第 9 篇:RAG 全链路增强技术——五维增强体系,把基础 RAG 优化到生产级

本文为「从零到落地:RAG 检索增强生成实战系列」第 9 篇,完整系列持续更新中。


前言

本篇聚焦 RAG 全链路增强技术,按五大维度系统讲解 13 种核心增强策略的原理、流程和落地思路,帮你把 RAG 从”能跑”优化到”好用”。

在前面 8 篇里,你已经用原生 Python、Dify、LangGraph、LlamaIndex、RAGFlow 五种方案分别跑通了基础 RAG。但”能跑”和”好用”之间还有一段距离——检索不准、回答幻觉、上下文过长、复杂问题答不上来……这些都是基础 RAG 的典型短板。

标准 RAG 流程是:文档分块 → 向量存储 → 相似度检索 → LLM 生成答案。这条流水线虽然简洁,但在真实业务场景中几乎必然遇到以下问题:

  • 用户问得模糊,检索结果答非所问
  • 专有名词、代码片段用向量检索根本匹配不到
  • 分块太细丢上下文,分块太粗检索不精准
  • LLM 拿到一堆冗余信息,反而”迷失在中间”
  • 知识库没有答案时系统照样瞎编

本篇从五个维度全面解析 RAG 增强技术,帮你系统性地解决上述问题。

本篇学完你将掌握

  • 五种 RAG 增强维度的核心目标和适用场景
  • 13 种增强策略的原理、流程和优缺点
  • 每种策略的代码落地思路(基于 LangGraph / LlamaIndex)
  • 不同业务场景下的增强策略组合方案
  • 从基础 RAG 到生产级 RAG 的完整优化路径

一、五维增强体系全景

RAG 增强不是某一个环节的优化,而是全链路的系统性工程。我们从五个维度全面分析:

维度 作用阶段 核心目标 策略数量
查询增强 输入阶段 优化查询表达,解决语义不匹配 4 种
索引增强 索引构建阶段 提升索引质量,优化分块与检索结构 3 种
检索器增强 检索阶段 精准获取内容,扩展上下文 3 种
生成器增强 生成阶段 优化提示效果,提升生成质量 2 种
管道增强 全流程 动态优化流程,引入智能决策 2 种

下面用一张流程图展示五大维度在 RAG 全链路中的位置:

1
2
3
4
5
6
7
用户查询 → [查询增强] → 优化后的查询

文档库 → [索引增强] → 优化后的索引 → [检索器增强] → 精准上下文

[生成器增强] → 优化后的提示 → LLM → 答案

[管道增强:自我反思 + 查询路由]

💡 建议:不需要一次性全部用上。根据实际业务痛点,选择 1-2 个维度的策略优先落地,效果达标后再逐步增强。


二、查询增强——让用户的”模糊提问”变成”精准检索”

用户的原始提问往往是模糊、简短或缺乏语义背景的。查询增强的核心目标是:在查询进入向量库之前对其进行改造,提升检索的精准度。

2.1 假设问题法(Hypothetical Questions)

核心思想:这是一种”以文档为中心”的策略。利用 LLM 预先为每个文档块生成可能的”用户提问”,在检索时将用户的 Query 与这些”假设问题”进行匹配,而非直接匹配文档内容。

解决的痛点

痛点 问题描述 解决方式
跨域不对称 用户问的是”问题”,文档写的是”陈述句答案”,语义模式不同 Query-to-Query 匹配,同为疑问句,语义更接近
长度不匹配 用户查询通常很短,文档块可能很长 假设问题长度与用户查询相近
意图模糊 用户可能不知道如何精确表达 预生成多种问法覆盖不同表达方式

核心流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
【阶段一:索引构建(离线预处理)】

文档块 A "本产品使用 5V 电池"
→ LLM 生成假设问题:
问题 1:"这个产品用多少伏的电池?"
问题 2:"电池规格是什么?"
问题 3:"需要什么类型的电池?"
→ 向量化存入向量库,建立 问题→文档块 映射

【阶段二:在线检索】

用户查询 "怎么换电池?"
→ 匹配最相似的假设问题(Query-to-Query)
→ 通过映射关系定位原始文档块
→ 原始文档块 + 用户问题 → LLM → 最终答案

优点:Query-to-Query 匹配更精准,可覆盖多种问法,提升召回率。
代价:索引构建时需要大量 LLM 调用,成本高;索引体积增大(每个文档块对应多个问题向量)。

落地思路:适合文档库相对固定、可以预处理的场景。在 LlamaIndex 中可通过自定义 Node 处理器为每个 Node 附加假设问题。

2.2 假设文档嵌入(HyDE)

核心思想:这是一种”以查询为中心”的策略。利用 LLM 根据用户 Query 先生成一个”虚构的答案”(假设文档),然后用这个假设文档的向量去检索真实的文档块。

💡 关键洞察:假答案的内容准确性不重要,重要的是它的语义向量与真实答案在向量空间中的位置接近。

解决的痛点

痛点 问题描述 解决方式
查询-文档不对称 短查询 vs 长文档,语义密度差异 生成与文档形式相似的假答案进行匹配
索引不可变 已有索引无法修改,无法使用假设问题法 仅在查询端处理,不改变索引结构
零样本场景 用户问题可能涉及知识库中没有直接答案的内容 假答案提供语义”锚点”,帮助找到相关内容

核心流程

1
2
3
4
5
6
7
8
9
用户查询:"公司年假有几天?"

LLM 生成假答案(无上下文,可能有幻觉):
"根据公司年假制度,入职满1年可享受5天年假,
满3年可享受7天年假,满5年可享受10天年假..."

假答案向量化 → 在向量库中检索最相似的真实文档块

真实文档块 + 用户原始查询 → LLM → 最终准确答案

优点:不需要修改索引结构,即插即用;将”问题语义空间”转换到”答案语义空间”。
代价:每次查询都需要额外的 LLM 调用,增加延迟;如果 LLM 对领域完全陌生,假答案可能偏离真实答案语义空间。

落地思路:适合已有索引无法修改的场景。在 LangGraph 中可以在检索节点前加一个 HyDE 生成节点。

2.3 子查询分解(Sub-queries)

核心思想:当用户提出复杂问题(如对比、多条件、多实体)时,利用 LLM 将复杂问题分解为多个简单的子问题,分别检索后汇总上下文生成综合答案。

解决的痛点

痛点 问题描述 解决方式
多实体问题 “A 和 B 有什么区别?”需要同时了解 A 和 B 分别检索 A 和 B 的信息
多跳推理 答案需要结合多个知识点 分解为多个单跳问题逐一检索
向量稀释 复杂查询向量化后,各部分语义被稀释 每个子查询聚焦单一语义

核心流程

1
2
3
4
5
6
7
8
9
10
11
原始查询:"LangGraph 和 LlamaIndex 在功能上有什么不同?"

LLM 分解:
子查询 1:"LangGraph 的核心功能是什么?"
子查询 2:"LlamaIndex 的核心功能是什么?"

并行检索:每个子查询独立检索 Top-K 文档块

结果去重 + 合并上下文

LLM 综合回答

优点:有效处理对比类、多条件类复杂问题;支持多跳推理。
代价:检索次数成倍增加,延迟上升;上下文合并可能超出 LLM 窗口限制。

落地思路:在 LangGraph 中可以实现为”分解节点 → 并行检索节点 → 合并节点”的图结构(第 6 篇已演示过条件路由,这里是其典型应用场景)。

2.4 回溯提示(Step-back Prompting)

核心思想:”以退为进”。遇到过于具体的细节问题时,先通过 LLM 生成一个更高层级、更抽象的”背景问题”,先搞懂大道理,再解小问题。

解决的痛点

痛点 问题描述 解决方式
过度具体 “100 亿条数据能存吗?”知识库可能没有这个具体数字 抽象为”数据量限制是多少?”
缺乏背景 直接回答细节问题容易产生幻觉 先检索原理/定义,再推导具体答案
知识推理 需要基于基础知识进行推理计算 获取基础公式/原理后进行推导

核心流程

1
2
3
4
5
6
7
8
9
原始查询:"我有 100 亿条记录,想存到 Milvus 中查询,可以吗?"

LLM 抽象化:
回溯问题:"Milvus 可以处理的数据集大小限制是多少?"

检索背景知识 → 回溯答案:"Milvus 单集群支持百亿级向量"

原始问题 + 回溯答案 → LLM 综合推理 → 最终答案
"可以。Milvus 支持百亿级向量,100 亿条记录在支持范围内..."

优点:减少幻觉,强迫模型先找到可靠的理论依据再回答;特别适合科学、数学、逻辑推理场景。
代价:多了两轮 LLM 调用(回溯问题 + 回溯答案),流程最长,延迟最高;对于直接能检索到答案的问题反而是画蛇添足。

2.5 四种查询增强策略对比

策略 核心机制 作用阶段 适用场景 主要代价
假设问题法 预生成问题库 索引构建 文档固定、可预处理 索引成本高
HyDE 生成假答案检索 查询时 已有索引、无法修改 查询延迟
子查询分解 分解→并行检索→合并 查询时 复杂多实体问题 检索次数多
回溯提示 抽象→检索原理→推理 查询时 推理类、细节问题 多轮 LLM 调用

💡 选型建议:索引可修改且文档固定 → 假设问题法;已有索引无法改动 → HyDE;复杂对比类问题 → 子查询分解;推理类/过于具体的问题 → 回溯提示。这四种策略并不互斥,可根据场景组合使用。


三、索引增强——从源头提升检索质量

索引增强在文档进入向量库之前,通过优化索引结构和分块策略,解决”分块粒度”与”上下文完整性”之间的矛盾。

3.1 自动合并文档块(Auto-merging Chunks)

核心思想:建立父子两级分块结构。用细粒度的”子块”进行精准检索匹配,但在返回结果时,如果多个子块来自同一个”父块”,则自动合并返回整个父块,保证上下文完整性。

解决的痛点

痛点 问题描述 解决方式
粒度矛盾 小块检索精准但上下文不足,大块上下文完整但检索模糊 用小块检索,返回大块
信息割裂 相关内容被切分到不同块,LLM 无法理解完整逻辑 自动合并相邻相关块
冗余检索 检索到多个高度相关的小块,实际来自同一段落 合并去重,减少冗余

核心流程

1
2
3
4
5
6
7
8
【索引构建】
父块 A(512 tokens)→ 子块 A1/A2/A3/A4(各 128 tokens)
仅对子块向量化存入向量库,记录 子块→父块 映射

【检索与合并】
用户查询 → 向量检索 → 命中子块 A1、A2、A3
统计:父块 A 被命中 3 个子块 ≥ 阈值(2)
→ 返回整个父块 A 作为上下文

优点:兼顾检索精准度与上下文完整性;自动适应不同查询的信息密度需求。
代价:索引结构更复杂,需要维护父子映射关系;合并阈值需要根据业务调优。

落地思路:LlamaIndex 内置了 AutoMergingRetriever,可以直接使用。在第 7 篇的进阶配置中已经提到过父子分块策略,这里就是它的完整实现。

3.2 分层索引(Hierarchical Index)

核心思想:建立两级索引结构——第一层是文档/章节的摘要索引,第二层是详细的文档块索引。检索时先通过摘要层快速定位相关文档,再在目标文档内精确检索具体块。类似于”先查目录,再翻具体页”。

解决的痛点

痛点 问题描述 解决方式
海量数据 文档库太大,全量检索效率低 快速过滤,缩小检索范围
跨文档干扰 不同文档的相似片段互相干扰 先定位文档,再文档内检索
层级信息丢失 扁平化分块丢失文档结构信息 保留文档-章节-段落层级关系

核心流程

1
2
3
4
5
6
7
8
【索引构建】
第一层:为每个文档生成摘要 → 摘要向量化 → 摘要索引库
第二层:每个文档内部细粒度分块 → 块向量化 → 块索引库

【两级检索】
用户查询:"如何调用认证 API?"
→ 第一层:摘要匹配 → 命中"API 技术文档"
→ 第二层:仅在"API 技术文档"的块中检索 → 返回具体块

优点:大幅提升海量数据场景下的检索效率;保留文档层级结构信息。
代价:需要额外生成和维护摘要索引;摘要质量直接影响第一层过滤效果;如果第一层漏掉相关文档,后续无法弥补。

落地思路:适合多文档源、企业知识库场景。在 LangGraph 中可以实现为”摘要检索节点 → 文档内检索节点”的两级图结构。

3.3 混合检索 + 重排序(Hybrid Retrieval & Reranking)

核心思想:单一的向量检索(稠密检索)可能遗漏关键词精确匹配的结果。混合检索同时使用多种检索方法(向量检索 + BM25 + SPLADE),然后通过 Reranker 对多路结果进行重新排序,取长补短。

解决的痛点

痛点 问题描述 解决方式
关键词盲区 向量检索对专有名词、代码、ID 等不敏感 BM25 精确匹配关键词
语义盲区 BM25 无法理解同义词、上下文语义 向量检索捕捉语义相似
排序不准 各路召回的分数不可比 Reranker 统一评估相关性

核心流程

1
2
3
4
5
6
7
8
9
10
11
用户查询 → 三路并行召回:
① 向量检索(Dense)→ Top-K₁(语义相似)
② BM25(Sparse)→ Top-K₂(关键词匹配)
③ SPLADE(学习稀疏向量)→ Top-K₃(语义+关键词兼顾)

结果融合:去重 + RRF 融合排序
RRF 公式:score = Σ 1/(k + rank_i),k 通常取 60

Reranker 精排:Cross-Encoder 对 (Query, Document) 对深度评分

最终 Top-N 排序结果 → 送入 LLM

优点:综合多种检索方法优势,显著提升召回率;Reranker 提供更精准的相关性排序。
代价:多路检索增加系统复杂度;Reranker 计算成本高。

💡 提示:混合检索 + Rerank 是当前生产环境的标配方案。在第 5 篇 Dify 和第 7 篇 LlamaIndex 中都已经演示过混合检索的配置,这里讲清其背后的原理。

3.4 三种索引增强策略对比

策略 核心机制 作用阶段 适用场景 主要代价
自动合并 父子分块 + 动态合并 索引构建 + 检索 需要完整上下文 索引复杂度
分层索引 摘要层 + 块层两级检索 索引构建 + 检索 海量文档、多文档源 摘要生成成本
混合检索 多路召回 + Reranker 检索阶段 通用场景、提升召回 计算成本高

💡 建议:这三种策略可以组合使用。推荐的最小可行方案:混合检索 + Rerank(几乎适用所有场景)。如果文档层级明显,再加分层索引;如果上下文完整性是痛点,再加自动合并。


四、检索器增强——精准获取与上下文扩展

检索器增强在检索执行阶段,通过优化检索策略和结果处理方式,解决”精确匹配”与”完整上下文”之间的矛盾。

4.1 句子窗口检索(Sentence Window Retrieval)

核心思想:用细粒度小块(如单个句子)做向量检索匹配,命中后自动扩展上下文窗口,返回包含前后文的大块给 LLM。实现”小块精准匹配,大块完整上下文”的双赢效果。

解决的痛点

痛点 问题描述 解决方式
粒度悖论 小块检索精准但上下文不足,大块上下文完整但检索模糊 检索用小块,返回用大块
信息断裂 单独的句子缺乏背景,LLM 难以理解完整含义 自动扩展上下文窗口
语义不完整 关键信息可能分布在相邻句子中 前后句一并返回,保证完整性

核心流程

1
2
3
4
5
6
7
8
9
10
原始文档:[句子1][句子2][句子3][句子4][句子5][句子6]

【索引阶段】按句子单独向量化

【检索阶段】
用户查询 → 命中 [句子3](精准匹配)

【扩展阶段】以句子 3 为中心,向前扩展 2 句,向后扩展 2 句

返回给 LLM:[句子1][句子2][句子3][句子4][句子5]

窗口大小建议:技术文档建议 ±3 句,对话记录建议 ±5 句。

优点:检索精准度高(句子级小块匹配);上下文完整性好(窗口扩展返回);实现简单,无需复杂的父子索引结构。
代价:窗口大小需要调优,过大引入噪声,过小上下文不足。

落地思路:LlamaIndex 内置了 SentenceWindowNodeParserMetadataReplacementPostProcessor,可以直接实现句子窗口检索。

4.2 元数据过滤(Metadata Filtering)

核心思想:利用文档的结构化元数据(如时间、类别、来源、标签等)在向量检索之前进行预过滤,大幅缩小候选检索范围。先”结构化筛选”,再”语义匹配”。

解决的痛点

痛点 问题描述 解决方式
时间敏感 用户只关心特定时间段的内容 按时间范围预过滤
类别混淆 不同领域文档语义相近但主题不同 按类别/来源过滤
效率低下 海量文档场景下全量向量检索耗时长 先过滤后检索,提升效率

核心流程

1
2
3
4
5
6
7
8
9
用户查询:"2024 年 Q3 的财务报告中,营收情况如何?"

意图解析:
元数据条件:year=2024, quarter=Q3, doc_type=财务报告
语义查询:"营收增长情况如何?"

元数据过滤 → 缩小到符合条件的文档子集

在子集内进行向量语义检索 → 精准结果

代码示例(Chroma 过滤表达式):

1
2
3
4
5
6
7
8
9
10
11
12
# 元数据过滤 + 向量检索组合查询
results = collection.query(
query_embeddings=[query_vector],
where={
"$and": [
{"year": {"$eq": 2024}},
{"quarter": {"$eq": "Q3"}},
{"doc_type": {"$eq": "财务报告"}}
]
},
n_results=10
)

优点:大幅缩小检索范围,显著提升检索效率;精确满足用户的结构化查询需求。
代价:需要设计和维护完善的元数据体系;意图解析可能不准确,导致过度过滤或过滤不足。

4.3 组合检索策略(Combined Retrieval Strategy)

核心思想:将元数据过滤、向量语义检索、句子窗口扩展三种技术有机组合,形成”过滤→匹配→扩展”的完整检索流水线。

核心流程

1
2
3
4
5
6
7
用户查询:"2024 年 Q3 财务报告中,营收增长情况如何?"

① 意图解析 → 提取元数据条件 + 语义查询
② 元数据过滤 → 缩小检索范围
③ 向量检索 → 句子级精准匹配 Top-K
④ 窗口扩展 → 每个命中句子扩展前后 N 句
⑤ 结果合并 → 去重后构建完整上下文

优点:全面覆盖,同时处理结构化条件和语义需求;效率最优,元数据过滤大幅减少向量检索的候选集。
代价:流程复杂度增加,各环节参数需要联合调优。

4.4 三种检索器增强策略对比

策略 核心机制 作用阶段 适用场景 主要代价
句子窗口检索 小块匹配 + 窗口扩展 检索 + 后处理 需要完整上下文 窗口调优
元数据过滤 结构化预过滤 检索前 有明确筛选条件 元数据维护
组合检索 过滤→匹配→扩展 全流程 复杂查询场景 流程复杂度

五、生成器增强——让 LLM 看到最该看的内容

生成器增强在检索结果传递给 LLM 之前,通过优化提示内容和结构,解决”上下文过长”与”关键信息利用率低”之间的矛盾。

5.1 提示压缩(Prompt Compression)

核心思想:检索返回的多个文档块中往往包含噪声和低相关内容。通过相关性评分和内容筛选,去除冗余信息,只保留与用户查询高度相关的核心内容。

解决的痛点

痛点 问题描述 解决方式
噪声干扰 检索结果包含不相关内容,干扰 LLM 理解 Reranker 评分过滤低相关块
Token 浪费 大量冗余内容消耗宝贵的上下文窗口 压缩保留精华,减少 Token 用量
信息稀释 关键信息被淹没在大量文本中 筛选后关键信息密度更高

核心流程

1
2
3
4
5
6
7
8
检索结果(5 个文档块)→ Reranker 逐一评分
块 1:0.92(高相关)→ 保留
块 2:0.35(噪声)→ 过滤
块 3:0.87(高相关)→ 保留
块 4:0.41(低相关)→ 过滤
块 5:0.68(中等相关)→ 可选保留

压缩后结果(2-3 个精简块)→ 送入 LLM → 高质量答案

代码示例(基于 bge-reranker-v2-m3):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from FlagEmbedding import FlagReranker

# 初始化 Reranker 模型
reranker = FlagReranker('BAAI/bge-reranker-v2-m3', use_fp16=True)

def compress_prompt(query: str, chunks: list[str], top_n: int = 3, threshold: float = 0.5):
"""对检索结果进行 Reranker 评分 + 压缩过滤"""
# 计算每对 (query, chunk) 的相关性分数
pairs = [[query, chunk] for chunk in chunks]
scores = reranker.compute_score(pairs, normalize=True)

# 按分数降序排列,过滤低于阈值的块
scored_chunks = [(chunk, score) for chunk, score in zip(chunks, scores)]
scored_chunks.sort(key=lambda x: x[1], reverse=True)

# 保留 Top-N 且高于阈值的块
filtered = [chunk for chunk, score in scored_chunks[:top_n] if score >= threshold]
return filtered

优点:显著减少 Token 消耗,降低 API 成本;去除噪声,提升 LLM 生成答案的准确性。
代价:Reranker 增加计算成本和延迟;阈值设置不当可能误删重要内容。

5.2 块顺序调整(Chunk Ordering / Lost in the Middle)

核心思想:研究发现 LLM 存在”迷失在中间”(Lost in the Middle)现象——对于长上下文,LLM 更关注开头和结尾的内容,而容易忽略中间部分。块顺序调整策略利用这一特性,将高相关内容放在首尾位置,低相关内容放在中间。

解决的痛点

痛点 问题描述 解决方式
中间遗忘 LLM 对长文本中间部分的注意力下降 重要内容放首尾,利用注意力分布
顺序随机 检索结果按相似度排序,未考虑 LLM 特性 根据 LLM 注意力特点重排
关键信息利用率低 最相关的内容可能被放在中间被忽略 策略性布局,确保关键信息被关注

核心流程

1
2
3
4
5
6
7
8
9
原始顺序(按相似度降序):
[块A 0.95] [块B 0.88] [块C 0.82] [块D 0.75] [块E 0.70]

Lost in the Middle 重排策略:
高分在首尾,低分在中间
→ [块A 0.95] [块C 0.82] [块E 0.70] [块D 0.75] [块B 0.88]
↑ 开头(高度关注) ↑ 结尾(高度关注)

推荐流程:先压缩 → 再重排 → 送入 LLM

代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
def reorder_chunks(chunks: list[str], scores: list[float]) -> list[str]:
"""Lost in the Middle 重排:高分在首尾,低分在中间"""
# 按分数降序排列
paired = sorted(zip(chunks, scores), key=lambda x: x[1], reverse=True)

result = [None] * len(paired)
left, right = 0, len(paired) - 1

for i, (chunk, _) in enumerate(paired):
if i % 2 == 0:
# 偶数排名放开头
result[left] = chunk
left += 1
else:
# 奇数排名放结尾
result[right] = chunk
right -= 1

return [r for r in result if r is not None]

优点:无需额外计算,仅重排即可提升效果;实现简单,可与其他策略无缝组合。
代价:对短上下文效果不明显;不同 LLM 的注意力分布可能略有差异。

5.3 两种生成器增强策略对比

策略 核心机制 作用阶段 适用场景 主要代价
提示压缩 Reranker 筛选 + 过滤 检索后处理 检索结果噪声多、Token 预算紧张 计算成本
块顺序调整 Lost in the Middle 重排 提示构建 长上下文、多文档块场景 需要可靠分数

💡 建议:推荐流程是”先压缩 → 再重排 → 送入 LLM”。两者几乎零成本叠加,是性价比最高的增强组合。


六、管道增强——让 RAG 流程具备智能决策能力

管道增强在 RAG 全流程层面引入智能决策和自我修正机制,解决”一刀切流程”与”多样化查询需求”之间的矛盾。

6.1 自我反思(Self-Reflection / Corrective RAG)

核心思想:在检索完成后、生成答案前,引入反思验证环节,评估检索结果是否真正能够回答用户问题。如果检索结果不充分或不相关,则触发修正机制。这是一种”先验证,后生成”的质量保障策略。

解决的痛点

痛点 问题描述 解决方式
盲目信任检索 检索结果可能不相关,但系统仍基于其生成答案 反思验证,评估检索质量
知识库盲区 知识库中没有相关内容,但系统仍尝试回答 识别盲区,触发外部搜索或拒绝回答
一次检索不足 复杂问题可能需要多轮检索 自动触发补充检索

核心流程

1
2
3
4
5
6
7
8
9
10
用户查询 → 初次检索 → 获取候选文档块

反思验证(LLM 或 NLI 模型评估):
"检索结果能回答用户问题吗?"

完全相关(高分)→ 直接生成答案
部分相关(中分)→ 补充检索 / 扩展查询
不相关(低分)→ 外部搜索 / 拒绝回答

基于修正后的上下文 → 生成最终答案

反思 Prompt 示例

1
2
3
4
5
6
7
8
9
REFLECTION_PROMPT = """给定用户问题和检索到的文档,请判断:
1. 文档是否包含回答问题所需的信息?
2. 评分:完全相关(2) / 部分相关(1) / 不相关(0)
3. 如果部分相关,缺少什么信息?

用户问题:{question}
检索文档:{context}

请输出 JSON 格式:{{"score": 0/1/2, "reason": "...", "missing_info": "..."}}"""

优点:显著提升答案质量和可靠性;自动处理知识库盲区,避免胡编乱造。
代价:增加 LLM 调用次数,延迟上升;反思判断本身可能出错(假阳性/假阴性)。

落地思路:在第 6 篇 LangGraph 中已经演示过”质量评估节点 + 条件路由 + Query 改写”的自我修正循环,那就是 Corrective RAG 的标准实现。相关项目:Self-RAG、Corrective RAG (CRAG)。

6.2 查询路由(Query Routing with Agent)

核心思想:并非所有查询都需要完整的 RAG 流程。引入智能路由 Agent,在处理用户查询之前先分析查询特征,然后动态选择最优处理路径。

解决的痛点

痛点 问题描述 解决方式
过度处理 简单问题(如”1+1=?”)也走完整 RAG 流程 路由识别,简单问题直接回答
处理不足 需要实时信息的问题仅在静态知识库中检索 路由识别,调用外部搜索
一刀切流程 所有查询走相同流程,无法针对性优化 根据查询特征选择最优路径

核心流程

1
2
3
4
5
6
7
8
9
10
用户查询 → 路由 Agent 分析查询特征

路由决策:
if 简单常识问题 → 直接回答(跳过检索)
elif 知识库专业问题 → RAG 检索
elif 需要最新信息 → 外部搜索(Web Search)
elif 复杂多跳问题 → 子查询分解 + RAG
else → RAG 检索(默认)

执行选定路径 → 生成答案

路由代码示例(LangGraph 风格):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
from langgraph.graph import StateGraph, START, END

def route_query(state: dict) -> dict:
"""路由节点:分析查询特征,决定处理路径"""
question = state["question"]

# 用 LLM 判断查询类型
route_prompt = f"""分析以下问题属于哪种类型:
1. simple - 简单常识问题,无需检索
2. rag - 需要知识库检索的专业问题
3. web - 需要实时信息的问题

问题:{question}
只输出类型名称(simple/rag/web):"""

route_type = llm.invoke(route_prompt).strip().lower()
state["route"] = route_type
return state

def decide_route(state: dict) -> str:
"""条件路由:根据查询类型选择处理路径"""
route = state.get("route", "rag")
if route == "simple":
return "direct_answer"
elif route == "web":
return "web_search"
else:
return "rag_retrieve"

# 构建路由图
graph = StateGraph(RAGState)
graph.add_node("route", route_query)
graph.add_node("direct_answer", direct_answer_node)
graph.add_node("rag_retrieve", retrieve_node)
graph.add_node("web_search", web_search_node)
graph.add_node("generate", generate_node)

graph.add_edge(START, "route")
graph.add_conditional_edges("route", decide_route)
graph.add_edge("direct_answer", END)
graph.add_edge("rag_retrieve", "generate")
graph.add_edge("web_search", "generate")
graph.add_edge("generate", END)

优点:资源利用更高效,简单问题快速响应;灵活应对多种类型查询。
代价:路由判断本身消耗资源;路由决策错误可能导致处理不当。

6.3 两种管道增强策略对比

策略 核心机制 作用阶段 适用场景 主要代价
自我反思 检索后验证 + 修正 检索后 高质量要求、知识库可能不足 多轮 LLM 调用
查询路由 智能分析 + 路径选择 检索前 查询类型多样、需要灵活处理 路由判断成本

💡 建议:推荐架构是”路由 → 检索 → 反思 → 生成”。查询路由在前端减少不必要的检索开销,自我反思在后端保障答案质量,两者组合形成完整的智能管道。


七、踩坑记录

踩坑记录 1:增强策略叠加后延迟飙升

现象
叠加了 HyDE + 混合检索 + Reranker + 自我反思后,单次查询延迟从 2s 飙升到 15s+,用户体验极差。

原因
每种增强策略都引入了额外的 LLM 调用或计算步骤,叠加后延迟呈线性甚至指数增长。

解决方案

1
2
3
4
5
6
7
8
9
# 策略 1:按需启用,不要全量叠加
# 推荐的最小可行增强方案(延迟 < 5s):
enhanced_rag = {
"查询增强": "无(或仅在复杂查询时启用子查询分解)",
"索引增强": "混合检索 + Rerank(离线构建,在线检索时零额外延迟)",
"检索器增强": "句子窗口检索(配置项,几乎零额外延迟)",
"生成器增强": "块顺序调整(零额外计算)",
"管道增强": "仅在反思评分 < 阈值时触发补充检索",
}

避坑建议

  • 优先选择零额外延迟的增强策略(混合检索、块顺序调整、句子窗口)
  • 需要额外 LLM 调用的策略(HyDE、回溯提示、自我反思)按需启用,设置开关
  • 为每个 LLM 调用设置超时时间,整体查询设置最大延迟上限

踩坑记录 2:Reranker 阈值设置不当导致信息丢失

现象
设置 Reranker 阈值为 0.7 后,部分查询只返回 1 个文档块甚至 0 个,LLM 回答质量反而下降。

原因
阈值过高导致有效上下文被误删,尤其是知识库文档表述和用户问题差异较大时,Reranker 评分普遍偏低。

解决方案

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def smart_compress(query, chunks, reranker, min_chunks=2, threshold=0.5):
"""智能压缩:保证最少返回 min_chunks 个块,避免信息完全丢失"""
pairs = [[query, chunk] for chunk in chunks]
scores = reranker.compute_score(pairs, normalize=True)

scored = sorted(zip(chunks, scores), key=lambda x: x[1], reverse=True)

# 至少保留 min_chunks 个,其余按阈值过滤
result = scored[:min_chunks]
for chunk, score in scored[min_chunks:]:
if score >= threshold:
result.append((chunk, score))

return [chunk for chunk, _ in result]

避坑建议

  • 设置 min_chunks 保底参数,确保至少返回 2-3 个文档块
  • 阈值不要设太高,建议从 0.3-0.5 开始调试
  • 打印每次过滤后的块数量和分数分布,便于排查

八、增强策略组合方案——从基础到生产的三级配置

不同阶段的 RAG 系统需要不同级别的增强配置,下面给出三级推荐方案:

8.1 基础版(适合原型验证)

维度 策略 额外延迟
索引增强 混合检索(向量 + BM25)
生成器增强 块顺序调整
管道增强 -

预期效果:比基础 RAG 召回率提升 15-25%,几乎零额外延迟。

8.2 进阶版(适合内部使用)

维度 策略 额外延迟
索引增强 混合检索 + Reranker +1-2s
检索器增强 句子窗口检索
生成器增强 提示压缩 + 块顺序调整 +1-2s
管道增强 查询路由(简单问题跳过检索) -0.5s(节省)

预期效果:召回率提升 30-50%,平均延迟 3-5s。

8.3 生产版(适合对外服务)

维度 策略 额外延迟
查询增强 子查询分解(复杂查询时启用) +2-3s
索引增强 混合检索 + Reranker + 分层索引 +1-2s
检索器增强 组合检索(元数据过滤 + 句子窗口) +0.5s
生成器增强 提示压缩 + 块顺序调整 +1-2s
管道增强 查询路由 + 自我反思 +2-3s

预期效果:召回率提升 50-70%,答案质量显著提升,平均延迟 5-8s。

💡 建议:不要一步到位上生产版。先用基础版验证业务可行性,再根据实际痛点逐步叠加增强策略。每次只加一个策略,A/B 测试效果后再决定是否保留。


总结与回顾

维度 策略 一句话总结 适用痛点
查询增强 假设问题法 预生成问题库,Query-to-Query 匹配 问答语义模式不对称
查询增强 HyDE 生成假答案,用语义锚点检索 已有索引无法修改
查询增强 子查询分解 复杂问题拆成多个简单子问题 对比类、多实体问题
查询增强 回溯提示 以退为进,先检索原理再推理 推理类、过于具体的问题
索引增强 自动合并 父子分块,小块检索大块返回 上下文不完整
索引增强 分层索引 摘要层粗筛 + 块层精检 海量文档检索效率低
索引增强 混合检索 + Rerank 多路召回 + 深度重排 关键词和语义各有盲区
检索器增强 句子窗口 句子级匹配 + 窗口扩展 粒度悖论
检索器增强 元数据过滤 结构化预筛选缩小范围 有明确筛选条件
检索器增强 组合检索 过滤→匹配→扩展全流水线 复杂综合查询
生成器增强 提示压缩 Reranker 评分过滤噪声块 Token 浪费、噪声干扰
生成器增强 块顺序调整 高分在首尾,低分在中间 LLM 中间遗忘
管道增强 自我反思 检索后验证,不达标则修正 答案质量不可控
管道增强 查询路由 智能分析查询,选择最优路径 查询类型多样

参考资料

资源 链接
HyDE 论文 Precise Zero-Shot Dense Retrieval without Relevance Labels
Lost in the Middle 论文 How Language Models Use Long Contexts
Self-RAG 论文 Learning to Retrieve, Generate, and Critique through Self-Reflection
Corrective RAG 论文 Corrective Retrieval Augmented Generation
bge-reranker-v2-m3 HuggingFace 模型页
LangGraph 官方文档 langchain-ai.github.io/langgraph
LlamaIndex 官方文档 docs.llamaindex.ai

本文为「从零到落地:RAG 检索增强生成实战系列」第 9 篇,完整系列持续更新中。