扬声器日记服务器
  
GPU通过REST API、WebSocket流和用于AI代理的MCP服务器加速了说话者的日记化、转录和情绪识别。将pyannote.audio+更快的耳语+emotion2vec组合成一个FastAPI服务,在对话中保持说话者身份。
截图
示例 Next.js前端:
Voice Profile Management
Process Audio
Conversation Detail
Conversations List
Speaker Management
Live Recording
特性
- 持续的说话者身份 --注册一次语音,在未来的每次录音中都能识别。
- 追溯更新 --识别一个片段,并自动重新标记该语音的每个过去片段。
- 转录 --更快的耳语(
large-v3-turbo默认值,任何Whisper变体),单词级时间戳,99种语言。 - 双探测器情绪识别 --emotion2vec+(通用)与每个说话者的语音配置文件(个性化)相结合。9个类别。
- 直播 --使用VAD门控段刷新进行WebSocket音频摄取。
- 批量处理 --用于批量摄取的多GPU脚本(
run_batch.sh). - REST API 在
/api/v1/*交互式Swagger文档位于/docs. - MCP服务器 在
/mcp(JSON-RPC 2.0/HTTP)为Claude Desktop、Flowise或任何MCP客户端提供了11个工具。 - 备份/恢复 --将扬声器+分段+设置导出为JSON配置文件和检查点。
- SQLite WAL --处理数千个并发读取的对话。
技术栈
| 组件 | 实施 |
|---|---|
差异化 pyannote/speaker-diarization-community-1 (pyannote.audio 4.0.1) | |
| 扬声器嵌入 | pyannote/embedding (512-D) |
| 转录 | 更快的耳语1.2.1/C翻译2, large-v3-turbo 默认值 |
| 情感 | 通过FunASR获取情感2vec_plus_large(1024-D,9个类别) |
| API | FastAPI 0.115.5+WebSockets+uvicorn |
| 数据库 | SQLAlchemy 2.0/SQLite(WAL) |
| ML运行时 | PyTorch 2.10+CUDA 12.8(Blackwell/RTX 5090 sm_120 准备就绪) |
| MCP传输 | JSON-RPC 2.0通过HTTP(+可选SSE),无外部MCP库 |
需求
- GPU: NVIDIA搭载CUDA 12.x。已在RTX 3090(24 GB)和RTX 5090上测试。如果您选择较小的Whisper型号,则可以在6 GB+卡上运行。
- VRAM球场: 2-3GB的日记/嵌入+2GB的emotion2vec+耳语(0.5-4GB,取决于型号)。
large-v3-turbo+情感总计≈6-7GB。 - 操作系统: Linux(在Ubuntu上测试)。macOS/Windows通过Docker桌面+WSL2。
- 其他: ffmpeg、git。Python 3.11或3.12,如果在Docker之外运行。
- 拥抱脸令牌 接受pyannote条款(见下文)。
快速开始
1.拥抱脸部通道
在以下位置创建帐户+令牌 huggingface.co/settings/tokens,然后接受以下条款:
2.Docker(推荐)
git clone https://github.com/snailbrainx/speaker-diarization-server.git
cd speaker-diarization-server
cp .env.example .env # then edit .env and set HF_TOKEN
docker compose up --build # first run downloads ~3-5 GB of models3.地方发展
sudo apt-get install -y ffmpeg git
python -m venv venv && source venv/bin/activate
pip install -r requirements.txt
cp .env.example .env # edit HF_TOKEN
./run_local.sh4.验证
- Swagger用户界面:
http://:8418/docs - 健康:
curl http://:8418/api/v1/status - MCP:
http://:8418/mcp
远程访问
没有内置身份验证。为远程主机使用SSH隧道:
ssh -L 8418:localhost:8418 user@host对于网络部署,将其置于反向代理(nginx+HTTPS+auth)之后。浏览器中的WebSocket麦克风捕获 需要HTTPS/WSS --地点 cert.pem/key.pem 在 certs/ 服务器自动以HTTPS模式启动。
配置
所有配置都是环境变量(请参见 .env.example 对于策划的列表)。一切都是可选的,除了 HF_TOKEN.
必需
| 变量 | 目的 |
|---|---|
HF_TOKEN | 用于pyannote模型访问的HuggingFace令牌 |
存储和网络
| 变量 | 默认值 | 用途 |
|---|---|---|
DATABASE_URL | sqlite:////app/volumes/speakers.db | SQLAlchemy URL。默认情况下采用Docker卷布局。 |
DATA_PATH | ./data | 录制、流片段、临时文件的根。 |
VOLUMES_PATH | ./volumes | DB+模型缓存的根。 |
PORT | 8418 | HTTP/HTTPS端口 |
CORS_ORIGINS | *(未设置)* | 逗号分隔的起源。留空仅供内部使用。 |
LOG_LEVEL | INFO | Python日志级别。 |
语音和情感调节(也可在运行时通过以下方式编辑 /api/v1/settings/voice)
| 变量 | 默认值 | 用途 |
|---|---|---|
SPEAKER_THRESHOLD | 0.30 | 扬声器匹配的余弦阈值。降低→ 更严格。 |
CONTEXT_PADDING | 0.15 | 嵌入前在片段周围添加的音频秒数。 |
SILENCE_DURATION | 0.5 | 流媒体:刷新片段前的几秒钟静音。 |
FILTER_HALLUCINATIONS | true | 放下耳语填充短语(“谢谢”等)。 |
EMOTION_THRESHOLD | 0.6 | 全球情绪匹配敏感度。更高→ 更严格。 |
模型
| 变量 | 默认值 | 用途 |
|---|---|---|
WHISPER_MODEL | large-v3-turbo | 任何更快的耳语型号ID。请参阅 .env.example. |
WHISPER_LANGUAGE | en | auto 用于99语言检测或ISO-639代码。 |
EMOTION_MODEL | iic/emotion2vec_plus_large | FunASR情感模型ID |
ENABLE_PERSONALIZED_EMOTIONS | true | 在情绪匹配中使用每个说话者的语音配置文件。 |
OFFLINE_MODE | false | 仅强制缓存模型;永远不要撞到轮毂。 |
图形处理器
| 变量 | 默认值 | 用途 |
|---|---|---|
CUDA_VISIBLE_DEVICES | *(未设置)* | 选择物理GPU。 0, 0,2等等。 |
CUDA_DEVICE_ORDER | PCI_BUS_ID | 被钉住 run_local.sh 和 docker-compose.yml 所以指数匹配 nvidia-smi 而不是CUDA的“最快第一”改组。 |
CLEANUP_VRAM_THRESHOLD_GB | 12 | 仅当分配的内存超过此值时,才运行VRAM清理循环。 |
原理
upload / WS chunk
│
▼
┌─────────────────┐ ┌──────────────────┐
│ Whisper │ │ pyannote │
│ transcription │ │ diarization │ run in parallel
│ (text + words) │ │ (speaker turns) │ (ThreadPoolExecutor)
└────────┬────────┘ └────────┬─────────┘
└────────────┬────────┘
▼
segment alignment by timestamp
│
▼
pyannote embedding (512-D) per segment
│
▼
cosine match vs known speaker profiles
│ │
▼ ▼
known speaker Unknown_NN (auto-enrolled)
│
▼
emotion2vec (1024-D) + optional voice-profile emotion match
│
▼
ConversationSegment → SQLite (WAL)关键行为:
- 平行耳语+pyannote --比顺序快约2倍。
- 上下文填充 --片段每侧0.15秒的音频使嵌入对背景噪声具有鲁棒性。
- 自动注册 --不明声音
Unknown_N立即分析,以便他们积累样本。重命名/识别和每个过去Unknown_N分段重新标记。 - 错误识别的标志 --标记的细分市场
is_misidentified不包括嵌入平均值,保持配置文件干净。 - 双探测器情绪 --emotion2vec总是运行;如果说话者对一种情绪有≥3个语音样本,则语音简档检测器也会运行。根据确定性的决胜规则,最佳比赛获胜。
- 个性化学习 --用户情绪校正将1024-D情绪嵌入和512-D语音嵌入与加权平均合并到说话者的简档中。
情感识别
类别(9)
emotion2vec+large将每个片段分为以下标签之一:
angry · disgusted · fearful · happy · neutral · other · sad · surprised · unknown
双探测器决策
每个片段都运行着emotion2vec基础检测器。如果扬声器有 ≥3个用户校正样本 对于任何情绪,每个说话者的第二个语音简档检测器都会并行运行。这两个分数相加:
| 案例 | 最终情绪 |
|---|---|
| 只有emotion2vec有一场自信的比赛 | |
emotion2vec回归 neutral 或 `` | neutral |
| 两个探测器都同意 | 一致的情绪(更高的置信度) |
| 探测器不一致 | neutral (保守派的退路) |
| 语音轮廓检测器获胜(≥阈值) | 轮廓获胜者 |
服务器从两个检测器返回原始分数,以便客户端可以根据需要覆盖回退。
个性化流程
- 用户上传音频→ 段7被标记
neutralemotion2vec的置信度为0.94。 - 用户不同意,
POST /api/v1/conversations/{id}/segments/7/correct-emotion随着{"emotion_category": "surprised"}. - 服务器同时存储 1024-D情感向量嵌入 和那个 512-D pyannote语音嵌入 进入演讲者的
surprised轮廓,与任何先前样本平均(EWMA风格)。 - 一旦该简档达到3个样本,语音简档检测器就会为该说话者的未来片段激活。
- 当后面的片段具有相似的声音特征时,声音轮廓检测器返回
surprised超过阈值,赢得了emotion2vec的猜测。
还设置了更正 emotion_corrected=true 因此,它们被排除在个性化训练循环之外(没有反馈污染)。
示例——双探测器输出
来自一段10秒的大笑片段,该说话者之前没有语音配置文件:
{
"text": "Oh my god that is funny. Oh wow. Oh that's brilliant.",
"emotion_category": "neutral",
"emotion_confidence": 0.937,
"detector_breakdown": {
"emotion2vec_detector": { "emotion": "surprised", "confidence": 0.937 },
"voice_profile_detector": null,
"final_decision": {
"emotion": "neutral",
"confidence": 0.937,
"reason": "Disagree: surprised vs neutral",
"voice_profile_available": false
}
}
}emotion2vec说 *惊讶的* 但是个性化检测器不可用(还没有3+个样本),因此服务器退回到 neutral经过三次修正后 surprised 对于这位演讲者,轮廓检测器会同意,最终的决定也会改变。
REST API
完整的交互式文档,请访问 /docs (Swagger用户界面)。OpenAPI JSON位于 /openapi.json.端点组:
GET /api/v1/status--健康+GPU。GET/POST /api/v1/speakers,PATCH/DELETE /api/v1/speakers/{id}--演讲者CRUD、注册、重命名。GET /api/v1/speakers/{id}/emotion-profiles,每种情绪阈值——情绪档案CRUD。GET/POST /api/v1/conversations,PATCH/DELETE /api/v1/conversations/{id}--对话CRUD。POST /api/v1/conversations/{id}/reprocess,POST /api/v1/conversations/{id}/recalculate-emotions--重新运行管道。POST /api/v1/conversations/{id}/segments/{sid}/{identify,correct-emotion},PATCH .../misidentified--分段校正。GET /api/v1/conversations/segments/{sid}/audio--提取分段音频。POST /api/v1/process--上传并处理整个文件。GET/POST /api/v1/settings/voice,POST /api/v1/settings/voice/reset--运行时可调语音设置。GET/POST /api/v1/profiles,检查点,下载,导入,还原--备份/还原。WS /api/v1/streaming/ws--实时音频摄取。
示例——上传+处理
curl -X POST http://localhost:8418/api/v1/process \
-F "audio_file=@meeting.wav"响应(为了可读性,修剪成一段):
{
"id": 42,
"title": "Uploaded: meeting.wav",
"duration": 137.39,
"status": "completed",
"num_segments": 24,
"num_speakers": 2,
"transcript_segments": [
{
"id": 911,
"conversation_id": 42,
"speaker_id": 17,
"speaker_name": "Unknown_1776798145477718",
"text": "Call me Ishmael. Some years ago, never mind how long precisely...",
"start_offset": 0.05,
"end_offset": 6.71,
"confidence": 1.0,
"emotion_category": "neutral",
"emotion_confidence": 1.0,
"emotion_corrected": false,
"emotion_misidentified": false,
"detector_breakdown": null,
"words": [
{ "word": " Call", "start": 0.05, "end": 0.57, "probability": 0.91 },
{ "word": " me", "start": 0.57, "end": 0.75, "probability": 0.99 },
{ "word": " Ishmael.","start": 0.75, "end": 1.37, "probability": 0.99 }
]
}
]
}身份不明的声音会自动注册为 Unknown_ 因此它们积累样本。追溯重命名:
# Identify an unknown speaker (retroactively labels every past segment with that voice)
curl -X POST http://localhost:8418/api/v1/conversations/42/segments/911/identify \
-H 'Content-Type: application/json' \
-d '{"speaker_name": "Alice", "enroll": true}'
# Correct an emotion (feeds personalization)
curl -X POST http://localhost:8418/api/v1/conversations/42/segments/911/correct-emotion \
-H 'Content-Type: application/json' \
-d '{"emotion_category": "surprised"}'MCP服务器
基于HTTP的JSON-RPC 2.0 /mcp兼容Claude Desktop、Flowise和任何符合规范的客户端。
{
"mcpServers": {
"speaker-diarization": {
"url": "http://localhost:8418/mcp",
"transport": "http"
}
}
}十一种工具: list_conversations, get_conversation, get_latest_segments, list_speakers, rename_speaker, delete_speaker, delete_all_unknown_speakers, identify_speaker_in_segment, update_conversation_title, reprocess_conversation, search_conversations_by_speaker所有突变(识别/重命名)都会追溯更新过去的片段,因此AI不必手动协调身份。
数据持久层
speaker-diarization-server/
├── data/
│ ├── recordings/ # permanent audio (uploads + concatenated streams)
│ ├── stream_segments/ # per-conversation live-segment WAVs
│ └── temp/ # transient
├── volumes/
│ ├── speakers.db # SQLite (WAL)
│ └── huggingface_cache/ # pyannote + Whisper + emotion2vec
└── backups/
├── profile_.json # full speaker + segment + settings snapshot
└── checkpoint__.json # timestamped state captureDocker绑定挂载 ./volumes, ./data, ./backups,以及 ./certs (只读)放入容器中。两次跑步之间没有其他东西。
故障排除
“未找到HuggingFace令牌” --确认 HF_TOKEN 在 .env,无引号/空格,接受模型术语。
“无法加载libcunn_cnn.so.9” — run_local.sh 套 LD_LIBRARY_PATH 自动。手册: pip install nvidia-cudnn-cu12==9.* nvidia-cublas-cu12.
Docker权限错误 data/, volumes/, backups/ --容器以uid 1000运行。如果您的主机uid不同, sudo chown -R 1000:1000 data/ volumes/ backups/ 一次。
Docker GPU不可见 --验证 docker run --rm --gpus all nvidia/cuda:12.4.0-base-ubuntu22.04 nvidia-smi 作品;如果没有,请重新安装NVIDIA容器工具包。
选择了错误的GPU --set CUDA_VISIBLE_DEVICES 以匹配 nvidia-smi 你想要的索引; CUDA_DEVICE_ORDER=PCI_BUS_ID 已经为你钉好了。
“CUDA内存不足” --开关 WHISPER_MODEL 到一个较小的变体(small 或 base)或提高 CLEANUP_VRAM_THRESHOLD_GB 因此,清理工作更加频繁。
无法识别说话者 --注册时提供10-30秒的纯净音频。降低 SPEAKER_THRESHOLD 对于具有挑战性的音频(电影、音乐),约为0.20。
浏览器麦克风不工作 --WebSocket麦克风捕获需要HTTPS。生成证书/密钥或将其放入 certs/.
许可证
麻省理工学院——见 LICENSE所有捆绑的依赖项(pyannote.audio、快速耳语、FastAPI、PyTorch、SQLAlchemy、Pydantic)都在MIT或BSD下。pyannote 模型 要求HuggingFace账户+代币+条款接受;该软件仍然是开源的。
学分
- pyannote.audio -HervéBredin等人。
- 更快的耳语 --SYSTRAN
- 耳语 --OpenAI
- 快速API -塞巴斯蒂安·拉米雷斯
计划的
- 人工智能在定稿时生成对话摘要和标题。
- 在转录本上进行矢量搜索,以查找语义对话。
免责声明
按原样提供,不提供任何保证。说话人识别是概率性的——手动验证重要身份,并在记录其他身份之前获得同意。该代码库的部分内容与AI助手进行了配对编程;在关键设置中部署之前进行审查。
