Files
mercado.clube67.com/docs/analise-boilerplate-v22.md

384 lines
16 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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.**