雨天小六

读懂 Codex(8.23):WorldState Full/Patch 基线恢复

· 更新于 2026-08-03 · 专栏:读懂 Codex

#Codex#Agent Runtime#持久化#Rollout#软件架构

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 失效规则

正常路径:先看顺序点

WorldState Full/Patch 基线恢复的正常路径时序图,展示同一 Step 构造当前 snapshot、与上一 baseline 计算 diff/merge patch、先记录模型可见 context fragments、再持久化 WorldState item、Resume 从 full 起顺序应用 patch
图 8.23-1:同一 Step 构造当前 snapshot → 与上一 baseline 计算 diff/merge patch → 先记录模型可见 context fragments → 再持久化 WorldState item → Resume 从 full 起顺序应用 patch。这张图标出本节的实际顺序点与状态所有者。

这条路径可以压缩成五步:

  1. 同一 Step 构造当前 snapshot。
  2. 与上一 baseline 计算 diff/merge patch。
  3. 先记录模型可见 context fragments。
  4. 再持久化 WorldState item。
  5. 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 是可观察屏障,以及失败后保留的是已提交前缀、未提交后缀,还是完全独立的外部副作用。

失败、取消与恢复

WorldState Full/Patch 基线恢复的失败路径图,区分Patch 前没有 full、先写 patch 后写模型 diff、Compact 后沿用旧 baseline、Patch JSON 无法应用
图 8.23-2:同一操作在不同故障点留下的状态不同,恢复责任必须回到拥有该状态的组件。
故障点已留下的状态可观察结果恢复责任
Patch 前没有 full缺少解释基线恢复不能猜当前状态清除 baseline 并等待下一 full
先写 patch 后写模型 diff崩溃可能只留下状态历史与基线顺序不一致先 context item,后 WorldState
Compact 后沿用旧 baselinereplacement 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。

源码导航

相邻测试也很重要:

本节结论

WorldState 持久化的是可重放快照链:新窗口或无基线时写 full,稳态只写 JSON Merge Patch;恢复必须按时间顺序应用,并在 Compact 或非法 Patch 处重置可信基线。

阅读导航

上一节:8.22 · 下一节:8.24

评论


← 返回文章列表