Preskočite na glavni sadržaj
OpenAI

4. maj 2026.

Inženjering

Kako OpenAI isporučuje glasovni UI sa malim kašnjenjem na velikoj skali

Yi Zhang i William McDonald, članovi tehničkog osoblja

Glasovna umjetna inteligencija djeluje prirodno samo ako se razgovor odvija brzinom govora. Kada mreža smeta, ljudi to odmah čuju kao neugodne pauze, odsječene prekide ili zakašnjelo upadanje u riječ. To je važno za ChatGPT Voice, za programere koji razvijaju pomoću Realtime API-ja, za agente koji rade u interaktivnim tokovima rada i za modele koji trebaju obrađivati audio dok korisnik još govori.

Na skali na kojoj posluje OpenAI, to se prevodi u tri konkretna zahtjeva:

  • Globalni doseg za više od 900 miliona sedmično aktivnih korisnika
  • Brzo uspostavljanje veze kako bi korisnik mogao početi govoriti čim sesija započne
  • Nisko i stabilno vrijeme povratnog prijenosa medija, uz nisku varijaciju kašnjenja i gubitak paketa, tako da izmjena govornika djeluje prirodno i precizno

Tim u OpenAI-ju, odgovoran za interakcije umjetne inteligencije (UI) u realnom vremenu, nedavno je redizajnirao naš WebRTC stek kako bi riješio tri ograničenja koja su se pri skaliranju počela sudarati: terminacija medija po principu jedan port po sesiji ne uklapa se najbolje u infrastrukturu OpenAI-ja, sesije s održavanjem stanja za ICE (Interactive Connectivity Establishment) i DTLS (Datagram Transport Layer Security) zahtijevaju stabilno vlasništvo, a globalno usmjeravanje mora održavati nisku latenciju prvog skoka. U ovom postu pokazat ćemo vam kako smo napravili podijeljenu arhitekturu relej plus primopredajnik da bismo zadržali standardno WebRTC ponašanje za klijente, dok istovremeno mijenjamo način na koji se paketi usmjeravaju unutar OpenAI-jeve infrastrukture.

WebRTC nam omogućava da kreiramo proizvode umjetne inteligencije (UI) koji rade u stvarnom vremenu

WebRTC je otvoreni standard za slanje audija, videa i podataka sa niskom latencijom između preglednika, mobilnih aplikacija i servera. Često se povezuje s peer-to-peer pozivima, ali je također praktična osnova za klijent–server sisteme u stvarnom vremenu jer standardizira složene dijelove interaktivnih medija: ICE za uspostavljanje povezivosti i prolazak kroz NAT (Network Address Translation), DTLS i SRTP (sigurni transportni protokol u stvarnom vremenu) za šifrovani transport, pregovaranje kodeka za kompresiju i dekodiranje zvuka, RTCP (kontrolni protokol za transport u stvarnom vremenu) za kontrolu kvaliteta, te funkcije na strani klijenta kao što su uklanjanje eha i predmemoriranje varijacija kašnjenja.

Ta standardizacija je važna za UI proizvode. Bez WebRTC-a, svaki klijent bi trebao drugačije rješenje za uspostavljanje povezivosti preko NAT-ova, enkripciju medija, pregovaranje kodeka (kodera-dekodera odabranih za prijenos i dekompresiju) i prilagođavanje promjenjivim mrežnim uvjetima. Uz WebRTC možemo graditi na protokolskom stacku koji je već implementiran u preglednicima i na mobilnim platformama, usmjeravajući vlastiti rad na infrastrukturu koja povezuje medijske sadržaje u stvarnom vremenu s modelima.

Takođe se oslanjamo na sam WebRTC ekosistem, uključujući zrele implementacije otvorenog koda i standardizacijski rad koji omogućava interoperabilnost preglednika, mobilnih aplikacija i servera. Temeljni rad Justina Ubertija (jednog od originalnih arhitekata WebRTC-a) i Seana DuBoisa (kreatora i održavaoca Piona) omogućio je timovima poput našeg da se oslanjaju na provjerenu medijsku infrastrukturu, umjesto da iznova osmišljavaju niskonivojski transport, šifriranje i kontrolu zagušenja. Imamo sreću što su i Justin i Sean sada naše kolege ovdje u OpenAI-ju, gdje pomažu usmjeravati kako međusobno približavamo WebRTC i AI u stvarnom vremenu.

Za UI, najvažnije svojstvo je da audio stiže kao kontinuirani tok. Govorni agent može početi s transkribovanjem, rezonovanjem, pozivanjem alata ili generisanjem govora dok korisnik još uvijek govori, umjesto da čeka da se otpremanje u potpunosti završi. To je razlika između sistema koji djeluje kao prirodan razgovor i onog koji djeluje kao push-to-talk.

Odabir medijske arhitekture

Nakon što smo odabrali WebRTC, sljedeće pitanje bilo je gdje ga završiti (gdje bismo prihvatili i preuzeli kontrolu nad WebRTC vezom — na primjer, na rubu) i kako povezati te sesije sa pozadinskim sistemom za inferencu. Završetak je važan jer određuje kako upravljamo stanjem sesije u stvarnom vremenu, prijenosom medija, rutiranjem, kašnjenjem i izolacijom kvarova.

Opcija 1: Pristup SFU-a uključuje umjetnu inteligenciju kao WebRTC učesnika

SFU, odnosno jedinica za selektivno prosljeđivanje, je medijski server koji prima jednu WebRTC struju od svakog učesnika i selektivno prosljeđuje struje ostalima. U ovom modelu, SFU terminira zasebnu WebRTC vezu za svakog učesnika, a AI se pridružuje sesiji kao još jedan učesnik. To može biti dobar izbor za proizvode koji su po svojoj prirodi namijenjeni za više učesnika, kao što su grupni pozivi, učionice ili saradnički sastanci. Objedinjuje audio kodeke, RTCP poruke, kanale podataka, snimanje i politiku po toku na jednom mjestu.1

Čak i u proizvodima klijent–UI, SFU je često zadana polazna tačka jer omogućava timovima da ponovo koriste jedan dokazani sistem za signalizaciju, usmjeravanje medija, snimanje, observabilnost i buduća proširenja, kao što su predaja ljudskom operateru ili dodavanje više učesnika.

Opcija 2: Pristup s primopredajnikom završava WebRTC na rubu mreže i pretvara ga u pozadinski protokol

Naše radno opterećenje je drugačije. Većina sesija odvija se 1:1 — jedan korisnik razgovara s jednim modelom ili jedna aplikacija s jednim agentom u stvarnom vremenu — uz osjetljivost na kašnjenje pri svakom potezu. Za taj obrazac saobraćaja odabrali smo model primopredajnika: rubni WebRTC servis prekida klijentsku vezu, a zatim pretvara medije i događaje u jednostavnije interne protokole za zaključivanje modela, transkripciju, generisanje govora, korištenje alata i orkestraciju.

U ovom dizajnu, primopredajnik je jedina usluga koja posjeduje stanje WebRTC sesije, uključujući provjere ICE povezivosti, DTLS rukovanje, ključeve za SRTP šifriranje i životni ciklus sesije. „Terminacija” ovdje znači da je primopredajnik krajnja tačka koja završava ta rukovanja i šifrira ili dešifrira medijski sadržaj. Držanje tog stanja na jednom mjestu olakšalo je razumijevanje vlasništva nad sesijom i omogućilo backend servisima da se skaliraju kao obični servisi, umjesto da sami djeluju kao WebRTC partneri.

Ključni problem implementacije: WebRTC se susreće sa Kubernetesom

Nakon što smo odabrali model primopredajnika, naša prva implementacija bila je jedan Go servis izgrađen na Pionu koji je obrađivao i signalizaciju i završnu obradu medija. Pokreće glasovnu funkciju ChatGPT‑a, WebRTC krajnju tačku Realtime API-ja, kao i niz istraživačkih projekata.

Operativno, servis primopredajnika obavlja dva zadatka:

  • Signalizacija: SDP pregovaranje, izbor kodeka, ICE vjerodajnice i postavljanje sesije
  • Mediji: prekidanje nizvodnih WebRTC veza i održavanje uzvodnih veza prema pozadinskim servisima za zaključivanje i orkestraciju

Željeli smo da usluga funkcioniše kao i ostatak naše infrastrukture: na Kubernetesu, gdje se radna opterećenja mogu skalirati gore ili dolje i premještati između hostova kako se potražnja mijenja. Međutim, konvencionalni WebRTC model s jednim portom po sesiji nije prikladan za to okruženje, jer zavisi od velikih javnih raspona UDP portova koje je teško izložiti, osigurati i očuvati dok se podovi dodaju, uklanjaju ili ponovno raspoređuju.2

Iscrpljivanje portova

Prvi problem bio je sam model „jedan port po sesiji”. Pri visokom nivou istovremenosti, to znači izlaganje i upravljanje vrlo velikim rasponima UDP portova.

  • Balanseri opterećenja u oblaku i Kubernetes usluge nisu dizajnirani za desetke hiljada javnih UDP portova po usluzi. Svaki dodatni raspon povećava operativnu složenost u konfiguraciji balansatora opterećenja, provjerama zdravlja, politici vatrozida i sigurnosti implementacije.3
  • Velike raspone UDP portova teško je osigurati jer proširuju površinu dostupnu izvana i otežavaju provjeru mrežnih politika.
  • Također nisu dobar izbor za automatsko skaliranje. Podovi se stalno dodaju, uklanjaju i ponovo raspoređuju u Kubernetesu. Zahtijevanje da svaki pod rezervira i oglašava velik stabilan raspon portova čini tu elastičnost krhkom.4

Zbog toga mnogi WebRTC sistemi prelaze na jedan UDP port po serveru, uz demultipleksiranje na nivou aplikacije iza tog porta.5

Prijanjanje stanja

Dizajn sa jednim portom po serveru rješava problem broja portova, ali uvodi drugi: kako zadržati „vlasništvo” nad svakom sesijom kroz flotu servera.

ICE i DTLS su protokoli sa stanjem. Proces koji je kreirao sesiju mora nastaviti primati pakete te sesije kako bi mogao validirati provjere povezivosti, dovršiti DTLS rukovanje, dešifrirati SRTP i obraditi kasnije promjene sesije, kao što su ponovna pokretanja ICE-a. Ako paketi za istu sesiju završe u drugom procesu, uspostavljanje može ne uspjeti ili se medijski tok može prekinuti.

To nam je dalo konkretan cilj: izložiti malu, fiksnu UDP površinu javnom internetu, a i dalje usmjeravati svaki paket do primopredajnika kojem pripada odgovarajuća WebRTC sesija.

Poređenje medijskih arhitektura WebRTC-a

Procijenili smo nekoliko načina da to postignemo, uključujući TURN (Traversal Using Relays around NAT), pri kojem rubni relej terminira klijentske alokacije i prosljeđuje saobraćaj u njihovo ime.2

Pristup

Prednosti

Nedostaci

Jedinstveni IP:port po sesiji (poznato i kao izvorni direktni UDP)

Direktna putanja medija od klijenta do servera

Nema sloja za prosljeđivanje u toku podataka

Zahtijeva jedan javni UDP port po sesiji

Velike opsege portova teško je izložiti i osigurati.

Nije prikladno za Kubernetes i cloud balansere opterećenja

Jedinstveni IP:port po serveru

Mnogo manji javni UDP otisak u odnosu na izloženost po sesiji

Jedan dijeljeni soket po serveru može demultipleksirati mnoge sesije

Funkcioniše bez problema na jednom hostu, ali ne i samo po sebi u okviru zajedničke flote sa balansiranjem opterećenja

Demultipleksiranje sesija na jednom hostu pomaže tek nakon što paket stigne do tog hosta; u floti sa balansiranjem opterećenja prvi paket i dalje može završiti na pogrešnoj instanci, pa je i dalje potreban deterministički način da se usmjerava svaka sesija prema procesu kojem pripada.


TURN relej (s terminacijom protokola)

Klijenti trebaju imati pristup samo adresi i portu TURN releja

Može centralizirati pravila na rubu mreže

TURN alokacije povećavaju broj povratnih ciklusa tokom uspostavljanja veze

Premještanje ili oporavak alokacija između TURN servera i dalje predstavlja izazov

Stateless prosljeđivač + stateful terminator (OpenAI-jev relej + primopredajnik)

Mali javni otisak UDP-a

Transceiver i dalje upravlja cijelom WebRTC sesijom

Dodaje jednu tačku prosljeđivanja prije nego što medijski tok stigne do pripadajućeg primopredajnika

Treba prilagođenu koordinaciju između releja i primopredajnika

Pregled arhitekture: relej + primopredajnik

Arhitektura koju smo isporučili razdvaja usmjeravanje paketa od završetka protokola. Signalizacija i dalje dolazi do primopredajnika za uspostavljanje sesije, dok medijski sadržaj prvo prolazi kroz relej. Relej je lagani sloj za prosljeđivanje UDP-a s malim javnim otiskom, dok je primopredajnik WebRTC krajnja tačka sa stanjem iza njega.

Relej statelessly prosljeđuje pakete primopredajniku.

Relej ne dešifrira medije, ne pokreće ICE mašine stanja niti učestvuje u pregovaranju o kodecima. Čita dovoljno metapodataka paketa da odabere odredište, a zatim prosljeđuje paket primopredajniku kojem pripada sesija. Primopredajnik i dalje vidi normalan WebRTC tok i i dalje posjeduje cjelokupno stanje protokola. Iz perspektive klijenta, ništa u vezi s WebRTC sesijom se ne mijenja.

Rutiranje na osnovu ICE vjerodajnica

Usmjeravanje prvog paketa je ključni korak u ovoj postavci. Relej mora usmjeriti prvi paket od klijenta prije nego što postoji bilo kakva sesija na putanji paketa, umjesto da se zaustavlja radi vanjskog servisa za pretraživanje.

Svaka WebRTC sesija već sadrži mehanizam za usmjeravanje ugrađen u protokol: fragment korisničkog imena ICE-a (poznat kao ufrag), kratki identifikator koji se razmjenjuje tokom uspostavljanja sesije i ponavlja u STUN provjerama povezivosti. Generiramo ufrag na strani servera tako da sadrži dovoljno metapodataka o usmjeravanju kako bi relay mogao zaključiti odredišni klaster i vlasnički transceiver.

Sekvencijski dijagram prikazuje kako se uspostavlja veza.

Tokom signalizacije, primopredajnik dodjeljuje stanje sesije i vraća zajednički VIP releja i UDP port u SDP odgovoru. VIP je virtualna IP adresa koja stoji ispred flote releja; u kombinaciji s portom, klijentu daje jedno stabilno odredište, kao što je `203.0.113.10:3478`, iako se iza nje nalazi mnogo instanci releja. Prvi paket medijske putanje klijenta obično je STUN (Session Traversal Utilities for NAT) zahtjev za vezivanje, koji ICE koristi kako bi provjerio da paketi mogu stići do najavljene adrese.

Relay parsira samo onoliko prvog STUN paketa koliko je potrebno da pročita serverski ufrag, dekodira naznaku usmjeravanja i proslijedi paket odgovarajućem primopredajniku. Svaki primopredajnik osluškuje na zajedničkom UDP soketu, što znači jednu krajnju tačku operativnog sistema povezanu s internim IP:portom, a ne po jedan soket za svaku sesiju. Nakon što relej kreira sesiju od izvornog IP:porta klijenta do odredišta tog primopredajnika, naredni DTLS, RTP i RTCP paketi teku unutar sesije bez ponovnog dekodiranja ufraga.

Sesija releja je namjerno minimalna i sastoji se samo od sesije u memoriji koja služi za prosljeđivanje paketa, zajedno s neophodnim brojačima za praćenje i tajmerima za istek i čišćenje sesije. Ovaj dizajnerski izbor omogućava rutiranje paketa direktno kroz njegovu putanju. Ako se relej ponovo pokrene i izgubi sesiju, sljedeći STUN paket ponovo uspostavlja sesiju na osnovu ufrag parametra za usmjeravanje. Da bi se učinilo još pouzdanijim, koristi se Redis keš za čuvanje mapiranja <klijentski IP + port, IP primopredajnika + port> nakon što se ruta uspostavi, kako bi se ono moglo obnoviti mnogo ranije, prije nego što stigne sljedeći STUN paket.

Global Relay i geografski usmjerena signalizacija

Nakon što smo javnu UDP površinu sveli na mali broj stabilnih adresa i portova, mogli smo globalno implementirati isti obrazac releja. Global Relay je naša flota geografski distribuiranih ulaznih relejnih tačaka koje sve implementiraju isto ponašanje prosljeđivanja paketa.

Široki geografski pristup skraćuje prvi skok od klijenta do OpenAI-ja jer paket može ući u našu mrežu na releju blizu korisnika, kako geografski tako i u smislu mrežne topologije, umjesto da prvo prelazi javni internet do udaljene regije. U praktičnom smislu, to znači nižu latenciju, manje varijacija kašnjenja i manje izbjegnutih naleta gubitaka prije nego što promet stigne do naše okosnice.6

Sloj Global Relay prima pakete od klijenta i prosljeđuje ih klasteru prijemnika i predajnika

Koristimo Cloudflare geo i proximity steering za signalizaciju, tako da početni HTTP ili WebSocket zahtjev stigne do obližnjeg klastera primopredajnika. Kontekst zahtjeva određuje lokaciju sesije i to koja se ulazna (ingress) tačka Global Relayja prikazuje klijentu. SDP odgovor daje adresu Global Relayja, dok ufrag (ICE username fragment) sadrži dovoljno informacija da Global Relay usmjeri medije na određeni klaster, a relay da ih usmjeri do odredišnog primopredajnika.

Zajedno, geo-usmjereno signaliziranje i Global Relay omogućavaju da i postavljanje i medijski sadržaj budu usmjereni na obližnju ulaznu tačku, dok sesija ostaje vezana za jedan primopredajnik. To smanjuje vrijeme povratnog putovanja za signalizaciju i za prvu provjeru ICE (Interactive Connectivity Establishment) povezivosti, što direktno skraćuje vrijeme koje korisnik čeka prije nego što može početi govoriti.

Implementacija i performanse Relay-a

Servis za prosljeđivanje napisali smo u Go jeziku i namjerno zadržali uski opseg implementacije. Na Linuxu, mrežni stek kernela prima UDP pakete s mrežnog interfejsa računara i isporučuje ih soketu, krajnjoj tački operativnog sistema iz koje proces čita nakon vezivanja IP:Port kombinacije. Relay radi u korisničkom prostoru, tako da običan Go proces čita zaglavlja paketa iz tog socketa, ažurira malu količinu stanja toka i prosljeđuje pakete bez prekidanja WebRTC-a. Nije nam bio potreban nikakav okvir za zaobilaženje kernela, koji bi omogućio procesu u korisničkom prostoru da direktno ispituje mrežne redove čekanja radi većih stopa paketa, ali bi također povećao operativnu složenost.

Ključne dizajnerske odluke:

  • Nema terminacije protokola: relej parsira samo STUN zaglavlja/ufrag; koristi keširano stanje za naknadni DTLS, RTP i RTCP, ostavljajući pakete neprozirnim.
  • Efemerno stanje: održava malu mapu u memoriji sa kratkim vremenskim ograničenjem, od adrese klijenta do odredišta primopredajnika za stanje toka i opservabilnost.
  • Horizontalna skalabilnost: više relejnih instanci radi paralelno iza uređaja za raspodjelu opterećenja. Stanje nije fiksno WebRTC stanje, pa ponovna pokretanja uzrokuju minimalne gubitke prometa i brz oporavak toka.

Mjere za bolju efikasnost:

  • SO_REUSEPORT je opcija Linux socketa koja omogućava da se više radnih procesa za prosljeđivanje na istoj mašini poveže na isti UDP port. Jezgro zatim raspoređuje dolazne pakete među tim radnim procesima, čime se izbjegava usko grlo jedne petlje čitanja.
  • runtime.LockOSThread vezuje svaku gorutinu koja čita UDP za određenu nit operativnog sistema. U kombinaciji s SO_REUSEPORT, to obično zadržava pakete iz istog toka (izvorni i odredišni IP:Port plus protokol) na istoj CPU jezgri, poboljšavajući lokalnost keša i smanjujući prebacivanje konteksta.
  • Unaprijed alocirani baferi i minimalno kopiranje održavaju režijske troškove parsiranja i alokacije niskim kako bi se izbjeglo sakupljanje smeća u Go jeziku.

Ova implementacija je obradila naš globalni medijski saobraćaj u realnom vremenu uz relativno mali opseg relejne infrastrukture, pa smo zadržali jednostavniji dizajn umjesto da krenemo putem zaobilaženja jezgra.

Rezultati i saznanja

Ova arhitektura omogućava pokretanje WebRTC medijskog saobraćaja u Kubernetesu bez otkrivanja hiljada UDP portova. To je važno jer je manju i fiksnu UDP površinu lakše osigurati i balansirati, a infrastruktura može skalirati bez rezervisanja velikih raspona javnih portova. Uz bolju podršku infrastrukture iz Kubernetes-a i veću sigurnost zbog manje površine napada, ovaj dizajn zadržava standardno WebRTC ponašanje za klijente i potvrđuje da je dizajn bez SFU-a bio pravi izbor za naše radno opterećenje. Većina naših sesija je point-to-point, osjetljiva na kašnjenje i lakše ih je skalirati kada usluge inferencije ne moraju da se ponašaju kao WebRTC vršnjaci.

Šira lekcija je da je najbolje mjesto za dodavanje složenosti u tankom sloju za usmjeravanje, a ne u svakoj pozadinskoj usluzi niti u prilagođenom ponašanju klijenta. Kodiranje metapodataka za usmjeravanje u polje svojstveno protokolu omogućilo nam je determinističko usmjeravanje prvog paketa, mali javni UDP otisak i dovoljnu fleksibilnost da ulazne tačke smjestimo blizu korisnika širom svijeta.

Nekoliko odluka bilo je posebno važno:

  • Očuvajte semantiku protokola na rubu. Klijenti i dalje koriste standardni WebRTC, čime interoperabilnost pretraživača i mobilnih uređaja ostaje očuvana.
  • Čuvaj trajna stanja sesije na jednom mjestu. Primopredajnik upravlja ICE-om, DTLS-om, SRTP-om i životnim ciklusom sesije; relej samo prosljeđuje pakete.
  • Usmjeravaj na informacije koje su već prisutne u postavci. ICE ufrag nam je omogućio usmjeravanje prvog paketa bez potrebe za pretragom na kritičnoj putanji.
  • Optimizirajte za najčešći slučaj prije nego što posegnete za zaobilaženjem kernela. Usko fokusirana implementacija u Go-u, uz pažljivu upotrebu SO_REUSEPORT, vezivanje niti i parsiranje s malo alokacija, bila je dovoljna za naše opterećenje.

Glasovni UI u stvarnom vremenu funkcioniše samo kada infrastruktura učini latentnost neprimjetnom. Za nas, to je značilo promjenu načina naše WebRTC implementacije bez mijenjanja onoga što klijenti očekuju od samog WebRTC-a.