容错与恢复,是给 Agent 装安全气囊和备用刹车
上一章讲「目标设定与监控」,相当于给 Agent 装了方向盘和仪表盘:它知道自己要去哪,也知道当前状态好不好。
这一章讲「容错与恢复」,相当于给 Agent 装上安全气囊、ABS 防抱死、备用刹车、黑匣子,以及出事故后自动呼叫救援的系统。
一句话总结: 容错与恢复不是简单写几个
try-except,而是要假设 Agent 一定会出错,然后提前设计好「怎么发现、怎么止血、怎么降级、怎么回滚、怎么复盘」。
传统程序出错,通常会抛异常、返回 500、打印错误日志。Agent 不一样。它最危险的错误,往往是表面看起来一切正常,但结果已经错了。
API 返回 200。
JSON 格式合法。
工具调用成功。
Agent 还很自信地给出了答案。
但字段是编的,参数是错的,成本烧爆了,或者下游 Agent 已经被错误结果带偏了。这类错误不会触发普通异常处理。生产级 Agent 要从「出错后报错」升级到「出错前限速、出错中止血、出错后恢复、恢复后复盘」。
二、为什么需要它:Agent 最可怕的不是失败,而是静默失败
先用一个生活场景理解。
你请一个助理帮你订机票。网站打不开、付款失败、航班查询超时,这些失败很明显,助理会告诉你:「订不了。」
更危险的是:他订到了票,日期却错了;他以为订成功了,其实只是保存了草稿;他把护照信息发给了错误服务商;或者价格比预算高了 10 倍。
这些问题表面上都「完成了任务」,但实际结果比失败更糟。Agent 也是这样。
try:
result = call_tool()
except Exception:
result = fallback_tool()这只能处理网络抖动、接口超时、限流。真正麻烦的是这些不会抛异常的失败:
| 失败类型 | 表面看起来 | 实际发生了什么 |
|---|---|---|
| 内容幻觉 | 返回 200,格式正确 | 字段值是模型编的 |
| 参数错 | 工具调用成功 | 参数来自错误上下文 |
| 方向跑偏 | 每一步都合理 | 离用户真实目标越来越远 |
| 死循环 | 每次调用都成功 | 没有任何有效进展 |
| 预算爆炸 | 最后完成了 | 花了本不该花的钱 |
| 级联污染 | 单个结果看似合理 | 下游 Agent 信了,错误被放大 |
所以,2026 年做 Agent 容错,重点已经从「抓异常」变成了:
假设错误一定会发生。
假设错误不一定会抛异常。
假设错误会沿着工具链、Agent 链和业务流程扩散。
然后提前设计止血路径。三、它到底在做什么:四层防线
容错与恢复不是一个单点功能,而是一组防线。
| 防线 | 处理的问题 | 常见手段 |
|---|---|---|
| 显性失败处理 | 系统明确告诉你「我坏了」 | 重试、回退、熔断、超时控制 |
| 静默失败发现 | 系统没报错,但结果可能错了 | Schema 校验、业务断言、Verifier、预算护栏、进度检测 |
| 自动恢复 | 失败后不能只报错 | 降级、回滚、Saga 补偿、人工升级 |
| 事故复盘 | 防止同类问题再次发生 | Trace 保存、根因分析、评估用例、CI 回归 |
只要 Agent 的动作有副作用,就必须设计恢复。
只要 Agent 的输出会被下游继续使用,就必须设计校验。四、先把异常分类:7 种 Agent 失败模式
做容错前,先别急着写代码。第一步是把「错误」拆开。不同错误,处理方式完全不同。
| 类型 | 典型表现 | 是否抛异常 | 主要对策 |
|---|---|---|---|
| 瞬态故障 | 网络抖动、限流、超时 | 会 | 指数退避重试 |
| 系统性故障 | 服务下线、配额耗尽、模型停服 | 会 | 跨厂商回退 + 熔断 |
| 工具语义错误 | 返回 200,但内容错 | 不会 | Schema 校验 + 业务断言 |
| 模型幻觉 | 编造字段、工具名、参数 | 不会 | Verifier + 拒识机制 |
| 死循环 / 失控 | 反复尝试,无进展 | 不会 | 预算护栏 + 进度检测 |
| 级联放大 | 多 Agent 互相信任,错误传染 | 不会 | 信任链验证 + Saga 补偿 |
| 安全攻击 | Prompt Injection、Tool Poisoning | 不一定 | 零信任 + 沙箱隔离 |
新手最容易只处理前两类。真正烧钱、出事故、影响业务的,往往是后五类。
不抛异常 ≠ 没有错误。
工具成功 ≠ 任务正确。
Agent 自信 ≠ 结果可信。五、核心防线怎么落到工程里
1. 重试、回退、熔断器三件套
| 手段 | 解决什么 | 不解决什么 | 类比 |
|---|---|---|---|
| 重试 Retry | 瞬态故障 | 系统性故障、语义错误 | 电梯没来,再按一次 |
| 回退 Fallback | 主服务不可用 | 连环故障、质量退化 | 电梯坏了,走楼梯 |
| 熔断 Circuit Breaker | 防止雪崩扩散 | 业务补偿 | 楼梯也坏了,封楼止损 |
重试只能处理「值得重试」的错误。网络抖动可以重试;参数错误、权限错误、Schema 错误,重试 100 次也还是错。
只重试瞬态错误。
必须有次数上限。
必须用指数退避。
不要 catch-all。回退也不是「同一个模型再问一遍」,而是换厂商、换区域、换工具、降级为简单模式,或者转人工。熔断器除了看 HTTP 失败率,还要看质量评分、P95 延迟、单次任务成本、重试次数和用户负反馈率。
2. 校验门、预算护栏和进度检测
Agent 最容易犯的错误之一,是把 LLM 生成的参数直接丢给工具。
args = llm_output["tool_args"]
return await execute_tool(tool_name, args)正确做法是:所有工具调用必须经过校验门。
| 检查点 | 作用 |
|---|---|
| 工具名白名单 | 不存在的工具不能调用 |
| 参数 Schema | 格式不对直接拒绝 |
| 业务断言 | 金额超限、权限不符、账号不匹配时中止 |
| 权威源核对 | 关键字段回原始请求、数据库或业务系统确认 |
预算护栏解决另一类问题:Agent 不是「做错」,而是「一直做」。
| 预算维度 | 触发后动作 |
|---|---|
| 迭代次数 | 硬中止或转人工 |
| Token | 降级摘要或中止 |
| 成本 | 硬中止 |
| 时间 | 超时中止 |
预算护栏必须是硬中止,不是温柔提醒。死循环检测也一样,重点不是看 Agent 有没有动作,而是看状态有没有发生有意义的变化。
3. Saga、信任链和沙箱
很多 Agent 任务不是一步完成的,而是一串有副作用的步骤。比如订单退款:
1. 查询订单
2. 校验退款资格
3. 冻结退款金额
4. 调用支付平台退款
5. 更新订单状态
6. 通知用户如果第 4 步成功了,第 5 步失败了,钱已经退了,但订单状态没更新。系统进入了不一致状态。
这类问题要用 Saga 模式:
每个有副作用的步骤,都必须配一个补偿动作。
如果后面失败,就按相反顺序补偿前面已经成功的步骤。| 正向动作 | 补偿动作 |
|---|---|
| 冻结资金 | 释放冻结 |
| 创建订单 | 取消订单 |
| 扣款 | 退款 |
| 发通知 | 发更正通知 |
| 写入状态 | 写入回滚状态 |
Saga 有三条铁律:每个有副作用的步骤都要有补偿动作;每一步都必须幂等;补偿失败必须升级人工。
多 Agent 系统还要处理信任链。上游 Agent 的输出只能当参考,不能当真相。金额、用户 ID、账号、权限、订单状态、审批结果、合规判断,都要回权威源读取。
最后是权限隔离。只要 Agent 能执行代码、读写文件、访问网络,就必须进入沙箱,并用最小权限运行。
| 设计点 | 做法 |
|---|---|
| 能力声明 | 明确可读路径、可写路径、允许域名、允许工具和凭据命名空间 |
| 默认拒绝 | 默认没有文件、网络、工具和凭据权限 |
| 代码沙箱 | LLM 生成代码不能跑在主服务进程里 |
| 资源限制 | CPU、内存、网络、临时目录和任务生命周期都要受控 |
六、完整案例:一个可靠的退款 Agent 怎么设计
用户输入:
帮用户 order_123 退款 299 元。错误版本很简单:
1. Agent 理解用户请求
2. 调用退款工具
3. 更新订单状态
4. 发通知问题也很明显:金额可能解析错;订单可能不允许退款;退款成功但状态更新失败;通知失败后没人补发;重试可能导致重复退款;没有预算限制;没有 trace;也没有人工升级路径。
生产级流程应该长这样:
| 环节 | 设计要求 | 失败时怎么处理 |
|---|---|---|
| 请求校验 | order_id、金额、原因都过 Schema | 拒绝或要求补充信息 |
| 权威源核对 | 金额、订单状态从订单系统读取 | 不匹配则拒绝并升级人工 |
| 预算控制 | 限制迭代次数、成本和耗时 | 超限中止 |
| Saga 补偿 | 冻结、退款、状态更新、通知都有补偿动作 | 逆序补偿 |
| 失败升级 | 钱已动但状态不一致时必须人工介入 | 写入 incident trace |
这个案例体现的不是「多写点异常处理」,而是完整的可靠性设计:
参数先校验。
关键字段回权威源确认。
金额超限转人工。
多步骤用 Saga。
每一步可补偿、可幂等。
失败有 trace,补偿失败升级人工。
预算和时间有硬上限。涉及钱、订单、权限、数据写入的 Agent,不允许只靠 try-except。
七、和上一篇 / 相关模式的关系:这是 Agent 自我进化闭环的一环
第 12 章不是孤立的。它和第 4、9、11 章正好组成一个完整闭环。
| 齿轮 | 解决什么 | 时间尺度 | 推荐位置 |
|---|---|---|---|
| 目标监控 | 发现当前是否跑偏 | 毫秒到秒 | 同进程 + 异步上报 |
| 异常恢复 | 先止血、降级、回滚 | 秒到分钟 | 同进程拦截器 |
| 反思 | 分析为什么失败 | 分钟到小时 | 独立 Worker |
| 学习 | 更新策略、提示词、评估集 | 小时到天 | 批处理任务 |
新手最容易犯的错误,是把这四件事都塞进 Agent 主循环。真正成熟的系统会形成「伤疤数据集」:
生产失败 → 记录 trace → 异步反思 → 生成回归用例 → 加入 CI → 防止同类失败再次上线八、生产就绪检查表:上线前最后一关
| 检查项 | Demo 级 | 生产级 |
|---|---|---|
| Trace | 只 print 日志 | 全链路 span 上传观测平台 |
| 重试 | catch-all | 类型白名单 + 指数退避 + 上限 |
| 回退 | 无或同厂商 | 跨厂商 / 跨区域 / 转人工 |
| 熔断 | 只看 HTTP 错误 | HTTP + 质量分 + 成本 + 延迟 |
| Schema 校验 | LLM dict 直传 | Pydantic / JSON Schema 全覆盖 |
| 预算护栏 | 没有 | 步数 / Token / 成本 / 时间四维上限 |
| 死循环检测 | 没有 | 语义相似度检测进展 |
| 多步骤事务 | 顺序执行 | Saga + 补偿 + 幂等 |
| 权限控制 | 一个万能 Agent | 最小权限 + capability 声明 |
| 代码执行 | 主进程运行 | 容器 / VM 沙箱 |
| 失败学习 | 人工修一次 | Trace → 反思 → 回归测试 |
如果这些检查项大部分还是 Demo 级,就不要急着上线。Agent 系统最危险的阶段,往往不是「完全没做防护」,而是「做了一点点,于是误以为够用了」。
容错与恢复不是给 Agent 包一层
try-except,而是默认它一定会失败,然后提前设计好校验、限速、降级、补偿、隔离、追踪和复盘。能自己止血、能安全回滚、能从事故里长出回归测试的 Agent,才有资格进入生产环境。