Дата публикации
ai_products

AWS запустила двунаправочный стриминг в SageMaker AI: как собрать свой real-time голосовой сервис на vLLM и Voxtral

Что нового

Amazon добавила в SageMaker AI двунаправочный стриминг для real-time инференса. Это значит, что теперь можно держать одно постоянное соединение и одновременно:

  • отправлять аудио на сервер;
  • получать текстовую расшифровку в ответ в режиме реального времени.

Ключевые новшества и параметры:

  • Двунаправочный стриминг в SageMaker AI

    • Протокол на стороне клиента: HTTP/2 event stream на порту 8443.
    • Протокол внутри контейнера: WebSocket.
    • SageMaker автоматически мостит HTTP/2 ⇄ WebSocket, без ручной реализации.
  • Поддержка WebSocket-стриминга в vLLM (Realtime API)

    • Встроенный WebSocket-эндпоинт /v1/realtime для потоковой обработки аудио.
    • Поддержка нескольких речевых моделей, включая Voxtral-Mini-4B-Realtime-2602 от Mistral AI.
    • Реализация piecewise CUDA Graph для снижения overhead на запуск GPU-ядра и уменьшения задержки на токен.
  • Голосовая модель Voxtral-Mini-4B-Realtime-2602

    • Размер: 4B параметров, помещается на один GPU.
    • Рекомендуемый инстанс: ml.g6.4xlarge с 1× NVIDIA L4.
    • Максимальный контекст модели: 262 144 токенов.
    • В примере SageMaker используют SM_VLLM_MAX_MODEL_LEN=45000, чего хватает примерно на 1 час живой записи (3600 секунд / 0,08 сек на токен).
  • Готовый паттерн развёртывания

    • Кастомный Docker-образ на базе SageMaker vLLM Deep Learning Container.
    • Лейбл com.amazonaws.sagemaker.capabilities.bidirectional-streaming=true для включения стриминга.
    • Лёгкий FastAPI-мост внутри контейнера: /invocations-bidirectional-stream/v1/realtime.
    • Прокси health-check'ов /ping/health.
  • Готовые клиенты

    • Python-клиент на базе aws-sdk-sagemaker-runtime-http2, который стримит WAV-файл и печатает транскрипцию по мере прихода.
    • Gradio-клиент с микрофоном: говорит пользователь, текст появляется в интерфейсе сразу, без ожидания конца фразы.

Как это работает

Архитектура потока данных

Схема состоит из трёх слоёв:

  1. Клиент → SageMaker AI

    • Клиент (Python-скрипт или Gradio-приложение) подключается к runtime-эндпоинту SageMaker AI по HTTP/2 на порт 8443.
    • Сообщения Realtime-протокола vLLM (например, input_audio_buffer.append, transcription.delta) отправляются как JSON внутри RequestPayloadPart с DataType="UTF8".
    • Ответы приходят как ResponsePayloadPart и другие события стрима.
  2. SageMaker AI → Docker-контейнер

    • SageMaker сам открывает WebSocket к контейнеру по адресу:
      ws://localhost:8080/invocations-bidirectional-stream.
    • HTTP/2 event stream снаружи автоматически превращается в WebSocket-фреймы внутри.
    • Если клиент указал DataType="UTF8", SageMaker отправляет текстовые WebSocket-фреймы в контейнер.
  3. Docker-контейнер: FastAPI-мост → vLLM

    • В контейнере поднимается FastAPI-приложение на порту 8080 с маршрутом /invocations-bidirectional-stream.
    • При подключении SageMaker этот мост открывает второй WebSocket к vLLM Realtime API:
      ws://localhost:8081/v1/realtime.
    • Все текстовые сообщения прокидываются как есть, без трансформации протокола Realtime API.

vLLM-сервер работает на порту 8081, отдаёт WebSocket-эндпоинт /v1/realtime и health-эндпоинт /health. FastAPI-прокси дополнительно обрабатывает /ping, чтобы удовлетворить требования SageMaker к health-check'ам.

Протокол Realtime API vLLM

vLLM Realtime API реализует классическую схему для потокового ASR:

  1. Клиент подключается к ws://host/v1/realtime.
  2. Сервер отправляет событие session.created.
  3. Клиент при необходимости шлёт session.update с параметрами и именем модели.
  4. Клиент отправляет input_audio_buffer.commit, когда готов стримить аудио.
  5. Клиент отправляет серию input_audio_buffer.append с фрагментами аудио в base64 PCM16, 16 кГц, mono.
  6. Сервер по мере накопления контекста шлёт transcription.delta — приращения текста.
  7. В конце сервер шлёт transcription.done с финальной расшифровкой и статистикой использования.
  8. Для следующей реплики клиент снова начинает с шага 5.

Ключевой момент: модель не ждёт конца аудио. Как только контекста достаточно, она сразу выдаёт transcription.delta, пока клиент продолжает стримить новые куски.

Опционально клиент может завершить ввод аудио явным input_audio_buffer.commit с "final": true — удобно при проигрывании файлов.

Аудио-пайплайн

До того как аудио попадёт в модель, клиент обязан:

  • привести звук к 16 кГц, моно, PCM16;
  • нарезать на чанки подходящего размера (в примере — 4 КБ, это ≈128 мс аудио);
  • закодировать каждый чанк в base64;
  • упаковать в JSON-сообщение Realtime API и отправить через WebSocket (на стороне клиента) / HTTP/2 (на стороне SageMaker).

vLLM Realtime API ожидает именно такой формат, иначе транскрипция не запустится.

Как SageMaker понимает, что контейнер умеет стримить

В Dockerfile используется специальный лейбл:

LABEL com.amazonaws.sagemaker.capabilities.bidirectional-streaming=true

Без него SageMaker не будет открывать WebSocket к контейнеру и не включит двунаправочный стриминг.

CUDA Graph и производительность

vLLM внутри контейнера конфигурируется через переменные окружения SageMaker:

vllm_env = {
    "SM_VLLM_MAX_MODEL_LEN": "45000",
    "SM_VLLM_COMPILATION_CONFIG": '{"cudagraph_mode": "PIECEWISE"}'
}
  • SM_VLLM_MAX_MODEL_LEN=45000 — ограничивает контекст. Этого хватает примерно на 1 час живого аудио.
  • cudagraph_mode: "PIECEWISE" включает piecewise CUDA Graph. Это уменьшает overhead на запуск ядер GPU и снижает задержку на токен при стриминге.

Управление соединением и надёжность

SageMaker AI:

  • поддерживает WebSocket-соединение ping/pong-кадрами каждые 60 секунд;
  • разрывает соединение, если подряд не отвечают на 5 ping'ов;
  • мониторит endpoint в CloudWatch и выполняет health-check'и контейнера через /ping.

Клиенту приходится учитывать возможный разрыв и уметь переподключаться, особенно для длинных сессий.

Что это значит для вас

Для чего это хорошо подходит

  1. Голосовые ассистенты и voice-боты
    Если вы строите ассистента, который должен отвечать без длинных пауз, важно начинать транскрипцию ещё до конца реплики. Связка SageMaker AI + vLLM + Voxtral-Mini-4B-Realtime-2602 как раз делает это: вы стримите аудио, модель выдаёт текст по мере поступления.

  2. Живые субтитры и captioning
    Для онлайн-ивентов, стримов, внутренних митингов — можно транскрибировать речь в реальном времени и показывать текст сразу. Контекст до 45 000 токенов (в примере) позволяет держать длинный разговор, не обрезая историю, а полный лимит Voxtral в 262 144 токена даёт запас для ещё более длительных сессий.

  3. Аналитика контакт-центров и колл-трекинг
    Вариант: вы подключаете телефонию, конвертируете сигнал в 16 кГц PCM16 и стримите в SageMaker. В ответ получаете текст, который можно сразу скармливать LLM для резюме звонка, оценки качества диалога или поиска триггерных слов.

  4. Инструменты доступности
    Приложения для людей с нарушениями слуха выигрывают от минимальной задержки. Здесь важен именно стриминг: пользователь видит текст почти синхронно с речью.

  5. Real-time перевод и мультимодальные пайплайны
    Стек удобно использовать как первый шаг: аудио → текст. Дальше можно подключать свой LLM для перевода, суммаризации или генерации ответов. Архитектура уже рассчитана на двунаправочные сценарии.

Где лучше не использовать

  1. Офлайн-пакетная транскрипция больших архивов
    Если у вас тысячи часов записей, которые не нужно обрабатывать в реальном времени, проще и дешевле запускать batch-инференс или офлайн-пайплайн, а не держать real-time endpoint с GPU.

  2. Сценарии с жёсткими ограничениями по бюджету
    Модель на 4B параметров на инстансе с L4 — это не самый дешёвый вариант, особенно если endpoint работает 24/7. Для редких задач лучше запускать его по расписанию или использовать более компактные модели.

  3. Сверхнизкая латентность на миллисекундном уровне
    Здесь в цепочке есть HTTP/2, WebSocket и мост внутри контейнера. Для большинства продуктовых задач задержка будет приемлемой, но для экстремально чувствительных систем (например, специализированные голосовые интерфейсы в трейдинге) может потребоваться более прямое подключение к модели без managed-слоя.

  4. Работа без доступа к AWS
    Весь стек завязан на SageMaker AI и AWS-инфраструктуру. Если вы не можете использовать AWS (по юридическим причинам, политике компании или отсутствию биллинга), этот подход не подходит.

Доступность и ограничения для России

SageMaker AI и связанные managed-сервисы AWS официально доступны в поддерживаемых регионах AWS. Для России прямой доступ может быть затруднён или невозможен из-за сетевых и юридических ограничений. В ряде случаев потребуется:

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

Если у вас нет стабильного доступа к AWS, рассчитывать на эту архитектуру как на основу продукта не стоит.

Место на рынке

Прямых числовых сравнений с GPT-4o, Claude 3 или другими ASR-сервисами в материале нет. Зато ясно, на чём делает ставку AWS:

  • Открытый стек на vLLM

    • vLLM — open source. Вы полностью контролируете конфигурацию модели, квантование, компиляцию.
    • Нет жёсткой привязки к конкретному вендору на уровне сервинга: можно менять модели, не меняя инфраструктуру.
  • Managed-инфраструктура против self-hosted

    • В отличие от развёртывания vLLM на своих GPU-серверах, SageMaker AI берёт на себя: протокольный мост HTTP/2 ⇄ WebSocket, health-check'и, мониторинг через CloudWatch, управление жизненным циклом endpoint'а.
    • За это вы платите стоимость инстансов SageMaker и получаете меньше операционной рутины.
  • Сравнение с готовыми API (типа "Speech-to-Text как сервис")

    • Здесь вы сами выбираете модель (Voxtral-Mini-4B-Realtime-2602 или другую, совместимую с Realtime API).
    • Можно настраивать контекст, компиляцию, менять версию модели, не завися от roadmap внешнего API.
    • Зато нет простой поминутной тарификации "как у классического SaaS" — вы платите за выделенный GPU-инстанс, даже когда на нём мало трафика.

Если вам нужна полная управляемость и вы уже живёте в AWS, это логичный конкурент self-hosted vLLM или кастомных WebSocket-сервисов. Если вы хотите "просто API за запрос", без DevOps, проще смотреть в сторону готовых ASR-сервисов.

Установка

Ниже — ключевые шаги из оригинального примера: сборка Docker-образа, настройка среды и развёртывание endpoint'а в SageMaker AI.

Dockerfile для vLLM с двунаправочным стримингом

FROM public.ecr.aws/deep-learning-containers/vllm:0.17.1-gpu-py312-cu129-ubuntu22.04-sagemaker-v1.0-soci

# Tell SageMaker AI this container supports bidirectional streaming
LABEL com.amazonaws.sagemaker.capabilities.bidirectional-streaming=true

WORKDIR /opt/ml/code

# Install bridge dependencies
COPY requirements.txt .
RUN pip install --upgrade --no-cache-dir -r requirements.txt

# WebSocket bridge: routes /invocations-bidirectional-stream → /v1/realtime
COPY app.py .
COPY sagemaker-entrypoint.sh entrypoint.sh
RUN chmod +x entrypoint.sh

ENTRYPOINT ["./entrypoint.sh"]

HEALTHCHECK --interval=30s --timeout=10s --start-period=120s --retries=3 \
 CMD curl -f http://localhost:8080/ping || exit 1

Критичные моменты:

  • базовый образ — официальный SageMaker vLLM DLC с CUDA 12.9 и Python 3.12;
  • лейбл включает поддержку двунаправочного стриминга;
  • healthcheck смотрит на /ping FastAPI-моста.

FastAPI-мост app.py

VLLM_WS_URL = "ws://localhost:8081/v1/realtime"

@app.websocket("/invocations-bidirectional-stream")
async def websocket_bridge(sm_ws: WebSocket):
    await sm_ws.accept()
    async with websockets.connect(VLLM_WS_URL) as vllm_ws:
        async def sm_to_vllm():
            """Forward SageMaker AI → vLLM."""
            while True:
                message = await sm_ws.receive()
                if "text" in message and message["text"]:
                    await vllm_ws.send(message["text"])
                elif "bytes" in message and message["bytes"]:
                    # Fallback for non-UTF8 clients
                    await vllm_ws.send(message["bytes"].decode("utf-8"))

        async def vllm_to_sm():
            """Forward vLLM → SageMaker AI."""
            async for msg in vllm_ws:
                if isinstance(msg, str):
                    await sm_ws.send_text(msg)
                elif isinstance(msg, bytes):
                    await sm_ws.send_bytes(msg)

        await asyncio.gather(sm_to_vllm(), vllm_to_sm())

Здесь:

  • /invocations-bidirectional-stream — путь, который ожидает SageMaker AI для стриминга;
  • VLLM_WS_URL указывает на реальный Realtime API vLLM;
  • текстовые фреймы проходят без изменений, бинарные — декодируются как UTF-8 для совместимости с клиентами без DataType="UTF8".

Настройка окружения vLLM

vllm_env = {
    "SM_VLLM_MAX_MODEL_LEN": "45000",
    "SM_VLLM_COMPILATION_CONFIG": '{"cudagraph_mode": "PIECEWISE"}'
}

Эти переменные задаются в модели SageMaker и управляют поведением vLLM-сервера.

Создание модели и endpoint'а в SageMaker AI

# Create model
voxtral_model = Model.create(
    model_name=model_name,
    primary_container=ContainerDefinition(
        image=inference_image,
        model_data_source=ModelDataSource(
            s3_data_source=S3ModelDataSource(
                s3_uri=f"{model_artifact}/",
                s3_data_type="S3Prefix",
                compression_type="None",
            )
        ),
        environment=vllm_env
    ),
    execution_role_arn=role,
)

# Create config
endpoint_config = EndpointConfig.create(
    endpoint_config_name=endpoint_config_name,
    production_variants=[
        ProductionVariant(
            variant_name="AllTraffic",
            model_name=model_name,
            initial_variant_weight=1.0,
            instance_type=instance_type,
            initial_instance_count=1,
            model_data_download_timeout_in_seconds=health_check_timeout,
        )
    ]
)

# Create endpoint
endpoint = Endpoint.create(
    endpoint_name=endpoint_name,
    endpoint_config_name=endpoint_config_name
)
endpoint.wait_for_status("InService")

После этого у вас есть real-time endpoint SageMaker AI с поддержкой двунаправочного стриминга, за которым скрывается vLLM с Voxtral-Mini-4B-Realtime-2602.

Как запустить

Клиент для стриминга аудиофайла

Пример из репозитория использует Python SDK aws-sdk-sagemaker-runtime-http2.

Запуск:

python sagemaker_bidi_client.py ./audio.wav \
  --region us-east-1

Поведение клиента:

  • отправляет session.update для выбора модели;
  • читает WAV-файл и режет на чанки по 4 КБ PCM16;
  • отправляет чанки в стриме, делая await asyncio.sleep(chunk_duration) после каждого, чтобы имитировать real-time (≈128 мс на чанк при 16 кГц);
  • параллельно слушает ответы и печатает transcription.delta по мере прихода.

Важно: пауза asyncio.sleep даёт event loop возможность обрабатывать входящие события. Без неё отправка чанков может быть быстрее, чем модель успевает отвечать, и стрим визуально будет выглядеть последовательным, хотя технически он двунаправочный.

Клиент также различает типы событий:

  • ResponseStreamEventPayloadPart — нормальные JSON-сообщения (session.created, transcription.delta, transcription.done);
  • ResponseStreamEventModelStreamError и ResponseStreamEventInternalStreamFailure — отдельные ветки обработки, чтобы ошибки модели и платформы не терялись в общем потоке.

Live-демо с микрофоном на Gradio

Для минимальной задержки и демонстрации живой речи используется Gradio-клиент:

python sagemaker_bidi_microphone_client.py \
  --endpoint-name <имя_эндпоинта> \
  --region us-east-1

Что он делает:

  • в браузере поднимает интерфейс с микрофоном;
  • захватывает аудио потоком, ресемплирует до 16 кГц PCM16;
  • кодирует каждый чанк в base64 и отправляет через SageMaker AI bidirectional stream с DataType="UTF8";
  • отображает обновляющийся текст транскрипции по мере прихода transcription.delta, не дожидаясь конца фразы.

Практические нюансы

  • Формат аудио
    vLLM Realtime API жёстко ожидает base64-encoded PCM16, 16 кГц, mono. Если у вас другой формат (телефония, веб-аудио, записи с диктофона) — конвертируйте перед отправкой.

  • Размер чанков и задержка

    • Пример использует 4 КБ чанки, это ≈128 мс аудио.
    • Меньше чанк — ниже задержка, но больше overhead на сообщение.
    • Больше чанк — лучше throughput, но выше задержка.
      Подбирать размер стоит под ваши SLA по latency.
  • Ресурсы
    Voxtral-Mini-4B-Realtime-2602 помещается на один GPU, поэтому достаточно ml.g6.4xlarge с NVIDIA L4. Для более тяжёлых моделей потребуется масштабирование или другие типы инстансов.

  • Долгие сессии
    SageMaker отправляет ping каждые 60 секунд и закрывает соединение после 5 пропущенных ответов. Клиент должен быть готов к реконнекту и восстановлению состояния.

  • Очистка ресурсов
    Endpoint SageMaker AI тарифицируется по времени работы инстанса. После тестов имеет смысл:

    • удалить endpoint;
    • удалить endpoint config и модель;
    • при необходимости — почистить образ в Amazon ECR и артефакты модели в S3.

Что делать дальше

Если вы хотите применить этот стек в продакшене, логичный план такой:

  1. Развернуть свой endpoint
    Взять пример Dockerfile, FastAPI-мост и окружение vLLM, собрать образ, задеплоить в SageMaker AI, как в коде выше.

  2. Расширить Gradio-демо до продукта

    • добавить экспорт транскриптов;
    • прикрутить пост-обработку (пунктуация, нормализация);
    • подключить LLM для суммаризации или ответа пользователю.
  3. Подогнать latency и стоимость

    • поиграть размерами чанков;
    • протестировать разные типы инстансов;
    • настроить параметры компиляции vLLM и MAX_MODEL_LEN под ваши реальные сценарии.
  4. Поменять модель
    Если вам нужен другой язык, акцент или тип задачи, можно заменить Voxtral-Mini-4B-Realtime-2602 на любую модель, совместимую с Realtime API vLLM. Архитектуру и инфраструктуру при этом трогать не нужно — меняется только модель и её конфиг.


Читайте также