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

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

Mindket 编辑部 · 发布于 2026-11-04 · 更新于 2026-11-05 · https://mindket.com/cn/learn/claude-api/claude-prompt-caching

## 要点

- 把稳定的内容放在最前面，并标记最后一个永远不变的块。
- 核对你所用模型的最小长度，因为过短的前缀会被悄悄跳过。
- 每次响应都读 cache_creation_input_tokens 和 cache_read_input_tokens。
- 用缓存诊断指出导致未命中的那处改动。

## 开始之前

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

你需要[第 1 课](https://mindket.com/cn/learn/claude-api/claude-api-quickstart)里的环境，而[第 2 课](https://mindket.com/cn/learn/claude-api/claude-tool-use)里的工具列表，就是一个典型的缓存对象。缓存已经不再需要 beta 请求头。[提示词缓存页面](https://platform.claude.com/docs/en/build-with-claude/prompt-caching)提到了唯一的陷阱：老教程里调用 `client.beta.prompt_caching` 的写法，在当前 SDK 里会抛出 AttributeError。请改用 `client.messages.create`。

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

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

````python
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 个位置，找早先某次请求写下的条目。它永远找不到那些写入时并不是断点的稳定文字。

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

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

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

## 各模型的最小长度

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

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

| 可缓存的最小提示词 | 模型 |
| --- | --- |
| 512 个 token | Fable 5.1、Opus 5.5、Sonnet 5.5、Haiku 5.5、Opus 5、Fable 5 |
| 1,024 个 token | Opus 4.8、Sonnet 5、Sonnet 4.6、Sonnet 4.5 |
| 2,048 个 token | Opus 4.7 |
| 4,096 个 token | Opus 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](https://platform.claude.com/docs/en/build-with-claude/prompt-caching), 2026-11-05 查阅)
- **2x** 一小时缓存写入 (来源：[Anthropic Docs: Prompt caching](https://platform.claude.com/docs/en/build-with-claude/prompt-caching), 2026-11-05 查阅)
- **0.1x** 大多数模型的缓存读取 (来源：[Anthropic Docs: Prompt caching](https://platform.claude.com/docs/en/build-with-claude/prompt-caching), 2026-11-05 查阅)
- **0.05x** Opus 5.5 和 Sonnet 5.5 的缓存读取 (来源：[Anthropic Docs: Prompt caching](https://platform.claude.com/docs/en/build-with-claude/prompt-caching), 2026-11-05 查阅)

## 什么会让缓存失效

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

[缓存诊断页面](https://platform.claude.com/docs/en/build-with-claude/cache-diagnostics)点出了一个让人意外的原因：缓存是按模型分开的，所以路由或回退换了另一个模型，就要从零开始。另一个原因来自写 JSON 的代码。Swift 和 Go 等语言会随机打乱键的顺序，所以 `tool_use` 块里的键，每次都必须以同样的顺序输出。

> **工作区之间不共享缓存**
>
> 在 Claude API 上，缓存按工作区隔离，不同的组织即使提示词完全相同也不共享。命中要求前缀百分之百相同，包括文字和图片，并且在同一个工作区内。

| 改动的内容 | 工具 | 系统 | 消息 |
| --- | --- | --- | --- |
| 工具定义 | 失效 | 失效 | 失效 |
| 网页搜索或引用开关 | 保留 | 失效 | 失效 |
| 速度设置，快速或标准 | 保留 | 失效 | 失效 |
| `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` 通常意味着历史被截断、编辑或重新序列化了，而不是只往后追加（见[缓存诊断页面](https://platform.claude.com/docs/en/build-with-claude/cache-diagnostics)）。

````python
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 上写道](https://x.com/trq212/status/2024638793719177291)，智能体必须先围绕提示词缓存来设计，因为几乎每个功能都会以某种方式碰到缓存。放到你自己的智能体上，就是先定好断点放在哪里，再去加那些会在会话中途改动系统提示词或工具列表的功能。

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

> 从根本上说，设计智能体时必须先考虑提示词缓存，几乎每个功能都会以某种方式碰到它。
>
> Thariq Shihipar, 曾在 Anthropic 做 Claude Code，引文为译文, [X](https://x.com/trq212/status/2024638793719177291) (2026-11-05 查阅)

[![Thariq Shihipar（@trq212）2026 年 2 月 20 日在 X 上发帖：做 Claude Code 时我最大的体会之一，是设计智能体时从根本上必须先考虑提示词缓存，几乎每个功能都会以某种方式碰到它。这篇文章我一天写完，但它是几个月心得的总结。](https://mindket.com/blog/shots/x-trq212-prompt-caching-first.webp?v=0cac74185170)](https://x.com/trq212/status/2024638793719177291)

Thariq Shihipar 在 X 上，2026 年 2 月 20 日

[![ClaudeDevs（@ClaudeDevs）2026 年 5 月 14 日在 X 上发帖：在 API 里缩短长提示词首个 token 时间的小技巧，预热提示词缓存。先发送系统提示词，再发送用户提示词。Claude 会把它写进缓存，但不生成输出。真正的用户请求到来时，就会命中已预热的缓存。](https://mindket.com/blog/shots/x-claudedevs-pre-warm-cache.webp?v=2a74144f28b7)](https://x.com/ClaudeDevs/status/2055069548672631218)

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`，写出完整的[智能体循环](https://mindket.com/cn/learn/claude-api/build-ai-agent-claude-api)，让每个新回合之前的历史都从缓存里读取。缓存只节省输入方面的工作，[上下文窗口那一课](https://mindket.com/cn/learn/prompt-engineering/context-window)讲了哪些内容仍然要放得下。

## 自测一下

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

   A. 放在用户消息上，因为它每次都变
   B. 只放在第一个工具上
   C. 放在请求里的每个块上
   D. 放在各次请求里始终相同的最后一个块上

   答案：D。 写入只发生在断点处，所以放在会变化的块上，永远不会产生读取。

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

   A. 前缀比模型的最小长度短
   B. API 密钥没有访问权限
   C. 响应用了流式输出
   D. max_tokens 设得太高

   答案：A。 低于最小长度的前缀会照常处理但不做缓存，也不会返回错误。

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

   A. 请求里的每一个 token
   B. 只有最后一个断点之后的 token
   C. 只有 Claude 生成的 token
   D. 从缓存里读取的 token

   答案：B。 总输入等于缓存读取加缓存写入再加 input_tokens，最后一项只覆盖最后一个断点之后的部分。

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

   A. 改变 tool_choice
   B. 加入一张图片
   C. 编辑工具定义
   D. 打开网页搜索

   答案：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 倍。写入比普通输入贵，所以只用一次的前缀永远回不了本。

## 资料来源

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

[浏览 AI 智能体](https://mindket.com/cn/agents)
