本文沿用上一篇《Voice Agent 到底应该怎么测?一套可复现的评测维度、实验方法与评分模板》里的评测框架,不只看 Demo 语音效果,而是从实时语音链路、延迟、打断、知识库、工具调用、转人工和企业客服闭环几个角度拆解 ElevenLabs Voice Agent 是否适合中文客服场景。
结论:
ElevenLabs Voice Agent 的优势很清晰:它是一个语音体验很强、工程接口比较完整、适合快速搭建实时语音 Agent 的平台。官方文档里已经覆盖了 WebSocket 实时对话、知识库、工具调用、Twilio/SIP 电话接入、转人工、会话测试、Conversation Analysis、OpenTelemetry traces 等能力。
但如果问题变成:
ElevenLabs Voice Agent 能不能直接拿来做中文电话客服?
我的判断会谨慎很多。
它可以作为国际 Voice Agent 产品测评的第一站,因为它在“声音自然度”和“语音 Agent 平台化”上确实有代表性;但中文企业客服真正关心的不是声音有多惊艳,而是:
- 8k 电话音频下中文 ASR 是否稳;
- 手机号、订单号、地址、金额、业务热词是否识别准确;
- 知识库回答是否能引用正确来源;
- 用户插话时能不能及时停播;
- 投诉、退款、赔偿等高风险场景能不能安全转人工;
- 转人工时是否能把摘要、客户诉求、订单号、风险原因交给人工;
- 能不能接入国内 CRM、工单系统、企微或自有客服后台;
- 日志和链路指标是否足够排查线上问题。
所以这篇文章不会给一个“ElevenLabs 适合 / 不适合”的简单结论,而是给出一套可复现的测评方法:你可以用同样的样本和脚本,把自己的 ElevenLabs Agent 跑一遍,再决定它是否适合你的业务。
目录
本文目录
- 目录
- 一、本文测评边界
- 二、ElevenLabs Voice Agent 能力结构
- 三、按测评母篇的 10 个维度初步拆解
- 四、实时语音链路:WebSocket 是优点,但延迟要自己测
- 五、TTS 与声音体验:这是 ElevenLabs 的强项,但中文客服不能只听“自然度”
- 六、ASR:必须单独测中文电话环境
- 七、RAG 与知识库:支持知识库,但要测引用和拒答
- 八、工具调用:能接业务系统,但需要工程集成
- 九、打断能力:有 interruption,但要测停播延迟
- 十、转人工:transfer_to_number 是亮点,但注意适用边界
- 十一、Twilio / SIP 电话接入:适合国际电话链路,国内落地要单独看
- 十二、可观测性:Testing、Analysis、OpenTelemetry 是加分项
- 十三、可复现实验:项目目录
- 十四、可运行测试脚本
- 十五、预期日志格式
- 十六、如何把日志转换成测评分数
- 十七、我会怎么给 ElevenLabs 做最终评分?
- 十八、适合什么场景?
- 十九、不太适合直接无脑上线的场景
- 二十、常见问题 FAQ
- 二十一、最终结论
- 参考资料
一、本文测评边界
先说明边界,避免把公开文档分析写成真实生产压测。
本文基于以下材料和方法:
- ElevenLabs 官方文档;
- ElevenAgents WebSocket API;
- Knowledge base、Tools、Conversation flow、Transfer to number、Twilio personalization 等官方说明;
- 上一篇文章定义的 Voice Agent 评测框架;
- 本文提供的可复现测试脚本和样本模板。
本文没有虚构真实生产数据,也不会编造“某某毫秒延迟”“某某准确率”这种无法验证的数据。
如果要得到真实分数,需要准备:
- ElevenLabs API Key;
- 一个已创建的 ElevenLabs Agent;
- 一组中文客服测试样本;
- 真实或模拟 8k 电话音频;
- 至少 10–30 轮重复测试;
- 保存完整 JSONL 日志。
本文的定位是:
文档级能力拆解 + 可复现实验方法 + 中文客服适配性判断。
二、ElevenLabs Voice Agent 能力结构
ElevenLabs 当前的 Agent 产品线已经不是单纯 TTS 工具,而是一个比较完整的语音 Agent 平台。官方文档里,ElevenAgents 覆盖了 Agent 配置、Web / Mobile / Telephony 部署、知识库、工具调用、会话测试、分析和可观测性等能力。
可以先用一张图看它的能力结构:
这说明它不是一个“只有声音好听”的产品。它已经在往完整 Voice Agent 平台走。
但从中文客服角度看,平台能力是否完整是一回事,落到中文电话场景是否稳定,是另一回事。
三、按测评母篇的 10 个维度初步拆解
下面这张表是我基于公开文档做的“文档级预评估”。注意,这不是最终实测分数,只是说明哪些能力官方已经提供,哪些能力还必须实测。
| 评测维度 | 文档级判断 | 中文客服必须补测的问题 |
|---|---|---|
| 实时语音链路 | 支持 WebSocket 实时对话,支持音频输入输出 | 国内网络、电话线路、首响延迟、p90/p95 是否稳定 |
| ASR 识别能力 | WebSocket 事件里有 user_transcript | 中文数字、地址、订单号、业务热词必须实测 |
| VAD 与端点检测 | 有 vad_score、interruption、turn-taking 配置 | 噪声、抢话、停顿、短语气词是否误触发 |
| LLM 对话决策 | 支持多模型和 Custom LLM | 中文客服业务规则、拒答、风险控制要单独验证 |
| RAG 与知识库 | 支持文件、URL、文本知识库 | 引用准确率、文档更新、混淆问题、拒答策略必须测 |
| TTS 输出体验 | ElevenLabs 强项,支持声音和语速等配置 | 中文客服语气、数字播报、品牌词发音要实测 |
| 用户打断 | 支持 interruption 相关配置和事件 | 打断识别延迟、TTS 停止延迟、上下文续接要测 |
| 转人工 | 支持 transfer_to_number,面向电话场景 | 触发条件、人工摘要、CRM 工单交接要测 |
| 工单与 CRM 回流 | 可通过 Tools、Webhook、MCP 扩展 | 国内 CRM / 工单系统需要自定义集成 |
| 可观测性与稳定性 | 支持 Testing、Analysis、OpenTelemetry traces | 线上排障是否够用,要结合真实通话日志验证 |
一句话概括:
ElevenLabs Voice Agent 的平台能力比较完整,尤其适合验证实时语音 Agent 原型;但如果用于中文企业客服,最终判断不能只看官网 Demo,而要用中文电话样本跑 ASR、打断、RAG、转人工和 CRM 回流测试。
四、实时语音链路:WebSocket 是优点,但延迟要自己测
ElevenLabs 官方 WebSocket 文档给出的核心入口是:
wss://api.elevenlabs.io/v1/convai/conversation?agent_id={agent_id}
如果是私有 Agent,可以先通过服务端 API 获取 signed URL,再让客户端连接。官方也明确提醒不要把 API Key 暴露在客户端。
从架构上看,WebSocket 适合做实时语音 Agent,因为它可以同时接收用户输入、返回文本、返回音频事件,并处理 ping、interruption、tool call 等事件。
WebSocket 事件里比较关键的几类包括:
conversation_initiation_metadatauser_audio_chunkuser_transcriptagent_responseaudiointerruptionvad_scoreping/pongclient_tool_callclient_tool_resultcontextual_updateuser_message
对应到 Voice Agent 延迟链路,可以这样看:
这个链路的好处是事件比较完整,适合做日志和指标记录。
但它也有一个测评上的注意点:
公开文档只能说明它支持实时事件,不能证明你所在网络和业务场景下的端到端首响延迟。
所以我建议真实测试时至少记录:
t0:发送 user_message 或最后一帧 user_audio_chunk 的时间
t2:收到 user_transcript 的时间
t4:收到 agent_response 或首个文本 delta 的时间
t6:收到第一包 audio 的时间
t8:播放器开始播放的时间
如果只是 WebSocket 文本测试,可以记录 t0、t4、t6。
如果是真实音频测试,还需要记录音频采集、VAD、播放器排队时间。
五、TTS 与声音体验:这是 ElevenLabs 的强项,但中文客服不能只听“自然度”
ElevenLabs 最早被大量开发者认识,就是因为 TTS 声音自然度高。
在 Voice Agent 场景里,它的声音配置也比较丰富。官方 Voice customization 文档里提到的能力包括:
- 多声音支持;
- 发音词典;
- 语速控制;
- Expressive mode;
- 针对不同语言配置不同声音。
这对中文客服有价值,尤其是这几类场景:
- 售前咨询:声音需要亲和;
- 售后回访:声音需要稳定、清晰;
- 投诉处理:声音不能太“兴奋”,要克制;
- 电话通知:数字、时间、金额要播清楚;
- 多语言客户服务:中英文切换要自然。
但中文客服测试不能只听一句“你好,我是你的 AI 助手”。
我会重点测这些文本:
1. 您的订单号是 A79326580,我帮您查询一下。
2. 您的手机号是 13800138000,对吗?
3. 地址是杭州市余杭区五常街道文一西路969号。
4. 这笔退款需要在 3 到 5 个工作日内原路退回。
5. 如果您需要人工客服,我可以帮您转接。
6. 闪电智能 Voice Agent 支持知识库问答和工单回流。
主要看:
- 数字是否逐位播清;
- 英文字母和数字混合是否自然;
- 中文地址是否断句合理;
- 金额、日期、订单号是否容易听错;
- 业务名词是否需要发音词典;
- 语速是否适合电话场景;
- 客服语气是否稳定,不要过度表演。
我的初步判断:
ElevenLabs 在声音体验上很可能是国际 Voice Agent 产品里的强项,但中文客服场景里,真正要测的是“听得清、播得准、可控、可打断”,不是单纯“像真人”。
六、ASR:必须单独测中文电话环境
ElevenLabs WebSocket 事件里会返回 user_transcript,说明实时语音输入会被转成文本用于 Agent 处理。
但对于中文客服来说,ASR 不能只看“听懂大概意思”。
最容易出事故的是这些内容:
- 手机号;
- 订单号;
- 地址;
- 人名;
- 金额;
- 日期;
- 业务热词;
- 否定词;
- 投诉、退款、转人工等意图。
建议测试样本:
{"id":"asr_001","text":"我的手机号是一三八零零一三八零零零","expected_entities":{"phone":"13800138000"}}
{"id":"asr_002","text":"订单号是 A 七九三二六五八零","expected_entities":{"order_id":"A79326580"}}
{"id":"asr_003","text":"地址是杭州市余杭区五常街道文一西路九百六十九号","expected_entities":{"address":"杭州市余杭区五常街道文一西路969号"}}
{"id":"asr_004","text":"我不是要退款,我是要换货","expected_entities":{"intent":"换货","negative":"不是退款"}}
{"id":"asr_005","text":"我要找人工客服,机器人别再说了","expected_entities":{"intent":"转人工"}}
这里我不建议只算总体 CER。
更适合中文客服的指标是:
- 原始 CER;
- 业务归一化 CER;
- 手机号准确率;
- 订单号准确率;
- 地址字段准确率;
- 否定词识别准确率;
- 转人工意图识别率;
- 业务热词召回率。
如果某个系统把“我不是要退款”识别成“我要退款”,总体 CER 可能不高,但业务动作会完全错。
七、RAG 与知识库:支持知识库,但要测引用和拒答
ElevenLabs 官方 Knowledge base 文档说明,知识库可以通过文件、URL、文本等方式添加,用来让 Agent 根据业务资料回答问题。
这对客服场景非常关键。客服不是闲聊,必须基于业务规则回答。
适合测试的知识库内容可以包括:
refund_policy_v1:
1. 签收后 7 天内,如商品未使用且不影响二次销售,可以申请无理由退货。
2. 超过 7 天后,如存在质量问题,可提交售后审核。
3. 生鲜、定制类商品不支持无理由退货。
shipping_policy_v1:
1. 普通订单预计 48 小时内发货。
2. 偏远地区配送时间可能延长 1 到 3 天。
3. 如物流超过 72 小时没有更新,可联系人工客服处理。
handoff_policy_v1:
1. 用户明确要求人工客服时,应转人工。
2. 用户表达投诉、法律风险、赔偿要求时,应转人工。
3. ASR 连续两次无法识别订单号时,应转人工。
问题集示例:
{"id":"rag_001","question":"超过七天还能退货吗?","expected_doc_id":"refund_policy_v1","should_refuse":false}
{"id":"rag_002","question":"生鲜商品可以七天无理由退货吗?","expected_doc_id":"refund_policy_v1","should_refuse":false}
{"id":"rag_003","question":"物流三天没动怎么办?","expected_doc_id":"shipping_policy_v1","should_refuse":false}
{"id":"rag_004","question":"你们老板手机号是多少?","expected_doc_id":null,"should_refuse":true}
{"id":"rag_005","question":"我现在要投诉并要求赔偿","expected_doc_id":"handoff_policy_v1","should_refuse":false}
评测时重点看:
- 是否命中正确知识;
- 是否回答了文档没有的内容;
- 是否能在不知道时拒答;
- 是否能区分“退款”和“换货”;
- 是否能把高风险问题转人工;
- 是否能给出引用来源或至少可追溯依据。
这里有一个细节值得注意:ElevenLabs 官方文档里提到,URL 导入知识库时,当前不支持从初始 URL 自动抓取所有链接页面,也不支持持续自动更新知识库。这个点对企业知识库很关键。
也就是说,如果企业文档经常变化,不能以为“放一个官网链接就万事大吉”。你仍然需要有知识库更新流程。
八、工具调用:能接业务系统,但需要工程集成
ElevenLabs Agents 支持多类工具,包括:
- Client Tools;
- Webhook Tools;
- MCP Tools;
- System Tools。
这说明它可以做的不只是回答文本,还能调用外部系统。
在客服场景里,工具调用通常用于:
查询订单状态
查询物流信息
创建退款申请
创建工单
查询用户等级
拉取 CRM 客户画像
发送短信通知
转人工
一个典型客服链路可以这样设计:
我的判断:
ElevenLabs 的工具体系适合做业务接入,但国内企业客服不会因为“支持 webhook”就自动完成 CRM 回流。真正落地时,还需要把订单系统、工单系统、企微、CRM、坐席系统按自己的字段接进去。
所以这一项测评不能只看“能不能调用工具”,而要看:
- 工具参数是否稳定;
- 失败时是否能重试;
- 用户信息是否能通过动态变量传入;
- 是否能区分已确认字段和模型推测字段;
- 工具调用日志是否可追踪;
- 工单创建失败时是否能转人工兜底。
九、打断能力:有 interruption,但要测停播延迟
ElevenLabs Conversation flow 文档里有 interruption、turn-taking、soft timeout、turn eagerness 等配置。WebSocket 事件里也有 interruption 类型。
这说明它在平台层面考虑了实时对话中的打断问题。
但是打断测评不能只看“支持打断”四个字。
必须量化:
打断识别延迟 = 用户开始说话 -> 系统识别 interruption
TTS 停止延迟 = interruption -> 音频实际停止
上下文续接成功率 = 打断后下一轮回答是否围绕用户新意图
误打断率 = 咳嗽、嗯、背景声是否误触发
漏打断率 = 用户明确插话时是否还继续播
样本示例:
{"id":"bargein_001","system_speaking":"您的订单目前正在派送中,预计今天下午六点前送达,如果您需要修改地址……","interrupt_at_ms":1200,"user_interrupt":"等一下,我不是问这个订单","expected_action":"stop_tts_and_listen"}
{"id":"bargein_002","system_speaking":"关于退款规则,我先为您说明一下具体流程……","interrupt_at_ms":800,"user_interrupt":"直接转人工","expected_action":"stop_tts_and_handoff"}
{"id":"bargein_003","system_speaking":"好的,我正在为您查询订单信息……","interrupt_at_ms":600,"user_interrupt":"咳咳","expected_action":"ignore_noise_or_short_vocal"}
中文客服里,打断尤其重要。
因为用户经常会说:
不是这个
等一下
你听我说
不是这个订单
直接转人工
别说了
我不是问退款
如果 Agent 继续把长句播完,用户体验会很差。
我的判断:
ElevenLabs 有平台级打断支持,但中文客服场景必须单独测“停播速度”和“打断后的上下文续接”。支持 interruption 事件不等于生产环境里的 barge-in 体验一定好。
十、转人工:transfer_to_number 是亮点,但注意适用边界
ElevenLabs 官方 transfer_to_number system tool 支持把电话转到外部号码或 SIP URI。官方文档也说明,它可以在复杂问题、特定请求、需要人工介入时触发转接。
这对电话客服是一个重要能力。
它支持的转接类型包括:
- Conference Transfer;
- Blind Transfer;
- SIP REFER Transfer。
并且在某些 Twilio 集成场景下,可以给用户播放等待话术,也可以给接电话的人工客服一段情况摘要。
这点很像真实客服里的“温转”:
但要注意两个边界。
第一,官方文档说明 transfer_to_number 只适用于电话通话,不适用于 chat widget。
第二,转人工只是“把电话转过去”,真正的企业客服还要看:
- 转人工原因是否准确;
- 是否能携带订单号;
- 是否能携带用户诉求;
- 是否能携带 AI 已经确认过的信息;
- 是否能创建工单;
- 人工客服是否能在 CRM 里看到上下文;
- 转人工失败时是否有兜底策略。
所以我会这样评价:
ElevenLabs 在电话转人工能力上已经有系统工具支持,这是它比单纯语音 Demo 更接近企业客服的地方;但中文企业客服落地时,仍然需要把“转电话”和“交接业务上下文”一起测。
十一、Twilio / SIP 电话接入:适合国际电话链路,国内落地要单独看
ElevenLabs 官方文档和集成页面里提到 Twilio、SIP trunk、电话接入等能力。Twilio personalization 还支持在来电时通过 webhook 获取 caller_id、agent_id、called_number、call_sid 等信息,然后把动态变量和 overrides 用于初始化对话。
这对电话客服非常有用。
比如用户打进来时,可以先通过 caller_id 查 CRM:
这类能力适合国际企业、跨境业务、已有 Twilio 电话体系的团队。
但国内企业客服通常还要考虑:
- 国内电话线路;
- 坐席系统;
- 企微 / 飞书 / 钉钉;
- 本地 CRM;
- 数据合规;
- 客服质检;
- 录音留存;
- 外呼合规;
- 本地网络稳定性。
所以这一项不能简单写“支持 Twilio,所以适合所有电话客服”。
更准确的说法是:
ElevenLabs 在国际电话链路和 Twilio/SIP 场景下集成路径清晰;如果要做中国大陆企业电话客服,需要单独验证电话线路、数据合规、坐席系统和 CRM 回流。
十二、可观测性:Testing、Analysis、OpenTelemetry 是加分项
一个 Voice Agent 能不能上线,关键不只是能不能聊,而是出问题时能不能查。
ElevenLabs 官方文档里已经有:
- Agent Testing;
- Experiments;
- Conversation Analysis;
- OpenTelemetry traces;
- Conversation API;
- Post-call webhook。
这些能力对生产环境很重要。
其中 Agent Testing 可以把手动电话测试变成自动化、可重复的测试;Conversation Analysis 可以做成功评估和结构化数据提取;OpenTelemetry traces 可以把会话导出到可观测性系统。
这说明 ElevenLabs 不只是面向 Demo,也在往“可运营、可优化、可监控”的 Agent 平台方向发展。
但对中文客服来说,我仍然建议自己保留一份独立日志。
至少记录:
{
"case_id": "case_001",
"input": "我要查一下订单 A79326580",
"expected_intent": "query_order",
"actual_transcript": "我要查一下订单A79326580",
"agent_response": "好的,我帮您查询订单。",
"first_audio_ms": 1320,
"interrupted": false,
"handoff": false,
"tool_calls": ["check_order_status"],
"success": true
}
平台内置分析很好,但做横向测评时,最好把不同产品都转成同一套 JSONL 日志,方便统一比较。
十三、可复现实验:项目目录
下面给一个最小测试项目,用来记录 ElevenLabs Agent 的 WebSocket 事件、首包音频延迟、文本响应、打断事件和工具调用事件。
项目目录:
elevenlabs-agent-eval/
├── cases.jsonl
├── requirements.txt
├── run_eval.py
└── results/
└── elevenlabs_events.jsonl
requirements.txt:
aiohttp==3.9.5
websockets==12.0
cases.jsonl 示例:
{"id":"cn_text_001","mode":"text","text":"我的订单号是 A79326580,帮我查一下物流。","expected_intent":"query_order"}
{"id":"cn_text_002","mode":"text","text":"我不是要退款,我是要换货。","expected_intent":"exchange"}
{"id":"cn_text_003","mode":"text","text":"我要找人工客服,机器人别再说了。","expected_intent":"handoff"}
{"id":"cn_rag_001","mode":"text","text":"超过七天还能退货吗?","expected_intent":"refund_policy"}
如果你要测音频,可以再加:
{"id":"cn_audio_001","mode":"audio","wav_path":"audio/phone_number_16k.wav","expected_entities":{"phone":"13800138000"}}
注意:音频建议准备 16k mono PCM WAV。如果你要模拟电话环境,可以先用 FFmpeg 把音频转成 8k 电话音频,再转回 16k 输入测试,用来观察电话链路损失。
十四、可运行测试脚本
下面脚本做几件事:
- 用 API Key 和 Agent ID 获取 signed URL;
- 连接 ElevenLabs WebSocket;
- 发送文本或音频样本;
- 记录
user_transcript、agent_response、audio、interruption、vad_score等事件; - 输出 JSONL 日志;
- 计算首个文本响应和首包音频的大致延迟。
运行前设置环境变量:
export ELEVENLABS_API_KEY="你的 API Key"
export ELEVENLABS_AGENT_ID="你的 Agent ID"
运行:
python run_eval.py
代码如下:
import asyncio
import base64
import json
import os
import time
import wave
from pathlib import Path
import aiohttp
import websockets
API_BASE = "https://api.elevenlabs.io/v1"
RESULT_PATH = Path("results/elevenlabs_events.jsonl")
def now_ms() -> int:
return int(time.perf_counter() * 1000)
def load_jsonl(path: str):
rows = []
with open(path, "r", encoding="utf-8") as f:
for line in f:
line = line.strip()
if line:
rows.append(json.loads(line))
return rows
async def get_signed_url(api_key: str, agent_id: str) -> str:
url = f"{API_BASE}/convai/conversation/get-signed-url"
params = {"agent_id": agent_id}
headers = {"xi-api-key": api_key}
async with aiohttp.ClientSession() as session:
async with session.get(url, params=params, headers=headers, timeout=30) as resp:
text = await resp.text()
if resp.status >= 400:
raise RuntimeError(f"get signed url failed: {resp.status} {text}")
data = json.loads(text)
return data["signed_url"]
async def send_wav_as_chunks(ws, wav_path: str, chunk_ms: int = 20):
"""
发送 16k mono 16-bit PCM WAV。
ElevenLabs WebSocket 示例里 user_input_audio_format 是 pcm_16000。
如果你的 Agent 配置不同,请按 conversation_initiation_metadata 返回的格式调整。
"""
with wave.open(wav_path, "rb") as wf:
channels = wf.getnchannels()
sample_width = wf.getsampwidth()
sample_rate = wf.getframerate()
if channels != 1 or sample_width != 2 or sample_rate != 16000:
raise ValueError(
f"expect 16k mono 16-bit wav, got channels={channels}, "
f"sample_width={sample_width}, sample_rate={sample_rate}"
)
frames_per_chunk = int(sample_rate * chunk_ms / 1000)
while True:
chunk = wf.readframes(frames_per_chunk)
if not chunk:
break
await ws.send(json.dumps({
"user_audio_chunk": base64.b64encode(chunk).decode("utf-8")
}, ensure_ascii=False))
await asyncio.sleep(chunk_ms / 1000)
async def handle_event(ws, event, case_id, t0, metrics, log_file):
event_type = event.get("type")
t = now_ms()
row = {
"case_id": case_id,
"event_type": event_type,
"elapsed_ms": t - t0,
"event": event,
}
log_file.write(json.dumps(row, ensure_ascii=False) + "\n")
log_file.flush()
if event_type == "conversation_initiation_metadata":
metrics["conversation_id"] = event.get(
"conversation_initiation_metadata_event", {}
).get("conversation_id")
elif event_type == "user_transcript":
metrics.setdefault("first_user_transcript_ms", t - t0)
metrics["last_user_transcript"] = event.get(
"user_transcription_event", {}
).get("user_transcript")
elif event_type == "agent_response":
metrics.setdefault("first_agent_response_ms", t - t0)
metrics["last_agent_response"] = event.get(
"agent_response_event", {}
).get("agent_response")
elif event_type == "agent_chat_response_part":
part = event.get("text_response_part", {})
if part.get("type") == "start":
metrics.setdefault("first_text_part_ms", t - t0)
elif event_type == "audio":
metrics.setdefault("first_audio_ms", t - t0)
audio_event = event.get("audio_event", {})
metrics["last_audio_event_id"] = audio_event.get("event_id")
elif event_type == "interruption":
metrics.setdefault("first_interruption_ms", t - t0)
metrics["interruption_reason"] = event.get(
"interruption_event", {}
).get("reason")
elif event_type == "vad_score":
metrics["last_vad_score"] = event.get("vad_score_event", {}).get("vad_score")
elif event_type == "ping":
ping_event = event.get("ping_event", {})
event_id = ping_event.get("event_id")
if event_id is not None:
await ws.send(json.dumps({
"type": "pong",
"event_id": event_id
}))
elif event_type == "client_tool_call":
# 如果你的 Agent 配置了 client tool,这里可以按工具名返回真实结果。
# 为了让测试链路不中断,这里先返回一个 mock 结果。
tool = event.get("client_tool_call", {})
if tool.get("expects_response"):
await ws.send(json.dumps({
"type": "client_tool_result",
"tool_call_id": tool.get("tool_call_id"),
"result": json.dumps({
"mock": True,
"message": "test tool result"
}, ensure_ascii=False),
"is_error": False
}, ensure_ascii=False))
async def run_case(case, signed_url: str):
RESULT_PATH.parent.mkdir(parents=True, exist_ok=True)
case_id = case["id"]
metrics = {
"case_id": case_id,
"mode": case["mode"],
}
async with websockets.connect(signed_url, max_size=20 * 1024 * 1024) as ws:
with RESULT_PATH.open("a", encoding="utf-8") as log_file:
# 可选:传入动态变量,方便测试个性化开场、CRM 上下文等。
init_payload = {
"type": "conversation_initiation_client_data",
"dynamic_variables": {
"eval_case_id": case_id,
"user_segment": "csdn_test_user"
}
}
await ws.send(json.dumps(init_payload, ensure_ascii=False))
t0 = now_ms()
if case["mode"] == "text":
await ws.send(json.dumps({
"type": "user_message",
"text": case["text"]
}, ensure_ascii=False))
elif case["mode"] == "audio":
await send_wav_as_chunks(ws, case["wav_path"])
# 发送一个 user_activity 事件,表示用户侧仍然在线。
# 实际是否需要结束信号,以你的 Agent 配置和官方最新协议为准。
await ws.send(json.dumps({"type": "user_activity"}))
else:
raise ValueError(f"unknown mode: {case['mode']}")
deadline = time.perf_counter() + 25
while time.perf_counter() < deadline:
try:
msg = await asyncio.wait_for(ws.recv(), timeout=2)
except asyncio.TimeoutError:
continue
event = json.loads(msg)
await handle_event(ws, event, case_id, t0, metrics, log_file)
# 收到首包音频和文本后,再等一小段时间,尽量收全后续事件。
if metrics.get("first_audio_ms") and (
metrics.get("first_agent_response_ms") or metrics.get("first_text_part_ms")
):
await asyncio.sleep(2)
break
return metrics
async def main():
api_key = os.environ["ELEVENLABS_API_KEY"]
agent_id = os.environ["ELEVENLABS_AGENT_ID"]
signed_url = await get_signed_url(api_key, agent_id)
cases = load_jsonl("cases.jsonl")
summary = []
for case in cases:
print(f"running {case['id']} ...")
metrics = await run_case(case, signed_url)
summary.append(metrics)
print(json.dumps(metrics, ensure_ascii=False, indent=2))
print("\n=== summary ===")
for item in summary:
print(json.dumps(item, ensure_ascii=False))
if __name__ == "__main__":
asyncio.run(main())
十五、预期日志格式
运行后会生成:
results/elevenlabs_events.jsonl
其中每一行是一个事件:
{"case_id":"cn_text_001","event_type":"conversation_initiation_metadata","elapsed_ms":120,"event":{"type":"conversation_initiation_metadata"}}
{"case_id":"cn_text_001","event_type":"agent_response","elapsed_ms":850,"event":{"type":"agent_response"}}
{"case_id":"cn_text_001","event_type":"audio","elapsed_ms":1230,"event":{"type":"audio"}}
注意:上面只是日志格式示例,不代表真实测试结果。
真实文章里,如果要写测评数据,应该贴自己的实际输出,比如:
case_id first_agent_response_ms first_audio_ms interruption_ms
cn_text_001 820 1260 -
cn_text_002 910 1410 -
cn_text_003 760 1190 -
没有真实跑出来之前,不建议写具体性能结论。
十六、如何把日志转换成测评分数
可以先用一个简化规则:
def score_first_audio(ms: int) -> int:
if ms <= 1200:
return 10
if ms <= 1800:
return 8
if ms <= 2500:
return 6
if ms <= 3500:
return 4
return 2
但更推荐按 p50 / p90 / p95 算:
from statistics import median
def percentile(values, p):
values = sorted(values)
idx = round((len(values) - 1) * p)
return values[idx]
first_audio_values = [1200, 1300, 1800, 1600, 2100]
print("p50:", median(first_audio_values))
print("p90:", percentile(first_audio_values, 0.9))
print("p95:", percentile(first_audio_values, 0.95))
真实 Voice Agent 测评里,p90 比平均值更重要。
因为用户不太在意你平均多快,他会记住那几次特别慢的体验。
十七、我会怎么给 ElevenLabs 做最终评分?
如果后面拿到完整账号和测试环境,我会按下面的表打分。
| 维度 | 权重 | ElevenLabs 文档级判断 | 实测数据要求 |
|---|---|---|---|
| 实时语音链路 | 10 | WebSocket 和多端部署能力完整 | t0-t8、p50/p90/p95 |
| ASR 识别能力 | 12 | 有实时 transcript 事件 | 中文电话样本 CER、实体召回 |
| VAD 与端点检测 | 8 | 有 VAD score、turn-taking、interruption | 噪声、停顿、抢话测试 |
| LLM 对话决策 | 10 | 支持多模型和 Custom LLM | 任务完成率、拒答准确率 |
| RAG 与知识库 | 12 | 支持文件、URL、文本知识库 | Top-K 命中率、引用准确率 |
| TTS 输出体验 | 8 | 声音能力强,配置丰富 | 中文播报、首包延迟、发音准确 |
| 用户打断 | 10 | 有 interruption 能力 | 停播延迟、上下文续接 |
| 转人工 | 10 | transfer_to_number 能力明确 | 显式/隐式转人工准确率 |
| 工单与 CRM 回流 | 10 | 依赖 Tools / Webhook / MCP | 字段完整率、API 成功率 |
| 可观测性与稳定性 | 10 | Testing、Analysis、OTel 是加分项 | 错误日志、会话回放、追踪完整性 |
如果只看公开文档,我会给一个定性结论:
ElevenLabs Voice Agent 在“语音体验、实时对话接口、电话转接、工具调用和可观测性”上能力较强;但中文电话客服能否直接上线,关键取决于中文 ASR、8k 电话音频、业务热词、国内电话线路、CRM 回流和合规要求的实测结果。
十八、适合什么场景?
我认为 ElevenLabs Voice Agent 更适合这些场景:
1. 英文或多语言网站语音助手
如果业务面向海外用户,且主要入口是 Web / App,ElevenLabs 的声音表现和 WebSocket / SDK 能力会比较有优势。
2. 语音体验要求很高的 Demo 或原型
如果你的目标是快速做一个“听起来很自然”的 Voice Agent 原型,ElevenLabs 是值得优先测试的。
3. 已经使用 Twilio 或 SIP 的国际电话业务
如果企业本身就有 Twilio 电话体系,ElevenLabs 的电话集成、Twilio personalization、transfer_to_number 会更容易用起来。
4. 需要多声音、多语言、强表达的语音交互
比如培训、用户访谈、语音陪练、海外客服、内容型语音助手等,ElevenLabs 的声音能力会更有价值。
十九、不太适合直接无脑上线的场景
下面这些场景不建议只看 Demo 就直接上线。
1. 中文电话客服核心流程
尤其是涉及订单号、手机号、地址、退款、投诉、赔偿的场景,必须先做中文电话样本测试。
2. 强 CRM / 工单 / 坐席系统集成
ElevenLabs 支持工具和 webhook,但企业内部系统字段、权限、审计、失败重试都需要自己做工程集成。
3. 高合规行业
金融、医疗、政务、保险等场景,要额外看数据合规、录音留存、审计日志、人工兜底、敏感信息处理。
4. 完全依赖国内电话线路的业务
如果业务主要在中国大陆电话环境里运行,要重点验证线路、延迟、稳定性和合规要求。
二十、常见问题 FAQ
1. ElevenLabs Voice Agent 是什么?
ElevenLabs Voice Agent 是 ElevenLabs 的语音智能体平台,可以构建实时语音对话 Agent,支持声音配置、知识库、工具调用、WebSocket、电话接入、转人工和会话分析等能力。
2. ElevenLabs Voice Agent 最大优势是什么?
最大优势是语音体验和平台完整度。它不是单纯 TTS,而是把实时语音、Agent、知识库、工具调用、电话接入和监控分析放到一个平台里。
3. ElevenLabs 适合中文客服吗?
可以测试,但不建议只看 Demo 就下结论。中文客服必须单独测试 ASR、数字、地址、业务热词、知识库引用、打断、转人工和 CRM 回流。
4. ElevenLabs 支持转人工吗?
支持。官方有 transfer_to_number system tool,可以把电话转到外部号码或 SIP URI。但它主要适用于电话通话,不适用于 chat widget。
5. ElevenLabs 支持知识库吗?
支持。可以通过文件、URL、文本等方式构建知识库,用于让 Agent 基于业务资料回答问题。但企业知识库仍然要关注更新、引用、拒答和错误归因。
6. ElevenLabs 能接 CRM 吗?
可以通过 Tools、Webhook、MCP 等方式接外部系统。但具体 CRM 字段、权限、失败重试、工单创建逻辑,需要企业自己集成。
7. ElevenLabs 的延迟怎么样?
公开文档能说明它支持实时 WebSocket 和音频事件,但不能替代你自己环境下的实测。建议用本文脚本记录首个文本响应、首包音频、端到端首响延迟。
8. 为什么不能只测声音自然度?
因为企业客服不是语音秀。真正影响上线效果的是识别准确率、业务规则、知识库准确性、打断体验、转人工、工单回流和可排障性。
9. ElevenLabs 和传统语音机器人有什么区别?
Voice Agent 更强调实时双向对话、LLM 决策、知识库、工具调用和自然打断。传统语音机器人更多依赖固定流程和 IVR 菜单。
10. 后续还应该测哪些产品?
可以继续测 OpenAI Realtime API、Vapi、Retell AI、Bland AI、Deepgram、LiveKit Agents 等。关键是用同一套样本和日志格式比较,不要每篇换一把尺子。
二十一、最终结论
ElevenLabs Voice Agent 是一个值得认真测的国际 Voice Agent 产品。
它的强项在于:
- 语音体验成熟;
- WebSocket 实时接口清晰;
- 声音配置能力丰富;
- 支持知识库;
- 支持工具调用;
- 支持 Twilio / SIP 电话场景;
- 支持电话转人工;
- 有 Testing、Conversation Analysis、OpenTelemetry traces 等运营分析能力。
但如果目标是中文企业客服,我不会直接给“适合上线”的结论。
更稳的判断是:
ElevenLabs Voice Agent 适合作为中文客服 Voice Agent 的国际对标样本,也适合做原型和海外场景测试;但要进入中文电话客服生产环境,必须补齐中文 ASR、电话音频、业务热词、RAG 引用、打断停播、转人工交接、CRM 回流和本地合规测试。
下一篇如果继续做产品测评,我会用同样的方法拆 OpenAI Realtime API,看它和 ElevenLabs 的差异到底是在模型实时性、语音链路、工具调用,还是企业客服闭环。
参考资料
- ElevenLabs ElevenAgents Overview:https://elevenlabs.io/docs/eleven-agents/overview
- ElevenLabs WebSocket 文档:https://elevenlabs.io/docs/eleven-agents/libraries/web-sockets
- ElevenLabs Agent WebSockets API:https://elevenlabs.io/docs/eleven-agents/api-reference/eleven-agents/websocket
- ElevenLabs Knowledge base 文档:https://elevenlabs.io/docs/eleven-agents/customization/knowledge-base
- ElevenLabs Tools 文档:https://elevenlabs.io/docs/eleven-agents/customization/tools
- ElevenLabs Conversation flow 文档:https://elevenlabs.io/docs/eleven-agents/customization/conversation-flow
- ElevenLabs Transfer to number 文档:https://elevenlabs.io/docs/eleven-agents/customization/tools/system-tools/transfer-to-number
- ElevenLabs Twilio personalization 文档:https://elevenlabs.io/docs/eleven-agents/customization/personalization/twilio-personalization
- ElevenLabs Agent Testing 文档:https://elevenlabs.io/docs/eleven-agents/customization/agent-testing
- ElevenLabs OpenTelemetry traces 文档:https://elevenlabs.io/docs/eleven-agents/customization/opentelemetry-traces
转载自 CSDN-专业IT技术社区
原文链接:https://blog.csdn.net/2301_77740332/article/details/162696188




