Document va.ws.v1 protocol, client messages, server events, and interrupted flag behavior for product clients. Co-authored-by: Cursor <cursoragent@cursor.com>
148 lines
3.8 KiB
Markdown
148 lines
3.8 KiB
Markdown
# Product WebSocket 使用说明
|
||
|
||
Product WebSocket(`/ws-product`)面向浏览器、移动端等产品客户端,使用稳定的 JSON/base64 协议 `va.ws.v1`。
|
||
|
||
## 连接地址
|
||
|
||
```
|
||
ws://<host>:<port>/ws-product
|
||
```
|
||
|
||
生产环境请使用 `wss://`。可通过 `GET /health` 查看当前支持的协议与能力。
|
||
|
||
## 音频参数
|
||
|
||
| 参数 | 默认值 |
|
||
|------|--------|
|
||
| 编码 | PCM signed 16-bit little-endian(`pcm_s16le`) |
|
||
| 采样率 | 16000 Hz |
|
||
| 声道 | 1(单声道) |
|
||
|
||
## 基本流程
|
||
|
||
1. 建立 WebSocket 连接
|
||
2. 发送 `session.start` 握手
|
||
3. 持续发送音频或文本输入
|
||
4. 接收服务端事件(转写、回复文本、TTS 音频等)
|
||
5. 结束时发送 `session.stop`
|
||
|
||
### 1. 启动会话
|
||
|
||
```json
|
||
{
|
||
"type": "session.start",
|
||
"protocol": "va.ws.v1",
|
||
"audio": {
|
||
"encoding": "pcm_s16le",
|
||
"sample_rate": 16000,
|
||
"channels": 1
|
||
}
|
||
}
|
||
```
|
||
|
||
### 2. 发送音频
|
||
|
||
**JSON + base64**(便于调试):
|
||
|
||
```json
|
||
{
|
||
"type": "input.audio",
|
||
"audio": "<base64 编码的 pcm_s16le 数据>",
|
||
"sample_rate": 16000,
|
||
"channels": 1
|
||
}
|
||
```
|
||
|
||
**二进制帧**:也可直接发送原始 PCM16 字节,延迟和带宽更优。字段 `audio` 与 `data` 均可作为 base64 字段名。
|
||
|
||
建议按约 20 ms 一帧分片发送(16 kHz 单声道下每帧约 640 字节)。
|
||
|
||
### 3. 发送文本(可选)
|
||
|
||
```json
|
||
{
|
||
"type": "input.text",
|
||
"text": "你好",
|
||
"interrupt": true
|
||
}
|
||
```
|
||
|
||
`interrupt` 默认为 `true`,会打断当前正在播放的 bot 语音。
|
||
|
||
### 4. 取消当前回复
|
||
|
||
```json
|
||
{"type": "response.cancel"}
|
||
```
|
||
|
||
### 5. 结束会话
|
||
|
||
```json
|
||
{"type": "session.stop", "reason": "done"}
|
||
```
|
||
|
||
## 服务端事件
|
||
|
||
所有出站消息均为 JSON,包含 `type`、`protocol`(`va.ws.v1`)和递增的 `seq` 字段。
|
||
|
||
| 事件类型 | 说明 |
|
||
|----------|------|
|
||
| `input.transcript.interim` | 用户语音中间转写(流式 ASR 时) |
|
||
| `input.transcript.final` | 用户语音最终转写 |
|
||
| `response.text.started` | 助手开始回复 |
|
||
| `response.text.delta` | 助手文本流式片段(通常早于 TTS 音频) |
|
||
| `response.text.final` | 助手本轮回复结束,见下方 `interrupted` 说明 |
|
||
| `response.audio.started` | 开始播放 TTS |
|
||
| `response.audio.delta` | TTS 音频片段(base64 PCM16) |
|
||
| `response.audio.stopped` | TTS 播放结束 |
|
||
|
||
音频事件示例:
|
||
|
||
```json
|
||
{
|
||
"type": "response.audio.delta",
|
||
"protocol": "va.ws.v1",
|
||
"seq": 1,
|
||
"audio": "<base64>",
|
||
"bytes": 6400,
|
||
"sample_rate": 16000,
|
||
"channels": 1
|
||
}
|
||
```
|
||
|
||
### `response.text.final` 与 `interrupted`
|
||
|
||
每轮助手回复结束时都会发送一条 `response.text.final`:
|
||
|
||
```json
|
||
{
|
||
"type": "response.text.final",
|
||
"text": "今天北京天气晴朗,",
|
||
"interrupted": true
|
||
}
|
||
```
|
||
|
||
- **`interrupted: false`** — 助手正常说完本轮内容(文本流与 TTS 自然结束)。
|
||
- **`interrupted: true`** — 本轮被打断,`text` 只包含**已流式输出、尚未说完**的部分,不是完整原定回复。
|
||
|
||
以下情况会置 `interrupted` 为 `true`,并停止当前 TTS:
|
||
|
||
1. **文本打断** — 发送 `input.text` 且 `interrupt` 为 `true`(默认值)。
|
||
2. **主动取消** — 发送 `response.cancel`。
|
||
3. **语音插话(barge-in)** — 助手正在说话时,用户开口且满足打断条件:转写文本达到最短字数门槛,或命中常见短答词(如「是的」「行」「可以」)。过短的背景噪声通常不会触发打断。
|
||
|
||
客户端可用 `interrupted` 区分「完整回复」与「说到一半被截断」,例如在 UI 上对未说完的气泡做标记。
|
||
|
||
## 浏览器示例
|
||
|
||
引擎可在 `config.json` 中开启内置演示页:
|
||
|
||
```json
|
||
"server": {
|
||
"serve_webpage": true,
|
||
"webpage_mount": "/demo"
|
||
}
|
||
```
|
||
|
||
启动服务后访问 `http://localhost:8000/demo/`,默认连接同源的 `/ws-product`。参考实现见 `examples/webpage/`。
|