OpenAI 의 WebRTC 문제
요약
이 글은 OpenAI가 음성 AI에 WebRTC를 사용하는 것에 대해 비판적인 시각을 제시하며, WebRTC의 기술적 한계와 부적합성을 주장합니다. 필자는 자신이 WebRTC 전문가임을 강조하며, WebRTC가 네트워크 조건이 열악할 때 오디오 패킷을 공격적으로 떨어뜨리거나 지연 시간을 인위적으로 추가하는 경향이 있어 음성 AI의 실시간 대화 경험에 부정적인 영향을 미친다고 지적합니다. 대신, TTS(Text-to-Speech) 방식은 버퍼링과 스트리밍을 통해 더 안정적이고 사용자 친화적인 오디오 품질을 제공할 수 있다고 주장하며, WebRTC가 가진 근본적인 아키텍처 문제를 비판합니다.
핵심 포인트
- WebRTC는 음성 AI의 실시간 대화(Conversational) 환경에 적합하지 않다.
- WebRTC는 네트워크 불안정 시 오디오 패킷을 공격적으로 드롭하여 음질 저하를 유발한다.
- WebRTC는 지연 시간 평활화를 위해 동적 버퍼링을 사용하는데, 이는 스트리밍의 자연스러움을 해친다.
- TTS(Text-to-Speech) 기반 방식은 충분한 버퍼링과 스트리밍을 통해 WebRTC보다 더 안정적인 오디오 품질을 제공할 수 있다.
- WebRTC는 클라이언트 주소 변경 등 네트워크 환경 변화에 취약하여 포트 관련 문제도 발생한다.
OpenAI 의 WebRTC 문제
OpenAI 는 며칠 전 기술 블로그를 게시했습니다. 이 블로그 포스트는 제가 기대 이상으로 자극을 주었습니다. 키보드에 살살 손가락을 두드려야 할 것 같습니다.
OpenAI 를 모방하지 마세요.
저는 음성 AI 에 WebRTC 를 사용해야 한다고 생각하지 않습니다. WebRTC 는 문제입니다.
Me
6 년 전, 저는 Twitch 에서 WebRTC SFU 를 작성했습니다. 원래 Pion (Go) 을 사용했는데, 벤치마킹 결과 너무 느리다는 것을 발견했기 때문에 포크했습니다. 물론 제가 다시 썼습니다! 모든 프로토콜을 다시 작성했습니다.
1 년 전, 저는 Discord 에 있었고 WebRTC SFU 를 Rust 로 다시 작성했습니다. 물론 제가 다시 썼습니다! 아마도 당신은 트렌드를 감지하고 있을 것입니다.
흥미로운 사실: WebRTC 는 초기 2000 년대에 거슬러 올라가는 약 45 개의 RFC 와 일부 기술적 초안인 de-facto 표준 (예: TWCC, REMB) 을 포함합니다.
모두 구현해야 할 때 흥미로운 사실이 아닙니다.
저는 Certified WebRTC Expert로 간주할 수 있습니다.
그 이유는 제가 다시 WebRTC 를 사용하고 싶지 않기 때문입니다.
Product Fit
저는 조금 속도를 내어 뜨거운 의견부터 시작하겠습니다. 걱정하지 마세요, OpenAI 블로그 포스트와 로드 밸런싱에 대해 바로 이야기할 것입니다, 약속합니다.
WebRTC 는 음성 AI 에 적합하지 않습니다.
하지만 이는 역설적일 수 있습니다? WebRTC 는 회의용이며, 그것은 말하기 포함합니다. 그리고 로봇은 말할 수 있죠?
WebRTC 는 너무 공격적입니다
제 OpenAI 앱을 휴대폰에서 켜고 Sky 와 인사하고 다음에 다음과 같은 말을 합니다:
차량 세척으로 걸어가야 할지 운전해야 할지?
WebRTC 는 망 조건이 열악할 때 저의 프롬프트를 감소시키고 떨어뜨립니다.
내 친구, WTF
WebRTC 는 지연 시간을 낮추기 위해 오디오 패킷을 공격적으로 떨어뜨립니다. 만약 당신이 회의 전화에서 왜곡된 오디오를 들었다면, 그것은 WebRTC baybee 입니다. 아이디어는 회의 전화가 빠른 양방향 통신에 의존하므로 오디오를 기다리는 것이 허용되지 않는다는 것입니다.
…but 사용자로 하여금 저의 느리거나 비싼 프롬프트가 정확한지 더 200ms 기다리는 것을 선호합니다. 결국 저는 바다를 끓이는 데 좋은 돈을 지불하고 있으며, 쓰레기 프롬프트는 쓰레기 응답을 의미합니다. LLM 이 특히 반응성이 좋다는 것은 아닙니다.
하지만 기다릴 수 없습니다.
브라우저에서 WebRTC 오디오 패킷을 재전송하는 것은 불가능합니다; Discord 에서 시도했습니다.
구현은 실시간 지연 시간 또는 다른 것을 위해 하드 코딩되어 있습니다.
UPDATE: 일부 WebRTC 전문가들은 이것이 기술적 문제라고 주장합니다.
아udio NACKs 를 활성화할 수 있는 것은 가능하지만, 올바른 SDP 변형 (munging) 을 찾지 못했습니다.
어쨌든, WebRTC jitter buffer 는 매우 공격적으로 작습니다.
그리고 맞습니다. Voice AI 에이전트는 결국 대화 범위로 지연 시간을 줄일 것입니다.
하지만 지연 시간 감소에는 트레이드오프가 있습니다.
의도적으로 오디오 프롬프트를 저하하는 것이 결코 가치가 있는지조차 확실하지 않습니다.
TTS 는 실시간보다 빠릅니다
마이크로폰에 말하면, OpenAI 의 수십억 개의 서버 중 하나로 전송되고, 그 후 GPU 가 텍스트 음성 변환 (text-to-speech) 을 통해 당신과 대화한다고 pretends 합니다. Neato.
예를 들어, 2 초의 GPU 가 8 초의 오디오를 생성한다고 가정해 봅시다. 이상적인 세계에서는 오디오가 생성되는 동안 (2 초 이상) 스트리밍하고 클라이언트가 재생을 시작할 것입니다 (8 초 이상). 그 방식은 네트워크 블립이 발생하면 일부 오디오가 로컬로 버퍼링됩니다. 사용자는 네트워크 블립조차 알아채지 못할 수도 있습니다.
하지만 WebRTC 는 버퍼링이 없고, 입력 시간에 기반하여 렌더링합니다.
진지하게 말하자면, 타임스탬프는 단지 제안일 뿐입니다.
비디오가 화면에 들어오면 더 짜증납니다.
이를 보상하기 위해 OpenAI 는 패킷이 렌더링될 때 정확히 도착하도록 보장해야 합니다.
그들은 오디오 패킷을 전송 전에 추가적인 잠금 (sleep) 을 삽입해야 합니다.
하지만 네트워크 혼잡이 발생하면, oops, 우리는 그 오디오 패킷을 잃었고, 그것은 다시 전송되지 않습니다.
OpenAI 는 실제로 인위적인 지연 시간을 도입하고, "지연 시간을 낮추기 위해" 공격적으로 패킷을 떨어뜨립니다.
이는 YouTube 비디오를 공유하는 대신 버퍼링하는 것과 같습니다.
품질이 저하될 것입니다.
흥미로운 사실: WebRTC 는 실제로 지연 시간을 추가합니다.
그것은 많지 않지만, WebRTC 는 오디오 (20ms 에서 200ms) 로 anywhere sized 될 수 있는 동적 jitter buffer 를 가지고 있습니다.
이는 네트워크 jitter 를 평활화하기 위한 것이지만, 실시간보다 빠르게 전송한다면 모든 것은 필요하지 않습니다.
Ports Ports Ports
좋습니다. 이제 OpenAI article 의 기술 고기에 대해 이야기해 봅시다.
우리는 더 이상 배에 있지 않지만, 포트를 이야기해 봅시다.
TCP 서버를 호스팅할 때, 포트 (예: HTTPS 를 위한 443) 를 열고 들어오는 연결을 기다립니다.
TCP 클라이언트는 임의적으로 일시적인 포트를 선택하고, 연결은 소스/대상 IP/포트로 식별됩니다.
예를 들어, 연결은 123.45.67.89:54321 -> 192.168.1.2:443 로 식별될 수 있습니다
하지만 작은 문제가 하나 있습니다… 클라이언트 주소가 바뀔 수 있기 때문입니다. 전화기가 WiFi 에서 셀룰러로 전환되면 IP 가 바뀌고, NAT 는 물론 임의로 소스 IP/포트를 변경할 수 있습니다.
이런 일이 발생하면 연결은 안녕, 새로운 연결을 세팅해야 합니다.
그것은 최소 2-3 RTT 를 필요로 하는 비싼 TCP + TLS 핸드셰이크를 의미합니다.
사용자들은 라이브 스트리밍 중 네트워크 하프업을 확실히 느끼게 됩니다.
WebRTC 는 이 문제를 해결하려고 시도했지만, 상황을 더 나쁘게 만들었습니다. 진지하게.
WebRTC 구현은 각 연결마다 임시 포트를 할당해야 한다고 의미합니다.
그렇게 하면 WebRTC 세션은 목적지 IP/포트로만 식별될 수 있습니다; 소스는 무관합니다.
소스 IP/포트가 바뀐다면, 오호, 여전히 밥입니다. 목적지 포트가 동일하기 때문입니다.
하지만 OpenAI 는 이를 규모에 문제가 있음을 입증했습니다.
- 서버는 사용 가능한 포트의数量有限입니다.
- 방화벽은 임시 포트를 차단하는 것을 좋아합니다.
- Kubernetes 를 놀라게 합니다
IPv6 를 우회할 수 있을지 모르지만, IDK 시도한 적이 없습니다. Twitch 는 IPv6 를 지원하지도 않았습니다…
필요에 의한 해킹
따라서 대부분의 서비스는 WebRTC 규격을 무시하게 됩니다. 당연합니다. 우리는 여러 연결을 단일 포트로 멀티플렉싱합니다.
Twitch 에서 저는 UDP:443 에 WebRTC 서버를 호스팅했습니다.
그것은 HTTPS/QUIC 포트라고 의미하지만, 거짓말을 해서 더 많은 방화벽을 통과할 수 있었습니다.
예를 들어 Amazon 기업 네트워크는 ~30 개 포트 외 모든 것을 차단했습니다.
Discord 는 50000-50032 포트를 사용합니다. 각 CPU 코어 하나에 하나의 포트입니다. 결과적으로 더 많은 기업 네트워크에서 차단됩니다. 하지만, 만약 Amazon 기업 네트워크의 Discord 음성 통화 중이라면, 아마도 오래 머물지 않을 것입니다.
하지만, 거대한 문제.
WebRTC 는 사실 trenchcoat 를 입은 여러 표준들의 집합이며, 그중 5 개는 UDP 를 직접 사용합니다. 패킷이 어떤 프로토콜을 사용하는지 파악하는 것은 어렵지 않지만, 각 패킷을 라우팅하는 방법을 알아내야 합니다.
STUN: 우리는 고유한 ufrag 을 선택하고 라우팅할 수 있습니다.SRTP/SRTCP: 브라우저는 무작위 ssrc(u32) 를 선택합니다… 우리가 보통 라우팅할 수 있습니다.DTLS: 오호. RFC9146 가 광범위한 지원을 받기를 기도합니다.TURN: IDK 구현한 적이 없습니다.
따라서 OpenAI 는 STUN 만 사용합니다:
프로토콜 종료 없음: Relay 는 STUN 헤더/ufrag 만 파싱하며, DTLS, RTP, RTCP 를 위한 캐시된 상태를 사용하여 패킷을 불투명하게 유지합니다.
이는 다음과 같은 긍정적인 표현입니다:
사용자의 소스 IP/포트가 결코 변하지 않기를 진심으로 바랍니다. 왜냐하면 우리는 그 기능을 깨뜨렸기 때문입니다.
OpenAI 규모에서 어떤 것이든 로드 밸런싱은 인상적입니다. 하지만 그들의 커스텀 로드 밸런싱은 해킹입니다.
하지만 필요한 해킹입니다. 왜냐하면 핵심 프로토콜에 문제가 있기 때문입니다.
재미로운 사실: 브라우저는 ssrc 를 임의로 생성할 수 있습니다.
충돌이 발생하고 소스 IP/포트 매핑이 없다면, 디스코드는 가능한 모든 암호화 키로 패킷을 해독하려고 시도합니다. 키가 작동하면, 우리는 연결을 식별했습니다!
라운드 트립 (Round Trips) 와 U
OpenAI 블로그 포스트는 3 가지 요구 사항을 시작합니다. 그 중 하나는:
- 세션이 시작되면 사용자가 즉시 말할 수 있도록 빠른 연결 설정
lol
WebRTC 연결을 설정하는 데 최소 8* 라운드 트립 (RTT) 이 필요합니다.
우리는 RTT 를 최소화하기 위해 CDN 에지 노드를 가능한 모든 사용자에게 충분히 가까이 배치하려고 시도 하지만, 그것은 합쳐집니다.
신호 서버 (예: WHIP):
- TCP 1
- TLS 1.3 1
- HTTP 1
미디어 서버:
- ICE (서버 포함) 1
- DTLS 1.2 2
- SCTP 2
- 계산이 복잡합니다. 왜냐하면 일부 프로토콜은 0.5 RTT 를 피하기 위해 파이프라인화될 수 있기 때문입니다. 반 A-Press 처럼.
모든 이 허무한 것은 WebRTC 가 P2P 를 지원해야 하기 때문입니다. 정적 IP 주소가 있는 서버를 가지고 있다고 해서, 당신은 여전히 이 춤을 해야 합니다.
신호 서버와 미디어 서버가 같은 호스트/프로세스에서 실행될 때는 더 우울합니다. 당신은 두 가지 중복적이고 비싼 핸드셰이크를 수행하게 됩니다. 그것은 차를 세차에 데리고 가면서 차를 걷는 것과 같습니다.
프로토콜 분기 (Forking the Protocol)
재미로운 사실: 이것은 원래 재미로운 사실이 될 예정이었지만, 이제 별도의 섹션으로 나옵니다.
WebRTC 는 사실상 프로토콜을 분기하도록 권장합니다. 제게는 거의 표면만 긁어냈습니다. 브라우저 구현은 Google 에 의해 소유되어 Google Meet 를 위해 맞춤 제작되었으므로, 그것은 회의 앱에 존재적 위협입니다.
슬픈 사실: 이것이 모든 회의 앱 (Google Meet 제외) 이 당신의 목에 native app 을 밀어 넣으려고 하는 이유입니다.
WebRTC 를 사용하지 않는 유일한 방법입니다.
OpenAI 는 분명히 채무 자금으로 이것을 할 수 있습니다.
하지만 나는 그들이 태자를 욕탕 물과 함께 버려야 한다고 생각합니다.
WebRTC 를 분기하지 마세요. 브라우저 지원이 있는 것으로 대체하세요.
재미로운 사실: 디스코드는 WebRTC 를 그렇게 분기했습니다. 네이티브 클라이언트는 프로토콜의 아주 작은 부분만 구현합니다.
SDP/ICE/STUN/TURN/DTLS/SCTP/SRTP/etc. 더 이상 없습니다.
하지만 우리는 웹 클라이언트를 위해 모든 것을 구현해야 합니다.
하지만 대체로 무엇을 해야 할까?
WebRTC 가 아닌 경우, Voice AI 에는 무엇을 사용해야 할까요?
정말 솔직히 말하자면, 만약 제가 OpenAI 에서 일하고 있었다면 WebSockets 를 통해 오디오를 스트리밍하는 것으로 시작했을 것입니다. 기존 TCP/HTTP 인프라를 활용하여 커스텀 WebRTC 로드 밸런서를 발명할 필요가 없습니다. 이는 지루한 블로그 포스트가 될 수 있지만, 단순하며 Kubernetes 와 호환되며 확장 (SCALES) 합니다.
저는 헤드-오프-라인 블록킹이 사용자 경험의 부채지 rather than 가 아니라 바람직한 사용자 경험이라고 생각합니다.
하지만 운명적인 날이 오고 일부 패킷을 드롭하거나 우선순위 조정할 필요가 있을 것입니다.
그 후 저는 OpenAI 가 MoQ 를 복사하고 WebTransport 를 활용해야 한다고 생각합니다, 왜냐하면...
QUIC 이 이를 해결합니다.
라운드 트립 (round trip) 논의는 기억나시나요? 좋은 시절입니다. QUIC 연결을 설정하는 데 몇 개의 RTT (Round Trip Time) 가 필요한지 여기를 보십시오:
- 1 for QUIC+TLS
하지만 그것은 쉬운 일입니다. turbo QUIC nerd (it me) 가 아닌 이상 모를 수 있는 QUIC 의 더 깊은 세부 사항을 살펴보겠습니다.
Connection ID
RFC9146 링크를 기억하시나요? DTLS 섹션에서? 클릭하지 않으셨나요? 좋은 시절입니다. 아이디어는 문자 그대로 QUIC 에서 복사되었습니다.
QUIC 는 소스 IP/포트 기반 라우팅을 버립니다.
대신, 모든 패킷에 CONNECTION_ID
가 포함되어 있습니다.
그것은 0-20 바이트 길이를 가질 수 있습니다.
우리를 위한 가장 중요한 것은: 그것은 수신자 (receiver) 에 의해 선택됩니다.
따라서 우리의 QUIC 서버는 각 연결에 대해 고유한 CONNECTION_ID
를 생성합니다.
이제 우리는 단일 포트를 사용하면서도 소스 IP/포트가 변경될 때 이를 파악할 수 있습니다.
변경되면, QUIC 는 TCP 와 달리 연결을 종료하는 대신 자동으로 새로운 주소로 전환합니다.
하지만 당신의 직감이 "그들이 어떻게 dare 할 수 있니! 바이트의 낭비야!"라고 반응한다면,
이 바이트들은 매우 중요 합니다. 계속 읽으세요 u nerd.
Stateless Load Balancing
저는 이것을 간과했지만, OpenAI 의 로드 밸런서는 (대부분과 같이) 공유 상태 (shared state) 를 의존합니다.
Sticky packet router 가 있어도 로드 밸런서는 다시 시작하거나 충돌할 수 있습니다.
소스 IP/포트 -> 백엔드 서버의 매핑을 저장해야 하는 것이 무엇인가가 필요합니다.
그들은 Redis 인스턴스를 사용하여 소스 IP/포트를 백엔드 서버로 매핑합니다. 단순하고 쉬우니, 저는 승인합니다.
하지만 더 간단하고 쉬운 것은 뭘까요? 데이터베이스를 갖지 않는 것입니다. QUIC-LB 가 어떻게 하는지 여기를 보십시오:
클라이언트가 QUIC 연결을 시작할 때, 로드 밸런서는 건강한 백엔드 서버로 패킷을 전달합니다.
백엔드 서버는 핸드셰이크를 완료하고 CONNECTION_ID
에 자신의 ID 를 인코딩합니다.
그렇게 하면 모든 후속 QUIC 패킷이 백엔드 서버의 ID 를 포함합니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 HN AI Posts의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기