熟悉RAG工程的朋友应该都有这种体会:真正让人头疼的往往不是模型效果,而是喂给模型的数据。PDF表格提取得七零八落、打印扫描件复制出来全是乱码、好不容易抽出文字阅读顺序又是乱的——这些问题在项目初期简直能把人逼疯。我这段时间一直在折腾文档智能解析,手头用了不少工具,最后真正留下来并且想认真写一写的,就是IBM开源的docling。
docling是一套面向文档解析与结构化输出的开源工具集,它能把PDF、Word、PPT、Excel甚至图片这类非结构化文件,批量转换为带有语义结构的Markdown和JSON。它不是简单地把文字“抠”出来,而是连版面布局、表格结构、标题层级、阅读顺序一起还原。尤其适合RAG知识库、企业文档数字化、金融报告解析这类对数据质量要求比较高的场景。如果你正在搭知识库,或者做文档问答,又或者被手工处理一堆PDF表格搞到崩溃,这篇内容应该能帮你省下不少时间。
1. docling是什么:定位、对比与典型场景
1.1 一句话说清docling的定位
普通PDF解析工具干的事情,是把PDF当作一个“文本容器”,用底层库一页一页把字符读出来。docling的思路完全不一样,它把文档当作“视觉对象”来理解:先识别页面上的每个区块是什么,哪个表格有多少行多少列,标题和小节到底谁属于谁,正文的阅读顺序应该是怎样的,再把这一堆信息重新组织成结构化数据。这种从视觉到语义的处理方式,恰恰是RAG数据摄入最需要的。
我自己的感受是,docling的定位非常准确:它不是要取代PDF渲染引擎,也不是要做成在线预览工具,而是专注在“文档理解+结构化输出”这一段。它给开发者吐出来的是干干净净的Markdown和JSON,方便你直接接进下游系统。就好比一个整理师,把一堆乱七八糟的快递箱拆开、分类、贴好标签、按顺序码好,你拿到的不是快递纸壳,而是可用的物品清单。
1.2 和同类工具对比起来,优势在哪儿
为了说清楚docling的价值,我把常见的几个方案放在一起捋过一遍,包括PyMuPDF、pdfplumber、Unstructured,还有LlamaParse。这里不拉踩,只聊实际使用中的差异性:
| 工具 | 开源/费用 | 结构化程度 | 表格能力 | 复杂版式 | 适合场景 |
|---|---|---|---|---|---|
| PyMuPDF | 开源免费 | 低,偏文本流 | 一般 | 弱 | 简单文本提取、PDF操作 |
| pdfplumber | 开源免费 | 低,偏坐标 | 中 | 弱 | 简单表格、坐标定位 |
| Unstructured | 开源+商业版 | 中 | 中 | 中 | 多格式基础清洗 |
| LlamaParse | 闭源/按量付费 | 高 | 强 | 强 | 云端管道,不介意数据上云 |
| docling | 开源免费(Apache 2.0) | 高 | 强 | 强 | 本地化、高语义结构化解析 |
对比之后你会发现,docling的优势集中在三点:
第一,输出信息密度高。它不止给你文字,还把每个块的类型、坐标、层级关系都放在JSON里。比如“这是一个三级标题”“这段是表格的第2行第3列”“这段正文属于上一级标题的章节内容”,这些东西对下游切分和检索非常有价值。
第二,完全本地化运行。文档数据不用传给别人,所有模型推理都在自己机器上完成,对数据敏感的企业场景特别友好。隐私合规这条,有时候比模型效果还重要。
第三,多格式一把抓。PDF、DOCX、PPTX、XLSX、HTML、图片都能转,不用为每一种文件类型单独接一套解析方案,管线会省心非常多。
1.3 两个最典型的落地场景
我实际做过两类项目,恰好能把docling用得很舒展。
第一类是知识库问答。企业内部的规章制度、产品手册、售后文档,经常同时有PDF和Word版本,排版复杂,还有大量表格。之前用普通解析库抽出来的内容,切分时经常把表格拦腰截断,或者把多栏版面按错误顺序拼接,问出来的答案驴唇不对马嘴。换docling之后,输出里的标题层级和阅读顺序都是对的,知识库检索精度明显上来了。
第二类是财报和合同解析。金融文档里表格密度极高,合并单元格、跨行表头特别常见。docling对表格结构的识别能力比普通文本流方案强很多,转出来的Markdown表格基本可以直接喂给模型做结构化抽取,配合JSON里的坐标信息,还能做进一步的后处理校验。
2. docling安装教程:命令行和Python API两个上手路径
2.1 先装好环境:依赖与安装细节
docling是Python包,安装本身不复杂,但有几个前提条件建议先准备好。Python版本建议用3.10及以上,我实测在3.10和3.12下都很稳定,太老的版本容易遇到依赖冲突。
强烈建议在虚拟环境里装,不要直接怼进系统Python。因为docling会带上一堆依赖,包括PyTorch系列库,和现有项目里固定版本的torch很容易打架。我习惯用conda先建一个干净环境再操作:
conda create -n docling-env python=3.12 -y conda activate docling-env pip install docling执行完这一条之后,docling会把它依赖的深度学习模型框架、OCR引擎、PDF解析后端等都拉下来。首次跑命令时还需要下载模型权重,所以第一跑请务必在网络稳定的条件下进行。如果你在离线环境里用,需要提前在有网机器上把模型缓存准备好,再拷贝过去,不然会卡在加载那一步。
安装完成后,验证一下要不要额外装OCR组件。docling的OCR能力依赖EasyOCR或DocTR,如果文档里有扫描图片,需要按需安装对应依赖。你如果只是处理电子版PDF,默认配置已经够用,暂时先不用管OCR。
2.2 命令行模式:一条命令把PDF变成Markdown
docling自带命令行工具,这也是我最早体验它的方式。把PDF转成Markdown,一条命令搞定:
docling my_document.pdf --to md --output ./output_dir运行完去output_dir里看,会生成一个同名Markdown文件。打开看里面的表格,已经是标准的Markdown表格语法,不是那种用空格硬对齐的伪表格。这就是深度表格识别模型的功劳,和普通PDF文本抽取完全是两码事。
常用参数里,几个比较关键的我列一下:
--to md:输出格式,支持md、json、text等。--from:输入类型,docling会自动判断,一般不用显式指定。--ocr:启用或关闭OCR,默认是true还是false取决于输入格式,扫描版PDF记得显式打开。--no-ocr:明确禁用OCR,处理纯电子版PDF时能省不少时间。--image-export-mode:控制文档里的图片怎么导出,可选placeholder、embedded等。--output:输出目录。
我第一次用的时候,下意识以为Markdown只是给人读的,后来才发现docling生成的Markdown还会保留标题层级和列表结构,这个对后续切分太关键了。命令行输出适合快速验证效果,或者做批量转换的脚本基础。
2.3 Python API:在项目里把docling用起来
进入正式项目,用Python API更合适,灵活性高,可以在转换后继续做后处理。最基本的使用姿势是这样:
from docling.document_converter import DocumentConverter converter = DocumentConverter() result = converter.convert("my_document.pdf") # 导出Markdown markdown_output = result.document.export_to_markdown() with open("output.md", "w", encoding="utf-8") as f: f.write(markdown_output) # 导出JSON,包含丰富元数据 json_output = result.document.export_to_dict() print(json_output.keys())这里有个容易忽略的点:convert()返回的DocumentConversionResult里,除了document,还包含input、errors、status等字段。如果转换过程中有问题,能从errors里看到详细信息,别只盯着输出内容看。
再进一步,你可以在转换时指定页数范围,避免一次处理整个大文件:
from docling.datamodel.base_models import InputFormat from docling.datamodel.pipeline_options import PdfPipelineOptions from docling.document_converter import DocumentConverter pipeline_options = PdfPipelineOptions() pipeline_options.do_ocr = False # 纯电子版PDF可以关掉OCR提速 converter = DocumentConverter(pipeline_options=pipeline_options) result = converter.convert("my_large_document.pdf", page_limits=(10, 30))page_limits是(start, end)元组,起始页索引从0开始。这个参数在处理几百页的大文档时特别有用,可以先抽几页出来验证效果,再决定要不要全量转换。
Python API同样能处理DOCX、PPTX等格式,用法完全一样:
result_docx = converter.convert("meeting_notes.docx") result_xlsx = converter.convert("annual_report.xlsx")我实际测下来,DOCX的转换速度快于PDF,因为本身有文字层和结构信息,docling要做的工作少很多。XLSX会按工作表结构输出,对表格类数据的还原度也很高。
3. docling核心原理:从像素到结构化数据的处理管线
3.1 一次文档解析,里面跑了几套模型
docling解析PDF时,不是一步到位把文字抽出来,而是走了一条完整的“视觉理解”管线。我自己理解下来,大致是这样几个环节:
第一步,把PDF页面渲染成图像。这一步是为了让后续视觉模型能“看到”文档长什么样。对电子版PDF,它同时也会读取底层文本层;对扫描件,则完全依赖渲染得到的图像。
第二步,版面分析(Layout Analysis)。模型在页面图像上画框,标出每个区域是标题、正文、表格、图片、页眉页脚还是目录。这一步解决的是“文档有哪些块,块在哪”的问题。
第三步,表格结构识别(Table Structure Recognition)。专门针对表格区域做细粒度分析,识别表的行、列、单元格位置,以及合并单元格的情况,最后重建出表格结构。
第四步,阅读顺序重建(Reading Order)。把版面分析得到的各种块按照人类阅读顺序排序。这一步对多栏排版文献、报纸类版面尤其重要,否则内容顺序会乱。
第五步,语义卷积与输出组装。把识别结果组装成带层级结构的文档对象,再按你指定的格式导出Markdown或JSON。
听上去流程很长,但docling把这些都封装好了,对调用方来说就是一次convert()调用。理解这套管线有一个好处:出问题时你能快速判断是哪一步出了问题。比如扫描件文字提取不全,大概率是OCR环节的问题;内容顺序不对,大概率是阅读顺序重建的问题;表格错位,那就盯表格识别模型调参。
3.2 版面分析、表格识别和OCR都在干什么
版面分析是管线里最基础也最关键的一环。它用的是目标检测类模型,在整页图像上预测一个个边界框,并给每个框打上类别标签。常见类别包括标题、正文文本、表格、图片、页眉、页脚、页码、公式等。它和质量直接挂钩,因为后续表格识别、阅读顺序重建都依赖它给出的区域划分。
表格结构识别是另一个独立模型,它在版面分析框出的表格区域内再做细粒度识别。普通字符串抽取拿到的只有一个个文本片段,无法还原行列关系;而表格模型会预测表头行、每行每列的位置、单元格跨度等。合并单元格多、边框残缺的表格,如果没有这一步,后面转Markdown就不可能是完整的二维表结构。
OCR在docling里是可插拔的,主要处理扫描版或图像型文档。当版面分析发现页面图像里有文字区域,但底层没有文本层时,就会交给OCR引擎识别。docling本身不自研OCR,而是接EasyOCR、DocTR这类引擎。这也意味着扫描件的处理速度比电子版慢很多,因为OCR对每个文字区域都要跑一遍模型推理。
有一个细节我提一下:OCR引擎的识别结果会回填到文档对象里,但坐标位置可能和版面分析的框对不上。docling内部做了对齐处理,不过在实际使用中,如果扫描件清晰度太差,识别出来的文字顺序偶尔还是会乱,这种情况建议先把原图做增强处理,比如提高对比度、去噪,再交给docling。
3.3 为什么JSON输出是它的灵魂
如果说Markdown输出是给人看的,那JSON输出就是给程序用的。docling导出的dict里,包含每个文档元素的类型、文本内容、边界框坐标、层级关系、阅读顺序编号,以及对不同块之间的引用关系。
举个例子,一篇文章里有三个二级标题,每个标题下有三段正文,JSON里会把它们组织成嵌套结构,而不是扁平堆在一起。对RAG切分来说,这个信息太宝贵了——你可以直接根据标题层级切分文档,不需要自己写正则去猜什么是一级标题什么是二级标题。
用坐标信息还能做很多事:可以把识别出的文本块和原PDF页面对齐,做高亮展示;可以过滤掉页眉页脚;可以在表格识别结果不理想时,用坐标数据去原PDF里裁剪对应区域做二次识别。这些后处理手段,光靠Markdown文本是做不到的。
我习惯的做法是,转换完成后同时保留Markdown和JSON,Markdown给人快速浏览,JSON给下游程序做精确处理。两条腿走路,后面不管是接大模型还是接人工审核,都灵活得多。
4. docling在RAG场景怎么用:管线搭建与切分配置
4.1 一条比较顺手的RAG文档处理管线
做RAG项目,数据管道最忌讳的是“一堆脚本各干各的,格式五花八门”。docling最好的用法,是固定为整个管线的“统一入口”,让所有非结构化数据在那里汇合、标准化,然后以一个统一的结构化格式流出。
我现在的处理流程大致是这样:
- 收集各类文件,包括PDF、Word、PPT、图片,统一交给docling转换。
- 输出物保存为JSON和Markdown两份,JSON做后续处理,Markdown做归档和预览。
- 按文档的标题层级切片,把每个切片转成结构化的块(chunk)。
- 对每个chunk做向量化,连同标题、页码、文档来源等元数据一起入库。
- 检索时用向量相似度召回候选块,再按文档结构做重排序。
docling在这一套里承担的是第1、2步,但它输出的结果质量,直接决定了第3步切分能切得多干净。
用代码来表示的话,批量处理一批文件可以这样写:
from pathlib import Path from docling.document_converter import DocumentConverter converter = DocumentConverter() input_dir = Path("./raw_docs") output_dir = Path("./parsed") output_dir.mkdir(exist_ok=True) for file_path in input_dir.rglob("*"): if file_path.suffix.lower() not in {".pdf", ".docx", ".pptx", ".xlsx"}: continue try: result = converter.convert(str(file_path)) base_name = file_path.stem # 保存Markdown (output_dir / f"{base_name}.md").write_text( result.document.export_to_markdown(), encoding="utf-8" ) # 保存JSON import json (output_dir / f"{base_name}.json").write_text( json.dumps(result.document.export_to_dict(), ensure_ascii=False, indent=2), encoding="utf-8", ) except Exception as e: print(f"处理失败: {file_path}, 错误: {e}")注意这个批量脚本里加了try/except,实际项目里这不是可选项,是必备项。文档解析偶尔会遇到超级诡异的文件,一个坏文件挂掉整个批次,代价太高。
4.2 结构化切分与向量化时的几个注意点
拿到docling的结构化输出之后,切分策略会很自然地从“按字符数硬切”升级为“按语义边界切分”。我有几个经验可以分享:
标题是天然的切分点。用docling的JSON可以很容易拿到每个标题的层级和位置。一级标题之间通常对应一个较大的主题,二级、三级标题可以把一个主题继续细分。切分时保留标题路径,比如“安装指南 > 环境准备 > 创建虚拟环境”,这个路径本身就是很好的检索上下文。
表格不要跟正文混着切。表格是一个高度自包含的信息体,把它从中间截断会彻底破坏语义。docling输出里表格是一个独立的类型块,切分策略里应该优先把整个表格作为一个chunk,如果表格太大,再考虑按行分组。
元数据能带多少带多少。文档来源、页码、标题路径、块类型,这些都可以作为chunk的metadata存入向量库。检索后展示来源时很有用,做精细化权限控制时也需要。
另外,docling还提供了一些实验性质的切分组件,比如docling_chunking包里的DoclingChunking类,可以直接接收文档对象做语义切分。不过这类组件迭代比较快,API可能在版本间有变化,生产环境用之前一定要锁版本、做足测试。
5. docling实战排坑记录:我踩过的坑和解决思路
5.1 扫描版PDF不识别文字,OCR怎么开才靠谱
拿到一个扫描版PDF,直接丢给docling,经常发现Markdown里除了图片什么都没有,或者文字内容是空的。原因很简单:扫描件没有文本层,必须开启OCR才能提取内容。
开启OCR的办法是在PdfPipelineOptions里设置do_ocr=True,并选一个OCR引擎。docling支持EasyOCR和DocTR两种选项。我的建议是,时间不敏感就选EasyOCR,它对中文和日常文字的支持比较均衡;想要更快或者做批量处理,可以试试DocTR,在部分场景下速度更有优势。
实操时需要注意,OCR引擎首次运行会额外下载模型。另外OCR非常吃CPU/GPU资源,纯CPU机器处理扫描PDF,速度肉眼可见地慢,几十页的大文件可能要等好几分钟。我一般会对扫描件和电子版分开处理,别图省事统一开OCR。
如果扫描件本身尺寸很大,我建议先压缩一下,或者提高对比度,再做OCR。模糊的扫描图,即使再强的OCR引擎也白搭。
5.2 复杂表格识别错乱,我是这么补救的
docling对大多数规整表格的识别没问题,但遇到复杂表格还是可能翻车,典型场景是严重合并单元格、跨页的大宽表、表格内含图片或手写批注。
我第一次处理一份带大量合并单元格的财务表时,转出来的Markdown表格行列对不齐,有的单元格直接丢了内容。后来排查发现,问题出在表格识别模型对稀疏表格的框预测不够准,导致部分区域没进入表格结构。
我的补救策略是分层处理:先看docling的JSON输出,找到表格块的边界框坐标;然后根据坐标从原图裁剪出表格区域,用其他的表格识别方法做二次识别;最后再合回docling的结构里。虽然这一步要写额外代码,但复杂表格的准确率确实提上来了。
另一种更省事的思路是,把复杂表格直接按“图片+坐标”保存,大模型回答问题时直接把表格图片一起丢给多模态模型,效果也不错。表格不是一定要转成文本,有时候保留视觉信息反而更稳。
5.3 模型下载卡住、内存占用高、大文档处理慢
docling首次运行要下载模型,有时候会遇到下载很慢甚至超时。如果网络的稳定性一般,建议先把模型预先下载好,放在本地缓存目录,之后运行就完全不依赖网络了。
内存占用高是另一个常见问题。docling的深度学习模型本身要占一些显存或内存,处理超大PDF时还会累积中间结果。我处理一本几百页的手册时,中途内存占用直接飙到了好几个G。对策是分批处理:用page_limits限制每次转换的页码范围,全部转完后合并结果。一次别贪多,稳字当头。
处理速度方面,CPU上跑大文档确实慢。有条件就用GPU,在PipelineOptions里指定accelerator_options把批处理放到CUDA上。没有GPU的话,至少把OCR关掉,能省下一大截时间。
5.4 常见问题速查表
| 症状 | 可能原因 | 解决方向 |
|---|---|---|
| 扫描件输出的文字为空 | 未开启OCR | 在PipelineOptions里设置do_ocr=True |
| 中文识别效果差 | OCR模型对中文支持不够 | 换成支持中文更稳的EasyOCR引擎 |
| 表格行列错乱 | 表格过于复杂或边框模糊 | 用bbox坐标裁剪后做二次识别 |
| 模型下载卡住或超时 | 网络环境限制 | 预先下载模型至本地缓存目录 |
| 处理大PDF内存飙升 | 模型加载+中间态堆积 | 用page_limits分批转换 |
| CPU推理太慢 | 模型较大且未用GPU | 开启CUDA,或关闭OCR仅处理电子版 |
| 输出Markdown图片全是路径 | 图片导出模式为placeholder | 使用--image-export-mode embedded嵌入图片 |
排查问题的思路,我一直强调“先判断是哪一层出的问题”。版面分析问题会表现为块类型混乱;表格问题会表现为行列关系错误;OCR问题会表现为文字缺失或乱码;阅读顺序问题会表现为段落前后颠倒。定位到具体环节,再针对性处理,就不至于像无头苍蝇一样乱试参数。
6. 关于docling,最后说几句个人心得
用docling爬了这么多坑之后,我的结论是:它值得作为RAG文档预处理的首选方案,但也不要指望它是万能钥匙。它把“从非结构化到结构化”这件事做到了很高的完成度,让开发者不用再为格式解析花太多精力,但真正决定知识库效果上限的,还是切分策略、嵌入模型、检索逻辑这些下游环节。
我自己的习惯是,一个新的文档集进来,先用docling跑一小批,人工检查转换质量,特别是表格和标题层级,确认没问题后再全量处理。批量转完后,JSON文件一定留着,后面要调整切分逻辑时,不用重新解析原始文档,直接改切分代码就行,能省好几轮重复跑批的时间。
最后再分享一个小技巧:docling处理完的Markdown里,如果只是给人快速预览,可以顺手生成一个HTML版本放到内部文档站上,阅读体验比直接看Markdown源码舒服得多。这个做法成本极低,但对团队协作的帮助非常大,算是意外收获。