topfans/docs/laser-card-style-pool.md
Lenticular Studio Agent 67cf3d4177 chore: 清理 laserCompositor 微服务残留
- 删除已弃用的 compositor_client.go
- 删除激光合成微服务代码
- 添加 gateway 合成控制器和测试文件
- 添加 Dify prompt 补丁脚本

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-23 22:44:03 +08:00

228 lines
9.9 KiB
Markdown

# 镭射卡风格池 — 轻量抽卡设计
> 配套代码:
> - [`frontend/utils/laser-card/stylePool.js`](../frontend/utils/laser-card/stylePool.js) — 45 个风格定义
> - [`frontend/utils/laser-card/gacha.js`](../frontend/utils/laser-card/gacha.js) — 加权抽卡算法
> - [`frontend/utils/laser-card/laserPresets.js`](../frontend/utils/laser-card/laserPresets.js) — 入口
> - [`frontend/composables/useLaserDifyGenerate.js`](../frontend/composables/useLaserDifyGenerate.js) — 抽卡调度
## 设计哲学
**不要游戏化的抽卡 UI,要"概念上的抽卡" —— 每次点击生成,5 张卡都是不同的镭射风格,带来视觉上的惊喜感。**
- ✅ 算法按稀有度加权随机(常见款多,稀有款少)
- ✅ 每次生成内容都不一样(grating_config + bg_prompt 全部按抽卡结果)
-**不在 UI 显示稀有度徽章、星级、配色**(避免游戏化)
## 风格池规模与分层
| 档位 | 数量 | 单抽概率 | 视觉特征 |
|---|---|---|---|
| **N** (Normal) | 10 | 50% | 银底基础款,常见 |
| **R** (Rare) | 10 | 30% | Refractor 类,有色彩倾向 |
| **SR** (Super Rare) | 10 | 15% | Mosaic/Atomic/横纹限量 |
| **SSR** | 9 | 4% | Full Art/Rainbow/Gold |
| **UR** (Ultra Rare) | 6 | 1% | Secret/Star/Prismatic |
合计 45 个,保底:第 5 张若前 4 张全是 N,强制抽 R+。
## 风格分类(完整列表)
### N(10)—— 基础银底
classic_silver / moonlight_silver / pearl_basic / frost_silver / warm_silver / graphite_silver / rosegold_base / sky_silver / cream_silver / steel_silver
### R(10)—— Refractor 类
prizm_blue / prizm_red / prizm_green / prizm_orange / prizm_purple / prizm_pink / topps_refractor / reverse_holo / bowman_chrome / wave_blue
### SR(10)—— 限量工艺
mosaic / shimmer / neon_molectric / disco / topps_atomic / hyper_prizm / cracked_ice / yugioh_super / yugioh_ultra / ws_sr
### SSR(9)—— 稀有工艺
rainbow_rare / prizm_gold / prizm_black / cosmic_prizm / prizm_shattered / topps_sepia / pokemon_fullart / yugioh_secret / gold_secret
### UR(6)—— 终极稀有
pokemon_star / superfractor / yugioh_starlight / yugioh_platinum / yugioh_prismatic / ws_ssp
## 用户提示词的处理方式
**核心原则:用户描述为主轴,holographic 作为载体/底材**
当用户输入 prompt 时,前端 `wrapUserPrompt()` 会构造如下结构的 prompt:
```
[用户原始描述,例如 "海报文字风"],
on a [color_hint] holographic metallic card substrate,
single diagonal sheen sweep at [angle]°,
soft pearlescent foil coverage [coverage]%,
subtle rainbow diffraction with intensity [intensity]%,
refined laser card design,
no text, no people, no watermark
```
**关键设计**:
- ✅ 用户描述放**最前**(主轴 → AI 优先遵循)
- ✅ Holographic 描述在**主体之后**(底材约束 → 保持激光卡美学)
- ✅ 抽卡仍生效(5 张图 grating_config / overlay 各不同,共享同一 user 主题)
- ✅ 后端 controller 不再二次拼接(前端 user_prompt 字段传空)
**示例**:
| 用户输入 | 包装后 prompt(节选) |
|---|---|
| `海报文字风` | `海报文字风, on a silver holographic metallic card substrate, single diagonal sheen sweep at 135°, soft pearlescent foil coverage 75%, ..., no text, no people, no watermark` |
| `赛博朋克霓虹` | `赛博朋克霓虹, on a warm silver holographic metallic card substrate, single diagonal sheen sweep at 120°, soft pearlescent foil coverage 85%, ...` |
| `可爱卡哇伊` | `可爱卡哇伊, on a silver holographic metallic card substrate, single diagonal sheen sweep at 130°, soft pearlescent foil coverage 58%, ...` |
5 张抽卡结果会共享同一个 user 主题,但每张图的金属底材(角度/覆盖率/强度)不同 → 用户在固定风格下获得多样性。
## Style Schema
```js
{
id: 'prizm_blue',
rarity: 'R',
display_name_zh: '蓝棱镜',
display_name_en: 'Prizm Blue',
weight: 7, // 同档位内相对权重
family: 'panini_prizm',
reference: { series, year, note }, // 真实卡牌系列溯源
grating_config: {
sheen_band_angle: 120,
sheen_speed: 0.42,
sheen_intensity: 0.38,
foil_coverage: 0.70,
backdrop_tone: '#A8ACB2'
},
color_hint: '电光蓝 宝石蓝',
bg_prompt: '...', // 英文全量 prompt(锚定工艺 + sheen 角度 + negative)
overlay_prompt: '...',
tags: 'blue,sport,prizm'
}
```
## 抽卡算法
[`gacha.js`](../frontend/utils/laser-card/gacha.js) 主入口 `drawGachaStyles(count = 5)`:
1. **单张稀有度**:按 `RARITY_WEIGHTS = { N: 50, R: 30, SR: 15, SSR: 4, UR: 1 }` 加权
2. **同档内**:按 style.weight 二次加权
3. **不重复**:5 张 ID 集合 size === 5
4. **保底**:第 5 张时若前 4 张全是 N,强制抽 R+
纯 JS,~80 行,无第三方依赖。
## 集成模式:虚拟 ID + 实际 prompt
Dify workflow 仅识别旧 5 个 preset_id,但 pool 中有 45 个风格。采用**虚拟 ID 模式**让 pool 内容真正生效:
```
抽卡选中: cosmic_prizm (SSR)
preset_id 字段: 'dream'/'classic'/'holoFull'/'ice'/'sunset' 之一(循环取,保 Dify 兼容)
grating_config: cosmic_prizm 的 grating_config(深空紫)
bg_prompt: cosmic_prizm 的 bg_prompt(深空紫渐变 + 星点)
overlay_prompt: cosmic_prizm 的 overlay_prompt
Dify 收到 prompt + grating_config → 生成对应风格的图
前端收到 5 张图,用户感受到"这次是宇宙主题,下次是蓝棱镜"的惊喜
```
**优势**:
- ✅ Dify workflow 不重发布
- ✅ 后端零改动
- ✅ 数据库零迁移
- ✅ 用户实际感受到 45 种风格的不同
### 装饰层(overlay)与背景层(bg)对齐
Dify yml 中**装饰层 prompt 模板**与背景层采用相同结构 —— 简短引导 + `overlay_prompts_all` 为主体:
```yaml
# 修复前(硬编码压制了每张图的差异)
"柔和光晕叠加层,微光粒子飘散...不要边框,不要装饰线...生成5种不同效果: {{overlay_prompts_all}}"
# 修复后(简短引导 + 每张图的 overlay_prompt 主导)
"镭射卡装饰叠加层,透明背景,软光晕与微光粒子飘散...生成5种不同风格的装饰变体: {{overlay_prompts_all}}"
```
修复后,pool 中 45 个 style 各自的 `overlay_prompt`(如"star scatter corner"、"floral corner accents"、"geometric mosaic border"等)能真正生效,5 张装饰图会按风格变化。
## 现有 5 个 Preset 的提示词优化
模板结构(5 段式):
1. **整体氛围**(mood)
2. **工艺特征**(holographic/refractor/stripe/foil coverage)
3. **色彩描述**(具体颜色 + 渐变)
4. **光线描述**(`single diagonal sheen sweep at 135°` 与 grating_config 对齐)
5. **负向提示**(`no text, no people, no watermark`)
| Preset | 优化后 prompt(摘要) |
|---|---|
| **dream** | `soft dreamy holographic background, lavender and pink layered gradient, ..., single diagonal sheen sweep at 135°, dreamy celestial mood, no text, no people, no watermark` |
| **classic** | `classic vintage refractor holographic surface, warm champagne and pale gold gradient, ..., single diagonal sheen sweep at 120°, soft film grain texture, refined retro cardstock mood, ...` |
| **holoFull** | `full holographic tech background, silver metallic base with pearl glow, geometric neon circuit lines and star sparks, ..., vivid diagonal sheen sweep at 150°, high foil coverage 95%, ...` |
| **ice** | `cool ice holographic background, pale blue-gray base with crystalline frost texture, ..., fast diagonal sheen sweep at 110° (speed 0.50), crisp clean winter mood, ...` |
| **sunset** | `warm sunset holographic background, rose-gold base with amber and pink layered glow, ..., gentle diagonal sheen sweep at 140° (speed 0.40), romantic dreamy mood, ...` |
**为什么优化后更稳定**:AI 对抽象形容词理解漂移大,对"holographic + 量化角度"响应稳定。优化后 prompt 与 `grating_config` 字段对账,避免"参数是冷银但生图是紫色"的不一致。
## 兼容性矩阵
| 既有调用方 | 改动前行为 | 改动后行为 | 风险 |
|---|---|---|---|
| `success.vue` `getLaserPresetByIndex(0)` | 返回 `dream` | 完全一致(`PRESET_VARIANTS` 未动) | 零 |
| `useLaserMint` 接口 | 不变 | 不变 | 零 |
| `useLaserBatchGenerate.run()` | presetIds=null → 默认 5 个 | presetIds=null → submit 内部按 userPrompt 抽卡 | 零 |
| `laserBatchExport.js` | 旧 5 个 | 保留不变 | 零 |
| 后端 Gateway | 接收 render_configs | **零改动** | 零 |
| Dify workflow | 接收 5 个 preset_id | **零改动** | 零 |
| 数据库 | 存 preset_id 字符串 | **零改动** | 零 |
## 验收方法
### 单元测试(浏览器 console)
```js
// 抽卡分布
const gacha = await import('/utils/laser-card/gacha.js')
const dist = { N: 0, R: 0, SR: 0, SSR: 0, UR: 0 }
for (let i = 0; i < 1000; i++) {
gacha.drawGachaStyles(5).forEach(s => dist[s.rarity]++)
}
console.log(dist) // 应大致 N:2500 R:1500 SR:750 SSR:200 UR:50
// 保底
const noRplus = Array(100).fill(0).filter(() =>
gacha.drawGachaStyles(5).every(s => s.rarity === 'N')
).length
console.log('保底失败次数:', noRplus) // 应为 0
```
### 端到端验证
1. 启动 dev server
2. 进入镭射卡生成页
3. **不输入** prompt → 点击生成
4. 观察:5 张卡出现,每张视觉不同(不同 grating 角度/不同配色)
5. **再次点击**生成 → 又一组 5 张,与上次不同
**惊喜感验证**:连续生成 5 次,每次 5 张卡,应该看到:
- 至少 1-2 张明显色彩倾向(R+ 卡)
- 偶有 1 张视觉特别"满"(SR+ 高 foil_coverage)
- 极少数会"出彩"(UR 的全光谱彩虹)
**DevTools Network 验证**:
- `preset_codes` = `["dream","classic","holoFull","ice","sunset"]`(虚拟 ID)
- `render_configs[].bg_prompt` = 每次都不同(抽卡选出的)
-`_meta` / `rarity` 等额外字段污染后端
## 不在 Phase 1 范围内
- ❌ Dify workflow 真实派发 45 个 preset_id(需重发)
- ❌ "重抽一次"按钮(当前每次生成都是新抽卡)
- ❌ 抽卡动画(目前直接展示)
- ❌ 收藏册(用户历史)
- ❌ 后端 rarity 统计