
每天和 PDF、Word 打交道的人应该都体会过这种无力感内容明明就在眼前程序却拿不到“真正的”文本。用 PyPDF 或 pdfplumber 提取出来的段落东一个西一个表格行列错乱碰到扫描版 PDF 直接变成一坨无法检索的图片。我曾为了把这些东西喂给 RAG 知识库被迫把提取、OCR、版面还原、表格重建拼成一条脆弱又难维护的处理链。直到后来项目里引入了 docling 这个工具整个流程一下子清爽了。docling 是 IBM 开源的一个文档解析工具定位很直接把 PDF、Word、PPT、Excel、图片这些日常办公文件转换成 Markdown、JSON 这类结构化的、对 AI 友好的数据。它不只是一个“提取文本”的库而是把版面分析、阅读顺序识别、表格结构识别、OCR 都整合到了一套模型管线里。这篇文章我会从实际使用角度把它能做什么、怎么装、怎么调、有哪些坑以及怎么接进 RAG 流程一条条讲清楚。适合正在搭文档解析链路、想了解现代文档模型能力、或者已经被 PyPDF 折腾过的人。1. 在没有docling之前解析PDF为什么这么痛苦1.1 传统PDF解析的三个硬伤先说第一个硬伤文本提取乱序。PDF 内部存储的文本块顺序和人类阅读顺序不一定一致。特别是有两栏排版的论文、带侧边栏的杂志页提取出来经常会变成先读左栏再读右栏但跨栏时又乱跳。更麻烦的是标题和正文之间没有语义关系程序不知道哪行是标题哪行是正文哪个表格属于哪一段。第二个硬伤是表格识别。PDF 里的表格有两种形态一种是真正的文本坐标组成的表格另一种其实是一张图片。对前者pdfplumber 这类工具可以基于线条和坐标勉强还原对后者基本无能为力。即便还原成功跨页表格、合并单元格、多层表头也会让结果支离破碎。第三个硬伤是扫描件。扫描版 PDF 本质上是一堆图片必须 OCR。用 Tesseract 之类的 OCR 工具只能得到一堆无排版顺序的文字分段、表格、标题层级全部丢失。为了解决这些问题我过去要同时维护多套工具pdfplumber 负责文本和坐标camelot 负责表格Tesseract 负责扫描件再写一段逻辑把三者输出拼接起来。拼接逻辑本身就是巨坑因为三种工具各自的理解方式完全不一样一旦某页版面特殊结果就错得离谱。1.2 docling的定位一条链全包docling 解决的正是上面这一整摊问题。它内部把版面分析模型、表格结构模型、阅读顺序模型、OCR 组件串成一个完整的解析管线输入一个文件输出一份带上语义结构的文档对象哪些是标题、哪些是正文、哪些表格、表格里每个单元格在哪、阅读顺序是什么全部是结构化数据。你可以把它粗略理解为“一个留给 AI 程序看文件的接口”。传统工具解决的是“怎么把字节变成字符串”docling 解决的是“怎么把字节变成有结构的文档”。这个结构可以导出为 Markdown 喂给大模型可以导出为 JSON 做后续分析也可以保留元素坐标用于 UI 预览。目前它支持的输入包括 PDF、Worddocx、PowerPointpptx、Excelxlsx以及常见图片格式输出支持 Markdown、JSON、HTML 和纯文本。安装也简单一条 pip 命令就能搞定。最让我意外的是它的上手成本不需要先搞懂版面模型是什么只要装好跑一条命令Markdown 就出来了。2. 十五分钟跑通docling安装与首次转换2.1 环境准备与安装docling 在 PyPI 上直接发布依赖 Python 3.9 以及更高版本。建议用虚拟环境安装避免和系统里的其他包冲突尤其是 OpenAI、LangChain 那一套。安装命令很简单pip install docling它会自动拉入 PyTorch、transformers 以及 docling 自己的模型组件安装包体积不小建议留足磁盘空间。如果你的机器有 NVIDIA GPU并且想用 GPU 加速推荐同时安装带 CUDA 支持的 PyTorch 版本。比如先用pip install torch --index-url https://download.pytorch.org/whl/cu121装好对应 PyTorch再装 docling这样能在后续推理时大幅缩短处理时间。首次执行转换时docling 会从模型仓库下载版面分析模型、表格识别模型和 OCR 模型。这些模型文件总计大概几百 MB取决于你用到哪些能力。我在公司内网环境里踩过没配置外网代理的坑导致模型下载卡住。解决办法很简单先确保能正常访问模型下载地址把模型跑一次缓存好之后就能离线使用了。2.2 CLI一行命令出Markdown安装完成后最快体验方式是用命令行。准备一个带标题、正文、表格的 PDF然后执行docling mydoc.pdf --to markdown首次运行会看到模型加载日志稍等片刻命令结束后会在当前目录生成一个mydoc.md文件里面就是解析出来的结构化 Markdown。转换过程保留了标题层级表格转成了 Markdown 表格段落顺序基本符合阅读习惯。这一步对很多需要用 Markdown 喂给大模型做 RAG 的人来说已经解决了 80% 的需求。CLI 还支持不少参数常用的包括--from指定输入格式不传则自动识别--to指定输出格式支持markdown、json、html、text--output指定输出目录--ocr开启 OCR适合扫描版 PDF--no-ocr强制不启用 OCR--table-mode指定表格识别模式默认是精确模式--num-workers设置进程数批量处理时能用满 CPU我一般会同时生成 Markdown 和 JSON 两种输出Markdown 给人看JSON 给程序做下游处理。2.3 最快Python调用方式CLI 适合快速验证真正要集成到系统里还是得用 Python API。最小可用代码是这样的from docling.document_converter import DocumentConverter converter DocumentConverter() result converter.convert(mydoc.pdf) doc result.document # 导出 Markdown markdown_output doc.export_to_markdown() with open(mydoc.md, w, encodingutf-8) as fw: fw.write(markdown_output) # 导出 JSON json_output doc.export_to_dict() import json with open(mydoc.json, w, encodingutf-8) as fw: json.dump(json_output, fw, ensure_asciiFalse, indent2)这段代码和 CLI 做的事情完全一样。convert()接收一个路径或 URL返回一个DocumentConversionResult里面的.document就是结构化文档对象。拿到这个对象后可以调用export_to_markdown()、export_to_dict()、export_to_html()等方法来获得不同格式的导出结果。在实际项目里我建议先跑一遍 CLI 确认输出效果再换 Python API 接进流程这样能把“模型效果”和“工程集成”两个问题分开排查。3. 理解docling的文档对象JSON结果到底有什么3.1 一次完整的JSON输出结构用 Python API 导出 JSON 后第一眼看到的数据结构和想象的“提取出的文本列表”完全不同。它更像一份带标注的文档树schema_name和version标记了这份 JSON 遵循的 schema 版本texts列表保存所有文本块、标题、列表项每个元素带text内容和类型标签tables列表保存每个表格包含表格的网格坐标、单元格内容、行列跨度pictures列表保存文档中的图片信息包括坐标page相关字段记录每一页的尺寸和元素布局每个文本块不仅仅是“一句话”还带label字段比如title、paragraph、list_item等。这意味着你可以直接按类型过滤出标题和正文不需要再靠正则猜层级。对做文档解析的人来说这价值很大。JSON 里每个元素几乎都保留了坐标信息。比如一个标题元素会有自己的bbox边界框左上角和右下角的坐标清清楚楚。坐标的存在让前端可视化、文档比对、基于位置筛选文本成为可能。3.2 表格和阅读顺序是怎么保存的表格是 docling 处理得比较细的部分。导出的表格对象里不仅保存了每个单元格的文本内容还保存了单元格在表格网格中的行列位置、跨行跨列信息。对于需要把结果还原成 DataFrame 的场景只需要读table对象中的网格数组按单元格坐标重新拼接即可得到完整的二维结构而不需要自己处理合并单元格。阅读顺序在 docling 里是通过reading_order记录的它把标题、正文段落、表格、页眉页脚组织成一个有序数组这个数组就是人类阅读文档的正常顺序。这个能力的来源是版面分析模型它会判断哪些元素属于同一栏、标题和正文的从属关系、列表的包含关系。导出的 Markdown 之所以段落顺畅就是因为这套阅读顺序在发挥作用。3.3 用编程方式遍历正文、表格、图片如果你需要在代码里对文档内容做处理可以直接遍历文档对象。比如把所有表格提取成 Pandas DataFrameimport pandas as pd tables [] for table in doc.tables: data table.export_to_dataframe() tables.append(data) # tables 列表里每个元素都是一个 DataFrame如果要过滤出所有正文段落只保留正文内容用于后续向量化可以这样做paragraphs [] for text_item in doc.texts: if text_item.label.name paragraph: paragraphs.append(text_item.text)由于每个元素都带类型标签这类过滤非常简单不再需要靠“去掉空行”或“去掉像标题的行”这种 hack 方式来清洗数据。单独把图片和表格导出也有对应接口。这个文档对象实际上覆盖了“结构、语义、位置”三层信息大部分下游任务都可以直接在对象上完成不必自己解析 Markdown。4. 背后是模型在干活docling的版面感知管线4.1 为什么能“看懂”版面顺序很多人第一次用 docling 会疑惑它怎么知道这行是标题那边是正文答案在于它内置了基于深度学习的版面分析模型。这个模型会在文档图片上识别出不同的区域比如标题区、正文区、表格区、图片区并给出每个区域的类型和坐标。之后由阅读顺序模型把这些区域按阅读逻辑排序Markdown 输出才能顺畅连贯。模型的底层是视觉 Transformer 结构它“看”的是整个页面的图像而不是逐行扫描字符。也就是说它能感知一页内多栏的物理布局理解标题比正文字号大、位置居中这种视觉特征因此在两栏论文上表现明显优于传统规则方法。4.2 表格结构识别是怎么做的表格识别是 docling 的强项也是它和其他文本提取工具拉开差距的地方。传统的表格提取依赖表格线检测碰到无线表、彩色底纹表、复杂合并单元格就崩溃。docling 的表格结构模型走的是“单元格检测 结构预测”路线先用视觉模型发现表格区域内的单元格位置再预测单元格之间的行列关系最终还原成网格结构。这意味着即便表格没有明显的横竖线只要视觉上存在对齐关系模型也能把行列找出来。我试过一些只有空格的财务报表pdfplumber 直接散架docling 反而能还原成相对规整的表格虽然合并单元格偶尔会有偏差但整体可用度已经提高了一个台阶。4.3 OCR与扫描件处理的边界docling 默认对文本型 PDF 不需要 OCR直接处理内嵌文本即可。只有检测到页面缺少文本层时或者你显式传入--ocr时它才会调用 OCR 模型识别页面图像上的文字。这个设计和常见误区相反别一上来就开 OCR性能差且没必要。对可复制文本的 PDF强制 OCR 反而可能因为识别错误而降低精度。OCR 的选择也值得留意。docling 支持 EasyOCR 等后端你可以通过配置项指定语言。比如处理中文文献时需要把语言参数设为中文相关选项否则默认的英文模型会对中文识别得非常痛苦。OCR 是大计算量的操作建议只在有文本层缺失的页面使用或者利用 docling 的自动判断逻辑。4.4 性能与加速模型推理比传统正则解析要慢这是事实。纯 CPU 环境下处理普通 PDF相对较快但一旦开启 OCR 或多页大文件速度会明显下降。我的经验是按需调整参数普通文本 PDF 不开 OCR速度可接受大批量文件用多进程并行--num-workers参数可以充分利用 CPU有 GPU 时优先使用 GPU把模型加载到显存中处理速度能提升好几倍对超大 PDF先按页拆分或只取需要的页码范围不要一次性喂上百页给模型性能问题的核心是“算力换通用性”。docling 牺牲了速度换来的是对任意版面文档的强泛化能力。实际使用中建议用并行替代单进程用缓存替代重复加载模型。5. 实测排查docling最常见的坑与对策5.1 首次运行卡在下载模型我第一次在服务器上跑 docling执行完 CLI 后屏幕卡在“Downloading model”一行很久看起来像死机。其实它是在从模型仓库下载权重文件网络慢时会让人误判。这个阶段不要 CtrlC耐心等待模型下载完成后会自动进入解析流程。但如果下载速度确实太慢或者网络受阻也不是没有针对性办法。建议先找一台网络好的机器跑一次同样的命令跑完把模型缓存目录整体拷贝到目标机器docling 会在本地缓存命中后直接读取模型文件不再走网络下载。这个方案在内网环境尤其管用。5.2 OCR跑太慢/内存爆炸扫描版 PDF 页面数量多时OCR 默认配置可能把内存吃满。现象是进程 CPU 飙高内存不断增长页面多的情况下可能 OOM。我的处理方式是控制并行度降低进程数必要时按页切割 PDF 分配给多个进程每个进程处理一部分最后合并结果。对扫描版大文件分批处理远比一次处理完整文件稳定。还有一种情况是模型同时加载了多个组件内存占用比预期高。如果只是做 OCR 而不需要版面分析可以通过配置只启用必需组件把其他组件关掉以降低内存。5.3 表格复杂时识别串行复杂表格依旧可能识别出错。典型表现是多级表头被拍平、合并单元格被拆成两行、跨页表格的头部识别不完整。我实测过几种模型都无法保证 100% 还原所以实际项目中不要把表格识别结果直接入库至少加一个校验环节检查行列数是否和预期一致检查关键单元格里是否有非空值对不确定的表格降级为图片。docling 也提供不同的表格识别模式有的模式偏向速度有的偏向保真。对需要精确还原表格的报表建议使用精确模式对只需要提取正文的场景表格识别精度要求没那么高可以选更快的模式来节省时间。5.4 多栏文档的阅读顺序仍不完美版面分析模型整体可靠但碰上极端排版——比如三栏以上、图片穿插复杂、栏内再分栏——阅读顺序还是可能出问题。这时候不要把 Markdown 输出当作最终结果建议拿到 JSON 里的阅读顺序字段和原 PDF 做一次抽检比对。如果发现顺序错乱优先考虑手动后处理用坐标信息对元素排序或者按区域类型分组后人工确认。从工程角度说docling 的力量在于给了你一个可信的“基础解析结果”而不是一个永远正确的黑盒。复杂文档做一定的人工校验和容错设计是落地时必须接受的成本。5.5 中文与多语言环境需要注意的点我最早拿中文 PDF 测试时输出里出现了不少乱码。排查后发现两个原因一是某些字体没有嵌入PDF 里根本没有可复制的 Unicode 字符必须走 OCR二是 OCR 的默认语言模型不包含中文。解法是开启 OCR 并明确指定语言。同时对中文 PDF尽量选清晰的扫描件过低的扫描分辨率会明显拉低中文识别率。对于内嵌可复制文本的中文 PDF一般来说 docling 直接提取的效果已经不错不需要 OCR。6. 进阶把docling接进RAG和自动化流程6.1 为RAG准备高质量MarkdownRAG 的效果下限取决于文档解析质量。如果喂给知识库的 Markdown 段落顺序错乱、表格拍平、扫描件全是乱码后续不管怎么优化 embedding 和重排都没用。docling 在 RAG 场景中最大的价值就是能把 PDF 转成干净、有层级、表格规整的 Markdown 或按语义切片干净的结构化数据。我实际的做法是PDF 先经过 docling 导出 JSON再从 JSON 中按阅读顺序遍历文本按标题层级进行分块把表格转成 Markdown 表格字符串然后再做 embedding。相比直接切 500 字窗口这样能保住文档的上下文结构检索命中率明显更好。6.2 服务化与脚本化的一些思路docling 是 Python 库很容易封装成服务。我的项目里就是写了一个 Upload 接口接收 PDF调用 DocumentConverter 转换把 JSON 和 Markdown 写入对象存储再把 JSON 里的文本块送入向量库。全部逻辑用几十行代码就能串起来。批量处理时要注意模型加载开销。DocumentConverter 对象内部的模型资源应该在服务启动时初始化一次而不是每次请求都重新创建。我见过直接在请求函数里实例化 converter 的写法并发一多就卡死。正确做法是把 converter 作为单例复用模型推理实例。并发请求也要看显卡显存和 CPU 核心数来设置合理的线程数。我们不希望几十个请求同时触发模型推理把机器资源打爆。建议用队列控制并发上限超出的请求排队等待。这套方案的稳定性比我之前拼装 pdfplumber camelot Tesseract 的方式强了不止一个量级。6.3 从解析到交付的完整链路建议如果从一个空目录开始搭建一套文档解析服务我个人会这样组织工程结构输入层接收 PDF/docx/pptx/图片保存到本地临时目录解析层docling 负责转换输出 JSON 和 Markdown清洗层从 JSON 中过滤无效内容处理表格修正阅读顺序存储层JSON 和 Markdown 存对象存储文本块向量化后存向量库服务层提供 HTTP 接口用队列控制并发每一层都独立出问题可以单独重跑。比如清洗层发现某个文件表格识别错了只需要重新调用解析层处理那一个文件不需要重建整个索引。这个链路我从 3 个文件试到上千个文件单文件处理时间、内存占用、失败重试机制都能准确预估。最后再分享一个小经验。很多人问 docling 能不能替代所有解析工具我的观点是它能替代 80% 的通用解析需求但遇到极度复杂或精度要求极高的场景还是需要结合领域知识做后处理。最稳妥的策略不是把所有希望押在一个工具上而是利用 docling 稳定输出的结构化 JSON在它之上构建自己的校验、清洗、兜底逻辑。这样既能享受到现代视觉模型带来的通用性又能保证业务链路的可控性。