LLM 应用的缓存工程:当每次 API 调用都在燃烧成本

在生产环境中运营 LLM 应用,最直接的痛感往往来自账单。

随着用户量和交互频次上升,API 调用成本与规模几乎呈线性关系。一个日均处理一万次对话的应用,每次请求携带数千 token 的系统指令和历史上下文,月支出可以轻松突破五位数。更令人困扰的是,大量请求在语义层面高度重复------用户反复问相似的问题、客服场景中同一类别的查询反复出现、定时任务每天带着几乎相同的上下文请求模型做同一类判断。把同样的工作交给模型重复做,然后为每一次推理付费,这显然不是最优解。

缓存是解决这类问题的经典手段,但在 LLM 场景中,缓存的含义和实现方式发生了根本变化。传统缓存缓存的是精确匹配的数据对象,而 LLM 的缓存至少需要覆盖两个层次:Provider 侧的 Prompt Caching 负责减少每次请求的输入 token 消耗,应用层的 Semantic Caching 负责减少推理请求本身的次数。这两层缓存各有各的机制、各有各的适用场景,组合起来才能形成完整的缓存策略。

Provider 侧的 Prompt Caching:自动省掉重复的上下文

当前主流模型服务商都在提供 Prompt Caching 能力。虽然各自实现方式不同,但核心理念一致:当用户请求中出现了与之前请求重复的 prompt 前缀时,服务端可以复用上一次推理时缓存的 KV Cache 状态,从而避免重复计算这些 token 的注意力。

OpenAI 的 Prompt Caching 是自动触发的。当请求的 prompt 前缀达到 1024 token 以上且与缓存中的内容匹配时,会自动命中缓存。命中部分享受 50% 的输入 token 价格折扣,同时首 token 延迟可以降低 50% 到 80%。开发者不需要在代码中做特殊标记,只需要确保系统指令和上下文历史在请求中保持稳定的前缀位置。工程上的关键在于设计 prompt 结构------把稳定不变的内容放在前缀部分,把每次不同的用户输入放在后缀。如果系统指令、工具定义、few-shot 示例每次请求都会变化,缓存命中率就会急剧下降。

Anthropic 的 Prompt Caching 则是显式控制的。开发者需要在请求中通过 cache_control 标记指定哪些内容块应该被缓存。这意味着可以精细控制缓存策略------给系统指令设置 5 分钟 TTL,给长篇文档设置 1 小时 TTL,用户消息不缓存。不同 TTL 的缓存块可以混合在同一次请求中,服务端会自动计算每个缓存位置的失效时间和费用优惠。

工程落地时,应对 Provider Prompt Caching 的第一步是审查 prompt 的结构稳定性。举例来说,一个 Agent 应用的每次请求中,系统指令和工具 Schema 通常是不变的,历史消息摘要可能每几轮才更新一次,当前用户消息则每次都不同。把这三部分按"最稳定到最动态"的顺序排列,让稳定内容占据 prompt 前缀位置,就可以最大化缓存命中。LangChain 等框架已经内置了适用于 OpenAI 和 Anthropic 的缓存封装,但即便不使用框架,在 API 调用层自己控制缓存标记也并不复杂------核心是理解缓存命中的条件,并在 prompt 模板设计阶段就把它考虑进去。

不过 Provider Prompt Caching 有明显的工程边界。它只能缓存输入侧的 token 成本,对推理过程和输出 token 没有加速效果。缓存依赖于 prompt 前缀的精确匹配------只要前缀有任何字符变化,缓存就会失效。而且,缓存是在 Provider 侧管理的,应用无法直接控制缓存的生命周期、无法按自己的业务逻辑做缓存驱逐,也无法对缓存命中做细粒度监控。

应用层的 Semantic Caching:用相似度代替精确匹配

Prompt Caching 解决的是"同一段文本反复发送"的问题,但实际场景中,用户的请求很少有完全一致的精确字符串。更常见的情况是,不同用户用不同的措辞表达同一个意图------"今天的服务器负载如何"和"现在服务器的压力大不大"在语义上高度相似,但文本层面几乎找不到重叠。

Semantic Caching 就是为了解决这个问题而设计的。它的基本流程是:将用户的输入请求通过 embedding 模型转化为向量,在向量数据库中搜索与之语义最相似的历史请求缓存。如果相似度超过预设阈值,直接返回缓存中的模型回复结果,不再调用 LLM API;否则,正常调用模型,并将新请求及其回复写入缓存。

这个流程在架构上并不复杂,但几个工程参数的选择直接影响实际收益。最关键的参数是相似度阈值。阈值设得太低(如 0.85),缓存命中率会很高,但假阳性也多------语义相近但实际需要不同回复的请求可能被错误地返回旧结果。阈值设得太高(如 0.98),命中率会很低,缓存几乎不起作用。实践中的一个合理起点是 0.93 到 0.95,然后根据实际业务数据做调整。另一个重要参数是 embedding 模型------轻量模型(如 text-embedding-3-small)速度快、成本低,但对语义细微差别的感知能力不如大模型;如果面向的查询差异微妙(如"价格是多少"与"折扣是多少"),可能需要更敏感的 embedding。

存储后端的选择同样影响工程复杂度。Redis Stack 提供了内置的向量搜索能力,可以在同一集群中同时管理缓存数据和向量索引,减少了外部依赖。GPTCache 是一个更完整的语义缓存框架,封装了 embedding 生成、向量存储、缓存淘汰策略和一致性管理,适合快速集成。对于更高吞吐的场景,使用独立的向量数据库(如 Milvus、Qdrant)作为缓存存储层虽然增加了运维成本,但可以支撑更大规模的缓存数据量和更复杂的过滤逻辑。

缓存 key 的设计也是容易被忽视的细节。简单的做法是把整个用户输入作为 key,但这样会忽略对话上下文的维度。一个更合理的做法是将 system prompt 的 hash、用户的身份标识、以及对话轮次组合成复合 key,确保不同上下文环境中的相同问题不会互相污染缓存。

两层缓存的协同:各管各的问题

把 Provider Prompt Caching 和 Semantic Caching 组合在一起,就形成了一个两层的缓存体系。

第一层是 Semantic Caching,运行在应用进程中或与应用相邻的缓存服务中。当用户请求到达时,先走 Semantic Cache:如果命中,直接返回缓存的回复,整个过程不产生任何 LLM API 调用,延迟通常在几十毫秒以内。这里节省的是完整的推理成本------既包括输入 token 也包括输出 token。

第二层是 Provider Prompt Caching,发生在 API 请求到达模型服务商之后。如果 Semantic Cache 没有命中,请求会带着完整的 prompt 发往 Provider。此时,如果 prompt 的前缀部分命中了 Provider 侧的 KV Cache,就可以节省大部分输入 token 的推理成本。

这两层缓存不仅在功能上互补,在成本维度上也是递进关系。Semantic Cache 拦截的是请求级别的重复,省的是全量推理费用;Prompt Cache 拦截的是 token 级别的重复,省的是部分输入费用。从工程效益来看,优先优化 Semantic Cache 的命中率,然后再通过 prompt 结构设计优化 Prompt Cache 的命中率,是一个合理的推进顺序。

落地时的工程考量

在实际工程中应用 LLM 缓存,有几个关键点需要提前处理。

第一是缓存一致性。Semantic Caching 返回的是历史模型的回复,如果模型本身发生了更新(如切换了模型版本,或更新了系统指令),历史缓存的内容在语义上可能已经不再准确。一个简单的应对策略是在缓存中附带生成缓存时的模型版本、系统指令版本和 temperature 参数,在读取缓存时做版本比对。版本不一致时直接跳过缓存、重新调用模型。

第二是数据安全。输入请求中如果包含用户身份信息、业务敏感数据或 PII,不适合进入语义缓存层,因为这些数据一旦被缓存并被后续的相似查询命中,就可能造成信息泄露。简单的做法是在 embedding 之前对请求做脱敏处理,或者为不同的用户身份设计隔离的缓存空间。

第三是缓存监控。仅仅知道缓存命中率是不够的,更重要的是知道缓存是否在产生实际价值。需要追踪的指标包括:Semantic Cache 命中率、缓存回复的平均质量评分(可以周期性抽样,用 LLM-as-Judge 评估缓存回复对当前请求的适用性)、误命中率(缓存返回后被用户或下游系统废弃的比例),以及缓存带来的实际成本节省金额。

第四是动态内容的权衡。对于实时性要求高的场景------如查询当前服务器状态、检索最新日志------语义缓存可能会返回过时的信息。一种实践模式是:为不同的请求类型配置不同的 TTL,对实时性要求高的请求跳过缓存直通模型,而对知识问答、文档分析等对时效性不敏感的场景充分使用缓存。

最后,缓存不是万能的。当输入内容高度个性化(如每个用户的查询都完全不同)、业务对回复的准确性有极高要求、或者模型回复中需要包含实时的外部数据时,缓存带来的收益可能不足以覆盖其引入的复杂性和潜在风险。在这些场景中,与其强行缓存,不如把精力花在优化 prompt 结构来利用 Provider Prompt Caching,或者在应用层通过请求合并、上下文复用等非缓存手段来控制成本。

LLM 应用的缓存工程不是一个开箱即用的功能,而是一组需要根据业务特性精心设计和调优的系统决策。从 Provider Prompt Caching 到 Semantic Caching,每一层缓存的引入都在成本、延迟和回复质量之间做取舍。理解这些取舍,并建立起与之匹配的监控和评估体系,才是把 LLM 应用从实验阶段推向规模化运营的关键一步。

相关推荐
code 小楊1 小时前
AI函数调用:Function Calling从理论到实战全解析
人工智能
delishcomcn1 小时前
智切未来:AI算法如何重塑不干胶标签分切机的精度与效率边界
人工智能·神经网络·计算机视觉
观远数据1 小时前
ChatBI选型对比:从意图识别到SQL修复,六个维度打分决定是否值得投产
数据库·人工智能·sql
犀利豆1 小时前
AI in Harness(四)
人工智能·后端
Hui Baby2 小时前
Spring Security
java·后端·spring
imbackneverdie2 小时前
AI4S不止于分子药物:以MedPeer为代表的科研基建打开产业新增量
大数据·人工智能·算法·aigc·科研·学术·ai 4s
IT笔记2 小时前
【Rust】Rust Match 模式匹配详解
java·开发语言·rust
agent喵2 小时前
从知识库问答到业务自动化:AI Agent 工作流搭建经验分享
人工智能
周末程序猿2 小时前
图解 120 个大语言模型(LLM)核心概念(1-30)
人工智能