声纹注册与说话人识别(认领式)
本技能支持把说话人声纹注册到本地声纹库,之后每次转录自动识别已注册说话人并直接标注其名称;未识别的说话人经用户确认身份后可"认领"入库,下次自动识别。适用场景:在大量客户录音中自动标注"我"的声音,区分自己与对方。
工作原理
- 转录时(默认 MOSS 路径、开启
diarize),CAM++ 声纹模型为每个文件内说话人提取 192 维声纹向量(每人最多取 30 秒、至少 3 秒音频)。 - 转录响应携带
speaker_embeddings(各说话人向量,供认领注册)、speaker_identification(与声纹库的逐人比对结果,首次空库时全部为 null)和speaker_states(每位说话人的完整识别状态)。 speaker_states状态语义:matched(命中注册声纹)、unknown(有声纹未达阈值,可认领注册)、insufficient_audio(总发言不足 3 秒,无声纹向量,只能本稿标注不能注册)、extraction_failed(提取/识别失败,已降级保持匿名并告警)、disabled(fast/单段显式关闭提取/CAM++ 不可用)。认领流程只对unknown执行。disabled只在实际未提取声纹的路径出现:多段长录音的跨段链接不受FUNASR_MOSS_SPEAKER_EMBEDDINGS=0开关影响(既有行为),链接产出向量的标签在空库时为unknown、命中时为matched,不会被误标为 disabled;开关仅跳过单段录音的附加提取。状态、向量、warnings 与 CLI 认领提示逐标签一致。- 比对使用余弦相似度,默认阈值 0.55(环境变量
FUNASR_SPEAKER_IDENTIFY_THRESHOLD可调,范围 0–1;由六段真实录音验收标定——同人跨录音最低 0.56、非同人最高 0.14,分离边际充足,0.60 会漏识微信 8kHz 弱信道录音)。达到阈值的标签标注注册名,未达阈值保持匿名并可在 warnings 中看到提示。score 是归一化向量的余弦相似度(0–1),不是经过校准的身份正确概率。 - 同一次录音内,一个注册名最多命中一个说话人标签(分数最高者优先),避免两个匿名标签都映射到同一注册人。
- 命中的说话人在输出 Markdown 中直接显示注册名(如"杨律师 00:02:49"),并在文件头附识别注记(含声纹相似度);未命中的保持"发言人N"。
- 声纹库读写共用存储层:注册向量按当前模型(CAM++,192 维)显式校验维度/数值/范数,非法输入被拒绝且不改库文件;历史坏条目读取时隔离告警,不影响其他有效记录;识别或提取故障只降级身份识别,不阻断文字稿交付。
注册流程(认领式,无需预录样本)
注册样本直接取自真实转录结果(同信道、同麦克风条件),比预先录制样本更可靠。首次注册发生在第一份多人录音转录完成后:
- 转录:
POST /transcribe {"file_path": "..."}(默认参数即可,diarize默认开启)。 - 查看识别结果:响应中
speaker_identification显示各标签是否已识别(首次库为空,全部为 null);speaker_embeddings提供各标签的声纹向量。 - 生成说话人叙述并询问用户:Agent 按响应
segments为每位未识别说话人归纳简要叙述——发言占比、内容概述(诉求/立场/信息)、身份线索(自称、被称呼、角色口吻)——随询问一并呈现,供用户凭内容打标。例如:"发言人1(占 35% 发言):自述是房东,询问起诉流程;发言人2(占 65%):律师口吻分析证据,开头称对方'张女士'。这两位分别是谁?" - 认领注册:对用户命名的说话人,调用:
curl -s -X POST http://127.0.0.1:8765/speaker/register \
-H "Content-Type: application/json" \
-d '{"name": "杨律师", "embedding": <响应中 speaker_embeddings.S01 的数组>, "source_file": "meeting.m4a", "source_label": "S01"}'同名重复注册视为重新认领,覆盖旧向量。注册完成后,同一份录音无需重跑;下一份录音中同一人的声音将被自动识别并直接标注。
识别效果验证
- 转录新录音时观察
speaker_identification中的score:同信道、同麦克风条件下,同一人通常在 0.7 以上;跨信道(如电话 8kHz 与现场 16kHz 混用)分数会明显下降。注意 score 是余弦相似度,不等于身份正确概率。 - 用一段只包含目标说话人的音频快速验证:
curl -s -X POST http://127.0.0.1:8765/speaker/test \
-H "Content-Type: application/json" \
-d '{"file_path": "/path/to/我的语音.wav"}'返回与库内每个注册名的相似度分数、最佳匹配(best)与阈值判定(best_is_match)。最高分低于阈值时 identified 为 null——那是"未识别"而不是"已识别为最高分者"。CLI 等价命令:python3 scripts/speaker_registry.py test /path/to/音频 --server http://127.0.0.1:8765。
声纹库管理
声纹库文件为 assets/speaker-profiles.json(本地生成,含姓名与声纹向量,已被 .gitignore 排除,不入公开仓库,不随技能分发)。库文件由服务与 CLI 共用的存储层管理:原子保存(写入中断不损坏旧库)、进程间锁(并发注册/删除不丢记录)、损坏保护(库损坏时读路径降级告警、写路径拒绝并保留原件)、仅当前用户可读写。
# 列出已注册说话人(无需服务运行)
python3 scripts/speaker_registry.py list
# 删除标错的注册(删除后可重新认领)
python3 scripts/speaker_registry.py remove 杨律师
# 认领注册:从转录结果 JSON 按标签取向量(需服务运行;仅对 unknown 状态、
# 且用户确认身份并同意长期注册时使用)
python3 scripts/speaker_registry.py claim 杨律师 --result /tmp/asr-result.json --label S01
# 服务端点等价操作
curl http://127.0.0.1:8765/speaker/list
curl -s -X POST http://127.0.0.1:8765/speaker/remove -H "Content-Type: application/json" -d '{"name": "杨律师"}'识别错了的纠错方式:remove 删除该注册名 → 重新转录或复用最近响应中的向量 → 以正确身份重新认领注册。
边界与限制
- 仅 MOSS 路径:识别与认领只在默认
moss-mlx后端生效;显式选择paraformer/paraformer-onnx时不识别(响应无相关字段)。 - 单人快速模式不识别:
fast: true或diarize: false关闭说话人分离,同时跳过声纹提取。 - 短发言不参与:总发言不足 3 秒的说话人无声纹向量,不参与识别(与跨段链接相同的下限)。
- 辅助标注定位,不是证据级身份认定:识别结果带声纹相似度(余弦相似度,非概率、非证据),跨信道、噪音、感冒疲劳等因素会降低匹配分。用于快速定位"谁说了什么"可以,用于诉讼中的身份证明不可以。
- 准确率边界:现有实测样本(六段录音验收)包含参与注册与调参的录音,不能证明对完全未见录音的泛化准确率;独立样本的误识/漏识量化评估另行推进(NOT_VERIFIED)。
- 阈值可调:默认 0.55 已按真实录音标定;误识别多时调高(如 0.60–0.65),弱信道录音漏识时可结合人工确认而非一味调低。需重启服务生效。
- 性能:声纹提取复用 CAM++ 模型(28MB),单段短录音的提取与比对通常在 1–2 秒内;
FUNASR_MOSS_SPEAKER_EMBEDDINGS=0可跳过单段录音的声纹提取以省资源(多段长录音的跨段链接不受此开关影响)。
隐私与合规(重要)
- 声纹属《个人信息保护法》下的生物识别信息,即敏感个人信息。
- 注册本人声纹无合规障碍。
- 若要注册客户或第三方的声纹并长期自动识别,应当事先取得该个人的单独同意;仅做单次录音内的匿名分离(不注册)不涉及此要求。
- 声纹库文件仅保存在本机,请勿通过任何渠道分发;技能分享、备份或提交代码时确认
assets/speaker-profiles.json未被包含(.gitignore 已覆盖,git status可复核)。
回归验证
cd <skill目录>/scripts && python3 verify_speaker_registry.py覆盖向量输入校验(维度/空/零/NaN/Inf/float32 溢出全部拒绝)、识别故障隔离(坏条目隔离告警、识别异常降级匿名)、说话人状态契约(空库/部分命中/短发言/提取失败/主动关闭/fast)、库读写端点(注册/覆盖/列表/删除/损坏拒绝 409)、原子保存与并发安全(写入中断保旧库、双进程并发注册不丢项、文件权限),不下载模型。真实录音的注册→跨录音识别准确率需用实际样本人工验收。