語音 AI 只有在對話速度與語速同步時,才能感覺自然。當網路造成阻礙時,人們會立即察覺到尷尬的停頓、斷續的打斷或延遲的插話。這對於 ChatGPT 語音功能、使用 Realtime API 開發的開發者、在互動式工作流程中運作的代理程式,以及需要在用戶說話時即時處理音訊的模型來說都至關重要。在 OpenAI 的規模下,這轉化為三個具體要求:服務超過 9 億週活躍用戶的全球覆蓋能力;快速的連線建立,讓用戶在會話開始後能立即開始說話;低且穩定的媒體往返時間,並具備低抖動和低封包遺失率,使輪流對話感覺流暢清晰。OpenAI 負責即時 AI 互動的團隊最近重新架構了我們的 WebRTC 技術堆疊,以解決三個在規模擴展時開始衝突的限制:每會話一埠的媒體終止模式不適用於 OpenAI 的基礎設施、有狀態的 ICE (互動式連線建立) 和 DTLS (資料包傳輸層安全) 會話需要穩定的所有權,以及全球路由必須保持首跳延遲低。在這篇文章中,我們將深入探討我們所建立的分離式「中繼器加收發器」架構,該架構在改變 OpenAI 內部封包路由方式的同時,仍為客戶端保留了標準的 WebRTC 行為。WebRTC 是一個開放標準,用於在瀏覽器、行動應用程式和伺服器之間傳輸低延遲的音訊、視訊和資料。它通常與點對點通話相關聯,但它也是客戶端到伺服器即時系統的實用基礎,因為它標準化了互動式媒體的困難部分:用於連線建立和 NAT (網路位址轉譯) 穿越的 ICE、用於加密傳輸的 DTLS 和 SRTP (安全即時傳輸協定)、用於壓縮和解碼音訊的編解碼器協商、用於品質控制的 RTCP (即時傳輸控制協定),以及回音消除和抖動緩衝等客戶端功能。這種標準化對於 AI 產品至關重要。如果沒有 WebRTC,每個客戶端都需要不同的方法來建立跨 NAT 的連線、加密媒體、協商編解碼器(用於傳輸和解壓縮的編碼器-解碼器),並適應不斷變化的網路條件。有了 WebRTC,我們可以在已於瀏覽器和行動平台實作的協定堆疊上進行開發,將我們的精力集中在連接即時媒體與模型的基礎設施上。我們也建立在 WebRTC 生態系統本身之上,包括成熟的開源實作以及保持瀏覽器、行動應用程式和伺服器互通的標準工作。Justin Uberti(WebRTC 的原始架構師之一)和 Sean DuBois(Pion 的創建者和維護者)的基礎性工作,使像我們這樣的團隊能夠在經過實戰考驗的媒體基礎設施上進行開發,而不是重新發明低層次的傳輸、加密和擁塞控制行為。我們很幸運 Justin 和 Sean 現在都是 OpenAI 的同事,協助指導我們如何將 WebRTC 和即時 AI 更緊密地結合。對於 AI 而言,最重要的特性是音訊以連續串流的形式到達。語音代理程式可以在用戶仍在說話時就開始進行語音轉文字、推理、呼叫工具或生成語音,而不是等待完整的上傳。這就是一個感覺像對話的系統與一個感覺像按鈕說話的系統之間的區別。一旦我們選擇了 WebRTC,下一個問題就是在哪裡終止它(例如,在邊緣接受並擁有 WebRTC 連線),以及如何將這些會話連接到推論後端。終止點很重要,因為它決定了我們如何處理即時會話狀態、媒體傳輸、路由、延遲和故障隔離。SFU (選擇性轉發單元) 是一種媒體伺服器,它從每個參與者接收一個 WebRTC 串流,並選擇性地將串流轉發給其他參與者。在此模型中,SFU 為每個參與者終止一個單獨的 WebRTC 連線,而 AI 作為會話中的另一個參與者加入。這非常適合本質上是多方的產品,例如群組通話、線上教室或協作會議。它將音訊編解碼器、RTCP 訊息、資料通道、錄製和每個串流的策略集中在一處。即使在客戶端到 AI 的產品中,SFU 也通常是預設的起點,因為它允許團隊重複使用一個經過驗證的系統來處理信令、媒體路由、錄製、可觀察性以及未來擴展,例如人工接手或添加更多參與者。我們的負載情況有所不同。大多數會話都是 1 對 1 的——一個用戶與一個模型對話,或一個應用程式與一個即時代理程式對話——並且每次輪流對延遲都非常敏感。對於這種流量模式,我們選擇了「收發器」模型:一個 WebRTC 邊緣服務終止客戶端連線,然後將媒體和事件轉換為更簡單的內部協定,用於模型推論、語音轉文字、語音生成、工具使用和編排。在此設計中,收發器是唯一擁有 WebRTC 會話狀態的服務,包括 ICE 連線檢查、DTLS 握手、SRTP 加密金鑰和會話生命週期。「終止」在這裡意味著收發器是完成這些握手並加密或解密媒體的端點。將狀態集中在一處使會話所有權更容易理解,並且讓後端服務能夠像普通服務一樣擴展,而不是充當 WebRTC 對等節點本身。選擇收發器模型後,我們的第一個實作是一個基於 Pion 構建的單一 Go 服務,它同時處理信令與媒體終止。它為 ChatGPT 語音、Realtime API 的 WebRTC 端點以及多個研究專案提供支援。在操作上,收發器服務執行兩項工作:信令:SDP 協商、編解碼器選擇、ICE 憑證和會話設定;媒體:終止下游 WebRTC 連線並維護與後端服務的上游連線,用於推論和編排。我們希望該服務能像我們基礎設施的其他部分一樣運行:在 Kubernetes 上,工作負載可以根據需求增減並在主機之間移動。但傳統的每會話一埠 WebRTC 模型與這種環境格格不入,因為它依賴於難以公開、保護和在 Pod 新增、移除或重新排程時保留的大量公共 UDP 埠範圍。第一個問題是每會話一埠模型本身。在高併發情況下,這意味著需要公開和管理非常大的 UDP 埠範圍。雲端負載平衡器和 Kubernetes 服務並非為每個服務數萬個公共 UDP 埠而設計。每個額外的範圍都會增加負載平衡器配置、健康檢查、防火牆策略和發布安全方面的操作複雜性。大量 UDP 埠範圍難以保護,因為它們擴大了外部可達的攻擊面,並使網路策略更難以審核。它們也不適合自動擴展。Pod 在 Kubernetes 中不斷地被新增、移除和重新排程。要求每個 Pod 保留和宣告一個大的穩定埠範圍會使這種彈性變得脆弱。這就是為什麼許多 WebRTC 系統轉向每個伺服器一個 UDP 埠,並在該埠後進行應用層解多工的原因。每個伺服器單埠的設計解決了埠數量問題,但它們引入了第二個問題:如何在整個機群中保留每個會話的所有權。ICE 和 DTLS 是有狀態協定。建立會話的程序需要持續接收該會話的封包,以便驗證連線檢查、完成 DTLS 握手、解密 SRTP,並處理後續的會話變更,例如 ICE 重新啟動。如果相同會話的封包落在不同的程序上,設定可能會失敗或媒體可能會中斷。這給了我們一個明確的目標:向公共網路公開一個小而固定的 UDP 介面,同時仍將每個封包路由到擁有相應 WebRTC 會話的收發器。我們評估了幾種實現方式,包括 TURN (透過 NAT 轉發),其中邊緣中繼器終止客戶端分配並代表其轉發流量。方法優點缺點每個會話獨特的 IP:埠(也稱為原生直接 UDP)直接的客戶端到伺服器媒體路徑資料路徑中沒有轉發層需要每個會話一個公共 UDP 埠大量埠範圍難以公開和保護不適用於 Kubernetes 和雲端負載平衡器每個伺服器獨特的 IP:埠比每會話公開的 UDP 佔用空間小得多每個伺服器一個共享 Socket 可以解多工許多會話在單一主機上運作良好,但無法單獨跨共享負載平衡機群運作單一主機上的會話解多工僅在封包到達該主機後才有用;在負載平衡機群中,第一個封包仍可能落在錯誤的實例上,因此您仍然需要一種確定性方式將每個會話引導到擁有它的程序TURN 中繼器(協定終止)客戶端只需到達 TURN 中繼器位址和埠可以在邊緣集中化策略TURN 分配會增加設定往返時間在 TURN 伺服器之間移動或恢復分配仍然很困難無狀態轉發器 + 有狀態終止器(OpenAI 的中繼器 + 收發器)公共 UDP 佔用空間小收發器仍擁有完整的 WebRTC 會話在媒體到達擁有它的收發器之前增加一個轉發跳數需要中繼器和收發器之間的客製化協調我們推出的架構將封包路由與協定終止分離。信令仍到達收發器進行會話設定,而媒體則首先透過中繼器進入。中繼器是一個輕量級的 UDP 轉發層,具有小的公共佔用空間,而收發器是其後面的有狀態 WebRTC 端點。中繼器不解密媒體、不運行 ICE 狀態機,也不參與編解碼器協商。它讀取足夠的封包元數據以選擇目的地,然後將封包轉發給擁有該會話的收發器。收發器仍然看到正常的 WebRTC 流程,並且仍然擁有所有協定狀態。從客戶端的角度來看,WebRTC 會話沒有任何變化。首個封包路由是此設定中的關鍵步驟。中繼器必須在封包路徑本身上不存在任何會話之前,就路由來自客戶端的第一個封包,而不是透過暫停在外部查詢服務上。每個 WebRTC 會話都帶有一個協定原生的路由掛鉤:ICE 使用者名稱片段,即 _ufrag_,它是一個在會話設定期間交換並在 STUN 連線檢查中回顯的短識別碼。我們生成伺服器端的 ufrag,使其包含足夠的路由元數據,供中繼器推斷目標叢集和擁有該會話的收發器。在信令期間,收發器分配會話狀態並在 SDP 回應中返回一個共享的中繼器 VIP 和 UDP 埠。VIP 是一個虛擬 IP 位址,位於中繼器機群的前端;結合埠,它為客戶端提供一個單一穩定的目的地,例如 `203.0.113.10:3478`,即使其背後有許多中繼器實例。客戶端的第一個媒體路徑封包通常是 STUN (NAT 會話穿越工具) 綁定請求,ICE 用它來驗證封包是否能到達宣告的位址。中繼器僅解析第一個 STUN 封包的足夠部分,以讀取伺服器 ufrag,解碼路由提示,並將封包轉發給擁有該會話的收發器。每個收發器都監聽一個共享的 UDP Socket,這意味著一個綁定到內部 IP:埠的作業系統端點,而不是每個會話一個 Socket。在中繼器從客戶端的來源 IP:埠到該收發器目的地建立會話後,後續的 DTLS、RTP 和 RTCP 封包將在會話內流動,而無需重新解碼 ufrag。中繼器的會話刻意保持最小化,僅包含一個記憶體中的會話,用於