Instalando e usando o MarkItDown Image: AI generated

Basta pip install markitdown para transformar PDFs, documentos do Word, planilhas do Excel e apresentações do PowerPoint em markdown. Mas dê a ele um PDF que não seja nada além de imagens digitalizadas — um contrato escaneado, por exemplo — e o resultado é um documento vazio. Este artigo segue duas linhas: por que e como usar o MarkItDown, e como acoplar OCR exatamente no ponto em que essa confiança se rompe — PDFs que contêm apenas imagens.


Por que usar o MarkItDown — a condição agent-operable

O MarkItDown é a biblioteca de CLI/Python de código aberto da Microsoft que converte arquivos PDF, Word, Excel, PowerPoint, HTML, imagem e áudio em markdown.

Comece pelo conceito de agent-operable (um estado com o qual um agente consegue trabalhar). Para que um agente lide com um documento de forma autônoma, esse documento precisa estar em um estado que uma máquina consiga analisar estruturalmente — não apenas em um estado que um humano ache legível. Essa não é uma condição que se aplica só ao código. Uma regra de negócio enterrada dentro de um PDF ou de uma planilha do Excel pode muito bem não existir para um agente, já que um humano precisa abri-la para saber o que ela diz. Esta é a primeira de três condições — legível, verificável, persistente — apresentadas em Construindo sistemas agent-operable: “legível sem ruído”.

Então por que o MarkItDown especificamente? Você poderia construir a mesma conversão sozinho, combinando parsers de terceiros (PyPDF2, python-docx). A diferença está em quem construiu. docx, xlsx e pptx são todos OOXML — uma especificação que a própria Microsoft possui. Quando a empresa dona da especificação constrói o conversor para o seu próprio formato, casos extremos (células mescladas, tabelas aninhadas, notas de rodapé) tendem estruturalmente a se manter mais confiáveis do que com um parser de terceiros. Se a conversão em si não for confiável, tudo o que se constrói em cima dela rumo a um estado agent-operable perde o sentido. Esse é o motivo para usar o MarkItDown — para os formatos próprios da Microsoft, essa confiança já vem embutida por padrão.

O problema é que essa confiança não se estende a PDFs, especialmente os que contêm apenas imagens. A segunda metade deste artigo se concentra em fechar essa lacuna com OCR.


Instalação

Instalação básica

pip install markitdown

Somente a instalação básica lida apenas com formatos leves, como texto simples e HTML. Para converter documentos PDF e do Office, são necessários os extras específicos de cada formato.

Extras específicos por formato

pip install 'markitdown[pdf]'    # PDF (pdfminer.six + pdfplumber)
pip install 'markitdown[docx]'   # Word
pip install 'markitdown[pptx]'   # PowerPoint
pip install 'markitdown[xlsx]'   # Excel
pip install 'markitdown[all]'    # everything above, plus audio transcription, YouTube captions, Azure Document Intelligence, etc.

Se este artigo só precisar de PDF, markitdown[pdf] já é suficiente.


Uso básico

CLI

markitdown example.pdf                  # print markdown to stdout
markitdown example.pdf -o example.md    # save to a file
cat example.pdf | markitdown            # read from stdin (pipe)

API Python

from markitdown import MarkItDown

md = MarkItDown()
result = md.convert("example.pdf")
print(result.markdown)

Ambas as abordagens selecionam automaticamente o conversor com base na extensão do arquivo ou no tipo MIME. Quando não há informação de extensão — ao ler de stdin, por exemplo — é possível passar uma dica com -x (--extension) ou -m (--mime-type).


O que a conversão de PDF realmente faz — ela só lê a camada de texto

O conversor de PDF do MarkItDown (packages/markitdown/src/markitdown/converters/_pdf_converter.py) primeiro usa o pdfplumber para detectar estrutura de tabela/formulário em cada página, e recorre a page.extract_text() quando uma página não é uma tabela. Se o pdfplumber falhar, ele recorre ao pdfminer.six.

Ambas as bibliotecas são ferramentas que leem uma camada de texto que já existe dentro do PDF. Se uma página consiste apenas em uma imagem digitalizada sem camada de texto, extract_text() retorna uma string vazia, e a saída do MarkItDown acaba efetivamente vazia. Não existe nenhum caminho de código no pacote base que renderize uma imagem ou reconheça caracteres nela.

Em outras palavras, são dois problemas diferentes:

  • Um PDF com camada de texto (exportado do Word, a maioria dos contratos eletrônicos) → a instalação básica funciona bem. A mesma confiança que sustenta os formatos próprios da Microsoft se estende até aqui também.
  • Um PDF sem camada de texto (um documento passado por um scanner, uma foto incorporada como imagem) → a instalação básica retorna um resultado vazio. É aqui que a condição “legível” do agent-operable se rompe.

Configurando o OCR — onde a confiança quebrada é reparada

É aqui que começa a segunda linha do artigo. A confiança que o MarkItDown garante para os formatos próprios da Microsoft precisa ser reconstruída separadamente para PDFs que contêm apenas imagens.

1. Instale o plugin

O suporte a OCR vive em um pacote separado, o markitdown-ocr. Ele está no mesmo repositório (microsoft/markitdown), mas não está incluído em pip install markitdown.

pip install markitdown-ocr
pip install openai   # an OpenAI-compatible client — this plugin has no OCR model of its own

Depois de instalar, verifique se o plugin é reconhecido.

markitdown --list-plugins
#   * ocr             (package: markitdown_ocr)

2. O que é inegociável — um cliente de LLM Vision

O markitdown-ocr não embute um motor de OCR tradicional como Tesseract ou PaddleOCR. Tudo o que ele realmente faz é codificar a imagem em base64 e pedir a uma API chat.completions compatível com OpenAI (por exemplo, gpt-4o) para “extrair o texto desta imagem” (LLMVisionOCRService em _ocr_service.py). Por isso, uma chave de API capaz de chamar um modelo de visão é um requisito obrigatório para que o OCR funcione, ponto final.

from markitdown import MarkItDown
from openai import OpenAI

md = MarkItDown(
    enable_plugins=True,
    llm_client=OpenAI(),      # reads OPENAI_API_KEY from the environment
    llm_model="gpt-4o",
)

result = md.convert("scanned_contract.pdf")
print(result.markdown)

Qualquer cliente compatível com OpenAI — incluindo o Azure OpenAI — pode ser passado da mesma forma.

from openai import AzureOpenAI

md = MarkItDown(
    enable_plugins=True,
    llm_client=AzureOpenAI(
        api_key="...",
        azure_endpoint="https://<resource>.openai.azure.com/",
        api_version="2024-02-01",
    ),
    llm_model="gpt-4o",
)

3. Não funciona pela CLI — verificado no código real

O README do markitdown-ocr apresenta este comando como exemplo de uso:

markitdown document.pdf --use-plugins --llm-client openai --llm-model gpt-4o

Mas ao verificar diretamente as definições de argumentos da CLI do núcleo do MarkItDown (packages/markitdown/src/markitdown/__main__.py), fica claro que essas flags não existem — não há --llm-client nem --llm-model. A CLI suporta -o (arquivo de saída), -x (dica de extensão), -m (dica de MIME), -c (charset), -d/--use-docintel, --use-cu, -p/--use-plugins, --list-plugins e --keep-data-uris, e é só isso — o construtor de MarkItDown só recebe enable_plugins=args.use_plugins. Em outras palavras, o exemplo de CLI do README é um erro de documentação; ele simplesmente não funciona.

O OCR só funciona pela API Pythonllm_client/llm_model precisam ser passados diretamente. Não há como entregar uma chave de API para a CLI.

4. Como funciona de fato — OCR de imagens incorporadas e fallback de OCR de página inteira

O PdfConverterWithOCR (_pdf_converter_with_ocr.py) opera em dois estágios.

  1. Imagens incorporadas primeiro: quando uma página mistura texto com imagens (digamos, uma assinatura digitalizada colocada no meio do corpo do texto), apenas a imagem é recortada, enviada ao LLM Vision, e o resultado é intercalado de volta com o texto ao redor pela sua posição Y original, preservando a ordem de leitura.
  2. Fallback de OCR de página inteira: se a extração de texto voltar completamente vazia (um PDF totalmente digitalizado), a página inteira é renderizada como PNG a 300dpi e enviada ao LLM Vision uma página de cada vez (_ocr_full_pages).

O texto extraído é sempre envolvido neste formato:

*[Image OCR]
<extracted text>
[End OCR]*

5. Ajustando a precisão com um prompt personalizado

O prompt padrão é “extraia todo o texto desta imagem, preservando o layout e a ordem originais”. Para documentos cheios de tabelas, texto vertical, ou carimbos e assinaturas, sobrescrevê-lo com llm_prompt pode melhorar a precisão.

md = MarkItDown(
    enable_plugins=True,
    llm_client=OpenAI(),
    llm_model="gpt-4o",
    llm_prompt="Extract all text from this image, but preserve table structure as markdown tables.",
)

6. Processamento em lote de vários arquivos

Ao rodar uma pasta inteira de PDFs digitalizados de uma vez, cada arquivo dispara sua própria chamada de API — é preciso envolver cada conversão para que uma única falha não derrube o lote inteiro.

from pathlib import Path
from markitdown import MarkItDown
from openai import OpenAI

md = MarkItDown(enable_plugins=True, llm_client=OpenAI(), llm_model="gpt-4o")

for pdf_path in Path("scans/").glob("*.pdf"):
    try:
        result = md.convert(str(pdf_path))
        pdf_path.with_suffix(".md").write_text(result.markdown, encoding="utf-8")
    except Exception as e:
        print(f"failed: {pdf_path.name}{e}")

7. E se você instalar o plugin sem um llm_client?

O plugin carrega, mas o OCR se desliga silenciosamente. register_converters() em _plugin.py só constrói um LLMVisionOCRService quando llm_client e llm_model estão presentes juntos; caso contrário, registra os conversores com ocr_service=None. Nesse estado, ele recorre silenciosamente à extração apenas da camada de texto — sem erro, sem aviso. A maioria dos problemas de “instalei o plugin mas o OCR não funciona” remonta exatamente a esse ponto.

8. Ele até tenta recuperar PDFs corrompidos

Quando o pdfplumber e o pdfminer não conseguem abrir um PDF de jeito nenhum — um EOF truncado, digamos — o markitdown-ocr recorre a renderizar as páginas diretamente com o PyMuPDF (fitz) e tenta de novo. Se esse caminho também falhar, ele deixa *[Error: Could not process scanned PDF]* no resultado.

9. Custo e velocidade

Como envia páginas inteiras para um modelo de visão, o custo e a latência se acumulam proporcionalmente ao número de páginas do documento digitalizado. Uma renderização a 300dpi não é barata em tokens por imagem, então é mais seguro amostrar algumas páginas primeiro para avaliar qualidade e custo antes de rodar um documento inteiro com centenas de páginas.


Verificando o resultado do OCR — em breve, separadamente

Tudo o que foi visto até aqui trata de como “ligar” o OCR. Ligá-lo não é o mesmo que poder confiar no resultado — sob o design atual, em que tudo passa silenciosamente independentemente de llm_client estar configurado ou não, e independentemente de a página estar em branco ou não, não existe um procedimento determinístico para julgar se a saída do OCR é realmente confiável.

Esse procedimento não é abordado aqui. Como aplicar o princípio do Symbolic Feedback Loop — o LLM gera → uma ferramenta determinística julga → retroalimentação → repetir — à saída do OCR é assunto para um artigo/ferramenta separado.


Checklist prático

  • Se você só lida com PDFs comuns que têm camada de texto, a instalação básica markitdown[pdf] é tudo que você precisa. Nenhum plugin de OCR é necessário.
  • Se você precisa lidar com PDFs digitalizados ou fotografados, markitdown-ocr mais uma chave de API da OpenAI (ou compatível) são obrigatórios. Não há integração com um motor de OCR tradicional, local e gratuito, como o Tesseract.
  • Se o resultado do OCR voltar vazio, a primeira coisa a verificar é se llm_client/llm_model foram realmente passados. Ele falha silenciosamente, então nada aparece nos logs.
  • O OCR não pode ser ativado só pela CLI. É necessário um script Python chamando MarkItDown(enable_plugins=True, llm_client=..., llm_model=...).
  • Ainda não há uma forma mecânica de julgar quão confiável é o resultado — por enquanto, um humano precisa verificar uma amostra.

Tabela resumo

SituaçãoO que você precisaNotas
PDF com camada de textopip install markitdown[pdf]pdfplumber/pdfminer extraem o texto
Algumas imagens incorporadas precisam de OCRmarkitdown-ocr + cliente de LLM Visionrecorta só as imagens, intercala com o texto
Totalmente digitalizado (a página inteira é uma imagem)markitdown-ocr + cliente de LLM Visionrenderiza a página inteira a 300dpi, depois aplica OCR
PDF corrompidoigual ao acima (fallback automático)tenta de novo renderizando com PyMuPDF
llm_client não configuradoOCR ignorado silenciosamente, sem erro
Passar --llm-client na CLInão é possível (essa flag não existe)apenas API Python
Julgar a confiabilidade do resultado do OCR(planejado)será coberto por um verificador separado

Artigos relacionados


Fontes

Changelog

  • 2026-07-09: Versão inicial — adicionado o enquadramento agent-operable, configuração de OCR ampliada como segunda metade, adicionada uma seção reservada para o verificador