用Kreuzberg提取文档结构
从PDF中提取结构化数据是AI基础设施中最难的问题之一。大多数工具只给你文本转储,没有标题、没有表格边界、没有区分标题和脚注。当Docling发布时,它用真正优秀的布局模型改变了游戏规则。
我们想明确——Docling是一个伟大的项目,我们对IBM团队将其开源表示最大敬意。它也是在宽松的Apache-2.0许可证下的完全开源项目。我们将他们的模型集成到Kreuzberg中,并将其嵌入到Rust原生管道中。目前,它的运行速度提高了2.8倍,内存占用仅为一小部分。
本文涵盖了幕后部分:我们使用了什么、我们从头重建了什么,以及速度的来源。
1、为什么文档结构对AI和RAG管道很重要
如果你正在构建AI基础设施,如RAG管道、文档处理工作流或任何大规模处理PDF的AI应用,扁平的文本提取已经不够了。
考虑一下当你将PDF作为单个文本块提供给LLM时会发生什么。模型无法区分节标题和正文。它无法判断一个数字是属于表格单元格还是脚注。它将多列布局合并为无意义的内容。整个管道的检索质量下降,因为源数据没有结构。
Docling,IBM的开源文档理解库,正面解决了这个问题。他们的RT-DETR v2布局模型(称为Docling Heron)分类17种不同的文档元素类型:标题、段落、表格、图表、标题、页眉、页脚等。它产生了一个下游系统可以实际使用的结构化表示。
模型非常出色。问题在于它周围的部分。
Docling是一个基于深度学习推理的Python库。模型加载需要时间。处理是顺序的。内存使用随文档复杂性而增加。对于单个文档或研究原型,这没问题。对于生产管道中的数千个文档,特别是如果你的技术栈不是Python的,它就开始重要了。这就是我们着手解决的差距。
2、基础
从Kreuzberg v4.5.0开始,我们将Docling的RT-DETR v2布局模型直接集成到我们的Rust原生管道中。该模型在Apache-2.0下开源,我们希望对其使用保持透明。Docling的团队构建了出色的东西。但模型只是文档提取系统的一部分。推理运行时、文本提取层、页面处理策略、表格重建管道——所有这些你现在都可以在Rust中使用。结果是一个使用Docling布局智能但通过完全不同的执行引擎运行的系统。
这就是工程差异所在。
3、工程管道
3.1 ONNX运行时用于布局推理
RT-DETR v2模型通过ONNX运行时运行,而不是Python的PyTorch。没有Python依赖、没有GIL争用,并且原生支持CPU、CUDA、CoreML和TensorRT等硬件加速。所有这些都可以通过类型化的AccelerationConfig配置,该配置在Kreuzberg支持的所有语言绑定中工作。
仅此一项就消除了冷启动惩罚。ONNX会话加载一次并保持驻留。
3.2 并行页面处理
布局推理在单个session.run()调用中处理页面批次。SLANet-Plus(表格结构识别模型)和布局推理都使用线程本地模型实例和Rayon工作者并行运行。每个页面独立处理并在提取后释放,即使在500页文档上也能保持内存使用平坦。
Docling通过Python顺序处理页面。Kreuzberg通过Rust并发处理它们。在100页PDF上,这种差异会快速累积。
3.3 通过PDFium的原生PDF文本提取
这是质量提升最大的地方,也是与Docling最大的架构差异。
Kreuzberg不是依赖布局模型的管道来处理文本提取,而是使用PDFium的字符级API直接从PDF的原生文本层读取文本。这保留了确切的字符位置、字体元数据(粗体、斜体、大小)和Unicode编码。然后布局模型根据文档的视觉结构对这些高保真文本进行分类和组织。
这种区分很重要,因为Docling的管道将渲染的页面图像视为布局检测和文本提取的主要输入。Kreuzberg仅将页面图像用于布局检测,然后从PDF的原生层提取文本。你获得了神经网络质量的结构分类和无损文本保真度。
3.4 结构树集成
当PDF包含标记结构树(常见于PDF/A和无障碍合规文档)时,Kreuzberg使用作者的原始段落边界和标题层次结构,然后应用布局模型预测作为分类覆盖。结构树给你作者的意图;布局模型给你视觉分类。两者结合产生的结果比单独任何一个都好。
修复PDF中的边缘情况
最大的单一质量改进不是来自布局模型集成,而是来自重写我们在字符级别从PDF提取文本的方式。
在v4.5.0之前,Kreuzberg使用PdfiumParagraph::from_objects(),这是一种段落级提取方法,依赖于PDFium内置的文本分割。它在干净的文档上有效,但在任何具有非标准字体矩阵、复杂列布局或损坏的CMap编码的文档上都会崩溃。而PDF充满了这些问题。
我们用PDFium的PdfPageText::chars() API替换了它。每个字符都以其确切位置、字体大小和基线坐标单独读取。从那里,我们自己重建文本结构。
这解锁了一系列在段落级别不可能实现的修复:
- 损坏的字体指标。 许多PDF由于字体矩阵缩放报告错误的字体大小。PDFium可能说font_size=1,而渲染的文本明显是12pt。我们旧的4pt最小过滤器会静默删除这些页面的所有内容。现在,当过滤器移除所有内容时,它会自动跳过。边距过滤的逻辑相同。当它移除页面上的所有文本时(基线值超出预期范围的PDF),过滤器会优雅地回退。
- 连字损坏。 带有损坏的ToUnicode CMap的LaTeX生成PDF会产生乱码文本:different变成di!erent,offices变成o"ces。我们在字符迭代期间使用元音/辅音启发式方法内联修复这些。在提取期间而不是作为后处理修复提高了准确性和性能。
- 单词间距伪影。 PDFium有时会在单词中间插入虚假空格——shall be active变成s hall a b e active。检测到损坏间距的页面使用字符级间隙分析重新提取(font_size × 0.33阈值)。干净的页面使用快速的单调用路径。在ISO 21111–10测试文档上,这将乱码行从406减少到零。
- 多列阅读顺序。 联邦公报风格的多列PDF在切换到PDFium的文本API后,F1从69.9%跃升至90.7%,它自然地处理列阅读顺序,而无需我们实现列检测启发式方法。
最终结果:Kreuzberg的PDF markdown提取在16个测试PDF上达到91.0%的平均F1,而Docling为91.4%。实际上持平,同时速度快10-50倍。
表格提取如何工作
表格提取分两个阶段运行。
首先,RT-DETR v2布局模型在页面图像上识别表格区域。然后,Kreuzberg裁剪每个检测到的区域并运行SLANet-Plus,这是一个专门的模型,预测内部表格结构:行、列、单元格,包括colspan和rowspan。
预测的单元格网格与原生PDF文本位置匹配以重建准确的markdown表格。这种混合方法——神经结构预测加原生文本提取——避免了仅使用渲染页面图像时的OCR式质量损失。
我们还收紧了检测启发式方法。表格检测现在需要至少3个对齐的列,这消除了来自两列文本布局(如学术论文和新闻通讯)的误报。后处理拒绝2列或更少的表格、超过50%的单元格包含长文本的表格或平均单元格长度超过50个字符的表格。这些规则在不损害召回率的情况下显著减少了误报检测。
基准测试:我们如何测量
我们在171个PDF文档上对Kreuzberg进行了基准测试,涵盖学术论文、政府和法律文件、发票、OCR扫描和边缘情况。F1分数测量精确率和召回率的调和平均值,以及预期内容有多少被正确提取,以及提取的内容中有多少是实际正确的。
指标 Kreuzberg Docling
结构F1 42.1% 41.7%
文本F1 88.9% 86.7%
平均处理时间 1,032 ms/文档 2,894 ms/文档
2.8倍的速度优势来自四个方面:Rust的原生内存管理、PDFium字符级文本提取(无Python开销)、ONNX运行时推理(无PyTorch)和Rayon跨页面并行。结构F1测量文档元素(如标题、段落和表格)的检测准确性。
在更广泛的基准测试中,我们将Kreuzberg与Apache Tika、Docling、MarkItDown、Unstructured.io、PDFPlumber、MinerU、MuPDF4LLM等进行了比较。在那里,你可以看到Kreuzberg平均快得多,内存使用率更低,安装体积更小。Docker镜像约为1-1.3GB,而Docling的1GB+ Python安装甚至还没有添加你的应用程序代码。
这对你的技术栈意味着什么
**已经在使用Docling并对质量满意?**你将从Kreuzberg获得同等的提取准确性,但基础设施开销更少。布局模型相同,执行更快,内存更低。
**运行多语言技术栈?**如果你的后端是Rust、Ruby、Java、Go、PHP、Elixir、C#、R、C、TypeScript(Node/Bun/Wasm/Deno),Kreuzberg为你提供相同的布局检测能力,而无需在HTTP端点后面包装Python服务。12种语言的原生绑定,底层是相同的Rust核心。
**大规模处理?**并行页面处理、原生文本提取和高效ONNX推理的组合意味着在相同硬件上显著更高的文档吞吐量。布局检测不需要GPU;CPU推理对于大多数生产工作负载来说足够快。
布局检测在所有12种语言绑定、CLI、REST API和MCP服务器中都可用。模型在首次使用时从HuggingFace自动下载并本地缓存。
开始使用
CLI
kreuzberg extract document.pdf — layout-detection
Python
from kreuzberg import extract_file, ExtractionConfig
result = await extract_file("document.pdf", ExtractionConfig(
layout_detection=True,
output_format="markdown"
))
文档结构提取正在成为生产AI管道的标准。现代AI系统依赖于结构化文档数据。提取数据越快,你的管道就越可扩展。
我们感谢IBM的Docling团队提供的真正伟大的基础。如果你今天在生产中运行Docling,请在你的实际文档上尝试Kreuzberg与之比较,并告诉我们你的想法。
原文链接: Document Structure Extraction with Kreuzberg
汇智网翻译整理,转载请标明出处