Skip to content

第 4 章 · 检索优化技术:从"能用"到"好用"

🎯 本章目标:掌握把 RAG 召回从 60% 拉到 90%+ 的核心工程技术,理解每种技术的"为什么"和"什么时候用"。

💡 核心思路:基础 RAG = 单一向量检索。生产级 RAG = 查询优化 + 混合检索 + Rerank + 元数据过滤 的组合拳。


目录

  1. 为什么需要检索优化
  2. 查询改写(Query Rewriting)
  3. 混合检索(Hybrid Search)
  4. Rerank 重排(精排)
  5. HyDE:先脑补答案再检索
  6. 多查询生成(Multi-Query)
  7. 元数据过滤与混合检索
  8. Context 压缩与去重
  9. 综合优化路径
  10. 思考题

1. 为什么需要检索优化

1.1 基础 RAG 的常见困境

困境 1:查询和文档"词不达意"

用户问:「怎么解决程序闪退?」
文档里写:「应用程序异常退出(崩溃)的排查方法...」

→ 用户用的是"闪退",文档用的是"异常退出"
→ 纯向量检索可能漏掉这条

困境 2:短查询语义不明

用户问:「退货」
→ 是想问退货流程?还是退货政策?还是退货状态查询?
→ 系统迷茫,乱召回

困境 3:复杂多意图问题

用户问:「比较一下产品 A 和产品 B 的价格、库存和发货时间」
→ 需要同时找 6 个事实
→ 单次向量检索只能找一个角度

困境 4:相似但不相关

用户问:「Python 的装饰器怎么用?」
召回:「Java 的注解(Annotation)」  ← 概念相似但语言不对

1.2 检索优化的四大武器


2. 查询改写 Query Rewriting

2.1 什么是查询改写

用 LLM 把用户原始查询改写成更利于检索的版本

2.2 改写的几种类型

类型 A:拼写纠错和规范化

原查询:「我家猫米发烧了怎么办」
改写后:「我家猫咪发烧了怎么办」

类型 B:同义词扩展

原查询:「程序闪退」
改写后:「程序闪退 / 应用崩溃 / 异常退出 / Crash」

类型 C:补全省略信息

对话历史:
  用户:「我家有只布偶猫」
  AI:「好的」
  用户:「它生病了怎么办?」  ← "它" 指代不明

改写后:「我家布偶猫生病了怎么办?」

类型 D:多轮意图融合

对话历史:
  用户:「介绍下你们的产品 A」
  AI:「产品 A 是......」
  用户:「价格呢?」  ← 缺主语

改写后:「产品 A 的价格是多少?」

类型 E:分解复杂问题

原查询:「比较产品 A 和产品 B 的价格和库存」
分解为:
  - "产品 A 的价格"
  - "产品 A 的库存"
  - "产品 B 的价格"
  - "产品 B 的库存"
(分别检索后再综合)

2.3 改写的 Prompt 模板

你是一个查询改写助手。
请根据【对话历史】和【当前查询】,生成一个独立、完整、清晰的查询,
便于在知识库中检索相关信息。

要求:
1. 消解指代("它"、"那个"等替换为具体实体)
2. 补全省略的主语/宾语
3. 修正明显的拼写错误
4. 保持查询的原意,不要扩展额外含义

【对话历史】
{history}

【当前查询】
{current_query}

【改写后的查询】

2.4 改写的代价

优点缺点
✅ 大幅提升召回率❌ 多一次 LLM 调用,延迟增加 100-500ms
✅ 解决短查询 / 多轮指代问题❌ 改写错误可能误导检索
✅ 提升用户体验❌ 增加 Token 成本

💡 建议:用小而快的模型(如 GPT-4o-mini、Qwen-Turbo)做改写,平衡成本和效果。


3.1 单一向量检索的局限

向量检索擅长 语义匹配,但有两个软肋:

软肋例子
专有名词、术语不敏感用户搜 "API-X-2024 错误码",向量可能匹到无关的 API 概念
精确关键词不重视用户搜 "iPhone 15 Pro Max",可能返回 iPhone 12 相关

3.2 关键词检索(BM25)回归

BM25 是传统搜索引擎(Elasticsearch、Lucene)的核心算法,本质是 "关键词频率 + 文档长度归一化"。

优势

  • 精确匹配术语、产品名、错误码
  • 速度极快(倒排索引)
  • 不需要 GPU

劣势

  • 不理解同义词("汽车" 搜不到 "车辆")
  • 不理解上下文

3.3 混合检索:1 + 1 > 2

混合检索 = 向量检索 + BM25 检索,结果融合排序

工作流程

融合算法:RRF(Reciprocal Rank Fusion)

最常用、最简单有效的融合方法。

公式(不需要记,理解思路即可):

RRF_score(doc) = sum(1 / (k + rank_i(doc)))

其中:
  rank_i = 该文档在第 i 路检索中的排名
  k = 常数,通常取 60

直观例子

用户查询:「iPhone 15 Pro 防水等级」

向量检索 Top-3:
  Rank 1: "iPhone 15 Pro 拥有 IP68 防水防尘等级..."
  Rank 2: "iPhone 14 Pro 的防水性能..."
  Rank 3: "智能手机防水等级标准 IP67 / IP68..."

BM25 检索 Top-3:
  Rank 1: "iPhone 15 Pro 拥有 IP68 防水防尘等级..."  ← 关键词完全匹配
  Rank 2: "iPhone 15 Pro Max 规格清单..."
  Rank 3: "苹果官方 iPhone 15 系列发布会..."

RRF 融合后:
  → "iPhone 15 Pro 拥有 IP68 防水防尘等级..." 在两路都排第一,最终也是第一
  → 其他文档按 RRF 公式重新排序

适用场景

是否推荐场景
✅ 强烈推荐包含大量术语、产品名、错误码的文档
✅ 强烈推荐电商、企业知识库、技术文档
✅ 推荐几乎所有生产 RAG
⭕ 可选纯口语化文本

💡 业界共识:单一向量检索 → 混合检索,召回率通常提升 10%-30%

3.4 工程实现选择

方案实现
Elasticsearch + 向量字段ES 8.x 原生支持
Weaviate内置 hybrid search API
Qdrant多查询 + 客户端融合
OpenSearchk-NN + BM25
自己拼装向量库召回 + ES 召回 + Python 写 RRF

4. Rerank 重排(精排)

4.1 为什么 Rerank 是性价比之王?

检索(粗排)找到的 Top-50,可能只有 5 个真正相关。Rerank 帮你把这 5 个挑出来。

数据说话

业界经验数据:

优化方法召回质量提升实现复杂度
加 Rerank+15%-30%⭐⭐(接 API)
加混合检索+10%-20%⭐⭐⭐
切分调优+5%-15%⭐⭐⭐⭐
换更好的 Embedding+5%-10%⭐⭐

Rerank 是性价比最高的优化

4.2 Rerank vs Embedding 的本质区别

工作机制不同

Embedding(Bi-Encoder)Rerank(Cross-Encoder)
输入查询、文档独立编码查询和文档拼在一起编码
优势快(向量预计算可缓存)准(能捕捉查询-文档细粒度交互)
劣势准确度有上限慢(每对都要算)
典型耗时1-10ms/查询50-200ms/Top-50
用法粗排(亿级数据)精排(候选 50-100)

💡 类比

  • Embedding 像看相亲简介(快速筛选,但只能看个大概)
  • Rerank 像真人见面(深度交流,了解契合度)

4.3 主流 Rerank 方案

方案类型特点
Cohere Rerank v3API易用,多语支持,效果好
bge-reranker-v2-m3开源中英文,免费,效果接近 SOTA
bge-reranker-large开源中文优秀
Jina RerankerAPI多语
Voyage RerankAPI垂直领域强
LLM-as-Reranker用 GPT-4 直接打分灵活但慢且贵

4.4 Rerank 接入示意

4.5 Rerank 实战参数

参数建议
粗排 Top-N50-100(太少召回不全,太多 Rerank 慢)
精排 Top-K3-10(喂给 LLM 的最终数量)
相关性阈值< 0.3 直接丢弃(防止低质量片段进入 Context)

5. HyDE:先脑补答案再检索

5.1 HyDE 是什么

HyDE = Hypothetical Document Embeddings

核心思路:用户的查询往往很短、信息量小,直接 Embedding 检索效果差。 先用 LLM 根据查询生成一段假想答案,用假想答案的 Embedding 去检索。

5.2 为什么 HyDE 有效?

直观理解

用户查询:「光合作用」  (太短,语义弱)
     ↓ LLM 脑补一个假想答案
假想答案:「光合作用是植物利用阳光、水和二氧化碳合成葡萄糖和氧气的过程。
            叶绿体是光合作用的主要场所。光合作用分为光反应和暗反应两个阶段...」
     ↓ 用假想答案做 Embedding,再去检索
召回:真实文档中关于光合作用的详细章节

💡 本质:把"短查询"转成"伪文档",让"查询的向量"和"目标文档的向量"分布更接近。

5.3 工作流程

5.4 HyDE 的 Prompt 模板

请根据以下问题,写一段详细的、像维基百科条目的回答。
注意:你的回答不必完全正确,目的是用于检索相关文档。

问题:{user_query}

回答:

5.5 HyDE 适用场景

适用不适用
✅ 短查询("光合作用")❌ 长查询本身已经很完整
✅ 学术、知识查询❌ 实时事件查询(LLM 不知道)
✅ 跨语言查询❌ 严格事实查询(LLM 可能脑补错)

5.6 HyDE 的副作用

⚠️ HyDE 不是银弹

问题解释
延迟增加多一次 LLM 调用(500ms-2s)
成本增加多消耗 Token
幻觉传染LLM 脑补的内容可能完全错,导致检索方向跑偏
不总有效对长查询提升不明显

6. 多查询生成 Multi-Query

6.1 思路

一个问题从多个角度同时检索,结果合并

用户查询:「Python 装饰器如何实现?」
     ↓ LLM 生成多个变体
变体 1:「Python decorator 的工作原理是什么?」
变体 2:「Python 装饰器的语法和用法示例」
变体 3:「如何定义一个 Python 装饰器函数?」
     ↓ 每个变体分别检索
     ↓ 结果去重 + 融合
最终 Top-K

6.2 为什么有效?

不同表述能命中不同的文档片段:

变体 1 命中:"decorator 原理章节"
变体 2 命中:"语法示例代码"
变体 3 命中:"函数定义教程"

合并后召回更全面

6.3 工作流程

6.4 Multi-Query 的进阶版:RAG-Fusion

RAG-Fusion = Multi-Query + RRF 融合排序

1. LLM 生成 N 个变体查询
2. 每个变体独立检索 Top-K
3. 用 RRF 算法融合所有结果
4. 输出全局 Top-K

💡 在多个公开评测中,RAG-Fusion 比单一查询召回率提升 15%-25%

6.5 Multi-Query 的取舍

优点缺点
✅ 召回更全❌ 调用次数 N 倍 → 成本 N 倍
✅ 对模糊查询友好❌ 延迟增加
✅ 适合开放性问题❌ 对精确查询可能引入噪声

7. 元数据过滤与混合检索

7.1 为什么需要元数据过滤

真实场景

公司知识库有 100 万文档,包括:
  - 2020-2024 年的所有文档
  - 涵盖产品 A、B、C、D 四个产品线
  - 分中文版、英文版

用户问:「产品 A 在 2024 年新功能有哪些?」

如果不过滤,可能召回到:
  ❌ 产品 B 的 2024 年新功能(产品错)
  ❌ 产品 A 的 2022 年新功能(时间错)
  ❌ 产品 A 的 2024 年英文版新功能(语言可能错)

7.2 元数据过滤

入库时给每个 Chunk 打标签,检索时按标签过滤。

入库时

{
  vector: [...],
  metadata: {
    product: "A",
    year: 2024,
    language: "zh",
    department: "engineering",
    update_time: "2024-09-15"
  }
}

检索时

vector_db.search(
    query_vector=q,
    filter={
        "product": "A",
        "year": 2024,
        "language": "zh"
    },
    top_k=5
)

7.3 自然语言 → 结构化过滤(Self-Query)

让 LLM 自动从用户查询中提取过滤条件。

用户查询:「2024 年产品 A 的中文版新功能」

LLM 解析:
{
  semantic_query: "新功能",
  filters: {
    product: "A",
    year: 2024,
    language: "zh"
  }
}

→ 检索时既做语义匹配,又做精确过滤

💡 LangChain 里叫 SelfQueryRetriever,LlamaIndex 里叫 AutoRetriever

7.4 元数据设计的最佳实践

字段类型用途
source字符串来源文件名(引用追溯)
page数字页码
section字符串章节标题
category枚举分类("产品" / "运营" / "法务")
tags列表标签(["发烧", "猫咪", "急救"])
update_time时间戳更新时间(时效性过滤)
access_level枚举权限("public" / "internal" / "secret")
language枚举语言("zh" / "en")
author字符串作者

⚠️ 关键经验:元数据入库时就要设计好,后期补加要全量重新入库。


8. Context 压缩与去重

8.1 问题:Context 冗余

Top-5 召回到:
  Chunk 1: "猫咪发烧的处理方法包括降温、补水、送医..."
  Chunk 2: "猫咪发烧时,可以采取降温、补水的方法..."  ← 和 1 大量重复
  Chunk 3: "宠物发烧时家长常用的处理方式有..."        ← 也重复
  Chunk 4: "猫咪疫苗副作用..."                       ← 不相关
  Chunk 5: "猫咪体温正常范围是 38-39 度"

→ 喂给 LLM 的 Context 浪费严重

8.2 解决方案

方案 A:去重

  • 基于 Chunk 之间的相似度,相似度 > 0.9 的视为重复,只保留一个
  • 或者基于文本字面去重

方案 B:Context 压缩(Contextual Compression)

用 LLM 把召回的每个 Chunk 抽取出和查询真正相关的部分,丢弃无关内容。

原 Chunk: "猫咪发烧的处理方法包括降温、补水、送医。
          此外,猫咪还需要定期接种疫苗,注意饮食卫生。
          注意环境清洁,避免接触流浪动物。"

LLM 压缩(针对查询"猫咪发烧怎么办"):
"猫咪发烧的处理方法包括降温、补水、送医。"

方案 C:相关性过滤

  • 用 Rerank 分数过滤掉低相关性(如 < 0.3)的 Chunk
  • 避免无关片段进入 Context 干扰 LLM

8.3 LangChain 的 Compression 工具

工具作用
LLMChainExtractor用 LLM 抽取相关句子
LLMChainFilter用 LLM 判断是否保留整个 Chunk
EmbeddingsFilter按相似度阈值过滤
DocumentCompressorPipeline组合多种压缩器

9. 综合优化路径

9.1 从 0 到生产级的演进

9.2 优化优先级建议

优先级优化项投入收益
🔥 P0加 Rerank1 天+15-30%
🔥 P0切分调优1-3 天+5-15%
🌟 P1混合检索2-5 天+10-20%
🌟 P1元数据过滤1-2 天+10%(特定场景 +30%)
⭐ P2查询改写1-2 天+5-10%
⭐ P2Context 压缩1-2 天降低成本 30%+
💎 P3HyDE / Multi-Query1 天+5-15%(场景相关)

9.3 优化前后对比示例

指标基础 RAG优化后
Recall@560%92%
答案准确率65%88%
延迟800ms1.5s
单次成本$0.002$0.005

💡 典型权衡:质量 ↑ → 延迟 ↑ + 成本 ↑。需要根据业务需求平衡。

9.4 一个生产级 RAG Pipeline 完整图


10. 思考题

  1. Rerank 这么有效,为什么不直接用 Rerank 检索整个知识库,省去向量检索?

    参考答案
    • Rerank(Cross-Encoder)必须把"查询 + 每个候选文档"拼起来过模型,复杂度 O(N×D),N 个候选要算 N 次。
    • 知识库百万级文档,Rerank 每个都算一遍要几分钟,根本不可行。
    • Bi-Encoder(Embedding + 向量检索)通过向量预计算 + ANN,复杂度近似 O(log N),可以毫秒级检索。
    • 结论:向量检索负责 "快速从亿级中选 Top-50",Rerank 负责 "从 50 中精选 Top-5"。两者互补。
  2. 混合检索的两路结果(向量 + BM25),如果一个文档只在向量检索里出现没在 BM25 里,怎么处理?

    参考答案
    • 用 RRF 公式时,没出现的那一路视为排名无穷大(贡献 0 分)。
    • 因此 RRF 自动处理 "只在一路出现" 的情况,不需要特殊逻辑。
    • 优点:单路漏召的文档,另一路能补回来。
  3. HyDE 在哪种场景下会变得有害?

    参考答案
    • 新事实查询:LLM 不知道的最新信息,HyDE 脑补的答案完全错,导致检索跑偏。
    • 严格事实查询:例如 "2024 年 10 月 1 日股价",LLM 脑补会瞎写数字,检索方向受污染。
    • 本身已经很完整的长查询:HyDE 收益小,反而增加延迟。
    • 小众领域 / 私有知识:LLM 没学过的内容,脑补质量低。
  4. 如果我的 RAG 召回经常返回"似是而非"的文档(看起来相关其实不是),怎么诊断和优化?

    参考答案

    诊断步骤:

    1. 检查切分:是不是 Chunk 太大,多个主题混在一起?
    2. 检查 Embedding:是不是模型对你的领域不敏感?跑个垂直领域评测看 Recall@K。
    3. 检查相似度分布:召回的 Top-5 相似度都很低(< 0.5)?说明知识库里根本没相关内容,需要补数据。

    优化方案:

    • 加 Rerank:用 Cross-Encoder 把"看起来相关"和"真正相关"区分开。
    • 加元数据过滤:缩小检索范围,减少干扰。
    • 加阈值:相似度 < X 直接返回 "不知道",避免硬答。
    • 换 Embedding:试 BGE-M3 或 Cohere v3。

📌 本章小结

你应该已经知道
查询改写解决"短查询"和"多轮指代"问题
混合检索(向量 + BM25)是生产标配
RRF 是融合多路检索结果的事实标准
Rerank 是性价比最高的优化
Bi-Encoder 和 Cross-Encoder 的本质区别
HyDE 适用场景和坑
Multi-Query 和 RAG-Fusion 的关系
元数据设计对检索精度的影响
一个生产级 RAG Pipeline 包含哪些组件

🚀 下一步

工程优化已经到位,下一章看 学术前沿 的高级 RAG 架构,让你的简历多几个亮点:

👉 第 5 章 · 高级 RAG 模式