# Prompt Cache 最后的4朵小小乌云

> 龙虾AGI通用实验室 · 2026-08-25

## 一

上个月有一天，我正用 Fable 5 写一个比较复杂的后端服务。聊了大概十五轮，上下文已经积累了不少。模型对项目结构很熟悉了，改代码又快又准。

然后我注意到模型的回答风格突然变了。

看了一眼状态栏，发现模型不知道什么时候从 Fable 5 变成了 Opus 4.8。不是我切的，是 Fable 5 的安全分类器觉得某个请求有风险，自动降级了。

我当时第一反应不是"为什么降级"，而是"我的缓存还在吗"。

之前写《大模型缓存机制完全指南》的时候就知道，切模型是最贵的操作。每个模型有自己独立的缓存，Fable 5 的 KV 不能给 Opus 4.8 用。这意味着十五轮积累起来的缓存前缀，可能一瞬间全部作废。

但这不是我主动切的。是系统替我切的。**那这笔重建缓存的费用，算谁的？**

## 二

带着这个问题去翻文档，在 Anthropic 官方 Cookbook 里找到了答案。

结论比我预想的要好：安全分类器触发的降级，有专门的计费规则。

降级到 Opus 4.8 之后，回退请求的输入 token 按缓存读取价计费，按基础价的 10%，不是全价，也不是缓存写入价（1.25 倍或 2 倍）。如果请求在输出任何内容之前就被完全拦截，输入 token 干脆不计费。

也就是说，底层确实发生了一次完整的 KV 重算（Opus 4.8 的权重跟 Fable 5 不同，同样的文字必须重新算），但 Anthropic 在账单上替你承担了大部分。跟自己手动执行 /model 切换的代价完全不同。

手动切模型：整个上下文按 cache write（1.25x）重建 → 最贵分类器降级：整个上下文按 cache read（0.1x）计费  → 官方补贴

这件事让我踏实了一些。但也让我开始想另一个问题：既然切模型的代价这么大，那其他操作呢？我平时用的 /btw、rewind、分叉，这些到底碰没碰缓存？

## 三

先说 /btw，因为这个最容易搞混。

/btw 的卖点是"不污染主线程上下文"。问一个旁路问题，回答不追加到对话历史里。但"不污染上下文"和"不花钱"是两回事。我一直不确定它到底有没有消耗缓存。

去查了一下，答案是：花钱，但花得很少，而且不影响主线程。

/btw 本质上是一次独立的 API 调用。它把主对话的完整上下文作为输入发给模型，拿到回答后丢弃，不写回主线程。

主线程：Q1-A1 → Q2-A2 → Q3-A3     ← 缓存前缀/btw "这个函数为什么这样写？"  ↓独立 API 调用：  输入 = [系统 + Q1-A1 + Q2-A2 + Q3-A3 + btw问题]         ←── cache read（10%）────────→   ← 全价 →  回答完毕，丢弃，不写入主线程主线程依然是：Q1-A1 → Q2-A2 → Q3-A3  ← 前缀没变

社区有人用 statusline 跟踪过：一次 /btw 调用后成本增加约 $0.08。是真实的 API 调用，走的是缓存读取。但因为主线程前缀完全没变，下一个正式问题照常命中缓存，就像 /btw 从来没发生过一样。

消耗缓存但不污染缓存。知道了，踏实了。

## 四

接下来是一个我经常遇到的操作：rewind。

写代码经常会走弯路。聊了六轮，觉得第五轮问错了方向，想 rewind 回到第四轮重新来。

直觉上，前四轮没变，不应该重新全价计算。但直觉靠不靠得住？

靠得住。

原始：  [系统][Q1-A1][Q2-A2][Q3-A3][Q4-A4][Q5-A5][Q6-A6]        ←────────────── 完整缓存前缀 ──────────────────→rewind 到第四轮：        [系统][Q1-A1][Q2-A2][Q3-A3][Q4-A4]        ←── 跟原缓存前缀的前半段完全一致 ──→发修改后的 Q5'：        [系统][Q1-A1][Q2-A2][Q3-A3][Q4-A4][Q5']        ←──── cache read（10%）──────────→ ← 全价 →

前四轮走缓存读取，新的第五轮全价。这就是前缀匹配规则的直接推论：rewind 删掉了后面的轮次，剩余的前缀跟之前缓存的内容完全一致，当然命中。

但这里有一个细节我当时想了一会儿才想通。第四轮是可能很久以前说的，比如半小时前。缓存的 TTL 对订阅用户是 1 小时，对 API 用户是 5 分钟。半小时前的缓存，没过期吗？

没过期。因为从第四轮到第六轮，中间每一轮请求都在读取包含第四轮的前缀。每次读取都刷新了 TTL 计时器。即使第四轮本身是半小时前的，只要中间一直在持续对话，它的缓存就一直活着。

所以 rewind 对缓存是安全的。Claude Code 官方文档也确认了这一点，把它列在"不会破坏缓存的操作"里。

## 五

想通了 rewind 之后，我又想到一个骚操作。

有时候我正在写一段代码，上一个问题刚答完，下一个问题还没想好。但我知道缓存有 TTL，订阅用户是 1 小时，API 用户是 5 分钟。如果我想太久，缓存就过期了，下一个问题要全价重建整个上下文。

那能不能在缓存快过期的时候，先随便发条"你好"，让模型回复一下，保住缓存？等想好了再 rewind 撤回"你好"，发真正的问题？

想了一下，应该可以。

[系统][Q1-A1][Q2-A2]        ← 缓存活着，TTL 在倒计时发"你好"：[系统][Q1-A1][Q2-A2][你好]   ← cache read 命中，TTL 重置 ✅Claude 回复之后 rewind 删掉"你好"这轮：[系统][Q1-A1][Q2-A2]        ← 前缀恢复发真正的问题 Q3：[系统][Q1-A1][Q2-A2][Q3]    ← cache read 命中 ✅

"你好"那轮虽然被撤回了，但它已经完成了使命：命中缓存并重置了 TTL。rewind 之后前缀恢复到"你好"之前的状态，跟缓存里的条目完全一致，所以下一个真正的问题照常命中。

唯一的前提是：发"你好"的时候缓存还没过期。如果已经超过了 TTL，那"你好"触发的就不是保活，而是缓存重建（cache write），性质完全不同。

后来发现社区早就有人想到了这个，甚至做了一个叫 cache-keepalive 的 Claude Code 插件，在 TTL 快过期时自动注入保活消息。原理完全一样，只是自动化了。

## 六

到这里，前面几个场景都搞清楚了。但有一个更复杂的情况我一直在想。

实际工作中我经常用分叉。问了六轮，觉得可能走了弯路，rewind 到第四轮分叉出一条新路径试试。在新路径上干了一会儿，发现还是原来的方向对，想切回原始的第六轮继续。

这时候原始第六轮的缓存还在吗？

这个问题用前面学到的规则可以自己推。

原始分支：  [系统][Q1..Q4] → [Q5-A5][Q6-A6]      ← 分叉后没人访问新分支：    [系统][Q1..Q4] → [Q5'-A5'][Q6'-A6']   ← 活跃共享前缀：  [系统][Q1..Q4]   ← 被新分支持续保活独有前缀：  [Q5-A5][Q6-A6]   ← 只有原始分支访问过

两条线索交叉。

第一条：共享前缀（Q1 到 Q4）一定还活着。新分支的每次请求都在读它，TTL 持续刷新。

第二条：原始分支独有的 Q5 和 Q6，从分叉那一刻起就没有任何请求在读它了。如果从分叉到现在没有超过 TTL，缓存还在，切回后可以命中完整六轮。如果超过了 TTL，Q5-Q6 的缓存已经过期，切回后只有 Q1-Q4 走缓存，Q5-Q6 全价重算。

核心就一句话：缓存不关心你在哪个分支上工作，它只关心"这段前缀最后一次被任何请求读取是什么时候"。

## 七

到这一步，我对缓存的使用层面已经没什么疑问了。所有场景都可以用三条规则解释：前缀匹配、模型隔离、TTL 续命。

但有一个问题一直在脑子里转，之前几篇文章都没有彻底回答过。

前缀匹配规则说：中间改了一个 token，后面全部失效。

为什么？

后面的 token 本身没变啊。比如一段十万 token 的对话，我改了第五百个 token，第五百零一到第十万个 token 一个字都没动。为什么这九万九千五百个 token 的缓存不能继续用？

## 八

这个问题要到 Transformer 的 attention 机制里去找答案。

KV cache 存的不是 token 的文字，而是模型对这个 token 在当前上下文中的"理解"。这个理解是通过 self-attention 计算出来的：每个 token 会对它前面所有 token 做加权求和，提取信息，形成自己的内部表示。

用一个中文例子。

原始序列：我 喜欢 苹果 汁"汁"的 KV 是怎么算出来的：  attention(汁→我)+ attention(汁→喜欢)+ attention(汁→苹果)+ attention(汁→汁)= "汁"的 hiddenstate="汁"的 KV

"汁"的 KV 值不只取决于"汁"自己，还取决于前面每一个 token 贡献的信息。

现在把"喜欢"改成"讨厌"：

修改后：我 讨厌 苹果 汁"汁"的 KV 重新计算：  attention(汁→我)+ attention(汁→讨厌)    ← 这里变了+ attention(汁→苹果)+ attention(汁→汁)= 不同的 hidden state= 不同的 KV

"汁"这个字本身没变，但它的含义变了。"喜欢苹果汁"里的"汁"和"讨厌苹果汁"里的"汁"，在模型的内部表示中是不同的东西。模型可能在第一种情况下把"汁"理解为"一种令人愉悦的饮品"，在第二种情况下理解为"一种令人厌恶的液体"。同一个字，不同的上下文，完全不同的内部状态。

同理，"苹果"的 KV 也变了：它前面的"喜欢"变成了"讨厌"，它接收到的上下文信息不一样了。从修改点开始，后面每一个 token 的 KV 都像多米诺骨牌一样连锁变化。

原始：  我    喜欢    苹果    汁KV：    KV1   KV2    KV3    KV4修改：  我    讨厌    苹果    汁KV：    KV1   KV2'   KV3'   KV4'        ✅    ❌     ❌     ❌        命中   ← 从这里开始全部重算 →

这就是"改一个字后面全部重算"的数学根源。不是缓存系统设计者偷懒只做了前缀匹配，而是 Transformer 的 attention 机制在数学上决定了：只有连续不变的前缀，其 KV 值才保证不变。

反过来也成立。在末尾追加新 token，比如用户发了一条新消息，前面的 KV 完全不受影响。因为因果掩码让每个 token 只看前面不看后面。后面来了什么新内容，跟前面的 KV 无关。

追加：  我    喜欢    苹果    汁    很好喝KV：    KV1   KV2    KV3    KV4   KV5（新算）        ✅    ✅     ✅     ✅    ← 只算这一个

追加对话能命中缓存，修改历史不行。不是规则规定的，是数学决定的。

## 九

想通了"为什么改中间不行"之后，另一个"为什么"也顺着解开了。

为什么不同模型不能共享缓存？

因为 KV 值是 token 经过模型权重矩阵计算后的中间产物。不同模型的权重不同，同一段文字会产出完全不同的 KV。

同一段输入："你好世界"Fable5：  K = f(token, 权重_Fable5)  → K_AV= g(token, 权重_Fable5)  → V_AOpus4.8：  K = f(token, 权重_Opus4.8) → K_BV= g(token, 权重_Opus4.8) → V_B权重_Fable5 ≠ 权重_Opus4.8→ K_A ≠ K_B→ V_A ≠ V_B

同一段中文经过两个不同翻译引擎，内部的中间表示完全不同。一个引擎的草稿纸不能给另一个引擎用。缓存系统按模型隔离不是人为的限制，是物理上不兼容。

这里有一个容易搞混的地方。"切模型导致缓存失效"不等于"旧模型的缓存被删除"。你从 Fable5 切到 Opus4.8，Fable5 的缓存还在（只要没超过 TTL）。只是 Opus4.8 用不了它。如果你在 TTL 内切回 Fable5，前缀又没变，Fable5 的旧缓存可以继续命中。

## 十

回头看这一路追下来的问题。

从"系统替我切了模型怎么办"开始，到"/btw 花不花钱"，到"rewind 前面的轮次要不要重新付费"，到"能不能发个你好续命"，到"分叉后还能不能切回来"，这些问题看起来五花八门，但答案全部来自同一套规则。

而那套规则本身，又来自 Transformer attention 机制的两个数学性质：每个 token 的 KV 依赖于它前面所有 token（所以改中间就全部重算），每个 token 的 KV 由模型权重算出（所以换模型就不能复用）。

最后整理一张速查表，方便随时对照。

操作	缓存影响	成本后果

正常追问	前缀命中，只有新内容全价	最便宜的交互方式

/btw 旁路问题	主线程缓存不受影响	消耗很低但不是零

rewind 回退	保留的前缀命中旧缓存	前面的轮次走 cache read

发消息保活后 rewind	TTL 被续命，前缀恢复后仍命中	浪费一轮但比缓存过期便宜

分叉后切回	共享前缀保活，独有前缀看 TTL	TTL 内切回可命中完整缓存

安全分类器降级	新模型缓存从头建立	输入按 10% 计费，官方承担大部分

手动切模型	新模型缓存从头建立	全价重算，最贵的操作

/compact	消息历史改写，缓存断裂	下一轮重建缓存

以后遇到新的缓存问题，不用查表。画出前缀变化图，看模型有没有变，看 TTL 有没有过期。答案自己就出来了。

![](images/5b9611/img_001.png)

我建了一个AI学习研究群，目前几十来人，都是在真正动手搞AI的人。

如果你也在自己**跑模型、写代码、做项目**，

或者使用**Claude**和**Claude code**，

欢迎**点赞关注转发**一键三连，然后加我微信，备注写清"你在做的AI方向"，聊得来的话我拉你进群。纯围观的和小白就不加了，有一定门槛。当然你也可以简单粗暴的甩给我999元，我拉你进群，这个没门槛。

![](images/5b9611/img_002.jpg)
