Agent checkpoint设计

这不是一项新增加的功能,而是在实际运行中遇到的问题。我们在使用codex,Claude code等等Agent时,我们可以清楚的感受到。如果遭遇电脑意外宕机/系统崩溃,那之前跑的任务就"卡死"了,就要重新再执行一遍,这就非常浪费时间,token等。所以,checkpoint断点恢复,就是为了解决这种问题,留存断点信息,再继续执行任务。这是一个Agent能持续进程任务的重要能力,往往很多时候没有最优的设计,都是不断迭代,优化,设计的过程。

这个设计并不陌生,在LLM中也有相关的内容------断点续训。

但checkpoint的设计要与git worktree,snapshot做区分,并不能划等号。

1. 恢复靠什么,保存什么?

先思考这个问题 > Agent要拿到什么信息,才能继续恢复状态去执行原任务?

其实在之前维护Agent时就已经有一些设计了。在memory中,最简单的方式就是持久化保存,无论是长文压缩的summary,还是各种类型记忆的保存;在LangGraph中我们也会定义state类,记录节点流转,工具使用,token消耗,todo规划,task依赖等信息。所以,对于持久化保存的内容,即使程序崩溃,它仍能保存,但对于内存信息,程序一旦中断,就会消失。我们要保存的,Agent所依靠的,就是这些内容。

a. 状态

Agent State,包含message,todo,task,plan,context。当前Agent执行所包含的内容

execution State,包含当前的tool_call,running_tasks/todos/plans/子agent。状态类变化的内容

enviroment State,包含当前目录,git_commite,worktree。外都环境。

b. 上下文

怎么去组装/恢复context?

比起保存一大段prompt,利用结构化输出更有利于信息提取。模型上,减少注意力稀疏所导致的信息缺失;Agent上,利用关键字段拼接,表达不长又明确。总的来说,前期可持久化的文件就直接引用,特别注意context设计,context应由各字段结合而成(当前的task/todo/plan...,执行的agent,有关联的file...),不应去保存一大段prompt,重要的是截断时的work_state。

c. 其他的设计(参考)

light模式

1.用什么恢复

所以,light模式很大程度上,依赖

  • TODO.md ------ 当前计划、待办、验收标准、验证命令
  • NOTEPAD.md ------ Agent 自己记的长期笔记、发现、风险
  • HISTORY_SUMMARY.md ------ 上下文压缩后留下的历史摘要

checkpoint.json保存了许多字段,涉及(恢复模式,工作环境,当前任务,上下节点,重试次数,摘要),既有人可读的md,又有机器友好的结构化字段。同时,字段要有上限。

2.恢复时的内容,summary设计,恢复后加入"继续任务"前缀(去重)

使用light模式恢复时,我们不还原消息历史。那么模型凭什么知道之前干了什么?靠 _copy_summary_fields()把这批字段直接塞回 state,以及拼出一段 context_summary。summary是上下文的替代品,只服务于light,有上限。

------------------>>>

根据graph各节点来去拼接,每个node_summary, planner_todo, searchagent_sources, error, verifier_command

这就是为什么字段这么多:它要覆盖整条图的每一个交接面。少一个字段,恢复后模型就会在那个环节"失忆"------比如不存 verification_commands,恢复后 verifier 就不知道该跑什么命令。

last_error 单独值得说一下:它服务于"上一次因为什么失败了"这个场景。中断恢复最怕的不是"不知道 做到哪",而是"不知道上次为什么卡住",然后原样再撞一次。

strict模式

用state.json 反序列化后的原样对象,state.json由loop中的另一个current_state去维护,更新状态后去合并进入。message进行归并追加,其余值进行覆盖。

event.json记录关键节点变化,写入时间信息。 "关键"的说明:在LangGraph中,记录每个节点更新;tool失败/需要人工审批时 节点更新保证"可重放",工具失败与审批保证"可解释"。生命周期点不进来,是因为 status 已经说了;普通 custom event不进来,是因为它们的内容本来就在 state.json 里------重复记只是让文件更大,不会让恢复更准。

2. 什么时候保存?

直接上结论:保存关键节点,关键变化内容。

  1. 发生"不可逆状态变化"之后,tool执行前后,目录/文件变化时。

  2. 状态更新后,todo/task的状态发送变化时,子agent完成任务状态改变时。

  3. context压缩时,重建context时。

  4. agent暂停,人工审批时。

  5. 某些长耗时任务/安装,如bash command...

  6. agent完成某个阶段性任务时,work完后下一步review。

  7. 整个任务完成后

  8. 不要无脑保存

事件驱动保存,而不是执行某step时,在某些关键节点处。也有别的方案会通过记录Agent的执行step来保存,我们这里采用的是Event ------> State ------> Checkpoint。在关键事件触发时,对于Agent来说,很多state会更新,我们要保存的是这些变化的内容/新的内容,而不是关键节点。

Checkpoint 应该有 parent_id。对某个输出不满意,可以重新去在上一checkpoint再跑一边/改动。

checkpoint不应该无限保存,还可以做 Compaction,(cp_1...cp_100)------> cp_11

3. 代码上的设计

持久化保存.CHECKPOINT/,会有很多目录/文件创建,这里挑重点说明。

`events.jsonl` --- 事实源(append-only)

一行一个 JSON 事件,追加写、每条 flush。记录"发生了什么变化",是整份设计的**唯一事实来源(source of truth)。

事件样例:

python 复制代码
```jsonl

{"seq":41,"ts":"2026-09-12 10:31:07","type":"tool_done","actor":"lead","name":"edit_file","tool":"edit_file","files":["encoder/agent.py"],"status":"ok","output":"...","workspace":{"cwd":"D:/EnCoder","branch":"main"}}

{"seq":42,"ts":"2026-09-12 10:31:09","type":"todo_changed","actor":"lead","data":{"todo_id":"t2","from":"pending","to":"in_progress"}}

{"seq":43,"ts":"2026-09-12 10:31:14","type":"compress","actor":"system","data":{"layer":2,"tokens_before":91234,"tokens_after":41022}}

```

`cp_0001.json` --- 状态快照(物化视图)

某一刻的完整可恢复状态,是 `events.jsonl` 的一次**物化视图**(materialized view)。恢复时只读这一个文件,不需要重放引擎。

python 复制代码
```json
{
  "meta": {
    "id": "cp_0007", "parent_id": "cp_0006",
    "created_at": "2026-09-12 10:31:14",
    "trigger": "compress", "label": "压缩前的 work_state",
    "seq": 43, "compacted": false, "replaces": []
  },
  "state": {
    "agent":     { "messages": [], "todos": [], "tasks": [], "context": {} },
    "execution": { "pending_tool_call": null, "running": [], "pending_approval": null },
    "env":       { "cwd": "", "git_head": "", "branch": "", "dirty": [], "worktrees": [] }
  }
}
```

总结来说,event会对每个关键节点记录内容,cp_id用于真正checkpoint恢复,提供起点。但cp_id不是肆无忌惮地保存,存太多,占用多。

如果是only_read操作不会去存,上下cp_id近似不会存,短时内的节点变化(超过5个在一起存,操作时长>1min再存),最后还有cp_compaction。

相关推荐
Ai-_Man3 小时前
Meta Muse还是不可以导出左侧所有对话,只能点进去单条内容导出,方法不对吗?
开发语言·人工智能·ai·小程序·c#·1024程序员节
秋饼3 小时前
Spring AI 2.1 Message Parts 深度实战:推理签名回放与跨轮记忆持久化的源码级落地
java·ai·技术分享·后端开发
hpoenixf3 小时前
金融 AI 最危险的“专业感”:数据是真的,数字却算错了
agent
Solange__3 小时前
影视预演怎么用AI生成3D场景?从分镜、Blockout到镜头交付的完整流程
人工智能·游戏·3d·ai·自然语言处理·aigc·游戏策划
又过二十3 小时前
Qwen-Image2.1游戏素材工坊:一句话,生成一整套游戏美术素材(一)
ai·aigc·comfyui·game·qwen-image·游戏素材
冬奇Lab3 小时前
一次 Skill 评测的复盘:我推翻了一份现成的模型选型结论,然后自己造了个评测平台
人工智能·llm·agent
known5 小时前
用 C# + Blazor 开发智能体(Agent)教程
ai·blazor
Together_CZ5 小时前
Introducing Kimi K3.1 Pro——隆重推出 Kimi K3.1 Pro
llm·agent·强化学习·1024程序员节·kimi·kimi k3.1 pro·3t参数
ZhengEnCi5 小时前
AI Agent工具调用防护层怎么写-一份能直接跑的Python四层拦截实现
python·llm·agent
todoitbo5 小时前
在 Ubuntu ARM64 上运行 Q1 V3.0 MuJoCo 模型:加载、步进和单执行器响应
linux·运维·ubuntu·ai·机器人·仿真教程·启元