目录1. 项目背景与整体架构项目需求整体技术链路2. 环境依赖安装FunASR官方文档3. 完整前后端源码后端 asr_api.py前端 index.html自动适配 http/ws、https/wss4. 启动运行方式局域网访问方案推荐首选5. 部署踩坑全记录核心章节问题1PyAudio OSError: [Errno -9999] Unanticipated host error报错原因最终解决方案问题2WebSocket连接成功浏览器不弹出麦克风授权完全无法收音根本原因方案A局域网最优零证书、零配置强烈推荐方案Bmkcert生成自签名HTTPS证书问题3浏览器访问https提示Invalid HTTP request received.原因问题4WinError 10054 远程主机强迫关闭了一个现有的连接现象原因解决办法问题5WebSocket显示[accepted]说话但是页面没有任何文字排查步骤6. 功能扩展语音识别对接大模型问答7. 总结1. 项目背景与整体架构项目需求搭建局域网实时流式语音字幕服务。最初方案后端通过PyAudio调用本机麦克风录音但是部署到无声卡、无音频硬件的台式机时频繁报错。最终改造方案把麦克风采集迁移到浏览器前端后端只通过WebSocket接收二进制音频流不再依赖任何声卡硬件。整体技术链路前端网页浏览器调用麦克风生成 16000Hz 单声道16位PCM音频WebSocket长连接二进制音频上行识别文字实时下发后端服务FunASR paraformer流式模型做语音识别增强逻辑静音停顿0.6秒自动清空字幕并重置模型缓存避免上下文串话。2. 环境依赖安装pipinstallfunasrFunASR官方文档注意如果只是跑语音识别建议降到 Python3.9 ~ Python3.12funasr 兼容性最好使用Python3.13 就会报下面这个错误使用 pipinstallfunasr 安装时报错 FileC:\Users\Administrator\AppData\Local\Temp\pip-build-env-fr3svpmz\overlay\Lib\site-packages\pdm\backend\hooks\setuptools.py, line92,in_build_lib raise BuildError(fError occurs when running {build_args}:\n{e})pdm.backend.exceptions.BuildError: Error occurs when running[C:\\Python313\\python.exe,C:\\Users\\Administrator\\AppData\\Local\\Temp\\pip-install-ozs44hc3\\editdistance_ecb96c8585a54a4485377adc1e68862b\\setup.py,build,-b,C:\\Users\\ADMINI~1\\AppData\\Local\\Temp\\pdm-build-7bg3n_hl]: Command[C:\\Python313\\python.exe, C:\\Users\\Administrator\\AppData\\Local\\Temp\\pip-install-ozs44hc3\\editdistance_ecb96c8585a54a4485377adc1e68862b\\setup.py, build, -b, C:\\Users\\ADMINI~1\\AppData\\Local\\Temp\\pdm-build-7bg3n_hl]returned non-zeroexitstatus1.[end of output]note: This error originates from a subprocess, and is likely not a problem with pip. ERROR: Failed building wheelforeditdistance Failed to build editdistance error: failed-wheel-build-for-install × Failed to build installable wheelsforsome pyproject.toml based projects ╰─editdistance3. 完整前后端源码后端 asr_api.py# -*- coding: utf-8 -*- Created on 2026/6/25 16:12 creator er_nao File asr_api.py Description fromfastapiimportFastAPI,HTTPException,WebSocketfromfastapi.staticfilesimportStaticFilesfromfastapi.responsesimportFileResponsefrompydanticimportBaseModelfromfunasrimportAutoModelimportpyaudioimportasyncioimportthreadingimportosimportsysimporttime# 屏蔽进度条os.environ[TQDM_DISABLE]1devnullopen(os.devnull,w)sys.stdoutdevnull sys.stderrdevnull appFastAPI()# 挂载当前目录为静态资源目录app.mount(/static,StaticFiles(directory.),namestatic)# 访问根路径自动打开 index.htmlapp.get(/)asyncdefindex():returnFileResponse(index.html)# ---------------------- 加载模型 ----------------------# 音频url转文字模型modelAutoModel(modelparaformer-zh,vad_modelfsmn-vad,punc_modelct-punc)# 加载实时流式模型asr_modelAutoModel(modelparaformer-zh-streaming,disable_updateTrue)CHUNK960RATE16000chunk_size[0,10,5]# 消息队列 客户端连接池# text_queue asyncio.Queue()# client_set set()# main_loop: asyncio.AbstractEventLoop None# 定义请求体格式只接收音频urlclassAudioUrlRequest(BaseModel):audio_url:str 音频链接转文字 app.post(/asr)asyncdefspeech_to_text(req:AudioUrlRequest):try:# 直接把网络地址传给model.generateresmodel.generate(inputreq.audio_url)return{code:200,text:res[0][text]}exceptExceptionase:raiseHTTPException(status_code500,detailstr(e)) 实时语音转文字WebSocket接口 app.websocket(/ws)asyncdefhandle_audio_stream(websocket:WebSocket):awaitwebsocket.accept()print(客户端已连接)cache{}sentence_bufferlast_voice_timetime.time()silence_timeout1try:whileTrue:try:pcm_dataawaitwebsocket.receive_bytes()print(f收到音频字节{len(pcm_data)})# 打印收到的数据长度exceptException:print(连接断开)breaknowtime.time()resasr_model.generate(inputpcm_data,cachecache,is_finalFalse,chunk_size[0,10,5])print(识别结果,res)# 打印模型输出ifresandres[0][text].strip():textres[0][text].strip()sentence_buffertext last_voice_timenowawaitwebsocket.send_text(sentence_buffer)ifsentence_bufferand(now-last_voice_timesilence_timeout):sentence_buffercache.clear()try:awaitwebsocket.send_text()except:passfinally:cache.clear()if__name____main__:importuvicorn# 开启https局域网IP直接变成安全网站uvicorn.run(asr_api:app,host0.0.0.0,port8088,ssl_keyfile./key.pem,ssl_certfile./cert.pem)前端 index.html自动适配 http/ws、https/wss!DOCTYPEhtmlhtmlheadmetacharsetutf-8title实时字幕/title/headbodydivstylefont-size:48px;padding:20px;idsubtitle/divscriptconstwsnewWebSocket(wss://${location.host}/ws);constsubtitleDomdocument.getElementById(subtitle);ws.onmessagee{subtitleDom.innerTexte.data;};asyncfunctionstartRecord(){try{conststreamawaitnavigator.mediaDevices.getUserMedia({audio:{sampleRate:16000,channelCount:1}});constctxnewAudioContext({sampleRate:16000});constsrcctx.createMediaStreamSource(stream);constprocessorctx.createScriptProcessor(1024,1,1);src.connect(processor);processor.connect(ctx.destination);processor.onaudioprocessevt{constinputevt.inputBuffer.getChannelData(0);constbuffernewInt16Array(input.length);for(leti0;iinput.length;i){letsMath.max(-1,Math.min(1,input[i]));buffer[i]s0?s*0x8000:s*0x7FFF;}if(ws.readyStateWebSocket.OPEN){ws.send(buffer.buffer);}};}catch(err){console.error(麦克风权限失败,err);}}startRecord();/script/body/html4. 启动运行方式把两个文件放在同一文件夹打开PowerShell执行命令uvicorn asr_api:app--host 0.0.0.0--port 8088局域网访问方案推荐首选查询服务端本机局域网IPipconfig配置Windows hosts实现内网安全域名解决麦克风权限拦截下文详细说明。5. 部署踩坑全记录核心章节问题1PyAudio OSError: [Errno -9999] Unanticipated host error报错原因后端使用PyAudio读取本机麦克风服务器无声卡、无音频输入设备直接打开流失败。最终解决方案彻底删除PyAudio录音代码改为前端浏览器采集音频后端只接收网络二进制流。后端不再依赖任何音频硬件彻底根除该异常。问题2WebSocket连接成功浏览器不弹出麦克风授权完全无法收音根本原因浏览器安全策略http://192.168.x.x内网IP属于非安全上下文Chrome直接禁用麦克风API不会弹出授权窗口。方案A局域网最优零证书、零配置强烈推荐使用.local内网域名绕过安全限制用管理员权限打开记事本编辑hosts文件C:\Windows\System32\drivers\etc\hosts在文件末尾添加一行192.168.0.110 asr.local浏览器访问地址http://asr.local:8088浏览器自动把.local域名判定为内网安全域HTTP环境下正常弹出麦克风权限。方案Bmkcert生成自签名HTTPS证书下载mkcert.exe执行命令安装根证书.\mkcert.exe-install为内网IP生成证书.\mkcert.exe xxx.xxx.x.xxx(你的电脑ip地址 cmd弹出黑框 输入ipconfig就能看到)执行完毕文件夹会多出 2 个文件xxx.xxx.x.xxx-key.pem 私钥xxx.xxx.x.xxx.pem 证书文件把两个文件改名为key.pemcert.pem复制到你的 asr_api.py 同一个文件夹。带证书启动HTTPS服务uvicorn asr_api:app--host 0.0.0.0--port 8088--ssl-keyfile key.pem--ssl-certfile cert.pem重要HTTPS网页中WebSocket必须使用wss://继续使用ws://会被浏览器拦截二进制数据流。问题3浏览器访问https提示Invalid HTTP request received.原因服务没有加载SSL证书仍然是普通HTTP服务浏览器发起HTTPS握手协议不匹配。只有启动命令带上--ssl-keyfile与--ssl-certfile服务才是HTTPS服务。问题4WinError 10054 远程主机强迫关闭了一个现有的连接现象页面刷新、麦克风暂停后控制台抛出连接重置异常。原因浏览器主动断开套接字Windows系统强制关闭TCP连接asyncio抛出警告。解决办法对receive_bytes()增加内层异常捕获优雅退出循环全局屏蔽asyncio警告日志logging.getLogger(asyncio).setLevel(logging.CRITICAL)问题5WebSocket显示[accepted]说话但是页面没有任何文字排查步骤HTTPS页面必须使用wss://ws://会被浏览器静默拦截二进制音频浏览器禁止页面自动调用麦克风录音逻辑必须写在按钮点击事件内音频参数必须严格为16000Hz、单声道、Int16 PCM在后端打印接收到的字节长度确认前端音频流正常传输。6. 功能扩展语音识别对接大模型问答FunASR只负责语音转文字本身不具备对话问答能力。有两种扩展方案FunASR一体化语音对话模型LLMASR、Qwen/Qwen3-ASR-0.6B输入音频直接返回回答文本模块化架构Paraformer识别语音文本 → 调用本地轻量化Qwen小模型生成回答 → 把答案通过WebSocket推送回前端页面。原有WebSocket接口完全不用改动可直接升级为语音对话机器人。演示视频7. 总结架构优化前端拾音后端纯服务彻底消灭PyAudio声卡硬件报错权限最优解Hosts配置.local内网域名不用给所有访客安装证书、修改浏览器配置协议严格区分http对应wshttps对应wss避免二进制音频被浏览器拦截完善异常捕获解决Windows套接字10054强制断开警告增加静音自动清空字幕逻辑大幅提升流式识别体验。