English

第 7 章:Resume、Rollback、Fork 与 Replay

阅读契约: 把本章当作 reconstruction 问题来看 resume、rollback、fork 和 replay。阅读时找出哪些持久事实让 Codex 能在时间流逝或分支之后重建 prompt state。

第 6 章把 compaction 解释为 checkpoint 协议。这套协议只有在系统之后能重建有效上下文时才划算。Codex 必须能 resume 旧 thread、回滚最近用户 turn、为子 agent 派生工作、并向客户端 replay 足够多的历史。进程退出或分叉之后,runtime 不能信任内存里的 vector,必须从 rollout 证据重建。

重建代码是 Codex 上下文纪律最清楚的例子之一。它从新到旧扫描 rollout items,找到最近仍有效的 replacement-history checkpoint 与 resume 元信息,然后把存活的后缀向前重放。Rollback 标记在扫描时被解释,重建出的历史反映的是有效状态,而不是 raw 事件顺序。

读完本章,你应该明白持久上下文与持久 transcript 不是一回事。

Rollout reconstruction 逆向扫描 checkpoint、rollback marker、agent envelope、user turn 与 assistant turn 的边界
Resume 与 fork 都是投影问题:raw rollout 仍保持 append-only,重建过程选择最新存活 checkpoint、应用 rollback marker,并尊重 agent 触发的边界。

重建的三个输出

重建返回三件东西:

输出为什么重要
重建的历史后续 turn 用的模型可见账本。
上一次 turn 设置resume 时决定模型/realtime diff 所需的元信息。
Reference context item设置 diff 的 baseline,或显式的”已清空”状态。

后两个输出容易被忽视。Resume 不只是”加载消息”,它还必须恢复第 4 章 diff 系统使用的上下文 baseline。如果 compaction 清空了那个 baseline,resume 必须保持已清空的状态。 逆向扫描高效,因为一旦找到更新的存活 replacement-history checkpoint 与所需元信息,旧 rollout items 就与重建无关。

阅读 Rollout 布局

Rollout 是一份 append-only 的结构化 item 日志,典型片段会混合 initial context、user turns、assistant turns、tool observations、checkpoint、rollback marker 和 fork boundary。

扫描从右往左走,碰到最近的存活 checkpoint 就短路。例子里 u3u4 之间的 CP 就是这个点,于是 i_cu1a1t1u2a2 对重建已无意义。Rollback 标记 RB 在扫描中被解释。

逆向扫描算法

算法不长但谨慎。下面的伪代码忠实地反映其形状:

// 伪代码 -- 逆向重建。
rebuiltSuffix      = []
referenceCleared   = false
sawCheckpoint      = false

for item in reversed(rollout):
    if not sawCheckpoint and item.isReplacementCheckpoint():
        rebuiltSuffix = item.replacementBase + rebuiltSuffix
        sawCheckpoint = true
        continue

    if item.isRollbackMarker():
        applyRollback(rebuiltSuffix, item.scope)
        continue

    if item.isPreviousSettings():
        previousSettings = previousSettings or item.value
        continue

    if item.isReferenceContext():
        referenceContext = referenceContext or item.value
        if item.cleared: referenceCleared = true
        continue

    if not sawCheckpoint:
        rebuiltSuffix.prepend(item)

if referenceCleared:
    referenceContext = None

return rebuiltSuffix, previousSettings, referenceContext

注意三条性质。其一,循环在拿到所需信息后立刻终止;其二,“已清空”标志即使较早出现(逆序中较晚扫到),也会胜过更早的 baseline;其三,rollback 标记在构建过程中应用,避免对 suffix 编辑两次。

Rollback 改变过去的含义

Rollback 标记不会删除 raw rollout 记录,它改变哪些 user-turn 段落计入有效历史。逆向扫描时,Codex 把”丢弃最近 N 个用户 turn”理解为”跳过接下来 N 个完成的 user-turn 段落”。这让重建在保留 raw 证据的同时,重建出用户要求的状态。

同样的逻辑出现在 rollout truncation helpers:在应用 rollback 标记的同时跟踪 user-message 位置。Fork-turn 位置同时包含真实用户消息与触发 turn 的 assistant inter-agent envelope;rollback 按 instruction-turn 边界移除过期后缀。

这是一个严肃设计:rollback 是账本里的事件,而不是对日志的破坏性编辑。

朴素做法Codex 做法
修改日志:删除被回滚的 items。追加一个 marker, 在重建时应用。
Resume 直接读磁盘上的内容。Resume 读取 rollout 并做投影。
审计只能看到存活状态, 看不到为什么存活。审计可以重放回滚决定。
删除前后读到的客户端会出现分歧。所有客户端看到同一份 append-only 日志。

Codex 方式付出少量重放代价,换来完整可审计性。

Fork 边界不止是人类消息

多 agent 工作让上下文边界更复杂。子 agent 可能从 assistant inter-agent envelope 而不是普通用户消息开始。Codex 的 fork turn 逻辑把某些 assistant envelope 在它们触发 turn 时视为边界,保住了被委托工作的语义单元。

// 伪代码 -- 说明有效 fork 截断。
for item in rollout:
    if item.isRollbackMarker():
        removeRolledBackInstructionTurns()
    if item.isRealUserMessage()
       or item.isTriggeringAgentEnvelope():
        rememberForkBoundary(item.position)
return suffixStartingAtNthBoundaryFromEnd()

这个模式不限于多 agent 场景。如果你的 runtime 启动工作的方式不止一种,上下文截断必须理解所有方式。

Fork 边界规则很简洁: AGENT_ENVELOPE 是触发了子 agent turn 的 assistant 消息。它被视为边界,因为从它开始的后缀本身是一个工作单元。用户消息也是边界,但不是唯一的种类。

遗留 Compaction

重建代码仍然处理没有 replacement history 的遗留 compaction 记录:从用户消息和已存的 compaction message 重建 compacted history、清空 reference baseline、接受相对不那么理想的 prompt 形状。这条向后兼容路径有教育意义:新 checkpoint 协议存在,正是因为单纯摘要不够。

这也是源码区分持久 rollout 证据与 live history 的原因。Live history 可以随时间改进,rollout replay 则要兼容旧记录。

遗留判断规则虽小但值得命名: 分支从不”无声”产生劣化 prompt;形状差异是显式的、被记录的、telemetry 可见的。

应用模式

  1. Replay From Evidence: 从 append-only rollout 事实重建上下文;迁移时把 live 内存当作缓存;避免 resume 路径信任过期内存状态。
  2. Reverse Checkpoint Search: 反向扫描找最新存活起点;迁移到事件溯源系统;有 checkpoint 能界定工作量时,不要重放整段日志。
  3. Rollback Marker: 把 rollback 记录为事件;迁移时在重建时应用 marker;避免破坏性日志编辑抹掉可审计性。
  4. Semantic Boundaries: 显式定义 user、agent 与 fork turn 边界;迁移到每一种工作来源;拒绝只懂人类消息的截断。
  5. Legacy Bridge: 保留兼容路径但清空不安全 baseline;迁移时把正确性放在 prompt 完美形状之前;不要把旧记录当作新 checkpoint。