run_turn 不是简单的 while-model-call。一次调用先建立模型可见世界,再执行零到多次采样,最后把 last agent message 或错误交回 Task wrapper。持久化终态和清 ActiveTurn 不属于它,而属于更外层的统一任务生命周期。
三阶段边界
准备阶段按顺序完成:预采样 compact;从用户输入解析 required MCP servers 和 plugin mentions;捕获 first StepContext;记录 world-state/reference context;建立 skill/plugin injections;执行 session-start 和 input hooks;记录原始输入及 injection;更新 connector selection、PreviousTurnSettings 与 analytics。
这些动作有严格先后。首 Step 必须先于 world-state 记录,因为后者要保存模型真正看到的环境和工具上下文;输入 hooks 必须先于采样,因为它们可能要求停止或补充上下文;PreviousTurnSettings 在历史记录后用于下一轮差异注入。
async def run_turn(session, turn, input, cancel):
await maybe_pre_sampling_compact(turn, cancel)
required, plugins = await resolve_mentions(input, cancel)
first_step = await capture_step(turn, required, cancel)
world = await record_reference_world_state(first_step)
injections = await build_skills_plugins(first_step, input, plugins, cancel)
if await run_session_start_hooks(turn): return None
if await hooks_and_record_inputs(turn, input): return None
await record_injections_and_resolved_settings(injections)
return await sampling_loop(turn, first_step, world, cancel)
循环阶段每次做什么
循环在允许时排空 pending input并先运行 hooks;记录 rollout/token/time reminder;取得 first 或重新捕获 StepContext;若世界状态变化则追加 diff;从 ContextManager 克隆并按模型输入模态编译历史;构造 metadata;进入 run_sampling_request。
采样函数内部再分两层:外层负责 retryable stream error,复用同一个 ModelClientSession;内层消费 Responses stream、投影增量、调度工具并等到 response.completed。
async def sampling_loop(turn, first_step, world, cancel):
next_step = first_step
while True:
pending = await drain_if_allowed()
await hooks_and_record_inputs(turn, pending)
step = next_step or await capture_step(turn, required_by(pending), cancel)
world = await record_world_diff_if_changed(world, step)
prompt_input = history.for_prompt(turn.model.input_modalities)
result = await run_sampling_request(step, prompt_input, cancel.child())
if await should_compact_and_continue(result):
next_step = await compact_and_prepare_continuation(step)
continue
if not await needs_follow_up(result):
return result.last_agent_message
错误收尾不是一个动作
预采样 compact 的普通失败会发 turn-error lifecycle 并返回 None;TurnAborted 原样向外传播。required server 或 Step 捕获若被取消,会先确保输入 hooks/记录符合约定再返回。采样期 InvalidImageRequest 映射成 BadRequest 和用户可理解错误;其他 CodexErr 发 Error 事件并结束本次 run_turn,允许 Thread 继续接收下一轮。
run_turn 最终只返回 Option<String>。它不发送 TurnComplete、不计算整轮 Token delta、不清 ActiveTurn。Task wrapper 先 flush,on_task_finished 再执行这些公共动作。这样 Compact/Review/UserShell 也能复用相同终态规则。
评论
登录后即可评论