Files

13 KiB
Raw Permalink Blame History

NewWhats v2.2 — Plano Real (pós-análise do cérebro)

Documento gerado em 2026-04-18 após análise completa do motor, satélite, proxy e ProtocolEngine.
Base: código real de newwhats.local + alemaoconveniencias.local/sistema-nuvem/plugins/newwhats/.


1. Estado atual — O que já existe (v2.1)

Motor (newwhats.local:8008)

Componente Rota interna Status
Agents CRUD GET/POST/PUT/DELETE /api/secretaria/agents operacional
Brain Nodes GET/POST/PUT/DELETE /api/secretaria/nodes operacional
Conversations GET/POST/PATCH /api/secretaria/conversations operacional
Chat (brain) POST /api/secretaria/conversations/:id/chat operacional
Finalizar protocolo POST /api/secretaria/conversations/:id/finalize operacional
Calendar GET/POST /api/secretaria/calendar operacional
sec_numbers GET/POST/PUT/DELETE /api/secretaria/numbers tabela + rotas ok
WebSocket stream UPGRADE /api/ext/v1/stream operacional
Sessions QR GET /api/ext/v1/sessions/:id/qr operacional

ProtocolEngine (cérebro) — arquitetura real

Sistema prompt = persona + knowledge + rules + calendar + escalation
                (nós ativos, ordenados por sort_order)

Economia de tokens:
  context_window = 8  → carrega apenas últimas 8 mensagens
  summarize a cada 10 trocas → atualiza conversation.summary
  system prompt nunca cresce — sempre reconstruído do zero

Fallback chain de providers:
  1. openai (gpt-4o-mini) — se openai_key configurado
  2. gemini (gemini-2.0-flash) — se gemini_key configurado
  3. anthropic (claude-3-5-haiku) — se anthropic_key configurado
  4. ollama (llama3) — sempre disponível (local)

Persona pode override o model via node_model (por nó)

Satélite (sistema-nuvem) — o que existe hoje

/api/nw/v1/*           → proxy REST para /api/ext/v1/*  ✅
/api/nw/v1/stream      → proxy WS                       ✅
/api/webhook/newwhats  → webhook-receiver.js             ✅ (só loga)
/api/admin/nw/sync-knowledge      → syncKnowledge()      ✅ (corrigido)
/api/admin/nw/sync-knowledge/preview → buildKnowledgeText() ✅

Rotas Alemão expostas ao motor (/nw/*):
  GET /nw/cliente?telefone=    → user + tickets + clubes + candidaturas ✅
  GET /nw/clube?cpf=           → memberships + dependentes + parceiros  ✅
  GET /nw/candidaturas?cpf=    → candidaturas com status PT             ✅
  GET /nw/parceiros?clube=slug → parceiros de um clube                  ✅
  GET /nw/vagas                → vagas abertas formatadas               ✅
  GET /nw/sorteio              → sorteio ativo + contagem               ✅
  GET /nw/sorteio/tickets?tel= → tickets do cliente                     ✅
  GET /nw/promocoes            → promoções ativas                       ✅

Knowledge Node — situação atual

Agente:  Ana — Atendente Virtual
Node ID: ed968e6c-a031-44d2-8f5b-1ab4e6f55075
Chars:   1895
Última sync: 2026-04-18T23:28:46Z

Conteúdo real (dados do banco):
  - Clube Odonto (planos + parceiros)
  - Clube Médico (planos + parceiros)
  - Sorteio ativo (preço + disponíveis)
  - Promoções ativas
  - Vagas abertas
  - Atendimento geral

O que NÃO existe ainda

  • Auto-resposta (webhook só loga, nunca envia)
  • Lookup automático de dados do cliente ao receber mensagem
  • UI de gerenciamento de números no admin (Frente 1 em progresso)
  • Botão "Sincronizar Knowledge" no painel
  • Escalação para humano com notificação
  • Métricas de atendimento IA
  • Modo semi-auto (IA sugere, humano confirma)

2. Frentes v2.2

Frente 1 — Gerenciamento de Números no Admin UI

Onde: admin-app/src/pages/WhatsAppInbox.tsx

O que fazer:

  • Constantes ROLE_LABELS, ROLE_COLORS, PRIORITY_LABELS
  • Componente NumberBadge (role + prioridade visual)
  • Modal AddNumberModal — 2 etapas:
    • Step 1: preenche label, role, área, prioridade, notas
    • Step 2: chama NwService.createSession(label)NwService.saveNumber(...) → poll QR a cada 2s
  • Modal EditNumberModal — edita metadados + toggle ativo/inativo
  • Estado + fetchNumbers() no componente principal
  • Botão "+ Novo Número" no header da aba Sessões
  • Session cards: badges de categoria/prioridade (cruzar sessions × numbers por instance_id)
  • Ações por sessão: botão Editar (abre EditModal) + Excluir (confirm → deleteSession + deleteNumber)

Fluxo de dados:

NwService.getNumbers() → /api/nw/v1/secretaria/numbers
  → proxy → /api/ext/v1/secretaria/numbers
    → motor → SELECT sec_numbers ORDER BY priority, label

Cruzamento visual: session.id === number.instance_id

Lógica de fallback (display):

Ao mostrar sessões ordená-las por: priority ASC (números sem metadado = priority 99)
Badge role: azul=notificacoes | laranja=entregas | roxo=suporte | rosa=marketing | cinza=geral
Badge prioridade: verde=Principal | amarelo=Fallback | cinza=Terciário+

Frente 2 — Auto-resposta Inteligente

Onde: sistema-nuvem/plugins/newwhats/webhook-receiver.js
Depende de: Frente 1 concluída (sec_numbers deve ter roles configurados)

Princípio de funcionamento:

Motor dispara webhook → POST /api/webhook/newwhats
  evento: message.new, fromMe: false

Satélite:
  1. Verifica HMAC (já existe)
  2. Lookup sec_numbers pelo instanceId → obtém role
  3. Se role == 'notificacoes' → NUNCA responder (só logar)
  4. Se role == 'suporte' | 'geral' | 'marketing' | 'entregas':
       a. Lookup ou cria conversa no motor: POST /api/secretaria/conversations
       b. Chama brain: POST /api/secretaria/conversations/:id/chat { message }
       c. Envia resposta: POST /api/ext/v1/inbox/:chatId/send { text: reply }
       d. Atualiza nw_event_logs com status='replied'

Modos de operação (configurável por plugin_config):

auto_reply_mode:
  off        → nunca responde (comportamento atual)
  assistido  → IA sugere na inbox, humano confirma antes de enviar
  automatico → IA responde direto, sem intervenção humana

Throttle / anti-loop:

- Nunca responder se fromMe = true
- Cooldown: não responder ao mesmo chatId por 30s após última resposta automática
- Máximo: 5 respostas automáticas por conversa antes de escalar
- Horário: respeitar horário de atendimento (plugin_config: auto_reply_hours)

Tabela nova: nw_auto_replies

CREATE TABLE nw_auto_replies (
  id          SERIAL PRIMARY KEY,
  instance_id VARCHAR(128)  NOT NULL,
  chat_id     VARCHAR(128)  NOT NULL,
  conv_id     UUID,                        -- sec_conversations.id no motor
  reply       TEXT,
  status      VARCHAR(20)   DEFAULT 'sent',-- sent | failed | pending_confirm
  created_at  TIMESTAMPTZ   DEFAULT NOW()
);

Frente 3 — Botão "Sincronizar Knowledge" no Painel

Onde: admin-app/src/pages/WhatsAppInbox.tsx (aba Sessões) + admin-app/src/services/api.ts

O que fazer:

// api.ts — adicionar ao NwService:
syncKnowledge: () =>
  api.post('/admin/nw/sync-knowledge').then(r => r.data),
previewKnowledge: () =>
  api.get('/admin/nw/sync-knowledge/preview').then(r => r.data),

UI:

  • Botão Sincronizar IA com ícone Brain/Zap no header da aba Sessões (ao lado do +Novo Número)
  • Estado: idle → syncing (spinner) → ok (checkmark verde 2s) → idle
  • Toast: " Base sincronizada — 1895 chars — Ana atualizada"
  • Botão Preview (expandable) mostra o texto que será enviado ao motor

Quando disparar automaticamente (futuro):
Webhook interno: quando admin salva promoção, clube, vaga → dispara syncKnowledge() em background.


Frente 4 — Escalação para Humano

Onde: webhook-receiver.js + novo endpoint no admin

Gatilhos de escalação (detectados pelo brain):

  • Palavra-chave na resposta da IA: [ESCALAR], [HUMANO], transferir para atendente
  • Mais de 5 trocas sem resolução (contador na conversa)
  • Cliente digita: "falar com atendente", "humano", "responsável"

Fluxo:

Brain retorna resposta com [ESCALAR] → satélite detecta
  → marca conversa: status='escalated'
  → INSERT nw_escalations (chat_id, conv_id, reason, created_at)
  → badge vermelho na inbox do admin (unread count especial)
  → IA para de responder automaticamente para esse chatId
  → Humano assume via inbox normal

Tabela nova: nw_escalations

CREATE TABLE nw_escalations (
  id          SERIAL PRIMARY KEY,
  instance_id VARCHAR(128)  NOT NULL,
  chat_id     VARCHAR(128)  NOT NULL,
  conv_id     UUID,
  reason      TEXT,
  resolved    BOOLEAN DEFAULT FALSE,
  resolved_by VARCHAR(128),
  created_at  TIMESTAMPTZ DEFAULT NOW(),
  resolved_at TIMESTAMPTZ
);

Frente 5 — Métricas de Atendimento IA

Onde: sistema-nuvem/server.js + novo card no Dashboard admin

KPIs:

- Total de mensagens recebidas (nw_event_logs)
- Total respondidas pela IA (nw_auto_replies)
- Taxa de auto-atendimento: respondidas / recebidas
- Escalações hoje / semana
- Tempo médio de resposta (created_at do webhook → created_at do reply)
- Distribuição por role (suporte X entregas X geral)
- Top 5 sessões por volume

Endpoint:

GET /api/admin/nw/metrics?period=7d
→ { total_in, total_replied, escalations, avg_response_ms, by_role[], top_sessions[] }

3. Sequência de implementação recomendada

Semana 1:
  [ ] Frente 1 — concluir WhatsAppInbox.tsx (estado + UI badges + botões)
  [ ] Frente 3 — botão SyncKnowledge no painel (rápido, alto impacto)

Semana 2:
  [ ] Frente 2 — webhook auto-resposta (modo 'automatico')
      → criar nw_auto_replies
      → integrar brain via fetch interno
      → throttle + anti-loop

Semana 3:
  [ ] Frente 4 — escalação (detectar [ESCALAR] na resposta + tabela + badge)
  [ ] Frente 5 — métricas básicas (endpoint + card dashboard)

4. Dependências técnicas críticas

4.1 O motor precisa de webhook configurado para auto-resposta funcionar

Motor → POST /api/webhook/newwhats (satélite)
Configurar via: POST /api/ext/v1/webhooks {
  url: 'https://alemaoconveniencias.com.br/api/webhook/newwhats',
  events: ['message.new', 'session.status'],
  secret: '<webhook_secret do plugin_configs>'
}

4.2 Rotas do motor que o satélite usa internamente (sem auth)

/api/secretaria/conversations          → busca/cria conversa por chatId
/api/secretaria/conversations/:id/chat → chama brain
/api/secretaria/agents                 → lista agentes ativos

4.3 Rota que satélite usa com auth (api key) para enviar resposta

POST /api/ext/v1/inbox/:chatId/send { text }
Header: x-nw-key: <integration_key>

4.4 Lookup chatId → conversa existente

O motor hoje não tem endpoint GET /api/secretaria/conversations?chatId=.
Precisará ser adicionado no motor ou o satélite usa sec_conversations via lookup próprio.
Solução recomendada: adicionar no motor:

GET /api/secretaria/conversations?chat_id=<chatId>&create=true
→ retorna conversa existente ou cria nova automaticamente

5. Arquivos impactados

Arquivo Frente Mudança
plugins/newwhats/webhook-receiver.js F2 auto-resposta + throttle
plugins/newwhats/sync-knowledge.js F3 sem mudança (já funcional)
database/database.js F2+F4 CREATE TABLE nw_auto_replies, nw_escalations
sistema-nuvem/server.js F3+F5 /admin/nw/metrics
admin-app/src/pages/WhatsAppInbox.tsx F1+F3 estado numbers + modais + badges + sync btn
admin-app/src/services/api.ts F3 NwService.syncKnowledge + previewKnowledge
newwhats.local/plugins/secretaria/routes.ts F4 GET /conversations?chat_id=

6. Riscos e mitigações

Risco Impacto Mitigação
IA responde cliente errado (loop) Alto fromMe=true nunca dispara; cooldown 30s; max 5 auto/conversa
Knowledge desatualizado Médio Sync automático ao salvar promoção/vaga; preview antes do sync
Motor fora do ar Alto Fallback chain (openai→gemini→anthropic→ollama); modo off por padrão
Webhook sem HMAC Médio Em dev aceita sem secret + aviso; em prod bloqueia
chatId não mapeado para conversa Médio Criar conversa automaticamente no primeiro contato
QR code expirado no modal Baixo Poll a cada 2s, aviso "QR expira em 60s"
Escalação ignorada Alto Badge vermelho persistente + email de alerta (futuro)

7. Glossário técnico deste projeto

Termo Significado
Motor newwhats.local:8008 — instância independente do NewWhats
Satélite sistema-nuvem do alemão — proxy + regras de negócio
ProtocolEngine Classe brain.ts — core de IA stateful com nós
Brain Node Registro em sec_brain_nodes (persona/knowledge/rules/calendar/escalation)
sec_numbers Tabela do motor com metadados dos números (role, priority, area)
nw_event_logs Tabela do satélite com todos os eventos recebidos via webhook
integration_key Chave usada no header x-nw-key para rotas /api/ext/v1/*
motorUrl URL do motor, lida de plugin_configs WHERE key='newwhats_url'
sync-knowledge Script que lê BD alemão e atualiza o nó knowledge da Ana
context_window Número de mensagens recentes passadas ao LLM (padrão: 8)
protocol_number Identificador único da conversa formato DDMMAAHHmmss