# 火山引擎大模型语音流式接入 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 ``` 浏览器与 `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: X-Api-Resource-Id: X-Api-Request-Id: X-Api-Sequence: -1 ``` ASR 二进制协议: 1. 4 字节 header,大端整数。 2. full client request:message type `0b0001`,JSON 序列化,gzip 压缩。 3. audio only request:message type `0b0010`,raw payload,gzip 压缩。 4. 最后一包音频使用 flags `0b0010`。 5. full server response:message type `0b1001`,payload 为 gzip JSON。 6. error response:message 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 ``` 浏览器向后端发送 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: X-Api-Resource-Id: X-Api-Connect-Id: ``` 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 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 ASR;TTS 默认使用 24k mp3,运行时可按玩法需要改为 pcm。 3. 火山返回的 `X-Tt-Logid` 是排障关键信息,应记录 logid,但不能记录密钥。 4. 语音流式能力是平台副作用,不涉及 SpacetimeDB 表结构变更,本次无需修改 `migration.rs`。