LangChain RAG 基础实战:从文档加载到智能问答
RAG(检索增强生成)是一种结合信息检索和文本生成的技术,通过访问特定知识库生成更准确的回答。相比传统生成模型,RAG具有知识实时性、答案可追溯、减少幻觉和成本效益等优势。系统核心包括文档加载处理、文本分割、向量化检索和智能生成等组件。文章详细介绍了RAG实现流程,包括代码示例和优化建议(如分块策略、检索优化、提示词设计等),并以人事问答系统为例展示了实际应用。通过合理调优,RAG能构建可靠的领域
什么是RAG?
RAG(Retrieval-Augmented Generation,检索增强生成)是当前自然语言处理领域的重要技术范式,它通过结合信息检索和文本生成的能力,让大语言模型能够基于特定知识库生成准确、可靠的答案。与传统的纯生成模型相比,RAG系统具有以下优势:
-
知识实时性:可以访问最新或特定领域的知识
-
答案可追溯:每个回答都有明确的来源依据
-
减少幻觉:大幅降低模型编造信息的可能性
-
成本效益:不需要为每个特定任务重新训练模型
RAG系统核心组件
一个完整的RAG系统通常包含以下关键组件:
1. 文档加载与处理
from langchain_community.document_loaders import TextLoader
def load_text_file(file_path: str) -> list[Document]:
text_loader = TextLoader(file_path, encoding="utf-8")
documents = text_loader.load()
return documents
文档加载是RAG流水线的第一步。我们使用LangChain的TextLoader来加载文本文件,支持UTF-8编码确保中文文本正确处理。
2. 文本分割策略
from langchain_text_splitters import RecursiveCharacterTextSplitter
def split_documents(documents: list[Document],
split_way: list[str] = None,
chunk_size: int = 800,
chunk_overlap: int = 200) -> list[str]:
# 实现文本分割逻辑
文本分割对RAG性能至关重要。合适的chunk大小和重叠策略能平衡信息的完整性和检索的精准度。实践中,我们使用递归字符文本分割器,支持自定义分隔符。
3. 向量化与检索
from langchain_community.vectorstores import FAISS
from langchain_openai import OpenAIEmbeddings
emb = OpenAIEmbeddings(
model="Qwen/Qwen3-Embedding-8B",
openai_api_base="https://api.siliconflow.cn/v1",
openai_api_key="your-api-key",
)
vector_store = FAISS.from_texts(split_docs, emb)
我们使用FAISS作为向量数据库,它提供了高效的相似性搜索能力。结合强大的Qwen嵌入模型,将文本转换为高维向量表示。
4. 智能生成与提示工程
prompt = PromptTemplate.from_template(
"- Role: 人事客服助理"
"- Background: 用户在寻求与人事相关的问题解答..."
# 详细的提示词设计
)
提示工程是RAG系统的灵魂。我们设计了详细的角色设定、背景描述、技能要求和约束条件,确保模型生成符合预期的回答。
完整RAG流水线实现
chain = (
{
"docs": vector_store.as_retriever() | (lambda rag_docs: display_rag_result(rag_docs, False)),
"question": RunnablePassthrough()
}
| prompt
| llm
| StrOutputParser()
)
这个链式调用实现了完整的RAG流程:
-
检索相关文档片段
-
组合检索结果和用户问题
-
使用精心设计的提示词
-
调用语言模型生成答案
-
解析输出格式
实战应用:人事问答系统
基于上述技术,我们构建了一个人事管理问答系统。系统能够准确回答关于公司政策、流程和规章制度的问题:
# 使用示例
result = chain.invoke("员工入职多久内必须签订劳务合同?")
print(result)
系统严格遵循"不知道"原则:当参考信息中没有相关内容时,会明确告知用户无法回答,而不是编造信息。
然而运行结果,往往不尽人意
- 有时无法获取到正确的答案


- 在对分块chunk_size做了大小调整,增加topK数值后,终于可以正确回答
性能优化建议
1. 分块策略优化:平衡信息的完整性与检索精度
分块是RAG的基础,直接影响检索质量。
-
语义完整性:对于技术文档或法律条文,应保持逻辑段落的完整性(如一个完整的步骤、一个条款),可适当增大
chunk_size(如 800-1200 tokens)。 -
事实精准性:对于问答对或知识条目,应采用更小的
chunk_size(如 200-500 tokens) 来确保检索到最相关的事实片段,减少噪声。 -
高级策略:超越简单的递归分割,尝试按标题分层、按语义分割或使用
LLM辅助分割,确保块边界在语义上合理。
2. 检索优化:提升相关文档的召回率
-
相似度算法:根据任务特性选择算法。
cosine相似度通用性最好;max marginal relevance (MMR)在保证相关性的同时兼顾多样性,避免返回多个重复内容的片段。 -
检索阈值:设置相似度分数或相关度阈值,过滤掉低置信度的检索结果,减少无关上下文对生成环节的干扰。
-
元数据过滤:为文档块添加元数据(如来源章节、文档类型、日期),检索时结合元数据过滤器进行混合查询,实现更精准的筛选。
3. 重排序(Re-Ranking):精排检索结果,提升TOP-K质量
这是连接“粗筛”检索和“精加工”生成的关键桥梁,能显著改善答案质量。
-
问题所在:向量检索返回的TOP-K文档,可能包含与问题语义相似但实际不相关的片段(例如,都提到“合同”但讨论的是不同方面)。直接将这些全部塞给LLM会引入噪声。
-
解决方案:使用一个专门的、更精细的重排序模型对初步检索到的文档列表进行重新打分和排序。
- 如何实现:
-
使用交叉编码器模型:相比双塔式向量模型(快速但精度相对较低),交叉编码器(Cross-Encoder)将问题和每个文档片段进行深度交互计算,能得到更精确的相关性分数,但计算成本更高。
-
工作流:
向量检索 -> 返回Top N个候选片段 (e.g., N=20) -> 重排序模型精排 -> 选择Top K个最相关的片段 (e.g., K=5) -> 填入Prompt。 - 推荐工具:
-
LangChain集成:使用
LangChain的ContextualCompressionRetriever与CrossEncoderReranker(例如基于BAAI/bge-reranker系列或Cohere的在线重排序API)。 - 代码示例:
from langchain.retrievers import ContextualCompressionRetriever from langchain.retrievers.document_compressors import CrossEncoderReranker from langchain_community.utilities import BingSearchAPIWrapper from langchain_community.cross_encoders import HuggingFaceCrossEncoder # 初始化重排序模型 model = HuggingFaceCrossEncoder(model_name="BAAI/bge-reranker-large") compressor = CrossEncoderReranker(model=model, top_n=5) # 精排后只保留Top5 # 包装原有的检索器 compression_retriever = ContextualCompressionRetriever( base_compressor=compressor, base_retriever=vector_store.as_retriever(search_kwargs={"k": 20}) # 先检索20个 ) # 然后在chain中使用 compression_retriever 代替原来的 retriever
-
-
4. 提示词优化:精确引导LLM生成高质量答案
-
结构化提示:采用清晰的模块化结构(如Role/Context/Task/Constraints),便于LLM理解和遵循。
-
少样本学习:在提示词中包含几个高质量的输入-输出示例(Few-shot Learning),明确教导LLM所需的回答格式和风格。
-
强制约束:在提示词中明确强调“仅根据上下文回答”和“拒绝回答未知问题”,并使用
XML或JSON等格式来结构化输出,减少模型幻觉。
5. 多检索器融合:结合不同检索算法的优势
-
混合检索:将向量检索(擅长语义匹配)和关键词检索(如BM25,擅长精确术语匹配)的结果进行融合,综合两者的召回优势。例如,使用
LangChain的EnsembleRetriever。 -
工作流融合:可以并行执行多种检索策略,然后对结果集进行去重、加权或使用重排序模型统一排序,再输入给LLM。
6. 迭代与评估:构建优化闭环
-
建立评估体系:定义关键指标(如答案准确性、相关性、忠实度)并构建一个包含多种问题的测试集。
-
持续迭代:任何优化策略(调整分块参数、更换模型、修改提示词)都应通过评估集进行量化评估,用数据驱动决策,形成一个“优化-评估-迭代”的闭环。
完整代码示例
from typing import List, Type
from langchain_community.document_loaders import TextLoader
from langchain_community.vectorstores import FAISS
from langchain_core.documents import Document
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import PromptTemplate
from langchain_core.runnables import RunnablePassthrough
from langchain_openai import OpenAIEmbeddings, ChatOpenAI
from langchain_text_splitters import RecursiveCharacterTextSplitter
emb = OpenAIEmbeddings(
model="Qwen/Qwen3-Embedding-8B",
openai_api_base="https://api.siliconflow.cn/v1",
openai_api_key="your-api-key",
)
llm = ChatOpenAI(
model="deepseek-ai/DeepSeek-R1-Distill-Qwen-7B",
openai_api_base="https://api.siliconflow.cn",
openai_api_key="your-api-key",
temperature=0.1, # 控制生成多样性
max_tokens=2000 # 限制输出长度
)
# 加载文本文件
def load_text_file(file_path: str) -> list[Document]:
text_loader = TextLoader(file_path, encoding="utf-8")
documents = text_loader.load()
return documents
# 文本分割
def split_documents(documents: list[Document],
split_ways: list[str] = None,
chunk_size: int = 800,
chunk_overlap: int = 200) -> list[str]:
"""
将文档切分为多个文本块
Args:
documents: 需要切分的文档列表
split_ways: 分隔符列表,默认使用RecursiveCharacterTextSplitter的默认分隔符
chunk_size: 每个文本块的最大长度
chunk_overlap: 文本块之间的重叠长度
Returns:
切分后的文本字符串列表
"""
if split_ways is None:
# 使用RecursiveCharacterTextSplitter的默认分隔符
separators = None
else:
separators = split_ways
text_splitter = RecursiveCharacterTextSplitter(
separators=separators,
chunk_size=chunk_size,
chunk_overlap=chunk_overlap
)
split_documents = text_splitter.split_documents(documents)
res = []
for doc in split_documents:
content = doc.page_content
for sep in separators:
content = content.replace(sep, "")
res.append(content)
return res
def display_rag_result(search_docs, is_show_log=True) -> str:
"""
显示RAG检索结果并返回合并的上下文内容
参数:
search_docs: 检索到的文档列表,每个文档应包含page_content属性
is_show_log: 是否显示日志信息,默认为True
返回值:
str: 合并后的文档内容字符串
"""
context = ''
# 遍历检索到的文档,构建合并的上下文内容
for doc in search_docs:
if is_show_log:
print("-" * 50)
print(doc.page_content)
# 将当前文档内容追加到上下文字符串中
if context:
context += "\n" + doc.page_content
else:
context = doc.page_content
return context
if __name__ == '__main__':
# 加载文本文件
docs = load_text_file("公司人事管理流程章程.txt")
split_way = ["---#*split*#---"]
# 文本分割
split_docs = split_documents(docs, split_way, 300, 100)
# 向量化存储
vector_store = FAISS.from_texts(split_docs, emb)
# 定义提示词
prompt = PromptTemplate.from_template(
"- Role: 人事客服助理"
"- Background: 用户在寻求与人事相关的问题解答,需要基于提供的参考信息({docs})获取准确答案。用户可能正在处理与人力资源相关的事务,如招聘、员工关系、薪酬福利等,需要明确、准确的信息来解决问题。"
"- Profile: 你是一位经验丰富的人事客服助理,对人力资源管理的各个模块有着扎实的了解,熟悉招聘流程、员工关系维护、薪酬福利政策等。你擅长从提供的文件或信息中快速提取关键内容,并以简洁明了的方式回答用户的问题。"
"- Skills: 你具备快速阅读和理解文件的能力,能够准确识别与问题相关的关键信息。你擅长逻辑分析,能够将复杂的人事政策或流程简化为易于理解的答案。同时,你具备良好的沟通能力,能够以礼貌、专业的态度回应用户。"
"- Goals: 基于提供的参考信息({docs}),准确回答用户的问题({question}),确保回答内容严格遵循参考信息,不使用任何其他信息。如果无法从参考信息中找到答案,明确告知用户“不知道”,不编造答案。"
"- Constrains: 你只能使用提供的参考信息({docs})作为回答的依据,不能使用任何外部资源或编造答案。如果参考信息中没有相关内容,必须明确告知用户“不知道”。"
"- OutputFormat: 以简洁明了的语言回答用户的问题,确保回答内容直接、准确。"
"- Workflow:"
" 1. 仔细阅读用户提供的参考信息({docs}),提取与问题({question})相关的关键内容。"
" 2. 分析问题({question}),确定其核心要点。"
" 3. 根据提取的关键内容,结合问题的核心要点,给出准确的回答。如果参考信息中没有相关内容,明确告知用户“不知道”。"
"- Examples:"
" - 例子1:"
" - 参考信息::公司规定,员工每月可享受3天带薪病假。"
" - 问题::员工每月可以享受多少天带薪病假?"
" - 回答:根据公司规定,员工每月可享受3天带薪病假。"
" - 例子2:"
" - 参考信息::公司目前没有关于远程工作的政策。"
" - 问题:公司是否有远程工作的政策?"
" - 回答:不知道。"
)
chain = (
{
"docs": vector_store.as_retriever(search_kwargs={"k": 10})
| (lambda rag_docs: display_rag_result(rag_docs, False)),
"question": RunnablePassthrough()
}
| prompt
| llm
| StrOutputParser()
)
print("正在生成答案...")
result = chain.invoke("员工入职多久内必须签订劳务合同?")
print("..." * 100)
print(result)
总结
RAG技术为构建领域特定的智能问答系统提供了强大而灵活的框架。通过合理的组件选择和参数调优,我们可以创建出既准确又可靠的AI助手。本文展示的实现方案使用了流行的LangChain框架和高效的FAISS向量数据库,为开发者提供了一个可扩展的RAG系统基础架构。
随着大语言模型和嵌入技术的不断发展,RAG系统的性能和应用场景将会进一步扩展,为各行各业带来更智能的知识管理和问答解决方案。
注:本文代码示例中的API密钥仅为演示用途,实际使用时请替换为您自己的密钥,并确保遵循相关API的使用条款和安全性要求。
下一篇将引入重排序模型,提升Rag准确度
更多推荐


所有评论(0)