qwen-audio-3.0-realtime-plus
Модель живого голосового диалога, которая принимает речь и текст и отвечает голосом или текстом. Рассчитана на естественный темп общения и взаимодействие с инструментами.
О модели
Описание раскрывает возможности модели. Доступные в Ailoot входные данные, результаты и параметры выбранного метода перечислены ниже.
Qwen-Audio-3.0-Realtime-Plus — модель Alibaba для живого голосового диалога. Она принимает речь и текст, отвечает аудио и текстом и поддерживает двусторонний разговор с естественным обменом репликами. Звук можно передавать и получать по частям, поэтому взаимодействие развивается в ходе разговора.
Когда собеседник закончил мысль
Предусмотрены несколько способов определять границы реплики: по звуковой активности, по смысловой завершённости и через явное управление. У производителя режим выбирают до передачи первого аудио; для его изменения после начала разговора требуется новое соединение. Поддержка этого управления определяется выбранным методом. Смысловой режим учитывает паузы и заполнители речи: краткое замешательство не обязательно означает, что пользователь завершил мысль. Новая речь пользователя может прервать формируемый ответ, чтобы разговор продолжился с учётом следующей реплики. В смысловом режиме можно ориентировать распознавание на речевой образец выбранного собеседника, чтобы меньше отвлекаться на чужие голоса и фоновый шум.
Инструкции определяют роль помощника и стиль общения. Голосовая подача может учитывать тон, темп и эмоцию; для версии 3.0 предусмотрены пять системных голосов и голоса по речевому образцу. Модель поддерживает 11 языков, включая русский, а также указанные производителем китайские диалектные варианты. У производителя голос задаётся при первом обновлении настроек сессии; последующая передача нового голоса в том же разговоре игнорируется. Эти настройки требуют поддержки выбранного способа обращения.
Инструменты и история разговора
Поддерживается вызов функций. Модель формирует нужное действие, подключённое приложение выполняет его и возвращает результат в диалог. Это позволяет строить разговор вокруг фактических данных и продолжать его после выполненной операции. Голосовой помощник может опираться на результаты инструментов, сохраняя контекст беседы. Возможности производителя также включают встроенный веб-поиск, если его предоставляет выбранный метод. Веб-поиск и вызовы функций нельзя одновременно включить в одной сессии.
По умолчанию история содержит 20 ходов разговора, предусмотрен диапазон от одного до 50. Для накопленного аудио установлен предел 300 секунд; старые части истории удаляются при превышении ограничений. Это отличается от длительности отдельной реплики: история определяет, какой предыдущий материал остаётся доступным при продолжении диалога.
Текстовый контекст и звук
Формальный контекст составляет 40 960 токенов. Предельная длина входа — 16 384 токена, выхода — 8192; те же пределы входа и выхода указаны для режима рассуждения, с отдельным пределом цепочки рассуждения 1024 токена.
Для потокового звука предусмотрен одноканальный PCM с 16-битным представлением: на входе — 16 кГц, на выходе — 24 кГц. Текстовые и аудиоданные относятся к одному разговору. Визуальный вход и выдача изображений для этой конкретной модели не заявлены; её специализация — речь, текст и голосовое взаимодействие.
Ailoot · Голос в реальном времени GET /v1/realtime?model=qwen-audio-3.0-realtime-plus qwen-audio-3.0-realtime-plus
qwen-audio-3.0-realtime-plusНачало
Модель живого голосового диалога, которая принимает речь и текст и отвечает голосом или текстом. Рассчитана на естественный темп общения и взаимодействие с инструментами.
- Допустимые входные данные
- Текст · Аудио
- Результаты
- Текст · Аудио
Доступно в Ailoot · входные данные и методы
Ailoot · Голос в реальном времени GET /v1/realtime?model=qwen-audio-3.0-realtime-plus
- Входные данные
text,audio- Результат
text,audio- Выходной формат
- события сессии
| Поле | Тип | Требование | Значения и ограничения | По умолчанию |
|---|---|---|---|---|
Полеquery.model | Типstring | ТребованиеОбязательно | Значения и ограничения qwen-audio-3.0-realtime-plus | По умолчанию— |
Полеsession.input_audio_format | Типstring | ТребованиеНеобязательно | Значения и ограничения pcm16, pcm | По умолчанию— |
Полеsession.output_audio_format | Типstring | ТребованиеНеобязательно | Значения и ограничения pcm16, pcm | По умолчанию— |
Полеsession.instructions | Типstring | ТребованиеНеобязательно | Значения и ограничения Объект ограничения: выбранная политика сессии | По умолчанию— |
Полеsession.max_history_turns | Типinteger | ТребованиеНеобязательно | Значения и ограничения Минимум: 1 Объект ограничения: выбранная политика сессии | По умолчанию— |
Полеinput_audio_buffer.append.audio | Типbase64_string | ТребованиеОбязательно | Значения и ограничения Входное содержимое: PCM16 моно; вход 16 kHz, выход 24 kHz; непустые данные с чётным количеством байтов | По умолчанию— |
Сочетания входных материалов
realtime_text_audio
Параметры и жизненный цикл метода
| Параметр | Контракт |
|---|---|
Параметрmaterial_types | Контрактtext, audio |
Параметрrequired | Контрактnull |
Параметрoptional_controls | Контрактnull |
Параметрcontent_type | КонтрактWebSocket |
Жизненный цикл: exact retained published method
Структуры методов и ID указаны точно; возможности поставщика рассматриваются отдельно от допуска.
Тарифы
Ставки методов Ailoot
GET /v1/realtime?model=qwen-audio-3.0-realtime-plus- Вход
- 0,0008 $ / 1K
- Выход
- 0,0064 $ / 1K
- Вход
- 0,0048 $ за минуту аудио
- Выход
- 0,018 $ за минуту выходного аудио
Ailoot резервирует расчётную сумму перед запросом. После получения результата списывает итоговую плату по измеренному расходу. Для окончательного списания за потоковый ответ используется итоговая запись расхода. Как работают резервирование и расчёт →
Примеры запросов
Ailoot · Голос в реальном времени
# Install the documented client once: python3 -m pip install websockets
import base64, json, os, time, wave
from pathlib import Path
from websockets.sync.client import connect
key = os.environ.get("AILOOT_API_KEY", "")
if not key.strip():
raise SystemExit("Set your AILOOT_API_KEY before connecting")
file = Path(os.environ.get("AILOOT_TEXT_FILE", ""))
if not file.is_file(): raise SystemExit("Set AILOOT_TEXT_FILE to your actual UTF-8 material")
material = file.read_text(encoding="utf-8")
if not material.strip(): raise SystemExit("Text material must not be empty")
out = Path("realtime-results")
out.mkdir(exist_ok=True)
audio, texts, complete = {}, {}, []
current_response = None
pending = False
deadline = time.monotonic() + 570 # Local client bound; lower server/session limits still apply.
def receive(ws, log):
global current_response, pending
left = deadline - time.monotonic()
if left <= 0:
raise TimeoutError("Client session limit; preserve IDs and partial results, do not replay")
event = json.loads(ws.recv(timeout=min(60, left)))
log.write(json.dumps(event, ensure_ascii=False) + "\n")
log.flush()
kind = event.get("type")
if kind == "error":
raise RuntimeError(json.dumps(event, ensure_ascii=False))
if kind == "response.created":
current_response = event["response"]["id"]
if kind in {"response.audio.delta", "response.output_audio.delta", "response.text.delta", "response.output_text.delta", "response.audio_transcript.delta", "response.output_audio_transcript.delta", "response.text.done", "response.audio_transcript.done"}:
identity = (event["response_id"], event["item_id"], event["output_index"], event["content_index"])
if kind in {"response.audio.delta", "response.output_audio.delta"}:
raw = base64.b64decode(event["delta"], validate=True)
if len(raw) % 2: raise ValueError("Output PCM16 chunk must have an even byte length")
audio.setdefault(identity, bytearray()).extend(raw)
elif kind.endswith(".done"):
texts[identity] = event.get("text", event.get("transcript", ""))
else:
texts[identity] = texts.get(identity, "") + event["delta"]
print(event["delta"], end="", flush=True)
if kind == "response.done":
response = event["response"]
complete.append(response)
if current_response is None: current_response = response["id"]
if response["id"] == current_response: pending = False
return event
def wait_for(ws, log, kind):
while True:
event = receive(ws, log)
if event.get("type") == kind: return event
def send(ws, event):
ws.send(json.dumps(event, ensure_ascii=False))
try:
# One exact model query; no reconnect/retry loop and no provider credentials.
with connect("wss://api.ailoot.ai/v1/realtime?model=qwen-audio-3.0-realtime-plus", additional_headers={"Authorization": "Bearer " + key}) as ws:
with (out / "events.jsonl").open("w", encoding="utf-8") as log:
wait_for(ws, log, "session.created")
send(ws, {"type":"session.update", "session":{"modalities":["audio","text"], "input_audio_format":"pcm", "output_audio_format":"pcm", "turn_detection":None}})
wait_for(ws, log, "session.updated")
send(ws, {"type":"conversation.item.create", "item":{"type":"message", "role":"user", "content":[{"type":"input_text", "text":material}]}})
send(ws, {"type":"response.create"})
pending = True
try:
while pending: receive(ws, log)
except KeyboardInterrupt:
# Cancel the actual current inference, then preserve its returned status.
send(ws, {"type":"response.cancel"})
while pending: receive(ws, log)
if not complete or complete[-1].get("status") != "completed":
raise RuntimeError("Response did not complete normally; inspect saved events and status")
finally:
manifest = {"responses":complete, "parts":[]}
for number, identity in enumerate(dict.fromkeys(list(audio) + list(texts))):
record = {"response_id":identity[0], "item_id":identity[1], "output_index":identity[2], "content_index":identity[3]}
if identity in audio:
raw = bytes(audio[identity])
pcm_file = out / ("audio-" + str(number) + ".pcm")
wav_file = out / ("audio-" + str(number) + ".wav")
pcm_file.write_bytes(raw)
with wave.open(str(wav_file), "wb") as wav:
wav.setnchannels(1); wav.setsampwidth(2); wav.setframerate(24000); wav.writeframes(raw)
record.update({"pcm":str(pcm_file), "wav":str(wav_file), "sample_rate_hz":24000})
if identity in texts:
text_file = out / ("text-" + str(number) + ".txt")
text_file.write_text(texts[identity], encoding="utf-8")
record["text"] = str(text_file)
manifest["parts"].append(record)
(out / "results.json").write_text(json.dumps(manifest, ensure_ascii=False, indent=2), encoding="utf-8")
print("\nSaved every received event and result in", out)
# Install the documented client once: python3 -m pip install websockets
import base64, json, os, time, wave
from pathlib import Path
from websockets.sync.client import connect
key = os.environ.get("AILOOT_API_KEY", "")
if not key.strip():
raise SystemExit("Set your AILOOT_API_KEY before connecting")
file = Path(os.environ.get("AILOOT_AUDIO_FILE", ""))
if not file.is_file(): raise SystemExit("Set AILOOT_AUDIO_FILE to your raw PCM16 mono 16-kHz recording")
material = file.read_bytes()
if not material or len(material) % 2 or material.startswith(b"RIFF"):
raise SystemExit("Use nonempty signed little-endian PCM16 mono 16-kHz bytes, not a WAV header")
out = Path("realtime-results")
out.mkdir(exist_ok=True)
audio, texts, complete = {}, {}, []
current_response = None
pending = False
deadline = time.monotonic() + 570 # Local client bound; lower server/session limits still apply.
def receive(ws, log):
global current_response, pending
left = deadline - time.monotonic()
if left <= 0:
raise TimeoutError("Client session limit; preserve IDs and partial results, do not replay")
event = json.loads(ws.recv(timeout=min(60, left)))
log.write(json.dumps(event, ensure_ascii=False) + "\n")
log.flush()
kind = event.get("type")
if kind == "error":
raise RuntimeError(json.dumps(event, ensure_ascii=False))
if kind == "response.created":
current_response = event["response"]["id"]
if kind in {"response.audio.delta", "response.output_audio.delta", "response.text.delta", "response.output_text.delta", "response.audio_transcript.delta", "response.output_audio_transcript.delta", "response.text.done", "response.audio_transcript.done"}:
identity = (event["response_id"], event["item_id"], event["output_index"], event["content_index"])
if kind in {"response.audio.delta", "response.output_audio.delta"}:
raw = base64.b64decode(event["delta"], validate=True)
if len(raw) % 2: raise ValueError("Output PCM16 chunk must have an even byte length")
audio.setdefault(identity, bytearray()).extend(raw)
elif kind.endswith(".done"):
texts[identity] = event.get("text", event.get("transcript", ""))
else:
texts[identity] = texts.get(identity, "") + event["delta"]
print(event["delta"], end="", flush=True)
if kind == "response.done":
response = event["response"]
complete.append(response)
if current_response is None: current_response = response["id"]
if response["id"] == current_response: pending = False
return event
def wait_for(ws, log, kind):
while True:
event = receive(ws, log)
if event.get("type") == kind: return event
def send(ws, event):
ws.send(json.dumps(event, ensure_ascii=False))
try:
# One exact model query; no reconnect/retry loop and no provider credentials.
with connect("wss://api.ailoot.ai/v1/realtime?model=qwen-audio-3.0-realtime-plus", additional_headers={"Authorization": "Bearer " + key}) as ws:
with (out / "events.jsonl").open("w", encoding="utf-8") as log:
wait_for(ws, log, "session.created")
send(ws, {"type":"session.update", "session":{"modalities":["audio","text"], "input_audio_format":"pcm", "output_audio_format":"pcm", "turn_detection":None}})
wait_for(ws, log, "session.updated")
for offset in range(0, len(material), 640):
send(ws, {"type":"input_audio_buffer.append", "audio":base64.b64encode(material[offset:offset+640]).decode("ascii")})
time.sleep(0.02)
send(ws, {"type":"input_audio_buffer.commit"})
wait_for(ws, log, "input_audio_buffer.committed")
send(ws, {"type":"response.create"})
pending = True
try:
while pending: receive(ws, log)
except KeyboardInterrupt:
# Cancel the actual current inference, then preserve its returned status.
send(ws, {"type":"response.cancel"})
while pending: receive(ws, log)
if not complete or complete[-1].get("status") != "completed":
raise RuntimeError("Response did not complete normally; inspect saved events and status")
finally:
manifest = {"responses":complete, "parts":[]}
for number, identity in enumerate(dict.fromkeys(list(audio) + list(texts))):
record = {"response_id":identity[0], "item_id":identity[1], "output_index":identity[2], "content_index":identity[3]}
if identity in audio:
raw = bytes(audio[identity])
pcm_file = out / ("audio-" + str(number) + ".pcm")
wav_file = out / ("audio-" + str(number) + ".wav")
pcm_file.write_bytes(raw)
with wave.open(str(wav_file), "wb") as wav:
wav.setnchannels(1); wav.setsampwidth(2); wav.setframerate(24000); wav.writeframes(raw)
record.update({"pcm":str(pcm_file), "wav":str(wav_file), "sample_rate_hz":24000})
if identity in texts:
text_file = out / ("text-" + str(number) + ".txt")
text_file.write_text(texts[identity], encoding="utf-8")
record["text"] = str(text_file)
manifest["parts"].append(record)
(out / "results.json").write_text(json.dumps(manifest, ensure_ascii=False, indent=2), encoding="utf-8")
print("\nSaved every received event and result in", out)
Получить и сохранить результаты
Ailoot · Голос в реальном времени
события сессии
Сохраняйте все готовые результаты
Сохраняйте каждый полностью полученный ответ, файл и элемент содержимого.
Соберите каждый возвращённый фрагмент аудио и текстовый результат или событие для каждого фактического ID ответа.
Если нужно проверить исходный ответ
Сохраняйте фактические ID сессии, ответа, событий и подтверждающие записи.
Ошибки и дальнейшие шаги
| Ответ | Условие | Следующий шаг |
|---|---|---|
Ответ426 · invalid_request_error | УсловиеБез переключения протокола на WebSocket | Следующий шагИспользуйте клиент WebSocket с Upgrade: websocket. |
Псевдонимы
Псевдонимы моделей
Ailoot qwen-audio-3.0-realtime-plus
Псевдонимы путей метода
Используйте пути методов, указанные выше.
Связанные ID для вызова
FAQ · qwen-audio-3.0-realtime-plus
- Какие входные данные и результаты доступны?
GET /v1/realtime?model=qwen-audio-3.0-realtime-plus·qwen-audio-3.0-realtime-plus:text,audio→text,audio- Какие ограничения действуют?
GET /v1/realtime?model=qwen-audio-3.0-realtime-plus·qwen-audio-3.0-realtime-plus:session.max_history_turns.minimum:1