YAOTU INSIGHTS

文档统一转Markdown:提升AI理解与RAG检索效果的实践指南

文档统一转Markdown:提升AI理解与RAG检索效果的实践指南
把 PDF、Word、Excel、图片交给 AI 之前先统一成 Markdown这个思路我是在做一批文档问答项目时彻底想通的。当时团队接到的需求是把几百份混合格式的资料喂给大模型做检索问答一开始图省事直接调各种解析库把文本抠出来塞给模型。结果效果一言难尽表格数据答错、章节层级混乱、检索经常命中无关段落。后来把所有文档统一转成 Markdown再走切分和向量化整个链路才真正稳下来。这篇文章就是围绕这条实践路径展开的。我会先解释为什么偏偏是 Markdown 而不是纯文本再给出不同格式文档的转换选型和具体操作步骤接着聊编排策略和工程化落地的细节最后把实际项目里高频踩坑的问题整理成排查清单。内容偏工程实践适合正在做 RAG、知识库问答、文档解析开发的团队参考。1. 为什么非要把文档先统一成 Markdown先问一个根本问题大模型读取文档时它到底需要什么模型处理文本的核心机制是 token 预测它不会像人一样“看版面”而是把文本切碎后计算注意力。这意味着同样的内容不同的组织方式模型理解的难度完全不同。纯文本把所有信息拍平标题、列表、表格全混在一起模型要靠上下文去猜结构而 Markdown 用轻量符号保留了标题层级、表格行列、强调关系这些结构信息正是模型理解文档语义的关键锚点。我拿一个三栏排版的调研报告举例。原始 PDF 转成纯文本输出可能是这样的项目背景 行业分析 市场数据 截至2023年底市场规模达到...这段文字没有任何层级信息模型只能通过语义去猜“市场规模”是不是“行业分析”下面的内容。但如果转成 Markdown## 项目背景 ### 行业分析 #### 市场数据 截至 2023 年底市场规模达到...模型一眼就能识别章节归属后续检索时也能更精准地命中对应小节。表格的差异更明显。Excel 的单元格坐标、合并规则、数据类型在纯文本抽取后几乎全部丢失。但 Markdown 表格用管道符和分隔行保留了行列关系| 季度 | 营收 | 同比 | | ---- | ---- | ---- | | Q1 | 100 | 10% | | Q2 | 120 | 20% |模型看到的是结构化表格它知道第三行第二列是“营收”自然能正确回答“Q2 营收是多少”这类问题。如果换成纯文本“Q1 营收 100同比增长 10%Q2 营收 120同比增长 20%”看起来也能读但一旦表格列数多、行数多文本形式就开始混乱模型经常抓错对应关系。还有一个常被忽略的点是 embedding。RAG 场景下文档要切成 chunk 再向量化纯文本的切分边界往往落在句子中间语义不完整而 Markdown 可以根据标题层级来切分每个 chunk 都有清晰的语义边界。我们实测过同一份文档按 Markdown 标题切分后的检索命中率明显高于固定字符数切分。结论其实很直接Markdown 是目前综合成本最低的 AI 友好中间格式。它不是银弹但相比原始文件它让模型更好读相比纯文本它保留了结构。2. 转换前先看文档类型PDF、Word、Excel、图片的差异化处理不同格式的解析难度天差地别最忌讳的就是拿一个工具通吃所有类型。PDF 是排版文件Word 是内容文件Excel 是数据文件图片是像素文件它们的解析逻辑完全不同。选型必须先按文档类型拆开看。2.1 PDF文字型、扫描型、复杂版面三分法PDF 是最麻烦的一类。它不是一种“内容格式”而是一种“印前格式”内部可能由 Word 导出、扫描件生成、设计软件输出结构差异极大。文字型 PDF文本可以直接提取用 PyMuPDF 或 pdfplumber 就能拿到文字内容和位置信息。速度快但复杂表格容易乱序。扫描型 PDF本质是图片集合必须走 OCR。中文识别推荐 PaddleOCR 或 RapidOCR精度高但速度慢大量处理建议带 GPU。复杂版面 PDF多栏、图文混排、表格密集这类需要版面分析能力更强的工具比如 Marker、MinerU或者成熟的商业转换服务。它们会用模型识别标题、段落、表格区域再重组为 Markdown效果好但部署成本高。2.2 Worddocx 与 doc 命运不同docx 本质是一个 zip 包内部是 XML 文档用 Pandoc 一条命令就能无缝转成 Markdown标题、列表、粗体基本保留。老旧的 doc 是二进制格式Pandoc 无法直接处理需要先用 LibreOffice 转成 docx 再转 Markdown。这个过程中样式可能有些变化比如居中标题被改成左对齐但正文内容一般没问题。2.3 Excel先拆 sheet再固化公式Excel 的核心价值在于单元格坐标、合并单元格、公式计算。直接转 Markdown 会丢失坐标和公式语义。实际操作中我习惯按 sheet 拆分每个 sheet 转成一张或多张 Markdown 表格并且注意表头语义清晰。一个关键坑是公式pandas 读取 Excel 时拿到的是缓存值不是公式本身。如果 Excel 里有动态计算的单元格必须先打开文件让公式计算结果并保存再转 Markdown否则 AI 看到的是空值。2.4 图片OCR 之外还要考虑版面图片转 Markdown 的核心是 OCR但 OCR 出来的一串文字通常没有结构。要得到可用的 Markdown需要先做版面分析区分标题、正文、表格区域再按顺序拼装。轻量级可以用 OCR 返回的坐标信息排序复杂版面只能用模型工具或人工介入。图片还有一种更省事的路线直接用多模态大模型识别图片内容。这种方式不要求把图片转成 Markdown模型本身就能理解版面结构。是否要走 OCR 转 Markdown取决于你的下游模型能力和业务需求不必为了统一而统一。3. 实操从混合格式文档到 Markdown 的完整流程选型讲完接下来看具体怎么落地。我以一套常用的开源组合为例演示如何把一批混合格式文档统一成 Markdown。整体流程分四步环境准备、按格式转换、后处理清洗、输出校验。3.1 环境准备建议使用虚拟环境或 Docker避免依赖冲突。我这里基于 Python 3.10pip install pymupdf pdfplumber pandas openpyxl pip install rapidocr-onnxruntimePaddleOCR 需要额外安装如果只处理少量扫描件RapidOCR 更轻量效果也够用。复杂版面 PDF 可以再部署 Marker 或 MinerU这两个项目排版还原能力强但需要下载模型权重首次运行较慢。3.2 PDF文字型抽取与扫描型 OCR文字型 PDF 直接提取文本块保留页面顺序代码大致这样import fitz def pdf_to_markdown(pdf_path): doc fitz.open(pdf_path) md_lines [] for page_num in range(len(doc)): page doc[page_num] blocks page.get_text(dict)[blocks] for block in blocks: if lines not in block: continue for line in block[lines]: text .join(span[text] for span in line[spans]).strip() if text: md_lines.append(text) md_lines.append(f\n!-- page {page_num 1} --\n) return \n.join(md_lines)思路是按页面把文本行取出来页面之间插入注释。更进一步可以根据字体大小判断标题层级把大号字转成 Markdown 的 # 或 ##这对后续切分和检索帮助很大。扫描型 PDF 需要 OCR。先用 PyMuPDF 把每页渲染成图片再调 RapidOCR 识别import fitz from rapidocr_onnxruntime import RapidOCR ocr RapidOCR() def ocr_pdf_to_markdown(pdf_path): doc fitz.open(pdf_path) md_lines [] for page_num in range(len(doc)): page doc[page_num] pix page.get_pixmap(dpi200) img_path fpage_{page_num}.png pix.save(img_path) result, _ ocr(img_path) if result: for box, text, score in result: md_lines.append(text) md_lines.append(\n) return \n.join(md_lines)OCR 速度是硬伤一页扫描件大概要两三秒。大规模处理建议做成批任务并行或者上 GPU。另外 OCR 偶尔会有错字清洗阶段可以用规则替换常见错别字。3.3 WordPandoc 的极简路线docx 转 Markdown 是几类文档里最简单的一条命令pandoc input.docx -t gfm -o output.md-t gfm指定 GitHub 风格 Markdown适合直接喂给 AI。Pandoc 会保留标题、列表、粗体、斜体但会丢弃页眉页脚。对 AI 来说这是好事页眉页脚往往是重复噪音。旧版 .doc 文件先转成 docxlibreoffice --headless --convert-to docx input.doc pandoc input.docx -t gfm -o output.md这里有个小坑LibreOffice 转换时可能丢失某些格式比如标题样式变成普通文本。如果文档是公司老系统导出的建议转完后用脚本检查标题数量是否明显偏少。3.4 Excelpandas 转 Markdown 表格Excel 转换我会写成脚本基础版本如下import pandas as pd def excel_to_markdown(xlsx_path, sheet_nameNone, header_row0): df pd.read_excel(xlsx_path, sheet_namesheet_name, headerheader_row) if isinstance(df, dict): for name, sub_df in df.items(): print(f## {name}) print(sub_df.to_markdown(indexFalse)) else: print(df.to_markdown(indexFalse))header_row参数很关键。很多业务报表第一行是报表标题第二行才是表头需要根据实际情况传入 1 或 2。如果省略pandas 会把标题误当列名整个表格语义就错了。合并单元格也要处理。pandas 读取时合并区域只有左上角有值其他位置是 NaN。需要做 forward filldf df.fillna(methodffill) # 或者用 df df.ffill()这个操作能把合并单元格的值填充到整列避免表格里出现大片空单元格。3.5 图片OCR 加简易版面排序图片转 Markdown 的轻量做法是调用 OCR 拿到识别框坐标按纵向位置排序后输出。只适合文字结构简单的图片比如截图、通知、公告。from rapidocr_onnxruntime import RapidOCR ocr RapidOCR() def image_to_markdown(img_path): result, _ ocr(img_path) if not result: return # 按 y 坐标排序同一行内按 x 坐标排序 lines sorted(result, keylambda item: (item[0][0][1], item[0][0][0])) md_lines [] for box, text, score in lines: md_lines.append(text) return \n.join(md_lines)如果图片是复杂的多栏排版这个简单排序会乱序。这时候要么做竖线检测后按列切割要么直接用多模态模型不要硬磕 OCR。3.6 转换后的统一清洗与质量校验不同工具产出的 Markdown 质量参差不齐需要一个统一的清洗层。我常用的规则有三类清理多余空行和占位符把连续三个以上换行压缩成两个去掉独立的分页注释。修正标题层级把页眉页脚里误判为标题的文本降级把正文里加粗但不该作为标题的内容移除。修复表格格式pandas 输出的表格有时缺少分隔行需要补| --- | --- |。校验我习惯分两层。第一层是脚本统计标题总数、表格总数、字符数、图片引用数指标异常就说明某个文件转换出了问题。第二层是人工抽检每批随机抽 5 份看标题层级、表格对齐、OCR 乱码。注意清洗阶段只做格式层面的事不要动内容和数据。金额、日期、编号这些出现转换异常时宁可保留原始形态也不能在清洗时误删。语义正确性留给人工校验。4. 给 AI 喂 Markdown 的编排策略转换完 Markdown 只是基础怎么把这份 Markdown 交给 AI 才是决定理解质量的关键环节。这一节分享几个我实践验证过的编排要点。4.1 控制单文档大小与切分粒度一份 100 页的 PDF 转成 Markdown 后可能有两三万行。一旦全塞进提示词token 会爆模型注意力也会被无关内容稀释。所以按逻辑章节切分而不是固定字符数切分。最常用的做法是用 Markdown 标题层级作为切分边界二级标题作为一个 chunk三级标题作为子块。切分后只把相关章节传给模型既能节省 token又能提升准确率。切分时特别注意不要把一个表格切到两个 chunk 里。表格是结构化数据切分必须保持完整。如果表格特别大单独把表格作为一个文档块用“表1营收数据”命名方便检索时直接命中。4.2 在 Markdown 中保留元信息与引用锚点AI 回答问题时如果能直接给出“这个数据来自报告第 3 章”可信度会高很多。实现方式是在 Markdown 里插入注释## 第三章 市场分析 !-- source: report_2024.pdf, page 12 -- | 年份 | 营收 | |------|------| | 2023 | 100 万 |切分成 chunk 时元信息注释会跟着内容走。回答生成时提示词可以要求模型先检索注释再引用原文。这个设计对 RAG 应用非常有用问题答案的可追溯性就是从这一步来的。4.3 提示词里明确要求“阅读 Markdown 结构”模型并不知道你喂的是 Markdown 还是纯文本除非你在系统提示词里告诉它。我常用的表述是你收到的是 Markdown 格式文档请优先利用标题层级和表格结构理解内容。回答问题时引用对应的章节和表格编号。这一句话就能显著减少模型忽略表格、只抓正文的情况。实测下来同样内容加了结构阅读说明后表格相关问答的准确率能提升近两成成本几乎为零。4.4 表格与图片的兜底策略转换质量差的表格和图片不要硬塞给 AI。表格结构乱序时直接把表格截图或原图喂给多模态模型效果反而更好。扫描件 OCR 结果差时把原图作为补充信息一起交给模型让模型结合图片内容和 OCR 文本综合判断。这背后的逻辑是统一成 Markdown 是为了提升效率而不是为了统一而统一。当 Markdown 质量无法保证时原始图片本身就是高质量的信息载体多模态模型可以直接理解没必要强行转文本。5. 批量场景下的工程化落地单文件转换是基础实际项目中往往要处理几百上千份混合格式文档。这一节聊聊工程链路的串联、性能优化和质量保障。5.1 批量文档处理流水线我在项目中把整套流程串成一条流水线接收文件 - 类型检测 - 格式转换 - Markdown 清洗 - 切分索引 - 存入向量库类型检测用扩展名最简单但更可靠的是读文件头。docx 和 xlsx 都是 zip 格式PDF 以%PDF开头图片按扩展名区分。检测完之后进入各自的转换分支。批处理建议用任务队列每个文件转换任务丢进去多个 worker 并行执行。OCR 阶段是性能瓶颈文件多时单独扩容 OCR worker或者用 GPU。文字型 PDF 转换占用资源低CPU 跑就行。5.2 缓存与增量更新文档转换很耗资源同一个文件重复转换很浪费。我会上传文件时计算 MD5 做哈希缓存如果内容没变直接读缓存 Markdown。增量更新也很重要。业务文档经常变更全量重建索引成本高。我习惯记录每个文件的转换时间和版本号变更时只重新处理增量文件并更新对应 chunk。知识库规模大了之后这个策略能省不少成本。5.3 转换质量监控链路搭完不代表它永远正确。我主要盯三个指标转换成功率、Markdown 平均字符数、表格数量。只要这些指标出现明显波动大概率是某个转换工具出了问题。质量评估方面可以设计一组标准问答用 AI 自己来评估回答准确率。这个方法成本低能直观看出转换质量对最终效果的影响。人工抽检作为补充确认脚本指标看不出问题的部分。6. 文档转换中的常见问题与排查技巧最后整理一份实际项目中反复出现的问题清单算是避坑指南。6.1 PDF 表格乱序、错位这是普遍问题。PDF 里的表格没有“行”的概念只有文本块和坐标。提取时如果列间距过宽会把同一行的两列拆成两段文本顺序就乱了。排查方法对比原始 PDF 和 Markdown 表格确定乱序范围。如果是简单表格用 pdfplumber 的extract_table方法重新提取再手动转 Markdown。复杂表格直接上 Marker 或 MinerU效果会好很多。6.2 Word 转出后标题丢失Pandoc 对某些自定义样式名识别不全比如非标准 Heading 样式会变成普通文本。排查时先看原始 docx 的样式名用 Python 读取styles.xml确认。解决办法两个一是转换前用脚本把自定义样式改成标准标题二是转换后根据文本特征居中、加粗、字号补标题。最省事的还是源头规范文档样式但这往往需要业务部门配合。6.3 Excel 多级表头如何转成 AI 友好的表格财务或调研类报表通常有两级表头pandas 默认只取第一行做列名上面的分组层级会丢失。处理方式是读两次第一次用header0读取第一级第二次用header1读取第二级再把两层表头合并。另一种更直接的方法把多级表头拍平为一级。比如“2023 年”“Q1”合并成“2023 年 Q1”。这样 Markdown 表格虽然列名变长但语义完整AI 理解起来反而更容易。6.4 OCR 误识别、乱码扫描件 OCR 总是有概率出错的。除了常见错别字双栏版面识别顺序混乱也很典型。处理思路是先做竖线检测把图片按列切割分别 OCR再按顺序拼装。如果 OCR 质量差到改不动建议别浪费时间。把扫描件原图作为补充信息一起交给多模态模型让模型结合 OCR 文本和图片内容判断。根据我的经验这个方案比反复调 OCR 参数性价比高得多。6.5 向量化失败或检索效果差有时候 Markdown 里包含大量 HTML 标签或特殊符号向量化模型不认这些 token导致 chunk 索引质量差。我的做法是在清洗阶段保留一份纯文本版本专供向量化带格式的 Markdown 版本用于大模型阅读。这里要区分清楚向量化用纯文本生成答案用 Markdown。纯文本保证检索时语义相似的内容能聚合Markdown 保证模型生成时结构清晰可引用。两者并存不冲突。6.6 转换结果与原文不一致偶尔会出现转换后的 Markdown 内容与原文对不上比如数字少了 0、句子被截断。这种情况大多是转换工具在处理特殊字符时出错。排查时用 diff 工具对比原始文本和 Markdown 文本定位到具体差异。预防办法是检查原始文档里有没有公式、脚注、尾注、文本框。这些内容在转换链路上经常被遗漏。公式复杂时建议直接截图嵌入 Markdown不要强转纯文本脚注和尾注可以转化后统一放在文档末尾。我最后想说的从纯文本到 MarkdownAI 对文档的理解能力上了一个台阶前提是我们先给出它真正“读得懂”的文档。转换工具终究只是手段统一成 Markdown 的真正价值在于让 AI 把结构当结构看把表格当表格看而不是让它在杂乱文本里盲猜。我还有个体会是格式统一这件事越早做越好。很多团队都是在 RAG 检索效果差、AI 答非所问之后才回头补文档转换那时候数据已经堆了一堆再清洗成本就高了。如果一开始就规划好 Markdown 作为中间格式后面无论是做问答、摘要还是知识库检索都会顺畅很多。最后分享一个小习惯每次转换完一批文档我会随机抽几份人工检查 Markdown 质量花不了多少时间但能避免整批索引被低质量转换污染。文档转换不上心AI 上层做得再好也白搭。