- 前端: VAD语音检测(@ricky0123/vad-web) + useVoiceInput双模式(流式WS/REST) - Gateway: VoiceStreamManager代理WS流式STT到voice-service - Voice-service: DashScope REST → Realtime WS → Whisper三级引擎 + ffmpeg转码 - 共享模块: pkg/audio(音频转换) + pkg/dashscope(ASR REST客户端) - 清理: 移除旧plugin-manager和pkg/plugins,完成插件→工具合并 - 文档: 完善gateway-api.md和voice-service.md语音API文档 - 工具: scripts/voice/ 语音转换脚本集 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
7.9 KiB
Voice-Service API
Base URL: http://<host>:8093 | Auth: 无
语音服务封装两层引擎:
- STT (语音转文字): DashScope REST 离线模型
qwen3-asr-flash-2026-02-10(主) → DashScope Realtime WSqwen3-asr-flash-realtime(流式) → 本地 Whisper (备) - TTS (文字转语音): edge-tts (主) + espeak-ng (备)
引擎分层说明:
- 离线转录 (
POST /api/v1/transcribe): 使用 DashScope REST API,无需 session 协商和 Server VAD,延迟更低。失败自动回退 Whisper。- 流式转录 (
GET /api/v1/stt/stream): 使用 DashScope Realtime WebSocket,支持实时分片输入和中间结果输出,需客户端发送 PCM 音频。- 音频转码: 所有非 PCM 格式通过 ffmpeg 转码为 16kHz mono PCM 后再识别,支持 WAV/MP3/OGG/FLAC/WebM/Opus/AMR 等格式。
目录
- POST /api/v1/transcribe — 语音转文字
- POST /api/v1/tts/synthesize — 文字转语音
- GET /api/v1/tts/voices — 发音人列表
- GET /api/v1/health — 健康检查
- GET /api/v1/status — 服务状态
- GET /api/v1/tts/status — TTS 状态
- WebSocket GET /api/v1/stt/stream — 流式 STT
1. POST /api/v1/transcribe — 语音转文字
Content-Type: multipart/form-data | Max body: 10 MB
引擎选择
优先使用 DashScope REST 离线模型 qwen3-asr-flash-2026-02-10,失败自动回退本地 Whisper。
接收音频 → DashScope REST API (HTTP POST)
↓ 失败
ffmpeg 转码 PCM → 本地 Whisper 引擎
表单字段
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
audio |
file | 是 | 音频文件。支持格式: wav, mp3, ogg, flac, m4a, aac, webm, opus, amr, pcm |
language |
string | 否 | 默认 "zh"。可选: zh, en, ja, ko, auto |
转码说明: 非 PCM 格式(含 Opus/WebM/AMR)通过 ffmpeg 自动转码为 16-bit PCM 16000Hz mono 后识别。需部署环境安装 ffmpeg。
响应 200
{
"success": true,
"text": "转录结果文本",
"language": "zh",
"duration_ms": 1234
}
错误
| 状态码 | 错误体 |
|---|---|
| 400 | {"error":"文件过大或解析失败,最大支持 10MB"} |
| 400 | {"error":"缺少 audio 文件字段"} |
| 400 | {"error":"音频文件为空"} |
| 400 | {"error":"不支持的语言: <lang>,支持的语言: zh, en, ja, ko, auto"} |
| 405 | {"error":"method not allowed"} |
| 500 | {"error":"读取音频文件失败"} |
| 500 | {"success":false,"error":"<engine error>"} |
2. POST /api/v1/tts/synthesize — 文字转语音
Content-Type: application/json
请求
{
"text": "你好世界 (必填)",
"voice": "zh-CN-XiaoxiaoNeural (默认)",
"rate": "+0% (默认,如 +20%/-20%)"
}
响应 200 — 原始音频流
- Content-Type:
audio/mpeg(edge-tts) 或audio/wav(espeak-ng/fallback) - Content-Disposition:
inline; filename=synthesized.mp3
引擎回退链: edge-tts (mp3) → espeak-ng (wav) → silent WAV
错误
| 状态码 | 错误体 |
|---|---|
| 400 | {"error":"请求体解析失败: ..."} |
| 400 | {"error":"text 字段不能为空"} |
| 405 | {"error":"method not allowed"} |
| 500 | {"error":"TTS 合成失败: ..."} |
3. GET /api/v1/tts/voices — 发音人列表
// 响应 200
{
"voices": [
{ "name": "zh-CN-XiaoxiaoNeural", "display_name": "晓晓 (女声)", "gender": "Female", "locale": "zh-CN" },
{ "name": "zh-CN-YunxiNeural", "display_name": "云希 (男声)", "gender": "Male", "locale": "zh-CN" },
{ "name": "zh-CN-XiaoyiNeural", "display_name": "晓伊 (女声)", "gender": "Female", "locale": "zh-CN" }
],
"count": 3
}
4. GET /api/v1/health — 健康检查
{
"status": "ok",
"service": "voice-service",
"stt": {
"available": true,
"primary": "dashscope_rest",
"dashscope_rest": {
"available": true,
"model": "qwen3-asr-flash-2026-02-10",
"protocol": "rest"
},
"dashscope_ws": {
"available": true,
"model": "qwen3-asr-flash-realtime",
"protocol": "websocket",
"state": "idle"
},
"whisper": {
"available": true,
"binary_available": true,
"model_loaded": true,
"ffmpeg_available": true,
"model_name": "ggml-small.bin"
},
"default_language": "zh",
"supported_languages": ["zh","en","ja","ko","auto"]
},
"tts": {
"available": true,
"edge_tts": true,
"espeak_ng": false,
"engine": "edge-tts",
"default_voice": "zh-CN-XiaoxiaoNeural",
"builtin_voices": 3
}
}
状态字段说明
| 字段 | 说明 |
|---|---|
stt.available |
DashScope REST / WS 或 Whisper 至少一个可用 |
stt.primary |
当前优先引擎: dashscope_rest |
stt.dashscope_rest.available |
DashScope REST API Key 已配置 |
stt.dashscope_rest.protocol |
协议类型: rest |
stt.dashscope_ws.available |
DashScope Realtime WS 可用 |
stt.dashscope_ws.protocol |
协议类型: websocket |
stt.dashscope_ws.state |
连接状态: idle, connected, error |
stt.whisper.available |
Whisper 二进制 + 模型文件 + ffmpeg 均存在 |
stt.whisper.ffmpeg_available |
ffmpeg 可用于音频转码 |
tts.available |
至少一个 TTS 引擎可用 |
tts.engine |
当前激活引擎: edge-tts, espeak-ng, fallback (silent WAV), none |
5. GET /api/v1/status — 服务状态
同 /health 但无顶层 status 字段:
{
"service": "voice-service",
"stt": { ... }, // 同 health.stt
"tts": { ... } // 同 health.tts
}
6. GET /api/v1/tts/status — TTS 单独状态
{
"service": "voice-service",
"tts": {
"available": true,
"edge_tts": true,
"espeak_ng": false,
"engine": "edge-tts",
"default_voice": "zh-CN-XiaoxiaoNeural",
"builtin_voices": 3
}
}
7. WebSocket GET /api/v1/stt/stream — 流式 STT
Query 参数: ?language=zh&format=pcm (language 默认 zh, format 默认 pcm)
Read deadline: 300s
注意: 此端点使用 DashScope Realtime WebSocket (
qwen3-asr-flash-realtime),音频帧必须是 PCM 格式。非 PCM 格式应使用 REST 离线转录 (POST /api/v1/transcribe)。Gateway 代理: Gateway 的
voice_stream_*消息类型通过此端点与前端 VAD 配合,实现端到端流式语音 → STT → LLM 管道。详见 Gateway WebSocket 文档。
客户端 → 服务端
Binary 帧: 原始 PCM 音频 (16-bit LE, 16000Hz, mono)。每帧通过 input_audio_buffer.append 转发到 DashScope。
JSON 控制帧:
{ "action": "stop" }
// 请求结束会话。服务端返回 done 后关闭。
{ "language": "en" }
// 动态切换识别语言。
服务端 → 客户端 (JSON 文本帧)
result — 识别结果
{
"type": "result",
"text": "识别文本片段",
"isFinal": true
}
| 字段 | 说明 |
|---|---|
isFinal: true |
VAD 端点检测到的完整句子 |
isFinal: false |
中间增量 (delta) |
error
{ "type": "error", "error": "错误描述" }
done — 响应 stop
{ "type": "done", "action": "stop" }
连接生命周期
- HTTP 升级请求 → 验证 STT 引擎可用性 (不可用返回 503)
- 建立 DashScope realtime 会话 (
session.created→session.update→session.updated) - 客户端发送 binary PCM 帧 → 服务端 base64 编码后
input_audio_buffer.append - DashScope VAD 自动检测 →
conversation.item.input_audio_transcription.completed→ 转发 result - 客户端发送
{"action":"stop"}→ 服务端session.finish→ 关闭连接