Claude 上下文缓存
- 通过
cache_control标记要缓存的前缀 - 可以标记多个断点,按层级复用
- 长系统提示与固定文档的典型场景收益最明显
机制
Claude 原生结构下,缓存不是自动的,需要在内容块上显式打标记:
{
"role": "system",
"content": [
{
"type": "text",
"text": "【这里是很长的固定文档】",
"cache_control": { "type": "ephemeral" }
}
]
}标记之后,这段前缀会被缓存,后续请求命中就不再重复计费。
缓存以「标记点之前的全部内容」为单位。也就是说,断点越靠后,缓存的内容越多,能省下的也越多。但前缀只要有一个字符变化,整段缓存就失效。
With Claude's native schema, caching isn't automatic — you mark content blocks explicitly:
{
"role": "system",
"content": [
{
"type": "text",
"text": "[A very long fixed document goes here]",
"cache_control": { "type": "ephemeral" }
}
]
}Once marked, that prefix is cached, and later requests that hit it aren't charged for it again.
Caching works by "everything up to the marked point". That means the later the breakpoint, the more content is cached and the more you save. But if even one character of the prefix changes, the whole cached segment is invalidated.
最多几个断点
单次请求最多 4 个缓存断点。适合这样的结构:
| 断点位置 | 缓存内容 | 变化频率 |
|---|---|---|
| 第 1 个 | 系统提示与工具定义 | 几乎不变 |
| 第 2 个 | 产品手册正文 | 偶尔更新 |
| 第 3 个 | 本次会话的早期对话 | 会话内固定 |
| 第 4 个 | 最近一轮上下文 | 每轮变化 |
这样设计可以让不同层级的缓存各自生效,避免一处改动把整条缓存冲掉。
A single request supports up to 4 cache breakpoints. A structure like this works well:
| Breakpoint | Cached content | How often it changes |
|---|---|---|
| 1st | System prompt and tool definitions | Almost never |
| 2nd | Product manual body | Occasionally |
| 3rd | Earlier turns of this session | Fixed within a session |
| 4th | The latest turn of context | Changes every turn |
This lets caches at different layers take effect independently, so one change doesn't wipe out the whole chain.
参数
目前只接受 ephemeral,表示短期缓存。
生存时间。不传时用默认值,可显式写成 5m 或 1h。
Currently accepts only ephemeral, meaning short-term caching.
Time to live. Uses the default when omitted; can be set explicitly to 5m or 1h.
响应中的用量
本次写入缓存的 token 数。首次请求时出现。
本次从缓存读取的 token 数。命中时出现。
未命中缓存的普通输入 token 数。
Tokens written to the cache in this request. Appears on the first request.
Tokens read from the cache in this request. Appears on a hit.
Ordinary input tokens that didn't hit the cache.
命中判定
| 情况 | cache_read | cache_creation |
|---|---|---|
| 首次请求 | 0 | 大于 0 |
| 完全相同的前缀再次请求 | 大于 0 | 0 |
| 前缀有改动 | 0 | 按新内容计 |
| 缓存已过期 | 0 | 重新写入 |
| Case | cache_read | cache_creation |
|---|---|---|
| First request | 0 | Greater than 0 |
| Identical prefix, later request | Greater than 0 | 0 |
| Prefix changed | 0 | Billed as new content |
| Cache expired | 0 | Written again |
使用示例
标记缓存断点
在需要缓存的内容块上加 cache_control,从开头到断点的全部内容进入缓存。断点之前的内容要稳定,否则每次都算新内容。
多点断点
一段对话里可以在多个位置标记断点,最多四个。每多一个断点就多一层缓存,适合长对话中分段复用。
核对命中情况
usage 里的 cache_creation_input_tokens 是本次写入缓存的数量,cache_read_input_tokens 是命中的数量。首次请求前者大于 0,之后应该是后者大于 0。
curl --request POST https://api.oairoute.com/v1/messages \
-H "Authorization: Bearer YOUR_OAIRoute_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "claude-sonnet-5-5",
"max_tokens": 1024,
"messages": [{ "role": "user", "content": "你好" }]
}' | jq '.usage | {created: .cache_creation_input_tokens, read: .cache_read_input_tokens}'Mark a cache breakpoint
Add cache_control to the content block you want cached; everything from the start to the breakpoint enters the cache. Content before the breakpoint must stay stable, or every request counts as new content.
Multiple breakpoints
In a conversation you can mark breakpoints at several positions, up to four. Each extra breakpoint adds a cache layer, which suits reusing segments of a long conversation.
Check whether you hit
In usage, cache_creation_input_tokens is how many tokens were written to the cache this time and cache_read_input_tokens is how many hit. The first request has the former greater than 0; subsequent ones should have the latter greater than 0.
curl --request POST https://api.oairoute.com/v1/messages \
-H "Authorization: Bearer YOUR_OAIRoute_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "claude-sonnet-5-5",
"max_tokens": 1024,
"messages": [{ "role": "user", "content": "Hello" }]
}' | jq '.usage | {created: .cache_creation_input_tokens, read: .cache_read_input_tokens}'