巧用缓存,让 Claude 用得更划算
了解 Claude 的提示缓存(prompt cache)如何工作,以及如何控制 token 用量
你可能注意到,一段很短的提示词有时会消耗掉惊人的 Claude 用量。如果你把这段提示发到一个已经闲置了一阵子的会话里,很可能就是没命中缓存。以 Opus 5.5 为例,缓存未命中的成本最高可达命中时的 40 倍。下面我们来看看究竟发生了什么,以及怎么避免。
#缓存
简单说,缓存就是存放你之前提示词处理结果的临时存储。实际机制要更复杂一些,因为存下来的是中间结果(KV 表示),但要理解怎么利用它,不需要了解这些细节。
举个例子:你发一条提示,让 Claude 开发某个功能。你的工具(比如 Claude Code)会把你的提示发给 Opus,同时附带额外上下文:系统提示、你的 AGENTS.md 内容、技能(skills)等等。
Claude 处理完整个提示,把结果存入缓存,然后回复你。
当你发第二条消息时,工具会把整个上下文再发一遍。好消息是,Claude 不会从头处理。它会复用缓存里存的结果,只处理新增的信息。新的结果又会被存进缓存,为你下一轮对话做好准备。同样,这是简化后的说法,有些细节差异,但作为心智模型够用了。
缓存带来了巨大的性能提升,但它要占存储空间,而空间是有限的。如果过了一段时间 Claude 没收到你的后续消息,它就会清掉存下的结果,把空间让给别人。
这意味着,缓存被清掉之后再发的消息,必须把整个上下文重新处理一遍。
这就是所谓的“缓存未命中”。反过来,当你的消息能复用缓存里的结果时,就叫“缓存命中”。
#缓存未命中的代价
你已经看到,每个模型都会列出每百万输入 token 和输出 token 的价格,同时还会列出“缓存读取”和“缓存写入”的价格。
比如,Opus 5.5 每百万输入 token 收费 4 美元,但缓存读取每百万只要 0.20 美元。也就是说,命中缓存便宜了 20 倍。
写入缓存还有额外成本:Opus 5.5 对 1 小时缓存写入收取正常输入价格的 2 倍。因此,命中缓存和未命中缓存的成本差距达到 40 倍。

Claude API 定价
#示例
假设你正在一个带有 30 万 token 上下文的会话中工作,然后发送一条 1k token 的新消息。会有两种情况:
- 命中缓存:从缓存读取 30 万 token,按基础价格的 1/20 计费;另外向缓存写入 1k token,按基础价格的 2 倍计费。合计相当于 15k + 2k = 17k 个基础输入 token。
- 未命中缓存:向缓存写入 301k token,按基础价格的 2 倍计费。合计相当于 602k 个基础输入 token。
在这个贴近实际的例子里,未命中缓存的成本要高 35 倍以上。
#一般情况
如果已有 token 数为 P,新 token 数为 N,两种情况的成本分别是:
- 命中缓存:P/20 + 2N
- 未命中缓存:2(P+N)
两者之比为:
已有上下文 P 越大、新消息 N 越小,未命中缓存的成本就越接近命中缓存的 40 倍。
#如何避免缓存未命中
默认情况下,Claude Code 使用 1 小时缓存,也就是说,缓存在最后一次写入或读取后 1 小时过期。其他运行框架(harness)可能使用不同的缓存时长。
一种策略是持续发送消息,让缓存一直不过期。手动做法是,在缓存过期前发送类似下面这样的话:
wait, I'm still working on this
这条消息大约 10 个 token,但它可能向缓存写入超过 100 个 token,因为发送时会附带额外数据,比如上一条回复以及用户/系统标记。
所以,套用上面的公式,相比缓存未命中,这样做的成本节省接近 40 倍。上下文只有 100k token 时,就已经能带来 38.5 倍的成本节省。
注意,把第一个小时写入缓存要花 2 倍基础 token;而命中缓存后,每延长一小时只需 0.05 倍基础 token。也就是说,如果能把这个过程完全自动化,1 小时缓存成本是 2 倍,2 小时缓存是 2.05 倍,3小时缓存成本是 2.1 倍,以此类推。这里的成本不对称非常明显,完全可以加以利用。
自动化执行
Claude Code 有一个 /loop 命令,可以按计划定时发送消息。
它不是免费的,但循环大约 40 次的开销,才抵得上一次缓存未命中。
问题在于,cron 语法里没有简单办法表达「每 50 分钟一次」。最简单的替代方案是在 :00 和 :30 各跑一次,20 小时内正好 40 轮。
保住缓存的替代方案
最好的替代方案是用 /compact 命令压缩上下文。压缩会对对话做摘要,让上下文本身变小。代价是你可能丢掉一部分上下文,而这些内容之后可能恰好用得上。
压缩的成本取决于你的 harness,也就是发出压缩指令的那个工具。对 Claude Code 来说,这条指令大约 2k token。当前整个上下文都会命中缓存*,价格是基础 token 的二十分之一,而那 2k 指令 token 是新增的。这些指令也可以选择不写入缓存,但文档里没说清楚实际会怎样。
然后 Claude 输出摘要,这属于输出 token。输出 token 比输入 token 贵得多。比如 Opus 5.5,输出价格是基础输入价的 5 倍。按我的经验,摘要通常在 30k token 以内,相当于 150k 基础输入 token 的费用。这部分很容易被忽略,但往往是压缩过程中最贵的开销。
压缩后的第一条消息会把摘要写入缓存,同时写进去的还有系统提示词和工具定义。系统提示词和工具可能又是 30k token,但这很大程度上取决于你的配置。可以用 /context 查看上下文大小。
*注意
如果压缩时没有命中缓存,那绝对是最糟的情况。你要按 2 倍价格把整个上下文写入缓存,再加上压缩本身的费用、按 5 倍基础 token 价格输出的摘要,然后还要再按 2 倍价格把摘要以及系统提示词和工具写回缓存。记住:缓存过期时千万别压缩!
示例
回到那个上下文有 300k token 的会话,在缓存过期前对它做压缩:
-
压缩指令:从缓存读取 300k token,按基础价格的 1/20 计费;同时把 2k 指令 token 写入缓存,按 2 倍计费。合计 15k + 4k = 19k 基础输入 token。
-
摘要生成:30k 输出 token,按基础价格的 5 倍计费。折合 150k 基础输入 token。
-
压缩后的第一条消息:30k 摘要 + 30k 系统提示词与工具定义 + 新消息(假设 1k token),全部写入缓存,按 2 倍计费。折合 122k 基础输入 token。
总计 291k 基础输入 token。大约是那次 602k 缓存未命中花费的一半,但比缓存命中的 17k 贵得多。
省钱发生在后面。此后每条消息只需从缓存读取约 60k token,而不是 300k,成本约 5k 基础输入 token,而不是 17k。消息发得足够多,压缩的成本就赚回来了。
可以看到,不同策略的优劣很大程度上取决于几个变量,比如上下文大小和摘要长度。该做一次交互式对比了。
#交互式对比
下面是我们讨论过的三种场景:让缓存过期、循环发送等待消息、压缩上下文。图表展示了暂停期间以及暂停后前 10 条消息的累计基础 token 成本。
哪种策略最好,主要看上下文大小和需要循环的时长。粗略的经验是:上下文大或暂停时间长,就压缩;否则就循环。
至于什么算“大上下文”、什么算“长暂停”,两者互相牵制。但大致上,600k token 算大上下文,8 小时以上算长暂停。
如果你喜欢更细的判断流程,可以参考这套决策树:
-
上下文不到 100k:想太多了,让缓存过期就行。
-
否则,如果上下文超过 600k:压缩。反正你很快也得压。
-
否则,如果暂停不超过 2 小时:循环。
-
否则,如果暂停 16 小时以上:压缩。
-
否则,如果暂停 8–16 小时:上下文超过 200k 就压缩,否则循环。
-
否则,如果上下文不到 300k:循环。
-
否则,如果暂停 4 小时以上:压缩。
2–4 小时这一段是灰区。在这里,主要的问题是:
-
这个会话还会持续很久吗?如果是,压缩(compact)可能更合适。
-
只差几个提示就能完成任务吗?如果是,循环(loop)可能更合适。
下面是 Claude 提供的一张流程图:

在暂停时选择 /loop 还是 /compact 的流程图
#结论
当我意识到命中缓存和未命中缓存会带来 40 倍的使用量差异时,我以为自己靠循环策略挖到了宝藏。但把 /loop 和 /compact 放在一起对比后,我重新评估了它们各自更适用的场景。
我依然认为,在很多常见场景下 /loop 是最佳选择,比如午休时间较长,或者出门办事。
即便如此,上下文很大时,或者上下文中等而暂停时间很长时,压缩显然是更明智的选择。丢失相关上下文是倾向用 /loop 的一个理由,但随着模型越来越聪明,它们生成的摘要也会越来越好,这个问题会慢慢消失。
不过,压缩在保真度上有硬性上限。从 800k 的上下文压到 30k,压缩比超过 26 倍,肯定会丢失一些相关内容。作为参考,关于提示压缩的研究似乎都认为,10 到 20 倍压缩是甜点区,此时大部分相关信息仍然保留,而到 25 倍左右性能就会下降。那项研究压缩的是提示本身,而不是对它做摘要,但这个数字仍是一个有用的参照。
#附:Claude Code 可能会自动压缩你的上下文
就在我写这篇文章的时候,看到 Claude Code 报告:
⏺ Compacted while idle, before the prompt cache expired
当时我正在试验不同的上下文大小和缓存,刚抓取了我的 /context,只有 220.3k tokens。
上下文使用情况
-
系统提示:2.5k tokens(0.3%)
-
系统工具:12.9k tokens(1.3%)
-
MCP 服务器指令:752 tokens(0.1%)
-
记忆文件:1.6k tokens(0.2%)
-
技能:10k tokens(1.0%)
-
消息:192.6k tokens(19.3%)
-
剩余空间:746.7k(74.7%)
-
自动压缩缓冲区:33k tokens(3.3%)
看起来 Anthropic 也认同缓存未命中不是什么好事,只是在这个场景下,双方对最佳策略的看法不同——毕竟我当时只离开了一个多小时。
如果 Claude Code 能提供一个管理缓存过期时间的设置就好了。这样我就能根据手头的工作,把它设成:
- 保活(Keep Alive,也就是我那套循环策略)
- 智能缓存(Smart Cache,根据上下文大小和离开时长,在压缩或循环之间选择)
- 允许过期(Let Expire)
“离开时长”可以作为一项输入,交给运行框架(harness)。