Você arrasta uma foto de nota fiscal para dentro do ChatGPT e ele responde sobre o conteúdo. Parece que o modelo "olhou" a imagem. Não olhou — pelo menos não da forma que a maioria imagina.

Esta parte monta a peça de olhos: como o pixel vira texto, quais opções abertas usar e, principalmente, os dois erros que fazem essa etapa custar dez vezes mais do que deveria.

📚 Série "Monte o seu próprio ChatGPT" — 9 partes

1. O que tem dentro · 2. O cérebro · 3. Os olhos (OCR) ← você está aqui · 4. A memória (documentos) · 5. Imagem · 6. Vídeo · 7. Voz · 8. Ferramentas e agentes · 9. Juntando tudo e a conta

A decisão que economiza 90% e ninguém faz

Antes de qualquer modelo, uma pergunta: esse PDF já tem texto dentro?

PDF é um formato traiçoeiro porque a mesma extensão guarda duas coisas completamente diferentes. Um PDF gerado por um sistema (nota fiscal eletrônica, extrato, contrato exportado do editor) já contém o texto como texto — dá para copiar e colar. Um PDF escaneado é uma sequência de fotografias de papel: para o computador, não há uma letra ali.

Na média dos acervos corporativos, a maioria dos arquivos é do primeiro tipo. E extrair texto de um PDF que já tem texto custa aproximadamente nada: é uma biblioteca, roda em CPU, processa milhares de páginas por minuto e não erra um caractere.

💡 A regra de ouro do pipeline de documentos

Nunca rode OCR sem antes testar se o texto já está lá. São cinco linhas de código que evitam pagar GPU, esperar minutos e ainda introduzir erros de leitura em documentos que estavam perfeitos.

O teste é simples: extraia o texto da página; se vier menos de ~100 caracteres, é imagem, e aí sim chame o OCR.

As quatro formas de ler um documento

FerramentaPara que serveOnde rodaQuando usar
Extração direta (pdfplumber, pypdf)Pega o texto que já está no arquivoCPUSempre — é a primeira tentativa
SuryaOCR neural, multilíngue, com detecção de linha e de tabelaGPU pequenaEscaneado, foto de celular, documento torto
MarkerConverte PDF inteiro em Markdown preservando estruturaGPU pequenaQuando o layout importa: títulos, tabelas, seções
GROBIDExtrai metadados e referências de artigo científicoCPU/GPU leveAcervo acadêmico, papers, teses

Os três últimos rodam em 1 clique na plataforma, numa placa a partir de R$ 1,07 por hora. Você liga a máquina, processa o lote, desliga.

O que quebra em documento brasileiro

Os modelos são treinados em acervos majoritariamente em inglês, e os nossos documentos têm particularidades que derrubam a taxa de acerto:

O erro caro: pedir o JSON direto ao modelo grande

É tentador: joga a imagem no modelo mais capaz e pede "extraia número da nota, CNPJ, valor e itens em JSON". Funciona na demonstração. Em produção, tem três problemas sérios.

  1. Custa caro. Uma imagem consome muitos tokens de entrada. Multiplique por 10 mil documentos e a conta sai numa ordem de grandeza acima da alternativa.
  2. Alucina número. Este é o problema grave. Quando o dígito está borrado, o modelo generativo não diz "não consegui ler" — ele completa com o que é plausível. Um valor de nota que vira R$ 1.850,00 em vez de R$ 1.650,00 passa despercebido por meses.
  3. Não dá para auditar. Você não tem a posição na página de onde saiu cada campo, então não consegue mostrar a evidência quando alguém questionar.
⚠️ Separe LER de ENTENDER

OCR lê. O modelo de linguagem estrutura. O OCR devolve o texto com a posição e um índice de confiança por trecho — é determinístico e auditável. O modelo de linguagem recebe esse texto (não a imagem) e organiza em campos.

Com essa separação, quando o dígito está ilegível, o OCR marca confiança baixa e o seu sistema manda para revisão em vez de inventar. É a diferença entre um processo confiável e uma bomba-relógio contábil.

O pipeline em quatro etapas

import pdfplumber, json
from openai import OpenAI

cliente = OpenAI(base_url="https://gpubrasil.com.br/v1",
                 api_key="gpub_live_suachaveaqui")

def texto_da_pagina(pagina):
    """1) Tenta extrair o texto que já existe. 2) Só se não houver, chama o OCR."""
    direto = (pagina.extract_text() or "").strip()
    if len(direto) > 100:
        return direto, "direto"
    imagem = pagina.to_image(resolution=300).original
    return ocr(imagem), "ocr"          # Surya rodando na sua instância

ESQUEMA = """Extraia do texto abaixo, em JSON e nada mais:
{"numero": str, "cnpj_emitente": str, "data": "AAAA-MM-DD",
 "valor_total": number, "itens": [{"descricao": str, "valor": number}]}
Se um campo não estiver claramente legível no texto, use null. NUNCA invente um valor."""

def estruturar(texto):
    """3) O modelo organiza o texto em campos — barato, porque a entrada é texto."""
    r = cliente.chat.completions.create(
        model="gpub-fast",
        messages=[{"role": "system", "content": ESQUEMA},
                  {"role": "user", "content": texto[:8000]}],
        max_tokens=800,
        temperature=0,
        response_format={"type": "json_object"},
    )
    return json.loads(r.choices[0].message.content)

def validar(dado):
    """4) A rede de segurança que o modelo não te dá."""
    problemas = []
    if not dado.get("valor_total"):        problemas.append("valor ausente")
    if dado.get("itens"):
        soma = sum(i["valor"] for i in dado["itens"] if i.get("valor"))
        if dado.get("valor_total") and abs(soma - dado["valor_total"]) > 0.02:
            problemas.append(f"itens somam {soma}, total diz {dado['valor_total']}")
    return problemas

with pdfplumber.open("nota.pdf") as pdf:
    for pagina in pdf.pages:
        texto, origem = texto_da_pagina(pagina)
        dado = estruturar(texto)
        alertas = validar(dado)
        destino = "revisao" if alertas else "aprovado"
        print(destino, origem, alertas or "", dado.get("numero"))
🔍 Repare na etapa 4

A validação por regra de negócio — a soma dos itens bate com o total? o CNPJ tem 14 dígitos? a data é plausível? — é o que transforma extração por IA em processo confiável.

Nenhum modelo, por melhor que seja, substitui essa checagem. E ela custa zero: é aritmética.

A conta de 10 mil páginas

Um acervo de 10 mil páginas escaneadas, do zero ao banco de dados:

EtapaOnde rodaTempoCusto
Triagem (tem texto? é imagem?)CPUminutos≈ R$ 0
OCR das páginas escaneadasGPU de R$ 1,07/h~3,4 hR$ 3,60
Estruturação com gpub-fastAPI por tokenem paraleloR$ 5,10
Validação por regraCPUsegundosR$ 0
Total≈ R$ 8,70

Preços de agosto de 2026, para uma GPU avulsa, sem interrupção programada. O catálogo muda: confira a vitrine antes de fechar a conta.

Menos de nove reais por 10 mil páginas, ou R$ 0,00087 por página. As APIs de documento das grandes nuvens cobram por mil páginas numa faixa que fica ordens de grandeza acima disso — e é por essa diferença que vale montar a peça em vez de assinar.

Repare também na divisão de trabalho, que é o padrão desta série inteira: a GPU trabalha 3 horas e é desligada; o cérebro é pago por token, sem máquina para administrar.

Três detalhes que decidem a qualidade

A leitura é barata; o entendimento é por token

O OCR roda algumas horas numa placa pequena e desliga. A estruturação de cada documento é uma chamada de API cobrada por token, em reais — e é ela que roda todo dia, para sempre.

Ver a API por token →

Próxima parte: a memória — por que "mandar o PDF inteiro" não escala.

Continue: parte 4: a memória · Surya OCR em 1 clique · extrair dados de notas e contratos