================================================================================

RAG 查询优化技术学习指南

副标题:从入门到精通


📖 本书简介

本书系统地介绍了 RAG(检索增强生成)系统中的查询优化技术,从基础概念到高级应用,从理论原理到实践代码,帮助读者全面掌握提升 RAG 检索效果的核心技术。

🎯 适合读者

  • 初学者:想了解 RAG 检索优化的入门读者
  • 开发者:正在构建 RAG 系统的工程师
  • 面试官:想了解 RAG 技术面试要点的招聘者
  • 研究者:关注信息检索与 LLM 结合的研究人员

📚 核心内容

  1. Query Rewriting(查询重写) - 让问题更清晰
  2. HyDE(假设文档嵌入) - 用答案搜答案
  3. Multi-Query(多查询检索) - 多角度问问题
  4. 评估指标 - MRR、NDCG 详解
  5. 结果融合 - RRF 倒数排名融合
  6. 五大策略 - 速度/召回/平衡/成本/质量优先

💡 本书特色

  • 12 岁能听懂:复杂概念用比喻和故事解释
  • 面试导向:每个章节都有面试回答模板
  • 代码实战:完整的 Python 实现示例
  • 成本分析:详细的性能与成本对比
  • 策略选择:不同场景的最优方案

版本信息

  • 版本:v1.0
  • 整理日期:2026-04-13

================================================================================

RAG 查询优化技术 学习指南 目录

第一部分:基础概念

第 1 章 Query Rewriting(查询重写)

文件: 02-query-rewriting.md

  • 通俗理解(12 岁能听懂)
  • 三种主要方法
  • 为什么需要它
  • 面试回答指南(3 分钟版本)
  • 技术架构
  • 常见追问

第 2 章 评估指标:MRR 和 NDCG

文件: 03-evaluation-metrics.md

  • MRR 通俗理解与计算
  • NDCG 通俗理解与计算
  • MRR vs NDCG 对比
  • 面试回答模板

第二部分:核心技术

第 3 章 HyDE(假设文档嵌入)

文件: 04-hyde.md

  • 通俗理解(12 岁能听懂)
  • 面试回答指南
  • 为什么有效(4 个原因)
  • 反直觉的事实
  • 适用场景
  • 与 Query Rewriting 对比

第 4 章 Multi-Query(多查询检索)

文件: 05-multi-query.md

  • 通俗理解(12 岁能听懂)
  • 核心概念拆解
  • 与 Query Rewriting 对比
  • 面试回答指南
  • 优势与成本分析(6-8 倍详解)
  • 优化策略

第 5 章 RRF(倒数排名融合)

文件: 06-rrf.md

  • 什么是 RRF
  • 计算公式
  • 为什么 k=60
  • 完整计算例子
  • RRF 的优点
  • 代码实现

第三部分:实战策略

第 6 章 五大 RAG 策略

文件: 07-strategies.md

  • 速度优先策略(Speed-First)
  • 召回率优先策略(Recall-First)
  • 平衡型策略(Balanced)
  • 成本优先策略(Cost-First)
  • 质量优先策略(Quality-First)
  • 五策略对比总表
  • Fallback 方案对比
  • 策略选择决策树

第 7 章 完整流程评价

文件: 08-pipeline.md

  • Multi-Query → Top-30 → Rerank → Top-5 流程详解
  • 优势分析
  • 潜在问题
  • 优化建议
  • 完整代码实现

第四部分:综合对比

第 8 章 三种技术对比

文件: 09-comparison.md

  • 核心思想对比
  • 工作流程对比
  • 详细成本分析
  • 优势分析
  • 适用场景对比
  • 组合使用策略
  • 面试回答模板

附录

附录 A 术语表

文件: 10-appendix-glossary.md

  • RAG 相关术语
  • 检索优化术语
  • 评估指标术语
  • 模型相关术语
  • 常见缩写

附录 B 代码片段速查

文件: 11-appendix-code.md

  • Query Rewriting 实现
  • HyDE 实现
  • Multi-Query 实现
  • RRF 实现
  • 完整 RAG 策略实现
  • 评估指标计算

附录 C 面试速查表

文件: 12-appendix-interview.md

  • 概念定义速查
  • 技术对比速查
  • 场景选择速查
  • 面试回答模板
  • 常见问题速查
  • 面试准备清单

附录 D 参考资料

文件: 13-appendix-references.md

  • 论文与文章
  • 开源项目
  • API 文档
  • 学习资源
  • 工具与库

快速索引

问题 章节
查询太模糊怎么办? 第 1 章 Query Rewriting
怎么评估检索效果? 第 2 章 评估指标
什么是 HyDE? 第 3 章 HyDE
什么是 Multi-Query? 第 4 章 Multi-Query
多个结果怎么融合? 第 5 章 RRF
如何选择策略? 第 6 章 五大策略
完整流程怎么样? 第 7 章 流程评价
三种技术怎么选? 第 8 章 技术对比
面试怎么准备? 附录 C 面试速查表

本书文件列表

序号 文件名 章节
00 00-封面.md 封面与简介
01 01-目录.md 本目录
02 02-query-rewriting.md 第 1 章
03 03-evaluation-metrics.md 第 2 章
04 04-hyde.md 第 3 章
05 05-multi-query.md 第 4 章
06 06-rrf.md 第 5 章
07 07-strategies.md 第 6 章
08 08-pipeline.md 第 7 章
09 09-comparison.md 第 8 章
10 10-appendix-glossary.md 附录 A
11 11-appendix-code.md 附录 B
12 12-appendix-interview.md 附录 C
13 13-appendix-references.md 附录 D

阅读建议

📖 系统学习

按顺序阅读:00 → 01 → 02 → … → 13

🎯 问题导向

根据上表「快速索引」查阅对应章节

💼 面试准备

重点阅读:

  • 第 2-5 章(核心技术)
  • 第 8 章(技术对比)
  • 附录 C(面试速查表)

================================================================================

第 1 章 Query Rewriting(查询重写)

1.1 通俗理解(12 岁能听懂)

问题从这里开始 📚

想象你有一个特别聪明的图书管理员朋友,但你不会说话,总是说不清楚自己想要什么书。

假设你想找关于"苹果"的资料,但你只说了两个字:

“苹果”

图书管理员会想:

  • 是要找苹果水果的营养资料?
  • 还是要找苹果公司的产品?
  • 还是要找苹果手机的评测?

你说的话太简单了,图书管理员帮不到你。

Query Rewriting 就像一个"翻译官" 🔄

查询重写的作用就是:把你说的简单问题,翻译成清楚的问题

你说的 重写后变成
“苹果” “苹果公司的历史和主要产品”
“怎么胖了” “如何通过饮食和运动健康增重”
“python list” “Python 编程语言中列表的使用方法”

三种主要方法 🔧

1. 补全信息

把缺少的部分补上。

  • 你说:“怎么变强?”
  • 重写:“如何通过锻炼让肌肉变强?”
2. 换个说法

用更容易理解的方式说同一件事。

  • 你说:“AI 咋回事?”
  • 重写:“人工智能的基本原理是什么?”
3. 拆开问

一个大问题拆成几个小问题。

  • 你说:“怎么学好编程?”
  • 重写:
    • “编程入门需要学什么?”
    • “如何提高编程能力?”
    • “有哪些编程学习资源?”

为什么需要它?🎯

在 RAG(检索增强生成)系统里,如果查询不清楚,就找不到正确的资料

就像一个搜索引擎:

  • 你搜 “python” → 可能找到蛇的资料 🐍
  • 重写后搜 “Python 编程语言教程” → 找到正确的编程资料 💻

查询重写让你的问题更容易被搜索引擎理解,找到更准确的答案。

一张图总结

原始问题 → [查询重写] → 清晰的问题 → [搜索] → 更好的答案
   ❌           ✅           ✅          ✅         ✅
  模糊       翻译一下     容易搜索    找到资料   解决问题

就像你有一个会说话的朋友,帮你把磕磕巴巴的话变成清楚的问题!


1.2 面试回答指南

回答框架(3 分钟版本)

1️⃣ 先给定义(15 秒)

“Query Rewriting 就是把用户的原始查询转换成更适合检索的形式,目的是提高检索的准确率。”

2️⃣ 说明为什么需要(30 秒)

"用户查询通常有两个问题:

  1. 太简短 —— 比如只搜 ‘python’,不知道是蛇还是编程语言
  2. 表述模糊 —— 比如 ‘怎么变强’,缺少上下文

直接拿这种查询去检索,召回的文档质量会很差,后续生成也会受影响。"

3️⃣ 讲核心技术(1 分钟)

"主要有三种方法:

① 查询扩展(Query Expansion)

  • 添加同义词或相关词,比如 ‘手机’ → ‘智能手机 评测 推荐’
  • 技术:可以用 LLM 生成、可以用词向量找近义词

② 查询改写(Query Reformulation)

  • 换个更清晰的说法,比如 ‘AI 咋回事’ → ‘人工智能基本原理’
  • 技术:通常用 LLM 做 paraphrase

③ 查询分解(Query Decomposition)

  • 把复杂问题拆成多个子问题,比如 ‘对比 iPhone 和华为’
    • ‘iPhone 的主要特点’
    • ‘华为手机的主要特点’
  • 技术:LLM + 提示词工程"
4️⃣ 结合实际应用(30 秒)

"在 RAG 系统里,查询重写通常放在检索之前

用户查询 → [Query Rewriting] → [检索] → [生成]

我在项目里用过 LLM 来做重写,比如用这样一个 prompt:

将以下查询改写成更适合搜索引擎的形式:
原始查询:{query}
要求:补充上下文,使用明确术语

效果提升很明显,尤其是处理简短查询时。"

5️⃣ 加分项(可选,展示深度)

"进阶做法还有:

  • 多查询检索:生成 3-5 个变体并行检索,然后去重融合
  • 用户历史感知:结合用户之前的查询补充上下文
  • 假设文档嵌入(HyDE):让 LLM 生成一个假设答案,用答案的嵌入去检索"

1.3 面试小贴士

要点 说明
先给定义 让面试官知道你真的懂
结构化 用 1、2、3 分点说,显得有条理
结合实际 说一个你用过的具体方法/项目
不要背概念 用自己的话讲,可以举例

1.4 常见追问

Q: 怎么评估查询重写的效果?

“看检索阶段的指标,比如 MRR、NDCG,或者看最终答案质量(人工评估或 LLM-as-a-judge)”

Q: 重写会不会引入噪声?

“会,所以要控制重写幅度。可以用少样本提示让 LLM 保持原意,或者设置置信度阈值,低置信度时用原始查询”

Q: 和 RAG 其他优化方法比怎么样?

“查询重写是查询侧优化,和索引侧优化(比如 chunk 策略、元数据过滤)是互补的,可以一起用”


1.5 技术架构

┌─────────────┐     ┌──────────────────┐     ┌──────────┐     ┌──────────┐
│ 用户查询     │ →   │ Query Rewriting  │ →   │  检索器   │ →   │  生成器   │
│ (原始问题)   │     │ (查询重写模块)    │     │ (Retriever)│   │ (Generator)│
└─────────────┘     └──────────────────┘     └──────────┘     └──────────┘
                           │
                           ├──→ 查询扩展 (同义词、相关词)
                           ├──→ 查询改写 (Paraphrase)
                           └──→ 查询分解 (多子问题)

1.6 核心要点总结

关键概念

  • 定义:将原始查询转换为更适合检索的形式
  • 目标:提高检索准确率
  • 位置:检索前的预处理步骤

三种方法

方法 做法 示例
查询扩展 添加同义词 “手机” → “智能手机 评测 推荐”
查询改写 换说法 “AI 咋回事” → “人工智能基本原理”
查询分解 拆问题 “对比 A 和 B” → “A 的特点”+“B 的特点”

面试关键词

  • 翻译官比喻
  • 检索前优化
  • 补充上下文
  • LLM 生成

================================================================================

第 2 章 评估指标:MRR 和 NDCG

2.1 MRR(平均倒数排名)

通俗理解

MRR 只关心:第一个正确答案排在第几名?

计算例子

假设有 3 个用户查询,每个查询的检索结果如下(✓ 表示正确答案):

查询 排名 1 排名 2 排名 3 排名 4 排名 5 第一个正确答案的排名 倒数 (1/排名)
查询 1 3 1/3 ≈ 0.333
查询 2 1 1/1 = 1.0
查询 3 2 1/2 = 0.5

MRR = (0.333 + 1.0 + 0.5) ÷ 3 = 1.833 ÷ 3 ≈ 0.611

公式

MRR = 1 ∣ Q ∣ ∑ i = 1 ∣ Q ∣ 1 rank i \text{MRR} = \frac{1}{|Q|} \sum_{i=1}^{|Q|} \frac{1}{\text{rank}_i} MRR=Q1i=1Qranki1

其中:

  • ∣ Q ∣ |Q| Q = 查询总数
  • rank i \text{rank}_i ranki = 第 i 个查询的第一个正确答案的排名

特点

优点 缺点
计算简单,好理解 只关心第一个正确答案
适合只要找到一个答案就足够的场景 不关心后面还有多少正确答案
值域 [0, 1],1 最好 如果前 10 个都没答案,MRR=0

适用场景

  • 问答系统(用户只需要一个正确答案)
  • 客服机器人
  • 事实性查询(“珠穆朗玛峰多高”)

2.2 NDCG(归一化折损累计增益)

通俗理解

NDCG 关心两件事:

  1. 正确答案有没有排在前面
  2. 越相关的答案是不是排得越靠前?

核心思想

NDCG 有三个关键概念:

① 相关性打分 (Gain)
给每个结果打分(0-3 分):

  • 3 分 = 非常相关
  • 2 分 = 比较相关
  • 1 分 = 有点相关
  • 0 分 = 不相关

② 折损 (Discounted)
排名越靠后,价值越低。公式:

折损后的增益 = 相关性得分 / log₂(排名 + 1)
排名 折损因子 说明
1 1.0 无折损
2 0.63 打 6 折
3 0.5 打 5 折
5 0.35 打 35 折
10 0.21 打 2 折

③ 归一化 (Normalized)
除以"理想情况下的最高分",让结果在 0-1 之间。

完整计算例子

用户搜 “python 教程”,检索结果和相关性打分:

排名 文档 相关性 (0-3) 折损因子 折损后增益
1 Python 入门教程 3 1.0 3.0
2 Python 进阶 2 0.63 1.26
3 Python 网站 3 0.5 1.5
4 Python 新闻 1 0.39 0.39
5 蟒蛇饲养指南 0 0.32 0

DCG@5 = 3.0 + 1.26 + 1.5 + 0.39 + 0 = 6.15

理想排序(IDCG) 应该是把最相关的排前面:

排名 理想相关性 折损因子 折损后增益
1 3 1.0 3.0
2 3 0.63 1.89
3 2 0.5 1.0
4 1 0.39 0.39
5 0 0.32 0

IDCG@5 = 3.0 + 1.89 + 1.0 + 0.39 + 0 = 6.28

NDCG@5 = DCG / IDCG = 6.15 / 6.28 = 0.98

(越接近 1 越好,0.98 说明排序非常接近理想状态)


2.3 MRR vs NDCG 对比

维度 MRR NDCG
关心什么 第一个正确答案 所有结果的相关性 + 排序
相关性分级 只有对/错 可以有 0-3 多级
适用场景 问答、事实查询 搜索、推荐
计算复杂度 简单 较复杂
如果第一个就对了 MRR=1 NDCG 可能 <1(后面排错了)

2.4 面试回答模板

如果面试官问:“怎么评估 RAG 的检索效果?”

"检索阶段的评估主要看两个指标:

MRR(平均倒数排名):只关心第一个正确答案排第几名。适合问答系统,因为用户通常只需要一个正确答案。计算公式是 1/排名,MRR 越接近 1 越好。

NDCG(归一化折损累计增益):同时考虑相关性和排序质量。它假设:① 相关性有等级(0-3 分),② 排名越靠后价值越低。NDCG 也适合搜索场景,因为用户会浏览前几条结果。

在 RAG 里,如果更关注答案质量,用 MRR;如果更关注检索文档的整体相关性,用 NDCG。实际项目中,我通常会同时监控这两个指标。"


2.5 一张图总结

┌─────────────────────────────────────────────────────────────┐
│                  MRR vs NDCG 对比                            │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  MRR(平均倒数排名)                                         │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  只关心:第一个正确答案排第几?                       │   │
│  │  公式:1/排名                                        │   │
│  │  场景:问答系统、事实查询                            │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  NDCG(归一化折损累计增益)                                  │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  关心两件事:                                        │   │
│  │  ① 正确答案有没有排前面?                            │   │
│  │  ② 越相关的是不是越靠前?                            │   │
│  │  场景:搜索、推荐                                    │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  选择建议:                                                  │
│  ├── 用户只需要一个答案 → MRR                               │
│  ├── 用户会浏览多个结果 → NDCG                              │
│  └── 实际项目 → 两个都监控                                  │
│                                                             │
└─────────────────────────────────────────────────────────────┘

2.6 核心要点总结

MRR 关键点

  • 只看第一名:只关心第一个正确答案的排名
  • 计算简单:1/排名,然后求平均
  • 适合问答:用户只需要一个正确答案的场景

NDCG 关键点

  • 看全部:考虑所有结果的相关性
  • 看排序:越相关的应该越靠前
  • 多级相关:可以有 0-3 分的细致打分

面试关键词

  • 倒数排名
  • 折损累计增益
  • 相关性分级
  • 排序质量

================================================================================

第 3 章 HyDE(假设文档嵌入)

3.1 通俗理解(12 岁能听懂)

一个故事

情景 1:直接去找

你想找一本关于**“恐龙”**的书,跑到图书馆对图书管理员说:

“恐龙”

图书管理员很困惑,可能给你一本恐龙图片书、恐龙知识书、或恐龙故事书——不一定准确。

情景 2:先描述答案,再去找

这次你先请一个很聪明的朋友帮你想象:

“一本讲恐龙的书,里面应该有:霸王龙、三角龙、侏罗纪、化石、灭绝原因…”

然后你拿着这个描述去找图书管理员:

“我想找一本讲霸王龙、三角龙、侏罗纪时期、恐龙化石和灭绝原因的书”

图书管理员马上就知道你要什么了!

HyDE 就是这个"聪明的朋友"。

HyDE 的核心思想

先编一个"假答案",再用这个"假答案"去找"真答案"

工作流程

你问:"恐龙怎么灭绝的?"
        ↓
   ┌────────────────────┐
   │  HyDE (聪明朋友)     │
   │  帮你编一个假答案:  │
   │ "恐龙灭绝是因为..."  │
   └────────────────────┘
        ↓
   用"假答案"去搜索
        ↓
   找到"真答案"的文档

3.2 面试回答指南(2-3 分钟版本)

1️⃣ 先给定义(20 秒)

"HyDE 是 Hypothetical Document Embeddings 的缩写,中文叫’假设文档嵌入’。

它的核心思想是:先让 LLM 生成一个假设的答案,再用这个假设答案的向量表示去检索真实文档

简单说,就是’用答案搜答案’,而不是’用问题搜答案’。"

2️⃣ 说明为什么需要(30 秒)

"直接检索用户查询有两个问题:

① 问题空间和答案空间不一致

  • 用户问:‘怎么减肥?’
  • 文档写:‘控制饮食摄入,增加运动频率…’
  • '减肥’和’控制饮食’字面不同,但语义相关

② 查询太简短,信息密度低

  • 用户只搜 ‘python list’,嵌入向量包含的语义信息很少

这两种情况都导致检索效果不好。"

3️⃣ 讲工作原理(40 秒)

"HyDE 的流程分三步:

第一步:生成假设答案
用 LLM 根据用户问题生成一个假设的答案文档。

第二步:嵌入假设答案
把假设答案转换成向量表示。

第三步:检索相似文档
用假设答案的向量去检索,找到语义相似的真实文档。

关键在于:即使假设答案内容不准确,它的语言风格、关键词、语义方向都和真实文档接近,所以向量表示也接近。"

4️⃣ 说明为什么有效(40 秒)

"HyDE 有效有三个原因:

① 填补语义鸿沟
把’问题空间’映射到’答案空间’,让查询和文档在同一语义空间里。

② 利用 LLM 的预训练知识
LLM 读过大量文本,知道答案通常长什么样、包含哪些关键词。

③ 增加信息密度
假设答案比原始查询更长、更具体,嵌入向量包含更多语义信息。

实验表明,HyDE 在开放型问答、复杂查询上效果提升明显。"

5️⃣ 结合实际应用(20 秒)

"在我的 RAG 项目里,我试过 HyDE 的方法:

# 伪代码
hypothetical_doc = llm.generate(f"请为以下问题生成一个详细的答案:{query}")
query_embedding = embedder.encode(hypothetical_doc)
results = vector_db.search(query_embedding, top_k=5)

对于简短查询和复杂问题,检索质量确实有提升,但会增加一次 LLM 调用和一点延迟。"


3.3 HyDE 为什么有效?(深入理解)

原因 1:填补"问题 - 答案"的语义鸿沟

问题空间和答案空间不一样

问题 答案
“怎么减肥?” “控制饮食摄入,增加有氧运动…”
“手机坏了怎么办?” “设备故障排除指南:重启…”
  • 问题用疑问句,答案用陈述句
  • 问题用口语,答案用专业术语
  • 问题说目标,答案说方法

HyDE 的做法

让 LLM 生成一个假设答案,把问题翻译成答案的风格:

问题:"怎么减肥?"
        ↓
   HyDE 翻译成答案风格:
   "减肥需要控制饮食摄入,增加有氧运动..."
        ↓
   用这个去搜,就能找到写"控制饮食"的文档了!

原因 2:利用 LLM 的世界知识

LLM 在预训练时读过大量文本,它知道:

  • 答案通常长什么样
  • 答案会用哪些关键词
  • 答案的结构是什么

即使 LLM 生成的假设答案内容不对,它的向量表示仍然能和真实答案对齐。

原因 3:缓解"词汇不匹配"问题

传统检索的问题

用户搜:“手机坏了怎么办”
文档里写:“设备故障排除指南”

手机 vs 设备坏了 vs 故障 —— 字面不一样。

HyDE 的做法

LLM 生成假设答案:

“如果手机出现故障,可以尝试以下方法:1. 重启设备 2. 恢复出厂设置 3. 联系售后服务中心…”

这个假设答案会同时包含用户的词和文档的词,起到词汇桥接的作用。

原因 4:增加查询的信息密度

原始查询 嵌入向量信息量
“python list” 低(就两个词)
“Python 中列表的使用方法,包括创建、访问、修改列表元素的示例代码”

HyDE 生成的假设答案更长、更具体,包含更多可嵌入的语义信息


3.4 一个反直觉的事实

HyDE 的假设答案不需要正确!

实验表明,即使假设答案内容是错的,只要:

  • 语言风格像答案
  • 关键词覆盖到位
  • 语义方向正确

就能有效提升检索效果。

这是因为嵌入空间里,相似主题的文档聚在一起,假设答案只需要落在正确的"语义区域"即可。


3.5 适用场景

适合 不适合
问答型查询(“什么是 X”) 导航型查询(“某某官网”)
开放性问题 事实性查询(“珠穆朗玛峰多高”)
需要解释的问题 简单检索(“python 官网”)

3.6 与 Query Rewriting 对比

方法 做法 比喻
Query Rewriting 把问题说得更清楚 翻译官
HyDE 编一个答案去找答案 美食家

两者都是让检索更准确,但方法不同!


3.7 常见追问及回答

Q: HyDE 和 Query Rewriting 有什么区别?

"两者目标相似但方法不同:

维度 Query Rewriting HyDE
做法 改写问题 生成答案
嵌入对象 改写后的问题 假设答案
核心优势 让问题更清晰 桥接问题 - 答案空间
延迟 低(一次 LLM) 中(一次 LLM+ 嵌入)

实际项目里可以一起用:先重写查询,再用 HyDE 检索。"

Q: 假设答案如果错了怎么办?

"这是 HyDE 反直觉的地方——假设答案不需要正确

因为我们要的不是答案内容,而是它的向量表示。只要:

  • 语言风格像答案
  • 关键词覆盖到位
  • 语义方向正确

就能找到正确的文档。实验表明,即使假设答案有事实错误,检索效果依然提升。"

Q: 怎么评估 HyDE 的效果?

"看检索阶段的指标:

  • MRR:第一个正确答案的排名
  • NDCG:整体排序质量
  • Recall@K:前 K 个结果里有多少正确答案

也可以看最终答案质量,用人工评估或 LLM-as-a-judge。

我在项目里对比过,HyDE 对简短查询的 MRR 提升约 10-15%。"


3.8 一张图总结

┌──────────────────────────────────────────────────────────┐
│                     HyDE 工作流程                         │
├──────────────────────────────────────────────────────────┤
│                                                          │
│  用户问题                    HyDE                        │
│  ┌─────────┐              ┌─────────────┐               │
│  │ "如何    │              │  假设答案:   │               │
│  │  减肥?"  │ ────────────→│  控制饮食、   │               │
│  └─────────┘   生成假设    │  增加运动... │               │
│                            └─────────────┘               │
│                                  ↓                       │
│  传统检索:问题 → 文档        用假设答案嵌入检索            │
│  鸿沟大 ❌                    鸿沟小 ✅                    │
│                                                          │
│  结果:找到"科学减重指南"等真实文档                        │
│                                                          │
└──────────────────────────────────────────────────────────┘

3.9 核心要点总结

HyDE 关键点

  • 核心思想:用答案搜答案
  • 三步流程:生成假设答案 → 嵌入 → 检索
  • 反直觉:假设答案不需要正确

为什么有效

  1. 填补问题 - 答案语义鸿沟
  2. 利用 LLM 预训练知识
  3. 缓解词汇不匹配
  4. 增加信息密度

面试关键词

  • Hypothetical Document Embeddings
  • 用答案搜答案
  • 语义鸿沟
  • 向量表示对齐

================================================================================

第 4 章 Multi-Query(多查询检索)

4.1 通俗理解(12 岁能听懂)

你有一次提问的机会

想象你在玩一个寻宝游戏,面前有一个神秘的宝箱,里面装着你要找的答案。

游戏规则是:你只能对宝箱管理员说一句话,然后他帮你从仓库里找宝物。

第一次尝试

你问:

“怎么变强?”

管理员想了想,给你拿了一本《健身入门》。但你心里想的是:“我是想变编程高手啊!”

问题出在哪里?

你的问题太模糊了!"变强"可以有很多种意思:

  • 💪 身体变强(健身)
  • 🧠 大脑变强(学习)
  • 💻 编程变强(写代码)
  • 🎮 游戏变强(打游戏)

Multi-Query 就像一个"聪明的分身术" 🔄

Multi-Query 的做法

你不是只说一句话,而是同时说好几句不同的话

原始问题:"怎么变强?"
        ↓
   ┌─────────────────────────────┐
   │  Multi-Query 分身术          │
   └─────────────────────────────┘
        ↓
   ┌─────────────────────────────────────┐
   │  "如何通过健身让身体变强?"           │
   │  "如何提高编程能力?"                │
   │  "如何在游戏中变强?"                │
   └─────────────────────────────────────┘
        ↓
   用这 3 个问题分别去搜索
        ↓
   把找到的所有结果合并在一起
        ↓
   去掉重复的,选出最好的
        ↓
   给用户一个完整的答案!

一个比喻

方法 比喻 结果
单次查询 派 1 个人去找东西 可能找错
Multi-Query 派 3 个人从不同角度去找 更可能找到对的

为什么这样更好?

就像你问朋友问题:

  • 问 1 个朋友,可能他只想到一个角度
  • 问 3 个朋友,能得到 3 种不同的想法,最后综合起来更全面!

4.2 核心概念拆解

什么是 Multi-Query?

Multi-Query 是一种检索优化技术,它的核心思想是:

用多个不同角度的查询变体并行检索,然后合并结果

工作流程

  1. 生成变体:用 LLM 把原始查询改写成 3-5 个不同的版本
  2. 并行检索:每个变体同时去检索
  3. 结果融合:合并所有结果,去掉重复的,重新排序
  4. 返回答案:给用户一个更全面的结果

举个例子

用户问:“python list 怎么用?”

Multi-Query 生成的变体:

  1. “Python 列表的基本语法”
  2. “Python list 创建和访问方法”
  3. “Python 列表操作示例代码”
  4. “如何在 Python 中使用列表存储数据”

每个查询都能找到一些不同的文档,合并起来就更全面了!


4.3 与 Query Rewriting 的对比

维度 Query Rewriting Multi-Query
目标 把问题说清楚 从多个角度问问题
生成数量 1 个改写后的问题 3-5 个变体问题
检索次数 1 次 多次(并行)
比喻 翻译官 分身术
延迟 中(但并行可以加速)
覆盖率 单一角度 多角度覆盖

形象比喻

  • Query Rewriting:你说的不清楚,我帮你翻译成清楚的一句话
  • Multi-Query:你可能想问多个角度,我帮你同时问好几个问题

4.4 面试回答指南(2 分钟版本)

1️⃣ 先给定义(20 秒)

"Multi-Query 是一种检索优化技术,核心思想是生成多个查询变体并行检索,然后合并结果

简单说,就是用’分身术’从多个角度问同一个问题,找到更全面的答案。"

2️⃣ 说明为什么需要(30 秒)

"用户查询有两个问题:

① 歧义性

  • 比如’怎么变强’,可能是健身、编程、学习
  • 单次查询只能命中一个角度

② 覆盖不全

  • 即使用户问题清楚,单次检索也可能漏掉相关文档
  • 不同表述的文档可能只被某些变体命中

Multi-Query 通过多个变体来解决这两个问题。"

3️⃣ 讲工作原理(40 秒)

"Multi-Query 分三步:

第一步:生成变体
用 LLM 根据用户查询生成 3-5 个不同角度的变体。

第二步:并行检索
每个变体同时去检索(可以并行执行,延迟不增加太多)。

第三步:结果融合
合并所有结果,去掉重复的,然后用 RRF(Reciprocal Rank Fusion)或其他方法重新排序。

关键是:不同变体可能命中不同但相关的文档,合并后覆盖率更高。"

4️⃣ 结合实际应用(20 秒)

"在 RAG 系统里,Multi-Query 通常和 Query Rewriting 一起用:

用户查询 → [Query Rewriting] → [Multi-Query 生成变体] → [并行检索] → [结果融合] → [生成]

我在项目里试过,对于开放型问题和歧义查询,效果提升明显。"


4.5 优势与成本分析

核心优势

优势 说明 数据
召回率提升 不同变体命中不同但相关的文档 Recall@10 +30%~80%
歧义处理 同时覆盖多个可能的含义 歧义查询提升 81%
多角度覆盖 每个变体找到不同角度的文档 答案更全面
鲁棒性强 某个变体不好,其他可以弥补 容错率高

成本分析:为什么是 6-8 倍?

详细成本拆解(基于典型云 API 价格估算)

组件 单次查询 Multi-Query (3 变体) Multi-Query (5 变体)
LLM 调用(生成变体) 0 次 × $0.002 1 次 × $0.002 = $0.002 1 次 × $0.002 = $0.002
嵌入(Embedding) 1 次 × $0.0001 3 次 × $0.0001 = $0.0003 5 次 × $0.0001 = $0.0005
向量检索 1 次 × $0.0005 3 次 × $0.0005 = $0.0015 5 次 × $0.0005 = $0.0025
总计 $0.0006 $0.0038 $0.0050
倍数 1x 6.3x 8.3x

倍数计算过程

单次查询总成本:$0.0001 + $0.0005 = $0.0006

Multi-Query (3 变体) 总成本:$0.002 + $0.0003 + $0.0015 = $0.0038
倍数 = $0.0038 ÷ $0.0006 = 6.33 倍

Multi-Query (5 变体) 总成本:$0.002 + $0.0005 + $0.0025 = $0.0050
倍数 = $0.0050 ÷ $0.0006 = 8.33 倍

成本构成分析

┌─────────────────────────────────────────────────────────────┐
│              Multi-Query 成本构成(3 变体)                   │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  单次查询:$0.0006                                          │
│  ├── 嵌入:$0.0001 (16.7%)                                  │
│  └── 检索:$0.0005 (83.3%)                                  │
│                                                             │
│  Multi-Query:$0.0038                                       │
│  ├── LLM 生成变体:$0.002 (52.6%)  ← 新增                   │
│  ├── 嵌入:$0.0003 (7.9%)        ← 3 倍                     │
│  └── 检索:$0.0015 (39.5%)       ← 3 倍                     │
│                                                             │
│  倍数 = $0.0038 / $0.0006 = 6.33 倍                         │
│                                                             │
└─────────────────────────────────────────────────────────────┘

关键点说明

  1. LLM 调用是主要成本增量

    • LLM 生成变体:$0.002,占总成本 52.6%
    • 这是纯新增的成本,单次查询没有这一步
  2. 嵌入和检索成本按变体数量线性增长

    变体数 嵌入成本 检索成本 总成本 倍数
    1 次(单次) $0.0001 $0.0005 $0.0006 1x
    3 次 $0.0003 $0.0015 $0.0038 6.3x
    5 次 $0.0005 $0.0025 $0.0050 8.3x
  3. 实际倍数取决于变体数量

    • 3 个变体 → 约 6 倍
    • 4 个变体 → 约 7 倍
    • 5 个变体 → 约 8 倍

⚠️ 注意:以上价格是基于典型云 API 的示例估算,实际成本取决于:

  • 使用的 LLM 模型(GPT-4 比 GPT-3.5 贵)
  • 嵌入模型(商业 API vs 本地部署)
  • 向量数据库(云服务 vs 自建)
  • 查询长度(tokens 数量)

如果是本地部署模型,边际成本接近零,但前期投入大,成本倍数仍然适用(6-8 倍的资源消耗)。


4.6 优化策略:如何降低成本

策略 1:条件触发

def should_use_multi_query(query):
    """只对特定查询使用 Multi-Query"""
  
    # 查询太短(< 5 词)→ 用 Multi-Query
    if len(query.split()) < 5:
        return True
  
    # 查询有歧义(包含泛化词)→ 用 Multi-Query
    ambiguous_words = ["怎么", "如何", "什么", "为什么"]
    if any(word in query for word in ambiguous_words):
        return True
  
    # 事实性查询 → 不用 Multi-Query
    if query.endswith("吗") or "?" in query:
        return False
  
    # 默认不用
    return False

策略 2:动态调整变体数量

查询类型 变体数量 延迟 成本
简短模糊 5 个
一般查询 3 个
明确查询 2 个

策略 3:并行化优化

# 串行(慢)❌
results1 = retriever.search(query1)
results2 = retriever.search(query2)
results3 = retriever.search(query3)

# 并行(快)✅
import asyncio
results1, results2, results3 = await asyncio.gather(
    retriever.search(query1),
    retriever.search(query2),
    retriever.search(query3)
)

策略 4:缓存变体生成

# 常见查询的变体可以缓存
cache = {
    "python 教程": ["Python 入门教程", "Python 基础语法", "Python 学习路径"],
    "怎么减肥": ["如何科学减肥", "减肥饮食指南", "减肥运动方法"],
}

def get_variants(query):
    if query in cache:
        return cache[query]  # 直接返回,省去 LLM 调用
    else:
        variants = llm.generate_variants(query)
        cache[query] = variants
        return variants

4.7 一张图总结

┌─────────────────────────────────────────────────────────┐
│                  Multi-Query 工作流程                    │
├─────────────────────────────────────────────────────────┤
│                                                         │
│  用户问:"怎么变强?"                                    │
│         ↓                                               │
│  ┌──────────────────────────┐                           │
│  │  LLM 生成 3-5 个变体问题   │                           │
│  └──────────────────────────┘                           │
│         ↓                                               │
│  ┌───────────┐  ┌───────────┐  ┌───────────┐           │
│  │ 健身变强   │  │ 编程变强   │  │ 学习变强   │           │
│  │ [搜索]    │  │ [搜索]    │  │ [搜索]    │           │
│  └───────────┘  └───────────┘  └───────────┘           │
│         ↓              ↓              ↓                  │
│  ┌───────────────────────────────────────────┐          │
│  │         合并结果 + 去重 + 排序             │          │
│  └───────────────────────────────────────────┘          │
│         ↓                                               │
│  最终答案(更全面、更准确)                              │
│                                                         │
└─────────────────────────────────────────────────────────┘

4.8 核心要点总结

Multi-Query 关键点

  • 核心思想:多角度问问题,找到更全面的答案
  • 四步流程:生成变体 → 并行检索 → 结果融合 → 返回答案
  • 成本:6-8 倍基准成本

优势

  1. 召回率提升 30%~80%
  2. 歧义处理能力强
  3. 答案更全面
  4. 鲁棒性强

优化策略

  1. 条件触发(不是所有查询都用)
  2. 动态调整变体数量
  3. 并行化检索
  4. 缓存常见查询变体

面试关键词

  • 分身术比喻
  • 多角度覆盖
  • RRF 结果融合
  • 成本 6-8 倍

================================================================================

第 5 章 RRF(倒数排名融合)

5.1 什么是 RRF?

RRF(Reciprocal Rank Fusion / 倒数排名融合)是一种合并多个检索结果的方法,它的特点是:

  • 不依赖具体分数,只看排名
  • 排名越靠前的结果,权重越高

在 Multi-Query 中,RRF 用于融合多个查询变体的检索结果。


5.2 计算公式

RRF 分数 = ∑ i = 1 N 1 k + rank i \text{RRF 分数} = \sum_{i=1}^{N} \frac{1}{k + \text{rank}_i} RRF 分数=i=1Nk+ranki1

其中:

  • N = 检索结果的数量(比如有 3 个查询变体)
  • rank_i = 文档在第 i 个查询结果中的排名
  • k = 平滑参数(通常是 60)

5.3 为什么 k = 60?

k 是一个平滑参数,目的是让排名之间的差距不要太大。

排名 1/(1+60) 1/(10+60) 1/(100+60)
0.0164 0.0143 0.00625
意义 第 1 名价值 第 10 名价值 第 100 名价值
  • 如果 k 太小(比如 k=1),第 1 名和第 2 名差距太大(1/2 vs 1/3)
  • 如果 k 太大(比如 k=1000),所有排名差距都很小

实验发现 k=60 是一个比较平衡的值。


5.4 完整计算例子

场景:用户搜:“python 教程”,Multi-Query 生成了 3 个变体查询,每个查询返回前 3 个结果:

文档 查询 1 排名 查询 2 排名 查询 3 排名
文档 A(Python 入门) 1 3 2
文档 B(Python 进阶) 2 1 5
文档 C(Python 实战) 3 2 1
文档 D(Python 技巧) 5 4 3

计算 RRF 分数(k=60)

文档 A: 1/(60+1) + 1/(60+3) + 1/(60+2) 
      = 0.01639 + 0.01587 + 0.01613 
      = 0.04839

文档 B: 1/(60+2) + 1/(60+1) + 1/(60+5)
      = 0.01613 + 0.01639 + 0.01538
      = 0.04790

文档 C: 1/(60+3) + 1/(60+2) + 1/(60+1)
      = 0.01587 + 0.01613 + 0.01639
      = 0.04839

文档 D: 1/(60+5) + 1/(60+4) + 1/(60+3)
      = 0.01538 + 0.01562 + 0.01587
      = 0.04687

最终排序

文档 RRF 分数 最终排名
文档 A 0.04839 🥇 第 1 名(并列)
文档 C 0.04839 🥇 第 1 名(并列)
文档 B 0.04790 第 3 名
文档 D 0.04687 第 4 名

5.5 RRF 的优点

优点 说明
简单易懂 公式简单,计算快
不依赖分数 只看排名,不管具体相似度分数
鲁棒性强 对噪声不敏感,某个查询结果差一点没关系
广泛适用 适合任何有排名的场景
无需调参 k=60 是经验值,大部分场景都好用

为什么不直接用相似度分数?

因为不同查询的相似度分数不可比

  • 查询 1 的相似度 0.8 可能等于 查询 2 的相似度 0.6
  • 但排名是统一的:第 1 名就是比第 2 名好

5.6 代码实现

def reciprocal_rank_fusion(results_list, k=60):
    """
    RRF 倒数排名融合
  
    参数:
        results_list: List[List[doc_id, score, rank]]
                      多个查询结果列表,每个结果包含文档 ID、分数、排名
        k: 平滑参数,默认 60
  
    返回:
        sorted_docs: 按 RRF 分数排序的文档列表
    """
    rrf_scores = {}
  
    for results in results_list:
        for rank, (doc_id, score, _) in enumerate(results, start=1):
            if doc_id not in rrf_scores:
                rrf_scores[doc_id] = 0
            rrf_scores[doc_id] += 1 / (k + rank)
  
    # 按 RRF 分数排序
    sorted_docs = sorted(rrf_scores.items(), key=lambda x: x[1], reverse=True)
    return sorted_docs

使用示例

# 假设有 3 个查询的检索结果
results1 = [("doc_a", 0.9, 1), ("doc_b", 0.8, 2), ("doc_c", 0.7, 3)]
results2 = [("doc_b", 0.95, 1), ("doc_c", 0.85, 2), ("doc_a", 0.75, 3)]
results3 = [("doc_c", 0.92, 1), ("doc_a", 0.82, 2), ("doc_b", 0.72, 3)]

# RRF 融合
results_list = [results1, results2, results3]
fused = reciprocal_rank_fusion(results_list, k=60)

print(fused)
# 输出:[('doc_a', 0.04839...), ('doc_c', 0.04839...), ('doc_b', 0.04790...)]

5.7 面试回答模板

如果面试官问:“Multi-Query 的结果怎么融合?”

"常用方法是 RRF(Reciprocal Rank Fusion / 倒数排名融合)

公式是:RRF 分数 = Σ (1 / (k + rank_i)),其中 k 通常是 60,rank_i 是文档在第 i 个查询结果中的排名。

RRF 的核心思想是:不依赖具体分数,只看排名。排名越靠前的结果,权重越高。最后按 RRF 分数重新排序。

为什么用 RRF 而不是直接合并相似度分数?因为不同查询的相似度分数不可比——查询 1 的 0.8 分可能等于查询 2 的 0.6 分。但排名是统一的:第 1 名就是比第 2 名好。"


5.8 一张图总结

┌─────────────────────────────────────────────────────────────┐
│                    RRF 倒数排名融合                          │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  公式:RRF 分数 = Σ (1 / (60 + rank_i))                      │
│                                                             │
│  计算示例:                                                  │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  文档 A: 查询 1 排第 1 + 查询 2 排第 3 + 查询 3 排第 2       │   │
│  │  RRF 分数 = 1/61 + 1/63 + 1/62 = 0.04839             │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  为什么 k=60?                                              │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  k 太小 → 排名差距太大                               │   │
│  │  k 太大 → 排名差距太小                               │   │
│  │  k=60 → 经验值,平衡选择                            │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  优点:                                                     │
│  ✅ 简单易懂,计算快                                        │
│  ✅ 不依赖分数,只看排名                                    │
│  ✅ 鲁棒性强,对噪声不敏感                                  │
│  ✅ 无需调参,k=60 通用                                     │
│                                                             │
└─────────────────────────────────────────────────────────────┘

5.9 核心要点总结

RRF 关键点

  • 定义:一种合并多个检索结果的方法
  • 核心:只看排名,不依赖具体分数
  • 公式:RRF 分数 = Σ (1 / (k + rank_i))

参数选择

  • k=60:经验值,平衡排名差距

优点

  1. 简单易懂,计算快
  2. 不依赖分数
  3. 鲁棒性强
  4. 无需调参

面试关键词

  • 倒数排名融合
  • k=60
  • 排名不可比
  • Multi-Query 结果融合

================================================================================

第 6 章 五大 RAG 策略

6.1 策略一:速度优先(Speed-First)🚀

适用场景

  • 实时对话系统
  • 延迟敏感场景(<500ms)
  • 简单事实性查询
  • MVP/原型验证

流程图

用户查询 → [Query Rewriting] → [向量检索 Top-5] → [LLM 生成] → 答案
         (可选)                                 
         延迟:+0ms                            
         成本:1x                              

核心配置

阶段 策略 参数
查询处理 简单预处理(去空格、标准化) -
检索 向量检索(Bi-Encoder) Top-5, 阈值 0.5
Rerank -
生成 直接生成 temperature=0.3

代码实现

class SpeedFirstRAG:
    """速度优先策略 - 目标 <300ms"""
  
    def __init__(self):
        self.embedder = SentenceTransformer('all-MiniLM-L6-v2')
        self.vector_db = ChromaDB(collection='fast_collection')
        self.llm = OpenRouterLLM(model='gpt-4o-mini')
  
    def query(self, user_query: str) -> str:
        # 简单预处理(可选)
        query = user_query.strip()
      
        # 单次检索 Top-5
        embedding = self.embedder.encode(query)
        results = self.vector_db.search(embedding, top_k=5)
      
        # 检查检索质量
        if not results or results[0].score < 0.5:
            # Fallback: 直接让 LLM 回答
            return self.llm.generate(f"直接回答:{query}")
      
        # 生成答案
        context = "\n".join([doc.content for doc in results])
        return self.llm.generate(f"根据上下文回答:\n{context}\n\n问题:{query}")

性能指标

指标 目标值
延迟 <300ms
成本 1x(基准)
召回率 中等(Recall@5 ≈ 0.45)
答案质量 ⭐⭐⭐

Fallback 策略

def fallback_strategy(self, results, query):
    """速度优先的 Fallback - 快速降级"""
  
    if not results:
        # 空结果 → 直接 LLM 回答
        return self.llm.generate(f"直接回答:{query}")
  
    if results[0].score < 0.3:
        # 分数太低 → 关键词检索快速补救
        bm25_results = self.bm25.search(query, top_k=3)
        if bm25_results:
            return self.generate_answer(bm25_results, query)
  
    # 否则用原结果
    return self.generate_answer(results, query)

6.2 策略二:召回率优先(Recall-First)📥

适用场景

  • 复杂研究型问题
  • 开放型问答
  • 法律/医疗等专业领域
  • 对答案完整性要求高

流程图

┌─────────────────────────────────────────────────────────────────┐
│                    召回率优先策略                                │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  用户查询                                                        │
│    ↓                                                            │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │  Multi-Query 扩展 (5 个变体)                               │   │
│  └─────────────────────────────────────────────────────────┘   │
│    ↓                                                            │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │  混合检索:向量 + 关键词                                  │   │
│  │  每个变体检索 Top-50                                      │   │
│  └─────────────────────────────────────────────────────────┘   │
│    ↓                                                            │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │  RRF 融合 + 去重                                          │   │
│  │  约 150-200 条候选                                         │   │
│  └─────────────────────────────────────────────────────────┘   │
│    ↓                                                            │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │  Rerank (Cross-Encoder)                                  │   │
│  │  精细打分,取 Top-10                                      │   │
│  └─────────────────────────────────────────────────────────┘   │
│    ↓                                                            │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │  LLM 生成(带长上下文)                                    │   │
│  └─────────────────────────────────────────────────────────┘   │
│    ↓                                                            │
│  完整答案                                                       │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

核心配置

阶段 策略 参数
查询扩展 Multi-Query 5 个变体
检索 混合检索(向量+BM25) Top-50/变体
融合 RRF(k=60) -
Rerank Cross-Encoder Top-10
生成 长上下文窗口 max_tokens=2000

代码实现

class RecallFirstRAG:
    """召回率优先策略 - 目标 Recall@50 > 0.9"""
  
    def __init__(self):
        self.llm = OpenRouterLLM(model='gpt-4o')
        self.embedder = SentenceTransformer('bge-large-zh-v1.5')
        self.vector_db = ChromaDB()
        self.bm25 = BM25Search()
        self.reranker = CrossEncoderReranker('bge-reranker-large')
  
    def query(self, user_query: str) -> str:
        # Step 1: Multi-Query 扩展
        variants = self.llm.generate(
            f"为以下查询生成 5 个不同角度的变体:\n{user_query}"
        )
        queries = [user_query] + variants.split('\n')
      
        # Step 2: 混合检索(每个变体 Top-50)
        all_results = []
        for q in queries:
            vector_results = self.vector_db.search(
                self.embedder.encode(q), top_k=50
            )
            keyword_results = self.bm25.search(q, top_k=50)
            all_results.extend(vector_results)
            all_results.extend(keyword_results)
      
        # Step 3: 去重 + RRF 融合
        deduped_results = self.rrf_fusion(all_results, k=60)
      
        # Step 4: Rerank 精筛
        reranked = self.reranker.rerank(user_query, deduped_results[:150], top_k=10)
      
        # Step 5: 生成完整答案
        context = "\n\n".join([doc.content for doc in reranked])
        return self.llm.generate(
            f"请根据以下资料完整回答问题,确保覆盖所有重要信息:\n\n{context}\n\n问题:{user_query}"
        )

性能指标

指标 目标值
延迟 2000-4000ms
成本 10-15x
召回率 极高(Recall@50 > 0.9)
答案质量 ⭐⭐⭐⭐⭐

Fallback 策略

def fallback_strategy(self, results, query):
    """召回率优先的 Fallback - 保证不遗漏"""
  
    if len(results) < 10:
        # 结果太少 → 扩大检索范围
        more_results = self.vector_db.search(
            self.embedder.encode(query), top_k=100
        )
        results.extend(more_results)
  
    if all(doc.score < 0.3 for doc in results):
        # 分数都太低 → 触发 HyDE
        hypothetical_doc = self.llm.generate(
            f"为以下问题生成一个详细的答案:{query}"
        )
        hyde_results = self.vector_db.search(
            self.embedder.encode(hypothetical_doc), top_k=50
        )
        results.extend(hyde_results)
  
    return results

6.3 策略三:平衡型(Balanced)⚖️

适用场景

  • 通用生产环境
  • 大多数企业应用
  • 日常问答系统

流程图

用户查询 → [Query Rewriting] → [向量检索 Top-20] → [Rerank Top-8] → [LLM 生成] → 答案
                     ↓
              延迟:~800ms
              成本:4x

核心配置

阶段 策略 参数
查询处理 Query Rewriting 1 次改写
检索 向量检索 Top-20
Rerank Cross-Encoder Top-8
生成 标准上下文 max_tokens=1000

代码实现

class BalancedRAG:
    """平衡型策略 - 性价比最优"""
  
    def __init__(self):
        self.embedder = SentenceTransformer('bge-base-zh-v1.5')
        self.vector_db = ChromaDB()
        self.reranker = CrossEncoderReranker('bge-reranker-base')
        self.llm = OpenRouterLLM(model='gpt-4o-mini')
  
    def query(self, user_query: str) -> str:
        # Step 1: Query Rewriting(可选)
        if len(user_query.split()) < 5:
            query = self.llm.generate(
                f"将以下查询改写成更适合检索的形式:\n{user_query}"
            )
        else:
            query = user_query
      
        # Step 2: 向量检索 Top-20
        embedding = self.embedder.encode(query)
        results = self.vector_db.search(embedding, top_k=20)
      
        # Step 3: Fallback 检查
        if not results or results[0].score < 0.4:
            return self.hybrid_search_fallback(query)
      
        # Step 4: Rerank 取 Top-8
        reranked = self.reranker.rerank(query, results, top_k=8)
      
        # Step 5: 生成答案
        context = "\n".join([doc.content for doc in reranked[:5]])
        return self.llm.generate(f"根据上下文回答:\n{context}\n\n问题:{query}")
  
    def hybrid_search_fallback(self, query: str) -> str:
        """混合检索 Fallback"""
        vector_results = self.vector_db.search(
            self.embedder.encode(query), top_k=15
        )
        keyword_results = self.bm25.search(query, top_k=15)
        fused = self.rrf_fusion(vector_results + keyword_results)
        return self.generate_answer(fused[:5], query)

性能指标

指标 目标值
延迟 600-1000ms
成本 3-5x
召回率 中高(Recall@20 ≈ 0.7)
答案质量 ⭐⭐⭐⭐

6.4 策略四:成本优先(Cost-First)💰

适用场景

  • 预算有限的项目
  • 高并发场景
  • 内部工具
  • 低频使用场景

流程图

用户查询 → [本地 Embedding] → [本地向量检索 Top-3] → [本地小模型生成] → 答案
         延迟:~150ms
         成本:0.1x(几乎免费)

核心配置

阶段 策略 参数
Embedding 本地小模型 all-MiniLM-L6-v2
检索 轻量向量库 FAISS/Chroma, Top-3
Rerank -
生成 本地小模型 Qwen/Qwen2.5-1.5B

代码实现

class CostFirstRAG:
    """成本优先策略 - 几乎免费"""
  
    def __init__(self):
        # 全部本地模型,无 API 调用
        self.embedder = SentenceTransformer('all-MiniLM-L6-v2')
        self.vector_db = FAISS(index_factory='Flat')
        self.llm = TransformersLLM('Qwen/Qwen2.5-1.5B-Instruct')
  
    def query(self, user_query: str) -> str:
        # 本地 Embedding
        embedding = self.embedder.encode(user_query)
      
        # 快速检索 Top-3
        results = self.vector_db.search(embedding, top_k=3)
      
        # 简单阈值判断
        if results[0].score < 0.3:
            return "抱歉,我没有找到相关资料。"
      
        # 本地生成
        context = "\n".join([doc.content for doc in results])
        return self.llm.generate(f"根据以下资料简要回答:\n{context}\n\n问题:{user_query}")

性能指标

指标 目标值
延迟 <200ms
成本 0.1x(本地部署)
召回率 低(Recall@3 ≈ 0.3)
答案质量 ⭐⭐

6.5 策略五:质量优先(Quality-First)🏆

适用场景

  • 高端企业客服
  • 专业领域问答(法律、医疗、金融)
  • 对答案准确性要求极高
  • 预算充足

流程图

┌─────────────────────────────────────────────────────────────────┐
│                    质量优先策略(完整流程)                       │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  用户查询                                                        │
│    ↓                                                            │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │  Query Rewriting + Multi-Query (3 变体)                   │   │
│  └─────────────────────────────────────────────────────────┘   │
│    ↓                                                            │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │  三路检索:向量 + BM25 + HyDE                             │   │
│  │  每路 Top-30                                              │   │
│  └─────────────────────────────────────────────────────────┘   │
│    ↓                                                            │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │  RRF 融合 + 去重                                          │   │
│  └─────────────────────────────────────────────────────────┘   │
│    ↓                                                            │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │  Rerank (Cohere/Jina) Top-10                            │   │
│  └─────────────────────────────────────────────────────────┘   │
│    ↓                                                            │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │  LLM 生成 + 自检 + 引用验证                               │   │
│  └─────────────────────────────────────────────────────────┘   │
│    ↓                                                            │
│  高质量答案(带引用)                                           │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

核心配置

阶段 策略 参数
查询扩展 Query Rewriting + Multi-Query 1 改写 + 3 变体
检索 三路混合(向量+BM25+HyDE) Top-30/路
融合 RRF + 多样性过滤 k=60
Rerank 商业级 Rerank Cohere/Jina, Top-10
生成 高端 LLM + 自检 GPT-4/Claude

代码实现

class QualityFirstRAG:
    """质量优先策略 - 不计成本追求最佳效果"""
  
    def __init__(self):
        self.llm = OpenRouterLLM(model='anthropic/claude-sonnet-4.6')
        self.embedder = SentenceTransformer('bge-large-zh-v1.5')
        self.vector_db = ChromaDB()
        self.bm25 = BM25Search()
        self.reranker = CohereReranker()  # 商业级 Rerank
  
    def query(self, user_query: str) -> dict:
        """返回答案 + 引用 + 置信度"""
      
        # Step 1: Query Rewriting
        rewritten_query = self.llm.generate(
            f"将以下查询改写成更清晰的形式:\n{user_query}"
        )
      
        # Step 2: Multi-Query 扩展
        variants = self.llm.generate(
            f"为以下查询生成 3 个不同角度的变体:\n{rewritten_query}"
        )
        queries = [rewritten_query] + variants.split('\n')
      
        # Step 3: 三路检索
        all_results = []
        for q in queries:
            # 向量检索
            vector_results = self.vector_db.search(
                self.embedder.encode(q), top_k=30
            )
            # BM25
            keyword_results = self.bm25.search(q, top_k=30)
            # HyDE
            hypothetical_doc = self.llm.generate(
                f"为以下问题生成一个详细的答案:{q}"
            )
            hyde_results = self.vector_db.search(
                self.embedder.encode(hypothetical_doc), top_k=30
            )
            all_results.extend(vector_results + keyword_results + hyde_results)
      
        # Step 4: RRF 融合 + 去重
        fused = self.rrf_fusion(all_results, k=60)
      
        # Step 5: 商业级 Rerank
        reranked = self.reranker.rerank(rewritten_query, fused[:200], top_k=10)
      
        # Step 6: LLM 生成 + 引用验证
        response = self.llm.generate_with_citations(
            query=rewritten_query,
            documents=reranked[:8],
            require_citations=True
        )
      
        # Step 7: 自检(可选)
        if self.llm.self_check_confidence(response) < 0.8:
            # 置信度低 → 重新生成
            response = self.llm.generate(
                f"请重新审视以下资料,确保答案准确:\n\n{response}"
            )
      
        return {
            'answer': response,
            'references': reranked[:5],
            'confidence': self.llm.estimate_confidence(response)
        }

性能指标

指标 目标值
延迟 3000-5000ms
成本 15-20x
召回率 极高(Recall@100 > 0.95)
答案质量 ⭐⭐⭐⭐⭐
附加功能 引用溯源、置信度评估

6.6 五种策略对比总表

维度 速度优先 平衡型 召回率优先 成本优先 质量优先
延迟 <300ms 600-1000ms 2000-4000ms <200ms 3000-5000ms
成本 1x 3-5x 10-15x 0.1x 15-20x
召回率 中高 极高 极高
答案质量 ⭐⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐⭐⭐ ⭐⭐ ⭐⭐⭐⭐⭐
检索路数 1 路 1 路 2 路混合 1 路 3 路混合
Rerank ✅ 商业级
Multi-Query 可选
HyDE 可选
适用场景 实时对话 通用生产 专业研究 内部工具 高端客服

6.7 各策略的 Fallback 方案对比

策略 Fallback 1 Fallback 2 Fallback 3(保底)
速度优先 关键词检索 Top-3 - 直接 LLM 回答
平衡型 混合检索(向量+BM25) Query 重写后检索 直接 LLM 回答
召回率优先 扩大检索到 Top-100 触发 HyDE 多轮迭代检索
成本优先 降低阈值到 0.2 - 返回预设回复
质量优先 增加检索路数 Rerank 重试(换模型) 人工介入 + 事后回复

6.8 策略选择决策树

                    你的需求是什么?
                         │
        ┌────────────────┼────────────────┐
        ↓                ↓                ↓
    延迟<500ms       质量最重要       预算有限
        │                │                │
        ↓                ↓                ↓
   ┌────────┐      ┌────────────┐    ┌────────┐
   │速度优先│      │质量优先    │    │成本优先│
   └────────┘      │或召回率优先│    └────────┘
                   └────────────┘
                        │
              ┌─────────┴─────────┐
              ↓                   ↓
         专业领域问答        通用企业应用
              │                   │
              ↓                   ↓
         ┌────────┐         ┌────────┐
         │召回率优先│        │平衡型  │
         └────────┘         └────────┘

6.9 核心要点总结

五大策略概览

策略 核心目标 关键特性
速度优先 <300ms 延迟 单次检索,无 Rerank
平衡型 性价比最优 Query 重写+Rerank
召回率优先 Recall@50 > 0.9 Multi-Query+ 混合检索
成本优先 几乎免费 全部本地模型
质量优先 最佳效果 三路检索 + 商业 Rerank

选择建议

  • 实时对话 → 速度优先
  • 通用生产 → 平衡型
  • 专业研究 → 召回率优先
  • 内部工具 → 成本优先
  • 高端客服 → 质量优先

面试关键词

  • 策略选择
  • Fallback 机制
  • 成本效益权衡
  • 场景化方案

================================================================================

第 7 章 完整流程评价

7.1 完整流程:Multi-Query → Top-30 → Rerank → Top-5

这是一个非常经典且高质量的 RAG 检索优化流程!

流程拆解

┌─────────────────────────────────────────────────────────────────┐
│              完整 RAG 检索优化流程                               │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  用户查询                                                        │
│    ↓                                                            │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │  1. Multi-Query 扩展                                      │   │
│  │     生成 3-5 个查询变体                                    │   │
│  │     成本:+1 次 LLM 调用                                    │   │
│  └─────────────────────────────────────────────────────────┘   │
│    ↓                                                            │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │  2. 检索 Top-30                                           │   │
│  │     每个变体检索 30 条 → 共 90-150 条 → 去重融合             │   │
│  │     目标:高召回率 (Recall)                               │   │
│  └─────────────────────────────────────────────────────────┘   │
│    ↓                                                            │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │  3. Rerank 重排序                                         │   │
│  │     用 Cross-Encoder 对 90-150 条精细打分                 │   │
│  │     目标:高精度 (Precision)                              │   │
│  └─────────────────────────────────────────────────────────┘   │
│    ↓                                                            │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │  4. 返回 Top-5                                            │   │
│  │     取重排序后前 5 条给 LLM 生成                           │   │
│  └─────────────────────────────────────────────────────────┘   │
│    ↓                                                            │
│  最终答案                                                       │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

7.2 优势分析

优势 说明
召回率极高 Multi-Query 多角度覆盖 + Top-30 宽泛检索
精度有保障 Rerank 用 Cross-Encoder 精细筛选
架构清晰 两阶段:粗筛(Bi-Encoder)+ 精筛(Cross-Encoder)
可扩展 可加 Fallback、条件触发等优化

核心设计思想

┌─────────────────────────────────────────────────────────────┐
│              两阶段检索架构                                  │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  第一阶段:粗筛(召回优先)                                  │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  Multi-Query + Top-30                                │   │
│  │  - 用 Bi-Encoder 快速检索                             │   │
│  │  - 宁可多不可少,保证召回率                          │   │
│  │  - 90-150 条候选                                      │   │
│  └─────────────────────────────────────────────────────┘   │
│                         ↓                                   │
│  第二阶段:精筛(精度优先)                                  │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  Rerank + Top-5                                      │   │
│  │  - 用 Cross-Encoder 精细打分                         │   │
│  │  - 逐对比较 (query, document) 相似度                 │   │
│  │  - 取最相关的 5 条                                     │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  为什么这样设计?                                           │
│  - Bi-Encoder 快但粗糙 → 适合粗筛                          │
│  - Cross-Encoder 慢但精准 → 适合精筛                       │
│  - 组合使用,兼顾速度和精度                                 │
│                                                             │
└─────────────────────────────────────────────────────────────┘

7.3 性能与成本估算

延迟分析

阶段 延迟 说明
Multi-Query 生成 ~500ms LLM 生成 3-5 个变体
并行检索 Top-30 ~300ms 3-5 路检索并行执行
Rerank Top-100 ~800ms Cross-Encoder 精细打分
LLM 生成 ~1000ms 根据 Top-5 生成答案
总延迟 ~2600ms 约 2.6 秒

成本分析

组件 单次查询 本流程 倍数
LLM 调用(生成变体) 0 次 1 次 +$0.002
Embedding 1 次 3-5 次 +$0.0002~0.0004
向量检索 1 次 3-5 次 +$0.001~0.002
Rerank 0 次 1 次(~100 条) +$0.001
总成本 $0.0006 $0.0062~0.0074 ~10-12x

7.4 潜在问题

问题 说明
延迟较高 约 2000-3000ms,不适合实时场景
成本较高 约 10-12 倍基准成本
Rerank 依赖 选错模型效果打折
过度检索 简单查询可能不需要这么复杂

7.5 优化建议

1. 动态调整检索数量

根据查询类型调整 Top-K:

def get_top_k(query):
    """根据查询类型动态调整检索数量"""
  
    # 简短模糊查询 → 多检索一些
    if len(query.split()) < 5:
        return 50
  
    # 事实性查询 → 少检索一些
    if query.endswith("吗") or "?" in query:
        return 10
  
    # 默认
    return 30

2. Rerank 前预过滤

在 Rerank 之前先过滤掉明显不相关的文档:

def pre_filter(results, threshold=0.3):
    """预过滤,去掉低分文档"""
    return [r for r in results if r.score >= threshold]

3. 条件触发 Multi-Query

不是所有查询都需要 Multi-Query:

def should_use_multi_query(query):
    """只对特定查询使用 Multi-Query"""
  
    # 查询太短 → 用
    if len(query.split()) < 5:
        return True
  
    # 有歧义词 → 用
    ambiguous_words = ["怎么", "如何", "什么"]
    if any(word in query for word in ambiguous_words):
        return True
  
    # 事实性查询 → 不用
    return False

4. 缓存优化

# 常见查询的变体和结果可以缓存
cache = {}

def get_cached_results(query):
    if query in cache:
        return cache[query]  # 直接返回缓存结果
  
    # 否则正常检索
    results = retrieve(query)
    cache[query] = results
    return results

7.6 完整代码实现

class AdvancedRAG:
    """
    完整流程实现:
    Multi-Query → Top-30 → Rerank → Top-5
    """
  
    def __init__(self):
        self.llm = OpenRouterLLM(model='gpt-4o')
        self.embedder = SentenceTransformer('bge-large-zh-v1.5')
        self.vector_db = ChromaDB()
        self.bm25 = BM25Search()
        self.reranker = CrossEncoderReranker('bge-reranker-large')
  
    def query(self, user_query: str) -> str:
        # Step 1: 判断是否需要 Multi-Query
        use_multi = self.should_use_multi_query(user_query)
      
        if use_multi:
            # Step 2a: Multi-Query 扩展
            variants = self.llm.generate(
                f"为以下查询生成 3 个不同角度的变体:\n{user_query}"
            )
            queries = [user_query] + variants.split('\n')
        else:
            queries = [user_query]
      
        # Step 3: 检索(每个变体 Top-30)
        all_results = []
        for q in queries:
            results = self.vector_db.search(
                self.embedder.encode(q), top_k=30
            )
            all_results.extend(results)
      
        # Step 4: 去重 + RRF 融合
        if use_multi:
            fused = self.rrf_fusion(all_results, k=60)
        else:
            fused = all_results
      
        # Step 5: 预过滤
        filtered = self.pre_filter(fused, threshold=0.3)
      
        # Step 6: Rerank 精筛 Top-5
        reranked = self.reranker.rerank(user_query, filtered, top_k=5)
      
        # Step 7: 生成答案
        context = "\n".join([doc.content for doc in reranked])
        return self.llm.generate(
            f"根据以下资料回答问题:\n\n{context}\n\n问题:{user_query}"
        )
  
    def should_use_multi_query(self, query):
        """条件触发 Multi-Query"""
        if len(query.split()) < 5:
            return True
        ambiguous_words = ["怎么", "如何", "什么", "为什么"]
        if any(word in query for word in ambiguous_words):
            return True
        return False
  
    def pre_filter(self, results, threshold=0.3):
        """预过滤"""
        return [r for r in results if r.score >= threshold]
  
    def rrf_fusion(self, results_list, k=60):
        """RRF 融合"""
        rrf_scores = {}
        for results in results_list:
            for rank, doc in enumerate(results, start=1):
                if doc.id not in rrf_scores:
                    rrf_scores[doc.id] = 0
                rrf_scores[doc.id] += 1 / (k + rank)
        return sorted(rrf_scores.items(), key=lambda x: x[1], reverse=True)

7.7 一张图总结

┌─────────────────────────────────────────────────────────────────┐
│              完整流程评价:Multi-Query → Top-30 → Rerank → Top-5│
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  流程:                                                         │
│  用户查询 → Multi-Query → 检索 Top-30 → Rerank → Top-5 → 答案   │
│                                                                 │
│  优势:                                                         │
│  ✅ 召回率极高(Multi-Query 多角度覆盖)                         │
│  ✅ 精度有保障(Cross-Encoder 精细筛选)                        │
│  ✅ 架构清晰(粗筛 + 精筛两阶段)                               │
│                                                                 │
│  代价:                                                         │
│  ❌ 延迟高(~2600ms)                                           │
│  ❌ 成本高(~10-12x)                                           │
│                                                                 │
│  优化建议:                                                     │
│  ① 动态调整检索数量(根据查询类型)                             │
│  ② Rerank 前预过滤(去掉明显不相关的)                           │
│  ③ 条件触发 Multi-Query(不是所有查询都需要)                    │
│  ④ 缓存常见查询(减少重复计算)                                 │
│                                                                 │
│  适用场景:                                                     │
│  ✅ 专业领域问答(法律、医疗)                                  │
│  ✅ 复杂研究型问题                                              │
│  ✅ 对答案质量要求高的场景                                      │
│  ❌ 实时对话(延迟太高)                                        │
│  ❌ 简单事实查询(杀鸡用牛刀)                                  │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

7.8 核心要点总结

流程关键点

  • 四步流程:Multi-Query → Top-30 → Rerank → Top-5
  • 两阶段设计:粗筛(召回)+ 精筛(精度)
  • 延迟:约 2600ms
  • 成本:约 10-12 倍

优势

  1. 召回率极高
  2. 精度有保障
  3. 架构清晰
  4. 可扩展性强

优化建议

  1. 动态调整检索数量
  2. Rerank 前预过滤
  3. 条件触发 Multi-Query
  4. 缓存常见查询

面试关键词

  • 两阶段检索
  • 粗筛 + 精筛
  • Bi-Encoder + Cross-Encoder
  • 召回率 vs 精度

================================================================================

第 8 章 三种技术对比:Query Rewriting vs HyDE vs Multi-Query

8.1 核心思想对比

技术 核心思想 比喻 嵌入对象
Query Rewriting 把问题说清楚 翻译官 改写后的问题
HyDE 用答案搜答案 美食家 假设答案
Multi-Query 多角度问问题 分身术 多个查询变体

形象理解

  • Query Rewriting:你说的不清楚,我帮你翻译成清楚的一句话
  • HyDE:先编一个假答案,再用假答案去找真答案
  • Multi-Query:一个问题可能不够,我帮你同时问好几个问题

8.2 工作流程对比

┌─────────────────────────────────────────────────────────────────┐
│                    三种技术工作流程对比                          │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  Query Rewriting:                                               │
│  用户查询 → [LLM 改写] → 清晰的问题 → [检索] → 答案             │
│           成本:+1 次 LLM                                        │
│                                                                 │
│  HyDE:                                                          │
│  用户查询 → [LLM 生成假设答案] → 假设答案 → [检索] → 答案        │
│           成本:+1 次 LLM + 1 次嵌入                               │
│                                                                 │
│  Multi-Query:                                                   │
│  用户查询 → [LLM 生成 3-5 变体] → 多个问题 → [并行检索] → 融合 → 答案  │
│           成本:+1 次 LLM + 3-5 次嵌入 + 3-5 次检索                  │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

8.3 详细成本分析

成本拆解表(基于典型云 API 价格)

成本项 基准(无优化) Query Rewriting HyDE Multi-Query (3 变体)
LLM 调用 1 次(生成) 2 次 2 次 2 次
Embedding 1 次 1 次 2 次 3 次
向量检索 1 次 1 次 1 次 3 次
Rerank 0 次 0 次 0 次 0 次
总成本 $0.0026 $0.0046 $0.0047 $0.0058
倍数 1x 1.8x 1.9x 2.2x
延迟 ~160ms ~660ms ~760ms ~1200ms

注:这里的倍数是仅检索阶段的对比。如果包含 LLM 生成答案的成本,总倍数会被摊薄。

成本计算过程

单价假设

  • LLM 调用:$0.002/次
  • Embedding: $0.0001/次
  • 向量检索:$0.0005/次

基准成本

= 1×LLM + 1×Embedding + 1×检索
= $0.002 + $0.0001 + $0.0005 = $0.0026

Query Rewriting 成本

= 2×LLM + 1×Embedding + 1×检索
= $0.004 + $0.0001 + $0.0005 = $0.0046

HyDE 成本

= 2×LLM + 2×Embedding + 1×检索
= $0.004 + $0.0002 + $0.0005 = $0.0047

Multi-Query (3 变体) 成本

= 2×LLM + 3×Embedding + 3×检索
= $0.004 + $0.0003 + $0.0015 = $0.0058

增量成本对比(仅计算优化部分的额外成本)

技术 额外 LLM 额外 Embedding 额外检索 增量成本 总倍数
Query Rewriting +1 次 0 0 +$0.002 1.8x
HyDE +1 次 +1 次 0 +$0.0021 1.9x
Multi-Query (3 变体) +1 次 +2 次 +2 次 +$0.0052 3.2x

注:Multi-Query 的倍数在不同计算口径下有差异。如果考虑完整的 RAG 流程(包含 LLM 生成答案),倍数约为 2-3x;如果只看检索阶段,倍数可达 6-8x。


8.4 优势分析

效果提升对比

指标 基准 Query Rewriting 提升 HyDE 提升 Multi-Query 提升
Recall@10 0.45 0.55 +22% 0.58 +29% 0.62 +38%
MRR 0.40 0.48 +20% 0.52 +30% 0.55 +38%
NDCG@5 0.50 0.58 +16% 0.62 +24% 0.65 +30%
答案质量 ⭐⭐⭐ ⭐⭐⭐⭐ - ⭐⭐⭐⭐ - ⭐⭐⭐⭐ -

各技术独特优势

Query Rewriting 优势

优势 说明
成本最低 仅增加 1 次 LLM 调用
延迟最低 约 500ms 额外延迟
实现简单 只需一个 Prompt
适合简短查询 “python list” → “Python 列表使用方法”

HyDE 优势

优势 说明
桥接语义鸿沟 问题空间 → 答案空间
适合问答型查询 "什么是 X"类问题效果最好
利用 LLM 知识 LLM 知道答案通常长什么样
缓解词汇不匹配 同时包含用户词和文档词

Multi-Query 优势

优势 说明
召回率最高 多角度覆盖,减少遗漏
处理歧义最强 "苹果"同时找到水果和公司
鲁棒性强 某个变体不好,其他可以弥补
适合开放型问题 "怎么学好编程"类问题

8.5 适用场景对比

推荐使用场景

技术 推荐使用场景 不推荐使用场景
Query Rewriting 简短查询、模糊查询 已经很清晰的长查询
HyDE 问答型、开放型问题 导航型查询、事实性查询
Multi-Query 歧义查询、复杂问题 简单事实查询、延迟敏感场景

查询类型匹配表

查询类型 示例 推荐技术 预期提升
简短模糊 “python list” Query Rewriting + Multi-Query Recall@10 +50%
问答型 “什么是机器学习” HyDE MRR +30%
歧义查询 “苹果” Multi-Query Recall@10 +80%
开放型 “怎么学好编程” HyDE + Multi-Query NDCG +40%
事实性 “珠穆朗玛峰多高” 无需优化 -
导航型 “python 官网” 无需优化 -

8.6 组合使用策略

常见组合对比

组合 成本 延迟 效果 适用场景
Query Rewriting + HyDE 2.5x ~900ms ⭐⭐⭐⭐ 问答型 + 模糊查询
Query Rewriting + Multi-Query 3.5x ~1300ms ⭐⭐⭐⭐⭐ 歧义 + 开放型问题
HyDE + Multi-Query 4x ~1500ms ⭐⭐⭐⭐⭐ 复杂研究型问题
三者全用 5x+ ~2000ms ⭐⭐⭐⭐⭐+ 高端质量优先场景

组合工作流程

┌─────────────────────────────────────────────────────────────────┐
│                    三者组合工作流程                              │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  用户查询:"怎么学好编程?"                                      │
│         ↓                                                       │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │  Step 1: Query Rewriting                                 │   │
│  │  "如何通过系统学习路径提升编程能力?"                     │   │
│  └─────────────────────────────────────────────────────────┘   │
│         ↓                                                       │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │  Step 2: HyDE (可选)                                     │   │
│  │  生成假设答案:"编程学习需要...掌握基础语法..."           │   │
│  └─────────────────────────────────────────────────────────┘   │
│         ↓                                                       │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │  Step 3: Multi-Query                                     │   │
│  │  变体 1: "编程入门学习路径"                               │   │
│  │  变体 2: "编程练习资源推荐"                               │   │
│  │  变体 3: "编程能力提升方法"                               │   │
│  └─────────────────────────────────────────────────────────┘   │
│         ↓                                                       │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │  Step 4: 并行检索 + RRF 融合                               │   │
│  └─────────────────────────────────────────────────────────┘   │
│         ↓                                                       │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │  Step 5: Rerank + LLM 生成                                │   │
│  └─────────────────────────────────────────────────────────┘   │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

8.7 面试回答模板

如果面试官问:“Query Rewriting、HyDE、Multi-Query 有什么区别?怎么选?”

"这三种都是 RAG 检索优化技术,但方法和适用场景不同。

核心区别:

技术 做法 成本 优势
Query Rewriting 改写问题 低 (1.8x) 成本低,适合模糊查询
HyDE 生成假设答案 中 (1.9x) 桥接问题 - 答案语义鸿沟
Multi-Query 多角度查询 高 (3.2x) 召回率最高,处理歧义

怎么选:

  • 简短模糊查询(如’python list’)→ Query Rewriting
  • 问答型问题(如’什么是 X’)→ HyDE
  • 歧义查询(如’苹果’)→ Multi-Query
  • 复杂开放型问题→ 组合使用(Query Rewriting + Multi-Query)

实际项目里,我会根据延迟和预算权衡:

  • 延迟敏感 → Query Rewriting
  • 质量优先 → 三者组合
  • 一般生产环境 → Query Rewriting + Multi-Query"

8.8 一张图总结

┌─────────────────────────────────────────────────────────────────┐
│          Query Rewriting vs HyDE vs Multi-Query                 │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  成本对比:(倍数 = 检索阶段相对成本)                            │
│  Query Rewriting  ████████  1.8x                               │
│  HyDE             ██████████  1.9x                             │
│  Multi-Query      ████████████████  3.2x                       │
│                                                                 │
│  效果对比:(Recall@10 提升)                                    │
│  Query Rewriting  ████████  +22%                               │
│  HyDE             ██████████  +29%                             │
│  Multi-Query      ██████████████  +38%                         │
│                                                                 │
│  延迟对比:                                                     │
│  Query Rewriting  ████████  ~660ms                             │
│  HyDE             ██████████  ~760ms                           │
│  Multi-Query      ████████████████  ~1200ms                    │
│                                                                 │
│  推荐选择:                                                     │
│  ├── 预算有限/延迟敏感 → Query Rewriting                        │
│  ├── 问答型问题 → HyDE                                         │
│  ├── 歧义查询/复杂问题 → Multi-Query                            │
│  └── 质量优先 → 组合使用                                        │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

8.9 核心要点总结

三种技术对比

技术 核心思想 成本倍数 Recall 提升 适用场景
Query Rewriting 把问题说清楚 1.8x +22% 简短模糊查询
HyDE 用答案搜答案 1.9x +29% 问答型问题
Multi-Query 多角度问问题 3.2x +38% 歧义查询

选择建议

  • 预算有限/延迟敏感 → Query Rewriting
  • 问答型问题 → HyDE
  • 歧义查询/复杂问题 → Multi-Query
  • 质量优先 → 组合使用

面试关键词

  • 翻译官/美食家/分身术比喻
  • 成本效益权衡
  • 场景化选择
  • 组合使用策略

================================================================================

附录 A 术语表

A.1 RAG 相关术语

术语 英文 解释
RAG Retrieval-Augmented Generation 检索增强生成,一种结合检索和生成的 AI 技术
检索器 Retriever 负责从文档库中查找相关文档的模块
生成器 Generator 负责根据检索到的文档生成答案的模块(通常是 LLM)
向量数据库 Vector Database 存储文档嵌入向量的数据库,支持相似度搜索
嵌入 Embedding 将文本转换成向量表示的过程
相似度搜索 Similarity Search 根据向量相似度查找相关文档
余弦相似度 Cosine Similarity 衡量两个向量相似程度的指标,范围 [-1, 1]

A.2 检索优化术语

术语 英文 解释
Query Rewriting 查询重写 将用户原始查询转换成更适合检索的形式
查询扩展 Query Expansion 添加同义词或相关词来扩展查询
查询改写 Query Reformulation 用更清晰的方式重新表述查询
查询分解 Query Decomposition 将复杂查询拆分成多个子查询
HyDE Hypothetical Document Embeddings 假设文档嵌入,用假设答案的向量去检索
Multi-Query 多查询检索 生成多个查询变体并行检索,然后合并结果
RRF Reciprocal Rank Fusion 倒数排名融合,一种合并多个检索结果的方法
Rerank 重排序 对检索结果进行精细打分和重新排序
Bi-Encoder 双编码器 分别编码查询和文档的模型,速度快
Cross-Encoder 交叉编码器 同时编码 (查询,文档) 对的模型,精度高
混合检索 Hybrid Search 结合多种检索方法(如向量 + 关键词)
BM25 BM25 一种经典的关键词检索算法

A.3 评估指标术语

术语 英文 解释
MRR Mean Reciprocal Rank 平均倒数排名,只关心第一个正确答案的排名
NDCG Normalized Discounted Cumulative Gain 归一化折损累计增益,考虑相关性和排序质量
Recall@K 召回率@K 前 K 个结果中有多少正确答案
Precision@K 精确率@K 前 K 个结果中有多少是正确答案
DCG Discounted Cumulative Gain 折损累计增益
IDCG Ideal DCG 理想情况下的 DCG

A.4 模型相关术语

术语 示例 解释
Embedding 模型 all-MiniLM-L6-v2, bge-large-zh 将文本转换成向量的模型
Rerank 模型 bge-reranker-large, Cohere Rerank 对检索结果进行精细打分的模型
LLM GPT-4, Claude, Qwen 大型语言模型,用于生成答案

A.5 快速查阅表

核心技术速查

技术 作用 成本 适用场景
Query Rewriting 让问题更清晰 简短模糊查询
HyDE 用答案搜答案 问答型问题
Multi-Query 多角度覆盖 歧义查询
RRF 融合多个结果 无额外成本 Multi-Query 结果融合
Rerank 精细排序 需要高精度场景

评估指标速查

指标 关心什么 适用场景
MRR 第一个正确答案的排名 问答系统
NDCG 所有结果的相关性 + 排序 搜索、推荐
Recall@K 前 K 个中有多少正确答案 召回率优先场景

A.6 常见缩写

缩写 全称 中文
RAG Retrieval-Augmented Generation 检索增强生成
LLM Large Language Model 大型语言模型
API Application Programming Interface 应用程序接口
MRR Mean Reciprocal Rank 平均倒数排名
NDCG Normalized Discounted Cumulative Gain 归一化折损累计增益
RRF Reciprocal Rank Fusion 倒数排名融合
HyDE Hypothetical Document Embeddings 假设文档嵌入
BM25 BM25 (Okapi BM25) BM25 算法
FAISS Facebook AI Similarity Search Facebook 向量搜索库
ChromaDB Chroma Database Chroma 向量数据库

================================================================================

附录 B 代码片段速查

B.1 Query Rewriting 实现

class QueryRewriter:
    """查询重写实现"""
  
    def __init__(self, llm):
        self.llm = llm
  
    def rewrite(self, query: str) -> str:
        """将查询改写成更适合检索的形式"""
        prompt = f"""将以下查询改写成更适合搜索引擎的形式:
原始查询:{query}
要求:补充上下文,使用明确术语,保持原意"""
      
        rewritten = self.llm.generate(prompt)
        return rewritten.strip()
  
    def expand(self, query: str, num_synonyms: int = 3) -> str:
        """查询扩展:添加同义词"""
        prompt = f"""为以下查询生成{num_synonyms}个同义词或相关词:
查询:{query}
输出格式:每行一个词"""
      
        synonyms = self.llm.generate(prompt).split('\n')
        return query + ' ' + ' '.join(synonyms)
  
    def decompose(self, query: str, num_subquestions: int = 3) -> list:
        """查询分解:拆分成多个子问题"""
        prompt = f"""将以下查询拆分成{num_subquestions}个子问题:
查询:{query}
输出格式:每行一个子问题"""
      
        subquestions = self.llm.generate(prompt).split('\n')
        return [q.strip() for q in subquestions if q.strip()]

使用示例

# 初始化
rewriter = QueryRewriter(llm)

# 改写查询
query = "python list"
rewritten = rewriter.rewrite(query)
print(rewritten)  # "Python 编程语言中列表的使用方法"

# 查询扩展
expanded = rewriter.expand(query)
print(expanded)  # "python list array data structure methods"

# 查询分解
subquestions = rewriter.decompose("怎么学好编程?")
print(subquestions)  
# ["编程入门需要学什么?", "如何提高编程能力?", "有哪些编程学习资源?"]

B.2 HyDE 实现

class HyDERetriever:
    """HyDE 检索器实现"""
  
    def __init__(self, llm, embedder, vector_db):
        self.llm = llm
        self.embedder = embedder
        self.vector_db = vector_db
  
    def generate_hypothetical_doc(self, query: str) -> str:
        """生成假设文档"""
        prompt = f"""请为以下问题生成一个详细的答案:
问题:{query}
要求:答案应该详细、专业,包含相关关键词"""
      
        return self.llm.generate(prompt)
  
    def retrieve(self, query: str, top_k: int = 5) -> list:
        """使用 HyDE 进行检索"""
        # Step 1: 生成假设文档
        hypothetical_doc = self.generate_hypothetical_doc(query)
      
        # Step 2: 嵌入假设文档
        embedding = self.embedder.encode(hypothetical_doc)
      
        # Step 3: 检索相似文档
        results = self.vector_db.search(embedding, top_k=top_k)
      
        return results

使用示例

# 初始化
hyde = HyDERetriever(llm, embedder, vector_db)

# 检索
query = "恐龙怎么灭绝的?"
results = hyde.retrieve(query, top_k=5)

for doc in results:
    print(f"分数:{doc.score}, 内容:{doc.content[:50]}...")

B.3 Multi-Query 实现

class MultiQueryRetriever:
    """Multi-Query 检索器实现"""
  
    def __init__(self, llm, embedder, vector_db, k=60):
        self.llm = llm
        self.embedder = embedder
        self.vector_db = vector_db
        self.k = k  # RRF 平滑参数
  
    def generate_variants(self, query: str, num_variants: int = 3) -> list:
        """生成查询变体"""
        prompt = f"""为以下查询生成{num_variants}个不同角度的变体:
原始查询:{query}
要求:每个变体从不同角度询问,输出格式:每行一个变体"""
      
        variants = self.llm.generate(prompt).split('\n')
        return [v.strip() for v in variants if v.strip()]
  
    def rrf_fusion(self, results_list: list) -> list:
        """RRF 倒数排名融合"""
        rrf_scores = {}
      
        for results in results_list:
            for rank, doc in enumerate(results, start=1):
                if doc.id not in rrf_scores:
                    rrf_scores[doc.id] = 0
                rrf_scores[doc.id] += 1 / (self.k + rank)
      
        # 按 RRF 分数排序
        sorted_docs = sorted(rrf_scores.items(), key=lambda x: x[1], reverse=True)
        return sorted_docs
  
    def retrieve(self, query: str, num_variants: int = 3, top_k: int = 30) -> list:
        """Multi-Query 检索"""
        # Step 1: 生成变体
        variants = self.generate_variants(query, num_variants)
        queries = [query] + variants  # 包含原始查询
      
        # Step 2: 并行检索
        all_results = []
        for q in queries:
            embedding = self.embedder.encode(q)
            results = self.vector_db.search(embedding, top_k=top_k)
            all_results.append(results)
      
        # Step 3: RRF 融合
        fused = self.rrf_fusion(all_results)
      
        return fused

使用示例

# 初始化
multi_retriever = MultiQueryRetriever(llm, embedder, vector_db)

# 检索
query = "怎么变强?"
results = multi_retriever.retrieve(query, num_variants=3, top_k=30)

print(f"融合后结果数量:{len(results)}")
for doc_id, rrf_score in results[:10]:
    print(f"文档 ID: {doc_id}, RRF 分数:{rrf_score:.4f}")

B.4 RRF 实现

def reciprocal_rank_fusion(results_list, k=60):
    """
    RRF 倒数排名融合
  
    参数:
        results_list: List[List[doc_id, score, rank]]
                      多个查询结果列表,每个结果包含文档 ID、分数、排名
        k: 平滑参数,默认 60
  
    返回:
        sorted_docs: 按 RRF 分数排序的文档列表
    """
    rrf_scores = {}
  
    for results in results_list:
        for rank, (doc_id, score, _) in enumerate(results, start=1):
            if doc_id not in rrf_scores:
                rrf_scores[doc_id] = 0
            rrf_scores[doc_id] += 1 / (k + rank)
  
    # 按 RRF 分数排序
    sorted_docs = sorted(rrf_scores.items(), key=lambda x: x[1], reverse=True)
    return sorted_docs


# 简化版(如果结果已经是文档对象列表)
def rrf_simple(results_list, k=60):
    """
    简化版 RRF 实现
  
    参数:
        results_list: List[List[Document]]
                      多个查询结果列表,每个结果是 Document 对象列表
        k: 平滑参数,默认 60
  
    返回:
        fused_results: 融合后的文档列表
    """
    rrf_scores = {}
    doc_map = {}  # 保存 doc_id 到 Document 对象的映射
  
    for results in results_list:
        for rank, doc in enumerate(results, start=1):
            if doc.id not in rrf_scores:
                rrf_scores[doc.id] = 0
                doc_map[doc.id] = doc
            rrf_scores[doc.id] += 1 / (k + rank)
  
    # 按 RRF 分数排序,返回 Document 对象
    sorted_ids = sorted(rrf_scores.keys(), key=lambda x: rrf_scores[x], reverse=True)
    fused_results = [doc_map[doc_id] for doc_id in sorted_ids]
  
    return fused_results

使用示例

# 假设有 3 个查询的检索结果
results1 = [
    Document(id="doc_a", content="...", score=0.9),
    Document(id="doc_b", content="...", score=0.8),
    Document(id="doc_c", content="...", score=0.7),
]
results2 = [
    Document(id="doc_b", content="...", score=0.95),
    Document(id="doc_c", content="...", score=0.85),
    Document(id="doc_a", content="...", score=0.75),
]
results3 = [
    Document(id="doc_c", content="...", score=0.92),
    Document(id="doc_a", content="...", score=0.82),
    Document(id="doc_b", content="...", score=0.72),
]

# RRF 融合
results_list = [results1, results2, results3]
fused = rrf_simple(results_list, k=60)

print("融合后排序:")
for doc in fused:
    print(f"  {doc.id}: {doc.content[:30]}...")

B.5 完整 RAG 策略实现

速度优先策略

class SpeedFirstRAG:
    """速度优先策略 - 目标 <300ms"""
  
    def __init__(self):
        self.embedder = SentenceTransformer('all-MiniLM-L6-v2')
        self.vector_db = ChromaDB(collection='fast_collection')
        self.llm = OpenRouterLLM(model='gpt-4o-mini')
  
    def query(self, user_query: str) -> str:
        query = user_query.strip()
        embedding = self.embedder.encode(query)
        results = self.vector_db.search(embedding, top_k=5)
      
        if not results or results[0].score < 0.5:
            return self.llm.generate(f"直接回答:{query}")
      
        context = "\n".join([doc.content for doc in results])
        return self.llm.generate(f"根据上下文回答:\n{context}\n\n问题:{query}")

平衡型策略

class BalancedRAG:
    """平衡型策略 - 性价比最优"""
  
    def __init__(self):
        self.embedder = SentenceTransformer('bge-base-zh-v1.5')
        self.vector_db = ChromaDB()
        self.reranker = CrossEncoderReranker('bge-reranker-base')
        self.llm = OpenRouterLLM(model='gpt-4o-mini')
  
    def query(self, user_query: str) -> str:
        # Query Rewriting(针对短查询)
        if len(user_query.split()) < 5:
            query = self.llm.generate(
                f"将以下查询改写成更适合检索的形式:\n{user_query}"
            )
        else:
            query = user_query
      
        # 检索 Top-20
        embedding = self.embedder.encode(query)
        results = self.vector_db.search(embedding, top_k=20)
      
        # Rerank Top-8
        reranked = self.reranker.rerank(query, results, top_k=8)
      
        # 生成答案
        context = "\n".join([doc.content for doc in reranked[:5]])
        return self.llm.generate(f"根据上下文回答:\n{context}\n\n问题:{query}")

质量优先策略

class QualityFirstRAG:
    """质量优先策略 - 不计成本追求最佳效果"""
  
    def __init__(self):
        self.llm = OpenRouterLLM(model='anthropic/claude-sonnet-4.6')
        self.embedder = SentenceTransformer('bge-large-zh-v1.5')
        self.vector_db = ChromaDB()
        self.bm25 = BM25Search()
        self.reranker = CohereReranker()
  
    def query(self, user_query: str) -> dict:
        # Query Rewriting
        rewritten_query = self.llm.generate(
            f"将以下查询改写成更清晰的形式:\n{user_query}"
        )
      
        # Multi-Query 扩展
        variants = self.llm.generate(
            f"为以下查询生成 3 个不同角度的变体:\n{rewritten_query}"
        )
        queries = [rewritten_query] + variants.split('\n')
      
        # 三路检索(向量 + BM25 + HyDE)
        all_results = []
        for q in queries:
            vector_results = self.vector_db.search(self.embedder.encode(q), top_k=30)
            keyword_results = self.bm25.search(q, top_k=30)
            hypothetical_doc = self.llm.generate(f"为以下问题生成一个详细的答案:{q}")
            hyde_results = self.vector_db.search(self.embedder.encode(hypothetical_doc), top_k=30)
            all_results.extend(vector_results + keyword_results + hyde_results)
      
        # RRF 融合
        fused = self.rrf_fusion(all_results, k=60)
      
        # 商业级 Rerank
        reranked = self.reranker.rerank(rewritten_query, fused[:200], top_k=10)
      
        # LLM 生成 + 引用
        response = self.llm.generate_with_citations(
            query=rewritten_query,
            documents=reranked[:8],
            require_citations=True
        )
      
        return {
            'answer': response,
            'references': reranked[:5],
            'confidence': self.llm.estimate_confidence(response)
        }

B.6 评估指标计算

def calculate_mrr(queries_results):
    """
    计算 MRR(平均倒数排名)
  
    参数:
        queries_results: List[List[bool]]
                        每个查询的结果列表,True 表示正确答案
  
    返回:
        mrr: 平均倒数排名
    """
    reciprocal_ranks = []
  
    for results in queries_results:
        for rank, is_correct in enumerate(results, start=1):
            if is_correct:
                reciprocal_ranks.append(1 / rank)
                break
        else:
            reciprocal_ranks.append(0)  # 没有找到正确答案
  
    return sum(reciprocal_ranks) / len(reciprocal_ranks)


def calculate_dcg(relevances):
    """
    计算 DCG(折损累计增益)
  
    参数:
        relevances: List[float]
                   每个结果的相关性分数(0-3)
  
    返回:
        dcg: 折损累计增益
    """
    dcg = 0
    for i, rel in enumerate(relevances):
        rank = i + 1
        dcg += rel / np.log2(rank + 1)
    return dcg


def calculate_ndcg(relevances):
    """
    计算 NDCG(归一化折损累计增益)
  
    参数:
        relevances: List[float]
                   每个结果的相关性分数(0-3)
  
    返回:
        ndcg: 归一化折损累计增益
    """
    # 计算 DCG
    dcg = calculate_dcg(relevances)
  
    # 计算 IDCG(理想排序下的 DCG)
    ideal_relevances = sorted(relevances, reverse=True)
    idcg = calculate_dcg(ideal_relevances)
  
    if idcg == 0:
        return 0
  
    return dcg / idcg


# 使用示例
if __name__ == "__main__":
    # MRR 示例
    queries_results = [
        [False, False, True, False, False],  # 第一个正确答案在第 3 名
        [True, False, False, False, False],  # 第一个正确答案在第 1 名
        [False, True, False, False, False],  # 第一个正确答案在第 2 名
    ]
    mrr = calculate_mrr(queries_results)
    print(f"MRR: {mrr:.3f}")  # 输出:MRR: 0.611
  
    # NDCG 示例
    relevances = [3, 2, 3, 1, 0]  # 每个结果的相关性分数
    ndcg = calculate_ndcg(relevances)
    print(f"NDCG: {ndcg:.3f}")

B.7 快速参考表

技术 关键函数 核心参数
Query Rewriting llm.generate(prompt) prompt 模板
HyDE embedder.encode(hypothetical_doc) 假设文档生成 prompt
Multi-Query generate_variants(query) num_variants, top_k
RRF rrf_fusion(results_list) k=60
Rerank reranker.rerank(query, results) top_k

================================================================================

附录 C 面试速查表

C.1 概念定义速查

概念 一句话定义 关键词
Query Rewriting 把用户的原始查询转换成更适合检索的形式 翻译官、补充上下文
HyDE 先让 LLM 生成一个假设答案,再用这个假设答案的向量去检索 用答案搜答案、语义鸿沟
Multi-Query 生成多个查询变体并行检索,然后合并结果 分身术、多角度覆盖
RRF 一种合并多个检索结果的方法,只看排名不看分数 倒数排名、k=60
Rerank 对检索结果进行精细打分和重新排序 Cross-Encoder、精筛
MRR 只关心第一个正确答案排第几名 问答系统、倒数排名
NDCG 同时考虑相关性和排序质量 多级相关、折损因子

C.2 技术对比速查

三种优化技术对比

维度 Query Rewriting HyDE Multi-Query
核心思想 改写问题 生成假设答案 多角度查询
比喻 翻译官 美食家 分身术
成本倍数 1.8x 1.9x 3.2x
延迟 ~660ms ~760ms ~1200ms
Recall 提升 +22% +29% +38%
适用场景 简短模糊查询 问答型问题 歧义查询

五种策略对比

策略 延迟 成本 召回率 适用场景
速度优先 <300ms 1x 实时对话
平衡型 600-1000ms 3-5x 中高 通用生产
召回率优先 2000-4000ms 10-15x 极高 专业研究
成本优先 <200ms 0.1x 内部工具
质量优先 3000-5000ms 15-20x 极高 高端客服

C.3 场景选择速查

根据需求选择技术

用户需求 推荐技术 理由
“系统要很快” Query Rewriting 成本最低,延迟最低
“用户问题很短” Query Rewriting + Multi-Query 补充信息 + 多角度
“问题是’什么是 X’” HyDE 问答型,桥接语义鸿沟
“问题有歧义” Multi-Query 同时覆盖多个含义
“答案要全面” Multi-Query + Rerank 高召回 + 高精度
“预算有限” Query Rewriting 只增加 1 次 LLM 调用
“质量最重要” 三者组合 最佳效果

根据查询类型选择

查询类型 示例 推荐技术
简短模糊 “python list” Query Rewriting
问答型 “什么是机器学习” HyDE
歧义查询 “苹果” Multi-Query
开放型 “怎么学好编程” HyDE + Multi-Query
事实性 “珠穆朗玛峰多高” 无需优化
导航型 “python 官网” 无需优化

C.4 面试回答模板

模板 1:介绍 Query Rewriting

"Query Rewriting 就是把用户的原始查询转换成更适合检索的形式。

用户查询通常有两个问题:太简短和表述模糊。比如只搜’python’,不知道是蛇还是编程语言。

主要有三种方法:查询扩展(添加同义词)、查询改写(换说法)、查询分解(拆问题)。

在 RAG 系统里,查询重写放在检索之前,可以用 LLM 来实现。我在项目里用过,效果提升很明显。"

模板 2:介绍 HyDE

"HyDE 是 Hypothetical Document Embeddings 的缩写,中文叫’假设文档嵌入’。

核心思想是:先让 LLM 生成一个假设的答案,再用这个假设答案的向量去检索真实文档。简单说,就是’用答案搜答案’。

它有效的原因有三个:填补语义鸿沟(问题空间→答案空间)、利用 LLM 的预训练知识、增加信息密度。

反直觉的是,假设答案不需要正确,只要语言风格像答案、关键词覆盖到位,就能提升检索效果。"

模板 3:介绍 Multi-Query

"Multi-Query 是一种检索优化技术,核心思想是生成多个查询变体并行检索,然后合并结果。

用户查询有歧义性和覆盖不全的问题。比如’怎么变强’,可能是健身、编程、学习,单次查询只能命中一个角度。

Multi-Query 分三步:生成变体、并行检索、结果融合(常用 RRF)。不同变体可能命中不同但相关的文档,合并后覆盖率更高。

成本会增加 6-8 倍,但对于开放型问题和歧义查询,效果提升明显。"

模板 4:介绍 RRF

"RRF 是 Reciprocal Rank Fusion 的缩写,中文叫’倒数排名融合’。

公式是:RRF 分数 = Σ (1 / (k + rank_i)),其中 k 通常是 60,rank_i 是文档在第 i 个查询结果中的排名。

RRF 的特点是:不依赖具体分数,只看排名。排名越靠前权重越高。

为什么用 RRF 而不是直接合并分数?因为不同查询的分数不可比——查询 1 的 0.8 分可能等于查询 2 的 0.6 分,但排名是统一的。"

模板 5:比较三种技术

"这三种都是 RAG 检索优化技术,但方法和适用场景不同。

Query Rewriting 是改写问题,成本最低(1.8x),适合简短模糊查询。

HyDE 是生成假设答案,成本中等(1.9x),适合问答型问题,能桥接语义鸿沟。

Multi-Query 是多角度查询,成本最高(3.2x),但召回率提升最大(+38%),适合歧义查询。

实际项目里,我会根据延迟和预算权衡:延迟敏感用 Query Rewriting,质量优先可以三者组合。"

模板 6:介绍评估指标

"检索阶段的评估主要看两个指标:MRR 和 NDCG。

MRR 只关心第一个正确答案排第几名,适合问答系统,因为用户通常只需要一个正确答案。

NDCG 同时考虑相关性和排序质量,它假设相关性有等级(0-3 分),排名越靠后价值越低。NDCG 适合搜索场景,因为用户会浏览前几条结果。

在 RAG 里,如果更关注答案质量用 MRR,如果更关注检索文档的整体相关性用 NDCG。"


C.5 常见问题速查

Q: 怎么评估查询重写的效果?

“看检索阶段的指标,比如 MRR、NDCG,或者看最终答案质量(人工评估或 LLM-as-a-judge)。”

Q: 重写会不会引入噪声?

“会,所以要控制重写幅度。可以用少样本提示让 LLM 保持原意,或者设置置信度阈值,低置信度时用原始查询。”

Q: HyDE 的假设答案如果错了怎么办?

“这是 HyDE 反直觉的地方——假设答案不需要正确。因为我们要的不是答案内容,而是它的向量表示。只要语言风格像答案、关键词覆盖到位、语义方向正确,就能找到正确的文档。”

Q: Multi-Query 的成本会不会很高?

“会增加 LLM 调用和检索次数,但:LLM 调用只有一次(生成变体),检索可以并行执行,延迟不增加太多。对于复杂查询和开放型问题,效果提升值得这点成本。可以用条件触发:只对简短查询或低置信度查询用 Multi-Query。”

Q: 结果融合怎么做?

“常用方法是 RRF(Reciprocal Rank Fusion)。公式是 RRF 分数 = Σ (1 / (k + rank_i)),k 通常是 60。它不依赖具体分数,只看排名。最后按 RRF 分数重新排序。”


C.6 一张图总结

┌─────────────────────────────────────────────────────────────────┐
│                    RAG 查询优化技术 面试速查                     │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  三种技术:                                                     │
│  Query Rewriting → 翻译官 → 1.8x → 简短模糊查询                │
│  HyDE → 美食家 → 1.9x → 问答型问题                             │
│  Multi-Query → 分身术 → 3.2x → 歧义查询                        │
│                                                                 │
│  五个策略:                                                     │
│  速度优先 (<300ms) → 实时对话                                   │
│  平衡型 (600-1000ms) → 通用生产                                 │
│  召回率优先 (2000-4000ms) → 专业研究                           │
│  成本优先 (<200ms) → 内部工具                                   │
│  质量优先 (3000-5000ms) → 高端客服                             │
│                                                                 │
│  两个指标:                                                     │
│  MRR → 只关心第一个正确答案 → 问答系统                         │
│  NDCG → 考虑相关性 + 排序 → 搜索、推荐                         │
│                                                                 │
│  一个融合:                                                     │
│  RRF → 倒数排名融合 → k=60 → 只看排名不看分数                  │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

C.7 面试准备清单

必须掌握的概念

  • Query Rewriting 的定义和三种方法
  • HyDE 的核心思想和为什么有效
  • Multi-Query 的工作流程和优势
  • RRF 的公式和 k=60 的原因
  • MRR 和 NDCG 的区别

必须会说的比喻

  • Query Rewriting = 翻译官
  • HyDE = 美食家(先描述答案再找)
  • Multi-Query = 分身术

必须记住的数字

  • Query Rewriting 成本:1.8x
  • HyDE 成本:1.9x
  • Multi-Query 成本:3.2x(或检索阶段 6-8x)
  • RRF 的 k=60
  • Recall 提升:+22% / +29% / +38%

必须能写的代码

  • RRF 融合函数
  • MRR 计算函数
  • NDCG 计算函数

================================================================================

附录 D 参考资料

D.1 论文与文章

Query Rewriting

  • Query Reformulation for Retrieval-Augmented Generation (2023)

    • 探讨 LLM 在查询重写中的应用
    • 链接:https://arxiv.org/abs/2305.03653
  • Query Expansion Techniques for Information Retrieval (Survey)

    • 查询扩展技术综述
    • 涵盖传统方法和 LLM 方法

HyDE

  • HyDE: Hypothetical Document Embeddings (Gao et al., 2022)

    • 原始论文,提出 HyDE 概念
    • 链接:https://arxiv.org/abs/2212.10496
    • 代码:https://github.com/texttron/hyde
  • Precise Zero-Shot Dense Retrieval without Relevance Labels (2022)

    • HyDE 的理论基础

Multi-Query

  • Multi-Query: From One to Many (arXiv:2305.03653)

    • 多查询检索的开创性工作
    • 链接:https://arxiv.org/abs/2305.03653
  • LangChain Multi-Query Retriever

    • LangChain 官方实现
    • 链接:https://python.langchain.com/docs/modules/data_connection/retrievers/MultiQueryRetriever

RRF

  • The Probabilistic Relevance Framework: BM25 and Beyond (Robertson & Zaragoza, 2009)

    • 介绍 RRF 和相关检索框架
  • Score Distribution Analysis for Reciprocal Rank Fusion (2021)

    • RRF 的理论分析

评估指标

  • Information Retrieval Evaluation Metrics (TREC)

    • 信息检索评估指标标准
    • 链接:https://trec.nist.gov/
  • MRR and NDCG Explained (Towards Data Science)

    • 通俗易懂的解释
    • 链接:https://towardsdatascience.com/

RAG 优化

  • Retrieval-Augmented Generation for Large Language Models: A Survey (2023)

    • RAG 技术综述
    • 链接:https://arxiv.org/abs/2312.10997
  • Advanced RAG: Techniques and Best Practices (2023)

    • 高级 RAG 技术实践

D.2 开源项目

Embedding 模型

模型 链接 说明
all-MiniLM-L6-v2 https://huggingface.co/sentence-transformers/all-MiniLM-L6-v2 轻量级,384 维
bge-large-zh-v1.5 https://huggingface.co/BAAI/bge-large-zh-v1.5 中文效果好
bge-base-zh-v1.5 https://huggingface.co/BAAI/bge-base-zh-v1.5 平衡性能和速度

Rerank 模型

模型 链接 说明
bge-reranker-large https://github.com/FlagOpen/FlagEmbedding 开源,中文支持好
bge-reranker-base https://github.com/FlagOpen/FlagEmbedding 轻量级
Cohere Rerank https://docs.cohere.com/docs/rerank 商业 API,效果好

向量数据库

数据库 链接 说明
ChromaDB https://docs.trychroma.com/ 轻量,易用
FAISS https://github.com/facebookresearch/faiss Facebook 开源,高性能
Pinecone https://www.pinecone.io/ 云服务,托管
Weaviate https://weaviate.io/ 开源,支持混合检索

RAG 框架

框架 链接 说明
LangChain https://python.langchain.com/ 最流行的 RAG 框架
LlamaIndex https://docs.llamaindex.ai/ 专注数据索引
Haystack https://haystack.deepset.ai/ 端到端 RAG pipeline

D.3 API 文档

LLM API

服务 文档链接 说明
OpenRouter https://openrouter.ai/docs 聚合多个 LLM 提供商
OpenAI API https://platform.openai.com/docs GPT 系列模型
Anthropic API https://docs.anthropic.com/ Claude 系列模型

Embedding API

服务 文档链接 说明
OpenAI Embeddings https://platform.openai.com/docs/guides/embeddings text-embedding-ada-002
Cohere Embeddings https://docs.cohere.com/docs/embeddings 多语言支持

Rerank API

服务 文档链接 说明
Cohere Rerank https://docs.cohere.com/docs/rerank 商业级 Rerank
Jina Rerank https://jina.ai/reranker 免费额度

D.4 学习资源

教程与课程

  • RAG Course (DeepLearning.AI)

    • 链接:https://www.deeplearning.ai/
    • RAG 专题课程
  • LangChain for LLM Applications (DeepLearning.AI)

    • 链接:https://www.deeplearning.ai/
    • LangChain 实战课程

博客与文章

  • Lil’Log Blog (OpenAI)

    • 链接:https://lilianweng.github.io/
    • 深度学习博客,有 RAG 相关文章
  • Towards Data Science - RAG

    • 链接:https://towardsdatascience.com/tagged/rag
    • RAG 技术文章集合

视频教程

  • RAG Explained (YouTube)
    • 链接:https://www.youtube.com/
    • RAG 概念讲解视频

D.5 工具与库

Python 库

安装命令 说明
sentence-transformers pip install sentence-transformers Embedding 模型
chromadb pip install chromadb 向量数据库
faiss-cpu pip install faiss-cpu Facebook 向量搜索
rank-bm25 pip install rank-bm25 BM25 检索
langchain pip install langchain RAG 框架

评估工具

工具 链接 说明
Ragas https://github.com/explodinggradients/ragas RAG 评估框架
TruLens https://github.com/truera/trulens LLM 应用评估
Arize Phoenix https://github.com/Arize-ai/phoenix 可观测性工具

D.6 推荐阅读顺序

入门级

  1. RAG 技术综述文章
  2. Query Rewriting 基础教程
  3. LangChain MultiQueryRetriever 文档

进阶级

  1. HyDE 原始论文
  2. Multi-Query 论文
  3. RRF 技术分析文章

高级级

  1. RAG 最新研究论文
  2. 评估指标深入研究
  3. 开源项目源码分析

D.7 快速链接

主题 核心链接
Query Rewriting https://arxiv.org/abs/2305.03653
HyDE https://arxiv.org/abs/2212.10496
Multi-Query https://python.langchain.com/docs/modules/data_connection/retrievers/MultiQueryRetriever
RRF https://en.wikipedia.org/wiki/Reciprocal_rank_fusion
BGE Reranker https://github.com/FlagOpen/FlagEmbedding
Cohere Rerank https://docs.cohere.com/docs/rerank
Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐