# 实时会议 WebSocket 对接文档 本文档用于客户侧接入实时会议识别服务,包含接口地址、消息格式、返回示例和重连约定。 ## 1. 接口地址 - 推荐地址:`/ws/v1/asr` - 显式地址:`/ws/v1/asr/qwen` 说明: - 当前协议为 JSON 控制消息 + 音频二进制流。 - 返回结构按实时会议场景组织,核心消息类型为:`voice_id`、`start`、`sentences`、`end`、`error`。 ## 2. 交互流程 客户端调用顺序: 1. 建立 WebSocket 连接 2. 发送 `start` 文本消息 3. 持续发送音频二进制数据 4. 持续接收服务端返回的 `voice_id`、`start`、`sentences` 5. 识别结束时发送 `stop` 6. 接收最终 `end` ## 3. start 请求 ### 3.1 请求示例 ```json { "type": "start", "payload": { "format": "pcm", "sample_rate": 16000, "session_id": "meeting-abc-123", "language": null, "context": "", "enable_inverse_text_normalization": true, "silence_duration_ms": 800, "min_partial_sec": 0.3, "pre_roll_ms": 240, "max_sentence_count": 8, "partial_holdback_chars": 2, "unfixed_token_num": 3, "enable_native_partial_stream": true, "enable_speaker": true, "match_speaker_registry": true, "speaker_threshold": 0.6, "enable_realtime_vad_split": true, "enable_realtime_longform": false, "force_stable_segment_sec": 6, "force_stable_min_chars": 24, "max_segment_sec": 12 } } ``` ### 3.2 主要参数说明 | 字段 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | | `format` | string | `pcm` | 建议传 `pcm` | | `sample_rate` | number | `16000` | 音频采样率 | | `session_id` | string | 可不传 | 会话标识;断线重连时传同一个值可恢复同一会议会话 | | `language` | string/null | `null` | 留空表示自动识别 | | `context` | string | `""` | 业务上下文提示,如会议主题、术语 | | `enable_inverse_text_normalization` | boolean | `true` | 是否启用数字归一化 | | `silence_duration_ms` | number | `800` | 静音多久触发收段 | | `min_partial_sec` | number | 后端默认 | 最短 partial 窗口 | | `pre_roll_ms` | number | `240` | 句首预读时长 | | `max_sentence_count` | number | `8` | 单段最多句数 | | `partial_holdback_chars` | number | 后端默认 | partial 尾部保留字数,减少尾字抖动 | | `unfixed_token_num` | number | 后端默认 | native partial 模式下回滚 token 数 | | `enable_native_partial_stream` | boolean | `false` | 是否启用原生流式 partial | | `enable_speaker` | boolean | `true` | 是否开启说话人分离 | | `match_speaker_registry` | boolean | `false` | 是否匹配已注册声纹库 | | `speaker_threshold` | number/null | `null` | 本次声纹匹配阈值 | | `enable_realtime_vad_split` | boolean | `false` | 是否启用实时 VAD 拆段 | | `enable_realtime_longform` | boolean | `false` | 是否启用长段重转写 | | `force_stable_segment_sec` | number | 后端默认 | 超时后软提交阈值 | | `force_stable_min_chars` | number | 后端默认 | 软提交最少字数 | | `max_segment_sec` | number | 后端默认 | 单段最长秒数 | 说明: - `enable_speaker=true` 时,服务端会先返回文本,再异步补充 speaker 归属。 - `match_speaker_registry=true` 时,若匹配到声纹库,会返回实名 speaker 信息。 ## 4. 音频发送 `start` 成功后,客户端持续发送音频二进制帧。 推荐格式: - 单声道 PCM - 16k 采样率 - 小块连续发送 示例: ```js ws.send(pcmChunkArrayBuffer); ``` ## 5. stop 请求 ```json { "type": "stop" } ``` ## 6. 服务端返回 ## 6.1 `voice_id` 连接建立并收到 `start` 后,服务端先返回: ```json { "type": "voice_id", "voice_id": "41ff1926", "session_id": "meeting-abc-123" } ``` 说明: - `voice_id` 为本次实时识别标识。 - `session_id` 为本次会话标识;若未传,服务端会返回自动生成值。 ## 6.2 `start` 服务端确认开始识别: ```json { "type": "start", "session_id": "meeting-abc-123" } ``` ## 6.3 `sentences` 识别过程中会持续返回 `sentences`。 重要说明: - `sentence_type=0` / `slice_type=1` 表示实时 partial,仅用于实时展示。 - `sentence_type=1` / `slice_type=2` 表示该段最终定稿,建议作为最终入库依据。 - 说话人可能异步补写,不保证和首个 final 同时返回。 ### partial 返回示例 ```json { "type": "sentences", "code": 0, "voice_id": "41ff1926", "final": 0, "result": { "slice_type": 1, "index": 3, "voice_text_str": "今天这个会议主要讨论预算。" }, "sentences": [ { "sentence_id": 3, "sentence_type": 0, "speaker_id": -1, "start_time": 12000, "end_time": 15600, "sentence": "今天这个会议主要讨论预算。" } ] } ``` ### final 返回示例 ```json { "type": "sentences", "code": 0, "voice_id": "41ff1926", "final": 0, "result": { "slice_type": 2, "index": 3, "voice_text_str": "今天这个会议主要讨论预算。" }, "sentences": [ { "sentence_id": 3, "sentence_type": 1, "speaker_id": -1, "start_time": 12000, "end_time": 15880, "sentence": "今天这个会议主要讨论预算。", "speaker_name": "", "user_id": null } ] } ``` ### speaker 回写示例 如果 speaker 后续识别完成,服务端会再次返回相同 `sentence_id` 的消息,只更新 speaker 信息: ```json { "type": "sentences", "code": 0, "voice_id": "41ff1926", "final": 0, "result": { "slice_type": 2, "index": 3, "voice_text_str": "今天这个会议主要讨论预算。" }, "sentences": [ { "sentence_id": 3, "sentence_type": 1, "speaker_id": 19, "start_time": 12000, "end_time": 15880, "sentence": "今天这个会议主要讨论预算。", "speaker_name": "Alan Paine", "user_id": "3" } ] } ``` 接入建议: - 客户端按 `sentence_id` 更新句子,不要把 speaker 回写当成新句子追加。 - `speaker_id=-1` 代表当前 speaker 暂未确认,不代表识别失败。 ## 6.4 `end` 识别结束后返回: ```json { "type": "end", "code": 0, "message": "", "voice_id": "41ff1926", "session_id": "meeting-abc-123", "final": 1, "result": { "slice_type": 2, "index": 5, "voice_text_str": "完整会议文本" }, "sentences": [] } ``` ## 6.5 `error` 异常时返回: ```json { "type": "error", "code": "INVALID_STATE", "message": "请先发送 start", "voice_id": "41ff1926" } ``` ## 7. 断线重连 支持基于 `session_id` 的会话恢复。 约定如下: 1. 首次连接时,客户端可以自行生成 `session_id` 并放入 `start.payload.session_id` 2. 若客户端未传,服务端会返回自动生成的 `session_id` 3. 断线重连时,客户端使用同一个 `session_id` 再次发送 `start` 4. 若服务端会话仍在保活期内,则恢复同一会议上下文 建议: - 客户端在会议生命周期内保持 `session_id` 稳定 - 网络抖动或页面刷新后,优先使用上一次会话的 `session_id` 重连 ## 8. 客户端接入建议 - 实时展示可消费 `sentence_type=0` 的 partial - 业务落库建议只以 `sentence_type=1` 的 final 为准 - 若后续收到相同 `sentence_id` 的 final 更新,应执行更新而不是新增 - 若需要最佳实时体验,建议开启 `enable_native_partial_stream=true` - 若希望 speaker 识别更完整,建议开启 `enable_speaker=true`