Langsung ke konten utama
OpenAI

4 Mei 2026

Teknik Rekayasa

Bagaimana OpenAI menghadirkan AI suara berlatensi rendah dalam skala besar

Oleh Yi Zhang dan William McDonald, Anggota Staf Teknis

AI suara hanya terasa alami jika percakapan bergerak secepat ujaran. Ketika jaringan menghambat, orang langsung mendengarnya sebagai jeda yang canggung, interupsi yang terpotong, atau barge-in yang tertunda. Hal ini penting untuk ChatGPT Suara, bagi pengembang yang membangun dengan Realtime API, bagi agen yang bekerja dalam alur kerja interaktif, dan bagi model yang perlu memproses audio saat pengguna masih berbicara.

Pada skala OpenAI, hal ini diterjemahkan menjadi tiga kebutuhan konkret:

  • Jangkauan global untuk lebih dari 900 juta pengguna aktif mingguan
  • Penyiapan koneksi yang cepat agar pengguna dapat mulai berbicara segera setelah sesi dimulai
  • Waktu tempuh bolak-balik media yang rendah dan stabil, dengan jitter dan kehilangan paket yang rendah, agar pergiliran bicara terasa responsif

Tim di OpenAI yang bertanggung jawab atas interaksi AI real-time baru-baru ini merancang ulang stack WebRTC kami untuk mengatasi tiga kendala yang mulai berbenturan pada skala besar: terminasi media satu-port-per-sesi tidak cocok dengan infrastruktur OpenAI, sesi ICE (Interactive Connectivity Establishment) dan DTLS (Datagram Transport Layer Security) yang stateful memerlukan kepemilikan yang stabil, dan perutean global harus menjaga latensi hop pertama tetap rendah. Dalam tulisan ini, kami membahas arsitektur terpisah relay plus transceiver yang kami bangun untuk mempertahankan perilaku WebRTC standar bagi klien sambil mengubah cara paket dirutekan di dalam infrastruktur OpenAI.

WebRTC memungkinkan kami membuat produk AI real-time

WebRTC adalah standar terbuka untuk mengirim audio, video, dan data berlatensi rendah antara browser, aplikasi seluler, dan server. WebRTC sering dikaitkan dengan panggilan peer-to-peer, tetapi juga menjadi fondasi praktis untuk sistem real-time klien-ke-server karena menstandarkan bagian-bagian sulit dari media interaktif: ICE untuk pembentukan konektivitas dan traversal NAT (Network Address Translation), DTLS dan SRTP (Secure Real-time Transport Protocol) untuk transport terenkripsi, negosiasi codec untuk mengompresi dan mendekode audio, RTCP (Real-time Transport Control Protocol) untuk pengendalian kualitas, dan fitur sisi klien seperti pembatalan gema dan buffering jitter.

Standardisasi itu penting untuk produk AI. Tanpa WebRTC, setiap klien akan membutuhkan jawaban berbeda tentang cara membangun konektivitas melintasi NAT, mengenkripsi media, menegosiasikan codec (coder-decoder yang dipilih untuk transmisi dan dekompresi), serta beradaptasi dengan kondisi jaringan yang berubah. Dengan WebRTC, kami dapat membangun di atas stack protokol yang sudah diimplementasikan di browser dan platform seluler, sambil memfokuskan pekerjaan kami sendiri pada infrastruktur yang menghubungkan media real-time ke model.

Kami juga membangun di atas ekosistem WebRTC itu sendiri, termasuk implementasi open-source yang matang dan kerja standar yang menjaga interoperabilitas antara browser, aplikasi seluler, dan server. Pekerjaan dasar oleh Justin Uberti (salah satu arsitek asli WebRTC) dan Sean DuBois (pencipta dan maintainer Pion) memungkinkan tim seperti kami membangun di atas infrastruktur media yang telah teruji alih-alih menciptakan ulang perilaku transport, enkripsi, dan pengendalian kemacetan tingkat rendah. Kami beruntung karena Justin dan Sean kini menjadi rekan kerja di OpenAI, membantu memandu bagaimana kami mendekatkan WebRTC dan AI real-time.

Untuk AI, sifat yang paling penting adalah audio tiba sebagai aliran kontinu. Agen yang berbicara dapat mulai mentranskripsikan, melakukan penalaran, memanggil alat, atau menghasilkan ujaran saat pengguna masih berbicara, alih-alih menunggu unggahan penuh. Itulah perbedaan antara sistem yang terasa seperti percakapan dan sistem yang terasa seperti push-to-talk.

Memilih arsitektur media

Setelah memilih WebRTC, pertanyaan berikutnya adalah di mana men-terminate-nya (tempat kami menerima dan memiliki koneksi WebRTC—misalnya, di edge) dan bagaimana menghubungkan sesi-sesi tersebut ke backend inferensi. Titik terminasi penting karena menentukan bagaimana kami menangani status sesi real-time, transport media, perutean, latensi, dan isolasi kegagalan.

Opsi 1: Pendekatan SFU menyertakan AI sebagai peserta WebRTC

SFU, atau selective forwarding unit, adalah server media yang menerima satu aliran WebRTC dari setiap peserta dan secara selektif meneruskan aliran ke peserta lain. Dalam model ini, SFU men-terminate koneksi WebRTC terpisah untuk setiap peserta, dan AI bergabung sebagai peserta lain dalam sesi. Ini bisa sangat cocok untuk produk yang secara inheren multipihak, seperti panggilan grup, kelas, atau rapat kolaboratif. Model ini menyatukan codec audio, pesan RTCP, data channel, perekaman, dan kebijakan per aliran di satu tempat.1

Bahkan dalam produk klien-ke-AI, SFU sering menjadi titik awal default karena memungkinkan tim menggunakan ulang satu sistem yang telah terbukti untuk signaling, perutean media, perekaman, observabilitas, dan perluasan di masa depan seperti handoff ke manusia atau penambahan lebih banyak peserta.

Opsi 2: Pendekatan transceiver men-terminate WebRTC di edge dan mengonversinya ke protokol backend

Beban kerja kami berbeda. Sebagian besar sesi bersifat 1:1—satu pengguna berbicara dengan satu model, atau satu aplikasi berbicara dengan satu agen real-time—dengan sensitivitas latensi di setiap giliran. Untuk pola lalu lintas seperti itu, kami memilih model transceiver: layanan edge WebRTC men-terminate koneksi klien lalu mengonversi media dan peristiwa ke protokol internal yang lebih sederhana untuk inferensi model, transkripsi, pembuatan ujaran, penggunaan alat, dan orkestrasi.

Dalam desain ini, transceiver adalah satu-satunya layanan yang memiliki status sesi WebRTC, termasuk pemeriksaan konektivitas ICE, handshake DTLS, kunci enkripsi SRTP, dan siklus hidup sesi. “Terminasi” di sini berarti transceiver adalah endpoint yang menyelesaikan handshake tersebut dan mengenkripsi atau mendekripsi media. Menjaga status itu di satu tempat membuat kepemilikan sesi lebih mudah dipahami, dan memungkinkan layanan backend diskalakan seperti layanan biasa alih-alih bertindak sebagai peer WebRTC itu sendiri.

Masalah deployment inti: WebRTC bertemu Kubernetes

Setelah memilih model transceiver, implementasi pertama kami adalah satu layanan Go yang dibangun di atas Pion yang menangani signaling dan terminasi media. Layanan ini mendukung ChatGPT Suara, endpoint WebRTC Realtime API, dan sejumlah proyek riset.

Secara operasional, layanan transceiver menjalankan dua tugas:

  • Signaling: negosiasi SDP, pemilihan codec, kredensial ICE, dan penyiapan sesi
  • Media: men-terminate koneksi WebRTC downstream dan mempertahankan koneksi upstream ke layanan backend untuk inferensi dan orkestrasi

Kami ingin layanan ini berjalan seperti infrastruktur kami yang lain: di Kubernetes, tempat beban kerja dapat naik dan turun skalanya, serta berpindah antar-host saat permintaan berubah. Namun model WebRTC konvensional satu-port-per-sesi kurang cocok untuk lingkungan tersebut, karena bergantung pada rentang port UDP publik yang besar yang sulit diekspos, diamankan, dan dipertahankan saat pod ditambahkan, dihapus, atau dijadwal ulang.2

Kehabisan port

Masalah pertama adalah model satu-port-per-sesi itu sendiri. Pada konkurensi tinggi, itu berarti mengekspos dan mengelola rentang port UDP yang sangat besar.

  • Load balancer cloud dan layanan Kubernetes tidak dirancang untuk puluhan ribu port UDP publik per layanan. Setiap rentang tambahan menambah kompleksitas operasional dalam konfigurasi load balancer, health check, kebijakan firewall, dan keamanan rollout.3
  • Rentang port UDP yang besar sulit diamankan karena memperluas area yang dapat dijangkau dari luar dan membuat kebijakan jaringan lebih sulit diaudit.
  • Rentang ini juga kurang cocok untuk autoscaling. Pod terus-menerus ditambahkan, dihapus, dan dijadwal ulang di Kubernetes. Mengharuskan setiap pod memesan dan mengiklankan rentang port stabil yang besar membuat elastisitas ini rapuh.4

Inilah sebabnya banyak sistem WebRTC bergerak menuju satu port UDP per server, dengan demultipleksing tingkat aplikasi di balik port tersebut.5

Kelekatan state

Desain satu-port-per-server menyelesaikan jumlah port, tetapi menimbulkan masalah kedua: mempertahankan kepemilikan setiap sesi di seluruh armada.

ICE dan DTLS adalah protokol stateful. Proses yang membuat sesi perlu terus menerima paket untuk sesi tersebut agar dapat memvalidasi pemeriksaan konektivitas, menyelesaikan handshake DTLS, mendekripsi SRTP, dan memproses perubahan sesi berikutnya seperti restart ICE. Jika paket untuk sesi yang sama jatuh ke proses yang berbeda, penyiapan dapat gagal atau media dapat rusak.

Itu memberi kami target yang spesifik: mengekspos permukaan UDP publik yang kecil dan tetap ke internet publik, sambil tetap merutekan setiap paket ke transceiver yang memiliki sesi WebRTC terkait.

Perbandingan arsitektur media WebRTC

Kami mengevaluasi beberapa cara untuk mencapainya, termasuk TURN (Traversal Using Relays around NAT), di mana relay edge men-terminate alokasi klien dan meneruskan lalu lintas atas nama mereka.2

Pendekatan

Kelebihan

Kekurangan

IP:port unik per sesi (juga dikenal sebagai UDP langsung native)

Jalur media langsung dari klien ke server

Tidak ada lapisan penerusan di jalur data

Memerlukan satu port UDP publik per sesi

Rentang port besar sulit diekspos dan diamankan

Kurang cocok untuk Kubernetes dan load balancer cloud

IP:port unik per server

Jejak UDP publik jauh lebih kecil dibanding eksposur per sesi

Satu soket bersama per server dapat mendemultipleks banyak sesi

Bekerja dengan baik pada satu host, tetapi tidak dengan sendirinya di seluruh armada bersama yang di-load-balance

Demultipleksing sesi pada satu host hanya membantu setelah paket mencapai host tersebut; di seluruh armada yang di-load-balance, paket pertama masih bisa tiba di instance yang salah, jadi Anda tetap memerlukan cara deterministik untuk mengarahkan setiap sesi ke proses yang memilikinya


Relay TURN (terminasi protokol)

Klien hanya perlu menjangkau alamat dan port relay TURN

Dapat memusatkan kebijakan di edge

Alokasi TURN menambah round trip penyiapan

Memindahkan atau memulihkan alokasi antar-server TURN tetap sulit

Forwarder stateless + terminator stateful (relay + transceiver OpenAI)

Jejak UDP publik kecil

Transceiver tetap memiliki sesi WebRTC penuh

Menambahkan satu hop penerusan sebelum media mencapai transceiver pemilik

Memerlukan koordinasi kustom antara relay dan transceiver

Gambaran umum arsitektur: relay + transceiver

Arsitektur yang kami kirim memisahkan perutean paket dari terminasi protokol. Signaling tetap mencapai transceiver untuk penyiapan sesi, sementara media masuk melalui relay terlebih dahulu. Relay adalah lapisan penerusan UDP ringan dengan jejak publik yang kecil, dan transceiver adalah endpoint WebRTC stateful di belakangnya.

Relay meneruskan paket ke transceiver tanpa menyimpan state

Relay tidak mendekripsi media, tidak menjalankan state machine ICE, dan tidak berpartisipasi dalam negosiasi codec. Relay membaca cukup metadata paket untuk memilih tujuan, lalu meneruskan paket ke transceiver yang memiliki sesi. Transceiver tetap melihat aliran WebRTC yang normal dan tetap memiliki seluruh status protokol. Dari sudut pandang klien, tidak ada yang berubah pada sesi WebRTC.

Perutean berdasarkan kredensial ICE

Perutean paket pertama adalah langkah kunci dalam penyiapan ini. Relay harus merutekan paket pertama dari klien sebelum ada sesi apa pun pada jalur paket itu sendiri, bukan dengan berhenti untuk melakukan pencarian ke layanan eksternal.

Setiap sesi WebRTC sudah membawa pengait perutean bawaan protokol: fragmen nama pengguna ICE, atau ufrag, pengenal singkat yang dipertukarkan selama penyiapan sesi dan digaungkan dalam pemeriksaan konektivitas STUN. Kami menghasilkan ufrag sisi server agar memuat metadata perutean yang secukupnya sehingga relay dapat menyimpulkan klaster tujuan dan transceiver pemilik.

Diagram urutan menunjukkan bagaimana koneksi dibuat

Selama signaling, transceiver mengalokasikan status sesi dan mengembalikan VIP relay bersama serta port UDP dalam jawaban SDP. VIP adalah alamat IP virtual di depan armada relay; jika digabungkan dengan port, ini memberi klien satu tujuan stabil, seperti `203.0.113.10:3478`, meskipun banyak instance relay berada di belakangnya. Paket pertama klien pada jalur media biasanya adalah permintaan binding STUN (Session Traversal Utilities for NAT), yang digunakan ICE untuk memverifikasi bahwa paket dapat mencapai alamat yang diiklankan.

Relay mem-parsing secukupnya dari paket STUN pertama itu untuk membaca ufrag server, mendekode petunjuk perutean, dan meneruskan paket ke transceiver pemilik. Setiap transceiver mendengarkan pada soket UDP bersama, yang berarti satu endpoint sistem operasi yang diikat ke IP:port internal, bukan satu soket per sesi. Setelah relay membuat sesi dari IP:port sumber klien ke tujuan transceiver itu, paket DTLS, RTP, dan RTCP berikutnya mengalir dalam sesi tersebut tanpa perlu mendekode ulang ufrag.

Sesi relay sengaja dibuat minimal, hanya terdiri dari sesi in-memory untuk menginformasikan penerusan paket, beserta counter yang diperlukan untuk pemantauan dan timer untuk kedaluwarsa serta pembersihan sesi. Pilihan desain ini menjaga perutean paket tetap langsung berada pada jalur paket. Jika relay restart dan kehilangan sesi, paket STUN berikutnya akan membangun ulang sesi dari petunjuk perutean ufrag. Agar lebih andal lagi, cache Redis digunakan untuk menyimpan pemetaan <IP klien + Port, IP transceiver + Port> setelah rute terbentuk sehingga dapat dipulihkan jauh lebih awal, sebelum paket STUN berikutnya tiba.

Global Relay dan signaling yang diarahkan secara geografis

Setelah kami mengurangi permukaan UDP publik menjadi sejumlah kecil alamat dan port yang stabil, kami dapat menerapkan pola relay yang sama secara global. Global Relay adalah armada titik masuk relay kami yang tersebar secara geografis dan semuanya menerapkan perilaku penerusan paket yang sama.

Titik masuk geografis yang luas memperpendek hop pertama klien-ke-OpenAI karena paket dapat masuk ke jaringan kami melalui relay yang dekat dengan pengguna, baik secara geografis maupun dalam topologi jaringan, alih-alih terlebih dahulu melintasi internet publik ke wilayah yang jauh. Secara praktis, itu berarti latensi lebih rendah, jitter lebih sedikit, dan lebih sedikit lonjakan kehilangan yang sebenarnya dapat dihindari sebelum lalu lintas mencapai backbone kami.6

Lapisan Global Relay menerima paket dari klien dan meneruskannya ke klaster transceiver

Kami menggunakan pengarah geo dan proximity Cloudflare untuk signaling agar permintaan HTTP atau WebSocket awal mencapai klaster transceiver terdekat. Konteks permintaan menentukan lokasi sesi dan titik masuk Global Relay mana yang diiklankan ke klien. Jawaban SDP menyediakan alamat Global Relay, sementara ufrag memuat informasi yang cukup bagi Global Relay untuk merutekan media ke klaster yang ditentukan dan bagi relay untuk merutekan ke transceiver tujuan.

Bersama-sama, signaling yang diarahkan secara geografis dan Global Relay menempatkan penyiapan serta media pada jalur masuk terdekat sambil menjaga sesi tetap tertambat ke satu transceiver. Hal ini mengurangi round-trip time untuk signaling dan untuk pemeriksaan konektivitas ICE pertama, yang secara langsung memperpendek waktu tunggu pengguna sebelum ujaran dapat dimulai.

Implementasi dan performa relay

Kami menulis layanan relay dalam Go dan sengaja menjaga implementasinya tetap sempit. Di Linux, stack jaringan kernel menerima paket UDP dari antarmuka jaringan mesin dan mengirimkannya ke soket, endpoint sistem operasi yang dibaca proses setelah mengikat IP:Port. Relay berjalan di userspace, jadi proses Go biasa membaca header paket dari soket itu, memperbarui sejumlah kecil state aliran, dan meneruskan paket tanpa men-terminate WebRTC. Kami tidak memerlukan framework kernel-bypass, yang memungkinkan proses userspace melakukan polling antrean jaringan secara langsung untuk laju paket yang lebih tinggi tetapi juga menambah kompleksitas operasional.

Pilihan desain utama:

  • Tanpa terminasi protokol: Relay hanya mem-parsing header STUN/ufrag; untuk DTLS, RTP, dan RTCP berikutnya, relay menggunakan state yang di-cache, sehingga paket tetap opak.
  • State ephemeral: Relay mempertahankan map kecil dalam memori dengan timeout singkat dari alamat klien ke tujuan transceiver untuk state aliran dan observabilitas.
  • Skalabilitas horizontal: Beberapa instance relay berjalan paralel di belakang load balancer. State ini bukan state WebRTC yang keras, sehingga restart hanya menyebabkan traffic drop minimal dan pemulihan aliran yang cepat.

Langkah efisiensi:

  • SO_REUSEPORT adalah opsi soket Linux yang memungkinkan beberapa worker relay pada mesin yang sama mengikat port UDP yang sama. Kernel kemudian mendistribusikan paket masuk ke para worker tersebut, yang menghindari bottleneck pada satu read loop.
  • runtime.LockOSThread menyematkan setiap goroutine pembaca UDP ke thread OS tertentu. Dikombinasikan dengan SO_REUSEPORT, hal ini cenderung menjaga paket dari aliran yang sama (IP:Port sumber dan tujuan ditambah protokol) tetap berada pada inti CPU yang sama, meningkatkan locality cache dan mengurangi context switching.
  • Buffer yang telah dialokasikan sebelumnya dan penyalinan yang minimal menjaga overhead parsing dan alokasi tetap rendah untuk menghindari garbage collection di Go.

Implementasi ini menangani traffic media real-time global kami dengan jejak relay yang relatif kecil, jadi kami mempertahankan desain yang lebih sederhana alih-alih mengambil jalur kernel bypass.

Hasil dan pelajaran

Arsitektur ini memungkinkan kami menjalankan media WebRTC di Kubernetes tanpa mengekspos ribuan port UDP. Ini penting karena permukaan UDP yang lebih kecil dan tetap lebih mudah diamankan dan di-load-balance, serta memungkinkan infrastruktur diskalakan tanpa memesan rentang port publik yang besar. Dengan dukungan infra yang lebih baik dari Kubernetes dan keamanan yang lebih tinggi karena area permukaan yang lebih kecil, desain ini juga mempertahankan perilaku WebRTC standar bagi klien dan menegaskan bahwa desain tanpa SFU adalah default yang tepat untuk beban kerja kami. Sebagian besar sesi kami bersifat point-to-point, sensitif terhadap latensi, dan lebih mudah diskalakan ketika layanan inferensi tidak perlu berperilaku seperti peer WebRTC.

Pelajaran yang lebih luas adalah bahwa tempat terbaik untuk menambahkan kompleksitas adalah pada lapisan perutean yang tipis, bukan pada setiap layanan backend, dan bukan pada perilaku klien kustom. Menyandikan metadata perutean ke dalam field bawaan protokol memberi kami perutean paket pertama yang deterministik, jejak UDP publik yang kecil, dan fleksibilitas yang cukup untuk menempatkan titik masuk dekat pengguna di seluruh dunia.

Beberapa pilihan sangat penting:

  • Pertahankan semantik protokol di edge. Klien tetap berbicara dengan WebRTC standar, yang menjaga interoperabilitas browser dan seluler tetap utuh.
  • Simpan state sesi yang sulit di satu tempat. Transceiver memiliki ICE, DTLS, SRTP, dan siklus hidup sesi; relay hanya meneruskan paket.
  • Rutekan berdasarkan informasi yang sudah ada saat penyiapan. ICE ufrag memberi kami pengait perutean paket pertama tanpa menambahkan dependensi lookup pada hot path.
  • Optimalkan untuk kasus umum sebelum beralih ke kernel bypass. Implementasi Go yang sempit dengan penggunaan SO_REUSEPORT, penyematan thread, dan parsing dengan alokasi rendah sudah cukup untuk beban kerja kami.

AI suara real-time hanya berfungsi ketika infrastruktur membuat latensi terasa tak terlihat. Bagi kami, itu berarti mengubah bentuk deployment WebRTC kami tanpa mengubah apa yang diharapkan klien dari WebRTC itu sendiri.