# Auditoria do workflow “Milla Agent - Millano Madeira (n8n)”

Data da análise: 07/09/2026 (horário de Brasília)  
Workflow: `kmQGGQ7gX8ZqTk2P`  
Versão ativa observada: `5d67bbbd-6616-49ea-88b4-78b39b69e1a3`  
Estado: ativo  
Escopo: leitura do workflow e das 23 execuções disponíveis pela API pública do n8n. Nenhum nó foi alterado ou executado durante esta auditoria.

## 1. Resumo executivo

O fluxo já tem uma boa intenção de atendimento: recebe mensagens da Evolution API, normaliza texto/áudio/imagem, agrupa mensagens consecutivas, usa memória por telefone, orienta a Milla com um prompt comercial detalhado e divide a resposta em mensagens de WhatsApp.

No entanto, ele ainda não está seguro nem confiável para operação comercial sem correções. Os pontos mais graves são:

1. `notify_vendedor` e `schedule_followup` apontam para IDs-placeholder. Nas três tentativas de encaminhamento observadas, `notify_vendedor` falhou com “Workflow does not exist”, mas a Milla informou ao cliente que o vendedor continuaria o atendimento.
2. Uma chave da Evolution API está gravada diretamente no nó `Credenciais` e também aparece nos dados de execução. A chave do n8n compartilhada para esta auditoria também deve ser rotacionada.
3. O webhook não mostra autenticação e o nó `Busca Quoted` usa `server_url` e `apikey` recebidos no corpo para fazer `fetch`. Isso permite requisições para destinos controlados por quem chamar o webhook e deve ser tratado como risco crítico de SSRF/abuso.
4. O fluxo promete ler PDF/listas, mas não possui rota para documentos. Também não trata vídeo e vários tipos de mensagem.
5. A rota de `extendedTextMessage` consulta `$json.body...` depois da normalização, embora esse `body` já não exista no item. Respostas citadas podem ficar sem processamento.
6. A Milla inventou um fato proibido: “nossas estacas de cerca são de eucalipto tratado”. O catálogo deveria servir somente para reconhecer palavras, não para inferir espécie, tratamento, estoque ou garantia.
7. O prompt diz que nome e cidade são opcionais, mas a conversa insistiu no nome e chegou a perguntar novamente depois de o cliente dizer que só queria o tamanho.
8. Não existe estado confiável de “handoff já solicitado”. Por isso, a mesma conversa acionou `notify_vendedor` duas vezes e repetiu a promessa de contato.
9. Existe um segundo LLM apenas para formatar/dividir a resposta. Ele aumenta custo, latência e risco de mudar o sentido do texto. O tempo médio ponta a ponta foi 14,45 s; o máximo, 34,24 s.
10. Há nós desconectados ou redundantes (`Redis Chat Memory`, `Enviar Texto WhatsApp`, `Enviar Mensagem WhatsApp Lead`, `Envia Texto`, `If2` com as duas saídas iguais, entre outros), o que dificulta manutenção e cria caminhos antigos concorrendo com a versão atual.

## 2. Como o fluxo funciona hoje

### Entrada e normalização

- `input evolution`: webhook POST `milla-agent-webhook`.
- `normalizacao`: extrai ID, telefone, tipo, conteúdo, direção, nome de exibição e dados da instância.
- `Busca Quoted`: consulta a Evolution API para recuperar o texto citado.
- `Switch1`: separa mensagens enviadas (`fromMe`) das recebidas.

### Bloqueio por intervenção humana

- Mensagem `fromMe` grava `<chat_id>_block=true` por 900 segundos.
- Mensagem recebida consulta esse bloqueio; se existir, encerra no `No Operation`.

Problema: não há distinção explícita entre mensagem enviada por humano e mensagem enviada pela própria automação. Dependendo da configuração dos webhooks da Evolution, a própria resposta da Milla pode bloquear o próximo turno do cliente. Além disso, 15 minutos é um estado frágil para takeover humano: pode ser curto para o vendedor e longo para um falso positivo.

### Tratamento de conteúdo

- Texto simples: segue para o agrupamento.
- Áudio: baixa base64, converte para binário e transcreve com Gemini 2.5 Flash.
- Imagem: baixa base64 e pede uma descrição resumida ao Gemini 2.5 Flash.
- Documento/PDF: não há rota implementada.
- Mensagens não reconhecidas: caem no fallback sem resposta.

### Agrupamento/debounce

- Cada mensagem é adicionada a uma lista Redis por telefone.
- O fluxo espera 9 segundos.
- Somente a execução cuja mensagem é a última da lista continua; as anteriores terminam sem resposta.
- As mensagens são unidas, a lista é apagada e o texto passa por uma limpeza.

Riscos: a lista não recebe TTL visível; a comparação falha quando o texto salvo ganhou o prefixo de mensagem citada; e a lista é apagada antes da chamada ao agente, portanto uma falha posterior pode perder o lote.

### Contexto e memória

- Horário comercial é calculado em `America/Sao_Paulo`.
- Redis guarda primeira interação, nome de exibição, última mensagem e indicadores por 30 dias.
- A memória principal conectada ao agente é PostgreSQL, tabela `n8n_Milla`, com chave de sessão baseada no telefone.
- O campo `ConversationID` é preenchido com `body.sender`, que no histórico observado corresponde ao número da própria operação, não ao contato. Hoje isso não quebra a memória porque o nó PostgreSQL usa `phone`, mas o campo é enganoso e pode causar erro futuro.

### Agente, ferramentas e saída

- `Milla`: agente LangChain com Google Gemini e o prompt comercial.
- `notify_vendedor`: deveria chamar um subworkflow, mas o ID ainda é `COLE_AQUI_O_ID_DO_SUBWORKFLOW_NOTIFY_VENDEDOR`.
- `schedule_followup`: também está com ID-placeholder.
- `Parser Chain1`: chama outro Gemini para transformar o texto em `{messages: [...]}`.
- `Segmentos` + loop: envia cada bloco pela Evolution API, com espera entre mensagens.

## 3. Histórico completo disponível

As 23 execuções pertencem ao mesmo contato de teste (telefone mascarado: `***6607`; nome de exibição: Rafael Caus). Foram observadas quatro versões do workflow durante os testes. O histórico não contém conversas de outros clientes.

### Jornada A — tentativa inicial de orçamento de deck

1. Cliente: “Boa noite” — armazenada no debounce; sem resposta individual.
2. Cliente: “Preciso de um orçamento” — armazenada no debounce; sem resposta individual.
3. Cliente: “Deck de madeira” — armazenada no debounce; sem resposta individual.
4. Cliente: “10x2m2” — o lote chegou ao agente, mas falhou no subnó `Postgres Chat Memory`. Nenhuma resposta foi enviada e o lote já havia sido apagado do Redis.
5. Evento `secretEncryptedMessage` — ignorado pelo `Switch`, sem resposta. É provavelmente um evento técnico do WhatsApp, mas deveria ser filtrado explicitamente e não contado como conversa.
6. Cliente: “Oi” — resposta: “Olá! Bem-vindo à Millano Madeira. / Como posso ajudar você hoje?”. A resposta ignorou o pedido de deck perdido na falha anterior.

Avaliação: falha grave de continuidade. Um erro de memória transformou uma solicitação já detalhada em um reinício genérico.

### Jornada A2 — nova tentativa de orçamento de deck

7. Cliente: “Preciso de um orçamento” — debounce.
8. Cliente: “O deck da promo ao” — debounce.
9. Cliente: “Promoção” — o agente entendeu o pedido e perguntou a cidade, mas o formatador falhou no modelo `OpenAI4` com “The resource you are requesting could not be found”. Nenhuma mensagem foi enviada.
10. Reexecução da nº 9 — enviou duas mensagens: reconhecimento do orçamento para deck da promoção e pergunta da cidade.
11. Cliente: “Sou de Domingos Martins” — a Milla reconheceu a cidade e pediu o nome.
12. Cliente: “Rafael Caus” — a Milla disse que um vendedor entraria em contato e mencionou que a loja estava fechada. A ferramenta `notify_vendedor` falhou, mas o cliente recebeu a confirmação como se o encaminhamento tivesse funcionado.
13. Cliente: “Que horas vai abrir” — informou corretamente a grade de funcionamento e disse que o vendedor continuaria a partir das 7h.

Avaliação: a conversa ficou compreensível, mas houve um falso handoff. Também foi desnecessário condicionar o avanço ao nome, pois o produto, o pedido e a cidade já eram suficientes.

### Jornada B — estacas de cerca

14. Cliente: “Boa noite” — debounce.
15. Cliente: “Preciso saber o preço da estaca de cerca” — debounce.
16. Cliente: “Qual é o tamanho que você tem” — debounce.
17. Cliente: “E bitola” — debounce.
18. Cliente: “??” — a Milla reconheceu preço/tamanhos/bitolas, mas pediu o nome antes de encaminhar.
19. Cliente: “Ele é tratado com garantia??” — a Milla afirmou: “Sim, nossas estacas de cerca são de eucalipto tratado”. Essa confirmação não existia nos fatos autorizados. A parte sobre garantia foi corretamente delegada ao vendedor, mas voltou a exigir o nome.
20. Cliente: “Só preciso saber o tamanho” — debounce junto com a próxima mensagem.
21. Cliente: “Tem que falar o nome??” — a Milla insistiu que precisava do nome, mencionou loja fechada e perguntou se poderia agilizar. Isso contradiz a regra de não forçar dados opcionais e não respondeu com clareza que o nome não era obrigatório.
22. Cliente: “Sim agiliza” — a Milla confirmou encaminhamento; `notify_vendedor` falhou. O resumo da ferramenta usou um formato diferente do esquema rígido pedido no prompt.
23. Cliente: “Ok” — a Milla acionou `notify_vendedor` de novo, ele falhou novamente e o cliente recebeu outra promessa repetida de contato.

Avaliação: esta jornada expôs os maiores problemas de atendimento: alucinação de característica do produto, insistência burocrática, handoff falso e duplicado, e repetição após um simples “Ok”.

## 4. Métricas observadas

- Execuções: 23.
- Execuções com status de erro: 2 (8,7%).
- Passagens pelo agente: 12.
- Respostas produzidas pelo agente: 11.
- Falhas internas de `notify_vendedor`: 3 de 3 tentativas (100%). Essas falhas não marcaram a execução principal como erro.
- Duração média das execuções: 14,45 s.
- Mediana: 9,35 s.
- Máxima: 34,24 s.
- Espera do debounce: aproximadamente 9 s por mensagem candidata.
- Agente principal: média aproximada de 4,0 s por chamada.
- LLM formatador: média aproximada de 4,1 s por chamada.
- Envio de cada segmento: média aproximada de 1,7 s, além da espera de 1,2 s entre segmentos.

Observação: “2 execuções com erro” subestima a falha real, pois os três erros do subworkflow de notificação ficaram internos à ferramenta e as execuções terminaram como `success`.

## 5. Análise do prompt atual

### Pontos fortes

- Define claramente o papel de primeiro atendimento.
- Proíbe preço, estoque, prazo, garantia e promessa comercial sem validação humana.
- Orienta uma pergunta por vez e respostas curtas.
- Adapta o ritmo ao perfil do cliente.
- Traz regras úteis para horário, fornecedor, vagas, listas e local de entrega.
- Procura evitar tom robótico, bajulação e repetição.

### Problemas

1. O prompt é longo, repetitivo e possui muitas regras negativas. Isso reduz a saliência das regras realmente críticas.
2. O “catálogo interno” está perto das regras comerciais e favoreceu uma inferência indevida sobre estaca/eucalipto tratado.
3. “Nome e cidade se possível” conflita com frases que orientam incluir esses dados no handoff; o modelo transformou preferência em obrigação.
4. “Nunca envie texto corrido” depende de um segundo LLM, cujo prompt usa limite de 100 caracteres, enquanto o agente aceita cerca de 200. Há duas autoridades de formato.
5. O formatador tem instruções sobre markdown que contradizem a saída sem markdown do agente e pode reescrever o conteúdo.
6. O prompt exige que o agente chame ferramentas, mas não define uma saída transacional: ele pode prometer handoff mesmo quando a ferramenta falha.
7. Não existe regra operacional/stateful que impeça novo handoff após o primeiro.
8. O esquema de `notify_vendedor` aceita apenas uma string livre, embora o prompt exija campos rígidos. O modelo naturalmente varia o formato.
9. “Responda sempre” conflita com mensagens técnicas ou tipos não suportados que devem ser ignorados com segurança.
10. O prompt promete interpretar PDF, mas o workflow não fornece PDF ao agente.

## 6. Melhorias prioritárias

### P0 — antes de colocar em produção

1. Criar e conectar os subworkflows reais de notificação e follow-up. O cliente só deve receber confirmação de handoff depois de sucesso comprovado.
2. Mover todas as chaves para credenciais nativas do n8n; remover segredos dos nós e dos payloads; rotacionar a chave da Evolution API e a chave pública do n8n usada nesta auditoria.
3. Autenticar o webhook (assinatura/segredo), validar esquema e origem, limitar taxa e rejeitar eventos de instâncias desconhecidas.
4. Remover `server_url` controlado pelo payload do `Busca Quoted`; usar uma URL fixa/allowlist e credencial configurada internamente.
5. Corrigir a rota `extendedTextMessage` para consultar diretamente o nó de entrada ou preservar o corpo original.
6. Implementar documentos/PDF antes de manter essa promessa no prompt. Enquanto não existir, alterar a mensagem para encaminhar o arquivo ao vendedor sem afirmar que foi lido.
7. Criar idempotência por `message_id` com `SET NX` e TTL para evitar respostas duplicadas.

### P1 — confiabilidade e experiência

1. Substituir o segundo LLM formatador por divisão determinística nos blocos em branco produzidos pelo agente, ou fazer o agente retornar JSON estruturado validado diretamente.
2. Manter o lote no Redis até a resposta ser enviada; em erro, preservar/reprocessar com fila e dead-letter.
3. Adicionar TTL à lista de debounce e um lock/timestamp atômico por contato. Reduzir a janela inicial para algo entre 3 e 5 segundos e medir impacto.
4. Guardar estado explícito: `triage_open`, `handoff_requested`, `handoff_confirmed`, `human_active`, `closed`. Não depender somente de um bloqueio de 15 minutos.
5. Separar mensagem humana `fromMe` de mensagem enviada pela automação, usando IDs de saída gravados pela própria Milla.
6. Após `handoff_confirmed`, “Ok”, “obrigado” e equivalentes devem receber no máximo um encerramento curto, sem nova notificação.
7. Tornar nome e cidade opcionais de verdade: nunca bloquear resposta ou handoff por falta deles.
8. Criar monitoramento para erro de memória, modelo, ferramenta e envio; hoje o status `success` pode esconder falhas comerciais.

### P2 — manutenção, dados e qualidade

1. Remover nós mortos/antigos e renomear os restantes por função.
2. Corrigir `ConversationID` para o contato remoto ou removê-lo.
3. Trocar o schema da ferramenta de handoff por campos separados: `name`, `phone`, `location`, `products[]`, `quantity`, `need`, `notes`, `conversation_url`.
4. Para imagem/PDF, usar extração estruturada item a item, sem resumo que possa omitir medidas.
5. Definir retenção e minimização de dados para Redis, PostgreSQL e execuções do n8n; evitar salvar payload completo e credenciais em execuções de sucesso.
6. Criar suíte de testes com conversas curtas, mensagens em rajada, citações, áudio, imagem, PDF, irritação, nome recusado, handoff repetido, falha de ferramenta e intervenção humana.

## 7. Comportamento recomendado para os casos observados

### Pedido de deck em rajada

Resposta ideal após o agrupamento:

> Em qual cidade seria a entrega?

Se o cliente não quiser informar cidade, encaminhar mesmo assim, com `local não informado`.

### Estacas — preço, tamanho, bitola e garantia

Resposta ideal:

> Sabe a quantidade e a medida que precisa?

Se o cliente não souber:

> Sem problema. Um vendedor verifica preço, tamanhos, bitolas, tratamento e garantia com você por aqui.

Não afirmar espécie, tratamento, garantia, estoque ou medida. Não exigir nome. Fora do expediente, adicionar somente no encaminhamento: “Como a loja está fechada agora, ele continua assim que abrir.”

### Cliente pergunta se precisa dar o nome

Resposta ideal:

> Não, o nome não é obrigatório. Posso encaminhar sua dúvida sobre os tamanhos das estacas sem ele.

### Cliente diz apenas “Ok” após handoff

Resposta ideal:

> Certo. O atendimento continua por aqui assim que a loja abrir.

Sem nova chamada da ferramenta.

## 8. Plano de validação

Antes de ativar para clientes reais, executar pelo menos estes testes e exigir evidência de envio/notificação:

1. Texto simples e mensagem em rajada.
2. Resposta citada (`extendedTextMessage`).
3. Áudio curto e longo.
4. Foto de lista legível e ilegível.
5. PDF de relação de materiais.
6. Cliente recusa nome e/ou cidade.
7. Pergunta de preço, estoque, prazo, garantia e especificação técnica.
8. Produto fora do catálogo.
9. Falha do PostgreSQL, Gemini, Redis, Evolution e subworkflow de handoff.
10. Webhook duplicado com o mesmo `message_id`.
11. Humano assume a conversa e depois devolve à Milla.
12. “Ok/obrigado” depois de handoff confirmado.

Critério mínimo: nenhuma promessa sem ferramenta confirmada; nenhum fato comercial inventado; nenhuma duplicidade; todas as falhas relevantes visíveis em monitoramento; e 100% das mensagens suportadas com resposta ou encaminhamento explícito.
