CosyVoice 是开源的中文语音合成模型,几秒参考音频就能克隆音色;Velum 负责把它从「一个能跑通的脚本」变成「一个有接口、有日志、能挂到你现有应用后面的推理服务」。下面这条路径从零开始把两者串起来,走完之后用一条 curl 就能听到目标音色合成的句子。
需要先说明一句:CosyVoice 与 Velum 都迭代较快,本文涉及的目录名、配置字段、启动参数以各自官方仓库当前版本为准,思路和排查方法是通用的。
适用场景
适合这几类情况:语音合成数据不方便出内网;不想按调用量付费或受第三方额度限制;需要固定使用某个特定说话人的音色(自己的、主播的、企业代言人的);以及要给内部应用提供一个统一的 TTS 接口,希望在模型前面加一层鉴权、限流和日志。
不太适合:对延迟有极端要求、或者需要商业级 SLA 兜底的对外产品。本地部署的稳定性取决于这台机器和你的运维水平,Velum 能帮你把服务管起来,但做不了机器本身的冗余。
环境与前置条件
| 项目 | 建议 |
|---|---|
| 操作系统 | Ubuntu 20.04 及以上的 LTS 版本,或同级 Linux 发行版;Windows 建议走 WSL2 |
| Python | 3.10,用 conda 或 venv 隔离,不要用系统自带 Python |
| CUDA / 驱动 | 与所选 PyTorch 版本匹配;CPU 也能推理,但速度会慢到不适合当服务 |
| GPU 显存 | 8GB 起步比较稳妥,并发请求会显著抬高占用 |
| 内存 | 16GB 起 |
| 磁盘 | 预留 30GB 以上,模型权重 + conda 环境 + 缓存加起来不小 |
| 其他 | git、git-lfs、ffmpeg、curl |
网络上有两点要注意:模型权重一般托管在 ModelScope 与 HuggingFace 两处,国内环境用 ModelScope 会顺一些;Velum 必须能访问到 CosyVoice 服务监听的本地端口,如果以容器方式部署,这一点容易踩坑。
分步骤部署
第 1 步:装系统依赖
```bash
sudo apt update
sudo apt install -y git git-lfs ffmpeg curl build-essential
git lfs install
```
这一步在解决两类问题:git-lfs 用来拉取大体积的模型文件,ffmpeg 用来把参考音频转成模型要求的格式。
成功标志:ffmpeg -version 能打印版本信息,git lfs version 有正常输出。
第 2 步:建 Python 环境并安装 PyTorch
```bash
conda create -n cosyvoice python=3.10 -y
conda activate cosyvoice
python -m pip install --upgrade pip
```
PyTorch 的安装命令随 CUDA 版本变化,不要去记,直接打开 pytorch.org 的安装选择器,按你的驱动版本选好,复制当前给出的那条命令执行。
装完立刻验证:
```bash
python -c "import torch; print(torch.__version__, torch.cuda.is_available())"
```
输出里既有版本号又是 True,说明 GPU 可用。如果打印 False,先停下排查,多半是显卡驱动与 CUDA 版本不匹配,继续往下走只会在启动推理时再报一次错。
第 3 步:拉取 CosyVoice 代码并装依赖
```bash
cd ~
git clone --recursive <CosyVoice 官方仓库地址>
cd CosyVoice
pip install -r requirements.txt
```
--recursive 不能省略。仓库里带了子模块,漏掉的话不会立刻报错,而是在运行时才提示找不到某个模块,排查起来更麻烦。
依赖里有两类可选组件(文本正则化、声学相关),按官方 README 的说明安装即可。这一层经常卡住,遇到问题看后面的报错一节。
第 4 步:下载模型权重
权重在 ModelScope 和 HuggingFace 都有,选一个源就行。用 ModelScope 命令行工具的话,大致是这样(具体命令以 ModelScope 文档为准):
```bash
pip install modelscope
modelscope download --model <模型仓库名> --local_dir ./pretrained_models/<目录名>
```
也可以用 git-lfs 直接克隆:
```bash
git clone https://www.modelscope.cn/<模型仓库>.git pretrained_models/<目录名>
```
成功标志:pretrained_models/ 下出现目录,里面有 .pt、.onnx 以及若干配置文件,整体体积与官方说明的量级一致。下载中断会留下不完整的文件,重下之前先删干净,否则会出现「文件在但加载报错」的情况。
第 5 步:准备参考音频
音色克隆的效果,八成由这段参考音频决定。
要求:
- 时长 3 到 10 秒。太短抓不到音色特征,太长容易混进换气声和环境噪声。
- 单人说话,没有第二个人声、没有背景音乐、没有明显混响。
- 采样率 16kHz 以上,单声道,wav 格式。
转换命令:
```bash
ffmpeg -i input.mp3 -ac 1 -ar 16000 -c:a pcm_s16le ref.wav
```
转完一定要自己戴耳机听一遍,确认没有爆音、截断、忽大忽小。这段音频后面会被反复使用,值得花时间挑一段干净的。
同时要准备参考文本:把参考音频里说的每个字原样写下来,包括语气词。
第 6 步:先跑通命令行推理
不要一上手就起服务。先用仓库自带的示例脚本确认模型能出声:
```bash
python <官方示例脚本>.py
```
脚本名和参数以官方 README 为准。等到输出目录里出现 wav 文件、播放后音色和内容都符合预期,再往下走。这一步单独跑通,后面出问题时就能快速判断是模型的问题还是服务层的问题。
第 7 步:起 HTTP 推理服务
CosyVoice 仓库的 runtime 目录下通常带有 FastAPI 或 gRPC 的服务示例,可以直接用:
```bash
cd runtime/python/fastapi
python server.py --port 50000 --model_dir <权重目录>
```
参数名以官方示例为准。如果用 uvicorn 拉起:
```bash
uvicorn server:app --host 0.0.0.0 --port 50000
```
成功标志:终端打印出监听地址,且没有 traceback。要让服务常驻,用 systemd 或 supervisor 包一层,不要挂在 SSH 会话里,否则断开连接服务就没了。
第 8 步:用 Velum 接入
Velum 的位置在模型服务前面,把本地的 CosyVoice 注册成一个后端,对外提供带鉴权、限流和日志的统一接口。配置结构大致如下(字段名以官方文档为准):
```yaml
backends:
- name: cosyvoice-local
type: http
endpoint: http://127.0.0.1:50000
timeout: 120s
health_check: /health
routes:
- path: /v1/audio/speech
backend: cosyvoice-local
```
几个要点:
timeout要放大。语音合成是典型的慢请求,长文本尤其慢,默认的 30 秒经常不够。- 健康检查路径必须和 CosyVoice 服务实际暴露的一致。路径写错的表现是「模型服务明明在跑,Velum 却一直标记后端不可用」。
- 如果 Velum 以容器方式运行,配置里的
127.0.0.1指的是容器自己,要换成宿主机可达的 IP 或服务名,或者让容器使用 host 网络。 - 网关超时要比模型侧超时更宽松,否则会出现「合成其实成功了,但网关先断开连接」。
改完重启 Velum,看它的启动日志里有没有后端注册成功的记录。
验证部署是否成功
分三层验证,从下往上,哪一层断了就只查那一层。
第一层,模型服务本身。
```bash
curl -s http://127.0.0.1:50000/health
```
返回 200 或一个表示健康的状态即可,具体路径以服务实现为准。
直接调一次克隆合成:
```bash
curl -X POST http://127.0.0.1:50000/inference_zero_shot \
-H "Content-Type: application/json" \
-d '{"tts_text":"今天天气不错","prompt_text":"参考音频对应的文字","prompt_wav":"/path/to/ref.wav"}' \
-o out.wav
```
字段名以服务实现为准。out.wav 能被播放器打开、内容正确,第一层就通了。
第二层,Velum 网关层。
```bash
curl -X POST http://127.0.0.1:<velum端口>/v1/audio/speech \
-H "Authorization: Bearer <你的key>" \
-H "Content-Type: application/json" \
-d '{"input":"今天天气不错","voice":"my-voice"}' \
-o out2.wav
```
out2.wav 能正常播放,说明后端注册、路由转发、鉴权都通了。
第三层,端到端。
用几行 Python 模拟业务代码的调用方式,确认接口形状是你想要的:
```python
import requests
r = requests.post(
"http://127.0.0.1:<velum端口>/v1/audio/speech",
json={"input": "测试一句话", "voice": "my-voice"},
headers={"Authorization": "Bearer <你的key>"},
timeout=120,
)
open("test.wav", "wb").write(r.content)
```
test.wav 可以播放,整条链路就通了。
常见报错与解决
报错:ModuleNotFoundError: No module named 'pynini',或安装 pynini 时编译失败
原因:文本正则化依赖 pynini / WeTextProcessing,直接用 pip 安装经常因为缺少相关底层库而失败。
解决:优先用 conda 从 conda-forge 渠道装,再装其余依赖。
```bash
conda install -c conda-forge pynini -y
pip install WeTextProcessing
```
如果只是想让服务先跑起来,也可以在配置里关闭文本正则化,但要清楚代价:数字、日期、金额这类文本的读法会不符合预期。
报错:torch.cuda.OutOfMemoryError 或 CUDA out of memory
原因:并发请求太多,或者单条文本过长导致显存峰值上升,也可能是别的进程占着显存。
解决:
```bash
nvidia-smi # 先看是谁在占显存
export PYTORCH_CUDA_ALLOC_CONF=max_split_size_mb:128
```
同时在 Velum 侧把并发限制到 1 至 2,并对超过阈值的文本强制分块。语音合成属于 GPU 密集任务,并发调高不一定换来更高吞吐,很容易换来一批 OOM。
报错:克隆出来的声音不像本人,或者带电流声、回音
原因:参考音频质量不达标,或者参考文本和音频内容对不上。
解决:按第 5 步的命令重新处理参考音频,确认单声道、16kHz、无背景音。重点检查 prompt_text 是否和 prompt_wav 里说的内容逐字一致——多一个字、少一个语气词都会明显影响效果。截取更干净的一小段重新试。
报错:长文本返回被截断,或者请求直接超时
原因:多数本地推理服务对单次请求的文本长度有隐含上限,而且合成耗时基本随字数线性增长。
解决:在客户端分块,按句号、问号、逗号这类标点切分,每块控制在几十到一两百字,逐块请求后再拼接音频。拼接不能直接把二进制相加,wav 有文件头,要按帧拼:
```python
import numpy as np
import soundfile as sf
chunks = [sf.read(p) for p in paths]
sr = chunks[0][1]
data = np.concatenate([c[0] for c in chunks])
sf.write("merged.wav", data, sr)
```
分块有两个坑要提前知道。一是切分点如果落在句子中间,语气会断,所以按标点切而不是按字数硬切。二是每块单独合成时音色会有细微波动,要求高的话让相邻块之间留一点重叠,再在重叠处做淡入淡出。
报错:Velum 显示后端不可用,或者调用返回 502
原因:健康检查路径写错、端口不通,或者 Velum 在容器里访问不到宿主机服务。
解决:分两层测连通性。
```bash
curl http://127.0.0.1:50000/health # 宿主机上通不通
docker exec -it <velum容器> curl http://<宿主机IP>:50000/health # 容器里通不通
```
第二条不通就是网络模式或地址的问题,把配置里的地址换成宿主机 IP 或正确的服务名。
后续维护
备份。 需要备份三样东西:Velum 的配置文件、参考音频及其对应的参考文本、以及服务如果支持导出音色特征文件则一并保存。模型权重不用备份,重新下载即可,但建议记下用的是哪个模型仓库、哪个版本,否则升级后音色对不上会很难查。
升级。 分两步走,先升推理侧(CosyVoice 及其依赖),再升 Velum。两者靠 HTTP 协议解耦,先动后端一般不会打断现有调用。每次升级前用同一段参考音频、同一句文本跑一次回归,对比新旧输出是否都能接受——模型迭代后音色发生细微变化是常事,提前有心理预期。
日志与监控。 重点盯四个指标:请求排队时长、单次合成耗时、GPU 显存占用、失败率。合成耗时和文本长度强相关,只看平均值意义不大,按文本长度分桶看更准确。
运行策略。 常驻服务用 systemd 管理并配好 restart 策略。GPU 上跑推理容易遇到显存缓慢增长,可以配合定期健康探测做自动摘除,或者设置低峰期定时重启。并发上限保守一点,宁可让请求排队,也不要因为 OOM 让整个服务不可用。
安全。 如果 Velum 对外暴露,务必开启鉴权。另外合成文本往往包含用户隐私,日志里建议只记录长度和耗时,不要落全文。
