具体问题与边界
哪些数据在一次用户 Turn 内必须固定,哪些数据要在每次模型采样前重新观察?
TurnContext 持有顶层指令和取消;StepContext 持有序号与新捕获的 WorldState;文件副作用必须在下一 Step 可见。 本节不是把 Rust 改写成 Python;它从锁定提交的字段、调用顺序和测试行为提炼实现合同,再检查 Mini Codex 是否以 Python 的并发原语保持同一条不变量。
协议、类型与状态所有权
| 对象 | 创建/所有者 | 生命周期与作用域 | 是否持久化 |
|---|---|---|---|
| TurnContext | Session._execute_turn | 整个 Turn 不变 | Turn 语义可投影,Python 对象不持久化 |
| StepContext | Session._capture_step | 一次模型采样 | 否 |
| WorldState | WorldStateBuilder | 每 Step 新值 | 变化时转成 Developer Item 持久化 |
_last_world_state | Session | 跨 Step 比较基线 | Mini 仅内存;官方有 full/patch 恢复 |
正常路径
- Turn 开始时冻结 base instructions 和 CancellationToken,避免同一用户请求中途切换顶层行为。
- 每次循环顶部先检查取消,再调用 WorldStateBuilder 重新扫描 workspace 文件集合。
- StepContext 获得新的 step ID 和递增 sequence;模型请求、工具调用上下文都引用这个 Step。
- 新 WorldState 与上次不同才生成 Developer Message。该 Item 先进入 History/Rollout,再构造 ModelRequest。
- 工具创建文件后,下一轮采样会重新捕获;测试断言第二个请求看见新文件且 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 沙箱。
失败、取消与恢复
| 故障或错误设计 | 会留下什么 | 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 测试需要显式环境变量,默认跳过。单项测试名用于定位,不代替对断言内容的解释。
官方源码导航
- codex-rs/core/src/session/turn_context.rs:TurnContext 字段构造与持久化投影
- codex-rs/core/src/session/step_context.rs:StepContext 对 TurnContext 的引用
- codex-rs/core/src/session/mod.rs:Step 捕获与 WorldState 记录
- codex-rs/core/src/context_manager/history.rs:WorldState full/patch baseline
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 的动态观察覆盖;任何影响下一次推理的文件集合变化必须在该请求构造前进入模型视图。
尚未覆盖的生产问题由后续单元继续展开;公开版保留官方源码链接和可运行测试合同。
评论
登录后即可评论