单项目深读

markitdown:把 PDF、Word、Excel 统一转成 Markdown 喂给 LLM

微软开源的 Python 工具,把 PDF、Word、Excel 等文档转成 Markdown,为 RAG 与文档问答做前置清洗。

|INS-0020

RAG 系统里最脏的活,从来不是检索排序,也不是 prompt 调优,而是把一堆格式各异的文件变成模型能读的文本。PDF 里套着表格,Word 里嵌着图片,Excel 有多张 sheet,PPT 的正文散落在文本框里——这些结构一旦被粗暴地抽成纯文本,标题层级、列表、表格行列关系全部塌掉,后面再怎么切 chunk、怎么召回,都是在垃圾上做精排。

markitdown 就是冲着这一层来的。它不碰检索,不碰编排,只做一件事:把各种文件转成 Markdown,并且尽量保留文档结构。README 里对标的对象是 textract,但它明确写了差异点——focus on preserving important document structure and content as Markdown(including: headings, lists, tables, links, etc.)。这个定位很克制,也很关键。

为什么偏偏是 Markdown

README 里给的理由值得单独拎出来:Markdown 极接近纯文本,标记极少,但仍能表达标题、列表、表格、链接这些结构。更实际的一点是,主流 LLM(README 点名了 GPT-4o)本身就"说" Markdown,经常在回答里不请自来地用 Markdown 格式,说明训练数据里有大量 Markdown 文本,模型理解它没什么障碍。附带好处是 token 效率高。

所以 markitdown 的目标读者不是人,是文本分析工具。README 写得很直白:输出通常还算可读、对人类友好,但它是给 text analysis tools 消费的,不是做高保真文档转换的最佳选择。这句话决定了它的能力边界——它不会为了排版还原去死磕,而是为了"模型能读懂结构"做取舍。

架构:一个分发器 + 一组格式转换器 + 三层可选增强

README 没有给架构图,但从它的功能描述、CLI 参数、Python API 和可选依赖的组织方式,可以清楚归纳出它的分层。核心是一个转换分发逻辑:你丢进来一个文件,它判断类型,挑一个能处理该类型的 converter,跑完返回一个带 markdown 属性的结果对象。

第一层:入口层。 两条路径,CLI 和 Python API。CLI 是 markitdown path-to-file.pdf,支持 -o 输出到文件,也支持管道(cat file.pdf | markitdown)。Python 侧是 MarkItDown() 实例加 md.convert(...)。入口层还负责解析一堆开关:--use-plugins、--list-plugins、-d(Document Intelligence)、--use-cu(Content Understanding)等。README 的安全提示也落在这一层:MarkItDown 以当前进程的权限做 I/O,和 open()、requests.get() 一样能访问进程能访问的资源,所以在不可信环境里要自己清洗输入,并且**调用范围最窄的 convert_* 函数**(比如 convert_stream() 或 convert_local()),而不是无差别地丢任意路径进去。

第二层:converter 层(内置转换器)。 这是项目的主体。README 列出的支持格式覆盖了 PDF、PowerPoint、Word、Excel、Images(EXIF 元数据 + OCR)、Audio(EXIF 元数据 + 语音转写)、HTML、文本类格式(CSV/JSON/XML)、ZIP(遍历内部内容)、YouTube URL、EPub 等。每个格式对应一个 converter,各自带自己的解析依赖。这一层是离线、本地计算的,也是默认路径。

第三层:可选增强层。 这一层是 markitdown 真正的架构看点,它把一个"格式转换库"扩展成了"可插拔的转换管线",有四个并行的增强入口:

增强入口触发方式作用依赖
可选依赖(extras)pip install 'markitdown[pdf, docx, pptx]'按格式装依赖,控制体积各格式独立
第三方插件--use-plugins / enable_plugins=True扩展 converter 能力,默认关闭插件包
LLM 视觉llm_client + llm_model图片描述,目前仅 pptx 与图片文件OpenAI 兼容客户端
云端转换docintel_endpoint / cu_endpoint更高精度的版面分析、结构化字段、音视频Azure 服务(计费)

这四层不是互斥的,是叠加的。一个 PDF 可以走内置 converter,也可以被 cu_file_types 限定路由到 Content Understanding;一个带图的 PDF 可以同时挂 markitdown-ocr 插件和 llm_client。

数据流转大致是:文件 → 类型判定 → 选 converter(内置 / 插件 / 云端,取决于开关)→ 转换 → 返回结果对象 → 取 .markdown。失败时的行为也写清楚了:如果 LLM 客户端重试耗尽或遇到不可重试错误,MarkItDown 会尝试其他适用的 converter,只有全部失败才抛 FileConversionException。这是一个 fallback 链的设计,不是单点失败即终止。

可选依赖的粒度

值得单独说,因为这是它控制安装体积的手段。[all] 全装,也可以按需:

  • [pptx] / [docx] / [xlsx] / [xls] / [pdf] / [outlook]:各格式独立
  • [az-doc-intel] / [az-content-understanding]:Azure 集成
  • [audio-transcription]:wav 和 mp3 的语音转写
  • [youtube-transcription]:抓 YouTube 视频转写

只处理 PDF 和 Office 三件套的话,pip install 'markitdown[pdf, docx, pptx]' 就够了,不用背全部依赖。

跑起来:从装到出 Markdown

安装:

bash
pip install 'markitdown[all]'

或者从源码装:

bash
git clone git@github.com:microsoft/markitdown.git cd markitdown pip install -e 'packages/markitdown[all]'

CLI 最小用法,三种写法都行:

bash
markitdown path-to-file.pdf > document.md markitdown path-to-file.pdf -o document.md cat path-to-file.pdf | markitdown

Python API 最小用法:

python
from markitdown import MarkItDown md = MarkItDown(enable_plugins=False) # Set to True to enable plugins result = md.convert("test.xlsx") print(result.markdown)

注意 enable_plugins=False 是默认值——插件默认关闭,要用得显式打开。

挂 LLM 做图片描述(目前只支持 pptx 和图片文件):

python
from markitdown import MarkItDown from openai import OpenAI client = OpenAI(max_retries=5) md = MarkItDown(llm_client=client, llm_model="gpt-4o", llm_prompt="optional custom prompt") result = md.convert("example.jpg") print(result.markdown)

max_retries 控制 OpenAI 客户端的自动重试,README 说默认是 2,设成 5 意味着最多六次尝试带退避。

Docker 方式:

sh
docker build -t markitdown:latest . docker run --rm -i markitdown:latest < ~/your-file.pdf > output.md

插件机制:OCR 是怎么"零依赖"加进来的

插件默认关闭,markitdown --list-plugins 列出已装的,markitdown --use-plugins path-to-file.pdf 启用。找插件搜 GitHub 的 #markitdown-plugin 标签,开发插件看 packages/markitdown-sample-plugin。

README 里详细展开的是官方 markitdown-ocr 插件,它很能说明这套架构的复用逻辑:给 PDF、DOCX、PPTX、XLSX 四个 converter 加上 OCR,从嵌入图片里抽文本,用的是 LLM Vision——也就是 markitdown 本来就用于图片描述的同一套 llm_client / llm_model 模式。它明确写了 no new ML libraries or binary dependencies required,不引入新的 ML 库或二进制依赖。

bash
pip install markitdown-ocr pip install openai # or any OpenAI-compatible client
python
from markitdown import MarkItDown from openai import OpenAI md = MarkItDown( enable_plugins=True, llm_client=OpenAI(), llm_model="gpt-4o", ) result = md.convert("document_with_images.pdf") print(result.markdown)

一个细节值得注意:如果没提供 llm_client,插件仍然加载,但 OCR 会被静默跳过,回退到内置的标准 converter。这种"软降级"设计和前面说的 fallback 链是一致的——它优先保证转换能出结果,而不是因为某个增强能力缺失就整体失败。

三档转换质量:内置 / Doc Intel / Content Understanding

这是 markitdown 最容易被低估的部分。它不是只有"本地转换"一档,而是给了三档,README 用一张表把差异摆得很清楚:

能力内置 converterAzure Document IntelligenceAzure Content Understanding
文档转换离线,按格式抽取云端版面抽取云端多模态抽取
结构化字段不支持该集成未暴露analyzer 字段输出为 YAML front matter
自定义 analyzer不支持该集成不可配置支持 cu_analyzer_id
音频与视频基础音频,无视频不支持音频与视频 analyzer
成本仅本地计算Azure API 计费Azure API 计费

三档的定位差异是清晰的:

  • 内置:离线、免费、按格式抽取。覆盖广,但扫描版 PDF、复杂表格、多页文档的抽取质量有限,也没有结构化字段。
  • Document Intelligence:云端版面抽取,CLI 用 -d -e "<endpoint>",或设 MARKITDOWN_DOCINTEL_ENDPOINT 环境变量后只传 -d。Python 侧是 MarkItDown(docintel_endpoint=...)。它提升了版面质量,但这个集成不暴露结构化字段,也不支持自定义 analyzer。
  • Content Understanding:README 说它是唯一能处理视频的选项,也是音频更高质量的云端选项。它支持 prebuilt 或自定义 analyzer,把领域字段(发票金额、收据日期、合同条款)抽出来序列化成 YAML front matter。一个 cu_endpoint 处理文档、图片、音频、视频,自动路由 analyzer。

Content Understanding 的零配置用法:

python
from markitdown import MarkItDown md = MarkItDown(cu_endpoint="<content_understanding_endpoint>") result = md.convert("report.pdf") # documents → prebuilt-documentSearch result = md.convert("meeting.mp4") # video → prebuilt-videoSearch result = md.convert("call.wav") # audio → prebuilt-audioSearch print(result.markdown)

自定义 analyzer 抽字段:

python
md = MarkItDown( cu_endpoint="<content_understanding_endpoint>", cu_analyzer_id="my-invoice-analyzer", ) result = md.convert("invoice.pdf") print(result.markdown)

输出会带上 YAML front matter,形如:


---
contentType: document
fields:
  VendorName: CONTOSO LTD.
  InvoiceDate: '2019-11-15'
---
<!-- page 1 -->
...

CLI 侧对应 --use-cu --cu-endpoint "<endpoint>",endpoint 也可以先 export MARKITDOWN_CU_ENDPOINT=... 再只传 --use-cu。

两个设计细节值得记:当设了 cu_analyzer_id,converter 会按 analyzer 的模态自动把它限定到兼容的文件类型,不兼容的(比如用文档 analyzer 处理音频)自动路由到默认 prebuilt analyzer。另外,每个走 CU 的 convert() 都是一次计费的 Azure 调用,所以提供了 cu_file_types 来限制哪些格式走 CU:

python
from markitdown.converters import ContentUnderstandingFileType md = MarkItDown( cu_endpoint="<content_understanding_endpoint>", cu_file_types=[ContentUnderstandingFileType.PDF], # only PDFs use CU )

这个参数的存在本身就说明维护者清楚成本是真实约束,不是所有文件都值得上云。

和同主题项目比,它站在哪一格

把 markitdown 放进这批对照项目里,定位差异一目了然——它们大多在下游,markitdown 在上游。

  • headroom 在内容送进模型前压缩 tool 输出、日志、文件和 RAG chunk 来省 token。它和 markitdown 是接力关系:markitdown 负责把文档变成结构化的 Markdown,headroom 负责把已经变成文本的东西再压瘦。一个管"能不能读",一个管"读得贵不贵"。
  • litellm 是 AI Gateway,统一接口调 100 多个 LLM API,做路由、负载均衡、成本追踪。markitdown 里的 llm_client 恰恰可以是任意 OpenAI 兼容客户端,两者不在一个层面,但可以组合——markitdown 用 litellm 背后的模型做图片描述或 OCR。
  • Langchain-Chatchat 是本地知识库问答应用,解决私有文档接入 LLM。它需要前置的文档清洗,markitdown 正是这一步的候选工具:先转 Markdown,再建库、检索、作答。README 的 topics 里直接列了 langchain 和 autogen,说明它被设计成能嵌进这类编排栈。
  • langfuse 做追踪、评估、调试,关心上线后回答为什么变差。它不解决输入格式问题,但转换质量差会直接反映在它的评估指标上。
  • deepagents 是 LangChain 的 agent harness,内置任务规划、文件操作、子 agent。agent 要读文件,就需要一个把各种格式变成可读文本的层,markitdown 补的正是这个空。

一句话概括:这批项目里,markitdown 是唯一专注"输入侧格式归一"的那个。它不做编排、不做网关、不做评估,只把脏输入洗干净,交给下游。

它适合谁,以及几个必须知道的约束

适合:搭 RAG 或文档问答、需要把 PDF/Office/HTML/EPub 等混合资料统一成 Markdown 再切 chunk 的团队;需要把图片、音频、YouTube 转写也纳入同一管线的场景(走插件或 CU);想在本地离线跑、不想为每次转换付费的场合(内置 converter)。

必须知道的约束:

  1. 不是高保真转换器。README 明说输出是给文本分析工具消费的,人类阅读的高保真转换不是它的目标。
  2. 安全边界等于进程边界。它以当前进程权限做 I/O,不可信环境要清洗输入,并调用最窄的 convert_* 函数(convert_stream()、convert_local()),别把任意路径直接喂进去。
  3. 插件默认关闭,要用得显式开;markitdown-ocr 在缺 llm_client 时会静默跳过 OCR,回退内置 converter——如果你以为 OCR 生效了但其实没有,输出里不会报错。
  4. 云端转换计费。Doc Intel 和 CU 都是按调用计费的 Azure API,CU 还提供了 cu_file_types 让你限定范围。
  5. Python 版本要求 3.10 到 3.14,README 建议用虚拟环境避免依赖冲突。

项目基本面

项值
源码github.com/microsoft/markitdown
许可MIT
语言Python
星标190788
forks14191
open issues737
topicsautogen, autogen-extension, langchain, markdown, microsoft-office, openai, pdf
仓库创建2024-11-13
最近推送2026-10-04

官网地址:素材中未提供,故不列出。

MIT 许可、微软维护、近 19 万星标——这些数字说明它已经成了"文档转 Markdown 喂 LLM"这个细分动作的事实默认选项。但真正决定它价值的不是星标,而是那套分层设计:内置 converter 保证离线可用和格式广度,插件机制让 OCR 这类能力以零新依赖的方式接入,LLM 视觉和 Azure 云端转换则把质量上限往上抬了两档。你可以只用第一层,也可以按需叠到第四层,而 fallback 链保证任何一层出问题时转换仍尽量出结果。

对一个前置清洗工具来说,这种"默认能用、按需增强、失败可降级"的结构,比任何单点精度都更重要。