Agent 工程对照
← 返回框架对照

上下文 · 第 4 章

会话状态存储

一次会话的事实存在哪里、以什么形式?

openJiuwen 存状态快照,只能回到保存点;另三家存追加式日志,能回溯和分叉,恢复时要重放。

会话怎么存

状态快照

追加式日志

Pi 的日志是一棵可分支的树

场景建议

编辑判断

按你的需求,倾向哪种做法

借鉴的是那一家的设计,不等于选用那个框架。

场景

要审计、回溯每一步

三家存的都是追加式日志;openJiuwen 存快照,保存点之间不留痕。

场景

要从历史某一点分叉重来

每个条目都带 parentId,会话天然是树,分支不需要复制历史。

场景

存储后端要可替换

DeepSeek 有 JSONL 与 SQLite 两个后端共用写协调器;Pi 有内存、JSONL 与 SQLite;openJiuwen 底层是可替换的 KV 存储。

四家怎么做

差异与源码

openJiuwen

状态快照挂在执行生命周期上:执行前恢复,中断与结束时保存

openJiuwen 存的是状态快照,不是事件流。Checkpointer 抽象在 agent、agent team、workflow 三类执行体的前后各留一个挂载点:执行前 recover 把上次的状态整块恢复进 session,需要人工交互而中断时、执行完成时各 save 一次。底层是可替换的 KV 存储(BaseKVStore),键按「会话:命名空间:实体:后缀」分区,agent、agent-team、workflow、workflow-graph 各占一个命名空间(见同目录 base.py 的常量)。代价是只能回到保存点,保存点之间发生了什么不留痕。

适合
只关心「从上次保存点接着跑」、不需要回溯过程的业务流程。
代价
只能回到保存点,保存点之间发生了什么不留痕,审计与分叉都做不了。
Codex

JSONL rollout 是正本,SQLite 只镜像元数据

会话的正本是 ~/.codex/sessions 下的 rollout JSONL 文件,注释直接建议用 jq 查看。写入由后台任务完成,待写条目先进 pending_items,只有写成功才出队;I/O 失败时丢掉文件句柄但保留未写的尾巴,下一次刷盘重新打开文件重试。另外两个 crate 各司其职:state 把 rollout 的元数据抽出来镜像进本地 SQLite(crate 文档写明它刻意保持小而专注),thread-store 则把 ThreadId 定为唯一的持久句柄,背后是本地文件还是 RPC 由实现决定。

适合
单机、会话文件要能直接用 jq 查看与调试的开发者工具。
代价
正本是 JSONL,列表与检索依赖另一份 SQLite 镜像,两份数据要保持一致。

此后的变化

  • 2026-W36 →

    会话与托管 worktree 绑定:TUI 可浏览仓库配置池中的检出、按归属元数据恢复对应线程;worktree 切换拆为多阶段,各自在新的事件循环轮次执行。

DeepSeek Harness

事件溯源:日志即事实,JSONL 与 SQLite 两个后端可替换

持久化是一个抽象服务(ctx.sessionPersistence),持久化单元就是运行时的 SessionEvent 本身,不另设一套「持久化消息」类型;不可重放的元数据(格式版本、cwd、血缘等)单独放在 SessionHeader 里。append 只追加、seq 必须连续;恢复会话时 prepare 先 load 出一份平衡的日志,再把事件作为种子交给会话仓库。仓库里同时提供 session-persistence-jsonl 与 session-persistence-sqlite 两个后端,共用同一个写协调器,所以生命周期上的正确性只写一遍。

适合
要换存储后端、并且要求存储语义与运行时事件完全一致的系统。
代价
事件溯源的全部成本:格式版本、旧记录迁移、崩溃尾部修复都要自己做。
Pi

会话是一棵条目树,lane 指向叶子,存储后端可换

每个条目都带 parentId,由存储在追加时填成所在 lane 的当前叶子——所以会话天然是树,分支、回到旧节点重来都不需要复制历史。SessionStorage 接口把 lane 的创建与移动、条目与记录的追加、按分支读取分开;findOpenOperations 用 limit 2 判定状态:0 条是空闲,1 条是挂起,2 条说明数据已损坏。同目录下有内存与 JSONL 两种实现,另有独立的 session-backends/sqlite-node 包。

适合
需要分支、回到旧节点重试的交互(例如编辑某条历史再来一次)。
代价
树形结构让读取变成「按分支取路径」,lane 与叶子指针多了一层概念。

此后的变化

  • 2026-W38 →

    系统提示词文本变化和工具可用性变化,从悄悄重写会话起点改为写入 transcript 本身(#9548),恢复会话或切换分支时据此还原当时的运行条件,并在上游支持时保留被缓存的提示词前缀。本格此前描述 Pi 的会话是带 parentId 的树,本周变化说明这棵树的节点不仅记录消息,也记录了运行时配置的时间线。

横向读这一行

四家分成快照与日志两派。openJiuwen 存状态快照,在执行前恢复、中断与结束时保存,只能回到保存点;另外三家都存追加式日志——Codex 以 JSONL rollout 为正本、SQLite 只镜像元数据,DeepSeek 把运行时事件本身当持久化单元、JSONL 与 SQLite 两个后端可换,Pi 更进一步让每个条目带 parentId,会话天然是一棵可分支的树。存日志的代价是恢复要重放,收益是任何时刻都能回溯、分叉、审计。

这个问题为什么存在

第 4 章

上下文工程

上下文是稀缺资源:腐化、焦虑、压缩与缓存