语料库揭示了什么
关于生产环境中 agent 的企业案例研究,其失败往往收敛于一小类问题,而其中大多数并非模型失败,而是界面失败。同样的摩擦出现在原本互不相关的部署中:
- 工具返回非结构化的成功。像
{"status": "ok"}这样的响应,或一个带有可读正文的裸 HTTP 200,会让 agent 去推断预期效果是否真的发生。 - 部分失败被隐藏。一个批处理操作处理了部分条目、跳过了其他条目,却因为传输成功而报告成功。
- 一个意图需要多次调用的仪式。创建一个资源意味着调用创建端点、轮询状态端点,再调用最终化端点。每次调用都是一次半途而止的机会,而中间状态在工具描述中根本没有被建模。
- 重试针对的是从不发出终态信号的端点。事故报告描述了 agent 在一个不断返回看似瞬时错误的调用上循环,因为响应中没有任何东西能区分“再试一次”和“这永远不会成功”。
模式是一致的:工具接口被要求承载它从未被设计来表达的语义。与其说 agent 行为不当,不如说它只是在完成该界面所允许的唯一解释。
封装谬误
把现有的人类 API 封装进 JSON schema 并不会产生一个工具接口。它产生的是一个带有单薄描述的远程过程调用。schema 告诉模型要发送哪些字段;它几乎不说明这个调用意味着什么、何时有效,或某个响应暗示了世界的什么状态。
人类 API 是为能够弥补缺失信息的读者设计的。这样的读者可以从周围语境推断意图,问同事某个状态码是什么意思,或注意到某个结果看起来过时并去调查。对于在循环中运行的 agent 来说,这些恢复路径一个都不存在。当界面含糊时,agent 唯一可用的策略就是猜测、重试或放弃,而这三者在生产环境中都很昂贵。
封装方法还引入了无法在转换中存活的假设。人类 API 可能把带有错误正文的 200 视为团队里人人都知道的约定。它可能把空列表既当作“没有结果”又当作“查询格式错误”,因为人类会去核实。它可能让破坏性操作非幂等,因为人类不会点两次按钮。对于人类调用者,这些都是合理的设计决策;对于机器调用者,它们都是缺陷。
Agent 需要的反而是一个具有显式前置条件、显式后置条件和终态错误分类的接口。前置条件说明调用有效之前必须为真的事情。后置条件说明成功调用之后将为真的事情。终态错误分类说明哪些失败可重试、哪些是永久性的、哪些需要完全不同的调用。
一次工具调用,一个意图

一次工具调用应当表达单一意图。如果完成一个目标需要一连串无法恢复的调用,这个接口就在招致部分完成和含糊的重试。当多步骤过程不可避免时,应显式建模中间状态,让每次转换都成为具有自身前置条件的一等操作,而不是把它们藏在调用者被期望正确执行的仪式背后。
每个响应都是三者之一
每个响应都必须可归类为成功、可重试失败或终态失败。没有第四种状态。含糊的响应会迫使调用者发明策略,而发明的策略正是重试风暴的来源。具体而言,这意味着一个响应信封,带有小而封闭的结果种类集合,以及一条规则:未知的结果种类应被视为终态失败而非重试。
一个有用的机制是带有显式结果判别字段和有界重试提示的响应信封:
{
"outcome": "retryable_failure",
"reason": "upstream_unavailable",
"retry_after_ms": 2000,
"observed_state": null
}
重要的属性不是具体的字段名,而是调用者无需解释即可分支。terminal_failure 意味着停止并抛出错误。retryable_failure 意味着在所述界限内重试。success 意味着后置条件成立。
破坏性操作按键幂等
破坏性操作应当按键幂等,这样重试在构造上就是安全的。调用者提供一个由意图派生的稳定幂等键,接口对重复的键返回相同结果,而不是执行两次效果。这消除了 agent 去推理上一次尝试是否落地的需要,而这恰恰是它最不擅长的推理。
返回观察到的状态,而不只是声称的结果
响应应当把观察到的状态与声称的结果一并返回,这样调用者可以验证而非信任。如果工具声称某个资源已创建,它应当返回该资源当前可观察的状态。验证于是

诚实的权衡
这些规则并非没有代价。显式的结果信封会让响应更大,从而消耗上下文,并可能把工具描述推到模型无法可靠关注的程度。幂等键需要服务端状态和保留窗口,对于此前没有这些的团队来说是运维开销。把仪式拆分成显式、可恢复的操作会产生更多工具,而更多工具意味着调用者面临更多选择歧义。设计问题不是要不要付出这些代价,而是没有它们时失败模式在哪里更糟。在大多数生产系统中,含糊的重试比更大的响应更昂贵。
评估含义
如果工具接口才是问题所在,那么工具调用正确性就不应仅以任务完成来评分。一次通过重试穿过终态失败而完成任务的运行不是正确的运行;它是一次在未能传达信息的接口上侥幸成功的运行。评分应分离两个维度:
- 意图到调用的映射。agent 是否针对所述意图选择了正确的工具和正确的参数,包括正确行为是什么都不调用的情形?
- 失败类别的处理。对于接口能发出的每个失败类别,agent 是否以正确的策略回应:在界限内重试、停止并抛出,还是切换到不同的调用?
这意味着回归测试套件需要把对抗性工具响应作为一等夹具,而不是偶尔的混沌实验。最重要的四种是慢速成功、部分成功、静默失败和带过时数据的成功。每一种都探测不同的假设:
- 慢速成功测试 agent 的超时策略是否区分延迟与失败。
- 部分成功测试它是否读取观察到的状态而非声称的结果。
- 静默失败测试它是否根本验证后置条件。
- 带过时数据的成功测试它是否比较时间戳或版本,而不是信任响应。
一个无法在测试框架中产生这些响应的接口,就是一个其失败模式直到出现在生产环境中才为人所知的接口。构建夹具是构建界面的一部分。
结语
工具接口是界面设计问题。封装方法把界面当作现有人类 API 之上的翻译层,并继承了该 API 对能够弥补信息的读者所做的一切假设。为机器调用者设计意味着让前置条件、后置条件和终态错误语义显式化,并接受随之而来的代价。替代方案是一个其失败与成功无法区分、直到有人去读日志的系统。
封面照片:Jakub Pabis / Pexels。
