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

Потоковое распознавание и синтез (gRPC)

Помимо HTTP REST API, сервис предоставляет gRPC-интерфейс для высокопроизводительных интеграций и потоковой обработки. Источником звука может быть микрофон или готовая длинная запись: клиент передаёт аудио частями и получает текст по мере распознавания. Это совместимый поднабор Yandex SpeechKit API v3, а не полная реализация всех сообщений STT v3.

Подключение

import grpc

channel = grpc.insecure_channel("api.speech.example.com:50052")

В примерах используется публичный порт 50052.

Аутентификация

gRPC требует аутентификации: передавайте действующий API-ключ в metadata. Запросы без валидного ключа отклоняются с кодом UNAUTHENTICATED. Принимаются обе схемы — Bearer (как в клиентах в стиле Yandex) и Api-Key:

authorization: Bearer <api-key>

Сервисы

Recognizer — потоковое распознавание (STT v3)

Сервис speechkit.stt.v3.Recognizer реализует основной потоковый контракт Yandex SpeechKit STT v3.

Метод Описание
RecognizeStreaming Двунаправленный поток: клиент отправляет аудио (StreamingRequest), сервер возвращает промежуточные и итоговые результаты (StreamingResponse)

Первый кадр от клиента должен содержать session_options; только после него отправляйте аудио. Конфигурация задаётся ровно один раз: повторный session_options, даже с теми же значениями, отклоняется с INVALID_ARGUMENT. Для смены режима распознавания, языка или формата откройте новый RPC. Поток без конфигурации сессии отклоняется. Поддерживаются следующие параметры:

Поле Поведение
recognition_model.model Для русского по умолчанию SpeechExpert-STT-RU: T-One preview и Riva final после EOF; можно явно выбрать прямой SpeechExpert-STT-RU-stream или другую совместимую модель
recognition_model.language_restriction.language_code[0] Языковая подсказка; остальные коды и restriction_type не применяются
recognition_model.audio_format.raw_audio.sample_rate_hertz Частота входного PCM
recognition_model.audio_format.raw_audio.audio_channel_count Число interleaved-каналов, от 1 до 32
recognition_model.text_normalization Нормализация, profanity filter, литературный текст/пунктуация и форматирование телефонов
recognition_model.text_normalization.literature_text true включает восстановление пунктуации в финальном тексте, если оно поддерживается выбранной моделью; по умолчанию false
recognition_model.audio_processing_type REAL_TIME по умолчанию; FULL_DATA выдаёт только финалы после успешного завершения всего ввода
speaker_labeling.speaker_labeling SPEAKER_LABELING_ENABLED включает потоковую диаризацию Nemotron для mono; по умолчанию выключена
eou_classifier.default_classifier.max_pause_between_words_hint_ms Пауза, после которой фраза считается завершённой

eou_classifier.external_classifier принимается protobuf-схемой, но игнорируется. Параметры HTTP v3 recognitionClassifier, speechAnalysis и summarization в потоковую gRPC-сессию не входят. Неизвестные значения enum audio_processing_type и speaker_labeling отклоняются с INVALID_ARGUMENT.

После конфигурации клиент отправляет little-endian PCM16 в chunk.data. Для нескольких каналов сэмплы должны быть interleaved; сервер разделяет каналы и распознаёт их параллельно. Поток, оборванный посередине многоканального PCM-фрейма, отклоняется с INVALID_ARGUMENT.

Для готовой записи преобразуйте аудио в PCM16 на стороне клиента и отправляйте его последовательно, одновременно читая ответы. Загружать исходный файл целиком перед получением текста не требуется. В режимах пофразового распознавания для русского, английского, казахского, кыргызского, узбекского и таджикского языков клиент получает partial текущей фразы и её final по мере обработки. После последнего фрагмента завершите отправку запросов и дочитайте ответы до CLOSED. Русский режим по умолчанию SpeechExpert-STT-RU выдаёт накопительный T-One preview во время передачи, а окончательный результат Riva для всей записи — после завершения ввода. Каждый такой partial заменяет весь предварительный транскрипт сессии. Подробнее — распознавание длинных записей.

Многоканальный поток применяет обратное давление: когда распознаватель или получатель ответов не успевает, чтение аудио приостанавливается. Если доставка данных каналам или помещение результата в очередь не продвигается 30 секунд, весь RPC завершается с UNAVAILABLE; его дочерние распознаватели закрываются. Аудио и ответы не отбрасываются для обхода заполненной очереди.

Баланс проверяется до выдачи результатов. При успешном EOF оплачивается принятая длительность аудио. При ошибке или отключении после выдачи partial или final оплачивается проверенная граница последнего отправленного ответа; позднее аудио без нового ответа не отменяет это списание. Запрос, отклонённый до выдачи результата, не создаёт такое списание. Исходный код ошибки сохраняется, а повторная отмена RPC не запускает второе списание.

Вместо байтов можно отправить silence_chunk.duration_ms. Допустимая длительность одного события — от 1 до 300000 мс. Для тишины частота должна быть задана в raw_audio либо использовать значение по умолчанию.

Минимальный клиент для mono PCM16 16 кГц:

import os

import grpc

from speechkit.stt.v3 import stt_pb2, stt_service_pb2_grpc


def requests():
    options = stt_pb2.StreamingOptions()
    model = options.recognition_model
    model.model = "SpeechExpert-STT-RU"
    model.audio_format.raw_audio.sample_rate_hertz = 16_000
    model.audio_format.raw_audio.audio_channel_count = 1
    model.language_restriction.language_code.append("ru-RU")
    yield stt_pb2.StreamingRequest(session_options=options)

    with open("audio.pcm", "rb") as source:
        while chunk := source.read(3200):  # около 100 мс
            yield stt_pb2.StreamingRequest(
                chunk=stt_pb2.AudioChunk(data=chunk)
            )


channel = grpc.insecure_channel("api.speech.example.com:50052")
stub = stt_service_pb2_grpc.RecognizerStub(channel)
metadata = (("authorization", f"Bearer {os.environ['API_KEY']}"),)

for response in stub.RecognizeStreaming(requests(), metadata=metadata):
    event = response.WhichOneof("Event")
    if event in {"partial", "final"}:
        update = getattr(response, event)
        print(event, response.channel_tag, update.alternatives[0].text)
    elif event == "status_code":
        print("closed", response.status_code.code_type)

Импорт в клиенте зависит от package path, который использовался при генерации кода из stt.proto; имена protobuf-сообщений остаются теми же.

Пример использует русский режим по умолчанию: SpeechExpert-STT-RU возвращает preview T-One, затем один окончательный текст Riva после EOF. Чтобы получать прямые пофразовые T-One finals во время передачи, явно задайте options.recognition_model.model = "SpeechExpert-STT-RU-stream".

Автоматическое восстановление пунктуации сервисом выключено по умолчанию. Чтобы включить его, добавьте настройку до отправки первого session_options:

options.recognition_model.text_normalization.literature_text = True

Восстановление применяется только к final; partial не проходит этот шаг. Знаки препинания, уже выданные выбранной моделью, не удаляются. В protobuf эта настройка называется literature_text.

ElevenLabs Scribe v2 Realtime

Для Scribe Realtime замените настройку модели в приведённом клиенте:

options = stt_pb2.StreamingOptions()
model = options.recognition_model
model.model = "ElevenLabs-Scribe-v2-Realtime"  # также принимается scribe_v2_realtime
model.language_restriction.language_code.append("ru-RU")
model.audio_format.raw_audio.sample_rate_hertz = 16_000
model.audio_format.raw_audio.audio_channel_count = 1
model.audio_processing_type = stt_pb2.RecognitionModelOptions.REAL_TIME

Отправьте эти session_options первыми, затем передавайте PCM и одновременно читайте ответы, как в полном примере. Модель принимает 75 языков сервиса, включая ru-RU, en-US, kk-KZ, ky-KG, uz-UZ, tg-TJ и fr-FR. Для многоканального PCM каждый канал распознаётся своим соединением с ElevenLabs. Сервер преобразует звук каждого канала в mono PCM16 16 кГц для провайдера.

Промежуточные гипотезы приходят в стандартном partial, окончательные фразы — в final. Пословные таймкоды провайдера публикуются в Alternative.words, границы фразы — в start_time_ms и end_time_ms; все значения отсчитываются от начала входного аудио. Финал выдаётся один раз после получения таймкодов. Если таймкоды не пришли вовремя, публикуются оценённые границы фразы без слов. Текст Scribe Realtime сохраняется вместе с его пословной разметкой: параметры recognition_model.text_normalization, включая литературный текст, не изменяют результат этой модели.

После последнего чанка закройте клиентский поток и дочитайте ответы до CLOSED. При стандартной настройке дочитывание ElevenLabs после EOF может занять до 15 секунд. В этом режиме не вызываются файловые Scribe v2 или Medical для уточнения текста. FULL_DATA только откладывает выдачу финалов до успешного EOF; отдельного распознавания полной записи нет.

У провайдера нет нативной диаризации Realtime; для mono можно включить потоковую диаризацию Nemotron. Аудио передаётся внешнему сервису ElevenLabs; на сервере требуется ELEVENLABS_API_KEY, webhook не нужен. Стоимость и поведение при ошибках описаны в обзоре Realtime.

Потоковая диаризация

Для mono-аудио добавьте в начальные session_options стандартные поля SpeechKit v3. Их имена, номера и значения enum совместимы с официальными protobuf-клиентами:

options = stt_pb2.StreamingOptions()
options.recognition_model.model = "SpeechExpert-STT-RU-stream"
options.recognition_model.language_restriction.language_code.append("ru-RU")
options.recognition_model.audio_format.raw_audio.sample_rate_hertz = 16_000
options.recognition_model.audio_format.raw_audio.audio_channel_count = 1
options.recognition_model.audio_processing_type = stt_pb2.RecognitionModelOptions.REAL_TIME
options.speaker_labeling.speaker_labeling = (
    stt_pb2.SpeakerLabelingOptions.SPEAKER_LABELING_ENABLED
)
# Отправьте StreamingRequest(session_options=options) первым сообщением,
# затем chunk.data с PCM16, как в полном примере выше.

В этом примере явно выбран прямой SpeechExpert-STT-RU-stream, чтобы финальные фрагменты появлялись во время записи. В SpeechExpert-STT-RU окончательный текст ASR появляется после EOF, поэтому диаризованные финалы также ждут EOF.

partial приходит без метки диктора. Выдача final ждёт, пока диаризация зафиксирует соответствующий участок аудио. В ответе StreamingResponse.channel_tag содержит строковый ID диктора от "0" до "7"; для старых клиентов то же значение дублируется в final.channel_tag. Если принадлежность речи определить не удалось, метка остаётся пустой, в том числе у непустого final. Идентификаторы сохраняются в пределах одной сессии и не обозначают личность человека между подключениями. Многоканальный вход с включённой диаризацией отклоняется с INVALID_ARGUMENT.

При наличии согласованных пословных таймкодов одна ASR-фраза может стать несколькими final с разными дикторами; каждый увеличивает audio_cursors.final_index. Если доступны только границы фразы, ей назначается преобладающий диктор целиком. При одновременной речи нескольких людей разделение их слов не гарантируется. speaker_analysis — отдельная статистика речи и этим режимом не добавляется.

REAL_TIME с диаризацией — расширение нашего сервера: Yandex описывает определение дикторов только для FULL_DATA, mono и не более двух дикторов. Для отложенной выдачи укажите stt_pb2.RecognitionModelOptions.FULL_DATA. Аудио обрабатывается последовательно, финалы временно сохраняются и отправляются только после успешного EOF; partial в этом режиме не выдаются. Это не запускает дополнительное офлайн-распознавание или уточнение текста. При ошибке до завершения обработки накопленные финалы не отправляются. Режим действует и без диаризации.

Подробнее о задержке и доступных таймкодах — в обзоре диаризации.

Поддерживаемые языки и результаты

Локальное потоковое распознавание с промежуточным текстом доступно для русского, английского, казахского, кыргызского, узбекского и таджикского языков. Укажите язык в recognition_model.language_restriction.language_code и соответствующее значение recognition_model.model:

Язык Код языка Значение model
Русский, по умолчанию: итог после EOF ru-RU SpeechExpert-STT-RU
Русский, прямой пофразовый поток ru-RU SpeechExpert-STT-RU-stream
Английский en-US Parakeet-EN
Казахский kk-KZ или kk SpeechExpert-STT-KK-stream
Кыргызский ky-KG или ky GigaAM-Multilingual-Large-CTC
Узбекский uz-UZ или uz SpeechExpert-STT-UZ-stream
Таджикский tg-TJ или tg SpeechExpert-STT-TG-stream

Для всех этих языков и остальных языков курируемого набора можно явно выбрать ElevenLabs-Scribe-v2-Realtime; пример приведён выше.

Идентификаторы в таблице нужны для настройки запроса. Несовместимые сочетания языка и значения model отклоняются до распознавания. Подробнее о выборе интерфейса — в обзоре потокового распознавания.

В русском SpeechExpert-STT-RU каждый partial заменяет preview всей сессии; Riva final после EOF заменяет его окончательным текстом. Если Riva не вернул текст, итогом становится последний preview. В остальных пофразовых режимах, включая SpeechExpert-STT-RU-stream, каждый новый partial заменяет предварительный текст текущей фразы. final завершает её: добавьте его текст к итоговому транскрипту и очистите предварительный текст этой фразы. Финальный текст может отличаться от промежуточного. Локальный таджикский поток возвращает итог без автоматической пунктуации и нормализации текста независимо от настроек recognition_model.text_normalization. Успешный пустой final также завершает фразу. После отправки всего аудио закройте клиентский поток запросов и продолжайте читать ответы до CLOSED: сервер завершит оставшуюся фразу.

Без диаризации в локальном кыргызском потоке итог каждой фразы уточняется отдельно. Пока готовится предыдущий final, уже могут приходить partial следующей фразы. Храните гипотезы по паре channel_tag и Alternative.start_time_ms; финал очищает только гипотезу с этим ключом. Финалы приходят в исходном порядке. Нового поля phrase_id в protobuf нет.

При диаризации channel_tag обозначает диктора, а у partial остаётся пустым; не используйте его как единственный ключ предварительной фразы. Сопоставляйте финальные фрагменты с гипотезами по диапазону времени и учитывайте, что одна фраза может содержать несколько финальных фрагментов разных дикторов.

Для локальных казахского, кыргызского, узбекского и таджикского потоков границы фраз определяются автоматически; параметр max_pause_between_words_hint_ms на них не влияет. Для этих языков многоканальный запрос принимается целиком: при нехватке доступной ёмкости сервер отклоняет весь запрос.

Доступны также режимы с уточнением предварительного текста:

Язык Значение model Поведение
Русский, по умолчанию SpeechExpert-STT-RU Накопительный T-One preview всей записи и окончательный текст Riva после EOF
Русский GigaAM-Multilingual-Large-CTC Накопительный partial для всей записи и один уточнённый final после завершения ввода
Казахский GigaAM-Multilingual-Large-CTC partial текущей фразы и уточнённый final каждой завершённой фразы, в исходном порядке

В режимах с единственным итогом после завершения ввода partial заменяет весь предварительный транскрипт. Если уточнение не дало текста, итогом становится последний предварительный результат. Для казахского режима с уточнением действуют правила пофразовой обработки, описанные выше.

В узбекском потоке SpeechExpert-STT-UZ-stream возвращает предварительный текст по мере поступления аудио и уточняет каждую завершённую фразу, включая оставшийся фрагмент при завершении ввода.

Ответы и таймстемпы

Сервер возвращает только partial, final и завершающий status_code. События HTTP v3 finalRefinement, eouUpdate, classifierUpdate, аналитика спикеров/разговора и суммаризация в этом gRPC-контракте не реализованы.

Для русского и английского пофразового распознавания поля Alternative.start_time_ms, Alternative.end_time_ms и Alternative.words[] содержат доступные границы речи на шкале исходного потока. Это время речи, а не момент получения сетевого чанка. Нулевой start_time_ms валиден.

В русских режимах с единственным итогом после завершения ввода промежуточные и итоговый результаты получают накопленный диапазон аудио. Если итоговый текст отличается от предварительного, words у финала остаётся пустым, поскольку точное пословное выравнивание неизвестно.

Для локальных казахского, кыргызского, узбекского и таджикского потоков start_time_ms и end_time_ms обозначают границы обработанного аудиофрагмента, включая паузы. Это не точное время произнесения слов, поэтому words[] остаётся пустым. При отсутствии доступного тайминга scalar-поля protobuf остаются со значением 0, а words пуст.

AudioCursors — отдельная шкала прогресса транспорта:

Поле Семантика
received_data_ms Объём принятых PCM-данных, включая silence_chunk
partial_time_ms Монотонная граница обработанного аудио; не меньше final_time_ms
final_time_ms Монотонный максимум границ отправленных финалов
eou_time_ms Совпадает с final_time_ms
final_index Число отправленных final в сессии

Границы речи в Alternative не подменяются курсорами: уточнённый финал может заканчиваться раньше предыдущего partial, тогда как partial_time_ms всё равно движется только вперёд. Без диаризации для многоканального аудио StreamingResponse.channel_tag и AlternativeUpdate.channel_tag равны строковому номеру физического канала, начиная с "1"; для mono они пусты. После обработки всего ввода сервер отправляет отдельный status_code с code_type=CLOSED.

response_wall_time_ms в gRPC содержит Unix-время формирования ответа в миллисекундах. Это отличается от одноимённого JSON-поля HTTP v3, где хранится прошедшее время обработки операции.

Если нормализация доступна для языка и включена, она применяется к каждому partial и final; отдельного finalRefinement нет. Правила преобразования чисел, телефонов и фильтрации ненормативной лексики применяются только к русскому. Таджикский текст возвращается без такой постобработки. Если объект text_normalization передан с PHONE_FORMATTING_MODE_UNSPECIFIED, форматирование телефонов считается включённым. words[] сохраняют исходные распознанные слова и поэтому после пунктуации, фильтрации или нормализации могут уже не совпадать с итоговым text.

WebSocket STT

Для браузерных клиентов и более простого JSON-протокола используйте отдельный WebSocket STT. Он принимает mono PCM16 16 кГц и не требует генерации клиентского кода из protobuf.

Synthesizer — потоковый синтез (TTS v3)

Сервис speechkit.tts.v3.Synthesizer поддерживает синтез речи через методы UtteranceSynthesis и StreamSynthesis.

Server Reflection

Сервер поддерживает gRPC Server Reflection — API можно исследовать через grpcurl:

# Список доступных сервисов
grpcurl -plaintext -H "authorization: Bearer ${API_KEY}" api.speech.example.com:50052 list

# Описание доступных методов
grpcurl -plaintext -H "authorization: Bearer ${API_KEY}" api.speech.example.com:50052 describe

Для изучения доступных сервисов и генерации клиента можно использовать Server Reflection.

См. также