Qwen3TTS(OpenAI 兼容 TTS)
Qwen3TTS 是基于 faster-qwen3-tts 封装的 OpenAI 兼容文本生成语音服务。算力舱中的推荐服务名为 qwen3tts-ai,适合接入 OpenWebUI、OpenAI-compatible SDK 或任意自定义 HTTP 客户端。
和 Kokoro tts-ai 相比,这个服务的特点是:
- 兼容
POST /v1/audio/speech - 支持
wav/pcm流式音频返回 - 启动时先完成模型 warmup,再对外标记 ready
- 支持多音色查询接口
/v1/audio/voices - 支持请求级
language、instruct、seed clone模式下支持生成和复用speaker.pt- 额外提供
POST /v1/audio/voice-clone/pt
服务入口
- 推荐服务域名:
qwen3tts-ai - 若沿用当前示例 compose,也可使用
qwen3-tts-ai - 健康检查:
/health - 音色列表:
/v1/audio/voices - TTS 接口:
/v1/audio/speech - PT 生成接口:
/v1/audio/voice-clone/pt
部署到算力舱后的访问形式示例:
- 服务地址:
https://qwen3tts-ai.${LAZYCAT_BOX_DOMAIN} - 健康检查:
https://qwen3tts-ai.${LAZYCAT_BOX_DOMAIN}/health - 音色列表:
https://qwen3tts-ai.${LAZYCAT_BOX_DOMAIN}/v1/audio/voices - TTS 接口:
https://qwen3tts-ai.${LAZYCAT_BOX_DOMAIN}/v1/audio/speech - PT 生成接口:
https://qwen3tts-ai.${LAZYCAT_BOX_DOMAIN}/v1/audio/voice-clone/pt
执行与启动行为
qwen3tts-ai 和传统“进程启动即 ready”的服务不一样,它在正式接收请求前还会执行一轮 startup warmup。
启动流程大致如下:
- 加载模型与音色配置
- 初始化 CUDA graphs
- 执行一轮 warmup 请求
/health中的ready变为true- 才建议上游 AI 应用或脚本发起正式 TTS 请求
执行上的注意点:
- 容器进程已经起来,不代表服务已经 ready
- 应用接入时应轮询
/health,并以ready=true作为可用标准 - 文档中的 Docker
healthcheck主要用于检测 HTTP 服务存活;如果你需要严格 readiness,请额外检查/health返回体中的ready字段
健康检查
GET /health
该接口用于判断服务是否完成模型加载和 startup warmup。
请求:
curl https://qwen3tts-ai.${LAZYCAT_BOX_DOMAIN}/health响应示例:
{
"status": "ok",
"ready": true,
"model_loaded": true,
"mode": "custom",
"voices": ["aiden", "dylan", "vivian"],
"startup_warmup_enabled": true,
"startup_warmup_completed": true,
"startup_warmup_seconds": 15.11
}建议:
- 只有在
ready=true时再发起正式 TTS 请求 - 可以通过
mode字段判断当前服务运行模式:custom表示 CustomVoice 固定音色模式,clone表示 Base 声纹克隆模式
音色列表接口
GET /v1/audio/voices
请求:
curl https://qwen3tts-ai.${LAZYCAT_BOX_DOMAIN}/v1/audio/voices响应示例:
{
"voices": [
"aiden",
"dylan",
"eric",
"ono_anna",
"ryan",
"serena",
"sohee",
"uncle_fu",
"vivian"
],
"default_voice": "vivian",
"mode": "custom"
}说明:
voice请求参数必须从这里获取- 如果传入不存在的
voice,服务会优先回退到default_voice
TTS 接口
POST /v1/audio/speech
该接口兼容 OpenAI TTS API 的常见调用方式。
常规 JSON 请求头:
Content-Type: application/json
Accept: audio/wavJSON 请求体示例:
{
"model": "tts-1",
"input": "欢迎使用 Qwen3TTS 服务。",
"voice": "vivian",
"response_format": "wav",
"language": "Chinese",
"instruct": "请用温和、清晰、偏慢一点的语气朗读。",
"seed": 1234
}参数说明:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
model | string | 否 | 兼容字段,建议固定传 tts-1 |
input | string | 是 | 待合成文本,不能为空 |
voice | string | 否 | 音色名,建议先通过 /v1/audio/voices 获取 |
response_format | string | 否 | 支持 wav / pcm / mp3 |
language | string | 否 | 请求级语言,传入时覆盖该 voice 的默认语言配置 |
instruct | string | 否 | 自定义指令,用于控制语气、风格、节奏或口音倾向 |
seed | integer | 否 | 请求级随机种子。相同文本、音色、语言、指令和模型版本下,固定同一个 seed 可以复现同一份音频输出 |
instruct 的执行规则:
- 当前服务运行在
custom或clone模式时可用 - 请求里传入
instruct时,优先级高于服务端静态 voice 配置中的默认instruct - 请求里不传
instruct时,沿用当前服务默认行为 - 如果你只关心 OpenAI 兼容调用方式,可以把它理解为一个可选扩展字段
当前请求级扩展参数:
- 已暴露并建议使用的字段:
language、instruct、seed language用于覆盖音色默认语言instruct用于控制语气、风格、节奏或口音倾向seed用于让同一请求在同一服务版本下可复现voice_clone_pt仅在clone模式下可用,并且必须通过multipart/form-data文件上传
seed 的使用建议:
- 建议传非负整数,例如
1234 - 要复现结果时,应同时固定
input、voice、language、instruct、response_format - 复现结论以同一镜像版本、同一模型权重、同一音色配置为前提
- 不传
seed时,请求会走默认随机路径,不保证多次结果一致
language 的执行规则:
- 请求里传入
language时,优先级高于服务端静态 voice 配置中的默认语言 - 请求里不传
language时,沿用当前服务默认行为;Orin 自定义镜像默认一般为Auto - 常见可用值可按模型能力使用,例如:
Chinese、English、Japanese、Korean、German、French、Russian、Portuguese、Spanish、Italian
voice 的执行规则:
voice请求参数大小写不敏感,建议先通过/v1/audio/voices获取可用值- 如果传入不存在的
voice,服务会优先回退到default_voice
当前不支持的请求级参数:
speakerinstructionstemperaturedo_sampletop_ktop_prepetition_penalty
说明:
- OpenAI 接口里应使用
voice,而不是speaker - OpenAI 接口里应使用
instruct,而不是instructions - 采样相关内部参数当前没有作为 OpenAI 请求字段对外暴露,文档中不建议继续传递
voice_clone_pt不是 JSON 字段;如需传入pt文件,请改用multipart/form-data
响应说明:
response_format | Content-Type | 是否流式 | 说明 |
|---|---|---|---|
wav | audio/wav | 是 | 先返回 WAV header,再持续返回 PCM 音频块 |
pcm | audio/pcm | 是 | 直接返回 16-bit little-endian PCM 音频流 |
mp3 | audio/mpeg | 否 | 先生成完整音频,再整体编码为 mp3 |
PT 声纹克隆接口
以下能力只在服务运行于 clone 模式时可用,典型镜像为:
registry.lazycat.cloud/x/faster-qwen3-tts:0.6b-base-clone-openai-orin-v2
clone 模式下推荐把 voice 固定传成 dynamic,然后通过请求级 voice_clone_pt 或静态 voices.json 决定说话人。
POST /v1/audio/voice-clone/pt
该接口根据一段参考音频提取 speaker embedding,并直接把对应的 pt 文件返回给调用方。
请求方式:
Content-Type: multipart/form-data- 文件字段:
ref_audio - 可选表单字段:
filename
请求示例:
curl https://qwen3tts-ai.${LAZYCAT_BOX_DOMAIN}/v1/audio/voice-clone/pt \
-F "ref_audio=@ref_audio.wav;type=audio/wav" \
-F "filename=speaker.pt" \
--output speaker.pt说明:
- 服务端不会持久化这个
pt文件 pt文件应由调用方自行保存,例如放到对象存储、NAS、数据库或业务侧文件目录- 同一个说话人的后续请求可以反复复用同一个
pt
在请求中直接使用 pt
当你已经拿到 speaker.pt 后,可以直接在 POST /v1/audio/speech 中上传。
这时请求必须使用 multipart/form-data,因为既要传文本字段,也要传文件字段。
请求示例:
curl https://qwen3tts-ai.${LAZYCAT_BOX_DOMAIN}/v1/audio/speech \
-H "Accept: audio/wav" \
-F "model=tts-1" \
-F "input=今天的风从江面慢慢吹过来,像有人把一句话轻轻放在耳边。" \
-F "voice=dynamic" \
-F "response_format=wav" \
-F "language=Chinese" \
-F "seed=1234" \
-F "voice_clone_pt=@speaker.pt;type=application/octet-stream" \
--output speech.wav可用文件字段名:
voice_clone_ptspeaker_ptpt
执行规则:
- 只在
clone模式下可用 - 请求里上传
voice_clone_pt时,优先级高于服务端静态voices.json里的speaker_pt - 同一个
pt搭配不同文本时,应保持同一说话人特征;如需提升复现性,建议同时固定seed
将 pt 静态挂到服务里
如果你不想每次请求都重复上传 speaker.pt,可以把 pt 文件和 voices.json 一起挂到容器里。
voices.json 示例:
{
"alice": {
"speaker_pt": "/data/voices/alice.pt",
"language": "Chinese"
},
"narrator": {
"speaker_pt": "/data/voices/narrator.pt",
"language": "Chinese",
"instruct": "请用平稳、自然的旁白语气朗读。"
}
}启动后:
GET /v1/audio/voices会返回alice、narrator- 调用
POST /v1/audio/speech时直接传voice=alice或voice=narrator - 这时请求体可以继续使用普通 JSON,不需要每次再上传
pt speaker_pt和instruct可以同时使用;如果voices.json已配置默认instruct,请求里再传instruct时会覆盖静态配置- 但在
speaker_pt场景下,instruct仍属于实验性支持,效果不保证稳定;如果你强依赖指令跟随,优先考虑直接使用ref_audio + ref_text的 ICL 模式
非流式调用示例
生成 WAV
curl https://qwen3tts-ai.${LAZYCAT_BOX_DOMAIN}/v1/audio/speech \
-H "Content-Type: application/json" \
-d '{"model":"tts-1","input":"欢迎使用 Qwen3TTS 服务。","voice":"vivian","response_format":"wav"}' \
--output speech.wav生成 WAV 并传入 instruct
curl https://qwen3tts-ai.${LAZYCAT_BOX_DOMAIN}/v1/audio/speech \
-H "Content-Type: application/json" \
-d '{"model":"tts-1","input":"欢迎使用 Qwen3TTS 服务。","voice":"vivian","response_format":"wav","instruct":"请用温和、清晰、偏慢一点的语气朗读。"}' \
--output speech.wav生成 WAV,并固定 seed
curl https://qwen3tts-ai.${LAZYCAT_BOX_DOMAIN}/v1/audio/speech \
-H "Content-Type: application/json" \
-d '{"model":"tts-1","input":"欢迎使用 Qwen3TTS 服务。","voice":"vivian","response_format":"wav","seed":1234}' \
--output speech.wav说明:
- 相同请求体重复调用时,固定同一个
seed可以得到相同结果 - 如果你更换了文本、音色、语言、指令、模型版本或镜像版本,输出也会随之变化
生成 WAV,并显式指定 language + instruct + seed
curl https://qwen3tts-ai.${LAZYCAT_BOX_DOMAIN}/v1/audio/speech \
-H "Content-Type: application/json" \
-d '{"model":"tts-1","input":"其实我真的有发现,我是一个特别善于观察别人情绪的人。","voice":"vivian","response_format":"wav","language":"Chinese","instruct":"用特别愤怒的语气说","seed":1234}' \
--output speech.wav生成 MP3
curl https://qwen3tts-ai.${LAZYCAT_BOX_DOMAIN}/v1/audio/speech \
-H "Content-Type: application/json" \
-d '{"model":"tts-1","input":"欢迎使用 Qwen3TTS 服务。","voice":"vivian","response_format":"mp3"}' \
--output speech.mp3说明:
mp3会在完整音频生成后再编码,不适合低延迟首包场景- 边收边播建议优先用
wav或pcm
流式调用说明
这个服务的“流式”不是 SSE 文本事件流,而是直接返回音频字节流:
wav/pcm会通过 HTTP chunked transfer 逐块返回mp3不支持边生成边返回
curl 流式保存
curl -N https://qwen3tts-ai.${LAZYCAT_BOX_DOMAIN}/v1/audio/speech \
-H "Content-Type: application/json" \
-d '{"model":"tts-1","input":"今天我们测试流式音频返回。","voice":"vivian","response_format":"wav"}' \
--output stream.wavPython 流式读取
import requests
url = f"https://qwen3tts-ai.{LAZYCAT_BOX_DOMAIN}/v1/audio/speech"
payload = {
"model": "tts-1",
"input": "今天我们测试流式音频返回。",
"voice": "vivian",
"response_format": "wav",
"language": "Chinese",
"instruct": "请用自然停顿、比较平稳的语气朗读。",
}
with requests.post(url, json=payload, stream=True, timeout=600) as resp:
resp.raise_for_status()
with open("stream.wav", "wb") as f:
for chunk in resp.iter_content(chunk_size=4096):
if chunk:
f.write(chunk)JavaScript 流式读取
const resp = await fetch(`https://qwen3tts-ai.${boxDomain}/v1/audio/speech`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
model: "tts-1",
input: "今天我们测试流式音频返回。",
voice: "vivian",
response_format: "wav",
language: "Chinese",
instruct: "请用自然、平稳的旁白语气朗读。"
})
});
if (!resp.ok) {
throw new Error(`HTTP ${resp.status}`);
}
const reader = resp.body.getReader();
const chunks = [];
while (true) {
const { value, done } = await reader.read();
if (done) break;
chunks.push(value);
}
const blob = new Blob(chunks, { type: "audio/wav" });常见错误
400 参数错误
{
"detail": "'input' text is empty"
}或:
{
"detail": "response_format 'aac' not supported. Use: wav, pcm, mp3"
}400 音色不存在
{
"detail": "Voice 'unknown' is not configured. Available voices: ['aiden', 'vivian']"
}503 服务未就绪
{
"detail": "Model not loaded"
}单独部署使用
方案一:CustomVoice 固定音色
- 将下面内容保存到一个
docker-compose.yml文件中
services:
faster-qwen3-tts:
image: registry.lazycat.cloud/x/faster-qwen3-tts:0.6b-custom-openai-orin-v11
environment:
HF_HUB_OFFLINE: "1"
TRANSFORMERS_OFFLINE: "1"
HF_HOME: /root/.cache/huggingface
XDG_CACHE_HOME: /root/.cache
QWEN_TTS_MODEL: /opt/models/Qwen3-TTS-12Hz-0.6B-CustomVoice
QWEN_TTS_MODE: custom
QWEN_TTS_DEFAULT_VOICE: vivian
QWEN_TTS_LANGUAGE: Auto
QWEN_TTS_CHUNK_SIZE: "8"
QWEN_TTS_WARMUP_MAX_NEW_TOKENS: "32"
volumes:
- /etc/timezone:/etc/timezone:ro
- ${LZC_AGENT_CACHE_DIR}/cache:/root/.cache
- ${LZC_AGENT_DATA_DIR}/data:/data
labels:
- "traefik.enable=true"
- "traefik.http.routers.${LZC_SERVICE_ID}-qwen3tts.rule=Host(`qwen3tts-ai`)"
expose:
- 8000
healthcheck:
test: [ "CMD", "curl", "-f", "http://localhost:8000/health" ]
interval: 30s
timeout: 5s
retries: 100
start_period: 1s
networks:
default:
external: true
name: traefik-shared-network- 在当前目录运行
docker-compose up -d启动 - 先通过
http://127.0.0.1:8000/health检查服务是否已经ready=true - 再通过
http://127.0.0.1:8000/v1/audio/voices获取可用音色 - 最后再调用
http://127.0.0.1:8000/v1/audio/speech
推荐的启动后校验顺序:
curl http://127.0.0.1:8000/health
curl http://127.0.0.1:8000/v1/audio/voices
curl http://127.0.0.1:8000/v1/audio/speech \
-H "Content-Type: application/json" \
-d '{"model":"tts-1","input":"启动校验。","voice":"vivian","response_format":"wav","instruct":"请用简短、清晰的方式朗读。"}' \
--output verify.wav如果你是本机单独调试而不是挂到算力舱网关后面,可以把上面的 expose 改成:
ports:
- "8000:8000"当前推荐部署说明:
- 推荐使用
0.6b-custom-openai-orin-v11 - OpenAI 接口当前建议使用的请求字段是:
voice、language、instruct、seed - 如果你需要复现同一份音频,请在请求中显式传入固定
seed speed当前虽被兼容接受,但暂未实际生效,因此文档未列为推荐参数
方案二:Base + Clone + PT
如果你希望调用方自己管理 speaker.pt,或者希望把同一个 pt 绑定成多个命名音色,推荐使用 clone 镜像。
docker-compose.yml 示例:
services:
faster-qwen3-tts:
image: registry.lazycat.cloud/x/faster-qwen3-tts:0.6b-base-clone-openai-orin-v2
environment:
HF_HUB_OFFLINE: "1"
TRANSFORMERS_OFFLINE: "1"
HF_HOME: /root/.cache/huggingface
XDG_CACHE_HOME: /root/.cache
QWEN_TTS_MODEL: /opt/models/Qwen3-TTS-12Hz-0.6B-Base
QWEN_TTS_MODE: clone
QWEN_TTS_DEFAULT_VOICE: dynamic
QWEN_TTS_VOICES: /data/voices.json
QWEN_TTS_LANGUAGE: Auto
QWEN_TTS_CHUNK_SIZE: "8"
QWEN_TTS_WARMUP_MAX_NEW_TOKENS: "32"
volumes:
- /etc/timezone:/etc/timezone:ro
- ${LZC_AGENT_CACHE_DIR}/cache:/root/.cache
- ${LZC_AGENT_DATA_DIR}/data:/data
labels:
- "traefik.enable=true"
- "traefik.http.routers.${LZC_SERVICE_ID}-qwen3tts.rule=Host(`qwen3tts-ai`)"
expose:
- 8000
healthcheck:
test: [ "CMD", "curl", "-f", "http://localhost:8000/health" ]
interval: 30s
timeout: 5s
retries: 100
start_period: 1s
networks:
default:
external: true
name: traefik-shared-network推荐目录结构:
/data
├── voices.json
└── voices
├── alice.pt
└── narrator.pt推荐调用顺序:
- 先调用
POST /v1/audio/voice-clone/pt生成speaker.pt - 把
speaker.pt保存到/data/voices/*.pt - 更新
/data/voices.json - 重启服务或重新部署,使服务加载新的
voices.json - 之后直接用
voice=<配置名>调用POST /v1/audio/speech
如果你不想重启服务,也可以继续走请求级上传:
curl https://qwen3tts-ai.${LAZYCAT_BOX_DOMAIN}/v1/audio/speech \
-H "Accept: audio/wav" \
-F "model=tts-1" \
-F "input=这是一次请求级 pt 调用。" \
-F "voice=dynamic" \
-F "response_format=wav" \
-F "voice_clone_pt=@speaker.pt;type=application/octet-stream" \
--output speech.wav与当前 tts-ai 服务的区别
tts-ai当前文档对应的是Kokoro服务qwen3tts-ai是 OpenAI 兼容的 Qwen3TTS 服务qwen3tts-ai适合多音色、OpenAI-compatible client 和流式音频返回场景