支持私有云部署
AI知识库

53AI知识库

学习大模型的前沿技术与行业应用场景


Prompt caching,一篇就够了。

发布日期:2025-03-21 05:41:56 浏览次数: 1583 来源:DevAI
推荐语

掌握Prompt Caching,高效降低AI模型延迟和成本。

核心内容:
1. Prompt Caching概念及必要性解析
2. Prompt Caching的工作机制和实现方法
3. 用户和开发者如何应用Prompt Caching以优化性能

杨芳贤
53A创始人/腾讯云(TVP)最具价值专家

 

⏰ Read: 25 + 30min

我第一次听到 prompt caching 可能是 DeepSeek 当时推出了缓存命中 0.1 元每百万 tokens(DeepSeek API 创新采用硬盘缓存,价格再降一个数量级[1]),随后各大厂商陆续推出了自己的 prompt caching 特性。

本文旨在回答四个问题

  • • 什么是 prompt caching ?为什么需要 prompt caching?
  • • prompt caching 的原理是什么?
  • • 我们作为 user 和 developer 如何更好地使用 prompt caching 呢?
  • • prompt caching 和 KV caching 有关系吗?使用 prompt caching 时是否会反过来影响输出的质量呢?

我会基于 OpenAI 的博客[2],论文 Prompt Cache: Modular Attention Reuse for Low-Latency Inference[3] 和我自己检索的信息,带着上面这四个问题尽力构建一篇足够简洁的博客。

当然,对于用户,我们追求的可能只是如何高效地利用这个特性。如果把原理混杂在 best practice 中非常影响阅读的流畅性,因此我会分成如何使用深入理解两个章节,力求一气呵成的阅读流畅性。水平有限,欢迎指正!

1. 如何使用

⏰ Read: 25min

Note

指令缓存 (prompt caching) 是一种用于降低延迟和成本的技术。

这句话介绍了 prompt caching 的具体作用。

model prompt = system prompt + user prompt

这是一个基本知识。通常情况下 model prompt 是包含许多重复内容的,例如 system prompt 和在 user prompt 中出现的常用指令(比如角色扮演的指令,数据分析的指令)。

因此,prompt caching 便是旨在 “存储”并“利用” 这些 model prompt 中的重复内容,来达到同时降低延迟和成本的好处。实现用户降低了成本,提升了速度;厂商减少了算力消耗,双赢的局面。

例如 OpenAI 会将 API 请求路由到最近处理过相同 prompt 的服务器,这比从头开始处理 prompt 更便宜、更快速。对于较长的 prompt,这可以将延迟降低高达 80%,并将成本降低 50%。prompt caching 会在你所有的 OpenAI API 请求中自动生效 (无需更改代码),并且不存在任何额外费用。

要理解这个优化效果,我们需要了解 LLM 响应中的一个重要概念:首 Token 延迟(TTFT, Time to Frsit Token)。这是从发送请求到收到模型第一个 token 响应所需的时间。

“在线服务场景中,如聊天机器人,大语言模型推理一般都采用流式输出(streaming)的形式,LLM 推理的 TTFT 就是用户感受到的 LLM 推理服务的响应时间,直接影响用户体验。”对于在线服务,为了提升用户体验,所以都希望 TTFT 要足够小,这也是我们为什么需要 prompt caching。

TTFT 和 prompt 的长度密切相关,对于长 prompt 来说,大部分延迟都发生在这个阶段,因为模型需要先处理完整个输入才能开始生成输出。 通过 prompt caching,模型可以直接利用已缓存的处理结果,显著减少这个首次等待时间。这就像是模型已经"预习"过这段内容,可以更快地开始响应(和 KV Cache 很像,可以思考下他们的不同之处,我会在后文给出我的思考)。特别是在处理包含大量重复内容(如系统提示词、固定指令等)的请求时,这个优势更为明显。

Tip

TTFT 的理解可能涉及到了小部分 LLM Inference 相关的知识,推荐阅读我的另一篇博客:Basic LLM Inference/Generation,一篇就够了。[4]

上面这一小段内容已经回答了问题1,已经了解到 prompt caching 是如此的 重要且实用,作为用户我们该如何高效的利用呢?

1.1 Structuring prompts

当前绝大部分的 LLM API,只有当两个请求的前缀内容相同时(从第 0 个 token 开始相同),才存在缓存命中。为了利用缓存的优势,请将静态内容 (例如说明和示例) 放在 prompt 的开头,并将可变内容 (例如用户特定的信息) 放在结尾。这也适用于图像和工具,它们在不同请求之间必须相同。

使用方法就是如此简单:“将需要缓存的内容放到 prompt 开头即可”。接下来,我综合了 OpenAI 和 Anthropic 的博客给出 best practices。

Tip

问题:为什么是 caching 是通过前缀匹配识别?必须使用前缀匹配吗?


思考: 前缀匹配的重要性在于其能够高效地识别出重复的 prompt 片段,进而实现缓存的复用。

为什么能高效地识别呢?从技术原理上讲,因为可以使用类似于计算机科学中的“字典树”(Trie) 数据结构。通过将 prompt 的前缀作为路径存储在树状结构中,系统可以快速地查找是否存在匹配的前缀。这种方式的优势在于查找速度快,时间复杂度通常为  ,其中  为前缀的长度。此外,前缀匹配也符合实际应用场景,因为大多数情况下, prompt 的变化主要集中在尾部,而开头部分往往是相对固定的。


 

那么必须是前缀匹配吗?在特定情况下我们可能只有中间某个位置不同,例如:

Original Prompt:
"你是一个专业的文章翻译助手。请将下面这篇关于人工智能的文章翻译成中文。
[文章内容...]
请保持专业术语的准确性。"

这一个 prompt 只是一个例子,但是我们发现,它只有中间会有变动,在这种情况下基于前缀匹配的 prompt caching 会无法 caching 后半部的静态内容。

Claude 支持的 cache_control 特性就更具灵活性,它可以分别 caching 不同的文本(因为我们可以把上面的 prompt 拆成三个部分,然后分别用 cache_control 修饰第一个和第三个部分) 。当然,后文中我们还会介绍另外一种方法, 但是我认为这种方法的缺陷是不够用户友好,前缀匹配是一个非常简洁,也在绝大部分情况下都奏效的匹配策略,这里的选择本质上是工程的 tradeoff。 

1.2 Best practices for effective caching

  • • 合理选择缓存内容 妥善缓存那些稳定且可重复使用的内容。这类内容包括:系统指令、背景信息、大段上下文或常用的工具定义等。这些内容往往是"一劳永逸"的基础组件,善加利用可事半功倍。
  • • 优化内容位置 将需要缓存的静态或重复内容放置在提示词的开头位置,将动态内容放在结尾。这种布局能够最大程度地发挥缓存机制的优势。
  • • 持续监控和优化 定期检查缓存相关的指标,例如缓存命中率、延迟和缓存的 token 百分比,根据实际使用情况优化 prompt 和缓存策略。这能确保缓存机制始终保持良好的效果。
  • • 优化请求策略 请使用较长的 prompt 并在非高峰时段发起 API 请求,因为在高峰时段缓存淘汰更为频繁。并且最近未使用的 prompt 会自动从缓存中移除。为了最大限度地减少淘汰,请保持使用相同 prompt 前缀的连续请求流。(通常情况下,API 只对长度超过某个阈值的 prompt 进行缓存,阈值通常是 1024)
  • • [Claude Only] Claude 并不会像 OpenAI 一样自动的启用 prompt caching,它需用手动的添加 cache_control 标识。因此,通过巧妙设置缓存断点,将不同的可缓存前缀部分加以区分。这样做既可以让缓存更有条理,又能提高缓存命中率。

到这里,前三个问题我们都有了大致的回答。作为一个用户,我们知道了什么是 prompt caching 以及如何最佳的使用 prompt caching 了!接下来,我们尝试进一步的理解其背后的逻辑。

2. 深入理解

⏰ Read: 30min

2.1 How prompt caching works

这里引用 OpenAI 官方博客中的内容(Claude 会更复杂一些,感兴趣的推荐阅读:Build with Claude - Prompt caching[5]

对于长度为 1024 个 token 或更长的 prompts ,缓存会自动启用。当你发起 API 请求时,将执行以下步骤:

  1. 1. 缓存查找 (Cache Lookup):系统检查 prompt 的初始部分 (前缀) 是否存储在缓存中。
  2. 2. 缓存命中 (Cache Hit):如果找到匹配的前缀,系统将使用缓存的结果。这将显著降低延迟并减少成本。
  3. 3. 缓存未命中 (Cache Miss):如果没有找到匹配的前缀,系统将处理你的完整 prompt 。处理后, prompt 的前缀将被缓存以供将来的请求使用。

缓存的前缀通常在 5 到 10 分钟的不活动状态后失效。但是,在非高峰时段,缓存可能会持续长达一小时。

当然,具体较大家如何使用 API 不是我这篇博客的目的,我会在附录中 refer 上对应的 API 文档以及他们提供的常见用例,关于一些 FAQ 也可以直接参考对应的文档~

2.2 Differences from KV Caching

对于熟悉 KV Caching 的朋友,prompt caching 和它有诸多相似之处。它们都是避免计算重复的注意力 embedding,通过缓存手段加速推理过程。

这里简单的 review 下 KV Cache 在干什么。所谓自回归推理,就是每次都要基于之前的内容来预测(计算)下一个 token。如果没有任何优化手段,自回归中预测下一个 token 的计算量是递增的(因为某些 token 被重复计算了多次),所以 KV Cache 就出现了,也就是常用的“以空间复杂度换时间复杂度”的思路。引用我之前博客中的动图(由于公众号的限制,我转化为了视频),灰色的部分是被保留的 KV cache,我们可以在下次自回归计算时直接使用。

prompt caching 也是干这事的,“复用之前计算的结果”,那么他们的区别到底在哪呢?下图引用自“Prompt Cache:Modular Attention Reuse for Low-Latency Inference“ 论文。

我认为其区别在于:KV Cache 是 single session 的 cache,而 prompt cache 是支持 cross session 的 cache(也许这里用 sequence 而非 session 会更好)。我们来分析这张图:

  • • 图(a)代表的是最原始的自回归计算过程,如上文所述,某些 token 需要重复多次计算。
  • • 图(b)则是 KV cache,多出了一个 attention states 的部分,这部分可以存储已经计算过的结果,再下一次自回归计算中复用,这大大降低了计算量。(至于为什么可以 KV cache,这是我很久之前挖的坑了,有机会补上后在此处更新引用)。
  • • 图(c)则是我们的主角 prompt cache,它在 KV cache 的基础上多了一个 prompt cache。本质上就是增加了一个全局的管理器,来实现跨序列的缓存。它可以将其他序列中缓存的结果提供给本次推理使用,所以它提供的是一个 overall 的加速而非单次推理的加速。(讲到这里有点类似 PagedAttention,通过降低显存占用来间接地提升吞吐,当然这是我粗糙的理解,欢迎指正)这里的缓存不一定在显存/内存上,甚至可能是磁盘。至于性能,我们一次性读取大量的 tokens 其实也没那么慢。(相比计算的开销来说)

所以回过来 prompt cache 和 kv cache 的区别在于: prompt cache 提供的是一个跨序列的 cache,它的效果也是作用于全局(多次)而非单次推理。

prompt cache 最大的问题就是如何维护与识别,区别于 kv cache 本质上是“一定”会被复用,prompt cache 可能会面临存储后无人问津的尴尬情况,并且如何识别也是影响缓存命中的关键因素。

2.3 Prompt Cache Matching Mechanisms

在 如何使用 的 part 中,我们介绍的是最粗暴的前缀匹配(即 OpenAI 使用的)。同时,我们在思考环节遇到了一个问题:“如果我们只有中间某处 prompt text 不同怎么办呢?”进一步推广:“如果我们的 prompt 的存在一个范式,只在某些固定位置不同怎么办呢(例子可以参考 python 的 String Formatting)?

比如下图,两个 prompt 只有某个特定位置的 token 不同,这时候简单的前缀匹配就会失效。

因此,论文中提到了 Prompt Markup Language(PML),什么是 PML 呢?提示标记语言(PML)旨在以结构化的方式表示提示,特别是突出 prompt 中的不变 (固定) 部分和可变 (可更改) 部分。

正如我们在问题中提到的,当前缀匹配遇到只有中间某处不同的 prompt 时,它就失效了。而 PML 通过明确标记出这些可变部分,巧妙地解决了这个问题。我举一个简单的例子大家一看便知:

  • • <item><quantity> 和 <no> 是可变槽位,代表可以更改的部分。
  • • 其余部分是固定文本

现在,即使两个顾客点了不同的菜品、数量和忌口,PML 都能识别出它们遵循相同的模板,并准确地识别出哪些部分是可变的。所以相较于粗暴的前缀匹配,PML 可以基于模版识别实现更准确的匹配, 并且得到更高效的缓存。

Important

我们还需要考虑一个问题:只有当一个文本片段在大型语言模型(LLM)输入中出现在相同位置时,其注意力状态才能被重用。 原因是,Transformer 将位置信息整合到了键(k)和值(v)注意力状态中。对于单个 prompt 的 KV cache 而言,这并不是问题,因为在所有步骤中,相同的 prompt 都位于相同的位置,即输入的开头。

 


那么如果我们使用非前缀匹配,就会遇到这个问题,即如何处理位置编码?共享的文本在不同 prompt 中可能出现在不同位置。为了实现跨 prompt (也就是不同位置)的注意力状态重用,cache system 必须解决两个问题。首先,尽管一个文本片段可能出现在不同 prompt 的不同位置,系统仍须支持其重用。其次,当系统接收到一个新的提示时,它必须能够高效地识别出其注意力状态可能已被缓存的文本片段,从而实现重用。

为解决这两个问题,论文结合了两种思路。首先,提出使用提示标记语言(PML)来明确提示的结构,将可重用文本片段明确表示为 module,即 prompt module。PML 很好的解决了上述的第二个问题(识别哪些是重复文本片段),还为解决第一个问题提供了可能,因为每个 prompt module 都可以被分配唯一的 Position IDs。其次,我们通过实验发现,LLM 可以处理带有不连续 Position IDs 的注意力状态。只要令牌间的相对位置保持不变,输出质量就不会受到影响。Our second idea is our empirical finding that LLMs can operate on attention states with discontinuous position IDs. As long as the relative position of tokens is preserved, output quality is not affected.)这意味着,我们可以提取不同的注意力状态片段,并将它们拼接起来以构建新的语义。利用这一特性,用户可以根据自身需求选择提示模块,甚至在运行时替换某些含义。

那另一个问题是:由于位置的变化,即使相同的文本,在真正推理时也会是不同的 kv attention state。但是,此处,我们使用的是相同的 kv attention state,是否会影响输出质量呢?在论文 5.3 节中提到:在所有数据集中,使用 prompt cache 输出的准确性与 baseline 相当。(但是仍然会有细微差异,所以基于 PML 的 prompt cache 并不像 kv cache 一样是完美的替换。

当然,这里的解释还点模糊,我暂时留下一个坑,阅读完相关代码后我会在这里尝试进一步解释什么是:“LLM 可以处理带有不连续 position ids 的注意力状态“。

同时,如何前文所说:Claude 提供的 cache_control 特性某种程度上可以实现类 PML的效果。对比 OpenAI 和 Claude:

  • • OpenAI 提供一个更直接的 caching 策略,更用户友好,但是在特定情况下容易失效。
  • • Claude 提供的是一个更专业的caching 策略,初学者可能难以看懂,但是学会后可以实现最佳的缓存性能。

回答最后一个问题:“使用 prompt caching 时是否会反过来影响输出的质量呢?” KV cache 是不会影响推理的效果,因为它是等价替换。同理使用基于前缀匹配的 prompt cache 也不会影响推理的效果。但是,在上文分析中,我们发现虽然实验结论证明基于 PML 的 prompt cache 能和 baseline 得到接近的输出,但是它们之间还是存在不同,并不是完美的等价,所以在使用基于 PML 的 prompt cache 时应当考虑匹配机制对于输出质量的影响。

本篇到此完结,当前其实还有一些细节可以深入挖掘,但是我觉得我已经回答了文章开始处的四个问题。就像 OpenAI 一样,我这里就提供一个“简单粗暴”的概览,以供大家了解什么是 prompt caching 以及 prompt caching 的 best practice。

 

53AI,企业落地大模型首选服务商

产品:场景落地咨询+大模型应用平台+行业解决方案

承诺:免费场景POC验证,效果验证后签署服务协议。零风险落地应用大模型,已交付160+中大型企业

联系我们

售前咨询
186 6662 7370
预约演示
185 8882 0121

微信扫码

添加专属顾问

回到顶部

加载中...

扫码咨询