Python + LangChain 环境搭建完全指南:从零构建本地 RAG 知识库(附 Ollama 本地模型集成)

🌸你好呀!我是 lbb小魔仙
🌟 感谢陪伴~ 小白博主在线求友
🌿 跟着小白学Linux/Java/Python
📖 专栏汇总:
《Linux》专栏 | 《Java》专栏 | 《Python》专栏

- [Python + LangChain 环境搭建完全指南:从零构建本地 RAG 知识库(附 Ollama 本地模型集成)](#Python + LangChain 环境搭建完全指南:从零构建本地 RAG 知识库(附 Ollama 本地模型集成))
-
- [1. 引言:什么是 RAG?为什么需要它?](#1. 引言:什么是 RAG?为什么需要它?)
- [2. 环境准备](#2. 环境准备)
-
- [2.1 系统与 Python 版本要求](#2.1 系统与 Python 版本要求)
- [2.2 安装 Ollama 本地模型服务](#2.2 安装 Ollama 本地模型服务)
- [2.3 创建 Python 虚拟环境](#2.3 创建 Python 虚拟环境)
- [3. 安装 LangChain 及相关依赖](#3. 安装 LangChain 及相关依赖)
-
- [3.1 核心包说明](#3.1 核心包说明)
- [3.2 安装命令(含国内镜像加速)](#3.2 安装命令(含国内镜像加速))
- [3.3 验证安装](#3.3 验证安装)
- [4. LangChain 基础:接入本地 Ollama 模型](#4. LangChain 基础:接入本地 Ollama 模型)
-
- [4.1 基础对话链](#4.1 基础对话链)
- [4.2 提示词模板(PromptTemplate)](#4.2 提示词模板(PromptTemplate))
- [4.3 对话记忆(Memory)](#4.3 对话记忆(Memory))
- [5. 构建 RAG 知识库](#5. 构建 RAG 知识库)
-
- [5.1 RAG 工作原理](#5.1 RAG 工作原理)
- [5.2 文档加载与分块](#5.2 文档加载与分块)
- [5.3 向量化与存储(ChromaDB)](#5.3 向量化与存储(ChromaDB))
- [5.4 检索链构建](#5.4 检索链构建)
- [5.5 完整 RAG 系统整合](#5.5 完整 RAG 系统整合)
- [6. 进阶:多格式文档支持](#6. 进阶:多格式文档支持)
- [7. 常见问题与解决方案](#7. 常见问题与解决方案)
-
- [问题一:`pip install chromadb` 安装报错(Windows)](#问题一:
pip install chromadb安装报错(Windows)) - [问题二:Embedding 向量化非常慢](#问题二:Embedding 向量化非常慢)
- [问题三:RAG 回答不准确或出现"幻觉"](#问题三:RAG 回答不准确或出现"幻觉")
- [问题四:`ImportError: cannot import name 'OllamaLLM' from 'langchain_community'`](#问题四:
ImportError: cannot import name 'OllamaLLM' from 'langchain_community')
- [问题一:`pip install chromadb` 安装报错(Windows)](#问题一:
- [8. 总结与拓展建议](#8. 总结与拓展建议)
-
- [📊 本教程覆盖内容回顾](#📊 本教程覆盖内容回顾)
- [🚀 拓展学习路径](#🚀 拓展学习路径)
- [📚 参考资源](#📚 参考资源)
摘要:本教程手把手带你在 Windows / Ubuntu 22.04 上搭建 LangChain 开发环境,并使用 LangChain + Ollama 本地大模型 + ChromaDB 向量数据库,从零构建一个可以"读懂"你的 PDF / TXT 文档的本地 RAG(检索增强生成)知识库系统。全程数据不上云,保护隐私,附完整可运行代码。
1. 引言:什么是 RAG?为什么需要它?
大语言模型(LLM)本质上是一个"知识截止"系统------它只了解训练数据中包含的信息,对于你的私有文档(公司内部手册、个人笔记、产品文档等)一无所知。
RAG(Retrieval-Augmented Generation,检索增强生成) 正是解决这个问题的核心技术:
┌─────────────────────────────────────────────────────┐
│ RAG 工作流程 │
│ │
│ 用户提问 → 向量检索相关文档片段 → 连同问题发给 LLM │
│ → LLM 基于检索到的上下文生成准确回答 │
└─────────────────────────────────────────────────────┘
| 对比项 | 纯 LLM | RAG + LLM |
|---|---|---|
| 私有文档知识 | ❌ 不了解 | ✅ 完全支持 |
| 知识实时更新 | ❌ 训练截止 | ✅ 随时更新 |
| 回答幻觉 | 较多 | 大幅减少 |
| 数据隐私 | 数据上传云端 | ✅ 本地处理 |
| 成本 | 按 Token 计费 | ✅ 本地免费 |
本教程技术栈:
- LangChain:AI 应用开发框架(编排工具)
- Ollama:本地大模型运行服务(推理引擎)
- Qwen2.5 / LLaMA3:开源大语言模型(大脑)
- ChromaDB:本地向量数据库(记忆存储)
- nomic-embed-text:文本向量化模型(理解工具)
2. 环境准备
2.1 系统与 Python 版本要求
| 环境 | 最低版本 | 推荐版本 |
|---|---|---|
| Python | 3.9 | 3.11(推荐) |
| pip | 21.0+ | 最新版 |
| 操作系统 | Windows 10 / Ubuntu 20.04 | Windows 11 / Ubuntu 22.04 |
| 内存 | 8 GB | 16 GB(运行 7B 模型) |
bash
# 检查 Python 版本(必须 ≥ 3.9)
python --version # Windows
python3 --version # Linux
# 升级 pip 到最新版(重要!旧版 pip 可能导致安装失败)
python -m pip install --upgrade pip
# 确认 pip 版本
pip --version
# 预期输出:pip 24.x.x from ...
2.2 安装 Ollama 本地模型服务
LangChain 需要通过 Ollama 来调用本地大模型和 Embedding 模型,请先完成 Ollama 安装:
bash
# Linux 一键安装
curl -fsSL https://ollama.com/install.sh | sh
# 拉取对话模型(用于回答问题)
ollama pull qwen2.5:7b
# 拉取 Embedding 模型(用于文本向量化,RAG 核心)
ollama pull nomic-embed-text
# 验证模型已就绪
ollama list
# 预期输出:
# NAME ID SIZE
# qwen2.5:7b ... 4.7 GB
# nomic-embed-text ... 274 MB
💡 提示 :
nomic-embed-text是专用文本向量化模型,文件小(274MB)、效果好,是本地 RAG 的首选 Embedding 模型。
2.3 创建 Python 虚拟环境
bash
# 创建专用虚拟环境(强烈推荐,避免依赖版本冲突)
python -m venv langchain_env
# 激活虚拟环境
# Linux / macOS:
source langchain_env/bin/activate
# Windows(PowerShell):
langchain_env\Scripts\Activate.ps1
# Windows(命令提示符 CMD):
langchain_env\Scripts\activate.bat
# 确认虚拟环境已激活(命令行前缀应显示 (langchain_env))
# (langchain_env) PS C:\Users\...>
⚠️ 注意 :后续所有
pip install命令都需要在激活虚拟环境后执行,否则包会安装到全局环境。
3. 安装 LangChain 及相关依赖
3.1 核心包说明
LangChain 生态由多个独立包组成,本项目用到以下几个:
| 包名 | 用途 | 备注 |
|---|---|---|
langchain |
LangChain 核心框架 | 必装 |
langchain-community |
第三方集成(Ollama、ChromaDB 等) | 必装 |
langchain-ollama |
Ollama 官方集成(新版专用包) | 必装 |
chromadb |
本地向量数据库 | RAG 必装 |
langchain-chroma |
LangChain 与 ChromaDB 集成 | RAG 必装 |
pypdf |
PDF 文档加载器 | 处理 PDF 必装 |
unstructured |
多格式文档加载器 | 处理 Word/HTML 等 |
3.2 安装命令(含国内镜像加速)
bash
# 方式一:使用清华镜像(国内推荐,速度快 5-10 倍)
pip install langchain langchain-community langchain-ollama \
chromadb langchain-chroma \
pypdf unstructured \
-i https://pypi.tuna.tsinghua.edu.cn/simple
# 方式二:使用阿里云镜像
pip install langchain langchain-community langchain-ollama \
chromadb langchain-chroma \
pypdf unstructured \
-i https://mirrors.aliyun.com/pypi/simple/
# 方式三:永久设置默认镜像(一劳永逸)
pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple
# 之后直接 pip install 即可自动使用镜像
pip install langchain langchain-community langchain-ollama chromadb langchain-chroma pypdf unstructured
⚠️ 常见坑 :
chromadb在安装时会编译 C++ 扩展,Windows 用户如遇错误请先安装 Microsoft C++ Build Tools。
3.3 验证安装
python
# verify_install.py
# 运行此脚本,确认所有依赖安装正确
def check_imports():
checks = [
("langchain", "from langchain.schema import HumanMessage"),
("langchain-ollama", "from langchain_ollama import OllamaLLM"),
("chromadb", "import chromadb"),
("langchain-chroma", "from langchain_chroma import Chroma"),
("pypdf", "from langchain_community.document_loaders import PyPDFLoader"),
]
print("=== 依赖包安装检查 ===\n")
all_ok = True
for name, import_stmt in checks:
try:
exec(import_stmt)
print(f" ✅ {name:<30} 安装正常")
except ImportError as e:
print(f" ❌ {name:<30} 安装失败: {e}")
all_ok = False
print()
if all_ok:
print("🎉 所有依赖安装正常,可以开始使用!")
else:
print("⚠️ 部分依赖安装失败,请根据提示重新安装")
check_imports()
bash
# 运行检查脚本
python verify_install.py
# 预期输出:
# === 依赖包安装检查 ===
#
# ✅ langchain 安装正常
# ✅ langchain-ollama 安装正常
# ✅ chromadb 安装正常
# ✅ langchain-chroma 安装正常
# ✅ pypdf 安装正常
#
# 🎉 所有依赖安装正常,可以开始使用!
4. LangChain 基础:接入本地 Ollama 模型
4.1 基础对话链
python
# 01_basic_llm.py
from langchain_ollama import OllamaLLM
from langchain_core.messages import HumanMessage, SystemMessage
# 初始化本地 Ollama 模型
llm = OllamaLLM(
model="qwen2.5:7b",
base_url="http://localhost:11434", # Ollama 默认地址
temperature=0.7, # 0.0=确定性输出,1.0=最具创造性
num_predict=512, # 最大生成 Token 数
)
# 方式一:直接调用
response = llm.invoke("Python 中什么是装饰器?请举例说明")
print("LLM 回复:")
print(response)
# 方式二:使用 ChatOllama(支持角色消息)
from langchain_ollama import ChatOllama
chat_model = ChatOllama(
model="qwen2.5:7b",
temperature=0.7,
)
messages = [
SystemMessage(content="你是一个专业的 Python 教学助手,解释要简洁易懂,并附上代码示例。"),
HumanMessage(content="什么是 Python 生成器?")
]
response = chat_model.invoke(messages)
print("\nChatOllama 回复:")
print(response.content)
4.2 提示词模板(PromptTemplate)
提示词模板是 LangChain 的核心概念之一,用于动态构建 Prompt:
python
# 02_prompt_template.py
from langchain_ollama import ChatOllama
from langchain_core.prompts import ChatPromptTemplate, PromptTemplate
from langchain_core.output_parsers import StrOutputParser
# 初始化模型
llm = ChatOllama(model="qwen2.5:7b", temperature=0.7)
# 方式一:ChatPromptTemplate(推荐)
chat_prompt = ChatPromptTemplate.from_messages([
("system", "你是一个专业的 {domain} 领域专家,回答要专业、准确、简洁。"),
("human", "{question}")
])
# 构建 LCEL 链(LangChain Expression Language)
# 语法:prompt | llm | parser,数据从左向右流动
chain = chat_prompt | llm | StrOutputParser()
# 调用链
response = chain.invoke({
"domain": "Python 数据科学",
"question": "pandas 中 groupby 操作的原理是什么?"
})
print("Chain 回复:")
print(response)
# 方式二:结构化提示词模板
code_review_prompt = ChatPromptTemplate.from_messages([
("system", """你是一个资深代码审查专家。
请对用户提供的代码进行审查,从以下维度给出建议:
1. 代码规范性
2. 潜在 Bug
3. 性能优化点
4. 可读性改进
输出格式:使用 Markdown 列表格式"""),
("human", "请审查以下 {language} 代码:\n\n```{language}\n{code}\n```")
])
review_chain = code_review_prompt | llm | StrOutputParser()
sample_code = """
def get_user_data(users):
result = []
for i in range(len(users)):
if users[i]['age'] > 18:
result.append(users[i])
return result
"""
review = review_chain.invoke({
"language": "Python",
"code": sample_code
})
print("\n代码审查结果:")
print(review)
4.3 对话记忆(Memory)
python
# 03_conversation_memory.py
from langchain_ollama import ChatOllama
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.chat_history import InMemoryChatMessageHistory
from langchain_core.runnables.history import RunnableWithMessageHistory
# 初始化模型
llm = ChatOllama(model="qwen2.5:7b", temperature=0.7)
# 构建支持历史记录的提示词模板
prompt = ChatPromptTemplate.from_messages([
("system", "你是一个友好的 AI 助手。"),
MessagesPlaceholder(variable_name="history"), # 历史消息占位符
("human", "{input}")
])
# 构建基础链
chain = prompt | llm
# 存储每个会话的历史记录
session_store = {}
def get_session_history(session_id: str) -> InMemoryChatMessageHistory:
"""根据 session_id 获取或创建对话历史"""
if session_id not in session_store:
session_store[session_id] = InMemoryChatMessageHistory()
return session_store[session_id]
# 将链包装成支持历史记录的版本
chain_with_history = RunnableWithMessageHistory(
chain,
get_session_history,
input_messages_key="input",
history_messages_key="history"
)
# 配置(每次调用需传入 session_id)
config = {"configurable": {"session_id": "user_001"}}
print("=== 多轮对话测试 ===\n")
# 第一轮
response1 = chain_with_history.invoke(
{"input": "我叫小明,我正在学习 Python"},
config=config
)
print(f"第一轮回复:{response1.content}\n")
# 第二轮(AI 能记住上文的"小明")
response2 = chain_with_history.invoke(
{"input": "我之前说我叫什么名字?"},
config=config
)
print(f"第二轮回复:{response2.content}\n")
# 第三轮
response3 = chain_with_history.invoke(
{"input": "给我推荐一个适合我学习的 Python 项目"},
config=config
)
print(f"第三轮回复:{response3.content}\n")
5. 构建 RAG 知识库
5.1 RAG 工作原理
在编写代码前,先理解 RAG 的完整流程:
【离线阶段:知识入库】
文档文件(PDF/TXT/MD)
↓ 文档加载器(DocumentLoader)
原始文本
↓ 文本分割器(TextSplitter)
文本块(Chunks)
↓ Embedding 模型(nomic-embed-text)
向量(Vectors)
↓ 存入向量数据库(ChromaDB)
向量索引(持久化到本地)
【在线阶段:问答检索】
用户提问
↓ Embedding 模型(同上)
问题向量
↓ 相似度搜索(ChromaDB)
相关文档片段(Top-K)
↓ 组合成 Prompt(RAG Prompt)
最终 Prompt = 问题 + 相关上下文
↓ 大语言模型(qwen2.5:7b)
最终答案
5.2 文档加载与分块
python
# 04_document_loading.py
from langchain_community.document_loaders import (
PyPDFLoader, # PDF 文件
TextLoader, # TXT 文件
DirectoryLoader, # 批量加载目录
)
from langchain.text_splitter import RecursiveCharacterTextSplitter
# ---- 加载单个 PDF 文件 ----
def load_pdf(file_path: str):
loader = PyPDFLoader(file_path)
pages = loader.load()
print(f"PDF 加载完成:共 {len(pages)} 页")
print(f"第一页内容预览:{pages[0].page_content[:200]}...")
return pages
# ---- 加载 TXT 文件 ----
def load_txt(file_path: str):
loader = TextLoader(file_path, encoding="utf-8")
docs = loader.load()
return docs
# ---- 批量加载目录中的所有 TXT 文件 ----
def load_directory(dir_path: str):
loader = DirectoryLoader(
dir_path,
glob="**/*.txt", # 匹配所有 TXT 文件
loader_cls=TextLoader,
loader_kwargs={"encoding": "utf-8"}
)
docs = loader.load()
print(f"目录加载完成:共 {len(docs)} 个文档")
return docs
# ---- 文本分块(核心步骤)----
def split_documents(docs, chunk_size=500, chunk_overlap=50):
"""
将文档切割成小块
Args:
docs: 文档列表
chunk_size: 每块的字符数(推荐 300~1000)
chunk_overlap: 相邻块的重叠字符数(保证上下文连续性)
Returns:
切割后的文档块列表
"""
splitter = RecursiveCharacterTextSplitter(
chunk_size=chunk_size, # 每块最大字符数
chunk_overlap=chunk_overlap, # 重叠量,避免切断语义
length_function=len,
separators=["\n\n", "\n", "。", "!", "?", " ", ""], # 优先按段落切
)
chunks = splitter.split_documents(docs)
print(f"文档分块完成:{len(docs)} 个文档 → {len(chunks)} 个文本块")
print(f"平均块大小:{sum(len(c.page_content) for c in chunks) // len(chunks)} 字符")
return chunks
# ---- 演示(使用临时文件测试)----
if __name__ == "__main__":
import os
# 创建一个测试文档
test_content = """
LangChain 是一个用于构建大语言模型应用的开源框架。
它的核心功能包括:
1. 模型集成:支持 OpenAI、Anthropic、Ollama 等主流模型
2. 提示词管理:灵活的 PromptTemplate 系统
3. 链式调用:通过 LCEL 组合多个组件
4. 记忆管理:支持多轮对话上下文保存
5. 文档检索:内置 RAG 支持,轻松构建知识库
RAG(检索增强生成)是一种将外部知识库与大语言模型结合的技术。
通过向量检索找到最相关的文档片段,并将其作为上下文注入到提示词中,
从而让模型能够回答关于私有数据的问题,同时减少模型"幻觉"。
"""
with open("test_doc.txt", "w", encoding="utf-8") as f:
f.write(test_content)
docs = load_txt("test_doc.txt")
chunks = split_documents(docs, chunk_size=200, chunk_overlap=30)
print(f"\n分块示例(第1块):")
print(chunks[0].page_content)
print(f"\n元数据:{chunks[0].metadata}")
5.3 向量化与存储(ChromaDB)
python
# 05_vector_store.py
from langchain_ollama import OllamaEmbeddings
from langchain_chroma import Chroma
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_community.document_loaders import TextLoader
import os
# ---- 初始化 Embedding 模型 ----
embeddings = OllamaEmbeddings(
model="nomic-embed-text", # 本地 Embedding 模型(需先 ollama pull nomic-embed-text)
base_url="http://localhost:11434"
)
# 测试 Embedding(确认服务正常)
test_embedding = embeddings.embed_query("测试文本")
print(f"Embedding 维度:{len(test_embedding)}") # nomic-embed-text 输出 768 维向量
# ---- 构建向量数据库 ----
def build_vector_store(documents, persist_dir: str = "./chroma_db"):
"""
将文档向量化并存储到 ChromaDB
Args:
documents: 文档块列表
persist_dir: 数据库持久化路径(本地磁盘)
Returns:
Chroma 向量数据库实例
"""
print(f"正在向量化 {len(documents)} 个文本块,请稍候...")
vector_store = Chroma.from_documents(
documents=documents,
embedding=embeddings,
persist_directory=persist_dir, # 持久化到本地,重启后无需重新建库
collection_name="my_knowledge_base"
)
print(f"✅ 向量库构建完成,已保存至: {persist_dir}")
print(f" 共存储 {vector_store._collection.count()} 个向量")
return vector_store
# ---- 加载已有向量数据库 ----
def load_vector_store(persist_dir: str = "./chroma_db"):
"""
从磁盘加载已构建好的向量数据库(无需重新向量化)
"""
if not os.path.exists(persist_dir):
raise FileNotFoundError(f"向量数据库不存在: {persist_dir},请先运行 build_vector_store()")
vector_store = Chroma(
persist_directory=persist_dir,
embedding_function=embeddings,
collection_name="my_knowledge_base"
)
print(f"✅ 向量库加载成功:{vector_store._collection.count()} 个向量")
return vector_store
# ---- 相似度检索测试 ----
def test_retrieval(vector_store, query: str, top_k: int = 3):
"""
测试向量检索效果
Args:
vector_store: Chroma 实例
query: 查询问题
top_k: 返回最相关的 K 个文档块
"""
results = vector_store.similarity_search_with_score(query, k=top_k)
print(f"\n查询:{query}")
print(f"检索到 {len(results)} 个相关片段:\n")
for i, (doc, score) in enumerate(results, 1):
print(f"【相关片段 {i}】相似度得分: {1 - score:.4f}")
print(f"内容: {doc.page_content[:200]}...")
print(f"来源: {doc.metadata.get('source', '未知')}")
print()
return [doc for doc, _ in results]
5.4 检索链构建
python
# 06_retrieval_chain.py
from langchain_ollama import ChatOllama
from langchain_chroma import Chroma
from langchain_ollama import OllamaEmbeddings
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough
# 初始化组件
llm = ChatOllama(model="qwen2.5:7b", temperature=0.3) # 知识库问答用低 temperature,更准确
embeddings = OllamaEmbeddings(model="nomic-embed-text")
# RAG 专用提示词模板
RAG_PROMPT = ChatPromptTemplate.from_messages([
("system", """你是一个知识库问答助手。
请根据以下【参考资料】来回答用户的问题。
回答要求:
- 只根据参考资料中的内容回答,不要添加资料中没有的信息
- 如果参考资料中没有相关信息,请明确说明"参考资料中未找到相关信息"
- 回答要准确、简洁,引用原文时加引号
【参考资料】
{context}
"""),
("human", "{question}")
])
def format_docs(docs) -> str:
"""将检索到的文档块格式化为字符串"""
return "\n\n---\n\n".join([
f"[来源: {doc.metadata.get('source', '未知')}]\n{doc.page_content}"
for doc in docs
])
def build_rag_chain(vector_store, top_k: int = 4):
"""
构建 RAG 检索链
Args:
vector_store: 向量数据库实例
top_k: 每次检索返回的最相关文档数
Returns:
LCEL RAG 链
"""
# 创建检索器
retriever = vector_store.as_retriever(
search_type="similarity", # 相似度搜索
search_kwargs={"k": top_k} # 返回前 K 个结果
)
# 构建 LCEL 链
# RunnablePassthrough:透传 question 字段
# retriever → format_docs:检索并格式化上下文
rag_chain = (
{
"context": retriever | format_docs, # 检索相关文档作为上下文
"question": RunnablePassthrough() # 用户问题直接传递
}
| RAG_PROMPT # 组合成完整 Prompt
| llm # 发给 LLM 生成回答
| StrOutputParser() # 提取纯文本输出
)
return rag_chain
5.5 完整 RAG 系统整合
python
# rag_system.py ← 主程序,整合所有模块
"""
完整的本地 RAG 知识库系统
使用方式:
1. 将你的文档放入 ./docs/ 目录(支持 .txt / .pdf)
2. 运行此脚本,首次运行会自动建库
3. 向 AI 提问,它会基于你的文档回答
"""
import os
from pathlib import Path
from langchain_community.document_loaders import (
PyPDFLoader, TextLoader, DirectoryLoader
)
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_ollama import OllamaEmbeddings, ChatOllama
from langchain_chroma import Chroma
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough
# ====== 配置项 ======
DOCS_DIR = "./docs" # 文档目录
CHROMA_DIR = "./chroma_db" # 向量库存储目录
EMBED_MODEL = "nomic-embed-text"
CHAT_MODEL = "qwen2.5:7b"
CHUNK_SIZE = 500
CHUNK_OVERLAP = 50
TOP_K = 4
# ====================
class LocalRAGSystem:
"""
本地 RAG 知识库系统
支持 PDF 和 TXT 格式文档
"""
def __init__(self):
self.embeddings = OllamaEmbeddings(model=EMBED_MODEL)
self.llm = ChatOllama(model=CHAT_MODEL, temperature=0.3)
self.vector_store = None
self.rag_chain = None
def _load_documents(self) -> list:
"""加载 docs 目录下的所有文档"""
docs = []
docs_path = Path(DOCS_DIR)
if not docs_path.exists():
docs_path.mkdir(parents=True)
print(f"已创建文档目录: {DOCS_DIR},请放入你的文档后重新运行")
return []
# 加载 TXT 文件
for txt_file in docs_path.rglob("*.txt"):
loader = TextLoader(str(txt_file), encoding="utf-8")
docs.extend(loader.load())
print(f" 已加载 TXT: {txt_file.name}")
# 加载 PDF 文件
for pdf_file in docs_path.rglob("*.pdf"):
loader = PyPDFLoader(str(pdf_file))
docs.extend(loader.load())
print(f" 已加载 PDF: {pdf_file.name}")
return docs
def _split_documents(self, docs: list) -> list:
"""文档分块"""
splitter = RecursiveCharacterTextSplitter(
chunk_size=CHUNK_SIZE,
chunk_overlap=CHUNK_OVERLAP,
separators=["\n\n", "\n", "。", "!", "?", " ", ""]
)
return splitter.split_documents(docs)
def build_or_load_db(self) -> None:
"""构建或加载向量数据库"""
if os.path.exists(CHROMA_DIR):
print(f"📂 加载已有向量库: {CHROMA_DIR}")
self.vector_store = Chroma(
persist_directory=CHROMA_DIR,
embedding_function=self.embeddings,
collection_name="knowledge_base"
)
count = self.vector_store._collection.count()
print(f"✅ 向量库加载完成,共 {count} 个向量")
else:
print("🔨 首次运行,开始构建向量库...")
print(f"加载 {DOCS_DIR} 目录下的文档...")
raw_docs = self._load_documents()
if not raw_docs:
return
print(f"文档分块中(chunk_size={CHUNK_SIZE})...")
chunks = self._split_documents(raw_docs)
print(f"分块完成:{len(chunks)} 个文本块")
print("向量化并存储(首次可能需要几分钟)...")
self.vector_store = Chroma.from_documents(
documents=chunks,
embedding=self.embeddings,
persist_directory=CHROMA_DIR,
collection_name="knowledge_base"
)
print(f"✅ 向量库构建完成!已保存至 {CHROMA_DIR}")
def _build_rag_chain(self):
"""构建 RAG 问答链"""
RAG_PROMPT = ChatPromptTemplate.from_messages([
("system", """你是一个专业的知识库问答助手。
请严格基于以下【参考资料】回答用户问题。
规则:
1. 只使用参考资料中的信息
2. 如果资料中没有答案,回复"我在知识库中未找到相关信息"
3. 回答要准确、简洁,可适当引用原文
【参考资料】
{context}
"""),
("human", "问题:{question}")
])
retriever = self.vector_store.as_retriever(
search_kwargs={"k": TOP_K}
)
def format_docs(docs):
return "\n\n---\n\n".join([
f"[来源文件: {doc.metadata.get('source', '未知')}]\n{doc.page_content}"
for doc in docs
])
self.rag_chain = (
{"context": retriever | format_docs, "question": RunnablePassthrough()}
| RAG_PROMPT
| self.llm
| StrOutputParser()
)
def ask(self, question: str) -> str:
"""向知识库提问"""
if self.vector_store is None:
return "错误:向量库未初始化,请先调用 build_or_load_db()"
if self.rag_chain is None:
self._build_rag_chain()
return self.rag_chain.invoke(question)
def add_document(self, file_path: str) -> None:
"""向现有知识库添加新文档(增量更新)"""
if file_path.endswith(".pdf"):
loader = PyPDFLoader(file_path)
else:
loader = TextLoader(file_path, encoding="utf-8")
docs = loader.load()
chunks = self._split_documents(docs)
self.vector_store.add_documents(chunks)
print(f"✅ 已添加 {len(chunks)} 个文本块到知识库:{file_path}")
def run_interactive(self):
"""启动交互式问答"""
print("\n" + "=" * 55)
print(" 📚 本地 RAG 知识库问答系统(基于 LangChain + Ollama)")
print("=" * 55)
print("输入问题与知识库对话,输入 'quit' 退出\n")
while True:
question = input("❓ 你的问题: ").strip()
if not question:
continue
if question.lower() == "quit":
print("再见!👋")
break
print("\n🔍 检索中...", end="", flush=True)
answer = self.ask(question)
print(f"\r💡 回答:\n{answer}\n")
def main():
rag = LocalRAGSystem()
rag.build_or_load_db()
if rag.vector_store:
rag.run_interactive()
if __name__ == "__main__":
main()
6. 进阶:多格式文档支持
python
# multi_format_loader.py
from langchain_community.document_loaders import (
PyPDFLoader, # PDF
TextLoader, # TXT
UnstructuredMarkdownLoader, # Markdown
UnstructuredWordDocumentLoader, # Word (.docx)
UnstructuredHTMLLoader, # HTML
CSVLoader, # CSV
JSONLoader, # JSON
WebBaseLoader, # 网页
)
import os
def load_document_by_type(file_path: str):
"""
根据文件扩展名自动选择加载器
Args:
file_path: 文件路径
Returns:
Document 列表
"""
ext = os.path.splitext(file_path)[-1].lower()
loader_map = {
".pdf": lambda p: PyPDFLoader(p),
".txt": lambda p: TextLoader(p, encoding="utf-8"),
".md": lambda p: UnstructuredMarkdownLoader(p),
".docx": lambda p: UnstructuredWordDocumentLoader(p),
".html": lambda p: UnstructuredHTMLLoader(p),
".csv": lambda p: CSVLoader(p, encoding="utf-8"),
}
if ext not in loader_map:
raise ValueError(f"不支持的文件格式: {ext}")
loader = loader_map[ext](file_path)
docs = loader.load()
print(f"✅ 加载 {ext} 文件成功:{os.path.basename(file_path)}({len(docs)} 页/段)")
return docs
# 加载网页内容并添加到知识库
def load_web_page(url: str):
"""
加载网页内容(如官方文档、博客等)
"""
loader = WebBaseLoader(url)
docs = loader.load()
print(f"✅ 已加载网页: {url}")
return docs
7. 常见问题与解决方案
问题一:pip install chromadb 安装报错(Windows)
错误信息:
error: Microsoft Visual C++ 14.0 or greater is required
原因:chromadb 需要编译 C++ 扩展,Windows 缺少编译工具
解决方案:
bash
# 方法一:安装 Microsoft C++ Build Tools
# 下载地址:https://visualstudio.microsoft.com/visual-cpp-build-tools/
# 安装时勾选"使用 C++ 的桌面开发"工作负载
# 方法二:使用预编译 wheel(跳过编译)
pip install chromadb --only-binary chromadb
# 方法三:升级 pip 后重试
pip install --upgrade pip setuptools wheel
pip install chromadb
问题二:Embedding 向量化非常慢
原因 :nomic-embed-text 模型首次加载需要时间;大量文档时向量化耗时较长
优化方案:
python
# 方法一:批量向量化,减少重复加载开销
from langchain_chroma import Chroma
# 一次性传入所有 chunks,比逐条插入快得多
vector_store = Chroma.from_documents(
documents=all_chunks, # 一次性传入全部文档块
embedding=embeddings,
persist_directory="./chroma_db"
)
# 方法二:增加批次大小(默认为 1)
embeddings = OllamaEmbeddings(
model="nomic-embed-text",
# 较新版本支持 batch_size 参数
)
问题三:RAG 回答不准确或出现"幻觉"
原因和解决方案:
python
# 原因一:检索 top_k 太少,相关内容未被找到
# 解决:增加 top_k 值
retriever = vector_store.as_retriever(search_kwargs={"k": 6}) # 从 4 增加到 6
# 原因二:文本块太大,稀释了相关信息
# 解决:减小 chunk_size
splitter = RecursiveCharacterTextSplitter(
chunk_size=300, # 从 500 减小到 300
chunk_overlap=50,
)
# 原因三:Prompt 设计不够严格
# 解决:加强系统提示词中的约束
system_prompt = """严格基于以下参考资料回答问题。
如果参考资料中没有相关信息,必须明确说"未找到相关信息",
绝对不可以根据自身知识补充未在资料中出现的内容。"""
问题四:ImportError: cannot import name 'OllamaLLM' from 'langchain_community'
原因 :新版 LangChain 将 Ollama 集成独立为 langchain-ollama 包
解决方案:
bash
# 安装新版专用包
pip install langchain-ollama
# 修改 import 语句
# 旧写法(已废弃):
# from langchain_community.llms import Ollama
# 新写法(推荐):
from langchain_ollama import OllamaLLM, ChatOllama, OllamaEmbeddings
8. 总结与拓展建议
📊 本教程覆盖内容回顾
| 章节 | 核心内容 |
|---|---|
| 环境准备 | Python 虚拟环境、Ollama 模型安装 |
| 依赖安装 | LangChain + ChromaDB + 国内镜像加速 |
| 基础对话 | OllamaLLM / ChatOllama 调用 |
| 提示词模板 | PromptTemplate + LCEL 链 |
| 对话记忆 | 多轮对话历史管理 |
| RAG 知识库 | 文档加载 → 分块 → 向量化 → 检索 → 问答 |
| 多格式支持 | PDF / TXT / DOCX / 网页 |
| 常见问题 | 4 个高频报错 + 解决方案 |
🚀 拓展学习路径
完成本教程后,推荐继续深入:
- LangChain Agent:让 AI 自主调用工具(搜索、计算、代码执行),实现自主决策
- 流式 RAG:结合 FastAPI 构建支持流式输出的 RAG 服务接口
- Rerank 重排序:引入 BGE-Reranker 对检索结果二次排序,提升准确率
- 多向量库:使用 FAISS(Meta 开源)替换 ChromaDB,性能更高
- 评估体系:使用 RAGAS 框架评估 RAG 系统的准确率和召回率
💡 关注博主 ,后续将更新 LangChain Agent 实战 、本地知识库接入 Web 界面等进阶教程!
📚 参考资源
如果本文对你有帮助,欢迎点赞 👍 + 收藏 ⭐ + 关注!有问题欢迎在评论区留言,博主会及时回复 💬
📕个人领域 :Linux/C++/java/AI🚀 个人主页 :有点流鼻涕 · CSDN
💬 座右铭 : "向光而行,沐光而生。"
