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_nameno 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), comaccount_ideinbox_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_KEYjá 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
modovariá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)¶
- Pegue
CHATWOOT_URL,api_access_token(user do Chatwoot),account_id,inbox_iddo cliente - Script de referência:
/tmp/chatwoot-janaina/list_conversations_v3.pyefetch_messages_v2.py(usados na Janaina). Adapte URL/token/inbox_id. - Baixe todas as conversas com resposta de atendente (
first_reply_created_at IS NOT NULL) dos últimos 3-6 meses - Rode
analyze_links.pypra extrair URLs que atendentes enviam (crítico pra evitar inventar link) - Rode
parse_dialogs.pypra 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_captacaoquando 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_SLUGSatualizado 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.