一份200页的财报PDF丢给AI,它把"第三季度营收"和页脚的小字备注搅成一团。你的RAG系统检索出来的内容,上下文是乱的,表格是碎的,连标题层级都分不清——这不是模型笨,是文档解析在拖后腿。
IBM今年开源的Docling解决了结构识别问题,能区分17种文档元素。但Python的原生实现让它在生产环境里像个吃内存的胖子。有团队用Rust把它整个重构了一遍,速度提到2.8倍,内存 footprint 压到原来的一小截。
从"能跑"到"能扛":生产环境的隐形门槛
Docling的RT-DETR v2模型(内部代号Heron)确实能打。标题、段落、表格、图表、页眉页脚、甚至脚注和题注,17类元素分得清清楚楚。开源协议是Apache-2.0,IBM这波没得黑。
但模型只是冰山一角。Docling本身是Python库,深度学习推理的标配生态。模型加载要时间,处理是单线程顺序跑,内存占用随文档复杂度线性膨胀。跑一篇论文没问题,跑一万份合同?你的流水线会卡在GIL(全局解释器锁)和Python的内存管理上。
更麻烦的是异构栈。如果你的核心服务用Go或Rust写的,插一个Python模块进去,等于在赛车引擎里塞了个家用发电机。容器镜像体积、冷启动延迟、依赖冲突,全是坑。
Kreuzberg团队的选择很直接:把Docling的模型抠出来, embedding 进Rust-native的流水线。不是 fork 代码改一改,是重写整个执行引擎——推理运行时、文本提取层、页面处理策略、表格重建管道,全换。
结果:同样的模型,不同的肉身。2.8倍速度,内存砍到"一小部分"(他们原话是a fraction,没给具体数字,但暗示是量级差异)。
ONNX Runtime:甩掉Python的GIL枷锁
关键切换在推理层。Docling原版用PyTorch,Kreuzberg换成了ONNX Runtime。这步操作砍掉了Python依赖,消灭了GIL竞争,内存管理交给Rust的所有权系统。
ONNX Runtime不是新东西,但用对地方才值钱。RT-DETR v2本身是个目标检测模型,输入页面图像,输出元素边界框和类别。ONNX的图优化能把模型算子 fuse 成更高效的执行计划,Rust端再配个线程池,多页并行处理。
Python的PyTorch在这类任务上有两个原罪:一是GIL让多线程变成伪并行,二是Tensor内存释放依赖垃圾回收,大文档容易OOM。Rust的所有权模型让内存生命周期显式可控,页面处理完立即释放,不拖泥带水。
团队没透露具体优化细节,但给了一个线索:他们的 pipeline 是"fully streaming"的。推测是页级流式处理,而不是整文档载入内存。这对百页以上的PDF是质变——你不需要等整个文件解析完,第一页的结构化数据先出来,下游系统先跑起来。
文本层重建:模型不管的脏活
布局模型只告诉你"这里有张表,边界坐标是(x1,y1,x2,y2)"。但表格里的文字怎么提取?单元格怎么对齐?跨页表格怎么拼接?这些是Docling原版的隐藏工作量,也是Kreuzberg重写的大头。
PDF的文本存储是个历史包袱。字符按绘制顺序存,不是阅读顺序。两栏布局的PDF,底层可能是"左栏第一行→右栏第一行→左栏第二行→右栏第二行"的乱序。更糟的是,有些PDF把表格画成一堆绝对定位的文本框,没有表格语义。
Kreuzberg的做法是分层:先用Docling的模型定位表格区域,再在区域内做文本提取和结构重建。文本层用了另一个Rust库(推测是pdf-extract或自研),把绘制指令还原成阅读顺序。表格重建则是个启发式算法,根据对齐线、间距、字体变化推断行列关系。
这部分没有开源模型可抄,全是工程脏活。团队的说法是"rebuilt from scratch",暗示Docling原版的表格处理逻辑被整个替换。
速度从哪来:三个杠杆的乘积效应
2.8倍不是单点优化,是三个杠杆的叠加:
杠杆一:运行时效率。ONNX Runtime的算子优化 + Rust的无GC内存管理,让单页推理更快、更稳。Python的PyTorch在这类CPU密集型任务上本就不是最优解。
杠杆二:并行架构。Rust的线程池可以真正并行处理多页,不受GIL限制。配合流式处理,CPU利用率能拉满。
杠杆三:内存策略。页级流式 + 显式内存释放,让内存占用和文档页数脱钩。处理1000页的文件,峰值内存和处理10页差不多。
团队特别强调"for production pipelines"——这不是实验室 benchmark,是扛过真实流量的数字。Docling原版在原型阶段够用了,但批量处理时的内存曲线和延迟抖动,是Kreuzberg想要抹平的东西。
开源生态的另一种玩法
这件事的微妙之处在于关系定位。Kreuzberg团队反复表态:Docling是great project,尊重IBM团队,模型是Apache-2.0的,我们用得光明正大。
这不是fork之后分道扬镳,是"模型层兼容,引擎层替换"的架构。对IBM来说,模型被更多项目采用是生态胜利;对Kreuzberg来说,借力成熟模型省去训练成本,专注工程优化。双赢,但前提是双方都认这个边界。
类似的模式在开源AIinfra里越来越常见。Llama.cpp用C++重写Llama的推理,Ollama再包一层;vLLM把PagedAttention塞进PyTorch的缝隙里。核心模型是公共品,执行引擎是竞争点。
Docling的Heron模型本身也在迭代。Kreuzberg的集成从v4.5.0开始,后续模型更新可以无缝跟进——只要ONNX格式兼容。这给他们留了一条后路,不用绑死在一个版本上。
一个值得追问的细节:Kreuzberg没公布内存占用的绝对数字,只说"a fraction"。是1/10还是1/3?处理100页PDF的P99延迟是多少?这些数字会决定它能不能真的挤进大厂的生产流水线,还是只在小团队里口碑相传。
热门跟贴