雨天小六

读懂 Codex(9.4):TurnContext、StepContext 和 WorldState 刷新

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

#Codex#Agent Runtime#Python#软件架构

具体问题与边界

哪些数据在一次用户 Turn 内必须固定,哪些数据要在每次模型采样前重新观察?

TurnContext 持有顶层指令和取消;StepContext 持有序号与新捕获的 WorldState;文件副作用必须在下一 Step 可见。 本节不是把 Rust 改写成 Python;它从锁定提交的字段、调用顺序和测试行为提炼实现合同,再检查 Mini Codex 是否以 Python 的并发原语保持同一条不变量。

协议、类型与状态所有权

对象创建/所有者生命周期与作用域是否持久化
TurnContextSession._execute_turn整个 Turn 不变Turn 语义可投影,Python 对象不持久化
StepContextSession._capture_step一次模型采样
WorldStateWorldStateBuilder每 Step 新值变化时转成 Developer Item 持久化
_last_world_stateSession跨 Step 比较基线Mini 仅内存;官方有 full/patch 恢复

正常路径

TurnContext、StepContext 和 WorldState 刷新正常路径图
图 9.4-1:哪些数据在一次用户 Turn 内必须固定,哪些数据要在每次模型采样前重新观察?
  1. Turn 开始时冻结 base instructions 和 CancellationToken,避免同一用户请求中途切换顶层行为。
  2. 每次循环顶部先检查取消,再调用 WorldStateBuilder 重新扫描 workspace 文件集合。
  3. StepContext 获得新的 step ID 和递增 sequence;模型请求、工具调用上下文都引用这个 Step。
  4. 新 WorldState 与上次不同才生成 Developer Message。该 Item 先进入 History/Rollout,再构造 ModelRequest。
  5. 工具创建文件后,下一轮采样会重新捕获;测试断言第二个请求看见新文件且 step ID 已改变。

机制调用链

1. Turn 开始时冻结 base instructions 和 CancellationToken,避免同一用户请求中途切换顶层行为
→ 2. 每次循环顶部先检查取消,再调用 WorldStateBuilder 重新扫描 workspace 文件集合
→ 3. StepContext 获得新的 step ID 和递增 sequence;模型请求、工具调用上下文都引用这个 Step
→ 4. 新 WorldState 与上次不同才生成 Developer Message
→ 5. 工具创建文件后,下一轮采样会重新捕获;测试断言第二个请求看见新文件且 step ID 已改变

这里最重要的不是类名,而是控制权何时转移:创建者决定 ID 和初值,状态所有者决定何时修改,跨越 await 的调用必须明确取消、失败和可见性边界。任何绕过这些边界的“便捷调用”都会让恢复或并发测试失去确定答案。

Python 风格伪代码

async def run_turn(turn):
    for sequence in range(1, max_steps + 1):
        turn.cancellation.raise_if_cancelled()
        step = await capture_step(turn, sequence)
        request = compile(turn, step, history.model_view(), planner.plan())
        response = await sample(request)
        if response.has_tools:
            await execute_tools(step, response.calls)
        else:
            return

async def capture_step(turn, sequence):
    world = await world_state_builder.capture()
    step = StepContext(new_step_id(), turn.id, sequence, world)
    if world != last_world:
        item = developer_world_state_item(world, turn.id)
        history.append(item)
        await rollout.append_and_flush(item)
        last_world = world
    return step

伪代码只保留设计职责;Mini Codex 的可运行版本见下方实现导航。它没有伪造官方源码中不存在的 Python API,也没有把路径策略写成 OS 沙箱。

失败、取消与恢复

TurnContext、StepContext 和 WorldState 刷新失败路径图
图 9.4-2:失败必须回到实际状态所有者,不能用一条通用异常吞掉协议差异。
故障或错误设计会留下什么Mini Codex 的处理
WorldState 只在 Turn 开头捕获工具副作用对后续采样不可见每 Step 重新捕获
变化 Item 在请求后才落库本次模型仍使用旧环境先写 History/Rollout,再 compile
把 Step 配置写回 Turn后续采样污染稳定快照StepContext 单独创建
扫描失败不能构造可信环境视图异常上交 Turn owner,收束为 Aborted

必须保持的不变量

Turn 稳定字段不能被某个 Step 的动态观察覆盖;任何影响下一次推理的文件集合变化必须在该请求构造前进入模型视图。

这条不变量同时约束正常路径、异常路径和恢复路径。只在 happy path 里得到正确输出,不足以证明该模块边界成立。

设计取舍

逐 Step 扫描增加 I/O。官方 WorldState 分区和 diff 更复杂;Mini 只扫描文件名,不能推导权限、Git、网络或项目指令已被完整复刻。

源码能够直接证明类型、分支、调用顺序和测试期望;关于工程动机的解释是基于这些事实的设计归纳,不冒充未公开承诺。

测试与复现实验

cd examples/mini-codex
uv run pytest -q -k 'test_patch_changes_world_state_before_second_sample'
uv run mypy src

本节对应的关键断言:

  • test_patch_changes_world_state_before_second_sample

全量离线基线为 27 项通过;真实 Responses 测试需要显式环境变量,默认跳过。单项测试名用于定位,不代替对断言内容的解释。

官方源码导航

Mini Codex 对照

  • src/mini_codex/runtime/context.py:两层 Context 数据类
  • src/mini_codex/context/world_state.py:异步工作区扫描
  • src/mini_codex/runtime/session.py_capture_step 与刷新顺序

Mini Codex 保留本节的状态所有权、顺序和失败反馈;省略的产品能力会在边界处明确列出,不能由测试通过外推为生产等价。

本节边界

已经证明:Turn 稳定字段不能被某个 Step 的动态观察覆盖;任何影响下一次推理的文件集合变化必须在该请求构造前进入模型视图。

尚未覆盖的生产问题由后续单元继续展开;公开版保留官方源码链接和可运行测试合同。

阅读导航

上一节:9.3 · 下一节:9.5

评论


← 返回文章列表