Files
Genarrative/docs/technical/VOLCENGINE_SPEECH_STREAMING_INTEGRATION_2026-05-08.md
2026-05-10 13:18:46 +08:00

226 lines
7.6 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 火山引擎大模型语音流式接入 2026-05-08
## 背景
本次接入火山引擎豆包语音能力,覆盖两类运行态语音链路:
1. 大模型流式语音识别 ASR使用 WebSocket 双向流式优化模式。
2. 大模型语音合成 TTS使用实时交互场景的 WebSocket 双向流式接口,并提供一次性文本输入的 HTTP SSE 单向流式接口。
语音能力属于外部副作用,按 server-rs DDD 分层落在 `platform-speech``api-server` 只负责平台账号鉴权、环境配置校验、协议代理和错误映射。前端不得直接持有火山引擎密钥。
## 官方文档依据
- ASR`https://www.volcengine.com/docs/6561/1354869?lang=zh`
- TTS WebSocket 双向流式:`https://www.volcengine.com/docs/6561/1329505?lang=zh`
- TTS WebSocket 单向流式:`https://www.volcengine.com/docs/6561/1719100?lang=zh`
- TTS HTTP Chunked / SSE 单向流式:`https://www.volcengine.com/docs/6561/1598757?lang=zh`
## 环境变量
真实值只能放在本地未提交的 `.env.local` / `.env.secrets.local` 或生产服务器环境文件,禁止提交到仓库。
```text
VOLCENGINE_SPEECH_API_KEY=
VOLCENGINE_SPEECH_APP_ID=
VOLCENGINE_SPEECH_ACCESS_KEY=
VOLCENGINE_SPEECH_ASR_RESOURCE_ID=volc.seedasr.sauc.concurrent
VOLCENGINE_SPEECH_TTS_RESOURCE_ID=seed-tts-2.0
VOLCENGINE_SPEECH_REQUEST_TIMEOUT_MS=180000
VOLCENGINE_SPEECH_ASR_WS_URL=wss://openspeech.bytedance.com/api/v3/sauc/bigmodel_async
VOLCENGINE_SPEECH_TTS_BIDIRECTION_WS_URL=wss://openspeech.bytedance.com/api/v3/tts/bidirection
VOLCENGINE_SPEECH_TTS_SSE_URL=https://openspeech.bytedance.com/api/v3/tts/unidirectional/sse
```
配置规则:
1. 优先使用新版控制台 `VOLCENGINE_SPEECH_API_KEY`,上游请求头写 `X-Api-Key`
2. 若只配置旧版控制台信息,则使用 `VOLCENGINE_SPEECH_APP_ID``VOLCENGINE_SPEECH_ACCESS_KEY`,上游请求头写 `X-Api-App-Key``X-Api-Access-Key`
3. ASR 默认资源 ID 选 ASR 2.0 并发版;如账号是小时版,部署时改成 `volc.seedasr.sauc.duration`
4. TTS 默认资源 ID 选 `seed-tts-2.0`;旧音色或 1.0 计费资源由部署环境覆盖。
## ASR 协议边界
客户端连接:
```text
GET /api/speech/volcengine/asr/stream
Authorization: Bearer <Genarrative JWT>
```
浏览器与 `api-server` 使用 WebSocket 二进制帧透传:
1. 首包必须是 JSON 文本,表示 ASR full client request 的业务参数。
2. 后续二进制帧是音频分片。
3. 浏览器发送文本帧 `{"type":"finish"}` 时,后端把最后一个空音频包按负包发送给火山。
4. 后端把火山 full server response 解析成 JSON 文本帧发回浏览器。
ASR 上游连接:
```text
wss://openspeech.bytedance.com/api/v3/sauc/bigmodel_async
X-Api-Key: <VOLCENGINE_SPEECH_API_KEY>
X-Api-Resource-Id: <VOLCENGINE_SPEECH_ASR_RESOURCE_ID>
X-Api-Request-Id: <uuid>
X-Api-Sequence: -1
```
ASR 二进制协议:
1. 4 字节 header大端整数。
2. full client requestmessage type `0b0001`JSON 序列化gzip 压缩。
3. audio only requestmessage type `0b0010`raw payloadgzip 压缩。
4. 最后一包音频使用 flags `0b0010`
5. full server responsemessage type `0b1001`payload 为 gzip JSON。
6. error responsemessage type `0b1111`payload 为错误 JSON 或 UTF-8 文本。
首包参数由前端传入,但后端会兜底:
```json
{
"user": { "uid": "current-user-id" },
"audio": {
"format": "pcm",
"codec": "raw",
"rate": 16000,
"bits": 16,
"channel": 1
},
"request": {
"model_name": "bigmodel",
"enable_itn": true,
"enable_punc": true,
"show_utterances": true,
"result_type": "full"
}
}
```
## TTS 协议边界
### WebSocket 双向流式
客户端连接:
```text
GET /api/speech/volcengine/tts/bidirection
Authorization: Bearer <Genarrative JWT>
```
浏览器向后端发送 JSON 文本帧:
```json
{ "type": "start_connection" }
{ "type": "start_session", "sessionId": "...", "payload": { "user": {}, "req_params": {} } }
{ "type": "task_request", "sessionId": "...", "payload": { "req_params": { "text": "..." } } }
{ "type": "finish_session", "sessionId": "..." }
{ "type": "finish_connection" }
```
后端转成火山 WebSocket V3 二进制帧,并把上游返回帧统一解析成 JSON 文本或音频二进制帧回传浏览器。
TTS 双向上游连接:
```text
wss://openspeech.bytedance.com/api/v3/tts/bidirection
X-Api-Key: <VOLCENGINE_SPEECH_API_KEY>
X-Api-Resource-Id: <VOLCENGINE_SPEECH_TTS_RESOURCE_ID>
X-Api-Connect-Id: <uuid>
```
V3 事件帧:
1. Full-client request + event number 用于 `StartConnection``StartSession``TaskRequest``FinishSession``FinishConnection`
2. Full-server response + event number 用于 `ConnectionStarted``SessionStarted``SessionFinished` 等状态事件。
3. Audio-only response + event number 用于返回音频二进制。
4. 错误帧必须转成结构化 JSON 错误,不把上游密钥或完整请求头写入日志。
### HTTP SSE 单向流式
客户端请求:
```text
POST /api/speech/volcengine/tts/sse
Authorization: Bearer <Genarrative JWT>
Content-Type: application/json
Accept: text/event-stream
```
请求体:
```json
{
"text": "你好,欢迎来到百梦。",
"speaker": "zh_female_cancan_mars_bigtts",
"audioParams": {
"format": "mp3",
"sampleRate": 24000
}
}
```
后端转换为火山 HTTP SSE 请求体:
```json
{
"user": { "uid": "current-user-id" },
"req_params": {
"text": "...",
"speaker": "...",
"audio_params": {
"format": "mp3",
"sample_rate": 24000
}
}
}
```
上游 SSE 的常见事件:
1. `352`TTSResponse`data` 为 base64 音频片段。
2. `351`TTSSentenceEnd`sentence` 为字幕或时间戳数据。
3. `152`SessionFinish合成完成可含 `usage.text_words`
4. `153`SessionFailed合成失败。
后端保持 SSE 形态透传,但会补齐平台 `requestId` 与上游 `X-Tt-Logid` 作为排障信息。
## api-server 路由
| 方法 | 路由 | 说明 |
|---|---|---|
| `GET` | `/api/speech/volcengine/config` | 返回前端可见的默认资源和推荐音频参数,不返回密钥 |
| `GET` | `/api/speech/volcengine/asr/stream` | ASR WebSocket 双向流式代理 |
| `GET` | `/api/speech/volcengine/tts/bidirection` | TTS WebSocket 双向流式代理 |
| `POST` | `/api/speech/volcengine/tts/sse` | TTS HTTP SSE 单向流式代理 |
所有路由必须走 `require_bearer_auth`
## 验收
代码级验收:
```bash
cargo fmt --manifest-path server-rs/Cargo.toml --all --check
cargo test --manifest-path server-rs/Cargo.toml -p platform-speech
cargo test --manifest-path server-rs/Cargo.toml -p api-server volcengine_speech
cargo check --manifest-path server-rs/Cargo.toml -p api-server
npm run check:encoding
```
联调验收:
1. 启动 `npm run api-server`
2. 检查 `/healthz` 返回 200。
3. 未登录访问语音路由返回 401。
4. 已登录后 `/api/speech/volcengine/config` 不返回任何密钥字段。
5. ASR WebSocket 发送首包和 200ms PCM 分片后能收到识别 JSON。
6. TTS SSE 能收到 `352` 音频事件与最终 `152` 完成事件。
7. TTS 双向 WebSocket 能复用连接完成至少一个 session。
## 注意事项
1. 不把 `VOLCENGINE_ACCESS_KEY_ID``VOLCENGINE_SECRET_ACCESS_KEY`、API Key、Access Token 或完整 Authorization 写入文档、日志、测试快照或前端状态。
2. 中文语音默认使用 16k 单声道 PCM ASRTTS 默认使用 24k mp3运行时可按玩法需要改为 pcm。
3. 火山返回的 `X-Tt-Logid` 是排障关键信息,应记录 logid但不能记录密钥。
4. 语音流式能力是平台副作用,不涉及 SpacetimeDB 表结构变更,本次无需修改 `migration.rs`