news 2026/9/12 9:28:53

9Router 文本转语音(TTS)API 完整实战指南:从 /v1/audio/speech 到多提供商语音合成

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
9Router 文本转语音(TTS)API 完整实战指南:从 /v1/audio/speech 到多提供商语音合成

9Router 文本转语音(TTS)API 完整实战指南:从 /v1/audio/speech 到多提供商语音合成

【免费下载链接】9routerUnlimited FREE AI coding. Connect Claude Code, Codex, Cursor, Cline, Copilot, Antigravity to FREE Claude/GPT/Gemini via 40+ providers. Auto-fallback, RTK -40% tokens, never hit limits.项目地址: https://gitcode.com/GitHub_Trending/9r/9router

本篇技术指南以 9Router 技能文档 skills/9router-tts/SKILL.md 为核心骨架,系统讲解如何通过 OpenAI 兼容的/v1/audio/speech接口,把文字转换为语音。文中覆盖模型与音色发现、请求格式、响应格式、以及 OpenAI、ElevenLabs、Edge TTS、Google TTS、Deepgram 等十余家提供商的model参数格式差异,并结合仓库源码(speech 路由、TTS 处理器、核心合成器)深入剖析其内部实现。读完本文,你将能够在任意语言环境中(curl、Node.js、Python 等)一键调用 9Router 完成语音合成,并理解其"统一接口、多提供商、自动回退"的底层机制。

一、环境准备:两个环境变量

9Router 的 TTS 能力通过统一的 OpenAI 兼容 HTTP 接口暴露,调用前需要配置两个环境变量:

变量必填说明
NINEROUTER_URL9Router 服务的根地址(例如自建部署的http://localhost:3000
NINEROUTER_KEY视配置而定API 密钥,仅当服务端开启了鉴权(requireApiKey)时需要

从源码看,密钥校验逻辑位于 src/sse/handlers/tts.js:处理器在收到请求后会读取本地设置,若settings.requireApiKey为真,则从请求中提取 API Key 并校验,缺失或非法时分别返回401 Missing API key401 Invalid API key。若你的部署未开启鉴权,则NINEROUTER_KEY可以省略。

服务的整体初始化与技能接入方式见 skills/9router/SKILL.md,TTS 只是 9Router 众多多模态能力之一,同一套鉴权与模型路由机制同样适用于聊天、图像、语音识别等接口。

二、发现能力:列出 TTS 模型、元数据与音色

在正式合成之前,建议先通过三个只读接口"侦察"可用的模型与音色。这三个接口在仓库中均有对应实现,且全部返回 OpenAI 风格的{ object: "list", data: [...] }结构。

1)列出所有 TTS 模型

curl $NINEROUTER_URL/v1/models/tts | jq '.data[].id'

该接口实现位于 src/app/api/v1/models/[kind]/route.js,tts是支持的 kind 之一(其余为imagesttembeddingimage-to-textweb)。它按能力类型过滤模型列表,返回形如el/eleven_multilingual_v2openai/tts-1edge-tts/vi-VN-HoaiMyNeural的完整模型 ID。

2)查询单个模型的元数据

curl "$NINEROUTER_URL/v1/models/info?id=el/eleven_multilingual_v2"

实现见 src/app/api/v1/models/info/route.js。返回的元数据包括idnamekindowned_byendpoint(对应/v1/audio/speech),并附带paramscapabilitiesoptions等扩展字段。关键点:对于支持"按 ID 选音色"的提供商(elevenlabs、edge-tts、deepgram、inworld、local-device),返回结果中还包含voicesUrl字段,指向对应的音色列表接口(源码)。

3)列出具体提供商的音色

# 列出 edge-tts 的全部音色(可用 ?lang=vi 按语言过滤) curl "$NINEROUTER_URL/v1/audio/voices?provider=edge-tts&lang=vi" | jq '.data[].model'

音色接口实现位于 src/app/api/v1/audio/voices/route.js,目前支持的提供商为:elevenlabsdeepgraminworldedge-ttslocal-device(源码)。不传lang时返回该提供商全部音色(按语言分组后展平);传lang时仅返回该语言下的音色。每个音色条目包含idnamelanggender,以及可直接用于/v1/audio/speech的完整model字符串(由提供商别名前缀 + 音色 ID 拼成,见 源码)。

重要约定:/v1/audio/speech请求体里的model字段,本质上是音色 ID 或"模型+音色"组合串,而非传统的 LLM 模型名。例如edge-tts/vi-VN-HoaiMyNeuralel/<voice_id>,或直接写openai/tts-1(此时使用该模型的默认音色)。

三、合成端点:POST /v1/audio/speech

POST $NINEROUTER_URL/v1/audio/speech

该端点的实现入口是 src/app/api/v1/audio/speech/route.js,它把请求直接转交给handleTts处理。请求体字段如下:

字段必填说明
model音色 ID 或模型/音色,取自/v1/models/tts/v1/audio/voices
input要朗读的文本

请求体校验同样在 src/sse/handlers/tts.js:缺model返回400 Missing model,缺input返回400 Missing required field: input

查询参数:response_format

通过 URL 查询参数控制返回格式:

  • ?response_format=mp3(默认):直接返回原始音频字节流,Content-Typeaudio/mp3
  • ?response_format=json:返回 JSON,包含 base64 编码的音频与格式信息:
    { "audio": "SUQzBAAAA...", "format": "mp3" }

响应格式的拼接逻辑见 open-sse/handlers/ttsCore.js:json模式返回{audio: base64, format}的 JSON;二进制模式则按audio/${format}设置 Content-Type 并附带Content-Length,两种模式均带 CORS 头。

可选字段:language

请求体还可携带可选的language字段作为语言提示,目前用于 Gemini 提供商(源码)。例如{"model":"gemini/...","input":"你好","language":"zh-CN"}

四、实战示例:curl 与 Node.js

curl 保存 MP3

curl -X POST "$NINEROUTER_URL/v1/audio/speech" \ -H "Authorization: Bearer $NINEROUTER_KEY" \ -H "Content-Type: application/json" \ -d '{"model":"openai/tts-1","input":"Hello world"}' \ --output speech.mp3

Node.js 保存文件

import { writeFile } from "node:fs/promises"; const r = await fetch(`${process.env.NINEROUTER_URL}/v1/audio/speech`, { method: "POST", headers: { "Authorization": `Bearer ${process.env.NINEROUTER_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ model: "el/eleven_multilingual_v2", input: "Xin chào" }), }); await writeFile("speech.mp3", Buffer.from(await r.arrayBuffer()));

Python 示例(requests)

import requests resp = requests.post( f"{NINEROUTER_URL}/v1/audio/speech", headers={"Authorization": f"Bearer {NINEROUTER_KEY}"}, json={"model": "edge-tts/vi-VN-HoaiMyNeural", "input": "Xin chào"}, ) open("speech.mp3", "wb").write(resp.content)

若需要 JSON 格式,只需追加查询参数:POST "$NINEROUTER_URL/v1/audio/speech?response_format=json",然后对返回的audio字段做 base64 解码即可。

五、提供商差异:model 格式速查表

不同提供商对model字段的格式约定差异较大,这是使用 TTS 接口时最容易踩坑的地方。以下完整继承自技能文档并补充说明:

Providermodel格式说明
openaitts-1/alloy(模型/音色)或仅音色默认模型gpt-4o-mini-tts
elevenlabs<model_id>/<voice_id><voice_id>默认模型eleven_flash_v2_5;音色可在 Dashboard 查看
openrouteropenai/gpt-4o-mini-tts/alloy通过 chat-completions 的 audio 模态流式输出
edge-tts音色 ID,如vi-VN-HoaiMyNeural无需鉴权;默认vi-VN-HoaiMyNeural
google-tts语言代码,如envi无需鉴权
local-device操作系统语音名(say -v ?/ SAPI)无需鉴权;需要本机安装ffmpeg
deepgramaura-asteria-enToken 鉴权
nvidiainworldcartesiaplayhtmodel/voice提供商专属鉴权头
coquitortoisespeaker / voice ID本地 localhost,无需鉴权
hyperbolic模型 ID请求体只有{text}

源码层面的解析规则

无论采用哪种格式,最终都要经过统一的model/voice拆解。核心实现在 open-sse/handlers/ttsProviders/_base.js 的parseModelVoice函数:

  1. 优先与已知模型 ID 列表做最长前缀匹配:若model恰好等于某模型 ID,则使用该模型 + 默认音色;若以模型ID/开头,则后半段作为音色;
  2. 匹配不到时,取最后一个/分割模型/音色
  3. 兜底:模型ID/音色整体作为默认模型,音色用默认值。

以 OpenAI 适配器为例(open-sse/handlers/ttsProviders/openai.js):openai/tts-1/alloy会被拆成模型tts-1与音色alloy,再转发到上游POST /v1/audio/speech;若只传alloy,则使用默认模型gpt-4o-mini-tts

六、架构与内部原理:一次 TTS 请求的完整旅程

理解内部调用链有助于排查问题和发挥接口的全部能力。一次合成请求会依次经过以下层级:

POST /v1/audio/speech └─ src/app/api/v1/audio/speech/route.js ← 路由入口 └─ src/sse/handlers/tts.js handleTts ← 校验 + 路由分发 ├─ 鉴权校验(requireApiKey) ├─ 参数校验(model / input) ├─ Combo 展开(可选)→ open-sse/services/combo.js └─ handleSingleModelTts ├─ 无凭据提供商 → 直接合成 └─ 有凭据提供商 → 凭据轮询 + 失败回退 └─ open-sse/handlers/ttsCore.js handleTtsCore ├─ 专用适配器(SPECIAL_ADAPTERS) └─ 通用配置驱动(synthesizeViaConfig) └─ ttsProviders/{provider}.js

1)Combo 多模型聚合

handleTts在正式合成前会检查model是否为已配置的 Combo 名称(源码)。若命中 Combo,会按其策略(fallback回退 / 轮询等)在多个 TTS 模型间自动切换,实现"一个 Combo 名 = 一组可用音色,自动兜底"。Combo 的建模与路由逻辑在 open-sse/services/combo.js,与聊天接口共用同一套机制。

2)凭据管理与失败回退

对于需要凭据的提供商(elevenlabs、openai、deepgram、inworld 等),处理器维护了一个CREDENTIALED_PROVIDERS集合(源码,凡声明了serviceKindstts、且noAuth不为真、且ttsConfig.authType !== "none"的提供商都算在内)。对这类提供商,请求进入凭据轮询 + 失败回退循环(源码):每次取一组可用凭据合成,若上游报错,则通过markAccountUnavailable标记该账号暂不可用并尝试下一个账号,直到成功或全部账号耗尽。这意味着即使某个账号额度耗尽,也能自动切到备用账号继续合成。

3)适配器与通用调度

核心合成器 open-sse/handlers/ttsCore.js 采用"专用适配器 + 通用配置驱动"双轨制:

  • 专用适配器google-ttsedge-ttslocal-deviceelevenlabsopenaiopenroutergemini七个提供商有自定义的synthesize()逻辑,注册表见 open-sse/handlers/ttsProviders/index.js;
  • 通用配置驱动:其余提供商(hyperbolic、deepgram、nvidia、huggingface、inworld、cartesia、playht、coqui、tortoise、qwen 等)通过ttsConfig.format映射到 genericFormats.js 中的格式处理器,按统一的{baseUrl, apiKey, text, modelId, voiceId}参数调用(源码)。

上游返回的音频统一转成{base64, format}内部结构(见 _base.js,会根据 Content-Type 识别wav/mp3/ogg),最终由createTtsResponseresponse_format打包返回给调用方。

七、实践建议与常见问题

  1. 优先走"发现"三步曲:写死音色 ID 容易在提供商调整音色后失效,建议在应用启动时调用/v1/models/tts/v1/audio/voices动态获取可用的model值。
  2. 无需鉴权的提供商edge-ttsgoogle-ttslocal-devicecoquitortoise免费可用,适合快速验证链路或做低成本批量合成;local-device依赖本机语音库与ffmpeg,运行环境需提前准备。
  3. 超长文本input为单次请求的文本,超长文本建议先分段再逐段合成,最后按序拼接音频。
  4. 调试技巧:返回400 Invalid model format时,检查model是否带上了合法前缀(如el/openai/edge-tts/);返回503且提示All accounts unavailable时,通常是该提供商所有已存凭据均被标记为限流,可稍后重试或在仪表盘中补充凭据。
  5. Combo 兜底:把多个提供商/音色组织成 Combo,可显著提升合成的可用性——单一上游抖动时自动回退,不影响最终用户体验。

八、延伸阅读

  • 技能定义文件:skills/9router-tts/SKILL.md(本文骨架来源)
  • 9Router 主技能(环境初始化):skills/9router/SKILL.md
  • 路由与处理器:src/app/api/v1/audio/speech/route.js、src/app/api/v1/audio/voices/route.js、src/sse/handlers/tts.js
  • 核心合成与提供商适配:open-sse/handlers/ttsCore.js、open-sse/handlers/ttsProviders/index.js、open-sse/handlers/ttsProviders/openai.js、open-sse/handlers/ttsProviders/_base.js
  • 模型发现接口:src/app/api/v1/models/[kind]/route.js、src/app/api/v1/models/info/route.js
  • 单元测试示例:tests/unit/gemini-tts.test.js、tests/unit/minimax-tts.test.js(含response_format与音色列表的断言,可作为接口行为的可执行文档)

【免费下载链接】9routerUnlimited FREE AI coding. Connect Claude Code, Codex, Cursor, Cline, Copilot, Antigravity to FREE Claude/GPT/Gemini via 40+ providers. Auto-fallback, RTK -40% tokens, never hit limits.项目地址: https://gitcode.com/GitHub_Trending/9r/9router

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/12 9:26:31

LunaTranslator 新手指南:5分钟跑通日文游戏实时翻译

LunaTranslator 新手指南&#xff1a;5分钟跑通日文游戏实时翻译 【免费下载链接】LunaTranslator 视觉小说翻译器 / Visual Novel Translator 项目地址: https://gitcode.com/GitHub_Trending/lu/LunaTranslator LunaTranslator 是一款 Windows 上的视觉小说实时翻译工…

作者头像 李华
网站建设 2026/9/12 9:26:23

RAG端到端信息流设计:政务场景下的切块、Embedding与多路召回实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/12 9:26:20

LLC电源调试:欠谐振与过谐振的波形判断与ZVS实现

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/12 9:25:44

Crayfish容器版:桌面智能体的可编程服务总线实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华