十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

LiveKit+阿里云NLS:构建实时语音交互助手的技术实践

LiveKit+阿里云NLS:构建实时语音交互助手的技术实践 做实时语音交互项目最头疼的往往不是你写业务逻辑而是底层那套“音视频管道”和“语音能力”怎么又快又稳地拼起来。我最近在阿里云 ECS 上把 LiveKit 和阿里云智能语音交互NLS完整打通做了一套支持实时双向语音的助手服务踩了不少坑也沉淀了不少经验。这个方案适合想自建实时语音应用、又希望语音识别和合成能力完全走国内服务的开发团队无论是做语音客服、会议转写、还是语音控制类应用都可以直接参考这套底座。先把结论放在前面LiveKit 负责 WebRTC 信令和媒体传输阿里云 NLS 负责语音识别ASR和语音合成TTS两者通过 LiveKit Agents 框架在服务端对接。客户端进房间、传音频、收音频走 LiveKit音频内容识别、文字转语音走阿里云。整个链路延迟在华东区域实测大概 300 到 500 毫秒稳定性足够支撑生产环境。1. 项目背景为什么是 LiveKit 加阿里云语音交互1.1 我到底想解决什么问题我的业务场景是一个面向特定行业用户的语音助手用户通过微信小程序和 App 直接说话系统需要把这句话转成文字、喂给大模型、再把模型回复用语音播出来。这是一个非常典型的“语音交互闭环”看起来简单真正做起来要处理音频采集、网络传输、回声消除、断句、流式返回一堆问题。音频采集和播放还好说难点在传输。WebRTC 是目前实时音视频事实标准但直接裸写 WebRTC 非常痛苦信令协商、ICE 穿透、弱网对抗这些底层细节足够拖垮一个团队。LiveKit 正好解决了这一层它开源、能自托管、有完整的 SDK 生态把 WebRTC 的复杂度封装得很好而且提供了 Agents 框架服务端可以订阅房间里的音频流做实时处理。对我来说这就是最合适的“管道层”。1.2 为什么语音能力选阿里云而不是其他方案语音识别和合成属于“能力层”国内几个云厂商都做得不错这次选阿里云主要考虑三点。第一中文识别准确率。阿里云 NLS 在中文普通话、带口音的中文、专业术语场景下表现稳定尤其是语音合成的声音自然度在同级别产品里很有竞争力。第二部署地域。既然 LiveKit 要部署在阿里云 ECS 上那语音服务也用阿里云同一个地域内网调用延迟极低还免去跨云调用的麻烦。第三生态完整。阿里云 NLS 提供实时语音识别、录音文件识别、语音合成长文本、一句话识别等多个接口一套 SDK 搞定接入成本低。1.3 这个方案的适用场景与边界这套方案不是只能做“语音助手”。LiveKit 本身是多房间多参与者的实时音视频基础设施阿里云 NLS 是纯语音能力服务二者组合后可以延伸出很多玩法在线课堂场景学生发言实时转写老师端看字幕。视频会议场景会后自动生成会议纪要会中多人语音区分转写。智能客服场景用户电话或 App 语音进线实时识别情绪和关键词。语音质检场景坐席通话内容实时转写触发违规词自动告警。当然也有边界。如果你是做超大并发、几十万人同时在线的音视频互动直播这套自托管 LiveKit 方案的规模化和成本需要重新评估如果只是简单“录一段音转文字”没必要上 WebRTC直接走阿里云录音文件识别更划算。选择技术栈之前一定先想清楚自己的传输规模和实时性要求。2. 整体架构设计与核心思路2.1 数据流转与组件职责整个系统分成四个核心组件客户端、LiveKit 服务端、LiveKit Agent、阿里云 NLS。客户端负责音频采集和播放LiveKit 服务端负责房间管理和媒体路由Agent 是跑在服务端的一段程序订阅房间里的音频流把 PCM 音频转给阿里云 NLS再把 NLS 返回的合成音频发布回房间。数据流是这样的客户端麦克风采集音频通过 WebRTC 推到 LiveKitAgent 在服务端订阅到这段音频流转发到阿里云 NLS 实时语音识别接口。识别出的文本文本进入大模型或其他业务逻辑得到回复文本后Agent 调用阿里云语音合成接口生成音频再通过 LiveKit 发布为一个音频轨客户端订阅这个轨就能播放。这个流程里客户端和服务端之间只有一条 WebRTC 连接所有语音能力都在服务端完成因此客户端不需要集成阿里云任何 SDK也不用担心 AccessKey 泄露。这是一个很重要的设计决策。2.2 为什么用 LiveKit Agents 而不是自己写网关LiveKit 官方提供了 Agents 框架底层思想是让开发者用一个简单的 Python 或 Node 程序就能处理房间里的实时音视频。我在最初设计时犹豫过两条路线一条是自己写一个 WebSocket 网关客户端直接把音频推过来然后我再去调阿里云另一条是用 LiveKit Agents。最后选了 Agents核心原因是“音视频传输这件事实在太难自己做”。WebRTC 的弱网对抗、回声消除、自动增益、降噪这些能力自己写不现实。LiveKit 的服务端内置了这些处理而且 Agent 可以复用同一套房间体系天然支持多用户并发不需要我单独维护一套音频网关集群。2.3 关键设计决策服务端对接还是客户端对接这里有一个很多团队会纠结的问题阿里云 NLS 的接入放在客户端还是服务端如果放客户端集成是简单但问题很多。AccessKey 或临时 Token 要下发到客户端有安全风险每次升级语音 SDK 要发版客户端客户端还要操心不同安卓机型、iOS 系统、小程序的音频格式兼容。服务端对接就干净很多客户端只负责“把声音传到位”识别、合成、并发管理全部集中在服务端出问题我只改一个 Agent 程序客户端完全不用动。所以我的建议很明确所有语音 AI 能力全部收归服务端客户端保持哑终端只做音频采集和播放。2.4 部署拓扑与资源规划整套环境我部署在阿里云华东 1杭州用了一台 4 核 8G 的 ECS系统选 Ubuntu 22.04。LiveKit 单独跑一个 Docker 容器Redis 也容器化Agent 用 systemd 托管。资源规划上4 核 8G 跑这套语音助手同时支持 20 路左右的实时语音会话是没问题的主要瓶颈在 Agent 进程的 CPU 和阿里云 NLS 的并发配额而不是 LiveKit 本身。网络方面客户端通过域名 wss 连接 LiveKitUDP 端口用于媒体传输。国内网络环境下只要选择地域离用户足够近延迟是可接受的。华东地区的用户实测网络抖动很小。3. LiveKit 服务端部署与配置实操3.1 服务器准备与基础环境我用的阿里云 ECS 是 Ubuntu 22.04新机器到手先把基础环境搞定。系统更新、装 Docker、装 docker-compose 插件这几步很常规但容易忽略尤其是 Docker 镜像源的问题。国内直接拉取 Docker Hub 镜像经常超时我建议先配置阿里云容器镜像加速器这样后续拉 LiveKit 镜像会快很多。安装 Docker 后我给服务端口留了足够的空间。LiveKit 默认需要这些端口TCP 7880 用于 HTTPS/WSS 信令UDP 50000 到 60000 用于 WebRTC 媒体传输单机部署至少要把这些端口在安全组放行。注意阿里云 ECS 的安全组规则分为入方向和出方向。入方向必须放行 7880 端口以及 WebRTC 的 UDP 媒体端口段。如果漏了 UDP 端口段会出现“进房成功但听不到声音”的典型故障。3.2 LiveKit 配置文件与密钥生成LiveKit 需要一个配置文件我把它放在/opt/livekit/livekit.yaml。核心配置项包括port、rtc、keys、redis等。需要特别注意LiveKit 的keys字段是访问 API 的凭证这是一个 API Key 和 Secret 的映射关系。生成密钥用openssl rand -base64 32即可但要注意 Secret 必须足够随机不要用弱口令。我用的最小配置如下port: 7880 bind_addresses: - rtc: tcp_port: 7881 port_range_start: 50000 port_range_end: 60000 use_external_ip: true enable_loopback_candidate: false redis: address: localhost:6379 db: 0 keys: APIKey: secret: 这里替换成openssl随机生成的字符串 logging: level: infouse_external_ip: true很关键它让 LiveKit 自动识别 ECS 的公网 IP并把这个 IP 写入 ICE 候选地址。如果不开启客户端在公网环境下可能拿不到可用的候选地址对导致无法建立 P2P 连接。3.3 使用 Docker 启动 LiveKit 服务LiveKit 官方提供了 Docker 镜像livekit/livekit-server我用 docker-compose 管理。同时把 Redis 也一起编排进去同机部署省去外部依赖。version: 3.9 services: redis: image: redis:7-alpine container_name: livekit-redis restart: always ports: - 6379:6379 livekit: image: livekit/livekit-server:latest container_name: livekit-server restart: always ports: - 7880:7880 - 7881:7881 - 50000-60000:50000-60000/udp command: --config /etc/livekit.yaml volumes: - /opt/livekit/livekit.yaml:/etc/livekit.yaml启动命令就一条docker compose up -d。启动后查看日志确认没有报错然后检查 7880 端口是否正常监听。我习惯再用curl探测一下 LiveKit 的 HTTP 接口看看服务是否响应。3.4 Token 生成客户端接入的核心客户端要进房间必须携带 LiveKit 签发的 JWT Token。这部分逻辑放服务端客户端根据用户名和房间名动态获取。Token 里要声明video权限否则客户端无法发布和订阅音视频轨。用 Go 写一个简单的签名服务核心代码如下import ( github.com/livekit/protocol/auth github.com/livekit/protocol/livekit ) func generateToken(apiKey, apiSecret, room, identity string) (string, error) { at : auth.NewAccessToken(apiKey, apiSecret) grant : auth.VideoGrant{ RoomJoin: true, Room: room, } at.AddGrant(grant). SetIdentity(identity). SetValidFor(time.Hour) return at.ToJWT() }注意identity必须在同一个房间内唯一如果有两个相同 identity 的客户端进同一房间前一个会被踢掉。这个坑我踩过当时测试时用了固定账号结果总是“无规律掉线”。3.5 客户端快速验证连接服务端起来之后先用 LiveKit 官方示例客户端做连通性验证要比直接写业务代码高效得多。我用官方那个 “rooms” 示例页面在本地浏览器里配置服务器地址和 Token能正常看到摄像头和麦克风画面就说明信令、媒体通道、ICE 都通了。这一步验证完再进入 Agent 开发。4. 阿里云语音交互服务集成与 Agent 开发4.1 阿里云 NLS 开通与鉴权准备阿里云智能语音交互的接入流程我走的是“创建项目 → 创建 AccessKey → 获取 AppKey → 部署”。登录阿里云控制台在产品列表里找到智能语音交互开通服务后创建一个项目。项目创建完会分配一个 AppKey这个 AppKey 是调用语音服务必须的标识。AccessKey 最好使用阿里云 RAM 子账号创建权限只授予调用智能语音交互所需要的权限并启用 2FA降低泄露风险。AccessKey 不要写在代码里我一般存在环境变量里Agent 启动时读取。4.2 实时语音识别接入流式处理音频阿里云 NLS 的实时语音识别通过 WebSocket 协议通信支持流式发送音频分片返回识别结果。客户端把麦克风采集的音频推给 LiveKitAgent 从音轨里拿到的是 48kHz 的 PCM 数据但阿里云 NLS 实时识别接口支持多种采样率我统一在 Agent 里重采样成 16kHz 单声道再发送。Python 端核心代码片段import nls from nls import SpeechRecognizer def on_result(result, *args): # 拿到识别结果文本 text result[payload][result] print(识别结果:, text) recognizer SpeechRecognizer( tokentoken, appkeyappkey, on_resulton_result, ) recognizer.start(audio_formatpcm, sample_rate16000, enable_intermediate_resultTrue, enable_punctuation_predictionTrue) # 持续送入音频帧 recognizer.send_audio(frame_bytes) # 结束时 recognizer.stop()enable_intermediate_resultTrue可以拿到增量识别结果适合做字幕效果enable_punctuation_predictionTrue会输出带标点的文本对后续大模型处理很有帮助。如果对延迟敏感可以把enable_semantic_sentence_detection打开服务端会根据语义断句更快回调最终结果。4.3 语音合成接入流式返回音频实时语音合成我用的同样是 WebSocket 接口传入文本服务端返回合成的音频数据有增量回调。把文本逐句发给合成接口返回的音频流再写入 LiveKit 的音频轨用户就能实时听到语音回复。from nls import SpeechSynthesizer def on_data(data, *args): # data 是音频二进制数据 audio_stream.write(data) synthesizer SpeechSynthesizer(tokentoken, appkeyappkey, on_dataon_data) synthesizer.start(text你好我是语音助手, voiceaixia, formatwav, sample_rate16000, volume50, speech_rate0)voice参数决定音色可以根据业务场景选择。这里要注意format和sample_rate必须和播放端兼容我统一用 16kHz 单声道 wav方便 Agent 直接复用不做额外转码。4.4 用 LiveKit Agents 把两端串起来LiveKit Agents 是这套集成的粘合剂。Agent 进程启动后监听一个或多个房间收到参与者发布音轨的事件后启动一个会话任务。这个任务里创建一个音频源AudioSource把阿里云合成的音频数据喂进去再从房间订阅音频轨AudioTrack把音频帧取出来送给实时识别接口。为了简洁我只画一下事件流的伪代码结构import asyncio from livekit import agents from livekit.agents import AutoSubscribe, JobContext, WorkerOptions, cli from livekit.plugins import silero async def entrypoint(job: JobContext): await job.connect(auto_subscribeAutoSubscribe.AUDIO_ONLY) participant await job.wait_for_participant() # 订阅参与者音频 audio_stream agents.audio.AudioStream( job.room, participantparticipant ) # 创建合成音频输出 audio_source agents.audio.AudioSource(16000, 1) await audio_source.start() # 把音频源发布为房间音轨 track agents.audio.AudioTrack(audio_source) await job.room.local_participant.publish_track(track) # 开始转发音频流 - 阿里云识别文本 - 大模型 - 合成 - audio_source await forward_audio(audio_stream, audio_source, participant) if __name__ __main__: cli.run_app(WorkerOptions(entrypoint_fncentrypoint))siler插件用来做 VAD语音活动检测可以自动判断说话人是否说完一句话避免把静音段发去识别减少无意义的识别请求也能让对话节奏更自然。4.5 打通链路后的最小验证Agent 写好之后我用两个客户端做端到端测试一个客户端说话一个客户端收听合成回复。这一步主要验证“进房 → 订阅音轨 → 识别 → 合成 → 发布音轨”整条链路。我遇到过几个问题比如 Agent 重复发布音轨导致回声、识别端返回结果延迟等都是在这一步暴露出来的。链路通了之后再接入大模型或者其他业务逻辑就简单很多。核心是把识别文本送到业务侧拿到回复文本再调用合成业务代码和音视频层完全解耦。5. 中国大陆部署的实践要点与性能优化5.1 地域选择就近原则是第一优先级语音交互对延迟极其敏感从用户嘴里说出一句话到听到回复这里面每一跳都算时间。LiveKit 服务器放在华东华北的客户端延迟就会明显增加。所以我的建议是初期业务用户集中在哪个区域就把 LiveKit 部署在哪个区域的阿里云 ECS 上等业务扩大了再考虑多区域部署并用 LiveKit 的节点选择机制。阿里云 NLS 本身在国内有多个地域的服务入口选择与 ECS 同地域的接入点最稳妥走内网通路。这里有个细节阿里云 NLS 的 WebSocket 接入地址是wss://nls-gateway-{region}.aliyuncs.com比如华东就是wss://nls-gateway-cn-shanghai.aliyuncs.com。5.2 安全组与防火墙配置的正确姿势这个章节我必须单独拿出来说因为真的太容易错了。LiveKit 的端口分配我前面提过安全组规则必须同时放行 TCP 和 UDP。有个容易忽略的点是LiveKit 服务器所在的安全组不仅要放行入方向还需要确认出方向没有被限制。如果出方向策略是默认放行那就没问题如果有白名单策略必须放行到阿里云 NLS 的 WebSocket 地址和端口否则 Agent 进程一直能启动但一调用语音服务就超时。客户端侧还有一个常见问题企业内网或部分运营商网络屏蔽了 UDP 媒体端口。这个场景下可以把 LiveKit 配置为强制走 TCP 中继虽然会牺牲一部分延迟但能换来连接成功率。5.3 音频参数调优从 48kHz 到 16kHz 的细节LiveKit 从麦克风采集到的音频通常带内回声和噪声Agent 端可以做处理但实时识别对干净度有要求。我在 Agent 里对音频做了几个处理重采样到 16kHz单声道应用高通滤波器去除低频噪声做了自动增益均衡。这些都是基础 DSP 操作不用做得太激进太激进反而可能损伤语音特征。延迟优化上最关键的是不要缓冲太多音频再发去识别。WebRTC 本身有抖动缓冲我的经验是 20 到 40 毫秒一个音频包直接发送不要在 Agent 里再攒一个大的 PCM 包。攒包越多延迟越高而且识别结果刷新更慢。5.4 成本权衡与并发规划实时语音识别的计费是按音频时长算的合成也是按字符数算的。成本大头在识别时长尤其是用户一直开着麦克风、静音也在计费。解决办法就是上一节提到的 VAD检测到有人说话才把音频送识别停顿不送能省下 30% 到 50% 的识别费用。这一条对一个日活十万用户的产品来说每年省下的钱非常可观。服务器成本方面LiveKit 单节点 4 核 8G 就能支撑中小规模业务初期完全没必要上集群。等到单节点撑不住了再考虑横向扩容LiveKit 本身是支持多节点部署的。6. 常见问题与排查技巧实录6.1 问题速查表我把这段时间遇到的典型问题整理成一张表同样的问题可以直接对着查现象可能原因解决方案客户端进不了房间报 401Token 无效或过期检查 API Key/Secret 是否匹配确认 Token 的 room 和 identity 正确过期时间是否设置合理能进房间但看不到画面LiveKit 公网 IP 配置错误检查use_external_ip是否开启确认安全组放行 UDP 媒体端口进房成功但听不到声音媒体端口被防火墙阻断检查安全组 50000-60000 UDP 端口尝试开 TCP 强制中继Agent 订阅不到音轨权限不足或订阅模式错误检查 Agent 的 AutoSubscribe 配置确认当前房间参与者已发布音轨阿里云识别结果长时间不返回网络不通或鉴权失败检查 AccessKey、AppKey 配置用官方 SDK 独立测试 NLS 连接识别文本全是乱码采样率或编码格式不匹配确认发送给 NLS 的音频是 16k PCM确认 NLS 接口的音频格式参数一致对话有明显回声播放的音轨又被采集进麦克风检查播放端降噪和 AEC 设置Agent 端可以把发布和订阅的音频流做隔离处理合成音频播放卡顿网络抖动或合成音频数据包缓冲不足适当增加播放端 jitter buffer检查 ECS 带宽是否被打满6.2 排查思路先分层再定位音视频链路问题最大特点是“现象在客户端根因在服务端”新手很容易一头扎进客户端代码里翻。我的排查顺序很简单先看客户端能不能进房不能进房看信令能进房再看媒体通不通媒体通了再看音频流进没进 AgentAgent 收到了再查识别和合成。每一层都有对应日志所以代码里一定要打够日志尤其是 Agent 的音频接收和 NLS 请求日志关键时刻就是救命稻草。阿里云 NLS 的官方控制台也提供调用日志和错误码查鉴权和配额问题非常方便。出现41000001这类错误码时通常是访问令牌失效刷新 Token 重连即可。6.3 经验补充Agent 稳定性与重连机制语音助手这类应用服务端 Agent 必须保证 7x24 小时稳定运行。我踩过一个坑Agent 进程偶发性崩溃重启后与 LiveKit 房间的连接状态没清理干净导致新会话异常。后来我加了两个保险一是 systemd 守护进程自动拉起二是在 Agent 启动时强制断开之前所有旧连接并清理 Redis 中的会话状态。另外断网重连也是必须处理的。WebRTC 的 ICE 重连比较成熟但阿里云 NLS 的 WebSocket 断线后需要重新建立会话这里要做一个指数退避重连策略避免网络抖动恢复后所有客户端同时重连打爆服务端。6.4 生产环境的监控与告警建议基础监控至少覆盖三个维度LiveKit 服务端的在线人数、CPU、带宽Agent 进程的内存和语音请求成功率阿里云 NLS 账号的调用量和错误码分布。有了这些指标才谈得上提前发现容量问题而不是等用户投诉了才被动排查。如果团队没有现成监控设施先用 node_exporter 加 Prometheus 对付一阵后面再接 Grafana 出面板。7. 一些实际的补充和未来扩展方向这套架构搭好之后扩展性其实是很强的。LiveKit 不仅能管语音还能管视频阿里云也有视觉相关的 AI 能力如果业务升级成“音视频多模态助手”底层不用大改。想要接大模型直接在 Agent 里把 NLS 识别出来的文本发给大模型再把返回文本走合成即可这一层我已经在实际项目中接入了大模型效果很稳。另外一个方向是录音文件识别和离线转写。如果用户声音不是实时对话而是事先录制好的音频可以在 Agent 里把音轨存成文件事后调用阿里云录音文件识别接口做异步转写成本比实时识别低很多。这套逻辑作为离线队列任务跑就行不影响实时链路。最后给一个最实在的建议先从最小闭环跑通再上功能。先把 LiveKit 部署好、用一个客户端连通、用阿里云官方 SDK 转一段音频把每一步的日志和数据流都吃透再往里面加 Agent 和大模型。很多团队一上来就想全链路一步到位结果出了问题到处查反而更慢。整个集成踩下来我的体会是 LiveKit 的文档和社区资料足够充分阿里云 NLS 的 SDK 也很成熟真正的复杂度不在某个单独环节而在把两个生态的“时钟”和“格式”对齐。只要把音频采样率、鉴权、并发和重连这四个核心点处理好这套方案能稳稳跑在生产环境里。
返回列表