03 - PDFs e documentos — extração e análise

TL;DR

Em 2026, três caminhos cobrem PDF: nativo (Claude, Gemini, OpenAI Files), página-a-página como imagem, e híbrido (PageIndex pra escolher páginas + multimodal pra ler). PDF nativo trata o documento como sequência de páginas-imagem + texto extraído internamente — o modelo vê layout, gráfico, tabela e texto no mesmo passo. Funciona bem até ~100 páginas (Claude) ou docs muito grandes (Gemini). Acima disso, PageIndex como retrieval (13 - PageIndex — RAG vectorless por árvore de documentos) escolhe páginas relevantes e o LLM multimodal lê só essas. OCR + texto puro segue válido pra docs sem layout relevante, mas perdeu o trono como default.

Três caminhos

Caminho 1 — PDF nativo

O provider trata o PDF como input nativo. Você manda os bytes do PDF, o modelo vê página por página com layout e texto preservados, e você não escreve nada além do prompt.

  • Anthropic Claude — até 100 páginas ou 32 MB por chamada, na família Claude 4.x (Opus 4.6, Sonnet 4.6, Haiku 4.5). Cada página vira ~1500-2000 tokens (combinação de imagem + texto extraído internamente). Suporta base64 e URL.
  • Google Gemini — suporta PDFs muito grandes (até centenas de páginas em Gemini 2.x Pro). Tokenização por página similar a uma imagem (~258 tokens) + texto extraído.
  • OpenAI — via Files API (upload e referencia por file_id) ou Assistants/Responses API. PDF é convertido internamente em imagens + texto.

Caminho 2 — Página-a-página como imagem

Quando o PDF é pequeno (1-3 páginas) ou você só precisa de uma página específica, mande como imagem. Já está coberto na 02 - Imagens como input — screenshots, charts, mockups. Vantagem: zero dependência de feature “PDF nativo”; funciona em qualquer modelo vision. Desvantagem: você renderiza o PDF (PyMuPDF, pdf2image), gerencia bytes, calcula custo manualmente.

Caminho 3 — Híbrido (PageIndex + multimodal)

Pra documento longo (>100 páginas) onde só algumas páginas importam:

  1. Gerar árvore PageIndex do documento (sumário enriquecido com intervalos de página).
  2. LLM navega a árvore por raciocínio, identifica as N páginas relevantes pra query.
  3. Renderiza só essas páginas como imagem, manda em chamada multimodal.

Cobre PDFs financeiros, jurídicos, regulatórios e livros sem comprar vector DB. Ver 13 - PageIndex — RAG vectorless por árvore de documentos pro pipeline completo de retrieval; aqui o foco é o passo final de leitura multimodal.

Code — Claude com PDF nativo

import anthropic
import base64
from pathlib import Path
 
client = anthropic.Anthropic()
 
pdf_bytes = Path("contrato.pdf").read_bytes()
pdf_b64 = base64.standard_b64encode(pdf_bytes).decode("utf-8")
 
response = client.messages.create(
    model="claude-sonnet-4-6",
    max_tokens=2048,
    messages=[{
        "role": "user",
        "content": [
            {
                "type": "document",
                "source": {
                    "type": "base64",
                    "media_type": "application/pdf",
                    "data": pdf_b64,
                },
            },
            {
                "type": "text",
                "text": (
                    "Extraia em JSON: partes contratantes, vigência (datas de "
                    "início e fim), valor total, cláusulas de rescisão. Cite a "
                    "página de cada campo."
                ),
            },
        ],
    }],
)
 
print(response.content[0].text)
print(f"Tokens: {response.usage.input_tokens} in / {response.usage.output_tokens} out")

Para URL pública, troque o bloco source por {"type": "url", "url": "https://..."}. Claude faz fetch e processa.

Code — Gemini com PDF nativo

from google import genai
from google.genai import types
from pathlib import Path
 
client = genai.Client()
 
# Upload via Files API (recomendado para PDFs grandes ou reutilizáveis)
file = client.files.upload(file="contrato.pdf")
 
response = client.models.generate_content(
    model="gemini-2.5-pro",
    contents=[
        file,
        (
            "Extraia em JSON: partes contratantes, vigência, valor total, "
            "cláusulas de rescisão. Cite a página de cada campo."
        ),
    ],
)
 
print(response.text)
print(f"Tokens: {response.usage_metadata.prompt_token_count} in")

Para PDFs pequenos (<20 MB), inline também funciona:

pdf_bytes = Path("contrato.pdf").read_bytes()
 
response = client.models.generate_content(
    model="gemini-2.5-pro",
    contents=[
        types.Part.from_bytes(data=pdf_bytes, mime_type="application/pdf"),
        "Extraia...",
    ],
)

Code — OpenAI via Files API

from openai import OpenAI
 
client = OpenAI()
 
# Upload do PDF
file = client.files.create(
    file=open("contrato.pdf", "rb"),
    purpose="user_data",
)
 
response = client.responses.create(
    model="gpt-5",
    input=[{
        "role": "user",
        "content": [
            {"type": "input_file", "file_id": file.id},
            {
                "type": "input_text",
                "text": (
                    "Extraia em JSON: partes contratantes, vigência, valor "
                    "total, cláusulas de rescisão. Cite a página."
                ),
            },
        ],
    }],
)
 
print(response.output_text)

A API Responses (sucessora do Chat Completions pra usos multimodais complexos) trata input_file de forma nativa.

Quando cada caminho ganha

CenárioRecomendado
Contrato de 20 páginas, extração de camposPDF nativo (Claude ou Gemini)
Relatório financeiro de 200 páginas, perguntas pontuaisPageIndex + multimodal nas páginas relevantes
Página de form preenchida (1 página)Página como imagem
Indexar 50k PDFs para buscaOCR + texto + vector DB (depois multimodal só nas top-K)
PDF com handwritingMultimodal nativo (OCR tradicional perde)
PDF puramente textual (e-book sem layout)OCR + texto puro (mais barato, sem perda)

Estratégia página-a-página para documentos grandes

Quando você ultrapassa o limite de PDF nativo (>100 páginas em Claude, doc gigante em Gemini), divida:

import fitz  # PyMuPDF
 
doc = fitz.open("livro.pdf")
chunks = []
chunk_size = 20  # páginas por chunk
 
for i in range(0, len(doc), chunk_size):
    sub = fitz.open()
    sub.insert_pdf(doc, from_page=i, to_page=min(i + chunk_size - 1, len(doc) - 1))
    pdf_bytes = sub.tobytes()
    chunks.append(pdf_bytes)
 
# Cada chunk vai como uma chamada Claude separada
# Resultados consolidados depois

Cuidado: se a informação atravessa fronteira de chunk (uma tabela divide entre página 20 e 21), perde contexto. Solução: chunks com overlap (último página do chunk anterior repete no próximo).

Quando página-como-imagem bate PDF-nativo

Cenários onde renderizar a página em PNG e mandar como imagem é melhor que PDF nativo:

  • Modelo só tem vision, não suporta PDF. Modelos open-weight, modelos antigos.
  • Só preciso de UMA página específica. Não faz sentido subir o PDF inteiro.
  • Quero controle de resolução fina. PDF nativo decide a renderização internamente; imagem permite escolher DPI exato.
  • Compliance ou debug. Quero ver o que o modelo “viu” — a imagem renderizada é evidência.

Renderização com PyMuPDF:

import fitz
 
doc = fitz.open("contrato.pdf")
page = doc[7]  # página 8
pix = page.get_pixmap(dpi=200)  # 200 DPI é bom para texto
pix.save("page_8.png")

Depois manda page_8.png como imagem (ver 02 - Imagens como input — screenshots, charts, mockups).

Boas práticas

  • Use PDF nativo como default em 2026. OCR + texto puro só pra casos onde layout não importa.
  • Cite a página no prompt. “Cite a página de cada campo extraído” — força o modelo a usar a referência espacial, reduz alucinação.
  • Pra docs gigantes, retrieval primeiro. PageIndex ou hierarchical chunking pra escolher páginas; multimodal pra ler.
  • Não compare PDF nativo vs OCR no benchmark errado. Acerto de extração de tabela formatada: nativo ganha. Acerto de texto corrido: empate. Custo: OCR ganha. Tempo de dev: nativo ganha por largo.

Estrutura de prompt recomendada pra extração de documento:

Você está analisando [tipo do documento: contrato, extrato, NF, laudo].

Extraia os seguintes campos em JSON:
- [campo_1]: [descrição de o que é e como identificar]
- [campo_2]: ...

Regras:
- Se um campo não aparecer no documento, retorne null.
- Para cada campo encontrado, inclua "pagina": N indicando a página de origem.
- Se um valor for ilegível, retorne "ilegivel" (não adivinhe).
- Não invente campos não listados.

Esse template — tipo do doc explícito + campos descritos + null como opção + citação de página + proibição de invenção — cobre as principais fontes de erro de extração em documento.

Cache de arquivo pra múltiplas queries no mesmo PDF:

Quando você vai fazer várias perguntas diferentes sobre o mesmo documento (ex: primeiro extrai campos, depois verifica compliance, depois gera resumo), re-enviar o PDF inteiro em cada chamada é caro. Prefer:

  • Anthropic prompt caching — adiciona cache_control: {"type": "ephemeral"} ao bloco document; o PDF fica em cache por 5 minutos na camada ephemeral.
  • Gemini Files API — faça upload uma vez, reutilize o file.uri em múltiplas chamadas sem re-subir os bytes.

Armadilhas comuns

Mandar o PDF inteiro quando só uma página importa — custo e latência desnecessários

PDF nativo em Claude custa ~1500-2000 tokens por página. Um relatório de 200 páginas onde a pergunta é sobre a página 47 custa ~300-400k tokens de input para ler o doc inteiro — caro e lento. O padrão certo é retrieval-first: PageIndex identifica as páginas relevantes, multimodal lê só essas. Pra documentos abaixo de 30 páginas com perguntas que podem se referir a qualquer parte, PDF nativo inteiro é razoável. Acima disso, retrieval-first.

Usar OCR + texto como default em 2026 — documentos com layout perdem sinal crítico

Muitos pipelines criados em 2022-2023 usam pdfplumber / pdfminer / Textract + LLM texto e funcionam “bem o suficiente”. Mas “bem o suficiente” oculta a perda silenciosa de layout: tabelas ficam linearizadas, cabeçalhos viram texto plano, a tabela de um contrato que tem colunas “parte A / parte B / obrigação” vira uma string sem estrutura. O modelo raciocina em cima de input degradado e pode tirar conclusões erradas. Antes de deixar o pipeline OCR no ar, teste com PDF nativo e compare acurácia de extração em 10 documentos representativos.

Não citar a página no prompt — modelo extrai sem referência e torna o output não-auditável

“Extraia os campos do contrato” sem pedir referência de página entrega um JSON limpo que parece correto mas que você não consegue verificar. Se o model alucinação uma data de vigência, você não sabe nem em que página procurar. Sempre peça: “Cite a página de cada campo extraído.” O modelo passa a incluir "pagina": 12 junto com cada valor, o que transforma qualquer discrepância em auditoria de 30 segundos em vez de revisão completa do documento.

Como explicar em inglês

Interview quote: “For document-heavy workflows in 2026, we default to native PDF — Claude, Gemini, and OpenAI Files all handle it without us writing rendering code. For large documents over 100 pages, we use PageIndex to identify relevant pages first, then pass only those pages to the multimodal model. We always ask the model to cite the page number for each extracted field — that makes hallucinations auditable in seconds.”

PortuguêsInglês
PDF nativo (suporte nativo do provider)Native PDF support
Extração de campos de documentoDocument field extraction
Renderização de página como imagemRendering page as image
Chunk de páginas com overlapPage chunk with overlap
Retrieval por árvore de índice (PageIndex)Structural retrieval / PageIndex
Citar a página de cada campoCite the page number per field
OCR + texto puroOCR-plus-text pipeline
Upload via Files APIFiles API upload
Custo por token de PDFToken cost per PDF page

O que vem a seguir

Com PDFs e documentos cobertos, a nota 04 entra em áudio e vídeo — as modalidades com maior custo de contexto e onde a escolha de provider (Whisper vs GPT-4o Realtime vs Gemini nativo) faz mais diferença do que em qualquer outra. Geração de áudio e vídeo também aparecem brevemente, mas o foco é no uso como input.

Fontes

  • @hooeemBecome an AI Engineer, cap #17.
  • AnthropicPDF support (docs). Limites e tokenização.
  • GoogleGemini API — Document understanding (docs).
  • OpenAIFiles API (docs). Upload e referência por file_id.
  • PyMuPDF (fitz) — biblioteca padrão pra render/cortar PDF em Python.

Veja também