文章

Prefix Caching 生产实战:让 RAG 与 Agent 服务复用 KV Cache 的完整指南

本文系统讲解 Prefix Caching 如何复用共享提示词与上下文的 KV Cache,降低 RAG、Agent 和长文档问答场景的 TTFT、推理成本与重复预填充开销,并给出 vLLM、OpenAI API、SGLang 的工程落地清单与常见误区。

背景问题:LLM 服务的重复 prefill 正在变贵

很多团队优化大模型推理服务时,第一反应是换更快的推理框架、做量化、搞连续批处理,或者拆分 prefill 和 decode 阶段。但在真实业务里,还有一类浪费更隐蔽:大量请求反复携带完全相同的前缀内容

这些前缀可能是:

  • 固定的 system prompt 和安全规则
  • RAG 场景中重复查询的同一份长文档
  • Agent 场景中稳定的工具列表、函数 schema、工作流说明
  • 多轮对话里每一轮都会重新提交的历史消息
  • 结构化输出场景中稳定的 JSON Schema 或格式约束

Transformer 推理通常分为 prefilldecode 两个阶段。prefill 处理输入 prompt,为每一层注意力计算 Key/Value 张量,也就是 KV Cache。输入越长,prefill 越重;而 RAG、Agent、长文档问答恰好会把大量上下文塞进 prompt。若每次请求都从头处理完全相同的前缀,就会浪费 GPU 计算,并拉高 TTFT(Time To First Token)

Prefix Caching 的目标很直接:当新请求与历史请求共享相同 prompt 前缀时,直接复用已计算好的 KV Cache,跳过共享部分的 prefill 计算。

这不是”接口响应缓存”。响应缓存命中后可以直接返回旧答案;Prefix Caching 命中后,模型仍然会继续读取后续的动态输入并生成新答案。它复用的是中间计算结果,而不是最终文本。


核心原理:缓存的是前缀 KV,而不是回答

1. 什么是可复用前缀

在自回归 Transformer 中,每个 token 在 prefill 阶段会产生对应层的 Key 和 Value 张量。若两个请求的前 N 个 token 完全一致,那么这 N 个 token 对应的 KV 张量也可以复用。

一个典型 RAG 请求可以拆成两部分:

[固定系统指令 + 工具定义 + 文档上下文] + [本次用户问题]

只要左侧部分保持 token 级一致,后续请求就有机会命中缓存。vLLM 的 Automatic Prefix Caching(APC)文档明确说明:APC 会缓存已有查询的 KV Cache,当新查询与已有查询共享相同前缀时,可以复用这部分 KV Cache,跳过共享部分的计算。

2. 为什么必须强调”token 级一致”

Prefix Caching 对”看起来差不多”的文本没有天然容忍能力。多一个空格、换一种字段顺序、时间戳写在前面、工具 schema 顺序变化,都可能导致 token 序列不同,从而无法命中缓存。

因此工程落地时,关键不是简单打开一个参数,而是重构 prompt 组织方式:

静态内容(高复用)动态内容(低复用)
系统提示词用户问题
工具定义与 schema当前时间
输出格式约束会话变量
长文档上下文个性化参数
few-shot 示例trace ID

原则:静态内容稳定地放在 prompt 前部,动态内容尽量放在后部。OpenAI Prompt Caching 文档也建议把静态指令和示例放在开头,把用户特定信息放在结尾,并说明缓存命中依赖 exact prefix match。

3. Prefix Caching 优化的是 prefill,不是 decode

这一点容易被误解。vLLM 文档明确指出,APC 只减少 query processing(prefill 阶段)的时间,不减少生成新 token 的 decode 时间。

因此 Prefix Caching 在以下场景收益更明显:

  • 输入很长、输出较短
  • 同一长文档被反复问不同问题
  • 多轮会话每轮都带上较长历史
  • Agent 工具定义很大,但每轮只变少量任务参数

对输出特别长、且输入前缀不重复的场景,收益有限。


工程落地:从”打开缓存”到”设计可命中的 prompt”

1. vLLM 场景:先启用,再观测

vLLM 的启用方式很简洁,设置 enable_prefix_caching=True 即可:

from vllm import LLM, SamplingParams

llm = LLM(
    model="meta-llama/Llama-3.1-8B-Instruct",
    enable_prefix_caching=True,
)

sampling_params = SamplingParams(temperature=0.2, max_tokens=512)

shared_context = """You are a production support assistant.
Follow the company policy below...
[long static document or tool policy]"""

prompts = [
    shared_context + "\nUser question: How should I handle case A?",
    shared_context + "\nUser question: What about case B?",
]

outputs = llm.generate(prompts, sampling_params)

这只是最小示例。生产环境还需要同时观测:

  • cache hit token 数量
  • prefill latency / TTFT
  • decode latency / TPOT
  • GPU KV Cache 占用
  • cache eviction 次数
  • 不同业务、租户、模型、prompt 模板的命中率

如果只看整体 QPS,很容易把连续批处理、模型量化、请求长度变化造成的效果误判为 Prefix Caching 的收益。

2. OpenAI API 场景:让前缀稳定,并读取 cached_tokens

OpenAI Prompt Caching 是自动启用的,但并非所有 prompt 都会产生收益。官方文档说明,缓存对 ≥1024 token 的 prompt 可用,usage 字段中会返回 cached_tokens

建议在日志里保留以下字段:

{
  "model": "gpt-5.5",
  "prompt_tokens": 2006,
  "cached_tokens": 1920,
  "ttft_ms": 430,
  "total_latency_ms": 1850,
  "prompt_template_version": "support-agent-v7"
}

业务侧真正要看的不是”有没有缓存”,而是:

cached_tokens / prompt_tokens

这个比例越高,说明重复 prefill 被复用得越充分。如果比例长期接近 0,通常不是模型问题,而是 prompt 结构或路由策略导致前缀无法稳定命中。

3. SGLang / RadixAttention:把公共前缀组织成树

SGLang 论文提出了 RadixAttention,用于跨多个生成调用复用 KV Cache。它的核心思想是用类似 radix tree 的结构组织共享前缀,让多个请求的公共 token 前缀可以共享已计算的中间状态。

这个方向对复杂 LLM 程序特别有价值,因为 Agent、RAG、多分支推理、few-shot 选择、结构化输出往往不是单次调用,而是一组有关联的调用。若运行时能识别这些调用之间的共享前缀,就能大幅减少重复计算。

4. Prompt 模板要做版本化

Prefix Caching 的稳定性高度依赖 prompt 模板。如果每次发布都调整系统提示词、工具 schema 排序、字段名称或示例顺序,缓存命中率会突然下降。

建议把 prompt 模板作为可观测、可回滚的工程对象:

prompt_template:
  id: support-agent-v7
  model: llama-3.1-8b-instruct
  static_prefix_hash: sha256:...
  tool_schema_version: tools-2026-06-30
  output_schema_version: answer-json-v3
  cache_strategy: prefix-cache-first

上线后按模板版本观察 TTFT 和 cached token 占比,避免”改了一个提示词,线上延迟突然升高”的问题。


适用场景:哪些业务最值得做

场景收益机制典型指标改善
长文档问答合同/手册/保单作为固定前缀复用TTFT 下降 50-90%
多轮对话历史消息前缀稳定,后续轮次复用第 2+ 轮延迟显著降低
Agent 工具调用工具 schema、权限说明、调用约束高度重复prefill 开销趋近 0
结构化输出JSON Schema、分类标签等固定前缀结构化输出延迟下降

长文档问答

同一份合同、手册、保单、技术文档被反复查询时,长文档本身可以成为高复用前缀。vLLM 文档也将 long document query 列为 APC 的典型收益场景:同一长文档只需处理一次,未来请求复用其 KV Cache。

多轮对话

多轮会话里,新的请求通常包含之前的聊天历史。如果历史前缀保持稳定,后续轮次可以复用前面已经处理过的内容。需要注意的是,如果每轮都对历史做摘要、裁剪或重排,可能破坏前缀一致性。

Agent 工具调用

Agent 的工具定义、权限说明、调用约束、输出格式经常很长,但同一个 Agent 模板在多个请求之间高度相似。把工具 schema 稳定放在前缀位置,可以显著提升缓存命中概率。

结构化输出

JSON Schema、分类标签说明、字段约束等内容往往固定且可复用。OpenAI 文档也将 structured outputs schema 归入可缓存内容,因为 schema 会作为系统消息前缀的一部分参与缓存。


常见误区

误区一:把 Prefix Caching 当作语义缓存

Prefix Caching 不会判断两个问题语义是否相似,只看前缀 token 是否匹配。若想缓存”相似问题的答案”,那是 semantic cache 或 response cache 的范畴,风险和治理方式完全不同。

误区二:在 prompt 开头放动态字段

很多系统会把当前时间、用户 ID、trace ID、实验组、随机 nonce 放在 prompt 开头。这会直接破坏前缀复用。动态字段应尽量后置,日志字段则不应进入模型 prompt。

误区三:只打开参数,不改 prompt 编排

开启 enable_prefix_caching 只是第一步。真正决定效果的是 prompt 结构、路由策略、模板稳定性和缓存容量管理。

误区四:忽略缓存驱逐策略

GPU KV Cache 空间有限,缓存块会被驱逐。SAECache 论文指出,不同 token 类型的复用率差异很大——系统提示词、用户查询、工具输出、模型回复等 token 的缓存价值并不相同。生产系统至少要按业务类型、模板版本和缓存命中率做观测,避免高价值前缀被低价值长尾请求挤出。


上线检查清单

Prompt 结构

  • 静态系统指令是否放在最前面
  • 工具 schema 是否稳定排序
  • few-shot 示例是否稳定
  • 当前时间、用户变量、trace ID 是否后置或移出 prompt
  • RAG 文档拼接顺序是否确定
  • 模板版本是否可回滚

推理服务

  • 是否启用 prefix caching
  • 是否记录 cached token 数
  • 是否区分 prefill latency 和 decode latency
  • 是否监控 TTFT、TPOT、P95、P99
  • 是否观察 KV Cache 命中率和驱逐率
  • 是否按模型、租户、业务线、模板版本拆分指标

业务治理

  • 是否避免跨租户共享敏感上下文
  • 是否确认缓存策略符合数据保留要求
  • 是否有模板发布灰度
  • 是否在回滚时同步回滚 prompt 模板
  • 是否为低命中模板设置优化任务

参考架构:把 Prefix Caching 做成一层能力

一个较稳妥的实现方式,是在 LLM Gateway 或 Inference Router 上增加 Prompt Assembly 与 Cache Metrics 两层:

业务请求
  → Prompt Assembly:固定前缀 + 动态后缀
    → Template Versioning:记录模板版本和前缀 hash
      → Inference Router:路由到支持 prefix caching 的推理后端
        → LLM Engine:vLLM / SGLang / API Provider
          → Metrics:cached_tokens、TTFT、TPOT、eviction、hit ratio

这样做有两个好处:

  1. 业务方不用理解底层 KV Cache 机制
  2. 平台团队可以通过统一指标判断哪些 prompt 模板值得优化

FAQ

Prefix Caching 会改变模型输出吗?

正常情况下不会。它复用的是相同前缀的 KV 中间表示,后续动态输入仍参与完整推理。OpenAI 文档也说明 Prompt Caching 不影响输出 token 生成或最终响应,命中缓存与否的输出应保持一致。

Prefix Caching 和 KV Cache Quantization 有什么关系?

两者解决的问题不同。Prefix Caching 关注复用相同前缀的 KV,减少重复 prefill;KV Cache Quantization 关注压缩 KV Cache 占用,降低显存压力。二者可以组合使用,但上线时要分别观察命中率、精度、延迟和显存。

为什么缓存命中率很低?

常见原因包括:动态字段被放在 prompt 开头、工具 schema 顺序不稳定、RAG 文档排序不确定、模板频繁改版、上下文裁剪破坏历史前缀、不同租户或请求被路由到不同缓存实例。

Prefix Caching 适合所有 Agent 吗?

不适合。若 Agent 每次都动态生成工具列表、动态改写系统提示词,或每轮上下文差异极大,命中率会很低。更适合工具集稳定、流程固定、上下文较长的生产型 Agent


主要参考资料

  1. vLLM - Automatic Prefix Cachingdocs.vllm.ai
  2. OpenAI API Docs - Prompt Cachingdevelopers.openai.com
  3. SGLang: Efficient Execution of Structured Language Model ProgramsarXiv:2312.07104
  4. ChunkAttention: Efficient Self-Attention with Prefix-Aware KV Cache and Two-Phase PartitionarXiv:2402.15220
  5. Not All Tokens Are Worth Caching: Learning Semantic-Aware Eviction for LLM Prefix CachesarXiv:2605.18825
  6. LMCache: An Efficient KV Cache Layer for Enterprise-Scale LLM InferencearXiv:2510.09665
  7. LLM Query Scheduling with Prefix Reuse and Latency ConstraintsarXiv:2502.04677

常见问题

Prefix Caching 和普通响应缓存有什么区别?
Prefix Caching 复用的是模型预填充阶段产生的 KV Cache 中间张量,不是最终回答文本;即使命中缓存,模型仍会继续根据完整上下文生成新输出。响应缓存命中后可直接返回旧答案,但 Prefix Caching 命中后仍需执行 decode 阶段。
为什么 RAG 和 Agent 系统特别适合 Prefix Caching?
这类系统通常有稳定的系统提示词、工具定义、输出格式和长文档上下文,只要把静态部分放在提示词前缀,就有机会跨请求复用预填充计算,显著降低 TTFT 和 GPU 开销。
Prefix Caching 是否一定能降低所有请求延迟?
不能。它主要降低 prefill 阶段开销,对长输出的 decode 阶段帮助有限;如果请求之间没有相同前缀,或缓存频繁被驱逐,收益会明显下降。对输入短、输出极长的场景几乎无效果。