Mindket

Claude 提示词缓存:添加 cache_control 并读懂 usage 字段

黑色横幅,标题是“Claude API 实战”,下面是“调用、工具、缓存与智能体循环”。

提示词缓存让 Claude 复用提示词的前缀,而不是重新处理一遍。你在最后一个始终不变的块上加一个 cache_control 标记,之后以相同前缀开头的请求,就会从缓存里读取它。第一次调用写入缓存,后面的调用有没有读到它,看 usage 字段就知道。

本节内容

  • 把稳定的内容放在最前面,并标记最后一个永远不变的块。
  • 核对你所用模型的最小长度,因为过短的前缀会被悄悄跳过。
  • 每次响应都读 cache_creation_input_tokens 和 cache_read_input_tokens。
  • 用缓存诊断指出导致未命中的那处改动。
全部课程
本页目录
  1. 开始之前
  2. 添加缓存断点并验证它生效
  3. 断点覆盖什么
  4. 各模型的最小长度
  5. 存活时间与成本
  6. 什么会让缓存失效
  7. 查出请求为什么没命中
  8. 在流量到来之前预热缓存
  9. Thariq Shihipar 和 ClaudeDevs 谈围绕缓存来设计
  10. 常见错误
  11. 自己试一试
  12. 接下来
  13. 自测一下
  14. 常见问题
  15. 资料来源

开始之前

当许多请求都以同一个大块内容开头时,缓存才划算,比如系统提示词、工具定义、参考文档或很长的历史。第一次请求在响应开始后,把这段前缀写入缓存。之后前缀完全相同的请求,会把它读回来。文档明确说,缓存不会影响你收到的输出。

你需要第 1 课里的环境,而第 2 课里的工具列表,就是一个典型的缓存对象。缓存已经不再需要 beta 请求头。提示词缓存页面提到了唯一的陷阱:老教程里调用 client.beta.prompt_caching 的写法,在当前 SDK 里会抛出 AttributeError。请改用 client.messages.create。

添加缓存断点并验证它生效

示例用一份存在文件里的风格指南来审阅文字。指南在每次请求里都一样,所以断点加在它身上,跟在后面的问题则每次不同。请用一份至少 512 个 token 的真实指南,否则缓存会被跳过。

import anthropic

client = anthropic.Anthropic()

# Your stable text. It must reach the model's minimum, 512 tokens on Opus 5.5.
STYLE_GUIDE = open("style_guide.md", encoding="utf-8").read()


def ask(question: str):
    return client.messages.create(
        model="claude-opus-5-5",
        max_tokens=500,
        system=[
            {"type": "text", "text": "You review text against the style guide below."},
            {
                "type": "text",
                "text": STYLE_GUIDE,
                "cache_control": {"type": "ephemeral"},
            },
        ],
        messages=[{"role": "user", "content": question}],
    )


for question in ["Is the word 'utilize' allowed?", "How should I write a date?"]:
    usage = ask(question).usage
    print(
        "written:", usage.cache_creation_input_tokens,
        "read:", usage.cache_read_input_tokens,
        "uncached:", usage.input_tokens,
    )
  1. 把稳定的内容放在最前面。缓存前缀按 tools、system、messages 的顺序构建。
  2. 把 "cache_control": {"type": "ephemeral"} 加到每次请求里都相同的最后一个块上。
  3. 运行脚本。第二次请求要在第一次的五分钟之内发出。
  4. 读打印出来的数字。第一行显示写入缓存的 token 数,第二行显示同样数量的 token 被读取。
  5. 如果写入和读取都是 0,说明前缀低于该模型的最小长度,或者被标记的块在两次调用之间变了。

断点覆盖什么

断点会缓存它之前的一切,从第一个工具一直到被标记的块。写入只发生在断点处。读取则反方向进行:API 先在你的断点处找条目,再往前回溯之前的块,最多查 20 个位置,找早先某次请求写下的条目。它永远找不到那些写入时并不是断点的稳定文字。

这条规则解释了最典型的失败。把时间戳或用户消息放进被标记的块里,每次请求都会写入一个新条目,却没有一次读到。应当标记静态前缀的末尾,让变化的部分跟在后面。

你最多可以放四个断点,断点本身不花钱。计费只涉及写入、读取和未缓存的输入。长对话里第二个断点很重要:当某一回合在上次写入之后新增了二十个或更多块,除非更近的断点一直在写入,否则回溯就找不到旧条目。

两种方式的最小长度、顺序和 20 个块的回溯相同。出自提示词缓存页面,2026-11-05 阅读。
方面自动缓存显式断点
放在哪里请求顶层放一个 cache_control放在工具、系统或消息块上的 cache_control
对话变长时每次请求都移到最后一个可缓存的块停在你放的位置
适合什么多轮对话和智能体循环变化速度不同的部分,比如工具、系统提示词和文档
占用名额占用四个名额中的一个最多四个
何时失败最后一个块带有 TTL 不同的显式标记,或者四个名额已经用完被标记的块在请求之间发生变化

各模型的最小长度

短于最小长度的提示词会照常处理但不做缓存,API 也不会返回错误。你只能从 usage 字段发现:如果两个缓存计数都是 0,就是前缀太短,或者没有匹配上。

如果你的提示词刚好差一点,文档说把缓存内容补到阈值,往往是值得的,因为读取比未缓存的输入便宜。

适用于 Claude API、Claude Platform on AWS、Google Cloud 和 Microsoft Foundry。Bedrock 有自己的数值。模型名称见模型总览。2026-11-05 阅读。
可缓存的最小提示词模型
512 个 tokenFable 5.1、Opus 5.5、Sonnet 5.5、Haiku 5.5、Opus 5、Fable 5
1,024 个 tokenOpus 4.8、Sonnet 5、Sonnet 4.6、Sonnet 4.5
2,048 个 tokenOpus 4.7
4,096 个 tokenOpus 4.6、Opus 4.5、Haiku 4.5

存活时间与成本

默认存活时间是五分钟,从写入或读取该条目的那次请求开始算起。每次读取都会免费刷新它,所以繁忙的智能体会让自己的缓存一直保持有效。一次流式输出了四分钟的响应,只给后续请求留下大约一分钟的启动时间。

间隔更长时,在 cache_control 里设置 "ttl": "1h"。一小时的写入成本更高,文档建议用在运行超过五分钟的子智能体,或者用户可能不会马上回复的聊天上。同一次请求混用两种存活时间时,一小时的条目必须排在前面。这时 usage 对象会把写入拆成 ephemeral_5m_input_tokens 和 ephemeral_1h_input_tokens。

usage 的几个字段加起来才是全部:总输入等于 cache_read_input_tokens 加 cache_creation_input_tokens 再加 input_tokens。最后一个只统计最后一个断点之后的部分,所以缓存生效之后它会变小。

相对基础输入价格的倍数

1.25x

五分钟缓存写入

来源:Anthropic Docs: Prompt caching (2026年11月5日 查阅)

2x

一小时缓存写入

来源:Anthropic Docs: Prompt caching (2026年11月5日 查阅)

0.1x

大多数模型的缓存读取

来源:Anthropic Docs: Prompt caching (2026年11月5日 查阅)

0.05x

Opus 5.5 和 Sonnet 5.5 的缓存读取

来源:Anthropic Docs: Prompt caching (2026年11月5日 查阅)

什么会让缓存失效

工具、系统提示词、消息这个顺序里,任何一层的改动,都会让这一层和它后面的每一层失效。工具定义排在最前面,所以改动一个名称、一段描述或一个参数,就会让一切作废。

缓存诊断页面点出了一个让人意外的原因:缓存是按模型分开的,所以路由或回退换了另一个模型,就要从零开始。另一个原因来自写 JSON 的代码。Swift 和 Go 等语言会随机打乱键的顺序,所以 tool_use 块里的键,每次都必须以同样的顺序输出。

出自提示词缓存页面上的失效表,2026-11-05 阅读。
改动的内容工具系统消息
工具定义失效失效失效
网页搜索或引用开关保留失效失效
速度设置,快速或标准保留失效失效
tool_choice保留保留失效
增加或移除图片保留保留失效
思考参数、output_config.effort取决于模型取决于模型失效

查出请求为什么没命中

读 usage 只能告诉你缓存没命中,说不出原因。缓存诊断会比较两次请求,指出它们最先出现差异的地方。第一个回合传入 diagnostics 对象,把 previous_message_id 设为 null,下一个回合再传上一次响应的 id。它只在 Claude API 上可用。以前需要的 beta 请求头现在已经不用了,不过文档里的 Python 示例仍然走 client.beta.messages。

如果响应里的 cache_miss_reason 是 model_changed、system_changed、tools_changed 或 messages_changed,就指出了问题所在,cache_missed_input_tokens 则粗略表示丢掉的前缀有多大。messages_changed 通常意味着历史被截断、编辑或重新序列化了,而不是只往后追加(见缓存诊断页面)。

r1 = client.beta.messages.create(
    model="claude-opus-5-5",
    max_tokens=1024,
    cache_control={"type": "ephemeral"},
    system=SYSTEM,
    messages=[{"role": "user", "content": "Summarize section 1."}],
    diagnostics={"previous_message_id": None},  # opt in on the first turn
)

r2 = client.beta.messages.create(
    model="claude-opus-5-5",
    max_tokens=1024,
    cache_control={"type": "ephemeral"},
    system=SYSTEM,
    messages=[
        {"role": "user", "content": "Summarize section 1."},
        {"role": "assistant", "content": r1.content},
        {"role": "user", "content": "Now summarize section 2."},
    ],
    diagnostics={"previous_message_id": r1.id},
)

if r2.diagnostics and r2.diagnostics.cache_miss_reason:
    print(r2.diagnostics.cache_miss_reason.type)
  • 结果为 null 而且缓存读取很多,说明前缀没变,缓存命中了。
  • 结果为 null 但读取为零,说明请求一致,可是条目已经过期,所以要缩短间隔,或者改用一小时的存活时间。
  • 出现 changed 类型而且读取为零,说明请求变了。按类型指出的内容去改,先改第一个被报告的。

在流量到来之前预热缓存

条目要等第一个响应开始之后才存在。同一时刻发出的请求读不到它,所以先发一个,等它的响应开始,再并行发送其余的。

想在用户提问之前填好缓存,就发一个预热请求:把 max_tokens 设为 0,并在真实请求共享的最后一个块上放显式断点,通常是系统提示词或工具列表。它返回空的 content 数组、stop_reason 为 max_tokens,以及完整的 usage 块。不会计输出 token,但仍然会计一次缓存写入。

零 token 的请求如果设置了 stream、扩展思考、结构化输出或强制的 tool_choice,API 会拒绝,在批处理里也会被拒绝。要让思考和强度设置与真实请求保持一致,因为缓存是按它们来区分的。

Thariq Shihipar 和 ClaudeDevs 谈围绕缓存来设计

Thariq Shihipar 发帖时在 Anthropic 做 Claude Code,他 2026-02-20 在 X 上写道,智能体必须先围绕提示词缓存来设计,因为几乎每个功能都会以某种方式碰到缓存。放到你自己的智能体上,就是先定好断点放在哪里,再去加那些会在会话中途改动系统提示词或工具列表的功能。

ClaudeDevs 是 Anthropic 面向开发者的账号,它在 2026-05-14 分享过一个技巧:先把系统提示词发出去,Claude 会把它写进缓存,但不生成任何输出,等真正的用户请求到来,就会命中已经预热的缓存。这就是本课里 max_tokens 为 0 的预热请求。文档还补了一条帖子里没说的规则:用默认的五分钟有效期时,至少每五分钟重新预热一次。

从根本上说,设计智能体时必须先考虑提示词缓存,几乎每个功能都会以某种方式碰到它。

Thariq Shihipar, 曾在 Anthropic 做 Claude Code,引文为译文 · X (2026年11月5日 查阅)
Thariq Shihipar(@trq212)2026 年 2 月 20 日在 X 上发帖:做 Claude Code 时我最大的体会之一,是设计智能体时从根本上必须先考虑提示词缓存,几乎每个功能都会以某种方式碰到它。这篇文章我一天写完,但它是几个月心得的总结。
Thariq Shihipar 在 X 上,2026 年 2 月 20 日
ClaudeDevs(@ClaudeDevs)2026 年 5 月 14 日在 X 上发帖:在 API 里缩短长提示词首个 token 时间的小技巧,预热提示词缓存。先发送系统提示词,再发送用户提示词。Claude 会把它写进缓存,但不生成输出。真正的用户请求到来时,就会命中已预热的缓存。
ClaudeDevs 在 X 上,2026 年 5 月 14 日

X 官方嵌入卡片的截图,拍摄于 2026-11-05,时间为 UTC。点击截图可到 X 上打开原帖。

常见错误

  • 标记了一个含有时间戳、请求 id 或用户消息的块。
  • 低于模型最小长度还期待命中,结果既没有命中,也没有报错。
  • 开启缓存之后,还把 input_tokens 当成全部输入。
  • 在请求之间编辑、重排或重新序列化工具列表。
  • 在对话中途切换 tool_choice、强度或图片,然后纳闷消息为什么不再命中。
  • 停顿时间超过了存活时间,还指望缓存还在。
  • 以为第二个工作区会共享缓存。

自己试一试

  • 把示例运行两次,打印那三个 usage 数字。再改动风格指南里的一个字符,重新运行。哪个数字变了?
  • 在断点上方的第一个系统块里加一个时间戳。运行两次,看读取数一直是 0。
  • 设置 "ttl": "1h",打印 usage.cache_creation,看两种存活时间是怎样分开计数的。
  • 两次调用之间等六分钟,看 usage 字段显示什么。

接下来

下一课会在每次调用里加上顶层的 cache_control,写出完整的智能体循环,让每个新回合之前的历史都从缓存里读取。缓存只节省输入方面的工作,上下文窗口那一课讲了哪些内容仍然要放得下。

自测一下

  1. cache_control 断点应该放在哪里?

    查看答案

    D. 放在各次请求里始终相同的最后一个块上 写入只发生在断点处,所以放在会变化的块上,永远不会产生读取。

  2. 一次带有 cache_control 的调用,两个缓存 usage 字段都是 0,最可能的原因是什么?

    查看答案

    A. 前缀比模型的最小长度短 低于最小长度的前缀会照常处理但不做缓存,也不会返回错误。

  3. 开启缓存之后,usage 里的 input_tokens 统计的是什么?

    查看答案

    B. 只有最后一个断点之后的 token 总输入等于缓存读取加缓存写入再加 input_tokens,最后一项只覆盖最后一个断点之后的部分。

  4. 哪一种改动会让工具、系统和消息三层缓存同时失效?

    查看答案

    C. 编辑工具定义 缓存顺序里工具排在最前面,所以工具定义一变,它后面的每一层都会失效。

常见问题

缓存会改变 Claude 的回答吗?

不会。文档说提示词缓存不影响输出 token 的生成,所以你得到的,和不用缓存时是同样的响应。

缓存能和 Batch API 一起用吗?

可以,但命中只是尽力而为,因为批处理请求可能并发运行,顺序也不固定。文档建议先发一个带共享前缀和一小时缓存块的请求,再发批处理里的其余请求。

能手动清除缓存吗?

不能,没有清除缓存的办法。条目在至少五分钟没有被使用之后过期,或者在它的一小时存活时间之后过期。

thinking 块会被缓存吗?

它们自己不能带 cache_control。当它们位于之前的助手回合里时,会和其他内容一起被缓存,从缓存读取时也计入输入 token。

缓存读取比普通输入便宜多少?

2026 年 11 月 5 日,文档列出的是:大多数模型为基础输入价格的 0.1 倍,Opus 和 Sonnet 5.5 为 0.05 倍,Fable 5.1 为 0.025 倍。写入比普通输入贵,所以只用一次的前缀永远回不了本。

资料来源

  1. Anthropic Docs: Prompt caching (2026年11月5日 查阅)https://platform.claude.com/docs/en/build-with-claude/prompt-caching
  2. Anthropic Docs: Cache diagnostics (2026年11月5日 查阅)https://platform.claude.com/docs/en/build-with-claude/cache-diagnostics
  3. Anthropic Docs: Models overview (2026年11月5日 查阅)https://platform.claude.com/docs/en/models/overview
  4. X: Thariq Shihipar on designing agents for prompt caching first (2026年11月5日 查阅)https://x.com/trq212/status/2024638793719177291
  5. X: ClaudeDevs on pre-warming the prompt cache (2026年11月5日 查阅)https://x.com/ClaudeDevs/status/2055069548672631218

分享本节课

浏览基于长提示词构建的 AI 智能体

带着大段系统提示词或工具列表的智能体,从缓存中获益最多。看看成品智能体在 Mindket 上是怎样上架的。

浏览 AI 智能体

本节课的 Markdown 版本 · Claude API 实战课:从零构建 AI 智能体 · 全部课程