LiveKit 实时语音 Agent 端到端实现解剖
从客户端声学、WebRTC SFU、Agent 调度到模型管线与自然打断
这是一份基于公开文档与开源代码的 clean-room 式系统解剖。LiveKit 的 Server、Agents 和客户端 SDK 是开源的,因此本文可以比闭源 Realtime API 更进一步,追踪到具体类、队列、状态机和网络连接;但不会把 LiveKit Cloud 的私有调度、Inference、增强降噪或 Insights 实现猜成开源事实。
文中的结论分为三类:源码事实来自固定 commit;官方事实来自 LiveKit 当前公开文档;设计判断是如果我们自己实现同类系统时的架构取舍。
证据快照:2026-08-23。固定版本为 LiveKit Serverv1.13.5、LiveKit Agents Python1.7.0、LiveKit Client SDK JS2.22.0,实施时仍应重新核对最新接口。
零、名词解释:阅读报告前先建立共同语言
LiveKit 对象与运行时
| 名词 | 全称 / 中文 | 通俗解释 | 本报告中的位置 |
|---|---|---|---|
| RTC | Real-Time Communication,实时通信 | 让音频、视频或数据低延迟双向流动的一类系统 | 整条实时语音链的技术类别 |
| Room | 房间 / 会话容器 | LiveKit 中把参与者、媒体轨道和数据消息组织在一起的逻辑空间,不是一个 UI 页面 | 每次实时对话的媒体容器 |
| Participant | 参与者 | 加入 Room 的用户、设备网关、电话或 Agent 进程 | 用户端与 Agent 都以 Participant 进入 Room |
| Track | 媒体轨道 | 一条持续的音频、视频或数据流;发布者 publish,接收者 subscribe | 用户麦克风上行与 Agent 语音下行 |
| SFU | Selective Forwarding Unit,选择性转发单元 | 不理解语义,只接收参与者的媒体包并转发给订阅者;比把所有音频混在一起更灵活 | LiveKit Server 内的媒体转发核心 |
| AgentServer | Agent 服务进程 | 向 LiveKit 注册可接任务的 Agent 代码与容量 | 管理 Worker 和 Job 的服务端入口 |
| Worker | 工作进程 / 工作单元 | 接受 LiveKit dispatch,启动具体会话 Job | 承载可用 Agent 容量 |
| Job | 单次会话任务 | 为一个 Room / 会话承载隔离的 Agent 运行实例 | 每个活跃会话的执行实例 |
| AgentSession | Agent 会话编排器 | 收音频、判断轮次、调用模型/工具、产生回答并发出状态事件 | AI 行为与会话状态的核心 |
| RoomIO | Room Input / Output,房间输入输出适配器 | 把 Room Track 和 AgentSession 的音频/转写输入输出连接起来 | Agent Participant 与 Room 的媒体接口 |
| 可听历史 | Audible history | 用户实际上已经听到的 Assistant 内容与播放位置,不等于模型已生成的全部内容 | 打断后修正对话上下文的事实基线 |
| Playout Ledger | 播放事实账本 | 记录声音排队、开始、实际播出与清空的位置 | 连接物理播放和 Conversation projection |
WebRTC 与网络安全
| 名词 | 全称 / 中文 | 通俗解释 | 本报告中的位置 |
|---|---|---|---|
| WebRTC | Web Real-Time Communication | 一整套实时媒体协议栈,包含建连、加密、抖动处理、拥塞控制与音频传输;并不只属于浏览器 | Client / Agent 与 LiveKit Server 的媒体传输 |
| Signaling | 信令 | 在真正传媒体前交换“谁加入、有哪些 Track、怎样建连”等控制信息 | SDK 与 LiveKit Server 之间的控制连接 |
| ICE | Interactive Connectivity Establishment,交互式连接建立 | 收集并选择双方可互通的网络候选地址 | WebRTC 建连机制 |
| STUN | Session Traversal Utilities for NAT | 帮客户端发现自己在公网看到的地址 | 跨网络建连辅助服务 |
| TURN | Traversal Using Relays around NAT | 直连失败时中继全部媒体,代价是额外带宽和延迟 | 复杂 NAT / 防火墙下的媒体兜底 |
| DTLS | Datagram Transport Layer Security | 在 UDP 上完成握手与密钥协商 | WebRTC 媒体加密的密钥协商层 |
| SRTP | Secure Real-time Transport Protocol,安全实时传输协议 | 加密音频/视频媒体包 | WebRTC 的安全媒体传输层 |
| RTP / RTCP | Real-time Transport Protocol / Control Protocol | RTP 送实时媒体,RTCP 回报丢包、抖动、时序等质量信息 | SDK 与 SFU 内部的媒体数据面 |
| Data Channel | 数据通道 | 与媒体 Track 并行传递低延迟应用数据 | 传状态、播放回执等事件 |
| RPC | Remote Procedure Call,远程过程调用 | 一个 Participant 调用另一个 Participant 暴露的受控方法并等待结果 | Agent 调用客户端/设备能力 |
| JWT | JSON Web Token | 带签名的短期入房凭据,声明 identity、Room 和权限 | Client 与 Agent 加入 Room 的授权凭证 |
音频与 AI
| 名词 | 全称 / 中文 | 通俗解释 | 本报告中的位置 |
|---|---|---|---|
| PCM | Pulse-Code Modulation,脉冲编码调制 | 未压缩音频样本,简单但占带宽 | 采集、处理和送入 audio source 的基础格式 |
| Opus | 实时音频编码器 | WebRTC 常用的低延迟语音/音乐压缩格式 | LiveKit 音频 Track 的常见编码 |
| AEC | Acoustic Echo Cancellation,声学回声消除 | 从麦克风中消除扬声器正在播放的回声 | 全双工边播边听的关键声学处理 |
| NS | Noise Suppression,噪声抑制 | 降低风扇、环境底噪等非语音噪声 | 客户端声学前处理 |
| AGC | Automatic Gain Control,自动增益控制 | 自动把过小或过大的输入音量拉到合理范围 | 客户端输入电平控制 |
| VAD | Voice Activity Detection,语音活动检测 | 判断当前有没有人在说话,不等于已经理解一句话是否结束 | AgentSession 的轮次与打断输入之一 |
| STT / ASR | Speech-to-Text / Automatic Speech Recognition,语音识别 | 把语音变成文本 | 级联模型路径第一段 |
| LLM | Large Language Model,大语言模型 | 理解文本、推理、决定回答和工具调用 | Agent 的推理模块 |
| TTS | Text-to-Speech,语音合成 | 把回答文本合成音频 | 级联模型路径最后一段 |
| Realtime Model | 原生实时语音模型 | 直接持续接收音频并流式产生音频/事件,内部不一定暴露独立 STT/TTS | 可替代 STT → LLM → TTS 整条级联路径 |
可选平台服务
| 名词 | 全称 / 中文 | 通俗解释 | 本报告中的位置 |
|---|---|---|---|
| Ingress | 外部媒体入流 | 把 RTMP、WHIP 等外部流送进 LiveKit Room | 外部媒体接入的可选服务 |
| Egress | 媒体输出 / 录制 | 从 Room 录制或转推到外部直播平台 | 录制与直播输出的可选服务 |
| SIP | Session Initiation Protocol,会话初始协议 | 连接电话网络与 VoIP 系统 | 电话渠道接入协议 |
| Redis | 内存数据存储 / 消息总线 | LiveKit 多节点时共享 Room 路由和节点状态 | 分布式部署依赖,单节点不需要 |
| OTel | OpenTelemetry | 统一采集 trace、metric 与日志的可观测标准 | 自建观测链的推荐接口 |
如果只读一页
LiveKit 不是另一个 OpenAI Realtime,也不是一个自己生成语音的基础模型。它更准确的定位是:
以 Room / Participant / Track 为统一对象模型,以 WebRTC SFU 为实时媒体底座,以 AgentSession 为会话状态机,以 AgentServer 为弹性执行池,把任意 STT、LLM、TTS 或原生 Realtime Model 接入同一条实时交互链路。
官网的 LiveKit Ecosystem 表格很大,但它是能力目录,不是安装依赖图:Browser、Swift、Android、ESP32 和 C++ SDK 是不同终端的替代入口;Python 与 Node.js Agents SDK 是两种 Agent 实现选择;Starter Apps、UI Components、Ingress、Egress、SIP 与 Cloud 托管能力都可以按场景独立采用。还要进一步区分两种集成深度:需要 Room、多端互通和标准 WebRTC 媒体时采用完整 LiveKit 平台;Cardputer 或桌面 App 只在本机连接一个 Agent 时,可以只复用 LiveKit Agents runtime,通过自定义 AudioInput / AudioOutput 直接进入 AgentSession,不部署 LiveKit Server、Client SDK、Room 或 SFU。
用户、浏览器、手机、SIP 来电和 AI Agent 在 LiveKit 中都被归一为 Room 内的 Participant。用户发布麦克风 Track;Agent 作为服务端 Participant 订阅它,经模型管线产生音频,再发布自己的 Track。LiveKit Server 负责信令、ICE/TURN、SRTP、SFU 转发、房间状态和 Agent dispatch;Agent 进程负责轮次、模型、工具、打断、可听历史和业务逻辑;模型本身仍可来自 OpenAI、Google、Deepgram、ElevenLabs、自托管推理或 LiveKit Cloud Inference。LiveKit overview Agents overview
与“浏览器直接连 OpenAI Realtime”的方案相比,典型 LiveKit 拓扑是:
浏览器 / App / SIP 电话
↕ WebRTC / RTP
LiveKit Room / SFU
↕ WebRTC Track
自有 Agent Job 进程
↕ Provider API / WebSocket
STT → LLM → TTS,或原生 Realtime Model
这张图刻意把模型画成 Agent Job 后方的三条可替换路径:Room/SFU 统一媒体对象,AgentSession 统一会话状态,业务后端继续掌握身份、权限与真实动作。
这多了一次 LiveKit ↔ Agent 的媒体路径,却换来六项平台能力:
- 模型可替换:级联管线、speech-to-speech、half-cascade 可以共用 Room 与 Agent 生命周期。
- 媒体可编排:多参与者、选择性订阅、数据通道、SIP、录制和前端状态都落在统一房间模型。
- 逻辑归自己:工具、身份、权限、业务状态和 Prompt 留在自有 Agent 进程,不需要塞进客户端。
- Agent 可调度:Agent Server 长连注册容量;LiveKit 按负载选择 Worker,再为 Job 启动隔离进程。
- 打断可闭环:生成取消、音频队列清空、实际播放位置、文本截断和远端模型上下文同步在同一框架中协调。
- 可以自托管:媒体 Server、Agents、SDK、SIP Server 均有开源路径;但 Cloud Inference、Insights 和部分增强音频能力不是开源 Server 的同义词。
LiveKit 最值得学习的不是某个 STT 或 TTS 封装,而是它把实时语音拆成了三个深模块:
- Media Fabric:Room、Participant、Track、WebRTC transport、SFU、重连和分布式房间路由。
- Agent Runtime:Worker 注册、容量选择、Job 隔离、RoomIO、AgentSession、AgentActivity、SpeechHandle。
- Conversation Consistency:轮次判断、暂停/恢复、播放游标、迟到音频 fencing、已听历史和模型上下文截断。
如果我们自己实现一套基本相似的系统,应复用成熟 WebRTC/SFU 和模型 Provider,重点自研的不是编解码器,而是 Session Actor、Turn Manager、Playout Ledger、Capability Gateway 与跨层可观测性。
一、证据边界与版本快照
1.1 这次能看到什么
OpenAI Realtime 的分析只能依据公开契约反推可能实现;LiveKit 则可以直接检查三条开源主链:
| 层 | 固定快照 | 本报告检查的核心实现 |
|---|---|---|
| LiveKit Server | v1.13.5 / 788d4c354474383956067e3a22ffba88dd4803e4 | 信令、Room、Participant、WebRTC transport、SFU、Redis router、Agent dispatch |
| LiveKit Agents Python | 1.7.0 / da6af86ac640a3bc54585764e64321d7048c1c16 | AgentServer、Job 进程、AgentSession、AgentActivity、RoomIO、OpenAI Realtime 插件 |
| LiveKit Client SDK JS | 2.22.0 / f3732d14bf3162981e4a57d1daa7305a1f3ab7b5 | getUserMedia、声学约束、PeerConnection 管理、重连、远端播放 |
三者均为 Apache-2.0。源码快照能证明公开仓库在该 commit 的实现,但不能证明 LiveKit Cloud 内部部署参数、私有服务、实时负载、SLA 或线上分支与仓库逐字相同。
1.2 开源与 Cloud 必须分开理解
| 能力 | 开源可自托管 | LiveKit Cloud 托管能力 |
|---|---|---|
| Room / Participant / Track / SFU | 是 | 托管运行 |
| WebRTC 信令、ICE/TURN 接入 | 是,需自己部署公网和 TURN | 托管全球边缘与网络 |
| Agents SDK 与 Agent Server | 是 | 可托管部署,也可自托管 Agent 连 Cloud Room |
| STT / LLM / TTS 插件 | 是,Provider 账密由自己管理 | 也可使用 Cloud Inference 统一入口 |
| SIP Server | 有独立开源部署路径 | Cloud 预置服务与电话能力 |
| Agent Insights 统一音频/转写/Trace/日志时间线 | 不是完全自托管产品 | Cloud 特性;完全自托管应接 OpenTelemetry、日志、对象存储 |
| 增强噪声消除 | 基础客户端 AEC/NS/AGC 与自选 DSP | 部分高级模型由 Cloud 提供 |
官方 Observability 文档明确说明 Cloud Insights 不适用于完全自托管媒体部署;不过 Agents SDK 的 OpenTelemetry spans 可以导出到任意 OTLP 后端。Observability overview Export traces
二、总体架构:五层、四条连接、三个状态权威
四条长期或半长期连接分别承担不同职责:
| 连接 | 端点 | 主要内容 | 谁负责恢复 |
|---|---|---|---|
| 客户端信令 | Client SDK ↔ LiveKit | Join、SDP、ICE、Track/Participant 更新、resume | Client SDK + LiveKit signal bridge |
| 客户端媒体 | Client PeerConnection ↔ LiveKit SFU | SRTP 音频、RTCP、DataChannel | WebRTC ICE/DTLS + SDK 重连策略 |
| Worker 控制 | AgentServer ↔ LiveKit /agent | 注册、容量、availability、assignment、termination | AgentServer 重连循环 |
| 模型连接 | Agent Job ↔ STT/LLM/TTS/Realtime Provider | 音频帧、文本 token、工具事件、生成音频 | 各 Provider plugin + AgentSession |
系统中至少有三个不同的状态权威:
- Room 是媒体成员关系的权威:谁在线、发布了什么 Track、谁订阅谁。
- AgentSession 是当前对话执行的权威:轮次、生成、工具、正在播放的 SpeechHandle。
- 业务后端是现实事实的权威:用户身份、订单、设备动作、支付、长期记忆和审计。
把三者混成一个“会话状态”会制造恢复错误:Room 重连不代表模型连接仍在;模型生成完成不代表声音已播放;工具函数返回不代表业务事务已提交。
三、客户端:声学现实仍然属于设备
3.1 采集链路
Web 客户端调用 getUserMedia 获取麦克风。JS SDK 的默认音频约束开启 autoGainControl、echoCancellation、noiseSuppression 和 voiceIsolation;代码再把约束传给浏览器的 navigator.mediaDevices.getUserMedia。audio defaults media capture
这说明 LiveKit 负责“请求浏览器启用声学处理”,不等于 LiveKit Server 自己替客户端完成 AEC:
扬声器参考信号 ─────┐
↓
麦克风 → AEC → NS → AGC → 浏览器 AudioTrack → Opus / WebRTC
↑
设备声学与 OS 音频栈生产客户端仍需承担:
- 权限与设备切换;
- 采样率、声道和路由变化;
- 蓝牙耳机切换及 SCO/A2DP 差异;
- 外放双讲、远场混响、风噪和键盘声测试;
- iOS 后台、Android audio focus、浏览器 autoplay 限制;
- 本地 mute、push-to-talk、音量表和设备故障提示。
AEC 是自然打断的前提。若助手播放音频重新进入麦克风,远端 VAD 会把助手自己的声音误判成用户插话;更强的服务端降噪无法可靠替代设备侧回声参考。
3.2 WebRTC 连接并非一条简单 Socket
JS SDK 用 PCTransportManager 管理 publisher 与可选 subscriber PeerConnection,并支持 subscriber-primary、publisher-primary、publisher-only 三种模式。双 PC 模式把发布和订阅隔离,单 PC 模式可以双向承载;ICE candidates、track、data channel 和 offer 都通过信令层协调。PCTransportManager
客户端建连的逻辑步骤是:
- 向自有后端登录并获取短期 Room access token;token 绑定 room、participant identity 和发布/订阅权限。
- 与 LiveKit 建立 TLS WebSocket 信令连接,交换 Join、SDP 和 ICE。
- ICE 选择 host / srflx / relay candidate;必要时经 TURN。
- DTLS 建立密钥,媒体走 SRTP,音频 Track 发布到 SFU。
- Agent 加入后,客户端订阅其音频 Track,SDK attach 到
HTMLMediaElement或 Web Audio graph 播放。Connecting to LiveKit RemoteAudioTrack
Room token 不是用户登录 token。推荐由业务后端在完成用户鉴权后签发短 TTL、最小权限的 LiveKit token;API secret 不应进入浏览器。
3.3 重连是“恢复”与“重建”两条路径
JS SDK 区分轻量 resumeConnection 和完整 restartConnection。前者恢复信令并保留/修复现有 PeerConnection;后者关闭旧连接、重新 Join、重建 PC,必要时尝试下一区域。失败的 resume 会升级为 full reconnect。reconnect decision restart and region fallback
产品不能只显示“网络已恢复”。完整恢复应分别确认:
- Room participant identity 是否延续;
- 本地麦克风 Track 是否重新发布;
- Agent 是否仍在同一 Job / Session;
- Provider 实时模型连接是否重放必要上下文;
- 旧音频是否已 flush,当前播放 cursor 是否有效;
- 未完成工具调用是继续、查询结果还是幂等重试。
四、LiveKit Server:信令、Room 与 SFU 如何接住音频
4.1 Room 是媒体 Actor,不是聊天记录
Room 持有 Participant、Track、订阅关系、AgentDispatch 和广播状态。新 Track 发布后,Room 登记 Track、广播 Participant 更新,并按 auto-subscribe 策略让已有 Participant 订阅;首次发布还可以触发 publisher 类型的 Agent job。onTrackPublished
因此 AI Agent 与普通参会者共享相同媒体原语:
- 用户麦克风:一个由用户 Participant 发布的 audio Track;
- Agent 耳朵:Agent Participant 对用户 Track 的 subscription;
- Agent 嘴巴:Agent Participant 发布的另一个 audio Track;
- 转写、状态、RPC:Room data / text / RPC 能力;
- 电话:SIP Participant 进入同一个 Room。
这套对象模型让“网页用户 ↔ Agent”“两名用户 + Agent”“电话用户 ↔ Agent”“机器人摄像头 + Agent”不必各造一套会话协议。
4.2 信令和媒体可以落在不同入口节点
客户端的持久 WebSocket 可能连接节点 A,而 Room 实际托管在节点 B。分布式模式下,入口节点作为 signaling bridge 把信令代理到 Room 节点;当前一个 Room 必须完整落在单个节点上。Distributed multi-region
Server 的 RedisRouter 用 Redis 保存 node 列表与 room → node 映射,并把 participant signal 路由到 Room 所在节点;drain 时把当前节点标记为 SHUTTING_DOWN。RedisRouter
这个约束直接影响容量设计:扩容可以增加并发 Room 数,但不能用加节点线性扩展单个超大 Room。语音 Agent 通常每 Room 参与者少,因此 room affinity 很自然;大型会议或广播场景则需要单房间容量测试。
4.3 SFU 不解码再混音,而是选择性转发 RTP
在媒体路径上,Pion WebRTC OnTrack 接到上行 TrackRemote,LiveKit 为发布 Track 建立 WebRTCReceiver;每个订阅者对应 DownTrack。Receiver 收到扩展 RTP packet 后分发给 DownTrack,DownTrack 做 sequence/timestamp、layer、重传和拥塞相关处理后写向订阅者。WebRTCReceiver DownTrack WriteRTP
对语音 Agent 来说,SFU 的价值不是视频多层,而是:
- 浏览器和 Agent 都使用标准 WebRTC,而非每个模型 Provider 的私有媒体协议;
- 一个用户 Track 可以同时给 Agent、录制器、监督员或第二参与者;
- RTCP、NACK、抖动和拥塞控制留在成熟实时媒体栈;
- Agent 换模型不要求客户端换连接协议。
代价也很明确:Agent 必须先从 SFU 收到媒体,再发给模型;模型音频又先回 Agent,再发布到 SFU。相较客户端直连模型,这增加网络 hop、缓冲点和故障域,因此 Agent 与 Room 节点、模型区域应尽量就近部署。
4.4 自托管部署的实际网络成本
真正的自托管不是只启动一个 Docker 容器。生产环境至少需要:
- 公网可达的 TLS 信令入口;
- UDP 媒体端口与 TCP/TLS fallback;
- TURN 服务应对严格 NAT、防火墙和企业网络;
- Redis 支撑多节点共享状态与消息总线;
- room-aware drain,不能把有活跃 Room 的节点直接杀掉;
- 区域 DNS/LB 与 node selector 协同;
- SFU 的带宽、包率、UDP buffer 和单房间容量监控。
LiveKit 官方的 drain 语义是:活跃 Room 继续运行、允许新 Participant 加入这些活跃 Room,但拒绝在该节点创建新 Room;所有参与者离开后才退出。Distributed multi-region
五、Agent 调度:一个 AI Participant 如何被拉起
5.1 AgentServer 是 Worker,不是每会话业务对象
Agent 应用启动后,AgentServer 与 LiveKit 的 /agent endpoint 建立长期 WebSocket,注册 agent name、job type、permissions、deployment、版本和当前负载。它不是先加入某个 Room 等用户,而是成为一台可接受 Job 的 Worker。Agent server
当 Room 或 Participant 满足 dispatch 条件时,链路如下:
调度链上同时存在两种权威:Room 的媒体节点亲和由 LiveKit Server / Redis 路由维护,Worker 的执行容量由 AgentServer 管理;独立 Job 进程则是一次会话的故障与资源隔离单元。
Server 端不是简单 round-robin。固定源码中,AgentHandler 按 (agentName, namespace, jobType, deployment) 找 Worker,用 max(0, 1-load) 作为剩余容量权重随机选择;若 Worker 不可用则尝试下一个。worker selection weighted load
Worker 收到 availability 后会立即计算有效负载并预留 slot,防止两个并发请求在 Job 尚未进入 active list 时同时超卖;收到 assignment 后才把带有 Room URL 与 token 的 RunningJobInfo 交给进程池。availability and reservation
5.2 为什么默认每 Job 一个进程
在非 Windows 平台,Python Agents 默认 JobExecutorType.PROCESS;生产默认负载阈值为 0.7,还提供 prewarm、idle process、memory warning/limit 和 drain 配置。worker defaults
每 Job 进程带来四个隔离收益:
- 一次会话的死循环、内存泄漏和 Provider SDK 崩溃不直接污染其他 Job;
- 可以在进程级执行资源计量、终止和重启;
- Room token、每会话 userdata 与工具上下文不必共享全局可变状态;
- AgentServer 可以先 prewarm 进程,减少 import、模型加载和连接初始化冷启动。
但它不是完整安全沙箱。恶意工具仍可能访问同一宿主凭据、文件系统和网络;高风险多租户应再叠加容器、VM、seccomp、网络策略和按租户的 capability credential。
5.3 调度层真正需要观测的不是 QPS
对语音 Agent,更关键的 Worker 指标是:
| 指标 | 原因 |
|---|---|
| dispatch wait / assignment latency | 用户进入 Room 后多久真正出现 Agent |
| cold vs warm Job start | 首轮延迟是否被 import、模型连接、VAD 加载拖慢 |
| active jobs / reserved slots / effective load | 发现并发超卖或过度保守 |
| room node ↔ agent node RTT | 直接进入双向媒体延迟预算 |
| agent node ↔ model region RTT | 级联每阶段、Realtime WebSocket 都受影响 |
| abnormal job exit / redispatch count | 会话是否被进程崩溃打断 |
| drain duration | 发布与缩容是否会破坏长通话 |
调度恢复也不能只“重新拉一个进程”。新 Job 必须知道它是新会话、接管还是恢复;业务层需为 tool call、用户身份、会话摘要和未完成事务提供可重放的 checkpoint。
六、Agent Runtime:AgentSession 是怎样的深模块
6.1 核心对象与职责
AgentSession 的构造函数直接接受 STT、VAD、LLM 或 RealtimeModel、TTS、turn handling、tools、MCP servers 和运行选项;源码对它的定义就是把媒体、模型、工具、端点判断和打断粘合成一个实时 Agent runtime。AgentSession
| 对象 | 深职责 | 不应承担 |
|---|---|---|
AgentServer | 注册、容量、dispatch、Job 生命周期 | 单轮对话状态 |
JobContext | Room URL/token、Job 元数据、进程上下文 | 长期业务记忆 |
AgentSession | 会话入口、I/O、事件、模型、工具、观测 | SFU 转发与业务权限真相 |
AgentActivity | 当前 Agent 的轮次、生成、打断和 speech 调度 | 跨会话 worker 调度 |
SpeechHandle | 一段可排队、可打断、可等待的发言生命周期 | 整个 Conversation |
RoomIO | Room Track ↔ Agent 音频/转写/状态适配 | 模型 Provider 协议 |
| Provider plugin | STT/LLM/TTS/Realtime 协议适配与重连 | Room membership 和业务授权 |
这是一组“深模块”:上层业务只需定义 Agent 指令、tools 和可选 node override,不必自己操作每个 WebRTC frame、STT partial、TTS chunk 和打断 race。
6.2 RoomIO 把 Agent 变成真正的 Participant
AgentSession.start() 若未提供自定义 I/O,会创建默认 RoomIO,并同时初始化 session trace、usage、recording 和 primary session 状态。session start
输出侧 _ParticipantAudioOutput 创建一个 rtc.AudioSource,默认队列上限 200 ms,把流重组为 20 ms frame,再发布 LocalAudioTrack 并等待订阅者。RoomIO output
这里有两个非常重要的工程信号:
- 音频生成与实际播放之间一定有队列。模型已经产生的音频不等于用户已经听到。
- frame 粒度决定打断下限。20 ms frame 与 200 ms queue 是该固定版本的实现参数,不是所有部署的永恒契约,但说明框架显式管理 playout backlog。
6.3 一次正常语音轮次
这条时序中存在五个独立的“完成”:用户停说、turn commit、模型 response complete、Agent output queue drained、客户端真实扬声器播放完成。只有最后两个接近“用户听完”,服务端不能用 response complete 替代。
6.4 可覆写的节点接口
级联管线向业务暴露 stt_node、llm_node、transcription_node 和 tts_node;它们都接受/返回 async iterable,因此可以在不改 Session 状态机的前提下插入预处理、路由、缓存、guardrail、文本规范化或自有模型。pipeline nodes
推荐把定制放在语义正确的 seam:
- 音频增益、带通或自有降噪:STT 前的 audio node;
- 多 STT fallback:STT adapter,而非业务 Agent;
- RAG、Prompt、LLM router:
llm_node; - Markdown/emoji/数字读法:TTS 前文本变换;
- 品牌音色与合规播报:独立 TTS 或
tts_node; - 权限、幂等、审计:工具外的 Capability Gateway,不能只写在 Prompt。
七、三种模型管线不是三套产品
LiveKit 把 Room、调度、工具和 turn runtime 与模型管线解耦,因此同一前端和后端可以选择三类语音路径。Pipeline models
| 路径 | 优势 | 代价 | 更适合 |
|---|---|---|---|
| STT → LLM → TTS | 每段可替换、转写及时、审计清楚、工具生态成熟、可精确播报 | 多阶段增加延迟;文本接口损失韵律与情绪 | 客服、交易、受监管流程、多语言和强可观测产品 |
| 原生 Realtime Model | 最低潜在延迟、自然 prosody、双讲与情绪表现更好 | Provider 锁定、远端上下文同步更复杂、转写可能滞后、难保证逐字播报 | 陪伴、教育、自然开放对话、低延迟体验 |
| Half-cascade | 保留原生音频理解,输出音色/合规文本更可控 | 仍要协调实时理解与独立 TTS,接口成熟度依 Provider | 品牌音色、强 TTS 能力、希望折中可控性与自然度 |
LiveKit 文档当前推荐多数生产项目先从级联管线开始,这不是因为 speech-to-speech 不好,而是级联更容易检查每一阶段、替换 Provider、断言工具与转写。Realtime 路径适合体验优先,但仍需补齐 conversation projection 和 interruption consistency。
7.1 级联管线的真正延迟结构
总首音延迟近似为:
T_first_audio ≈
T_capture_and_uplink
+ T_endpointing
+ T_STT_final_or_stable_partial
+ T_LLM_first_usable_text
+ T_TTS_first_chunk
+ T_agent_to_SFU
+ T_client_jitter_and_playout这些项不是全串行:streaming STT、preemptive generation、LLM sentence chunking 与 streaming TTS 可以重叠。但越早预测性生成,越容易在用户补充后作废,因此需要 generation id、cancel 和不提交未播放文本。
作为自建目标而非 LiveKit 官方性能承诺,可先设:
- 客户端 20 ms 帧;
- 用户停说后 200–500 ms 内 commit 普通短句;
- 400–900 ms 首个可听音频为优秀目标区间;
- 100–200 ms 内对明确 barge-in 停止远端播音;
- 所有分位数按 P50/P95/P99、网络类型、设备和语言拆分。
7.2 Realtime 路径仍然经过 Agent
以 OpenAI plugin 为例,RealtimeModel 被传给 AgentSession(llm=model);Agent Job 内的 RealtimeSession 再用 Provider API key 建立到 OpenAI 的 WebSocket。它把 Room audio frame 重采样、按 100 ms chunk 组织成 input_audio_buffer.append,并接收 response.output_audio.delta 等事件。OpenAI Realtime model Provider WebSocket push audio
所以实际拓扑是:
Browser ─WebRTC─> LiveKit SFU ─WebRTC─> Agent Job
│
└─WebSocket─> OpenAI Realtime而不是 Browser 直接把 PeerConnection 建到 OpenAI。这个桥接层的意义是:
- API key 留在服务器;
- Room/SIP/多参与者与模型 Provider 解耦;
- Agent 可插入工具、guardrail、转写投影和 handoff;
- 同一产品可以把 OpenAI 换成别的 realtime model。
代价是媒体不再是客户端到模型的最短路径。若只做单用户浏览器语音、业务控制很薄、且完全绑定一个 Provider,客户端直连模型通常更省 hop;若要多渠道、Provider 可替换、自托管媒体和复杂工具,LiveKit 的中间层更有价值。
八、轮次与自然打断:LiveKit 最值得逆向学习的部分
8.1 轮次结束不是一个 VAD 布尔值
LiveKit 当前支持多种 turn detection 策略:
| 模式 | 结束信号 | 优点 | 风险 |
|---|---|---|---|
| semantic turn detector + VAD | 声学停顿 + 语义完成度 | 避免用户思考停顿被抢话 | 需要模型推断,可能增加等待 |
| realtime model turn detection | Provider server-side VAD / semantic VAD | 与原生模型生成紧密协调 | 控制权和诊断依赖 Provider |
| VAD-only | 静音持续时间 | 快、语言无关 | 犹豫、长停顿时容易过早提交 |
| STT endpointing | STT final / endpoint event | 与转写一致 | 受 STT latency 和语言影响 |
| manual / push-to-talk | 客户端显式 commit | 最确定 | 交互不够自然,用户负担高 |
Turn detection and interruptions
高质量策略应把 speech_started、STT partial/final、语义 EOU、最大等待时间和场景策略组合成状态机。比如客服收集订单号时,可以延长端点;“确认/取消”短指令可更激进;电话噪声环境应提高 min speech duration。
8.2 打断是五阶段提交协议
图中的三个历史不是同一份状态:模型可能已经生成、Agent 可能已经推送,但用户未必真的听见。自然打断只有在生成、播放和 conversation projection 同时收敛后才算完成。
五个阶段分别解决不同 race:
- 检测:用户真的开始说,还是回声、咳嗽、短 backchannel?
- 决策:当前 speech 是否允许打断,是否只先暂停等待更多证据?
- 生成取消:取消当前 Realtime response、LLM/TTS task 和排队 SpeechHandle。
- 物理停播:清空 Agent 输出队列,让还未发往 SFU 的 frame 不再出去。
- 语义提交:只把用户实际听到的部分写回本地与远端 conversation。
AgentActivity.interrupt() 会取消 preemptive generation、后台/current/queued speeches,并通知 realtime session 取消模型生成。interrupt fan-out
8.3 停止生成不等于停止声音
RoomIO 在 interruption 时:
- 增加
_interruption_generation; - 丢弃内部 audio buffer;
- 从已推送时长中减去
AudioSource.queued_duration; - 调用
clear_queue(); - 以修正后的时长上报
playback_position; - forwarding task 对 generation 不匹配或 interrupted 的 frame 直接丢弃。
这里的 _interruption_generation 就是 generation fence:即使取消信号与 frame producer 交错,旧代 frame 也不能重新进入新一轮播放。
不过 Agent 侧的 AudioSource queue drained 仍不是客户端扬声器的绝对物理位置:SFU、网络和客户端 jitter buffer 还可能有少量在途音频。若产品要求逐字精确的“已听历史”,应让客户端回报 playout acknowledgement,或者把服务端 cursor 当可接受近似并测量其误差。
8.4 可听历史必须反写给模型
当 speech 只播放一部分,AgentActivity 以实际 playback_position 计算 audio_end_ms,调用 realtime provider 的 truncate,本地 chat context 也只 upsert forwarded_text;完全未播放的 message 会从远端 mutable chat context 移除。audible transcript commit
OpenAI plugin 的 truncate 会发送 conversation.item.truncate,没有可听音频时可删除 item;interrupt() 则发送 response.cancel。provider cancel and truncate
这建立了三个同步对象:
生成历史:模型曾产生什么
播放历史:Agent 已向 Room 推送到哪里
可听历史:用户实际听到了什么正确 conversation projection 应以可听历史为目标,而不是保存完整生成历史。否则下一轮模型会说“如我刚才解释的后半部分”,但用户从未听到那部分。
8.5 false interruption 不是异常,而是双讲必然现象
固定源码支持先 pause 当前 audio output,在超时或 turn verdict 后判断是否是假打断;若允许恢复且 SpeechHandle 尚未结束,就继续播放原 speech。Adaptive detector 出现不可恢复错误时,会降级为 VAD interruption,并释放被暂存的转写。false interruption resume adaptive fallback
这是比“VAD 一响就 kill”更成熟的策略:
- “嗯”“对”“我在听”可能只是 backchannel,不一定要夺取轮次;
- 咳嗽和瞬时噪声可以先暂停,而不是永久丢掉 response;
- 真正连续讲话或出现有效 final transcript 时再 commit interruption;
- detector 故障不能让 Agent 彻底失去打断能力,应降级到简单策略。
九、业务行动面:工具在 Agent 里执行,但权限不属于 Agent
9.1 模型工具调用只是行动提案
AgentSession 可以直接接收 tools、toolsets 和 MCP servers,LLM 或 Realtime Model 产生 function call 后由 Agent runtime 执行并把结果写回生成链。框架还支持多步工具、handoff 和 node override。AgentSession options Agent nodes
但生产系统应再包一层 Capability Gateway:
模型 function call
→ schema validation
→ 从可信 Session 取 user / tenant / role
→ policy check
→ 高风险动作二次确认
→ idempotency key / expected version
→ 业务 API
→ receipt / error / actual state
→ 结构化 tool result 回模型不要让模型参数中的 user_id、is_admin 或“用户已经同意”成为授权证据。可信身份来自业务登录、Room token binding 和服务端 Session;工具只允许引用当前上下文中可访问的资源。
9.2 工具与语音有不同的取消语义
用户打断 Agent 播报时,至少有三种可能:
- 只停止语音,已经成功的业务动作不能回滚;
- 取消尚未开始的只读查询;
- 对可取消的长任务发送 cancellation,但等待业务系统确认终态。
因此一次 tool run 应拥有独立 operation_id 与状态机:
proposed → authorized → running → succeeded | failed | cancel_requested → cancelledAgent SpeechHandle 的 cancel token 不能直接冒充业务事务回滚。若用户在“订单已经提交”播到一半时插话,conversation 需要保留真实订单 receipt,即使刚才的自然语言被截断。
9.3 长任务必须离开实时 Job 的内存
Realtime Job 适合低延迟会话,不适合作为数分钟到数小时任务的唯一容器。长任务应进入持久队列/工作流系统,保存:
task_id、用户/租户和权限快照;- 输入、审批、幂等键和业务版本;
- 可恢复 checkpoint、进度和最终 artifact;
- 与
room_name、participant_identity、agent_job_id的弱关联; - 结果通知渠道,而不是假设原 Room 永远在线。
LiveKit Agent 负责把用户意图转成受控任务、播报进度和结果;长期状态仍应由业务系统拥有。
十、前端交互面:音频 Track 之外还要同步什么
LiveKit Room 同时提供 media tracks、text/data、RPC 和 Participant metadata/attributes,因此前端不必从音频或转写猜 Agent 状态。建议定义一个小而版本化的 UI 协议:
| 消息 | 关键字段 | 用途 |
|---|---|---|
agent.state.v1 | state, turn_id, speech_id, seq | listening / thinking / speaking / interrupted / error |
transcript.delta.v1 | speaker, segment_id, text, final, seq | 实时字幕与修订 |
tool.status.v1 | operation_id, name, phase, display_text | 查询中、等待确认、已完成 |
session.error.v1 | code, recoverable, action | 明确提示重连、换设备、重试 |
playout.ack.v1 | speech_id, audio_ms, seq | 可选:精确回报客户端已播放位置 |
状态同步必须带 turn_id / speech_id / 单调 seq,避免重连或迟到消息让 UI 从 listening 倒退到 speaking。字幕也应区分:
- speculative partial:可修改;
- stable partial:短期 UI 可依赖;
- final transcript:语言识别完成;
- audible assistant transcript:用户实际听到部分;
- generated transcript:仅用于调试,不能直接当用户记忆。
对用户而言,最重要的恢复提示不是“WebRTC connected”,而是“我仍在听”“刚才的操作已经完成”“这段回答被你打断了”。底层连接状态要投影成产品语义。
十一、电话、机器人和多参与者为什么自然进入同一架构
11.1 SIP 来电也是 Participant
LiveKit Telephony 用 trunk 和 dispatch rule 把传统电话接入 Room。每个来电者被表示为 SIP Participant,拥有普通 Participant 的 metadata/attributes 和额外 SIP 属性;Agent 无需改写核心会话逻辑,只是输入音频从浏览器 WebRTC 变成 SIP/RTP gateway。Telephony introduction
PSTN / SIP Provider
→ SIP trunk
→ LiveKit SIP service
→ SIP Participant 加入 Room
→ Agent dispatch
→ 同一 AgentSession / tools / model pipeline电话场景还需额外处理:
- 8 kHz 窄带与 HD voice codec 差异;
- DTMF、呼叫转移、振铃、接听、挂断和 voicemail/IVR;
- caller ID 不能单独作为强身份认证;
- SIP trunk ACL、TLS/SRTP、区域和监管要求;
- 来电噪声、串音与没有可靠设备侧 AEC 的场景。
自托管 LiveKit Server 不会自动包含 SIP 服务,官方文档要求单独部署 LiveKit SIP。Telephony architecture
11.2 多参与者不是免费获得的“群聊智能”
Room 原语支持多参与者,但多方 Agent 仍需显式设计:
- diarization:哪位 Participant 在说,而不只是一个混合音频流;
- subscription policy:Agent 听谁、何时听;
- floor control:多个人同时说时谁拥有 turn;
- privacy:私聊 Track / data 是否允许 Agent 订阅;
- tool authority:不同 Participant 的权限和确认不能混在一个 user context;
- response addressing:Agent 回答全体还是某一人。
LiveKit 解决了媒体成员关系,不会自动解决多人社会规则。
十二、安全与隐私:E2EE 在 AI Agent 场景中的真实边界
12.1 传输加密、Room E2EE 与模型可见性不同
普通 WebRTC 使用 DTLS-SRTP 保护链路传输;LiveKit 还支持 media/data 的端到端加密,使中间 SFU 无法读取内容。密钥生成、分发和轮换由应用负责,信令/API 仍只是 TLS 传输加密而非 E2EE。Encryption overview
但 AI Agent 必须读取用户音频才能推理。因此 E2EE 的实际 endpoint 会包含 Agent Participant:
用户设备 ⇄ E2EE media ⇄ Agent endpoint ⇄ Provider TLS ⇄ 模型服务这能防止 SFU 明文读取媒体,却不能让音频对 Agent 进程、STT/LLM/TTS Provider 不可见。隐私评审应逐跳回答:
- 谁拥有 E2EE key;
- Agent 在哪里解密;
- 原始音频是否录制、保留多久;
- 转写、trace 和日志是否包含 PII;
- Provider 是否用于训练、在哪个区域处理;
- 工具输出是否回到第三方模型;
- 用户如何撤回同意和删除数据。
12.2 最小权限边界
| 主体 | 最小凭据 |
|---|---|
| 浏览器/App | 短 TTL Room token,仅目标 room/identity 和必要 publish/subscribe grants |
| Agent Worker 注册 | Agent grant;不复用业务管理员 token |
| Job Participant | 只允许当前 Room 与需要的 Track/Data 权限 |
| Model plugin | 单 Provider、单环境 secret,最好短期/代理化 |
| Tool | 按用户/租户和 operation scope 的 capability token |
| Observability exporter | 只写入目标 telemetry backend,不可读取业务数据库 |
Room metadata 与 Participant attributes 可被其他参与者或服务读取时,不应存放长期密钥、完整 OAuth token 或不必要的 PII。
十三、可观测与测试:要重建一条跨媒体的因果链
13.1 Trace 不能只停在 LLM
LiveKit Agents 为 Session、语音管线和工具提供 OpenTelemetry instrumentation;Cloud Insights 可把音频、转写、trace、日志和事件指标放入统一时间线,完全自托管则应把相同语义导出到自有系统。Export traces Agent insights
推荐统一以下关联键:
trace_id
room_name / room_sid
participant_identity / participant_sid
agent_job_id / worker_id
session_id / turn_id / speech_id
provider_request_id / response_id
tool_operation_id端到端时间线至少记录:
| 时间点 | 说明 |
|---|---|
mic_capture_at | 客户端采到首帧/末帧 |
room_track_rx_at | Agent 收到媒体 |
speech_start/end_at | VAD 边界 |
turn_committed_at | 轮次正式提交 |
stt_partial/final_at | 转写阶段 |
llm_first_token_at | 文本生成首 token |
tool_start/end_at | 外部副作用 |
tts_first_frame_at / realtime_audio_at | 模型首音频 |
room_audio_published_at | Agent 发布输出 |
client_playout_started/ended_at | 用户真实播放近似 |
interrupt_detected/flushed/truncated_at | 打断三阶段 |
只有这样才能区分“模型慢”“网络慢”“端点判断慢”“TTS 慢”“音频已到但 autoplay 被阻止”。
13.2 指标分成四组
- 媒体 QoE:RTT、jitter、packet loss、NACK、TURN ratio、audio concealment、input/output level。
- 会话体验:time-to-agent-ready、end-of-turn latency、time-to-first-audio、barge-in stop latency、false interruption rate、dead air。
- 模型与业务:STT WER 抽样、tool success/duplicate/cancel、hallucination、handoff、token/audio seconds/cost。
- 平台可靠性:worker load、dispatch wait、job crash、provider reconnect、Room node drain、Redis/TURN failure。
均值会掩盖坏体验,应按 P50/P95/P99、设备、网络、语言、Provider、区域、电话/网页入口拆分。
13.3 测试分层
LiveKit 自带的测试框架可在 pytest/Vitest 中断言 message、tool call、arguments 和 handoff;Agent simulations 可测试完整多轮对话,但默认文本模式不覆盖真实音频链路。Testing and evaluation
因此需要四层测试:
- 确定性逻辑测试:工具 schema、权限、幂等、状态机、conversation truncation。
- 文本行为测试:Prompt、tool selection、handoff、错误恢复、grounding。
- 合成音频回归:多语言、口音、噪声、长停顿、数字、人名、重叠说话。
- 真实设备 E2E:手机/浏览器/耳机/外放、Wi-Fi/4G/高丢包、后台切换、SIP 电话。
必须单独做打断故障注入:在不同生成位置插话、连续两次插话、咳嗽、短 backchannel、cancel 后注入迟到音频、Provider 断线、Room resume、Job crash 和工具成功后播报被打断。
十四、与直接 OpenAI Realtime 架构的本质比较
| 维度 | 客户端直连 OpenAI Realtime | LiveKit + Agent + OpenAI Realtime |
|---|---|---|
| 客户端媒体路径 | Client ↔ OpenAI | Client ↔ LiveKit SFU ↔ Agent ↔ OpenAI |
| 首要目标 | 最短的单用户模型媒体路径 | Provider-neutral 的实时媒体与 Agent 平台 |
| API key | 客户端短期凭据 + 可选后端 sideband | Provider key 只在 Agent 服务器 |
| 多参与者 / Track | 需自建产品编排 | Room 原生 |
| SIP / 电话 | OpenAI SIP 接入或自有网关 | SIP Participant 进入同一 Room |
| 模型切换 | 客户端/后端需适配 Provider | Agent plugin / pipeline seam |
| 级联 STT-LLM-TTS | 另建管线 | 一等路径 |
| 工具执行位置 | sideband backend / application | Agent 进程 + 自有 Capability Gateway |
| 打断 | OpenAI + 客户端/sideband 协调 | AgentActivity + RoomIO + provider truncate |
| 可听历史 | WebRTC provider 可处理一部分;复杂产品仍需投影 | 显式 playout position 与 transcript/context sync |
| 自托管媒体 | 不可自托管 OpenAI model/edge | LiveKit media 与 Agent 可自托管;模型仍依选择而定 |
| 运维成本 | 较低 | SFU、TURN、Redis、Agent pool、模型连接均需运营 |
| Provider 锁定 | 高 | 较低,但 LiveKit SDK/Room 模型形成平台依赖 |
两者不是简单替代关系:LiveKit 可以把 OpenAI Realtime 当作其中一个模型 Provider。真正的选择是:
- 最短路径优先:单用户、Web/App、Provider 固定、业务工具简单 → 直接 Realtime 更合适。
- 平台控制优先:网页 + 电话 + 多参与者、Provider 可换、复杂工具、自托管媒体 → LiveKit 更合适。
- 混合:关键消费端走直连 Realtime,企业/电话/多方渠道走 LiveKit;自有业务 Capability Gateway 和长期任务层保持一致。
十五、LiveKit 生态矩阵:完整平台的分层架构与模块职责
15.1 先正确阅读 Ecosystem 截图
图 15-1:用户提供的 LiveKit Ecosystem 页面截图(1704×844,截取于 2026-09-01)。它把语言入口、示例、UI、控制面 SDK、运行服务、托管平台和社区资源放在一张表中;这是一张能力索引,不是单个应用的部署清单。
截图中有三种不同的“并列关系”:
- 替代实现:Browser、Swift、Android、Flutter、ESP32 等 Client SDK 按终端选一个;Python 与 Node.js Agents SDK 按 Agent 技术栈选一个。
- 开发资产:Starter Apps、UI Components、Docs、CLI 与 Community 帮助开发和展示,通常不是运行时依赖。
- 独立服务:LiveKit Server、Ingress、Egress 与 SIP 是可分别部署的服务;采用 Server 不等于必须部署后三者。
15.2 完整 LiveKit 平台的七层架构
| 层 | 核心对象 / 模块 | 主要职责 | 掌握的权威状态 | 不负责什么 |
|---|---|---|---|---|
| 终端产品层 | Browser、Swift、Android、ESP32 等 Client SDK;Starter Apps;UI Components | 采集/播放音视频、呈现 UI、加入 Room、发布/订阅 Track、收发 Data/RPC | 本地设备、权限、实际扬声器播放位置 | Agent 业务逻辑、SFU 路由、模型推理 |
| 媒体接入层 | WebRTC、ICE、DTLS-SRTP、RTP/RTCP、Opus | 建连、链路加密、实时媒体传输、丢包与抖动反馈 | 单条 PeerConnection 与本地媒体队列 | Room 业务身份、Agent 工具权限 |
| Room / SFU 层 | LiveKit Server、Room、Participant、Track、SFU | 信令、房间成员、Track 路由、订阅关系、质量控制、Agent dispatch 入口 | Room 状态、Participant/Track 注册与媒体路由 | STT/LLM/TTS、Prompt、真实业务动作 |
| Agent 执行层 | Agents SDK、AgentServer、Worker、Job | 注册执行容量、接受 dispatch、为会话启动隔离 Job | Worker capacity、Job 生命周期 | 终端 WebRTC 接入、Room 路由 |
| 会话智能层 | AgentSession、RoomIO、VAD/turn、tools/MCP | 音频与转写 I/O、轮次、打断、模型编排、工具调用与会话事件 | 当前 Session、generation、speech handle、模型上下文投影 | SFU 转发、业务副作用真相 |
| 模型与业务层 | Realtime Model 或 STT→LLM→TTS;Capability Gateway | 语音理解/生成、推理、权限校验、幂等动作、记忆和审计 | Provider generation 与业务 receipt | Room 媒体路由 |
| 平台运维层 | Cloud、Server APIs、Redis、TURN、Ingress、Egress、SIP、Insights/OTel | 托管、扩容、NAT 中继、外部媒体、录制、电话、观测与管理 | 集群路由、部署容量、录制/电话任务、trace | 产品 UI 与 Agent Prompt |
这七层通过两个共享模型连接:媒体侧使用 Room → Participant → Track,Agent 侧使用 AgentServer → Worker → Job → AgentSession → RoomIO。LiveKit 的价值不是某一个 SDK,而是让不同终端、媒体路由、Agent 生命周期和模型管线采用可组合的公共契约。LiveKit overview Rooms, participants, and tracks
15.3 Ecosystem 每个模块的职责、作用与依赖
图 15-2:LiveKit 生态模块落位图。中间是产品运行时真正经过的语音主链;上方是开发与产品入口;下方是由公网、规模、电话、录制或外部媒体需求触发的控制、扩展与运维面。Starter Apps 与 UI Components 复用相应 Client SDK;多语言 SDK 是替代项,不是累计安装项。该图是基于公开模块职责绘制的编辑性解释,不是 LiveKit 官方部署拓扑。
先用图定位模块所在平面,再用下表确认它的交付形态、依赖和明确边界:
| 生态模块 | 交付形态 | 核心职责与作用 | 进入哪条链 | 依赖 / 被谁依赖 | 明确边界 |
|---|---|---|---|---|---|
| Agents SDK:Python / Node.js | 应用框架与库 | 提供 AgentServer、Worker/Job、AgentSession、RoomIO、模型插件、tools、turn 与 interruption | Room Track → AI 执行 → Agent Track | Room 模式依赖 LiveKit Server;也允许自定义 Session I/O | 不是 SFU,不原生监听任意客户端音频协议 |
| LiveKit Client SDKs | 各平台客户端库 | 封装 WebRTC、Room 状态、Track capture/render、重连、Data/RPC 与平台媒体 API | 终端 ↔ Room | 依赖 LiveKit Server/Cloud;被 Starter/UI 使用 | 多语言 SDK 是替代项,不是累计依赖 |
| ESP32 SDK | ESP-IDF 组件 | 在受限硬件上实现 LiveKit Participant、双向音频与设备数据 | 微控制器 ↔ Room | 依赖 LiveKit Server/Cloud 与硬件 capturer/renderer | Developer Preview;不是使用 Agents SDK 的前提 |
| Starter Apps | 可运行样板项目 | 演示 token、入房、媒体、转写、Agent 状态和部署组合 | 开发启动路径 | 依赖对应 Client/Agents SDK | 可复制、可删除;不是 framework runtime |
| UI Components | React、SwiftUI、Compose、Flutter 组件 | 波形、设备选择、转写、Agent 状态和交互控件 | 展示层 | 依赖相应 Client SDK | 不参与媒体路由或 Agent 推理 |
| Server APIs | Node/Go/Ruby/Java/Python/Rust/PHP/.NET SDK | 签 token,管理 Room/Participant/dispatch/recording,执行可信控制面操作 | 业务后端 ↔ LiveKit 控制面 | 依赖 Server/Cloud API | 只需选后端所用语言;不是实时媒体数据面 |
| LiveKit Server | Go 服务进程 | WebRTC 信令、NAT traversal、Room 状态、Participant/Track、SFU RTP 转发、QoS 与 Agent dispatch | Client ↔ Room ↔ Agent | Client SDK 与 RoomIO 都依赖它 | 不是模型 Provider,也不执行 Agent tools |
| LiveKit Cloud | 托管平台 | 托管 Server、全球网络、TURN、观测以及可选 Inference/Agent 服务 | 完整托管部署 | 替代自建 Server/相关运维 | 不是使用开源 SDK 的强制前提 |
| Ingress | 独立 OSS 服务 | 将 RTMP、WHIP 等外部媒体转换为 Room Track | 外部流 → Room | 依赖 LiveKit Server | 普通 Client SDK 发布媒体时不需要 |
| Egress | 独立 OSS 服务 | 录制 Room/Track、合成布局、导出文件或转推直播 | Room → 文件/直播 | 依赖 LiveKit Server 与存储/目标平台 | 不负责客户端播放 |
| SIP | 独立 OSS 服务 | 将电话 trunk、DTMF 与呼叫生命周期映射为 Participant | 电话网 ↔ Room | 依赖 LiveKit Server 与 SIP provider | 非电话产品无需部署 |
| Redis | 外部数据存储 / 消息总线 | 多节点 Room 路由、节点状态与分布式消息 | Server 集群内部 | 分布式模式需要;单节点不需要 | 不是对话记忆数据库 |
| TURN | 媒体中继服务 | ICE 直连失败时中继媒体 | Client ↔ TURN ↔ Server | 跨网、防火墙、复杂 NAT 时需要 | 本机/简单 LAN 不一定需要,但完整公网产品必须准备 |
| CLI | 命令行工具 | 本地启动、token/Room 调试、部署和运维操作 | 开发与运维 | 调用 Server/Cloud API | 不进入应用数据链 |
| Docs / Docs MCP | 文档与机器可读知识接口 | 帮助人和 Agent 查询 API、示例与概念 | 开发知识链 | 无 runtime 依赖 | 不部署到产品运行环境 |
| Community | Slack、X、YouTube、Developer Community | 支持、案例、讨论和问题排查 | 生态支持 | 无 | 不属于软件架构 |
| Provider plugins | Agents SDK 插件 | 适配 OpenAI、Google、Deepgram、TTS 或自托管模型的事件与能力 | AgentSession ↔ Model | 依赖 Provider API | 必须统一 cancel、tool、truncate 和 usage 语义 |
15.4 一次完整语音会话怎样穿过这些模块
- 业务后端用一个 Server API SDK 签发短期 Room JWT。
- Browser/Swift/Android 等 Client SDK 通过信令加入 Room,再以 ICE + DTLS-SRTP 建立媒体连接。
- Client 发布麦克风 Track;LiveKit Server 登记 Track 并由 SFU 转发给订阅者。
- AgentServer 预先向 LiveKit 注册 Worker;Room 触发 dispatch 后,某个 Worker 接受 Job。
- Job 进程以 Agent Participant 加入 Room;RoomIO 订阅用户 Track。
- AgentSession 完成 VAD/turn、Realtime 或 STT→LLM→TTS、tools/MCP 与 interruption。
- Agent 的工具调用通过 Capability Gateway 执行真实动作并返回业务 receipt。
- RoomIO 发布 Agent 音频 Track;SFU 将它转发给 Client SDK 播放。
- 客户端掌握物理播放位置;需要精确可听历史时应回传 playout acknowledgement。
- Egress、Ingress、SIP、Cloud、Redis、TURN 和 Insights 只在各自触发条件出现时进入旁路或运维面。
15.5 完整平台的最小切片与扩展模块
| 类型 | 最小完整 Room 模式 | 何时增加 |
|---|---|---|
| 必选替代项 | 一个 Client SDK;LiveKit Server 或 Cloud;Python/Node Agents SDK 二选一;一条模型路径 | 根据终端、托管方式和 Agent 语言选择,不是全装 |
| 可信控制面 | 能签发短期 JWT 的最小后端;必要时调用 dispatch/Room API | 多租户、动态权限、管理后台出现后扩展 |
| 产品展示 | 自有 UI 即可 | 希望快速采用官方交互模式时选 Starter/UI Components |
| 媒体旁路 | 无 | 外部流用 Ingress;录制/直播用 Egress;电话用 SIP |
| 网络与规模 | 单节点可无 Redis;网络简单时可能不走 TURN | 多节点加 Redis;公网弱网必须准备 TURN;多区域再加路由与观测 |
| 托管能力 | 可全部自建 | 不想运营媒体网络、Agent 部署或观测时采用 Cloud |
十六、本地应用集成:Cardputer 与桌面 App 直接进入 Agent Runtime
16.1 结论:可以绕过 Server/SFU,但直连的是 AgentSession 自定义 I/O
Cardputer、桌面 App 与本地 Agent 都处于同一设备或简单局域网时,如果产品不需要 Room、多 Participant、标准 Client SDK 互通、SIP/录制或公网 NAT 穿透,那么 LiveKit Server、SFU、Client SDK、WebRTC、JWT 与 RoomIO 都可能是多余的媒体中间层。此时可以只复用 LiveKit Agents 的会话与模型 runtime:
Cardputer / Desktop App
↕ 自定义本地音频与控制协议
LocalTransportAdapter
↕ Custom AudioInput / AudioOutput
AgentSession
↕ Provider plugin / tools
Model + Capability Gateway但必须精确使用术语:标准 AgentServer 默认是 Worker/Job 生命周期与 dispatch 控制面,不是 WebRTC、UDP 或 PCM 音频服务器。 官方 AgentServer 会向 LiveKit Server 注册并接受 Job;若要“客户端直接对接 Agent Server”,需要在 Agent 进程中新增 LocalTransportAdapter,或者由本地宿主直接创建 AgentSession,再把自定义音频 I/O 绑定到 session.input.audio 与 session.output.audio。当前源码只在传入 Room 且未设置外部音频 I/O 时创建默认 RoomIO,这正是直连模式的扩展 seam。Agent server AgentSession source
官方 console 模式已经证明 AgentSession 可以在没有外部 LiveKit Server 的情况下取得本地/TCP 音频 I/O;但其 fake job / console host 是开发工具,不应未经稳定性审计直接当生产协议。生产实现应建立自己的 LocalAgentHost 和公开、可测试的 transport contract。Agents console source
16.2 本地直连架构
图 16-1:Cardputer 与桌面 App 绕过 Room/SFU,直接通过 LocalTransportAdapter 驱动 AgentSession。图中
AgentServer只表示可选的生命周期宿主;音频入口来自自定义AudioInput/AudioOutput,不是 AgentServer 原生媒体协议。该图是待实现设计,不是运行态观测。
16.3 Cardputer 与桌面 App 的两种落地形态
| 本地客户端 | 推荐进程边界 | 音频与控制入口 | 播放出口 | 为什么不需要 LiveKit Server |
|---|---|---|---|---|
| Cardputer-ADV + Mac App | Swift Mac App 管设备/UI;Python/Node local-agent sidecar 管 AgentSession | ADV 加密 UDP 音频 + BLE/GATT 控制 → Mac DeviceGateway → Unix Domain Socket / localhost framed stream | Agent AudioOutput → sidecar IPC → Mac 下行队列 → ADV 扬声器 | ADV 与 Agent 都只服务一台 Mac;不需要 Room 路由、WebRTC 互通或 NAT traversal |
| 纯桌面端 App | 若语言兼容可同进程;Swift/Electron + Python Agent 通常使用 sidecar | CoreAudio/AVAudioEngine/AudioWorklet → UDS/TCP/IPC → LocalTransportAdapter | Agent AudioOutput → App 播放队列 | 客户端与 Agent 在同机,系统 IPC 已提供寻址和可靠传输 |
本地 sidecar 不需要对局域网开放端口。优先使用 Unix Domain Socket;若跨语言库限制必须用 TCP,只绑定 127.0.0.1,再增加随机会话 secret、peer credential 校验、长度上限与 backpressure。Cardputer 到 Mac 的无线链仍需既有配对、认证加密、序号和重放保护。
16.4 LocalAgentHost 需要补齐哪些职责
| 本地模块 | 职责 | 替代完整 LiveKit 中的什么 | 权威状态 |
|---|---|---|---|
| LocalTransportAdapter | 接收/发送 AudioFrame 与控制事件,处理 framing、版本、backpressure 和连接生命周期 | Client SDK + WebRTC + RoomIO 的 I/O 接缝 | 连接、frame 序号、generation id |
| DeviceGateway | Cardputer 配对、UDP 解密、jitter buffer、解码/重采样、下行队列 | 终端 Client SDK 的设备与媒体适配 | 设备会话、无线质量、实际播放回执 |
| SessionRegistry | 以 device/app session id 创建、查找、关闭 AgentSession | Room + dispatch 的会话发现 | 本地会话唯一性与生命周期 |
| Custom AudioInput | 将本地 PCM frame 异步推入 AgentSession,提供 attach/detach 与关闭语义 | RoomIO 的 Participant audio input | 输入流边界 |
| Custom AudioOutput | 接收 Agent 音频、排队、flush、回传真实播放位置 | RoomIO / Client playout 组合 | 输出 generation、queued/played position |
| LocalAgentHost | 加载 Agent/模型、创建 AgentSession、监管任务、崩溃恢复和 graceful shutdown | AgentServer/Worker/Job 的本地简化形态 | Session task 与进程健康 |
| Capability Gateway | 工具 allowlist、系统权限、幂等、超时、receipt 和审计 | 完整平台外的业务后端 | 真实业务动作 |
| Telemetry | 贯穿 device/session/generation/tool 的 trace 与延迟指标 | Insights/OTel 的本地实现 | 可诊断证据 |
16.5 从用户说话到语音回答的直接链路
- 本地 App 启动 Agent sidecar,通过 UDS 握手并协商 protocol version、audio format、session id 与能力。
- Cardputer 或桌面麦克风产生 frame;LocalTransportAdapter 校验长度、序号与时间戳,必要时做 jitter buffer 和重采样。
- Custom AudioInput 将 frame 推入 AgentSession;PTT/VAD/commit/interrupt 作为独立控制事件传入,不能伪装成音频样本。
- AgentSession 运行 Realtime Model,或 STT → LLM → TTS;tools/MCP 仍由 Agents SDK 编排。
- 真实动作进入 Capability Gateway,使用本地可信身份,不接受模型伪造的 user/device id。
- Custom AudioOutput 收到生成音频,附加 generation id,经 IPC 返回 App;Cardputer 路径再由 Mac 封装成无线下行。
- 客户端播放并回报
played_sample/played_ms;LocalAgentHost 据此维护 Playout Ledger 与可听历史。 - 打断时同时 cancel 当前模型、flush Custom AudioOutput、清空 App/ADV 播放队列,并让旧 generation 的迟到 frame 被 fence 丢弃。
- App 或设备断线时关闭对应 AgentSession;Provider 断线只重建模型连接,不重启 DeviceGateway;sidecar 崩溃由 Mac App supervisor 拉起。
16.6 复用了什么,又主动放弃了什么
| 维度 | Agents-only 本地直连 | 完整 LiveKit Room 模式 |
|---|---|---|
| 保留 | AgentSession、Provider plugins、VAD/turn、interruption、tools/MCP、events、usage/tracing | 以上全部,加上 Room 媒体平台 |
| 音频入口 | Custom AudioInput / AudioOutput | RoomIO + Participant Track |
| 会话发现 | App 自有 SessionRegistry | Room + dispatch |
| 本地身份 | UDS peer / 配对密钥 / app session | Room JWT + Participant identity |
| 网络 | UDS、localhost TCP 或自有 UDP/BLE | WebRTC、ICE、DTLS-SRTP、RTP/RTCP |
| 得到的收益 | 最少进程、最少复制/编码、低延迟、直接拿到播放回执 | 标准多端 SDK、Room/Track、重连、质量统计、多参与者与媒体扩展 |
| 放弃的能力 | Client SDK 互通、SFU、多 Participant、Data/RPC、SIP、Ingress/Egress、Cloud media | 需要部署 Server/Cloud 与 token/control plane |
16.7 用一个稳定 I/O 接口保留未来迁移能力
不要把 AgentSession 直接耦合到 UDP、Swift IPC 或 Cardputer 包格式。先定义应用自己的深接口:
class SessionTransport(Protocol):
async def receive(self) -> AudioFrame | ControlEvent: ...
async def send_audio(self, generation_id: str, frame: AudioFrame) -> None: ...
async def flush(self, generation_id: str) -> PlayedPosition: ...
async def send_event(self, event: SessionEvent) -> None: ...
async def close(self, reason: str) -> None: ...
# 当前:CardputerTransport / DesktopIPCTransport
# 未来:LiveKitRoomTransport(内部使用 RoomIO)这样“本地直连 → 完整 LiveKit”只替换 transport adapter;Agent、模型、工具、Capability Gateway、generation fence、可听历史与测试合同保持不变。
16.8 什么时候重新引入 LiveKit Server
出现下列任一明确需求时,Room/SFU 不再是多余层:
- 同一 Agent 同时服务浏览器、手机、Cardputer、电话或多人会话;
- 客户端离开本机,需要 NAT traversal、TURN、弱网重连和质量控制;
- 需要标准 LiveKit SDK、Participant/Track/Data/RPC 语义;
- 需要 SIP、Ingress、Egress、录制、远程监看或 Cloud;
- Agent 数量与会话量需要 Worker dispatch、隔离、容量和弹性调度;
- 希望第三方客户端无需理解自定义 UDP/IPC 协议即可接入。
16.9 推荐实施顺序
- Transport loopback:不接模型,验证 Desktop/Cardputer frame → LocalTransportAdapter → 原样 AudioOutput → 播放,测 backpressure、断线和格式错误。
- AgentSession fake path:接自定义 AudioInput/Output 与确定性 fake STT/LLM/TTS,验证 session lifecycle、PTT、flush 和 generation fence。
- 真实 Provider:只选 Realtime 或一条 cascade,测首音、取消、断线重连与成本。
- Cardputer downlink:补 Mac → ADV 音频、jitter buffer、真实播放回执和连续 100 次 PTT。
- Capability Gateway:先接一个只读工具,再验证授权、幂等、receipt 与审计。
- Production host:sidecar 签名/打包、UDS 权限、crash supervisor、升级、日志与隐私保留策略。
- RoomIO adapter PoC:只有产品出现多端/远程信号后,再用同一
SessionTransportcontract 实现完整 LiveKit 模式。
一句话结论:本地应用可以直接复用 LiveKit Agents runtime,但不应把 AgentServer 误当成现成音频网关;真正需要新增的是 LocalTransportAdapter 与自定义 AgentSession I/O。
十七、如果我们自己实现一套类 LiveKit 系统
17.1 不建议从零重写 WebRTC 与 SFU
自己实现“基本相似系统”时,最理性的路径是复用成熟 Media Fabric,而不是从 ICE、DTLS、SRTP、NACK、TURN、拥塞控制和跨浏览器兼容开始。可以采用 LiveKit、mediasoup、Janus、Pion 或托管 RTC;自研价值应集中在语音 Agent 的控制闭环。
推荐模块边界:
| 模块 | 核心接口 | 权威状态 |
|---|---|---|
TokenBroker | mint(room, identity, grants, ttl) | 身份与最小 Room 权限 |
MediaGateway | join/publish/subscribe/data/reconnect | Participant / Track |
AgentScheduler | register/offer/assign/drain | Worker capacity / Job lease |
SessionActor | on_audio/on_text/generate/interrupt/close | 当前会话和 turn |
TurnManager | speech_event/stt_event/eou/commit | 谁拥有轮次 |
ModelPipeline | push_audio/generate/cancel/truncate | Provider generation |
PlayoutLedger | enqueue/played/flush/audible_text | 用户可听历史 |
CapabilityGateway | authorize/execute/status/cancel | 真实业务动作 |
ConversationStore | append/project/checkpoint/restore | 持久对话投影 |
TelemetryPlane | span/metric/audio_ref/audit | 跨层证据 |
接口要暴露取消与游标,而不能只有 respond(audio) -> audio:
class ModelGeneration(Protocol):
generation_id: str
async def audio(self) -> AsyncIterator[AudioFrame]: ...
async def text(self) -> AsyncIterator[TextDelta]: ...
async def cancel(self, reason: str) -> None: ...
async def truncate(self, audible_audio_ms: int, audible_text: str) -> None: ...
class AudioOutput(Protocol):
async def enqueue(self, generation_id: str, frame: AudioFrame) -> None: ...
async def pause(self, generation_id: str) -> None: ...
async def flush(self, generation_id: str) -> PlayoutReceipt: ...
# receipt 必须返回可用于 conversation projection 的播放位置17.2 Session Actor 的最低状态机
CONNECTING
→ LISTENING
→ USER_SPEAKING
→ ENDPOINTING
→ THINKING / TOOL_RUNNING
→ AGENT_SPEAKING
→ INTERRUPTING
→ LISTENING
任何状态
→ RECOVERING
→ 原状态或 CLOSED每个异步回调都必须带 session_epoch + turn_id + generation_id。Session 重建时 epoch 增加;上一 epoch 的 STT final、LLM token、TTS frame、tool callback 若迟到,只能记录审计,不能改变当前 UI、播放或 conversation。
17.3 Conversation 应保存事件,再投影成模型上下文
推荐事件:
UserSpeechStarted
UserTranscriptUpdated / Finalized
TurnCommitted
ModelGenerationStarted
ToolProposed / Authorized / Completed
AgentAudioEnqueued
AgentAudioPlayed
GenerationInterrupted
AudibleAssistantTextCommitted
SessionReconnected由事件投影得到三个视图:
model_context:下一次生成需要的上下文;user_transcript_view:UI 显示的稳定对话;audit_view:包含被取消生成、工具尝试和错误的完整证据。
不要让一份 mutable chat history 同时承担三种语义。
17.4 推荐生产部署
Room 节点、Agent worker 和模型 endpoint 应尽量同区域。Agent autoscaling 不应只看 CPU:至少组合 active jobs、reserved slots、dispatch wait、model connections、memory 和 event-loop lag。缩容先 drain Worker;SFU 则遵守 Room drain,不能把活跃会话迁移假设成无损操作。
十八、实施路线:从 PoC 到可上线系统
Phase 0:两条模型路径的技术样机
- 一个 Web 客户端加入 Room、发布麦克风、播放 Agent Track;
- 一条 STT → LLM → TTS;
- 一条 OpenAI 或其他 Realtime Model;
- 只做一个无副作用查询工具;
- 采集各阶段 timestamp,建立首音延迟瀑布图;
- 在耳机与外放下人工测试打断。
退出条件:两条路径均能连续对话、可手动/自动打断,且 Trace 能解释一次慢请求。
Phase 1:会话一致性
- 实现 turn/generation/session epoch;
- 接入 RoomIO playout receipt 或客户端 playout ack;
- 验证 cancel 后迟到 frame 全被 fencing;
- conversation 只提交 audible assistant text;
- 完成 false interruption pause/resume;
- Provider 断线时重建连接并重放最小上下文。
退出条件:连续 100 次随机位置插话不出现旧音频复活,模型不引用未播放内容。
Phase 2:业务与安全
- Room token broker、租户隔离、Capability Gateway;
- tool schema、权限、确认、幂等、receipt 和 audit;
- 长任务迁入持久 workflow;
- PII redaction、recording consent、retention 与 deletion;
- E2EE 是否包含 Agent endpoint 的明确威胁模型。
退出条件:重复/越权工具测试、断线重试和审批绕过测试全部通过。
Phase 3:多渠道与生产可靠性
- SIP inbound/outbound、DTMF、transfer、hangup;
- Redis、多 SFU、多 Agent Worker、TURN;
- load/drain、job crash、Redis/TURN/provider fault injection;
- 真实设备与网络矩阵;
- 以 P95/P99 和 QoE SLO 驱动扩容。
退出条件:在目标并发、区域和设备矩阵下达到 SLO,发布与缩容不主动中断活跃 Room。
十九、验收矩阵
| 领域 | 场景 | 通过标准 |
|---|---|---|
| 客户端声学 | 外放时用户插话 | Agent 不被自身回声反复打断,用户首个有效词后迅速停播 |
| 音频连续性 | 5% 丢包、100 ms jitter | 可理解且不无限积压;恢复后无爆音和旧音频复活 |
| Room 恢复 | 信令短断 | Participant/Track 恢复,UI 不重复显示旧 turn |
| 完整重连 | 网络切换 Wi-Fi → 4G | 重建 PC、重发 Track,Session 明确恢复或重开 |
| Dispatch | 冷 worker / 满负载 | 不超卖 reserved slot;可选其他 Worker;超时有明确错误 |
| Job 隔离 | 单 Job crash / memory limit | 其他会话不受影响;当前会话按产品策略重建或结束 |
| Turn detection | 犹豫、长停顿、短回答 | 不频繁抢话,最大等待有界 |
| Backchannel | “嗯/对”与咳嗽 | 可 pause 后恢复,不永久丢失回答 |
| 真打断 | 任意播放位置连续插话 | generation、queue、context 三处一致取消/截断 |
| 迟到事件 | cancel 后注入旧 TTS frame/token | generation fence 全部丢弃 |
| 工具幂等 | success 后连接断开并重试 | 业务副作用只发生一次,Agent 查询 receipt 后播报 |
| 权限 | 模型伪造 user_id/tenant_id | Gateway 仅使用可信 Session 身份并拒绝越权 |
| Realtime Provider | WebSocket 中断 | 有界重试,远端 chat context 与本地投影重新对齐 |
| SIP | 入呼、DTMF、转移、挂断 | Room/SIP Participant/Agent lifecycle 一致,不遗留 Job |
| 隐私 | recording off / deletion | 音频、转写、trace、日志均遵循配置和保留策略 |
| Drain | SFU/Worker 发布升级 | 新会话不进入 draining 实例,活跃会话按契约完成 |
二十、最终判断
- LiveKit 是实时 AI 的媒体与运行时平台,不是语音基础模型。 它把用户、Agent、电话和设备统一成 Room Participant,把任意模型接在 Agent 之后。
- 它真正的架构护城河是跨层状态编排。 SFU 本身重要,但 AgentSession、Worker dispatch、打断、playout ledger 和 conversation truncation 才把 RTC 变成可用的语音 Agent 平台。
- 原生 Realtime Model 没有消灭 Agent runtime。 在 LiveKit 拓扑中,模型仍由 Agent 进程桥接;工具、身份、房间、多渠道和可听历史依然在模型外。
- 自然打断必须同时取消生成、清空物理播放并修正语义历史。 只做 VAD 或
response.cancel都不完整。 - LiveKit 的代价是额外 hop 与更大的运维面。 单用户、单 Provider、最短路径应用可能更适合客户端直连 Realtime;平台化、多渠道、可替换和自托管场景才充分发挥 LiveKit 价值。
- 自建同类系统最应该复用媒体底座,自己掌握控制闭环。 Session Actor、Turn Manager、Playout Ledger、Capability Gateway、Conversation projection 与 Telemetry 才是业务差异化所在。
- LiveKit 生态是菜单,不是必须全装的发行版。 远程、多参与者和多渠道产品采用 Client SDK → Server/SFU → RoomIO → AgentSession 的完整平台链;Cardputer-ADV + Mac 或单机桌面 App 若没有这些网络与互通需求,则可省去 Client SDK、WebRTC、LiveKit Server、SFU、Room 与 RoomIO,只保留 Agents SDK、AgentSession、模型插件、tools,并用 LocalTransportAdapter + Custom AudioInput/AudioOutput 接入本地音频。这里的
AgentServer仍是可选的生命周期宿主,不是现成的 PCM/UDP 媒体端点。
一句话概括:
OpenAI Realtime 更像可直接连接的实时语音智能引擎;LiveKit 更像让任意智能引擎进入真实多人、多设备、多渠道世界的实时操作系统内核。
Source Manifest
本报告的 5 张原创机制图、完整生成提示词、SHA-256 与逐图视觉验收记录见 ImageGen Manifest。这些图是基于下列固定源码与官方文档的编辑性解释,不是 LiveKit 官方架构图或运行态观测。
A. 固定源码快照
| 来源 | 固定版本 / commit | 使用范围 | 许可证 | 验证方式 |
|---|---|---|---|---|
| livekit/livekit | Server v1.13.5; 788d4c354474383956067e3a22ffba88dd4803e4 | Room、Participant、Transport、SFU Receiver/DownTrack、RedisRouter、signal bridge、AgentService、AgentDispatchService | Apache-2.0 | 2026-08-23 shallow clone;逐文件 rg / line-level read;版本与 release 核对 |
| livekit/agents | Python Agents 1.7.0; da6af86ac640a3bc54585764e64321d7048c1c16 | AgentServer/Worker、Job process pool、AgentSession、AgentActivity、RoomIO、自定义 Session I/O、pipeline nodes、OpenAI Realtime plugin | Apache-2.0 | 2026-08-23 shallow clone;2026-09-01 复核 AgentSession 外部 AudioInput/AudioOutput 与 CLI console 的无 Room I/O 路径 |
| livekit/client-sdk-js | JS SDK 2.22.0; f3732d14bf3162981e4a57d1daa7305a1f3ab7b5 | getUserMedia、AEC/NS/AGC defaults、PCTransportManager、RTCEngine reconnect、RemoteAudioTrack | Apache-2.0 | 2026-08-23 shallow clone;逐文件 rg / line-level read;版本与 release 核对 |
B. LiveKit 官方文档
| 来源 | 用于支持 | 访问日期 |
|---|---|---|
| Agents overview | Agent 作为 Room Participant、WebRTC bridge、模型插件和工具 | 2026-08-23 |
| LiveKit overview | Room / Participant / Track 与平台边界 | 2026-08-23 |
| Connecting to LiveKit | access token、room/identity/grants、建连职责 | 2026-08-23 |
| Agent server 与 lifecycle | Worker 注册、Job 生命周期、dispatch 与 recovery | 2026-08-23 |
| Agent dispatch | automatic / explicit dispatch | 2026-08-23 |
| Agent session | AgentSession 角色、I/O、tools、turn options | 2026-08-23 |
| AgentSession source 与 session I/O source | 外部 AudioInput/AudioOutput 可直接绑定到 Session;只有 Room 模式才需要默认 RoomIO | 2026-09-01 |
| CLI console source | 本地/TCP AudioInput/AudioOutput 与 unregistered fake Job 证明无 Server/Room I/O 在技术上可行;仅作为开发工具证据,不当作生产传输协议 | 2026-09-01 |
| Running LiveKit locally | macOS 安装、本地 livekit-server --dev、默认 127.0.0.1:7880 与 LAN bind 边界 | 2026-09-01 |
| LiveKit Swift SDK | iOS/macOS Client、Room/Track、音频与数据能力,以及本机 Gateway Participant 的实现入口 | 2026-09-01 |
| Publishing audio tracks | 自定义 audio source、PCM frame、发布 Track 与内部发送缓冲语义 | 2026-09-01 |
| Track management | Track / TrackPublication、publish / subscribe 与 mute 边界 | 2026-09-01 |
| Data packets 与 RPC | PTT、状态、播放回执与设备控制的数据面选择 | 2026-09-01 |
| Turn detection and interruptions | semantic/VAD/STT/manual/model turn modes 与打断行为 | 2026-08-23 |
| Pipeline models | STT-LLM-TTS、Realtime、half-cascade 的选择边界 | 2026-08-23 |
| OpenAI integration | OpenAI LLM/STT/TTS/Realtime plugin 的官方用法 | 2026-08-23 |
| Distributed multi-region | Redis、room affinity、signal bridge、drain、多区域 | 2026-08-23 |
| Encryption overview | media/data E2EE、key distribution、signaling boundary | 2026-08-23 |
| Telephony introduction | SIP Participant、trunk、dispatch rule 与自托管 SIP 边界 | 2026-08-23 |
| Testing and evaluation | 文本行为测试、simulation 与音频 E2E 的边界 | 2026-08-23 |
| Export traces 与 Agent insights | OpenTelemetry 导出与 Cloud Insights 边界 | 2026-08-23 |
| Agent frontends 与 Starter apps | Client SDK、Starter App、UI Components 是按平台选择的入口,不是累计依赖 | 2026-09-01 |
| ESP32 microcontrollers 与 Hardware & devices | ESP32-S3/P4 支持、Track 限制、最小 Room 状态、capturer/renderer 与 AEC 责任 | 2026-09-01 |
| client-sdk-esp32 | 0.3.10、Developer Preview、Opus/AEC/Data/RPC、固定版本要求 | 2026-09-01 |
| ESP32 custom hardware example 与 sdkconfig | 非预置开发板的 I²C/I²S/codec 适配方法,以及示例板的 PSRAM 配置 | 2026-09-01 |
| Self-hosting overview | Cloud / self-hosted 边界,以及 Ingress/Egress/SIP 等独立可选服务 | 2026-09-01 |
C. 关联综合、实现与对照材料
| 来源 | 用途 |
|---|---|
| 实时语音 Agent 端到端架构 | 媒体、会话、业务行动、观测治理四平面综合 |
| OpenAI Realtime API 端到端实现逆向分析 | 对照客户端直连模型、sideband、Conversation 与闭源边界 |
| Cardputer-ADV 硬件与资源边界 | ESP32-S3、512KB 片上 SRAM、8MB Flash、无额外 PSRAM及实时音频资源心智模型 |
| Cardputer Bridge 实机实现 | 固定 v0.10.5 的 ES8311/I²S 音频、加密 UDP、Mac 虚拟麦克风、BLE、OTA 与 Harness 证据 |
用户提供的 LiveKit Ecosystem 截图 88bd19fe…4888 | 记录生态页面的视觉呈现,并作为“能力目录容易被误读为部署依赖图”的界面证据;原图归档于 references/livekit-ecosystem-user-screenshot.png,1704×844;接收日期 2026-09-01 |
D. 未执行与未知边界
- 未使用真实 LiveKit Cloud 项目或 OpenAI/Google 等 Provider 账户执行端到端建连。
- 未在浏览器、iOS、Android、蓝牙、外放和 SIP 真机上测量 AEC、打断和首音延迟。
- 未部署多节点 LiveKit、Redis、TURN、SIP 与 Agent pool,也未做故障注入或压测。
- 未实现本文建议的 ADV → Mac Device Gateway → LocalTransportAdapter → Custom AudioInput/AudioOutput → AgentSession → 模型/工具 → ADV 扬声器闭环;当前只有既有 ADV → Mac 麦克风链证据,图 16-1 是待实现设计而非运行态观测。
- Cloud Inference、增强噪声消除、Agent Insights 后端、全球调度和 SLA 只按公开文档描述,没有内部源码证据。
- 固定源码中的默认值是该版本实现,不应当作未来稳定 API;生产决策需重跑版本审计与 E2E。