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

发布: (2026年1月18日 GMT+8 00:50)
10 min read
原文: Dev.to

Source: Dev.to

LLM 系统缓存策略封面图:精确匹配与语义缓存

vaibhav ahluwalia

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)

调整相似度与安全性

  • 校准: 在留出的同义句和不相关提示集合上评估相似度阈值,以估计误用的假阳性率。
  • 混合检查: 对高风险输出,结合语义匹配与轻量级启发式规则(例如实体重叠、输出形状检查)或快速重排序器,在返回缓存内容前进行二次验证。
  • 元数据门控: 确保模型版本、模式版本等相关参数匹配后才复用缓存响应。

优势

  • 能处理同义改写
  • 对对话查询的有效缓存命中率更高

局限

  • 需要嵌入向量、向量存储,并且必须仔细调参以避免错误复用

在精确匹配缓存和语义缓存之间的选择

  • 精确匹配缓存 – 当正确性和确定性很重要且提示高度模板化时使用。
  • 语义缓存 – 当查询为自然语言、常见同义改写且可以接受一定近似,以换取更高命中率时使用。

混合方法

一种有效的生产设计通常结合两者:

  1. 首先尝试精确匹配。
  2. 若未命中,则回退到语义搜索。
  3. 存储两种键并在插入时去重。

指标、监控与运营关注点

关键指标

  • Cache hit rate(精确 / 语义)
  • End‑to‑end latency 对于缓存命中与未命中
  • Cost saved(节省的 token / 计算)
  • False‑reuse incidents(语义误报)及用户影响

运营关注点

  • Eviction policy & TTL – 在存储成本和新鲜度之间取得平衡。
  • Model upgrades – 使旧模型版本产生的缓存条目失效或打标签(或提升 schema 版本)。
  • Privacy & sensitivity – 除非加密并受访问控制,否则避免缓存 PII 或敏感输出。
  • Auditability – 记录响应是从缓存提供的时间以及匹配的键/分数。

实现与代码

想要查看实际示例吗?请查看带代码的实现:

GitHub logo

仓库: VaibhavAhluwalia / llm-caching-systems

实际实现和实验,旨在使用缓存技术构建快速、可扩展且成本高效的大型语言模型(LLM)应用。

该仓库包括:

  • 演示两种缓存策略的交互式笔记本
  • 一个用于快速设置的 requirements.txt 文件

结论与下一步(第 2 部分)

精确匹配和语义缓存是基础。它们共同使大型语言模型系统更快、更便宜,同时保留大模型的优势。

在本系列的 第 2 部分 中,我们将介绍其他技术。

在你的 LLM 项目中,哪些缓存策略效果最佳?请在下方评论分享你的经验!

与我联系

DEV 社区横幅

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

DEV 标志

Back to Blog

相关文章

阅读更多 »