停止并非一种状态:为什么智能体需要类型化的终端原因协议
打开一个 Agent 集群(fleet)的仪表板,你会看到一个干净的数字:完成率,94%。在它下方是一系列运行记录,每条都标记着两种状态之一 —— 正在运行(running)或未在运行(not running)。那 6% “未在运行”的记录看起来完全一样。其中一些完美地完成了任务。一些在离完成还差两步时达到了步骤限制。一些捕获到了工具错误并放弃了。一些正确地判定任务是不可能的。还有一些则干脆断了思路,停止输出 token。
你的监控无法区分这些情况。它只知道流程不再运行了。它不知道 为什么,而“为什么”正是你在决定是否要呼叫(page)值班人员时唯一关心的事情。
这是大多数 Agent 部署中悄然存在的架构鸿沟。我们痴迷于对 Agent 的 路径 进行埋点 —— 每一次工具调用、每一个 token、每一次检索 —— 然后却让 Agent 的 退出 坠入深渊,变成一个单一的、无类型的事件。一次运行结束了。框架记录了它的结束。值班人员得到一个数字,它将健康的完成与礼貌的放弃混为一谈, 而区分它们的唯一方法就是在事后打开追踪(trace)记录,逐一阅读。
一个可能以六种具有显著差异的方式结束的过程,需要说明它是以哪种方式结束的。这句话听起来显而易见,但几乎没有 Agent 框架强制执行这一点。
完成率掩盖了分布情况
首先看看“完成率”到底测量的是什么。它是达到终端文本输出的运行次数与所有启动运行次数的比率。这个指标基本上是不加思索地从请求/响应模式的世界中借用来的,在那个世界里,服务器要么返回 200,要么不返回,而 500 则是明确的错误。
Agent 打破了这种模式。一次 Agent 运行并没有单一的失败模式和成功的模式。它是一个光谱,而光谱的两端都是合理的结局:
- 它 完成 了任务并生成了正确的结果。
- 它在任务中途 耗尽了预算 —— 达到了最大轮次上限或实际时长超时。
- 它 卡在了一个工具上,该工具报错、超时或返回了垃圾数据,且 Agent 无法绕过它。
- 它 判定任务不可行 并主动停止,这通常是 正确 的决定。
- 它 升级给人类 处理,交接工作而不是盲目猜测。
- 它 遗弃 了运行 —— 停止取得进展、陷入循环、偏离主题,并在上述情况都没有明确发生的情况下陷入沉寂。
94% 的完成率告诉你,有 6% 的情况落在了后面五种。它不会告诉你这 6% 主要是干净的不可行判定(一个健康的系 统拒绝了它不该做的工作),还是主要是遗弃(一个系统在默默地失败)。这两种解读需要完全相反的应对措施。一种意味着可以发布;另一种则意味着停止推广。原本应该驱动这一决策的指标,在结构上却无法区分它们。
这是多 Agent 失败文献中不断出现的同一个陷阱。加州大学伯克利分校的 Sky Computing Lab 通过手动标注大约 150 条追踪记录并扩展到 1600 多条,建立了一个 Agent 失败分类法 —— MAST。核心发现并不是 LLM 很笨,而是大多数失败都是 结构性 的:缺失终止标准、没有角色约束、没有人防范的崩溃。在他们的 14 种失败模式中,有几种 —— 步骤重复、提前终止、忽略其他 Agent —— 正是完成率使之变得不可见的东西。你无法管理一个你的核心指标看不见的失败类别。
“停止原因”已经存在 —— 只是在错误的层级
令人沮丧的地方在于:模型提供商已经解决了这个问题的某个版本。每个聊天补全(chat-completions)响应都带有一个 finish_reason,而且它是一个封闭的枚举(enum) —— stop、length、tool_calls、content_filter。Anthropic 的 API 也有自己的:end_turn、max_tokens、tool_use、refusal 等。Claude Agent SDK 的 ResultMessage 更进一步,携带了一个 subtype,用于区分 success、error_max_turns 和 error_during_execution。
因此,单次 LLM 调用 清楚地知道它为什么停止。问题在于,这个信号存在于你实际操作的对象下方一两层。在 40 轮 Agent 运行的第 14 轮出现 finish_reason: length,只告诉你单次模型调用达到了 token 上限。它没有告诉你 Agent —— 即包装了数十次此类调用、工具调用、重试和分支的编排循环 —— 是成功了、放弃了还是升级了。
终端原因必须被提升到循环边界。Agent 流程,也就是你编写 SLO 所针对的单元,必须是发出类型化退出信号的单元。而几乎没有编排层这样做。框架将控制权返回给调用者,调用者看到循环结束,模型层丰富的枚举早已被扁平化为“运行结束”。
这种扁平化就是 Bug 所在。修复方案不是建立新的基础设施,而是拒绝丢弃你已经拥有的信号。
- https://opentelemetry.io/blog/2025/ai-agent-observability/
- https://arxiv.org/abs/2503.13657
- https://sky.cs.berkeley.edu/project/mast/
- https://blog.sentry.io/ai-agent-observability-developers-guide-to-agent-monitoring/
- https://www.langchain.com/articles/agent-observability
- https://futureagi.com/glossary/tool-timeout/
- https://code.claude.com/docs/en/agent-sdk/agent-loop
- https://sre.google/workbook/implementing-slos/
- https://galileo.ai/blog/why-multi-agent-systems-fail
