Голосовой диалог (Realtime)
Как подключиться к realtime-модели по WebSocket, отправить запрос и получить текст или аудио в ответ.
Realtime-модель держит открытое соединение и отвечает по мере того, как вы говорите или пишете. Это основа для голосовых помощников, синхронного перевода и звонков с ботом.
Как это устроено
- Клиент открывает WebSocket на
wss://api.zveno.ai/v1/realtimeи называет модель в адресе. - Клиент настраивает сессию событием
session.update: что вернуть — текст или аудио. - Клиент отправляет сообщение и просит ответ событием
response.create. - Сервер присылает ответ частями:
response.output_text.deltaдля текста,response.output_audio.deltaдля аудио.
ZvenoAI говорит на протоколе OpenAI Realtime. Клиент, написанный для него, работает без правок: поменяйте адрес, ключ и название модели.
Из браузера подключиться нельзя: ключ передаётся в заголовке Authorization, а браузерный
WebSocket заголовки задавать не умеет. Открывайте соединение на своём сервере или в приложении.
Подключение
| Параметр | Значение |
|---|---|
| Адрес | wss://api.zveno.ai/v1/realtime?model=<модель> |
| Подпротокол | realtime |
| Авторизация | заголовок Authorization: Bearer <ZVENOAI_API_KEY> |
| Формат аудио | audio/pcm: PCM16, моно, 24 кГц |
Почта аккаунта должна быть подтверждена. Какие модели работают в этом режиме, смотрите в каталоге моделей.
Быстрый старт
Пример отправляет текстовый вопрос и печатает ответ по мере поступления.
import asyncio
import json
import websockets
URL = "wss://api.zveno.ai/v1/realtime?model=qwen/qwen-audio-3.1-realtime-plus"
async def main():
async with websockets.connect(
URL,
subprotocols=["realtime"],
additional_headers={"Authorization": "Bearer <ZVENOAI_API_KEY>"},
) as ws:
await ws.send(json.dumps({
"type": "session.update",
"session": {"type": "realtime", "output_modalities": ["text"]},
}))
await ws.send(json.dumps({
"type": "conversation.item.create",
"item": {
"type": "message",
"role": "user",
"content": [{"type": "input_text", "text": "Привет! Расскажи о себе."}],
},
}))
await ws.send(json.dumps({"type": "response.create"}))
async for message in ws:
event = json.loads(message)
if event["type"] == "response.output_text.delta":
print(event["delta"], end="", flush=True)
if event["type"] in ("response.done", "error"):
break
asyncio.run(main())import WebSocket from 'ws'
const url = 'wss://api.zveno.ai/v1/realtime?model=qwen/qwen-audio-3.1-realtime-plus'
const ws = new WebSocket(url, 'realtime', {
headers: { Authorization: 'Bearer <ZVENOAI_API_KEY>' },
})
ws.on('open', () => {
ws.send(JSON.stringify({
type: 'session.update',
session: { type: 'realtime', output_modalities: ['text'] },
}))
ws.send(JSON.stringify({
type: 'conversation.item.create',
item: {
type: 'message',
role: 'user',
content: [{ type: 'input_text', text: 'Привет! Расскажи о себе.' }],
},
}))
ws.send(JSON.stringify({ type: 'response.create' }))
})
ws.on('message', (data) => {
const event = JSON.parse(data.toString())
if (event.type === 'response.output_text.delta') process.stdout.write(event.delta)
if (event.type === 'response.done' || event.type === 'error') ws.close()
})Настройка сессии
Событие session.update задаёт поведение модели на всё соединение. ZvenoAI читает эти поля:
| Поле | Что задаёт |
|---|---|
instructions | Системная инструкция для модели |
output_modalities | Что вернуть: ["text"] или ["audio"] |
tools | Инструменты, которые модель может вызвать |
audio.input.format | Формат входящего аудио |
audio.input.transcription.language | Язык распознавания речи |
audio.input.turn_detection | Как модель понимает, что вы договорили |
audio.output.format | Формат аудио в ответе |
audio.output.voice | Голос ответа |
Сессия с аудио в ответе:
{
"type": "session.update",
"session": {
"type": "realtime",
"output_modalities": ["audio"],
"audio": {
"input": { "format": { "type": "audio/pcm", "rate": 24000 } },
"output": { "format": { "type": "audio/pcm", "rate": 24000 } }
}
}
}Формат аудио один: audio/pcm, PCM16 little-endian, моно, 24 кГц. Поле rate сервер не читает
и частоту не проверяет: аудио с другой частотой он примет, но исказит. Форматы pcmu и pcma
сервер отклоняет событием error.
Голос ответа задаётся в audio.output.voice. Список голосов смотрите в документации провайдера
модели.
Отправка аудио
Аудио с микрофона уходит частями в событиях input_audio_buffer.append. Поле audio — base64
от PCM16 little-endian, моно, 24 кГц.
{ "type": "input_audio_buffer.append", "audio": "<base64 PCM16>" }Кто решает, что реплика закончена, задаёт поле audio.input.turn_detection:
| Значение | Поведение |
|---|---|
null | Вручную: отправьте input_audio_buffer.commit и response.create |
{ "type": "semantic_vad" } | Модель сама определяет конец реплики и отвечает |
Событие input_audio_buffer.clear сбрасывает накопленное аудио.
Инструменты
Передайте функции в session.tools в том же виде, что в OpenAI Realtime:
{
"type": "session.update",
"session": {
"type": "realtime",
"tools": [
{
"type": "function",
"name": "get_weather",
"description": "Погода в городе",
"parameters": {
"type": "object",
"properties": { "city": { "type": "string" } },
"required": ["city"]
}
}
]
}
}События ответа
| Событие | Когда приходит |
|---|---|
response.created | Модель начала отвечать |
conversation.item.added | Сообщение добавлено в диалог |
response.output_text.delta | Очередная часть текста, поле delta |
response.output_text.done | Текст ответа закончен |
response.output_audio.delta | Очередная часть аудио в base64, поле delta |
response.output_audio_transcript.delta | Очередная часть расшифровки аудиоответа |
response.done | Ответ закончен целиком |
error | Ошибка, см. раздел ниже |
Тарификация
- Деньги списываются с баланса за каждый ответ модели отдельно: от
response.createdдоresponse.done. - Если средств не хватает, сервер отменяет ответ и закрывает соединение с кодом
1008. - Подписка на realtime-модели не действует: они работают только с оплатой по факту.
- Текстовые и аудиотокены стоят по-разному, аудио дороже. Цены модели — на её странице в каталоге.
Обработка ошибок
До установки соединения сервер отвечает обычной HTTP-ошибкой в формате JSON:
| Код | Причина |
|---|---|
400 | Нет параметра model, модель не realtime или запрос не WebSocket |
401 | Нет ключа или ключ неверный |
402 | Не хватает средств на балансе |
404 | Модель не найдена |
502 | Провайдер модели не ответил |
503 | Сервис занят или перезапускается, повторите позже |
После установки соединения сервер присылает событие error и закрывает соединение с кодом:
| Код закрытия | Причина |
|---|---|
1008 | Закончились средства, ключ отозван или исчерпан бюджет ключа |
1011 | Сбой на стороне провайдера модели |
1001 | Сервер перезапускается, откройте соединение заново |
Формат ошибок и общие правила повторов описаны в руководстве «Обработка ошибок».
Решение типичных проблем
Что дальше
Видеогенерация
Асинхронный API для генерации видео по тексту и изображениям через единую точку входа ZvenoAI с автоматическим выбором провайдера и оплатой в рублях.
Веб-чат
Полное руководство по веб-чату ZvenoAI — выбор модели, настройка параметров, вложения, веб-поиск и Web App режим. Начните без кода прямо в браузере.