# Beatra Skill + MCP — AI 导演

> 先让 AI 导演读懂文字、安排演绎，再交给语音合成，让声音像真人在讲。
> 面向 AI 智能体的独立工作流指南。
> 对应网页：https://docs.beatra.cn/zh/docs/capabilities/speech/ai-director

- 主要接入方式：通用 Beatra Skill，经已授权的 MCP 调用。
- 安装或重新连接：https://beatra.ai/install.md
- MCP 端点：`https://mcp.beatra.cn/mcp`
- 计费操作：`beatra.speech.prepare_script`
- 交互方式：生成一个不透明的 `client_request_id`，把最终参数只提交一次，再用 `beatra.tasks.get` 轮询返回的 `task_id`，直到任务结束。
- 本地媒体：调用 `beatra.assets.upload`，严格按返回的 HTTPS PUT 上传，再使用 PUT 响应里的 artifact ID。

REST/OpenAPI 是备用的协议路径，智能体以 Skill 为主要接入方式。

---

先让 AI 导演读懂文字、安排演绎，再交给语音合成，让声音像真人在讲。

AI 导演会读懂原文和用户的要求，按目标声音能执行的写法，安排情绪、表演、音效、停顿和读音，
返回一份可以直接合成的配音稿。它只添加演绎，不改动原文的任何一个字。没有指定声音时，
它还会推荐一个合适的声音和模型，并说明理由。

## Agent 主路径

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

1. 把原文交给 `input`，用户对效果的要求（如有）交给 `instructions`。用户指定了声音或模型就一并传入；
   没有就省略，让 AI 导演推荐。
2. 创建一个不透明的 `client_request_id`，只提交一次；用 `beatra.tasks.get` 轮询到终态。
3. 把结果里每一稿的 `synthesis` 原样作为 `beatra.speech.synthesize` 的参数，另加你自己的
   `client_request_id`。`synthesis` 为 `null` 的稿子不能直接合成，原因见提示。
4. 用户想调整时（例如"第二段再紧张一点"），把这句话加进 `instructions`（保留之前的要求）再调用一次，然后重新合成。

## 工具参数

```json
{
  "input": "夜深了，他推开门。“有人吗？”屋里没有回答。",
  "instructions": "悬疑一点，问话那句压低声音",
  "client_request_id": "script-opaque-1"
}
```

- `input`：原文，任何支持的语言，最多 50,000 字符。原文里已经写好的标记（如 `{pause 0.5}`）会原样保留，
  AI 导演只在此基础上添加。原文超出所指定声音或模型的单次上限时（ElevenLabs 为 10,000 字符），
  会直接返回与语音合成相同的 422 错误，不产生费用。
- `instructions`：可选，用户的要求，自然语言，任何语言，最多 2,000 字符。传了就以要求为准，
  要求没提到的部分按文字本身的意思自然演绎；不传时整篇按文字的意思自然演绎。
- `voice`、`model`：可选，含义和默认规则与 `beatra.speech.synthesize` 相同。都不传时由 AI 导演推荐；
  只传声音时在它支持的模型里选；只传模型时在这个模型可用的声音里推荐。
- `language`：可选，通常不传（默认 `auto`，自动识别）。只有用户指定了语言，或者文字本身看不出来的方言
  （如粤语 `yue`）时才传。
- `context`：可选，进阶用法。分段导演长文时，传入这一段前后的文字 `{"before": "…", "after": "…"}`，
  各最多 2,000 字符。只用来理解语气，不会被标注、朗读或返回；目标模型支持时，`before` 的结尾会作为建议的上文
  放进 `synthesis.previous_text`，让前后语气接得上。

## 结果

任务成功后，`output` 里是一组配音稿：

```json
{
  "type": "speech_script",
  "model": "ai-director",
  "directed": true,
  "scripts": [
    {
      "synthesis": {
        "input": "{emotion calm}夜深了，他推开门。{emotion fearful}{whisper}“有人吗？”屋里没有回答。",
        "voice": "voice_01JX...",
        "model": "elevenlabs-v4-turbo",
        "language": "zh"
      },
      "summary": "悬疑的开场，问话压低声音",
      "voice_selection": {
        "source": "recommended",
        "voice": "voice_01JX...",
        "model": "elevenlabs-v4-turbo",
        "name": "Wei",
        "preview_url": "https://cdn.beatra.cn/voices/previews/voice_01JX....mp3",
        "reason": "成熟男声，适合旁白，能逐句表演"
      },
      "alternatives": [
        {"voice": "voice_01JY...", "model": "minimax-speech-2.8-hd", "name": "念念",
         "preview_url": "https://cdn.beatra.cn/voices/previews/voice_01JY....mp3", "reason": "成熟女声，适合有声书"}
      ],
      "estimate": {"weighted_characters": 54, "price_credits": "13"},
      "warnings": []
    }
  ],
  "warnings": []
}
```

- `scripts`：每一稿对应一次合成调用。目前总是一稿；以后会有多稿（例如多人分配），请逐稿处理。
- `synthesis`：这一稿的合成参数，原样作为 `beatra.speech.synthesize` 的参数即可（另加 `client_request_id`）。
  `input` 是写好标记的配音稿，另有 `voice`、`model`、`language`，以及目标声音确实执行的整段参数
  `emotion`、`direction`、`previous_text`（不执行的不会出现）。这一稿不能直接合成时为 `null`：
  没有可用的声音、声音读不了这份稿子，或这次没能完成预检（稍后重试即可），原因见提示。
- `summary`：一句话演绎说明，可以直接讲给用户听，不含参数名和标记。语言跟随 `instructions`；
  没有要求时跟随原文。
- `voice_selection`：这一稿用的声音，包括 `voice`、`model`、声音名字 `name` 和试听链接 `preview_url`。
  `source` 为 `recommended` 时由 AI 导演推荐，`reason` 是推荐理由；为 `given` 时是你指定的声音，`reason`
  为 `null`。没有声音时整个字段为 `null`。
- `alternatives`：推荐声音时另给最多 3 个备选，字段与 `voice_selection` 相同（没有 `source`）；
  指定了声音时为空列表。
- `estimate`：用这一稿合成的准确价格，与免费预检一致；`synthesis` 为 `null` 时它也为 `null`。
- `directed`：为 `false` 时 AI 导演这次没能完成演绎，稿子是原文，导演部分不收费；有 `synthesis` 时仍可以直接合成。
- 没有可推荐的声音时（提示 `no_voice_available`），`synthesis`、`voice_selection` 和 `estimate` 都为 `null`：
  请用 `beatra.voices.list` 选一个声音，带上 `voice` 再调用一次。

想换成备选声音时，在 2 天内只改 `voice`（需要时连同 `model`），其余参数不变，用新的 `client_request_id`
再调用一次。AI 导演会复用上次的理解，同一份理解不会重复收费，只按新声音重新安排写法。这次调用同样要满足余额预估和
同时最多 2 个的限制，但复用的理解不会再收费。

## 不同模型上的演绎

AI 导演只写目标模型能执行的写法：

- ElevenLabs：逐句切换情绪，支持耳语、大喊等表演，以及笑声、叹气等音效。
- MiniMax：整段一种情绪，支持停顿、指定读音和音效。
- 豆包：整段一种情绪，支持停顿和指定读音；是否跟随情绪、表演指令和上文因音色而异。

用户明确要求的效果所选声音做不到时，AI 导演会完成其余部分，并在提示里说明做不到什么、用了什么代替。
需要逐句演绎时，可以换一个能逐句表演的声音。

## 提示

顶层 `warnings` 是 AI 导演给用户的说明，需要转告用户；每一稿的 `warnings` 是免费预检对这一稿的提示，通常为空。

| `code` | 含义 |
|---|---|
| `direction_unavailable` | AI 导演没能完成全部或部分演绎，这些句子按原文返回，这部分不收费。`field` 为 `voice` 时表示你指定的声音读不了这一稿（例如文字的语言与声音不一致），请换一个声音 |
| `effect_unavailable` | 所选声音做不到用户要求的某个效果，或文字接近单次长度上限、这个效果没能保留。`effect` 写明哪个效果，`substitute` 写明用了什么代替（没有代替时为 `null`）；`effect` 为 `voice_gender` 时表示这种语言暂无符合要求性别的声音，已推荐最合适的其他声音 |
| `instruction_unsupported` | 要求里有暂不支持的操作，例如翻译、改写原文或分配多个说话人，这部分已忽略 |
| `no_voice_available` | 没有可推荐的声音：没有声音能读这种语言、指定的模型没有能读这种语言的声音、文字超出所有声音的单次上限，或能读的声音暂时都不可用。稿子照常返回 |

## 计费与任务状态

AI 导演先推理再标注，按实际输入、输出和推理 token 后付费。价格和每千字的预估费用见
能力目录 `GET /v1/catalog/capabilities/speech_script`。提交时，余额需不低于这段文字的预估费用（不足 1,000 字按
1,000 字计）；如果本账号还有一个 AI 导演任务正在进行，还要加上它的预估费用。按每千字 146 积分计算，1 万字需要
1,460 积分。余额不足时返回 402 `insufficient_balance`，`details.reason` 为 `director_estimate`，不会创建任务。
最终按实际用量扣费，通常低于预估。`directed` 为 `false`，或复用了 2 天内的同一份理解时，
导演部分不收费。`directed` 为 `true` 时按实际 token 计费；AI 导演读完后判断原文已经合适、没有添加演绎时也是如此。
合成另按语音合成计费，价格见每一稿的 `estimate`。

每个账号同时最多 2 个 AI 导演任务在进行，全平台同时进行的数量也有上限。第 3 个请求返回 429 `rate_limited`，
`details.reason` 为 `director_inflight_limit`（通过 MCP 调用时，套餐的同时进行上限可能先生效）；AI 导演当前繁忙时
返回 429，`details.reason` 为 `director_busy`。两种情况都不会开始任务，也不扣费。遇到 `director_inflight_limit`
时，请等本账号的一个 AI 导演任务结束后再提交；遇到 `director_busy` 时，请在 `details.retry_after_seconds` 秒后
重试（REST 的 `Retry-After` 响应头给出相同的秒数）。AI 导演暂时不可用时返回 503 `model_unavailable`；这项能力
不用选模型，请稍后重试。

`queued` 与 `running` 不是失败，请持续轮询同一个 `task_id`。创建响应丢失时，用同一个 `client_request_id`
原样重试；任何输入变化都要换新 ID。任务列表（`GET /v1/tasks`）里只显示结果的摘要：标有 `truncated: true`，
不含 `synthesis`；要拿到可以合成的配音稿，请按任务 ID 读取（`beatra.tasks.get` 或 `GET /v1/tasks/{task_id}`）。
AI 导演的结果是一份配音稿，不是作品，不会出现在作品墙；在控制台的动态和账单里显示为"AI 导演"。

## 通过 REST 调用

直连协议集成可使用 `POST /v1/audio/speech/scripts` 与共享任务端点：把每一稿的 `synthesis` 原样作为
`POST /v1/audio/speech` 的请求体。参数见生成的
[接口定义](https://docs.beatra.cn/zh/docs/api-reference/operations/audio/prepare_speech_script_v1_audio_speech_scripts_post)。
推荐通过 Skill + MCP 接入；REST 适合自定义应用直连。

---

## 附录：Skill + MCP 通用约定

### 重试标识

一次逻辑上的生成对应一个 `client_request_id`。创建请求的响应丢失时，用同一个 ID 和完全相同的参数重试。提示词、媒体、模型或输出控制项有任何变化，就换一个新 ID。任务处于 `queued` 或 `running` 是正常状态，继续轮询同一个任务即可，不另建付费任务。

### 轮询与计费以任务为准

用有上限的退避间隔调用 `beatra.tasks.get` 轮询，遇到 `succeeded`、`failed` 或 `canceled` 时停止。实际使用的模型、产物、扣除的积分和退回的积分，以任务响应为准。

### 出错后的处理

保留 Beatra 返回的结构化错误码。鉴权失败时重新连接；余额不足时停下来，请用户充值；模型校验失败后用 `beatra.models.list` 刷新当前约束；上传授权过期，或 MIME 类型、长度不符时，申请新的上传授权。生成参数有变化时，一律使用新的 `client_request_id`。
