文章

Prefix Caching 生产实战:用 KV Cache 复用降低 TTFT 与输入成本

系统讲解 Prefix Caching 与 KV Cache 复用在大模型推理中的核心原理、工程落地五步流程、关键监控指标、五大适用场景及常见误区,并附上线检查清单,帮助团队降低首 token 延迟与重复计算成本。

背景:大模型服务的隐藏成本

大模型线上服务的成本远不止输出 token。对于 RAG、Agent、多轮对话和企业知识库问答等场景,大量请求都携带固定的长内容——系统提示词、工具定义、JSON Schema、权限规则、少样本示例、长文档片段或历史会话记录。

这些内容如果每次请求都重新送入模型,就会触发重复的 prefill 计算。prefill 阶段负责处理输入 token 并为后续解码构建 Key-Value Cache(KV Cache)。当共享前缀很长且请求频率很高时,重复 prefill 会直接推高三个核心指标:

  • TTFT(Time To First Token):首 token 延迟显著变长。
  • GPU 利用率浪费:大量算力消耗在重复前缀计算上。
  • 输入成本:托管模型 API 中,重复输入 token 可能构成可观的成本项。

Prefix Caching 的核心思路,就是将这些稳定前缀计算一次,然后在后续共享相同前缀的请求中复用已生成的 KV Cache(或托管 API 的 prompt cache),从根本上减少重复计算。

核心原理

1. Transformer 推理中的 KV Cache

自回归模型在逐 token 生成时,每一步都需要关注前文的所有历史信息。为了避免每生成一个 token 都重新计算所有历史 token 的 Key 和 Value,推理框架会将历史 token 的 key/value 张量保存下来——这就是 KV Cache

在常规请求中,模型需要先处理完整 prompt,为前缀 token 生成 KV Cache,然后才开始输出。这个处理输入 prompt 的阶段通常称为 prefill

如果两个请求的前缀完全相同:

Request A: [system prompt][tool definitions][document prefix][user question A]
Request B: [system prompt][tool definitions][document prefix][user question B]

那么 [system prompt][tool definitions][document prefix] 对应的 KV Cache 理论上可以复用。Request B 无需重新计算这部分前缀,只需从缓存中读取对应的 KV 状态,再处理新的后缀即可。

2. Prefix Caching 与普通响应缓存的本质区别

Prefix Caching 不是将最终回答缓存下来。它缓存的是模型内部推理状态——输入前缀对应的 KV Cache 或托管 API 的 prompt cache。

这意味着:

对比维度HTTP Response CachePrefix Caching
缓存对象完整回答文本模型内部 KV 状态
后缀变化无法命中,返回旧回答正常生成新答案
适用场景完全相同的请求前缀相同、后缀变化的请求

因此,Prefix Caching 最适合”前缀稳定、后缀千变万化”的请求模式。

3. 块级复用:为什么前缀必须稳定

以 vLLM 为例,Automatic Prefix Caching 会缓存已处理请求的 KV cache blocks,并在新请求到来时,根据相同前缀复用这些 block。vLLM 的设计文档指出,它会对每个 KV cache block 进行哈希,哈希内容包含:父 block hash、当前 block tokens、LoRA ID、多模态输入哈希以及 cache salt 等额外信息。

这带来一个关键的工程结论:相同语义不等于可命中缓存,必须是 token 层面的稳定前缀。如果每次请求在系统提示词前插入了时间戳、trace ID、随机 nonce、用户昵称或动态权限描述,就会破坏前缀一致性,导致 cache miss。

工程落地五步流程

第一步:拆分静态前缀与动态后缀

上线 Prefix Caching 之前,不要直接”打开开关”。先把 prompt 结构拆清楚:

[静态系统提示词] → [稳定工具定义] → [稳定输出格式说明] → [可复用长文档/检索上下文] → [动态用户问题] → [动态运行时参数]

核心原则:越稳定、越长、越频繁复用的内容越靠前;越动态、越用户相关、越一次性的内容越靠后

第二步:规范 Prompt Template

很多团队自认为有共享前缀,但实际请求无法命中缓存,根本原因往往是模板不稳定。建议落实以下规范:

  • 系统提示词版本化:如 system_prompt_v3,确保同版本内容完全一致。
  • 工具定义排序固定:不随请求随机排序,保持 JSON 数组顺序稳定。
  • JSON Schema 固定放在前缀区:不动态生成或调整字段顺序。
  • 用户 ID、trace ID、时间戳、AB 实验标签:放到后缀区或 metadata,不混入可缓存前缀。
  • RAG 场景:对可复用文档片段做稳定排序和稳定格式化输出。

第三步:启用推理框架的 Prefix Cache

如果使用自托管模型,可以从推理框架层开启缓存能力。

vLLM 的 Automatic Prefix Caching 基于 KV cache block 复用,适合共享系统提示词、多轮会话和长文档问答场景。TensorRT-LLM 的 KV cache reuse 文档同样说明,以相同 prompt 开头的多个请求可以共享和复用 KV cache pages,从而降低 first token latency。

示例配置:

vllm serve Qwen/Qwen2.5-14B-Instruct \
  --enable-prefix-caching \
  --gpu-memory-utilization 0.9

实际参数请以当前 vLLM 版本为准。上线前务必在 staging 环境确认 cache hit 指标、TTFT 和吞吐变化。

第四步:托管 API 场景使用 Prompt Caching

使用托管模型 API 时,通常无法直接访问底层 KV Cache,但可以使用平台提供的 Prompt Caching

平台关键特性
OpenAI建议将静态内容放 prompt 开头、动态内容放末尾;监控 cached_tokens、cache hit rate
Anthropic支持 automatic caching 与 explicit cache breakpoints;cache point 随会话增长自动移动

托管 API 场景下,关键不在于”有没有缓存”,而在于:

  • 前缀是否足够长,满足最小缓存 token 要求。
  • 前缀是否稳定,是否持续被复用。
  • cache TTL 是否覆盖真实请求间隔。
  • 是否能从 usage 字段中观测到 cache read / cache creation tokens。

第五步:监控命中率,而非只看平均延迟

Prefix Caching 的收益高度依赖流量结构。平均延迟下降不明显,不一定说明缓存无效——命中可能集中在某些业务路径上。

建议至少记录以下指标:

指标作用
cache_hit_rate衡量共享前缀是否真正命中
cached_input_tokens / total_input_tokens衡量输入 token 中有多少被复用
TTFT p50 / p95 / p99衡量首 token 延迟改善
prefill_time直接观察重复 prefill 是否下降
KV block eviction count判断 GPU cache 是否被频繁挤出
request queue time防止缓存收益被排队延迟抵消
cost_per_successful_task判断是否真正降低了业务成本

适用场景

1. 企业知识库问答

同一个政策文件、合同模板或产品手册被不同用户反复提问。如果文档片段稳定且较长,Prefix Caching 可以复用文档前缀,显著降低首 token 延迟。

注意:RAG 检索结果如果每次顺序不同、格式不同、片段边界不同,缓存命中率会下降。因此 RAG 系统需要稳定 chunk 排序、稳定引用格式和可复用片段策略。

2. Agent 工具调用

Agent 请求常带有大量工具定义、函数 schema、权限边界和系统规则。这些内容变化较少,非常适合作为缓存前缀。

推荐结构:

[system rules] → [tool definitions] → [output schema] → [user task] → [latest observations]

不要把最新工具结果、随机 trace 信息放到工具定义前面,这会破坏前缀稳定性。

3. 多轮会话

多轮会话天然存在共享历史。Anthropic 的 automatic caching 机制适合不断增长的多轮对话——它会随着会话推进自动移动 cache point。

自托管场景下,需注意 serving 框架通常不会自动理解”会话”概念。客户端需要将完整对话历史传入请求,底层框架再根据 token prefix 判断是否能复用。

4. 结构化输出与长 Schema

结构化输出、函数调用、复杂 JSON Schema 经常形成很长的固定前缀。OpenAI Prompt Caching 文档指出,structured output schema 可作为 system message 的前缀被缓存。这类场景适合将 schema 固定在前面,避免每次动态生成 schema 文本。

常见误区

误区一:语义相同就能命中缓存

不能。 Prefix Caching 依赖 token 级别的前缀匹配。中文标点差异、空格差异、字段顺序变化、工具定义顺序变化,都可能导致 cache miss。

误区二:打开开关就一定降成本

不一定。 缓存写入本身也有计算和内存成本。如果前缀太短、复用频率低、TTL 太短或缓存频繁被驱逐,收益可能微乎其微。

误区三:只看 cache hit rate

cache hit rate 高,但如果命中的只是很短的前缀,对 TTFT 和成本的帮助有限。需要同时关注 cached tokens 占比、prefill time 和业务路径分布。

误区四:忽略多租户隔离

KV Cache 复用涉及请求前缀。多租户系统必须保证不同租户之间不会错误复用缓存。vLLM 的设计中提到 extra hashes 可包含 cache salts,用于多租户环境下的缓存隔离。

误区五:RAG 内容越多越适合缓存

不是。 RAG 内容如果每次检索结果不同,缓存价值不高。更合理的做法是识别高频、稳定、长文档前缀,或对热门知识库入口做预热。

上线检查清单

Prompt 结构

  • 静态系统提示词放在最前面
  • 工具定义、输出格式、JSON Schema 顺序固定
  • 动态用户信息、时间戳、trace ID 不放在可缓存前缀前
  • RAG 片段排序和格式稳定
  • 不同 prompt 版本有明确版本号

推理服务

  • 已确认当前 vLLM / TensorRT-LLM / SGLang 版本支持对应缓存能力
  • 已确认 cache block size、GPU memory utilization、eviction 策略
  • 已在 staging 环境对比开启前后的 TTFT、吞吐和显存
  • 已评估大 batch、大输出长度对复用概率的影响

托管 API

  • 已确认模型是否支持 prompt caching
  • 已确认最小缓存 token 要求
  • 已确认 TTL 与真实请求间隔匹配
  • 已记录 cached_tokenscache_read_input_tokens 等 usage 字段
  • 已区分 cache write 成本与 cache read 成本

安全与治理

  • 多租户系统有 cache salt 或租户隔离策略
  • 敏感数据不会被错误放入跨用户共享前缀
  • 缓存命中不影响权限判断
  • 有缓存关闭开关和灰度策略

可落地的请求构造策略

下面是一个工程上更容易命中缓存的构造示例:

from dataclasses import dataclass

@dataclass
class PromptParts:
    system_prompt: str
    tool_schema: str
    output_schema: str
    stable_context: str
    user_question: str

def build_prompt(parts: PromptParts) -> str:
    # 稳定、长、可复用内容放在前面
    prefix = "\n\n".join([
        "<system>\n" + parts.system_prompt.strip() + "\n</system>",
        "<tools>\n" + parts.tool_schema.strip() + "\n</tools>",
        "<output_schema>\n" + parts.output_schema.strip() + "\n</output_schema>",
        "<stable_context>\n" + parts.stable_context.strip() + "\n</stable_context>",
    ])
    # 动态内容放在最后
    suffix = "<user_question>\n" + parts.user_question.strip() + "\n</user_question>"
    return prefix + "\n\n" + suffix

这个例子并非追求模板复杂度,而是强调一个核心原则:让可复用前缀尽量稳定、靠前、足够长且可观测

总结

Prefix Caching 是大模型推理优化的关键手段之一。落地成功的关键不在于技术本身有多复杂,而在于:

  1. 把 prompt 结构拆清楚——静态在前,动态在后。
  2. 模板规范做到位——token 级别的稳定,而非语义上的”差不多”。
  3. 指标体系建完整——不止看 cache hit rate,更要看 TTFT、cached tokens 占比和业务成本。
  4. 安全隔离不妥协——多租户场景必须有 cache salt 或等价隔离机制。

做好这些基础工作,Prefix Caching 才能真正从”开了开关”变成”降了成本”。

参考资料

  1. vLLM Documentation — Automatic Prefix Caching
  2. NVIDIA TensorRT-LLM Documentation — KV Cache Reuse
  3. OpenAI API Documentation — Prompt Caching
  4. Anthropic Claude Platform Docs — Prompt Caching
  5. SparseX: Efficient Segment-Level KV Cache Sharing for Interleaved LLM Serving
  6. Not All Tokens Are Worth Caching: Learning Semantic-Aware Eviction for LLM Prefix Caches
  7. LMCache: An Efficient KV Cache Layer for Enterprise-Scale LLM Inference

常见问题

Prefix Caching 适合所有大模型请求吗?
不适合。它最适合大量请求共享稳定前缀的场景,例如固定系统提示词、工具定义、长文档问答、多轮会话和模板化 Agent 调用。
Prefix Caching 会改变模型输出吗?
正确实现的 KV Cache 复用通常不改变模型语义结果,因为复用的是已经计算过的前缀注意力状态,而不是改写提示词或替换模型生成逻辑。
上线 Prefix Caching 最重要的指标是什么?
首先看 cache hit rate、TTFT、prefill tokens saved、GPU KV cache eviction、请求排队时间和成本变化,再结合业务成功率判断是否真正有效。