# agent_remix_orchestrator — Remix 编排器指南

**定位**：L0 Manager 的操作手册。本 Skill 不生成任何文件，只提供从用户意图到完整 Remix 产出的完整 7 步工作流指南。

## Recipe

| 决策 | 原因 |
|------|------|
| **7 步线性工作流** | 将 DAG 操作链路分解为可执行的离散步骤，Agent 可逐步确认状态，避免因顺序错误导致管线断裂 |
| **三信号融合权重** | 单信号（LLM 直觉 / 历史 CTR / 用户偏好）各有盲区；三信号加权合成在冷启动和成熟期均可用 |
| **分歧度驱动 HITL** | Agent 自主执行高一致决策，只在真正有分歧时才打扰用户，降低交互成本 |
| **非代码 Skill** | 编排逻辑是 Agent 的认知框架，不产出项目文件；作为 SKILL.md 存在使其能被 `search_skills` 发现并加载 |

## Adapter

- **Role**: `remixOrchestrator` — L0 Manager 从意图到 Remix 的完整 7 步工作流 SOP
- **Provides**: 工作流步骤文档、API 调用示例、bindingRole 速查表、错误处理原则
- **Requires**: 所有 Atom Skill（在工作流 Step 6 中按 DAG 拓扑序逐一加载）
- **Consumed by**: Agent L0 Manager 在接收用户意图时首先加载本 Skill 作为决策手册
- **Integration point**: 无代码集成点；Agent 读取 SKILL.md 内容后按工作流步骤执行

**前置阅读**：
- `docs/recommend/design-framework.md` — 核心概念：Remix = DAG(Atom[])
- `docs/recommend/agent-decision-mechanism.md` — 三信号融合与权重计算
- `docs/recommend/atom-to-remix-design.md` — Binding 机制与 Recipe 封包格式

---

## 总体工作流

```
用户意图
  ↓ Step 1: parse_intent
IntentTags（在乎维度 / 不在乎维度）
  ↓ Step 2: search_skills
候选 Atom 列表
  ↓ Step 3: DAG 拓扑排序
生成顺序
  ↓ Step 4: 计算三信号权重
每个 Atom 的 final_score 排序
  ↓ Step 5: 分歧度判断
自主执行 / 问人确认
  ↓ Step 6: 按拓扑序执行各 Atom Skill
封包写入沙箱分支
  ↓ Step 7: 触发构建
可交付 Playable
```

---

## Step 1：解析意图

**调用**：`POST /api/agent/tools/parse-intent`

```json
// Request
{ "prompt": "热带休闲 match-3，参考 R2 风格，保留我的难度曲线" }

// Response
{
  "intentTags": {
    "画风": ["热带", "自然"],
    "节奏": ["休闲"],
    "核心玩法": ["消除"],
    "关卡难度": null,
    "音乐风格": null
  },
  "caredDimensions": ["画风", "节奏", "核心玩法"],
  "uncaredDimensions": ["关卡难度", "音乐风格"]
}
```

**规则**：
- `caredDimensions`（non-null）= 用户在乎，后续 α=0.8
- `uncaredDimensions`（null）= 用户不在乎，后续 β=0.8
- 用户显式锁定的维度（"保留我的难度曲线"）单独记录为 `C_locked`，不参与策略搜索

**Phase 3 MVP 说明**：当前 `parse_intent` 为 mock 实现，所有维度返回 null，退化为双信号（历史权重 + LLM 判断）。`caredDimensions` 始终为空。

---

## Step 2：查询 Atom 候选空间

**调用**：`POST /api/agent/tools/search-skills`

按 bindingRole / category / bundleType 查询可用 Skill 候选列表。

```json
// 查询所有背景音乐候选
{
  "matchBindingRoles": ["bgMusic"],
  "allowedBundleTypes": ["aiaudio"]
}

// 查询所有玩法候选（已确认 phaser 引擎在上游）
{
  "allowedCategories": ["gameplay"],
  "contextAtomIds": ["phaser.aicomponent"]
}
```

**Response**：每个候选包含 `atomId`、`label`、`tags`、`bindingRoles`、`imports`（依赖声明）。

**速查表：各维度对应的 bindingRole**

| 维度 | matchBindingRoles | 典型 bundleType |
|------|-------------------|----------------|
| 引擎 | `gameEngine` | `aicomponent` |
| 背景音乐 | `bgMusic` | `aiaudio` |
| 点击音效 | `sfxClick` | `aiaudio` |
| 玩法规则 | `gameplayRule` | `aigameplay` |
| 关卡数据格式 | `levelDataFormat` | `aicomponent` |
| 棋盘布局 | `gridBoardLayout` | `aicomponent` |
| 响应式布局 | `responsiveLayout` | `aicomponent` |
| HUD 屏幕分区（规范） | `playableHudLayout` | `aicomponent` |
| 结算屏布局（规范） | `playableEndScreenLayout` | `aicomponent` |
| 指引层布局（规范） | `playableGuidanceLayout` | `aicomponent` |
| 顶部 HUD 条 | `topUiBar` | `aicomponent` |
| 底部 HUD 条 | `bottomUiBar` | `aicomponent` |
| 游戏主场景 | `gameMainScene` | `aicomponent` |
| 预加载场景 | `preloaderScene` | `aicomponent` |
| 胜负面板 | `gameOverDialog` | `aicomponent` |
| 下载按钮 | `downloadCta` | `aicomponent` |
| App Logo | `appLogo` | `aiimage` |
| 胜利面板图 | `successFeedbackPanel` | `aiimage` |
| 关卡数据集 | `levelDataPack` | `aiconfig` |
| App 文案 | `appMetadata` | `aiconfig` |

---

## Step 3：DAG 拓扑排序

从 `basicAtomSkillGraph`（位于 `packages/common/src/recommend/atom-graph/basic-atom-skill-graph.ts`）读取图结构，按 `requires` 边做拓扑排序，确定生成顺序：**入度为 0 的节点优先生成**。

**典型路径消除游戏的拓扑顺序**：

```
1. phaser.aicomponent              (引擎，入度=0)
2. playable_scripts_build.aicomponent (构建管线，入度=0)
3. responsive_2d_layout.aicomponent   (布局数学 Canon，入度=0)
3b. playable_hud_layout / playable_end_screen_layout / playable_guidance_layer（屏幕分区规范，可与 3 并行阅读，无代码）
4. phaser_scene_lifecycle.aicomponent (场景架构，depends: phaser)
5. grid_board_layout.aicomponent      (棋盘布局，depends: phaser)
6. path_elimination_rules.aigameplay  (玩法，depends: phaser)
7. arrow_path_data_format.aicomponent (数据格式，depends: gameplay)
8. game_scene.aicomponent             (主场景，depends: 上游多个)
9. preloader_scene.aicomponent        (预加载，depends: phaser)
10. [UI 组件层，互相独立，并行生成]
11. [资源层 aiimage/aiaudio，弱依赖容器，并行生成]
12. [配置层 aiconfig，独立]
```

**规则**：
- `requires` 边是硬约束，必须按序
- `compatibleWith`、`semanticallyRelatedTo`、`alternativeTo` 边不影响顺序，只影响候选选择

---

## Step 4：计算三信号权重

对每个 Atom 候选，计算 `final_score`，确定最终选哪个：

```
final_score = α × relevance_score + β × prior_score
```

### 4.1 查先验分

**调用**：`GET /api/agent/tools/prior-weight?atomId=bg_music.aiaudio`

```json
// Response
{ "atomId": "bg_music.aiaudio", "priorScore": 0.6, "isMock": true }
```

`isMock: true` 时，prior_score 来自 mock JSON，不是真实 CTR 数据。

### 4.2 计算相关分（LLM 判断）

对每个候选 Atom，用 LLM 判断其与 IntentTags 的语义关联（0~1）：

```
示例：用户意图画风=[热带, 自然]
  bg_music.aiaudio   → LLM："热带轻松风格" → 相关分 0.65
  calm_piano.aiaudio → LLM："钢琴冥想" → 相关分 0.3
  energetic_electronic.aiaudio → LLM："电子" → 相关分 0.2
```

### 4.3 合成

```
在乎维度（画风、节奏、玩法）: α=0.8, β=0.2
不在乎维度（音乐、难度）:     α=0.2, β=0.8
锁定维度:                    直接取用户指定值，跳过合成

示例（背景音乐，不在乎维度，α=0.2, β=0.8）:
  bg_music.aiaudio:         0.2×0.65 + 0.8×0.6 = 0.61 ← 最高
  calm_piano.aiaudio:       0.2×0.3  + 0.8×0.5 = 0.46
  energetic_electronic:     0.2×0.2  + 0.8×0.5 = 0.44
```

**Phase 3 MVP 说明**：由于 `parse_intent` 返回空 IntentTags，`caredDimensions` 为空，所有维度均为"不在乎"，α=0.2, β=0.8，先验权重主导选择。

---

## Step 5：分歧度判断

| 状态 | 条件 | Agent 行为 |
|------|------|-----------|
| **高一致** | 三信号 Top-1 一致，或不一致的来自"不在乎"维度 | 自主执行，不打扰用户 |
| **中分歧** | 最终分与相关分一致但与先验分不一致，且用户在乎该维度 | 呈报冲突："数据说 X 好，但你倾向 Y，按哪个走？" |
| **高分歧** | 三信号各异，或在乎维度上信号互相矛盾 | 主动补问，列出 2-3 个方向让用户选 |

用户每次选择 = 最干净的信用分配信号，记录为偏好。

---

## Step 6：按拓扑序执行各 Atom Skill

对 Step 3 排好序的每个 Atom，读取其 `SKILL.md` 并执行：

1. 对 **aicomponent** Skill：根据 `scaffold.files` 把 `ref/` 下的文件写入 Remix 沙箱项目目录。
2. 对 **aiimage** Skill：按 `generation.prompt` 调用 AI 图像生成管线，产出图片并写入 `assets/`。
3. 对 **aiaudio** Skill：若目录下有 `ref/*.mp3`，直接复制；否则调用 AI 音频生成。
4. 对 **aiconfig** Skill：按 `generation.params` 写入配置 JSON。
5. 对 **aigameplay** Skill：直写 PGS JSON，运行 testCases 验证，迭代直到通过。

**写入路径**：所有文件相对于 Remix 沙箱的 `/project` 根目录。写完后调用 `SandboxNotifyService.notifyChange(...)` 触发前端刷新。

---

## Step 7：触发构建

所有封包写入分支后，调用现有构建 API 触发 `@playcraft/build`，产出可交付的 Playable（HTML+JS 单文件）。

---

## 快速参考：当前 MVP 能力边界

| 功能 | 状态 | 说明 |
|------|------|------|
| `search_skills` | ✅ 可用 | 读文件系统，过滤 Skill 候选 |
| `prior_weight` | ✅ 可用（mock） | mock JSON，isMock=true，先验分仅供参考 |
| `parse_intent` | ⚠️ Mock | 全部返回 null，退化为双信号，`TODO(P3.intent)` |
| 分歧度判断 | ✅ 可用 | 规则计算，不依赖服务 |
| AtomInstance 持久化 | ❌ 未做 | Phase 5 |
| Skill Inspector（一致性） | ❌ 未做 | Phase 4 |
| 自动构建触发 | ❌ 未做 | Phase 4 |

---

## 错误处理原则

- **Atom 生成失败**：回退到 Phase 2（使用现有封包），不中断整个流程
- **`search_skills` 返回空**：扩大过滤条件重试，或向用户报告无候选
- **预算耗尽**：触发 HITL，由用户决定追加还是终止（见 `docs/agent/agent-human-in-the-loop-design.md`）
- **`parse_intent` 返回空 IntentTags**（mock 状态）：降级为双信号，记录 warning
