RAG 检索策略详解
RAG 检索策略详解:向量检索、关键词检索、混合检索
RAG 的核心问题是:用户提了一个问题,怎么从知识库里找到最相关的内容?
目前主流有三种检索策略,各有优劣,实际生产中通常结合使用。
一、关键词检索(Keyword Search)
原理
最传统的搜索方式,核心算法是 BM25(Best Match 25)。
不理解语义,只看字面上有没有出现查询词,并根据词频、文档长度等因素打分排名。
用户问:"iPhone 电池续航怎么样?"
BM25 做的事:
1. 提取关键词:["iPhone", "电池", "续航"]
2. 扫描所有文档块,看哪些块包含这些词
3. 根据词频、稀有程度打分
4. 返回得分最高的 Top-K 块
BM25 打分逻辑(简化版)
某个词越稀有(区分度高)→ 得分加成越大
某个词在块里出现越多次 → 得分越高(但有上限,避免堆关键词)
块的长度越短 → 得分加成(短文本命中更精准)
优点
- 精确匹配强:用户输入专业术语、型号、人名、缩写时,能精准命中
"GPT-4o 的上下文窗口是多少?" → 关键词检索能精准找到含 "GPT-4o" 的文档 ✅ → 向量检索可能返回泛泛讨论 LLM 的文档 ❌ - 速度极快:倒排索引,毫秒级响应,无需 GPU
- 可解释性强:能看到哪些词贡献了得分
- 无需预计算 Embedding:索引构建成本极低
缺点
- 同义词/近义词盲区:字面不同但语义相同的内容找不到
用户问:"手机电池保养方法" 文档里写的是:"移动设备蓄电池维护技巧" → 没有共同词,BM25 得分为 0,召回失败 ❌ - 无法理解语义:不懂”苹果”在不同语境下的含义
- 多语言/跨语言场景差:中英文混合时匹配困难
二、向量检索(Vector Search / Dense Retrieval)
原理
把文本转成向量后,通过计算向量之间的距离来衡量语义相似度,找到语义最接近的 Top-K 个块。
用户问:"手机电池保养方法"
↓ Embedding
查询向量 [0.23, -0.41, ...]
↓ 在向量数据库中计算余弦相似度
文档块向量们:
"移动设备蓄电池维护技巧" → 相似度 0.91 ✅ 命中!
"iPhone 电池续航测试" → 相似度 0.87 ✅ 命中!
"今天天气很好" → 相似度 0.05 ❌ 排除
向量数据库的检索方式
真实场景中文档块可能有几十万甚至上千万个,不可能和每个向量都算一遍相似度。 向量数据库用近似最近邻算法(ANN)来加速:
| 算法 | 代表实现 | 特点 |
|---|---|---|
| HNSW | Qdrant、Weaviate | 速度快,精度高,内存占用大 |
| IVF | FAISS | 可压缩,适合超大规模 |
| LSH | 早期方案 | 实现简单,精度一般 |
这些算法牺牲一点点精度,换来几十倍的速度提升。
优点
- 语义理解强:能找到字面不同但语义相近的内容
- 同义词、近义词、跨语言都能处理
- 泛化能力强:用户问法多变也能召回
缺点
- 精确词匹配弱:遇到专有名词、缩写、型号时容易失准
用户问:"RAG-Fusion 是什么?" → 向量检索可能返回泛泛讨论 RAG 的文档 → 而不是精确介绍 RAG-Fusion 这个技术的文档 ❌ - 需要预计算 Embedding:索引构建成本较高
- 向量漂移问题:更换 Embedding 模型后所有向量需要重建
- 黑盒:不好解释为什么召回了某个结果
三、混合检索(Hybrid Search)
原理
同时跑关键词检索和向量检索,把两路结果合并后重新排序。
两种检索各取所长,互补短板:
- 关键词检索负责”精确命中”
- 向量检索负责”语义理解”
用户问:"iPhone 16 Pro 电池续航怎么样?"
关键词检索结果(BM25):
#1 "iPhone 16 Pro 官方电池规格:4685mAh..." ← 精确命中型号
#2 "iPhone 16 Pro 评测:续航表现..."
#3 "iPhone 16 系列电池对比..."
向量检索结果:
#1 "新款苹果旗舰手机的电池表现分析..." ← 语义相近但无精确词
#2 "iPhone 16 Pro 续航测试报告..."
#3 "苹果手机省电技巧..."
合并 + 重排序后:
#1 "iPhone 16 Pro 续航测试报告..." ← 两路都召回,得分最高
#2 "iPhone 16 Pro 官方电池规格:4685mAh..."
#3 "新款苹果旗舰手机的电池表现分析..."
#4 "iPhone 16 系列电池对比..."
结果合并:RRF 算法
两路结果如何合并成一个排名?最常用的是 RRF(Reciprocal Rank Fusion,倒数排名融合)。
核心公式:
RRF 得分 = Σ 1 / (k + rank_i)
k 通常取 60(平滑系数,避免第一名权重过大)
rank_i 是该文档在第 i 路检索中的排名
举例:
文档A:
关键词检索排名 = 第1名 → 1/(60+1) = 0.0164
向量检索排名 = 第3名 → 1/(60+3) = 0.0159
RRF 总分 = 0.0164 + 0.0159 = 0.0323
文档B:
关键词检索排名 = 第5名 → 1/(60+5) = 0.0154
向量检索排名 = 第1名 → 1/(60+1) = 0.0164
RRF 总分 = 0.0154 + 0.0164 = 0.0318
文档C:
只在关键词检索中出现,排名第2 → 1/(60+2) = 0.0161
向量检索中未出现 → 0
RRF 总分 = 0.0161
最终排名:文档A > 文档B > 文档C
→ 两路都召回的文档排名更靠前 ✅
RRF 的好处是不需要对两路得分做归一化(BM25 和余弦相似度的数值范围完全不同,无法直接相加),只用排名位置就能融合。
三种检索策略对比
| 维度 | 关键词检索(BM25) | 向量检索 | 混合检索 |
|---|---|---|---|
| 语义理解 | ❌ 不理解语义 | ✅ 强 | ✅ 强 |
| 精确词匹配 | ✅ 强 | ❌ 弱 | ✅ 强 |
| 同义词/近义词 | ❌ 不支持 | ✅ 支持 | ✅ 支持 |
| 专有名词/型号 | ✅ 精准 | ⚠️ 一般 | ✅ 精准 |
| 索引构建成本 | 低 | 高(需计算向量) | 高 |
| 查询速度 | 极快 | 快 | 略慢(两路并行) |
| 可解释性 | 高 | 低 | 中 |
| 实现复杂度 | 低 | 中 | 高 |
| 效果 | ⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
各自的典型失败案例
关键词检索的失败场景
用户问:"怎么让笔记本电脑跑得更快?"
文档里: "提升 laptop 性能的 10 个技巧"
→ "笔记本电脑" ≠ "laptop",中英文不匹配
→ "跑得更快" ≠ "性能",表达方式不同
→ BM25 得分极低,召回失败 ❌
向量检索的失败场景
用户问:"Python 3.12 新增了哪些特性?"
文档里: "Python 3.12 发布说明:新增 f-string 嵌套..."
→ 向量检索可能返回 Python 3.10、3.11 的文档(语义相近)
→ 而不是精确的 3.12 版本文档 ❌
→ 版本号这种精确信息,向量检索容易混淆
混合检索解决了什么
上面两个例子用混合检索:
例1:"怎么让笔记本电脑跑得更快?"
关键词检索:得分低(词不匹配)
向量检索: 得分高(语义匹配)✅ 补位成功
例2:"Python 3.12 新增了哪些特性?"
关键词检索:精确命中 "Python 3.12" ✅
向量检索: 可能返回其他版本
混合后: "Python 3.12" 相关文档因关键词加分排名靠前 ✅
实际工程中怎么用?
架构示意
用户提问
│
├──────────────────────────────────┐
↓ ↓
关键词检索(BM25) 向量检索(ANN)
│ │
└──────────────┬───────────────────┘
↓
RRF 结果融合
↓
Reranker 精排(可选)
↓
Top-K 最终结果
↓
构造 Prompt → LLM
主流框架的支持
| 框架/数据库 | 混合检索支持 |
|---|---|
| Elasticsearch | ✅ 内置 BM25 + kNN 混合 |
| Weaviate | ✅ 原生混合检索 |
| Qdrant | ✅ 支持 sparse + dense 混合 |
| LangChain | ✅ EnsembleRetriever |
| LlamaIndex | ✅ QueryFusionRetriever |
一句话总结
- 关键词检索:认字不认意,适合找专有名词和精确术语
- 向量检索:认意不认字,适合语义模糊、表达多变的查询
- 混合检索:两者结合,取长补短,是生产环境的首选方案