Unstructured — 把任意文档解析成 LLM 能吃的元素列表
已复核Unstructured 是一个专门把”乱七八糟的文档”压成”一串带类型的小块”的开源 Python 库。日常类比:你把一摞混在一起的纸(PDF 报告、Word 简历、PPT、网页打印件、邮件、扫描照片)扔给一个分拣员,他逐张读、贴标签——这是标题、这是正文段、这是列表项、这是表格、这是图——最后给你一份按顺序排好、每块都带”类别 + 页码 + 坐标”的卡片清单。
它解决的痛点很具体:要做 RAG,第一步永远是”把文档变成可切块的有序文本”。但25+ 种格式各有各的解析坑——PDF 有版面、PPTX 是 XML、邮件有头有正文有附件、扫描件还得 OCR。Unstructured 把这些坑集中到一处,对外只暴露一个 partition()。
最简一行:
from unstructured.partition.auto import partitionelements = partition(filename="报销政策.pdf")for el in elements: print(type(el).__name__, el.text[:60])输出会是 Title 公司差旅报销政策 / NarrativeText 第一条 ... / ListItem 出租车 ... / Table | 类别 | 上限 | ...——每个 Element 都是 Python 对象,有 .text、.category、.metadata.page_number 等字段。
不理解 Unstructured,下面这些事都没法解释:
- 为什么 langchain 和 llamaindex 的”默认文档加载器”列表里都摆着 Unstructured——它把上游脏活做深了,下游不愿意再造一遍轮子
- 为什么 RAG 项目卡在”PDF 解析质量”——纯
pdfplumber抽不出表格、PyMuPDF 抽不出版面层级,它把这些拼成一套 - 为什么”非结构化数据准备”不是一个
extract_text():文件探测、格式分发、元素类型和 metadata 都会影响下游 - 为什么它把策略显式分档(
fast/hi_res/ocr_only),而不是”自动适配”——延迟和精度只能由用户拍板
Unstructured 的处理流程可以拆成 三步:
-
Partition(识别 + 切分):
partition(filename=...)自动嗅探文件后缀和 magic number(文件开头几个字节的指纹,比如 PDF 是%PDF),分发到对应的子函数(partition_pdf/partition_docx/partition_html…),把整篇文档拆成有序的 Element 列表。类比:分拣员先看一眼文件是哪种格式,再用对应的拆封工具。 -
Element 类型化:每个 Element 有明确类别——
Title(标题)/NarrativeText(正文段)/ListItem(列表项)/Table(表格)/Image/FigureCaption/Header/Footer,并附带元数据:页码、bbox(bounding box,矩形边界框坐标)、parent_id(层级关系)、languages(语种)。这一步让”一份 PDF”变成”可程序化操作的有序结构”。 -
Chunking(按语义切块):
chunk_by_title把同一标题下的内容粘成一个 chunk;chunk_elements按 token 数硬切。这一步是为下游 embedding 准备等长且语义相对完整的输入——直接喂裸 Element 列表给向量化会切太碎,喂整篇又超 token。
案例 1:最简 RAG 前置
Section titled “案例 1:最简 RAG 前置”from unstructured.partition.auto import partitionfrom unstructured.chunking.title import chunk_by_title
elements = partition(filename="report.pdf")chunks = chunk_by_title(elements, max_characters=1000, combine_text_under_n_chars=200)逐部分解释:
partition(...):自动认格式,拆成带类型的 Element 列表(标题 / 正文 / 表格…)chunk_by_title(...):同一标题下的块粘在一起,并限制每块大约 1000 字符——方便下游 embedding(把文字变成向量的模型)吃下- 下一步把
chunks交给 llamaindex / langchain 的 vector store 即可
案例 2:hi_res 策略抠表格
Section titled “案例 2:hi_res 策略抠表格”默认 fast 会丢表格结构。扫描 PDF / 财报要显式升档:
elements = partition( filename="财报.pdf", strategy="hi_res", skip_infer_table_types=[],)逐部分解释:
strategy="hi_res":启用版面检测模型(detectron2 / yolox 一类),先找”哪里是表”skip_infer_table_types=[]:不跳过 PDF 表格结构抽取;旧的pdf_infer_table_structure在固定源码已标记弃用- 代价:需要版面/OCR 相关 extras 和系统依赖,延迟与资源消耗必须用自己的文档集测量
案例 3:元数据做 citation
Section titled “案例 3:元数据做 citation”for el in elements: print(el.text, "← page", el.metadata.page_number, el.metadata.coordinates)每个 Element 自带页码和 bbox(矩形框坐标)。RAG 回答后可指回原文——“来自第 3 页”——裸抽 text 做不到。页码像书签:检索命中后读者能翻回原页核对。
案例 4:按类型过滤正文
Section titled “案例 4:按类型过滤正文”Header / Footer / 页码会污染检索(每页重复”公司机密”)。按 Element 类型过滤:
narrative = [el for el in elements if type(el).__name__ in ("NarrativeText", "Title", "ListItem", "Table")]只有”已贴标签”的列表才能这样切——这是比单纯抽 text 多出来的核心价值。
- 依赖按格式变化:基础安装覆盖文本、HTML、XML、JSON 和部分邮件;PDF/图片、Office 等格式需要相应 extras 和系统依赖,不能把一台机器的安装清单推广到所有格式。
- 速度不能写死:
fast、hi_res、ocr_only的差距受文档、硬件、模型和批处理影响。生产线应记录 p50/p95、页失败率和峰值内存,而不是引用通用页速。 - 表格靠版面运气:合并单元格、跨页表、横向表时
text_as_html常错位;专业财报往往要换 LlamaParse 等专门工具。 - OCR 别乱开:文本型 PDF 先尝试直接抽取;只有扫描档或质量门失败时再进入 OCR 路径,并保留策略与工具版本。
适用 vs 不适用场景
Section titled “适用 vs 不适用场景”适用:
- RAG 管线的前置文档解析——25+ 格式统一一个
partition() - 需要按”标题 / 段落 / 列表 / 表格”语义切块的场景(学术论文、技术文档、合同条款)
- 需要保留版面元数据(页码、bbox)做 citation 回溯
- 图文混排文档——一次提取文字 + 表格 + 图片标题
- 接 langchain / llamaindex / haystack——这些框架都内置了 Unstructured loader
不适用:
- 纯文本日志 / CSV / JSON——直接
pandas/open(),杀鸡用牛刀 - 极致延迟(< 100 ms)的在线请求——hi_res 一页就要几百毫秒
- 需要完美 OCR 的扫描档——它的 OCR 是 tesseract 包装,专业场景该上 PaddleOCR / Azure Document Intelligence
- 完全离线 + 无 GPU 环境——hi_res 的版面模型在 CPU 上慢且吃内存
- 单一格式且量大——只解析 PDF 的话,PyMuPDF / pdfplumber 直接调更轻
固定版本边界
Section titled “固定版本边界”- 本文绑定
Unstructured-IO/unstructured@d309caf8...,提交日期为 2026-07-15,库版本为0.25.1。 - 固定版本要求 Python
>=3.11,<3.14,不同格式通过 optional dependencies 分开安装。 partition()保证文件探测与格式分发,不保证所有 Element 都有页码或 coordinates;metadata 取决于格式和策略。chunk_by_title()以 Title/metadata 边界组织 chunk,并支持字符或 token 上限;它不是摘要器。- 本文没有安装
all-docs、运行 PDF/OCR 或比较 parser,运行状态保持UNVERIFIED。
- 把脏活做深就是护城河——25+ 格式 × 3 种策略 × 元数据完整度,新框架很难一次抄齐
- 策略显式分档比”自动适配”更工程——
fast/hi_res/ocr_only让用户拍板 SLA - 下游生态决定上游存活——框架换默认 loader,护城河就会松,必须持续 co-evolve
- 元数据是未来杠杆——页码、bbox 当初像”顺手存”,后来 citation、版面感知 chunk 全靠它
partition()返回了文本,但没有coordinates。能否生成精确到页面区域的 citation?- 一批 PDF 同时包含文本型报告和扫描合同,是否应该全部固定用
ocr_only? chunk_by_title()输出长度合规,是否说明表格和阅读顺序也一定正确?
检查点:
- 不能。citation 粒度受实际 metadata 限制,缺坐标时只能降级或换策略。
- 不应。先做文件/质量分流,扫描档再走 OCR,并分别记录成本和失败率。
- 不能。chunking 消费上游 Element;解析结构错误会被原样带入下游。
- 官方文档:Unstructured Docs(按格式、按策略两条主索引)
- 源码起点:
unstructured/partition/auto.py——看清”自动嗅探 + 分发” - 固定源码:Unstructured-IO/unstructured —— 本文绑定提交
d309caf8ee20b735eb105d4e16ac3f04e5a48172 - 融资背景:TechCrunch 报道 Seed+A 轮
- langchain 的
UnstructuredFileLoader章节——看下游怎么消费 Element
- langchain —— 通用 LLM 框架,
UnstructuredFileLoader是常见文档加载器之一 - llamaindex —— RAG 框架,复杂格式下常会调到 Unstructured
- haystack —— 另一个把 Unstructured 当前置 loader 的 RAG 框架
- paddleocr —— 专业扫描档 OCR 替代,比内置 tesseract 更准
- vllm —— 下游生成侧;解析质量再好,也要接上能跑的推理引擎
(暂无反向链接)