深入 Harness:Codex 如何处理新消息与中断
Codex 把三件事分开处理:
- 新消息:改变当前工作的后续方向。
- 主动停止:结束当前 turn,并记录中断状态。
- 清理后台进程:单独管理已脱离当前 turn 的终端任务。
新消息通常不会创建并发 turn,也不会取消正在执行的工具;主动停止也不等于回滚副作用或关闭所有后台进程。
1. 新消息如何进入执行
空闲时
用户消息通过 turn/start 创建一个新 turn。
运行中
用户消息通过 turn/steer 追加到当前 regular turn:
用户发送消息
→ 校验 active turn
→ 写入 pending input
→ 当前阶段结束
→ 下一次模型请求读取新消息
steer 必须携带预期的 turn id,避免迟到消息进入已经切换的任务。
普通 turn 即使正在等待工具,也可以接收 steer:消息先进入父 turn 的 pending input,工具结束后,父 turn 再带着工具结果和新消息继续请求模型。
Review 和 Compact 不走这个普通循环。Review 由专用任务启动一次性审查子 Agent,收到审查结果后直接结束;Compact 完成上下文压缩后也直接结束。它们没有“读取 pending input 后再次请求父模型”的阶段,因此不接受同一 turn 内的 steer。TUI 会保留消息,等任务结束后再创建普通 turn;这属于后续消息,不是对 Review 或 Compact 的转向。
模型正在输出时
新消息到达后,Codex 会先抢占本地的流式等待。若当前 Responses WebSocket 支持中断,它会向模型服务发送:
{
"type": "response.interrupt",
"response_id": "...",
"mode": "discard_partial_items"
}
steer 使用 Drain 模式:保留已经完成的响应项,丢弃未完成项,并继续读取流直到服务端完成收尾,然后在同一 turn 内携带新消息发起下一次模型请求。它不结束 turn,也不取消正在运行的工具。
如果当前传输不支持中断,Codex 只能停止本地消费并丢弃后续响应,不能保证模型服务立即停止计算。主动停止 turn 时,支持中断的传输会使用 Cancel 模式,并等待服务端返回中断成功或失败。
如果新消息到达时正在执行工具,需要等工具返回后才能进入下一次模型请求。
2. 主动停止如何执行
TUI 发送 turn/interrupt,并指定要停止的 turn id。服务端确认目标仍是 active turn 后,开始中断:
移除 active turn
→ 取消 turn 级任务
→ 等待任务短暂自行退出
→ 强制终止未退出的任务
→ 执行清理和 interrupt hooks
→ 写入中断历史
→ 发出 TurnAborted
turn/interrupt 在收到 TurnAborted 后才返回成功。因此,它表示 turn 已完成中断收束,不只是取消信号已发送。
中断信号会传给模型流、前台 shell、审批和用户提问等等待状态;未及时退出的任务会被强制终止。发出 TurnAborted 前,Codex 还会写入模型可见的中断标记,说明上一轮由用户停止、工具可能只执行了一部分、后台进程可能仍在运行。
中断不是事务回滚。文件修改、网络请求或外部工具已经产生的副作用不会自动撤销。
3. 新消息与停止同时发生
典型竞态是:
用户发送 steer
→ 消息尚未被模型消费
→ 用户立即停止 turn
Core 会清空旧 turn 的 pending input,不会自动把它带到下一 turn,因此 TUI 会保留一份尚未确认的消息。默认 TUI 用按键区分用户意图:
Esc:如果存在 pending steer,中断旧 turn 后立即把这些消息合并成新 turn;没有 pending steer 时只中断。Ctrl+C:只中断,尚未生效的消息恢复到输入框,不自动提交。
例如 Agent 正在执行方案 A,用户发送“改用方案 B”,但模型还没读到:按 Esc 会停止方案 A,并用“改用方案 B”启动新 turn;按 Ctrl+C 只停止方案 A,“改用方案 B”回到输入框。
职责边界是:
Core 保证旧 turn 干净结束
TUI 保证用户未完成的输入不丢失
4. 后台进程为何单独处理
interrupt 会取消当前 turn 内仍在执行的工具调用。对于前台 shell,工具调用和进程仍绑定在一起,因此进程也会被终止。
但长时间运行的命令可能已经转为后台终端:此时工具调用已经返回,进程成为 thread 级资源,不再属于当前 turn。interrupt 不会终止这类进程。
后台开发服务器、watcher 或长任务可能是用户有意保留的,因此 Codex 提供独立操作:
- 查看后台终端。
- 终止指定进程。
- 清理当前 thread 的全部后台终端。
这意味着“Agent 已停止”和“机器上没有相关进程运行”是两个不同状态。
5. 设计判断
Codex 的关键设计不是取消能力本身,而是把控制粒度拆清楚:
| 动作 | 作用范围 |
|---|---|
| steer | 当前 turn 的后续模型请求 |
| sampling preempt | 当前模型响应 |
| interrupt | 当前 turn |
| background terminal cleanup | thread 下的后台进程 |
这套设计保护了四个不变量:
- 同一 thread 不并发运行多个用户 turn。
- 新消息不会破坏工具调用与结果的配对。
- 中断完成后,历史明确记录“可能部分执行”。
- 停止 Agent 不会误杀用户有意保留的后台服务。
代价是客户端必须维护 pending 消息、处理 turn id 竞态,并清楚展示后台进程状态。
6. 可复用原则
其他 Agent Harness 可以直接复用以下边界:
- 把运行中新消息建模为 pending input,不创建并发 turn。
- steer 和 interrupt 都必须绑定目标 turn id。
- 分离模型采样抢占与整个 turn 的取消。
- 先完成清理和状态持久化,再确认 interrupt 成功。
- 在历史中记录中断及部分执行风险。
- 由客户端保存尚未确认的用户输入。
- 后台进程使用独立生命周期。
一句话概括:
新消息决定接下来怎么做,主动停止负责让当前工作可靠结束,后台清理负责回收已经独立运行的资源。
更多推荐

所有评论(0)