语音机器人怎么测并发?高峰期 ASR 识别延迟与卡顿压测实战
摘要
环境:自建/云上语音机器人系统(也叫打电话机器人、AI 语音机器人),SIP 线路 + ASR 语音识别 + 对话引擎,被测机 8 核 16G 起步。解决的问题:上线前不知道系统能扛多少路并发,业务高峰期来电集中时出现语音识别延迟、识别卡顿、对话断片。读完能拿到:并发压力测试的四层拆法、SIPp 与 Python 音频回放的可跑脚本、ASR 延迟和卡顿的量化阈值、高峰期卡顿的六类根因排查清单、上线灰度与限流降级策略。适合准备上线语音机器人、需要提前预判承载能力的技术人员和运维参考。
一、压测环境说明
先交代环境,不然文中的延迟数字没法对照。本文压测数据来自以下环境,读者按自己环境等比调整:
| 项目 | 配置 |
|---|---|
| 压测机 | 4 核 8G,CentOS 7,SIPp 3.6 + Python 3.9 |
| 被测机(SIP/媒体) | 8 核 16G,Asterisk 18 LTS,千兆内网 |
| ASR 服务 | 自建 FunASR,2 实例,每实例 4 核 8G,同机房 |
| 对话引擎 | 单实例 4 核 8G,外部接口走内网 |
| 网络 | 压测机与被测机同机房千兆内网,RTT < 1ms |
| 音频样本 | 查件回访类真实录音 200 条,8kHz 单声道 WAV,覆盖不同性别和语速 |
不同环境结论会差很多。 跨机房压测,RTP 丢包和抖动会让识别延迟明显偏高;ASR 用第三方托管,网络往返会吃掉一部分余量。本文数字只在上述环境下有参考意义,生产规划请以自己环境的监控基线为准。
二、先搞清楚“并发”到底指什么
很多人一说语音机器人并发测试,第一反应是“同时拨出去多少路”。这个理解不完整,压测做出来也对不上生产表现。语音机器人的并发至少分四层,每层的瓶颈点不一样:
| 层级 | 并发含义 | 常见瓶颈 |
|---|---|---|
| 线路层 | 同时占用的 SIP 通道数 | 运营商线路并发上限、SIP 注册数 |
| 信令层 | 同时建立的 INVITE 会话数 | 软交换 CPU、注册风暴 |
| 媒体层 | 同时收发的 RTP 流数量 | 带宽、网卡软中断、转码 CPU |
| 识别层 | 同时送进 ASR 的音频流数量 | ASR 实例并发、队列堆积、GPU 显存 |
真正决定“高峰期会不会卡”的是识别层并发,不是线路层。线路开到 200 路,ASR 只扛得住 80 路,剩下的 120 路音频会在队列里排队,表现出来就是客户说完话机器人半天不响应,也就是常说的识别卡顿。所以压测要分层测,不能只测一个总数。
我们当时的做法是:线路层和信令层用 SIPp 压,媒体层和识别层用真实音频回放压,两层数据对上了,才敢报“系统能扛多少路”。
三、压测前要准备什么
3.1 明确业务模型,别拿平均值压
语音机器人的通话时长分布很关键。我们统计过查件回访类通话 200 通、统计周期一周,平均时长 48 秒,但长尾能到 3 分钟以上。如果压测只按平均 48 秒造并发,测出来的承载能力会偏高。正确做法是按 P50、P90、P99 三档时长分别压,或者直接按真实通话时长分布随机采样。
还要区分“有效并发”和“峰值并发”。业务高峰期不是均匀分布的,9:30—10:30 这一小时里,前一分钟可能只有 20 路,下一分钟冲到 60 路。压测要造阶跃式并发,比如每 30 秒加 10 路,观察系统在哪个点开始劣化。
3.2 准备可回放的音频样本
ASR 压测不能用静音或者正弦波,识别引擎对静音的处理路径和真实语音完全不同。要准备至少 50 条真实业务录音,覆盖不同性别、语速、方言口音、背景噪声,转成 8kHz 单声道 WAV。样本太单一,压测出来的 ASR 延迟会偏乐观。
3.3 监控先埋好,不然压完不知道瓶颈在哪
压测前把这几项监控挂上,压的过程中实时看:
-
线路层:SIP 通道占用数、注册状态、INVITE 失败率
-
媒体层:RTP 丢包率、抖动、网卡软中断分布
-
识别层:ASR 请求队列长度、首字延迟、识别完整率、实例 CPU/GPU 利用率
-
对话层:端到端响应延迟(客户说完到机器人开口)、打断响应时间
-
系统层:CPU load、内存、文件句柄、线程数
监控没埋好就开压,等于蒙眼开车。
四、四层并发压测怎么做
4.1 线路层和信令层:SIPp 造并发
SIPp 是压 SIP 信令的常用工具,适合测线路层和信令层的承载上限。核心是写一个 UAC 场景文件,模拟同时发起 INVITE、等待应答、发 RTP、挂断的完整流程。
简化版场景文件 robot_uac.xml:
xml
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE scenario SYSTEM "sipp.dtd">
<scenario name="robot_concurrent_call">
<send retrans="500">
<![CDATA[
INVITE sip:[service]@[remote_ip]:[remote_port] SIP/2.0
Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]
From: <sip:loadtest@[local_ip]>;tag=[call_number]
To: <sip:[service]@[remote_ip]:[remote_port]>
Call-ID: [call_id]
CSeq: 1 INVITE
Contact: sip:loadtest@[local_ip]:[local_port]
Content-Type: application/sdp
Content-Length: [len]
v=0
o=user1 53655765 2353687637 IN IP[local_ip_type] [local_ip]
s=-
c=IN IP[media_ip_type] [media_ip]
t=0 0
m=audio [media_port] RTP/AVP 0
a=rtpmap:0 PCMU/8000
]]>
</send>
<recv response="100" optional="true"/>
<recv response="180" optional="true"/>
<recv response="200" rtd="true"/>
<send>
<![CDATA[
ACK sip:[service]@[remote_ip]:[remote_port] SIP/2.0
Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]
From: <sip:loadtest@[local_ip]>;tag=[call_number]
To: <sip:[service]@[remote_ip]:[remote_port]>[peer_tag_param]
Call-ID: [call_id]
CSeq: 1 ACK
Content-Length: 0
]]>
</send>
<pause milliseconds="48000"/>
<send retrans="500">
<![CDATA[
BYE sip:[service]@[remote_ip]:[remote_port] SIP/2.0
Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]
From: <sip:loadtest@[local_ip]>;tag=[call_number]
To: <sip:[service]@[remote_ip]:[remote_port]>[peer_tag_param]
Call-ID: [call_id]
CSeq: 2 BYE
Content-Length: 0
]]>
</send>
<recv response="200" optional="true"/>
</scenario>
压测命令分两段跑,先稳态再加阶跃,避免参数互相打架:
bash
# 第一段:稳态压到 60 路并发
sipp -sf robot_uac.xml -i 192.168.1.100 -p 5060 \
-m 60 -l 60 -r 5 \
-trace_err -trace_stat \
10.0.0.20:5060
# 第二段:从 60 路开始,每秒新增 2 路,最高 120 路
sipp -sf robot_uac.xml -i 192.168.1.100 -p 5060 \
-m 120 -l 120 -r 2 \
-trace_err -trace_stat \
10.0.0.20:5060
参数含义:-l 是最大并发,-r 是每秒新增呼叫数,-m 是总呼叫数。第二段 -r 2 配合 -l 120,从 60 到 120 路大约 30 秒完成爬坡,是阶跃式加压,不是瞬间打满。压的过程中看 -trace_stat 输出的 INVITE 成功率、平均建立时长,成功率跌破 99% 或建立时长 P95 超过 500ms,基本就是信令层到瓶颈了。
这里有个坑:SIPp 默认只发 RTP 包但不带真实语音内容,ASR 收不到有效音频,识别层压不到。所以 SIPp 只能测到媒体层,识别层要另做。
4.2 媒体层和识别层:真实音频回放压
识别层的压测要用真实音频喂给 ASR。思路是写一个并发脚本,开 N 个协程,每个协程模拟一路通话:建立 WebSocket 通道,按实时速率推送音频帧(每 20ms 一帧 160 字节的 PCMU),等 ASR 返回识别结果,记录首字延迟和完整结果。
Python 示意脚本:
python
import asyncio
import time
import wave
import websockets
ASR_WS = 'ws://asr-gateway:9000/stream'
AUDIO_FILE = 'sample_8k.wav'
CONCURRENCY = 80
async def one_call(call_id, sem, stats):
async with sem:
with wave.open(AUDIO_FILE, 'rb') as wf:
frames = wf.readframes(wf.getnframes())
frame_size = 160
chunks = [frames[i:i+frame_size] for i in range(0, len(frames), frame_size)]
async with websockets.connect(ASR_WS) as ws:
t0 = time.time()
first_result = None
for chunk in chunks:
await ws.send(chunk)
await asyncio.sleep(0.02) # 实时速率
try:
msg = await asyncio.wait_for(ws.recv(), timeout=0.01)
if msg and first_result is None:
first_result = time.time() - t0
except asyncio.TimeoutError:
pass
stats.append({'call_id': call_id, 'first_result': first_result,
'total': time.time() - t0})
async def main():
sem = asyncio.Semaphore(CONCURRENCY)
stats = []
tasks = [one_call(i, sem, stats) for i in range(CONCURRENCY * 2)]
await asyncio.gather(*tasks)
lat = [s['first_result'] for s in stats if s['first_result']]
lat.sort()
print(f'样本数={len(lat)} P50={lat[len(lat)//2]:.3f}s '
f'P95={lat[int(len(lat)*0.95)]:.3f}s P99={lat[int(len(lat)*0.99)]:.3f}s')
asyncio.run(main())
这个脚本只测 ASR 通道的识别延迟,不测完整对话链路。完整链路压测要把对话引擎、TTS 也串进去,用真实业务场景回放,观察端到端响应。我们当时分两步:先单独压 ASR 找到识别层上限,再串全链路压,看端到端劣化点比识别层上限低多少,差值就是对话引擎和 TTS 吃掉的余量。
4.3 关键指标和阈值参考
压测不是看“有没有报错”,是看指标在哪个并发点开始劣化。以下阈值基于本文第一节环境、200 通查件回访录音样本、统计周期一周的基线,具体数值请按自己系统调整:
| 指标 | 正常区间 | 劣化信号 | 不可接受 |
|---|---|---|---|
| ASR 首字延迟 P95 | < 300ms | 300—800ms | > 800ms |
| 端到端响应延迟 P95 | < 800ms | 800—1500ms | > 1500ms |
| 识别完整率 | > 97% | 95%—97% | < 95% |
| 打断响应时间 P95 | < 400ms | 400—800ms | > 800ms |
| RTP 丢包率 | < 0.5% | 0.5%—2% | > 2% |
| SIP INVITE 成功率 | > 99.5% | 99%—99.5% | < 99% |
拐点比绝对值更重要。 比如并发从 60 加到 80 路,ASR 首字延迟 P95 从 280ms 跳到 900ms,说明 70 路左右就是识别层拐点,生产环境要留 30% 余量,按 50 路规划。
五、高峰期会不会识别卡顿:六类根因排查
压测通过不代表高峰期一定不卡。生产环境变量更多,卡顿的根因通常在这六类里:
第一类:ASR 实例并发打满,请求排队。
表现是首字延迟随并发线性上升,CPU/GPU 利用率到 90% 以上。排查看 ASR 网关的队列长度和实例利用率,队列持续大于 0 就是实例不够,要么加实例,要么做请求分流。
第二类:音频预处理环节资源争抢。
降噪、VAD、重采样这些预处理如果和 ASR 主模型跑在同一台机器上,高峰期 CPU 争抢会导致音频帧堆积。排查看预处理线程的队列深度和 CPU steal 时间。我们踩过一次,VAD 线程和转码线程抢核,把 VAD 单独拆到独立进程绑核后就好了。
第三类:网络抖动导致 RTP 丢包。
高峰期带宽被打满或者网卡软中断不均,RTP 丢包会让 ASR 反复请求重传或补帧,表现出来也是卡顿。排查看 sar -n DEV 1 和网卡中断分布,丢包率超过 0.5% 就要查线路和网卡队列。
第四类:对话引擎同步阻塞。
有些对话引擎调外部接口(查订单、查物流)是同步的,高峰期外部接口变慢,整个对话链路被拖住,客户感知就是机器人“愣住不说话”。排查看对话引擎的接口调用耗时 P95,超过 500ms 的接口要做异步化或加缓存。
第五类:TTS 合成排队。
TTS 和 ASR 抢资源,或者 TTS 实例并发不够,机器人识别完了但说不出话,客户感知同样是卡。排查看 TTS 合成延迟和队列长度,和 ASR 分开部署。
第六类:模型冷启动和扩缩容滞后。
云上 ASR 按 CPU 触发扩容,但模型加载要几十秒,高峰期并发涨得比扩容快,中间这段时间就是卡顿窗口。排查看扩容触发到实例就绪的时长,提前做预热或者设最小实例数。
这六类里,第一类和第六类最常见。压测阶段把 ASR 队列长度和扩容就绪时长也纳入监控,比只看延迟数字有用。
六、压测报告怎么写,上线策略怎么定
压测做完要输出一份能指导上线的报告,至少包含:
-
分层承载结论:线路层、信令层、媒体层、识别层各自的安全并发上限。
-
拐点数据:每层从哪个并发点开始指标劣化,劣化斜率多陡。
-
瓶颈定位:拐点处哪个资源先打满,是 CPU、GPU、带宽还是队列。
-
长尾表现:P99 延迟和错误率,高峰期最怕的是长尾拖垮体验。
-
建议容量:生产按拐点的 70% 规划,留 30% 余量应对突发。
上线策略建议分三步:先灰度 10% 流量跑一周,对比压测预测和实际表现;再放到 50%,观察高峰期 ASR 队列和端到端延迟;最后全量。同时把限流和降级配好——识别层队列超过阈值时,新进来的通话走简化话术或转人工,别让队列无限堆积把整条链路拖死。
七、常见问题与避坑
Q1:压测用 SIPp 就够了,为什么还要单独压 ASR?
一句话结论:SIPp 只发 RTP 包不带真实语音,识别层压不到。SIPp 测线路和信令,真实音频回放测识别,两层都测才能对上生产表现。
Q2:压测并发数应该按峰值还是按平均值?
一句话结论:按峰值阶跃压,按拐点打折规划。用平均值压会高估承载能力,高峰期一来就露馅。
Q3:ASR 首字延迟多少算卡顿?
一句话结论:P95 超过 800ms 客户能明显感知到停顿,超过 1500ms 基本就是对话断片。300ms 以内体验接近真人,300—800ms 属于可接受但偏慢。
Q4:压测通过但高峰期还是卡,先查什么?
一句话结论:先看 ASR 请求队列长度和实例利用率。队列持续大于 0 说明实例不够或扩容滞后,这是高峰期卡顿最常见的原因。
Q5:自建压测环境成本高,有没有更省事的办法?
一句话结论:先压识别层再压全链路,别一上来就全链路压。识别层用开源 ASR 加回放脚本就能压,成本最低;全链路等识别层上限清楚了再串。如果自建压测和调优投入太大,选型阶段可以重点关注服务商能否提供明确的并发规格说明和扩容策略(如优音通信的平台文档中会标注单集群并发规格),把高峰期余量问清楚,比只看标称并发数有用。
Q6:限流降级会不会影响业务?
一句话结论:会,但比整条链路拖死好。限流阈值设在拐点的 80% 左右,触发后新通话走简化流程或转人工,保证已接入的通话不被拖垮。
八、方案边界
这套压测方法适合自建或半自建的语音机器人系统,单集群 200 路以内、ASR 实例数 10 个以内的规模,脚本和监控自己维护成本可控。如果并发规模到 500 路以上、多地域部署、ASR 和 TTS 都是第三方托管,压测重点要从“压系统”转向“压链路和配额”,重点确认各环节的并发配额和限流策略,自建 SIPp 压测的意义会下降。
另外压测数据只能反映压测环境的表现,生产环境的线路质量、网络路径、外部接口耗时都会让实际承载低于压测值。压测结论按 70% 打折用,比压测数字本身更重要。
九、小结
语音机器人怎么测并发,核心是分层拆:线路层和信令层用 SIPp 压,媒体层和识别层用真实音频回放压,看拐点不看绝对值,按拐点 70% 规划容量。高峰期识别卡顿的根因大多在 ASR 队列堆积、预处理资源争抢和扩容滞后这三类,压测阶段就把这些监控埋好,比上线后救火省事得多。
关键词:语音机器人并发测试、打电话机器人压测、AI 语音机器人压力测试、语音识别延迟、高峰期识别卡顿、ASR 压测、SIPp 压测、并发承载能力、端到端响应延迟、ASR 首字延迟、RTP 丢包、限流降级、WebSocket 音频回放
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐


所有评论(0)