# ADR-007: Пословная диаризация через sherpa-onnx **Статус**: Принято **Дата**: 2026-08-14 ## Контекст Разделение говорящих — главный структурный разрыв между локальным транскриптом и облачными сервисами в сценарии подготовки конспектов и протоколов встреч. Диаризация при этом не является ещё одним движком распознавания: она независимо строит [разметку говорящих](../../CONTEXT.md#language), которую затем нужно свести с результатом ASR. Привязка одного говорящего ко всему сегменту распознавания оказалась слишком грубой. На трёх русскоязычных рабочих созвонах чужая реплика не короче секунды встретилась в 6–7% сегментов разговоров на двоих и в 27% сегментов встречи втроём. Сегменты распознавания проходят по тишине, а не по смене говорящего, поэтому сохранить контекст RNN-T и получить реплики можно только через более мелкую единицу сведения. ## Эксперимент Локальная связка `sherpa-onnx` с сегментацией Pyannote 3.0 и эмбеддингами WeSpeaker ResNet34 LM проверена на трёх записях с известным составом. Для автоматического определения числа голосовых кластеров выбран порог 0,89: это единственное проверенное значение, которое на трёх контрольных фрагментах дало 3 / 2 / 2 кластера. На полной встрече втроём остался ложный кластер длительностью 19,1 секунды; поэтому малые кластеры нельзя молча отбрасывать, а разметку нельзя считать эталоном точных границ и перекрывающейся речи. На доступном слабом Intel baseline, Core i7-6820HQ с урезанным питанием, последовательные ASR и диаризация обработали час записи примерно за 23 минуты. Диаризация увеличивает полное время примерно в 2,4 раза, но остаётся быстрее реального времени и приемлема как явно включаемая функция. Конкретный Core i5 11-го поколения не проверен, поскольку такого устройства нет. Исходные данные и ограничения зафиксированы в отчётах о [калибровке](../benchmarks/2026-08-14-diarization-calibration.md), [смешении говорящих](../benchmarks/2026-08-14-asr-segment-speaker-mixing.md) и [производительности на Intel](../benchmarks/2026-08-14-diarization-intel-i7.md). ## Решение Диаризацию реализуем как явно включаемый пост-процессинг через `sherpa-onnx`. Первая версия использует Pyannote segmentation 3.0, WeSpeaker ResNet34 LM, порог кластеризации 0,89 и автоматическое число кластеров; известное число участников можно передать явно. Говорящий назначается [слову с временной привязкой](../../CONTEXT.md#language), а не сегменту распознавания. Каждый ASR-бэкенд приводит свой результат к общему набору слов с положением на временной шкале. Проходы ASR и диаризации независимо получают одно аудио, после чего отдельная операция сводит слова с интервалами разметки говорящих и объединяет соседние слова одного говорящего в реплики. Распознавание по-прежнему выполняется на полных сегментах и сохраняет контекст модели. Первая реализация последовательна на всех устройствах: ASR, диаризация, сведение, Markdown. В батче один диаризатор создаётся после prescan, переиспользуется для всех файлов и освобождается вместе с командой. Разметка говорящих живёт только в памяти текущего запуска; постоянного кеша результата нет. Грубого fallback на целый сегмент и автоматического переключения устройства нет. Отсутствие пословных таймкодов или ошибка инициализации диаризатора останавливают запуск до ASR. Ошибка диаризации конкретного файла после успешного ASR не уничтожает полезный результат: сохраняется обычный транскрипт с явным предупреждением и ненулевым статусом, а батч продолжает остальные файлы. Подробная матрица поведения находится в [спецификации](../specs/2026-08-14-speaker-diarization.md). ## Последствия - Общий контракт результата распознавания расширяется каноническими словами с временной привязкой; сегменты распознавания сохраняются для совместимости и контроля качества. - FasterWhisper, ONNX-ASR и OpenVINO должны экспортировать один и тот же пословный контракт. OpenVINO GenAI 2026.x уже предоставляет нужные таймкоды, поэтому ограничение находится в адаптере проекта, а не в движке. - `sherpa-onnx` становится обычной runtime-зависимостью, а две модели диаризации скачиваются и кешируются лениво при первом запросе. - Выход остаётся линейным Markdown с анонимными метками `Speaker N`. Сопоставление голосов с именами и специальная запись перекрывающейся речи не входят в ядро CLI. - Последовательный режим задаёт корректный baseline. Параллельный запуск и автоматическое включение на мощных устройствах требуют отдельных измерений после стабилизации. ## Отклонённые альтернативы | Альтернатива | Почему отклонена | |---|---| | Не делать диаризацию | Оставляет главный продуктовый разрыв, хотя измеренная стоимость допустима для явной функции | | Мажоритарный говорящий на весь сегмент распознавания | Теряет чужие реплики на всех трёх проверенных записях | | Сначала диаризация, затем ASR коротких интервалов | Лишает RNN-T длинного контекста и ухудшает согласование и пунктуацию | | `pyannote.audio` | Тянет PyTorch и требует Hugging Face token с принятием лицензии | | Сборка поверх приватных деталей `onnx-asr` | Экономит небольшую отдельную зависимость ценой нестабильного внутреннего API и собственной кластеризации | | Параллельные проходы в первой версии | Нет прямого benchmark и измеренного общего пика памяти; сначала нужен корректный последовательный baseline |