如果你使用 DeepSeek、元宝 AI、Copilot、ChatGPT 等 AI 工具,协助配置轩辕镜像、编写 docker pull 命令、修改 Docker Compose 镜像地址、配置镜像加速、排查镜像拉取失败、分析报错日志等问题,请先让 AI 阅读并遵守轩辕镜像的规则文档。
只需在 AI 对话中先发送下面这段话即可:
请先阅读并遵守:https://xuanyuan.cloud/agents.md
未读文档前不要生成 pull 命令或排错方案。查看 agents.md 用法指南与完整示范。国内用户首推 元宝 AI、DeepSeek 的深度思考模式,不推荐豆包 AI;Cursor 等编辑器可在对话 @ 该链接,或加入 User Rules。 若 AI 无法访问外链,可 打开说明文档 复制全文粘贴。文档会随站点更新,复制内容可能过期,建议定期检查。
Kokoro-FastAPI 是 https://huggingface.co/hexgrad/Kokoro-82M 文本转语音模型的 Docker 化 FastAPI 封装。可在几分钟内生成数小时的高质量语音。
兼容 OpenAI 的语音端点,支持多语言
英语(美国/英国)、西班牙语、法语、印地语、意大利语、日语、巴西葡萄牙语、普通话
通过 https://github.com/remsky/inno-kokoro 生成自定义语音包
可选集成 WebUI;支持长文本朗读跟随
内联多说话人生成与语音混合 + 别名加权组合,支持 SSML
逐词或逐块时间戳字幕生成
音素端点:从文本生成音素,或从音素生成音频
预构建多平台镜像
CPU 和 NVIDIA GPU(CUDA):linux/amd64 + linux/arm64
AMD GPU(ROCm,实验性):仅 linux/amd64
Apple Silicon(MPS)支持通过 UV 直接运行(无镜像)
使用比例的加权语音组合(例如,"af_bella(2)+af_heart(1)" 表示 67%/33% 的混合)
比例会自动归一化,总和为 100%
可通过在任何端点的括号中添加权重来使用
保存生成的语音包以供将来使用
组合语音并生成音频:
import requests
response = requests.get("http://localhost:8880/v1/audio/voices")
voices = [v["id"] for v in response.json()["voices"]]
# 加权语音组合(67%/33% 混合)
response = requests.post(
"http://localhost:8880/v1/audio/speech",
json={
"input": "Hello world!",
"voice": "af_bella(2)+af_sky(1)", # 2:1 比例 = 67%/33%
"response_format": "mp3"
}
)
# 下载组合语音为 .pt 文件
response = requests.post(
"http://localhost:8880/v1/audio/voices/combine",
json="af_bella(2)+af_sky(1)" # 2:1 比例 = 67%/33%
)
# 保存 .pt 文件
with open("combined_voice.pt", "wb") as f:
f.write(response.content)
# 使用下载的语音文件
response = requests.post(
"http://localhost:8880/v1/audio/speech",
json={
"input": "Hello world!",
"voice": "combined_voice", # 使用保存的语音文件
"response_format": "mp3"
}
)
加权混合可能会很快变得冗长。voice_aliases 为每个请求映射一个短名称,适用于 voice 字段和 [voice:...] 标签:
{"voice_aliases": {...}});可与 API 调用互换。from openai import OpenAI
client = OpenAI(base_url="http://localhost:8880/v1", api_key="not-needed")
client.audio.speech.create(
model="kokoro",
voice="narrator",
input="[voice:narrator] Once upon a time. [voice:villain] Never!",
extra_body={
"allow_voice_tags": True,
"voice_aliases": {"narrator": "af_bella(2)+af_sky", "villain": "am_michael"},
},
)
或
curl -X POST http://localhost:8880/v1/audio/speech \
-H "Content-Type: application/json" \
-d '{
"model": "kokoro",
"voice": "narrator",
"input": "[voice:narrator] Once upon a time. [voice:villain] Never!",
"allow_voice_tags": true,
"voice_aliases": {"narrator": "af_bella(2)+af_sky", "villain": "am_michael"},
"response_format": "mp3"
}' --output aliased.mp3
POST /dev/tune 通过 https://github.com/remsky/inno-kokoro 接收 3 至 30 秒的单个英语说话者音频片段,并生成向该音频调优的语音。这是调优工具,而非克隆工具:预期语音风格相似,但并非完全匹配。仅调优您有权使用的语音。
curl -s http://localhost:8880/dev/tune -F audio=@ref.wav -F 'request={"input":"Hello there."}' -o out.mp3
curl -s http://localhost:8880/dev/tune -F audio=@ref.wav -F return_voice_pack=true -o ref.pt
curl -s http://localhost:8880/dev/tune -F audio=@ref.wav -F save_voice=am_ref # 保存为 am_ref_tuned,需设置 ALLOW_LOCAL_VOICE_SAVING=true
request 是 /v1/audio/speech 的请求体(不含 voice 字段);除非使用 save_voice 将其保存在 VOICES_DIR 中,否则语音包仅在响应期间存在am_、bf_、ax_ 等),保存的语音会添加 _tuned 后缀。捆绑的调优语音以 _inno 结尾prosody_head(默认启用)、fmax 音高上限(单位 Hz,默认自动)ENABLE_INNO_TUNER=true 启用并显示 Web 播放器选项卡ALLOW_LOCAL_VOICE_SAVING=true 允许将语音保存到运行中的服务器。[voice:...] 标签可在任何接受 input 的位置内联切换说话者/v1/audio/speech 每个请求需要 allow_voice_tags: true;/dev/dialogue 默认允许标签ENABLE_VOICE_TAGS=false 全局禁用:该参数会被拒绝,且 /dev/dialogue 返回 403 错误curl -X POST http://localhost:8880/v1/audio/speech \
-H "Content-Type: application/json" \
-d '{
"model": "kokoro",
"voice": "af_heart",
"input": "The narrator opens. [voice:af_bella] Did it land? [pause:0.3s] [voice:am_michael] It did.",
"allow_voice_tags": true,
"response_format": "mp3"
}' --output dialogue.mp3
使用官方 OpenAI 客户端时,在 extra_body 中传递参数:
client.audio.speech.create(
model="kokoro",
voice="af_jadzia",
input="The narrator opens. [voice:af_bella] Did it land?",
extra_body={"allow_voice_tags": True},
)
POST /dev/dialogue 使用结构化的对话轮次,并允许通过参数控制 pause_between_turns。
curl -X POST http://localhost:8880/dev/dialogue \
-H "Content-Type: application/json" \
-d '{
"turns": [
{"voice": "af_bella", "text": "Did the multi speaker support land?"},
{"voice": "am_michael", "text": "It did. Turns switch voices inline."}
],
"pause_between_turns": 0.4,
"response_format": "mp3"
}' --output dialogue.mp3
注意:
voice 或默认语音。lang_code 会覆盖所有说话者的语言设置。语音数量对生成速度的影响极小。但对于连续切换的情况,如果每个说话者的文本少于约 2 句话,分块处理需求会减慢生成速度。生成成本仍为固定值,不会随文本长度增加而复合增长。可使用 examples/assorted_checks/test_dialogue/ 重新生成。
[pause:1.5s] 插入指定时长的静音。必须严格遵循此格式(冒号、末尾 s、不区分大小写)。[pause=1.5] 和 [PAUSE 1.0] 无法识别,会被直接朗读。[Worcester](/wˈʊstər/) 会朗读斜杠间的国际音标(IPA)而非单词本身。仅支持英语;使用 /dev/phonemize 获取国际音标。[voice:am_michael] 切换后续内容的发言者。allow_voice_tags: true,且服务器端需启用 ENABLE_VOICE_TAGS(默认开启)。否则标签会按原样朗读。voice 参数相同的组合语法([voice:af_bella(2)+af_sky]),voice_aliases 中定义短名称/别名,[rate:1.5] 调整发言语速,直至下一个语速标签或语音切换;[rate:1.0] 恢复默认语速。在请求 speed 的基础上叠加生效,范围限制在 0.25-4.0。与语音标签使用相同的限制条件。{"grandpa": {"voice": "am_michael", "rate": 0.8}} 会在别名被使用时(作为 voice 参数或标签)应用该语速。适用于语速过快或过慢的语音,以及同一语音的命名预设(如 narrator_fast、narrator_slow)。[rate:] 标签会缩放发言者的校准语速而非替换,因此被限制为 0.8 的别名在 [rate:1.1] 下仍保持相应比例的慢速。每个 [voice:...] 标签会重置为新语音的自身语速,因此校准后的语速不会影响下一个语音。使用 speed 可设置整个请求的语速。The city of [Worcester](/wˈʊstər/) is easy. [pause:1s] See?
SSML 输入 🧪
在 /v1/audio/speech 或 /dev/captioned_speech 接口中,同时发送 ssml: true 和 allow_voice_tags: true 可实现一次调用完成翻译和语音合成。这两个标志均为必需,因为翻译会生成 [voice:] 和 [rate:] 片段,若缺少标志这些片段会被直接朗读;仅设置 ssml 会返回 400 错误。
{
"model": "kokoro",
"input": " Hi there ",
"voice": "af_bella",
"allow_voice_tags": true,
"ssml": true
}
POST /dev/ssml 接口可单独进行翻译,适用于需要文本形式的令牌而非音频,或在合成前检查令牌的场景。发送 text,若语音请求使用了语音则需同时发送 voice,然后将结果与 allow_voice_tags: true 一起传回。若未指定语音,会剥离 / 并保留其内容。
<break time="0.75s"> 转换为 [pause:0.75s]。使用 strength= 代替 time= 时,none/x-weak 对应 0s,weak 对应 0.25s,medium 对应 0.5s,strong 对应 1s,x-strong 对应 1.5s<voice name="am_michael"> 转换为 [voice:am_michael],在闭合标签处恢复原语音<prosody rate="0.75"> 转换为 [rate:0.75],也支持 80% 或 1.2。在请求 speed 的基础上缩放语速,范围限制在 0.25-4.0,在闭合标签处恢复。忽略 pitch/volume<phoneme alphabet="ipa" ph="wˈʊstər">Worcester</phoneme> 转换为 [Worcester](/wˈʊstər/),仅支持国际音标和英语<say-as alias="WWW"> 朗读别名<desc> 及其文本会被丢弃,音频描述不属于语音内容<p>、<s>、<emphasis>、<sub>、<b> 等:标记会被丢弃,文本会被朗读SSML_MAX_DEPTH(10)会返回 400 错误;没有方言使用 DTD,实际文档嵌套深度通常为 2-5google:style、mstts:express-as、amazon:effect)需要在 <speak> 标签上添加 xmlns: 声明,厂商文档常省略此声明GET /dev/ssml 接口以数据形式提供这些转换表,直接读取翻译器配置curl -s http://localhost:8880/dev/ssml -H "Content-Type: application/json" \
-d '{"text": " The city of Worcester is easy. See? "}'
# {"text": "The city of [Worcester](/wˈʊstər/) is easy. [pause:1.0s] See?"}
自然边界检测
模型每个分块最多处理 510 个音素化令牌,但过长的分块容易导致“急促”语音及其他失真。服务器在顶层添加了自己的分块层,大小由 TARGET_MIN_TOKENS、TARGET_MAX_TOKENS 和 ABSOLUTE_MAX_TOKENS 控制(默认值分别为 175、250、450,可通过环境变量设置)。
音素与令牌路由
将文本转换为音素和/或直接从音素生成音频:
import requests
def get_phonemes(text: str, language: str = "a"):
"""Get phonemes and tokens for input text"""
response = requests.post(
"http://localhost:8880/dev/phonemize",
json={"text": text, "language": language} # "a" for American English
)
response.raise_for_status()
result = response.json()
return result["phonemes"], result["tokens"]
def generate_audio_from_phonemes(phonemes: str, voice: str = "af_bella"):
"""Generate audio from phonemes"""
response = requests.post(
"http://localhost:8880/dev/generate_from_phonemes",
json={"phonemes": phonemes, "voice": voice},
headers={"Accept": "audio/wav"}
)
if response.status_code != 200:
print(f"Error: {response.text}")
return None
return response.content
# Example usage
text = "Hello world!"
try:
# Convert text to phonemes
phonemes, tokens = get_phonemes(text)
print(f"Phonemes: {phonemes}") # e.g. ðɪs ɪz ˈoʊnli ɐ tˈɛst
print(f"Tokens: {tokens}") # Token IDs including start/end tokens
# Generate and save audio
if audio_bytes := generate_audio_from_phonemes(phonemes):
with open("speech.wav", "wb") as f:
f.write(audio_bytes)
print(f"Generated {len(audio_bytes)} bytes of audio")
except Exception as e:
print(f"Error: {e}")
示例脚本见 examples/phoneme_examples/generate_phonemes.py。
时间戳(单词级别)
生成带单词级别时间戳的音频(非流式):
import requests
import base64
import json
response = requests.post(
"http://localhost:8880/dev/captioned_speech",
json={
"model": "kokoro",
"input": "Hello world!",
"voice": "af_bella",
"speed": 1.0,
"response_format": "mp3",
"stream": False,
},
stream=False
)
with open("output.mp3","wb") as f:
audio_json=json.loads(response.content)
chunk_audio=base64.b64decode(audio_json["audio"].encode("utf-8"))
f.write(chunk_audio)
print(audio_json["timestamps"])
生成带单词级别时间戳的音频(流式):
import requests
import base64
import json
response = requests.post(
"http://localhost:8880/dev/captioned_speech",
json={
"model": "kokoro",
"input": "Hello world!",
"voice": "af_bella",
"speed": 1.0,
"response_format": "mp3",
"stream": True,
},
stream=True
)
f=open("output.mp3","wb")
for chunk in response.iter_lines(decode_unicode=True):
if chunk:
chunk_json=json.loads(chunk)
chunk_audio=base64.b64decode(chunk_json["audio"].encode("utf-8"))
f.write(chunk_audio)
print(chunk_json["timestamps"])
启用 "allow_voice_tags": true 后,每个时间戳还会携带单词的发言 voice,因此多发言者字幕可直接标注,无需客户端重新推导分割。未启用时该字段不存在。
时间戳(流式分块)
吞吐量
通过本地API生成时,文本长度可达长篇书籍(约1.5小时输出),测量处理时间和实时因子。测试环境:
关键性能指标:
模型卸载/显存回收
POST /dev/unload 会将模型从显存中释放,并在下次请求时延迟重新加载。回收量随负载(激活池,而非仅权重)变化但会达到稳定值:文本块上限为450 tokens。长文本约等于30个段落。测试环境同上。
| 工作负载 | 已加载 | 基准值 | 回收量 | 重新加载时间 |
|---|---|---|---|---|
| 短文本(6秒音频) | 3.11 GB | 2.37 GB | 758 MiB | +4.9s |
| 长文本(7.5分钟) | 3.98 GB | 2.37 GB | 1,656 MiB | +5.1s |
基准值为宿主系统+CUDA上下文占用。可通过项目 examples/ 目录下的 uv run --extra benchmarks assorted_checks/benchmarks/benchmark_model_unload.py 命令复现。
若要在闲置超时后自动卸载模型,可将 MODEL_AUTO_UNLOAD_TIMEOUT_SECONDS 设置为正整数秒数。默认值 0 表示禁用自动卸载。
转录往返测试(WER/CER)
端到端往返测试:使用Kokoro合成音频,通过 https://github.com/SYSTRAN/faster-whisper 将结果转录回文本,与源文本比较。相关脚本和数据位于 examples/assorted_checks/test_transcription/。
长篇英文(整本书,《地心游记》,古腾堡计划,语音 af_heart,CUDA float16 上的 base.en Whisper模型,基准测试基于cu126 GPU构建):
| 测试 | 输入字符数 | 音频时长 | 合成加速比 | 转录加速比 | WER |
|---|---|---|---|---|---|
| 短文本(约第7章) | 64,996 | 66分06秒 | 36.4x 实时 | 62.4x 实时 | 0.047 |
| 整本书 | 502,766 | 507分52秒 | 45.7x 实时 | 65.1x 实时 | 0.033 |
完整回归区间详见 examples/assorted_checks/test_transcription/BASELINE.md。
多语言检查(每种语音单句,多语言Whisper small模型。拉丁语系使用WER,日语/中文/印地语使用CER):
| 语言 | 语音 | 指标 | 分数 |
|---|---|---|---|
| 英语 | af_heart | WER | 0.000 |
| 英语(英国) | bf_emma | WER | 0.111 |
| 西班牙语 | ef_dora | WER | 0.000 |
| 法语 | ff_siwis | WER | 0.000 |
| 意大利语 | if_sara | WER | 0.000 |
| 葡萄牙语 | pf_dora | WER | 0.000 |
| 印地语 | hf_alpha | CER | 0.059 |
| 日语 | jf_alpha | CER | 0.000 |
| 中文 | zf_xiaobei | CER | 0.143 |
注意:这些是单句测试,非全面的语言质量基准。它们仅确认每个语音能生成目标语言的可转录音频;更深入的语言质量评估仍在进行中。
复现方法参见 examples/assorted_checks/test_transcription/README.md。
所有设置均通过环境变量或项目根目录下 .env 文件中的行进行配置。完整参考见 配置指南。
用于调试资源耗尽或性能问题的系统状态和资源使用情况。/debug/* 路由会暴露宿主和进程内部信息,因此默认关闭;需设置 ENABLE_DEBUG_ENDPOINTS=true 启用。
/debug/threads - 获取线程信息和堆栈跟踪/debug/storage - 各挂载分区的磁盘使用情况/debug/system - 获取系统信息(CPU、内存、GPU)/dev/model - 获取模型加载状态和自动卸载计时。默认关闭;需设置 ALLOW_DEV_UNLOAD=true 启用POST /dev/unload - 从显存释放模型;下次请求时延迟重新加载。默认关闭;需设置 ALLOW_DEV_UNLOAD=true 启用POST /dev/reload - 将模型加载到显存。默认关闭;需设置 ALLOW_DEV_UNLOAD=true 启用稳定性说明:/v1/* OpenAI兼容路由为稳定API。/dev/* 和 /debug/* 为运维辅助工具,可能在次要版本间变更或需通过标志启用。
全局API https://loguru.readthedocs.io/en/stable/api/logger.html#levels 可通过 API_LOG_LEVEL 环境变量设置。默认值为 DEBUG。各运行方法的详细配置见 配置指南。
API会对输入文本进行规范化,可能错误删除或更改某些短语。可通过在请求JSON中设置 "normalization_options":{"normalize": false} 禁用规范化,完整字段列表见 文本规范化:
import requests
response = requests.post(
"http://localhost:8880/v1/audio/speech",
json={
"input": "Hello world!",
"voice": "af_heart",
"response_format": "pcm",
"normalization_options":
{
"normalize": False
}
},
stream=True
)
for chunk in response.iter_content(chunk_size=1024):
if chunk:
# 处理流式块
pass
有关容器组、宿主组和设备权限选项,详见 docs/troubleshooting.md#linux-gpu-permissions。
有关HSA覆盖、MIOpen预热、hipBLAS回退和原生Linux宿主要求,详见 docs/troubleshooting.md#amd-gpu-rocm。
WAV响应在头部的大小字段中使用流式哨兵(0xFFFFFFFF)。大多数读取器都能正常处理:soundfile、pydub/ffmpeg、浏览器、操作系统播放器。Python标准库的wave模块则无法处理,并会报告错误的时长。如需获取准确长度,请使用soundfile.info(path).duration或ffprobe。
分支策略:
release 分支: 包含最新的稳定构建版本,推荐用于生产环境。带有特定版本标签的Docker镜像从此分支构建。master 分支: 活跃开发中。包含实验性功能、进行中的更改以及尚未发布的修复。用于获取最新代码,但稳定性较低。此分支不发布镜像;:latest 标签与其他所有标签一样,均从 release 分支构建。[!NOTE] 本项目本质上是一个以开发为核心的项目。
如果遇到问题,若出现意外情况,你可能需要回退到发布标签中的某个版本,或从源代码构建和/或进行故障排除并提交PR。
自由开源是社区共同的努力,而一天的时间毕竟有限。如果你想支持本项目,欢迎提交PR、请我喝杯咖啡,或报告使用过程中发现的任何bug/功能需求等。
正在处理代码,或让AI代理处理代码?AGENTS.md 涵盖了仓库布局、命令和约定。
本API使用HuggingFace的https://huggingface.co/hexgrad/Kokoro-82M模型。
访问模型页面了解更多关于训练、架构和功能的详细信息。我与他们的工作没有任何关联,开发此包装器是为了方便使用和个人项目。
本项目采用Apache License 2.0许可证 - 详情如下:
完整的Apache 2.0许可证文本可在以下地址获取:https://www.apache.org/licenses/LICENSE-2.0
使用contrib.rocks生成。
来自真实用户的反馈,见证轩辕镜像的优质服务