AGTCloud API Docs
가이드API 레퍼런스통합 및 리소스업데이트 및 도움말
실시간 상호작용

GPT-Live 실시간 음성

GPT-Live WebSocket으로 전이중 음성 상호작용을 수행하고 세션 이벤트를 처리합니다.

GPT-Live는 지속 WebSocket으로 전이중 음성 상호작용을 제공하며 동일한 세션에서 입력 오디오 전송, 음성 및 전사 결과 수신, 세션 구성 업데이트 및 다중 턴 문맥 유지를 할 수 있습니다. 현재 기록된 모델은 gpt-live-1이며 실제 사용 가능 범위는 현재 계정과 모델 카탈로그를 기준으로 합니다.

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

인증 및 연결

서버 애플리케이션은 WebSocket 핸드셰이크 Header에서 Bearer API 키를 사용합니다. 모델은 쿼리 매개변수에 넣지 않고 연결 후 첫 session.start 이벤트에서 구성합니다.

항목값
연결 주소wss://api.agtcloud.cn/v1/live/sessions
AuthorizationBearer your-api-key
모델 위치session.start.session.model
입력 오디오Base64로 인코딩한 원시 PCM 오디오 청크

장기 API 키는 신뢰할 수 있는 서버에만 보관합니다. 이 문서는 해당 WebSocket 라우트만 다루며 WebRTC, SIP, sideband, 세션 fork 또는 녹음 다운로드는 포함하지 않습니다.

세션 흐름

  1. Bearer API 키로 WebSocket 연결을 설정합니다.
  2. session.start를 보내 모델, 지침, 오디오 및 위임 방식을 구성합니다.
  3. session.started를 받은 후 오디오 또는 애플리케이션 명령 전송을 시작합니다.
  4. 입력 전사, 출력 전사, 출력 오디오 및 사용량 이벤트를 지속적으로 처리합니다.
  5. 오디오 생성을 중지한 뒤 session.close를 보냅니다.
  6. 이벤트를 계속 수신하고 session.closed의 최종 사용량으로 마무리합니다.

발화 턴 종료가 세션 종료를 뜻하지는 않습니다. 다중 턴 대화는 동일한 연결을 유지하고 업무 로직에서 전체 세션을 종료할 준비가 되었을 때만 session.close를 보냅니다.

시작 매개변수

필드유형설명
typestring첫 프레임은 항상 session.start입니다
event_idstring오류를 연결하는 선택적 클라이언트 이벤트 식별자
session.modelstring필수, GPT-Live 모델 ID
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
WSSGPT-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('사용자가 중지했습니다'));

이벤트 Reference

이 인터페이스는 session.input_audio.append로 Base64 오디오를 보내며 Muse의 바이너리 오디오 프레임 및 endStream이나 OpenAI Realtime의 input_audio_buffer.* 이벤트를 사용하지 않습니다.

클라이언트 이벤트 · 세션

session.start

GPT-Live 세션을 시작하고 모델, 지침, 오디오 및 위임 구성을 제출합니다.

필드유형필수설명예시 값
typestring예항상 session.start입니다.session.start
event_idstring아니요클라이언트 이벤트 식별자입니다.start_001
session.modelstring예GPT-Live 모델 ID입니다.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 모델, 도구, 권한 및 현재 사이트의 지원 범위를 확인합니다

참고 자료