跳到主要内容

你从未注入过的故障:给你的 Agent 提供一个说谎的工具

· 阅读需 11 分钟
Tian Pan
Software Engineer

打开你的智能体(agent)韧性测试套件,看看它实际上在测试什么。你会发现超时。你会发现连接中断、500 错误、频率限制响应、格式错误的 JSON,也许还有一个在失败前卡死三十秒的工具。所有这些都是经典模式下的故障注入:工具坏了,问题在于你的智能体是否能优雅地降级。

现在找找看那个工具完全没坏的测试。那个工具在 80 毫秒内响应,返回了完全符合 schema 的有效 JSON,但里面的值纯粹是错的。一个过期了三天的余额。一个交换了两个字段的客户记录。一个两位数移位的订单数量。一个本应返回四十行却返回空的查询结果列表。

你找不到它。几乎没有人注入过这种故障。而这正是你的智能体最无法抵御的故障,因为所有其他故障都会自我宣告,而这种故障不会。

这种差距的存在是结构性的。混沌工程(Chaos engineering)是围绕分布式系统成长起来的,在那里的故障大多是崩溃故障(crash failures)——一个组件停止响应、返回错误或消失。你的重试逻辑、断路器、回退方案都是为了捕捉“良好响应”的缺失。一个调用工具并遇到超时的智能体有一个清晰的信号可以采取行动。一个调用工具并得到一个自信、格式良好的谎言的智能体则完全没有信号。它得到了它想要的东西,形状完全符合预期,它没有任何本能去怀疑它。

崩溃故障会自我宣告;撒谎的工具则不会

分布式系统理论对你的测试套件所缺失的这种区别有一个专门的称呼。崩溃故障是一个通过停止来失效的组件。而*拜占庭(Byzantine)*故障是一个通过撒谎来失效的组件——它继续参与、继续响应,且其响应内容是错误的,但在外观上与正确内容无法区分。众所周知,容忍拜占庭故障的成本非常高,因为你无法通过检查响应是否到达来检测它们。

你的智能体工具也可能发生拜占庭式故障。搜索索引返回了不再存在的文档。计价服务返回了上次更新前的缓存数字。一个本应连接两张表的数据库查询静默地丢弃了连接条件,并返回了一个笛卡尔积(Cartesian-product)乱局,但它仍然被解析为行列表。这些都不会触发异常。这些都不会出现在你的错误率中。按照你的可观测性栈记录的每一项指标来看,这次工具调用都是成功的。

这里是让智能体显得特别脆弱的部分:语言模型将成功的工具调用视为绝对真理(ground truth)。它没有怀疑反射。当你把工具结果交给模型时,模型不会将其视为带有概率的证据来权衡——它将其作为事实吸收并向前推导。研究人员将 LLM 描述为在优化“合理性(plausibility)”而非“真实性”,而在整个上下文窗口中,工具输出是最合理的东西。它来自系统,它是结构化的。模型会不假思索地在其基础上进行创作。

使用同样数据的人类拥有智能体不具备的防御机制。一个看到客户账户余额跳变 100 倍的人类分析师会觉得有些不对劲。而智能体毫无感觉。它把这个数字写进下一步推理中并继续前进,三步之后,它就产生了一个自信、格式良好、但完全错误的建议——而现在,这个失败几乎无法追溯到导致它的那个工具调用。

为什么你的“诚实失败”测试什么也没告诉你

绿色的韧性测试套件中隐藏着一个舒适的假设:如果智能体能很好地处理失败,它就是健壮的。但对“诚实失败”的健壮性和对“不诚实失败”的健壮性是不同的特性,通过前者并不能说明后者的情况。

当工具超时,你的智能体执行的是你编写的代码路径——重试、回退、向用户道歉。你可以测试那个路径,因为它是确定性的且受你控制。而当工具撒谎时,你的智能体执行的是它的推理——而在推理领域,你没有任何保证。智能体必须注意到一个数字是不合理的,注意到上下文中的两个事实相互矛盾,注意到结果集空得可疑。那是一种判断,而不是一个分支,而判断正是你的超时测试从未演练过的东西。

这就是为什么团队在生产环境中屡屡感到惊讶。在预发环境中优雅处理了每一次宕机的智能体发布了,然后下游服务在一次糟糕的缓存部署后开始返回过期数据。没有任何警报触发。服务在线。延迟正常。智能体只是静默地、自信地对每一个查询了该数据的用户出错,直到一周后收到支持工单,人们才得到第一个信号。“诚实失败”测试套件全程都是绿色的。

上下文中毒(Context poisoning)是智能体社区中针对这一现象出现的术语,定义中最重要的词是静默(silent)。它不是崩溃。它是从一个不可靠的输入开始并不断复合的决策质量的缓慢下降。智能体没有崩溃。它一直在工作——这正是问题所在。

构建一个“合理谎言”故障库

修复工作始于将“自信的工具错误输出”视为一等故障类别,并为其建立一个库,就像你为超时和错误率故障建立库一样。每个条目的设计约束都是相同的:损坏的响应必须是格式良好、响应迅速且合理的。如果它未通过 schema 验证,那你测试的是验证器,而不是你的 Agent。关键在于测试那些能通过所有机械化检查的情况。

以下是一个初步的目录,提取自实际出现的失败模式:

  • 过期值。 返回在早些时候是正确的数据——比如余额、状态、库存数量——且不提示其已过时。这是现实世界中最常见的版本,而且它是不可见的:过期数据和新鲜数据在形态上是字节一致的。
  • 字段对调。 将送货地址放在账单字段中,将 created_at 放在 updated_at 中,或者将客户的姓和名颠倒。每个值单独看都是有效的;但记录是错误的。
  • 数字移位和单位错误。1209 返回为 1290,在 schema 隐含单位为分的地方返回美元,或者数量级差了一个数量级。数字可以解析,但它不是那个正确的数字。
  • 错误的空值。 对本应有结果的查询返回空列表。Agent 会将“无结果”视为合法答案,并欣然告诉用户“你没有未解决的工单”,而事实是查询静默地过滤掉了所有工单。
  • 微妙的错误联接。 返回的结果集中大部分行是正确的,但有几行完全属于另一个实体——这是查询丢失了 WHERE 子句时的故障模式。
  • 自信的矛盾。 返回一个与 Agent 上下文中已确立的事实直接矛盾的值,看看 Agent 是注意到了冲突,还是仅仅覆盖了旧的事实。

针对 Agent 特定混沌工程的开源工具已经开始出现,但你不需要框架就可以开始。在你的工具调用层周围添加一个中间件垫片 (middleware shim),拦截配置比例的响应并应用其中一种变异,就足以运行实验。

以“演练日”形式运行并衡量推理,而非运行时间

只有当你刻意运行故障库并观察正确的指标时,它才有意义。借鉴 SRE 实践中的“演练日”(Game Day)纪律:这是一项有计划、受控的练习,团队向真实的(或类生产的)系统注入故障,并观察其反应。针对 Agent 的调整在于你衡量的内容。

在基础设施演练日中,你衡量的是恢复情况:系统是否保持运行,故障转移是否生效,恢复正常需要多长时间。对于“撒谎工具”演练日,运行时间 (uptime) 毫无意义——Agent 在每次注入中都保持“运行”状态,因为谎言永远不会击垮它。真正关键的指标是 Agent 的推理是否质疑过中毒的输入。

因此,为此进行检测。对于每个注入的故障,将结果归为以下三类之一:

  1. 捕获了它。 Agent 标记该值为不合理的,通过另一个工具进行了交叉检查,要求用户确认,或者拒绝基于该值采取行动。这是获胜条件。
  2. 吸收了它但控制了影响。 Agent 使用了错误的值,但下游防护栏——验证步骤、写入前的确认提示、对最终答案的合规性检查——阻止了它到达用户或引发行动。
  3. 在其之上构建。 Agent 将谎言视为真理,并毫无阻碍地产生了一个自信的错误输出。

第三类是你真实的失败率,在运行这项练习之前,你几乎肯定不知道这个数字是多少。大多数团队发现这个数字很高——Agent 基本上从不怀疑工具,因为在其训练或提示词中,没有任何内容告诉它要这么做。

如何处理这个数字才是真正的工程工作。有些谎言在 Agent 层面是无法预防的,属于上游问题——数据源上的新鲜度检查和 TTL,或者使字段交换变得不可能的 schema 约束。有些在 Agent 中是可以捕获的:一个验证步骤,将高风险值与第二个独立工具进行交叉检查,这种“拜占庭容错”的做法是不信任任何触发不可逆操作的单一来源。还有一些属于提示词和工作流:明确指示 Agent 将工具输出视为证据而非事实,并在两个来源不一致或数值看起来极端时进行升级。

这里有一个从 AI 系统拜占庭容错研究中借用的有用原则:将每个工具和每个模型都视为一个可能在撒谎的节点,并在设计上确保没有任何单个撒谎节点可以破坏重要的结果。你不需要完整的共识机制。你需要停止构建那种直接采纳单一来源的第一个答案并付诸行动的 Agent。

在测试过谎言之前,不要称其为稳健

一个令人不安的结论是,绿色的弹性仪表盘一直以来衡量的只是问题中简单的那一半。诚实的故障——超时、500 错误、连接断开——是你的 Agent 必然会处理的故障,因为处理这些故障是你编写并测试过的代码。不诚实的故障则是那些未经验证就进入生产环境的故障,因为没有人注入过它们,因为它们看起来不像故障。

你本季度可以运行的最有价值的实验也是成本最低的实验之一:挑选三个风险最高的工具,为每个工具编写五个合理的谎言,通过中间件垫片路由它们,并观察你的 Agent 有多频繁地在垃圾数据之上自信地构建。无论那个数字是多少,它都决定了你的用户是否可以信任该系统——而现在,那是一个你从未衡量过的数字。

Agent 对诚实故障的稳健性告诉你它能在停机中幸存。它完全没有告诉你,当它被一个没有理由怀疑的工具告知虚假信息时,它是否能幸存。在你特意注入这种故障之前,“稳健”只是你的测试套件名不副实的一个词。

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