从0打造编码Agent 导读 3 课 + 2 条岔路 + 1 节实验课 ≈ 3 小时
A8 模型接入:换模型、控成本怎么办? 章导读

模型接入:换模型、控成本怎么办?

harness 和模型之间隔着一层线格式:请求怎么写、回复怎么一帧帧流回来、账单按哪几段 token 算。这一章从「写死一家的格式」出发,被真数字逼着走:同一段对话换一家,9 个语义位置全换了路径;并行的两个工具调用共用一个缓冲,拼出来不是合法 JSON;按全价估账,比按缓存四段算高出 2.6 倍,而开思考时输出才是大头。线缆数据来自本课项目在本机 mlx_lm.server 上的真实流式录制;两家云端的事件流按官方文档构造,标明不是录制;价格取 2026-09-26 的官方价格页快照。
本章一问:换模型、控成本怎么办?

主线定位

🧭

上游(A1 循环、A5 上下文、A6 记忆):模型 API 不记得上一轮,harness 每一轮都把全部历史整份重发;上下文会涨;system 前缀要保持不变,缓存才一路命中。
本章(A8 模型接入):这些请求真正落到网络上是什么样子——三家的线格式各不相同,回复一帧一帧流回来,账单按命中、写入、未缓存、输出四段算。
下游(A9 评测):能跑、能换模型、能算清账了,它到底修得好不好?

累计数据流里,本章点亮「模型接入与计费」:循环里每一次「请求模型」都从这里出去、从这里回来。

累计数据流 · 一次任务在 harness 里经过哪些环节 已学本课新增还没学
每一轮循环都经过

本章问题链

每一课只回答一个问题;上一课的失败,逼出下一课的问题。

课这一课的问题朴素方案在哪个真数字上失败一句话
第 1 课同一段带工具调用的对话,发给不同厂商长得一样吗?写死 Chat 格式只改 base URL:同一段对话 9 个语义位置,换成 Anthropic Messages 有 9 个换了路径或类型,参数从字符串变成对象换 provider 换的是整套线格式:要么加一层抽象替每家补差异,要么只接一家、用满它
岔路怎么让模型的输出一定是合法 JSON?靠服务端解析:本课项目 26 次运行里,整条调用被服务端丢掉 7 次,T5 又 3 次;CoreCoder 则把坏 JSON 静默变成空参数一个机制管模型「说什么」,一个管它「怎么调用」;约束采样之外的例外,harness 仍要兜
第 2 课流式回来的工具参数,什么时候才能解析?两个并行调用共用一个缓冲:{"path": "dateparse.py"}{"path": "test_dateparse.py"} 不是合法 JSON(真实录制)流式工具参数按调用分别累积,收到结束信号才解析
岔路出错、限流、变慢,怎么定位?只看总耗时:T5 的 9 轮里,第 8、9 轮首帧要等 1,029 / 978 ms,是其他只调工具的轮的两倍多,原因藏在帧里先按 span 拆开时间再找慢在哪;重试要分层,限流不在传输层硬重试
第 3 课每轮都重发全部历史,钱花在哪?缓存能省多少?CoreCoder 按全价估账:T5 在 Claude Sonnet 5 价目下是按四段算的 2.64 倍缓存命中率就是输入侧的钱:前缀一变就归零;开思考时,大头在输出
实验课线缆与账单:真实帧回放、同一次运行换价目重算—线格式、帧、账单三样都要能从真实记录里复算出来

演进地图

本章的「现代做法」是被一连串真实数字逼出来的。每一课都会用这张图标出「你在这里」。

模型接入层的演进:每一步都被上一步的真数字逼出来
  1. CoreCoder(OpenAI 兼容 + litellm 兜底)、本课项目 miniagent(标准库 urllib)
    解决了最短的路:CoreCoder 的 llm.py 净 259 行,换 provider 只改 base URL 与 key。
    卡在同一段带工具调用的对话,换成 Anthropic Messages,9 个语义位置里 9 个换了路径或类型(参数从字符串变成对象,5 条消息变 3 条);Codex 干脆不认 Chat:配置 wire_api = "chat" 直接报错。
  2. Pi 自建 pi-ai · OpenCode 用 Vercel AI SDK · Codex 只留 Responses
    解决了Pi 把 10 种协议与 40 个 provider 做成正交的两维;OpenCode 挂了 19 个 @ai-sdk/* 依赖;Codex 的 WireApi 只剩 1 个变体。
    卡在抽象层得替每一家补差异:OpenCode 逐 provider 改写消息的 transform.ts 有 1,909 行,还给 8 个 @ai-sdk 包打了补丁;而且抽象挡不住流式与缓存的细节。
  3. Anthropic 流式文档 · OpenAI function-calling 流式指南 · Codex 只收 output_item.done
    解决了开思考的一轮,第一帧 125.9 ms 就到;整包等回复的话,正文要等到 6,631 ms、工具调用 6,798 ms。
    卡在流式只让回复来得早,不让它便宜:T5 的 9 轮每轮整份重发,输入累计 15,921 token,是最后一轮 2,389 的 6.7 倍。
  4. Anthropic / OpenAI prompt caching · Codex prompt_cache_key · OpenCode applyCaching
    解决了按 usage 四段计价:T5 在 Claude Sonnet 5 价目下 $0.0140;CoreCoder 按全价估 $0.0371,是它的 2.64 倍。
    卡在缓存只省输入:开思考的 pd-v2-unlimited-s3 账单里输出占 94%;前缀里一处变、后面全失效——T10 刷新组第 7 轮 system 重建,命中从 2,072 掉到 294。(→ A9:能跑、能算账了,怎么知道它好不好?)

数字口径:pin 源码计数(Codex 8cabf5a、OpenCode v1.18.32、Pi 31b513e3、CoreCoder v0.7.0)、项目录制 T5 / T10 与逐轮 usage(本机 Qwen3-8B,token 按它的 tokenizer 数)、官方价格页快照 2026-09-26;由 a08-model-access.json 构建时现算。

贯穿任务:修好 test_parse_date

全课的贯穿任务是项目 04-实践项目/tasks/parse_date:让失败的 test_parse_date 通过,测试文件不许改。这一章给它的 trace 加上线缆层:每一轮请求在网络上的样子、回复的每一帧什么时候到、这一轮按价目算多少钱。

本章用到的真实数据(项目线的 T5,本机 Apple M5 Max, 128 GB,Qwen/Qwen3-8B-MLX-4bit,mlx_lm.server on 127.0.0.1:8080 (OpenAI-compatible /v1/chat/completions)):

⚠

这一章没有云端 provider 的真实录制。规则禁止经手任何 API key,所以设计卡里「同一任务经 Anthropic Messages 与 OpenAI Responses 各跑一次」没有做。页面上这两家的请求与事件流,都是把本机那条真实回复按官方文档的格式转换或构造出来的,一律标「按文档构造,非真实录制」;项目的 Anthropic 拼装器也只对照过这种构造的事件,不是录制。本机服务的帧是真实录制,两者在组件里用实线 / 虚线按钮分开。

三层交互

层本章里的组件在哪一课
概念动画演进地图(每一步卡在哪个真数字上);流式拆帧器的「按真实节奏播放」,帧落在时间轴上每一课、第 2 课
算法模拟器(真算法、小数据)三种线格式的现场转换(JS 与 python 两份独立实现逐字节一致);三种拼装策略跑同一条流;Pi 的 calculateCost 与 CoreCoder 的 estimated_cost 移植进浏览器,插入「换模型 / 空闲 6 分钟 / system 放时间戳」按官方规则重算;五种实现对同一段参数文字的处理第 1、2、3 课,岔路 B2
真实数据T5 的全部真实帧(并行探针、706 帧思考、agent 9 轮)逐帧回放并与录制的完整消息比对;六次真实运行的逐轮账单;由 trace 现算的 span 瀑布第 2、3 课,岔路 B1,实验课

每个移植都配了自检,组件底部显示结果:拼装器与录制消息逐字节比对;账单在 276 个用例上与 bun 跑的 Pi 原文、python 跑的项目 cost.py 和 CoreCoder 原文逐项比对(误差都小于 1e-9)。生成脚本:src/data/precomputed/gen_a08_model_access.py(+ .ts)与 gen_a08_prices.py;组件纯函数的 node 单测:test_a08_pure.mjs。

项目推进:里程碑 M8 · provider 抽象 + 流式 + 成本账

对应 04-实践项目/MILESTONES.md 的「A8 · 模型接入与工程化」。已有:标准库写的 OpenAI 兼容客户端(miniagent/llm.py)、两套拼装器(stream.py)、按 usage 算成本(cost.py)、429 不在传输层重试。代码 AI 来写,决策你来做。

🧭
你要拍板的决策
  1. 直连 SDK、第三方抽象(litellm / Vercel AI SDK),还是自建最薄一层?第 1 课的对照:CoreCoder 直连 + litellm 兜底,Pi 自建 10 种协议 × 40 个 provider,OpenCode 用 AI SDK 但要维护 1,909 行的逐家改写,Codex 只留一种。本项目现在:标准库自建,两家线格式各一个拼装器。写进 DECISIONS.md,理由要引用第 1 课的三栏对照。
  2. 事件处理:穷举还是兜底?Anthropic 文档要求「优雅地处理未知事件」;Codex 把认识但不用的事件显式列成一张忽略表,真不认识的记一条 debug 日志(第 2 课)。本项目:已知类型逐一处理,ping 忽略,error 抛异常。
  3. 开不开缓存,断点放哪?第 3 课的账单:前缀够不到最小可缓存长度时根本不缓存;system 里放会变的内容,每轮都得重写。本地服务自动做前缀缓存,M6 的「冻结」布局决定命中多少。
  4. 限流谁来重试?Codex 的传输层 retry_429: false,CoreCoder 在传输层按 2 的幂重试 429(岔路 B1)。本项目:429 立刻抛给上层。

验收标准(对应大纲 M8 ①-⑥;括号里是 2026-09-26 在项目里核对的现状):

坑与带走

本章拦截的误解
误解纠正在哪一课
OpenAI 兼容就是一样的同一个「兼容」接口也有方言:本地服务要 chat_template_kwargs,思考字段叫 reasoning 还是 reasoning_content 各家不同,OpenAI 新模型要把 max_tokens 改名第 1 课
Codex 只留 Responses = OpenAI 废弃了 Chat Completions那是 Codex 自己的选择(维护者公告 #7782);Chat Completions 仍在 OpenAI 平台上第 1 课
服务端给了 tool_calls,JSON 就一定合法本机服务遇到坏 JSON 直接丢掉整条调用;CoreCoder 把它静默变成空参数岔路 B2
流式只是 UI 效果它决定工具什么时候能开始执行:Codex 收到一个完整的调用项就把工具排进执行队列,流还没结束第 2 课
首 token 时间就是模型开始干活的时刻本机服务把工具调用攒完整才发:只调工具的一轮,首帧到达时整个调用已经生成完第 2 课、岔路 B1
大厂代码处处优雅Codex 的 Retry-After 是用正则从错误文字里刮出来的岔路 B1
缓存改变了模型的输出缓存复用的是同一段前缀的计算结果,请求一字不差;变的只是价格与速度第 3 课
编码 agent 的钱都花在重发的上下文上看你的运行:关思考时输入占大头,开思考时本课的运行里输出占 94%第 3 课
input_tokens 各家一个意思Anthropic 不含缓存,OpenAI 与 OTel 约定含缓存;读错一处就重复计费第 1、3 课
写代码时的坑
症状后果怎么办
写死一种线格式换 provider 要大改加抽象层,或明确「只用一家、用满它」
中途解析 partial JSON抛错或拿到半截参数按调用累积到结束信号(或像 Codex 只收完整项)
所有调用共用一个缓冲并行工具调用拼成非法 JSON每个 index 一个缓冲
_ => {} 吞掉未知事件协议新增事件被静默丢弃认识的逐一处理,认识但不用的列表忽略,不认识的至少记日志
坏 JSON 静默变成 {}工具以空参数执行,模型不知道自己错了把解析错误作为工具结果回给模型
前缀里有易变内容缓存每轮失效、每轮付写入价稳定前缀,易变内容后置或放进消息
账单按 prompt 全价估T5 上高估 2.64 倍按 usage 四段算,注意 input_tokens 的口径
429 立即重试加剧限流、烧配额交给上层按重置时间处理

带走这些句子(各课「一句话」):

  1. 换 provider 换的是整套线格式:要么加一层抽象替每家补差异,要么只接一家、用满它。
  2. 一个机制管模型「说什么」,一个管它「怎么调用」;约束采样之外的例外,harness 仍要兜。
  3. 流式工具参数按调用分别累积,收到结束信号才解析。
  4. 先按 span 拆开时间再找慢在哪;重试要分层,限流不在传输层硬重试。
  5. 缓存命中率就是输入侧的钱:前缀一变就归零;开思考时,大头在输出。

深入这一章

按你愿意花的时间选。每条都在 2026-09-26 实际打开核对过;站外链接需联网。视频缺口:缓存、计费、流式拆帧都没有找到专门讲机制的视频,用官方文档里的图与 Sam Rose 的交互长文代替。

🎮 想更直观 10–30 分钟

先看缓存怎么按前缀命中、一次运行的每一步怎么被看见
15 min英文需联网 官方一手 · 四轮前缀图
缓存的三层(system / 项目上下文 / 对话)各在何时变;哪些动作让缓存失效(换模型、改 effort、开 fast mode、增删 MCP、/compact……),哪些不失效。
对应第 3 课。账单组件里「换模型」「空闲」两个事件的规则就出自这里和 prompt-caching 文档。
20 min中文需联网 作者本人
KV Cache → Prompt Cache(严格前缀匹配)→ PagedAttention,并列出让缓存失效的反模式。
对应第 3 课,中文首选。先读它建立「前缀必须一字不差」的直觉,再回来看 T10 刷新组那次真实的断崖。
15 min英文需联网 官方文档
一次运行里每个 step(一次模型调用)的输入、输出、工具调用、token、耗时,按 run → step 分组。
对应第 1 课与岔路 B1。OpenCode 走的就是 AI SDK 路线;这是那条路线自带的观测工具。

🔬 想吃透原理 1–3 小时

唯一必看:Sam Rose 的交互长文,从 KV cache 一路讲到跨请求前缀缓存
30 min英文需联网 2025-12-16 · 交互文章
token → 注意力 → 逐 token 推理的重复计算 → 多个请求共享前缀时复用计算,六组可以动手的交互。
对应第 3 课。把「缓存复用的是前缀的计算」这件事从 KV cache 一路讲通;LLM 课 C3 第 7 课讲单请求内的 KV cache,这篇把它接到跨请求。
10 min英文需联网 2026-04-30 · 官方博客
把 plan mode 做成工具、工具定义延迟加载、不破坏缓存的压缩、用 system-reminder 代替改 system prompt;把缓存命中率当作可用性指标来监控。
对应第 3 课。「稳定前缀、易变内容后置」在一个真实产品里被推到了什么程度。
25 min中文需联网 季逸超 · 2025-07-18
KV-cache 命中率是生产 agent 的头号指标;前缀稳定、只追加;输入与输出约 100 比 1。
对应第 3 课。它说的 100 比 1 是 Manus 自己的负载;本课 T5 关思考的运行是 30.1 比 1,开思考的 pd-v2-unlimited-s3 是 1.9 比 1——比例取决于负载,这正是第 3 课要你自己算的。
21:52英文需联网 2026-03-14
Codex agent loop 的作者谈 harness 工程的整体取舍。
对应第 1 课「只留一种、用满它」一列。听写 Codex 的人怎么讲取舍;本课没有逐段核对访谈是否直接讲到线格式,弃用 Chat 的理由以维护者公告 #7782 为准。

📄 想读原文 按需

官方文档(2026-09-26 抓取)与 pin 源码
官方文档需联网
事件流程 message_start → content_block_* → message_delta → message_stop;input_json_delta 是不完整的 JSON 片段,收到 content_block_stop 再解析;要优雅处理未知事件。第 2 课构造事件流的依据。
官方文档需联网
Responses 的 output_item.added → function_call_arguments.delta → .done → output_item.done,按 output_index 累积。第 2 课 Responses 事件流的依据。
官方文档需联网
Anthropic:自动缓存、最多 4 个断点、20 块回看窗口、各模型最小可缓存长度;OpenAI(developers.openai.com/api/docs/guides/prompt-caching):GPT-5.6 起写入 1.25×、TTL 30m、最小前缀 1,024。第 3 课账单规则的依据。
官方价格页需联网
本章价格快照的两个来源(另一个是 developers.openai.com/api/docs/pricing);两家 Batch 都是 5 折,可与缓存叠加。价格会变,引用时带日期。
官方文档需联网
output_config.format 与 strict 工具两个机制,各自约束什么;约束采样的例外(拒答、max_tokens、enum 大小写)。岔路 B2。
规范需联网
推理 span、execute_tool span、usage 属性(input_tokens 含缓存);状态仍是 Development。岔路 B1。
源码需联网
WireApi 只剩 Responses 一个变体,配置 chat 直接报错,错误信息指向维护者公告 #7782。第 1 课。
🚫

我替你排除了什么:litellm 延迟「2s → 4.5s」一类的具体倍数(找不到可引用的测量)· 「OpenAI 缓存折扣从 50% 提到 90%」(历史变化未核实)· Braintrust「核心闭源」的竞品说法(未核实,且利益相关)· 需要登录才能看的 SaaS 追踪平台(Langfuse 云端控制台、OpenAI Traces dashboard;Langfuse 本身 MIT 可自托管,岔路 B1 提到但不列为资料)· AgentPrism 组件库(交互做得好,但本章的瀑布图已用真实 trace 实现,不再引入 React 依赖)· 任何「成本大头一定是上下文」的二手结论(第 3 课用自己的运行现算,结论随负载变)。