☰
docling:让RAG文档解析告别乱序与表格丢失的开源利器
2026/9/26 14:48:27 网站建设 项目流程

1. 为什么说文档解析是RAG链路里最容易被低估的一环

做RAG(检索增强生成)项目做得越久,我越有一个感受:大多数RAG效果不好,不是embedding模型选得差,也不是向量库不行,而是文档解析这一步就烂了。

你把一份PDF丢进去,解析出来的文本看着好像能用,实际上问题一大堆:双栏论文变成跨栏乱序、表格散成碎块、图表里的文字直接消失、页眉页脚混进正文。这些脏数据一旦进到chunk切割环节,再好的切分策略也救不回来。embedding模型本质上是对语义编码,喂给它的文本本身就是乱序的、割裂的,它怎么可能编码出有意义的向量?

有个很反直觉的结论是:很多团队在向量检索、重排、prompt优化上投入大量精力调优,最后发现精度提升的来源其实是换了一个更靠谱的文档解析器。这个现象在金融研报、学术论文、政府公文这类版式复杂的文档上尤其明显。

传统PDF解析器的思路基本上就是“把文本流按位置抽出来”,它不管页面里哪块是标题、哪块是正文、哪块是表格、图片里有没有字。而现代文档解析需要回答的是四个问题:

  • 页面上每个区域是什么类型(标题、正文、表格、图片、页眉页脚)?
  • 一个表格的完整结构长什么样,表头在哪,单元格之间什么关系?
  • 多栏、多块、多图文的版面里,正确的阅读顺序是什么?
  • 扫描件里没有文本层,怎么把图片里的字认出来?

这四个问题,正是docling这套开源工具重点要解决的。IBM把这套东西开源出来之后,在GitHub上热度涨得飞快,不是没有原因的——它把过去需要拼装七八个库才能完成的“重型文档解析”收拢成了一个开箱即用的工具链。

我最初注意到docling是因为一个实际场景:要给一批电力行业的设备说明书做知识库,这些PDF大多是老式扫描件,上面有大量铭牌照片、表格和带箭头的装配图。用pypdf抽出来基本是空白的,用OCR单跑文字又把表格结构全丢了。docling在这个场景里属于“开箱即用”地解决了问题,后面我会详细说它是怎么做到的。

这篇内容我会按我的实际使用路径来写:先讲它解决的核心问题,再讲安装和上手,然后是API集成和参数调优,接着和几个主流方案做个横向对比,最后放一些落地RAG时积累的踩坑经验。不管你是刚接触文档解析,还是已经在生产环境跑过几套方案,应该都能找到对你有用的东西。

2. docling的核心本事:版面识别、表格结构与阅读顺序

先说清楚docling到底有什么看家本领,这决定了你什么时候该选它。

2.1 版面分析:先分区块,再谈内容

docling处理一个PDF页面,第一步不是抽文字,而是做版面分析。它内部有专门的版面分析模型,能把页面划分成一个个带类型的区域块:正文段落、标题、表格、图片、公式、页眉、页脚、页码等。

这一步做得好的意义,远超“给文本加标注”这么简单。版面分析直接决定了后续所有环节的质量上限。如果模型把一列双栏正文当成一块连续文本抽取,那后面的阅读顺序也就彻底没救了。docling的版面分析在学术论文、技术手册这类典型场景下相当稳,分栏、跨栏标题、图注、表注都能区分开。

另一个容易被忽略的点是它识别出表格区域和图片区域之后,会走完全不同的处理管线:表格交给表格结构模型,图片里的文字交给OCR。区域分类一旦错了,对应的处理管线也就错了,后面的结果基本没法修正。

2.2 表格结构识别:TableFormer模型的实战表现

docling的表格识别在多个开源方案里算是第一梯队,这主要靠它集成的TableFormer模型。TableFormer输出的不是一段糊在一起的纯文本,而是一个带行列结构信息的树状结构,能还原出表头位置、单元格归属、跨行跨列关系,最后可以导出成HTML表格结构。

为什么这一步重要?给你看个对比就明白了。传统方案解析一个表格,得到的是这样的文本:

参数 数值 备注 输入电压 220V AC 单相 输出电压 24V DC 稳压

如果表格里还有嵌套结构、合并单元格,抽出来的文本就是一团乱麻,你根本不知道哪个单元格属于哪一行哪一列。而docling还原出来的是完整保留行列关系的结构化表格,转成Markdown之后长这样:

参数数值备注
输入电压220V AC单相
输出电压24V DC稳压

这个区别应用在RAG里非常直接——表格一旦结构完整,切chunk的时候就可以按行按块更有逻辑地切,检索时用户问“这台设备的输出电压是多少”,系统能准确锁定那一行,而不是在碎文本里大海捞针。

2.3 从物理版面到逻辑文档:阅读顺序才是灵魂

我见过不少解析工具,版面分析也做了,表格也识别了,但输出依然没法用。问题出在阅读顺序上。

一份双栏论文,物理排版顺序是“左栏从上到下,然后右栏从上到下”,但纯文本抽取常常变成“第一行左栏+第一行右栏+第二行左栏+第二行右栏”,读起来完全是乱的。docling内部维护了一个“文档逻辑树”(DoclingDocument),会把版面块按阅读逻辑重新排序,生成的是一个连贯的文档对象。

更关键的是,这个文档对象不是一次性输出成文本就完了,它保留了层级信息:哪句话属于哪个标题之下、哪个段落跟在哪个表格之后。这种结构化的中间表示,对RAG做chunk切分是极其友好的——你可以按标题层级去切分,也可以把标题和正文拼接起来,让每个chunk自带上下文。这一点后面的落地经验里我会展开讲。

2.4 OCR能力:扫描件靠什么识图识字

docling在OCR上不是自己重新发明轮子,而是设计了一套可插拔的OCR接口,支持EasyOCR、Tesseract以及一些云服务。底层逻辑是一样的:在版面分析识别出的文本区域、表格区域、图片区域里,用OCR把图像中的文字识别出来,再回填到对应的文档结构位置。

我实测EasyOCR在中文印刷体上效果不错,缺点是CPU环境下速度偏慢;Tesseract部署轻量但识别率略低。docling的好处是OCR引擎可以按场景替换,你不需要为了换OCR引擎重构整个解析管线。不过要泼一盆冷水:对扫描质量很差的文档,OCR识别recovery能力直接决定解析质量的下限,版面能恢复几成、图片里的数字能认对多少,这不只是docling一家的问题,而是所有依赖OCR的解析方案共同面临的挑战。我的建议是扫描件尽量保证原图清晰、方向正确,OCR环节别抱太多幻想,它解决的是“有没有”,不是“准不准”。

3. 先跑通再说:docling的安装和第一次命令行转换

讲完原理,直接上手跑一遍。docling的安装比我想象中简单得多,依赖没有想象中那么重。

3.1 环境准备与安装

docling要求Python 3.10及以上版本。我实测在Python 3.10和3.11下都正常,官方也声明支持3.12。强烈建议用一个独立的virtualenv或者conda环境装,因为它的依赖链包含了深度学习相关的库,跟系统Python混在一起容易出幺蛾子。

python -m venv docling-env source docling-env/bin/activate pip install docling

这一步会把docling本身以及它依赖的推理框架、OCR库、文档解析库一起装上。首次导入时会自动下载模型权重到~/.cache/docling/models目录,包括版面分析模型和TableFormer的权重,大概几百MB到1GB级别,取决于版本和模型组合。

如果下载卡住或者网络不好,模型文件会不完整,docling启动时可能会报奇怪的错误。我碰到过一次模型加载中断的情况,表现形式是报错信息里带“unexpected end of file”或者“file not found”,当时排查了半天,最后把~/.cache/docling/models整个目录删掉重新跑一次就好了。遇到这类问题别急着查代码,先考虑是不是模型缓存损坏。

3.2 命令行上手:一条命令输出Markdown和JSON

安装完成后,最简单的用法是命令行。准备一个PDF文件,然后执行:

docling example.pdf --to md --output ./output

--to md表示输出Markdown格式,--output指定输出目录。默认会在输出目录下生成一个example.md文件。转换过程中docling会在控制台打印当前处理的页面和耗时。第一次跑会比较慢,因为模型要加载到内存,后续页面处理速度会明显加快。

如果想同时拿到结构化JSON,可以这样:

docling example.pdf --to json --to md --output ./output

JSON输出是整个解析结果里最值钱的东西——它不是一段纯文本,而是包含了每个文本块、标题层级、表格HTML、图片引用、坐标信息等完整结构的对象。我在生产环境里基本都是取JSON,而不是取Markdown,因为Markdown适合人看,JSON适合程序处理。

3.3 第一次跑通了,但要注意这些基础表现

说几个我第一次跑完之后感受到的关键信息:

  • 纯CPU环境下,一份10页的普通PDF大概耗时20到50秒不等,取决于页面复杂度、是否启用OCR、机器性能。如果页面里全是表格和扫描图,时间会成倍增长。
  • GPU环境下速度提升明显,如果机器有NVIDIA显卡并且CUDA环境可用,docling会自动利用GPU推理,速度能快一个量级。对批量处理场景,GPU几乎是必需的。
  • docling对DOCX、PPTX、XLSX、HTML也有支持,不只是PDF。这一点我后面做文档库的时候用上了,一份构造复杂的Word流程图文件,docling也能解析出结构化的段落层级。

跑通这一个命令之后,你就可以把docling接入自己的处理流水线了。但想要用好它,不能只停留在CLI层面,下面进入API集成环节。

4. 程序化集成:DocumentConverter API与关键参数调优

生产环境基本不会用命令行一个个转文件,而是要写程序批量处理。docling的Python API设计得相当简洁,核心就一个类:DocumentConverter。

4.1 最简调用:转换文档为结构化对象

from docling.document_converter import DocumentConverter converter = DocumentConverter() result = converter.convert("example.pdf") document = result.document

这里的document就是一个完整的DoclingDocument对象。它包含了页面信息、文本块、表格、标题层级、阅读顺序等全套内容。你可以直接把它导出成Markdown、JSON或者HTML:

# 导出为Markdown文本 md_text = document.export_to_markdown() # 导出为JSON json_output = document.export_to_dict()

我实际项目里的用法是把JSON存下来,作为后续处理的中间产物。这样一个文档只需要解析一次,后续不管怎么改chunk策略、embedding模型,都不用重新跑解析,节省的时间相当可观。

4.2 关键参数:OCR开关与表格模式

DocumentConverter初始化时可以传入配置对象PipelineOptions,里面有几个参数直接影响解析效果。

from docling.datamodel.base_models import InputFormat from docling.datamodel.pipeline_options import PdfPipelineOptions from docling.document_converter import DocumentConverter pipeline_options = PdfPipelineOptions() # 启用OCR pipeline_options.do_ocr = True # 指定OCR引擎 pipeline_options.ocr_options.engine = "easyocr" # 表格识别模式 pipeline_options.table_structure_options.do_cell_matching = True converter = DocumentConverter(pipeline_options=pipeline_options)

do_ocr这个参数在扫描件场景下必须打开。但要注意一个坑:对于本身带文本层的PDF,如果强行开OCR,OCR识别结果会覆盖原始文本,反而可能降低准确率,尤其是数字、代码、特殊符号多的文档。

我的经验是:带文本层的文档直接用默认解析,扫描件才开OCR。更稳妥的做法是写代码先用工具检测PDF是否包含文本层,有文本层就关掉OCR,没有就打开,让整个处理流程自动化判断。

表格识别模式里的do_cell_matching控制是否把OCR识别出的文本与表格结构做单元格对齐。表格内容比较碎的时候,开这个可以提高表格还原度,但会增加耗时,要根据实际效果取舍。

4.3 批量处理与进度跟踪

实际处理一个文档库时,批量是标配。我通常这样写:

import json from pathlib import Path from docling.document_converter import DocumentConverter converter = DocumentConverter() input_dir = Path("./pdfs") output_dir = Path("./json_output") output_dir.mkdir(exist_ok=True) for pdf_path in input_dir.glob("*.pdf"): print(f"Processing: {pdf_path.name}") result = converter.convert(pdf_path) doc = result.document out_file = output_dir / f"{pdf_path.stem}.json" with open(out_file, "w", encoding="utf-8") as f: json.dump(doc.export_to_dict(), f, ensure_ascii=False, indent=2)

在处理上百份文档时,我强烈建议加上异常捕获和断点续跑。原因很现实:总有两三份PDF是损坏的、加密的、或者版式刁钻到模型都处理不了的,如果不做容错,整个批处理很容易跑一半挂掉,你还得手动找到到底是哪份文档引发的。

for pdf_path in input_dir.glob("*.pdf"): try: result = converter.convert(pdf_path) # 保存结果... except Exception as e: with open("failed.log", "a") as log: log.write(f"{pdf_path} - {e}\n") continue

这个简单的改动,能在处理几百份文档的时候省下大量的定位问题时间。日志里记录了失败文件名和异常原因,处理完一次性排查,比自己盯着控制台输出强太多。

4.4 按需提取:拿文本块、表格HTML和图片

DoclingDocument的export_to_dict()输出是全量结构,有时你只需要其中一部分内容。我总结几个高频的提取场景:

  • 只保留正文文本做全文检索:从文档的文本块中按阅读顺序拼接文本,不包含页眉页脚。
  • 单独提取所有表格:遍历文档对象,拿到每张表格的HTML结构或Markdown表示,存成结构化数据。
  • 提取文档里的图片:docling会把页面中的图片区域识别出来,你可以把图片单独导出,再配合OCR识别图片里的文字,补全信息。

这里的思路是:docling不是把你的文档变成一坨文本,而是变成一个可以按需要检索和提取的“内容库”。你的下游任务需要什么,就从这个库里取什么,而不是重新解析一遍。

5. 横向对比:它比pypdf、unstructured和PaddleOCR组合强在哪

说了一堆docling的好话,可能有人会问:那我直接pypdf抽文本,不够了再用PaddleOCR识别,效果不是也可以吗?这个观点我部分认同,但不完全赞同。做个横向对比会更清楚。

5.1 各方案的优势与局限

pypdf / pdfplumber:轻量文本抽取这类库简单直接,对版式工整、无复杂表格、纯文本型PDF效果不错,速度快、依赖少。但一旦遇到双栏、复杂表格、扫描件,就无能为力了。它们本质上是“文本流抽取器”,不是“版面理解器”。

unstructured:文档预处理全家桶unstructured做了很多文档解析的工程化工作,支持的语言多,和LangChain、LlamaIndex集成很顺。它的优势在于生态集成好,但在表格识别和版面理解上,我之前实测时感觉在复杂表格上表现不太稳定,有时候需要额外配置模型。

PaddleOCR:强大的OCR识别引擎PaddleOCR在中文识别上是出了名的强,配合版面分析模型也能做一定程度的还原。但它的定位是OCR引擎,不是完整的文档解析方案——你需要自己拼装检测、识别、方向分类、版面分析、表格识别这些模块,工程成本高,参数调优也需要经验。

docling:一站式文档视觉解析docling的策略是“一条命令完成版面分析+表格识别+OCR+阅读顺序还原”,它的核心优势在于整合度和结构化输出。直接给你一个带语义结构的DoclingDocument,而不是一堆半成品框和文本块。

5.2 一个直观的对比表格

维度pypdf/pdfplumberunstructuredPaddleOCR组合方案docling
版面分析无基础需要自行拼装内置模型,自动完成
表格结构还原无一般需额外模块强,TableFormer
OCR集成无可选核心能力可插拔,多引擎
阅读顺序还原无有限需自行处理自动按逻辑排序
结构化输出纯文本元素列表自定义DoclingDocument树
上手成本极低低高低
文档格式支持PDF为主多格式图像为主PDF+Word+PPT+HTML等

5.3 我的选型建议

结合我这几个月的实际使用经验,选型建议大概是这样的:

  • 如果只是要抽纯文本关键词,版式也很简单,用pypdf就够了,没必要上重型工具,杀鸡不用牛刀。
  • 如果文档版式复杂但都是电子版(带文本层),直接上docling,它比unstructured在表格处理上更省心。
  • 如果文档库以中文扫描件为主,且你已经部署了PaddleOCR的整套环境,那么PaddleOCR组合方案的表现确实很强,代价是维护成本高。
  • 如果是混合场景——有Word、有PDF、有扫描件、有表格也有图片,docling这种一站式方案明显更省事,这也是我最终主力使用它的原因。

这里多说一句:选型没有绝对的“谁替代谁”,更多是结合你面临的文档类型、算力条件和工程团队维护能力的综合取舍。docling的定位是“省事的工程化方案”,PaddleOCR的定位是“尽可能高的识别能力”,两者甚至可以在同一套系统里配合使用:docling负责整体解析,遇到OCR质量不佳的页面,再用PaddleOCR做局部增强。

6. 落地RAG时的真实经验:质量、速度与常见坑

最后一章,我结合自己跑下来的真实项目,写一些纯实操层面的经验和坑。这些如果没人告诉你,可能得踩好几轮才能摸清楚。

6.1 chunk切分:不要直接切解析出来的Markdown文本

这是我给所有做RAG的人最重要的一个建议。很多人辛辛苦苦把PDF解析成Markdown,然后直接用固定长度切chunk,这就把解析阶段的结构化优势全浪费了。

docling的JSON输出里有完整的标题层级关系。你在切chunk时,完全可以根据文档树结构来切:一个二级标题下包含的所有段落和表格,合并成一个语义完整的chunk;如果这个chunk长度超过阈值,再按段落边界切。这样每个chunk天然自带上下文,比盲目在固定字符数处硬切要好得多。

我实测下来,结构感知切分和固定长度切分在召回率上的差距大约在10%到20%之间,特别是对于表格穿插较多的技术手册,这个差距会更明显。

6.2 扫描件处理:先做图像预处理,别指望OCR万能

前面提到过,对于扫描件,OCR的效果直接决定解析质量。我的实际经验是:扫描件在喂给docling之前,先做一轮图像预处理,比在docling里反复调参数更有效。

预处理包括:

  • 方向校正:歪的页面先转正,识别率立刻上一个台阶。
  • 去阴影/去噪点:扫描件常见偏灰的底色,影响文字和背景的区分。
  • 提高对比度:字迹偏淡的文档,加强对比后OCR能多认出一批字符。

docling不是不能处理原始扫描件,但在粗糙输入下它的OCR准确率也确实会明显下降。给OCR一个好的输入图像,是所有OCR方案通用的前提,这一点放在docling身上同样适用。

6.3 表格误识别:复杂表格上要做人工抽检

虽然docling的表格识别能力不弱,但遇到特别复杂的表格——比如单元格里套单元格、大量合并单元格、跨页长表格——它依然会出现结构错乱。不要迷信任何解析工具在表格上的能力,这是目前文档解析领域公认的难点。

我的做法是:对每个文档库建一个抽检清单,人工抽查5%到10%的文档表格还原质量,重点看表头是否对齐、跨页表格是否被截断、合并单元格是否丢失。如果抽检发现问题,再回到解析参数和预处理环节去调整。比起一次性处理完几百份文档后才发现表格大面积出错,这个“抽检前置”的流程能帮你省下大量返工时间。

6.4 已知的坑:图片真的很大、模型缓存、解析时间

排几个实际跳过的坑:

图片存储可能远大于你的预期。有的PDF页面是高清扫描图,docling会把页面里的图片区域保存或引用出来,如果图片分辨率高、数量多,输出目录的体积会迅速膨胀。处理大文档库时,要评估存储成本,必要时限制导出图片的尺寸或只保存低分辨率版本。

模型缓存损坏问题。前文提过,模型文件下载不完整会导致解析报错。删除~/.cache/docling/models让它重新下载基本能解决。

解析时长的“文档差异”极大。一份纯文本PDF可能几秒钟完成,一份含大量扫描图和表格的PDF可能要好几分钟。批量任务设计时要考虑到这个差异,避免用平均耗时做进度预算,否则很容易出现在某几份文档上长时间卡住的情况。

6.5 生产化建议:解析层和索引层解耦

最后分享一个架构层面的经验:把文档解析做成独立服务,产出的JSON中间文件存起来,索引构建和查询都只依赖中间文件,不直接依赖解析脚本。

这样做的好处是:

  • 解析可以异步跑,不阻塞索引流程。
  • 换embedding模型、调整chunk策略时,不用重新解析原始文档。
  • 新来一个文档库时,可以先小批量解析,验证效果后再全量跑。

我在实际项目中的依赖关系很简单:原始文件进解析服务,流出带完整结构的JSON;索引服务读JSON,做chunk切分、向量化、入库;查询服务只读向量库和原始JSON。解析和索引完全解耦,出了问题也能定位到具体是哪个环节,排查起来比“一把梭”的脚本清晰太多。

docling给我的整体感觉是,它把过去需要自己折腾很久的“从PDF到干净结构化文本”这一跳,压缩成了一条非常顺畅的链路。虽然它在某些极端版式和低质量扫描件上仍然不算完美,但作为一个开源项目,它的开箱即用程度和结构化输出质量已经达到了可以直接上生产的水平。

如果你正被RAG的文档解析问题困扰,我建议你拿自己手头最头疼的那批文档,用docling跑一遍看看效果。可以重点观察两个地方:一是表格还原得完不完整,二是多栏版的阅读顺序对不对。这两个点过关了,其余的就只是装配和适配的问题了。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询