每轮都重发全部历史,钱花在哪?缓存能省多少?
本课一问:A1 第 2 课算过:每一轮都把全部历史整份重发。这些 token 要花多少钱?服务端的前缀缓存能省多少?钱到底花在哪一段?
这一课先不碰:KV cache 本身怎么省计算(LLM 课 C3 第 7 课);system 前缀怎么布局才不破坏缓存(A6 记忆那一章);Batch 接口(文末「你可能会问」)。
从已有出发
前几章已经各自看到一块:A1 第 2 课,9 轮请求的输入合计是最后一轮的好几倍;A2 岔路 B1,每一轮的新请求开头原封不动是上一轮;A5 第 1 课,命中缓存的 token 照样占着窗口;A6 的两天会话,「冻结」组的 system 一字不变、缓存一路命中,「刷新」组写完记忆一重建 system,缓存就断了。这一课把它们换算成钱。
本机服务的 usage 只有三个数:prompt_tokens(全部输入,含命中)、cached_tokens(命中)、completion_tokens(输出,含思考)。它不报缓存写入,导出时 cache_creation_input_tokens 记为 null——本课不编这个数,写入量一律由规则推算并标出来。
先自己设计:按全价估,或者按字段名加
- ① 朴素方案
- ② 它在哪失败
- ③ 改进方案
问题不在乘法,而在输入不是一种 token:同一轮的输入里,有从缓存读出来的(便宜),有这一轮写进缓存的(更贵),有既没读也没写的(原价)。三段价差十几倍,混成一个数就不可能对。下面先看价目,再看每一轮怎么拆。
estimated_cost 估出 $0.0371,按四段现算是 $0.0140——2.64 倍。把含缓存的 prompt_tokens 当成「未缓存输入」、再把命中加一遍,得 $0.0398(2.84 倍)。 第 1 课见过:Anthropic 的 input_tokens 不含缓存,OpenAI 的 prompt_tokens / input_tokens 与 OTel 约定的 gen_ai.usage.input_tokens 都含。上面第二种朴素算法就是把含缓存的数当成「未缓存」,再把命中加一遍。OpenCode 在源码里专门写了注释防这个坑:AI SDK v6 把各家的 inputTokens 统一成含缓存,算钱之前要把命中与写入减掉。
改进:按四段、按官方价目
两家官方价格页(快照 2026-09-26)的相关几行,单位是美元 / 百万 token:
| 模型 | 输入 | 命中(倍率) | 写入 5 分钟 | 写入 1 小时 | 输出 | 最小可缓存 |
|---|---|---|---|---|---|---|
| Claude Fable 5.1 | 10 | 0.25(0.025×) | 12.5 | 20 | 50 | 512 |
| Claude Opus 5.5 | 4 | 0.2(0.05×) | 5 | 8 | 20 | 512 |
| Claude Sonnet 5 | 2 | 0.2(0.1×) | 2.5 | 4 | 10 | 1024 |
| Claude Haiku 4.5 | 1 | 0.1(0.1×) | 1.25 | 2 | 5 | 4096 |
| gpt-6-sol | 2 | 0.2(0.1×) | 2.5(30 分钟) | — | 10 | 1024 |
| gpt-5.6-terra | 2 | 0.2(0.1×) | 2.5(30 分钟) | — | 12 | 1024 |
几条规则都来自两家的 prompt-caching 文档(同日抓取):缓存按前缀精确匹配;Anthropic 的写入按 5 分钟(1.25×)或 1 小时(2×)计,命中默认 0.1×,Opus 5.5 与 Fable 5.1 这类新旗舰更低;前缀不到「最小可缓存长度」就不缓存,也不报错;自动缓存把断点放在最后一个可缓存块上,随对话前移。OpenAI 从 GPT-5.6 起写入也要收 1.25×,有效期至少 30 分钟,最小前缀 1,024 证据等级 · 有理论动机 有数学或机制上的理由说明它可能有效,但没有证明它一定更好。最后是否采用,仍然要看实验。 本条官方一手:价格与规则逐条取自两家文档,生成脚本 gen_a08_prices.py 抓取原页并记 sha256。价格会变,引用时带日期。 出处Anthropic / OpenAI prompt-caching 与价格页(2026-09-26 抓取) ↗需联网 。
极小真数字:第 4 轮的一笔账
T5 第 4 轮:服务端报告输入 1,743、命中 1,296、输出 28。按 Claude Sonnet 5,未命中的部分按自动缓存写进 5 分钟缓存:
一整次运行逐轮摊开,就是下面这张账单:
价目可以改(美元 / 百万 token,默认值来自 2026-09-26 官方价格页快照)
试四件事:① 勾上「按最小可缓存长度推算」:Sonnet 5 的门槛是 1024,T5 前两轮不够、第 3 轮时缓存里还没有条目,三根柱子变成虚线「推算」;再把价目换成 Claude Haiku 4.5:单价是 Sonnet 5 的一半,可它的门槛是 4096,T5 最长的一轮才 2,389 token——每一轮都够不到,全按原价;两者的总账谁高?② 把运行换成 pd-v2-unlimited-s3(开思考):灰色的输出条压倒一切,输出占比那一行是多少?③ 换成 t10-refresh-day1:第 7 轮底下有紫字「重建」——这是真实发生的 system 重建,命中从 2,072 掉到 294,不是推算;④ 回到 T5,插入「从第 2 轮起 system 里放时间戳」:之后每一轮的命中都归零、整段重写,总账变成多少?
调研里这句话只有第三方博客级的证据,本课不把它当结论,而是用自己的运行现算:按 Sonnet 5,关思考的 T5 输出占 38%、另一次关思考的运行 46%;开思考的 pd-v2-unlimited-s3 输出占 94%,分层压缩那次 94%。账单的形状取决于负载:上下文长、输出短时输入占大头,缓存是省钱的主力;模型想得多时输出占大头,缓存几乎无关紧要。这些是本地 8B 模型在一个小任务上的比例(上下文最多两千多 token),换成长会话与云端模型,比例会变;要知道自己的账,得像这样用自己的 usage 算 证据等级 · 实验验证 由对比实验得出的经验结论,只在出处写明的模型、数据和规模下成立。换了条件可能不再成立,引用时要带上实验设置。 本条项目实测 usage × 官方价目快照的现算;token 数按 Qwen3-8B 的 tokenizer 计(Anthropic 页面写明 Claude 4.7 以后的新 tokenizer 同一段文字约多 30%),思考长度也是这个 8B 模型的。只说明「比例随负载变」,不估计任何云端模型的真实账单。 出处待补 。
前缀一变,后面全付写入价
缓存按前缀匹配,所以 harness 能做的主要是两件事:让前缀保持不变,和把断点放在不变的那一段末尾。
A6 的两天会话给了一个真实的反例:刷新组在第 6 轮写了一条记忆,第 7 轮 system 被重建(多了 51 个字符),而 system 在最前面——命中从上一轮的 2,072 掉到 294(剩下的是跨运行共享的基础前缀)。分层压缩那次运行更明显:前 4 轮命中率 98%,开始压缩之后的 17 轮只有 68%——每次压缩都换掉了历史,前缀随之改变 证据等级 · 实验验证 由对比实验得出的经验结论,只在出处写明的模型、数据和规模下成立。换了条件可能不再成立,引用时要带上实验设置。 本条项目实测:本机 mlx_lm.server 的前缀缓存(LRU,跨请求共享,没有最小长度),数值是服务端报告的 cached_tokens。云端缓存的规则不同(最小长度、断点、TTL),组件里用规则推算。 出处待补 。
OpenCode 给 Anthropic 一类模型放断点的办法:前两条 system 消息、最后两条非 system 消息,各挂一个 cacheControl——正好用满 Anthropic 的 4 个断点:
Codex 走 OpenAI,那边的缓存靠「缓存键」把相关请求路由到同一处:每个请求都带 prompt_cache_key,默认就是会话 id;Guardian 审查用 guardian:{父会话 id} 共享一条缓存:
缓存复用的是同一段前缀已经算好的中间结果;请求内容一字不差,模型看到的输入完全相同,变的只是价格和首 token 时间。LLM 课的 KV cache 是一个请求内部复用前面 token 的 K、V;前缀缓存是把这件事延伸到跨请求:下一轮的开头就是上一轮,那部分不用再算 证据等级 · 有理论动机 有数学或机制上的理由说明它可能有效,但没有证明它一定更好。最后是否采用,仍然要看实验。 本条机制层面的论证:相同前缀、相同模型、相同配置下,前缀部分的计算结果与重算相同。实现上的浮点细节本课没有核,也没有做「开 / 关缓存输出是否逐字相同」的对照实验。 出处待补 。
证据三问
| 问 | 本课的回答 |
|---|---|
| ① 它解决什么问题? | 算清每一轮的钱花在哪一段;知道缓存在自己的负载上能省多少、会在哪里失效 |
| ② 为什么可能有效? | 每轮整份重发时,前缀部分与上一轮相同;按前缀复用计算,这部分只收 0.1× 左右 证据等级 · 有理论动机 有数学或机制上的理由说明它可能有效,但没有证明它一定更好。最后是否采用,仍然要看实验。 本条价格倍率是官方一手;「前缀相同所以可复用」是机制论证。 出处Anthropic / OpenAI 价格页(2026-09-26) ↗需联网 |
| ③ 实验上呢? | 本课项目六次运行:关思考时按四段算比全价估少 62%;开思考时输出占 94%;真实的 system 重建让命中掉到 294 证据等级 · 实验验证 由对比实验得出的经验结论,只在出处写明的模型、数据和规模下成立。换了条件可能不再成立,引用时要带上实验设置。 本条项目实测 usage × 价目快照;本地模型、小任务、单次运行,只说明现象与方向。 出处待补 |
演进地图:你在这里
- 起点 写死一家的线格式CoreCoder(OpenAI 兼容 + litellm 兜底)、本课项目 miniagent(标准库 urllib)解决了最短的路:CoreCoder 的
llm.py净 259 行,换 provider 只改 base URL 与 key。卡在同一段带工具调用的对话,换成 Anthropic Messages,9 个语义位置里 9 个换了路径或类型(参数从字符串变成对象,5 条消息变 3 条);Codex 干脆不认 Chat:配置wire_api = "chat"直接报错。 - 第一步 加一层抽象,或只留一种用满它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包打了补丁;而且抽象挡不住流式与缓存的细节。 - 第二步 流式:按调用分别累积,结束才解析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 倍。
- 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 构建时现算。
一句话
先停在这里:记住账单的四段,和 input_tokens 在各家口径里含不含缓存。
再深一层:去实验课,把六次运行都放进账单里换价目、插事件,找出「单价低的模型反而更贵」的那一组。
下一个缺口
能跑、能换模型、能算清账了,怎么知道它到底修得好不好? → A9 评测
T5 这次运行花了 $0.0140(按 Sonnet 5 现算),验收没过;pd-v2-unlimited-s3 花了 $0.0631,过了。「便宜」和「好」要放在一起比——下一章讲怎么量「好」。
你可能会问
- 「5 分钟和 1 小时的写入,选哪个?」 1 小时写入贵(2× 对 1.25×),但一次空闲超过 5 分钟就要整段重写。组件里切到「写入 1 小时缓存」,再插一个「空闲 6 分钟」,对比两种 TTL 的总账。Anthropic 文档的算法:5 分钟写入读一次就回本,1 小时写入读两次回本。
- 「Batch 接口能再便宜一半吗?」 两家的 Batch 都是 5 折,文档写明可以和缓存折扣叠加;OpenAI 的 Batch 端点已包括
/v1/responses。代价是异步、最长 24 小时出结果——agent 每一轮都要等上一轮的结果,交互式循环用不上,离线批量评测(A9)可以用。 - 「本机服务第 1 轮为什么就命中了 293 个 token?」 本机的前缀缓存跨请求共享(LRU):T10 刷新组第 1 轮与之前运行共享了同一段基础前缀。云端的缓存也按组织隔离、按前缀共享,但有最小长度与有效期。