跳到正文

WebSocket 协议

协议

健康检查

text
GET /health

成功响应(HTTP 200)

json
{
  "ok": true,
  "service": "sherpa-asr-online-server",
  "activeSessions": 3,
  "maxSessions": 16,
  "wsPath": "/audio",
  "ioWorkers": 4
}
  • 任何方法 / 路径不在 healthPath、不是 WebSocket 升级到 wsPath,一律 返回 404 JSON 体并关闭连接

WebSocket 升级

text
GET /audio HTTP/1.1
Host: <host>:<port>
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Version: 13
Sec-WebSocket-Key: <base64-16>
Sec-WebSocket-Protocol: audio.drachtio.org

握手要求:

  • Sec-WebSocket-Version: 13,否则 400
  • Connection: upgradeUpgrade: websocket,否则 400
  • server.wsSubprotocol 非空,请求必须在 Sec-WebSocket-Protocol 中带上 对应 token,否则 400
  • 通过 concurrency.maxSessions 限流;连接数已满返回 503,并丢弃该连接

成功响应

text
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: <base64-20>
Sec-WebSocket-Protocol: audio.drachtio.org

升级成功后,服务端立即把连接挂到一个 ASR session(recognizer + VAD 已就绪), 并打日志 ws-<n> websocket session started on worker=<i>

客户端消息

1. metadata 文本帧(opcode=0x1)

第一帧通常是任意 JSON 文本,服务端会把整段文本存到会话状态,并尽力解析以下 任一字段作为 channelUuid(用于日志与录音文件名):

text
uuid | channel_uuid | call_uuid | sessionId | session_id

可选字段 hotwordProfile 用于选择本 WebSocket session 的 hotword profile:

json
{
  "uuid": "fs-channel-uuid",
  "hotwordProfile": "sales"
}

选择规则:

  • hotwordProfile 命中 hotwordProfiles 时,本连接以及连接内后续 utterance reset 都使用该 profile 的 hotwords
  • metadata 未带 hotwordProfile 时,如果配置了 defaultHotwordProfile,使用 默认 profile;否则不启用 hotwords
  • hotwordProfile 未命中、为空或类型不合法时,本连接继续运行但不启用 hotwords,并写入 warning 日志
  • 只处理第一帧 metadata;后续文本帧不会改变本连接的 hotword profile

示例(mod_audio_fork 默认下发的字段):

json
{
  "uuid": "fs-channel-uuid",
  "callerId": "10086",
  "direction": "inbound"
}

后续文本帧默认丢弃,仅在 logging.debugTextFrames=true 时落日志。

2. 二进制音频帧(opcode=0x2)

携带 16kHz / 单声道 / PCM16 little-endian 原始采样数据。

  • 帧大小受 socket.maxFrameBytes 限制,超过即触发 1002 协议错误并关闭
  • 任意时间发送 payload.length == 0 的二进制帧,等价于“flush”:服务端 立刻 flush 当前语音段并发出 final(如有未结算文本)
  • 服务端不会做重采样;如果上游不是 16kHz,partial 将完全错乱

3. 控制帧

  • 0x8 Close:服务端先 flush final,再回送 1000 bye close 帧
  • 0x9 Ping:原样回送 Pong
  • 0xA Pong:直接忽略
  • 其它 opcode:发出 error 事件后回送 1003 unsupported opcode 并关闭

不支持分片帧(FIN=0 或 opcode=0x0),收到会以 1002 protocol error 关闭。

服务端事件

服务端通过 WebSocket 文本帧(opcode=0x1)下发 JSON 事件,按 type 分三类。

type=transcription

每次 partial 推送 / final 段完成 / 客户端 flush 都可能发出一条:

json
{
  "type": "transcription",
  "data": {
    "text": "你好世界",
    "isFinal": false,
    "utteranceIndex": 3,
    "speechStarted": true,
    "speechActive": true,
    "speechSegmentDetected": true,
    "speechSegmentCompleted": false,
    "serverSessionId": "ws-12",
    "channelUuid": "fs-channel-uuid",
    "metadataText": "{\"uuid\":\"fs-channel-uuid\",\"direction\":\"inbound\"}",
    "finalReason": "segment-completed",
    "flushReason": "client-close"
  }
}
字段说明
text当前识别文本(partial 或 final)
isFinaltrue 表示本句已结算;下一段从 utteranceIndex+1 开始
utteranceIndex同一连接内的语句序号,从 1
speechStartedbridge/VAD 是否真实检测到开始说话
speechActivebridge/VAD 当前是否真实处于说话中
speechSegmentDetectedbridge/VAD 是否真实检测到语音段
speechSegmentCompletedbridge/VAD 是否真实检测到段落结算完成(驱动 final)
serverSessionId服务端会话 ID(ws-<n>
channelUuid从 metadata 解析到的通道 UUID(无则字段缺省)
metadataText原始 metadata 文本(仅有 metadata 时附带)
finalReasonisFinal=true 时填:segment-completed / final / flush
flushReason由 flush 触发时填:audio-idle / client-close / zero-length-binary / socket-read-end

partial 节流:同一句话内,partial 文本最快每 asr.partialMinIntervalMs ms 下发 一次(文本未变化的事件不会重发)。partial 携带的是产生该 pending 文本时的真实 bridge/VAD flags,不会为便于消费而硬编码为 true。partial / final 文本会先 trim;纯空格、换行等空白识别结果不会作为文本事件或 idle final 下发。

可在服务启动后运行 node request-asr.js --silence,发送至少 3 秒零 PCM;脚本会在 收到非空 transcription 或任一伪 speech flag 时失败。

type=error

json
{
  "type": "error",
  "data": {
    "message": "ASR accept failed: <reason>"
  }
}

error 一般紧随其后是连接关闭:

  • ASR AcceptPcm16 失败:1011 内部错误
  • 识别处理失败:1011 内部错误
  • 协议错误(帧过大 / 不支持的 opcode):1002 / 1003

type=disconnect

当服务端因协议错误主动断开时,可能在关闭前先发一条:

json
{
  "type": "disconnect",
  "data": {
    "reason": "<message>"
  }
}

连接生命周期

text
客户端                       服务端
  |--- TCP 连接 ------------>|
  |--- HTTP GET /audio ----->|
  |<-- 101 Switching Proto --|
  |--- TEXT metadata ------->|         (获取 session;记录 channelUuid)
  |--- BIN PCM16 frame ----->|         (partial 推送)
  |<-- transcription partial-|
  |--- BIN PCM16 frame ----->|
  |<-- transcription final --|         (speech_segment_completed)
  |--- BIN PCM16 frame ----->|
  |<-- transcription partial-|
  |--- BIN zero-length ----->|         (flush)
  |<-- transcription final --|         (flushReason=zero-length-binary)
  |--- CLOSE frame --------->|
  |<-- CLOSE 1000 bye -------|

关闭路径:

触发行为
客户端发 close 帧flush final → 回 1000 bye → 关闭
客户端单方面 TCP 断开flush final(flushReason=socket-read-end)→ 释放 session
服务端流式异常error 事件 → 关闭码 1011
协议错误error + disconnect → 关闭码 1002 / 1003

文档与代码在同一仓库维护,现有 Markdown 是唯一内容源。