使用 LangChain + ChromaDB 构建本地 RAG 知识库问答系统

# 使用 LangChain + ChromaDB 构建本地 RAG 知识库问答系统

## 背景介绍

企业日常运营中会产生大量的内部文档、技术手册、会议记录。当员工需要从这些海量文档中查找特定信息时,传统的关键词搜索效果往往不理想——它无法理解查询的语义意图,也无法处理自然语言提问。

检索增强生成(Retrieval-Augmented Generation,RAG)技术解决了这个问题。RAG 将大语言模型的语义理解能力与向量检索的精确匹配能力结合起来,用户可以用自然语言向知识库提问,获得准确、相关的回答。

本文手把手教你如何在本地搭建一个完整的 RAG 问答系统。我们使用 LangChain 作为应用框架,ChromaDB 作为向量数据库,OpenAI 的 GPT 模型作为生成引擎。整个系统可以在本地运行,数据隐私有保障。

## 问题描述

企业在构建 RAG 系统时通常面临几个实际困难:

数据导入很繁琐。不同的文档格式(PDF、Word、Markdown)需要不同的解析方式,处理不当会导致信息丢失。检索效果不稳定,简单的向量相似度搜索可能返回与问题相关但不足以回答的内容。系统集成复杂,检索、生成、提示词模板等多个组件串联起来需要不少胶水代码。

本文通过完整的代码示例演示如何解决这些问题。我们构建一个本地知识库问答系统,支持上传文档、向量化存储、自然语言提问等功能。

## 详细步骤

### 环境准备

安装必要的 Python 依赖库:

“`bash
pip install langchain langchain-openai chromadb pypdf python-docx tiktoken
“`

这些库分别提供:langchain 核心框架、OpenAI API 集成、向量数据库支持、PDF 解析、Word 解析、文本分词功能。

### 项目结构

创建以下项目结构:

“`
rag_project/
├── main.py # 主程序入口
├── ingest.py # 文档处理和导入
├── query.py # 问答查询
├── requirements.txt # 依赖列表
└──data/ # 文档存储目录
“`

### 核心代码实现

#### 1. 文档加载与处理(ingest.py)

“`python
from langchain_community.document_loaders import (
PyPDFLoader,
TextLoader,
Docx2txtLoader
)
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_community.vectorstores import Chroma
from langchain_openai import OpenAIEmbeddings
import os

class DocumentIngester:
def __init__(self, persist_directory=”./chroma_db”):
self.persist_directory = persist_directory
self.embeddings = OpenAIEmbeddings(
model=”text-embedding-3-small”
)
self.text_splitter = RecursiveCharacterTextSplitter(
chunk_size=1000,
chunk_overlap=200,
length_function=len,
)

def load_document(self, file_path):
“””根据文件类型选择对应的加载器”””
ext = os.path.splitext(file_path)[1].lower()

if ext == ‘.pdf’:
loader = PyPDFLoader(file_path)
elif ext == ‘.docx’:
loader = Docx2txtLoader(file_path)
elif ext == ‘.txt’:
loader = TextLoader(file_path, encoding=’utf-8′)
else:
raise ValueError(f”不支持的文件类型: {ext}”)

return loader.load()

def process_documents(self, file_paths):
“””处理多个文档并返回分块后的文本”””
documents = []
for file_path in file_paths:
docs = self.load_document(file_path)
documents.extend(docs)

# 文本分块
texts = self.text_splitter.split_documents(documents)
return texts

def create_vectorstore(self, texts, collection_name=”knowledge_base”):
“””创建向量数据库”””
vectorstore = Chroma.from_documents(
documents=texts,
embedding=self.embeddings,
persist_directory=self.persist_directory,
collection_name=collection_name
)
return vectorstore
“`

#### 2. 问答系统核心(query.py)

“`python
from langchain_openai import ChatOpenAI
from langchain.prompts import PromptTemplate
from langchain.chains import RetrievalQA
from langchain_community.vectorstores import Chroma
from langchain_openai import OpenAIEmbeddings

class RAGQuerySystem:
def __init__(self, persist_directory=”./chroma_db”):
self.embeddings = OpenAIEmbeddings(
model=”text-embedding-3-small”
)
self.llm = ChatOpenAI(
model=”gpt-3.5-turbo”,
temperature=0
)

# 加载向量数据库
self.vectorstore = Chroma(
persist_directory=persist_directory,
embedding_function=self.embeddings
)

# 定义提示词模板
self.prompt_template = “””你是一个专业的技术文档问答助手。请根据以下参考文档回答用户的问题。

参考文档:
{context}

用户问题:{question}

要求:
1. 只根据提供的参考文档内容回答,不要编造信息
2. 如果参考文档中没有相关信息,请明确告知用户
3. 回答要准确、简洁、有条理

回答:”””

self.prompt = PromptTemplate(
template=self.prompt_template,
input_variables=[“context”, “question”]
)

# 创建问答链
self.qa_chain = RetrievalQA.from_chain_type(
llm=self.llm,
chain_type=”stuff”,
retriever=self.vectorstore.as_retriever(
search_kwargs={“k”: 3}
),
chain_type_kwargs={“prompt”: self.prompt}
)

def query(self, question):
“””执行问答查询”””
result = self.qa_chain.invoke({“query”: question})
return result[“result”]
“`

#### 3. 主程序(main.py)

“`python
from ingest import DocumentIngester
from query import RAGQuerySystem
import os

def main():
# 初始化
data_dir = “./data”
persist_dir = “./chroma_db”

# 步骤1:导入文档(首次运行时执行)
if not os.path.exists(persist_dir):
print(“正在导入文档…”)
ingester = DocumentIngester(persist_directory=persist_dir)

# 扫描数据目录下的所有文档
file_paths = []
for root, dirs, files in os.walk(data_dir):
for file in files:
file_path = os.path.join(root, file)
if file.endswith((‘.pdf’, ‘.docx’, ‘.txt’)):
file_paths.append(file_path)

if file_paths:
texts = ingester.process_documents(file_paths)
print(f”成功处理 {len(texts)} 个文档片段”)

vectorstore = ingester.create_vectorstore(texts)
print(“向量数据库创建完成”)
else:
print(“未找到文档文件”)
return

# 步骤2:启动问答系统
print(“\n=== 本地 RAG 知识库问答系统 ===”)
print(“输入问题进行查询,输入 ‘quit’ 退出\n”)

qa_system = RAGQuerySystem(persist_directory=persist_dir)

while True:
question = input(“请输入问题: “)
if question.lower() == ‘quit’:
break

answer = qa_system.query(question)
print(f”\n回答: {answer}\n”)
print(“-” * 50)

if __name__ == “__main__”:
main()
“`

## 运行结果

运行主程序后,系统首先检查向量数据库是否已存在。如果不存在,会自动扫描 data 目录下的文档并创建向量索引。初始化完成后,系统进入交互模式。

“`
$ python main.py
正在导入文档…
成功处理 45 个文档片段
向量数据库创建完成

=== 本地 RAG 知识库问答系统 ===
输入问题进行查询,输入 ‘quit’ 退出

请输入问题: 如何安装 Python 环境?

回答: 根据文档,安装 Python 环境的步骤如下:
1. 访问 python.org 下载最新版本的 Python
2. 运行安装程序,勾选 “Add Python to PATH” 选项
3. 打开终端,输入 python –version 验证安装

请输入问题: 项目的代码规范是什么?

回答: 根据技术规范文档,本项目采用以下代码规范:
– 遵循 PEP 8 Python 代码规范
– 使用 Black 进行代码格式化
– 使用 Flake8 进行代码检查
– 函数和类必须有完整的文档字符串

请输入问题: quit
“`

系统能够准确理解自然语言问题,从向量数据库中检索相关文档片段,生成准确、相关的回答。整个过程完全在本地执行,不需要将敏感文档上传到第三方服务。

## 总结

本文介绍了如何使用 LangChain 和 ChromaDB 在本地构建 RAG 知识库问答系统。通过这个系统,用户可以:

将各种格式的文档(PDF、Word、TXT)导入到向量数据库中,系统会自动进行文本提取和分块处理。使用语义搜索而不是关键词匹配,可以更准确地找到与问题相关的内容。基于检索到的内容生成自然语言回答大大提高问答的准确性。

这个架构有良好的可扩展性。可以更换不同的 embedding 模型,比如本地部署的 BGE,或者用开源的大语言模型(如 Llama 2)替代 OpenAI 的 API,真正实现完全本地化部署。

如果对这个系统感兴趣,可以尝试添加更多功能:支持更多文档格式、添加对话历史记录、实现多轮对话等。这些增强功能可以让系统更好地满足实际业务需求。

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇