---
title: 缓存命中率为什么重要
description: 缓存读取按输入价的 0.1 倍计，写入 1.25 或 2 倍。跑 Claude Code 时缓存读取占绝大部分 token，命中率直接决定账单。含回本点、各模型的最小可缓存长度、静默失效的六个常见原因，以及比价时该怎么把缓存算进去。
published: 2026-07-24
updated: 2026-07-24
keywords:
  - prompt caching
  - 缓存命中率
  - cache_read_input_tokens
  - Claude Code 省钱
  - 缓存 失效
  - 中转站 缓存
  - 逆向档 没有缓存
---

# 缓存命中率为什么重要

> 我梳理了一下 Anthropic 提示词缓存的计价和失效规则。**跑 Claude Code 的时候，缓存读取占掉的 token 比其他三项加起来还多**，所以命中率不是优化项，是账单的主项。数字截至 2026 年 7 月。

## 三个价钱

| | 相对输入价 |
|---|---|
| 缓存读取 | **0.1 倍** |
| 缓存写入（5 分钟档） | **1.25 倍** |
| 缓存写入（1 小时档） | **2 倍** |

命中一次，那段 token 只花十分之一的钱。代价是写进去的时候要先付一次溢价。

**所以有回本点：**

- **5 分钟档**：写 1.25 + 读 0.1 = 1.35，而不缓存跑两次是 2.0。**两次请求就回本。**
- **1 小时档**：写 2 + 读 0.2 = 2.2，不缓存三次是 3.0。**要三次才回本。**

1 小时档的用处是熬过流量空档——请求间隔超过五分钟时缓存不会凉。但写入贵一倍，得靠更多次读取摊平。

---

## 短了不缓存，而且不报错

最小可缓存前缀的长度**按模型不同**，短于这个数就静默地不缓存——没有报错，只有 `cache_creation_input_tokens: 0`：

| 模型 | 最小长度 |
|---|---|
| Opus 4.8 / 4.7 / 4.6 / 4.5、Haiku 4.5 | 4096 token |
| Fable 5、Sonnet 4.6 | 2048 token |
| Sonnet 4.5 及更早 | 1024 token |

同一段 3000 token 的前缀，在 Sonnet 4.5 上缓存得好好的，换到 Opus 4.8 就一次都不命中。换模型之后账单变了却查不出原因，先看这张表。

---

## 怎么看有没有命中

响应的 `usage` 里三个字段：

| 字段 | 含义 |
|---|---|
| `cache_creation_input_tokens` | 这次写进缓存的（付了 1.25 或 2 倍） |
| `cache_read_input_tokens` | 这次从缓存读的（付了 0.1 倍） |
| `input_tokens` | **没命中的那部分**，全价 |

**`input_tokens` 是余数，不是总数。** 提示词总长 = 三个字段相加。跑了一整晚的 agent 只显示 4K `input_tokens`，不是它没吃 token，是剩下的都从缓存来了。

反过来，如果重复请求之间 `cache_read_input_tokens` 一直是 0，说明有东西在悄悄让缓存失效。

---

## 静默失效：前缀里改一个字节，后面全废

**缓存是前缀匹配。** 渲染顺序固定：`tools` → `system` → `messages`。前缀里任何位置变了一个字节，那个位置之后的全部作废。

常见的六个凶手，都在前缀最前面：

| 写法 | 后果 |
|---|---|
| system 里插 `datetime.now()` / `Date.now()` | 每次请求前缀都不一样，一次都不会命中 |
| 早期内容里放 UUID、请求 ID | 同上 |
| `json.dumps(d)` 没加 `sort_keys=True`，或者遍历 `set` | 序列化顺序不稳，字节就不稳 |
| 把用户 ID、会话 ID 拼进 system | 每个用户一套前缀，跨用户完全不共享 |
| `if flag: system += ...` 条件拼接 | 每种开关组合都是一份独立前缀 |
| 工具集按用户变 | 工具渲染在最前面，全员不共享 |

修法只有一个方向：**把会变的东西挪到最后，挪到最后一个断点之后。**

### 不是所有改动都全废

有个分层，改动只影响自己那层和下面：

| 改了什么 | 工具缓存 | system 缓存 | messages 缓存 |
|---|---|---|---|
| 工具定义（增删改顺序） | 废 | 废 | 废 |
| 换模型 | 废 | 废 | 废 |
| system 内容 | 保 | 废 | 废 |
| `tool_choice`、开关 thinking | 保 | 保 | 废 |

**换模型等于缓存清零**——缓存是按模型存的。这条对换中转站的人尤其要紧：换一家站就是换一个端点，之前攒的缓存一次都用不上，头几个请求全是冷写入。

---

## 两个不那么显眼的坑

**20 条回看窗口。** 每个断点最多往回找 20 个 content block。agent 循环里一轮塞进去二三十个 tool_use / tool_result 是常事，超过之后下一次的断点就找不到上一次的缓存，**静默 miss**。长轮次里每 15 条左右补一个断点。

**并发全付全价。** 缓存要等第一个响应**开始返回**之后才可读。同时发 N 个前缀相同的请求，谁也读不到还在写的那份，N 份全价。正确顺序是先发一个，等它吐出第一个 token，再发剩下的。

---

## 这跟买中转站有什么关系

三件事：

**一、逆向档大多没有缓存。** 这是它比价时最容易被漏掉的一块。假设一次请求里 80% 的输入是可缓存的重复前缀（跑 Claude Code 时这个比例不算高）：

- **有缓存**：0.8 × 0.1 + 0.2 × 1 = **0.28 倍**输入价
- **没缓存**：**1 倍**输入价

差 3.5 倍。一个名义上便宜三倍的逆向档，实际跑起来可能比官转还贵。（上面 80% 是举例用的假设，不是实测值——你自己的比例看 `usage` 三个字段现算。）

**二、比价要连缓存一起比。** 「占官方价百分比」比的是单价。如果两边的缓存支持不一样，单价一样也不等于账单一样。

**三、换站的成本不只是重新充值。** 缓存按模型和端点存，换过去头几个请求都是冷写入，付的是 1.25 或 2 倍。频繁比价换站的人，这笔钱是隐性的。

---

## 附：术语速查

| 词 | 意思 |
|---|---|
| 前缀匹配 | 缓存按提示词开头的字节序列命中，中间变一个字节，后面全废 |
| 断点（breakpoint） | `cache_control` 标记的位置，每次请求最多 4 个 |
| 冷写入 | 第一次写进缓存，付 1.25 或 2 倍 |
| TTL | 缓存存活时间，5 分钟或 1 小时两档 |
| 命中率 | `cache_read_input_tokens` 占总输入的比例 |

---

## 参考

- [Anthropic：Prompt caching 官方文档](https://platform.claude.com/docs/en/build-with-claude/prompt-caching)
- [Claude 官方定价页](https://claude.com/pricing)
- [在中国用 Claude 和 GPT 的五条路](/guides/china-claude-gpt)
- [中转站怎么掺水：七处标价与实付的落差](/guides/relay-water)
