跳到主要内容

Agent 降级模式规范是你没有撰写的文档

· 阅读需 12 分钟
Tian Pan
Software Engineer

当搜索索引失效、供应商 API 对你限流、数据库只读副本出现延迟,或者下游微服务开始返回 503 错误时,你的智能体(agent)必须决定该做什么。在大多数生产环境的智能体系统中,这个决定从未被真正做出。它是被“默默继承”下来的——往往来自于编写工具封装(tool wrapper)的工程师在项目第三周周二下午 4 点随手写下的代码。

其结果就是你的客户最终替你写出的内容:一个 Reddit 帖子、一份客服对话记录,或者是新闻稿中的一段引述。“助手告诉我余额是 0 美元,但其实我的账户没问题——结果发现是他们的查询服务挂了。”那段话就是你的团队没写的降级模式规范。现在它公开了,它属于客户了,而且你的工程部门在接下来的整个季度里都将忙于应对它。

这种模式在发布生产级智能体的团队中屡见不鲜。正常流程(happy-path)的规范非常细致:工具模式(tool schemas)、提示词模板、针对设计好的工作流的评估套件。而降级模式下的行为——即当其中一个工具宕机、变慢、被限流或返回过期数据时智能体的表现——则是隐式的。它散落在各个工具封装的 catch 块中,隐藏在从 HTTP 库继承的默认超时设置里,或是从 Stack Overflow 答案中复制来的重试次数中。五个工程师交付了五种不同的回退(fallback)行为。智能体在降级情况下的整体表现是这些个人选择的笛卡尔积,而且从来没有人完整地审阅过它。

你的封装程序无意中编码的故障清单

随意翻阅任何智能体代码库中的工具封装,你都会发现针对同一类故障至少有五种不同的反应:

  • 静默失败 (Silent fail)。封装程序捕获异常,返回 null 或空字符串,智能体将缺失字段视为一个“存在但未知”的值进行推理。用户得到了一个极其自信的错误答案,却没有任何迹象表明哪里出了问题。
  • 无限重试 (Retry forever)。封装程序有一个带指数退避且没有上限的重试循环。原本 30 秒的停机变成了 4 分钟的对话停滞。智能体的工具预算被消耗在尝试让同一个调用成功上,而用户只能看着加载图标旋转。
  • 无警告的部分回答 (Partial answer with no warning)。封装程序返回 {"data": null, "error": "timeout"},而提示词模板没有提到如何处理这种格式。模型选择了其中一个键(通常是错误的那个)并继续运行。
  • 通用拒绝 (Generic refusal)。封装程序将异常向上抛出,智能体层捕获它,用户看到“对不起,我现在无法提供帮助”——这与安全拒绝、权限失效或模型宕机无法区分。
  • 自信的错误 (Confidently wrong)。封装程序有一个没人记录的过时缓存回退。数据是昨天的。提示词没有信号表明这是回退数据,因此模型将其视为最新数据,答案的偏差直到三天后才被用户发现。

在孤立的情况下,每一个选择都是站得住脚的。当分布在十几个工具中叠加在一起时,它们造就了一个降级行为不可预测、未经测试、且无法向客户成功团队或监管机构解释的智能体。智能体确实有一份规范——只不过在发布之前没人读过它。

可靠性研究已经开始对此进行量化。最近的评估框架引入了诸如“平滑降级评分 (Graceful Degradation Score)”之类的指标,用于衡量在模拟依赖故障下的部分完成度;以及“崩溃起点 (Meltdown Onset Point)”,用于检测长程智能体在单个工具失败后开始产生复合错误的时刻。大多数运行这些评估的团队发现,他们智能体的降级曲线跌落悬崖的时间远早于预期。每一次,隐式规范都比团队乐观的心理模型要糟糕得多。

针对每个工具的降级模式契约

解决方案不是编写一个单一的回退函数并宣称系统足够健壮。而是要针对每个工具,明确智能体在每一类故障下的应对措施。这份契约很短——只有一页纸——而编写它的过程会强制开启那些隐式规范曾规避掉的对话。

一份可行的契约包含三列:故障类别、严重程度和降级动作。

  • 故障类别 (Failure class)。要具体。“工具报错”不是故障类别。“工具返回 429 限流”、“工具在接收首字节前超时”、“工具返回 200 但结果为空”、“工具返回的缓存数据超过了新鲜度契约”——这些才是智能体行为应该依赖的故障类别。
  • 严重程度 (Severity)。这是可恢复的、可恢复但需告知的,还是对此任务而言是致命的?在“显示我的仪表板”查询中命中过时缓存是“可恢复但需告知”的。在“我的账户余额是多少”查询中出现同样的过时缓存则是“对此任务而言是致命的”。
  • 降级动作 (Degraded action)。智能体做什么?选项包括:带退避的重试(有上限)、回退到成本更低的工具、返回带有明确说明的部分答案、提供具体原因的拒绝,或者升级给人工作业。

契约是针对每个工具的,因为正确的答案取决于该工具获取的数据。对于“总结关于 X 的文档”的查询,落后两小时的搜索索引没问题;但对于“过去一小时内有任何变化吗”的查询,这则是灾难性的。同样的工具,同样的陈旧度,却有两种不同的正确降级行为。如果一个团队在所有工具中推行全局的“如果数据陈旧,照常进行”策略,那么他们交付的就是一份如果真写下来、他们绝不会签字同意的规范。

编写契约的行为使那些此前散落在 catch 块中的决策浮出面。这就像基础设施从“由编写它的工程师随意配置”转向“由两人审查的 Terraform 清单”一样的转变。这种产物以前就存在,编码在代码中;而现在的规范化是使其显式化,以便团队可以进行治理。

告知作为一等输出

团队最常忽略的部分是面向用户的降级模式 UX。现在 Agent 处于不同的模式——客户应该能察觉到这一点。“抱歉,我无法提供帮助”是一个错误的消息,因为事实是:“我可以回答大部分问题,但目前无法连接库存系统,因此我正根据昨天的快照来提供库存水平。”这两者是不同的信号,需要用户采取不同的行动。

做得好的团队将降级告知视为一种结构化输出,而不是模型自创的字符串。Agent 的响应带有一个并行的 degraded 字段——{"missing_tools": ["inventory_lookup"], "data_freshness": "2026-04-27T14:00Z", "user_action": "verify before checkout"}——渲染层将其转化为 UX 横幅、响应中的提醒或带有原因的拒绝。模型负责发出结构化信号;前端负责将其展示出来。

这种分离非常重要,原因有两点。首先,模型在生成流畅文本时往往不可靠,容易忘记提及降级情况,尤其是在上下文压力下。将降级信息作为包装层始终设置的结构化字段(独立于模型编写的内容),可以保证告知行为一定会发生。其次,结构化字段使 SLO 变得可衡量。你很难从自然语言响应中轻易计算出“降级响应中告知降级的比例”;但通过布尔字段,你可以轻而易举地计算出来。

Cox Automotive 的自主客户服务部署就是一个具有启发性的例子:他们的 Agent 在成本和对话长度上执行熔断机制,当其中任何一项触发时,系统会优雅地将任务移交给经销商的人员,而不是以降级模式继续运行。这种移交就是降级模式下的 UX,它是设计出来的,而非即兴发挥。用户看到的是明显的过渡,而不是无声的质量断崖。

证明其确实有效的评估(Eval)

编写契约是必要的,但还不够。闭环的关键在于一套降级模式评估(Eval)套件,它模拟依赖项故障来运行 Agent 并对响应进行评分。

这与你的黄金路径(Golden-path)测试套件形态不同。输入不再是“Agent 应该处理的查询”,而是“Agent 应该以特定降级方式处理的(查询,依赖故障)对”。评分器衡量的不仅仅是回答质量——它们还在衡量 Agent 是否告知了降级、告知内容是否匹配实际的故障类别、Agent 是否保持在受限的重试范围内,以及响应是否是可恢复的,而非言之凿凿的错误。

在实践中,这意味着在工具边界注入故障:返回超时、陈旧数据、速率限制错误、畸形负载和空结果集。评估根据契约对每个场景进行评分。在告知方面,评分是二元的(“当依赖失败时,响应是否带有降级标志?”);在质量方面,评分是阶梯式的(“考虑到故障,这是否是 Agent 能给出的最佳部分答案?”)。

运行此类评估的团队通常会有两次惊讶。第一次是,他们发现 Agent 在契约规定应该告知降级时,竟然如此频繁地未能做到。第二次是,他们发现 Agent “带有告知的回答”实际上比同一个查询在正常路径下的响应更好——更实用、更具可操作性、更值得信赖。在用户信任指标上,诚实的缺陷胜过自信的捏造。那些收到“我目前无法连接库存系统,这是我所知道的信息”回复的客户会再次光顾。而那些收到言之凿凿的错误答案、随后发现真相的客户则不会。

以诚实告知为基准的 SLO

在运行时使契约具可操作性的指标是降级模式告知率:在所有工具依赖项降级的 Agent 回合中,有多大比例在结构化响应中发出了降级标志?这不同于你的延迟 SLO、可用性 SLO 或评估通过率 SLO。这个指标能捕捉到一种失效模式:仪表盘上一切看起来都很正常——工具在运行,模型在响应,延迟是绿色的——但由于包装层没有发出信号,Agent 在降级情况下正悄悄生成错误的答案。

95% 到 99% 之间的目标范围是现实的。剩余的百分之几通常是依赖状态与告知逻辑之间的竞争,或者是契约未涵盖的真实边缘案例。SLO 根据漏掉告知的成本进行校准:在低风险场景(如推荐流)中,你可以比在高风险场景(如账单 Agent、医疗分诊助手、财务建议工具)中容忍更多的隐性降级。

组织层面的失效模式是可以预见的。团队非常细致地衡量可用性和延迟,发布了一个在常见依赖故障下告知率隐含为 0% 的 Agent,并在第一次事故中发现了这个差距——通常是当客户投诉引出了一个言之凿凿的错误答案,而事后分析追踪到这是一个由于工具超时且 Agent 从未告知而导致的。修复方案不是一次性的补丁;它应该是团队从第一天起就应该在仪表盘上与其他指标一起监控的衡量标准。

在依赖失效前将其写下来

架构层面的认知是,Agent 的降级模式行为是一项产品规范。它应该得到与“快乐路径”(happy-path)规范相同的待遇:写在文档中,由承担后果的人员评审,在评估套件中测试,通过 SLO 进行监控,并随系统变化而更新。如果将其视为值班人员在故障期间临时发明的后备方案,最终结果就是客户在支持工单中替你写下这份规范。

完成这项工作的团队都有同样的感受:编写这类协议所花费的时间比预想的要少,它所强制开启的对话正是团队一直回避的,而且相关评估揭示了快乐路径评估套件已掩盖数月的特定 Bug。做的成本很小。而另一种选择的代价是,产生一份公司无法控制的规范——由掌握的信息远不如工程团队的客户编写,并且被除了那些原本能阻止它发生的人之外的所有人阅读。

如果你的 Agent 已投入生产,而你无法指明那份说明工具失效时该如何表现的文档,那么那份文档其实依然存在。只是它目前还不在你的代码库里。

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