2026 年 9 月,一位长期把 AI 编程智能体作为日常工具链的工程师,在 Hacker News 上抛出了一个问题:为什么 Agent 的账单会毫无征兆地飙升,而日志里既没有报错,也没有任何告警?他给这个问题的答案是:Prompt Cache 静默击穿了。这不是单点故障,而是几乎所有主流 Agent 编程助手——Claude Code、Cursor、Aider——共同踩进的一道暗坑。一旦缓存碎
2026 年 9 月,一位长期把 AI 编程智能体作为日常工具链的工程师,在 Hacker News 上抛出了一个问题:为什么 Agent 的账单会毫无征兆地飙升,而日志里既没有报错,也没有任何告警?他给这个问题的答案是:Prompt Cache 静默击穿了。这不是单点故障,而是几乎所有主流 Agent 编程助手——Claude Code、Cursor、Aider——共同踩进的一道暗坑。一旦缓存碎裂,服务方不会报错,只是把整段对话历史按冷写入价重新计费,而不是按缓存读取价。
更让人不安的是,这种击穿在工程监控层面是无声的。没有 5xx,没有超时,没有重试,没有配额告警,账单看起来就是"正常的用量增长"。等团队月底复盘时,才会发现某几个长会话的成本陡然翻了数倍——而他们找不到任何一个具体回合作为责任边界。这正是新工具 Replay Doctor 想要填补的可观测性空白:它读取本地已经落盘的 Agent 会话日志,逐回合重放到服务方公开的缓存规则状态机里,定位到"哪一轮缓存碎了、为什么碎、漏算了多少 Token"。
为什么 Prompt Cache 一旦碎裂就是"无声的爆炸"
主流大模型厂商——Anthropic、OpenAI、DeepSeek 等——都把 Prompt Cache 作为降低长上下文成本的核心机制。Anthropic 的定价规则是:5 分钟缓存窗口的写入价为基准输入价的 1.25 倍,1 小时窗口的写入价是基准输入价的 2 倍。也就是说,缓存命中时,读取开销按远低于基准价的折扣结算;而缓存击穿时,同一段历史会被以更高一档的价格全部重写一次。OpenAI 的缓存时长不支持付费延长,但在非高峰时段默认更长,扩展缓存最长可达 24 小时。DeepSeek 则对缓存写入免费,但读取才按折扣价计算,且实际观察到的缓存时长往往超过 12 小时。
三种模式的共同点是:缓存击穿本身不会触发任何业务侧的失败信号。对调用方来说,这次请求仍然成功了,只是 Tokens 计价类目从"缓存读取"滑回了"基础输入"——而这种切换只体现在账单里,不出现在模型的响应头、不出现在 Agent 的状态机、不出现在常规的 observability 仪表盘。一个典型的 Claude Code 长会话可能横跨数十分钟的思考、多次工具调用、子 Agent 派生,中间任何一次系统前缀(system prefix)的变更、工具描述的增删、子 Agent 派生导致的请求分片,都可能让前缀哈希发生偏移,缓存瞬间作废。
Replay 是怎么把"看不见的击穿"翻出来的
Replay Doctor 是一款用 Go 写的本地命令行工具,核心思路是反编译各家厂商的缓存状态机。它不会远程调用任何接口,而是把本地已有的会话日志——Claude Code 写在 ~/.claude/projects/、Codex 写在类似路径——当作原始证据,逐回合把请求序列跑回缓存规则里,看哪一轮前缀哈希匹配不上,以及那一轮的哪一个字段发生了改变。
工具的命令行被刻意设计成"按问题反查":
- replay:跑一遍本机所有会话,算出"可避免的重复计费"总额;
- replay diff <transcript>:定位到具体哪一轮缓存碎裂、原因是什么;
- replay advise <dir>:基于本机历史给出 prompt 结构的优化建议;
- replay context <transcript>:把上下文窗口里的内容按占用比排序,告诉用户哪段是冗余的;
- replay route <dir> --to <model>:模拟切换到另一款模型会怎么样,包括目标模型的冷启动前缀成本;
- replay doctor:体检本机环境,告诉用户应该先跑哪条命令。
工具的开发者把它定义成"ccusage 的下一个问题,而不是替代品":ccusage 擅长告诉你花了多少钱,Replay 擅长告诉你是哪一轮、为什么多花了。这两个问题并不等价——加总账单是向后看的总和,而击穿定位是向前看的归因,只有后者能回答"该不该改 prompt 结构、要不要换模型、哪个工具描述造成了最大浪费"。
实测数据:本机 122 个会话,3% 是被无声重写的
Replay 文档公开了一份 2026 年 9 月 13 日凌晨 0 点 29 分(PDT)读取的本机数据:122 个会话、1957 条 Agent lane,按 2026 年 9 月 7 日的列表价、anthropic-2026-09-01 缓存规则计算:cache write $2023.72、cache read $9812.82、uncached $1.82、output $823.07、total $12661.43,中位任务 $0.79,p90 任务 $5.62,被重复计费的额度 $349.02——占总开销的 3%,绝对值是 7150 万 Tokens。这份数字被刻意写了两次,因为大多数用平价订阅档的开发者会下意识忽略美元金额,但 Tokens 是每个人都逃不开的预算线:被重复计费的 Token 就是没机会进入上下文窗口的 Token,也是被速率限制卡住的 Token。
但开发者同样在 README 里诚实标注:这份数据来自"116 个独立会话、1751 个 transcript、同一台机器、同一账号、同一个操作者"。子 Agent 会为父会话写出多个 transcript,所以文件数和会话数不能等同视之。开发者把这件事明确写进文档:这一轮样本规模只算 116,不是 1751,而"独立性的鸿沟不是靠堆自己机器上的数据能填上的"。这种自我克制,本身就是 Agent 时代对可信观测的要求——数据带出处,数字带日期,样本带边界,而不是把单一机器的账单数据扩展成行业基准。
击穿的两类成因,形状完全相反
Replay diff 把每一次缓存击穿归类到具体成因,并发现两类成因占主导地位,且它们的形状截然相反。第一类是"客户端重渲染"——历史在系统前缀之后被重建,发生频率高,但每次只重计费一小段,日积月累占总量。第二类是"TTL 到期"——两次请求之间间隔超过了缓存窗口,发生频率低,但单次就能吞掉数十分钟的整段历史。一个开发者去吃午饭的窗口,可能比一整天无数次系统前缀抖动加起来还要贵。
这一观察让"哪种击穿占比更高"这个问题变得无关紧要——两类成因的形状差异足够大,讨论比例反而模糊了机制。机制本身不会腐化,但同一个机制在不同会话上的占比会随着会话长度、并发度、子 Agent 派生模式的不同而剧烈漂移。该项目早期文档里给过一张五行的占比表,五天后就被同一台机器上的二次测量推翻了——不是因为分类器变了,而是因为会话本身在生长。
独立论文给出的两个对照基线
为了让"本机 8% 的系统 prompt 占比算高还是低"这种问题有据可查,Replay 引入了两份 2026 年发表的论文作为外部基线。TraceLab(arXiv:2606.30560)用了 4265 个会话、43 位开发者的 Claude Code 与 Codex 数据,测量前缀缓存命中率、预填充放大、前缀成本占比。Agentic Coding in the Wild(arXiv:2608.00101)用了 1350 万个会话、7.605 亿次 LLM 调用、95 万亿 Token 的真实数据,测量回合内外缓存命中率、空闲间隔衰减、提示词组成。
其中两份论文的结论,被 Replay 在自己机器上用完全不同的方法独立复现过。第一份是"Don't Break the Cache"(arXiv:2601.06007):在 Sonnet 4.5 上,排除动态工具结果可以节省 78.5% 的成本,原因是工具结果一旦回写到前缀里,就会把缓存哈希带偏;Replay blame 在本机最大会话上把工具结果和工具调用排到了第 1、第 3、第 4 位,与论文通过三家厂商 A/B 测试得到的结论方向一致。第二份是"Keeping the Cache Warm Pays"(arXiv:2607.19214):论文推导出给缓存续期的保活机制盈亏平衡点,Replay 实测下来,87% 的缓存重建开销发生在 5 分钟以内的空隙——缓存本来就没过期——$509 对应保活可覆盖的 $77 段,保活机制被证伪为"在这个问题上用错了杠杆,差距大约是 7 倍"。
为什么数字必须带出处:被自己推翻的 97.46%
这个项目最有教育意义的地方,不在于它抓出了多少击穿,而在于它公开承认自己多次被自己的数据打脸。早期 README 写"1363 个会话",其实那是文件数,独立样本只有 116 左右——把规模夸大了大约 20 倍。后来又写成"97.46% 跨 1450 个 transcript",这份数字在文档里滞留了四天,而语料和引擎都已经变了,等于把"四天前的快照"当成"今天的事实"在分发。
更典型的是缓存引擎复现准确率的拆分:之前文档里写的"97.79% 匹配率"实际把两种情形合在一起——引擎完全复现的部分,以及服务方给了比引擎预测更多的缓存读取(往往是因为并发 lane 扩展了缓存)。后者其实是预测偏向了慷慨的方向,把所有情形加总起来报一个匹配率,会让人以为模型稳定,实际上在 2026 年 9 月 11 日的二次测量里,94.10% 是完全匹配、3.78% 是多读了、2.12% 是真的碎了,三者加起来 97.89%,但语义完全不同。开发者把这次拆分明确写进了每一处出现匹配率的地方。
另一处更刺眼的纠错,发生在子 Agent 并发场景。早期版本在测量一个并行派生子 Agent 的会话时,报告 98.8% 的重复计费来自同一个成因——但这个数字是错的。原因是引擎在做对比时,只用了会话级的一个 slot,而并行 lane 之间相互覆盖写入,每个 lane 都被比成了"最后一个写入的兄弟",导致 34 个事件中有 31 个被错误归因。逐 lane 重新测量后,真实数字是 4.2%,与原值相差 23 倍。开发者把这两个数字连同撤稿说明一起留在文档里,并明示"错的那个仍然在那里",不允许通过删改假装它不存在。
扁平订阅档下的成本归属:开放问题,不是已知结论
对按调用计费的开发者,缓存击穿直接变成美元;但对按月订阅、靠速率限制挡量的扁平档用户,击穿是否会同步消耗订阅配额?这一点官方没有公开说明。Replay 在 3.09M Token 的冷写 vs 暖读对照实验里,观察到订阅档的利用率计数器走了零步——也就是说,在这个实验条件下,缓存击穿没有消耗配额。但开发者把这件事写成"空结果",而不是"已经验证不会消耗",因为一次实验不能下普适结论。
这件事本身对 Agent 时代的成本治理有更深的意义:不是所有可观测的成本都会出现在账单上,也不是所有出现在账单上的成本都会被观测到。一份合格的 Agent 成本审计工具,需要同时回答"多少 Tokens 被重写"和"这部分重写是否同时消耗了订阅配额"——而后者目前只能由各厂商在文档里披露,工具侧只能做有限度的实验。Replay 把这个未解问题写进路线图,而不是当成已知结论,这是项目自我克制的一面。
成本、上下文与决策:把数据读懂的三道关
把 Replay 这套思路放到企业级 Agent 落地的语境里,会看到三个层面的信号。第一个层面是成本归因:按 Tokens 或美元计费的支出不再只是月末汇总,而是能定位到"哪一轮多花了"。第二个层面是上下文质量:上下文窗口的占用可以按内容排序,告诉业务方哪些是必要的、哪些是冗余的、哪些是因为工具结果回写导致缓存碎裂。第三个层面是决策杠杆:要不要换模型、要不要重写 system prompt、要不要给工具描述做版本管理、要不要限制子 Agent 并发数,这些决策都有可量化的依据,而不是凭直觉。
这套思路对企业内部 Agent 治理的启示是:任何把 Agent 用于生产链路的企业,都应该在三个层面建立可观测性——成本侧能定位到回合级,内容侧能定位到字段级,决策侧能定位到改动前后差异。否则就会出现一个反复发生的场景:Agent 账单在增长,但团队找不到任何一个回合作为责任边界,只能默认"贵是正常的",把问题推给"模型降价"。这种默认会同时遮蔽三个真相——缓存击穿、上下文膨胀、模型替换本身的冷启动成本。
可信工具的边界:不报不能报的数字
Replay 文档里有一句贯穿全文的设计原则:这个工具不能测量的数字,就不打印,会在数字本应出现的位置说明为什么。例如 replay route 在从未在真实线路上见过的模型对上不会给美元数字;replay context 在会话被压缩后会告知自身的回答不完整;每一个数字都附带测量时间和样本边界。这种"主动拒绝"看起来牺牲了首次使用的体验,实际上换来了可被信任的资格。
这套设计哲学对 Agent 时代的可观测性工具有普遍参考价值:大多数可观测性工具的诱惑是先报数字,再补注脚;而生产链路上能用得起的工具,需要反过来——先报可以报的,不能报的就在那个位置留下空白和原因。当一个数字没有出处、没有日期、没有样本边界,那它就是不安全的;当一个工具愿意主动放弃"先给一个答案"的诱惑,它才有资格进入生产链路的决策面板。
自我修正的工程文化:把打脸留在文档里
Replay 项目最值得记住的实践,是它从不抹除自己的错误结论——97.46% 那个夸大的数字、98.8% 那个误归因的成因、第一版里"两个网络请求"而漏掉 upgrade 的描述,这些错都被原样保留在文档里,旁边附上撤稿说明和正确数字。开发者把这种做法明确写进了贡献规范:每一个数字带出处,每一次纠错以新日期文件的形式保留,旧的快照也保留。
这种自我修正的工程文化,是 Agent 时代对工具可信度的最低要求。当一个工具自己都不愿意承认自己曾经错过的样子,它输出的数字就不可能被任何外部审计信任;当一个工具愿意把自己的错误公开陈列在主页上,它的数字反而更有可信度,因为读者已经看到了所有可能出错的环节。Agent 时代最稀缺的资源不是算力、不是模型,也不是缓存——是工具和它使用者之间那条以自我修正为锚的可信链。