@optima-chat/gen-cli 2.5.0 → 2.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/task.d.ts.map +1 -1
- package/dist/commands/task.js +12 -3
- package/dist/commands/task.js.map +1 -1
- package/dist/commands/video.d.ts.map +1 -1
- package/dist/commands/video.js +26 -2
- package/dist/commands/video.js.map +1 -1
- package/package.json +1 -2
- package/.claude/skills/digital-human/SKILL.md +0 -309
- package/.claude/skills/digital-human/references/avatar-catalog.md +0 -47
- package/.claude/skills/digital-human/references/edit.md +0 -219
- package/.claude/skills/digital-human/references/generate.md +0 -378
- package/.claude/skills/digital-human/references/manage.md +0 -137
- package/.claude/skills/digital-human/references/train.md +0 -276
- package/.claude/skills/gen/SKILL.md +0 -366
- package/.claude/skills/motion-control/SKILL.md +0 -68
- package/.claude/skills/multigrid-poster/SKILL.md +0 -194
- package/.claude/skills/multigrid-poster/layouts/2x2.json +0 -34
- package/.claude/skills/multigrid-poster/layouts/3x3.json +0 -43
- package/.claude/skills/multigrid-poster/scripts/compose.py +0 -116
- package/.claude/skills/multigrid-poster/scripts/placeholder.png +0 -0
- package/.claude/skills/multigrid-poster/shared/fonts/MaShanZheng-Regular.ttf +0 -0
- package/.claude/skills/video-compose/SKILL.md +0 -144
- package/.claude/skills/video-compose/scripts/video_compose.py +0 -290
- package/.claude/skills/video-edit/SKILL.md +0 -332
- package/.claude/skills/video-gen/SKILL.md +0 -662
- package/.claude/skills/video-gen/references/cinematic-language.md +0 -158
- package/.claude/skills/video-gen/references/confirm-card.md +0 -49
- package/.claude/skills/video-gen/references/prompt-craft.md +0 -72
- package/.claude/skills/video-gen/templates/INDEX.md +0 -78
- package/.claude/skills/video-gen/templates/before-after-beauty.md +0 -183
- package/.claude/skills/video-gen/templates/drama-fmcg.md +0 -183
- package/.claude/skills/video-gen/templates/kol-reaction-food.md +0 -193
- package/.claude/skills/video-gen/templates/multi-point-apparel.md +0 -185
- package/.claude/skills/video-gen/templates/pain-solution-home.md +0 -184
- package/.claude/skills/video-gen/templates/pdp-360-showcase.md +0 -189
- package/.claude/skills/video-gen/templates/pdp-feature-highlight.md +0 -182
- package/.claude/skills/video-gen/templates/scene-digital.md +0 -183
- package/.claude/skills/video-translate/SKILL.md +0 -547
|
@@ -1,137 +0,0 @@
|
|
|
1
|
-
# Manage Avatars & Videos
|
|
2
|
-
|
|
3
|
-
管理 `~/digital-human/` 工作空间下的分身(`avatars.md`)+ 生成过的视频(`videos/<id>/`)+ 训练素材(`training/<id>/`)。
|
|
4
|
-
|
|
5
|
-
## "我的分身有哪些"
|
|
6
|
-
|
|
7
|
-
直接 Read `~/digital-human/avatars.md` 渲染给用户。
|
|
8
|
-
|
|
9
|
-
文件不存在 → "你还没训过分身,要训一个吗?"(走 [train.md](train.md))
|
|
10
|
-
|
|
11
|
-
文件存在但只有表头(无数据行)→ 同上。
|
|
12
|
-
|
|
13
|
-
## "我的视频有哪些" / "列我的视频" / "做过哪些视频"
|
|
14
|
-
|
|
15
|
-
> ⚠️ **沙箱 bash 须知**(同 [generate.md §0](generate.md)):agent-runtime 的 bash 软拒绝 `for`/`while` 循环、`$( )`、`| grep`/`| sed`/`| awk`/`| wc` 管道、`>` 重定向。所以**别写 `for d in $(ls ...)` 循环**逐目录抓字段——靠 **LLM 自己迭代**:一条 `ls` 列目录,再对每个目录用 **Read tool** 读 meta.md,字段(分身 / 当前版本 / 总文本 / 段数)你读出来自己提取、自己映射 emoji,**不靠 shell**。
|
|
16
|
-
|
|
17
|
-
**步骤**:
|
|
18
|
-
|
|
19
|
-
```bash
|
|
20
|
-
ls -t ~/digital-human/videos/
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
对列出的每个 `<video-id>`,**用 Read tool** 读 `~/digital-human/videos/<video-id>/meta.md`,从中读出:`avatar`(分身)、`当前版本`、`总文本`前 30 字、segments 表行数(= 段数)。**按"当前版本"值映射 state emoji**:
|
|
24
|
-
|
|
25
|
-
| "当前版本" 值 | 状态标注 |
|
|
26
|
-
|---|---|
|
|
27
|
-
| `(audio generating)` | 🚧 音频生成中 (中断,可续接) |
|
|
28
|
-
| `(audio-review pending)` | ⏳ 待试听确认 (audio-review pending) |
|
|
29
|
-
| `(rendering)` | 🎬 渲染中 |
|
|
30
|
-
| `(generating)` / `(review pending)`(旧 v1 字面量) | 📼 旧流程视频 (v1,改动需整条重生) |
|
|
31
|
-
| `final-vN.mp4` | ✅ final-vN.mp4 |
|
|
32
|
-
| 其它 | ❓ 未知 state |
|
|
33
|
-
|
|
34
|
-
渲染给用户(markdown 表格更友好):
|
|
35
|
-
|
|
36
|
-
| video-id | 分身 | 总文本(前 30 字) | 段数 | 状态 |
|
|
37
|
-
|---|---|---|---|---|
|
|
38
|
-
| 20260515-1430 | 张医生 | "大家好,我是张医生。今天介..." | 4 | ✅ final-v2.mp4 |
|
|
39
|
-
| 20260515-1620 | 李老师 | "今天讲..." | 5 | ⏳ 待试听确认 (audio-review pending) |
|
|
40
|
-
| 20260516-0915 | 王主任 | "..." | 3 | 🚧 音频生成中 (中断,可续接) |
|
|
41
|
-
| 20260514-0945 | 张医生 | "..." | 6 | ✅ final-v1.mp4 |
|
|
42
|
-
|
|
43
|
-
**用户暗示 + 操作引导**(给 LLM 的内部规则,**对用户**只输出自然语言话术,**不**报 §-编号):
|
|
44
|
-
- 看到 ⏳ → user-facing 提示 "想继续把 X 的音频听一下、确认出片吗?"(LLM 内部跳 [generate.md §6.5 重展试听 checkpoint](generate.md))
|
|
45
|
-
- 看到 🚧 → user-facing 提示 "想续生剩余音频段吗?"(LLM 内部跳 [generate.md §6.8 续生流程](generate.md))
|
|
46
|
-
- 看到 🎬 → user-facing 提示 "X 还在渲染,稍等;若中断了我可以重渲"(LLM 内部跳 [generate.md §6.8 → (rendering) 重渲](generate.md))
|
|
47
|
-
- 看到 📼 → 旧流程(v1 逐段视频)视频;用户说"改"时 [edit.md §1.5 Step 0](edit.md) 会拦下、引导整条重新生成(新旧不兼容)
|
|
48
|
-
- 看到 ✅ → 视频已完成,用户说"改 seg-N"时由 [edit.md §1.5 state check](edit.md) 路由
|
|
49
|
-
|
|
50
|
-
文件夹不存在 / 空 → "你还没生成过视频,要做一条吗?"(走 [generate.md](generate.md))
|
|
51
|
-
|
|
52
|
-
## "把张医生改成张主任"
|
|
53
|
-
|
|
54
|
-
1. Read `~/digital-human/avatars.md`
|
|
55
|
-
2. 在"名字"列找 `张医生`
|
|
56
|
-
3. Edit 该行"名字"列的 cell:`张医生` → `张主任`
|
|
57
|
-
4. 反馈"已改名,external_id 不变,以后可以用'张主任'引用了"
|
|
58
|
-
|
|
59
|
-
如果用户给的旧名字找不到 → 反问"没找到叫'张医生'的分身,你现在的分身有:[列出]"
|
|
60
|
-
|
|
61
|
-
## "删了张医生"
|
|
62
|
-
|
|
63
|
-
1. Read `~/digital-human/avatars.md`
|
|
64
|
-
2. 找到"张医生"行,记下该行所有字段(名字 / external_id / 训练时间 / preview URL)
|
|
65
|
-
3. **二次确认 + 显式列出恢复锚点**:
|
|
66
|
-
> "确认删除'张医生'? **注意**:这是对你视角的硬删除 —
|
|
67
|
-
> - 你失去所有引用路径(external_id 从你视野消失 + 没有 list 端点)
|
|
68
|
-
> - 后端 DB 行成为 orphan 数据,**找回需要联系运维直接查 DB**
|
|
69
|
-
> - 运维查 DB 需要锚点,以下信息删除后你将看不到,**建议先记下来或截图**:
|
|
70
|
-
> - 名字:`张医生`
|
|
71
|
-
> - external_id:`dh-a7f2k9m1`
|
|
72
|
-
> - 训练时间:`2026-05-14`
|
|
73
|
-
> - preview:`https://...preview.jpg`
|
|
74
|
-
> 真要删吗?"
|
|
75
|
-
4. 用户确认 → 删该行
|
|
76
|
-
5. 反馈"已从你的列表删除'张医生'。如需找回请向运维提供上述 4 个锚点(名字 / external_id / 训练时间 / preview URL)。"
|
|
77
|
-
|
|
78
|
-
如果用户给的名字找不到 → 同上反问。
|
|
79
|
-
|
|
80
|
-
## 存量分身(用户在前 SPEC 落地之前训过 `dr-wang` 等)
|
|
81
|
-
|
|
82
|
-
用户说"用 dr-wang 做视频"但 `~/digital-human/avatars.md` "名字"列没有 `dr-wang`:
|
|
83
|
-
|
|
84
|
-
1. LLM 判断 `dr-wang` 是纯 ASCII + 含 `-` + 不在名字列 → 怀疑是存量 slug
|
|
85
|
-
2. LLM 试着 `gen avatar profile dr-wang`(后端单查仍支持任意 external_id)
|
|
86
|
-
3. **找到**(返回非 null) → 询问:
|
|
87
|
-
> "我发现你有个分身叫 'dr-wang',要起个友好名字加到你的列表吗?(以后可以用'张医生'这种说法引用,而不是记英文 slug)"
|
|
88
|
-
- 用户给名字 → 追加一行到 `avatars.md`:`| <用户给的名字> | dr-wang | <profile.created_at 或当天> | <preview_image_url> |`
|
|
89
|
-
- 用户说"不用" → 直接走生成流程,但下次还会反问
|
|
90
|
-
4. **找不到** → 反问"没训过 dr-wang,要训吗?"
|
|
91
|
-
|
|
92
|
-
这是**自动迁移路径**,不强制,用户继续用 dr-wang 也行。
|
|
93
|
-
|
|
94
|
-
## "删了视频 20260515-1430"
|
|
95
|
-
|
|
96
|
-
视频删除跟分身删除不同 —— 视频 workspace 是用户私有目录,直接删干净,**没有**后端 DB 行残留:
|
|
97
|
-
|
|
98
|
-
1. 确认 video-id 存在(`ls ~/digital-human/videos/<id>`)
|
|
99
|
-
2. 二次确认:
|
|
100
|
-
> "确认删除视频 `<id>`(<段数> 段, 当前 final-vX.mp4)?
|
|
101
|
-
> 这会删除整个目录(segments / final-v* / meta.md / history.md),**不可恢复**。"
|
|
102
|
-
3. 用户确认 → `rm -rf ~/digital-human/videos/<id>`
|
|
103
|
-
4. 反馈"已删除视频 `<id>`。"
|
|
104
|
-
|
|
105
|
-
跟分身删除的对比:分身有后端 DB 行,删 markdown 行 = orphan;视频纯客户端,删目录 = 真删干净。
|
|
106
|
-
|
|
107
|
-
## "改某段视频"
|
|
108
|
-
|
|
109
|
-
走 [edit.md](edit.md)。
|
|
110
|
-
|
|
111
|
-
## 磁盘占用查看
|
|
112
|
-
|
|
113
|
-
用户问"我用了多少磁盘"/"workspace 多大" → 让 ta 自己跑:
|
|
114
|
-
|
|
115
|
-
```bash
|
|
116
|
-
du -sh ~/digital-human/
|
|
117
|
-
du -sh ~/digital-human/training/*/
|
|
118
|
-
du -sh ~/digital-human/videos/*/
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
每个 training/<id>/ ~ 50-200 MB(主要是 source-video);每个 videos/<id>/ ~ 8-20 MB。
|
|
122
|
-
|
|
123
|
-
v1 不做自动清理。用户嫌占空间:
|
|
124
|
-
- 删旧视频:`rm -rf ~/digital-human/videos/<old-id>`
|
|
125
|
-
- 删某分身训练素材:`rm -rf ~/digital-human/training/<external_id>/`(但保留 `avatars.md` 那一行,以后还能用 ID 生成视频,只是不能重训了)
|
|
126
|
-
|
|
127
|
-
## "我有哪些分身做过 X"(进阶筛选)
|
|
128
|
-
|
|
129
|
-
v1 不支持。用户问就告诉:"workspace 不索引'分身 → 视频'反向关系。但可以 `grep` workspace:
|
|
130
|
-
```bash
|
|
131
|
-
grep -l 'external_id: dh-a7f2k9m1' ~/digital-human/videos/*/meta.md
|
|
132
|
-
```
|
|
133
|
-
找出该分身做过的所有视频。如需要常态化筛选请告诉我们。"
|
|
134
|
-
|
|
135
|
-
## LLM 读 avatars.md 时的健全性校验
|
|
136
|
-
|
|
137
|
-
每次 Read 后必须做一遍。**规则详见 [SKILL.md "工作空间" 段](../SKILL.md#工作空间)**,单源真值,本段不复述以免 drift。
|
|
@@ -1,276 +0,0 @@
|
|
|
1
|
-
# Training Workflow
|
|
2
|
-
|
|
3
|
-
按顺序执行,每步必走。把用户上传的真人视频训成数字分身(voice + avatar),写入 `~/digital-human/avatars.md`。
|
|
4
|
-
|
|
5
|
-
> ⚠️ **沙箱 bash 须知**(同 [generate.md §0](generate.md) / [SKILL.md Global Rule #7](../SKILL.md)):agent-runtime 的 bash **软拒绝** `$( )` 命令替换、`for`/`while` 循环、命令内换行(含 `\` 续行)、`;`/`&&`/`||` 串多条、`> file`/`>>`/heredoc(`<<EOF`)重定向、`| jq`/`| sed`/`| grep` 管道、`nohup &`。所以本文档:**取值靠 LLM 自己读单条命令的 stdout JSON**(不 `$()`/`jq`)、**写文件用 Write/Edit tool**(不 heredoc / `echo >`)、**轮询用单条命令逐次跑**(不 `while`)、**所有 id / 路径内联进命令**(不存 `$VAR`)。
|
|
6
|
-
|
|
7
|
-
## 0. 工作空间初始化(首次训练)
|
|
8
|
-
|
|
9
|
-
```bash
|
|
10
|
-
mkdir -p ~/digital-human/training ~/digital-human/videos
|
|
11
|
-
```
|
|
12
|
-
(`mkdir -p` 接受多个路径参数,一条命令建两个目录,不用 `{a,b}` brace 展开。)
|
|
13
|
-
|
|
14
|
-
如 `~/digital-human/avatars.md` 不存在,**创建**含 SKILL.md 模板的空表(见 SKILL.md "工作空间"段)。
|
|
15
|
-
|
|
16
|
-
`training/` 用于归档每个分身的训练源素材(原视频 / 提取音频 / 训练元数据);`videos/` 用于每条生成视频的 segments + final。这两个目录的存在让用户后续可以**改某段视频不用全重生**(见 [edit.md](edit.md))、**重训不用重传视频**(下文 §4.5)。
|
|
17
|
-
|
|
18
|
-
## 1. 询问名字
|
|
19
|
-
|
|
20
|
-
LLM 主动问:
|
|
21
|
-
|
|
22
|
-
> "训完叫什么名字?(中英文皆可,1-40 字,以后用这个名字找它)"
|
|
23
|
-
|
|
24
|
-
用户答 → 直接作为"名字"列的值,**不做任何 normalize / 转换 / 拒绝**。
|
|
25
|
-
|
|
26
|
-
约束(由 LLM 兜底):
|
|
27
|
-
- 长度 1-40 字符(Unicode 可见字符,禁控制字符 `\x00-\x1f`)
|
|
28
|
-
- 不能跟 `~/digital-human/avatars.md` 现有"名字"列重复
|
|
29
|
-
|
|
30
|
-
**重名处理**:
|
|
31
|
-
- 反问"你已经有'张医生'了,起个别的(例如'张医生2'/'张主任')"
|
|
32
|
-
- **不**提供"覆盖"选项(v1 不支持,见 [workspace SPEC §6 #6](../../../../docs/superpowers/specs/2026-05-14-digital-human-workspace-naming.md))
|
|
33
|
-
|
|
34
|
-
## 2. 内部生成 external_id
|
|
35
|
-
|
|
36
|
-
**由 LLM 自己生成**(你直接想一个,**别用 `$(openssl ...)`/管道**——沙箱拦):格式 `dh-` + 8 位随机小写字母数字(例 `dh-a7f2k9m1`)。生成后**记住这个值**,跨段(§4.5 / §5 / §6 / §6.5 / §8 / §9)在每条命令里**直接写全**,不存 `$EXTERNAL_ID` 变量(沙箱里没法安全赋值,且大小写混用会取空值导致路径错误)。
|
|
37
|
-
|
|
38
|
-
**碰撞处理**(随机 8 位,碰撞概率低但非零,后端 `external_id` 有 UNIQUE 约束 `migrate.ts:44`):
|
|
39
|
-
- onboard insert 失败且 error_code 表明唯一冲突 → 重新生成 + 重试
|
|
40
|
-
- **最多重试 3 次**,3 次失败 → 报错给用户
|
|
41
|
-
|
|
42
|
-
## 3. 视频规格检查
|
|
43
|
-
|
|
44
|
-
```bash
|
|
45
|
-
ffprobe -v quiet -print_format json -show_format -show_streams <video-path>
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
记录:
|
|
49
|
-
- `duration` 总时长
|
|
50
|
-
- `width` × `height` 分辨率
|
|
51
|
-
- `audio` 流是否存在 + sample_rate / channels
|
|
52
|
-
|
|
53
|
-
判定:
|
|
54
|
-
|
|
55
|
-
| 项 | 阈值 | 不达标动作 |
|
|
56
|
-
|---|---|---|
|
|
57
|
-
| 视频时长 | < 30s | 中止;要求重传 ≥ 30s 视频 |
|
|
58
|
-
| 视频时长 | 30-119s | 警告"voice OK,但 avatar 质量可能受影响,推荐 ≥ 2min" |
|
|
59
|
-
| 分辨率 | < 720p | 警告但继续 |
|
|
60
|
-
| 音频流 | 缺失 | 中止;视频里没声音怎么训 voice |
|
|
61
|
-
|
|
62
|
-
**不前置检测多人 vs 单人** — ffprobe 拿不到 speaker count。多人混杂等 voice clone fail 时按 `error_message` 反馈。
|
|
63
|
-
|
|
64
|
-
## 4. 背景判断 + 决策
|
|
65
|
-
|
|
66
|
-
spec §13.4 关键约束:训练系统会把整段视频画面(包含背景)整体作为 avatar look 内容,video gen 时**无法用 `background` 参数替换**。
|
|
67
|
-
|
|
68
|
-
**直接问用户三选一**(不要抽帧 + Read 图判断 — 视觉判断不稳定且贵):
|
|
69
|
-
|
|
70
|
-
> "训练前确认:这段视频的背景是?
|
|
71
|
-
> A. **白墙 / 棚拍 / 绿幕 / 单色背景**(干净)
|
|
72
|
-
> B. **标准固定场景**(诊室 / 办公桌 / 医院前台 — 这个背景将永远 baked 进分身,后续无法换)
|
|
73
|
-
> C. **户外 / 商场 / 多人 / 杂乱**(脏)"
|
|
74
|
-
|
|
75
|
-
| 用户选 | 决策 |
|
|
76
|
-
|---|---|
|
|
77
|
-
| A | `--clean-bg` |
|
|
78
|
-
| B | 二次确认:"分身的所有未来视频背景都会是这个场景,可接受吗?" → 接受用 `--accept-baked-bg`;不接受 → 让用户重拍 A 类背景 |
|
|
79
|
-
| C | 强烈劝退,推荐重拍干净背景。如用户坚持产出 → `--accept-baked-bg` + 明确告诉产出视频背景永远是录制时场景 |
|
|
80
|
-
|
|
81
|
-
**禁止**:
|
|
82
|
-
- ❌ silent 默认 `--clean-bg`(错判脏背景为干净 → 产出脏数据)
|
|
83
|
-
- ❌ 自己抽帧用图像识别判断(视觉判断不稳定 + 浪费 token + 用户答更准)
|
|
84
|
-
|
|
85
|
-
## 4.5 归档源视频(必须在 §5 拆音频之前)
|
|
86
|
-
|
|
87
|
-
把用户上传的视频复制到 workspace,后续步骤全部从复制好的副本走(把 `<external_id>` 换成 §2 你生成的那个 id,**写全**,别用 `$EXTERNAL_ID`)。两条单命令:
|
|
88
|
-
|
|
89
|
-
```bash
|
|
90
|
-
mkdir -p ~/digital-human/training/dh-a7f2k9m1
|
|
91
|
-
```
|
|
92
|
-
```bash
|
|
93
|
-
cp "<用户上传的视频路径>" ~/digital-human/training/dh-a7f2k9m1/source-video.mp4
|
|
94
|
-
```
|
|
95
|
-
|
|
96
|
-
**为什么必须先复制**:
|
|
97
|
-
- Claude Code session 里 attach 的视频通常在临时目录(`/tmp/...` 或类似),**session 结束就消失**
|
|
98
|
-
- 后续步骤(拆音频、ffprobe、onboard 上传)都依赖这个文件存在,临时附件中途消失会全部失败
|
|
99
|
-
- 先 cp 到 workspace,等于把素材"焊死"在本地,后续就算 session 关了重启也能续做
|
|
100
|
-
|
|
101
|
-
**为什么用 `cp` 而不是 `ln`(hardlink)或 `mv`(移动)**:
|
|
102
|
-
- `mv` 删原文件 → 用户原始路径丢失,违和
|
|
103
|
-
- `ln` 跨 filesystem 会失败,而且加 LLM 出错面(决策详见 SPEC `2026-05-15-digital-human-segment-persistence.md` 决策 #13)
|
|
104
|
-
- `cp` 简单可靠,磁盘代价可接受(2-5 min 1080p 视频 ~ 50-200 MB,个人开发机能扛)
|
|
105
|
-
|
|
106
|
-
**用户原始路径不动**,只是我们这边存一份副本。
|
|
107
|
-
|
|
108
|
-
## 5. 提音频(从复制好的 source-video.mp4)
|
|
109
|
-
|
|
110
|
-
`gen avatar onboard` 接受 `<audio-file> <video-file>` 两个文件参数。从 §4.5 复制好的 `source-video.mp4` 提音频(**不再从用户原始路径提**,因为原始路径可能中途消失)。**一条命令、路径写全、不换行**(`\` 续行沙箱拦):
|
|
111
|
-
|
|
112
|
-
```bash
|
|
113
|
-
ffmpeg -i ~/digital-human/training/dh-a7f2k9m1/source-video.mp4 -vn -acodec pcm_s16le -ar 44100 -ac 1 -t 90 ~/digital-human/training/dh-a7f2k9m1/source-audio.wav
|
|
114
|
-
```
|
|
115
|
-
|
|
116
|
-
取前 90 秒单声道 WAV,够 voice clone。路径固定 = `training/<external_id>/source-audio.wav`,不用 `mktemp`(单分身一个 audio,无并发覆盖问题)。
|
|
117
|
-
|
|
118
|
-
## 6. 触发训练(onboard)
|
|
119
|
-
|
|
120
|
-
**一条命令、路径 + id 写全、不换行**(`\` 续行沙箱拦;`--clean-bg` / `--accept-baked-bg` 按 §4 决策二选一,不能省):
|
|
121
|
-
|
|
122
|
-
```bash
|
|
123
|
-
gen avatar onboard ~/digital-human/training/dh-a7f2k9m1/source-audio.wav ~/digital-human/training/dh-a7f2k9m1/source-video.mp4 --external-id dh-a7f2k9m1 --clean-bg
|
|
124
|
-
```
|
|
125
|
-
|
|
126
|
-
> **不要传 `--display-name`** — 后端 CLI [`doctor.ts:36`](packages/cli/src/commands/doctor.ts) 不支持这个 flag,commander 会拒绝未知选项报错。display_name 唯一存在于 `~/digital-human/avatars.md`,backend 完全不感知。
|
|
127
|
-
|
|
128
|
-
返回 JSON:
|
|
129
|
-
```json
|
|
130
|
-
{ "external_id": "dh-a7f2k9m1", "voice_task_id": "<uuid>", "avatar_task_id": "<uuid>", "status": "pending" }
|
|
131
|
-
```
|
|
132
|
-
|
|
133
|
-
如返回唯一冲突错误 → 回到 §2 重新生成 external_id,重试(最多 3 次)。
|
|
134
|
-
|
|
135
|
-
## 6.5 写 train-meta.md(归档训练元数据)
|
|
136
|
-
|
|
137
|
-
onboard 提交成功后,从它 stdout 的 JSON 里读 `voice_task_id` / `avatar_task_id`(自己读,别 `$()`/`jq`)。用 **Write tool** 写训练元数据到 `~/digital-human/training/<external_id>/train-meta.md`(**别用 `cat <<EOF` heredoc**——沙箱拦)。所有值(名字 / external_id / 两个 task_id / 背景决策 / 今天日期 / §3 ffprobe 的时长分辨率)你手头都有,**内联**进模板:
|
|
138
|
-
|
|
139
|
-
```markdown
|
|
140
|
-
# Training: 张医生 (external_id: dh-a7f2k9m1)
|
|
141
|
-
|
|
142
|
-
| 字段 | 值 |
|
|
143
|
-
|---|---|
|
|
144
|
-
| display_name | 张医生 |
|
|
145
|
-
| external_id | dh-a7f2k9m1 |
|
|
146
|
-
| 训练时间 | 2026-06-01 |
|
|
147
|
-
| voice_task_id | <onboard 输出的 voice_task_id> |
|
|
148
|
-
| avatar_task_id | <onboard 输出的 avatar_task_id> |
|
|
149
|
-
| 背景决策 | --clean-bg(或 --accept-baked-bg) |
|
|
150
|
-
| 源视频时长 | (从 §3 ffprobe 输出取) |
|
|
151
|
-
| 源视频分辨率 | (同上) |
|
|
152
|
-
| preview_image_url | (训完 §8 拉到后回填这一行) |
|
|
153
|
-
```
|
|
154
|
-
|
|
155
|
-
(日期用你 LLM 知道的当天真实日期,别 `$(date)`。)
|
|
156
|
-
|
|
157
|
-
**为什么单独写文件**:
|
|
158
|
-
- avatars.md 是分身索引(用户看 + LLM 用),只放最关键字段(名字 / external_id / 训练时间 / preview)
|
|
159
|
-
- train-meta.md 是分身**完整训练记录**,含 task_ids / 背景决策 / 源视频 specs 等用户不关心但**重训时关键**的信息
|
|
160
|
-
|
|
161
|
-
## 7. Poll 直到完成
|
|
162
|
-
|
|
163
|
-
**沙箱里没有 `while` 循环**——靠 LLM **逐次发单条命令**轮询(把 task_id 内联进命令,从输出 JSON 自己读 `status`,别 `$()`/`jq`)。每轮两条:
|
|
164
|
-
|
|
165
|
-
```bash
|
|
166
|
-
gen task get <voice_task_id>
|
|
167
|
-
```
|
|
168
|
-
```bash
|
|
169
|
-
gen task get <avatar_task_id>
|
|
170
|
-
```
|
|
171
|
-
|
|
172
|
-
- 读两条输出里的 `status`。两个都 `completed` → 进 §8;任一 `failed` → 看 `error_message` 处理(见下)。
|
|
173
|
-
- 都还在 `pending`/`processing` → 等一会再来一轮。可选单条 `sleep 15`(**别** `sleep && gen ...` 串、别写 `while`),然后重发上面两条;或直接隔一轮再查。
|
|
174
|
-
- **上限 ~30 min**(avatar train 慢边界):LLM 自己记开始时间,超时仍未完成 → 告诉用户"训练超时,稍后用'X 训好了吗'再查"(state 不丢,§profile 可续查),不要无限轮询。
|
|
175
|
-
|
|
176
|
-
> **Fallback**:`gen task get` 不可用时,单条 curl 查后端(token / 域名内联,**别管道接 `| jq`**,直接看返回 JSON):
|
|
177
|
-
> ```bash
|
|
178
|
-
> curl -s -H "Authorization: Bearer <token>" "<generation-api>/api/task/<task_id>"
|
|
179
|
-
> ```
|
|
180
|
-
|
|
181
|
-
期望耗时:
|
|
182
|
-
- voice clone:30s - 2 min
|
|
183
|
-
- avatar train:5 - 30 min
|
|
184
|
-
- spec §13.2:trained look 在训练完成后还有短期 race(几分钟到 1 小时);**onboard 阶段不阻塞**
|
|
185
|
-
|
|
186
|
-
任一 fail → 看 `error_message`:
|
|
187
|
-
- voice clone fail → 大概率音频质量(噪声 / 多人混杂)→ 提醒用户重传清晰音频
|
|
188
|
-
- avatar train fail → 视频问题(时长 / 分辨率 / 内容)→ 给具体 error 让用户判断
|
|
189
|
-
|
|
190
|
-
## 8. 拉 profile 拿 preview
|
|
191
|
-
|
|
192
|
-
两个 task 都 completed 后(external_id 内联):
|
|
193
|
-
|
|
194
|
-
```bash
|
|
195
|
-
gen avatar profile dh-a7f2k9m1
|
|
196
|
-
```
|
|
197
|
-
|
|
198
|
-
返回(关键字段):
|
|
199
|
-
```json
|
|
200
|
-
{
|
|
201
|
-
"external_id": "dh-a7f2k9m1",
|
|
202
|
-
"heygen_voice_id": "f7a8ea9f...",
|
|
203
|
-
"heygen_avatar_look_ids": ["04182555..."],
|
|
204
|
-
"voice_status": "complete",
|
|
205
|
-
"avatar_status": "completed",
|
|
206
|
-
"preview_image_url": "https://...preview.jpg"
|
|
207
|
-
}
|
|
208
|
-
```
|
|
209
|
-
|
|
210
|
-
> DB 字段名 `heygen_*` 是历史命名,代码层不动。**LLM 给用户报告时不冒泡 UUID**(只展示名字 + preview)。
|
|
211
|
-
|
|
212
|
-
**回填 `train-meta.md`**:把 §6.5 写的 train-meta.md 里 `preview_image_url` 那行补上真值(profile 拿到的 URL)。
|
|
213
|
-
|
|
214
|
-
(可选)下载 preview 缓存到本地(url + 路径内联,单条命令):
|
|
215
|
-
```bash
|
|
216
|
-
curl -s -o ~/digital-human/training/dh-a7f2k9m1/preview.jpg "<preview_image_url>"
|
|
217
|
-
```
|
|
218
|
-
加快"列我的分身"渲染速度,避免每次都打外网。
|
|
219
|
-
|
|
220
|
-
## 9. 追加一行到 `avatars.md`
|
|
221
|
-
|
|
222
|
-
用 **Read + Edit tool** 往 `~/digital-human/avatars.md` 末尾追加一行(别用 `echo >>`)。日期用你 LLM 知道的**当天真实日期**(别 `$(date)`,别凭印象):
|
|
223
|
-
|
|
224
|
-
```
|
|
225
|
-
| <用户给的名字> | <external_id> | <TODAY> | <preview_image_url> |
|
|
226
|
-
```
|
|
227
|
-
|
|
228
|
-
## 10. 给用户报告
|
|
229
|
-
|
|
230
|
-
> "✅ 张医生 训练完成 [preview 图]
|
|
231
|
-
> 训练素材已存档到 `~/digital-human/training/张医生/`,以后想重训不用再上传视频。
|
|
232
|
-
> 以后说'用张医生做视频说...'即可。"
|
|
233
|
-
|
|
234
|
-
**不输出** voice_id / avatar_id / external_id(UUID 全部不冒泡)。
|
|
235
|
-
|
|
236
|
-
---
|
|
237
|
-
|
|
238
|
-
## Edge cases
|
|
239
|
-
|
|
240
|
-
### 1. 用户没给视频但说"训练 X"
|
|
241
|
-
|
|
242
|
-
反问:"X 的视频附件没看到,把视频发我(2-5 min,正面拍摄,干净背景)。"
|
|
243
|
-
**不要** 凭印象搜索 X 的公开视频去训(法律 + 同意书风险)。
|
|
244
|
-
|
|
245
|
-
### 2. 用户上传多个视频
|
|
246
|
-
|
|
247
|
-
主动列出最近上传的视频文件(文件名 + 时长),**让用户选哪一个用于训练**。不要假设最后一个 = 训练用。
|
|
248
|
-
|
|
249
|
-
### 3. 用户给了视频但意图模糊
|
|
250
|
-
|
|
251
|
-
视频可能是"复刻这条视频" / "把这条视频翻译" / "训分身"。如果不明确是后者,**不要触发本 skill**。问一句:
|
|
252
|
-
> "你想要 (a) 用这条视频训练 X 的数字分身(以后用分身做新视频),还是 (b) 复刻这条视频做产品宣传 / (c) 把视频翻译成其他语言?"
|
|
253
|
-
|
|
254
|
-
分别走 digital-human / video-gen / video-gen 视频翻译。
|
|
255
|
-
|
|
256
|
-
### 4. 用户中途改主意
|
|
257
|
-
|
|
258
|
-
- 想换视频 → 旧 task 让它跑完(训练资源已扣,不阻塞);用户拿新参数重新走 §1-§7
|
|
259
|
-
- 想取消 → `gen task cancel <task_id>` 各自 cancel(尽力而为,上游可能已 in-flight 不可中止)
|
|
260
|
-
|
|
261
|
-
---
|
|
262
|
-
|
|
263
|
-
## Common Mistakes
|
|
264
|
-
|
|
265
|
-
| ❌ Mistake | ✅ 正确做法 | 为什么 |
|
|
266
|
-
|---|---|---|
|
|
267
|
-
| silent 默认 `--clean-bg`(§4) | 用户三选一 → 显式 `--clean-bg` 或 `--accept-baked-bg` | 错判脏背景为干净 → baked 进 avatar look,后续 video 永远换不掉背景(spec §13.4) |
|
|
268
|
-
| 抽帧用图像识别判断背景(§4) | 直接问用户三选一 | 视觉判断不稳定 + 浪费 token + 用户答更准 |
|
|
269
|
-
| 让用户起 ASCII slug 当名字(§1) | 用户起的就是友好名字,external_id 由 LLM 内部生成 | 让用户记英文 slug 反人性;workspace 文件已经隔离了 ASCII 约束 |
|
|
270
|
-
| 给 `gen avatar onboard` 传 `--display-name`(§6) | 不要传,commander 会拒未知选项报错 | 后端 CLI 没这个 flag([doctor.ts:36](packages/cli/src/commands/doctor.ts));display_name 只在 workspace markdown |
|
|
271
|
-
| 凭印象搜公开视频去训(§Edge 1) | 反问用户上传视频 | 法律 + 同意书风险,无授权训练他人形象 |
|
|
272
|
-
| 假设最后一个上传的视频 = 训练用(§Edge 2) | 列出所有让用户选 | 错训不该训的人 |
|
|
273
|
-
| 视频含糊意图直接触发 skill(§Edge 3) | 三岔反问 (a) 训分身 / (b) 复刻 / (c) 翻译 | 错走训练流程浪费训练配额(训练成本 > 复刻) |
|
|
274
|
-
| 没视频但有意图就调 onboard | 反问"把视频发我",并明确质量要求 | onboard 没视频会立即失败,白调一次 |
|
|
275
|
-
| 训完报告里写 voice_id / avatar_id UUID | 只展示名字 + preview | UUID 不冒泡是核心 UX 原则 |
|
|
276
|
-
| 跳过 §9 写 avatars.md | 训完必须写一行 | 不写下次会话用户说"用张医生"找不到,得记 dh-* slug |
|