共计 2068 个字符,预计需要花费 6 分钟才能阅读完成。
背景痛点
在日常工作和学习中,我们会积累大量的 PDF 文档,比如论文、电子书、技术手册等。传统的 PDF 管理方式往往依赖于文件名搜索或简单的关键词匹配,这种方式存在几个明显的局限性:

- 内容不可见性 :PDF 中的内容无法被直接搜索,除非使用专业的 PDF 阅读器
- 语义鸿沟 :关键词检索无法理解同义词和上下文关系(比如搜索 ” 机器学习 ” 不会返回包含 ”ML” 的文档)
- 结构缺失 :无法识别文档中的层次结构(如章节、图表等)
技术选型
在构建语义检索系统时,常见的方案有:
- 传统全文检索引擎 (如 Elasticsearch)
- 优点:成熟稳定,支持复杂查询
-
缺点:无法理解语义,需要手动配置分词器
-
ClawHub 方案
- 优点:
- 内置语义理解能力
- 轻量级,适合个人知识库
- 支持增量更新
- 缺点:
- 不适合超大规模数据集
- 需要自行处理 PDF 解析
核心实现
1. PDF 文本提取与清洗
我们使用 PyPDF2 进行基础文本提取,配合正则表达式清洗:
import PyPDF2
import re
def extract_text_from_pdf(pdf_path):
text = ""with open(pdf_path,'rb') as file:
reader = PyPDF2.PdfReader(file)
for page in reader.pages:
text += page.extract_text()
# 基础清洗
text = re.sub(r'\s+', ' ', text) # 合并多余空格
text = re.sub(r'\n+', '\n', text) # 合并多余换行
return text
2. 文本向量化
使用 Sentence-BERT 生成文本向量(需要先安装 sentence-transformers):
from sentence_transformers import SentenceTransformer
model = SentenceTransformer('paraphrase-multilingual-MiniLM-L12-v2')
def get_embeddings(text):
return model.encode(text)
3. 向量存储与检索
采用 FAISS 进行高效相似度搜索:
import faiss
import numpy as np
# 创建索引
dimension = 384 # MiniLM 模型维度
index = faiss.IndexFlatIP(dimension)
# 添加向量
vectors = np.array([...]) # 你的向量数组
index.add(vectors)
# 搜索
query_vector = get_embeddings("机器学习应用")
D, I = index.search(query_vector.reshape(1, -1), k=5) # 返回 top5 结果
完整代码示例
下面是整合后的批处理入库流程:
import os
from glob import glob
# 初始化
pdf_dir = "path/to/your/pdfs"
model = SentenceTransformer('paraphrase-multilingual-MiniLM-L12-v2')
index = faiss.IndexFlatIP(384)
documents = [] # 存储原始文本
# 批处理 PDF
for pdf_path in glob(os.path.join(pdf_dir, "*.pdf")):
text = extract_text_from_pdf(pdf_path)
vector = model.encode(text)
# 添加到索引
documents.append({"text": text, "path": pdf_path})
index.add(vector.reshape(1, -1))
# 保存索引和文档
faiss.write_index(index, "knowledge_base.index")
import pickle
with open("documents.pkl", "wb") as f:
pickle.dump(documents, f)
性能优化
- 索引构建 :
- 对小文档使用 IVFFlat 索引加速
-
对大文档使用 PCA 降维
-
查询优化 :
- 缓存频繁查询的结果
- 使用量化减少内存占用
避坑指南
- PDF 格式兼容性 :
- 扫描件需要先用 OCR 处理
-
对复杂版式 PDF,建议使用 pdfminer
-
中文分块策略 :
- 按段落分块(检测连续换行)
-
每块不超过 512 个 token
-
维度灾难预防 :
- 定期重建索引
- 监控搜索质量变化
扩展思考
可以结合 LLM 实现更智能的问答:
- 先用语义检索找到相关文档
- 将文档片段作为上下文喂给 LLM
- 让 LLM 生成总结或答案
架构流程图
flowchart TD
A[PDF 文档] --> B[文本提取]
B --> C[文本清洗]
C --> D[向量化]
D --> E[向量数据库]
F[查询] --> G[向量搜索]
E --> G
G --> H[结果排序]
H --> I[返回文档]
实践建议
现在你已经掌握了构建语义检索知识库的核心方法。不妨思考:
- 如何设计一个增量更新的机制,避免每次全量重建索引?
- 对于专业领域的文档,是否需要微调嵌入模型?
- 如何评估搜索质量的好坏?
期待看到你的实践成果!
正文完
