# 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:** - [x] Constantes ROLE_LABELS, ROLE_COLORS, PRIORITY_LABELS - [x] Componente `NumberBadge` (role + prioridade visual) - [x] 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 - [x] 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`** ```sql 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:** ```typescript // 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`** ```sql 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: '' } ``` ### 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: ``` ### 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=&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` |