尚未偵測到更新。GitHub 發布版本後會顯示在這裡。
個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證SaaS de Licitação para o Fitness
翻譯準備中
取自儲存庫 README
正在載入完整 README。 以下為已儲存的摘錄。
Coletores LicitaGym (PNCP + SEST SENAT) → Supabase + RAG Versão no git: v15. A partir desta versão o coletor não é mais distribuído por ZIP. Código canônico: este repositório (coletor/, tests/, supabase/migrations/, monitor.sh). O detalhe da compra no PNCP passou para /api/consulta/v1/.... O caminho antigo /api/pncp/v1/.../compras/{ano}/{seq} responde 301 em JSON (sem Location útil). Itens, resultados e arquivos permanecem em /api/pncp/v1. Ver PNCP.detalhe_compra / PNCP.compra em coletor/pncp.py.檢視完整 README ↗
Versão no git: v15. A partir desta versão o coletor não é mais distribuído por ZIP.
Código canônico: este repositório (coletor/, tests/, supabase/migrations/, monitor.sh).
# primeira vez (Cloud Shell ou máquina local)
git clone https://github.com/n4rch6pjt9-rgb/LicitaGym.git
cd LicitaGym
git checkout main # após merge do PR v15; ou a branch feat/coletor-pncp-v15
pip install --user -r requirements.txt
# atualizar (substituí o unzip do zip)
cd ~/LicitaGym # ou o path do clone
git fetch origin
git checkout main
git pull --ff-only origin main
pip install --user -r requirements.txt # se requirements mudou
Monitor do job em andamento:
bash monitor.sh # padrão: processo_edital.log
bash monitor.sh pncp.log # outro log
Testes offline:
pip install --user -r requirements.txt pytest
python -m pytest tests/test_pncp.py tests/test_processo_edital.py -q
O detalhe da compra no PNCP passou para /api/consulta/v1/.... O caminho antigo
/api/pncp/v1/.../compras/{ano}/{seq} responde 301 em JSON (sem Location útil).
Itens, resultados e arquivos permanecem em /api/pncp/v1.
Ver PNCP.detalhe_compra / PNCP.compra em coletor/pncp.py.
O número do edital ("Pregão Eletrônico nº 001/2026") se repete entre órgãos e não identifica nada.
codigo_externo = número de controle PNCP (CNPJ-1-sequencial/ano).python3 -m coletor.buscar_processo "00007.20260204/0002-28" --cnpj 07.540.925/0001-74
python3 -m coletor.pncp --corrigir-processos # corrige compras gravadas até a v12
python3 -m coletor.processo_edital --dry-run # completa processos curtos ("4") lendo o edital
Busca nacional no PNCP por frase exata (por padrão os 160 termos de TERMOS_ESCOPO_COMPLETO; --termos-padrao = 12 termos), classifica cada compra
pelo objeto e pelos itens (coletor/escopo.py) e grava compra, itens, vencedores e arquivos.
Três modos. O modo só escolhe o filtro status da busca no PNCP; não define sozinho a prioridade gravada.
| Modo | Filtro PNCP | Prioridade |
|---|---|---|
leads (padrão) | recebendo_proposta | recebendo proposta |
monitorar | em_julgamento | em julgamento, suspensa, adjudicação ou recurso |
historico | encerradas | encerrada ou homologada |
Prioridade (owner, 02/10/2026): leads = recebendo proposta; monitorar = em julgamento, suspensa, adjudicação ou recurso; historico = encerrada ou homologada. Status desconhecido nunca vira lead. Falha de API ou documento ausente não pode gravar encerradas/historico nem promover a leads. Compra homologada não é lead.
Preço por item CATMAT, marca e mapa de fornecedor (revenda ou fabricante) saem só de certames homologados, ficam só no BI e são rastreáveis pelo certame. Revogada, anulada, deserta ou fracassada não alimentam esse mapa.
# schema: supabase/migrations/20260924100000_pncp_itens_resultados.sql (via supabase db push)
python3 -m coletor.pncp --dry-run --termos "borracha granulada" --tam 20 # teste rápido
python3 -m coletor.pncp # leads: recebendo proposta
python3 -m coletor.pncp --modo monitorar # em julgamento
python3 -m coletor.pncp --modo historico --paginas 10 --baixar-arquivos # encerradas
python3 -m coletor.indexador # arquivos baixados -> RAG
Reclassificar as linhas PNCP já gravadas (as antigas leads homologadas viram historico) — dry-run por padrão:
export SUPABASE_URL=... SUPABASE_SERVICE_ROLE_KEY=... # do ambiente, nunca no código
python3 -m coletor.backfill_prioridade_pncp # DRY-RUN: contagem por transição + amostra
python3 -m coletor.backfill_prioridade_pncp --consultar-pncp --limit 50 # DRY-RUN relendo o detalhe no PNCP (GET)
python3 -m coletor.reclassificar_escopo_pncp # DRY-RUN (prioridade + fase)
python3 -m coletor.reclassificar_escopo_pncp --apply # grava prioridade, fase e escopo
backfill_prioridade_pncp --apply está desativado (sai com código 2): ele decide só a prioridade, sem documentos
nem fase, e sobrescreveria fases documentais como "Suspensa (documento)". O dry-run dele fica como diagnóstico legado.
oportunidades_borracha lista primeiro os leads (certames abertos, ainda sem vencedor) e depois o resto, do
homologado mais recente para o mais antigo, com dias_desde_homologacao. Para oferecer raspa ao vencedor,
use as linhas historico com homologação recente (dias_desde_homologacao), não prioridade = 'leads'.
Volume observado (24/09/2026, frase exata): grama sintética 3.263 (85 abertas), academia ao ar livre 1.563, equipamentos de academia 1.023, piso emborrachado 986, campo society 794, borracha granulada 122. A busca do PNCP também acha termos dentro dos itens — decoração natalina, arbitragem e grama natural são descartadas pelo classificador.
Raspa de borracha para o vencedor: a view oportunidades_borracha junta compra, item e vencedor
(ex.: Carapicuíba/SP, 50 t de borracha granulada G3 homologadas a R$ 2.779,46/t para HG Comércio).
Coleta licitações encerradas do portal de compras do SEST SENAT
(compras.sestsenat.org.br, plataforma Paradigma) e registra no Supabase:
dados do processo, esclarecimentos/impugnações do fórum, notas da comissão e
todos os anexos (edital, propostas, lances, negociação, habilitação,
recursos, contrarrazões e pareceres). Os arquivos vão para o Cloud Storage.
O portal não tem API pública, mas a própria página usa um webservice JSON
interno (/portal/WebService/Servicos.asmx). O coletor chama esses mesmos
endpoints — não precisa de navegador headless.
Definido a partir da tabela pca_itens: 3.331 itens de PCA de 491 planos, ~R$ 146 mi,
100% na classe CATMAT 7830 — Equipamento para Ginástica e Recreação. PDMs mais planejados:
condicionamento físico (276), rede esporte (241), haltere (206), tatame (100), apito (71),
aparelho de ginástica (62), brinquedo inflável (53), bicicleta ergométrica (50), mesa de tênis de mesa (40).
coletor/escopo.py concentra: classes CATMAT, PDMs prioritários, termos de busca e o
classificador classificar(objeto, classes_catmat) → catmat | forte | fraco | None.
Também entram:
interesse_borracha(objeto) marca as licitações onde o vencedor vai precisar de raspa/granulado.
Serviço de evento, uniforme e troféu ficam fora. Um termo de busca sozinho não torna a linha forte.
Polia e espaldar são forte. Pilates, "aparelho para condicionamento físico" genérico e colchonete são fraco.
Puxador é positivo só na categoria acessórios.
O coletor usa --escopo fitness por padrão (--escopo tudo desliga o filtro).
O schema vem de supabase/migrations/20260923100000_licitacoes_externas.sql (aplicado por supabase db push;
em produção já existe e está registrado via supabase migration repair). Cria:
| Tabela | Conteúdo |
|---|---|
licitacoes_externas | 1 linha por processo (+ esclarecimentos e notas em JSON) |
licitacao_documentos | 1 linha por arquivo, com seção, fornecedor, lotes e status |
licitacao_chunks | trechos + embedding 768d (preenchida na próxima etapa) |
match_licitacao_chunks() | busca semântica com filtro por seção/fonte |
pip install -r requirements.txt
python -m coletor.main --ids 1,10,30,41 --dry-run # só lista, não grava
export SUPABASE_URL=https://<projeto>.supabase.co
export SUPABASE_SERVICE_ROLE_KEY=... # nunca no frontend
python -m coletor.main --ids 1,10 # grava; arquivos em ./dados
PROJECT=<seu-projeto-gcp>; REGION=southamerica-east1; BUCKET=licitagym-docs
gcloud config set project $PROJECT
gcloud services enable run.googleapis.com artifactregistry.googleapis.com \
cloudbuild.googleapis.com secretmanager.googleapis.com storage.googleapis.com
gcloud storage buckets create gs://$BUCKET --location=$REGION
printf '%s' "$SUPABASE_SERVICE_ROLE_KEY" | gcloud secrets create supabase-service-key --data-file=-
gcloud run jobs deploy coletor-sestsenat --source . --region $REGION \
--set-env-vars SUPABASE_URL=$SUPABASE_URL,GCS_BUCKET=$BUCKET,DELAY_SEGUNDOS=1.5 \
--set-secrets SUPABASE_SERVICE_ROLE_KEY=supabase-service-key:latest \
--args="--de,1,--ate,120" --task-timeout=6h --max-retries=1
gcloud run jobs execute coletor-sestsenat --region $REGION
A conta de serviço padrão do Cloud Run precisa de roles/storage.objectAdmin
no bucket e roles/secretmanager.secretAccessor no secret.
Para rodar toda semana: Cloud Scheduler → gcloud run jobs execute.
A coleta é idempotente (upsert + só baixa o que está pendente/erro).
| Variável | Padrão | Uso |
|---|---|---|
SECOES_DOWNLOAD | processo,recurso,contrarrazoes,parecer | Seções cujos arquivos são baixados. Metadados de todas as seções são sempre gravados. |
DELAY_SEGUNDOS | 1.5 | Pausa entre requisições (seja gentil com o portal). |
MAX_MB | 80 | Arquivos maiores ficam como ignorado. |
GCS_BUCKET | — | Sem ela, salva em ./dados. |
Flags: --ids, --de/--ate, --modulo (59 = pregão eletrônico), --todos
(inclui processos em andamento), --dry-run.
Um pregão de registro de preços grande (8453-4/2025) tem 32 arquivos no processo
mas ~1.000 arquivos únicos de fornecedores (proposta 32, lance 265, negociação 415,
habilitação 264), muitos em .zip/.rar. Por isso o padrão baixa só as seções
de maior valor para o RAG. Ligue proposta/negociacao quando quiser preços.
DELAY_SEGUNDOS ≥ 1 e rode fora do horário comercial.gcloud services enable aiplatform.googleapis.com
pip install --user -r requirements.txt
python3 -m coletor.indexador --limite 3 # teste com 3 arquivos
python3 -m coletor.indexador # todo o resto
python3 -m coletor.buscar "Por que a Freedom Motors recorreu e qual foi a decisão?"
.bin/sem extensão):
%PDF- → PDF; PK → ZIP, ou DOCX se tiver word/document.xml, ou XLSX se tiver xl/.pypdf; PDF escaneado → OCR pelo Gemini; ZIP é aberto (até 2 níveis, nomes
internos em cp850/UTF-8 sem flag corrigidos); DOCX lido direto; XLSX vira texto simples
(uma linha por linha da planilha, células com " | ", sem datas formatadas, até 200 mil caracteres);
.rar, .7z, .pptx, .doc/.xls antigos e imagens ficam em extracao.arquivos_ignorados.
Arquivo com nome .pdf que não é PDF (ex.: página HTML de erro) é ignorado, não vai para o OCR.licitacao_documentos.extracao (tipo, resumo,
fornecedores, decisão, motivos, fundamentos legais, valores, pontos-chave) e um trecho-resumo.text-multilingual-embedding-002 (lotes de até 30 trechos, 1 chamada a cada 12,5 s), 768 dimensões, RETRIEVAL_DOCUMENT (consulta usa RETRIEVAL_QUERY).
Cada chunk grava o modelo em licitacao_chunks.embedding_model (padrão da coluna: o mesmo modelo).GEN_MODEL (padrão gemini-3.8-flash, desde 22/10/2026: o gemini-2.5-flash foi descontinuado), GEN_LOCATION (padrão global, endpoint do 3.8 Flash) e GEN_THINKING (padrão deduzido do modelo: low no Gemini 3+, off no 2.5-flash; aceita off, low, medium, high ou padrao) são configuráveis. O embedding usa GOOGLE_CLOUD_LOCATION (padrão us-central1), separado da geração. O embedding do RAG permanece text-multilingual-embedding-002 com 768 dimensões; trocar o modelo exige um plano de reindex aprovado. Não apontar EMBED_MODEL para outro modelo sem esse plano.Cota do Vertex (projeto novo): o modelo de embeddings começa com 5 requisições/min
por região. O indexador já respeita isso (EMBED_INTERVALO=12.5). Para acelerar, peça aumento em
IAM e administrador → Cotas → "online prediction requests per base model … textembedding-gecko"
(us-central1) e reduza EMBED_INTERVALO. PDFs escaneados passam por OCR em partes de
OCR_PAGINAS=8 páginas.
Importante para o app: a pergunta do usuário precisa ser convertida em vetor com o
mesmo modelo (text-multilingual-embedding-002, 768d, RETRIEVAL_QUERY) antes de chamar
match_licitacao_chunks. Veja coletor/buscar.py como referência.