AI Agent 工具报错后,如何重试和熔断避免死循环

Agent 工具调用失败后,不应一律重试;应先判断错误类型,再分配有限预算。达到终止条件时停止调用,熔断器打开后转入降级。能否自动重试,取决于故障是否可恢复、调用是否能够安全重放,以及剩余预算是否充足;超时、参数错误和业务拒绝不能共用一套重试逻辑。

查询工具调用超时后,模型收到错误信息,再次生成相同调用,工具又超时,错误继续回灌,最终形成循环。掘金热榜文章将容易缺失的这一层机制概括为“错误自救与熔断”。这类循环通常源于执行器缺少重复检测、预算限制和明确的终止条件,而非工具单点故障。

失控重试始于错误回灌

典型的工具调用链包括生成结构化请求、解析参数、执行工具和回传结果。模型输出、参数解析、工具超时、网络抖动以及返回数据解析,任何一环都可能失败。

如果每次失败都把原始错误重新交给模型,却没有记录已消耗的尝试次数和调用指纹,模型很可能继续生成相同请求。无退避、无上限的重试与错误信息反复回灌,还会推高日志量和 Token 成本。因此,重试决策应该由执行器控制,而不是完全交给模型临场判断。

错误类型决定处置路径

先建立失败矩阵,比捕获异常后统一调用 retry() 更可靠:

失败类型 典型表现 默认处置
瞬时依赖故障 网络抖动、超时、依赖暂时不可用 在预算内退避重试
参数或协议错误 参数缺失、格式错误、Schema 不匹配 修正参数后重新生成,不原样重试
业务拒绝 资源不存在、状态不允许、权限不足 更换路径、追问用户或停止
无任务进展 工具执行成功,但任务状态没有变化 识别无进展并切换策略
副作用结果未知 下单、支付、删除等写操作超时 查询执行状态或使用幂等机制
高风险操作未授权 缺少授权或二次确认 阻断执行,确认后才能继续

参数错误可以把结构化错误反馈给模型,让它修正参数;业务拒绝则应换工具或向用户澄清,不能伪装成临时故障继续重试。适合自动重试的主要是网络抖动、超时和暂时不可用等瞬时故障。

重试预算要覆盖调用和任务

至少需要同时设置调用级的 maxAttempts、任务级的 maxRounds、总 deadline 和 tokenBudget。调用级预算限制一段重试序列,任务预算限制整个 Agent 循环,熔断器则描述工具依赖的健康状态,三者不能互相替代。

recovery:
  maxAttempts: ${TOOL_MAX_ATTEMPTS}
  maxRounds: ${TASK_MAX_ROUNDS}
  deadline: ${TASK_DEADLINE}
  tokenBudget: ${TASK_TOKEN_BUDGET}
  backoff:
    base: ${TOOL_BACKOFF_BASE}
    cap: ${TOOL_BACKOFF_CAP}
    jitter: true

maxAttempts 约束一个逻辑调用的总尝试次数,maxRounds 约束任务可以经历多少轮规划与执行,deadline 是墙钟时间上限,tokenBudget 则限制任务总 Token 消耗。具体数值应来自工具 SLA、业务成本和幂等性,不能给出通用固定值。

还要明确 Agent 层和 SDK 层各自是否负责重试。如果两层都自动重试,实际尝试次数可能成倍放大。

指数退避必须配合预算

对可重试的瞬时故障,可以采用有上限的指数退避并加入随机抖动:

delay = min(cap, base × 2^(attempt - 1) + jitter)

每次重试前都要重新检查 deadline 和剩余预算。如果剩余时间不足以完成下一次调用,就应停止并返回已有结果,而不是启动一次注定无法完成的等待。

退避只调整重试时机,不会纠正错误分类、修复错误参数或终止无进展循环。错误回灌时还应携带已消耗的预算与调用指纹,让执行器能够识别“正在重复同一件事”。

用调用指纹识别无效重试

使用工具名和规范化参数生成调用指纹,可以识别同工具、同参数的原样重复调用:

const fingerprint = hash(`${toolName}:${canonicalize(args)}`);

规范化可以统一对象字段顺序,但不能删除具有业务含义的参数。指纹还应限定在当前任务或会话内,避免不同任务之间的合法同名调用互相干扰。

如果同一指纹反复出现,而且错误码、返回状态和任务状态都没有变化,就应认定调用没有取得进展,停止原样重试。此时可以让模型修正参数、更换工具或向用户追问。对于状态持续变化的异步轮询,不应误判为重复调用,因为任务实际上仍在推进。

写操作要先确认执行结果

下单、支付、删除等写操作超时,只表示结果未知,不能直接认定执行失败。此时应优先通过请求标识查询执行状态,或者使用工具提供的幂等机制,避免重复扣费和重复写入。

如果无法确认副作用是否发生,执行器应停止自动重试,并把状态标记为“结果未知”,不能随意报告成功或失败。

终止条件应写进执行器

以下条件都应由执行器判断:

  • 错误不可重试:停止当前路径,修正参数或向用户澄清。
  • maxAttempts 耗尽:结束该工具的重试序列。
  • maxRounds、deadline 或 tokenBudget 耗尽:停止整个任务并输出部分结果。
  • 重复调用没有进展:切换工具或处理策略。
  • 高风险操作未确认:阻断执行并请求授权。
  • 副作用结果无法核对:停止盲目重试,标记结果未知。
  • 熔断器已经打开:停止调用该依赖,转入降级路径。

这些条件应当是可执行的状态机规则,而不是只写在提示词里。模型可以提出下一步建议,但是否继续调用必须由执行器决定。任务预算耗尽时,输出还应区分“已完成”“未执行”和“结果未知”,避免把不确定状态包装成成功。

熔断后转到备用路径

熔断器可以使用 Closed、Open、Half-Open 三种状态:Closed 正常放行,Open 使调用直接失败且不再触发重试,Half-Open 只放行探测请求,用于试探工具是否恢复。

熔断阈值应结合工具 SLA 和失败成本配置,不存在通用的固定次数。熔断器打开后,应改用替代工具、返回已有部分结果或向用户澄清;非关键步骤失败,不必直接判定整个任务失败。但支付、删除和管理员操作不能在降级时被静默替换,替代路径仍要满足授权与幂等要求。

我的判断是,可靠的 Agent 不必以永不失败为目标。系统应明确判断何时继续、何时停止,并让每次失败都进入受预算和终止条件约束的处置流程。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容