소개
실시간 대화는 WebSocket(클라이언트와 서버 사이에 장시간 연결을 유지하며 양방향으로 데이터를 푸시할 수 있는 프로토콜)으로 지속 연결을 맺어, 오디오 또는 텍스트 입력을 실시간으로 대화 모델에 전달하고 모델이 텍스트와 음성 답변을 증분 방식으로 푸시합니다. 음성 비서, 실시간 질의응답, 회화 연습 등 주고받는 상호작용이 필요한 시나리오에 적합합니다. 실시간 음성 전사와 마찬가지로 WebSocket을 사용하지만 용도가 다릅니다:
사용 가능한 모델:
- gpt-realtime-2.1: 음성 대화 모델이며 오디오와 텍스트 입력을 지원하고 텍스트와 음성 답변을 실시간으로 출력합니다.
빠른 시작
연결 엔드포인트
model=gpt-realtime-2.1: 필수, 모델은 연결 시 URL 파라미터로 고정되며 세션 중에는 변경할 수 없습니다(아래 제약 참조).- 전사와의 차이에 주의: 대화 엔드포인트에는
intent=transcription을 붙이지 않습니다.
인증
핸드셰이크 시 표준 HTTP 헤더로 API 키를 전달합니다:오디오 형식 요구사항
현재 오디오 입력과 출력은 각각 한 가지 형식만 지원합니다. 전송 전에 오디오를 다음으로 변환하십시오:- 인코딩: PCM16(16비트 부호 있는 정수, 리틀 엔디언)
- 샘플 레이트: 24000 Hz
- 채널: 모노(mono)
audio/pcm@24000입니다. 입력 또는 출력에서 다른 형식(예: G.711/µ-law)을 선언하면 거부되고 세션이 닫힙니다.
전사와 달리 대화 세션은 음성 활동 감지(turn_detection / VAD)를 지원합니다. 활성화하면 모델이 발화의 끝을 자동으로 판단해 답변을 트리거하고, 비활성화(
null 설정)하면 오디오 커밋과 답변 요청 시점을 직접 제어합니다. 필요에 따라 선택하십시오.세션 구성(session.update)
연결이 맺어진 뒤 클라이언트는session.update 한 프레임을 보내 대화 파라미터(음성 톤, 시스템 지시, VAD 활성화 여부 등)를 구성할 수 있습니다. 대화 세션의 모델은 연결 URL로 이미 고정되어 있으므로 session.update를 보내지 않아도 바로 대화할 수 있습니다. 음색이나 지시를 사용자 지정할 때 보내십시오.
구성 파라미터
string
필수
세션 유형이며 대화 시나리오에서는
realtime입니다.string
시스템 지시로, 모델의 역할과 어조, 답변 제약을 설정합니다.
string[]
출력 모달리티이며
["audio"] 또는 ["text"]입니다. ["audio"](기본값)이면 모델이 음성을 출력하고 답변 텍스트는 response.output_audio_transcript.delta 이벤트로 전달됩니다. ["text"]이면 텍스트만 출력하고 텍스트는 response.output_text.delta 이벤트로 전달됩니다. 그 밖의 조합(예: ["audio", "text"])은 거부되고 error 이벤트가 반환됩니다.object
필수
입력 오디오 형식이며
{ "type": "audio/pcm", "rate": 24000 }로 고정됩니다.object | null
음성 활동 감지입니다.
{ "type": "server_vad" }를 전달하면 자동 턴 감지를 켜고, null을 전달하면 꺼서 클라이언트가 직접 커밋과 답변 요청을 수행합니다.object
필수
출력 오디오 형식이며
{ "type": "audio/pcm", "rate": 24000 }로 고정됩니다.string
답변 음성의 음색입니다. 첫 답변이 시작된 뒤에는 변경할 수 없습니다: 세션이 생성 상태에 들어간 뒤 다시 보낸
voice는 무시됩니다(나머지 설정은 정상 반영). 음색을 지정하려면 첫 답변 요청 전에 설정하십시오.다음 기능은 이번 릴리스에서 지원하지 않으며, 구성하면 세션이 닫힙니다(닫힘 코드
1008): 대화 세션 내 인라인 전사 활성화(audio.input.transcription, 사유 input_transcription_not_supported), 대화 아이템을 통한 오디오(사유 item_audio_not_supported) 또는 이미지(사유 image_input_not_supported) 주입, 텍스트 이외의 콘텐츠 아이템 유형(사유 unsupported_content_part). 오디오 입력은 모두 input_audio_buffer.append 채널로 보내십시오.입력 전송
오디오 전송
PCM16 오디오를 작은 조각(예: 100ms당 한 조각)으로 자르고 base64로 인코딩한 뒤input_audio_buffer.append 이벤트로 계속 전송합니다:
텍스트 전송
텍스트 메시지를 직접 주입한 뒤 답변을 요청할 수도 있습니다:답변 수신
서버는 이벤트를 계속 푸시합니다. 주요 이벤트 유형:event
세션 생성과 구성 업데이트 확인입니다.
session.created를 받으면 오디오와 텍스트를 보낼 수 있습니다.event
대화 아이템 쓰기가 완료되었습니다. 사용자 입력과 모델 답변이 각각 하나의 아이템이 됩니다.
event
VAD가 켜진 상태에서 사용자가 말을 시작하고 끝냈음을 서버가 감지한 알림입니다.
speech_started는 보통 사용자가 모델을 방해하고 있음을 뜻하며, 처리 방법은 방해와 잘라내기를 참고하십시오.event
한 턴의 답변 생성이 시작되었습니다.
event
이번 턴 출력 아이템의 시작과 종료입니다.
added 이벤트의 item.id는 이후 오디오를 잘라낼 때 참조하는 대화 아이템 ID입니다.event
답변 음성의 증분 조각(base64 인코딩 PCM16)과 종료 표시이며, 받는 대로 재생할 수 있습니다.
event
답변 음성과 문장 단위로 대응하는 전사의 증분과 종료 표시입니다.
delta 필드에 이번에 추가된 텍스트가 들어 있습니다. 음성 출력을 켠 경우 답변 텍스트는 이 이벤트에서 가져옵니다. 음성을 재생하면서 자막을 표시할 수 있습니다.event
텍스트 전용 답변의 증분과 종료 표시이며, 출력 모달리티를 텍스트 전용(
output_modalities: ["text"])으로 설정한 경우에만 발생합니다.event
한 턴의 답변이 완료되었습니다. 이 이벤트는 해당 턴의 token 사용량(
usage)을 담고 있으며 과금의 근거가 됩니다.event
오류 이벤트이며 오류 코드와 설명을 담고 있습니다. 요청 자체의 문제(예:
output_modalities 값 오류)는 error 이벤트 한 건만 반환하고 세션은 계속 사용할 수 있습니다. 정책에 관련된 경우(모델 변경, 잔액 소진 등)에는 세션이 닫힙니다.답변 텍스트는 이벤트를 정확히 골라야 합니다. 기본(출력에 음성 포함)에서는 모델이
response.output_audio_transcript.delta만 푸시하고 response.output_text.delta는 푸시하지 않습니다. 출력 모달리티를 텍스트만으로 설정하면 텍스트가 response.output_text.delta로 이동합니다. 어느 모드에서든 둘 다 감시해 글자가 누락되지 않게 하십시오(아래 실행 결과 참조).방해와 잘라내기
모델이 말하는 중에 사용자가 말을 시작하면 이미 생성되었지만 아직 재생되지 않은 내용이 사용자의 다음 발화와 어긋납니다. WebSocket 연결에서는 재생을 클라이언트가 담당하므로, 방해 이후 정리도 클라이언트가 수행합니다. VAD가 켜져 있으면 서버는 사용자가 말을 시작했음을 감지한 뒤input_audio_buffer.speech_started를 보냅니다. 이 이벤트를 받은 클라이언트는 다음을 수행합니다:
- 로컬 재생을 즉시 중지하고 이번 턴 답변을 어디까지 재생했는지(밀리초) 기록합니다.
conversation.item.truncate를 보내 재생하지 않은 오디오를 대화에서 제거합니다. 다음 턴에서 모델이 해당 내용을 이미 말한 것으로 취급하지 않게 합니다.
item_id: 이번 턴 답변의 대화 아이템 ID이며response.output_item.added이벤트의item.id에서 가져옵니다.content_index: 오디오 콘텐츠 파트의 인덱스이며 항상0입니다.audio_end_ms: 유지할 오디오 길이(밀리초)이며 클라이언트가 실제로 재생한 위치를 적습니다.
conversation.item.truncated를 반환합니다. 잘라내기는 이번 턴 답변의 오디오와 해당 전사에만 영향을 주고 세션 자체는 그대로이므로 다음 턴을 이어갈 수 있습니다. OpenAI SDK에서는 conn.conversation.item.truncate(item_id=..., content_index=0, audio_end_ms=...)에 해당합니다.
VAD를 끈 경우(예: 누르고 말하기)에는 버튼을 누르는 것이 방해입니다. 누를 때 response.cancel을 보내 진행 중인 답변을 취소하고 위 절차대로 잘라냅니다. 놓으면 input_audio_buffer.append, input_audio_buffer.commit, response.create를 차례로 보냅니다.
전체 예제
아래에 세 가지 방식을 제시합니다. 하나를 선택하십시오:- OpenAI 공식 SDK(권장): WebSocket을 직접 작성할 필요 없이
websocket_base_url(SDK의 WebSocket 베이스 URL 파라미터)을 게이트웨이로 지정하면 공식 라이브러리를 그대로 재사용할 수 있습니다. - OpenAI Agents SDK: 공식 agent 프레임워크의 실시간 음성 형태이며,
model_config의url을 게이트웨이 주소로 바꾸면 됩니다. - 원시 websockets: SDK를 설치하지 않고 프로토콜에 따라 프레임을 직접 주고받습니다. 의존성이 가장 적고 문제 파악이 쉽습니다.
연결할 때 모델 이름을 왜 전달할까요? AiHubMix 게이트웨이는 WebSocket 핸드셰이크 시점에
model로 모델 프로바이더 선택, 인증, 할당량 예약을 수행해야 하는데, session.update는 핸드셰이크가 끝난 뒤에 도착해 시기적으로 늦습니다. 그래서 SDK를 쓸 때는 connect()에 model을 명시적으로 전달해야 합니다(SDK가 URL 쿼리에 넣습니다). 이것이 없으면 게이트웨이가 핸드셰이크 단계에서 거부해 연결이 아예 맺어지지 않습니다. 전사와 달리 대화 엔드포인트에는 intent=transcription이 필요하지 않습니다.공식 예제 재사용
OpenAI가 공개한 실시간 대화 예제는 대부분 SDK의 베이스 URL 파라미터에만 의존하므로 주소를 AiHubMix 엔드포인트로 바꾸면 재사용할 수 있습니다:실행 결과(프로덕션 실측)
다음은 OpenAI SDK 예제를aihubmix.com 프로덕션 환경(모델 gpt-realtime-2.1)에서 실행한 실제 결과입니다. 세션은 server_vad를 켜고, prompt와 음성은 모두 영어입니다.
텍스트 입력
실측 확인: 기본(출력에 음성 포함)에서는 답변 텍스트가
response.output_audio_transcript.delta로만 글자 단위로 도착하고 response.output_text.delta는 나타나지 않습니다. response.done은 해당 턴의 usage를 담습니다. 핸드셰이크에는 약 2~4초가 걸리며, 이는 세션을 맺을 때의 할당량 예약 비용입니다.과금
- token 과금: 대화 세션은 각 턴의 답변이 끝날 때
response.done이벤트로 해당 턴의 token 사용량(usage)을 반환하며 이를 기준으로 과금됩니다. 사용량은 오디오 입력 / 오디오 출력 / 텍스트 입력 / 텍스트 출력 등의 구성 요소별로 각각 계량되며, 각 구성 요소의 단가는 모델 상세 페이지의 실시간 표시 가격을 따릅니다. - 사용한 만큼 수시 정산: 이는 장시간 연결이며, 요금은 세션 진행 중 각 턴의 답변마다 실시간으로 차감되고 세션이 끝날 때 한꺼번에 정산되지 않습니다. 세션을 맺을 때 약 1분 분량의 할당량 예약을 먼저 수행하며(입장 심사일 뿐 실제 과금이 아님), 세션이 끝나면 남은 예약을 해제합니다. 따라서 계정의 사용 가능 잔액이 약 1분 분량을 감당할 수 있어야 세션을 맺을 수 있습니다.
- 사용량 및 결제의 사용 내역에서 실시간 대화별 과금 기록을 하나씩 확인할 수 있습니다.
제한과 제약
- 단일 세션 최대 시간: WebSocket 연결은 최대 62분이며, 도달하면 서버가 먼저 닫습니다(닫힘 코드
1000, 사유session_duration_limit). 더 길게 필요하면 구간을 나눠 재연결하십시오. - 유휴 연결 해제: 클라이언트와 모델 양쪽 모두 5분 동안 아무 활동이 없으면 서버가 세션을 닫습니다(닫힘 코드
1008, 사유idle_timeout). 어느 한쪽에 활동이 있으면 타이머가 초기화되므로, 출력을 계속하는 긴 답변이 중간에 끊기지 않습니다. - 잔액 부족: 세션을 맺을 때 사용 가능 잔액이 약 1분의 예약 분량을 감당하지 못하면 핸드셰이크가 곧바로 거부되고(HTTP 403) 세션이 맺어지지 않습니다. 세션 진행 중 잔액이 소진되면 이미 맺어진 연결이 즉시 닫힙니다.
- 서버 측 전용: 브라우저 직접 연결은 지원하지 않습니다(
Origin헤더 검증). 서버 측에서 통합하십시오. - 모델 고정: 모델은 연결 URL에 고정되며, 세션 중
session.update로 모델을 바꾸면 거부되고 세션이 닫힙니다. - 형식 고정: 오디오 입력과 출력은 모두
audio/pcm@24000모노만 지원하며 다른 형식은 거부됩니다. - 음색 고정:
voice는 첫 답변이 시작된 뒤에는 변경할 수 없습니다. 첫 답변 요청 전에 설정하십시오. - 미지원: 대화 세션 내 인라인 전사, 대화 아이템을 통한 오디오 또는 이미지 콘텐츠 주입, 텍스트 이외의 콘텐츠 아이템 유형.
- 한 번에 한 답변: 같은 세션에서 동시에 진행할 수 있는 답변은 하나입니다. 이전 턴이 끝나기 전에
response.create를 다시 보내면 거부되고 세션이 닫힙니다(닫힘 코드1008, 사유response_already_active).
자주 발생하는 오류
업데이트: 2026-09-21