test/docs/realtime_meeting_websocket_...

298 lines
7.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# 实时会议 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`