Agent 执行失败后怎么恢复:重试不是万能的
数据截至 2026-07,规范与各项目能力以官方文档当前版本为准。
Agent 失败之后要不要重试,取决于这次失败是”同样的输入再来一次可能成功”,还是”同样的输入再来一万次也是这个结果”。前者是瞬时故障,重试是对的;后者是语义失败,重试只是在用更多的时间和 token 换同一个错误,还会把日志淹掉。把这两类分开,是错误处理里回报最高的一步。
先承认一个相当普遍的做法:给工具调用套一层 try-except,捕获所有异常,退避几秒重试三次,三次不行就抛出去。这层保护有价值,它确实能挡掉大部分网络抖动和限流。问题在于它把所有失败都当成同一种失败对待——模型把参数填错了会重试,权限不够会重试,业务规则不允许的操作也会重试,最后重试次数用完,报出来的还是最初那条错误,中间白白多花了三倍的时间。更麻烦的是,如果那个工具不是幂等的,三次重试可能已经在下游产生了三条真实记录。
一、先把失败拆开:五类失败,处理方式完全不同
Agent 运行时遇到的失败,按”重跑一次会不会不一样”分成下面五类,后面所有的处理策略都建立在这个分类上:
- 瞬时故障:网络超时、连接被重置、上游服务偶发 5xx、限流返回 429。特征是错误跟你的请求内容无关,换个时间点大概率就过了。这类是重试的正当适用范围。
- 参数错误:模型给工具填了不存在的字段、日期格式不对、必填项漏了。特征是错误信息里通常写清楚了哪里不对。原样重试没有意义,但把错误信息喂回模型让它改一版参数很可能成功——这是”带修正的重试”,跟原样重试是两回事。
- 权限与配置错误:密钥失效、账号没开通某个接口、访问的资源不属于当前租户。这类靠 Agent 自己转不出来,重试只是浪费配额,应该立刻停下来报警。
- 业务规则拒绝:库存不足、订单已关闭、审批流程不允许当前状态跳转。接口返回的是”我理解你的请求,但按规则不能做”。这类要么换一条路径,要么交给人决定,重试属于对着规则硬撞。
- Agent 自身的规划失败:工具都调成功了,但组合出来的结果不对,或者绕了十几步陷在一个循环里出不来。这类最难,也最不该指望重试——重跑一遍通常会得到相似的错误路径。
这个分类要落到代码里才有用。 实际做法是在每个工具的封装层里,把上游返回的异常映射成自己定义的错误类型,比如 TransientError / InvalidArgument / PermissionDenied / BusinessRejected / Unknown,然后由统一的恢复逻辑按类型分派。不做这层映射,上层拿到的永远是一个笼统的 Exception,只能一刀切地重试。
顺带说一句,Unknown 这一类要保留,而且默认按”不重试、直接上报”处理。把没见过的错误默认当成瞬时故障重试,是很多线上事故的起点。
二、真要重试,怎么写才不添乱
确认属于瞬时故障之后,重试本身也有几个容易踩的点。
退避要带抖动。 固定间隔重试在单机测试时看不出问题,一旦有几十个 Agent 实例同时被同一次上游抖动打中,它们会在同一秒集体重试,把刚刚恢复的服务再压垮一次。常规做法是指数退避加随机抖动:第一次等约 1 秒,之后逐次翻倍,每次在计算出的等待时间上再叠加一个随机偏移量。
尊重服务端给的信号。 很多接口在返回限流错误时会在响应头里带上建议的等待秒数。如果对方明确告诉了你该等多久,就按它说的等,别用自己那套退避曲线去猜。
次数上限之外还要有时间上限。 只设”最多重试 3 次”是不够的,如果每次调用本身要等 60 秒才超时,三次重试就是三分多钟,对一个交互式任务来说已经等于卡死了。给整个任务设一个总的时间预算,预算用完就停,不管重试次数有没有用满。
重试要留痕。 每次重试至少记下:第几次、上一次的错误类型、这次等了多久、耗时多少。没有这些记录,线上出现”任务偶尔特别慢”时你根本无从判断慢在哪一步。
三、重试之前必须先回答:这一步重复做会不会出事
这是重试机制里最容易被跳过、后果又最严重的一环。超时不等于没执行。 一个”创建订单”的请求超时了,可能是请求根本没到服务端,也可能是服务端已经建好了订单、只是返回的响应丢在了路上。前者重试是对的,后者重试就会多出一张订单。
判断标准很简单:这个工具能不能安全地被调用两次。 只读查询天然安全;写操作则要看服务端是否支持幂等。可行的做法有下面几种:
- 用服务端提供的幂等键。不少写接口支持传一个客户端生成的唯一标识,服务端在一段时间内对同一个标识只执行一次。有这个能力就优先用,它是最干净的方案。
- 先查后写。重试前先查一次目标记录是否已经存在,存在就直接把已有结果当成成功返回。这个方案有竞态窗口,但对大多数低并发的 Agent 场景够用。
- 自己维护执行记录。在 Agent 侧记一张”这一步用这组参数已经执行过、结果是什么”的表,重试时先查表。这也是断点续跑要用的同一份数据,可以合并实现,具体存什么参见长时运行的 Agent 怎么做断点续跑。
- 确认做不到幂等的,就不要自动重试。把它标成”需要人工确认是否已生效”,交给人去核对。这不丢人,比悄悄制造重复数据强得多。
工具注册的时候顺手给每个工具打一个”是否可安全重试”的标记,恢复逻辑读这个标记决定要不要动手,比在恢复逻辑里写一堆 if-else 判断工具名字要清爽。
四、语义失败要换策略,而不是换一次运气
参数错误、业务规则拒绝、规划失败这三类,共同点是重跑同样的输入不会有新结果。它们需要的是改变下一次尝试的输入。
对参数错误,最直接的办法是把服务端返回的错误原文(去掉敏感信息之后)作为一条工具执行结果回填进对话,让模型看到自己错在哪。实践中这一步的成功率不低,因为多数接口的报错已经写明了字段名。要注意设一个”带修正的重试”上限,一般两到三次,超过之后模型往往开始在几个错误答案之间来回横跳,再试下去只是烧 token。
对业务规则拒绝,正确的动作通常不是让模型再想办法,而是让它明确地把”这条路走不通,原因是什么”作为结论返回。有些团队会让 Agent 在被拒绝后自动去尝试绕过规则的替代路径,这在内部工具上或许可以接受,一旦涉及外部业务系统就相当危险——你等于在鼓励它绕开风控。
对规划失败,先要能检测到。几个实用的信号:同一个工具用几乎相同的参数被连续调用了三次以上、步数超过了这类任务的经验上限、连续若干步之后关键状态没有任何变化。检测到之后应该中断当前轨迹,而不是让它继续绕。至于中断后是换个提示词重开、降级到更简单的固定流程,还是直接交给人,取决于任务的价值密度。如何定位这类循环,多智能体框架怎么调试里有更具体的手段。
五、失败发生在半路:回滚、补偿,还是就地封存
单步失败好办,难的是任务跑到第七步失败,前面六步已经在真实世界里留下了痕迹——发了邮件、改了数据库、扣了库存。这时候有三条路:
回滚是理论上最干净的,但对 Agent 来说往往不可行。已经发出去的邮件收不回来,调用过的外部接口大多没有配套的撤销接口。只有当所有副作用都落在你自己的事务性存储里时,回滚才真正成立。
补偿是更现实的选择:为每个有副作用的步骤配一个”抵消动作”,失败时按相反顺序执行。下了单就取消单,扣了库存就还库存。代价是补偿动作本身也会失败,你得决定补偿失败之后怎么办——通常是记一条待人工处理的记录,别再往下套第二层自动补偿。
就地封存在很多场景里其实是最优解:什么都不撤销,把当前的执行状态、已经完成的步骤、失败的原因完整存下来,标为待处理,等人来看。适用于副作用本身无害(比如只是写了几条草稿)、或者撤销的风险高于保留的场景。
选哪一条不该由框架决定,而应该在设计任务时逐个步骤想清楚:这一步做完之后如果后面失败了,我希望它保留还是撤销。把这个决定写在步骤定义旁边,比事后在异常处理里临时判断可靠。
六、什么时候该停下来交给人
自动恢复要有边界,几个明确该停手的信号:
- 恢复动作本身连续失败(重试失败了,补偿也失败了)。
- 累计消耗超过了给这个任务的预算,无论是时间、步数还是调用次数。
- 遇到权限、配置这类 Agent 转不出来的错误。
- 涉及资金、对外发布、删除数据这类做错了撤不回来的动作。
交给人的时候,交接质量决定了这套机制有没有用。只丢一句”任务失败”等于把排查成本全推给接手的人。至少要带上:失败在第几步、这一步的输入是什么、上游返回的原始错误、已经尝试过哪些恢复动作、当前可供选择的处理选项。介入点怎么设计、状态怎么冻住再解冻,Agent 里的人工介入怎么设计那篇讲得更细。
七、框架能帮到哪一步
这部分需要先说清口径:下面关于三个框架的比较,来自 2026 年的多篇第三方实战对比文章,不是各项目官方文档的逐条结论,具体能力请以各自官方文档当前版本为准。
按第三方口径,LangGraph 在这件事上给的原语最直接:它把流程建模成有向图,节点是函数或模型调用,边定义控制流,状态以带类型的字典在节点间传递,并内置了检查点、流式输出和人工介入原语,支持可持久化的长时运行工作流。带反馈环的循环任务上它被认为表现更好——而”失败之后回到某个节点重来”本质上就是一个反馈环。代价是同一份对比里也提到,它的学习曲线在三者中最陡。
CrewAI 走的是角色分工路线,一队有明确角色的 agent 串行或并行推进,上手门槛在三者中最低。同一批对比材料里提到,它在 2025 年增加了 Flows 这种事件驱动的 pipeline 模式,面向更可预测的生产型负载——不少更早的对比文章没有覆盖这一点,所以”CrewAI 只适合做原型”这个旧结论现在不宜照搬。至于循环任务,第三方口径认为它技术上支持,但调试起来比较费劲。
AutoGen / AG2 基于对话,多个 agent 互相交谈达成共识,对话模式最丰富。需要在选型时知道的是:据第三方材料,微软已把重心转向更大的 Agent Framework,AutoGen 的主要新功能开发停止,进入维护模式,仍有 bug 修复和安全补丁。有实践者据此认为 2026 年不宜把它作为新项目的起点。这不意味着它不能用,存量项目也不必急着迁移,但新项目选型时这条信息应该摆在桌面上。三者更完整的横向比较见多智能体框架怎么选。
还有一条值得前置的提醒,同样来自第三方实战总结:如果你的 Agent 只是单个角色、只调一两个工具,直接用厂商的 Agent SDK 往往比引入多智能体框架更快,错误处理自己写几十行就够了,不必为了”有 checkpoint”而背上整个框架的复杂度。这个取舍在Agent SDK 还是多智能体框架里有展开。
八、可以先做起来的最小方案
如果现在手上的 Agent 还没有任何错误处理,按下面的顺序加,性价比最高:
- 在工具封装层做错误分类,至少区分出”瞬时”和”其他”两类。
- 只对瞬时故障做指数退避加抖动的重试,设次数上限和总时间预算。
- 给每个工具打上”能否安全重试”的标记,标了不能的一律不自动重试。
- 参数错误走一次”把报错回填给模型”的修正重试,上限两到三次。
- 加一个循环检测:同参数连续调用超过阈值就中断。
- 失败退出时把执行现场完整落盘,附上足够人接手的上下文。
这六步都不依赖特定框架,纯手写也就是一两百行的量级。做完之后再考虑要不要上框架的检查点能力,判断依据会清晰很多。
九、这套做法的局限
得诚实说几个这篇解决不了的问题。
错误分类依赖上游接口的错误信息足够规范,现实中不少内部接口无论什么问题都返回 500 加一句中文描述,这种情况下分类只能靠匹配错误文本,脆弱且需要持续维护。
幂等这件事,很多时候不在你的控制范围内——如果下游系统就是不支持幂等键、也没有可靠的查询接口,那就只能靠人工核对兜底,没有纯技术的解法。
自动补偿越复杂越容易出新问题,补偿链本身也是一段需要测试的代码,而它平时几乎不执行,也就意味着它平时几乎得不到验证。上线前手动触发跑一遍是必要的。
至于规划失败,目前还没有可靠的通用检测手段,前面提到的那几个信号能捞回一部分明显的死循环,但对”绕了半天最后给出一个看起来合理其实错了的结论”这种情况基本无能为力,只能靠结果侧的校验。
小结
重试只是恢复策略里的一种,而且只对瞬时故障有效,把所有失败都塞给它处理,代价是时间、token 和可能的重复副作用。做错误分类是投入最小、收益最大的一步,它决定了后面所有分支的走向。写重试之前先确认这个工具能不能安全地重复执行,做不到幂等的宁可交给人,也别让它自动跑第二遍。半路失败时先想清楚每个步骤该回滚、该补偿还是该原样封存,这个决定属于任务设计阶段,不该留到异常处理里临时拍板。框架能帮你把状态存下来、把流程画成图,但工具重复调用会不会出事、业务规则拒绝之后往哪走,这些始终得自己定。