从零搭建离线语音识别服务:SenseVoice + sherpa-onnx 实战,支持普通话、粤语及多语种识别

0 阅读16分钟

image.png

image.png

image.png

image.png

一、项目背景:为什么要搭建离线语音识别服务?

在开发企业 AI 应用时,语音识别是一个比较重要的基础能力。

例如,在企业会议场景中,用户上传一段会议录音,系统需要完成:

  1. 将音频转换为文字。
  2. 自动识别普通话、粤语等不同语言。
  3. 自动添加标点符号。
  4. 区分不同发言人。
  5. 识别发言人身份。
  6. 将转写结果交给大模型,生成会议纪要、讨论结论和待办事项。

最初接触语音识别时,我也尝试过一些在线 ASR 服务。但在企业项目中,尤其是政务、国企和内网部署场景,音频数据往往不适合直接上传到第三方平台。

因此,我决定搭建一套本地离线语音识别服务。

这次实践主要采用:

  • SenseVoice:负责多语种语音识别。
  • sherpa-onnx:负责模型推理和语音服务运行。
  • FastAPI:封装 REST API。
  • WebSocket:提供语音识别服务接口。
  • Pyannote:扩展说话人分离能力。
  • 3D-Speaker:扩展声纹识别能力。

最终希望形成一套可以独立部署、供其他 AI 应用调用的语音服务。

本文记录实际部署过程、环境配置、服务启动和后续集成思路。


二、整体技术架构

先看一下整体结构。

                  TaskWeave AI 应用平台
                           |
                  会议助手 / 语音应用
                           |
                    TaskWeave ASR API
                           |
             +-------------+-------------+
             |                           |
        WebSocket                    REST API
         :6006                        :8088
             |                           |
             +-------------+-------------+
                           |
                    ASR 服务层
                           |
              +------------+------------+
              |            |            |
          SenseVoice    标点模型     音频处理
              |            |            |
              +------------+------------+
                           |
                    识别结果输出
                           |
              +------------+------------+
              |            |            |
           语音转写     自动标点     时间戳处理
              |
       +------+------+
       |             |
    说话人分离     声纹识别
       |             |
       +------+------+
              |
         会议助手业务
              |
      摘要 / 结论 / 待办

这里有一个需要注意的地方:

SenseVoice 主要负责语音识别,不应该把说话人分离、声纹识别等能力全部理解成同一个模型的功能。

实际项目中,我将这些能力拆分为不同的处理模块,方便独立部署、测试和后续替换。

三、服务器环境准备

本次使用的服务器环境如下:

项目配置
操作系统Anolis OS 8.10
系统架构x86_64
Python3.11
Python 环境Conda
环境名称taskweave-asr
推理框架sherpa-onnx 1.13.8
推理方式CPU
WebSocket6006
REST API8088

本次先以 CPU 推理为例,不依赖 NVIDIA GPU。

1. 检查系统环境

cat /etc/os-release

uname -m

python3 --version

检查系统架构:

uname -m

正常情况下应返回:

x86_64

2. 创建 Python 环境

如果服务器已经安装 Anaconda 或 Miniconda,可以创建独立环境:

conda create -n taskweave-asr python=3.11 -y

激活环境:

conda activate taskweave-asr

检查 Python 版本:

python --version

3. 安装 sherpa-onnx

安装指定版本:

pip install sherpa-onnx==1.13.8

安装 FastAPI 和 Uvicorn:

pip install fastapi uvicorn python-multipart

安装其他常用依赖:

pip install numpy soundfile librosa

检查安装结果:

python -c "import sherpa_onnx; print('sherpa-onnx installed')"

需要注意:不同系统架构、Python 版本和 CPU 指令集可能影响预编译包的兼容性。如果 pip 无法安装对应版本,需要先确认 sherpa-onnx 是否提供当前环境可用的发行包,再考虑源码编译。


四、部署 SenseVoice 多语种识别模型

这是整个服务最核心的部分。

我选择的是 SenseVoice 多语种模型:

SenseVoice zh-en-ja-ko-yue int8

模型支持的主要语言包括:

语言说明
zh中文普通话
en英语
ja日语
ko韩语
yue粤语

选择这个模型,主要是考虑到实际会议场景中,普通话、粤语和英语可能混合出现。

尤其是粤语识别,对于广东地区的企业应用来说,是一个比较有实际价值的能力。

1. 模型目录规划

我将模型文件统一放在独立目录中,方便后续管理:

TaskWeave-ASR/
│
├── models/
│   │
│   ├── sensevoice/
│   │   ├── model.int8.onnx
│   │   └── tokens.txt
│   │
│   ├── punctuation/
│   │   └── punct-ct-transformer/
│   │
│   ├── speaker/
│   │   └── pyannote-segmentation-3.0/
│   │
│   └── speaker-embedding/
│       └── 3dspeaker-eres2net/
│
├── app/
│   ├── main.py
│   ├── asr.py
│   ├── punctuation.py
│   └── speakers.py
│
├── logs/
│
├── start.sh
└── taskweave-asr.service

注意:以上是整理后的目录规划,具体模型文件名应以下载的模型包为准,不要直接将示例文件名当成所有模型包的固定名称。

2. 下载模型

建议从 sherpa-onnx 官方模型仓库选择对应的 SenseVoice 模型。

官方项目:

github.com/k2-fsa/sher…

模型仓库:

github.com/k2-fsa/sher…

下载模型后,将模型文件解压到:

models/sensevoice/

检查文件:

ls -lh models/sensevoice/

重点确认:

  • ONNX 模型文件完整。
  • tokens.txt 文件存在。
  • 模型文件与当前 sherpa-onnx 版本兼容。
  • 文件权限允许服务进程读取。

3. CPU 推理配置

本次使用 CPU 推理。

主要配置:

provider = cpu
num_threads = 4
language = auto

其中:

  • provider:指定 CPU 推理。
  • num_threads:设置推理线程数量。
  • language:使用自动语言识别模式。

线程数不一定越大越好,需要结合服务器 CPU 核数、并发量和音频时长进行测试。


五、启动 WebSocket 语音识别服务

模型准备完成后,可以使用 sherpa-onnx 提供的离线 WebSocket 服务程序。

它的作用是将语音识别能力封装为网络服务,供其他系统调用。

本次部署使用的服务程序:

sherpa-onnx-offline-websocket-server

先检查可执行程序是否存在:

which sherpa-onnx-offline-websocket-server

如果找不到,可以检查 Python 环境:

conda activate taskweave-asr

pip show sherpa-onnx

python -c "import sherpa_onnx; print(sherpa_onnx.__file__)"

查看服务程序支持的参数:

sherpa-onnx-offline-websocket-server --help

不同版本的命令行参数可能存在差异,因此应以当前安装版本的帮助信息为准。

启动时需要配置:

  • SenseVoice ONNX 模型路径。
  • tokens 文件路径。
  • CPU 推理方式。
  • 推理线程数。
  • WebSocket 监听端口。
  • 语言识别模式。

本次实际服务端口:

6006

服务启动后检查端口:

ss -lntp | grep 6006

如果出现监听记录,说明服务已经启动并开始监听。

注意:端口监听成功不代表识别功能已经正常,需要进一步通过实际音频进行识别测试。


六、增加自动标点能力

语音识别模型输出的文字,不一定符合正常阅读习惯。

例如:

原始识别结果:

大家下午好我们开始今天的项目周会
今天主要看一下语音模块目前的开发进度
重点验证语音识别自动标点说话人分离

这样的内容虽然能看懂,但是直接用于会议纪要并不理想。

因此,我额外部署了标点模型:

sherpa-onnx-punct-ct-transformer-zh-en-2024-04-12

主要用于中文和英文文本的自动标点处理。

处理流程:

音频输入
   |
SenseVoice 语音识别
   |
原始识别文本
   |
标点模型
   |
添加标点
   |
最终文本

例如:

处理前:

大家下午好我们开始今天的项目周会

处理后:

大家下午好,我们开始今天的项目周会。

在实际项目中,我将标点处理独立封装为模块,避免与 ASR 主识别逻辑耦合。

这样做有几个好处:

  1. 可以独立替换标点模型。
  2. 可以单独测试标点效果。
  3. 可以根据业务需要决定是否启用。
  4. 方便后续对会议文本进行二次处理。

七、进一步扩展:说话人分离与声纹识别

如果只是普通语音转文字,通常不需要区分说话人。

但会议场景不一样。

例如,一场五人会议,需要知道:

  • 谁在发言?
  • 每个人说了什么?
  • 发言顺序是什么?
  • 哪些观点属于同一个人?

因此,我进一步增加了两个模块。

1. 说话人分离

使用:

Pyannote Segmentation 3.0(ONNX)

说话人分离主要解决:

音频
  |
说话人分离
  |
Speaker 0
Speaker 1
Speaker 2
Speaker 3

它可以帮助识别不同时间段内的说话人。

需要注意,说话人分离得到的是不同的说话人编号,不会自动知道这个人真实叫什么名字。

2. 声纹识别

使用:

3D-Speaker ERes2Net Base SV zh-cn 16k(ONNX)

声纹识别主要用于建立和比对说话人的声音特征。

实际应用中,可以设计这样的流程:

第一次会议
    |
识别说话人
    |
Speaker 0
    |
管理员绑定员工档案
    |
张三
    |
保存声纹特征
    |
后续会议
    |
声纹比对
    |
辅助识别张三

这样,会议记录就有机会从:

说话人1:今天我们讨论项目进度。
说话人2:目前已经完成第一阶段。

逐步变成:

张三:今天我们讨论项目进度。
李四:目前已经完成第一阶段。

这里需要强调:声纹匹配是概率性识别,不能仅凭一次匹配结果就认定发言人身份。还需要考虑录音质量、重叠发言、噪声和相似声音,并提供人工纠正入口。

声纹数据也属于需要严格保护的个人信息,实际部署应获得适当授权并设置访问权限和保存期限。


八、使用 FastAPI 封装 REST API

只有 WebSocket 服务还不够。

在实际企业应用中,不同系统可能使用不同的接入方式。

例如:

  • Web 前端实时传递音频。
  • 后端上传录音文件。
  • 会议助手调用转写接口。
  • 其他业务系统通过 HTTP 接口接入。

因此,我另外使用 FastAPI 封装 REST API。

本次服务端口:

8088

接口规划如下:

接口功能
/api/v1/asr/transcriptions音频转写
/api/v1/asr/punctuation自动标点
/api/v1/asr/speakers说话人管理
/api/v1/asr/speakers/enroll声纹注册
/api/v1/asr/speakers/identify声纹识别
/api/v1/asr/speakers/list声纹列表
/api/v1/asr/speakers/delete声纹删除

这些是业务层接口路径规划,实际请求参数、响应字段和鉴权方式应以项目中的 FastAPI 实现为准。

REST 服务可以按照下面的方式启动:

uvicorn app.main:app \
  --host 0.0.0.0 \
  --port 8088 \
  --workers 1

这里先使用单 Worker 部署。

原因是模型可能在应用启动时加载到进程内存中,如果直接增加 Worker,可能导致多个进程分别加载模型,增加内存占用。

如果后续需要提高并发,可以通过多进程、任务队列或独立推理服务进行扩展,并根据实际资源情况测试。

启动后可以通过浏览器访问:

http://服务器IP:8088/docs

查看 FastAPI 自动生成的接口文档。

生产环境应根据实际网络环境增加 HTTPS、身份认证、请求大小限制和访问控制,不建议直接将未经保护的接口暴露到公网。


九、配置 Linux systemd 开机自启动

服务部署完成后,不能每次都手动启动。

因此,需要将服务交给 systemd 管理。

我使用的服务配置如下:

文件:

/etc/systemd/system/taskweave-asr.service

配置:

[Unit]
Description=TaskWeave ASR Service
After=network.target

[Service]
Type=simple

WorkingDirectory=/opt/TaskWeave-ASR

ExecStart=/bin/bash /opt/TaskWeave-ASR/start.sh

Restart=on-failure
RestartSec=5

Environment=PATH=/opt/asr-venv/bin:/usr/local/bin:/usr/bin:/bin

[Install]
WantedBy=multi-user.target

注意:

这里的 /opt/TaskWeave-ASR 和 /opt/asr-venv 是示例路径,部署时必须替换成服务器真实路径。

如果使用 Conda 环境,建议在启动脚本中显式加载 Conda 环境,而不是直接假定 systemd 能自动继承交互式终端的环境变量。

例如:

#!/bin/bash

set -e

source /opt/miniconda3/etc/profile.d/conda.sh

conda activate taskweave-asr

cd /opt/TaskWeave-ASR

# 在这里启动实际的 WebSocket 和 FastAPI 服务

如果两个服务需要同时运行,建议分别建立 systemd 服务单元,避免其中一个服务退出后,另一个服务的状态无法独立管理。

重新加载配置:

systemctl daemon-reload

设置开机启动:

systemctl enable taskweave-asr

启动服务:

systemctl start taskweave-asr

查看运行状态:

systemctl status taskweave-asr

查看运行日志:

journalctl -u taskweave-asr -f

如果启动失败,优先检查:

  1. Python 环境路径是否正确。
  2. 模型文件是否存在。
  3. 启动脚本是否有执行权限。
  4. 端口是否被其他进程占用。
  5. 服务运行用户是否有模型目录读取权限。

十、如何验证识别效果?

服务能够启动只是第一步。

真正需要验证的是:识别结果是否满足业务要求。

我专门准备了一份五人会议测试音频。

测试数据:

项目测试数据
会议人数5 人
音频时长约 385.8 秒
发言段数25 段
音频内容普通话、粤语等
测试文件meeting_5people.wav
对照文件ground_truth.json

这里使用 Ground Truth 作为人工校对后的参考文本,可以帮助比较识别结果。

1. 测试普通话

重点关注:

  • 普通话识别准确性。
  • 专业术语识别。
  • 数字和日期识别。
  • 长句断句效果。

2. 测试粤语

重点关注:

  • 粤语口语表达。
  • 粤语与普通话混合。
  • 语速较快的情况。
  • 地名、人名和专业词汇。

粤语识别不能只看是否输出了文字,还需要检查口语表达是否准确。

3. 测试说话人分离

重点关注:

  • 是否正确区分五位发言人。
  • 同一个人的多段发言是否能够关联。
  • 发言时间戳是否合理。
  • 多人连续发言时是否出现错误切分。

4. 建立量化评估

后续可以增加以下指标:

指标用途
CER中文字符错误率
WER英文词错误率
DER说话人分离错误率
RTF实时率,评估识别耗时
延迟从音频输入到结果输出的时间

其中,RTF 可以按以下方式计算:

RTF = 识别耗时 / 音频时长

例如,一段 100 秒的音频,用 20 秒完成识别:

RTF = 20 / 100 = 0.2

RTF 越低,说明相对于音频时长,处理速度越快。

需要注意,这些指标需要实际运行测试后才能得出,不能直接用模型宣传数据代替自己的测试结果。


十一、接入 TaskWeave 会议助手

完成 ASR 服务后,我没有把它当成一个独立的语音转文字工具,而是进一步集成到 TaskWeave 会议助手中。

整体业务流程:

用户上传会议录音
        |
        v
TaskWeave 会议助手
        |
        v
调用本地 ASR 服务
        |
        v
SenseVoice 语音识别
        |
        v
自动标点
        |
        v
说话人分离
        |
        v
声纹匹配
        |
        v
生成结构化会议记录
        |
        v
调用大模型
        |
   +----+----+--------+
   |         |        |
会议摘要  讨论结论   待办事项
   |         |        |
   +----+----+--------+
             |
         生成会议纪要

这里有一个重要的架构原则:

语音识别负责把音频转换成结构化文本,大模型负责理解内容并生成会议成果。

不应该让大模型承担本来应该由 ASR 处理的音频识别工作。

这样可以让两部分能力独立升级,也方便后续替换语音模型或大语言模型。


十二、离线部署需要注意什么?

在企业内网部署时,还需要考虑几个实际问题。

1. 模型文件与依赖管理

部署前应提前准备:

  • Python 运行环境。
  • sherpa-onnx 安装包。
  • ONNX 模型文件。
  • 标点模型。
  • 说话人相关模型。
  • 项目依赖。

如果目标服务器不能访问互联网,需要在外部环境完成下载,再将安装包和模型文件通过受控方式导入内网。

2. CPU 与内存资源

不同模型的资源消耗不同。

不能只看模型文件大小,还需要考虑:

  • 模型加载后的内存占用。
  • 音频解码占用。
  • 并发识别数量。
  • 说话人分离的额外计算。
  • 多个服务同时运行的资源消耗。

建议先使用单任务测试,再逐步提高并发。

3. 网络安全

内网部署也不意味着可以忽略安全问题。

建议:

  • 只开放必要的服务端口。
  • API 增加身份认证。
  • 对上传文件进行格式和大小校验。
  • 限制音频文件存储权限。
  • 设置临时文件清理机制。
  • 对声纹数据进行加密和权限管理。
  • 保留必要的访问日志。

4. 识别结果不能直接作为最终会议纪要

语音识别可能出现错字、漏字和说话人判断错误。

因此,正式会议纪要应保留人工纠错和确认环节。


十三、总结

这次实践让我更加明确了一点:

企业 AI 应用的开发,并不是简单地接入一个大模型 API。

很多时候,需要先把基础能力建设好,再通过业务流程将它们组合起来。

这次离线 ASR 部署,完成了多语种语音识别服务的基础建设,并进一步探索了自动标点、说话人分离、声纹识别以及会议助手集成。

后续还可以继续优化:

  1. 提高粤语和混合语言识别效果。
  2. 优化长音频分段和处理速度。
  3. 完善实时语音识别。
  4. 提高说话人分离准确率。
  5. 将声纹与企业员工档案关联。
  6. 优化会议纪要的自动生成和人工纠错流程。

这套能力也是我持续研发 TaskWeave AI Agent 平台的一部分。

后续我会继续分享 .NET、Vue、RAG、MCP、AI Agent 以及企业 AI 应用私有化部署方面的真实开发实践。


项目归属:TaskWeave 与 AgentWeave

本文介绍的离线语音识别服务,是我在持续研发 TaskWeave AI Agent 应用平台 过程中建设的一项基础技术能力。

TaskWeave 是由 AgentWeave 发起并持续研发的 AI Agent 应用平台,主要探索企业智能体、知识库 RAG、会议助手、材料写作、Skill、MCP 和 Workflow 等应用场景。

其中,TaskWeave ASR 是平台的语音能力模块,负责提供离线语音识别、多语种转写和相关语音处理服务,为会议助手等应用提供底层技术支持。

  • 品牌:AgentWeave
  • 产品:TaskWeave AI Agent 应用平台
  • 技术模块:TaskWeave ASR
  • 官方网站:www.agentweave.site/

本文记录的是其中一项真实技术实践。

关于作者与项目

我是张国军(Armys),一名长期从事 .NET、Java、Vue 企业应用开发,并持续探索 AI 应用落地的独立开发者。

AgentWeave 是我的个人 AI 应用开发品牌,专注于佛山及周边企业 AI 应用落地。

目前正在持续研发 TaskWeave AI Agent 应用平台,探索企业智能体、知识库 RAG、AI 会议助手、材料写作及企业业务系统 AI 改造。

相关项目:

GitHub:github.com/ArmysZHANG/…

Gitee:gitee.com/Armys/agent…

后续将持续分享 TaskWeave 的实际开发过程,以及 .NET、Vue、AI Agent 和企业 AI 私有化部署实践。