使用 Unstructured.io 与 Elasticsearch 向量数据库:高效搜索复杂文档的新方案
适合从事智能文档分析、法律科技、金融报告处理、企业知识搜索等行业开发者,构建能处理 PDF、Word、HTML、邮件等复杂文档的新一代搜索系统。
目录
- 为什么需要处理复杂文档?
- Unstructured.io 简介与优势
- Elasticsearch 向量数据库简介
- 整体架构图解:复杂文档 → 搜索引擎
- 文档处理流程与向量生成
- Elasticsearch 向量索引配置与搜索
- 完整实战代码示例:从文档到搜索结果
- 常见问题与性能优化
- 总结与推荐实践
一、为什么需要处理复杂文档?
企业中存在大量结构不清晰、跨格式的文档,如:
- 合同(PDF、DOCX)
- 技术手册(HTML、PPT)
- 邮件(.eml)
- 扫描件(OCR图像)
传统全文检索系统的难点:
- 格式繁多,解析复杂
- 内容结构嵌套,无法按段搜索
- 用户问题常以自然语言提出,需要语义匹配
因此,需要:
- 统一抽取内容
- 按段生成向量
- 在向量数据库中进行语义检索
二、Unstructured.io 简介与优势
Unstructured.io 是一个文档结构化开源工具,支持多种格式统一提取。
支持格式
类型 | 示例 |
---|---|
文档 | PDF, DOCX, PPTX |
网页 | HTML |
邮件 | .eml, .msg |
图像 | PNG, JPG(带OCR) |
输出格式
每段内容被提取为 JSON 对象,附带元信息(位置、页码、类型等):
{
"type": "NarrativeText",
"text": "本合同适用于...",
"metadata": {
"page_number": 3,
"element_id": "uuid-1234"
}
}
特点
- 基于分段(chunk)思想提取内容
- 自动识别结构:标题、表格、图像、正文等
- 可用于向量搜索预处理
三、Elasticsearch 向量数据库简介
Elasticsearch 自 8.x 起原生支持向量字段,支持:
- 精确 kNN 与近似 kNN(HNSW)
- 向量维度最大 2048
dense_vector
字段 +knn
查询
常配合 Embedding
模型实现语义搜索:
- 文本 → 向量(通过模型)
- 向量 → Elasticsearch 检索
四、整体架构图解(文字描述)
+------------------+
| PDF/DOCX 文件等 |
+--------+---------+
↓
+------------------+
| Unstructured.io | ← 文档结构提取 & 分段
+--------+---------+
↓
+------------------+
| Embedding 模型 | ← 将段落转为向量(如 BGE/MPNet)
+--------+---------+
↓
+------------------+
| Elasticsearch 向量索引 |
+------------------+
↓
+------------------+
| 自然语言查询 → 搜索 |
+------------------+
五、文档处理流程与向量生成
5.1 使用 unstructured
提取文档结构
安装:
pip install unstructured
解析 PDF 示例:
from unstructured.partition.pdf import partition_pdf
elements = partition_pdf("contract.pdf")
for el in elements:
print(el.text, el.metadata.page_number)
5.2 使用嵌入模型转向量
安装 HuggingFace 模型:
pip install sentence-transformers
示例:
from sentence_transformers import SentenceTransformer
model = SentenceTransformer("BAAI/bge-base-zh")
vectors = [model.encode(el.text) for el in elements if el.text.strip()]
六、Elasticsearch 向量索引配置与搜索
6.1 映射配置
PUT /document_index
{
"mappings": {
"properties": {
"text": { "type": "text" },
"embedding": {
"type": "dense_vector",
"dims": 768,
"index": true,
"similarity": "cosine",
"index_options": {
"type": "hnsw",
"m": 16,
"ef_construction": 128
}
},
"page": { "type": "integer" },
"file_id": { "type": "keyword" }
}
}
}
6.2 向量写入示例
from elasticsearch import Elasticsearch
es = Elasticsearch()
for i, el in enumerate(elements):
if el.text.strip():
doc = {
"text": el.text,
"embedding": vectors[i],
"page": el.metadata.page_number,
"file_id": "contract_2025"
}
es.index(index="document_index", document=doc)
七、完整实战代码流程(简化版)
from unstructured.partition.pdf import partition_pdf
from sentence_transformers import SentenceTransformer
from elasticsearch import Elasticsearch
# 文档提取
elements = partition_pdf("contract.pdf")
# 文本向量化
model = SentenceTransformer("BAAI/bge-base-zh")
texts = [el.text for el in elements if el.text.strip()]
vectors = model.encode(texts)
# 写入 Elasticsearch
es = Elasticsearch()
for i, el in enumerate(elements):
if el.text.strip():
es.index(index="document_index", document={
"text": el.text,
"embedding": vectors[i],
"page": el.metadata.page_number,
"file_id": "contract_2025"
})
八、自然语言搜索示例
用户输入:“合同中关于违约责任的条款是什么?”
搜索代码
query = "违约责任条款"
query_vector = model.encode(query)
resp = es.search(index="document_index", knn={
"field": "embedding",
"query_vector": query_vector.tolist(),
"k": 5,
"num_candidates": 100
})
for hit in resp["hits"]["hits"]:
print(hit["_score"], hit["_source"]["text"])
九、常见问题与优化建议
问题 | 原因 | 解决方式 |
---|---|---|
查询不准 | 向量召回数过低 | 调大 num_candidates |
PDF 无法读取结构 | PDF 不规范 | 使用 pdfplumber 替代解析 |
写入慢 | 向量维度大 + 网络延迟 | 批量 bulk 写入 |
查询慢 | dense_vector 无索引 | 设置 index: true 并使用 HNSW |
十、总结与推荐实践
模块 | 推荐工具/配置 |
---|---|
文档解析 | unstructured |
文本嵌入 | BAAI/bge-base-zh , sentence-transformers |
向量搜索 | Elasticsearch 8.x + HNSW |
查询接口 | REST API / LangChain 接入 |
扩展能力 | 可集成 OCR、表格结构提取等模块 |
评论已关闭