diff --git a/CLAUDE.md b/CLAUDE.md
index 8ed8276..739b820 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -8,11 +8,11 @@ gen2d — AI 驱动的 2D 游戏素材生成工具。用户通过文本提示词
## Directory Structure
-详见 [docs/architecture.md](docs/architecture.md) 中的后端分层结构与前端组件树。
+详见 [docs/_index.md](docs/_index.md) 中的后端分层结构与前端组件树。
## Common Commands
-### Backend (Go + Gin)
+### Backend (Go + Gin + Eino)
```bash
cd backend
@@ -62,7 +62,7 @@ npm run typecheck
## Architecture
-详见 [docs/architecture.md](docs/architecture.md),涵盖多智能体管线、后端分层、前端组件树、API 设计、素材生成约束等。
+详见 [docs/_index.md](docs/_index.md),涵盖多智能体管线、后端分层、前端组件树、API 设计、素材生成约束等。
## Conventions
diff --git a/docs/_index.md b/docs/_index.md
index 5e243e0..d76ac41 100644
--- a/docs/_index.md
+++ b/docs/_index.md
@@ -4,13 +4,14 @@ gen2d — AI 驱动的 2D 游戏素材生成工具。通过文本提示词生成
## 技术栈
-Go + Gin / Vite + React + TypeScript + zustand / 可替换 AI 推理模型
+Go + Gin + Eino / Vite + React + TypeScript + zustand / 可替换 AI 推理模型
## 文档索引
-- [多智能体生成管线](multi-agent-pipeline.md) — PromptBuilder → AssetGenerator → QualitySupervisor → FormatAdapter 三阶段流水线
+- [多智能体生成管线](multi-agent-pipeline.md) — PromptBuilder → AssetGenerator → QualitySupervisor → FormatAdapter 四阶段流水线
- [后端工程](backend.md) — 分层结构、目录组织、分层原则
- [前端工程](frontend.md) — 组件树、状态管理、路由、WebSocket 通信
- [异步任务](async-tasks.md) — 任务队列、状态机、并发控制、失败重试
- [数据存储](database.md) — 数据库选型、表结构、素材文件存储
- [API 设计](api.md) — 接口列表、请求/响应示例、实现状态
+- [预设风格键](style-keys.md) — 美术风格、色调、线条等风格键分类与可选值
diff --git a/docs/api.md b/docs/api.md
index 1b8b30c..e7a3897 100644
--- a/docs/api.md
+++ b/docs/api.md
@@ -19,35 +19,64 @@
|------|------|------|------|
| [x] | GET | `/api/v1/health` | 健康检查 |
-## 素材生成
+## 工程管理
| 状态 | 方法 | 路径 | 说明 |
|------|------|------|------|
-| [ ] | POST | `/api/v1/generate` | 提交生成任务,返回 jobId |
-| [ ] | GET | `/api/v1/generate/:jobId` | 查询任务状态与进度 |
-| [ ] | GET | `/api/v1/generate/:jobId/result` | 获取生成结果(素材 URL + 元数据) |
-| [ ] | WS | `/api/v1/generate/:jobId/ws` | WebSocket 实时进度推送 |
+| [ ] | POST | `/api/v1/projects` | 创建工程 |
+| [ ] | GET | `/api/v1/projects/:projectId` | 获取工程信息 |
+| [ ] | GET | `/api/v1/projects/:projectId/tasks` | 获取工程下的任务列表 |
-请求体示例 (POST /api/v1/generate):
+### POST /api/v1/projects
```json
{
- "prompt": "一个拿剑的小人",
- "assetType": "sprite",
- "taskStyle": {
- "scene": "dungeon",
- "mood": "dark"
- },
- "params": {
- "resolution": 64,
- "frames": { "directions": 8, "framesPerDirection": 4 },
- "format": "spritesheet"
+ "name": "我的像素游戏"
+}
+```
+
+响应:
+
+```json
+{
+ "code": 0,
+ "message": "ok",
+ "data": {
+ "id": "proj_abc123",
+ "name": "我的像素游戏",
+ "style": { "kvPairs": {} },
+ "createdAt": "2026-05-24T10:00:00Z"
}
}
```
-- `prompt`:用户原始文本,后端 PromptBuilder 负责三段式重写
-- `taskStyle`:可选,任务级别风格覆盖(同名键覆盖工程风格)
+### GET /api/v1/projects/:projectId/tasks
+
+| 参数 | 类型 | 说明 |
+|------|------|------|
+| `page` | int | 页码,默认 1 |
+| `pageSize` | int | 每页条数,默认 20 |
+
+响应:
+
+```json
+{
+ "code": 0,
+ "message": "ok",
+ "data": {
+ "total": 42,
+ "tasks": [
+ {
+ "id": "task_xyz789",
+ "prompt": "一个拿剑的小人",
+ "assetType": "sprite",
+ "status": "completed",
+ "createdAt": "2026-05-24T10:05:00Z"
+ }
+ ]
+ }
+}
+```
## 工程风格
@@ -69,6 +98,173 @@
}
```
+## 素材生成
+
+| 状态 | 方法 | 路径 | 说明 |
+|------|------|------|------|
+| [ ] | POST | `/api/v1/generate` | 提交生成任务,返回 taskId |
+| [ ] | GET | `/api/v1/tasks/:taskId` | 查询任务状态与进度 |
+| [ ] | GET | `/api/v1/tasks/:taskId/assets` | 获取生成结果(素材列表 + 元数据) |
+| [ ] | WS | `/api/v1/tasks/:taskId/ws` | WebSocket 实时进度推送 |
+
+### POST /api/v1/generate
+
+请求体对应后端 `PipelineInput`:
+
+```json
+{
+ "projectId": "proj_abc123",
+ "prompt": "一个拿剑的小人",
+ "assetType": "sprite",
+ "taskStyle": {
+ "scene": "dungeon",
+ "mood": "dark"
+ },
+ "params": {
+ "resolution": 64,
+ "frames": { "directions": 8, "framesPerDirection": 4 },
+ "format": "spritesheet"
+ }
+}
+```
+
+| 字段 | 类型 | 必填 | 说明 |
+|------|------|------|------|
+| `projectId` | string | 是 | 工程 ID,用于获取工程风格 |
+| `prompt` | string | 是 | 用户原始文本,后端 PromptBuilder 负责三段式重写 |
+| `assetType` | string | 是 | 素材类型:`sprite` / `background` / `ui` / `animation` |
+| `taskStyle` | object | 否 | 任务级别风格覆盖(同名键覆盖工程风格) |
+| `params.resolution` | int | 否 | 分辨率,默认 64 |
+| `params.frames` | object | 否 | 帧参数(仅 sprite/animation) |
+| `params.frames.directions` | int | 否 | 方向数,默认 4 |
+| `params.frames.framesPerDirection` | int | 否 | 每方向帧数,默认 4 |
+| `params.format` | string | 否 | 输出格式:`spritesheet` / `individual`,默认 `spritesheet` |
+
+响应:
+
+```json
+{
+ "code": 0,
+ "message": "ok",
+ "data": {
+ "taskId": "task_xyz789",
+ "status": "pending"
+ }
+}
+```
+
+### GET /api/v1/tasks/:taskId
+
+响应:
+
+```json
+{
+ "code": 0,
+ "message": "ok",
+ "data": {
+ "taskId": "task_xyz789",
+ "projectId": "proj_abc123",
+ "prompt": "一个拿剑的小人",
+ "assetType": "sprite",
+ "status": "running",
+ "stage": "asset_generator",
+ "progress": 45,
+ "retryCount": 0,
+ "createdAt": "2026-05-24T10:05:00Z",
+ "updatedAt": "2026-05-24T10:05:30Z"
+ }
+}
+```
+
+任务状态机:
+
+```mermaid
+stateDiagram-v2
+ [*] --> pending
+ pending --> running : 管线开始执行
+ running --> completed : 四阶段全部通过
+ running --> failed : 节点执行失败 / 超过重试次数
+ completed --> [*]
+ failed --> [*]
+```
+
+管线阶段(对应 Eino Graph 节点):
+
+| 阶段 | 说明 |
+|------|------|
+| `prompt_builder` | 合并风格,生成三段式提示词 |
+| `asset_generator` | 调用 AI 推理 API 出图 |
+| `quality_supervisor` | 视觉模型质检(可能触发重试回到 prompt_builder) |
+| `format_adapter` | 格式转换、spritesheet 打包 |
+
+### GET /api/v1/tasks/:taskId/assets
+
+```json
+{
+ "code": 0,
+ "message": "ok",
+ "data": {
+ "assets": [
+ {
+ "id": "asset_001",
+ "url": "/files/tasks/task_xyz789/spritesheet.png",
+ "format": "png",
+ "width": 256,
+ "height": 64,
+ "metadata": {
+ "frameWidth": 64,
+ "frameHeight": 64,
+ "frameCount": 4,
+ "directions": 1
+ }
+ }
+ ]
+ }
+}
+```
+
+### WebSocket 消息格式
+
+连接路径:`ws://host/api/v1/tasks/:taskId/ws`
+
+```typescript
+interface PipelineProgress {
+ stage: 'prompt_builder' | 'asset_generator' | 'quality_supervisor' | 'format_adapter';
+ status: 'running' | 'completed' | 'failed';
+ progress: number; // 0-100
+ message?: string; // 阶段描述
+ retryCount?: number; // 质检重试次数(仅 quality_supervisor 阶段)
+ rejectReason?: string; // 质检不通过原因(仅 quality_supervisor fail 时)
+ result?: { // 仅在 format_adapter completed 时返回
+ assets: Asset[];
+ };
+ error?: string; // 仅在 failed 时返回
+}
+```
+
+消息示例(正常流程):
+
+```json
+{"stage":"prompt_builder","status":"running","progress":0}
+{"stage":"prompt_builder","status":"completed","progress":100}
+{"stage":"asset_generator","status":"running","progress":0}
+{"stage":"asset_generator","status":"completed","progress":100}
+{"stage":"quality_supervisor","status":"completed","progress":100}
+{"stage":"format_adapter","status":"running","progress":0}
+{"stage":"format_adapter","status":"completed","progress":100,"result":{"assets":[...]}}
+```
+
+消息示例(质检重试):
+
+```json
+{"stage":"quality_supervisor","status":"running","progress":0}
+{"stage":"quality_supervisor","status":"failed","progress":100,"retryCount":1,"rejectReason":"风格不一致"}
+{"stage":"prompt_builder","status":"running","progress":0}
+{"stage":"asset_generator","status":"running","progress":0}
+{"stage":"quality_supervisor","status":"completed","progress":100}
+{"stage":"format_adapter","status":"completed","progress":100,"result":{"assets":[...]}}
+```
+
## 缓存管理
| 状态 | 方法 | 路径 | 说明 |
diff --git a/docs/backend.md b/docs/backend.md
index 518374e..715a76f 100644
--- a/docs/backend.md
+++ b/docs/backend.md
@@ -1,5 +1,11 @@
# 后端工程
+## 技术选型
+
+- **HTTP 框架**:Gin
+- **管线编排**:[Eino](https://github.com/cloudwego/eino) `compose.Graph` — 四阶段管线,含质检→重生成分支
+- **AI 组件**:Eino 的 model / tool 组件,统一接口便于替换模型提供商
+
## 分层结构
```
@@ -9,15 +15,16 @@ backend/internal/
│ ├── project_style.go # 工程风格 CRUD
│ └── health.go # [已有] 健康检查
├── service/ # 业务逻辑层
-│ ├── pipeline.go # 核心管线:接收请求 → PromptBuilder → AssetGenerator → QualitySupervisor → FormatAdapter
-│ ├── inference.go # AI 推理 API 调用封装(可替换模型提供商)
+│ ├── pipeline.go # Eino compose.Graph 编排:PromptBuilder → AssetGenerator → QualitySupervisor → FormatAdapter
+│ ├── nodes.go # 管线四个节点的实现(每个节点单一职责)
+│ ├── types.go # 管线输入/状态/输出的显式结构体定义
+│ ├── inference.go # AI 推理 API 调用封装(Eino model 组件)
│ └── project_style.go # 工程风格管理 & 风格合并逻辑
├── model/ # 数据模型 / DTO
│ ├── response.go # [已有] 统一响应
│ ├── task.go # 生成任务 & 素材
│ └── style.go # 工程风格 & 任务风格覆盖
├── middleware/ # 中间件
-│ ├── cors.go
│ ├── logger.go
│ └── ratelimit.go
├── config/ # [已有] 配置
@@ -31,9 +38,152 @@ backend/internal/
## 分层原则
- **handler**:只做参数绑定、校验、调用 service、返回响应。一个 handler 对应一组 API 路由。
-- **service**:承载所有业务逻辑。`pipeline.go` 是唯一编排入口,不再拆分成 orchestrator/preprocess/generator/postprocess 四个文件——这些是同一根管线的顺序步骤,拆开反而增加耦合面。
+- **service**:承载所有业务逻辑。`pipeline.go` 通过 Eino `compose.Graph` 编排四个节点;`nodes.go` 实现各节点逻辑;`types.go` 定义显式状态结构体。
- **model**:纯数据结构,不含业务逻辑。
+## 管线设计(Eino compose.Graph)
+
+四阶段管线,含质检不通过时的重生成分支:
+
+```mermaid
+flowchart LR
+ START --> PromptBuilder --> AssetGenerator --> QualitySupervisor
+ QualitySupervisor -- pass --> FormatAdapter --> END
+ QualitySupervisor -- fail --> PromptBuilder
+```
+
+### 类型定义(types.go)
+
+```go
+// PipelineInput 管线入口输入
+type PipelineInput struct {
+ Prompt string // 用户原始文本
+ AssetType string // 素材类型:sprite / background / ui / animation
+ ProjectStyle map[string]string // 工程风格键值对
+ TaskStyle map[string]string // 任务风格覆盖
+ Params AssetParams // 技术参数(分辨率、帧数、格式等)
+}
+
+// PipelineState Graph 全局状态,通过 WithGenLocalState 注入
+type PipelineState struct {
+ Input PipelineInput
+ FinalPrompt string // PromptBuilder 输出的三段式提示词
+ RawImages []GeneratedImage // AssetGenerator 输出的原始图片
+ PassQuality bool // QualitySupervisor 质检结果
+ RejectReason string // 质检不通过原因
+ RetryCount int // 重试次数
+}
+
+// PipelineOutput 管线最终输出
+type PipelineOutput struct {
+ Assets []Asset // 生成结果素材列表
+ Metadata AssetMetadata // 元数据(分辨率、帧数、格式)
+}
+```
+
+### 编排入口(pipeline.go)
+
+```go
+const (
+ nodePromptBuilder = "prompt_builder"
+ nodeAssetGenerator = "asset_generator"
+ nodeQualitySupervisor = "quality_supervisor"
+ nodeFormatAdapter = "format_adapter"
+)
+
+func NewGenerateGraph() (*compose.Graph[PipelineInput, PipelineOutput], error) {
+ g := compose.NewGraph[PipelineInput, PipelineOutput](
+ compose.WithGenLocalState(func(ctx context.Context) *PipelineState {
+ return &PipelineState{}
+ }),
+ )
+
+ // 添加节点
+ _ = g.AddLambdaNode(nodePromptBuilder, promptBuilderNode,
+ compose.WithStatePostHandler(promptBuilderPostHandler))
+ _ = g.AddLambdaNode(nodeAssetGenerator, assetGeneratorNode,
+ compose.WithStatePostHandler(assetGeneratorPostHandler))
+ _ = g.AddLambdaNode(nodeQualitySupervisor, qualitySupervisorNode,
+ compose.WithStatePostHandler(qualitySupervisorPostHandler))
+ _ = g.AddLambdaNode(nodeFormatAdapter, formatAdapterNode)
+
+ // 连线:正常路径
+ _ = g.AddEdge(compose.START, nodePromptBuilder)
+ _ = g.AddEdge(nodePromptBuilder, nodeAssetGenerator)
+ _ = g.AddEdge(nodeAssetGenerator, nodeQualitySupervisor)
+ _ = g.AddEdge(nodeFormatAdapter, compose.END)
+
+ // 连线:质检分支
+ _ = g.AddBranch(nodeQualitySupervisor, compose.NewGraphBranch(
+ func(ctx context.Context, state *PipelineState) (string, error) {
+ if state.PassQuality {
+ return nodeFormatAdapter, nil
+ }
+ if state.RetryCount >= 3 {
+ return nodeFormatAdapter, nil // 超过重试次数,降级输出
+ }
+ return nodePromptBuilder, nil // 重生成
+ },
+ map[string]bool{nodePromptBuilder: true, nodeFormatAdapter: true},
+ ))
+
+ return g, nil
+}
+```
+
+### 节点实现(nodes.go)
+
+每个节点是 Graph 中的 Lambda,通过 `StatePreHandler` / `StatePostHandler` 读写全局状态:
+
+| 节点 | Lambda 输入→输出 | State 交互 | 职责 |
+|------|-----------------|-----------|------|
+| PromptBuilder | `PipelineInput → string` | PostHandler 写入 FinalPrompt | 合并风格 → 生成三段式提示词 |
+| AssetGenerator | `string → []GeneratedImage` | PostHandler 写入 RawImages | 调用 AI 推理 API 出图 |
+| QualitySupervisor | `[]GeneratedImage → bool` | PostHandler 写入 PassQuality + RejectReason + RetryCount | 视觉模型质检 |
+| FormatAdapter | `[]GeneratedImage → PipelineOutput` | 无 | 格式转换、spritesheet 打包 |
+
+```go
+// PromptBuilder 节点:接收输入,输出提示词
+var promptBuilderNode = compose.InvokableLambda(func(ctx context.Context, in PipelineInput) (string, error) {
+ // 合并 projectStyle + taskStyle,生成三段式提示词
+ return buildPrompt(in), nil
+})
+
+// StatePostHandler:将提示词写入全局状态
+func promptBuilderPostHandler(ctx context.Context, out string, state *PipelineState) (string, error) {
+ state.FinalPrompt = out
+ return out, nil
+}
+
+// QualitySupervisor StatePostHandler:写入质检结果和重试计数
+func qualitySupervisorPostHandler(ctx context.Context, out bool, state *PipelineState) (bool, error) {
+ state.PassQuality = out
+ if !out {
+ state.RetryCount++
+ state.RejectReason = "风格不一致" // 实际由视觉模型返回
+ }
+ return out, nil
+}
+```
+
+### 运行入口
+
+```go
+func RunPipeline(ctx context.Context, in PipelineInput) (*PipelineOutput, error) {
+ g, err := NewGenerateGraph()
+ if err != nil {
+ return nil, err
+ }
+
+ r, err := g.Compile(ctx)
+ if err != nil {
+ return nil, err
+ }
+
+ return r.Invoke(ctx, in)
+}
+```
+
## 关键实体
| 实体 | 说明 | 关系 |
@@ -44,10 +194,11 @@ backend/internal/
| Prompt | PromptBuilder 输出的三段式提示词(主题+约束+内容),管线中间产物,不持久化 | 由 Task 生成 |
| Asset | 生成结果素材,关联到任务,包含 URL、元数据(分辨率、帧数、格式) | 属于 Task |
-```
-Project 1──1 ProjectStyle
-Project 1──N Task
-Task 1──N Asset
+```mermaid
+erDiagram
+ Project ||--|| ProjectStyle : has
+ Project ||--|{ Task : has
+ Task ||--|{ Asset : has
```
## 风格模型
@@ -82,4 +233,4 @@ type TaskStyle struct {
finalStyle = merge(projectStyle.KVPairs, taskStyle.KVPairs)
```
-任务同名键覆盖工程风格,由 PromptBuilder 在生成提示词时执行合并。
+任务同名键覆盖工程风格,由 PromptBuilder 节点在生成提示词时执行合并。
diff --git a/docs/frontend.md b/docs/frontend.md
index 7229fa2..80ff314 100644
--- a/docs/frontend.md
+++ b/docs/frontend.md
@@ -10,7 +10,7 @@ Vite 6 + React 18 + TypeScript + zustand + react-router-dom
| UI 框架 | React 18 | 函数组件 + Hooks |
| 状态管理 | zustand | 轻量,支持 devtools 中间件 |
| 路由 | react-router-dom v6 | SPA 模式 |
-| 实时通信 | 原生 WebSocket | 连接后端 `/api/v1/generate/:jobId/ws` |
+| 实时通信 | 原生 WebSocket | 连接后端 `/api/v1/tasks/:taskId/ws` |
| HTTP 请求 | fetch + 封装层 | 统一错误处理、响应解包 |
## 目录结构
@@ -21,13 +21,13 @@ frontend/src/
├── App.tsx # 根组件 + 路由配置
├── api/ # API 封装层
│ ├── client.ts # fetch 封装:baseURL、统一错误处理、响应解包
-│ ├── generate.ts # POST /generate、GET /generate/:jobId、GET /generate/:jobId/result
-│ ├── project.ts # GET/PUT /projects/:projectId/style
-│ └── types.ts # API 请求/响应类型定义(Task、Asset、Style、JobStatus 等)
+│ ├── project.ts # 工程 CRUD + 风格 GET/PUT
+│ ├── generate.ts # POST /generate、GET /tasks/:taskId、GET /tasks/:taskId/assets
+│ └── types.ts # API 请求/响应类型定义(Task、Asset、Style、PipelineProgress 等)
├── stores/ # zustand stores
-│ ├── project.ts # 工程风格(从 API 加载 + 本地编辑 + 持久化回写)
+│ ├── project.ts # 当前工程(id、风格、任务列表)
│ ├── task.ts # 当前任务草稿(用户文本、素材类型、任务风格覆盖、技术参数)
-│ └── generation.ts # 生成状态(jobId、进度、阶段、结果、错误)
+│ └── generation.ts # 生成状态(taskId、进度、阶段、结果、错误)
├── pages/ # 页面级组件
│ ├── ProjectPage.tsx # 工程首页:工程风格配置 + 任务列表
│ ├── GeneratePage.tsx # 生成页:提示词构建 + 提交
@@ -36,7 +36,7 @@ frontend/src/
│ ├── StyleSelector.tsx # 风格选择器
│ ├── PromptEditor.tsx # 三段式提示词编辑器
│ ├── GenerateForm.tsx # 生成表单
-│ ├── ProgressBar.tsx # 管线进度条(显示当前阶段)
+│ ├── ProgressBar.tsx # 管线进度条(显示当前阶段 + 重试状态)
│ └── AssetPreview.tsx # 素材预览(spritesheet 预览、单帧预览)
├── hooks/ # 自定义 Hooks
│ ├── useGenerate.ts # 提交生成任务 + WebSocket 订阅进度
@@ -58,95 +58,104 @@ frontend/src/
## 组件树
-```
-
-
-
- # 工程风格编辑
- # 历史任务列表
-
-
-
- # sprite / background / UI / animation
- # 任务风格覆盖(基于工程风格,高亮差异)
- # 三段式提示词预览 & 编辑
- # 分辨率、帧数等技术参数
-
- # 提交后显示管线进度
-
-
- # 素材预览
- # 元数据展示
- # 下载素材
-
-
-
+```mermaid
+graph TD
+ App --> Router
+ Router --> ProjectPage
+ Router --> GeneratePage
+ Router --> ResultPage
+
+ ProjectPage --> StyleSelector["StyleSelector(工程风格编辑)"]
+ ProjectPage --> TaskList["TaskList(历史任务列表)"]
+
+ GeneratePage --> GenerateForm
+ GenerateForm --> AssetTypePicker["AssetTypePicker"]
+ GenerateForm --> StyleSelector2["StyleSelector(任务风格覆盖)"]
+ GenerateForm --> PromptEditor["PromptEditor(三段式预览)"]
+ GenerateForm --> ParamsForm["ParamsForm(分辨率、帧数)"]
+ GeneratePage --> ProgressBar["ProgressBar(管线进度)"]
+
+ ResultPage --> AssetPreview["AssetPreview(素材预览)"]
+ ResultPage --> MetadataPanel["MetadataPanel(元数据)"]
+ ResultPage --> DownloadButton["DownloadButton"]
```
## 核心交互流程
### 生成流程
-```
-1. 用户进入 GeneratePage
-2. 填写文本描述(prompt)
-3. 选择素材类型(sprite / background / UI / animation)
-4. StyleSelector 展示工程风格,用户可点选覆盖(任务风格)
-5. PromptEditor 实时预览三段式提示词:
- - 【主题】从用户文本提取
- - 【约束】从风格选择自动生成(含负面提示词)
- - 【内容】从素材类型 + 技术参数生成
-6. 用户确认后提交
-7. 前端 POST /api/v1/generate → 获取 jobId
-8. 建立 WebSocket 连接 /api/v1/generate/:jobId/ws
-9. ProgressBar 实时显示管线阶段:PromptBuilder → AssetGenerator → QualitySupervisor → FormatAdapter
-10. 管线完成后跳转 ResultPage
+```mermaid
+sequenceDiagram
+ actor User
+ participant GP as GeneratePage
+ participant API as 后端 API
+ participant WS as WebSocket
+
+ User->>GP: 填写 prompt、选择素材类型
+ User->>GP: StyleSelector 点选任务风格覆盖
+ GP->>GP: PromptEditor 实时预览三段式提示词
+ User->>GP: 点击提交
+ GP->>API: POST /api/v1/generate
+ API-->>GP: 返回 taskId
+ GP->>WS: ws://host/api/v1/tasks/:taskId/ws
+ loop 管线执行中
+ WS-->>GP: PipelineProgress(stage + progress)
+ GP->>GP: ProgressBar 更新阶段和进度
+ end
+ WS-->>GP: format_adapter completed + assets
+ GP->>GP: 跳转 ResultPage
```
-### WebSocket 消息格式
+管线阶段对应 Eino Graph 节点,ProgressBar 展示:
-```typescript
-interface PipelineProgress {
- stage: 'prompt_builder' | 'asset_generator' | 'quality_supervisor' | 'format_adapter';
- status: 'running' | 'completed' | 'failed';
- progress: number; // 0-100
- message?: string; // 阶段描述
- result?: { // 仅在 format_adapter completed 时返回
- assets: Asset[];
- };
- error?: string; // 仅在 failed 时返回
-}
-```
+| 阶段 | 说明 | ProgressBar 展示 |
+|------|------|-----------------|
+| `prompt_builder` | 合并风格,生成三段式提示词 | 第 1 步 |
+| `asset_generator` | 调用 AI 推理 API 出图 | 第 2 步 |
+| `quality_supervisor` | 视觉模型质检 | 第 3 步(可能回退到第 1 步) |
+| `format_adapter` | 格式转换、spritesheet 打包 | 第 4 步 |
+
+质检重试时,ProgressBar 显示回退动画和重试次数。
### 风格编辑流程
-```
-1. 用户进入 ProjectPage
-2. StyleSelector 从 API 加载工程风格(GET /projects/:id/style)
-3. 用户按分类点选风格键值对(美术风格、色调、线条、场景、光照、情绪)
-4. 实时展示当前风格配置
-5. 保存 → PUT /projects/:id/style
-6. 工程风格持久化到后端,后续生成任务自动继承
+```mermaid
+sequenceDiagram
+ actor User
+ participant SP as StyleSelector
+ participant API as 后端 API
+
+ User->>SP: 进入 ProjectPage
+ SP->>API: GET /api/v1/projects/:id/style
+ API-->>SP: 返回 kvPairs
+ SP->>SP: 按分类展示风格键值对
+ User->>SP: 点选/修改风格
+ SP->>SP: 实时展示当前配置
+ User->>SP: 点击保存
+ SP->>API: PUT /api/v1/projects/:id/style
+ API-->>SP: 保存成功
```
## 状态管理
### zustand stores
-**project store** — 工程风格
+**project store** — 当前工程
```typescript
interface ProjectStore {
projectId: string;
+ name: string;
style: Record; // kvPairs
loading: boolean;
+ loadProject: (projectId: string) => Promise;
loadStyle: (projectId: string) => Promise;
updateStyle: (kvPairs: Record) => void;
saveStyle: () => Promise;
}
```
-**task store** — 任务草稿
+**task store** — 任务草稿(对应后端 `PipelineInput`)
```typescript
interface TaskStore {
@@ -156,10 +165,10 @@ interface TaskStore {
params: {
resolution: number;
frames?: { directions: number; framesPerDirection: number };
- format: string;
+ format: 'spritesheet' | 'individual';
};
setPrompt: (text: string) => void;
- setAssetType: (type: string) => void;
+ setAssetType: (type: TaskStore['assetType']) => void;
toggleTaskStyle: (key: string, value: string) => void;
setParams: (params: Partial) => void;
reset: () => void;
@@ -170,15 +179,23 @@ interface TaskStore {
```typescript
interface GenerationStore {
- jobId: string | null;
- stage: PipelineProgress['stage'] | null;
+ taskId: string | null;
+ stage: PipelineStage | null;
progress: number;
status: 'idle' | 'submitting' | 'running' | 'completed' | 'failed';
+ retryCount: number;
+ rejectReason: string | null;
assets: Asset[];
error: string | null;
submit: (projectId: string, task: TaskStore) => Promise;
reset: () => void;
}
+
+type PipelineStage =
+ | 'prompt_builder'
+ | 'asset_generator'
+ | 'quality_supervisor'
+ | 'format_adapter';
```
## API 封装
@@ -188,7 +205,79 @@ interface GenerationStore {
- baseURL 从环境变量读取,开发模式默认 `/api/v1`
- 所有响应按 `{ code, message, data }` 解包,`code !== 0` 时抛错
- 统一 401/403/500 错误处理
-- WebSocket 连接封装为 `createJobSocket(jobId)` 返回可订阅对象
+- WebSocket 连接封装为 `createTaskSocket(taskId)` 返回可订阅对象
+
+### API 类型定义(api/types.ts)
+
+```typescript
+// 请求
+interface CreateProjectRequest {
+ name: string;
+}
+
+interface GenerateRequest {
+ projectId: string;
+ prompt: string;
+ assetType: 'sprite' | 'background' | 'ui' | 'animation';
+ taskStyle?: Record;
+ params?: {
+ resolution?: number;
+ frames?: { directions?: number; framesPerDirection?: number };
+ format?: 'spritesheet' | 'individual';
+ };
+}
+
+interface UpdateStyleRequest {
+ kvPairs: Record;
+}
+
+// 响应
+interface Project {
+ id: string;
+ name: string;
+ style: { kvPairs: Record };
+ createdAt: string;
+}
+
+interface Task {
+ id: string;
+ projectId: string;
+ prompt: string;
+ assetType: string;
+ status: 'pending' | 'running' | 'completed' | 'failed';
+ stage?: PipelineStage;
+ progress?: number;
+ retryCount?: number;
+ createdAt: string;
+ updatedAt: string;
+}
+
+interface Asset {
+ id: string;
+ url: string;
+ format: string;
+ width: number;
+ height: number;
+ metadata: {
+ frameWidth?: number;
+ frameHeight?: number;
+ frameCount?: number;
+ directions?: number;
+ };
+}
+
+// WebSocket 消息
+interface PipelineProgress {
+ stage: PipelineStage;
+ status: 'running' | 'completed' | 'failed';
+ progress: number;
+ message?: string;
+ retryCount?: number;
+ rejectReason?: string;
+ result?: { assets: Asset[] };
+ error?: string;
+}
+```
## 预设风格键分类
diff --git a/docs/multi-agent-pipeline.md b/docs/multi-agent-pipeline.md
index c5355de..6b50dc8 100644
--- a/docs/multi-agent-pipeline.md
+++ b/docs/multi-agent-pipeline.md
@@ -1,12 +1,13 @@
# 多阶段生成管线
-gen2d 的核心生成流程采用四阶段顺序管线,上一步输出即下一步输入,无需编排。
+gen2d 的核心生成流程采用 Eino `compose.Graph` 编排的四阶段管线,含质检不通过时的重生成分支。
```mermaid
flowchart LR
A["PromptBuilder\n(提示词工程)"] --> B["AssetGenerator\n(AI 出图)"]
B --> C["QualitySupervisor\n(质检)"]
- C --> D["FormatAdapter\n(格式适配)"]
+ C -- pass --> D["FormatAdapter\n(格式适配)"]
+ C -- fail --> A
```
---
@@ -42,32 +43,18 @@ finalStyle = merge(projectStyle.KVPairs, taskStyle.KVPairs)
- **输入**:PromptBuilder 的输出
- **输出**:原始生成图片(单张或多张)
- **职责**:调用 AI 推理 API 出图。不同素材类型的差异化需求已在上一步注入提示词中。
+- **接口规范**:统一采用 OpenAI 兼容的 `/v1/images/generations` 格式。多张生成通过 `n` 参数请求,实际支持数量取决于后端模型(如 DALL-E 3 仅支持 `n=1`,需多次调用模拟多张)。
## 3. QualitySupervisor
- **输入**:原始生成图片 + 风格配置
-- **输出**:质量评分 + 是否通过
-- **职责**:评估素材质量,不通过则触发重新生成(最多 3 次)。
+- **输出**:通过 / 不通过 + 不通过的原因
+- **职责**:通过视觉模型检查素材是否符合提示词要求与风格约束。不通过时,Graph 分支将 RejectReason 回填到 PipelineState,路由回 PromptBuilder 重新生成(最多 3 次,超过则降级输出)。
## 4. FormatAdapter
- **输入**:通过质检的图片 + 素材类型
- **输出**:游戏引擎可用的素材文件 + 元数据
- **职责**:格式转换、spritesheet 打包、元数据生成。
+- **示例**:输入 4 张 64×64 的角色行走帧 PNG → 输出一张 256×64 的 spritesheet + JSON 元数据(帧尺寸、帧数、锚点偏移),可直接拖入 Unity 2D Animation 使用。
----
-
-## 预设风格键分类
-
-前端以分类标签组织,用户点选:
-
-| 分类 | 键名 | 可选值示例 |
-|------|------|-----------|
-| 美术风格 | `artStyle` | pixel, cartoon, hand-drawn, vector, flat |
-| 色调 | `palette` | warm, cool, neutral, vibrant, muted, monochrome |
-| 线条 | `lineWeight` | none, thin, medium, thick |
-| 场景 | `scene` | forest, dungeon, city, space, underwater, desert |
-| 光照 | `lighting` | bright, dim, dramatic, ambient, neon |
-| 情绪 | `mood` | cheerful, dark, mysterious, epic, calm |
-
-(具体键值后续可扩展,这里是初始集合)
diff --git a/docs/style-keys.md b/docs/style-keys.md
new file mode 100644
index 0000000..cc20738
--- /dev/null
+++ b/docs/style-keys.md
@@ -0,0 +1,14 @@
+# 预设风格键分类
+
+前端以分类标签组织,用户点选:
+
+| 分类 | 键名 | 可选值示例 |
+|------|------|-----------|
+| 美术风格 | `artStyle` | pixel, cartoon, hand-drawn, vector, flat |
+| 色调 | `palette` | warm, cool, neutral, vibrant, muted, monochrome |
+| 线条 | `lineWeight` | none, thin, medium, thick |
+| 场景 | `scene` | forest, dungeon, city, space, underwater, desert |
+| 光照 | `lighting` | bright, dim, dramatic, ambient, neon |
+| 情绪 | `mood` | cheerful, dark, mysterious, epic, calm |
+
+(具体键值后续可扩展,这里是初始集合)