用 Claude Code 一段时间后我发现,同样的工作量,token 账单能差好几倍——差别几乎全在 Prompt Cache 有没有用对。官方文档把"有这个功能"讲了,但"怎么才不会踩坑"基本没说。这篇把我踩过的坑和读源码/官方博客理解的机制整理一下。
纯技术,不涉及任何平台推荐,就事论事。
一、为什么 Claude Code 对缓存特别敏感
普通 API 调用输入输出大致 1:1 ,prompt 短,缓存收益有限。
Claude Code 反过来——单次请求的输入 token 远大于输出。一次"改个 bug"的对话,输出可能就 200 token ,但输入要带:
- 系统提示词(约 3000 token ,含全部工具定义)
- CLAUDE.md (项目指令,几百到几千 token )
- 历史对话(几千到几万 token )
- 当前文件内容
典型一次请求:输入 15K ,输出 500 ,输入是输出的 30 倍。 这种结构下,输入 token 的单价几乎决定总账单。而 Prompt Cache 对命中部分给的是 90% 折扣(只收 10%),不是小优化,是数量级差异。
二、工作原理:前缀匹配,不是"重复内容打折"
很多人以为是"系统检测到重复内容就打折",这是错的。
真实机制:服务端保存你最近发送的 prompt 前缀。下次请求只要前缀完全一致(一字不差),就从缓存读取、跳过重算。
关键点:前缀任何一处变化,后面所有内容的缓存全部失效。
请求 1:[系统提示][工具定义][CLAUDE.md][对话 1-5][新消息]
请求 2:[系统提示][工具定义][CLAUDE.md][对话 1-6][新消息]
前面完全相同 → 命中,只有"对话 6+新消息"按全价
但如果你在请求 2 里给 CLAUDE.md 偷偷加了一行,从 CLAUDE.md 往后全部失效,包括之前已经缓存的对话历史。
一个反直觉的设计细节
CLAUDE.md 不是拼在 system prompt 里发送的,而是通过 <system-reminder> 标签注入到 messages 数组。为什么?因为同版本 Claude Code 的 system prompt 在所有用户之间字字相同,服务端可以做全局共享缓存;如果把每个人不同的 CLAUDE.md 拼进 system prompt ,这个共享缓存就没了。拆开放,既保住全局共享,又让你的 CLAUDE.md 独立缓存。
三、5min vs 1h 两档,怎么选
| 档位 | 写入价格 | 读取价格 | 回本条件 |
|---|---|---|---|
| 5 分钟 | 1.25× 基础输入价 | 0.1× | 1 次命中即回本 |
| 1 小时 | 2× 基础输入价 | 0.1× | 2 次命中回本 |
- 高频连续工作(<5min 一次请求)→ 5min 档
- 任务间隔长、跨会议/午餐 → 1h 档
Claude Code 内部对 system prompt + 工具定义默认 5min (高频复用),用户上下文按 session 长度自动选档。
四、5 个让缓存失效的坑(最实用的部分)
坑 1:中途修改 CLAUDE.md
最常见。几轮对话后随手给 CLAUDE.md 加条规则——从 CLAUDE.md 往后全部失效,之前几轮的输入全按全价重算。
对策:session 开始前配好,开始后只读不改,要改先 /new。
坑 2:prompt 里塞动态内容
"当前时间 2026-07-21 15:23:45 ,请…"
时间戳、随机 ID 、UUID 只要进了前缀,缓存命中率直接归零(每次都不一样)。 对策:动态信息放最后一条用户消息里,别混进 system 或前置。
坑 3:中途切模型
不同模型缓存隔离。opus 切 sonnet ,之前的缓存直接作废。
对策:同任务保持模型一致,要切先 /new。
坑 4:/compact 的隐藏成本
/compact 的总结请求用的是专门的总结 system prompt 、且不带工具定义,前缀从第一个 token 就和日常缓存不同,整个对话历史按全价计费一次。
对策:别攒到几十轮才 compact ;做完一个子任务就 compact 一次(被全价的内容少);只想清空的话 /new 更划算。
坑 5:/resume 破坏缓存
--resume / /resume 在多个版本存在缓存失效——恢复后前几轮全价,可能 10–20 倍成本暴增。原因是序列化/反序列化后 messages 结构有微小差异,服务端当成新前缀。
对策:长任务尽量一个连续 session 做完;不得不断,宁可新 session 简短复述,也别 resume 。
五、怎么确认缓存真的命中了
看响应里的 usage 字段:
{
"usage": {
"input_tokens": 245,
"cache_creation_input_tokens": 3120,
"cache_read_input_tokens": 8450,
"output_tokens": 412
}
}
cache_creation_input_tokens:本次写入缓存( 1.25× 或 2×)cache_read_input_tokens:本次命中( 0.1×)- 连续 session 第 N 轮( N>1 ),
cache_read应远大于input_tokens - 如果
cache_read一直是 0 ,前缀肯定哪里被破坏了,照上面 5 个坑排查
六、三条核心原则
- 保持前缀稳定——别中途改 CLAUDE.md 、加时间戳、切模型
- 同任务一气呵成——长 session 比频繁 resume 划算
- /compact 早用别攒——越晚 compact 被全价的内容越多
CLAUDE.md 我个人保持在 50 行以内,当"索引"用(列关键文件路径、命令、约定),详细文档放 docs/ 让 Claude Code 自己 Read——既省 token 又不容易失效。
以上是我自己的实践,有不同经验欢迎评论区交流。
基于 Claude Code 1.x 与 Anthropic 官方文档,如有错误欢迎指正。