AI 实验室的节奏越来越快。Anthropic 发了 Fable 5,OpenAI 随后推出 GPT-5.6 系列(包括目前最强的 GPT-5.6 Sol),Kimi 发布了 K3,Opus 5 也在几天前亮相。模型能力在持续跃进。
但能力只是等式的一半。另一半是成本——模型完成一项任务到底要花多少钱。更低的单次任务成本,对用户意味着更便宜,对服务商意味着更可持续。以 GPT-5.6 Sol 为例,它在 Artificial Analysis 的 Coding Agent Index 上得分超过 Fable 5,成本却不到后者的一半。
为了理解前沿实验室到底用了哪些手段让 AI 应用更高效,Bytebytego 与 OpenAI 的工程师团队(Joe、Ahmed、Steve、Matthew、Philippe)做了一次深度交流。他们详细拆解了 Codex 和 ChatGPT Work 背后每一层的优化策略。
这篇文章把交流内容整理成三个层次:接入层(Harness)、API 层、推理层(Inference),以及每一层具体在做什么优化、为什么有效。
Agentic AI 应用的三层结构
当你让 Codex 修一个 bug,请求并不是直接扔给大模型。它要经过好几层。
一个像 Codex 这样的 AI 应用,不只是 LLM。它是一个系统,有很多组件。用户的查询在 LLM 看到哪怕一个 token 之前,已经走过了多个层次。
LLM 本质上是一个训练来预测下一个 token 的神经网络。输入 token 序列,输出 token 序列。它不能执行 shell 命令、不能编辑文件、也不能在调用之间记住任何东西。而 “修这个 bug 然后跑测试” 这类 agent 任务,大部分内容其实是动作。必须有某个东西把模型预测出的 token 转成真正要执行的命令,把结果喂回去,然后继续循环,直到任务完成。
这个角色由 Harness 接入层承担。它接收用户任务,决定在上下文中放哪些指令、哪些工具定义、保留多少历史记录,维护整个对话状态;当模型返回工具调用时,harness 在审批策略下执行它,把结果追加回对话,再发回给 LLM。
但即便是 harness 发出的请求,也不是直接抵达 LLM。在生产级应用里,harness 和推理端点之间还有一个 API 层。这层的存在是因为真实产品需要处理 harness 和 LLM 都不管的事:认证调用方、限流,以及最关键的——把文本转成 LLM 需要的 token ID,再把生成的 token 转回文本。
等 API 层准备好上下文并完成 tokenization,才轮到 推理层。这是一个由 GPU 集群支撑的远程端点,承载着模型本身。它的工作是把模型计算跑在准备好的 token 上,尽可能高效地产出回复(可能是工具调用,也可能是最终答案),然后交给 API 层。
一个请求穿过这三层的过程,大致是这样的:
- Harness 把指令、工具定义和你的任务打包成请求,发给 API
- API 缓存请求到内存,解析 JSON、验证格式和语义(模型支不支持这些特性),检查调用方身份、限流策略,执行预检
- API 把对话渲染成模型输入格式并 tokenize
- API 把 token 分派给推理层,同时启动安全检测(分类器检查网络攻击、生物武器等内容),这些检测抢在第一个生成 token 回来之前跑完
- 推理层处理 prompt、开始生成。第一轮输出通常不是最终答案,而是一个工具调用,比如 “搜索代码里 checkout timeout 相关逻辑”
- 生成的 token 流回 API,API 把 token 转成文本、封装为 API 事件,流式发给 harness
- Harness 识别工具调用,在沙箱里执行搜索,把结果追加到对话,再把更新后的对话发回 API
这是一次迭代。实际任务里这个循环要重复很多次(重任务可能超过 100 轮),直到模型产出最终摘要、harness 把控制权还给用户。
而每一轮迭代都重做了大量相同的活:历史记录被重新发送、文本被重新 tokenize、prompt 被重新处理。去掉这些重复工作,就是优化的主战场。
下面按三个层次分别展开 OpenAI 具体做了什么。
Harness 接入层:别再重复发相同的东西
Harness 是离用户最近的编排层。它有两个核心职责:第一,它是对话状态的唯一真相来源,持有每条指令、消息、工具调用、工具结果的权威记录;第二,它跑 agent 循环——决定给 LLM 的上下文里塞什么,发出请求,接收流式回复,解析、监视工具调用,执行后追加结果,再发回去。
每轮迭代都有开销。举例来说,如果每次模型调用多花 1 秒,一个长任务就要多等半分钟。
从 harness 的视角看,延迟来自四个地方:网络、prompt 处理、上下文内容、循环往返。OpenAI 用了四项技术来解决。
1. 持久 WebSocket 与增量请求
Harness 跑在用户机器上,模型跑在 OpenAI 数据中心里,每次模型调用都是一次网络交换。
传统做法是 HTTPS + SSE(Server-Sent Events)。聊天时代这很合理:一条用户消息触发一次模型调用、一个流式回复。但 agent 不是单次往返。一个 Codex 回合里可以有几十次模型调用。每次 HTTPS 调用都有两个独立成本:
- 连接建造成本:新 HTTPS 连接 = TCP 握手 + TLS 握手,几个网络往返后才有一字节有效载荷传过去。一次用户消息里付一次还能接受,一个对话回合里付几十次就贵了。
- 载荷重复成本:HTTP 无状态,所以每次请求都要带上全部内容——指令、工具定义、完整对话历史。到第 20 次工具调用时,harness 在重复发送原始 prompt、前 19 次工具调用和 19 次结果,只为了在末尾加一个新结果。载荷随迭代次数一直膨胀。
修复连接成本:打开一个连接、保持它活着。这就是 WebSocket。一次初始握手后,双方随时可以互发消息,没有每次消息的连接建立开销。Codex 打开一个 WebSocket 到 API,在一个回合的所有模型调用期间保持它。
修复载荷重复:不再重复发送服务器已经知道的内容。Harness 保留上一次的请求和完整回复。如果只有对话输入变了,就只发送新条目,同时带上对上一次回复的引用。工具调用之后,socket 上的下一条消息可以小到:
{ "new_items": [ { "tool_result": "..." } ], "previous_response_id": "resp_abc123" }
没有指令、没有工具 schema、没有历史记录。服务器从自己保留的状态重建完整上下文,模型看到的还是一样的信息。唯一变化的只是网络上传输的数据量。
2. 稳定 prompt 前缀
LLM 服务商会用 prompt 缓存来避免重复计算。prompt 到达时,如果开头部分和上一次请求完全一致(token 级别精确匹配),模型就复用缓存好的内部状态,只计算剩余部分。但只要前面某个 token 变了,后面全得重算。
这对 harness 意味着:每次调用构建的 prompt,开头必须和上一次逐字节相同。
听起来很自然,但 harness 每次都从运行时的内存状态重建请求,构建过程里的任何微小差异都可能无声地破坏匹配。OpenAI 分享了一个实例:Codex 把 MCP 工具定义存在 hash map 里,而 hash map 不保证顺序,所以同一组工具每次序列化出来的顺序可能不一样。工具还是那些工具,只是顺序不同。Codex 仍然能完成任务——只是更贵了。
优化手段是:把历史视为 append-only,把易变的运行时状态(比如审批设置)从 prompt 里拿出来。比如用户在对话中间改了审批策略,harness 不去编辑 prompt 里的工具定义,而是自己在下一次工具执行时应用新策略。这样 prompt 保持不变,缓存始终有效。
3. 延迟工具发现
稳定前缀让重复上下文更便宜,但没让 prompt 本身变小。当 agent 有大量工具时,问题就来了。
一个 Codex 会话,如果把连接的集成和 MCP 服务器算上,可以暴露上百个工具。每个工具 schema 都是一个 JSON 对象,包含名称、描述、参数定义。全部放进 prompt 之后,LLM 要对所有工具做注意力计算,而当前任务可能只用其中两三个。
OpenAI 的办法叫延迟发现(deferred discovery):prompt 里只携带核心工具(shell、文件编辑),外加一个 tool_search 工具。其他上百个集成定义完全不进 prompt。当模型需要某个能力时,调用 search 工具,带上关键词比如 “list deployments”。Harness 在工具目录里搜(用的是 BM25 词法排序算法),匹配到的定义才加载进上下文。
此外还有两个保持上下文精简的辅助手段:schema 压缩——把过大的工具 schema 按 token 预算裁剪,去掉描述、折叠嵌套结构,但保留参数名;对话压缩——把长历史浓缩成简短摘要。
4. Code Mode
常规模式下,模型每次产出一个工具调用,等结果回来,再推理,再产出下一个。很多场景里这些工具调用之间根本不需要推理,模型只是在逐个收集信息。而每次工具调用都要一次完整模型往返,每个工具结果还不断堆进上下文,浪费空间。
Code Mode 的做法是:让模型写一个小程序来批量执行工具调用,而不是一个一个发。Harness 在嵌入式 JavaScript 运行时里执行这段程序,每个工具都作为函数可用。脚本可以并行发出独立调用,在纯代码里过滤和合并结果,只把最终紧凑的答案返回。中间数据留在运行时里,不进上下文。
API 层:能省掉的 CPU 时间都省掉
API 层是 harness 真正对话的服务,跑在普通 CPU 上,夹在 harness 和推理层之间。Harness 发来的是结构化对话 JSON,而模型消费和生产的是 token ID。
一个请求经过 API 层时要做这些事情:缓存请求体到内存 → 解析并验证 JSON → 语义验证(模型是否支持请求的特性)→ 预检(认证、授权、限流、图片检查)→ 渲染并 tokenize 对话 → 同时发起推理请求和安全检测 → 当 token 从推理层流回来,逐 token 转文本、封装成 API 事件、流式发给客户端。
API 层控制不了 GPU 的速度。它能做的只是在推理前后增加尽可能少的延迟。
1. 只 tokenize 增量部分
模型不看文本,推理开始前 prompt 必须转成 token ID。Tokenizer 的转换是 O(n) 的,线性遍历每个 token 做替换。
在无状态 HTTP 模式下,每次调用都要重新 tokenize 整个对话。到第 20 次工具调用时,API 在重读几千个 token 只为了提取一个新结果。GPU 只需要那个新工具结果,但 CPU 从第一页开始重读全书。
有了 WebSocket 之后,API 在服务端内存里保留已 tokenize 的对话。首次请求 tokenize 完整 prompt,后续请求只发送新条目。API 只 tokenize 那一段,追加到已有序列后面。每次调用的 tokenization 成本不再取决于对话长度,而接近 O(1),取决于增量输入的长度。
2. 安全检测与推理并行跑
Agent 系统必须在每次请求时做安全检测。OpenAI 把安全检测放在 API 层实现。图片走自己的分类器,prompt 文本走检测危险内容(网络攻击、生物武器指令等)的分类模型。
简单做法是先跑检测、通过后再启动推理。问题是这个延迟会加到所有请求的首 token 时间(TTFT)上,而绝大多数请求完全无害。
优化方案是并行跑。模型处理 prompt 到产出第一个 token 需要一些时间,安全检测就利用这个窗口完成。如果检测不通过,API 根据模型类型采取不同策略:有些模型直接流式输出、检测失败时切断流;更敏感的模型则暂扣输出,等检测通过再放行。不管哪种,安全检测的时间都藏进了本来就要等的窗口里。
3. 把流量导向更新的 CPU
大规模运行服务时,集群里会混着不同年份买的机器,CPU 代际不同。调度软件通常把同类型机器视为等价——但它们不是。
OpenAI 查询了 Kubernetes 里每个部署背后的实际处理器型号,发现同一机器标签下混着老 Broadwell 芯片和新 Ice Lake 芯片。老芯片在同样流量下 TTFT 差约 20%,CPU 消耗却多出约一倍。
做法很简单:把更多流量导向更新处理器的机器,把 CPU 代际纳入容量规划的显式因子。有时候最好的软件优化就是更好的硬件。
推理层:每一分 GPU 算力都用在刀刃上
推理层是模型真正运行的地方。GPU 集群 + 推理引擎。模型的算力核心是海量矩阵运算,在加速器的数千个核心上并行。推理层负责跨集群调度和批处理请求、维护生成依赖的每会话状态、执行模型前向传播、把每个生成 token 交回 API 层。
token 需求的增长总是快于硬件扩充,所以在这一层,慢和浪费是同一回事。浪费藏在这四个地方:请求路由到错误的机器、内存持有错误的状态、并行硬件在顺序生成中空转、两个不匹配的阶段共享同一批机器。
1. 缓存感知路由
OpenAI 在多台 GPU 机器上部署模型。每个请求必须发到其中一台。如果路由不均,有些机器空转、有些排队——空闲 GPU 是整个技术栈里最贵的浪费。
还有第二个问题:每台机器为它最近服务过的对话保存计算状态(缓存)。如果同一个对话的下一次请求落在另一台机器上,缓存作废,新机器必须从头计算,尽管之前的结果存在于另一台机器上。
路由器的两个目标:
- 均匀分散负载:发给最不忙的机器
- 命中缓存:发回已经认识这个对话的机器
OpenAI 的路由对每个请求同时权衡这两个目标(加上地理位置、可用容量等基础因素)。光是负载均衡的改进就大幅降低了模型服务成本。
2. KV Cache 管理
Transformer 生成 token 时需要关注所有之前的 token。为了避免每次生成新 token 都重算注意力状态,推理系统把状态保存在加速器内存里,叫 KV Cache。
这个缓存随对话长度线性增长,再乘以整个集群同时服务的对话数。对于长上下文,它的大小可以和模型权重本身匹敌。如果软件驱逐了错误的状态,推理层就要为该对话的回归付出完整的重处理成本。
优化手段是基于真实使用数据管理缓存:分析生产轨迹来学习哪些缓存状态可能再次被需要,用真实数据测试驱逐策略,优化缓存的存储和内存层级间的移动方式。热门对话状态留在 GPU 上,冷门状态移走或驱逐。
3. 推测解码
生成是顺序的——每个 token 依赖前一个 token,所以一个能并行处理海量 prompt 的模型,回答时却只能一个一个 token 地出。
对大模型来说,这一过程又贵又慢,即使下一个 token 很容易猜。比如输入 “the capital of France is”,下一个 token 几乎肯定是 “Paris”,但模型还是得处理整个对话、执行大型矩阵乘法才能预测它。
推测解码的做法:用一个小型草稿模型先提出接下来的几个 token,大模型在单次并行前向里一次性验证它们。大多数情况下草稿提案是正确的,会被大模型接受,省下了大模型逐个生成的时间。如果某个提案错了,大模型丢弃它、产生自己的 token,所以输出质量不变。评估草稿模型的主要指标是平均接受长度——每次前向传播中主模型接受多少个草稿 token。
4. 分离 Prefill 和 Decode
推理有两个阶段:
- Prefill:在一次大规模并行前向中处理整个输入 prompt,构建模型需要的内部状态(KV Cache)
- Decode:一个 token 一个 token 地生成回复
这两种负载完全不同。Prefill 是算力密集型,Decode 是内存密集型——大部分时间花在把权重和缓存状态从内存中流过。两者跑在同一批机器上,意味着谁都没跑在最适合自己的硬件上。
优化方案是分离它们。集群里一部分机器专门做 prefill,另一部分做 decode,各自针对自己的瓶颈配置。一个请求完成 prefill 后,计算好的 KV cache 被传到 decode 机器,由后者逐 token 生成回复。
从这些优化里能学到什么
跳出具体技术,所有这些优化有一个共同主线:避免为同一件事付两次钱。
Harness 只发送新增内容、保持前缀稳定让缓存存活。API 只 tokenize 增量、把躲不开的工作藏进本来就要等的窗口里。推理层把对话路由回已有缓存状态的机器,而不是重算。
这些层次之间也是互相成全的:harness 保护好缓存,API 提高缓存命中率,节省最终体现在 GPU 上,也就是更低的用户成本。
OpenAI 工程师还分享了三条更宏观的经验:
保持设计简单。 优先选简单的方案,比如工具发现用词法搜索而不用 embedding,对话压缩只保留一条路径而非菜单。LLM 本身已经足够智能,在它前面加太多机制往往只增加复杂度、不增加价值。先做最简可行方案,再扩展。
用 agent 优化 agent 自己的技术栈。 Codex 参与了服务它自己的 API 的大量迁移工作,把几年的重写工程压缩到几个工程师的几个月。推理团队用它分析 trace、原型化 kernel。当 agent 自己写代码时,你不再需要人类好写的语言,直接选运行最高效的那个。效率工作正在变成一个自我加速的循环。
端到端优化。 推理团队告诉我们,每次他们挑一个最喜欢的技术集中投入,后来都后悔了。盯着技术栈的一部分会让其他部分投资不足。单个优化都不是 game changer,真正的大收益来自把许多小优化串起来。另外,要用和线上真实流量一致的流量模式来测试——一个离线看起来很好的改动,在真实负载下可能反而有害。