Skip to content

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
  • 支持请求级 languageinstructseed
  • 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。

启动流程大致如下:

  1. 加载模型与音色配置
  2. 初始化 CUDA graphs
  3. 执行一轮 warmup 请求
  4. /health 中的 ready 变为 true
  5. 才建议上游 AI 应用或脚本发起正式 TTS 请求

执行上的注意点:

  • 容器进程已经起来,不代表服务已经 ready
  • 应用接入时应轮询 /health,并以 ready=true 作为可用标准
  • 文档中的 Docker healthcheck 主要用于检测 HTTP 服务存活;如果你需要严格 readiness,请额外检查 /health 返回体中的 ready 字段

健康检查

GET /health

该接口用于判断服务是否完成模型加载和 startup warmup。

请求:

bash
curl https://qwen3tts-ai.${LAZYCAT_BOX_DOMAIN}/health

响应示例:

json
{
  "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

请求:

bash
curl https://qwen3tts-ai.${LAZYCAT_BOX_DOMAIN}/v1/audio/voices

响应示例:

json
{
  "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 请求头:

http
Content-Type: application/json
Accept: audio/wav

JSON 请求体示例:

json
{
  "model": "tts-1",
  "input": "欢迎使用 Qwen3TTS 服务。",
  "voice": "vivian",
  "response_format": "wav",
  "language": "Chinese",
  "instruct": "请用温和、清晰、偏慢一点的语气朗读。",
  "seed": 1234
}

参数说明:

参数类型必填说明
modelstring兼容字段,建议固定传 tts-1
inputstring待合成文本,不能为空
voicestring音色名,建议先通过 /v1/audio/voices 获取
response_formatstring支持 wav / pcm / mp3
languagestring请求级语言,传入时覆盖该 voice 的默认语言配置
instructstring自定义指令,用于控制语气、风格、节奏或口音倾向
seedinteger请求级随机种子。相同文本、音色、语言、指令和模型版本下,固定同一个 seed 可以复现同一份音频输出

instruct 的执行规则:

  • 当前服务运行在 customclone 模式时可用
  • 请求里传入 instruct 时,优先级高于服务端静态 voice 配置中的默认 instruct
  • 请求里不传 instruct 时,沿用当前服务默认行为
  • 如果你只关心 OpenAI 兼容调用方式,可以把它理解为一个可选扩展字段

当前请求级扩展参数:

  • 已暴露并建议使用的字段:languageinstructseed
  • language 用于覆盖音色默认语言
  • instruct 用于控制语气、风格、节奏或口音倾向
  • seed 用于让同一请求在同一服务版本下可复现
  • voice_clone_pt 仅在 clone 模式下可用,并且必须通过 multipart/form-data 文件上传

seed 的使用建议:

  • 建议传非负整数,例如 1234
  • 要复现结果时,应同时固定 inputvoicelanguageinstructresponse_format
  • 复现结论以同一镜像版本、同一模型权重、同一音色配置为前提
  • 不传 seed 时,请求会走默认随机路径,不保证多次结果一致

language 的执行规则:

  • 请求里传入 language 时,优先级高于服务端静态 voice 配置中的默认语言
  • 请求里不传 language 时,沿用当前服务默认行为;Orin 自定义镜像默认一般为 Auto
  • 常见可用值可按模型能力使用,例如:ChineseEnglishJapaneseKoreanGermanFrenchRussianPortugueseSpanishItalian

voice 的执行规则:

  • voice 请求参数大小写不敏感,建议先通过 /v1/audio/voices 获取可用值
  • 如果传入不存在的 voice,服务会优先回退到 default_voice

当前不支持的请求级参数:

  • speaker
  • instructions
  • temperature
  • do_sample
  • top_k
  • top_p
  • repetition_penalty

说明:

  • OpenAI 接口里应使用 voice,而不是 speaker
  • OpenAI 接口里应使用 instruct,而不是 instructions
  • 采样相关内部参数当前没有作为 OpenAI 请求字段对外暴露,文档中不建议继续传递
  • voice_clone_pt 不是 JSON 字段;如需传入 pt 文件,请改用 multipart/form-data

响应说明:

response_formatContent-Type是否流式说明
wavaudio/wav先返回 WAV header,再持续返回 PCM 音频块
pcmaudio/pcm直接返回 16-bit little-endian PCM 音频流
mp3audio/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

请求示例:

bash
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,因为既要传文本字段,也要传文件字段。

请求示例:

bash
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_pt
  • speaker_pt
  • pt

执行规则:

  • 只在 clone 模式下可用
  • 请求里上传 voice_clone_pt 时,优先级高于服务端静态 voices.json 里的 speaker_pt
  • 同一个 pt 搭配不同文本时,应保持同一说话人特征;如需提升复现性,建议同时固定 seed

pt 静态挂到服务里

如果你不想每次请求都重复上传 speaker.pt,可以把 pt 文件和 voices.json 一起挂到容器里。

voices.json 示例:

json
{
  "alice": {
    "speaker_pt": "/data/voices/alice.pt",
    "language": "Chinese"
  },
  "narrator": {
    "speaker_pt": "/data/voices/narrator.pt",
    "language": "Chinese",
    "instruct": "请用平稳、自然的旁白语气朗读。"
  }
}

启动后:

  • GET /v1/audio/voices 会返回 alicenarrator
  • 调用 POST /v1/audio/speech 时直接传 voice=alicevoice=narrator
  • 这时请求体可以继续使用普通 JSON,不需要每次再上传 pt
  • speaker_ptinstruct 可以同时使用;如果 voices.json 已配置默认 instruct,请求里再传 instruct 时会覆盖静态配置
  • 但在 speaker_pt 场景下,instruct 仍属于实验性支持,效果不保证稳定;如果你强依赖指令跟随,优先考虑直接使用 ref_audio + ref_text 的 ICL 模式

非流式调用示例

生成 WAV

bash
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

bash
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

bash
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

bash
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

bash
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 会在完整音频生成后再编码,不适合低延迟首包场景
  • 边收边播建议优先用 wavpcm

流式调用说明

这个服务的“流式”不是 SSE 文本事件流,而是直接返回音频字节流:

  • wav / pcm 会通过 HTTP chunked transfer 逐块返回
  • mp3 不支持边生成边返回

curl 流式保存

bash
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.wav

Python 流式读取

python
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 流式读取

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 参数错误

json
{
  "detail": "'input' text is empty"
}

或:

json
{
  "detail": "response_format 'aac' not supported. Use: wav, pcm, mp3"
}

400 音色不存在

json
{
  "detail": "Voice 'unknown' is not configured. Available voices: ['aiden', 'vivian']"
}

503 服务未就绪

json
{
  "detail": "Model not loaded"
}

单独部署使用

方案一:CustomVoice 固定音色

  1. 将下面内容保存到一个 docker-compose.yml 文件中
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
  1. 在当前目录运行 docker-compose up -d 启动
  2. 先通过 http://127.0.0.1:8000/health 检查服务是否已经 ready=true
  3. 再通过 http://127.0.0.1:8000/v1/audio/voices 获取可用音色
  4. 最后再调用 http://127.0.0.1:8000/v1/audio/speech

推荐的启动后校验顺序:

bash
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 改成:

yml
ports:
  - "8000:8000"

当前推荐部署说明:

  • 推荐使用 0.6b-custom-openai-orin-v11
  • OpenAI 接口当前建议使用的请求字段是:voicelanguageinstructseed
  • 如果你需要复现同一份音频,请在请求中显式传入固定 seed
  • speed 当前虽被兼容接受,但暂未实际生效,因此文档未列为推荐参数

方案二:Base + Clone + PT

如果你希望调用方自己管理 speaker.pt,或者希望把同一个 pt 绑定成多个命名音色,推荐使用 clone 镜像。

docker-compose.yml 示例:

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

推荐目录结构:

text
/data
├── voices.json
└── voices
    ├── alice.pt
    └── narrator.pt

推荐调用顺序:

  1. 先调用 POST /v1/audio/voice-clone/pt 生成 speaker.pt
  2. speaker.pt 保存到 /data/voices/*.pt
  3. 更新 /data/voices.json
  4. 重启服务或重新部署,使服务加载新的 voices.json
  5. 之后直接用 voice=<配置名> 调用 POST /v1/audio/speech

如果你不想重启服务,也可以继续走请求级上传:

bash
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 和流式音频返回场景