主题
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 的插件不击穿 |
| 整体拒绝某个工具 | 全部 | 加 Bash、WebFetch 这类裸工具名的 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 AWS | 5 分钟 | 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,不用 /compact | rewind 命中已有缓存,compact 要重建 |
/compact 放在任务边界,不要等自动触发 | 自动触发点通常在任务中段,且保留内容由它判断 |
| 批量脚本里不要逐次改系统提示词 | 把变化的内容放进用户消息 |
| MCP 保持 tool search 默认开启 | server 连断不再影响前缀 |
CLAUDE.md 改完就重启 | 不重启的话改动不生效,容易误判为「它不听话」 |
最后一条是行为问题不是成本问题,但它每周都在真实发生。