Skip to content

Формат ответа

GET /v1/jobs/{id}/result возвращает JSON. Пример для стерео-звонка со всеми опциями, сокращённый:

json
{
  "schema_version": "gapi_task_result.v0",
  "audio_seconds": 282.7,
  "speech_seconds": 230.4,
  "channels": 2,
  "stereo_parties": true,
  "speakers": 2,
  "vocabulary_terms": 4,
  "vocabulary_dropped": false,
  "text": "Assalomu alaykum, Metasell kompaniyasi. …",
  "segments": [
    {
      "index": 0,
      "start": 0.42,
      "end": 3.9,
      "speaker": 1,
      "text": "Assalomu alaykum, Metasell kompaniyasi.",
      "raw_asr_text": "assalomu alaykum metasell kompaniyasi",
      "formatted_text": "Assalomu alaykum, Metasell kompaniyasi.",
      "emotion": {"mood": "neutral", "label": "neutral", "confidence": 0.88, "valence": 0.52, "arousal": 0.31, "dominance": 0.47, "windows": 1,
                  "probabilities": {"neutral": 0.88, "happy": 0.05, "sad": 0.02, "angry": 0.02, "fearful": 0.01, "disgusted": 0.01, "surprised": 0.01}},
      "pii": {"entities": [], "redacted_text": "Assalomu alaykum, Metasell kompaniyasi."}
    }
  ],
  "speaker_info": [
    {
      "index": 0,
      "speech_seconds": 149.2,
      "gender": {"label": "male", "confidence": 0.97},
      "role": "client",
      "identity": {"status": "new", "voice_id": 100, "speaker_id": "e3db…", "name": null},
      "has_voiceprint": true,
      "emotion": {"dominant_mood": "negative", "mood_share": {"negative": 0.46, "neutral": 0.41, "positive": 0.13}, "valence": 0.38, "arousal": 0.61, "dominance": 0.44}
    }
  ],
  "voices": [
    {"label": "SPEAKER_1", "speaker": 0, "voice_id": 100, "name": null, "status": "new"},
    {"label": "SPEAKER_2", "speaker": 1, "voice_id": 101, "name": "Азимбек", "status": "matched", "score": 0.97}
  ],
  "options_report": {"requested": ["gender", "speaker_roles", "speaker_identification", "emotion", "pii"], "errors": {}, "seconds": 1.8},
  "timing": {"decode_seconds": 0.3, "vad_seconds": 0.4, "stt_seconds": 2.1, "total_seconds": 4.9}
}

Текст приходит латиницей: модель обучена на латинской записи узбекского, и все опции (пунктуация, числа, персональные данные) работают с ней. Кириллицы на выходе нет.

Верхний уровень

ПолеЧто это
audio_secondsДлина записи; из неё считается списание в g
speech_secondsСколько в ней речи
channels, stereo_partiesКаналов в файле; true, если стороны разделены по каналам
speakersСколько голосов нашло разделение по спикерам
textВесь текст подряд
segmentsРеплики по порядку
speaker_infoСводка по каждому спикеру записи
voicesПостоянные номера голосов проекта, при «Узнавать по голосу»
optionsНастройки, с которыми запись реально считалась: проект плюс settings запроса. Здесь все ключи со страницы Опции — включая stereo_channels_as_speakers, max_speakers, call_direction и fuzzy_itn (нестрогая нормализация чисел; по умолчанию выключена)
vocabulary_terms, vocabulary_droppedСколько терминов словаря взяла задача; true во втором, если после чистки не осталось ни одного
diarization_errortrue, если разделение по спикерам не отработало: номеров спикеров в записи не будет. При удачном разделении поле есть и равно null
options_reportКакие опции запрашивались, какие не посчитались и почему
segment_voiceprintsУ каких реплик есть голосовой отпечаток: {"dims", "format", "min_seconds", "segments": [индексы]}. Сами отпечатки наружу не отдаются — это список реплик, из которых можно завести голос через POST /v1/speakers/{id}/enroll с segment_indexes
timingСколько секунд заняла обработка: decode_seconds, vad_seconds, stt_seconds, format_seconds, diarization_seconds, total_seconds. Считаем по ним себя, а не вас

options_report показывает, что запрашивалось (requested), что не посчиталось и почему (errors), и сколько секунд ушло на опции (seconds). Если запрашивались роли, там же лежит roles — ответ модели ролей как есть, вместе с её score, first_speaker, status и direction. Это диагностика: числа в ней приходят строками, состав может поменяться. Роль каждого спикера берите из speaker_info[].role.

Рядом лежат служебные поля — word_timing, formatting, partial. Они для разбора наших же обращений: состав может поменяться, не стройте на них логику.

Реплика

ПолеЧто это
indexПорядковый номер, с 0
start, endСекунды от начала записи
speakerНомер спикера записи или null
textИтоговый текст с включёнными опциями
raw_asr_textКак услышала модель: строчные, без знаков
formatted_textС пунктуацией и заглавными
wordsСлова с временем, при word_timestamps; см. ниже
speaker_shareКакая доля речи реплики размечена самим разделением по спикерам, от 0 до 1; остальное унаследовано от соседних слов. Есть только там, где спикеры считались
contextual_textТекст после словаря, до пунктуации и чисел; между raw_asr_text и formatted_text
duration_bucket_secondsСлужебная корзина длительности для нашей телеметрии
emotion{"mood", "label", "confidence", "probabilities", "valence", "arousal", "dominance", "windows"}, при «Эмоциях»; у реплики короче секунды — {"label": null, "reason": "too_short"}; см. Шкалы эмоций
pii{"entities", "redacted_text"}, при «Персональных данных»

Значения mood: neutral, negative, distressed, positive, surprised. Метки pii: NAME, PHONE, ADDRESS, DATE, DOCUMENT_ID, CARD_NUMBER.

Слово

json
{"text": "Uzum", "raw": "uzim", "formatted": "Uzum", "start": 4.12, "end": 4.51, "speaker": 1, "aligned": "term"}
ПолеЧто это
textСлово после словаря, до пунктуации
rawЧто услышала модель на этом месте; у вставленного слова поля нет
formattedОно же с заглавными и знаками, когда реплика отформатирована
start, endСекунды от начала записи
speakerНомер спикера, если спикеры считались
alignedОткуда взято время слова

aligned стоит читать: exact — кадры самой модели на этом слове; term — слово пришло из словаря и размечено по кадрам поисковика терминов, то есть это прямое доказательство, что термин применился; span — доля от переписанного куска; chunk — на этот кусок модель не дала слов, время у всего куска общее; none — слово вставлено без акустического подтверждения, длина нулевая.

Спикер записи

ПолеЧто это
indexНомер спикера, тот же, что в репликах
speech_secondsСколько он говорил
gender{"label": "female" | "male" | "unknown", "confidence", "reason", "windows", "speech_seconds", "probabilities"}. У unknown поле confidence равно null, а reason говорит почему
role"client", "manager" или "unknown"
identity{"status", "voice_id", "speaker_id", "name", "score", "threshold", "runner_up", "kind"}
has_voiceprintУ спикера хватило речи на голосовой отпечаток: его можно назвать через Голоса и спикеры. Сам отпечаток наружу не отдаётся
emotion{"dominant_mood", "mood_share", "valence", "arousal", "dominance"}

identity.status отвечает на один вопрос: узнали ли этого человека и, если нет, почему.

СтатусЧто произошлоЕсть ли voice_id
matchedОтпечаток совпал с голосом проектада, номер знакомого голоса
newНе совпал, но речи хватило: голос заведён этой записью и дальше будет узнаватьсяда, новый номер
unknownНе совпал, и речи меньше 10 секунд — заводить голос по такому отпечатку ненадёжнонет
insufficient_speechОтпечаток не удалось посчитать вовсе: речи почти нетнет
no_enrolled_voicesВ проекте ещё нет ни одного голоса, сравнивать не с чемнет

Разница между unknown и insufficient_speech в том, что в первом случае отпечаток есть и сравнение прошло, просто человек незнаком и речи мало для регистрации; во втором считать было нечего.

У matched и unknown рядом лежат score, близость к ближайшему голосу от 0 до 1, и threshold, порог, начиная с которого совпадение засчитывается. Если score регулярно чуть ниже порога, голосу не хватает отпечатков: добавьте ещё один из хорошей записи.

Там же runner_up — близость ко второму по похожести голосу (-1, если сравнивать было не с чем). Совпадение засчитывается, только если первый обошёл второго с запасом: два похожих голоса не должны выигрывать друг у друга случайно. У matched и new есть ещё kind — какой это голос в проекте: candidate, recurring или named, см. Голоса и спикеры.

Шкалы эмоций

Модель эмоций слушает реплику и отвечает двумя способами сразу.

Класс. label это один из семи классов модели: neutral, happy, sad, angry, fearful, disgusted, surprised. probabilities даёт вероятность каждого, confidence это вероятность выбранного. Уверенность 0.5 при семи классах уже уверенный ответ; ниже 0.35 класс лучше не показывать оператору. mood это класс, свёрнутый до пяти понятных состояний: angry и disgusted в negative, sad и fearful в distressed, happy в positive.

Три шкалы от 0 до 1. Они не зависят от класса и описывают состояние непрерывно:

Шкала01Как читать
valenceнеприятноприятноНиже 0.4 клиент недоволен, выше 0.6 доволен. Самая полезная шкала для оценки разговора
arousalспокойнонапряжённоВысокий arousal при низком valence это раздражение или спор, при высоком valence воодушевление
dominanceнеувереннонапористоКто ведёт разговор. Низкое значение у клиента с высоким arousal часто означает растерянность

Значения около 0.5 нейтральны. Смотрите на динамику по репликам, а не на одно число: падение valence у клиента за разговор говорит больше, чем его абсолютное значение. windows показывает, из скольких окон по 15 секунд усреднена реплика; для коротких реплик оно равно 1 и оценка шумнее.

Реплика короче секунды не получает оценки вовсе: вместо чисел приходит {"label": null, "reason": "too_short"} — по обрывку модель не гадает. Короткие реплики одного спикера, идущие подряд, сначала пробуют упаковаться в одно окно, так что «слишком коротко» достаётся в основном одиночным репликам вроде «Ha» посреди чужой речи.

В speaker_info[].emotion те же три шкалы усреднены по всем репликам спикера, рядом dominant_mood и mood_share, доля каждого состояния. В кабинете шкалы показаны полосками: короткими в строке реплики и карточкой на каждого спикера под плеером. Наведите указатель на полоску, и она скажет, какой это показатель, что значат его концы и само число; там же рядом с репликой видна уверенность модели в эмоции.

Пока задача не готова

GET /v1/jobs/{id}/result отдаёт результат только у готовой задачи; пока она идёт, приходит 409 result_not_ready. Реплики по мере готовности берутся из GET /v1/jobs/{id}/events: см. API.