Skip to content

13.4 缓存命中与击穿的动作全表

缓存的原理见 03.5,本篇只回答一个问题:我刚才那个操作,会不会让下一轮重算全部历史。

读完你能做什么:对照全表判断任一操作是否击穿缓存,知道缓存的作用域边界在哪里,并能用两个 token 计数字段诊断命中率异常。


一、缓存键由什么构成

前缀匹配是精确匹配,但「前缀」不止是提示词文本。缓存键包含:

组成变化时的后果
提示词前缀的逐字节内容变化点之后全部重算
模型每个模型独立缓存,切换即全量重算
effort 档位同一模型下每档独立缓存
fast mode 请求头开启时整段重算一次
thinking 配置渲染进提示词,改动会失效消息层,部分模型连系统层一起失效
组织 / 工作区跨组织不共享;在 Claude API、Claude Platform on AWS、Microsoft Foundry 上跨工作区也不共享

后四项不是文本,最容易被忽略。


二、Claude Code 的三层前缀结构

Claude Code 刻意把不常变的内容排在前面:

内容什么时候变
系统提示词层核心指令、工具定义、输出风格已加载的工具定义集合变化,或 Claude Code 升级
项目上下文层CLAUDE.md、auto memory、无作用域 rules会话启动、/clear/compact
对话层你的消息、Claude 的回复、工具结果每一轮
text
[系统提示词层][项目上下文层][对话层 ······ 追加 ······>]
      ↑              ↑              ↑
   改这里 =       改这里 =       改这里 =
   全部重算      后两层重算      正常追加,不击穿

只在末尾追加,是唯一不产生额外代价的写法。 这条规则解释了本页几乎所有条目。


三、会击穿缓存的动作

动作击穿范围说明与规避
/model 切换模型全部缓存不跨模型。opusplan 设置在进出计划模式时会切换 Opus/Sonnet,所以每次切换计划模式都等于一次模型切换
/effort 改 effort 档全部会话开始后 Claude Code 会先弹确认框。改成与当前相同的档位不触发
开启 fast mode全部一次会话只付一次。从非 Opus 模型开启会同时触发模型切换
MCP server 连接 / 断开(工具已进前缀时)全部仅当 tool search 不可用、被禁用、或该 server 设了 alwaysLoad 时。stdio 进程退出、HTTP 会话过期、自动重连都会触发,且不需要你操作
启用 / 禁用带 MCP server 的插件全部只提供 skills、commands、agents、hooks 的插件不击穿
整体拒绝某个工具全部BashWebFetch 这类裸工具名的 deny 规则会把该工具从上下文移除。Bash(rm *) 这类带作用域的规则不会
Claude Code 升级全部系统提示词或工具定义变了。自动更新只在下次启动时应用,所以表现为重启后第一轮变慢
恢复升级前的会话全部历史现在挂在不同的系统提示词后面。恢复越长的会话越贵
/compact对话层设计如此。系统提示词层复用,项目上下文层从磁盘重读,若 CLAUDE.md 未变则命中
在文档 / 提示词开头插入内容插入点之后全部追加到末尾,不要插到前面

四、不会击穿缓存的动作

动作为什么安全
编辑仓库里的文件文件内容只在 Claude 读取时进入上下文。编辑不会追溯修改历史里那次读取,Claude Code 只追加一条 <system-reminder> 说明文件变了
中途编辑 CLAUDE.md项目根与用户级 CLAUDE.md 在启动时读入并驻留内存。改动既不击穿缓存,也不生效,要等 /clear/compact 或重启
中途改输出风格同上:属于系统提示词,启动时读一次,改动等下次会话生效
切换权限模式不改系统提示词与工具定义。例外是 opusplan 下的计划模式,那是模型切换
调用 Skill 和斜杠命令指令作为用户消息注入在调用位置,前面的内容没动
/recap生成摘要用于终端展示,作为命令输出追加,不替换历史
/rewind截断回到更早一轮,剩下的历史正是当时建缓存用的内容,直接命中旧条目
派出子代理子代理自己建自己的缓存。从父会话看,调用与结果都是追加
切换计划模式(非 opusplan计划模式的指令作为对话消息追加
开关 /advisor它的定义排在缓存断点之后

一条值得单独记的:/rewind/compact 便宜得多。 走错路想回退时,先想 rewind。


五、缓存的作用域边界

这一节解释「为什么我明明在做同样的事,缓存却没命中」。

Claude Code 层面:缓存实际上按「机器 + 目录」隔离。

系统提示词里嵌了工作目录、平台、shell、操作系统版本、auto memory 路径。所以:

场景是否共享缓存
同一目录下并行开的两个会话
同一仓库的两个 worktree(工作目录不同)
同一目录下先后开的两个会话仅当启动时的 git 状态快照一致(系统提示词也含分支与近期提交)
两台机器跑同一个脚本

给 Agent SDK 的批量场景,可以抑制系统提示词里的每机器段落来跨机器共享缓存,见 --exclude-dynamic-system-prompt-sections

子代理与 fork 的差别:

系统提示词首轮缓存TTL
子代理(subagent)自己的一套无命中,从零建5 分钟(即使订阅计划也是)
fork(复刻当前会话)继承父会话命中父会话缓存跟随父会话

所以需要完整上下文的一次性分叉用 fork,需要干净上下文的探索用 subagent。


六、TTL 的选择

认证方式默认 TTL覆盖方式
Claude 订阅(Pro / Max / Team / Enterprise)1 小时(自动申请)超出计划额度、开始消耗 usage credits 后自动降为 5 分钟
API key、Bedrock、Google Cloud、Microsoft Foundry、Claude Platform on AWS5 分钟ENABLE_PROMPT_CACHING_1H=1 开启 1 小时
任意方式,调试用FORCE_PROMPT_CACHING_5M=1 强制 5 分钟

命中会免费续期计时器,所以只要你在持续工作,5 分钟 TTL 也不会过期。TTL 真正影响的是离开之后回来的那一轮

关掉缓存(仅用于排查问题):

bash
DISABLE_PROMPT_CACHING=1 claude          # 全部模型
DISABLE_PROMPT_CACHING_SONNET=1 claude   # 只关 Sonnet

七、诊断:两个字段就够

每次响应都会带回这两个数:

字段含义健康信号
cache_read_input_tokens本轮从缓存读的量越大越好
cache_creation_input_tokens本轮写入缓存的量只应在新增内容的量级

读写比高 = 缓存工作正常。 如果 creation 一轮又一轮地维持高位,说明前缀里有东西在变,回第三节对照。

在 Claude Code 里最直接的观测方式是写一个状态栏脚本读 current_usage

bash
#!/bin/bash
# ~/.claude/statusline.sh —— 实时显示缓存读写比
input=$(cat)
read_tok=$(echo "$input" | jq -r '.current_usage.cache_read_input_tokens // 0')
write_tok=$(echo "$input" | jq -r '.current_usage.cache_creation_input_tokens // 0')
echo "cache r/w: ${read_tok} / ${write_tok}"

~/.claude/settings.json 里挂上:

json
{
  "statusLine": {
    "type": "command",
    "command": "~/.claude/statusline.sh"
  }
}

组织级别可以用 OpenTelemetry 导出器,它会按用户与会话上报缓存读写 token。

在 Pro / Max / Team / Enterprise 计划上,/usage 会在 cache misses 占近期用量 10% 以上时主动标出来。

API 侧还有 cache diagnostics(beta),让服务端比对相邻两次请求、直接告诉你前缀在哪里分叉了 —— 比人工二分快得多。


八、一份会话级操作纪律

按对缓存命中率的影响排序:

纪律理由
会话开头就定死模型和 effort 档中途改一次 = 全量重算一次
需要长时间离开前,先 /clear回来时反正要冷启动,不如从干净上下文开始
走错路用 /rewind,不用 /compactrewind 命中已有缓存,compact 要重建
/compact 放在任务边界,不要等自动触发自动触发点通常在任务中段,且保留内容由它判断
批量脚本里不要逐次改系统提示词把变化的内容放进用户消息
MCP 保持 tool search 默认开启server 连断不再影响前缀
CLAUDE.md 改完就重启不重启的话改动不生效,容易误判为「它不听话」

最后一条是行为问题不是成本问题,但它每周都在真实发生。


延伸阅读

基于 VitePress 构建 · 内容采用原作者授权