音视频推流完整交互协议
1. 能力边界
方向 | 已开放的报文 |
平台 → 开发者系统 |
|
开发者系统 → 平台 | SSE |
2. 接入方式
开发者须提供以下两个 API
接口 | 是否必需 | 接收请求 | 响应 |
会话流 API( | 必需 |
|
|
会话事件 API( | 非必需 |
|
|
接口地址、鉴权信息等通过开放平台的API插件完成接入
3. 交互流程
4. 会话交互
本章先说明平台请求的通用信封格式,再按一次会话的生命周期顺序说明各报文的交互细节:建立会话(SESSION_START)→ 返回结果(output)→ 播报(SPEAK)→ 停止播报(STOP_OUTPUT)→ 关闭会话(SESSION_CLOSE)。
4.1. 请求信封
平台使用 POST 和 application/json 发送请求:
{
"specVersion": "1.0",
"requestId": "req_001",
"sessionId": "ses_001",
"type": "SESSION_START | SESSION_CLOSE",
"occurredAt": "2026-08-06T10:00:00+08:00",
"payload": {}
}字段 | 类型 | 是否必填 | 含义 |
| string | 必填 | 协议版本,当前固定 |
| string | 必填 | 请求幂等键;同一次重试保持不变 |
| string | 必填 | 本次实时会话的唯一标识 |
| string | 必填 |
|
| string | 必填 | RFC 3339 时间 |
| object | 必填 | 事件负载,结构随 |
4.2. 建立会话:SESSION_START
流向:平台 -> 开发者系统
4.2.1. 请求
POST /audio-realtime/session HTTP/1.1
Content-Type: application/json
Accept: text/event-stream
X-Request-ID: req_start_001{
"specVersion": "1.0",
"requestId": "req_start_001",
"sessionId": "ses_001",
"type": "SESSION_START",
"occurredAt": "2026-08-06T10:00:00+08:00",
"payload": {
"entryContext": {
"source": "VOICE_SKILL",
"rawUtterance": "进入语音闲聊"
},
"rtc": {
"provider": "ALIYUN_RTC",
"channelId": "channel_001",
"consumerUserId": "third_party_consumer_001",
"consumerToken": "<RtcRoom.thirdToken>",
"expiresAt": "2026-08-07T10:00:00+08:00"
}
}
}payload.rtc 字段说明:
字段 | string | 是否必填 | 说明 |
| string | 必填 | 当前固定为 |
| string | 必填 | RTC 房间标识 |
| string | 必填 | 开发者服务加入 RTC 时使用的用户标识 |
| string | 必填 | 与本次房间和 consumer 匹配的临时凭证 |
| string | 必填 | 凭证过期时间,RFC 3339 格式 |
实现要求:
必须同时使用
channelId、consumerUserId和consumerToken入会,不要从 token 推断 userId;RTC consumer 准备失败时,在提交 SSE
200前返回错误;同一 START 重试不得创建第二个 RTC consumer、第二条 SSE 或第二份业务会话。
4.2.2. 响应
HTTP/1.1 200 OK
Content-Type: text/event-stream
Cache-Control: no-cache开发者服务应先完成 RTC consumer 的初始化和入会准备,再返回 SSE 200。平台确认 SSE 就绪后才会启动设备推流;过早返回 200 可能导致开头音频丢失。
4.2.3. 保活
SSE 空闲期间建议每 30 秒发送一次注释保活:
: keep-alive平台默认连续 90 秒未收到完整的 SSE 帧时会关闭会话。保活帧和 output 帧都必须以空行结束。当前不支持 SSE 续连、Last-Event-ID 或消息重放;连接断开后应释放资源,由用户重新进入会话。
4.3. 返回结果:output
流向:开发者系统 -> 平台
平台与会话流 API 建立的 SSE 只接受事件名为 output 的事件:
event: output
data: {"specVersion":"1.0","sessionId":"ses_001","actions":[],"final":false}字段 | 类型 | 是否必填 | 说明 |
| string | 必填 | 当前固定为 |
| string | 必填 | 必须与当前 SSE 会话一致 |
| array | 必填 | 按数组顺序执行,只允许 |
| boolean |
|
每个 Session 只能有一个 SSE writer。RTC、ASR、模型和定时任务产生的结果应先进入同一个串行队列,再写入 SSE,避免乱序、旧回复迟到或关闭后继续写入。
4.3.1. 播报Action:SPEAK
流向:开发者系统 -> 平台
{
"type": "SPEAK",
"payload": {
"speechId": "speech_001",
"index": 1,
"hasNext": true,
"text": "我先简单介绍一下,",
"textDisplayMode": "APPEND_HIGHLIGHT"
}
}字段 | 类型 | 是否必填 | 说明 |
| string | 必填 | 一次逻辑播报的唯一标识,在 Session 内不可复用 |
| integer | 必填 | 同一 |
| boolean | 必填 | 最后一片为 |
| string | 必填 | 非空 UTF-8 纯文本,不支持 SSML 或 Markdown |
| string |
|
同一播报必须满足以下规则:
单片播报固定为
index=1, hasNext=false;多个
speechId不能交错发送;同一
speechId的textDisplayMode必须保持一致;相同
speechId + index的重复帧必须内容完全相同,平台只消费一次;hasNext=false只表示该播报的文本输入结束,不表示音频已经播放完成;已完成或已取消的
speechId不能用于新的回复。
多片播报示例:
event: output
data: {"specVersion":"1.0","sessionId":"ses_001","actions":[{"type":"SPEAK","payload":{"speechId":"speech_001","index":1,"hasNext":true,"text":"李白是唐代诗人,","textDisplayMode":"APPEND_HIGHLIGHT"}}],"final":false}
event: output
data: {"specVersion":"1.0","sessionId":"ses_001","actions":[{"type":"SPEAK","payload":{"speechId":"speech_001","index":2,"hasNext":false,"text":"以浪漫奔放的诗风闻名。","textDisplayMode":"APPEND_HIGHLIGHT"}}],"final":false}使用 APPEND_HIGHLIGHT 时,同一份 text 会在支持该能力的设备上随播报追加并高亮;不支持展示的设备仍会正常播报。NONE 或缺省表示只播报。开发者不需要判断设备是否有屏。
4.3.2. 停止当前播报Action:STOP_OUTPUT
流向:开发者系统 -> 平台
{
"type": "STOP_OUTPUT",
"payload": {
"scope": "SPEAK",
"reason": "BARGE_IN"
}
}scope必填,当前固定为SPEAK;reason可选,仅用于说明停止原因;没有活跃播报时,STOP 按幂等空操作处理;
STOP 后的新回复必须使用新的
speechId,并从index=1开始。
需要用新回复打断旧播报时,将 STOP 和新回复首片放在同一个 output 中,并保证 STOP 在前:
event: output
data: {"specVersion":"1.0","sessionId":"ses_001","actions":[{"type":"STOP_OUTPUT","payload":{"scope":"SPEAK","reason":"BARGE_IN"}},{"type":"SPEAK","payload":{"speechId":"speech_002","index":1,"hasNext":false,"text":"三加三等于六。"}}],"final":false}不要在同一回复的每个分片前重复发送 STOP。新播报开始后才到达的 STOP 也会停止新播报,因此必须由单 writer 保证顺序,并丢弃被新一轮对话替换的旧任务结果。
4.4. 关闭会话:SESSION_CLOSE
流向:平台 -> 开发者系统
配置了会话事件接口时,平台会发送:
{
"specVersion": "1.0",
"requestId": "req_close_001",
"sessionId": "ses_001",
"type": "SESSION_CLOSE",
"occurredAt": "2026-08-10T10:30:00+08:00",
"payload": {
"reason": "USER_EXIT"
}
}开发者系统应返回 ACK:
{
"specVersion": "1.0",
"sessionId": "ses_001",
"requestId": "req_close_001",
"status": "RECEIVED"
}ACK 只表示通知已接收,不用于承载结束语。
5. 错误处理
SSE 建立前,可使用标准 HTTP 状态码返回错误:
{
"specVersion": "1.0",
"requestId": "req_001",
"error": {
"code": "INVALID_REQUEST",
"message": "sessionId is required",
"retryable": false
}
}HTTP 状态码 | 建议场景 |
| JSON、协议版本或 RTC 参数不合法 |
| 鉴权失败或无访问权限 |
| Session 不存在 |
| 幂等内容冲突或 Session 状态冲突 |
| 服务限流 |
| 服务暂时不可用 |
SSE 建立后无法再修改 HTTP 状态。仍可继续写流时,可发送一条简短提示并以 final=true 结束;无法继续写流时应断开连接,并确保所有关联资源被释放。
6. 推荐实现结构
建议为每个 Session 维护独立状态对象,至少包含:RTC consumer、ASR 流、当前模型任务、当前 speechId/index、串行 SSE writer、心跳任务和关闭状态。
核心处理原则:
收到 START 后先校验和幂等占位,再准备 RTC consumer;
RTC 准备成功后返回 SSE
200,启动心跳和音频处理;ASR 产生有效终句后启动对话任务;新一轮有效输入使旧任务结果失效;
模型文本按句读流式切片,所有 Action 进入单 writer;
明确退出、
final=true、CLOSE、SSE 断开或异常都进入同一个幂等关闭函数。
伪代码示例:
Session open(StartEvent event) {
Session session = sessions.createIdempotently(event.requestId(), event.sessionId());
session.prepareRtc(event.payload().rtc());
session.openSse();
session.startHeartbeat();
session.startAudioPipeline();
return session;
}
void onAsrFinal(Session session, String text) {
long turn = session.replaceCurrentTurn();
model.stream(text,
delta -> session.fragmenter(turn).accept(delta),
() -> session.fragmenter(turn).finish(),
error -> session.failTurn(turn, error));
}
void close(Session session, String reason) {
if (!session.beginCloseOnce()) return;
session.cancelModel();
session.closeAsr();
session.leaveRtc();
session.stopHeartbeat();
session.completeSse();
}切片器应保留一片前瞻:只有看到下一片时,才能确认上一片的 hasNext=true;模型流结束时,将最后缓存片以 hasNext=false 发送。不要发送空文本结束片。
7. 安全与可观测性
建议日志至少包含:
requestId、sessionId和本服务生成的 turn/speech 标识;START 接收、RTC 准备完成、首个音频帧、ASR FINAL;
模型开始、首字、首个 output、最后一个 output;
STOP、CLOSE、SSE 断开和资源释放原因;
各阶段耗时和错误码。
禁止记录完整鉴权凭证、RTC token、原始 PCM、完整用户音频或包含敏感参数的 URL。用户文本应按适用的隐私规则做脱敏、采样和保留期限控制。
8. 联调验收清单
8.1. 建联与音频
START 参数校验完整,RTC 准备成功后才返回 SSE
200;使用下发的
consumerUserId入会,能够持续收到音频帧;空闲时持续发送保活,断流后所有资源均被释放;
START 重试不会创建重复 Session 或重复 RTC consumer。
8.2. 播报与打断
单片和多片 SPEAK 的
speechId/index/hasNext均符合约束;同一播报不与其他
speechId交错;相同分片重试不会重复播报,冲突分片会被拒绝;
新回复打断旧播报时,同批只发送一次
STOP_OUTPUT → SPEAK;被替换的旧模型任务不会产生迟到分片。
8.3. 结束与恢复
final=true前没有未结束的 speech;CLOSE 重试保持幂等,未配置 CLOSE 接口时也能完成资源清理;
语音退出、SSE 断开、RTC 异常和服务超时均进入统一关闭流程;
退出后可立即使用新的
sessionId重新进入;至少完成 10 次“进入 → 多轮对话 → 退出 → 重进”的连续测试。
技能平台