检索增强生成(Retrieval-Augmented Generation,RAG)是大模型落地企业场景的核心方案。纯参数化模型存在知识截止、幻觉严重、领域知识不足等问题,RAG通过外挂知识库的方式,让大模型在生成回答前先检索相关文档片段,再基于检索结果生成输出,显著提升回答准确性和可溯源性。
RAG架构原理与核心组件拆解
RAG系统由三个核心环节构成:文档处理与向量化、检索召回、生成回答。文档处理阶段将PDF、Word、Markdown等格式的内容提取为纯文本,按固定长度切分为chunk,每个chunk经过Embedding模型编码后存入向量数据库。检索阶段将用户提问同样编码为向量,在向量数据库中做相似度检索,取Top-K相关片段。生成阶段将检索到的片段与原始提问拼接为Prompt,送入大模型生成最终回答。
关键参数包括chunk_size(切片长度)、chunk_overlap(切片重叠)、top_k(召回数量)、similarity_threshold(相似度阈值)。chunk_size通常设为500-1000字符,overlap设为chunk_size的10%-20%,避免语义被切断。top_k一般取3-5,太少会遗漏上下文,太多会引入噪声。
用LangChain搭建RAG管道:完整代码实现
以下是基于LangChain框架的完整RAG实现,使用Chroma作为向量数据库,OpenAI的text-embedding-3-small做Embedding,GPT-4o做生成模型。
import os
from langchain_community.document_loaders import PyPDFLoader, TextLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_openai import OpenAIEmbeddings, ChatOpenAI
from langchain_community.vectorstores import Chroma
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough
# 文档加载
loader = PyPDFLoader("knowledge_base.pdf")
documents = loader.load()
# 文本切分
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=800,
chunk_overlap=150,
separators=["\n\n", "\n", "。", ",", " "]
)
chunks = text_splitter.split_documents(documents)
print(f"切分为 {len(chunks)} 个片段")
# 向量化与存储
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
vectorstore = Chroma.from_documents(
documents=chunks,
embedding=embeddings,
persist_directory="./chroma_db"
)
# 构建检索器
retriever = vectorstore.as_retriever(
search_type="similarity",
search_kwargs={"k": 4}
)
# 定义Prompt模板
template = """你是一个知识库问答助手。请严格根据以下检索到的上下文回答问题。
如果上下文中没有相关信息,请明确说明"知识库中未找到相关内容",不要编造答案。
上下文:
{context}
问题:{question}
回答:"""
prompt = ChatPromptTemplate.from_template(template)
# 构建RAG链
llm = ChatOpenAI(model="gpt-4o", temperature=0)
def format_docs(docs):
return "\n\n".join(f"[片段{i+1}] {doc.page_content}" for i, doc in enumerate(docs))
rag_chain = (
{"context": retriever | format_docs, "question": RunnablePassthrough()}
| prompt
| llm
| StrOutputParser()
)
# 测试问答
question = "公司的退货政策是什么?"
answer = rag_chain.invoke(question)
print(answer)
RAG检索优化:混合检索与重排序
纯向量检索在处理专有名词、缩写、编号等精确匹配场景时效果不佳。混合检索(Hybrid Search)结合BM25关键词检索和向量语义检索,通过Reciprocal Rank Fusion(RRF)算法融合两路结果,召回率显著提升。
from langchain_community.retrievers import BM25Retriever
from langchain.retrievers import EnsembleRetriever
# BM25检索器
bm25_retriever = BM25Retriever.from_documents(chunks)
bm25_retriever.k = 4
# 向量检索器
vector_retriever = vectorstore.as_retriever(search_kwargs={"k": 4})
# 混合检索器(权重可调)
ensemble_retriever = EnsembleRetriever(
retrievers=[bm25_retriever, vector_retriever],
weights=[0.3, 0.7]
)
重排序(Rerank)是另一项关键优化。检索阶段取较大的top_k(如20),再用Cross-Encoder模型对这20个片段重新打分排序,取前4个送入生成阶段。Cross-Encoder比Bi-Encoder精度更高但速度更慢,用在小规模重排序上刚好合适。常用的Rerank模型包括bge-reranker-v2-m3、Cohere Rerank API等。
from langchain.retrievers import ContextualCompressionRetriever
from langchain_cohere import CohereRerank
compressor = CohereRerank(model="rerank-multilingual-v3.0", top_n=4)
compression_retriever = ContextualCompressionRetriever(
base_compressor=compressor,
base_retriever=ensemble_retriever
)
Prompt工程在RAG中的调优策略
RAG系统的Prompt设计直接影响输出质量。temperature设为0确保回答确定性。系统指令中需要明确约束:只基于上下文回答、不得编造、信息不足时声明。对于多轮对话场景,需要用历史记录改写模块将用户追问补全为独立问题,再送入检索器。
from langchain_core.history_aware_retriever import create_history_aware_retriever
# 历史感知检索:将追问改写为独立查询
condense_prompt = ChatPromptTemplate.from_template("""
根据对话历史和最新问题,将其改写为一个独立的检索查询。
不需要回答问题,只需要生成一个用于检索的搜索查询。
对话历史:{chat_history}
最新问题:{input}
检索查询:""")
history_aware_retriever = create_history_aware_retriever(
llm, ensemble_retriever, condense_prompt
)
Prompt中还可以要求模型引用来源片段编号,增强可溯源性。例如在模板末尾加入”请在回答中标注引用的片段编号,如[片段1]”,用户可据此回溯原文。
RAG系统评估方法与指标
评估RAG系统需要区分检索质量和生成质量。检索质量用Hit Rate(正确文档是否在Top-K中)和MRR(Mean Reciprocal Rank)衡量。生成质量用Faithfulness(回答是否忠于检索内容)、Answer Relevancy(回答是否切题)和Context Relevancy(检索内容是否相关)衡量。Ragas框架提供了这些指标的自动化评估工具。
from ragas import evaluate
from ragas.metrics import faithfulness, answer_relevancy, context_precision
from datasets import Dataset
eval_data = {
"question": ["公司的退货政策是什么?"],
"answer": [rag_answer],
"contexts": [[doc.page_content for doc in retrieved_docs]],
"ground_truth": ["7天内无理由退货,15天内质量问题可换货。"]
}
eval_dataset = Dataset.from_dict(eval_data)
result = evaluate(eval_dataset, metrics=[faithfulness, answer_relevancy, context_precision])
print(result)
构建RAG系统时常见的坑包括:文档切分时表格被截断导致语义丢失,可在切分前用Unstructured库按文档结构提取表格内容单独存储;多语言场景下Embedding模型选择不当导致召回率低,推荐使用multilingual-e5-large或bge-m3等多语言模型;向量数据库随着数据量增长查询变慢,需要定期重建索引或使用HNSW算法优化查询性能。
原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/rag-jian-suo-zeng-qiang-sheng-cheng-shi-zhan-yong-langchain/