전송 방식
세션이 실행되는 위치와 원문 미디어 또는 이벤트를 어느 정도까지 제어해야 하는지에 따라 전송 방식을 선택합니다.
| 시나리오 | 권장 전송 방식 | 이유 |
|---|---|---|
| 브라우저 음성 대 음성 앱 | OpenAIRealtimeWebRTC | 가장 간편한 방식입니다. SDK가 마이크 캡처, 재생, WebRTC 연결을 대신 관리합니다. |
| React Native 모바일 앱 | 네이티브 WebRTC 기반의 앱 소유 RealtimeTransportLayer | SDK는 React Native 패키지 조건을 제공하며, 앱이 네이티브 WebRTC, 권한, 오디오 라우팅, 수명 주기를 관리합니다. |
| 서버 측 음성 루프 또는 사용자 정의 오디오 파이프라인 | OpenAIRealtimeWebSocket | 오디오 캡처와 재생을 이미 직접 제어하고 있으며 이벤트에 직접 접근하려는 경우에 적합합니다. |
| SIP 또는 전화 통신 브리지 | OpenAIRealtimeSIP | callId를 통해 RealtimeSession을 기존 SIP 시작 Realtime 통화에 연결합니다. |
| Cloudflare Workers / workerd | Cloudflare 확장 전송 방식 | workerd에서는 전역 WebSocket 생성자로 아웃바운드 WebSocket을 열 수 없습니다. |
| Twilio의 공급자별 전화 통화 흐름 | Twilio 확장 전송 방식 | Twilio 오디오 전달과 인터럽션(중단 처리) 동작을 대신 처리합니다. |
기본 전송 계층
섹션 제목: “기본 전송 계층”브라우저 기본 선택지인 WebRTC
섹션 제목: “브라우저 기본 선택지인 WebRTC”기본 브라우저 전송 방식은 WebRTC를 사용합니다. 마이크에서 오디오를 캡처하고 자동으로 재생하므로 빠른 시작에서는 임시 토큰과 session.connect(...)만으로 연결할 수 있습니다.
이 방식에서 session.connect()는 완료되기 전에 초기 세션 설정이 session.updated로 확인될 때까지 기다리려고 하므로 오디오 스트리밍이 시작되기 전에 instructions와 tools가 적용됩니다. 확인 응답이 도착하지 않는 경우를 위한 시간 초과 대체 동작도 있습니다.
자체 미디어 스트림이나 오디오 요소를 사용하려면 세션을 생성할 때 OpenAIRealtimeWebRTC 인스턴스를 제공합니다.
import { RealtimeAgent, RealtimeSession, OpenAIRealtimeWebRTC,} from '@openai/agents/realtime';
const agent = new RealtimeAgent({ name: 'Greeter', instructions: 'Greet the user with cheer and answer questions.',});
async function main() { // Keep a handle on the stream you pass in. The transport does not stop a caller-supplied // stream on close(), so your application owns it and is responsible for ending it. const mediaStream = await navigator.mediaDevices.getUserMedia({ audio: true, });
const transport = new OpenAIRealtimeWebRTC({ mediaStream, audioElement: document.createElement('audio'), });
const customSession = new RealtimeSession(agent, { transport });
// Later, once the application is actually done with the microphone, release it. Leaving this // out keeps the microphone indicator on even after the session closes. // mediaStream.getTracks().forEach((track) => track.stop());}OpenAIRealtimeWebRTC에 전달한 mediaStream의 소유권은 애플리케이션에 유지됩니다. 전송 방식을 닫아도 해당 스트림의 트랙은 중지되지 않으므로 레벨 미터, 레코더 또는 재연결에 계속 사용할 수 있습니다. 애플리케이션에서 스트림 사용을 실제로 마쳤을 때 트랙의 stop()을 호출합니다. 전송 방식이 기본 마이크를 직접 열었다면 close()를 호출할 때 SDK가 소유한 해당 트랙은 계속 중지됩니다.
더 낮은 수준에서 사용자 정의하려는 경우 OpenAIRealtimeWebRTC는 changePeerConnection도 허용하므로 offer가 생성되기 전에 새로 생성된 RTCPeerConnection을 검사하거나 교체할 수 있습니다.
앱 소유 전송 방식을 사용하는 React Native
섹션 제목: “앱 소유 전송 방식을 사용하는 React Native”@openai/agents-core와 @openai/agents-realtime은 React Native 패키지 조건을 게시하므로 Metro가 Node.js 내장 기능 없이 이식 가능한 SDK shim을 해석할 수 있습니다. 내장 OpenAIRealtimeWebRTC는 브라우저 WebRTC 전역 객체와 DOM 오디오 요소에 의존하므로 브라우저 전송 방식으로 유지됩니다. React Native에서는 네이티브 WebRTC 구현을 설치하고 직접 관리한 다음, 사용자 정의 RealtimeTransportLayer를 통해 RealtimeSession에 연결합니다.
examples/realtime-react-native Expo 개발 빌드 예제에서는 앱 소유 react-native-webrtc 전송 방식, 마이크 권한, 오디오 라우팅, 서버 측 임시 토큰 엔드포인트를 보여 줍니다. react-native-webrtc에 네이티브 코드가 포함되어 있으므로 Expo Go는 지원되지 않습니다. 표준 OpenAI API 키는 서버에 보관하고 수명이 짧은 임시 클라이언트 토큰만 모바일 앱에 전달합니다.
서버 기본 선택지인 WebSocket
섹션 제목: “서버 기본 선택지인 WebSocket”세션을 생성할 때 transport: 'websocket' 또는 OpenAIRealtimeWebSocket 인스턴스를 전달하면 WebRTC 대신 WebSocket 연결을 사용합니다. 이 방식은 서버 측 사용 사례, 전화 통신 브리지, 사용자 정의 오디오 파이프라인에 적합합니다.
WebSocket 방식에서 session.connect()는 소켓이 열리고 초기 설정이 전송되면 완료됩니다. 이에 대응하는 session.updated 이벤트는 약간 늦게 도착할 수 있으므로 connect()가 완료되었다고 해서 해당 업데이트가 이미 되돌아왔다고 가정해서는 안 됩니다.
import { RealtimeAgent, RealtimeSession } from '@openai/agents/realtime';
const agent = new RealtimeAgent({ name: 'Greeter', instructions: 'Greet the user with cheer and answer questions.',});
const myRecordedArrayBuffer = new ArrayBuffer(0);
const wsSession = new RealtimeSession(agent, { transport: 'websocket', model: 'gpt-realtime-2.1',});await wsSession.connect({ apiKey: process.env.OPENAI_API_KEY! });
wsSession.on('audio', (event) => { // event.data is a chunk of PCM16 audio});
wsSession.sendAudio(myRecordedArrayBuffer);원문 PCM16 오디오 바이트는 원하는 녹음 및 재생 라이브러리로 처리합니다.
고급 연동을 위해 OpenAIRealtimeWebSocket은 자체 소켓 구현을 제공할 수 있는 createWebSocket()과 사용자 정의 커넥터가 소켓을 연결된 상태로 전환하는 역할을 담당할 때 사용하는 skipOpenEventListeners를 허용합니다. @openai/agents-extensions의 Cloudflare 전송 방식은 이러한 훅을 기반으로 구축되었습니다.
통화 공급자와 전화 통신 브리지용 SIP
섹션 제목: “통화 공급자와 전화 통신 브리지용 SIP”기존 SIP 시작 Realtime 통화에 RealtimeSession을 연결하려면 OpenAIRealtimeSIP를 사용합니다. 이는 SIP를 인식하는 경량 전송 방식입니다. 오디오는 SIP 통화 자체에서 처리되며, callId를 통해 SDK 세션을 연결합니다.
OpenAIRealtimeSIP.buildInitialConfig()로 초기 세션 설정을 생성하여 수신 통화를 수락합니다. 이렇게 하면 SIP 초대와 이후 SDK 세션이 동일한 기본값으로 시작됩니다.OpenAIRealtimeSIP전송 방식을 사용하는RealtimeSession을 연결하고 공급자 웹훅에서 발급한callId로 연결합니다.- 공급자별 미디어 전달 또는 이벤트 브리징이 필요한 경우 Twilio 확장과 같은 연동 전송 방식을 사용합니다.
import OpenAI from 'openai';import { OpenAIRealtimeSIP, RealtimeAgent, RealtimeSession, type RealtimeSessionOptions,} from '@openai/agents/realtime';
const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY!, webhookSecret: process.env.OPENAI_WEBHOOK_SECRET!,});
const agent = new RealtimeAgent({ name: 'Receptionist', instructions: 'Welcome the caller, answer scheduling questions, and hand off if the caller requests a human.',});
const sessionOptions: Partial<RealtimeSessionOptions> = { model: 'gpt-realtime-2.1', config: { audio: { input: { turnDetection: { type: 'semantic_vad', interruptResponse: true }, }, }, },};
export async function acceptIncomingCall(callId: string): Promise<void> { const initialConfig = await OpenAIRealtimeSIP.buildInitialConfig( agent, sessionOptions, ); await openai.realtime.calls.accept(callId, initialConfig);}
export async function attachRealtimeSession( callId: string,): Promise<RealtimeSession> { const session = new RealtimeSession(agent, { transport: new OpenAIRealtimeSIP(), ...sessionOptions, });
session.on('history_added', (item) => { console.log('Realtime update:', item.type); });
await session.connect({ apiKey: process.env.OPENAI_API_KEY!, callId, });
return session;}Cloudflare Workers와 workerd
섹션 제목: “Cloudflare Workers와 workerd”Cloudflare Workers와 기타 workerd 런타임에서는 전역 WebSocket 생성자를 사용하여 아웃바운드 WebSocket을 열 수 없습니다. 내부적으로 fetch() 기반 업그레이드를 수행하는 확장 패키지의 Cloudflare 전송 방식을 사용합니다.
import { CloudflareRealtimeTransportLayer } from '@openai/agents-extensions';import { RealtimeAgent, RealtimeSession } from '@openai/agents/realtime';
const agent = new RealtimeAgent({ name: 'My Agent',});
// Create a transport that connects to OpenAI Realtime via Cloudflare/workerd's fetch-based upgrade.const cfTransport = new CloudflareRealtimeTransportLayer({ url: 'wss://api.openai.com/v1/realtime?model=gpt-realtime-2.1',});
const session = new RealtimeSession(agent, { // Set your own transport. transport: cfTransport,});전체 설정은 Cloudflare용 Realtime 에이전트를 참조하세요.
Twilio 전화 통화
섹션 제목: “Twilio 전화 통화”원문 WebSocket 또는 @openai/agents-extensions의 전용 Twilio 전송 방식을 사용하여 RealtimeSession을 Twilio에 연결할 수 있습니다. SDK가 Twilio Media Streams의 인터럽션(중단 처리) 타이밍과 오디오 전달을 처리하도록 하려면 전용 전송 방식이 더 나은 기본 선택입니다.
전체 설정은 Twilio용 Realtime 에이전트를 참조하세요.
자체 전송 방식
섹션 제목: “자체 전송 방식”다른 음성 대 음성 API를 사용하거나 자체 사용자 정의 전송 메커니즘이 있는 경우 RealtimeTransportLayer 인터페이스를 구현하고 RealtimeTransportEventTypes 이벤트를 직접 발생시킬 수 있습니다.
필요시 원문 Realtime 이벤트 접근
섹션 제목: “필요시 원문 Realtime 이벤트 접근”기반 Realtime API에 더 직접적으로 접근하려는 경우 두 가지 옵션이 있습니다.
옵션 1 - 전송 계층 접근
섹션 제목: “옵션 1 - 전송 계층 접근”RealtimeSession의 모든 기능을 계속 활용하면서 session.transport를 통해 전송 계층에 접근할 수 있습니다.
전송 계층은 수신하는 모든 이벤트를 * 이벤트로 발생시키며, sendEvent()를 사용해 원문 이벤트를 전송할 수 있습니다. * 리스너는 현재 SDK 스키마가 인식하지 못하는 필드를 포함하여 원래 파싱된 JSON 페이로드의 구조화된 복제본을 수신합니다. 명명된 이벤트 리스너와 파생 이벤트 리스너는 검증되고 정규화된 이벤트 객체를 계속 수신합니다. 이는 session.update, response.create, response.cancel과 같은 낮은 수준의 작업을 위한 우회 수단입니다.
import { RealtimeAgent, RealtimeSession } from '@openai/agents/realtime';
const agent = new RealtimeAgent({ name: 'Greeter', instructions: 'Greet the user with cheer and answer questions.',});
const session = new RealtimeSession(agent, { model: 'gpt-realtime-2.1',});
session.transport.on('*', (event) => { // Event received from the underlying Realtime transport});
// Send any valid client event, for example, to trigger a new responsesession.transport.sendEvent({ type: 'response.create', // ...});옵션 2 - 전송 계층만 사용
섹션 제목: “옵션 2 - 전송 계층만 사용”도구 자동 실행, 가드레일 또는 로컬 기록 관리가 필요하지 않은 경우 전송 계층을 연결과 인터럽션(중단 처리)만 관리하는 “경량” 클라이언트로 사용할 수도 있습니다.
import { OpenAIRealtimeWebRTC } from '@openai/agents/realtime';
const client = new OpenAIRealtimeWebRTC();const audioBuffer = new ArrayBuffer(0);
await client.connect({ apiKey: '<api key>', model: 'gpt-realtime-2.1', initialSessionConfig: { instructions: 'Speak like a pirate', outputModalities: ['audio'], audio: { input: { format: 'pcm16', }, output: { format: 'pcm16', voice: 'ash', }, }, },});
// Listen for audio when you manage playback yourselfclient.on('audio', (newAudio) => {});
client.sendAudio(audioBuffer);