Claude 的提示缓存怎么用:怎么开、怎么看有没有命中、什么会让它失效,以及通过中转站时要多带的一个字段
提示缓存让重复发送的那段开头按十分之一甚至更低的价格计费。本文整理两种开法、响应里三个数字怎么读、哪些改动会让缓存失效、缓存诊断、数 token 和上下文窗口。通过中转站实测:不做处理时十次里只命中一次;请求里带上固定的 metadata.user_id 之后,七次里命中六到七次。
本站已实测,测试日期:2026-10-13。
本页目录(12 节)
这篇教程解决一个问题:自己写程序调 Claude,每次都要带上一大段不变的东西(系统提示、工具定义、前面的对话),怎么让这一段不用每次都按全价付钱?
本站另有一篇《提示词缓存为什么这么重要》,讲的是 Claude Code 团队的五条经验,偏道理。这一篇是接口层面的做法,并且每一条都发请求验证过。
| 实测环境 | |
|---|---|
| 日期 | 2026-10-13 |
| 方式 | 用 Python 直接发 HTTP 请求 |
| 模型 | Sonnet 5.5 |
| 测试用的提示 | 一段约 2,800 token 的系统提示,后面跟一句很短的问题 |
| 连接方式 | 通过酷站AI 中转站,不是直连官方 |
通过中转站时,缓存的表现和直连官方有一处很大的不同,见第六节。用中转站的读者可以先看那一节。
一、它省的是什么
缓存认的是开头。一个请求的内容按固定的顺序排:工具定义,然后是系统提示,然后是消息。从最前面到你标记的那个位置为止,这一段如果和最近某个请求的一模一样,就不用重新处理,按很低的价格计费。
价格是在输入价上乘一个倍数:
| 操作 | 倍数 | 说明 |
|---|---|---|
| 写缓存,保留 5 分钟 | 1.25 倍 | 第一次要多付四分之一 |
| 写缓存,保留 1 小时 | 2 倍 | |
| 读缓存 | 0.1 倍 | Opus 5.5 和 Sonnet 5.5 是 0.05 倍,Fable 5.1 是 0.025 倍 |
拿这次测试用的那段系统提示在 Sonnet 5.5 上算一下,按官方价:
| 情况 | 这一段的费用 |
|---|---|
| 不用缓存,每次全价 | 0.0056 美元 |
| 第一次,写缓存 | 0.0070 美元 |
| 之后每次,读缓存 | 0.0003 美元 |
命中之后是全价的十八分之一。读一次就把第一次多付的钱赚回来了。
两条要记住的规则:
- 缓存默认活 5 分钟,每被用到一次就免费续上 5 分钟。 这个时间从请求开始时算起,不是从回答结束时算起。一个回答流式输出了 4 分钟,下一个请求得在它结束后 1 分钟内发出才接得上。
- 内容太短进不了缓存,而且不报错。 Opus 5.5、Sonnet 5.5、Haiku 5.5、Fable 5.1 的门槛是 512 个 token,Sonnet 5 和 Sonnet 4.6 是 1,024,Opus 4.6 和 Haiku 4.5 是 4,096。
二、响应里的三个数字
开了缓存之后,输入的 token 数被拆成三项:
"usage": {
"input_tokens": 4,
"cache_creation_input_tokens": 16,
"cache_read_input_tokens": 2790
}
| 字段 | 意思 |
|---|---|
cache_read_input_tokens |
从缓存里读出来的,按读缓存的价 |
cache_creation_input_tokens |
这一次新写进缓存的,按写缓存的价 |
input_tokens |
最后一个缓存断点之后的,按全价 |
容易看错的是第三项:input_tokens 不是整个输入。上面这个例子里它只有 4,实际的输入是三项加起来的 2,810。
判断有没有命中,就看 cache_read_input_tokens 是不是大于零。两个缓存字段都是零,说明这次根本没有缓存,多半是内容太短。
三、两种开法
自动缓存:在请求的最外层加一个字段,剩下的交给接口。
{
"model": "claude-sonnet-5-5",
"max_tokens": 1024,
"cache_control": {"type": "ephemeral"},
"system": "……",
"messages": [……]
}
它把断点放在最后一个能缓存的块上,对话变长时断点自动往后挪。多轮对话用它最省事。实测一场四轮的对话:
第 1 轮:input=4 写缓存=2812 读缓存=0
第 2 轮:input=4 写缓存=86 读缓存=2812
第 3 轮:input=4 写缓存=42 读缓存=2898
第 4 轮:input=4 写缓存=91 读缓存=2940
每一轮都把前面的全部读出来,只把新增的那一点写进去。
显式断点:把 cache_control 直接写在某个内容块上,自己决定缓存到哪。
"system": [
{"type": "text", "text": "……很长的系统提示……", "cache_control": {"type": "ephemeral"}}
]
适合“前面一大段固定、后面每次不同”的请求。一个请求最多四个断点,自动缓存也占其中一个。
断点放哪有讲究:放在最后一个每次都不变的块上。 如果断点落在一个每次都变的块上(带时间戳的、当次的用户输入),那个位置的内容永远对不上,缓存就白写了。
要缓存 1 小时,加上 ttl:
"cache_control": {"type": "ephemeral", "ttl": "1h"}
什么时候值得多付那一倍:两次请求之间经常隔 5 分钟以上、但一般不超过 1 小时,比如等人回复的对话。请求本来就很密的话,5 分钟的那种每次都会免费续期,用不着 1 小时的。
四、什么会让缓存失效
因为认的是开头,前面任何一处变了,从那里往后全部作废。顺序是工具、系统提示、消息:
| 改了什么 | 哪些缓存作废 |
|---|---|
| 工具定义(增、删、改、换顺序) | 全部 |
| 系统提示里任何一个字 | 系统提示和消息 |
| 开关网页搜索或引用 | 系统提示和消息 |
| 换模型 | 全部。缓存按模型分开存 |
tool_choice、有没有图片 |
消息 |
| 思考的配置、effort | 消息一定作废;工具和系统提示的缓存看模型 |
| 快速模式和标准速度之间切换 | 系统提示和消息 |
实测把断点放在系统提示上,先发三次建立缓存,然后逐项改动,每次改完再改回来:
| 这一次改了什么 | 读缓存 | 结果 |
|---|---|---|
| 原样(第二、三次) | 2,790 | 命中 |
effort 改成 low |
2,790 | 命中 |
effort 明确写成默认的 high |
2,790 | 命中 |
display 设成 summarized |
2,790 | 命中 |
max_tokens 从 40 改成 80 |
2,790 | 命中 |
| 多加一个工具 | 0 | 没命中,整段重写 |
| 系统提示里改一个字 | 0 | 没命中 |
| 换成 Opus 5.5 | 0 | 没命中 |
| 每次改回原样之后 | 2,790 | 又命中了 |
几点说明:
- 改回去还能命中。 旧的缓存条目在有效期内一直在,只要开头重新对上就行。
- 改 effort 没有让系统提示的缓存失效。 文档说这一项看模型,在 Sonnet 5.5 上测到的是不失效。但文档也说消息部分的缓存一定会失效,这次每个请求都只有一句新问题,测不出这一点。长对话中途改 effort,前面那些轮次的缓存是要重写的。
- 加一个工具,整段都没了。 工具排在最前面,所以它一变,后面的系统提示也跟着作废。
两个常见的坑,文档专门提了:
- 往系统提示里放时间戳、请求编号。 每次都不一样,缓存永远对不上。把这类会变的东西挪到断点后面的用户消息里。
- 序列化不稳定。 有的语言把对象转成 JSON 时键的顺序是随机的,工具定义或工具调用的内容每次字节都不同。
五、缓存诊断:让接口告诉你哪里变了
缓存没命中时,你只能看到读缓存那一项变成零,不知道是哪里变了。缓存诊断补的是这个:把上一个响应的 id 带上,接口比较前后两个请求,告诉你第一处不同在哪。
"diagnostics": {"previous_message_id": "msg_上一个响应的id"}
第一轮没有上一个响应,写 null。响应里会多出一个 diagnostics:
cache_miss_reason 的类型 |
意思 |
|---|---|
model_changed |
模型换了 |
system_changed |
系统提示变了,常见的是塞了时间戳 |
tools_changed |
工具变了:增删、换顺序,或者序列化不稳定 |
messages_changed |
前面的消息被改过、删过,不是只在后面追加 |
previous_message_not_found |
找不到上一个请求的记录。不说明你的请求变了 |
unavailable |
这次给不出诊断 |
diagnostics 是 null 而读缓存又很低,说明你的请求没变,是缓存条目过期了,该缩短间隔或者换 1 小时的。
它只在 Claude API 上有,而且前后两个请求要在同一个工作区里。
实测通过中转站:请求带上了这个字段,故意在系统提示末尾加了时间戳,响应里的 diagnostics 始终是 null。在这家中转站上它用不了。
六、通过中转站:命中率差别很大
这是这次实测里最要紧的一节。
不做处理,几乎不命中
同一段系统提示,断点放在上面,每隔两秒发一次,每次只换问题。按道理第二次起每次都该命中:
第 1 次:写 2805 读 0
第 2 次:写 16 读 2789
第 3 次:写 2805 读 0
第 4 次:写 2805 读 0
……(第 5 到 10 次都是写 2805 读 0)
后 9 次里命中 1 次
后来又跑了两组各八次,后七次里分别命中 2 次和 0 次。
没命中的时候,每次都按写缓存的价付钱,比不开缓存还贵四分之一。
带上固定的 metadata.user_id,几乎全中
metadata.user_id 是接口的一个正式字段,用来标识请求背后是哪个用户,填一个不含个人信息的字符串。本站在请求里加上它,一组请求用同一个值:
"metadata": {"user_id": "my-app-session-0001"}
| 请求里带了什么 | 八次的读写情况 | 后七次命中 |
|---|---|---|
不带 metadata |
写 写 写 写 写 读 读 写 | 2 次 |
user_id 固定为一个普通字符串 |
写 写 读 读 读 读 读 读 | 6 次 |
user_id 用 Claude Code 的那种格式 |
写 读 读 读 读 读 读 读 | 7 次 |
不带 metadata(再来一遍) |
写 写 写 写 写 写 写 写 | 0 次 |
第四节那张“什么会失效”的表,就是带着固定的 user_id 测出来的,不带的话命中与否基本是随机的,什么规律也看不出。
所以通过中转站自己写程序,一场对话里的所有请求都带上同一个 metadata.user_id。 不同的对话用不同的值。
为什么会这样,本站只能推测。文档说缓存是按组织和工作区隔离的,不同的账号之间互不共享。如果一家中转站后面有不止一个上游账号,请求每次落在哪个账号上,缓存就留在哪个账号里;带上固定的 user_id,它才会把同一个用户的请求送到同一处。这是推测,中转站的文档里没有写。
这也能解释为什么用 Claude Code 时不需要操心这件事:Claude Code 自己会在每个请求里带这个字段。
另外两处不一样
- 不写
cache_control也在缓存。 官方接口上不标记就不缓存。通过中转站,不带任何缓存字段的请求,usage里照样出现了缓存的写入和读取,而且是 1 小时的那种。按官方价,1 小时的写入是输入价的两倍。中转站实际怎么扣费,以它的账单为准,本站没有核对。 - 读写的数字里有一小截不是你的内容。 每个请求的输入都比自己数出来的多二十几个 token,来源本站无法确定。文档提到接口会自动加一些系统层面的 token 并且不为它们收费,但没有官方的 key,本站分不清这一截里哪些是官方加的、哪些是中转站加的。
七、发之前先数一数:数 token 的接口
官方有一个免费的接口 /v1/messages/count_tokens,请求体和发消息时一样,返回输入有多少 token。用处是发之前估花费、判断会不会超出上下文窗口、决定用哪个模型。
要知道的三点:
- 它给的是估算,和实际的可能差一点。
- 它不走缓存的逻辑。 请求里可以带
cache_control,但数出来的不分读写。 - 要用你打算用的那个模型去数。 分词器不同,同一段文字在新旧模型上的数字不一样。
通过这家中转站,这个接口的数字和实际计费对不上,偏差最大到三分之一,见《Claude 现在有哪些模型、各多少钱、怎么选》。估算花费请看真实响应里的 usage。
八、上下文窗口
上下文窗口是模型在一次请求里能“看到”的全部内容,也包括它这次要输出的。
- 现在的主力模型都是 100 万 token,不用加任何 beta 头,全程按标准价(Haiku 5.5 超过 10 万 token 的提示另算)。
- 什么都算在里面:系统提示、每一条消息、工具结果、图片、文档、工具定义,还有这一轮的思考和回答。
- 一个请求最多带 600 张图片或 PDF 页面。图片多的时候,可能先碰到请求体 32 MB 的上限。
- 超了会怎样:Claude 4.5 及之后的模型,输入加上
max_tokens超过窗口时请求照样被接受;真的写到窗口满了,回答停下,stop_reason是model_context_window_exceeded。 - 窗口大不等于该塞满。 文档明说,内容越多,准确率和记忆越差。
以前几轮的思考留不留在窗口里,看模型:Opus 4.5 及之后的 Opus、Sonnet 4.6 及之后的 Sonnet、Haiku 5.5、Fable 都默认留着,按输入计费;更早的模型会自动丢掉。
窗口快满时怎么办,是压缩和上下文编辑要解决的问题,本站会另写一篇。
九、工具和缓存
- 缓存工具定义:把
cache_control放在tools数组的最后一个工具上,前面所有工具都进缓存。 tool_choice一改,消息部分的缓存作废。 中途需要改的话,在改动点之前放一个断点。- 用工具搜索按需加载工具不破坏缓存。 被发现的工具是追加在对话里的,开头没有变。相反,中途往
tools里加一个工具,整个缓存都没了。 - 服务端工具的结果会自动缓存。 请求里只要有一个
cache_control,接口会在网页搜索这类工具的结果后面自动加一个 5 分钟的断点。所以你明明全用的 1 小时,usage里却可能看到 5 分钟的写入。
和 Claude Code 的关系
Claude Code 已经替你把这些做了:自动开缓存,每个请求带着固定的会话标识,系统提示和工具排在前面。你要留意的只有会让它失效的那几件事:会话中途换模型、增减 MCP 服务器或工具、改 CLAUDE.md。道理见《提示词缓存为什么这么重要》。
用 Agent SDK 的也一样,SDK 底下跑的就是 Claude Code。只有自己直接调接口的程序,才需要按这篇文章自己处理。
花费
这一轮大约一百次请求,每次的输入不到三千 token,输出几十个。因为有相当一部分没有命中缓存,按官方价估算合计约 0.5 美元。这是估算,不是中转站实际扣的金额。
这次实测没有覆盖到的
- 官方接口没有测。 “不带
user_id就不稳定”是这一家中转站的表现,直连官方不存在这个问题。 - 只测了 Sonnet 5.5,只用了一段系统提示。
- 缓存过期没有测:5 分钟和 1 小时到期后的表现、流式输出时间长的情况,都没有等。
- 消息部分的缓存因 effort、
tool_choice失效没有测出来,这次每个请求只有一句新问题。 - 缓存诊断在这家中转站上不可用,它的各种返回值是按文档整理的。
- 服务端工具、工具搜索和缓存的配合没有测。
- 为什么带上
user_id就能命中是本站的推测。