Redis 缓存在 RAG 中:规范化查询、语义陷阱与实际有效的做法
请提供您希望翻译的完整文本内容,我会按照要求将其翻译成简体中文并保留原始的格式、代码块和链接。
为什么 Redis 缓存对 RAG 有效
RAG 流水线成本高,因为它们反复执行:
- 嵌入生成
- 向量检索
- 上下文组装
- LLM 推理
对于许多用户问题——尤其是在内部工具中——答案在请求之间不会改变。Redis 提供:
- 亚毫秒读取
- 基于 TTL 的驱逐
- 简单的运维模型
- 可预测的成本
什么是规范化查询的真正含义
问题
相同意图的不同表述会生成不同的缓存键:
"Explain docker networking"
"Can you explain Docker networking?"
"docker networking explained"
如果对原始查询进行哈希,Redis 会把每个查询视为不同的键,导致缓存命中率低。
目标
- 提高缓存命中率
- 避免返回错误答案
安全的规范化操作
- 小写化
- 去除首尾空白
- 删除标点符号
- 合并填充短语
危险的规范化操作
- 删除数字
- 合并版本号字符串
- 替换领域术语
- 同义词替换
- 语义猜测
在 RAG 场景中,错误的缓存命中比一次未命中更糟糕。
import re
FILLER_PHRASES = ["can you", "please", "tell me", "explain"]
def normalize_query(query: str) -> str:
q = query.lower().strip()
for phrase in FILLER_PHRASES:
q = q.replace(phrase, "")
q = re.sub(r"[^\w\s]", "", q) # remove punctuation
q = re.sub(r"\s+", " ", q) # collapse whitespace
return q.strip()
此处有意避免的内容:
- NLP 停用词列表
- 嵌入
- 同义词扩展
结果:可预测且正确的规范化。
构建稳健的缓存键
在规范化文本之外,还要包括模型和检索配置:
cache_key = hash(
model_name +
normalized_query +
retrieval_config
)
这可以防止:
- 在不同模型之间重复使用答案
- 混合检索策略
- 静默的正确性错误
语义缓存:何时可接受
语义缓存可以在以下情况下使用:
- 问题是常见问答(FAQ)
- 答案是通用的
- 正确性容忍度较高
- 存在精确缓存的回退方案
安全模式: 双层缓存
- 精确缓存 – 使用规范化查询(权威)
- 语义缓存 – 可选、受保护,绝不作为权威来源
结构化查询的意图级规范化
当 RAG 处理非文本查询(SQL、Athena、API、日志、指标)时,“查询”由意图和约束组成。应缓存规范化的表示,而不是原始文本。
{
"source": "athena",
"table": "deployments",
"metrics": ["count"],
"filters": {
"status": "FAILED",
"time_range": "LAST_7_DAYS"
}
}
对规范化的 JSON(例如,在对键进行排序后)进行哈希,即可得到确定性的缓存键。
最终设置
- Redis 用于快速缓存存储
- Conservative text normalization 用于自由形式查询
- Intent‑level normalization 用于结构化查询
- No semantic caching 用于关键路径
- TTL 与数据新鲜度保持一致
结果
- ~40 % 成本降低
- 延迟降低
- 零正确性回归
- 可预测的行为
最重要的是,系统重新赢得了信任。
要点
- 关键在于规范化形式而非意义
- 过度规范化会悄然破坏 RAG
- 语义缓存应当是可选的,绝不默认启用
- 结构化查询需要意图层面的规范化
- 确定性胜过巧妙性
在 RAG 中缓存不仅仅是为了节省 token,更是为了保持正确性。只要规范化做得恰当,Redis 就能成为超级力量。
P.S. 这是一道看似简单实则困难的问题,没有一刀切的解决方案。不同的 RAG 配置需要依据上下文的检索、结构化和验证方式采用不同的规范化策略。这里描述的方法是概念性指南,而非可直接使用的实现。