雨天小六

读懂 Codex(3.16):Steer 怎样改变活动 Turn 的后续采样

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

#Codex#Agent Runtime#生命周期#软件架构

Steer 不会修改正在传输的 HTTP/SSE 请求,也不会立即创建第二个 Turn。它在 ActiveTurn 锁下核对目标,把输入追加到当前 TurnState,并由 run_turn 在下一个安全采样边界写入历史。

接受 Steer 的四个前置条件

  1. ActiveTurn 存在且其中仍有 RunningTask;
  2. 调用者给出的 expected Turn ID(若有)与活动 id 一致;
  3. TaskKind 是 Regular;
  4. 用户 input 非空。

任一条件失败都不修改 TurnState。NoActiveTurn 会把原始 input 返还给上层,使 UserInput handler 能用它创建新 Turn;ID mismatch 和非 steerable task 则明确报错,不能降级为“随便启动一轮”。

Steer 对活动 Turn、expected id、TaskKind 和空输入的校验
图 3.16-1:校验与 pending update 在同一个 ActiveTurn 临界区完成,避免检查的是 Turn A、写入时已经变成 Turn B。
async def steer(input, expected_id=None):
    async with active_turn.lock() as active:
        if active is None or active.task is None:
            raise NoActiveTurn(return_input=input)
        task = active.task
        if expected_id is not None and expected_id != task.turn.id:
            raise ExpectedTurnMismatch(expected_id, task.turn.id)
        if task.kind != REGULAR:
            raise ActiveTurnNotSteerable(task.kind)
        if not input:
            raise EmptyInput()

        extra = await merge_additional_context()
        pending = [ResponseItem(x) for x in extra]
        pending.append(UserInput(input))
        await queue.extend_and_accept_mailbox(active.turn_state, pending)
        return task.turn.id

源码明确接受“持有 ActiveTurn lock 时再取得 SessionState/TurnState”的组合,并用 lint expectation 记录原因:目标检查和队列更新必须原子。其他路径若采用相反锁顺序就可能死锁,因此这是需要全局保持的锁序约束。

Steer 在模型哪里生效

正在运行的 stream 使用已经构造的 Prompt 和 StepContext,不会被中途改写。采样完成后 has_pending_input 使 needs_follow_up 为真;下一轮开头 get_pending_input 排空,hooks 处理并把内容记录进 ContextManager,然后才构建新 Prompt。

Steer 在当前模型流之后、下一次模型采样之前生效
图 3.16-2:Steer 的线性化点是 TurnState 入队;模型可见点是下一采样前的历史记录,两者不是同一时刻。
# 当前 request 已发出,不修改
current_result = await consume_current_stream()

if input_queue.has_pending_input(turn):
    steers = await input_queue.get_pending_input(turn)
    await hooks_and_record_inputs(steers)
    next_step = await capture_step_context(turn)
    await sample(history_including(steers), next_step)

AdditionalContext 先与 Session 跨 Turn accumulator merge,再转换为 ResponseItems排在 UserInput 之前。可选 Responses API client metadata 写入 TurnMetadataState,使后续请求携带最新调用方元数据,但不会更换 Turn id。

expected Turn ID 解决什么竞态

客户端看到 Turn A 正运行后发 steer;网络延迟期间 A 已完成、B 已开始。没有 expected id,文字会被合法地写入 B,语法上成功、语义上错误。expected id 把客户端观察转成 compare-and-set:只有活动 id 仍等于 A 才提交。

Review 和 Compact 拒绝 Steer 是输入契约,而非技术限制。Review 使用合成 rubric 和受限子线程;Compact 重写历史。把普通用户输入混进它们会破坏输出解析或压缩边界。

评论


← 返回文章列表