Node.js后端调用SenseVoice-Small:构建实时语音转文字服务

📅 发布时间:2026/7/13 15:57:09 👁️ 浏览次数:
Node.js后端调用SenseVoice-Small:构建实时语音转文字服务
Node.js后端调用SenseVoice-Small构建实时语音转文字服务最近在做一个需要实时语音识别的项目比如在线会议转录或者语音助手发现直接调用云端API不仅贵延迟还高。后来了解到像SenseVoice-Small这样的开源模型如果能部署在自己的服务器上成本可控延迟也能做到非常低。但怎么把Node.js后端、WebSocket和语音模型串起来实现一个端到端的实时服务这里面有不少坑。今天我就以一个全栈开发者的视角带你走一遍完整的流程。从Node.js环境准备开始到搭建WebSocket服务、处理音频流、调用模型最后把文字结果实时推给前端。整个过程就像搭积木我们一块一块来。1. 环境准备搭建Node.js舞台工欲善其事必先利其器。我们首先得把Node.js环境给准备好这是所有后续工作的基础。1.1 Node.js安装及环境配置现在安装Node.js已经非常方便了。我推荐直接去Node.js官网下载长期支持版本稳定性有保障。安装过程就是一路下一步没什么特别的。安装完成后打开你的终端输入下面这两条命令验证一下node --version npm --version如果能看到版本号比如v18.17.0和9.6.7那就说明安装成功了。接下来我们创建一个项目文件夹并初始化它mkdir realtime-speech-service cd realtime-speech-service npm init -y这个-y参数的意思是直接使用默认配置省得我们一路回车。初始化完成后你会看到一个package.json文件它就像我们项目的身份证记录了项目信息和依赖。1.2 安装核心依赖包我们这个项目需要几个关键的“帮手”Express: 一个非常流行的Web框架用来搭建基础的HTTP服务器。ws: 一个简单好用的WebSocket库负责处理实时双向通信。Axios: 用来向部署好的SenseVoice-Small模型服务发送HTTP请求。Multer: 中间件负责处理前端上传的音频文件如果采用文件上传方式的话。Dotenv: 管理环境变量比如模型服务的地址、端口这些敏感信息我们不希望硬编码在代码里。在项目根目录下运行这条安装命令npm install express ws axios multer dotenv安装完成后你的package.json文件的dependencies部分应该能看到这些包。1.3 准备模型服务这是最关键的一步。你需要先在星图GPU平台或其他支持的环境上把SenseVoice-Small模型部署起来。这个过程通常涉及选择镜像、配置资源、启动服务。假设你部署成功后模型服务提供了一个HTTP API接口地址是http://your-model-server:8000/v1/audio/transcriptions。这个接口接收音频数据返回识别出的文字。请记下你的服务地址和端口我们后面会用到。2. 构建WebSocket通信桥梁实时语音转文字核心在于“实时”。HTTP协议一问一答的模式不太适合而WebSocket可以建立一条持久连接允许服务器和客户端随时互相推送数据正好满足我们的需求。2.1 创建基础服务器我们在项目根目录创建一个server.js文件作为我们服务的主入口。首先引入必要的模块并创建一个Express应用和HTTP服务器// server.js const express require(express); const http require(http); const WebSocket require(ws); const axios require(axios); require(dotenv).config(); // 加载环境变量 const app express(); const server http.createServer(app); const wss new WebSocket.Server({ server }); // 从环境变量获取模型服务地址如果没有则使用默认值 const MODEL_API_URL process.env.MODEL_API_URL || http://localhost:8000/v1/audio/transcriptions; // 提供一个简单的静态页面方便测试 app.get(/, (req, res) { res.send( html body h2实时语音转文字测试页/h2 input typefile idaudioFile acceptaudio/* / button onclicksendAudio()上传并转写/button div idresult/div script const ws new WebSocket(ws:// window.location.host); ws.onmessage (event) { document.getElementById(result).innerHTML p event.data /p; }; function sendAudio() { const file document.getElementById(audioFile).files[0]; if (file) { ws.send(file); } } /script /body /html ); });这段代码做了几件事创建了WebSocket服务器定义了模型API地址并提供了一个极其简陋的前端页面用于选择音频文件并通过WebSocket发送。2.2 处理WebSocket连接与音频流现在我们来处理客户端的连接和它发来的音频数据。在server.js中接着添加// 存储活跃连接和对应的音频数据缓冲区如果需要流式拼接 const clients new Map(); wss.on(connection, (ws, req) { console.log(新的客户端连接); const clientId Date.now().toString(); // 简单生成一个客户端ID clients.set(ws, { id: clientId, buffer: [] }); ws.on(message, async (message) { // 假设前端发送的是ArrayBuffer格式的音频数据 if (message instanceof Buffer || message instanceof ArrayBuffer) { console.log(收到来自 ${clientId} 的音频数据块大小: ${message.byteLength} 字节); try { // 这里进行关键操作调用语音识别模型 const transcription await callSpeechToTextAPI(message); // 将识别结果实时推回给对应的客户端 ws.send(识别结果: ${transcription}); } catch (error) { console.error(处理客户端 ${clientId} 的音频时出错:, error); ws.send(识别服务暂时不可用); } } else { console.log(收到非音频消息:, message.toString()); ws.send(服务器期望接收音频数据); } }); ws.on(close, () { console.log(客户端 ${clientId} 断开连接); clients.delete(ws); }); ws.on(error, (error) { console.error(客户端 ${clientId} 连接错误:, error); }); });这里我们为每个新连接创建一个简单的上下文用来存储信息。当收到二进制消息我们约定为音频数据时就调用一个函数去处理识别并把结果发回去。3. 对接SenseVoice-Small模型服务桥梁搭好了现在要把数据送到“大脑”模型里去处理。3.1 封装模型调用函数上面代码中的callSpeechToTextAPI函数是我们与模型交互的核心。我们来实现它。在server.js文件顶部附近添加这个函数/** * 调用部署好的SenseVoice-Small模型进行语音识别 * param {Buffer|ArrayBuffer} audioData - 音频二进制数据 * returns {Promisestring} - 识别出的文本 */ async function callSpeechToTextAPI(audioData) { // 注意SenseVoice-Small的API可能要求特定的格式。 // 这里假设它接受multipart/form-data格式字段名为file且音频为wav格式。 // 你需要根据实际模型的API文档进行调整。 const formData new FormData(); // 在Node.js环境中我们需要使用form-data包或Buffer直接发送。 // 这里我们构造一个Buffer并设置合适的请求头。 const buffer Buffer.from(audioData); try { const response await axios.post(MODEL_API_URL, buffer, { headers: { Content-Type: audio/wav, // 根据你的音频格式调整如 audio/webm, audio/mpeg // 如果你的模型API需要其他认证头比如Authorization在这里添加 // Authorization: Bearer ${process.env.API_KEY} }, // 设置超时时间避免长时间等待 timeout: 30000, // 30秒 }); // 假设模型返回的JSON结构为 { text: 识别出的文字 } // 你需要根据模型的实际响应格式来解析 if (response.data response.data.text) { return response.data.text; } else { throw new Error(模型返回格式异常: JSON.stringify(response.data)); } } catch (error) { console.error(调用语音识别模型失败:, error.message); if (error.response) { // 模型服务返回了错误状态码 console.error(模型服务响应错误:, error.response.status, error.response.data); throw new Error(模型服务错误: ${error.response.status}); } else if (error.request) { // 请求发出了但没有收到响应 throw new Error(无法连接到模型服务请检查网络和地址); } else { // 其他错误 throw error; } } }重要提示这个函数是示例最关键的是headers里的Content-Type和请求体的格式。SenseVoice-Small模型可能要求音频是特定的采样率如16kHz、位深如16bit和格式如WAV。你很可能需要在调用API前用类似ffmpeg的工具在前端或后端对音频进行预处理。3.2 处理音频格式与流式传输对于真正的“流式”识别模型需要支持边接收音频流边返回中间结果。如果SenseVoice-Small提供流式API例如通过WebSocket或SSE那么我们的服务架构需要调整。模型支持流式那么我们的Node.js服务应该作为“中继”将客户端的WebSocket音频流转发到模型的流式API同时将模型的中间结果流式地转发回客户端。模型仅支持整段音频就像我们上面实现的客户端需要录制完一段音频或分片发送我们收集完一个完整的片段后一次性发送给模型识别。这对于近实时场景如每2-3秒发送一次也是可行的。假设我们采用第二种分片方式客户端每采集2秒音频就发送一次。那么服务器端的callSpeechToTextAPI函数就会被频繁调用。为了性能考虑你可能需要使用连接池管理到模型服务的HTTP连接。对非常短的音频分片进行缓存和合并减少不必要的模型调用。4. 完善与错误处理一个健壮的服务必须能妥善处理各种异常情况。4.1 增强WebSocket服务我们在server.js的启动部分添加错误处理和优雅关闭// 启动服务器 const PORT process.env.PORT || 3000; server.listen(PORT, () { console.log(服务已启动HTTP监听端口: ${PORT}); console.log(WebSocket服务运行在 ws://localhost:${PORT}); console.log(模型服务地址: ${MODEL_API_URL}); }); // 优雅关闭 process.on(SIGINT, () { console.log(正在关闭服务器...); wss.clients.forEach((client) { if (client.readyState WebSocket.OPEN) { client.close(); } }); server.close(() { console.log(服务器已关闭); process.exit(0); }); });4.2 创建环境变量文件在项目根目录创建.env文件存放你的配置# .env PORT3000 MODEL_API_URLhttp://your-model-server-ip:8000/v1/audio/transcriptions # API_KEYyour_secret_key_if_needed切记要将.env文件添加到.gitignore中避免敏感信息泄露。4.3 项目结构整理一个清晰的项目结构有助于维护。最终你的项目可能看起来像这样realtime-speech-service/ ├── node_modules/ ├── .env # 环境变量本地不上传git ├── .gitignore # 忽略node_modules和.env ├── package.json ├── package-lock.json └── server.js # 主服务文件5. 测试与运行一切就绪让我们来测试一下。启动你的SenseVoice-Small模型服务确保它在http://your-model-server:8000正常运行。在项目目录下启动Node.js服务node server.js看到控制台输出监听端口和模型地址信息。打开浏览器访问http://localhost:3000。使用网页上的按钮选择一个短的音频文件如一段.wav格式的“你好世界”进行上传。观察页面上的#result区域应该能很快看到服务器返回的识别文字。如果遇到问题首先检查模型服务地址是否正确、可达。音频格式是否符合模型要求。浏览器控制台和Node.js服务控制台的错误日志。6. 总结与展望走完这一趟我们实现了一个虽然基础但功能完整的实时语音转文字服务原型。核心在于利用Node.js搭建了一个WebSocket中继负责在浏览器和语音识别模型之间高效地传递数据。实际生产环境中还需要考虑更多音频预处理在调用模型前确保音频采样率、格式、音量标准化。性能与扩展当用户量上来单个Node.js进程和直连模型可能成为瓶颈。需要考虑引入消息队列如Redis来解耦WebSocket服务和模型调用工作进程甚至对模型服务做负载均衡。安全增加WebSocket连接认证防止恶意连接耗尽资源。前端优化实现一个真正的音频流采集使用WebRTC的MediaRecorderAPI并实现“边说边出字”的流畅体验。这个项目最大的价值是给了你一个完全可控的、低成本的实时语音识别解决方案底座。你可以基于它去探索更复杂的场景比如多语种识别、带标点的格式化输出、或者与你的业务逻辑深度集成。动手试试吧从让服务器成功识别出第一句“你好”开始那种成就感还是挺棒的。获取更多AI镜像想探索更多AI镜像和应用场景访问 CSDN星图镜像广场提供丰富的预置镜像覆盖大模型推理、图像生成、视频生成、模型微调等多个领域支持一键部署。