TTS API 演示:实时请求与响应示例
此演示展示了你可以在一分钟内运行的真实请求以及返回的响应格式。API 是文本模型,因此输出为脚本文本、JSON 和工具调用,随后可由单独的语音引擎朗读。
更新于

运行之前
给任何期望点击播放就能听到声音的人一个公平警告:这个 tts api 演示不产生声音。该服务通过 POST /v1/chat/completions 从模型 uncensored 返回文本。还有一个接口 GET /v1/models 用于列出可用内容。没有任何内容返回音频、图像或嵌入向量。
那为什么要有演示?因为许多语音项目正好需要这一部分:脚本编写器、文本清理器、决定谁说什么的分割器。下面的示例展示了这些任务,输出格式正是你的语音代码需要解析的格式。
你需要一个密钥。在 API 密钥页面 使用 Google 或电子邮件和密码注册,然后点击一次领取 $0.50 试用额度。有效期为 7 天,无需银行卡。导出密钥以便下面的命令可以读取它:
export XTTS_API_KEY="paste-your-key-here"
curl -s https://api.xtts.cc/v1/models -H "Authorization: Bearer $XTTS_API_KEY" | grep uncensored
如果该调用返回包含 uncensored 的 JSON 列表,则密钥有效,你可以继续。
演示 1:普通请求
最小的有用调用要求一段简短的口语风格段落:
curl -s https://api.xtts.cc/v1/chat/completions \
-H "Authorization: Bearer $XTTS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "uncensored",
"messages": [
{"role": "system", "content": "You write short lines for a voice actor. No stage directions."},
{"role": "user", "content": "Welcome listeners to a show about lighthouses. Two sentences."}
],
"temperature": 0.7,
"max_tokens": 120
}'
回复看起来像下面的示例。content 中的文本每次运行都会不同,因此将其视为结构说明,而非录制的输出。
{
"id": "chatcmpl-example",
"object": "chat.completion",
"model": "uncensored",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "Welcome to the show. Tonight we climb the stairs of the world's loneliest lighthouses."
},
"finish_reason": "stop"
}
],
"usage": {"prompt_tokens": 38, "completion_tokens": 21, "total_tokens": 59}
}
你的语音代码读取 choices[0].message.content。finish_reason 告诉你生成结束的原因:stop 表示自然结束,length 表示因 max_tokens 截断而提前结束。如果省略 max_tokens,补全可能会使用 100,000 token 上下文窗口中剩余的部分,这涵盖了提示词和补全内容。
标头告诉你的信息
向 curl 命令添加 -i,你将看到 X-Request-Id、X-Balance-USD、X-RateLimit-Limit-Requests 和 X-RateLimit-Limit-Concurrency。余额标头在演示中很有用,因为你可以看到每次调用后它下降了一点点。对于此请求,上述用法计算为 38 x $0.15 / 1,000,000 加上 21 x $0.50 / 1,000,000,即 $0.0000057 加上 $0.0000105,约 $0.0000162。
演示 2:流式输出脚本
对于音频,你通常不希望等待完整文本。添加 "stream": true,API 会发送服务器发送事件,一次发送一小段文本。下面的 Node 示例在文本到达时打印文本,这正是你填充句子队列的地方。
import OpenAI from "openai";
const client = new OpenAI({
baseURL: "https://api.xtts.cc/v1",
apiKey: process.env.XTTS_API_KEY,
});
const stream = await client.chat.completions.create({
model: "uncensored",
stream: true,
messages: [
{ role: "user", content: "Read a four-line weather report for a coastal town." },
],
});
for await (const chunk of stream) {
const piece = chunk.choices[0]?.delta?.content ?? "";
process.stdout.write(piece);
}
process.stdout.write("\n");
将其作为 ES 模块运行(保存为 demo.mjs 并在 npm install openai 后运行 node demo.mjs)。在传输线路上,每个事件都是以 data: 开头的一行,后跟 JSON,流以最终标记结束。Token 使用情况在流的末尾报告。规划时有一点很重要:流式请求会保留其并发槽位,直到流结束,因此慢速消费者可能会阻塞你自己的下一个请求。
演示 3:作为语音路由器的工具调用
工具调用让模型返回结构化指令而非散文。在语音项目中,这是一种为每行选择交付风格的干净方式。你定义一个名为 say 的函数,模型填充其参数。你的代码然后决定如何处理它们。模型本身不运行任何内容。
import json
import os
from openai import OpenAI
client = OpenAI(base_url="https://api.xtts.cc/v1", api_key=os.environ["XTTS_API_KEY"])
tools = [{
"type": "function",
"function": {
"name": "say",
"description": "Queue one line for the voice engine.",
"parameters": {
"type": "object",
"properties": {
"text": {"type": "string"},
"style": {"type": "string", "enum": ["calm", "urgent", "whisper"]},
},
"required": ["text", "style"],
},
},
}]
messages = [
{"role": "system", "content": "Always answer by calling say, once per line."},
{"role": "user", "content": "The alarm sounds. The guard tells everyone to leave."},
]
r = client.chat.completions.create(
model="uncensored", messages=messages, tools=tools, tool_choice="auto"
)
msg = r.choices[0].message
for call in msg.tool_calls or []:
args = json.loads(call.function.arguments)
print(args["style"], "->", args["text"])
参数作为 JSON 字符串到达,因此在使用前解析并验证它们。模型可能会生成超出你枚举范围的风格或省略字段,未检查的值不应到达你的引擎。如果你想让对话继续,附加助手消息和带有 role: "tool" 和匹配的 tool_call_id 的回复,然后再次调用 API。
流式传输时,工具调用分片到达。函数名首先出现,参数字符串在分块中构建,因此按索引收集片段,并在流结束后解析。
演示 4:用于演员表的 JSON 模式
如果你希望跳过工具定义,JSON 模式在消息内容中提供有效的 JSON。这非常适合将粗略的场景转换为按说话人划分的行。
curl -s https://api.xtts.cc/v1/chat/completions \
-H "Authorization: Bearer $XTTS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "uncensored",
"response_format": {"type": "json_object"},
"temperature": 0.2,
"messages": [
{"role": "system", "content": "Return JSON: {\"lines\": [{\"speaker\": string, \"text\": string}]}"},
{"role": "user", "content": "Ana asks Ben to hold the ladder. Ben agrees reluctantly."}
]
}'
JSON 模式保证输出可解析,但不保证匹配你的结构。在提示词中描述结构(如上所述),并在代码中检查键。低 temperature 可保持结构稳定。
演示 5:单次请求处理长文档
上下文窗口为 100,000 token,由提示词和补全共享。这足以发送长转录或整个短篇故事,并要求生成适合耳朵的版本。请求体最多可达 8 MB,因此大小通常不是限制;token 计数才是。
这里是一次此类调用的成本。假设你发送 80,000 token 并得到 2,000 返回。输入:80,000 x $0.15 / 1,000,000 = $0.012。输出:2,000 x $0.50 / 1,000,000 = $0.001。总计为 $0.013,因此 $0.50 试用额度可以覆盖大约 38 次这样的调用。长输入比长输出便宜得多,这就是为什么大文本的摘要和重写很便宜,而一次性生成整本小说则不可行。
长输入的一个实用技巧:将你的指令放在开头,并在末尾重复关键规则。模型往往最能遵循最近的指令,在 80,000 token 的源文本后添加一行提醒几乎不花钱。
阅读演示中的错误
故意破坏某项功能一次,以便日后识别。使用错误的密钥发送请求,你会得到认证错误而不是补全结果。从试用密钥同时发送十个请求,部分请求会返回 HTTP 429。
429 错误有三个原因,错误代码将它们区分开来:
rate_limited表示一分钟内请求过多:试用密钥为 60,完整密钥为 300。concurrency表示同时进行的请求过多:试用密钥为 2,完整密钥为 8。- 带有
Retry-After: 5标头的 429 表示服务短暂过载。等待五秒钟,然后再次发送。
失败或被拒绝的请求不会收费,因此错误不产生费用。要查看你的密钥的限制,请在任何正常响应中读取 X-RateLimit-Limit-Requests 和 X-RateLimit-Limit-Concurrency。一个简单的重试循环,等待 Retry-After 指定的时间,如果缺失则等待五秒,就足够演示脚本使用了。
接下来尝试什么
一旦这四个调用工作,你就拥有了脚本管道的组件。将普通请求包装在函数中,将流式循环运行到队列中,并将队列连接到你所选择的语音引擎。引擎在价格、许可和质量上有所不同,因此请阅读它们的当前条款。该服务不提供语音引擎,也不对它们发表任何声明。
在实验期间,成本保持很小。每百万输入 token $0.15,每百万输出 token $0.50,典型的演示调用成本不到一分钱,而 $0.50 试用额度可以覆盖数百次。查看 定价页面,充值从 $20 到 $500,附带 5% 到 15% 的奖金,可通过加密货币支付。文档 涵盖完整的参数列表、速率限制(完整密钥为 8 并发和每分钟 300 次)和错误代码。提示词不用于训练。
第一个下午的合理顺序:运行普通请求直到语气正确,切换到流式输出并在句子完成时打印句子,然后添加工具调用或 JSON 模式,取决于你的语音代码希望如何接收输入。将每个提示词保存在版本控制下的文件中。当最终音频中的一行听起来不对时,你可以将其追溯回产生它的精确指令,并一次更改一件事。
更多指南
问答
我可以在这个演示中听到示例语音吗?
不。API 仅返回文本、JSON 和工具调用。你提供单独的语音引擎将文本转换为音频。
我应该发送哪个模型 ID?
将 uncensored 作为模型值发送,基础 URL 为 https://api.xtts.cc/v1,并将你的密钥放在 Authorization: Bearer 头中。
演示在试用密钥上有效吗?
是的。试用密钥允许 2 个并发请求和每分钟 60 个请求,这足以满足这些示例的需求。请逐个运行它们。
为什么我会收到 429 响应?
你超过了每分钟请求数(rate_limited),同时打开了太多请求(concurrency),或者服务短暂过载。如果提供了 Retry-After,请等待该时间后重试。
只需填写一份表单即可获得密钥
创建账户,复制密钥,更改基础 URL。这就是全部设置。