OpenAI 의 대규모 저지연 음성 AI 구현 방법
요약
본 기사는 OpenAI의 대규모 실시간 음성 AI 상호작용을 구현하는 기술적 아키텍처를 설명합니다. 자연스러운 대화 경험을 위해 낮은 지연 시간과 안정적인 미디어 라운드 트립 타임을 유지하는 것이 핵심이며, 이를 위해 WebRTC 스택을 재설계했습니다. OpenAI는 기존의 다자간 통신 모델(SFU) 대신, 1:1 세션에 최적화된 '트랜시버(Transceiver)' 아키텍처를 구축하여 클라이언트 연결 상태와 미디어 처리를 중앙 집중화하고, 이를 추론 백엔드와의 통합을 용이하게 했습니다.
핵심 포인트
- 실시간 음성 AI의 핵심은 대화 속도에 맞는 낮은 지연 시간과 안정적인 미디어 라운드 트립 타임 유지입니다.
- OpenAI는 WebRTC 스택을 재설계하여 9억 명 이상의 글로벌 사용자 규모에서 발생하는 연결 설정 및 미디어 처리 문제를 해결했습니다.
- 기존 다자간 통신 모델(SFU) 대신, 1:1 상호작용에 최적화된 '트랜시버' 아키텍처를 채택했습니다.
- 트랜시버는 WebRTC 세션의 모든 상태(ICE, DTLS 핸드셰이크 등)를 소유함으로써, 미디어 처리를 추론 백엔드와 통합하는 핵심 엔드포인트 역할을 합니다.
음성 AI 가 자연스럽다고 느끼려면 대화의 속도가 말의 속도와 일치해야 합니다. 네트워크가 방해할 때 사람들은 즉시 어색한 침묵, 잘라진 중단, 또는 지연된 반입을 듣습니다. 이는 ChatGPT 음성, Realtime API 를 구축하는 개발자, 상호작용 워크플로우에서 작동하는 에이전트, 사용자가 계속 말하고 있는 동안 오디오를 처리해야 하는 모델 모두에게 중요합니다.
OpenAI 의 규모에서는 이것이 세 가지 구체적인 요구사항으로 번역됩니다:
- 9 억 명 이상의 주당 활성 사용자에 대한 글로벌 접근성
- 세션이 시작되는 즉시 사용자가 말을 시작할 수 있도록 빠른 연결 설정
- 낮은 지연 시간과 패킷 손실로 인한 낮고 안정적인 미디어 라운드 트립 타임, 턴 테이크가 날카롭게 느껴지도록
OpenAI 의 실시간 AI 상호작용을 담당하는 팀은 최근 WebRTC 스택을 재설계하여 규모에서 충돌하기 시작한 세 가지 제약을 해결했습니다: 한 세션당 하나의 포트를 사용하는 미디어 종말 처리는 OpenAI 인프라에 잘 맞지 않으며, 상태 기반 ICE (Interactive Connectivity Establishment) 와 DTLS (Datagram Transport Layer Security) 세션은 안정적인 소유가 필요하며, 글로벌 라우팅은 첫 번째 홉 지연 시간을 낮게 유지해야 합니다. 이 게시물에서는 클라이언트에서 표준 WebRTC 동작을 보존하면서 OpenAI 의 인프라 내부에서 패킷이 어떻게 라우팅되는지 변경하는 우리가 구축한 리레이 플러스 트랜시버 아키텍처를 살펴보겠습니다.
WebRTC 는 브라우저, 모바일 앱 및 서버 간에 저지연 오디오, 비디오 및 데이터를 전송하기 위한 오픈 표준입니다. 종종 피어 투 피어 호출과 관련이 있지만, 클라이언트에서 서버로 실시간 시스템을 구축하는 실용적인 기반이기도 합니다. 이는 상호작용 미디어의 어려운 부분을 표준화하기 때문입니다: 연결 설정을 위한 ICE 와 NAT (Network Address Translation) 트래버스, 암호화된 전송을 위한 DTLS 와 SRTP (Secure Real-time Transport Protocol), 오디오 압축 및 해독을 위한 코덱 협상, 품질 제어를 위한 RTCP (Real-time Transport Control Protocol), 그리고 에코 제거와 저지터 버퍼링과 같은 클라이언트 측 기능.
이 표준화는 AI 제품에 중요합니다. WebRTC 가 없다면 각 클라이언트는 NAT 를 가로지르는 연결 설정, 미디어 암호화, 코덱 협상 (전송 및 해독을 위한 선택된 코더/디코더), 그리고 변화하는 네트워크 조건에 적응하기 위해 다른 답변이 필요합니다. WebRTC 는 이미 브라우저와 모바일 플랫폼에서 구현된 프로토콜 스택을 기반으로 구축할 수 있으며, 실시간 미디어를 모델에 연결하는 인프라에 대한 우리의 작업을 집중할 수 있습니다.
우리는 또한 WebRTC 생태계를 기반으로 합니다: 성숙한 오픈 소스 구현과 브라우저, 모바일 앱 및 서버의 상호 운용성을 유지하는 표준 작업. Justin Uberti (WebRTC 의 원래 아키텍트 중 한 명) 와 Sean DuBois (Pion 의 창립자이자 관리자) 의 기초 작업은 저 수준 전송, 암호화 및 혼잡 제어 동작을 재발명하기보다 우리 팀이 검증된 미디어 인프라를 기반으로 구축할 수 있게 했습니다. Justin 과 Sean 은 이제 OpenAI 에서 동료로 일하고 있으며, 우리가 WebRTC 와 실시간 AI 를 더 가깝게 가져오도록 안내하는 데 도움을 주는 것을 운명이었습니다.
AI 에 있어 가장 중요한 속성은 오디오가 연속적인 스트림으로 도착한다는 것입니다. 말한 에이전트는 사용자가 계속 말하고 있는 동안 전사, 추론, 도구 호출 또는 음성 생성을 시작할 수 있습니다. 이는 대화형 시스템과 푸시 투 토크 시스템의 차이입니다.
WebRTC 를 선택한 후 다음으로 고려된 문제는 WebRTC 연결을 어디에서 종료할 것인가 (예: 엣지 에서 수용하고 소유하는 것) 및 이를 추론 백엔드와 어떻게 연결할 것인가였습니다. 종결은 실시간 세션 상태, 미디어 전송, 라우팅, 지연 시간, 그리고 실패 격리를 처리하는 방식을 결정하기 때문에 중요합니다.
SFU(Selective Forwarding Unit, 선택적 전달 장치) 는 각 참여자로부터 하나의 WebRTC 스트림을 수신하여 다른 참여자들에게 선택적으로 스트림을 전달하는 미디어 서버입니다. 이 모델에서 SFU 는 각 참여자에게 별도의 WebRTC 연결을 종료하며 AI 는 세션의 또 다른 참여자로 참여합니다. 이는 그룹 통화, 강의실, 협업 회의 등 본래 다자간인 제품에 적합할 수 있습니다. 오디오 코덱, RTCP 메시지, 데이터 채널, 녹화 및 스트림별 정책이 한 곳에 통합됩니다.
클라이언트에서 AI 로 가는 제품에서도 SFU 는 신호 전달, 미디어 라우팅, 녹화, 관찰 가능성 및 인간 교대 추가 등 미래 확장을 위한 단일 검증된 시스템을 재사용할 수 있게 함으로써 종종 기본 시작점으로 사용됩니다.
우리 작업은 다릅니다. 대부분의 세션은 1:1 입니다—one 사용자가 한 모델과 대화하거나, 하나의 애플리케이션이 실시간 에이전트와 대화하며, 각 턴마다 지연 시간 민감도가 있습니다. 이러한 형태의 트래픽을 위해 우리는 transceiver(변송기) 모델을 선택했습니다: WebRTC 엣지 서비스는 클라이언트 연결을 종료한 후 미디어 및 이벤트를 추론, 전사, 음성 생성, 도구 사용, 오케스트레이션 등 모델에 더 간단한 내부 프로토콜로 변환합니다.
이 설계에서 transceiver 는 ICE 연결성 확인, DTLS 핸드셰이크, SRTP 암호화 키, 세션 라이프사이클을 포함한 WebRTC 세션 상태를 소유하는 유일한 서비스입니다. 여기서 "종결" 은 transceiver 가 이러한 핸드셰이크를 완료하고 미디어를 암호화하거나 복호화하는 엔드포인트라는 것을 의미합니다. 해당 상태를 한 곳에 유지함으로써 세션 소유를 추론하기 쉬워졌으며, 백엔드 서비스가 WebRTC 페어 자체처럼 행동하지 않고 일반 서비스처럼 확장할 수 있게 했습니다.
transceiver 모델을 선택한 후 우리는 Pion 을 기반으로 신호 전달 및 미디어 종결을 모두 처리하는 단일 Go 서비스를 구현했습니다. 이는 ChatGPT 음성, Realtime API 의 WebRTC 엔드포인트 및 여러 연구 프로젝트에 구동력을 제공합니다.
운영적으로 transceiver 서비스는 두 가지 작업을 수행합니다:
- 신호 전달: SDP 협상, 코덱 선택, ICE 인증서 및 세션 설정
- 미디어: 다운스트림 WebRTC 연결을 종료하고 추론 및 오케스트레이션용 백엔드 서비스에 대한 업스트림 연결을 유지
우리는 이 서비스가 우리 인프라의 나머지 부분과 같이 작동하기를 원했습니다: Kubernetes 에서 워크로드를 확장하거나 축소할 수 있으며, 수요에 따라 호스트를 이동할 수 있습니다. 그러나 일반적인 한 포트당 세션 WebRTC 모델은 추가, 제거 또는 재스케줄링이 이루어지는 포드 (pod) 에 대한 대규모 공개 UDP 포트 범위를 노출하고 관리해야 하는 어려움 때문에 이 환경에 잘 맞지 않습니다.
첫 번째 문제는 한 포트당 세션 모델 자체였습니다. 고 병행률에서는 매우 큰 UDP 포트 범위를 노출하고 관리해야 합니다.
클라우드 로드 밸런서와 Kubernetes 서비스는 각 서비스에 대해 수만 개의 공개 UDP 포트를 기반으로 설계되지 않았습니다. 추가적인 포트 범위는 로드 밸런서 구성, 건강 체크, 방화벽 정책, 배포 안전성 등에서 운영 복잡성을 증가시킵니다.
3 - 대규모 UDP 포트 범위는 보안이 어렵습니다. 이는 외부로 접근 가능한 표면적을 확대하고 네트워크 정책을 감사하기 어렵게 만들기 때문입니다.
그들은 또한 자동 확장 (autoscaling) 에 적합하지 않습니다. Kubernetes 는 포드가 지속적으로 추가되고 제거되며 재배치됩니다. 각 포드가 큰 안정적인 포트 범위를 예약하고 광고하도록 요구하면 그 탄력성은 취약해집니다.
4
이러한 이유로 많은 WebRTC 시스템은 서버당 하나의 UDP 포트로, 그 뒤에 애플리케이션 레벨의 다중화 (demultiplexing) 를 도입하는 방향으로 이동합니다.5
서버당 단일 포트 설계는 포트 수 문제를 해결하지만, 플레트 (fleet) 전체에 걸쳐 각 세션의 소유권을 보존하는 2 차 문제도 발생시킵니다.
ICE 와 DTLS 는 상태ful 프로토콜입니다. 세션을 생성한 프로세스는 해당 세션의 패킷을 계속 수신해야 연결성 체크를 검증하고, DTLS 핸드셰이크를 완료하며, SRTP 을 암호화해내고, ICE 재시작과 같은 이후 세션 변경을 처리할 수 있습니다. 동일한 세션의 패킷이 다른 프로세스에 도착하면 설정이 실패하거나 미디어가 끊길 수 있습니다.
이는 작은 고정 UDP 표면적을 공개 인터넷에 노출시키되, 여전히 해당 WebRTC 세션을 소유하는 트랜시비어 (transceiver) 로 모든 패킷을 라우팅하는 것을 목표로 했습니다.
여러 방법 중 하나를 평가했습니다. TURN (Traversal Using Relays around NAT) 을 포함한 것으로, 에지 릴레이에서 클라이언트 할당을 종료하고 대신 트래픽을 전달합니다.2
| Unique IP:port per session (also known as native direct UDP) | Direct client-to-server media path No forwarding layer in the data path | Requires one public UDP port per session Large port ranges are difficult to expose and secure Poor fit for Kubernetes and cloud load balancers |
| Unique IP:port per server | Much smaller public UDP footprint than per-session exposure One shared socket per server can demultiplex many sessions | Works cleanly on a single host, but not across a shared load-balanced fleet by itself Session demultiplexing on a single host only helps after a packet reaches that host; across a load-balanced fleet, the first packet can still land on the wrong instance, so you still need a deterministic way to steer each session to the process that owns it |
| TURN relay (protocol-terminating) | Clients only need to reach the TURN relay address and port Can centralize policy at the edge | TURN allocations add setup round trips Moving or recovering allocations across TURN servers is still difficult |
| Stateless forwarder + stateful terminator (OpenAI's relay + transceiver) | Small public UDP footprint Transceiver still owns the full WebRTC session | Adds one forwarding hop before media reaches the owning transceiver Requires custom coordination between relay and transceiver |
우리가 배포한 아키텍처는 패킷 라우팅과 프로토콜 종료를 분리합니다. 세션 설정은 여전히 트랜시비어에 도달하지만, 미디어는 먼저 릴레이를 통해 들어옵니다. 릴레이는 작은 공개 표면적을 가진 경량 UDP 전달 계층이며, 트랜시비어는 그 뒤에 있는 상태ful WebRTC 엔드포인트입니다.
릴레이는 미디어를 복호화하거나, ICE 상태 머신을 실행하거나, 코덱 협상 (codec negotiation) 에 참여하지 않습니다. 릴레이는 목적지를 선택하기 위해 충분한 패킷 메타데이터를 읽은 후, 해당 세션을 소유하는 트랜시미터로 패킷을 전달합니다. 트랜시미터는 여전히 정상적인 WebRTC 흐름을 볼 수 있으며, 모든 프로토콜 상태를 소유합니다. 클라이언트의 관점에서는 WebRTC 세션에 대한 어떤 변화도 없습니다.
첫 번째 패킷 라우팅은 이 설정의 핵심 단계입니다. 릴레이는 패킷 경로 자체에서 세션이 존재하기 전에 클라이언트로부터 첫 번째 패킷을 라우팅해야 하며, 외부 조회 서비스에서 일시 정지하는 것이 아니라야 합니다.
각 WebRTC 세션에는 프로토콜 네이티브 라우팅 훅이 이미 포함되어 있습니다: ICE 사용자명 프래그먼트 또는 ufrag. 이는 세션 설정 기간 동안 교환되는 짧은 식별자로, STUN 연결성 확인 (connectivity checks) 에서 반향됩니다. 우리는 서버 측 ufrag 을 생성하여 릴레이가 목적지 클러스터와 소유 트랜시미터를 추론하기 위해 충분한 라우팅 메타데이터만 포함되도록 합니다.
신호링 (signaling) 기간 동안, 트랜시미터는 세션 상태를 할당하고 SDP 답변에서 공유 릴레이 VIP 와 UDP 포트를 반환합니다. VIP 는 릴레이 플렛을 프론트하는 가상 IP 주소입니다. 포트와 함께 사용하면 클라이언트는 여러 릴레이 인스턴스가 그 뒤에 위치하더라도 단일 안정적인 목적지, 예를 들어 203.0.113.10:3478 를 얻습니다. 클라이언트의 첫 번째 미디어 경로 패킷은 일반적으로 STUN (Session Traversal Utilities for NAT) 바인딩 요청이며, ICE 는 이를 사용하여 광고된 주소에 도달할 수 있는지 확인합니다.
릴레이는 해당 첫 번째 STUN 패킷을 충분히 파싱하여 서버 ufrag 을 읽거나, 라우팅 힌트를 디코딩하거나, 소유 트랜시미터로 패킷을 전달합니다. 각 트랜시미터는 공유 UDP 소켓에 경청하므로, 세션당 하나의 소켓이 아닌 내부 IP:포트에 바인드된 하나의 운영체제 엔드포인트를 의미합니다. 릴레이가 클라이언트의 소스 IP:포트에서 해당 트랜시미터 목적지로 세션을 생성한 후, 이후 DTLS, RTP, RTCP 패킷은 ufrag 을 다시 디코딩하지 않고 세션 내에서 흐릅니다.
릴레이의 세션은 의도적으로 최소로 구성되며, 패킷 전달을 알리기 위한 메모리 내 세션과 모니터링 및 세션 만료 및 정리 (cleanup) 를 위한 필요한 카운터와 타이머만 포함합니다. 이 설계 선택은 패킷 라우팅을 직접 패킷 경로에 유지합니다. 릴레이가 재시작하여 세션을 잃으면, 다음 STUN 패킷이 ufrag 라우팅 힌트에서 세션을 다시 구축합니다. 더 신뢰성을 높이기 위해, 루트가 설정된 후 <클라이언트 IP + 포트, 트랜시미터 IP + 포트> 매핑을 저장하기 위해 Redis 캐시를 사용하여 다음 STUN 패킷 도착 전에 훨씬 일찍 복구할 수 있습니다.
우리가 공개 UDP 표면 (surface) 을 소수의 안정적인 주소와 포트로 줄인 후, 전역적으로 동일한 릴레이 패턴을 배포할 수 있었습니다. 글로벌 릴레이 (Global Relay) 는 모두 동일한 패킷 전달 동작을 구현하는 지리 분산 릴레이 인그레스 포인트 플렛입니다.
광범위한 지리적 인그레스는 클라이언트에서 OpenAI 로의 첫 번째 홉을 단축합니다. 패킷은 지리와 네트워크 토폴로지 모두에서 사용자에게 가까운 릴레이에서 우리 네트워크에 진입할 수 있기 때문입니다. 즉, 트래픽이 our backbone 에 도달하기 전에 더 낮은 지연 시간 (latency), 더 적은 제터 (jitter) 및 더 적은 피해야 할 손실 버스트 (loss bursts) 를 의미합니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 OpenAI Blog의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기