GPT-Live 세션 관리

GPT-Live 세션 관리 (Managing GPT-Live sessions)

GPT-Live에 연결한 뒤 세션 이벤트를 사용해 컨텍스트를 추가하고, 대본을 표시하고, 연결을 관리할 수 있어요. GPT-Live는 듣고 말하는 것을 동시에 할 수 있어요. 대본 텍스트, 재생된 오디오, 백엔드 작업 진행을 각각 따로 추적해서 인터페이스가 어시스턴트가 말하는 것과 아직 실행 중인 작업을 함께 보여주게 하세요.

출처: 문서

본문

이 가이드는 연결이 session.started를 방출했다고 가정해요. 연결 설정과 오디오 스트리밍은 Connections, 백엔드 작업은 Delegation and tools를 참고하세요.

세션 구성하기

세션을 만들 때 모델, 음성, 위임 모드를 선택하세요. 모델에 대화용 지시를 주고 관련 히스토리를 포함하세요. GPT-Live는 대화가 커지면 컨텍스트를 자동으로 관리해요.

구성 필드

설정 시작 시 구성 세션 중 변경
Model 필요한 model을 설정하세요. 바꾸려면 새 세션을 시작하세요.
Instructions instructions로 대화 동작을 설정하세요, 최대 16,384 토큰. session.instructions.append로 지시 추가.
History input에 관련 이전 텍스트 메시지를 설정하세요. 기본값은 []. append 이벤트로 컨텍스트 추가.
Voice audio.output.voice를 지원 음성이나 승인된 맞춤 음성으로 설정하세요. 기본값은 marin. 바꾸려면 새 세션을 시작하세요.
Delegation delegation.type을 client나 responses로 설정하세요. 생략되거나 null이면 client 모드. 기존 모드 내에서 Responses 설정 업데이트.
Storage store를 true로 하면 세션을 fork에 사용할 수 있게 해요. 기본값 false. 시작 시 결정.

음성 옵션

세션을 만들 때 음성을 선택하세요. audio.output.voice를 "quartz" 같은 API 이름으로 설정하세요. GPT-Live는 이런 추가 음성 옵션을 포함해요.

음성 API 이름 언어 리전 영향 표현 출처
Quartz quartz English Australian Feminine Generated
Ripple ripple English Australian Masculine Natural
Vesper vesper English British Masculine Natural
Willow willow English Irish Feminine Natural
Stone stone English Irish Masculine Natural
Gleam gleam English North American Feminine Natural
Meridian meridian English North American Masculine Natural
Bossa bossa Portuguese Brazilian Feminine Natural
Tempo tempo Portuguese Brazilian Masculine Natural
Beacon beacon English Filipino Masculine Generated
Delta delta English Southern U.S. Feminine Generated
Cinder cinder English Southern U.S. Masculine Generated

리전 영향은 음성의 말하기 스타일을 설명해요. 애플리케이션이 필요로 하는 언어와 발음으로 음성을 테스트하세요. 직접 녹음으로 만든 승인된 음성은 Custom voices를 참고하세요.

WebSocket은 시작 시 audio.format을 선택하세요. 같은 형식이 세션의 입력·출력 오디오에 적용돼요. 다른 형식을 쓰려면 새 세션을 시작하세요. WebRTC는 연결 설정 중에 오디오 형식을 협상하므로 WebRTC 요청에서 audio.format을 빼세요. 지원 형식과 스트리밍 세부 사항은 WebSocket 오디오 형식을 참고하세요.

라이브 세션 업데이트

이미 Responses delegation을 사용하는 세션에서 session.delegation.responses 변경에는 session.update를 쓰세요. 바꾸려는 설정만 보내세요. 생략된 설정은 값을 유지해요. 설정과 업데이트 워크플로는 Responses delegation 구성을 참고하세요.

시작 시 위임 모드와 model, instructions, input, audio, store 필드를 선택하세요. session.update는 위에서 설명한 지원되는 Responses 설정에만 쓰고, 다른 구성 필드는 거부돼요. 위임 모드를 바꾸려면 새 세션을 만드세요. 시작 시 delegation: null은 기본 Responses 설정을 복원하는 것이 아니라 client delegation을 선택해요.

성공적인 업데이트는 결과 세션 구성과 함께 session.updated를 방출해요. client_event_id를 통해 나가는 event_id와 일치시키세요. 거부된 명령 처리도 같은 이벤트 루프에서 처리하세요. 백엔드 작업과 음성 출력은 자체 이벤트로 추적하세요.

히스토리와 컨텍스트 제공

시작 히스토리로 주제를 재개하고, 대화가 계속되면 관련 컨텍스트를 추가하세요. 신뢰하는 애플리케이션 지시와 사용자 메시지·사실 결과를 분리하세요.

이전 대화로 세션 시딩

세션을 만들 때 session.input에 이전 텍스트 메시지를 포함하세요. 예를 들어 세션 생성 구성에 이 input 필드를 추가하세요.

from openai.types.live.session_config_param import SessionConfigParam

session: SessionConfigParam = {
    "model": "gpt-live-1",
    "input": [
        {
            "type": "message",
            "role": "user",
            "content": [
                {"type": "input_text", "text": "I need help with my recent order."}
            ],
        },
        {
            "type": "message",
            "role": "assistant",
            "content": [{"type": "output_text", "text": "What is the order number?"}],
        },
    ],
}

목록은 최대 128개 메시지와 8,192개 결합 토큰을 받아요. 각 메시지에는 텍스트 부분 하나와 developer, user, assistant 중 하나의 역할이 있어요. Developer·user 메시지는 input_text를, assistant 메시지는 text나 output_text를 사용해요. 신뢰하는 애플리케이션 지시는 instructions나 developer 메시지에 넣으세요.

다음 상호작용에 필요한 텍스트 히스토리를 선택해 시작 시에 제공하세요. 세션 중에는 아래 컨텍스트 이벤트로 업데이트를 추가해요. 도구 결과 같은 백엔드 특정 항목은 delegation 워크플로로 보내세요.

컨텍스트가 모델에 도달하는 시점 이해하기

모델이 처음부터 필요로 하는 컨텍스트는 input에 넣으세요. 세션이 시작되면 전체 필드를 사용할 수 있어요.

실행 중 세션에서는 session.instructions.append, session.thinking.append, session.commentary.append가 시간에 따라 컨텍스트를 추가해요. 승인(acknowledgment)은 세션 타임라인이 추가된 컨텍스트의 추정 끝에 도달할 때 도착해요. 그 start_ms와 end_ms는 그 업데이트가 세션 타임라인에서 어디에 놓이는지 추정해요.

이 시간은 컨텍스트 전달을 설명하지 발화나 재생을 설명하지 않아요. 모델은 전체 업데이트를 사용하기 전에 응답할 수도 있어요. 새 지시나 사실에 의존하는 작업은 애플리케이션에서 결과 동작을 검증하세요.

세션 타임라인이 멈추면 승인이 보류 상태로 남을 수 있어요. client_event_id로 나가는 event_id에 승인을 일치시키고, 기다리는 동안 오류를 계속 처리하세요. 세션을 닫으면 아직 보류 중인 append에 대한 오류를 반환해요.

대화 중 컨텍스트 추가

모델이 업데이트를 어떻게 사용해야 하는지에 따라 이벤트를 선택하세요.

  • session.instructions.append: 동작과 발화에 영향을 주는 신뢰하는 애플리케이션 지시 추가.
  • session.thinking.append: 즉시 말하지 않아도 되는 사실 컨텍스트 추가.
  • session.commentary.append: 모델이 소리 내어 말할 정보를 제공(의역할 수 있음).

각 이벤트는 최대 500 토큰의 일반 문자열 content와 필수 delegation_id를 받아요. 세션 전체 컨텍스트에는 null을 쓰세요. 예를 들어 애플리케이션이 사용자 수락을 확인하고 조회를 시작한 뒤에 이렇게 보내요.

export function sendUpdate(connection) {
  connection.send({
    type: "session.thinking.append",
    event_id: "context_1",
    delegation_id: null,
    content:
      "The user has already accepted the terms. The account lookup is still running.",
  });
}
from openai.resources.live.live import AsyncLiveConnection
from openai.resources.live.sideband import AsyncSidebandConnection


async def send_update(
    connection: AsyncLiveConnection | AsyncSidebandConnection,
) -> None:
    await connection.session.thinking.append(
        event_id="context_1",
        delegation_id=None,
        content=(
            "The user has already accepted the terms. The account lookup is still "
            "running."
        ),
    )

이 업데이트를 추적하려면 client_event_id: "context_1"로 session.thinking.appended 또는 해당 오류를 처리하세요. 승인 타이밍은 컨텍스트가 모델에 도달하는 시점 이해하기를 참고하세요.

어시스턴트는 이 이벤트 중 어느 것으로든 제공된 정보를 반복할 수 있어요. 대화에 적합한 정보만 보내고 자격 증명·보안 정보는 백엔드에 두세요. 애플리케이션이 정의한 동작에는 session.instructions.append를, 사실 도구 결과는 컨텍스트로 제공하고, 권한·필수 확인은 애플리케이션 코드에서 강제하세요.

페이지 탐색, 선택, 기타 UI 변경은 Share UI context를 참고해 GPT-Live가 사용자가 가리키는 것을 이해하는 데 도움이 되는 간결한 업데이트를 보내세요.

특정 백엔드 작업에 대한 업데이트에는 관련 client delegation의 ID를 쓰세요. delegation ID는 Live 작업을 식별해요. Responses 응답 ID와 도구 호출 ID는 다른 객체를 식별해요. 워크플로는 올바른 종류의 업데이트 보내기를 참고하세요.

애플리케이션이 문제를 감지하면 세션의 기본 WebSocket이나 sideband WebSocket으로 짧은 수정을 보내세요. 검사·작업 제어·재생 처리는 대화 가드레일 적용을 참고하세요.

발화·대본 관리

대본 델타

사용자 발화는 session.input_transcript.delta, 어시스턴트 발화는 session.output_transcript.delta로 들어보세요. 각 이벤트에는 텍스트 조각과 세션 타임라인에서의 구간이 있어요.

{
  "type": "session.input_transcript.delta",
  "event_id": "event_transcript_1",
  "delta": "What is",
  "start_ms": 1000,
  "end_ms": 1200
}

각 화자의 delta 조각을 받은 그대로 정확히 이어 붙이세요. 공백과 반복된 단어를 보존해요. 그 start_ms와 end_ms를 유지하세요. 이 값은 세션 시작부터의 밀리초예요. 위 예시는 1,000 ms부터 1,200 ms 직전까지의 구간을 다뤄요. 정확한 단어 경계가 아니라 대략적 조각 타이밍을 설명해요. 패킷 도착 시간 대신 이 값들을 사용해 대본을 구성하세요.

대본 이벤트는 텍스트가 있는 구간에 도착하고 전달이 고르지 않을 수 있어요. 조각이 문장의 일부만 담을 수도 있고, 전달의 공백이 네트워크 지연일 수도 있어요. 대본 델타에는 완료된 대화 턴을 표시하는 항목 ID나 이벤트가 없으므로, 표시용으로 어떻게 묶을지는 애플리케이션이 결정해요.

대본 조각 처리는 선택사항이에요. UI 업데이트, 검사 실행, 대화가 계속되는 동안 작업을 일찍 시작하는 데 쓸 수 있어요. 가벼운 검사에는 낮은 reasoning effort의 gpt-5.6-luna 같은 작은 모델을 고려하세요. 예시와 연결 지침은 대본 조각에 반응하기를 참고하세요.

발화가 진행되는 동안 대화를 모니터링하고 개입을 트리거하려면 transcript guardrails를 쓰세요. 재생 전에 어시스턴트 발화를 검사해야 한다면 재생 전 발화 검사의 버퍼링·승인·인터럽션·복구 처리를 참고하세요.

대본 타이밍을 오디오 재생과 분리하세요. WebSocket session.output_audio.delta 이벤트에는 타이밍 필드나 output-audio-done 이벤트가 없어요. WebRTC는 미디어 트랙으로 오디오를 전달해요. 오디오 처리는 Connections를 참고하세요.

자막 표시

GPT-Live는 full duplex예요. 발신자와 어시스턴트가 동시에 말할 수 있어요. 캡션을 독립적으로 업데이트해서 겹치는 발화 중에 두 화자의 텍스트가 계속 자랄 수 있게 하세요.

앱이 채팅 버블을 쓴다면 "I'd like"와 " to change my booking" 조각이 하나의 발신자 버블에 나타날 수 있어요. 발신자가 계속 말하는 동안 어시스턴트가 "Sure"라고 하면 그 승인을 따로 보여주면서 발신자 버블이 계속 자라게 하세요. 원래 조각과 타임스탬프를 유지해서 나중에 도착하는 텍스트가 적절한 버블을 업데이트할 수 있게 하세요.

말하는 캡션은 session.output_transcript.delta를, 백엔드 업데이트는 따로 보여주세요. 도구 실행·작업 취소 결정은 텍스트를 표시용으로 묶는 방식과 분리해 애플리케이션 작업 로직에 두세요.

마이크 입력 제어

세션을 끝내지 않고 입력을 음소거하려면 session.input_audio.mute를 보내세요.

export function sendUpdate(connection) {
  connection.send({
    type: "session.input_audio.mute",
    event_id: "mute_1",
  });
}
from openai.resources.live.live import AsyncLiveConnection
from openai.resources.live.sideband import AsyncSidebandConnection


async def send_update(
    connection: AsyncLiveConnection | AsyncSidebandConnection,
) -> None:
    await connection.session.input_audio.mute(
        event_id="mute_1",
    )

명령이 수락된 것으로 처리하기 전에 client_event_id: "mute_1"로 session.input_audio.muted를 기다리세요. 입력을 재개하려면 session.input_audio.unmute를 보내고 session.input_audio.unmuted를 기다리세요. 두 명령 모두 오류를 처리하세요.

입력을 음소거해도 세션은 계속 실행돼요. 모델은 계속 발화를 생성하고 위임된 작업도 계속될 수 있어요. 로컬 녹음·재생도 멈춰야 한다면 애플리케이션의 마이크 캡처와 오디오 플레이어 제어를 사용하세요.

발신자가 말하기 전에 인사하기

GPT-Live가 대화를 열게 하려면 session.started 후에 인사 지시를 보내세요. 언어, 어시스턴트가 말할 내용, 즉시 시작한 뒤 듣기 멈춤을 지정하세요. 발신자가 말할 때까지 애플리케이션이 선택한 인사 언어를 사용해요. 예:

Greet the caller now in English. Introduce yourself as the support assistant and ask how you can help. Then pause and listen.

  1. 이 시퀀스 내내 입력 오디오를 계속 실행하세요. 발신자가 말하기 전의 침묵 포함. WebSocket은 session.input_audio.append를 계속 보내고, WebRTC는 입력 오디오 트랙을 활성화로 유지하세요.
  2. session.instructions.append와 delegation_id: null로 지시를 한 번 보내세요.
  3. client_event_id로 session.instructions.appended를 명령에 일치시키고 오류를 처리하세요. 이 승인은 지시가 수락됐음을 확인해요.

정확한 표현과 알려진 재생 완료 지점이 필요하면 검증된 녹음이나 렌더링된 클립을 애플리케이션을 통해 재생하고, 재생 중 GPT-Live 재생을 제어하세요. 지원하는 언어에서 인사를 테스트하세요. 인사 중 발신자가 말을 시작하는 경우 포함. 프롬프트 설계는 음성 모델 프롬프팅을 참고하세요.

고지 사항 전달

고지 사항(disclosure)의 특정 음성 표현을 요청하려면 session.instructions.append를 쓰세요. session.commentary.append는 텍스트를 의역할 수 있어요. session.started 후에 예를 들어 이렇게 보내요.

export function sendUpdate(connection) {
  connection.send({
    type: "session.instructions.append",
    event_id: "disclosure_1",
    delegation_id: null,
    content:
      "Immediately say the following disclosure exactly and in full before responding to the caller: This call may be recorded for quality and training purposes.",
  });
}
from openai.resources.live.live import AsyncLiveConnection
from openai.resources.live.sideband import AsyncSidebandConnection


async def send_update(
    connection: AsyncLiveConnection | AsyncSidebandConnection,
) -> None:
    await connection.session.instructions.append(
        event_id="disclosure_1",
        delegation_id=None,
        content=(
            "Immediately say the following disclosure exactly and in full before "
            "responding to the caller: This call may be recorded for quality and "
            "training purposes."
        ),
    )

발신자가 말하기 전에 인사하기처럼 입력 오디오를 계속 실행하세요. 대화 중에 보낸 지시는 진행 중인 발화를 중단할 수 있어요.

표시(delivered) 처리 전에 생성된 고지 사항과 그 재생을 확인하세요. 지시 승인은 수락을 기록할 뿐이므로 표현 확인은 오디오 자체를 사용하세요. 정확한 표현과 알려진 재생 완료 지점이 필요하면 검증된 녹음이나 렌더링된 클립을 재생하고 재생 중 필요할 때 재생 제어를 사용하세요.

긴 대화 관리

GPT-Live는 긴 대화를 자동으로 관리하고 원래 시작 지시를 보존해요.

기본 컨텍스트 창은 128,000 토큰을 보유하며, 지시, 대화 텍스트, 대본에 나타나지 않는 오디오 토큰을 포함해요.

GPT-Live는 백그라운드에서 오래된 대화 히스토리를 요약해요. 컨텍스트 사용이 90%를 초과하면 같은 세션 내에서 교체 음성 엔진을 시작해요. 교체 엔진은 원래 지시와 최대 8,192 토큰의 대화 히스토리를 받는데, 최근 메시지와 (가능하면) 오래된 메시지의 요약을 포함해요. 요약 준비가 실행 중 엔진의 컨텍스트를 즉시 바꾸지는 않아요.

오래된 대화 세부 사항은 요약되거나 생략될 수 있어요. 중요한 사실, 확인된 행동, 현재 작업 상태는 애플리케이션에 두고 필요할 때 관련 컨텍스트를 제공하세요.

세션 저장 및 포킹

Fork는 저장된 음성 대화에서 새 세션을 시작해요. 같은 참조 대화에서 여러 평가 시도를 실행하거나, 이전 세션이 끝난 뒤 사용자가 계속하게 하려는 경우에 쓸 수 있어요. 각 fork는 새 연결과 세션 ID를 받고, source 대화와 저장된 구성이 시작점이 돼요.

참조 대화에서 평가 실행

에이전트가 발신자의 주문 변경을 어떻게 처리하는지 테스트하고 싶다고 가정해 봅시다. 발신자가 주문을 식별한 시점까지 설정을 한 번 기록하세요. 발신자가 변경을 요청하기 전에 그 세션을 끝내고 마무리해요. 그러면 각 평가가 같은 참조 세션을 fork하고 같은 다음 발신자 오디오 "Actually, can you send it to my office instead?"를 받을 수 있어요.

각 시도마다 같은 테스트 주문과 애플리케이션 상태를 복원하고, 다음 발신자 입력을 제공하고, 새 응답과 도구 행동을 평가하세요. 시나리오를 반복하거나 지원되는 Responses 백엔드 설정을 비교할 수 있어요. 응답 시간과 분리해서 fork 시작을 측정하세요. 시나리오 선택과 결과 측정은 GPT-Live 평가 가이드를 참고하세요.

Fork는 GPT-Live 모델, 음성, 원래 지시를 상속해요. 다른 음성 모델이나 시작 프롬프트를 비교하려면 그 구성으로 새 세션을 만드세요. fork API는 완료된 source 녹음을 사용하므로, 평가가 시작되길 원하는 지점에서 참조 세션을 끝내세요.

세션 종료 후 계속하기

예를 들어 발신자가 끊었다가 나중에 다시 걸거나, 끊긴 호출 후 재연결할 수 있어요. 이전 세션에 완료된 저장 녹음이 있다면 애플리케이션이 새 연결에서 fork하고 저장된 대화에서 계속할 수 있어요.

애플리케이션 작업 상태를 source 세션 ID와 함께 저장하세요. 계속하기 전에 진행 중인 백엔드 작업 상태를 확인하고 새 세션에 현재 결과를 주세요. 예를 들어 주문 업데이트가 이미 제출됐다면 다른 업데이트를 시도하기 전에 그 결과를 확인하세요. 제어와 sideband 연결에는 새 세션 ID를 사용하고, 이후 백엔드 결과를 새 세션으로 라우팅하세요.

fork용 세션 준비

  1. source 생성 시 저장 활성화: 세션 구성에서 store: true를 설정하세요. Storage는 기본값이 false이고, 프로젝트에 활성화되어야 하며, 지속성을 허용하는 데이터 정책이 필요해요.
  2. source 세션 ID 저장: session.started나 WebRTC 생성 응답에서 읽고 애플리케이션 대화 기록과 연결하세요.
  3. source 마무리·닫기: 필요한 백엔드 작업을 완료한 뒤 Usage and graceful close를 따르세요. session.closed까지 연결을 열어두고 마무리 오류를 처리하세요. Fork는 완료된 저장 녹음이 필요하고, 저장하면 마무리에 시간이 추가될 수 있어요.
  4. 새 연결에서 fork 시작: 아래 전송 흐름으로 source ID를 사용하고, 새 세션 ID를 저장하고, 대화를 계속하기 전에 시작을 완료하세요. 자식의 store를 명시적으로 설정하세요: 나중에 그 연속을 fork하려면 true, 그 시도를 저장할 필요가 없으면 false. 생략하면 source 설정을 상속해요.

저장 녹음은 30일 동안 사용 가능해요. Zero Data Retention (ZDR)에서는 store가 false로 취급되고 fork를 사용할 수 없어요. fork 기반 평가에는 저장이 활성화된 비-ZDR 조직을 사용하세요.

완료된 저장 녹음이 없다면 관련 저장 텍스트 히스토리로 새 세션 시작하세요. 저장 요구사항은 GPT-Live 데이터 제어를 참고하세요.

예를 들어 source 세션의 WebSocket session.start 구성이나 WebRTC 생성 요청에 이 필드를 설정하세요.

{
  "store": true
}

애플리케이션이 쓰는 전송으로 fork를 시작하세요.

전송 fork 시작
WebSocket wss://api.openai.com/v1/live/sessions/{source_session_id}/fork에 연결.
WebRTC POST /v1/live/sessions/{source_session_id}/fork에 새 SDP offer를 보낸 뒤, 반환된 transport.sdp answer를 새 peer connection에 적용.

Fork는 source 세션의 모델, 원래 지시, 입력을 상속해요. 시작 시 지원되는 오버라이드만 보내세요.

  • WebSocket: store, Responses delegation 설정, 새 연결의 audio.format. session 객체와 함께 session.start 이벤트를 보내고, 지원되는 곳에서 상속 설정을 유지하려면 {}를 쓰세요.
  • WebRTC: store, Responses delegation 설정, 프론트엔드 클라이언트 권한.

WebSocket fork는 새 연결의 audio.format을 설정하거나 기본 PCM16 24 kHz를 사용하세요. source 오디오 형식과 프론트엔드 데이터 채널 권한은 상속되지 않아요. WebRTC는 연결 설정 중에 오디오 형식을 협상하므로 audio.format을 생략하세요. WebRTC는 명시적으로 오버라이드하지 않는 한 프론트엔드 권한 설정을 보존해요.

WebSocket은 더 많은 명령을 보내기 전에 session.started를 기다리세요. WebRTC는 HTTP 요청이 세션을 시작하므로, 추가 session.start 없이 협상된 연결을 통해 계속하세요.

WebSocket fork 시작

OPENAI_API_KEY를 설정하세요. 예시는 애플리케이션이 저장한 source 세션 ID를 사용해요. 시작을 확인한 뒤 fork를 닫아요. 대화를 계속하려면 session.started 후 WebSocket 연결 흐름으로 오디오를 주고받으세요. 시작 필드와 이벤트는 fork WebSocket 레퍼런스를 참고하세요.

import OpenAI from "openai";
import { ForksWS } from "openai/resources/live/forks/ws";

async function forkSession(sourceSessionId) {
  const ws = new ForksWS(new OpenAI(), { session_id: sourceSessionId });
  let finalized = false;
  try {
    for await (const event of ws) {
      if (event.type === "open") {
        ws.send({ type: "session.start", session: {} });
      } else if (event.type === "error") {
        throw event.error;
      } else if (event.type === "message") {
        if (event.message.type === "session.started") {
          console.log("Fork ready:", event.message.session.id);
          // This startup example closes the fork after confirming it is ready.
          ws.send({ type: "session.close" });
        } else if (event.message.type === "session.closed") {
          console.log("Final usage:", event.message.usage);
          finalized = true;
          break;
        }
      }
    }
    if (!finalized) throw new Error("Connection closed before session.closed");
  } finally {
    ws.close();
  }
}
from openai import OpenAI


def fork_session(source_session_id: str) -> None:
    client = OpenAI()
    with client.live.forks.connect(session_id=source_session_id) as connection:
        connection.session.start(session={})
        finalized = False
        for event in connection:
            if event.type == "session.started":
                print("Fork ready:", event.session.id)
                # This startup example closes the fork after confirming it is ready.
                connection.session.close()
            elif event.type == "session.closed":
                print("Final usage:", event.usage)
                finalized = True
                break
            elif event.type == "error":
                raise RuntimeError(event.error.message)
        if not finalized:
            raise RuntimeError("Connection closed before session.closed")

WebRTC fork 시작

프론트엔드에서 새 SDP offer를 만들고 백엔드로 보내세요. 아래 백엔드 예시는 그 offer와 애플리케이션의 저장 source 세션 ID를 사용해요.

import OpenAI from "openai";

async function forkSession(sourceSessionId, offerSdp) {
  const client = new OpenAI();
  const fork = await client.live.sessions.fork(sourceSessionId, {
    transport: { type: "webrtc", sdp: offerSdp },
  });
  console.log(JSON.stringify(fork));
}
from openai import OpenAI


def fork_session(source_session_id: str, offer_sdp: str) -> None:
    client = OpenAI()
    fork = client.live.sessions.fork(
        source_session_id,
        transport={"type": "webrtc", "sdp": offer_sdp},
    )
    print(fork.model_dump_json())

응답을 프론트엔드로 돌려보내고, transport.sdp를 새 peer connection의 answer로 적용하고, 새 session.id를 보관하세요. API 키는 백엔드에 두세요.

sideband 연결과 세션 제어에는 새 세션 ID를 사용하세요. 완료되지 않은 작업을 재시도하기 전에 백엔드에서 그 결과를 확인하고 현재 애플리케이션 작업 상태를 복원하세요. 완료된 저장 녹음이 없다면 저장 히스토리로 새 세션을 시드하세요.

녹음 다운로드

저장 녹음이 완료된 뒤 GET /v1/live/sessions/{session_id}/content로 오디오를 다운로드해요. 응답은 바이너리 스테레오 WAV로, 입력 오디오는 왼쪽 채널, 출력 오디오는 오른쪽 채널이에요. 예시는 애플리케이션의 저장 세션 ID를 사용하고 응답을 recording.wav로 스트리밍해요.

import OpenAI from "openai";
import { createWriteStream } from "node:fs";
import { pipeline } from "node:stream/promises";

async function downloadRecording(sessionId) {
  const client = new OpenAI();
  const response = await client.live.sessions.downloadRecording(sessionId);
  if (!response.body) throw new Error("Recording response has no body");
  await pipeline(response.body, createWriteStream("recording.wav"));
}
from openai import OpenAI


def download_recording(session_id: str) -> None:
    client = OpenAI()
    with client.live.sessions.with_streaming_response.download_recording(
        session_id
    ) as response:
        response.stream_to_file("recording.wav")

유휴 세션 닫기 및 재개

상호작용 사이에 긴 간격이 있는 애플리케이션은 비활성 동안 음성 세션을 닫고 사용자가 돌아오면 새 세션을 시작하세요. 대화 컨텍스트와 애플리케이션 작업 상태를 보관해서 사용자가 다시 말하지 않아도 계속할 수 있게 하세요. 예를 들어 차량 내 어시스턴트는 운전자가 다시 음성을 활성화하면 재개할 수 있고, 코딩 어시스턴트는 음성 대화 사이에 백엔드 워커를 계속 실행할 수 있어요.

  1. 닫을 시점 결정: 오디오 활동, 어시스턴트 재생, 애플리케이션 상호작용을 기반으로 애플리케이션 제어 무활동 타임아웃을 사용하세요. 읽기·생각하기 같은 예상 일시정지를 허용하세요. 재생이 끝나고 현재 음성 세션이 필요한 보류 작업이 없을 때만 닫으세요. 대본 이벤트 사이의 공백만으로는 침묵이 확립되지 않아요.
  2. 상태 저장 및 정상 종료: source 세션 ID, 대화 컨텍스트, 현재 작업 상태를 저장하세요. 필요한 Responses 작업을 끝낸 뒤 Usage and graceful close를 따르세요: session.closed 리스너를 설치하고, session.close를 보내고, 연결을 해제하기 전에 session.closed를 기다리세요. client delegation에서는 애플리케이션 관리 백엔드 작업이 음성이 닫힌 동안 독립적으로 계속될 수 있어요.
  3. 재시작 시점 감지: Resume conversation 버튼, push-to-talk 제어, 애플리케이션 관리 웨이크 트리거를 제공하세요. 닫힌 Live 세션은 사용자를 들을 수 없어요. 음성 감지로 자동 재시작한다면 마이크 캡처를 활성화로 유지하고 시작 문구를 연결 설정을 통해 버퍼링하세요. 새 세션이 준비되면 그 오디오를 전달해 사용자의 첫 말을 보존해요.
  4. 새 세션에서 컨텍스트 복원: source가 store: true로 생성되고, 저장이 활성화·허용되고, 녹음이 성공적으로 완료됐다면 저장 세션을 fork하세요. 그렇지 않으면 저장 텍스트 히스토리로 새 세션 시작하세요. 새 세션 ID를 저장하고, 진행 중인 백엔드 작업 상태를 확인하고, 이후 결과를 새 세션으로 라우팅하세요. 작업 상태를 애플리케이션에 유지해서 재시작이 완료된 행동을 반복하지 않게 하세요.

마이크를 음소거해도 세션은 활성 상태예요. 유휴 타임아웃은 피하는 음성 지속 시간과 세션 생성 비용, 음성이 다시 준비되기까지의 지연을 비교해 고르세요. 음성 세션 비용과 WebRTC 초기화 요금을 참고하세요.

오류 처리 및 세션 종료

세션이 완료될 때까지 세션 이벤트를 계속 읽으세요. 거부된 명령, 실패한 연결, 완료된 세션을 구분해 애플리케이션이 적절히 복구하게 하세요.

거부된 명령 처리

승인과 함께 error 이벤트를 읽으세요. error.client_event_id가 있으면 실패한 나가는 명령을 식별해요.

{
  "type": "error",
  "event_id": "event_error",
  "error": {
    "type": "invalid_request_error",
    "code": "immutable_field_update",
    "message": "The delegation type cannot change after session startup.",
    "param": "session.delegation.type",
    "client_event_id": "event_update"
  }
}

code가 null이거나 client 이벤트 ID가 없는 오류를 위한 일반 오류 처리기를 제공하세요. 불변 필드 오류는 현재 구성을 유지하거나 의도한 설정으로 새 세션을 만드세요.

Moderation 처리

Moderation은 세션에 두 가지 방식으로 영향을 줄 수 있어요.

  • 일부 moderation 이벤트가 세션을 끝냅니다.
  • 다른 것들은 현재 발화의 나머지에 대한 어시스턴트 오디오를 끊고, 세션을 끝내지 않고 error 이벤트를 냅니다.

오디오가 재생되는 동안 error 이벤트를 계속 처리하세요. 오디오 중단과 세션 종료를 따로 추적하고, 재생을 확인한 후에만 말한 메시지를 delivered로 표시하세요. 내장 moderation과 함께 자체 대화 가드레일을 적용하세요.

Usage 및 정상 종료

session.usage.updated는 누적 음성 지속 시간을 초 단위로 보고해요.

{
  "type": "session.usage.updated",
  "event_id": "event_usage_1",
  "usage": { "seconds": 12 },
  "context_window": { "usage_ratio": 0.42 }
}

음성 지속 시간의 누적 합계로 최신 usage.seconds를 사용하세요. 예를 들어 12초, 15초 업데이트는 15초 사용을 뜻해요. 백엔드 토큰 사용은 중첩 Responses 완료 이벤트와 분리해 추적하세요. 사용 회계는 비용 최적화를 참고하세요.

정상 종료하려면:

  1. 보류 중인 함수 결과와 응답 연속을 포함해 애플리케이션이 필요한 위임된 Responses 작업을 끝내세요.
  2. session.close를 보내기 전에 session.closed 리스너를 설치하세요.
  3. session.close를 보내고 세션에 새 작업 제출을 중단하세요. 보류 세션 이벤트가 소진되는 동안 WebSocket·WebRTC 연결, 데이터 채널, 첨부된 sideband 리시버를 유지하세요.
  4. session.closed에서 최종 usage.seconds, reason, 세션 스냅샷을 읽으세요. 이미 response.event로 받은 위임된 사용량을 보존하세요.
  5. 그 이벤트 후 전송과 오디오 기기를 정리하세요. 마무리가 실패하거나 애플리케이션이 설정한 타임아웃을 초과하면 불완전한 마무리를 보고하고 리소스를 해제하세요.

session.close를 보내면 대기 중인 Responses가 취소되고 추가 명령이 거부돼요. 활성 응답은 끝날 수 있지만, 함수 결과를 기다리는 응답은 종료가 시작된 뒤 계속할 수 없어요. client delegation을 통해 애플리케이션이 실행하는 작업을 완료할지 취소할지는 별도로 결정하세요.

session.closed로 마무리를 확인하고 최종 구성 스냅샷을 읽으세요. 이 이벤트가 올 때까지 전송을 열어두세요. 소켓이 먼저 닫히면 마무리를 미확인으로 기록하고, 유효한 session.closed 후에 닫히면 확인된 결과를 보존하세요.

최종 이벤트의 reason은 세션이 왜 끝났는지 설명해요.

Reason 의미
close_requested 애플리케이션이 session.close를 보내거나 hangup 엔드포인트를 호출함.
expired 세션이 지속 시간 한도에 도달함.
content 안전성 필터가 세션을 끝냄.
remote_hangup 원격 기본 연결이 정상 종료됨.
connection_lost 기본이나 업스트림 연결이 예기치 않게 끊김.

session.closed 이벤트는 reason이 연결 손실이나 안전성 종료여도 마무리를 확인해요. 그 이벤트가 없으면 최종 사용량은 미확인으로 남아요. 저장된 세션은 녹음이 저장되는 동안 마무리가 더 오래 걸릴 수 있으니, 저장을 고려한 애플리케이션 타임아웃을 선택하세요.

실패한 연결에서 복구

HTTP 세션 생성 오류는 세션이 session.started에 도달하지 못했다는 뜻이에요. 시작 오류는 실행 중 세션의 오류와 분리해 처리하세요. 실행 중 연결이 session.closed 전에 실패하면 마지막으로 관찰된 사용량을 보존하고 최종 사용량을 미확인으로 표시하세요.

완료된 저장 녹음이 있다면 fork해 새 세션에서 계속하세요. 그렇지 않으면 관련 저장 히스토리로 대체 세션을 만드세요. 계속하기 전에 백엔드로 미완료 작업을 확인하고, 현재 작업 상태를 복원하고, 이전 세션의 늦은 결과가 더 새로운 작업을 덮어쓰지 않도록 결과 라우팅을 업데이트하세요.

더 알아보기 (Learn more)

관련 문서: GPT-Live 시작하기, GPT-Live 프롬프팅, Delegation and tools 가이드를 함께 보면 좋아요.