Inteligência Editorial + Comercial
Camada adicionada ao pipeline automático do Portal Ao Vivo. Complementa o fluxo existente (coleta -> resumo IA -> imagem -> publicação -> redes sociais) sem substituí-lo: se o Gemini não estiver disponível, tudo funciona via regras locais.
Escopo
- 3 scores por notícia (editorial, comercial, Instagram).
- Detecção de eventos + registro de leads comerciais.
- Conteúdo social estruturado (Reel/Stories/Carousel) + CTA comercial.
- Priorização das matérias para o Instagram.
- Frontmatter expandido nas matérias publicadas.
Novo foco
Novo foco (2026): o portal agora publica APENAS conteúdo que gere clientes
— matérias sobre eventos ou com potencial comercial (patrocínio,
transmissão, esporte com apelo). Política, polícia, crime, acidentes,
falecimentos, concurso público e curiosidades são descartados pelo pipeline.
A prospecção (commercial_prospecting.py) gera uma fila de aprovação
(data/outbox.json) para o diretor comercial decidir manualmente.
Arquitetura
config/commercial_rules.json -> triggers, alvos, parágrafos comerciais, lead
config/social_rules.json -> pesos de priorização, CTAs, hashtags
config/prospecting_rules.json -> janelas, clientes, serviços, rascunhos, pesos
scripts/analyze_article.py -> análise (IA via Gemini ou regras locais)
scripts/event_detector.py -> detecção de evento + leads
scripts/commercial_radar.py -> TOP OPORTUNIDADES (ranking por lead_score)
scripts/commercial_prospecting.py-> prospecção inteligente (janela/prioridade/rascunho)
scripts/summarize.py -> resumo com parágrafo comercial opcional
scripts/publish.py -> frontmatter expandido (compatível Jekyll)
scripts/social_poster.py -> ordenação por prioridade + legendas estruturadas
scripts/social_poster.py -> interesse_comercial(): filtra SOCIAL por evento/foco
scripts/pipeline.py -> integração das etapas 1/8 a 8/8 (filtro de foco ETAPA 3)
data/commercial_leads.json -> oportunidades comerciais consolidadas
data/outbox.json -> fila de aprovação (envio sempre manual)
data/pending_social.json -> fila social (purge por interesse_comercial)
scripts/test_commercial_intelligence.py -> testes obrigatórios (7 casos)
scripts/test_prospecting.py -> testes obrigatórios de prospecção (10 cenários)
Filtro de foco (publica só o que gera clientes)
Em scripts/pipeline.py, logo após a análise (ETAPA 3), cada matéria passa por
interesse_comercial(analise):
- Mantém:
event_related=True,event_typepreenchido,commercial_anglepresente oucommercial_score >= 7. - Descarta: demais (política, polícia, crime, saúde, concursos, etc.) e encerra a rodada se nada passar.
O social_poster.py aplica o mesmo conceito (interesse_comercial) na fila
social: itens antigos sem análise só são publicados se o título indicar
evento/patrocínio, e conteúdo negativo (crime/falecimento/previsão do tempo/
concurso/política) é sempre bloqueado, mesmo que uma análise antiga tenha
atribuído score comercial alto. Editais de convocação de servidores são
descartados; licitações/festivais de pratos típicos passam.
A pesquisa viral de curiosidades (research_viral) está desativada no
pipeline por fugir do foco comercial.
Scores (0 a 10)
| Score | Significado |
|---|---|
| editorial_score | Valor jornalístico/regional da notícia |
| commercial_score | Potencial comercial (0-4 neutro, 5-6 editorial, 7-8 discreto, 9-10 CTA direto) |
| instagram_score | Potencial de engajamento/formatos (Reel, Stories, Carousel) |
Regra principal: NOTÍCIA > PROPAGANDA. Quanto maior o score comercial, mais
suave e contextual é a menção.
commercial_score >= 7-> permite CTA comercial no conteúdo social.commercial_score >= 8-> gera conteúdo social especial (Reel etc.).- Valores de fallback se a análise falhar: editorial 5, comercial 0, instagram 5.
Radar de oportunidades comerciais (lead_score)
Cada lead recebe um lead_score (0 a 10, 1 casa decimal) que pondera:
| Fator | Peso | Como mede |
|---|---|---|
| commercial_score | 0.30 | /10 |
| proximidade | 0.25 | janela ideal 1-15 dias (60d < 15d) |
| porte | 0.15 | público esperado / porte |
| tipo do evento | 0.08 | campeonato, festival, feira, congresso… |
| cidade | 0.05 | cidade monitorada da região |
| recorrência | 0.05 | evento tradicional/anual (Festa do Café, jubileu) |
| patrocinadores | 0.05 | presença de patrocinadores/marcas |
| organizador | 0.02 | organizador identificável |
| transmissão | 0.03 | potencial de transmissão |
| conteúdo | 0.02 | potencial Instagram/editorial |
Exemplo prático (mesmo score comercial):
- evento em 60 dias -> lead_score 8.1
- evento em 15 dias -> lead_score 8.8 (prospecção primeiro)
scripts/commercial_radar.py imprime o ranking no log do pipeline e em um
passo dedicado do GitHub Actions:
1. Festa do Café [Patrocínio] - 8.8 (em 15d, novo)
2. Leilão agropecuário [Vazante] - 8.4 (em 7d, contatado)
...
Prospecção inteligente (janela e prioridade de contato)
scripts/commercial_prospecting.py responde a duas perguntas por lead: QUEM
contratar e QUANDO prospectar. É um enriquecimento acoplado ao lead (não
depende de terceiros) e é só rascunho: NUNCA envia mensagem.
Preenchimento automático (enriquecimento)
Toda nova notícia registrada passa por enriquecer_lead() (chamado dentro de
event_detector.registrar_lead()):
- cliente provável (
potential_client_type), serviços recomendados (recommended_services) e dor (commercial_need) viaclient_types/servicesdeconfig/prospecting_rules.json. - janela de contato (
contact_window/recommended_contact_window) pela data do evento. - prioridade de prospecção (
prospecting_priority, na escadamonitorar < baixo < médio < alto < urgente < muito_urgente). - organizador/tipo/source só da notícia (nunca inventa;
organizer_urlfica vazio sem evidência). sponsors_detected/sponsors_evidencesó por evidência textual.prospecting_score(0-10, separado dolead_score): pesos emprospecting_weights(lead_score .30, proximidade .22, organizador .10, cliente .10, porte .08, recorrência .06, patrocinadores .06, cidade .05, serviços .03).- quando
prospecting_priorityaponta contato Elead_score >= 8, gera umsuggested_outreach(rascunho de abordagem) — nunca envia.
Janelas por tipo (config/prospecting_rules.json -> windows)
| Grupo | monitoring | prospecting_start | priority_days | urgent_high | urgent | too_late |
|---|---|---|---|---|---|---|
| default | 90 | 60 | 30 | 20 | 7 | 2 |
| festival | 90 | 90 | 45 | 20 | 7 | 2 |
| sports | 90 | 45 | 30 | 20 | 7 | 2 |
| corporate | 90 | 60 | 40 | 20 | 7 | 2 |
| education | 90 | 50 | 25 | 20 | 7 | 2 |
| religious | 90 | 75 | 40 | 20 | 7 | 2 |
Classificação por dias até o evento: evento passado -> não prospectar;
dias >= monitoring -> monitorar; dias <= too_late -> muito tarde;
dias <= urgent -> urgente; dias <= urgent_high -> urgente;
dias <= priority_days -> prioridade/urgente; dias <= prospecting_start ->
prioridade; senão -> prospectar. Evento sem data -> monitorar; evento hoje ->
urgente/muito_urgente.
Boost de prioridade (+1 degrau) quando o evento é recorrente (tradicional/
anual/edição) ou tem histórico nos leads (previous_event_detected) ou
lead_score >= 9.
TOP PROSPECÇÃO
top_prospeccao() / print_painel_prospeccao() ordenam pelos critérios:
prospecting_score (desc) e depois a escada de prioridade. Exclui eventos
passados. O pipeline imprime o painel ao final e o GitHub Actions roda um passo
dedicado:
Prioridade: URGENTE | Score: 8.5 | Festa do Café [Patrocínio] - em 12 dias
Prioridade: ALTO | Score: 7.6 | Campeonato Regional [Patos de Minas] - em 30 dias
Sem conflito com o status
prospecting_priority é um campo derivado, recalculado a cada enriquecimento;
não substitui o status do ciclo de vida (novo -> monitorando -> contatado
-> proposta_enviada -> fechado | perdido). Re-registrar a mesma notícia preserva
status/observações/histórico e atualiza prioridade, janela e rascunho.
Funil de acompanhamento (manual)
commercial_prospecting.resumo_funil() agrupa os leads ativos pelo momento de
prospecção, para o diretor comercial:
por_prioridade/por_janela: contagens por prioridade e janela;urgentes: leads com prioridade urgente/muito_urgente;a_fazer: urgentes sem avanço de contato (contato_statusvazio,novo_leadouaguardando_resposta) — sugestão de próximo passo;monitoring,muito_tarde,passados: agrupamentos para triagem.
Os campos contato_status e ultima_contato são de acompanhamento manual
(ex.: primeiro_contato, proposta_enviada, fechado, perdido). O sistema
apenas os preserva no enriquecimento, nunca os preenche nem envia nada.
Relatório diário (artefato markdown)
commercial_prospecting.gerar_relatorio_diario() grava data/prospeccao.md
ao final do pipeline com: agenda de contato por prioridade, prioridade de hoje,
leads perdendo o timing, eventos passados, rascunhos de abordagem
(suggested_outreach — não enviados) e a fila de aprovação
(outbox). O GitHub Actions faz upload do arquivo como artefato
(prospeccao-diaria) e o commit do estado inclui o relatório em data/.
Outbox — fila de aprovação semi-manual (ETAPA 5)
O pipeline gera data/outbox.json — uma fila de rascunhos prontos para o
diretor comercial decidir. O sistema nunca envia nada; apenas GERA a fila.
Funções em commercial_prospecting:
gerar_outbox(): reconcilia a outbox com os leads ativos. Entram na fila leads comprospecting_priorityurgente/muito_urgente,prospecting_score= 8, rascunho preenchido e evento futuro. Itens órfãos viram
descartado; decisões manuais (aprovado/enviado) são preservadas.listar_outbox(status=None): lista ordenada (pendente -> aprovado -> adiado -> enviado -> descartado).atualizar_status_outbox(lead_id, status, observacao=None): grava a decisão manual (aprovado/enviado/adiado/descartado) e preencheaprovado_em/enviado_em— nunca dispara envio.print_outbox()/resumo_outbox_json(): painel no terminal e payload JSON.
Status possíveis: pendente, aprovado, enviado, descartado, adiado.
O relatório diário, o pipeline.py e o workflow (pipeline.yml) incluem a
outbox: o workflow commita data/outbox.json junto com o estado.
Dashboard web (local-admin, aba Prospecção)
O painel de controle real é o local-admin/app.py (Flask local, gitignored).
Ao adicionar a aba Prospecção ele carrega data/outbox.json do repositório
(GitHub API) e permite, 100% manual: Marcar enviado, Aprovar, Adiar
ou Descartar cada item — que é salvo de volta em data/outbox.json com
commit to repositório. Nenhuma confirmação envia mensagem; o dashboard só
registra a decisão do diretor.
Scoring pondera porte e público estimado
prospecting_score usa o fator porte/público estimado
(_porte_score de event_detector): eventos grandes (ex.: 60 mil pessoas)
pontuam acima de eventos pequenos com proximidade e scores semelhantes.
Validado por cenário de teste (C12).
Tipos de evento (galeria)
Tipos cobertos pelo detector (_detectar_tipos) e pelas regras de prospecção:
festival, festa, feira, show, rodeio, cavalgada, romaria, corrida, maratona,
campeonato, torneio, exposição, agropecuária/leilão, congresso, conferência,
encontro cultural, formatura, festa religiosa, inauguração, evento empresarial.
Cada tipo tem cliente provável, serviços, dor, janela e rascunho próprios em
config/prospecting_rules.json.
status/observações/histórico e atualiza prioridade, janela e rascunho.
Ciclo de vida do lead (simples, sem CRM)
Arquivo data/commercial_leads.json com o schema:
{
"lead_score": 8.8,
"priority": "FORTE",
"event_name": "Festa do Café",
"event_type": "festa",
"city": "Patrocínio",
"event_date": "2026-09-25",
"days_until_event": 15,
"event_timing": "em_breve",
"organizer": "Prefeitura de Patrocínio",
"estimated_audience": 10000,
"porte": "grande",
"recurring": true,
"edition": 10,
"sponsors": [],
"sponsors_count": 0,
"source": "Patrocinio Online",
"source_url": "https://...",
"commercial_angle": "alcance",
"suggested_services": ["transmissao ao vivo"],
"target_customer": ["organizador de eventos"],
"commercial_score": 8,
"instagram_score": 8,
"transmission_opportunity": 9,
"status": "novo",
"detected_at": "...",
"updated_at": "...",
"event_id": "...",
"lead_id": "...",
"recurring_event": true,
"recurrence_pattern": "anual",
"previous_event_detected": true,
"organizer_type": "Prefeitura",
"organizer_source": "Citado na notícia de ...",
"organizer_url": "",
"sponsors_detected": true,
"sponsors_evidence": ["..."] ,
"potential_client_type": ["organizador de eventos"],
"recommended_services": ["transmissao ao vivo"],
"commercial_need": "...",
"contact_window": "prospectar_prioridade",
"recommended_contact_window": "20 a 45 dias antes",
"prospecting_priority": "urgente",
"contact_reason": "...",
"suggested_outreach": "...",
"contact_history": [],
"notes": "",
"prospecting_score": 8.5,
"sources": []
}
Os campos adicionados vêm da prospecção inteligente (ver seção acima). Para
contact_reason/suggested_outreach serem (re)gerados automaticamente, o
lead precisa estar ativo e com lead_score >= 8; prioridades antigas são
atualizadas a cada novo registro da mesma notícia.
status segue o ciclo: novo (inicial) -> monitorando -> contatado ->
proposta_enviada -> fechado | perdido. O pipeline NUNCA regressa o status:
só cria/atualiza novo. Tipos de evento ainda não detectados foram adicionados:
corrida/maratona, leilão/agropecuária, formatura, festa religiosa, conferência
e inauguração.
Detecção de eventos
event_detector.detectar_evento() extrai a partir da notícia, sem inventar dados:
- nome do evento (palavras-chave + corte em conectivos, ex.: “Campeonato Regional”)
- cidade regional (lista em
themes.json -> cidades) - data (dia/mês/texto: “15 de outubro”, “em outubro”, “daqui a 5 dias”, “domingo”)
- organizador (Prefeitura, Câmara, Secretaria, Liga, Clube)
- tipo/porte/público esperado/patrocinadores/redes sociais
Leads comerciais
event_detector.registrar_lead() grava em data/commercial_leads.json quando:
event_related == trueEcommercial_score >= lead.min_score(padrão 5).- Chave única:
nome do evento | cidade | data(sem duplicação). - Fontes múltiplas consolidam os campos vazios do mesmo evento.
Cada lead guarda: organizador, tipo, porte, público esperado, score, ângulo, serviços sugeridos, fonte e prioridade (BAIXA/MEDIA/FORTE/MUITO FORTE).
Serviços e ângulos comerciais
Definidos em config/commercial_rules.json:
target_mapping: organizadores-alvo por palavra-chave.triggers: grande evento, campeonato, patrocínio, streaming etc. (inclui corrida, agropecuária/leilão, formatura, evento religioso, conferência, inauguração).commercial_paragraphs: parágrafo comercial por ângulo e nível (5-6 / 7-8 / 9-10). Ângulos: alcance, audiencia, esporte, visibilidade, empresarial.lead.min_score: mínimo de commercial_score para virar lead.
Conteúdo social
analyze_article.gerar_conteudo_social() produz (IA ou template):
reel-> hook, script, caption, ctastories-> lista de frases para storiescarousel-> título + slides
CTA e hashtags por ângulo vêm de config/social_rules.json.
Priorização do Instagram
social_poster.calcular_priority_score() pondera (pesos em social_rules.json):
views (+1.0 até o teto) + instagram_score (x2) + commercial (x2)
+ regional (x1.5) + evento (x1.5)
postar_materias() ordena a fila por prioridade antes de postar.
Frontmatter
publish.py adiciona (sem quebrar os campos atuais):
categoria: eventos
editorial_score: 7
commercial_score: 8
instagram_score: 7
event_related: true
generate_reel: true
generate_cta: true
event_type: festival
event_types:
- festival
commercial_angle: alcance
target_customer: ...
suggested_service: ...
reason: ...
instagram_hook: ...
instagram_script: ...
instagram_caption: ...
Como ajustar
Edite apenas os JSONs de config/; não precisa mexer no código:
- Aumentar peso do Instagram na fila:
config/social_rules.json -> priorizacao.weights.instagram. - Novos gatilhos comerciais:
config/commercial_rules.json -> triggers. - Novas cidades:
themes.json -> cidades. - Parágrafos/CTA/hashtags por ângulo:
config/commercial_rules.jsoneconfig/social_rules.json. - Janelas de prospecção por tipo:
config/prospecting_rules.json -> windows. - Clientes/serviços/dores e rascunhos de abordagem:
config/prospecting_rules.json -> client_types/services/outreach_templates. - Pesos do
prospecting_score:config/prospecting_rules.json -> prospecting_weights.
Testes
python scripts/test_commercial_intelligence.py # sem API: usa regras locais
python scripts/test_prospecting.py # ETAPA 3: prospecção (10 cenários + ETAPAS 4 e 5)
Cobre: festival municipal futuro, final de campeonato regional, lançamento de câmera profissional, festival com público recorde, reunião administrativa, congresso empresarial regional, notícia política sem evento, classificação temporal (passado/em breve/futuro), ranking por proximidade (15 dias > 60 dias), ciclo de vida do status, radar TOP 10, conteúdo social, frontmatter e imports do pipeline antigo.
test_prospecting.py cobre os 10 cenários de prospecção (festival em 15 dias,
campeonato em 30, congresso em 90, evento passado, sem data, evento recorrente,
com/sem patrocinadores, empresarial e esportivo) e valida: nunca envia
mensagem (só rascunho suggested_outreach), nunca inventa contato
(organizador vem da notícia, organizer_url vazio), a prioridade de prospecção
não substitui o status, o TOP PROSPECÇÃO é ordenado por prospecting_score,
o painel imprime sem quebrar, o funil/relatório markdown são gerados, o scoring
pondera porte/público e a outbox entra na fila apenas com leads fortes,
preservando a decisão manual de envio (ETAPA 5).
Resultado esperado: 21 OK / 0 FALHAS em test_prospecting.py e
10 OK / 0 FALHAS em test_commercial_intelligence.py.