1. 项目概述:用Python和LangChain构建RAG知识库
作为一名长期从事AI应用开发的工程师,我经常遇到需要快速处理非结构化文档的场景。RAG(检索增强生成)技术正成为连接大模型与专业领域知识的关键桥梁,而LangChain作为当前最流行的AI应用开发框架,为Python开发者提供了极简的文档处理工具链。
这个教程将带新手从零开始,完成三个核心目标:
- 用LangChain加载PDF/Word/TXT等常见格式的文档
- 实现文本分块、向量化等标准化处理
- 搭建可对接大模型的RAG基础架构
整个过程不需要NLP或机器学习基础,只要会Python基础语法就能跟着操作。我会分享自己在大规模文档处理中积累的6个关键技巧,帮你避开90%的初学者陷阱。
2. 环境准备与工具选型
2.1 开发环境配置
推荐使用Python 3.8+环境,这是目前与LangChain兼容性最好的版本。安装只需一条命令:
bash复制pip install langchain python-dotenv tiktoken pypdf unstructured
注意:建议在虚拟环境中操作,避免包冲突。我习惯用conda创建独立环境:
bash复制conda create -n rag python=3.8 conda activate rag
2.2 文档加载器对比选择
LangChain支持20+文档加载器,根据我的实战经验,这几个最实用:
| 文件类型 | 推荐加载器 | 特点 |
|---|---|---|
| PyPDFLoader | 轻量但可能丢失复杂格式 | |
| Word | UnstructuredWord | 保留格式但需额外依赖 |
| HTML | BSHTMLLoader | 自动清理网页噪声 |
| 纯文本 | TextLoader | 直接读取无处理 |
对于中文文档,强烈建议添加unstructured[local-inference]扩展:
bash复制pip install "unstructured[local-inference]"
它能显著提升中文标点和段落识别的准确率。
3. 文档加载实战演示
3.1 基础加载流程
以处理项目说明书PDF为例,典型代码如下:
python复制from langchain.document_loaders import PyPDFLoader
loader = PyPDFLoader("specification.pdf")
pages = loader.load_and_split()
这里有个关键细节:load_and_split()默认按页分割,但技术文档往往需要按章节划分。我的改进方案是:
python复制from langchain.text_splitter import RecursiveCharacterTextSplitter
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=1000,
chunk_overlap=200,
separators=["\n\n", "\n", "。", "!", "?"]
)
docs = text_splitter.split_documents(pages)
3.2 中文文档特殊处理
中文文档需要特别注意:
- 添加中文标点到分隔符
- 调整chunk_size到500-800字(英文的1/2到1/3)
- 使用
ChineseTextSplitter等定制化工具
这是我优化后的配置:
python复制custom_splitter = RecursiveCharacterTextSplitter(
chunk_size=600,
chunk_overlap=100,
separators=["\n\n", "\n", "。", "!", "?", ";", "…"]
)
4. 文档标准化关键步骤
4.1 文本清洗策略
原始文档常包含干扰元素,需要多层清洗:
- 去除不可见字符
- 统一全角/半角符号
- 合并连续空行
python复制import re
def clean_text(text):
text = re.sub(r'[\x00-\x1F\x7F]', '', text) # 控制字符
text = re.sub(r'[ ]+', ' ', text) # 全角空格
text = re.sub(r'\n{3,}', '\n\n', text) # 多空行
return text.strip()
4.2 元数据标准化
良好的元数据能提升检索效果,建议包含:
- 来源文件名
- 创建时间戳
- 文档类型标签
- 关键实体标记
python复制from datetime import datetime
for doc in docs:
doc.metadata.update({
"processed_at": datetime.now().isoformat(),
"doc_type": "technical_spec",
"language": "zh-CN"
})
5. 向量化与RAG集成
5.1 嵌入模型选择
对于中文场景,这些模型表现较好:
| 模型名称 | 特点 | 推荐场景 |
|---|---|---|
| text-embedding-3-small | OpenAI最新小模型 | 通用场景 |
| bge-small-zh | 专为中文优化 | 专业领域 |
| m3e-base | 开源可商用 | 本地化部署 |
配置示例:
python复制from langchain.embeddings import OpenAIEmbeddings
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
5.2 向量数据库接入
最简单的方案是使用Chroma内存数据库:
python复制from langchain.vectorstores import Chroma
vector_db = Chroma.from_documents(
documents=docs,
embedding=embeddings,
persist_directory="./chroma_db"
)
生产环境推荐改用Weaviate或Milvus:
python复制# Weaviate配置示例
from langchain.vectorstores import Weaviate
import weaviate
client = weaviate.Client(url="http://localhost:8080")
vector_db = Weaviate.from_documents(
docs,
embeddings,
client=client,
by_text=False
)
6. 常见问题排查指南
6.1 编码问题解决方案
遇到编码错误时,按这个流程排查:
- 先用
chardet检测实际编码 - 尝试常见中文编码:
python复制encodings = ['utf-8', 'gb18030', 'gbk', 'big5'] for enc in encodings: try: with open(file, 'r', encoding=enc) as f: return f.read() except UnicodeDecodeError: continue - 终极方案:用二进制读取后替换
python复制with open(file, 'rb') as f: content = f.read().decode('utf-8', errors='replace')
6.2 性能优化技巧
处理大量文档时,这些方法能提升10倍以上速度:
- 使用
UnstructuredFileLoader的mode="elements"参数 - 启用多线程加载:
python复制loader = DirectoryLoader( "./docs", glob="**/*.pdf", loader_cls=PyPDFLoader, use_multithreading=True ) - 对纯文本禁用冗余分析:
python复制loader = UnstructuredFileLoader("doc.txt", mode="single")
7. 进阶开发建议
当基础流程跑通后,可以尝试这些增强功能:
- 添加文档预处理流水线(OCR识别、表格提取)
- 实现增量更新机制(监控文件夹变动)
- 加入语义分块策略(利用NLP模型识别段落边界)
一个实用的增量更新实现:
python复制from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler
class DocHandler(FileSystemEventHandler):
def on_modified(self, event):
if event.src_path.endswith(".pdf"):
update_vector_db(event.src_path)
observer = Observer()
observer.schedule(DocHandler(), path='./docs')
observer.start()
我在实际项目中发现,结合LangChain和FastAPI搭建的文档处理服务,配合适当的缓存策略,可以支撑日均10万级文档的处理需求。关键是要根据文档特点调整分块策略——技术文档适合按章节划分,而合同类文档则应按条款切分。
