WhisperLiveKit 实时说话人区分:1 条命令给会议转录标上"谁在说"
【免费下载链接】WhisperLiveKitReal-time, local speech-to-text with streaming ASR, speaker diarization, translation, and OpenAI/Deepgram-compatible APIs.项目地址: https://gitcode.com/GitHub_Trending/wh/WhisperLiveKit
周五复盘会录了 40 分钟,导出纪要却分不清每句是谁说的。WhisperLiveKit 的 Sortformer 说话人区分功能,边录音边给每行文字贴上说话人编号,不用等录完。
它解决什么
先说它对付的几类真实麻烦:
- 发言归属不清:多人讨论被转成一大段文字,没法知道哪句谁说的,归档只能重听。
- 说话人混淆:两个音色接近的人,事后补标注要反复回放音频,越补越乱。
- 实时字幕残缺:直播和访谈字幕缺"谁在说",观众只能猜发言者。
- 行为分析缺位:想统计每人发言时长和次数,连最基础的说话人数据都没有。
WhisperLiveKit 架构:说话人区分与流式 ASR、翻译并列,是实时管线的一环
技术底座一句话:NVIDIA NeMo 的流式 Sortformer(默认模型diar_streaming_sortformer_4spk-v2),按 1 秒音频块推理,最多 4 个说话人通道。
与常规路线的差异
| 维度 | Sortformer 流式分人 | 离线批处理分人 |
|---|---|---|
| 出结果时机 | 每 1 秒音频块更新,边说边出 | 整段音频录完才能跑 |
| 内存占用 | 固定双缓存(各 188 帧),不随会议变长 | 存整段音频和全部中间结果 |
| 说话人身份 | 按到达顺序编号,长会里身份保持 | 事后聚类,中途换人易断号 |
| 适用场景 | 实时会议、直播字幕、访谈 | 录音归档、事后整理 |
流式路线把"谁说的"变成和文字一样即时产出的数据,身份从第一句话开始就稳定。
跑通最短路径:启动说话人区分服务
装 Sortformer 依赖
# 克隆源码 git clone https://gitcode.com/GitHub_Trending/wh/WhisperLiveKit cd WhisperLiveKit # 安装分人扩展(自动带上 nemo-toolkit[asr]) pip install -e ".[diarization-sortformer]" # 或一行 Docker(GPU 预配置镜像) docker compose up --build wlk-gpu-sortformer启动带分人的转录服务
# 最简启动:base 模型 + 实时分人,默认 8000 端口 wlk --model base --diarization # 变体 1:中文会议,换更大模型 wlk --model medium --diarization --language zh # 变体 2:用本地 .nemo 模型覆盖默认模型 wlk --model base --diarization --sortformer-model-path /path/model.nemo看到效果:浏览器里确认说话人标签
打开http://localhost:8000,每句话单独成行,行首带说话人编号和小人图标;当前句的分人还在确认时会显示加载状态。WebSocket/asr推给前端的行数据长这样:
[00:00 - 00:04] Speaker 1: 大家好,今天过一下项目进展 [00:04 - 00:11] Speaker 2: 第一阶段开发已经完成 [00:11 - 00:16] Speaker 1: 好,测试那边什么情况 [00:16 - 00:23] Speaker 3: 覆盖率到 85% 了说话人区分运行效果:实时转录界面中每行文字带独立说话人编号
它内部怎么跑的
双缓存记忆(spkcache + fifo)→ 一份存从开会到现在的说话人嵌入,一份存最近几个块;新说话人插话或换人时,模型同时对照两块缓存做归属判断。
spkcache_len = 188 # 长记忆帧数 fifo_len = 188 # 短期帧数1 秒块流式推理→ 每满 1 秒音频,先转成 128 维梅尔频谱图,再拼上上一块尾部 99 帧当左上下文,走一次forward_streaming_step,模型状态随块滚动。
chunk_len=10 × subsampling_factor=10 × 10ms 帧步长 = 1s/块到达顺序编号 + 逐帧 argmax 出段→ 模型的 4 个通道按说话人到达先后编号,身份锁在缓存里;每帧取 argmax 通道,相邻同说话人帧合并成(speaker, start, end)片段;长静音由insert_silence(sec)写进全局时间轴,防止时间漂移。
active = argmax(preds[:, :max_speakers], axis=1)调参数:说话人区分常用旋钮
--sortformer-max-speakers(默认 None,即模型全部 4 通道)→ 往小调到 1–3 → 会议已知不超过 N 人时,避免空通道抢归属。它是断言不是估计,人多了不会更准。--sortformer-model-path(默认 None,加载nvidia/diar_streaming_sortformer_4spk-v2)→ 换成自调.nemo→ 强领域噪声场景,如呼叫中心、课堂录音。spkcache_len(源码内,默认 188)→ 调大 → 超长会议需要更多长期说话人上下文。--min-chunk-size(默认 0.1s)→ 调大 → CPU 跟不上实时时,用更新频率换吞吐。
不同 ASR 模型的速度-精度对比:给分人服务选--model时同样适用
# 固定 3 人会议,用本地微调模型 wlk --model base --diarization --sortformer-max-speakers 3 \ --sortformer-model-path ./my_4spk.nemo踩坑速查:说话人标签错乱与依赖报错
现象:启动直接退出,提示 "Sortformer diarization requires NeMo"。原因:只装了基础包,diarization-sortformer扩展没装。解法:pip install -e ".[diarization-sortformer]";uv 用户执行uv sync --extra diarization-sortformer。
现象:Python 3.13 环境里 Diart 扩展安装失败,报 NumPy 版本冲突。原因:Diart 0.9.2 要求 NumPy 低于 2,与 3.13 不兼容。解法:不用装 diart 扩展,直接用默认后端--diarization-backend sortformer。
现象:长停顿后说话人编号跳变,时间戳跟着漂移。原因:静音区间没写进全局时间轴,缓存里的身份估计开始晃动。解法:保持 VAD 开启(别传--no-vad);自己集成代码时,检测到长静音后调用diarize.insert_silence(1.5)。
往更大系统里塞
- 翻译同屏:加
--target-language en,定稿句子走 NLLB 翻译,译文跟随说话人编号;怕译文闪烁就加--translate-on-complete。 - 中英混说:
--language auto自动检测语言,Sortformer 的归属输出与语言无关,一个服务吃下混合会议。 - API 接入:WebSocket
/asr每行带speaker字段;OpenAI 兼容的 REST/v1/audio/transcriptions可处理带说话人标签的录音文件,直接接进现有录音流水线。
看源码
- sortformer_backend.py:缓存大小、流式状态和分段逻辑,改行为看这里。
- diarization/:Sortformer 与 Diart 两个后端。
- parse_args.py:
--diarization系列参数的入口。
启动你的第一个实时说话人区分服务,把下一场会议直接按人归档。
【免费下载链接】WhisperLiveKitReal-time, local speech-to-text with streaming ASR, speaker diarization, translation, and OpenAI/Deepgram-compatible APIs.项目地址: https://gitcode.com/GitHub_Trending/wh/WhisperLiveKit
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考