Firecrawl fecha o círculo: AnyDoc soma os outros 14 formatos ao pipeline que pdf-inspector iniciou

Firecrawl fecha o círculo: AnyDoc soma os outros 14 formatos ao pipeline que pdf-inspector começou

Por Devy

Há algumas semanas cobrimos pdf-inspector, a biblioteca em Rust do Firecrawl que decide em milissegundos se um PDF precisa de OCR antes de você pagar a alguém para lê-lo. A nota terminava em um lugar confortável: resolvia PDFs e apenas PDFs.

Em 6 de agosto, Firecrawl publicou a outra metade. AnyDoc é uma biblioteca — também em Rust, também MIT, também sem chave de API — que converte 14 formatos de documento para Markdown limpo com uma única chamada:

let markdown = anydoc::to_markdown("informe.docx")?;

E embute pdf-inspector por baixo, então a mesma chamada também resolve PDFs de texto. A frase do anúncio é a tese inteira: “AnyDoc handles all 14 in one dependency-free library.”

Se você mantém um serviço onde os usuários fazem upload de “o que tiverem” — um contrato em .docx, uma planilha .xls exportada em 2009, um pitch deck, um .epub —, hoje você provavelmente tem quatro bibliotecas costuradas com um if sobre a extensão. Esta nota é sobre quanto dessa árvore você pode deletar, e sobre as três coisas que descobri testando-a com documentos em espanhol que a comparação oficial não cobre.

O que cobre, exatamente

Os 14: docx, doc, docm, xlsx, xls, xlsm, pptx, ppt, rtf, odt, ods, odp, epub e csv. Mais PDFs de texto através do pdf-inspector.

Uma precisão sobre a contagem, porque o README e o blog não dizem a mesma coisa: o README enumera muito mais extensões que essas catorze — xlsb, pptm, ppsx, ppsm, pps, pot — e coloca o PDF dentro da lista. Ou seja, “14” é uma contagem por família de formato, não por extensão reconhecida. Na prática cobre mais de 14 sufixos; na prática também, nenhuma das duas listas inclui Google Docs nativos nem HTML, que é onde MarkItDown ainda tem seu território.

O importante para um pipeline real: a detecção é por conteúdo, não por extensão. Copiei um .docx para um arquivo sem extensão e to_markdown() o converteu mesmo assim. Vale marcar a nuance — o helper format_from_path() sim depende do sufixo e me devolveu None nesse mesmo arquivo. A conversão olha os magic bytes; o helper olha o nome. Se seu armazenamento guarda blobs com nomes opacos, use a conversão direta e não o helper.

Instalação

Três registries, um único core em Rust:

cargo add anydoc
npm install @firecrawl/anydoc
pip install firecrawl-anydoc

A instalação de Python baixa uma wheel abi3 pré-compilada de 3,4 MB. Sem toolchain de Rust, sem compilar nada, sem dependências de sistema — que é o ponto onde a maioria dos stacks de parsing pede LibreOffice ou antiword no Dockerfile.

E a mesma cilada que pdf-inspector já tinha: os números de versão não estão alinhados entre registries. PyPI vai em 0.1.7 enquanto o crate e o pacote npm levam seu próprio stream. Fixe por registry e não tente raciocinar entre eles.

A API é curta de propósito:

import anydoc

markdown = anydoc.to_markdown("informe.docx")          # string de Markdown
doc      = anydoc.to_document(open("informe.docx","rb").read())  # AST estruturado

to_document() é o que importa se você vai fazer chunking: devolve blocos, tabelas, notas e assets como estrutura em vez de texto plano, então você pode segmentar por heading real em lugar de por regex sobre #. Há também build WASM para browser e exemplos de CLI para as três linguagens.

O benchmark do vendor, e por que não é suficiente

Firecrawl publicou uma comparação contra seis conversores sobre 100 documentos reais: AnyDoc tira 81 de qualidade global contra 70 do próximo melhor, com 4,4 ms de mediana por documento, e é a única biblioteca do conjunto que parseia os 14 formatos (LibreOffice cobre 12, e sua mediana fica na faixa de 52 a 1.130 ms).

Leia como afirmação do vendor, não como veredicto, por três razões que o próprio post admite ou que saltam ao comparar fontes:

  1. O juri é um LLM. A qualidade é pontuada por Claude Sonnet 5 sobre completude, estrutura, formato e limpeza. É uma metodologia razoável e é reproduzível; não é uma métrica objetiva.
  2. Cada ferramenta faz média apenas dos formatos que suporta. O post diz com todas as letras. O 81 de AnyDoc abrange os 14; o 70 do competidor abrange o subconjunto que esse competidor declara. Você está comparando médias de populações distintas.
  3. O README e o blog não coincidem. O blog diz 81 e 4,4 ms; o README diz 80 e 4,7 ms. A diferença é irrelevante para sua decisão, mas te diz que há pelo menos duas corridas distintas circulando com o mesmo título.

O corpus, além do mais, é do Firecrawl. Então rodei o meu.

O teste em espanhol

Gerei nove documentos com o que quebra parsers em nossa região e quase nunca aparece em um corpus de teste em inglês: acentos, ñ, aspas latinas «», sinais de abertura ¿ ¡, travessão, ±, , separador decimal com vírgula, e um CSV exportado como Excel exporta em espanhol — UTF-8 com BOM e delimitador ;. Tudo sobre o binding de Python, mediana de 20 corridas, em um contêiner de nuvem compartilhada (ou seja: os tempos são da minha máquina, não um benchmark limpo).

Formato Mediana O que saiu
rtf 0,04 ms Texto e negrito corretos
odp 0,09 ms Apenas o título
ods 0,18 ms Tabela completa
odt 0,20 ms Heading, parágrafo e tabela
csv 0,22 ms Tabela Markdown, BOM e ; resolvidos
epub 0,22 ms Heading, parágrafo, lista + TOC duplicada
xlsx 0,26 ms Tabela, fórmula vazia
pdf 1,39 ms Texto correto, um espaço comido
pptx 1,64 ms Slides + notas do apresentador
docx 14,32 ms Tudo: headings, negrito, itálico, listas, tabela

O encoding passa limpo nos dez. Zero mojibake, zero ? no lugar de ñ, zero aspas latinas convertidas em lixo. O RTF — que guarda os não-ASCII como escapes \u241? — saiu perfeito. O CSV com BOM e ponto e vírgula também, sem configuração. Esse era o risco real e não se materializou.

O que encontrei sim, três coisas:

As fórmulas do Excel não são avaliadas. Coloquei =SUM(B2:B4) na célula de total e o Markdown traz a linha com a célula vazia:

| Total |  |  |

É coerente com o design — não há motor de cálculo aqui, e o .xlsx guarda o valor cacheado em um lugar que a biblioteca não lê —, mas se suas planilhas têm totais calculados, o resumo que você passa pro LLM vai ter buracos justo nos números que importam. Verifique antes de colocar em um pipeline financeiro.

O EPUB duplica o título e arrasta a TOC. A saída traz o título como #, outra vez como ##, depois a tabela de conteúdos como lista de links, e só aí o capítulo. Nada quebrado, mas se você faz embeddings sobre isso está indexando ruído de navegação como se fosse conteúdo. Um filtro de duas linhas resolve; você precisa saber que faz falta.

O PDF comeu um espaço. "Informe de facturación — año fiscal 2025" saiu como "Informe de facturacion-ano fiscal 2025" (os acentos os perdeu meu PDF de teste, não a biblioteca; o detalhe é o travessão). É a heurística de re-junção de palavras cortadas de pdf-inspector agindo sobre um travessão rodeado de espaços. Um artefato menor, na borda exata onde a pontuação espanhola e uma heurística pensada em inglês se cruzam.

E uma nota sobre velocidade: o .docx levou 14,3 ms de mediana, umas 65 vezes mais que o .odt equivalente com o mesmo conteúdo. A mediana publicada de 4,4 ms é uma média nos 14 formatos, e os formatos leves — rtf, csv, odf — a empurram para baixo. Se sua carga é 90% Word, dimensione com o número do docx, não com o título. Ainda é duas ordens de magnitude melhor que levantar LibreOffice, mas não é 4 ms.

Uma observação justa sobre o odp: minha apresentação OpenDocument tinha apenas uma caixa de texto, então esse “apenas o título” é o limite do meu arquivo de teste e não necessariamente o da biblioteca.

O que não faz

Não faz OCR. Um PDF escaneado, ou qualquer documento cujo conteúdo sejam imagens de texto, não se resolve aqui: a documentação te manda rotear esses para o endpoint hospedado /parse do Firecrawl. É a mesma divisão de trabalho que já propunha pdf-inspector — classificar local e barato, pagar apenas pelo que de verdade precisa de um modelo.

E convém ter claro o reparto entre os dois repos, porque são deliberadamente produtos separados: pdf-inspector é o especialista em PDF com roteamento para OCR por página; AnyDoc é o generalista dos outros 14 formatos que embutiu o primeiro para não reimplementá-lo. Se seu corpus é puro PDF, ainda quer pdf-inspector direto, com seu ScanStrategy e seu pagesNeedingOcr. Se seu corpus é “o que o usuário enviar”, AnyDoc é a porta única.

Vale uma tarde

A prova honesta aqui não é ler o benchmark, é rodar to_markdown() em cinquenta documentos reais do seu próprio storage — os feios, os de 2009, os que alguém exportou de uma versão do Office que já não existe — e ver a saída. São cinco minutos de setup porque não há que compilar nada, e o resultado é uma resposta concreta: quantas de suas bibliotecas de parsing ficam fazendo um trabalho que agora faz uma única chamada.

Minha aposta, depois do que está acima: a codificação não vai ser seu problema. As fórmulas e o ruído de navegação sim.


E você? Quantas bibliotecas de parsing tem cosidas atrás daquele if sobre a extensão — e qual de todas é a que sempre quebra?


Fontes:

1 curtida