Перейти к содержанию

Классификация эмоций в аудио

Emotion API анализирует голосовую окраску записи моделью DUSHA и возвращает вероятности пяти классов: angry, neutral, other, positive, sad. Штатно используется локальный ONNX ensemble; сервер можно переключить на Triton-провайдер настройкой EMOTION_BACKEND. Модель валидирована для русской речи: API принимает lang=ru и lang=ru-RU. Поддержка эмоций для казахской, кыргызской, узбекской и таджикской речи не заявляется, даже если выбранная STT-модель умеет распознавать эти языки. Доступны два эквивалентных пути:

POST /api/stt/v1:recognizeEmotion
POST /api/emotion/v1

Запрос — multipart/form-data; требуется обычная аутентификация API-ключом или portal-сессией.

Параметры

Параметр По умолчанию Описание
audio Обязательный аудио- или видеофайл, до MAX_AUDIO_UPLOAD_BYTES
aggregate серверный EMOTION_TRITON_AGGREGATE, штатно mean mean усредняет вероятности всех окон; max_confidence выбирает окно с самой уверенной меткой
includeSegments false Вернуть результат каждого временного окна в segments; принимается также include_segments
negativeEmotionThreshold серверный EMOTION_NEGATIVE_THRESHOLD, штатно 0.5 Порог от 0 до 1 для суммы вероятностей angry + sad; принимается также negative_emotion_threshold
lang ru-RU После BCP-47-нормализации допустимы только ru и ru-RU; другой locale отклоняется с 400
format oggopus Подсказка формата, если контейнер нельзя определить по байтам
sampleRateHertz 16000 Частота только для headerless format=lpcm

Параметры можно передавать в query string или форму; для дублирующихся значений форма имеет приоритет. WAV читается напрямую, остальные контейнеры конвертируются в mono WAV 16 кГц. В отличие от файлового STT v1, здесь headerless PCM поддержан явно через format=lpcm&sampleRateHertz=.... В этом случае ожидается signed little-endian PCM16 mono.

Ответ

{
  "result": "neutral",
  "audio": "call.wav",
  "source_sample_rate": 48000,
  "model_sample_rate": 16000,
  "duration_sec": 12.4,
  "model": "dusha_emotion_ensemble_wav2vec_conformer_calibrated",
  "model_version": "1",
  "calibration_version": "sha256:...",
  "prediction": "neutral",
  "confidence": 0.71,
  "dominantEmotion": {
    "label": "neutral",
    "score": 0.71,
    "negative": false
  },
  "emotions": [
    {"label": "angry", "score": 0.05, "negative": true},
    {"label": "neutral", "score": 0.71, "negative": false},
    {"label": "other", "score": 0.08, "negative": false},
    {"label": "positive", "score": 0.12, "negative": false},
    {"label": "sad", "score": 0.04, "negative": true}
  ],
  "negative_emotion": false,
  "negative_emotion_score": 0.09,
  "negative_emotion_threshold": 0.5,
  "negativeEmotion": {
    "detected": false,
    "score": 0.09,
    "threshold": 0.5,
    "labels": ["angry", "sad"]
  },
  "probs": {
    "angry": 0.05,
    "neutral": 0.71,
    "other": 0.08,
    "positive": 0.12,
    "sad": 0.04
  },
  "top": [
    {"label": "neutral", "prob": 0.71},
    {"label": "positive", "prob": 0.12},
    {"label": "other", "prob": 0.08},
    {"label": "angry", "prob": 0.05},
    {"label": "sad", "prob": 0.04}
  ],
  "window_seconds": 3.0,
  "hop_seconds": 1.0,
  "num_windows": 11,
  "aggregate": "mean",
  "segments": null
}

top содержит все пять классов по убыванию вероятности. negativeEmotion дублирует snake_case-поля в удобной для совместимых клиентов форме. negative_emotion_score равен P(angry) + P(sad). Класс other следует интерпретировать только как другую или неопределённую акустическую окраску: контракт не называет его отдельным психологическим состоянием и не выдаёт за механизм достоверного определения намерений.

model_version фиксирует версию модели в Triton-style repository, а calibration_version — SHA-256 fingerprint фактически развёрнутого файла метаданных калибровки. Если этот файл недоступен приложению при удалённом Triton, calibration_version возвращается как null.

При includeSegments=true каждый элемент segments содержит start_sec, end_sec, prediction, confidence, negative_emotion, negative_emotion_score, dominantEmotion, emotions и probs. Штатно модель использует окна 3 секунды с шагом 1 секунда; точные значения возвращаются в ответе и могут меняться настройками сервера. В отдельном Emotion API это окна аудио без привязки к словам, репликам или спикерам STT. Для готовой связи «эпизод → таймкод → реплика → аудио» используйте интегрированный анализ в STT v1.

Интеграция со STT v1

POST /api/stt/v1 и POST /api/stt/v1:recognizeAsync могут запустить DUSHA параллельно с распознаванием того же канонического WAV. Повторно отправлять аудио в отдельный Emotion API не нужно:

emotionAnalysis=true
emotionIncludeSegments=true
emotionNegativeThreshold=0.5

Принимаются также snake_case-формы emotion_analysis, emotion_include_segments, emotion_negative_threshold. В ответ добавляются emotion_analysis и emotion_analysis_status. Сервер сглаживает оконный negative-score с помощью EMA, выделяет устойчивые негативные, позитивные и неопределённые эпизоды, объединяет короткие разрывы и связывает эпизод с пересекающимися utterances или segments. После сведения физических stereo- каналов модель не приписывает сигнал конкретному спикеру; персональная атрибуция требует отдельного per-channel анализа.

В session доступны:

  • negative_share — доля исходной временной шкалы, где сглаженный сигнал выше порога;
  • peak_negative_score — максимум сглаженного negative-score; поле peak_negative_ema содержит тот же показатель как явный alias;
  • peak_negative_raw_score — максимум исходного, несглаженного P(angry) + P(sad);
  • peak_at_ms — положение максимума сглаженного сигнала;
  • deescalation_score — среднее сглаженное значение на первых 20% окон минус среднее на последних 20%; положительное значение означает снижение сигнала;
  • каждый эпизод хранит mean_confidence и max_confidence, а совместимое поле confidence повторяет среднее значение;
  • deescalation_trendimproved, stable или worsened с опубликованным в metadata порогом изменения.

negative_share взвешен по времени исходной записи, а positive_share и uncertain_share показывают долю окон соответствующего производного состояния. Каждое окно и каждый эпизод сохраняют все пять raw probabilities. Порог, параметр EMA и правила объединения эпизодов возвращаются вместе с результатом, поэтому клиенту не нужно угадывать серверные настройки. Если emotion backend временно недоступен, успешный STT не теряется: статус становится unavailable или failed, а транскрипт возвращается как обычно.

Независимый сигнал по тексту

Для lang=ru-RU тот же emotionAnalysis=true дополнительно анализирует финальные utterances или segments локальной моделью cointegrated/rubert-tiny2-cedr-emotion-detection, закреплённой на revision 453ae93ca895c98cda29522c72b6fbc5a08067b9. Отдельный контейнер не нужен. Результаты не смешиваются с акустическими вероятностями и возвращаются в соседних полях text_emotion_analysis и text_emotion_analysis_status.

Модель многометочная: сервер применяет sigmoid отдельно к joy, sadness, surprise, fear, anger и no_emotion. Штатный порог показа равен 0.7 и настраивается через TEXT_EMOTION_SIGNAL_THRESHOLD. Каждый сигнал содержит model_score, все raw scores, исходную цитату, проанализированную clause, таймкоды, speaker и индексы исходной реплики/сегмента. model_score — выход модели, а не калиброванная вероятность истинной эмоции.

Перед inference длинные реплики делятся по границам предложений и контрастным союзам. Контексты с отрицанием, возможным сарказмом, вопросом, косвенной речью или несколькими активными метками не подавляются, а получают warnings и review_required=true. Ошибка загрузки модели даёт текстовый статус unavailable, ошибка inference — failed; акустический анализ и готовая транскрипция при этом сохраняются.

Пример

curl -X POST \
  "https://api.speech.example.com/api/stt/v1:recognizeEmotion?aggregate=mean&includeSegments=true" \
  -H "Authorization: Api-Key $API_KEY" \
  -F "audio=@call.wav" \
  -F "negativeEmotionThreshold=0.5"

Связь со STT v3

Классификатор recognitionClassifier.classifiers[].classifier="negative" в STT v3 использует тот же emotion backend, но возвращает SpeechKit-совместимый classifierUpdate. Сейчас v3 вычисляет одну классификацию по полному файлу. Отдельный Emotion API удобнее, если нужны все вероятности и временные окна.

Интерпретация и ограничения

Модель оценивает вероятностную акустическую окраску русской речи. Это сигнал для навигации и ручной проверки исходного аудио, а не объективное определение внутреннего состояния человека. Безопасная последовательность использования:

emotion signal → выбор фрагмента → проверка человеком

Не используйте score как единственное основание для оценки сотрудника, штрафа, найма, медицинского вывода, оценки честности, кредитного или страхового решения. Для контакт-центра порог и качество следует отдельно проверить на своей телефонии, шуме, кодеках и составе спикеров.

Inference выполняется вне event loop. Единый process-wide лимит для отдельного Emotion API, интегрированных STT v1/v3 вызовов и ONNX/Triton задаётся EMOTION_MAX_CONCURRENCY (штатно 1).

Ошибки

Код Когда возникает
400 Пустой файл, неподдерживаемый язык, невалидный порог, частота LPCM, WAV или ошибка конвертации
401 Нет действующей аутентификации
413 Файл превышает серверный лимит
422 aggregate или другой параметр не прошёл проверку схемы
500 Неожиданная внутренняя ошибка
503 ONNX/Triton emotion backend недоступен или вернул ошибку

См. также