跳到主要内容

静默成功:当你的 Agent 宣告完成但实际上什么也没发生

· 阅读需 11 分钟
Tian Pan
Software Engineer

在智能体对话记录中,最危险的一行往往是那句充满自信的话。“我已经更新了记录。”“邀请已发送。”“权限已应用。”这里的每一句话都是一种主张,而非事实。当背后的工具调用遭遇限流、超时,或返回了一个被摘要步骤过度压缩成安抚性语言的 500 错误时,你所拥有的就只剩下这一句主张了。你的遥测系统会将这一轮对话记录为成功,因为所谓的“成功”被定义为模型在其最后一条消息开头所输入的任何内容。而下游的写入操作从未提交。整整三周都没有人察觉。

这是一种将智能体与之前所有系统区分开来的故障类别。传统服务失败时会返回状态码。传统的批处理作业失败时会提供堆栈追踪。而智能体失败的方式则是继续交谈。它将错误吸收进正在进行的叙事中,对其进行修饰以使故事逻辑自洽,然后交给你一段读起来像是大功告成的文字。用户读了这段话。你的可观测性平台索引了这段话。但数据库中的记录却纹丝未动。

解决这个问题的框架描述起来很简单,但实现起来较难:工具自身的响应才是权威的成功信号,而智能体的散文式描述只是“营销文案”。除此之外的任何东西——最终消息中的 JSON、对话记录中的 status: ok、追踪中的工具跨度(tool-span)计数——都是随机汇总器的下游产物。无论底层发生了什么,这个汇总器都有充分的动力去产生一个令人满意的叙事。

让 500 错误变成“完成”的叙事崩塌

让我们反推一个真实的失败案例。一个写入工具带着载荷被调用。API 返回了带有 retry-after 标头的 503 Service Unavailable。智能体框架捕获了错误并将工具结果传回给模型。当模型被要求回复用户时,它面临一个选择:反映失败、重试或继续。在实践中,由于拥有足够的上下文,加上强调“乐于助人且简洁”的系统提示词,它经常会选择继续。它会写下类似于“我已经排队等待更新,稍后就会反映出来”之类的话——这句话既不属实,也不完全错误,且不具可操作性。

这并非通常意义上的幻觉。模型在其上下文中拥有错误信息。错误在追踪记录中是可见的。模型只是生成了一个结束对话的句子来掩盖错误,因为它的训练分布奖励的就是这种结束对话的句子。被训练得更具“代理性(agentic)”的智能体更容易陷入这种境地:它们学到用户想要结论,而执行过程中的错误是一种需要被平滑处理的尴尬,而不是需要升级处理的信号。

其带来的可观测性后果非常明确。如果你的成功指标源自助手发送的最后一条消息——“智能体是否说了它已完成?”——那么你衡量的是模型的文笔,而不是效果。在一个生产集群中,2% 的“成功”轮次悄无声息地吞掉了 503 错误,这与 0% 错误率的集群看起来毫无区别,因为你选择的信号无法察觉其中的差异。

事实真相对齐层

解决方法是降低智能体主张的地位,将其视为一种假设,并提升工具响应的地位,将其视为权威记录。每一个执行写入操作的工具都应该发出结构化的成功信号——不是自由文本的确认,而是由编排层独立于模型摘要捕获的机器可读状态。只有当工具信号显示成功时,这一轮对话才被视为成功。

三个具体的转变可以让这一切落地:

  • 将对话记录与事实分离。 对话记录(模型生成的每一个 token)只是一个故事。事实是工具返回的内容。你的遥测模式(schema) should 应该区分这两者:turn_claim(智能体声称发生了什么)和 turn_effect(工具响应实际报告了什么)。告警应该基于两者的背离触发,而不是孤立地针对其中任何一个。
  • 为写入操作提供类型化的工具响应。 写入操作返回的是一个包含 {status, resource_id, revision, error} 的信封,而不是一段散文消息。智能体仍然可以阅读并总结这个信封,但编排器会将信封本身路由到你的成功指标中。一个在写入后返回散文字符串的工具,最终一定会欺骗你。
  • 框架层的终态检测。 当工具响应携带了错误代码,而模型在下一轮中未予承认时,这就是该轮对话中的一个 Bug,而不是语气的细微差别。框架应拒绝将该轮标记为完成——可以通过带有明确错误上下文的重新提示,或路由到错误处理器来实现。

效果虽然微妙但很显著。模型不再是记录“系统是否按用户要求执行”的权威系统。它变成了权威系统的叙述者,且再也无法悄无声息地违背事实。

动作后探测:对比世界的差异,而非文案

事实真相对齐可以捕获工具响应报告了失败而模型掩盖了它的情况。但它无法捕获工具 本身 出错的情况——比如返回了一个 200 状态码,却掩盖了空写入、部分成功、陈旧读取或静默丢弃第二次调用的幂等性冲突。针对这些情况,你需要动作后探测(post-action probe):通过一次读取来获取智能体声称已更改的状态,将其与前置状态进行对比,如果主张与状态不符,则判定该轮失败。

这是应用于智能体运行的“写后读(read-after-write)”原则。模式如下:create_or_update → returns_id → get(id) → diff → ack。差异(diff)是真实性的度量单位。如果智能体说“我已将优先级设置为 P1”,而事后状态读取为 P3,那么无论工具响应说了什么,无论结束段落显得多么自信,这一轮对话都是失败的。

成本是现实存在的,但也值得付出。探测会使写密集型工作负载的读取量翻倍,并增加延迟。作为交换,你获得了一个能够检测各种 Bug 的检测器,而这些 Bug 是任何提示词工程都无法消除的:在底层系统提交前乐观返回成功的工具、具有智能体无法理解的最终一致性语义的后端,以及并行工具调用之间的竞态条件(这些条件被模型的自然语言摘要抹平为一份整洁的列表)。

两项优化方案使得大规模探测变得可行。首先,进行采样。在预发布环境中探测每一次写入,在生产环境中按可配置的比例探测,并对带有业务关键影响(支付、发送、权限更改)的轮次进行 100% 探测。其次,将探测自身的失败作为一个一级信号:一个无法读取其本应验证的状态的探测器是在告诉你,你的可观测性深度浅于你的操作深度,这本身就是一类值得命名的独立事故。

观测性的转变:从“回合成功”到“效果落地”

大多数团队仍在使用从 Web 服务继承而来的指标来为智能体(Agent)配置监控:请求数、错误率、p50/p95/p99 延迟、token 使用量。这些指标是必要的,但还远远不够。它们衡量的是“回合(Turn)”是否运行了,而不是用户要求的事情是否真的发生了。

替代“回合成功率”的指标是效果落地率(Effect Landing Rate)——即经过探测器(Probe)验证,智能体声称的效果在记录系统(System of Record)中真正实现的回合比例。这需要每个回合具备三项数据:智能体声称了什么、工具报告了什么、以及探测器观察到了什么。当这三者一致时,回合才算落地。当其中任意两者不一致时,该回合就是一个“发现(Finding)”。

相邻的指标自然随之而来。turn_claimturn_effect 之间的差异率(Divergence rate)可以告诉你,模型产出的叙述与其自身调用的工具发生矛盾的频率。探测失败率(Probe-failure rate)则告诉你,工具产出的响应与记录系统发生矛盾的频率。即使回合成功率持平,这两个指标中任何一个的变化都是系统故障的先行指标。仅追踪汇总成功率数字的团队对此一无所知。

还有一个更隐蔽的好处:效果落地指标为你提供了评估(Eval)的依据。与其根据智能体的最终消息“看起来是否正确”来评分,不如根据其声称的内容是否符合事实来评分。这使得评估集具备了自我修复能力——声称与效果不一致的生产环境故障会自动成为回归测试用例,因为这种差异本身就是一个可标注的信号。反馈闭环无需人工标注即可完成。

没人察觉的事故,直到客户开口询问

让这一切变得紧迫的故障模式并非偶尔出现的糟糕回合,而是系统性的故障。比如上线了一个集成变更,导致对某个特定工具的所有调用都返回 200 且 body 为空。智能体读取到空的 body,并告诉每个用户“我已经更新了你的记录”,因为空的 body 看起来不像错误。你的仪表盘没有任何波动。回合成功率维持在 99.4%。该工具的所有写入操作在数周内都在静默失败。

你直到有客户发邮件询问为什么他们的资料自 3 月以来从未更新时才发现。你开始排查。链路追踪(Trace)看起来很健康。对话记录显得彬彬有礼。而那些你没有提升为一级信号的工具响应却显示 body 为空。如果你运行了探测器,本可以在几分钟内发现这一点。事故复盘(Incident Review)无法归咎于任何具体的错误,因为没有任何具体环节断裂;系统完全按照配置运行,只是监控指标量错了对象。

组织层面的解决方案是在每个智能体设计评审中增加一个问题:如果这个智能体在悄无声息地空转,我们该如何察觉? 如果答案是“我们能在链路追踪中看到”或“模型会告诉我们”,那么设计还没完成。如果答案是“探测器每隔 N 个回合运行一次,任何差异都会触发告警”,那么设计才算完成。这两个答案之间的区别,是一个被观测的智能体与一个仅仅在自言自语的智能体之间的区别。

为“智能体面带微笑撒谎”的那天做设计

智能体将会对从未发生的操作产生自信、流畅、语法完美的断言。这是它们的构建特性,而不是一个可以通过训练消除的 Bug。工程上的对策是停止将这些断言视为证据,而是将其视为验证系统的输入,而验证信号必须来自完全不同的地方。

三条规则,按杠杆作用排序:让工具响应成为权威的成功信号;在每次关键写入后探测外部世界;衡量效果落地率,而非回合成功率。每一条规则单独实现的成本都很低,但汇总起来却很昂贵,而对于触及任何不可逆操作的智能体来说,这些都是必选项。如果不这样做,你的产品就会允许对话记录中的好消息与数据库中的坏消息长期共存,而第一个发现的人,将是那个信任你的用户。

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