3_diarization — микросервисная транскрибация с диаризацией
Три Docker-контейнера: Whisper (GPU) + pyannote (CPU) + API Gateway. Работают параллельно. Итоговое время ≈ max(whisper, pyannote), не сумма.
Архитектура
POST /process (audio.aac)
│
┌──────┴──────┐
│ Gateway │ :8000
└──────┬──────┘
┌───────────┴───────────┐
▼ ▼
┌──────────────┐ ┌──────────────┐
│ Whisper │ │ Pyannote │
│ (GPU) │ │ (CPU) │
│ :8001 │ │ :8002 │
└──────┬───────┘ └──────┬───────┘
│ │
▼ ▼
текст + слова кто когда говорил
с таймкодами SPEAKER_00: 0–4с
│ │
└───────────┬───────────┘
▼
reconciliation
(сшивка по словам)
│
▼
SPEAKER_00: Привет, как дела с проектом?
SPEAKER_01: Нормально, закончил doctype.
Предварительные требования
-
Docker + Docker Compose v2+
-
NVIDIA Container Toolkit (
nvidia-ctk)# проверить, что GPU виден Docker'у docker run --rm --gpus all nvidia/smi -
HuggingFace-токен для pyannote
- Создать токен: https://huggingface.co/settings/tokens
- Принять условия (заполнить форму на каждой странице):
Быстрый старт
# 1. Клонировать / скопировать папку 3_diarization
cd 3_diarization
# 2. Создать .env из примера и вставить свой HF-токен
cp .env.example .env
nano .env # вставить HF_TOKEN=hf_...
# 3. Собрать и запустить
docker compose up --build -d
# 4. Подождать ~1–2 минуты, пока модели загрузятся
docker compose logs -f
# Ждём:
# whisper-service: "Model loaded in X.Xs"
# pyannote-service: "Pipeline loaded in X.Xs"
# 5. Проверить здоровье
curl http://localhost:8000/health
API
POST /transcribe — только транскрибация
curl -X POST http://localhost:8000/transcribe \
-F "file=@audio.aac" \
-F "language=ru" \
-F "initial_prompt=Event Forge, Frappe, doctype" \
-F "batch_size=16"
Ответ (JSON):
{
"language": "ru",
"language_probability": 0.99,
"duration": 197.6,
"processing_time": 8.2,
"segments": [
{
"start": 0.0,
"end": 3.2,
"text": "Привет, это запись.",
"words": [
{"start": 0.0, "end": 0.4, "word": " Привет"},
{"start": 0.4, "end": 0.6, "word": ","},
{"start": 0.7, "end": 1.1, "word": " это"},
{"start": 1.1, "end": 1.5, "word": " запись."}
]
}
]
}
POST /diarize — только диаризация
curl -X POST http://localhost:8000/diarize \
-F "file=@audio.aac" \
-F "num_speakers=2"
Ответ:
{
"speakers": ["SPEAKER_00", "SPEAKER_01"],
"num_speakers": 2,
"processing_time": 45.3,
"turns": [
{"start": 0.0, "end": 4.2, "speaker": "SPEAKER_00"},
{"start": 4.2, "end": 9.1, "speaker": "SPEAKER_01"}
]
}
POST /process — транскрибация + диаризация (параллельно)
curl -X POST http://localhost:8000/process \
-F "file=@audio.aac" \
-F "language=ru" \
-F "initial_prompt=Event Forge, Frappe, doctype, Vladimir" \
-F "num_speakers=2" \
-F "batch_size=16"
Ответ:
{
"language": "ru",
"duration": 197.6,
"processing_time": 47.1,
"whisper_time": 8.2,
"pyannote_time": 45.3,
"speakers": ["SPEAKER_00", "SPEAKER_01"],
"num_speakers": 2,
"utterances": [
{
"speaker": "SPEAKER_00",
"start": 0.0,
"end": 4.1,
"text": "Привет, как дела с проектом?",
"words": [...]
},
{
"speaker": "SPEAKER_01",
"start": 4.2,
"end": 9.0,
"text": "Нормально, закончил doctype для метрик.",
"words": [...]
}
]
}
Форматы вывода
Добавьте response_format в /transcribe или /process:
# SRT-субтитры с именами спикеров
curl -X POST http://localhost:8000/process \
-F "file=@audio.aac" \
-F "language=ru" \
-F "response_format=srt"
# VTT
-F "response_format=vtt"
# Текст (спикер: реплика)
-F "response_format=txt"
Параметры
| Параметр | Где | По умолчанию | Описание |
|---|---|---|---|
language |
whisper | auto | Код языка (ru, en, de, ...) |
initial_prompt |
whisper | — | Термины/имена через запятую |
beam_size |
whisper | 5 | Размер beam search |
batch_size |
whisper | 16 | Размер батча (1–32) |
num_speakers |
pyannote | auto | Точное число спикеров |
min_speakers |
pyannote | — | Минимум спикеров |
max_speakers |
pyannote | — | Максимум спикеров |
response_format |
gateway | json | json / srt / vtt / txt |
Конфигурация через .env
# Обязательные
HF_TOKEN=hf_xxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# Опциональные
WHISPER_MODEL=large-v3 # tiny/base/small/medium/large-v3/turbo
WHISPER_BATCH_SIZE=16 # размер батча
PYANNOTE_DEVICE=cpu # cpu или cuda
Полезные команды
# Логи конкретного сервиса
docker compose logs -f whisper-service
docker compose logs -f pyannote-service
# Перезапустить один сервис (например, после смены модели)
docker compose restart whisper-service
# Остановить всё
docker compose down
# Пересобрать всё с нуля
docker compose down && docker compose up --build -d
Производительность (RTX 5060 16 ГБ)
Ориентировочные цифры для 1 часа аудио:
| Операция | Время |
|---|---|
| Whisper large-v3 b=16 | ~2–4 мин |
| Pyannote CPU | ~5–10 мин |
| /process (параллельно) | ~5–10 мин |
| Reconciliation | ~1 сек |
Bottleneck — pyannote на CPU. Если нужно ещё быстрее — поменяйте
PYANNOTE_DEVICE=cuda в .env и пересоберите pyannote-service с GPU-версией
PyTorch (потребует изменения Dockerfile).
Траблшутинг
Контейнер whisper-service не стартует / no CUDA-capable device — проверьте
nvidia-container-toolkit. Тест: docker run --rm --gpus all nvidia/smi.
pyannote-service падает с 401 Unauthorized — невалидный HF_TOKEN или не
приняты условия моделей на HuggingFace.
CUBLAS_STATUS_NOT_SUPPORTED — int8 на Blackwell. В конфиге уже стоит
WHISPER_COMPUTE_TYPE=float16.
OOM при batch_size=16 — уменьшите WHISPER_BATCH_SIZE=8 в .env.
Первый запрос медленный — модели загружаются при старте контейнера
(large-v3 ~3 ГБ, pyannote ~500 МБ). Дождитесь Model loaded в логах.
Дальнейшие запросы используют загруженные модели.