很多 AI 应用最后一步都要”开口说话”——有声书、智能客服、视频配音、播客生成,背后都离不开 TTS(Text-To-Speech,语音合成)。调用云厂商接口最省事,但按字符计费、有合规和隐私顾虑,还受网络波动影响。本文用一线的本地 TTS 方案,带你把文字转语音服务跑在自己机器上,覆盖 ChatTTS、CosyVoice2 两大主流开源模型的选型、部署与 API 封装。
一、为什么要在本地跑 TTS
云端 TTS 看似简单,落地到生产常踩三个坑:第一是成本,长文本按字符累加,有声书批量生成时账单惊人;第二是数据合规,把内部文案、用户对话送出去做语音合成,可能触碰隐私红线;第三是可用性,外网抖动会让整个播报链路雪崩。把 TTS 收到本地,一次部署、无限调用,延迟和费用都可控,配合你已经跑起来的本地大模型生态(参考用 Dify + Ollama 搭建私有 AI 知识库)就能组成完整的”听—说”闭环。
举个真实场景:一位做法律科普的博主,每天要把近两万字的稿子转成配音。用云端 TTS 按千字几分钱累计,一个月也要上百元,而且稿件里常有未公开案例,根本不敢往外传。换成一台闲置换下的游戏本跑 ChatTTS,电费几乎可以忽略,稿子全程不出本机。这正是本地 TTS 的甜区:高频、批量、对隐私敏感的生产任务。
二、主流本地 TTS 方案速览
开源社区这几年进展飞快,下面四种是工程里最常遇到的选型,按”是否需本地模型”分成两派:
| 方案 | 是否需要本地模型 | 中文自然度 | 特色能力 | 硬件门槛 |
|---|---|---|---|---|
| ChatTTS | 是(约 1.5G) | 高,带语气词 | 笑声、停顿标签可控 | 消费级 GPU 即可 |
| CosyVoice2 | 是(0.5B~1.5B) | 很高,支持多语种 | 音色克隆、情感指令 | 建议 6G+ 显存 |
| Bark | 是 | 中,偏口语 | 音乐、非语言声音 | 显存占用较大 |
| edge-tts | 否(调微软接口) | 高 | 免模型、零部署 | 仅联网,非真正本地 |
追求”真正离线 + 可控”,优先考虑 ChatTTS 与 CosyVoice2;只想免部署快速验证,edge-tts 仍是好补丁。下文重点讲前两者。
三、ChatTTS 快速上手
3.1 安装与基础调用
ChatTTS 是社区最易上手的本地 TTS,几行代码就能出声。先装依赖:
pip install ChatTTS torch soundfile
# 如需 GPU 加速,安装对应 CUDA 版本的 torch
基础合成只需要加载模型、喂文本、写出 wav:
from chattts import ChatTTS
import torch, soundfile as sf
chat = ChatTTS.Chat()
chat.load(compile=False) # compile=True 可提速但首次较慢
texts = ["你好,这是一段本地合成的语音。"]
wavs = chat.infer(texts)
sf.write("out.wav", wavs[0], 24000) # ChatTTS 输出采样率 24kHz
ChatTTS 的模型权重约 1.5G,首次 chat.load() 会下载并缓存到本地,之后启动只需几秒。它对显存很友好,4G 显存的入门卡就能实时合成;如果只有 CPU,速度会慢几倍,但批量转长音频完全够用。社区还提供了预编译的 WebUI,不熟悉 Python 也能图形化操作。
3.2 用语气标签让声音更自然
ChatTTS 的杀手锏是口语化标签:用 [laugh] 插入笑声、[uv_break] 控制停顿。配合随机种子还能固定某个”音色”:
texts = ["今天天气真好[laugh],我们一起去散步吧[uv_break]。"]
params = ChatTTS.Chat.InferCodeParams(
spk_emb = chat.sample_random_speaker(), # 固定说话人
temperature = 0.3,
)
wavs = chat.infer(texts, params_infer_code=params)
sf.write("natural.wav", wavs[0], 24000)
四、CosyVoice2:跨语言与音色克隆
4.1 拉起推理服务
CosyVoice2 由阿里开源,强项是多语种和零样本音色克隆。克隆只需 3–10 秒参考音频:
import sys, torchaudio
sys.path.append("CosyVoice")
from cosyvoice.cli.cosyvoice import CosyVoice2
from cosyvoice.utils.file_utils import load_wav
cosyvoice = CosyVoice2("models/CosyVoice2-0.5B")
# 基础指令式 TTS:用自然语言描述想要的语气
out = cosyvoice.inference_instruct(
tts_text="欢迎使用本地语音合成服务。",
instruct_text="用温柔的女声、语速稍慢地朗读。",
spk_id="中文女",
)
torchaudio.save("cosy.wav", out["tts_speech"], 24000)
CosyVoice2 的 0.5B 版本对显存更友好,单张 6G 卡即可跑起来;若要更高音质或做服务端并发,建议 12G 以上并配合推理引擎的连续批处理。它和 ChatTTS 最大的区别在于”指令驱动”——你不用录参考音,直接用自然语言告诉它想要什么声线,适合快速试不同风格。
4.2 用参考音频克隆音色
如果不想写指令,直接给一段参考音频让模型”学声线”:
prompt = load_wav("ref.wav", 24000) # 3~10 秒干净人声
out = cosyvoice.inference_zero_shot(
tts_text="这段话会用参考音色念出来。",
prompt_text="参考音频对应的原文。",
prompt_speech=prompt,
)
torchaudio.save("clone.wav", out["tts_speech"], 24000)
五、封装成 HTTP 服务
单条脚本不够用,生产里通常包一层 API。下面用 FastAPI 把 ChatTTS 包成可并发调用的服务:
from fastapi import FastAPI, Query
from chattts import ChatTTS
import torch, io, soundfile as sf, base64
app = FastAPI()
chat = ChatTTS.Chat(); chat.load(compile=False)
@app.get("/tts")
def tts(text: str = Query(..., max_length=2000)):
wav = chat.infer([text])[0]
buf = io.BytesIO(); sf.write(buf, wav, 24000)
return {"audio_b64": base64.b64encode(buf.getvalue()).decode()}
# 启动:uvicorn main:app --host 0.0.0.0 --port 8000
前端拿到 audio_b64 直接 new Audio("data:audio/wav;base64,"+...) 即可播放。若并发高、要 GPUs 调度,可参考大模型推理引擎选型(vLLM/SGLang/Ollama)的思路把模型服务独立部署。
真实业务里 TTS 往往是被其他服务调用的”下游”,所以接口设计要尽量简单可靠:入参只留文本,出参返回可直接播放的数据。上面用 base64 内联只是演示;生产环境更推荐把 wav 存到对象存储或本地磁盘,返回 URL,避免把大体积音频塞进 JSON。如果担心单点,把 ChatTTS 进程和 FastAPI 拆成独立容器,用消息队列削峰,稳定性会好很多。
六、接进你的 AI 应用
TTS 很少单独存在,它通常是 AI 管线的”出口”。典型组合是:大模型生成文案 → TTS 转语音 → 推流或存文件。把 TTS 服务和本地大模型放在同一内网,配合用 GGUF 与 llama.cpp 做本地量化部署跑出来的轻量模型,整条链路完全离线,既快又省。需要可视化编排时,Dify 的工作流也能直接挂一个 HTTP 节点调用上面的 /tts 接口。
一个落地的例子:先用本地大模型把产品文档总结成口播稿,再调 TTS 生成讲解音频,最后自动上传到播客平台。全程不依赖任何外部 API,文案和音频都不会离开你的服务器,既满足合规,又把边际成本压到接近零。这种”本地大模型 + 本地 TTS”的组合,正在成为很多个人开发者和中小企业做语音内容的标准打法。
七、性能与延迟预期
落地前对性能有个大致预期,能少踩很多坑。下面是消费级硬件上的经验值,实际会随模型与文本长度波动:
| 方案 | 硬件 | 单句延迟(约) | 实时倍数 |
|---|---|---|---|
| ChatTTS | RTX 3060 12G | 0.3–0.8s / 句 | 5–10x |
| CosyVoice2 0.5B | RTX 4060 8G | 0.5–1.2s / 句 | 3–8x |
| ChatTTS(纯 CPU) | i7 笔记本 | 3–6s / 句 | 约 1x |
所谓”实时倍数”指合成速度相对音频时长的比例,大于 1 即比真人说话还快。做实时对话要保住 5x 以上;做离线批量配音,纯 CPU 也能扛。关键是把长文本先按句号切句,再并发或排队合成,避免单次塞入上千字导致显存峰值和截断。
八、上线前的避坑清单
本地 TTS 真上线,有几个高频雷区:
- 中文标点:ChatTTS 对句号、逗号敏感,漏标点会导致吞字或断句怪异,生成前先用正则规整标点。
- 显存峰值:CosyVoice2 推理时激活占用不低,批量任务用队列削峰,别一次性灌满。
- 采样率:两个模型都输出 24kHz,前端播放器或下游编码要匹配,否则变调。
- 长文本切分:单次超过模型上限会截断,按句号切句后逐句合成再拼接更稳。
- 并发:单模型实例不是线程安全的,多 worker 用多进程或独立服务实例。
九、小结
本地 TTS 已经从”玩具”变成可落地的生产组件。轻量、要语气可控选 ChatTTS;要跨语言、音色克隆选 CosyVoice2。把它们封成 HTTP 服务,再接进你已有的本地大模型体系,就能拥有零成本、可离线、数据不出域的语音合成能力。下一步可以叠加声音克隆做品牌专属播报,或和 ASR 组成语音对话闭环。




