不再被 FunASR 官方服务折磨:基于 FastAPI 构建生产级中文语音识别服务

📅 2026/7/10 21:26:52
不再被 FunASR 官方服务折磨:基于 FastAPI 构建生产级中文语音识别服务
不再被 FunASR 官方服务折磨基于 FastAPI 构建生产级中文语音识别服务当 FunASR 官方 runtime 服务在内网环境反复崩溃我决定绕过它的服务层只取用它的模型推理能力自建一套真正可部署的 ASR 服务。项目地址github仓库 | gitee仓库一、项目背景FunASR 好用但它的服务层不行FunASR 是阿里达摩院开源的语音识别工具包其中的Paraformer-large模型是目前中文 ASR 领域效果最好的开源模型之一。然而当你尝试将 FunASR 部署为生产服务时会遇到一系列令人头疼的问题。FunASR 官方服务的典型踩坑场景在实际生产环境中我们尝试使用 FunASR 官方提供的runtime服务遇到了以下问题问题具体表现启动即崩溃官方 runtime 服务依赖 grpc asyncio在不同环境中频繁出现ImportError、grpc版本冲突文档与代码脱节README 中的部署步骤与实际代码逻辑不一致照着做经常报错联网行为无法关闭服务启动时强制检查更新、拉取模型元信息内网环境直接卡死或超时模型管理缺失没有提供运行时加载/卸载模型的 API无法动态管理模型生命周期错误处理粗糙遇到异常音频或超长音频时服务直接崩溃而非返回结构化错误信息解决思路取其精华自建服务层FunASR 的模型是可靠的问题出在它的服务层代码。所以我们的方案很直接传统方案你的应用 → FunASR runtime 服务grpc问题多→ 模型 本项目 你的应用 → Fun-ASR ServerFastAPI稳定→ funasr Python 库 → 模型Fun-ASR Server只使用 FunASR 的funasrPython 推理库不碰它有问题的服务层。我们用成熟的 FastAPI 框架重新构建了完整的 RESTful 服务解决了稳定性、内网部署、错误处理等全部痛点。二、核心功能与技术实现2.1 技术架构总览┌──────────────────────────────────────┐ │ FastAPI (Port 9000) │ │ ┌────────────┐ ┌──────────────┐ │ HTTP Request ────────►│ │ Routes │──►│ Schemas │ │ │ └──────┬─────┘ └──────────────┘ │ │ │ │ │ ┌──────▼──────────────────────┐ │ │ │ ModelManager │ │ │ │ (注册表 生命周期管理) │ │ │ │ │ │ │ │ ┌────────────────────┐ │ │ │ │ │ ParaformerZHModel │ │ │ │ │ │ ┌─────────────────┐ │ │ │ │ │ │ │ funasr.AutoModel│ │ │ │ │ │ │ │ ├─ ASR 主模型 │ │ │ │ │ │ │ │ ├─ VAD 分段 │ │ │ │ │ │ │ │ └─ Punc 标点 │ │ │ │ │ │ │ └─────────────────┘ │ │ │ │ │ └────────────────────┘ │ │ │ └─────────────────────────────┘ │ └──────────────────────────────────────┘整个架构分为四层API 层app/api/— FastAPI 路由处理 HTTP 请求/响应管理层app/core/— 模型注册表 生命周期管理模型层app/models/— 具体的 ASR 模型实现目前为 Paraformer-zh推理层—funasr.AutoModel封装了 ASR VAD 标点三个子模型2.2 抽象基类 注册表可扩展的模型管理项目最核心的设计是BaseASRModel 抽象基类 ModelManager 注册表模式。任何新的 ASR 模型只需实现四个方法注册一行代码即可获得完整的 API 支持。抽象基类定义# app/models/base.pyclassBaseASRModel(ABC):语音识别模型抽象基类。propertyabstractmethoddefname(self)-str:模型唯一标识例如 paraformer-zh。...propertyabstractmethoddefis_loaded(self)-bool:模型是否已加载到内存。...abstractmethoddefload(self)-None:加载模型权重到设备。...abstractmethoddefunload(self)-None:卸载模型并释放 GPU 显存。...abstractmethoddeftranscribe(self,audio_path:str,**kwargs:Any)-str:对音频文件执行语音识别返回识别文本。...注册表管理器# app/core/manager.py_MODEL_REGISTRY:Dict[str,Type[BaseASRModel]]{paraformer-zh:ParaformerZHModel,}classModelManager:def__init__(self,device:str|NoneNone):self.devicedeviceorsettings.DEVICE self._instances:Dict[str,BaseASRModel]{}defload(self,name:str|NoneNone)-BaseASRModel:加载指定模型默认加载 DEFAULT_MODEL。namenameorsettings.DEFAULT_MODEL instself._create_instance(name)ifnotinst.is_loaded:inst.load()returninstdeftranscribe(self,name:str,audio_path:str,**kwargs)-str:按模型名分发识别调用。instself._instances.get(name)ifinstisNoneornotinst.is_loaded:instself.load(name)returninst.transcribe(audio_path,**kwargs)这种设计的好处是解耦API 路由不需要知道具体用哪个模型只需通过ModelManager分发。未来添加 Whisper、SenseVoice 等新模型业务代码零改动。2.3 Paraformer 模型实现三合一推理Paraformer-zh 的实现亮点在于自动携带 VAD 和标点恢复子模型用户无需关心音频分段和标点添加# app/models/paraformer.pyclassParaformerZHModel(BaseASRModel):基于 FunASR paraformer-zh 的中文语音识别模型。 自动携带 VAD (fsmn-vad) 和标点 (ct-punc) 子模型。 defload(self)-None:self._modelAutoModel(modelself._model_id,# paraformer-zh 主模型vad_modelfsmn-vad,# 语音活动检测vad_kwargs{max_single_segment_time:60000},punc_modelct-punc,# 标点恢复deviceself._device,trust_remote_codeTrue,)deftranscribe(self,audio_path:str,**kwargs:Any)-str:# 1. ffmpeg 预处理转码 → 16kHz mono WAVwav_path,is_tempconvert_audio_to_wav(audio_path)# 2. 模型推理resultself._model.generate(inputwav_path,batch_size_s300)# 3. 文本后处理去除重复短语textresult[0].get(text,)textremove_repeated_text(text.strip())returntext三个子模型各司其职子模型功能为什么需要paraformer-zh语音 → 文字核心 ASR 模型fsmn-vad语音活动检测自动切分长音频每段最多 60s静音段不送入 ASR提升效率和准确率ct-punc标点恢复ASR 输出的纯文本无标点ct-punc 自动添加逗号、句号等提升可读性2.4 音频预处理与文本后处理项目中两个容易被忽视但非常实用的工具函数音频预处理— 非 WAV 格式自动通过 ffmpeg 转码defconvert_audio_to_wav(audio_path,sample_rate16000,monoTrue):将任意音频格式转换为 16kHz 单声道 WAV。cmd[ffmpeg,-y,-i,audio_path,-ar,str(sample_rate),-sample_fmt,s16]ifmono:cmd.extend([-ac,1])subprocess.run(cmd,checkTrue,capture_outputTrue,timeout120)这解决了一个常见痛点用户上传的音频格式五花八门mp3、m4a、ogg、flac…采样率也不固定。统一转码为 16kHz mono WAV 是 Paraformer 模型获得最佳识别效果的前提。文本后处理— 去除 ASR 常见的重复短语defremove_repeated_text(text,max_repeat3):去除识别结果中的重复文本。 例如 去去去那个那个那个地方 → 去那个地方 # 去除连续重复的短词1-4字符patternrf([\u4e00-\u9fff\w]{{1,4}})(\1){{{max_repeat},}}textre.sub(pattern,r\1*max_repeat,text)returntextASR 模型在处理口吃、犹豫语或短促重复发音时容易产生重复输出。这个正则清理函数能有效提升最终文本质量。2.5 内网离线部署三层防护机制这是项目最硬核的工程实践。内网环境无法访问公网但 FunASR/ModelScope 默认会尝试联网检查更新和下载模型导致服务启动失败。项目通过三层防护彻底解决┌─────────────────────────────────────────────────────────┐ │ 内网服务器无外网 │ │ │ │ ┌─── 防护层 1环境变量 ──────────────────────────────┐ │ │ │ MODELSCOPE_OFFLINE1 │ │ │ │ HF_HUB_OFFLINE1 │ │ │ │ TRANSFORMERS_OFFLINE1 │ │ │ └─────────────────────────────────────────────────────┘ │ │ ┌─── 防护层 2AutoModel 猴子补丁 ────────────────────┐ │ │ │ 强制 disable_updateTrue │ │ │ │ → 阻止 Check update of funasr │ │ │ └─────────────────────────────────────────────────────┘ │ │ ┌─── 防护层 3ModelScope 补丁 ───────────────────────┐ │ │ │ 强制 snapshot_download(local_files_onlyTrue) │ │ │ │ → 阻止 download models from model hub │ │ │ └─────────────────────────────────────────────────────┘ │ │ │ │ ┌─── Swagger UI 离线 ─────────────────────────────────┐ │ │ │ /docs 只加载本地 /static/swagger-ui/ CSS/JS │ │ │ │ → 不依赖 cdn.jsdelivr.net 等公网 CDN │ │ │ └─────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────┘关键实现在entrypoint.py中必须在同进程内执行猴子补丁subprocess 会使补丁失效# entrypoint.py简化版importos,sys# 1. 设置离线环境变量forkin[MODELSCOPE_OFFLINE,HF_HUB_OFFLINE,TRANSFORMERS_OFFLINE]:os.environ[k]1# 2. 猴子补丁 AutoModel阻止检查更新fromfunasrimportAutoModel _orig_initAutoModel.__init__def_patched_init(self,*args,**kwargs):kwargs[disable_update]True_orig_init(self,*args,**kwargs)AutoModel.__init___patched_init# 3. 猴子补丁 ModelScope阻止下载importmodelscope.hub.snapshot_downloadas_sd_mod _orig_sd_sd_mod.snapshot_downloaddef_patched_sd(*args,**kwargs):kwargs[local_files_only]Truereturn_orig_sd(*args,**kwargs)_sd_mod.snapshot_download_patched_sd# 4. 同进程启动应用关键fromapp.mainimportmain sys.argv[app.main]sys.argv[1:]main()三、快速上手3.1 Docker 一键部署GPU 模式推荐生产使用gitclone https://github.com/MLiueng/fun_asr.gitcdfun_asr# 构建并启动首次自动下载模型约 1.2GBdockercompose up-d--build# 查看日志等待模型加载完成dockercompose logs-ffun-asr# 看到以下日志即就绪# [paraformer-zh] Model loaded in 5.32s.# Uvicorn running on http://0.0.0.0:9000CPU 模式无 GPU 服务器cdcpu/dockercompose up-d--build3.2 API 调用示例文件上传识别curl-XPOST http://localhost:9000/api/asr\-Ffilemeeting-recording.wav响应{code:0,message:success,data:{text:今天我们讨论一下下季度的产品规划首先看看用户反馈的数据。,model:paraformer-zh,filename:meeting-recording.wav,file_size:1920000}}URL 方式识别服务端自动下载curl-XPOST http://localhost:9000/api/asr/url\-HContent-Type: application/json\-d{url: https://example.com/audio.wav}模型管理 API# 查询模型状态curlhttp://localhost:9000/api/models/status# 手动加载模型curl-XPOSThttp://localhost:9000/api/models/load?model_nameparaformer-zh# 卸载模型释放显存curl-XDELETEhttp://localhost:9000/api/models/unload?model_nameparaformer-zh# 健康检查curlhttp://localhost:9000/api/health3.3 Python 客户端集成importhttpxclassASRClient:def__init__(self,base_urlhttp://localhost:9000):self.base_urlbase_urldeftranscribe_file(self,file_path:str)-str:withopen(file_path,rb)asf:resphttpx.post(f{self.base_url}/api/asr,files{file:(file_path,f)},timeout120.0,)resp.raise_for_status()returnresp.json()[data][text]deftranscribe_url(self,url:str)-str:resphttpx.post(f{self.base_url}/api/asr/url,json{url:url},timeout120.0,)resp.raise_for_status()returnresp.json()[data][text]# 使用clientASRClient()textclient.transcribe_file(interview.mp3)print(f识别结果:{text})3.4 Swagger UI 交互文档浏览器访问http://localhost:9000/docs即可获得完整的交互式 API 文档支持在线测试所有接口。Swagger UI 的 CSS/JS 静态文件已内置在镜像中内网环境完全可用。四、项目亮点与应用场景4.1 核心亮点1. 稳定可靠的服务层用 FastAPI 替代 FunASR 官方的 grpc 服务所有异常都被捕获并返回结构化的错误响应不会因为单个异常音频导致服务崩溃。2. GPU/CPU 双模 内网离线一套代码两种部署模式。GPU 版基于nvidia/cuda:12.1.1兼容驱动 ≥ 525CPU 版基于python:3.11-slim仅 ~2GB。三层离线防护确保内网零外网依赖。3. 开箱即用的音频处理内置 ffmpeg 自动转码mp3/m4a/ogg/flac → 16kHz mono wav和文本去重后处理用户无需预处理音频也无需后处理识别结果。4. 可扩展的模型架构抽象基类 注册表模式添加新模型只需 2 步# 步骤 1实现模型类classWhisperModel(BaseASRModel):defname(self):returnwhisper-largedefis_loaded(self):returnself._modelisnotNonedefload(self):...defunload(self):...deftranscribe(self,audio_path,**kwargs):...# 步骤 2注册_MODEL_REGISTRY[whisper-large]WhisperModel注册后自动获得完整的 API 支持零业务代码改动。5. 完善的运维设计日志按天自动轮转仅保留当天 1 份备份模型缓存持久化到宿主机重启不重下Docker HEALTHCHECK 自动健康检查restart: unless-stopped异常自动恢复4.2 性能参考场景CPU (Xeon 8核)GPU (V100)模型加载~30-60s~5-10s10s 音频识别~3-5s~0.5s60s 音频识别~15-30s~2-4s内存/显存占用~2GB RAM~4GB VRAMGPU 模式下10 秒音频 0.5 秒识别完成实时率约 20x完全满足实时转写需求。4.3 典型应用场景会议录音转文字将会议录音上传到服务自动获得带标点的文字稿。VAD 自动分段处理 1 小时以上的长录音。# 批量处理会议录音forfileinrecordings/*.m4a;docurl-XPOST http://asr-server:9000/api/asr-Ffile$filedone客服质检对接呼叫中心系统将客户通话实时转写为文字用于质检分析和关键词检测。视频字幕生成提取视频音轨后调用 ASR 服务生成带标点的时间轴字幕文本。语音搜索 / 指令系统作为语音交互的前置模块将用户语音转为文字后送入 NLP 系统。内网/私有化部署金融、政务、医疗等对数据安全要求高的场景通过离线部署方案在内网环境运行语音数据不出网。4.4 与其他方案对比方案中文效果部署难度内网支持标点恢复开源协议Fun-ASR Server优秀低Docker 一键原生支持内置Apache 2.0FunASR 官方 runtime优秀高频繁报错不支持内置MITOpenAI Whisper一般中文中需自行处理无MIT百度/讯飞云 API优秀低不支持需联网内置商业五、总结Fun-ASR Server 证明了一件事好模型不等于好服务。FunASR 的 Paraformer 模型效果出色但官方服务层的工程质量令人失望。本项目的做法很简单取 FunASR 的模型推理能力funasrPython 包扔掉有问题的服务层用 FastAPI 重新构建一个稳定、可部署、可扩展的 ASR 服务。如果你也在寻找一个开箱即用、支持内网部署的中文语音识别服务欢迎试用GitHubhttps://github.com/MLiueng/fun_asrGiteehttps://gitee.com/mengl248/fun_asr