系列目标: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 等外部检索源!