O problema que ninguém enxerga até medir
O governo brasileiro compra mais de R$ 1 trilhão por ano e publica cada licitação como PDF — muitas vezes com dezenas de anexos, cada um guardando exigências que decidem se a empresa é habilitada ou não. Empresa de porte médio não dá conta desse volume. É esse o produto: ler tudo, dar nota de aderência e dizer onde existe chance real.
A versão um usava a API de um LLM comercial para leitura e match. Funcionava, no sentido de que devolvia resposta. A falha era invisível até a gente testar direito — e vinha em duas formas.
LLM comercial
0–3
Documentos de habilitação extraídos de um edital que continha 23. Lia o documento parcialmente e descartava o resto — em silêncio, com saída confiante.
Motor próprio
23–24
O mesmo edital, lido de ponta a ponta, incluindo todos os anexos. Nada resumido fora, porque nada foi pulado antes.
A segunda falha era pior e mais difícil de perceber: nos 3.000 matches mais recentes da base, a esteira comercial tinha produzido zero positivos. Não poucos. Zero. Todo dia passavam oportunidades que o cliente poderia ter vencido — e nada na interface sugeria que havia algo errado. Um sistema que não acha nada tem exatamente a mesma cara de um mercado onde não há nada.
Por que o modelo não era o problema
O instinto é culpar a qualidade do modelo e comprar um maior. Essa leitura está errada, e é por isso que este case vale ser publicado.
O modelo comercial truncava porque a esteira em volta dele foi desenhada para triar — filtrar edital por palavra-chave, escolher os anexos "relevantes", resumir cedo para economizar token. Cada uma dessas decisões se defende no custo, e cada uma joga fora exatamente a informação que decide a habilitação. A exigência que desclassifica uma empresa raramente está no documento principal. Está no anexo sete.
Então reconstruímos em cima de quatro princípios — e foram eles, não o modelo, que produziram o resultado:
- Ler o edital inteiro. Sem triagem por palavra-chave antes da leitura. Baixa tudo, lê tudo, e o match decide sobre o conteúdo completo.
- Ler todos os anexos. Não os relevantes. Todos.
- Nunca deixar um modelo produzir um número. Valores e itens vêm da API oficial de compras. Modelo de linguagem que lê preço pode alucinar preço.
- Guardar o edital decomposto, não resumido. O ativo não é o resumo bonito — é o documento inteiro, página a página, com tabelas e itens, recuperável depois.
A arquitetura
Tudo abaixo roda em infraestrutura própria. Nenhum LLM de terceiro é chamado em nenhuma etapa.
1
Ingestão — ler tudo, preservando a estrutura
Cada anexo é baixado e processado com PyMuPDF, quebrado página a página junto com as tabelas, e gravado num banco local. É a camada que define o teto de tudo que vem depois: o que não é ingerido nunca poderá ser encontrado.
2
Itens — da fonte, com zero IA
Quantidades e valores vêm direto do endpoint oficial /itens do PNCP. Determinístico, auditável e imune a alucinação. A regra é simples: modelo de linguagem lê linguagem, API fornece número.
3
Extração — map-reduce sobre o documento inteiro
Um Qwen 2.5 7B hospedado localmente lê o edital em blocos e extrai o resumo, os documentos de habilitação e os itens — map-reduce em vez de uma passada truncada, que é exatamente por que nada se perde no fim de um documento longo.
4
Match — três camadas, não uma
Filtro por palavra-chave, depois embeddings multilíngues de sentença (paraphrase-multilingual-mpnet-base-v2) para similaridade semântica, e então um modelo local atuando como juiz, que devolve nota com justificativa escrita. Três camadas porque cada uma erra de um jeito diferente, e a combinação cancela esses erros.
5
Score supervisionado — treinado no nosso próprio histórico
Sobre os embeddings, um classificador de gradient boosting (XGBoost) treinado no histórico de compatibilidade acumulado pelo produto, mais um cross-encoder multilíngue com fine-tune. Avaliado com validação cruzada em AUC-PR, ROC-AUC e Precision@K — average precision porque as classes são muito desbalanceadas e acurácia simples elogiaria um modelo que diz "não" para tudo.
O que a camada semântica realmente corrigiu
Duas falhas reais do sistema antigo mostram por que busca por palavra-chave não funciona na linguagem de licitação:
- Parou de casar o que era errado. Uma gráfica que vendia material "descartável" estava sendo casada com editais de fralda descartável. Mesma palavra, negócios sem relação.
- Passou a casar o que era certo. Uma empresa de "locação de veículos com motorista" agora casa com edital de "transporte escolar" — nenhuma palavra em comum, mesmo serviço. Esse match vale dinheiro de verdade, e busca por palavra-chave jamais o encontraria.
A decisão que mais diz sobre como a gente constrói
Quando amarramos a notificação ao resumo — o cliente só é avisado depois que o edital foi de fato lido —, alguém propôs uma válvula de escape: se o prazo estivesse acabando e o resumo não estivesse pronto, avisar assim mesmo.
Foi recusado. O sistema não alerta sem a análise por trás, mesmo ao custo de perder um prazo. Mandar para o cliente uma oportunidade que ele não consegue avaliar não é funcionalidade, é ruído que corrói a confiança em todo alerta futuro. A escolha foi qualidade da informação acima da velocidade do aviso — a mesma escolha que motivou a reconstrução inteira.
O que este case prova para o seu projeto
A LicitaSmart é um produto de licitações, e é quase certo que você não precisa de um. O que ela demonstra é a capacidade por baixo, e essa transfere direto:
- Ingerir dado sujo e não estruturado em volume — milhares de PDFs com estrutura inconsistente, decompostos em algo consultável.
- Saber onde a IA entra e onde não entra. Modelo de linguagem para linguagem, API oficial para número, parser determinístico para estrutura. A maioria dos projetos de IA que fracassa erra exatamente essa fronteira.
- ML supervisionado com avaliação honesta. AUC-PR e Precision@K em dado desbalanceado, não uma demo que fica bonita no slide.
- Rodar inferência em infraestrutura própria. Se o seu dado não pode sair do seu ambiente — saúde, jurídico, financeiro — isso já foi construído e operado por nós, em produção, com agendamento.
- Medir em vez de supor. A descoberta dos zero positivos veio de auditar a nossa própria saída. É esse hábito que a gente leva para o seu dado.
Tem dado que não está te dizendo nada?
É esse o trabalho. A gente audita primeiro e diz com honestidade se compensa consertar ou refazer — e você fala com quem faria, não com camada de vendas.
Falar com a gente