3 pontos por GN⁺ 7 시간 전 | 1 comentários | Compartilhar no WhatsApp
  • A OpenAI redesenhou o roteamento interno de pacotes em uma estrutura de relay + transceiver, mantendo o comportamento padrão do WebRTC, para oferecer conversas por voz naturais a mais de 900 milhões de usuários ativos por semana
  • O WebRTC padroniza ICE, DTLS/SRTP, negociação de codecs, controle de qualidade via RTCP, cancelamento de eco e buffering de jitter, sendo adequado para processar fluxos contínuos de áudio de baixa latência entre navegador, app móvel e servidor
  • Como a maioria das sessões da OpenAI é de 1:1, com 1 usuário e 1 modelo ou 1 aplicação e 1 agente em tempo real conversando, o modelo de transceiver era mais adequado para latência e escalabilidade do que um SFU voltado para chamadas com vários participantes
  • No Kubernetes, o modelo WebRTC de expor 1 porta UDP por sessão tornava complexos o grande intervalo de portas públicas, a configuração de load balancers, health checks, políticas de firewall e a segurança de rollouts, exigindo uma superfície UDP pequena e fixa
  • O relay localiza o transceiver proprietário por meio de pistas de roteamento contidas no ICE ufrag do primeiro pacote STUN, enquanto o transceiver mantém ICE, DTLS, SRTP e o ciclo de vida da sessão, preservando a compatibilidade com o WebRTC padrão e caminhos de ingresso próximos no mundo todo

Requisitos da IA de voz de baixa latência

  • A IA de voz parece natural quando a conversa anda na velocidade da fala, e a latência de rede aparece imediatamente como silêncios estranhos, interrupções cortadas ou respostas tardias a interrupções
  • Na escala da OpenAI, é necessário alcance global para mais de 900 milhões de usuários ativos por semana, estabelecimento rápido de conexão para que se possa falar logo após o início da sessão, tempo de ida e volta de mídia baixo e estável, além de baixo jitter e perda de pacotes
  • ChatGPT voice, a Realtime API, agentes em fluxos de trabalho conversacionais e modelos que precisam processar áudio enquanto o usuário está falando são todos afetados por essas características de latência
  • A OpenAI redesenhou a stack de WebRTC com uma estrutura separada de relay + transceiver para mudar o roteamento interno de pacotes sem alterar o comportamento padrão do WebRTC no cliente

Por que escolher WebRTC

  • WebRTC é um padrão aberto para enviar áudio, vídeo e dados de baixa latência entre navegadores, apps móveis e servidores, e também é adequado como base para sistemas cliente-servidor em tempo real
  • O WebRTC padroniza o estabelecimento de conexão e a travessia de NAT com ICE, o transporte criptografado com DTLS/SRTP, a negociação de codecs, o controle de qualidade via RTCP e recursos do cliente como cancelamento de eco e buffering de jitter
  • Sem WebRTC, cada cliente precisaria resolver separadamente a criação de conexão em ambientes com NAT, a criptografia da mídia, a negociação de codecs e a adaptação a mudanças na rede
  • Uma característica importante em produtos de IA é que o áudio chega como um fluxo contínuo, permitindo iniciar transcrição, inferência, chamadas de ferramentas e geração de voz sem esperar o upload completo do usuário terminar
  • Essa diferença separa um sistema que parece conversacional de um sistema que parece push-to-talk
  • A OpenAI se apoia no ecossistema do WebRTC, incluindo implementações open source maduras e o trabalho de padronização, e graças à base construída por Justin Uberti e Sean DuBois pôde desenvolver sobre uma infraestrutura de mídia já validada, sem recriar transporte de baixo nível, criptografia e controle de congestionamento

A arquitetura que escolheu transceiver em vez de SFU

  • Quando um SFU faz sentido

    • Um SFU é um servidor de mídia que recebe fluxos WebRTC de cada participante e os encaminha seletivamente aos demais
    • No modelo com SFU, uma conexão WebRTC separada é encerrada para cada participante, e a IA também entra como mais um participante da sessão
    • Um SFU pode ser uma boa opção para produtos que são inerentemente multiparte, como chamadas em grupo, salas de aula e reuniões colaborativas
    • Ele permite concentrar em um único lugar codecs de áudio, mensagens RTCP, data channels, gravação e políticas por fluxo
    • Mesmo em produtos cliente-para-IA, ele pode parecer um ponto de partida natural, pois reutiliza no mesmo sistema o processamento de sinal, o roteamento de mídia, a gravação, a observabilidade e futuras expansões como transferir para um humano ou adicionar participantes
  • A carga de trabalho da OpenAI

    • A maioria das sessões da OpenAI é de 1:1, com 1 usuário e 1 modelo, ou 1 aplicação e 1 agente em tempo real
    • Como esse padrão de tráfego é sensível à latência em cada turno, foi escolhido o modelo de transceiver
    • No modelo de transceiver, o serviço WebRTC de edge encerra a conexão do cliente e então converte mídia e eventos em um protocolo interno simples para inferência do modelo, transcrição, geração de voz, uso de ferramentas e orquestração
    • O transceiver é o único serviço que detém o estado da sessão WebRTC, incluindo verificações de conectividade ICE, handshake DTLS, chaves de criptografia SRTP e o ciclo de vida da sessão
    • Manter o estado da sessão em um único lugar torna mais fácil entender a propriedade da sessão, e os serviços de backend podem escalar como serviços comuns, sem precisar se comportar como peers WebRTC

Implementação inicial e as limitações encontradas no Kubernetes

  • A primeira implementação da OpenAI foi um único serviço em Go baseado em Pion, responsável tanto pelo signaling quanto pelo encerramento da mídia
  • Esse serviço transceiver atende o ChatGPT voice, o endpoint WebRTC da Realtime API e vários projetos de pesquisa
  • Operacionalmente, no signaling, o transceiver trata da negociação SDP, seleção de codecs, credenciais ICE e configuração da sessão
  • Na parte de media, ele encerra a conexão WebRTC downstream e mantém conexões upstream com serviços de backend para inferência e orquestração
  • A OpenAI queria operar esse serviço sobre Kubernetes, onde ele pudesse escalar para cima e para baixo conforme a demanda e mover-se entre hosts
  • O modelo WebRTC tradicional de 1 porta por sessão não se encaixava bem no ambiente Kubernetes, exigindo expor, proteger e manter um grande intervalo de portas UDP públicas
  • Em alta concorrência, 1 porta por sessão significa expor e administrar um intervalo muito grande de portas UDP
  • Load balancers em nuvem e serviços do Kubernetes não foram projetados assumindo dezenas de milhares de portas UDP públicas por serviço, e à medida que o intervalo de portas cresce, aumentam a complexidade da configuração do load balancer, dos health checks, das políticas de firewall e da segurança dos rollouts
  • Um grande intervalo de portas UDP também é pior para a segurança, pois amplia a superfície acessível externamente e dificulta auditorias de políticas de rede
  • No Kubernetes, pods são adicionados, removidos e reagendados o tempo todo, então a elasticidade fica frágil se cada pod precisar reservar e anunciar um intervalo grande e estável de portas

O problema da porta única e da propriedade da sessão

  • Muitos sistemas WebRTC usam uma única porta UDP por servidor e multiplexação no nível da aplicação para reduzir o problema da quantidade de portas
  • Um design de porta única por servidor reduz o número de portas, mas cria um segundo problema: é preciso preservar a propriedade de cada sessão em toda a frota
  • Como ICE e DTLS são protocolos com estado, o processo que criou a sessão precisa continuar recebendo seus pacotes para validar verificações de conectividade, concluir o handshake DTLS, descriptografar SRTP e lidar com mudanças posteriores da sessão, como ICE restart
  • Se pacotes da mesma sessão chegarem a outro processo, a configuração pode falhar ou a mídia pode se corromper
  • O objetivo da OpenAI era expor apenas uma superfície UDP pequena e fixa à internet pública, ao mesmo tempo garantindo que todos os pacotes fossem roteados ao transceiver proprietário daquela sessão WebRTC
  • Abordagens avaliadas

    • IP:porta único por sessão oferece um caminho direto de mídia cliente-servidor e não adiciona uma camada de encaminhamento no caminho de dados, mas exige 1 porta UDP pública por sessão e um grande intervalo de portas, o que não combina com Kubernetes, load balancers em nuvem e requisitos de segurança
    • IP:porta único por servidor tem uma footprint UDP pública muito menor do que a exposição por sessão e permite que um soquete compartilhado faça multiplexação de muitas sessões, mas em uma frota com load balancing compartilhado o primeiro pacote pode chegar à instância errada, então é necessário um modo determinístico de enviá-lo ao processo que detém a sessão
    • TURN relay exige apenas que o cliente alcance o endereço e a porta do relay TURN e centraliza políticas no edge, mas a allocation TURN adiciona ida e volta à configuração, e migrar ou recuperar allocations entre servidores TURN continua difícil
    • A estrutura relay + transceiver da OpenAI, com stateless forwarder + stateful terminator, mantém uma footprint UDP pública pequena e faz o transceiver deter toda a sessão WebRTC, mas adiciona um salto de encaminhamento antes de a mídia chegar ao transceiver proprietário e exige coordenação customizada entre relay e transceiver

Arquitetura relay + transceiver

  • A estrutura implantada pela OpenAI separa roteamento de pacotes de encerramento do protocolo
  • O signaling chega ao transceiver para configurar a sessão, enquanto a mídia entra primeiro pelo relay
  • O relay é uma camada leve de encaminhamento UDP com footprint pública pequena, e o transceiver é o endpoint WebRTC com estado que fica atrás dele
  • O relay não descriptografa mídia, não executa a máquina de estados do ICE e não participa da negociação de codecs
  • O relay lê apenas os metadados do pacote necessários para escolher o destino e encaminha os pacotes ao transceiver que detém a sessão
  • O transceiver continua vendo um fluxo WebRTC normal e mantém todo o estado do protocolo
  • Do ponto de vista do cliente, a sessão WebRTC não muda

Roteamento do primeiro pacote e uso do ICE ufrag

  • O ponto central dessa arquitetura é que o relay roteia o primeiro pacote do cliente no próprio caminho dos pacotes
  • Uma sessão WebRTC já possui um gancho de roteamento nativo do protocolo: o ICE username fragment, ou ufrag
  • O ufrag é um identificador curto trocado durante a configuração da sessão e transportado novamente nas verificações de conectividade STUN
  • A OpenAI gera o ufrag do lado do servidor de forma que ele carregue metadados de roteamento suficientes para o relay inferir o cluster de destino e o transceiver proprietário
  • Durante o signaling, o transceiver aloca o estado da sessão e retorna no SDP answer um VIP compartilhado de relay e uma porta UDP
  • O VIP é um endereço IP virtual na frente da frota de relays e, combinado com a porta, fornece ao cliente um único destino estável, como 203.0.113.10:3478, mesmo por trás de várias instâncias de relay
  • O primeiro pacote no caminho de mídia vindo do cliente geralmente é um STUN binding request, que o ICE usa para verificar se pacotes conseguem alcançar o endereço anunciado
  • O relay faz o parsing apenas do necessário no primeiro pacote STUN para ler o ufrag do servidor, decodificar a pista de roteamento e encaminhar o pacote ao transceiver proprietário da sessão
  • Cada transceiver recebe em um soquete UDP compartilhado, que é um endpoint do sistema operacional vinculado a um IP:porta interno, e não em um soquete por sessão
  • Quando o relay cria uma sessão do IP:porta de origem do cliente até o destino do transceiver, os pacotes posteriores de DTLS, RTP e RTCP passam a fluir nessa sessão sem precisar decodificar o ufrag novamente
  • A sessão no relay mantém o mínimo de estado possível: uma sessão em memória para encaminhamento de pacotes, contadores de monitoramento e timers para expiração e limpeza
  • Mesmo se o relay reiniciar e perder a sessão, o próximo pacote STUN recria a sessão usando a pista de roteamento contida no ufrag
  • Depois que o caminho é estabelecido, um cache Redis armazena o mapeamento <IP do cliente + Porta, IP do transceiver + Porta>, permitindo recuperação mais rápida mesmo antes do próximo pacote STUN

Global Relay e caminhos de entrada próximos

  • Depois de reduzir a superfície UDP pública a um conjunto pequeno e estável de endereços e portas, a OpenAI pôde implantar o mesmo padrão de relay no mundo todo
  • O Global Relay é uma frota geograficamente distribuída de pontos de entrada relay que implementa o mesmo comportamento de encaminhamento de pacotes
  • Ter ingressos amplamente distribuídos geograficamente faz com que os pacotes do usuário entrem na rede da OpenAI por um relay próximo em geografia e topologia de rede, em vez de primeiro atravessarem a internet pública até uma região distante, reduzindo o primeiro salto entre cliente e OpenAI
  • Isso reduz latência, jitter e rajadas evitáveis de perda antes de o tráfego alcançar o backbone
  • A OpenAI usa geolocalização da Cloudflare e steering por proximidade no signaling para que a requisição inicial via HTTP ou WebSocket chegue a um cluster de transceivers próximo
  • O contexto da requisição determina a localização da sessão e o ponto de entrada do Global Relay que será anunciado ao cliente
  • O SDP answer fornece o endereço do Global Relay, e o ufrag contém informações suficientes para que o Global Relay roteie a mídia ao cluster designado e o relay a encaminhe ao transceiver de destino
  • Com o uso conjunto de signaling com geo-steering e Global Relay, tanto a configuração quanto a mídia passam por um caminho de entrada próximo, enquanto a sessão continua fixada em um único transceiver
  • Essa arquitetura reduz o tempo de ida e volta do signaling e da primeira verificação de conectividade ICE, diminuindo diretamente o tempo de espera até o usuário começar a falar

Como o relay foi implementado

  • O serviço de relay foi escrito em Go e teve deliberadamente um escopo de implementação estreito
  • No Linux, a stack de rede do kernel recebe pacotes UDP da interface de rede e os entrega ao socket, e o relay roda como um processo Go comum em userspace, lendo os headers dos pacotes no socket
  • O relay atualiza uma pequena quantidade de estado de fluxo e encaminha os pacotes sem encerrar o WebRTC
  • A OpenAI não usou um kernel-bypass framework, no qual um processo em userspace faz polling direto das filas de rede para suportar taxas maiores de pacotes, pois considerou que isso adicionaria complexidade operacional
  • Principais escolhas de design

    • Sem encerramento de protocolo: o relay faz parsing apenas do header STUN e do ufrag e, depois disso, usa estado em cache para manter DTLS, RTP e RTCP como pacotes opacos
    • Estado temporário: ele mantém um mapa em memória pequeno, com timeout curto, do endereço do cliente para o destino do transceiver, para estado de fluxo e observabilidade
    • Escalabilidade horizontal: várias instâncias de relay podem rodar em paralelo atrás de um load balancer e, como o estado do relay não é o estado hard do WebRTC, reinícios causam pouca perda de tráfego e a recuperação de fluxos é rápida
  • Medidas de eficiência

    • SO_REUSEPORT é uma opção de socket do Linux que permite que vários workers de relay façam bind na mesma porta UDP na mesma máquina, e o kernel distribui os pacotes recebidos entre eles, evitando o gargalo de um único loop de leitura
    • runtime.LockOSThread fixa cada goroutine que lê UDP a uma thread específica do sistema operacional
    • O uso combinado de SO_REUSEPORT e fixação de thread faz com que os pacotes do mesmo fluxo tendam a permanecer no mesmo core de CPU, melhorando a localidade de cache e reduzindo trocas de contexto
    • Buffers pré-alocados e cópia mínima reduzem o custo de parsing e alocação, ajudando a evitar garbage collection no Go
    • Como essa implementação conseguiu processar o tráfego global de mídia em tempo real da OpenAI com uma footprint de relay relativamente pequena, a empresa optou por manter o design mais simples em vez de seguir o caminho de kernel bypass

Resultados e lições

  • Essa arquitetura permitiu executar mídia WebRTC no Kubernetes sem expor milhares de portas UDP
  • Uma superfície UDP pequena e fixa facilita segurança e load balancing, além de permitir que a infraestrutura escale sem reservar grandes intervalos de portas públicas
  • O design preserva o comportamento padrão do WebRTC no cliente e confirmou que, para a carga de trabalho da OpenAI, uma arquitetura sem SFU era a escolha padrão adequada
  • A maioria das sessões é ponto a ponto e sensível à latência, e os serviços de inferência escalam mais facilmente quando não precisam agir como peers WebRTC
  • Foi mais apropriado concentrar a complexidade em uma camada fina de roteamento do que distribuí-la por todos os serviços de backend ou exigir comportamento customizado dos clientes
  • Ao codificar metadados de roteamento em um campo nativo do protocolo, a OpenAI obteve roteamento determinístico do primeiro pacote, uma footprint UDP pública pequena e flexibilidade para posicionar ingressos próximos dos usuários no mundo todo
  • Escolhas especialmente importantes

    • Preservar a semântica do protocolo no edge: o cliente continua usando WebRTC padrão, mantendo interoperabilidade com navegadores e dispositivos móveis
    • Manter o estado difícil da sessão em um só lugar: o transceiver detém ICE, DTLS, SRTP e o ciclo de vida da sessão, enquanto o relay apenas encaminha pacotes
    • Roteamento com informações já presentes na configuração: o ICE ufrag fornece um gancho de roteamento do primeiro pacote sem dependência de lookup no hot path
    • Priorizar a otimização do caso comum em vez de kernel bypass: uma implementação enxuta em Go, com uso criterioso de SO_REUSEPORT, fixação de thread e parsing com baixa alocação, foi suficiente para a carga de trabalho da OpenAI
    • A IA de voz em tempo real funciona quando a infraestrutura torna a latência imperceptível, e a OpenAI escolheu mudar a forma de implantar o WebRTC sem mudar o comportamento que o cliente espera dele

1 comentários

 
GN⁺ 7 시간 전
Comentários do Hacker News
  • Muito grato à OpenAI por divulgar um caso de uso da biblioteca Pion em que estou trabalhando
    Se você não conhece bem WebRTC, é uma área bem interessante, e também estou trabalhando no livro WebRTC for the Curious, que explica como isso funciona
    https://github.com/pion/webrtc
    https://webrtcforthecurious.com

    • Uso Pion. Mas fico me perguntando se a abordagem da OpenAI era realmente necessária
      Pareceu que aumentaram muito a complexidade para reduzir uma parte que já está entre as mais rápidas na arquitetura de IA por voz. Modelos rápidos e detecção de atividade de voz (VAD) precisa parecem muito mais importantes do que ajustar finamente o tempo de transporte do WebRTC
    • Obrigado por colocar o livro inteiro online
      Há algum tempo tive a ideia de usar um canal de dados WebRTC para enviar dados de um banco de dados para um cliente no navegador via CLI, então li uma parte e entendi que não se encaixaria bem no meu caso de uso. No fim, usei um plano de controle centralizado e WebSocket
      Ainda assim, parece que daria para fazer algo interessante com WebRTC data channel + Apache Arrow ArrayBuffer sem cópia + duckdb WASM, mas ainda não descobri exatamente o quê
    • Fugindo um pouco do assunto, tenho curiosidade sobre o motivo de manter toda a base de código no diretório raiz em vez de em pastas src aninhadas
      Isso torna bem mais difícil chegar ao README
  • Baixa latência é mais uma dor do que uma vantagem de implementação
    Quando tento conversar de forma casual, é natural a pessoa dar pequenas pausas, mas o GPT interpreta isso como “terminei de falar” e já começa a tagarelar na hora
    Conforme envelheço, demoro mais para encontrar a palavra que quero, e esses GPTs de voz super rápidos acabam sendo mais irritantes do que úteis. Tenho que pensar a frase inteira na cabeça antes de falar, o que não é nada natural

    • Aqui estão misturados dois níveis diferentes de latência
      A latência de que o texto fala é a latência de transmissão do próprio fluxo de áudio, enquanto a latência neste caso está mais ligada a quão rápido ele começa a responder dentro desse fluxo de áudio
    • Também passei por isso e é realmente irritante
      Surge uma pressão para continuar falando mesmo quando você ainda não terminou de pensar, então fica bem artificial. Se você está tentando encontrar a palavra certa, precisa ter a chance de encontrá-la
      Acho que a solução não é um protocolo com latência maior, e sim um tratamento mais inteligente das pausas. Se a latência for baixa, o bot pode parar de falar imediatamente quando o usuário interromper
    • Em conversa por voz, eu mando não responder de jeito nenhum até eu usar uma certa palavra-código, ou responder só “entendido”
      Não é perfeito, mas interrompe menos
    • Isso tem mais a ver com detecção de atividade de voz (VAD) do que com a latência mencionada no texto
    • É um problema difícil. Para impedir que o bot fique tagarelando, sem perceber eu acabo colocando expressões de preenchimento
      E também parece que ele usa a maior parte da inteligência para soar plausível, em vez de pensar no problema. Algo como “Sim, claro. Entendo por que você quer isso...”. Imagino que seja por haver limite de tempo e porque processamento de voz é mais caro. Respostas em texto gastam mais tempo na tarefa em si
  • “Mais de 900 milhões de usuários ativos semanais” obviamente parece se referir ao total de usuários do ChatGPT, e imagino que a proporção que usa o recurso de voz seja bem menor
    Números assim influenciam decisões de negócio, como quanto de otimização de hardware e software vale a pena investir no problema

    • Sim. Acho que foi por isso que usaram a palavra “reach”
      Significa o total de usuários que podem ser expostos ao recurso, independentemente de usá-lo de fato
  • É muito bom ver esse compartilhamento, mas vale lembrar que o modelo de áudio em tempo real da OpenAI ainda está, em termos de capacidade, no nível da família 4o
    Mesmo assim, continua sendo muito útil, e é uma pena que não haja concorrentes de verdade nessa área. A experiência de algo parecido com uma conversa real ajudou bastante a expressar ideias e conceitos
    Diferentemente da época do lançamento, agora ele já não é um modelo de ponta, então vale ter isso em mente. Se o Sam vir isso, seria ótimo se lançasse um novo modelo de áudio em tempo real

    • A parte de voz do realtime/voice mode da OpenAI é excelente, mas comparada aos modelos mais novos ela é bem mais limitada e às vezes fica repetindo as mesmas coisas
      O Gemini flash live 3.1 do Google é melhor, especialmente via API. Também permite chamadas de ferramentas, pode ser conectado a outros LLMs mais inteligentes se você montar a solução por conta própria, e dá para ajustar o nível de raciocínio. Mesmo um nível alto de raciocínio continua perto o suficiente de tempo real, e as respostas podem ser reforçadas com base na busca do Google. Se você gosta de voz bidirecional, provavelmente é a melhor opção hoje, e dá para testar no AI Studio
  • Para quem quer entrar nessa área, pipecat é um ótimo repositório open source e uma boa comunidade
    https://github.com/pipecat-ai/pipecat

  • Se um modelo melhor pensa mais antes de responder, tudo bem esperar mais pela resposta
    Só que ele precisa lidar bem com interrupções, não começar a responder só porque houve 1 segundo de silêncio, e ser inteligente para perceber se eu realmente terminei de falar

  • Talvez isso não seja apenas um problema de latência
    Se você mantém o usuário em uma conversa por voz, consegue dados de treinamento que nunca obteria por texto. Então talvez tenha sido por isso que escolheram uma abordagem com transceiver em vez de SFU e ignoraram quase totalmente conversas com várias pessoas

  • Dá para ler isso como sinal de que a OpenAI agora não usa mais LiveKit para WebRTC/áudio?

    • Parece que sim
      Nessa arquitetura, um servidor LiveKit provavelmente não teria o formato ideal. A discussão sobre SFU no texto praticamente diz isso. Ainda assim, há bastante coisa útil nos SDKs de cliente
  • Se o transceiver cair no meio do stream, como uma sessão ativa seria recuperada?
    O sistema restabelece automaticamente o contexto em uma nova sessão WebRTC?

  • Se você quer fazer amigos, acho melhor entrar em algum tipo de clube ou grupo de encontro