本地说话人分离:我们如何打造 100% 隐私的会议记录工具
OpenWhispr 是一款音频永不离开设备的开源会议助手。每一段声纹、每一个说话人标签、每一个嵌入向量——全部在你自己的机器上处理和存储。
OpenWhispr
工程
目录
本地说话人分离是指为会议录音标注“谁在何时说话”的过程,全程在用户设备上运行,不向任何外部服务器传输音频、嵌入向量或转录文本。 会议转录有一个“谁说了什么”的难题。Zoom 只给你一整面文字墙。大多数 AI 记录工具——Otter.ai、Fireflies、Read、Granola——的解决办法都是把你的会议音频上传到它们的服务器,在那里运行说话人模型,再把带标签的片段发回来。你的声音,以及你同事的声音,最终都变成了别人数据库里的向量。我们不想要这样,我们的用户也不想要。OpenWhispr 是一款建立在相反假设之上的开源会议记录工具:音频永不离开设备,说话人声纹存放在你磁盘上的本地 SQLite 文件中,除非你明确选择,否则会议的任何内容都不会被上传。本文将从技术层面解析这一承诺中的分离部分——它是什么、我们选了哪些模型、它们如何协同工作,以及每个步骤中每一字节数据究竟存放在哪里。
最后更新:2026 年 4 月 15 日。实现细节参考的是 2026 年 4 月 14 日合并进 OpenWhispr 桌面应用的说话人分离功能。下文的每一项技术声明都来自一手参考资料,或可直接在开源仓库中查验。
事实核查快照(双重来源)
- 说话人分离回答的是“谁在何时说话”。 它的评估基准是 NIST Rich Transcription 等社区评测以及 pyannote.audio 参考流程。 NIST RT 评测 · pyannote.audio 论文(Bredin 等,2020)
- 我们使用 pyannote-segmentation-3.0,以及来自 3D-Speaker 项目的 CAM++。 两者都是开源的,都可移植为 ONNX,都直接从 sherpa-onnx 模型发布页下载。 pyannote-segmentation-3.0 模型卡 · CAM++ / 3D-Speaker 论文(Wang 等,2023)
- 所有推理都通过 sherpa-onnx 运行——一个原生 ONNX 二进制文件,无需 Python 运行时、无需 PyTorch、无需 GPU。 我们在构建时锁定该二进制文件及其依赖的版本。 sherpa-onnx(GitHub) · sherpa-onnx 发布页
- 声纹是 512 维的 float32 向量,作为本地 SQLite BLOB 存储, 每位说话人约 2KB。它们永不离开设备。 SQLite 数据类型文档 · ONNX Runtime
- 分离过程中不会向设备外传输任何音频、嵌入向量或转录文本。 整个功能唯一的网络活动是首次启动时的一次性模型下载,你可以亲自在开源仓库中查验。 OpenWhispr 隐私政策 · OpenWhispr(GitHub)
说话人分离究竟是什么
说话人分离是为录音标注“谁在何时说话”的过程——而不是他们说了什么。 转录回答“说了什么”;分离回答“谁在说”。两者相辅相成:一款好的会议记录工具两者都需要,而让它们协同工作比单独搞定任何一个都要难。
分离之所以真正困难,源于几个顽固的原因。同一通话里两个人的声音可能重叠。背景噪声会渗进来。简短的发言(“嗯”“好的”)几乎不携带说话人信息。新的说话人会在会议进行到一半时出现。而且与转录不同,分离必须做出全局决策——它无法在不与第二段对比的情况下为第七段贴标签。正是这种全局语境,使得大多数商业方案选择在服务器端针对完整录音做分离,再把带标签的转录发回来。
仅有转录是不够的。一场十人参加、四十五分钟充满交叉对话的会议,如果呈现为一面无差别的文字墙,根本无法阅读。会议笔记——人们真正想要的成果——需要说话人归属,因为待办事项、决定和承诺都是挂在具体的人身上的。“Alice 会在周五前交付迁移”是一条笔记。“会在周五前交付迁移”是噪声。
分离不等于说话人识别
分离说的是“这三段是说话人 A;这两段是说话人 B”。它并不知道说话人 A 是谁。说话人识别则通过把声纹与已存档案匹配,给说话人 A 安上一个名字。OpenWhispr 两者都做——分离在每场会议上运行,而一旦你为某人标注了名字,识别就开始介入。两者都保持在本地。
四阶段本地流程
OpenWhispr 的说话人分离流程分为四个阶段:语音活动检测、分割、说话人嵌入和聚类——随后还有一个合并步骤,把说话人片段与转录文本对齐。 每个阶段都是一个独立的 ONNX 模型或原生二进制文件。无需 Python 运行时、无需 PyTorch、无需 CUDA。所有模型在磁盘上的总占用约为 45MB。
本地说话人分离管道
为什么它保持本地化:
每个阶段都通过单个 sherpa-onnx 二进制文件在您的设备上运行 - 没有 Python,没有云。
ONNX 模型中的 ~45MB 下载一次,然后缓存在 ~/.cache/openwhispr/diarization-models/. 中
嵌入是 512-dim float32 向量,在本地磁盘上存储为 SQLite BLOBs 。
音频采集从第一天起就是双流的。麦克风按定义就是“你”,所以我们从不浪费算力去分离你自己的声音——每一段麦克风片段都按来源标记为“你”,而非靠声音匹配。系统音频(通话中的其他所有人)才是唯一真正需要做说话人切分的东西,这一下就把工作量减半了。
音频采集完成后,四个阶段依次运行。VAD 去除静音,让昂贵的嵌入阶段不会把算力浪费在空帧上。分割把连续语音切成单说话人的片段。嵌入模型把每个片段转换为一个 512 维的声纹。凝聚聚类把相似的声纹归为一组,得出最终的一组说话人 ID。最后一道工序按时间戳重叠把这些 ID 合并回转录文本,于是每个词都被打上了一个说话人标签。
整套流程在一个从应用派生出的子进程内运行——没有服务器,没有监听端口,也没有任何指向 Electron 之外的 IPC。你可以通过阅读开源仓库中的 src/helpers/diarization.js 来确认这一点。
第一阶段:语音活动检测(Silero)
语音活动检测(VAD)是在昂贵阶段运行之前,把语音和静音区分开的廉价过滤器。 对静音运行说话人嵌入模型既浪费 CPU 又会产出无用的向量。VAD 正是让流程其余部分保持快速的关键。
我们使用 Silero VAD:一个 2MB 的 ONNX 模型,采用 MIT 许可,实际上已成为开源行业的默认选择。Silero 会针对每一帧返回当前 32 毫秒窗口包含语音的概率。在现代 CPU 上,它的开销微不足道——每个 32 毫秒片段约 0.1 毫秒,大约相当于单核性能的千分之三。
实时流程在通话期间对系统音频持续运行 Silero。我们实际发布时采用的阈值是针对真实会议音频调校的,而非干净的基准测试:
- 窗口大小: 512 个采样点(16kHz 下为 32 毫秒)
- 语音阈值: 0.15——刻意设得激进,以免漏掉音量低或距离远的说话人
- 静音阈值: 0.08
- 片段结束条件: 连续 16 个静音窗口(约 512 毫秒)
- 嵌入所需最小片段: 0.8 秒
- 实时识别节奏: 一旦累积到 ≥1.6 秒的语音,每 1 秒一次
坦诚的取舍
0.15 这一激进的语音阈值能捕捉到远处麦克风上的轻声说话者,但偶尔也会被响亮的键盘敲击声或房间噪声误触发。0.8 秒的最小片段过滤器会在这些误报到达嵌入阶段之前剔除其中大部分,从而保护了下游的准确率。
第二阶段:分割(pyannote 3.0)
分割负责把连续语音切成各自只包含一个说话人的片段,同时检测说话人切换点和重叠区域。 它是决定说话人边界在哪里的阶段。之后的所有环节都假定这些边界是正确的。
我们使用 pyannote-segmentation-3.0,这是 pyannote.audio 团队该模型的当前版本,由 sherpa-onnx 项目导出为 ONNX。Pyannote 是事实上的学术基准:pyannote.audio 流程在 AMI 和 CALLHOME 等标准基准上的分离错误率处于 12–15% 区间——正是商业云服务商爱引用的那组数字。
为什么偏偏要 ONNX 移植版?因为在 Electron 应用里塞进 PyTorch 根本行不通——光是运行时就约 1.5GB,而且实际上要有 GPU 才够快。ONNX 导出版是一个 6.6MB 的单文件,通过 ONNX Runtime在 CPU 上运行。同样的模型,重量却只有一小部分。
我们通过 sherpa-onnx 的离线分离二进制文件配上几个参数来调用它。最小时长设置取自 pyannote 论文推荐的默认值:短促的语音爆发会被并入相邻的静音,而短暂的静音不会打断一个连续的说话人片段。输出是一组带临时说话人 ID 标记的起止时间对( speaker_0, speaker_1等等)。
第三阶段:说话人嵌入(CAM++)
说话人嵌入是一串定长的数字,它代表一段声音,就像哈希值代表一个文件。 同一声音的两段录音会产生在向量空间中相近的嵌入;不同的声音则产生彼此相距很远的嵌入。“相近”用余弦相似度来衡量——一个介于 -1 和 1 之间的单一数值。这是流程中其余一切的数学核心。
我们的嵌入模型是来自阿里巴巴达摩院 3D-Speaker 项目的 CAM++,在 VoxCeleb 说话人验证基准上训练。我们实际采用的具体文件是 3dspeaker_speech_campplus_sv_en_voxceleb_16k.onnx,一个约 28MB 的 ONNX 导出版,为每个片段产出一个 512 维的 float32 向量。论文是 arXiv:2303.00332(Wang 等,2023)。
我们选择 CAM++ 而非那个显而易见的替代—— ECAPA-TDNN——出于三个具体原因:参数量大约只有一半、在 VoxCeleb1-O 上的等错误率更低、CPU 推理更快。NVIDIA NeMo 的 MSDD 模型没有官方 ONNX 导出,且严重依赖 PyTorch。Picovoice Falcon 带有按席位收费的商业许可。Apple 的 CoreML Speech 框架仅限 macOS。CAM++ 是唯一满足我们全部约束的选项——跨平台、ONNX、开放许可、CPU 上速度快。
为了给 CAM++ 喂数据,我们在 src/helpers/speakerEmbeddings.js中自带了一个 log-mel 滤波器组特征提取器:80 个梅尔频带、25 毫秒窗口、10 毫秒帧移,都是这类模型的标准参数。特征通过 onnxruntime-node流入 ONNX 模型,在 Electron 主进程中运行。另一端出来的是一个 512 项的 Float32Array,我们把它作为 2048 字节的 BLOB 写入 SQLite。
匹配步骤本身是最简单不过的事情:两个向量之间的余弦相似度。下面就是决定两个会议片段是否为同一说话人的实际代码:
// src/helpers/speakerEmbeddings.js
cosineSimilarity(a, b) {
let dot = 0, normA = 0, normB = 0;
for (let i = 0; i < a.length; i++) {
dot += a[i] * b[i];
normA += a[i] * a[i];
normB += b[i] * b[i];
}
const denom = Math.sqrt(normA) * Math.sqrt(normB);
return denom === 0 ? 0 : dot / denom;
}为什么是 512 维?
512 是当前 VoxCeleb 说话人验证排行榜上准确率与存储成本之间的最佳平衡点。256 维在区分相近但不同的声音时开始丧失辨别力;1024 维则增加了存储和算力,回报却递减。512 个 float32 × 2KB × 一千场各有十名说话人的会议,磁盘占用约 20MB。这在一个本地 SQLite 文件里绰绰有余。
第四阶段:聚类——“有多少个说话人?”的难题
对每个片段做完嵌入后,你手上有 N 个向量。聚类把它们归为 K 个说话人——但难点在于你通常事先并不知道 K 是多少。 一通销售电话可能有 2 个说话人。一场全员大会可能有 8 个。固定 K 值的聚类在两端都会失灵。
我们使用余弦相似度阈值为 0.5 的凝聚聚类:先把每个片段各自当作一个簇,合并相似度最高的两个簇,重复进行,直到没有任何一对的相似度超过阈值为止。决定何时停止的是阈值,而非某个目标数量。这样就能从两个说话人到十个自然扩展,无需任何配置。
实际的调用位于 src/helpers/diarization.js。它派生 sherpa-onnx 二进制文件,附带几个命令行参数:
// src/helpers/diarization.js
const args = [
`--segmentation.pyannote-model=${segPath}`,
`--embedding.model=${embPath}`,
`--clustering.num-clusters=${numSpeakers}`, // -1 = auto
`--clustering.cluster-threshold=${threshold}`, // 0.5 default
"--min-duration-on=0.2",
"--min-duration-off=0.5",
wavPath,
];输出是纯文本:每个片段一行,格式为 start_sec -- end_sec speaker_NN。我们把它解析成一个对象数组,然后遍历转录文本,把每个转录片段匹配到时间重叠最大的分离片段。来自麦克风的片段始终被标记为 you——来源永远压过声音。
0.5 这个阈值不是凭空猜的。我们在真实会议录音上、用自己的评测框架(scripts/meeting-diarization-eval.js)对它进行了调校。阈值过高会欠聚类——一个说话人被拆成两个。阈值过低会过聚类——两个相似的声音被合并。0.5 是我们最终收敛到的最佳平衡点。
实时分离:边说边标注
纯批处理分离很准确,但在通话过程中是看不见的。纯实时则即时却嘈杂。OpenWhispr 两者都跑:实时标签在会议进行中出现,会后再用一次完整的批处理来优化它们。
实时路径位于 src/helpers/liveSpeakerIdentifier.js。它以 16kHz 接入系统音频流,把每个 32 毫秒帧送进 Silero VAD,并不断累积语音帧,直到某个片段闭合(在约 512 毫秒的静音之后)或达到实时识别节奏(一旦累积到 ≥1.6 秒,每 1 秒一次)。一旦触发,它会提取一个 CAM++ 嵌入,将其与内存中当前通话嵌入及已存说话人档案的映射进行比对,并触发一个 IPC 事件—— meeting-speaker-identified——附带最佳匹配。React 端接收到后,原地更新转录气泡。
一旦有人在界面中手动设定了某个说话人的名字,这个指派就被 锁定。会后的批处理不得覆盖它,哪怕它自己的分析结果有异议。这一锁定行为位于 src/utils/transcriptSpeakerState.ts,而它很重要:最糟糕的用户体验,就是让一次自动处理悄悄覆盖掉用户的修正。
录制停止后,我们会对完整的 WAV 文件运行完整的离线分离。批处理拥有完整语境——它看到的是整场会议,而不只是最后的 1.6 秒——因此能产出更干净的最终标注。批处理结果会与实时标签对齐,尊重用户的每一处锁定,并用更准确的分组替换掉临时的猜测。
为什么混合模式胜过单独任何一种
纯实时很快,但在短片段上很嘈杂。纯批处理很准确,却要等到会议结束才出声。混合模式让你两者兼得——通话中即时反馈,以及一份反映完整语境分析的最终转录。代价只是多一步对齐,它在后台运行一次,实际上几乎不耗成本。
声纹档案:跨会议记住说话人
一旦你在某场会议里把某位说话人标注为“Alice”,OpenWhispr 就会在之后的每一场会议中自动为她标注——你无需再做任何事,而且她的声纹永远不会离开你的设备。
机制很直接:每位说话人的质心嵌入都会存入你的本地 SQLite 数据库。当未来某通话出现新的嵌入时,我们用余弦相似度把它与每一个已存档案比对。相似度超过阈值的匹配会自动指派名字。这个过程完全不涉及服务器。支撑它的三张表都是普通的 SQLite:
-- src/helpers/database.js
CREATE TABLE speaker_profiles (
id INTEGER PRIMARY KEY AUTOINCREMENT,
display_name TEXT NOT NULL,
email TEXT,
embedding BLOB NOT NULL, -- 512 float32s (~2KB)
sample_count INTEGER DEFAULT 1,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE speaker_mappings (
note_id INTEGER NOT NULL,
speaker_id TEXT NOT NULL,
profile_id INTEGER,
display_name TEXT NOT NULL,
PRIMARY KEY (note_id, speaker_id),
FOREIGN KEY (note_id) REFERENCES notes(id) ON DELETE CASCADE,
FOREIGN KEY (profile_id) REFERENCES speaker_profiles(id) ON DELETE SET NULL
);
CREATE TABLE note_speaker_embeddings (
note_id INTEGER NOT NULL,
speaker_id TEXT NOT NULL,
embedding BLOB NOT NULL,
PRIMARY KEY (note_id, speaker_id),
FOREIGN KEY (note_id) REFERENCES notes(id) ON DELETE CASCADE
);匹配分三档,以在置信度和误报之间取得平衡:
- 余弦相似度 ≥ 0.70: 自动确认。标签立即出现,无需任何提示。
- 0.55 ≤ 余弦相似度 < 0.70: 建议。界面会显示“这是 Alice 吗?”并附带确认 / 忽略按钮,等待你的输入。
- 余弦相似度 < 0.55: 保持匿名。该片段维持为“说话人 N”,直到你手动为其命名。
当你确认一个匹配时,已存的质心会通过滑动平均来更新,而非直接覆盖。公式很简单: new_avg = (stored_avg × sample_count + new_embedding) / (sample_count + 1)。这能应对声音漂移——一个寒冷的早晨、一支不同的麦克风、一根坏掉的线缆——而不会破坏未来的匹配。系统见过 Alice 声音的样本越多,她的档案就越稳定。
新档案还会触发 追溯重新标注。当你第一次为某位说话人命名时,一个后台任务会遍历过去每场会议已存的嵌入,找出匹配,并更新历史转录中此前未命名的说话人。被用户锁定的映射永远不会被触碰。结果是:为 Alice 命名一次,就能追溯地在她参加过的每一通话中为她标注。
如果连接了 Google Calendar 且会议有参会者,说话人选择器界面会预先填入他们的姓名和邮箱。一键即可把 说话人 2 → alice@example.com映射好并永久保存——参会者与声纹档案保持关联,因此即使换了设备,正确的名字也会出现。
你的数据究竟存放在哪里
音频永不离开设备。这不是一句营销口号——它是代码的机械属性,而且由于 OpenWhispr 是开源的,你可以亲自验证。 下面就是每一类数据在每个步骤究竟会发生什么。
| 数据 | 存放位置 | 是否离开设备? |
|---|---|---|
| 原始会议音频 | 操作系统临时目录,分离完成后删除 | 否——永不 |
| 分离用 ONNX 模型 | ~/.cache/openwhispr/diarization-models/ | 首次启动时下载一次,之后再不下载 |
| 说话人嵌入(声纹) | 本地 SQLite BLOB | 否——永不 |
| 说话人到姓名的映射 | 本地 SQLite 表 | 否——永不 |
| 转录文本 | 本地 SQLite(如你开启,可选云同步) | 仅在你主动选择时 |
会议的原始 PCM 音频会被写入你操作系统临时目录中的一个文件,交给本地派生的 sherpa-onnx 二进制文件,并在分离完成时于一个 finally块中删除。OpenWhispr 的分离流程中不存在任何打开网络套接字上传音频的代码路径。由于代码是开源的,你可以在 src/helpers/diarization.js, src/helpers/liveSpeakerIdentifier.js、 src/helpers/speakerEmbeddings.js 中 grep fetch, axios或 http ,亲自确认这一点。
Electron 主进程与渲染进程之间的 IPC 使用内置的 contextBridge ——它是单个进程内的内核级管道,而非网络套接字。说话人嵌入和声纹档案作为 SQLite BLOB,与你的笔记存储在同一个数据库文件中,享有相同的操作系统级文件权限。
一个例外,开诚布公
这个功能唯一的网络活动,是首次启动时的一次性模型下载——从 sherpa-onnx 的 GitHub 发布页 CDN 获取三个 ONNX 文件。我们明确说明这一点,是因为“安装时一次网络调用、之后零调用”与“始终在线的云端推理”在隐私层面是截然不同的两个故事;而作为一款开源会议助手,我们宁愿主动告诉你,也不愿让你自己去发现。
坦诚的局限
本地分离很好,但并不完美。下面是它确实力不从心的几种情况,以及我们针对每一种所做的应对。
- 说话人重叠。 两个人同时讲话,对单一标签归属来说本质上是有损的。Pyannote 3.0 能检测出重叠区域,但每个片段只有一个 speaker_id,不足以表示“两个人同时在说话”。我们会把这些片段标为临时,让你在界面中加以修正。
- 冷启动准确率。 与一位新同事的第一场会议只能依靠 VoxCeleb 训练出的通用特征。一旦你为他标注过一次、档案积累了几个样本,后续会议就会明显更准。
- 简短发言。 “嗯”、“好的”、一声短促的笑——任何短于约 0.8 秒的内容都无法产生可靠的嵌入。我们会把这些片段从嵌入阶段剔除,转而依据周围上下文做标签传播。
- 远场或低信噪比音频。 在嘈杂房间里用免提电话的人,质量会优雅但明显地下降。自动增益控制和 VAD 调校会有所帮助,但再精巧的模型也无法完全克服糟糕的物理条件。
- 仅 CPU 推理。 GPU 会更快,但我们刻意不要求它。实际上,现代 CPU 的吞吐量已经足够:每个嵌入约 100 毫秒,在 M1 Mac 上对一场 45 分钟的会议做批处理分离约需 30 秒。
我们在开源仓库中公开了自己的评测框架( (scripts/meeting-diarization-eval.js) ),这样你就能用自己的录音亲自测量上述任何一项——而不是我们精挑细选的演示片段。这比营销材料里一个光鲜的数字要诚实得多。
我们为什么选择 Sherpa-ONNX
我们需要一个跨平台、无需 Python、CPU 上速度快且开源的分离运行时。Sherpa-ONNX 是唯一四项全中的选项。
我们的要求很具体。它必须能在 macOS(Intel 和 Apple Silicon 都要)、Windows x64 和 Linux x64 上运行,且无需分别维护不同的代码路径。它不能捆绑 PyTorch——光是运行时就约 1.5GB,而且要在交互级延迟下可用基本上得有 GPU。它不能要求 CUDA。而且许可证要足够宽松,能在一款“商用但开源”的桌面应用中发布。
以下是我们实际评估过的选项:
- pyannote.audio(Python): 排除。在 Electron 应用里捆绑 PyTorch 根本行不通。
- NVIDIA NeMo MSDD: 依赖 PyTorch,无官方 ONNX 导出,以 GPU 为中心。
- Picovoice Falcon: 按活跃用户收费的商业许可,外加首次运行时需云端激活。
- Apple CoreML + Speech 框架: 仅限 macOS,没有跨平台对等性。
- sherpa-onnx: Apache 2.0 许可,原生 ONNX 二进制文件,约 35MB 的模型,派生进程读取 stdout 的接口,由 k2-fsa 积极维护。同一套工具链也支撑着其他已发布产品中的生产级 ASR。
一个坦诚的提醒:sherpa-onnx 的离线说话人分离二进制文件相对较新。我们将版本锁定在 v1.12.23,并在升级锁定版本之前,针对每次更新运行我们完整的评测框架。对于这样一个承重的部件,这是正确的取舍。
常见问题
- 有没有一款不会把我的音频上传到云端的隐私会议记录工具?
- 有。OpenWhispr 是一款免费、开源的会议记录工具,你的音频永不离开设备。转录在本地用 OpenAI Whisper 或 NVIDIA Parakeet 运行,说话人分离在本地用 pyannote 分割和 CAM++ 嵌入运行,声纹则存储在你自己机器上的本地 SQLite 文件中——而不是我们的服务器上。
- Otter.ai、Fireflies 或 Granola 能看到我的会议吗?
- 能。基于云的 AI 记录工具会把你的会议音频上传到它们的服务器,以便转录和分离。说话人标签、转录文本和声音嵌入都在它们的基础设施中计算,并存储在它们的数据库里。如果你需要一个能把每一字节音频都留在设备上的替代方案,你需要的是 OpenWhispr 这样的本地优先工具。
- 什么是说话人分离?
- 说话人分离是为录音标注“谁在何时说话”的过程,与回答“说了什么”的转录有所区别。一份完成分离的转录会为每段发言打上说话人标签——Alice、Bob、说话人 3——而不是一整面无差别的文字墙。
- 本地说话人分离真的管用吗,还是说它是云端的降级版?
- 它管用。OpenWhispr 的流程——pyannote-segmentation-3.0 加上来自 3D-Speaker 项目的 CAM++ 嵌入,通过 sherpa-onnx 运行——在标准基准上的分离错误率与商业云 API 处于同一水平。主要的坦诚取舍在于对重叠说话人的处理,以及对系统从未听过的声音的冷启动准确率。
- 本地分离模型占多少磁盘空间?
- 总共约 45MB:pyannote 分割(6.6MB)加上 CAM++ 说话人嵌入模型(28MB)再加 Silero VAD(2MB)。这三者会在首次启动时下载一次,并缓存到 ~/.cache/openwhispr/diarization-models/。此后它们就留在磁盘上,再也不会重新下载。
- 运行 OpenWhispr 的本地分离需要 GPU 吗?
- 不需要。一切都通过 ONNX Runtime 在 CPU 上运行。现代 Apple Silicon 和较新的 x86 CPU 都能轻松应对——实际上,在 M1 Mac 上对一场 45 分钟的会议做批处理分离约需 30 秒,而通话过程中实时说话人标签会在一两秒内出现。
- OpenWhispr 真的是开源的吗?
- 是的。分离代码、转录流程以及桌面应用的其余部分都在 GitHub 上开源。本文中的隐私声明可直接验证:在分离辅助代码里 grep fetch、axios 或 http,你在分离路径中找不到任何网络调用。
- 通话中两个人同时讲话会怎样?
- Pyannote 3.0 会明确检测并标注重叠区域,但重叠期间的单一标签归属本质上是有损的。我们会把这些片段在转录中标为临时,让你通过点击说话人标签来修正。你的手动修正会被锁定,永远不会被之后的自动处理覆盖。
试试这款隐私会议记录工具
OpenWhispr 是一款免费、开源的会议助手,你的音频永不离开设备。本地分离、本地转录、本地声纹档案。支持 macOS、Windows 和 Linux。
无需账号 · 离线可用 · 永久开源