AI Native 应用缓存架构实战:语义缓存、Prompt 缓存与响应缓存的三层设计

9 阅读1分钟

AI Native 应用缓存架构实战:语义缓存、Prompt 缓存与响应缓存的三层设计

当 LLM 账单增速超过用户增速,"再调调模型"往往不是第一解。对大多数生产级 AI 应用而言,设计一套分层缓存架构才是成本与延迟的最高 ROI 杠杆。

一、为什么缓存是 AI Native 架构的必修课

大模型推理的成本结构与传统服务完全不同:每一次请求都要为输入 token 付费,长上下文还会线性放大延迟。更麻烦的是,用户查询天然具有高度重复性——客服机器人里 30% 以上的问题只是换了个问法,RAG 应用里同一份文档被反复加载,Agent 的工具定义在每个请求里都要重传一遍。

缓存的价值因此变得异常直接:

  • 响应缓存能直接省掉一次完整的 LLM 调用;
  • 语义缓存把"同一个意思的不同表达"也纳入命中范围;
  • **Prompt 缓存(前缀缓存)**则让模型厂商帮你复用 system prompt、工具目录和长文档的 KV 计算。

三类缓存不是互斥的。生产系统里它们通常以漏斗状分层组合:先查精确匹配,再查语义相似,最后依赖厂商前缀缓存。本文将逐层拆解原理、实现与选型决策。

二、三层缓存架构总览

一个典型的生产级 LLM 缓存流水线如下:

┌─────────────────────────────────────────────────────────────┐
                        用户请求                              
└──────────────────────┬──────────────────────────────────────┘
                       
                       
┌────────────────────────────────────────┐
 Layer 1: 响应缓存(精确匹配)            
 键:SHA-256(prompt + model + params)   
 命中:直接返回,延迟 < 5ms               
└──────────────┬─────────────────────────┘
                未命中
               
┌────────────────────────────────────────┐
 Layer 2: 语义缓存(向量相似)            
 键:query embedding                     
 命中:返回相似响应,延迟 20–50ms         
└──────────────┬─────────────────────────┘
                未命中
               
┌────────────────────────────────────────┐
 Layer 3: Prompt 缓存(厂商前缀缓存)     
 复用 system prompt / tools / docs  KV 
 命中:节省 50–90% 输入 token 成本        
└──────────────┬─────────────────────────┘
                未命中
               
┌────────────────────────────────────────┐
          完整 LLM 推理调用               
└────────────────────────────────────────┘

关键设计原则:越靠近入口的层越"Lossy",越靠近模型的层越"Lossless"。响应缓存和语义缓存可能返回旧答案,必须接受正确性权衡;Prompt 缓存只复用计算状态,输出仍由模型生成,因此是无损的。

三、Layer 1:响应缓存——把重复的请求拦截在网关层

响应缓存是最简单、收益最确定的一层。它的核心假设是:完全相同的请求应该得到完全相同的回答。在客服 FAQ、文档问答、自动化测试等场景,这一层能命中 5%–15% 的流量。

3.1 缓存键设计

不要用原始 prompt 字符串当 key。一个稳定的 key 必须包含:

  • 模型名称与版本
  • 完整消息列表(按顺序)
  • temperature、top_p、max_tokens 等影响输出的参数
  • 工具定义(如果使用 function calling)

常见错误是把请求 ID、时间戳、用户昵称塞进 system prompt,导致缓存永远失效。

import hashlib, json
from typing import Any

def make_cache_key(model: str, messages: list[dict], params: dict) -> str:
    canonical = json.dumps({
        "model": model,
        "messages": messages,
        "params": {k: params[k] for k in sorted(params) if k != "stream"}
    }, sort_keys=True, ensure_ascii=False)
    return hashlib.sha256(canonical.encode()).hexdigest()

3.2 最小实现

import redis, json

class ResponseCache:
    def __init__(self, redis_url: str, ttl: int = 3600):
        self.r = redis.from_url(redis_url)
        self.ttl = ttl

    def get(self, key: str):
        data = self.r.get(f"llm:resp:{key}")
        return json.loads(data) if data else None

    def set(self, key: str, response: dict):
        self.r.setex(f"llm:resp:{key}", self.ttl, json.dumps(response))

    async def call_with_cache(self, llm_call, key: str):
        cached = self.get(key)
        if cached:
            return {"from_cache": True, **cached}
        resp = await llm_call()
        self.set(key, resp)
        return {"from_cache": False, **resp}

响应缓存的后端通常选 Redis 或 Memcached,追求亚毫秒级查询和低成本。它适合温度参数为 0、答案稳定的场景;一旦 temperature > 0,就要谨慎缓存或按温度分层。

四、Layer 2:语义缓存——让"换种问法"也能命中

语义缓存是 AI 应用区别于传统缓存的最大创新点。它不再比较字符串,而是比较向量空间中的语义距离

4.1 工作原理

  1. 对进入的请求做 embedding;
  2. 在向量数据库中搜索最近邻;
  3. 若余弦相似度超过阈值(通常 0.92–0.97),返回缓存响应;
  4. 否则调用 LLM,并将 (query_embedding, response) 写入缓存。
用户:"怎么重置密码?"
用户:"忘记密码了怎么办?"
        │
        ▼  embedding
   [0.12, -0.05, 0.33, ...]
        │
        ▼  ANN 搜索
   top1: "如何重置账户密码?"  similarity=0.96
        │
        ▼  命中 → 返回缓存答案

4.2 生产实现:基于 Redis Vector Search

Redis 8 已原生支持向量索引,适合不想额外引入专用向量数据库的团队。

import redis
from redis.commands.search.query import Query
from redis.commands.search.field import TextField, VectorField
from redis.commands.search.indexDefinition import IndexDefinition, IndexType

r = redis.Redis(host="localhost", port=6379)
INDEX = "semantic_cache_idx"
DIM = 1536  # text-embedding-3-small

def create_index():
    schema = (
        TextField("query"),
        TextField("response"),
        VectorField("vec", "HNSW", {
            "TYPE": "FLOAT32",
            "DIM": DIM,
            "DISTANCE_METRIC": "COSINE"
        }),
    )
    try:
        r.ft(INDEX).create_index(schema, definition=IndexDefinition(prefix=["cache:"], index_type=IndexType.HASH))
    except Exception:
        pass  # 已存在

def cache_lookup(query_vec: bytes, threshold: float = 0.95):
    q = (Query("*=>[KNN 1 @vec $vec AS score]")
         .return_fields("response", "score")
         .dialect(2).paging(0, 1))
    res = r.ft(INDEX).search(q, query_params={"vec": query_vec})
    if not res.docs:
        return None
    top = res.docs[0]
    sim = 1 - float(top.score)  # Redis 返回距离,转换为相似度
    if sim >= threshold:
        return top.response
    return None

4.3 阈值是唯一的调参杠杆

相似度阈值是语义缓存的核心权衡点:

阈值命中率误命中风险适用场景
0.99极低法律、医疗、合规问答
0.95通用客服、文档问答
0.90内部工具、非关键推荐

不要凭直觉设阈值。正确做法是:准备一批已知"应该命中"和"不应该命中"的 query pair,做阈值扫描,选择误命中率可接受前提下的最大命中率

4.4 多轮对话的上下文陷阱

同一个问题在不同上下文中可能应有不同答案。简单做法:

  • 上下文无关缓存:只对首轮用户问题做语义缓存;
  • 上下文感知缓存:把最近 N 轮对话也嵌入到 key 中,命中率下降但正确性提升。

对于 Agent 场景,更安全的模式是:语义缓存只用于事实型查询,状态型/操作型查询跳过缓存。

五、Layer 3:Prompt 缓存——让厂商替你复用 KV

Prompt 缓存(Prefix Caching)是 2025–2026 年各大厂商重点推出的能力。它不缓存最终回答,而是缓存 prompt 前缀的 KV 状态,让后续请求跳过重复的前向计算。

5.1 三大厂商实现对比

厂商模式最小 token折扣控制粒度
Anthropic Claude显式 cache_control1024–4096读:-90%,写:+25%高,最多 4 个断点
OpenAI自动前缀缓存1024-50%低,无 API 开关
Google Gemini显式 cachedContent1024/4096-75%中,按小时存储计费

5.2 Anthropic 显式缓存示例

import anthropic

client = anthropic.Anthropic()

response = client.messages.create(
    model="claude-sonnet-4-6",
    max_tokens=1024,
    system=[
        {
            "type": "text",
            "text": "你是企业 SaaS 产品的技术支持专家。",
        },
        {
            "type": "text",
            "text": LONG_PRODUCT_DOCS,  # 几千到几万 token 的文档
            "cache_control": {"type": "ephemeral"}  # ← 缓存断点
        }
    ],
    messages=[{"role": "user", "content": user_question}]
)

首次请求会多付 25% 的写成本;从第二次请求开始,该前缀按 0.1 倍价格计费。盈亏平衡点约为同一前缀被调用 2 次

5.3 Prompt 缓存的架构原则

想要高命中率,prompt 结构必须遵循"静态前置、动态后置":

[系统提示 — 静态]          ← 缓存
[工具定义 — 静态]          ← 缓存
[长文档/RAG 上下文 — 静态]  ← 缓存
[对话历史 — 半静态]        ← 部分缓存
[用户当前输入 — 动态]      ← 不缓存

常见翻车点:

  • 在 system prompt 里塞 datetime.now()
  • 工具定义用非确定性字典序序列化;
  • 把用户 ID、会话 ID 放在缓存前缀之前;
  • A/B 测试版本号随机注入。

建议做法:在服务端定期计算并监控 prompt 前缀的哈希,缓存命中率暴跌时第一时间告警。

六、三层组合架构的完整代码骨架

import hashlib, json
import openai
from gptcache import cache as gpt_cache
from gptcache.adapter import openai as cached_openai
from gptcache.embedding import Onnx
from gptcache.similarity_evaluation.distance import SearchDistanceEvaluation

# 1) 响应缓存:Redis 精确匹配
redis_cache = ResponseCache(redis_url="redis://localhost:6379")

# 2) 语义缓存:GPTCache 默认 ONNX 本地嵌入
gpt_cache.init(
    embedding_func=Onnx().to_embeddings,
    similarity_evaluation=SearchDistanceEvaluation(),
)

async def chat(messages, model="gpt-4o", params=None):
    params = params or {}

    # Layer 1: 精确匹配
    exact_key = make_cache_key(model, messages, params)
    hit = redis_cache.get(exact_key)
    if hit:
        return hit

    # Layer 2: 语义缓存(只对单轮、事实型查询启用)
    last_user_msg = messages[-1]["content"] if messages[-1]["role"] == "user" else ""
    if len(messages) <= 2:
        semantic_hit = cached_openai.semantic_search(last_user_msg, top_k=1)
        if semantic_hit and semantic_hit.score > 0.95:
            return semantic_hit.response

    # Layer 3 + 完整调用:把静态内容前置以命中厂商前缀缓存
    response = await cached_openai.ChatCompletion.acreate(
        model=model,
        messages=messages,
        temperature=params.get("temperature", 0),
    )

    # 回填响应缓存
    redis_cache.set(exact_key, response)
    return response

七、基础设施选型对比

维度Redis / KeyDB专用向量数据库(Qdrant/Milvus)GPTCache
适用层响应缓存语义缓存语义缓存封装
查询延迟< 5ms10–30ms(含 embedding)依赖底层存储
容量与扩展中等,适合热数据十亿级向量,分布式中等
运维成本中–高
混合搜索Redis Search 支持原生支持需配置

一个务实的起步方案:

  • 响应缓存:Redis(或 Upstash 用于 serverless);
  • 语义缓存:小规模用 GPTCache + FAISS/SQLite,生产用 Redis Vector Search 或 Qdrant;
  • Prompt 缓存:直接利用厂商能力,无需自建基础设施。

八、性能优化与 ROI 量化

8.1 成本模型

假设某客服系统日均 10 万次请求,平均 prompt 4000 token,输出 800 token,使用 Claude Sonnet(输入 3/MTok,输出3/MTok,输出 15/MTok):

方案日成本估算备注
无缓存$1,920全额计费
仅响应缓存(10% 命中)$1,728省 $192
+ 语义缓存(额外 40% 命中)$921省 $1,000
+ Prompt 缓存(前缀 3000 token,80% 命中)~$450省近 $1,500

8.2 关键优化建议

  1. 先做响应缓存,再做语义缓存。响应缓存实现成本最低,且不误命中。
  2. 语义缓存只缓存确定性回答。对价格、政策、实时数据设置短 TTL 或跳过缓存。
  3. Prompt 缓存的静态前缀越大越划算。把工具定义、RAG 文档、few-shot 示例都往前放。
  4. 监控三个命中率。每一层都要独立计数,不要只看总命中率。
  5. 建立缓存质量评估循环。定期抽样 LLM-as-a-judge 或人工评审语义缓存命中的正确性。

九、常见陷阱与安全边界

  • 数据隔离:不要把用户 A 的个性化响应缓存给用户 B。按 user_idtenant_id 分区。
  • PII 与合规:缓存中可能存储敏感对话,需加密、设置 TTL,并满足数据驻留要求。
  • 缓存投毒:恶意用户可能通过构造相似 query 让系统返回错误缓存。结合输入过滤与阈值保守策略。
  • 陈旧性:文档更新后,旧缓存可能给出过期答案。关键领域使用 tag-based 失效或短 TTL。

十、结语

AI Native 应用的缓存不是传统缓存的简单迁移,而是围绕"token 经济性"重新设计的一套分层体系。响应缓存解决重复,语义缓存解决同义,Prompt 缓存解决前缀冗余。三者叠加,才能在不牺牲体验的前提下,把 LLM 成本真正压下来。

最值得记住的一点:缓存是一项正确性工程,不是纯基础设施工程。每一层缓存都意味着"有时不调用模型",而模型的缺席必须由严格的评估、监控和失效策略来兜底。只有把成本、延迟、正确性三者的关系量化到仪表盘上,缓存才算真正落地。