Prompt caching,一篇就够了
你信吗?我花了整整两年,才搞懂一件事——
Prompt Caching,根本不是什么“技术”,它是一门关于“排队”的艺术。
网上那帮人都在写什么呢?什么“KV Cache 是对注意力机制中 Key-Value 矩阵计算结果的复用”——话是没错,但你写代码的时候,脑子里会转着 Softmax 那点事吗?不会的。
去年我接了个 Agent 系统优化的活,第一天就把 Anthropic 的缓存 API 全接好了,每个 cache_control 断点标得清清楚楚。你猜结果怎么着?
命中率 12%。
12%!我当时差点以为 API 文档在耍我。
后来我花了三周,把 Codex 和 Claude Code 两套开源方案的源码硬啃了一遍,才发现一个让我后背发凉的真相——
Prompt Caching 根本不是技术问题,它是一个“排序”问题。
你不需要懂 Transformer 的位置编码,你只需要搞清楚一件事:什么东西该排前面,什么东西该放后面。
讲到这,我给你讲个故事。
Anthropic 有篇博客,标题很夸张:“Prompt Caching is everything”。我一开始觉得这不就是营销文案?直到自己踩坑踩到怀疑人生。
他们是怎么排的呢?
1. 静态的系统 prompt 和工具定义(所有人共享)
2. CLAUDE.md 文档(项目组共享)
3. 聊天的上下文(这场对话的)
4. 最新的那条消息(每轮都在变)
你发现规律没有?
越不容易变的东西,越往前放。
就这一句话,起码 80% 的生产系统没做到。
我给你举个例子。Codex 团队踩过一个大坑。他们后来接入 MCP 工具支持,工具枚举的顺序居然是不确定的。今天 list_files 排在 read_file 前面,明天可能就是 read_file 排在 list_files 前面。看起来就变了一丢丢?
整个缓存全废了。
我当时读完这个案例,后背一凉。因为我的代码里也是用 dict 来定义工具的啊!Python 3.7 之前的 dict 可不会保持插入顺序。虽然现在用的是 3.11,但谁晓得底层库有没有坑。
后来我硬是在每个请求前对工具定义做了排序,用了一个 frozenset 加排序列表的组合拳。
命中率直接从 12% 飙到了 67%。
这事跟你也有关系。检查一下:代码里有没有把时间戳塞进静态 prompt?有没有用 set 或者 dict 定义工具?如果答案是“有”——那你的缓存可能一直在当摆设。
再跟你说第二个坑,比上一个更邪门。
ChatGPT 出来之后,分了两派人。一派人迷信 OpenAI 的自动缓存,一派人迷信 Anthropic 的显式控制。
我都试过,结果发现两边都有坑。
OpenAI 的自动缓存是很省事,但问题在于——你根本不知道它什么时候命中,什么时候清掉。我做过一个测试,同一个 prompt 连续发了 15 次,有 3 次触发了 cache miss。不是前缀变了,就是系统负载高了,缓存被清掉。
你一点掌控感都没有。
Anthropic 的显式控制呢?表面上粒度很细,实际操作起来全是雷。
比如那个 cache_control 断点的位置。官方文档说“在满足最小 token 要求后写入缓存”,但就是不告诉你这个最小 token 是多少。我试过 1000 token 的 prompt 加上 cache_control,结果永远不命中。后来翻了很多资料才发现——
Anthropic 的缓存需要对前缀长度有要求,至少 1024 个 token 才会生效。
还有 TTL。默认好像是 5 分钟,你设成一个小时,还得小心缓存如果一直没人访问,会自动淘汰。
我有个朋友在创业公司做 AI 产品,用的 Claude Code 当底层引擎。有一天突然发消息说成本涨了 120 倍。120 倍!排查了半天,发现某个工具的 description 被人手贱改了一个标点符号。
一个标点符号,成本涨了 120 倍。
这事放在后端缓存领域,简直离谱。但在大模型 API 这儿,它就是日常。
所以我现在用了一个特别土但特别管用的办法:把静态内容和动态内容做成两个独立的对象。 静态内容预创建缓存,动态内容每次单独传。
Gemini 的显式缓存 API 其实做得最干脆。它允许你直接创建一个 Cache 对象,指定 TTL,然后在请求里引用它。这样一来,静态内容的缓存跟请求完全脱钩,动态内容再怎么变都影响不到它。
代码大概长这样:
cache = client.Caches.Create(
contents=[system_prompt, tools_definition],
ttl=3600
)
response = client.Models.GenerateContent(model_name, user_query, cached_content=cache.name)这个模式我用了快半年,命中率稳定在 85% 以上。
你可能会觉得:“不就缓存吗?命不命中也就多几毛钱的事。”
我跟你说,这想法会害死你。
Anthropic 内部把 Prompt Cache 命中率当成基础设施级别的指标来监控,地位跟服务器 uptime 差不多。一旦命中率下降,直接触发 oncall 告警。你要是跟他们工程师说“缓存也就多几毛钱”,人家能把你当傻子看。
为什么?因为 Agent 类产品有一个特殊性——长对话。
一个 AI 编程助手,可能在一个 session 里跟你聊几十轮。每一轮都要把之前的上下文全带上,重新发给模型。如果没有缓存,每一轮都在重复算前面的历史。计算量不是线性增长的,是近乎二次方增长的。
我粗略测算过:一个 50 轮对话的场景,无缓存时首 token 延迟大约 8 秒,有缓存呢?1.2 秒。
差了将近 7 倍。
这还是乐观的。如果你用的是 DeepSeek V4,缓存命中后的输入价格只有原来的十分之一。同样的钱,你可以跑 10 倍的请求。
你想想看,如果你的产品面向用户,等 8 秒和等 1 秒,体验差了多少?用户留存率差了多少?
所以我说,Prompt Caching 不是锦上添花的优化,它是系统能跑起来的前提。
没有缓存,就没有 Claude Code。Anthropic 自己都承认这点。
但你发现没有,很多人到现在还在当这事是“优化”。觉得“先把功能做出来再说”——结果功能做出来,成本高得做不下去了。然后不得不用更大的人力返工重写 prompt 结构、改代码架构。这种酸爽,谁经历谁知道。
还有一个隐藏的坑,极少有人提过。
我翻资料的时候,看到一篇论文(叫《An Evaluation of Prompt Caching for Long-Horizon Agentic Tasks》),讲了一个我从来没想过的问题:
缓存状态的位置编码偏移。
这技术问题我尽量说人话。意思就是——你的 prompt 里的内容,在 Transformer 眼里是有位置信息的。第 10 个 token 和第 100 个 token,它们的注意力计算是完全不一样的。
当一段缓存的注意力状态被插进不同 prompt 的不同位置时,理论上这些状态是不能直接复用的,因为它们的位置信息对不上。
论文里提了一个解法:用 Prompt Markup Language 给每个可复用模块分配唯一的 Position IDs。实验发现,“LLM 可以处理带有不连续 Position IDs 的注意力状态,只要令牌间的相对位置保持不变”。
翻译成人话:模型比你想象的宽容,但你也别太离谱。
我自己测试过:把一段系统 prompt 从开头挪到中间,内容一模一样,但输出质量确实有细微下降。这种下降在大多数业务场景里可以接受。但如果你在做代码生成、数学推理这种需要精确输出的任务,这 1% 的差异,可能导致 10% 的准确率下降。
所以我的建议是:别考验模型的宽容度。 老老实实把静态内容放开头,别动。
讲了这么多,我给你上点硬货。
这是我自己花了几个月总结出来的实战清单,你直接对着干:
第一步,分析你的 prompt 内容变化频率。
把你所有请求里的内容按变化频率分三类:
- **从不变化**:系统指令、角色定义、安全规则
- **偶尔变化**:工具定义、文档上下文、代码库索引
- **每轮都变**:用户消息、历史对话、最近输出
第二步,按稳定度排序。
从不变化的放最前面,偶尔变化的放中间,每轮都变的放最后。
这一步看上去简单,但实际操作中你可能会发现:有些你以为“从不变化”的东西,其实在悄悄变。比如时间戳、随机数、Session ID。这些玩意儿能去掉就去掉,去不掉就单独拎出来放后面。
第三步,选合适的缓存策略。
- 对于 Claude,可以用多缓存点来分离不同更新频率的内容,但要注意最小 token 的要求。
- 对于 OpenAI,信任自动机制,但要把 prompt 结构搞对。它的缓存也是需要前缀匹配的。
- 对于 Gemini,强烈建议用显式缓存 API,把静态内容做成 Cache 对象。
第四步,监控缓存命中率。
这是一个硬性指标。我习惯用 Anthropic 返回的 cache_creation_input_tokens 和 cache_read_input_tokens。如果在 60% 以下,说明你的 prompt 结构有问题。
我自己的经验:正常命中率应该在 75% 到 90% 之间。低于 60% 的,立刻排查。
第五步,定期检查你的工具定义。
这个坑太隐蔽了。你的工具定义可能被同事改过,被 MCP server 动态更新过,或者在代码重构时不小心动了顺序。
我建议在 CI/CD 流程里加一个检查:工具定义是否保持一致的顺序。
不是小题大做。这是血的教训。
跟你说句实话。
我这几年搞 AI 应用,最大的体会就是:越基础的东西,越容易被忽视。
大家一上来就研究怎么 prompt engineering、怎么 fine-tune、怎么搭建 Agent 链路。但连 Prompt Caching 都没搞明白。
这就好比什么呢?
你要装修房子,急着去买最贵的沙发和吊灯。但地基都没打牢呢。
而且你发现了没有,这事儿其实跟技术能力关系不大。它更多是你对代码的掌控力。
你能不能沉下心来分析自己的 prompt 变化频率?你愿不愿意为了 5% 的缓存命中率去重构代码?你有没有想过,一个标点符号的改动,能让成本暴涨 120 倍?
这些都不是技术问题,是你对自己代码的掌控力问题。
最后,我不怕跟你说句大实话:
Prompt Caching 这套东西,两年后可能就过时了。模型厂商会不断优化缓存策略,推出更自动化的方案。
但在那之前——你不把这套吃透,你的成本就是别人的 10 倍,延迟就是别人的 8 倍。
地基不牢,地动山摇。
你自己掂量吧。
读者评论 4