- WebRTC ưu tiên độ trễ thấp như trong các cuộc gọi hội nghị, nên khi mạng kém sẽ chủ động loại bỏ nhiều gói âm thanh, nhưng với Voice AI thì việc hỏng prompt giọng nói có thể làm giảm chất lượng phản hồi nghiêm trọng hơn so với phản hồi chậm
- TTS có thể tạo âm thanh nhanh hơn thời gian thực, nên bộ đệm phía client có thể che giấu các sự cố mạng ngắn, nhưng WebRTC lại render theo thời điểm gói đến nơi và có jitter buffer nhỏ, nên phải chờ nhân tạo để gửi gói đúng lúc
- WebRTC phức tạp trong cả thiết lập và vận hành kết nối vì dùng cổng tạm thời, ICE, DTLS, SCTP..., và khi ghép kênh trên một cổng duy nhất thì rất khó định tuyến các gói STUN, SRTP/SRTCP, DTLS, TURN tới đúng kết nối
- Dù OpenAI yêu cầu thiết lập kết nối nhanh, WebRTC vẫn có thể cần tối thiểu 8 RTT nếu tính cả signaling và quy trình media server, và vì được thiết kế để hỗ trợ P2P nên ngay cả khi server có IP cố định vẫn phải đi qua cùng quy trình đó
- Các lựa chọn thay thế được nêu ra là WebSockets và QUIC/WebTransport; QUIC hỗ trợ cổng đơn, thay đổi địa chỉ, cân bằng tải không trạng thái và kết hợp anycast với unicast đơn giản hơn thông qua
CONNECTION_ID, QUIC-LB và preferred_address
Vì sao WebRTC không phù hợp với Voice AI
- WebRTC được thiết kế cho đối thoại qua lại nhanh như gọi hội nghị, nên khi tình trạng mạng xấu sẽ chủ động loại bỏ gói âm thanh để giữ độ trễ thấp
- Trong Voice AI, điều quan trọng hơn là prompt được truyền chính xác, ngay cả khi người dùng phải chờ phản hồi lâu hơn một chút
- Ví dụ, nếu một prompt thoại như “đi bộ đến tiệm rửa xe hay lái xe đến đó” bị hỏng, chất lượng phản hồi về sau cũng có thể giảm theo
- Phần triển khai âm thanh WebRTC trong trình duyệt giả định rất mạnh về độ trễ thời gian thực, và theo trải nghiệm thử nghiệm tại Discord thì không thể truyền lại gói âm thanh WebRTC
- Sau đó có cập nhật rằng một số người trong cộng đồng WebRTC cho rằng có thể bật audio NACK, nhưng Discord không tìm ra cách thao tác SDP đúng, và hạn chế về jitter buffer rất nhỏ của WebRTC vẫn còn nguyên
- Ngay cả khi tác nhân Voice AI một ngày nào đó đạt được độ trễ ở mức hội thoại, việc giảm độ trễ vẫn có đánh đổi, và chưa rõ liệu việc cố tình làm suy giảm prompt giọng nói có đáng giá hay không
Vấn đề bộ đệm giữa TTS và WebRTC
- Chuyển văn bản thành giọng nói (TTS) có thể tạo âm thanh nhanh hơn thời gian thực
- Ví dụ, nếu GPU tạo ra 8 giây âm thanh chỉ trong 2 giây, thì lý tưởng là có thể stream phần âm thanh đó trong 2 giây lúc tạo, còn client phát trong 8 giây và tích lũy bộ đệm cục bộ
- Như vậy, ngay cả khi có gián đoạn mạng ngắn, người dùng có thể sẽ không nhận ra
- WebRTC không phù hợp với cách này
- WebRTC không có kiểu buffer như vậy và render theo thời điểm gói đến, đồng thời xem timestamp không phải là chuẩn phát lại đủ mạnh
- Nếu có cả video thì vấn đề còn phức tạp hơn
- Với các dịch vụ như OpenAI, phải chờ nhân tạo trước khi gửi từng gói âm thanh để chúng đến đúng thời điểm cần phát
- Nếu xảy ra tắc nghẽn mạng, gói âm thanh đó sẽ bị mất và không được truyền lại
- Kết quả là một cấu trúc vừa thêm độ trễ nhân tạo, vừa chủ động bỏ gói để đạt “độ trễ thấp”, giống như phát video YouTube qua chia sẻ màn hình mà không buffer
- WebRTC dùng jitter buffer cho âm thanh, điều chỉnh động từ 20ms đến 200ms; mục đích là giảm jitter mạng, nhưng theo bài viết thì nếu có thể gửi nhanh hơn thời gian thực thì điều đó là không cần thiết
Giới hạn của cổng và nhận diện kết nối
- Máy chủ TCP thường mở một cổng như
443 để nhận kết nối, và kết nối được nhận diện bằng tổ hợp IP và cổng nguồn/đích
- Ví dụ:
123.45.67.89:54321 -> 192.168.1.2:443
- Khi điện thoại chuyển từ WiFi sang mạng di động hoặc NAT thay đổi IP/cổng nguồn, kết nối TCP sẽ bị ngắt và phải tạo kết nối mới
- Bắt tay TCP và TLS cần ít nhất 2~3 RTT, nên trong livestream người dùng có thể cảm nhận được sự gián đoạn mạng
- Để giải quyết việc này, WebRTC giả định cách cấp phát cổng đích tạm thời cho từng kết nối
- Nếu nhận diện phiên chỉ bằng IP/cổng đích, thì dù IP/cổng nguồn thay đổi vẫn có thể xem là cùng một người dùng
- Nhưng khi áp vào kiến trúc của OpenAI, cách này gây ra vấn đề trong vận hành quy mô lớn
- Số cổng mà server có thể dùng là có hạn
- Tường lửa thường chặn cổng tạm thời
- Cũng không phù hợp lắm với môi trường Kubernetes
Vì sao các dịch vụ WebRTC chuyển sang ghép kênh một cổng
- Nhiều dịch vụ không đi đúng nguyên mẫu của đặc tả WebRTC mà ghép nhiều kết nối trên một cổng duy nhất
- Ở Twitch, server WebRTC từng chạy trên
UDP:443
- Ban đầu
443 là cổng HTTPS/QUIC, nhưng làm vậy giúp vượt qua được nhiều tường lửa hơn
- Theo bài viết, mạng nội bộ của Amazon chỉ cho phép khoảng 30 cổng
- Discord dùng các cổng
50000-50032, mỗi lõi CPU một cổng
- Cách này có thể bị chặn bởi nhiều mạng nội bộ hơn
- Vấn đề lớn của việc ghép kênh một cổng là WebRTC là một cấu trúc ghép từ nhiều tiêu chuẩn
- Có 5 giao thức chạy trực tiếp trên UDP; việc phân biệt một gói thuộc giao thức nào không quá khó, nhưng khó là định tuyến mỗi gói đến đúng kết nối nào
-
Khó khăn định tuyến theo từng giao thức
- STUN
- Có thể chọn
ufrag duy nhất và dùng nó để định tuyến
- SRTP/SRTCP
- Trình duyệt chọn giá trị
ssrc ngẫu nhiên, và thông thường có thể định tuyến dựa trên đó
- DTLS
- Đây là tình huống phải kỳ vọng vào sự hỗ trợ rộng rãi của RFC9146
- TURN
- Tác giả nói không có kinh nghiệm triển khai
- OpenAI cho biết chỉ parse STUN, còn sau đó DTLS, RTP, RTCP được xử lý mờ đục dựa trên trạng thái cache
- Điều này được hiểu là một cấu trúc kỳ vọng IP/cổng nguồn của người dùng sẽ không thay đổi
- Trình duyệt cũng có thể ngẫu nhiên tạo cùng một
ssrc
- Nếu xảy ra xung đột và không có ánh xạ IP/cổng nguồn, Discord sẽ thử giải mã gói bằng từng khóa giải mã khả dĩ để tìm khóa đúng và từ đó nhận diện kết nối
Độ trễ khứ hồi trong thiết lập kết nối WebRTC
- OpenAI nêu “thiết lập kết nối nhanh để người dùng có thể nói ngay khi phiên bắt đầu” là một trong các yêu cầu, nhưng bài viết cho rằng thiết lập WebRTC vẫn cần tối thiểu 8 RTT
-
Ví dụ signaling server
- Với signaling server như WHIP, sẽ cần các vòng khứ hồi sau
- 1 RTT cho TCP
- 1 RTT cho TLS 1.3
- 1 RTT cho HTTP
-
Media server
- 1 RTT cho ICE
- 2 RTT cho DTLS 1.2
- 2 RTT cho SCTP
- Một số giao thức có thể tránh được 0.5 RTT nhờ pipelining nên việc tính chính xác khá phức tạp, nhưng nhìn chung vẫn cần nhiều vòng khứ hồi
- Quy trình này tồn tại vì WebRTC phải hỗ trợ P2P, và ngay cả khi server có IP cố định vẫn phải đi qua các bước tương tự
- Khi signaling server và media server chạy trên cùng host hoặc cùng process, các bắt tay trùng lặp và tốn kém này xảy ra hai lần
Cấu trúc khiến WebRTC gần như bị fork
- Theo bài viết, do có quá nhiều giới hạn nên WebRTC trên thực tế gần như thúc đẩy việc fork giao thức
- WebRTC gồm khoảng 45 RFC cùng các bản thảo chuẩn de facto như TWCC và REMB, nên gánh nặng triển khai rất lớn
- Phần triển khai trong trình duyệt do Google kiểm soát và được tối ưu cho Google Meet, điều này bị xem là một mối đe dọa mang tính tồn tại đối với các ứng dụng họp trực tuyến
- Tác giả cũng cho rằng lý do các ứng dụng họp ngoài Google Meet thường khuyến khích cài app native là để tránh dùng WebRTC
- Discord đã fork WebRTC khá mạnh ở client native, không triển khai phần lớn SDP, ICE, STUN, TURN, DTLS, SCTP, SRTP..., nhưng để hỗ trợ web client thì vẫn phải triển khai đầy đủ
- Tác giả cho rằng OpenAI dù có đủ nguồn lực, vẫn nên thay WebRTC bằng một phương án khác có hỗ trợ trình duyệt thay vì fork nó
Thay thế: WebSockets và QUIC
- Một lựa chọn để bắt đầu cho Voice AI thay vì WebRTC là WebSockets
- Có thể tận dụng hạ tầng TCP/HTTP hiện có
- Không cần tự xây load balancer WebRTC tùy biến
- Phù hợp với Kubernetes và có thể mở rộng tốt
- Head-of-line blocking trong ngữ cảnh này có thể không phải nhược điểm mà là trải nghiệm người dùng mong muốn
- Giả định ở đây là truyền đủ theo đúng thứ tự vẫn tốt hơn việc bị mất một phần prompt thoại
- Nếu đến lúc cần bỏ một số gói hoặc đặt ưu tiên, tác giả cho rằng OpenAI nên dùng WebTransport giống MoQ
- Thiết lập kết nối QUIC chỉ cần QUIC+TLS 1 RTT, đơn giản hơn nhiều so với chuỗi bắt tay chồng lớp của WebRTC
Ưu điểm của QUIC Connection ID
- QUIC từ bỏ định tuyến dựa trên IP/cổng nguồn và đưa
CONNECTION_ID vào mọi gói
CONNECTION_ID có thể dài từ 0 đến 20 byte
- Điểm quan trọng là bên nhận sẽ chọn giá trị này
- Server QUIC có thể tạo
CONNECTION_ID duy nhất cho từng kết nối
- Nhờ đó có thể dùng một cổng duy nhất mà vẫn nhận diện được kết nối ngay cả khi IP/cổng nguồn thay đổi
- Khi địa chỉ nguồn thay đổi, QUIC tự động chuyển sang địa chỉ mới thay vì ngắt kết nối như TCP
- Bài viết cho rằng ý tưởng trong RFC9146 được lấy từ QUIC
Cân bằng tải không trạng thái
- Load balancer của OpenAI phụ thuộc vào trạng thái chia sẻ, giống nhiều load balancer khác
- Phải lưu ánh xạ từ IP/cổng nguồn tới backend server
- Vì load balancer có thể khởi động lại hoặc crash, nên cần kho lưu trữ cho ánh xạ này
- OpenAI dùng một instance Redis để lưu ánh xạ giữa IP/cổng nguồn và backend server
- Bài viết đánh giá đây là cách đơn giản và dễ làm
- QUIC-LB cung cấp cách đơn giản hơn mà không cần cơ sở dữ liệu
- Khi client bắt đầu kết nối QUIC, load balancer chuyển tiếp gói đến backend server thích hợp
- Backend server hoàn tất bắt tay và mã hóa ID của chính nó vào
CONNECTION_ID
- Từ đó, mọi gói QUIC tiếp theo đều mang ID backend server
- Load balancer không cần khóa mã hóa hay bảng định tuyến; chỉ cần giải mã vài byte đầu để chuyển tiếp tới server tương ứng
- Cách này vẫn hoạt động ngay cả khi server khởi động lại
- Không trạng thái cũng có nghĩa là không có trạng thái toàn cục
- Load balancer có thể nhận trên địa chỉ anycast toàn cục và chuyển tiếp toàn cục tới backend server được chỉ ra
- Theo bài viết, Cloudflare dùng cách này rất rộng rãi
- AWS NLB cung cấp cân bằng tải QUIC dùng QUIC-LB
Kết hợp Anycast và Unicast
- Theo cách triển khai của OpenAI, kết nối dường như được gán vào load balancer theo khu vực; cách này vẫn hoạt động về mặt chức năng, nhưng anycast được nêu là phương án tốt hơn
preferred_address của QUIC được đánh giá là tính năng quan trọng cho cân bằng tải
-
Cách hoạt động
- Nhiều backend server trên toàn cầu cùng quảng bá một địa chỉ anycast
1.2.3.4
- Khi client thử kết nối tới
1.2.3.4, router Internet sẽ chuyển gói tới một trong các server đó
- Mỗi QUIC server cũng có thể có địa chỉ unicast riêng như
5.6.7.8
- Anycast dùng cho bắt tay, còn kết nối có trạng thái sẽ được duy trì bằng unicast
-
Ví dụ luồng xử lý
- Server nhận gói QUIC trên cả
1.2.3.4 và 5.6.7.8
- Client gửi gói bắt tay QUIC tới
1.2.3.4
- Server tạo kết nối QUIC và thông báo
preferred_address=5.6.7.8
- Từ đó client gửi các gói tiếp theo tới
5.6.7.8
- Nếu server quá tải và không muốn nhận kết nối mới, chỉ cần ngừng quảng bá
1.2.3.4
- Các kết nối hiện có vẫn không bị ngắt vì đang ở địa chỉ unicast
- Theo đó, địa chỉ anycast gần như hoạt động như một health check
- Với cấu trúc này, tác giả cho rằng không cần load balancer riêng
Giới hạn và kết luận
- Tác giả thừa nhận các kỹ sư của OpenAI rất giỏi và đang chịu áp lực phải mở rộng quy mô lớn ngay lập tức
- Tuy vậy, với Voice AI, WebRTC dù có vẻ là lựa chọn hiển nhiên nhưng lại không phù hợp với sản phẩm và cũng khó mở rộng
- MoQ cũng không hoàn toàn phù hợp với Voice AI
- Trong âm thanh 1:1, nhiều phần về cache và fan-out semantics là không hữu ích
- Dù vậy, kết luận vẫn là nên dùng QUIC
1 bình luận
Ý kiến trên Hacker News
Tôi chưa đọc hết bài, nhưng tôi cho rằng tác giả về cơ bản hiểu mục đích của WebRTC. Việc tự nhận là chuyên gia và đã xây SFU bằng Go/Rust ở nhiều công ty có thể là thật, nhưng lý lịch kỹ thuật không tự động đảm bảo kết luận là đúng
Có thể tôi hiểu sai, nhưng có vẻ họ đang xem STUN và DTLS như những yếu tố tích lũy có liên quan với nhau trong vấn đề thời gian khứ hồi, trong khi thực tế chúng khá trực giao. Ngoài ra, tác giả dành quá nhiều thời gian để nói về việc không có cơ chế truyền lại gói, rồi lặp đi lặp lại kiểu như Discord đã phải nỗ lực ghê gớm, nên tôi thấy lập luận bị lạc hướng
RTC trong WebRTC là giao tiếp thời gian thực, và con người đôi khi ghét âm thanh bị trễ hoặc tốc độ phát lúc nhanh lúc chậm hơn cả việc mất một vài gói. Ở đây là đang nói đến giọng nói của con người
Nếu không muốn chấp nhận mất gói, hãy dùng giao thức dựa trên TCP thay vì UDP. Nhưng nếu gửi giọng nói qua TCP trên mạng xấu, phía nhận sẽ bị khựng vì phải chờ gói đúng tiếp theo. Khi các gói quay lại sau vài giây trễ, bạn phải quyết định phát phần âm thanh bị dồn lại ở tốc độ bình thường hay phát nhanh để bắt kịp kênh khác, mà đa số người dùng không thích trải nghiệm đó
Tạm quên WebRTC đi và chỉ nghĩ về TCP với UDP trong thoại, có lý do khiến VoIP dựa trên UDP từ tận thập niên 90
Trước hết, để trả lời phần kỹ thuật, tôi nghĩ có một tương lai không phải WebRTC. Chỉ là tôi không biết liệu hướng đó có trùng với hướng của WebTransport+WebCodecs hay không
Ý rằng người dùng sẵn sàng đợi thêm 200ms để prompt chậm/đắt trở nên chính xác hơn thì hoàn toàn trái với phản hồi tôi nhận được. Người dùng muốn phản hồi tức thì. Nếu việc tạo phản hồi hay xử lý ngắt lời có độ trễ, cảm giác kỳ diệu sẽ biến mất. Họ cũng không muốn gửi nhanh hơn thời gian thực. Nếu người dùng ngắt mô hình giữa chừng, thì việc gửi một đoạn audio dài 3 phút nhưng chỉ phát 10 giây là lãng phí băng thông
Về nhận định TTS nhanh hơn thời gian thực, voice AI hiện đại / định hướng tương lai đang rời khỏi cách mà tác giả mô tả: https://research.nvidia.com/labs/adlr/personaplex/ theo hướng nhập/xuất từng chút một theo đơn vị 20ms
Phần mong muốn IP/port gốc của người dùng không đổi thì có hỗ trợ rồi. Nếu có IP mới đi vào ufrag thì có thể xử lý được
Nhận định cần tối thiểu 8 RTT cũng sai: https://datatracker.ietf.org/doc/draft-hancke-webrtc-sped/
Việc chọn stream audio bằng WebSocket sẽ làm mất các tính năng như AEC, và đẩy độ phức tạp sang phía client. Sự đơn giản của WebRTC, tức luồng createOffer -> setRemoteDescription, giúp mọi người bắt đầu dễ hơn. Với Realtime API + WebSocket, rất nhiều lập trình viên đã khổ sở vì mã nhiều hơn và có quá nhiều phần phải tự xử lý
Nếu là tôi chọn, tôi vẫn giữ mô hình Offer/Answer nhưng dùng QUIC thay vì DTLS+SCTP. Có lẽ thậm chí có thể chạy RTP trên QUIC. Tôi không có sở thích mạnh với giao thức nào, nhưng tôi không rõ làm sao triển khai code với footprint lớn hơn nhiều cho đủ loại client và cả client của khách hàng
Tôi không phải chuyên gia TTS, nhưng tôi không hiểu lợi ích của việc nhỏ giọt kết quả ra từng chút. Silicon đâu có quan tâm con số thời gian tăng nhanh đến mức nào
Có những trường hợp client biết IP của mình thay đổi và có thể tái thương lượng ICE, nhưng nhiều khi không biết, và thường người ta kỳ vọng server phát hiện thay đổi đó. Nhưng với cấu hình load balancer hiện tại thì điều đó là bất khả thi. Không phải vấn đề quá lớn, nhưng vẫn đáng tiếc khi đã có quá nhiều thủ tục phải vượt qua
Nếu bản nháp đó có nghĩa là 7 RTT chứ không phải 8 RTT, thì một phần có thể pipeline nên con số thực có thể thấp hơn nữa. Nhưng vấn đề thật sự là chỉ vì có khả năng dùng P2P mà lại phát sinh signaling server bắt buộc và bắt tay TLS kép
Lý do WebRTC dễ với dev mới là vì nó là một ứng dụng họp dạng hộp đen. Nhưng ở công ty lớn như OpenAI, cái hộp đen đó bắt đầu tạo ra những vấn đề mà lẽ ra có thể sửa bằng các primitive mức thấp hơn
RTP over QUIC rất đáng để thử nghiệm, và tôi sẵn sàng hỗ trợ. Nếu lo về kích thước code thì browser, và sau này có thể là cả OS, sẽ cung cấp thư viện QUIC. Nếu chuyển theo hướng gần với MoQ hơn, QUIC sẽ xử lý fragmentation, retransmission, congestion control v.v., nên ứng dụng sẽ nhỏ đến mức đáng ngạc nhiên
Hạn chế lớn của RoQ/MoQ là QUIC thực hiện congestion control kể cả với datagram, nên không thể triển khai GCC. Khi gửi từ browser thì trước mắt sẽ bị trói vào cubic/BBR
Công việc hiện tại của tôi là họp thoại/video và cuộc gọi 1:1, và độ phức tạp của WebRTC là khủng khiếp. Nó giúp khởi động sản phẩm nhanh, nhưng rất khó sửa khi làm những thứ kỳ quặc, kể cả khi đã fork cho phía client
Tôi có thể viết cả một bài ca thán dài về TURN. Thật ra cả bó giao thức WebRTC có cảm giác như được thiết kế cho một Internet không tồn tại
TURN lẽ ra nên cấp một rendezvous id thay vì một cổng tạm khi client yêu cầu allocation. Khi đó peer sẽ kết nối tới TURN server qua service port và yêu cầu kết nối cho rendezvous id đó, còn client không cần biết địa chỉ peer để thêm permission. Như vậy sẽ giảm bớt lượng giao tiếp cần relay end-to-end. Với cluster cao cấp hơn, có thể mã hóa thông tin vào id để client và peer mỗi bên kết nối tới TURN server gần mình, rồi các server kết nối với nhau. Với cluster đơn giản hơn thì phải chia sẻ kèm IP TURN server và service port cùng với id
Đoạn hỏi tại sao lại cần biết timestamp hiển thị thực tế và nó tương ứng với thời gian thực ra sao thực sự chạm đúng nỗi đau. Có vẻ như không ai trong số những người làm ra WebRTC từng phải đồng bộ các luồng dữ liệu từ nhiều nguồn khác nhau với độ chính xác mức mili giây
Tôi từng làm demo chống rung video trong browser bằng webcam và module IMU, và độ trễ của đường video->rtc->browser với sensor->websocket->browser khác nhau rất nhiều và cũng không ổn định. Cách giải quyết hiển nhiên là gửi timestamp UTC cho dữ liệu cảm biến rồi đồng bộ trong browser, nhưng video lại không có mốc timestamp UTC nên không thể làm được
Nếu kiểm soát cả hai đầu ống WebRTC thì có thể làm vài trò thú vị như gửi timestamp UTC của thời điểm bắt đầu stream, nhưng vẫn không giải quyết được jitter trong browser. Với proof of concept thì đủ chạy, nhưng giải pháp tổng thể phải thiết kế lại
Tôi có nhiều kinh nghiệm trong lĩnh vực này và cũng có vài bằng sáng chế đang nộp. Ở Alexa, thiết bị tạo kết nối tới server rồi giữ mở, sau đó khi phát hiện wake word thì gửi qua kết nối đó thứ gì đó về cơ bản giống HTTP2/SPDY. Nhờ vậy có thể bắt đầu xử lý STT trước khi người dùng nói xong, và chỉ còn độ trễ xử lý của vài mảnh cuối
Phản hồi cũng quay về trên chính kết nối đó
Với OpenAI thì khó giữ kết nối luôn mở như Alexa, nhưng nếu dùng HTTP2 trên điện thoại thì iOS và Android gần như tự quản lý kết nối đó giúp bạn
Tác giả nói đúng. Không nhất thiết phải có giao thức thời gian thực, và việc nhận đủ toàn bộ dữ liệu mới là điều quan trọng hơn. Người dùng gần như không nhận ra độ trễ cho đến khi nó vượt 500ms. Đặc biệt trong thời đại di động, đa số người dùng đã quen với việc ngay cả giao tiếp thời gian thực giữa người với người cũng có độ trễ
Nếu bạn làm ở OpenAI hay Anthropic thì cứ liên hệ. Tôi có thể trao đổi chi tiết hơn
Độ trễ truyền tải chỉ là phần cộng thêm lên trên hàng loạt độ trễ lớn khác vốn đã tồn tại
Vì vậy tôi nghĩ họ chọn giải pháp độ trễ thấp nhất có thể để giảm độ trễ đầu-cuối của toàn bộ pipeline
Phép so sánh với độ trễ thoại giữa người với người không đúng lắm, vì trong trường hợp đó bạn đang mặc định con người là thực thể không có độ trễ
500ms gần như là mức sàn trong triển khai thoại tiên tiến hiện nay nếu bạn gặp may, không tiếc tiền, và còn dùng cả các kỹ thuật đắt đỏ như speculative decoding hay reasoning. Riêng bước LLM đã mất 450ms. Trong voice AI thương mại, từng ms đều quan trọng, và chỉ cần cộng thêm 200~300ms là chất lượng hội thoại giảm rõ rệt
Doanh nghiệp của chúng tôi chủ yếu làm thoại cho người dùng không rành kỹ thuật. Năm ngoái khi độ trễ giữa các lượt thoại là 1200~1500ms, người dùng thường bị rối, ngắt lời nhiều, thoát khỏi cuộc trò chuyện, và nhìn chung là trải nghiệm khó chịu. Hiện tại tùy công cụ cần dùng mà chúng tôi ở mức khoảng 700ms, và đã tiến gần đến một trải nghiệm khá ổn, có thể so được với tương tác với người thật. Từ đó trở đi, để giảm thêm 100ms chúng tôi sẵn sàng tốn khá nhiều chi phí
Chúng tôi còn làm những thứ đắt đỏ và lãng phí như speculative LLM pass, speculative tool execution. Ví dụ chạy nhiều suy luận LLM trong lúc người dùng đang nói để tiết kiệm 100~200ms, nhưng chưa thật sự thực thi các tool call không idempotent cho đến khi biết pass đó dùng được và người dùng không nói thêm điều quan trọng ở cuối câu. Nói rằng 500ms là không đáng kể có lẽ đang nhắm tới tình huống khác, chứ không phải tương tác thoại người-AI
Vấn đề khó thật sự trong voice AI không phải là mấy gói WebRTC thỉnh thoảng bị rơi, mà là tiếng ồn nền mạnh, echo, và giọng địa phương. Triển khai AEC đã được mài giũa của WebRTC ít nhất cũng giúp khá nhiều cho bài toán echo. Tôi hiểu ở quy mô như OpenAI đây là một giao thức rất đau đầu để triển khai, nhưng với các ứng dụng chưa ở mức siêu lớn thì có khá nhiều lời giải thương mại ổn như Daily. Những vấn đề thật sự cần giải nằm ở chỗ khác. Dù vậy, nếu cộng thêm 500ms vào ngân sách độ trễ của tôi thì ứng dụng coi như chết
Đáng tiếc là hiếm có giao thức nào mà tôi lại không muốn triển khai như WebRTC. Chỉ để dựng lên một client đơn giản cũng phải nhanh chóng làm quen với SDP, TURN/STUN, ICE candidates, offer, giao thức P2P, cùng một quy trình bắt tay phức tạp mà lần nào cũng như phải làm lại từ đầu
Tôi thậm chí không dám tưởng tượng việc phải viết lại toàn bộ chiếc áo khoác trench coat đó, với từng lớp giao thức và “best practice” phát sinh ngoài ý muốn chồng chất lên nhau
Hy vọng mọi thứ đang dần tốt hơn khi tài liệu và thư viện ngày càng nhiều. Cũng khá bất ngờ khi các công cụ như Codex giờ đã đẩy qua được những phần kiểu này khá tốt
Toàn bộ vấn đề ổn định của browser API có rất nhiều góc cạnh không được tài liệu hóa, và đó không chỉ là vấn đề riêng của WebRTC
Đây là một bài viết thiên lệch đến mức gây bực bội. Đúng là WebRTC có giới hạn, nhưng dựa vào tiêu chuẩn giúp đạt được nhiều độ chính xác hơn và giảm chi phí kỹ thuật dài hạn. Việc WebRTC phức tạp không có nghĩa nó sai, mà có nghĩa là media thời gian thực trên Internet công cộng vốn dĩ phức tạp
Networking về bản chất là có trạng thái. NAT traversal, jitter buffer, congestion control, packet loss, trạng thái codec, mã hóa, session routing sẽ không tự biến mất chỉ vì bạn chuyển audio sang TCP hay WebSocket. Giả vờ như vậy không phải là sự rõ ràng về kiến trúc, mà chỉ là chuyển độ phức tạp sang nơi ít nhìn thấy hơn
Một năm trước ở Discord, anh ấy lại viết lại WebRTC SFU bằng Rust, và bạn có thể thấy mô típ lặp lại
WebRTC là sự kết hợp của khoảng 45 RFC kéo dài từ đầu những năm 2000, cùng với các tiêu chuẩn trên thực tế dù về mặt kỹ thuật vẫn chỉ là bản nháp như TWCC hay REMB. Khi phải tự triển khai tất cả những thứ này thì chẳng vui vẻ gì cả
Có thể xem anh ấy là chuyên gia WebRTC được chứng nhận, và vì thế anh ấy nói mình không bao giờ muốn dùng WebRTC nữa
Anh ấy đã thử đủ nhiều theo cách thông thường rồi, nên tôi nghĩ anh ấy hoàn toàn có tư cách đưa ra góc nhìn trái chiều
Tôi không có lập trường rõ ràng về chủ đề, nhưng bài viết có chất người rất rõ nên tôi thích
Nếu đây là bài do AI viết thì đúng là đáng lo thật
Dù đã là 2026, hội họp từ xa vẫn cứ tệ. Có hàng tỷ đô la trong cuộc chơi, Zoom làm tốt lắm cũng chỉ ở mức bình thường, và đôi khi còn tệ như cái thứ nào đó của Microsoft. Tôi chưa từng thấy họp từ xa nào mà không vụng về và lộn xộn
Bài viết tuyệt vời. Giá mà có thể trao giải cho các bài blog khi tác giả là chuyên gia trong lĩnh vực đó
Về câu “WebRTC được thiết kế để làm giảm chất lượng và làm rớt prompt của tôi trong điều kiện mạng xấu”, nếu muốn thời gian thực thì đó là cái giá phải chấp nhận. Nếu bạn không cần thời gian thực và hình dung mọi thứ là STT -> Prompt -> TTS, thì ngay từ đầu có lẽ cũng không cần gửi audio qua mạng
Mọi ứng dụng độ trễ thấp đều phải quyết định điểm đánh đổi trải nghiệm người dùng giữa chất lượng và độ trễ. Tắc nghẽn tạo ra hàng đợi, tức độ trễ, và để tránh điều đó thì phải bỏ qua thứ gì đó, tức chất lượng giảm
Cái núm điều chỉnh giữa độ trễ và chất lượng của WebRTC là cố định. Nó rất giỏi trong việc tối thiểu hóa độ trễ, nhưng thiếu linh hoạt. Dù vậy, nhờ hỗ trợ từ browser mà thực tế nó vẫn là một trong số rất ít lựa chọn, nên tôi vẫn định dùng WebRTC
Nhưng giờ đã có WebTransport. Với tư cách giao thức mục đích chung, nó có thể tạo ra hành vi gần giống WebRTC. Ứng dụng có thể tự chọn chờ bao lâu trước khi drop/reset stream, thay vì bị ép nhận quyết định đó
Ý chính của bài là người dùng thường muốn streaming nhưng không muốn bị drop. Streaming đầu vào/đầu ra audio dĩ nhiên có thể làm mà không cần WebRTC. Ứng dụng phải có quyền quyết định khi nào một gói audio được xem là mất vĩnh viễn: 50ms, 500ms hay 5000ms. Lập luận ở đây là voice AI không nên chọn tùy chọn 50ms
Khi OpenAI phản hồi, họ đã có sẵn phần lớn audio trước thời điểm người dùng cần nghe nó. Họ tạo audio nhanh hơn thời gian thực, nên giao thức thời gian thực là một lựa chọn không phù hợp
Tôi đang chạy Gemini Live API trên một managed WebRTC cloud mesh và nó hoạt động rất tốt, đã vận hành được 2 năm. Có thể thử WebSocket và tự xử lý những thứ như khóa tạm thời, nhưng nếu nói chuyện với những người đang vận hành voice agent quy mô lớn trong mảng này, thì phần lớn đã được giải quyết nhờ WebRTC, Pipecat, và lượng tài nguyên lớn đổ vào các vấn đề vốn đã được xử lý
Rõ ràng nó có cảm giác overkill, và thực tế có thể đúng là như vậy, nhưng một khi kết nối đã được thiết lập thì nó khá kỳ diệu. Thời gian khởi động và buffering cũng đã được giải quyết cho các kết nối thoại nhanh hơn: https://github.com/pipecat-ai/pipecat-examples/tree/main/ins... video còn khó hơn nữa