评估通过,但工具全是 Mock 的:为什么你的 Agent 最棘手的生产故障从未进入测试框架
你的智能体在评估测试集上达到了 94% 的准确率。然而你的轮值告警却响个不停。房间里没人撒谎,这两个数字都是真实的。实际情况是,测试框架(harness)在测试提示词(prompt),而生产环境在测试智能体(agent),这是两个完全不同的产物,只是恰好共享了权重。
Mock 工具的评估通常是产生这种差距的原因。你用预设的 JSON 存根(stub)了 search_orders、charge_card 和 send_email,给模型输入一个用户回合,并对最终响应进行断言。这种运行方式成本低、具有确定性且可复现——这些都是 CI 系统喜欢的特性。但它对工具选择、延迟、速率限制(rate limits)、部分失败和重试行为保持沉默,也就是说,它忽略了那些在事故回顾中占主导地位的失败因素。
这并不是在反对 Mock 评估。对于提示词级别的断言,它们是正确的工具,且永远如此。我想表达的是,一个完全由 Mock 工具组成的测试套件,从未真正见过你的智 能体。下面我们将探讨这种差距的具体形态、为什么经济引力不断将团队推向这种做法,以及能够真正捕捉到那些频发事故的分层方法。
Mock 工具评估暗含的三个假设
每一个 Mock 的工具响应都包含了测试框架永远不会检查的三个假设:
- 智能体选择了正确的工具来调用。
- 它以正确的格式传递了正确的参数。
- 它在第一次尝试时就收到了格式良好的响应。
在生产环境中,这三点经常失效。当用户询问订单状态时,智能体可能会调用 search_products,因为这两个工具的描述中有一个名词重叠了。它们可能会在 Schema 预期 Epoch 秒数时传递一个字符串格式的时间戳。它们会遇到下游 API 的 429 错误,而模型的下一轮回应是“抱歉造成困扰,让我再试一次”——然后进入一个以同样方式失败的相同调用。Mock 评估对这三种模式都是盲目的,因为 Mock 接受任何参数形状,总是在第一次尝试时返回,并且隐式地为测试框架硬编码的工具选择背书。
一项针对生产环境智能体事故的从业者调查发现了大约四种反复出现的典型类型:缺乏依据的过早行动、捏造缺失实体的过度热情、干扰项引起的上下文污染,以及负载下的脆弱执行。这四种都是工具交互失败。在 Mock 工具套件中,这四种情况都不会可靠地出现,因为 Mock 避开了它们赖以生存的执行闭环。
为什么团队首先会选择 Mock
经济因素至关重要,因为对大多数团队来说,“只使用真实工具”并不是一个现实的选项。
使用真实工具的评估比 Mock 评估贵 10 到 50 倍。你需要为智能体的每一轮 LLM Token 付费,加上工具调用的下游 API 费用,再加上必须存在并保持干净的共享固件(测试账户、沙盒数据库、种子数据)。一个在 Mock 环境下运行只需 90 秒的 200 例评估套件,在真实工具上可能需要 40 分钟,而这还只是理想情况——还没考虑到第三方的不稳定性、沙盒速率限制,以及“一个测试留下的残余数据破坏了下一个测试”的问题。
不稳定性是另一半代价。一个 96% 的时间显示为绿色的真实工具测试套件其实并不合格——它只是噪音。开发者会开始不再信任它,不再在本地运行它,也不再让它在 CI 中起到拦截作用。不到一个季度,这个套件就会变成参考性的,然后被忽视,最后被删除,团队最终回到了原点:只有 Mock 和满腹牢骚。
错误并不在于 Mock 本身。而在于将 Mock 套件视为智能体行为的权威测试(test of record),而它实际上只是提示词行为的权威测试。
Mock 套件隐藏的具体失败情况
值得具体列出这些情况,因为“工具交互失败”这个抽象概念会让团队只是随声附和,而不会改变他们的测试方式。
工具选择错误首当其冲。你的智能体有 12 个工具,而模型选错了。Mock 无法捕获这一点,因为测试框架通常硬编码了哪个 Mock 做出响应。即使你设置了路由,Mock 也不会惩罚选择了一个在现实中响应毫无用处的工具的行为——因为虚假的响应总是看起来很合理。
参数形状错误排在第二位。模型输出了 {"user_id": "12345"},而工具期望的是一个整数。在生产环境中,API 会返回 400 错误;但在 Mock 中,你可能因为测试不关心而放宽了 Schema 的编写,导致智能体径直进入下一个“成功”的回合。
延迟引起的超时是最让人头疼的。工具的 p99 延迟超过了智能体的每轮预算,编排器(orchestrator)开始中途杀掉该回合。当工具调用在中途断掉时,模型会做什么——重试?放弃?幻觉出一个结果?——这完全取决于你的测试框架连接方式,而在瞬时返回的 Mock 环境下是测试不到的。
速率限制级联。智能体遇到了 429 错误,决定重试,再次遇到错误,由于循环中没有计数机制,智能体在重复调用同一个失败的请求中耗尽了所有的 Token 预算。这种模式只在能访问真实工具并施加真实背压(backpressure)时才会存在。Mock 没有 429 状态。
部分失败是最后一种重要的情况。一个并行工具调用返回了两个结果和一个超时。智能体如何处理超时?诚实地报告?静默忽略?假设成功?每个团队都是在生产环境中才发现问题的,因为 Mock 测试只是派发了一个单一的合成字典,然后就继续运行了。
