阿里qwen大模型AI智能分析实时对话生成病例的DEMO

发布于:2025-03-22 ⋅ 阅读:(35) ⋅ 点赞:(0)

业务背景

在HIS或者其他医疗系统中,为了提高医生的现场或者线上问诊工作效率,在系统的开病例这块可以通过对话录音,实时的把录音内容通过Qwen大模型直接生成病例数据,即医患对话完成即可生成病例,避免医生在手动开病例

涉及前端技术

  • 音频流(数据转化)
  • websocket
  • 有可能涉及到nginx解决跨域问题

涉及后端技术

  • nodejs
  • nginx配置

阿里云文档

  • https://help.aliyun.com/zh/isi/developer-reference/sdk-for-node-js?spm=a2c4g.11186623.help-menu-30413.d_3_0_1_6.4182626bmyHNeU&scm=20140722.H_410564._.OR_help-T_cn~zh-V_1

完整代码(复制即可运行)

  • HTML
	 <h1>实时语音识别</h1>
    <div>
      <label for="appkey">AppKey:</label>
      <input
        type="password"
        id="appkey"
        value="你的阿里智能语音应用的Appkey"
        placeholder=""
      />
    </div>
    <div>
      <label for="token">Token:</label>
      <input
        type="password"
        id="token"
        value="你的阿里智能语音应用的token"
        placeholder="请输入 Token"
      />
    </div>
    <div id="status">未连接</div>
    <div id="messages"></div>
    <button onclick="connectWebSocket()">开始连接</button>
    <button onclick="startRecording()" disabled id="startButton">
      开始录音
    </button>
    <button onclick="stopRecording()" disabled id="stopButton">停止录音</button>
    <button onclick="disconnectWebSocket()" disabled id="disconnectButton">
      断开连接
    </button>
  • CSS
  <style>
      body {
        font-family: Arial, sans-serif;
        margin: 20px;
      }
      #status {
        margin-bottom: 10px;
        color: green;
      }
      #messages {
        border: 1px solid #ccc;
        padding: 10px;
        height: 200px;
        overflow-y: scroll;
        margin-bottom: 10px;
      }
      button {
        margin: 5px;
      }
    </style>
  • JS
    <script>
      let websocket;
      let audioContext;
      let scriptProcessor;
      let audioInput;
      let audioStream;
      let allText = "";
      let lastIndex = 1;
      let currentDialogueArr = [];

      const aiCase = (caseDescription) => {
        var url = "你自己的AI后端接口";
        var data = {
          caseDescription,
        };

        fetch(url, {
          method: "POST", // 设置请求方法为POST
          headers: {
            "Content-Type": "application/json", // 设置请求头信息
            "Accept-Encoding": "gzip, deflate, br",
            Connection: "keep-alive",
            Accept: "*/*",
            "User-Agent": "PostmanRuntime/7.37.3",
            Authorization:"你自己的Authorization",
          },
          body: JSON.stringify(data), // 将JavaScript对象转换为JSON字符串并作为请求体发送
        })
          .then((response) => response.json()) // 解析响应为JSON格式
          .then((data) => {
            // 请求成功,处理返回的数据
            console.log("解析数据", data);
            logMessage("AI病例最终解析: " + JSON.stringify(data));
          })
          .catch((error) => {
            // 处理请求错误
            console.error("Error:", error);
          });
      };

      // 更新连接状态
      function updateStatus(status) {
        document.getElementById("status").textContent = status;
        document.getElementById("status").style.color =
          status === "已连接" ? "green" : "red";
      }

      // 生成 UUID
      function generateUUID() {
        return ([1e7] + -1e3 + -4e3 + -8e3 + -1e11)
          .replace(/[018]/g, (c) =>
            (
              c ^
              (crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (c / 4)))
            ).toString(16)
          )
          .replace(/-/g, "");
      }

      // 打开WebSocket连接
      function connectWebSocket() {
        const appkey = document.getElementById("appkey").value;
        const token = document.getElementById("token").value;
        const socketUrl = `wss://nls-gateway.cn-shanghai.aliyuncs.com/ws/v1?token=${token}`;

        websocket = new WebSocket(socketUrl);
        websocket.onopen = function () {
          updateStatus("已连接");
          logMessage("连接到 WebSocket 服务器");

          var startTranscriptionMessage = {
            header: {
              appkey: appkey,
              namespace: "SpeechTranscriber",
              name: "StartTranscription",
              task_id: generateUUID(),
              message_id: generateUUID(),
            },
            payload: {
              format: "pcm",
              sample_rate: 16000,
              enable_intermediate_result: true,
              enable_punctuation_prediction: true,
              enable_inverse_text_normalization: true,
            },
          };

          websocket.send(JSON.stringify(startTranscriptionMessage));
        };

        const concatText = (data) => {
          if (data.payload && data.payload.index) {
            let index = data.payload.index;
            if (index === 1) {
              currentDialogueArr.push(data.payload);
            } else if (index === lastIndex) {
              currentDialogueArr.push(data.payload);
            }

            if (index > lastIndex) {
              lastIndex++;
              allText +=
                currentDialogueArr[currentDialogueArr.length - 1]?.result;
              currentDialogueArr = [];
            }
          }
          console.log("结果", allText);
        };
        websocket.onmessage = function (event) {
          // logMessage("服务端: " + event.data);
          const message = JSON.parse(event.data);
          concatText(message);

          if (message.header.name === "TranscriptionStarted") {
            // 启用开始录音按钮
            document.getElementById("startButton").disabled = false;
            document.getElementById("stopButton").disabled = false;
          }
        };

        websocket.onerror = function (event) {
          updateStatus("错误");
          logMessage("WebSocket 错误: " + event);
        };

        websocket.onclose = function () {
          updateStatus("断开连接");
          logMessage("与 WebSocket 服务器断开");
        };

        document.getElementById("disconnectButton").disabled = false;
      }

      // 断开WebSocket连接
      function disconnectWebSocket() {
        if (websocket) {
          websocket.close();
        }
        document.getElementById("disconnectButton").disabled = true;
        updateStatus("未连接");
      }

      // 日志消息
      function logMessage(message) {
        const messagesDiv = document.getElementById("messages");
        const messageElement = document.createElement("div");
        messageElement.textContent = message;
        messagesDiv.appendChild(messageElement);
        messagesDiv.scrollTop = messagesDiv.scrollHeight;
      }

      // 开始录音
      let inputData16 = null;
      async function startRecording() {
        try {
          // 获取音频输入设备
          audioStream = await navigator.mediaDevices.getUserMedia({
            audio: true,
          });
          audioContext = new (window.AudioContext || window.webkitAudioContext)(
            {
              sampleRate: 16000,
            }
          );
          audioInput = audioContext.createMediaStreamSource(audioStream);

          // 设置缓冲区大小为2048的脚本处理器
          scriptProcessor = audioContext.createScriptProcessor(2048, 1, 1);

          scriptProcessor.onaudioprocess = function (event) {
            const inputData = event.inputBuffer.getChannelData(0);
            inputData16 = new Int16Array(inputData.length);
            for (let i = 0; i < inputData.length; ++i) {
              inputData16[i] = Math.max(-1, Math.min(1, inputData[i])) * 0x7fff; // PCM 16-bit
            }
            if (websocket && websocket.readyState === WebSocket.OPEN) {
              websocket.send(inputData16.buffer);
              // logMessage("发送音频数据块");
            }
          };

          audioInput.connect(scriptProcessor);
          scriptProcessor.connect(audioContext.destination);
        } catch (e) {
          logMessage("录音失败: " + e);
        }
      }

      // 停止录音
      function stopRecording() {
        //加上最后一句
        allText += currentDialogueArr[currentDialogueArr.length - 1]?.result;
        console.log("加上最后一句", allText);
        // 发送给AI大模型
        aiCase(allText);

        if (scriptProcessor) {
          scriptProcessor.disconnect();
        }
        if (audioInput) {
          audioInput.disconnect();
        }
        if (audioStream) {
          audioStream.getTracks().forEach((track) => track.stop());
        }
        if (audioContext) {
          audioContext.close();
        }
        document.getElementById("startButton").disabled = true;
        document.getElementById("stopButton").disabled = true;
      }
    </script>