LLM 系统的缓存策略:精确匹配与语义缓存
Source: Dev.to

LLM 调用在延迟、令牌消耗和计算资源方面成本高昂。缓存是降低成本、加快响应的最有效手段之一。本文阐述了两种你今天即可实现的基础缓存技术:精确匹配(键值)缓存和语义(嵌入)缓存。我们将介绍每种方法的工作原理、典型实现方式、优缺点以及常见陷阱。
为什么缓存对 LLM 系统很重要
每次 LLM 调用都有三项主要成本:
- 网络延迟 – 与 API 或推理集群的往返时间。
- 令牌费用 – 许多 API 按输入 + 输出令牌计费。
- 计算开销 – 运行模型所消耗的 CPU/GPU 时间。
在生产环境中,许多查询会重复(完全相同或语义相似)。缓存使系统能够在不重新运行模型的情况下返回之前的结果,从而在延迟、吞吐量和成本上立即获益。
关键好处
- 降低终端用户的响应时间。
- 减少 API 费用和计算消耗。
- 提高吞吐量,在大规模下提供更好的用户体验。
周全的缓存层往往是 LLM 产品中投入回报率最高的工程工作之一。
精确匹配(键值)缓存
工作原理
精确匹配缓存会将 LLM 的响应存储在一个由提示(以及任何上下文状态)决定的确定性键下。当再次出现相同的键时,缓存会返回已存储的响应。
输入提示 → 标准化 → 哈希/键 → 在 KV 存储中查找 → 返回存储的响应
实现说明
- Normalization(可选但推荐): 去除多余空白、统一换行符、删除瞬时元数据,并确保参数顺序一致。
- Key generation: 使用稳定的哈希函数(例如 SHA‑256)对标准化后的提示以及相关元数据(系统提示、temperature、模型名称、会话 ID、schema 版本)进行哈希。
- Storage: 原型阶段可使用简单的内存
dict;生产环境可使用 Redis/KeyDB;对于大响应可使用持久化对象存储。 - Validation: 将元数据(模型版本、temperature、时间戳、源提示)与响应一起存储,以便安全判断缓存结果是否仍然有效或需要失效。
简单的 Python 示例(概念性)
import hashlib
import json
def make_key(
prompt: str,
system_prompt: str = "",
model: str = "gpt-x",
schema_version: str = "v1",
) -> str:
# Normalise whitespace
normalized = "\n".join(line.strip() for line in prompt.strip().splitlines())
payload = json.dumps(
{
"system": system_prompt,
"prompt": normalized,
"model": model,
"schema": schema_version,
},
sort_keys=True,
)
return hashlib.sha256(payload.encode()).hexdigest()
# Example usage:
# key = make_key(user_prompt, system_prompt, model_name)
# if key in kv_store:
# return kv_store[key]
何时使用精确缓存
- 确定性工作流(例如,代理步骤输出)。
- 重复的系统提示和模板。
- 需要精确复用以确保正确性的情况(避免因上下文不匹配导致的幻觉风险)。
优势: 简单、确定性、零误报风险。
局限性: 对自由形式自然语言的命中率低;对细微的提示更改非常脆弱。
语义缓存
工作原理
语义缓存为每个提示存储一个嵌入向量以及对应的响应。对于新提示,计算其嵌入,在缓存的向量中进行最近邻搜索,如果相似度超过阈值,则复用缓存的响应。
Prompt → Embedding → Similarity search in vector store →
If max_sim ≥ threshold → reuse response
实现要点
- 嵌入向量: 选择统一的嵌入模型。存储归一化后的提示文本、嵌入向量、响应以及元数据(模型、生成参数、时间戳、模式版本)。
- 向量存储: FAISS、Milvus、Pinecone、Weaviate 或 Redis Vector 是常见选项,可根据规模和延迟需求选择。
- 相似度度量: 余弦相似度是文本嵌入的标准做法。索引和查询时使用相同的度量方式。
- 阈值设定: 设置一个在复用与安全之间取得平衡的阈值。典型的余弦阈值随嵌入模型而异——在你的数据集上进行调优(通常从 0.85–0.90 的保守值开始)。
概念示例(伪 Python)
# Compute embedding for new prompt
q_vec = embed(prompt)
# Nearest‑neighbor search → returns (id, sim_score)
nearest_id, sim = vector_store.search(q_vec, k=1)
if sim >= SIM_THRESHOLD:
response = cache_lookup(nearest_id)
else:
response = call_llm(prompt)
store_embedding_and_response(q_vec, prompt, response)
调整相似度与安全性
- 校准: 在留出的同义句和不相关提示集合上评估相似度阈值,以估计误用的假阳性率。
- 混合检查: 对高风险输出,结合语义匹配与轻量级启发式规则(例如实体重叠、输出形状检查)或快速重排序器,在返回缓存内容前进行二次验证。
- 元数据门控: 确保模型版本、模式版本等相关参数匹配后才复用缓存响应。
优势
- 能处理同义改写
- 对对话查询的有效缓存命中率更高
局限
- 需要嵌入向量、向量存储,并且必须仔细调参以避免错误复用
在精确匹配缓存和语义缓存之间的选择
- 精确匹配缓存 – 当正确性和确定性很重要且提示高度模板化时使用。
- 语义缓存 – 当查询为自然语言、常见同义改写且可以接受一定近似,以换取更高命中率时使用。
混合方法
一种有效的生产设计通常结合两者:
- 首先尝试精确匹配。
- 若未命中,则回退到语义搜索。
- 存储两种键并在插入时去重。
指标、监控与运营关注点
关键指标
- Cache hit rate(精确 / 语义)
- End‑to‑end latency 对于缓存命中与未命中
- Cost saved(节省的 token / 计算)
- False‑reuse incidents(语义误报)及用户影响
运营关注点
- Eviction policy & TTL – 在存储成本和新鲜度之间取得平衡。
- Model upgrades – 使旧模型版本产生的缓存条目失效或打标签(或提升 schema 版本)。
- Privacy & sensitivity – 除非加密并受访问控制,否则避免缓存 PII 或敏感输出。
- Auditability – 记录响应是从缓存提供的时间以及匹配的键/分数。
实现与代码
想要查看实际示例吗?请查看带代码的实现:
仓库: VaibhavAhluwalia / llm-caching-systems
实际实现和实验,旨在使用缓存技术构建快速、可扩展且成本高效的大型语言模型(LLM)应用。
该仓库包括:
- 演示两种缓存策略的交互式笔记本
- 一个用于快速设置的
requirements.txt文件
结论与下一步(第 2 部分)
精确匹配和语义缓存是基础。它们共同使大型语言模型系统更快、更便宜,同时保留大模型的优势。
在本系列的 第 2 部分 中,我们将介绍其他技术。
在你的 LLM 项目中,哪些缓存策略效果最佳?请在下方评论分享你的经验!
与我联系
- GitHub: @VaibhavAhluwalia
- LinkedIn: Vaibhav Ahluwalia

DEV Community – 一个讨论和跟进软件开发、管理软件职业的空间。

