# Análise: Boilerplate Secretária Virtual × NewWhats v2.2

> Análise técnica real — comparando o boilerplate enviado com a arquitetura atual do motor NewWhats
> e identificando o que cada Frente v2.2 ganha (ou não) com esse padrão.

---

## TL;DR — Veredicto em 3 linhas

O boilerplate implementa **Tool Use** (IA chama funções em tempo real).
O motor atual implementa **Knowledge Node** (dados pré-carregados no system prompt).
**São complementares, não concorrentes — e o motor está faltando o Tool Use.**

---

## 1. O que o boilerplate faz certo

### ✅ Segurança: patientId vem da sessão, nunca da IA
```javascript
// patientId sempre da sessão — a IA nunca pode sobrescrever isso
async function executeTool(toolName, toolInput, patientId) { ... }

// No endpoint:
const patient = req.session.patient  // ← fonte da verdade
const { reply } = await runAgentLoop(messages, patient)
```
**Impacto:** A IA pode pedir "cancele o agendamento 999" de outro paciente, mas o
`WHERE patient_id = ?` garante que só o paciente da sessão é afetado.
Isso é o padrão correto de segurança com tool use.

### ✅ IA nunca toca o banco — executa via função intermediária
A IA solicita `{ name: 'cancel_appointment', input: { appointment_id: 42 } }`.
O servidor executa. A IA nunca tem credenciais, nunca faz queries diretas.

### ✅ Agent loop: multi-step tool chains
```javascript
while (true) {
  const response = await anthropic.messages.create({ tools: TOOLS, messages })
  if (response.stop_reason === 'end_turn') return reply
  if (response.stop_reason === 'tool_use') {
    // executa todas as tools solicitadas
    // devolve resultados e volta ao loop
    messages.push({ role: 'user', content: toolResults })
  }
}
```
Isso permite: `list_doctors` → apresenta ao usuário → `check_availability` → `create_appointment`
em uma única conversa, sem o usuário perceber que são 3 chamadas de API.

### ✅ Confirmação antes de ação destrutiva
O system prompt exige confirmação explícita antes de cancelar.
E o `cancel_appointment` re-verifica `WHERE patient_id = ?` como segunda camada de segurança.

### ✅ System prompt como "contrato" da IA
As regras absolutas são claras, específicas e mensuráveis:
- "NUNCA confirma agendamento sem check_availability" 
- "NUNCA discute temas fora do escopo"
- "NUNCA pede CPF ou dados bancários"

---

## 2. O que está faltando / gaps do boilerplate

### ❌ Zero prompt caching
Cada request reconstrói o system prompt + histórico do zero.
Com Claude, o system prompt idêntico entre requests ativa cache automático de prefix.
O boilerplate não usa `cache_control: { type: "ephemeral" }` nos blocos de sistema.

**Custo real:** Um system prompt de 800 tokens × 100 conversas/dia = 80.000 tokens de input pagos
inteiramente toda vez. Com cache, ~90% vira cache hit (5× mais barato).

### ❌ Histórico em RAM (express-session em memória)
```javascript
req.session.messages = updatedMessages.slice(-50)
```
- Reiniciar o servidor = perder todas as conversas ativas
- Não escala horizontalmente (sem Redis/banco)
- Slice(-50) é bruto — 50 mensagens podem ser 40.000 tokens se forem longas

**O motor faz certo:** persiste em `sec_conversations` + `sec_messages` no banco.

### ❌ Sem fallback de provider
Se a API key da Anthropic falhar → a secretária morre.
O motor tem a chain `openai → gemini → anthropic → ollama`.

### ❌ Sem limite de iterações no agent loop
```javascript
while (true) { ... }  // pode rodar para sempre
```
Se a IA entrar em loop (chama tool → recebe erro → chama tool de novo), nunca sai.
Falta: `if (iteration > MAX_ITER) throw new Error('Loop de ferramentas excedido')`

### ❌ Sem multi-tenant
Uma instância, um banco de dados de clínica. Não serve para SaaS.
O motor tem `tenantId` em todas as queries e usa `ext_chat_id = {tenantId}:{chatId}`.

### ❌ Sem sumarização automática
Histórico só cresce (limitado por slice bruto).
O motor sumariza a cada 10 trocas e descarta mensagens antigas, mantendo o contexto leve.

### ❌ Sem webhook — só polling request/response
O boilerplate assume que o humano digita no browser.
Para WhatsApp, o fluxo é invertido: o motor recebe webhook e precisa
chamar o runAgentLoop de forma assíncrona, depois enviar a resposta proativamente.

---

## 3. A diferença arquitetural central

```
BOILERPLATE                          MOTOR ATUAL (newwhats)
─────────────────────────────        ──────────────────────────────
Pattern: Tool Use (runtime)          Pattern: Knowledge Node (RAG)

IA decide quando buscar dados  →     Dados pré-carregados no prompt
Queries em tempo real por conv →     sync-knowledge.js roda periodicamente
+1-3 chamadas API por mensagem →     Contexto maior, 0 chamadas extras
Dados sempre frescos          →     Dados podem ter até 1h de delay
Conhece dados do CLIENTE      →     Conhece catálogo GERAL da empresa
```

### O que cada abordagem responde bem

| Pergunta do cliente | Knowledge Node | Tool Use |
|---|---|---|
| "Quais planos do clube odonto?" | ✅ (sync-knowledge) | funciona, mas desnecessário |
| "Quais parceiros aceitam meu plano?" | ✅ (sync-knowledge) | funciona |
| "Tenho promoção ativa?" | ✅ (sync-knowledge) | funciona |
| **"Quais são meus números da rifa?"** | ❌ **não sabe** | ✅ lookup por telefone |
| **"Meu plano está ativo?"** | ❌ **não sabe** | ✅ lookup por CPF |
| **"Minha candidatura foi aprovada?"** | ❌ **não sabe** | ✅ lookup por CPF |
| **"Quando é meu próximo vencimento?"** | ❌ **não sabe** | ✅ lookup por CPF |

**Conclusão:** O motor conhece o catálogo do Alemão, mas é cego para dados individuais do cliente.
O boilerplate mostra exatamente o que falta.

---

## 4. Mapeamento por Frente v2.2

### Frente 1 — Gerenciamento de Números ← **não usa o boilerplate**
O boilerplate é para chat de clínica, não para gerenciar instâncias WhatsApp.
Porém o padrão de segurança (`phone from webhook, never from AI`) é o mesmo princípio
que usaremos na auto-resposta: o número de origem vem do evento, nunca da IA.

**Status:** em implementação (modais AddNumber/EditNumber já adicionados no WhatsAppInbox.tsx).

---

### Frente 2 — Auto-resposta ← **o boilerplate É o que falta**

O `runAgentLoop` + `executeTool` é exatamente o núcleo que o webhook-receiver precisa.

**Hoje:**
```javascript
// webhook-receiver.js — handleMessageNew
async function handleMessageNew(data) {
  await execute(`INSERT INTO nw_event_logs ...`)
  // FIM — nunca responde
}
```

**Com padrão do boilerplate:**
```javascript
async function handleMessageNew(data) {
  await execute(`INSERT INTO nw_event_logs ...`)

  const msg = data.message
  if (msg.fromMe) return  // nunca responder a si mesmo

  // 1. Verifica role do número (notificacoes = nunca auto-responde)
  const numInfo = await getNwNumber(data.instanceId)
  if (!numInfo || numInfo.role === 'notificacoes') return
  if (auto_reply_mode === 'off') return

  // 2. Identifica cliente pelo telefone
  const phone = data.chatId.replace('@s.whatsapp.net', '')
  const clientCtx = await fetch(`http://localhost:3000/nw/cliente?telefone=${phone}`)

  // 3. Busca/cria conversa no motor — usa ext_chat_id = chatId
  const conv = await motorFetch('POST', '/secretaria/ask', {
    chatId:      data.chatId,
    message:     msg.body,
    contactName: msg.pushName ?? phone,
  })
  // motor.brain.chat() já executa o agent loop internamente

  // 4. Envia resposta via motor
  await motorFetch('POST', `/inbox/${data.chatId}/send`, { text: conv.reply }, useIntegKey)
}
```

**Mas há um problema crítico detectado:**
O `/secretaria/ask` do motor chama `brain.chat()` que é **somente texto** —
não tem tool use. A IA não pode buscar dados do cliente em tempo real.

Quando o cliente perguntar "meus números da rifa" a Ana vai responder
"acesse o site" porque não tem como saber.

---

### Frente 3 — Sync Knowledge ← **complementar ao boilerplate**

O boilerplate usa o banco direto (tool `list_doctors`).
O sync-knowledge.js usa o banco para construir o system prompt.

São estratégias diferentes para o mesmo problema:

```
Boilerplate approach:           sync-knowledge approach:
list_doctors como tool    →     médicos/parceiros no knowledge node
resultado por request     →     resultado atualizado de hora em hora
+tokens de tool call      →     +tokens no system prompt
sempre atualizado         →     atraso de até 1h
```

**Para o Alemão, o melhor é:**
- `sync-knowledge` para catálogo (planos, parceiros, promoções, vagas) ← já feito
- tool use para dados do cliente (tickets, memberships, candidaturas) ← falta

**Status:** botão de sync no admin ainda não implementado (simples, próximo passo).

---

### Frente 4 — Escalação ← **o boilerplate mostra o anti-padrão**

O boilerplate usa system prompt para definir regras de escalação:
```
"NUNCA realize ações destrutivas sem confirmação"
```

O motor já tem o nó `escalation` com o mesmo conceito, porém mais estruturado.

O que falta no motor (e o boilerplate não resolve) é o **lado operacional**:
detectar `[ESCALAR]` na resposta da IA e notificar o humano no painel.

O boilerplate não implementa escalação porque assume atendimento 100% humano de qualquer forma
(login no browser = humano já presente). Para WhatsApp assíncrono, escalação é crítica.

---

### Frente 5 — Métricas ← **o boilerplate tem zero**

Apenas `console.log` dos tool calls. Sem persistência, sem dashboard.

O `nw_event_logs` já é a base certa para métricas.
Quando a auto-resposta for implementada, adicionar `nw_auto_replies` resolve as métricas.

---

## 5. O gap mais crítico identificado: Motor sem Tool Use

O motor atual em `brain.ts`:
```typescript
private async callAI(...): Promise<string> { ... }
// Retorna sempre string — sem suporte a tool_use
```

Todos os providers são chamados sem `tools: []`. O motor nunca vai receber
`stop_reason: 'tool_use'` porque nunca passa ferramentas para a IA.

**Para a Ana ser realmente útil individualmente, o motor precisa evoluir:**

```typescript
// Hoje: brain.chat() → string
const reply = await brain.chat(convId, message)

// Precisaria: brain.chat() com tool loop (como o boilerplate)
interface Tool { name: string; description: string; input_schema: object }
interface ToolExecutor { (name: string, input: any, context: ChatContext): Promise<any> }

async chat(convId: string, message: string, tools?: Tool[], executor?: ToolExecutor): Promise<string>
```

As ferramentas que a Ana precisaria para o Alemão:
```javascript
const ALEMAO_TOOLS = [
  {
    name: 'buscar_cliente',
    description: 'Busca dados do cliente pelo telefone. Retorna tickets da rifa, clubes ativos e candidaturas.',
    input_schema: { type: 'object', properties: { telefone: { type: 'string' } }, required: ['telefone'] }
  },
  {
    name: 'buscar_clube',
    description: 'Retorna plano ativo do cliente, dependentes e parceiros credenciados.',
    input_schema: { type: 'object', properties: { cpf: { type: 'string' } }, required: ['cpf'] }
  },
  {
    name: 'buscar_candidaturas',
    description: 'Retorna candidaturas do cliente com status atual.',
    input_schema: { type: 'object', properties: { cpf: { type: 'string' } }, required: ['cpf'] }
  },
]
// Executor: chama as rotas /nw/* já existentes no satélite
```

Essas rotas `/nw/*` **já existem** no satélite. O problema é que a Ana não consegue chamá-las.

---

## 6. Resumo executivo — O que fazer com isso

### Aproveitar do boilerplate para v2.2

| O quê | Onde aplicar | Prioridade |
|---|---|---|
| Agent loop (while + tool_use) | webhook-receiver.js (Frente 2) | 🔴 Alta |
| Security pattern (phone from webhook only) | webhook-receiver.js (Frente 2) | 🔴 Alta |
| Max iterations guard | webhook-receiver.js anti-loop | 🔴 Alta |
| Tool definitions ALEMAO_TOOLS | motor brain.ts (evolução futura) | 🟡 Média |
| Confirmação antes de ação destrutiva | já no nó escalation do motor | ✅ Já existe |

### NÃO aproveitar do boilerplate

| O quê | Motivo |
|---|---|
| express-session em memória | motor já usa banco (correto) |
| Histórico slice(-50) | motor já sumariza (correto) |
| Single provider | motor já tem fallback chain (correto) |
| MySQL | projeto usa PostgreSQL |
| Sem multi-tenant | motor já tem tenantId (correto) |

### Sequência correta para Frente 2

```
1. webhook-receiver.js: verificar role do número (notificacoes = skip)
2. webhook-receiver.js: chamar POST /api/secretaria/ask (motor)
   → motor já faz: lookup/cria conversa, chama brain.chat()
   → brain.chat() responde com texto (sem tool use por ora)
3. webhook-receiver.js: enviar reply via POST /api/ext/v1/inbox/:chatId/send
4. Resultado: Ana responde perguntas gerais (catálogo) mas não dados individuais

5. (Evolução): adicionar tool use no brain.ts
   → Ana passa a responder "seus tickets são 003, 047, 112"
   → Usando as rotas /nw/* já existentes como tool executors
```

A Frente 2 básica (sem tool use) já resolve 70% dos casos de uso.
A evolução com tool use resolve os 30% restantes (dados pessoais do cliente).

---

## 7. Arquitetura alvo — Sistema completo

```
                    ┌─────────────────────────────────────┐
                    │         WhatsApp (cliente)           │
                    └──────────────┬──────────────────────┘
                                   │ mensagem
                                   ▼
                    ┌─────────────────────────────────────┐
                    │      Motor NewWhats (webhook)        │
                    │  POST /api/webhook/newwhats          │
                    └──────────────┬──────────────────────┘
                                   │
                    ┌──────────────▼──────────────────────┐
                    │      webhook-receiver.js             │
                    │  1. verifica role do número          │
                    │  2. verifica auto_reply_mode         │
                    │  3. cooldown / anti-loop             │
                    └──────────────┬──────────────────────┘
                                   │
                    ┌──────────────▼──────────────────────┐
                    │   POST /api/secretaria/ask           │
                    │   Motor: cria/retoma conversa        │
                    │   brain.chat(convId, message)        │
                    └──────────────┬──────────────────────┘
                                   │ [futuro: tool use]
          ┌───────────────────────►│◄───────────────────────┐
          │                        │                        │
┌─────────▼──────────┐   ┌────────▼──────────┐   ┌────────▼──────────┐
│  Knowledge Nodes   │   │  /nw/cliente       │   │  /nw/clube        │
│  (sync-knowledge)  │   │  /nw/candidaturas  │   │  /nw/sorteio      │
│  planos, parceiros │   │  /nw/vagas         │   │  /nw/promocoes    │
│  promoções, vagas  │   │  (dados pessoais)  │   │  (dados pessoais) │
└────────────────────┘   └───────────────────┘   └───────────────────┘
         (RAG)                    (Tool Use)              (Tool Use)
     dados estáticos           dados individuais        dados individuais
```

O boilerplate mostra como construir a metade direita do diagrama.
O motor já tem a metade esquerda funcionando.
**Frente 2 une as duas.**
