Featured image of post RAG向量检索策略与召回优化详解

RAG向量检索策略与召回优化详解

深入RAG检索模块:从密集/稀疏检索到混合策略,从查询改写、多路召回到重排序,全面掌握召回优化技术

为什么检索是 RAG 的瓶颈

RAG 系统的回答质量取决于 LLM 生成质量,但生成质量的天花板由检索环节决定。检索回来的文档如果不相关,LLM 再强也给不出正确答案——垃圾进,垃圾出。

RAG 的核心瓶颈不是生成,是召回(Recall)

据统计,企业 RAG 项目失败的案例中,60% 以上根因在检索环节:检索不到、检索不准、检索到的内容噪声太大。本文深入 RAG 检索模块,系统性地梳理向量检索策略和召回优化手段。

基础:文本如何变成向量

嵌入模型(Embedding Model)

嵌入模型将文本映射到高维向量空间。语义相近的文本,向量距离也相近。

1
2
3
"今天天气真好" → [0.12, -0.34, 0.56, ...]  (1536维)
"今日气候宜人" → [0.11, -0.32, 0.54, ...]  (向量距离很近)
"数据库索引优化" → [-0.78, 0.23, 0.91, ...] (向量距离很远)

主流嵌入模型选型

模型 维度 最大输入 多语言 开源
OpenAI text-embedding-3-large 256~3072 8192 token 一般
OpenAI text-embedding-3-small 512~1536 8192 token 一般
BGE-M3 (BAAI) 1024 8192 token
Cohere Embed v3 1024 512 token
jina-embeddings-v3 1024 8192 token
E5-mistral-7b-instruct 4096 32768 token

选型建议:

  • 中文为主:BGE-M3 是首选,多语言能力强
  • 对维度敏感:OpenAI 支持自定义维度,可平衡精度和效率
  • 本地部署:BGE-M3 或 E5 系列
  • 长文档:jina-embeddings-v3 或 E5-mistral

相似度度量

检索的本质是在向量空间中找最接近的 top-k 个向量:

余弦相似度(最常用):

1
cos(u, v) = (u·v) / (|u|·|v|)

值域 [-1, 1],越接近 1 越相似。对方向敏感,不受向量长度影响。

欧氏距离

1
d(u, v) = sqrt(Σ(ui - vi)²)

对向量长度敏感,适合归一化后的向量。

内积(Dot Product)

1
u·v = Σ(ui × vi)

适合预归一化的向量(如 OpenAI 嵌入),计算开销最小。

多数向量数据库默认使用余弦相似度,这也是嵌入模型训练时最常用的一致性目标。

基础检索策略

密集检索(Dense Retrieval)

纯向量检索,直接用 query 向量在向量库中做 ANN(近似最近邻)搜索:

1
2
3
# 伪代码
query_vec = embedding_model.encode("用户的提问")
results = vector_db.search(query_vec, top_k=10)

优点:语义理解强,能召回字面不同但意思相同的文档。

缺点:对专有名词、精确 ID、数字等不敏感。比如"订单号 ORD-2024001"这种情况,纯向量检索容易跑偏。

稀疏检索(Sparse Retrieval / BM25)

传统搜索引擎的核心算法,基于词频-逆文档频率(TF-IDF):

1
BM25(q, d) = Σ IDF(qi) × TF(qi, d) × (k1 + 1) / (TF(qi, d) + k1 × ...)

优点:精确关键词匹配,专有名词、编码、数字等场景表现好。

缺点:不懂语义。搜索"怎么连接到数据库"匹配不到"如何建立数据库连接"。

密集 + 稀疏 = 互补融合,这是目前工业界的主流方案:

1
2
3
4
# 伪代码
dense_results = vector_db.search(query_vec, top_k=20)   # 语义检索
sparse_results = bm25_index.search(query_text, top_k=20) # 关键词检索
final_results = fusion(dense_results, sparse_results)    # 融合排序

融合策略

  • RRF(Reciprocal Rank Fusion)score(d) = Σ 1/(k + rank_i(d)),简单有效,无需调权
  • 加权求和score(d) = α × dense_score + β × sparse_score,需要调超参数
  • 学习融合:用一个小模型学习 dense 和 sparse 的融合权重

RRF 因其无需调参、效果稳定,是目前混合检索最常用的融合策略。

召回优化

查询改写(Query Rewriting)

用户自然的提问方式,和文档的书写风格,往往存在巨大差异。

用户问:“上次那个登录报错的 bug 修好了吗?”

但知识库里的文档写的是:“2026-04-15 修复 auth 模块 session 过期导致 401 的问题”。

直接用原问题检索,大概率召回不到

解决方案——用 LLM 改写查询:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
原始查询 → LLM 改写 → 多个标准化查询 → 检索 → 合并去重

Prompt:
"请将用户的问题改写为多个适合知识库检索的关键词查询。提取关键实体、技术术语和可能的同义表达。
用户问题:上次那个登录报错的 bug 修好了吗?

输出:
1. 登录 报错 bug 修复
2. auth 认证 错误 修复记录
3. session 过期 401 错误 fix
4. 登录失败 问题修复 changelog"

查询改写的常见模式:

模式 做法 适用
关键词提取 LLM 提取关键实体和术语 用户问题口语化
多角度生成 从不同角度生成多个查询 问题模糊、维度多
假设文档 让 LLM 先生成假想答案,用答案当 query 检索 问题复杂需要推理
逐步细化 根据检索结果迭代改写 query 初检不理想时

多路召回(Multi-Channel Recall)

一条检索路径容易漏,多条路径交叉覆盖:

1
2
3
4
5
6
7
8
query
  ├── 路径1:原始 query → 密集检索 → Top-20
  ├── 路径2:改写 query → 密集检索 → Top-20
  ├── 路径3:原始 query → BM25 稀疏检索 → Top-20
  ├── 路径4:提取实体 → 精确过滤检索 → Top-10
  └── 路径5:query 向量 → 跨模态检索 → Top-10
         RRF 融合 → 最终 Top-10

这个架构是目前生产级 RAG 的标配。多路召回的本质是用冗余换覆盖,用融合算法保证最终结果的质量。

重排序(Re-ranking)

初检的 Top-K 只是"粗排"——向量相似度高不代表真正语义相关。重排序用小模型对初检结果做精排。

1
初检 Top-50 → Re-ranker → 精排 Top-5 → 送 LLM 生成

常用 Re-ranker

模型 特点
Cohere Rerank v3 云服务,效果优秀
BGE-Reranker-v2-m3 开源,支持多语言
Cross-Encoder (SBERT) 经典方案,准确但较慢
LLM as Reranker 用 LLM 直接打分排序

Re-ranker 本质是 Cross-Encoder 架构:将 query 和 document 拼接后送入模型,输出一个 0~1 的相关性分数。比向量余弦相似度更准确,但计算开销大,所以只对初检 Top-K 使用。

重排序的关键权衡:K 越大,精排效果越好,但延迟和成本也越高。经验值 20~50 是一个不错的起点。

分段检索与上下文扩展

检索时只返回匹配的 chunk,但 chunk 前后可能有重要上下文。需要在检索后做上下文扩展:

窗口扩展:返回匹配 chunk + 前后各 N 个 chunk

1
检索命中 chunk 5 → 实际返回 chunk 3, 4, 5, 6, 7

句子滑动窗口:以匹配句子为中心,前后各取 M 个句子

父文档检索:检索小 chunk,返回其所属的父文档

这就是 Small-to-Big 策略:用小粒度做检索(避免噪声),用大粒度喂 LLM(保留上下文)。

索引优化

分块策略对检索的影响

分块是 RAG 的"基础工程",分块方式直接决定检索质量:

策略 做法 检索影响
固定 Token 分块 每 512/1024 token 切一块 简单但容易割裂语义
递归字符分割 按段落→句子→词的优先级切 尽量保留自然边界
语义分块 LLM 判断分块边界 效果最好但成本高
层级分块 父子文档多层索引 支持多粒度检索

经验分块参数

  • 文档问答:256~512 token
  • 技术文档:512~1024 token
  • 长文总结:1024~2048 token
  • chunk 重叠度:10%~20%

元数据过滤

纯向量检索是在全库中搜索。加上元数据过滤,可以先缩小搜索范围:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# 伪代码:带过滤的向量检索
results = vector_db.search(
    query_vec,
    top_k=20,
    filter={
        "doc_type": "技术文档",    # 只搜技术文档
        "date": ">2026-01-01",     # 只看今年
        "department": "后端组"      # 只看本组
    }
)

元数据设计原则:

  • 记录时间戳(时效性过滤)
  • 标注文档类型(分类过滤)
  • 保留来源路径(可追溯)
  • 添加自定义标签(业务过滤)

层级索引(Hierarchical Index)

对于大型知识库,全库平面检索效率低、精度差。层级索引先定位范围再精细检索:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
用户 query
第一层:粗粒度检索(文档/章节级)
  → 定位到 "后端-数据库-MySQL" 目录下的 5 篇文档
第二层:细粒度检索(段落/chunk级)
  → 在这 5 篇文档的 chunk 中检索
返回 Top-10

适合文档数量 > 10,000 的大规模场景。

高级检索技术

HyDE(Hypothetical Document Embeddings)

用 LLM 先生成假想答案,再用假想答案的向量去检索:

1
用户问题 → LLM 生成假设答案 → 假设答案向量化 → 检索 → 真实文档

为什么有效?因为真实文档和"假设答案"往往比和"简短问题"在向量空间中更接近。尤其在问答类场景中效果显著。

代价:多一次 LLM 调用,增加延迟和成本。

自查询检索(Self-Query Retrieval)

让 LLM 从用户问题中提取结构化查询条件 + 语义向量:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
用户:"2026 年 3 月之后后端组写的关于 Redis 的文章"

LLM 提取:
{
  "semantic_query": "Redis 文章",
  "filter": {
    "date": ">2026-03-01",
    "department": "后端组"
  }
}

多跳检索(Multi-hop Retrieval)

复杂问题需要多步检索,每一步的结果指导下一步:

1
2
3
4
问题:张三所在部门的数据库负责人是谁?

Hop 1:检索 "张三" → 得到 "张三,后端开发部"
Hop 2:检索 "后端开发部 数据库负责人" → 得到 "李四"

需要 Agent 范式配合——Agent 判断是否需要多跳、何时终止。

查询分解(Query Decomposition)

复杂问题拆解为多个子问题分别检索:

1
2
3
4
5
6
7
问题:Redis Cluster 和 Codis 的对比,以及各自的适用场景

拆解:
1. "Redis Cluster 架构特点优势劣势"
2. "Codis 架构特点优势劣势"
3. "Redis Cluster 适用场景"
4. "Codis 适用场景"

各子问题检索结果汇总去重后送给 LLM。

检索效果评估

关键指标

指标 含义 目标
Recall@K Top-K 中相关文档占全部相关文档的比例 越高越好(>80%)
Precision@K Top-K 中相关文档的比例 越高越好
MRR 第一个相关文档排名的倒数均值 越高越好
NDCG@K 考虑排序位置的归一化指标 越高越好(>0.7)
Hit Rate 至少命中一个相关文档的比例 越高越好(>90%)

构建评估集

需要一个"黄金测试集"——(问题, 正确答案/相关文档)对:

  1. 从历史问答中收集 100~500 个真实问题
  2. 人工标注每个问题对应的正确答案和应该召回的文档
  3. 用评估集测试不同检索策略的效果

没有评估集的调优是盲调——你不知道改了参数到底是变好了还是变坏了。

实践:检索优化清单

按优先级排列的调试清单:

  1. [必做] 检查嵌入模型:模型和语料语言是否匹配?中文用 BGE-M3 通常比 OpenAI 好
  2. [必做] 检查分块质量:切出来的 chunk 语义完整吗?相邻 chunk 之间有信息断层吗?
  3. [必做] 上混合检索:密集 + BM25,用 RRF 融合,这个改动通常能带来 10%~20% 的召回提升
  4. [推荐] 加上重排序:初检 Top-50 + BGE-Reranker 精排 Top-5,对最终答案质量提升显著
  5. [推荐] 查询改写:如果用户提问偏口语化,加一层 LLM 改写
  6. [进阶] 多路召回:在混合检索基础上增加改写查询、实体匹配等召回通道
  7. [进阶] Small-to-Big:小粒度检索 + 父文档上下文扩展
  8. [高阶] HyDE:问答类场景效果明显,但需评估额外延迟

小结

RAG 的检索优化本质是做减法:从海量文档中筛出最相关的那几条,同时尽可能不遗漏。

核心链路:好的嵌入模型 → 合理的分块 → 混合检索(密集+稀疏)→ 重排序 → 上下文扩展 → 喂给 LLM

在这个链路上,每一个环节都有优化空间,但混合检索 + 重排序是高性价比的组合——一个保证覆盖,一个保证精度。在这套基本功之上,再按实际场景评估是否需要查询改写、多路召回、HyDE 等高级策略。

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计