← Projetos

Automação de WhatsApp para CRM, com IA classificando

Script Python que lê as conversas pela Evolution API, classifica com a API da Anthropic e alimenta o CRM sozinho toda manhã, com banco e processamento na própria máquina.

PythonAPI AnthropicEvolution APISQLiteFlask
Status
Em produção
Ano
2026
Contexto
Produto próprio, em uso diário
07h30
Roda sozinho todo dia
Local
Banco e processamento na máquina
Automação de WhatsApp para CRM, com IA classificando

O problema

Todo o atendimento acontecia no WhatsApp, e o histórico ficava só lá. Saber quem perguntou sobre o quê, em que mês, e o que ficou combinado exigia rolar centenas de conversas. O dado existia, só não era consultável.

O que construí

  • Coleta. A Evolution API expõe as conversas; um script Python lê as mensagens novas desde a última execução bem-sucedida.
  • Classificação. Cada conversa passa pela API da Anthropic, que devolve o estágio do funil e um resumo estruturado. Para contato que já existe, o estado atual do CRM entra no prompt, então o modelo consolida em vez de sobrescrever o que a última janela de conversa não menciona.
  • Persistência. Banco local, com deduplicação por mensagem para que reexecuções não inflem o histórico.
  • Agendamento. O Task Scheduler do Windows dispara às 07h30, com recuperação quando a máquina passou o horário desligada.
  • Fila de triagem. Uma varredura separada analisa o histórico antigo, que a janela do sync diário nunca alcançou, e deposita o resultado numa fila para revisão.
  • Espelho de catálogo. Consome a API pública do site e mantém um cache local, então a ficha do contato já mostra o item sobre o qual ele perguntou.

Decisões técnicas

Simplificação que também resolveu privacidade. A primeira versão passava por um orquestrador de workflows na nuvem. Funcionava, mas era mais uma peça para manter e fazia conversa de cliente trafegar sem necessidade. Trocar por um script agendado deixou o sistema mais simples e mais privado ao mesmo tempo, que é a combinação rara. O orquestrador foi aposentado desse fluxo.

Saída de IA tratada como entrada não confiável. A classificação vem de um modelo, então a resposta é validada contra um conjunto fechado de estágios antes de tocar o banco. Se vier algo fora do esperado, a conversa fica sem classificação em vez de gravar um valor inventado. Prefiro campo vazio a dado errado que ninguém vai auditar depois. Na mesma linha, há travas em código, e não só no prompt: o modelo nunca fecha uma negociação sozinho e nunca move alguém para fora da própria categoria.

A anotação de quem atende vence a leitura da máquina. As notas ficam em tabela própria, append-only e datada. A IA lê essas notas com precedência sobre a conversa e nunca as reescreve. Antes disso, elas dividiam campo com o resumo automático e sumiam na análise seguinte, sem erro nenhum. São duas fontes com donos diferentes.

A varredura não escreve no funil. Analisar o histórico antigo podia bagunçar meses de classificação de uma vez, então ela grava numa fila separada e só um clique promove alguém para a base. Custo não era o obstáculo, curadoria era.

Deduplicação na fonte. Reexecutar é normal, seja por falha de rede ou máquina desligada. Sem chave de deduplicação por mensagem, cada reexecução duplicaria o histórico.

Como verifiquei

Reexecução sobre a mesma janela. A primeira carga real criou 23 contatos, atualizou 1 e não deu erro; a segunda passada, sobre o mesmo intervalo, foi 100% deduplicação.

Piloto antes de soltar a varredura no histórico inteiro. Rodei em 15 conversas e conferi uma a uma: 13 eram contatos legítimos. O caso difícil foi o que mais me interessou, porque a conversa mais longa da fila era de um fornecedor e o classificador acertou. Um filtro por tamanho de conversa, que era o caminho óbvio e barato, teria colocado justamente ela no topo.

Teste da trava de conexão nos dois sentidos. Aqui estava o problema mais perigoso do projeto: com o WhatsApp desconectado, a API continuava respondendo normalmente, servindo do cache dela. O sync rodaria, não encontraria mensagem nova e registraria “0 criados, 0 atualizados, 0 erros” por semanas. Sucesso aparente é pior que erro, porque ninguém vai investigar. Passei a checar o estado da conexão antes de qualquer coisa e a abortar quando ele não estiver aberto, tratando “não consegui verificar” também como falha. Testei desconectado, e abortou; reconectei, e importou 62 conversas.

Foi o terceiro caso da mesma família neste projeto. Antes dele, uma rotina de backup que falhava devolvendo só um código de erro que ninguém lia, e um nome de usuário mal configurado que invertia métricas de conversa sem nunca dar erro. Desde então, cada rotina agendada grava o resultado da própria execução em arquivo, para que o silêncio deixe de parecer sucesso.