系列: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 迁移。今天保存的状态,代码升级后可能缺新字段或字段含义变化;没有版本号,恢复旧任务会悄悄读错。

审批与恢复流程

Tool User Checkpoint Store Graph Tool User Checkpoint Store Graph alt [approve] [reject] 保存 pending_action 与幂等键 展示动作、范围和风险,暂停 approve / reject / edit 保存审批决定 带幂等键执行 返回业务结果 ID 保存结果 ID 与完成状态 标记 cancelled

如果工具自身支持幂等键,恢复时重复请求可返回同一业务结果;如果不支持,应用要在业务库记录“动作是否已提交”,不能仅凭内存判断。

Checkpoint 里不该放什么

  • API 密钥、访问令牌和不必要的个人敏感信息;
  • 可以通过稳定 ID 重新读取的大型工具原始输出;
  • 无限制增长的完整消息和每次中间推理;
  • 无法序列化的连接、文件句柄和客户端对象;
  • 没有来源的长期用户画像。

状态里保存引用和摘要,原始对象放在合适的受控存储。还要考虑加密、租户隔离、保留期限和删除请求。

恢复不是“从上一个节点继续”这么简单

需要验证:恢复者是否仍有权限;审批是否过期;工具和 Prompt 版本是否兼容;外部资源是否变化;重试预算是否已经用完。对于关键任务,恢复前可以运行 resume_guard,发现不一致就转人工,而不是盲目继续。

下一篇会把 thread 内的短期状态与跨 thread 的长期记忆分开。今天的 checkpoint 解决的是“这件事做到哪”,不是“这个人长期喜欢什么”。

面试官会追问:有 Checkpoint 就一定能恢复吗?

不一定。Checkpoint 只能恢复已保存的状态,不能撤销已经发出的邮件或支付。要实现可靠恢复,还需要稳定的 thread_id、节点提交边界、外部写操作的幂等键,以及“结果未知”时的状态查询。

我会验证三个故障点:工具执行前崩溃、工具成功后写 checkpoint 前崩溃、等待人工审批时服务重启。第二种最危险,因为本地看似没成功,外部系统可能已经完成。恢复逻辑必须先用 request_id 查询外部结果,再决定是否补偿。

面试回答里应区分 replay 与 resume:replay 用于调试历史轨迹,不能再次触发真实副作用;resume 才是在授权边界内继续原任务。

今日检查清单

  • Checkpoint、业务事实、记忆和审计分库或分命名空间
  • 副作用节点有幂等键与业务结果 ID
  • 重试计数、审批决定和状态版本进入快照
  • 恢复前重新检查权限、有效期和兼容性
  • 敏感信息与大对象不直接塞入状态
Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐