Day 025|持久化与 Checkpoint:让 Agent 失败后还能继续
系列:100 天系统学习 AI Agent 开发
当前阶段:LangChain 与 LangGraph 工程化
今日目标:持久化让 Agent 保存中间状态,适合长流程、可恢复任务和人工审批场景。
恢复执行最怕的,不是忘了进度,而是重复做了一次
长报告写到一半进程退出,从最近状态继续很有价值。但如果 Agent 已经发送邮件,刚发送完还没保存 checkpoint 就崩溃,恢复后再次执行发送节点,会产生真实事故。因此“能恢复”不仅是保存一个步骤编号,还要处理状态一致性与副作用幂等。
Checkpoint 可以理解为某个线程在图执行过程中的状态快照。它回答“这次运行走到哪、状态是什么”;它不等同长期用户记忆,也不替代业务数据库和审计日志。
四类存储别混在一起
| 类型 | 保存什么 | 典型主键 | 生命周期 |
|---|---|---|---|
| Checkpoint | 图状态、当前节点、待恢复信息 | thread_id + checkpoint_id | 随任务/线程 |
| 业务数据库 | 订单、文章、任务等正式事实 | 业务 ID | 按业务规则 |
| 长期记忆 | 用户偏好、经验、稳定事实 | user_id + namespace | 可更新/过期/删除 |
| 审计日志 | 谁在何时做了什么 | event_id / run_id | 合规周期内只追加 |
把所有内容塞进 checkpoint,会让状态巨大且难治理;只存 checkpoint 又无法证明外部动作是否已经发生。
哪些任务值得 checkpoint
| 任务 | 是否值得 | 建议保存点 | 特别风险 |
|---|---|---|---|
| 长报告生成 | 是 | 每个章节或研究阶段后 | 上下文过大 |
| 审批流 | 是 | 提交审批前、审批结果后 | 伪造/过期审批 |
| 分批数据清洗 | 是 | 每批提交后 | 重复处理同一批 |
| 长时间爬取 | 是 | 游标/页面批次后 | 目标变化、限流 |
| 一次简单摘要 | 通常否 | 无 | 持久化成本高于收益 |
| 付款/发信 | 仅 checkpoint 不够 | 动作前后 + 业务幂等记录 | 重复副作用 |
一个可恢复状态草案
from typing import Literal, TypedDict
class JobState(TypedDict):
thread_id: str
run_id: str
current_step: str
completed_steps: list[str]
pending_action: dict | None
idempotency_keys: dict[str, str]
attempts: dict[str, int]
status: Literal["running", "needs_approval", "failed", "done"]
state_version: int
state_version 用于 schema 迁移。今天保存的状态,代码升级后可能缺新字段或字段含义变化;没有版本号,恢复旧任务会悄悄读错。
审批与恢复流程
如果工具自身支持幂等键,恢复时重复请求可返回同一业务结果;如果不支持,应用要在业务库记录“动作是否已提交”,不能仅凭内存判断。
Checkpoint 里不该放什么
- API 密钥、访问令牌和不必要的个人敏感信息;
- 可以通过稳定 ID 重新读取的大型工具原始输出;
- 无限制增长的完整消息和每次中间推理;
- 无法序列化的连接、文件句柄和客户端对象;
- 没有来源的长期用户画像。
状态里保存引用和摘要,原始对象放在合适的受控存储。还要考虑加密、租户隔离、保留期限和删除请求。
恢复不是“从上一个节点继续”这么简单
需要验证:恢复者是否仍有权限;审批是否过期;工具和 Prompt 版本是否兼容;外部资源是否变化;重试预算是否已经用完。对于关键任务,恢复前可以运行 resume_guard,发现不一致就转人工,而不是盲目继续。
下一篇会把 thread 内的短期状态与跨 thread 的长期记忆分开。今天的 checkpoint 解决的是“这件事做到哪”,不是“这个人长期喜欢什么”。
面试官会追问:有 Checkpoint 就一定能恢复吗?
不一定。Checkpoint 只能恢复已保存的状态,不能撤销已经发出的邮件或支付。要实现可靠恢复,还需要稳定的 thread_id、节点提交边界、外部写操作的幂等键,以及“结果未知”时的状态查询。
我会验证三个故障点:工具执行前崩溃、工具成功后写 checkpoint 前崩溃、等待人工审批时服务重启。第二种最危险,因为本地看似没成功,外部系统可能已经完成。恢复逻辑必须先用 request_id 查询外部结果,再决定是否补偿。
面试回答里应区分 replay 与 resume:replay 用于调试历史轨迹,不能再次触发真实副作用;resume 才是在授权边界内继续原任务。
今日检查清单
- Checkpoint、业务事实、记忆和审计分库或分命名空间
- 副作用节点有幂等键与业务结果 ID
- 重试计数、审批决定和状态版本进入快照
- 恢复前重新检查权限、有效期和兼容性
- 敏感信息与大对象不直接塞入状态
更多推荐


所有评论(0)