1. 为什么文档解析是 RAG 管线里最容易被低估的一环做过 RAG 项目的人大概都有过这种体验模型选型纠结了半天向量库对比了一轮又一轮检索策略从朴素向量检索一路升级到混合检索加重排序结果上线之后回答质量还是不稳定。排查到最后问题往往不在检索层也不在生成层而是卡在最前面那一步——文档解析。我自己的项目里就踩过这个坑。早期做企业知识库问答PDF 丢进去直接按固定长度切块切出来的东西惨不忍睹表格被拦腰截断标题和正文混在一起页眉页脚重复出现在每个块里跨页的段落被硬生生拆成两半。检索的时候命中率低得可怜用户问一个表格里的数据召回的却是一堆无关的页眉文字。后来花了大量时间做后处理效果也就那样。这就是 RAG 管线里最痛的一环文档解析的质量直接决定了后续所有环节的天花板。检索再强也救不回一堆语义破碎的文本块。而现实中的文档格式又极其复杂——PDF、Word、PPT、Excel、扫描件、HTML每种格式的解析难度都不一样PDF 尤其麻烦因为它本质上是一种排版描述而非结构描述文字在 PDF 里的存储顺序和人类阅读顺序经常对不上。IBM 开源的Docling就是冲着这个问题来的。它的定位很明确把各种格式的文档统一解析成结构化的、适合 RAG 使用的中间表示输出包含页码、章节层级、段落、表格结构、阅读顺序等信息的结构化文本。换句话说它试图把文档解析这一环从各家自己造轮子变成一个可以统一处理的标准件。这篇文章我会从实际使用的角度把 Docling 的能力边界、核心原理、实操流程、参数调优和踩坑经验完整拆一遍。不管你是刚接触 RAG 的新手还是已经被文档解析折磨过的老手应该都能从中找到能直接抄作业的部分。2. Docling 到底解决了什么问题核心能力与设计思路拆解2.1 传统文档解析方案的三种路线及其局限在聊 Docling 之前先理一下目前主流的文档解析方案这样你才能理解它为什么值得关注。第一种是基于规则和坐标的解析典型代表是各种 PDF 文本提取库。它们的工作原理是读取 PDF 内部的文字对象和坐标信息然后按位置排序输出。这种方案速度快、依赖少但遇到多栏排版、复杂表格、图文混排就歇菜了。因为 PDF 里文字对象的存储顺序和视觉阅读顺序没有必然关系纯靠坐标排序经常会把两栏的内容交错在一起。第二种是基于深度学习的版面分析用目标检测模型识别页面上的标题、正文、表格、图片等区域再按区域提取内容。这种方案对复杂版面的适应性好很多但需要针对不同文档类型调优而且模型推理有额外的算力和时间成本。第三种是基于多模态大模型的端到端解析直接把页面图像丢给视觉语言模型让它输出结构化的 Markdown 或 JSON。效果上限高但成本也高而且大模型偶尔会幻觉把原文没有的内容编进去这在需要精确引用的场景里是致命的。Docling 的思路是把这几条路线组合起来底层用版面分析模型做区域识别和阅读顺序判断表格用专门的表格识别模型处理结构文字提取则结合 PDF 原生文本层和 OCR最后统一输出成结构化的文档对象。它不是单纯依赖某一种技术而是根据文档类型和内容特征动态选择最合适的处理路径。2.2 统一中间表示DoclingDocument 的设计哲学Docling 最核心的设计是它的输出格式——DoclingDocument。这不是简单的 Markdown 字符串而是一个带有完整层级关系的文档对象模型。它记录的信息包括文档的标题层级一级标题、二级标题……、每个文本块的类型正文、标题、列表项、代码块、公式、表格的完整结构行列、合并单元格、表头、图片的位置和说明文字、每个元素的页码和边界框坐标、以及最重要的——阅读顺序。为什么这个中间表示很重要因为 RAG 的切块策略需要依赖这些信息。举个例子如果你知道某个段落属于第三章第二节切块的时候就可以把章节标题作为上下文拼进块里检索时用户问第三章讲了什么命中率会高很多。再比如表格如果解析出来的是结构化的行列数据你就可以把表格转成自然语言描述或者按行切块而不是把整个表格当成一坨文字。提示DoclingDocument 可以序列化为 JSON这意味着你可以在解析和切块之间插入自己的处理逻辑比如按章节聚合、按语义合并、过滤页眉页脚等。这个灵活性是纯 Markdown 输出方案给不了的。2.3 支持的格式与典型应用场景Docling 目前支持的输入格式覆盖了绝大多数企业文档场景格式类型具体格式解析特点文档类PDF、DOCX、PPTX、XLSX完整结构解析支持表格和层级标记类HTML、Markdown、AsciiDoc直接解析已有结构图像类PNG、JPEG、TIFF走 OCR 和版面分析路径音频类WAV、MP3语音转文字后进入解析流程典型应用场景我归纳了几类。企业知识库是最常见的把制度文件、产品手册、技术文档统一解析后入库支撑内部问答。合同和招标文件处理是另一个高频场景这类文档的特点是结构严谨、条款编号清晰、表格多Docling 的层级识别能力在这里很吃香。学术论文和研究报告也适合因为需要保留公式、图表和引用关系。财务报告和年报则考验表格解析能力资产负债表、利润表这类复杂表格能不能准确还原结构直接决定后续分析的可行性。3. 核心细节解析Docling 的技术栈与关键参数3.1 版面分析与阅读顺序判断的工作原理Docling 的版面分析基于深度学习模型它把每一页文档图像切成若干区域给每个区域打上标签——正文、标题、表格、图片、页眉、页脚、页码等。这个过程类似给页面做语义分割。阅读顺序的判断是难点。对于单栏文档从上到下、从左到右基本没问题。但双栏、三栏、甚至图文环绕的排版就需要模型理解人类的阅读习惯。Docling 的做法是结合区域的位置关系和类型信息构建一个阅读顺序图然后用图算法推导出合理的顺序。比如它知道标题后面应该跟正文表格应该作为一个整体处理页眉页脚应该被识别出来而不是混进正文。这里有个实操细节值得注意页眉页脚的识别准确率直接影响切块质量。如果页眉没被正确识别它会在每一页的文本块里重复出现检索时造成大量噪声。Docling 默认会尝试识别并标记这些元素但不同文档的页眉样式差异很大有时候需要手动后处理过滤。3.2 表格识别从图像到结构化数据表格是文档解析里最难的部分没有之一。PDF 里的表格可能是有边框的、无边框的、跨页的、嵌套的甚至是用空格对齐的伪表格。Docling 的表格处理分两步。第一步是表格检测在版面分析阶段定位表格区域。第二步是表格结构识别判断这个区域里哪些是行、哪些是列、哪些单元格合并了、表头是哪一行。这一步用的是专门的表格识别模型输出的是类似 HTML 表格的结构。实测下来对于有明确边框的规则表格识别准确率很高。对于无边框表格模型会依赖文字对齐关系来推断列边界准确率会下降一些。跨页表格是最麻烦的Docling 会尝试把连续两页的表格合并但如果中间隔了其他内容就可能断成两个表。注意表格解析结果建议人工抽检。我一般会随机抽 10% 的表格对比原文和解析结果确认行列对应关系没错。特别是财务数据类的表格一个单元格错位就可能导致后续分析全盘皆错。3.3 OCR 引擎的选择与配置对于扫描件或者没有文本层的 PDFDocling 需要走 OCR 路径。它默认集成了 OCR 引擎但也支持切换不同的后端。OCR 的质量取决于几个因素图像分辨率、文字清晰度、语言类型、字体复杂度。我的经验是扫描件至少保证 300 DPI低于这个值识别错误率会明显上升。中文文档建议选择对中文优化过的 OCR 模型英文文档用通用模型即可。配置上有个容易忽略的点OCR 语言设置。如果文档是中英混排只设置中文可能导致英文识别错误反之亦然。Docling 支持多语言配置中英混排场景建议同时启用两种语言。3.4 关键参数速查与调优建议Docling 的参数不算多但有几个直接影响解析质量和速度我整理成表格方便对照参数作用推荐值调优说明do_ocr是否启用 OCR扫描件 true文本层 PDF false有文本层时关闭可大幅提速do_table_structure是否解析表格结构true表格多的文档必开table_mode表格识别模式accurate / fast精度优先选 accurateocr_langOCR 语言按文档实际语言中英混排填多个num_threads并行线程数CPU 核数的 70%太高反而因调度开销变慢page_range解析页码范围按需指定调试时只解析前几页调优的核心思路是在精度和速度之间找平衡。全量解析一份几百页的 PDF如果开启所有高精度选项可能要几分钟甚至更久。我的做法是先用 fast 模式跑一遍看整体质量对质量不达标的文档再单独用 accurate 模式重跑。4. 实操过程从安装到接入 RAG 管线的完整流程4.1 环境准备与安装Docling 是 Python 包安装本身不复杂但有几个依赖需要注意。# 基础安装 pip install docling # 如果需要处理 PDF 的深度学习模型首次运行会自动下载模型权重 # 建议提前设置好模型缓存目录避免占满系统盘 export DOCLING_ARTIFACTS_PATH/your/cache/path首次运行时会下载版面分析和表格识别的模型权重大小在几百 MB 到 1 GB 之间。如果网络环境不稳定建议提前下载好放到缓存目录。模型下载完成后会缓存在本地后续运行不再重复下载。Python 版本建议 3.9 以上低于这个版本可能遇到依赖冲突。如果项目里已经有其他深度学习框架注意检查版本兼容性特别是 PyTorch 的版本。4.2 单文档解析最小可用示例先看一个最简单的例子把一份 PDF 解析成 Markdown 和结构化 JSONfrom docling.document_converter import DocumentConverter # 初始化转换器 converter DocumentConverter() # 解析文档 result converter.convert(your_document.pdf) # 导出为 Markdown markdown_output result.document.export_to_markdown() # 导出为结构化字典 dict_output result.document.export_to_dict() # 保存结果 with open(output.md, w, encodingutf-8) as f: f.write(markdown_output)这段代码跑通之后你会得到两个东西一份人类可读的 Markdown和一份机器可处理的 JSON。Markdown 适合快速预览解析质量JSON 才是接入 RAG 管线的关键。4.3 批量处理与性能优化实际项目里不可能一次只处理一个文档。批量处理的时候性能优化就很重要了。from docling.document_converter import DocumentConverter from pathlib import Path from concurrent.futures import ThreadPoolExecutor converter DocumentConverter() pdf_files list(Path(./docs).glob(*.pdf)) def process_file(pdf_path): try: result converter.convert(str(pdf_path)) return { file: pdf_path.name, markdown: result.document.export_to_markdown(), dict: result.document.export_to_dict() } except Exception as e: return {file: pdf_path.name, error: str(e)} # 控制并发数避免内存溢出 with ThreadPoolExecutor(max_workers4) as executor: results list(executor.map(process_file, pdf_files))并发数不是越高越好。Docling 的模型推理会占用较多内存并发太高容易 OOM。我的经验是按可用内存来定每并发大约需要 1-2 GB 内存8 GB 内存的机器开到 4 并发比较稳妥。4.4 从解析结果到 RAG 切块关键处理逻辑解析只是第一步怎么把 DoclingDocument 变成适合检索的文本块才是重点。这里分享我的切块策略。第一按章节层级聚合。利用 Docling 输出的标题层级信息把同一章节下的内容聚合在一起。如果章节太长再在章节内部按段落切分。这样每个块都带有明确的章节上下文。第二表格单独处理。表格不要和正文混在一起切。我的做法是把表格转成两种形式一种是保留结构的 Markdown 表格另一种是逐行的自然语言描述。检索时两种都入库提高命中率。第三过滤噪声元素。页眉、页脚、页码这些元素在解析结果里是有标记的切块前直接过滤掉。def build_chunks(doc_dict, max_chunk_size800): chunks [] current_section current_text for item in doc_dict.get(texts, []): # 过滤页眉页脚 if item.get(label) in [page_header, page_footer, page_number]: continue # 标题作为章节上下文 if item.get(label) section_header: if current_text: chunks.append({ section: current_section, text: current_text }) current_section item.get(text, ) current_text else: current_text item.get(text, ) \n # 超长时切分 if len(current_text) max_chunk_size: chunks.append({ section: current_section, text: current_text }) current_text if current_text: chunks.append({section: current_section, text: current_text}) return chunks这段逻辑的核心是让每个块都携带章节信息。检索时即使块内的文字没有直接提到章节名章节信息也能作为上下文帮助模型理解。4.5 接入向量库与检索链路切好的块接下来要入库。这里不展开讲向量库选型重点说 Docling 的输出怎么和检索链路配合。每个块建议存储这些字段块文本、章节路径、页码、来源文件名、块类型正文/表格/列表。检索的时候除了向量相似度还可以用章节路径做过滤比如用户明确问第三章的内容就可以限定只在第三章的块里检索。页码信息在需要引用来源的场景特别有用。用户问这个数据出自哪里你可以直接返回文件名和页码可信度比单纯给一段文字高得多。5. 常见问题与排查技巧实录5.1 解析质量问题的排查思路解析质量出问题排查要按顺序来不要一上来就怀疑模型。第一步确认文档本身有没有文本层。用 PDF 阅读器试着选中文字如果能选中说明有文本层OCR 应该关闭如果选不中说明是扫描件必须开 OCR。这个判断错了后面全白搭。第二步检查页面图像质量。扫描件如果分辨率太低、倾斜、有噪点OCR 识别率会断崖式下降。这种情况建议先做图像预处理——去噪、纠偏、提高对比度再喂给 Docling。第三步看版面分析结果。把解析出的区域可视化出来看看标题、正文、表格的框选是否准确。如果框选错了说明版面分析模型不适应这类文档可能需要换模型或者调整参数。第四步检查阅读顺序。如果解析出的文字顺序混乱多半是阅读顺序判断出了问题这在多栏排版里比较常见。5.2 高频问题速查表问题现象可能原因解决方法文字顺序错乱多栏排版阅读顺序判断错误检查版面分析结果必要时按栏切分表格内容错位无边框表格列边界识别错误改用 accurate 模式或人工校正页眉页脚混入正文页眉页脚识别失败后处理按位置和重复模式过滤OCR 识别率低图像质量差或语言设置错误图像预处理检查 ocr_lang 配置解析速度极慢全量开启高精度选项按需开启先用 fast 模式筛选跨页表格断裂表格跨页且中间有其他内容后处理合并相邻页的同类表格公式解析成乱码公式识别能力有限公式单独提取用专门工具处理内存溢出并发数过高或单文档过大降低并发大文档分页处理5.3 几个我踩过的坑坑一以为解析一次就一劳永逸。文档会更新解析结果也会过时。我的做法是给每个文档记录解析时间和版本文档更新后重新解析并且对比新旧解析结果的差异避免因为解析器升级导致内容突变。坑二忽略了解析结果的存储成本。DoclingDocument 的 JSON 包含大量坐标和结构信息体积可能是原始文本的好几倍。如果文档量大存储成本要提前算进去。我的做法是只保留切块需要的字段坐标信息在切块后就可以丢弃。坑三切块参数一刀切。不同类型的文档最优切块大小不一样。技术文档段落长块可以大一些FAQ 类文档段落短块要小一些。我后来改成按文档类型配置不同的切块参数检索效果明显改善。坑四没有建立解析质量的评估机制。早期全靠人工抽检效率低还容易漏。后来我建了一个简单的评估集包含各种典型文档和对应的关键信息点每次调整解析参数后自动跑一遍看关键信息点的召回率有没有下降。这个机制帮我避免了好几次优化反而变差的情况。提示解析质量评估集不需要很大20-30 份覆盖主要文档类型的样本就够用。关键是每次改动后都要跑形成基线对比。6. 关于 Docling 在 RAG 管线中的定位我的几点实际体会用了一段时间 Docling 之后我对它在 RAG 管线里的定位有了比较清晰的认识。它不是万能的但在统一处理多种格式文档并输出结构化结果这件事上确实省了我很多自己造轮子的时间。它最适合的场景是文档格式多样、结构相对规范、对解析质量有要求但预算有限的项目。如果你的文档全是干净的 Markdown那用不上它如果你的文档是极其复杂的扫描件且要求 100% 准确那可能还需要配合人工校对。我个人的建议是把 Docling 当成 RAG 管线里的一个标准组件来用但不要指望它解决所有问题。解析之后的后处理、切块策略、检索优化每一环都还有大量工作要做。文档解析只是把地基打好了上面的楼怎么盖还是取决于你对业务场景的理解。最后分享一个实用的小技巧先用 Docling 把文档解析成 Markdown人工快速浏览一遍。这一步花不了多少时间但能让你对文档的实际结构心里有数后续设计切块策略和检索方案时会少走很多弯路。很多时候问题不是出在工具上而是出在我们对文档本身的理解不够。