跳到主要内容

创建日期:2026-10-13 | 最近更新:2026-10-13 事实核对基于 sherpa-onnx 1.13.8(sherpa-onnx-node 原生 addon 与 sherpa-onnx WebAssembly 包;node v24.19.0、Chrome 149、Linux x64)。文中所有耗时、内存、体积均为本机实测,脚本与完整日志留档于 source/sherpa-onnx-lab/(bash run.sh | tee run.log 可复现)。

sherpa-onnx 101(第 0 篇 · 入门):把语音能力搬进前端工程

一句话:sherpa-onnx 是一个纯本地的语音推理引擎——语音识别、语音合成、说话人、唤醒词、降噪全都在你自己的进程里跑,不联网、不调 API、不花 token。对前端来说,它的意义是:浏览器或 Node 里终于有了一套不依赖云服务的语音能力。

这个系列按「一个前端工程师真会用到什么」来组织:先把两条 Node 路线和成本记账讲清楚(本篇),再挑模型(第 1 篇)、落地识别链路(第 2 篇)、做领域定制(第 3 篇)、加 TTS 和唤醒词(第 4 篇),最后进浏览器(第 5 篇)。

0. 先给结论​

#结论证据
1Node 里有两套互不相同的包:sherpa-onnx-node(原生 addon,多线程)和 sherpa-onnx(WebAssembly,单线程)。API 都不一样,选错了要重写代码§2
2同一模型同一段音频:WASM 版解码慢 3.4~3.5 倍(RTF 0.085 对 0.299;两次运行 3.41 / 3.53),文本还不完全一致§2、第 5 篇
3最小的能力只要 630 KB:Silero VAD 模型 0.6 MB,加载后常驻内存 74 MB,处理速度是实时的 117 倍§3
4成本差异极大:唤醒词模型加载后 93 MB,中英双语流式识别 324 MB,TTS(Kokoro)496 MB§4
5全部能力都能离线跑,模型不需要联网校验;下载一次之后断网可用§4
6真正要花时间的地方不是 API,是选模型和裁词表——这两件事决定「它能不能听懂你们组的黑话」第 1、3 篇

1. 它是什么​

sherpa-onnx 是 k2-fsa(下一代 Kaldi 团队,小米)做的离线语音推理引擎,Apache-2.0,C++ 写成,底层跑 ONNX Runtime。它自己不训练模型,只负责"把训练好的模型高效地跑起来",然后给几十种语言/平台发绑定:C/C++、Python、Java/Kotlin、Swift、Go、Rust、C#、Pascal、Node.js、WebAssembly、Android、iOS、鸿蒙、Flutter、Tauri……

把它和常见的两条路对比一下就清楚它的位置:

方案音频出不出你的机器成本延迟离线可用
云厂商 ASR/TTS API出(要上传)按量计费网络往返 + 排队否
浏览器原生 SpeechRecognition出(Chrome 走云端)免费但有配额不可控否
sherpa-onnx不出一次性下载模型 + 你的 CPU本地算力决定是

代价也很直白:模型要自己下、要自己挑、要自己算体积和内存,识别准确率也不会比大厂云端模型好。它换来的东西是隐私、离线、零边际成本和完全可控的延迟。

1.1 它能做什么​

这个清单不用查文档——sherpa-onnx-node 导出的就是全部能力,跑一行就看得到:

$ node -e "console.log(Object.keys(require('sherpa-onnx-node')).sort().join(', '))"

AudioTagging, CircularBuffer, Display, GenerationConfig, KeywordSpotter,
LinearResampler, OfflinePunctuation, OfflineRecognizer, OfflineSpeakerDiarization,
OfflineSpeechDenoiser, OfflineTts, OnlinePunctuation, OnlineRecognizer,
OnlineSpeechDenoiser, SpeakerEmbeddingExtractor, SpeakerEmbeddingManager,
SpokenLanguageIdentification, Vad, gitDate, gitSha1, onnxruntimeVersion,
readWave, version, writeWave

翻译成人话:

类干什么典型用途
OnlineRecognizer流式语音识别(边说边出字)实时字幕、语音输入
OfflineRecognizer非流式识别(整段音频一次出结果)转录音频文件
Vad语音活动检测(把静音剪掉、按句切分)长音频预处理、省算力
KeywordSpotter唤醒词 / 关键词检测「打开控制台」这种语音命令
OfflineTts语音合成朗读、语音播报
OfflinePunctuation / OnlinePunctuation加标点识别结果可读化
SpeakerEmbeddingExtractor / Manager声纹(说话人识别)谁在说话
OfflineSpeakerDiarization说话人分离会议记录分角色
SpokenLanguageIdentification语种识别多语种路由
OfflineSpeechDenoiser / OnlineSpeechDenoiser降噪嘈杂环境前置处理
AudioTagging音频事件打标(狗叫、警报…)环境感知
LinearResampler / CircularBuffer重采样、环形缓冲麦克风采集必备胶水

注意 Online = 流式,Offline = 非流式,这个命名贯穿整个库。

2. 两条 Node 路线:先选对包​

这是最容易踩的第一个坑。npm 上两个名字很像的包是两套完全不同的东西:

sherpa-onnx-nodesherpa-onnx
实现原生 addon(node-addon-api + .so/.dll)WebAssembly
多线程支持不支持
最低 Node1618
安装体积约 32 MB(含 libonnxruntime.so 27 MB)约 15 MB(单个 .wasm)
浏览器能跑不能能(但要另一套构建产物,见第 5 篇)

官方 README 原文就是这么一句话对照的:

||Method|Support multiple threads|Minimum required node version| |---|---|---| |this package| https://github.com/nodejs/node-addon-api | Yes | v16| |https://www.npmjs.com/package/sherpa-onnx| WebAssembly | No | v18|

(摘自 sherpa-onnx-node 包内 README.md,留档见 source/sherpa-onnx-lab/official-sources/nodejs-addon-examples-README.md)

它们连 API 签名都不一样,这不是风格差异,是实打实的迁移成本。举个最小例子:

// 原生 addon:参数是一个对象
stream.acceptWaveform({ sampleRate: wave.sampleRate, samples: wave.samples });

// WebAssembly:两个位置参数,而且顺序是 (采样率, 样本)
stream.acceptWaveform(wave.sampleRate, wave.samples);

而且 WASM 版还要手动 free():

stream.free();
recognizer.free();

同一个模型(2023 版中英双语 mobile、int8)、同一段 10.05 秒音频、同样 100 ms 一包喂进去:

实现加载模型解码RTF识别结果
原生 addon(2 线程)0.923 s0.852 s0.085昨天是 MONDAY TODAY IS LIBRAR THE DAY AFTER TOMORROW是星期三
WebAssembly(单线程,SIMD)1.059 s3.010 s0.299昨天是 MONDAY TODAY IS LIBR THE DAY AFTER TOMORROW是星期三

慢三倍多,文本还差一个字母。 这个差距主要来自线程数(WASM 版据其 CI 构建脚本 build-wasm-simd-nodejs.sh 是带 SIMD 的,但不带 pthread)。所以:

  • 服务端 / 桌面端 / Electron 主进程 → 用 sherpa-onnx-node,把 numThreads 加上去;
  • 必须在浏览器里跑 → 只能 WASM,先算清楚性能预算(第 5 篇有一份实测账)。

两种实现都装在一个工程里不冲突(我这份实验就同时装了),但代码里最好包一层适配器,别让上层感知到差异。

3. 五分钟跑起来:从 630 KB 的 VAD 开始​

建议第一个上手的能力不是识别,是 VAD。 理由:模型最小(0.6 MB)、不需要词表、不需要调参、输出是数字(好断言),而且它是后面所有链路的第一个环节。

mkdir sherpa-lab && cd sherpa-lab
npm i sherpa-onnx-node
curl -sSL -O https://github.com/k2-fsa/sherpa-onnx/releases/download/asr-models/silero_vad.onnx
const sherpa = require('sherpa-onnx-node');

const vad = new sherpa.Vad({
sileroVad: {
model: './silero_vad.onnx',
threshold: 0.5,
minSpeechDuration: 0.25, // 短于 250ms 的不算语音
minSilenceDuration: 0.5, // 静音超过 500ms 才断开
windowSize: 512, // 16kHz 下 = 32ms 一窗,这个值是模型定死的
},
sampleRate: 16000,
numThreads: 1,
debug: false,
}, 60); // 第二个参数:内部环形缓冲 60 秒

const wave = sherpa.readWave('test.wav');
const win = vad.config.sileroVad.windowSize;

for (let i = 0; i + win <= wave.samples.length; i += win) {
vad.acceptWaveform(wave.samples.subarray(i, i + win));
while (!vad.isEmpty()) {
const seg = vad.front();
vad.pop();
console.log(`语音段 ${(seg.start / wave.sampleRate).toFixed(3)}s 起,` +
`长 ${(seg.samples.length / wave.sampleRate).toFixed(3)}s`);
}
}

本机实测(10.05 秒中文音频):

VAD 切出段数 : 3
端到端耗时 : 0.086 s (RTF = 0.0085 )

段 0: start=0.678s dur=1.606s end=2.284s
段 1: start=3.814s dur=2.118s end=5.932s
段 2: start=6.534s dur=3.366s end=9.900s

能量包络起点(参照) : 0.710s(原始音频,判据 = RMS > 10% 峰值)
与能量参照之差 : -0.032 s

三件事值得记下来:

  1. 快得离谱:RTF 0.0085,即 1 秒音频花 8.5 毫秒,约 117 倍实时。VAD 在前端链路里几乎不占预算,可以放心每帧都跑。
  2. 起点精度约 ±30 ms。我用「RMS 超过峰值 10%」这个粗糙判据做参照,VAD 报的起点比它早 32 毫秒——这个量级对切分足够,但不要拿它当逐字对齐用(那个要用识别结果自带的 timestamps)。
  3. windowSize: 512 不是随便填的,是 Silero 模型的固定输入长度(16 kHz 下 32 ms)。喂进去的块必须刚好这个大小。

4. 记账:体积、内存、速度​

选型真正要看的三个数。下面全部是本机(Linux x64、28 核、DDR4)实测,体积按解包后的模型目录算(含官方附带的测试音频和 fp32/int8 两套权重,所以比单个 .onnx 大):

能力模型磁盘体积加载后常驻 RSSRTF(越低越快)
VADSilero VAD0.6 MB74 MB0.0085
唤醒词(中文)KWS zipformer 3.3M36 MB—0.030
唤醒词(中英)KWS zipformer 3M(2025-12)39 MB93 MB0.029
英文识别(非流式)Moonshine v2 tiny 量化43 MB162 MB0.026
中英识别(流式)X-ASR 480ms int8(2026-06)163 MB324 MB0.112 ~ 0.130
中英识别(流式,老)zipformer bilingual mobile(2023)360 MB253 MB0.088 ~ 0.099
语音合成Kokoro 多语言(54 音色)384 MB496 MB1.00

读数说明:

  • RSS 是"加载完模型、还没开始推理"时的进程常驻内存,每个模型在独立进程里量的。基础 Node 进程本身约 50 MB。
  • RTF = 处理耗时 ÷ 音频时长,小于 1 就是比实时快。RTF 0.09 表示「能同时盯 11 路实时音频」,RTF 1.00 表示「合成 1 秒语音要花 1 秒 CPU」。
  • 这里的内存是全量权重常驻。前端场景下这是硬约束:Kokoro 那 496 MB 在浏览器里就是实打实的内存占用,手机浏览器上要慎重。

两条直接可用的结论:

  1. TTS 是最贵的一环。 RTF 约等于 1 意味着它刚好实时,没有余量——这就是为什么前端做语音播报要「分句流式合成 + 边合成边播」,而不是等整段合成完(第 4 篇细说)。
  2. 识别和唤醒词是可以叠加的:VAD(74 MB)+ 唤醒词(93 MB)不到 170 MB,能先在本地待机听唤醒词,唤醒了再加载大模型——这是前端最省内存的架构。

5. 模型从哪儿下​

全部在 GitHub Releases 上,按用途分成不同的 tag:

tag内容
asr-models语音识别(zipformer / paraformer / whisper / moonshine / sensevoice / dolphin…),498 个资产
kws-models唤醒词
tts-models语音合成(kokoro / matcha / vits / zipvoice / pocket…)
punctuation-models标点
speaker-recongition-models声纹(官方 tag 名就是这个拼写,少了个 i)
speaker-segmentation-models说话人分离
speech-enhancement-models降噪
hr-files同音词替换用的拼音词典与规则(第 3 篇)
audio-tagging-models音频事件打标

下载就是一个 curl:

curl -sSL -O https://github.com/k2-fsa/sherpa-onnx/releases/download/asr-models/silero_vad.onnx

包内一般自带 test_wavs/,先用它跑通链路再换自己的音频——这一步能省掉大量「到底是模型不行还是我的音频不对」的排查。本实验为了方便复现,把用到的模型整理成了一份 download-models.sh(约 1.1 GB)。

6. 什么时候不该用它​

说清楚边界比堆优点有用:

  • 要识别得又准又省心 → 用云端大模型。sherpa-onnx 的本地模型准确率有明确上限,尤其嘈杂环境、方言、远场。
  • 要跑在低端安卓机上做实时字幕 → 先做主频和内存预算,int8 小模型(唤醒词、14M/20M 级 zipformer)之外都容易掉帧。
  • 要做「理解」而不是「转写」 → 它只输出文字,语义理解是另一层的事。
  • 要多人实时会议转写 → 说话人分离能跑,但那是"先录完整段再离线分"的模式,不是流式。

反过来说,它特别适合这些场景:内网/离线环境、隐私敏感(音频不能出设备)、要零边际成本地跑量(批量转写几千小时音频)、要给语音命令做本地唤醒、要把语音能力打进浏览器做 demo。

7. 本系列路线图​

篇主题回答的问题
0入门(本篇)两条 Node 路线怎么选?成本怎么算?
1模型选型程序员说的是「把 useEffect 的依赖数组补上」,哪个模型听得懂?
2落地识别链路流式怎么用?长音频怎么切?端点检测的坑在哪?
3听懂你的黑话热词和同音词替换怎么把术语救回来?为什么有时候救不回来?
4说话与听见本地 TTS 什么水平?自定义唤醒词要几步?
5前端集成浏览器里怎么跑?体积和性能代价是多少?
6视频转字幕手里一堆金融/编程长视频要字幕,选哪个模型、CPU 还是 GPU、能到多准?
7词表工程左边那列「模型听成的样子」,你是怎么知道的?——词表不是写出来的,是挖出来的
8并发与吞吐做成服务后能同时转多少路?瓶颈其实在切分(VAD),不在识别

全部实验脚本在 source/sherpa-onnx-lab/,bash run.sh | tee run.log 一次跑完,输出即文中引用的原始日志。模型不进仓库,用 download-models.sh 拉。