← 返回文章列表 / Back to list
unified

缓存层设计

Cache Layer Design

⚠️ 待更新·2026-08-29核验 · 更新时间待核验 · 本文信息可能已过期,请以官方文档为准 更新时间:2026-08-29 · 核验状态:待更新 · 官方溯源待补

背景

免费额度按 RPM/RPD 计,但 30% 的真实流量其实是"同一问题被多人问"。比如"用一句话解释 RAG"被一百个用户各问一次,直接打免费 API 一百次,RPD 当天就被烧光。缓存层把这些重复流量拦下,把免费额度留给真正的新问题。

两层缓存设计

  • 精确缓存(L1):hash(messages + model + temperature) → response,Redis 存 1 小时。命中率通常 20-40%,直接拦截字面重复。
  • 语义缓存(L2):把 prompt 转成 embedding,存入向量库。新请求先 embed,检索 top-k 相似 prompt,若余弦相似度 >0.92 且对应响应仍新鲜,直接返回。可拦截"换个说法问同一件事"。
  • 预热缓存(L3,可选):对高频问题(产品 FAQ、文档摘要)离线预生成响应,放在 CDN 上,请求连网关都不用打。

命中策略与失效

  • 可缓存判定:temperature=0 且不含 tool_calls 的请求可缓存;创意写作默认不可缓存。
  • TTL 分级:事实性问答 24h、代码生成 6h、时效性内容 1h。
  • 主动失效:模型版本升级时清空对应 prefix;用户可在响应 header 看到 x-cache: HIT/MISS
  • 缓存 poisoning 防御:对带密钥的 prompt(如 key=xxx)一律不缓存。

代码示例

import hashlib, redis, numpy as np
r = redis.Redis()
from openai import OpenAI
emb_client = OpenAI()

def cache_key(messages, model):
    return "v1:" + hashlib.sha256(
        (str(messages) + model).encode()).hexdigest()

def get_or_call(messages, model, call_fn):
    k = cache_key(messages, model)
    if cached := r.get(k):
        return cached
    # semantic check: embed and search neighbors
    emb = emb_client.embeddings.create(
        model="text-embedding-3-small",
        input=str(messages)).data[0].embedding
    if near := vector_db.search(emb, threshold=0.92):
        r.setex(k, 3600, near)
        return near
    resp = call_fn()
    r.setex(k, 3600, resp)
    vector_db.upsert(emb, resp)
    return resp

Embedding 模型选型

语义缓存的命中率取决于 embedding 模型。用 OpenAI text-embedding-3-small(1536 维)命中率好但有成本;用本地 bge-small-zh 零成本但准确率略低。关键陷阱:一旦换了 embedding 模型,向量库全部失效,要重新索引。所以网关要支持"双索引并存"——新模型建立新向量库,旧库继续服务直到命中率为零,然后下线。

最佳实践

  • 测量命中率:命中率 <10% 说明缓存策略错了,通常因为 key 设计太粗或 TTL 太短。
  • 缓存大小有上限:LRU + 1GB 上限,防止把 Redis 撑爆。
  • 可关停:为调试方便,提供 x-bypass-cache: true header 一键绕过。
  • 跨用户隔离:不同 client_id 的缓存不要共享,避免敏感数据越过边界。

缓存让免费额度变成"无限"——只要你的问题分布有长尾。

相关文章 / Related

Groq 渠道正式上线:13 个免费模型极速推理(6 对话 + 7 专项)用 APIShare 统一接入多家免费 APIOpenAI 兼容格式统一调用统一日志与监控流式响应统一处理