> ## Documentation Index
> Fetch the complete documentation index at: https://docs.youka.io/llms.txt
> Use this file to discover all available pages before exploring further.

# AI 代理

> 供调用 Youka CLI 或 API 的代理使用的操作规则与工作流模式

Youka 的设计理念是由代理来驱动。每条 CLI 命令都会返回机器可读的 JSON 信封（envelope），每次写入都支持幂等键（idempotency keys），并且公共 API 暴露的能力面与语义保持一致。本页面汇总了代理作者在将 Youka 接入工作流之前需要了解的操作规则。

## 快速开始

如果你是第一次为代理做接入，请先安装 Youka CLI，然后安装 Youka 卡拉 OK 技能：

```bash theme={null}
npm install -g @youka/cli
npx skills add https://github.com/youka-io/skills --skill youka-karaoke
```

这样会为代理提供一个由 CLI 驱动的现成 Youka skill。安装完成后，使用下面的工作流模式来创建卡拉 OK 视频、自定义样式，并安全导出 MP4 文件。

### 给你的代理下指令

安装好 CLI 和 skill 之后，你可以这样给代理一个直接任务：

<CodeGroup>
  ```text local-file theme={null}
  使用 Youka 从 /path/to/song.mp3 创建一个卡拉 OK 视频。将歌词转录为英文，使用干净的卡拉 OK 字幕风格，导出最终视频为 1080p，并保存为 ./karaoke.mp4。
  ```

  ```text url theme={null}
  使用 Youka 从 https://example.com/song.mp4 创建一个卡拉 OK 视频。如果缺少 URL 依赖项，先安装它们。然后自动转录歌词，使用干净的卡拉 OK 字幕风格，导出最终视频，并保存为 ./karaoke.mp4。
  ```
</CodeGroup>

## 操作规则

<CardGroup cols={2}>
  <Card title="始终使用 --json" icon="code">
    在 CLI 中，始终传 `--json`。在 API 中，始终设置 `Accept:
            application/json`。不要解析面向人类的输出。
  </Card>

  <Card title="始终发送幂等键" icon="rotate">
    每次写入都应携带稳定的幂等键，这样在超时后重试会返回原始结果，而不是重复执行工作。
  </Card>

  <Card title="重新读取持久化状态" icon="database">
    不要信任过时的变更响应。在终态任务之后，重新读取 project 或 export 以获取最终状态。
  </Card>

  <Card title="使用退避轮询" icon="hourglass">
    从 2–3 秒的间隔开始。对于长任务使用指数退避。尊重 429 响应。
  </Card>
</CardGroup>

## 输出约定

每个以 `--json` 模式运行的 CLI 命令都会向 stdout 写入且只写入一个信封（envelope）。

成功：

```json theme={null}
{
  "schemaVersion": "1",
  "ok": true,
  "data": { "id": "prj_abc123", "title": "My Song" }
}
```

失败：

```json theme={null}
{
  "schemaVersion": "1",
  "ok": false,
  "error": {
    "code": "CONFLICT",
    "message": "A request with this Idempotency-Key is already in progress.",
    "retryable": true
  }
}
```

退出码：

| Code | Meaning                     | Retry?                |
| ---- | --------------------------- | --------------------- |
| `0`  | 成功。                         | N/A                   |
| `1`  | 运行时错误（网络、API、渲染）。           | 检查 `error.retryable`。 |
| `2`  | 输入无效（错误 flags、不可读 payload）。 | 否 — 修复输入。             |

## 端到端工作流

规范的代理工作流是：创建 project、等待、导出、下载结果。下面给出 CLI 与 SDK 的完整模式实现。

<CodeGroup>
  ```bash CLI theme={null}
  # 1. Create the project and wait for stems + lyrics sync to finish
  PROJECT_JSON=$(youka project create ./song.mp3 \
    --mode transcribe \
    --lang en \
    --wait \
    --idempotency-key "create-song-2026-04-08-001" \
    --json)

  PROJECT_ID=$(echo "$PROJECT_JSON" | jq -r '.data.projectId')

  # 2. Start and wait for an export
  EXPORT_JSON=$(youka export create "$PROJECT_ID" \
    --resolution 1080p \
    --quality high \
    --wait \
    --download \
    --output ./karaoke.mp4 \
    --idempotency-key "export-$PROJECT_ID-v1" \
    --json)

  echo "Done:" "$(echo "$EXPORT_JSON" | jq -r '.data.fileUrl')"
  ```

  ```ts SDK theme={null}
  import { YoukaClient, YoukaRequestError, YoukaTaskError } from "@youka/zh/sdk";

  const client = new YoukaClient({ apiKey: process.env.YOUKA_API_KEY! });

  async function createAndExport() {
    // 1. Create the project and wait for stems + lyrics sync
    const created = await client.projects.create(
      {
        source: { type: "path", path: "./song.mp3" },
        lyricsSource: { type: "transcribe", language: "en" },
      },
      { idempotencyKey: "create-song-2026-04-08-001" },
    );

    const { project } = await client.projects.wait(created);

    // 2. Start and wait for an export
    const exportResult = await client.exports.create(
      project.id,
      { resolution: "1080p", quality: "high" },
      { idempotencyKey: `export-${project.id}-v1` },
    );

    const finalized = await client.exports.wait(exportResult);

    // 3. Download the file
    await client.exports.download(finalized, {
      output: "./karaoke.mp4",
    });
  }

  createAndExport().catch((error) => {
    if (error instanceof YoukaRequestError && error.retryable) {
      // Retry with the same idempotency keys
    }
    if (error instanceof YoukaTaskError) {
      console.error("Task failed:", error.code, error.task.error);
    }
    throw error;
  });
  ```
</CodeGroup>

## 轮询建议

在 SDK 中，大多数写操作都会返回 operation handles。优先使用 `client.projects.wait(...)` 和 `client.exports.wait(...)`，只有在你明确需要更底层的 task 访问时才使用 `client.tasks.*`。

| Job type                             | Recommended initial interval | Max wait |
| ------------------------------------ | ---------------------------- | -------- |
| Project create (stems + lyrics sync) | 3 s                          | 15 min   |
| Stem separation only                 | 3 s                          | 10 min   |
| Lyrics sync only                     | 2 s                          | 5 min    |
| Export render                        | 3 s                          | 30 min   |

在 CLI 中，`--wait` 会为你处理轮询。在 SDK 中，wait helpers 默认使用 2 s 间隔，并接受 `pollIntervalMs`。

<Tip>
  尊重 429 响应。遇到限流时，至少按 `Retry-After`
  header（如果没有则按 30 s）进行退避。
</Tip>

## 幂等键

每个 create 与 update 操作都支持幂等键。规则如下：

* 为每一次“逻辑变更”使用 **稳定、唯一、确定性** 的 key。好例子：`create-song-${sourceHash}`。坏例子：每次重试都生成一个新的 UUID。
* 在重试时复用 **相同** 的 key。服务端会识别重放并返回原始结果。
* Key 的作用域是 per-account，并在 24 小时后过期。
* 如果服务端返回 `IDEMPOTENT_REPLAY_IN_PROGRESS`（HTTP 202），说明原始请求仍在运行。等待后使用相同 key 重试。

示例模式：

```ts theme={null}
const key = `create-${sha256(sourceBytes)}`;

try {
  return await client.projects.create(body, { idempotencyKey: key });
} catch (error) {
  if (error instanceof YoukaRequestError && error.retryable) {
    // Safe to retry with the same key
    return await client.projects.create(body, { idempotencyKey: key });
  }
  throw error;
}
```

完整约定请参见 [API idempotency](/zh/api/idempotency)。

## 错误恢复

根据 `error.code` 与 `error.retryable` 分支处理。常见情况如下：

| Code                                  | Cause             | Action                           |
| ------------------------------------- | ----------------- | -------------------------------- |
| `INVALID_REQUEST`                     | 请求体未通过 schema 校验。 | 修复 payload。不重试。                  |
| `UNAUTHORIZED`                        | 缺少或无效的 API key。   | 反馈给调用方。不重试。                      |
| `NOT_FOUND`                           | 资源不存在或你没有访问权限。    | 不重试。                             |
| `CONFLICT` (409)                      | 版本冲突或幂等冲突。        | 使用相同幂等键重试。                       |
| `TOO_MANY_REQUESTS` (429)             | 限流。               | 按 `Retry-After` 或 30 s 退避。       |
| `INTERNAL_SERVER_ERROR` (500)         | 暂时性的服务端故障。        | 退避重试最多 3 次。                      |
| `IDEMPOTENT_REPLAY_IN_PROGRESS` (202) | 之前的请求仍在运行。        | 等待并使用相同 key 重试。                  |
| `TASK_FAILED`                         | 异步任务以失败结束。        | 将 `error.task.error` 反馈给调用方。不重试。 |
| `TASK_TIMED_OUT`                      | 异步任务超出预算时间。       | 可安全重试一次。                         |

## 发现可变字段

在修改 presets 之前，先获取 preset schema，让模型知道有效字段与取值类型：

<CodeGroup>
  ```bash CLI theme={null}
  # Preset body schema
  youka preset schema --json
  ```

  ```ts SDK theme={null}
  import { KaraokePresetSchema } from "@youka/zh/sdk";

  const presetSchema = KaraokePresetSchema.toJSONSchema();
  ```
</CodeGroup>

对于 project settings，优先先读取当前 project settings，然后通过 `youka project settings <id> --body ...` 提交一个最小化的 update body。

每个代理会话获取一次并缓存结果。

## 并行代理

当多个代理并发对同一账号运行时：

* 将幂等键同时限定到 **代理身份** 与 **逻辑变更**：`agent-${agentId}-create-${sourceHash}`。这可以防止某个代理的重试与另一个代理的新请求发生冲突。
* 保持读取操作不受限制 — `GET` 请求很便宜且没有副作用。
* 如果你需要有序的导出历史，每个 project 对 `POST /projects/{projectId}/exports` 使用单一队列；否则导出结果可能会乱序到达。

## 下一步

* [CLI global options](/zh/cli/global-options) — 代理可用的所有 flag
* [SDK errors](/zh/sdk/errors) — 完整错误类参考
* [API async jobs](/zh/api/async-jobs) — 轮询约定
* [API idempotency](/zh/api/idempotency) — 幂等约定
