Claude-Code-Token-缓存机制解读
首页 / AI工具 / 文章

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),而非单轮快照。

字段官方名称含义(累计值)计费权重
Inputinput_tokens累计 首次处理 的 token(未命中缓存)。包含用户输入、工具返回结果、系统 Prompt 变动。全价 (1×)
Outputoutput_tokens累计 模型生成 的 token(回答、代码、工具调用)。全价 (通常 3~5× Input 单价)
Cache Readcache_read_input_tokens累计从缓存 读取 的 token(命中缓存的历史内容)。极低价 (约 0.1× Input,即 1 折)
Cache Writecache_creation_input_tokens累计 写入 缓存的 token(新内容首次存档)。较高价 (约 1.25× ~ 2× Input)

关键纠正点#

  1. Input ≠ 用户打字量:Agent 模式下,Input 包含大量 Tool Output(如 Read 文件内容、Grep 结果)。你打 10 个字,Input 却 8.4k,是因为后台静默工具返回了几千 token 的代码片段。
  2. 是累计值,不是单轮:每轮都在累加。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 Prompt1 Block
Tool Definition1 Block
每条 User Message1 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 > ReadSession 刚开始,写入积累快于读取正常过渡
❌ 异常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 小时 TTL5 分钟 → 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 不是一次缓存安全的操作。它的真实行为:

  1. 临时请求命中缓存:Claude Code 先发起一次临时请求(该请求本身命中缓存)来生成摘要。
  2. 摘要替换历史:用新摘要彻底替换掉现有的对话历史层。
  3. 下一轮必定 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 节)被冲刷掉时才会发生。

Write 大于 Read 的异常面板
Write 大于 Read 的异常面板

解读

  • Input: 131.4k:累计首次处理量。
  • Cache Read: 276.2k:累计复用旧历史量。
  • Cache Write: 316.5k:累计写入量。
  • 结论:系统大部分时间在”重新计算”而非”复习”。这是典型的网关缓存不工作特征。

8.2 技术原因#

  1. DashScope 隐式缓存不工作

    • Claude Code v2.x+ 自动注入 cache_control 标记。
    • 已知缺陷:DashScope 的 /apps/anthropic 端点上,隐式缓存不工作GitHub Issue #2823)。
    • 结果:网关未正确响应 Cache Read,所有历史按 Input 全价计费。
  2. 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 原生 API80-90%Input 全价的 10-20%
Qwen 网关(当前)接近 0%Input 全价(无缓存优惠)

Reddit 上有用户报告通过第三方网关使用 Claude Code 时 Token 费用异常高,原因正是缓存未生效。

8.5 建议#

  1. 验证状态:检查连续几轮 Cache Read。若一直为 0 或极低,确认缓存未工作。
  2. 权衡方案
    • 追求缓存优化 → 切换 Anthropic 原生模型(Sonnet/Opus)。
    • 继续用 Qwen → 接受 Input 全价,但 Qwen 单价低,可能仍有成本优势。
  3. 关注修复:DashScope 正在改进 Anthropic 兼容端点缓存支持。

来源GitHub Issue #2823 | Reddit 讨论 | 阿里云文档

9. 调研纠错记录#

本次调研过程充满反复验证,以下记录核心争议点及 AI 口误,供日后参考。

争议一:为什么打 10 个字,Input 却有 8.4k?#

错误猜测:System Prompt 没命中缓存?Input 包含了上一轮 Output? 正确结论Tool Output 是 Input 的大头。Agent 模式下,模型为回答 10 个字,后台可能执行 GrepRead 等静默工具,返回的代码片段(几千 token)都算 Input。

争议二:Cache Read 是否有 180k 天花板?#

错误假设:181.9k 是窗口上限,到达后截断。 真相:180k 是单轮前缀缓存上限,2.5m 是累计总量(15 轮 × 180k)。

争议三:AI 混淆”单轮”与”累计”#

在解释过程中,AI 多次用”本轮”描述累计数据,忽略这是 3.5 小时的总账本。

AI 混淆单轮与累计的表格
AI 混淆单轮与累计的表格

AI 持续使用"本轮"描述累计数据
AI 持续使用"本轮"描述累计数据

反思:解释 Token 机制时,必须坚持 “面板数字 = 累计总额” 第一性原理。任何”本轮”描述必须明确指”这一轮对累计值的贡献”。

10. 总结#

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