8
n8n 中文网amn8n.com

使用 OpenAI GPT-4o-mini 和文本转语音创建语音助手界面

中级

这是一个Support Chatbot, AI Chatbot领域的自动化工作流,包含 14 个节点。主要使用 Html, Webhook, Agent, OpenAi, RespondToWebhook 等节点。 使用 OpenAI GPT-4o-mini 和文本转语音创建语音助手界面

前置要求
  • HTTP Webhook 端点(n8n 会自动生成)
  • OpenAI API Key
工作流预览
可视化展示节点连接关系,支持缩放和平移
导出工作流
复制以下 JSON 配置到 n8n 导入,即可使用此工作流
{
  "meta": {
    "instanceId": "6052a1b29f061469e8139dae44556603650099c3365d7598798f132ae827fa1c"
  },
  "nodes": [
    {
      "id": "251e4d25-f04b-4861-b4fb-c9aa63654d2e",
      "name": "语音接口端点",
      "type": "n8n-nodes-base.webhook",
      "position": [
        880,
        180
      ],
      "webhookId": "71ac230d-5949-41ba-b05e-761cb5cb07f3",
      "parameters": {
        "path": "voice-assistant",
        "options": {},
        "responseMode": "responseNode"
      },
      "typeVersion": 2
    },
    {
      "id": "299de4f0-2bdd-46da-bfdb-35128a6240e0",
      "name": "语音助手界面",
      "type": "n8n-nodes-base.html",
      "position": [
        1100,
        180
      ],
      "parameters": {
        "html": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n  <meta charset=\"UTF-8\">\n  <title>AI Voice Assistant</title>\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n  <style>\n    body {\n      background-color: #000;\n      color: white;\n      font-family: 'Segoe UI', sans-serif;\n      display: flex;\n      flex-direction: column;\n      justify-content: center;\n      align-items: center;\n      height: 100vh;\n      margin: 0;\n      overflow: hidden;\n    }\n\n    .orb-container {\n      position: relative;\n    }\n\n    .orb {\n      width: 200px;\n      height: 200px;\n      background: radial-gradient(circle at 30% 30%, #00ffff, #004d4d);\n      border-radius: 50%;\n      box-shadow: 0 0 40px #00ffff88;\n      cursor: pointer;\n      display: flex;\n      align-items: center;\n      justify-content: center;\n      z-index: 2; /* Ensures clickability */\n    }\n\n    .ring {\n      position: absolute;\n      top: -20px;\n      left: -20px;\n      width: 240px;\n      height: 240px;\n      border-radius: 50%;\n      box-shadow: 0 0 30px #00ffff55;\n      animation: pulse 2s infinite;\n      z-index: 1;\n      pointer-events: none; /* Prevents click interference */\n    }\n    \n    .orb.listening {\n      background: radial-gradient(circle at 30% 30%, #00ffcc, #005c5c);\n      box-shadow: 0 0 50px #00ffff;\n    }\n\n    .orb.playing {\n      background: radial-gradient(circle at 30% 30%, #ff00ff, #520052);\n      box-shadow: 0 0 50px #ff00ff99;\n    }\n\n    .ring.listening {\n      animation: vibrate 0.4s infinite;\n    }\n\n    .ring.playing {\n      animation: glow 1s ease-in-out infinite alternate;\n    }\n\n    @keyframes pulse {\n      0%, 100% { transform: scale(1); opacity: 0.6; }\n      50% { transform: scale(1.2); opacity: 1; }\n    }\n\n    @keyframes vibrate {\n      0% { transform: scale(1.05); }\n      50% { transform: scale(1.15); }\n      100% { transform: scale(1.05); }\n    }\n\n    @keyframes glow {\n      0% { box-shadow: 0 0 40px #ff00ff88; }\n      100% { box-shadow: 0 0 60px #ff00ffcc; }\n    }\n\n    .status-text {\n      margin-top: 20px;\n      font-size: 18px;\n      color: #aaa;\n      font-weight: 300;\n      font-style: italic;\n    }\n  </style>\n</head>\n<body>\n  <div class=\"orb-container\">\n    <div id=\"ring\" class=\"ring\"></div>\n    <div id=\"orb\" class=\"orb\" title=\"Click to speak with AI\"></div>\n  </div>\n  <div id=\"status\" class=\"status-text\">Click to speak</div>\n\n  <audio id=\"beep\" src=\"https://www.soundjay.com/button/sounds/button-3.mp3\" preload=\"auto\"></audio>\n\n  <script>\n    const orb = document.getElementById('orb');\n    const ring = document.getElementById('ring');\n    const statusText = document.getElementById('status');\n    const beep = document.getElementById('beep');\n\n    let recognition;\n    let lastTranscript = \"\";\n    let silenceTimeout;\n\n    // Initialize speech recognition\n    function startRecognition() {\n      if (!('webkitSpeechRecognition' in window)) {\n        alert('This browser does not support speech recognition.');\n        return;\n      }\n\n      beep.play();\n      recognition = new webkitSpeechRecognition();\n      recognition.lang = 'en-US'; // Change to your preferred language\n      recognition.interimResults = true;\n      recognition.continuous = true;\n      lastTranscript = \"\";\n\n      recognition.onstart = () => {\n        orb.classList.add('listening');\n        ring.classList.add('listening');\n        statusText.textContent = \"Listening...\";\n      };\n\n      recognition.onresult = (event) => {\n        for (let i = event.resultIndex; i < event.results.length; ++i) {\n          if (event.results[i].isFinal) {\n            lastTranscript += event.results[i][0].transcript;\n          }\n        }\n\n        // Reset silence timeout\n        clearTimeout(silenceTimeout);\n        silenceTimeout = setTimeout(stopRecognition, 1000);\n      };\n\n      recognition.onerror = (event) => {\n        console.error('Error:', event.error);\n        orb.classList.remove('listening');\n        ring.classList.remove('listening');\n        statusText.textContent = \"Error: \" + event.error;\n      };\n\n      recognition.onend = () => {\n        orb.classList.remove('listening');\n        ring.classList.remove('listening');\n      };\n\n      recognition.start();\n    }\n\n    // Stop recognition and send to webhook\n    async function stopRecognition() {\n      if (recognition) {\n        recognition.stop();\n        orb.classList.remove('listening');\n        ring.classList.remove('listening');\n        statusText.textContent = \"Processing...\";\n\n        if (lastTranscript.trim() !== '') {\n          try {\n            // IMPORTANT: Replace YOUR_WEBHOOK_URL_HERE with the actual webhook URL from the 'Audio Processing Endpoint' node\n            const response = await fetch(\"YOUR_WEBHOOK_URL_HERE\", {\n              method: \"POST\",\n              headers: { \"Content-Type\": \"application/json\" },\n              body: JSON.stringify({ question: lastTranscript.trim() })\n            });\n\n            if (!response.ok) throw new Error(\"Server response error\");\n\n            const blob = await response.blob();\n            const audioURL = URL.createObjectURL(blob);\n            const audio = new Audio(audioURL);\n\n            audio.onplay = () => {\n              orb.classList.add('playing');\n              ring.classList.add('playing');\n              statusText.textContent = \"Responding...\";\n            };\n\n            audio.onended = () => {\n              orb.classList.remove('playing');\n              ring.classList.remove('playing');\n              statusText.textContent = \"Click to speak\";\n            };\n\n            audio.play();\n\n          } catch (err) {\n            console.error(\"Error sending or processing response:\", err);\n            statusText.textContent = \"Error communicating with AI\";\n          }\n        }\n      }\n    }\n\n    // Click handler for the orb\n    orb.addEventListener('click', () => {\n      if (recognition && recognition.running) {\n        stopRecognition();\n      } else {\n        startRecognition();\n      }\n    });\n  </script>\n</body>\n</html>\n"
      },
      "typeVersion": 1.2
    },
    {
      "id": "cbfca6b1-5b62-414d-a71b-e5a6b03236f1",
      "name": "发送 HTML 界面",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        1300,
        180
      ],
      "parameters": {
        "options": {},
        "respondWith": "text",
        "responseBody": "={{ $json.html }}"
      },
      "typeVersion": 1.1
    },
    {
      "id": "028000ca-2232-4168-867b-88f53eab9760",
      "name": "音频处理端点",
      "type": "n8n-nodes-base.webhook",
      "position": [
        720,
        720
      ],
      "webhookId": "287d40b1-4172-4ba0-9a1d-6d971dd9cf68",
      "parameters": {
        "path": "process-audio",
        "options": {},
        "httpMethod": "POST",
        "responseMode": "responseNode"
      },
      "typeVersion": 2
    },
    {
      "id": "d86e58ed-a0be-4853-a1dd-7d59bd6d2c1f",
      "name": "处理用户查询",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        940,
        720
      ],
      "parameters": {
        "text": "={{ $json.body.question }}",
        "options": {
          "systemMessage": "You are a helpful AI assistant. Respond in a friendly and conversational manner."
        },
        "promptType": "define"
      },
      "typeVersion": 1.8
    },
    {
      "id": "9f336bfd-1dfc-4d4a-9fad-74d3df57bf0c",
      "name": "对话记忆",
      "type": "@n8n/n8n-nodes-langchain.memoryBufferWindow",
      "position": [
        1040,
        920
      ],
      "parameters": {
        "sessionKey": "voice-assistant-session",
        "sessionIdType": "customKey",
        "contextWindowLength": 30
      },
      "typeVersion": 1.3
    },
    {
      "id": "4e400995-440d-4a2b-927c-9e612a649fe8",
      "name": "发送音频响应",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        1520,
        720
      ],
      "parameters": {
        "options": {},
        "respondWith": "binary"
      },
      "typeVersion": 1.1
    },
    {
      "id": "e468576e-22b6-44ef-9b0e-d2a95d72d2aa",
      "name": "生成语音响应",
      "type": "@n8n/n8n-nodes-langchain.openAi",
      "disabled": true,
      "position": [
        1300,
        720
      ],
      "parameters": {
        "input": "={{ $json.output }}",
        "voice": "onyx",
        "options": {},
        "resource": "audio"
      },
      "typeVersion": 1.8
    },
    {
      "id": "be60d217-d893-497f-87ff-a882ef11afc9",
      "name": "便签",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        740,
        -20
      ],
      "parameters": {
        "color": 5,
        "width": 840,
        "height": 420,
        "content": "## 语音助手界面"
      },
      "typeVersion": 1
    },
    {
      "id": "12cca86e-0868-4569-b89c-7f0d638254d1",
      "name": "便签1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        620,
        500
      ],
      "parameters": {
        "color": 3,
        "width": 1100,
        "height": 580,
        "content": "## 后端处理"
      },
      "typeVersion": 1
    },
    {
      "id": "e5a3196b-2865-4a62-b2d9-d755a67fcb38",
      "name": "模板描述",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -120,
        -20
      ],
      "parameters": {
        "color": 6,
        "width": 600,
        "height": 1460,
        "content": "## 使用 n8n 和 OpenAI 的语音助手界面"
      },
      "typeVersion": 1
    },
    {
      "id": "eba88594-9e7e-47b1-b1de-5e19e4607035",
      "name": "设置说明",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1820,
        -40
      ],
      "parameters": {
        "color": 7,
        "width": 400,
        "height": 500,
        "content": "## ⚙️ 设置说明"
      },
      "typeVersion": 1
    },
    {
      "id": "8eb03b82-cc02-46d9-b9b6-873718202e32",
      "name": "自定义选项",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1820,
        560
      ],
      "parameters": {
        "color": 7,
        "width": 400,
        "height": 440,
        "content": "## 🎨 自定义选项"
      },
      "typeVersion": 1
    },
    {
      "id": "c7d2aac4-5cb2-405c-8a4f-0f1020d76eec",
      "name": "GPT-4o-mini 模型",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
      "disabled": true,
      "position": [
        900,
        920
      ],
      "parameters": {
        "model": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-4o-mini",
          "cachedResultName": "gpt-4o-mini"
        },
        "options": {}
      },
      "typeVersion": 1.2
    }
  ],
  "pinData": {},
  "connections": {
    "GPT-4o-mini Model": {
      "ai_languageModel": [
        [
          {
            "node": "Process User Query",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Process User Query": {
      "main": [
        [
          {
            "node": "Generate Voice Response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Voice Assistant UI": {
      "main": [
        [
          {
            "node": "Send HTML Interface",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Conversation Memory": {
      "ai_memory": [
        [
          {
            "node": "Process User Query",
            "type": "ai_memory",
            "index": 0
          }
        ]
      ]
    },
    "Generate Voice Response": {
      "main": [
        [
          {
            "node": "Send Audio Response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Voice Interface Endpoint": {
      "main": [
        [
          {
            "node": "Voice Assistant UI",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Audio Processing Endpoint": {
      "main": [
        [
          {
            "node": "Process User Query",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
常见问题

如何使用这个工作流?

复制上方的 JSON 配置代码,在您的 n8n 实例中创建新工作流并选择「从 JSON 导入」,粘贴配置后根据需要修改凭证设置即可。

这个工作流适合什么场景?

中级 - 客服机器人, AI 聊天机器人

需要付费吗?

本工作流完全免费,您可以直接导入使用。但请注意,工作流中使用的第三方服务(如 OpenAI API)可能需要您自行付费。

工作流信息
难度等级
中级
节点数量14
分类2
节点类型8
难度说明

适合有一定经验的用户,包含 6-15 个节点的中等复杂度工作流

外部链接
在 n8n.io 查看

分享此工作流