analise-officers

Análise Officers — Lead Scorer: priorização de pipeline com frescor do deal

Projeto pessoal de demonstração com dados públicos e fictícios (dataset CRM Sales Predictive Analytics, Kaggle, CC0).

Esta versão pública reorganiza a documentação do experimento original; métodos, números, decisões e erros registrados são os mesmos.

Este repositório é um experimento pessoal para testar a aplicabilidade do Claude Code num problema real de ponta a ponta: um motor de score validado, uma ferramenta funcional e governança do processo. O problema de negócio é um cenário hipotético, em que uma Head de RevOps pede uma ferramenta para os officers (os vendedores, neste cenário) saberem onde focar na segunda-feira.


Resumo executivo

Neste experimento com Claude Code, num cenário hipotético de RevOps, o que separa os deals do CRM é o frescor (há quanto tempo estão abertos), não o produto, o setor, a região ou o porte (todos ficam entre 60% e 65% de vitória), e a ferramenta usa isso para dizer a cada vendedor onde focar, o que reativar e o que largar. Em quatro datas passadas, os 20% de deals no topo do score concentraram em média 62,5% da receita ganha depois, contra 48,1% ordenando só pelo valor, 29,7% pelos mais recentes e 20,0% num sorteio, e o critério fixado antes do teste passou, mas a curva atual foi trocada depois do backtest, quando usei a ferramenta como vendedor e vi que a primeira versão dava a mesma chance de 0 a 95 dias. Hoje, 1.463 dos 1.589 deals em Engaging estão na lista Limpe (chance abaixo de 2% ou mais velhos que o maior ciclo já fechado), cerca de US$ 3,6 mi de valor de tabela provavelmente inflado, e só 60 estão no Foque. O ganho vem sobretudo de tirar esses deals parados do topo: entre os deals ainda vivos o score só empata com a ordenação por valor, dentro do Foque ele ordena pouco, e os dados são sintéticos. Recomendo um sprint de limpeza da lista Limpe, confirmando cada deal com o cliente antes de encerrar, e uma rotina de segunda-feira com as listas Foque e Reative.


Experimente

Uma página estática, sem servidor, com duas visões. O vendedor abre “Minha semana” na segunda de manhã: escolhe o próprio nome e vê só os seus deals em quatro listas (Foque, Reative, Limpe e Qualifique), cada deal com uma explicação de 2 a 4 frases, ações salvas no navegador (“contatei”, “agendei”, “encerrar no CRM”) e um simulador “e se eu fechar este?”. O manager abre “Meu time”: um mapa por vendedor (vivos, esfriando e zumbis, com o valor de cada grupo), a higiene da carteira e a pauta do 1:1, sem taxa de vitória nem ranking de vendedores. Há também um “modo teste”, que mostra o que o score teria recomendado numa data passada e o que de fato aconteceu. Por trás, um motor em Python (pandas, funções puras, pytest) gera os dados da página. Documentos: escopo, qualidade dos dados, score e validação, model card e decisões.

  1. Abrir a ferramenta sem instalar nada. Duplo-clique em solution/app/index.html (funciona em file://, sem servidor e sem internet).
  2. Página publicada (GitHub Pages): https://rafael-pereira-silva.github.io/analise-officers/solution/app/. Links diretos, um por visão:

Atenção: nesta versão a separação entre vendedor e manager é de experiência, não de segurança. O data.js contém os deals de todos os vendedores; escolher um nome só filtra a tela. A separação real (login e acesso por linha) está em docs/ARQUITETURA_PRODUCAO.md.


Como rodar

  1. Regenerar tudo a partir dos CSVs (Python 3.11; testado com pandas 3.1, numpy 2.4, scipy 1.17 e pytest 9.1):
cd solution
pip install -r requirements.txt
python -m pytest
python -m scorer.build --reference-date 2017-12-31
  1. Verificação opcional da interface (abre a página num navegador, confere contagens, filtros, layout em computador e celular, contraste e console):
pip install -r requirements-dev.txt
python tools/check_app.py --prefix final
python tools/check_views.py

O segundo script testa as visões por perfil: vendedor isolado, pauta do manager, totais contra o data.js, alternância de tema e acessibilidade com o axe-core nos dois temas. O Lighthouse não foi executado (a máquina de desenvolvimento não tinha Node).

O build é determinístico: rodar duas vezes gera os mesmos bytes. A data de referência (“hoje”, 31/12/2017) é um parâmetro, não está fixa no código.


Abordagem

  1. Do problema da Head de RevOps ao que medir. As duas dores são tempo jogado em deal que não fecha e deal bom esfriando sem alerta. Isso não pede “qual deal tem mais chance”, pede “onde ponho minha próxima hora”. Por isso a métrica primária é a receita ganha capturada pelos 20% do topo, e a AUC é reportada, mas não decide. Essa regra foi escrita no escopo antes do código de avaliação.
  2. Os dados derrubaram a ideia óbvia. Um score por atributos dá perto de 63% para todo deal. O que separa é o ciclo: nenhum deal foi ganho depois de 138 dias, e 81% dos Engaging já passaram disso. Antes de usar qualquer coisa, limpei o que quebra o join e o que vaza: “GTXPro” contra “GTX Pro” (1.480 deals sumiriam) e os 1.425 deals sem conta, todos abertos, que funcionariam como detector de zumbi (qualidade dos dados).
  3. O score. Chance de ganho em 90 dias olhando só a idade do deal, vezes o preço de tabela, numa escala de 0 a 100. A curva cai com a idade desde o primeiro dia (faixas de idade com regressão isotônica) e é treinada só com as fotos do mesmo ponto do trimestre que a data do modelo. Os limites das listas saem da curva (Foque até a chance cair à metade da de um deal novo; Limpe quando ela fica abaixo de 2% ou o deal passa do maior ciclo já ganho), não de um “se idade > 138”. O vendedor não entra, e nenhum atributo entrou depois do teste (score e validação).
  4. Validação sempre no tempo. Quatro cortes passados (30/06, 31/07, 31/08 e 30/09/2017), cada um com um modelo que só vê o que o CRM sabia naquele dia, contra três baselines: valor, recência e sorteio. O critério para “vencer” foi fixado antes do primeiro número e foi mantido na correção da curva. Um teste troca tudo o que acontece depois do corte por valores aleatórios e exige que o modelo e os scores não mudem.
  5. Interface de decisão. Tela de entrada “Quem é você?” (vendedor ou manager), frase de ação no topo (“foque em N, reative M, limpe K”), uma lista por ação, explicação por deal e o modo teste, para a Head de RevOps (hipotética) ver o score contra a história antes de confiar nele. A primeira versão tinha um visual sóbrio, em marinho, off-white e um único dourado de ação; o tema atual o substituiu: um tema azul (azul #0021AF, Poppins, botões em pílula). Os tokens, o contraste e as decisões estão em docs/DESIGN.md e em process-log/09_tema-azul.md.

Resultados

Seis achados, todos gerados por script a partir do backtest_metrics.json (a curva v1 é a versão antes da correção; a v2 é a atual):

A correção da curva (depois do backtest, a pedido meu). Usei a página publicada como vendedor e o top da Rosalina Dieter era um GTK 500 com 95 dias e score 100. A curva v1 era plana de 0 a 95 dias; o briefing mostrava 50%, 34%, 7% e 0% por faixa de idade. Dois motivos: o treino misturava fotos de fases diferentes do trimestre, com padrões opostos, e o desenho “platô + queda” impõe chance constante antes do joelho. A v2 treina cada data só com fotos do mesmo mês do trimestre e é monotônica desde o dia 0. Antes e depois:

A melhora está concentrada no corte de fim de trimestre (30/09). A ordem dentro do Foque (deals de até 90 dias, média dos 4 cortes) é só um pouco melhor que a do preço:

| Método (deals de até 90 dias, média dos cortes) | AUC | Receita no top 20% | | :— | —: | —: | | Score v2 (curva por faixas) | 0,52 | 52,5% | | Score v1 (platô) | 0,51 | 49,7% | | Ordenar por valor | 0,50 | 49,1% | | Mais recentes primeiro | 0,56 | 23,5% | | Aleatório (referência) | 0,50 | 20,1% |

Backtest corte a corte (a tabela completa, com ganhos capturados, bootstrap e calibração, está no SCORING):

| Corte | Receita no top 20%: score v2 | score v1 | valor | recência | aleatório | AUC do score v2 | AUC da recência | | :— | —: | —: | —: | —: | —: | —: | —: | | Média dos 4 cortes | 62,5% | 61,1% | 48,1% | 29,7% | 20,0% | 0,66 | 0,70 | | 30/06/2017 | 61,0% | 61,3% | 48,6% | 27,7% | 20,0% | 0,66 | 0,73 | | 31/07/2017 | 57,4% | 57,9% | 48,7% | 12,8% | 20,1% | 0,63 | 0,58 | | 31/08/2017 | 59,9% | 60,7% | 48,2% | 31,6% | 19,8% | 0,63 | 0,66 | | 30/09/2017 | 71,7% | 64,8% | 47,1% | 46,8% | 20,3% | 0,72 | 0,83 |

Receita capturada pelos 20% de deals no topo da ordenação, como fração da receita que foi de fato ganha depois. Aleatório = média de 200 sorteios. Cada corte usa só o que o CRM sabia naquele dia.

As quatro listas de hoje (31/12/2017):

| Lista | Idade do deal | Ação | Deals | Valor de tabela | | :— | :— | :— | —: | —: | | Foque | até 61 dias | avançar | 60 | US$ 123.253 | | Reative | 62 a 95 dias | retomar o contato | 66 | US$ 177.537 | | Limpe | mais de 95 dias | confirmar e encerrar ou atualizar | 1.463 | US$ 3.591.439 | | Qualifique | Prospecting (sem data) | qualificar | 500 | US$ 1.073.986 |

O Limpe tem zero ganhos nos quatro cortes do backtest, por construção do dataset. A lista Reative teve de 0% a 5% de ganhos nos cortes: promete pouco e entrega pouco.

A interface (computador e celular; dados reais, tirados por tools/check_app.py):

Tela inicial, versão para computador

Tela inicial, versão para celular

As duas visões depois de escolher o perfil (vendedor Rosalina Dieter e manager Celia Rouche), em computador e celular, e o tema escuro:

Visão do vendedor, versão para computador

Visão do vendedor, versão para celular

Visão do manager, versão para computador

Visão do manager, versão para celular

Visão do vendedor no tema escuro

Atributos testados e descartados (um por vez, com a regra fixada antes): produto, região, setor e porte. A região parecia ajudar dentro da amostra de treino e piorou fora dela em três dos quatro cortes, que é o motivo de a validação ser temporal.


Recomendações

Para a Head de RevOps, em ordem de prioridade:

  1. Sprint de limpeza da lista Limpe. São 1.463 deals e cerca de US$ 3,6 mi de valor de tabela (1.291 deles passam do maior ciclo já fechado). Limpe quer dizer confirmar com o cliente e então encerrar ou atualizar, não descartar: o deal parado pode ser CRM desatualizado. Feito isso, o pipeline passa a mostrar o que é vivo.
  2. Rotina de segunda-feira com Foque e Reative. Cada vendedor abre o próprio brief, trabalha o Foque e dá um contato de retomada no Reative. Hoje é fim de trimestre, e o Foque ficou curto (deals de até 61 dias): o que já passou disso cai para Reative ou Limpe.
  3. Higiene do CRM. Preencher a conta (1.425 deals não têm), registrar a data em que o deal entrou em Prospecting (os 500 não têm, e por isso não podem ser pontuados) e guardar o histórico de estágios. Sem isso, a lista Qualifique não tem prova e o score só enxerga “dias desde o engajamento”.
  4. Não usar win rate por vendedor. A diferença entre vendedores é compatível com ruído (qui-quadrado p=0,26). Cobrar ou premiar com base nela pune pessoas por acaso.
  5. Recalibrar com dados reais. Este dataset é sintético. Rodar o mesmo backtest, com o mesmo critério fixado antes, num CRM real, a cada trimestre (regras de recalibração no model card).

Limitações


Como usei IA

Como eu trabalhei, em uma frase: primeiro explorei os dados em conversa com o Claude até entender o problema. Depois transformei o que aprendi em regras escritas (o “kit”: CLAUDE.md, PROMPTS.md, GUIA.md e PROMPT_AUTONOMO.md). Por fim, deleguei a execução a uma equipe de agentes Claude, sob controles que eu defini. Não colei o problema numa IA para esperar o resultado. Montei e governei um processo de desenvolvimento com IA, e cada decisão dele é auditável. O mapa completo das evidências está em process-log/README.md.

Ferramentas usadas

Ferramenta Para que usou
Claude (app), Sonnet 5.5 e depois Opus 5.5 Fase 0, em conversa comigo: ler o material de partida, comparar 4 problemas em consideração, explorar os dados, derrubar a hipótese “score por atributos” e validar o frescor. Depois, redigir comigo o kit de regras e prompts por bloco (00_fase0_exploracao.md)
Claude Code com Opus 5.5 (orquestrador) Executar o meu prompt de operação (PROMPT_AUTONOMO.md): preparar o ambiente, disparar e conferir os executores, consolidar os logs e commitar um bloco por vez
Subagente Claude Opus 5.5 (planejador, somente leitura) Transformar o kit num plano de execução com especificação por etapa e protótipos descartáveis (plano_execucao_opus.md)
Subagentes Claude Sonnet e Haiku (executores) Fazer o volume: código, testes, interface e documentos. Haiku no trabalho mecânico, Sonnet no que exige julgamento local. Cada log declara o executor
Subagente de contexto limpo (revisor, bloco 6) Fazer o papel de revisor independente: duas revisões, sem acesso ao processo, que avaliam a entrega contra critérios de qualidade (funciona, scoring, usabilidade, decisão, código, documentação e processo) e apontam o que pareceria entrega de IA sem supervisão (06_revisao-independente.md)
Playwright + Edge, axe-core, pytest, git/gh Ferramentas que não são de IA e que eu usei para não acreditar na palavra da IA: o Playwright abre a página de verdade (local e publicada), o axe-core audita a acessibilidade, o pytest prova as regras e o git registra um commit por etapa

Workflow

Quem fez o quê. A IA escreveu quase todo o código e o texto. O desenho, as regras e os pontos de decisão foram meus.

O que eu decidi ou defini O que deleguei à IA O controle que eu exigi para confiar no resultado
A escolha do problema para o experimento e a leitura da dor como “alocação de tempo”, não “qual deal tem mais chance” Escopo detalhado e respostas às 4 perguntas abertas Cada resposta com alternativa descartada e número (SCOPE.md)
A tese “frescor > atributos”, depois que os dados derrubaram o meu plano inicial Curva de chance por idade, score e explicações Backtest temporal em 4 cortes contra 3 baselines, com critério e ajuste único fixados antes
Tirar o vendedor do score (diferença compatível com ruído) Implementação do score Teste de permutação: trocar os vendedores de lugar não muda nenhum score
“Nenhum número in-sample é resultado” e “nenhum número digitado à mão” Limpeza, métricas e documentos Teste que confere cada número da prosa contra os arquivos gerados; teste de mutação do futuro contra vazamento
Não usar Streamlit; página estática que abre com duplo-clique Interface e build Playwright em file:// com dezenas de checagens, inclusive contagens contra o SCORING, mais uma bateria própria para as visões por perfil e o axe-core
Não publicar nada em meu nome sem mim Commits locais e texto de publicação O push e o GitHub Pages ficaram reservados para mim
Revisão independente e “não maquie resultado” Correções apontadas pelo revisor Modelo, parâmetros e métrica congelados no bloco 6: crítica de modelagem vira limitação documentada, não ajuste pós-hoc. Depois, ao usar a ferramenta como vendedor, pedi a troca da curva, que ficou declarada como mudança pós-backtest, com a versão antiga ao lado

Passo a passo:

  1. Fase 0 (eu + Claude app) e bloco 0 (setup). Explorei os dados em conversa, achei o join quebrado (“GTXPro”) e a métrica inflada, e pivotei do score por atributos para o frescor. Com isso redigi o kit: 7 decisões fechadas, um prompt por bloco com checkpoints, o guia de armadilhas e o prompt de operação. No Claude Code, o orquestrador montou o ambiente, e um executor Haiku reproduziu os números da fase 0 contra o briefing antes de qualquer código novo.
  2. Bloco 1, escopo. Um executor Sonnet respondeu às 4 perguntas que eu tinha deixado em aberto. Cada resposta traz alternativa descartada e evidência. O executor também achou um erro no meu briefing (o produto com mais deals) e contestou com dados uma justificativa do plano.
  3. Bloco 2, dados. Limpeza com três investigações, cada uma com critério escrito antes do resultado. Montamos a infraestrutura “todo número vem de script”: blocos de documento regenerados e um teste que confere cada número da prosa.
  4. Bloco 3, score e validação. A checagem de vazamento foi escrita antes do código, como exigi. O critério de sucesso foi registrado antes do primeiro número de desempenho. O critério passou na primeira rodada, e o ajuste único reservado não foi usado (a troca de curva do item 7 é outra coisa, feita depois).
  5. Bloco 4, ferramenta. Para acelerar sem perder qualidade, a interface foi construída em paralelo ao motor, a partir de um contrato de dados escrito antes. Depois a página foi verificada com Playwright, e uma passada “vendedor na segunda de manhã” corrigiu 3 atritos com screenshots de antes e depois.
  6. Bloco 5, documentação. README e model card, com todos os números vindos de blocos regenerados e conferidos por teste.
  7. Bloco 6, revisão independente. Dois revisores de contexto limpo avaliaram a entrega contra critérios de qualidade (funciona, scoring, usabilidade, decisão, código, documentação e processo). As críticas de interface e documentação foram corrigidas. As de modelagem viraram texto com número, porque eu tinha congelado o modelo para evitar ajuste pós-hoc (relatórios e triagem).
  8. Revisão minha e correção da curva. Depois do bloco 6, revisei a entrega e usei a página publicada como vendedor: a chance era constante de 0 a 95 dias e o top da Rosalina Dieter era um GTK 500 com 95 dias. Pedi a correção de ponta a ponta (curva, limiares, métrica de ordem dentro do Foque, frase do card, interface e documentos), com a curva antiga mantida para comparação e a mudança declarada como pós-backtest (registro).
  9. Visões por perfil, arquitetura de produção e design. Escrevi a especificação (PROMPT_VISOES_E_DESIGN.md) com três peças:
    • a entrada por perfil: “Minha semana” para o vendedor, “Meu time” para o manager, com pauta do 1:1 e higiene de carteira (sem ranking de vendedor), e uma visão completa para RevOps;
    • a segregação real de dados documentada como arquitetura de produção (ARQUITETURA_PRODUCAO.md);
    • uma direção visual (a identidade da primeira versão, em marinho e dourado, foi depois substituída pelo tema atual; DESIGN.md e 09_tema-azul.md). A interface passou por três rodadas de screenshots com crítica e correção, e por auditoria de acessibilidade com axe-core (registro).

Onde eu intervim durante a execução (registrado nos logs por bloco e no DECISIONS.md; o export integral da conversa não está nesta versão pública, ver chat-exports/):

Onde a IA errou e como corrigi

Só episódios registrados em process-log/ e em docs/DECISIONS.md. Na execução delegada, quem pegou os erros foram os controles que eu tinha exigido: verificação de números, testes de vazamento, critério pré-registrado e conferência visual.

Na fase 0, com o Rafael:

Na execução delegada (os onze primeiros foram pegos pelos controles; o último, não):

O que eu adicionei que a IA sozinha não faria

Trabalho com governança de IA, e foi com esse olhar que conduzi o experimento: tratei o meu próprio uso de IA como um sistema que precisa de regras, rastreabilidade e um humano responsável. O que adicionei vai de entender o problema antes de abrir a ferramenta a decidir o que automatizar e o que não automatizar.


Reaproveitamento em outros contextos

O que sai deste dataset e o que fica nele.

O que é reaproveitável fora deste dataset

O que não é reaproveitável

Um piloto em 2 passos

  1. Diagnóstico com histórico real de CRM. Rodar o mesmo backtest, com o critério fixado antes, sobre o histórico de um CRM real, e ver se o score bate os baselines (valor, recência e sorteio).
  2. Teste com um time e controle, só se o passo 1 bater os baselines. Dar as listas a um time e manter outro como controle, e comparar a receita ganha e o tempo dedicado a deals que não fecham.

Evidências

Tudo está neste repositório. O índice, que liga cada pergunta sobre o processo ao arquivo que a responde, é o process-log/README.md.


Sobre o autor

Rafael Pereira. Trabalho com governança de IA. LinkedIn: https://www.linkedin.com/in/rafael-pereira-30122003-