> ## Documentation Index
> Fetch the complete documentation index at: https://docs.apiyi.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Realtime Voice (WebSocket)

> Четыре модели Realtime Voice, один эндпоинт wss, один ключ APIYI: двунаправленная потоковая передача аудио, barge-in, два режима определения конца реплики, вызов функций и ввод изображений. Закрытая бета-версия — включает полное сравнение двух протоколов по каждому полю и бесплатный путь самотестирования только с текстом.

## Обзор

Модели Realtime работают через **долгоживущее WebSocket-соединение**: аудио поступает на вход, аудио выходит на выходе, а модель можно прервать посреди предложения — без цикла «записать, загрузить, подождать, воспроизвести». Отличие от связки ASR + text model + TTS в том, что это сквозной процесс: модель напрямую слышит тембр, паузы и эмоции и так же напрямую говорит. Задержка укладывается в диапазон менее секунды.

APIYI сейчас предлагает **4 модели в 2 протоколах**, используя один эндпоинт и один ключ:

* `gpt-realtime-2.1` / `gpt-realtime-2.1-mini` — OpenAI Realtime GA protocol
* `qwen3.5-omni-plus-realtime` / `qwen3.5-omni-flash-realtime` — Alibaba Cloud Model Studio protocol

<Warning>
  **Статус: закрытая бета / интеграция в процессе.** Голосовой Realtime доступен в ограниченном объеме и **пока не открыт для самостоятельного подключения** — для активации нужно связаться с нами. Вышестоящий протокол и поведение во время беты еще могут измениться; все в разделе «Известные ограничения» ниже измерено и будет обновляться по мере изменений upstream. Пожалуйста, не запускайте в production без fallback-пути. Если вы планируете интеграцию или вам нужна более высокая параллельность, свяжитесь с нами через [WeCom support](https://work.weixin.qq.com/kfid/kfc9adfd5810ece25ec) или по почте [hi@apiyi.com](mailto:hi@apiyi.com) / [feedback@apiyi.com](mailto:feedback@apiyi.com).
</Warning>

<Note>
  **🎤 Основные возможности**: двунаправленная потоковая передача аудио по одному соединению, **barge-in в любой момент**, `server_vad` и `semantic_vad` обнаружение смены реплики, полный round trip function-calling (включая внедрение результата), ввод изображений и `usage`, разделённые по модальностям. Все перечисленное подтверждено для всех четырех моделей (2026-08-24, UTC+8).
</Note>

<Info>
  **Первое, что нужно запомнить**: 4 модели используют **два разных протокола запросов**, с разными именами полей и разными именами событий. **Изменение только параметра `model` без изменения тела запроса не сработает** — это, безусловно, самая частая ошибка интеграции. Различия сводятся к 6 полям и 3 именам событий; все они перечислены ниже в разделе «Сравнение протоколов».
</Info>

<CardGroup cols={2}>
  <Card title="Запросить доступ к бете" icon="headphones" href="https://work.weixin.qq.com/kfid/kfc9adfd5810ece25ec">
    Свяжитесь со службой поддержки WeCom, укажите ваш аккаунт и ожидаемую параллельность, и мы включим бета-группу на вашем ключе.
  </Card>

  <Card title="Руководство по API" icon="book-open" href="/ru/api-manual">
    Создание ключей, base URL, режимы тарификации и другие общие соглашения.
  </Card>

  <Card title="Ключи и группы" icon="key-round" href="/ru/api-capabilities/token-management">
    Создавайте ключи, выбирайте группы и задавайте квоты.
  </Card>

  <Card title="Журналы вызовов" icon="receipt-text" href="https://api.apiyi.com/log">
    Просматривайте использование token и фактические начисления по каждому вызову в консоли.
  </Card>
</CardGroup>

Эта страница длинная. Три раздела обязательны к прочтению: **Сравнение протоколов** (прочитайте перед переключением между моделями), **Начните с текста** (проверьте всю цепочку без микрофона) и **Известные ограничения** (четыре измеренных различия, влияющие на код клиента).

## Пусть AI-агент выполнит интеграцию

<Note>
  Если вы разрабатываете с Codex / Claude Code / Cursor, скопируйте туда prompt ниже. Сначала он получит текстовую версию этой страницы (добавьте `.md` к любому docs URL), а затем напишет code под ваш стек — в требования уже заложены **две семейства полей**, жёсткое ограничение по sample rate, семантика cancel и отключения при простое.
</Note>

<Prompt description="Пусть кодирующий агент интегрирует или отладит Realtime voice. Скопируйте и вставьте в Codex, Claude Code, Cursor и похожие инструменты." icon="bot" actions={["copy"]}>
  Помогите мне интегрировать / отладить APIYI Realtime voice (двунаправленная потоковая передача через WebSocket) в этом проекте.

  Перед тем как писать code, прочитайте docs: получите [https://docs.apiyi.com/en/api-capabilities/realtime/overview.md](https://docs.apiyi.com/en/api-capabilities/realtime/overview.md) в текстовой версии этой страницы, сосредоточившись на разделах «Сравнение протоколов» и «Известные ограничения».

  Требования:

  1. Это **долгоживущее WebSocket-соединение**, а не HTTP request. Endpoint — `wss://api.apiyi.com/v1/realtime?model=<model-name>`, а авторизация передаётся в заголовке `Authorization: Bearer <key>`. **Не оформляйте это как HTTP POST и не пытайтесь строить URL для `/v1/audio/speech` или `/v1/audio/transcriptions`** — это другой API.

  2. **Прежде чем писать какое-либо поле, определите, к какому семейству протокола относится model.** `gpt-realtime-2.1` и `gpt-realtime-2.1-mini` используют протокол Realtime GA; `qwen3.5-omni-plus-realtime` и `qwen3.5-omni-flash-realtime` используют протокол Alibaba Cloud Model Studio. Общими являются только endpoint и auth — тела request и имена событий различаются повсюду: режим вывода `modalities` vs `output_modalities`; voice на верхнем уровне `voice` vs `audio.output.voice`; `input_audio_format` vs `audio.input.format`; верхний уровень `turn_detection` vs `audio.input.turn_detection`; `input_audio_transcription` vs `audio.input.transcription`. Имена событий: `response.text.delta` vs `response.output_text.delta`, `response.audio.delta` vs `response.output_audio.delta`. **Оформите это как два шаблона конфигурации, не размазывайте if-ветки по code.**

  3. Жёсткое ограничение по audio format: всегда PCM signed 16-bit, mono, кодируется в Base64 в `input_audio_buffer.append`. **Sample rate отличается между ними** — Model Studio использует 16000, а Realtime GA требует не менее 24000 и отклоняет 16000 с `integer_below_min_value`. Выполняйте resample на клиенте; не ждите, что server исправит это.

  4. Не блокируйтесь на `response.done`, чтобы завершить barge-in. После отправки `response.cancel`, **две модели Model Studio сейчас не присылают `response.done`** (это стабильно воспроизводится в testing). Используйте `response.output_item.done` как сигнал завершения хода и добавьте 5-секундный timeout как страховку; две модели Realtime GA работают корректно, но та же логика подходит для обеих.

  5. Долгим соединениям нужны keepalive и reconnect. **Model Studio разрывает idle-соединения через 300 секунд, а WebSocket-level ping/pong не считается активностью** — этот таймер он не продлевает. Либо периодически отправляйте событие уровня application, пока соединение простаивает, либо принимайте disconnect и reconnect автоматически. Сеансы Realtime GA имеют `expires_at` (примерно через 30 минут от connect) и тоже требуют повторного подключения. **После reconnect необходимо повторно отправить `session.update` и весь необходимый context**, иначе новая session будет работать со значениями по умолчанию.

  6. Зафиксируйте voice в первом `session.update`. После того как session уже сгенерировала audio output, изменение voice завершается с `cannot_update_voice`. Чтобы сменить voice, откройте новую session.

  7. Читайте key из environment variable `APIYI_API_KEY`; никогда не встраивайте его в code и никогда не коммитьте его. **Не подключайтесь из frontend** — напишите backend relay, который хранит key и пересылает audio frames.

  8. Перед тем как использовать microphone, выполните smoke test только с text: установите `output_modalities` в text only, отправьте один `input_text` и убедитесь, что вы получаете text deltas и объект `usage` внутри `response.done`. Затем переходите к audio. Когда закончите, реально выполните один call для каждого семейства протокола и вставьте оба объекта `usage` обратно мне.
</Prompt>

<Accordion title="Что этот prompt помогает вам избежать">
  | Требование                                         | Какую ошибку оно предотвращает                                                            |
  | -------------------------------------------------- | ----------------------------------------------------------------------------------------- |
  | Сначала определить семейство протокола             | Если менять только `model`, handshake проходит, но `session.update` затем отклоняется     |
  | Sample rate зафиксирован по семейству              | Отправка 16 kHz в Realtime GA завершается с `integer_below_min_value`                     |
  | Поле output-modality было переименовано            | Запись `modalities` в протоколе GA — это просто неизвестное поле                          |
  | Имена событий тоже изменились                      | Подписка на `response.text.delta` в протоколе GA никогда не срабатывает                   |
  | Завершать по `output_item.done`                    | Model Studio не присылает `response.done` после cancel; ожидание его зависает             |
  | Нужен keepalive, ping не считается                 | Предположение, что heartbeat предотвращает разрыв простоя через 300 секунд                |
  | voice зафиксирован в первом кадре                  | Изменение после того, как audio уже был сгенерирован, завершается с `cannot_update_voice` |
  | Backend relay, без прямого подключения из браузера | При подключении из браузера key достаётся каждому посетителю                              |
</Accordion>

## Почему APIYI для голоса в реальном времени

<CardGroup cols={2}>
  <Card title="Один ключ, четыре модели" icon="key-round">
    Тот же `wss` эндпоинт, та же авторизация. При смене моделей нужно изменить параметр `model` и соответствующий шаблон полей — не нужно поддерживать второй аккаунт поставщика.
  </Card>

  <Card title="Прямой доступ, без зарубежной настройки" icon="globe">
    Подключайтесь к `api.apiyi.com` из дата-центров материкового Китая, домашнего широкополосного интернета или зарубежных узлов. Не требуется аккаунт внешнего поставщика, подтверждение личности или предоплата.
  </Card>

  <Card title="Различия протоколов уже сопоставлены" icon="git-compare">
    Сравнение полей, сравнение названий событий, ограничения sample-rate и четыре измеренных ограничения уже задокументированы здесь, чтобы вам не пришлось заново их выяснять.
  </Card>

  <Card title="Бесплатная самопроверка по тексту" icon="terminal">
    Проверьте handshake, auth, поля, подключение tools и параллельные запросы без микрофона — аудио-тарифы стоят на порядок дороже текста, так что на интеграции это реальные сэкономленные деньги.
  </Card>

  <Card title="Измеренная задержка и параллельные запросы" icon="gauge">
    Handshake p50 0.65–1.08 s и первый text delta p50 0.54–0.95 s при 20 параллельных сессиях, при этом условия теста и дата указаны в разделе Technical Specs.
  </Card>

  <Card title="Прямая поддержка в период беты" icon="handshake">
    Пользователи беты получают прямой канал WeCom для вопросов по интеграции, увеличения параллельных запросов и изменений поведения upstream.
  </Card>
</CardGroup>

## Основные возможности

<CardGroup cols={2}>
  <Card title="Двунаправленная потоковая передача, прерываемая" icon="radio">
    Аудио передаётся по мере генерации; клиент может отправить `response.cancel` в любое время. Сессия сохраняется, контекст сохраняется. Проверено на всех четырёх моделях.
  </Card>

  <Card title="Два режима определения смены реплики" icon="scissors">
    `server_vad` разделяет по длительности тишины, `semantic_vad` разделяет по намерению (лучше игнорирует слова-паразиты вроде «uh-huh»). Оба режима проверены на всех четырёх моделях.
  </Card>

  <Card title="Полный цикл вызова функций" icon="wrench">
    Модель вызывает инструмент, клиент выполняет его, `function_call_output` внедряет результат, и модель продолжает отвечать. Проверено от начала до конца на всех четырёх моделях.
  </Card>

  <Card title="Ввод изображений, учёт использования по модальностям" icon="image">
    Отправляйте изображения в ходе сеанса, чтобы модель могла их прочитать; `usage` возвращает tokens текста / аудио / изображений отдельно, чтобы можно было отнести стоимость. Проверено на всех четырёх моделях.
  </Card>
</CardGroup>

## Поддерживаемые модели

| Модель                        | Семейство протокола | Голос по умолчанию | Частота дискретизации входного сигнала | Кэширование промптов | Позиционирование                                      |
| ----------------------------- | ------------------- | ------------------ | -------------------------------------- | -------------------- | ----------------------------------------------------- |
| `gpt-realtime-2.1`            | Realtime GA         | `marin`            | ≥ 24 kHz                               | ✅ Поддерживается     | Флагман; самый сильный в многоязычности и рассуждении |
| `gpt-realtime-2.1-mini`       | Realtime GA         | `marin`            | ≥ 24 kHz                               | ✅ Поддерживается     | Экономичный; достаточно для повседневного общения     |
| `qwen3.5-omni-plus-realtime`  | Model Studio        | `Tina`             | 16 kHz                                 | ⏸ Не обнаружено      | Флагман для сценариев на китайском языке              |
| `qwen3.5-omni-flash-realtime` | Model Studio        | `Tina`             | 16 kHz                                 | ⏸ Не обнаружено      | Экономичный для сценариев на китайском языке          |

Выходной аудиосигнал во всех четырёх моделях — **PCM со знаком 16-bit / mono / 24 kHz**.

<Warning>
  Два семейства протоколов **имеют общими только эндпоинт и схему аутентификации**. Поля запроса и имена серверных событий также различаются. При смене моделей вам нужно менять и шаблоны полей — см. ниже «Сравнение протоколов».
</Warning>

## Цены

<Info>
  **Цены в одном предложении**: тарификация по token, и **аудио обходится в разы дороже текста** (для `gpt-realtime-2.1`: вход аудио \$32 против входа текста \$4; выход аудио \$64 против выхода текста \$24). Во время интеграции запускайте только текстовый режим и переключайтесь на аудио после проверки цепочки — см. «Начните с текста» ниже.
</Info>

Приведённые ниже таблицы — это **официальные прайс-листы поставщиков**, в USD за 1M token. **Фактические списания в APIYI — это то, что показывают [журналы вызовов](https://api.apiyi.com/log)**; [бонус за пополнение](/ru/faq/recharge-promotions) ещё больше снижает эффективную стоимость.

### Протокол Realtime GA

| Модель                  | Текстовый ввод | Текстовый вывод | Чтение из кэша | Ввод изображения | Ввод аудио | Вывод аудио |
| ----------------------- | -------------- | --------------- | -------------- | ---------------- | ---------- | ----------- |
| `gpt-realtime-2.1`      | \$4            | \$24            | \$0.4          | \$5              | \$32       | \$64        |
| `gpt-realtime-2.1-mini` | \$0.6          | \$2.4           | \$0.06         | \$0.8            | \$10       | \$30        |

### Протокол Model Studio

Измерения тарификации различаются: ввод изображения включён в текстовый тариф, а выход разделён на «только текст» и «текст + аудио» (по последнему тарифу оплачивается только аудиочасть).

| Модель                        | Текстовый / ввод изображения | Ввод аудио | Текстовый вывод | Вывод текста + аудио |
| ----------------------------- | ---------------------------- | ---------- | --------------- | -------------------- |
| `qwen3.5-omni-plus-realtime`  | \$1.38                       | \$11       | \$8.25          | \$41.26              |
| `qwen3.5-omni-flash-realtime` | \$0.45                       | \$3.71     | \$2.75          | \$14.71              |

<Note>
  **Примечание о Beta**: голосовой Realtime доступен в ограниченном объёме, и тарификация всё ещё приводится в соответствие с upstream. Если ваши фактические списания заметно отличаются от таблиц выше, пожалуйста, обратитесь в поддержку, чтобы мы могли провести сверку. Цены могут меняться в зависимости от политики поставщика и доступности. Эта возможность предлагается, чтобы **обеспечить поставки и обслуживать клиентов**, а не как предложение, ориентированное на прибыль.
</Note>

## Группа доступа

<Note>
  **Как получить доступ во время беты**: самостоятельный выбор группы пока недоступен; доступ предоставляется по запросу. Свяжитесь со [службой поддержки WeCom](https://work.weixin.qq.com/kfid/kfc9adfd5810ece25ec) с вашим аккаунтом, вариантом использования и ожидаемыми параллельными запросами, и мы включим beta-группу на ваш ключ и сообщим о текущих ограничениях. О выпуске для общего доступа будет объявлено в [журнале изменений](/en/changelog); тогда не потребуется никаких изменений в ключе или коде.
</Note>

## Технические характеристики

| Параметр                      | Значение                                                                                                                                                              |
| ----------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Транспорт**                 | WebSocket (`wss://`), двунаправленная потоковая передача по одному соединению                                                                                         |
| **Авторизация**               | заголовок `Authorization: Bearer <key>`                                                                                                                               |
| **Формат событий**            | Совместим с моделью событий OpenAI Realtime (события клиента / события сервера)                                                                                       |
| **Входной аудио**             | PCM со знаком, 16-бит, моно, Base64. **Model Studio 16 kHz; Realtime GA ≥ 24 kHz**                                                                                    |
| **Выходной аудио**            | PCM со знаком, 16-бит, моно, 24 kHz                                                                                                                                   |
| **Режимы вывода**             | Текст / аудио (допускается только текст)                                                                                                                              |
| **Определение конца реплики** | `server_vad`, `semantic_vad`, или отключено для ручного `commit`                                                                                                      |
| **Вызов функций**             | Поддерживается, включая вставку результата `function_call_output`                                                                                                     |
| **Ввод изображений**          | Поддерживается (синтаксис отличается в зависимости от семейства, см. ниже)                                                                                            |
| **Срок жизни сессии**         | Realtime GA: сессия сохраняет `expires_at`, что измеряется примерно как 30 минут с момента подключения. Model Studio: отключение при простое измеряется на 300 секунд |

### Измеренная задержка и параллельные запросы

Измерено 2026-08-24 (UTC+8) по публичному `api.apiyi.com` пути, 20 параллельных сессий × 2 модели, одноходовой обмен только текстом:

| Метрика                   | Измерено                                                         |
| ------------------------- | ---------------------------------------------------------------- |
| Рукопожатие WebSocket     | p50 0.65–1.08 s                                                  |
| Первый текстовый фрагмент | p50 0.54–0.95 s                                                  |
| Полный один ход           | p50 \< 1 s                                                       |
| Успешность сессии         | 99.6% (один сбой рукопожатия, восстановлено при переподключении) |

<Warning>
  Это разовые измерения на конкретном уровне параллельных запросов и не являются обязательством по производительности. **Во время бета-версии SLA по доступности не предоставляется** — реализуйте переподключение и плавную деградацию на клиенте.
</Warning>

## Эндпоинт

| Эндпоинт                                             | Назначение                                                 | Аутентификация                |
| ---------------------------------------------------- | ---------------------------------------------------------- | ----------------------------- |
| `wss://api.apiyi.com/v1/realtime?model=<model-name>` | Открыть сеанс голосового взаимодействия в реальном времени | `Authorization: Bearer <key>` |

Все четыре модели используют этот эндпоинт; параметр запроса `model` выбирает, к какой из них вы обратитесь.

<Warning>
  **При подключении из браузера**: этот эндпоинт также поддерживает аутентификацию через субпротокол `Sec-WebSocket-Protocol` (`realtime, openai-insecure-api-key.<key>, openai-beta.realtime-v1`), так что браузерный `WebSocket` может подключаться напрямую — но это **передаёт ваш ключ браузеру**, где любой посетитель может прочитать его в сетевой панели. **Используйте это только для локальной проверки.** В production настройте бэкенд-релей: бэкенд хранит ключ и открывает соединение с APIYI, а фронтенд общается только с вашим сервисом.
</Warning>

## ⚠️ Сравнение протоколов (прочтите перед переключением моделей)

Обе семейства используют один и тот же эндпоинт, схему авторизации и общий поток событий. Различия сосредоточены в структуре поля `session.update` и нескольких именах серверных событий.

### Сравнение полей запроса

| Назначение                  | Протокол Model Studio               | Протокол Realtime GA                                      |
| --------------------------- | ----------------------------------- | --------------------------------------------------------- |
| Режим вывода                | `modalities: ["text","audio"]`      | `output_modalities: ["audio"]`                            |
| Голос                       | `voice` (верхнего уровня)           | `audio.output.voice`                                      |
| Скорость                    | Не поддерживается                   | `audio.output.speed` (0.7 / 1.0 / 1.5, по линейной шкале) |
| Формат входного аудио       | `input_audio_format: "pcm"`, 16 kHz | `audio.input.format: {"type":"audio/pcm","rate":24000}`   |
| Формат выходного аудио      | `output_audio_format: "pcm"`        | `audio.output.format: {"type":"audio/pcm","rate":24000}`  |
| Определение смены реплики   | `turn_detection` (верхнего уровня)  | `audio.input.turn_detection`                              |
| Транскрипция входного аудио | `input_audio_transcription`         | `audio.input.transcription`                               |

### Сравнение серверных событий

| Содержимое                  | Протокол Model Studio             | Протокол Realtime GA                     |
| --------------------------- | --------------------------------- | ---------------------------------------- |
| Текстовое изменение         | `response.text.delta`             | `response.output_text.delta`             |
| Аудио изменение             | `response.audio.delta`            | `response.output_audio.delta`            |
| Изменение транскрипта аудио | `response.audio_transcript.delta` | `response.output_audio_transcript.delta` |

Все остальные события — `session.created`, `session.updated`, `conversation.item.create`, `input_audio_buffer.append`, `input_audio_buffer.commit`, `response.create`, `response.cancel`, `response.done` — называются одинаково в обоих.

### Два минимальных payload для session.update

Один и тот же текст, записанный дважды; копируйте напрямую. **Протокол Model Studio**:

```json theme={null}
{
  "type": "session.update",
  "session": {
    "modalities": ["text", "audio"],
    "voice": "Ethan",
    "input_audio_format": "pcm",
    "output_audio_format": "pcm",
    "input_audio_transcription": { "model": "qwen3-asr-flash-realtime" },
    "turn_detection": { "type": "semantic_vad" }
  }
}
```

**Протокол Realtime GA**:

```json theme={null}
{
  "type": "session.update",
  "session": {
    "type": "realtime",
    "output_modalities": ["audio"],
    "audio": {
      "input": {
        "format": { "type": "audio/pcm", "rate": 24000 },
        "transcription": { "model": "whisper-1" },
        "turn_detection": { "type": "semantic_vad" }
      },
      "output": {
        "format": { "type": "audio/pcm", "rate": 24000 },
        "voice": "alloy",
        "speed": 1.0
      }
    }
  }
}
```

<Warning>
  **Частота дискретизации — жёсткое ограничение**: `audio.input.format.rate` в протоколе Realtime GA должна быть **≥ 24000**; при отправке 16000 запрос сразу завершается с `integer_below_min_value: Expected a value >= 24000`. Протокол Model Studio требует входные данные 16 kHz. Выполните ресемплирование на клиенте.
</Warning>

## Начните с текста: для чего нужен текстовый канал и трёхэтапная самопроверка

Аудиопайплайн включает захват с микрофона, ресемплинг, chunking и определение turns. Любое слабое звено проявляется как «ничего не происходит», а это трудно диагностировать. Поэтому **не начинайте с микрофона**.

### Текст — это управляющая плоскость, а не запасной способ ввода

В realtime voice model текст — это не «ещё один способ передать ввод», а **весь канал управления, кроме аудиопотока**:

| Комбинация                    | Типичное использование                                                                                                                                    |
| ----------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Текст → управляющая плоскость | `instructions` system prompts, `function_call_output` результаты tool, извлечённый контекст — всё это текст, и всё это недоступно через микрофон          |
| Текст → аудио                 | По сути, **TTS с полным контекстом диалога**: пусть обычная модель всё обдумает, а затем realtime-модель это озвучит. Подходит для объявлений и prompt'ов |
| Текст → текст                 | **Самый дешёвый канал отладки.** Для обычного текстового чата лучше подходит обычная chat model; здесь её ценность — проверить всю цепочку без затрат     |

### Трёхэтапная самопроверка

<Steps>
  <Step title="Шаг 1: только текст, без микрофона">
    Установите `output_modalities` в режим только текста, отключите turn detection и отправьте один `input_text`. Уже это проверяет handshake, ключ и группу, **правильно ли вы выбрали шаблон полей**, сработал ли `session.update`, корректно ли подставляются tools, сохраняется ли многотуровый контекст и как ведут себя параллельные запросы. **Вообще не создаётся ни одного audio token.**
  </Step>

  <Step title="Шаг 2: воспроизведите локальный wav-файл">
    Используйте фиксированный локальный аудиофайл вместо микрофона, передавая его в `input_audio_buffer.append` блоками по 100 ms. Это отделяет **аудиопайплайн** (формат, частоту дискретизации, разбиение на чанки, `commit`, срабатывание VAD) от вашей бизнес-логики и делает процесс воспроизводимым — один и тот же файл должен дважды давать один и тот же результат.
  </Step>

  <Step title="Шаг 3: подключите живой микрофон">
    Когда первые два шага пройдены, остаются только захват и воспроизведение. Если теперь что-то ломается, пространство поиска уже небольшое.
  </Step>
</Steps>

<Tip>
  **Нет под рукой тестового аудио?** На macOS встроенные инструменты за одну строку создают совместимый файл:

  ```bash theme={null}
  say -v Samantha -o /tmp/ask.aiff "What is the weather in Beijing today? Answer in one sentence."

  # Realtime GA protocol uses 24000
  afconvert -f WAVE -d LEI16@24000 -c 1 /tmp/ask.aiff ask_24k.wav
  # Model Studio protocol uses 16000
  afconvert -f WAVE -d LEI16@16000 -c 1 /tmp/ask.aiff ask_16k.wav
  ```

  Выбор неверной частоты дискретизации — самая частая ошибка на шаге 2: два протокола различаются, не путайте их.
</Tip>

### Запускаемый текстовый smoke test

Зависит только от `websockets` (`pip install websockets`). Переключайте протоколы, изменяя одну переменную:

```python theme={null}
import asyncio, json, os, websockets

FAMILY = "ga"           # ga = gpt-realtime-2.1 series; omni = qwen3.5-omni series
MODEL = "gpt-realtime-2.1" if FAMILY == "ga" else "qwen3.5-omni-plus-realtime"
URL = f"wss://api.apiyi.com/v1/realtime?model={MODEL}"
HEADERS = {"Authorization": "Bearer " + os.environ["APIYI_API_KEY"]}

# The two protocols diverge only here: session structure and the text-delta event name.
SESSION = ({"type": "realtime", "output_modalities": ["text"],
            "audio": {"input": {"turn_detection": None}}}
           if FAMILY == "ga" else
           {"modalities": ["text"], "turn_detection": None})
TEXT_DELTA = "response.output_text.delta" if FAMILY == "ga" else "response.text.delta"

async def main():
    async with websockets.connect(URL, additional_headers=HEADERS) as ws:
        while json.loads(await ws.recv())["type"] != "session.created":
            pass
        await ws.send(json.dumps({"type": "session.update", "session": SESSION}))
        await ws.send(json.dumps({"type": "conversation.item.create", "item": {
            "type": "message", "role": "user",
            "content": [{"type": "input_text", "text": "Explain WebSocket in one sentence."}]}}))
        await ws.send(json.dumps({"type": "response.create"}))
        while True:
            e = json.loads(await ws.recv())
            if e["type"] == TEXT_DELTA:
                print(e["delta"], end="", flush=True)
            elif e["type"] == "response.done":
                print("\n\nusage =", json.dumps(e["response"]["usage"]))
                return
            elif e["type"] == "error":
                print("\nERROR:", json.dumps(e))
                return

asyncio.run(main())
```

Если это запускается, эндпоинт, ключ, группа и шаблон полей настроены правильно — переходите к шагу 2.

## Особенности сессии: голос, определение смены реплики, инструменты, изображения

### Голос

| Элемент                | протокол Model Studio           | протокол Realtime GA                                                          |
| ---------------------- | ------------------------------- | ----------------------------------------------------------------------------- |
| Голос по умолчанию     | `Tina`                          | `marin`                                                                       |
| Подтверждённо работает | `Tina`, `Ethan` и другие        | `alloy`, `marin`, `cedar`, `shimmer`, `verse`                                 |
| Управление скоростью   | Не поддерживается               | `audio.output.speed`; длительность масштабируется линейно при 0.7 / 1.0 / 1.5 |
| Недопустимый голос     | `Voice 'xxx' is not supported.` | `invalid_value`                                                               |

<Warning>
  **Закрепляйте голос в первом `session.update`.** После того как сессия сгенерировала аудиовывод, изменение голоса завершается `cannot_update_voice` — это относится к обоим протоколам. Чтобы сменить голос, откройте новую сессию. Также в протоколе Model Studio **не отправляйте пустую строку в качестве голоса**; система откатится к неподдерживаемому голосу и вернёт 400. Если вам не нужно задавать значение, просто не передавайте это поле.
</Warning>

### Определение смены реплики: server\_vad и semantic\_vad

* `server_vad` — разделяет по длительности тишины, с простыми параметрами (`threshold`, `silence_duration_ms`, `prefix_padding_ms`).
* `semantic_vad` — разделяет по намерению в разговоре, игнорируя слова-паразиты и бессмысленный фоновый шум. Более устойчиво в средах с несколькими говорящими.
* Вы также можете отключить определение смены реплики (`null` или `none`) и работать в **ручном режиме**: сами отправляйте `input_audio_buffer.commit`, затем `response.create`. Это подходит для интерфейсов push-to-talk, где сменой реплик управляет UI.

<Tip>
  В режиме VAD вы **обязаны продолжать потоковую передачу**. После окончания речи продолжайте передавать короткий отрезок тишины (2 секунды достаточно при тестировании), чтобы сервер мог определить конец речи. Если вы отправите только озвученную часть и затем остановитесь, `speech_stopped` никогда не сработает и ответ не будет сгенерирован.
</Tip>

### Вызов функций

Порядок событий: модель отправляет `response.output_item.done` типа `function_call` (содержащий `call_id` и `arguments`) → клиент выполняет его → результат подставляется → ещё одно `response.create` позволяет модели продолжить.

```json theme={null}
{
  "type": "conversation.item.create",
  "item": {
    "type": "function_call_output",
    "call_id": "call_xxx",
    "output": "{\"city\":\"Beijing\",\"weather\":\"light rain\",\"temp_c\":21}"
  }
}
```

Полный цикл подтверждён на всех четырёх моделях — после подстановки модель корректно пересказывает, что вернул инструмент.

### Ввод изображений

**протокол Realtime GA**: поместите `input_image` напрямую в сообщение; значение может быть data URI.

```json theme={null}
{
  "type": "conversation.item.create",
  "item": {
    "type": "message",
    "role": "user",
    "content": [
      { "type": "input_image", "image_url": "data:image/jpeg;base64,..." },
      { "type": "input_text", "text": "What does the image say?" }
    ]
  }
}
```

**протокол Model Studio**: изображения рассматриваются как **кадры видео**, поэтому сначала нужно добавить audio, иначе вы получите `Error append image before append audio.`. При тестировании рабочий подход — вставлять `input_image_buffer.append` в поток `input_audio_buffer.append` примерно с частотой один кадр в секунду.

## Известные ограничения (beta)

Все четыре пункта ниже подтверждены, и все они влияют на код клиента. Прочитайте это перед интеграцией.

| Поведение                                                                                                         | Затронутое семейство                                      | Обходное решение на стороне клиента                                                                                                                                                                            |
| ----------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `response.done` не доставляется после `response.cancel`, поэтому ход никогда не завершается                       | Model Studio (воспроизведено во всех 6 тестовых запусках) | Используйте `response.output_item.done` как сигнал завершения хода, а также тайм-аут 5 секунд. Сама сессия не затрагивается, и после прерывания диалог продолжается нормально                                  |
| Бездействующие соединения разрываются через 300 секунд; WebSocket ping/pong **не считается активностью**          | Model Studio                                              | Периодически отправляйте событие прикладного уровня во время бездействия либо примите разрыв соединения и выполняйте автоматическое переподключение; после переподключения повторно отправьте `session.update` |
| Голос нельзя изменить после того, как сессия уже сгенерировала audio; завершается с ошибкой `cannot_update_voice` | Оба семейства                                             | Зафиксируйте `voice` в первом `session.update`; чтобы переключиться, откройте новую сессию                                                                                                                     |
| Событие завершения input-transcription не доставляется в ручном режиме `commit`                                   | Модель `flash` в Model Studio                             | Переключитесь на `server_vad` / `semantic_vad`, где transcription работает нормально. Сама беседа не затрагивается — модель корректно понимает audio и отвечает на него                                        |

<Warning>
  Эти поведения могут измениться по мере эволюции upstream в течение beta; эта страница будет поддерживаться в актуальном состоянии. Если вы столкнётесь с чем-то, чего нет в этом списке, сообщите об этом через [поддержку WeCom](https://work.weixin.qq.com/kfid/kfc9adfd5810ece25ec) или на [feedback@apiyi.com](mailto:feedback@apiyi.com), указав метку времени и `session.id`, чтобы мы могли отследить это.
</Warning>

## Лучшие практики

<Steps>
  <Step title="Сначала выбирайте шаблон поля по семейству протокола">
    Записывайте два `session.update` payloads как две константы конфигурации, выбираемые по имени модели, а не разбросанные по if-веткам. Именно эта часть чаще всего ломается при сопровождении спустя шесть месяцев.
  </Step>

  <Step title="Закрепите параметры сеанса в первом фрейме">
    Установите `output_modalities`, `voice`, `speed`, `turn_detection` и `transcription` в самом первом `session.update`. Особенно для голосового режима — после того как audio уже сгенерировано, будет слишком поздно.
  </Step>

  <Step title="Сначала пройдите текстовый smoke test перед добавлением audio">
    Запустите текстовый smoke test на этой странице, чтобы убедиться, что endpoint, ключ, группа и шаблон поля заданы верно, а затем переходите к audio. Аудио-уровни стоят на порядок дороже текстовых, так что это экономит большую часть вашего бюджета на интеграцию.
  </Step>

  <Step title="Преобразуйте sample rate и channels на клиенте">
    PCM 16-битный со знаком, моно; 16 kHz для Model Studio, ≥ 24 kHz для Realtime GA. Не ожидайте исправления на стороне сервера — неверный формат обычно проявляется тишиной, а не явной ошибкой.
  </Step>

  <Step title="Завершайте работу по output_item.done с тайм-аутом">
    Не ждите только `response.done`. Этот подход корректен для обеих семейств и помогает не зависать на одном ходе, когда пользователь прерывает.
  </Step>

  <Step title="Добавьте поддержание соединения и переподключение для длительных сеансов">
    Следите за 300-секундным лимитом простоя в Model Studio и `expires_at` в Realtime GA. **После переподключения повторно отправьте `session.update` и весь необходимый контекст**, иначе новый сеанс будет работать с настройками по умолчанию.
  </Step>

  <Step title="Используйте бэкенд-релей в продакшене">
    Храните ключ на бэкенде и заставьте фронтенд обращаться только к вашему сервису. Прямые подключения из браузера технически работают, но раскрывают ключ.
  </Step>
</Steps>

## Ошибки и повторные попытки

| Симптом                                                      | Значение                                                         | Что делать                                                                                                   |
| ------------------------------------------------------------ | ---------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ |
| Установление соединения возвращает 401                       | Неверный ключ или заголовок `Authorization` не был отправлен     | Проверьте сам ключ, не использовали ли вы по ошибке `https://`, и присутствует ли заголовок                  |
| Установление соединения возвращает 503 без доступного канала | Ключ не включен для бета-группы, либо неверно указано имя модели | Обратитесь в поддержку, чтобы подтвердить, что группа включена; проверьте параметр `model`                   |
| Установление соединения возвращает 400                       | Отсутствует query-параметр `model`                               | Эндпоинт должен включать `?model=<model-name>`                                                               |
| `invalid_request_error` при неизвестном поле                 | **Неверное семейство протокола**                                 | Переключитесь на шаблон полей для этой модели, используя сравнительные таблицы выше                          |
| `integer_below_min_value`                                    | Частота дискретизации входного сигнала ниже 24000 в Realtime GA  | Повторно выполните семплирование до 24 кГц или выше на клиенте                                               |
| `cannot_update_voice`                                        | Голос изменился после того, как сессия начала выдавать аудио     | Зафиксируйте голос в первом кадре; чтобы сменить его, откройте новую сессию                                  |
| `Error append image before append audio.`                    | Изображение было добавлено до какого-либо audio в Model Studio   | Сначала добавьте audio через `input_audio_buffer.append`, затем кадры изображения                            |
| WebSocket 1006 / 1011                                        | Нестабильность сети или разрыв на стороне upstream               | Повторно подключайтесь с экспоненциальной задержкой (1 s / 4 s / 16 s) и повторно отправьте `session.update` |
| Соединение обрывается примерно через 5 минут бездействия     | Лимит простоя Model Studio                                       | См. подход keepalive в разделе «Известные ограничения»                                                       |

<Info>
  Совет по устранению неполадок: записывайте `event_id` каждого события и `session.id` сессии и включайте их в сообщение о проблеме — это значительно сокращает время диагностики. Также обратите внимание, что **объекты ошибок Realtime GA содержат `code` и `param`** (то есть указывают точное имя поля и его допустимые значения), тогда как сообщения об ошибках Model Studio более общие. При отладке сначала проверяйте синтаксис поля именно в первом случае.
</Info>

## Частые вопросы

<AccordionGroup>
  <Accordion title="Почему на этой странице нет интерактивной песочницы?">
    Интерактивные песочницы основаны на спецификациях OpenAPI, которые описывают один запрос и один ответ по HTTP. Realtime — это десятки типов событий, идущих в обе стороны по одному долго живущему соединению, что не укладывается в эту модель. Альтернатива — текстовый smoke test в разделе «Начните с текста»: несколько десятков строк, без микрофона, и он подтверждает, что цепочка работает.
  </Accordion>

  <Accordion title="Могу ли я переключаться между четырьмя моделями, меняя только имя модели?">
    **Нет.** Эндпоинт и авторизация одинаковы, но поля запроса и имена событий относятся к двум протоколам. Как минимум вам нужно изменить: `modalities` ↔ `output_modalities`, `voice` ↔ `audio.output.voice`, `input_audio_format` ↔ `audio.input.format`, `turn_detection` ↔ `audio.input.turn_detection`, `input_audio_transcription` ↔ `audio.input.transcription`, а также имена событий `response.text.delta` ↔ `response.output_text.delta` и `response.audio.delta` ↔ `response.output_audio.delta`. См. раздел сравнения протоколов для полной сопоставительной таблицы.
  </Accordion>

  <Accordion title="Рукопожатие вообще не проходит. Как это отладить?">
    Проверьте пять вещей по порядку: 1. схема — `wss://`, а не `https://`; 2. эндпоинт включает `?model=<model-name>`; 3. заголовок `Authorization: Bearer <key>` присутствует; 4. ключ включён для beta-группы (503 с «no available channel», если нет); 5. промежуточный reverse proxy не удаляет заголовок `Upgrade` — это частая проблема при проксировании через собственный шлюз.
  </Accordion>

  <Accordion title="Могу ли я подключиться из браузера? Не утечёт ли мой ключ?">
    Технически да — эндпоинт принимает авторизацию через subprotocol `Sec-WebSocket-Protocol`, поэтому браузер `WebSocket` может подключиться напрямую. Но это **передаёт ваш ключ в браузер**, где любой посетитель может прочитать его на панели сети, поэтому это **подходит только для локальной проверки**. В production реализуйте backend relay: backend хранит ключ и открывает соединение с APIYI, а frontend взаимодействует только с вашим сервисом.
  </Accordion>

  <Accordion title="Отправка аудио 16 kHz в gpt-realtime-2.1 завершается ошибкой. Почему?">
    Протокол Realtime GA требует частоту дискретизации входа **не ниже 24000**; 16000 возвращает `integer_below_min_value`. Правильный формат — `"audio": {"input": {"format": {"type": "audio/pcm", "rate": 24000}}}`. Две модели Model Studio, напротив, требуют 16 kHz — они не взаимозаменяемы.
  </Accordion>

  <Accordion title="У меня нет микрофона / аудио трудно тестировать. Что делать?">
    Выполните трёхшаговую самопроверку в разделе «Начните с текста»: сначала проверьте цепочку только по тексту (без генерации audio tokens), затем воспроизведите локальный wav-файл, чтобы проверить audio pipeline, и только после этого подключайте живой микрофон. Тестовое аудио можно сгенерировать одной строкой с помощью встроенных средств macOS `say` и `afconvert` — команды приведены в том разделе.
  </Accordion>

  <Accordion title="После отправки response.cancel я никогда не получаю response.done.">
    Это известное поведение двух моделей Model Studio (воспроизведено во всех 6 тестовых прогонах): после прерывания вы получаете `response.text.done`, `response.content_part.done` и `response.output_item.done`, но `response.done` не доставляется. **Используйте `response.output_item.done` как сигнал завершения хода и добавьте timeout в качестве запасного варианта.** Сама сессия не затрагивается, и разговор продолжается нормально. Две модели Realtime GA ведут себя здесь корректно.
  </Accordion>

  <Accordion title="Соединение обрывается примерно через 5 минут.">
    Протокол Model Studio разрывает соединения после **300 секунд бездействия**, и **ping/pong на уровне WebSocket не считается активностью** — heartbeat не продлит этот таймер. Либо периодически отправляйте событие уровня приложения в период простоя (например, `session.update`), либо принимайте разрыв соединения и переподключайтесь автоматически. Не забудьте повторно отправить `session.update` и любой необходимый контекст после переподключения.
  </Accordion>

  <Accordion title="Как долго может оставаться открытой одна сессия?">
    В протоколе Realtime GA событие `session.created` содержит `expires_at`, которое измеряется примерно через 30 минут после подключения, после чего нужно переподключиться. В протоколе Model Studio основное ограничение, которое мы наблюдали, — разрыв соединения после 300 секунд простоя. Проектируйте длинные разговоры, исходя из того, что сессии истекают, и заранее продумайте, как контекст будет переноситься между сессиями.
  </Accordion>

  <Accordion title="Как задать voice и почему при его изменении возвращается cannot_update_voice?">
    Задавайте voice в `session.update`: поле верхнего уровня `voice` в Model Studio, `audio.output.voice` в Realtime GA. **Как только сессия выдала аудиовывод, voice больше нельзя изменить** — это относится к обоим протоколам и возвращает `cannot_update_voice`. Зафиксируйте его в первом frame и откройте новую сессию для переключения. Также не отправляйте пустую строку как voice в Model Studio; он возвращает 400.
  </Accordion>

  <Accordion title="В ручном режиме commit у меня нет input transcription.">
    Модель `flash` в Model Studio не доставляет событие завершения транскрипции в ручном режиме `commit` (стабильно воспроизведено во всех запусках); модель `plus` — доставляет, и обе работают в режиме VAD. **Переключитесь на `server_vad` или `semantic_vad`.** Тестирование показывает, что текст транскрипта в этом случае попадает в недокументированное поле в delta events, но это поле может измениться в любой момент, и на него не следует полагаться. Учтите, что это влияет только на отображение того, что сказал пользователь, в вашем UI — на разговор это не влияет, и модель правильно понимает аудио и отвечает на него.
  </Accordion>

  <Accordion title="Поддерживается ли image input? Почему я получаю Error append image before append audio.?">
    Все четыре модели поддерживают image input, но синтаксис отличается. В Realtime GA вы помещаете `input_image` непосредственно в сообщение. В Model Studio изображения рассматриваются как video frames, поэтому audio должен быть добавлен до любого image, и именно это вызывает данную ошибку. На практике рабочий подход — вставлять image frames в audio stream примерно по одному frame в секунду.
  </Accordion>

  <Accordion title="Есть ли prompt caching? Как подтвердить попадание в кэш?">
    Две модели Realtime GA поддерживают это, и это применяется автоматически — в тестах уже во втором ходе внутри сессии было попадание в кэш, со значением в `usage.input_token_details.cached_tokens`. На двух моделях Model Studio попаданий в кэш не наблюдалось.
  </Accordion>

  <Accordion title="Как оценить стоимость? Текст и аудио тарифицируются отдельно?">
    Объект `usage` в `response.done` показывает tokens по модальностям (text / audio / image, отдельно для input и output), поэтому стоимость можно отнести по модальностям. Уровни тарификации audio существенно выше, чем text, поэтому во время интеграции рекомендуется использовать только text. **За фактической тарификацией см. [журналы вызовов](https://api.apiyi.com/log).**
  </Accordion>
</AccordionGroup>

## Связанная документация

<CardGroup cols={2}>
  <Card title="Руководство по API" icon="book-open" href="/ru/api-manual">
    Создание ключей, базовый URL, режимы тарификации и другие общие соглашения.
  </Card>

  <Card title="Ключи и группы" icon="key-round" href="/ru/api-capabilities/token-management">
    Создавайте ключи, выбирайте группы и задавайте квоты.
  </Card>

  <Card title="Генерация текста" icon="file-text" href="/ru/api-capabilities/text-generation">
    Обычные chat-модели — лучший выбор для текстового общения.
  </Card>

  <Card title="Тарификация моделей" icon="table" href="/en/models">
    Актуальная тарификация, эндпоинты и группы для каждой модели на платформе.
  </Card>

  <Card title="Бонус за пополнение" icon="percent" href="/ru/faq/recharge-promotions">
    Ещё больше снижает вашу фактическую стоимость.
  </Card>

  <Card title="Запросить доступ к beta" icon="headphones" href="https://work.weixin.qq.com/kfid/kfc9adfd5810ece25ec">
    Свяжитесь с поддержкой WeCom, указав ваш аккаунт и ожидаемые параллельные запросы.
  </Card>
</CardGroup>

<Info>
  Realtime voice сейчас находится в **закрытой beta**. Каждый измеренный результат на этой странице датирован 2026-08-24 (UTC+8) и будет обновляться по мере изменений upstream. Если вы планируете интеграцию, столкнулись с тем, что эта страница не покрывает, или вам нужны более высокие параллельные запросы, свяжитесь с нами по адресу [hi@apiyi.com](mailto:hi@apiyi.com) / [feedback@apiyi.com](mailto:feedback@apiyi.com).
</Info>
