想要让你的 AI 应用访问私有数据?本文详解如何用 LangChain 框架搭建检索增强生成(RAG)系统,让大模型基于你的文档回答问题。
什么是 RAG?为什么你需要它?
检索增强生成(Retrieval-Augmented Generation,简称 RAG)是一种将信息检索与文本生成相结合的技术架构。简单来说,RAG 让大语言模型在回答问题之前,先从你的私有文档库中检索相关信息,然后基于这些信息进行回答。
为什么 RAG 如此重要?
- 解决幻觉问题:大模型可能会”编造”信息,RAG 通过提供真实文档作为上下文,大幅减少幻觉
- 访问私有数据:无需微调模型,即可让 AI 理解你的内部文档、知识库、代码库等
- 成本效益高:相比微调,RAG implementation 成本更低,更新更灵活
- 可追溯性:每个回答都可以追溯到具体的文档来源,便于验证和审计
LangChain 框架简介
LangChain 是目前最流行的 LLM 应用开发框架之一,它提供了模块化、可组合的抽象层,让构建 RAG 应用变得简单。
核心组件:
- Document Loaders:从各种数据源加载文档
- Text Splitters:将长文档切分成适合模型处理的小块
- Embeddings:将文本转换为向量表示
- Vector Stores:存储和检索向量化的文档块
- Retrievers:从向量存储中检索相关文档
- Chains:组合多个组件形成完整的工作流
第一步:环境准备与安装
开始之前,确保你已安装 Python 3.9+ 和 pip。创建虚拟环境并安装必要依赖:
# 创建项目目录 mkdir langchain-rag-demo cd langchain-rag-demo # 创建虚拟环境 python -m venv venv source venv/bin/activate # Windows: venv\Scripts\activate # 安装 LangChain 及相关依赖 pip install langchain langchain-community langchain-openai pip install chromadb # 本地向量数据库 pip install python-dotenv # 环境变量管理
创建 .env 文件存储 API 密钥:
OPENAI_API_KEY=your_openai_api_key_here
第二步:加载与处理文档
RAG 的第一步是将你的文档加载到系统中。LangChain 支持数十种文档加载器,包括 PDF、Word、Markdown、网页等。
from langchain_community.document_loaders import DirectoryLoader, TextLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter
# 加载文档(假设你有一个 docs/ 文件夹存放 Markdown 文件)
loader = DirectoryLoader(
'./docs',
glob='**/*.md',
loader_cls=TextLoader,
loader_kwargs={'encoding': 'utf-8'}
)
documents = loader.load()
print(f"加载了 {len(documents)} 个文档")
# 文本分割:将长文档切分成小块
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=1000, # 每块最大字符数
chunk_overlap=200, # 块之间重叠字符数(保持上下文连贯)
length_function=len,
separators=['\n\n', '\n', '。', '!', '?', ' ', '']
)
texts = text_splitter.split_documents(documents)
print(f"分割成 {len(texts)} 个文本块")
分割策略建议:
- 代码文档:使用更小的 chunk_size(500-800),因为代码结构紧凑
- 技术文章:1000-1500 字符较为合适
- 法律/合同文档:可能需要更大的 chunk(2000+)以保持条款完整性
- 重叠设置:15-20% 的重叠率通常效果最佳
第三步:向量化与存储
将文本块转换为向量并存储到向量数据库中。这里我们使用 ChromaDB(本地、无需配置):
from langchain_openai import OpenAIEmbeddings
from langchain_community.vectorstores import Chroma
# 初始化嵌入模型
embeddings = OpenAIEmbeddings(
model='text-embedding-3-small', # 性价比高
# model='text-embedding-3-large' # 精度更高,成本也更高
)
# 创建向量存储
vectorstore = Chroma.from_documents(
documents=texts,
embedding=embeddings,
persist_directory='./chroma_db' # 持久化存储路径
)
print(f"向量数据库已创建,包含 {vectorstore._collection.count()} 个向量")
向量模型选择指南:
| 模型 | 维度 | 成本 | 适用场景 |
|---|---|---|---|
| text-embedding-3-small | 1536 | $ | 一般用途,性价比高 |
| text-embedding-3-large | 3072 | $$ | 高精度需求,复杂语义 |
| text-embedding-ada-002 | 1536 | $$ | 旧模型,已不推荐 |
其他向量数据库选项:
- ChromaDB:轻量级,适合开发和小型部署
- FAISS:Facebook 开源,性能优秀,适合大规模
- Pinecone:托管服务,开箱即用
- Weaviate:支持混合搜索(向量 + 关键词)
- Qdrant:Rust 编写,性能出色
第四步:构建检索器与问答链
现在创建检索器,并组合成完整的问答链:
from langchain_openai import ChatOpenAI
from langchain.chains import create_retrieval_chain
from langchain.chains.combine_documents import create_stuff_documents_chain
from langchain_core.prompts import ChatPromptTemplate
# 初始化 LLM
llm = ChatOpenAI(
model='gpt-4o-mini', # 性价比高
temperature=0, # RAG 场景建议低温,减少幻觉
# model='gpt-4o' # 更高精度
)
# 创建检索器
retriever = vectorstore.as_retriever(
search_type='similarity',
search_kwargs={'k': 4} # 每次检索返回 4 个最相关文档块
)
# 自定义系统提示词
system_prompt = '''你是一位专业的技术助手,基于提供的上下文回答用户问题。
请遵循以下规则:
1. 只根据提供的上下文回答问题
2. 如果上下文中没有答案,明确告知用户
3. 引用相关文档来源
4. 回答要简洁、准确、有条理
上下文:
{context}
'''
prompt = ChatPromptTemplate.from_messages([
('system', system_prompt),
('human', '{input}')
])
# 创建文档组合链
question_answer_chain = create_stuff_documents_chain(llm, prompt)
# 创建完整的 RAG 链
rag_chain = create_retrieval_chain(retriever, question_answer_chain)
第五步:测试与优化
现在测试你的 RAG 系统:
# 测试问答
question = '如何在 LangChain 中设置自定义提示词?'
result = rag_chain.invoke({'input': question})
print('问题:', question)
print('回答:', result['answer'])
print('来源文档:')
for doc in result['context']:
print(f' - {doc.metadata.get("source", "未知")}')
优化技巧:
1. 调整检索参数
# 增加检索数量
retriever = vectorstore.as_retriever(search_kwargs={'k': 8})
# 使用相似度阈值过滤
retriever = vectorstore.as_retriever(
search_type='similarity_score_threshold',
search_kwargs={'score_threshold': 0.7}
)
# 混合搜索(向量 + 关键词)
retriever = vectorstore.as_retriever(search_type='mmr', search_kwargs={'k': 4, 'fetch_k': 20})
2. 优化提示词
system_prompt = '''你是一位专业的技术文档助手。
回答规则:
- 严格基于提供的上下文
- 如果信息不足,说"根据提供的文档,无法找到相关信息"
- 使用 Markdown 格式组织回答
- 包含代码示例时使用代码块
- 列出引用来源
相关文档:
{context}
用户问题:{input}
回答:'''
3. 添加对话历史(多轮对话)
from langchain.chains import create_history_aware_retriever
from langchain_core.prompts import MessagesPlaceholder
# 支持多轮对话的提示词
contextualize_q_system_prompt = '''给定聊天历史和最新用户问题,
如果最新问题指代了历史中的内容,将其改写为独立的问题。
否则,原样返回最新问题。
聊天历史:{chat_history}
最新问题:{input}
独立问题:'''
contextualize_q_prompt = ChatPromptTemplate.from_messages([
('system', contextualize_q_system_prompt),
MessagesPlaceholder('chat_history'),
('human', '{input}')
])
# 创建历史感知检索器
history_aware_retriever = create_history_aware_retriever(
llm,
retriever,
contextualize_q_prompt
)
完整示例代码
将以上步骤整合为一个完整的可运行脚本:
#!/usr/bin/env python3
"""
LangChain RAG 应用完整示例
"""
import os
from dotenv import load_dotenv
from langchain_community.document_loaders import DirectoryLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_openai import OpenAIEmbeddings, ChatOpenAI
from langchain_community.vectorstores import Chroma
from langchain.chains import create_retrieval_chain
from langchain.chains.combine_documents import create_stuff_documents_chain
from langchain_core.prompts import ChatPromptTemplate
# 加载环境变量
load_dotenv()
def setup_rag(docs_path='./docs'):
"""初始化 RAG 系统"""
# 加载文档
loader = DirectoryLoader(docs_path, glob='**/*.md')
documents = loader.load()
# 分割文本
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=1000,
chunk_overlap=200
)
texts = text_splitter.split_documents(documents)
# 创建向量存储
embeddings = OpenAIEmbeddings(model='text-embedding-3-small')
vectorstore = Chroma.from_documents(
documents=texts,
embedding=embeddings,
persist_directory='./chroma_db'
)
# 创建检索器
retriever = vectorstore.as_retriever(search_kwargs={'k': 4})
# 初始化 LLM
llm = ChatOpenAI(model='gpt-4o-mini', temperature=0)
# 创建提示词
system_prompt = '''基于以下上下文回答问题。如果信息不足,请说明。
上下文:
{context}
'''
prompt = ChatPromptTemplate.from_messages([
('system', system_prompt),
('human', '{input}')
])
# 创建 RAG 链
qa_chain = create_stuff_documents_chain(llm, prompt)
rag_chain = create_retrieval_chain(retriever, qa_chain)
return rag_chain
def ask_question(rag_chain, question):
"""提问并获取回答"""
result = rag_chain.invoke({'input': question})
return result['answer'], result['context']
if __name__ == '__main__':
# 初始化
print('正在初始化 RAG 系统...')
rag = setup_rag()
print('初始化完成!\n')
# 交互式问答
while True:
question = input('请输入问题(输入 q 退出): ')
if question.lower() == 'q':
break
answer, sources = ask_question(rag, question)
print(f'\n回答:{answer}\n')
print('参考来源:')
for src in sources:
print(f' - {src.metadata.get("source", "未知")}')
print()
常见问题解答
Q1: RAG 回答不准确怎么办?
可能原因与解决方案:
- 检索到的文档不相关 → 调整 chunk_size 或增加 k 值
- 提示词不够明确 → 优化 system prompt,添加更具体的指令
- 嵌入模型不合适 → 尝试不同的 embedding 模型
- 文档质量差 → 预处理文档,去除噪声
Q2: 如何处理大型文档库(10 万 + 文档)?
- 使用 FAISS 或 Qdrant 等高性能向量数据库
- 实现分层检索(先分类,再检索)
- 添加元数据过滤(按日期、类别等筛选)
- 考虑分布式部署
Q3: 如何降低 API 成本?
- 使用更小的模型(gpt-4o-mini 而非 gpt-4o)
- 减少检索的 k 值
- 缓存常见问题的回答
- 使用本地嵌入模型(如 sentence-transformers)
Q4: 如何评估 RAG 系统质量?
关键指标:
- 检索准确率:检索到的文档是否相关
- 回答准确性:回答是否正确且基于上下文
- 响应时间:端到端延迟
- 用户满意度:收集用户反馈
总结
通过本文的 5 个步骤,你已经掌握了使用 LangChain 构建 RAG 应用的核心技能:
- ✅ 环境准备与依赖安装
- ✅ 文档加载与文本分割
- ✅ 向量化与向量存储
- ✅ 检索器与问答链构建
- ✅ 测试与优化技巧
RAG 是构建企业级 AI 应用的关键技术,掌握它将让你能够创建基于私有数据的智能助手、知识库问答系统、代码助手等强大应用。
下一步学习建议:
- 尝试不同的向量数据库(FAISS、Pinecone)
- 探索多模态 RAG(图像 + 文本)
- 学习 Agent 框架,让 AI 自主调用工具
- 研究高级检索策略(HyDE、多查询检索)
参考资源:
