Agent 中途失败后怎么办:检查点、重试与幂等执行
一个 Agent 依次读取信息、生成方案、调用工具并检查结果。如果它在第三步超时,再次运行时应该从哪里开始?这比“失败后重试三次”复杂,因为超时只意味着调用方没有及时得到结果,并不意味着外部操作没有发生。
假设工具已经创建了一条业务记录,但响应在返回途中丢失。Agent 若从头再次调用,可能创建重复记录;如果直接标记完成,又可能把一次真正失败的调用当成成功。
我在LangGraph 工作流笔记中讨论过显式状态的价值。这篇把关注点缩小到一个问题:流程如何在结果不确定时恢复,而不是盲目继续。
1. 先分清流程状态与业务状态
检查点记录的是工作流已经推进到哪里,例如已完成规划、生成了候选参数、下一步准备执行什么节点。它可以帮助进程重启后恢复上下文,但并不天然知道外部系统到底发生了什么。
| 状态来源 | 能回答的问题 | 不能单独证明的事情 |
|---|---|---|
| 工作流检查点 | 哪些节点及状态已持久化 | 外部 API 是否产生副作用 |
| 工具调用日志 | 发出了什么请求、收到什么响应 | 响应丢失时对方是否已执行 |
| 业务系统记录 | 目标对象是否存在、当前状态是什么 | 它是否一定属于本次操作 |
因此,恢复时往往需要把检查点、操作标识和业务查询结果结合起来。状态快照越完整,也不意味着外部副作用就能自动获得“恰好一次”的保证。
2. 最危险的窗口在执行之后、记录之前
可以把一次工具调用简化为以下顺序:
准备参数 → 记录执行意图 → 调用外部工具 → 保存结果 → 推进工作流如果进程在工具执行前崩溃,通常可以重新尝试。如果在保存结果后崩溃,可以从记录恢复。真正棘手的是:外部动作已经生效,进程却在保存结果前退出。
为了表达这种情况,调用状态不宜只有“成功”和“失败”,还应允许“结果未知”。恢复后先核验,再决定复用结果、重试或交给人工处理。
一个用于讨论的状态转换是:
prepared → running → succeeded → failed → unknown → 核验外部结果这里的 unknown 不是可以无限重试的错误分类,而是要求补充证据的状态。
3. 操作标识要绑定业务意图
如果外部服务支持幂等键,同一次逻辑操作的所有重试应复用同一个键。每次重试都生成新的随机键,只能标记不同请求,不能防止重复副作用。
下面是一个简化的调用记录示例:
{ "operation_id": "task-demo:write-report:1", "tool": "create_report", "input_hash": "sha256:...", "status": "unknown", "external_resource_id": null, "attempt": 2}operation_id 在发起外部动作前生成并持久化;重试沿用它,同时核对输入摘要。相同标识对应不同参数时应拒绝复用,而不是返回旧结果。业务意图发生变化,则应该创建新的操作,并重新判断所需授权。
如果自己实现幂等记录,还要考虑并发占用、唯一约束、结果保存与有效期。仅在内存里缓存成功结果,无法处理进程重启;仅给数据库表增加唯一键,也不能自动把另一个系统的操作纳入同一事务。
4. 重试策略取决于错误语义
| 情况 | 更合适的处理 |
|---|---|
| 参数不合法 | 修正输入,保留校验错误供规划节点使用 |
| 身份或权限不足 | 停止执行,补齐授权条件 |
| 限流或暂时不可用 | 在工具语义允许时,按预算退避重试 |
| 请求超时且可能已经产生副作用 | 先通过操作标识查询结果 |
| 操作不可逆且无法确认结果 | 保留证据,交给人工核验 |
退避通常需要最大尝试次数、总时长上限和抖动;服务端给出 Retry-After 时应考虑它。取消状态也要参与判断,用户已经停止任务后,不应让后台重试继续执行写操作。
模型重新生成参数与程序重发同一请求也应区分。前者可能改变业务含义,不能自动当作同一次幂等重试。
5. 在 LangGraph 中划清恢复边界
使用持久化 checkpointer,可以按线程保存图的状态,并从已有进度恢复。但它不是外部 API 的事务管理器。流程恢复时,部分执行逻辑可能重放,节点内部的副作用仍然需要独立设计。
我会优先让节点职责清楚:读取证据、生成计划、校验参数、执行动作和核验结果分别承担明确工作。具有外部副作用的部分通过任务封装、稳定操作标识和业务结果查询来保护,具体方式要以当前框架版本支持的恢复语义为准。
人工确认也需要与操作绑定。确认的是某个目标、某份参数和某个动作范围;恢复时如果这些内容发生变化,应重新校验授权,而不是因为线程里存在一个“已确认”布尔值就继续执行。
6. 补偿不是万能回滚
创建的草稿可以删除,更新的配置可能恢复旧值,但已经发出的消息无法真正撤回接收者的阅读,已经触发的外部流程也未必能够完全逆转。
补偿动作本身也会失败,需要记录执行结果,并尽可能具备幂等性。设计时应明确哪些步骤可逆、哪些只能做业务补救、哪些必须人工处理,不要把“有一个撤销接口”当成事务回滚保证。
人工接管记录至少应包含:任务目标、操作标识、已确认完成的步骤、结果未知的步骤、最后一次错误,以及可用于核验的外部资源标识。接管者应该能据此判断下一步,而不是重新猜测整个任务发生了什么。
7. 把恢复能力做成可以验证的行为
最有价值的测试位置,是故障窗口附近:
- 工具执行前退出,恢复后确认操作可以继续。
- 外部操作成功后、结果落库前退出,恢复后确认先核验而非直接重复执行。
- 相同操作同时被两个执行器领取,确认并发约束有效。
- 同一个幂等键携带不同参数,确认冲突被拒绝。
- 用户确认后修改目标参数,确认旧授权不会被复用。
- 重试期间取消任务,确认不会再发起新的副作用操作。
验证不只看工作流最终是不是绿色,还要看外部记录是否重复、取消后是否还有写操作、未知状态是否留下了可核验信息。Agent 的恢复能力,应当同时说明“下一步从哪里继续”和“已经做过的事情如何确认”。
参考与延伸
文章分享
如果这篇文章对你有帮助,欢迎分享给更多人!












