前言
LangChain 在 RAG 领域的地位,就像 React 在前端开发中的地位——争议很多,但绕不开。本文不讨论"要不要用 LangChain",而是聚焦在用 LangChain 实现生产级 RAG 的关键技术点,包括很多官方文档里没说清楚的坑。
一、RAG 系统的质量三角
在动手写代码之前,先理解 RAG 的质量评估框架——RAGAS 三角:
答案相关性(Answer Relevance)
▲
/ \
/ \
/ \
上下文相关性 ──── 答案忠实度
(Context (Faithfulness)
Relevance)
三个维度缺一不可:
- 上下文相关性:检索到的片段和问题有多相关?(检索质量)
- 答案忠实度:生成的答案是否基于检索内容,没有幻觉?(生成质量)
- 答案相关性:最终回答是否真的回答了用户的问题?(整体质量)
优化顺序建议:先优化上下文相关性,再优化答案忠实度,最后调整答案相关性。
二、文档解析:被忽视的关键环节
大多数 RAG 教程直接从"加载文档"开始,但文档解析质量直接决定 RAG 天花板。
PDF 解析的坑
# 低质量方案(很多教程在用)
from langchain.document_loaders import PyPDFLoader
loader = PyPDFLoader("document.pdf")
docs = loader.load()
# 问题:表格变乱码,多栏排版错位,图表丢失
# 高质量方案:使用 marker 或 docling
# 安装:pip install marker-pdf
from marker.convert import convert_single_pdf
from marker.models import load_all_models
models = load_all_models()
full_text, images, metadata = convert_single_pdf(
"document.pdf",
models,
batch_multiplier=2
)
# 效果:正确识别表格、多栏排版、公式
语义切片 vs 固定长度切片
固定长度切片(如每 512 tokens 切一刀)会把语义相关的内容切断。
语义切片实现:
from langchain_experimental.text_splitter import SemanticChunker
from langchain_openai.embeddings import OpenAIEmbeddings
# 使用嵌入模型计算语义边界
text_splitter = SemanticChunker(
OpenAIEmbeddings(),
breakpoint_threshold_type="percentile", # 按相似度百分位切
breakpoint_threshold_amount=95 # 相似度低于95%分位时切割
)
docs = text_splitter.create_documents([text])
效果:语义切片的 RAG 准确率比固定切片高约 15-20%。
三、混合检索:BM25 + 向量的最优组合
纯向量检索的问题:对精确匹配不友好。 "公司的注册资本是多少?"——用户想要精确数字,向量检索可能返回相关但不精确的内容。
纯 BM25 关键词检索的问题:无法理解语义相近但措辞不同的查询。
解法:混合检索(Hybrid Search)
from langchain.retrievers import EnsembleRetriever
from langchain_community.retrievers import BM25Retriever
from langchain_community.vectorstores import Qdrant
# 向量检索器
vector_store = Qdrant.from_documents(docs, embeddings)
vector_retriever = vector_store.as_retriever(search_kwargs={"k": 10})
# BM25 检索器
bm25_retriever = BM25Retriever.from_documents(docs)
bm25_retriever.k = 10
# 混合检索:权重 4:6(向量:BM25)
ensemble_retriever = EnsembleRetriever(
retrievers=[bm25_retriever, vector_retriever],
weights=[0.4, 0.6]
)
# 使用
docs = ensemble_retriever.invoke("公司注册资本是多少?")
权重调优经验:
- 以精确信息为主(法律、财务):BM25 权重 0.6
- 以语义理解为主(技术问答):向量权重 0.7
- 均衡场景:各 0.5
四、查询优化:让 AI 理解用户真实意图
用户的问题往往不是最优的检索查询。
查询重写(Query Rewriting)
from langchain.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
rewrite_prompt = ChatPromptTemplate.from_template("""
你是一个查询优化专家。将用户的问题重写为更适合检索的查询。
原始问题:{question}
要求:
1. 扩展关键词(包含同义词、相关术语)
2. 消歧义(明确指代对象)
3. 拆分多意图问题为单一问题
输出格式:重写后的查询(只输出查询,不要解释)
""")
llm = ChatOpenAI(model="gpt-5-mini")
rewriter = rewrite_prompt | llm
rewritten = rewriter.invoke({"question": "这个产品怎么用?"})
# 输出:"{产品名称} 的使用方法、操作步骤和注意事项"
HyDE(假设文档生成)
不用原始问题检索,而是先让 LLM 生成一个"假设答案文档",再用这个文档检索:
from langchain.chains import HypotheticalDocumentEmbedder
hyde_chain = HypotheticalDocumentEmbedder.from_llm(
llm=ChatOpenAI(model="gpt-5-mini"),
embeddings=OpenAIEmbeddings(),
chain_type="stuff"
)
# 生成假设文档并检索
similar_docs = hyde_chain.retrieve("解释 transformer 的注意力机制")
五、Reranker:提升精准度的最后一道关
初始检索(Top-20)→ Reranker 重排序 → 取 Top-5 给 LLM
from langchain.retrievers import ContextualCompressionRetriever
from langchain_community.cross_encoders import HuggingFaceCrossEncoder
from langchain.retrievers.document_compressors import CrossEncoderReranker
# 加载本地 Reranker 模型(BGE-Reranker-v2-m3 是 2026 年中文最佳)
model = HuggingFaceCrossEncoder(
model_name="BAAI/bge-reranker-v2-m3"
)
# 重排 Top-20 取 Top-5
compressor = CrossEncoderReranker(model=model, top_n=5)
compression_retriever = ContextualCompressionRetriever(
base_compressor=compressor,
base_retriever=ensemble_retriever
)
docs = compression_retriever.invoke("你的问题")
性能数据:加 Reranker 后,NDCG@5 平均提升 18%,延迟增加约 150ms。
六、生成优化:让 LLM 只说检索到的内容
from langchain.prompts import ChatPromptTemplate
qa_prompt = ChatPromptTemplate.from_messages([
("system", """你是一个严谨的问答助手。
回答规则:
1. 只使用以下检索到的内容回答问题
2. 如果检索内容中没有相关信息,明确说"根据现有资料,无法回答此问题"
3. 在答案末尾标注信息来源(参考文档标题)
4. 不要添加检索内容之外的任何信息
检索内容:
{context}
"""),
("human", "{question}")
])
七、完整生产级 RAG 流水线
from langchain_core.runnables import RunnablePassthrough
from langchain_core.output_parsers import StrOutputParser
def format_docs(docs):
return "\n\n".join([
f"[来源: {doc.metadata.get('source', '未知')}]\n{doc.page_content}"
for doc in docs
])
# 完整 RAG 链
rag_chain = (
{
"context": rewrite_prompt | llm | (lambda x: x.content) | compression_retriever | format_docs,
"question": RunnablePassthrough()
}
| qa_prompt
| llm
| StrOutputParser()
)
# 调用
answer = rag_chain.invoke("你的问题")
八、评估与监控
from ragas import evaluate
from ragas.metrics import (
faithfulness,
answer_relevancy,
context_recall,
context_precision
)
# 准备评估数据集
eval_dataset = Dataset.from_dict({
"question": questions,
"answer": answers,
"contexts": retrieved_contexts,
"ground_truth": ground_truths
})
# 运行评估
results = evaluate(
eval_dataset,
metrics=[faithfulness, answer_relevancy, context_recall, context_precision]
)
print(results)
# 输出:
# faithfulness: 0.87
# answer_relevancy: 0.91
# context_recall: 0.83
# context_precision: 0.79
总结
生产级 RAG 的质量提升路径:
- 优先修文档解析(换 marker/docling 代替 PyPDF)→ 召回率 +20%
- 换语义切片(SemanticChunker)→ 相关性 +15%
- 加混合检索(BM25 + 向量)→ 综合精准度 +15%
- 加 Reranker(BGE-Reranker)→ Top-5 精准度 +18%
- 加查询重写(HyDE / Query Rewriting)→ 复杂问题 +10%
按优先级依次实施,每一步都有明确的效果提升,别一口气全上。