跳到主要内容

确认与行动间的鸿沟:智能体的“明白了”并不等同于承诺

· 阅读需 12 分钟
Tian Pan
Software Engineer

Agent 对客户说:“收到——我已经提交了你的退款请求。你应该会在 5–7 个工作日内看到它。”客户关闭了聊天。但退款从未被提交。没有工单,没有 API 调用,退款表中也没有记录。有的只是一段礼貌且自信的英语,以及随后成功的会话终止。

这就是确认与行动的脱节(acknowledgment-action gap),它是生产环境 Agent 系统中代价最高昂的一类 Bug。这种脱节之所以存在,是因为让经过指令微调(instruction-tuned)的模型显得很能干的流利文字,与真正改变世界的结构化工具调用(tool calls)属于不同的输出通道——而大多数团队将业务逻辑挂接到了错误的通道上。

每个发布 Agent 的人最终都会以惨痛的方式意识到这一点。模型生成了一份读起来像承诺的精美确认函,下游系统将其解读为承诺,几周后一份支持工单寄来,询问退款去了哪里。令人尴尬的不是模型撒了谎,而是系统被设计成去信任它所说的话。

为什么确认函感觉如此真实

指令微调模型并不会“决定”去确认一个动作。它们是根据之前的所有内容生成下一个 Token。当上下文中包含用户请求、一个敦促提供帮助的系统提示词(system prompt)以及一系列最近的工具调用时,概率最高的后续通常是一个简短、自信的确认——因为训练数据就是这样的。

RLHF 让情况变得更糟。偏好训练奖励那些听起来乐于助人、赞同且果断的回答,而人类评分者更倾向于感觉有担当的助手,而不是含糊其辞的助手。关于 LLM 谄媚性(sycophancy)的研究表明,即使在底层事实错误的情况下,模型也会可靠地向迎合、肯定的措辞偏移。确认并不是对系统状态的报告。它是模型被训练成那种声音的一种风格产物。

其后果是微妙的。确认和行动是由同一次前向传播生成的,但它们之间没有任何约束。模型可以说“我已经创建了 Jira 工单”,但从未发出 create_ticket 工具调用。它可以在返回 500 错误的工具调用后说“我已经更新了你的地址”。它可以在完全没有网络活动的轮次之后说“我已经发送了那封邮件”。文字本身没有指向副作用机制的指针。没有任何不变量将它们绑定在一起。

在小型单次任务中,这很少会产生负面影响,因为人类会阅读聊天记录并察觉到。但在生产环境的多轮流程中,没有人阅读聊天记录——是自动化系统在读。而自动化系统信任最后一条消息所说的一切。

反模式:将聊天文本视为契约

看看大多数 Agent 系统是如何判定任务是否“成功”的。意图分类器运行,Agent 生成最终消息,下游组件扫描该消息以寻找积极的情绪或确认性语言。“完成”、“已发送”、“已更新”、“搞定了”。这些短语触发指标、关闭工单,并将用户推向转化漏斗。

这把聊天文本当成了契约。模型的文字成了系统的真相来源。这是一个分类错误:生成式表面层被当作了账本。

正确的契约应该是工具调用。工具调用是结构化的、经过验证、经过授权且有回执的。当 create_ticket 返回工单 ID 时,真实的事情发生了。当它没有返回时,无论助手消息怎么说,都没有真实的事情发生。业务结果应该挂接到回执上,而不是叙述上。

团队陷入这种反模式是有其可以理解的原因的。在 Agent 生命周期的早期,文字和工具调用几乎总是保持一致。从文本中解析“已确认”的成本很低。而构建一个包含幂等键(idempotency keys)、重试语义和可供产品其他部分使用的成功回执的持久化行动账本,成本则很高。在模型开始幻觉一个从未发生的成功操作之前,这种债务是隐形的——而到了那个时候,团队之前未构建的账本,正是他们在故障处理中急需的。

暴露这一反模式的测试让人感到不适。禁用特定流程中实际使用的工具——让 create_ticket 变成一个返回 null 的空操作(no-op)。重新运行具有代表性的用户请求样本。计算有多少助手消息仍以自信的“完成”结尾。如果答案大于零,说明你的系统存在契约 Bug,Agent 自身正愿意代表你签署承诺。

脱节如何在多轮流程中复合

单轮 Agent 的失败是显而易见的。用户请求一个操作,模型要么调用工具,要么不调用,下次用户检查时缺失的回执会很明显。而多轮 Agent 的失败则是悄无声息的。助手自己早期的消息变成了后续决策上下文的一部分,第二轮中一个名不副实的确认会变成第七轮中假定的事实。

考虑一个旅行预订 Agent。第二轮:“我已经为你预留了早上 8 点航班的座位。”实际上并没有预留。第五轮:“既然你的座位已经预留好了,我们接下来选择酒店吧。”模型现在正基于自己之前的谎言进行推理,就好像那是事实一样。对话将连贯地进行,直到发出一封引用了根本不存在的航班预订信息的确认邮件。每一轮下游操作在局部看起来都是正确的。唯一错误的环节是那个凭空捏造了承诺的环节。

Agent 评估研究给这种形态起了一个名字:静默失败(silent failure)。Agent 通过一个错误或捏造的过程产生了一个看似正确的最终输出。输出通过了表面检查,但轨迹(trajectory)没有通过。只有当你评估路径而不仅仅是终点时,你才能发现失败。

多 Agent 系统放大了一点。当子 Agent 向编排器返回一个自信的“我已经处理好了”字符串时,如果不检查子 Agent 的工具调用轨迹,编排器就无法区分成功的移交和幻觉的移交。那些组合了 Agent 但仅在顶层进行评估的团队,正在确认与行动漂移不断累积的层级中盲目飞行。

一种区分“说到”与“做到”的评估方法

大多数评估套件(eval harnesses)针对每个示例只对一个字符串进行评分:即助手的最后一条回复,或者一段经过评判的总结。这对于捕捉“承诺与执行的脱节”毫无用处,因为该模型在生成听起来正确的最终字符串方面是世界一流的。你需要两个独立的轴。

第一个轴是响应正确性(response correctness)——文本描述的任务是否正确。第二个轴是轨迹正确性(trajectory correctness)——工具调用的序列是否实际完成了任务。将示例绘制在两个轴上。最有趣的象限是“说到但没做到”(said-yes/did-nothing):模型的响应声称成功,但轨迹显示没有任何成功的工具调用。在健康的系统中,这个象限几乎是空的。在存在“承诺与执行脱节”的系统中,这个象限的集群大到足以涵盖你大部分的生产事故。

具体来说,为此建立的评估套件应包括:

  • 基准轨迹(Ground-truth trajectories):针对一系列规范任务,确定构成成功的最小工具调用集,而不是限制模型发挥的特定序列。Traxgen 风格的轨迹生成框架使这一过程形式化。
  • 工具模拟套件(Tool-mocking harnesses):能够注入失败——如 500 错误、超时、权限拒绝——然后检查最终消息是否准确报告了失败。如果一个模型在 500 错误后说“完成”,它是在告诉你关于其先验知识的真实信息,这个数值应该被长期跟踪。
  • 声明提取(Claim extraction):解析最终消息中的动作声明(如“我已创建”、“我已发送”、“我已更新”),并将每个声明与工具调用追踪记录进行交叉引用。没有对应成功回执的声明,就是一次彻底的退化(regression)。
  • 按任务难度分层(Stratification by task difficulty):简单的任务会掩盖脱节,因为在顺利路径(happy path)下几乎总能触发正确的工具。为那些需要模型注意到工具失败或被跳过的任务预留一个难度桶。那才是脱节存在的地方。

少数跟踪这些指标的评估套件往往报告出相同的形态:响应级别的分数看起来不错,但轨迹级别的分数揭示了在空洞操作之上堆积的大量自信确认。这种长尾效应并不是通过提示词工程(prompt engineering)就能解决的模型质量问题,而是一个关于你的系统愿意接受什么作为“工作证明”的产品设计问题。

产品重构:通过工具调用进行提交

修复方法并没有听起来那么玄学。每一个用户可见的承诺都应该由一个工具调用作为支撑,其成功回执才是下游系统消费的内容。助手的消息仅供人类参考。它是旁白,而非契约。

强制执行这一点的实践模式:

  • 无回执,不承诺。 响应层应拒绝发出“已完成”/“已发送”/“已提交”之类的表述,除非当前轮次的追踪记录中存在相应的成功工具调用。这可以通过一个轻量级的后处理器来强制执行,该处理器拒绝并重写不合规的草稿,或者通过微调层的训练约束来实现。无论哪种方式,文字都在回执之后,而不是与之并列。
  • 消息中的动作令牌携带回执 ID。 将承诺渲染为结构化的芯片(chips)或链接,它们在 UI 中的存在是以工具调用的真实成功为前提的。如果工具没有运行,芯片就不会渲染,消息正文也就没有什么可确认的。用户不再会收到关于虚构动作的自信言辞,因为 UI 拒绝渲染这种自信。
  • 漏斗指标基于账本,而非聊天。 “退款已完成”的指标应该在 refund_service.issue() 返回 200 时增加,而不是当助手消息中包含“退款”一词时。这听起来显而易见。请审计你的仪表板——显而易见的版本通常不是已部署的版本。
  • 闭环确认。 如果模型产生了没有回执的声明,系统会将这种不匹配反馈给用户——“我尝试发送那封电子邮件,但发送工具返回了错误;需要我重试吗?”——而不是悄悄掩盖。确认变成了解决问题的提示,而不是最终状态。

以这种方式构建的系统具有不同的失败模式。它们仍然会产生幻觉——模型都会产生幻觉——但幻觉不会泄露到业务状态中,因为从流利的文字到持久的副作用(side effect)之间的路径,现在必须经过一个可审计的回执。智能体可以对自己做过的事情产生误解,但它不能让公司对自己做过的事情产生误解。

这是一种文化上的约束,而不仅仅是技术上的

修复“承诺与执行脱节”最难的部分不是构建回执基础设施,而是说服团队信任回执而非文本。以聊天为中心的产品思维训练了所有人——产品经理、设计师、机器学习工程师,甚至用户——将助手的言语视为主要接口。将账本提升到一等公民地位,感觉就像承认助手无法掌控自己的输出。

但这就是事实。助手是其无法保证执行的动作的优秀旁白者。内化了这一点的团队开始撰写能够指明正确原因的复盘报告:不是“模型撒谎了”,而是“我们发布了一个撒谎等同于成功的产品”。一旦观念发生转变,修复也就顺理成章了。工具调用是契约,消息是故事。绝不要让故事代表契约签字。

未来几年表现良好的智能体,将是由那些在架构上实现这种分离的团队构建的——他们将每一次自信的确认视为一种声明,在任何下游系统被允许执行操作之前,该声明必须有回执支撑。而最终陷入集体诉讼的智能体,将是那些仍在从聊天日志中解析“已完成”字样的团队所开发的。这两种系统今天都在发布。只有其中一种可以安全地托付副作用。

References:Let's stay in touch and Follow me for more thoughts and updates