WorldState 持久化的是可重放快照链:新窗口或无基线时写 full,稳态只写 JSON Merge Patch;恢复必须按时间顺序应用,并在 Compact 或非法 Patch 处重置可信基线。
本节解决的不是“把对象存一下”这种抽象问题,而是把问题限定在:研究 WorldState 记录顺序、full/patch 生成和 replay 失效规则。输入:Step 捕获的 WorldStateSnapshot、上一基线与 Compacted 边界;状态所有者:ContextManager baseline + Rollout WorldStateItem;成功结果:与模型可见上下文一致的当前 WorldStateSnapshot。先把这些边界钉住,后面的顺序、失败和恢复才不会混成一句“持久化失败后重试”。
状态边界
| 问题 | 本节答案 |
|---|---|
| 输入 | Step 捕获的 WorldStateSnapshot、上一基线与 Compacted 边界 |
| 状态所有者 | ContextManager baseline + Rollout WorldStateItem |
| 成功产物 | 与模型可见上下文一致的当前 WorldStateSnapshot |
| 研究范围 | 研究 WorldState 记录顺序、full/patch 生成和 replay 失效规则 |
正常路径:先看顺序点
这条路径可以压缩成五步:
- 同一 Step 构造当前 snapshot。
- 与上一 baseline 计算 diff/merge patch。
- 先记录模型可见 context fragments。
- 再持久化 WorldState item。
- Resume 从 full 起顺序应用 patch。
图中的箭头不是“可能调用”的依赖图,而是源码中决定可见性和所有权转移的先后关系。前一步没有确认时,后一步不能替它作出更强的成功承诺。
源码机制拆解
模型文本和 Patch 来自同一对快照
record_step_world_state_if_changed 先抓 current snapshot,同时计算 render_diff 与 merge_patch_from(previous)。避免工具执行用一份环境、日志却记录稍后另一份环境。
顺序是“可见上下文在前、状态记录在后”
若 diff 产生模型可见 ResponseItem,Session 先 record_conversation_items,再 append WorldState patch。恢复看到 patch 时,可以假定其描述的上下文已经位于 history 前缀。
无 baseline 时只能写 full
首次上下文注入、start_new_context_window 和 Compaction replacement 都建立新基线,使用 WorldStateItem::full。Patch 没有 base 就不能独立解释。
JSON Merge Patch 表达删除与替换
Snapshot.merge_patch_from 返回 RFC 7396 风格 JSON patch;空变化返回 None,避免重复记录。数组等值按整体替换,不应把它误写成通用操作日志。
恢复在边界上清除基线
按时间顺序 replay:full 覆盖,patch 只在已有 baseline 时 apply;Compacted 先清掉更旧 WorldState,随后应由 checkpoint 后的 full 重新建立。解析或 patch 失败会 warning 并清除,不能继续叠在可疑状态上。
Python 风格伪代码
下面的伪代码只保留设计职责、状态和失败顺序;它不逐行翻译 Rust,也不借 Python 语法虚构源码中不存在的事务:
async def record_world_state(session, step, previous):
current = await build_world_state_from_same_step(step)
visible_diff = current.render_diff(previous)
patch = current.snapshot.merge_patch_from(previous.snapshot)
if visible_diff:
await session.record_conversation_items(visible_diff)
session.context_history.world_state_baseline = current.snapshot
if patch is not None:
await session.persist(WorldStateItem.patch(patch))
return current
def replay_world_state(items):
baseline = None
for item in items:
match item:
case Compacted(): baseline = None
case WorldState(full=True, state=value): baseline = parse_snapshot(value)
case WorldState(full=False, state=patch) if baseline is not None:
baseline = apply_json_merge_patch(baseline, patch)
case WorldState(full=False): baseline = None # invalid orphan patch
return baseline
阅读时要特别看三处:哪个对象拥有可变状态,哪一个 await 是可观察屏障,以及失败后保留的是已提交前缀、未提交后缀,还是完全独立的外部副作用。
失败、取消与恢复
| 故障点 | 已留下的状态 | 可观察结果 | 恢复责任 |
|---|---|---|---|
| Patch 前没有 full | 缺少解释基线 | 恢复不能猜当前状态 | 清除 baseline 并等待下一 full |
| 先写 patch 后写模型 diff | 崩溃可能只留下状态 | 历史与基线顺序不一致 | 先 context item,后 WorldState |
| Compact 后沿用旧 baseline | replacement history 已换窗口 | 后续 diff 针对不可见状态 | Compact 清除并写新 full |
| Patch JSON 无法应用 | baseline 变得可疑 | 后续 patch 不能安全叠加 | warning 后清除 baseline |
这里没有统一的“回滚一切”。内存状态、日志行、SQLite 投影、父子拓扑和工具造成的文件/网络变化分别有自己的提交点。恢复代码只能根据已经存在的权威证据继续,不能用较弱的投影替较强的事实背书。
必须保持的不变量
- Patch 必须有已知 full/有效 baseline
- 模型可见 diff 的记录顺序早于对应 state patch
- Compaction 新窗口重新建立 full baseline
- 同一 Step 快照用于工具、上下文和持久化
这些不变量比“最终能 Resume”更严格:正常路径要成立,Writer 竞争、任务取消、坏尾行、投影落后和旧格式兼容时也必须成立。
设计取舍
full+patch 链比每步完整快照省空间,但恢复器必须严格处理基线丢失;周期性 checkpoint 则把链长重新归零。
源码可以直接证明字段、分支、调用顺序和测试期望;“为什么这样设计”的表述是基于这些事实作出的工程归纳,不把它包装成未公开的产品承诺。
Mini Codex 复刻
定义 immutable WorldStateSnapshot 和 merge_patch;每个 context window 首条写 full,之后写 patch,replay 遇到 orphan/invalid patch 立即 invalidate。
复刻时先验证协议不变量,再补性能优化。一个能在故障注入下说明“留下了什么”的小实现,比一个只在正常路径调用 save() 的演示更接近真实 Runtime。
源码导航
- codex-rs/core/src/session/mod.rs:WorldState diff、记录顺序、full 基线与 compact 安装
- codex-rs/core/src/context_manager/history.rs:WorldState baseline 与 update_world_state
- codex-rs/protocol/src/protocol.rs:WorldStateItem full/patch 协议
- codex-rs/core/src/session/rollout_reconstruction.rs:恢复时 full/patch/compact 顺序
相邻测试也很重要:
- codex-rs/core/src/session/rollout_reconstruction_tests.rs:full+patch、compact reset、非法 snapshot/patch 的恢复行为
- codex-rs/core/src/session/turn_tests.rs:Turn Step 中 WorldState 更新时机
本节结论
WorldState 持久化的是可重放快照链:新窗口或无基线时写 full,稳态只写 JSON Merge Patch;恢复必须按时间顺序应用,并在 Compact 或非法 Patch 处重置可信基线。
评论
登录后即可评论