Prompt Caching
提示缓存会复用 LLM 针对重复提示前缀计算出的键值状态,因此后续请求无需重复计算,可降低成本。
提示缓存也称前缀缓存,它会存储大型语言模型(LLM)对提示开头部分计算出的中间结果,并在后续请求以完全相同的文本开头时重复使用。许多请求共享一个很长且不变的前缀,例如系统提示、工具定义、一组示例、长文档或截至当前的对话记录,后面再接一小段新内容。命中缓存后,服务器只需处理新的词元,从而缩短首个输出词元的生成时间;对于托管 API,缓存输入的费用也会降至常规费率的一小部分。
提示缓存的工作原理#
Transformer LLM 分两个阶段生成回答。在预填充阶段,每一层都会为每个提示词元计算一个键向量和一个值向量;在解码阶段,每个新词元都会通过注意力机制关注所有这些键和值。KV 缓存会在单次请求期间保存这些张量,因此每个生成的词元都不必重新计算提示内容。提示缓存则会在多个请求之间保留这些张量。由于这些模型中的注意力是因果的,词元的键和值只取决于它之前的词元,因此具有相同前缀的两个提示在该前缀部分拥有相同的键和值,已存储的副本可以代替重新计算。
以下示例展示了如何使用 Hugging Face Transformers 中的 GPT-2 重用缓存。示例只对 409 个词元的系统提示进行一次预填充,然后将两个问题各自的词元接在缓存前缀上运行并作答,最后将结果与完整重新计算进行比较:
import copy
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained("gpt2")
model = AutoModelForCausalLM.from_pretrained("gpt2")
# A long system prompt shared by every request, followed by different user questions
system = "You answer questions about a camera store. " + "Returns are accepted within 30 days with a receipt. " * 40
questions = [" Question: Can I return a lens?", " Question: Do you ship abroad?"]
prefix_ids = tokenizer(system, return_tensors="pt").input_ids
with torch.no_grad():
# Prefill the shared prefix once and keep its key-value cache
prefix_cache = model(prefix_ids).past_key_values
for question in questions:
ids = tokenizer(question, return_tensors="pt").input_ids
# Cache hit: only the question's tokens run through the model
cached = model(ids, past_key_values=copy.deepcopy(prefix_cache)).logits[0, -1]
# No cache: the whole prompt is computed from scratch
full = model(torch.cat([prefix_ids, ids], dim=1)).logits[0, -1]
same = torch.allclose(cached, full, atol=1e-3)
print(f"Computed {ids.shape[1]} of {prefix_ids.shape[1] + ids.shape[1]} tokens, same next-token scores: {same}")程序会打印 Computed 8 of 417 tokens, same next-token scores: True 和 Computed 7 of 416 tokens, same next-token scores: True:每个请求送入模型的提示内容都不到 2%,而且得到的下一词元分数与完整计算相同。
开源模型服务引擎会自动应用这一机制。vLLM 将 KV 缓存保存在固定大小的块中,这种内存布局称为 PagedAttention;对于每个新请求,它会重用最长匹配前缀的块,并在 GPU内存不足时逐出最近最少使用的块。SGLang 的 RadixAttention 会将缓存前缀保存在基数树中,让共享任意长度前缀的请求都能重复利用缓存;SGLang 论文报告称,其吞吐量比其他最先进的推理引擎最高可提升 6.4 倍。In Gim 和同事在 MLSys 2024 上发表的 Prompt Cache 论文将重用范围从完全匹配的前缀扩展到已声明的提示模块(如模板和文档),并报告首词元生成时间在 GPU 上最多缩短 8 倍,在 CPU 上最多缩短 60 倍。
LLM API 中的提示缓存#
托管 API 也提供相同机制,但会收取相应费用。OpenAI 的提示缓存默认适用于支持的模型;Anthropic 的 Claude 提示缓存则通过 cache_control 字段启用,可应用于每个请求一次,或最多应用于四个内容块。Anthropic 对缓存读取收取基础输入价格的十分之一或更低费用,对默认五分钟缓存的写入则收取基础价格的 1.25 倍;每次命中都会重置五分钟的有效期。两家服务提供商都只匹配完全相同的前缀:Anthropic 依次比较工具、系统提示和消息,因此修改工具定义会使其之后缓存的所有内容失效;OpenAI 的指南则建议将稳定指令和参考资料放在前面,将每位用户的具体信息放在最后。
共享长前缀的工作负载获益最大:
- AI 智能体:每一步都会重新发送工具定义和完整历史记录,也就是上一步的提示加上最新操作及其结果。
- 聊天机器人:对话中的每一轮都会在上一轮提示的基础上继续,因此直到新消息到来之前,历史记录都会被缓存。
- 文档问答:针对一份长合同、报告或代码仓库的许多问题都会重复使用已处理的文档;检索流程也会在再次检索到相同段落时重复使用这些段落。
- 少样本提示:几十个固定示例只需计算一次,就能在每次输入时重复使用。
开放词汇视觉模型也会以类似方式复用提示计算。Ultralytics YOLOE会在调用set_classes()时,用 MobileCLIP 文本编码器对“forklift”等类别提示进行一次编码,并将生成的嵌入存储在模型中,因此后续预测完全可以跳过文本编码器;save_prompt_embeddings()会将这些嵌入写入 NPZ 文件,之后的会话可通过load_prompt_embeddings()加载该文件,而不必再次运行编码器。










