Agent Loop

Codex 的 agent loop 位于 codex-rs/core/src/session/turn.rsrun_turn。源码注释里把模型输出分成两类:函数调用或 assistant message。实现上,loop 会持续采样,直到模型不再需要工具 follow-up,也没有 pending user input。

Agent Loop

采样前

一次 turn 开始后,run_turn 先做几件事:

  • run_pre_sampling_compact:如果上下文接近阈值,先压缩。
  • record_context_updates_and_set_reference_context_item:记录 turn context 的差量或 baseline。
  • build_skills_and_plugins:根据用户输入里的显式 mention、可用插件、MCP 工具和 connectors 构建注入项。
  • run_pending_session_start_hooksrun_hooks_and_record_inputs:让 hook 有机会阻止或改写输入。

这说明 Codex 的 prompt 不是静态模板,而是每个 turn 动态组装的运行时视图。

采样中

每次进入 loop,核心步骤是:

  1. InputQueue 取 pending input。
  2. 运行 pending input hook 并写入历史。
  3. 记录时间提醒、rollout budget 提醒等上下文。
  4. clone_history().for_prompt(...) 生成模型输入。
  5. 通过 run_sampling_request 请求模型流。
  6. 解析 response item,遇到 tool call 就通过 ToolCallRuntime 执行。

ModelClientSession 是 turn-scoped 的,它会跨 retry 复用,用于保持 websocket、sticky routing 等 turn 内状态。

采样后

模型返回后,loop 会判断:

  • model_needs_follow_up:模型调用工具后,需要把工具结果再发给模型。
  • has_pending_input:用户在运行期间又输入了内容。
  • token_limit_reached:上下文超过自动压缩阈值。

如果需要继续,就再次采样;如果 token limit 触发,则调用 run_auto_compact,再继续。

结束条件

当既没有 follow-up,也没有 pending input 时,Codex 会:

  • 运行 run_turn_stop_hooks
  • 如果 stop hook 要求继续,会把 hook prompt 写入历史并继续 loop。
  • 运行 legacy after-agent hook。
  • 返回最后一条 assistant message。

这意味着“一个用户 turn”内部可能包含多次模型请求、多个工具调用、一次上下文压缩,甚至 stop hook 触发的额外模型继续。

Loop Engineering 的价值

Codex 的 loop 有几个值得借鉴的点:

  • 把 pending user input 作为 loop 的一等输入,而不是简单拒绝。
  • 把压缩放进 loop,而不是只在 turn 之间做后台任务。
  • 把 hook 作为明确的阻断点和续跑点。
  • 把 token usage、window id、trace、telemetry 和 history version 都绑定到 turn。
  • 工具调用结果回到模型前会先经过统一生命周期和 hook。