1. 为什么直接丢文件给 AI 常常翻车:三个被忽视的底层差异
很多人觉得,AI 连代码都能读懂,PDF、Word 还不是顺手就来?我在实际测试中踩过不少坑之后发现,这个想法恰恰是效果差的根源。你丢给 AI 一个 PDF,它确实能打开,但它读到的内容和你看到的完全不是一回事。它拿到的是一堆按页面坐标排列的字符流,而不是你肉眼感知到的“一篇文章”。
先说第一个差异:PDF 根本不是文本格式。它本质上是一种排版描述语言,记录的是“哪个字符画在哪一页的哪个位置”。这意味着,同一个句子在视觉上完整,但在底层数据里可能是按空格、按字体、按行切碎的片段。尤其是双栏排版、图文混排、页眉页脚和带水印的文档,AI 拿到的线性文本流经常是“左栏读一半,跳到右栏,再跳回左栏”。这种乱序对模型理解长文是灾难级的。
第二个差异是噪声。原始文件转成文本之后,页眉、页码、目录、网址、脚注全都会混在正文里。我见过一份 20 页的 PDF,光页眉重复出现就有 40 多次。这些重复的噪声并不会因为“AI 聪明”就自动忽略,它们会真实占用上下文窗口,稀释注意力权重。模型需要花额外计算去判断哪些内容是主体,哪些是装饰,结果就是关键信息被挤到边缘,回答自然容易泛泛而谈。
第三个差异,也是很多人没意识到的:AI 没有“视觉排版感知”。人看一眼标题字号大、加粗、居中,就知道这是重点;但 AI 拿到纯文本后,只能靠换行和空格猜测层级。如果你转成 Markdown,用#、##、-、表格语法把这些结构显式标注出来,模型就能在 token 流里直接识别“这是标题”“这是列表”“这是表格”,理解准确率完全不是一个量级。
所以,转 Markdown 再喂 AI,不是形式主义,而是把“机器能读的文本”升级成“模型能理解的语义结构”。下面我会把我在实操中验证过的完整方案拆开讲。
2. 转换前的摸底:三类常见文件各有各的坑
我在做文档预处理时发现,不能指望一个工具通吃所有格式。每种文件的“坑”完全不同,先摸清类型和障碍,才能选对处理路径。这里把最常见的三类文件分别拆开说。
2.1 扫描版 PDF:没有文本层的“图片文档”
先说最容易被忽略的坑。你拿到的 PDF 如果是扫描件或者打印后重新扫描的,它里面根本没有文本层。每一页就是一张大图。这时候你用任何文本提取工具都会发现,提取结果要么是空的,要么是几个孤立字符。很多人的第一反应是“工具不行”,其实是把提起工具和 OCR 工具混为一谈。
我的经验是,拿到 PDF 先做一次“是否有文本层”的检测,而不是直接上转 Markdown 工具。用 PyMuPDF 的page.get_text()跑一遍,如果一页提取出的字符数低于某个阈值,比如 20 个字符,基本可以判定是扫描件,直接进入 OCR 流程。OCR 环节我常用两个思路:一是本地 OCR 工具保证隐私,二是调用大模型的视觉能力做图文混合识别。本地工具胜在离线、批量快;视觉模型胜在能理解复杂版式,比如把标题、表格框、图片说明一起还原成 Markdown 结构。两者各有优劣,后面我会给一个完整的决策流程。
2.2 Word 文档:样式层级清晰,但转纯文本后层级全丢
和 PDF 相反,Word 文件天生就有完整的语义结构。标题、正文、列表、表格、批注,全都存在。问题是你不能直接把.docx丢给 AI,或者丢进去之后让 AI 自己“猜结构”。很多初版总结工具就是把 docx 当纯文本读出来,结果所有标题变成普通句子,层级信息全丢。更麻烦的是,Word 里经常有修订记录、批注、文本框、目录域代码,这些内容混在一起会让提取结果非常脏。
所以 Word 转 Markdown 的关键不是“能不能转”,而是“转完之后层级保留得全不全”。我个人强烈推荐用 Pandoc 来做 Word 的转换,它能正确识别Heading 1、Heading 2、普通段落、列表和表格,并映射成对应层级的 Markdown 标记。但前提是,原文档的作者规范地使用了样式,而不是手动调字号模拟标题。如果原文档是全手动格式的那种,Pandoc 出来也会比较乱,这时候需要用 python-docx 逐段识别字号、加粗、缩进再人工映射到 Markdown 层级。
2.3 Excel 与 PPT:二维结构最难啃的骨头
Excel 和 PPT 是我认为最容易被误处理的两种格式。Excel 的问题在于它的信息天然是二维的。一个合并单元格、一条跨行公式、一个隐藏列,在转成纯文本之后,要么信息丢失,要么格式错乱。比如常见的“合并单元格”,你用 pandas 直接读出来,合并部分的单元格只有左上角有值,其他全是 NaN;但人眼看到的是一个跨多行的分类名。如果直接把这些内容给 AI,它根本无从判断某个数值归属于哪个类别。我的处理办法是先对 DataFrame 做“向前填充”,把合并单元格的值复制到空白区域,再转成 Markdown 表格。这样 AI 看到的每一行都是完整的“类别-指标-数值”三元组。
PPT 则相反,它的信息是碎片化的。幻灯片上散落着几十个文本框、形状、图表,视觉上是一个整体,底层却是独立对象。直接提取文本,你得到的是没有顺序、没有层级的大杂烩。我在做 PPT 转换时的思路是,按照形状在页面上的坐标排序线性化,再根据文本框的相对位置和字号判断它是标题、要点还是说明文字。坐标排序能解决视觉顺序问题,文本聚类则能把零散文本框合并成逻辑块。这类转换工具没有开箱即用的完美方案,需要一定的脚本加工。
3. 一套能直接抄的 Markdown 转换流水线
先说结论:不要做梦有一个命令行工具能把任何文档一键变 Markdown。我试过的所谓“全自动转 MD”工具,面对复杂版式时经常是 80 分水平,剩下 20 分需要自己写脚本补。我更推荐“先分类、再选路”的流水线方案。
3.1 第一步:判断文件类型和解析难度
无论拿到什么文件,先跑一遍这个诊断脚本,确认有没有文本层、有没有表格、有没有图片。下面这个 Python 片段,我常放在流水线最前面当探针:
import fitz # PyMuPDF def probe_pdf(path): doc = fitz.open(path) total_chars = 0 img_count = 0 for page in doc: total_chars += len(page.get_text().strip()) img_count += len(page.get_images()) print(f"总字符数: {total_chars}") print(f"总图片数: {img_count}") if total_chars < 200 and img_count > 0: print("判定: 扫描版PDF, 需要走OCR") else: print("判定: 有文本层, 可以走直接提取")这个探针非常重要。我见过有人拿着一个扫描件跑完整个流程,然后质问我“你的 Markdown 转换方案为什么提取出来全是乱码”。问题出在起点就选错了。
3.2 有文本层 PDF 的转换:PyMuPDF 抽取 + 结构增强
当确认有文本层后,处理就轻松很多。最基础的方案是page.get_text("text")直接拿纯文本,但这样拿到的 Markdown 不够好。我更推荐page.get_text("dict"),它把每个文本块的位置、字体、大小都返回了,然后可以根据坐标和字号重建标题层级:
import fitz def pdf_to_markdown(path, output_path): doc = fitz.open(path) md_lines = [] for page_num, page in enumerate(doc): blocks = page.get_text("dict")["blocks"] for block in blocks: if "lines" not in block: continue for line in block["lines"]: spans = line["spans"] if not spans: continue # 取第一个span的字号和字体作为判断依据 size = spans[0]["size"] text = "".join(span["text"] for span in spans).strip() if not text: continue # 字号阈值需要根据实际文档调整 if size > 18: md_lines.append(f"# {text}") elif size > 14: md_lines.append(f"## {text}") elif size > 12: md_lines.append(f"### {text}") else: md_lines.append(text) with open(output_path, "w", encoding="utf-8") as f: f.write("\n".join(md_lines))注意,这个方案有个需要人工校准的地方:字号阈值。不同文档的“标题字号”差别很大,你需要先跑一段统计代码看一下文本块字号分布,再设定合理的阈值。我就是这样调参的:先打印排序后的字号列表,发现正文集中在 10.5 和 12,标题在 16 以上,于是把 14 当分界线。这个步骤看着不高级,但能显著提升转换质量。
3.3 无文本层 PDF:OCR 路径与质量兜底
无文本层的 PDF,不要浪费时间研究“提取”工具,直接走 OCR。我试过本地 Tesseract、PaddleOCR 和专业转换工具,各有脾气。Tesseract 对中文的支持需要额外下载语言包,识别率一般,胜在简单;PaddleOCR 对中文版面识别友好很多,但对复杂表格的还原能力弱;如果你要连图片里的表格也变成 Markdown 表格,我推荐用带视觉理解能力的模型做一次“版面还原”。
这里有个非常实用的流程,我称为“OCR 前先拆分版面”。先用工具检测页面里的图片区域、文本区域、表格区域,再对每个区域分别识别。这个“版面分析”步骤能避免图片本身被当成文字段,也避免表格和正文混在一起。拆分后再识别,准确率提升非常明显。OCR 完成后,一定要人工抽检。不要相信任何 OCR 的置信度,尤其中文姓名、编号、金额这类信息,错了就是错了,AI 后续再怎么理解也救不回来。
3.4 Word、Excel、PPT 的批量转换脚本
Word 我用 Pandoc 最多。单文件转换命令很简单:
pandoc input.docx -t markdown -o output.md但我一般不会直接跑完就收工,因为 Pandoc 默认会把批注、修订内容处理成很古怪的语法。所以我会加--extract-media=assets把图片单独抽出来,再手动删掉文档里无意义的批注段。Pandoc 最大的优点是保留了标题层级,这在全文检索和后续切片时价值极大。
Excel 我推荐用 pandas + 手动 Markdown 渲染的方式,而不是直接.to_markdown()。原因有两个:一是to_markdown()对合并单元格处理很差,二是它不处理多 sheet。我封装了一个函数,遍历每个 sheet,先做前向填充,再输出一个带## Sheet名的文件:
import pandas as pd def excel_to_markdown(path, output_path): sheets = pd.read_excel(path, sheet_name=None) with open(output_path, "w", encoding="utf-8") as f: for sheet_name, df in sheets.items(): f.write(f"## {sheet_name}\n\n") df = df.ffill() # 处理合并单元格 f.write(df.to_markdown(index=False)) f.write("\n\n")PPT 的转换则用 python-pptx,按坐标排序后把每个文本框转成列表项。这里我不建议给太多自动化空间,因为 PPT 的视觉层级太主观,尽量只做“保留文字和坐标”这一步,后续人为调整会比完全不同。
3.5 处理“双栏 PDF”和“复杂版面”的特殊说明
双栏排版的学术论文是重灾区。直接提取文本,内容顺序会左右穿插。我处理这类文档时用一个简单策略:获取每个文本块的bbox坐标,然后先按 y 坐标排序,再按 x 坐标分栏。粗略的逻辑是,如果页面的中心线左侧存在超过一半的文本块,就把页面切成左右两栏,分别按 y 排序后拼接,最后再合成一栏顺序。这个逻辑能够覆盖大部分标准双栏文档,但对跨栏标题、浮动体、脚注仍会有错位。实测下来,能把双栏顺序错误从 70% 降到 10% 左右,剩下的 10% 只能靠后续人工校稿。
对于复杂版面,比如画册、设计稿导出的 PDF,我建议直接放弃“精确还原结构”,只追求“内容完整、层级可读”。因为这种文档的视觉语言本身就多种多样,你不可能用一套通用规则去通吃。
4. 转换完成不等于结束:校验、切片与投喂姿势
把文件转成.md只是第一步。很多人以为拿到 Markdown 就能直接甩给 AI,结果问答效果依然一般。问题通常出在转换后的内容“太长太乱”,或者没有按 AI 习惯的方式组织。这里有两个关键动作:校验和切片。
4.1 转换质量校验清单,我每次都跑这四项
- 乱码检测。重点查看中文是否出现
锟斤拷、�这类替换字符,出现这些,说明编码链断了,需要检查源文件的编码声明和转换工具的编码参数。 - 表格结构。用 Markdown 表格语法的话,去看列数是否一致。
| 姓名 | 年龄 |和| 姓名 | 年龄 | 备注 |混在一起,AI 会困惑。我会写一个脚本统计每个表格块的列数,不匹配的标红。 - 标题层级连续性。如果文档出现
##直接跳到####,中间漏了###,说明标题层级可能被误判。我会用正则扫一遍层级跳跃,再人工确认。 - 图片引用有效性。转出来的 Markdown 可能有
这类引用,但附件在后续处理中容易被遗漏。我会确认附件存在,必要时转成 base64 内嵌,或者统一改名成显式路径。
校验是性价比最高的一步,花五分钟检查,可能避免 AI 在后续回答里编造一个表格。
4.2 基于 Markdown 结构做切片,而不是按字符数硬切
给 AI 喂长文档时,很多人会直接把整篇 Markdown 丢进去,美其名曰“上下文长”。但上下文长了,模型对细节的召回反而会下降。我习惯在喂给 AI 之前,先按 Markdown 标题层级做切片。核心思路:#级别是一级主题,##级别是二级主题,把每个二级主题下的内容作为一个切片单元。这样每个切片都有自己的语义边界,AI 不会把上一段的背景知识误套到下一段。
切片的时候,还要考虑重叠区。我的默认策略是每个切片保留 100-200 字符的重叠区域,尤其是连续表格和列表之间,重叠能避免切在关键数据中间。如果是数字类内容比较多,重叠可以多一点;如果是故事叙述性内容,重叠反而会造成混淆,可以少一点。
切片之后,建议把切片存成 JSONL 格式,每一行包含id、title、content、metadata。metadata 里存原文文件名、页码、标题路径,这个信息在未来做 RAG 检索、引用溯源时非常有用。我见过不少团队直接拿原文全文压进向量库,检索精度和带标题元数据的版本差了不止一个档次。
4.3 喂给 AI 的两种姿势:一次性粘贴 vs 检索式问答
弄清自己的使用场景很重要。如果是短文档,比如三五页的合同扫描件、一份产品说明书,转换后直接整体丢给 AI 对话窗口即可。但要注意,即便整体丢,我也推荐先做一遍“结构提示”。比如我会在正文前加一行:
以下是一个文档的 Markdown 结构化内容,请基于原文回答,不要补充外部信息。这行提示的作用是让模型知道这不仅是文本,而是需要遵守结构语义的材料,回答时保持信息边界。
如果是长文档库,比如几十份研究报告、多份产品手册,一次性全丢就不现实了。这时候需要把这些 Markdown 切片喂入向量库,做一个检索问答系统。检索时,优先按标题元数据过滤,再按向量相似度排序。实测下来,这种“结构切片+元数据过滤+向量检索”的组合,比“装全文进去硬搜”的准确性提升很大,尤其在跨文档对比时能避免 S 文档的信息被 T 文档污染。
5. 我踩过几次坑之后总结的几个进阶经验
转换、校验、切片这套流程跑熟之后,我慢慢发现几个能让最终效果再上台阶的经验。它们不一定适合所有场景,但遇到相似问题时可以直接试试。
5.1 在 Markdown 里显式标注“低置信度区域”
OCR 结果不可能百分百准确。与其把所有错字留给 AI 猜,不如在 Markdown 里显式标注哪些地方可能是错的。我常用的做法是,对 OCR 置信度低于某个阈值的行,加一个 HTML 注释标记:
<!-- OCR 置信度较低,文字可能有误,请核对下面这段 --> 2024年度第三季度收入为3.75亿元这个注释对普通读者不可见,但 AI 在读取时能感知到这段文字的置信度状态。实测中,它很可能会回答“该数据可能存在识别误差,请以原始文件为准”,而不是自信地报一个错误数字。这个技巧对于合同金额、编号这类关键信息尤其有价值。
5.2 表格别等 AI 自己理解,先“人工加一行解释”
很多转换后的表格只有表头和数值,但表头往往有歧义。比如“增长率”到底是环比还是同比?在原始文件里可能是单位小字里的注释,转换后注释容易丢。我会在表格的 Markdown 代码块前加一行说明:
<!-- 说明: 表格数据均来自财报附注,同比增长率以括号内的百分比为准 --> | 年度 | 主营收入(万元) | 同比增长率(%) |这类前置注释为 AI 提供了数据语义的上下文,特别适合财报、实验数据表这类需要精确解读的内容。加了这层之后,AI 回答里的“根据表格数据,收入同比增长 X%”会明显更可靠。
5.3 把流水线固化成监控文件夹的自动脚本
走到这一步,其实你已经不需要每次都手动转文件了。我习惯用一个简单的目录监控脚本,把转换、校验、切片串起来,每次往“待处理”文件夹丢文件,会自动输出 Markdown 和 JSONL 到“已处理”文件夹。实现上用 Python 的watchdog监听目录变化即可,代码逻辑不复杂。
但我必须提醒一个坑:自动化的同时一定要留“人工复核”的口子。我见过一个自动化流水线,把项目周报自动转 Markdown 后喂给分析助手,结果把一张扫描的目录页当成表格内容输出,后续所有分析都引用这份错误数据,花了不少时间去排查。自动化可以做琐碎搬砖,但关键节点的抽检不能少。我的做法是,每日查看一次转换日志里的异常文件列表,重点看“没有文本层”“表格列数不一致”这些标记。
另外,如果你长期处理同一个来源的文档,建议把“这套文档的字号阈值、表格结构特征”沉淀成一份配置。我在脚本里用了一个digest.yaml,专门记录不同来源文档的转换参数。这样每次跑流水线就不会因为某个新项目文件格式略有不同而翻车。
6. 最后补一句大实话:别把这步当负担,它其实是最稳的“翻译层”
整套流程跑下来,你会发现“转 Markdown”其实是在人和机器之间搭了一层翻译层。人用视觉理解文件,模型用 token 序列理解文件,Markdown 恰好是两者都能高效对齐的中间语言。不要指望模型某天能有完美排版感知能力,然后跳过这一步。至少在最近几年,把文件结构化之后再给 AI,依然是最经济、最可控的最优解。
我自己现在办公桌上的所有文档,都默认经过“转 Markdown + 抽检关键页”这一关,再交给 AI 去做总结、对比或问答。那些跳过这步直接硬喂的版本,我不是没用过,结果就是让我多花两三个小时去校正一段幻觉内容。所以如果你正在被 AI 答非所问折磨,别急着换大模型,先回去看看喂进去的文件长什么样。这是我最想分享的一个经验。