测试工具:单击打开测试工具 Demo
注意:说明:
一、1. 服务概述
·通信协议:WebSocket (RFC 6455) over TLS,wss:// 生产强制、ws:// 仅内网调试。
·消息格式:Text 帧承载 JSON 事件,Binary 帧不接受(会被服务端忽略并打 warn); 原厂协议透传模式(isProxy=1)下使用原厂协议。
·音频承载:所有音频均以 base64 字符串放在 JSON 事件的 audio / delta 字段。
·支持的能力系列:
| model 参数 | 定位 |
| audio-og-realtime-2.1 | 端到端语音对话 · 基础能力。 |
| audio-og-realtime-2.1mini | 端到端语音对话 · 轻量型。 |
| audio-og-live-1 | 端到端语音对话 · Live 能力 |
| audio-s-realtime-3.0 | 端到端语音对话 · 增强能力。 |
| audio-gg-3.8-live | 端到端语音对话 · Live 能力(支持图片输入) |
注意:说明:
·单会话最大空闲超时:默认30秒,可通过 timeoutSec 查询参数覆盖,上限120秒。
·未指定 model 时的兜底:服务端根据账号维度的默认路由做兜底,仍未命中时使用 audio-og-realtime-2.1。
三、2.1 URL 模板
wss://{host}/ete/v1/{appid} ?model=<audio-og-realtime-2.1|audio-og-realtime-2.1mini|audio-og-live-1|audio-s-realtime-3.0|audio-gg-3.8-live> &secretId=<TC3 SecretId> &signature=<TC3-HMAC-SHA256 hex> &timeStamp=<unix 秒> &expired=<unix 秒> &nonce=<随机整数> &timeoutSec=<会话空闲超时秒> &isProxy=<0|1>
注意:说明:
注意:
四、2.2 路径参数
| 参数 | 必填 | 说明 |
| appid | 是 | 腾讯云 AppID,十进制无符号整数,随路径 /ete/v1/{appid} 传入。非数字时握手返回 400;必须与 secretId 所属主账号 AppID 一致,否则返回 401 authentication_failed: code=4111。 |
五、2.3 查询参数(Query)
| 参数 | 必填 | 类型 | 说明 |
| secretId | 是 | string | CAM SecretId。 |
| signature | 是 | string | TC3-HMAC-SHA256 签名(hex,小写),见 第 3 节。 |
| timeStamp | 是 | int64 | 签名时的当前 UNIX 时间戳(秒)。 |
| expired | 是 | int64 | 签名过期时间的 UNIX 时间戳(秒),必须 >= timeStamp 且 >= now,否则返回401 invalid_timestamp。 |
| nonce | 是 | string | 随机串(推荐随机整数),参与签名,防重放由 CAM 校验。 |
| model | 否 | string | 能力系列别名,见 服务概述。未指定时按账号默认路由兜底。默认模式下握手阶段不校验取值,非法值会在首次 session.update 时返回 error(code=pipeline_init_failed) 并断开连接;isProxy=1 时在握手阶段校验,非法值返回400 invalid_request: unsupported model。 |
| timeoutSec | 否 | int | 会话空闲超时(秒),默认30;> 120 时截断为120;<= 0 或非数字时忽略并使用默认值30。 |
| resId | 否 | string | 业务侧资源 ID,透传用于对账。 |
| isProxy | 否 | string | 协议模式,仅接受 0 / 1,其它值握手返回400 invalid_request: invalid param。0 或不传(默认):这篇档描述的统一 Realtime GA 协议。1:原厂协议透传,服务端只做鉴权 / 限流并原样转发消息,见 2.4.1。 |
注意:说明:
注意:除上表参数外,服务端不读取其他 query 参数;但任何出现在 URL 上的参数(signature 除外)都会参与签名校验。
六、2.4 URL 示例
wss://mps.cloud.tencent.com/ete/v1/1301234567 ?model=audio-s-realtime-3.0 &secretId=AKIDzxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx &signature=8f2c…4b3a &timeStamp=1735012345 &expired=1735015945 &nonce=8391023472 &timeoutSec=60 &resId=order_20260826_0001
七、2.4.1 原厂协议透传模式(isProxy=1)
URL 参数 isProxy=1 时,服务端不再提供统一的 Realtime GA 协议,客户端直接使用各能力系列对应模型的原厂协议收发消息,服务端原样转发。握手阶段的鉴权、账户状态检查、并发连接数限流与默认模式一致(见 第 3 章 / 11.1)。
适用于已按模型原厂协议完成开发、希望直接复用原厂能力的接入方。
2.4.1.1 开启方式
在 URL 上追加 isProxy=1,该参数必须参与签名(见 3.1):
wss://mps.cloud.tencent.com/ete/v1/1301234567 ?model=audio-s-realtime-3.0 &isProxy=1 &secretId=AKIDzxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx &signature=8f2c…4b3a &timeStamp=1735012345 &expired=1735015945 &nonce=8391023472 &timeoutSec=60
| isProxy 取值 | 行为 |
| 不传 / 0 | 默认模式,统一 Realtime GA 协议(这篇档除本节外的全部内容),行为不变。 |
| 1 | 原厂协议透传模式(本节)。 |
| 其它 | 握手返回 400 invalid_request: invalid param。 |
model 的取值与默认模式相同(不传时按账号默认路由兜底),无法识别时握手返回400 invalid_request: unsupported model。
九、2.5.1 概述
本节集中罗列5个对外能力系列(audio-og-realtime-2.1、audio-og-realtime-2.1mini、audio-og-live-1、audio-s-realtime-3.0、audio-gg-3.8-live)对上行事件 / 下行事件 / 可变字段 / 多模态输入的支持情况。矩阵列为 model 取值,行为能力点。
注意:说明:
十、2.5.2 上行事件(Client → Server)
| 事件 / 能力点 | audio-og-realtime-2.1(mini) | audio-og-live-1 | audio-s-realtime-3.0 | audio-gg-3.8-live |
| session.update(首次) | ✅ 建立会话 | ✅ 建立会话 | ✅ 建立会话 | ✅ 建立会话 |
| session.update(后续再次发送) | ⚠️ 见下方注 | ⚠️ 同左 | ⚠️ 同左 | ⚠️ 同左 |
| input_audio_buffer.append | ✅ | ✅ | ✅ | ✅ |
| input_audio_buffer.commit | ✅ | ❌ 无回执(内置 VAD 自动切分)。 | ✅ | ⚠️ 通知尽快结束当前用户输入,无回执。 |
| input_audio_buffer.clear | ✅ 清除已送入音频 + 回 cleared。 | ⚠️ 仅回 cleared,已送入的音频不会被清除。 | ⚠️ 同左 | ⚠️ 同左 |
| response.create(带 instructions) | ✅ 作为本轮生成指令。 | ⚠️ 见 9.1 | ✅ 让服务端说出该文本。 | ⚠️ 作为一条用户文本输入并回复,见 10.3。 |
| response.create(不带 instructions) | ✅ 触发一轮生成。 | ⚠️ 见 9.1 | ⚠️ 静默 no-op(无回执)。 | ⚠️ 仅在之前追加过 message 时触发回复,否则 no-op。 |
| response.cancel | ✅ | ❌ 无回执(no-op,依赖内置 barge-in)。 | ✅ | ⚠️ 立即回 response.done(cancelled),见 10.4。 |
| conversation.item.create – message / function_call_output | ✅ | ⚠️ 见 9.3 | ✅ | ✅(不回 conversation.item.created)。 |
| conversation.item.create – 含 input_image | ✅ Data URL / HTTP URL / detail | ❌ not_supported | ❌ not_supported | ⚠️ 仅 Data URL,见 10.5。 |
| conversation.item.retrieve / delete | ✅ | ❌ 无回执 | ✅ | ❌ 无回执 |
| conversation.item.truncate | ✅ | ❌ not_supported | ❌ not_supported | ❌ not_supported |
| output_audio_buffer.clear | ✅ 取消当前 response + 回 cleared。 | ⚠️ 仅回 cleared(不会打断当前回复)。 | ✅ 同第一列 | ✅ 同 response.cancel + 回 cleared。 |
| session.close | ✅ | ✅ | ✅ | ✅ |
| transcription_session.update | ❌ not_supported | ❌ not_supported | ❌ not_supported | ❌ not_supported |
| 其它未知 type | 静默忽略 | 静默忽略 | 静默忽略 | 静默忽略 |
注意:说明:
注意:后续 session.update 的实际行为(所有能力一致):会话参数在首次 session.update 时一次性生效,此后再发送的 session.update 不会生效,voice / instructions / tools / delegation 等均不会变更;服务端仍会回一条 session.updated,其内容是客户端历次提交字段的浅合并回显,不代表已生效。唯一的例外是下行音频格式:每次 session.update 都会重新校验 audio.output.format,非法时回 unsupported_output_format 并断开连接。如需变更会话参数,请重新建立连接。
十一、2.5.3 下行事件(Server → Client)
| 事件 | audio-og-realtime-2.1(mini) | audio-og-live-1 | audio-s-realtime-3.0 | audio-gg-3.8-live |
| session.updated | ✅ | ✅ | ✅ | ✅ |
| input_audio_buffer.speech_started / speech_stopped | ✅ | ❌ 不下发 | ❌ 不下发 | ⚠️ 不保证下发,见 10.2 |
| input_audio_buffer.committed | ✅ | ❌ 不下发 | ✅ | ⚠️ 不保证下发,同上。 |
| input_audio_buffer.cleared | ✅ | ✅ | ✅ | ✅ |
| conversation.item.input_audio_transcription.delta | ✅ | ⚠️ 携带 start_ms / end_ms,不带 item_id。 | ✅ | ✅ |
| conversation.item.input_audio_transcription.completed | ✅(可能带 usage) | ❌ 不下发(客户端自行断句,见 9.5)。 | ✅ | ✅(不带 usage;在助手开始回复时下发)。 |
| conversation.item.input_audio_transcription.failed | ❌ 不下发 | ❌ 不下发 | ✅ | ❌ 不下发 |
| conversation.item.created | ✅ | ❌ 不下发 | ✅ | ❌ 不下发 |
| conversation.item.retrieved / deleted | ✅ | ❌ 不下发 | ✅ | ❌ 不下发 |
| response.created | ✅ | ⚠️ 见 9.1 | ❌ 不下发 | ✅ |
| response.output_item.added / content_part.added | ✅ | ⚠️ 仅 function call 时下发。 | ✅ | ✅ |
| response.output_audio.delta | ✅ | ⚠️ 不带 item_id / response_id。 | ✅ | ✅ |
| response.output_audio_transcript.delta | ✅ | ⚠️ 携带 start_ms / end_ms,不带 item_id / response_id。 | ✅ | ✅ |
| response.output_audio.done / response.output_audio_transcript.done | ✅ | ❌ 不下发 | ✅ | ✅(被打断 / 取消的轮次同样下发)。 |
| response.content_part.done / response.output_item.done | ✅ | ⚠️ 仅 function call 时下发。 | ✅ | ✅ |
| response.done(含 usage) | ✅ | ⚠️ 仅 responses delegation 下发;client delegation 不下发。 | ✅ | ✅ |
| response.function_call_arguments.done | ✅ | ⚠️ 仅 responses delegation。 | ✅ | ✅ |
| response.function_call_arguments.delta | ❌ 不下发 | ❌ 不下发 | ❌ 不下发 | ❌ 不下发 |
| output_audio_buffer.started | ❌ 不下发 | ❌ 不下发 | ✅ | ❌ 不下发 |
| output_audio_buffer.stopped | ✅ | ❌ 不下发 | ✅ | ✅ |
| output_audio_buffer.cleared | ✅ | ✅ | ✅ | ✅ |
| session.delegation.created | ❌ 不下发 | ✅ 专属(见 9.2) | ❌ 不下发 | ❌ 不下发 |
| session.closed | ✅ 客户端 session.close 后下发。 | ✅ 同左 | ✅ 同左 | ✅ 同左 |
| rate_limits.updated / response.output_text.* | ❌ 不下发 | ❌ 不下发 | ❌ 不下发 | ❌ 不下发 |
| error | ✅ | ✅ | ✅ | ✅ |
十二、2.5.4 session.update 字段支持(仅首次 session.update 生效)
| 字段 | audio-og-realtime-2.1(mini) | audio-og-live-1 | audio-s-realtime-3.0 | audio-gg-3.8-live |
| instructions | ✅ | ✅ | ✅ | ✅ |
| audio.output.voice | ⚠️ 需在服务端音色白名单内,否则使用默认音色。 | ✅ 不校验,直接使用。 | ⚠️ 仅支持加密音色 ID,见 Q9。 | ⚠️ 需在内置音色列表内(大小写不敏感),否则使用默认音色。 |
| audio.input.format / audio.output.format | ✅ 见 第 7 节 | ✅ 同左 | ✅ 同左 | ✅ 同左 |
| audio.input.transcription.language | ✅ | ❌ 静默忽略(自动识别语言)。 | ❌ 静默忽略(自动识别语言)。 | ✅ 推荐 BCP-47(如 zh-CN / en-US)。 |
| audio.input.turn_detection(VAD) | ❌ 静默忽略(由服务端配置决定)。 | ❌ 静默忽略(强制启用 VAD)。 | ❌ 静默忽略。 | ❌ 静默忽略(强制启用 VAD)。 |
| tools | ✅ | ✅ 非空时自动切换为 responses delegation(见 9.2)。 | ✅ | ✅ 仅 type=function |
| tool_choice | ❌ 静默忽略 | ❌ 静默忽略(固定 auto) | ❌ 静默忽略 | ❌ 静默忽略 |
| audio.output.speed / audio.output.loudness | ❌ 静默忽略 | ❌ 静默忽略 | ✅ | ❌ 静默忽略 |
| delegation(client / responses) | ❌ 静默忽略 | ✅ 专属 | ❌ 静默忽略 | ❌ 静默忽略 |
| extension(私有扩展 asr / tts / dialog) | ❌ 静默忽略 | ❌ 静默忽略 | ✅ | ❌ 静默忽略 |
十三、2.5.5 多模态输入与用量字段
| 能力点 | audio-og-realtime-2.1(mini) | audio-og-live-1 | audio-s-realtime-3.0 | audio-gg-3.8-live |
| 音频输入 | ✅ | ✅ | ✅ | ✅ |
| 文本输入 | ✅ | ✅(client delegation 下仅 system / assistant 角色生效)。 | ✅ | ✅ |
| 图片输入(input_image) | ✅ | ❌ | ❌ | ⚠️ 仅 Data URL(png / jpeg / webp / heic / heif)。 |
| response.done.usage | ⚠️ 有用量数据时携带。 | ⚠️ 仅 responses delegation。 | ⚠️ 有用量数据时携带。 | ⚠️ 有用量数据时携带。 |
| reasoning_tokens / cache_write_tokens | ⚠️ 适用时存在。 | ⚠️ 仅 responses delegation 且适用时存在。 | ⚠️ 适用时存在。 | ⚠️ 仅 reasoning_tokens,适用时存在。 |
十四、3. 签名
签名沿用腾讯云 CAM 签名标准。
流程参考:签名生成。
十五、3.1 参与签名的查询参数
除 signature 本身外的所有 query 参数都参与签名(按字母升序排列)。这意味着:
·必填参数 secretId / timeStamp / expired / nonce 一定参与签名。
·可选参数(如 model / timeoutSec / resId)只要你在 URL 里带上了就必须参与签名;未带则不参与。
·signature 参数本身不参与签名(作为签名结果放在 URL 上)。
其他业务参数(如音频格式、voice、instructions、tools 等)通过握手成功后的 session.update 事件传递,与 URL 无关。
十六、3.2 签名算法
Step 1: 排序并 URL-encode,构造规范化查询串 keys = sort(参与签名的 params keys) // 除 signature 之外的所有 query canonicalQueryString = keys.map(k => k + '=' + urlEncode(params[k])).join('&')Step 2: 构造规范化请求 canonicalRequest = "post" + "\\n" + "/ete/v1/{appid}" + "\\n" + canonicalQueryString + "\\n" + "content-type:application/json; charset=utf-8" + "\\n" + "host:" + host + "\\n" + "" + "\\n" + "content-type;host" + "\\n"Step 3: 构造待签字符串 date = UTC(timeStamp) 的 YYYY-MM-DD credentialScope = date + "/mps/tc3_request" hashedRequest = hex( SHA-256( canonicalRequest ) ) stringToSign = "TC3-HMAC-SHA256" + "\\n" + timeStamp + "\\n" + credentialScope + "\\n" + hashedRequestStep 4: 计算派生签名密钥 kDate = HMAC-SHA256( date, "TC3" + secretKey ) kService = HMAC-SHA256( "mps", kDate ) kSigning = HMAC-SHA256( "tc3_request", kService )Step 5: 计算最终签名 signature = hex( HMAC-SHA256( stringToSign, kSigning ) ) // 小写 hex
十七、3.3 JavaScript 参考实现
浏览器环境下的完整实现见 测试 Demo。
generateSignature() 方法,使用浏览器原生 crypto.subtle API 完成。以下节选核心片段:
async function sign(cfg) { const now = Math.floor(Date.now() / 1000); const expired = now + 3600; const params = { // 除 signature 外的所有 query 参数, 均需参与签名 model: cfg.model, // 可选; 未传则不放入 params timeStamp: String(now), expired: String(expired), nonce: String(Math.floor(Math.random() * 1e10)), secretId: cfg.secretId, timeoutSec: String(cfg.timeoutSec || 30), // 若使用 resId, 也一并放入并参与签名: // resId: cfg.resId, }; const path = `/ete/v1/${cfg.appid}`; const keys = Object.keys(params).sort(); const canonicalQueryString = keys .map((k) => `${k}=${encodeURIComponent(params[k])}`).join('&'); const canonicalRequest = [ 'post', path, canonicalQueryString, 'content-type:application/json; charset=utf-8', `host:${cfg.host}`, '', 'content-type;host', '', ].join('\\n'); const hashed = await sha256Hex(canonicalRequest); const date = new Date(now * 1000).toISOString().split('T')[0]; const scope = `${date}/mps/tc3_request`; const stringToSign = ['TC3-HMAC-SHA256', String(now), scope, hashed].join('\\n'); const kDate = await hmacSha256(date, 'TC3' + cfg.secretKey); const kService = await hmacSha256('mps', kDate); const kSigning = await hmacSha256('tc3_request', kService); const signature = bufToHex(await hmacSha256(stringToSign, kSigning)); const q = new URLSearchParams(); keys.forEach((k) => q.append(k, params[k])); q.append('signature', signature); return `wss://${cfg.host}${path}?${q.toString()}`;}
十八、3.4 Go 参考实现
func sign(host, appID, secretID, secretKey, model string, timeoutSec int) string { now := time.Now().Unix() expired := now + 3600 params := map[string]string{ "timeStamp": strconv.FormatInt(now, 10), "expired": strconv.FormatInt(expired, 10), "nonce": strconv.Itoa(rand.Intn(1e9)), "secretId": secretID, "timeoutSec": strconv.Itoa(timeoutSec), } if model != "" { params["model"] = model } keys := make([]string, 0, len(params)) for k := range params { keys = append(keys, k) } sort.Strings(keys) parts := make([]string, 0, len(keys)) for _, k := range keys { parts = append(parts, k+"="+url.QueryEscape(params[k])) } cqs := strings.Join(parts, "&") path := "/ete/v1/" + appID canonical := strings.Join([]string{ "post", path, cqs, "content-type:application/json; charset=utf-8", "host:" + host, "", "content-type;host", "", }, "\\n") hashed := sha256Hex([]byte(canonical)) date := time.Unix(now, 0).UTC().Format("2006-01-02") scope := date + "/mps/tc3_request" stringToSign := strings.Join([]string{ "TC3-HMAC-SHA256", strconv.FormatInt(now, 10), scope, hashed, }, "\\n") kDate := hmac256([]byte(date), []byte("TC3"+secretKey)) kService := hmac256([]byte("mps"), kDate) kSigning := hmac256([]byte("tc3_request"), kService) signature := hex.EncodeToString(hmac256([]byte(stringToSign), kSigning)) q := url.Values{} for k, v := range params { q.Set(k, v) } q.Set("signature", signature) return "wss://" + host + path + "?" + q.Encode()}func sha256Hex(b []byte) string { h := sha256.Sum256(b); return hex.EncodeToString(h[:]) }func hmac256(msg, key []byte) []byte { m := hmac.New(sha256.New, key); m.Write(msg); return m.Sum(nil) }
十九、3.5 鉴权失败的握手响应
服务端在 WebSocket 握手升级前就完成鉴权与并发检查,任一步失败会以 HTTP 响应拒绝,不会进入 WebSocket 通道,因此客户端不会收到 error 事件,而是拿到一个标准的 HTTP 状态码。见 第 11.1 节。
21、4.1 关键约定
握手成功后服务端不主动下发任何事件。首条消息必须由客户端发起:
·客户端发 session.update 完成会话初始化,服务端才回一条 session.updated。
·收到 session.updated 才代表“可以开始送音频”。
23、4.3 打断当前 response
客户端可以随时发送 {"type": "response.cancel"} 打断当前正在生成的 response。
服务端会立即停止 TTS 下发,并回一条 response.done(status 可能为 cancelled)。
25、5.1 事件类型速览
下表仅列事件用途;各 model 对每个事件的支持与差异情况集中见 2.5 模型能力矩阵。
| 事件 type | 用途 |
| session.update | 更新会话参数(首次调用等价于“完成握手初始化”)。 |
| input_audio_buffer.append | 追加一段用户音频(base64 编码)。 |
| input_audio_buffer.commit | 手动提交音频缓冲(关闭 server_vad 时使用)。 |
| input_audio_buffer.clear | 清空未 commit 的上行音频缓冲。 |
| response.create | 主动请求服务端生成一次 response(可携带 instructions 用作打招呼 / 干预回复)。 |
| response.cancel | 打断当前正在生成的 response。 |
| conversation.item.create | 向会话上下文新增一条 item(含 function_call_output 回传)。 |
| conversation.item.retrieve | 请求获取指定 item 详情。 |
| conversation.item.delete | 删除指定 item。 |
| conversation.item.truncate | 截断已生成的 assistant 音频(部分场景不支持,会回 error(not_supported))。 |
| output_audio_buffer.clear | 清空当前 assistant 音频播放缓冲(服务端会同步映射为 response.cancel)。 |
| transcription_session.update | 独立 transcription 会话更新(当前不支持,会回 error(not_supported))。 |
| session.close | 主动优雅关闭会话,服务端会回 session.closed 携带会话总用量后断开(所有能力均支持,详见 9.4) |
注意:说明:
注意:首次 session.update 之前收到的控制事件:response.create / conversation.item.create / retrieve / delete / truncate 回 error(code=session_not_ready);input_audio_buffer.append / commit / response.cancel 静默丢弃;input_audio_buffer.clear / output_audio_buffer.clear 仍回 cleared;session.close 正常执行关闭流程。未知 type 一律静默忽略。
26、5.2 session.update
{ "type": "session.update", "session": { "instructions": "你是一个专业的智能助手, 说话简洁友好。", "audio": { "input": { "format": { "type": "audio/pcm", "rate": 16000 }, "transcription": { "language": "zh" } }, "output": { "format": { "type": "audio/pcm", "rate": 24000 }, "voice": "xxxxxx", "speed": 100, "loudness": 100 } }, "tools": [ { "type": "function", "name": "get_current_time", "description": "获取当前系统时间", "parameters": { "type": "object", "properties": {}, "required": [] } } ] }}
字段说明:
| 字段 | 类型 | 说明 |
| session.instructions | string | System instructions,覆盖服务端默认。 |
| session.audio.input.format.type | string | 上行音频格式,见 第 7 节。 |
| session.audio.input.format.rate | int | 上行采样率(Hz)。 |
| session.audio.input.transcription.language | string | ASR 转写语言(ISO 639-1,如 zh / en / ja)。留空则由服务端自动检测;部分能力基于内建语言自适应,客户端指定值可能会被忽略。 |
| session.audio.output.format.type | string | 下行音频格式。 |
| session.audio.output.format.rate | int | 下行采样率(Hz)。 |
| session.audio.output.voice | string | 发音人 ID。支持明文音色 ID 与加密音色 ID(由音色加密服务生成),服务端自动识别。若音色不适用于当前会话能力,服务端会静默 fallback 到默认音色(详见 Q9)。 |
| session.audio.output.speed | int | 语速(部分能力支持,不支持时静默忽略)。 |
| session.audio.output.loudness | int | 响度(部分能力支持,不支持时静默忽略)。 |
| session.tools | array | Function tool 定义,遵循标准 GA Function Tool schema(audio-gg-3.8-live 仅支持 type=function,其它类型被忽略)。 |
| session.delegation | string/object/null | 仅 audio-og-live-1,见 9.2。 |
| session.extension | object | 私有扩展字段 {asr, tts, dialog}(仅 audio-s-realtime-3.0 生效,其它能力忽略)。 |
VAD / turn_detection:服务端默认启用 server_vad。客户端在 session.audio.input.turn_detection 里的自定义参数,服务端会尽力适配;具体生效情况以 session.updated.session.audio.input.turn_detection 回执为准。
27、5.3 input_audio_buffer.append
{ "type": "input_audio_buffer.append", "audio": "<base64-encoded audio chunk>"}
·audio 是一段完整的音频负载,服务端按 session.audio.input.format 声明的格式解码。
·推荐分片大小:20ms-40ms(PCM 16k 单声道下约 640~1280 字节 base64 前)。
·首次 session.update 之前送入的 append 会被静默丢弃。
28、5.4 input_audio_buffer.commit / clear
{ "type": "input_audio_buffer.commit" }{ "type": "input_audio_buffer.clear" }
·commit 仅在关闭 server_vad 时需要显式调用。
·clear 用于取消当前未提交的用户音频,无论能力后端是否原生支持,服务端都会回一条 input_audio_buffer.cleared 事件给客户端。
29、5.5 response.create / cancel
{ "type": "response.create", "response": { "instructions": "你好, 我是你的智能助手。" // 可选 }}{ "type": "response.cancel" }
各能力语义:
| 能力 | 带 response.instructions | 不带 instructions |
| audio-og-realtime-2.1(mini) | instructions 作为本轮生成指令(不是逐字朗读)。 | 触发一轮生成。 |
| audio-s-realtime-3.0 | 服务端逐字合成该文本(打招呼 / 播报)。 | 静默 no-op,无回执(function call 回传后会自动续接,无需发送)。 |
| audio-og-live-1(client delegation) | 服务端让模型说出该文本。 | 回 error(code=not_supported)。 |
| audio-og-live-1(responses delegation) | 触发继续生成,instructions 被丢弃。 | 继续生成(function call 回传后必须发送)。 |
| audio-gg-3.8-live | instructions 作为一条用户文本输入,模型据此回复(不是逐字朗读)。 | 之前通过 conversation.item.create 追加过 message 时触发回复,否则静默 no-op。 |
response.create 支持的字段(仅 audio-og-realtime-2.1(mini) 支持 instructions 以外的字段):
| 字段 | 类型 | 说明 |
| response.instructions | string | 见上表。 |
| response.voice | string | 本轮临时覆盖的音色(可选)。 |
| response.modalities | []string | 本轮输出模态(可选,标准 GA 语义)。 |
| response.metadata | object | 元数据(可选)。 |
30、5.6 conversation.item.create
用途 1:追加 user / assistant message
{ "type": "conversation.item.create", "item": { "type": "message", "role": "user", "content": [{ "type": "input_text", "text": "帮我查一下深圳今天的天气" }] }}
用途 2:回传 function call 结果(详见 第 8 节)
{ "type": "conversation.item.create", "item": { "type": "function_call_output", "call_id": "call_abc123", "output": "2026-08-26 09:30:00" }}
用途 3:图片输入(多模态)
在同一条 user message 里混合 input_text 与 input_image 内容块,可以让模型基于图片回答问题。当前仅 audio-og-realtime-2.1 / audio-og-realtime-2.1mini /audio-gg-3.8-live 支持图片输入,其它 model 收到含 input_image 的 item 会返回 error(code=not_supported)。
注意:说明:
注意:audio-gg-3.8-live 仅支持写法 A(Data URL),格式与大小限制不同,且不回执。conversation.item.created,见 10.5。
写法 A:Data URL(推荐,无需暴露外部 URL)
{ "type": "conversation.item.create", "item": { "type": "message", "role": "user", "content": [ { "type": "input_text", "text": "请描述这张图里的关键信息" }, { "type": "input_image", "image_url": "data:image/jpeg;base64,/9j/4AAQSkZJRg…", "detail": "auto" } ] }}
写法 B:HTTP(S) URL
{ "type": "conversation.item.create", "item": { "type": "message", "role": "user", "content": [ { "type": "input_text", "text": "帮我识别这张截图里的错误码" }, { "type": "input_image", "image_url": "https://example.com/foo.png", "detail": "high" } ] }}
字段说明:
| 字段 | 类型 | 必填 | 说明 |
| type | string | 是 | 固定 "input_image"。 |
| image_url | string | 是 | Data URL(data:image/{jpeg,png,gif,webp};base64,…)或 HTTP/HTTPS URL。 |
| detail | string | 否 | "auto"(默认)/ "low" / "high"。low 定量小 token 快回复;high 分辨率高,识别细节更好,消耗更多 tokens。 |
发送后的时序:
1. 服务端确认:conversation.item.created。
2. 客户端需主动触发一次响应:{"type": "response.create"}(因为图片消息不像语音会自动触发 VAD)。
3. 服务端下发常规的 response.output_audio.delta / response.output_audio_transcript.delta / response.done。
图片 tokens 计入 response.done.response.usage.input_tokens 总量;detail=high 的图片 tokens 显著高于 detail=low。
限制与建议:
·单条 message 建议图片数量 ≤ 4 张,避免 message 过大导致响应变慢。
·Data URL 单张建议 ≤ 2 MB(base64 后 ≤ 2.7 MB),过大可能被拒绝。
·HTTP URL 必须公网可访问,不支持带鉴权头的私有资源。
·动态图(gif) 可能只取首帧,不做逐帧理解。
·图片输入不会占用语音会话的音频通道,与麦克风持续送入的 input_audio_buffer.append 可并行使用。
31、5.7 conversation.item.retrieve / delete / truncate
{ "type": "conversation.item.retrieve", "item_id": "item_xxx" }{ "type": "conversation.item.delete", "item_id": "item_xxx" }{ "type": "conversation.item.truncate", "item_id": "item_xxx", "content_index": 0, "audio_end_ms": 1500 }
32、5.8 output_audio_buffer.clear
{ "type": "output_audio_buffer.clear" }
服务端会:
1. 内部触发一次 response.cancel 打断当前 TTS。
2. 回一条 output_audio_buffer.cleared,客户端据此清空本地音频播放缓冲。
34、6.1 完整事件清单
下表仅列事件触发时机;各 model 对每个事件的支持情况集中见 2.5 模型能力矩阵。
| 事件 type | 触发时机 |
| session.updated | 每次收到 session.update 后回显;首次即为握手后的第一条下行事件。 |
| error | 参数错误 / 服务后端异常 / 不支持的能力。 |
| conversation.item.created | 一条 item 加入会话上下文(服务端确认回执)。 |
| conversation.item.retrieved | 响应 conversation.item.retrieve。 |
| conversation.item.deleted | 响应 conversation.item.delete。 |
| conversation.item.input_audio_transcription.delta | 用户 ASR 增量转写(非稳态)。 |
| conversation.item.input_audio_transcription.completed | 用户 ASR 稳态转写(可能携带 usage)。 |
| conversation.item.input_audio_transcription.failed | 用户 ASR 转写失败。 |
| input_audio_buffer.committed | 用户一句话音频入缓冲(VAD 触发或客户端 commit)。 |
| input_audio_buffer.cleared | 响应 input_audio_buffer.clear。 |
| input_audio_buffer.speech_started | 服务端 VAD 检测到用户开始说话。 |
| input_audio_buffer.speech_stopped | 服务端 VAD 检测到用户停止说话。 |
| response.created | 一次 response 开始生成。 |
| response.output_item.added | response 内新增一个 output_item(message / function_call)。 |
| response.content_part.added | output_item 内新增一个 content part(audio / text)。 |
| response.output_audio.delta | 助手音频增量(base64)。 |
| response.output_audio.done | 助手音频结束。 |
| response.output_audio_transcript.delta | 助手音频伴随文本增量。 |
| response.output_audio_transcript.done | 助手音频伴随文本稳态。 |
| response.function_call_arguments.done | Function call 参数完整下发(不下发 .delta 增量)。 |
| response.content_part.done | content part 结束。 |
| response.output_item.done | output_item 结束。 |
| response.done | 一次 response 全部结束(可能携带 usage;被打断时 status=cancelled)。 |
| output_audio_buffer.started | 服务端开始下发助手音频。 |
| output_audio_buffer.stopped | 服务端一段助手音频结束。 |
| output_audio_buffer.cleared | 响应客户端 output_audio_buffer.clear。 |
| session.delegation.created | audio-og-live-1 专属:模型发起 delegation(见 9.2)。 |
| session.closed | 客户端主动 session.close 后下发一次,携带 reason / usage / session_duration_seconds(详见 9.4)。 |
注意:说明:
35、6.2 session.updated
{ "type": "session.updated", "event_id": "evt_xxxxxxxx", "session": { "id": "sess_xxxxxxxx", "type": "realtime", "model": "audio-s-realtime-3.0", "audio": { "input": {…}, "output": {…} }, "instructions": "…" }}
客户端应在收到此事件之后才开始送 input_audio_buffer.append。
session 对象是客户端历次 session.update 中 session 字段的浅合并回显,服务端仅补充 id(会话 ID)、type(固定 realtime)、model 三个字段,不代表实际生效的配置。其中 model 取值规则:客户端在 session.model 中自行传入时原样回显;否则为 URL 参数 modelURL 未指定时 model 则显 audio-og-realtime-2.1——注意此时实际生效的能力可能是账号默认路由选中的其它能力,客户端不要依赖该字段判断实际能力,建议始终在 URL 中显式传 model。
36、6.3 用户 ASR 事件
{ "type": "conversation.item.input_audio_transcription.delta", "event_id": "evt_xxx", "item_id": "item_xxx", "content_index": 0, "delta": "深圳", "start_ms": 1240, "end_ms": 1560}{ "type": "conversation.item.input_audio_transcription.completed", "event_id": "evt_xxx", "item_id": "item_xxx", "content_index": 0, "transcript": "深圳今天天气怎么样", "usage": { "type": "duration", "seconds": 2 }}
usage 字段承载本次 ASR 转写的用量(type="duration" + seconds),可用于客户端本地成本观测。目前仅 audio-og-realtime-2.1(mini) 可能携带,其余情况缺省(omitempty);客户端应做字段可选兼容。
start_ms / end_ms 是 delta 对应音频在会话时间线上的起止毫秒偏移(以会话开始为零点),仅 audio-og-live-1 携带,且取值为 0 时字段会被省略。该能力下 delta 不带 item_id,服务端也不下发 transcription.completed,客户端需按 start_ms / end_ms 的 gap 自合并完整问话,具体见 9.5。其它能力正常下发 completed,不携带这两个字段。
audio-s-realtime-3.0 的 ASR delta 为追加式增量;识别过程中若修正了已识别文字,增量可能为空并被跳过,最终以 completed.transcript 为准。
transcription.completed.usage 与 response.done.response.usage(token 用量)是两个独立并列的用量字段:前者按 ASR 音频转写秒数记录,后者按 response 的 token 用量记录,量纲不同不可互相换算。
37、6.4 助手音频 / 文本事件
{ "type": "response.output_audio.delta", "event_id": "evt_xxx", "response_id": "resp_xxx", "item_id": "item_xxx", "output_index": 0, "content_index": 0, "delta": "<base64 pcm16 / opus chunk>"}{ "type": "response.output_audio_transcript.delta", "event_id": "evt_xxx", "response_id": "resp_xxx", "item_id": "item_xxx", "delta": "深圳"}
注意:说明:
注意:audio-og-live-1 下这两个事件不带 response_id / item_id,transcript delta 额外带 start_ms / end_ms,见 9.5。客户端解析时请把 item_id 作可选
38、6.5 response.done
{ "type": "response.done", "event_id": "evt_xxx", "response": { "id": "resp_xxx", "status": "completed", "usage": { "total_tokens": 123, "input_tokens": 45, "output_tokens": 78, "input_token_details": { "text_tokens": 20, "audio_tokens": 15, "cached_tokens": 10, "cached_tokens_details": { "text_tokens": 6, "audio_tokens": 4 } }, "output_token_details": { "text_tokens": 30, "audio_tokens": 48, "reasoning_tokens": 25 } } }}
注意:说明:
注意:reasoning_tokens(仅具备推理能力的模型会下发)表示本轮回答的推理 tokens,为 output_token_details.text_tokens 的内含子集(不与 text / audio 并列);仅在适用时存在,其它能力下为 0 或缺省。input_token_details.cache_write_tokens 表示提示词缓存首次写入的 tokens(与 cached_tokens 命中互斥)。
status 取值:audio-og-realtime-2.1(mini) 为 completed / cancelled / failed /incomplete;audio-s-realtime-3.0 / audio-gg-3.8-live 仅有 completed / cancelled;audio-og-live-1 缺省为 completed。
response.usage 承载本轮 response 的 token 用量明细,字段结构对齐标准 Realtime GA;无用量数据时该字段缺省(omitempty)。客户端可用于本地用量观测。
39、6.6 error
{ "type": "error", "event_id": "evt_xxx", "error": { "type": "invalid_request_error", "code": "unsupported_output_format", "message": "unsupported opus output rate 44100, allowed: 8000/12000/16000/24000/48000", "event_id": "客户端触发本 error 的原事件 event_id" }}
以下错误发生后服务端会主动关闭连接:unsupported_output_format /pipeline_init_failed / upstream_send_error / upstream_error / upstream_closed。空闲超时(idle_timeout)时服务端直接关闭连接,客户端通常收不到该 error 事件,应以 WebSocket close 作为判断依据。完整错误码见 11.1。
41、7.1 上行音频(Input)
| 格式(format.type) | 说明 | 允许的采样率 |
| pcm / audio/pcm / pcm16 / pcm_s16le / s16le | 16-bit signed LE mono PCM | 任意(推荐 16000) |
| opus / audio/opus | 裸 Opus packet(每个 append 携带一个完整 packet) | 8k / 12k / 16k / 24k / 48k |
42、7.2 下行音频(Output)
| 格式 | 允许的采样率 | 备注 |
| pcm | 任意 | 服务端会按需做重采样至客户端声明的采样率。 |
| opus | 8k / 12k / 16k / 24k / 48k | 服务端负责编码为裸 Opus packet;每个 response.output_audio.delta.delta 通常对应一个 20 ms Opus 帧。 |
兼容 MIME 别名:客户端可以用 audio/pcm / pcm / pcm16 / pcm_s16le 任一形式声明 PCM;用 audio/opus / opus 声明 Opus。服务端会自动归一化。
43、7.3 服务端下行链路策略
服务端在会话建立时会根据客户端声明的 session.audio.output.format 自动选择最优下行链路(直接透传或服务端重采样 / 格式转换),客户端无感知。
客户端只需保证 format / rate 落在 7.1 / 7.2 表格声明的合法范围内;实际生效的格式会在 session.updated.session.audio 里给出。
45、8.1 时序
回传工具结果后是否需要 response.create:
| 能力 | 是否需要 | 说明 |
| audio-og-realtime-2.1(mini) | 需要 | 标准 GA 语义,回传后发 {"type":"response.create"}。 |
| audio-s-realtime-3.0 | 不需要 | 收到工具结果后自动续接;发送空 response.create 无副作用(no-op)。 |
| audio-og-live-1(responses delegation) | 需要 | 不会自动续接;多个并行 call 需全部回传后再发一次。 |
| audio-og-live-1(client delegation) | — | 不会触发 function call,见 9.2。 |
| audio-gg-3.8-live | 不需要 | 收到工具结果后自动续接(新的一轮 response);发送空 response.create 无副作用(no-op)。 |
注意:说明:
注意:统一建议:所有 function_call_output 回传后都发送一次不带 instructions 的 response.create,可同时兼容以上能力。
46、8.2 关键字段
下行 response.function_call_arguments.done:
{ "type": "response.function_call_arguments.done", "event_id": "evt_xxx", "response_id": "resp_xxx", "item_id": "item_xxx", "call_id": "call_abc123", "name": "get_current_time", "arguments": "{}"}
上行 conversation.item.create 回传:
{ "type": "conversation.item.create", "item": { "type": "function_call_output", "call_id": "call_abc123", "output": "2026-08-26 09:30:00" }}
注意:说明:
注意:客户端按标准 Realtime GA 语义发送即可;按上表"统一建议"处理续接即可兼容各能力。audio-s-realtime-3.0 一轮触发多个 call 时,服务端会拆成多条 function_call_arguments.done(该能力下事件不带 item_id),每个 call 需分别回传。audio-gg-3.8-live 一轮可能触发多个 call,每个 call 需分别回传;触发 function call 的那一轮会先下发 response.done,续接回复是新一轮 response_id
47、8.3 完整可运行示例
参见 测试工具 中:
·勾选左侧 “启用 Demo Tool” 复选框。
·提问 “现在几点了?”。
·观察运行日志中完整的 function call 时序。
48、9. audio-og-live-1 专属交互协议
本章集中说明 audio-og-live-1 相比其它能力系列的行为差异与专属协议扩展。事件名与字段结构仍沿用 Realtime GA,但该能力缺少多类边界事件、部分控制事件语义不同,客户端需要按本章做单独适配(主要是按时间戳自行断句、按 delegation 模式处理工具调用与上下文注入)。
49、9.1 协议兼容性总览
·会话参数一次性生效:voice / instructions / tools / delegation 均在首次 session.update 时生效,后续 session.update 不生效(所有能力一致,见2.5.1)。如需变更,请重开会话。
·上行控制事件的实际行为:
| 事件 | 行为 |
| input_audio_buffer.commit | 不支持,无回执(内置 VAD 自动切分轮次)。 |
| input_audio_buffer.clear | 仅回 input_audio_buffer.cleared,已送入的音频不会被清除。 |
| response.cancel | no-op,无回执(内置 barge-in,用户说话即自动打断)。 |
| output_audio_buffer.clear | 仅回 output_audio_buffer.cleared,当前回复不会被打断。 |
| response.create | client delegation:带 instructions 时让模型说出该文本,不带时回 not_supported;responses delegation:触发继续生成(instructions 被丢弃)。 |
| conversation.item.create | 见 9.3;含 input_image 时回 not_supported。 |
| conversation.item.retrieve / delete | 不支持,无回执。 |
| conversation.item.truncate | 回 error(code=not_supported)。 |
·VAD 强制启用:内置语音端点检测且不可关闭,turn_detection 会被忽略。
·不下发 ASR / 助手文本的边界事件:不下发 conversation.item.input_audio_transcription.completed、response.output_audio.done、
response.output_audio_transcript.done、speech_started / speech_stopped、input_audio_buffer.committed。用户 ASR delta 与助手 transcript delta 额外携带 start_ms / end_ms(以会话开始为零点的毫秒偏移),客户端按 gap 自行断句,见 9.5。
·delta 事件不带 item_id / response_id:该能力为连续流模型,没有 item 维度:
·conversation.item.input_audio_transcription.delta 不带 item_id。
·response.output_audio_transcript.delta / response.output_audio.delta 不带 item_id / response_id。
客户端展示层应基于 start_ms / end_ms 自行分组(例如本地生成 client-asr-${serial} / client-llm-${serial})。function_call 相关事件仍携带 item_id / call_id。
·response.created / response.done:
·responses delegation:只在整个会话首个助手 delta 到达时下发一次;不下发 response.done。
·client delegation:response.created,此后不再下发;不下发 response.done。客户端应根据 transcript 时间戳判定每轮回复结束。不下发 response.done。
50、9.2 Delegation 模式 (responses 模式需要联系我们单独开启,默认不支持)
audio-og-live-1 有两种 delegation 模式,客户端可在首次 session.update 中通过 session.delegation 字段声明。两者的核心区别是是否支持 function call:
| 模式 | function call | 上下文注入 | 适用场景 |
| responses | ✅ 模型将工具推理委托给服务端工具推理服务,下发 function_call_arguments.done,客户端执行后回传。 | ❌ | 需要调用外部工具(天气、查询、本地数据)。 |
| client | ❌ 不会触发 function call,tools 被忽略。 | ✅ 通过 conversation.item.create(role=system/assistant) | 纯语音对话,背景信息由客户端主动注入。 |
注意:说明:
注意:使用 responses 模式需要为账号开通工具推理能力;未配置时首次 session.update 回 error(code=pipeline_init_failed) 并断开连接。
两种模式的交互时序对比:
responses 模式(需回传工具结果并再发 response.create):
client 模式(无 function call,背景信息由客户端注入):
51、9.2.1 显式声明
支持以下写法(取值大小写不敏感):
// 简写{ "type": "session.update", "session": { "delegation": "responses" } }// 原生对象(仅读取 type 字段){ "type": "session.update", "session": { "delegation": { "type": "responses" } } }// 显式 null:强制 client 模式(覆盖账号默认配置){ "type": "session.update", "session": { "delegation": null } }
非法值(既不是 client 也不是 responses)视为未声明,进入下一级自动决策,不会导致会话拒绝。
52、9.2.2 自动决策优先级
服务端按以下优先级选择模式(高 → 低):
1. 客户端显式声明的 session.delegation(含显式 null → client)。
2. session.tools 非空 → responses。
3. 账号维度默认配置。
4. 兜底 client。
决策流程可视化:
53、9.2.3 边界行为
·客户端声明 client 但同时携带了 session.tools:尊重客户端选择走 client,tools 被忽略(仅服务端日志 warn),不阻断会话。
·最终选中 responses 但账号未开通工具推理能力:首次 session.update 回 error(code=pipeline_init_failed) 并断开连接;此时建议改用 client 模式或联系管理员开通。
·responses 模式下 tool_choice 固定为 auto,客户端传入的 tool_choice 被忽略。
54、9.3 client 模式下的上下文注入
client 模式下,客户端可以在任意时刻(无需等待服务端请求)通过 conversation.item.create 向模型注入上下文。模型在需要外部信息时会下发
session.delegation.created(target=client),客户端可据此补充上下文,但这不是必需的。
服务端会自动为注入内容附带最近一次 target=client 的 delegation ID(尚未收到时按会话级上下文注入),客户端无需也无法自行指定 delegation_id(item 中携带该字段会被忽略)。
注意:说明:
注意:responses 模式下 conversation.item.create 的 item 按标准语义处理(用于 function_call_output 回传等),不做下表的 role 映射。
55、9.3.1 支持的 role 语义
| item.role | 语义 | 适用场景 |
| system | 静默上下文,模型会用作思考依据但不会主动读出来。 | 注入用户画像、时间地点、外部查询结果等。 |
| assistant | 让模型主动说出的一段话。 | 定制开场白、播报事件通知。 |
| user / 其他 / 缺省(如 function_call_output) | 忽略,无回执(仅服务端日志)。 | — |
注意:说明:
注意:本模式下服务端不会回 conversation.item.created;文本为空的 item 同样被忽略且无回执。
上下文注入的完整闭环时序:
注意:system 与 assistant 两种 role 的区别:system 是静默注入(模型会思考但不发声),assistant 是主动说出(模型立即用 TTS 合成语音)。
56、9.3.2 item.content 写法
支持两种 GA 兼容写法(等价):
// 写法 A: input_text (推荐)"content": [{ "type": "input_text", "text": "…" }]// 写法 B: text"content": [{ "type": "text", "text": "…" }]
多个 content 块的 text 会被服务端直接拼接为单个字符串(不加分隔符);拼接后超过1800 个字符的部分会被截断(单次注入上限约 500 tokens)。content 中包含 input_image 时整条 item 回 error(code=not_supported)。
57、9.3.3 完整示例
示例 A:注入用户画像作为静默背景
{ "type": "conversation.item.create", "item": { "type": "message", "role": "system", "content": [{ "type": "input_text", "text": "用户身份: VIP; 语言偏好: 中文; 回复要简短" }] }}
效果:模型后续回答会考虑这些信息,但不会主动读出来。
示例 B:让模型主动开场白
{ "type": "conversation.item.create", "item": { "type": "message", "role": "assistant", "content": [{ "type": "input_text", "text": "你好,我是你的 AI 助手,请问有什么可以帮你?" }] }}
效果:模型立即用 TTS 说出这段文本。
示例 C:注入外部查询结果
{ "type": "conversation.item.create", "item": { "type": "message", "role": "system", "content": [{ "type": "input_text", "text": "北京当前晴, 25°C, 湿度 40%" }] }}
效果:模型基于新信息继续对话,比如回答"今天穿什么合适"。
58、9.4 优雅关闭与最终用量
注意:说明:
注意:适用范围:本节协议(session.close / session.closed)是本服务的私有扩展,四种能力均支持,放在本章仅为历史原因。
客户端可在断开 WebSocket 之前发送 session.close,让服务端返回本会话的总用量。
标准 Realtime GA 协议下客户端直接 ws.close() 无法感知服务端会话级总消耗。
59、9.4.1 上行 session.close
客户端主动关闭会话时发送:
{ "type": "session.close" }
无其他字段。服务端收到后会:
1. 停止后续上行处理;
2. 收尾当前会话:
·audio-og-realtime-2.1(mini) / audio-s-realtime-3.0 / audio-gg-3.8-live:若有进行中的 response,先执行一次 response.cancel(当前回复会被打断),再最多等待 10 秒收到该轮 response.done 以加最后一轮 usage。
·audio-og-live-1:等待模型侧结束会话,最多 10 秒(不打断当前回复)。
3. 下发一次 session.closed 事件携带本会话总用量。
4. 关闭 WebSocket 连接。
因此从发送 session.close 到收到 session.closed 最长可能约 10 秒。首次 session.update 之前发送 session.close 也会正常收到 session.closed(用量为 0)。
60、9.4.2 下行 session.closed
{ "type": "session.closed", "event_id": "evt_xxx", "reason": "client_request", "session_duration_seconds": 87.3, "usage": { "total_tokens": 1488, "input_tokens": 1442, "output_tokens": 46, "input_token_details": { "cached_tokens": 0, "cache_write_tokens": 0 }, "output_token_details": { "reasoning_tokens": 11 } }}
字段语义:
·reason:当前固定为 client_request。空闲超时、服务内部异常等服务端主动断开的场景下,session.closed 不会送达客户端,客户端只能观察到 WebSocket close。
·session_duration_seconds:会话时长(秒)。audio-og-live-1 优先使用模型侧统计的会话时长;其它能力或无该统计时,按上行音频累计时长计算(Opus 上行按解码后的时长)。
·usage:会话内各轮 response.done.response.usage 的累计值,字段结构与之一致(含 reasoning_tokens / cache_write_tokens 明细)。该字段总是存在;audio-og-live-1的 client delegation 下或从未产生 token 用量时各字段为 0。
61、9.4.3 推荐流程
兜底建议:服务端收尾最长约 10 秒,客户端发 session.close 后建议启动一个略大于10秒的定时器(如12秒),超时未收到 session.closed(例如网络异常)则主动 ws.close(),避免 UI 卡住。若业务更在意断开速度而非拿到总用量,可缩短该定时器,代价是可能收不到 session.closed。测试页里的 disconnect() 是这套流程的参考实现(定时器为5秒)。
注意:如果客户端直接 ws.close() 而不发 session.close,服务端无法下发 session.closed,但会话总用量仍会在服务端完整统计;如需获取可携带会话 ID 联系管理员。
62、9.5 ASR / 助手文本的客户端自行断句
audio-og-live-1 是"连续流"模型,用户 ASR 与助手 transcript 的 delta 边界只反映音频节奏,不是语义 turn 边界。服务端把每个 delta 携带的 start_ms / end_ms(以会话开始为零点的毫秒偏移)原样下发给客户端,不在服务端合成 .completed / .done 边界事件。
63、9.5.1 影响范围
受此策略影响、audio-og-live-1 下 不下发的事件:
·conversation.item.input_audio_transcription.completed
·conversation.item.input_audio_transcription.failed
·input_audio_buffer.speech_started / .speech_stopped
·input_audio_buffer.committed
·response.output_audio.done
·response.output_audio_transcript.done
对应会额外携带 start_ms / end_ms 、且不带 item_id / response_id 的下行事件:
·conversation.item.input_audio_transcription.delta(用户 ASR)
·response.output_audio_transcript.delta(助手文本)
·response.output_audio.delta(助手音频;不带 start_ms / end_ms,仅不带 ID)
注意:说明:
注意:start_ms / end_ms 为0时字段会被省略,客户端应按缺省为0处理。
64、9.5.2 推荐的客户端断句算法
·维护 { curItemID, lastEndMs }:
·curItemID 为当前合成中的 utterance 本地 ID。
·lastEndMs 为当前 utterance 最后一条 delta 的 end_ms。
·收到新的 delta { start_ms, end_ms, delta }:
·若 curItemID 为空 或 start_ms – lastEndMs > GAP_MS(经验值 600~1000ms):
·认为上一句已结束,把 curItemID 对应的 UI 条目标为稳态。
·分配新的 curItemID(例如 client-asr-${serial},client-llm-${serial})。
·把 delta 追加到 curItemID 的缓冲,刷新 UI 为非稳态;
·更新 lastEndMs = end_ms。
ASR 与助手 transcript 两条通道互相独立,各自维护一套状态。
注意:说明:
注意:其它能力系列不受影响:audio-og-realtime-2.1(mini) 与 audio-s-realtime-3.0 仍按标准 Realtime GA 下发完整的 .completed / .done 事件,start_ms / end_ms 不保证存在,客户端应按能力系列分支处理:收到 delta 带 start_ms 则走时间戳断句;否则沿用 .completed / .done 稳态事件驱动。
65、9.5.3 参考实现
浏览器测试页中有两个对称的最小参考实现(均 gapMs = 800):
·handleClientSynthAsrDelta():用户 ASR 断句,生成 client-asr-${serial} 本地 ID。
·handleClientSynthAssistantDelta():助手文本断句,生成 client-llm-${serial} 本地 ID。
两个函数共享相同的模板:按 start_ms – lastEndMs > gapMs 切换条目,将上一条标为稳态后开启新条目。
66、9.6 完整交互时序(client / responses 两种模式)
audio-og-live-1 的下行时序与 4.2 典型时序 相比有若干关键差异:不下发 transcription.completed / speech_started / speech_stopped / input_audio_buffer.committed / response.output_audio.done / response.output_audio_transcript.done;所有 ASR 与助手 transcript delta
额外携带 start_ms / end_ms;response.done 行为随 delegation.type 而不同。本节分别给出两种模式的完整时序。
注意:约定:
67、9.6.1 client delegation 模式
模型自己完成推理与 TTS;需要外部信息时下发 session.delegation.created(target=client),客户端可通过 conversation.item.create(role=system/assistant)回灌。本模式不下发 response.done,response.created 仅在会话首轮下发一次,客户端通过 ASR / assistant transcript 的 start_ms / end_ms 时间戳 gap 自行判定 turn 边界。
68、9.6.2 responses delegation 模式
模型把工具推理委托给服务端工具推理服务;每轮推理完成时服务端立即下发一次 response.done(含 token usage)。多轮 function_call 场景下每轮独立下发一次 response.done 传 function_call_output 后 function_call_output 必须再发 response.create 才会续接。
69、9.6.3 两种模式快速对照
| 维度 | client delegation | responses delegation |
| 工具推理 | ❌ 无 | ✅ 服务端工具推理服务 |
| 工具能力 | ❌ 不支持(tools 被忽略) | ✅ 支持 tools + function_call |
| 上下文注入 | ✅ conversation.item.create(role=system/assistant) | ❌ item 按标准语义处理(用于回传 function_call_output) |
| session.delegation.created | target: "client" | target: "responses" |
| response.created | 仅会话首轮一次 | 每轮一次 |
| response.done 下发 | ❌ 不下发(客户端自行断句) | ✅ 每轮推理完成立即下发 |
| Token usage | ❌ 无(session.closed.usage 各字段为 0) | ✅ response.done.response.usage 携带每轮 token |
| 典型适用场景 | 快速接入、依赖客户端状态的对话 | 需外部工具查询(天气、股价、本地数据) |
70、9.7 与其它能力系列的对照速查
完整的模型×事件支持矩阵集中在 2.5 模型能力矩阵 一节提供,此处仅给出 audio-og-live-1 最需关注的行为差异:
·VAD 强制启用:turn_detection 被忽略;input_audio_buffer.commit 与 response.cancel 无回执(no-op),由内置 VAD 自动切分轮次与内置 barge-in 处理;output_audio_buffer.clear 只回 cleared,无法打断当前回复。
·response.create 语义随 delegation 而异:见 9.1。
·response.done 语义不同:仅 responses delegation 下在每轮推理完成时立即下发,含 token usage;client delegation 下不下发。
·不下发 ASR / 助手文本的边界事件:transcription.completed / speech_started /speech_stopped / input_audio_buffer.committed / response.output_audio.done /response.output_audio_transcript.done 均不下发;ASR / assistant transcript delta 额外携带 start_ms / end_ms,客户端自行断句,见 9.5。
·专属协议扩展:
·session.delegation:client / responses 双模式(见 9.2)。
·conversation.item.create 在 client 模式下用于上下文注入(见 9.3)。
·session.delegation.created 下行事件。
·不支持图片输入:input_image content 块会直接回 not_supported,不会静默丢弃图片部分后只用文本;需图片能力时请切换到 audio-og-realtime-2.1(mini) 或 audio-gg-3.8-live。
·非致命错误不下发:模型侧的非致命错误(如工具推理服务异常)仅记录在服务端,会话继续(工具调用可能失效);只有连接级异常时客户端才会收到 upstream_error /upstream_closed 并断开。
71、10. audio-gg-3.8-live 接入说明
audio-gg-3.8-live 沿用标准 Realtime GA 事件名、字段结构与 response 生命周期事件(response.created → output_item.added → output_audio.delta → *.done → response.done),按 第 4 章 编写的客户端可以直接接入。本章列出需要关注的行为差异,完整 支持矩阵见 2.5。
建连示例:
wss://{host}/ete/v1/{appid}?model=audio-gg-3.8-live&secretId=…&signature=…&timeStamp=…&expired=…&nonce=…
注意:兼容说明:
注意:历史别名 audio-gem-3.8-live(任意大小写)仍可继续使用,服务端会自动归一为audio-gg-3.8-live,行为与计费完全一致。新接入请使用 audio-gg-3.8-live。
72、10.1 会话参数
·一次性生效:instructions / voice / tools / language 均在首次 session.update 时生效,后续 session.update 不生效(所有能力一致)。如需变更请重新建立连接。
·音色(session.audio.output.voice):仅支持以下内置音色,大小写不敏感;不在列表内(如 alloy)时静默回退到默认音色,不中断建连:
Zephyr / Puck / Charon / Kore / Fenrir / Leda / Orus / Aoede / Callirrhoe /
Autonoe / Enceladus / Iapetus / Umbriel / Algieba / Despina / Erinome / Algenib /
Rasalgethi / Laomedeia / Achernar / Alnilam / Schedar / Gacrux / Pulcherrima /
Achird / Zubenelgenubi / Vindemiatrix / Sadachbia / Sadaltager / Sulafat
·转写语言(session.audio.input.transcription.language):可选,作为 ASR 语言提示,推荐 BCP-47 形式(如 zh-CN / en-US);不传时自动识别。
·工具(session.tools):遵循标准 GA Function Tool schema(name / description /parameters),仅支持 type=function,其它类型被忽略。
·VAD:服务端强制启用,turn_detection 被忽略。
·音频格式:上下行格式与采样率同 第 7 节,由服务端统一转换。
·会话时长:本服务不限制会话总时长,但模型服务对单会话时长可能有上限;达到上限时连接以 error(code=upstream_closed) 断开,客户端需重新建连(上下文不会保留)。
73、10.2 下行事件差异
| 事件 | 行为 |
| input_audio_buffer.speech_started / speech_stopped / committed | 不保证下发(依赖模型服务是否提供语音活动信号)。客户端请勿以此作为必需的状态驱动,推荐以 response.created / transcription.delta 作为"用户已说话 / 模型已开始回复"的信号。 |
| conversation.item.input_audio_transcription.delta | ✅ 追加式增量 |
| conversation.item.input_audio_transcription.completed | ✅ 由服务端合成,transcript 为本段用户话语各 delta 的拼接;在助手开始回复时下发(模型未回复时,在用户开始下一段话或本轮结束时下发)。不带 usage。 |
| conversation.item.created | ❌ 不下发(含 conversation.item.create 的回执)。 |
| response.created / response.done | ✅ 每轮回复各一次;response_id 由服务端生成。 |
| response.output_audio_transcript.delta / .done | ✅ .done 的 transcript 为本轮各 delta 的拼接。 |
| output_audio_buffer.started | ❌ 不下发。 |
| output_audio_buffer.stopped | ✅ 在本轮音频结束时下发。 |
74、10.3 response.create 语义
| 场景 | 行为 |
| 带 response.instructions | instructions 作为一条用户文本输入发给模型,模型据此回复(不是逐字朗读)。用于"主动打招呼"时请写成指令形式,如 "请用一句话向用户打招呼"。 |
| 不带 instructions,且之前通过 conversation.item.create 追加过 message | 触发模型基于已追加内容回复。 |
| 不带 instructions,其它情况 | 静默 no-op,无回执(function call 回传后模型会自动续接,无需发送)。 |
conversation.item.create(type=message)只追加上下文、不会自动触发回复,与标准 GA 一致;role=assistant 作为模型历史发言,user / system 均作为用户输入。
注意:触发回复(带 instructions 或追加 message 后的 response.create)时,若当前有回复正在进行,该回复可能被打断并以 response.done(status=cancelled) 结束。
75、10.4 打断与取消
·用户说话打断(barge-in):模型正在回复时用户开始说话,当前回复会被自动打断,服务端立即下发 response.output_audio.done / response.output_audio_transcript.done(如有)与 response.done.status=cancelled)。客户端收到后应清空本地播放缓冲。
·response.cancel / output_audio_buffer.clear:服务端立即以 status=cancelled 结束当前 response,并丢弃该轮后续的音频 / 文本 / function call;output_audio_buffer.clear 额外回 output_audio_buffer.cleared。无进行中的 response 时 response.cancel 为 no-op,无回执。
·被取消轮次的 usage 可能不完整,未结算部分会计入下一轮 response.done.usage。
76、10.5 图片输入
请求写法同 5.6 用途 3 · 写法 A,差异如下:
| 项目 | audio-gg-3.8-live |
| image_url | 仅支持 Data URL:data:image/{png,jpeg,webp,heic,heif};base64,…(image/jpg 视同 image/jpeg)。也兼容 {"url": "data:…"} 对象写法。 |
| HTTP(S) URL | ❌ 不支持,回 error(code=invalid_image)。 |
| gif 等其它格式 | ❌ 不支持,回 error(code=invalid_image)。 |
| 单张大小 | 解码后 ≤ 7 MB,超过回 error(code=invalid_image);建议 ≤ 2 MB 以降低延迟。 |
| detail | 忽略。 |
| 回执 | 不下发 conversation.item.created;成功时无回执,失败时回 error(code=invalid_image)。 |
| 校验失败 | 整条 item 不生效(文本也不会发送),不会静默丢弃图片只发文本。 |
发送后需主动发 {"type": "response.create"} 触发回复。图片 tokens 计入 response.done.response.usage.input_token_details.image_tokens。
77、10.6 Function Call
流程同 第 8 节,差异:
·模型发起调用时下发 response.function_call_arguments.done(带 response_id / item_id /call_id / name / arguments),随后立即下发本轮 response.done。
·客户端通过 conversation.item.create(type=function_call_output) 回传结果,call_id 须与下行一致;output 推荐使用 JSON 字符串(如 "{\\"time\\":\\"09:30\\"}"),非 JSON 字符串按纯文本处理。
·模型收到结果后自动续接,以新的一轮 response(新 response_id)播报结果,无需发送 response.create。
·一轮可能同时触发多个 call,每个 call 需分别回传。
·模型可能在等待工具结果时继续与用户对话,客户端请勿假设"function call 之后的下一轮 response 一定是工具结果的播报"。
78、10.7 用量
response.done.response.usage 与 session.closed.usage 结构同 Q11:
·input_token_details:text_tokens / audio_tokens / image_tokens / cached_tokens(适用时)。
·output_token_details:text_tokens / audio_tokens / reasoning_tokens(适用时,为 text_tokens 的内含子集)。
·不下发 cache_write_tokens。
81、11.1 握手阶段的 HTTP 拒绝码
在 WebSocket 升级完成前的鉴权与并发检查阶段,任一失败会以 HTTP 状态码拒绝:
| HTTP 状态 | Reason Phrase | 触发原因 |
| 400 | invalid_request: … | URL 参数缺失 / appid 非法 / model 值非法。 |
| 401 | invalid_timestamp | timeStamp 或 expired 已过期或格式错误。 |
| 401 | authentication_failed: code=<n> | CAM 签名校验失败。 |
| 403 | account_invalid | 账户欠费 / 冻结。 |
| 429 | connection_limit_exceeded: uin=… num=… max=… | 并发连接数超上限。 |
| 500 | authentication_failed: <err> | CAM 后端调用异常。 |
| 500 | check_user_status_error: <err> | 账户状态查询异常。 |
| 500 | limit_check_error: <err> | 并发限流查询异常。 |
客户端应根据这些状态码提示用户重试 / 联系管理员。
82、11.2 会话阶段的 error 事件
握手成功后,业务错误通过 JSON error 事件下发:
| error.type | error.code | 说明 |
| invalid_request_error | invalid_json | 消息不是合法 JSON。 |
| invalid_request_error | invalid_audio | 上行音频 base64 解码或解码器解析失败。 |
| invalid_request_error | unsupported_output_format | 下行 format 非 pcm / opus,或 opus 采样率不在白名单。 |
| invalid_request_error | not_supported | 使用了当前会话能力下不支持的控制事件(如 conversation.item.truncate / transcription_session.update / 不带 instructions 的空 response.create)。 |
| invalid_request_error | session_not_ready | 未先发 session.update 就送控制事件(response.create / conversation.item.* 等)。 |
| invalid_request_error | missing_item | conversation.item.create 缺 item 字段。 |
| invalid_request_error | missing_item_id | conversation.item.retrieve / delete 缺 item_id 字段。 |
| server_error | pipeline_init_failed | 服务端会话初始化失败。 |
| server_error | upstream_send_error | 服务端向能力后端发送数据失败。 |
| server_error | upstream_error | 能力后端返回异常。 |
| server_error | upstream_closed | 能力后端主动关闭。 |
| session_expired | idle_timeout | 会话空闲超时(默认 30 s,可通过 timeoutSec 覆盖)。 |
84、Q1: 为什么建连成功后什么事件都没有收到?
本服务采用“客户端主导”模式,握手成功后服务端不主动下发任何事件。
客户端必须先发一条 session.update 完成会话初始化,服务端才回 session.updated(作为“首次可见响应”),此后才能开始送音频。这与标准 GA 规范中 session.created 先行的行为有意不同,为的是更符合“客户端主动”的语义。
85、Q2: 客户端签名总是 401,如何排查?
优先检查:
1. 本机时钟偏差:TC3 允许 timeStamp 与服务器差异不超过 15 分钟,先用 date -u 校准机器时间。
2. 参与签名的参数集:除 signature 外的所有 query 参数都要参与签名。
timeoutSec / model / resId 只要出现在 URL 上就必须一并进入 canonicalQueryString。
3. 参数排序:keys 必须按字母升序,然后逐个 k=urlEncode(v) 用 & 拼接。
4. canonicalRequest 空行:content-type;host 之前必须有一个空行(\\n\\n)。
5. date 使用 UTC:不要用本地时区。
86、Q3: 上行只能发 JSON 吗?为什么发 Binary 帧被忽略?
是的,本服务与标准 Realtime GA 规范保持一致,只接受 WebSocket Text 帧承载的 JSON 事件。音频通过 input_audio_buffer.append 事件里的 audio 字段以 base64 承载。
87、Q4: 如何让服务端主动打招呼?
在 session.updated 之后立即发:
{ "type": "response.create", "response": { "instructions": "你好, 我是智能助手, 请问需要什么帮助?" } }
所有 model 都支持这种“客户端指定文本,让服务端主动说出”的用法。
88、Q5: 客户端下行想要 opus 而不是 pcm,怎么做?
在 session.update.audio.output.format 中声明:
{ "format": { "type": "opus", "rate": 48000 } }
采样率必须是 Opus 白名单:8000 / 12000 / 16000 / 24000 / 48000。返回的 response.output_audio.delta.delta 字段是裸 Opus packet 的 base64(一次 delta 通常对应一个20ms Opus 帧),客户端需用 Opus 解码器还原为 PCM。
89、Q6: conversation.item.truncate 报 not_supported,如何截断已生成的助手音频?
当前会话若回复了 not_supported,说明实际生效的 model 未提供原生的 truncate 能力。推荐替代方案:
·打断当前 TTS:发 response.cancel。
·清空已下发但未播完的音频:发 output_audio_buffer.clear,服务端会一并下发 output_audio_buffer.cleared,客户端本地清空播放调度即可。
90、Q7: 单次会话最长多久?
本服务不限制会话总时长,只做空闲超时:默认30秒无收发消息即断开。
可通过 URL 参数 timeoutSec 覆盖,上限120秒。心跳发法建议:客户端在语音间歇期偶发送20ms的静音 PCM input_audio_buffer.append 即可保活。
91、Q8: 有 Python / Java / iOS / Android 的 SDK 吗?
本服务对外协议 100% 兼容 标准 Realtime GA WebSocket API,因此任何按标准 GA 规范编写的客户端 SDK 都可以直接接入,只需改动:
·WebSocket URL 换成本服务的 URL。
·增加 TC3-HMAC-SHA256 签名逻辑。
·session.audio.output.format 使用本服务支持的格式。
92、Q9: 如何使用加密音色 ID?和明文音色可以混用吗?
session.audio.output.voice 字段同时支持明文音色 ID 与加密音色 ID,服务端会自动识别并处理:
·明文音色 ID:直接使用。
·加密音色 ID(由音色加密服务生成):服务端通过内部解密接口换回真实音色 ID 后再使用。
Fallback 策略:以下情况都会静默降级到服务端配置的默认音色,不会中断会话建立:
1. 服务端解析异常 / 超时(内部会重试一定次数,仍失败则 fallback)。
2. 明文音色 ID(服务端无需解密,直接使用)。
3. 音色 ID 不适用于当前 model(例如把一个 model 下的专用音色传给了另一个 model)。
4. 音色 ID 不在当前 model 可用音色集内。
因此客户端可以放心把任何合法音色 ID 直接填进去,服务端会尽力解析并给出最好的结果;只有当所有路径都失败时才用服务端默认音色。
93、Q10: response.done.response.usage 与 transcription.completed.usage 分别怎么计费?
两个字段承载两个独立并列的计费维度:
·conversation.item.input_audio_transcription.completed.usage
= 本次 ASR 转写的按秒用量(type="duration" + seconds);部分能力会携带此字段,其余情况缺省。
·response.done.response.usage
= 本轮 response 的按 token 用量明细(total_tokens / input_tokens / output_tokens / *_token_details);能力后端未返回 usage 时该字段缺省。
客户端可用于本地成本观测;实际计费以账单为准。
94、Q11: 客户端可观测的 usage 字段有哪些?
response.done.response.usage(每轮)与 session.closed.usage(会话累计,所有能力)字段结构对齐 Realtime GA response.usage,客户端可据此展示完整明细:
input_token_details: text, audio, image, cached, cache_write └── cached_tokens_details: text, audio, imageoutput_token_details: text, audio, reasoning
各字段语义与标准 Realtime GA 一致;无用量数据时字段缺省(omitempty)。
关于 reasoning_tokens 的加总关系:
output_token_details.reasoning_tokens 是 output_token_details.text_tokens 的内含子集(不是与 text / audio 并列的第三项),代表本轮回答里被消耗在推理阶段的 tokens 数量。加总关系:
output_tokens = output_token_details.text_tokens + output_token_details.audio_tokensreasoning_tokens ≤ output_token_details.text_tokens // 内含关系
例如某轮 output_tokens=1260、text_tokens=314、audio_tokens=946、reasoning_tokens=87,可验证 314 + 946 = 1260;reasoning=87 已计入 text=314。客户端做本地用量展示时,按 text / audio 两项拆分即完整覆盖output_tokens reasoning_tokens 作观测字段展示、不重复计入总量。
95、附录:完整可运行 Demo
浏览器 Demo:测试 Demo (单文件,零依赖,单击打开即可)。