🌟 LangChain 30 天保姆级教程 · Day 19|RAG 进阶三板斧!多路召回 + 重排序 + HyDE,让检索准确率飙升!

5 阅读4分钟

系列目标:30 天从 LangChain 入门到企业级部署
今日任务:掌握 RAG 三大进阶技巧 → 构建高精度检索管道 → 让 AI 回答“又快又准”!


🎯 一、为什么需要 RAG 优化?

在 Day 18 中,我们构建了基础 RAG 系统,但它存在检索瓶颈

  • ❌ 单一向量检索可能漏掉关键文档
  • ❌ 相似度高的片段未必最相关(语义鸿沟)
  • ❌ 用户问题表述模糊(如“怎么弄?”)

解决方案

✅ 多路召回(Multi-stage Retrieval) :融合多种检索策略
✅ 重排序(Rerank) :用更强模型对结果精排
✅ HyDE(Hypothetical Document Embeddings) :生成假设答案再检索

💡 今天,我们就用这“三板斧”,把 RAG 准确率提升 30%+!


🧰 二、准备工作:安装依赖

# 重排序模型(本地运行)
pip install sentence-transformers torch

# LangChain 组件
pip install langchain-community

⚠️ 重排序模型(如 bge-reranker-large)约 1.3GB,首次运行自动下载。


🔍 三、技巧 1:多路召回(Multi-stage Retrieval)

思路

同时用 向量检索 + 关键词检索(BM25) ,合并结果去重。

# day19_rag_advanced.py
from langchain_chroma import Chroma
from langchain_ollama import OllamaEmbeddings
from langchain.retrievers import BM25Retriever, EnsembleRetriever

# 加载向量库
embeddings = OllamaEmbeddings(model="nomic-embed-text")
vectorstore = Chroma(persist_directory="./chroma_db", embedding_function=embeddings)

# 1. 向量检索器
vector_retriever = vectorstore.as_retriever(search_kwargs={"k": 4})

# 2. BM25 关键词检索器(基于分词)
docs = vectorstore.get()["documents"]  # 获取所有文本
bm25_retriever = BM25Retriever.from_texts(docs, k=4)

# 3. 融合检索器(权重:向量 0.7,关键词 0.3)
ensemble_retriever = EnsembleRetriever(
    retrievers=[vector_retriever, bm25_retriever],
    weights=[0.7, 0.3]
)

✅ 优势:

  • 向量检索抓语义
  • BM25 抓关键词(如产品编号、人名)
  • 融合后覆盖更全面

📊 四、技巧 2:重排序(Rerank)

思路

用一个专用排序模型对召回结果重新打分,只保留 Top-K 最相关。

from langchain_community.cross_encoders import HuggingFaceCrossEncoder
from langchain.retrievers import ContextualCompressionRetriever
from langchain.retrievers.document_compressors import CrossEncoderReranker

# 加载开源重排序模型(支持中文)
model = HuggingFaceCrossEncoder(model_name="BAAI/bge-reranker-large")

# 创建压缩器(即重排序器)
compressor = CrossEncoderReranker(model=model, top_n=2)

# 包装检索器
compression_retriever = ContextualCompressionRetriever(
    base_compressor=compressor,
    base_retriever=ensemble_retriever  # 使用多路召回结果
)

🔑 top_n=2:从 8 个召回结果中精选 2 个最相关的!

✅ 效果

  • 去除噪声片段
  • 提升 LLM 上下文质量
  • 减少 token 消耗

💭 五、技巧 3:HyDE(假设性文档嵌入)

思路

用户问题太模糊?让 LLM 先生成一个假设答案,再用这个“假答案”去检索真实文档!

例如:
用户问:“怎么弄?”
LLM 生成假设答案:“用户可能想了解如何申请年假。”
用这句话去检索 → 找到年假政策!

from langchain.chains import LLMChain
from langchain_core.prompts import PromptTemplate
from langchain.retrievers import ContextualCompressionRetriever
from langchain_ollama import ChatOllama

# 1. 定义 HyDE Prompt
hyde_prompt = PromptTemplate(
    input_variables=["question"],
    template="请根据以下问题,生成一个假设性的详细答案:{question}"
)

# 2. 创建 HyDE 链
llm = ChatOllama(model="qwen:7b", temperature=0)
hyde_chain = LLMChain(llm=llm, prompt=hyde_prompt)

# 3. 自定义 HyDE 检索器
class HyDERetriever:
    def __init__(self, base_retriever, hyde_chain):
        self.base_retriever = base_retriever
        self.hyde_chain = hyde_chain

    def get_relevant_documents(self, query):
        # 生成假设答案
        hypothetical_doc = self.hyde_chain.run(query)
        print(f"🔍 HyDE 生成假设:{hypothetical_doc[:50]}...")
        # 用假设答案检索
        return self.base_retriever.get_relevant_documents(hypothetical_doc)

# 组装最终检索器
final_retriever = HyDERetriever(
    base_retriever=compression_retriever,
    hyde_chain=hyde_chain
)

✅ 特别适合模糊、简短、口语化的用户提问!


🤖 六、整合到 RAG Chain

from langchain.chains import RetrievalQA

# 自定义检索函数(适配 RetrievalQA)
def custom_retriever(query):
    return final_retriever.get_relevant_documents(query)

# 创建 RAG 链(注意:需包装 retriever)
qa_chain = RetrievalQA.from_chain_type(
    llm=llm,
    chain_type="stuff",
    retriever=final_retriever,  # LangChain 会自动调用 get_relevant_documents
    return_source_documents=True
)

# 测试
query = "怎么弄年假?"
result = qa_chain({"query": query})
print("🤖 AI 回答:", result["result"])

⚠️ 七、性能与效果权衡

表格

技巧提升效果增加延迟适用场景
多路召回★★★☆+200ms通用
重排序★★★★+500ms高精度要求
HyDE★★☆+800ms模糊查询

💡 生产建议

  • 对高频问题缓存检索结果
  • 重排序模型用 GPU 加速(如有)
  • HyDE 仅用于短/模糊问题,长问题直接检索

📦 八、配套代码结构

langchain-30-days/
└── day19/
    └── advanced_rag_pipeline.py  # 多路召回 + Rerank + HyDE

📝 九、今日小结

  • ✅ 理解了基础 RAG 的局限性
  • ✅ 掌握了多路召回融合语义与关键词
  • ✅ 学会了用 BGE-Reranker 精排结果
  • ✅ 实现了 HyDE 解决模糊查询
  • ✅ 知道了如何在效果与性能间做权衡

🎯 明日预告:Day 20 —— 自定义 Retriever!接入 Elasticsearch、数据库、API 等外部检索源!