# 如何使用 Python + LangChain 构建基于 LLM 的本地知识库问答系统
## 背景介绍
很多公司都有大量的内部文档:技术手册、FAQ、产品说明之类的。问题是,用传统关键词搜索来查这些资料,效果通常不怎么样。用户搜”电脑开不了机”,系统可能返回”显示器不显示”——虽然有点关系,但不完全是一回事。
大语言模型出现以后,情况变了。RAG(检索增强生成)这个架构,能让 AI 在回答问题之前先从你的知识库里查相关资料,然后把查到的内容和问题一起发给 LLM,让 AI 基于真实文档来生成答案。这样既保留了 LLM 的语言能力,又解决了它”胡编乱造”的问题,还能让它学到你的私有知识。
LangChain 是现在最火的 LLM 开发框架,封装了各种工具,做 RAG 系统特别方便。这篇文章就手把手教你怎么用 Python、LangChain、开源 Embedding 模型和向量数据库,在本地搭一个完整的知识库问答系统。
## 问题描述
实际落地的时候,有几个坑需要填:
**数据安全**——很多公司的文档是敏感的,不能传上云。必须整个系统跑在本地,所有数据不出门。
**检索不准**——关键词匹配解决不了同义词、多义词的问题。用户说”打印机不工作”,你得能返回”复印机故障”相关内容。
**答案可信度**——直接让 LLM 回答专业问题,它可能会瞎编。通过 RAG 架构,让它根据你的文档来回答,就能避免这个问题。
**成本**——用 OpenAI API 方便是方便,但量大起来费用惊人。得支持本地部署的开源模型。
## 详细步骤
整个系统搭建分这几步:
**环境准备**
Python 3.8 以上,装一堆包:langchain、langchain-community、sentence-transformers、chromadb、pypdf。具体命令如下:
“`bash
pip install langchain langchain-community langchain-openai \
sentence-transformers chromadb pypdf
“`
**文档加载与预处理**
LangChain 带的加载器支持 PDF、Word、Markdown、TXT 各种格式。加载完了要把长文档切成小块——这个叫文本分块(Text Chunking)。一般设 chunk_size=500、chunk_overlap=50。分块大小很关键,太大了检索不精准,太小了上下文不够。
**文本向量化和存储**
用预训练的中文 Sentence Transformer 模型(shibing624/text2vec-base-chinese)把文本块变成向量,然后存到 ChromaDB 里。ChromaDB 是个轻量级向量数据库,直接存本地文件,很适合本地部署。
**构建检索和问答链**
用 LangChain 的 RetrievalQA 链把检索和 LLM 连起来。用户问问题 → 检索器从向量数据库找相关文档 → 把问题和文档一起发给 LLM → LLM 生成答案。
**LLM 选择**
可以用本地部署的开源模型(Qwen、ChatGLM 之类的),也可以用 OpenAI API。后面示例用 OpenAI,但换成开源模型只需要改几行配置。
## 完整代码示例
下面是完整代码,直接能跑:
“`python
# 导入必要的库
import os
from langchain_community.document_loaders import PyPDFLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_community.embeddings import HuggingFaceEmbeddings
from langchain_community.vectorstores import Chroma
from langchain_openai import ChatOpenAI
from langchain.chains import RetrievalQA
# 配置 OpenAI API(请替换为您的实际 API Key)
os.environ[“OPENAI_API_KEY”] = “your-api-key-here”
class LocalKnowledgeBaseQA:
def __init__(self, pdf_path: str, persist_directory: str = “./chroma_db”):
self.pdf_path = pdf_path
self.persist_directory = persist_directory
self.embeddings = None
self.vectorstore = None
self.qa_chain = None
def load_documents(self):
“””加载 PDF 文档”””
print(“正在加载文档…”)
loader = PyPDFLoader(self.pdf_path)
documents = loader.load()
print(f”成功加载 {len(documents)} 页文档”)
return documents
def split_documents(self, documents):
“””将文档分割成文本块”””
print(“正在分割文档…”)
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=500,
chunk_overlap=50,
length_function=len,
)
texts = text_splitter.split_documents(documents)
print(f”分割完成,共 {len(texts)} 个文本块”)
return texts
def create_vectorstore(self, texts):
“””创建向量数据库”””
print(“正在创建向量数据库…”)
# 使用中文 Embedding 模型
self.embeddings = HuggingFaceEmbeddings(
model_name=”shibing624/text2vec-base-chinese”,
model_kwargs={‘device’: ‘cpu’}
)
# 检查是否已有持久化的向量数据库
if os.path.exists(self.persist_directory):
print(“加载已有向量数据库…”)
self.vectorstore = Chroma(
persist_directory=self.persist_directory,
embedding_function=self.embeddings
)
else:
print(“创建新的向量数据库…”)
self.vectorstore = Chroma.from_documents(
documents=texts,
embedding=self.embeddings,
persist_directory=self.persist_directory
)
print(“向量数据库创建完成”)
return self.vectorstore
def setup_qa_chain(self):
“””设置问答链”””
print(“正在初始化 LLM 和问答链…”)
# 初始化 LLM
llm = ChatOpenAI(
model_name=”gpt-3.5-turbo”,
temperature=0,
openai_api_key=os.environ[“OPENAI_API_KEY”]
)
# 创建检索器
retriever = self.vectorstore.as_retriever(
search_type=”similarity”,
search_kwargs={“k”: 3}
)
# 创建 RetrievalQA 链
self.qa_chain = RetrievalQA.from_chain_type(
llm=llm,
chain_type=”stuff”,
retriever=retriever,
return_source_documents=True
)
print(“问答系统初始化完成”)
def initialize(self):
“””初始化整个系统”””
documents = self.load_documents()
texts = self.split_documents(documents)
self.create_vectorstore(texts)
self.setup_qa_chain()
def ask(self, question: str):
“””问答接口”””
if not self.qa_chain:
raise ValueError(“请先调用 initialize() 初始化系统”)
result = self.qa_chain.invoke({“query”: question})
return {
“answer”: result[“result”],
“source_documents”: result[“source_documents”]
}
# 使用示例
if __name__ == “__main__”:
qa_system = LocalKnowledgeBaseQA(
pdf_path=”./knowledge_base/manual.pdf”,
persist_directory=”./chroma_db”
)
qa_system.initialize()
while True:
question = input(“\n请输入您的问题(输入 q 退出):”)
if question.lower() == ‘q’:
break
result = qa_system.ask(question)
print(“\n=== 回答 ===”)
print(result[“answer”])
print(“\n=== 参考来源 ===”)
for i, doc in enumerate(result[“source_documents”], 1):
print(f”{i}. {doc.page_content[:200]}…”)
“`
## 运行结果
跑起来是这样的:
**初始化时:**
系统加载 PDF,切成小文本块,用中文模型转成向量,存到 Chroma。文档大的话这个过程要几分钟。
**问答时:**
用户提问 → 系统把问题转成向量 → 在向量库里找最像的 3 个文档块 → 把问题和这些块发给 LLM → LLM 生成答案,同时给出参考来源。
比如问”怎么重置打印机?”,系统会找出知识库里相关的操作说明,生成清晰回答,最后标明这条回答参考了哪几页文档。
## 总结
这篇文章介绍了怎么用 Python 和 LangChain 在本地搭知识库问答系统。这个方案的好处:
**安全**——全程本地处理,不用把敏感资料传上网,满足合规要求。
**灵活**——支持各种开源模型(中文用 shibing624/text2vec-base-chinese,LLM 用 Qwen 或 ChatGLM),想换就换。
**省钱**——用开源模型不用付 API 费,大规模部署也不心疼。
**可扩展**——LangChain 组件很丰富,加聊天历史、支持多文档都很容易。
把代码复制下来改改路径就能跑起来,先跑通原型,再根据自己业务需求调整。RAG 是现在 LLM 落地的标配,学会这个,你就能把 AI 能力真正用到工作里去了。