1. 项目概述:用LangChain快速构建RAG问答系统
作为一名非科班出身的编程爱好者,我一直在寻找能够快速上手AI应用开发的学习路径。去年接触RAG(检索增强生成)技术时,曾被各种晦涩的概念和复杂的框架搞得晕头转向。直到发现LangChain这个"AI应用开发的乐高积木",配合通义千问大模型,终于在三小时内搭建出了第一个可用的智能文档助手。这个项目完美复刻了Angular"英雄之旅"教程的理念——通过最小可行产品快速掌握核心概念。
这个系统实现了三大核心功能:
- 智能对话:基于上传的文档内容进行问答
- 知识库管理:支持PDF/TXT文件上传与向量化存储
- 系统分析:可视化检索过程与生成结果
技术栈选择上,我采用了最轻量级的组合:
- LangChain:作为AI应用编排框架,简化了RAG流程开发
- Qwen-Plus:通义千问的中等规模模型,性价比优异
- FAISS:Facebook开源的本地向量数据库,零配置即可使用
关键提示:LangChain在2023年10月进行了重大架构调整,网上大部分旧版代码已不兼容。本文所有代码均基于langchain-core 0.1.0+版本验证通过。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 环境配置与项目初始化
2.1 开发环境准备
首先需要确保本地已安装Python 3.8+环境。建议使用conda创建独立环境以避免依赖冲突:
bash复制conda create -n langchain-qwen python=3.10
conda activate langchain-qwen
项目目录结构设计如下:
code复制langchain-qwen-hero/
├── docs/ # 存放待处理的文档
├── vector_store/ # FAISS向量数据库
├── app.py # Streamlit主程序
├── qwen_client.py # 千问模型客户端封装
├── rag_chain.py # RAG流程核心逻辑
└── .env # 环境变量配置
2.2 依赖安装与版本控制
执行以下命令安装关键依赖(特别注意版本号):
bash复制pip install langchain==0.1.0 langchain-community==0.0.10 \
dashscope==1.14.0 python-dotenv==1.0.0 \
pypdf==3.17.0 faiss-cpu==1.7.4 \
tiktoken==0.5.1 langchain-text-splitters==0.0.1 \
streamlit==1.28.0
常见安装问题解决方案:
- 如遇
grpc相关错误,先执行pip install --upgrade grpcio - Windows系统需安装Microsoft Visual C++ 14.0+编译工具
- Mac M系列芯片建议使用
faiss-cpu而非faiss-gpu
2.3 通义千问API配置
- 登录阿里云百炼控制台
- 在"API密钥管理"中创建新密钥
- 将获取的API_KEY存入项目根目录的.env文件:
ini复制DASHSCOPE_API_KEY=sk-你的实际密钥
QWEN_MODEL=qwen-plus # 可选qwen-max(更强)或qwen-turbo(更快)
安全提示:切勿将.env文件提交到Git仓库!建议在.gitignore中添加
.env
3. 核心模块实现
3.1 千问模型客户端封装
创建qwen_client.py实现模型调用抽象层:
python复制import os
from dotenv import load_dotenv
from dashscope import Generation
from langchain_core.language_models.llms import BaseLLM
load_dotenv()
class QwenClient(BaseLLM):
@property
def _llm_type(self) -> str:
return "qwen"
def _call(self, prompt: str, stop=None, **kwargs) -> str:
response = Generation.call(
model=os.getenv("QWEN_MODEL", "qwen-plus"),
prompt=prompt,
**kwargs
)
return response.output.text
async def _acall(self, prompt: str, stop=None, **kwargs) -> str:
# 异步实现略
pass
关键设计考量:
- 继承
BaseLLM使其兼容LangChain生态 - 通过
_call实现同步调用,_acall支持异步(需额外处理) - 从.env读取配置,提高灵活性
3.2 文档处理流水线设计
在rag_chain.py中构建文档处理全流程:
python复制from langchain_community.document_loaders import PyPDFLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_community.vectorstores import FAISS
from langchain_core.embeddings import Embeddings
from typing import List
class DocumentProcessor:
def __init__(self, embeddings: Embeddings):
self.embeddings = embeddings
self.text_splitter = RecursiveCharacterTextSplitter(
chunk_size=500,
chunk_overlap=50,
length_function=len
)
def load_documents(self, file_path: str) -> List[Document]:
if file_path.endswith(".pdf"):
loader = PyPDFLoader(file_path)
else: # 支持txt等文本格式
loader = TextLoader(file_path)
return loader.load()
def process_documents(self, docs: List[Document]) -> FAISS:
splits = self.text_splitter.split_documents(docs)
return FAISS.from_documents(splits, self.embeddings)
文档处理注意事项:
- PDF解析使用
pypdf,对复杂格式可能需调整解析策略 - 分块大小(chunk_size)影响检索精度,建议300-800之间
- 重叠字符(chunk_overlap)可避免上下文断裂,建议10%-20%
3.3 RAG核心链实现
继续在rag_chain.py中添加问答逻辑:
python复制from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnablePassthrough
from langchain_core.output_parsers import StrOutputParser
class QwenRAGChain:
def __init__(self, llm: BaseLLM, vectorstore: FAISS):
self.retriever = vectorstore.as_retriever()
self.llm = llm
self.prompt = ChatPromptTemplate.from_template("""
你是一个专业的文档助手,请根据以下上下文回答问题:
上下文:{context}
问题:{question}
回答时请:
1. 严格基于提供的上下文
2. 如果上下文不相关,回答"根据文档无法回答该问题"
3. 保持回答简洁专业
""")
self.chain = (
{"context": self.retriever, "question": RunnablePassthrough()}
| self.prompt
| self.llm
| StrOutputParser()
)
def query(self, question: str) -> str:
return self.chain.invoke(question)
RAG链设计要点:
- 检索器配置
search_kwargs={"k": 3}可调整返回的上下文数量 - 提示模板严格控制回答范围,避免幻觉(hallucination)
- 使用
RunnablePassthrough保持问题原样传递
4. Streamlit可视化界面开发
创建app.py实现Web界面:
python复制import streamlit as st
from qwen_client import QwenClient
from rag_chain import DocumentProcessor, QwenRAGChain
import os
from dotenv import load_dotenv
load_dotenv()
# 初始化组件
@st.cache_resource
def init_components():
llm = QwenClient()
embeddings = ... # 使用DashScopeEmbeddings
processor = DocumentProcessor(embeddings)
return llm, processor
llm, processor = init_components()
# 界面布局
st.title("📄 智能文档助手")
tab1, tab2, tab3 = st.tabs(["智能对话", "知识库管理", "系统分析"])
with tab1:
if "vectorstore" not in st.session_state:
st.warning("请先上传文档构建知识库")
else:
question = st.text_input("输入您的问题")
if question:
rag = QwenRAGChain(llm, st.session_state.vectorstore)
answer = rag.query(question)
st.markdown(f"**回答:** {answer}")
with tab2:
uploaded_file = st.file_uploader("上传文档", type=["pdf", "txt"])
if uploaded_file:
file_path = f"./docs/{uploaded_file.name}"
with open(file_path, "wb") as f:
f.write(uploaded_file.getbuffer())
docs = processor.load_documents(file_path)
vectorstore = processor.process_documents(docs)
st.session_state.vectorstore = vectorstore
st.success(f"已成功处理 {len(docs)} 页文档!")
with tab3:
if "vectorstore" in st.session_state:
st.write("最近检索记录将在这里可视化...")
界面优化技巧:
- 使用
@st.cache_resource缓存初始化耗时的组件 - 通过
st.session_state保持向量库状态 - 文件上传后保存到本地,避免重复处理
5. 常见问题与调试技巧
5.1 依赖冲突解决
当出现ImportError时,通常是由于LangChain生态的模块化改造导致。新版中需要显式安装:
- 向量存储:
langchain-community - 文档加载器:
langchain-text-splitters - 大模型集成:
langchain-llm-dashscope
5.2 中文处理优化
默认配置对中文支持不够友好,可通过以下调整优化:
python复制text_splitter = RecursiveCharacterTextSplitter(
separators=["\n\n", "\n", "。", "!", "?", ","], # 中文标点优先
chunk_size=300,
chunk_overlap=30
)
5.3 检索效果提升
若发现检索结果不准确:
- 调整分块策略,使每个chunk包含完整语义
- 在
as_retriever()中配置search_type="mmr"(最大边际相关) - 添加元数据过滤:
python复制vectorstore.as_retriever(
search_kwargs={
"k": 4,
"filter": {"source": "重要文档.pdf"}
}
)
5.4 性能监控
添加日志记录检索耗时:
python复制import time
from functools import wraps
def log_time(func):
@wraps(func)
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
print(f"{func.__name__}耗时: {time.time()-start:.2f}s")
return result
return wrapper
# 装饰query方法
@log_time
def query(self, question: str) -> str:
...
6. 项目扩展方向
这个基础版本完成后,可以考虑以下增强功能:
-
多文档管理:
- 实现文档命名空间隔离
- 添加文档删除/更新功能
- 支持批量上传文件夹
-
对话历史:
python复制from langchain_core.messages import HumanMessage, AIMessage chat_history = [] def query_with_history(question): result = rag.chain.invoke({ "question": question, "chat_history": chat_history }) chat_history.extend([ HumanMessage(content=question), AIMessage(content=result) ]) return result -
混合检索策略:
- 结合关键词检索与向量检索
- 实现rerank重排序
- 添加元数据过滤条件
-
部署优化:
- 使用Docker容器化
- 配置Nginx反向代理
- 添加API访问鉴权
这个项目最让我惊喜的是,用如此简洁的代码就实现了可用的RAG系统。LangChain虽然学习曲线陡峭,但一旦掌握其设计哲学,就能快速搭建出功能丰富的AI应用。建议初学者不要被官方文档的复杂度吓退,从这个"英雄之旅"项目入手,逐步探索更高级的功能。
