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>
<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>