Claude-Code-Token-缓存机制解读
问题:Agent 每执行一步,都要从头重新阅读系统指令、工具定义、历史对话。
Claude Code Token 缓存机制解读
1. 核心概念:上下文税(Context Tax)
问题:Agent 每执行一步,都要从头重新阅读系统指令、工具定义、历史对话。
算账:20,000 token 的系统提示 × 50 轮 = 100 万 token 冗余计算,按全价计费但不产生新价值。这就是”上下文税”。
解决方法:Prompt Caching(提示词缓存)。但要用好它,必须理解其底层逻辑。
2. 面板数字:四个累计指标
Claude Code /usage 面板显示的四个数值均为 Session 累计值(Session Totals),而非单轮快照。
| 字段 | 官方名称 | 含义(累计值) | 计费权重 |
|---|---|---|---|
| Input | input_tokens | 累计 首次处理 的 token(未命中缓存)。包含用户输入、工具返回结果、系统 Prompt 变动。 | 全价 (1×) |
| Output | output_tokens | 累计 模型生成 的 token(回答、代码、工具调用)。 | 全价 (通常 3~5× Input 单价) |
| Cache Read | cache_read_input_tokens | 累计从缓存 读取 的 token(命中缓存的历史内容)。 | 极低价 (约 0.1× Input,即 1 折) |
| Cache Write | cache_creation_input_tokens | 累计 写入 缓存的 token(新内容首次存档)。 | 较高价 (约 1.25× ~ 2× Input) |
关键纠正点
- Input ≠ 用户打字量:Agent 模式下,
Input包含大量 Tool Output(如Read文件内容、Grep结果)。你打 10 个字,Input 却 8.4k,是因为后台静默工具返回了几千 token 的代码片段。 - 是累计值,不是单轮:每轮都在累加。
Cache Read的 2.5m = 15 轮 × 每轮 180k 旧历史复用。
3. 底层原理:为什么能缓存?
3.1 Transformer 的 KV Cache 机制
每个 LLM 推理请求有两个阶段:
| 阶段 | 名称 | 特点 | 缓存作用 |
|---|---|---|---|
| 阶段 1 | 预填充(Prefill) | 计算密集型,为每个 token 计算 Query、Key、Value 向量 | 生成 KV 张量 |
| 阶段 2 | 解码(Decode) | 内存受限型,逐个生成输出 token | 复用 KV 状态 |
关键洞察:Key 和 Value 向量仅取决于其之前的 token。一旦为前缀计算出 KV 张量,就永远不需要改变。
缓存的本质:基础设施存储这些 KV 张量,通过**累加级联哈希(Cumulative/Cascaded Hashing)**索引。API 内部以内容块(Blocks)为单位维护哈希链——不是对整个前缀算一个哈希,而是随着内容块流式累加。当新请求前缀匹配时,系统自动向后滚动寻找最长匹配前缀(2026 年新版 API 的 Auto-Caching 机制),KV 张量直接检索,跳过所有预填充计算。
复杂度降级:从 O(n²) 降到 O(n)。对于重复 50 轮的 20,000 token 前缀,这是巨大的缩减。
⚠️ 关键陷阱:哈希是流式累加的,这意味着一旦中间插入了任何动态内容(如随机排序的 JSON 键、每次不同的 tool output 顺序),其后的所有 KV 缓存都会雪崩式失效。这也是为什么”静态前缀必须绝对不变”——不是设计建议,是底层机制的硬约束。
什么是 API 视角下的 Block(内容块)?
Anthropic 协议中,内容是高度结构化的:
| 内容类型 | Block 计数 |
|---|---|
| System Prompt | 1 Block |
| Tool Definition | 1 Block |
| 每条 User Message | 1 Block |
| 每个独立的 Tool Use(工具调用) | 1 Block |
| 每个对应的 Tool Result(工具返回) | 1 Block |
工程含义:一轮并发跑 5 个 Bash 命令 = 10 个 Blocks,哈希链极速延长。回溯窗口上限约 20 Blocks,超出部分会被冲刷掉。
3.2 静态前缀 vs 动态尾部
智能体发送的每个请求由两部分组成:
┌─────────────────────────────────────────┐│ 静态前缀(Static Prefix) │ ← 缓存命中区,只付一次钱│ 系统指令 + 工具定义 + CLAUDE.md │├─────────────────────────────────────────┤│ 动态尾部(Dynamic Tail) │ ← 每轮新增,全价计算│ 用户消息 + 工具输出 + 对话历史 │└─────────────────────────────────────────- 静态前缀:整个会话中完全相同的部分。这是你一直在无谓重复计算的昂贵部分。
- 动态尾部:随请求变化的部分。这才是真正需要新鲜计算的内容。
提示词缓存的工作原理:存储静态前缀的数学状态(KV 张量),后续请求直接读取,跳过重新计算。你只需支付一次处理该前缀的费用。
4. 架构设计:如何构建提示词
4.1 最佳结构(从变与不变开始)
Claude Code 的请求组织方式(按变更频率排序):
| 层级 | 内容 | 变更时机 | 缓存影响 |
|---|---|---|---|
| 系统提示层 | 核心指令、工具定义、输出样式 | 切换模型、升级 CC、加载新工具 | 变更会失效整个缓存 |
| 项目上下文层 | CLAUDE.md、自动内存、无范围规则 | /clear 或 /compact 后 | 会话中期编辑不生效也不失效 |
| 对话层 | 用户消息、模型响应、工具结果 | 每个回合 | 仅前缀匹配部分命中 |
启示:如果你在开发自己的 Agent,遵循此结构:
顶部:系统指令和规则(不要在中途改变)中间:预先加载所有工具(不增不减)随后:检索上下文(会话期间保持静态)底部:对话历史和工具输出(动态增长)4.2 三层缓存架构(失效范围)
Prompt 按变更频率分层,缓存失效是分层级的:
- 系统提示层变更(切换模型、升级 CC)→ 整个缓存失效,下一轮全价重建
- 项目上下文层变更(
/compact)→ 对话层失效,系统提示层保留 - 对话层变更(每轮新消息)→ 仅新增部分按 Input 计费,前缀命中 Cache Read
5. 健康状态诊断
5.1 缓存效率公式
缓存效率得分 = cache_read_input_tokens / cache_creation_input_tokens像监控系统 uptime 一样监控它。Claude Code 实现了 92% 命中率 和 81% 成本削减。
5.2 健康 vs 异常状态对比
| 状态 | Read vs Write | 含义 | 成本 |
|---|---|---|---|
| ✅ 健康 | Read >> Write | 滚雪球效应,大部分内容从缓存读取 | 极低(1 折) |
| ️ 初期 | Write > Read | Session 刚开始,写入积累快于读取 | 正常过渡 |
| ❌ 异常 | Write > Read(长会话) | TTL 过期或前缀断裂,反复重建 | 极高(全价 × 1.25) |
5.3 异常诊断指南
当面板显示 Write > Read(长会话中)时,按以下顺序排查:
| 排查项 | 检查方法 | 处理 |
|---|---|---|
| TTL 过期 | 是否空闲 >5 分钟(API 用户) | 保持活跃,避免中断 |
| 前缀断裂 | 是否切换模型/改 CLAUDE.md/拒绝裸工具名 | 会话初期锁定配置 |
| 网关不支持 | 连续 3 轮 Cache Read ≈ 0 | 见第 8 节前线战报,考虑切换原生 API |
6. TTL 策略与缓存生命周期
| 身份验证方式 | 默认 TTL | 说明 |
|---|---|---|
| Claude 订阅 | 1 小时 | 自动开启,不额外收费。适合长时工作。 |
| API 密钥 / 第三方网关 | 5 分钟 | 你当前使用 Qwen 网关,属于此类。空闲 5 分钟即失效。 |
| 开启 1 小时 TTL | 5 分钟 → 1 小时 | 设置 ENABLE_PROMPT_CACHING_1H=1。注意:1 小时 TTL 的 Cache Write 费率更高(2× 基础价)。 |
缓存保持机制:每个命中缓存的请求都会重置 TTL 计时器。只要持续工作,缓存就保持”温暖”。长时间间隙后,下一请求重新计算完整输入并重建缓存。
7. 操作红黑榜(安全 vs 危险)
| 分类 | 操作示例 | 缓存影响 | 建议 |
|---|---|---|---|
| ✅ 安全操作 | 编辑文件 | 仅附加 <system-reminder>,前缀不变 | 放心操作 |
/recap | 附加摘要到对话末尾,不替换历史 | 放心操作 | |
/rewind(重绕) | 截断回早期回合,直接命中已缓存前缀 | 优先于 /compact | |
| 调用 Skills/Commands | 指令注入为用户消息,附加在末尾 | 放心操作 | |
| 更改权限模式 | 不改变系统提示或工具定义 | 放心操作 | |
| ❌ 危险操作 | 切换模型 (/model) | 完全失效,每个模型独立缓存 | 会话初期选定,中途不改 |
更改工作量 (/effort) | 改变缓存键,完全失效 | 同上 | |
| 开启 Fast Mode | 添加请求头改变缓存键 | 同上 | |
Compact (/compact) | 下一轮必定缓存未命中,对话层哈希完全改变,需重新全量 Cache Write | 仅在任务自然中断点使用,严禁高频迭代中期执行 | |
拒绝裸工具名 (Bash) | 移除系统提示中的工具定义 | 用作用域规则替代 Bash(rm *) | |
| 连接/断开 MCP | 若工具加载到前缀(非延迟模式),完全失效 | 检查 MCP 延迟加载配置 |
7.1 关于 /compact 的正确认知
/compact 不是一次缓存安全的操作。它的真实行为:
- 临时请求命中缓存:Claude Code 先发起一次临时请求(该请求本身命中缓存)来生成摘要。
- 摘要替换历史:用新摘要彻底替换掉现有的对话历史层。
- 下一轮必定 Cache Miss:由于对话层被砍掉重练,前缀的哈希完全改变,下一轮必须重新进行一次高昂的 Cache Write。
忠告:/compact 是一次性断骨疗伤,切忌在任务高频迭代中期执行。必须等待任务彻底结束、自然中断时才可使用。高频任务中需要压缩时,优先考虑 /recap(附加摘要到末尾,不替换历史)。
7.2 子代理缓存
- 独立构建:子代理启动自己的对话,不继承父级缓存,首轮无命中。
- 强制 TTL:子代理强制使用 5 分钟 TTL(即使主对话配置了 1 小时)。
- 父级无影响:子代理结果仅附加到父对话末尾,父级前缀保持完整。
7.3 MCP 服务器缓存细节
- 延迟工具(默认,支持模型上):服务器连接/断开/更改工具列表不破坏缓存。
- 加载到前缀的工具(Haiku、Vertex AI、自定义网关):任何变化都会使缓存失效。
- 排查:缓存频繁失效时,检查 MCP 服务器是否意外断开重连。
8. 前线战报:第三方网关缓存不生效(Qwen/DashScope)
8.1 异常面板现场
现象:某 Session 显示 Cache Write: 316.5k > Cache Read: 276.2k。
在原生 Anthropic API 下,长会话几乎不可能出现 Write > Read——正常情况是雪崩式滚雪球效应(Read >> Write)。只有在网关根本不支持缓存、或回溯窗口(Backtrack Window > 20 blocks,Block 定义见 3.1 节)被冲刷掉时才会发生。

解读:
Input: 131.4k:累计首次处理量。Cache Read: 276.2k:累计复用旧历史量。Cache Write: 316.5k:累计写入量。- 结论:系统大部分时间在”重新计算”而非”复习”。这是典型的网关缓存不工作特征。
8.2 技术原因
-
DashScope 隐式缓存不工作:
- Claude Code v2.x+ 自动注入
cache_control标记。 - 已知缺陷:DashScope 的
/apps/anthropic端点上,隐式缓存不工作(GitHub Issue #2823)。 - 结果:网关未正确响应 Cache Read,所有历史按 Input 全价计费。
- Claude Code v2.x+ 自动注入
-
Compact 加剧问题:
/compact本身就会导致下一轮缓存未命中(见 7.1 节)。- 在网关缓存不生效的前提下,Compact 后的全量重新计算雪上加霜。
8.3 验证方法
对比连续 3 轮数据:
如果缓存正常工作(Anthropic 原生 API):Round 1: Input 高 (重建), Cache Write 高Round 2: Input 降低, Cache Read 增长 ✅Round 3: Read >> Write, 健康状态
如果缓存不工作(Qwen 网关):Round 1: Input 高Round 2: Input 依然高 (无 Cache Read)Round 3: Input 依然高, Cache Read ≈ 0 ❌8.4 成本影响
| 方案 | 缓存命中率 | 实际成本 |
|---|---|---|
| Anthropic 原生 API | 80-90% | Input 全价的 10-20% |
| Qwen 网关(当前) | 接近 0% | Input 全价(无缓存优惠) |
Reddit 上有用户报告通过第三方网关使用 Claude Code 时 Token 费用异常高,原因正是缓存未生效。
8.5 建议
- 验证状态:检查连续几轮
Cache Read。若一直为 0 或极低,确认缓存未工作。 - 权衡方案:
- 追求缓存优化 → 切换 Anthropic 原生模型(Sonnet/Opus)。
- 继续用 Qwen → 接受 Input 全价,但 Qwen 单价低,可能仍有成本优势。
- 关注修复:DashScope 正在改进 Anthropic 兼容端点缓存支持。
来源:GitHub Issue #2823 | Reddit 讨论 | 阿里云文档
9. 调研纠错记录
本次调研过程充满反复验证,以下记录核心争议点及 AI 口误,供日后参考。
争议一:为什么打 10 个字,Input 却有 8.4k?
错误猜测:System Prompt 没命中缓存?Input 包含了上一轮 Output?
正确结论:Tool Output 是 Input 的大头。Agent 模式下,模型为回答 10 个字,后台可能执行 Grep、Read 等静默工具,返回的代码片段(几千 token)都算 Input。
争议二:Cache Read 是否有 180k 天花板?
错误假设:181.9k 是窗口上限,到达后截断。 真相:180k 是单轮前缀缓存上限,2.5m 是累计总量(15 轮 × 180k)。
争议三:AI 混淆”单轮”与”累计”
在解释过程中,AI 多次用”本轮”描述累计数据,忽略这是 3.5 小时的总账本。


反思:解释 Token 机制时,必须坚持 “面板数字 = 累计总额” 第一性原理。任何”本轮”描述必须明确指”这一轮对累计值的贡献”。
10. 总结
- 面板数字 = 历史账单总额(累计值),非单轮快照。
- Cost 估算 = 本地估值,第三方模型费率可能不准,以服务商后台为准。
- 核心机制 = KV Cache 前缀复用。静态前缀只付一次钱,动态尾部按轮计费。
- 缓存不是”功能”,是架构规范:提示词必须按”变与不变”分层组织。
- 最佳实践:
- 保持活跃:避免 >5 分钟中断(API 用户)。
- 锁定配置:会话初期选定模型/Effort,中途不改。
- 善用 Rewind:走错路时用
/rewind替代/compact,可复用旧缓存。 - 监控 Read/Write:健康比率
Read >> Write。异常时检查 TTL 或前缀断裂。 - 验证网关缓存:第三方网关(Qwen)当前可能不生效,定期检查确认。
- 控制并发 Tool Calls:单轮并发 10 个命令 = 20 Blocks,容易冲刷回溯窗口。尽量串行或分批执行。