跳转至

06. 模型流式请求与重试

模型层连接模型服务和本地运行时。它负责把当前轮次的历史、上下文规则和工具说明转换成一次采样请求,并把模型流式返回的事件转换成可展示、可记录、可执行的结构。这里的 API 指模型服务接口;模型层承担智能体循环中“模型决策”和“本地执行”之间的事件转换。

一次 Turn 内可以包含多次模型请求。每次请求都基于最新历史和最新工具集合重新构造,上一轮工具结果会影响下一轮模型决策。

flowchart TD Req[采样请求<br/>历史 + 上下文 + 工具] --> Stream[模型事件流] Stream --> Created[响应 created] Stream --> Delta[文本/推理/工具参数增量] Stream --> Done[输出条目完成] Stream --> Completed[响应完成] Delta --> UI[客户端增量] Done --> Hist[完整条目进入历史] Done --> Tool{工具调用?} Tool -->|否| Continue[等待完成] Tool -->|是| Dispatch[工具分发] Dispatch --> Output[工具输出] Output --> Hist Completed --> Follow{需要后续?} Follow -->|是| Req Follow -->|否| TurnDone[交给任务收尾]

采样请求的组成

每次采样前,运行时会生成一份请求快照。快照包含以下部分:

模型输入:用户输入、助手历史、工具调用、工具输出、上下文条目。
基础指令:系统/开发者指令、权限说明、环境说明、协作模式。
工具说明:本轮模型可见工具规范。
输出约束:输出结构、响应格式、模型能力限制。
运行元信息:对话 id、模型参数、推理设置、传输配置。

历史在进入请求前会被规范化。缺失输出的工具调用会被处理,孤立工具输出会被清理,当前模型不支持的内容会被过滤,过长工具输出可能被截断。规范化阶段保持模型接口输入合法,同时保留任务继续所需的因果关系。

工具说明也在采样前计算。MCP 工具、动态工具、插件连接器、内置工具和延迟工具的可见性都可能随轮次状态变化。模型本次能请求哪些工具,由这次请求中的工具规范决定。

流式响应生命周期

模型返回的是事件流。运行时维护一次响应的生命周期,并把事件映射到历史、客户端通知和工具执行。

响应 created
  -> 输出条目开始
  -> 增量 events
  -> 输出条目完成
  -> 响应完成

响应创建事件建立一次模型响应的上下文。输出条目开始表示模型开始生成一个结构化输出项。增量事件表示这个输出项的内容正在流式增长。输出条目完成表示该条目已完整,可以被记录或执行。响应完成表示本次模型请求结束,并携带用量、完成状态和可能的服务端元信息。

这个生命周期使运行时能够同时满足三个需求:客户端实时展示增量,历史记录完整条目,本地工具只在工具调用完整后执行。

文本输出处理

助手文本通常以增量到达。运行时会将增量立即转成客户端事件,使终端界面或一次性执行能显示模型正在输出的内容。

增量不是最终历史记录。完整输出条目完成后,运行时才将文本条目作为稳定历史保存。这样既支持实时显示,又避免恢复历史时依赖零散增量。

文本输出不会触发本地副作用。它只改变客户端展示和模型历史。模型说“我将运行测试”并不执行测试;只有后续出现 shell 工具调用,运行时才会进入工具链路。

推理与结构化输出

推理内容、计划、内部摘要或其他结构化输出与普通助手文本分开处理。它们可能进入客户端事件,也可能进入历史或遥测,但不会被混同为用户可见正文。

这种区分保持了输出语义。助手消息是用户沟通内容,推理内容或结构化条目是运行过程记录。不同条目在客户端、历史和持久化中的用途不同。

工具调用参数流

工具调用也以流式事件返回。模型可能先生成工具名称,再逐步生成 JSON 参数。运行时在参数流结束前不会执行工具,因为半截参数可能不是合法 JSON,也可能缺少关键字段。

完整工具调用 item done 后,运行时才进行以下步骤:

解析工具名称和参数
匹配本轮工具路由器
构造本地 ToolCall
交给对应处理器
进入权限和沙箱流程
执行或拒绝工具

这一设计保证本地工具执行基于完整且可校验的调用,而不是基于模型流中间状态。

工具任务与结果回填

工具调用可能需要时间。shell 命令会运行进程,MCP 调用会等待外部服务端,补丁应用会检查和修改文件。运行时会将这些动作作为本地工具执行任务处理,并在执行期间继续发出事件。

工具完成后,结果被转成模型可读的工具输出。工具输出与原始工具调用形成对应关系,一起写入历史。下一次模型采样时,模型能够看到:

自己请求了哪个工具
本地运行时是否允许执行
工具实际返回了什么
失败或拒绝的原因是什么

这条因果链是智能体循环的核心。模型后续推理依赖真实工具反馈,而不是依赖客户端日志或自然语言摘要。

后续判断

一次响应完成后,运行时会判断是否需要继续采样。需要后续的典型情况包括:

模型产生了工具调用,且工具结果需要交回模型。
模型返回 end_turn=false。
Session 中存在待处理输入。
上下文窗口触发压缩,压缩后需要继续当前轮次。
工具拒绝或失败需要模型调整方案。

后续采样会重新构造请求。运行时不会把工具输出简单拼成一段普通文本,而是以结构化工具输出形式放回历史,并重新计算工具集合和上下文变化。

完成与轮次完成

响应完成只结束一次模型请求。轮次完成需要整个普通任务收束。二者之间可能存在工具执行、权限审批、待处理输入合并、压缩和再次采样。

响应完成后若存在工具调用,运行时进入工具执行;工具执行后若需要后续,再次进入模型采样;所有工具和输入处理完后,普通任务才发送轮次完成。

用量与模型状态

模型完成事件通常携带 token 用量。运行时会将用量计入轮次和 thread 统计,并通过事件通知客户端。用量记录用于展示、预算判断和后续运行状态。

模型服务还可能返回 server model、rate limit、routing token 或其他元信息。这些信息不会直接改变用户可见文本,但会影响后续采样、连接复用或重试策略。

HTTP 与 WebSocket

Codex 可以通过 HTTP Responses API 或 Responses WebSocket 与模型通信。WebSocket 用于降低延迟和复用连接,HTTP 提供通用降级。传输方式不改变模型事件的语义;两种通道最终都要产出统一的响应事件、output item、增量、工具调用和完成状态。

WebSocket 可以预热。预热失败不会改变任务语义,运行时可继续使用 HTTP。WebSocket 运行中出错且允许降级时,运行时会切换传输通道,使底层连接问题不直接等同于智能体任务失败。

重试边界

重试只覆盖可恢复的模型传输问题。临时网络错误、服务端可重试错误、WebSocket 断开等可以触发重试。

重试不覆盖已经完成的本地工具动作。工具结果写入历史后,本地副作用已经发生;后续重试只能重试模型请求,不能重新执行已经完成的 shell、补丁或 MCP 调用。该边界防止模型传输失败造成命令重复执行或文件重复修改。

压缩后的模型窗口

压缩会替换旧历史,改变模型看到的上下文窗口。压缩完成后,运行时需要使模型连接状态与新的历史对齐。后续采样基于压缩 replacement 历史,而不是继续沿用旧窗口。

如果传输层持有旧窗口或 routing 状态,运行时会推进窗口代数或重置相关 session 状态。这个步骤保证模型服务端和本地历史对当前上下文达成一致。

错误传播

模型层错误会按性质进入不同路径。可重试传输错误进入重试;WebSocket 失败可进入 HTTP 降级;不可重试模型错误进入轮次错误;上下文限制相关错误可能触发压缩;工具执行错误则作为工具输出交回模型或导致工具条目失败。

错误传播的目标是保持边界清晰。模型传输错误不伪装成工具失败,工具失败不伪装成模型拒绝,权限拒绝不伪装成普通命令输出。不同错误类型对应不同恢复策略。

模型层在智能体循环中的位置

模型层将历史、上下文和工具说明交给模型,将模型流式输出拆解成事件,将工具调用交给本地运行时,并将工具结果接回下一轮采样。它保持文本展示、历史记录、工具执行和重试边界之间的一致性。

因此,模型流式处理的实现质量直接影响智能体循环的可靠性。事件解析不完整会导致客户端展示错误;工具调用处理过早会执行不完整参数;重试边界不清会造成副作用重复;完成语义处理过粗会导致轮次提前结束或无法收束。