直播场景里加一个"能听懂观众说话、实时回应"的 AI 主持人,听起来很酷,落地却很疼:音频采集延迟、双向流同步、模型推理卡顿、WebRTC 信令协商复杂……一个环节掉链子,用户体验就崩。AWS 最近给出了一个组合方案——Amazon Nova 2 Sonic 负责语音理解与生成,Kinesis Video Streams WebRTC 负责低延迟双向传输,两者拼起来刚好把最难的部分接住。
下面拆解这套架构的核心思路,并给出可以直接改造的代码示例。
系统架构:两条流,一个闭环
整体数据流只有一条主线:
观众麦克风 → WebRTC Audio Track → Signaling → Media Server
→ Nova Sonic (语音识别 + 语义理解 + 语音合成)
→ 合成音频回推 WebRTC → 观众扬声器
关键组件分工:
| 组件 | 职责 |
|---|---|
| KVS WebRTC | 信令协商、音频 Track 的收发,保证端到端延迟在百毫秒级 |
| Nova Sonic | 流式语音识别 → 文本 → 语义决策 → 流式语音合成,全链路流式处理 |
| Media Server(C++/Python) | 挂在 WebRTC Master 端,把收到的音频帧喂给 Nova Sonic,再把合成帧回推 |
Nova Sonic 的优势在于它把 ASR 和 TTS 做成了同一个模型内的流式管线,不需要中间文本缓冲,识别一段就能立刻开始合成,省掉了传统方案里"等一句话说完再处理"的延迟。
信令与连接:WebRTC 端的起步姿势
KVS WebRTC 提供了现成的信令通道(通过 AWS SDK 的 GetSignalingChannelEndpoint + SDP 交换),不需要自己搭 TURN/STUN 服务器。客户端只需要知道 Channel 名称和 AWS Region。
下面是一个最小化的 Python 信令 + 音频回推示例,展示 Master 端如何接入 WebRTC 并把音频帧转发给 Nova Sonic:
import boto3
import json
import asyncio
from amazon_kinesis_video_streaming_webrtc_client import (
KvsWebRTCClient,
SignalingClient,
)
# ---------- 1. 创建信令通道 ----------
kvs = boto3.client("kinesisvideo", region_name="us-east-1")
channel_name = "live-voice-demo"
# 确保通道存在(生产环境应提前创建)
try:
kvs.create_signaling_channel(
ChannelName=channel_name,
ChannelType="SINGLE_MASTER",
)
except kvs.exceptions.ResourceAlreadyExistsException:
pass # 通道已存在,继续
# ---------- 2. Master 端接入 WebRTC ----------
signaling_client = SignalingClient(
channel_name=channel_name,
region="us-east-1",
role="MASTER",
# 使用 IAM Role 或硬编码凭证(示例用后者,生产请用 Role)
access_key="AKIAIOSFODNN7EXAMPLE",
secret_key="wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
)
webrtc_client = KvsWebRTCClient(signaling_client=signaling_client)
# ---------- 3. 音频帧回调 → Nova Sonic ----------
nova_client = boto3.client("nova-sonic", region_name="us-east-1")
session_id = "demo-session-001"
# 启动 Nova Sonic 流式会话(伪接口,展示调用模式)
nova_session = nova_client.start_streaming_session(
SessionId=session_id,
InputFormat="pcm_16khz_mono", # WebRTC 默认音频格式
OutputFormat="pcm_16khz_mono",
VoiceId="ai-host-v1", # 选择合成音色
)
async def on_audio_frame(frame_bytes: bytes):
"""WebRTC 收到 Viewer 音频帧后的回调"""
# 喂给 Nova Sonic
nova_client.send_audio_chunk(
SessionId=session_id,
AudioData=frame_bytes,
)
# 从 Nova Sonic 拿合成音频(流式,非阻塞)
response = nova_client.receive_audio_chunk(SessionId=session_id)
if response and response.get("AudioData"):
# 回推给 WebRTC Viewer
webrtc_client.send_audio_frame(response["AudioData"])
# ---------- 4. 启动 ----------
async def main():
await signaling_client.connect()
await webrtc_client.start(on_audio_frame=on_audio_frame)
print("Master 端已启动,等待 Viewer 连接…")
# 保持运行
await asyncio.Event().wait()
asyncio.run(main())
运行前需要改动的地方: - 把
access_key/secret_key替换成你的 IAM 凭证,生产环境建议用 EC2 Instance Profile 或 ECS Task Role。 -nova-sonic的 SDK 接口名称可能随版本变化,请参照当前 AWS SDK 文档确认实际方法名。 -on_audio_frame的异步模式需要和 WebRTC Client 的回调机制对齐,KVS WebRTC C SDK 用回调函数,Python 封装层可能用 asyncio Queue,按实际封装调整。
Viewer 端:浏览器里收发音频
Viewer 端更简单——用浏览器 WebRTC API 直接连 KVS 信令通道,不需要额外 SDK:
// ---------- Viewer 端:浏览器 JavaScript ----------
const channelName = "live-voice-demo";
const region = "us-east-1";
// 1. 通过 KVS Signaling API 获取 ICE Server 配置和 SDP offer
// (实际项目中这一步通常由后端 API 代理,避免凭证暴露到前端)
const signalingUrl = `https://kvssignaling.${region}.amazonaws.com`;
const resp = await fetch(`/api/webrtc-signaling?channel=${channelName}`);
const { sdpOffer, iceServers } = await resp.json();
// 2. 创建 PeerConnection,加入 ICE servers
const pc = new RTCPeerConnection({ iceServers });
// 3. 添加本地麦克风 Track
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
stream.getAudioTracks().forEach(track => pc.addTrack(track, stream));
// 4. 接收远端(Master/Nova Sonic)音频
pc.ontrack = (event) => {
const remoteAudio = document.getElementById("remoteAudio");
remoteAudio.srcObject = event.streams[0];
remoteAudio.play();
};
// 5. SDP 交换
await pc.setRemoteDescription(new RTCSessionDescription(sdpOffer));
const answer = await pc.createAnswer();
await pc.setLocalDescription(answer);
// 把 answer 回传给信令服务
await fetch(`/api/webrtc-answer?channel=${channelName}`, {
method: "POST",
body: JSON.stringify({ sdpAnswer: answer.sdp }),
});
这段代码的核心逻辑就是:拿麦克风 → 加到 PeerConnection → SDP 协商 → 播放远端音频。前端不需要碰 Nova Sonic,所有 AI 处理都在 Master 端完成。
两个真实场景的适配思路
原文给出了两个落地场景,这里提炼适配要点:
场景一:AI 直播主持人
观众通过 WebRTC 语音提问,Nova Sonic 实时识别意图、调用知识库(可以用 Lambda + Bedrock 补充 RAG),再合成语音回答推回直播间。适配重点:
- 并发管理:多人同时提问时,每个 Viewer 建一条独立 WebRTC PeerConnection,Master 端按 session_id 区分 Nova Sonic 会话。
- 混音输出:如果需要把 AI 回答广播给所有观众(而非只回提问者),Master 端需要把合成音频混入直播推流的音频 Track,而非单独回推某条 PeerConnection。
场景二:实时语音客服
客户进页面后直接语音对话,Nova Sonic 充当客服大脑。适配重点:
- 对话状态管理:Nova Sonic 的 session 需要绑定客户 ID,跨轮次保持上下文。
- 降级兜底:WebRTC 连接断开时,自动切到 HTTP 轮询模式(用 Transcribe + Polly 组合),延迟升到 1-2 秒但保证不丢服务。
上手 Checklist
| 步骤 | 要点 |
|---|---|
| 创建 KVS Signaling Channel | 选 SINGLE_MASTER,Master 端跑在后端服务器 |
| IAM 权限 | Master 需要 kinesisvideo:* + nova-sonic:*,Viewer 端只走后端代理,不直接接触 AWS 凭证 |
| 音频格式对齐 | WebRTC 默认 OPUS 48kHz,Nova Sonic 目前接受 PCM 16kHz Mono,Master 端需要做一次解码 + 重采样 |
| 延端延迟测试 | 用 webrtc_client.get_stats() 看 audio round-trip time,目标 < 300ms |
| Nova Sonic 流式会话生命周期 | 每次 Viewer 连接创建一个 session,断开时调用 stop_streaming_session 释放资源 |
一句话总结:KVS WebRTC 解决"怎么把音频低延迟搬过去搬回来",Nova Sonic 解决"搬回来之后怎么让 AI 听懂并开口说",两者拼起来就是一条从观众嘴巴到 AI 嘴巴的闭环管线。动手起步只需要一个 Signaling Channel 和一台跑 Master 端的 EC2,建议先在单 Viewer 场景下把延迟调到满意,再扩展并发。