Claude Code 团队经验:Prompt Caching 就是一切
.webp) 工程界有句老话:“缓存统治一切(Cache Rules Everything Around Me)”。在开发 AI 智能体(Agent)时,这句话同样适用。 像 Claude Code 这样需要长时间运行的智能体产品,之所以具有可行性,很大程度上归功于 Prompt 缓存(Prompt Caching)。它允许我们复用之前的计算结果,从而大幅降低延迟和成本。 关于 Prompt 缓存的原理和技术实现,这里不展开。在 Claude Code 团队,我们整个底层架构都是围绕 Prompt 缓存设计的。如果缓存命中率高,成本就能降下来,我们也能为订阅用户提供更宽松的使用限制。为此,我们甚至对缓存命中率设置了告警,一旦低于某个阈值,就会触发紧急故障(SEV)处理。 在优化大规模 Prompt 缓存的过程中,我们学到了很多经验。有些经验甚至有点反直觉,下面就和大家分享。 一、合理安排提示词的结构 Prompt 缓存的工作原理是“前缀匹配”(prefix matching)。API 会从请求的开头开始,一直缓存到你设置的断点。这意味着,内容的排列顺序至关重要。你希望尽可能多的请求,能够共享相同的前缀。 最好的做法是:把静态内容放在前面,动态内容放在后面。 在 Claude Code 里,我们的排列顺序是这样的: 静态系统提示词与工具定义(全局缓存) Claude.md 文件(在项目级别缓存) 会话上下文(在单个会话内缓存) 当前对话的具体消息 通过这种方式,我们能让更多的会话共享缓存命中。 但要注意,这种顺序出乎意料地脆弱!我们曾经踩过坑,破坏了这种顺序。比如:把详细的时间戳放进了静态系统提示词里、工具的排列顺序变成随机的、或者动态修改了工具的参数。这些都会导致前缀改变,缓存失效。 二、用消息来传递状态更新 有时候,你放在提示词里的信息会过期。比如,时间变了,或者用户修改了某个文件。你可能会想,那我去更新一下系统提示词吧。千万别这么做。这会导致缓存未命中,让用户付出高昂的成本。 更好的做法是:在下一轮对话中,通过“消息”来传递这些更新。 在 Claude Code 中,如果信息有更新(比如“现在是星期三了”),我们会在下一条用户消息或工具结果中插入一个 <system-reminder> 标签。这样既告诉了模型新情况,又保住了前面的缓存。 三、不要在对话中途切换模型 Prompt 缓存是和特定模型绑定的。这就导致了一个很反直觉的成本计算。 假设你正在用最强大的 Opus 模型聊天,已经积累了 10 万 token 的上下文。这时,你想问一个非常简单的问题。你可能觉得切换到便宜的 Haiku 模型会更省钱。错了。切换到 Haiku 反而更贵,因为你需要为 Haiku 重新建立那 10 万 token 的缓存。 ...