深入解析提示词缓存技术:加速LLM并降低成本的核心机制

本文详细解释了提示词缓存这一原生提供商功能,它通过存储和复用提示词中不变的前缀部分,显著降低大语言模型调用的延迟和输入令牌成本,并探讨了其工作原理、不同供应商的实现差异以及最佳实践。

引言

提示词缓存是一项提供商原生的功能,它存储并复用提示词中初始、不变的部分(提示词前缀),使得大语言模型无需在每个请求中重新处理这部分内容。更具体地说,它缓存了模型对该前缀的内部状态,减少了冗余计算。这带来了延迟降低和输入令牌节省,且不会损失任何输出质量。换句话说,当你多次请求使用带有长且相同前缀(如系统指令、工具定义或上下文数据)的提示词时,提示词缓存能让你的LLM调用更快、更便宜。 本文将解释其幕后工作原理,如何构建提示词以最大化缓存命中率,以及OpenAI、Anthropic Claude和Google Gemini在实现上的差异。我们还将展示它与语义缓存的对比,以及如何衡量其投资回报率并避免常见陷阱。

关键要点

  • 提示词缓存复用模型状态,而非输出:提示词缓存通过复用模型对给定提示词前缀的内部计算状态来显著降低延迟和输入令牌成本,且不改变模型输出质量。
  • 精确的前缀匹配是硬性要求:即使是微小的差异(空格、JSON键顺序、工具定义)也会破坏缓存命中。始终将提示词结构设置为静态部分在前,动态部分在后。
  • 不同提供商的实现存在实质性差异:OpenAI提供带路由和保留控制的自动提示词缓存,Claude需要显式的cache_control断点标记,而Gemini则同时提供隐式和显式的可配置TTL缓存对象。
  • 衡量决定ROI:通过测量缓存命中率、缓存令牌比例、首令牌生成时间以及输入令牌节省量,来验证实际的性能和成本改进。
  • 最佳结果来自分层缓存:将提示词缓存(在模型内部)与语义缓存(在应用层)结合使用,以最小化单位请求成本和总体的规模化LLM调用。

提示词缓存是什么?

提示词缓存是实现更快、更便宜LLM调用的便捷途径,它能在多次调用中复用提示词中不变的前缀部分。启用后,LLM的API会检查你的提示词是否以模型最近见过的某个前缀开头。如果是,它将跳过对该前缀的计算,而不是重新处理这些令牌。对于大型且重复的提示词,这可以减少高达80%的延迟,并降低高达90%的输入令牌成本。关键在于,提示词缓存复用的是模型对前缀令牌的中间状态(例如,Transformer注意力层中的键值张量),而不是输出文本本身。这意味着模型的最终输出不受影响——提示词缓存将产生与正常处理相同的输出,但避免了前缀部分的冗余工作。

提示词缓存的工作原理

从高层次看,提示词缓存依赖于精确的前缀匹配以及提供商端对该前缀模型状态的缓存。其机制可概括为以下几个步骤:

  1. 前缀哈希与路由:一个传入的API请求会根据提示词的第一部分(例如,前几百个令牌)分配一个哈希值/键。这个哈希值用于将请求路由到可能最近接收过具有相同提示词前缀请求的服务器。
    • 如果你在短时间内发出许多具有完全相同前缀的请求,它们将被路由到同一个缓存节点——不过,在非常高的速率下(例如,每分钟超过15个相同前缀的请求),部分请求可能会溢出到其他机器,缓存效率会降低。
  2. 缓存查找:一旦请求被路由到服务器,系统将尝试确定它是否已经拥有与当前提示词开头匹配的缓存前缀状态。请注意,这是一个精确匹配检查;前缀文本(甚至包括图片或工具定义等附件)必须与先前传入请求的前缀字节对字节完全相同才能命中缓存。
    • 单个字符的差异、不同的JSON键顺序或前缀中切换的设置都会导致不匹配,并造成缓存未命中。
  3. 缓存命中:如果系统找到匹配的缓存前缀,模型将直接转换到缓存状态,绕过重新处理输入令牌的需要。缓存的键/值张量或类似的模型状态被加载到内存中,模型立即开始生成响应的其余部分,就好像它已经读完了前缀一样。
  4. 缓存未命中:如果没有找到匹配的缓存前缀,请求将正常进行——模型将照常读取整个提示词,产生完整的延迟/成本。好消息是,在该请求之后,系统会将该提示词前缀存储到缓存中(在该服务器上),以便未来的请求可以复用。

供应商实现说明

提供商原生的提示词缓存能显著降低首令牌生成时间和输入成本,但具体“如何实现”在OpenAI、Anthropic Claude和Google Gemini之间有所不同。实践目标保持不变:识别请求中最大的稳定前缀,在调用间保持其字节对字节一致,并利用缓存指标来验证生产环境中的命中率和投资回报率。

OpenAI:具备路由控制的自动缓存

OpenAI的提示词缓存在支持的模型上自动启用,前提是你的提示词达到最小大小(通常≥ 1,024个令牌)。核心规则很简单:缓存命中要求精确匹配的前缀,因此请将稳定内容放在前面,将请求特定的数据(用户问题、检索到的片段、易变的元数据)推到最后。 在生产中有两个控制参数很重要。首先,prompt_cache_key可以影响路由,通过将具有相似前缀的流量引导到同一个缓存“桶”来提高命中率。其次,可以通过prompt_cache_retention(例如,内存中的短期保留)控制保留时间,在支持的模型上还可以使用如“24h”的扩展保留。为了可观测性,可以记录usage.prompt_tokens_details.cached_tokens来测量有多少提示词令牌是从缓存提供的。

Claude:显式的cache_control断点

Anthropic的技术更直接:你使用cache_control标记内容块(通常为“ephemeral”)中的可缓存段,这相当于说“缓存到此为止”。Claude按照指定顺序(工具 → 系统 → 消息)构建缓存,并精确匹配缓存区域(包括图片和工具定义)。 实际上,Claude的设计允许最多四个断点,缓存匹配步骤涉及有限回溯(文档记载约为20个块)。因此,对于长提示词,多断点设计可以降低未命中率。TTL默认为5分钟,可选择额外付费的1小时TTL。cache_creation_input_tokens(缓存写入)和cache_read_input_tokens(缓存读取)日志记录了提示词中有多少部分贡献于预热与稳态性能。

Gemini:隐式缓存加显式缓存对象

在Gemini API中,你通常可以在隐式缓存(自动的,模型相关)和显式缓存(创建一个缓存对象,然后引用它)之间选择。隐式缓存在Gemini 2.5模型上默认启用,并具有模型相关的最小大小(例如,Flash与Pro模型的阈值不同)。显式缓存允许你指定TTL(通常默认为1小时),并将一个大型静态上下文作为前缀在许多问题中重复使用。 同样,在Vertex AI上,也有隐式和显式缓存。请求必须达到某个最小大小(文档记载,例如2,048个令牌),隐式缓存的令牌可能有资格享受大幅折扣,而显式缓存则会产生存储成本。

最大化缓存命中率的提示词结构

提示词缓存的黄金法则:“静态在前,动态在后”。为了最大化缓存命中率,构建你的提示词结构,使其前缀(提示词的开头)包含静态和可复用的部分,而后缀(提示词的末尾)包含所有请求特定或用户提供的内容。只有匹配的前缀才能被缓存;提示词中在请求间变化的任何部分必须排除在缓存段之外。在实践中,这意味着你应该按照一致的顺序拼接提示词,例如:

  1. 系统指令/角色提示词:例如,“你是一个AI助手,任务是……”——所有保持不变规则、指南和角色描述。
  2. 工具定义或函数模式(如果使用代理):每次复用的工具及其设置的JSON或XML定义,或函数签名。
  3. 长上下文或背景数据:例如,整个文档、知识库摘录、代码库或大型示例列表。如果API允许,甚至可以包括图片或其他媒体——但为了缓存目的,每次提示词中必须包含完全相同的图片或文件。
  4. 少样本示例或对话:如果你将示例问答对或对话历史作为提示词的一部分,将所有那些静态示例放在此前缀部分。
  5. (缓存断点标记) – 如果你的API要求你指示一个分割点,以区分哪些应该缓存,哪些不应该,你可以在那里做一个标记。(对于OpenAI,这是自动的;对于Claude,你会在这一点包含一个cache_control块;对于Google的显式缓存,这是你上传并引用缓存内容的地方。)
  6. 用户查询或动态输入(最后):提示词的最终部分是每次请求发生变化的部分,例如,最新的用户问题或任何新数据。这部分在缓存前缀之后,不会被缓存(也不需要缓存),因为它每次都不同。

提示词缓存 vs. 语义缓存(何时结合两者)

请注意,提示词缓存(也称为前缀缓存)与语义缓存不同,后者解决类似问题,但位于不同层面且方式不同。提示词缓存,如此处所述,严格针对拥有完全相同的前缀:它非常适合直接复用上下文。相比之下,语义缓存意味着基于含义缓存用户查询和响应级别的内容,即使措辞不同。

缓存方法 提示词缓存(精确前缀) 语义缓存(基于含义)
缓存内容 提示词前缀的LLM内部状态(例如,前N个令牌的模型上下文)。模型每次仍生成新的输出(只是从保存的状态开始)。 给定输入查询的输出(响应),存储以备重用。通常涉及保存查询或提示词的完整答案文本。
命中条件 提示词前缀的精确令牌匹配。前缀文本(或参数如系统提示词、温度等)的任何差异都意味着未命中。 新查询与过去查询的语义相似性。使用嵌入来查找新问题是否与以前见过的问题基本相同(即使表述不同)。
用例 跨多个请求重复的静态上下文(长指令、文档、工具规范)。在多轮对话或代理工具中很有用,其中环境保持不变但问题变化。 用户重复的问题、FAQ式查询,或用户经常问相同内容的情景。非常适合缓存常见或昂贵查询的完整答案,即使措辞不同。
优势 通过避免重新计算静态部分,节省输入令牌处理成本并降低大型提示词的延迟。LLM每次仍会根据新问题定制答案。 如果类似查询之前已回答过,可以完全避免LLM调用——立即返回存储的响应。这可以实现数量级的加速(响应在毫秒级),并通过跳过调用来降低成本。
局限性 仅适用于完全相同的前缀;如果提示词上下文发生变化,或者成本主要在于输出令牌,则无法提供帮助。无法处理语义相似但不完全相同的提示词。 需要维护Q&A对的外部缓存(数据库或向量索引)。如果两个查询在嵌入上相似但实际上意图不同,则存在答案过时或错误匹配的风险,因此需要仔细调整(阈值、验证)。此外,语义缓存通常对提示词的上下文长度没有帮助(它是关于复用输出的)。

结合使用提示词缓存和语义缓存是有意义的——有时被称为“双重缓存”策略。例如,在客户支持聊天机器人中,你可能使用提示词缓存来提供大型静态知识库(这样LLM就不必每次调用时都重新阅读手册,以减少令牌负载),同时使用最终答案的语义缓存,以便如果用户X问了一个问题,随后用户Y问了同样的问题,你可以直接提供缓存的答案,甚至无需调用LLM。 提示词缓存和语义缓存在不同层面运作(一个在模型的推理过程内部,另一个在应用层外部),因此它们并不互斥,可以轻松互补。提示词缓存使每次LLM调用尽可能高效,而语义缓存通过消除重复调用从一开始就减少了所需的LLM调用总数。

衡量指标与投资回报率清单

你如何知道提示词缓存是否正在为你的应用程序增加价值?什么时候它才值得使用?以下是一些你应该跟踪的指标和一些用于评估投资回报率的经验法则:

  • 缓存命中率:跟踪你的请求收到缓存命中的频率。这可以按请求跟踪(cached_tokens > 0cache_read_input_tokens > 0的请求百分比),也可以按令牌跟踪(总输入令牌中来自缓存的比例)。大多数提供商都会提供计算此值所需的数字——例如,OpenAI的cached_tokens字段,或Claude的cache_read_input_tokens与总数的对比,或Google的cachedContentTokenCount
    • 高命中率(例如,80%以上的令牌来自缓存)是一个好迹象,表明你大部分时间都成功复用了上下文。低命中率意味着要么你的提示词重复不够频繁,要么你的缓存过期/失效太频繁。
  • 首令牌生成时间:从发送请求到接收到响应第一个字节的延迟。对于长提示词,这个时间应该显著下降,因为提示词开头没有计算时间。你可以测量有缓存和没有缓存时的首令牌生成时间(尝试禁用实验性缓存功能或比较首次调用与后续调用)。
  • 输入令牌节省量:得益于缓存,你无需处理的输入令牌数量。如果每个请求是10k个令牌,其中9k个来自缓存,那么你为该请求节省了90%的输入令牌。在大量请求中,这会累积成真实的成本节省。
  • 吞吐量与可扩展性:如果缓存按预期工作,那么每个单独的请求在提供商端使用的计算量会更少。这可以帮助你处理更多的每秒请求数,或在你的速率限制内服务更多用户。监控系统的吞吐量,并尝试观察提示词缓存是否允许你提高吞吐量(避免因缓存令牌可能不完全计入这些限制而快速达到令牌速率限制;或者仅仅因为响应返回更快,你的应用程序可以更快地服务下一个查询)。
  • 缓存利用模式:检查你的缓存条目是如何被复用的。是否有某些大型上下文被大量重复?如果是,考虑让这些缓存保持更长时间的“热度”。或者大多数提示词都是一次性的?在这种情况下,缓存可能不会带来大的投资回报率。这些信息可以帮助决定是否重新设计提示词或使用更长的保留期。

常见陷阱与故障排除

缓存问题可能由三个原因引起:(1) 提示词不可缓存(太短或缓存被禁用),(2) 调用间的缓存前缀不完全相同(即使是1位的差异也会触发未命中),或 (3) 缓存窗口过期或被切换模式(工具/网络/引用/图片)而失效。下表提供了一个系统化的清单,用于识别症状、验证原因并使用最合适的解决方案:

问题/症状 诊断检查(需要验证什么) 修复 / 最佳实践(该做什么)
无缓存命中(缓存令牌始终为0) 1) 提示词长度达到提供商阈值(例如,OpenAI ~1024+,Vertex隐式 ~2048+)。2) 前缀在调用间字节对字节完全相同。3) 每次请求都启用了提供商特定的缓存(例如,Claude每次都需要cache_control)。 增加提示词大小使其超过阈值(将稳定的文档/系统文本移入前缀)。使提示词构建具有确定性。记录完整的提示词并对比差异。确保每次调用都发送了必需的缓存标志/标记。
“微小”更改后缓存未命中 对比一个已知命中和未命中的提示词:系统文本、工具模式、JSON序列化、空格/换行、时间戳/ID,或非固定的默认值。检查JSON键顺序和模板渲染的稳定性。 使用稳定的序列化(规范的JSON / 固定的键顺序)。冻结默认值并从缓存前缀中移除动态字段。规范化空格和模板。在日志中添加“提示词指纹”(哈希)以及早发现漂移。
切换功能/模式时缓存失效 你是否在调用之间切换了网络/工具/引用功能、更改了工具定义、添加了图片或编辑了系统指令?(这些通常会创建一个逻辑上的新上下文。) 将每种模式/切换组合视为一个单独的缓存“配置文件”。在会话中保持切换一致。如果必须切换模式,请预期会发生未命中,并预热该模式的缓存。
缓存过期后才复用 用户轮次之间的时间与TTL/保留窗口的比较。你是否可以访问更长的保留期(OpenAI扩展缓存、Claude更长TTL、Google上的显式缓存TTL)? 为你的交互模式选择最经济的长期保留(几小时后的后续跟进→扩展保留)。如果无法延长保留期,考虑仅为高价值上下文进行周期性的轻量级刷新调用(并量化其成本)。
部分提示词更新未按预期工作 更改发生在哪里?如果你在缓存前缀内插入/编辑内容,那就是一个新的前缀(未命中)。如果你只在前缀后面追加内容,缓存仍应适用。如果提供商支持多个检查点,它们是否放置正确? 将提示词结构化为稳定的前序块(系统+长文档)并在后面追加易变部分(用户轮次)。在支持的地方使用多个缓存断点/块,以便独立部分保持可缓存。避免在前缀中间插入内容。

结论

提示词缓存是LLM应用中投资回报率最高的性能杠杆之一,因为它减少了冗余的预填充计算,且不改变模型质量。基本要点很简单:标准化并版本化你的提示词前缀,将动态输入放在末尾,并在首令牌生成时间和成本之外,使用缓存读取信号(缓存令牌、缓存读/写计数器)进行监测。在此基础上,你可以叠加语义缓存策略来消除整个调用。这将形成一个“双重缓存”模式,可以在规模化运营中同时赢得单位经济效益和用户感知的响应速度。

参考资料

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计