AI agent 一旦从“单次回答”走向“多步工作流”,就会遇到一个问题:任务跑到一半失败了,应该怎么恢复?很多系统默认答案是“从头重来”。短流程问题不大,但对长任务、昂贵任务、有人工确认节点的任务,这种做法会把成本、等待时间和副作用同时放大。
Checkpoint 的意义不是让任务永远不失败,而是把失败限制在一个可恢复的范围内。
建议先配合 AI Agent 状态机设计指南、AI agent 错误分类与恢复策略、AI agent 幂等与去重实践 和 AI Agent Session Replay 调试指南 一起看。
先给结论:不是每一步都值得做 checkpoint
| 阶段 | 是否建议做 checkpoint | 原因 |
|---|---|---|
| 输入校验 | 否 | 成本低,重跑便宜 |
| 检索与上下文整理 | 视情况 | 结果可能依赖时效 |
| 长规划结果 | 是 | 可复用,重算成本高 |
| 写入前审批 | 是 | 需要保留人工决策 |
| 外部工具写入后 | 是 | 需要明确副作用已发生 |
| 最终响应 | 否 | 可重新组织 |
Checkpoint 不是越多越好。太细会把系统变成快照存储器,太粗又失去恢复价值。
一、先区分三种恢复目标
恢复并不总是“接着往下跑”。至少有三种目标:
| 恢复目标 | 适用场景 |
|---|---|
| 从最近安全点继续 | 工具超时、worker 中断 |
| 从上一个稳定规划重算 | 检索过期、上下文变化 |
| 终止自动恢复,转人工 | 写入冲突、审批被拒 |
如果系统只有一个 resume=true 开关,恢复逻辑大概率会越来越乱。
二、Checkpoint 至少要保存什么
一个可恢复 checkpoint 不能只存“做到第几步”。至少要保留:
{
"checkpointId": "cp_03",
"runId": "run_123",
"state": "waiting_tool_result",
"stepName": "fetch_customer_profile",
"schemaVersion": "v2",
"inputSnapshot": { "customerId": "c_456" },
"artifactRefs": ["artifact_plan_01", "artifact_evidence_02"],
"sideEffectStatus": {
"writePerformed": false,
"externalMessageSent": false
},
"createdAt": "2026-05-07T10:00:00Z"
}
这里最关键的是 sideEffectStatus。如果系统不知道副作用是否已经发生,就无法安全恢复。
再往前走一步,很多团队会发现仅有 snapshot 还不够,因为“恢复是否安全”往往取决于 snapshot 之外的环境条件。一个更稳的 checkpoint 记录,通常还会补上:
resumePreconditions: 恢复时必须再次满足的条件leaseEpoch: 当前 worker 所属租约代次replaySafeActions: 允许重复执行的动作列表guardHash: 恢复前需要再次校验的关键上下文摘要
这些字段看起来偏底层,但它们能把“能恢复”与“应该恢复”分开。
三、恢复前一定要做 resume guard
恢复不是读取 checkpoint 就直接继续,而是要先判断当前世界是否还允许继续:
| 检查项 | 目的 |
|---|---|
| 任务是否仍有效 | 用户可能已取消 |
| schemaVersion 是否仍可读 | 避免协议漂移 |
| artifact 是否完整 | 避免基于损坏数据恢复 |
| 外部状态是否变化 | 目标对象可能已被他人修改 |
| lease 是否仍归当前 worker | 防止重复执行 |
这一步可以叫 resume guard。很多恢复事故都不是 checkpoint 丢了,而是跳过了 guard。
四、恢复状态机最好单独建模,不要散落在 if/else 里
Checkpoint 文章里最容易漏掉的一点是:恢复本身也是一个流程,而不是主流程里的一个布尔分支。一个最小恢复状态机通常至少要有:
| 状态 | 含义 |
|---|---|
resume_requested | 收到恢复请求,尚未开始校验 |
guard_checking | 正在验证前置条件 |
resume_ready | 可以从安全点继续 |
resume_blocked | 条件不满足,需要转人工或重算 |
resuming | 已重新拿到 lease,准备进入后续 step |
resume_failed | 恢复过程本身失败 |
如果这些状态没有显式存在,系统就很难回答两个关键问题:
- 当前失败是业务步骤失败,还是恢复动作失败
- 一次 Run 到底经历了几轮恢复尝试
这也是为什么恢复日志最好和普通执行日志分开,因为两者表达的问题不同。
五、写入类步骤要区分“可重放”和“不可重放”
恢复时最危险的是写入和外发:
| 动作 | 恢复策略 |
|---|---|
| 读取资料 | 可重新执行 |
| 生成草稿 | 通常可重放,但要幂等 |
| 更新状态 | 需要版本检查 |
| 发送通知 | 默认不可直接重放 |
| 扣减额度 | 必须依赖 actionId 幂等 |
凡是副作用对外可见的动作,都应该先判断“上次到底有没有做成”。不要因为“worker 掉线了”就假设动作没发生。
很多实现最后都会补一份 side effect ledger,专门回答“动作有没有发生过、是否完成、幂等键是什么”。例如:
{
"actionId": "send_email_001",
"runId": "run_123",
"stepName": "notify_customer",
"idempotencyKey": "run_123_notify_customer_v1",
"status": "committed",
"externalRef": "provider_msg_789"
}
有了这层记录,恢复动作就不必从 checkpoint 快照里猜“上次大概做到哪了”,而是直接查副作用账本。
六、Checkpoint 粒度怎么定才划算
一个实用标准是:
- 比一次完整 Run 便宜得多的步骤,不必存 checkpoint
- 需要人工输入或人工确认的节点,应该存 checkpoint
- 会产生外部副作用的节点,前后都值得存 checkpoint
- 纯短时推理步骤,通常只保留 trace,不单独 checkpoint
你可以把 checkpoint 理解成“恢复成本拐点”的记录,而不是每一步的录像。
如果想把粒度讨论得更具体,可以用这张判断表:
| 判断问题 | 是 | 否 |
|---|---|---|
| 这一步重跑成本高吗 | 倾向做 checkpoint | 倾向跳过 |
| 这一步会跨人工或跨系统边界吗 | 倾向做 checkpoint | 继续看下一项 |
| 这一步有不可逆副作用吗 | 前后都做 checkpoint | 可以只保留 trace |
| 这一步输出会被后续多次复用吗 | 倾向做 checkpoint 或 artifact | 只保留临时状态 |
这样比“重要步骤就存一下”更容易在团队内达成一致。
七、恢复预算和熔断要先定义,不要无限次自救
如果系统每次失败都自动 resume,看起来很积极,现实里却可能把单次故障放大成资源雪崩。建议至少定义:
| 规则 | 建议 |
|---|---|
| 单次 Run 最大恢复次数 | 2 到 3 次 |
| 同一 checkpoint 最大重复恢复次数 | 1 到 2 次 |
| 恢复冷却时间 | 指数退避或按错误类别定义 |
| 连续 guard 失败处理 | 直接转人工,不再自动尝试 |
这套预算不是保守,而是为了防止系统在明知上下文已变化的情况下继续硬闯。
八、失败案例:恢复时直接跳过审批,造成重复外发
一个内容发布 Agent 的流程是:生成草稿 -> 人工审批 -> 发送通知 -> 更新状态。某次在“发送通知后、更新状态前” worker 崩了。恢复逻辑看见上一个 checkpoint 在 approved,就重新跑发送通知,结果通知发了两次。
根因有两个:
- checkpoint 没记录通知是否已发送
- resume guard 没检查 outbox/messageId
修复后,发送通知前后都写 checkpoint,并把 messageId 和 deliveryState 存进 side effect ledger。恢复时若发现 already_sent,就跳过发送,只继续更新状态。
九、上线后要看恢复质量指标,而不只是恢复次数
恢复做出来以后,最值得看的不是“系统有没有触发恢复”,而是“恢复到底帮了多少忙,还是制造了更多噪音”。常见指标包括:
| 指标 | 解释 |
|---|---|
| 恢复成功率 | 恢复后能否真正完成 Run |
| 恢复后再次失败率 | 判断 checkpoint 位置是否不合理 |
| guard 拦截率 | 判断恢复尝试是否过于激进 |
| 副作用重复拦截数 | 判断 ledger 与幂等设计是否有效 |
| 平均恢复耗时 | 判断恢复是否真的节省时间 |
如果恢复次数很高但恢复成功率很低,这不叫系统健壮,而叫系统在重复试错。
十、恢复系统要有“放弃恢复”的出口
不是所有失败都值得自动恢复。以下场景更适合直接转人工:
- 外部数据已明显变化
- 人工审批结论已被修改
- schema 版本不再兼容
- 关键 artifact 丢失或损坏
- 同一 Run 恢复次数超过阈值
恢复系统最危险的不是保守,而是逞强。
十一、Checkpoint Checklist
- 是否只在真正有恢复价值的节点写 checkpoint
- checkpoint 是否保存 schemaVersion 和 sideEffectStatus
- 是否单独建模恢复状态机,而不是散在主流程分支里
- 恢复前是否执行 resume guard
- 写入和外发动作是否区分可重放与不可重放
- 是否有 side effect ledger 或等价账本记录外部副作用
- 人工审批节点是否可恢复但不重复执行
- 是否定义恢复预算、冷却时间和熔断规则
- 同一 Run 是否有恢复次数上限
- 无法安全恢复时是否明确转人工
结语
AI agent 的断点恢复,不是“失败后继续跑”这么简单,而是“在确认世界没有变、协议没漂、副作用没重复的前提下继续跑”。Checkpoint 真正保护的不是执行进度,而是恢复时的正确性。
延伸阅读:


