AGTCloud API Docs
РуководстваСправочник APIИнтеграции и ресурсыОбновления и помощь
Взаимодействие в реальном времени

GPT-Live: речь в реальном времени

Ведите полнодуплексное голосовое взаимодействие через GPT-Live WebSocket и обрабатывайте события сеанса.

GPT-Live обеспечивает полнодуплексное голосовое взаимодействие через постоянное соединение WebSocket. В одном сеансе можно отправлять входное аудио, получать речь и транскрипции, обновлять настройки и сохранять контекст нескольких ходов. Сейчас зарегистрирована модель gpt-live-1; фактическая доступность определяется текущим аккаунтом и каталогом моделей.

WSS
wss://api.agtcloud.cn/v1/live/sessions

Аутентификация и подключение

Серверное приложение передаёт Bearer-ключ API в Header рукопожатия WebSocket. Модель задаётся не в параметрах запроса URL, а в первом событии session.start после подключения.

ПараметрЗначение
Адрес подключенияwss://api.agtcloud.cn/v1/live/sessions
AuthorizationBearer your-api-key
Расположение моделиsession.start.session.model
Входное аудиоБлоки необёрнутого PCM-аудио в кодировке Base64

Долгосрочный ключ API должен храниться только на доверенном сервере. Этот документ описывает только указанный маршрут WebSocket, без WebRTC, SIP, sideband, fork сеанса или загрузки записей.

Процесс сеанса

  1. Установите соединение WebSocket с Bearer-ключом API.
  2. Отправьте session.start с настройками модели, инструкций, аудио и делегирования.
  3. После получения session.started начните отправлять аудио или команды приложения.
  4. Непрерывно обрабатывайте входные и выходные транскрипции, выходное аудио и события использования.
  5. Остановите создание аудио и отправьте session.close.
  6. Продолжайте получать события; завершите обработку по окончательному использованию в session.closed.

Окончание речевого хода не означает окончания сеанса. Для многоходового диалога сохраняйте одно соединение и отправляйте session.close только тогда, когда приложение готово завершить весь сеанс.

Параметры запуска

ПолеТипОписание
typestringПервый кадр всегда session.start
event_idstringНеобязательный идентификатор клиентского события для связи с ошибками
session.modelstringОбязательный ID модели GPT-Live
session.instructionsstringНеобязательные инструкции голосового ассистента
session.audio.formatobjectФормат аудио, например audio/pcm, 24 kHz
session.audio.output.voicestringВыходной голос, например marin; допустимые значения зависят от модели
session.delegationobjectСпособ делегирования: используйте client или responses

После запуска соединения можно обновлять инструкции и другие изменяемые настройки через session.update, ожидая подтверждения session.updated. Не отправляйте session.start повторно по тому же соединению и не меняйте голосовую модель верхнего уровня через событие обновления.

Подготовка аудио и примеры вызова

В примере используется аудио PCM signed 16-bit little-endian, 24 kHz, моно, без заголовка WAV. Каждые 100 ms содержат 4800 байтов; перед отправкой каждый аудиоблок кодируется в Base64.

# Подготовьте входное аудио
ffmpeg -i input.wav -f s16le -acodec pcm_s16le -ac 1 -ar 24000 audio.pcm

# Воспроизведите сохранённое выходное аудио
ffplay -f s16le -ar 24000 -ac 1 reply.pcm

Для примера Node.js требуется ws; пример Python проверяет только запуск и закрытие сеанса, не отправляя аудио.

npm install ws
python -m pip install websocket-client
WSSПримеры сеанса GPT-Live
import fs from 'node:fs';
import WebSocket from 'ws';

const key = process.env.API_KEY;
const file = process.argv[2];
if (!key || !file) throw new Error('Требуются API_KEY и путь к файлу PCM');

const pcm = fs.readFileSync(file);
if (!pcm.length || pcm.length % 2) {
  throw new Error('PCM должен быть непустым и выровненным по 16-битным сэмплам');
}

const ws = new WebSocket('wss://api.agtcloud.cn/v1/live/sessions', {
  headers: { Authorization: 'Bearer ' + key },
  handshakeTimeout: 10000,
});
const audioOut = fs.createWriteStream('reply.pcm');
let started = false;
let closing = false;
let finalized = false;
let sender;
let drain;
let offset = 0;
let inputFinishedAt = 0;
let lastOutputAt = 0;

function send(event) {
  ws.send(JSON.stringify(event));
}

function closeSession(reason) {
  if (closing) return;
  closing = true;
  clearInterval(sender);
  console.error('Запрос завершения:', reason);
  if (started && ws.readyState === WebSocket.OPEN) {
    send({ type: 'session.close' });
    drain = setTimeout(() => ws.terminate(), 15000);
  } else {
    ws.terminate();
  }
}

const limit = setTimeout(() => closeSession('Ограничение примера: 60 секунд'), 60000);

ws.on('open', () => send({
  type: 'session.start',
  event_id: 'start_001',
  session: {
    model: 'gpt-live-1',
    instructions: 'Отвечайте кратко на китайском языке и дождитесь, пока пользователь заговорит первым.',
    audio: {
      format: { type: 'audio/pcm', rate: 24000 },
      output: { voice: 'marin' },
    },
    delegation: { type: 'client' },
  },
}));

ws.on('message', (raw) => {
  let event;
  try {
    event = JSON.parse(raw.toString());
  } catch {
    closeSession('Недействительный JSON');
    return;
  }

  if (event.type !== 'session.output_audio.delta') {
    console.log(raw.toString());
  }
  if (event.type === 'session.started' && !started) {
    started = true;
    sender = setInterval(() => {
      if (closing || ws.readyState !== WebSocket.OPEN) return;
      if (offset >= pcm.length) {
        inputFinishedAt ||= Date.now();
        if (
          lastOutputAt &&
          Date.now() - Math.max(lastOutputAt, inputFinishedAt) >= 3000
        ) {
          closeSession('Вывод завершён');
          return;
        }
        send({
          type: 'session.input_audio.append',
          audio: Buffer.alloc(4800).toString('base64'),
        });
        return;
      }
      const chunk = pcm.subarray(offset, offset + 4800);
      offset += chunk.length;
      send({
        type: 'session.input_audio.append',
        audio: chunk.toString('base64'),
      });
    }, 100);
  }
  if (event.type === 'session.output_audio.delta') {
    lastOutputAt = Date.now();
    audioOut.write(Buffer.from(event.delta, 'base64'));
  }
  if (event.type === 'session.output_transcript.delta') {
    lastOutputAt = Date.now();
  }
  if (event.type === 'session.closed') {
    finalized = true;
    ws.close(1000);
  }
  if (event.type === 'error') closeSession('Ошибка сервиса');
});

ws.on('error', (error) => {
  console.error(error.message);
  closeSession('Ошибка подключения');
});

ws.on('close', (code, reason) => {
  clearTimeout(limit);
  clearTimeout(drain);
  clearInterval(sender);
  audioOut.end();
  console.error({ code, reason: reason.toString(), finalized });
  if (!finalized || code !== 1000) process.exitCode = 1;
});

process.on('SIGINT', () => closeSession('Остановлено пользователем'));

Справочник событий

Этот интерфейс отправляет аудио Base64 через session.input_audio.append. Он не использует двоичные аудиокадры Muse, endStream или события OpenAI Realtime input_audio_buffer.*.

События клиента · Сеанс

session.start

Запускает сеанс GPT-Live и отправляет настройки модели, инструкций, аудио и делегирования.

ПолеТипОбязательноОписаниеПример
typestringДаВсегда session.start.session.start
event_idstringНетИдентификатор клиентского события.start_001
session.modelstringДаID модели GPT-Live.gpt-live-1
session.instructionsstringНетИнструкции диалога для голосового ассистента.Отвечайте кратко на китайском языке.
session.audio.format.typestringНетТип формата аудио.audio/pcm
session.audio.format.rateintegerНетЧастота дискретизации аудио.24000
session.audio.output.voicestringНетВыходной голос.marin
session.delegation.typestringДаСпособ делегирования.client
Пример JSON
{
  "type": "session.start",
  "event_id": "start_001",
  "session": {
    "model": "gpt-live-1",
    "instructions": "Отвечайте кратко на китайском языке.",
    "audio": {
      "format": {
        "type": "audio/pcm",
        "rate": 24000
      },
      "output": {
        "voice": "marin"
      }
    },
    "delegation": {
      "type": "client"
    }
  }
}

Использование и тарификация

session.usage.updated возвращает накопленное значение текущего сеанса, а не новое использование отдельного хода. Клиент не должен суммировать значения разных этапов; при нормальном закрытии окончательную длительность определяет session.closed.usage.seconds.

Стоимость может включать длительность голосового сеанса, использование фоновой модели и вызовы инструментов. Точные цены и доступность моделей определяются Каталогом моделей.

Способ делегирования

  • client: Приложение само обрабатывает делегированные задачи; подходит для первоначальной проверки базового голосового сеанса.
  • responses: Сервер выполняет настроенные модель Responses и инструменты. Целевая модель, инструменты и разрешения должны быть доступны на текущем сайте.

При использовании responses стоимость моделей и инструментов фоновых задач может начисляться отдельно. Не считайте MODEL_ID в примере конкретной гарантированно доступной моделью.

Обработка ошибок

СимптомПроверка и действия
Рукопожатие возвращает 401Проверьте Bearer-ключ API, права на модель и адрес текущего сайта
Маршрут возвращает 404Убедитесь, что итоговый путь — /v1/live/sessions, без повторного добавления /v1
Ошибка первого кадра или тайм-аут запускаПосле подключения сначала отправьте session.start; отправляйте аудио только после session.started
Нет звука или звук искажёнПроверьте разрядность PCM, порядок байтов, частоту дискретизации, каналы и заголовок WAV
Не получено session.closedСохраните последнее накопленное использование, событие ошибки и причину закрытия; не воспроизводите автоматически всю аудиозапись повторно
Фоновое делегирование отклоненоПроверьте модель Responses, инструменты, разрешения и поддержку на текущем сайте

Справочные материалы