在处理本地 PDF 文档的智能对话需求时,很多人会面临 "云端依赖泄露隐私""开源项目部署门槛高" 的问题 ------ 而 GitHub 上的 Local_Pdf_Chat_RAG 项目,恰好以 "本地化部署 + RAG 技术" 为核心,解决了 PDF 文档的高效检索与自然语言对话难题。近期我系统学习了这个项目,从环境搭建、代码核心模块拆解,到实际场景测试与调优,踩过不少新手常见的坑,也总结了一套可复用的实践经验。因此整理这份学习笔记,一方面记录自己的技术成长,另一方面希望能帮到同样想入门 "本地 PDF+RAG" 的朋友。整篇笔记会围绕「环境准备→核心组件解析(PDF 加载 / 向量存储 / 对话逻辑)→部署测试→问题排查」四个维度展开,尽量兼顾原理讲解与实战操作,适合有 Python 基础、对 RAG 技术感兴趣的学习者参考。
一、环境准备:从依赖到服务的完整搭建
Local_Pdf_Chat_RAG 项目的核心优势是 "本地化部署",但新手常因环境配置不完整导致项目启动失败。本节将按 "系统要求→Python 环境→核心依赖→服务启动" 的顺序,提供可复现的操作步骤,规避常见的 "依赖冲突""服务失联" 问题。
1.1 基础系统与硬件要求
项目对硬件的要求主要取决于本地模型的规模(如 7B 模型需至少 16GB 内存,120B 模型需 64GB+ 内存),系统需满足以下条件:
- 操作系统:Windows 10/11(PowerShell 5.1+)、Linux(Ubuntu 20.04+)、macOS 12+(M1/M2 芯片需开启 Rosetta 兼容);
- 权限:本地磁盘读写权限(用于存储 PDF 文档、向量数据库缓存);
- 网络:首次部署需联网下载依赖和模型(无需科学上网,可通过国内镜像加速)。
1.2 Python 环境配置
项目依赖 Python 3.8~3.11(3.12+ 可能存在部分库兼容性问题),建议使用虚拟环境隔离依赖:
步骤 1:创建并激活虚拟环境
-
Windows(PowerShell) :
powershell
# 安装虚拟环境工具(若未安装) pip install virtualenv # 在项目根目录创建虚拟环境 virtualenv .venv # 激活虚拟环境 .\.venv\Scripts\Activate.ps1
-
Linux/macOS(终端) :
bash
# 创建虚拟环境 python3 -m venv .venv # 激活虚拟环境 source .venv/bin/activate
步骤 2:配置国内镜像(解决依赖下载慢)
在虚拟环境中执行以下命令,优先使用阿里云镜像:
bash
pip config set global.index-url https://mirrors.aliyun.com/pypi/simple/
1.3 核心依赖安装
项目依赖分为 "基础工具库""RAG 核心库""本地化服务库" 三类,可通过 requirements.txt
批量安装(建议手动创建该文件,避免版本冲突):
1.3.1 编写 requirements.txt
txt
# 基础工具库(PDF 提取、文本处理)
pdfminer.six==20221105 # PDF 文本提取(比 PyPDF2 准确率更高)
jieba==0.42.1 # 中文分词(用于 BM25 检索)
langchain-text-splitters==0.2.2 # 文本分块(RecursiveCharacterTextSplitter)
python-dotenv==1.0.1 # 环境变量管理(读取 .env 文件)
# RAG 核心库(向量生成、检索、重排序)
sentence-transformers==2.7.0 # 嵌入模型(生成文本向量)
faiss-cpu==1.7.4 # 向量数据库(轻量版,GPU 版需装 faiss-gpu)
rank-bm25==0.2.2 # BM25 关键词检索(与向量检索混合提升精度)
# 本地化服务库(Web 界面、模型调用)
gradio==4.44.1 # Web 交互界面(避免用 5.x 版本,部分 API 不兼容)
requests==2.32.3 # HTTP 请求(调用 Ollama/SiliconFlow 服务)
urllib3==2.2.2 # 解决 requests 依赖冲突
1.3.2 执行安装命令
bash
pip install -r requirements.txt
1.3.3 特殊依赖:HF 镜像配置(无需科学上网)
项目需下载 sentence-transformers
嵌入模型(如 all-MiniLM-L6-v2
),默认从 Hugging Face 官网下载较慢,需配置国内镜像 hf-mirror.com
:
- 临时生效(仅当前终端) :
- Windows(PowerShell):
$env:HF_ENDPOINT = "https://hf-mirror.com"
- Linux/macOS(终端):
export HF_ENDPOINT=https://hf-mirror.com
- Windows(PowerShell):
- 永久生效 :
- Windows:将
$env:HF_ENDPOINT = "https://hf-mirror.com"
写入~\.bashrc
(需安装 Git Bash); - Linux:将
export HF_ENDPOINT=https://hf-mirror.com
写入~/.bashrc
,执行source ~/.bashrc
生效。
- Windows:将
1.4 Ollama 服务与模型部署
项目默认使用 Ollama 运行本地大模型(如 gpt-oss:120b-cloud
),需先安装 Ollama 并拉取模型:
步骤 1:安装 Ollama
- 下载地址:Ollama 官网(Windows 需勾选 "添加到 PATH",Linux/macOS 按官网脚本安装);
- 验证安装:终端执行
ollama --version
,输出ollama version 0.1.xx
即成功。
步骤 2:拉取本地模型
根据硬件配置选择模型(新手推荐先试小模型如 deepseek-r1:1.5b
,再用大模型 gpt-oss:120b-cloud
):
bash
# 拉取 gpt-oss:120b-cloud(用户实际使用的模型)
ollama pull gpt-oss:120b-cloud
# 拉取轻量模型(测试用)
ollama pull deepseek-r1:1.5b
步骤 3:启动 Ollama 服务
项目需通过 Ollama 的 API 调用模型,需先启动服务:
- Windows:无需手动启动,拉取模型后自动后台运行;
- Linux/macOS :终端执行
ollama serve
(需保持终端开启,关闭则服务停止); - 验证服务:浏览器访问
http://localhost:11434
,显示 "Ollama is running" 即成功。
1.5 .env 文件配置(敏感信息管理)
项目需配置 SERPAPI_KEY
(联网搜索功能)、SILICONFLOW_API_KEY
(云端模型备用),避免硬编码泄露信息。在项目根目录创建 .env
文件,内容如下:
env
# SERPAPI_KEY:用于联网搜索(可选,不配置则关闭联网功能)
# 申请地址:https://serpapi.com/(免费额度足够测试)
SERPAPI_KEY=your_serpapi_key_here
# SILICONFLOW_API_KEY:云端模型密钥(可选,优先用本地 Ollama)
# 申请地址:https://www.siliconflow.cn/
SILICONFLOW_API_KEY=your_siliconflow_key_here
# 重排序方法(默认 cross_encoder,可选 llm)
RERANK_METHOD=cross_encoder
二、核心组件解析:RAG 技术如何实现 "PDF 对话"
Local_Pdf_Chat_RAG 的核心逻辑是 "PDF 解析→文本向量化→检索增强生成",对应三个核心组件:PDF 加载与分块、向量存储与混合检索、对话生成与优化。本节结合代码细节,拆解每个组件的原理与作用。
2.1 PDF 加载与分块:让文档 "可检索"
PDF 文档是二进制格式,无法直接被模型理解,需先提取文本并拆分为 "小片段"(分块)------ 分块质量直接影响后续检索精度(太大易包含无关信息,太小易丢失上下文)。
2.1.1 PDF 文本提取:pdfminer 的优势
项目使用 pdfminer.six
提取文本,而非常见的 PyPDF2
,原因是它能更好地保留 PDF 中的段落结构、表格文本(需开启特定配置):
python
def extract_text(filepath):
"""改进的 PDF 文本提取方法"""
output = StringIO() # 内存中存储提取的文本
with open(filepath, 'rb') as file:
# 关键参数:提取文本时保留换行符,避免段落合并
extract_text_to_fp(file, output, laparams=dict(line_margin=0.1))
return output.getvalue() # 返回纯文本字符串
2.1.2 文本分块:RecursiveCharacterTextSplitter 的逻辑
项目使用 RecursiveCharacterTextSplitter
按 "段落→句子→字符" 的优先级拆分文本,参数经过优化(针对中文文档):
python
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=400, # 每个块的字符数(中文约 130~150 字,适配模型上下文窗口)
chunk_overlap=40, # 块重叠字符数(避免拆分完整句子,如"你好,世界"不被拆成"你好"和"世界")
separators=["\n\n", "\n", "。", ",", ";", ":", " ", ""] # 中文优先分隔符
)
chunks = text_splitter.split_text(text) # 生成文本块列表
- 分块效果:一份 10 页的 PDF 会被拆成 50~100 个文本块,每个块包含完整的语义(如一个段落、一个表格的某一行)。
2.1.3 分块元数据管理
为了后续追溯 "回答来自哪个 PDF",每个文本块会绑定元数据(来源文件、文档 ID):
python
# 为每个文本块生成唯一 ID(避免重复)
doc_id = f"doc_{int(time.time())}_{idx}" # idx 是文件序号
# 元数据:记录文本块来自哪个 PDF
current_file_metadatas = [{"source": file_name, "doc_id": doc_id} for _ in chunks]
2.2 向量存储与混合检索:快速找到 "相关内容"
RAG 的核心是 "检索"------ 当用户提问时,需从数百个文本块中快速找到最相关的内容。项目采用 "向量检索(FAISS)+ 关键词检索(BM25)" 的混合模式,平衡 "语义理解" 与 "精准匹配"。
2.2.1 文本向量化:SentenceTransformer 模型
文本块需转换为 "向量"(数字列表)才能被 FAISS 检索,项目使用轻量且高效的 all-MiniLM-L6-v2
模型:
python
# 初始化嵌入模型(全局唯一,避免重复加载)
EMBED_MODEL = SentenceTransformer('all-MiniLM-L6-v2')
# 批量生成文本向量(速度比单条生成快 5~10 倍)
embeddings = EMBED_MODEL.encode(all_new_chunks, show_progress_bar=True)
embeddings_np = np.array(embeddings).astype('float32') # 转换为 FAISS 支持的格式
- 向量维度 :
all-MiniLM-L6-v2
生成 384 维向量,1000 个文本块仅占用约 1.5MB 内存(轻量易存储); - 中文适配 :若处理纯中文文档,可替换为
shibing624/text2vec-base-chinese
(中文优化模型,需通过 HF 镜像下载)。
2.2.2 向量存储:FAISS 的 IndexFlatL2 索引
项目使用 FAISS 的 IndexFlatL2
索引(适合小规模向量库,检索速度快),存储向量并建立 "ID→文本" 的映射:
python
# 初始化 FAISS 索引(维度与嵌入模型一致,384 维)
dimension = embeddings_np.shape[1]
faiss_index = faiss.IndexFlatL2(dimension)
# 添加向量到索引
faiss_index.add(embeddings_np)
# 建立映射:文本块 ID → 原始文本/元数据(检索后快速获取内容)
faiss_contents_map[original_id] = chunk # original_id 是文本块唯一 ID
faiss_metadatas_map[original_id] = metadata
- 检索逻辑:用户提问时,先将问题转为向量,再通过 FAISS 计算 "问题向量" 与 "文本块向量" 的 L2 距离(距离越小越相关),返回 Top10 相关文本块。
2.2.3 混合检索:BM25 补向量检索的短板
向量检索擅长 "语义理解"(如 "手机续航" 能匹配 "电池容量"),但对 "关键词精准匹配"(如 "XPhone 15")效果弱。项目通过 BM25 索引补充:
python
class BM25IndexManager:
def build_index(self, documents, doc_ids):
# 中文分词(jieba 分词,比默认英文分词更精准)
self.tokenized_corpus = [list(jieba.cut(doc)) for doc in documents]
# 创建 BM25 索引
self.bm25_index = BM25Okapi(self.tokenized_corpus)
def search(self, query, top_k=5):
# 问题分词
tokenized_query = list(jieba.cut(query))
# 计算 BM25 得分(得分越高越相关)
bm25_scores = self.bm25_index.get_scores(tokenized_query)
# 返回 Top5 相关文本块
top_indices = np.argsort(bm25_scores)[-top_k:][::-1]
return [{"id": self.doc_mapping[idx], "content": self.raw_corpus[idx]} for idx in top_indices]
2.2.4 结果合并:alpha 权重平衡两种检索
项目用 hybrid_merge
函数合并两种检索结果,通过 alpha
控制权重(默认 alpha=0.7
,向量检索占 70%,BM25 占 30%):
python
def hybrid_merge(semantic_results, bm25_results, alpha=0.7):
merged_dict = {}
# 处理向量检索结果(按排名打分)
for i, (doc_id, doc, meta) in enumerate(semantic_results):
score = alpha * (1.0 - i/len(semantic_results)) # 排名越前得分越高
merged_dict[doc_id] = {"score": score, "content": doc, "metadata": meta}
# 处理 BM25 结果(归一化得分后叠加)
max_bm25_score = max([r["score"] for r in bm25_results]) if bm25_results else 1.0
for r in bm25_results:
normalized_score = (1 - alpha) * (r["score"] / max_bm25_score)
if r["id"] in merged_dict:
merged_dict[r["id"]]["score"] += normalized_score # 叠加得分
else:
merged_dict[r["id"]] = {"score": normalized_score, "content": r["content"]}
# 按总得分排序,返回 Top10 结果
return sorted(merged_dict.items(), key=lambda x: x[1]["score"], reverse=True)
2.3 对话生成与优化:让回答 "准且全"
检索到相关文本块后,项目需结合这些内容生成回答,同时解决 "信息不全""来源冲突""流式输出" 等问题。
2.3.1 递归检索:多轮优化查询
当首次检索结果不足时(如用户问 "XPhone 15 续航",仅检索到 "电池容量 5000mAh"),系统会自动生成新查询(如 "5000mAh 电池续航时间"),进行多轮检索:
python
def recursive_retrieval(initial_query, max_iterations=3):
query = initial_query
all_contexts = []
for i in range(max_iterations):
# 1. 执行混合检索
hybrid_results = hybrid_merge(...)
# 2. 收集检索结果
current_contexts = [r["content"] for _, r in hybrid_results]
all_contexts.extend(current_contexts)
# 3. 判断是否需要继续检索(调用本地模型分析信息是否足够)
next_query = get_next_query(initial_query, current_contexts)
if "不需要进一步查询" in next_query:
break
query = next_query # 用新查询继续检索
return all_contexts # 返回多轮检索的所有相关内容
2.3.2 重排序:交叉编码器提升精度
混合检索返回的 Top10 结果中,可能存在 "语义不相关" 的文本块(如关键词匹配但语义无关),项目用交叉编码器进一步筛选:
python
def rerank_with_cross_encoder(query, docs, doc_ids, metadata_list):
# 加载多语言交叉编码器(支持中文语义判断)
cross_encoder = CrossEncoder('sentence-transformers/distiluse-base-multilingual-cased-v2')
# 输入格式:[(问题, 文本块1), (问题, 文本块2), ...]
cross_inputs = [[query, doc] for doc in docs]
# 计算相关性得分(0~1,得分越高越相关)
scores = cross_encoder.predict(cross_inputs)
# 按得分排序,返回 Top5 结果
results = sorted(zip(doc_ids, docs, metadata_list, scores), key=lambda x: x[3], reverse=True)
return [(doc_id, {"content": doc, "metadata": meta, "score": score}) for doc_id, doc, meta, score in results[:5]]
2.3.3 流式回答:提升用户体验
项目支持流式输出回答(边生成边显示),避免用户等待过长时间,核心逻辑是调用 Ollama 的流式 API:
python
def stream_answer(question, enable_web_search=False):
# 1. 执行递归检索,获取上下文
all_contexts, _, _ = recursive_retrieval(question, enable_web_search=enable_web_search)
# 2. 构建提示词(包含检索到的上下文)
prompt = build_prompt(question, all_contexts)
# 3. 调用 Ollama 流式 API
response = session.post(
"http://localhost:11434/api/generate",
json={"model": AUXILIARY_MODEL, "prompt": prompt, "stream": True},
stream=True # 开启流式响应
)
# 4. 逐段返回回答
full_answer = ""
for line in response.iter_lines():
if line:
chunk = json.loads(line.decode()).get("response", "")
full_answer += chunk
yield full_answer, "生成回答中..." # 流式输出
# 5. 输出最终结果
yield process_thinking_content(full_answer), "完成!"
2.3.4 矛盾检测与来源标注
为避免 "多个来源信息冲突",项目会检测检索结果中的矛盾(如 A 文档说 "电池 5000mAh",B 文档说 "4000mAh"),并标注回答来源:
python
def detect_conflicts(sources):
key_facts = {}
for item in sources:
# 提取关键事实(如"电池容量:5000mAh")
facts = extract_facts(item["text"])
for fact, value in facts.items():
if fact in key_facts and key_facts[fact] != value:
return True # 检测到矛盾
key_facts[fact] = value
return False
# 回答末尾标注来源
prompt_template = """...
5. 在回答末尾标注信息来源{conflict_instruction}
请现在开始回答:"""
conflict_instruction = ",并明确指出不同来源的差异" if detect_conflicts(sources) else ""
三、部署测试:从启动到交互的完整流程
环境搭建和组件理解后,需通过实际操作验证项目是否正常运行。本节将以 "上传 PDF→提问→验证回答" 为例,提供完整的测试流程,并说明 Web 界面各功能的用法。
3.1 启动项目脚本
在项目根目录执行以下命令(确保虚拟环境已激活、Ollama 服务已启动):
bash
# Windows(PowerShell)
.\.venv\Scripts\python.exe rag_demo_pro.py
# Linux/macOS(终端)
python3 rag_demo_pro.py
启动成功的标志:
- 终端输出 "Gradio version: 4.44.1"(Gradio 界面启动成功);
- 自动打开浏览器,访问
http://127.0.0.1:17995
(端口从 17995~17999 自动选择未占用的); - 终端无 "模型未加载""端口被占用" 等错误提示。
3.2 Web 界面功能与操作
项目 Web 界面分为 "问答对话" 和 "分块可视化" 两个标签页,核心操作在 "问答对话" 页完成:
3.2.1 步骤 1:上传并处理 PDF
- 在 "文档处理区" 点击 "上传 PDF 文档",选择 1~3 个本地 PDF(建议单个文件不超过 20MB,避免处理超时);
- 点击 "🚀 开始处理",终端会显示处理进度(如 "处理文件 1/2: xxx.pdf");
- 处理完成后,"处理状态" 会显示 "总计处理 2 个文件,86 个文本块","已处理文件" 会列出所有成功处理的 PDF。
3.2.2 步骤 2:输入问题并提问
- 在 "输入问题" 框中输入具体问题(如 "这份产品手册中提到的 XPhone 15 电池容量是多少?");
- 可选配置:
- 启用联网搜索 :若 PDF 中无相关信息,可勾选 "启用联网搜索"(需配置
SERPAPI_KEY
); - 模型选择 :默认 "ollama"(本地模型),若本地硬件不足,可选择 "siliconflow"(云端模型,需配置
SILICONFLOW_API_KEY
);
- 启用联网搜索 :若 PDF 中无相关信息,可勾选 "启用联网搜索"(需配置
- 点击 "🔍 开始提问",对话记录区会流式显示回答,同时标注来源(如 "[本地文档: xxx.pdf]")。
3.2.3 步骤 3:分块可视化验证(可选)
若想确认 "回答来自哪个文本块",可切换到 "📊 分块可视化" 标签页:
- 点击 "🔄 刷新分块数据",表格会显示所有文本块的 "来源、字符数、内容预览";
- 点击某一行文本块,下方 "分块详情" 会显示完整内容,可验证是否与回答匹配。
3.3 测试场景设计与结果验证
为确保项目正常工作,建议设计以下 3 类测试场景,覆盖核心功能:
场景 1:基础检索测试(验证 PDF 处理是否正常)
- 操作:上传一份包含明确信息的 PDF(如 "2024 产品规格书.pdf",其中明确写有 "XPhone 15 电池容量 5000mAh"),提问 "XPhone 15 的电池容量是多少?";
- 预期结果:回答应包含 "5000mAh",并标注来源为 "[本地文档: 2024 产品规格书.pdf]";
- 失败排查:若回答 "未找到相关信息",需检查 PDF 是否处理成功("已处理文件" 中是否有该文件)、问题是否包含关键词(如 "XPhone 15" 是否在 PDF 中存在)。
场景 2:递归检索测试(验证多轮检索功能)
- 操作:上传同上的 PDF(仅包含 "电池容量 5000mAh"),提问 "XPhone 15 的电池容量能支持多久续航?";
- 预期结果:系统会先检索到 "电池容量 5000mAh",发现信息不足,自动生成新查询 "5000mAh 电池续航时间",若 PDF 中无续航信息,会提示 "未找到相关续航信息"(或启用联网搜索后返回网络结果);
- 失败排查 :若未触发递归检索,需检查
recursive_retrieval
函数的max_iterations
是否设为 3(默认值)。
场景 3:矛盾检测测试(验证信息可靠性)
- 操作:上传两份矛盾的 PDF(A 文档说 "XPhone 15 重量 180g",B 文档说 "XPhone 15 重量 200g"),提问 "XPhone 15 的重量是多少?";
- 预期结果:回答会标注 "不同来源存在差异",并分别列出 A、B 文档的信息及来源;
- 失败排查 :若未检测到矛盾,需检查
detect_conflicts
函数中 "关键事实提取" 逻辑(如是否正确提取 "重量" 这类数值事实)。
四、问题排查:新手常见坑与解决方案
Local_Pdf_Chat_RAG 项目的部署和使用中,新手常遇到 "模型调用失败""PDF 处理报错""Web 界面打不开" 等问题。本节整理了 8 类高频问题,提供 "症状→原因→解决方案" 的排查路径。
4.1 启动类问题:项目无法启动
问题 1:终端提示 "模型未加载!请先执行:ollama pull deepseek-r1:7b"
- 症状 :执行
python rag_demo_pro.py
后,终端报错,退出代码为 1; - 原因 :代码中
check_environment
函数默认检查MAIN_MODEL
对应的模型是否存在,若模型未拉取则报错; - 解决方案 :
-
执行
ollama list
查看已拉取的模型(确认是否有gpt-oss:120b-cloud
); -
若未拉取,执行
ollama pull gpt-oss:120b-cloud
; -
确保
check_environment
函数中检查的模型是MAIN_MODEL
(而非硬编码的deepseek-r1:7b
):python
model_check = session.post( "http://localhost:11434/api/show", json={"name": MAIN_MODEL}, # 改为全局变量,而非硬编码 timeout=10 )
-
问题 2:终端提示 "所有端口都被占用,请手动释放端口"
- 症状:项目尝试使用 17995~17999 端口,均被占用;
- 原因:其他程序(如浏览器、其他 Gradio 项目)占用了这些端口;
- 解决方案 :
-
手动指定未占用的端口(如 18000),修改主入口代码: python
if __name__ == "__main__": # 注释原有端口列表,手动指定端口 # ports = [17995, 17996, 17997, 17998, 17999] # selected_port = next((p for p in ports if is_port_available(p)), None) selected_port = 18000 # 手动指定端口 if not is_port_available(selected_port): print(f"端口 {selected_port} 被占用,请更换端口") exit(1)
-
重启项目,访问
http://127.0.0.1:18000
。
-
问题 3:浏览器打开后显示 "无法访问此网站"
- 症状 :终端显示 "Gradio running on http://0.0.0.0:17995",但浏览器访问失败;
- 原因 :
server_name="0.0.0.0"
允许局域网访问,但本地访问需用127.0.0.1
; - 解决方案 :
-
修改
demo.launch
配置,添加server_name="127.0.0.1"
:python
demo.launch( server_port=selected_port, server_name="127.0.0.1", # 本地访问 show_error=True, ssl_verify=False, height=900 )
-
重启项目,浏览器会自动打开
http://127.0.0.1:17995
。
-
4.2 PDF 处理类问题:无法上传或处理 PDF
问题 1:上传 PDF 后,"处理状态" 显示 "处理失败:文档内容为空或无法提取文本"
- 症状:上传 PDF 后,处理进度卡住,最终提示内容为空;
- 原因 :
- PDF 是扫描件(图片格式,无文本信息);
- PDF 被加密(需密码才能打开);
pdfminer
版本过低,无法解析新格式 PDF;
- 解决方案 :
- 验证 PDF 是否有文本:用 Adobe Reader 打开,尝试复制文本(若无法复制,即为扫描件);
- 若为扫描件:需先通过 OCR 工具(如天若 OCR、Adobe Acrobat)将图片转为文本 PDF;
- 若为加密 PDF:用工具解密(如 SmallPDF)后重新上传;
- 更新
pdfminer.six
:pip install --upgrade pdfminer.six
。
问题 2:处理 PDF 时,终端报错 "ModuleNotFoundError: No module named 'langchain_text_splitters'"
- 症状:点击 "开始处理" 后,终端报错,处理中断;
- 原因 :未安装
langchain-text-splitters
依赖(代码中用它进行文本分块); - 解决方案 :执行
pip install langchain-text-splitters==0.2.2
(指定版本,避免与其他库冲突)。
4.3 模型调用类问题:提问后无回答
问题 1:提问后,对话区显示 "系统错误:连接被拒绝"
- 症状:输入问题并点击 "开始提问" 后,报错 "连接被拒绝";
- 原因:Ollama 服务未启动,或服务端口被占用;
- 解决方案 :
- 检查 Ollama 服务是否运行:终端执行
curl http://localhost:11434
,若返回 "Ollama is running" 则正常; - 若服务未启动:
- Windows:打开 "任务管理器",找到 "ollama.exe" 并重启;
- Linux/macOS:终端执行
ollama serve
(保持终端开启);
- 若端口被占用:重启电脑,释放 11434 端口,再启动 Ollama 服务。
- 检查 Ollama 服务是否运行:终端执行
问题 2:提问后,终端报错 "ValueError: Invalid model name: gpt-oss:120b-cloud"
- 症状:调用模型时,终端提示模型名称无效;
- 原因:Ollama 中没有该模型(可能拉取失败,或模型名称拼写错误);
- 解决方案 :
- 执行
ollama list
确认模型是否存在(正确名称应为gpt-oss:120b-cloud
); - 若不存在,重新拉取:
ollama pull gpt-oss:120b-cloud
(拉取时确保网络稳定,避免中断); - 检查代码中
MAIN_MODEL
和AUXILIARY_MODEL
的拼写是否正确(避免多空格、少字符)。
- 执行
4.4 其他高频问题
问题 1:启动项目时,终端提示 "UserWarning: pkg_resources is deprecated as an API"
- 症状:启动时出现警告(不影响功能,但看着不舒服);
- 原因 :
jieba
库依赖 deprecated 的pkg_resources
; - 解决方案 :
- 升级
jieba
到最新版本:pip install --upgrade jieba
; - 若仍有警告,可忽略(是
jieba
库的问题,而非项目代码问题)。
- 升级
问题 2:启用联网搜索后,报错 "未设置 SERPAPI_KEY 环境变量"
- 症状 :勾选 "启用联网搜索" 并提问后,报错提示缺少
SERPAPI_KEY
; - 原因 :
.env
文件中未配置SERPAPI_KEY
,或配置后未生效; - 解决方案 :
- 打开
.env
文件,添加SERPAPI_KEY=你的密钥
(密钥从 SerpAPI 官网 申请,免费额度足够测试); - 重启项目(环境变量需重启项目才能生效);
- 若仍报错,检查代码中是否加载
.env
文件:确保load_dotenv()
函数在代码开头执行(如load_dotenv()
应在SERPAPI_KEY = os.getenv("SERPAPI_KEY")
之前)。
- 打开
总结
Local_Pdf_Chat_RAG 项目通过 "本地化部署" 解决了隐私泄露问题,通过 "RAG 技术" 解决了 PDF 检索效率问题,是新手入门 "本地文档对话" 的优质案例。从环境搭建到核心组件,再到部署测试与问题排查,核心是理解 "检索→增强→生成" 的 RAG 逻辑,以及 "本地化服务" 的依赖管理(如 Ollama 服务、HF 镜像)。
对于新手而言,无需一开始深究 FAISS 向量检索的底层算法,或大模型的生成原理,可先通过 "部署→测试→调优" 的流程建立直观认知,再逐步深入组件细节(如调整文本分块大小、优化检索权重 alpha
)。后续可尝试扩展功能,如支持 Word 文档、添加 OCR 处理扫描件,进一步提升项目的实用性。