文章

Prompt Caching 实战:长上下文 Agent 的成本与延迟治理

深入解析 Prompt Caching 的核心原理与工程实践,从前缀稳定性、缓存边界到 Agent 场景落地,提供可复现的优化方案与生产检查清单,帮助团队在不改变模型语义的前提下显著降低输入成本与首字延迟。

背景:长上下文不是免费午餐

长上下文让大模型应用从”单轮问答”走向”带资料、带工具、带记忆的 Agent”。一个真实的业务 Agent 往往会把系统提示词、工具说明、权限边界、业务术语、历史摘要、检索结果和用户问题一起塞进上下文。这提升了可用性,也带来了两个直接问题:输入 token 成本高,以及 TTFT(Time To First Token,首字延迟)变长

很多团队会先想到压缩 prompt、减少工具描述、做上下文裁剪。这些都有效,但还有一个常被低估的优化点:Prompt Caching。它不是”缓存最终回答”,而是让模型服务端复用相同 prompt 前缀的预处理结果。对于多轮 Agent、长文档问答、固定工具集调用、代码仓库分析等场景,它可以在不改变模型输出语义的前提下,降低重复输入带来的成本和延迟。

不过,Prompt Caching 不是开关式优化。真正上线时,经常出现三类反直觉现象:

  • 明明供应商说自动缓存,但账单里 cached_tokens 很少;
  • 本地测试有收益,上生产后命中率波动很大;
  • 全量上下文缓存看起来最简单,实际反而可能增加延迟。

问题不在”有没有缓存”,而在 prompt 前缀是否稳定、缓存边界是否正确、动态内容是否污染了可复用部分

核心原理:Prompt Caching 复用的是稳定前缀

从工程角度看,Prompt Caching 的核心是 prefix reuse。模型处理输入时,需要对 prompt token 做前向计算,并产生可复用的中间状态。供应商或推理框架可以在后续请求遇到相同前缀时跳过部分重复计算,从而减少 prefill 成本。

1. 精确前缀匹配

多数 Prompt Caching 都依赖”前缀一致”。也就是说,缓存命中的关键不是”语义相似”,而是请求开头的一段内容在 token 层面保持一致。只要前缀中插入了一个变化字段——时间戳、UUID、临时工具结果、用户姓名、随机排序的 JSON 字段——就可能破坏命中。

因此,Prompt Caching 的第一条工程原则是:静态内容靠前,动态内容靠后

推荐顺序如下:

稳定系统指令 → 稳定安全边界 → 稳定工具定义 → 稳定业务术语
→ 稳定示例 → 可复用长文档/知识背景
--- 缓存边界 ---
→ 用户本轮输入 → 检索结果 → 工具调用结果 → 临时状态 → 输出格式补充要求

不推荐这样组织:

当前时间:2026-06-29 10:01:19 | 用户 ID:xxx | 本轮请求 ID:uuid
→ 稳定系统指令 → 稳定工具定义 → 长文档背景 → 用户问题

第二种写法把高频变化字段放到了最前面,会让后面的稳定内容无法形成稳定前缀。

2. 自动缓存与显式缓存边界

不同供应商的缓存能力差异明显:

供应商缓存方式特点
OpenAI自动缓存近期模型自动启用;请求达到一定长度后,开头前缀匹配即产生缓存收益;强调 exact prefix match
Anthropic Claude自动 + 显式 cache_control支持 automatic caching 和 explicit cache breakpoints;适合多轮对话、长背景、多示例任务
Google Gemini隐式 + 显式 context cachingimplicit caching 默认启用;同时提供 context caching API 创建可复用的缓存对象

结论是:不要把 Prompt Caching 当成某个供应商的单一功能。更稳妥的做法是从应用层定义一套 缓存友好的上下文结构,再根据不同模型供应商适配自动缓存、显式缓存边界或 context cache 对象。

3. Prompt Caching 不是 Semantic Cache

Prompt Caching 与常见的缓存层容易混淆,下表给出区分:

类型缓存对象命中条件适用场景风险
Prompt Caching相同前缀的预处理结果token 级前缀一致长系统提示词、工具定义、长文档、多轮 Agent前缀被动态内容破坏
Response Cache完整模型回答请求完全相同或规范化后相同FAQ、固定查询、低变化问答过期答案、权限泄露
Semantic Cache语义相似问题的结果向量相似或语义判定客服、知识库问答错误复用、语义漂移
Retrieval Cache检索结果query 或改写 query 一致RAG、多轮搜索文档更新滞后

Prompt Caching 的价值在于:即使用户每次问题不同,只要前面的系统提示词、工具定义和长背景一致,也可能复用大量输入处理成本。

工程落地:把上下文拆成”稳定区”和”动态区”

1. 建立上下文分层

一个适合 Prompt Caching 的 Agent prompt 可以拆成五层:

第一层:Kernel Prompt。包括角色、边界、安全规则、输出质量要求,通常长时间不变。

第二层:Tool Schema。包括工具列表、参数 schema、调用约束、错误处理规则。工具变更不频繁,但一旦工具顺序或描述动态变化,就会影响缓存命中。

第三层:Domain Pack。包括业务术语、产品规则、固定知识、代码仓库说明、接口规范等。它可能按任务类型选择加载,但在同一类任务中应保持稳定。

第四层:Session Summary。对历史对话的压缩摘要。比前几层更动态,是否放入缓存区要谨慎——如果摘要每轮改写,会破坏后续匹配。

第五层:Turn Context。用户本轮问题、检索结果、工具结果、错误栈、当前时间、请求 ID、临时变量。应放在最后,通常不作为缓存重点。

示例结构:

<stable_prefix>
  <kernel_prompt>
    你是一个企业级任务执行 Agent。
    你必须遵守权限、审计、输出格式和错误处理要求。
  </kernel_prompt>
  <tool_definitions>
    工具列表固定排序。
    参数 schema 固定格式。
    不在这里插入本轮工具结果。
  </tool_definitions>
  <domain_pack>
    固定业务规则。
    固定术语表。
    固定示例。
  </domain_pack>
</stable_prefix>

<dynamic_context>
  <user_request>用户本轮输入。</user_request>
  <retrieval_results>本轮检索结果。</retrieval_results>
  <tool_observations>本轮工具调用结果。</tool_observations>
</dynamic_context>

2. 避免工具结果污染缓存前缀

Agent 系统最容易破坏 Prompt Cache 的地方是工具调用。工具返回结果通常高度动态:网页搜索结果会变化,数据库结果会变化,执行日志会变化,时间戳会变化。如果把这些内容插入到系统提示词或工具定义之前,缓存基本失效。

正确方式是把工具定义和工具结果分开:

# Stable
你可以使用以下工具:
- search_web(query)
- read_file(path)
- run_test(command)

# Dynamic
本轮工具执行结果:
...

同时要避免每轮动态重排工具列表。即使工具语义没有变化,字段顺序、JSON 格式化、描述模板的随机变化,也可能造成缓存 miss。

3. 不要在前缀中放”看似无害”的动态字段

很多缓存命中率低的问题来自工程细节。例如:

{
  "request_id": "a8f9...",
  "timestamp": "2026-06-29T10:01:19-04:00",
  "user": "u123",
  "instructions": "..."
}

这段结构里,request_idtimestamp 放在最前面,会让后面的 instructions 无法形成稳定前缀。更好的做法是:

{
  "instructions": "...",
  "tools": [...],
  "domain_context": "...",
  "runtime": {
    "request_id": "a8f9...",
    "timestamp": "2026-06-29T10:01:19-04:00",
    "user": "u123"
  }
}

进一步优化时,可以把 runtime 放到完整 prompt 的最后,而不是仅在 JSON 内部后置。

4. 让模板渲染可复现

Prompt Caching 需要稳定输入,因此模板渲染必须可复现。上线前建议检查:

  • JSON 字段顺序是否稳定;
  • 工具列表顺序是否稳定;
  • 示例顺序是否稳定;
  • 多语言文案是否会自动切换;
  • 是否存在随机空格、随机换行、随机分隔符;
  • 是否每轮都重新生成”会话摘要”;
  • 是否把实时日期写进了系统提示词;
  • 是否把用户变量插进了固定模板开头。

这些问题不影响功能正确性,却会明显影响缓存收益。

适用场景:哪里最值得做 Prompt Caching

场景一:长系统提示词 Agent

如果 Agent 的系统提示词包含大量规则、工具说明、错误处理策略和输出格式,Prompt Caching 往往收益明显。尤其是同一个 Agent 在短时间内处理多轮请求时,稳定前缀会重复出现。

典型场景:编程 Agent、数据分析 Agent、深度研究 Agent、工单处理 Agent、合规审查 Agent。

场景二:固定文档上的多轮问答

用户围绕同一份长文档连续提问时,可以把文档内容或文档摘要放入稳定区,再把每轮问题放到后面。如果供应商支持显式 context caching,也可以把长文档创建成 cache object。

适合:合同审查、论文阅读、代码仓库分析、产品手册问答、政策文件解读。

场景三:少量固定工具、大量相似任务

很多企业应用的工具集合稳定——例如查询客户、查询订单、创建工单、更新状态。工具 schema 很长,但每轮调用只变化用户输入和工具结果。这类场景适合把工具定义放在缓存前缀中。

场景四:批量处理同类任务

如果你要处理大量同类文档或同类数据——例如批量抽取、批量分类、批量生成 SEO 文章——固定指令和固定输出 schema 可以缓存,单个样本放到后面。

常见误区

误区一:只要供应商自动缓存,就不用管 prompt 结构

自动缓存只能在”有稳定前缀”的前提下生效。如果应用层每次把动态字段放在 prompt 开头,自动缓存也无法稳定命中。

误区二:缓存越多越好

长上下文 Agent 中,全量上下文并不总是最佳缓存对象。工具结果、搜索结果和观察日志变化频繁,缓存这些内容可能造成大量 cache write,却很少产生 cache read。排除动态工具结果、控制缓存边界,通常比朴素 full-context caching 更稳定。

误区三:Prompt Caching 可以替代上下文压缩

Prompt Caching 降低重复前缀的处理成本,但不会解决上下文无限增长问题。历史对话、工具结果、检索片段仍然需要裁剪、摘要和分层管理。

误区四:只看总 token,不看 cached token

上线后必须观察缓存命中。只看 prompt_tokens 和总费用,很难判断优化是否生效。应重点记录:

  • 输入 token 量
  • cached token 量
  • cache hit ratio
  • TTFT(P50/P95/P99)
  • 总延迟
  • cache write 与 cache read 的比例
  • 不同任务类型的命中率

误区五:忽略安全边界

Prompt Cache 涉及请求内容的服务端复用。生产环境要确认供应商的隔离策略、组织级边界、数据保留策略、ZDR 支持情况,以及网关层是否可能引入共享凭证导致的隔离问题。对于高敏感业务,Prompt Caching 应纳入安全评审,而不是仅作为成本优化项。

上线检查清单

Prompt 结构

  • 稳定系统提示词是否固定在最前面
  • 工具定义是否固定排序
  • 示例是否固定排序
  • 动态变量是否全部后置
  • 检索结果和工具结果是否避免插入缓存前缀
  • 长文档是否按任务类型形成可复用块

模板稳定性

  • JSON 序列化是否稳定
  • 是否存在随机 UUID、时间戳、trace id 出现在前缀
  • 是否有 A/B prompt 实验导致前缀频繁变化
  • 是否每次请求都重写系统提示词
  • 是否有自动摘要污染缓存区

观测指标

  • cached_tokens 或供应商等价字段
  • cache hit ratio
  • TTFT P50/P95/P99
  • 输入 token 成本
  • cache write/read 比例
  • 按任务类型拆分的命中率
  • 不同模型供应商的缓存差异

安全与合规

  • 缓存是否按组织、项目或订阅隔离
  • 是否支持 ZDR 或等价数据保留策略
  • 网关服务是否会共享组织凭证
  • 是否禁止缓存高敏感内容
  • 日志中是否记录了必要但不过度暴露的缓存观测字段

结论:缓存收益来自结构,而不是开关

Prompt Caching 的关键不是”供应商有没有这个功能”,而是应用能否持续生成稳定、可复用、边界清晰的 prompt 前缀。对于长上下文 Agent,最有效的策略通常不是缓存全部上下文,而是把系统提示词、工具定义、业务规则和固定材料放在前面,把用户输入、工具结果、检索片段、时间戳和临时状态放在后面。

生产环境要用指标说话:看 cached_token、TTFT、cache hit ratio、cache write/read 比例和任务类型拆分。如果缓存命中率低,不要先换模型——先检查 prompt 模板是否稳定。

Prompt Caching 不是玄学,也不是万能药。它是上下文工程的一部分。结构正确时,它能显著降低长上下文应用的重复成本;结构错误时,它只会成为账单里一个看不见收益的功能名词。

参考资料

常见问题

Prompt Caching 和普通结果缓存有什么区别?
Prompt Caching 复用的是模型对相同输入前缀的预处理结果,通常降低输入 token 成本和首字延迟;结果缓存复用的是完整回答,适合重复问题。
为什么 Agent 系统更容易浪费 Prompt Cache?
Agent 每轮会追加工具调用、观察结果、时间戳和临时状态,如果这些动态内容插入到前缀中,就会破坏精确前缀匹配,导致缓存全部失效。
生产环境应该先缓存什么?
优先缓存稳定系统提示词、工具定义、长文档背景和少量固定示例,把用户输入、工具结果、时间戳等动态内容放到后面。上线后重点观察 cached token、cache hit ratio 和 TTFT。
为什么我用了长 prompt,cached token 还是 0?
常见原因:请求长度未达到供应商阈值、前缀不完全一致、动态字段放在开头、请求间隔超过缓存生命周期、路由没有命中同一缓存位置、模型或接口不支持对应缓存能力。