fix(n8n): torna o fluxo conversacional (texto e/ou imagem, com memoria de sessao)
O fluxo anterior encerrava sem responder quando a mensagem nao tinha imagem, o que nao reflete um atendimento real (cliente manda texto antes/depois, ou digita os exames em vez de mandar foto). Agora toda mensagem chega ao AI Agent (com ou sem imagem), com memoria de conversa via Postgres, seguindo o mesmo padrao do bot de agendamento ja usado nessa conta. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -164,6 +164,53 @@ Webhook (Z-PRO)
|
||||
6. Trocar a lista de exemplo pela lista real de exames/valores no prompt do
|
||||
`AI Agent`.
|
||||
|
||||
## Revisão 1 (2026-08-21) — fluxo conversacional (texto + imagem)
|
||||
|
||||
Depois do primeiro teste real, o usuário apontou que um atendimento real não
|
||||
chega só como uma foto isolada: o cliente pode mandar texto antes/depois, ou
|
||||
digitar os nomes dos exames em vez de (ou além de) mandar foto, tudo em
|
||||
mensagens separadas. O desenho original tratava qualquer mensagem sem imagem
|
||||
como "não se aplica" e encerrava sem responder — o que quebra esse caso real.
|
||||
|
||||
Mudanças no workflow, alinhadas ao mesmo padrão já usado pelo bot de
|
||||
agendamento desta conta (`Agendamento - GChat - Z-PRO (clínica exemplo)`):
|
||||
|
||||
- O node **"É mensagem de imagem?"** deixou de ser um filtro que encerra o
|
||||
fluxo — agora só decide se baixa a imagem antes do agente ou não. As duas
|
||||
saídas (com ou sem imagem) chegam no `AI Agent`; nenhuma mensagem é mais
|
||||
ignorada.
|
||||
- **"Extrair campos do webhook"** agora também extrai o texto da mensagem
|
||||
(`entrada`), no mesmo nome de campo usado pelos bots de agendamento que já
|
||||
funcionam nessa conta.
|
||||
- Adicionada **memória de conversa por sessão** (node `Memória da Sessão
|
||||
(Postgres)`, reaproveitando a credencial `Postgres Agendamento - (clínica
|
||||
exemplo)`, com `sessionKey` prefixado (`orcamento-<protocolo>`) para não
|
||||
colidir com a memória do bot de agendamento). Isso é necessário porque cada
|
||||
mensagem do WhatsApp chega como uma execução nova e separada do workflow —
|
||||
sem memória persistente, o agente esqueceria o que o cliente já tinha
|
||||
mandado numa mensagem anterior.
|
||||
- O **system prompt do `AI Agent`** foi reescrito para ser conversacional: se
|
||||
o cliente ainda não mandou nem foto nem lista de exames (nesta mensagem ou
|
||||
antes, na mesma conversa), o agente pede isso educadamente em vez de
|
||||
inventar um orçamento vazio; assim que houver pelo menos um exame
|
||||
identificável (por imagem, texto, ou o histórico da conversa), ele monta o
|
||||
orçamento normalmente.
|
||||
- O envio da resposta (via sub-workflow `enviar_texto`) passou a acontecer
|
||||
sempre, em toda mensagem — porque agora toda mensagem gera uma resposta do
|
||||
agente (seja um pedido de mais informação, seja o orçamento final), não só
|
||||
quando uma imagem é reconhecida.
|
||||
|
||||
### Descoberta durante o teste: payload do webhook chegou vazio
|
||||
|
||||
Os dois testes reais feitos pelo usuário mostraram o webhook recebendo
|
||||
`body: {}` (content-length 0, user-agent `axios/1.19.0`) — ou seja, o Z-PRO
|
||||
não mandou `protocol`/`entrada`/`contactName`/`number` como os bots de
|
||||
agendamento recebem. Isso sugere que a URL foi configurada num campo de
|
||||
webhook diferente do usado pelo ChatFlow desses bots (que é o que gera esse
|
||||
formato JSON). Ainda em aberto — precisa confirmar com o usuário onde
|
||||
exatamente a URL foi cadastrada no painel do Z-PRO, e ajustar pro mesmo
|
||||
mecanismo usado pelos bots que já funcionam (ChatFlow → passo "Webhook").
|
||||
|
||||
## Fora de escopo (por enquanto)
|
||||
|
||||
- Fila/aprovação humana antes do envio (usuário optou por envio automático).
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"id": "6FU0jjPwEKDBrFSW",
|
||||
"name": "Orçamento de Exames - GChat - Z-PRO (IA Visão)",
|
||||
"active": false,
|
||||
"active": true,
|
||||
"nodes": [
|
||||
{
|
||||
"id": "webhook-orcamento",
|
||||
@@ -51,6 +51,12 @@
|
||||
"type": "string",
|
||||
"value": "={{ $json.body.contactName }}"
|
||||
},
|
||||
{
|
||||
"id": "campo-entrada",
|
||||
"name": "entrada",
|
||||
"type": "string",
|
||||
"value": "={{ String($json.body.entrada || $json.body.text || '').trim() }}"
|
||||
},
|
||||
{
|
||||
"id": "campo-imagemurl",
|
||||
"name": "imagemUrl",
|
||||
@@ -102,17 +108,6 @@
|
||||
"options": {}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "noop-nao-imagem",
|
||||
"name": "Não é imagem - encerra",
|
||||
"type": "n8n-nodes-base.noOp",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
120,
|
||||
160
|
||||
],
|
||||
"parameters": {}
|
||||
},
|
||||
{
|
||||
"id": "baixar-imagem",
|
||||
"name": "Baixar imagem",
|
||||
@@ -170,9 +165,9 @@
|
||||
],
|
||||
"parameters": {
|
||||
"promptType": "define",
|
||||
"text": "Segue a imagem do pedido de exames do cliente. Monte o orçamento conforme as instruções do system prompt.",
|
||||
"text": "={{ $('Extrair campos do webhook').item.json.entrada || 'O cliente enviou uma imagem (sem texto). Veja a imagem anexada, que é o pedido médico dele.' }}",
|
||||
"options": {
|
||||
"systemMessage": "Você é o assistente de orçamentos de exames de uma clínica, respondendo automaticamente por WhatsApp.\n\nTABELA DE EXAMES E VALORES (lista de exemplo — será substituída pela lista real depois):\n- Hemograma completo — R$ 35,00\n- Glicemia de jejum — R$ 20,00\n- Colesterol total e frações — R$ 45,00\n- Triglicérideos — R$ 25,00\n- TSH (hormônio tireoidiano) — R$ 40,00\n- Exame de urina (EAS) — R$ 30,00\n- Ultrassonografia abdominal — R$ 150,00\n- Raio-X de tórax — R$ 90,00\n- Eletrocardiograma (ECG) — R$ 80,00\n- Papanicolau — R$ 60,00\n\nINSTRUÇÕES:\n1. Leia a imagem anexada, que é a foto de um pedido médico de exames (pode estar manuscrita ou impressa).\n2. Identifique cada exame solicitado na imagem.\n3. Para cada exame identificado que corresponde (mesmo com sinônimo, abreviação comum ou pequena diferença de grafia) a um item da tabela acima, inclua na resposta o nome do exame e o valor.\n4. Para exames identificados na imagem que NÃO correspondem a nenhum item da tabela, ou que você não conseguiu ler com confiança, liste-os separadamente avisando que um atendente vai confirmar o valor.\n5. Nunca deixe de responder por causa de um item não reconhecido — sempre orçe o que for possível reconhecer.\n6. Calcule e mostre o valor total somando apenas os itens com preço confirmado na tabela.\n7. Responda em português, tom cordial e objetivo, formatado para WhatsApp: texto simples, sem markdown (nada de ** ou #), pode usar emojis com moderação e quebras de linha para organizar.\n8. Sua resposta deve conter SOMENTE o texto final a ser enviado ao cliente — nada de comentários extras, explicações do seu raciocínio, ou texto fora do orçamento.",
|
||||
"systemMessage": "Você é o assistente de orçamentos de exames de uma clínica, atendendo automaticamente pelo WhatsApp, numa conversa com várias mensagens (não só uma).\n\nTABELA DE EXAMES E VALORES (lista de exemplo — será substituída pela lista real depois):\n- Hemograma completo — R$ 35,00\n- Glicemia de jejum — R$ 20,00\n- Colesterol total e frações — R$ 45,00\n- Triglicérideos — R$ 25,00\n- TSH (hormônio tireoidiano) — R$ 40,00\n- Exame de urina (EAS) — R$ 30,00\n- Ultrassonografia abdominal — R$ 150,00\n- Raio-X de tórax — R$ 90,00\n- Eletrocardiograma (ECG) — R$ 80,00\n- Papanicolau — R$ 60,00\n\nCOMO CONVERSAR:\n1. O cliente pode mandar uma foto do pedido médico (com ou sem legenda), digitar os nomes dos exames, ou fazer as duas coisas em mensagens separadas, em qualquer ordem — use também o histórico da conversa (mensagens anteriores) pra juntar as informações, não só a mensagem atual. Uma imagem anexada a esta mensagem (quando houver) é o pedido médico do cliente.\n2. Se o cliente ainda não mandou, nem agora nem antes nesta conversa, nenhuma foto de pedido nem nomes de exames, NÃO invente orçamento — cumprimente rapidamente e peça pra ele mandar a foto do pedido médico ou digitar os nomes dos exames.\n3. Assim que tiver pelo menos um exame identificável (considerando a imagem atual, o texto atual E o histórico da conversa), identifique cada exame pedido.\n4. Para cada exame identificado que corresponde (mesmo com sinônimo, abreviação comum ou pequena diferença de grafia) a um item da tabela acima, inclua na resposta o nome do exame e o valor.\n5. Para exames identificados que NÃO correspondem a nenhum item da tabela, ou que você não conseguiu ler/entender com confiança, liste-os separadamente avisando que um atendente vai confirmar o valor.\n6. Nunca deixe de orçar por causa de um item não reconhecido — sempre orçe o que for possível reconhecer.\n7. Calcule e mostre o valor total somando apenas os itens com preço confirmado na tabela.\n8. Responda sempre em português, tom cordial e objetivo, formatado para WhatsApp: texto simples, sem markdown (nada de ** ou #), pode usar emojis com moderação e quebras de linha para organizar.\n9. Sua resposta deve conter SOMENTE o texto a ser enviado ao cliente nesta mensagem — nada de comentários extras, explicações do seu raciocínio, ou texto fora da conversa com o cliente.",
|
||||
"passthroughBinaryImages": true
|
||||
}
|
||||
},
|
||||
@@ -286,6 +281,27 @@
|
||||
"convertFieldsToString": false
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "memoria-sessao-orcamento",
|
||||
"name": "Memória da Sessão (Postgres)",
|
||||
"type": "@n8n/n8n-nodes-langchain.memoryPostgresChat",
|
||||
"typeVersion": 1.4,
|
||||
"position": [
|
||||
360,
|
||||
40
|
||||
],
|
||||
"parameters": {
|
||||
"sessionIdType": "customKey",
|
||||
"sessionKey": "={{ 'orcamento-' + ($('Extrair campos do webhook').item.json.sessionId || $('Extrair campos do webhook').item.json.telefone) }}",
|
||||
"contextWindowLength": 20
|
||||
},
|
||||
"credentials": {
|
||||
"postgres": {
|
||||
"id": "xq3QAN3MZSOCVgdY",
|
||||
"name": "Postgres Agendamento - (clínica exemplo)"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"connections": {
|
||||
@@ -322,7 +338,7 @@
|
||||
],
|
||||
[
|
||||
{
|
||||
"node": "Não é imagem - encerra",
|
||||
"node": "AI Agent - Orçamento",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
@@ -386,6 +402,17 @@
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Memória da Sessão (Postgres)": {
|
||||
"ai_memory": [
|
||||
[
|
||||
{
|
||||
"node": "AI Agent - Orçamento",
|
||||
"type": "ai_memory",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"settings": {
|
||||
|
||||
Reference in New Issue
Block a user