feat(n8n): adiciona suporte a audio (transcricao via Whisper)

Baixa o audio (mesma autenticacao Bearer usada pra imagem/PDF), transcreve
com o node OpenAI (resource audio, operation transcribe, modelo whisper-1)
e trata o texto transcrito exatamente como uma mensagem digitada pelo
cliente. Erro no download ou na transcricao cai na mensagem padrao de
fallback. AI Agent agora le entrada do item atual ($json.entrada) em vez
de referenciar sempre o node original, para funcionar nos tres caminhos
(texto direto, imagem/PDF, audio transcrito).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Eric
2026-08-21 11:36:52 -03:00
co-authored by Claude Sonnet 5
parent ed1d154b95
commit a367637606
+266 -82
View File
@@ -4,6 +4,11 @@
"active": true,
"nodes": [
{
"parameters": {
"httpMethod": "POST",
"path": "orcamento-exames-canal-zpro",
"options": {}
},
"id": "webhook-canal-zpro",
"name": "Webhook - Canal Z-PRO",
"type": "n8n-nodes-base.webhook",
@@ -12,12 +17,6 @@
-608,
320
],
"parameters": {
"httpMethod": "POST",
"path": "orcamento-exames-canal-zpro",
"responseMode": "onReceived",
"options": {}
},
"webhookId": "a00a2a36-f7ec-4e8e-a98f-93d3f93dfe45",
"onError": "continueRegularOutput"
},
@@ -55,6 +54,12 @@
"type": "string",
"value": "={{ ($json.body.msg.image && $json.body.msg.image.url) || ($json.body.msg.document && $json.body.msg.document.url) || '' }}"
},
{
"id": "campo-audiourl",
"name": "audioUrl",
"type": "string",
"value": "={{ ($json.body.msg.audio && $json.body.msg.audio.url) || '' }}"
},
{
"id": "campo-tipomidia",
"name": "tipoMidia",
@@ -82,26 +87,18 @@
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
-368,
0
-544,
-32
]
},
{
"id": "if-esta-em-orcamento",
"name": "Está no fluxo de Orçamento?",
"type": "n8n-nodes-base.if",
"typeVersion": 2.3,
"position": [
-240,
0
],
"parameters": {
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
"leftValue": "",
"typeValidation": "strict",
"version": 2
},
"combinator": "or",
"conditions": [
@@ -126,18 +123,141 @@
]
},
"options": {}
}
},
"id": "if-esta-em-orcamento",
"name": "Está no fluxo de Orçamento?",
"type": "n8n-nodes-base.if",
"typeVersion": 2.3,
"position": [
-368,
-48
]
},
{
"parameters": {},
"id": "noop-fora-orcamento",
"name": "Não é fluxo de Orçamento - ignora",
"type": "n8n-nodes-base.noOp",
"typeVersion": 1,
"position": [
-240,
200
208
]
},
{
"id": "if-e-audio",
"name": "É mensagem de áudio?",
"type": "n8n-nodes-base.if",
"typeVersion": 2.3,
"position": [
-120,
-160
],
"parameters": {}
"parameters": {
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "cond-tem-audio",
"leftValue": "={{ $json.audioUrl }}",
"rightValue": "",
"operator": {
"type": "string",
"operation": "notEmpty",
"singleValue": true
}
}
]
},
"options": {}
}
},
{
"id": "baixar-audio",
"name": "Baixar áudio",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.4,
"position": [
80,
-400
],
"parameters": {
"url": "={{ $json.audioUrl }}",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Authorization",
"value": "=Bearer {{ $json.mediaToken }}"
}
]
},
"options": {
"response": {
"response": {
"responseFormat": "file"
}
},
"timeout": 20000
}
},
"onError": "continueErrorOutput"
},
{
"id": "transcrever-audio",
"name": "Transcrever áudio",
"type": "@n8n/n8n-nodes-langchain.openAi",
"typeVersion": 2.3,
"position": [
280,
-400
],
"parameters": {
"resource": "audio",
"operation": "transcribe",
"binaryPropertyName": "data",
"modelId": {
"mode": "id",
"value": "whisper-1"
},
"options": {}
},
"credentials": {
"openAiApi": {
"id": "h16jFm10cTU15k6M",
"name": "OpenAI - Orçamento de Exames"
}
},
"onError": "continueErrorOutput"
},
{
"id": "preparar-texto-audio",
"name": "Preparar texto do áudio",
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
480,
-400
],
"parameters": {
"assignments": {
"assignments": [
{
"id": "audio-entrada",
"name": "entrada",
"type": "string",
"value": "={{ $json.text }}"
}
]
},
"options": {}
}
},
{
"parameters": {
@@ -170,20 +290,12 @@
"typeVersion": 2.3,
"position": [
-128,
0
-48
]
},
{
"parameters": {
"url": "={{ $json.imagemUrl }}",
"options": {
"response": {
"response": {
"responseFormat": "file"
}
},
"timeout": 20000
},
"sendHeaders": true,
"headerParameters": {
"parameters": [
@@ -192,6 +304,14 @@
"value": "=Bearer {{ $json.mediaToken }}"
}
]
},
"options": {
"response": {
"response": {
"responseFormat": "file"
}
},
"timeout": 20000
}
},
"id": "baixar-imagem",
@@ -232,9 +352,9 @@
{
"parameters": {
"promptType": "define",
"text": "={{ $('Extrair campos do webhook').item.json.entrada || 'O cliente enviou uma imagem. Veja a imagem anexada, que é o pedido médico dele.' }}",
"text": "={{ $json.entrada || 'O cliente enviou uma imagem. Veja a imagem anexada, que é o pedido médico dele.' }}",
"options": {
"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:\n- Hemograma — R$ 35,00\n- Sódio e Potássio — R$ 35,00\n- Ureia e Creatinina — R$ 40,00\n- 25OH / Vitamina D — R$ 80,00\n- Glicemia de Jejum — R$ 20,00\n- Teste Oral de Tolerância à Glicose — R$ 70,00\n- Hemoglobina Glicada — R$ 40,00\n- Colesterol Total e Frações — R$ 50,00\n- Triglicerídeos — R$ 20,00\n- TSH e T4 Livre — R$ 60,00\n- CK — R$ 30,00\n- TGO/TGP — R$ 30,00\n- Gama GT — R$ 20,00\n- Bilirrubina Total e Frações — R$ 30,00\n- Amilase — R$ 30,00\n- Lipase — R$ 35,00\n- Fosfatase Alcalina — R$ 20,00\n- Proteínas Totais e Frações — R$ 25,00\n- Ferro, Ferritina e Transferrina — R$ 100,00\n- Cálcio Total — R$ 20,00\n- Cálcio Ionizado — R$ 30,00\n- Selênio — R$ 100,00\n- Magnésio — R$ 20,00\n- Fósforo — R$ 20,00\n- Ácido Úrico — R$ 20,00\n- VHS — R$ 20,00\n- PCR — R$ 35,00\n- APTT / TTPa — R$ 25,00\n- VDRL — R$ 25,00\n- Dosagem de Vitamina B12 — R$ 60,00\n- Dosagem de Ácido Fólico — R$ 55,00\n- Sorologia para Hepatites A, B e C — R$ 180,00\n- Sorologia para outras doenças (fora hepatites A, B e C) — sob consulta, valor confirmado por um atendente\n- PSA Total e Livre — R$ 60,00\n- Alfa-Fetoproteína + CEA + CA 19-9 — R$ 180,00\n- CA 125 + CA 15-3 — R$ 130,00\n- Eletroforese de Proteínas — R$ 60,00\n- Eletroforese de Hemoglobina — R$ 70,00\n- Tipo Sanguíneo — R$ 30,00\n- Urina I — R$ 25,00\n- Urocultura — R$ 50,00\n- Urina de 24 horas (Creatinina, Albumina e Cálcio) — R$ 90,00\n- Protoparasitológico de Fezes – 1 amostra — R$ 30,00\n- Sangue Oculto nas Fezes — R$ 30,00\n\nCOMO LER O PEDIDO MÉDICO NA IMAGEM:\n- Muitos pedidos médicos vêm como um formulário com caixinhas de seleção (checkbox) ao lado do nome de cada exame, tipo um formulário de \"Solicitação de Exames\" com lista pré-impressa.\n- Nesses formulários, ORCE APENAS os exames cuja caixinha está MARCADA (com X, risco, preenchida, ou qualquer marcação visível). NUNCA orce um exame que está listado no formulário mas cuja caixinha está vazia/desmarcada — ele não foi solicitado, mesmo que apareça impresso na folha.\n- Se não conseguir ver com clareza se uma caixinha específica está marcada ou não, NÃO inclua esse exame no orçamento — trate como não identificado com confiança (ver regra 5 abaixo).\n- Se o pedido não for desse formato de formulário (por exemplo, uma receita escrita à mão listando os exames diretamente, sem caixinhas), considere todos os exames escritos como solicitados normalmente.\n\nCOMO CONVERSAR:\n1. O cliente pode mandar uma foto do pedido médico, um PDF do pedido médico, digitar os nomes dos exames, ou combinar essas formas 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 ou PDF anexado a esta mensagem (quando houver) é o pedido médico do cliente — leia o conteúdo do PDF (todas as páginas relevantes) do mesmo jeito que leria uma foto.\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 com valor fixo, inclua na resposta o nome do exame e o valor.\n5. Para exames que caírem em itens \"sob consulta\" (como sorologias fora de Hepatites A, B e C), ou 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 fixo confirmado na tabela (nunca inclua itens \"sob consulta\" no total).\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.",
"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:\n- Hemograma — R$ 35,00\n- Sódio e Potássio — R$ 35,00\n- Ureia e Creatinina — R$ 40,00\n- 25OH / Vitamina D — R$ 80,00\n- Glicemia de Jejum — R$ 20,00\n- Teste Oral de Tolerância à Glicose — R$ 70,00\n- Hemoglobina Glicada — R$ 40,00\n- Colesterol Total e Frações — R$ 50,00\n- Triglicerídeos — R$ 20,00\n- TSH e T4 Livre — R$ 60,00\n- CK — R$ 30,00\n- TGO/TGP — R$ 30,00\n- Gama GT — R$ 20,00\n- Bilirrubina Total e Frações — R$ 30,00\n- Amilase — R$ 30,00\n- Lipase — R$ 35,00\n- Fosfatase Alcalina — R$ 20,00\n- Proteínas Totais e Frações — R$ 25,00\n- Ferro, Ferritina e Transferrina — R$ 100,00\n- Cálcio Total — R$ 20,00\n- Cálcio Ionizado — R$ 30,00\n- Selênio — R$ 100,00\n- Magnésio — R$ 20,00\n- Fósforo — R$ 20,00\n- Ácido Úrico — R$ 20,00\n- VHS — R$ 20,00\n- PCR — R$ 35,00\n- APTT / TTPa — R$ 25,00\n- VDRL — R$ 25,00\n- Dosagem de Vitamina B12 — R$ 60,00\n- Dosagem de Ácido Fólico — R$ 55,00\n- Sorologia para Hepatites A, B e C — R$ 180,00\n- Sorologia para outras doenças (fora hepatites A, B e C) — sob consulta, valor confirmado por um atendente\n- PSA Total e Livre — R$ 60,00\n- Alfa-Fetoproteína + CEA + CA 19-9 — R$ 180,00\n- CA 125 + CA 15-3 — R$ 130,00\n- Eletroforese de Proteínas — R$ 60,00\n- Eletroforese de Hemoglobina — R$ 70,00\n- Tipo Sanguíneo — R$ 30,00\n- Urina I — R$ 25,00\n- Urocultura — R$ 50,00\n- Urina de 24 horas (Creatinina, Albumina e Cálcio) — R$ 90,00\n- Protoparasitológico de Fezes – 1 amostra — R$ 30,00\n- Sangue Oculto nas Fezes — R$ 30,00\n\nCOMO LER O PEDIDO MÉDICO NA IMAGEM:\n- Muitos pedidos médicos vêm como um formulário com caixinhas de seleção (checkbox) ao lado do nome de cada exame, tipo um formulário de \"Solicitação de Exames\" com lista pré-impressa.\n- Nesses formulários, ORCE APENAS os exames cuja caixinha está MARCADA (com X, risco, preenchida, ou qualquer marcação visível). NUNCA orçe um exame que está listado no formulário mas cuja caixinha está vazia/desmarcada — ele não foi solicitado, mesmo que apareça impresso na folha.\n- Se não conseguir ver com clareza se uma caixinha específica está marcada ou não, NÃO inclua esse exame no orçamento — trate como não identificado com confiança (ver regra 5 abaixo).\n- Se o pedido não for desse formato de formulário (por exemplo, uma receita escrita à mão listando os exames diretamente, sem caixinhas), considere todos os exames escritos como solicitados normalmente.\n\nCOMO CONVERSAR:\n1. O cliente pode mandar uma foto do pedido médico, um PDF do pedido médico, digitar os nomes dos exames, ou combinar essas formas 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 ou PDF anexado a esta mensagem (quando houver) é o pedido médico do cliente — leia o conteúdo do PDF (todas as páginas relevantes) do mesmo jeito que leria uma foto.\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 com valor fixo, inclua na resposta o nome do exame e o valor.\n5. Para exames que caírem em itens \"sob consulta\" (como sorologias fora de Hepatites A, B e C), ou 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 fixo confirmado na tabela (nunca inclua itens \"sob consulta\" no total).\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,
"passthroughBinaryPdfs": true
}
@@ -306,27 +426,26 @@
"name": "Simple Memory"
},
{
"id": "enviar-orcamento-zpro-http",
"name": "Enviar orçamento (Z-PRO)",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.4,
"position": [
900,
-128
],
"parameters": {
"method": "POST",
"url": "https://apigchat.gtigrupo.com.br/v2/api/external/fc30a773-0089-43b9-8b05-a5d92c48eb9b/",
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth",
"sendBody": true,
"contentType": "json",
"specifyBody": "json",
"jsonBody": "={{ JSON.stringify({ body: $json.body, number: $json.number, externalKey: $json.externalKey, isClosed: $json.isClosed }) }}",
"options": {
"timeout": 15000
}
},
"id": "enviar-orcamento-zpro-http",
"name": "Enviar orçamento (Z-PRO)",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.4,
"position": [
912,
-128
],
"credentials": {
"httpHeaderAuth": {
"id": "6IxSBH9y4fXsIoyA",
@@ -337,6 +456,111 @@
}
],
"connections": {
"Webhook - Canal Z-PRO": {
"main": [
[
{
"node": "Extrair campos do webhook",
"type": "main",
"index": 0
}
]
]
},
"Extrair campos do webhook": {
"main": [
[
{
"node": "Está no fluxo de Orçamento?",
"type": "main",
"index": 0
}
]
]
},
"Está no fluxo de Orçamento?": {
"main": [
[
{
"node": "É mensagem de áudio?",
"type": "main",
"index": 0
}
],
[
{
"node": "Não é fluxo de Orçamento - ignora",
"type": "main",
"index": 0
}
]
]
},
"É mensagem de áudio?": {
"main": [
[
{
"node": "Baixar áudio",
"type": "main",
"index": 0
}
],
[
{
"node": "É mensagem de imagem?",
"type": "main",
"index": 0
}
]
]
},
"Baixar áudio": {
"main": [
[
{
"node": "Transcrever áudio",
"type": "main",
"index": 0
}
],
[
{
"node": "Montar corpo da resposta",
"type": "main",
"index": 0
}
]
]
},
"Transcrever áudio": {
"main": [
[
{
"node": "Preparar texto do áudio",
"type": "main",
"index": 0
}
],
[
{
"node": "Montar corpo da resposta",
"type": "main",
"index": 0
}
]
]
},
"Preparar texto do áudio": {
"main": [
[
{
"node": "AI Agent - Orçamento",
"type": "main",
"index": 0
}
]
]
},
"É mensagem de imagem?": {
"main": [
[
@@ -413,17 +637,6 @@
]
]
},
"Extrair campos do webhook": {
"main": [
[
{
"node": "Está no fluxo de Orçamento?",
"type": "main",
"index": 0
}
]
]
},
"Montar corpo da resposta": {
"main": [
[
@@ -434,35 +647,6 @@
}
]
]
},
"Webhook - Canal Z-PRO": {
"main": [
[
{
"node": "Extrair campos do webhook",
"type": "main",
"index": 0
}
]
]
},
"Está no fluxo de Orçamento?": {
"main": [
[
{
"node": "É mensagem de imagem?",
"type": "main",
"index": 0
}
],
[
{
"node": "Não é fluxo de Orçamento - ignora",
"type": "main",
"index": 0
}
]
]
}
},
"settings": {