# 生图链路重构交接文档

> 给后续 AI 开发者的实施清单。当前代码的整体骨架已搭好（模板、Pack、Manifest、Seedance、GPT provider），但**一致性机制是假的**，需要按本文档重构。
>
> 路径：`/Users/kangwan/Projects/code/20260518-ai-toy-patent-workflow/HANDOFF_IMAGE_PIPELINE.md`

---

## 0. 一句话目标

让"上传图 + 风格 → 意向图 → 选中主方案 → 专利包 → 配件包 → 生产包 → 宣发包 → 视频"的整条链路里，**每张图都基于上游锚图生成、每张图都能单独重做且不脱链**。专利申请要求前后一致，配件六视图也必须自成体系。

---

## 1. 用户期望的目标流程

```
[上传图（单张/多张） + 选风格（风格库可视化）]
   ↓
批量生图（4/8/12 张候选）
   ↓
九宫格快筛 → 选中主方案
   ↓
锁定 CharacterSpec（角色基线）+ 生成 L1 净化锚图
   ↓
按顺序生成（每步可单独重做，但参考链不能脱）：
   ├─ 专利包（六视图 + 立体图 + 局部图）
   ├─ 配件包（先识别配件 → 每件孤立锚图 → 每件 6 视图 → 组合图）
   ├─ 生产包（尺寸/材料/拆件/包装）
   ├─ 宣发包（白底/场景/卖点/详情页）
   └─ Seedance 视频（用宣发白底图当锚）
```

**核心约束**：
1. 风格库要可视化（缩略图代表）+ 内容完整（lighting/composition/material/negative）
2. 每张图都有明确的上游 anchor，参考链不能跨级
3. 单张重做必须沿用同一个 anchor
4. 配件六视图必须基于配件孤立锚图，不是娃娃源图
5. 视频参考宣发白底图，不是意向图

---

## 2. 现状的 6 个关键 Gap

### Gap 1：参考图根本没传给模型（最严重）

**位置**：`src/lib/providers.ts:62-65`

```typescript
const refHint = opts.refImages?.length
  ? `\n参考图 URL，用于保持角色一致：\n${opts.refImages.join('\n')}`
  : '';
```

**问题**：把参考图 URL 当文本拼在 prompt 末尾发给 `/images/generations`。这个端点根本不读图，模型只看到一串文本 URL，**等于没参考**。

**真正的图生图**必须走 `/images/edits` + multipart 上传图像字节，或者用 `/responses` + vision input 让 GPT 先描述再二次生图。

### Gap 2：风格库太薄、看不到样子

**位置**：`src/components/PromptPanel.tsx:5-12`

只有 6 个文字按钮：`毛绒玩偶 / 机甲风 / 可爱萌系 / 专利蓝图 / 赛博朋克 / 极简`。

**问题**：
- 没有代表缩略图（用户看不到"这个风格长什么样"）
- 没有完整的 style block（lighting、composition、color palette、material hint、negative prompt）
- 只是简单拼成 `prompt + ", style: 机甲风"`

### Gap 3：所有后续图都参考"最初那张意向图"，逐级漂移

**位置**：`src/lib/packGenerator.ts:144`

```typescript
const prompt = renderPrompt(template.promptTemplate, characterSpec, opts.sourceImage.url);
```

**问题**：专利右视图、宣发场景图、配件六视图……全部参考同一张意向图。

**正确做法 — 锚图链**：

```
L0 锚图 = 用户选中的意向图（可能还有背景、不够干净）
L1 锚图 = L0 净化后的白底正面图（CharacterSpec 锁定时生成）
L2 锚图 = 各包的首图（patent_front / acc_inventory / mkt_white_front）
L3 节点 = pack 内其他图都参考自己包的 L2
```

现在所有 L2/L3 都跨过 L1 直接参考 L0，**6-30 张图相互之间没有锚定，越生越漂**。

### Gap 4：配件链路是假的

**位置**：`src/lib/packGenerator.ts:36-37`

```typescript
if (prompt.includes('机甲')) accessories.unshift('机甲头盔');
if (prompt.includes('M logo')) accessories.unshift('胸前 M 标识');
```

**问题**：
- 配件清单是**关键词硬匹配**，换个 prompt 就识别不出
- 配件六视图都把"主娃娃源图"当 ref，配件本身轮廓、材质会被娃娃身体盖住

**正确做法**：
1. 锁定主方案后，调 GPT Vision 解析 L1 锚图 → 输出 `[{name, isolatedDescription, bbox}]`
2. 为每个配件生成**配件孤立锚图**（白底、单件、无娃娃）
3. 配件六视图基于自己的孤立锚图
4. 最后单独生成"配件+娃娃组合图"

### Gap 5：单张重做会脱链

代码里只有 `generateAssetPack`（整包重做），**没有单张重做接口**。

如果右视图不满意要单独重做，需要：
- 知道这张图的锚图是谁（同包主视图）
- 锚图必须存在且未变
- 可选传 `userRefinement` 文本补充

需要新增 API `POST /api/assets/[assetId]/regenerate`，并在 `ToyAsset` 类型上加 `anchorAssetId` 字段。

### Gap 6：视频参考也不一致

**位置**：`src/app/page.tsx:161`

```typescript
imageUrl: image.url  // 还是那张原始意向图
```

**问题**：视频里的玩具和已定稿的电商图长得不一样。

**正确做法**：视频参考宣发白底主图（`mkt_white_front`），如果未生成就退到专利主图。

---

## 3. 实施方案（按依赖顺序）

### 阶段 1：真图生图链路（最高优先级，1+2+3+5 是最小可用版本）

#### 1.1 新增 `generateGptImageEdit`（providers.ts）

```typescript
export async function generateGptImageEdit(opts: {
  prompt: string;
  anchorImage: Buffer | string;   // 真实图片字节或本地路径
  maskImage?: Buffer;             // 可选 mask（局部重绘）
  size?: '1024x1024' | '1024x1536' | '1536x1024';
}): Promise<GenImage>
```

实现要点：
- 走 `https://api.openai.com/v1/images/edits`
- `multipart/form-data`：`image` (Buffer)、`prompt`、`model=gpt-image-1`、`size`
- **必须传图字节而不是 URL**
- 返回 `b64_json` → 解码为 data URL

保留现有 `generateGptImages` 用于 **L0 意向图阶段**（无锚图，纯文本生图）。

#### 1.2 数据模型扩展（types.ts）

```typescript
export type ToyAsset = {
  // ...existing
  anchorAssetId?: string;       // 上游锚图 asset id
  anchorImageUrl?: string;       // 解析后的锚图实际 URL
  derivationLevel: 0 | 1 | 2 | 3;
};

export type CharacterSpec = {
  // ...existing
  cleanReferenceImageUrl?: string;  // L1 净化锚图（白底正面）
};

export type AssetTemplate = {
  // ...existing
  anchorTemplateId?: string;    // 显式指明上游锚图模板
};
```

#### 1.3 模板加 anchorTemplateId（templates.ts）

```typescript
{ id: 'patent_front', anchorTemplateId: undefined, ... }       // 用 L1 锚图
{ id: 'patent_back', anchorTemplateId: 'patent_front', ... }   // 用包内主图
{ id: 'patent_left', anchorTemplateId: 'patent_front', ... }
// 配件同理
{ id: 'acc_inventory_sheet', anchorTemplateId: undefined, ... } // 用 L1 锚图
{ id: 'acc_front', anchorTemplateId: 'acc_inventory_sheet', ... }
{ id: 'acc_back', anchorTemplateId: 'acc_inventory_sheet', ... }
```

#### 1.4 新增 API：净化锚图

```
POST /api/character/cleanup
```

逻辑：
- 输入：`sessionId + imageId`
- 调 `generateGptImageEdit`，prompt = "保持角色完全一致，把背景换成纯白色，产品居中，无任何文字水印，光线均匀"
- 输出图 URL 写回 `session.characterSpec.cleanReferenceImageUrl`

#### 1.5 改造 generateAssetPack（packGenerator.ts）

```typescript
async function resolveAnchorImage(template, packAssets, characterSpec) {
  if (!template.anchorTemplateId) {
    // 用 L1 净化锚图，没有则退到 L0
    return characterSpec.cleanReferenceImageUrl ?? characterSpec.sourceImageUrl;
  }
  const upstream = packAssets.find(a => a.templateId === template.anchorTemplateId);
  if (!upstream) throw new Error(`anchor ${template.anchorTemplateId} not generated yet`);
  return upstream.url;
}
```

按模板拓扑顺序生成（先无 anchor 的，再依次）：
1. 第一张 → 走 `generateGptImageEdit(prompt, L1Buffer)`
2. 其它张 → 走 `generateGptImageEdit(prompt, 同包首图 Buffer)`

需要新增工具函数：从 URL（如 `/api/img/packs/xxx.png`）读回 Buffer，供 multipart 上传。

### 阶段 2：单张重做

#### 2.1 新增 API

```
POST /api/assets/[assetId]/regenerate
Body: { sessionId, userRefinement?: string }
```

逻辑：
- 找到这个 asset 在哪个 pack
- 解析它的 anchor（按 template.anchorTemplateId）
- 走 `generateGptImageEdit`，prompt 末尾追加 `userRefinement`
- 替换原 asset，保留 id 不变
- 更新 session JSON

#### 2.2 UI

`PackPanel.tsx` 里每个 `AssetRow` 加"重做"按钮和"refinement"输入框。

### 阶段 3：风格库可视化

#### 3.1 新增 `src/lib/styles.ts`

```typescript
export type StylePreset = {
  id: string;
  label: string;
  thumbnailUrl: string;          // /styles/plush-classic.png
  promptBlock: string;            // 完整 style prompt 段
  negativePrompt: string;
  recommendedPalette: string[];
  recommendedMaterials: string[];
  goodFor: PackKind[];
};
```

至少 12-16 个预设，每个对应一张 256×256 缩略图放 `public/styles/`。

建议初始风格列表：
- 经典毛绒、长毛毛绒、超柔短绒、卡通圆胖
- 机甲风、赛博朋克、未来科技
- 可爱萌系、治愈系、Kuromi 暗黑可爱
- 复古玩具、迪士尼风、皮克斯风
- 黏土材质、绒线编织、3D 渲染、专利蓝图

#### 3.2 改 PromptPanel

风格选择从 6 个按钮 → 4 列网格的图卡（缩略图 + 名称 + 适用包 tag）。

风格切换时：
- `promptBlock` 合并到生图 prompt
- `negativePrompt` 单独传给 provider（GPT image edit 支持 negative）
- `recommendedPalette/Materials` 自动填到 CharacterSpec 默认值

### 阶段 4：配件 Vision 识别

#### 4.1 新增 `src/lib/accessoryDetector.ts`

```typescript
export type DetectedAccessory = {
  id: string;
  name: string;
  isolatedDescription: string;
  recommendedColors: string[];
  approximateBBox?: { x: number; y: number; w: number; h: number };
};

export async function detectAccessories(anchorImageUrl: string): Promise<DetectedAccessory[]>
```

实现：
- 走 `/responses` 端点 + vision input（GPT-4.1-vision 或 gpt-5.5 多模态）
- 把 L1 锚图作为图像输入
- prompt = "识别图中玩具身上所有独立配件，输出 JSON 数组，每项包含 name、isolatedDescription、recommendedColors。不包括玩具主体本身。"
- 严格 JSON 输出

#### 4.2 配件包生成流程重构

```
1. 调 detectAccessories(L1_anchor) → [帽子, 背包, 标牌, ...]
2. 把每个 accessory 加入 session.characterSpec.accessoriesDetected[]
3. 为每个 accessory 生成 isolated_anchor（白底、孤立、单件）
   - 走 generateGptImageEdit(L1锚图, "只保留 ${name}，其它部分擦除，白底，居中")
4. 每个 accessory 的 6 视图（front/back/left/right/top/bottom/perspective）
   都基于自己的 isolated_anchor
5. 最后生成 with_doll_assembly（参考 L1锚图 + isolated_anchors 组合）
```

数据模型加：

```typescript
export type AccessoryGroup = {
  id: string;
  name: string;
  isolatedAnchorUrl: string;
  views: ToyAsset[];  // 6+ 视图
};

export type AssetPack = {
  // ...existing
  accessoryGroups?: AccessoryGroup[];  // 仅 kind === 'accessories' 用
};
```

### 阶段 5：视频参考一致性

#### 5.1 改 `handleGenerateVideo`（page.tsx）

```typescript
const mktFront = packs
  .find(p => p.kind === 'marketing')?.assets
  .find(a => a.templateId === 'mkt_white_front');
const patentFront = packs
  .find(p => p.kind === 'patent')?.assets
  .find(a => a.templateId === 'patent_front');
const videoAnchor =
  mktFront?.url ??
  patentFront?.url ??
  session.characterSpec?.cleanReferenceImageUrl ??
  image.url;
```

UI 上视频按钮旁边显示「参考：宣发白底图 / 专利主图 / 意向图」，用户清楚视频基于哪张。

如果宣发主图未生成，按钮可选「强制要求先生成宣发主图」或「使用专利主图」。

### 阶段 6：UI 锚图链可视化

`PackPanel` 顶部"角色锁定 & 资产清单"卡片下方加一个可视化树：

```
L0 意向图 ──→ L1 白底锚图 ──┬──→ 专利主图 ──→ 专利右视图 / 左视图 / ...
                              ├──→ 配件锚图 ──→ 帽子 6 视图 / 背包 6 视图
                              ├──→ 宣发白底图 ──→ 视频任务
                              └──→ 生产主图 ──→ 尺寸图 / 拆件图
```

让用户一眼看到每张图沿用哪张作为基准，重做某个节点会影响下游哪些。

可以用简单的 flexbox 树或 SVG 连线。

---

## 4. 实施 Checklist

### 最小可用版本（先做这 4 项）

- [ ] 1.1 新增 `generateGptImageEdit`（multipart upload）
- [ ] 1.2 数据模型加 `anchorAssetId / anchorImageUrl / derivationLevel / cleanReferenceImageUrl / anchorTemplateId`
- [ ] 1.3 模板加 `anchorTemplateId`
- [ ] 1.5 `generateAssetPack` 按拓扑生成、用真图生图
- [ ] 1.4 `POST /api/character/cleanup` 生成 L1 锚图

### 单张重做

- [ ] 2.1 `POST /api/assets/[assetId]/regenerate`
- [ ] 2.2 UI 加重做按钮 + refinement 输入框

### 风格库

- [ ] 3.1 `src/lib/styles.ts` + 12-16 张 thumbnails（`public/styles/`）
- [ ] 3.2 `PromptPanel` 改成图卡选择器

### 配件 Vision

- [ ] 4.1 `accessoryDetector.ts` 用 GPT Vision
- [ ] 4.2 配件包改成「识别 → 孤立锚图 → 6 视图 → 组合图」

### 视频和可视化

- [ ] 5.1 视频参考切到宣发主图
- [ ] 6.1 `PackPanel` 加锚图链可视化

---

## 5. 关键文件清单

| 用途 | 路径 |
|---|---|
| GPT provider | `src/lib/providers.ts` |
| 视频 provider | `src/lib/videoProviders.ts` |
| 包生成主逻辑 | `src/lib/packGenerator.ts` |
| 模板定义 | `src/lib/templates.ts` |
| 类型定义 | `src/lib/types.ts` |
| 存储 | `src/lib/storage.ts` |
| 主页 | `src/app/page.tsx` |
| 输入面板 | `src/components/PromptPanel.tsx` |
| 九宫格 | `src/components/ResultGrid.tsx` |
| 资产面板 | `src/components/PackPanel.tsx` |
| 生图 API | `src/app/api/generate/route.ts` |
| 模板查询 API | `src/app/api/templates/route.ts` |
| 角色锁定 API | `src/app/api/character/lock/route.ts` |
| 单包生成 API | `src/app/api/packs/generate/route.ts` |
| 全包生成 API | `src/app/api/packs/generate-all/route.ts` |
| 视频生成 API | `src/app/api/video/generate/route.ts` |

---

## 6. 模型/环境变量约定

- 文本 / 结构化 / Vision：`OPENAI_API_KEY` + `GPT_TEXT_MODEL`（默认 `gpt-5.5`）
- 图像生成 / 编辑：`OPENAI_API_KEY` + `GPT_IMAGE_MODEL`（默认 `gpt-image-2`，edits 端点可能要 `gpt-image-1`，按 OpenAI 实际支持调整）
- 视频：`SEEDANCE_API_KEY` + `SEEDANCE_MODEL`（默认 `seedance-1-0-pro`）

**重要**：本项目"文本 / 图片统一走 GPT 最高规格，视频固定 Seedance"是硬约束。不要引入其他供应商。

---

## 7. 验收标准

完成最小可用版本后，应该满足：

1. 选中意向图 → 锁定 → 自动生成 L1 净化锚图
2. 生成专利包，主视图基于 L1，其它五视图基于专利主图（实际传图，不是文本 URL）
3. 重做任意一张图，UI 显示它的 anchor 是谁，并能单独重做
4. 风格切换有可视化预览
5. 配件包能自动识别玩具上有几个配件，分别生成 6 视图
6. Seedance 视频参考用的是宣发白底图

实测时拿一张复杂玩具图（带帽子、背包、标牌）跑全链路，所有图角色一致、配件清晰、视频与电商图一致。

---

## 8. 上传图入口的三种模式（二创 / 复刻 / 复刻+补全）

### 8.1 场景

用户的实际使用场景不止"从一句话开始"，还有：

- **已经有玩具/IP 图**（手稿、成品照、参考海报、其他设计师的稿）
- **只缺某些视角**（手上有正面图，要补侧/背/俯视）
- **想做风格化变体**（已有原型，但要换成赛博朋克版/绒线编织版/迪士尼风）

当前 `PromptPanel` 是 prompt-first，上传图只是被丢进 prompt 末尾当文本提示（见 Gap 1），**完全没真正参与生成**。需要重新设计上传图的语义。

### 8.2 三种入口模式

#### Mode A · 二创（Remix）

```
用户：[上传图 1-4 张] + [选风格 + 描述变化方向]
系统：保留核心识别（轮廓/五官/品牌符号），按用户要求做风格/材质/配色变化
输出：4-12 张候选变体
后续：选中 → Lock → 正常 Pack 流程
```

技术实现：
- 走 `/images/edits` multipart，传第 1 张为主参考图
- prompt 拼接：`{风格 promptBlock} + {用户变化描述} + 强制 negative：不改变身体比例、五官相对位置、品牌符号、配件轮廓`
- `n=4/8/12` 生成多张候选

#### Mode B · 复刻（Replicate）

```
用户：[上传图 1 张]，标记为"主体图"
系统：跳过批量生图，直接以这张图作为 L0 锚图
       → 立刻做 L1 净化（白底、保真）
       → 调 Vision 识别配件、推断 CharacterSpec
       → 用户确认后进入 Pack
输出：完整专利/配件/生产/宣发包
```

适用场景：手上已有终稿原画，只需要把它"扩展"成完整素材包。

技术实现：
- 上传图后跳过 `/api/generate`，直接构造一个 `GenImage` 写入 session，status='selected'
- 自动触发 `/api/character/cleanup` 生成 L1
- 自动触发 `/api/character/lock`（CharacterSpec 让 Vision 推断，用户可编辑）
- 用户点"进入 Pack"才开始 pack 生成

#### Mode C · 复刻 + 补全（Extend）

```
用户：[上传图 1-4 张]，每张标记为"主体 / 同角色另一视角 / 配件孤立图"
系统：先把"主体"作为 L0
       已有视角直接占用对应 slot（如上传了"正面图" → 占用 patent_front）
       缺失的 slot 才调 API 生成
输出：节省 80% 算力，前后一致性最强
```

适用场景：用户已经手工画了正面+背面，要补侧视图+俯视图+配件六视图。

技术实现：
- 上传时弹出 tagging UI，让用户选每张图的"槽位"
- 后端在 `generateAssetPack` 前先合并预占的 slot
- 已占 slot 跳过 API 调用，直接复用上传图

### 8.3 UI 入口设计

`PromptPanel` 改成 3 个 tab：

```
┌─────────────────────────────────────────┐
│ [💡 想法]  [🎨 二创]  [📐 复刻]              │
├─────────────────────────────────────────┤
│ Mode A 二创 tab 内容：                    │
│   - 上传图（1-4 张，第 1 张为主参考）        │
│   - 风格选择（图卡，引用 §3 风格库）          │
│   - 变化方向描述（textarea）                │
│   - 数量 4/8/12                          │
│   - [生成变体]                            │
└─────────────────────────────────────────┘
```

Mode B 和 Mode C 共享一个 tab，差异在上传后的处理：
- 只传 1 张且不标记 → Mode B
- 传多张并标注槽位 → Mode C

### 8.4 上传图的元数据（共用）

每张上传图都要附带：

```typescript
export type UploadedImage = {
  id: string;
  url: string;                  // /api/img/uploads/xxx.png
  filename: string;
  uploadedAt: number;
  role: 'reference'              // 二创模式的灵感图
       | 'subject'               // 复刻模式的主体
       | 'view-front'            // 已有的视角，占用对应 slot
       | 'view-back'
       | 'view-left'
       | 'view-right'
       | 'view-top'
       | 'view-bottom'
       | 'accessory-isolated'    // 配件孤立图
       | 'accessory-named';      // 已命名的配件图
  accessoryName?: string;        // 仅 accessory-* 时有效
  needsCleanup: boolean;         // 是否需要先净化才能用
};
```

存储路径：`data/uploads/`。

### 8.5 新增 API

```
POST /api/uploads
  Body: multipart, image file + role + accessoryName?
  Resp: UploadedImage

POST /api/projects/from-upload
  Body: { uploadedImages: UploadedImage[], mode: 'remix' | 'replicate' | 'extend',
          remixPrompt?: string, styleId?: string, count?: number }
  Resp:
    - mode=remix: { sessionId, images } (4-12 变体)
    - mode=replicate: { sessionId, characterSpec, l1AnchorUrl }
    - mode=extend: { sessionId, characterSpec, l1AnchorUrl, preFilledSlots }

POST /api/character/lock-from-upload
  Body: { sessionId, subjectImageId }
  Resp: 调 Vision 推断 CharacterSpec
```

### 8.6 Vision 推断 CharacterSpec

Mode B 和 C 没有原始 prompt，需要 GPT Vision 看图推断。

`src/lib/providers.ts` 新增：

```typescript
export async function inferCharacterSpecFromImage(opts: {
  imageUrl: string;
  userHint?: string;       // 用户可选输入"这是我的 IP, 名字叫小桔"
}): Promise<CharacterSpec>
```

实现：
- 走 `/responses` + vision input
- prompt = "你是玩具产品经理，根据图片推断 CharacterSpec。严格 JSON，包含 name/oneLiner/speciesShape/bodyRatio/faceFeatures/colorPalette/materials/accessories/signatureElements/patentFocus/negativePrompt 字段"
- 如果 userHint 有值，name 用它，否则让 GPT 生成

### 8.7 复刻模式的 L1 净化注意事项

复刻模式比"从 prompt 开始"对净化要求更高：**不能改任何角色细节**。

`/api/character/cleanup` 在复刻模式下要使用更强的约束 prompt：

```
保持原图完全一致，仅做以下修改：
1. 把背景换成纯白色
2. 去除任何水印、文字、价格标签、网页 UI 元素
3. 居中并适当裁剪到正方形构图

绝对不要修改：
- 角色五官、表情、姿态
- 主体配色、材质、纹理
- 配件位置、轮廓、细节
- 任何品牌符号（如胸前 logo）

输出风格：商业产品图，柔和均匀打光，无阴影。
```

可以在 `cleanup` API 里加 `preserveLevel: 'strict' | 'normal'` 参数。

### 8.8 已有视角槽位的智能匹配（Mode C）

用户上传时标的 `role: view-front` 等，直接 1:1 占用专利包/配件包的对应模板：

| 上传 role | 占用模板 ID |
|---|---|
| `view-front` | `patent_front` + `acc_front`（如是配件） |
| `view-back` | `patent_back` |
| `view-left` | `patent_left` |
| `view-right` | `patent_right` |
| `view-top` | `patent_top` |
| `view-bottom` | `patent_bottom` |
| `accessory-isolated` | 该配件的 `isolated_anchor` |

`generateAssetPack` 在生成前先检查 session.preFilledSlots，已占用的 template 直接复用上传图，不调 API。

### 8.9 合规和版权提示

UI 上传图区域要醒目提示：

> ⚠️ 上传图必须为你拥有或有合法授权使用的素材。请勿上传迪士尼、三丽鸥、泡泡玛特等已注册 IP 的图像；生成结果用于专利申请时，需自行确认不与他人在先权利冲突。

后端可选做基础检测：
- 调 GPT Vision 看图，若识别为已知品牌 IP（"Hello Kitty"、"Mickey Mouse"、"Labubu"）→ 阻止上传并给出明确警告
- 这一步初版可不做，但要在 UI 留位

### 8.10 实施 Checklist 增量

在 §4 Checklist 之外，额外加：

- [x] 8.A 上传 API + 存储 `data/uploads/`
- [x] 8.B `PromptPanel` 改 3-tab：想法 / 二创 / 复刻
- [x] 8.C Mode A（二创）：multipart + `/images/edits` 批量变体
- [x] 8.D Mode B（复刻）：跳过批量生图，直接 lock + cleanup
- [x] 8.E `inferCharacterSpecFromImage` Vision 推断
- [ ] 8.F Mode C：上传图 role 标注 UI + `preFilledSlots` 合并
- [x] 8.G cleanup 加 `preserveLevel` 参数，复刻模式用 strict
- [x] 8.H 版权合规提示 UI

### 8.11 优先级建议

最实用顺序：

1. **Mode B 复刻**（最常用：用户已有玩具图想做素材包）
2. **Mode A 二创**（次常用：用户有概念图想做风格变体）
3. **Mode C 复刻+补全**（高级：用户有部分视图想补齐）

如果只做一个，先做 Mode B —— 它对"前后一致"的帮助最直接，相当于直接拿用户图当 L0 锚图，跳过最容易漂移的"prompt → 意向图"阶段。

---

## 9. 实例：上传一张 lookbook 整图的工作流

### 9.1 场景描述

用户拿到一张已经完整的商品 lookbook 图（如 MUSE MATE 街头潮玩公仔的 14 区块大图），里面已经包含核心形象、包装、三视图、细节、场景、配件、社媒图、专利六视图、产品信息等。这是 Mode C 复刻+补全的极端情况——**几乎所有 slot 都已经有素材**，只需要补少量缺失视角和细节。

### 9.2 上传图的内容分类（以 MUSE MATE lookbook 为例）

```
01. 核心形象      → 单只主角图
02. 包装展示      → 礼盒 + 配件平铺
03. 三视图        → Front / Side / Back
04. 细节展示      → 头部 / 滑板 / 卫衣特写 ×4
05. 场景展示      → 涂鸦墙 / 唱片店 / 滑板公园 / 书桌 / 车载 / 包挂 ×6
06. 配件展示      → 帽子 / 耳机 / 滑板 / 喷漆 / 卫衣 / 钥匙扣 / 编号卡 / 贴纸 ×8
07. 可替换造型    → 黑 / 灰 / 橙 / 绿 4 套服饰
08. 灯光效果      → 白光 / 暖光 2 张
09. 证书 + 编号卡 → 收藏卡
10. 社媒展示      → 明星种草 3 张
11. 系列款展示    → 6 个配色变体
12. 专利图纸      → 已完整的六视图
13. 产品信息      → ABS/PVC、高度 12cm、包装尺寸文字
14. 合作流程      → 流程图（非产品素材）
```

### 9.3 系统映射表

| Lookbook 区块 | 系统 slot | 数量 | 备注 |
|---|---|---|---|
| 01 核心形象 | L0 主体图 → `subject` role | 1 | 净化后做 L1 锚图 |
| 02 包装 | `mkt_packaging_render` + `prod_packaging_structure` | 2 | 切出 |
| 03 三视图 | `patent_front` / `patent_left` / `patent_back` | 3 | 直接占用 |
| 04 细节 | `patent_detail_face` + `patent_detail_accessory` + `mkt_detail_face` + `mkt_detail_material` | 4 | 切出 |
| 05 场景 | `mkt_scene_bedroom/desk/gift` + 新增「街头 / 车载 / 包挂」slot | 6 | 拓展模板 |
| 06 配件 | `acc_inventory_sheet` + 8 个配件孤立锚图 | 9 | 触发 8 个 AccessoryGroup |
| 07 服饰变体 | **新 slot：`variant_outfit`** | 4 | 拓展（系列变体） |
| 08 灯光变体 | **新 slot：`variant_lighting`** | 2 | 拓展 |
| 09 证书卡 | **新 slot：`cert_card`** | 1 | 收藏品需要 |
| 10 社媒 | `mkt_social_vertical` | 3 | 占用 |
| 11 系列款 | **新 slot：`series_lineup`** | 1 | 拓展 |
| 12 专利六视图 | `patent_front/back/left/right/top/bottom` | 6 | 完全占满 |
| 13 产品信息 | OCR 后填到 `text_production_brief` / `text_production_cmf` | - | 文字 slot |
| 14 合作流程 | 忽略 | - | 非素材 |

### 9.4 用户操作流程

```
1. 上传 lookbook 整图（role: 'lookbook-composite'）
2. 系统检测到合成图 → 弹出区块切割界面
   - Vision 识别"01."至"14."编号定位分区线
   - 用户可手动调整裁剪框
   - 每块标 role
3. 切完得到 30-40 张独立图，写入 data/uploads/
4. 系统按 role 自动分配 slot
5. 调 Vision 看 L0 + 三视图 + 配件清单 → 自动推断 CharacterSpec
6. 用户进入 PackPanel：
   - 已占用 slot 显示 ✓
   - 缺失 slot 显示「待补生成」
7. 用户决定一键补全 / 挑重要 slot 补全
```

### 9.5 算力节省

对这张 lookbook 来说：

| Pack | 全量生成需要 | 上传图已占 | 实际需补生成 |
|---|---|---|---|
| 专利包 | 12 张 | 7 张 | 5 张（右/上/下/立体×2） |
| 配件包 | 9 张（清单）+ 6×8 = 57 张 | 9 张（清单 + 各 1 视图） | ~48 张（每件还缺 5 视图 + 组合图） |
| 生产包 | 18 张 | 0 张（lookbook 没生产图） | 18 张全补 |
| 宣发包 | 22 张 | 11 张（KV/包装/场景/社媒） | 11 张 |
| **合计** | **≈118 张** | **≈40 张** | **≈82 张** |

省下约 **34% API 调用**。更重要的是：用户自己的图是最强 anchor，前后一致性最高。

### 9.6 需要新增的模板 / 数据结构

为支撑 lookbook 场景，建议扩展：

```typescript
// 新增 role 类型
export type UploadRole =
  | 'subject' | 'reference'
  | 'view-front' | 'view-back' | 'view-left' | 'view-right' | 'view-top' | 'view-bottom'
  | 'accessory-isolated' | 'accessory-named'
  | 'scene-bedroom' | 'scene-desk' | 'scene-gift'
  | 'scene-street' | 'scene-car' | 'scene-bag'    // 新增场景
  | 'detail-face' | 'detail-accessory' | 'detail-material'
  | 'social-vertical' | 'social-square'
  | 'packaging-overview' | 'packaging-structure'
  | 'variant-outfit' | 'variant-lighting'         // 新拓展
  | 'cert-card' | 'series-lineup'                 // 新拓展
  | 'lookbook-composite';                          // 整张 lookbook
```

新增模板（templates.ts 里追加）：

- `mkt_scene_street` / `mkt_scene_car` / `mkt_scene_bag`（场景包补 3 个）
- `variant_outfit_*` × 4（服饰变体包）
- `variant_lighting_white` / `variant_lighting_warm`（灯光变体）
- `cert_card`（收藏品类附件）
- `series_lineup`（系列陈列图）

新增 API：

```
POST /api/uploads/split-composite
  Body: { uploadedImageId, regions: Array<{ role, bbox, accessoryName? }> }
  Resp: { sessionId, splitImages: UploadedImage[] }
```

### 9.7 这个实例对实施顺序的影响

如果用户主要场景是"已有完整或半完整 lookbook"，那 §4 实施 Checklist 的优先级应该调整：

1. **优先做 §8 上传图模式（Mode B 复刻）**
2. 其次做 §1 锚图链
3. 再做 §9 区块切割 + role 标注 + slot 自动占用
4. 最后做风格库、Vision 配件识别等增强功能

因为 lookbook 用户根本不需要"从 prompt 生意向图"，他们要的是"把这套素材合理拆分填进系统，缺什么补什么"。

---

## 10. 完整 Agent 编排：从任意输入到完整 lookbook

### 10.1 目标

用户无论上传什么（一句话 / 单张主角图 / 完整 lookbook 大图 / 几张零碎参考图），系统都能自动跑到同一个终态：**一套完整的专利包 + 配件包 + 生产包 + 宣发包 + 视频任务 + 设计说明文字**，并显式区分「已占用」「AI 补生成」「需人工确认」三种状态。

### 10.2 三层 Agent 架构

```
┌──────────────────────────────────────────────────────────┐
│ Orchestrator Agent — 决策总指挥                              │
│  · 决定走哪条路径（Mode A/B/C）                                │
│  · 调度拓扑生成顺序                                            │
│  · 触发自检 & 重做                                             │
└──────────────────────────────────────────────────────────┘
          │                    │                    │
          ▼                    ▼                    ▼
┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐
│ Vision Analyst   │ │ Generation Worker│ │ Quality Checker  │
│  · 识图分类       │ │  · 调 GPT 生图    │ │  · 角色一致性     │
│  · 区块切割       │ │  · 调 Seedance   │ │  · 视角正确性     │
│  · 推断 Spec     │ │  · multipart 上传 │ │  · 风格统一       │
│  · 配件识别       │ │  · 锚图链解析     │ │  · 标红需重做      │
└──────────────────┘ └──────────────────┘ └──────────────────┘
```

实现层面：
- 三个 Agent 可以是同一个 GPT 模型不同 prompt
- 也可以分别用：`gpt-5.5-vision` 做识图、`gpt-image-2` 做生图、`gpt-5.5` 做质检
- 编排可以用 Vercel AI SDK / LangChain，**也可以纯 TypeScript 状态机**（推荐先用后者，可控性强）

### 10.3 完整流程状态机

```
┌────────────────────────────────────────────────────────────┐
│ STATE: idle                                                 │
│   用户输入：prompt? upload? both?                            │
└────────────────────────────────────────────────────────────┘
                       ▼
┌────────────────────────────────────────────────────────────┐
│ STATE: input-analysis                                       │
│   Vision Agent 看输入图（如有）                                │
│   输出 InputClassification：                                  │
│     { mode: 'prompt-only' | 'single-subject' | 'lookbook'   │
│            | 'multi-reference',                              │
│       blocksDetected?: BlockBBox[],                          │
│       detectedSubject?: SubjectGuess,                        │
│       detectedAccessories?: AccessoryGuess[],                │
│       confidence: 0..1 }                                     │
│   confidence < 0.7 → 询问用户                                 │
└────────────────────────────────────────────────────────────┘
                       ▼
            ┌──────────┴──────────┐
            ▼                     ▼
┌──────────────────┐   ┌──────────────────┐
│ Path A: prompt   │   │ Path B: image    │
│ → 批量生意向图     │   │  ┌──────────────┤
│ → 九宫格筛选      │   │  ▼              │
│ → 选中            │   │ Mode B 单图     │
│                   │   │ Mode C lookbook │
│                   │   │ Mode A multi-ref│
└──────────────────┘   └──────────────────┘
            └──────────┬──────────┘
                       ▼
┌────────────────────────────────────────────────────────────┐
│ STATE: anchor-preparation                                   │
│   · L0 = 选中图或主体图                                       │
│   · L1 = L0 经 cleanup 净化（preserveLevel=strict 复刻；      │
│           normal 二创可允许微调）                             │
│   · 若是 lookbook：先做区块切割 → slot 自动占用                │
└────────────────────────────────────────────────────────────┘
                       ▼
┌────────────────────────────────────────────────────────────┐
│ STATE: character-inference                                  │
│   Vision Agent 看 L1 + 已占用 slot                            │
│   输出 CharacterSpec（含 accessoriesDetected[]）              │
│   用户确认/编辑                                               │
└────────────────────────────────────────────────────────────┘
                       ▼
┌────────────────────────────────────────────────────────────┐
│ STATE: pack-generation（拓扑）                                │
│                                                              │
│   Wave 1（并行）：                                            │
│     · patent_front（用 L1）                                   │
│     · acc_inventory_sheet（用 L1）                            │
│     · mkt_white_front（用 L1）                                │
│                                                              │
│   Wave 2（并行）：                                            │
│     · patent_back/left/right/top/bottom（用 patent_front）    │
│     · 每个配件 accessory_isolated（用 acc_inventory）         │
│     · mkt_white_45/back（用 mkt_white_front）                 │
│     · prod_front_spec/back_spec/...（用 patent_front）        │
│                                                              │
│   Wave 3（并行）：                                            │
│     · patent_perspective_front/back / detail_*                │
│     · 每个配件的 6 视图（用对应 accessory_isolated）           │
│     · mkt_scene_* / mkt_detail_*                              │
│     · prod_material_board / color_board / part_breakdown      │
│                                                              │
│   Wave 4：                                                   │
│     · acc_with_doll_assembly（用 L1 + 各 isolated）           │
│     · mkt_size_lifestyle / longpage / packaging_render        │
│                                                              │
│   Wave 5：                                                   │
│     · 设计说明文字（GPT text，基于 CharacterSpec + 各 anchor）│
│     · 视频任务（用 mkt_white_front）                          │
└────────────────────────────────────────────────────────────┘
                       ▼
┌────────────────────────────────────────────────────────────┐
│ STATE: quality-check                                        │
│   Quality Checker Agent 看每张产物                            │
│   对比 anchor → 一致性评分                                    │
│   标记需重做的图（红色）                                       │
└────────────────────────────────────────────────────────────┘
                       ▼
┌────────────────────────────────────────────────────────────┐
│ STATE: review                                               │
│   用户在 PackPanel 看完整产出                                  │
│   每张图状态：✓ 已占用 / ✨ AI 生成 / 🔴 待重做 / ⚠ 需人工确认 │
│   一键重做标红的图 / 手动重做某张                              │
└────────────────────────────────────────────────────────────┘
                       ▼
┌────────────────────────────────────────────────────────────┐
│ STATE: export                                               │
│   导出 ZIP / PDF / manifest.json                             │
└────────────────────────────────────────────────────────────┘
```

### 10.4 关键 Agent 函数（不写代码，只列接口）

```typescript
// === Vision Analyst ===

inferInputClassification(uploads: UploadedImage[], prompt?: string): InputClassification
  // 决定走 prompt / single-subject / lookbook / multi-reference

detectLookbookBlocks(imageUrl: string): BlockBBox[]
  // 返回每个区块的 bbox + 自动建议 role

inferCharacterSpec(anchorImageUrl: string, userHint?: string): CharacterSpec
  // 看图推断完整 CharacterSpec

detectAccessories(anchorImageUrl: string): DetectedAccessory[]
  // 看图识别所有独立配件

// === Generation Worker ===

generateImage({ prompt, anchorBuffer, maskBuffer?, size, negative }): GenImage
  // 真图生图，multipart /images/edits

generateText({ prompt, format: 'json' | 'markdown' | 'plain' }): string
  // GPT text

generateVideo({ prompt, anchorImageUrl, duration, ratio }): VideoTask
  // Seedance

// === Quality Checker ===

assessConsistency({ generatedImage, anchorImage }): {
  score: 0..1,                  // 角色一致性评分
  drifts: string[],              // 漂移点说明
  needsRedo: boolean
}

assessViewAccuracy({ image, expectedView: 'front' | 'left' | ... }): {
  score: 0..1,
  notes: string[]
}

// === Orchestrator ===

planTopologicalGeneration(session): GenerationWave[]
  // 计算各 wave 依赖关系

runGenerationLoop(session): AsyncGenerator<ProgressEvent>
  // 跑完整生成 + 自检 + 重试
```

### 10.5 Topological Generation 详解

每个 `AssetTemplate` 加 `anchorTemplateId` 字段后，可以构建 DAG：

```typescript
type GenerationNode = {
  templateId: string;
  packKind: PackKind;
  dependsOn: string[];          // 上游 templateIds
  alreadySatisfied: boolean;    // 已由上传图占用？
};

function buildDAG(session): GenerationNode[]
function topologicalSort(nodes): GenerationNode[][]   // 分波次
```

**关键**：每个 Wave 内的节点可以**并行执行**（concurrency=4 或 8），跨 Wave 必须串行（因为下游需要上游图作为 anchor）。

实测一张主角图全量生成（专利 12 + 配件清单 9 + 配件六视图 48 + 生产 18 + 宣发 22 + 视频 5 = 114 张图）+ 16 段文字，按 5 Wave 并行（concurrency=4），用时大约：

- Wave 1：3 张并行 → ~10s
- Wave 2：~20 张并行（分 5 批）→ ~50s
- Wave 3：~70 张并行（分 18 批）→ ~3min
- Wave 4：~10 张 → ~25s
- Wave 5：文字 + 视频提交（视频是异步任务）→ ~30s

**总计约 5 分钟出完整 lookbook**（视频是异步任务还要等几分钟）。比串行生成（每张 3s × 114 = 5.7min 还要排队）快不少，且一致性最强。

### 10.6 Quality Check 的具体策略

让 Vision Agent 做 4 项检查：

1. **角色一致性**：把生成图和 L1 锚图拼成一张图，问 GPT "这两张是同一个角色吗？打分 0-1，列出差异"
2. **视角正确性**：问 "这张图是正面/左视图/俯视图吗？"
3. **背景清洁度**（专利图必须）：问 "是否有水印、文字、场景道具？"
4. **配件完整性**：问 "源图上的 X 配件在这张里是否清晰可见？"

每项分数 < 0.7 → 标红待重做。重做时把上一次的差异点写进 `userRefinement` 反馈给 prompt：

```
追加约束：上次生成中 ${drifts} 出现问题，本次必须修正。
```

### 10.7 UI 上的 Agent 进度展示

`PackPanel` 顶部加一条**生成总进度条**：

```
┌─────────────────────────────────────────────────────┐
│ 🤖 Agent 工作中 · Wave 3/5 · 已生成 47/114 张        │
│ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 41%   │
│ 当前批次：配件六视图（帽子/耳机/滑板...）              │
│ 已完成自检 ✓ 33 张 · 🔴 待重做 2 张                  │
└─────────────────────────────────────────────────────┘
```

每个 Pack 内的 AssetRow 显示状态徽章：
- ✓ 绿色 = 已占用（来自上传图）
- ✨ 紫色 = AI 已生成（通过自检）
- 🔴 红色 = AI 生成但自检不过，建议重做
- ⚠ 黄色 = 自检不确定，需人工确认
- ⏳ 灰色 = 等待生成

点单张图可看详情：`anchor 来源 / prompt / 自检评分 / 漂移点`。

### 10.8 Agent 配置（环境变量补充）

```bash
# Agent 并发度
AGENT_CONCURRENCY=4               # 单 Wave 并行数
AGENT_MAX_RETRY=2                 # 自检失败最多重试次数
AGENT_AUTO_REDO_THRESHOLD=0.7     # 自检分数低于此值自动重做

# Vision 模型
GPT_VISION_MODEL=gpt-5.5          # 用于识图、自检
```

### 10.9 失败恢复

Agent 跑到中途失败（API 超时、Key 限流）的处理：

- 每个 Wave 完成后**写一次 session.json 到 data/sessions/**
- Wave 中单张失败 → 标记 `status: 'failed'`，记录错误，**不阻塞其它节点**
- 用户刷新页面看到失败的 slot 显示红色，可一键重做
- 全 Wave 完成后，Orchestrator 输出失败摘要

### 10.10 Agent 输入两种输入的对比

| 输入 | Vision 分析判定 | 走的路径 | 实际工作量 |
|---|---|---|---|
| **一张单主角图**（普通玩具照） | `single-subject` | Mode B 复刻 | L1 净化 → 推断 Spec → 全量补 ~114 张 + 文字 |
| **lookbook 大图** | `lookbook` | Mode C 拆解+补全 | 切 30-40 块 → 自动占用 → 补 ~80 张 |
| **多张参考图**（同一角色多视角） | `multi-reference` | 自动分发 + 复刻 | 已有视角占用 → 补缺失 |
| **概念参考 + Prompt** | `multi-reference + prompt` | Mode A 二创 | 批量变体 → 选 → 复刻流程 |
| **纯文字 prompt** | `prompt-only` | 原 prompt-first | 批量生意向图 → 选 → 复刻流程 |

无论哪种入口，都最终汇入同一个 **anchor-preparation → character-inference → pack-generation** 状态机，**输出统一**。

### 10.11 实施 Checklist 增量（在 §4 和 §8.10 基础上）

- [ ] 10.A 设计 `InputClassification` + `inferInputClassification` Vision 调用
- [ ] 10.B 实现 `buildDAG` + `topologicalSort` 拓扑生成
- [ ] 10.C 实现 `runGenerationLoop` 异步生成器（emit ProgressEvent）
- [ ] 10.D 实现 `assessConsistency / assessViewAccuracy` 质量检查
- [ ] 10.E `PackPanel` 顶部加总进度条 + 每张图状态徽章
- [ ] 10.F session.json 增量写入（每 Wave 完成后保存）
- [ ] 10.G 失败恢复 UI（红色 slot 一键重做）
- [ ] 10.H 自动重做循环（自检不过 → 加 refinement → 最多重试 N 次）

### 10.12 推荐实施分期

**第 1 期：手动模式跑通**（不上 agent）
- 完成 §1（真图生图）+ §8 Mode B（单图复刻）+ §9 lookbook 拆解
- 用户手动点每个包的"生成"按钮
- 没有自动拓扑、没有自检

**第 2 期：拓扑批量生成**
- 完成 §10.5（buildDAG + topologicalSort）+ §10.C（runGenerationLoop）
- 用户点一次"一键全包"，agent 按 wave 并行跑完
- 还没有自检

**第 3 期：自检 + 自动重做**
- 完成 §10.6 + §10.H
- agent 自检不过的图自动重试 N 次

**第 4 期：完全自主 agent**
- 完成 §10.A（InputClassification）+ 自动路径选择
- 用户只需上传图，剩下全部 agent 自主完成
- 用户只看进度条和最终结果

**建议**：先做完第 1+2 期，能覆盖 80% 场景；第 3+4 期是质量优化和体验升级，可以按用户反馈再迭代。

---

## 11. 真人模特互动包（Talent Pack）

### 11.1 需求场景

潮玩宣发离不开"真人模特 × 玩具"的内容：

- 帅气男孩/漂亮女孩手持玩具的合影
- 模特把玩、拥抱、肩扛玩具的生活方式图
- 短视频：开箱、日常陪伴、Vlog 把玩、自拍展示
- 类似 LABUBU / MOLLY / Sonny Angel 那种小红书爆款图

要求：**尽可能真实**——真人皮肤纹理、自然光、自然表情、玩具材质对、手指抓握自然。

当前系统的 `mkt_scene_*` 只有"玩具放在场景里"，**完全没有真人模特出现**。需要把"真人互动"独立成 **Talent Pack（真人互动包）** —— 既不是单纯的宣发图，也不是社媒文案图，而是**模特 + 玩具同框**这一类特殊产物。

### 11.2 为什么独立成包

| 维度 | 普通宣发图 | 真人互动图 |
|---|---|---|
| 主体 | 玩具 | 模特 + 玩具 |
| 锚图 | 1 张（玩具 L1） | 2 张（模特参考 + 玩具 L1） |
| 一致性挑战 | 单角色一致 | 同一模特跨多图一致 + 玩具一致 |
| 合规风险 | 低 | 高（肖像权 / 儿童保护） |
| 视频生成难度 | 中（玩具旋转/特写） | 高（模特动作 + 玩具互动） |

要素完全不同，必须作为独立 `PackKind: 'talent'`。

### 11.3 数据模型扩展

```typescript
// types.ts
export type PackKind = 'patent' | 'production' | 'marketing' | 'accessories' | 'talent';

export type ModelPersona = {
  id: string;
  label: string;                  // "帅气男孩 · 嘻哈风"
  category: 'male' | 'female' | 'kid' | 'couple' | 'group';
  ageRange: '5-12' | '13-18' | '18-25' | '25-35' | '35-50';
  styleTags: string[];            // ['街头', '嘻哈', '复古']
  referenceImageUrl: string;      // 预生成的合成模特参考图
  characterPrompt: string;        // 完整 persona prompt block
  negativePrompt: string;
};

export type TalentAsset = ToyAsset & {
  modelPersonaId: string;         // 用了哪个模特
  modelAnchorUrl: string;         // 该模特的参考图 URL
  scenarioId: string;             // 互动场景模板 ID
};
```

### 11.4 模特库 ModelPersona

类似风格库，做一组可视化的模特预设。建议初版 10-12 个：

| ID | 类型 | 风格定位 |
|---|---|---|
| `male-street-cool` | 男 / 18-25 | 帅气男孩 · 街头嘻哈 |
| `male-creative-warm` | 男 / 25-35 | 温柔创意人 · 治愈 |
| `male-business-clean` | 男 / 25-35 | 都市精英 · 简洁 |
| `female-cute-soft` | 女 / 18-25 | 治愈系女孩 · 软妹 |
| `female-trendy-cool` | 女 / 18-25 | 潮酷女孩 · 街头 |
| `female-elegant-fashion` | 女 / 25-35 | 时尚白领 · 优雅 |
| `kid-boy-playful` | 童 / 5-12 | 活泼男孩 |
| `kid-girl-curious` | 童 / 5-12 | 好奇女孩 |
| `couple-young` | 情侣 / 18-25 | 青春情侣 |
| `collector-male` | 男 / 25-35 | 潮玩藏家 |
| `collector-female` | 女 / 25-35 | 潮玩藏家 |
| `office-worker` | 中性 / 25-35 | 办公室桌搭 |

每个 persona 准备：
1. **参考图**（256×256 缩略 + 1024×1024 高清）：合法合成肖像（**不能用真实明星**），存 `public/personas/`
2. **`characterPrompt` block**：完整描述脸型 / 发型 / 体型 / 服装 / 气质，给 GPT image-2 用
3. **`negativePrompt`**：明确禁止"识别为某真实明星"、"具体已知 IP 长相"

### 11.5 Talent Pack 模板槽位

新增模板（templates.ts 里加 `TALENT_TEMPLATES`）：

#### A 单人互动图（每个 persona 一组 6 张）

| Template ID | 内容 | 画幅 |
|---|---|---|
| `talent_portrait_handheld` | 模特正面手持玩具，眼神对镜头 | 4:5 |
| `talent_portrait_hug` | 模特怀抱玩具，温柔表情 | 4:5 |
| `talent_lifestyle_desk` | 模特在书桌/办公桌前，玩具桌搭 | 4:5 |
| `talent_lifestyle_outdoor` | 模特户外手持玩具（街头/咖啡店） | 4:5 |
| `talent_selfie_phone` | 自拍式构图，玩具在画面前景 | 9:16 |
| `talent_action_play` | 模特正在把玩玩具（互动动作） | 1:1 |

#### B 多人/情侣互动图

| Template ID | 内容 |
|---|---|
| `talent_couple_share` | 情侣共享玩具 |
| `talent_group_gift` | 朋友间赠送场景 |
| `talent_parent_child` | 亲子陪伴 |

#### C 视频脚本

视频走 Seedance，新增视频模板：

| Template ID | 内容 | 时长 |
|---|---|---|
| `video_talent_unbox` | 模特开箱第一视角 | 6-8s |
| `video_talent_play` | 模特把玩玩具 | 6s |
| `video_talent_daily` | 模特日常带玩具出门 | 8s |
| `video_talent_selfie` | 模特自拍 Vlog 展示 | 6s |

### 11.6 生成链路（关键）

真人互动图需要 **双锚图**：

```
模特参考图（modelPersonaReferenceUrl）
       +
玩具锚图（L1 净化锚图）
       ↓
   合成 prompt + 调 GPT image-2 /images/edits
       ↓
   talent_portrait_handheld
       ↓ （作为后续 talent 图的人物锚）
   talent_portrait_hug / talent_lifestyle_* （都参考此图保证模特一致）
       ↓
   视频任务（Seedance）参考 talent_portrait_handheld 当锚帧
```

技术实现：

1. **双图输入**：`/images/edits` 端点不原生支持多图，**用 image+mask 方式**或拼成"参考板"再生成
2. **更可靠方案**：先用 GPT-vision 看模特参考图 → 输出详细描述（脸型/发型/眼睛/嘴型），把这段描述拼进 prompt，再加玩具 L1 作为 image input
3. **第二选**：用 OpenAI 的 image-2 multi-image 入参（如果支持），分别标注 `[reference: model]` 和 `[reference: toy]`

### 11.7 模特一致性问题

最棘手：**同一个 persona 在多张图里要长得像同一个人**。

策略：
1. 第一张 `talent_portrait_handheld` 生成后，**它成为该 persona 在本项目的"人物锚图"**
2. 后续所有 talent_* 都把这张作为人物 anchor，玩具仍用 L1 锚
3. 添加更细致的 `characterPrompt`：脸型 / 发型 / 眼距 / 嘴角 / 标志性服饰
4. 用 Quality Checker 跨张比对模特相似度，相似度 < 0.6 标红重做

### 11.8 真实感提升要点

prompt 模板必须包含的真实感要素：

```
photorealistic, shot on Sony A7IV with 50mm f/1.8 lens,
natural daylight, soft shadows, skin texture visible,
shallow depth of field, real environment, candid moment,
authentic emotion, no AI-art artifacts, no plastic skin,
hands holding toy naturally with realistic finger curvature
```

negative：

```
cartoon, anime, 3D render, plastic skin, perfect symmetry,
glowing eyes, anime eyes, AI artifacts, deformed hands,
extra fingers, distorted toy proportions
```

### 11.9 合规与肖像权

**严格要求**：

1. **不允许使用真实明星 / 公众人物的样貌**
   - prompt 不允许包含明星名（"长得像 XXX"）
   - Vision Agent 检测生成图，如果识别为已知明星 → 自动拒绝并重做
2. **儿童 persona 需要额外标记**
   - UI 上显示 "⚠ 儿童形象，请确认有合法授权或仅用于内部参考"
   - 不允许儿童单独出现的不合适场景
3. **合成模特身份**
   - 所有生成图导出时 manifest 必须包含 `"talent_disclaimer": "本图模特为 AI 合成，非真实人物"`
   - 用户使用时需自行决定是否标注
4. **真实模特照片导入**
   - 如果用户上传自己拍的模特照片作为 reference，UI 要求确认 "我拥有/已获得该模特肖像使用授权"

### 11.10 UI 设计

`PackPanel` 加新的 Talent Pack section，结构和其它包类似但有特殊控件：

```
┌────────────────────────────────────────────────────┐
│ 👤 真人互动包 · 模特 × 玩具                          │
├────────────────────────────────────────────────────┤
│ Step 1: 选模特类型（可多选）                          │
│  [🧑‍🎤帅气男孩-嘻哈] [👧治愈系女孩] [👨‍💼都市精英]      │
│  [👶活泼男孩] [👩‍🎤潮酷女孩] [...] 共 12 款            │
│                                                     │
│ Step 2: 选互动场景（默认 6 个，可勾选）                │
│  [✓] 手持正面  [✓] 怀抱  [ ] 桌搭                    │
│  [✓] 户外     [ ] 自拍   [ ] 把玩                    │
│                                                     │
│ Step 3: 视频任务（可选）                              │
│  [ ] 开箱 6s  [ ] 把玩 6s  [ ] 日常 8s               │
│                                                     │
│ [生成 talent pack（4 张 × 选中模特数 + 视频任务）]   │
└────────────────────────────────────────────────────┘
```

每张生成图显示「模特：xxx」「场景：xxx」「自检评分：0.85 ✓」。

### 11.11 实施 Checklist 增量

- [ ] 11.A `PackKind` 加 `'talent'`，类型扩展 `ModelPersona` / `TalentAsset`
- [ ] 11.B 准备 12 个 ModelPersona + 缩略图（放 `public/personas/`）
- [ ] 11.C `TALENT_TEMPLATES` 模板定义（单人 6 + 多人 3 + 视频 4）
- [ ] 11.D 双锚图生成函数：`generateTalentImage({ modelPersona, toyAnchorUrl, scenario })`
- [ ] 11.E 模特一致性自检（Quality Checker 扩展）
- [ ] 11.F UI：模特选择卡片 + 场景勾选 + 进度
- [ ] 11.G 合规模块：明星人脸检测拒绝、儿童形象提示、肖像授权确认
- [ ] 11.H Seedance 视频任务支持 talent 锚图

### 11.12 优先级建议

`talent` 包属于**第 2 阶段**功能（在主链路打通之后）。原因：

- 主体（玩具）一致性都还没解决前，加真人会进一步放大漂移问题
- 必须先有稳定的 L1 锚图 + 真图生图链路（§1）
- 视频部分必须有稳定的静态 talent 图作为锚帧

建议实施顺序：
1. 完成 §1（真图生图）+ §8 Mode B（单图复刻）
2. 实现 §11.B（模特库准备）+ §11.D（双锚图生成）
3. 再做视频 talent 任务

如果用户特别强调真人互动，可以**优先做最有传播力的 3 个 slot**：
- `talent_portrait_handheld`（小红书爆款基础图）
- `talent_lifestyle_outdoor`（生活方式种草）
- `video_talent_play`（短视频把玩）

3 个 slot 跑通了，宣发素材就够发一波。


