feat: expand gen2D deck with deep technical slides and interactive elements
Deploy Slides / build-and-deploy (push) Successful in 1m8s

- Add 5 new slide pages: eino-deep-dive, ratelimit-overview, ratelimit-lua, async-deep-dive, harness
- Add 4 new drawio SVGs: pipeline-detail, ratelimit, async-task, harness
- Update all existing slides with v-clicks progressive reveal
- Add presenter notes and interactive prompts
- Expand slides.md to register all new pages (16 → 25+ slides)
- Fix text overflow by trimming content per Item block
- Add Eino Graph deep dive: WithGenLocalState, Pre/Post Handler, branch routing
- Add rate limiting section: algorithm comparison, Lua script, Gin middleware
- Add async task deep dive: TaskQueue FIFO, context progress injection
- Add consistency section: style/pipeline/data/deployment 4-layer guarantees
This commit is contained in:
2026-05-30 22:42:23 +08:00
parent 659c22707b
commit 67ee76e58d
24 changed files with 878 additions and 33 deletions
+5 -1
View File
@@ -1,5 +1,9 @@
# 系统架构全景
<div class="mt-2 flex justify-center">
<img src="../public/architecture.svg" class="w-full max-h-[500px] object-contain" />
<img src="../public/architecture.svg" class="w-full max-h-[450px] object-contain" />
</div>
<!--
指向架构图各层讲解:前端 → API → 管线 → 存储
-->
+71
View File
@@ -0,0 +1,71 @@
# 异步任务 — 队列与调度
FIFO 串行执行,信号驱动调度
<div class="mt-2">
```go {1-6|8-12|all}
type TaskQueue struct {
mu sync.Mutex
jobs []*TaskJob
ready chan struct{} // 新任务到达信号
stop chan struct{} // 优雅关闭信号
}
func (q *TaskQueue) Enqueue(job *TaskJob) {
q.mu.Lock()
q.jobs = append(q.jobs, job)
q.mu.Unlock()
q.ready <- struct{}{} // 唤醒 run() 协程
}
```
</div>
<v-clicks>
<Item title="Enqueue → run() → processNext() 流程">
提交任务入队并发送信号。`run()` 协程阻塞等待信号,收到后调用 `processNext()` 取队首任务执行。FIFO 保证任务按提交顺序串行处理。
</Item>
<Item title="每个 Job 是一个闭包">
`TaskJob` 封装了 `context.Context` 和执行函数。闭包捕获任务参数,context 控制超时和取消,确保每个任务独立且可中断。
</Item>
</v-clicks>
---
# 进度推送与 WebSocket
Context 注入 + 多通道推送
<div class="mt-2">
```go {1-3|5-8|all}
func WithProgressReporter(ctx context.Context, r ProgressReporter) context.Context {
return context.WithValue(ctx, progressCtxKey, r)
}
// 管线节点中使用
reporter := GetProgressReporter(ctx)
reporter.Report(Progress{Stage: "prompt", Percent: 25})
```
</div>
<v-clicks>
<Item title="进度阶段划分">
5%(任务创建)→ 25%(提示词优化完成)→ 60%(素材生成完成)→ 80%(质检通过)→ 100%(格式适配 + 上传)。每个阶段由对应节点触发回调。
</Item>
<Item title="双通道推送">
**WebSocket**:`ws://host/api/v1/tasks/:taskId/ws`,实时推送进度和结果。**HTTP 轮询**:`GET /api/v1/tasks/:taskId`,兼容降级方案。
</Item>
</v-clicks>
<div class="flex justify-center mt-2">
<img src="../public/async-task.svg" class="w-full max-h-[180px] object-contain" />
</div>
+4
View File
@@ -2,6 +2,8 @@
提交即返回,后台并发执行
<v-clicks>
<Item title="📤 任务提交 → 后台执行">
1. 提交请求 → 创建 Task(`running`)→ 立即返回 `taskId`
@@ -22,3 +24,5 @@
质检重试最多 3 次,任务级重试 1 次,超限降级输出。
</Item>
</v-clicks>
+6 -6
View File
@@ -3,20 +3,20 @@
四表结构,清晰的实体关系
<div class="flex justify-center">
<img src="../public/data-model.svg" class="w-full max-h-[250px] object-contain" />
<img src="../public/data-model.svg" class="w-full max-h-[220px] object-contain" />
</div>
<div class="grid grid-cols-2 gap-4 mt-2">
<Item title="👤 USER + PROJECT">
- 用户:`id`(base62)、`username`、`password_hash`(bcrypt)
- 工程:`id`、`user_id`、`name`、`description`
用户:`id`(base62)、`username`、`password_hash`(bcrypt)
工程:`id`、`user_id`、`name`
</Item>
<Item title="🎨 STYLE + TASK + ASSET">
- 风格表:`project_id` 一对一,`kv_pairs` JSON 存储
- 任务:`prompt`、`status`、`style_override`
- 素材:`file_url`、`metadata`,存于七牛云 Kodo
风格表:`project_id` 一对一,`kv_pairs` JSON
任务:`prompt`、`status`、`progress`
素材:`file_url`、`metadata`
</Item>
</div>
+4
View File
@@ -2,6 +2,8 @@
两项关键的工程优化
<v-clicks>
<Item title="🔍 请求去重(Request Dedup)">
`hash(prompt + assetType + params)` 生成唯一键,`sync.Map` 内存缓存。相同参数并发请求自动合并,返回已有 `taskId`,节省 API 调用成本。
</Item>
@@ -9,3 +11,5 @@
<Item title="📡 WebSocket 实时推送">
建立 `ws://host/api/v1/tasks/:taskId/ws` 连接,管线各阶段推送进度、质检重试信息、最终结果。同时兼容 HTTP 轮询方案。
</Item>
</v-clicks>
+7 -3
View File
@@ -3,13 +3,17 @@
Docker Compose 双容器 + 一键部署
<div class="flex justify-center">
<img src="../public/deployment.svg" class="w-full max-h-[300px] object-contain" />
<img src="../public/deployment.svg" class="w-full max-h-[260px] object-contain" />
</div>
<v-clicks>
<Item title="🐳 容器化方案">
nginx 容器:Vite 构建产物 + 反向代理 `/api`。app 容器:Go 二进制 + SQLite 挂载。内部网络通信,nginx 暴露 8080 端口。
nginx 容器:Vite 构建产物 + 反向代理 `/api`。app 容器:Go 二进制 + SQLite 挂载。nginx 暴露 8080 端口。
</Item>
<Item title="🚀 一键部署">
`docker compose up -d --build` 或 `./deploy.sh`。Gitee CI/CD 自动触发,SQLite 嵌入式零外部依赖。
`docker compose up -d --build` 或 `./deploy.sh`。Gitee CI/CD 自动触发,SQLite 零外部依赖。
</Item>
</v-clicks>
+76
View File
@@ -0,0 +1,76 @@
# Eino Graph 深入 — 构建与编排
有向图构建与本地状态管理
<div class="mt-2">
```go {1-5|7-11|all}
g := compose.NewGraph[PipelineInput, PipelineOutput](
compose.WithGenLocalState(func(ctx context.Context) *PipelineState {
return &PipelineState{} // 每次调用获得全新状态
}),
)
g.AddLambdaNode(nodePromptOptimizer, &compose.Lambda{
StatePreHandler: preparePromptInput, // 从 state 读取输入
StatePostHandler: storePromptOutput, // 结果写回 state
})
```
</div>
<v-clicks>
<Item title="WithGenLocalState — 隔离的执行上下文">
每次 `graph.Invoke()` 调用生成独立的 `PipelineState`,避免并发调用间的状态污染。State 在节点间通过 Pre/Post Handler 流转。
</Item>
<Item title="StatePreHandler / StatePostHandler 模式">
PreHandler 从全局 State 提取当前节点所需字段;PostHandler 将节点输出写回 State。节点本身无状态,所有数据经 State 中转。
</Item>
</v-clicks>
---
# Eino Graph 深入 — 分支路由与重试
QualitySupervisor 驱动的条件分支
<div class="mt-2">
```go {1-10|all}
g.AddBranch(nodeQualitySupervisor, compose.NewGraphBranch(
func(ctx context.Context, _ PipelineInput) (string, error) {
var next string
_ = compose.ProcessState[*PipelineState](ctx,
func(_ context.Context, state *PipelineState) error {
next = state.NextNode // 质检结果决定路由
return nil
})
return next, nil
},
map[string]bool{
nodePromptOptimizer: true,
nodeFormatAdapter: true,
},
))
```
</div>
<v-clicks>
<Item title="路由逻辑">
质检通过 → FormatAdapter 直接输出;不通过且 retry < 3 → PromptOptimizer 重新优化;不通过且 retry >= 3 → FormatAdapter 降级输出。
</Item>
<Item title="管线全貌">
下图展示了完整的分支路由与重试机制,包括状态流转和降级路径。
</Item>
</v-clicks>
<div class="flex justify-center mt-2">
<img src="../public/pipeline-detail.svg" class="w-full max-h-[200px] object-contain" />
</div>
+5 -1
View File
@@ -2,7 +2,7 @@
<div class="mt-2">
```go
```go {all|1-4|6-10|all}
type PipelineInput struct {
UserID, ProjectID, TaskID string
Prompt, AssetType string
@@ -22,6 +22,8 @@ type PipelineOutput struct {
</div>
<v-clicks>
<Item title="graph.Build() — 构建管线">
`compose.NewGraph` 创建有向图,注册四节点。质检节点条件分支:通过 → 格式适配,不通过 → 回退提示词优化。
</Item>
@@ -29,3 +31,5 @@ type PipelineOutput struct {
<Item title="graph.Invoke() — 执行管线">
传入 `PipelineInput`,按拓扑顺序执行。每节点完成回调更新进度(5% → 25% → 60% → 80% → 100%)。
</Item>
</v-clicks>
+62
View File
@@ -0,0 +1,62 @@
# 一致性保障 — 风格与管线
多层一致性确保输出可控
<Item title="风格一致性 — 合并策略">
Project Style(基底)+ Task Override(覆盖)→ `mergeStyle()` 逐键合并。工程风格定义全局基调,任务风格按需微调,互不干扰。
</Item>
<div class="mt-2">
```go
func mergeStyle(projectStyle, taskStyle map[string]string) map[string]string {
result := make(map[string]string)
for k, v := range projectStyle { result[k] = v }
for k, v := range taskStyle { result[k] = v } // 覆盖
return result
}
```
</div>
<v-clicks>
<Item title="管线一致性 — Eino Graph 保证">
Graph 强制拓扑顺序执行,State 每次调用独立生成。节点无状态,数据经 State 中转,避免并发污染。
</Item>
<Item title="质量一致性 — 质检 + 重试 + 降级">
QualitySupervisor 自动质检,不合格重试(最多 3 次),超限降级输出。确保最终产物始终可用。
</Item>
</v-clicks>
---
# 一致性保障 — 数据与部署
从存储到部署的全链路一致性
<v-clicks>
<Item title="数据一致性 — ACID 事务">
SQLite 单文件 ACID + GORM 事务包裹关键操作。CASCADE 删除保证级联清理,外键约束维护引用完整性。
</Item>
<Item title="ID 一致性 — 前缀 + 随机">
格式:`prefix_base62`(如 `task_9bN4vR`)。`crypto/rand` 生成 base62 编码,全局唯一且可读。前缀标识实体类型,便于调试。
</Item>
<Item title="部署一致性 — 容器化 + 确定性构建">
Docker Compose 双容器(nginx + app),确定性构建保证环境一致。CI/CD 推送 master 自动触发部署。
</Item>
<Item title="配置一致性 — Viper 多层覆盖">
配置优先级:配置文件 → 环境变量 → 默认值。`Viper` 统一管理,开发/测试/生产环境无缝切换。
</Item>
</v-clicks>
<div class="flex justify-center mt-2">
<img src="../public/harness.svg" class="w-full max-h-[160px] object-contain" />
</div>
+4
View File
@@ -1,5 +1,7 @@
# 核心亮点总结
<v-clicks>
<Item title="🤖 多智能体协作管线">
Eino `compose.Graph` 编排四阶段智能体,支持质检重试(最多 3 次)与降级输出。
</Item>
@@ -15,3 +17,5 @@ WebSocket 推送各阶段进度与质检信息。相同参数并发请求自动
<Item title="🎨 风格系统 + 🚀 一键部署">
两层风格架构(Project + Task Override)保证一致性。Docker Compose + Gitee CI/CD 零依赖部署。
</Item>
</v-clicks>
+14 -6
View File
@@ -2,18 +2,26 @@
基于 Eino `compose.Graph` 编排的四阶段智能体协作管线
<v-clicks>
<Item title="① PromptOptimizer — 提示词优化">
合并工程风格 + 任务风格覆盖,生成三段式规范提示词。调用 Eino Chain 驱动 LLM 优化,无 Key 自动回退模板。
合并工程风格 + 任务风格覆盖,生成三段式规范提示词。LLM 优化,无 Key 自动回退模板。
</Item>
<Item title="② AssetGenerator — 素材生成">
调用 OpenAI 兼容的文生图 API(默认 Stability AI),根据优化后提示词生成素材。
调用 OpenAI 兼容的文生图 API,根据优化后提示词生成素材。
</Item>
<Item title="③ QualitySupervisor → ④ FormatAdapter">
视觉模型自动质检,不合格触发重优化(最多 3 次)。通过后组装 Spritesheet + 元数据,上传七牛云 Kodo。
<Item title="③ QualitySupervisor — 质检">
视觉模型自动质检,不合格触发重优化(最多 3 次)。
</Item>
<div class="mt-4 flex justify-center">
<img src="../public/pipeline.svg" class="w-full max-h-[180px] object-contain" />
<Item title="④ FormatAdapter — 格式适配">
组装 Spritesheet + 元数据,上传七牛云 Kodo。
</Item>
</v-clicks>
<div class="mt-2 flex justify-center">
<img src="../public/pipeline.svg" class="w-full max-h-[140px] object-contain" />
</div>
+11 -3
View File
@@ -1,7 +1,9 @@
# 痛点:2D 游戏素材制作
<v-clicks>
<Item title="🎨 美术成本高">
专业美术人员费用高,独立开发者难以负担。外包单张 50-500 元,迭代周期长,试错成本高。
专业美术费用高,独立开发者难以负担。外包单张 50-500 元,迭代周期长。
</Item>
<Item title="🔧 管线适配难">
@@ -9,9 +11,15 @@
</Item>
<Item title="🤖 现有 AI 工具的局限">
通用文生图模型缺乏游戏素材领域知识,无法理解"像素风"等术语,输出不可控,无质检重试。
通用文生图模型缺乏游戏素材领域知识,无法理解"像素风"等术语,输出不可控。
</Item>
<div class="mt-4 text-center text-gray-400">
</v-clicks>
<div v-click class="mt-4 text-center text-gray-400">
<b>核心诉求</b>:低门槛 · 风格一致 · 管线友好 · 质量可控
</div>
<!--
提问互动:在座各位有没有做过 2D 游戏?素材制作是不是最头疼的环节?
-->
+9 -5
View File
@@ -2,18 +2,22 @@
Eino Chain 驱动的智能提示词生成
<v-clicks>
<Item title="① 风格合并">
`finalStyle = merge(projectStyle, taskStyle)` — 工程风格为基底,任务风格逐键覆盖。自定义标签自动提取。
`finalStyle = merge(projectStyle, taskStyle)` — 工程风格为基底,任务风格逐键覆盖。
</Item>
<Item title="② 三段式提示词组装">
**主体描述**(用户输入)+ **风格约束**(美术风格、色调、线条)+ **技术规格**(尺寸、格式、透明背景)
**【主题】** 画面主体与场景 + **【风格】** 艺术风格与视觉特征 + **【技术】** 分辨率、格式等参数
</Item>
<Item title="③ LLM 优化 + 回退机制">
调用 Eino Chain 发给 DeepSeek 润色。**无 API Key 自动回退模板**,失败时注入重试上下文。
调用 Eino Chain 发给 DeepSeek 润色。无 API Key 自动回退模板,失败时注入重试上下文。
</Item>
<div class="flex justify-center">
<img src="../public/prompt-agent.svg" class="w-full max-h-[160px] object-contain" />
</v-clicks>
<div class="flex justify-center mt-2">
<img src="../public/prompt-agent.svg" class="w-full max-h-[140px] object-contain" />
</div>
+60
View File
@@ -0,0 +1,60 @@
# 令牌桶 — Redis Lua 脚本
原子操作,单次 Redis 往返
<div class="mt-2">
```lua {1-2|4-8|10-14|all}
local data = redis.call('HMGET', key, 'tokens', 'ts')
local tokens = tonumber(data[1])
local last_ts = tonumber(data[2])
-- 首次访问,初始化满桶
if tokens == nil then
tokens = burst
last_ts = now
end
-- 计算时间差,补充令牌
local delta = now - last_ts
if delta > 0 then
local refill = (delta * rate) / 1000
tokens = math.min(burst, tokens + refill)
end
```
</div>
<v-clicks>
<Item title="为什么用 Lua 脚本?">
整个令牌桶逻辑(读取 → 计算 → 扣减 → 写回)在 Redis 服务端原子执行,避免竞态条件。单次网络往返,性能最优。
</Item>
<Item title="关键逻辑">
**初始化**:首次访问设满桶令牌。**补充**:按时间差 × 速率补充,上限为 burst。**扣减**:请求到达时 tokens - 1,不足则拒绝。
</Item>
</v-clicks>
---
# 限流中间件集成
Gin 中间件 + 统一错误格式
<Item title="中间件签名">
`func RateLimit(limiter Limiter, keyFunc func(c *gin.Context) string) gin.HandlerFunc` — 可配置的 key 提取函数,支持按用户ID、IP、端点等维度限流。
</Item>
<Item title="注册方式">
- **用户级**:auth 路由组,key = userID,保护个人配额
- **全局级**:`/generate` 端点,key = endpoint,保护总容量
</Item>
<Item title="响应规范">
- 通过:设置 `X-RateLimit-Remaining` 头
- 拒绝:HTTP 429 + `Retry-After` 头 + 统一 JSON 错误体
</Item>
+34
View File
@@ -0,0 +1,34 @@
# 分布式限流 — 算法选型与架构
保护 API 端点,防止资源滥用
<div class="mt-2">
| 算法 | 优点 | 缺点 | 适用场景 |
|------|------|------|----------|
| 固定窗口计数器 | 实现简单 | 窗口边界突发 | 低精度场景 |
| 滑动窗口 | 边界平滑 | 内存开销大 | 中等精度 |
| 漏桶 | 严格平滑 | 无法应对突发 | 流量整形 |
| **令牌桶** | **平滑 + 允许突发** | 实现稍复杂 | **API 限流** |
</div>
<v-clicks>
<Item title="选择令牌桶 — 平滑限速 + 突发容忍">
令牌按固定速率填充,请求消耗令牌。桶满时可应对短时突发流量,同时保持长期平均速率稳定。
</Item>
<Item title="两级限流架构">
**用户级**:per-user 维度,防止单用户滥用。**全局级**:per-endpoint 维度,保护后端服务总容量。两级独立配置,互不影响。
</Item>
<Item title="容错策略 — Fail-Open">
Redis 不可用时限流器自动放行(fail-open),保障核心业务不因限流组件故障而中断。
</Item>
</v-clicks>
<div class="flex justify-center mt-2">
<img src="../public/ratelimit.svg" class="w-full max-h-[180px] object-contain" />
</div>
+2 -2
View File
@@ -20,10 +20,10 @@
- WebSocket 实时进度推送
- 批量生成与队列调度
- 文生图多模型适配(DALL-E、Midjourney)
- 文生图多模型适配
- 素材版本管理与历史记录
- 分布式令牌桶限流(Redis + Lua)
- 自定义协程池(NumCPU × 4 workers)
- 自定义协程池
- RabbitMQ 消息队列解耦
- Prometheus 监控 + Grafana 看板
+12 -4
View File
@@ -6,12 +6,20 @@
<v-clicks>
<Item title="🗣️ 自然语言驱动 + 🎨 风格一致">
用文字描述素材需求,AI 自动生成。工程级风格预设保证同一项目素材风格统一。
<Item title="🗣️ 自然语言驱动">
用文字描述素材需求,AI 自动生成。降低创作门槛。
</Item>
<Item title="📦 管线友好 + ✅ 质量可控">
标准尺寸输出,适配 Unity / Godot。自动质检 + 重试(最多 3 次)+ 降级兜底。
<Item title="🎨 风格一致">
工程级风格预设保证同一项目素材风格统一。
</Item>
<Item title="📦 管线友好">
标准尺寸输出,适配 Unity / Godot。
</Item>
<Item title="✅ 质量可控">
自动质检 + 重试(最多 3 次)+ 降级兜底。
</Item>
</v-clicks>
+10 -1
View File
@@ -2,8 +2,10 @@
保证一致性与灵活性的风格管理方案
<v-clicks>
<Item title="🏗️ 工程风格(Project Style)">
全局风格配置,以 kvPairs 存储(如 `artStyle=pixel`)。同一工程下所有素材自动继承,保证风格统一。
全局风格配置,以 kvPairs 存储(如 `artStyle=pixel`)。同一工程下所有素材自动继承。
</Item>
<Item title="🔀 任务风格覆盖(Task Override)">
@@ -13,3 +15,10 @@
<Item title="⚙️ 合并逻辑">
`finalStyle = merge(projectStyle, taskStyle)` — 工程风格为基底,任务风格逐键覆盖。自定义标签(`custom:` 前缀)自动提取注入提示词。
</Item>
</v-clicks>
<!--
演示:假设工程风格 artStyle=pixel, palette=warm
任务覆盖 palette=cool → 最终 palette=cool,其他继承
-->
+7
View File
@@ -36,3 +36,10 @@
</Item>
</div>
<!--
重点:为什么选 Go?为什么选 Eino?为什么 SQLite?
Go: 高并发 goroutine,编译型语言性能好
Eino: 字节跳动出品,Graph 编排天然支持分支重试
SQLite: 单实例部署,零配置,足够用
-->