教程Claude Code系列第 6 / 21 篇

Claude 的提示缓存怎么用:怎么开、怎么看有没有命中、什么会让它失效,以及通过中转站时要多带的一个字段

提示缓存让重复发送的那段开头按十分之一甚至更低的价格计费。本文整理两种开法、响应里三个数字怎么读、哪些改动会让缓存失效、缓存诊断、数 token 和上下文窗口。通过中转站实测:不做处理时十次里只命中一次;请求里带上固定的 metadata.user_id 之后,七次里命中六到七次。

发布 · 约 11 分钟读完 · 经人工审核()· 本站的内容怎么来的

本站已实测,测试日期:2026-10-13。

本页目录(12 节)
  1. 一、它省的是什么
  2. 二、响应里的三个数字
  3. 三、两种开法
  4. 四、什么会让缓存失效
  5. 五、缓存诊断:让接口告诉你哪里变了
  6. 六、通过中转站:命中率差别很大
  7. 七、发之前先数一数:数 token 的接口
  8. 八、上下文窗口
  9. 九、工具和缓存
  10. 和 Claude Code 的关系
  11. 花费
  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 就能命中是本站的推测。

来源

通过 API 中转站使用这些模型?到 Veridrop 免费检测中转站给你的是不是它声称的模型,或者看看中转站排行榜前 100 名。

相关文章