ASR + TTS API 网关,兼容阿里云语音 API 与 OpenAI TTS API,支持 WebSocket 流式协议。 模型推理由独立子服务承载(每个引擎独立 venv + 容器),通过 docker-compose 编排。
Important
从单体版升级? 先读 docs/migration_to_latest.md
本分支已重构为微服务架构 (gateway + 4 个 GPU 子服务)。对外 HTTP/WS 协议字节级兼容, 客户端代码不用动; 但部署侧 docker-compose、模型缓存挂载路径、.env 变量都有变化。迁移文档覆盖:数据原地复用、必改的 mount 路径、.env 增删项、零停机切换、回滚步骤。
┌─────────────────────────┐
外部客户端 ────────► │ gateway (CPU) │
(HTTP/WS) │ - Aliyun/OpenAI 协议 │
│ - 句子状态机 + ITN │
│ - 采样率/格式转换 │
└────────┬────────────────┘
│ HTTP / WS (X-Internal-Token)
┌───────────────┼───────────────┬───────────────┐
▼ ▼ ▼ ▼
funasr (GPU) dolphin (GPU) qwen3-asr (GPU) cosyvoice (GPU)
Paraformer/ DataoceanAI Qwen3-ASR-1.7B CosyVoice2/3
SenseVoice Dolphin Small (vLLM 加速) (in-process)
子服务各自一个 pyproject.toml + uv.lock + Dockerfile,依赖完全隔离。
例如 funasr 用 transformers 4.51.3,qwen3-asr 用 transformers 4.57.1 + vLLM 0.11+,
互不冲突。
git clone https://cnb.cool/nexa/FunSpeech.git
cd FunSpeech
# 必须: 拉 cosyvoice 上游源码 (git submodule)
# 不做这一步, cosyvoice 镜像启动会 ModuleNotFoundError
git submodule update --init --recursive
cp .env.example .env # 按需修改如果机器在国内,把代理放进 .env:
HTTP_PROXY=http://host.docker.internal:7890 # macOS/Windows Docker Desktop
HTTPS_PROXY=http://host.docker.internal:7890
# Linux 服务器: 改成宿主机 LAN IP, 例如 http://192.168.1.10:7890Dockerfile 用了 RUN --mount=type=cache 给 apt / uv 双缓存,需要 BuildKit。Docker 23+ 默认开启;低版本手动启用:
export DOCKER_BUILDKIT=1
export COMPOSE_DOCKER_CLI_BUILD=1docker compose build # 重 build 时 apt/uv 走本机缓存,极快
docker compose up -d # 默认: gateway + qwen3-asr + cosyvoice
docker compose --profile funasr up -d # 加上 funasr (paraformer/sensevoice)
docker compose --profile dolphin up -d # 加上 dolphin
docker compose --profile funasr --profile dolphin up -d # 全部 ASR 引擎服务暴露在 http://localhost:${GATEWAY_PORT:-8000}。
# 健康
curl http://localhost:8000/stream/v1/asr/health
curl http://localhost:8000/stream/v1/tts/health
# ASR
curl -X POST "http://localhost:8000/stream/v1/asr?format=wav&sample_rate=16000" \
-H "Content-Type: application/octet-stream" \
--data-binary @audio.wav
# TTS
curl -X POST "http://localhost:8000/stream/v1/tts" \
-H "Content-Type: application/json" \
-d '{"text":"你好","voice":"中文女"}' \
--output speech.wavWebSocket 测试页:
- ASR:
http://localhost:8000/ws/v1/asr/test - TTS:
http://localhost:8000/ws/v1/tts/test
| 服务 | 端口 | 镜像 | GPU | 默认启动 | profile |
|---|---|---|---|---|---|
| gateway | 8000 | funspeech/gateway | ❌ | ✅ | (默认) |
| funasr-0 | 8001 | funspeech/funasr | ✅ | ❌ | funasr |
| dolphin-0 | 8002 | funspeech/dolphin | ✅ | ❌ | dolphin |
| qwen3-asr-0 | 8003 | funspeech/qwen3-asr | ✅ | ✅ | (默认, 默认 ASR 引擎) |
| cosyvoice-0 | 8004 | funspeech/cosyvoice | ✅ | ✅ | (默认) |
每个子服务暴露 GET /health + 自有业务端点(详见各 services/*/README.md)。
| 端点 | 方法 | 说明 |
|---|---|---|
/stream/v1/asr |
POST | 一句话语音识别 |
/stream/v1/asr/models |
GET | 模型列表 |
/stream/v1/asr/health |
GET | 健康检查 |
/ws/v1/asr |
WS | 流式识别(Aliyun 协议) |
可识别模型(models.json):qwen3-asr-flash(默认)、paraformer-large、sensevoice-small(后两者需 --profile funasr)、dolphin-small(需 --profile dolphin)。
| 端点 | 方法 | 说明 |
|---|---|---|
/stream/v1/tts |
POST | 语音合成 |
/openai/v1/audio/speech |
POST | OpenAI 兼容 |
/rest/v1/tts/async |
POST/GET | 异步长文本合成 |
/stream/v1/tts/voices |
GET | 音色列表 |
/stream/v1/tts/voices/info |
GET | 音色详细信息 |
/stream/v1/tts/voices/refresh |
POST | 刷新音色 |
/stream/v1/tts/health |
GET | 健康检查 |
/ws/v1/tts |
WS | 双向流式合成(Aliyun 协议) |
外部协议与之前的进程内版本完全兼容。
| 变量 | 默认 | 说明 |
|---|---|---|
GATEWAY_PORT |
8000 |
对外暴露端口 |
APPTOKEN / APPKEY |
- | 外部鉴权(可选) |
INTERNAL_SERVICE_TOKEN |
funspeech-internal |
网关→子服务鉴权头(X-Internal-Token) |
ASR_MODEL_MODE |
all |
all / offline / realtime |
TTS_MODEL_MODE |
all |
all / sft / clone |
ASR_ENABLE_REALTIME_PUNC |
false |
流式中间结果是否带标点 |
AUTO_LOAD_CUSTOM_ASR_MODELS |
- | 启动时预热的额外 ASR 模型 id |
FUNASR_SERVICE_URLS |
http://funasr-0:8001 |
子服务 URL,逗号分隔多副本 |
DOLPHIN_SERVICE_URLS |
http://dolphin-0:8002 |
|
QWEN3_ASR_SERVICE_URLS |
http://qwen3-asr-0:8003 |
|
COSYVOICE_SERVICE_URLS |
http://cosyvoice-0:8004 |
|
SERVICE_REQUEST_TIMEOUT |
60 |
子服务调用超时(秒) |
INFERENCE_THREAD_POOL_SIZE |
max(4, CPU 核数) |
网关同步调用线程池大小;高 QPS 调大 |
HTTPX_MAX_CONNECTIONS |
200 |
网关→子服务 HTTP 连接池上限;>100 req/s 时调到 500+ |
HTTPX_MAX_KEEPALIVE |
50 |
保活连接数上限 |
子服务专属 env(模型版本、TRT/FP16/vLLM 等)请见 .env.example、docs/deployment.md §6 和各 services/*/README.md。
关于"GPU 并发数":子服务内部的 GPU 并发已在代码里硬编码 (funasr/dolphin=1, cosyvoice=2, qwen3-asr=Lock+vLLM 内部 batching), 不通过环境变量暴露。横向扩展请用多副本, 见下文。
cd services/funasr
uv sync
PORT=8001 INTERNAL_SERVICE_TOKEN=test uv run python server.pyuv sync
FUNASR_SERVICE_URLS=http://localhost:8001 \
COSYVOICE_SERVICE_URLS=http://localhost:8004 \
INTERNAL_SERVICE_TOKEN=funspeech-internal \
uv run python start.py在 NVIDIA RTX 4090 24G 上的实测单副本容量 (完整数据见 benchmarks/):
| 子服务 | 单副本容量 | 单条延迟 | 显存 |
|---|---|---|---|
| funasr (all) | ~12 req/s | ~80 ms | ~3 GiB |
| dolphin | ~12 req/s | ~80 ms | ~1 GiB |
| qwen3-asr | ~5 req/s (vLLM 内部 batch 可吃 64 并发) | ~190 ms | 0.85 × 卡显存 (vLLM KV pool) |
| cosyvoice (clone) | 2 路实时 TTS (RTF ≈ 1.05) | ~3.5 s / 句 | ~4 GiB |
ASR 看 req/s, TTS 看"几路实时" (RTF ≤ 1) — 单副本 sem=2 时同时 2 路 RTF=1.05 刚好实时, 4 路就开始 RTF=1.75 卡顿。想 N 路实时 TTS 需 ceil(N/2) 张卡。
横向扩展 = 多卡多副本, 不是单卡多副本 (同一服务两副本绑同一张卡, 实测总容量不升反降)。
用规划脚本一键算出应该开几副本 / 怎么绑卡, 并在当前目录生成完整 docker-compose.generated.yml:
python3 scripts/plan_deployment.py # 交互式
python3 scripts/plan_deployment.py --preset 4090-quad # 看预设
python3 scripts/plan_deployment.py --list-presets详细说明见 docs/deployment.md §4.3。
所有 GPU 子服务通过 bind mount 共享 MODELSCOPE_CACHE(默认 ~/.cache/modelscope/hub/models,
mount 到容器 /root/.cache/modelscope/hub),
因此即便 funasr / cosyvoice 各自的 transformers 版本不同,权重文件可以复用。
提前下载:
pip install modelscope
modelscope download --model iic/CosyVoice-300M-SFT
modelscope download --model FunAudioLLM/Fun-CosyVoice3-0.5B-2512
modelscope download --model iic/speech_paraformer-large_asr_nat-zh-cn-16k-common-vocab8404-pytorch
modelscope download --model iic/speech_paraformer-large_asr_nat-zh-cn-16k-common-vocab8404-online
modelscope download --model iic/punc_ct-transformer_zh-cn-common-vocab272727-pytorch
modelscope download --model iic/speech_fsmn_vad_zh-cn-16k-common-pytorch
# 按需:
modelscope download --model DataoceanAI/dolphin-small
modelscope download --model Qwen/Qwen3-ASR-1.7B零样本克隆音色由 cosyvoice 子服务托管。把 张三.wav + 张三.txt 放到 ./voices/ 卷,然后:
curl -X POST http://localhost:8000/stream/v1/tts/voices/refresh或通过 API 上传(需要外部 multipart 接入,见 docs/)。所有音色状态(spk2info.pt + voice_registry.json)
持久化到 ./voices/ 卷。
- vLLM 加速 CosyVoice 默认关闭:vLLM 0.11+ 要求 transformers ≥4.55,
与 CosyVoice 主代码所需的 4.51.3 冲突。如果性能瓶颈明显,可拆出
services/cosyvoice_vllm/独立 venv 启用,见services/cosyvoice/README.md的多副本注释。 - Qwen3-ASR 流式不走 vLLM 通用
/v1/realtime:vLLM 通用 realtime 端点对 Qwen3-ASR 质量明显劣化(无跨段上下文、无 token 修订,见 vllm Issue #35767)。 我们用官方qwen_asr.Qwen3ASRModel.streaming_transcribe+init_streaming_state, 在子服务进程内跑,具备 unfixed_chunk / unfixed_token 修订能力。 - Qwen3-ASR 子服务进程内 GPU 串行: vLLM 的 Python
LLM(...)入口 非线程安全, 子服务用asyncio.Lock保证只有一个调用进 vLLM。这不影响 vLLM 自身的 continuous batching (batching 在 engine 内部), 但要靠多副本扩吞吐。 - 音色 CRUD 多副本同步 (commit
299075b): URL 列表第一个 = 写副本 (primary)。 所有POST/DELETE /voices自动路由到 primary, 写后网关广播POST /voices/reload让其它副本从磁盘热重载spk2info.pt。详见docs/deployment.md §5.2。
.
├── app/ # 网关代码
│ ├── api/v1/ # FastAPI 路由 (Aliyun + OpenAI)
│ ├── services/asr/ # ASR 引擎抽象 + HTTP 客户端
│ ├── services/tts/ # TTS 引擎抽象 + HTTP 客户端 (含 voice_manager)
│ ├── services/websocket_*.py
│ ├── core/config.py # 网关 env
│ └── utils/audio.py # 重采样、PCM/WAV 转码、ITN
├── services/ # 各子服务(独立 venv + Dockerfile)
│ ├── funasr/
│ ├── dolphin/
│ ├── qwen3_asr_vllm/
│ └── cosyvoice/ # third_party/CosyVoice 是官方 submodule
├── scripts/
│ ├── plan_deployment.py # 副本规划 (零依赖)
│ └── analyze_audio_rms.py # 远场过滤阈值分析
├── benchmarks/ # 4090 实测数据 + 测试脚本 (git-lfs)
│ ├── README.md
│ ├── scripts/ # bench_tts.py / bench_asr.py / ...
│ ├── audio/ # TTS 生成的测试样本
│ └── results/ # 结果 (json + log)
├── docker-compose.yml
├── .env.example
└── pyproject.toml # 网关 deps (CPU only, ~57 包)
MIT — 见 LICENSE。
