摘要:长周期Agent、人在回路(Human‑in‑the‑loop)任务中,会话中断恢复是工程落地核心痛点。当前主流代码Agent(Claude Code、Qoder、Codex‑CLI)普遍采用JSONL事件日志+全量重放 架构;以LangGraph为代表的工作流框架,采用结构化Checkpoint状态快照。两种方案底层机制、故障恢复能力、上下文压缩行为存在本质差异,本文剖析两种技术路线原理、边界缺陷与工程选型策略。
一、背景:Agent中断恢复现实问题
现代Agent经常出现执行中断场景:进程崩溃、用户主动暂停、调用AskUserQuestion人机交互等待外部输入、长时间任务跨会话执行。
中断之后如何恢复任务进度,业界分化为两套截然不同的实现范式:
- 事件日志重放范式:记录全部执行流水,恢复时重放完整会话历史重建上下文,代表产品:Claude Code、Qoder、Codex‑CLI、Cursor。
- 状态快照Checkpoint范式:保存独立结构化运行时状态,恢复直接加载快照,无需完整重放历史消息,代表:LangGraph Checkpointer(SqliteSaver/PostgresSaver)。
很多开发者会混淆二者:二者都可以实现"中断后继续跑任务",但在上下文压缩、任务失忆、跨进程恢复、时光回溯能力上差距巨大。
二、事件日志范式:JSONL会话日志原理
2.1 JSONL文件格式定义
JSONL(JSON Lines),每行是一条独立合法JSON对象,换行符\n作为记录分隔符;整个文件不是JSON数组 ,只支持逐行解析,不支持整体反序列化读取。
示例片段:
json
{"role":"user","content":"实现登录接口","timestamp":"2026‑09‑04T10:00:00"}
{"role":"assistant","tool_calls":[{"name":"AskUserQuestion","arguments":{"question":"确认数据库类型"}}]}
{"role":"tool","name":"AskUserQuestion","content":"使用Oracle数据库"}
写入特性:追加写入,新事件直接追加文件末尾,不会修改历史行,IO开销极低,非常适配Agent一轮轮迭代产生事件的场景。
2.2 存储内容
磁盘JSONL完整保存全部事件:用户消息、模型输出、tool_call调用、工具返回结果、人机交互事件、压缩标记事件,磁盘原始记录不会被compact上下文压缩删除。
关键区分:
- 磁盘层JSONL:原始流水完整留存,用于审计、事后回溯;
- 内存上下文(送入大模型) :触发compact压缩时,内存中将旧消息替换为LLM生成摘要;磁盘文件原样不动。
现象:打开日志文件能看见全部历史,但是Agent运行时模型只能看到摘要,出现"磁盘有记录,Agent却失忆"的现象。
2.3 中断恢复流程:全量重放
当Agent进程重启、AskUserQuestion人机交互结束继续执行:
- 读取磁盘完整JSONL会话文件;
- 逐行解析全部事件,内存重建完整消息列表;
- 将重建之后的消息列表送入LLM,继续Agent循环执行。
核心约束:恢复质量完全依赖送入模型的上下文窗口。一旦会话过长触发compact压缩,内存上下文被摘要替换,即便磁盘原始记录完好无损,模型无法读取原始细节,会丢失任务中间细节、历史约束条件,引发任务失忆、幻觉偏离目标。
2.4 缓解失忆的配套手段
事件日志架构本身没有独立任务状态存储,工程上增加外部文件作为补偿,属于LLM主动写入的记忆,非框架自动快照:
- Claude Code:
CLAUDE.md、Auto‑Memory,Agent在关键节点主动写入任务进度、待办事项;会话恢复时把文件内容注入上下文。缺陷:依赖大模型写作质量,存在摘要漏写、幻觉失真风险。 - Qoder:
AGENTS.md结构化记忆文件,原理同上。
本质:属于应用层补救,不是运行时原生状态持久化。
三、Checkpoint状态快照范式(LangGraph)
LangGraph的Checkpointer(PostgresSaver/SqliteSaver),不依赖完整对话日志做恢复。
3.1 存储内容
持久化存储结构化Graph State快照:
- 当前运行节点标识
next; - 全局State对象(任务清单、中间产出、子Agent返回结果、业务变量);
- 元数据:thread_id、checkpoint_id,支持多版本历史快照。
消息历史可以作为State其中一个字段,但不是恢复的必要条件 。中断(interrupt(),对应AskUserQuestion能力)发生时,直接把当前运行现场完整序列化存入数据库,不依赖对话消息保存任务进度。
3.2 中断恢复流程
- 中断发生,完整业务状态写入数据库;
- 用户输入回答;
- 恢复时,直接读取对应
thread_id的checkpoint快照,加载业务状态,从暂停节点继续执行; - 不需要重放全部历史对话消息。
巨大优势:即便对话消息触发上下文压缩被裁剪,独立保存的业务State不受影响,长任务、多次HITL人机中断,不容易出现任务失忆;原生支持时光回溯Replay、Fork分支调试,可以回到任意历史快照点继续执行。
3.3 短板
- 需要预先定义State Schema,业务字段变更需要处理序列化、版本迁移;
- Checkpoint快照只保存运行时状态,不适合做审计溯源,生产环境建议配套事件日志共同使用;
- 存储、序列化开销大于简单JSONL追加写日志模式。
四、两种架构核心对比
| 对比维度 | JSONL事件日志重放(Claude Code/Qoder) | LangGraph Checkpoint状态快照 |
|---|---|---|
| 持久化对象 | 消息、工具调用事件流水 | 独立结构化业务State快照 |
| 中断恢复方式 | 读取完整JSONL,重建全部消息送入LLM | 直接加载快照,从断点节点继续,无需全量重放消息 |
| compact上下文压缩影响 | 高,内存摘要替换原始消息,容易失忆;磁盘日志完整 | 低,业务状态独立存储,消息压缩不破坏任务进度 |
| 人在回路AskUserQuestion实现 | 工具调用暂停,依靠会话日志恢复 | 原生interrupt()运行时断点,快照保存执行现场 |
| 时光回溯能力 | 弱,只能重放完整会话,无法局部回退业务状态 | 强,支持Replay重跑、Fork分支,多版本快照 |
| Schema约束 | 无,直接追加事件,迭代成本低 | 需要定义State结构,需要处理版本兼容 |
| 审计能力 | 优秀,完整原始事件留存 | 快照面向恢复,审计需要额外事件日志 |
| 适用场景 | 短‑中等会话、代码Agent、快速原型 | 长周期多Agent、多次人工介入、跨天复杂工作流 |
五、工程选型策略
场景1:CLI/IDE代码Agent,任务单次会话可完成
优先选择JSONL事件日志方案 。开发迭代速度快,实现简单,足够满足绝大多数编码场景;搭配结构化记忆文件(CLAUDE.md/AGENTS.md)缓解长会话失忆风险。
代表:Claude Code、Qoder、Codex‑CLI。
场景2:多Agent Supervisor调度、多次人工确认、跨天长任务、生产级工作流
优先Checkpoint快照架构。规避上下文压缩带来的任务丢失风险,HITL人机中断可靠性更高;同时保留事件日志做审计溯源,二者组合使用是生产最佳实践。
场景3:折中方案(推荐生产)
事件日志负责审计溯源;Checkpoint快照负责故障恢复、断点续跑。
事件日志永久追加存储全部原始交互;Checkpoint在节点边界、人工交互点写入业务快照;既保留完整可审计流水,又拥有可靠的中断恢复能力。
六、常见误区澄清
- ❌ compact压缩会删除磁盘JSONL原始记录
✅ compact仅修改内存送入模型的上下文,磁盘原始行完整保留,仅内存中旧消息被摘要替换。 - ❌ 能够中断继续执行,就代表底层机制一致
✅ Claude Code AskUserQuestion依靠日志重放;LangGraph interrupt依靠状态快照;表层行为接近,底层故障边界完全不同。 - ❌ 把关键业务逻辑全部放在对话消息里,依靠大模型记忆
✅ 长周期Agent,关键任务状态不能依赖对话上下文;要么写入外部结构化记忆文件,要么使用独立Checkpoint快照存储。
七、总结
JSONL事件日志重放实现简单,是当前绝大多数代码Agent的主流方案,但受限于LLM上下文窗口压缩,长周期、多次人机交互场景会出现任务失忆;Checkpoint快照将业务运行状态与对话消息解耦,大幅提升复杂Agent的可靠性,但会带来Schema维护成本。
在实际工程落地中,不要非此即彼,根据任务周期、人机交互频次,选择架构,条件允许时采用"事件日志+状态快照"组合模式,兼顾可观测审计与故障恢复能力。
参考文献:LangGraph官方Checkpointer文档、Claude‑Code会话持久化源码、Agent事件溯源工程实践。