Pular para conteúdo

Runbook — Migrar novo cliente do N8N para o Gita Agents

Este runbook é o ponto de entrada para qualquer nova migração. Quando abrir nova conversa com o Claude pra um cliente novo, aponte ele pra este arquivo: ele lê, entende o padrão e executa.


Pré-requisitos

Antes de começar a migração, tenha em mãos:

Do cliente

  • Nome do cliente (ex: "Adriano Moleiro", "Janaina Ortiga") — vira client_name no banco
  • Slug base (ex: moleiro, janaina) — minúsculo, sem espaço
  • Produto principal (nome, preço, link de checkout Hotmart/TMB)
  • Brand voice (documento em ~/gita/data/products/<cliente>/)
  • Persona do público (dores, objeções, vocabulário)
  • Histórico de atendimento — idealmente acesso à inbox do cliente no Chatwoot (https://atendimento.gita.work), com account_id e inbox_id

Da infra

  • Instância Uazapi do cliente conectada e com token (painel https://grupogita.uazapi.com)
  • Chatwoot inbox configurada (ou outra integração equivalente)
  • API key Gemini (reusa a da Gita ou cria nova)
  • OPENAI_API_KEY já configurada globalmente no Easypanel (Whisper compartilhado)

Decisões prévias

  • Quantos fluxos distintos o atendimento humano do cliente cobre? (Ex: Janaina tem 3 — captação/conversão/suporte. Alguns clientes podem ter só 1 ou 2.)
  • O cliente tem lançamentos sazonais ou é perpétuo? (Afeta se precisa modo variável no prompt de captação.)
  • Cliente quer 1 número único ou múltiplos números? (Padrão Gita: 1 número + router.)

Padrão arquitetural (não reinventar)

Toda migração segue este padrão, validado na Janaina:

WhatsApp do cliente (1 número, Uazapi)
           │
           ▼
/webhook/whatsapp/<slug>-router
           │
           ├── Sticky routing (lead existe? mesmo tenant? usa)
           ├── Regex classifier (opener → slug alvo)
           └── Fallback (agente default, em modo triagem)
           │
           ▼
       <slug>-captacao | <slug>-conversao | <slug>-suporte

Nunca crie arquitetura diferente sem motivo explícito. Se o cliente tem 1 fluxo só, cria 1 agente sem router. Se tem 2, cria 2 agentes + router minimalista.

Ver arquitetura.md pra detalhes técnicos.


Passo 1 — Extrair contexto do atendimento humano (1-2h)

Objetivo: ter 500-1000 conversas reais do cliente pra alimentar prompt e few-shots. Sem isso, prompt vira suposição.

Se o cliente tem Chatwoot (preferível)

  1. Pegue CHATWOOT_URL, api_access_token (user do Chatwoot), account_id, inbox_id do cliente
  2. Script de referência: /tmp/chatwoot-janaina/list_conversations_v3.py e fetch_messages_v2.py (usados na Janaina). Adapte URL/token/inbox_id.
  3. Baixe todas as conversas com resposta de atendente (first_reply_created_at IS NOT NULL) dos últimos 3-6 meses
  4. Rode analyze_links.py pra extrair URLs que atendentes enviam (crítico pra evitar inventar link)
  5. Rode parse_dialogs.py pra gerar markdown legível + padrões (openers, objeções, vocabulário)

Se não tem Chatwoot

Peça ao cliente prints/exports das conversas reais. Mínimo 50 conversas por fluxo (captação/conversão/suporte). Se não houver dados, prompt vai ser baseado em brand voice + persona (menos preciso).

Saídas esperadas nesta fase

  • /tmp/chatwoot-<cliente>/dialogos_completos.md — diálogos legíveis
  • /tmp/chatwoot-<cliente>/padroes.md — openers, objeções, vocabulário, links
  • Lista de links oficiais confirmados com o cliente (checkout, suporte, eventos)

Passo 2 — Identificar fluxos e openers (30min)

Baseado no dump, classifique openers por frequência:

  • Openers de captação: material grátis, lista de espera, desafio, aulas, calendário
  • Openers de conversão: checkout travado, dúvida de pagamento, link da oferta
  • Openers de suporte: "sou aluno/a", "preciso de acesso", "material da aula"

Se um fluxo tem <10% das conversas, pode ser coberto como sub-fluxo dentro de outro (não precisa agente separado).

Gere uma tabela de regras de roteamento com regex candidatas. Ver exemplos em arquitetura.md.


Passo 3 — Escrever os prompts (2-3h)

Um arquivo por agente em ~/gita/agents/<cliente>/whatsapp/:

~/gita/agents/<cliente>/whatsapp/
  captacao.prompt.md
  conversao.prompt.md
  suporte.prompt.md
  few-shots-captacao.md
  few-shots-conversao.md
  few-shots-suporte.md

Modelo de prompt (copiar da Janaina como base): - ~/gita/agents/janaina-ortiga/whatsapp/*.prompt.md

Estrutura padrão (seções obrigatórias): 1. Identidade (nome do atendente, papel, quem NÃO é) 2. Estilo WhatsApp (balões curtos, oral, sem markdown visível) 3. Abertura padrão 4. Contexto do produto (fixo) 5. Variáveis dinâmicas ({{chave}} — editáveis via admin) 6. Fluxo de conversa 7. Tratamento de objeções (extraídas do dump) 8. Regra crítica sobre URLs (anti-alucinação) 9. Regra crítica sobre contato/telefone (anti-alucinação) 10. Quando escalar via reportar 11. Tools disponíveis 12. Regras invioláveis

Guia detalhado: edicao-prompts.md


Passo 4 — Criar migrations + inserir agentes no Supabase (1h)

Crie migrations seguindo numeração incremental: - supabase/migrations/0XX_insert_<cliente>_suporte.sql - supabase/migrations/0XX_insert_<cliente>_captacao.sql - supabase/migrations/0XX_insert_<cliente>_conversao.sql - supabase/migrations/0XX_insert_<cliente>_router.sql

Modelo: copiar 010_insert_janaina_suporte.sql, 011_insert_janaina_captacao.sql, 012_insert_janaina_router.sql e adaptar.

Campos obrigatórios do ai_agents: - slug, name, client_name (mesmo em todos os 4 agentes do cliente — crítico pra tenant isolation) - prompt_text com placeholders {{chave}} - model (gemini-2.5-pro pra captação/conversão, gemini-2.5-flash pra suporte/router) - channel: 'whatsapp', provider: 'uazapi' - provider_config: {api_url, gemini_api_key, token} - variables: JSON com todos placeholders do prompt preenchidos - filter_own_messages: 'fromMe'

Aplicar no banco:

# Ou copie/cole no SQL Editor do Supabase
# Ou use PostgREST via curl (ver memory project_janaina_whatsapp_agent.md)


Passo 5 — Adicionar slug do router no código (5min)

packages/engine/src/pipeline/orchestrator.ts:

const ROUTER_SLUGS = new Set(['janaina-router', '<cliente>-router'])

Adicione o slug. Esse é o único ponto de código hardcoded por cliente hoje.


Passo 6 — Criar regex de classificação (30min)

packages/engine/src/pipeline/router-classifier.ts:

Hoje as regras são globais (mesmo arquivo, todos os clientes). Isso funciona enquanto poucos clientes. Estrutura atual:

const RULES: Rule[] = [
  { slug: 'janaina-suporte', regex: /.../ },
  { slug: 'janaina-conversao', regex: /.../ },
  { slug: 'janaina-captacao', regex: /.../ },
]

Adicione as regras do novo cliente:

const RULES: Rule[] = [
  // Janaina
  { slug: 'janaina-suporte', regex: /.../ },
  ...
  // <cliente>
  { slug: '<cliente>-suporte', regex: /.../ },
  { slug: '<cliente>-conversao', regex: /.../ },
  { slug: '<cliente>-captacao', regex: /.../ },
]

const DEFAULT_BY_TENANT: Record<string, string> = {
  'Janaina Ortiga': 'janaina-captacao',
  '<client_name>': '<cliente>-captacao',
}

Atenção: quando houver 3+ clientes, refatorar pra regras por tenant (evita colisão entre regex de clientes diferentes). Hoje é aceitável só com 1-2.


Passo 7 — Compile check + commit + push (10min)

cd ~/projetos/gita-agents
npx tsc --noEmit -p packages/engine/tsconfig.json
npx tsc --noEmit -p packages/admin/tsconfig.json

git add <arquivos>
git commit -m "feat(agents): migração cliente <nome>"
git push origin main

Easypanel redeploya automaticamente.


Passo 8 — Configurar webhook da Uazapi (5min)

No painel Uazapi, instância do cliente:

URL: https://gita-engine-gita-agents.ewzc9p.easypanel.host/webhook/whatsapp/<cliente>-router
Método: POST
Eventos: messages (obrigatório)
Status: Ativo

Passo 9 — Testar end-to-end (30min)

Testes obrigatórios

# Teste 1: classificação de 3 openers distintos
curl -X POST "https://gita-engine-gita-agents.ewzc9p.easypanel.host/webhook/whatsapp/<cliente>-router" \
  -H "Content-Type: application/json" \
  -d '{
    "message": {
      "chatid": "[email protected]",
      "messageType": "conversation",
      "text": "<opener de captação do dump>",
      "messageid": "TEST_1",
      "messageTimestamp": TIMESTAMP,
      "fromMe": false
    },
    "chat": {"name": "Teste"},
    "BaseUrl": "https://grupogita.uazapi.com",
    "token": "<token da instância>"
  }'

Verificar em agent_logs: - router.classified com resolvedSlug: <cliente>-captacao|conversao|suporte (matching correto) - tenant: <client_name> (isolamento funcionando) - pipeline.completed (resposta gerada) - send.success (Uazapi recebeu)

Teste 2 — áudio real (transcrição + Uazapi download)

Mande áudio de voz real pelo WhatsApp do cliente pro número configurado. Verificar: - uazapi.download_success (base64 baixado) - transcribe.openai.success (Whisper transcreveu) - pipeline.completed

Teste 3 — tenant isolation

Se o número de teste tem histórico em outros clientes (outros client_name), confirme que router NÃO delega pra agente de outro cliente. Ver logs router.sticky e tenant: <client_name>.

Teste 4 — conversa em balões

Mande mensagem real pelo WhatsApp. Confirme: - Resposta chega em múltiplos balões (chunker funcionando) - Delay perceptível entre balões (simula digitação) - Sem {{placeholders}} literais


Passo 10 — Ativar em produção (5min)

Se todos os testes passaram: - Avisar equipe de atendimento do cliente que IA está ativa - Monitorar logs nas primeiras 24h (agent_logs filtrado por tenant) - Humano pode pausar IA em conversa específica via label pausar-ia no Chatwoot


Pós-migração — Operação contínua (cliente sozinho)

Depois de migrado, o usuário opera sozinho pelo admin (packages/admin em Easypanel):

  • Editar variáveis (ofertas, links, condição especial) — sem precisar de dev
  • Trocar modo_captacao quando lançamento abre/fecha
  • Ligar/desligar agentes
  • Ver leads, logs, conversas

Chama dev quando: - Mudar prompts estrutural mente (não só variáveis) — idealmente ainda com dev pra revisar - Bug no pipeline - Nova regex de classificação - Nova integração (novo CRM, outro canal) - Novo fluxo que não existia (ex: recuperação pós-venda) - Migrar outro cliente (abre nova conversa com Claude apontando pra este runbook)


Checklist visual

  • Dados do cliente coletados
  • Contexto extraído do Chatwoot (dialogos + padrões)
  • Links oficiais confirmados com cliente
  • Fluxos identificados (captação/conversão/suporte ou variantes)
  • Prompts escritos em ~/gita/agents/<cliente>/whatsapp/
  • Few-shots com diálogos reais
  • Migrations criadas
  • Agentes inseridos no Supabase (conferir com SELECT)
  • ROUTER_SLUGS atualizado no orchestrator
  • Regras de regex adicionadas no router-classifier
  • TypeScript compila limpo (npx tsc --noEmit)
  • Commit + push
  • Redeploy confirmado no Easypanel
  • Webhook Uazapi apontado pro novo router
  • Testes curl (3 openers distintos) passaram
  • Teste áudio real passou
  • Tenant isolation verificado
  • Cliente comunicado e operando

Tempo estimado total

  • Cliente com Chatwoot organizado: 6-10h
  • Cliente sem dados históricos: 4-6h (prompt baseado só em brand voice)
  • Cliente com fluxo único (sem router): 3-4h

Nunca tentar fazer em <3h — pular etapas é onde bugs entram.