跳到主要内容

取消安全的智能体:你的“停止”按钮背后已经产生的副作用

· 阅读需 12 分钟
Tian Pan
Software Engineer

用户点击“停止”,因为智能体(agent)误解了请求。UI 界面闪烁着“已停止”。在加载图标消失时,智能体已经发送了两封邮件,在你的日历上安排了周二的会议,针对错误的分支开启了一个草稿拉取请求(pull request),并排队发送了一条正在通过工具层追赶取消信号的 Slack 消息。模型已经听话地停止了生成 token。但外部世界并未停止对它三十秒前生成的 token 做出反应。

这是智能体演示中没人提及的失败模式。同步代码中的取消操作本身就是一个难题,背后有一整代协作式取消理论的支持:Go contexts、Python 的 asyncio.cancel、带有任务组的结构化并发,以及“礼貌请求、谨慎升级、不留资源”的整套语法。智能体在这个本就困难的问题上又增加了一层复杂性:规划器不知道用户在第 4 步和第 5 步之间撤回了授权,而它在第 4 步启动的工具在第 5 步被取消时也不会收到通知。“停止”只是一个 UI 交互功能。其背后的系统必须经过专门设计。

除非能明确已经发生的情况,否则“停止”就是一个谎言

大多数智能体 UI 将取消视为一种二元状态:一个按钮,一个消失的加载图标,一个“已停止”的提示。用户看到“已停止”后,会假定系统不再代表他们执行任何操作。这种假定正是 Bug 所在。

推理取消(inference cancellation)是简单的一半。token 流可以干净地停止,因为模型是一个运行时可以中断的函数。工具执行则是困难的一半。当取消信号到达工具层时,发往日历 API 的 HTTP 请求可能已经在传输中,一封邮件可能已经在队列中等待处理,而数据库迁移可能已经进行到了一半,智能体永远不会看到事务的提交或回滚。一个值得信赖的停止按钮必须体现这些情况。取消后正确的 UI 交互不应该是“已停止”——而是“这是你的智能体在取消前所做的操作,这是被中断的操作,这是仍在进行中的操作,这是你可以撤销的操作”。

构建这样的 UI 需要底层系统知道答案。但系统几乎从不掌握这些,因为副作用清单是隐式的:分散在工具调用日志、可能已写入也可能未写入的供应商请求 ID,以及描述智能体尝试了什么而非最终落地了什么的异常追踪中。第一个架构动作是让清单显性化。每一个工具调用在发出前都必须记录日志,并带有足够的元数据,以便在取消后的审计中进行重建:尝试了什么、什么到达了上游系统、返回了什么,以及存在哪些补偿动作(如果有的话)。

第 4 步和第 5 步之间的授权窗口

运行时间较长的智能体存在一个同步工具从未遇到过的微妙授权问题:用户在运行过程中可能会改变主意。他们看着智能体执行第 1 步、第 2 步、第 3 步,然后在第 4 步意识到智能体误读了目标。他们点击取消。此时规划器正处于第 5 步工具调用的 token 生成中。工具调用随后落地。

在设计良好的系统中,工具的执行权限不是在会话开始时的一次性授权,而是在执行瞬间重新检查的。这种架构原语更像是一个可以被撤销的 OAuth 风格作用域令牌,工具层在每次调用时都会出示该令牌,而不是规划器只读取一次的全局会话标志。授权应在请求时评估,而不只是在初始连接期间。这样,当智能体偏离目标(即用户已撤回授权的目标)时,就无法继续凭旧授权行事。具体而言:取消信号不仅停止规划器,还会使挂起和未来的工具调用必须持有的执行授权失效。

这将失败模式从“取消后工具调用成功”转变为了“取消后尝试工具调用但被授权层拒绝”。第二种失败模式是可恢复的,而第一种则是事故。

一个有用的思路:每个工具调用都是一个两阶段提交(two-phase commit),其中第二阶段取决于“授权依然有效”的信号。工具准备副作用(撰写邮件、锁定日历时段、开启数据库事务),但在重新验证授权之前不会提交。取消操作会翻转状态位,已准备但未提交的副作用将作废,而不是逃逸到外部世界。这种模式并非没有代价——每个工具集成都必须设计准备/提交边界——但对于不可逆的操作,这是让“取消”符合用户预期的唯一方法。

正向计划需要逆向计划

二十年来,分布式事务中的 Saga 模式一直是解决“一系列副作用必须部分失败时该怎么办”的答案。无论开发者是否意识到,智能体本质上就是 Saga。标准的 Saga 原则同样适用:当某个步骤产生副作用时,在发布该步骤之前,请定义在语义上撤销该副作用的补偿动作。退款、取消日历邀请、如果协议支持则撤回邮件。将数据库记录标记为已回滚并添加墓碑行,因为原始写入仍保留在审计日志中。

更难且属于智能体特有的点是:补偿并不总是能在事后推导出来。一个可以调用五十种工具的规划器无法通用地知道如何撤销这五十种工具。撤销方案必须由集成工具的人编写,而不是由模型生成。在生产环境中有效的架构模式是,在注册每个工具的同时,注册一个配对的补偿动作和可逆性分类:

加载中…
References:Let's stay in touch and Follow me for more thoughts and updates