ESC
开源 3 分钟阅读

anydoc:用Rust构建的文档转Markdown库,支持Word、PowerPoint、Excel、PDF等格式

anydoc是一个快速Rust库,可将Word、PowerPoint、Excel、OpenDocument、RTF、EPUB、CSV和PDF等文档转换为干净的GitHub风格Markdown,并提供Node.js和Python绑定。由Firecrawl构建,旨在将任意办公文档在几毫秒内转换为适合LLM的Markdown。支持内容识别格式、嵌入资源、完整文档结构,中位转换时间低于5毫秒。

来源:GitHub

anydoc

Crates.io npm PyPI License: MIT skills.sh

一个快速的Rust库,可将多种文档(Word、PowerPoint、Excel、OpenDocument、RTF、EPUB、CSV和PDF)转换为干净的GitHub风格Markdown。包含Node.js和Python绑定。

由Firecrawl构建,旨在将任意办公文档在单位数毫秒内转换为可供LLM直接使用的Markdown,无论输入什么格式,输出都保持一致。它驱动着Firecrawl Parse,如果你不想自己运行,托管API也能提供相同的转换,外加针对anydoc无法识别的扫描页面所训练的OCR模型。

快速开始

智能体技能(Agent Skill)

anydoc 以 Agent Skill 形式提供,因此你的智能体可以读取它遇到的任何文档:

npx skills add firecrawl/anydoc

该技能教会智能体使用anydoc CLI转换文档。兼容Claude Code、Codex、Cursor、OpenCode以及任何其他兼容智能体。

CLI

npx @firecrawl/anydoc report.docx               # Markdown输出到stdout
npx @firecrawl/anydoc slides.pptx -o slides.md  # 或输出到文件
npx @firecrawl/anydoc - --format csv < data.csv # 从stdin读取

npx 会在首次运行时为你的平台下载预构建二进制文件。如需永久安装anydoc命令,可全局安装:npm install -g @firecrawl/anydoc。运行 anydoc --help 查看所有选项。

Node.js

npm install @firecrawl/anydoc
import { toDocument, toMarkdown, toMarkdownBytes } from '@firecrawl/anydoc';

// 从文件路径:
const markdown = await toMarkdown('report.docx');

// 从字节,格式从内容中检测:
const fromBytes = await toMarkdownBytes(bytes);

// 或指定格式,无签名格式(如CSV)需要:
const fromCsv = await toMarkdownBytes(bytes, 'csv');

// 或停在文档模型,其中也包含嵌入资源:
const document = await toDocument(bytes);

完整API参考:node/README.md

Python

pip install firecrawl-anydoc
import anydoc

# 从文件路径:
markdown = anydoc.to_markdown("report.docx")

# 从字节,格式从内容中检测:
markdown = anydoc.to_markdown_bytes(data)

# 或指定格式,无签名格式(如CSV)需要:
markdown = anydoc.to_markdown_bytes(data, "csv")

# 或停在文档模型,其中也包含嵌入资源:
document = anydoc.to_document(data)

完整API参考:python/README.md

Rust

cargo add anydoc
// 从文件路径:
let markdown = anydoc::to_markdown("report.docx")?;

// 从字节,格式从内容中检测:
let markdown = anydoc::to_markdown_bytes(&bytes, None)?;

// 或指定格式,无签名格式(如CSV)需要:
let markdown = anydoc::to_markdown_bytes(&bytes, anydoc::Format::Csv)?;

// 或停在文档模型,其中也包含嵌入资源:
let document = anydoc::to_document(&bytes, None)?;

特性

  • 每种格式统一输出。 每种格式都解析为共享的文档模型,并通过单一的Markdown序列化器渲染,因此无论是2003年的.doc还是昨天的.pptx,转义、表格、标题锚点和脚注的行为都完全一致。
  • 完整文档结构。 带锚点的标题、粗体/斜体/删除线、行内代码和代码块、链接和内部交叉引用、项目符号/编号/嵌套/任务列表(保留原始编号)、带合并单元格和表头的表格、块引用、脚注和尾注,以及演讲者备注。
  • 嵌入资源。 图片和嵌入对象在Markdown中渲染为替代文本,原始字节保留在文档模型上,并标记媒体类型。具有外部URL的图片则成为普通Markdown图片。
  • 基于内容的格式检测。 格式从字节本身读取(PDF头、RTF开组、OLE流名称、ZIP包 mimetype),因此打错标签的文件也能正确转换。
  • 速度快。 纯Rust实现,无机器学习模型,无外部服务。每个文档的中位转换时间低于5毫秒。
  • 不碍事的绑定。 Node.js转换在libuv线程池上运行,永不阻塞事件循环;Python释放GIL,其他线程可继续运行。TypeScript类型和Python存根随包发布。
  • 内置PDF支持。 基于文本的PDF通过pdf-inspector本地转换,无需OCR服务。
  • 适配智能体。 以Agent Skill形式提供:一条npx skills add firecrawl/anydoc命令即可让任何智能体读取办公文档。

支持的格式

格式扩展名
Word.doc, .docx, .docm
PowerPoint.ppt, .pps, .pot, .pptx, .pptm, .ppsx, .ppsm
Excel.xls, .xlsx, .xlsm, .xlsb
OpenDocument.odt, .ods, .odp
Rich Text Format.rtf
EPUB.epub
CSV.csv
PDF.pdf

基准测试

anydoc 与其他六个转换器在100个跨十四种格式的真实文档上进行了对比。分数范围0到100,越高越好;速度是转换一个文档的中位时间。

工具格式数中位毫秒文档数分数完整性结构格式整洁度
anydoc14/144.7948088787779
libreoffice12/141129.5874059434324
unstructured8/14572.9586576625267
markitdown6/14134.8336580676153
pandoc5/14102.1345775575839
docling4/14513.6215763595752
mammoth1/1452.587085687455

按格式逐一对比:

格式anydoclibreofficeunstructuredmarkitdownpandocdoclingmammoth
doc885868----
docm8249-----
docx86535672686870
epub74-747753--
odp8722-----
ods8242-----
odt805270-61--
ppt8025-----
pptx7622-59-50-
rtf895848-46--
xls77406864---
xlsm7030-----
xlsx70316955-51-

质量评分方式: 一个LLM评判器(Claude Sonnet 5)对两个工具的输出进行盲测,与基准真相对比:文档前六页由LibreOffice渲染为图像。每个输出按完整性、结构、格式和整洁度评分。每对输出在交换顺序后评判两次,以消除位置偏差,共479个评判。每个工具的score是其支持格式的格式分数平均值,因此某个格式占比很高的语料库不会造成偏差。这也意味着每行平均了不同的格式集(mammoth的70仅来自docx,而anydoc的80横跨全部十四种),因此按格式对比的表才是公平的比较。

速度测试为在Ryzen 9 9950X3D(Windows 11, 64 GB DDR5-6400)上每个文档一次热转换。anydoc和Python库的计时不包括进程生成;CLI工具则包括,因为这是它们的使用方式。测试框架位于bench/;语料库不可再分发且不在仓库中。

最佳使用场景: 需要接收混合办公文档并输出一致、结构化Markdown的流水线。在此对比中,anydoc是唯一覆盖全部十四种格式的工具,在除EPUB外的每个被评判格式上得分最高,且转换速度比第二快的工具快一个数量级。

格式检测

格式从文件内容中读取,使用其规范指定的标记:PDF头、RTF开组、OLE流名称、ZIP包mimetype和内容类型。CSV没有此类标记,因此使用扩展名或显式格式名来指定。

Format::from_bytes(&bytes); // Some(Format::Docx),无匹配时为None
Format::from_extension("pptm"); // Some(Format::Pptx)
Format::from_path(Path::new("report.odt")); // Some(Format::Odt)

同样的三个函数在Node(formatFromBytes,…)和Python(anydoc.format_from_bytes,…)中也存在。

工作原理

文档字节
  │
  ├─► 格式检测      → 内容标记,而非扩展名
  │
  ├─► 格式解析器     → 每种格式一个(doc, docx, ppt, pptx, xls,
  │                            xlsx, odt/ods/odp, rtf, epub, csv)
  │         │
  │         └─► Document     → 共享模型:块、行内元素、表格、
  │                            脚注、资源
  │               │
  │               └─► GFM序列化器 → Markdown
  │
  └─► PDF → pdf-inspector    → 直接输出Markdown

由于每种格式都汇入相同的文档模型和序列化器,输出问题只需修复一次。针对docx的表格转义修复会自动应用到rtf、odt及其他所有格式。

开发

cargo test
cd node && npm install && npm run build && npm test
cd python && pip install maturin && maturin develop && python -m unittest discover -s tests

tests/fixtures/下有一套已提交的固定语料进行快照测试,tests/robustness.rs对每个固定样本进行变异测试,fuzz/为每种格式提供cargo-fuzz目标。速度和质量基准位于bench/。

发布时打标签v<version>,从.github/workflows/release.yml发布crate、npm包和PyPI wheels。版本号存在于三处,发布时同步更新:

许可证

MIT