回合循环:采样、工具与继续条件
把一次用户回合拆成多次模型请求,追踪继续条件、异步结果与停止路径。
学完你能做到
- 区分用户与请求
- 追踪流式事件和工具结果
- 理解继续、结束、与取消的分支
本课目录 · 4 节
接受输入,准备这一轮的工作材料
核心接受操作并保留输入相关元数据。run_turn 收到初始输入、状态和取消信号。开始时可能处理前置与;随后准备本步的、工具计划和历史,再发起。这里的“步”是一次请求所依赖的配置与环境快照,避免中途设置变化让调用失去对应关系。
跟着一次调用走
教学模拟:不调用模型、不执行命令。输出按事件到达
不是一条完整字符串。run_turn 所在模块处理输出项完成、文字增量与请求完成等事件。工具相关输出进入工具执行路径;普通文字则进入消息与历史处理。不能因为已经出现几个文字片段,就断言这一结束。
完成后,结果记录进历史;下一次使用整理后的历史。因此能看到工具返回的真实内容,而不仅是先前的计划。工具错误也可能以结果形式反馈;致命错误和取消则有单独处理路径。
谁决定再来一次?
let (has_pending_input, token_status) = async {
let has_pending_input =
sess.input_queue.has_pending_input(&sess.active_turn).await;
let token_status = super::context_window::context_window_token_status(
sess.as_ref(),
turn_context.as_ref(),
)
.await;
(has_pending_input, token_status)
}
.instrument(trace_span!("run_turn.collect_post_sampling_state"))
.await;
let needs_follow_up = model_needs_follow_up || has_pending_input;
let token_limit_reached = token_status.token_limit_reached;继续条件同时考虑是否需要后续处理,以及有没有等待中的输入。不要把“出现 assistant 文本”简化成唯一的结束条件。
查看原文及相邻代码 ↗若需要继续且达到限制,代码可能进入整理历史的分支。若不需要继续,还要运行停止;它可能改变停止行为。用户取消也可以打断网络或工具等待。基础图省略了这些分支,但读源码时必须保留它们。
沿着源码读
以下定位均锁定本课程版本。先读注释与分支,再深入类型定义。
- 提交的数据及关联元信息
codex-rs/core/src/session/submission.rs:1–20 - 入口与前置整理
codex-rs/core/src/session/turn.rs:150–210 - 历史准备与继续条件
codex-rs/core/src/session/turn.rs:515–575 - 历史整理及停止判断
codex-rs/core/src/session/turn.rs:609–675 - 流式输出项处理
codex-rs/core/src/session/turn.rs:2674–2814
实现推演
写一条事件轨迹:用户问测试入口 → 请求读取文件 → 返回文件内容 → 说明测试命令。数一数这至少包含几次请求,再想想读取失败后会发生什么。
阅读练习无需模型账号。示例命令请先确认目录与环境。
自测与边界检查
用户发了一条消息,是否只能有一次请求?
展开自测答案
不是。基础例子至少有两次:一次提出读取请求,一次消费结果并回答。真实运行还可能有重试、更多或历史整理。
为什么 needs_follow_up 还要看 pending input?
展开自测答案
运行过程中可能收到新的用户输入。框架需要把待处理输入纳入推进判断,避免仅根据上一条响应就结束。