使用 LangChain 构建基于 RAG 的本地知识库问答系统

# 使用 LangChain 构建基于 RAG 的本地知识库问答系统

## 背景介绍

在企业日常运营中,我们积累了大量内部文档、技术手册、产品说明、会议纪要等非结构化数据。但当需要从中查找特定信息时,却只能在海量文件中进行繁琐的人工检索。传统的方式是通过关键词搜索或者人工查阅,效率低下,而且难以处理语义相关的查询。

检索增强生成(Retrieval Augmented Generation,RAG)是一种将外部知识库与大语言模型结合的技术架构。它能够根据用户的问题,从本地文档中检索相关内容,然后让 LLM 基于这些内容生成准确的回答。这种方式不仅解决了 LLM 知识过时的问题,还保证了回答的准确性和可追溯性。

LangChain 是目前最流行的 LLM 应用开发框架之一,提供了丰富的工具和组件,能够帮助开发者快速构建 RAG 系统。本文将详细介绍如何使用 LangChain 构建一个完整的本地知识库问答系统,从环境准备到代码实现,再到实际运行效果,带你一步步完成这个实用的人工智能应用。

## 问题描述

在构建本地知识库问答系统时,开发者通常会遇到以下几个核心挑战:

**文档处理**是第一个难题。不同格式的文档(如 PDF、Word、TXT、Markdown 等)需要统一转换为 LLM 能够处理的文本格式。每个文档格式都有其特殊的解析逻辑,比如 PDF 需要处理文本流和布局,Word 需要解析段落和表格结构。

**向量存储**是第二个挑战。如何将文本转换为向量并高效存储,以便后续进行相似度检索。常见的向量数据库包括 Chroma、FAISS、Milvus、Pinecone 等,每种都有自己的适用场景和优缺点。

**检索精度**也很关键。即使是语义相近的查询,也可能返回不相关的结果。如何设计合适的分块策略(Chunking Strategy)和检索策略,直接影响到最终回答的质量。

**系统集成**是最后一个环节。如何将检索到的内容与 LLM 进行有效结合,生成流畅自然的回答,同时提供引用来源,方便用户验证。

本文将通过一个完整的代码示例,展示如何解决这些问题。我们将使用 LangChain + Chroma + OpenAI 的技术栈,构建一个可运行的本地知识库问答系统。

## 详细步骤

### 第一步:环境准备

首先,我们需要安装必要的 Python 包:

“`bash
pip install langchain langchain-openai langchain-community langchain-chroma pypdf python-docx tqdm
“`

同时需要设置 OpenAI API 密钥:

“`bash
export OPENAI_API_KEY=”your-api-key”
“`

### 第二步:文档加载器实现

LangChain 提供了丰富的文档加载器,支持多种格式。我们创建一个工具类来处理不同类型的文档:

“`python
from langchain_community.document_loaders import (
PyPDFLoader,
TextLoader,
MarkdownLoader,
Docx2txtLoader
)
from langchain.schema import Document
from pathlib import Path
import os

class DocumentLoader:
“””文档加载器,支持多种格式”””

def __init__(self, chunk_size: int = 500, chunk_overlap: int = 50):
self.chunk_size = chunk_size
self.chunk_overlap = chunk_overlap

def load_directory(self, directory_path: str):
“””加载目录下的所有文档”””
documents = []
path = Path(directory_path)

for file_path in path.rglob(*):
if file_path.is_file():
docs = self._load_file(str(file_path))
documents.extend(docs)

return documents

def _load_file(self, file_path: str):
“””根据文件类型选择合适的加载器”””
ext = Path(file_path).suffix.lower()

try:
if ext == .pdf:
loader = PyPDFLoader(file_path)
elif ext == .txt:
loader = TextLoader(file_path, encoding=utf-8)
elif ext == .md:
loader = MarkdownLoader(file_path)
elif ext in [.docx, .doc]:
loader = Docx2txtLoader(file_path)
else:
print(f”不支持的文件格式: {ext}”)
return []

return loader.load()
except Exception as e:
print(f”加载文件失败 {file_path}: {e}”)
return []
“`

### 第三步:文本分块处理

将长文档分割成较小的文本块是 RAG 系统的关键步骤。合适的分块大小能够平衡检索精度和上下文完整性:

“`python
from langchain.text_splitter import RecursiveCharacterTextSplitter

class TextChunker:
“””文本分块器”””

def __init__(self, chunk_size: int = 500, chunk_overlap: int = 50):
self.splitter = RecursiveCharacterTextSplitter(
chunk_size=chunk_size,
chunk_overlap=chunk_overlap,
length_function=len,
separators=[“\n\n”, “\n”, “。”, “.”, ” “, “”]
)

def split_documents(self, documents):
“””分割文档为文本块”””
return self.splitter.split_documents(documents)

def split_text(self, text: str):
“””分割文本为文本块”””
return self.splitter.split_text(text)
“`

### 第四步:向量存储与检索

使用 Chroma 作为向量数据库,它是轻量级且易于部署的选择:

“`python
from langchain_chroma import Chroma
from langchain_openai import OpenAIEmbeddings

class VectorStore:
“””向量存储管理”””

def __init__(self, persist_directory: str = “./chroma_db”):
self.persist_directory = persist_directory
self.embeddings = OpenAIEmbeddings(model=”text-embedding-3-small”)
self.vectorstore = None

def create_vectorstore(self, documents):
“””从文档创建向量存储”””
self.vectorstore = Chroma.from_documents(
documents=documents,
embedding=self.embeddings,
persist_directory=self.persist_directory
)
return self.vectorstore

def load_vectorstore(self):
“””加载已存在的向量存储”””
self.vectorstore = Chroma(
persist_directory=self.persist_directory,
embedding_function=self.embeddings
)
return self.vectorstore

def similarity_search(self, query: str, k: int = 4):
“””相似度搜索”””
if not self.vectorstore:
raise ValueError(“向量存储未初始化”)
return self.vectorstore.similarity_search(query, k=k)

def similarity_search_with_score(self, query: str, k: int = 4):
“””带相似度分数的搜索”””
if not self.vectorstore:
raise ValueError(“向量存储未初始化”)
return self.vectorstore.similarity_search_with_score(query, k=k)
“`

### 第五步:RAG 问答链构建

将检索和生成结合在一起,形成完整的问答系统:

“`python
from langchain import hub
from langchain_openai import ChatOpenAI
from langchain_core.runnables import RunnablePassthrough
from langchain_core.output_parsers import StrOutputParser

class RAGQASystem:
“””RAG 问答系统”””

def __init__(self, vectorstore, model_name: str = “gpt-3.5-turbo”):
self.vectorstore = vectorstore
self.llm = ChatOpenAI(model=model_name, temperature=0)

# 定义提示模板
self.prompt = hub.pull(“rlang/rag-prompt-mini-chinese”)

# 构建问答链
self.qa_chain = (
{“context”: self._format_docs, “question”: RunnablePassthrough()}
| self.prompt
| self.llm
| StrOutputParser()
)

def _format_docs(self, docs):
“””格式化检索到的文档”””
return “\n\n”.join(doc.page_content for doc in docs)

def ask(self, question: str):
“””提问并获取回答”””
# 检索相关文档
relevant_docs = self.vectorstore.similarity_search(question, k=4)

# 生成回答
answer = self.qa_chain.invoke(question)

return {
“answer”: answer,
“sources”: [doc.metadata for doc in relevant_docs],
“contexts”: [doc.page_content[:200] + “…” for doc in relevant_docs]
}
“`

### 第六步:主程序整合

将所有组件整合在一起,形成完整的应用:

“`python
import os
os.environ[“OPENAI_API_KEY”] = “your-api-key”

def main():
# 初始化各组件
loader = DocumentLoader(chunk_size=500, chunk_overlap=50)
chunker = TextChunker(chunk_size=500, chunk_overlap=50)
vector_store = VectorStore(persist_directory=”./knowledge_base”)

# 加载文档
print(“正在加载文档…”)
documents = loader.load_directory(“./documents”)
print(f”共加载 {len(documents)} 个文档”)

# 分块处理
print(“正在处理文本块…”)
chunks = chunker.split_documents(documents)
print(f”共生成 {len(chunks)} 个文本块”)

# 创建向量存储
print(“正在创建向量存储…”)
vectorstore = vector_store.create_vectorstore(chunks)
print(“向量存储创建完成”)

# 构建问答系统
print(“初始化问答系统…”)
qa_system = RAGQASystem(vectorstore)

# 交互式问答
print(“\n知识库问答系统已就绪!输入问题开始查询,输入 quit 退出。”)
while True:
question = input(“\n你: “).strip()
if question.lower() == quit:
break

result = qa_system.ask(question)
print(f”\n回答: {result[answer]}”)
print(f”\n参考来源:”)
for i, source in enumerate(result[sources], 1):
print(f” {i}. {source}”)

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

## 运行结果

假设我们在 documents 文件夹中放置了一些技术文档,运行程序后效果如下:

“`
正在加载文档…
共加载 5 个文档
正在处理文本块…
共生成 128 个文本块
正在创建向量存储…
向量存储创建完成
初始化问答系统…

知识库问答系统已就绪!输入问题开始查询,输入 quit 退出。

你: 如何安装 Python 包?

回答: 可以使用 pip 来安装 Python 包。基本命令是 pip install 包名。例如,要安装 requests 包,只需运行 pip install requests。如果需要安装特定版本,可以使用 pip install 包名==版本号。建议使用虚拟环境来管理项目依赖,避免全局安装导致的版本冲突。

参考来源:
1. {source: documents/python_tutorial.md}
2. {source: documents/python_tutorial.md}
3. {source: documents/best_practices.md}
4. {source: documents/python_tutorial.md}
“`

可以看到,系统成功从文档中检索到了相关内容,并生成了准确、有用的回答。每个回答都附带参考来源,方便用户进行溯源和验证。

## 总结

本文详细介绍了如何使用 LangChain 构建基于 RAG 的本地知识库问答系统。从环境准备开始,逐步实现了文档加载、文本分块、向量存储和检索增强生成等核心功能。

通过这个系统,用户可以快速构建自己的知识库,实现基于私有数据的智能问答。与传统的关键词搜索相比,RAG 系统能够理解语义,提供更加准确和上下文相关的回答。

在实际应用中,还可以根据具体需求进行优化:选择合适的嵌入模型、调整分块大小和重叠度、实现混合检索策略、添加对话历史记录等。LangChain 提供了丰富的组件和灵活的接口,开发者可以根据场景进行定制和扩展。

RAG 技术正在成为企业级 AI 应用的标准架构,掌握这些核心技术将为你的 AI 开发之路打下坚实的基础。

暂无评论

发送评论 编辑评论


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