缓存经济学:一行时间戳如何让账单翻倍
这一章讲三件事: KV Cache(把算过的先存 起来)为什么既提速又省钱; 一行「正确」的代码如何拖垮一个每天 10 万次对话的系统;以及当省钱足够重要时, 缓存怎样反过来决定整个系统的架构长什么样。
1. 这一章讲什么
02 章结尾划出了「静态前缀 / 轨迹」的分界线,并许诺「前面不能动」有重大回报。 这一章兑现:回报叫 KV Cache——把模型对前文的中间计算结果存下来,下次只算 新增部分。它是大模型 API 速度快、价格低的主力功臣,也是全书技术密度最高的一节。
好消息是:原理可以跳过,三条铁律必须带走(第 3.2 节);坏消息的故事先讲, 因为它便宜得惊人。
2. 顶层全景:主走查——时间戳事故
事故经过、全部数字来自原书;我们逐层补的推算会当场标明。
一家团队的客服 Agent,每天处理 10 万次对话,一切正常。某天工程师为了让
Agent「知道现在几点」,在系统提示词里加了一行 Current time: {{now}},把
时间戳实时注入。代码看起来完全没问题。第二天监控告警:所有对话的首 token
延迟(模型吐出第一个字的等待时间)从 0.5 秒涨到 3-5 秒,月度推理账单几乎翻倍1。
这笔账要分三层算:
第 1 层 前缀变了什么?
之前:10 万次对话共用同一个系统提示词 → 服务商缓存命中,前缀只算一次
之后:时间戳精确到分钟 → 每隔一分钟,前缀字节就变一次
第 2 层 缓存还剩多少?
时间戳之后的每个对话,前缀都和其他对话不同
→ 跨对话的全局缓存命中率趋近于零(我们按机制推算;书里只给了结果)
第 3 层 账单怎么变?
缓存读取约为全价十分之一(见 3.4 节);命中率归零 ≈ 全部按全价计费
→ 输入侧成本接近翻倍;prefill(读前文建缓存)重算
→ 首 token 延迟 0.5s → 3-5s
一行「正确」的代码, 毁掉的是「10 万次对话共享一次前缀计算」这个隐藏的规模红利。 这不是 bug,是没把缓存当架构约束——这七个字是本章的标题句。
3. 核心原理
3.1 KV Cache 为什么快:先看直觉
模型每生成一个 token,都要「回头看」前文所有 token 的中间计算结果。如果每轮 从头算,开销随上下文长度爆炸式增长。KV Cache 的做法是把前文的中间结果缓存下来, 下一轮只算新增 token 的部分——前提是前缀完全不变,改一个字符,缓存全部作废2。
书里给了一个做菜的比方:前几步完全一样(同样的食材、同样的刀工),你可以从上次 切好的地方继续;前面任何一步变了,后面全部重来。系统提示词和工具定义就是 「前几步」3。
想再往下走一层,书里用四个字母演示了机制(细节可跳过,不影响后面):上下文 [A, B, C, D],要生成第 5 个 token E。不用缓存:生成 E 要算 5 组键值,第 6 个 算 6 组……总量与长度的平方成正比。
用缓存:A-D 的键值算一次存起来,生成 E 只算 E 自己的——但注意,注意力(模型决定重点看前文哪里的机制)计算仍要遍历全部缓存。
代价是:随长度线性(等比例、不加速)增长——这就是长上下文解码慢的根源4。
这一段的分工:前面半段解释注意力为什么必须遍历(它要挨个看),后半段解释 线性为什么仍慢(看一眼也是钱)。至于「改一个字为什么全废」:模型几十上百层串联, 第一层的输出喂第二层——第一层某处改了,它之后每一层的中间结果全部不同, 层层重算。
3.2 三条铁律
书里把这一节的所有内容压缩成三条结论,并明说:原理可以不读,这三条必须记住5:
| # | 铁律 | 一句话理由 |
|---|---|---|
| 1 | 系统提示词和工具定义一旦确定就不要改 | 哪怕多一个空格,缓存全废,延迟成倍、成本上升 |
| 2 | 动态信息永远追加到末尾 | 时间、用户状态这些会变的,作为新消息追加,别改前缀 |
| 3 | 用标准 API 格式,别自行拼接消息 | 标准格式会被翻译成模型训练时见过的固定序列(固定顺序的一串符号);自行拼 USER:…ASSISTANT:… 的主要危害不是缓存,是偏离训练格式、削弱多步思考能力 |
第 3 条值得 展开,因为最反直觉:缓存只认字节序列,只要拼出来的前缀字节级稳定, 照样能命中。文本格式化真正坏掉的是模型能力——工具结果被当成普通文字塞进去, 模型可能把它误认成新的用户查询,连带着把训练时建立的「思考保留」机制打断6。 省钱的事归缓存管,能力的事归训练分布(训练数据长什么样的统计)管,两条线别混。
3.3 模型怎么看上下文:注意力实验的三个发现
为什么「动态信息追加到末尾」特别有效?书里的注意力可视化实验给了机理。 注意力(模型决定「现在该重点看前文哪个部分」的机制):处理「怎么样」这个词时, 它给「北京天气怎么样」里每个词打分——「天气」0.55、「北京」0.35、「的」0.05, 其余约 0.05 给自己,合计恰好 1;输出主要由「天气」的信息构成(这些分数是 实验测出的真实值)。书里还用一张表格类比了这套打分的三个角色:查询(我在找什么)、 键(我是什么)、值(我携带的信息)7。
从热力图里读出三个对写提示词直接有用的模式:
-
注意力储存池(Attention Sink):序列第一个 token 常 常吸走超过 70% 的 注意力(模型分配给每个位置的份额,行话叫权重)。
原因是个硬约束——所有权重加起来必须恰好等于 100%(由一个叫 softmax 的数学函数保证),模型无法表达「什么都不关注」,多余的权重总得倒在某个稳定的 地方,于是开头那个固定位置成了公共回收站。这不是模型缺陷,是系统性现象8;
-
位置偏好:模型对开头和结尾的信息分配更高注意力,中间容易被忽视。 所以「关键信息放开头或结尾」是重要的实践原则9;
-
三角形模式:思考过程对后续输出呈现三角形注意力——模型拿自己的思考当 草稿来写答案。这为第 04 章「状态栏放末尾、紧邻新 token」埋下伏笔。
3.4 两层缓存与真实价格
两个容易混淆的概念,书里分得很清:KV Cache 是模型内部、单次推理内的提速手段; Prompt Cache 是 API 服务层、跨请求的优化——前缀相同的请求直接复用算好的 键值对10。
对开发者钱包真正有感觉的是后者,书里给了当时的行情:缓存读取约 为全价的 十分之一(Anthropic、DeepSeek、GPT-5 系列都是这个量级;更早的 GPT-4o 是 五折);但各家启用方式不同——Anthropic 要在请求里显式设置缓存断点(不是 自动命中),写入有约 1.25 倍加价,还有最小可缓存长度(1024 token)和约 5 分钟 的保鲜期,过期即失效;OpenAI 则是自动前缀缓存。数字会过时,结构不会: 读缓存远便宜于算缓存,写缓存可能略贵,保鲜期短到要求「持续命中才划算」11。
3.5 缓存作为架构约束:Claude Code 的三个决定
当省钱足够重要,缓存一致性会反过来主导架构。书里解剖 Claude Code 得到三个 设计决定,每个都反直觉:
-
提示词的结构由缓存边界决定。 系统提示词在物理上被一个缓存边界记号一分为二:记号之前可跨 用户、跨多场对话全局缓存;之后放用户与对话特定信息。于是提示词的排列顺序 首先由缓存经济性决定,其次才是语义(含义)逻辑——每个二值条件若放进边界之前, 缓存键变体翻一倍,N 个条件 2^N 种组合:3 个条件(系统类型/模式/语言)就是 8 种缓存键12;
-
子 Agent 必须与父 Agent 字节级对齐(对齐=逐字节完全一致)。 子 Agent 的提示词、工具定义、消息 前缀必须与父 Agent 逐字节一致,否则它发起的请求命中不了同一份缓存—— 「并行加速」与「省钱」在这里发生冲突,书里选择了钱13;
-
替换文本首次出现即冻结(此后不再改动)。 大工具输出被换成摘要时,摘要文本被持久化; 哪怕重启对话,也用完全相同的替换文本——只为让恢复后的字节流与缓存一致14。
这一节的启示是作者的原话:缓存经济性不是事后优化,而是前置约束——越早 纳入架构设计,工程代价越小15。
3.6 研究前沿:缓存未必是一次性的(选读)
本章到此都建立在「改一字节、全废」的铁律上。书末有一节研究前沿指出这条铁律 并非物理必然:模型在 prefill(逐字读前文建缓存的阶段)其实是在「做笔记」—— 读到「用户所在城市:北京」时,它把「这意味着什么」的结论顺手写进了后面每层的 缓存状态。既然笔记存在,改字段后 若有显式思考链,就能让改动顺着笔记传播, 用约 1% 的算力(计算代价)得到接近整段重算的效果;甚至能把上次为别的问题记的笔记重新编号 (一项叫 RoPE 重定位的技术)粘进新问题。书里报告的实验量级:上下文组装从平方级 重算变成线性拼接,首 token 延迟在实验里下降几十到几百倍。但这仍是研究阶段, 本节前面的三条铁律在当前生产系统里依然是默认原则16。
4. 作者的判断与证据
书里给了证据的: 时间戳事故是真实生产事件(有监控数字);注意力实验的 0.55/0.35/0.05 与 70% 储存池是书里跑出的测量;缓存价格是各厂商公开定价的转述 (会随定价调整过时)。
是作者的判断: 「缓存经济性主导架构」是从 Claude Code 一个样本归纳的模式; 「KV Cache 可编辑」来自作者自己的论文(书里注明),尚未成为生产默认。两者的 可证伪(怎么才算它错了)条件:若其他生产系统普遍不做字节级对齐也活得很好,前者就只是样本内规律; 若可编辑缓存在没有思考链的模型上也能安全使用,后者的边界就被推翻——书里恰好 给出了相反的实验观察(无思考链时改动会被忽略)。
5. 边界与局限
- 价格、加价倍数、最小长度、保鲜期全是 2026 年年中的行情,会变;「读远便宜于 算、前缀必须稳定」这个结构不大会变;
- 注意力分数与 70% 储存池来自特定模型的可视化实验,不同模型数值不同,机制相同;
- 三个发现(储存池/位置偏好/三角形)解释的是「统计倾向」,不是硬规则—— 别把它当成「中间的信息一定读不到」;
- 本章默认单模型单请求;多模型级联、跨厂商缓存互不通用。
6. 可带走的
- 前缀不变是省钱省时的全部前提:改一个字符,缓存全废;
- 三条铁律:前缀定了不改;动态信息永远追加末尾;用标准 API 格式;
- 实时信息进系统提示词 = 每次请求都换前缀 = 缓存归零——时间戳事故的完整机理;
- 缓存读取约全价 1/10;写入可能加价;有保鲜期——「持续命中」才有意义;
- 关键信息放开头或结尾,别埋中间(位置偏好);
- 第一个 token 常吸走七成注意力(储存池)——不是 bug,是权重必须有处可去;
- 缓存键变体随条件数指数(每加一个条件就翻倍式)增长:运行时条件一律放到缓存边界之后;
- 子 Agent 与父 Agent 字节级对齐,否则共享缓存失效;
- 缓存经济性是前置约束,不是事后优化;
- 「缓存可编辑」是研究方向:今天仍按「不可编辑」设计。
7. 原文地图
| 主题 | 原书章 | 原文位置 |
|---|---|---|
| KV Cache 直觉与前提 | 2 上下文工程 | text/04-ch02.txt:391(搜「爆炸式增长」) |
| 时间戳事故 | 2 上下文工程 | text/04-ch02.txt:393(搜「10 万次对话」) |
| 三条铁律 | 2 上下文工程 | text/04-ch02.txt:397(搜「三条核心结论」) · text/04-ch02.txt:399(搜「一旦确定就不要改」) · text/04-ch02.txt:401(搜「追加到末尾」) · text/04-ch02.txt:403(搜「标准 API 格式」) |
| 做菜类比 | 2 上下文工程 | text/04-ch02.txt:405(搜「做菜」) |
| 注意力打分 0.55/0.35/0.05 | 2 上下文工程 | text/04-ch02.txt:443(搜「0.55」) · text/04-ch02.txt:419(搜「Query、Key、Value」) |
| 注意力储存池与 softmax 约束 | 2 上下文工程 | text/04-ch02.txt:455(搜「注意力储存池」) · text/04-ch02.txt:457(搜「softmax」) |
| 位置偏好 | 2 上下文工程 | text/04-ch02.txt:463(搜「位置偏好」) |
| 上下文学习 | 2 上下文工程 | text/04-ch02.txt:465(搜「上下文学习」) |
| Chat Template 与思维链保留 | 2 上下文工程 | text/04-ch02.txt:469(搜「Chat Template」) · text/04-ch02.txt:483(搜「思维链保留机制」) |
| 平方级 vs 线性 | 2 上下文工程 | text/04-ch02.txt:495(搜「N² 成正比」) · text/04-ch02.txt:497(搜「线性增长」) |
| 改前缀为何全废 | 2 上下文工程 | text/04-ch02.txt:499( 搜「层层向下传递」) |
| 错误模式:滑动窗口/文本格式化 | 2 上下文工程 | text/04-ch02.txt:511(搜「滑动窗口」) · text/04-ch02.txt:513(搜「文本格式化」) · text/04-ch02.txt:515(搜「收敛回本节开篇」) |
| 两层缓存的区分与定价 | 2 上下文工程 | text/04-ch02.txt:519(搜「Prompt Cache 则是」) · text/04-ch02.txt:519(搜「十分之一」) |
| 架构三决定 | 2 上下文工程 | text/04-ch02.txt:531(搜「缓存边界」) · text/04-ch02.txt:533(搜「字节级对齐」) · text/04-ch02.txt:535(搜「冻结」) · text/04-ch02.txt:537(搜「前置约束」) |
| 可编辑缓存前沿 | 2 上下文工程 | text/04-ch02.txt:543(搜「做笔记」) · text/04-ch02.txt:545(搜「1% 的算力」) · text/04-ch02.txt:549(搜「笔记拼接」) |