10.09概念更新于 2026年9月17日

错误恢复与重试

工具失败、参数错误、模型误判与“做到一半”的处理。

10.09 · 课时讲解

错误恢复与重试

能自动恢复的交给程序,能自我纠正的交给模型,都不行就安全地停下来。

1 / 13

一句话理解:Agent 的错误分四层:模型 API、工具执行、模型决策、做到一半;每层由不同的角色处理,核心原则是“能自动恢复的交给程序,能自我纠正的交给模型,都不行就安全地停下来”。

传统软件出错,通常是一个请求失败、返回一个错误码。Agent 出错要复杂得多:它可能在第 12 步因为支付接口超时而失败,而前 11 步已经创建了工单、发出了通知。此时简单地“整个重来”,会给用户发第二条通知、建第二张工单。

很多 Agent 在 Demo 阶段看起来很稳,是因为 Demo 环境里接口从不超时、数据从不缺失。上线后第一周,错误恢复往往是投入最大的一块。这一课给你一套分层思路,失败类型的完整分类见 失败分类,这里聚焦 Harness 层面“怎么恢复”。

四层错误,四种处理者

层级 典型错误 谁来处理 策略
模型 API 层 限流、服务过载、网络中断、请求超时 Harness(SDK) 指数退避重试;持续失败则暂停任务
工具执行层 接口超时、5xx、参数不合法、数据不存在 Harness 捕获,交给模型判断 瞬时错误程序重试;业务错误以“错误结果”返回模型
模型决策层 选错工具、编造参数、原地打转 模型自我纠正 + Harness 兜底 可行动的错误信息;重复检测;步数上限
做到一半 多步写操作中途失败或进程崩溃 Harness 的状态与幂等设计 检查点续跑;幂等键;补偿操作

模型 API 层:交给 SDK,但要知道边界

Claude API 的官方 SDK 会对连接错误、限流(429)和 5xx 服务端错误自动做指数退避重试,默认重试两次,并遵守 retry-after 响应头;次数可以配置。你需要额外想清楚的是:

  • 重试耗尽之后:长任务不应该因为一次 529(服务过载)就整体失败,应该把任务状态保存下来,稍后续跑。
  • 不该重试的错误:400 参数错误、401 认证失败重试一百次也不会好,应立即停止并报警。
  • 花费上限类的 429:Claude API 文档特别指出,触达用量档位的月度花费上限时返回的 429 没有 retry-after,在额度恢复前会一直失败,重试无意义。

工具执行层:把错误“翻译”给模型

工具出错时,最常见的坏做法有两个:让异常直接把程序打崩,或者吞掉错误返回空结果(模型会以为“查无记录”并据此下结论)。

正确做法是把错误作为工具结果返回,并明确标记为错误。Claude API 的 tool_result 提供了 is_error 字段,官方文档建议写“可行动”的错误信息:说明出了什么问题、下一步可以尝试什么。文档还提到,当工具调用缺少必填参数时,把错误返回给模型,模型通常会补全信息后重试两三次,然后才向用户致歉;要从根源上杜绝参数不合法,可以在工具定义上启用严格模式(strict: true)。

同一个错误,两种写法:

写法 模型看到的内容 模型可能的反应
差 error 原样重试,或放弃
差 空数组 误判为“该订单没有支付记录”,得出错误结论
好 支付服务超时(已重试 2 次)。订单基础信息可正常查询,可先基于订单状态给出初步判断,并提示用户支付数据暂缺 降级回答,并诚实说明数据缺口
好 订单号 ORD10O1 格式不正确:第 6 位是字母 O,应为数字 0 修正参数后重试

一个实用分工:瞬时错误(超时、5xx)由工具层自己先重试一两次;重试后仍失败、或属于业务错误(数据不存在、权限不足),再交给模型决定。 这样既不浪费模型的步数,又保留了模型换路径的能力。

模型决策层:检测“原地打转”

模型有时会陷入循环:用同样的参数反复调用同一个工具,或在两个工具之间来回切换。Harness 应该检测这些信号:

  • 同一工具、同一参数在最近 N 步内重复出现;
  • 连续多步没有新的工具调用结果进入上下文;
  • 步数或花费接近上限,但任务清单没有推进。

检测到后,可以先给模型一条提示(“你已用相同参数查询 3 次,结果不会变化,请换一种方式或说明无法完成”),仍无改善就停止并交给人。更多停止策略见 停止条件,何时升级到人工见 什么时候停下来找人。

最难的情况:做到一半

来看可执行的售后处理 Agent 的一次运行:

售后处理 Agent 在第 4 步失败时的状态,以及整体重跑、从失败处续跑、补偿回滚三种恢复选择 步骤 1 查询订单 ✔ 完成 步骤 2 查询支付记录 ✔ 完成 步骤 3 创建售后工单 ✔ 工单 T-8812 步骤 4 发送短信通知 ✘ 短信网关超时 步骤 5 更新订单状态 未执行 整体重跑 从步骤 1 重新开始 风险:创建第二张工单 推荐 从失败处续跑 跳过已完成,重试步骤 4 前提:知道哪些已做过 补偿回滚 关闭 T-8812,标记失败 前提:写操作都能撤销 续跑依赖三件事:先记意图再执行再记结果 · 写操作幂等 · 恢复前重新确认现实
图 1 · 做到一半:前 3 步已产生副作用,恢复方式决定会不会重复执行

此时有三种恢复选择:

选择 做法 风险
整体重跑 从步骤 1 重新开始 创建第二张工单
从失败处续跑 跳过已完成的步骤,重试步骤 4 需要知道“哪些已经做过”
补偿回滚 关闭工单 T-8812,标记任务失败 需要每个写操作都有对应的撤销动作

绝大多数业务场景应该选“从失败处续跑”,它依赖三个设计:

1. 先记意图,再执行,再记结果。 每个写操作执行前,先把“准备做什么”写进任务状态;执行后写入外部系统返回的 ID。进程在任何时刻崩溃,恢复时都能判断这一步是“没做”“做了一半”还是“做完了”。

2. 写操作必须幂等。 给每个写操作生成一个幂等键(例如“任务 ID + 步骤名”),外部系统收到重复的键时返回第一次的结果,而不是再创建一次。

3. 恢复前重新确认现实。 距离上次运行可能已经过去几个小时,订单可能已被人工处理。续跑前先重新查询关键业务状态,而不是相信记忆里的旧数据。

下面是一段伪代码,用来说明这三点如何组合(不对应任何特定 SDK):

# 伪代码:带意图记录与幂等键的写操作
def run_write_step(task, step_name, action, params):
    record = task.steps.get(step_name)
    if record and record.status == "done":
        return record.result                      # 已完成:直接复用结果

    idempotency_key = f"{task.id}:{step_name}"
    task.save_step(step_name, status="started", params=params)   # 先记意图

    result = action(params, idempotency_key=idempotency_key)     # 外部系统按键去重
    task.save_step(step_name, status="done", result=result)      # 再记结果
    return result

任务状态的结构设计与检查点粒度,会在 任务状态与 Checkpoint 中详细展开。

框架里的“做到一半”陷阱

框架的恢复机制也有自己的语义,必须读懂:

  • LangGraph:官方文档明确说明,用 interrupt() 暂停后恢复时,整个节点会从头重新执行,而不是从暂停的那一行继续。因此在 interrupt() 之前的代码必须是幂等的,否则可能重复写库。
  • Claude Agent SDK:会话恢复的是对话历史,不是文件系统;因轮数或预算上限停止时,会返回 error_max_turns 或 error_max_budget_usd 这类结果类型,并带上会话 ID,可以用更高的上限恢复该会话继续。

一张恢复决策图

工具调用失败后的恢复决策:瞬时错误重试、参数错误让模型修正、业务错误换路径、结果未知的写操作先确认状态 工具调用失败 瞬时错误? 超时 · 5xx · 限流 是 否 参数或格式错误? 缺字段 · 格式不对 是 否 业务错误? 数据不存在 · 无权限 是 否 写操作且结果未知? 超时,但可能已经生效 是 工具层退避重试 最多 2 次 成功 继续执行 仍失败:交给模型,标记 is_error 返回可行动的错误信息 让模型修正参数后重试 返回错误结果 让模型换路径,或如实告知用户 先用幂等键或查询确认真实状态 不要盲目重试 已确认 再决定是否重试 无法确认 停止,交给人工
图 2 · 恢复决策图:先判断错误类型,再决定由程序重试、交给模型,还是交给人

最后一个分支最容易被忽略:写操作超时不等于没执行。 支付、发送、创建类操作超时后盲目重试,是重复扣款和重复通知的主要来源。

常见误区

  • “失败了就整个重跑。” 只读任务可以,包含写操作的任务几乎一定会产生重复副作用。
  • “错误信息越短越好,省 Token。” 错误信息是模型做下一步决策的唯一依据,几十个字的说明远比一次无效重试便宜。
  • “重试次数越多越稳。” 无限重试会放大故障、烧掉预算。每一层都要有上限,并且上限耗尽后有明确的去处。

产品经理检查清单

  • 每个工具的错误,模型看到的是什么文本?能据此决定下一步吗?
  • 哪些写操作支持幂等?不支持的,超时后怎么确认状态?
  • 任务中途失败,用户看到的是“失败”还是“已完成 3/5 步,第 4 步失败,可重试”?(呈现方式见 失败的呈现)
  • 恢复运行前,会重新检查哪些业务状态?
  • 模型 API 持续不可用时,长任务是直接失败,还是保存进度稍后自动续跑?

练习

为「可执行的售后处理 Agent」的 5 个工具(查询订单、查询支付记录、创建工单、发送通知、更新状态)填写下表,用时约 45 分钟:

工具 读 / 写 可能的错误 工具层是否自动重试 返回给模型的错误信息 是否幂等 做到一半时的恢复方式
创建工单 写 超时 否 创建请求超时,结果未知,请先按幂等键查询是否已创建 是(按幂等键) 查询后决定是否重试

延伸阅读

下一步

当任务要跑几十分钟甚至几个小时,错误恢复就和“后台执行、断点续跑、进度汇报”绑在一起了,这是下一课「长时间运行的任务」的主题。

试试:上下文、MCP、沙箱、Prompt 注入、外链