一、为什么物流行业需要RAG?
物流行业涉及大量非结构化文档:运单规则、时效说明、理赔条款、仓储操作手册等。这些知识频繁更新,且属于企业内部私有数据,通用大模型无法覆盖。
两种主流私有知识处理方案对比:
| 方案 | 优点 | 缺点 |
|---|---|---|
| 模型微调 | 知识内化,推理快 | 成本高、周期长、更新困难 |
| RAG(检索增强生成) | 即插即用、知识可随时更新、成本低 | 检索质量决定回答质量 |
本项目选择RAG路线,基于FAISS向量数据库 + Ollama本地模型,构建了一个完全离线的物流行业智能问答系统。
二、RAG核心原理回顾
整个系统遵循经典的RAG三阶段架构:
用户提问 → 向量检索(相似度匹配)→ 检索结果注入Prompt → LLM生成回答
具体流程:
-
离线阶段:PDF文档 → 分块 → Embedding → 存入FAISS向量库
-
在线阶段:用户问题 → 向量化 → FAISS相似度搜索(Top-K)→ 拼接上下文 → LLM生成
三、知识库构建:
3.1 文档加载:PyMuPDFLoader
python
from langchain_community.document_loaders import PyMuPDFLoader
loader = PyMuPDFLoader("../物流信息.pdf")
data = loader.load()
PyMuPDFLoader 基于 fitz 库,相比 PyPDFLoader 速度更快,且能保留更多格式信息。加载后返回 List[Document],每个Document包含 page_content 和 metadata。
技术点 :如果PDF包含扫描图片(无文本层),需配合OCR方案(如 pytesseract),本项目假设为文本型PDF。
3.2 文本分块:RecursiveCharacterTextSplitter
python
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=50, # 每块最多50个字符
chunk_overlap=20 # 块间重叠20字符
)
split_docs = text_splitter.split_documents(data)
为什么分块很关键?
-
块太大 → 检索精度下降(包含太多无关信息)
-
块太小 → 上下文断裂,回答不完整
-
重叠机制:保证跨块边界的语义连续性
参数调优建议 :chunk_size=50 对于中文物流信息可能偏小(中文信息密度高),实践中可调整至 200-500 并配合实际效果测试。
RecursiveCharacterTextSplitter 的分割逻辑是递归尝试不同分隔符:["\n\n", "\n", "。", "!", "?", ";", ",", " ", ""],优先保留语义完整性。
3.3 Embedding模型:HuggingFaceEmbeddings + BGE-M3
python
from langchain_huggingface import HuggingFaceEmbeddings
embeddings = HuggingFaceEmbeddings(
model_name="D:/bge/bge-m3",
model_kwargs={'device': 'cpu'},
encode_kwargs={'normalize_embeddings': True}
)
为什么选BGE-M3?
-
BAAI开源的通用嵌入模型,在MTEB中文榜单中表现优异
-
支持稠密检索 + 稀疏检索 + 多向量,适合物流领域术语丰富的场景
-
normalize_embeddings=True启用余弦相似度计算(归一化后点积等价于余弦)
本地加载 :使用本地路径 "D:/bge/bge-m3" 避免了联网下载,适合内网或离线环境。
3.4 向量存储:FAISS
python
db = FAISS.from_documents(split_docs, embeddings)
db.save_local("../faiss/wuliu")
FAISS(Facebook AI Similarity Search)是高性能向量检索库,特点:
-
支持CPU/GPU加速
-
支持多种索引类型(Flat、IVF、HNSW)
-
save_local序列化索引到本地,无需重复构建
项目优势:相比Chroma,FAISS更轻量,不依赖外部服务,适合单机部署。
四、知识库构建流程图
┌─────────────────┐
│ 物流信息.pdf │
└────────┬────────┘
▼
┌─────────────────┐
│ PyMuPDFLoader │ → 提取文本 + 元数据
└────────┬────────┘
▼
┌─────────────────────────────────┐
│ RecursiveCharacterTextSplitter │ → chunk_size=50, overlap=20
└────────┬────────────────────────┘
▼
┌─────────────────┐
│ BGE-M3 Embedding│ → 768维向量(bge-m3默认1024维,可配置)
└────────┬────────┘
▼
┌─────────────────┐
│ FAISS向量库 │ → 保存至 ../faiss/wuliu
└─────────────────┘
五、踩坑与优化建议
| 问题 | 解决方案 |
|---|---|
| PDF中文乱码 | 检查系统字体,或在PyMuPDFLoader中指定encoding |
| 分块后语义断裂 | 适当调大chunk_size,使用更语义化的分割器(如SemanticChunker) |
| 向量检索不准确 | 换用更优质的Embedding模型(如bge-large、text2vec) |
| FAISS加载报错 | 添加allow_dangerous_deserialization=True(LangChain安全策略) |
小结(上篇)
上篇我们完整剖析了知识库构建链路:
-
PDF加载 → 文本分块策略 → Embedding模型选型 → FAISS向量存储
-
理解了每个环节的设计意图与调优方向
下篇我们将进入问答系统实现,从检索增强、Prompt工程到Streamlit WebUI,完整交付一个可交互的RAG应用。