PROMPT CACHING

Claude 提示缓存(Prompt Caching)

Claude 提示缓存中文文档:自动缓存与显式断点、5 分钟和 1 小时 TTL、计费倍率、缓存失效规则、usage 字段与命中率计算,以及经 RootFlowAI 调用的注意事项。

最后验证:2026-11-04

提示缓存让 Claude 从请求中已经处理过的前缀继续处理,重复的系统提示、工具定义、长文档和多轮对话历史不必每次重新计算,可以降低费用和首字延迟。

本文译自 Anthropic 官方文档 Prompt caching,资料核对日期为 2026-11-04。示例地址和认证方式已改为 RootFlowAI,并补充了经本站调用时的差异。官方价格表未搬运,实际单价以模型价格页为准。

启用提示缓存有两种方式:

  • 自动缓存:在请求顶层加一个 cache_control 字段。系统会把缓存断点放在最后一个可缓存的块上,并随对话增长自动后移,适合多轮对话。
  • 显式缓存断点:直接在单个内容块上放 cache_control,精确控制缓存哪些内容。

经 RootFlowAI 调用前须知

  • 接口:使用 Anthropic Messages 协议 POST https://api.rootflowai.com/v1/messages。本文的 cache_control 写法只适用于这个接口;通过 /v1/chat/completions 调用 Claude 时,不保证消息里的 cache_control 会被转换到上游。
  • 认证:使用本站 API Key(见获取 API Key)。curl 可以用 Authorization: Bearer 或 x-api-key 请求头。Anthropic 官方 SDK 的 Base URL 填 https://api.rootflowai.com,不要带 /v1。
  • 缓存位置:缓存保存在实际处理请求的上游账号中。切换分组、切换模型或上游故障切换后,第一次请求通常要重新写入缓存。
  • 分组差异:不同分组的上游不同,自动缓存、1 小时 TTL、最低缓存长度和命中表现可能与官方不完全一致。例如经 AWS Bedrock 线路转发时,顶层 cache_control 可能不会被转发。需要在各分组稳定生效时,优先使用显式断点,并用响应里的 usage 字段确认。
  • 预热:官方的 max_tokens: 0 预热请求经本站转发时,可能被当作未填写 max_tokens,按默认输出上限正常生成回复并产生输出费用。预热做法见下文“缓存预热”。
  • 费用:缓存读取、缓存创建的实际单价见模型价格页,扣费以消费日志为准。

快速开始

最简单的做法是使用自动缓存。以下示例先在环境变量 ROOTFLOWAI_API_KEY 中设置本站 API Key。

curl https://api.rootflowai.com/v1/messages \
  -H "Authorization: Bearer $ROOTFLOWAI_API_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -H "content-type: application/json" \
  -d '{
    "model": "claude-opus-5-5",
    "max_tokens": 1024,
    "cache_control": {"type": "ephemeral"},
    "system": "你是一名文学作品分析助手,负责点评作品的主题、人物和写作风格。",
    "messages": [
      {
        "role": "user",
        "content": "分析《傲慢与偏见》的主要主题。"
      }
    ]
  }'

Python(需要 pip install anthropic):

import os

import anthropic

client = anthropic.Anthropic(
    api_key=os.environ["ROOTFLOWAI_API_KEY"],
    base_url="https://api.rootflowai.com",
)

response = client.messages.create(
    model="claude-opus-5-5",
    max_tokens=1024,
    cache_control={"type": "ephemeral"},
    system="你是一名文学作品分析助手,负责点评作品的主题、人物和写作风格。",
    messages=[
        {"role": "user", "content": "分析《傲慢与偏见》的主要主题。"},
    ],
)
print(response.usage.model_dump_json())

TypeScript(需要 npm install @anthropic-ai/sdk):

import Anthropic from "@anthropic-ai/sdk";

const client = new Anthropic({
  apiKey: process.env.ROOTFLOWAI_API_KEY,
  baseURL: "https://api.rootflowai.com",
});

const response = await client.messages.create({
  model: "claude-opus-5-5",
  max_tokens: 1024,
  cache_control: { type: "ephemeral" },
  system: "你是一名文学作品分析助手,负责点评作品的主题、人物和写作风格。",
  messages: [{ role: "user", content: "分析《傲慢与偏见》的主要主题。" }],
});
console.log(response.usage);

使用自动缓存时,系统会缓存到最后一个可缓存块为止的全部内容。之后前缀相同的请求会自动复用缓存。


提示缓存如何工作

发送启用了提示缓存的请求时:

  1. 系统检查到缓存断点为止的提示前缀,是否已被最近的请求缓存。
  2. 如果找到,就使用缓存版本,减少处理时间和费用。
  3. 如果没有,就处理完整提示,并在响应开始时缓存该前缀。

以下场景尤其适合提示缓存:

  • 包含大量示例的提示
  • 大段上下文或背景资料
  • 指令固定、反复执行的任务
  • 很长的多轮对话

缓存默认有效期为 5 分钟。每次使用缓存内容都会免费刷新有效期。

有效期从写入或读取该缓存的请求开始时计算,而不是从响应结束时计算。生成响应的时间也会计入有效期:如果一次响应流式输出用了 4 分钟,复用同一前缀的下一次请求必须在响应结束后约 1 分钟内发出。

如果 5 分钟太短,可以使用费用更高的 1 小时缓存,见下文“1 小时缓存”。


计费倍率

提示缓存把输入分成普通输入、缓存写入和缓存读取三类计费。官方倍率以基础输入单价为基准:

计费项官方倍率
5 分钟缓存写入基础输入单价的 1.25 倍
1 小时缓存写入基础输入单价的 2 倍
缓存命中与刷新(读取)基础输入单价的 0.1 倍
读取倍率例外:Claude Opus 5.5、Claude Sonnet 5.5基础输入单价的 0.05 倍
读取倍率例外:Claude Fable 5.1、Claude Mythos 5.1基础输入单价的 0.025 倍

官方说明这些倍率会与 Batch API 折扣、长上下文定价、数据驻留等其他价格调整叠加;按提示长度分档定价的模型,缓存读取和写入也计入请求的提示长度。

在 RootFlowAI:

  • 各模型、各分组的输入、缓存读取和缓存创建单价见模型价格页。部分模型或分组的缓存价格与上述官方倍率不同,以价格页为准。
  • 价格页的“缓存创建”是 5 分钟写入的单价。按倍率计费的 Claude 模型,1 小时写入按该单价的 1.6 倍计算;缓存创建为基础输入 1.25 倍时,1 小时写入即为基础输入的 2 倍,与官方一致。
  • 计费口径和核对方法见服务定价与计费说明。

支持的模型

官方说明,自动缓存和显式断点都支持所有在售的 Claude 模型。经 RootFlowAI 调用时,模型是否可用以模型价格页和 API Key 的分组权限为准。


自动缓存

自动缓存是启用提示缓存最简单的方式。不需要在单个内容块上放 cache_control,只在请求体顶层加一个 cache_control 字段,系统会自动把缓存断点放到最后一个可缓存的块上。

curl https://api.rootflowai.com/v1/messages \
  -H "Authorization: Bearer $ROOTFLOWAI_API_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -H "content-type: application/json" \
  -d '{
    "model": "claude-opus-5-5",
    "max_tokens": 1024,
    "cache_control": {"type": "ephemeral"},
    "system": "你是一个会记住对话内容的助手。",
    "messages": [
      {"role": "user", "content": "我叫 Alex,从事机器学习工作。"},
      {"role": "assistant", "content": "你好 Alex!今天有什么机器学习问题需要帮忙?"},
      {"role": "user", "content": "我刚才说我做什么工作?"}
    ]
  }'

多轮对话中的自动缓存

使用自动缓存时,缓存点会随对话增长自动后移。每个新请求都缓存到最后一个可缓存块为止的全部内容,之前的内容从缓存读取。

请求内容缓存行为
请求 1System + User(1) + Asst(1) + User(2) ◀ 缓存点全部写入缓存
请求 2System + User(1) + Asst(1) + User(2) + Asst(2) + User(3) ◀ 缓存点System 到 User(2) 从缓存读取;Asst(2) + User(3) 写入缓存
请求 3System + User(1) + Asst(1) + User(2) + Asst(2) + User(3) + Asst(3) + User(4) ◀ 缓存点System 到 User(3) 从缓存读取;Asst(3) + User(4) 写入缓存

缓存断点每次都会自动移到最后一个可缓存块,对话变长时不需要更新任何 cache_control 标记。

TTL 设置

自动缓存默认使用 5 分钟 TTL。也可以指定 1 小时 TTL,写入价格为基础输入单价的 2 倍:

{ "cache_control": { "type": "ephemeral", "ttl": "1h" } }

与块级缓存组合

自动缓存可以和显式缓存断点一起使用。一起使用时,自动缓存断点占用 4 个断点名额中的 1 个。

例如用显式断点缓存系统提示,同时让自动缓存处理对话:

{
  "model": "claude-opus-5-5",
  "max_tokens": 1024,
  "cache_control": { "type": "ephemeral" },
  "system": [
    {
      "type": "text",
      "text": "你是一个乐于助人的助手。",
      "cache_control": { "type": "ephemeral" }
    }
  ],
  "messages": [{ "role": "user", "content": "这份协议的关键条款有哪些?" }]
}

与显式断点相同的规则

自动缓存使用同一套缓存机制。价格、最低 token 数、内容顺序要求和 20 个块的回溯窗口,都与显式断点相同。

边界情况

  • 最后一个块已经带有相同 TTL 的显式 cache_control 时,自动缓存不产生额外效果。
  • 最后一个块带有不同 TTL 的显式 cache_control 时,API 返回 400 错误。
  • 已经有 4 个显式块级断点时,API 返回 400 错误(没有名额留给自动缓存)。
  • 最后一个块不能作为自动缓存断点时,系统会静默向前查找最近的可用块;找不到就跳过缓存。

显式缓存断点

需要更细的控制时,可以直接在单个内容块上放 cache_control。适用于需要分别缓存变化频率不同的内容,或要精确控制缓存范围的场景。

组织提示结构

把固定内容(工具定义、系统指令、上下文、示例)放在提示开头,用 cache_control 标记可复用内容的结尾。

缓存前缀按 tools、system、messages 的顺序生成。这个顺序形成层级,后一层建立在前一层之上。

自动前缀检查如何工作

只在固定内容的末尾放一个缓存断点即可,系统会自动找到之前的请求已经写入缓存的最长前缀。理解下面的机制有助于设计缓存策略。

三个核心原则:

  1. 只在断点处写入缓存。 给一个块加上 cache_control,就只写入一个缓存条目:以该块结尾的前缀的哈希。系统不会为更早的位置写入条目。哈希是累积的,覆盖从开头到断点(含断点)的全部内容,所以修改断点处或之前的任何块,下一次请求都会得到不同的哈希。
  2. 读取时向前查找之前写入的条目。 每次请求,系统计算断点处的前缀哈希并查找匹配的缓存条目。如果没有,就一次后退一个块,检查更早位置的前缀哈希是否与已有缓存匹配。它查找的是之前的写入,而不是内容是否稳定。
  3. 回溯窗口为 20 个块。 每个断点最多检查 20 个位置,断点本身算第一个。窗口内找不到匹配条目就停止检查(如果还有更早的显式断点,就从那个断点继续)。在官方 Claude API 上,连续的 tool_use 块算一个位置,连续的 tool_result 块也算一个位置,所以一轮里有很多并行工具调用,不会单独把上一请求的条目挤出窗口。

示例:在增长的对话中回溯

每轮追加新块,并把 cache_control 放在每次请求的最后一个块上:

  • 第 1 轮:10 个块,断点在第 10 块。还没有缓存条目,系统在第 10 块写入条目。
  • 第 2 轮:15 个块,断点在第 15 块。第 15 块没有条目,系统回溯到第 10 块,找到第 1 轮的条目,在第 10 块命中。系统只重新处理第 11 到 15 块,并在第 15 块写入新条目。
  • 第 3 轮:35 个块,断点在第 35 块。系统检查 20 个位置(第 35 到 16 块),什么也没找到。第 2 轮在第 15 块的条目正好在窗口外一个位置,因此没有命中。在第 15 块再加一个断点,会从那里开始第二个回溯窗口,找到第 2 轮的条目。

常见错误:把断点放在每次都变化的内容上

提示里有一段大的固定系统上下文(第 1 到 5 块),后面是每次请求都不同的块,包含时间戳和用户消息(第 6 块)。你把 cache_control 放在第 6 块:

  • 请求 1:在第 6 块写入缓存,哈希包含时间戳。
  • 请求 2:时间戳不同,第 6 块的前缀哈希也不同。回溯依次检查第 5、4、3、2、1 块,但系统从未在这些位置写入过条目,所以没有命中。每次请求都付一次缓存写入费用,却从不读取。

回溯不会自动找到断点之前的稳定内容并缓存它,只会找到之前请求已经写入的条目,而写入只发生在断点处。把 cache_control 移到第 5 块,也就是各次请求都相同的最后一个块,之后每次请求都能读取缓存的前缀。自动缓存也有同样的问题:它把断点放在最后一个可缓存块,而在这种结构里,那正是每次都变化的块,所以应改用放在第 5 块的显式断点。

要点:把 cache_control 放在“希望共享缓存的各次请求中,前缀完全相同的最后一个块”上。在持续增长的对话中,只要每轮新增少于 20 个块,放在最后一个块就可以:之前的内容不会变,下一次请求的回溯能找到上一次的写入。如果提示后面有变化的部分(时间戳、按请求变化的上下文、新来的消息),断点应放在固定前缀的末尾,而不是变化的块上。

何时使用多个断点

最多可以定义 4 个缓存断点,适用于:

  • 分别缓存变化频率不同的部分(例如工具很少变,上下文每天更新)
  • 更精确地控制缓存内容
  • 对话增长使断点距离上次缓存写入 20 个块或更多时,仍能命中

缓存断点的费用

缓存断点本身不收费。 只对以下内容计费:

  • 缓存写入:新内容写入缓存时(5 分钟 TTL 比基础输入贵 25%)
  • 缓存读取:使用缓存内容时(基础输入单价的 10%,部分模型例外,见“计费倍率”)
  • 普通输入:未缓存的内容

增加 cache_control 断点不会增加费用,费用只取决于实际缓存和读取的内容。断点的作用是让不同部分可以独立缓存。


缓存策略与注意事项

缓存限制

官方 Claude API 上,最低可缓存提示长度如下:

最低长度模型
512 tokensClaude Fable 5.1、Claude Mythos 5.1、Claude Opus 5.5、Claude Opus 5、Claude Sonnet 5.5、Claude Fable 5、Claude Mythos 5、Claude Haiku 5.5
1,024 tokensClaude Opus 4.8、Claude Sonnet 5、Claude Sonnet 4.6、Claude Sonnet 4.5、Claude Opus 4.1、Claude Opus 4、Claude Sonnet 4
2,048 tokensClaude Mythos Preview、Claude Opus 4.7、Claude Haiku 3.5
4,096 tokensClaude Opus 4.6、Claude Opus 4.5、Claude Haiku 4.5

其中 Opus 4.1、Opus 4、Sonnet 4、Haiku 3.5 已在官方 API 退役,仅部分云平台仍提供。Amazon Bedrock 的最低长度、失败行为和 usage 字段名以 AWS 文档为准。

更短的提示即使标记了 cache_control 也不会被缓存。这类请求按不缓存处理,不会返回错误。要确认提示是否被缓存,查看响应中的 usage 字段:cache_creation_input_tokens 和 cache_read_input_tokens 都为 0,说明没有缓存(多半是没达到最低长度)。

如果提示只差一点达到所用模型的最低长度,扩充缓存内容凑够门槛往往是值得的。缓存读取比未缓存输入便宜得多,对经常复用的提示能降低费用。

并发请求时注意:缓存条目要等第一个响应开始后才可用。如果希望并行请求也能命中缓存,先等第一个请求开始返回,再发送后续请求。

目前 ephemeral 是唯一支持的缓存类型,默认有效期 5 分钟。

可以缓存的内容

请求中的大多数块都可以缓存,包括:

  • 工具:tools 数组中的工具定义
  • 系统消息:system 数组中的内容块
  • 文本消息:messages.content 数组中的内容块,用户和助手轮次都可以
  • 图片和文档:用户轮次 messages.content 数组中的内容块
  • 工具调用和工具结果:messages.content 数组中的内容块,用户和助手轮次都可以

这些元素都可以通过自动缓存或 cache_control 标记来缓存。

不能缓存的内容

  • thinking 块不能直接用 cache_control 缓存。但当它们出现在之前的助手轮次中时,可以和其他内容一起被缓存;这样从缓存读取时,它们计入输入 token。
  • 子内容块(例如引用 citations)本身不能直接缓存,应缓存顶层块。对于引用,可以缓存作为引用来源的顶层 document 内容块,从而在使用引用时也能利用提示缓存。
  • 空的文本块不能缓存。

导致缓存失效的变更

修改已缓存的内容,可能让部分或全部缓存失效。

缓存遵循 tools → system → messages 的层级。某一层发生变化,会使该层及之后各层的缓存失效。

下表列出不同变更影响哪部分缓存。“工具”“系统”“消息”三列分别指这三层的缓存,✘ 表示缓存失效,✓ 表示缓存仍然有效。

变更内容工具系统消息说明
工具定义✘✘✘修改工具定义(名称、描述、参数)会使整个缓存失效
联网搜索开关✓✘✘开启或关闭联网搜索会修改系统提示
引用开关✓✘✘开启或关闭 citations 会修改系统提示
速度设置✓✘✘在 speed: "fast" 与标准速度之间切换,会使系统和消息缓存失效
tool_choice✓✓✘修改 tool_choice 参数只影响消息块
图片✓✓✘在提示任意位置增加或删除图片,会影响消息块
thinking 参数视模型而定视模型而定✘thinking 配置(模式,以及扩展模式下的 budget_tokens)会渲染进提示,修改后一定使消息缓存失效;在把该配置渲染到工具和系统之前的模型上,工具和系统缓存也会失效
effort 设置视模型而定视模型而定✘修改 output_config.effort 一定使消息缓存失效,对工具和系统缓存的影响与 thinking 参数相同。把 effort 显式设为模型默认值等同于不设置,不会失效。支持按消息调整 effort 的模型上,通过 messages 中 role: "system" 消息携带的 effort 变更不影响已缓存前缀
向带 thinking 的请求传入非工具结果内容✓✓视模型而定Opus 4.5 及以上、Sonnet 4.6 及以上和 Haiku 5.5 默认保留 thinking 块,缓存仍有效。更早的 Opus/Sonnet 模型,以及 Haiku 4.5 及更早的 Haiku 模型,会剥离之前缓存的 thinking 块,这些块之后的消息也会从缓存中移除
被丢弃的 thinking 块✓✓✘API 丢弃 Claude 5.1 及更新模型在本次请求中未被保留的 thinking 块时,缓存前缀从该块的位置起发生变化。接收模型能读取、且原样回传的块不影响缓存

官方说明,在支持对话中途修改工具的模型上,可以用 inline-tools-2026-09-15 beta 请求头,在对话中途新增工具或修改工具定义,而不改动 tools:把定义放在对话中途 system 消息的 tool_addition 块里,tools 保持第一次发送时的样子。缓存前缀仍然匹配,只有追加的消息按新输入处理。唯一的例外是 tools 数组中没有任何非延迟加载(non-deferred)工具时,第一个这样定义的工具会让该请求完全不命中缓存一次。经 RootFlowAI 调用时,beta 功能是否可用取决于所选分组的上游。

跟踪缓存效果

通过响应 usage 中的字段监控缓存效果(流式响应在 message_start 事件中):

  • cache_creation_input_tokens:本次新建缓存条目时写入缓存的 token 数
  • cache_read_input_tokens:本次从缓存读取的 token 数
  • input_tokens:既没有从缓存读取、也没有用于创建缓存的输入 token 数,即最后一个缓存断点之后的 token

input_tokens 只表示最后一个缓存断点之后的 token,不是你发送的全部输入。计算总输入:

总输入 tokens = cache_read_input_tokens + cache_creation_input_tokens + input_tokens

按位置理解:

  • cache_read_input_tokens:断点之前、已经缓存的 token(读取)
  • cache_creation_input_tokens:断点之前、本次正在缓存的 token(写入)
  • input_tokens:最后一个断点之后的 token(不参与缓存)

示例:一个请求有 100,000 个 token 的缓存内容(从缓存读取),没有新写入的内容,用户消息在断点之后有 50 个 token:

  • cache_read_input_tokens:100,000
  • cache_creation_input_tokens:0
  • input_tokens:50
  • 实际处理的总输入:100,050 tokens

这一点对理解费用和速率限制都很重要:缓存用得好时,input_tokens 通常远小于总输入。

计算缓存命中率时,分母应是全部输入:

缓存命中率 = cache_read_input_tokens ÷ (input_tokens + cache_read_input_tokens + cache_creation_input_tokens)

不要把“缓存读取 ÷(缓存读取 + 缓存创建)”当作命中率,它忽略了未缓存的普通输入。OpenAI 和 Gemini 协议的 usage 中,输入总量已包含缓存命中部分,口径与 Claude 不同,不能用同一公式混算。在本站消费日志中也可以逐条核对缓存读取与缓存创建的用量。

thinking 块与缓存

在扩展思考中使用提示缓存时,thinking 块有特殊行为:

随其他内容一起缓存:thinking 块不能用 cache_control 显式标记,但在后续请求中随工具结果一起回传时,会作为请求内容的一部分被缓存。这在工具调用场景中很常见:你把 thinking 块传回去继续对话。

输入 token 计数:thinking 块从缓存读取时,计入 usage 中的输入 token。计算费用和规划 token 预算时要考虑这一点。

缓存失效规则:

  • 用户消息只包含工具结果时,缓存保持有效。
  • Opus 4.5 及以上、Sonnet 4.6 及以上和 Haiku 5.5 默认保留 thinking 块,即使加入非工具结果的用户内容,缓存仍有效。
  • 更早的 Opus/Sonnet 模型,以及 Haiku 4.5 及更早的 Haiku 模型,加入非工具结果的用户内容时缓存失效,之前所有 thinking 块都会从上下文中剥离。
  • 即使没有显式的 cache_control 标记,上述缓存行为也会发生。

工具调用示例:

请求 1:User: "巴黎天气怎么样?"
响应:[thinking_block_1] + [tool_use block 1]

请求 2:
User: ["巴黎天气怎么样?"],
Assistant: [thinking_block_1] + [tool_use block 1],
User: [tool_result_1, cache=True]
响应:[thinking_block_2] + [text block 2]
# 请求 2 缓存的是请求内容(不是响应)
# 缓存包括:用户消息、thinking_block_1、tool_use block 1 和 tool_result_1

请求 3:
User: ["巴黎天气怎么样?"],
Assistant: [thinking_block_1] + [tool_use block 1],
User: [tool_result_1, cache=True],
Assistant: [thinking_block_2] + [text block 2],
User: [文本回复, cache=True]
# 这条非工具结果的用户内容,会根据模型保留或剥离之前的 thinking 块

在更早的 Opus/Sonnet 模型和 Haiku 4.5 及更早的 Haiku 模型上,此时所有之前的 thinking 块都会从上下文中移除。在 Opus 4.5 及以上、Sonnet 4.6 及以上和 Haiku 5.5 上,之前的 thinking 块默认保留,仍是缓存前缀的一部分。

缓存隔离与共享

  • 组织与工作区隔离:官方说明,缓存在不同组织之间隔离,提示完全相同也不会共享。在 Claude API、Claude Platform on AWS 和 Microsoft Foundry 上,同一组织内的缓存还按工作区隔离;Google Cloud(Google 托管)和 Bedrock 只按组织隔离。
  • 经 RootFlowAI 转发:缓存保存在实际处理请求的上游账号中。分组、模型或上游账号发生变化时,之前的缓存不会跟随迁移,需要重新写入。
  • 完全匹配:缓存命中要求提示片段 100% 相同,包括到 cache_control 所在块为止的全部文本和图片。
  • 不影响输出:提示缓存不影响输出 token 的生成。返回的内容与不使用缓存时相同。

最佳实践

  • 多轮对话先用自动缓存,它会自动管理断点。
  • 需要分别缓存变化频率不同的部分时,使用显式块级断点。
  • 缓存稳定、可复用的内容,例如系统指令、背景资料、大段上下文和常用工具定义。
  • 把要缓存的内容放在提示开头。
  • 用缓存断点分隔不同的可缓存前缀。
  • 把断点放在各次请求都相同的最后一个块上。如果提示由固定前缀和变化的后缀(时间戳、按请求变化的上下文、新来的消息)组成,断点应放在前缀末尾,而不是变化的块上。
  • 定期分析缓存命中率,按需调整策略。

不同场景的用法

  • 对话类智能体:降低长对话的费用和延迟,尤其是包含长指令或上传文档的对话。
  • 编程助手:在提示中保留代码库的相关部分或摘要,改进自动补全和代码库问答。
  • 长文档处理:把包括图片在内的完整长篇材料放进提示,而不明显增加响应延迟。
  • 详细指令集:共享大量指令、流程和示例。开发者通常只放一两个示例,有了提示缓存,可以放 20 个以上多样的高质量示例来获得更好的效果。
  • 工具调用型智能体:多次工具调用和反复修改代码时,每一步通常都要新的 API 调用,缓存能提升这类场景的性能。
  • 书籍、论文、文档、播客文字稿等长内容:把整份文档放进提示,让用户直接提问。

常见问题排查

如果缓存表现不符合预期:

  • 确认各次调用中被缓存的部分完全相同。使用显式断点时,确认 cache_control 标记位置一致。
  • 确认调用发生在缓存有效期内(默认 5 分钟)。
  • 确认 tool_choice、图片使用情况、thinking 配置和 output_config.effort 在各次调用中保持一致。
  • 确认缓存内容达到所用模型的最低 token 数(见“缓存限制”)。
  • 确认断点所在的块在各次请求中保持不变。写入只发生在断点处,如果该块变化(时间戳、按请求变化的上下文、新来的消息),前缀哈希永远对不上。回溯不会找到断点之前的稳定内容,只会找到之前请求在各自断点处写入的条目。
  • 确认 tool_use 内容块中的键顺序稳定。某些语言(例如 Swift、Go)在 JSON 转换时会打乱键顺序,导致缓存失效。
  • 经 RootFlowAI 调用时,确认使用的是 /v1/messages,并且各次请求使用同一 API Key 分组和同一模型。切换分组或模型后的第一次请求会重新写入缓存。

1 小时缓存

如果 5 分钟太短,可以使用费用更高的 1 小时缓存。官方说明,1 小时缓存可在 Claude API、Amazon Bedrock、Claude Platform on AWS、Google Cloud(Google 托管)和 Microsoft Foundry 上使用。经 RootFlowAI 调用时是否生效取决于分组上游,以 usage 中的 cache_creation 明细为准。

在 cache_control 中加入 ttl:

"cache_control": {
  "type": "ephemeral",
  "ttl": "1h"
}

响应会包含详细的缓存信息,例如:

{
  "usage": {
    "input_tokens": 2048,
    "cache_read_input_tokens": 1800,
    "cache_creation_input_tokens": 248,
    "output_tokens": 503,

    "cache_creation": {
      "ephemeral_5m_input_tokens": 148,
      "ephemeral_1h_input_tokens": 100
    }
  }
}

cache_creation_input_tokens 等于 cache_creation 对象中各值之和。

如果使用联网搜索等服务端工具时,看到了自己没有请求的 ephemeral_5m_input_tokens 写入,原因是服务端工具结果会被自动缓存,详见官方文档 Tool use with prompt caching。

何时使用 1 小时缓存

如果提示按固定节奏使用(例如系统提示的使用间隔短于 5 分钟),继续使用 5 分钟缓存即可,它每次使用都会免费刷新。

1 小时缓存适合以下场景:

  • 提示的使用间隔可能超过 5 分钟,但短于 1 小时。例如某个辅助智能体的任务要运行 5 分钟以上,或保存和用户的长对话、而用户通常不会在 5 分钟内回复。
  • 延迟很重要,而后续请求可能在 5 分钟之后才发出。
  • 希望提高速率限制的利用率。官方说明,缓存命中不计入速率限制。

5 分钟和 1 小时缓存的延迟表现相同。对长文档,通常都能看到首字延迟改善。

混合使用不同 TTL

同一请求中可以同时使用 1 小时和 5 分钟缓存,但有一个限制:TTL 更长的缓存条目必须出现在更短的之前,即 1 小时缓存条目必须在所有 5 分钟缓存条目之前。

混合使用时,API 在提示中确定三个计费位置:

  1. 位置 A:最高缓存命中处的 token 数(没有命中时为 0)。
  2. 位置 B:A 之后最高的 1 小时 cache_control 块处的 token 数(没有则等于 A)。
  3. 位置 C:最后一个 cache_control 块处的 token 数。

B 或 C 大于 A 时,那一段必然是缓存未命中,因为 A 是最高命中处。

计费方式:

  1. A 部分按缓存读取计费。
  2. (B - A) 部分按 1 小时缓存写入计费。
  3. (C - B) 部分按 5 分钟缓存写入计费。

官方文档用一张示意图说明三种情况,下表是按同一规则整理的文字示例:提示在 30,000 tokens 处有一个 1 小时断点,在 35,000 tokens 处有一个 5 分钟断点。

情况ABC计费
首次请求,没有任何命中030,00035,0001 小时写入 30,000;5 分钟写入 5,000
5 分钟后、1 小时内再次请求(5 分钟缓存已过期)30,00030,00035,000读取 30,000;5 分钟写入 5,000
5 分钟内再次请求35,00035,00035,000读取 35,000

缓存预热

缓存预热让你在用户发出真实请求之前,先把系统提示或工具定义写入缓存。这样用户第一次交互就不会因缓存未命中而变慢,适合对首字延迟敏感的应用。

官方做法:max_tokens 为 0

官方 API 支持在请求中设置 max_tokens: 0。API 读取提示并在各个 cache_control 断点写入缓存,然后立即返回、不生成任何输出。响应的 content 为空数组,stop_reason 为 "max_tokens",usage 字段完整。

以下设置与 max_tokens: 0 冲突,官方 API 会返回 invalid_request_error:

  • stream: true
  • 扩展思考(thinking.type: "enabled")
  • 结构化输出(output_config.format)
  • tool_choice 为 {"type": "tool", ...} 或 {"type": "any"}

max_tokens: 0 在 Message Batches 请求中也会被拒绝。

在 RootFlowAI 预热

把 cache_control 断点放在与后续请求共享的最后一个块上(通常是系统提示或工具定义),不要放在占位用户消息上,否则缓存条目会以占位消息为键,后续请求命中不了。因此这里要用显式断点,而不是自动缓存:自动缓存会把断点放在最后一个块,也就是占位消息上。占位用户消息可以是任何非空白字符串(示例用 "warmup")。

预热请求的 thinking 配置和 output_config.effort 也要与后续请求一致。这些值会渲染进提示,配置不同的预热可能写入一个真实请求永远命中不了的条目。正式请求开启扩展思考(thinking.type: "enabled")或使用名称带 -thinking 的模型时,max_tokens 必须大于思考预算,无法用 max_tokens: 1 预热;这种情况可以让第一个真实请求写入缓存,或改用 1 小时缓存减少过期。

# 应用启动时或按固定间隔预热
curl https://api.rootflowai.com/v1/messages \
  -H "Authorization: Bearer $ROOTFLOWAI_API_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -H "content-type: application/json" \
  -d '{
    "model": "claude-opus-5-5",
    "max_tokens": 1,
    "system": [
      {
        "type": "text",
        "text": "你是一名熟悉分布式系统的资深软件工程师……",
        "cache_control": {"type": "ephemeral"}
      }
    ],
    "messages": [{"role": "user", "content": "warmup"}]
  }'

# 之后用户发来消息时,系统提示前缀已经在缓存中
curl https://api.rootflowai.com/v1/messages \
  -H "Authorization: Bearer $ROOTFLOWAI_API_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -H "content-type: application/json" \
  -d '{
    "model": "claude-opus-5-5",
    "max_tokens": 1024,
    "system": [
      {
        "type": "text",
        "text": "你是一名熟悉分布式系统的资深软件工程师……",
        "cache_control": {"type": "ephemeral"}
      }
    ],
    "messages": [{"role": "user", "content": "如何实现一棵二叉搜索树?"}]
  }'

Python 写法:

import os

import anthropic

client = anthropic.Anthropic(
    api_key=os.environ["ROOTFLOWAI_API_KEY"],
    base_url="https://api.rootflowai.com",
)

SYSTEM_PROMPT = [
    {
        "type": "text",
        "text": "你是一名熟悉分布式系统的资深软件工程师……",
        "cache_control": {"type": "ephemeral"},
    }
]


def prewarm_cache():
    """在应用启动时或按固定间隔调用。"""
    response = client.messages.create(
        model="claude-opus-5-5",
        max_tokens=1,
        system=SYSTEM_PROMPT,
        messages=[{"role": "user", "content": "warmup"}],
    )
    # 首次预热应看到 cache_creation_input_tokens 大于 0
    print(response.usage.model_dump_json())


def respond(user_message: str):
    return client.messages.create(
        model="claude-opus-5-5",
        max_tokens=1024,
        system=SYSTEM_PROMPT,
        messages=[{"role": "user", "content": user_message}],
    )


prewarm_cache()
response = respond("如何实现一棵二叉搜索树?")
print(response.usage.model_dump_json())

预热请求在前缀尚未缓存时,和其他请求一样收取缓存写入费用。用响应中的 usage.cache_creation_input_tokens 确认写入成功,用后续请求的 cache_read_input_tokens 确认命中。

缓存 TTL 仍然适用。使用默认 5 分钟缓存时,至少每 5 分钟发一次预热请求才能保持缓存;用户请求间隔更长时,改用 1 小时缓存。


提示缓存示例

官方 Claude Cookbook 中的 prompt caching recipe 提供了更多示例和最佳实践。以下示例展示几种常见的缓存模式。

缓存大段上下文

curl https://api.rootflowai.com/v1/messages \
  -H "Authorization: Bearer $ROOTFLOWAI_API_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -H "content-type: application/json" \
  -d '{
    "model": "claude-opus-5-5",
    "max_tokens": 1024,
    "system": [
      {
        "type": "text",
        "text": "你是一名法律文书分析助手。"
      },
      {
        "type": "text",
        "text": "以下是一份复杂法律协议的全文:[此处放入 50 页协议全文]",
        "cache_control": {"type": "ephemeral"}
      }
    ],
    "messages": [
      {
        "role": "user",
        "content": "这份协议的关键条款和条件有哪些?"
      }
    ]
  }'

这个示例缓存了法律协议全文作为前缀,用户问题不缓存。

第一次请求:

  • input_tokens:只有用户消息的 token 数
  • cache_creation_input_tokens:整个系统消息(包括法律文书)的 token 数
  • cache_read_input_tokens:0(第一次请求没有命中)

有效期内的后续请求:

  • input_tokens:只有用户消息的 token 数
  • cache_creation_input_tokens:0(没有新建缓存)
  • cache_read_input_tokens:整个已缓存系统消息的 token 数

缓存工具定义

在 tools 数组的最后一个工具上放 cache_control,就能缓存工具定义。该工具及之前定义的所有工具作为一个前缀被缓存。

{
  "model": "claude-opus-5-5",
  "max_tokens": 1024,
  "tools": [
    {
      "name": "get_weather",
      "description": "Get the current weather in a given location",
      "input_schema": {
        "type": "object",
        "properties": { "location": { "type": "string" } },
        "required": ["location"]
      }
    },
    {
      "name": "get_time",
      "description": "Get the current time in a given time zone",
      "input_schema": {
        "type": "object",
        "properties": { "timezone": { "type": "string" } },
        "required": ["timezone"]
      },
      "cache_control": { "type": "ephemeral" }
    }
  ],
  "messages": [{ "role": "user", "content": "纽约现在的天气和时间是多少?" }]
}

第一次请求时,cache_creation_input_tokens 等于全部工具定义的 token 数。有效期内的后续请求中,这些 token 改为出现在 cache_read_input_tokens 中。

工具定义、defer_loading 与缓存失效的关系,见官方文档 Tool use with prompt caching。

继续多轮对话

import os

import anthropic

client = anthropic.Anthropic(
    api_key=os.environ["ROOTFLOWAI_API_KEY"],
    base_url="https://api.rootflowai.com",
)

response = client.messages.create(
    model="claude-opus-5-5",
    max_tokens=1024,
    system=[
        {
            "type": "text",
            "text": "……很长的系统提示",
            "cache_control": {"type": "ephemeral"},
        }
    ],
    messages=[
        # ……之前的长对话
        {
            "role": "user",
            "content": [{"type": "text", "text": "你好,能介绍一下太阳系吗?"}],
        },
        {
            "role": "assistant",
            "content": "当然。太阳系由太阳以及围绕它运行的天体组成,包括八大行星、众多卫星、小行星和彗星……你想了解哪方面?",
        },
        {
            "role": "user",
            "content": [
                {"type": "text", "text": "了解了。"},
                {
                    "type": "text",
                    "text": "再多讲讲火星。",
                    "cache_control": {"type": "ephemeral"},
                },
            ],
        },
    ],
)
print(response.usage.model_dump_json())

每一轮都把最后一条消息的最后一个块标记为 cache_control,让对话可以增量缓存。系统会自动查找并使用之前缓存过的最长块序列。之前带过 cache_control 的块,后来即使不再标记,只要在 5 分钟内被用到,仍算缓存命中(同时刷新缓存)。

系统消息上也放了 cache_control。这样即使它因超过 5 分钟未使用而被移出缓存,下一次请求也会把它重新加入缓存。

设置正确时,每次请求的 usage 应该是:

  • input_tokens:新用户消息的 token 数(很少)
  • cache_creation_input_tokens:新的助手轮次和用户轮次的 token 数
  • cache_read_input_tokens:到上一轮为止的对话 token 数

组合使用 4 个缓存断点

curl https://api.rootflowai.com/v1/messages \
  -H "Authorization: Bearer $ROOTFLOWAI_API_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -H "content-type: application/json" \
  -d '{
    "model": "claude-opus-5-5",
    "max_tokens": 1024,
    "tools": [
      {
        "name": "search_documents",
        "description": "Search through the knowledge base",
        "input_schema": {
          "type": "object",
          "properties": {
            "query": {"type": "string", "description": "Search query"}
          },
          "required": ["query"]
        }
      },
      {
        "name": "get_document",
        "description": "Retrieve a specific document by ID",
        "input_schema": {
          "type": "object",
          "properties": {
            "doc_id": {"type": "string", "description": "Document ID"}
          },
          "required": ["doc_id"]
        },
        "cache_control": {"type": "ephemeral"}
      }
    ],
    "system": [
      {
        "type": "text",
        "text": "你是一名可以访问文档知识库的研究助手。\n\n# 指令\n- 回答前先搜索相关文档\n- 注明信息来源\n- 回答客观准确\n- 多份文档都相关时综合整理\n- 知识库中没有的信息要明确说明",
        "cache_control": {"type": "ephemeral"}
      },
      {
        "type": "text",
        "text": "# 知识库上下文\n\n## 文档 1:太阳系概览\n太阳系由太阳及所有围绕它运行的天体组成……\n\n## 文档 2:行星特征\n每颗行星都有独特之处,水星是最小的行星……\n\n## 文档 3:火星探索\n几十年来火星一直是探索目标……\n\n[更多文档……]",
        "cache_control": {"type": "ephemeral"}
      }
    ],
    "messages": [
      {"role": "user", "content": "能搜索一下火星车的资料吗?"},
      {
        "role": "assistant",
        "content": [
          {"type": "tool_use", "id": "tool_1", "name": "search_documents", "input": {"query": "Mars rovers"}}
        ]
      },
      {
        "role": "user",
        "content": [
          {"type": "tool_result", "tool_use_id": "tool_1", "content": "找到 3 份相关文档:文档 3(火星探索)、文档 7(火星车技术)、文档 9(任务历史)"}
        ]
      },
      {
        "role": "assistant",
        "content": [
          {"type": "text", "text": "我找到了 3 份关于火星车的文档,接下来从火星探索文档中获取更多细节。"}
        ]
      },
      {
        "role": "user",
        "content": [
          {"type": "text", "text": "好的,请重点介绍毅力号火星车。", "cache_control": {"type": "ephemeral"}}
        ]
      }
    ]
  }'

这个示例用满了 4 个缓存断点,分别优化提示的不同部分:

  1. 工具缓存(断点 1):最后一个工具定义上的 cache_control 缓存全部工具定义。
  2. 固定指令缓存(断点 2):系统提示中的固定指令单独缓存,这部分很少变化。
  3. RAG 上下文缓存(断点 3):知识库文档独立缓存,更新文档时不影响工具和指令的缓存。
  4. 对话历史缓存(断点 4):最后一条用户消息带 cache_control,让对话随进展增量缓存。

这样做的灵活性:

  • 只追加新一轮对话、不改之前的内容时,4 段缓存全部复用。
  • 更新 RAG 文档、但工具和指令不变时,复用前两段缓存。
  • 对话变化、但工具、指令和文档不变时,复用前三段缓存。
  • 任一断点处发生变化,会使该段及之后的缓存失效,之前的缓存段仍然有效。

第一次请求:

  • input_tokens:很少(最后一个断点之后的 token,本例中接近 0)
  • cache_creation_input_tokens:所有缓存段的 token(工具 + 指令 + RAG 文档 + 对话历史)
  • cache_read_input_tokens:0(没有命中)

之后只追加一条新用户消息,并把第 4 个断点移到这条新消息上:

  • input_tokens:很少(最后一个断点之后的 token,本例中接近 0)
  • cache_creation_input_tokens:新用户消息和上一轮助手回复的 token(正在缓存的新对话段)
  • cache_read_input_tokens:之前已缓存的全部 token(工具 + 指令 + RAG 文档 + 之前的对话)

这种模式特别适合:

  • 带大段文档上下文的 RAG 应用
  • 使用多个工具的智能体系统
  • 需要保持上下文的长对话
  • 需要分别优化提示不同部分的应用

常见问题

需要多个缓存断点吗?只在末尾放一个够不够?

大多数情况下,在固定内容末尾放一个缓存断点就够了。 写入只发生在你标记的块上。把它放在各次请求都相同的最后一个块上,之后每次请求都会读取同一个条目。如果后面的块每次都变化(时间戳、新来的消息),断点放在它之前的最后一个稳定块上。

只有以下情况才需要多个断点:

  • 对话增长使断点距上次缓存写入 20 个块或更多,之前的条目落在回溯窗口之外
  • 希望分别缓存更新频率不同的部分
  • 为了优化费用,需要明确控制缓存哪些内容

例如系统指令很少变化、RAG 上下文每天更新,可以用两个断点分别缓存。

缓存断点会额外收费吗?

不会,断点本身免费。只对以下内容收费:

  • 写入缓存的内容(5 分钟 TTL 比基础输入贵 25%)
  • 从缓存读取的内容(基础输入单价的一小部分,见“计费倍率”)
  • 未缓存内容的普通输入

断点数量不影响价格,只有缓存和读取的内容量有影响。

如何从 usage 字段计算总输入 token?

usage 中三个输入字段合起来是全部输入:

总输入 tokens = cache_read_input_tokens + cache_creation_input_tokens + input_tokens

input_tokens 不是全部输入,只是最后一个缓存断点之后的部分。例如缓存了 200k token 的文档、用户问题 50 token:cache_read_input_tokens 为 200,000,cache_creation_input_tokens 为 0,input_tokens 为 50,总计 200,050 tokens。

缓存有效期多长?从什么时候开始算?

默认最短有效期(TTL)为 5 分钟,每次使用缓存内容都会刷新。也可以使用 1 小时 TTL。

有效期从写入或读取缓存的请求开始时计算,而不是响应结束时。生成响应的时间也计入有效期,所以下一次请求复用缓存的时间窗口等于有效期减去生成时间。响应很长、下一次请求可能在有效期之后才开始时,使用 1 小时 TTL。

最多能用几个缓存断点?

提示中最多可以定义 4 个缓存断点(cache_control 参数)。

提示缓存和 thinking 一起用会怎样?

修改 thinking 参数(切换模式,或修改扩展模式下的 budget)会使已缓存的消息前缀失效,也可能使缓存的系统提示和工具失效,因为 thinking 配置会渲染进提示。output_config.effort 的行为相同。详见“导致缓存失效的变更”。

如何启用提示缓存?

最简单的方式是在请求体顶层加 "cache_control": {"type": "ephemeral"}(自动缓存)。也可以在单个内容块上至少放一个 cache_control 断点(显式缓存断点)。经 RootFlowAI 调用时,两种方式都只适用于 /v1/messages。

能和其他 API 功能一起用吗?

可以,提示缓存能和工具调用、图片理解等功能一起使用。但改变提示中是否包含图片,或修改工具调用设置,会使缓存失效。

能手动清除缓存吗?

目前不能手动清除。缓存的前缀在至少 5 分钟未使用后自动过期。

如何判断缓存策略是否有效?

查看响应中的 cache_creation_input_tokens 和 cache_read_input_tokens 字段,并按“跟踪缓存效果”中的公式计算命中率。在本站也可以到消费日志按时间和模型核对缓存用量与扣费。

提示缓存如何保证隐私和数据隔离?

官方说明:缓存键由到缓存控制点为止的提示的加密哈希生成,只有提示完全相同的请求才能访问对应缓存;缓存永远不会跨组织共享。在任何位置使用 cache_control 都是安全的。要产生缓存读取,断点应放在稳定前缀的末尾;放在每次都变化的块上(例如时间戳或用户任意输入)会每次写入新条目,永远不会命中。

RootFlowAI 如何处理你的请求数据,见隐私政策。

Message Batches 能用提示缓存吗?

官方 Batches API 支持提示缓存,但批量请求会并发且无序处理,缓存命中只能尽力而为。RootFlowAI 当前没有开放 Message Batches 接口。

Python 报错 AttributeError: 'Beta' object has no attribute 'prompt_caching'

这通常是升级了 SDK 或沿用了旧示例代码。提示缓存已经不需要 beta 前缀。把 client.beta.prompt_caching.messages.create(**params) 改为 client.messages.create(**params)。

TypeScript 报错 TypeError: Cannot read properties of undefined (reading 'messages')

原因同上。把 client.beta.promptCaching.messages.create(...) 改为 client.messages.create(...)。


官方数据保留说明

以下是 Anthropic 对其官方平台的说明:提示缓存(自动缓存和显式断点)符合零数据保留(ZDR)条件,Anthropic 不存储提示和 Claude 回复的原始文本。缓存内容的 KV(key-value)表示和加密哈希只保存在内存中,不落盘。缓存条目的最短生命周期为 5 分钟(标准)或 1 小时(扩展),过期后会尽快删除,但不是立即删除。

RootFlowAI 的数据处理方式以本站隐私政策为准。

相关文档