Ako OpenAI poskytuje hlasovú umelú inteligenciu s nízkou latenciou vo veľkom meradle
Autor: Yi Zhang a William McDonald, členovia technického personálu
Hlasová umelá inteligencia pôsobí prirodzene iba vtedy, ak sa konverzácia pohybuje rýchlosťou reči. Ak sieť prekáža, ľudia to okamžite vnímajú ako neprirodzené pauzy, useknuté prerušenia alebo oneskorené vstupy do reči. To je dôležité pre Chat GPT Voice, pre vývojárov, ktorí pracujú s Realtime API, pre agentov v interaktívnych pracovných postupoch a pre modely, ktoré spracovávajú zvuk, kým používateľ ešte hovorí.
V rozsahu OpenAI sa to premieta do troch konkrétnych požiadaviek:
- Globálny dosah na viac ako 900 miliónov aktívnych používateľov týždenne
- Rýchle nadviazanie pripojenia, aby používateľ mohol začať hovoriť hneď po začatí relácie
- Nízka a stabilná latencia obojsmerného prenosu médií, s minimálnym jitterom a stratou paketov, aby striedanie v konverzácii pôsobilo prirodzene a plynulo
Tím v OpenAI zodpovedný za interakcie s umelou inteligenciou v reálnom čase nedávno prepracoval architektúru nášho WebRTC stacku s cieľom riešiť tri obmedzenia, ktoré začali vo veľkom meradle kolidovať: ukončovanie médií na jednom porte na reláciu sa dobre nehodí do infraštruktúry OpenAI, stavové relácie ICE (Interactive Connectivity Establishment) a DTLS (Datagram Transport Layer Security) vyžadujú stabilné vlastníctvo a globálne smerovanie musí udržiavať nízku latenciu prvého kroku. V tomto príspevku si prejdeme architektúru rozdeleného relé a transceivera , ktorú sme vytvorili s cieľom zachovať štandardné správanie WebRTC pre klientov a zároveň zmeniť spôsob smerovania paketov v rámci infraštruktúry OpenAI.
„WebRTC je otvorený štandard na prenos audia, videa a dát s nízkou latenciou medzi prehliadačmi, mobilnými aplikáciami a servermi. Často sa spája s volaniami typu peer-to-peer, ale je aj praktickým základom pre systémy v reálnom čase typu klient-server, pretože štandardizuje náročné časti interaktívnych médií: ICE na nadviazanie konektivity a prechod cez NAT (Network Address Translation), DTLS a SRTP (Secure Real-time Transport Protocol) na šifrovaný prenos, vyjednávanie kodekov na kompresiu a dekódovanie zvuku, RTCP (Real-time Transport Control Protocol) na kontrolu kvality a funkcie na strane klienta, ako sú potlačenie ozveny a bufferovanie jitteru.
Táto štandardizácia je dôležitá pre produkty umelej inteligencie. Bez WebRTC by každý klient potreboval vlastné riešenie na nadviazanie spojenia cez NAT, šifrovanie médií, vyjednávanie kodekov (kódovačov/dekódovačov pre prenos a dekompresiu) a prispôsobenie sa meniacim sa sieťovým podmienkam. Vďaka WebRTC môžeme využiť už existujúci protokolový stack v prehliadačoch a mobilných platformách a zamerať sa na infraštruktúru, ktorá prepája médiá v reálnom čase s modelmi.
Zároveň staviame na samotnom ekosystéme WebRTC, vrátane vyspelých open-source implementácií a štandardizačnej práce, ktorá zabezpečuje interoperabilitu medzi prehliadačmi, mobilnými aplikáciami a servermi. Základná práca Justina Ubertiho (jedného z pôvodných architektov WebRTC) a Seana DuBoisa (tvorcu a správcu Pionu) umožnila tímom, ako je ten náš, nadviazať na osvedčenú mediálnu infraštruktúru namiesto toho, aby znovu riešili nízkoúrovňový prenos, šifrovanie a správanie pri kontrole zahltenia. Máme šťastie, že Justin aj Sean sú teraz našimi kolegami tu v OpenAI a pomáhajú určovať smer, akým zbližujeme WebRTC a umelú inteligenciu v reálnom čase.
Pre umelú inteligenciu je najdôležitejšou vlastnosťou to, že zvuk prichádza ako nepretržitý prúd. Hlasový agent môže začať prepisovať, uvažovať, získavať nástroje alebo generovať reč ešte počas toho, ako používateľ hovorí, namiesto čakania na úplné nahranie. To je rozdiel medzi systémom, ktorý pôsobí konverzačne, a takým, ktorý pôsobí ako režim push-to-talk.
Keď sme sa rozhodli pre WebRTC, ďalšou otázkou bolo, kde ho ukončiť (kde by sme prijímali a mali pod kontrolou pripojenie WebRTC, napríklad na edge) a ako tieto relácie prepojiť s inferenčným backendom. Ukončenie je dôležité, pretože určuje, ako spracúvame stav relácie v reálnom čase, prenos médií, smerovanie, latenciu a izoláciu zlyhaní.
SFU (jednotka selektívneho preposielania) je mediálny server, ktorý prijíma jeden stream WebRTC od každého účastníka a selektívne preposiela streamy ostatným. V tomto modeli SFU ukončuje samostatné pripojenie WebRTC pre každého účastníka a umelá inteligencia sa pripája k relácii ako ďalší účastník. To môže byť vhodné pre produkty, ktoré prirodzene zahŕňajú viacerých účastníkov, ako sú skupinové hovory, učebne alebo spoločné porady. Sústreďuje zvukové kodeky, správy RTCP, dátové kanály, nahrávanie a politiku pre jednotlivé streamy na jednom mieste.1
Aj v produktoch prepájajúcich klienta s umelou inteligenciou je SFU často predvoleným východiskovým bodom, pretože tímom umožňuje opätovne používať jeden osvedčený systém na signalizáciu, smerovanie médií, nahrávanie, pozorovateľnosť a budúce rozšírenia, ako je odovzdanie človeku alebo pridanie ďalších účastníkov.
Naša pracovná záťaž je iná. Väčšina relácií prebieha 1:1 – jeden používateľ komunikuje s jedným modelom alebo jedna aplikácia komunikuje s jedným agentom v reálnom čase – s citlivosťou na latenciu pri každom kroku. Pre takýto charakter prevádzky sme zvolili model transceivera: edge služba WebRTC ukončí klientské spojenie a následne prevedie médiá a udalosti do jednoduchších interných protokolov pre inferenciu modelu, prepis, generovanie reči, využívanie nástrojov a orchestráciu.
V tomto návrhu je transceiver jedinou službou, ktorá vlastní stav relácie WebRTC, vrátane kontrol pripojiteľnosti ICE, nadviazania spojenia DTLS, šifrovacích kľúčov SRTP a životného cyklu relácie. „Ukončenie“ tu znamená, že transceiver je koncový bod, ktorý dokončuje tieto spojenia a šifruje alebo dešifruje médiá. Udržiavanie tohto stavu na jednom mieste zjednodušilo uvažovanie o vlastníctve relácie a umožnilo backendovým službám škálovať ako bežné služby, namiesto toho, aby samy fungovali ako WebRTC peery.
Po výbere modelu transceivera sme najprv implementovali jednu službu v Go, postavenú na Pione, ktorá zabezpečovala signalizáciu aj ukončenie médií. Poháňa Chat GPT Voice, koncový bod WebRTC rozhrania Realtime API a množstvo výskumných projektov.
Z prevádzkového hľadiska služba transceivera plní dve úlohy:
- Signalizácia: Vyjednávanie SDP, výber kodekov, prihlasovacie údaje ICE a nastavenie relácie
- Médiá: Ukončovanie downstreamových pripojení WebRTC a udržiavanie upstreamových pripojení k backendovým službám na inferenciu a orchestráciu
Chceli sme, aby služba fungovala ako zvyšok našej infraštruktúry: na Kubernetes, kde sa pracovné úlohy môžu škálovať nahor aj nadol a presúvať medzi hostiteľmi podľa toho, ako sa mení dopyt. Konvenčný model WebRTC s jedným portom na reláciu sa však do tohto prostredia nehodí, pretože závisí od veľkých rozsahov verejných portov UDP, ktoré je ťažké sprístupniť, zabezpečiť a zachovať pri pridávaní, odstraňovaní alebo preplánovaní podov.2
Prvým problémom bol samotný model jedného portu na reláciu. Pri vysokej súbežnosti to znamená sprístupňovanie a správu veľmi veľkých rozsahov portov UDP.
- Vyvažovače zaťaženia v cloude a služby Kubernetes nie sú navrhnuté na podporu desaťtisícov verejných portov UDP na jednu službu. Každý ďalší rozsah zvyšuje prevádzkovú zložitosť v konfigurácii vyvažovača zaťaženia, kontrole zdravia, firewallovej politike a bezpečnosti zavádzania.3
- Veľké rozsahy portov UDP sa ťažko zabezpečujú, pretože rozširujú plochu dostupnú zvonka a sťažujú audit sieťovej politiky.
- Takisto nie sú vhodné na automatické škálovanie. V Kubernetes sa pody neustále pridávajú, odstraňujú a znovu plánujú. Požiadavka, aby každý pod rezervoval a zverejňoval veľký stabilný rozsah portov, oslabuje elasticitu systému.4
To je dôvod, prečo mnohé systémy WebRTC smerujú k jednému portu UDP na server, pričom demultiplexovanie prebieha na úrovni aplikácie za týmto portom.5
Návrhy s jedným portom na server riešia počet portov, no prinášajú druhý problém: zachovanie vlastníctva každej relácie naprieč skupinou serverov.
ICE a DTLS sú stavové protokoly. Proces, ktorý vytvoril reláciu, musí naďalej prijímať jej pakety, aby mohol overovať kontroly konektivity, dokončiť DTLS handshake, dešifrovať SRTP a spracovať neskoršie zmeny relácie, ako sú napríklad reštarty ICE. Ak pakety tej istej relácie skončia v inom procese, nastavenie môže zlyhať alebo môže dôjsť k prerušeniu prenosu médií.
To nám dalo konkrétny cieľ: vystaviť malý, fixný rozsah UDP portov verejnému internetu a zároveň smerovať každý paket k transceiveru, ktorý vlastní príslušnú WebRTC reláciu..
Zvažovali sme viacero spôsobov, ako to dosiahnuť, vrátane TURN (Traversal Using Relays around NAT), kde edge relé ukončuje klientské alokácie a preposiela prevádzku v ich mene.2
Prístup | Výhody | Nevýhody |
Jedinečný IP:port na reláciu (tiež známy ako natívny priamy UDP) | Priama cesta médií od klienta k serveru Žiadna vrstva preposielania v dátovej ceste | Vyžaduje jeden verejný port UDP na každú reláciu Veľké rozsahy portov je náročné sprístupniť a zabezpečiť Nevhodné pre Kubernetes a vyvažovače zaťaženia v cloude |
Jedinečný IP: port pre každý server | Výrazne menšia stopa verejných UDP portov v porovnaní s expozíciou na úrovni jednotlivých relácií Jeden zdieľaný soket na server dokáže demultiplexovať mnoho relácií | Funguje bez problémov na jednom hostiteľovi, ale samo osebe nie v rámci zdieľanej skupiny serverov s vyvažovaním zaťaženia Demultiplexovanie relácií na jednom hostiteľovi pomáha až po tom, čo paket dosiahne daného hostiteľa; v rámci skupiny s vyvažovaním zaťaženia môže prvý paket stále pristáť na nesprávnej inštancii, takže stále potrebujete deterministický spôsob, ako nasmerovať každú reláciu k procesu, ktorý ju vlastní. |
Relé TURN (ukončujúce protokol) | Klienti potrebujú mať prístup len k adrese a portu relé TURN. Dokáže centralizovať zásady v edge | Alokácie TURN pridávajú pri nastavovaní ďalšie komunikačné kolá Presúvať alebo obnovovať alokácie medzi servermi TURN je stále ťažké |
Bezstavový forwarder + stavový terminátor (relay + transceiver od OpenAI) | Malá verejná stopa UDP Transceiver stále vlastní celú reláciu WebRTC | Pridáva jeden krok preposielania predtým, než sa médiá dostanú k transceiveru, ktorý reláciu spravuje Vyžaduje si prispôsobenú koordináciu medzi relé a transceiverom. |
Architektúra, ktorú sme dodali, oddeľuje smerovanie paketov od ukončenia protokolu. Signalizácia sa naďalej dostáva k transceiveru na nastavenie relácie, zatiaľ čo médiá najprv vstupujú cez relé. Relé je ľahká vrstva preposielania UDP s malou verejnou stopou a transceiver je stavový koncový bod WebRTC za ňou.
Relé nedešifruje médiá, nespúšťa stavové automaty ICE ani sa nezúčastňuje vyjednávania kodekov. Prečíta dostatok metaúdajov paketu na výber cieľa, potom paket prepošle transceiveru, ktorému patrí daná relácia. Transceiver stále vidí bežný tok WebRTC a má pod kontrolou celý stav protokolu. Z pohľadu klienta sa na relácii WebRTC nič nemení.
Smerovanie prvého paketu je kľúčovým krokom v tomto nastavení. Relé musí smerovať prvý paket od klienta ešte predtým, ako na ceste paketu existuje akákoľvek relácia, a nie pozastavením na externej vyhľadávacej službe.
Každá relácia WebRTC už obsahuje natívny mechanizmus smerovania na úrovni protokolu: fragment používateľského mena ICE, ufrag, krátky identifikátor vymieňaný pri nadväzovaní relácie a opakovane používaný v kontrolách konektivity STAN. Ufrag na strane servera generujeme tak, aby obsahoval len toľko smerovacích metadát, aby relé mohlo odvodiť cieľový klaster a príslušný transceiver.
Počas signalizácie transceiver alokuje stav relácie a v odpovedi SDP vráti zdieľanú reléovú VIP adresu a port UDP. VIP je virtuálna IP adresa, ktorá slúži ako vstupný bod pre reléovú skupinu. V kombinácii s portom poskytuje klientovi jednu stabilnú cieľovú adresu, napríklad `203.0.113.10:3478`, aj keď sa za ňou nachádza mnoho reléových inštancií. Prvý paket mediálnej cesty klienta je zvyčajne požiadavka na väzbu STUN (Session Traversal Utilities for NAT), ktorú ICE používa na overenie, či pakety môžu dosiahnuť inzerovanú adresu.
Relé analyzuje z toho prvého paketu STUN len toľko, aby prečítal ufrag servera, dekódoval smerovaciu nápovedu a preposlal paket transceiveru, ktorému patrí. Každý transceiver načúva na zdieľanom sokete UDP, čo znamená jeden koncový bod operačného systému viazaný na interný IP:port, nie samostatný soket pre každú reláciu. Po tom, čo relé vytvorí reláciu zo zdrojovej dvojice IP:port klienta k cieľu daného transceivera, následné pakety DTLS, RTP a RTCP prúdia v rámci relácie bez opätovného dekódovania hodnoty ufrag.
Relácia relé je zámerne minimálna a pozostáva iba z relácie v pamäti na informovanie o presmerovaní paketov spolu s potrebnými počítadlami na monitorovanie a časovačmi na vypršanie platnosti relácie a jej vyčistenie. Táto návrhová voľba zachováva smerovanie paketov priamo na ceste paketov. Ak sa relay reštartuje a stratí reláciu, nasledujúci STUN paket ju znovu vytvorí na základe smerovacieho údaja v ufrag. Pre ešte väčšiu spoľahlivosť sa po vytvorení cesty používa vyrovnávacia pamäť Redis na uchovávanie mapovania <client IP + Port, transceiver IP + Port>, aby sa cesta dala obnoviť oveľa skôr, ešte pred príchodom ďalšieho STUN paketu.
Keď sme zredukovali verejnú UDP plochu na malý počet stabilných adries a portov, mohli sme rovnaký vzorec prenosu nasadiť globálne. Globálne relé je naša skupina geograficky rozmiestnených vstupných bodov relé, ktoré fungujú podľa rovnakých pravidiel pre preposielanie paketov.
Široký geografický prístup skracuje prvý skok medzi klientom a OpenAI, pretože paket môže vstúpiť do našej siete cez relé blízko používateľa, a to geograficky aj z hľadiska sieťovej topológie, namiesto toho, aby najprv prechádzal verejným internetom do vzdialeného regiónu. V praxi to znamená nižšiu latenciu, menší jitter a menej odvrátiteľných nárazových strát ešte predtým, ako prevádzka dosiahne náš bakbone.6
Na signalizáciu používame geografické smerovanie a smerovanie podľa blízkosti od spoločnosti Cloudflare, aby sa úvodná požiadavka HTTP alebo WebSocket dostala do blízkeho klastra transceiverov. Kontext požiadavky určuje umiestnenie relácie a to, ktorý vstupný bod globálneho relé sa oznámi klientovi. Odpoveď SDP poskytuje adresu globálneho relé, zatiaľ čo ufrag obsahuje dostatok informácií na to, aby globálne relé smerovalo médiá do určeného klastra a relé ich smerovalo do cieľového transceivera.
Geograficky riadená signalizácia a globálne relé spoločne umiestňujú nastavenie aj médiá na blízku vstupnú cestu, pričom relácia zostáva ukotvená k jednému transceiveru. Tým sa skracuje čas obojsmernej komunikácie pri signalizácii aj pri prvej kontrole konektivity ICE, čo priamo znižuje dobu čakania používateľa pred začiatkom hovoru.
Reléovú službu sme napísali v Go a implementáciu sme zámerne ponechali úzko zameranú. V Linuxe sieťový zásobník jadra prijíma UDP pakety zo sieťového rozhrania zariadenia a doručuje ich do socketu, teda koncového bodu operačného systému, z ktorého proces číta po naviazaní na IP:Port. Relé beží v používateľskom priestore, takže bežný proces Go číta hlavičky paketov z tohto socketu, aktualizuje malé množstvo stavu toku a preposiela pakety bez ukončenia WebRTC. Nepotrebovali sme žiadny framework na obídenie jadra, ktorý by umožňoval procesom v používateľskom priestore priamo oslovovať sieťové fronty s cieľom získať vyššie rýchlosti paketov, ale zároveň by zvyšoval prevádzkovú zložitosť.
Kľúčové rozhodnutia o návrhu:
- Bez ukončenia protokolu: Relé analyzuje iba hlavičky STUN/ufrag; pre následnú prevádzku DTLS, RTP a RTCP používa stav z vyrovnávacej pamäte, pričom pakety ponecháva nepriehľadné.
- Krátkodobý stav: Udržiava malú, krátkodobú mapu adresy klienta v pamäti s časovým limitom do cieľa transceivera pre stav toku a pozorovateľnosť.
- Horizontálna škálovateľnosť: Viaceré inštancie relé bežia súbežne za vyvažovačom zaťaženia. Stav nie je pevný stav WebRTC, takže reštarty spôsobujú len minimálne výpadky prevádzky a rýchle obnovenie tokov.
Opatrenia na zvýšenie efektívnosti:
SO_REUSEPORTje možnosť socketu v systéme Linux, ktorá umožňuje viacerým pracovníkom prenosu na tom istom počítači viazať sa na ten istý UDP port. Jadro potom distribuuje prichádzajúce pakety medzi týchto pracovníkov, čím sa predíde prekážke v podobe jednej čítacej slučky.runtime.LockOSThreadpripína každú gorutinu čítajúcu UDP ku konkrétnemu vláknu operačného systému. V kombinácii sSO_REUSEPORTto má tendenciu udržiavať pakety z rovnakého toku (zdrojová a cieľová IP:Port plus protokol) na rovnakom jadre CPU, čím sa zlepšuje lokalita vyrovnávacej pamäte a znižuje sa prepínanie kontextu.- Vopred alokované buffery a minimálne kopírovanie udržiavajú režijné náklady na parsing a alokáciu nízke, aby sa predišlo zberu odpadu v Go.
Táto implementácia zvládala našu globálnu mediálnu prevádzku v reálnom čase s relatívne malým pokrytím relé, takže sme si zachovali jednoduchší dizajn namiesto toho, aby sme použili obídenie jadra.
Táto architektúra nám umožňuje spúšťať médiá WebRTC v Kubernetes bez odhalenia tisícok UDP portov. Je to dôležité, pretože menšia a pevne stanovená plocha UDP sa ľahšie zabezpečuje a vyvažuje zaťaženie, čo umožňuje infraštruktúre škálovať bez rezervovania veľkých rozsahov verejných portov. Vďaka lepšej podpore infraštruktúry zo strany Kubernetes a vyššej bezpečnosti vďaka menšej útočnej ploche tento návrh zároveň zachováva štandardné správanie WebRTC pre klientov a potvrdzuje, že dizajn bez SFU bol pre naše zaťaženie správnou voľbou. Väčšina našich relácií je typu point-to-point, citlivá na latenciu a dá sa ľahšie škálovať, keď sa inferenčné služby nemusia správať ako peery WebRTC.
Širšie ponaučenie je, že najlepším miestom na pridanie komplexnosti je tenká vrstva smerovania, nie každá backendová služba a nie vlastné správanie klienta. Kódovanie metadát smerovania do poľa natívneho pre protokol nám poskytlo deterministické smerovanie prvého paketu, malú verejnú UDP stopu a dostatočnú flexibilitu na umiestnenie vstupu blízko používateľov na celom svete.
Niekoľko rozhodnutí bolo fakt dôležitých:
- Zachovajte sémantiku protokolov na edge. Klienti naďalej komunikujú podľa štandardu WebRTC, vďaka čomu zostáva interoperabilita prehliadačov a mobilných zariadení zachovaná.
- Uchovávajte komplexný stav relácií na jednom mieste. Transceiver zodpovedá za ICE, DTLS, SRTP a životný cyklus relácie. Relé iba preposiela pakety.
- Smerovanie na základe informácií, ktoré sa už nachádzajú v nastavení. ICE ufrag nám poskytol mechanizmus smerovania prvého paketu bez pridania závislosti od vyhľadávania na kritickej ceste.
- Optimalizujte pre bežný scenár skôr, než siahnete po obchádzaní jadra. Pre naše pracovné zaťaženie stačila úzka implementácia Go s opatrným použitím
SO_REUSEPORT, pripínaním vlákien a parsovaním s nízkou alokáciou.
Hlasová umelá inteligencia v reálnom čase funguje iba vtedy, keď infraštruktúra spôsobuje, že latencia je neviditeľná. Pre nás to znamenalo zmeniť podobu nášho nasadenia WebRTC bez zmeny toho, čo klienti očakávajú od samotného WebRTC.
Autor
Referencie
2. GitHub - l7mp/stunner: Mediálna brána Kubernetes pre WebRTC(otvorí sa v novom okne)
3. Porty WebRTC v skratke [Príklady] - BlogGeek.me(otvorí sa v novom okne)
4. Nasadenie do Kubernetes: dokumentácia LiveKit(otvorí sa v novom okne)
6. Cloudflare Calls: milióny kaskádových stromov až úplne nadol(otvorí sa v novom okne)


