Grok Build 是如何工作的(二):模型调用

上一章说明了三层循环。本章说明 L3 内的一次模型调用:请求如何组装、如何发 HTTP 并处理流式响应、结果如何写回、默认使用哪一类 API,以及 Session 与模型调用层的边界。

Copiloted by Grok Build.

一次模型调用的流程


1. 确定本轮请求附带内容

Turn 在调用前确定:

  • 本 turn 可用的客户端 tool 定义(含 MCP;初始化策略为阻塞时,可能先等待 MCP 就绪)
  • 可选的输出 JSON Schema
  • 可选的服务端托管 tool(由推理服务在响应过程中执行;是否启用取决于模型与配置)
  • 模型调用配置:model、temperature、reasoning effort、context window 等

tool 定义放在请求的 tools 字段中,不写入历史正文。plan mode、fork、结构化输出会改变 tools 内容。

2. 由对话状态生成请求

对话状态保存内部条目列表(system、user、assistant、tool result、reasoning、服务端已执行的 tool 记录等)。列表格式与实际发往模型服务的 JSON 无关。

每次调用模型前,按固定顺序从该列表生成一份 ConversationRequest。

  1. 复制

    复制一份将要发给模型的条目列表。后续步骤可以只改这份副本。

  2. 修复结构

    修副本里与 API 不兼容的部分,例如未闭合的 tool_call、重复的 tool_result

  3. 按需写入 memory 类 reminder

    若需要,把 memory 类 reminder 写入 system 段。
    写入目标分两种调用方式:只写进本次请求副本,或持久写进对话状态中的 system 条目(由调用参数决定)。

  4. 按需去掉较早的 inline 图片

    若序列化后的请求体接近体积上限,从副本中移除较早的 inline 图片。

  5. 按需裁剪旧 tool result

    若 token 占用超过上下文窗口的约一半,在副本上按 tool result 所属 turn 的新旧裁剪。龄期从对话尾向前数 User 条数估算。

龄期 行为 具体做法
最近 N 个 turn(默认 N=3) 不裁剪 原文完整保留在本次请求里
中等龄:超过 N,且未到 hard-clear 龄期(默认 hard-clear 龄期 = 10 个 turn) soft-trim 仅当该条正文超过字符阈值(默认 4000)时改写:保留文首 M 字 + 固定分隔标记 + 文尾 K 字(默认 M=K=1500),中间删除;分隔标记形如 […trimmed…]。未超阈值则原样保留
更早:龄期 ≥ hard-clear 龄期 hard-clear 不论原文字多长,整段正文换成固定占位句(形如 [Tool result omitted — too old]),不保留原文任何片段
  1. 组装 ConversationRequest

    填入 items、tools、model 参数、追踪用 header 字段、可选 json_schema 等。


第 4、5 步与“历史真值”

第 4、5 步只改“本次发给模型的副本”,不改对话状态中的历史真值。
另有一条与第 5 步 hard-clear 同龄期、但作用对象不同的路径:在用户消息写入对话状态时,可对内存中的历史真值做 hard-clear(不写 soft-trim),目的是限制长 session 内存占用;磁盘上的 updates.jsonl 仍可保留原文供 replay。这两条路径不要和 compact(见后文)混为一谈。

token 估计:上次服务端返回的 total(或等价字段),加上此后新增内容的字节估算。用于 compact 阈值、界面 context 显示,以及是否进入第 4、5 步。

3. 提交到模型调用层

Session 把 ConversationRequest 交给模型调用层,由后者发 HTTP、处理流式/非流式响应,并回传统一的流式事件与最终结果(或错误)。

4. 流式过程

流式事件顺序(建立流 → delta → 完成/失败)以及正文与 reasoning 分 channel,与常见流式 chat 一致,不展开。

需要单独说明的是 Responses 路径上的 context 长度:若服务端在一次 HTTP 响应内执行托管 tool 循环,usage 里的累计 token 可能大于“当前上下文窗口占用”。流中若带有 context_details,客户端用它计算当前上下文长度,写入供 compact 阈值与 context 显示使用的 total 类字段;计费相关累计字段仍可按服务端累计值处理。

5. 写回对话状态

调用成功后,Session 将 ConversationResponse 写入 ChatState。

ConversationResponse 的 items 为有序列表,形态接近 Responses API 的 output:

  • 零条或多条 Reasoning
  • 零条或多条服务端已执行 tool 的记录(客户端不再执行,保留用于后续回放)
  • 末尾通常为一条 Assistant(正文与客户端 function call)

写回之后,L3 按上一章规则分支:有客户端 tool call 则执行;无则尝试结束本段 L3。

API backend 与中间表示


模型调用层支持三种 backend:

Backend 路径 使用场景
Chat Completions /v1/chat/completions 兼容;枚举默认值
Responses /v1/responses 默认模型 grok-4.5 的配置
Messages Anthropic /v1/messages Anthropic 兼容后端

业务逻辑使用中间类型 ConversationRequest、ConversationResponse、ConversationItem。发送时按当前模型的 api_backend 映射为实际发往模型服务的 JSON。切换模型即切换配置(含 backend),不改变 L1/L2/L3 结构。

默认路径

内置模型表中,grok-4.5 的 api_backendresponses,context window 为 500000 量级,并配置 reasoning effort 选项。默认安装下的主路径为 Responses API,通常使用流式请求。

Backend 能力差异

能力 Chat Completions Responses Messages
原生 JSON Schema 与 tools 同时使用 支持 支持 不支持(Messages 上 schema 与 tools 冲突时改用 StructuredOutput 伪 tool)
将 prompt_cache_key 写入请求
回放历史中的 reasoning / thinking 时的额外限制 无(按映射能力) 无(按映射能力) 在缺少 thinking 配置时可能需剥离 thinking 块
服务端托管 tool 的记录与回放 弱或无 支持 形态不同

是否使用原生 schema,由 backend 是否支持“schema 与 tools 同时使用”决定。

中间类型

业务层用 ConversationItem / ConversationRequest / ConversationResponse,由模型调用层映射为各 backend 的 JSON。system、user、assistant、tool result 及 request/response 常规字段不展开。

常规角色以外的 item:

类型 行为
Reasoning 独立条目,顺序与响应一致(可与 tool 记录交错)。回放保持该前缀顺序,供 prefix / KV cache。
服务端已执行的 tool 记录 响应内由推理服务执行;客户端只落盘并回放,不本地执行。
客户端 function call 模型返回 call → 本地执行 → 写 tool result;L3 工具循环只处理此类。

模型调用层:请求、流、重试、取消


同一次提交里的多次 HTTP

Session / L3 向模型调用层提交一份 ConversationRequest,并等待一次交付结果(ConversationResponse 或错误)。

在这份请求尚未向 L3 交付之前,模型调用层内部可以多次发起 HTTP。对 L3 而言仍是同一次模型调用;ChatState 不会因这些内部 attempt 多写几轮 assistant。

内部再发 HTTP 只有两类原因,计数预算分开

类型 原因 默认次数量级 退避
网络 / 瞬时失败重试 请求未正常完成,或结果瞬时无效 约 15(可配置) 指数退避,有上限
Doom-loop 重发 HTTP 往往已成功,但生成内容被判定为空转 默认 2(可配置,0–5) 接近 0

网络 / 瞬时失败重试

同一份请求再发一次 HTTP。触发条件如下。

会重试:

  • 5xx、连接失败、流中断
  • 空响应(含仅有 reasoning、无可见正文且无 tool call)
  • 429(单独使用更低次数上限)

会重试,且可能先改请求再发:

  • 413 或请求体过大:去掉 inline 图片后再试;连接被 reset 时也可能先去图再试
  • 首次传输失败:可强制改用 HTTP/1.1 并重建客户端后再试

不重试(错误直接返回上层):

  • 多数 4xx(400、401、403、404 等)
  • 空闲超时(默认约 300 秒无新 chunk)
  • 响应反序列化失败
  • max tokens 截断(按既有策略结束,不当作可重试传输失败)
  • 配置或凭证错误

服务端可返回“是否应重试”类 header;明确指示不重试时,客户端停止网络 / 瞬时失败重试。

Doom-loop 重发

Doom-loop:模型在同一次生成过程中进入无意义循环(反复产出同一段尾巴,或极低信息量输出)。由推理服务端在生成过程中检测,经 Responses 流报告客户端。名称来自实现与协议中的标签。

作用对象是单次 HTTP 响应内的生成文本(是否在通道内重复/退化),与 L3 动作停滞(连续相同 tool 名 + 参数)无关。

如何开启

  • 主要路径:/v1/responses 流式请求。
  • 客户端在请求上带 opt-in header:x-grok-doom-loop-check。未配置恢复策略时不带该 header,服务端不按此协议报告,客户端也不做 doom-loop 重发。
  • 配置来源:环境变量 / 本地配置 / 远端设置等解析为 DoomLoopRecoveryPolicy;解析结果为“关闭”时整条能力关闭。

服务端如何报告

触发标签字符串放在:

  1. 流中途的非标准 SSE 事件:response.doom_loop_checktriggers 为累计集合)
  2. 结束时响应对象上的字段:doom_loop_check.triggers

常见标签形态:

形态 含义
tail_repetition:{n}@{channel} 在通道 channel 上检测到尾部重复;n 为检测阈值,数值越小表示重复越紧、证据越强
low_logprob@{channel} 该通道生成熵很低(退化输出)
其它 客户端记为未知类型,保留原文;不因解析失败打断整条流

channel 例如 thinking(思考)、response(可见输出)。示例:tail_repetition:4@thinking

客户端何时真的丢弃并重发

并非所有触发都会重发。默认策略(可配置阈值)下:

信号 客户端行为
thinking 通道上的 tail_repetition,且 n ≤ 配置上限(默认 8) 置信:整段本次响应丢弃,同一份 ConversationRequest 再发 HTTP(doom-loop 重发计数 +1)
仅在 response 通道上的重复、low_logprob、更松的阈值、未知标签 仅记录 / 警告,不因该项做 doom-loop 重发

默认仅对 thinking 通道上满足阈值的 tail_repetition 自动重发;response 通道及其它标签默认不触发 doom-loop 重发。

doom-loop 重发次数用尽后:不再因 doom-loop 重发;接受当前响应,或按错误路径返回(与信号出现在流中途或结束时有关)。

与网络重试、与 L3 的边界

机制 条件 对 L3 的可见结果
网络 / 瞬时失败重试 模型调用层 传输失败或瞬时无效响应 成功则仍交付一次 Completed;耗尽则交付错误
Doom-loop 重发 模型调用层 服务端报告且达到置信条件 中间 attempt 的生成不写进 ChatState;最终交付一次结果或错误
动作停滞 8 / 16 L3 连续相同 tool 名 + 参数 注入 reminder 或结束 L3
max_turns L3 工具轮次达到上限 结束 L3

取消

每个模型调用请求关联取消 token。用户取消、Turn 被抢占、等待完成的 future 被 drop 时,模型调用任务停止当前 attempt。

结果如何交回

模型调用层对同一请求可同时使用两条交付路径:

  1. 流式事件
    经共享 channel 按 request_id 推送,TUI / ACP 用这条路径做实时显示。

  2. 整次结果
    提交时可附带一次性完成通知。在内部网络重试与 doom-loop 重发全部结束后,调用方收到完整的 ConversationResponse 或错误,不必自己从事件流里拼装。

主 turn 路径同时使用两条:TUI / ACP 消费流式事件;L3 在收到整次结果后写入 ChatState。
compact、会话摘要、/btw 等顺序逻辑使用“提交并等待整次结果”的接口;流式事件仍会推送到共享 channel。

Session 在调用失败后的处理


下列失败由 Session 处理。模型调用层返回错误,不修改 ChatState 历史策略。

上下文超限

错误表明超过 context window 时,Session 可执行 compact(改写内存对话历史),再让 L3 重新生成请求并调用模型。触发、与请求副本 tool 裁剪 / 内存 hard-clear / 请求体去图的差别、two-pass、compaction mode 见文末「compact 与会话减负」。

Compact

调用前 auto-compact、失败后超窗恢复 compact、用户 /compact,共用摘要替换内核。细节见文末「compact 与会话减负」。

401

模型调用层将 401 标为鉴权错误且不做网络 / 瞬时失败重试;是否刷新凭证、是否允许把当前 token 发往当前 base URL,以及 tool 侧 401 是否刷新后重试,由 Session / 鉴权模块决定。

encrypted_content 无法解密

Responses 路径会把加密 reasoning 写入历史并在后续请求回传;换模型族或服务端无法解密时返回 400,Session 视为会话与当前模型不兼容并提示新建 session,不重试。

调用前提前执行的步骤

提交模型调用前,Turn 还可能刷新将过期的凭证,以及按阈值触发 auto-compact(含可选的 two-pass Pass 1)。

结构化输出


调用方可为单次 Prompt 指定 JSON Schema(例如 ACP meta、headless 参数)。普通 TUI 对话通常不指定。

处理方式:

存在合法 schema
  ├─ backend 支持 schema 与 tools 同时使用
  │    → 使用原生 structured output 字段
  └─ 否则
       → 向 tools 增加 StructuredOutput 伪 tool
         要求模型在结束时调用该 tool 提交结果
         校验失败时可有限次重试

json_schema 属于单次请求。原生路径下,模型仍可先调用普通 tool,再给出符合 schema 的最终输出(具体语义依赖 backend)。伪 tool 路径将结构化提交放进 L3 的 tool 循环。

实际发往模型服务的 JSON 形态(概要)


Responses(grok-4.5 默认)

这里的“扁平”指:请求和响应的内容是一维有序列表,每一项是带类型的 item;reasoning、function_call、message 等并列排在同一列表里。不是 message 套 content / tool_calls 再套一层的树形结构。

Chat Completions 常见形态(嵌套):

messages: [
  { role: "assistant", content: "...", tool_calls: [ {...}, {...} ] },
  { role: "tool", tool_call_id: "...", content: "..." },
]

一层是 message;tool call 挂在 assistant 下面;tool 结果再另成 role=tool 的 message。

Responses(字段名以实际 API 为准):

input: [
  { type: "message", role: "user", content: [...] },
  { type: "function_call", ... },
  { type: "function_call_output", ... },
  { type: "reasoning", ... },
  ...
]

顶层就是 item 数组。output 同理:reasoning、message、function_call、服务端 tool 记录等,也是同一条列表里的多个 item。

“扁平”不表示没有类型、全是纯字符串,也不表示没有多轮历史。只表示历史和输出在协议上展成 item 序列。

其余要点:

  • 可带原生 JSON Schema 相关字段
  • 可带 prompt_cache_key
  • 可声明托管 tool
  • 流式事件中可能包含非标准 doom-loop 事件;客户端在 typed 反序列化前拦截

Chat Completions

  • messages + tools(结构如上节嵌套示例)
  • 流式 delta 合并为 assistant
  • 映射时将内部 reasoning、服务端 tool 记录转换为该 API 可接受的形式

Messages

  • system 与 messages 分离;含 thinking 与 signature 等
  • 流开始时可提供 message id 与输入 token 分桶
  • 原生 schema 与 tools 不能按 Chat/Responses 方式并用时,结构化输出走伪 tool

写回内部历史时保留顺序与可回放字段。映射丢失的是目标 API 无法表达的信息。

模型调用小结

  1. 对话状态从历史生成请求。模型调用层发 HTTP、收 stream 流、按规则重试。Session 把结果写回历史,并在失败或需要压缩时决定是否再提交。
  2. 默认模型 grok-4.5 使用 Responses API。Chat Completions 与 Messages 共用同一套中间类型,只是映射到不同协议。
  3. 内部历史是有序条目列表。reasoning 与服务端已执行的 tool 记录单独成条,用于回放,也用于和服务端缓存对齐。
  4. 网络失败、空响应、限流、请求体过大,由模型调用层分类处理。生成被判定为空转时,走 doom-loop,单独计数,可能整段重发。鉴权失败、上下文超限后的 compact、换模型后 encrypted_content 无法解密,由 Session 处理。
  5. 一次模型调用结束,只表示这一次 I/O 做完。本 turn 是否继续,由上一章的 L3 与 L2 规则决定。

作者的邮箱:tokamak9000@163.com。如有问题,欢迎讨论

posted @ 2026-08-05 22:56  tokamak9000  阅读(1)  评论(0)    收藏  举报