Skip to content

Repository files navigation

Radar de Licitações de Software

Coleta, classifica e rankeia licitações públicas de software — foco inicial em Sergipe, arquitetura preparada para o Brasil inteiro.

Fonte primária: PNCP (Portal Nacional de Contratações Públicas). Pela Lei 14.133/2021 a divulgação no PNCP é condição de eficácia do ato, então ele é um espelho legalmente obrigatório de tudo — não é preciso raspar portal nenhum.

O estudo que fundamenta cada decisão de arquitetura está em ESTUDO.md, com os números medidos contra a API de produção e os scripts que os produziram em prototipo/.


Rodando

Opção 1 — Docker (produção, PostgreSQL)

cp .env.example .env      # ajuste UFS_MONITORADAS e USER_AGENT
docker compose up --build

Abra http://localhost:8000. Na aba Configuração → Operação, clique em carga inicial (12 meses) para a primeira coleta.

Opção 2 — Local (desenvolvimento, SQLite)

python -m venv .venv
.venv/Scripts/pip install -e ".[dev]"        # Linux/macOS: .venv/bin/pip
cd frontend && npm install && npm run build && cd ..
.venv/Scripts/python -m backend.main

O SQLite é o padrão: sem Docker, sem Postgres, sem serviço externo. O schema é criado na primeira execução.

Para desenvolver o frontend com hot reload, rode npm run dev em frontend/ (porta 5173, com proxy do /api para o backend na 8000).


Primeira coleta

Pela interface (Configuração → Operação) ou pela API:

curl -X POST localhost:8000/api/coletar -H 'Content-Type: application/json' \
     -d '{"job":"carga_inicial"}'

A carga inicial leva alguns minutos. O PNCP falha com frequência (500 de banco, timeouts); a coleta isola a falha por segmento e o job termina com status parcial, listando exatamente quais segmentos ficaram de fora. Rodar de novo costuma resolver — e parcial nunca é reportado como ok, justamente para que a lacuna não passe por "o PNCP não publicou".

Depois disso o agendador assume:

Job Frequência Por quê
Propostas abertas 1 hora A janela mediana de uma dispensa de software em SE é de 5 dias; 6% têm 3 dias ou menos
Publicações recentes 3 horas Órgão atrasa publicação e errata republica o edital
Detalhe e resultados diário Itens, PDFs e vencedores
Dossiê do edital 6 horas Baixa o PDF e extrai as exigências de habilitação
Contratos e atas semanal Prospecção antes do edital existir; atas nossas viram adesão
Impedimentos CEIS/CNEP diário O Portal da Transparência só publica o arquivo do dia corrente
PCA mensal Intenção de compra do ano
Resumo por e-mail 7h Para quem prefere e-mail a notificação

Como o sistema decide

Classificação em duas camadas. Medi 389 itens sergipanos: 0% têm CATMAT/CATSER preenchido, então não existe filtro estruturado — é texto ou nada. A primeira camada é regex ponderado (determinístico, auditável, reprocessável); só a zona de dúvida (~6% dos editais) vai para o LLM. Sem ANTHROPIC_API_KEY o sistema roda apenas com as regras e a dúvida fica para revisão humana.

Dossiê do edital. O PNCP entrega o edital em /arquivos — PDF nativo, PDF escaneado ou ZIP com os anexos. O sistema baixa, extrai o texto (testado em editais reais: ~200 mil caracteres, inclusive de dentro de ZIP) e, com ANTHROPIC_API_KEY configurada, estrutura as exigências: atestado de capacidade técnica, capital social mínimo, sede local, prova de conceito, garantia. Essas exigências entram no componente "ausência de barreiras" do score — é o que evita montar proposta e descobrir cláusula eliminatória na página 60. Quando o PDF é escaneado, o sistema diz isso explicitamente em vez de deixar o campo vazio e parecer falha de coleta.

Atas e adesão ("carona"). Ganhar uma ata não garante faturamento — o órgão compra por demanda. O valor está no art. 86 da Lei 14.133: outro órgão pode aderir a uma ata existente e comprar sem nova licitação. Configure o CNPJ da empresa e o sistema identifica em quais atas vencemos (prospecção de adesão) e quais são de concorrentes (preço praticado e posicionamento).

Impedimentos de licitar (CEIS/CNEP). Serve a dois lados. Sobre a nossa empresa: sanção vigente inabilita em qualquer sessão, e descobrir isso no dia é perder a licitação por algo que se sabia com meses de antecedência — daí o alerta crítico. Sobre concorrentes: impedido declarado vencedor é fundamento de impugnação. Os dois cadastros vêm do download em massa do Portal da Transparência (23.364 + 1.716 registros, sem precisar de chave de API).

O sistema separa impedimento de sanção registrada: multa e publicação extraordinária da condenação constam no cadastro mas não barram a disputa. São 1.655 dos 1.716 registros do CNEP — tratar tudo como impedimento marcaria 96% do cadastro como impedido. No sentido oposto, "declaração de inidoneidade sem prazo determinado" (1.078 registros do CEIS sem data final) é impedimento por tempo indeterminado: ali a ausência de data significa vigente, não ausência de sanção.

Funil de acompanhamento. Analisando → vamos participar → proposta enviada → em disputa → encerrada. Só entra o que a equipe colocou lá: a grade tem centenas de linhas e o funil, dezenas. Cada coluna soma o valor em jogo, e mover para uma etapa marca a oportunidade como interesse — a estrela da grade e o funil contam a mesma história.

Ranking explicável. Seis componentes com pesos configuráveis pela interface: aderência técnica, baixa concorrência, janela de tempo, adequação de valor, ausência de barreiras e proximidade. Cada linha da grade mostra por que está naquela posição.

O sistema mede a si mesmo. A aba Qualidade calcula precisão e recall do classificador contra as correções feitas na grade (metas do estudo: 85% e 95%), a taxa de participação e de vitória, e a correlação entre score e vitória real — se licitação de score alto não é ganha com mais frequência, o ranking está errado e os pesos precisam mudar. Nenhum número aparece sem o tamanho da amostra: com 3 rótulos, 100% de precisão não é informação. Para isso o veredito automático é guardado à parte da correção humana; sem esse par, a correção apagaria justamente a evidência de que a máquina errou.

"Menos concorrentes" é estimativa declarada, não medição. Nenhuma API pública expõe número de participantes — verifiquei nos schemas do PNCP e nos 77 endpoints do Compras.gov.br. Os sinais usados são proxies (exclusividade ME/EPP, dispensa, item deserto/fracassado, praça pouco disputada, portal de nicho). A aba Participações existe para registrar quantos concorrentes realmente apareceram; com algumas dezenas de registros os pesos passam a ser calibrados por evidência em vez de opinião.


Estrutura

backend/
  coletores/     cliente PNCP resiliente, mapeamento, persistência, jobs
  classificacao/ regras determinísticas + triagem por LLM
  enriquecimento/ download do edital, extração de PDF/ZIP e dossiê
  api/exportacao.py  CSV (BOM + `;` para o Excel pt-BR) e XLSX
  ranking/       motor de score explicável
  api/           rotas e schemas
  qualidade.py   precisão/recall do classificador, correlação score x vitória
  alertas.py     Telegram e e-mail
  agendador.py   APScheduler
frontend/src/    React + TanStack Table + Tailwind (7 abas: Radar, Funil,
                 Concorrentes, Prospecção, Participações, Certidões, Qualidade)
alembic/         migrações
prototipo/       scripts que produziram os números do estudo
tests/           unitários, pipeline e API (offline) + testes de rede

Testes

.venv/Scripts/python -m pytest              # 842 testes, rápido, offline
.venv/Scripts/python -m pytest -m rede      # bate na API real do PNCP
.venv/Scripts/python -m ruff check backend/ tests/

Os testes de pipeline e de API rodam offline, com payloads reais do PNCP guardados em tests/fixtures/. Isso foi uma decisão tomada na marra: durante o desenvolvimento o PNCP caiu no meio de um teste ponta a ponta. Um pipeline que só pode ser verificado quando um serviço de terceiro está saudável não é verificável.

Os testes do dossiê geram PDFs em memória com a própria PyMuPDF, cobrindo PDF, ZIP de PDFs e PDF escaneado sem binário no repositório.

Todo falso positivo encontrado em coleta real virou teste de regressão — mão de obra de R$ 47 milhões, "Sistema Único de Saúde", papel A4 para o almoxarifado, aluguel de impressora térmica, R$ 13 milhões de livros de espanhol que vinham com "plataforma digital", o SAEB confundido com sistema, e aplicação de provas confundida com plataforma de provas. O ESTUDO.md (Anexo E) explica cada caso.

Há um teste que percorre todos os padrões do classificador e reprova qualquer um que contenha caractere de controle ou que não case nem com um texto derivado de si mesmo. Ele existe porque \b já virou o byte 0x08 duas vezes ao editar as regras por script: o padrão continua compilando, nunca mais casa com nada, e não há sintoma — um grupo negativo inteiro ficou inativo em silêncio.

Os testes do CEIS/CNEP carregam os cabeçalhos reais dos dois arquivos, porque o CEIS tem 24 colunas e o CNEP tem 25: o CNEP insere VALOR DA MULTA na posição 10 e desloca todo o resto. Ler por posição colocaria a data de início do CNEP no campo da categoria — sem erro nenhum, com dado errado no banco.


Configuração

Tudo em .env (veja .env.example). O que mais importa:

Variável Padrão Observação
BANCO_URL SQLite local Postgres em produção
UFS_MONITORADAS SE Vazio = Brasil inteiro
USER_AGENT Coloque um contato real: é API pública, identificar-se é o mínimo
ANTHROPIC_API_KEY vazio Sem ela, só as regras determinísticas
TELEGRAM_BOT_TOKEN / TELEGRAM_CHAT_ID vazio Alertas urgentes
SMTP_* vazio Alertas por e-mail

Pesos do ranking, faixa de valor, capacidades técnicas e portais credenciados ficam no banco, editáveis pela interface — a equipe muda conforme a estratégia do trimestre e vê o efeito na hora.


Antes de faturar (não depende de software)

  1. Credenciar no Licitanet — dominante nos municípios de Sergipe (141 dos 640 editais de software medidos).
  2. SICAF atualizado e certificado digital e-CNPJ A1.
  3. Certidões em dia com calendário de renovação (federal, FGTS, CNDT, estadual, municipal, falência).
  4. Emitir atestado de capacidade técnica de todo cliente atual — é o ativo mais escasso de empresa nova e o que mais elimina pequenas na habilitação.
  5. Enquadramento ME/EPP formalizado: destrava os 30% de itens exclusivos.

O sistema avisa quando a disputa vai acontecer em portal onde a empresa não está credenciada — score 95 em portal inacessível vale zero.


Conformidade

Os dados são públicos por determinação legal e coletá-los é lícito. O sistema não raspa portais privados: o robots.txt do Licitanet bloqueia /api/ e o BLL declara sinais contra coleta automatizada — e não é necessário, porque o PNCP cobre. As chamadas se identificam via USER_AGENT, respeitam intervalo entre requisições e nunca são retentadas em erro 4xx (que é bug nosso, não instabilidade deles).

About

Radar de licitacoes publicas de software: coleta, classifica e rankeia editais do PNCP (Lei 14.133/2021), com decisoes de arquitetura medidas contra a API de producao.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages