Claude Code 开发经验深度解析:为什么提示词缓存是一切

从 prefix matching 到 cache-safe forking,系统拆解 Claude Code 背后的提示词缓存工程

Posted by iceyao on Friday, May 1, 2026

一、引言:长期运行 Agent 的成本问题,最后都会变成缓存问题

原文链接:Lessons from building Claude Code: Prompt caching is everything
作者:Thariq Shihipar(Claude Code 团队 technical staff)
发布时间:2026 年 4 月 30 日

Claude Code 这类编程 Agent 和普通聊天机器人最大的区别,不是它会调用工具,而是它会在一个会话里持续积累大量上下文:系统提示、工具定义、项目规则、CLAUDE.md、代码片段、终端输出、文件 diff、历史决策,以及用户一轮又一轮追加的新要求。

如果每次模型调用都从头计算完整 prompt,那么会话越长,系统越不可用:输入 Token 成本持续上升,首 Token 延迟变长,用户订阅计划的 rate limit 也会被更快消耗。Anthropic 在这篇文章里给出的核心结论非常直接:长期运行的 Agent 产品之所以在经济上可行,很大程度上依赖 prompt caching。

这不是一个“后期优化项”,而是 Claude Code 从 agent harness 设计之初就必须纳入的基础设施。原文甚至提到,Claude Code 团队会像监控 uptime 一样监控 prompt cache hit rate;当命中率异常下降时,会触发告警,甚至按 SEV 级别处理。

下面这张图概括了 Claude Code 中 prompt caching 所处的位置:

Claude Code Prompt Caching 架构总览

图注:本文配图均为作者基于原文机制整理的概念示意,并非 Anthropic 官方架构图。


二、技术背景:为什么 Agent 比普通 Chat 更需要 prompt caching

普通 Chat 产品通常以“短对话 + 少量上下文”为主。即使没有缓存,每次请求的输入规模也相对可控。而 Claude Code 的典型使用方式完全不同:

场景 普通聊天助手 Claude Code 这类编程 Agent
会话长度 较短,主题容易切换 长时间围绕同一代码库推进
上下文组成 用户消息和回答为主 系统提示、工具 schema、项目规则、文件内容、命令输出、diff 等
上下文增长 相对缓慢 工具调用会快速堆积大量 Token
成本结构 单次请求成本较稳定 越到后期,完整 prompt 越昂贵
延迟风险 主要来自模型推理 输入处理和缓存未命中也会显著影响延迟

对于 Agent 来说,很多上下文其实是稳定的:工具定义不会每轮都变,系统提示不会每轮都变,项目规则也不会每轮都变。真正动态增长的部分主要是 conversation messages 和工具输出。因此,prompt caching 的核心价值在于:把稳定前缀的计算结果复用起来,只为新增尾部内容付出完整处理成本。

可以把一次 Claude Code 请求想象成下面的结构:

Claude Code 请求的上下文分层与缓存友好顺序

这也是原文反复强调的布局原则:Static content first, dynamic content last. 稳定内容越靠前、越确定,多个请求之间可复用的前缀就越长,缓存收益也越大。


三、实现原理:prompt caching 本质是 prefix matching

原文给出了一个非常关键的技术事实:Claude API 的 prompt caching 依赖 prefix matching。也就是说,API 会从请求开头开始匹配,直到每个 cache_control breakpoint;只有前缀完全一致的部分,才能命中缓存。

这带来三个直接后果:

  1. 顺序比内容本身更重要:同样的工具定义,如果顺序随机变化,也会破坏前缀一致性。
  2. 前面的微小变化会放大为后面的大面积 miss:如果 system prompt 开头加入当前时间戳,后续所有内容都无法复用旧缓存。
  3. 稳定内容必须前置,动态内容必须后置:越动态的内容越应该放在 prompt 尾部。

可以用下面的流程图理解缓存匹配过程:

Prompt Cache 前缀匹配流程

一个简化的概念模型如下:

# 概念模型:真实 API 行为以官方文档为准
request = [
    stable_system_prompt,
    stable_tool_definitions,
    project_context,
    session_context,
    conversation_messages,
]

cacheable_prefix = request[:cache_control_breakpoint]

if cacheable_prefix == previous_cacheable_prefix:
    reuse_cached_computation()
    compute_only_new_suffix()
else:
    recompute_from_first_changed_token()

这也是为什么 prompt caching 不是“加一个缓存开关”这么简单。它要求整个 Agent 的上下文组织方式都围绕“前缀稳定”来设计。


四、核心优势:降低成本、降低延迟、扩大上下文可用性

Prompt caching 对 Claude Code 的价值可以概括为四类。

4.1 成本优势:长会话不再每轮从零开始

在一个 100k tokens 的长会话中,如果每轮请求都重新处理完整输入,越到后期成本越高。缓存命中后,系统可以复用前面稳定内容的计算结果,只处理新增消息、工具输出和用户指令。

对比维度 缓存命中率低 缓存命中率高
长会话输入成本 每轮接近重新处理完整上下文 大量稳定前缀可复用
订阅计划 rate limit 更容易被长会话消耗 可提供更宽松的使用额度
会话越长的边际成本 持续放大 主要由新增尾部内容决定
工程可预测性 成本波动明显 成本模型更稳定

原文明确指出,高 prompt cache hit rate 能降低成本,并帮助 Claude Code 提供更宽松的订阅计划 rate limits。这说明缓存命中率不仅是后端指标,还会直接影响产品形态。

Prompt Caching 对长会话成本曲线的影响

4.2 延迟优势:用户感知的是“是否立刻开始思考”

编程 Agent 的用户体验很大程度上取决于首 Token 延迟。如果每轮都重新吞入巨大上下文,用户会感觉 Agent “卡住了”。缓存命中可以减少重复前缀处理,让模型更快进入新增问题的推理阶段。

缓存命中与未命中的延迟对比

4.3 上下文优势:让更长的 Agent 会话变得可用

Claude Code 的价值来自持续上下文:它知道前面读过哪些文件、做过哪些修改、哪些方案被否决、哪些测试失败过。如果没有缓存,长上下文会在成本和延迟上变得不可接受;有了缓存,长会话才具备工程可行性。

4.4 可靠性优势:缓存命中率成为生产指标

原文中最有启发的一点是:Claude Code 团队会监控 prompt cache hit rate,并在命中率下降时触发告警。这意味着缓存已经从“性能优化”升级为“可靠性指标”。

对 Agent 平台来说,下面这些指标应当一起观察:

指标 代表含义 异常时的常见原因
Cache hit rate 稳定前缀是否被复用 prompt 顺序变化、工具集合变化、动态信息放在前部
Uncached input tokens 未命中部分的输入规模 compaction 方式错误、模型切换、system prompt 改动
First token latency 用户感知启动速度 大量前缀重新计算、工具 schema 过重
Cost per session 单个会话总成本 长会话 miss、fork 不复用父上下文

Prompt Cache 生产可观测性面板


五、最容易破坏缓存的三个细节

原文把 Claude Code 的缓存布局形容为“surprisingly fragile”。很多缓存事故并不来自大的架构调整,而来自看似无害的小改动。

三类最常见的 Prompt Cache Breakers

5.1 在 system prompt 中加入详细时间戳

如果 system prompt 里直接写入当前时间,例如 current_time: 2026-05-03 01:07:23,那么每次请求的前缀都会变化。由于 system prompt 位于最前面,这个变化会导致后续工具定义、项目上下文、历史消息全部无法复用旧缓存。

更好的做法是:保持 system prompt 稳定,把动态信息放到后续 message 或 tool result 中。 Claude Code 使用类似 <system-reminder> 的消息机制传递临时状态,例如时间变化、文件被用户修改等。

修改系统提示 vs 追加动态消息

5.2 非确定性地排列工具定义

工具定义是缓存前缀的一部分。如果工具列表来自 map 遍历、插件扫描或 MCP server 聚合,而排序不稳定,那么即使工具内容完全一样,也可能因为顺序变化导致缓存 miss。

工程上应当保证:

  • 工具按稳定 key 排序;
  • tool schema 序列化结果确定;
  • 插件加载顺序不影响最终 prompt;
  • 新增工具时评估对 cache hit rate 的影响。

5.3 会话中途切换模型或工具集合

原文特别强调:prompt caches are unique to models。不同模型之间不共享 prompt cache。一个反直觉例子是:如果你已经在 Opus 中进行到 100k tokens 的长会话,此时切到 Haiku 回答一个简单问题,可能并不更便宜,因为 Haiku 需要重新构建完整缓存。

同理,会话中途添加、删除或重排 tools,也是最常见的缓存破坏方式之一。直觉上,“只给模型当前需要的工具”似乎更省;但对长会话来说,动态裁剪工具集合会破坏前缀,反而更贵。


六、Claude Code 的关键设计模式

6.1 用 messages 表达状态变化,而不是修改 system prompt

Agent 运行过程中一定会遇到动态状态:当前时间变化、用户手动编辑了文件、某个命令刚刚失败、工作模式从执行切换到规划。Claude Code 的经验是,不要为了这些状态去改 system prompt,而是用消息把变化追加到上下文尾部。

这种设计背后的原则是:状态是动态的,规则是稳定的;动态状态应当出现在 prompt 尾部,稳定规则才适合放在 prompt 前缀。

6.2 Plan Mode:用工具建模状态迁移,而不是改变工具集合

Plan Mode 是原文中非常典型的案例。直觉设计是:进入 Plan Mode 后,只给模型 read-only tools,禁用编辑工具。但这会改变 tool set,从而破坏缓存。

Claude Code 的做法更缓存友好:

  1. 请求中始终保留完整工具集合;
  2. EnterPlanModeExitPlanMode 设计成工具;
  3. 进入 Plan Mode 时,通过消息告诉 Agent 当前处于规划模式,不要编辑文件;
  4. 完成计划后由 Agent 调用 ExitPlanMode

Plan Mode 用工具表达状态迁移

这个模式可以抽象为一句话:Use tools to model state transitions rather than changing the tool set. 对任何复杂 Agent 系统,这都是非常重要的架构原则。

6.3 MCP 工具很多时,用 defer_loading,而不是动态移除工具

Claude Code 可能接入 dozens of MCP tools。如果每次请求都发送所有完整 tool schema,输入会很重;但如果按需增删工具,又会破坏缓存。

原文提到的折中方案是 tool search 和 defer_loading

  • 始终发送稳定、轻量的 tool stubs;
  • stub 中保留 tool name 和必要元信息;
  • 标记 defer_loading: true
  • 当模型通过 tool search 选中某个工具时,再加载完整 schema;
  • 所有 stub 始终按确定顺序出现。

MCP 工具延迟加载与缓存稳定性

这解决的是一个典型矛盾:工具太多会增加 prompt 体积,工具动态裁剪会破坏缓存。 延迟加载让系统同时获得“工具列表稳定”和“完整 schema 按需加载”的好处。


七、Compaction:最容易掉进的成本陷阱

当上下文窗口接近上限时,Agent 通常需要 compaction:把历史对话总结成摘要,再用摘要替换原始消息,继续后续工作。

一个看似自然的实现方式是:单独发起一次 summarization call,用一个新的 system prompt,例如“请总结下面的对话”,然后把完整历史塞进去。原文指出,这正是成本陷阱:这个请求从第一个 token 开始就和父会话不同,因此无法复用父会话缓存,需要为整段历史支付 uncached input cost。

Claude Code 的解决方案是 cache-safe forking

  1. fork 请求使用与父会话完全相同的 system prompt、user context、system context 和 tool definitions;
  2. 先放入父会话已有 conversation messages;
  3. 最后追加 compaction prompt,作为新的 user message;
  4. 这样 API 看到的前缀几乎等同于父会话最后一次请求,可以复用已有缓存;
  5. 同时预留 compaction buffer,确保有空间容纳压缩指令和摘要输出。

Cache-safe Forking 时序

这条经验可以推广到所有 fork 类操作:总结、子任务、技能执行、旁路分析、审查任务,只要希望复用父会话上下文,就必须尽量共享父会话 prefix。


八、实际应用场景:不只是 Claude Code

Prompt caching 的经验并不只适用于 Claude Code。任何长期运行、上下文密集、工具密集的 Agent 系统,都可以借鉴这些模式。

Prompt Caching 的典型迁移场景

8.1 企业代码助手

企业代码助手通常会注入大量稳定上下文:安全规范、代码风格、仓库结构、构建命令、内部 SDK 文档。如果这些内容每轮都变化或顺序不稳定,缓存命中率会很差。

推荐做法:

  • 把组织级规范放在最前面,并保持版本稳定;
  • 项目级规则放在组织规则之后;
  • 用户本轮需求和工具输出放在最后;
  • 对规则文件变更建立版本号,而不是每轮注入时间戳。

8.2 多 Agent 协作系统

多 Agent 系统常见做法是让主 Agent 把完整上下文复制给子 Agent。但如果子 Agent 使用不同模型、不同工具集合、不同 system prompt,就会失去父会话缓存。

更好的方式是:

  • 主会话保持稳定;
  • 子 Agent 通过 hand-off message 接收必要摘要;
  • 简单任务用轻量 hand-off,避免复制 100k tokens;
  • 需要完整上下文的 fork 操作尽量复用父会话 prefix。

8.3 MCP 工具平台

MCP 生态的工具数量可能迅速膨胀。一个企业内部 Agent 可能同时接入 Git、CI、监控、文档、工单、数据库、云资源等几十类工具。

此时不要在每轮按“猜测需要什么工具”动态裁剪 tool set,而应:

  • 保持工具 stub 列表稳定;
  • 使用 tool search 发现能力;
  • 对完整 schema 延迟加载;
  • 对工具定义排序和序列化做确定性约束。

8.4 长文档 / 长代码库分析

对于长文档分析、代码库审查、迁移规划等任务,用户往往希望 Agent 保留上下文并持续推进。Prompt caching 可以让长上下文的使用成本更接近“增量更新”,而不是“每轮重读整本书”。


九、工程落地清单

如果要把 Claude Code 的经验迁移到自己的 Agent 产品,可以从下面这份清单开始。

Prompt Caching 工程落地五步闭环

9.1 Prompt 布局清单

  • 稳定 system prompt 放在最前面。
  • 工具定义放在动态消息之前。
  • 工具列表顺序确定,schema 序列化确定。
  • 项目级规则放在会话级动态状态之前。
  • 用户消息、工具输出、临时提醒放在尾部。
  • 不在 system prompt 中放当前时间、随机 ID、临时状态。

9.2 工具设计清单

  • 不在会话中途添加、删除或重排 tools。
  • 用工具调用表达状态迁移,例如进入 / 退出规划模式。
  • 工具过多时使用稳定 stub + 延迟加载,而不是动态裁剪。
  • 新增工具时观察 cache hit rate 和 uncached input tokens。

9.3 模型与子任务清单

  • 不在长会话中途随意切换模型。
  • 如需低成本模型处理子任务,优先使用 hand-off message。
  • fork 类请求尽量共享父会话 prefix。
  • compaction 预留 buffer,避免上下文满了才被动压缩。

9.4 可观测性清单

监控项 建议告警方向
Prompt cache hit rate 低于基线或短时间突降时告警
Uncached input tokens 单会话异常升高时排查 prompt 变化
Tool schema token 占比 工具膨胀时评估 defer loading
Compaction cost 压缩请求成本异常时检查是否 cache-safe fork
Model switch rate 长会话频繁切模型时评估是否破坏缓存

十、总结:把缓存当成 Agent 架构约束,而不是性能补丁

Claude Code 的 prompt caching 经验可以浓缩成五句话:

  1. Prompt caching 是 prefix matching:任何前缀变化都会影响后续缓存。
  2. 稳定内容前置,动态内容后置:prompt 顺序决定缓存收益上限。
  3. 用 messages 表达动态状态:不要为了临时状态修改 system prompt。
  4. 不要中途改变 tools 或 models:工具集合和模型都是缓存边界的一部分。
  5. fork 操作必须共享父会话 prefix:compaction、总结和子任务都要避免从零开始。

这篇 Anthropic 官方博客最值得借鉴的地方,不是某个具体 API 参数,而是它展示了一种 Agent 工程观:当 Agent 会话变长、工具变多、上下文变复杂,缓存命中率就会从性能指标变成产品可用性的底层约束。

对开发者而言,真正的问题不再是“要不要开启 prompt caching”,而是“你的 Agent 架构是否从第一天起就尊重缓存的工作方式”。如果答案是否定的,那么随着会话长度和工具规模增长,成本、延迟和稳定性问题迟早会一起出现。

「真诚赞赏,手留余香」

爱折腾的工程师

真诚赞赏,手留余香

使用微信扫描二维码完成支付