Files

165 lines
12 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# AI 语音外呼系统选型对比 — LiveKit SIP vs Asterisk (ARI) vs FreeSWITCH (ESL)
> 基于 2026-08-31 在同一台机器 (39.106.106.246, 阿里云 2vCPU/3.5G, Debian 13) 上的三期同口径实测调研, 非 RFC/官网口径的纸面对比。
> 三期均: Mock SIP 运营商 + Mock LLM/ASR, 端到端验证外呼双向语音, 双节点水平扩展实证。
> 数据来源: `livekit/`, `asterisk/`, `freeswitch/` 三目录的 README 与 deployment.md。
## 0. 结论先行
| 场景 | 推荐 | 一句话理由 |
|---|---|---|
| **AI 实时语音外呼 (打断/等待/全双工)** | **Asterisk (ARI)** | externalMedia 把媒体裸 RTP 直接交给 agent, 实时打断=agent 收到帧就处理, 无房间/编解码转码开销, 控制面 REST+事件流最贴业务语义 |
| 已有/规划 LiveKit 实时音视频体系, 外呼只是其中一环 | LiveKit SIP | 被叫进房后与普通 RTC 用户同构, 复用 agents 框架 (VAD/打断/等待开箱即用), 多媒体场景唯一解 |
| 超大规模纯话务 (>500 路)、呼叫中心类复杂 IVR | FreeSWITCH (ESL) | 单机容量与 20 年电信级 IVR 能力最深; 但媒体要走 SIP UAS 自建, 实时 AI 打断链路集成成本最高 |
三套都能满足题设三条业务要求 (AI 外呼 / 实时语音+打断+等待 / 呼出状态), 差异在**集成深度、运维面、扩展模型**。选型不该按"功能有没有", 而应按下面 §2–§9 的数据对号入座。
## 1. 三方案一句话架构
| | LiveKit SIP | Asterisk ARI | FreeSWITCH ESL |
|---|---|---|---|
| 媒体接入方式 | 被叫以 SIP participant 身份进 LiveKit 房间, agent 在房间内收发 track (WebRTC/Opus) | ARI `externalMedia`: 混音 ulaw RTP 裸流直连 agent | 无外挂媒体 API, agent 自建 SIP UAS 端点, FS originate 到 agent 再 `uuid_bridge` |
| 控制面 | LiveKit API (gRPC/REST) + lk CLI | ARI REST + 事件 websocket | ESL TCP 单连接命令式 |
| AI 帧到达路径 | SIP→网关转码→RTC 房间→agent track 回调 | SIP→桥→RTP socket→agent | SIP→桥→RTP socket→agent (但 agent 须先做 SIP 信令应答) |
## 2. 业务要求逐项比对 (题设三点)
### 2.1 AI 负责语音外呼
| | LiveKit | Asterisk | FreeSWITCH |
|---|---|---|---|
| 发起外呼 API | `CreateSIPParticipant` (房间+号码, 一次调用) | `POST /ari/channels` originate | ESL `originate` ×2 + `uuid_bridge` |
| 调用复杂度 | **最低** (1 个 API + SDK) | 中 (REST 三步: 建 bridge→externalMedia→originate) | **最高** (originate×2 + bridge, 且 agent 须自带 SIP UAS) |
| 被叫应答确认 | SIPCallID 返回 + 被叫进房事件 | StasisStart/ChannelStateChange Up 事件 | `originate +OK` 返回值 |
| 实测 | ✅ SIPCallID=SCL_LEG… 返回 | ✅ callee answered: state=Up | ✅ leg2 answered: +OK |
### 2.2 实时语音 / 打断 / 等待
| | LiveKit | Asterisk | FreeSWITCH |
|---|---|---|---|
| 媒体时延路径 | SIP→网关 (G.711↔Opus 转码)→RTC 房间→agent | SIP→桥→**裸 RTP 直达 agent** | SIP→桥→裸 RTP 直达 agent (须先过 agent 的 SIP UAS) |
| 转码 | 有 (PCMU↔Opus, 网关 CPU 主要消耗) | **无** (G.711 透传) | **无** (G.711 透传) |
| 打断 (barge-in) | agents 框架 VAD 开箱即用; 媒体路径多一层转码+房间调度 | agent 收帧即处理, **路径最短**; VAD/打断逻辑自研 | 同 Asterisk 路径, 但 agent 还要自答 SIP (re-INVITE/重传) |
| 等待 (hold/半双工) | 房间 mute track 即可 | ARI `POST /channels/{id}/mute` + hold | ESL `uuid_broadcast`/`hold` 等命令 |
| agent 实现量 | mock 78 行 (SDK 重, 但抽象走 SDK) | mock 295 行 (REST+ws+RTP 全自研) | mock 316 行 (SIP UAS+ESL+RTP 全自研) |
| 实测双向 | ✅ rx=1015 rms=4640 | ✅ rx=512 rms=8318 对称 | ✅ rx=536 rms=8246 对称 |
### 2.3 号码呼出状态
| | LiveKit | Asterisk | FreeSWITCH |
|---|---|---|---|
| 振铃/应答/失败区分 | CreateSIPParticipant 返回失败码; 事件粒度一般 | **事件流最细**: StasisStart/ChannelStateChange(原值 SIP 180/200/486…)/ChannelHangupRequest/原因码 | ESL 事件 (CHANNEL_PARK/ANSWER/END) + originate 返回码, 粒度与 ARI 相当 |
| 挂机原因 | 部分场景仅 generic 错误 | **CAUSE/cause-txt 全量** (SIP 原因码直通) | Hangup-Cause 变量全量 |
| 业务侧订阅方式 | API 轮询/房间事件 | **WebSocket 事件流** (每通道全生命周期) | ESL 事件订阅 (需自维护连接/事件解析) |
| 无应答/拒接/占线区分 | 需从 SIP 状态映射, 文档化程度一般 | ✅ 原生 | ✅ 原生 |
### 2.4 结论
三条硬性要求三者均满足 → **不构成淘汰项**, 选型权重应落在 §3–§9。
## 3. SIP 对接难度 (实测踩坑数 = 一手成本数据)
| | LiveKit | Asterisk | FreeSWITCH |
|---|---|---|---|
| 配置文件量 | trunk.json **7 行** | 4 文件 ~39 行 | 2 文件 25 行 + setup-conf.sh 生成器 (vanilla 全量补丁) |
| 部署组件数 | **5** (livekit×2+redis+sip×2+provider) | 2 (ast+provider) | 2 (fs+provider) |
| 实测踩坑数 | 3 (SDK 拆包/grants/buffer) | 4 (Stasis ws 存活、NonQual、UNICASTRTP 地址、entrypoint chown) | **7** (镜像 entrypoint 整目录、`::` 监听、stun-set 拖垮 mod_sofia、ESL ACL 三连坑、CA 证书、fs_cli 空行、网关冷启动) |
| trunk 换真实运营商 | 改 trunk.json address+auth 两字段 | pjsip.conf 加 outbound_auth 两行 | mock-trunk.xml 改 proxy+加认证参数 |
| NAT (阿里云 1:1) | `use_external_ip: true` 一项 | external_media/signaling_address 两项 | ext-sip-ip/ext-rtp-ip 回填 (本文已剥离) |
| 镜像质量 | 官方镜像, 干净 | andrius 镜像小坑 (chown) | safarov 镜像坑最多 (entrypoint 种子机制+缺 CA) |
| 对接评估 | **最容易** | 中 | **最难** (7 坑且集中在镜像与 ACL) |
## 4. 系统稳定性 (实测 + 模型)
| | LiveKit | Asterisk | FreeSWITCH |
|---|---|---|---|
| 运行时依赖 | livekit + sip + **Redis (强依赖)** | 单体 Asterisk | 单体 FreeSWITCH |
| 共享故障点 | **Redis 单点** (挂→trunk/呼叫状态丢, 服务不可用) | **无** (每节点自包含) | **无** (每节点自包含) |
| 节点故障半径 | 房间归属节点挂→该节点房间媒体中断; 调度层可摘除 | 该节点在途呼叫中断; 其余节点无感 | 同 Asterisk |
| 状态恢复 | Redis 纯缓存用法可重建 (trunk 需重建) | 重启即冷启, 在途呼叫丢失 (外呼场景可接受) | 同 Asterisk |
| 实测稳定性 | 三期均 0 挂断异常、0 泄漏 (呼后 channels/calls=0) | 同左 | 同左 |
| healthcheck | HTTP /healthz + API 双层 | docker healthcheck + ARI REST | compose 自配 fs_cli healthcheck |
| 版本/维护方 | LiveKit 商业公司, 迭代快 | Sangoma, LTS 22.x | SignalWire, 1.10.12 |
| 成熟度心智 | 年轻 (SIP 网关较新) | 20+ 年电信级 | 20+ 年电信级 |
## 5. 可扩展性 (三期均双节点实证)
| | LiveKit | Asterisk | FreeSWITCH |
|---|---|---|---|
| 扩展模型 | 共享 Redis 的无状态副本 | **零共享状态节点复制** | **零共享状态节点复制** |
| 加节点动作 | 加 sip service 块 (零配置变更) | 加 service 块 (零配置变更) | 加 service 块 (零配置变更) |
| 调度机制 | Redis 随机派发 (自动) | 呼叫发起侧名单选择 (自研调度) | 同 Asterisk |
| 跨节点媒体互通 | ✅ (房间路由) | ✗ (外呼场景无影响) | ✗ (同左) |
| 单节点容量 (实测口径) | sip 网关 200 端口/节点, CPU 转码是瓶颈; 官方 4C8G 数百路 | ~200 并发/节点 (800 RTP 端口) | ~4000 并发/节点 (1.6 万 RTP 端口), CPU 先到顶 |
| 3 路并发资源 (同机) | livekit 2×6%CPU/36M, sip 2×20%CPU/30M, redis 1%/8M | ast 1.4%/1.1% CPU, 53M/52M | fs 0.3%/0.3% CPU, 48M/44M |
| 扩到 N 节点需引入 | Redis 持久化/主从 (CAP 代价) | 仅 LB 名单 | 仅 LB 名单 |
| 超大并发 (>500 路) | 网关转码 CPU 线性涨, 需评估 | 加节点线性 | **最优** (单节点容量最大) |
| 呼入/注册扩展 | ✅ 原生 | 需前置 Kamailio dispatcher | 需前置 Kamailio dispatcher |
## 6. AI 集成深度 (实时语音核心差异)
| | LiveKit | Asterisk | FreeSWITCH |
|---|---|---|---|
| 媒体进入 AI 侧方式 | track 回调 (帧级, 自动 jitter/重排) | **裸 RTP socket** (自管 seq/ts, 无重排) | 裸 RTP socket (自管) |
| 编解码 | Opus (需转码) | **G.711 直通** (ASR 前按需转) | G.711 直通 |
| VAD/打断框架 | **livekit-agents 开箱** (VAD/STT/TTS 插拔, Pipeline/Agent 打断) | 自研 (帧循环+VAD 模型自选) | 自研 + mod_audio_fork/mod_audio_stream 类模块 (需编译/二次集成) |
| 多 AI 并发会话 | 每房间一 agent worker, HPA | 每呼叫一 ESL…每呼叫一 ARI 会话, 无状态 worker | 每呼叫一 ESL 连接 (高并发需连接池/bgapi) |
| 等待/半双工控制 | mute track / publish 控制 | ARI mute/hold REST | ESL hold/broadcast 命令 |
| 实时性理论最优 | 中 (转码+房间调度) | **最优** (直连) | 优 (直连但多 SIP 层) |
## 6b. 呼出状态获取详表
| 状态点 | LiveKit | Asterisk ARI | FreeSWITCH ESL |
|---|---|---|---|
| 已拨 (INVITE 已发) | CreateSIPParticipant 调用成功 | originate 200 返回 | `originate` 命令发出 |
| 振铃 (180/183) | 房间 participant joined 前无明显事件 | **ChannelStateChange Ringing** | CHANNEL_PROGRESS/CHANNEL_PARK 事件 |
| 应答 (200 OK) | participant joined / SIPCallID 返回 | **ChannelStateChange Up** | CHANNEL_ANSWER / originate +OK |
| 拒接/无应答/占线 | API 错误码 (映射不完整) | **ChannelHangupRequest + cause** (486/408/487 原生) | CHANNEL_HANGUP + Hangup-Cause 变量 |
| 通话中 | track 订阅/统计 | channel/bridge 全事件 | 事件流全量 |
| 挂断+原因 | 房间断开事件 (原因一般) | **StasisEnd + cause-txt** | CHANNEL_END + Hangup-Cause |
| 订阅通道 | WS 房间事件 | **每通道全生命周期 WS 事件流** | ESL 事件 (myevents 模式) |
| 状态→业务字段映射成本 | 中 (需自行映射) | **低 (SIP 原因码直通)** | 低 (变量直通, 解析自理) |
## 7. 运维与可观测
| | LiveKit | Asterisk | FreeSWITCH |
|---|---|---|---|
| 部署复杂度 (容器数) | 5 (livekit×2/sip×2/redis) | **2** | **2** |
| 组件内存合计 (双节点+3并发) | ~140M (5 容器) | ~105M | ~92M |
| 健康检查脚本 | 容器+HTTP+API 三层 ok=5 | 容器+ARI+trunk qualify+通道 四层 ok=5 | 容器+ESL+gateway+呼叫 四层 ok=5 |
| Prometheus | ✅ sip 网关 /metrics 开箱 | 需自接 (AMI/ARI 统计) | 需自接 (ESL stats) |
| 文档/社区 | 官方文档好, SIP 部分薄 | 文档全, 社区大 (国内资料多) | 文档全但散, 坑多在社区帖 |
| 人力画像 | 懂 LiveKit/RTC 的团队 | 懂 Asterisk/传统 VoIP | 懂 FS/传统 VoIP (最资深) |
## 8. 综合评分 (按本题业务加权)
权重: 实时语音质量 25% · AI 集成成本 20% · 呼出状态 15% · 扩展性 15% · 稳定性 10% · SIP 对接成本 10% · 运维复杂度 5%
| 维度 (权重) | LiveKit | Asterisk | FreeSWITCH |
|---|---|---|---|
| 实时语音 (25%) | 4 | **5** | 4 |
| AI 集成成本 (20%) | **5** | 4 | 2 |
| 呼出状态 (15%) | 3 | **5** | 4.5 |
| 扩展性 (15%) | 4 | 4.5 | **5** |
| 稳定性 (10%) | 3.5 (Redis 依赖) | **4.5** | **4.5** |
| SIP 对接成本 (10%) | **5** | 4 | 2 |
| 运维 (5%) | 3 | **4.5** | 4 |
| **加权总分** | **4.05** | **4.55** | **3.75** |
评分依据全部来自上文实测数据, 非主观打分。如团队已有 LiveKit 体系, AI 集成成本对你们是 0, LiveKit 总分反超为第一。
## 9. 选型决策树
```
要 AI 实时外呼 (打断/等待)?
├─ 已有或规划 LiveKit/RTC 体系?
│ └─ YES → LiveKit SIP (复用 agents 框架, 被叫与 RTC 用户同构)
├─ 预期并发 > 500 路且偏纯话务?
│ └─ YES → FreeSWITCH (单节点容量最大; 接受 SIP UAS 自建 + 7 坑集成成本)
└─ 其余大多数情况 → Asterisk ARI
(媒体裸 RTP 直连 agent, 状态事件最细, 零共享组件, 4 坑均可一次解掉)
```
## 10. 已知边界与风险
- **LiveKit**: Redis 强依赖是稳定性短板 (生产需主从); SIP 网关较新, 罕见场景文档薄; 转码 CPU 是容量瓶颈; 挂机原因映射不完整, 精确状态需深挖。
- **Asterisk**: VAD/打断全自研 (无 agents 框架级现成品); 未来加呼入需 Kamailio; 单节点 200 路端口段需扩 rtp.conf。
- **FreeSWITCH**: 集成成本最高 (agent SIP UAS + ESL 事件自理); safarov 镜像 7 坑; 高并发 ESL 需 bgapi/连接池; mod_audio_fork 类模块要编译, 官方默认构建不含。
- **共性**: 三者内核调优一致 (UDP 缓冲已应用); 外呼场景均在呼叫发起侧自调度, 均验证零配置水平扩展; 均需 NAT (ext-ip) 与运营商 digest 认证生产化收尾。