🤖 给 AI 的参考
查看 .md 原文
这是 Hearo(听见)的《WebSocket》文档,可作为开发参考。文档链接:https://hearo.apps.aisp24.com/docs/websocket.md

WebSocket 接入

最通用的实时音频通道:连接、控制消息、事件、音频格式

WebSocket 是最简单通用的接入方式:建立一条全双工连接,二进制帧发送麦克风音频,**文本帧(JSON)**收发控制消息与事件。适合自定义客户端、IoT、服务器中转。

1. 建立连接

WS wss://hearo-gw.apps.aisp24.com/v1/connect?token=<客户端 Token>&...

ℹ️ 本部署的网关地址wss://hearo-gw.apps.aisp24.com/v1/connect(下方示例已代入,可直接复制使用;地址由平台管理员在 运营中心 → 平台设置 → 接入地址 配置)。

查询参数(除 token 外均可选):

参数默认说明
token必填客户端 Token(见 鉴权与 Token
encodingpcm_s16le上行编码:pcm_s16le | opus(别名 pcm/pcm16/l16 同 pcm_s16le)
sample_rate16000上行采样率。pcm_s16le:16000 / 24000 / 48000;opus:必须 48000
channels1声道数:1 或 2
frame_duration_ms20帧时长:10 / 20 / 40
downlink_codecpcm_s16le下行编码:pcm_s16le | opus
name在房间花名册(roster)中显示的昵称
lang你说的语言(如 zh-CN / en),供字幕/翻译使用(见 实时字幕
caption_targets连接即开启本路字幕,翻译为这些语言(逗号分隔);也可连接后用 caption_start 开启
session_id自动生成会话标识,断线重连时带上同一个值
resume_from_seq0重连续传的起始帧序号(配合 session_id)
javascript
const url =
  "wss://hearo-gw.apps.aisp24.com/v1/connect" +
  "?token=" + encodeURIComponent(token) +
  "&encoding=pcm_s16le&sample_rate=16000&channels=1" +
  "&frame_duration_ms=20&downlink_codec=pcm_s16le";

const ws = new WebSocket(url);
ws.binaryType = "arraybuffer";

连接成功后,服务端先发一条 ready 文本帧(回显协商结果):

json
{ "type": "ready", "session_id": "...", "resume_from_seq": 0,
  "encoding": "pcm_s16le", "sample_rate": 16000, "channels": 1,
  "frame_duration_ms": 20 }

进入哪个房间由平台的 client→room 映射决定(Token 里不含房间),见 客户端与接入模型

2. 发送音频(二进制帧)

按协商好的格式分帧、作为二进制 WebSocket 帧发送即可。默认格式(16kHz / 单声道 / s16le)下 20ms 一帧 = 320 采样 = 640 字节。选 encoding=opus 时发送 48kHz 单声道 Opus 包。无论上行是什么格式,gateway 都会在边缘解码/重采样成统一的 16k 单声道 PCM 再进房间。

💡 按 ~1x 实时节奏发送:不要把整段音频一次性灌入;按帧、按实时节奏发送,打断(barge-in)和延迟才正常。

3. 控制消息(客户端 → 服务端,文本帧)

gateway 只在本地处理少数几种控制消息,其余原样透传给房间和房间里的 AI 智能体:

type处理方说明
pinggateway心跳,服务端回 {"type":"pong"}
caption_start / caption_stopgateway开/关本路实时字幕(见 实时字幕
audiogateway可选的二进制帧头 {"type":"audio","seq":...}:为紧随其后的二进制帧标注序号
configgateway + 智能体更新音频格式 / 实时调智能体模式与 VAD(见 交互模式与 VAD
speech_start / turn_start智能体开始说话/新轮次:打断当前回答并开始新一轮录音
speech_end智能体结束说话(PTT 松手):停止送入 ASR 并收尾本句
commit智能体显式收尾当前话语(PTT 释放)
interrupt智能体立即取消 AI 当前回答(LLM+TTS)
javascript
ws.send(JSON.stringify({ type: "interrupt" }));     // 打断 AI
ws.send(JSON.stringify({ type: "speech_start" }));  // 开始新一轮

ℹ️ 交互模式决定该发哪些控制:按住说话(ptt) 用 speech_start/speech_end;自然对话(natural) 由服务端 VAD 自动判断,通常无需手动发。详见 交互模式与 VAD

参与者发送的 kick 控制不会执行。精确断开连接属于服务端运营权限, 只能通过 Console 到 Room 的私有、鉴权操作接口发起。

4. 事件(服务端 → 客户端)

文本帧为 JSON 事件,按 type 区分:

type字段说明
transcripttext, is_final, confidenceASR 识别结果;is_final 前为流式部分结果
captionid, speaker, source_lang, text, translations, is_final实时字幕/翻译(见 实时字幕
turn_startedturn_idAI 开始回答
assistant_deltaturn_id, deltaLLM 流式文本增量
audioturn_id, seq, sample_rate, channels, encoding, bytes紧随其后的二进制音频帧的头
turn_completeturn_id, textAI 回答结束
turn_cancelledturn_id, reason被打断/中断(reason=barge_in 等)
session_idle多轮模式空闲超时
rostermembers[]房间成员变化({id,name,kind:"human"|"agent"}),进房即收到一份全量快照
kicked你被踢出了房间
pong对 ping 的应答
errormessage错误

下行音频:每段合成音频先来一条 {type:"audio", ...} 文本头,紧接着一个二进制帧(按 downlink_codec 编码,默认 16k PCM)。

javascript
let pendingAudio = null;
ws.onmessage = (ev) => {
  if (typeof ev.data === "string") {
    const msg = JSON.parse(ev.data);
    if (msg.type === "audio") pendingAudio = msg;          // 头
    else if (msg.type === "transcript") render(msg.text);
    else if (msg.type === "roster") renderMembers(msg.members);
    else if (msg.type === "turn_cancelled") stopPlayback(); // 打断:停播
  } else {
    play(ev.data, pendingAudio);   // 紧随头的二进制音频
    pendingAudio = null;
  }
};

5. 断线重连

重连时带上同一个 session_id,并把 resume_from_seq 设为你已成功发送的最后帧序号,服务端从该序号之后续收、避免重复帧。简单客户端也可以不管这两个参数——直接重连即可(按新会话处理)。

6. 邀请 AI

连上房间后,AI 智能体由控制面调度进房间(不是通过这条 WS):调用 POST /api/v1/agents(见 REST API)、在控制台「演示」页点「邀请 AI 加入」、或给房间配默认智能体(进房自动就位,见 房间与分组)。AI 进来后会出现在 roster 里(kind=agent)。

💡 对照实时调试台:控制台的「演示」页就是一个完整的 WebSocket 客户端参考实现(含重采样、分帧、播放、打断),可边读代码边对照行为。