构建本地知识库:基于 LangChain 和 Ollama 的 RAG 实现教程
简介
在这个教程中,我们将学习如何构建一个本地运行的知识库系统,它能够让用户上传 PDF 或 TXT 文档,并通过自然语言与文档内容进行交互。这个系统使用了 RAG(检索增强生成)技术,结合了 LangChain、Ollama 和 Streamlit 等现代工具,实现了一个完整的本地知识库解决方案。
技术栈
- LangChain: 用于构建 LLM 应用的框架
- Ollama: 本地运行的 LLM 模型服务
- FastEmbed: 高效的文本嵌入模型
- Chroma: 向量数据库
- Streamlit: Web 界面框架
系统架构
该系统主要包含以下几个核心组件:
- 文档处理器: 支持 PDF 和 TXT 文件的加载和处理
- 文本分割器: 将文档分割成适当大小的块
- 向量存储: 使用 Chroma 存储文档块的向量表示
- 检索器: 基于相似度搜索相关文档块
- LLM 接口: 与 Ollama 模型交互生成回答
- Web 界面: 用户友好的交互界面
实现步骤
1. 环境配置
首先,创建一个新的 Python 环境并安装必要的依赖:
# 创建虚拟环境
python -m venv .venv
# 激活环境
# Windows:
.venv\Scripts\activate
# Linux/Mac:
source .venv/bin/activate
# 安装依赖
pip install -r requirements-windows.txt
requirements-windows.txt
aiohappyeyeballs>=2.4.0
aiohttp>=3.10.5
aiosignal>=1.3.1
altair>=5.4.1
annotated-types>=0.7.0
anyio>=4.6.0
asgiref>=3.8.1
attrs>=24.2.0
backoff>=1.11.1
bcrypt>=4.2.0
blinker>=1.8.2
build>=1.2.2
cachetools>=5.5.0
certifi>=2024.8.30
charset-normalizer>=3.3.2
chroma-hnswlib>=0.7.6
chromadb>=0.5.7
click>=8.1.7
coloredlogs>=15.0.1
dataclasses-json>=0.6.7
Deprecated>=1.2.14
distlib>=0.3.8
durationpy>=0.7
fastapi>=0.115.0
fastembed>=0.3.6
filelock>=3.16.1
flatbuffers>=24.3.25
frozenlist>=1.4.1
fsspec>=2024.9.0
gitdb>=4.0.11
GitPython>=3.1.43
google-auth>=2.35.0
googleapis-common-protos>=1.65.0
grpcio>=1.66.1
h11>=0.14.0
httpcore>=1.0.5
httptools>=0.6.1
httpx>=0.27.2
huggingface-hub>=0.25.0
humanfriendly>=10.0
idna>=3.10
importlib_metadata>=8.4.0
importlib_resources>=6.4.5
Jinja2>=3.1.4
jsonpatch>=1.33
jsonpointer>=3.0.0
jsonschema>=4.23.0
jsonschema-specifications>=2023.12.1
langchain>=0.1.12
langchain-community>=0.0.27
langchain-core>=0.1.30
langsmith>=0.1.27
markdown-it-py>=3.0.0
MarkupSafe>=2.1.5
marshmallow>=3.20.2
mdurl>=0.1.2
monotonic>=1.6
mpmath>=1.3.0
multidict>=6.0.5
mypy-extensions>=1.0.0
numpy>=1.26.4
oauthlib>=3.2.2
onnxruntime>=1.17.1
openai>=1.12.0
opentelemetry-api>=1.23.0
opentelemetry-instrumentation>=0.44b0
opentelemetry-semantic-conventions>=0.44b0
overrides>=7.7.0
packaging>=23.2
pandas>=2.2.0
Pillow>=10.2.0
pip>=24.0
platformdirs>=4.2.0
posthog>=3.4.1
protobuf>=4.25.3
pulsar-client>=3.4.0
pyarrow>=15.0.0
pydantic>=2.6.1
pydantic_core>=2.16.2
pydeck>=0.8.1b0
Pygments>=2.17.2
PyJWT>=2.8.0
pyparsing>=3.1.1
python-dateutil>=2.8.2
python-dotenv>=1.0.1
pytz>=2024.1
PyYAML>=6.0.1
referencing>=0.33.0
regex>=2023.12.25
requests>=2.31.0
requests-oauthlib>=1.3.1
rich>=13.7.0
rpds-py>=0.17.1
rsa>=4.9
setuptools>=69.0.3
six>=1.16.0
smmap>=5.0.1
sniffio>=1.3.0
SQLAlchemy>=2.0.25
starlette>=0.35.1
streamlit>=1.31.0
streamlit-chat>=0.1.1
sympy>=1.12
tenacity>=8.2.3
tiktoken>=0.6.0
tokenizers>=0.15.2
toml>=0.10.2
toolz>=0.12.1
tornado>=6.4
tqdm>=4.66.2
typing_extensions>=4.9.0
typing-inspect>=0.9.0
tzdata>=2024.1
tzlocal>=5.2
urllib3>=2.2.0
validators>=0.22.0
watchdog>=3.0.0
websockets>=12.0
wheel>=0.42.0
wrapt>=1.16.0
yarl>=1.9.4
zipp>=3.17.0
2. RAG 实现(rag.py)
RAG 实现的核心是 ChatPDF
类,它处理文档的加载、向量化和查询:
class ChatPDF:
def __init__(self, llm_model: str = "qwen2.5"):
# 初始化 LLM 模型
self.model = ChatOllama(model=llm_model)
# 配置文本分割器
self.text_splitter = RecursiveCharacterTextSplitter(
chunk_size=1024,
chunk_overlap=100
)
# 设置提示模板
self.prompt = ChatPromptTemplate([
("system", "You are a helpful assistant that can answer questions about the PDF document that uploaded by the user."),
("human", "Here is the document pieces: {context}\nQuestion: {question}")
])
def ingest(self, file_path: str):
# 加载文档
if file_path.lower().endswith('.pdf'):
docs = PyPDFLoader(file_path=file_path).load()
elif file_path.lower().endswith('.txt'):
docs = TextLoader(file_path=file_path).load()
# 分割文档
chunks = self.text_splitter.split_documents(docs)
chunks = filter_complex_metadata(chunks)
# 创建向量存储
self.vector_store = Chroma.from_documents(
documents=chunks,
embedding=FastEmbedEmbeddings(),
persist_directory="./chroma_db"
)
self.vector_store.persist()
def ask(self, query: str):
# 初始化检索器
if not self.vector_store:
self.vector_store = Chroma(
persist_directory="./chroma_db",
embedding=FastEmbedEmbeddings()
)
# 配置检索参数
self.retriever = self.vector_store.as_retriever(
search_type="similarity_score_threshold",
search_kwargs={"k": 10, "score_threshold": 0.0}
)
# 构建查询链
self.chain = (
{"context": self.retriever, "question": RunnablePassthrough()}
| self.prompt
| self.model
| StrOutputParser()
)
return self.chain.invoke(query)
3. Web 界面实现(app.py)
使用 Streamlit 构建用户界面:
def page():
if len(st.session_state) == 0:
st.session_state["messages"] = []
st.session_state["assistant"] = ChatPDF()
st.header("本地知识库")
# 文件上传组件
st.subheader("上传文件 PDF/TXT")
st.file_uploader(
"上传文件",
type=["pdf", "txt"],
key="file_uploader",
on_change=read_and_save_file,
accept_multiple_files=True
)
# 显示对话历史
display_messages()
# 用户输入框
st.text_input("Message", key="user_input", on_change=process_input)
核心功能说明
1. 文档处理
系统支持 PDF 和 TXT 格式的文档处理。文档首先被加载,然后使用 RecursiveCharacterTextSplitter 分割成较小的块,每个块大小为 1024 个字符,相邻块之间有 100 个字符的重叠,以保持上下文的连贯性。
2. 向量化和存储
使用 FastEmbed 模型将文本块转换为向量表示,并存储在 Chroma 向量数据库中。Chroma 提供了高效的相似度搜索功能,使我们能够快速找到与用户查询最相关的文档片段。
3. 检索增强生成
当用户提出问题时,系统会:
- 搜索最相关的文档片段(检索 top-10 个相关度最高的片段)
- 将这些片段与用户的问题组合成提示
- 使用 Ollama 模型生成回答
4. 用户界面
Streamlit 提供了简洁的用户界面,包括:
- 文件上传功能
- 聊天历史显示
- 实时对话交互
运行项目
- 确保已安装 Ollama 并运行了所需的模型(默认使用 qwen2.5)
- 运行 Web 应用:
streamlit run app.py
最佳实践
-
文档预处理:
- 合理设置文档分块大小
- 保持适当的块重叠以维持上下文
-
检索优化:
- 调整检索数量(k值)和相似度阈值
- 根据实际需求平衡召回率和精确度
-
提示工程:
- 使用清晰的系统提示
- 在提示中包含充分的上下文信息
扩展建议
- 添加文档管理功能(删除、更新)
- 实现多种文档格式支持
- 添加文档预处理选项
- 支持多语言处理
- 添加对话历史持久化
结论
这个项目展示了如何使用现代工具构建一个实用的本地知识库系统。通过结合 RAG 技术和本地 LLM,我们能够创建一个既保护隐私又高效的文档问答系统。这个实现方案可以作为构建更复杂知识库系统的基础。
标签:RAG,self,LangChain,st,文档,file,path,Ollama From: https://blog.csdn.net/hzether/article/details/145157864