Você realmente precisa de um LLM para processar essa planilha ou JSON?
Nos últimos anos, com a popularização dos Grandes Modelos de Linguagem (LLMs), vimos uma corrida para resolver qualquer problema de software jogando prompts na tela. Se o documento é um PDF não estruturado, um contrato escaneado ou uma transcrição de áudio, o LLM é fantástico e insubstituível.
Porém, quando o assunto é dado estruturado ou semi-estruturado (como planilhas Excel, CSVs ou grandes JSONs), jogar a massa de dados inteira para a janela de contexto do modelo é um dos erros arquiteturais mais caros e ineficientes que podemos cometer.
A pergunta que devemos nos fazer é: Será que não existe uma maneira mais barata, rápida e precisa de resolver isso usando ferramentas determinísticas para auxiliar o modelo?
Neste artigo, compartilho a filosofia de usar o LLM apenas como um compilador de intenção, delegando o processamento pesado para motores determinísticos.
💡 O Paradigma: LLM como Roteador de Intenção, não como Engine de Execução
Modelos de linguagem são probabilísticos. Eles são ótimos para inferir o que precisa ser feito, mas não para realizar operações matemáticas, paginações ou transformações repetitivas em grandes volumes de dados.
A literatura acadêmica e a prática de engenharia já comprovaram limitações severas da dependência cega da janela de contexto:
- Lost in the Middle: Pesquisadores de Stanford, UC Berkeley e Samaya AI (Liu et al., 2023) demonstraram no estudo “Lost in the Middle: How Language Models Use Long Contexts” que o desempenho dos LLMs cai drasticamente quando a informação necessária está no meio de um contexto longo, mesmo em modelos treinados para 100k+ tokens.
- Custo e Latência Quadrática/Linear: Manter contextos massivos infla o custo por requisição e aumenta a latência de resposta a níveis inaceitáveis para produção.
- Alucinação Numérica e Estrutural: LLMs prevêem o próximo token mais provável; eles não possuem uma ULA (Unidade Lógica e Aritmética) nativa para garantir agregamentos exatos ou estruturas de chave-valor estritas.
O Fluxo Híbrido LLM + Motor Determinístico
┌─────────────────┐ Sintaxe / Query ┌────────────────────────┐
│ Prompt LLM │ ──────────────────────────> │ Motor Determinístico │
│ (Intenção/DDL) │ │ (DuckDB / JSONata) │
└─────────────────┘ └────────────────────────┘
│
Dados │ Filtrados
▼
┌────────────────────────┐
│ Mapeador Programático │
│ (JSON de Saída 100%) │
└────────────────────────┘
- LLM (Probabilístico): Lê os metadados (esquema, nomes de colunas, chaves do JSON) + a intenção do usuário e gera uma instrução determinística (uma query SQL ou uma expressão JSONata).
- Motor Determinístico: Executa essa instrução diretamente na memória do servidor com velocidade nativa (C++/Rust) e custo zero de tokens.
- Mapeador Programático: Transforma o resultado no formato final esperado pela aplicação via código determinístico.
📊 Cenário 1: Processando Planilhas Grandes sem Estourar Contexto (O Padrão DuckDB)
O Problema Tradicional
Você tem uma planilha Excel com milhares de linhas ou múltiplas abas e precisa extrair apenas certas informações para alimentar uma API. A abordagem ingênua é converter a planilha para texto/HTML/Markdown e injetar tudo no prompt.
A Abordagem Determinística (DuckDB + SQL)
Em vez de enviar os dados da planilha para o modelo:
- Envie apenas o Esquema (
DDL): Converta a planilha para tabelas temporárias no DuckDB (um banco OLAP analítico de altíssima performance em memória) e extraia apenas a DDL dos esquemas (CREATE TABLE table_0 ("data" DATE, "valor" DOUBLE, ...)). - Peça ao LLM para Gerar SQL: O prompt recebe apenas o esquema e a lista de entidades desejadas. O LLM retorna uma consulta SQL compatível com DuckDB, utilizando aliases (
AS) que correspondem exatamente aos identificadores dos campos solicitados. - Execução Local Nativa: O DuckDB executa a query em microssegundos direto na memória local.
- Mapeamento via Código: Um script simples lê a tabela resultante já filtrada e monta a estrutura JSON de saída com 100% de precisão estrutural.
Ganha-se: Custo reduzido drasticamente (tokens caem de centenas de milhares para poucas centenas do esquema), zero risco de alucinação de dados e processamento instantâneo.
🔄 Cenário 2: Transformando e Filtrando JSONs Complexos (O Padrão JSONata)
O Problema Tradicional
Você recebe um payload JSON gigante de uma API externa com dezenas de níveis aninhados, arrays e atributos irrelevantes, e precisa reformatar esse JSON para um esquema simplificado. Passar o JSON inteiro para o LLM reescrever é ineficiente e propício a erros de digitação de chaves.
A Abordagem Determinística (JSONata)
Em vez de pedir para o LLM reescrever a string do JSON:
- Apresente o Esquema ao LLM: Passe uma amostra das chaves do JSON e a estrutura desejada.
- Gere uma Expressão JSONata: O LLM atua apenas gerando uma consulta declarativa em JSONata (linguagem leve de consulta e transformação de dados JSON).
- Execução pelo Evaluator JSONata: Um evaluator JSONata na sua aplicação aplica a expressão diretamente sobre o JSON original em memória.
Ganha-se: Nenhuma mutação acidental de dados, garantia de tipo, performance de microssegundos e facilidade de auditar a regra de transformação gerada.
🛠️ O Paralelo com Agentes Modernos (ex: Claude Code)
Esta filosofia de não confiar a memória inteira ao LLM e sim delegar para ferramentas determinísticas não é nova. É a base de engenharia por trás do Claude Code e de agentes autônomos de código:
- Gerenciamento de Memória por Apontadores: O Claude Code não mantém o histórico completo do seu repositório na janela de contexto (o que destruiria o orçamento de tokens e a atenção do modelo).
- Offloading para Sistema de Arquivos: Ele lê e grava arquivos locais (
CLAUDE.md, arquivos temporários de planejamento) como estado de verdade e dispara ferramentas determinísticas nativas (grep,glob,ast-grep) para recortar cirurgicamente apenas as linhas necessárias antes de raciocinar sobre elas.
🛡️ Pormenores e Resiliência na Prática
Quando aplicamos essa estratégia no dia a dia em produção, é crucial incluir pequenas camadas de proteção (Guardrails):
- Normalização Prévia de Esquemas: Nomes de colunas em planilhas humanas frequentemente vêm sujos, com espaços ou caracteres especiais. Fazer uma higienização simples (
col.strip().lower().replace(" ", "_")) reduz falhas de sintaxe SQL drasticamente. - Prompts Rígidos de Identificadores: Instrua o LLM a sempre utilizar aspas duplas nos identificadores de colunas e tabelas (
"coluna" AS "entidade") para evitar erros de parser SQL no DuckDB. - Fallbacks Gracioso em Camadas: Se o LLM gerar uma consulta SQL/JSONata com erro de sintaxe, sua aplicação deve tratar o erro de forma resiliente:
- Fallback por Tabela: Se a query de uma aba falhar, capture o erro e recupere a tabela original completa via
SELECT * FROM table_name. - Fallback por Pipeline: Se o motor falhar por completo, a aplicação pode recorrer à estratégia tradicional de loteamento/paginação.
- Fallback por Tabela: Se a query de uma aba falhar, capture o erro e recupere a tabela original completa via
📌 Conclusão
Sempre que você estiver prestes a enviar uma massa de dados estruturados para um LLM, pergunte-se: Eu preciso que a IA processe estes dados, ou preciso apenas que ela entenda o que fazer e escreva a instrução de processamento?
Na maioria das vezes, combinar a capacidade de interpretação do LLM com motores determinísticos em memória como DuckDB e JSONata vai te entregar uma solução mais barata, infinitamente mais rápida e com zero margem para alucinações.