- OpenAI a repensé son routage interne des paquets selon une architecture relay + transceiver tout en conservant le comportement WebRTC standard, afin de proposer des conversations vocales naturelles à plus de 900 millions d’utilisateurs actifs hebdomadaires
- WebRTC standardise ICE, DTLS/SRTP, la négociation des codecs, le contrôle qualité RTCP, l’annulation d’écho et le buffering anti-gigue, ce qui le rend adapté au traitement de flux audio continus à faible latence entre navigateurs, applications mobiles et serveurs
- La plupart des sessions d’OpenAI sont des sessions 1:1 dans lesquelles un utilisateur parle avec un modèle, ou une application avec un agent temps réel ; le modèle transceiver était donc plus adapté qu’un SFU, conçu pour les appels multipoints, en matière de latence et de passage à l’échelle
- Dans Kubernetes, le modèle WebRTC exposant un port UDP par session complexifiait les grandes plages de ports publics, la configuration du load balancing, les health checks, les politiques de pare-feu et la sécurité des déploiements, d’où le besoin d’une surface UDP réduite et fixe
- Le relay identifie le transceiver propriétaire grâce à un indice de routage contenu dans le ufrag ICE du premier paquet STUN, puis le transceiver prend en charge ICE, DTLS, SRTP et le cycle de vie de la session, afin de préserver à la fois la compatibilité WebRTC standard et des chemins d’ingress proches des utilisateurs partout dans le monde
Exigences d’une IA vocale à faible latence
- Une IA vocale paraît naturelle lorsque la conversation avance au rythme de la parole ; la latence réseau se traduit immédiatement par des silences gênants, des interruptions tronquées ou des réactions tardives aux interruptions
- À l’échelle d’OpenAI, il faut une portée mondiale pour plus de 900 millions d’utilisateurs actifs hebdomadaires, une mise en relation rapide permettant de parler juste après le démarrage de la session, un aller-retour média faible et stable, ainsi qu’une faible gigue et peu de pertes de paquets
- ChatGPT voice, la Realtime API, les agents dans les workflows conversationnels et, plus généralement, tous les modèles qui doivent traiter l’audio pendant que l’utilisateur parle sont affectés par ces caractéristiques de latence
- OpenAI a repensé sa pile WebRTC avec une architecture séparée relay + transceiver pour modifier le routage interne des paquets tout en conservant le comportement WebRTC standard côté client
Pourquoi avoir choisi WebRTC
- WebRTC est un standard ouvert pour envoyer audio, vidéo et données à faible latence entre navigateurs, applications mobiles et serveurs, et il convient aussi comme base pour des systèmes temps réel client-serveur
- WebRTC standardise l’établissement de connexion et la traversée NAT via ICE, le transport chiffré DTLS/SRTP, la négociation des codecs, le contrôle qualité RTCP, ainsi que des fonctions côté client comme l’annulation d’écho et le buffering anti-gigue
- Sans WebRTC, chaque client devrait résoudre séparément l’établissement de la connexion dans des environnements NAT, le chiffrement des médias, la négociation des codecs et l’adaptation aux changements réseau
- Une propriété essentielle pour les produits IA est que l’audio arrive sous forme de flux continu, ce qui permet de lancer la transcription, l’inférence, les appels d’outils et la génération vocale sans attendre la fin de l’upload complet de l’utilisateur
- C’est cette différence qui sépare un système perçu comme conversationnel d’un système qui ressemble à du push-to-talk
- OpenAI s’appuie sur l’écosystème WebRTC, avec ses implémentations open source matures et son travail de standardisation ; grâce aux fondations posées notamment par Justin Uberti et Sean DuBois, l’entreprise a pu construire sur une infrastructure média éprouvée sans réinventer le transport bas niveau, le chiffrement ni le contrôle de congestion
Une architecture fondée sur un transceiver plutôt qu’un SFU
-
Quand un SFU est pertinent
- Un SFU est un serveur média qui reçoit les flux WebRTC de chaque participant et les retransmet sélectivement aux autres participants
- Dans un modèle SFU, une connexion WebRTC distincte est terminée pour chaque participant, et l’IA rejoint elle aussi la session comme un participant supplémentaire
- Les SFU peuvent être bien adaptés aux produits intrinsèquement multipoints, comme les appels de groupe, les classes ou les réunions collaboratives
- Ils permettent de centraliser codecs audio, messages RTCP, canaux de données, enregistrements et politiques par flux
- Même dans des produits client-vers-IA, ils constituent souvent un point de départ naturel car ils permettent de réutiliser dans un même système le traitement du signal, le routage média, l’enregistrement, l’observabilité et de futures extensions comme le transfert vers un humain ou l’ajout de participants
-
La charge de travail d’OpenAI
- La plupart des sessions d’OpenAI sont des sessions 1:1 où un utilisateur échange avec un modèle, ou une application avec un agent temps réel
- Comme ce trafic est sensible à la latence à chaque tour de parole, OpenAI a retenu le modèle transceiver
- Dans ce modèle, le service WebRTC en edge termine la connexion client, puis convertit les médias et événements vers un protocole interne simple destiné à l’inférence du modèle, la transcription, la synthèse vocale, l’utilisation d’outils et l’orchestration
- Le transceiver est le seul service qui possède l’état de la session WebRTC, y compris les vérifications de connexion ICE, le handshake DTLS, les clés de chiffrement SRTP et le cycle de vie de la session
- En centralisant l’état de session à un seul endroit, l’appartenance d’une session devient plus simple à comprendre, et les services backend peuvent monter en charge comme des services classiques au lieu de se comporter comme des pairs WebRTC
Première implémentation et contraintes rencontrées dans Kubernetes
- La première implémentation d’OpenAI était un service Go monolithique basé sur Pion, chargé à la fois de la signalisation et de la terminaison média
- Ce service de transceiver alimente ChatGPT voice, l’endpoint WebRTC de la Realtime API et plusieurs projets de recherche
- Côté exploitation, le transceiver gère en signaling la négociation SDP, le choix des codecs, les identifiants ICE et l’établissement de session
- Côté media, il termine la connexion WebRTC downstream et maintient des connexions upstream vers les services backend d’inférence et d’orchestration
- OpenAI voulait exécuter ce service sur Kubernetes, afin qu’il puisse monter ou descendre en charge selon la demande et se déplacer entre hôtes
- Le modèle WebRTC traditionnel avec un port par session s’accordait mal avec un environnement Kubernetes, car il fallait exposer, sécuriser et maintenir une très grande plage de ports UDP publics
- À forte concurrence, un port par session impose d’exposer et d’administrer une plage de ports UDP extrêmement large
- Les load balancers cloud et les services Kubernetes ne sont pas conçus avec l’hypothèse de dizaines de milliers de ports UDP publics par service, et plus la plage de ports grandit, plus la configuration du load balancing, les health checks, les politiques de pare-feu et la sécurité des déploiements se compliquent
- Une grande plage de ports UDP augmente aussi la surface accessible depuis l’extérieur et complique l’audit des politiques réseau, ce qui nuit à la sécurité
- Dans Kubernetes, comme les pods sont continuellement ajoutés, supprimés ou replanifiés, exiger que chaque pod réserve et annonce une grande plage de ports stable fragilise l’élasticité
Port unique et problème d’appartenance des sessions
- Beaucoup de systèmes WebRTC réduisent le problème du nombre de ports en utilisant un port UDP unique par serveur et un démultiplexage au niveau applicatif
- Une conception à port unique par serveur réduit le nombre de ports, mais crée un second problème : il faut préserver l’appartenance de chaque session à travers l’ensemble de la flotte
- ICE et DTLS sont des protocoles à état ; le processus qui a créé la session doit donc continuer à recevoir les paquets de cette session pour valider les vérifications de connexion, terminer le handshake DTLS, déchiffrer le SRTP et gérer les modifications ultérieures comme un redémarrage ICE
- Si des paquets d’une même session arrivent sur un autre processus, l’établissement peut échouer ou les médias se dégrader
- L’objectif d’OpenAI était d’exposer à Internet public uniquement une surface UDP réduite et fixe, tout en routant tous les paquets vers le transceiver propriétaire de la session WebRTC correspondante
-
Approches examinées
- Un IP:port unique par session offre un chemin média client-serveur direct sans couche de forwarding dans le chemin de données, mais exige un port UDP public par session, ce qui s’accorde mal avec Kubernetes, les load balancers cloud et les exigences de sécurité
- Un IP:port unique par serveur réduit fortement l’empreinte UDP publique par rapport à une exposition par session et permet à un socket partagé de démultiplexer de nombreuses sessions, mais dans une flotte avec load balancing partagé, le premier paquet peut arriver sur la mauvaise instance ; il faut donc un moyen déterministe de l’envoyer au processus propriétaire de la session
- Un relay TURN impose seulement que le client puisse joindre l’adresse et le port du relay TURN, et permet de centraliser les politiques en edge, mais l’allocation TURN ajoute des allers-retours à l’établissement, et il reste difficile de déplacer ou restaurer des allocations entre serveurs TURN
- L’architecture relay + transceiver d’OpenAI, soit un forwarder stateless + terminator stateful, conserve une petite empreinte UDP publique et laisse le transceiver propriétaire de l’intégralité de la session WebRTC, mais ajoute un saut de forwarding avant que le média n’atteigne le transceiver propriétaire et nécessite une coordination sur mesure entre relay et transceiver
Architecture relay + transceiver
- L’architecture déployée par OpenAI sépare le routage des paquets de la terminaison du protocole
- La signalisation atteint le transceiver pour l’établissement de la session, tandis que le média entre d’abord par le relay
- Le relay est une couche légère de forwarding UDP avec une petite empreinte publique, tandis que le transceiver est l’endpoint WebRTC stateful situé derrière
- Le relay ne déchiffre pas les médias, n’exécute pas de machine à états ICE et ne participe pas à la négociation des codecs
- Il ne lit que les métadonnées de paquet suffisantes pour choisir la destination, puis transmet le paquet au transceiver propriétaire de la session
- Le transceiver, lui, voit toujours un flux WebRTC normal et conserve tout l’état du protocole
- Du point de vue du client, la session WebRTC ne change pas
Routage du premier paquet et usage de l’ufrag ICE
- La clé de cette architecture est que le relay route le premier paquet client directement dans le chemin des paquets lui-même
- Une session WebRTC possède déjà un crochet de routage natif au protocole : l’ICE username fragment, ou ufrag
- L’ufrag est un identifiant court échangé pendant l’établissement de session puis transporté à nouveau dans les vérifications de connexion STUN
- OpenAI génère l’ufrag côté serveur de façon à y intégrer assez de métadonnées de routage pour que le relay puisse déduire le cluster de destination et le transceiver propriétaire
- Pendant le signaling, le transceiver alloue l’état de session et renvoie dans la réponse SDP le VIP partagé du relay ainsi que le port UDP
- Le VIP est une adresse IP virtuelle placée devant la flotte de relays ; combinée au port, elle fournit au client une destination unique et stable, par exemple
203.0.113.10:3478, même derrière plusieurs instances de relay - Le premier paquet sur le chemin média côté client est généralement une requête STUN binding, qu’ICE utilise pour vérifier que les paquets peuvent atteindre l’adresse annoncée
- Le relay parse juste assez le premier paquet STUN pour lire l’ufrag serveur, décoder l’indice de routage et transmettre le paquet au transceiver propriétaire de la session
- Chaque transceiver reçoit sur un socket UDP partagé lié à un IP:port interne, c’est-à-dire un endpoint du système d’exploitation, et non un socket distinct par session
- Une fois que le relay a créé une session entre l’IP:port source du client et la destination du transceiver, les paquets DTLS, RTP et RTCP suivants circulent dans cette session sans qu’il soit nécessaire de redécoder l’ufrag
- La session du relay conserve un état minimal : une session en mémoire pour le forwarding des paquets, des compteurs de monitoring, ainsi que des timers pour l’expiration et le nettoyage des sessions
- Si le relay redémarre et perd sa session, le paquet STUN suivant la recrée grâce à l’indice de routage contenu dans l’ufrag
- Une fois le chemin établi, un cache Redis conserve le mapping
<client IP + Port, transceiver IP + Port>, ce qui permet une récupération plus rapide avant même l’arrivée du paquet STUN suivant
Global Relay et chemin d’entrée de proximité
- Après avoir réduit la surface UDP publique à un petit nombre d’adresses et de ports stables, OpenAI a pu déployer le même modèle de relay à l’échelle mondiale
- Global Relay est une flotte de points d’ingress relay géographiquement distribués qui implémentent le même comportement de forwarding de paquets
- Un ingress géographique étendu permet aux paquets des utilisateurs d’entrer dans le réseau d’OpenAI via un relay proche en termes de géographie et de topologie réseau, au lieu de traverser d’abord l’Internet public jusqu’à une région éloignée, ce qui réduit le premier saut client-vers-OpenAI
- Cette approche diminue la latence, la gigue et les rafales de pertes évitables avant que le trafic n’atteigne le backbone
- OpenAI utilise le géoroutage et le steering de proximité de Cloudflare pour que la requête HTTP ou WebSocket initiale atteigne un cluster de transceivers proche
- Le contexte de la requête détermine l’emplacement de la session et le point d’ingress Global Relay à annoncer au client
- La réponse SDP fournit l’adresse Global Relay, et l’ufrag contient assez d’informations pour que Global Relay route le média vers le cluster désigné, puis que le relay l’achemine jusqu’au transceiver de destination
- En combinant signaling géorouté et Global Relay, aussi bien l’établissement que le média passent par un chemin d’entrée proche, tout en gardant la session attachée à un transceiver unique
- Cette structure réduit le temps aller-retour du signaling et des premières vérifications de connexion ICE, diminuant directement l’attente avant que l’utilisateur puisse commencer à parler
Mise en œuvre du relay
- Le service relay est écrit en Go et son périmètre d’implémentation a été volontairement gardé étroit
- Sous Linux, la pile réseau du kernel reçoit les paquets UDP sur l’interface réseau et les remet à un socket ; le relay, simple processus Go en userspace, lit les en-têtes des paquets depuis ce socket
- Le relay met à jour une petite quantité d’état de flux, puis transmet les paquets sans terminer WebRTC
- OpenAI n’a pas utilisé de kernel-bypass framework, c’est-à-dire un mécanisme où un processus userspace sonde directement la file réseau pour obtenir un débit de paquets supérieur, estimant que cette approche aurait ajouté de la complexité opérationnelle
-
Principaux choix de conception
- Aucune terminaison de protocole : le relay ne parse que l’en-tête STUN et l’ufrag ; ensuite, pour DTLS, RTP et RTCP, il s’appuie sur l’état mis en cache afin de laisser les paquets opaques
- État transitoire : il maintient une petite map en mémoire, à timeout court, allant de l’adresse client à la destination du transceiver, pour l’état de flux et l’observabilité
- Scalabilité horizontale : plusieurs instances de relay s’exécutent en parallèle derrière un load balancer ; comme l’état du relay n’est pas un état WebRTC dur, les pertes de trafic lors d’un redémarrage restent faibles et la récupération des flux est rapide
-
Mesures d’optimisation
SO_REUSEPORTest une option de socket Linux qui permet à plusieurs workers relay sur une même machine de se binder au même port UDP ; le kernel répartit alors les paquets entrants entre eux, évitant qu’une seule boucle de lecture ne devienne un goulot d’étranglementruntime.LockOSThreadfixe chaque goroutine de lecture UDP à un thread OS spécifique- En combinant
SO_REUSEPORTet l’affinité de thread, les paquets d’un même flux ont tendance à rester sur le même cœur CPU, ce qui améliore la localité du cache et réduit les changements de contexte - Des buffers préalloués et un minimum de copies réduisent le coût du parsing et des allocations, aidant à éviter le garbage collection de Go
- Cette implémentation a suffi à traiter le trafic média temps réel mondial d’OpenAI avec une empreinte relay relativement réduite ; l’entreprise a donc conservé une conception plus simple plutôt que d’opter pour une voie de kernel bypass
Résultats et enseignements
- Cette architecture permet d’exécuter des médias WebRTC dans Kubernetes sans exposer des milliers de ports UDP
- Une surface UDP réduite et fixe simplifie la sécurité et le load balancing, tout en permettant à l’infrastructure de monter en charge sans réserver de grandes plages de ports publics
- Cette conception préserve le comportement WebRTC standard côté client et confirme qu’une architecture sans SFU constituait le bon choix par défaut pour la charge de travail d’OpenAI
- La plupart des sessions sont point à point, sensibles à la latence, et les services d’inférence montent plus facilement en charge lorsqu’ils n’ont pas à se comporter comme des pairs WebRTC
- Il était plus pertinent de placer la complexité dans une fine couche de routage plutôt que dans tous les services backend ou dans un comportement client personnalisé
- En encodant des métadonnées de routage dans un champ natif au protocole, OpenAI a obtenu un routage déterministe du premier paquet, une faible empreinte UDP publique et la flexibilité nécessaire pour placer des points d’ingress proches des utilisateurs partout dans le monde
-
Choix particulièrement importants
- Préserver la sémantique du protocole en edge : le client continue d’utiliser WebRTC standard, ce qui maintient l’interopérabilité avec les navigateurs et les applications mobiles
- Garder l’état de session difficile à un seul endroit : le transceiver possède ICE, DTLS, SRTP et le cycle de vie de session, tandis que le relay ne fait que transmettre les paquets
- Router à partir d’informations déjà présentes dans l’établissement : l’ufrag ICE fournit un crochet de routage du premier paquet sans dépendance à une lookup sur le hot path
- Privilégier l’optimisation du cas courant plutôt que le kernel bypass : une implémentation Go ciblée, utilisant soigneusement
SO_REUSEPORT, l’affinité de thread et un parsing à faibles allocations, s’est révélée suffisante pour la charge de travail d’OpenAI - L’IA vocale temps réel fonctionne lorsque l’infrastructure rend la latence imperceptible ; OpenAI a choisi de changer la manière de déployer WebRTC, sans changer le comportement que les clients attendent de WebRTC
1 commentaires
Commentaires sur Hacker News
Merci beaucoup à OpenAI d’avoir mis en avant un cas d’usage de Pion, la bibliothèque sur laquelle je travaille
Si vous ne connaissez pas bien WebRTC, c’est un domaine assez fascinant, et je suis aussi en train d’écrire un livre qui explique son fonctionnement, WebRTC for the Curious
https://github.com/pion/webrtc
https://webrtcforthecurious.com
On avait l’impression qu’ils avaient énormément augmenté la complexité pour réduire une partie déjà parmi les plus rapides dans la pile de l’IA vocale. Un modèle rapide et une détection d’activité vocale (VAD) précise me semblent bien plus importants que de peaufiner au millimètre le temps de transport WebRTC
J’en avais lu une partie, car j’avais autrefois l’idée d’envoyer des données d’une base de données vers un client navigateur via CLI en utilisant les canaux de données WebRTC, et cela m’a aidé à comprendre que ce n’était sans doute pas adapté à mon cas d’usage. Au final, j’ai utilisé un plan de contrôle centralisé et WebSocket
Cela dit, on pourrait sûrement faire quelque chose d’intéressant avec WebRTC data channels + Apache Arrow ArrayBuffer sans copie + duckdb WASM, mais je n’ai pas encore trouvé quoi
srcimbriquésÇa rend le README beaucoup plus difficile à atteindre
La faible latence ressemble moins à un avantage d’implémentation qu’à une souffrance
Quand on essaie d’avoir une conversation détendue, on fait naturellement de petites pauses, mais GPT interprète cela comme « j’ai fini de parler » et se met aussitôt à bavarder
En vieillissant, il me faut plus de temps pour trouver le mot que je veux, et ces GPT vocaux très rapides sont plus irritants qu’utiles. Il faut avoir toute la phrase déjà formulée dans sa tête avant de parler, ce qui n’a rien de naturel
La latence évoquée dans l’article correspond au délai de transport du flux audio lui-même, tandis qu’ici il s’agit plutôt de la rapidité avec laquelle on commence à répondre à l’intérieur du flux audio
On ressent une sorte de pression à continuer de parler alors même qu’on n’a pas encore fini de réfléchir, ce qui rend l’expérience assez peu naturelle. Si on cherche le bon mot, il faut avoir l’occasion de le trouver
À mon avis, la solution n’est pas un protocole à plus forte latence, mais une gestion plus intelligente des pauses. Avec une faible latence, le bot peut s’interrompre immédiatement quand l’utilisateur coupe la parole
Ce n’est pas parfait, mais ça interrompt moins
J’ai aussi l’impression qu’il consacre l’essentiel de son intelligence non pas à réfléchir au problème, mais à avoir l’air plausible. Des choses comme : « Oui, bien sûr. Je comprends pourquoi vous souhaitez cela… ». C’est probablement lié aux contraintes de temps et au coût plus élevé du traitement vocal. Les réponses textuelles consacrent davantage de temps à la tâche elle-même
« Plus de 900 millions d’utilisateurs actifs hebdomadaires » semble évidemment désigner l’ensemble des utilisateurs de ChatGPT, et j’imagine que la part de ceux qui utilisent les fonctions vocales est bien plus faible
Ce genre de chiffres influe sur les décisions métier, par exemple sur le niveau d’optimisation matérielle et logicielle qu’il vaut la peine d’investir dans ce problème
Cela désigne le nombre total d’utilisateurs susceptibles d’être exposés à la fonctionnalité, qu’ils l’utilisent réellement ou non
C’est vraiment appréciable qu’ils partagent cela, mais il faut garder à l’esprit que les modèles audio temps réel d’OpenAI en sont encore, côté capacités, au niveau de la famille 4o
Cela reste malgré tout très utile, et c’est dommage qu’il n’y ait pas de véritable concurrent dans ce domaine. Une expérience proche d’une vraie conversation m’a beaucoup aidé à exprimer des idées et des concepts
Il faut simplement garder en tête qu’à ce stade, ce n’est plus le modèle de pointe comme à sa sortie. Si Sam voit ça, j’aimerais bien qu’il sorte un nouveau modèle audio temps réel
Le Gemini flash live 3.1 de Google est meilleur, surtout via l’API. Il permet aussi les appels d’outils, peut être branché à d’autres LLM plus intelligents si on le configure soi-même, et on peut régler le niveau de raisonnement. Même avec un niveau de raisonnement élevé, on reste assez proche du temps réel, et il peut enrichir ses réponses avec la recherche Google. Si vous aimez la voix bidirectionnelle, c’est probablement la meilleure option actuellement, et on peut l’essayer dans AI Studio
Pour quelqu’un qui veut se lancer dans ce domaine, pipecat est un excellent dépôt open source et une bonne communauté
https://github.com/pipecat-ai/pipecat
Je ne l’ai découvert qu’il y a quelques semaines, et depuis la sortie de Gemma 4, je construis depuis zéro un assistant vocal entièrement local avec Gemma 4 + Kokoro TTS + Whisper : https://github.com/pncnmnp/strawberry
Le modèle smart turn de Pipecat est vraiment bon pour la détection d’activité vocale (VAD) : https://huggingface.co/pipecat-ai/smart-turn-v3
Si un meilleur modèle réfléchit davantage avant de répondre, cela me va d’attendre plus longtemps la réponse
À condition qu’il gère bien les interruptions, qu’il ne se mette pas à répondre immédiatement après une seconde de silence, et qu’il sache intelligemment déterminer si j’ai fini de parler
Ce n’est peut-être pas seulement une question de latence
Si on maintient l’utilisateur dans une conversation vocale, on peut obtenir des données d’apprentissage impossibles à avoir par le texte. C’est peut-être pour cela qu’ils ont choisi une approche par transceiver plutôt que par SFU, et qu’ils ont pu en grande partie ignorer les conversations à plusieurs
Faut-il comprendre qu’OpenAI n’utilise désormais plus LiveKit pour WebRTC/l’audio ?
Dans cette architecture, un serveur LiveKit ne paraît pas vraiment correspondre à ce qu’ils veulent. D’ailleurs, la discussion sur le SFU dans l’article le dit pratiquement tel quel. En revanche, il y a beaucoup de choses utiles dans les SDK client
Si le transceiver plante en plein flux, comment une session active peut-elle être récupérée ?
Le système rétablit-il automatiquement le contexte sur une nouvelle session WebRTC ?
On peut sauvegarder ou suspendre tout l’état WebRTC, puis le restaurer dans le processus suivant
Si vous voulez vous faire des amis, je pense qu’il vaut mieux rejoindre un club ou un groupe, sous une forme ou une autre