AI 缓存不只是“记住答案”:从响应复用到 KV Cache 的实践指南

2026-08-19 50 预计阅读时间: 1 分钟
来源: ruanyifeng.com AI 摘要 Original link

Disclaimer: This article is an AI-assisted summary. Read it together with the original source when precision matters. The summary may omit context, version differences, or edge cases and is not official documentation.

预计阅读时间:11 分钟

AI 应用一旦进入真实流量场景,成本、延迟和稳定性往往不再只取决于模型本身。大量请求会重复使用相同的系统提示词、知识库片段、工具描述,甚至直接重复提问。理解不同层次的缓存,才能知道哪些内容可以复用、哪些内容绝不能缓存,以及缓存命中后如何避免返回过期或串租户的数据。

先分清:AI 系统里有几种“缓存”

“AI 缓存”不是单一技术,至少可以拆成下面几类。

1. 请求结果缓存:直接复用完整答案

如果用户的问题、模型版本、系统提示词和关键参数都相同,可以缓存整个模型响应。下次遇到相同请求时,应用直接返回缓存结果,不再调用模型。

这种方式最容易实现,也最容易看到成本下降,但适用范围有限:

  • 适合 FAQ、固定格式的分类、重复的摘要任务;
  • 不适合强依赖实时数据的问题,例如库存、汇率和新闻;
  • temperature、工具列表、用户权限、语言等参数都可能影响答案,必须纳入缓存键;
  • 多租户系统中,用户身份或数据权限必须参与隔离,不能只按问题文本缓存。

2. 提示词前缀缓存:复用重复的上下文

一次模型请求通常包含很长的固定前缀,例如系统指令、工具定义、产品手册和对话历史。即使最后一个用户问题不同,前面的 token 仍可能高度相似。

部分模型服务或推理框架会对这些重复前缀做缓存,从而减少重复的预处理工作。它和“缓存最终答案”不同:模型仍会针对新问题生成回答,只是不用从头处理全部输入。

设计提示词时,可以把稳定内容放在前面,把每次变化的内容放在后面。例如:

[稳定前缀]
系统角色
工具定义
产品规则
长期有效的知识

[动态后缀]
本次用户问题
实时检索结果
当前时间

这不是任何模型都必然支持的优化。实际效果取决于模型服务的缓存策略、前缀匹配规则和计费方式,应通过请求日志或服务商指标验证,而不是凭感觉判断。

3. KV Cache:推理过程中的显存缓存

在自回归生成中,模型每生成一个 token,都需要利用此前 token 的注意力结果。推理引擎会把这些中间结果保存为 KV Cache,避免每一步重新计算整个上下文。

KV Cache 通常由推理框架管理,应用开发者不需要自行实现。但它会直接影响并发量和显存占用:上下文越长、批次越大、模型层数越多,KV Cache 越昂贵。长上下文应用常见的工程选择包括限制上下文长度、压缩历史消息、使用分页式 KV Cache,或在质量允许时减少无关上下文。

4. 检索缓存:缓存向量搜索和外部数据

RAG 应用还可以缓存 embedding、查询改写结果、向量检索结果和文档切片。它们通常比完整答案更容易保持可控,因为最终答案仍可以结合当前问题和模型重新生成。

但文档更新后,旧的检索结果可能失效。缓存键中可以加入知识库版本号、文档更新时间或索引版本,而不是简单设置一个很长的 TTL。

一个可运行的响应缓存示例

下面的示例只使用 Python 标准库,模拟一个“调用模型并缓存结果”的最小实现。call_model 是占位函数,实际使用时可以替换成你的模型 SDK 或 HTTP 请求。

import hashlib
import json
import time
from typing import Any

CACHE: dict[str, tuple[float, str]] = {}
TTL_SECONDS = 60


def make_cache_key(
    model: str,
    messages: list[dict[str, str]],
    temperature: float,
    tenant_id: str,
) -> str:
    """把会影响答案的关键输入统一序列化后生成缓存键。"""
    payload = {
        "model": model,
        "messages": messages,
        "temperature": temperature,
        "tenant_id": tenant_id,
    }
    raw = json.dumps(payload, ensure_ascii=False, sort_keys=True)
    return hashlib.sha256(raw.encode("utf-8")).hexdigest()


def call_model(messages: list[dict[str, str]]) -> str:
    """替换为真实的 LLM API 调用。"""
    question = messages[-1]["content"]
    return f"模拟回答:{question}"


def chat(
    model: str,
    messages: list[dict[str, str]],
    temperature: float,
    tenant_id: str,
) -> tuple[str, bool]:
    key = make_cache_key(model, messages, temperature, tenant_id)
    now = time.time()

    cached = CACHE.get(key)
    if cached is not None:
        expires_at, answer = cached
        if expires_at > now:
            return answer, True
        del CACHE[key]

    answer = call_model(messages)
    CACHE[key] = (now + TTL_SECONDS, answer)
    return answer, False


if __name__ == "__main__":
    request = [{"role": "user", "content": "什么是 KV Cache?"}]

    answer, hit = chat("demo-model", request, 0.2, "tenant-a")
    print(f"hit={hit}, {answer}")

    answer, hit = chat("demo-model", request, 0.2, "tenant-a")
    print(f"hit={hit}, {answer}")

运行方式:

python ai_cache_demo.py

第二次调用会命中缓存。生产环境中,进程内字典应替换为 Redis、Memcached 或云厂商提供的缓存服务,并补充以下字段:

  • 模型版本和提示词版本;
  • 工具定义、检索结果版本和知识库版本;
  • 用户语言、权限范围和租户标识;
  • 超时、缓存命中率、缓存大小和淘汰原因。

缓存键比缓存本身更重要

缓存系统最危险的 bug 通常不是“没有命中”,而是“错误命中”。一个只使用用户问题作为键的实现,很容易把下面两种请求错误地视为相同:

用户 A:请总结我的销售数据
用户 B:请总结我的销售数据

即使文本完全一致,两人的数据权限也可能不同。因此缓存键应反映答案的所有重要输入。可以采用类似下面的分层思路:

hash(
  model_version,
  prompt_version,
  normalized_messages,
  tool_schema_version,
  retrieval_snapshot,
  user_scope_or_tenant,
  generation_parameters
)

并不是所有字段都要原样放进键里。大型检索结果可以先转成内容哈希,实时数据则可以直接禁用缓存或设置极短 TTL。关键是:任何会改变答案语义的输入,都不能被缓存设计忽略。

命中率不是唯一指标

AI 缓存至少需要同时观察四类指标:

  1. 命中率:缓存是否真的减少了模型请求。
  2. 节省成本:命中请求减少了多少输入和输出 token。
  3. 延迟变化:命中后的 P50、P95 延迟是否明显下降。
  4. 正确性风险:是否出现过期答案、权限串数据或格式不一致。

可以把缓存命中当作一种“带条件的复用”,而不是无条件加速。对客服、代码生成、医疗和金融等场景,宁可少命中,也不要为了追求命中率返回不该返回的内容。

落地时的检查清单

  • 先从低风险、高重复率的请求开始,例如固定分类和 FAQ。
  • 对实时数据、个性化数据和权限敏感数据默认谨慎,必要时禁止缓存。
  • 为系统提示词、模型和知识库建立版本号。
  • 给缓存设置 TTL、容量上限和明确的淘汰策略。
  • 记录命中与未命中的原因,但不要把完整隐私内容写入普通日志。
  • 在灰度环境中比较缓存前后的质量、延迟和成本。
  • 区分响应缓存、提示词前缀缓存、检索缓存和 KV Cache,不要用一个指标解释所有收益。

AI 缓存的核心不是“把答案存起来”,而是识别哪些计算可以安全复用。把稳定上下文、动态输入、权限边界和版本信息拆开处理,缓存才能从一个简单的性能技巧,变成可控的模型基础设施。


相关推荐