Todos os artigos
extração de dadosLLMLGPDJSON

Extração de dados fiscais e contratos com LLM em JSON

Veja como extrair dados de notas fiscais e contratos com LLMs, validar o JSON de saída, tratar falhas e minimizar dados enviados para conformidade com a LGPD.

3 min de leitura

Processar documentos fiscais e contratos manualmente ainda consome horas de trabalho em muitas empresas brasileiras. LLMs podem automatizar a extração de campos como CNPJ, valor total, datas e partes envolvidas, mas a adoção em produção exige cuidados: saída JSON validada, tratamento robusto de erros e conformidade com a LGPD na minimização de dados enviados.

Neste artigo, mostramos um cenário ilustrativo de integração com uma API compatível com OpenAI, usando Python. O foco é prático: código, validação e boas práticas de privacidade.

Cenário ilustrativo

Uma empresa recebe diariamente dezenas de PDFs de notas fiscais e contratos. O time de backoffice precisa lançar manualmente no ERP campos como CNPJ do emitente, valor total, data de emissão e número da nota. A ideia é enviar o texto extraído do PDF (via OCR ou parser) para um LLM e receber de volta um JSON estruturado, pronto para inserção no banco.

Com uma API compatível com a OpenAI, você usa a biblioteca oficial openai apenas trocando a base_url e a chave de API. Endereço e chave são fornecidos pela sua equipe de acesso; guarde-os em variáveis de ambiente (BASE_URL, API_KEY).

Extração com saída JSON

O primeiro passo é instruir o modelo a retornar apenas JSON válido. Para aumentar a confiabilidade, usamos response_format={"type": "json_object"} e definimos um schema claro no prompt.

python
from openai import OpenAI
import os

client = OpenAI(
    api_key=os.getenv("API_KEY"),
    base_url=os.getenv("BASE_URL")
)

def extrair_dados_nota_fiscal(texto_documento):
    prompt = f"""
    Extraia os seguintes campos da nota fiscal abaixo e retorne APENAS JSON válido, sem comentários:
    - cnpj_emitente: string
    - cnpj_destinatario: string
    - valor_total: number
    - data_emissao: string (formato ISO 8601)
    - numero_nota: string
    Documento:
    {texto_documento}
    """
    response = client.chat.completions.create(
        model="gpt-4o-mini",  # ou outro modelo disponível no seu contrato
        messages=[{"role": "user", "content": prompt}],
        temperature=0,
        response_format={"type": "json_object"}
    )
    return response.choices[0].message.content

A saída esperada é algo como:

json
{
  "cnpj_emitente": "12.345.678/0001-90",
  "cnpj_destinatario": "98.765.432/0001-10",
  "valor_total": 1500.75,
  "data_emissao": "2026-09-05",
  "numero_nota": "123456"
}

Validação de saída com schema

Não basta confiar que o LLM retornará exatamente o que pedimos. Valide o JSON com uma biblioteca como Pydantic para garantir tipos, formatos e presença de campos obrigatórios.

python
from pydantic import BaseModel, Field
from typing import Optional
import json

class NotaFiscal(BaseModel):
    cnpj_emitente: str
    cnpj_destinatario: str
    valor_total: float = Field(gt=0)
    data_emissao: str  # validar depois com datetime
    numero_nota: str

def validar_json_extraido(json_str: str) -> Optional[NotaFiscal]:
    try:
        dados = json.loads(json_str)
        return NotaFiscal(**dados)
    except (json.JSONDecodeError, ValidationError) as e:
        print(f"Erro de validação: {e}")
        return None

Se o JSON vier malformado ou faltar campos, a função retorna None e você pode acionar um fluxo de contingência.

Tratamento de erros e resiliência

Erros de rede, timeouts ou respostas inesperadas são comuns em integrações com APIs. Implemente retry com backoff exponencial e um fallback para revisão manual.

python
import time
from tenacity import retry, stop_after_attempt, wait_exponential

@retry(stop=stop_after_attempt(3), wait=wait_exponential(multiplier=1, min=2, max=10))
def chamar_llm_com_retry(texto_documento):
    try:
        return extrair_dados_nota_fiscal(texto_documento)
    except Exception as e:
        print(f"Tentativa falhou: {e}")
        raise

def processar_documento(texto_documento):
    try:
        json_str = chamar_llm_com_retry(texto_documento)
        nota = validar_json_extraido(json_str)
        if nota is None:
            return {"status": "revisao_manual", "documento": texto_documento[:200]}
        return {"status": "ok", "dados": nota.dict()}
    except Exception as e:
        # Log estruturado para monitoramento
        print(f"Erro final no processamento: {e}")
        return {"status": "erro", "detalhe": str(e)}

No exemplo acima, usamos a biblioteca tenacity para simplificar o retry. Ajuste o número de tentativas e o tempo de espera conforme a criticidade do processo.

LGPD e minimização de dados enviados

A LGPD exige que o tratamento de dados pessoais seja adequado, necessário e limitado à finalidade. Ao enviar documentos para um LLM, você deve minimizar os dados que trafegam para a API.

Boas práticas:

  1. Envie apenas o necessário: se o campo de interesse é apenas o valor total e o CNPJ, não envie o documento inteiro com dados de terceiros. Extraia previamente trechos relevantes.
  2. Anonimize ou pseudonimize: substitua CPFs, e-mails e outros identificadores diretos por máscaras antes de enviar. Por exemplo, transforme 123.456.789-00 em ***.***.***-00.
  3. Não armazene os textos brutos em logs: registre apenas metadados (ID do documento, status, timestamp), nunca o conteúdo completo.
  4. Use contratos de processamento: exija do fornecedor um contrato de tratamento de dados alinhado à LGPD, mas lembre que é sua responsabilidade como controlador garantir que a base legal para o envio esteja correta.

Exemplo de minimização antes do envio:

python
import re

def mascarar_dados_sensiveis(texto):
    # Remove CPF (formato 000.000.000-00)
    texto = re.sub(r'\d{3}\.\d{3}\.\d{3}-\d{2}', '***.***.***-**', texto)
    # Remove e-mails
    texto = re.sub(r'[\w\.-]+@[\w\.-]+\.\w+', '[EMAIL_REMOVIDO]', texto)
    return texto

texto_original = extrair_texto_pdf("nota_fiscal.pdf")
texto_minimizado = mascarar_dados_sensiveis(texto_original)
resultado = processar_documento(texto_minimizado)

Dessa forma, o LLM nunca recebe dados pessoais desnecessários para a extração dos campos de interesse.

Conclusão

Extração estruturada de documentos fiscais e contratos com LLM é viável e pode reduzir trabalho manual, desde que você implemente validação de schema, tratamento de erros e minimização de dados.

Explore outros artigos no blog.

Quer entender como a sua empresa pode usar modelos de IA com atendimento e faturamento no Brasil? Fale com a gente — a equipe da TokenRecarga responde em português.

Quer usar isso na sua empresa?

Conte o que sua equipe precisa e preparamos uma proposta.

Continue lendo