具体问题与边界
如果只保存聊天消息,Runtime 无法恢复 Compact、工具配对和 Turn 设置;如果把 UI Delta、全文索引、 Agent 拓扑和规范历史全塞进同一日志,任何查询或格式升级都会触碰恢复主链。Codex 将 Context History、 Rollout、ThreadStore、State DB 与 AgentGraph 分开,各自回答不同问题。
本节评估多权威边界,不暗示这些存储具有跨系统事务,也不把 Rollback 解释为文件系统回滚。
权威与派生关系
| 层 | 权威问题 | 写入频率/生命周期 | 可否由其他层重建 |
|---|---|---|---|
| Context History | 当前模型下一次应看什么 | 内存、Step/Turn 高频 | 可从有效 Rollout 重建 |
| Rollout JSONL | 本地会话语义怎样重放 | 追加、跨进程 | 规范恢复输入 |
| ThreadStore | 如何分页/物化线程历史 | 查询导向 | 可追赶 Rollout/底层存储 |
| State DB | 线程元数据、索引和查询 | 异步投影 | 部分可从 Rollout 重建 |
| AgentGraphStore | 父子边、开放/关闭拓扑 | Agent 生命周期 | 不能从聊天文本可靠推断 |
| 外部副作用 | 文件、进程、远端服务事实 | 工具执行时 | 不由会话 Rollback 撤销 |
正常写入与恢复
- Turn 把完整 ResponseItem、上下文变化和 checkpoint 交给 Recorder,而不是持久化 UI Delta。
- 专用 Writer Task 按顺序写完整 JSONL 行,Flush/Persist 明确形成可恢复屏障。
- State/ThreadStore 在规范写入之后更新元数据和分页投影,允许短暂落后但不能反向改写 Rollout。
- Spawn/Fork 同时维护独立 AgentGraph 边;线程内容与拓扑生命周期并不相同。
- Resume 读取有效 Rollout,应用 Compact/Rollback replacement,再重建内存 History。
- 查询投影落后时可以追赶;规范日志坏尾则按已证明的解析/修复规则处理。
写入“先后”不是全局原子事务。JSONL 已确认而 SQLite 未更新时,系统应把后者视为可修复落后, 而不是重复执行前一条工具副作用。
Python 风格伪代码
async def commit_semantic_records(records: tuple[RolloutRecord, ...], stores: Stores) -> None:
confirmed = await stores.rollout.append_and_flush(records)
assert confirmed == len(records)
await stores.state.project(records) # may lag; never rewrites rollout
async def restore_thread(thread_id: str, stores: Stores) -> RestoredThread:
records = await stores.rollout.read_tolerant(thread_id)
effective = apply_replacement_checkpoints(records)
history = normalize_prompt_pairs(rebuild_history(effective))
metadata = await stores.state.read_or_repair(thread_id, records)
graph = await stores.agent_graph.read_edges(thread_id)
return RestoredThread(history, metadata, graph)
async def rollback_conversation(turns: int, stores: Stores) -> None:
replacement = stores.history.without_last_turns(turns)
await stores.rollout.append_and_flush((RolledBack(replacement),))
stores.history.replace(replacement)
# Files and remote effects are deliberately untouched.
伪代码保留规范先写、派生可追赶和 replacement checkpoint;它没有虚构跨 JSONL/SQLite/文件系统事务。
崩溃和不一致
| 崩溃点 | 已确认事实 | 可能落后 | 恢复 |
|---|---|---|---|
| JSONL 行写一半 | 前缀行 | 尾行 | 忽略/修复坏尾,不重放外部工具 |
| Rollout flush 后、State 前 | 新语义记录 | 元数据/索引 | 从 Rollout 追赶投影 |
| Tool 副作用后、Result 前 | 外部世界已变化 | 会话配对 | 重建失败 Result,禁止盲目重跑 |
| Graph 边写入失败 | 子 Thread 可能存在 | 拓扑 | 报告孤立/补边,不从文本猜测 |
| Rollback checkpoint 后 | 新 effective history | 文件系统 | 恢复新历史,明确外部改动仍在 |
| 压缩物化中断 | 原引用/压缩文件之一有效 | 新物化 | 根据 reference index 保留祖先 |
设计判断
多层存储的收益是让追加恢复、全文查询、压缩归档和 Agent 拓扑独立演进;成本是可观察到的短暂不一致、 迁移工具、保留约束和更复杂的故障诊断。每增加一层都必须写清权威问题和重建方向,否则只是重复状态。
单用户教学 Runtime 可以只用 JSONL,但仍应区分内存 History 和耐久 Rollout;当需要分页、搜索或多 Agent 恢复时,再增加派生层,而不是预先复制 Codex 的全部存储矩阵。
证据与 Mini Codex
生产锚点为 rollout/src/recorder.rs、thread-store/src/store.rs、state/src/lib.rs、
agent-graph-store/src/store.rs 和 reconstruction;8.1—8.34 已覆盖写入、恢复、压缩、Rollback/Fork。
Mini Codex 验证 Writer Task、confirmed prefix、坏尾、Resume 和 replacement checkpoint:
cd examples/mini-codex
uv run pytest -q -k 'writer or resume or rollback or fork'
它没有 SQLite、跨进程锁、压缩引用或 AgentGraph,因此只能证明单 JSONL 权威链。
本节边界
持久化可以恢复 Agent 的语义状态,却不能消除多 Agent 自身的容量与协调成本。下一节讨论这部分代价。 详细源码映射由研究仓库中的配套索引维护。
评论
登录后即可评论