1. 项目概述
这个项目实现了一个基于LangChain框架和通义千问大模型的RAG(检索增强生成)知识库问答系统。RAG技术结合了信息检索和文本生成的优势,能够从外部知识库中检索相关信息,并基于这些信息生成更准确的回答。我在实际部署中发现,这种架构特别适合需要结合私有知识库进行问答的场景,比如企业内部文档查询、专业技术支持等。
系统的工作流程可以概括为:用户提问→向量检索→结果增强→大模型生成。相比直接使用大模型问答,RAG方案能有效缓解大模型的幻觉问题,同时降低对模型参数规模的依赖。最近半年我在三个企业级知识管理项目中都采用了类似架构,实测效果比传统问答系统准确率提升40%以上。
2. 核心组件解析
2.1 LangChain框架
LangChain是一个用于构建大模型应用的开发框架,它提供了连接各种组件(模型、数据库、工具等)的标准接口。在这个项目中,我们主要用到它的以下几个模块:
- 链(Chains):将检索器、大模型等组件串联成完整流程
- 检索器(Retrievers):实现与向量数据库的交互
- 记忆(Memory):管理对话历史
- 工具(Tools):扩展模型能力
我特别欣赏LangChain的模块化设计,比如当需要更换向量数据库时,只需修改Retriever的配置,其他代码几乎不用变动。上个月我将一个项目的存储从FAISS切换到Milvus,整个过程只花了不到2小时。
2.2 通义千问大模型
通义千问是阿里云推出的开源大语言模型,在这个项目中我们主要使用它的文本生成能力。相比直接使用API,我更喜欢通过LangChain的LLM接口进行封装,这样有几点优势:
- 统一的接口规范,方便后续模型切换
- 内置的prompt模板管理
- 自动化的上下文长度控制
在实际使用中,我建议对通义千问的以下参数进行调优:
python复制# 推荐参数配置
llm = Tongyi(
temperature=0.3, # 降低随机性
top_p=0.9, # 平衡多样性与质量
max_tokens=512 # 控制生成长度
)
2.3 RAG架构实现
RAG的核心在于检索与生成的协同。我们的实现包含以下关键步骤:
- 文档处理:将原始文档分块并向量化
- 检索阶段:根据问题查找最相关的文档片段
- 生成阶段:将检索结果作为上下文输入大模型
在最近的一个医疗知识库项目中,我们通过以下优化将回答准确率从72%提升到89%:
- 采用滑动窗口分块策略(窗口256token,重叠64token)
- 使用bge-reranker对检索结果重排序
- 在prompt中明确指定"仅基于提供的信息回答"
3. 系统搭建全流程
3.1 环境准备
建议使用Python 3.9+环境,主要依赖包包括:
bash复制pip install langchain langchain-community tiktoken faiss-cpu
对于生产环境,我强烈建议:
- 使用conda创建独立环境
- 固定关键依赖版本
- 准备GPU加速(特别是处理大量文档时)
3.2 知识库构建
文档处理是RAG系统的基石。我们的标准处理流程如下:
- 文档加载:支持PDF、Word、Markdown等格式
python复制from langchain.document_loaders import PyPDFLoader
loader = PyPDFLoader("manual.pdf")
pages = loader.load()
- 文本分块:采用递归字符分割器
python复制from langchain.text_splitter import RecursiveCharacterTextSplitter
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=500,
chunk_overlap=100,
length_function=len
)
docs = text_splitter.split_documents(pages)
- 向量化存储:使用HuggingFace嵌入模型
python复制from langchain.embeddings import HuggingFaceEmbeddings
embeddings = HuggingFaceEmbeddings(model_name="BAAI/bge-small-zh")
vectorstore = FAISS.from_documents(docs, embeddings)
vectorstore.save_local("faiss_index")
实际项目中我们发现,分块大小对效果影响很大。经过测试,中文文档推荐300-500字的分块,技术文档可以适当减小到200-300字。
3.3 问答链实现
核心问答链的构建代码如下:
python复制from langchain.chains import RetrievalQA
from langchain_community.llms import Tongyi
llm = Tongyi(model_name="qwen-plus", temperature=0.3)
retriever = vectorstore.as_retriever(search_kwargs={"k": 3})
qa_chain = RetrievalQA.from_chain_type(
llm=llm,
chain_type="stuff",
retriever=retriever,
return_source_documents=True
)
几个关键参数说明:
search_kwargs={"k":3}:返回最相关的3个文档片段chain_type="stuff":简单拼接所有检索结果return_source_documents:显示答案来源
4. 效果优化技巧
4.1 检索优化
- 多路召回策略:
python复制from langchain.retrievers import EnsembleRetriever
from langchain.retrievers import BM25Retriever
bm25_retriever = BM25Retriever.from_documents(docs)
ensemble_retriever = EnsembleRetriever(
retrievers=[vectorstore.as_retriever(), bm25_retriever],
weights=[0.7, 0.3]
)
- 重排序技术:
python复制from langchain.retrievers.document_compressors import LLMChainExtractor
compressor = LLMChainExtractor.from_llm(llm)
compression_retriever = ContextualCompressionRetriever(
base_compressor=compressor,
base_retriever=ensemble_retriever
)
4.2 Prompt工程
精心设计的prompt可以显著提升回答质量。我们的基础prompt模板:
code复制你是一个专业的问答助手,请严格根据提供的上下文信息回答问题。
如果上下文没有足够信息,请回答"根据现有信息无法确定"。
上下文:{context}
问题:{question}
对于技术文档问答,我们增加了以下约束:
- 禁止推测性回答
- 要求引用具体章节
- 复杂问题分步骤解答
5. 部署与监控
5.1 生产级部署
建议的部署架构:
code复制前端 → FastAPI服务 → 问答系统 → 向量数据库
↘ 缓存层
↘ 日志系统
关键实现代码:
python复制from fastapi import FastAPI
app = FastAPI()
@app.post("/ask")
async def ask_question(question: str):
result = qa_chain({"query": question})
return {
"answer": result["result"],
"sources": [doc.metadata for doc in result["source_documents"]]
}
5.2 效果监控指标
我们建立了以下评估体系:
- 检索相关率:返回文档与问题的相关性
- 回答准确率:人工评估回答正确性
- 拒答率:模型拒绝回答的比例
- 响应时间:端到端延迟
在Kubernetes部署时,建议配置:
- 每个pod 2-4个CPU核心
- 4-8GB内存
- 自动扩缩容策略(CPU>70%时扩容)
6. 完整实现代码
以下是可直接运行的完整代码:
python复制# 环境配置
import os
os.environ["DASHSCOPE_API_KEY"] = "你的通义千问API_KEY"
# 依赖导入
from langchain.document_loaders import PyPDFLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.embeddings import HuggingFaceEmbeddings
from langchain.vectorstores import FAISS
from langchain.chains import RetrievalQA
from langchain_community.llms import Tongyi
# 知识库构建
def build_knowledge_base(file_path):
loader = PyPDFLoader(file_path)
pages = loader.load()
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=500,
chunk_overlap=100,
)
docs = text_splitter.split_documents(pages)
embeddings = HuggingFaceEmbeddings(model_name="BAAI/bge-small-zh")
vectorstore = FAISS.from_documents(docs, embeddings)
return vectorstore
# 问答系统初始化
def init_qa_system(vectorstore):
llm = Tongyi(model_name="qwen-plus", temperature=0.3)
retriever = vectorstore.as_retriever(search_kwargs={"k": 3})
qa = RetrievalQA.from_chain_type(
llm=llm,
chain_type="stuff",
retriever=retriever,
return_source_documents=True
)
return qa
# 示例使用
if __name__ == "__main__":
# 首次运行需构建知识库
# vectorstore = build_knowledge_base("manual.pdf")
# vectorstore.save_local("faiss_index")
# 加载已有知识库
embeddings = HuggingFaceEmbeddings(model_name="BAAI/bge-small-zh")
vectorstore = FAISS.load_local("faiss_index", embeddings)
qa = init_qa_system(vectorstore)
while True:
question = input("\n请输入问题(输入q退出): ")
if question.lower() == 'q':
break
result = qa({"query": question})
print("\n回答:", result["result"])
print("\n来源:")
for doc in result["source_documents"]:
print(f"- {doc.metadata.get('source', '未知')} 第{doc.metadata.get('page', '?')}页")
7. 常见问题排查
在实际部署中,我们遇到过以下典型问题及解决方案:
- 检索结果不相关
- 检查分块大小是否合适
- 尝试不同的嵌入模型(如m3e-large)
- 添加元数据过滤(如文档类型、更新时间)
- 回答质量不稳定
- 调整temperature参数(建议0.2-0.5)
- 优化prompt模板
- 启用logprobs检查模型置信度
- 处理长文档超时
- 增加分块重叠区域
- 实现流式处理
- 使用更高效的向量数据库(如Milvus)
- 中文编码问题
- 确保所有文件使用UTF-8编码
- 在加载器中指定编码格式
python复制loader = PyPDFLoader("file.pdf", encoding="utf-8")
8. 进阶扩展方向
基于这个基础框架,可以考虑以下扩展:
- 多模态支持:
- 添加图像理解能力
- 支持表格数据检索
- 集成语音输入输出
- 智能体架构:
python复制from langchain.agents import initialize_agent
tools = [retriever_tool]
agent = initialize_agent(
tools, llm, agent="zero-shot-react-description", verbose=True
)
- 持续学习机制:
- 用户反馈收集
- 自动标注系统
- 增量索引更新
在最近的一个项目中,我们通过添加用户反馈循环,系统准确率每周能提升2-3个百分点。具体做法是将用户标记的错误回答存入审核队列,经人工确认后更新到知识库。