# 语音合成
> 用预置或克隆音色，把文本合成为语音。

用预置或克隆音色，把文本合成为语音。

## Agent 主路径

通过 Universal Skill 使用 MCP 工具 `beatra.speech.synthesize`。该工具计费，
返回异步任务。

1. 先选音色：`beatra.voices.list` 返回预置与克隆音色的 `voice_id` 和试听链接。
2. 当模型、语言、格式或价格有影响时，用 `capability: "text_to_speech"` 调用
   `beatra.models.list`；否则使用 `model: "auto"` 或省略 `model`。
3. 创建一个不透明的 `client_request_id`，把最终参数提交一次。
4. 用 `beatra.tasks.get` 轮询返回的任务直到终态。

想让声音更像真人在讲，可以先用 `beatra.speech.prepare_script` 让[智能语音导演](https://docs.beatra.cn/zh/docs/capabilities/speech/ai-director)
读懂文字、安排语气、停顿和情绪，再把它返回的稿子交给本工具合成。

## AI 指南

AI integration guide: https://docs.beatra.cn/llms/audio-text-to-speech.md

## 工具参数

```json
{
  "voice": "voice_01JX...",
  "input": "Welcome to Beatra — make your first call in three minutes.",
  "language": "en-US",
  "format": "mp3",
  "speed": 1.0,
  "client_request_id": "tts-opaque-1"
}
```

`voice` 接受[音色管理](https://docs.beatra.cn/zh/docs/capabilities/speech/voices)返回的任何
`voice_id`，预置或[克隆音色](https://docs.beatra.cn/zh/docs/capabilities/speech/voice-clone)都可以。每个音色属于一个引擎（见它的 `compatible_models`）；
有的模型上，音色跟随哪些控制项因音色而异，音色的 `persona` 会写明。
本能力不含流式与实时对话。

## 规范标记

`input` 只能使用下面这一组标记。标记计入所选模型 `constraints.input_max_characters` 的字符上限，也计入加权字符计费。
词表与各模型生效值域在 `beatra.models.list` 模型卡的 `constraints.script_markup`。
同一套标记适用于每个语音模型；模型做不到的标记会被去掉或忽略，并返回警告，不会做近似替代。
写错的标记也不会让请求失败：能纠正的按规范写法处理，不能纠正的去掉或按原词朗读，并返回 `markup_adjusted`，写明原因和正确写法。只有标记、没有要朗读的文字时，请求在扣费前以 422 拒绝（所有模型）。
下文的「模型特化参数」是 `model_options` 字段，不属于标记语言。

- `{pause <seconds>}` — 停顿，最多两位小数，取值在模型卡的 `script_markup.pause.range` 之内（MiniMax 最长 99.99 秒，豆包最长 10 秒，超出按上限处理并提示）；秒数也可以带单位，如 `{pause 500ms}`、`{pause 0.5s}`、`{pause 2秒}`。ElevenLabs 不执行停顿，会移除停顿并返回 `markup_degraded`（见“ElevenLabs v4 与 v4 Turbo”）。写错的停顿（如 `{pause}`、`{pause abc}`）和整段开头或结尾的停顿会被去掉，相邻的停顿（中间没有可朗读的文字）只保留第一个，都返回 `markup_adjusted`。
- 声音标签：`{laugh}` 笑声、`{chuckle}` 轻笑、`{sigh}` 叹气、`{breath}` 换气、`{cough}` 咳嗽、`{clear-throat}` 清嗓子、`{gasp}` 倒吸一口气、`{groan}` 痛苦或不满的哼声、`{inhale}` 吸气、`{exhale}` 呼气、`{pant}` 喘气、`{sniff}` 吸鼻子、`{hmm}` 嗯（迟疑、思考）。`script_markup.tags` 列出模型能发出的标签；不支持的模型会把标签去掉（不会读出来），并返回 `markup_degraded` 警告。
- `{pron <word> <reading>}` — 为标记的这一处固定读法：同一个字在别处仍按默认读法，同一请求里也可以给同一个字标不同读音。读法记法为拼音：音节直接相连、每音节以声调数字 1–5 结尾、v 代表 ü，中文词每个汉字对应一个音节；其他记法不支持；音节之间有空格时会合并。写错时（缺读音、拼音不合法、音节数和字数对不上）按原词朗读，并返回 `markup_adjusted`；读音分成多段且其中有不是拼音的内容时（多半是漏了右括号），整段按原文保留，并返回 `unknown_markup`。例：`孩子{pron 长 zhang3}大了，头发也很{pron 长 chang2}。`
- `{sub <word> <spoken text>}` — 把标记处的词按给定文字朗读（最多 100 个字符），字幕仍显示原词；适合品牌名、缩写、英文词和需要逐位读的数字。缺读法时按原词朗读，并返回 `markup_adjusted`；读法超过 100 个字符时整段按原文保留，并返回 `unknown_markup`。例：`{sub W3C 万维网联盟}`。
- `{{` — 字面左花括号：写成 `{{` 的位置会朗读一个 `{`，这对花括号本身不会被解析为标记；其后的文本仍按正常规则解析（只需转义左花括号，右花括号原样书写即可）。
- `{emotion <情绪>}` — 行内情绪：从标记处起换成这种情绪，持续到下一个 `{emotion …}`；请求的 `emotion` 参数是开头的情绪，取值和它相同。只有模型卡带 `script_markup.inline_emotion` 的模型逐句执行；其他模型去掉标记、整段按 `emotion` 参数合成，并返回 `markup_degraded`，有支持的模型时写明是哪些。取值无效时标记被去掉、不会朗读，并返回 `markup_adjusted`。例：`我们出发吧。{emotion happy}看，前面就是海！{emotion sad}可惜明天就要回去了。`
- 表演标记 — 作用于其后的语句，不跨段延续。可用的标记以模型卡的 `script_markup.performance` 为准，只有列出它们的模型执行；其他模型去掉标记，并返回 `markup_degraded`，有支持的模型时写明是哪些。
- 近似写法按规范写法处理（如 `{laughs}`、`{Laugh}` 按 `{laugh}`），并返回 `markup_adjusted`。全角花括号里的已知写法（如 `｛pause 1｝`）按规范写法处理，并返回 `markup_adjusted`；其他全角花括号属于正文。不认识的花括号内容（如 `{name}`），以及像标记但多了别的内容的写法（如 `{pause: 1000}`、`{pause and think}`），按原文保留，并返回 `unknown_markup`。方括号、圆括号里的文字（如 `[笑]`、`（小声）`）属于正文，原样交给模型；看起来像表演标签时返回 `unrecognized_markup`；情绪词和表演词只建议当前模型能执行的写法。

每个模型卡的 `constraints.limitations` 用完整句子写明该模型的限制，调用哪个模型就读哪张卡。例如：

- 音量、语速、音高在一次请求内整段生效，不能只作用于某一句或某个词；情绪也是，除非模型卡带 `script_markup.inline_emotion`。如需局部变化，请拆分成多次调用（各自设置参数，会生成多个音频文件），或通过改写文本（省略号、短句、措辞）间接实现。
- 有的模型在长文本（超过 3000 字时走异步路径）上不支持高质量数字归一化，或会去掉 `{pause}`、`{pron}` 标记（返回警告）。需要这些效果时，按段落把文本拆成每次不超过 3000 字的请求。

## 表演指令与上文

下面两个可选字段描述这段语音该怎么读，执行它们的模型会在模型卡的 `controls` 里列出（相关数值见 `constraints.direction_max_characters` 和 `constraints.previous_text_tail_characters`）：

- `direction` — 整段的自然语言表演指令，例如 `像在耳边说悄悄话一样，轻声地说`、`read it like breaking news`。写成一两句话即可：超过 `direction_max_characters` 的指令会被忽略并返回警告。实际效果取决于模型、音色和文本；调音量请用 `volume`。
- `previous_text` — 紧挨在这段之前的文字（对话的上一句、上一段），让分开合成的片段语气连贯。长度不限，模型只用最后 `previous_text_tail_characters` 个字。它不会被读出来。

这两个字段都不计费，也不会让请求失败。不执行某个字段的模型、音色或路径会忽略它并返回 `option_ignored` 警告，音频照常生成。有的模型上这两项还取决于音色（例如角色类音色两项都不跟随），模型卡的 `constraints.limitations` 会写明。`emotion` 同理：模型或音色做不到所请求的情绪时，忽略并返回警告。想在计费前知道某个音色是否跟随这些控制项，把同样的请求发给下面的 validate 接口即可：它免费返回同样的警告。

计费提交前可调用 `beatra.speech.validate` 或 `POST /v1/audio/speech/validate`。
它执行同一套解析和警告检查，返回加权字符数与所需积分；不创建任务、不扣积分、不调用语音引擎，
也不需要 `Idempotency-Key`。它不检查 ElevenLabs 两个任务的并发上限，也不检查当前容量。

warnings 非空时出现在 202（含幂等重放）、`GET /v1/tasks/{task_id}`、
`beatra.tasks.get` 和客户回调的 `task.warnings`。任务列表、`expand`、账户详情
和 admin 不保证返回。字幕位置指向统一显示文本，见下文“字幕”。计费字数见下文“模型、控制项与成本”一节；
`output.characters` 是引擎实际朗读的字符数，两者可以不同；在 ElevenLabs 上，`output.characters` 就是计费的加权字数。

## 模型、控制项与成本

通用控制项：`input`（必填，上限见所选模型卡）、`voice`（必填）、
`language`、`format`（默认 `mp3`；模型卡的 `constraints.formats` 列出该模型支持的其他格式）、
`speed`（0.5–2.0）、`volume`（在模型卡的 `constraints.volume` 范围内，例如 >0–10 或 0.5–2；超出范围会在扣费前被拒绝）、`pitch`（−1–1，归一化：0 = 音色原始
音高，−1 为该模型允许的最低，1 为最高；微调请用小数如 0.25）、`emotion`、
`sample_rate`、`subtitle_enable`（默认 `true`；免费提供可用的逐句字幕及 JSON/SRT/VTT 文件，字幕失败不影响音频成功）。`language` 在有的模型上只部分生效，
`constraints.limitations` 写明所选模型怎样处理它。每个控制项只按
所选模型的元数据使用。并非每种格式都支持全部采样率（例如 `opus` 在有的模型上仅支持
8/16/24 kHz，在有的模型上固定输出 48 kHz、不接受 `sample_rate`）：请读模型卡上的 `constraints.sample_rates_by_format`，不要
假设平铺采样率列表对每种格式都成立。

`beatra.models.list` 是可选模型、支持语言、格式、采样率与价格的当前
事实来源。不要把模型目录或价格表复制进 agent 提示词。

语音按请求估价预扣，按加权字符**线性**计费（报价以每 1,000 加权字符为
单位）：每个汉字计 2，其他码位计 1。按提交原文（含标记）计字。引擎按收到的控制文本长度计费的模型上，发给引擎的文本更长时按它计：停顿、读法会变成控制标签，带标签（或正文以 `<speak` 开头）的请求整段按这种形式发送（`&`、`<`、`>` 会转义）。validate 返回准确字数。

## 模型特化参数

`model_options` 承载只有某一类模型执行的参数，按模型类别命名。模型卡的 `controls` 列出该模型执行的键（例如 `model_options.doubao.explicit_dialect`）；其他类别的键不生效，模型可能返回 `option_ignored` 警告说明。

### MiniMax：读音词典

生僻字与多音字可以直接指定读音，不必用同音字改写：

```json
{
  "model_options": {
    "minimax": {
      "pronunciation_dict": {
        "tone": ["湛/(zhan4)", "晞/(xi1)", "厌/(yan1)"]
      }
    }
  }
}
```

格式为 `字/(拼音+声调数字)`，声调数字为 1–4（四声）与 5（轻声）。词典条目对请求里
这个词的每一处都生效；只想改某一处时，用上文的 `{pron}` 标记。仅 MiniMax 系模型支持；
其他模型会忽略该字段。

### 豆包

- `model_options.doubao.explicit_dialect` — `beijing`、`dongbei`、`henan`、`shaanxi`、`shanghai`、`sichuan`、`tianjin`（粤语用 `language: "yue-HK"`）。只有 `languages_supported` 含 `yue` 的音色能说方言，其他音色会在扣费前被拒绝。
- `model_options.doubao.silence_duration` — 在最后一个字后追加的静音，0–30,000 毫秒。
- `model_options.doubao.explicit_language` — 强制朗读语种（`zh-cn`、`en`、`ja`、`es-mx`、`ko`、`de`、`fr` 等）。它是过滤，不是提示：其他语种的句子会被跳过，音色读不了的文本不会出声（任务失败并退款）。`nl`、`no`、`uk`、`fi`、`da`、`cs`、`hu`、`el`、`ro`、`hi`、`es-es` 只对中文（`zh_…`）预置音色有效。长文本只接受 `zh-cn`、`en`、`es-mx`、`id`、`pt-br`。
- `model_options.doubao.latex_parser` — 设为 `"v2"` 会朗读 LaTeX 公式（教育类脚本）。
- `model_options.doubao.pronunciation_dict` — `{"tone": ["北京/(bei3)(jing1)", "omg/oh my god"]}`：最多 5,000 条，对请求里这个词的每一处都生效（读法或替换文本）。只想改某一处时，用 `{pron}`。

文本里用了 `{pause}` 或 `{pron}` 时以标记为准：`latex_parser` 和 `pronunciation_dict` 会被忽略并返回警告，因为引擎不能把它们和标记编译成的形式同时使用。

## 语言

`language` 是可选的供应商中立 BCP-47 标签，如 `zh-CN`、`yue-HK`、`en-US`。
Beatra 用主语言子标签与所选模型匹配；省略该字段则由音色或供应商自行选择。
仅当你明确要求自动语种检测时才传固定值 `auto`（模型卡上的
`constraints.language_auto` 标明该模型是否支持）。不支持的语言会被拒绝，
而不是被悄悄替换。

各模型对 `language` 的用法不同。有的模型把它当作给音色的识别提示；有的模型默认读中文和英文，
`language` 用来为其他语种开启语种识别（例如中文音色读日语时，不传可能读不出来）以及选择方言（`yue`），
这类模型上每个音色只读它 `languages_supported` 里的语种，传入音色不会说的语种会返回 `option_ignored`
警告；结果只会失败的情况在扣费前就拒绝：给不会粤语的音色传粤语，或让音色读它读不了的语种的长文本。
以模型卡的 `constraints.limitations` 为准。

当前语音模型最多覆盖下列 40 种语言，每个模型覆盖其中一部分；`beatra.models.list` 的
`constraints.supported_languages` 是实时的、按模型的事实来源。

| 代码 | 语言 | 代码 | 语言 |
| --- | --- | --- | --- |
| `zh` | 中文 | `tr` | 土耳其语 |
| `yue` | 粤语 | `nl` | 荷兰语 |
| `en` | 英语 | `uk` | 乌克兰语 |
| `es` | 西班牙语 | `th` | 泰语 |
| `fr` | 法语 | `pl` | 波兰语 |
| `ru` | 俄语 | `ro` | 罗马尼亚语 |
| `de` | 德语 | `el` | 希腊语 |
| `pt` | 葡萄牙语 | `cs` | 捷克语 |
| `ar` | 阿拉伯语 | `fi` | 芬兰语 |
| `it` | 意大利语 | `hi` | 印地语 |
| `ja` | 日语 | `bg` | 保加利亚语 |
| `ko` | 韩语 | `da` | 丹麦语 |
| `id` | 印尼语 | `he` | 希伯来语 |
| `vi` | 越南语 | `ms` | 马来语 |
| `fa` | 波斯语 | `sk` | 斯洛伐克语 |
| `sv` | 瑞典语 | `hr` | 克罗地亚语 |
| `fil` | 菲律宾语 | `hu` | 匈牙利语 |
| `no` | 挪威语 | `sl` | 斯洛文尼亚语 |
| `ca` | 加泰罗尼亚语 | `nn` | 新挪威语 |
| `ta` | 泰米尔语 | `af` | 南非荷兰语 |

## ElevenLabs v4 与 v4 Turbo

`elevenlabs-v4` 和 `elevenlabs-v4-turbo` 向所有账号开放，出现在默认模型目录中，并配有 80 个预置音色（见[音色管理](https://docs.beatra.cn/zh/docs/capabilities/speech/voices)）。`elevenlabs-v4` 是两者中表现力最强的；`elevenlabs-v4-turbo` 使用同一批音色和语种，速度更快，价格约为一半。v4 会持续训练，同一音色的效果可能随时间变化，相同的 `seed` 也不保证在模型更新后得到相同结果。

| 模型 | 每 1,000 加权字的积分 |
| --- | ---: |
| `elevenlabs-v4-turbo` | 240 |
| `elevenlabs-v4` | 450 |

ElevenLabs 音色使用 `model: "auto"` 时，在兼容模型中优先选择 Turbo。受理前固定模型，失败不会更换模型或音色。

### 长度与计数

每次请求最多 **10,000 个码位**，按 Unicode 码位计数，提交原文和编译后文本（标记生效后）各自都不能超过。其他引擎为 50,000。超限在扣费前返回 `422 invalid_request`：缩短正文、减少标记或去掉 `emotion` 后重新预检；服务不会自动拆成多个音频。

看起来是一个符号的，可能由多个码位组成；上限里每个码位算一个：

- 一个汉字、一个字母、由单个码位构成的 emoji 算 1。
- emoji 序列按每个码位计：👍🏽 = 2，❤️ = 2，🇨🇳 = 2，1️⃣ = 3，👨‍👩‍👧‍👦 = 7。
- 组合符号各算一个码位：天城文元音符号、阿拉伯语变音符号、分解形式的重音符号都会各加 1。

计费用同样的码位规则再加权重：每个汉字计 2，其他码位计 1。按原文与编译后文本中加权后较大的那个收费。在 ElevenLabs 上，`output.characters` 就是这个计费的加权字数。

### 格式、语种与控制项

- 语种：`en`、`zh`、`ja`、`ko`、`es`、`pt`、`ar`、`hi`、`fr`、`it`。省略 `language` 或使用 `auto` 由模型识别；显式传其他语种在扣费前拒绝。
- 格式：MP3 为 22,050 / 24,000 / 44,100 Hz（默认 44,100）；WAV、裸 PCM 为 8,000 / 16,000 / 22,050 / 24,000 / 32,000 Hz（默认 24,000）；Opus 固定 48,000 Hz，必须省略 `sample_rate`。不支持 FLAC。PCM 结果的 `mime_type` 为 `audio/L16`，并带真实的 `duration_seconds`。预计音频超过单个结果大小上限的超长 WAV/PCM 请求，会在扣费前以 422 `unsupported_option`（字段 `format`）拒绝；只适用于 32,000 Hz 下的中文、日文或韩文长文本（约 7,500 字符及以上）。请改用 MP3 或 Opus、降低采样率，或缩短文本。
- `direction` 及非默认的 `speed`、`volume`、`pitch` 会被忽略，并返回 `option_ignored` 警告。
- `emotion` 支持除 `fluent` 外的 7 种情绪：Beatra 在每段（以空行分段）开头加一个情绪标签（如 `[sad]`）。情绪从标签处开始，段落很长时可能逐渐减弱，需要时多用空行分段。标签让发送的文本变长，可能提高计费字数（计费按原文与发送文本取大），也计入 10,000 码位上限。`fluent` 会被忽略并返回 `option_ignored`。13 个声音标签（`{laugh}` 等）都支持，在标记处发出对应的声音。效果因音色和文本而异，`{sigh}`、`{breath}` 等呼吸类声音较轻，可能不明显。
- 不执行停顿：`{pause}` 会被移除并返回 `markup_degraded`（模型卡公布的范围是 [0, 0]）；写错的停顿按通用规则去掉，并返回 `markup_adjusted`。`{pron}` 按原文朗读，并返回 `markup_degraded`。
- 行内情绪逐句生效：`{emotion <情绪>}` 处插入情绪标签，之后每段开头补当前情绪；`fluent` 会被忽略并提示，之后的段落不再加情绪。表演标记按模型卡的 `script_markup.performance` 执行，两个模型都支持 10 个：`{whisper}` 耳语、`{shout}` 大喊、`{excited}` 兴奋、`{sarcastic}` 讽刺、`{curious}` 好奇、`{tearful}` 带哭腔、`{playful}` 俏皮、`{nervous}` 紧张、`{tired}` 疲惫、`{gentle}` 温柔。标签加在其后第一处要朗读的内容前，作用于其后的语句，不跨段延续。插入的标签不进字幕，计入发送文本。
- `{sub 原词 读法}` 朗读替换词，字幕显示原词。你自己写的方括号属于客户文本：原样传递，保留在字幕中，不是 Beatra 标记；上游模型可能把它当作声音或语气来表现。
- 字幕遵循下方统一契约；字幕失败时仍交付有效音频并正常计费。
- `previous_text` 和 `model_options.elevenlabs.next_text` 只作上下文，从不计费；Beatra 发给模型的是前文末 100 字和后文首 100 字。

`model_options.elevenlabs` 支持 `stability`、`similarity_boost`（0–1 的有限数值）、`seed`（0–4,294,967,295 的整数）、`next_text`（字符串），以及 `apply_text_normalization`。当前可用的文本规整值在模型卡的 `constraints.limitations` 中列出；两个模型目前都只有 `auto`，`on`、`off` 会被忽略并返回警告。未知键忽略并提示，已知键的非法值在扣费前拒绝。

确认费用前，对**完整的最终参数**做免费预检。validate 返回加权字数和价格，检查同样的语法和上限，但不检查两个任务的并发上限，也不检查当前容量，所以通过预检的请求仍可能遇到下面的错误。测试中 2,000 字约耗时一分钟；10,000 字可能需要数分钟，持续轮询同一任务即可。

### 上限与错误

每个账号最多同时有两个 ElevenLabs 合成任务在进行，v4 与 v4 Turbo 合计。免费的私有[克隆试听](https://docs.beatra.cn/zh/docs/capabilities/speech/voice-clone)计入这两个；MiniMax 和豆包的任务不计入。

扣费前的错误。不扣费，也不创建任务，所以重试时可以沿用同一个 Idempotency-Key / `client_request_id`，也可以换新的。`voice_unavailable` 例外：它是永久性的，不可重试。

| 错误 | `details.reason` | 含义与处理 |
| --- | --- | --- |
| 429 `rate_limited` | `speech_inflight_limit` | 已有两个 ElevenLabs 合成任务在进行。等其中一个结束。`retry_after_seconds` 为 20，REST 会返回同样秒数的 `Retry-After` 响应头（MCP 没有响应头，请用 `details.retry_after_seconds`）。 |
| 503 `model_unavailable` | `voice_temporarily_busy` | 克隆音色正在准备或恢复。按 `retry_after_seconds` 后重试（约 30 秒；上一次恢复还在核查时为几分钟）。 |
| 503 `model_unavailable` | `voice_temporarily_unavailable` | 这个克隆音色暂时无法恢复，其他音色不受影响。已知恢复时间时见 `available_at`。 |
| 422 `invalid_request`、`unsupported_option`、`voice_unavailable` 或 `voice_retired` | — | 修正请求，或见下文“音色下线”和克隆页。 |

任务受理之后的错误是任务错误，全部全额退款；之后重试需要新的 `client_request_id` 或 `Idempotency-Key`：

| 任务错误 | 场景 |
| --- | --- |
| `model_rate_limited` | 模型容量已满。 |
| `model_unavailable` | 模型额度用尽；已知恢复时间时见 `available_at`。 |
| `invalid_request` | 模型认为文本过长而拒绝。 |
| `model_output_invalid` | 模型返回了不可用的音频。 |
| `voice_retired` | 请求受理后该音色已下线。 |
| `voice_unavailable`，或原因为 `voice_temporarily_busy` / `voice_temporarily_unavailable` 的 `model_unavailable` | 执行时克隆音色无法准备；含义与扣费前相同，任务退款。 |

### 音色下线

计划下线的预置音色在下线日之前仍会列出并可使用，其音色对象带有 `retirement: {"retire_at": "<ISO 8601 UTC>", "replacement_voice_id": "<id 或 null>"}`；没有计划时该字段不存在。使用这类音色合成会成功，并带一条 `voice_retiring` 警告（中文文案，字段为 `voice`），写明下线日期，已知时还给出推荐的替代音色。过了这个日期，请求返回 422 `voice_retired`，`details.retirement_at` 给出下线时间，已知时 `details.replacement_voice_id` 给出替代音色。受理时检查一次，执行时再检查一次；执行时命中会让任务失败并退款。替代音色只是建议，Beatra 不会自动换音色。保存的样本无法再恢复的克隆音色返回 422 `voice_unavailable`：这是永久性的，请勿重试，请联系客服；该次合成不收费，原克隆费用不退。

## 字幕

字幕默认开启，不另收费。不传 `subtitle_enable` 即请求字幕，传 `false` 关闭。字幕能力不足不会拒绝请求，字幕处理失败也不会让有效音频的任务失败；无法交付字幕时仍交付音频并正常计费，ElevenLabs 同样如此。不额外调用付费识别或对齐服务。

有字幕时，任务只有一个 `output.subtitles` 对象：

```json
{"url":"https://cdn.beatra.cn/tenants/…/subtitles.json","format":"json","files":{"json":"https://cdn.beatra.cn/tenants/…/subtitles.json","srt":"https://cdn.beatra.cn/tenants/…/subtitles.srt","vtt":"https://cdn.beatra.cn/tenants/…/subtitles.vtt"},"segment_count":2}
```

三个文件写入后不再修改，链接永久有效。没有字幕时整个 `output.subtitles` 字段省略。REST 查询、MCP 任务结果和回调使用同一结构；MCP 的音频仍是第一个产物，字幕不加入该产物数组。

JSON 是逐句数组：`text` 为显示文字，`time_begin`、`time_end` 为整数毫秒；`text_begin`、`text_end` 为显示文本中的字符位置，含头不含尾。显示文本去掉表演标记，`{pron}`、`{sub}` 显示原词。三家使用同一套分句规则：中日韩长句按每条最多 32 字拆分，其他语言最多 80 字符，优先在标点或词边界拆开；时间按朗读起止，不延伸到后面的停顿。MiniMax 长文中落在上游段内的边界按字符比例估算，对应字幕条带 `estimated: true`。SRT、VTT 与 JSON 逐条对应，客户端可直接下载文件。

声音列表提供 `subtitles: true/false`。明确需要字幕时选择带字幕的声音，并查看模型的语言范围。豆包提供中文、英文、粤语逐句字幕；其他语言组合可能只交付音频。已知不能提供时，成功消息会简短提示换一个声音；偶发字幕失败不增加用户警告。不交付残缺时间线，不回填旧任务或改写旧文件。

## 任务状态与恢复

`queued` 与 `running` 不是失败。持续轮询同一个 `task_id`；不要因为任务还在
运行就提交替代请求。创建响应丢失时，用同一个 `client_request_id` 原样重试；
任何输入变化都要换新 ID。失败任务自动退款。只从终态任务响应返回音频产物、
实际模型与扣费；产物 URL 是 CDN 地址——请尽快转存到自己的存储。

## 通过 REST 调用

直连协议集成可使用 `POST /v1/audio/speech` 与共享任务端点，参数见生成的
[接口定义](https://docs.beatra.cn/zh/docs/api-reference/operations/audio/text_to_speech_v1_audio_speech_post)。
推荐通过 Skill + MCP 接入；REST 适合自定义应用直连。
