為什麼你需要關注 anydoc

給大模型餵文件時,格式轉換品質直接影響 RAG 效果。Word 裡的表格錯位、PPT 的層級丟失、PDF 的段落斷裂——每一個格式問題都會讓向量檢索的精度大打折扣。過去我們不得不把 pandoc、python-docx、LibreOffice 等多個工具拼在一起,才能勉強覆蓋主流辦公格式,維護成本高、輸出不一致。

anydoc 是 Firecrawl 團隊用 Rust 從零打造的文件轉換庫,一個 API 呼叫搞定 14 種格式,中位數轉換時間僅 4.4 毫秒。它已在 GitHub 斬獲 12000+ Star,正在成為 RAG/LLM 工具鏈中文件預處理的新標準。

支援的 14 種格式

anydoc 的格式覆蓋面是目前所有開源轉換工具中最廣的:

格式類別 支援的副檔名
Word 文件 .doc, .docx, .docm
PowerPoint 簡報 .ppt, .pps, .pot, .pptx, .pptm, .ppsx, .ppsm
Excel 表格 .xls, .xlsx, .xlsm, .xlsb
OpenDocument .odt, .ods, .odp
富文字 .rtf
電子書 .epub
資料檔案 .csv
PDF .pdf

注意 .doc(舊版二進位格式)和 .docx(新版 XML 格式)都支援——這在同類工具中非常少見。大多數工具只支援 .docx,遇到舊版 .doc 就束手無策。

效能實測:4.4ms 的秘密

anydoc 的官方基準測試在 100 份真實文件上對比了 7 款工具(包括它自己),覆蓋全部 14 種格式:

工具 支援格式 中位數耗時 品質評分 完整性 結構 格式 清潔度
anydoc 14/14 4.4ms 81 87 79 78 81
libreoffice 12/14 1129.5ms 40 59 42 40 24
unstructured 8/14 572.9ms 63 76 59 51 63
markitdown 6/14 134.8ms 65 78 66 60 52
pandoc 5/14 102.1ms 56 74 57 56 38
docling 4/14 513.6ms 57 60 60 57 51
mammoth 1/14 52.5ms 70 84 71 75 51

幾個關鍵發現:

  1. 速度輾壓:anydoc 的 4.4ms 是第二名 pandoc(102.1ms)的 1/23,是 LibreOffice(1129.5ms)的 1/257
  2. 品質最高:在所有被評測的格式上,anydoc 的得分都是最高的
  3. 覆蓋最全:唯一支援全部 14 種格式的工具
  4. Rust 優勢:純 Rust 實現,無 ML 模型依賴,無外部服務呼叫

品質評分由 Claude Sonnet 5 作為 LLM 裁判,對兩份輸出進行盲評對比(以 LibreOffice 渲染的前 6 頁圖片為基準),每個工具對每對比較進行兩次評判以消除位置偏差,共計 482 次評判。

逐格式對比:anydoc 全面領先

下面的資料展示了各工具在相同格式上的直接對比(滿分 100):

格式 anydoc libreoffice unstructured markitdown pandoc docling mammoth
.doc 87 57 67 - - - -
.docx 88 56 53 71 68 71 70
.pptx 74 24 - 66 - 52 -
.xlsx 72 30 66 55 - 47 -
.rtf 88 53 46 - 45 - -
.odt 80 51 68 - 60 - -
.epub 77 - 72 72 52 - -

mammoth 在 .docx 上得分 70 看似不錯,但它只支援一種格式;anydoc 的 81 分是橫跨全部 14 種格式的平均值,含金量完全不同。

安裝與快速上手

anydoc 提供 Rust 核心 + Node.js / Python / WebAssembly 綁定,以及開箱即用的 CLI。

CLI 方式(零安裝)

# 直接用 npx 執行,首次會自動下載預編譯二進位
npx @firecrawl/anydoc report.docx               # 輸出到 stdout
npx @firecrawl/anydoc slides.pptx -o slides.md  # 輸出到檔案
npx @firecrawl/anydoc - --format csv < data.csv # 從 stdin 讀取

Python 整合

pip install firecrawl-anydoc
import anydoc

# 從檔案路徑轉換
markdown = anydoc.to_markdown("report.docx")
print(markdown)

# 從位元組轉換(自動偵測格式)
with open("report.docx", "rb") as f:
    data = f.read()
markdown = anydoc.to_markdown_bytes(data)

# 指定格式(CSV 等無魔數的格式需要)
markdown = anydoc.to_markdown_bytes(csv_data, "csv")

# 取得文件模型(包含嵌入圖片等資源)
document = anydoc.to_document(data)

Node.js 整合

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

// 從檔案路徑
const md = await toMarkdown('report.docx');

// 從位元組
const mdFromBytes = await toMarkdownBytes(bytes);

// 取得文件模型(含嵌入資源)
const doc = await toDocument(bytes);

Rust 原生使用

cargo add anydoc
// 從檔案路徑
let markdown = anydoc::to_markdown("report.docx")?;

// 從位元組(自動偵測格式)
let markdown = anydoc::to_markdown_bytes(&bytes, None)?;

// 指定格式
let markdown = anydoc::to_markdown_bytes(&bytes, anydoc::Format::Csv)?;

與 LangChain / LlamaIndex 搭配實戰

在 RAG 管道中,anydoc 最典型的用法是作為文件載入的前置步驟,將各種格式統一轉為 Markdown 後再做切片和向量化。

搭配 LangChain

import anydoc
from langchain.text_splitter import MarkdownTextSplitter
from langchain.embeddings import OpenAIEmbeddings
from langchain.vectorstores import FAISS

# 1. 用 anydoc 統一轉 Markdown
def load_documents(file_paths):
    docs = []
    for path in file_paths:
        md = anydoc.to_markdown(path)
        docs.append({"content": md, "source": path})
    return docs

# 2. Markdown 切片
splitter = MarkdownTextSplitter(chunk_size=1000, chunk_overlap=100)
raw_docs = load_documents(["report.docx", "slides.pptx", "data.xlsx"])

chunks = []
for doc in raw_docs:
    chunks.extend(splitter.split_text(doc["content"]))

# 3. 向量化入庫
embeddings = OpenAIEmbeddings()
db = FAISS.from_texts(chunks, embeddings)
db.save_local("./faiss_index")

搭配 LlamaIndex

import anydoc
from llama_index.core import Document, VectorStoreIndex

# anydoc 轉換
file_paths = ["contract.docx", "presentation.pptx", "budget.xlsx"]
documents = []
for path in file_paths:
    md = anydoc.to_markdown(path)
    documents.append(Document(text=md, metadata={"source": path}))

# LlamaIndex 索引
index = VectorStoreIndex.from_documents(documents)
query_engine = index.as_query_engine()
response = query_engine.query("合同中的違約條款是什麼?")
print(response)

RAG 場景下的最佳實踐

1. 優先使用 to_document() 取得嵌入資源

當文件包含圖片時,to_document() 傳回的文件模型會保留圖片的位元組資料和 media type,你可以選擇: - 提取圖片 alt text 作為文字內容 - 將圖片單獨送入多模態模型(如 GPT-4V)做 OCR - 保留圖片 URL 引用(如果圖片有外部連結)

2. 利用格式自動偵測

anydoc 透過檔案內容的魔數(magic bytes)偵測格式,而非依賴副檔名。這意味著即使檔案被錯誤命名(比如 .pdf 實際是 .docx),anydoc 也能正確識別並轉換。

# 偵測格式
fmt = anydoc.format_from_bytes(data)  # 傳回 Format 列舉
print(fmt)  # Format.DOCX

3. 批次處理時注意 GIL

Python 綁定會釋放 GIL,因此多執行緒批次轉換不會互相阻塞:

import concurrent.futures
import anydoc

def convert(path):
    return anydoc.to_markdown(path)

with concurrent.futures.ThreadPoolExecutor(max_workers=8) as executor:
    results = list(executor.map(convert, file_paths))

4. 掃描件 PDF 的處理

anydoc 內建了 pdf-inspector,能自動判斷 PDF 是文字型還是掃描型。對於文字型 PDF,直接本地提取;對於掃描型 PDF,建議搭配 Firecrawl 的 Parse API(帶 OCR 能力)使用。

局限性與注意事項

  1. 掃描件 PDF 能力有限:anydoc 本身不做 OCR,掃描型 PDF 需要外部 OCR 服務。Firecrawl 的 Parse API 可以補充這一點。
  2. 複雜排版可能有偏差:對於多欄排版、複雜圖文混排的文件,轉換結果可能不如專業排版工具精確。anydoc 的目標是 LLM 可讀的結構化文字,而非像素級還原。
  3. 舊版 .doc 格式:雖然支援,但 .doc(OLE2 二進位格式)的解析複雜度高於 .docx,轉換品質可能略低。
  4. 無 ML 增強:anydoc 刻意不依賴 ML 模型,這意味著它不會像 docling 或 marker 那樣利用深度學習做版面分析。優勢是速度快、無 GPU 依賴;劣勢是對複雜版面的理解力有限。
  5. 瀏覽器 WASM 版本:WebAssembly 版本可在瀏覽器中本機執行,檔案不離開使用者裝置,適合隱私敏感場景,但大檔案轉換可能受瀏覽器記憶體限制。

總結評價

anydoc 解決了一個很實際的痛點:在 RAG/LLM 管道中,用一個輕量、快速、格式全覆蓋的工具替代過去需要拼湊四五個庫才能完成的文件轉換工作。

優勢: - 14 種格式全覆蓋,開源工具中唯一 - 4.4ms 中位數轉換速度,比第二名快 23 倍 - 品質評分全面領先,輸出一致性高 - 純 Rust 實現,無外部依賴,無 GPU 需求 - 提供 Python/Node.js/Rust/WASM 四種綁定

適合誰: - 建構 RAG 管道的開發者,需要處理使用者上傳的各種格式文件 - 需要統一文件輸出格式的團隊 - 對轉換速度有要求的即時應用場景 - 不想維護多個轉換工具依賴的開發者

GitHub 位址: firecrawl/anydoc(12000+ Star)

常見問題(FAQ)

Q1: anydoc 和 pandoc 有什麼區別? pandoc 是通用文件格式轉換工具,支援輸入輸出格式極多但每種格式的轉換深度有限。anydoc 專注於將辦公文件轉為 LLM 友好的 Markdown,在覆蓋的 14 種格式上轉換品質更高、速度快 23 倍,且專為 RAG 場景最佳化。

Q2: anydoc 需要 GPU 或外部服務嗎? 不需要。anydoc 是純 Rust 實現,無 ML 模型依賴,無外部 API 呼叫。本機執行,單檔案轉換中位數 4.4ms。

Q3: anydoc 能處理掃描型 PDF 嗎? anydoc 內建 pdf-inspector 可以處理文字型 PDF。對於掃描型 PDF(圖片型),建議搭配 Firecrawl Parse API(帶 OCR 能力)使用。

Q4: anydoc 的輸出格式是什麼樣的? 統一輸出為 GitHub Flavored Markdown(GFM),包含標題層級、表格、列表、程式碼區塊、連結等結構。所有 14 種格式的輸出風格一致。

Q5: anydoc 可以在瀏覽器中執行嗎? 可以。anydoc 提供 WebAssembly 版本(@firecrawl/anydoc-wasm),檔案在瀏覽器本機轉換,不上傳到伺服器,適合隱私敏感場景。