Codex 源码导读:第八部分——Agent Loop 状态机

1 阅读2分钟

第八部分:Agent Loop 状态机

总体结构

入口
  → 准备上下文并调用模型

处理
  → 处理文本、工具调用、工具结果和新输入

出口判断
  ├─ continue:继续下一轮
  ├─ compact:压缩后继续
  ├─ break:正常结束
  └─ return error:异常或取消

run_turn() 承担本段内层循环的编排,run_sampling_request() 负责一次模型采样及其工具处理。下面的状态图是源码控制流的概念归纳,不是代码里已有的枚举;采样过程中会边读流边启动工具,并非模型流结束后才开始执行工具。

Agent Loop 状态机

一次 Turn 的主流程

用户输入
  ↓
准备规则、Skill、MCP、工具和环境
  ↓
调用一次模型
  ↓
处理文本、工具调用和工具结果
  ↓
判断是否还有工具或新输入
  ├─ 有,且上下文足够 → continue
  ├─ 有,但上下文已满 → compact → continue
  └─ 没有 → 运行 Stop Hook
                   ├─ Hook 要求补充 → 写入提示 → continue
                   └─ 允许结束 → break

关键判断变量位于 core/src/session/turn.rs:

  • model_needs_follow_up:模型或工具是否要求继续。
  • has_pending_input:用户或子 Agent 是否有新消息。
  • needs_follow_up:以上两者的合并结果。
  • token_limit_reached:是否达到自动压缩阈值;本轮还需要继续时,阈值命中或主动换窗口请求才触发 should_roll_over。
  • stop_outcome.should_block:Stop Hook 是否阻止结束;还需有有效补充提示,才会再次请求模型。
  • stop_outcome.should_stop:Hook 是否明确要求停止,命中时直接 break,不再运行后续 legacy after-agent hook;它不是唯一的结束条件。

这不是一个单独的状态枚举,而是由这些判断值和 continue、break、return 共同组成的状态机。

这里的结束是“本次 run_turn 返回”,不是删除 Session,也不是关闭 Agent。外层 RegularTask 还会检查待处理输入,必要时再次调用 run_turn;没有后续输入才进入任务收尾。普通错误有些会先发错误事件再返回,取消则按 TurnAborted 向上传递,不能把所有失败都等同于 return error。

代码锚点

  • core/src/session/turn.rs:155:Turn 初始化和首次上下文准备。
  • core/src/session/turn.rs:304:主 loop,读取待处理输入。
  • core/src/session/turn.rs:370:构造历史输入并调用 run_sampling_request。
  • core/src/session/turn.rs:412-473:合并 follow-up、待处理输入和 token 状态,决定是否压缩。
  • core/src/session/turn.rs:490:上下文压缩后 continue。
  • core/src/session/turn.rs:503-562:Stop Hook 和最终 break。
  • core/src/tasks/regular.rs:76-95:外层检查待处理输入,决定是否再次调用 run_turn。

源码基线:OpenAI Codex d58d0e5841e0de08e251673db2d5af8cf3a1ad51。文中的流程图用于标出本篇所处的运行阶段。