---
name: digital-human
description: "数字人（avatar + voice clone）一站式：训练 + 生成 + 段落编辑 + 管理。触发场景：(a) 训练分身——\"训这个人/克隆这个人/做 AI 老师/做 AI 主播\"；(b) 生成口播视频——\"用张医生说欢迎语/用我训的分身介绍产品\"；(c) 编辑某段——\"改第 N 段 / 重做第 2 段 / 刚才那条改一句话 / seg-3 重新生成\"；(d) 管理——\"我的分身有哪些 / 我的视频有哪些 / 张医生训过没 / 列我的视频\"。范围：所有\"画面里出现真人/数字人讲话\"的视频。"
version: 1.0.3
owner_repo: Optima-Chat/optima-gen
---

# Digital Human Skill

> **CLI 版本要求**：本 SKILL（v2 音频先行）依赖 `@optima-chat/gen-cli ≥ 2.3.0`（含 `gen voice preview` / `gen voice concat` / `gen avatar video --audio`）。LLM 首次调这些命令失败 `command not found` / `unknown command` / `unknown option` → 报错引导用户升级 `@optima-chat/gen-cli`，**不**回退到 inline ffmpeg / 旧的逐段视频拼接流程。
>
> **后端要求**：音频驱动出片（`gen avatar video --audio`）需后端启用 `DOCTOR_VIDEO_AUDIO_DRIVEN`。未启用时命令返回"音频驱动 doctor-video 暂未开放"——这是部署灰度状态，不是用户错误，告知用户稍后可用、不重试。

数字分身一站式：训练用户上传的真人视频得到 voice + avatar，后续用名字引用做口播视频。

> **用户向输出原则**：对话 / status / 报告里**不出现具体上游品牌 / 服务名**，统一用"训练服务" / "数字分身训练" / "上游" 等中性词。skill 内部 reasoning 引用（spec / probe / source code）可保留具体名称用于 LLM 思考。

## 路由

| 用户输入 | 走哪段 |
|---|---|
| 有视频 + 训练意图（"训这个人"/"克隆"/"做 AI X"） | [references/train.md](references/train.md) |
| 已有分身 + 要做视频（"用张医生说..."/"用我训的分身..."） | [references/generate.md](references/generate.md) |
| 没指定主体但要"数字人/口播/真人讲解" | [references/generate.md](references/generate.md) → "选公共 avatar" 分支 |
| **要改已有视频的某段**（"改第 N 段 / 重做第 2 段 / 刚才那条改一句话"） | [references/edit.md](references/edit.md) **§1.5 state check** —— 实际走 generate.md §6.6 还是 edit.md §4 由 video 的 `meta.md` "当前版本" state 决定,**不靠用户措辞猜** |
| **出片确认** / **续接未完成视频**（"都 OK 出视频 / 继续 X 视频 / 刚才那条出片"） | [references/generate.md §6.5](references/generate.md)(重展音频试听 checkpoint)/ §6.8(跨 session 续接,自动识别 `(audio generating)` / `(audio-review pending)` / `(rendering)` 各 state) |
| "我的分身有哪些"/"我的视频有哪些"/"X 训过吗"/"改名"/"删了 X" | [references/manage.md](references/manage.md) |

公共 avatar / voice 清单见 [references/avatar-catalog.md](references/avatar-catalog.md)。

## 工作空间

所有用户私有状态在 `~/digital-human/`,分 3 部分:

```
~/digital-human/
├── avatars.md                          # 分身索引(用户能直接看 + 改)
├── training/<external_id>/             # 每个分身的训练素材归档
│   ├── source-video.mp4                # 用户上传的原视频副本
│   ├── source-audio.wav                # 提取的 90s 单声道
│   ├── train-meta.md                   # 训练元数据(背景决策 / task ids / specs)
│   └── preview.jpg                     # 缓存的 preview 图(可选)
└── videos/<YYYYMMDD-HHMM>/              # 每条生成视频的 workspace
    ├── meta.md                         # 这条视频的 avatar / 总文本 / segments 表
    ├── segments/
    │   ├── seg-NN.txt                  # 这一段的文本
    │   └── seg-NN.wav                  # 这一段的音频(voice preview 出,逐段试听用)
    ├── full-audio.mp3                  # 确认后拼接的整条音频(驱动渲染)
    ├── final-v1.mp4                    # 单条音频驱动渲染的初版
    ├── final-vN.mp4                    # 改段后重渲的新版(最多 10 个)
    └── history.md                      # 迭代记录
```

**首次训练**时 LLM 自动 `mkdir -p ~/digital-human/{training,videos}` + 创建 `avatars.md`,模板:

```markdown
# 我的数字分身

| 名字 | external_id | 训练时间 | preview |
|---|---|---|---|

<!-- 使用说明:
- "名字"是你叫它的方式,可以改(直接改这一列就行)。
- "external_id"是系统内部 ID,格式为 `dh-` + 8 个小写字母数字,**不要改**;改了 agent 找不到对应分身,你也无法通过运维找回(因为只有这个 ID 能从后端查)。
- 训完一个分身后,这里会自动加一行。
- 删除某一行 = **对你视角的硬删除**:你失去所有引用路径,后端 orphan 数据需要联系运维直接查 DB 才能找回。删除前 agent 会二次确认。
- voice_id / avatar_id 这些底层标识不在这里,需要时 agent 会自动从后端查。
-->
```

### `videos/<id>/meta.md` 模板

每次生成视频时创建:

```markdown
# Video: <slug from text or topic>

| 字段 | 值 |
|---|---|
| video-id | 20260515-1430 |
| avatar | 张医生 (external_id: dh-a7f2k9m1) |
| 创建时间 | 2026-05-15 14:30 |
| 当前版本 | final-v1.mp4 |  ← 见下方"当前版本" 4 阶段 state machine

## 总文本

> <用户给的完整 text>

## Segments

| seg | text | 音频时长 | 音频文件 | 最后更新 |
|---|---|---|---|---|
| 01 | "..." | ~Xs | segments/seg-01.wav | <时间> |
```

**"当前版本"字段 = 4 阶段 state machine**(音频先行,详见 [generate.md §5 / §6.5 / §7](references/generate.md)):

| state | 何时写入 | 含义 |
|---|---|---|
| `(audio generating)` | generate.md §5(初版 placeholder) | 音频段还在生成,跨 session 续接靠这个 marker |
| `(audio-review pending)` | generate.md §6.5(进音频试听 checkpoint) | 音频段都生成完,等用户逐段试听确认 |
| `(rendering)` | generate.md §7(确认后,拼音频 + 单条渲染中) | 已确认,视频渲染中 |
| `final-vN.mp4` | generate.md §7(渲染成功后) | 已出片,可正常 edit.md 主流程改段 |

`manage.md` 列视频时按 state 显示 ✅ / ⏳ / 🎬 / 🚧;`edit.md` §1.5 按 state 路由(state 即 routing source-of-truth)。

**不写 UUID** —— meta.md 是用户视野;voice_id / avatar_id 需要时 LLM `gen avatar profile` 反查。

### `training/<external_id>/train-meta.md` 模板

```markdown
# Training: 张医生 (external_id: dh-a7f2k9m1)

| 字段 | 值 |
|---|---|
| display_name | 张医生 |
| external_id | dh-a7f2k9m1 |
| 训练时间 | 2026-05-15 |
| voice_task_id | uuid-xxx |
| avatar_task_id | uuid-yyy |
| 背景决策 | --clean-bg |
| 源视频时长 | 145s |
| 源视频分辨率 | 1080×1920 |
| preview_image_url | https://...preview.jpg |
```

用于:重训(可重用 source-video / source-audio)、debug、运维 orphan 找回。

**LLM 读 `avatars.md` 时的健全性校验**（防用户手抖改坏）：

| 检查 | 错误处理 |
|---|---|
| external_id 不符 `^dh-[a-z0-9]{8}$` 且不符存量 ASCII slug 模式（`^[a-z0-9_-]+$`） | 警告"第 N 行的 external_id 看起来损坏，可能无法引用：[名字]" |
| 名字列为空 | 警告 + 建议补名字 |
| 名字列重复 | 警告"两个分身都叫 'X'，下次引用会有歧义" |

校验失败**不阻止**继续操作（用户的文件，LLM 不越界改），只发警告。

## LLM 如何区分用户输入是名字还是 external_id

当用户说"用 X 做视频"时：

| X 形态 | 判断 | 走流程 |
|---|---|---|
| 含中文 / 非 ASCII | 必是名字 | 在 `avatars.md` "名字"列模糊匹配 |
| 匹配 `avatars.md` "名字"列任一行 | 名字 | 取该行 external_id |
| 纯 ASCII + 形如 `dh-[a-z0-9]{8}` | external_id（新格式） | 直接走 backend profile 查 |
| 纯 ASCII + 含 `-` + 不在"名字"列 | 大概率存量 external_id（旧 slug，例 `dr-wang`） | 走 [manage.md "存量分身"](references/manage.md) 流程 |
| 纯 ASCII + 不含 `-` + 不在"名字"列 | 歧义 | 反问"是分身名字还是 ID?" |

## Global Rules

优先级高于任何 pipeline 步骤。违反会导致泄漏、超支或数据错误。

1. **触发严格**：必须有视频 + 训练意图二者。**只有视频没意图 → 不触发**（可能用户只想让你看视频）。**只有意图没视频 → 反问**"要训练谁的分身，把视频发我"。
2. **背景必须显式决策**：训练系统会把视频背景 baked 进 avatar look，**video gen 时无法替换**。`--clean-bg` 或 `--accept-baked-bg` 二选一，不能 silent 默认。
3. **名字由用户起、external_id 由 LLM 内部生成**：用户只面对一个名字（中英文皆可），UUID（voice_id / avatar_id / external_id）全程不冒泡。
4. **Anti-fabrication**：命令 / flag / 端点必须用本 skill 列出的精确版本。CLI sub-command / API 端点不允许凭印象拼。**特别注意**：`gen avatar video` 要 `--avatar` + `--voice` 两个 UUID（不支持 `--external-id` 自动反查，必须先 `gen avatar profile` 拿 IDs）。
5. **用户向不暴露上游品牌**：见上面"用户向输出原则"。
6. **限制硬约束**（来自 spec §13）：
   - ~~分身合成视频 text ≤ 50 字 / 输出 ≤ 10 秒~~ **已取消**：那是旧的逐段 text-driven 视频约束；音频先行下成片是**单条音频驱动渲染**，实测能吃长文本（69s+），无 50 字限制。拆段只为逐段试听粒度。
   - 视频 ≥ 30 秒（voice 训练用），建议 2-5 分钟（avatar 质量，spec §13.5）
   - 单声道音频质量好（噪声 / 多人混杂会 fail）
7. **沙箱 bash 须知（issue #183）**：agent-runtime 的 bash 是安全沙箱，**软拒绝** `$( )` 命令替换、`for`/`while` 循环、命令内换行、`;`/`&&`/`||` 串多条、`> file`/`>>` 重定向、`| jq`/`| sed`/`| grep`/`| wc` 管道、`nohup &`。所以**取值 / 判断 / 写文件全走 LLM tool_use**：用 **Read tool** 读文件 / 把内容内联进命令，用 **Write/Edit tool** 写文件，从单条 CLI 的 stdout JSON 里**自己读字段**（不 `$()`/`jq` 截），并发靠**一轮发多条单命令**（不写 `for`/`nohup &`）。详见 [generate.md §0](references/generate.md) + [edit.md 顶部沙箱须知](references/edit.md)。**最严重后果**：撞 soft-deny 后回退去手搓 `ffmpeg -f concat` 拼音频 / 拼视频——**绝对禁止**，永远用 `gen voice concat`（这就是 #183 的根因）。

## 限制 + 失败模式（来自 spec §13）

| 限制 | 说明 |
|---|---|
| ~~text ≤ 50 字~~ 已取消 | 音频先行单条渲染能吃长文本（旧约束是逐段 text-driven 才有；spec §13.3 对单条 audio-driven 不成立，已实证） |
| 单视频 4 looks 只用第一个 | 上游服务未暴露 list-looks endpoint（spec §13.5，v1.1 issue #54） |
| trained look "still processing" 短期 race | adapter 自动 retry，30min 失败降级公共 avatar（spec §13.2） |
| 背景 baked | video gen `background` 参数无效，训练时必须干净背景（spec §13.4） |
| consent | API 不强制，产品上自行做合规（医院授权书 / 法律协议）（spec §13.6） |
| 跨设备同步 | workspace `~/digital-human/avatars.md` 本地，换设备看不到（已知妥协，spec [naming-ownership](../../../docs/superpowers/specs/2026-05-14-digital-human-naming-ownership.md) 是 must-have 时的恢复路径） |

## 不在范围

- ❌ Multi-persona 一次训（一个 external_id 对应一次 onboard）
- ❌ Background remove / matting（让用户重拍干净背景或自己 preprocess）
- ❌ License / consent 流程（产品层做，不在 skill 内）
- ❌ 跨设备 workspace 同步
- ❌ 团队共享 / 跨用户分身
- ❌ mix-and-match 主流程支持（见 [generate.md "advanced 参考实现"](references/generate.md)）

## Contract for business-layer wrappers

业务层(例 sibada-doctor / 其他)可以基于 digital-human 包 wrapper SKILL。本节文档化**稳定契约**,wrapper 可以安全 depend on。

> **⚠️ v2 破坏性变更(skill 2.0.0 / gen-cli 2.3.0)**:音频先行流程把成片从"逐段 mp4 + `gen video stitch` 拼接"换成"逐段音频试听 + `gen voice concat` + `gen avatar video --audio` 单条渲染"。受影响的稳定面:`segments/seg-NN.mp4` → `seg-NN.wav` + `full-audio.mp3`;`gen video stitch`(仍在 CLI,但不再是 digital-human 出片路径);state machine 取值。Mode A wrapper(reference 本 skill reference 文档)自动获得新流程;Mode B wrapper(直接编排 CLI)需迁移到新命令。

### 稳定 CLI surface(`@optima-chat/gen-cli ≥ 2.3.0`)

| 命令 | 用途 | 稳定性承诺 |
|---|---|---|
| `gen avatar train` | 训练 avatar look | flag 名 + 必填项稳定 |
| `gen avatar onboard` | 并发训 voice+avatar | flag 名 + 必填项稳定 |
| `gen avatar profile` | 查分身 profile | 返回 JSON 字段 `heygen_voice_id` / `heygen_avatar_look_ids` 稳定 |
| `gen voice preview` | 逐段出音频试听(纯音频,不烧视频额度) | `--voice / --text / --output` + 可选 `--text-type` 稳定(**无** `--voice-speed`/`--voice-emotion`) |
| `gen voice concat` | 多段音频拼成整条(concat filter 重编码) | `--workspace-dir / --inputs / --out` 稳定;返回 JSON `output_path / duration_sec / input_count` 稳定 |
| `gen avatar video` | 用训练分身合成 | `--avatar` + (音频驱动 `--audio` \| 文本驱动 `--voice / --text`) + `-o, --output / --title` 稳定 |
| `gen video --provider heygen` | 用任意 heygen avatar 合成(含公共 avatar) | `--avatar / --voice / --text / -o, --output` + 可选 `--voice-emotion / --bg-color / --caption / --title / --avatar-style / --voice-speed / --bg-image` 稳定 |
| `gen video stitch` | 多段 mp4 拼接(仍存在,但 digital-human v2 出片**不用**它) | `--workspace-dir / --inputs / --out / --codec` 稳定 |
| `gen voice clone` | 低层 voice 训练 | flag 稳定 |

注:`gen doctor *` 是 `gen avatar *` 的兼容性别名,wrapper **应该用 `gen avatar`**,不用 doctor(虽然两者等价)。

### 稳定 workspace 布局

```
~/digital-human/
├── avatars.md                              # 用户分身索引
├── training/<external_id>/                 # 每分身训练素材
│   ├── source-video.mp4
│   ├── source-audio.wav
│   ├── train-meta.md
│   └── preview.jpg
└── videos/<YYYYMMDD-HHMM>/                  # 每条视频 workspace
    ├── meta.md
    ├── segments/seg-NN.{txt,wav}
    ├── full-audio.mp3
    ├── final-vN.mp4
    └── history.md
```

wrapper 可以:
- 在 `videos/<id>/` 下加 wrapper 自己的文件(例 `sibada-meta.md` 存 hospital/department 字段),只要不重名 + 不动 digital-human 自己的文件
- 解析 `meta.md` 取分身、segments、当前版本(state machine);**不**写 meta.md 字段(LLM agent 才写)

### 稳定 `meta.md` schema

"当前版本"字段 = 4 阶段 state machine(音频先行,详见 [audio-first-flow SPEC](docs/2026-05-30-digital-human-audio-first-flow.md)):

| state | 含义 |
|---|---|
| `(audio generating)` | 音频段还在生 |
| `(audio-review pending)` | 音频段都生完,等用户逐段试听确认 |
| `(rendering)` | 已确认,拼音频 + 单条渲染中 |
| `final-vN.mp4` | 已出片 |

wrapper depending on this schema:**字段名 / 表结构 / state machine 取值** 都稳定(v2 取值已换,见顶部破坏性变更说明);新增字段会向后兼容(只加不删)。

### 两种集成模式

**Mode A: SKILL composition with pre/post hooks(推荐)**

wrapper SKILL 在自己 `references/*.md` 流程里 reference digital-human:
- (wrapper) 验证业务前置 → 走 [train.md](references/train.md) 训练 → (wrapper) post-hook 写业务 DB
- (wrapper) 业务上下文 → 走 [generate.md](references/generate.md) 生成 → (wrapper) post-hook 写 video task 到 DB
- (wrapper) 改段触发 → 走 [edit.md](references/edit.md) → (wrapper) post-hook 更新 DB

LLM 跟 wrapper SKILL 主流程,**穿插**调用 digital-human 各 reference。digital-human 内部不感知 wrapper 存在。复用完整逻辑(不重写),只插 hook;升级 digital-human 自动获益。

**Mode B: Direct CLI primitives orchestration**

wrapper 不 reference digital-human SKILL,直接编排 CLI:`gen avatar onboard ... → gen voice preview ...(逐段)→ gen voice concat ... → gen avatar video --audio ...`。wrapper 自己定义文件布局 / state。适用流程跟 digital-human 差异大的场景。

### 不稳定项(wrapper 不应 depend on)

- 内部 bash 步骤编号 / 章节号(`§6.5` 等):随实施迭代会变
- `history.md` 行格式 / `[audio-review]` / `[v1]` 标签:可能演化
- 错误码 `code` 稳定,`message` 文案可能改
- `segments/seg-NN.wav` / `full-audio.mp3` 内部码率 / 编码参数:跟上游返回 + concat 编码有关
- LLM user-facing 话术:产品 UX 调整时会改

### Versioning

- `@optima-chat/gen-cli`:semver,major 破坏性变更才升(删 flag);minor 加 primitive / flag(向后兼容);patch 修 bug
- workspace 布局 + meta.md schema:跟随 optima-agent skill 包版本;major 升才破坏,minor 只加不删

wrapper SKILL 应在自己 SKILL.md 声明:
```
依赖:
- @optima-chat/gen-cli ≥ 2.3.0           (含 voice preview / voice concat / avatar video --audio)
- @optima-chat/optima-agent ≥ <含 digital-human 2.0.0 的包版本>  (音频先行 + wrapper contract)
```

## 相关 / 参考

- **Backend**：`optima-gen/packages/generation`（adapters / routes / db）
- **CLI**:`@optima-chat/gen-cli ≥ 2.3.0`(含 `gen voice preview` / `gen voice concat` / `gen avatar video --audio`),命名空间分层:
  - **高层 facade**(本 skill 主用):`gen avatar onboard / profile`(训 + 查) + `gen avatar video --audio`(音频驱动单条出片)
  - **音频先行**:`gen voice preview`(逐段试听音频)/ `gen voice concat`(拼整条音频),本 skill `generate.md §6 / §7` 和 `edit.md §5 / §6` 用
  - **视频处理**:`gen video --provider heygen`(任意 heygen avatar 单条合成,含公共 avatar 路径)/ `gen video stitch`(多段 mp4 拼接,仍在但 digital-human v2 不用)
  - **低层**(单独操作):`gen avatar train`(仅训 avatar 不训 voice) / `gen voice clone`(仅训 voice)
  - **辅助**:`gen task get/cancel/list`
  - **兼容性别名**:`gen doctor onboard / video / profile` 仍可用,跟 `gen avatar *` 行为等价;本 skill 统一用新名
- **Spec**：[optima-doctor-avatar §13](https://github.com/Optima-Chat/optima-doctor-avatar/blob/main/docs/superpowers/specs/2026-05-07-doctor-avatar-poc-design.md#13-phase-1-probe-findings-2026-05-08)
- **v1.1 follow-up**：[optima-gen issue #54](https://github.com/Optima-Chat/optima-gen/issues/54)
- **设计 SPEC**：
  - **[音频先行流程 (v2,当前权威)](docs/2026-05-30-digital-human-audio-first-flow.md)** —— 逐段音频试听 + 单条音频驱动渲染,取代旧的逐段视频 + stitch(在 optima-gen 仓)
  - [skill 归并](../../../docs/superpowers/specs/2026-05-14-digital-human-skill-consolidation.md)
  - [workspace 命名](../../../docs/superpowers/specs/2026-05-14-digital-human-workspace-naming.md)
  - [segment 持久化](../../../docs/superpowers/specs/2026-05-15-digital-human-segment-persistence.md)
  - [review checkpoint](../../../docs/superpowers/specs/2026-05-16-digital-human-segment-review-checkpoint.md)
  - [gen video stitch primitive + wrapper contract](../../../docs/superpowers/specs/2026-05-16-gen-video-stitch-primitive.md)
- **相关 skill**:`video-gen`(产品视频;与本 skill 互斥 — 见两边 description)

## 磁盘提示

workspace 持久化每条视频的 segments + final + 训练源视频,长期重度用户(年频次 100+ 视频)可能积累几 GB。v1 不自动清理,用户嫌占空间:

```bash
du -sh ~/digital-human/                       # 总占用
du -sh ~/digital-human/training/*/            # 各分身训练素材占用
du -sh ~/digital-human/videos/*/              # 各视频占用
```

清理:`rm -rf ~/digital-human/videos/<old-id>` 删旧视频目录,`rm -f ~/digital-human/training/<external_id>/source-video.mp4` 单独删源视频文件(保留 train-meta.md 当存根)。
