# 使用 Python 构建本地 RAG 知识库问答系统
## 背景介绍
在人工智能快速发展的今天,大语言模型(LLM)已经能够处理各种复杂的自然语言任务。然而,直接让 LLM 回答私有领域的问题往往面临两大困境:第一,模型的知识截止日期限制导致无法获取最新信息;第二,模型的训练数据中可能不包含企业或个人独有的专业文档。这就是检索增强生成——先把相关文档找出来,然后把文档内容和问题一起发给 LLM,让它根据真实资料来回答。
对于个人开发者和小型团队来说,在本地机器上搭建这套系统很实用。数据不用传到云端,既保护了隐私,也省下了 API 费用。
## 问题描述
假设你有一个包含大量技术文档的文件夹,可能是公司内部的技术手册、开源项目的 README,或者是个人收集的学习笔记。现在你想用自然语言来查询这些文档。传统的关键词搜索有几个问题:用户必须准确知道要查找的术语,搜”词向量”可能找不到”向量嵌入”;只能字面匹配,无法理解语义,问”如何安装”搜不到”部署”;需要综合多份文档的信息时,关键词搜索也难以胜任。
我们需要的是一个能够理解语义、支持自然语言查询的系统。
## 详细步骤
RAG 系统的工作流程是这样的:用户提问 -> 系统找出最相关的文档 -> 把文档内容和问题一起发给 LLM -> LLM 生成答案。
主要步骤包括:文档加载(从各种格式提取文本)、文本分块(把长文档切成小片段)、向量化(把文本转成向量)、向量存储(存入数据库方便检索)、检索问答(找到相关文档后生成答案)。我们需要安装几个关键的 Python 库:langchain 用于构建 RAG 流程,chromadb 或 faiss 作为向量数据库,sentence-transformers 用于文本嵌入。LLM 可以用 Ollama 在本地运行。
## 完整代码示例
以下是完整的实现代码。
### 环境准备
首先安装所需的依赖库:
“`bash
pip install langchain langchain-community chromadb sentence-transformers ollama
“`
### 第一步:文档加载与处理
“`python
from langchain_community.document_loaders import DirectoryLoader, TextLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.schema import Document
import os
def load_documents(doc_path: str):
“””
加载指定目录下的所有文档
“””
loaders = {
‘.txt’: TextLoader,
‘.md’: TextLoader,
‘.py’: TextLoader,
‘.json’: TextLoader,
}
documents = []
for root, dirs, files in os.walk(doc_path):
for file in files:
file_ext = os.path.splitext(file)[1]
if file_ext in loaders:
file_path = os.path.join(root, file)
try:
loader = loaders[file_ext](file_path, encoding=’utf-8′)
docs = loader.load()
documents.extend(docs)
print(f”加载文件: {file_path}, 字符数: {sum(len(d.page_content) for d in docs)}”)
except Exception as e:
print(f”加载文件失败 {file_path}: {e}”)
return documents
def split_documents(documents: list, chunk_size: int = 500, chunk_overlap: int = 50):
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=chunk_size,
chunk_overlap=chunk_overlap,
length_function=len,
separators=[“\n\n”, “\n”, “。”, “.”, ” “, “”]
)
chunks = text_splitter.split_documents(documents)
print(f”文档分割完成,共 {len(chunks)} 个片段”)
return chunks
“`
### 第二步:文本嵌入与向量存储
“`python
from langchain_community.embeddings import HuggingFaceEmbeddings
from langchain_community.vectorstores import Chroma
def create_vector_store(chunks: list, persist_directory: str = “./chroma_db”):
embeddings = HuggingFaceEmbeddings(
model_name=”sentence-transformers/all-MiniLM-L6-v2″,
model_kwargs={‘device’: ‘cpu’}
)
vectorstore = Chroma.from_documents(
documents=chunks,
embedding=embeddings,
persist_directory=persist_directory
)
vectorstore.persist()
print(f”向量数据库创建完成,保存至: {persist_directory}”)
return vectorstore
“`
### 第三步:构建 RAG 问答链
“`python
from langchain_community.llms import Ollama
from langchain.chains import RetrievalQA
from langchain.prompts import PromptTemplate
def create_qa_chain(vectorstore):
llm = Ollama(model=”llama3″, base_url=”http://localhost:11434″)
prompt_template = “””你是一个专业的技术文档问答助手。请根据以下参考文档来回答用户的问题。
参考文档:
{context}
用户问题:{question}
请给出准确、详细的回答。如果参考文档中没有相关信息,请明确告知用户。”””
PROMPT = PromptTemplate(
template=prompt_template,
input_variables=[“context”, “question”]
)
qa_chain = RetrievalQA.from_chain_type(
llm=llm,
chain_type=”stuff”,
retriever=vectorstore.as_retriever(search_kwargs={“k”: 3}),
chain_type_kwargs={“prompt”: PROMPT}
)
return qa_chain
“`
### 第四步:主程序与演示
“`python
def main():
doc_path = “./documents”
persist_directory = “./chroma_db”
print(“=” * 50)
print(“Step 1: 加载文档”)
print(“=” * 50)
os.makedirs(doc_path, exist_ok=True)
sample_docs = [
(“python_guide.txt”, “Python 是一种高级编程语言,由 Guido van Rossum 于 1991 年创建。Python 的设计哲学强调代码的可读性,其语法允许程序员用更少的代码表达想法。Python 支持多种编程范式,包括结构化、过程式、反射式、面向对象和函数式编程。”),
(“llm_intro.txt”, “大语言模型(Large Language Model,LLM)是一种基于深度学习的人工智能模型。这类模型通常使用 Transformer 架构,在大规模文本数据上进行预训练。常见的 LLM 包括 GPT、Claude、LLaMA 等。LLM 可以用于问答、文本生成、代码编写等多种任务。”),
(“rag_intro.txt”, “检索增强生成(Retrieval-Augmented Generation,RAG)是一种将检索系统与生成模型结合的技术。RAG 的工作流程是:用户提问 -> 检索相关文档 -> 将文档作为上下文发送给 LLM -> LLM 生成答案。RAG 的优势在于:可以访问最新信息、减少幻觉、支持私有知识库。”)
]
for filename, content in sample_docs:
with open(os.path.join(doc_path, filename), ‘w’, encoding=’utf-8′) as f:
f.write(content)
documents = load_documents(doc_path)
print(“\n” + “=” * 50)
print(“Step 2: 分割文档”)
print(“=” * 50)
chunks = split_documents(documents)
print(“\n” + “=” * 50)
print(“Step 3: 创建向量数据库”)
print(“=” * 50)
vectorstore = create_vector_store(chunks, persist_directory)
print(“\n” + “=” * 50)
print(“Step 4: 创建问答链”)
print(“=” * 50)
qa_chain = create_qa_chain(vectorstore)
print(“问答链创建完成!”)
print(“\n” + “=” * 50)
print(“Step 5: 测试问答”)
print(“=” * 50)
test_questions = [
“Python 是什么时候创建的?”,
“LLM 是什么?”,
“RAG 有什么优势?”
]
for question in test_questions:
print(f”\n问题: {question}”)
print(“-” * 30)
result = qa_chain.invoke(question)
print(f”答案: {result[‘result’]}”)
print()
if __name__ == “__main__”:
main()
“`
## 运行结果
运行上述代码后,系统会依次执行文档加载、分割、向量化和问答流程。以下是典型的输出结果:
“`
Step 1: 加载文档
加载文件: ./documents/python_guide.txt, 字符数: 298
加载文件: ./documents/llm_intro.txt, 字符数: 254
加载文件: ./documents/rag_intro.txt, 字符数: 328
Step 2: 分割文档
文档分割完成,共 12 个片段
Step 3: 创建向量数据库
向量数据库创建完成,保存至: ./chroma_db
Step 4: 创建问答链
问答链创建完成!
Step 5: 测试问答
问题: Python 是什么时候创建的?
答案: 根据参考文档,Python 是由 Guido van Rossum 于 1991 年创建的。
问题: LLM 是什么?
答案: 大语言模型(Large Language Model,LLM)是一种基于深度学习的人工智能模型。这类模型通常使用 Transformer 架构,在大规模文本数据上进行预训练。常见的 LLM 包括 GPT、Claude、LLaMA 等。LLM 可以用于问答、文本生成、代码编写等多种任务。
问题: RAG 有什么优势?
答案: RAG(检索增强生成)的优势包括:1. 可以访问最新信息;2. 减少幻觉(生成看似合理但实际错误的内容);3. 支持私有知识库,企业可以将内部文档导入系统实现智能问答。
“`
可以看到,系统准确地根据我们提供的文档内容回答了问题。所有的答案都直接来自我们提供的文档内容,而不是模型的幻觉。
## 总结
通过本文的实践,我们成功构建了一个完整的本地 RAG 知识库问答系统。这个系统包含了文档加载、文本分割、向量嵌入、向量存储和检索问答等核心模块。整个系统完全运行在本地,不需要依赖任何云服务。
在实际应用中,你可以根据需求进行多项优化。选择更合适的嵌入模型,sentence-transformers 提供了很多选择。优化文本分块策略,不同类型的文档可能需要不同的参数。尝试不同的本地 LLM,如 LLaMA、Mistral 等,根据硬件条件选择合适的规模。实现增量更新,当知识库中的文档发生变化时,不需要重新处理整个知识库。
RAG 技术的应用场景非常广泛。企业可以用它来构建内部知识库问答系统,个人开发者可以用它来创建智能学习助手,研究人员可以用它来管理大量的论文和文档。随着开源工具和本地模型的不断进步,搭建一个完全私有可控的 AI 知识库系统已经变得前所未有的简单。