第三章:Advanced RAG 高级技术与 Paper 解读
3.1 主流 Advanced RAG 架构深度解析
这部分我们将解读几篇改变 RAG 发展走向的经典论文,理解它们是如何优化基础流程的。
- 3.1.1 T-RAG(Tree RAG):层级索引与多跳检索
- 核心思想:将文档组织成树状结构(根节点是摘要,叶子节点是细节)。
- 原理:当面对复杂问题时,先检索树的顶层(摘要),确定方向后,再逐层向下检索细节。这解决了"在大海捞针时迷失方向"的问题。
- 适用场景:超长文档(如整本小说、几百页的技术手册)的问答。
- 3.1.2 CRAG(Corrective RAG):检索结果校验与纠错
- 核心思想:**"如果不确定,就不要瞎编"**。
- 原理 :在检索后引入一个轻量级的"评估器"。
- 如果检索结果相关度高 → 送入 LLM 生成。
- 如果相关度低 → 触发"纠错机制"(如调用搜索引擎进行网络检索,或者回答"知识库中未找到相关信息")。
- 价值:大幅降低幻觉,防止模型基于错误信息强行回答。
- 3.1.3 Self-RAG:模型自我反思与自适应
- 核心思想:让 LLM 学会"自我反省"。
- 原理:训练模型输出特殊的"反思标记"。例如,模型在生成前先判断"我需要检索吗?",生成后判断"我的回答有依据吗?"。
- 特点:它不是固定的流程,而是让模型根据问题动态决定是"直接回答"还是"检索后回答"。
- 3.1.4 RAG-Fusion:多查询生成与结果融合
- 核心思想:**"兼听则明"**。
- 原理 :
- 用户提问:"如何减肥?"
- LLM 改写生成多个相关问题:"减肥食谱"、"运动减肥方法"、"减肥药副作用"。
- 对这组问题分别检索。
- 使用**倒数融合算法(Reciprocal Rank Fusion, RRF)**将所有结果合并排序。
- 价值:解决单一查询视角的局限性,召回更多维度的信息。
- 3.1.5 Rewrite-Retrieve-Read:查询改写
- 核心思想:**"垃圾进,垃圾出"**。用户的问题往往很烂(含糊不清、有错别字)。
- 原理:在检索前,用 LLM 把用户的问题改写成标准的、适合搜索引擎的查询语句。
- 场景:多轮对话中,用户说"它多少钱?",改写为"iPhone 15 多少钱?"。
- 3.1.6 GraphRAG:基于知识图谱的结构化 RAG
- 核心思想:**"不仅看词,还要看关系"**。
- 原理 :
- 构建:从文档中抽取实体(如"马斯克"、"SpaceX")和关系("创始人"),构建知识图谱。
- 检索:不仅检索向量相似的文本,还检索图谱中的关联路径。
- 价值:解决"多跳推理"问题(如"马斯克的公司的竞争对手的总部在哪里?"),这是传统向量检索的弱项。
低质量
高质量
用户提问
RAG 架构选择
RAG-Fusion
CRAG
GraphRAG
生成多个相关问题
并行检索 + 融合
检索结果评估
触发纠错/网络搜索
生成回答
实体抽取与图谱构建
图遍历检索
3.2 商业化 RAG 优化与方案实践
这部分是"避坑指南",基于真实商业项目的痛点,提供具体的解决方案。
- 3.2.1 RAG 商业化问题诊断
- 文档加载端:PDF 表格解析错乱、扫描件无法识别(需 OCR)。
- 分块端:切断了句子("第 1 条"和"具体内容"被切开了)、粒度太粗(一个块 5000 字,全是噪声)。
- 检索端:查不到(召回率低)、查一堆废话(准确率低)。
- 生成端:模型看着检索到的内容编故事(幻觉)。
- 3.2.2 索引优化实战(Pre-Retrieval)
- 分块优化 :
- 滑动窗口:像 CNN 处理图片一样处理文本,保留上下文。
- 语义分块:不按字数切,按"意思"切(如一段话讲完一个概念再切)。
- **父 - 子索引(Parent-Child Indexing)**:
- 痛点:小块检索准但上下文少,大块上下文多但检索不准。
- 方案:把文档切成小块(Child)用于检索,但存储大块(Parent)。检索到小块后,把对应的大块(父文档)喂给 LLM。
- **假设性问题索引(HyDE)**:
- 原理:用户问"怎么修电脑?",先用 LLM 写一个"修电脑的教程"作为假文档,用这个假文档去向量库匹配。因为"教程"和"教程"的向量距离,比"问题"和"教程"的距离更近。
- 分块优化 :
- 3.2.3 检索优化实战(Retrieval)
- **混合检索(Hybrid Search)**:
- 向量检索:懂语义("苹果"匹配"水果"),但不懂专有名词。
- **关键词检索(BM25)**:懂精确匹配("iPhone 15"必须匹配"iPhone 15"),但不懂语义。
- 结合:两者结果加权融合,取长补短。
- 元数据过滤:在检索时加上条件,如"只看 2024 年的文档"或"只看技术部的文档"。
- **混合检索(Hybrid Search)**:
- 3.2.4 检索后优化实战(Post-Retrieval)
- **重排序(Rerank)**:
- 痛点:向量检索为了速度,精度一般,可能把不相关的排在前面。
- 方案 :先粗排(召回 50 条),再用 Cross-Encoder 模型(如 BGE-Reranker)精排(选出 Top 5)。这是提升效果最立竿见影的手段。
- 信息压缩:检索到的 5 个文档块可能很长,用 LLM 把它们压缩成简短的"关键句",腾出上下文窗口。
- **重排序(Rerank)**:
配套代码实现
我们将实现两个最核心的 Advanced RAG 技术:RAG-Fusion(多查询融合) 和 **重排序(Rerank)**。
1. 环境准备
bash
# 安装 langchain 和 sentence-transformers (用于 Rerank)
pip install langchain langchain-community langchain-huggingface rank_bm25
2. RAG-Fusion:多查询生成与融合
这段代码演示如何把一个简单问题变成多个问题,并融合结果。
python
from langchain.prompts import ChatPromptTemplate
from langchain.schema import Document
from langchain.output_parsers import CommaSeparatedListOutputParser
from langchain_huggingface import HuggingFaceEmbeddings
from langchain_community.vectorstores import FAISS
# 模拟一个简单的向量库
def create_mock_vectorstore():
docs = [
Document(page_content="减肥需要控制饮食,少吃高糖食物。"),
Document(page_content="运动减肥推荐跑步和游泳,每周三次。"),
Document(page_content="减肥药可能含有咖啡因,有副作用。"),
Document(page_content="Python 是一门编程语言,用于开发 AI。"),
Document(page_content="健康的减肥速度是每周 0.5 到 1 公斤。")
]
embeddings = HuggingFaceEmbeddings(model_name="sentence-transformers/all-MiniLM-L6-v2")
return FAISS.from_documents(docs, embeddings)
def rag_fusion_pipeline(user_query):
print(f"--- 原始提问: {user_query} ---")
# 1. 查询生成 (Query Generation)
# 使用 Prompt 让 LLM 生成多个相关问题
# 这里为了演示,我们模拟 LLM 的输出,实际需接入 LLM API
generated_queries = [
"减肥饮食建议",
"减肥运动计划",
"健康减肥速度"
]
print(f"🤖 生成的相关问题: {generated_queries}")
# 2. 并行检索 (Retrieval)
vectorstore = create_mock_vectorstore()
all_results = []
for query in generated_queries:
# 每个问题都去检索
docs = vectorstore.similarity_search(query, k=2)
all_results.extend(docs)
# 3. 结果融合 (Fusion - 简化版去重)
# 实际 RRF 算法会更复杂,这里仅作演示去重
unique_docs = []
seen_content = set()
for doc in all_results:
if doc.page_content not in seen_content:
unique_docs.append(doc)
seen_content.add(doc.page_content)
print(f"\n--- 融合后结果 (Top {len(unique_docs)}) ---")
for i, doc in enumerate(unique_docs):
print(f"{i+1}. {doc.page_content}")
# 运行演示
# rag_fusion_pipeline("怎么减肥?")
3. 重排序(Rerank)实战
这是工业界最常用的提效手段。先用向量检索(快)召回 10 条,再用 Rerank 模型(慢但准)选出 Top 3。
python
from langchain_huggingface import HuggingFaceCrossEncoder
from langchain.retrievers import ContextualCompressionRetriever
from langchain.retrievers.document_compressors import CrossEncoderReranker
def rerank_demo():
# 1. 准备模拟的"粗糙"检索结果 (包含一些噪声)
mock_docs = [
Document(page_content="今天天气真不错,适合出去玩。"), # 噪声
Document(page_content="CRAG 是一种纠正检索错误的技术。"), # 相关
Document(page_content="RAG 系统常遇到幻觉问题。"), # 相关
Document(page_content="苹果很好吃,红色的。"), # 噪声
Document(page_content="Advanced RAG 包含重排序步骤。") # 相关
]
query = "RAG 技术是什么?"
# 2. 加载重排序模型 (Cross-Encoder)
# 使用 BGE-Reranker,中文效果极佳
print("--- 正在加载 Rerank 模型 (首次需下载) ---")
compressor = HuggingFaceCrossEncoder(model_name="BAAI/bge-reranker-large")
reranker = CrossEncoderReranker(compressor=compresser, top_n=3)
# 3. 执行重排序
# 这一步会计算 Query 和每个文档的交互得分
compressed_docs = reranker.compress_documents(mock_docs, query)
print(f"\n--- 原始结果数: {len(mock_docs)} ---")
print(f"--- 重排序后结果数: {len(compressed_docs)} ---")
print(f"🔥 最终精炼结果:")
for i, doc in enumerate(compressed_docs):
print(f"{i+1}. {doc.page_content}")
if __name__ == "__main__":
# 注意:运行此函数需要下载约 1GB 的模型文件
# rerank_demo()
print("代码已就绪,取消注释即可运行重排序演示。")
代码解析
- RAG-Fusion:核心在于"发散"。通过生成多个视角的查询,我们弥补了单一查询可能存在的语义偏差。比如用户问"怎么减肥",可能只匹配到"减肥"这个词,但通过生成"饮食"、"运动"等子问题,我们能覆盖更全面的知识点。
- **重排序(Rerank)**:核心在于"精选"。向量检索是"海选",速度快但粗糙;重排序是"决赛",模型会逐字逐句对比问题和文档的相关性(使用 Cross-Encoder 架构),虽然慢,但精度极高。这是解决"检索内容不相关"的最有效手段。