Qwen大模型根据医患对话录音生成病例
- 业务背景
- 涉及前端技术
- 涉及后端技术
- 阿里云文档
- 完整代码(复制即可运行)
业务背景
在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><inputtype="password"id="appkey"value="你的阿里智能语音应用的Appkey"placeholder=""/></div><div><label for="token">Token:</label><inputtype="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", // 设置请求方法为POSTheaders: {"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";}// 生成 UUIDfunction 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>