TokenPlus

缓存命中率为什么重要

更新·markdown 原文

我梳理了一下 Anthropic 提示词缓存的计价和失效规则。跑 Claude Code 的时候,缓存读取占掉的 token 比其他三项加起来还多,所以命中率不是优化项,是账单的主项。数字截至 2026 年 7 月。

三个价钱

相对输入价
缓存读取0.1 倍
缓存写入(5 分钟档)1.25 倍
缓存写入(1 小时档)2 倍

命中一次,那段 token 只花十分之一的钱。代价是写进去的时候要先付一次溢价。

所以有回本点:

1 小时档的用处是熬过流量空档——请求间隔超过五分钟时缓存不会凉。但写入贵一倍,得靠更多次读取摊平。


短了不缓存,而且不报错

最小可缓存前缀的长度按模型不同,短于这个数就静默地不缓存——没有报错,只有 cache_creation_input_tokens: 0

模型最小长度
Opus 4.8 / 4.7 / 4.6 / 4.5、Haiku 4.54096 token
Fable 5、Sonnet 4.62048 token
Sonnet 4.5 及更早1024 token

同一段 3000 token 的前缀,在 Sonnet 4.5 上缓存得好好的,换到 Opus 4.8 就一次都不命中。换模型之后账单变了却查不出原因,先看这张表。


怎么看有没有命中

响应的 usage 里三个字段:

字段含义
cache_creation_input_tokens这次写进缓存的(付了 1.25 或 2 倍)
cache_read_input_tokens这次从缓存读的(付了 0.1 倍)
input_tokens没命中的那部分,全价

`input_tokens` 是余数,不是总数。 提示词总长 = 三个字段相加。跑了一整晚的 agent 只显示 4K input_tokens,不是它没吃 token,是剩下的都从缓存来了。

反过来,如果重复请求之间 cache_read_input_tokens 一直是 0,说明有东西在悄悄让缓存失效。


静默失效:前缀里改一个字节,后面全废

缓存是前缀匹配。 渲染顺序固定:toolssystemmessages。前缀里任何位置变了一个字节,那个位置之后的全部作废。

常见的六个凶手,都在前缀最前面:

写法后果
system 里插 datetime.now() / Date.now()每次请求前缀都不一样,一次都不会命中
早期内容里放 UUID、请求 ID同上
json.dumps(d) 没加 sort_keys=True,或者遍历 set序列化顺序不稳,字节就不稳
把用户 ID、会话 ID 拼进 system每个用户一套前缀,跨用户完全不共享
if flag: system += ... 条件拼接每种开关组合都是一份独立前缀
工具集按用户变工具渲染在最前面,全员不共享

修法只有一个方向:把会变的东西挪到最后,挪到最后一个断点之后。

不是所有改动都全废

有个分层,改动只影响自己那层和下面:

改了什么工具缓存system 缓存messages 缓存
工具定义(增删改顺序)
换模型
system 内容
tool_choice、开关 thinking

换模型等于缓存清零——缓存是按模型存的。这条对换中转站的人尤其要紧:换一家站就是换一个端点,之前攒的缓存一次都用不上,头几个请求全是冷写入。


两个不那么显眼的坑

20 条回看窗口。 每个断点最多往回找 20 个 content block。agent 循环里一轮塞进去二三十个 tool_use / tool_result 是常事,超过之后下一次的断点就找不到上一次的缓存,静默 miss。长轮次里每 15 条左右补一个断点。

并发全付全价。 缓存要等第一个响应开始返回之后才可读。同时发 N 个前缀相同的请求,谁也读不到还在写的那份,N 份全价。正确顺序是先发一个,等它吐出第一个 token,再发剩下的。


这跟买中转站有什么关系

三件事:

一、逆向档大多没有缓存。 这是它比价时最容易被漏掉的一块。假设一次请求里 80% 的输入是可缓存的重复前缀(跑 Claude Code 时这个比例不算高):

差 3.5 倍。一个名义上便宜三倍的逆向档,实际跑起来可能比官转还贵。(上面 80% 是举例用的假设,不是实测值——你自己的比例看 usage 三个字段现算。)

二、比价要连缓存一起比。 「占官方价百分比」比的是单价。如果两边的缓存支持不一样,单价一样也不等于账单一样。

三、换站的成本不只是重新充值。 缓存按模型和端点存,换过去头几个请求都是冷写入,付的是 1.25 或 2 倍。频繁比价换站的人,这笔钱是隐性的。


附:术语速查

意思
前缀匹配缓存按提示词开头的字节序列命中,中间变一个字节,后面全废
断点(breakpoint)cache_control 标记的位置,每次请求最多 4 个
冷写入第一次写进缓存,付 1.25 或 2 倍
TTL缓存存活时间,5 分钟或 1 小时两档
命中率cache_read_input_tokens 占总输入的比例

参考