docs(architecture): fan-out 重写架构/前端/运营/后端/运维 5 域(20 档 · 全 opus)
Workflow 每目标档一个 opus agent,以产品域 README 为风格样板:架构 L2 主档 + 13模块 + 生成引擎旗舰子树(11 档:README/固定游戏架构/SAA编排/引擎与运行时/验收门-W-G1/prompt治理/设计合理性裁决/OpenGame对照/WG1基准/SAA能力API-dossier/开闸接线)+ 前端 + 运营(README/变现与单位经济/渠道发行/合规闸门)+ 后端瘦主档 + 运维瘦主档。全部:人读散文 + 多图(Mermaid)+ 少黑话(专名首现解释)+ 单档 ≤2000(最大 452)+ 品牌绘境AI + 硬事实保留(门ID/数字/契约/裁决)。源档归档、§10 治理反转、~17 连带引用、L1 总索引、_index/AGENTS 对账、死链门统一留 Phase7。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
366a44b4b5
commit
fc0c164678
225
docs/architecture/前端/README.md
Normal file
225
docs/architecture/前端/README.md
Normal file
@ -0,0 +1,225 @@
|
||||
# 前端域 · 设计主文档
|
||||
|
||||
> **这是什么**:绘境AI 前端域的设计主文档,回答"**两个前端长什么样、靠什么撑起一致的体验**"。它聚焦设计体系(design system)这一层——即把颜色、间距、字体、组件、主题、多语言这些"视觉与交互的共同语言"统一沉淀下来,让所有界面看起来像同一个产品。
|
||||
> **给谁看**:前端工程师、设计师、产品经理、新加入的同事、外部技术尽调。
|
||||
> **怎么读**:先读本页建立全局认知——我们有哪两个前端、它们各服务谁、设计体系分几层、为什么这么分、创始人定下了哪些不可动摇的硬约束;需要某条具体令牌(token)的取值或某个组件的签名时,再回到代码里的权威源文件(文末有指针)。
|
||||
> **读这一页 = 前端设计体系的当前真相**。它从子系统活档 `docs/agent-specs/studio前端设计体系.md` 蒸馏而来,只取其架构骨架与关键决策的"为什么";逐令牌清单、逐组件实现、构建与走查的具体命令不在这里(分别交给代码里的 `tokens.css` 与 `.agents/skills/` 下的操作手册)。
|
||||
|
||||
---
|
||||
|
||||
## 1. 绘境AI 有两个前端
|
||||
|
||||
绘境AI 把面向不同人群的界面拆成两个独立的前端应用,它们在[架构域](../架构/README.md)里都画在"前端应用层",但服务的人和承载的能力截然不同。
|
||||
|
||||
**game-studio** 是产品前端,同时服务**创作者**和**玩家**两类终端用户。创作者在这里一句话生成游戏、预览迭代、发布到多个渠道、查看收益;玩家在这里刷"游戏信息流"(类似短视频的竖屏即刷即玩流)、点赞分享评论。它用 **Vue3 + Vant** 构建——Vant 是一套移动优先的 Vue 组件库,天然适配游戏流的滑动交互。
|
||||
|
||||
**game-admin** 是管理后台前端,服务**平台运营**和**管理员**。他们在这里做内容审核、用户管理、数据看板、合规处置等后台工作。它用 **Vue3 + Element Plus** 构建——Element Plus 是 Huijing(我们 fork 的开源 Java 企业级后台框架)官方主推的桌面端组件库,二次开发友好。
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
subgraph STUDIO["game-studio — 产品前端(消费级)"]
|
||||
direction LR
|
||||
CRT["创作者<br/>生成 / 预览 / 发布 / 收益"]
|
||||
PLY["玩家<br/>游戏信息流 / 互动"]
|
||||
end
|
||||
subgraph ADMIN["game-admin — 管理后台前端"]
|
||||
direction LR
|
||||
OPS["运营<br/>审核 / 数据看板"]
|
||||
MGR["管理员<br/>用户 / 合规处置"]
|
||||
end
|
||||
STUDIO -.->|Vue3 + Vant<br/>移动优先| DS["共同的设计语言<br/>(品牌色 / 间距 / 字体 / 圆角)"]
|
||||
ADMIN -.->|Vue3 + Element Plus<br/>桌面端| DS
|
||||
```
|
||||
|
||||
> **范围说明**:本档的设计体系建设当前**聚焦 game-studio**——因为它是直面大众市场的消费级产品,卖相与一致性的要求最高,投入产出比也最高。game-admin 是内部后台工具,沿用 Element Plus 默认体系即可满足,只在品牌色这一层与 studio 对齐。下文除非特别标注 admin,默认讲的都是 studio 的设计体系。
|
||||
|
||||
---
|
||||
|
||||
## 2. 一句话定位:studio 的体系化波次
|
||||
|
||||
我们给 studio 的设计体系建设起了一个内部编号 **HJ-FE-DS-001**(HJ = Huijing/绘境,FE = Frontend,DS = Design System)。它不是零散地改几个页面,而是一次**体系化的波次**(wave,指一组围绕同一目标、成批推进并统一收口的工作),一次性把六件事立起来:
|
||||
|
||||
1. **设计 token 两层** —— 把所有颜色、间距、字号抽成可复用的"设计令牌",分两层管理(详见 §4)。
|
||||
2. **vue-i18n 中英双语** —— 用 vue-i18n(Vue 的国际化插件)做中英文切换,杜绝组件里写死中文。
|
||||
3. **通用组件库** —— 在 Vant 之上封装一层带绘境AI 品牌气质的组件。
|
||||
4. **暗/浅/柔三档主题** —— 用户可在设置里切换暗色、浅色、柔和三种舒适度的配色。
|
||||
5. **统一圆角矩形** —— 全站圆角风格统一,禁止药丸形、椭圆、正圆混用。
|
||||
6. **SVG 图标** —— 用矢量图标替代 Unicode 字符和 emoji。
|
||||
|
||||
**现状 = 已全量收口并合入主干 `dev/2.0.0`。** 19 个视图全部接入新体系,6 道 mini-desktop(在一台模拟桌面环境里跑真实浏览器)构建门全绿,真机三主题 × 双语的 CDP 走查(经 Chrome DevTools Protocol 远程驱动真浏览器逐屏验收)全部通过。落地经过了六个有序的提交批次,从最初的"卖相批"(先把退出登录入口、品牌渐变、Inter 字体这些最影响第一印象的点修好)一路推进到代表性页面全量上体系、品牌统一为绘境AI。
|
||||
|
||||
---
|
||||
|
||||
## 3. 一个核心判断:学 demo 的"设计语言",不学它的"布局范式"
|
||||
|
||||
设计体系从哪里起步?我们手上有一份早期的投资人路演原型 `docs-design/huijing-ai-demo.html`(下文简称 **demo**)。对它,我们做了一个关键的取舍判断,这个判断决定了整个前端的形态。
|
||||
|
||||
**保留** demo 的"设计语言"——它的 token 体系、`cyan→green`(青到绿)的品牌渐变、暗色玻璃质感。这套视觉语言有辨识度,值得沿用。
|
||||
|
||||
**否决** demo 的"布局范式"。demo 是一个**桌面产品外壳**:固定 236px 宽的左侧栏 + 1140px 宽的工作台,把短视频流硬塞进一个 390×660 的模拟手机框里展示。这是投资人路演用的"控制台"叙事,不是真正给大众用户的消费级界面。
|
||||
|
||||
现行定位因此是**响应式优先的消费级 Web**:移动端做沉浸式全屏体验,桌面端做居中放大,一套组件适配两端。原生桌面 App、原生移动 App 都是更远期的层级,现在不做,但设计 token 提前为它们铺好了路(见 §4)。
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
DEMO["demo 原型<br/>huijing-ai-demo.html"]
|
||||
DEMO -->|保留| KEEP["设计语言<br/>token 体系 + cyan→green 渐变 + 暗色玻璃质感"]
|
||||
DEMO -->|否决| DROP["桌面侧栏控制台布局<br/>(236px 侧栏 + 1140px 工作台 + 模拟手机框)"]
|
||||
KEEP --> NOW["现行:响应式优先消费级 Web<br/>移动沉浸 / 桌面居中放大 / 一套组件"]
|
||||
DROP -. 远期 .-> LATER["原生桌面 App / 移动 App<br/>(仅 token 契约先就绪)"]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 设计体系怎么搭:token 两层 + 组件库四层
|
||||
|
||||
这是整个前端域最核心的一张架构图。它由两条主线组成:一条是**设计 token 的两层结构**(为什么是"两层"见下),另一条是**组件库的四层封装**,两者在语义层交汇。
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
subgraph TOKEN["设计 token · 跨端单一事实源"]
|
||||
P["primitive 原始层<br/>原色板 / 灰阶 / 尺寸原子"] --> S["semantic 语义层<br/>语义令牌(bg / surface / accent / danger…)"]
|
||||
S --> CT["component 组件层<br/>组件级令牌"]
|
||||
end
|
||||
S --> L0
|
||||
subgraph LIB["组件库 · 四层封装"]
|
||||
L0["L0 token<br/>CSS 变量落地"] --> L1["L1 Vant 适配<br/>把 Vant 变量映射到语义层<br/>复用 Vant 的交互 / 无障碍 / 键盘"]
|
||||
L1 --> L2["L2 品牌基元<br/>AppButton / Field / Icon / Card / Chip<br/>EmptyState / NavHeader / TabBar / Skeleton / Toast"]
|
||||
L2 --> L3["L3 业务组合<br/>GameCard / InteractBar / FilterTabs…"]
|
||||
end
|
||||
S -. 同一份 token JSON .-> APP["桌面 App 复用 Web 技术<br/>移动 App 用 RN / Flutter(远期)"]
|
||||
```
|
||||
|
||||
### 4.1 为什么 token 要分两层
|
||||
|
||||
"token 两层"指的是**原始层(primitive)→ 语义层(semantic)** 的分离,组件层(component)是按需追加的第三层。
|
||||
|
||||
- **原始层**只描述"物理事实":`--c-cyan` 是某个青色、`--sp-3` 是 12px。它不带含义,只是调色板和尺子。
|
||||
- **语义层**描述"用途":`--bg` 是页面背景、`--accent` 是强调色、`--danger` 是危险色、`--surface-1..3` 是从底到顶的层叠表面。组件**只准引用语义令牌**,绝不直接碰原始层。
|
||||
|
||||
这样分层带来一个关键好处:**换肤时零改组件**。主题切换只需在语义层重新指向不同的原始色(比如暗色主题下 `--bg` 指向深色、浅色主题下指向浅色),组件因为只认 `--bg` 这个语义名,会自动跟着变,一行组件代码都不用动。
|
||||
|
||||
### 4.2 token 作为"跨端单一事实源"
|
||||
|
||||
token 还承担一个长期使命:**做跨端的单一事实源**(single source of truth,即同一份事实只有一处权威定义)。同一份 token 现在服务 Web(吃 CSS 变量),未来桌面 App 复用 Web 技术栈直接沿用,移动 App 则把同一份 token 导出成 JSON 喂给 React Native 或 Flutter。这样无论平台怎么扩,品牌的颜色和尺寸只有一处定义,既服务"远期双 App",又避免长期沉淀出互相打架的垃圾代码。
|
||||
|
||||
### 4.3 组件库为什么是四层
|
||||
|
||||
组件库自底向上四层,每层职责单一:
|
||||
|
||||
- **L0 token 层**:把上面的设计令牌落地成 CSS 变量,是组件库的地基。
|
||||
- **L1 Vant 适配层**:通过 `vant-theme.css` 把 Vant 自带的 `--van-*` 变量映射到我们的语义层。这一层的价值是**白嫖 Vant 的交互、无障碍、键盘支持**——这些是社区打磨多年的能力,不必自研。
|
||||
- **L2 品牌基元层**:封装一批带绘境AI 品牌气质的基础组件,如 `AppButton`(应用按钮)、`Field`(输入域)、`Icon`(图标)、`Card`、`Chip`、`Badge`、`EmptyState`(空状态)、`NavHeader`(导航头)、`TabBar`(底部标签栏)、`Skeleton`(骨架屏)、`Toast`(轻提示)。
|
||||
- **L3 业务组合层**:把基元拼成业务组件,如 `GameCard`(游戏卡片)、`InteractBar`(互动条)、`FilterTabs`(筛选标签)。
|
||||
|
||||
### 4.4 三条数据流
|
||||
|
||||
整个体系靠三条数据流串起来:
|
||||
|
||||
- **token 流**:`L0 token → L1 vant-theme → L2 品牌组件 → L3 业务面`,自底向上逐层消费。
|
||||
- **theme 流**(主题/换肤):`localStorage(studio.theme) → <html data-theme> → 语义层覆盖 → 全组件自动反应`。为防止 FOUC(Flash of Unstyled Content,即页面首帧闪现未应用样式的难看瞬间),`index.html` 在首屏渲染前内联一段脚本,先读 `localStorage` 把 `data-theme` 和 `lang` 设好。
|
||||
- **i18n 流**(国际化):`main.ts 注册 vue-i18n → 组件用 useI18n($t) 取文案 → localStorage(studio.lang) 与 <html lang> 同步`。
|
||||
|
||||
---
|
||||
|
||||
## 5. 创始人 5 项决议(已拍板 · 硬约束)
|
||||
|
||||
下面五条是创始人拍板的硬约束,不可动摇,是整个前端设计体系的地基。
|
||||
|
||||
1. **响应式优先消费级 Web**。否决 demo 的桌面侧栏控制台范式;移动端沉浸、桌面端居中放大,一套组件适配两端。
|
||||
|
||||
2. **暗色先行 + 设置内多档舒适主题可切**(暗 / 浅 / 柔和三档)。实现方式是在语义层用 `[data-theme]` 属性做覆盖,用 `localStorage` 持久化用户选择,首次进入跟随系统的 `prefers-color-scheme`(浏览器暴露的"用户偏好深色还是浅色"信号)。
|
||||
|
||||
3. **SVG 图标取代 Unicode/emoji**,取向 lucide(一套开源的线性 SVG 图标库)。
|
||||
|
||||
4. **金样板先行**。不一上来就全站铺开,而是先把"token + 组件库 + 1 个消费面(游戏信息流 Feed)+ 1 个创作面(Create)"打磨到位、双语化、验证通过,再批量推其余视图。这是项目"黄金模板先行"效率原则在前端的落地。
|
||||
|
||||
5. **统一圆角矩形**。禁止药丸形、椭圆、正圆混用;圆角半径按尺寸成比例分级——`xs/sm/md/lg/xl = 4/6/8/12/20`(像素),默认用 `md`(8px)。唯一例外是 loading 旋转环保留圆形。这条是铁律,单独立了一份规则文档 `.agents/rules/ui-uniform-rounded-rectangles.md` 约束。
|
||||
|
||||
---
|
||||
|
||||
## 6. 三大契约:token / 组件 / i18n
|
||||
|
||||
设计体系对外暴露三套契约(contract,即跨人协作时大家都遵守、不能私自破坏的约定)。三套都坚持 **additive**(增量式)原则——旧令牌一律保留为别名,各页面在迁移期新旧共存、逐面切换,绝不一刀切破坏现有引用。逐令牌的权威清单以代码里的 `game-studio/src/styles/tokens.css` 为准,这里只讲骨架与命名空间。
|
||||
|
||||
### 6.1 token 契约
|
||||
|
||||
旧令牌(`--cyan`/`--green`/`--radius`/`--shadow` 等)全部保留,其中 `--cyan` 等被设为新语义令牌 `--accent` 的别名,对现有引用零破坏。新增三组:
|
||||
|
||||
- **primitive 原始层**:`--c-cyan/green/pink/amber/violet/red`、灰阶 `--c-ink-0..3`、以及一对"在彩色背景上的前景色"`--c-on-accent:#061014`、`--c-on-danger:#2a0608`。
|
||||
- **semantic 语义层**:`--bg`、`--surface-1..3`、`--accent`、`--success`、`--warning`、`--danger`、`--border-1..2`、品牌渐变 `--grad-primary`、热区渐变 `--grad-hot`、焦点环 `--focus-ring`。
|
||||
- **尺度令牌**:间距 `--sp-1..6 = 4/8/12/16/22/32`、圆角 `--radius-xs..xl = 4/6/8/12/20`(默认 `md`)、阴影 `--elev-1..3`、字号 `--fs-h1/h2/h3/body/caption`、字重 `--fw-*`。
|
||||
|
||||
三档主题 `[data-theme=light|dim]` **只覆盖语义层**,不碰原始层。另一条收口约定:手写的 `rgba()` 透明度一律改用 `color-mix(in srgb, var(--token) N%, transparent)`,让透明色也走令牌而非硬编码。
|
||||
|
||||
### 6.2 组件契约
|
||||
|
||||
`AppButton` 的 props 保持不变(`type/size/block/loading/disabled`),向后兼容;但 primary(主)按钮恢复了品牌的签名渐变(`--grad-primary` + `--fw-cta` 字重 + `--on-accent` 前景),并补齐全部交互态:`focus-visible` 焦点环、`disabled` 时 0.42 透明度、`active` 时 scale 0.97 的按压反馈。`Icon` 组件**禁用 emoji**。
|
||||
|
||||
### 6.3 i18n 契约
|
||||
|
||||
采用 **vue-i18n@10**。文案文件放在 `src/locales/*.{zh,en}.ts`,按业务域划分命名空间(`common/feed/create/profile/entry/taskflow/project/hub/biz`)。硬约束:**禁止组件内出现裸中文**;缺失的 key 回退(fallback)到 `zh-CN`;数字、日期、货币一律用浏览器原生的 `Intl` API 本地化;品牌字符串统一收敛到 `common.brand` 一个 key(其值 = "绘境AI"),改名时只改一处。
|
||||
|
||||
---
|
||||
|
||||
## 7. studio vs demo:勿误砍 / 勿误判清单
|
||||
|
||||
这一节是两份"记录型"结论,目的是防止后人把 demo 当成目标、反而削弱了 studio 已有的真实能力,或者把"有意砍掉的范围"误判成"缺陷"去返工。
|
||||
|
||||
### 7.1 反向超出(studio 有 / demo 无 · MVP 真实可用必需 · 勿砍)
|
||||
|
||||
这些是 studio 比 demo 多出来、且 MVP 真实可用所必需的能力,绝不能因为"demo 里没有"就砍掉:
|
||||
|
||||
- **真实试玩宿主**:iframe 沙箱 + SDK/Runtime 注入 + postMessage 双向桥接双校验 + manifest 的 sha256 校验。这是护城河的命门——demo 里的"试玩"只是一个 toast 提示加一块静态 div,根本不是真在跑游戏。
|
||||
- **登录注册整屏**:短信登录即注册 + 协议同意门 + 登录后 redirect 回跳 + 匿名 id(anonId)衔接。demo 完全没有登录概念。
|
||||
- **分享落地页**:独立的 `/share/:gameId` 页 + Open Graph 元数据(社交平台抓取生成卡片预览用)+ utm 渠道归因参数。demo 里分享仅是一个 toast。
|
||||
- **失败/取消/重试闭环**:生成任务的七种失败原因有可读的中文映射 + 可取消可重试 + 进度色反馈。demo 用 720ms 模拟一卡到底的 complete,从不出现失败态。
|
||||
- **发布门禁**:7 项客户端预检 + 服务端权威返回 `{admitted, gates}` + 三态展示。demo 是乐观直发,无审核。
|
||||
- **鉴权守卫 + 全链路埋点**:路由守卫只认真实 token(`isRealLogin`)+ 性能/游玩/会话埋点。demo 是纯前端切屏,不接任何后端契约。
|
||||
|
||||
### 7.2 有意 MVP descope(是范围裁剪 · 不是缺陷 · 勿返工)
|
||||
|
||||
下面这些是有意排除在 MVP 之外的(descope = 主动缩减范围),不是漏做的缺陷:
|
||||
|
||||
- **首页门户**:投资人路演的载体,归 PC 官网/投资人 demo,远期。
|
||||
- **素材中心**:属 P1/v2.0,不在 55 个 P0 之列;真要落地须先补素材契约 + 授权/分成支付模型,且采购流程必须走后端可信边界,不能停在前端 mock。
|
||||
- **玩法模板中心**:注意——这里指的是 demo 那块"玩法品类层市场"屏,已被创始人 2026-06-12 的 W-CLEAN 清理(清理旧填参式模板的收口工作)废除。它与架构文档里"玩法模板 = 品类框架、未废、待建"是两回事,**不要混淆**。
|
||||
- **创作者数据后台**:远期;落地须反向定义出回读 API + 把诊断接到生成侧,避免产生没有入口的孤儿数据。
|
||||
- **多渠道发布**:抖音/微信/快手/TapTap 等渠道导出,归独立的渠道发行专项另行推进,游戏信息流的终裁不受影响。
|
||||
- **工作坊 IDE 化**:六类资产台、智能体对话、授权 IP 模式、编辑态调参等;MVP 走的是"一句话 + 选模板 → 整包生成"的简化路径。
|
||||
|
||||
---
|
||||
|
||||
## 8. 现状:已完成与遗留
|
||||
|
||||
**已完成**(都已在 `dev/2.0.0`):
|
||||
|
||||
- 退出登录的孤儿入口已闭合——Profile 页调用既有的 `userStore.logout()`,真实清掉 5 个 localStorage 键。
|
||||
- 主按钮品牌青绿渐变已回归(`AppButton.primary` 用 `--grad-primary` + `--on-accent`,字重约 800)。
|
||||
- Inter 字体已 self-host(自托管,不依赖外部 CDN;`reset.css` 里 `@font-face` + `font-display:swap`,中文有兜底字体)。
|
||||
- 消息中心一级入口已落地(底部标签栏从 3 个 tab 扩到 4 个,含 `/message`,带 `messageStore.unreadTotal` 未读角标)。
|
||||
- B 端前端归属已定为 studio 承接(`src/views/biz/` 下已建 BizLeads/BizLeadDetail/BizTemplatePicker/BizCreate,接既有的 `/app-api/biz/*` 7 个端点)。
|
||||
- 字号阶梯令牌已抽出(`--fs-h1/h2/h3/body/caption`)。
|
||||
|
||||
**遗留(小尾巴)**:
|
||||
|
||||
- `tokens.css:3` 的注释里还残留旧路径 `zaomeng-ai-demo.html`(实际文件是 `huijing-ai-demo.html`,品牌应为绘境AI),措辞漂移待清。
|
||||
- 明确不做的范围:原生 App(仅 token 契约就绪)、RTL(从右到左书写,服务阿拉伯语等)、护眼/高对比主题(留作后续增量)。
|
||||
|
||||
---
|
||||
|
||||
## 9. 关键指针
|
||||
|
||||
需要更深的细节时,回到这些权威源:
|
||||
|
||||
| 你想找 | 去哪里 |
|
||||
|---|---|
|
||||
| 逐令牌的权威取值 | `game-studio/src/styles/tokens.css`(token 单一事实源) |
|
||||
| 子系统活档(本档的来源) | `docs/agent-specs/studio前端设计体系.md` |
|
||||
| 可直接打开的设计稿(含实时中英 + 三主题切换,真 Chrome 验过) | `docs-design/studio-redesign-mockup.html` |
|
||||
| 设计基准原型(只取设计语言,不取布局) | `docs-design/huijing-ai-demo.html` |
|
||||
| 构建门与真机走查的操作配方 | `.agents/skills/staging-ops.md`(构建真门 = `npm run build` / `vue-tsc -b && vite build`;注意 `vue-tsc --noEmit` 是假门不算数)、`.agents/skills/ui-walkthrough-cdp.md`(CDP 三主题 × 双语走查) |
|
||||
| 圆角铁律 | `.agents/rules/ui-uniform-rounded-rectangles.md` |
|
||||
| 落地提交记录(均在 `dev/2.0.0`) | `42342e8c` 卖相批 → `f98ad0f8` token 层 → `ecae9025` i18n 脚手架 → `655673eb` 组件库 → `caa2e1ba` 代表面 → `2c96e63a` 品牌+收尾 |
|
||||
|
||||
> **纪律**:前端域只讲"两个前端长什么样、靠什么撑起一致体验";产品对用户提供什么记在[产品域](../产品/README.md),系统用什么技术搭起来记在[架构域](../架构/README.md),它们各司其职、互不重复。
|
||||
97
docs/architecture/后端/README.md
Normal file
97
docs/architecture/后端/README.md
Normal file
@ -0,0 +1,97 @@
|
||||
# 后端域 · 服务实现视角
|
||||
|
||||
> **这是什么**:绘境AI 后端域的入口文档,从"**这 13 个业务模块在工程上怎么落地、怎么组织代码**"这个角度切入——也就是服务实现视角(包结构、分层约定、模块归属、构建与运行形态)。它不重复回答"每个模块各自管什么领域、彼此怎么依赖",那是架构域([../架构/](../架构/README.md))的职责。
|
||||
> **给谁看**:要动后端代码的工程师、做后端 code review 的人、排查跨模块依赖的人。
|
||||
> **怎么读**:本页很短,先建立"后端长什么样、边界划在哪、权威源在哪"的认知;真要写某个模块、查某个 T-id、看某条依赖,直接跳到文末三个权威源。
|
||||
|
||||
---
|
||||
|
||||
## 1. 边界声明:本域 = 13 个后端业务模块的服务实现视角
|
||||
|
||||
绘境AI 的后端不是一个空泛的"服务端",而是一组**边界清晰、各管一摊**的业务模块:从创作编排(studio)、无状态生成原子(aigc)、编译与多渠道发布(runtime),到项目生命周期(project)、游戏信息流(feed)、遥测数据底座(telemetry),再到支付(pay)、分账结算(trade)、社区互动(community)、素材与授权(ip)、内容安全与审核(compliance)、B 端定制(biz)、广告引擎(ad)——一共 **13 个 game 业务模块**,叠加在 Huijing Cloud(一套基于 Spring Cloud Alibaba 的开源 Java 企业级后台框架,我们 fork 它做二次开发;下文简称 Huijing)提供的用户、权限、工作流、文件等基座能力之上。
|
||||
|
||||
需要先讲清楚一件事:**"13 个模块各自负责什么领域、它们之间的依赖关系"属于架构域,不在本页展开**。本页只回答工程落地层面的问题——这些模块在代码里怎么摆、包怎么命名、构建产物长什么样、当前哪些已经建成在跑。换句话说,架构域回答"模块是什么(WHAT/边界)",本后端域回答"模块在工程上怎么实现(HOW/落地)"。两者各守一摊、互不重复,这样无论是改架构还是改代码,都只有一处需要更新。
|
||||
|
||||
把这套实现视角浓缩成一张图,就是下面这个分层与依赖示意:
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
subgraph HJ["Huijing fork — 基座(开箱即用,不属于业务)"]
|
||||
SYS["system<br/>用户 / 权限 / OAuth2"]
|
||||
INFRA["infra<br/>文件 / 任务 / 日志"]
|
||||
BPM["bpm 工作流"]
|
||||
PAYN["pay(huijing 原生)<br/>当前未接入单体"]
|
||||
end
|
||||
subgraph BIZ["13 个 game 业务模块 — 本域主体"]
|
||||
direction LR
|
||||
STU["studio 创作编排"]
|
||||
AGC["aigc 生成原子"]
|
||||
RT["runtime 编译/发布"]
|
||||
PRJ["project 生命周期"]
|
||||
FED["feed 游戏流"]
|
||||
TEL["telemetry 遥测"]
|
||||
TRD["trade 分账结算"]
|
||||
CMU["community 社区"]
|
||||
IP["ip 素材/授权<br/>(seam 寄宿 compliance)"]
|
||||
CMP["compliance 安全/审核"]
|
||||
BIZM["biz B端定制"]
|
||||
AD["ad 广告"]
|
||||
end
|
||||
subgraph PKG["每个模块的两段式工程结构"]
|
||||
API["-api 包<br/>枚举 / 错误码段 / 跨模块 DTO·Feign"]
|
||||
SRV["-server 包<br/>controller / service / dal / convert"]
|
||||
end
|
||||
BIZ --> HJ
|
||||
STU --> PKG
|
||||
AGC --> PKG
|
||||
RT --> PKG
|
||||
note["全部以 jar 聚合进 huijing-server 单体运行<br/>包名统一 com.wanxiang.huijing.game.module.{模块}.{层}"]
|
||||
PKG -.-> note
|
||||
```
|
||||
|
||||
> 图里把 pay 单独画在基座里:**pay 是 Huijing 原生模块**,MVP 阶段还没接入这套单体;而 **ip 不独立建模块,而是作为一条 seam(接缝/横切能力)寄宿在 compliance 里**。这两点是后端实现上的两个特殊归属,后面"权威源"里有完整说明。
|
||||
|
||||
---
|
||||
|
||||
## 2. 为什么这份文档这么薄
|
||||
|
||||
一句话:**逐模块的详细设计不该有两份。** 这份后端域 README 刻意保持薄,是为了防止它变成一份和架构域内容重叠、却又各自漂移的"影子文档"——那是文档治理里最该避免的反模式(同一件事写两遍,迟早一份过期、读者不知道信哪份)。
|
||||
|
||||
具体来说,后端的知识天然分布在三个层次,各有各的归宿,不该在本页重抄:
|
||||
|
||||
- **模块边界与技术功能注册表(WHAT/结构权威)** —— 每个模块的职责、IN/OUT 边界、依赖关系,以及每一项技术功能的 `T-{模块}-{nn}` 编号(例如 `T-AGC-04` = aigc 模块的"GameConfig 结构化生成 + JSON Schema 校验"),全部集中在架构域的 [13模块.md](../架构/13模块.md)。那是结构权威:T-id 只增不改号、废弃打墓碑标记。本页若再列一遍模块卡片,只会制造第二份会过期的副本。
|
||||
- **工程落地现状与代码锚点(HOW/实现现状)** —— 包名怎么命名、`-api`/`-server` 怎么分、Flyway 迁移脚本放哪、错误码段怎么分配、哪些模块已经建成接入单体、哪些还是桩——这些随代码演进而变的实时状态,归 [game-cloud/.agent](../../../game-cloud/.agent)。它跟着代码走,改了结构就改它,是后端工程现状的单一事实源。
|
||||
- **横切进度与 13 模块完成度** —— 哪个模块是真实现、哪个是桩、哪个未建,以 `docs/mvp/MVP进度总账.md` 的 §2 矩阵为准(它是模块进度的单一 SoT)。
|
||||
|
||||
把这三层钉死在各自的权威源,本页就只需要做一件事:讲清楚"后端是什么形状、边界划在哪",然后把读者**准确地**导到该去的地方。这不是偷懒,而是治理纪律——薄,是为了不和权威源打架。
|
||||
|
||||
至于一条冲突该信谁,后端工程沿用架构域定下的读序:**状态 > 实现 > 结构**。也就是说,进度总账里的真/桩/未建状态最高,其次是 game-cloud 里的实现现状,最后才是 13模块.md 的结构定义;当三者表述不一致时,按这个优先级裁断。
|
||||
|
||||
---
|
||||
|
||||
## 3. 几条贯穿全后端的工程约定(速记,细节见权威源)
|
||||
|
||||
下面这些约定不是新规定,而是把散落在 `.agents/rules/engineering-conventions.md` 和 game-cloud `.agent` 里、且对"看懂后端长什么样"最关键的几条拎出来速记。任何一条的完整版都在那两处,本页只给指针级的一句话:
|
||||
|
||||
- **包名统一**:所有业务代码都在 `com.wanxiang.huijing.game.module.{模块}.{层}` 之下(例如 `...game.module.project.service`)。这条命名规则也是端前缀自动生效的前提——控制器按包路径 `**.controller.app/admin.**` 通配,Huijing 框架据此自动给上 `/app-api`、`/admin-api` 两个端前缀,控制器代码本身零改。
|
||||
- **两段式模块结构**:每个 game 模块拆成 `-api` 与 `-server` 两个 Maven 子模块。`-api` 只放对外契约面——枚举、错误码常量、跨模块 DTO 与 Feign 接口;`-server` 放真正的实现——controller(app/admin 双端)、service、dal(DO/Mapper)、convert。跨模块只能依赖对方的 `-api`,不能直接碰 `-server`,这是低耦合的硬边界。
|
||||
- **错误码分段**:每个模块独占一段错误码区间(例如 project 占 1–100,Wave1 闭环脊柱模块占 100–104),不重叠。完整分配表在 `.agents/rules/engineering-conventions.md §1.3`。
|
||||
- **契约不放后端**:API/DB/SDK/event 等 8 类跨端契约的单一事实源是仓根的 `contracts/`,后端只引用它、不在模块里另立一份。数据库 schema 的源在 `contracts/db-schemas/`,Flyway 执行副本放在 `huijing-server/.../db/migration/`(两边 diff 必须一致,因为 Flyway 对校验和敏感)。
|
||||
- **物理形态 = 单体**:13 个模块是**逻辑划分**,物理上全部以 jar 聚合进 `huijing-server` 单体进程运行(monorepo 先行、后续再拆独立仓)。Nacos、RocketMQ 等是框架自带的远期形态,MVP 阶段并未部署。
|
||||
- **技术栈基线**:Java 17 + Spring Cloud Alibaba + MySQL 8.0 + Redis 7 + Flyway;生成主线是便宜 LLM(如 DeepSeek / MiniMax)经 new-api 网关(一个 OpenAI 兼容的多模型 LLM 网关)直出,配合 SAA(Spring AI Alibaba)裸图编排——这部分的现行架构归架构域的[生成引擎](../架构/生成引擎/)子文档,不在后端域展开。
|
||||
|
||||
**建设现状(随代码演进,以权威源为准)**:13 个模块中已建成并接入单体的有 11 个(project / aigc / runtime / feed / telemetry / ad / trade / compliance / studio,以及 Wave4 的 community / biz);ip 作为 seam 寄宿 compliance、不独立建;pay 为 Huijing 原生、当前未接入单体。这个数字会变,**任何时候都以 `docs/mvp/MVP进度总账.md` §2 矩阵 + game-cloud `.agent` 为准,不以本页为准**。
|
||||
|
||||
---
|
||||
|
||||
## 4. 权威源指针:要动后端,去这三处
|
||||
|
||||
本页到此为止只做了"定形状、划边界"。真正要写代码、查 T-id、看依赖、对进度,请直接去下面三个权威源——它们才是各自维度上"读它 = 当前真相"的单一事实源:
|
||||
|
||||
| 权威源 | 它是什么维度的真相 | 什么时候去 |
|
||||
|---|---|---|
|
||||
| [../架构/13模块.md](../架构/13模块.md) | **结构权威**:13 模块卡片(职责 / IN-OUT 边界 / 依赖)、全部 `T-{模块}-{nn}` 技术功能注册表、横切关注点 owner | 要查某模块边界、某条 T-id、模块间依赖时 |
|
||||
| [../架构/README.md](../架构/README.md) | **架构骨架**:六层分层图、关键选型的"为什么"、13 模块全局依赖图 | 要建立后端整体技术认知、理解选型理由时 |
|
||||
| [game-cloud/.agent](../../../game-cloud/.agent) | **工程现状**:目录规划、`-api`/`-server` 落地、Flyway 版本、各 Wave 建设进度、代码锚点 | 要实际改代码、看哪些已建成 / 还是桩时 |
|
||||
|
||||
> **纪律**:后端域只讲"后端的工程形状与边界",不重抄模块详设(在 13模块.md)、不重抄实现现状(在 game-cloud/.agent)、不重抄进度(在 MVP进度总账.md)。这份文档薄,是因为它的价值在于**把人准确导到对的权威源**,而不是再造一份会过期的副本。
|
||||
452
docs/architecture/架构/13模块.md
Normal file
452
docs/architecture/架构/13模块.md
Normal file
@ -0,0 +1,452 @@
|
||||
# 架构域 · 13 个后端业务模块
|
||||
|
||||
> **这是什么**:绘境AI 后端 13 个业务模块的逐个详解——每个模块管什么、内部包含哪些技术功能、跟谁有依赖、现在建到了什么程度。它是[架构域主文档](README.md) §3–4("13 模块总览 + 依赖方向")的下钻细化。
|
||||
> **给谁看**:要动某个模块的工程师、做模块边界评审的架构师、想知道"这块功能落在哪个模块"的人。
|
||||
> **怎么读**:先看 §1 的全景图与依赖图建立空间感,再按需跳到 §3 对应模块的卡片;每张卡片自带一句"现在建到哪了",想看全局完成度就读 §4 的状态总表。
|
||||
> **读这一页 = 模块结构的当前真相**,但有一条铁律:**结构、实现、状态三者冲突时,以"状态"为准**(详见文末注)。本页讲的是"应该怎么切"(结构权威);"实际建成多少"以 `docs/mvp/MVP进度总账.md` 为准。
|
||||
|
||||
---
|
||||
|
||||
## 1. 一张图看清 13 个模块怎么分工
|
||||
|
||||
绘境AI 的后端把全部业务能力按**领域边界**(domain boundary,即"一类职责归一处")切成 **13 个模块**,追求高内聚、低耦合——每个模块对内是一件完整的事,对外只通过窄接口交互。这 13 个模块物理上不是 13 个独立服务,而是以 jar(Java 打包单元)聚合进 Huijing 单体一起运行(Huijing 是我们 fork 的开源 Java 后台框架);逻辑上它们各自独立、互不越界,为将来从单体平滑拆成微服务预留了天然的切割线。
|
||||
|
||||
每个模块有一个三字母前缀(如 studio = STU),内部的每一项技术功能用 `T-{模块}-{编号}` 来标识——例如 `T-AGC-04` 是 aigc 模块的第 4 项技术功能。这套 T-id 是整套架构的"结构注册表",全平台共 **204 项技术功能**,只增不改号,废弃的打墓碑标记保留(这样引用它的其他文档不会因为重新编号而失效)。
|
||||
|
||||
按它们在业务闭环里的位置,13 个模块可以归成四组:
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
subgraph 创作["创作链路 — 把游戏做出来"]
|
||||
STU["studio · 创作编排 / 编辑器域"]
|
||||
AGC["aigc · 无状态生成原子"]
|
||||
RT["runtime · 编译 / 沙箱 / 多渠道发布"]
|
||||
PRJ["project · 项目生命周期 + 专区 Zone"]
|
||||
end
|
||||
subgraph 分发["分发与数据 — 让游戏被玩到、被看清"]
|
||||
FED["feed · 游戏流推荐 / 互动 / 分享"]
|
||||
TEL["telemetry · 事件摄取 / 聚合 / 质量评分"]
|
||||
end
|
||||
subgraph 变现["变现链路 — 把钱赚回来"]
|
||||
PAY["pay · 支付收单 / 订单 / 退款对账"]
|
||||
TRD["trade · 分账 / 结算 / 钱包 / 财税"]
|
||||
AD["ad · 广告联盟 / 植入 / 曝光计费 / 归因"]
|
||||
end
|
||||
subgraph 生态["平台与合规 — 让生态转得起来、守得住"]
|
||||
CMU["community · 社交 / 互动 / 排行 / 通知"]
|
||||
IP["ip · 素材安全 / 授权链 / IP 风格原子"]
|
||||
CMP["compliance · 内容安全 / 审核 / 锁风门 / RBAC"]
|
||||
BIZ["biz · B/G 端定制工程底座"]
|
||||
end
|
||||
```
|
||||
|
||||
- **创作链路**:用户从这里把游戏做出来。studio 是有状态的创作工作台,它把活分派下去;aigc 负责"一次请求出一个产物"的无状态生成;runtime 把生成结果编译成能跑的包;project 把项目和版本落库管起来。
|
||||
- **分发与数据**:feed 把已发布的游戏排成竖屏游戏流推给玩家;telemetry 把全链路的行为事件收上来、算出质量分,再回喂给 feed。两者互相依赖,构成"越用越聪明"的数据回路。
|
||||
- **变现链路**:pay 统一收钱,trade 把多来源的收入分账结算到创作者钱包,ad 把游戏内广告从植入到计费做成后端原子。
|
||||
- **平台与合规**:community 承载社交关系与全渠道通知;ip 为素材和授权资产提供安全可信的底座;compliance 是内容安全与审核中枢、也是锁风门的裁决方;biz 支撑 B/G 端(企业 / 政府客户)定制业务。
|
||||
|
||||
---
|
||||
|
||||
## 2. 模块之间怎么依赖:为什么是这个方向
|
||||
|
||||
模块间是**单向、低耦合**的依赖——A 依赖 B、B 绝不反向依赖 A,且只通过每个模块的 `-api` 包(只声明数据结构 DTO 与远程调用接口、不含实现)交互。这样任何一个模块都能独立演进、独立测试。下图箭头 A→B 表示"A 依赖 B":
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
STU[studio] --> AGC[aigc]
|
||||
STU --> RT[runtime]
|
||||
STU --> IP[ip]
|
||||
STU --> PRJ[project]
|
||||
STU --> CMP[compliance]
|
||||
AGC --> CMP
|
||||
AGC --> PRJ
|
||||
RT --> PRJ
|
||||
RT --> TEL[telemetry]
|
||||
FED[feed] --> PRJ
|
||||
FED --> TEL
|
||||
TEL --> FED
|
||||
IP --> CMP
|
||||
IP --> TRD[trade]
|
||||
CMP --> AGC
|
||||
CMP --> IP
|
||||
PRJ --> CMP
|
||||
TRD --> PAY[pay]
|
||||
TRD --> AD[ad]
|
||||
AD --> RT
|
||||
BIZ[biz] --> PRJ
|
||||
BIZ --> AGC
|
||||
BIZ --> TRD
|
||||
CMU[community] --> PRJ
|
||||
```
|
||||
|
||||
读这张图有四条主线:
|
||||
|
||||
- **创作侧 studio 站在最上游**,把活分派给 aigc(生成)、runtime(编译)、ip(素材)、project(落库)、compliance(安全裁决)。它自己几乎不被别人依赖——这是"工作台"该有的位置。
|
||||
- **生成原子 aigc 保持无状态**,只向下依赖 compliance(做 Prompt 安全检测)和 project(写入结果),自己不持有任何会话或项目状态。无状态意味着它天然可重放、可横向扩容。
|
||||
- **数据回路 telemetry ↔ feed 互相依赖**:feed 消费 telemetry 算出的质量分来排序,telemetry 回收 feed 上的互动信号来计算——这是图里唯一一处双向边,也正是平台"越用越聪明"的机制所在。
|
||||
- **资金侧 trade 向下收口**:它依赖 pay(收单)和 ad(广告收入)拿到资金原始数据,自己只管分账、结算、对账。compliance ↔ ip ↔ aigc 之间还有一处"风格-版权"裁决的环路,下面专门讲。
|
||||
|
||||
### 两个跨模块的关键概念(横切关注点)
|
||||
|
||||
有些能力不属于单一模块,而是横跨多个模块协作完成。为避免出现"人人都管、结果没人管"的灰色地带,每个横切关注点都指定**唯一的 owner**(责任主)。MVP 阶段有两个:
|
||||
|
||||
| 横切关注点 | owner(责任主) | 谁供原料 / 谁消费 | 标识 |
|
||||
|---|---|---|---|
|
||||
| **锁风门 Gate**(风格-版权一致性门) | compliance | aigc 的风格检测原子(T-AGC-19)、ip 的 IP 风格校验原子(T-IP-04)供原料;project 的发布前检查(T-PRJ-05)消费 | T-CMP-12 |
|
||||
| **专区 Zone**(运营双轨分区) | project | feed 据它分区推荐(T-FED-15);ip 据它做双轨归类(T-IP-10);studio 决定创作去向 | T-PRJ-07 |
|
||||
|
||||
- **锁风门 Gate**:一道"风格-版权一致性"门。"锁风"= 锁定风格、防止侵权。它本身不会检测风格,而是**聚合** aigc 和 ip 各自产出的风格检测原子(原子 = 最小的、可独立调用的检测单元),综合裁决出 pass(放行)/ review(转人工)/ block(拦截)三种结论,挂在 project 的发布前检查上。owner 是 compliance,因为它是全平台的内容安全与裁决中枢。
|
||||
- **专区 Zone**:运营用的双轨分区——授权 IP 区(放基于授权形象创作的游戏)和 UGC 区(UGC = User Generated Content,用户原创内容)。Zone 的实体归 project 所有(owner),feed 据它分区推荐、ip 据它给素材双轨归类。MVP 阶段 Zone 还没有独立的数据库实体,是用字段承载的(有意简化)。
|
||||
|
||||
> **建设形态注记(2026-06-10 审计)**:13 模块是逻辑划分,物理上以 jar 聚合进 Huijing 单体运行。其中 **ip 不独立建模块,而是以 seam(接缝/预留位)寄宿在 compliance 里**(D5 裁定);**pay 是 Huijing 原生模块,当前尚未接入单体启动器**;**community / biz 属 Wave4(第四批开发波)、目前未建**。这些形态在下面各模块卡片里会逐一标注。
|
||||
|
||||
---
|
||||
|
||||
## 3. 13 个模块逐个看
|
||||
|
||||
下面每张卡片包含四件事:**职责**(这个模块负责什么)、**边界**(IN = 它管什么 / OUT = 它不管什么,委托给谁)、**依赖**(它向下依赖哪些模块)、以及它内部的**技术功能 T-id 清单**。卡片末尾用一句话给出"现在建到哪了"(完整状态表见 §4)。
|
||||
|
||||
> 卡片里偶尔出现的 📌 标记,表示这一项的 MVP 现实形态与原蓝图设计有出入——蓝图是远期形态,📌 后面是 MVP 当下的落地方式。
|
||||
|
||||
### 3.1 studio — 创作编排 / 编辑器域 ★新增
|
||||
|
||||
studio 是有状态的**创作工作台后端**。它的活是:把一次"创作会话"编排成一份可试玩的草稿——调度 aigc 的生成原子、把多种资产装配到一起、管理角色的骨骼绑定(rig)/ 对白分支树 / 任务链,最后落地成 project 的草稿。它是创作链路的总指挥,但**自己不干脏活**:不跑大模型、不持久化版本、不编译、不拥有素材库、不做锁风裁决,这些都委托出去。
|
||||
|
||||
- **边界 IN**:创作会话、资产图、角色 rig、对白分支树、任务链编排、附件上下文装配、六类资产生成调度、批量 / 可视化工作流编排。
|
||||
- **边界 OUT**:不跑 LLM / 扩散模型(委托 aigc)|不持久化项目版本(交 project)|不编译 / 沙箱(交 runtime)|不拥有素材库(读 ip)|不做锁风裁决(调 compliance)。
|
||||
- **依赖**:aigc · runtime · ip · project · compliance。
|
||||
|
||||
| ID | 技术功能 |
|
||||
|---|---|
|
||||
| T-STU-01 | 创作会话状态机与持久化 |
|
||||
| T-STU-02 | 资产图模型(多资产组合关系) |
|
||||
| T-STU-03 | 角色骨骼 rig 数据结构与动作编辑 |
|
||||
| T-STU-04 | 对白分支树(编辑 + 实时写入预览节点) |
|
||||
| T-STU-05 | agentic 任务链编排引擎(7 步生成链) |
|
||||
| T-STU-06 | 附件上下文装配(生成产物 / 上传 → 生成上下文) |
|
||||
| T-STU-07 | SSE 进度推送 📌 MVP = 同步轮询,SSE 后置 |
|
||||
| T-STU-08 | 草稿装配与 project 移交契约 |
|
||||
| T-STU-09 | 六类资产模块化生成调度(按资产种类编排 aigc 原子) |
|
||||
| T-STU-10 | 用户工作流编排(可视化,创作者自定义流程节点)·远期 |
|
||||
| T-STU-11 | 批量游戏生成调度 · 远期 |
|
||||
|
||||
> SSE = Server-Sent Events,服务器主动向浏览器推送进度的技术;MVP 先用更简单的同步轮询代替。T-STU-10/11(可视化工作流、批量生成)是远期能力,产品出口待定,当前未映射。
|
||||
>
|
||||
> **现在建到哪了**:🟡 部分。创作编排的委托层是真的(草稿装配 + aigc 接缝 + 血缘追溯),但所谓"7 步生成链"其实不在 studio、而在 aigc 的 SAA 生成图里;六类资产 / 角色 rig / 对白树 / 可视化编排目前都还是桩(stub,即只有壳、无真实实现)。
|
||||
|
||||
### 3.2 aigc — 无状态生成原子 收敛
|
||||
|
||||
aigc 是生成侧的**原子工厂**,也是平台护城河的核心路径。它的契约非常干净:**单个 Prompt / 请求 → 单个确定性产物**(结构化参数 / GameConfig / 图 / 音 / 剧情 / 封面 / 校验结果 / 风格指纹),无状态、幂等(同样输入给同样输出)、可重放。所有"会话""多资产装配""进度链"这类有状态的编排都不归它,归 studio。
|
||||
|
||||
- **边界 IN**:Prompt 解析 / 安全、模板匹配 / Schema、LLM 编排 / 熔断、生成任务队列 / 状态机、Fallback 兜底、图 / 音 / 剧情 / 封面生成原子、风格标签、内置素材库、质量评估。
|
||||
- **边界 OUT**:不管会话 / 编辑 / 多资产装配与进度链编排(交 studio)|不持久化项目(交 project)|不编译 / 沙箱(交 runtime)|不做锁风裁决(只供风格原子给 compliance)。
|
||||
- **依赖**:compliance(Prompt 安全)· project(写入结果)· 基础设施(文件)。
|
||||
|
||||
| ID | 技术功能 | ID | 技术功能 |
|
||||
|---|---|---|---|
|
||||
| T-AGC-01 | Prompt 解析与归一化 | T-AGC-12 | 内置素材库管理 |
|
||||
| T-AGC-02 | Prompt 安全检测 | T-AGC-13 | 素材上传校验 + SHA256 去重 |
|
||||
| T-AGC-03 | 模板分类匹配 / 注册 / 扩展 | T-AGC-14 | 图像 AI 生成原子 |
|
||||
| T-AGC-04 | GameConfig 结构化生成 + JSON Schema 校验 | T-AGC-15 | 音乐 / 音效 AI 生成原子 |
|
||||
| T-AGC-05 | 模板参数校验与兜底 | T-AGC-16 | 剧情 / 对话 AI 生成原子 |
|
||||
| T-AGC-06 | LLM 调用编排 / 熔断 / 重试 / 多供应商 | T-AGC-17 | 封面 / 宣传图 AI 生成原子 |
|
||||
| T-AGC-07 | 生成任务异步队列调度 📌 MVP 用执行器轮询 | T-AGC-18 | 节点配置参数化(载体 = SAA,非自研引擎) |
|
||||
| T-AGC-08 | 生成任务状态机(queued/running/succeeded/failed/timed_out/canceled) | T-AGC-19 | 生成风格一致性检测原子(供锁风门) |
|
||||
| T-AGC-09 | 确定性 Fallback 生成 | T-AGC-20 | Golden Config 回归测试 |
|
||||
| T-AGC-10 | 生成失败原因分类(结构化错误码) | T-AGC-21 | 生成结果质量自动评估 |
|
||||
| T-AGC-11 | 风格标签体系 | | |
|
||||
|
||||
几个名词的一句话解释:**GameConfig** 是描述一款游戏的结构化配置(玩法参数、资产引用等),是生成链的核心产物;**SHA256 去重**是用内容哈希识别重复素材;**T-AGC-18 的"自研 DAG / 工作流引擎"表述已作废**——agentic 编排基建统一改用 SAA 裸图(Spring AI Alibaba 的状态图编排,见[架构主文档](README.md) §2.2),编排不自研,此项保留为远期"专业创作者可视化编排"占位、载体改 SAA;**T-AGC-07 的 RocketMQ 异步队列是远期形态**,MVP 用进程内执行器轮询代替。
|
||||
|
||||
> **现在建到哪了**:🟡 部分。执行器主链是真的(M2 阶段已 e2e 实测:一句话生成 18–27s 出可玩游戏,成功率约 80%);但 SAA 控制平面属于"建成但未默认上生产"。最大缺口是 **W-G1 worker 尚未接为 staging 默认产线**(W-G1 = 便宜模型造游戏的工作器,worker = 后台生成进程),且玩法模板注册未建。
|
||||
|
||||
### 3.3 runtime — 编译 / 预览 / 渠道发布 扩
|
||||
|
||||
runtime 把 aigc 产出的 GameConfig **变成能真正跑起来的东西**:编译成版本化的可运行 Web 包、在沙箱里预览、转换成各渠道(微信 / 抖音 / 快手小游戏、TapTap 试玩包)的格式并驱动发布状态机。它不决定"能不能发"(那是 compliance 的裁决),只负责"怎么把它做出来、推出去"。
|
||||
|
||||
- **边界 IN**:编译 / Manifest(产物清单)/ 打包、沙箱与事件桥接、体积与完整性校验、预加载与缓存、渠道转换与提审、发布状态机、运行时质量采集。
|
||||
- **边界 OUT**:不决定发布放行(交 compliance)|不做推荐分发(交 feed)|不持久化项目(读 project)|不跑生成(消费 aigc 产物)。
|
||||
- **依赖**:project · aigc · telemetry · compliance(渠道合规,被动)。
|
||||
|
||||
runtime 是技术功能最密集的模块之一(32 项),覆盖了从编译、资源优化、沙箱安全到多渠道转换的全链路。核心几项:
|
||||
|
||||
| ID | 技术功能 | ID | 技术功能 |
|
||||
|---|---|---|---|
|
||||
| T-RT-01 | GameConfig → 可运行 Web 包编译 | T-RT-04 | iframe 沙箱隔离 + CSP |
|
||||
| T-RT-02 | GameManifest 生成与打包 | T-RT-05 | Game SDK 事件桥接 |
|
||||
| T-RT-03 | 资源打包上传 OSS(版本化路径) | T-RT-08 | 资源体积限制(≤10MB / 首屏 ≤2MB) |
|
||||
| T-RT-11 | 预加载与三容器策略 | T-RT-14 | postMessage 来源与 schema 校验 |
|
||||
| T-RT-21~23 | 小游戏转换(微信 / 抖音 / 快手) | T-RT-27 | 游戏可玩性自动测试 📌 自研 CDP 客户端 |
|
||||
| T-RT-26 | 统一工程转译引擎 | T-RT-31 | TapTap 试玩包提审通道 |
|
||||
| T-RT-28 | 运行时链路追踪(trace_id) | T-RT-32 | 渠道发布状态机(待开通→申请→开通→上线) |
|
||||
| T-RT-30 | 云游戏即时运行容器 · 远期 | | (另含 FPS 监控、素材压缩、CDN 刷新等共 32 项) |
|
||||
|
||||
> iframe 沙箱 = 浏览器内嵌的隔离框架,与平台彻底隔离;CSP = 内容安全策略,限制游戏内的网络与脚本行为;postMessage = iframe 与宿主页之间唯一的通信通道。**T-RT-27 的可玩性自动测试**原蓝图写的是 Playwright(浏览器自动化框架),实现现状是**自研 CDP 客户端 `player_cdp.py`**(CDP = Chrome DevTools Protocol,直接驱动 Chrome 的协议),已在尽调补录中说明。
|
||||
>
|
||||
> **现在建到哪了**:🟡 部分。存包 / 取包、engineBundle 内嵌(engineBundle = 把引擎打进游戏包一起发的产物形态)、沙箱桥接都是真的;但**真实编译引擎目前是桩**,微信 / 抖音 / 快手 / TapTap 渠道转换未建,OSS 存取也是桩。
|
||||
|
||||
### 3.4 project — 项目生命周期 + 专区 Zone 扩
|
||||
|
||||
project 是全仓**最成熟**的模块,管的是项目 / 版本 / 草稿的**全生命周期与状态流转**,并编排发布。它还是专区 Zone 实体的 owner。它的纪律很清晰:**只驱动状态、不裁决结论**——审核结论由 compliance 给,它只负责把状态机往前推。
|
||||
|
||||
- **边界 IN**:项目 / 版本 CRUD、状态机、草稿持久化、发布前检查、发布审核流程对接、统一发布编排、Zone 实体 / 归属 / 运营位。
|
||||
- **边界 OUT**:不生成内容(aigc / studio)|不裁决审核结论(compliance,本模块只驱动状态)|不编译 / 转换(runtime)|不做推荐排序(feed)。
|
||||
- **依赖**:compliance · runtime · feed · BPM(工作流引擎)· 基础设施。
|
||||
|
||||
| ID | 技术功能 |
|
||||
|---|---|
|
||||
| T-PRJ-01 | 项目 CRUD(元数据持久化 + 字段约束校验) |
|
||||
| T-PRJ-02 | 游戏状态机(draft…published/rejected/unpublished/deleted + 下架 / 封禁迁移) |
|
||||
| T-PRJ-03 | 版本管理(自增 / 覆盖 / 历史 / current_version_id 切换) |
|
||||
| T-PRJ-04 | 草稿保存与恢复机制 |
|
||||
| T-PRJ-05 | 发布前检查清单(聚合锁风门 + 性能 + 版权) |
|
||||
| T-PRJ-06 | 发布审核 BPM 对接 📌 MVP = 轻量状态机 + admin 审核队列,Flowable 为远期 |
|
||||
| T-PRJ-07 | 专区 Zone 实体 + 游戏归属 + 运营位(精选 / 新游 / 全部)+ launchZone |
|
||||
| T-PRJ-08 | 统一发布编排(compliance→runtime→feed,失败回滚) |
|
||||
| T-PRJ-09 | 游戏工程包导出(+ 多渠道审核元数据预留) |
|
||||
| T-PRJ-10 | 版本回退 |
|
||||
|
||||
> CRUD = 增删改查;BPM = 业务流程 / 工作流引擎,Flowable 是一种重型 BPM 实现,MVP 先用轻量状态机 + admin 审核队列代替。**T-PRJ-08 的统一发布编排是全仓唯一一处真正的跨表原子发布**(把 compliance 裁决、runtime 出包、feed 入流串成一个事务,任一步失败就回滚),是脊柱级能力。
|
||||
>
|
||||
> **现在建到哪了**:✅ 完成。全仓最成熟:状态机 + 唯一真原子跨表发布编排 + 审核队列真查库都已落地。已知缺口仅 Zone 没有独立实体(用字段承载,MVP 有意简化)。
|
||||
|
||||
### 3.5 feed — 游戏流 + 专区分区 扩
|
||||
|
||||
feed 把**已发布的游戏按规则推荐排序成竖屏游戏流**(类似短视频信息流),回收玩家的互动信号,提供分享外链,并按 Zone 分区。它不存游戏本体(读 project)、不算质量分(消费 telemetry)、不裁决举报(转 compliance),只做"排序 + 分发 + 信号回收"这一件事。
|
||||
|
||||
- **边界 IN**:推荐打分 / 候选集、保底 / 降权 / 冷启动、cursor 分页 / 去重、互动信号写入、分享页 / OG、按 Zone 分区、A/B 实验框架。
|
||||
- **边界 OUT**:不存游戏本体 / Zone 实体(读 project)|不算质量分(消费 telemetry)|不裁决举报(转 compliance)。
|
||||
- **依赖**:project · telemetry · compliance。
|
||||
|
||||
| ID | 技术功能 | ID | 技术功能 |
|
||||
|---|---|---|---|
|
||||
| T-FED-01 | 规则推荐打分 | T-FED-09 | 分享落地页渲染 + OG 元数据 |
|
||||
| T-FED-02 | 候选集缓存 📌 MVP 直查 MySQL,Redis TTL 60s 为增长期 | T-FED-10 | 渠道参数解析(utm / channel) |
|
||||
| T-FED-03 | 新人保底曝光 | T-FED-11 | 行为信号加权排序 |
|
||||
| T-FED-04 | 低质内容降权 | T-FED-12 | 推荐策略 A/B 实验框架 |
|
||||
| T-FED-05 | 冷启动策略 | T-FED-13 | 个性化推荐算法 · 增长期 |
|
||||
| T-FED-06 | cursor 分页 | T-FED-14 | 精选池管理接口 |
|
||||
| T-FED-07 | Feed 去重 | T-FED-15 | 按 Zone 分区推荐(授权 IP / UGC 双区独立候选) |
|
||||
| T-FED-08 | 互动信号写入(赞 / 藏 / 享 / 举报 → 权重) | | |
|
||||
|
||||
> cursor 分页 = 用游标(而非页码)翻页,适合无限流;OG 元数据 = Open Graph,决定分享链接在社交平台的卡片预览;A/B 实验 = 同时跑两套策略对比效果。MVP 阶段推荐是"规则 + 信号"、不上机器学习,候选集可直查 MySQL(Redis 缓存是增长期形态)。
|
||||
>
|
||||
> **现在建到哪了**:🟡 部分。互动信号、getZones、quality 排序都是真的(B2 阶段已 e2e 实证"互动回灌翻转排序");但**游标分页是桩(永远返回第一页)**,候选集纯查 MySQL 无 Redis,举报转 compliance 也是桩。
|
||||
|
||||
### 3.6 telemetry — 遥测与数据底座
|
||||
|
||||
telemetry 是平台的**数据底座**。它统一摄取全链路的行为事件(创作、生成、游玩、互动),治理事件 Schema(数据格式规范),做增量聚合,计算每款游戏的 `quality_score`(质量分),并产出监控告警与数据质量信号。它只负责"把数据收上来、算清楚、喂出去",不渲染看板(那是产品域)、不做推荐决策(只产信号给 feed)。
|
||||
|
||||
- **边界 IN**:事件摄取 / SDK 上报、Schema 治理、聚合、质量 / 漏斗 / 留存 / 归因计算、埋点采集、健康监控、告警、数据质量、重复检测原子。
|
||||
- **边界 OUT**:不渲染看板 / 建议 / 导出 UI(供数据给产品域)|不做推荐决策(产信号给 feed)|不生成迭代内容(建议交 studio / aigc)。
|
||||
- **依赖**:feed · project · compliance。
|
||||
|
||||
telemetry 共 22 项技术功能,核心是"摄取 → 聚合 → 质量分 → 监控"四段:
|
||||
|
||||
| ID | 技术功能 | ID | 技术功能 |
|
||||
|---|---|---|---|
|
||||
| T-TEL-01 | 事件批量摄取(/events/batch) | T-TEL-08 | 玩家留存计算 |
|
||||
| T-TEL-02 | 事件 Schema 治理 | T-TEL-09 | 创作者复创率计算 |
|
||||
| T-TEL-03 | 埋点批量上报通道(sendBeacon 兜底) | T-TEL-13~15 | 实时监控(服务 / 生成 / Runtime 健康) |
|
||||
| T-TEL-04 | quality_score 计算 | T-TEL-16 | 异常检测与告警 |
|
||||
| T-TEL-05 | 增量聚合(GameDailyStats) | T-TEL-17 | 生成失败根因聚合 |
|
||||
| T-TEL-06 | 创作漏斗计算 | T-TEL-18 | 渠道归因计算 |
|
||||
| T-TEL-07 | 游戏流消费漏斗计算 | T-TEL-22 | 重复内容检测原子 |
|
||||
|
||||
> quality_score = 综合质量分,是 feed 排序的核心输入;sendBeacon = 浏览器在页面关闭时仍能可靠上报埋点的 API;GameDailyStats = 按天聚合的游戏统计表。
|
||||
>
|
||||
> **现在建到哪了**:✅ 完成。唯一真端到端的数据底座:事件落库 + quality 真算分 + 日聚合 upsert(有则更新无则插入)+ 创作者 stats 都打通了。已知缺口仅 MQ 异步聚合是桩(有意),广告营收字段占位。
|
||||
|
||||
### 3.7 pay — 支付收单 / 订单 / 退款对账
|
||||
|
||||
pay 把各类付费**统一收单为可追溯、可对账、可幂等的支付订单**,对上层屏蔽不同支付渠道的差异。它只管"把钱收进来",不定义会员 / 积分 / 内购的产品形态与定价(那是产品域配置),也不做分账 / 钱包 / 提现(交 trade)。
|
||||
|
||||
- **边界 IN**:支付渠道抽象、订单状态机、退款、对账、幂等。
|
||||
- **边界 OUT**:不定义会员 / 积分 / 内购产品形态与定价(产品域配置)|不做分账 / 钱包 / 提现(交 trade)。
|
||||
- **依赖**:huijing-system · trade · 微信 / 支付宝 / Apple IAP · Redis。
|
||||
|
||||
| ID | 技术功能 |
|
||||
|---|---|
|
||||
| T-PAY-01 | 支付网关抽象(微信 / 支付宝 / Apple IAP) |
|
||||
| T-PAY-02 | 支付订单状态机 |
|
||||
| T-PAY-03 | 退款处理 |
|
||||
| T-PAY-04 | 支付对账 |
|
||||
| T-PAY-05 | 支付幂等(Redis 防重复扣款 / 回调) |
|
||||
|
||||
> Apple IAP = 苹果应用内购买;幂等 = 同一笔操作重复执行不会重复扣款(靠 Redis 去重 + 订单状态机保证)。
|
||||
>
|
||||
> **现在建到哪了**:◻ 未接单体。pay 是 yudao(Huijing 上游开源框架)的原生模块,server 启动器里被显式注释排除。真实支付收单全未接入,卡在"支付进件"这类不可压缩的日历闸门(指必须等监管 / 渠道审批的外部环节)。
|
||||
|
||||
### 3.8 trade — 分账 / 结算 / 对账域
|
||||
|
||||
trade 是**无感的资金清算后端**。它把多来源的收入(广告、渠道分发、素材交易)按规则分账、归集、结算、对账,并出财税报表。它不收单(交 pay)、不产广告原始数据(读 ad)、不提供钱包 UI(只供数据给 studio),只做"钱进来之后怎么分、怎么结、怎么对账"。
|
||||
|
||||
- **边界 IN**:分账规则、收入归集与台账、T+1 / 月结、对账、佣金、税务、营收聚合、应收账款、提现门槛与打款、奖金 / 补贴发放执行。
|
||||
- **边界 OUT**:不做支付收单(交 pay)|不产广告收益原始数据(读 ad)|不撮合素材交易(读 ip)|不提供看板 / 钱包 UI(供数据给 studio)。
|
||||
- **依赖**:pay · ad · ip · telemetry。
|
||||
|
||||
| ID | 技术功能 | ID | 技术功能 |
|
||||
|---|---|---|---|
|
||||
| T-TRD-01 | 广告分成分账引擎(80% / 75% / 70%) | T-TRD-07 | 平台营收报表聚合 |
|
||||
| T-TRD-02 | 多渠道广告收入归集 | T-TRD-08 | 渠道分发收入统一台账 |
|
||||
| T-TRD-03 | T+1 / 月结结算周期调度 | T-TRD-09 | B 端应收账款管理 |
|
||||
| T-TRD-04 | 对账引擎 | T-TRD-10 | 财务合规(增值税 / 发票 / 个税) |
|
||||
| T-TRD-05 | 素材交易佣金计算(抽 30%) | T-TRD-11 | 提现门槛校验与自动打款执行 |
|
||||
| T-TRD-06 | 税务代扣与凭证生成 | T-TRD-12 | 奖金 / 补贴发放执行引擎 |
|
||||
|
||||
> T+1 = 交易次日结算;**广告分成的 80% / 75% / 70% 是分给创作者的三档分账比例**(按创作者等级 / 渠道区分);素材交易平台抽佣 30%。
|
||||
>
|
||||
> **现在建到哪了**:✅ 完成。资金状态机真(逐笔入账 + 冻结用 CAS 比较交换 + 对账补偿),41 个单元测试;打款做了 fail-fast(快速失败)防止打出假钱。已知缺口是真 pay 对接还是桩,受支付进件闸门 mock-gated(被 mock 挡在闸门前)。
|
||||
|
||||
### 3.9 community — 社交 / 互动 / 通知底座
|
||||
|
||||
community 承载**社交关系、互动计数、排行 / 成就计算与全渠道通知投递**。它是底座——提供能力原子,不定义"可感知的社交玩法形态"(那是产品侧的事)。它不渲染社区前端、不裁决内容安全(调 compliance)、不算质量分(读 telemetry)、不结算奖金(交 trade)。
|
||||
|
||||
- **边界 IN**:社交关系图谱、互动计数、排行计算、成就引擎、动态扇出、站内信、推送 / 邮件 / 短信通道、通知编排、等级自动升降计算。
|
||||
- **边界 OUT**:不渲染社区前端 / 不定义玩法(产品侧)|不裁决内容安全(调 compliance)|不算质量分(读 telemetry)|不结算奖金分成(交 trade)。
|
||||
- **依赖**:project · telemetry · compliance · huijing-system · huijing-infra · trade。
|
||||
|
||||
| ID | 技术功能 | ID | 技术功能 |
|
||||
|---|---|---|---|
|
||||
| T-CMU-01 | 社交关系图谱存储(关注 / 粉丝 / 好友) | T-CMU-07 | 推送通道适配(APNs / FCM / 厂商) |
|
||||
| T-CMU-02 | 互动数据存储与计数(评论 / 赞 / 弹幕) | T-CMU-08 | 邮件投递通道 |
|
||||
| T-CMU-03 | 多维排行榜计算与缓存 | T-CMU-09 | 短信投递通道 |
|
||||
| T-CMU-04 | 成就规则引擎与进度计数 | T-CMU-10 | 事件驱动通知编排(路由 / 模板 / 去重) |
|
||||
| T-CMU-05 | 创作者动态扇出 / timeline | T-CMU-11 | 创作者等级自动升降计算引擎 |
|
||||
| T-CMU-06 | 站内信投递与已读 | | |
|
||||
|
||||
> 动态扇出(fan-out)= 创作者发动态时把它推送到所有粉丝的时间线;APNs / FCM = 苹果 / 谷歌的推送服务。
|
||||
>
|
||||
> **现在建到哪了**:✅ 完成(MVP 裁剪后的范围)。通知底座 + 等级引擎是真的,三个上游(生成 / 审核 / 收益)的通知挂点全通,22 个单元测试。需要说明的是**排行榜 / 成就引擎根本没建**(MVP 有意裁剪),多级 / 多通道也未建。
|
||||
|
||||
### 3.10 ip — 素材安全 / 授权 / IP 风格原子 扩
|
||||
|
||||
ip 为素材和 IP(知识产权)资产提供**安全可信、授权可溯、风格可校的底座原子**,并按 Zone 双轨归类。它供给原子、不做最终裁决(锁风裁决归 compliance)、不做资金结算(交 trade)、不跑生成(aigc)。
|
||||
|
||||
- **边界 IN**:素材安全扫描、授权链追溯、授权 / 分成数据、IP 风格校验原子、冻结、盗用监测、模型训练对接、Zone 双轨归类。
|
||||
- **边界 OUT**:不做最终锁风裁决(供原子给 compliance)|不做资金结算(交 trade)|不跑生成(aigc)|不承载素材 / 模板市场交互(供料给产品域)。
|
||||
- **依赖**:compliance · trade · project · aigc · 基础设施 / OSS。
|
||||
|
||||
| ID | 技术功能 | ID | 技术功能 |
|
||||
|---|---|---|---|
|
||||
| T-IP-01 | 素材安全扫描(涉黄 / 暴 / 政) | T-IP-06 | IP 盗用监测 |
|
||||
| T-IP-02 | 商用授权链建模与追溯 | T-IP-07 | IP 风格模型训练对接 |
|
||||
| T-IP-03 | 授权 / 分成范围数据与计费口径 | T-IP-08 | 音色克隆训练对接 · 远期 |
|
||||
| T-IP-04 | IP 风格一致性校验原子(供锁风门) | T-IP-09 | 模板授权分成原子 |
|
||||
| T-IP-05 | 高风险素材冻结 | T-IP-10 | 素材按 Zone 双轨归类(授权 IP / UGC) |
|
||||
|
||||
> 授权链 = 一份素材"谁授权给谁、能用在什么范围"的可追溯链条;**T-IP-04 是锁风门两大原料之一**(另一个是 aigc 的 T-AGC-19)。
|
||||
>
|
||||
> **现在建到哪了**:◻ seam(接缝寄宿)。ip **有意不独立建模块,而是寄宿在 compliance 里**。当前风格原子是桩,素材安全 / 授权链 / 盗用监测全未建(属远期)。
|
||||
|
||||
### 3.11 compliance — 内容安全 / 审核 / 锁风门 扩
|
||||
|
||||
compliance 是**内容安全与审核中枢**——它做安全检测、跑审核状态机与人工复核、聚合风格原子做锁风裁决,并承载 RBAC(基于角色的访问控制)、审计、加密、安全基线、防沉迷。它是被 project / aigc / feed 共同依赖的横切地基,技术功能多达 38 项,是 13 个模块里最重的一个。
|
||||
|
||||
- **边界 IN**:文本 / 图片 / AI 产物安全检测、违禁词、审核状态机 / 分层 / 人工复核、举报降权、锁风门聚合裁决、RBAC、审计、安全基线、防沉迷接入、分级判定、渠道合规、封禁、备份。
|
||||
- **边界 OUT**:不自产风格检测原子(由 aigc / ip 供给)|不拥有项目实体(读 project)|不做推荐(产降权信号给 feed)|不展示隐私 / 申诉 UI(产品功能,交 studio)。
|
||||
- **依赖**:aigc(T-AGC-19)· ip(T-IP-04)· project · feed · huijing-system / bpm · 内容安全 API · Vault。
|
||||
|
||||
compliance 的 38 项技术功能可归为四块:**内容安全检测**(T-CMP-01~05)、**审核流程**(T-CMP-06~15,含核心的锁风门)、**权限与审计**(T-CMP-16~20)、**安全基线**(T-CMP-21~38)。核心几项:
|
||||
|
||||
| ID | 技术功能 |
|
||||
|---|---|
|
||||
| T-CMP-01~03 | 文本 / 图片 / AI 生成内容安全检测 |
|
||||
| T-CMP-05 | Prompt 注入防护 |
|
||||
| T-CMP-06 | 审核决策与状态机 |
|
||||
| T-CMP-09 | 人工审核队列 |
|
||||
| **T-CMP-12** | **锁风门 Gate**(聚合 T-AGC-19 + T-IP-04 → pass / review / block + 标准 / 严格 / 人工复核;挂 T-PRJ-05) |
|
||||
| T-CMP-16 | RBAC 权限控制 |
|
||||
| T-CMP-17 | 匿名用户权限约束 |
|
||||
| T-CMP-23 | OWASP Top 10 安全基线 |
|
||||
| T-CMP-27 | Secrets 管理(Vault) |
|
||||
| T-CMP-36 | 未成年人防沉迷接入 |
|
||||
| | (另含违禁词库、SSRF 防护、CORS/CSP、数据加密、审计日志 ≥180 天等共 38 项) |
|
||||
|
||||
> RBAC = 基于角色的访问控制;OWASP Top 10 = 业界公认的十大 Web 安全风险清单;Vault = 密钥管理工具;SSRF = 服务端请求伪造攻击;**T-CMP-12 锁风门是全平台的发布合规闸门**——它聚合 aigc 与 ip 的风格原子,裁决 pass / review / block,挂在 project 的发布前检查上。
|
||||
>
|
||||
> **现在建到哪了**:🟡 部分。锁风门框架 + 真注入发布链 + RBAC 都是真的;但**核心检测原子全是桩、恒返回 pass**(风格 / IP / 内容安全),意味着发布链对违规内容目前零自动拦截、纯靠人工兜底——这是放量 / 接广告 / 上渠道前必须硬化的合规死锁。
|
||||
|
||||
### 3.12 biz — B/G 端定制工程底座
|
||||
|
||||
biz 为 B 端(企业)/ G 端(政府)定制业务提供**报价、BPM 编排、签章、CRM、交付验收状态机与效果报告聚合**。它不生成游戏(aigc)、不持久化项目(project)、不收款结算(trade)、不裁决合规(compliance),只承载定制业务的"工程流程骨架"。
|
||||
|
||||
- **边界 IN**:报价计算、BPM 实例编排、电子签章对接、CRM 对接、交付验收状态机、效果数据聚合、代办工单跟踪。
|
||||
- **边界 OUT**:不生成游戏(aigc)|不持久化项目(project)|不收款结算(trade)|不裁决合规(compliance)|不承载 B 端 UI 语义(产品域)。
|
||||
- **依赖**:project · aigc · trade · compliance · bpm · 电子签章 · CRM。
|
||||
|
||||
| ID | 技术功能 | ID | 技术功能 |
|
||||
|---|---|---|---|
|
||||
| T-BIZ-01 | B 端项目报价引擎 | T-BIZ-05 | 交付验收状态机 |
|
||||
| T-BIZ-02 | BPM 定制项目编排(需求→报价→签约→交付) | T-BIZ-06 | B 端效果数据聚合 |
|
||||
| T-BIZ-03 | 电子签章服务对接 | T-BIZ-07 | 代办流程外部工单与状态跟踪(软著 / 渠道 / 资质) |
|
||||
| T-BIZ-04 | CRM 客户 / 合同数据对接 | T-BIZ-08 | 收费与变更管理(预付 / 尾款 / 变更单) |
|
||||
|
||||
> CRM = 客户关系管理;软著 = 软件著作权登记。
|
||||
>
|
||||
> **现在建到哪了**:🟡 部分。lead(销售线索)状态机 + 报价落库是真的,13 个单元测试;但模板硬编码 4 个(而非完整 32 套),报价引擎 / 签章 / CRM 纯未建。
|
||||
|
||||
### 3.13 ad — 广告引擎(植入 → 展示 → 计费 → 优化)
|
||||
|
||||
ad 把**联盟接入、广告位 AI 植入、曝光计费、eCPM 优化与归因**做成游戏内广告的后端原子。它不渲染广告 UI(那由 runtime 的 SDK 在游戏内呈现)、不做结算提现(交 trade)、不产看板视图(telemetry / biz 消费它的归因信号)。
|
||||
|
||||
- **边界 IN**:联盟 SDK 对接、AI 植入、曝光上报与计费、eCPM 优化、合规植入校验、效果归因。
|
||||
- **边界 OUT**:不渲染广告 UI(runtime SDK 在游戏内呈现)|不做结算提现(trade)|不产看板视图(telemetry / biz 消费归因信号)。
|
||||
- **依赖**:runtime · trade · telemetry。
|
||||
|
||||
| ID | 技术功能 | ID | 技术功能 |
|
||||
|---|---|---|---|
|
||||
| T-AD-01 | 广告位 AI 自动植入 | T-AD-06 | 展示上报与有效曝光计费 |
|
||||
| T-AD-02~05 | 联盟对接(穿山甲 / 优量汇 / 快手 / 百青藤) | T-AD-07 | eCPM 优化与精准匹配 |
|
||||
| T-AD-08 | 广告合规植入校验 | T-AD-09 | 广告位效果归因 |
|
||||
|
||||
> eCPM = 每千次展示有效收入,是衡量广告变现效率的核心指标;穿山甲 / 优量汇 = 国内主流广告联盟;归因 = 把一次收益正确算到对应的游戏 / 渠道上。
|
||||
>
|
||||
> **现在建到哪了**:✅ 完成。计费全链真(合规 → 幂等 → 归因 → 台账)+ 验签 fail-fast,24 个单元测试。已知缺口是**真联盟 SDK(穿山甲 / 优量汇)是桩**,受广告审核闸门 mock-gated。
|
||||
|
||||
> **广告曝光反作弊补登(2026-06-10)**:ad 的两个计费端点(impression 曝光 / reward 激励)已随鉴权波放开匿名访问。MVP 形态的反作弊 = 幂等键 traceId + 按 IP 维度限流 + 消费 telemetry 的异常剔除桩(AnomalyFilterService,按匿名 ID / IP 滑窗,已实证 IP 维拦截);**真接广告联盟前,必须升级为正式反作弊项**(曝光 / 激励计费事件异常剔除 + 对账闸门)。
|
||||
|
||||
---
|
||||
|
||||
## 4. 13 个模块现在各建到了什么程度
|
||||
|
||||
把上面每张卡片末尾的状态汇成一张表,这是判断"哪里能用、哪里是债"的快速索引。口径三档:**✅ implemented-real**(真端到端、可验收)、**🟡 partial-stub**(有壳 / 半真 / 部分接线)、**◻ 特殊形态**(seam 寄宿或未接单体)。状态以代码 / 测试 / e2e 实测为准,不以文档声称为准。
|
||||
|
||||
| # | 模块 | 状态 | 一句话 | 最大缺口 / 债 |
|
||||
|---|---|---|---|---|
|
||||
| 1 | **project** 项目 | ✅ 完成 | 全仓最成熟:状态机 + 唯一真原子跨表发布编排 + 审核队列真查库 | Zone 无独立实体(字段承载,MVP 有意简化) |
|
||||
| 2 | **aigc** 生成 | 🟡 部分 | 执行器主链真(M2 e2e,一句话 18–27s 可玩,80% 成功);SAA 控制平面"建成未默认生产" | W-G1 worker 未接为 staging 默认产线;玩法模板注册未建 |
|
||||
| 3 | **runtime** 运行时 | 🟡 部分 | 存包 / 取包 / engineBundle 内嵌真,沙箱桥接真 | 真实编译引擎 = 桩;渠道转换(微信 / 抖音 / 快手 / TapTap)未建;OSS 存取桩 |
|
||||
| 4 | **feed** 信息流 | 🟡 部分 | 互动信号 / getZones / quality 排序真,B2 回灌翻转 e2e 实证 | 游标分页 = 桩(永远第一页);候选集纯查 MySQL 无 Redis;举报转 compliance 桩 |
|
||||
| 5 | **telemetry** 遥测 | ✅ 完成 | 唯一真端到端数据底座:事件落库 + quality 真算分 + 日聚合 upsert + 创作者 stats | MQ 异步聚合 = 桩(有意);广告营收字段占位 |
|
||||
| 6 | **ad** 广告 | ✅ 完成 | 计费全链真(合规 → 幂等 → 归因 → 台账)+ 验签 fail-fast,24 单测 | 真联盟 SDK(穿山甲 / 优量汇)= 桩,受广告审核闸门 mock-gated |
|
||||
| 7 | **trade** 资金 | ✅ 完成 | 资金状态机真(逐笔入账 + 冻结 CAS + 对账补偿),41 单测;打款 fail-fast 防假钱 | 真 pay 对接 = 桩,受支付进件闸门 mock-gated |
|
||||
| 8 | **community** 社区 | ✅ 完成 | 通知底座 + 等级引擎真,3 上游 notify 挂点全通,22 单测 | 排行榜 / 成就引擎根本没建(MVP 裁剪);多级 / 多通道未建 |
|
||||
| 9 | **studio** 工作室 | 🟡 部分 | 创作编排委托层真(草稿装配 + aigc 接缝 + 血缘) | "7 步生成链"不在 studio(在 aigc SAA 图);六资产 / 角色 rig / 对白树 / 可视化全桩 |
|
||||
| 10 | **compliance** 合规 | 🟡 部分 | 锁风门框架 + 真注入发布链 + RBAC 真 | 核心检测原子全桩恒 pass(风格 / IP / 内容安全)→ 发布链零自动拦截,纯人工兜底 |
|
||||
| 11 | **biz** B 端 | 🟡 部分 | lead 状态机 + 报价落库真,13 单测 | 模板硬编码 4 个(非 32 套);报价引擎 / 签章 / CRM 纯未建 |
|
||||
| 12 | **ip** 版权 | ◻ seam | 有意不独立建,寄宿 compliance | 风格原子桩;素材安全 / 授权链 / 盗用监测全未建(远期) |
|
||||
| 13 | **pay** 支付 | ◻ 未接单体 | yudao 原生模块,server 显式注释排除出启动器 | 真实支付收单全未接,受支付进件闸门 |
|
||||
|
||||
**合计**:✅ 完成 5(project / telemetry / ad / trade / community)· 🟡 部分 6(aigc / runtime / feed / studio / compliance / biz)· ◻ 特殊 2(ip / pay)· 🔴 纯未建 0。
|
||||
|
||||
几条值得单独记住的判断:
|
||||
|
||||
- **地基稳**:单体真启、Flyway(数据库迁移)V1–V17 全绿、12 份契约 yaml 锁定、project 真原子跨表发布、telemetry 真闭环——脊柱可承重,不存在 split-brain(指"代码两套各跑各的、互不一致"的脑裂状态)。
|
||||
- **生成主线单独评 🟡(护城河关键路径)**:W-G1 worker 已实证 HJ-GEN-001 成立(便宜模型在 LittleJS 插件库里写出可真玩的轻游戏,经 7 道 CDP 真机真玩验证,单款成本 ¥0.01–0.06,远低于 ¥0.15 的成本闸)。但 worker 尚未接为默认产线,玩法模板未建,插件库还有 4 项短板(文本 HUD / grid / CCD / drag)。
|
||||
- **三处合规与变现的"逻辑已真、卡在闸门"**:ad / trade 的业务逻辑都已是真的、有几十个单元测试,但真广告联盟 / 真支付渠道被 mock 挡着——卡的不是代码,而是 ICP 备案、支付进件、广告审核这类**不可压缩的日历闸门**(必须等外部审批的时间)。策略是先把逻辑建好,闸门到位即接。
|
||||
|
||||
> **结构权威的边界**:本页给的是"应该怎么切"(模块边界与 T-id 注册表),不是验收清单、也不是实现设计。当结构、实现、状态三者出现冲突时,**优先级是:状态 > 实现 > 结构**——以 `docs/mvp/MVP进度总账.md` 的真 / 桩 / 未建状态为最高口径,以 `docs/agent-specs/_archive/2026-06-09-核心功能技术实现方案.md` 为实现细节,本页只定结构。各模块的产品出口(哪条产品需求由它实现)见[需求模块映射](../产品/需求模块映射.md)。
|
||||
|
||||
---
|
||||
|
||||
## 5. 相关文档
|
||||
|
||||
| 文档 | 回答什么 |
|
||||
|---|---|
|
||||
| [架构域主文档](README.md) | 整体分层、关键技术决策的"为什么"、一条生成请求怎么跑通(本页是它 §3–4 的下钻) |
|
||||
| [生成引擎](生成引擎/) | aigc / runtime 生成主线的深层架构:SAA 节点拓扑、new-api 接入、九门 harness |
|
||||
| [需求模块映射](../产品/需求模块映射.md) | 每条产品需求由哪些技术模块(T-id)实现的 M:N 映射 |
|
||||
| `docs/mvp/MVP进度总账.md` | 13 模块真 / 桩 / 未建的实时状态(状态冲突时的最高口径) |
|
||||
|
||||
> **源头长文档**(需逐条考据时回溯):`技术架构与模块.md`(HJ-TECH-002,13 模块完整卡片与 204 项 T-id 注册表)、`2026-06-17-产品功能与技术模块-完成度与优先级总账.md`(逐条读码核实的完成度)。
|
||||
410
docs/architecture/架构/README.md
Normal file
410
docs/architecture/架构/README.md
Normal file
@ -0,0 +1,410 @@
|
||||
# 架构域 · 设计主文档
|
||||
|
||||
> **这是什么**:绘境AI 架构域的设计主文档,回答"**这套系统用什么技术、怎么搭起来**"(HOW),不重复"产品对用户提供什么"(WHAT——那部分在[产品域](../产品/README.md))。
|
||||
> **给谁看**:CTO、技术合伙人、首席架构师、新加入的工程师、外部技术尽调。
|
||||
> **怎么读**:先读本页建立全局的技术认知——分层怎么分、关键选型为什么这么选、13 个后端模块各管什么、它们之间怎么依赖;需要某个子系统的更深细节时,再深入文末的子文档(如[生成引擎](生成引擎/))。
|
||||
> **读这一页 = 当前架构真相**。源头有三份很长的设计文档(技术决策版 1500+ 行、技术架构与模块、开发团队版),本页只取它们的**架构骨架与关键决策的"为什么"**;实现级细节、环境搭建、部署命令不在这里(分别交给各子文档与[运维域](../运维/))。
|
||||
|
||||
---
|
||||
|
||||
## 1. 一张图看懂整体分层
|
||||
|
||||
绘境AI 的后端基于 **Huijing Cloud**(一套开源的 Java 企业级后台框架,基于 Spring Cloud Alibaba;我们 fork 它做二次开发,下文简称 Huijing)搭建,在它提供的基座能力(用户/权限/工作流/文件等)之上,叠加 13 个游戏业务模块。整套系统自上而下分为六层:用户从浏览器进来,经接入层和网关层,落到业务服务层;业务服务依赖 Huijing 原生的基础设施层,并向上调用一个独立的 AI 生成层来产出游戏;所有这些都坐在中间件层(数据库/缓存/对象存储)之上,由可观测性层旁路监控。
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
subgraph ACCESS["接入层 — 把请求接进来"]
|
||||
CDN["CDN<br/>静态资源 / 游戏包加速"]
|
||||
NGINX["Nginx<br/>前端托管 / SSL 终结"]
|
||||
end
|
||||
subgraph FE["前端应用 — 用户看到的界面"]
|
||||
STUDIO["game-studio<br/>Vue3 + Vant<br/>创作者 + 玩家"]
|
||||
ADMIN["game-admin<br/>Vue3 + Element Plus<br/>运营 + 管理员"]
|
||||
end
|
||||
subgraph GW["网关层 — 统一入口"]
|
||||
GATEWAY["Spring Cloud Gateway<br/>路由 / 限流 / 鉴权 / 灰度"]
|
||||
end
|
||||
subgraph BIZ["业务服务层 — 13 个游戏业务模块"]
|
||||
M["studio · project · aigc · runtime · feed<br/>telemetry · pay · trade · community<br/>ip · compliance · biz · ad"]
|
||||
end
|
||||
subgraph INFRA["基础设施层 — Huijing 原生开箱即用"]
|
||||
SYS["system 用户/权限/OAuth2"]
|
||||
INFRASVC["infra 文件/任务/日志"]
|
||||
BPM["bpm 工作流"]
|
||||
end
|
||||
subgraph AI["AI 生成层 — 现行生成主线"]
|
||||
SAA["SAA 裸图编排<br/>Spring AI Alibaba v1.1.2.2"]
|
||||
NEWAPI["new-api 网关<br/>OpenAI 兼容 / 多模型"]
|
||||
LLM["便宜 LLM<br/>DeepSeek / MiniMax"]
|
||||
LJS["LittleJS + Runner v2<br/>Tier1 引擎 / 插件库"]
|
||||
end
|
||||
subgraph MW["中间件层 — 数据与存储"]
|
||||
MYSQL["MySQL 8.0"]
|
||||
REDIS["Redis 7"]
|
||||
MINIO["MinIO / 阿里云 OSS"]
|
||||
FUTURE["Nacos · RocketMQ<br/>(框架自带 · MVP 未部署)"]
|
||||
end
|
||||
subgraph OBS["可观测性层 — 旁路监控"]
|
||||
OBSV["Prometheus · Grafana<br/>Sentry · Jaeger"]
|
||||
end
|
||||
|
||||
NGINX --> STUDIO & ADMIN
|
||||
STUDIO & ADMIN --> GATEWAY
|
||||
GATEWAY --> BIZ
|
||||
BIZ --> INFRA
|
||||
BIZ --> AI
|
||||
AI --> NEWAPI
|
||||
NEWAPI --> LLM
|
||||
BIZ --> MW
|
||||
```
|
||||
|
||||
这套分层有一条贯穿始终的设计取向:**先简后扩,能复用就不自研**。后端复用 Huijing 现成的 60% 后台能力;生成不自研大模型而是接通用模型;引擎不自研而是用成熟的 LittleJS。系统因此能用很小的团队(建议 5 人)和很低的成本(MVP 基础设施约 ¥4,300/月,上限 < ¥5,000/月)跑起来,同时为后续从单体平滑长成微服务预留好了路径。
|
||||
|
||||
---
|
||||
|
||||
## 2. 关键技术决策:每一项的"为什么"
|
||||
|
||||
架构里真正值钱的不是组件清单,而是**为什么选 A 不选 B**。下面把对整体形态影响最大的几项决策讲清楚,每项都给出它击败的候选与理由。这些是经过多轮验证(spike,即小规模技术验证实验)和创始人裁定后落定的现行真相;被推翻的旧方案保留为"决策史",帮助理解演进,但**不再是现行架构**。
|
||||
|
||||
### 2.1 后端框架 = Huijing Cloud(Java 17 + Spring Cloud Alibaba)
|
||||
|
||||
| 候选 | 结论 | 理由 |
|
||||
|---|---|---|
|
||||
| **Huijing Cloud** | ✅ 选用 | 60%+ 后台能力开箱即用——RBAC(基于角色的访问控制)、OAuth2、BPM(业务流程/工作流引擎)、文件、通知、审计、代码生成、多租户全覆盖;社区活跃(60k+ star);fork 二开可控 |
|
||||
| NestJS (Node.js) | ❌ 放弃 | 早期原型已验证单体可行,但缺企业级基础设施,后台管理/工作流要从零建 |
|
||||
| Go (Kratos / go-zero) | ❌ 放弃 | 性能好但后台基础设施缺失,RBAC/BPM/代码生成无现成方案 |
|
||||
|
||||
选 Huijing 的核心算盘:把"做后台基础设施"这件耗时但不差异化的事直接买现成的,把团队的力气全押在生成、流量、变现这三件真正的护城河上。
|
||||
|
||||
### 2.2 生成主线 = new-api 网关 → 便宜 LLM + SAA 裸图编排(现行)
|
||||
|
||||
这是整个平台**演进最剧烈、也最关键**的一处决策,值得展开。
|
||||
|
||||
最初的蓝图原案是 **Dify + OpenGame**(Dify 是一个带可视化工作流编排界面的 LLM 应用平台;OpenGame 是一套游戏生成 agent),理由是开箱即用、省自研时间。但随后的技术验证推翻了它:
|
||||
|
||||
- **C2 裁定(2026-06-09)**:一次 spike 用 4 个模板 × 13 个创意 = **52/52 结构层 100%** 通过,证实了"LLM 直连便宜模型直出可玩游戏 + 固定运行时"这条更轻的路就能达标。
|
||||
- **HJ-GEN-001 终审(2026-06-12)**:进一步把方向定为"agent 写码于插件库"(让模型直接写游戏代码,落在 LittleJS 能力插件库里),把更早的"游戏模板/填参式模板"路线退役(代号 **W-CLEAN**,即清理旧模板的收口工作:旧 4 模板 clicker/dodge/runner/match + 存量数据已清除)。
|
||||
- **HJ-AGI-002(2026-06-15)**:把编排基建定为 **SAA 裸 `StateGraph` 编排**。SAA = Spring AI Alibaba(阿里巴巴出的 Spring AI 框架,GA 版 v1.1.2.2);`StateGraph` 是它的状态图编排原语;"裸图"指我们直接用它的状态图节点布线,而不是用它更上层的 ReactAgent 封装。
|
||||
|
||||
结果:**Dify / OpenGame 均从未部署、降级为远期增强**;AgentScope(另一个 agent-native 探索框架)降为 long-term premium 独立轨,同样不在 MVP 运行时。现行主线已经过后端实测——`game-module-aigc-server` 有 14 个文件接通 new-api,`spring-ai-alibaba` 已进 pom 并跑起 `SaaStudioGraph`。
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
subgraph 现行["✅ 现行主线(已实测)"]
|
||||
A1["创作者 Prompt"] --> A2["SAA 裸 StateGraph 编排<br/>11 节点 / 4 条件边"]
|
||||
A2 --> A3["new-api 网关<br/>OpenAI 兼容 · 多模型"]
|
||||
A3 --> A4["便宜 LLM<br/>DeepSeek-v4 / MiniMax-M2.7·M3"]
|
||||
A4 --> A5["harness 九门兜底<br/>校验 / 构建 / 真玩"]
|
||||
A5 --> A6["GamePackage 出包"]
|
||||
end
|
||||
subgraph 决策史["❌ 蓝图原案(从未部署 · 降远期)"]
|
||||
B1["Dify 可视化编排"]
|
||||
B2["OpenGame 生成 agent"]
|
||||
end
|
||||
```
|
||||
|
||||
几个术语一句话解释:
|
||||
|
||||
- **new-api 网关**:一个自部署的 LLM 网关,对外提供 OpenAI 兼容接口,对内可挂多个模型通道。它让"换模型/多通道兜底"变成网关层的配置事,业务代码无感;且单 key 自动入 `newapi_cost` 计费平面,成本可对账。接入时有个工程坑:baseUrl 要剥掉末尾的 `/v1`。
|
||||
- **harness 九门**:生成产物要真正"能玩"才算数,所以在生成链路后端串了九道确定性门(校验 → 构建 → 真玩),**done 由这些确定性门判定,不让 LLM 给自己打分**。门不过就不出包。
|
||||
- **SAA 图的形状**:11 个节点 + 4 处条件边,主链是 `START → render → design → generate → validate → scaffold → build → play → player → emit → END`,validate 不通过则走 `repair → generate` 回环修复,修不好则 `giveup`。唯一布线源是 `SaaStudioGraph.assemble()`,生产派发与回归测试共用同一份,避免布线漂移。
|
||||
|
||||
> 演进的更深细节(16/11 节点拓扑、加节点方法、checkpoint 框架坑、观测、dispatcher 契约)在子文档[生成引擎](生成引擎/)与 `.agents/skills/saa-graph-orchestration.md`。
|
||||
|
||||
### 2.3 游戏运行时引擎 = LittleJS 增强发行版(Tier1)+ Cocos(Tier2/3)
|
||||
|
||||
运行时按产物复杂度分层,不同层用不同引擎。LittleJS 是一个极小的开源 2D 游戏引擎;"增强发行版"指我们在它基础上叠了能力插件库与装载契约。
|
||||
|
||||
| 候选 | 结论 | 理由 |
|
||||
|---|---|---|
|
||||
| **LittleJS 增强发行版 + Runner v2(Tier1)** | ✅ 现行 | 引擎仅 **55KB gz**;URL 化交付享 HTTP + 编译双缓存,**冷启动 0.54s vs 自研壳 2.86s(快 4.8~5.3 倍)**;agent 写码于插件库直接可运行,装载契约为 `game-host.d.ts`;平台完全掌控沙箱与 SDK 注入。**MVP 唯一交付层**(2026-06-12 终裁,spike 比分 85/82) |
|
||||
| ~~自研轻量 Canvas Runtime < 15KB~~ | ❌ 已废除 | 自研壳实测只是机制演示(资产载而不绘、零打磨、只能赢不能输),手搓引擎属 critical risk;**15KB 红线一并废除**——它本是"srcdoc 内联"那套老架构衍生出的约束,前提失效就该废,改为三层约束框架(见下) |
|
||||
| **Cocos Creator 3.8.8 + MCP(Tier2/3)** | ✅ 选用 | 一栈覆盖复杂 2D+3D+原生与小游戏导出;MCP(158 个工具)可被 AI 驱动出功能快;MVP 阶段至多 1 个探针 demo |
|
||||
| Phaser 3 / LayaAir / Unity / Three.js | ❌ 放弃 | Phaser 经 T1 eval-spike 败于冷启动与交付形态;LayaAir 引擎过重且官方导出**不含快手**;Unity 启动重(7-10s)与 P75<3s 目标冲突;Three.js 仅 web3D 与 Cocos 重复 |
|
||||
|
||||
废掉 15KB 红线后,质量改由**三层约束框架**守:① 性能 SLO(在千元机 + 4G 网络下的首屏 P75 达标);② 预算入场券(压缩后 gz≤350KB、原始 raw≤1.5MB);③ 工程增强层(juice/手感等)。沙箱、SDK、三容器控制点保持不变。
|
||||
|
||||
> **模板哲学(W-CLEAN)**:"游戏模板/填参式模板"已废除——模板 = LittleJS 能力插件/二次开发件,玩法/美术/关卡/UI 全是 agent 生成域。需要区分的是,**"玩法模板"(指品类/玩法框架,用来引导 AI 生成,而非预制代码)并未废除**,它是有效功能、待建,但优先级排在"生成可靠"(Tier 0)之后(HJ-DEMO-AUDIT-001,创始人 2026-06-17)。
|
||||
|
||||
为什么要分层:Web 预览和游戏流要求极快加载(P75<3s),Tier1 的 LittleJS 正好满足,所以 MVP 只交付这一层;复杂 2D/3D/原生(Tier2/3,远期)需要成熟引擎,复用 Cocos 而非自研(自研 3D/原生引擎工期数十人月不可行),且 MCP 让 AI 驱动可行;多渠道导出以微信小游戏格式包为统一中转,可异步离线进行,不影响实时预览。
|
||||
|
||||
### 2.4 AI 素材工具链 = mmx-cli(MiniMax)
|
||||
|
||||
游戏要图、要音乐、要封面。这一链路现行统一走 **mmx-cli**(MiniMax 的命令行工具,2026-06-12 创始人亲验拍板默认):agent 造游戏时直接 CLI 调用,**免 GPU、免训练**,成本随 new-api 单 key 自动入计费台账。原候选 **ComfyUI**(开源、可训 IP 风格 LoRA、自部署无审查,但需 GPU,无 GPU 走 CPU 慢 10 倍)退为备选;Stability Audio 等降级远期。语音/音色克隆备选 Fish Audio / 阿里 CosyVoice(中文效果好)。
|
||||
|
||||
### 2.5 其余基线选型(简表)
|
||||
|
||||
| 维度 | 选型 | 一句话理由 |
|
||||
|---|---|---|
|
||||
| 前端 admin | Vue3 + Element Plus | Huijing 官方主推,二开友好 |
|
||||
| 前端 studio | Vue3 + Vant | 移动优先组件库,适配游戏流滑动 |
|
||||
| 数据库 | MySQL 8.0 | Huijing 默认,社区方案最多,迁移成本最低 |
|
||||
| 对象存储 | MinIO(本地)/ 阿里云 OSS(生产) | 对象存储 + CDN 加速,放游戏包/素材/封面 |
|
||||
| 缓存/热数据 | Redis 7 | 低延迟,Sorted Set 适合推荐候选集排序 |
|
||||
| 消息队列 | RocketMQ 5(**future-state**) | Huijing 默认集成,延迟/事务消息完整;**MVP 未部署 broker**,生成派发现走进程内 |
|
||||
| 配置/注册 | Nacos(**future-state**) | Huijing 框架自带;**MVP 未部署 registry**,单体走本地配置 + Spring Profile |
|
||||
|
||||
> **关于 future-state(框架自带、MVP 未部署)**:RocketMQ 与 Nacos 是 Huijing(yudao fork)框架自带的依赖,yaml 配置也在仓里,但 **MVP 运行时并未启动 broker / registry**。所以凡是涉及"异步 MQ""服务注册发现"的设计,在 MVP 阶段都以"进程内调用 / 本地配置"落地;待产能或异步化需求上台阶时再启用。读设计时遇到这两者,一律按此折算。
|
||||
|
||||
---
|
||||
|
||||
## 3. 13 个后端业务模块总览
|
||||
|
||||
业务能力按领域边界切成 **13 个模块**,高内聚、低耦合。它们物理上以 jar 聚合进 Huijing 单体一起运行(MVP 单体启动:所有模块编译进同一个 JAR `game-server`,用 Spring Profile 控制加载),逻辑上各自独立、可被未来拆分。每个模块有一个三字母前缀,技术功能用 `T-{模块}-{nn}` 编号(共 204 项技术功能,作为结构权威的注册表)。
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
subgraph 创作["创作链路"]
|
||||
STU["studio · 创作编排/编辑器域"]
|
||||
AGC["aigc · 无状态生成原子"]
|
||||
RT["runtime · 编译/沙箱/多渠道发布"]
|
||||
PRJ["project · 项目生命周期 + 专区 Zone"]
|
||||
end
|
||||
subgraph 分发["分发与数据"]
|
||||
FED["feed · 游戏流推荐/互动/分享"]
|
||||
TEL["telemetry · 事件摄取/聚合/质量评分"]
|
||||
end
|
||||
subgraph 变现["变现链路"]
|
||||
PAY["pay · 支付收单/订单/退款对账"]
|
||||
TRD["trade · 分账/结算/钱包/财税"]
|
||||
AD["ad · 广告联盟/植入/曝光计费/归因"]
|
||||
end
|
||||
subgraph 生态["平台与合规"]
|
||||
CMU["community · 社交/互动/排行/通知"]
|
||||
IP["ip · 素材安全/授权链/IP 风格原子"]
|
||||
CMP["compliance · 内容安全/审核/锁风门/RBAC"]
|
||||
BIZ["biz · B/G 端定制工程底座"]
|
||||
end
|
||||
```
|
||||
|
||||
| 模块 | 前缀 | 一句话职责 | 建设形态(2026-06-10 审计) |
|
||||
|---|---|---|---|
|
||||
| studio | STU | 把"创作会话"编排成可试玩草稿:调度 aigc 原子、装配多资产、管理角色 rig / 对白树 / 任务链 | ★新增 |
|
||||
| aigc | AGC | 单 Prompt → 单确定性产物(GameConfig/图/音/剧情/封面/校验/风格指纹),无状态、幂等、可重放 | 收敛 |
|
||||
| runtime | RT | GameConfig → 版本化可运行包,沙箱预览,多渠道转换与发布状态机 | 扩 |
|
||||
| project | PRJ | 项目/版本/草稿全生命周期与状态流转,编排发布,承载专区 Zone 实体 | 扩 |
|
||||
| feed | FED | 已发布游戏按规则推荐成竖屏游戏流,回收互动信号,分享外链,按 Zone 分区 | 扩 |
|
||||
| telemetry | TEL | 统一摄取全链路事件,治理 Schema、增量聚合、算 quality_score、产出监控告警 | — |
|
||||
| pay | PAY | 把各类付费统一收单为可追溯、可对账、可幂等的支付订单 | huijing 原生(未接入单体) |
|
||||
| trade | TRD | 把多源收入按规则分账、归集、结算、对账并出财税报表 | — |
|
||||
| community | CMU | 社交关系、互动计数、排行/成就、全渠道通知投递 | 未建(Wave4) |
|
||||
| ip | IP | 为素材/IP 资产提供安全可信、授权可溯、风格可校的底座原子,按 Zone 双轨归类 | seam 寄宿 compliance |
|
||||
| compliance | CMP | 内容安全与审核中枢 + 锁风门裁决,并承载 RBAC/审计/加密/安全基线/防沉迷 | 扩 |
|
||||
| biz | BIZ | 为 B/G 端定制提供报价、BPM 编排、签章、CRM、交付验收状态机 | 未建(Wave4) |
|
||||
| ad | AD | 把联盟接入、广告位 AI 植入、曝光计费、eCPM 优化与归因做成游戏内广告后端 | — |
|
||||
|
||||
几个跨模块的关键概念:
|
||||
|
||||
- **专区 Zone**:运营用的双轨分区(授权 IP 区 / UGC 用户原创区),owner 是 project;feed 据它分区推荐,ip 据它双轨归类。UGC = User Generated Content,用户生成内容。
|
||||
- **锁风门 Gate**(T-CMP-12):一道"风格-版权一致性"门,owner 是 compliance。它聚合 aigc 的风格检测原子(T-AGC-19)和 ip 的 IP 风格校验原子(T-IP-04),裁决 pass / review / block,挂在 project 的发布前检查(T-PRJ-05)上。"锁风"= 锁定风格、防侵权。
|
||||
|
||||
> 各模块卡片(职责 IN/OUT 边界、技术功能 T-id 清单)与重划状态详见 `docs/architecture/技术架构与模块.md`;真/桩/未建的实时状态以 `docs/mvp/MVP进度总账.md` 为准。**冲突时口径:状态 > 实现 > 结构**。
|
||||
|
||||
---
|
||||
|
||||
## 4. 模块依赖:为什么是这个方向
|
||||
|
||||
模块间是**单向、低耦合**的依赖,只经各模块的 `-api` 包(声明 DTO + Feign 接口)交互。下图是依赖方向(箭头 A→B 表示 A 依赖 B):
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
STU[studio] --> AGC[aigc]
|
||||
STU --> RT[runtime]
|
||||
STU --> IP[ip]
|
||||
STU --> PRJ[project]
|
||||
STU --> CMP[compliance]
|
||||
AGC --> CMP
|
||||
AGC --> PRJ
|
||||
RT --> PRJ
|
||||
RT --> TEL[telemetry]
|
||||
FED[feed] --> PRJ
|
||||
FED --> TEL
|
||||
TEL --> FED
|
||||
IP --> CMP
|
||||
IP --> TRD[trade]
|
||||
CMP --> AGC
|
||||
CMP --> IP
|
||||
PRJ --> CMP
|
||||
TRD --> PAY[pay]
|
||||
TRD --> AD[ad]
|
||||
AD --> RT
|
||||
BIZ[biz] --> PRJ
|
||||
BIZ --> AGC
|
||||
BIZ --> TRD
|
||||
CMU[community] --> PRJ
|
||||
```
|
||||
|
||||
读这张图有几条主线:**创作侧** studio 站在最上游,把活分派给 aigc(生成)、runtime(编译)、ip(素材)、project(落库)、compliance(安全);**生成原子** aigc 只向下依赖 compliance(Prompt 安全)和 project(写结果),自己保持无状态;**数据回路** telemetry 与 feed 互相依赖(feed 消费质量信号、telemetry 回收互动),形成"越用越聪明"的闭环;**资金侧** trade 向下依赖 pay(收单)和 ad(广告收入),自己只管分账结算。这种单向切分让任何一个模块都能独立演进、独立测试,也是未来从单体拆成微服务时的天然切割线。
|
||||
|
||||
---
|
||||
|
||||
## 5. 一条生成请求是怎么跑通的
|
||||
|
||||
把上面的分层、生成主线、模块依赖串起来,看一次"一句话生成游戏"的完整时序——这是创作链路的核心路径。注意生成任务的派发走 `GenerationDispatcher`(http worker 与进程内 SAA 图二选一、单写),生成态 SAA 编排藏在 job/callback 契约后,保持可替换。
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant C as 创作者
|
||||
participant S as game-studio
|
||||
participant GW as Gateway
|
||||
participant AIGC as aigc 模块
|
||||
participant SAA as SAA StateGraph 编排
|
||||
participant NEWAPI as new-api 网关
|
||||
participant LLM as 便宜 LLM
|
||||
participant GATE as harness 九门
|
||||
participant PRJ as project 模块
|
||||
|
||||
C->>S: 输入 Prompt + 选风格
|
||||
S->>GW: POST /app/aigc/generate
|
||||
GW->>AIGC: 转发(鉴权通过)
|
||||
AIGC->>AIGC: 创建生成任务(GenerationDispatcher 派发)
|
||||
AIGC-->>S: 202 Accepted {taskId}
|
||||
S->>S: 轮询/SSE 监听任务状态
|
||||
|
||||
AIGC->>SAA: dispatch(job) 进入裸图编排
|
||||
SAA->>SAA: 节点:render→design→generate(11 节点/4 条件边)
|
||||
SAA->>NEWAPI: 各节点经 OpenAI 兼容接口调模型
|
||||
NEWAPI->>LLM: 转发(单 key → 自动入 newapi_cost 计费)
|
||||
LLM-->>NEWAPI: 返回 GameConfig / 游戏代码
|
||||
NEWAPI-->>SAA: 模型产物
|
||||
SAA->>GATE: 校验/构建/真玩九门(done 由确定性门定)
|
||||
GATE-->>SAA: 通过 → 出 GamePackage;不通过 → 修复回环
|
||||
SAA-->>AIGC: 回调 callback:packageUrl + manifest
|
||||
AIGC->>PRJ: 写入草稿版本
|
||||
AIGC-->>S: 任务完成通知
|
||||
```
|
||||
|
||||
生成任务本身是一个有明确状态机的对象,它给"超时/失败/重试/取消"都定义了清楚的边——这是任何涉及外部模型调用的链路都必须考虑的可靠性设计:
|
||||
|
||||
```mermaid
|
||||
stateDiagram-v2
|
||||
[*] --> QUEUED: 创建任务
|
||||
QUEUED --> RUNNING: 消费/派发
|
||||
RUNNING --> SUCCEEDED: 生成完成 + 质量通过
|
||||
RUNNING --> FAILED: 生成失败 / 质量不达标
|
||||
RUNNING --> TIMED_OUT: 超时(120s)
|
||||
FAILED --> QUEUED: 重试(≤2 次)
|
||||
TIMED_OUT --> QUEUED: 重试(≤1 次)
|
||||
SUCCEEDED --> [*]
|
||||
FAILED --> [*]: 超过重试次数
|
||||
QUEUED --> CANCELED: 用户取消
|
||||
RUNNING --> CANCELED: 用户取消
|
||||
```
|
||||
|
||||
生成性能与质量的硬指标:生成 **P50 < 60s、P95 < 180s**,**生成成功率 ≥ 80%**(MVP 验收线;远期蓝图目标 ≥85%),队列最大积压 500 任务、超过返回 429。
|
||||
|
||||
---
|
||||
|
||||
## 6. 游戏怎么安全地跑在玩家面前
|
||||
|
||||
生成出来的游戏运行在 **iframe 沙箱**(浏览器内嵌的隔离框架)里,与平台彻底隔离。平台能力靠 **WanxiangGameSDK** 注入进去——这是平台向游戏注入能力的**唯一通道**,没有它平台就只是个静态文件托管。SDK 分两层:Core 层(生命周期/事件总线/遥测/错误捕获)内联进游戏入口、压缩后 **< 8KB**;Plugin 层(广告/支付/社交/云存档/调试)按需懒加载,首屏不付出代价。
|
||||
|
||||
加载用**三容器策略**(参考抖音短视频预加载):当前播放的容器之外,前一个在销毁、后一个已预加载完成,上滑切换时无缝衔接。
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant APP as game-studio
|
||||
participant CDN as CDN/OSS
|
||||
participant SANDBOX as iframe sandbox
|
||||
APP->>CDN: 请求 manifest.json(hash 缓存)
|
||||
CDN-->>APP: {entry, assets[], checksum}
|
||||
APP->>APP: 校验 manifest 完整性
|
||||
APP->>CDN: 并行请求 entry.js + 关键 assets
|
||||
APP->>SANDBOX: 创建 iframe(CSP + sandbox)
|
||||
APP->>SANDBOX: 注入 GameConfig + Game SDK bridge
|
||||
SANDBOX->>SANDBOX: 执行 entry.js → 初始化游戏
|
||||
SANDBOX->>APP: postMessage({type:'game_loaded'})
|
||||
Note over APP,SANDBOX: 游玩中:SDK bridge 上报生命周期事件
|
||||
SANDBOX->>APP: postMessage({type:'game_complete', score})
|
||||
```
|
||||
|
||||
安全边界是几条不能破的红线:
|
||||
|
||||
- **iframe 沙箱**用 `sandbox="allow-scripts allow-same-origin"`,但有一条 2026-06-10 审计补的红线:`allow-same-origin` **仅当游戏包部署在独立源**(usercontent 子域,与宿主不同源)时才可用——同源下 iframe 能触宿主 DOM/存储、甚至自己移除 sandbox,沙箱铁律就失效了。独立源就绪前,**禁用 `allow-same-origin`**。
|
||||
- **内容安全策略 CSP**:`script-src 'self'; connect-src 'none'`,即游戏内**无任何网络请求**;CSP 必须经游戏包托管侧的 HTTP 响应头下发,不能只靠 meta 标签。
|
||||
- **LLM 产物消毒**:GameConfig 的文案字段(title/label/theme 等)入库前做白名单字符集 + 长度校验,渲染侧一律转义后绘制,禁止 innerHTML/eval。
|
||||
- 资源总大小 ≤ 10MB、首屏 ≤ 2MB;postMessage 必须校验来源 + schema。
|
||||
|
||||
**底线原则**:创作者通过配置(不是写代码)驱动游戏行为,平台对运行时代码拥有完全控制权——这是安全与合规可控的根。
|
||||
|
||||
> 运行时打包、沙箱、SDK 分层、多渠道导出的完整手册见[运维域](../运维/)与 `.agents/skills/runtime-and-multichannel.md`;广告/支付/社交各 Plugin 的降级铁律("失败即跳过、绝不阻断游戏")见技术决策版 §3.4。
|
||||
|
||||
---
|
||||
|
||||
## 7. 推荐引擎:让好游戏被刷到
|
||||
|
||||
MVP 阶段的推荐是**规则 + 信号**,不上机器学习(那是增长期的事)。每款游戏的曝光排序由一个打分公式决定,正向信号(质量分/新鲜度/互动率)加分,负向信号(跳过率/错误率/举报率)减分,再叠加新创作者保底与运营精选加分:
|
||||
|
||||
```
|
||||
Score = w1·quality_score + w2·freshness + w3·interaction_rate
|
||||
− w4·skip_rate − w5·error_rate − w6·report_rate
|
||||
+ bonus_new_creator + bonus_featured
|
||||
```
|
||||
|
||||
其中 error_rate(加载失败/试玩次数)是硬降权——技术上跑不动的游戏直接沉底;report_rate 超阈值会触发人工审核。候选集存在 Redis Sorted Set,TTL 60s,用 cursor 分页(MVP 也可直查 MySQL,Redis 缓存是增长期形态)。这条设计让"质量好 + 玩家爱玩"的游戏自然浮上来,数据回流又持续校准推荐——这正是平台"越用越聪明"的来源。
|
||||
|
||||
---
|
||||
|
||||
## 8. 数据怎么存、怎么流
|
||||
|
||||
核心实体围绕"用户拥有项目、项目有版本、版本编译成包、版本由生成任务产出"这条主线展开:
|
||||
|
||||
```mermaid
|
||||
erDiagram
|
||||
USER ||--o{ GAME_PROJECT : owns
|
||||
GAME_PROJECT ||--o{ GAME_VERSION : has
|
||||
GAME_VERSION ||--|| GAME_PACKAGE : builds_to
|
||||
GAME_VERSION ||--o{ GENERATION_TASK : generated_by
|
||||
GAME_VERSION ||--o{ REVIEW_RECORD : reviewed_in
|
||||
USER ||--o{ INTERACTION : performs
|
||||
INTERACTION }o--|| GAME_PROJECT : targets
|
||||
USER ||--o{ WALLET : has
|
||||
WALLET ||--o{ TRANSACTION : records
|
||||
GAME_PROJECT ||--o{ AD_SLOT : contains
|
||||
AD_SLOT ||--o{ AD_IMPRESSION : tracks
|
||||
```
|
||||
|
||||
存储选型遵循"先简后扩":业务实体进 MySQL(事务一致性),游戏包/素材/封面进 MinIO/OSS(对象存储 + CDN),推荐热数据进 Redis;事件流 MVP 用 MySQL 分区表、增长期迁 ClickHouse,搜索 MVP 用 MySQL FULLTEXT、增长期迁 Elasticsearch。数据流向上,埋点事件经 telemetry 入库、定时聚合成 `GameDailyStats`、写入 Redis 候选集供 feed 读取;生成产物落 OSS 经 CDN 分发给前端。
|
||||
|
||||
---
|
||||
|
||||
## 9. 工程治理:把可靠性和质量钉死
|
||||
|
||||
这套系统涉及外部模型、异步任务、支付,所以可靠性不是事后补的,而是设计进去的。下面是几条贯穿的硬约束。
|
||||
|
||||
**幂等性**:用户重复点"生成"用 idempotency_key(Redis 5 分钟去重);支付回调重复靠订单状态机 + 乐观锁;发布重复提交靠 project_version 唯一约束。**分布式一致性**:原则是尽量避免分布式事务,改用最终一致性 + 补偿,并用定时任务扫描"中间态超 5 分钟"的记录来兜底。
|
||||
|
||||
**SLO(服务等级目标)与可用性**:
|
||||
|
||||
| 服务 | SLO | Error Budget(月) |
|
||||
|---|---|---|
|
||||
| 游戏流 API | 99.5% 可用 | 3.6 小时 |
|
||||
| AI 生成 | 99%(允许更高失败率) | 7.2 小时 |
|
||||
| 支付 | 99.9% | 43 分钟 |
|
||||
|
||||
整体可用性目标 **≥99.5%(MVP)/ ≥99.9%(正式)**。**性能目标**:游戏流首屏 P75<3s(正式 P75<1.5s)、API P95<500ms、并发从 1,000 DAU 起步水平扩到 100,000 DAU。**可观测性**:业务服务把 Metrics/Traces/Logs/Errors 分别送 Prometheus/Jaeger/Loki/Sentry,汇到 Grafana 看板与告警;日志 JSON 结构化、Token/手机号脱敏、trace_id 从 Gateway 入口全链路透传(含 SAA 编排节点 observation + new-api 调用)。
|
||||
|
||||
**关键技术风险**里有几条已经真实发生或影响重大,值得单列:
|
||||
|
||||
- **LLM 网关单点通道批量失效**(2026-06 已真实发生:二厂系全废)——应对是多通道健康巡检 + key 激活状态监控 + 同模型降级抽检 + 批跑冻结阀。
|
||||
- **LLM 成本失控**(免费生成被滥刷 / 推理输出吃光配额)——应对是生成限频限额 + 日预算熔断告警 + 显式 max_tokens,超额则暂停生成入口、仅确定性模板兜底。
|
||||
- **监管与上游平台风险**(无版号 UGC 定性 / AIGC 标识义务 / 微信抖音下场)——这是非纯技术风险,由合规专项与对外材料承载,登记在此防失踪。
|
||||
|
||||
> 完整的幂等矩阵、分布式事务补偿表、安全分层、CI/CD、测试金字塔、灰度与 Feature Flag、扩展性 SPI 接口见技术决策版 §7;安全与可靠性的硬约束基线见 `.agents/rules/security-and-reliability.md`。
|
||||
|
||||
---
|
||||
|
||||
## 10. 子文档导航
|
||||
|
||||
本页是架构域的入口与骨架。更深的子系统现行架构在下列子文档,需要细节时再深入:
|
||||
|
||||
| 文档 | 回答什么 |
|
||||
|---|---|
|
||||
| [生成引擎](生成引擎/) | 生成主线的深层架构:SAA 16/11 节点拓扑、加节点方法、new-api 接入、checkpoint 框架坑、dispatcher 契约、九门 harness |
|
||||
| [前端域](../前端/) | game-studio / game-admin 的前端设计体系、组件分层、SDK 源码组织、运行时容器 |
|
||||
| [后端域](../后端/) | 后端模块的实现级设计、API 路径规范、错误码分配、Flyway 迁移、契约对齐 |
|
||||
| [运维域](../运维/) | 环境/部署/staging 运维、构建门、smoke 门、运行时打包与多渠道导出手册 |
|
||||
|
||||
> **纪律**:架构域只讲"系统怎么搭、关键决策为什么";产品对用户提供什么在[产品域](../产品/README.md),哪条产品需求由哪些技术模块实现在[需求模块映射](../产品/需求模块映射.md)。各域各司其职、互不重复。
|
||||
>
|
||||
> **源头长文档**(需要逐条考据时回溯):`系统概要设计-技术决策版.md`(HJ-ARCH-001,架构全貌与决策记录)、`技术架构与模块.md`(HJ-TECH-002,13 模块 T-id 注册表)、`系统概要设计-开发团队版.md`(HJ-ARCH-003,日常参考手册,注意其环境/命令段为 target-state、与现实有出入,以 `.agents/rules` + `docs/mvp/MVP进度总账.md` 为准)。现行口径的日常入口 = `.agents/knowledge/tech-decisions.md` + `.agents/skills/saa-graph-orchestration.md`。
|
||||
409
docs/architecture/架构/生成引擎/OpenGame对照.md
Normal file
409
docs/architecture/架构/生成引擎/OpenGame对照.md
Normal file
@ -0,0 +1,409 @@
|
||||
# OpenGame 对照 · 外部标杆深读
|
||||
|
||||
> **这是什么**:绘境AI 生成引擎对外部最强开源标杆 **OpenGame** 的源码级深读,以及由此照出的复刻缺口。它回答两个问题——"**OpenGame 到底是怎么把一句话变成游戏的**",以及"**它身上哪三层东西值钱、我们还没有**"。
|
||||
> **给谁看**:负责生成主线的工程师、做技术选型与对标的架构评审、想看清护城河关键路径上"我们与最强开源系统差在哪"的人。
|
||||
> **怎么读**:先读 §1 的定位与一张对照全景图;§2 是主体——逐层拆 OpenGame 的真实机理(主循环 / 韧性 / 子 agent / 系统提示 / 六阶段生成 / 三大支柱);§3 落到我们的现状,把缺口分四类诚实交代。想要"往哪走、先做什么"的统一行动项,去[生成主线架构演进路线](../../../agent-specs/生成主线架构演进路线.md)拿——本档只负责把 OpenGame 的内部机理和逐项缺口的来龙去脉讲透。
|
||||
|
||||
---
|
||||
|
||||
## 0. 先讲清楚:这份文档在体系里的位置
|
||||
|
||||
生成引擎子树的主文档([生成引擎 README](README.md))已经在它的 §5 用一页篇幅,概括了"对标 OpenGame 我们缺哪三层"。那是**结论**。本文档是那一页结论背后的**源**——把 OpenGame 的源码真正读穿,逐层讲清它每个机制为什么这么设计、对用便宜模型的人意味着什么,再把每一处缺口的取舍依据摆出来。
|
||||
|
||||
> **OpenGame 是什么**:一个把"一句话需求"变成"可玩游戏"的开源生成系统(源码在 `github.com/leigest519/OpenGame`,作者是香港中文大学 MMLab,对应论文 arXiv 2604.18394)。它是当前这一赛道最完整、最值得深读的开源对照物。本档的所有结论都锚在 clone 下来的**真源码**上,不取信于论文 README 的宣称——这个区分在 §2.7 会变得至关重要。
|
||||
|
||||
一句话定位:**README §5 给结论,本档给证据与机理。** 两者职责不同,不互相重复;当你只想知道"我们要做什么"时读 README,当你想知道"OpenGame 凭什么、以及我们为什么这么取舍"时读这里。
|
||||
|
||||
---
|
||||
|
||||
## 1. 一句话与一张图:两条路,同一个终点
|
||||
|
||||
在拆 OpenGame 之前,得先把我们自己和它摆在同一张图上,否则后面所有的"抄"与"不抄"都没有坐标系。
|
||||
|
||||
绘境AI 的生成主线,本质是一条**用便宜的通用模型驱动、用确定性的图编排约束控制流、用一套自动验收门兜底**的流水线。它的骨架是 **SAA 裸 StateGraph**(SAA = Spring AI Alibaba,阿里在 Spring 生态里的 AI 编排框架;"裸 StateGraph"指直接用它的有向状态图原语手写编排,而不是再套一层 agent 框架),一共 16 个节点,每个节点是一段实打实的 Java 代码,节点间的流转**由图预先固定**,而非由模型在运行时临场决定下一步。
|
||||
|
||||
OpenGame 走的是另一条路:它是一个**命令式自主循环**——在一个 `while(true)` 死循环里,模型**自由决定**下一步调用哪个工具,控制流**由模型驱动**。这是和我们正交的另一种范式。
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
subgraph 我方["绘境AI 生成引擎 — 声明式有向图"]
|
||||
direction LR
|
||||
HG["SAA 裸 StateGraph<br/>16 节点,控制流由图固定"]
|
||||
HN["九门 harness<br/>确定性机制验收(领先)"]
|
||||
HM["便宜通用模型 + 门兜底<br/>不训专用大模型"]
|
||||
end
|
||||
subgraph 对方["OpenGame — 命令式自主循环"]
|
||||
direction LR
|
||||
OL["while(true) ReAct 循环<br/>控制流由模型驱动"]
|
||||
OT["4 个原子游戏工具<br/>+ 六阶段 SOP 接力"]
|
||||
OS["经验自进化(护城河)<br/>Template / Debug Skill"]
|
||||
end
|
||||
对方 -. "抄它的工具层 / 韧性兜底 / 经验复利,当零件库落进 SAA 节点内部" .-> 我方
|
||||
对方 -. "不抄它的『模型自由驱动主循环』范式" .-> X["缝合设计红线"]
|
||||
style X fill:#f5b7b1
|
||||
style HN fill:#a9dfbf
|
||||
style OS fill:#f9e79f
|
||||
```
|
||||
|
||||
这张图先把全篇最重要的取舍钉死:**OpenGame 的控制流我们用 SAA 图来表达**(我们本来就这么做,而且对卡 80% 成功率门而言,图的确定性比口头督促更稳,这是升级不是抄);**OpenGame 的工具层、韧性兜底、防脏脚手架与经验复利机制,我们当成零件库,原样落进 SAA 节点内部**。一句话——**抄它的工具与兜底,不抄它的主循环范式**。把主循环硬塞进来,就成了项目明令反对的"缝合设计"(指把一份为 A 范式写的代码硬粘到 B 范式上当接口,而不从整体架构出发)。
|
||||
|
||||
带着这个坐标系,下面进入主体:OpenGame 到底是怎么运转的。
|
||||
|
||||
---
|
||||
|
||||
## 2. OpenGame 是怎么生成游戏的
|
||||
|
||||
### 2.1 它的出身:不是新框架,是 Qwen-Code 的二次 fork
|
||||
|
||||
读 OpenGame 源码,第一个、也是最重要的认知矫正是:**它不是从零写的 agent 框架,而是 `Gemini-CLI → Qwen-Code → OpenGame` 这条二次 fork 的产物。**(fork = 把别人的开源项目拷一份在其上改;"二次 fork"指它本身又是 fork 之上的 fork。)
|
||||
|
||||
这个判断有硬证据,不是猜的:
|
||||
|
||||
- `geminiChat.ts` 文件顶部注释自承是 js-genai 的 `chats.ts` 逐行拷贝改版;
|
||||
- `subagent.ts` 的版权头写着 Qwen;
|
||||
- 系统提示里自述"built on top of the Qwen Code agent framework by Alibaba Group";
|
||||
- shell 执行时注入 `QWEN_CODE=1` 环境变量;
|
||||
- MCP 客户端名还残留着 `qwen-cli-mcp-client`、OAuth 客户端名是"Gemini CLI MCP Client"。
|
||||
|
||||
这个出身决定了 OpenGame 的能力分布。**MCP 接入、shell 沙箱、会话持久化、配置面、OAuth——这一整套执行基座,是从上游成熟代码完整继承的。**(MCP = Model Context Protocol,模型上下文协议,一种让 agent 接外部工具/数据源的标准总线;OAuth = 第三方授权登录协议。)OpenGame 自己真正写的,只有三样:tools 层的四个游戏专用工具、一个音乐合成服务,以及一套 Phaser + Vite 工程模板(Phaser = 一个成熟的 2D HTML5 游戏引擎;Vite = 前端构建工具)。
|
||||
|
||||
换句话说:**"复刻 OpenGame 的引擎",本质是复刻 Qwen-Code,而不是发明什么新架构**——而我们已经选了 SAA 图这条不同的路。这一点先记下,它会贯穿整篇对照:真正属于"游戏生成"的创新,集中在那四个工具和那套模板里,其余都是继承的通用 agent 基建。
|
||||
|
||||
### 2.2 它的主循环:三层权力分配的 ReAct
|
||||
|
||||
OpenGame 的 agent 主循环,核心不是一段编排脚本,而是一个经典的 **ReAct/工具循环**(ReAct = Reason+Act,指"模型先推理、再调工具、看结果、再推理"的循环范式),但被干净地拆成三层。值得细看它的"权力是怎么分配的",因为这正是它可移植性好的原因。
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
autonumber
|
||||
participant Loop as 外层 while(true)<br/>(nonInteractiveCli)
|
||||
participant Client as 中层 sendMessageStream<br/>(client.ts)
|
||||
participant Turn as 内层 Turn.run<br/>(turn.ts)
|
||||
participant Model as 大模型
|
||||
participant Tool as 工具执行
|
||||
|
||||
Loop->>Client: 用当前消息开一个回合
|
||||
Client->>Client: 压缩历史 / 检查 token 上限<br/>注入 IDE 上下文 + system-reminder<br/>跑循环检测
|
||||
Client->>Turn: 进入单回合
|
||||
Turn->>Model: chat.sendMessageStream(一次)
|
||||
Model-->>Turn: 流式 chunk(内容/思考/工具请求)
|
||||
Note over Turn: Turn 只『发出工具请求事件』<br/>自己绝不执行任何工具
|
||||
Turn-->>Loop: 事件流(含工具调用请求)
|
||||
alt 这一圈有工具请求
|
||||
Loop->>Tool: 逐个真正执行
|
||||
Tool-->>Loop: 结果打包成 role=user 的 functionResponse
|
||||
Loop->>Loop: 回灌到循环顶部,再来一圈
|
||||
else 这一圈只说话、没发起工具
|
||||
Loop->>Loop: 终止(收敛)
|
||||
end
|
||||
```
|
||||
|
||||
读这张时序图,抓住三层的职责切分:
|
||||
|
||||
- **最外层**是非交互 CLI 里的 `while(true)`(在 `nonInteractiveCli.ts`)。每一圈做四件事:用当前消息调一次流式模型;消费事件流,把模型发起的工具调用请求累积起来,把文本和思考直接打到标准输出;流结束后,如果有工具请求就逐个真正执行,把每个工具的返回打包成**一条 role 为 user 的 functionResponse 消息**回灌到循环顶部——这就是"喂回结果再循环"的本质。(functionResponse = 工具调用的结构化返回,以 user 角色塞回对话,让模型读到上一步工具产出了什么。)
|
||||
- **中间一层**是 `client.ts` 的 `sendMessageStream`,负责把"一个回合"武装起来:压缩历史、检查会话 token 上限、注入 IDE 上下文增量、注入 system-reminder、跑循环检测,然后才进入真正的单回合。(system-reminder = 每轮临时注入的一段提醒文本,用于喂动态信息,不进固定的系统提示。)
|
||||
- **最内层**是 `turn.ts` 的 `Turn.run`,它只负责调一次 `chat.sendMessageStream`,把模型吐出的 chunk 翻译成事件(内容/思考/工具请求/完成/错误)。**关键设计:Turn 自己绝不执行任何工具,它只发出"我要调这个工具"的请求事件。**
|
||||
|
||||
这个"权力分配"是整个设计的精髓:**模型回合只有发起权,执行权牢牢握在外层循环手里;工具结果统一以 user-role functionResponse 回灌。** 三层职责清晰,可移植性很好。它的**收敛条件极其朴素:模型这一回合只说话、不调工具,就算完成了。**
|
||||
|
||||
围绕这条主循环,它还打了好几个护栏:`MAX_TURNS=100` 硬顶、会话级回合上限、token 上限、`LoopDetectionService` 检出复读直接退出。最有意思的一个补丁叫 **`checkNextSpeaker`**——当模型说了"接下来我要做某某"却没真去发工具调用时,它会用一次额外的 LLM 判定 next_speaker(下一个该谁说话),如果判定该轮到模型,就自动注入一句"Please continue."把它顶回正轨。**这是专门用来对付"模型话痨但不动手"的早停问题的,而便宜模型恰恰最容易犯这个毛病。**
|
||||
|
||||
### 2.3 它的韧性:把一切错误降级成喂回模型的 functionResponse error
|
||||
|
||||
如果说主循环是骨架,那 **`CoreToolScheduler`**(核心工具调度器)就是让这副骨架在脏数据下不散架的关节。它把每个工具调用建模成一个状态机。
|
||||
|
||||
```mermaid
|
||||
stateDiagram-v2
|
||||
[*] --> validating: 收到工具调用请求
|
||||
validating --> scheduled: 参数校验通过
|
||||
validating --> error: 参数非法 / 幻觉工具名
|
||||
scheduled --> awaiting_approval: 需 HITL 确认
|
||||
scheduled --> executing: 确认门齐开
|
||||
awaiting_approval --> executing: 用户批准
|
||||
awaiting_approval --> cancelled: 用户拒绝
|
||||
executing --> success: 执行成功
|
||||
executing --> error: 执行失败
|
||||
error --> [*]: 降级成 functionResponse error<br/>喂回模型,让它自己纠正
|
||||
cancelled --> [*]: 同样喂回模型
|
||||
success --> [*]
|
||||
```
|
||||
|
||||
调度语义可以一句话概括:**批内并行、批间串行、确认门齐开才执行**——同一批工具调用先全部发起再一起 await(并发),但不同批次之间严格串行排队;而且要等这一批所有调用都到了 scheduled 或终态,才统一开始执行。(HITL = Human-In-The-Loop,人在环路里,指需要人工点一下"批准"的确认门。)
|
||||
|
||||
但对我们最有借鉴价值的,不是它的并发语义,而是它的**容错哲学**:幻觉工具名(模型编了个不存在的工具)、参数非法、被用户拒绝、工具执行失败——这些统统不让它崩溃,而是**降级成一条 functionResponse error 喂回给模型,让模型自己读着错误去纠正**。尤其当模型造了个不存在的工具名时,它会用 **Levenshtein 编辑距离**(衡量两个字符串差几个字符的算法)算出"你是不是想用 X"的建议,一并塞回去。
|
||||
|
||||
**便宜模型最大的问题就是输出脏——乱造工具名、参数对不上——而这一层"错误即上下文"的兜底,比模型本身更决定成败。这是 OpenGame 给所有想用便宜模型的人上的第一课。**
|
||||
|
||||
### 2.4 它的子 agent:一层深的"上下文隔离 + 只回吐结论"
|
||||
|
||||
OpenGame 的 subagent(子 agent,指主 agent 派生出去干一件子活的独立 agent)是一等公民,而且设计得很克制。主 agent 把 `task` 当成一个普通工具来调;系统提示里明确教模型"做文件搜索这类活优先用 task 工具以减少上下文占用",每轮请求前还会动态注入一段 system-reminder 把可用的子 agent 名字塞进去、怂恿模型去委派。
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
Main["主 agent<br/>(完整工具集,含 task)"] -->|task_prompt 注入任务| Sub["子 agent<br/>全新 GeminiChat"]
|
||||
Sub --> SubLoop["独立 while(true) mini-loop<br/>探索几十轮"]
|
||||
SubLoop --> SubTools["子 agent 工具集<br/>(被裁剪,强制剔除 task 本身)"]
|
||||
SubTools -. "不能再派生子 agent<br/>隔离深度只有一层" .-> NoDeep["✗ 不是任意深度 agent 树"]
|
||||
SubLoop -->|"只回吐最后一轮纯文本结论"| Main
|
||||
SubLoop -. "中间几十轮探索噪音<br/>全吞在自己肚子里" .-> Swallow["主 agent 上下文不被污染"]
|
||||
style NoDeep fill:#f5b7b1
|
||||
style Swallow fill:#a9dfbf
|
||||
```
|
||||
|
||||
机制上,子 agent 会开一个**全新的 GeminiChat**,只继承环境历史,系统提示由模板占位符渲染(主 agent 只通过 `task_prompt` 注入任务描述),并且**自带一个独立的 `while(true)` mini-loop**。最关键的两个设计:
|
||||
|
||||
1. 子 agent 的工具集被裁剪,而且**强制剔除 task 工具本身**——所以子 agent 不能再派生子 agent,**隔离深度只有一层**,这不是任意深度的 agent 树。
|
||||
2. 子 agent 跑完几十轮探索后,**只把最后一轮的纯文本结论回吐给主 agent**,中间过程全吞在自己肚子里。
|
||||
|
||||
这正好兑现了系统提示里说的"减少上下文占用":主 agent 不会被子任务的探索噪音污染。**代价是委派靠 prompt 怂恿而非硬路由,委派质量死死绑在主模型的判断力上**——便宜模型在这里大概率不会主动、聪明地委派。这是一个对我们有直接启示的取舍:我们用 SAA 图把"什么时候该分子任务"固定成边,正好绕开了"便宜模型不会自己委派"这个坑。
|
||||
|
||||
### 2.5 它的系统提示:静态人格 + 环境分支 + 模型方言,动态信息走 system-reminder
|
||||
|
||||
OpenGame 的系统提示拼装很能体现工程成熟度。一个 `getCoreSystemPrompt(userMemory, model)` 函数顺序拼接四段:
|
||||
|
||||
1. **可被外部 `.md` 文件整体覆盖的基座大模板**——身份、Core Mandates(核心行为准则)、强制频繁用 todo 的任务管理、软件工程的 Plan/Implement/Verify(计划/实现/验证)工作流,甚至写死了"2D 游戏用 HTML/CSS/JS、3D 用 Three.js"的技术默认。
|
||||
2. **按 SANDBOX 环境变量三选一注入的沙箱段落**。
|
||||
3. **按是否 git 仓库追加的 Git 守则**。
|
||||
4. 然后是一个很妙的细节——**按模型名切换三套 few-shot 工具调用示例方言**(few-shot = 在提示里给几个示范例子):通用自然语言、qwen-coder 的 XML 风格、qwen-vl 的 JSON 风格,去适配不同模型对工具调用语法的偏好。
|
||||
|
||||
这里藏着两个对我们有用的工程判断:
|
||||
|
||||
- **真正每轮变化的动态信息不进系统提示**,而是作为 user 消息或 system-reminder 在每轮注入(IDE 上下文、subagent 提醒、plan-mode 提醒)——这样系统提示可以被缓存,省钱。
|
||||
- **"按模型切 toolcall 方言"是支撑多便宜模型的关键工程点**。换一个便宜的 Qwen 系或别的模型,主要改的就是这套方言示例和底层的 ContentGenerator——它支持 OpenAI / Anthropic / Gemini / Vertex / Qwen 五种认证,`baseUrl` / `model` / `apiKey` 全是 env + config 双层可配,还带 `retryWithBackoff`(指数退避重试)和 429 限流回退到 flash 模型,而循环骨架完全不动。
|
||||
|
||||
**一句话:"用便宜通用模型"在 OpenGame 里是一等公民,开关早就铺好了。** 这和我们"便宜模型 + 门兜底"的路线判断是一致的——区别在于它用提示方言切模型,我们应当把这层做成外置的 `models.yaml` 数据契约(见 §3 与演进路线)。
|
||||
|
||||
### 2.6 它真正怎么把一句话变成游戏:四个原子工具 + 六阶段 SOP 接力
|
||||
|
||||
这是整篇分析最该被吸收的部分。OpenGame 的工具集**刻意地小,只有四个游戏专用工具,没有一个"端到端生成游戏"的大黑盒**。生成逻辑全靠 agent 用这堆原子工具,按相位一步步串起来。
|
||||
|
||||
最反直觉的发现是:**这条端到端流水线没有任何中央编排文件**(全仓 grep 不到驱动相位的 workflow 代码)。它是靠两样东西驱动的——一份叫 `custom.md` 的确定性 SOP 系统提示(SOP = Standard Operating Procedure,标准作业流程),以及**每个工具返回内容末尾注入的 `<system-reminder>` 接力指令**。
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
Brief["一句话需求"] --> P1
|
||||
subgraph P1["Phase 1 · 物理优先分类 + 脚手架"]
|
||||
C["classify_game_type<br/>归入 5 archetype 之一"]
|
||||
C -->|"返回里塞 system-reminder:<br/>给出 cp 命令拷模板"| ROUTE["路由器:archetype 一旦定<br/>后面规则/资产/模板全被决定"]
|
||||
end
|
||||
P1 -->|"接力指令:下一步调 generate-gdd"| P2
|
||||
subgraph P2["Phase 2 · 把一句话编成六节技术规格书"]
|
||||
G["generate_gdd<br/>动态拼三层规则 → GAME_DESIGN.md"]
|
||||
G --> RULE["三铁律:Config-First /<br/>Zero Custom Code / Hook Integrity"]
|
||||
end
|
||||
P2 -->|"DO NOT STOP. CONTINUE TO PHASE 3 NOW"| P3
|
||||
subgraph P3["Phase 3 · 多模态资产 + 确定性贴图(最重)"]
|
||||
A["generate_game_assets<br/>生成→抠图→I2V抽帧→多级回退→装配"]
|
||||
T["generate_tilemap<br/>AI画3×3 + AI写ASCII<br/>+ 确定性 bitmask 自动贴图"]
|
||||
end
|
||||
P3 -->|接力| P5
|
||||
subgraph P5["Phase 5 · 落地代码"]
|
||||
E["smart_edit(注册名 edit)<br/>三级匹配 + LLM 自纠错<br/>把规格写进模板文件"]
|
||||
end
|
||||
P5 --> OUT["可玩游戏"]
|
||||
style ROUTE fill:#f9e79f
|
||||
style RULE fill:#a9dfbf
|
||||
```
|
||||
|
||||
让我顺着这几个相位讲一遍——这是整篇最该被吸收的部分。
|
||||
|
||||
**Phase 1,物理优先分类 + 脚手架。** agent 调 `classify_game_type`,把用户那句话归入五个 archetype 之一:platformer(平台跳跃)、top_down(俯视)、grid_logic(网格逻辑)、tower_defense(塔防)、ui_heavy(以 UI 为主)。(archetype = 游戏原型/品类,这里指按"物理形态"划分的玩法大类。)它的分类规则刻意"不看品类名,只看物理"——重力方向、视角、移动方式,系统提示里还专门列了易错例(Terraria 是 platformer 不是 top_down)。这个工具的容错解析很务实:先剥 markdown 代码围栏,JSON 解析失败就退化成在字符串里找关键词,再不行默认 platformer。但它真正的产出不在分类结果本身,而在它返回内容里塞的一段 system-reminder——**直接给 agent 下一步要跑的 `cp` 命令**(把 `templates/core` 和 `templates/modules/{archetype}` 拷进工作区,把对应文档拷进 docs),并指示"下一步调 generate-gdd"。**这是整条链的路由器:archetype 一旦定下,后面用哪套 GDD 规则、什么视角的资产、COPY 哪套模板,全被决定了。分类错,后面全错。**
|
||||
|
||||
**Phase 2,把一句话编成六节技术规格书。** agent 调 `generate_gdd`(GDD = Game Design Document,游戏设计文档),它会向上递归找 docs 目录,动态拼三层规则——通用 GDD 格式、该 archetype 的设计视角规则、该 archetype 的 `template_api.md`(可用的代码能力 / hook 清单;hook = 模板预留给生成代码挂接的接口点)——找不到文件就回退到内置规则(内置规则本身极其详尽,光 grid_logic 那段就有上百行讲三相回合管线、cell 类型、undo、AI)。它真发一次 LLM 请求,产出一份六节的 `GAME_DESIGN.md`。
|
||||
|
||||
**这份 GDD 的精髓在于:它不是给人看的文案,而是一份"每一节都硬绑一个下游工具入参或代码文件"的契约**——Section 1 的资产表喂给 `generate_game_assets`,Section 4 的 ASCII 布局喂给 `generate_tilemap`,Section 2 的数值合并进 `gameConfig.json`,Section 3/5 去改 LevelManager 和 main.ts。它把开放式的"做个游戏"收敛成了一张结构化、可被便宜模型逐项落地的待办清单。约束便宜模型不跑飞的,是它的**三条铁律**:
|
||||
|
||||
- **Config-First**:数值只能进 `gameConfig`;
|
||||
- **Zero Custom Code**:只用模板已有行为,不许自己写新逻辑;
|
||||
- **Hook Integrity**:绝不许编造 `template_api.md` 里没有的 hook。
|
||||
|
||||
返回内容末尾又是一大段 system-reminder,命令 agent 存盘后逐 Phase 往下走,还专门写了"DO NOT STOP. CONTINUE TO PHASE 3 NOW"。
|
||||
|
||||
**Phase 3,多模态资产流水线——这是最重、最难抄的一块。** `generate_game_assets` 不是简单调个文生图,而是一整套"生成 → 抠图 → 视频抽帧 → 多级回退 → Phaser 清单装配"的流水线:
|
||||
|
||||
- **背景图**强制"纯场景无人物无文字";
|
||||
- **角色图**文生图后过抠图服务去白底;
|
||||
- **动画**是亮点——先生成 idle 基帧,然后**优先走图生视频(I2V,Image-to-Video)再用 ffmpeg 本地抽帧逐帧抠图**;如果没有 ffmpeg 或失败,**回退到图生图(I2I,Image-to-Image)逐帧编辑**;
|
||||
- **音频**则是三级回退——文生视频抽音轨,失败就让模型产 ABC 记谱法再用 Python 的 symusic 库离线合成 WAV,再失败就纯程序生成占位音;
|
||||
- **瓦片集**强制"先画 3×3 九宫格再扩成 7×7 blob"。
|
||||
|
||||
所有产物登记进一份 Phaser 能直接 `load.pack` 的 `asset-pack.json`。**这块每个模态都有降级路径保证不空手而归,但它依赖视频模型 + ffmpeg + 抠图服务——便宜的文生图模型替不了动画质量,这是真壁垒。**
|
||||
|
||||
**Phase 3 配套,确定性贴图——OpenGame 最聪明的工程判断之一。** `generate_tilemap` 把"AI 画不好整张地图"这个老问题,拆成了"AI 只画 3×3 材质 + AI 只写 ASCII 布局",中间用一个**确定性的 8 邻域 bitmask 自动贴图算法**(bitmask = 位掩码;根据上下左右及对角是否实心,算出 47-tile blob 里的精确瓦片 id)来补全墙体接缝和拐角。**这等于用确定性算法绕开了模型的空间推理能力**,极大降低了对模型的要求。而且它有个关键的架构判断:**只对 platformer/top_down 用瓦片地图;grid_logic/tower_defense/ui_heavy 故意走代码定义网格**,理由是这类逻辑游戏运行时 cell 会变(门会开、洞会填),用瓦片地图会造成双数据源不同步。
|
||||
|
||||
**Phase 5,落地代码——靠 smart_edit 把规格变成模板里的真实代码。** 前面 GDD、资产、tilemap 产的都是"规格和素材",真正把游戏代码写进模板文件,靠的是 agent 反复调 `smart_edit`(注册名就叫 edit)。它的**三级匹配**很能说明问题:精确字面替换 → 柔性匹配(逐行 trim 后比对、自动套用目标缩进)→ 正则匹配(把 token 用 `\s*` 串起来容忍空白差异);三级都不命中时,还会拿 instruction 和原始错误**喂给 LLM 重算一遍 search/replace 再试**。**这套三级匹配加 LLM 自纠错,完全是为了对抗便宜模型给出的 `old_string` 经常对不上(空白、缩进、转义不一致)**——这是"改源不改包、按 GDD 实现每个文件"这套范式能跑通的工程保障。
|
||||
|
||||
**把这六个相位串起来看,OpenGame 的核心洞察就清楚了:它不靠图编排,靠的是"预建模板 + archetype 路由 + GDD 把任务结构化成填空题 + system-reminder 单线接力 + 工具内置降级与自纠错"。生成质量不是来自强模型自由发挥,而是来自把开放问题收敛成填空题。** 不过这条 system-reminder 单线接力是它的软肋:它靠"DO NOT STOP"这种口头督促来推进相位,**便宜模型很容易在中途断流**。这恰恰反衬出我们用 SAA 图固定相位的价值——图的边不会"忘了继续"。
|
||||
|
||||
### 2.7 它的三大支柱:经验进化、专训模型、VLM 验收——以及论文与代码的落差
|
||||
|
||||
OpenGame 论文卖的是三大支柱,但深读源码后必须诚实区分**哪些是真代码、哪些只是 README 宣称**。这个区分是本档最重要的"防误判"产出——把论文当代码现状,会严重高估它、也会错配我们的补强方向。
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
subgraph 真["✓ 真代码(仓里能跑)"]
|
||||
GS["Game Skill 经验自进化<br/>= 真护城河<br/>纯本地 JSON · 零向量 · 零 embedding"]
|
||||
end
|
||||
subgraph 半["⚠ 只在 README / 论文"]
|
||||
GC["GameCoder-27B 专训模型<br/>权重/训练码/数据都不在仓<br/>实测默认接 claude-opus-4.6"]
|
||||
VB["OpenGame-Bench 三轴 VLM 验收<br/>grep 零命中,只留<br/>'will be released soon'"]
|
||||
end
|
||||
GS -. "真实验收只到 build + test<br/>不验证可玩性" .-> 真相["仓里真实的『验收』<br/>= driver subtype + build/test"]
|
||||
半 -.-> 真相
|
||||
style GS fill:#a9dfbf
|
||||
style GC fill:#f9e79f
|
||||
style VB fill:#f9e79f
|
||||
```
|
||||
|
||||
**第一支柱,Game Skill 经验自进化——这是仓里真正能跑、也是真正的护城河。** 它由两套同构的离线进化机制组成,两者都遵循同一个闭环:**完成一个任务 → 抽取经验 → 去具体化 → 沉淀回持久库 → 下次先查库命中复用 → 重复达到阈值再升格成可执行规则**。而且都做成"LLM 优先 + 规则兜底"的双轨,库本身是纯本地 JSON——**零向量、零 embedding**(向量 / embedding 指把文本转成向量做相似度检索的 RAG 技术;它一概不用)。
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
Task["完成一个任务"] --> Extract["抽取经验"]
|
||||
Extract --> Generalize["去具体化<br/>(角色名→Player,硬编码值→config)"]
|
||||
Generalize --> Store["沉淀回本地 JSON 库"]
|
||||
Store --> Hit{"下次先查库"}
|
||||
Hit -->|命中| Reuse["复用(整个 family / 已验证 fix)<br/>每次命中省一次 LLM 调用"]
|
||||
Hit -->|未命中| Novel["LLM 兜底:诊断新错 / 归纳新模板"]
|
||||
Reuse --> Count["重复计数"]
|
||||
Novel --> Count
|
||||
Count -->|"达阈值(如重复3次)"| Promote["升格成可执行规则<br/>救火 → 免疫"]
|
||||
Promote --> Store
|
||||
style Reuse fill:#a9dfbf
|
||||
style Promote fill:#f9e79f
|
||||
```
|
||||
|
||||
这套机制有两个具体实现:
|
||||
|
||||
- **Template Skill(模板技能)** 从一个 game-agnostic(与具体游戏无关)的 Phaser 元模板 M0 出发,每完成一个项目跑五段流水线:**Collector** 读文件树、**Classifier** 用 LLM 加启发式判物理 regime(注意它是"library-aware"的,会把库里已有家族的物理画像塞进 prompt,让 LLM 判断新项目是命中旧 archetype 还是新物种,而 archetype 是 LLM 自创的 snake_case 物理标签,不是预设枚举)、**Extractor** 纯规则抽类继承树和 hook、**Abstractor** 用 LLM 把具体代码泛化成模板(角色名→Player、硬编码值→config 引用,并给每个文件打 role:base_class 是 KEEP 不可改、copy_template 是待复制改)、**Merger** 决定建新家族还是并入旧家族。命中复用的匹配键是**物理三元组(重力 / 视角 / 移动)加 archetype 名,而不是文本相似度**;复用单位是整个 family(家族);还有个 stability 信号 = min(1, 贡献项目数 / 5),五个项目贡献过就算满稳定。**所以这套"经验记忆"本质上是 case-based reasoning(基于案例的推理),不是 RAG。**
|
||||
|
||||
- **Debug Skill(调试技能)** 是论文 Algorithm 1 的"REPEAT...UNTIL"实现,也是一本活协议 P。它的数据结构是 `(signature, cause, fix)` 三元组,signature(错误签名)= stage + errorCode + 正则化的 messagePattern + fileContext。协议分两类条目:**reactive**(失败后诊断用)和 **proactive**(执行前预校验用),种子里有 7 条 reactive 加 7 条 proactive 的 Phaser 常见错。它的循环是:先跑 proactive 预校验,然后 build → test,失败就 diagnose(先用 signature 加权打分匹配已知错,errorCode 权重 0.5、messagePattern 0.35、fileContext 0.15,阈值 0.8,命中就直接 applyKnownFix,未命中才走 LLM 诊断)→ repair → 重跑同 stage 验证 → 记录结果。**进化最实的一环是:reactive 的事后修复在重复出现 3 次后,会被自动升格成 proactive 的事前预校验规则——经验从"救火"升级成"免疫"。而且只有跑通验证的修复才进库,这是它唯一的质量门。**
|
||||
|
||||
但要诚实:这套自进化"是真的,但很轻"——**只有单调累积、计数强化、阈值升格,没有遗忘、淘汰、冲突消解**;模板冲突用"保留更长文件"这种启发式;规则一旦生成就不回收。更重要的是两个硬缺口:
|
||||
|
||||
1. **Template Skill 侧完全没有"这个沉淀的骨架真能编译、真可玩"的回验门**,带病的骨架可能直接沉进库;
|
||||
2. **Debug Loop 只验证到 build + test 层,完全不验证可玩性。**
|
||||
|
||||
这两点很关键——它们恰恰是我们的强项能补上的地方(我们有真机九门),§3 会展开。
|
||||
|
||||
**第二支柱,GameCoder-27B 专训模型——README 宣称,但权重、训练码、数据都不在仓。** 它被描述成一个为游戏定制的 27B 代码模型,三段式训练:游戏引擎语料持续预训练 → 在引擎 API 和 bug-fix 轨迹上 SFT(Supervised Fine-Tuning,监督微调)→ 用真实可玩性当奖励信号的 execution-grounded RL(以执行结果为依据的强化学习)。但仓里只有集成点和文字描述。反过来看,这恰恰证明了**框架被刻意设计成 model-agnostic(与模型无关)**——实测代码里默认接的是 openrouter 上的 `claude-opus-4.6` 而不是 GameCoder,设个 `OPENAI_MODEL` 就能换。**专用模型不是跑通的必要条件,模型这层可以被便宜通用模型替换,质量缺口靠模板约束加 Debug Skill 兜底。**
|
||||
|
||||
**第三支柱,OpenGame-Bench 三轴 VLM 验收——README 宣称,但代码库里根本不存在。**(VLM = Vision-Language Model,视觉语言模型,指能"看图打分"的模型。)README 把它描述成动态启动生成的游戏、headless 浏览器执行加 VLM judging,沿 **Build Health**(能否构建运行)、**Visual Usability**(画面是否可用)、**Intent Alignment**(是否符合意图)三轴打分,跨 150 个 prompt。但 README 自己写着"evaluation pipeline will be released soon",全仓 grep `puppeteer|playwright|VLM|screenshot|judge` 在源码里零命中。**仓里真实的"验收"只有两层:生成 driver 的 success 仅取 SDK 返回的 subtype,根本不打三轴分;debug-loop 只跑 build + test。** 所谓的 headless 是 Phaser HEADLESS 类型跑在 jsdom 下,canvas 用 node-canvas 的 Image 桩,**不渲染真实像素、无真实浏览器、无 VLM。把 README 当代码现状,会严重误判。**
|
||||
|
||||
### 2.8 它的执行基座与 MCP:从上游继承的重型沙箱,以及一个被误读的扩展插槽
|
||||
|
||||
最后补两笔执行环境,它们决定了"复刻 OpenGame 要不要搬这些重家伙"。
|
||||
|
||||
OpenGame 的 shell 执行是**重型**的:PTY 优先(node-pty 加 @xterm/headless 把 ANSI 输出解析成结构化数据;PTY = 伪终端),拿不到 PTY 就用 child_process 兜底,detached 让命令独占进程组,中止靠对进程组发 SIGTERM 再 200ms 后 SIGKILL,还用 pgrep 回收后台子进程 PID,外加 docker / podman / seatbelt 沙箱和 HITL 白名单。**这是 agent"写代码 → 跑构建 → 看报错"闭环的物理基座,深度绑定 Node 生态。** 它之所以这么重,是因为它的自主循环要让模型自由地反复跑命令;我们走图编排,build 节点职责窄得多,MVP 阶段并不需要这么重。
|
||||
|
||||
至于 MCP,必须澄清一个常见误读:**OpenGame 自身零内置 MCP server。** 它的 MCP 客户端是从上游完整继承的成熟管线(四种 transport、OAuth 全套、把 MCP tools 经 `mcpToTool` 降维成 Gemini FunctionDeclaration),但 `getMcpServers` 默认是空的——没有任何硬编码的 cocos / figma / playwright server。**MCP 在 OpenGame 里只是一条"用户在 settings 里配什么就连什么"的通用扩展总线,游戏生成能力根本不来自 MCP,而来自内置确定性工具加工程模板。谁要是以为复刻 OpenGame 得复刻一堆 MCP server,就彻底读反了。**
|
||||
|
||||
---
|
||||
|
||||
## 3. 落到我们身上:要在 SAA 图上复刻 OpenGame,到底缺什么
|
||||
|
||||
把 OpenGame 看透之后,落到我们 SAA 配置式生成的现状上,把缺口分成四类来诚实交代:**能直接抄的设计、得换底座重写的、我们其实已有等价物的、以及我们刻意不走所以抄不动也不必抄的。**
|
||||
|
||||
先说一个贯穿性的范式判断,它决定了"抄"的边界——这条在 §1 已经摆过坐标系,这里把它落到具体取舍:**OpenGame 的 `client.ts`/`turn.ts` 那套主循环,我们不该嫁接进来——硬塞就成了缝合设计。** 务实的态度是:**把 OpenGame 的控制流交给 SAA 图来表达**(我们本来就这么做,而且对卡 80% 成功率门而言,图的确定性比"DO NOT STOP"的口头督促更稳,这是升级不是抄);**把 OpenGame 的工具层、韧性兜底、防脏脚手架当成零件库,原样落进 SAA 节点内部的工具执行环节。**
|
||||
|
||||
下面是逐项对照表,然后分四类展开。
|
||||
|
||||
| 能力 | OpenGame 有 | 我们的现状 | 优先级 | 能不能上 SAA 图 |
|
||||
|---|---|---|---|---|
|
||||
| 物理优先分类 | classify 工具 + 5 archetype + 易错例 prompt | 有 classify 节点,但 archetype 体系 / 物理优先 prompt 待补 | 高 | 直接做成 classify 节点 |
|
||||
| GDD 结构化契约 | 6 节硬绑下游 + 三铁律 + 三层规则 | design 节点存在,契约化程度待补 | **最高** | 直接做成 design 节点 |
|
||||
| 确定性贴图 | bitmask 自动贴图 + 3×3→7×7 | 无(且引擎是 LittleJS 非 Phaser) | 中 | 纯算法,直接搬 |
|
||||
| smart_edit 自纠错 | 三级匹配 + LLM 重算 | gameDefinition 结构化生成弱化了文本编辑需求 | 中 | 落进 generate/modify 节点 |
|
||||
| Template Skill 骨架库 | 五段流水线 + family 自进化 | **完全没有** | 高 | 离线进化,SAA 外挂库 |
|
||||
| Debug Skill 活协议 | (sig,cause,fix) + 升格规则 | 规划过、MVP 简化 | 高 | diagnose/repair 节点 + checkpoint |
|
||||
| 资产流水线 | I2V / 抠图 / 多级回退 / 装配 | 走 mmx-cli | 中 | asset 节点接 mmx |
|
||||
| 可玩性验收 | 论文宣称三轴 VLM,**代码未开源** | 机制层=九门真机 CDP(强);语义层=player 软门 M3 截图判(弱) | 机制已领先 · 语义待补强 | 机制已有;语义软门待做厚 |
|
||||
| 专训模型 | GameCoder-27B(权重不在仓) | 便宜通用模型 + harness | **不抄** | 路线不同 |
|
||||
|
||||
> 表中名词补注:**LittleJS** 是我们选定的轻量 H5 游戏引擎(对标 OpenGame 用的 Phaser);**gameDefinition** 是我们当前实现里便宜模型友好的结构化中间表示;**九门 / 九门 harness** 是我们在真浏览器里跑的九道确定性机制验收门(CDP = Chrome DevTools Protocol,用来程序化驱动真实浏览器);**player 软门** 是用视觉模型 M3 看截图打分的软性检查;**mmx-cli** 是投资人版已拍板的媒体生成命令行工具。
|
||||
|
||||
### 3.1 第一类:设计可直接抄进 SAA 节点(价值最高)
|
||||
|
||||
物理优先分类的系统提示(含易错例)加五 archetype 体系,本质就是一个 classify 节点加 JSON 容错解析,可以直接照搬。GDD 的"六节每节映射下游"契约加三铁律加按 archetype 动态拼三层规则,**是 OpenGame 最值钱的产物**——它把开放问题结构化成填空题,这正是我们用便宜模型卡 80% 成功率门最需要的杠杆,做成一个 design 节点,内置规则可以整段移植。确定性 bitmask 贴图是纯算法,与模型无关,直接搬。smart_edit 的三级匹配加 LLM 自纠错对便宜模型尤其必要,务必移植(虽然我们走 gameDefinition 结构化源会弱化纯文本编辑的需求,但 modify/repair 节点里仍用得上)。
|
||||
|
||||
这里有个**范式级前提必须点破**:OpenGame 整条链能跑,根本前提是它有一套预建 Phaser 模板和每个 archetype 的 `template_api.md` hook 清单当"填空靶子"。**Hook Integrity 这条铁律之所以能约束便宜模型,是因为有一份明确的 API 清单让模型不能编造。** 我们要复刻,就必须**先有等价物:一份"LittleJS 增强发行版 + 插件库公开 API 清单"当我们的 `template_api`。** 没有这个,便宜模型一定会编造 API。这一块 OpenGame 抄不来、必须我们自建——而它恰好和创始人 2026-06-17 定的"游戏 = 长生命周期结构化源项目"基座、以及"玩法模板 = 品类框架"的定位是同一个东西。
|
||||
|
||||
### 3.2 第二类:设计可抄,但得换底座重写
|
||||
|
||||
Template Skill 和 Debug Skill 是我们当前最大的能力缺口,也是最该补的。
|
||||
|
||||
**Debug Skill 几乎可以整体移植思想**——验证驱动的 REPEAT 循环加 `(signature, cause, fix)` JSON 账本加分组阈值升格,正好对应 SAA 裸图里一个 diagnose 节点加 repair 节点加 checkpoint(检查点)持久化;签名匹配是纯算法零 LLM,便宜模型只在"诊断 novel 错"和"归纳规则"两处兜底调用,**每次命中都省一次 LLM 调用,对便宜模型尤其划算(把高频错变成确定性查表)。** 种子协议里的 Phaser 错误码要换成我们自己的 LittleJS 运行时和九门错误码,但结构原样可用。
|
||||
|
||||
**Template Skill 可以抄它的范式**(结构化源项目模板的 KEEP/copy/hook 分层、经验计数、离线蒸馏),但实现得整体重写两处:它的物理信号正则和 hook 命名约定死绑了 Phaser 和 OpenGame 自家模板的类名,我们是 LittleJS 加结构化 gameDefinition,得换成"对 gameDefinition 结构做 archetype 分类、对源项目模块做抽取";更重要的是,**它的 Abstractor 泛化对便宜模型是质量重灾区,而原版入库前没有任何回验门,照抄会把坏骨架沉进库——我们必须给它加一道"入库前过九门 / 可构建校验"的质量门,这恰恰是我们的强项**(把我们最硬的东西用在它最弱的地方)。另外要预警一个原版的硬缺口:这套进化没有遗忘、淘汰、冲突消解,也没有向量检索,family 一多线性扫加物理三元组离散匹配会退化;等品类规模上来,我们可能需要补一层真正的检索(上 embedding 或按 gameDefinition 字段索引)。
|
||||
|
||||
**资产流水线属于这一类里"换替代方案"的**:OpenGame 的 I2V 抽帧、T2V 抽音轨、抠图后端依赖视频模型加 ffmpeg,便宜文生图模型替不了动画质量——我们的 asset 节点接投资人版已经拍板的 mmx-cli 即可,大幅降级的部分(静态帧 + 程序音)也照 OpenGame 的多级回退思路兜底。
|
||||
|
||||
### 3.3 第三类:我们其实已有等价物、甚至更强
|
||||
|
||||
OpenGame-Bench 那套三轴 VLM 验收,有个必须点破的事实:**它在开源代码里根本不存在**——README 描述得很全(headless 浏览器跑生成的游戏、VLM 沿 Build Health / Visual Usability / Intent Alignment 三轴打分),但全仓 grep `VLM|screenshot|judge` 零命中,只留一句"will be released soon"。所以单比已经开源出来的东西,我们确实更硬:我们有真机 CDP 真玩的九门加 M3 截图判,OpenGame 放出来的只有 build + test、并不验证可玩性。
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
subgraph 三轴["OpenGame 论文宣称的三轴(代码未开源)"]
|
||||
BH["Build Health<br/>能不能构建运行"]
|
||||
VU["Visual Usability<br/>好不好看"]
|
||||
IA["Intent Alignment<br/>是不是用户要的那个游戏"]
|
||||
end
|
||||
BH -->|"对应 · 我们确定性领先"| NG["九门 harness<br/>真机 CDP · 确定性机制判定<br/>装载/帧/输入/终态"]
|
||||
VU -->|"对应 · 我们偏弱"| PS["player 软门<br/>M3 截图判 · 软 · 跑便宜模型 · 不成体系"]
|
||||
IA -->|"对应 · 我们偏弱"| PS
|
||||
NG -. "判不了好不好看 / 对不对" .-> 边界["别把九门当 VLM 等价物"]
|
||||
PS --> 动作["正确动作:把 player 软门做厚<br/>独立交叉校验 + 离线 bench<br/>蓝本 = OpenGame 论文三轴"]
|
||||
style NG fill:#a9dfbf
|
||||
style PS fill:#f9e79f
|
||||
style 边界 fill:#f5b7b1
|
||||
```
|
||||
|
||||
但这里要分清两层、别就此自满——这也正是不该把"九门"简单当成 VLM Bench 等价物的原因。九门判的是**机制**:能不能装载、跑不跑帧、响不响应输入、到不到终态,全是确定性的,这一层我们确定性地领先,没有疑问。可 VLM 的另外两轴——Visual Usability(好不好看)、Intent Alignment(是不是用户要的那个游戏)——**九门根本判不了**;这两件事在我们这边对应的不是九门,而是 **player 软门(M3 视觉模型看截图)**,而 player 软门是软的、跑便宜模型、不成体系,这恰恰是我们的弱项而非强项。
|
||||
|
||||
所以正确的动作**不是**"把九门结果套个三轴外壳就交差"(九门产不出"好不好看""对不对"这两轴),而是**把 player 软门那条做厚**:让"是不是要的游戏、好不好看"有更系统、可复现的判定——独立于生成方的交叉校验,甚至一个离线 bench。OpenGame 论文那套三轴设计,正是这一层值得我们抄的蓝本,即便它代码并没放出来。
|
||||
|
||||
### 3.4 第四类:刻意不走、抄不动也不必抄
|
||||
|
||||
**GameCoder-27B 专训模型**——权重、训练数据、RL 代码都不在仓,而且我们的路线明确是"便宜通用模型 + harness 兜底",不自训模型。OpenGame 框架自身的 model-agnostic 设计反而印证了我们这条路走得通:专用模型不是跑通的必要条件。这一项无需复刻。
|
||||
|
||||
**OpenGame 的命令式自主主循环、重型 PTY 沙箱、@google/genai 的 MCP 适配层**,都是 Node/TS 生态产物,且与我们 SAA 图的控制流范式正交。主循环用 SAA 图替代;PTY 沙箱如果将来需要,Java 侧得用 ProcessBuilder/pty4j 自建等价物(工作量不小,但 MVP 阶段我们的 build 节点未必需要这么重);MCP 走 SAA v1.1.2.2 自带的 MCP 支持,别试图移植那套 JS 适配。
|
||||
|
||||
---
|
||||
|
||||
## 4. 收口结论
|
||||
|
||||
我们和 OpenGame 走的是两条路,但**终点一致:都靠"把开放式游戏生成收敛成受控填空"来让不够强的模型也能稳定产出。** OpenGame 用命令式 agent 循环加 system-reminder 接力来收敛,我们用 SAA 图来收敛——**在这一点上我们不是落后,而是用了更确定的范式。**
|
||||
|
||||
真正的差距集中在三处——这三处正是 README §5 那一页结论的来源:
|
||||
|
||||
1. **生成约束的厚度**(物理优先分类 + GDD 契约 + template_api 清单,这是设计可直接抄的最高价值项,且与"结构化源项目"基座天然契合);
|
||||
2. **经验复利层**(Template/Debug Skill,我们几乎从零,但设计可抄、且能用九门当原版缺失的质量门);
|
||||
3. **资产线**(接 mmx 替代)。
|
||||
|
||||
至于 OpenGame 论文最响的两个卖点,得分开说。**专训 GameCoder-27B 我们刻意不走**——走便宜通用模型加 harness 兜底,而它自己框架的 model-agnostic 设计反而证明了专训模型不是跑通的必需。**VLM Bench 则要说准:它的三轴验收在开源代码里其实没放出来、只在论文里**,所以单比已开源的,我们的真机九门已经更硬;但别把"九门"当成 VLM 的等价物——九门更强的只是"能不能跑"这一层(Build Health),而 VLM 的"是不是要的游戏、好不好看"那两轴九门判不了,对应的是我们偏弱的 player 软门,那是待补强的缺口,不是已经赢了的项。
|
||||
|
||||
**最该立刻动手的,按优先级是:**
|
||||
|
||||
1. 把 **template_api**(LittleJS 插件库公开 API 清单)和 **GDD 契约**补上,这是一切约束的靶子和最高价值杠杆;
|
||||
2. 把 **Debug Skill** 的"签名 → 已验证修复 → 重复升格"活协议落成 diagnose/repair 节点加 checkpoint;
|
||||
3. 把 **Template Skill** 的骨架进化库按 gameDefinition 重写,并强制加入库前过九门的质量门。
|
||||
|
||||
这三件做完,我们这条 SAA 配置式流水线就补齐了 OpenGame 真正值钱的那部分,而且是用我们自己更确定、更可验收的方式补齐的。
|
||||
|
||||
---
|
||||
|
||||
## 5. 源档与去向导航
|
||||
|
||||
| 去向 | 找什么 |
|
||||
|---|---|
|
||||
| [生成主线架构演进路线](../../../agent-specs/生成主线架构演进路线.md) | **统一的 to-do 在这里**:本档照出的三处缺口已并入那份单一 SoT 的"统一演进路线",含阶段序列、关键路径、cutover 门——要"做什么"去那份拿 |
|
||||
| [生成引擎 README](README.md) | 生成引擎子树主文档,§5 是本档的一页结论版;§3 讲"游戏 = 长生命周期源项目、LLM as 工作室"的范式原则 |
|
||||
| [SAA 编排](SAA编排.md) | 我方 16 节点拓扑图、六条不变量、split-brain 两条治理铁律、落地接入坑清单 |
|
||||
| `github.com/leigest519/OpenGame` | OpenGame 真源码本体(本档所有结论的锚点,论文 arXiv 2604.18394) |
|
||||
|
||||
> **纪律**:本档是 OpenGame 外部标杆的**深读参考**——回答"它的内部机理"和"逐项缺口的来龙去脉"。"我们要做什么"的统一行动项不在这里,在[生成主线架构演进路线](../../../agent-specs/生成主线架构演进路线.md);两者职责不同,不互相重复。当行动项与本档分析冲突时,以那份演进路线的最新裁定为准。
|
||||
|
||||
---
|
||||
|
||||
> **验证状态**:本文档为架构对照参考文档,由一份已评审源档(`2026-06-20-OpenGame对照分析与复刻缺口.md`,该档由 5 个 agent 分片深读 clone 下来的 OpenGame 真源码后综合)重写而成,未改任何代码。承重硬事实——OpenGame = Gemini-CLI→Qwen-Code 二次 fork 的三处铁证、四原子工具 + 六阶段 SOP、三铁律(Config-First/Zero Custom Code/Hook Integrity)、`MAX_TURNS=100`、Debug Skill 加权阈值(0.5/0.35/0.15,阈值 0.8)与重复 3 次升格、stability=min(1,项目数/5)、三大支柱中仅 Game Skill 是真代码而 GameCoder-27B 与三轴 VLM Bench 只在 README、我方 SAA 16 节点 / 九门 / 80% 门 / gamedef 双证、三处复刻缺口与四类取舍——均沿用源档已抽查属实的结论;品牌已统一为"绘境AI"。
|
||||
345
docs/architecture/架构/生成引擎/README.md
Normal file
345
docs/architecture/架构/生成引擎/README.md
Normal file
@ -0,0 +1,345 @@
|
||||
# 生成引擎 · 设计主文档
|
||||
|
||||
> **这是什么**:绘境AI 生成引擎子树的设计主文档,回答"**一句话怎么变成一款可上线的游戏**"这条技术主线——它的现行架构、要往哪走的演进路线,以及贯穿全程的一条范式原则。
|
||||
> **给谁看**:负责生成主线的工程师、新加入这条线的同事、架构评审、做尽调时想看清护城河关键路径的人。
|
||||
> **怎么读**:先读 §1 建立"这台机器是什么"的整体认知,再看 §2 那张端到端流程图;想了解当前债与未来路线读 §4–§6;想要更深的子系统细节,从文末的源档导航进去。
|
||||
|
||||
生成引擎是绘境AI 护城河的关键路径。竞品大多止步于"能生成";真正难的是让一个普通模型**稳定地、可维护地、可长期演进地**把一句话变成一款真游戏。这份文档讲的就是这条路怎么走通。
|
||||
|
||||
---
|
||||
|
||||
## 1. 一句话与一张图:生成引擎是什么
|
||||
|
||||
绘境AI 的游戏生成主线,本质上是一台**被刻意设计成可靠的、固定相位的生成机器**。它不是一个让强模型自由发挥的开放式编码助手,而是一条流水线:**用便宜的通用模型驱动、用确定性的图编排约束控制流、用一套自动验收门兜底**。
|
||||
|
||||
这台机器有三个互相支撑的设计支点,理解了它们就理解了整条线为什么这么搭:
|
||||
|
||||
- **便宜模型 + 门兜底,而不是训一个专用大模型。** 模型这一层硬编码了 11 个便宜的通用模型(以 deepseek-v4-flash 打底),没有任何一个是为游戏代码专门训练过的。质量缺口不靠把模型做重来补,而靠模板约束和验收门来补。这是成本策略,也是差异化判断——大模型迟早会追上,真正的护城河不在生成引擎本身。
|
||||
- **图编排约束控制流,而不是让模型自由决定下一步。** 整条线的骨架是 **SAA 裸 StateGraph**(SAA = Spring AI Alibaba,阿里的 Spring 生态 AI 编排框架;"裸 StateGraph"指直接用它的有向状态图原语手写编排,而不是用声明式 YAML 配置)。一共 16 个节点,每个节点是一段实打实的 Java 代码,节点之间的流转由图预先固定,而非由模型在运行时临场决定"下一步调什么工具"。
|
||||
- **一套硬验收门当地板。** 这就是俗称的"**九门 harness**"(harness 指把生成产物放进一个真实运行环境去自动检验的测试夹具;"九门"是其中九道确定性检查):能不能装载、跑不跑帧、响不响应输入、到不到游戏终态……全部是确定性的判定。这一层是这套架构最大的工程价值,也是绝不动它的底座。
|
||||
|
||||
把这三点合起来,可以用一句话概括整台机器的定位:**LLM 在这里扮演的是"游戏工作室"的角色**——它替代的是一个真实游戏工作室原本要做的策划、写码、配资产的活,而不是充当一个一次性出活的黑盒。这个"LLM as 工作室"的定位是整条线的灵魂,§3 会专门展开。
|
||||
|
||||
这台机器当前的真实状态需要诚实说清:**结构正确、控制流确定、底层验收很硬的流水线已经建成并合入主干;但生成质量尚未稳定达到 80% 这道门,而且一部分本该可替换的"策略"被焊死进了不该重编译的"机制"代码里。** 换句话说,骨架立住了,接下来要解决的是"质量"和"策略外置"两件事——这正是 §4–§6 演进路线要回答的。
|
||||
|
||||
---
|
||||
|
||||
## 2. 一张端到端流程图:一句话怎么变成一款游戏
|
||||
|
||||
下面这张图是整条生成主线的全貌。16 个节点不是抽象概念,而是图里真实存在的执行单元。读图时抓住三段就好:**先理解题面(render→classify→design)→ 再造出东西(generate→scaffold→asset→build)→ 最后反复验收直到合格或放弃(play/player/nreview→modify/repair/escalate→emit/giveup)**。
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
Brief["一句话 / 选模板 + 素材"] --> Render["render<br/>渲染请求与上下文"]
|
||||
Render --> Classify["classify<br/>判定游戏品类"]
|
||||
Classify --> Design["design<br/>产出设计文档 GDD"]
|
||||
Design --> Generate["generate<br/>便宜模型生成玩法逻辑"]
|
||||
Generate --> Scaffold["scaffold<br/>脚手架出源项目结构"]
|
||||
Scaffold --> Asset["asset<br/>生成 / 装配资产"]
|
||||
Asset --> Build["build<br/>确定性构建出可玩产物"]
|
||||
Build --> Play["play + player<br/>真机运行 + 视觉软门看截图"]
|
||||
Play --> NReview["nreview<br/>九门 harness 机制验收"]
|
||||
|
||||
NReview -->|过门| Emit["emit<br/>发布产物 + 落库"]
|
||||
NReview -->|可修| Modify["modify / repair<br/>定位并修复"]
|
||||
NReview -->|升档| Escalate["escalate<br/>换更强模型重试"]
|
||||
NReview -->|彻底失败| Giveup["giveup<br/>显式放弃 + 留证据"]
|
||||
|
||||
Modify --> Generate
|
||||
Repair["repair"] --> Generate
|
||||
Escalate --> Generate
|
||||
|
||||
Validate["validate<br/>校验中间产物"] -.横切.- Generate
|
||||
|
||||
style NReview fill:#f9e79f
|
||||
style Emit fill:#a9dfbf
|
||||
style Giveup fill:#f5b7b1
|
||||
```
|
||||
|
||||
这张图里有几个名词第一次出现,先解释清楚:
|
||||
|
||||
- **GDD(Game Design Document,游戏设计文档)**:design 节点的产物。它把"做个游戏"这件开放的事,结构化成一张每一节都硬绑下游工具入参或代码文件的待办清单——资产表喂给资产工具、布局喂给贴图工具、数值合并进配置。它是约束便宜模型不跑飞的核心抓手。
|
||||
- **品类(archetype)**:游戏的玩法类型(如平台跳跃、俯视射击、逻辑解谜)。classify 节点先把题面归到某个品类,后续的设计与生成都围绕这个品类的框架展开。
|
||||
- **escalate(升档)**:当前模型反复失败时,图层会切换到一个更强的模型重试。这是图层显式的"加钱保质量"机制。
|
||||
- **emit / giveup(发布 / 放弃)**:两个终态。emit 是成功出口——产物落库并进入发布链;giveup 是失败出口——**显式放弃并留下证据**,绝不交付一款坏游戏。"宁可显式失败,也不交付坏游戏"是这条线的铁律。
|
||||
|
||||
整条流程最关键的特征是**它是一个带回环的有向图,不是一条直线**:验收不过时,nreview 会把控制权交回 modify(确定性修复)、repair(基于经验的修复)或 escalate(升档重试),修完再重新走 generate→build→验收。这个回环是质量的来源,但也正是后面要小心治理的地方——出题的和被考的不能是同一只模型(§4 会讲)。
|
||||
|
||||
---
|
||||
|
||||
## 3. 范式原则:游戏是长生命周期的源项目,LLM 是它的工作室
|
||||
|
||||
在讲债和路线之前,必须先钉死一条**比任何技术债都更根本的范式原则**,因为它决定了这条生成主线"什么才算做对了"。这条原则由创始人在 2026-06-17 裁定、2026-06-20 进一步拍板,是整个生成引擎子树的定调。
|
||||
|
||||
### 3.1 范式转变:从"一次性产物"到"长生命周期项目"
|
||||
|
||||
过去的做法(称为"旧范式")是:生成 = 产出一坨能玩的打包文件存下来;想修改时就去 diff 这个打包产物——而这条路根本走不通。新范式把这个死结从根上解开了:
|
||||
|
||||
| 旧范式(一次性反模式) | 新范式(长生命周期项目) |
|
||||
|---|---|
|
||||
| 生成 = 产出一坨可玩的打包文件,存下来 | 生成 = LLM(工作室)**创建并长期维护一个游戏源项目** |
|
||||
| 修改 = 想办法 diff 打包产物(不可行) | 修改 = 工作室在**源项目**上做一次开发迭代,再构建 |
|
||||
| 产物 = 一次性、不可维护的硬编码代码块 | 规范物 = **结构化、可维护、数据驱动的源项目**(像工作室的代码仓) |
|
||||
| 打包 / 发布是终点 | 打包 = 一个构建步、发布 = 一次版本发布;项目**持续演进** |
|
||||
|
||||
一句话:**把游戏建模成长生命周期的软件项目,LLM 是它的工作室;create / modify / extend / build / release / maintain 都是项目生命周期操作。** 这从根上化解了"怎么改打包产物"的死结——**不碰产物,在源项目上演进,再重新构建**。这里的"打包产物"和"源项目"是两个必须分清的东西:打包产物是给玩家跑的最终包,源项目是工作室回头要改的那份真实代码。
|
||||
|
||||
为什么源项目必须结构化、模块化、数据驱动?**因为工作室(LLM)将来要回来改它。** 一份长这样的源项目结构,正是为"可被 agent 快速定位、局部修改、增量扩展"而设计的:
|
||||
|
||||
```
|
||||
game-project/
|
||||
├── game.json # 项目元数据(引擎版本 / 入口 / 品类 / 标题 / 版本)
|
||||
├── design.md # GDD:玩法意图 / 机制 / 胜负条件(工作室的"需求与设计")
|
||||
├── config/
|
||||
│ ├── params.json # 平衡参数(数值 / 难度 / 速度)——改这里 = 调平衡,免 LLM
|
||||
│ └── levels/ # 关卡数据——加一个文件 = 加一关
|
||||
├── src/ # 模块化游戏逻辑(按系统拆:input / spawn / score / physics…)
|
||||
├── assets/ # 六类资产:sprite / character / effect / scene / ui / music(引用,非内联)
|
||||
└── build.json # 构建配置
|
||||
```
|
||||
|
||||
这样一来,生命周期里的每种操作都各得其所:**create** 是脚手架出这个项目;**modify** 时,换美术 / 调参 / 改关卡是确定性地编辑源文件(免 LLM、秒级),只有改玩法逻辑才让 LLM 重新生成那一个模块;**extend** 是加文件(additive,即只增不改);**build** 是确定性地把源项目构建成可玩产物;**release** 是把构建产物发布为一个版本、过审进入游戏信息流;**maintain** 是据玩家遥测和反馈,工作室回到项目继续演进——这一步把数据闭环接回了护城河。
|
||||
|
||||
### 3.2 终态硬约束:产物必须是 src/ 工程,gameDefinition 只是脚手架
|
||||
|
||||
创始人在 2026-06-20 拍下一条**不可妥协的终态定调**,它直接决定了生成产物"合不合格":
|
||||
|
||||
> **生成游戏的终态产物,必须是一个 `src/` 多文件代码工程——结构化、模块化、可导航、agent 能快速定位与探索的真实源码项目。无论游戏简单还是复杂,最终落地的都必须是 src/ 工程。**
|
||||
|
||||
由此必须诚实纠正一处此前的认知偏差。当前实现里有一个叫 **gameDefinition** 的 JSON 中间表示——它把整段玩法逻辑塞进 `behavior.code` 这个 JSON 字符串字段,运行时用 `new Function`(JavaScript 里把字符串当代码执行的机制)直接解释执行。这恰恰是设计明令反对的"硬编码代码块":改一行要在字符串里找、agent 无法结构化定位。所以两者的关系在此一次性钉死:
|
||||
|
||||
- **终态产物 = src/ 工程**(不可妥协);
|
||||
- **gameDefinition = 通往 src/ 的中间妥协态**——妥协只允许在"输入端"(让便宜模型先产一个极简的声明式描述,因为便宜模型确实更擅长产这种结构),**绝不允许妥协在"产物端"**;它必须经过一道真实的"gameDefinition 编译 / 展开成 src/ 工程"的构建,而不是停在 JSON 里被 `new Function` 跑掉;
|
||||
- **当前实现(JSON 内嵌 JS 串 + `new Function` 解释执行)= 已知偏离终态的债**,缺的正是"gameDefinition → src/"产物侧这一段展开。
|
||||
|
||||
需要把"双证地基成立"这件事说准:它证明的是一件真实但有限的事——**便宜模型能可靠地产出连贯的 gameDefinition 中间表示**;它没有、也不能证明"gameDefinition 就是合格的终态产物"。
|
||||
|
||||
这条定调过去"写了却传不到实现",所以它必须配一道**机制门(doc↔code 兑现门)**:生成产物要能被自动断言为"存在 src/ 多文件结构、玩法逻辑落在真实源码文件里、不存在'逻辑只以 JSON 字符串形态存在 / 运行时 new Function 跑内嵌串'"。设计声称要 src/,代码就必须可验证地兑现 src/——否则"产物必须是 src/"这条就只是又一条等人自觉的软约束。
|
||||
|
||||
### 3.3 七质量 → 生成产出的硬契约
|
||||
|
||||
这条范式原则不是口号,它落到生成产出上是七条可检验的硬约束。它们共同回答了一个被早期方案忽略的根本问题:**生成质量的真标尺不是"这次能不能玩",而是"工作室将来能不能维护它"。**
|
||||
|
||||
| 质量 | 落到生成产出的硬约束 |
|
||||
|---|---|
|
||||
| 长期 / 可维护 | 模块化代码 + GDD;工作室能回来定位并改 |
|
||||
| 可扩展 | additive 内容模型(加关卡 / 资产 / 模块不重写) |
|
||||
| 灵活 | **数据驱动**(行为走 config / data,不硬编码) |
|
||||
| 稳定 | 确定性构建 + 版本化发布 + 回滚 + 构建门(九门 / 质量门) |
|
||||
| 简单 | 轻游戏轻结构,不过度工程(够维护即可) |
|
||||
| 高效 | 增量改 + 增量构建;确定性操作免 LLM;复用模块 / 资产 |
|
||||
|
||||
---
|
||||
|
||||
## 4. 现状诊断:我们的债在哪里
|
||||
|
||||
把这台机器拆开看,真正的病根不是"没建成",而是**机制已通、生成质量未达门,且策略下沉进机制层造成了多源漂移**。债集中在三类。
|
||||
|
||||
**第一类是策略下沉进机制层。** 本该作为可替换数据的策略,被焊死进了不该重编译重部署的代码里,造成三处倒置:
|
||||
|
||||
- **模型路由**:11 个便宜模型的名字和每个角色的采样配置,全是 Java 常量,换一个模型要改代码、重新构建。
|
||||
- **prompt 正文**:gameDefinition 的生成约定(系统提示、运行时写法约定、命名对齐)是 Java 字面量,而另一侧的 Python worker(worker 指实际跑生成的独立工作进程)又镜像了一份。同一份约定存在两个源,这是一个潜在的 **split-brain**(直译"裂脑",指同一份事实存在两个独立副本、迟早会不一致的隐患)。当前 worker 走的还是纯 iife 路(iife 即立即执行函数,指把游戏打成一个单文件代码块的老路),这条裂缝尚未真正激活;但只要后续切换依赖 worker 线的数据,它就会立刻变成承重的坑。
|
||||
- **品类骨架**:由 design 节点自己产出,没有外部锚点。
|
||||
|
||||
这三处倒置的共同后果是:凡涉及跨 Python 与 SAA 双轨的策略,一旦"策略化"做得不干净,就会从"一处硬编码"恶化成"多处不同步",拆东补西。
|
||||
|
||||
**第二类是验收 oracle 的 Goodhart 风险——九门是机制地板,不是质量裁判。**(oracle 在测试语境里指"判定对错的权威";Goodhart 风险指"一旦一个指标变成目标,它就不再是好指标"——刷指标而非真达标。)九门 harness 判的是**机制**:能不能装载、跑不跑帧、响不响应输入、到不到终态,全是确定性的,这一层我们确定性地领先,零理由动它。但它有两个真实口子:
|
||||
|
||||
- **latch 终态语义不分胜负**:latch 指"游戏到达了某个终态"的锁存信号。当前它由系统强加(永远期望为真),哪怕游戏弃守排空也能逼出一个游戏结束信号——这意味着一个空壳游戏可以蹭过这道门,而 latch 正是"机制有进展"那一门的承重件。它对"这到底是不是题面要的那个游戏"几乎零鉴别力。
|
||||
- **出题与被考同源**:classify 自产品类、design 自产验收规格,而便宜模型的核心毛病恰恰是连 `ball.x`、`driver:none` 这种最基本的东西都会反复写错。让同一只待验模型既出题又答题,验收闭环就有一格没真正断开。
|
||||
|
||||
**第三类是 factory 与 gamedef 双轨未切换。** 我们有两条生成产线并存:
|
||||
|
||||
- 老的 **factory 路**(填参装配,当前默认产线,但实测成功率约 **60%**,低于 80% 门);
|
||||
- 新的 **gamedef 路**(便宜模型友好的结构化中间表示,已双证"能被可靠产出",但产物侧"展开成 src/ 工程"那段尚缺,且尚未切为默认——见 §3.2)。
|
||||
|
||||
双轨并存本身是健康的演进态——新路没稳前不该贸然切换——但如果不立一道硬退役门,演进态就会沉淀成永久债。与之伴生还有一个成本侧盲区:generate 节点内部有一条回退链会静默切模型,但它不计入失败计数、也不进升档事件,与图层的 escalate 升档是两套互不相通的机制;这导致"一次失败到底走了几层模型"算不清,而切换的成本归因恰恰依赖这个数。
|
||||
|
||||
> **一处已被实证反掉的误判,记在这里免得重复建设**:源项目持久层不是悬空的。`game_source_project` 的建表迁移真实存在(V18/V20 双副本),落库与标记构建完成的调用在回调实现里被真正调用(事务外层、按内容哈希幂等)。所以它已经是一等公民,真实缺口只是血缘丰富度和 gamedef 路的端到端验证——这是 P2,不是要去补一条根本不存在的落库链。
|
||||
|
||||
---
|
||||
|
||||
## 5. 对标 OpenGame:我们缺哪几层
|
||||
|
||||
把当前最强的外部开源系统 **OpenGame**(一个把一句话变成游戏的开源生成系统,源码在 `github.com/leigest519/OpenGame`)看透之后,落到我们的现状上,真正的差距集中在三层。它们与 §4 的内部债不重叠——内部债是"已有的东西没做对",这三层是"值钱的东西我们还没有"。
|
||||
|
||||
在展开之前,要先讲清一个决定取舍边界的判断:**OpenGame 是命令式自主循环**(在一个 `while(true)` 里,模型自由决定下一步调什么工具,控制流由模型驱动),**我们是声明式有向图编排**(节点、边、条件预先固定,控制流由图驱动)。这是两种正交的范式。所以我们**不把 OpenGame 的主循环嫁接进来**——硬塞就成了项目明令反对的"缝合设计",而且对卡 80% 成功率门这件事,图的确定性本就比"DO NOT STOP"那种口头督促更稳。我们要抄的,是 OpenGame 的工具层、韧性兜底、防脏脚手架与经验复利机制,把它们当零件库,原样落进 SAA 节点内部;我们不抄它"模型自由驱动主循环"那套范式。
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
subgraph 我方["绘境AI 生成引擎"]
|
||||
G["声明式有向图编排<br/>16 节点 SAA StateGraph"]
|
||||
N["九门 harness<br/>机制验收(领先)"]
|
||||
end
|
||||
subgraph 缺口["OpenGame 证明值钱、我们要补的三层"]
|
||||
L1["① GDD 契约 + template_api<br/>把开放题收敛成填空题"]
|
||||
L2["② 经验复利层<br/>Debug Skill + Template Skill"]
|
||||
L3["③ 资产线 + 语义验收<br/>asset 接 mmx + player 软门做厚"]
|
||||
end
|
||||
G -. 当零件库落进节点内部 .-> 缺口
|
||||
```
|
||||
|
||||
**第一层缺的是生成约束的厚度——最高价值缺口。** OpenGame 端到端能跑通的根本前提,是它有一套预建模板,外加每个品类一份 hook 清单当"填空靶子"(hook 指模板预留给生成代码挂接的接口点)。它的生成质量不来自强模型自由发挥,而来自**把开放问题收敛成填空题**:一份 GDD 把"做个游戏"结构化成每一节都硬绑下游工具或代码文件的待办清单。约束便宜模型不跑飞的是三条铁律:**数值只能进配置、只用模板已有行为、绝不许编造清单里没有的 hook**。而第三条铁律之所以**能**约束住便宜模型,正是因为有一份明确的 API 清单让它无从编造。
|
||||
|
||||
我们这一侧,classify 和 design 节点存在,但品类体系、物理优先的分类提示、GDD 的契约化程度,以及最关键的那份"**LittleJS 增强发行版 + 插件库公开 API 清单**"当我们的填空靶子——都还没补齐。(LittleJS 是我们选定的轻量 H5 游戏引擎,"增强发行版"指我们在它之上加了一层能力插件库。)没有这份清单,便宜模型一定会编造 API。这一层 OpenGame 抄不来、必须我们自建,而它恰好和"游戏 = 长生命周期结构化源项目"基座、"玩法模板 = 品类框架"的定位是同一个东西。
|
||||
|
||||
**第二层缺的是经验复利层,我们几乎从零。** OpenGame 真正的护城河是两套同构的离线经验自进化机制,都遵循同一个闭环:完成任务 → 抽取经验 → 去具体化 → 沉淀回本地 JSON 库 → 下次先查库命中复用 → 重复达阈值再升格成可执行规则——**纯本地、零向量、零 embedding,本质是基于案例的推理而非检索增强**:
|
||||
|
||||
- **Debug Skill(调试技能)** 是一本活协议:数据结构是(错误签名、原因、修复)三元组,签名匹配是纯算法零 LLM,只在"诊断新错"和"归纳新规则"两处兜底调 LLM。最实的一环是——同一个修复在重复出现三次后,会自动升格成"执行前的预校验规则",经验从"救火"升级成"免疫";而且只有跑通验证的修复才进库。
|
||||
- **Template Skill(模板技能)** 从一个与具体游戏无关的元模板出发,每完成一个项目跑一条流水线,把具体代码泛化成带 KEEP / copy / hook 分层的模板,以"物理三元组 + 品类名"为匹配键复用整个家族。
|
||||
|
||||
我们规划过 Debug Skill 但 MVP 做了简化,Template Skill 那种"从完成的游戏项目反向生长品类骨架库"的能力则完全没有。它对便宜模型尤其划算——每一次签名命中都省一次 LLM 调用,把高频错变成确定性查表。
|
||||
|
||||
**第三层缺的是资产线,以及偏弱的语义验收。** 资产这块,OpenGame 走的是一整套"生成、抠图、视频抽帧、多级回退、清单装配"的重流水线,依赖视频模型 + ffmpeg + 抠图后端——便宜的文生图模型替不了动画质量,这是真壁垒。我们不照抄这套依赖,而是让 asset 节点接已拍板的 **mmx-cli**(王蓝莓投资人版定的媒体生成工具),大幅降级的部分(静态帧 + 程序音)照它的多级回退思路兜底即可。
|
||||
|
||||
语义验收这块要说准一个事实:**OpenGame 论文最响的"三轴 VLM 验收"在开源代码里其实并不存在。**(VLM = Vision-Language Model 视觉语言模型,指能看图打分的模型。)它的 README 描述得很全(用无头浏览器跑生成的游戏,VLM 沿 Build Health 构建健康、Visual Usability 视觉可用、Intent Alignment 意图对齐三轴打分),但全仓搜不到任何渲染真实像素、真实浏览器或 VLM 打分的代码,只留一句"will be released soon",真实验收只到构建 + 测试。所以**单比已开源的东西,我们的真机九门已经更硬**。
|
||||
|
||||
但这里要分清两层、别就此自满:九门判的是机制(对应 Build Health 那一轴),我们确定性领先;可另外两轴——好不好看、是不是用户要的那个游戏——九门根本判不了,它们在我们这边对应的是 **player 软门**(用视觉模型看截图打分的软性检查),而 player 软门是软的、跑便宜模型、不成体系,这恰恰是我们的弱项。所以正确动作不是"把九门结果套个三轴外壳就交差",而是**把 player 软门那条做厚**——OpenGame 论文那套三轴设计,正是这一层值得我们抄的蓝本,哪怕它代码并没放出来。
|
||||
|
||||
---
|
||||
|
||||
## 6. 统一演进路线
|
||||
|
||||
把内部债和外部缺口合起来,演进沿**两条并行的线**推进:**线 A 向内加固现有架构**(把硬编码的策略抽成数据契约、把跨进程对齐点锁成单一事实源、把验收信号补上独立的判定依据);**线 B 向外补齐 OpenGame 证明值钱的能力层**。
|
||||
|
||||
贯穿两条线的总原则有三条,它们是后面所有具体动作的护栏:
|
||||
|
||||
1. **不重写任何主干。** 裸图躯干、九门、源项目持久化都留着,改的只是策略层外置与验收门加固,而且全部是 additive(只增不改)加 fallback(失败回落)。
|
||||
2. **所有新增的质量门默认只观测不收紧。** 当前是刻意宽门放量在攒数据(开闸两门焊死放行、三门只观测),任何新门若一上来就生效,会立刻掐断这条灰度闭环。所以新门一律默认关闭、只记录轨迹,达标率稳定后再一行翻开。
|
||||
3. **凡涉及跨 Python 与 SAA 双轨的策略外置,数据契约必须是语言无关的 JSON / YAML、两轨都读它、CI 卡一致性(parity)**——否则"策略化"就沦为"跨语言漂移",把一处硬编码换成多处不同步。
|
||||
|
||||
两条线不是各跑各的:它们在两个点上解的是同一个问题,**必须合并成一处实现,不能各列一遍**;最后由一道里程碑门(cutover)收口。下面这张图先把这层关系摆清楚,再分小节展开:
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
subgraph LA["线 A · 向内加固(把策略从机制里抽出来)"]
|
||||
A1["trace 抽取 + 文件拆分<br/>(纯重构)"]
|
||||
A2["★ prompt 同源<br/>解 split-brain"]
|
||||
A3["模型外置 models.yaml"]
|
||||
A4["latch 终态语义<br/>分胜利 / 弃守"]
|
||||
end
|
||||
subgraph LB["线 B · 向外补能力(抄 OpenGame 的零件)"]
|
||||
B1["★ GDD 契约 + template_api<br/>(最高价值)"]
|
||||
B2["Debug Skill<br/>diagnose / repair + checkpoint"]
|
||||
B3["Template Skill<br/>按 gameDefinition 重写 + 九门门"]
|
||||
B4["资产线接 mmx"]
|
||||
end
|
||||
subgraph X["两处交汇点(一件事,一处实现)"]
|
||||
X1["交汇点一:品类独立交叉校验<br/>= 物理优先分类"]
|
||||
X2["交汇点二:player 软门做厚<br/>= 验收去自评"]
|
||||
end
|
||||
CUT["★ cutover 里程碑门<br/>gamedef 成功率达 80% → 切默认产线<br/>二分退役 factory · 拦截门翻开"]
|
||||
|
||||
A4 --> X1
|
||||
B1 --> X1
|
||||
A4 --> X2
|
||||
B1 --> X2
|
||||
A2 --> CUT
|
||||
X1 --> CUT
|
||||
X2 --> CUT
|
||||
style A2 fill:#fde68a
|
||||
style B1 fill:#fde68a
|
||||
style CUT fill:#fca5a5
|
||||
```
|
||||
|
||||
关键路径(图中黄色 ★)是一条线:**prompt 同源(解 split-brain)→ 质量信号可信(两交汇点)→ cutover**;红色的 cutover 是最终里程碑门,只有 gamedef 路成功率真达 80% 才放行切换。下面三小节依次展开线 A、线 B、两处交汇点。
|
||||
|
||||
### 6.1 线 A:加固现有架构
|
||||
|
||||
| 事项 | 做什么 | 性质 |
|
||||
|---|---|---|
|
||||
| trace 抽取与文件拆分 | 把内联在近九百行调度器里的轨迹抽取逻辑(约 280 行)抽成独立纯函数 `SaaTraceExtractor` 补单测;把过载的 `SaaStudioNodes` 单文件(一千六百多行)按职责拆包。调度器从近九百行瘦到约六百行 | 纯重构、零行为变更 |
|
||||
| **prompt 同源,解 split-brain** | 把 gameDefinition 的生成约定从 Java 字面量提为 `contracts/prompts` 下的版本化资产,让 Java 与 worker 都从这一份资产读、CI 卡到字节一致 | **线 A 关键路径起点** |
|
||||
| 模型外置 | 把 11 个模型名 + 每角色采样配置外置为 `models.yaml`,加载逻辑从配置读、常量作 fallback | 契约先行 |
|
||||
| latch 终态语义 | 区分胜利与弃守 / 超时——见交汇点(§6.3) | 线 A 唯一 P0 |
|
||||
| **cutover(切换)** | 立硬退役门:gamedef 路成功率达 80% 后,切为默认产线、二分收敛退役 factory、把"失败即拦截"的门翻开、闭合 modify 的"从源真构建"。同期补成本侧盲区(给静默回退链加 `fallbackEvents` 进轨迹)、修诊断盲区(把全归一类的失败原因扩出子码,注意 DB 列宽 32 字符约束) | 线 A 收口里程碑 |
|
||||
|
||||
prompt 同源这一步一举两得:既根除了那处 P0 级 split-brain,又坐实了三段式缓存的前缀——前缀字节一致才能命中,实测命中率有 **76% 到 93%**。这里有一条硬纪律:**split-brain 未解之前,不得拿 worker 线的数据去做 cutover 决策。**
|
||||
|
||||
### 6.2 线 B:补齐能力层
|
||||
|
||||
线 B 的四件事全部源自 OpenGame,且全部落在 SAA 图内,不引入外部主循环:
|
||||
|
||||
- **GDD 契约 + template_api(最高价值项)。** 把物理优先分类的系统提示(含易错例)和品类体系做成 classify 节点;把 GDD"六节每节硬映射下游"的契约、三铁律做成 design 节点。但有一个范式级前提必须先立:**先得有那份"LittleJS 增强发行版 + 插件库公开 API 清单"当填空靶子**,否则 hook 完整性无从约束,便宜模型必然编造 API。
|
||||
- **Debug Skill 落地为 diagnose / repair 节点 + checkpoint(检查点持久化)。** 把(签名、原因、修复)三元组账本、分组阈值升格这套机制对应到 SAA 图里。签名匹配纯算法零 LLM,便宜模型只在两处兜底,每次命中省一次 LLM 调用。种子协议里的错误码要从 OpenGame 用的 Phaser 引擎换成我们的 LittleJS 运行时和九门错误码,但结构原样可用。
|
||||
- **Template Skill 按 gameDefinition 重写,且入库前过九门质量门。** 抄它的范式,但实现整体重写(它的物理信号正则和 hook 命名死绑了 Phaser)。**更关键的是:原版入库前没有任何回验门,带病的骨架会直接沉进库;我们必须给它加一道"入库前过九门或可构建校验"的质量门**——这恰恰是我们相对它的强项,把我们最硬的东西用在它最弱的地方。另要预警原版一个硬缺口:这套进化只有单调累积、没有遗忘 / 淘汰 / 冲突消解,也没有向量检索,家族一多会退化;等品类规模上来,我们可能需要补一层真正的检索。
|
||||
- **资产线接 mmx。** asset 节点接 mmx-cli 替代那套重流水线,降级部分照多级回退思路兜底。顺带把它的确定性 bitmask 贴图(纯算法、与模型无关,用确定性算法绕开模型的空间推理)直接搬进来——这是 OpenGame 一个聪明的工程判断,几乎零成本可得。
|
||||
|
||||
### 6.3 两处交汇点:必须合并,不能各列一遍
|
||||
|
||||
线 A 和线 B 有两处在解同一个问题,必须合成一项实现:
|
||||
|
||||
- **交汇点一:品类的独立交叉校验。** 线 A 的"品类独立交叉校验"和线 B 的"物理优先分类"指向同一件事。内核是:保留 classify / design 自产骨架的生成路径,但**验收的判定依据绝不能押在同一只待验模型的分类输出上**。做法是引入一个独立于 design 的交叉校验——从题面关键词、从 player 软门的视觉反推品类,与 design 自报的品类比对,不一致即降权乃至拒发。线 B 的物理优先分类(看重力方向、视角、移动方式而非品类名,比如"Terraria 是平台跳跃不是俯视类"这类易错例)正好为这个独立校验提供高质量判定依据。**一件事,一处实现。**
|
||||
- **交汇点二:把 player 软门做厚,验收去自评。** 线 A 的"九门去自评闭环"和线 B 的"补强 VLM 语义层"是同一件事。九门那一层(机制)已领先,不动它;要补厚的是判"好不好看、是不是要的那个游戏"的 player 软门。动作是让这条软门更系统、可复现:做成独立于生成方的交叉校验,甚至一个离线评测基准,蓝本就是 OpenGame 论文那套三轴。配套把"模型靠运行时内置基元蹭过门、还是真写了交互逻辑"的占比,以及 player 自评与九门判定的交叉一致性都进轨迹,长期背离即告警——用数据积累"门的有效性"证据。
|
||||
|
||||
### 6.4 统一迁移序列
|
||||
|
||||
已有三份执行计划承接本序列:`2026-06-17-001`(后端生成主线基座)、`2026-06-18-001`(生命周期 + 广度)、`2026-06-18-002`(广度后端)。下面的阶段挂入这些计划的验收,不新建编号;标 **★** 的是关键路径,标 **[新增]** 的是 OpenGame 对照带来的、需补进对应计划的新工作项。
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
P0["阶段0<br/>零风险立桩<br/>收敛常量来源"] --> P1["阶段1<br/>线A·重构<br/>抽 TraceExtractor"]
|
||||
P1 --> P2["阶段2 ★<br/>线A·解 P0<br/>prompt 同源"]
|
||||
P2 --> P3["阶段3<br/>线A·契约先行<br/>模型外置 + 失败子码"]
|
||||
P3 --> P4["阶段4 ★<br/>质量加固·全 flag-off<br/>latch + 两交汇点"]
|
||||
P4 --> P5["阶段5 ★<br/>里程碑门·cutover<br/>达 80% 切默认产线"]
|
||||
B1["阶段B1 ★ [新增]<br/>GDD 契约 + template_api"] -.越早越好.-> P4
|
||||
B1 --> B2["阶段B2 [新增]<br/>Debug/Template Skill + 资产线"]
|
||||
B2 -.支撑.-> P5
|
||||
```
|
||||
|
||||
- **阶段 0(零风险·立桩)**:收敛 job 核心字段名、状态键集、轨迹键集的常量来源。这里要切分清楚——Java 内部键名可以收敛到常量类,而 Java 与 Python 之间的轨迹键只能靠契约和测试夹具对齐,不能并入 Java 常量类(这是一处假锚)。纯重命名,无行为变化。
|
||||
- **阶段 1(线 A·重构)**:抽 `SaaTraceExtractor`、拆 `SaaStudioNodes` 分包,调度器瘦身,零行为变更。
|
||||
- **阶段 2 ★(线 A·解 P0)**:gameDefinition prompt 约定提为版本化资产,两轨读它、CI 卡字节。关键路径:split-brain 未解前不得拿 worker 线数据做 cutover 决策。
|
||||
- **阶段 3(线 A·契约先行)**:模型外置 + CI 一致性校验;失败原因扩子码(注意列宽 32);把作业对象在调度器边界 record 化。全 additive 加 fallback。
|
||||
- **阶段 4 ★(质量加固·全 flag-off 落轨迹)**:① latch 终态语义区分胜利与弃守 / 超时(P0);② 交汇点一(品类独立交叉校验);③ 交汇点二(player 软门做厚 + 可观测化);④ fallbackEvents 入轨迹。全部默认关闭、只观测、不改放行行为。
|
||||
- **阶段 B1 ★[新增]**:GDD 契约 + template_api(LittleJS 插件库公开 API 清单),classify 与 design 节点契约化。这是约束力的靶子和最高价值杠杆,建议优先排进 `2026-06-18-001`。其中"物理优先分类"与阶段 4 交汇点一是同一处实现,不重复建。
|
||||
- **阶段 B2 [新增]**:Debug Skill 落 diagnose / repair 节点 + checkpoint;Template Skill 按 gameDefinition 重写并强制入库前过九门;asset 节点接 mmx 并搬入确定性 bitmask 贴图。可在 B1 之后并行。
|
||||
- **阶段 5 ★(里程碑门·cutover)**:gamedef 成功率达 80% 门后,切默认产线、二分退役 factory、把拦截门翻开、闭合 modify"从源真构建"。依赖阶段 2(prompt 同源)、阶段 4(质量信号可信)与 80% 达标率三个前置。
|
||||
|
||||
**关键路径是一条线:阶段 2(prompt 同源解 split-brain)→ 阶段 4(质量信号可信)→ 阶段 5(cutover)。** 阶段 0 / 1 / 3 可并行。按价值排,最该立刻动手的三件依次是:**先补 template_api 与 GDD 契约(B1,一切约束的靶子)、再把 Debug Skill 落成 diagnose / repair + checkpoint、然后把 Template Skill 按 gameDefinition 重写并加九门质量门。**
|
||||
|
||||
---
|
||||
|
||||
## 7. 我们刻意不做的
|
||||
|
||||
有三件 OpenGame 看起来在做、但我们刻意不做的事,讲清楚为什么,免得被论文卖点带偏:
|
||||
|
||||
- **不专训专用大模型(GameCoder-27B)。** OpenGame 论文把一个为游戏定制的 27B 代码模型当支柱,但它的权重、训练码、数据都不在仓里,实测代码默认接的反而是通用强模型,设一个环境变量就能换。这恰恰证明它的框架被刻意设计成与模型无关——专用模型不是跑通的必要条件。我们的路线明确是便宜通用模型 + 门兜底,质量缺口靠模板约束加 Debug Skill 补。
|
||||
- **不把 VLM 当硬门。** player 软门要做厚,但它的判定是视觉模型的概率性判断,不能用来定一次生成"算不算完成"。把软判定提成硬门,会直接撞上铁律——"完成"必须由确定性的门来定。所以 VLM 这条线只做软门、做离线基准、做交叉校验与告警,产出的是"门的有效性"证据和质量趋势,绝不进放行的确定性判定。**九门是机制地板,VLM 是质量观测,两者职责不混。**
|
||||
- **不上重型沙箱。** OpenGame 的命令执行是重型的(独占进程组、信号管控、容器沙箱,深度绑定 Node 生态),这是它"模型自由写代码再跑构建看报错"那套自主循环的物理基座。我们走图编排,build 节点职责窄得多,MVP 阶段不需要这么重的沙箱;将来真需要,Java 侧自建等价物即可,绝不移植那套 JS 适配。同理 MCP(模型上下文协议)走 SAA 自带支持,不搬 OpenGame 那套 JS 适配层——何况 OpenGame 的游戏生成能力本就不来自 MCP,而来自内置确定性工具加工程模板。
|
||||
|
||||
---
|
||||
|
||||
## 8. 三级生成与契约体系(蓝图锚点)
|
||||
|
||||
上面讲的是 MVP 阶段的生成主线现状与近期演进。从更长的产品视角看,生成能力被规划为**三级**,对应创作者梯度和三条收入线。这部分是蓝图级锚点,这里只给骨架,细节在源档:
|
||||
|
||||
| 维度 | L1 模板成游戏 | L2 模板扩玩法 | L3 全栈造游戏 |
|
||||
|---|---|---|---|
|
||||
| agent 职责面 | 填参 + 文案 + 资产变体选择 | 受控补丁面改码 + 关卡 + 生成资产 | 全内容域,多 agent 分工 |
|
||||
| 预算档(工程门) | ≤ ¥0.15 / 款全成本 | ≤ ¥5 / 款全成本 | ¥50~500 / 款(B 端定价覆盖) |
|
||||
| 时延 SLO | P75 ≤ 30s | P75 ≤ 10min | 小时级,阶段进度可见 |
|
||||
| 对应人群 | 小白(一句话) | 进阶创作者(资产 + 对话) | 专业 / B 端 |
|
||||
|
||||
> **一条重要的模板哲学拍板(2026-06-12)**:**模板 = 引擎能力插件**(碰撞 / 粒子 / 物理 / 手感等,LittleJS 二次开发件),**不含玩法 / 美术 / 关卡 / UI——这四者是 agent 生成域**。玩法模板层被废除(它是同质化的根源)。所以"玩法模板"在本项目语义里 = 品类引导框架,影响生成 prompt 与脚手架,**不是一个可执行的游戏壳**。
|
||||
|
||||
支撑这套体系的是**第 9 契约组**(生成工厂契约包,9a~9g):9a 模板协议(能力插件清单)、9b 作业与状态、9c 评估与就绪评分、9d 全链 trace、9e 代码补丁、9f 资产血缘、9g 收益归因。配套还有一个**生成控制平面**(配额 / 计费 / 背压 / 取消 / 幂等 / 补偿 / 防刷 / 降级——没有控制面的 agentic 系统会同时烧钱、压垮环境、放大滥用),以及一套**双轨灰度矩阵**(工厂路径按品类 × 人群灰度,黄金评估集通过率 ≥ 80% 且灰度 7 天错误率达标才切默认,跌幅 > 5pp 或破红线自动切回)。L2 起代码不可信,有一套七层信任边界(补丁面白名单、依赖锁定、静态安全门、构建隔离、运行取证、发布隔离、代码补丁契约)——细节都在源档,不在此展开。
|
||||
|
||||
---
|
||||
|
||||
## 9. 源档导航
|
||||
|
||||
本文档是生成引擎子树的策展层主档,把三份源档合成了一张可读的全景。要看更深的设计推演、逐条裁决与证据,从下面进去:
|
||||
|
||||
| 源档 | 回答什么 |
|
||||
|---|---|
|
||||
| [生成主线架构演进路线](../../../agent-specs/生成主线架构演进路线.md) | 生成主线"往哪走、先做什么"的单一 SoT:现状诊断、OpenGame 对标、统一演进路线(本文 §4–§7 的权威源) |
|
||||
| [生命周期项目管理 review](../../../agent-specs/2026-06-17-生成主线-游戏生命周期项目管理-review.md) | "游戏 = 长生命周期源项目、LLM as 工作室"范式的完整裁定与契约 delta(本文 §3 的权威源) |
|
||||
| [游戏生成系统总体架构 review](../../../agent-specs/2026-06-12-游戏生成系统总体架构-review.md) | 三级生成、第 9 契约组、控制平面、灰度矩阵、29 条意图覆盖矩阵(本文 §8 的权威源) |
|
||||
|
||||
> **纪律**:本档是生成引擎的策展层 SoT——读它 = 当前真相。带日期的源档是留痕层,记录设计如何演进至此;两者职责不同,不互相重复。当现行真相与某份带日期的源档冲突时,以本档与它引用的最新裁定为准。
|
||||
|
||||
---
|
||||
|
||||
> **验证状态**:本文档为架构策展文档,由三份已评审源档合成,未改任何代码。承重硬事实(V18/V20 建表、prompt registry 已存在、SAA 16 节点、九门、80% 门、¥0.15 / P75≤30s 预算时延门、76%–93% 缓存命中、OpenGame 结论锚在其真源码)均沿用源档已抽查属实的结论;品牌已统一为"绘境AI"。
|
||||
312
docs/architecture/架构/生成引擎/SAA编排.md
Normal file
312
docs/architecture/架构/生成引擎/SAA编排.md
Normal file
@ -0,0 +1,312 @@
|
||||
# SAA 编排 · 生成引擎基建设计
|
||||
|
||||
> **这是什么**:绘境AI 生成引擎的 AI 编排基建设计文档,回答"**用什么框架把一句话编排成一款游戏、为什么这样分层、有哪些不能破的约束**"。
|
||||
> **给谁看**:负责生成主线的后端工程师、架构评审、新加入参与生成模块的同事。
|
||||
> **怎么读**:先读第 1 节建立决策锚点(为什么是 SAA、它和"自治型 agent"是两种问题),再看第 2 节那张编排拓扑图建立全局;想了解硬约束读第 4 节六条不变量,想了解过渡期治理风险读第 5 节 split-brain 防线,想看清和旧编排器的职责边界读第 7 节。实现级的坑清单与逐键源码旁证不在本页,通过文末第 10 节的指针跳转。
|
||||
|
||||
这份文档讲的是生成引擎最底层的那台"编排机器"——不是讲它生成出来的游戏长什么样(那在[固定游戏架构](固定游戏架构.md)),也不是讲游戏跑在什么引擎上(那在[引擎与运行时](引擎与运行时.md)),而是讲**用什么框架、按什么拓扑、在什么约束下,把生成流程一步步驱动起来**。
|
||||
|
||||
---
|
||||
|
||||
## 1. 一句话与决策锚点
|
||||
|
||||
绘境AI 的 AI 游戏生成主线,采用 **SAA(Spring AI Alibaba,阿里在 Spring 生态里的 AI 编排框架,这里用的是 GA 正式版 v1.1.2.2)的裸 `StateGraph` 做确定性编排**。这一决策有正式编号 **HJ-AGI-002**,它演进自更早的方案 **HJ-AGI-001**(那一版主张"以 AgentScope 作为 agentic 基建";AgentScope 是另一套面向多智能体的开源框架)。
|
||||
|
||||
"裸 `StateGraph`"这个说法需要先解释:`StateGraph` 是 SAA 提供的"有向状态图"原语——你用它声明一个个**节点**和节点之间的**条件边**,描述流程怎么从一步走到下一步。"裸"指我们**直接使用这层图原语手写编排**,而不是在它上面再套一层 agent 框架去让模型自己决定下一步。
|
||||
|
||||
为什么要"裸"着用、刻意不让模型自治?因为游戏生成主线的问题形态,本质上是一条**确定性多步编排**:依次解析需求 → 生成代码 → 构建产物 → 通过一套自动验收门——每一步该干什么、下一步走哪里,都是可以预先固定的。这和"给 LLM 一堆工具、让它自由决定下一步调什么"的**自治型 agent**(如 ReactAgent 那一类)是完全两种问题。用确定性图来约束控制流,正是为了让一个普通便宜模型也能被"框"在轨道上稳定产出。
|
||||
|
||||
下面三条口径是这套架构永久锚定、不随迭代变化的定调:
|
||||
|
||||
1. **短期 = SAA-only;长期 = AgentScope 降为高端独立轨。** 生产主线只养 SAA 一套基建。AgentScope 只服务于"极高质量、宽预算、独立 App"这类远期场景,与生产主线彻底解耦,不嵌入 MVP。这一取舍回归了 HJ-AGI-001 最初"只养一套基建"的初衷。
|
||||
2. **生成逻辑藏在 job/callback 契约(契约编号 #6)之后,对外只暴露 `GenerationDispatcher` 接口。** 框架未来可以整体替换,而业务侧调用方的契约不变。同样关键的是:一次生成"完成"与否,由**九门确定性 harness**裁定,**禁止让 LLM 自评**是否通过(下一节与第 4 节会反复强调这条)。
|
||||
3. **所有模型调用统一走 new-api 网关。** new-api 是绘境AI 内部的模型成本/凭证统一网关,负责 API key 管理与用量计费。SAA 图里每个节点用到的模型 bean 都指向这一层,任何节点不得绕过它去直连大模型。
|
||||
|
||||
> 现行真相基准对齐:玩法模板未废、待建(它是"品类 prompt 模板"——给主 agent 调度子 agent 用的提示词模板,既不是代码也不是图节点;真正废掉的是早期"游戏模板 / 填参"那条线);引擎为 LittleJS(Tier1 主引擎);网关为 new-api;Java 命名空间 `com.wanxiang.huijing`;对外品牌统一为绘境AI。
|
||||
|
||||
### 为什么是 SAA-only(决策理由精炼)
|
||||
|
||||
这条决策不是拍脑袋,而是经过双评审(Codex + Opus 两个模型对抗式评审)与源码核验后定下的,核心三点:
|
||||
|
||||
- **本项目绝大多数 agentic 活就是"编排型"。** 生成主线、五资产生成、远期的用户编排(mode-C),都是确定性多步流程;只有极少数 Tier-3"自治型"场景(多 agent 开发组)才真正需要嵌入式 agent。所以主干用裸图编排,不套 ReactAgent / FlowAgent 那套给"LLM + 工具自治"用的东西。
|
||||
- **SAA 自带完整 agentic 能力。** StateGraph、ReactAgent、`ReactAgent.asNode()`(把一个自治 agent 包装成图里的一个节点)、子图、MySQL/Redis 的状态保存器(Saver)等都齐全。需要调子 agent 时,这是 SAA 原生能力——**生产侧根本不需要、也不嵌 AgentScope**。
|
||||
- **源码证伪了"把 AgentScope 2.0 嵌进来"的外部建议。** 双评审查到:SAA 里那个 `asNode()` 适配器绑定的是 `agentscope-core 1.0.9`、且只能代理单个 ReActAgent;而本地 agentscope-java 是 `2.0.0-SNAPSHOT`——把 2.0 硬塞进 1.0.9 的接缝属于未验证路径,风险不可控。因此 AgentScope 的开源贡献单独走一条轨,与生产解耦。
|
||||
|
||||
---
|
||||
|
||||
## 2. 编排拓扑图
|
||||
|
||||
下面这张图描述了游戏生成主线的完整节点拓扑。图里每个**方块**是一个 SAA 图节点(一段实打实的 Java 代码),每个**菱形**是一道条件边——注意,条件边的走向由 harness 的**真实检测结果**决定,而**不是**让 LLM 来判断。读图时抓三段:**先理解题面(render→classify→design)→ 再造出东西(generate→scaffold→asset→build)→ 最后反复验收直到合格或显式放弃(play/player/nreview → modify/repair/escalate → emit/giveup)**。
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
subgraph JOB["game-cloud 业务层(稳定接缝)"]
|
||||
CB["job/callback 契约 #6\n触发 & 回调"]
|
||||
end
|
||||
|
||||
subgraph STUDIO["SAA StateGraph · 16 节点编排图(GA v1.1.2.2)"]
|
||||
direction TB
|
||||
N_render["render\n解析创作意图 → PromptContext"]
|
||||
N_classify["classify\n判断游戏品类"]
|
||||
N_design["design\n产出 GameSpec 设计方案"]
|
||||
N_generate["generate\n生成 gameDefinition\n(声明式实体/场景/规则 + 行为JS)"]
|
||||
N_validate{"validate\nschema 合法性校验"}
|
||||
N_scaffold["scaffold\n初始化项目脚手架"]
|
||||
N_asset["asset\n生成/下载图片音效等资产"]
|
||||
N_build["build\nbuild-from-source\ngameDefinition → 装配 → engineBundle"]
|
||||
N_play{"play\n九门真玩 harness\n(CDP 驱动真浏览器)"}
|
||||
N_player["player\n软门·人工可读视觉报告"]
|
||||
N_nreview["nreview\n内容安全审核"]
|
||||
N_modify["modify\n确定性修改配置/资产"]
|
||||
N_repair["repair\n受约束 LLM 节点(重写代码)"]
|
||||
N_escalate["escalate\n升档换更强模型或报警"]
|
||||
N_emit["emit\n发布到 feed / 入库"]
|
||||
N_giveup["giveup\n超限后显式放弃本轮"]
|
||||
|
||||
N_render --> N_classify --> N_design --> N_generate
|
||||
N_generate --> N_validate
|
||||
N_validate -- "schema 不合法" --> N_repair
|
||||
N_validate -- "schema 合法" --> N_scaffold --> N_asset --> N_build
|
||||
N_build --> N_play
|
||||
N_play -- "九门通过" --> N_player --> N_nreview --> N_emit
|
||||
N_play -- "门失败" --> N_modify
|
||||
N_modify --> N_repair
|
||||
N_repair --> N_generate
|
||||
N_nreview -- "内容违规" --> N_escalate
|
||||
N_escalate --> N_giveup
|
||||
end
|
||||
|
||||
subgraph HARNESS["复用既有确定性 harness(子进程,零重写)"]
|
||||
H_build["esbuild / node\nbuild 子进程"]
|
||||
H_play["CDP 九门 play.cdp.cjs\n真浏览器驱动"]
|
||||
end
|
||||
|
||||
subgraph STORE["已部署存储(零新基建,Phase 1)"]
|
||||
MY[("MySQL\nStateGraph checkpoint")]
|
||||
RD[("Redis\nsession / 短期记忆")]
|
||||
FUTURE["Nacos · RocketMQ · Sentinel\n(future-state · MVP 不部署)"]
|
||||
end
|
||||
|
||||
CB -->|触发| N_render
|
||||
N_build -.->|shell 调用| H_build
|
||||
N_play -.->|shell 调用| H_play
|
||||
N_emit -->|handleCallback| CB
|
||||
STUDIO -.->|state checkpoint| MY
|
||||
STUDIO -.->|session 缓存| RD
|
||||
|
||||
style FUTURE stroke-dasharray: 5 5,fill:#eee
|
||||
```
|
||||
|
||||
图里有几个关键设计意图,需要在图后讲清楚:
|
||||
|
||||
**为什么 build 和 play 是 shell 子进程调用,而不是 Java 内嵌?** 这两步复用了已经验证过的一套 JavaScript harness——esbuild 打包工具和 CDP 浏览器驱动脚本(CDP = Chrome DevTools Protocol,Chrome 提供的调试协议,可以用程序去操控一个真实浏览器)。把它们保持为子进程调用,既省掉了在 JVM 里重写整条 JS 工具链的成本,也让这套 harness 能独立演进、与 SAA 图解耦。这是"零重写、复用既有 harness"原则的直接体现。
|
||||
|
||||
**"九门"(9-gate harness)到底是什么?** 它是一套在真实浏览器里自动执行的验收检测,涵盖游戏能否加载、能否渲染、能否响应输入、能否到达胜负终态等**九道门**。所有门都是确定性的代码判断(检测 DOM 状态、读 Canvas 像素、监听 JS 事件……),没有任何主观评分。只有九门**全部通过**,这款游戏才被认为"机制可玩"。这一层是整套架构最大的工程价值,也是绝不能动的地板——它是"完成"二字唯一的裁判。
|
||||
|
||||
**engineBundle 是什么?** 它是最终的打包产物:build 节点把 gameDefinition(声明式的游戏描述)和 LittleJS(绘境AI 选用的 Tier-1 游戏引擎,详见[引擎与运行时](引擎与运行时.md))装配在一起,产出一个可以直接在浏览器里运行的 bundle。
|
||||
|
||||
**为什么 MySQL/Redis 之外还画了一组虚线框?** 那是 future-state(未来态)的中间件——Nacos(服务注册/配置中心)、RocketMQ(消息队列)、Sentinel(流控熔断)。它们是 SAA / Spring Cloud Alibaba 框架自带的能力,但**基建按"消费者驱动"分期上线**:MVP 阶段只用已部署的 MySQL + Redis(Phase 1 零新基建);只有当真正出现第一个需要它们的消费者(例如 Tier-3 自治开发组,且 MVP 闭环已上线)时,才把这套控制面铺上去,绝不预先铺设。
|
||||
|
||||
---
|
||||
|
||||
## 3. 两条产线的现状与演进路径
|
||||
|
||||
图里目前实际存在**两条并行产线**,处于不同成熟度。分清它们,对排查问题和规划开发顺序都很关键——因为这张图既是"现在跑的",也是"目标态"。
|
||||
|
||||
### 3.1 现默认产线:iife/GameConfig 路
|
||||
|
||||
在这条路上,generate 节点产出的是一段 **IIFE**(Immediately Invoked Function Expression,立即执行函数表达式)格式的 `factorySrc`,build 节点直接打包这段代码字符串,**不读取结构化的源文件**。这是当前 `dispatcher=saa` 进程内派发在生产里走的路。它的端到端测试 `SaaFullGraphE2eTest` 成功率为 **60%**,线上零 prod bug,但 worker 切 SAA 的 cutover(U7 门)按门尚未切。
|
||||
|
||||
### 3.2 目标态产线:gameDefinition 真结构化 studio
|
||||
|
||||
目标态把 generate 节点改为产出**声明式 gameDefinition**——一个结构化描述对象,包含 entities(实体)、components(组件)、scenes(场景)、rules(规则),以及 behaviors(行为逻辑,每个行为带一段真实可跑的 JS)。build 节点相应改走 **build-from-source** 路径:从 gameDefinition 出发,装配成 engineBundle,而九门 harness 一行都不用改。
|
||||
|
||||
这条路线已经完成**双证验证(2026-06-18)**——所谓"双证",指它在两个相互独立的环节各自拿到了真实证据:
|
||||
|
||||
- **Spike 验证(生成段)**:便宜模型针对 `source-project.schema.json` 这份 schema 产出连贯的 gameDefinition,成功率 **92–100%**,每款游戏成本约 **¥0.011**,且 behaviors 字段里是真实可运行的逻辑 JS,不是空壳。
|
||||
- **Build 段验证(构建+验收段)**:gameDefinition → build-from-source → 真浏览器过九门;其中 deepseek-v4-flash 满分,4 款便宜模型里 3 款通过门,有截图实证。
|
||||
|
||||
至此,"改源不改包 / 可维护源项目 / LLM 作工作室"这个核心设计命题,已在**代码层面成立**。目标产线正在产线化(执行计划 `docs/plans/2026-06-18-001`,分 U1–U4 四个阶段),完成后将替换掉 iife 路。
|
||||
|
||||
> 这里有两个内部代号要点清:**接法A** 指当前这套"裸图确定性编排"——图把控制流焊死,模型只在节点内干活;它已建成约七成、是整条线的躯干。**接法B** 指未来给 generate/repair 节点装上真实工具、让它们逐步长成自治 ReactAgent 的演进方向(详见第 9 节"进行中")。先用接法A 把地基打稳,再用接法B 增量演进,避免返工——这是刻意的先后次序。
|
||||
|
||||
---
|
||||
|
||||
## 4. 六条不变量(任何阶段不得破)
|
||||
|
||||
这六条是架构红线,不随版本迭代变化。任何对生成引擎的改动,都必须在这六条约束下进行。
|
||||
|
||||
1. **单一成本/凭证层。** 模型 API key 与用量计费只有一处权威——new-api 网关(配合项目内 `newapi_cost` 表)。SAA 图里所有节点,以及未来可能引入的 AgentScope,都必须从这一层取凭证,不得硬编码、也不得另建密钥存储。
|
||||
|
||||
2. **"完成"由确定性门裁定,禁止 LLM 自评。** 九门 harness 是、且只是游戏质量的验收权威。让 LLM 自己说"我生成的游戏通过了"属于 **Goodhart 定律**陷阱(Goodhart 定律:一旦把某个度量当成目标去优化,它本身就不再是个好度量)——被优化的指标随即失效。出题的和被考的绝不能是同一只模型。
|
||||
|
||||
3. **写工具幂等。** 幂等键 = `sessionId + step`(worker 侧用 `traceId`)。图在 repair / retry 循环里反复执行同一步时,不得产生重复副作用,也不得重复计费。
|
||||
|
||||
4. **成本与迭代次数上限。** 每次生成任务都有 `max-iters`(最大重试轮次)和 `max-cost-per-run`(单次最大成本)两个天花板,超出后进入 escalate → giveup 路径,绝不无限烧钱。
|
||||
|
||||
5. **框架可换,契约不变。** 生成逻辑的边界就在 job/callback 契约 #6(`GenerationDispatcher` 接口)。未来若把 SAA 替换成别的框架,业务侧调用方不需要改一行。
|
||||
|
||||
6. **日历优先。** 完成 MVP 的硬约束是 ICP 备案、支付资质、广告接入这些**外部日历闸门**(ICP = 中国大陆网站/应用的工信部备案)。生成引擎这条技术线在它们之下,必须在这些时间盒内完成,不得因技术完美主义而拖垮整条闭环的上线。
|
||||
|
||||
---
|
||||
|
||||
## 5. split-brain(双脑)防线:两条治理铁律
|
||||
|
||||
**split-brain**(直译"裂脑",这里是绘境AI 内部对"同一件事存在两处不一致的权威"这种问题形态的代号)。在多路派发的过渡期——也就是 HTTP worker 路和 SAA 进程内路**并行存在**的这段时间——有两处已知的 split-brain 风险,各配一条铁律。
|
||||
|
||||
### 铁律一:「worker 通过九门 ≠ 准予发布」
|
||||
|
||||
SAA worker 的出口只代表这款游戏达到了"机制可玩"水准(过了九门 = succeeded)。但**发布裁决**是另一回事,它涉及三件 worker 管不到的事:
|
||||
|
||||
- **跨创意查重**:防止平台冒出大量同质化游戏。这道关是 D9 落库门;它对游戏的 title / theme 必须做 **NFKC + casefold 归一化**(NFKC 是 Unicode 的一种规范化形式,casefold 是更激进的大小写折叠)——目的是防止用字符变体(全角/半角、花式字符)绕过查重。落库门的归一化口径必须**继承**旧编排器里那套 `_dedup_gate` 的口径,防止语义漂移。
|
||||
- **对抗 P0 合规审核**:内容安全审核,不能只靠 SAA 路自己过一道 nreview 节点。
|
||||
- **金丝雀发布**:新生成的游戏须经灰度验证,而不是直接全量。
|
||||
|
||||
**红线:SAA 切生产之前,GP9 合规门必须先接管发布裁决**(GP9 是 W-G1 落库治理里那道对抗式 P0 安全门)。在 GP9 就位之前,SAA 路生成的游戏**不得**绕过合规二审、直接进入游戏信息流。
|
||||
|
||||
### 铁律二:「多派发路·回调可观测字段每路都要 set」
|
||||
|
||||
`trace_json` 和 `readiness_score` 这两个回调字段,如果只在 HTTP worker 的回调路径里赋值,那么一旦切到 SAA 进程内路,它们就会**静默丢失**——字段在数据库里恒为 NULL,但代码不报错,问题完全不可见。(这正是 split-brain 的典型表现:换一条派发路,某些字段就悄悄没了。)
|
||||
|
||||
修法是:每个派发器的回调点,都必须**字节兼容地**镜像 HTTP worker 的 `_extract_trace` 逻辑——把 snake_case 字段(如 `stage_fail`)转成 camelCase(如 `stageFail`)后再调用 `setTrace`。这里有个命门:snake → camel 的映射(`stage_fail → stageFail` 等)必须逐个对齐。同时,SAA 这条路本身没有 `guards` / `sevenGatePass` 这些字段,就**绝不伪造**它们;整体采用 additive(只增不改)+ best-effort(尽力而为)策略,并复用 `aigc.trace.enabled` 这个开关控制。
|
||||
|
||||
**已闭合**(commit `703e462c`,真库验证):task141 的 `trace_json` 非空、`readiness=74`;task145 在开关关闭时 `trace_json` 为 NULL(零字节变更);进程内路不经 HTTP、不做 HMAC 签名。
|
||||
|
||||
---
|
||||
|
||||
## 6. 落地接入要点
|
||||
|
||||
这一节给负责落地实现的工程师。架构描述不在此重复,重点是接入时**最容易踩的坑和必须遵守的约束**。
|
||||
|
||||
### 依赖配置
|
||||
|
||||
只引入三个 BOM(Bill of Materials,依赖版本管理清单,统一锁定一组相关依赖的版本):
|
||||
|
||||
- `spring-ai-bom:1.1.2`
|
||||
- `spring-ai-alibaba-bom:1.1.2.2`
|
||||
- `spring-ai-alibaba-extensions-bom:1.1.2.2`
|
||||
|
||||
再加两个直接依赖:`spring-ai-alibaba-graph-core` 和 `spring-ai-openai`。
|
||||
|
||||
**绝不要引入**这三类东西:
|
||||
|
||||
- SAA 自带的 Boot BOM——它会让项目自身的 Spring Boot 3.5.14 输给 SAA 内置的 3.5.8,造成版本冲突。
|
||||
- `RedisSaver`——它会拖进 Redisson,触发 3.x 与 4.x 的版本冲突。
|
||||
- `agent-framework` 或 `builtin-nodes`——裸图编排用不到。
|
||||
|
||||
所有依赖只加到 `game-module-aigc-server/pom.xml`,**不动根 POM**。
|
||||
|
||||
### 模型 Bean 配置
|
||||
|
||||
每个图节点对应一个 `OpenAiChatModel` bean,所有 bean **共用同一个** `OpenAiApi` 实例,仅通过 `defaultOptions.model` 区分用哪个模型;节点用 `@Qualifier` 注解取到对应 bean。`baseUrl` 必须指向 new-api 网关,而且**必须去掉 `/v1` 后缀**(内部称这步为 stripV1)——否则 new-api 的路由规则会让请求 404。
|
||||
|
||||
### Checkpoint 存储
|
||||
|
||||
`MysqlSaver` 作为**唯一权威**的 checkpoint saver(一张图只能注册一个 saver;checkpoint 就是把图执行到一半的状态存盘,以便续跑)。跑图时必须配 `RunnableConfig.threadId(traceId)` 并设 `releaseThread(false)`,才能支持长期续跑。
|
||||
|
||||
**已知坑:`saved_at` 字段没有 tiebreaker**——同一 traceId 下若存在多条 checkpoint 记录,框架无法确定哪条是"最新"。所以续跑时**必须显式传 `checkPointId`**,不能依赖框架自动选最新。
|
||||
|
||||
### 线程模型
|
||||
|
||||
`invoke()` 方法内部会阻塞(它在内部调了 `.block()`),因此图的执行**必须在后台线程池里跑**,禁止占用 Tomcat 的请求线程或 Reactor 的事件循环线程(占了会把 Web 容器拖死)。图的执行过程**不包大事务**,跑完之后再单独开一个短事务落库。
|
||||
|
||||
### 生产派发
|
||||
|
||||
`GenerationDispatcher` 接口有两个实现:
|
||||
|
||||
- `http`:HTTP worker 路,是**默认路径**,非破坏性。
|
||||
- `SaaGraphDispatcher`:SAA 进程内路,用于灰度切换。
|
||||
|
||||
代码落地有两个核心文件:`SaaStudioGraph.java`(唯一的节点布线源)和 `SaaGraphDispatcher.java`。
|
||||
|
||||
---
|
||||
|
||||
## 7. 与旧 agent-loop-v1 编排器的边界
|
||||
|
||||
**agent-loop-v1** 是绘境AI 早期那套用 Python 写的批量生成编排器。SAA 是它的**继任**,但继任的范围是有限的——分清这条边界,可以避免误删一个还在用的生产工具。
|
||||
|
||||
SAA 继任的是 **per-job worker 的躯体**:就是原来 Python `wg1/gen-worker` 负责的那条**单次任务执行链**——design → 九门验收 → player。
|
||||
|
||||
SAA **没有**继任的是 **batch 治理外壳**:跨创意查重、JSONL 账本幂等重放、八项预算闸、四条熔断、发布段金丝雀、换模型抽检。这些能力目前仍由 agent-loop-v1 提供,两者职责**不重叠**。
|
||||
|
||||
退役分两段执行:
|
||||
|
||||
- **A 段(worker 冻结,近期可执行):** form① 端到端测试通过、且对比测试确认 SAA 不劣化之后 → 冻结 Python `wg1/gen-worker`,留作对比基线。
|
||||
- **B 段(batch 治理层退役,须等替身全部就位):** 必须以下条件全部满足——① D12 控制平面 v0 上线;② D11 就绪分落库、D9 反同质化落库、9d trace 落库(这些就是上面 split-brain 铁律里说的可观测字段);③ GP9 合规门接管发布裁决;④ 新的 batch 入口能"成批驱动 `dispatcher=saa` 并裁决发布"。四者全绿后,`run_batch.py`、`judge.py`、`ledger.py` 才整体退役(移入 `_archive`,或迁为 `tools/` 后冻结)。
|
||||
|
||||
> **红线:B 段四个条件全绿之前,禁止删除 agent-loop-v1 编排器。** 它是当前唯一可用的"批跑 + 真玩取证 + 发布裁决"工具,在 merge-prod-20 和 batch-002b 中有生产实证,被 **11 处活资产**引用。judge 的核心判据(D1/D2/D7/D9,其中 runnableOk 就是九门)已以契约形式迁入 worker;ledger 的幂等/续跑能力直接退役(SAA 的 traceId + checkpoint + watchdog 已全覆盖且更强);预算闸里 per-job 的项已迁,跨批的项留在治理层。
|
||||
|
||||
---
|
||||
|
||||
## 8. 四类工作负载映射
|
||||
|
||||
不同类型的 AI 工作负载,在绘境AI 的编排基建里走不同的形态。下表(图)给出全景视图,说明"哪类活该用 SAA 的哪种能力"。
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
subgraph LOADS["四类工作负载"]
|
||||
L1["游戏生成主线\n(MVP 关键路径)"]
|
||||
L2["五资产生成\n美术 / 音乐 / 背景等"]
|
||||
L3["Tier-3 多 agent 开发组\n(远期·高阶层)"]
|
||||
L4["mode-C 用户编排\n(远期·单独规划)"]
|
||||
end
|
||||
|
||||
subgraph INFRA["编排基建"]
|
||||
I1["SAA 裸图 StateGraph\n确定性多步编排 + repair 兜底"]
|
||||
I2["SAA Sequential / Parallel Agent\n工具 / 模型调用流水"]
|
||||
I3["AgentScope asNode() 嵌入\n开放式自治·外层图卡门"]
|
||||
I4["SAA Admin\n可视化工作流面(形态待定)"]
|
||||
end
|
||||
|
||||
L1 --> I1
|
||||
L2 --> I2
|
||||
L3 --> I3
|
||||
L4 --> I4
|
||||
```
|
||||
|
||||
- **游戏生成主线**:SAA 裸图,当前重点,以编排为主、加一个 repair 节点兜底。这就是第 2 节那张拓扑图。
|
||||
- **五资产生成**(美术、音乐等辅助资产):SAA 的 Sequential / Parallel Agent,模型调用流水线形态,不需要复杂的条件分支。
|
||||
- **Tier-3 多 agent 开发组**(远期,针对极高质量需求):AgentScope 通过 `asNode()` 嵌进外层 SAA 图,自治区域**在九门门控之内**,而发布裁决在门外——这正是"自治在门内、裁决在门外"那条原则的落点。
|
||||
- **mode-C 用户编排**(远期,允许用户自己配 AI 工作流):SAA Admin,可视化工作流面,具体形态待后续规划。
|
||||
|
||||
---
|
||||
|
||||
## 9. 现状清单(done / 在飞 / 待办)
|
||||
|
||||
### 已完成
|
||||
|
||||
- **迁移 spike 全链跑通**:首款 breakout 游戏九门全过(9/9),对比测试首轮 SAA 平手或更优。
|
||||
- **form① 端到端真库通过**:隔离实例 `dispatcher=saa`,进程内九门 → handleCallback → 游戏入 feed 的完整链路打通。
|
||||
- **trace split-brain 真库闭合**(commit `703e462c` + `a09a03e9`,round3 verdict)。
|
||||
- **依赖 / 编译 / checkpoint 真恢复门全绿**(门 A–F)。
|
||||
- **16 节点 studio 全图已建并合入 dev/2.0.0**:包含固定架构图新增的 5 个节点(classify / asset / modify / escalate / nreview)、救场阶梯,以及 8 个契约冻结。`SaaFullGraphE2eTest` 提供真全图 e2e 首证,iife 路当前成功率 60%、零 prod bug。
|
||||
- **真结构化地基双证成立(2026-06-18)**:Spike 段验证 gameDefinition 产出质量 92–100%(¥0.011/款,behaviors 带真逻辑 JS);Build 段验证 gameDefinition → build-from-source → 真浏览器过九门(deepseek-v4-flash 满分,便宜模型 3/4 过门,有截图实证)。同时产出"运行时访问约定 v0"(一个受控的 `rt` 对象,统一收口 getEntity / spawn / input / 受控的 time-random / score-win-lose latch / fx 真调引擎)和 build-from-source 装配 PoC。
|
||||
|
||||
### 进行中(本 session 独家开发)
|
||||
|
||||
- **gameDefinition 真结构化 studio 产线化**(计划 `docs/plans/2026-06-18-001`,U1–U4,已合入 dev/2.0.0):U1 运行时约定 2D 适配器契约 / U2 build-from-source 产线化 / U3 让 generate 真正产出 gameDefinition / U4 模型可靠性兜底。这就是上面说的**接法A**的产线化收尾。
|
||||
- **接法B(ReAct 自治 loop)= 演进方向 = P2 / Phase-1.5 的 agent-native 轨**:给 generate / repair 节点装上真实工具(写源 / build / 跑九门 / 读错误 / 改配置资产),让它们逐步长成 ReactAgent。这里的张力——"自治会不会失控"——由九门兜底来化解:**自治在门内、发布裁决在门外**。先用接法A(已建七成、是躯干)把地基稳住,再用接法B 增量演进,不返工。(该演进任务由创始人于 2026-06-18 从 plan `001` 移出、归入本轨;`001` 的范围就是 U1–U4。)
|
||||
- **W-G1 开闸验收门**(与上并行)。
|
||||
|
||||
### 待办 follow-up
|
||||
|
||||
- **F3**:SAA 的 cost 字段目前只落 `tokens` 数,未折算为人民币金额(token ≠ ¥)。所以效率分现在取中性值 0.5 作占位。
|
||||
- **F5**:`ReadinessScorer.scoreFirstPlay` 里,嵌套的 `H_progress` 字段被 `asBool` 方法读取时恒返回 false,导致 firstPlay 分数永远卡在 0.5。这是 HTTP 路和 SAA 路**共有的既存 bug**,修复须另立 ticket、改读 `guards.H_progress.pass`;本线不动(改了会破字节兼容)。
|
||||
- **F2**:SAA 路接入 D9 去重(独立 spec 规划)。
|
||||
- **早期冷启动预热**:SAA 图的前 2–3 次 dispatch 会瞬态失败,第 4 次起进入稳态;建议在 dispatch 层加预热 / 重试逻辑。
|
||||
|
||||
> 一个容易被误读为缺陷的点:readiness 真值会随真跑质量浮动。上面 task141 的 `readiness=74` 之所以低于 HTTP 路常见的 96,是因为这一跑 repairs=5(稳定性因此打折)、cost 字段缺失(效率取中性)——这是**真值如实反映**,不是 bug。
|
||||
|
||||
---
|
||||
|
||||
## 10. 深度参考
|
||||
|
||||
本文档聚焦架构全景。以下内容不在此展开,通过指针跳转:
|
||||
|
||||
| 主题 | 位置 |
|
||||
|---|---|
|
||||
| 完整落地坑清单(HumanNode 已废 / 无 streamEvents / supervisor 未实现 / `Semaphore(1)` 串行守端口 4320·9222 等) | `.agents/skills/saa-graph-orchestration.md` |
|
||||
| API 逐键源码旁证(API 速查 / 能力矩阵 / 接入部署 / 用法范式) | `docs/agent-specs/` 下的 dossier 文档 |
|
||||
| gameDefinition 固定架构与 studio execution 规范(16 节点全图拓扑 / 8 契约 / 救场阶梯) | [`固定游戏架构.md`](固定游戏架构.md) · `docs/agent-specs/2026-06-17-固定游戏架构与SAA-agentic-studio-execution.md` |
|
||||
| 真结构化产线化执行计划(U1–U4) | `docs/plans/2026-06-18-001-feat-gen-lifecycle-and-tier1-breadth-plan.md` |
|
||||
| 游戏跑在什么引擎上 / 运行时访问约定 | [`引擎与运行时.md`](引擎与运行时.md) |
|
||||
242
docs/architecture/架构/生成引擎/SAA能力API-dossier.md
Normal file
242
docs/architecture/架构/生成引擎/SAA能力API-dossier.md
Normal file
@ -0,0 +1,242 @@
|
||||
# SAA 能力 / API / 接入 · 逐键源码证据底座
|
||||
|
||||
> **这是什么**:绘境AI 生成引擎采用 SAA(Spring AI Alibaba,阿里在 Spring 生态里的 AI 编排框架)作为编排基建——这份文档是那一决策背后的**逐键源码证据底座**。它不重复"为什么这样分层、有哪些不能破的不变量"那套架构论证(那在[SAA 编排](SAA编排.md)),而是回答更下沉的一层:**照着写代码时,该调哪些 API、本期到底哪些能力能用哪些不能、引进 game-cloud 会撞哪些依赖、按什么范式落节点、最容易踩哪几个坑——每一条都钉在 SAA 某个类的某一行源码上。**
|
||||
> **给谁看**:真正动手把 SAA 图落进 `game-module-aigc-server` 的后端工程师、做依赖冲突评审的人、复核"这条结论有没有源码支撑"的架构评审。
|
||||
> **怎么读**:这是一份**技术参考册**,不必线性通读。先扫第 0 节那五条决策级结论建立锚点,然后按需跳——要照着写代码翻第 1 节 API 速查,要判断"这个能力本期能不能用"翻第 2 节能力矩阵,要落 Maven / 解依赖冲突翻第 3 节,要照范式骨架写节点翻第 4 节,**动手前务必先过一遍第 5 节那六个坑**。
|
||||
|
||||
这份文档在生成引擎子树里的定位,是[SAA 编排](SAA编排.md)那篇架构文档的**证据附录**。编排文档讲"这台编排机器长什么样、为什么这么搭";本文档讲"那篇文档里每一条工程结论,在 SAA 源码里能不能落地、落在哪一行"。两者一上一下:看设计读编排文档,照代码写实现、或要复核某条结论的源码出处,读本文档。
|
||||
|
||||
---
|
||||
|
||||
## 调查方法与基线(先讲清证据从哪来)
|
||||
|
||||
本文档的全部结论,来自一轮**多路独立源码取证**:四个 Claude / Opus 子代理(子代理 = 并行派出去独立干活的 AI 调查员)分别从**四个视角**逐行读 SAA 源码——API 面、能力盘点、接入部署、用法范式——再彼此交叉验证,结论一致。原计划还有 Codex(另一个模型)跑第 5 路,在 51 分钟时卡死被取消;因为前四路已互相印证,这不影响任何结论。
|
||||
|
||||
所有取证都钉在同一个代码基线上,引用结论时请认准这个版本:
|
||||
|
||||
- **被调查代码库**:`/root/oss/spring-ai-alibaba`,checkout 到 tag **v1.1.2.2**(HEAD commit `7405a7d`)——这是 **GA 正式版**(GA = General Availability,正式发布版,区别于预览 / SNAPSHOT 快照版),不是预览版。
|
||||
- **底层依赖**:SAA v1.1.2.2 之下压着 **spring-ai 1.1.2**(Spring 官方的 AI 抽象层,SAA 在它之上做编排扩展)。
|
||||
- **关联文档**:本调查服务于编号 **HJ-AGI-002** 的那条决策(用 SAA 裸 `StateGraph` 做确定性编排;裁定与不变量见[SAA 编排](SAA编排.md));spike(spike = 一次性的技术验证小实验)执行稿与目标架构 review 在 `docs/agent-specs/` 留痕层。
|
||||
|
||||
> 一个口径要先点明:本文档里凡出现具体类名 / 方法名 / 文件行号,都是**真实读到的源码事实**,不是设计设想。带"⚠️"标记的是**框架已知缺陷或反直觉行为**——这些正是最容易让人想当然写错的地方,务必当心。
|
||||
|
||||
---
|
||||
|
||||
## 0. 五条决策级结论(锚点)
|
||||
|
||||
下面五条是整轮调查蒸馏出的最高层结论。它们既是后续各节的总纲,也是落地时反复要回看的定盘星。
|
||||
|
||||
**第一,我们的生成流水是确定性多步编排,因此用「裸 `StateGraph` + 自定义 `NodeAction`」,不用 agent 自治那套。** `StateGraph` 是 SAA 提供的"有向状态图"原语——你用它声明一个个节点和节点之间的条件边;`NodeAction` 是每个节点里那段实打实的业务代码(你自己实现的一个接口)。"裸着用"指我们直接拿这层图原语手写编排,而**不**套 `ReactAgent` / flow agent——那一类是给"LLM 自己挑工具、自由决定下一步"的自治场景准备的,与我们"步骤预先固定"的形态不匹配。生成里的 repair(修复重试)环,靠 `addConditionalEdges`(添加条件边)画一条回边,再配一个放在状态里的计数器来控制轮次,**不用** SAA 的 `LoopAgent`。
|
||||
|
||||
**第二,模型这一层 SAA 没有自己的封装,直接复用上游 Spring AI 的 `OpenAiChatModel` / `OpenAiApi`。** 也就是说 SAA 圈里凡调 OpenAI 兼容接口的,都是 Spring AI 原生那两个类。具体接法是:`baseUrl` 指向 **new-api**(new-api = 绘境AI 内部统一的模型成本 / 凭证网关,负责 API key 管理与用量计费),每个图节点配一个 `OpenAiChatModel` bean,这些 bean **共用同一个** `OpenAiApi` 实例、仅 `defaultOptions.model`(默认用哪个模型)不同,节点用 `@Qualifier` 注解取到对应那个。便宜模型(如 DeepSeek)结构完全一样,还有官方原生 starter(starter = Spring Boot 的一键依赖包)可用。
|
||||
|
||||
**第三,状态持久化用 `MysqlSaver` 作引擎唯一权威 saver,Redis 只作业务旁路缓存。** saver(保存器)就是把图执行到一半的状态存盘、以便续跑的组件;`MysqlSaver` 直接收一个 `DataSource`(数据源),可以把项目现成的 yudao Druid 连接池注进去(yudao 是 game-cloud 后端所基于的那套脚手架,Druid 是它用的数据库连接池)。配合 `RunnableConfig.threadId(任务id)`(把每个生成任务的 id 当线程标识),就能精确续跑。**关键约束:一张图只能注册一个 saver,注册两个会直接抛异常**——所以 MySQL 当权威、Redis 当旁路缓存,而不是两个都注册成 saver。另外必须设 `releaseThread(false)`,图才能长期续跑而不被框架回收线程。
|
||||
|
||||
**第四,把 SAA 引进 game-cloud 的依赖冲突风险是「低到中、可控」。** 只引 `graph-core`(图引擎核心)加三个 BOM(BOM = Bill of Materials,依赖版本管理清单,统一锁定一组相关依赖的版本);**绝不引 SAA 自带的 Boot BOM**(那会让项目自己的 Spring Boot 3.5.14 输给 SAA 内置的 3.5.8);**只要不用 `RedisSaver`,就避开了** Redisson(一个 Redis 的 Java 客户端)3.x 与 4.x 的版本冲突。其余依赖双方完全一致:fastjson、okhttp 版本都对齐;而且 `graph-core` 不引入任何 web 框架(只依赖 reactor-core),不会和 yudao 的 spring-mvc 抢运行栈。
|
||||
|
||||
**第五,运行形态是「进程内嵌 + 后台线程池跑图」,而不是独立 worker。** 我们抽一个 `GenerationDispatcher`(生成派发器)接口,挂在现有 `AigcTaskService.submitGenerate → completeWithVersion` 这条契约后面;它有两个实现——`HttpWorkerDispatcher`(走外部 HTTP worker)和 `SaaGraphDispatcher`(进程内跑 SAA 图),灰度切换、框架可换。图用 SAA 自带的阻塞式 `invoke()` 在**后台线程池**里跑(**禁止**占用 Tomcat 请求线程或 Reactor 事件循环线程),而且图的执行过程**不包大事务**。独立 worker 进程那条路留到 Phase 2(第二阶段)再说。
|
||||
|
||||
---
|
||||
|
||||
## 1. 关键 API(照着写代码那一层)
|
||||
|
||||
这一节是"打开 IDE 就能照抄"的 API 速查。按图怎么搭、状态怎么读写、怎么执行、模型怎么配、怎么持久化、怎么流式输出六块组织。每一行括号里的注解,是这个 API 在我们场景下的用法要点或坑。
|
||||
|
||||
**搭图(StateGraph 的骨架 API)**
|
||||
|
||||
- `new StateGraph(name, KeyStrategyFactory)` —— 新建一张图;`KeyStrategyFactory`(键策略工厂)声明状态里每个 key 在合并时用什么策略,见下一块。
|
||||
- `addNode(id, node_async(NodeAction))` —— 加一个节点;`node_async(...)` 是把你写的 `NodeAction` 包装成框架内部的异步节点。
|
||||
- `addEdge(START, ..)` / `addEdge(.., END)` —— 加一条无条件边;`START` / `END` 是框架预定义的起止虚拟节点。
|
||||
- `addConditionalEdges(from, edge_async(EdgeAction→String), Map<key,目标节点>)` —— 加条件边:`EdgeAction` 返回一个字符串 key,框架按 `Map` 把这个 key 映射到下一个节点。**生成里的 repair 回边就靠它;⚠️ 那个 `Map`(mappings)不能为空,空了会报错。**
|
||||
- `compile(CompileConfig) → CompiledGraph` —— 把声明好的图编译成可执行的 `CompiledGraph`;`CompileConfig` 里挂 saver、中断点等编译期配置。
|
||||
|
||||
**状态读写契约(最反直觉、最容易写错的一块)**
|
||||
|
||||
节点里读写状态**不是用 setter / getter**,而是:
|
||||
|
||||
- **读**:在 `NodeAction.apply(OverAllState)` 方法里,用 `state.value("k", T.class)` 按 key 取值(`OverAllState` 是贯穿整张图的那个全局状态对象)。
|
||||
- **写**:靠 `return Map.of("k", v)` —— 你**返回**一个 Map,框架按每个 key 配置的 `KeyStrategy`(键策略)去**合并**进全局状态,而不是你直接改对象。`ReplaceStrategy` = 覆盖旧值,`AppendStrategy` = 追加(比如把新消息追加进 messages 列表)。
|
||||
- ⚠️ 记住这条:**写状态 = 返回 Map,不是调 set**。习惯了命令式写法的人在这里几乎必踩。
|
||||
|
||||
**执行图**
|
||||
|
||||
- `invoke(inputs, RunnableConfig) → Optional<OverAllState>` —— 同步阻塞执行,跑完返回最终状态。它内部其实就是 `stream().last().block()`,**自带阻塞**——这正是第 3 节要强调"必须丢后台线程池跑"的原因。
|
||||
- `stream(...) → Flux<NodeOutput>` —— 流式执行,返回一个 `Flux`(reactor 的响应式流),逐个吐出节点输出。
|
||||
|
||||
**模型(上游 Spring AI 三件套,照此装配)**
|
||||
|
||||
```
|
||||
OpenAiApi.builder()
|
||||
.baseUrl(NEW_API) // 指向 new-api 网关
|
||||
.apiKey(KEY)
|
||||
.build();
|
||||
|
||||
OpenAiChatModel.builder()
|
||||
.openAiApi(api) // 共用同一个 OpenAiApi
|
||||
.defaultOptions(
|
||||
OpenAiChatOptions.builder()
|
||||
.model("deepseek-chat") // 各节点仅此处不同
|
||||
.build())
|
||||
.build();
|
||||
```
|
||||
|
||||
**持久化(saver 的装配链)**
|
||||
|
||||
- `MysqlSaver.builder().dataSource(ds).createOption(CREATE_IF_NOT_EXISTS).build()` —— MySQL 保存器,`CREATE_IF_NOT_EXISTS` 表示表不存在就自动建。
|
||||
- `RedisSaver.builder().redisson(redissonClient).build()` —— Redis 保存器(⚠️ 引它就拖进 Redisson,见第 3 节冲突 C2;我们不用它当 saver)。
|
||||
- 装配链:`SaverConfig.builder().register(saver).build()` → `CompileConfig.builder().saverConfig(..).releaseThread(false)` → `RunnableConfig.builder().threadId("..")`。`releaseThread(false)` 是长期续跑的开关,`threadId` 是续跑的定位键。
|
||||
|
||||
**流式输出(往前端推 token 增量)**
|
||||
|
||||
- `stream() → Flux<NodeOutput>` —— 拿到节点输出流。
|
||||
- 用 `instanceof StreamingOutput` 区分哪个输出是 LLM 吐出的 token chunk(增量片段),再 `.message().getText()` 取这一段增量文本。
|
||||
- ⚠️ **SAA 没有 `streamEvents` 这个 API**(有些框架有,SAA 没有)——只能走 `stream()` + `instanceof` 这条路判类型,别去找 `streamEvents`。
|
||||
|
||||
---
|
||||
|
||||
## 2. 能力盘点:本期(只有 MySQL + Redis)能用 vs 要等 Phase 2
|
||||
|
||||
这一节回答一个很实际的问题:**在只部署了 MySQL + Redis 的现状下,SAA 的哪些能力当下就能用,哪些必须等更多基建到位?** 误判这条边界,要么白白等基建、要么起了个根本起不来的重组件。下面这张图先给全景,再逐条列。
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
subgraph NOW["✅ 本期即可落 — 纯 JVM 或仅需 MySQL+Redis"]
|
||||
direction TB
|
||||
C1["图编排全集<br/>条件 / 并行 / 嵌套子图 / 循环"]
|
||||
C2["agentic ReAct + 工具调用"]
|
||||
C3["多 agent<br/>sequential / parallel / routing / conditional / loop"]
|
||||
C4["持久化 checkpoint / resume<br/>MysqlSaver(仅 MySQL) · RedisSaver(仅 Redis)<br/>均已验证零 Nacos / 零 MQ import"]
|
||||
C5["流式 · HITL 中断恢复"]
|
||||
C6["Hook ×10 / 拦截器 ×13"]
|
||||
C7["OpenAI 兼容(new-api) + DeepSeek"]
|
||||
C8["A2A 点对点 · 可观测埋点"]
|
||||
end
|
||||
subgraph LATER["⛔ 需 Phase 2 基建"]
|
||||
direction TB
|
||||
P1["完整 Admin 平台<br/>MySQL+Redis+ES+RocketMQ+Nacos 五件套全硬<br/>缺一启动失败"]
|
||||
P2["A2A-Nacos 服务发现 · config-nacos · MCP-Nacos 网关"]
|
||||
P3["sandbox(Docker 容器隔离)"]
|
||||
P4["RAG 走 ES · Prompt 线上热更(写 Nacos)"]
|
||||
end
|
||||
style NOW fill:#eafaf1
|
||||
style LATER fill:#fdedec
|
||||
```
|
||||
|
||||
**✅ 本期即可落(纯 JVM,或仅依赖 MySQL + Redis,覆盖我们的核心用例)**:
|
||||
|
||||
图编排全集(条件分支 / 并行 / 嵌套子图 / 循环都有);agentic ReAct(ReAct = Reason+Act,让模型"边推理边调工具"的经典范式);工具调用;多 agent 的全部组合(sequential 顺序 / parallel 并行 / routing 路由 / conditional 条件 / loop 循环);持久化的 checkpoint(检查点存盘)与 resume(续跑)——`MysqlSaver` 只需要 MySQL、`RedisSaver` 只需要 Redis,二者都已**逐行验证零 Nacos、零 MQ(消息队列)的 import**(Nacos = 服务注册与配置中心,MQ = 消息中间件);流式输出;HITL(Human-In-The-Loop,人工介入环节)的中断与恢复;Hook(钩子)10 个、拦截器 13 个;OpenAI 兼容接口(经 new-api)加 DeepSeek;A2A(Agent-to-Agent,智能体间通信)的点对点形态;可观测埋点。
|
||||
|
||||
**⛔ 需要 Phase 2 基建(现状起不来)**:
|
||||
|
||||
完整的 **Admin 平台**——它把 MySQL + Redis + ES(Elasticsearch,搜索 / 向量库)+ RocketMQ(消息队列)+ Nacos **五件套全部硬依赖,缺一个就启动失败**;A2A 的 Nacos 服务发现;config-nacos(配置走 Nacos);MCP-Nacos 网关(MCP = Model Context Protocol,模型上下文协议,给模型挂外部工具 / 数据的标准接口);sandbox(基于 Docker 的容器沙箱隔离);RAG(检索增强生成)走 ES;以及 Prompt 的线上热更新(它要写 Nacos)。
|
||||
|
||||
**两条最容易误判的边界,单独拎出来强调**:
|
||||
|
||||
1. **「Admin 整包」≠「有 MySQL + Redis 就够」。** Admin 是个重组件,要五件套。如果只是想用编排引擎,**千万别起 admin**,直接用 `graph-core` + `agent-framework` 的库依赖即可——这是本期能落地的关键前提。
|
||||
2. **向量库在这份 clone 里只有 ES 一个具体实现。** 想做 RAG / 向量检索,本期实质上被 ES 这一依赖绑住,属于 Phase 2。
|
||||
|
||||
---
|
||||
|
||||
## 3. 接入 game-cloud(依赖冲突解法放最前)
|
||||
|
||||
这一节给真正动手把 SAA 引进后端的人。最该先解决的是依赖冲突——所以把它放在最前。整轮调查的结论是:**真正的冲突只有两处,其余全是零冲突**。
|
||||
|
||||
### 3.1 两处真冲突及解法
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
subgraph C1["C1 · Spring Boot 版本"]
|
||||
direction TB
|
||||
C1a["项目:3.5.14<br/>(huijing-dependencies BOM 实际生效)"]
|
||||
C1b["SAA 要:3.5.8"]
|
||||
C1c["✅ 解:不引 SAA 的 Boot BOM<br/>让 3.5.14 胜<br/>(同 minor 补丁前向兼容)"]
|
||||
C1a --- C1b --- C1c
|
||||
end
|
||||
subgraph C2["C2 · Redisson 版本"]
|
||||
direction TB
|
||||
C2a["项目:4.4.0"]
|
||||
C2b["SAA graph-core:3.22.0<br/>(但 optional=true)"]
|
||||
C2c["✅ 解:不用 RedisSaver<br/>→ 零冲突"]
|
||||
C2a --- C2b --- C2c
|
||||
end
|
||||
```
|
||||
|
||||
- **C1 — Spring Boot 版本**:项目实际生效的是 **3.5.14**(由 `huijing-dependencies` 这个 BOM 锁定),SAA 想要 **3.5.8**。**解法:不引 SAA 自带的 Boot BOM,让项目的 3.5.14 胜出。** 二者是同一个 minor 版本(3.5.x)下的补丁差异,前向兼容——jackson / reactor / spring 都只差补丁号,实测安全。
|
||||
- **C2 — Redisson 版本**:项目用 **4.4.0**,SAA 的 `graph-core` 里写的是 **3.22.0**——**但它标了 `optional=true`**(可选依赖,不主动传递)。**解法:只要不用 `RedisSaver`,就零冲突。** 如果将来非用不可,需要实测 Redisson 4.x 对 `RMap` / `RBucket` / `RLock` 这三个 API 与 3.22.0 的兼容性。
|
||||
- **零冲突的部分**:fastjson(1.2.83)、okhttp(4.12.0)双方版本完全一致;`graph-core` 不引任何 web 框架,不会与 yudao 的 spring-mvc 抢栈。
|
||||
|
||||
### 3.2 Maven 怎么加(只动一个 pom)
|
||||
|
||||
所有依赖**只加到 `game-module-aigc-server/pom.xml`,不动根 POM / 根 BOM**:
|
||||
|
||||
- import **三个 BOM**:`spring-ai-bom:1.1.2` + `spring-ai-alibaba-bom:1.1.2.2` + `spring-ai-alibaba-extensions-bom:1.1.2.2`。
|
||||
- 依赖只加 `spring-ai-alibaba-graph-core`(图引擎,唯一必需的)+ 一个模型 starter(走 new-api 就用 `spring-ai-openai`)。
|
||||
|
||||
### 3.3 reactive↔blocking 桥接(防死锁的命门)
|
||||
|
||||
`CompiledGraph.invoke()` 自带阻塞(内部调了 `.block()`),所以**必须在后台 `ExecutorService`(线程池)里调它,绝不能在 Web 线程或 Reactor 线程上调**——否则就是经典的"在 reactor 线程里 block"死锁。超时控制用 `Future.get(timeout)` 从外面包一层;取消接现有的 `cancelTask` 逻辑。图的执行**不要包在大 `@Transactional` 事务里**,跑完之后单独开一个短事务落库即可。
|
||||
|
||||
### 3.4 最小验证门 E:6 步,在 mini-desktop 上跑
|
||||
|
||||
门 E(门 = gate,一道必须通过才算数的验收检查;mini-desktop 是内网那台用来跑验证的桌面机)的设计哲学是**先验最便宜的那个命题**——"SAA 这套库到底能不能干净地进 game-cloud 这个进程",而把模型、saver、job 链全部隔离在外、暂不接入。六步如下:
|
||||
|
||||
1. 加三个 BOM + `graph-core`。
|
||||
2. 跑 `mvn -pl …aigc-server -am dependency:tree` 并断言:Spring Boot 全是 3.5.14、Redisson 是 4.4.0、**没有 3.22.0 泄漏进来**、jackson 是 2.21.x。
|
||||
3. 写一个**纯 Java 节点**(不调任何 LLM)的 "hello" `StateGraph`。
|
||||
4. `mvn package` 过编译门。
|
||||
5. **单体启动门**:验证 SAA 与 yudao 的自动配置能共存,没有 `BeanCreation` 失败。
|
||||
6. 调 `invoke` 跑那一个节点,断言返回了预期 state。
|
||||
|
||||
> 门 E 的精髓:**这一步先不接模型、不接 `MysqlSaver`、不接 job 链**——只隔离验证"SAA 库能进 game-cloud 进程"这个最便宜、最该先确认的命题,把昂贵的集成验证留到后面。
|
||||
|
||||
---
|
||||
|
||||
## 4. 面向我们生成流水的用法范式(7 条)
|
||||
|
||||
这一节是"该怎么用 SAA 把我们的生成流水搭出来"的范式手册——七条,每条都对应生成主线的一个实际需求。完整的节点骨架代码在各子代理报告里,这里给的是范式要点与对应 API。
|
||||
|
||||
1. **主干 = `StateGraph` + 每步一个 `node_async(NodeAction)`。** 生成主线按 render → generate → validate → scaffold → build → play → emit 一步步串(解析意图 → 生成 → 校验 → 脚手架 → 构建 → 真玩 → 发布),每一步是图里一个节点。
|
||||
|
||||
2. **repair 环 = 条件回边 + 状态计数器。** 写法:`addConditionalEdges("validate", edge_async(s → ok ? "done" : cnt >= N ? "giveup" : "repair"), …)`——校验通过就走 done,重试超过 N 次就 giveup,否则回到 repair。轮次靠状态里的 `repair_count` 计数器控制。另有一个 `recursionLimit`(递归上限,默认 100)作**硬刹车**——注意它触发时是**优雅终止、不是抛异常**,而且它是兜底,**不替代**业务自己的 max-iters(最大迭代次数)上限。
|
||||
|
||||
3. **build / play 节点 = 自定义 `NodeAction` 里用 `ProcessBuilder` 调子进程。** build 步在节点里 `new ProcessBuilder(...)` 起一个 shell 调 node / esbuild(esbuild = 一个极快的 JS 打包工具);play 步同样起子进程调 CDP 九门 harness(CDP = Chrome DevTools Protocol,Chrome 的调试协议,可用程序操控真实浏览器;"九门 harness" = 一套在真浏览器里跑九道确定性验收检测的测试夹具,详见[验收门 W-G1](验收门-W-G1.md))。用 `mapper.readTree` 收子进程吐回的 JSON,用 `waitFor(timeout)` / `destroyForcibly` 控超时与强杀。⚠️ **别用 SAA 的 sandbox 模块**——那是给 AgentScope 远程容器用的,与我们本地子进程形态不符;节点骨架可以照抄 `LocalFilesystemBackend.java` 的第 457–507 行。
|
||||
|
||||
4. **进度推前端 = 节点返回的 Map 里塞一个 `Flux`。** 节点 `apply` 返回的 Map 里放一个 `Flux`,框架会自动在 `stream()` 上把它展开;出口侧用 `@PostMapping(produces = TEXT_EVENT_STREAM_VALUE) Flux<ServerSentEvent<String>>` 以 SSE(Server-Sent Events,服务器推送事件)往前端吐进度。
|
||||
|
||||
5. **checkpoint = MySQL 单权威 saver + `releaseThread(false)`。** Redis 作业务旁路;同一个 `threadId` 第二次 `invoke` 即自动续跑;要做 time-travel(回到历史某个状态点)用 `getStateHistory` 配 `checkPointId`。
|
||||
|
||||
6. **可选 HITL = 节点实现 `InterruptableAction`。** 在 `interrupt()` 里按状态里的 `review_required` 开关决定停下等人、还是放行(条件式,**默认自动放行**);恢复时用 `updateState(...)` + `withResume()` + `stream(null, cfg)`。⚠️ **`HumanNode` 已废弃,而且 HITL 必须配 saver 才能用**(没有 saver 中断后没法续)。
|
||||
|
||||
7. **模型 = N 个 `OpenAiChatModel` bean 全指 new-api、仅 model 不同,节点 `@Qualifier` 取。** ⚠️ 一个反直觉点:**`AGENT_MODEL_NAME` 不是"选哪个模型"的机制**——它只是节点 ID 加流式事件的前缀,别误以为改它能换模型。
|
||||
|
||||
---
|
||||
|
||||
## 5. 六个最容易踩的坑(四路交叉验证确认)
|
||||
|
||||
这一节是整份文档里**最该先读**的部分。下面六个坑,每一个都是框架的已知缺陷或反直觉行为,而且都经四路独立取证交叉确认。动手前过一遍,能省掉绝大多数返工。
|
||||
|
||||
| # | 坑(框架的反直觉 / 缺陷) | 正确做法 |
|
||||
|---|---|---|
|
||||
| 1 | **`HumanNode` 整个文件被注释掉、不可用** —— 想当然去 new 一个 `HumanNode` 会找不到。 | 用 `CompileConfig.interruptBefore` / `interruptAfter` 设中断点 + `resume()` 恢复。 |
|
||||
| 2 | **没有 `streamEvents` 这个 API** —— 别去找。 | 走 `stream() → Flux<NodeOutput>` + `instanceof StreamingOutput` 判类型取增量。 |
|
||||
| 3 | **ReAct 循环默认无上限**(上限是 `Integer.MAX_VALUE`,等于没上限)—— 用 `ReactAgent` 时不挂限制会无限烧。 | 用 `ReactAgent` 时生产环境**必须**挂 `ModelCallLimitHook` / `ToolCallLimitHook`;我们走裸 `StateGraph`,则靠状态计数器 + `recursionLimit` 兜底。 |
|
||||
| 4 | **supervisor / handoff 没真正实现**(只有枚举占位,不能用)—— 以为能直接用多 agent 的 supervisor 模式会落空。 | 用 routing(路由)模式替代。 |
|
||||
| 5 | **一张图只能注册一个 saver**(注册第二个直接抛 `IllegalStateException`)。 | MySQL 当权威 saver + Redis 当业务旁路缓存;真要双写就自己写一个双写装饰器,而不是注册两个 saver。 |
|
||||
| 6 | **Admin 整包硬依赖五件套**(MySQL + Redis + ES + RocketMQ + Nacos,缺一启动失败)—— 只想用引擎却起了 admin 会卡在启动。 | 只要引擎就别起 admin,直接用 `graph-core` + `agent-framework` 的库依赖。 |
|
||||
|
||||
---
|
||||
|
||||
## 6. 深度参考
|
||||
|
||||
本文档是决策级蒸馏——把四路源码取证的结论收成一份可照着落地的参考册。再往下两个方向的细节,通过指针跳转,不在此展开:
|
||||
|
||||
| 主题 | 位置 |
|
||||
|---|---|
|
||||
| 全量逐行证据(每个类的 `文件:行号`,含 API 速查 / 能力矩阵 / 接入部署 / 用法范式四份子代理报告原文) | 本轮四个子代理报告(`docs/agent-specs/` 留痕层) |
|
||||
| 上层架构论证(为什么是 SAA-only、16 节点编排拓扑、六条不变量、split-brain 防线、与旧编排器边界) | [SAA 编排](SAA编排.md) |
|
||||
| 落地坑清单的扩充版(`Semaphore(1)` 串行守端口 4320 / 9222、checkpoint `saved_at` 无 tiebreaker 的显式 `checkPointId` 修法等) | `.agents/skills/saa-graph-orchestration.md` |
|
||||
| 这套编排在生成主线里生成什么、生成产物长什么样 | [固定游戏架构](固定游戏架构.md) · [生成引擎主文档](README.md) |
|
||||
|
||||
---
|
||||
|
||||
> **本文档定位**:这是生成引擎子树里的**技术参考底座**——SAA 的 API / 能力 / 接入 / 坑,逐键钉在 v1.1.2.2(HEAD `7405a7d`)源码上。它是[SAA 编排](SAA编排.md)那篇架构文档的证据附录:看设计读编排篇,照代码写实现、或复核某条结论的源码出处读本篇。承重事实(基线版本、三 BOM + `graph-core`、C1/C2 两处冲突、门 E 六步、六个坑、`MysqlSaver` 单权威 + `releaseThread(false)`、`AGENT_MODEL_NAME` 非选模型机制等)均为四路独立取证交叉验证的源码事实;品牌统一为"绘境AI"。
|
||||
315
docs/architecture/架构/生成引擎/WG1基准.md
Normal file
315
docs/architecture/架构/生成引擎/WG1基准.md
Normal file
@ -0,0 +1,315 @@
|
||||
# W-G1 基准 · 20 款经典轻游戏靶集与 4 类能力缺口
|
||||
|
||||
> **这是什么**:绘境AI 生成引擎用来回答一个很实在的问题的设计文档——**"便宜的通用模型 + 我们当前这套引擎和插件库,到底能造出什么样的游戏、又造不出什么"**。它把 20 款人人都玩过的经典轻游戏铺成一张靶集,既验证生成管线确实能端到端跑通,又拿这些游戏去压测能力上限、把引擎和插件库的短板逼出来。
|
||||
> **给谁看**:负责生成主线的工程师、想知道"哪些品类现在就能放量做、哪些还得先补能力"的产品与创始人,以及做技术尽调时想看清"生成能力真实边界在哪"的架构评审。
|
||||
> **怎么读**:先读 §1 那张图,搞清楚"靶集是什么、为什么要这么排";想看每款游戏的覆盖判定读 §3 那张大表;想看四类能力缺口的诊断与证据读 §4;想看怎么分批跑读 §5。
|
||||
>
|
||||
> **W-G1 是什么**:它是"便宜模型造游戏"这条路线第一批真刀真枪的实测靶集(W = Wave 批次,G1 = Game 第一批)。这份文档是它的策展层主档,把一份调研产出整理成给人读的全景;它本身**未改任何运行时代码**,是一次只读的能力盘点。
|
||||
|
||||
生成引擎主文档([README.md](README.md))讲的是"一句话怎么变成一款游戏"那条技术主线,验收门文档([验收门-W-G1.md](验收门-W-G1.md))讲的是"那条主线怎么对真实用户安全开闸"。本文讲的是第三件事:**开闸之后,我们到底拿什么去喂这台机器、又该期待它产出什么质量**。20 款经典游戏就是那批"考题",它们的覆盖判定和暴露的缺口,直接决定了批量放量时该先做哪些品类、又该先补哪层能力。
|
||||
|
||||
---
|
||||
|
||||
## 1. 一句话与一张图:W-G1 靶集是什么、为什么这么排
|
||||
|
||||
W-G1 靶集的本质,是用 **20 款机制各异的经典轻游戏**,去给"便宜模型 + 当前引擎/插件库"这套组合做一次**能力体检**。这里要先把两个判断说清楚,它们决定了整份文档怎么读:
|
||||
|
||||
- **靶集不是为了"做出 20 款游戏",而是为了"摸清能力边界"。** 选这 20 款,是因为它们刚好铺满了轻游戏的机制谱系——从最简单的回合制点击(井字棋),到最难的连续物理与程序化生成(小行星、多球弹球)。把它们一字排开,哪一档开始崩、崩在什么能力上,就一目了然了。
|
||||
- **便宜模型是这条路线的成本前提,不是临时凑合。** 这里的"便宜模型"特指 **M3 / M2.7 / DS-V4** 这一类低成本通用模型(M3 = MiniMax 的一档模型,DS-V4 = DeepSeek V4 系列;它们都不是为游戏代码专门训练的)。整条生成主线的判断就是用便宜模型打底、靠模板约束和验收门补质量缺口(详见[生成引擎主文档](README.md) §1),所以体检也必须用便宜模型来做,而不是用强模型。
|
||||
|
||||
把这 20 款按"当前能不能造得出"分档,得到的全景是这样的:
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
subgraph 齐备["✅ 能力面齐备 · 约 12 款 · 可立即冒烟"]
|
||||
direction LR
|
||||
E1["井字棋 / Pong / Simon<br/>打地鼠 / 记忆翻牌"]
|
||||
E2["Flappy / 打砖块 / 太空侵略者<br/>Doodle Jump / 跑酷"]
|
||||
E3["扫雷 / 见缝插针 / 节奏点击"]
|
||||
end
|
||||
subgraph 半缺["🟡 半缺口 · 底层有原语但无门面 · agent 须自补层"]
|
||||
direction LR
|
||||
H1["2048 / 贪吃蛇<br/>(网格 + 文本 HUD)"]
|
||||
H2["Tetris / Match-3<br/>(网格状态机上限)"]
|
||||
H3["愤怒小鸟<br/>(拖拽手势)"]
|
||||
end
|
||||
subgraph 硬边界["❌ 真缺口 · 当前造不出 · 已主动规避"]
|
||||
X1["高速球穿透类<br/>(连续碰撞 CCD 缺失)"]
|
||||
X2["完整 Pacman 鬼 AI<br/>(寻路 A* 缺失 · 未入选)"]
|
||||
end
|
||||
齐备 -->|"批次1 验管线打通"| 结论["20 款 = 一张能力体检表<br/>暴露 4 类短板"]
|
||||
半缺 -->|"批次2/3 量化自补出错率"| 结论
|
||||
硬边界 -->|"定死引擎硬边界"| 结论
|
||||
```
|
||||
|
||||
读这张图,记住三句话就够了。**第一,约 12 款(井字棋、2048、Flappy、打砖块、贪吃蛇、Pong、扫雷、Simon、Snake 类……)当前插件和引擎已经直接覆盖**,便宜模型在现有能力面上应该就能产出可玩件——这批先拿来做冒烟,验证管线通不通。**第二,缺口集中在 4 类能力上**,它们决定了 Tetris、小行星、高速打砖块、消消乐这几款难游戏的产出质量,是本基准最核心的产出。**第三,真正"造不出"的硬边界只有两条**——高速小目标的连续碰撞、以及敌人智能寻路,后者已经在选题时主动规避掉了。
|
||||
|
||||
那 4 类能力缺口,是整份文档的重点,先在这里列出名字(§4 逐个展开):
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
subgraph G["4 类能力缺口(本基准核心产出)"]
|
||||
direction TB
|
||||
C1["① 文本 / 分数 HUD 渲染<br/>无插件门面<br/>(最高频缺口)"]
|
||||
C2["② 网格 / 棋盘状态抽象<br/>无 grid / tilemap 门面"]
|
||||
C3["③ 连续碰撞 CCD<br/>collision 仅离散 MTV<br/>(真缺口)"]
|
||||
C4["④ 通用状态机 FSM<br/>gamefeel 仅 3 个特化计时器"]
|
||||
end
|
||||
C1 --> R["决定 Tetris / 小行星<br/>高速打砖块 / Match-3<br/>的产出质量"]
|
||||
C2 --> R
|
||||
C3 --> R
|
||||
C4 --> R
|
||||
```
|
||||
|
||||
这里要钉死一个**最关键的判断,它纠正了一个直觉误区**:真正的短板不在于"造不出来",而在于"**agent 每次都要自己重新补一层**"。上面的 ①②④ 其实引擎或插件库里都有底层原语,或者本就属于"agent 写码生成"的范畴(比如引擎的 `LJS.drawText` 文本绘制函数是存在的,只是没被插件包装出来;网格状态本就该由 agent 自己管)。**但"没有插件门面"意味着每一款游戏的便宜模型都得把这个轮子重造一遍**——结果就是一致性差、token 贵、容易出错。所以本基准给出的核心建议是:**W-G1 实测之后,把高频缺口(文本 HUD、网格)补成插件门面**,而不是放任 20 个 prompt 各自硬写。
|
||||
|
||||
> 这里反复出现的两个词先解释清楚。**门面(façade)**:指插件对外暴露的那层公开 API——便宜模型只能用这层 API,看不到也碰不到引擎底层。**生成域**:指那些约定让 agent 自己写码实现、不归插件管的东西(玩法逻辑、美术、关卡、UI)。一个能力到底"算不算缺口",判据就是它需要的底层原语有没有门面或引擎透传面兜底(详见 §2.4)。
|
||||
|
||||
---
|
||||
|
||||
## 2. 事实基:当前能力面到底有什么(都带证据锚点)
|
||||
|
||||
要判断 20 款游戏能不能造出来,前提是先把"当前到底有什么能力"盘点清楚。这份盘点有一条很硬的纪律:**只认源码实际导出的东西,不认"我以为有"**。具体来说,覆盖判定的依据是三处源码——插件手写的 API 门面声明(`api.d.ts`)、插件实际的代码返回面(`impl.js`),以及宿主把引擎能力透传出来的那个函数(`host.js` 里的 `makeEngineCaps()`)。下面每一条能力都带 `文件:行号` 的证据锚点,可逐条复核。
|
||||
|
||||
> **几个专有名词先解释**。**插件库**:在 LittleJS 引擎(我们选定的轻量 H5 游戏引擎)之上加的一层能力插件,把碰撞、粒子、物理、手感等做成可复用的 API。**透传**:引擎本身能力很多,但插件墙只把其中一小部分"透"出来给 agent 用,没透出来的引擎能力就不构成 agent 的"能力词汇"。**受控面**:一组刻意收窄的底层接口(画布、输入、时钟、随机等),它是"引擎将来可替换"的边界——只要这层接口不变,底层换引擎上层无感。
|
||||
|
||||
### 2.1 插件库实际导出(7 个业务插件 + 1 个埋点插件)
|
||||
|
||||
这是 agent 能直接调用的能力主体。每个插件只暴露它"源码里真的会 return 出来"的那些函数:
|
||||
|
||||
| 插件 | 公开能力(源码实际 return 面) | 证据锚点 |
|
||||
|---|---|---|
|
||||
| **collision**(碰撞) | 离散几何相交:圆碰圆 / 圆碰矩形 / 矩形碰矩形;**凸多边形 SAT + MTV**(SAT = 分离轴定理,判两个凸多边形相不相交;MTV = 最小平移向量,告诉你怎么把它们推开)、射线命中(RayHit)、**空间哈希**(SpatialHash,用均匀网格做碰撞粗筛) | `collision/impl.js:268/295/337/395/462/500`、`SpatialHash:112-264`;api.d.ts `Manifold`/`RayHit:80/97` |
|
||||
| **physics-lite**(轻物理) | 离散运动数学:抛体 + 重力、弹簧 / 临界阻尼、带约束移动、限速 / 摩擦 | `physics-lite/impl.js:104/127/181/194/241/268/286` |
|
||||
| **particles-juice**(粒子手感) | 粒子发射器、命中停顿(hitStop)、屏幕震动、闪白、缩放脉冲、预设 | `particles-juice/impl.js:438/504/524/544/562`;api.d.ts `:204-240` |
|
||||
| **gamefeel**(手感) | 输入缓冲、郊狼时间(CoyoteTimer)、连击窗(ComboWindow)、**11 条 easing 标准缓动曲线**(纯函数,经引擎门面) | `gamefeel/impl.js: InputBuffer:232/CoyoteTimer:319/ComboWindow:390`;api.d.ts `:108/154/196` |
|
||||
| **audio-music**(音频) | 音效(blip/thud/chime)、BGM 播放 / 循环 / 停止、强度分层、加载曲目 | `audio-music/impl.js:438/455/463/467/471/481` |
|
||||
| **palette-post**(调色后处理) | 调色板映射、HSL 色彩变换、后处理(暗角 / 抖动 / 扫描线) | `palette-post/impl.js:142/210/238/62/90/392/412` |
|
||||
| **save-progress**(存档) | KV 持久化:读 / 写 / 删 / 清(命名空间隔离 + 损坏容错 + 单条上限) | `save-progress/impl.js:219/243/285/294` |
|
||||
| **runtime-probe**(取证埋点) | 取证记录 / JSONL 导出 / 哈希链校验——**非玩法能力**,专给 e2e 证据用 | `runtime-probe/impl.js:330/335/340` |
|
||||
|
||||
> **郊狼时间(coyote time)** 是个游戏手感术语:角色刚离开平台边缘的一小段时间内仍允许起跳,避免"明明踩着了却没跳起来"的挫败感。**输入缓冲(input buffer)** 则是提前一点点按键也能被记下来、等条件满足时生效。这两个是平台跳跃类游戏的"手感润滑剂"。
|
||||
|
||||
### 2.2 引擎透传真实粒度(`makeEngineCaps()`,只此三面)
|
||||
|
||||
除了上面 7 个插件,宿主还从引擎本体直接透出了三面能力。**注意:只有这三面**——引擎(LittleJS)本身远不止这点(它有文本绘制 `drawText`、贴图绘制 `drawTile`、瓦片碰撞、相机等等),但插件墙内**只透出这三面**,其余引擎能力都不构成 agent 的能力词汇:
|
||||
|
||||
| 透传面 | 真实粒度 | 证据锚点 |
|
||||
|---|---|---|
|
||||
| **particles**(粒子) | 薄包装引擎的 `ParticleEmitter`(27 个参数全映射),做了像素↔世界坐标的阻抗换算 | `host.js:213-273` |
|
||||
| **audio.synth**(音频合成) | 把音效 / 音乐合成为 PCM 样本(只合成不播放,播放层另补) | `host.js:279-316` |
|
||||
| **math**(数学) | `lerp` 线性插值 / `smoothStep` 平滑步进 + 11 条等价缓动曲线(POWER/BACK/ELASTIC 族),纯函数 | `host.js:204-209` + `engine-math.js:56-87` |
|
||||
|
||||
### 2.3 受控面 6 项(引擎可换边界)
|
||||
|
||||
这是最底层的一组接口,定义在 `api.d.ts:264-304`,是"引擎将来可替换"的边界:`getContext2d()` 取 2D 画布、`onFrame()` 帧回调、`getInput()` 归一化输入、`getAudioContext()` 取音频上下文、`time` 受控时钟、`random` 确定性随机。
|
||||
|
||||
这里有一个对触屏轻游戏特别要命的事实必须说清:**输入只有 5 类原始事件**(`api.d.ts:218`,无非是 pointer 的三态和 key 的两态),**没有手势识别**。也就是说,滑动(swipe)和拖拽(drag)这类手势,引擎不给现成的——agent 必须在生成域里自己把一串 pointer 序列(按下→移动→抬起)缓存起来、算出方向和距离来合成手势。这正是后面 2048、消消乐、愤怒小鸟会暴露的一处半缺口(§4)。
|
||||
|
||||
### 2.4 一条决定"什么算覆盖"的纲领
|
||||
|
||||
最后这条是判定的总纲,直接决定了 §3 那张表里每一格的 ✅ / 🟡 / ❌ 怎么打。技术决策里有一条终裁(`tech-decisions.md:38`):**玩法、美术、关卡、UI = agent 写码的生成域;插件 = 能力 API**。由此推出覆盖判定的口径:
|
||||
|
||||
- 某个机制所需的能力,**只要有插件门面或引擎透传面兜底,就算覆盖**;
|
||||
- 纯玩法逻辑(回合制、计分、棋盘状态机)是 agent 自写域,**不算缺口**——
|
||||
- **除非**它需要的底层原语(比如文本渲染、网格抽象)根本没有门面。那才是真正要补的地方。
|
||||
|
||||
换句话说:本基准从来不把"agent 要写代码"当缺口(那本就是 agent 的活),它只把"agent 想写代码却连底层原语都没有门面可调"当缺口。
|
||||
|
||||
---
|
||||
|
||||
## 3. 20 款经典轻游戏靶集表
|
||||
|
||||
下面这张表是靶集的主体。每一款游戏给出它的核心机制、所需能力、当前覆盖判定、工程难度档,以及一句话生成种子(就是喂给便宜模型的那句话)。
|
||||
|
||||
覆盖判定的三档含义是:**✅** 表示插件或引擎透传面已覆盖;**🟡** 表示引擎有底层原语、或属生成域但没有门面(agent 得自己补层,如果高频就建议补门面);**❌** 表示真缺口(没门面、且 agent 自写成本高 / 易错)。难度档则是:**易** = 单机制 + 静态或网格;**中** = 2-3 机制叠加 + 实时;**难** = 多机制 + 连续物理 / 程序化 / 高频碰撞。
|
||||
|
||||
| # | 游戏 | 核心机制 | 当前覆盖判定 | 难度 | 一句话生成种子 |
|
||||
|---|---|---|---|---|---|
|
||||
| 1 | **井字棋** | 3×3 落子 / 胜负判定 | 🟡 网格 + 文本🟡、输入✅、AI = 纯逻辑 | 易 | 3×3 棋盘点击落子 X,AI 落 O,三连判胜 |
|
||||
| 2 | **2048** | 网格滑动合并 / 数字 | 🟡 网格 + 文本🟡(高频缺口)、swipe = 合成、合并 = 纯逻辑 | 中 | 4×4 数字方块,滑动合并同值,生成新块,显分 |
|
||||
| 3 | **贪吃蛇** | 网格移动 / 自增长 / 自碰撞 | 🟡 网格 = agent 自管、输入✅、碰撞 = 网格逻辑、计分文本🟡 | 易 | 蛇沿网格吃食物变长,撞墙 / 撞自己结束 |
|
||||
| 4 | **Flappy Bird** | 重力跳跃 / 管道避障 / 无尽 | ✅ physics 抛体 `:104`、collision `aabbVsAabb:337`、输入✅;文本🟡 | 中 | 点击让小鸟上跳,重力下落,穿随机高度管道 |
|
||||
| 5 | **打砖块 Breakout** | 弹球反弹 / 挡板 / 砖块网格 | ✅ collision `circleVsAabb:295` + MTV 反弹、physics 限速;**高速球🟡** | 中 | 挡板接弹球击碎砖阵,球随挡板位置改反弹角 |
|
||||
| 6 | **Pong** | 双挡板 / 弹球 / 简单 AI | ✅ collision / physics / 输入✅;AI = 纯逻辑 | 易 | 左玩家右 AI 各控挡板,漏球对方得分 |
|
||||
| 7 | **扫雷** | 网格揭示 / 数字提示 / 标记 | 🟡 网格 + 文本🟡、输入✅、洪水填充 = 纯逻辑 | 中 | 点击揭示显周围雷数,右键标记,踩雷结束 |
|
||||
| 8 | **Simon 记忆** | 序列记忆 / 颜色按钮 / 渐进 | ✅ audio `playSfx:481`、gamefeel 计时、输入✅;序列 = 纯逻辑 | 易 | 四色按钮按序闪烁播音,玩家复现,逐轮加长 |
|
||||
| 9 | **记忆翻牌** | 翻牌配对 / 网格 | ✅ easing `:205` / 输入✅;网格🟡、图案 = agent 绘 | 易 | 牌面朝下,翻两张配对,全配对获胜 |
|
||||
| 10 | **太空侵略者** | 波次敌阵 / 射击 / 碰撞 | ✅ collision AABB + SpatialHash `:112`、particles 爆炸;文本🟡 | 中 | 炮台左右移动射击,敌阵整体平移下压,清屏过关 |
|
||||
| 11 | **俄罗斯方块 Tetris** | 下落方块 / 网格堆叠 / 消行 | 🟡 网格堆叠 = agent 自管(**高频痛点**)、输入✅、文本🟡 | 难 | 七种方块下落,旋转堆叠,满行消除,逐级加速 |
|
||||
| 12 | **小行星 Asteroids** | 惯性飞船 / 任意角度 / 程序化碎裂 | ✅ collision `satVsPolygon:395` + RayHit、physics `applyFriction:286`;**高速子弹 CCD❌** | 难 | 飞船惯性漂移射击,大陨石击中裂小块,屏幕环绕 |
|
||||
| 13 | **消消乐 Match-3** | 网格交换 / 三连消除 / 下落填充 | 🟡 网格 + 文本🟡(**高频痛点**)、swipe = 合成、消除 = 逻辑、easing✅ | 难 | 8×8 宝石网格,交换凑三连消除,上方填充连锁 |
|
||||
| 14 | **跳跃者 Doodle Jump** | 垂直无尽 / 平台跳 / 重力 | ✅ physics 抛体 `:104`、collision AABB、coyote `:319`;相机 = 受控面、文本🟡 | 中 | 持续上跳踩平台,左右移动,平台程序化生成 |
|
||||
| 15 | **愤怒小鸟(简版)** | 抛射弹道 / 拖拽蓄力 / 碰撞倒塌 | 🟡 physics 抛体✅ / 弹簧✅、collision MTV✅;**拖拽 = agent 合成 pointer 链** | 难 | 拖拽弹弓蓄力发射,抛物线击中结构使其倒塌 |
|
||||
| 16 | **跑酷 Runner** | 横向无尽 / 跳跃滑铲 / 障碍 | ✅ physics 跳、collision AABB、buffer `:232` + coyote;swipe = 合成、文本🟡 | 中 | 自动向右跑,点击跳跃避障,距离计分,逐渐加速 |
|
||||
| 17 | **打地鼠** | 随机出现 / 限时 tap / 计分 | ✅ random✅、collision 点 / 圆判、particles 命中;文本🟡 | 易 | 地鼠随机洞口冒出,限时点中得分,超时消失 |
|
||||
| 18 | **见缝插针** | 旋转 / 时机 tap / 碰撞检测 | ✅ math 角度、collision `raycastCircle:462`、particles;文本🟡 | 中 | 中心圆持续转,点击射针,针不能相撞,逐根加针 |
|
||||
| 19 | **节奏点击** | 时间窗判定 / 节奏 / 连击 | ✅ `ComboWindow:390` + `InputBuffer`、audio、easing;文本🟡 | 易 | 音符落判定线时点击,perfect / good 分级,连击加成 |
|
||||
| 20 | **弹球变体(多球)** | 多球 / 道具掉落 / 物理反弹 | ✅ collision 圆 + AABB、physics 限速、particles `shakeScreen:524` / `flashScreen:544`;**高速多球 CCD🟡** | 难 | 挡板接多球击砖,砖落道具(增球 / 扩板),命中屏震闪白 |
|
||||
|
||||
> 表里反复出现的 **AABB**,是 axis-aligned bounding box(轴对齐包围盒)的缩写,指那种边沿和坐标轴对齐的矩形碰撞框——绝大多数 2D 碰撞都用它,因为判定最快。
|
||||
|
||||
---
|
||||
|
||||
## 4. 四类能力缺口诊断(本基准的核心价值)
|
||||
|
||||
这一节是 W-G1 真正要回答的东西。20 款游戏合计考到的能力维度里,绝大多数都有插件或透传面兜着(✅),它们能立即冒烟;真正暴露问题的,是下面这 4 类(再加 2 条已规避的硬边界)。**缺口判定全部用"源码证伪法"**——不是按名字臆断"应该有",而是 `grep` 搜关键原语全空、再加上插件 `impl.js` 自述的能力边界,两头夹住才下结论。
|
||||
|
||||
先用一张图把它们的性质分清楚:**❌ 是"真造不出"的硬缺口,🟡 是"造得出但每款重造轮子"的半缺口**。两者的应对动作完全不同:
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
subgraph 真缺口["❌ 真缺口:无门面 + agent 自写成本高/易错"]
|
||||
CCD["① 连续碰撞 CCD(swept)<br/>暴露:#5 高速球 / #12 子弹 / #20 多球<br/>后果:高速小球穿砖穿墙(tunneling)"]
|
||||
PATH["寻路 A* / 导航<br/>已主动规避(完整 Pacman 鬼 AI 未入选)<br/>→ 明确的引擎硬边界"]
|
||||
end
|
||||
subgraph 半缺口["🟡 半缺口:底层有原语/属生成域,但无门面 → 每款重造轮子"]
|
||||
TEXT["② 文本 / 分数 HUD 渲染<br/>暴露:几乎全部(~15/20 款)<br/>→ 最高频缺口,W-G1 后第一优先补门面"]
|
||||
GRID["③ 网格 / 棋盘状态抽象<br/>暴露:#1/2/3/7/9/11/13<br/>→ 2048/Tetris/Match-3 核心难度"]
|
||||
GEST["手势识别(swipe / 拖拽)<br/>暴露:#2/13 swipe / #15 拖拽 / #16<br/>→ 触屏轻游戏刚需,宜并入 gamefeel"]
|
||||
FSM["④ 通用状态机 FSM<br/>暴露:#8 Simon / #11 Tetris / 回合制类<br/>→ 属生成域,优先级最低"]
|
||||
end
|
||||
真缺口 -->|"等批次3实测再拍是否补门面"| DECIDE["决策:补哪个门面<br/>预判优先级<br/>文本 > 网格 > swipe/拖拽 > CCD swept"]
|
||||
半缺口 -->|"量化便宜模型自写出错率"| DECIDE
|
||||
```
|
||||
|
||||
### 4.1 真缺口之一:连续碰撞 CCD(高速小目标会穿透)
|
||||
|
||||
**CCD**(Continuous Collision Detection,连续碰撞检测;也叫 swept,扫掠碰撞)指的是这样一种能力:当一个物体一帧之内移动得太快、跨度超过了目标的厚度时,普通的"逐帧看现在有没有重叠"会漏判——物体在两帧之间就"穿"过去了(术语叫 tunneling,隧穿)。CCD 的做法是检查整段移动轨迹有没有掠过目标,而不只看落点。
|
||||
|
||||
- **性质**:我们的 collision 插件**只有离散的 MTV / 穿透判定**(`circleVsAabb:295`),没有 swept / toi(toi = time of impact,碰撞时刻)。`grep` 搜 `swept|ccd|continuous|toi|tunnel` 全空,`impl.js:21` 也自述能力止于"MTV / 穿透 / RayHit"。
|
||||
- **后果**:**高速小球会穿砖、穿墙**,便宜模型多半会产出"球偶尔卡进砖里 / 直接穿透"的 bug 件。暴露这个缺口的是 **#5 高速打砖块、#12 小行星子弹、#20 多球弹球**。
|
||||
- **绕法与建议**:agent 可以用射线 `raycastAabb:500` 沿速度向量手做一个粗糙的 toi(可行,但每款都得重写、容易错)。如果实测发现高频,就给 collision 补一个 swept 门面。**是否本期补,建议等批次 3 实测数据再拍**,不预先投入。
|
||||
|
||||
### 4.2 真缺口之二:寻路 A* / 导航(已主动规避)
|
||||
|
||||
- **性质**:`grep` 搜 `astar|pathfind|navmesh` 全空;那个 SpatialHash 只做碰撞粗筛,**不是寻路**。
|
||||
- **结论**:**任何需要敌人智能寻路的品类,当前都造不出**——这是引擎和插件库一条明确的边界。正因为如此,本基准在选题时就**主动规避**了完整的 Pacman(吃豆人鬼魂寻路)。真要做,得新增一个寻路插件。
|
||||
|
||||
### 4.3 半缺口之一:文本 / 分数 HUD 渲染(最高频)
|
||||
|
||||
**HUD**(Heads-Up Display,抬头显示)指叠在游戏画面上的那层信息——分数、生命、计时之类。这是 20 款里**最高频**的能力需求,却恰恰没有门面:
|
||||
|
||||
- **性质**:没有任何文本渲染门面(`grep` 搜 `drawText|renderText` 全空)。引擎的 `LJS.drawText` 是存在的,但没经插件透出,`makeEngineCaps()` 那三面也不含文本(`host.js:210-317`)。
|
||||
- **后果**:每个游戏的便宜模型都得自己去 `ctx.getContext2d().fillText` 手画 HUD——结果就是风格各异、字体和对齐到处出错。
|
||||
- **判定**:**这是最高频缺口**,20 款里约 15 款需要文本。所以**强烈建议补一个 text / HUD 门面插件,作为 W-G1 实测后的第一优先**。
|
||||
|
||||
### 4.4 半缺口之二:网格 / 棋盘状态抽象
|
||||
|
||||
- **性质**:没有 grid / tilemap 门面(`grep` 搜 `tilemap|gridMap` 全空)。网格状态本属 agent 生成域,但"坐标↔索引转换、邻接查询、遍历、合并"这些操作每款都得重写。
|
||||
- **后果**:2048、Tetris、Match-3 的网格状态机正是它们的核心难度所在,便宜模型自管很容易出"合并方向错 / 消行错 / 连锁错"的 bug。暴露这个缺口的是 **#1/2/3/7/9/11/13**。
|
||||
- **建议**:W-G1 重点观察便宜模型自写网格的出错率;如果高,就补一个 grid 工具门面。
|
||||
|
||||
### 4.5 半缺口之三:手势识别(swipe / 拖拽)
|
||||
|
||||
- **性质**:受控面 `getInput` 只给 5 类原始事件(`api.d.ts:218`),没有 swipe / drag 的合成。
|
||||
- **后果**:agent 必须自己缓存 pointerdown→move→up 这串序列、算出方向和距离;愤怒小鸟那种拖拽蓄力尤其容易错。暴露这个缺口的是 **#2 / #13(swipe)、#15(拖拽)、#16**。
|
||||
- **建议**:swipe / drag 是触屏轻游戏的刚需,可以补进 gamefeel 插件——它本就管输入缓冲,手势识别归它最自然。
|
||||
|
||||
### 4.6 半缺口之四:通用状态机 FSM
|
||||
|
||||
**FSM**(Finite State Machine,有限状态机)指那种"在若干个明确状态之间按规则切换"的逻辑结构,序列记忆、关卡阶段、回合流转都用得上。
|
||||
|
||||
- **性质**:gamefeel 只有 3 个特化计时器,没有通用 FSM 原语。
|
||||
- **后果**:多状态玩法自己写状态切换容易乱。暴露这个缺口的是 **#8 Simon、#11 Tetris** 及回合制类。
|
||||
- **判定**:它属生成域,**优先级低于文本 / 网格**——因为纯逻辑这块,便宜模型相对擅长。
|
||||
|
||||
### 4.7 一句话审计结论
|
||||
|
||||
把上面六条合起来,审计结论是这样的:**20 款里约 12 款能力面齐备(✅),可以立即冒烟;余下 8 款集中暴露 4 类短板。最痛的不是"造不出",而是"文本 HUD + 网格状态"这两个高频能力没有门面——便宜模型 20 个 prompt 各造一遍轮子,在一致性、token、出错率三个维度上三重受损。真正"造不出"的硬边界只有两条:连续碰撞 CCD(高速穿透)与寻路(已规避)。**
|
||||
|
||||
下面这张表把"谁兜每一类能力、覆盖状态如何"汇总成一览(行号为证据锚点):
|
||||
|
||||
| 能力维度 | 由谁兜 | 覆盖状态 |
|
||||
|---|---|---|
|
||||
| 离散几何碰撞(圆 / AABB / 多边形 MTV) | collision `:268-431` | ✅ |
|
||||
| 射线命中 / 空间粗筛 | collision `:462` / `SpatialHash:112` | ✅ |
|
||||
| 抛体 / 重力 / 摩擦 / 限速 / 弹簧 | physics-lite `:104/286/194` | ✅ |
|
||||
| 输入缓冲 / 郊狼 / 连击窗 / easing 11 条 | gamefeel `:232/319/390/205` | ✅ |
|
||||
| 粒子 / 屏震 / 闪白 / 命中停顿 | particles-juice `:438/504/524` + 透传 | ✅ |
|
||||
| 音效 / BGM / 强度分层 | audio-music `:455-481` + 透传 | ✅ |
|
||||
| tap / 方向键输入 · 确定性随机 | 受控面 `getInput:218` / `random` | ✅ |
|
||||
| **文本 / 分数 HUD 渲染** | **无插件门面**(引擎 drawText 未透出) | **🟡 缺门面(最高频)** |
|
||||
| **网格 / 棋盘状态抽象** | **无 grid / tilemap 门面** | **🟡 缺门面** |
|
||||
| **连续碰撞 CCD(swept)** | **collision 仅离散 MTV** | **❌ 真缺口** |
|
||||
| **手势识别(swipe / 拖拽)** | **无门面**(agent 合成 pointer 序列) | **🟡 自合成** |
|
||||
| **通用状态机 FSM** | **无**(gamefeel 仅 3 特化计时器) | **🟡 agent 自写** |
|
||||
| **寻路 A* / 导航** | **无**(grep astar/pathfind 全空) | **❌ 硬边界(已规避)** |
|
||||
|
||||
---
|
||||
|
||||
## 5. 分批分档执行建议
|
||||
|
||||
有了上面的判定,W-G1 就不该 20 款一拥而上,而应**先用能力面齐备的易档验通管线(冒烟),再用中 / 难档加缺口款去压测能力上限、量化便宜模型在缺口处的产出质量**。据此分成三批,逐批加难:
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
B1["批次1 · 冒烟批<br/>6 款 · 全 ✅ · 难度易<br/>Pong / Simon / 打地鼠<br/>记忆翻牌 / 贪吃蛇 / 节奏点击"]
|
||||
B2["批次2 · 主力批<br/>8 款 · ✅ 为主 · 难度中<br/>Flappy / 打砖块 / 太空侵略者<br/>Doodle Jump / 跑酷 / 扫雷<br/>见缝插针 / 井字棋"]
|
||||
B3["批次3 · 压测批<br/>6 款 · 含 ❌ / 难 🟡 · 难度难<br/>Tetris / Match-3 / 小行星<br/>愤怒小鸟 / 多球弹球 / 2048"]
|
||||
B1 -->|"验管线零阻塞<br/>≥4/6 过好玩基线"| B2
|
||||
B2 -->|"量化文本/网格/swipe<br/>三个高频🟡出错率<br/>≥6/8 可玩"| B3
|
||||
B3 -->|"不强求高通过率<br/>重点产缺口实证报告"| OUT["喂决策:<br/>是否补 text/grid/swipe/swept 门面"]
|
||||
|
||||
style B1 fill:#a9dfbf
|
||||
style B2 fill:#f9e79f
|
||||
style B3 fill:#f5b7b1
|
||||
```
|
||||
|
||||
### 5.1 批次 1 · 冒烟批(验管线打通)— 6 款 · 全 ✅ · 难度易
|
||||
|
||||
入选:**#6 Pong / #8 Simon / #17 打地鼠 / #9 记忆翻牌 / #3 贪吃蛇 / #19 节奏点击**。
|
||||
|
||||
理由是这 6 款能力面 100% 齐备、机制单一。这一批的目标不是出多好的游戏,而是**证明"便宜模型 + 插件库 + new-api 网关 + harness 门"这条链端到端能跑通**、能产出可玩件。(new-api = 绘境AI 内部统一管理模型 key 与计费的网关;harness = 把生成产物放进真实运行环境自动检验的测试夹具。)验收标准:**管线零阻塞 + 至少 4/6 款通过好玩基线评估门**。
|
||||
|
||||
### 5.2 批次 2 · 主力批(验常见机制覆盖)— 8 款 · ✅ 为主 · 难度中
|
||||
|
||||
入选:**#4 Flappy / #5 打砖块 / #10 太空侵略者 / #14 Doodle Jump / #16 跑酷 / #7 扫雷 / #18 见缝插针 / #1 井字棋**。
|
||||
|
||||
理由是这一批覆盖了物理、碰撞、波次、程序化、网格这些主流机制,并带少量 🟡(文本 / 网格 / swipe)。它的核心产出是一组**决策数据**:**量化文本 HUD、网格、swipe 这三个高频 🟡 缺口的便宜模型自写出错率**——这正是"到底要不要补门面"那个决策的依据。验收标准:**至少 6/8 款可玩**;并逐款记录 agent 在 🟡 缺口处是怎么补层的、出了哪些 bug。
|
||||
|
||||
### 5.3 批次 3 · 压测批(测能力上限 + 暴露缺口)— 6 款 · 含 ❌ / 难 🟡 · 难度难
|
||||
|
||||
入选:**#11 Tetris / #13 Match-3 / #12 小行星 / #15 愤怒小鸟 / #20 多球弹球 / #2 2048**。
|
||||
|
||||
理由是每款都压一到多个短板——Tetris / Match-3 / 2048 压**网格状态机上限**,小行星 / 多球弹球压**CCD 缺口**,愤怒小鸟压**拖拽手势缺口**。它的核心产出是**证实 §4 的缺口判定**:便宜模型在 CCD / 网格 / 拖拽处的产出质量到底如何(会不会穿透?消行对不对?蓄力准不准?),给创始人一张"引擎短板实测画像"。验收标准比较特殊:**不强求高通过率**(本批就是测上限的);**重点是产出缺口实证报告**,去喂"是否补 text / grid / swipe / swept 门面"那个决策。
|
||||
|
||||
### 5.4 跨批观测项(W-G1 真正要回答的三个问题)
|
||||
|
||||
不管哪一批,W-G1 全程要盯着三个问题——它们才是这次实测真正的产出:
|
||||
|
||||
1. **便宜模型在三档的产出质量梯度**:M3 / M2.7 / DS-V4 从易到难,哪一档开始崩?这定下能力天花板。
|
||||
2. **四类短板各拖垮多少款**:文本 / 网格 / CCD / 拖拽各自影响几款,据此排补门面的优先级。**预判是:文本 HUD > 网格 > swipe / 拖拽 > CCD swept**。
|
||||
3. **harness 门的判定有效性**:那套门能不能稳定逮住"穿透 / 消行错 / 不可玩"?门兜底是便宜模型路线的命门——门不硬,整条路线就立不住。
|
||||
|
||||
---
|
||||
|
||||
## 6. 风险与假设(诚实标注)
|
||||
|
||||
最后把这份基准的证据强度和未决项诚实标清楚,免得把推断当成实测、把假设当成结论:
|
||||
|
||||
- **[已验证]** 全部覆盖判定都基于源码导出(`api.d.ts` + `impl.js` 的 return 面 + `host.js` 的 `makeEngineCaps()`),证据锚点见 §2–§4 各处的 `文件:行号`。缺口判定用的是"源码证伪法"(关键原语 `grep` 全空 + `impl.js` 自述能力边界),不是按预期名字臆断。
|
||||
- **[推断]** "便宜模型能产出 ✅ 款的可玩件"是**推断,不是实测**——这恰恰是 W-G1 要验的。本基准只确证了"能力面齐备",不预断模型的产出质量。
|
||||
- **[推断]** 难度档(易 / 中 / 难)是按"机制数 × 实时性 × 缺口数"做的工程推断,不是用户体感难度。
|
||||
- **[假设]** 好玩基线的具体判据未在本次检索中定位到原文,验收门的精确判据需在 W-G1 开工前从评估门 spec 取齐。
|
||||
- **[未决]** 20 款的最终取舍可由创始人按"想优先验证的机制谱系"调整。CCD 与寻路这两条硬边界是否本期补门面,**建议等批次 3 实测数据再拍**,不预先投入。
|
||||
|
||||
---
|
||||
|
||||
## 7. 源档导航
|
||||
|
||||
本文档是 W-G1 基准的策展层主档,把一份只读调研产出整理成了这份给人读的全景。要看更深的逐款分析、能力盘点的完整证据链与决策史,从下面进去:
|
||||
|
||||
| 去处 | 回答什么 |
|
||||
|---|---|
|
||||
| `.agents/skills/cheap-model-game-generation.md` | 便宜模型造游戏的 worker loop、九门真玩 harness、design-agent 自产 gatespec、成本与模型选择、5 个坑(深落地权威源) |
|
||||
| `.agents/skills/game-e2e-cdp-harness.md` | Canvas 游戏 e2e 证据 harness:编排形态、driver 六规则、ship 红线、四件套证据(W-G1 复用) |
|
||||
| [生成引擎主文档](README.md) | "一句话怎么变成一款游戏"的生成主线全景(本文的上游) |
|
||||
| [验收门-W-G1](验收门-W-G1.md) | 这条主线"对外开闸"要先架的 6 道门 + 两道开闸前置验证 |
|
||||
| [引擎与运行时](引擎与运行时.md) | LittleJS 增强发行版、插件库分层、运行时沙箱与边界模型 |
|
||||
|
||||
> **纪律**:本档是 W-G1 基准的策展层 SoT——读它 = 当前真相。逐款落地与证据链在 `.agents/skills/`,决策史在 git 与带日期的源档里;两者职责不同,不互相重复。当现行真相与某份带日期的源档冲突时,以本档与它引用的最新裁定为准。
|
||||
|
||||
---
|
||||
|
||||
> **验证状态**:本文档为架构策展文档,由 W-G1 基准调研产出(只读盘点,未改任何运行时代码)改写而成。承重硬事实——约 12 款 ✅ 可立即冒烟、4 类能力缺口(文本 HUD / 网格 / CCD / FSM)、2 条硬边界(CCD swept 与寻路 A*)、7 插件 + 引擎三透传面 + 受控面 6 项、各能力的 `文件:行号` 证据锚点、三批分档(冒烟 6 款 / 主力 8 款 / 压测 6 款)与各自验收口径、补门面优先级预判(文本 > 网格 > swipe / 拖拽 > CCD swept)、源码证伪法纪律——均沿用源档已抽查属实的结论;品牌统一为"绘境AI",无旧名残留。
|
||||
243
docs/architecture/架构/生成引擎/prompt治理.md
Normal file
243
docs/architecture/架构/生成引擎/prompt治理.md
Normal file
@ -0,0 +1,243 @@
|
||||
# Prompt 治理 · 生成引擎设计文档
|
||||
|
||||
> **这是什么**:绘境AI 生成引擎里「Prompt 治理」这一子系统的设计文档,回答"**为什么把 prompt 当成一份正式契约来管、它长什么样、改一条 prompt 要过哪些关、人在什么环节介入**"。
|
||||
> **给谁看**:负责生成主线的后端工程师、写 prompt 的工位负责人、做架构评审的人,以及想搞清楚"生成质量为什么可控、可回归"的新同事。
|
||||
> **怎么读**:先读 §1 建立"prompt 为什么要当契约管"的整体认知,再看 §2 的数据流图理解它在生成链路里的位置;§4 是这套体系的心脏(改一条 prompt 的四道关),§6 讲人怎么介入;想要落地细节从 §3、§5 看接口与目录结构。
|
||||
|
||||
生成引擎是绘境AI 护城河的关键路径,而 prompt 是这条路径上**最容易失控的一环**。模型每天在变、链路上有十几个地方会调用它、一句话改动就可能让生成质量悄悄塌掉。这份文档讲的就是:如何把这堆散落、易变、改了没人知道的文本,收成一份**可版本化、改动可自动回归、人能在关键节点把关**的工程资产。
|
||||
|
||||
---
|
||||
|
||||
## 1. 一句话与核心理念:Prompt 是第 8 类契约
|
||||
|
||||
绘境AI 把所有 prompt 统一收进 git 仓库的 `contracts/prompts/` 目录,作为整个项目的**单一事实源**(single source of truth,意为"这件事只有一个权威出处,别处都不算数")。这个决策有一个明确的定位——**Prompt 是项目的第 8 类契约资产**。
|
||||
|
||||
要理解这句话的份量,得先解释"契约"在本项目里是什么。绘境AI 在开工第一天(内部称 Day-0)就锁定了 7 类跨端契约:API 接口定义、数据库迁移脚本、客户端 SDK、游戏产物包格式、事件埋点、生成任务的 job/callback 协议、广告位定义。它们的共同特征是:**多个团队都依赖它、谁都不能私自改、改了必须走评审**。把 prompt 提升为"第 8 类契约",就是宣布:**prompt 和数据库 schema、API 定义是同一个等级的东西,不再是某个工程师藏在代码里、想改就改的字符串。**
|
||||
|
||||
为什么必须这么管?因为生成主线是一条多步、多模型的流水线,prompt 的数量和形态会持续膨胀。如果没有单一事实源,会同时出现三个致命问题:**改了没人知道**(prompt 内嵌在某个节点代码里,改动不留痕)、**改了无法验证**(没有基线,不知道这次改动让质量变好还是变坏)、**跨链路无法统一治理**(同一类约束散落在十几个地方,改一处漏九处)。
|
||||
|
||||
这套体系的核心承诺,可以浓缩成一条纪律:
|
||||
|
||||
> **每条 prompt 都绑定一份完整契约——输入 Schema + 输出 Schema + 硬约束块 + Golden 样本集 + owner + version;运行时按 `id@version` 加载注入,绝不在编排内核里内嵌。**
|
||||
|
||||
这里有两个名词先解释清楚:**Schema**(模式)是一份机器可读的"格式说明书",规定输入/输出必须长什么样,不符合就报错;**Golden 样本集**(也叫黄金集)是一组"标准答案"——固定的输入配上期望的产物,用来在每次改动后自动比对"还对不对"。"按 `id@version` 加载"的意思是:prompt 像一个带版本号的库依赖,代码只持有它的编号(如 `config.clicker-designer@1.1.0`),真正的文本从 Registry(注册表,即上面那个目录)里取——这样换 prompt 不用重新编译代码,改文本即生效。
|
||||
|
||||
这条"能力增强放壳层、不动内核"的纪律,和生成引擎其它子系统是一脉相承的:生成逻辑藏在 job/callback 契约之后、框架可替换而业务契约不变;同理,prompt 藏在 Registry 之后,文本可迭代而调用代码不变。
|
||||
|
||||
> **引擎上下文纠偏(2026-06-12 裁决,沿用至今)**:本体系的**核心理念——Prompt 即第 8 契约 / Registry / eval 门禁 / HITL——现行有效、永久不变**。但本文涉及"注入到哪个引擎"的具体载体,按现行架构裁决理解:**早期方案里的 Dify(一个可视化 LLM 工作流编排平台)和 OpenGame(一个文生游戏代码的第三方服务)已降级为远期增强、从未在 MVP 部署**;当前 Tier1(第一梯队、即 MVP 生产主线)的生成主线是 **new-api 网关 + agent 把代码写进插件库**(new-api = 绘境AI 内部统一的模型成本/凭证网关,所有模型调用都走它;插件库引擎 = LittleJS 增强发行版)。编排基建则是 **SAA 裸 StateGraph**(SAA = Spring AI Alibaba,阿里的 Spring 生态 AI 编排框架)。下文凡提"注入目标",按此理解;**治理机制本身与载体无关,完全不变。**
|
||||
|
||||
---
|
||||
|
||||
## 2. 数据流:Prompt 治理在生成链路里的位置
|
||||
|
||||
把 Prompt 治理放进生成主线来看,它扮演的是一个**横切的供给与把关层**:一边给生成链路供给 prompt 文本,一边在 prompt 变更进入生产前拦一道质量门。下面这张图描述了这两条数据流——左侧是**运行时供给**(实线),右侧是**变更治理**(从一个 Pull Request 出发的虚线闭环)。
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
subgraph SOT["单一事实源"]
|
||||
GIT["contracts/prompts/<br/>(git 仓库 · 第 8 类契约)"]
|
||||
end
|
||||
|
||||
subgraph RUNTIME["运行时供给(实线)"]
|
||||
LOADER["PromptRegistryLoader<br/>加载 / 渲染器(aigc 壳层)"]
|
||||
NODE["SAA StateGraph 各节点<br/>(render / design / generate / qa ...)"]
|
||||
MODEL["new-api 网关<br/>→ 大模型"]
|
||||
end
|
||||
|
||||
subgraph GOV["变更治理(虚线闭环)"]
|
||||
PR["Prompt 改动 PR<br/>(必须升 version)"]
|
||||
CI["prompt-eval CI<br/>(PR 触发)"]
|
||||
GATE["四道闸<br/>Schema / 成功率 / 回归 diff / 成本延迟"]
|
||||
HUMAN["人工抽检<br/>(HITL 把关)"]
|
||||
MERGE["合入 → 部署同步"]
|
||||
end
|
||||
|
||||
GIT -->|"按 id@version 读"| LOADER
|
||||
LOADER -->|"渲染后注入"| NODE
|
||||
NODE --> MODEL
|
||||
|
||||
PR -.->|触发| CI
|
||||
CI -.->|读基线 + 跑样本| GIT
|
||||
CI -.-> GATE
|
||||
GATE -.->|全绿| HUMAN
|
||||
HUMAN -.->|批准| MERGE
|
||||
MERGE -.->|更新| GIT
|
||||
|
||||
TEL["telemetry 行为指标<br/>(线上真实表现)"] -.->|反哺改进| PR
|
||||
|
||||
style GIT fill:#d6eaf8
|
||||
style GATE fill:#f9e79f
|
||||
style HUMAN fill:#fadbd8
|
||||
```
|
||||
|
||||
读这张图抓住三点就够了:
|
||||
|
||||
- **供给侧是只读的、轻量的。** 生成链路的每个节点要 prompt 时,经 `PromptRegistryLoader`(加载器)按编号从 Registry 取文本、填入变量、注入给模型。Registry 是一份**文件契约,不是一个服务**——所以它不引入新的跨模块强耦合,也不会成为一个会宕机的依赖。
|
||||
- **治理侧是闭环的、有门的。** 任何人想改 prompt,都得提一个 PR(Pull Request,代码改动的评审请求),并且**必须升 version**(改了不升版号会被 CI 直接拒)。PR 一提,持续集成(CI)就自动跑"四道闸"——这是本体系最核心的质量门,§4 详述。四闸全绿,再交人工抽检批准,才允许合入并同步到生产。
|
||||
- **数据回流让 prompt 越改越准。** 线上的 telemetry(遥测,即用户真实行为埋点数据)会反哺回来:哪类 prompt 产出的游戏没人玩、留存差,就成为下一轮改 prompt 的依据。这一条把"治理"从被动防退化,升级成主动求进步。
|
||||
|
||||
依赖方向很干净:**壳层 → Registry(读);CI → Registry(读)+ 模型(跑样本);运行时构建流水线 → 测试原子(执行)。** 没有任何一条引入新的服务级强耦合。
|
||||
|
||||
---
|
||||
|
||||
## 3. Registry 长什么样:目录结构与一条 prompt 的构造
|
||||
|
||||
Registry 的物理形态就是 `contracts/prompts/` 下的一棵目录树。它按**生成生命周期的阶段**分目录,每个阶段放该阶段会用到的 prompt。当前实现采用 **8 阶段编号命名**(便于一眼看清执行顺序),其中已投产的几个阶段如下:
|
||||
|
||||
```
|
||||
contracts/prompts/
|
||||
├── registry.yaml # 索引:所有 prompt 的 id / 版本 / owner / 绑定 schema / eval 集
|
||||
├── _schemas/ # 输入 / 输出 JSON Schema(prompt 的格式契约)
|
||||
├── 01-safety/ # 创作者输入的安全 / 注入检测(生成链路第 1 道节点)
|
||||
├── 04-config/ # 各玩法的策划 prompt(clicker / merge / idle / tycoon ... designer)
|
||||
│ # + generic-coder(P3 编码 prompt,产出可玩产物源码)
|
||||
├── 06-quality/ # 对抗式质量评审 prompt(adversary-review)
|
||||
├── 07-fix/ # 按评审意见回灌重出的修订 prompt(design-revise)
|
||||
└── eval/ # 每条 prompt 的 Golden 样本集,目录名 = prompt id
|
||||
```
|
||||
|
||||
> 阶段编号的完整规划是 `01-safety 02-intent 03-template 04-config 05-asset 06-quality 07-fix 08-meta`;上面只列出当前已落地的几个。其余阶段随生成链路演进逐步迁入,Registry 的设计本就允许增量入库。
|
||||
|
||||
**每条 prompt 的构造 = 一段 frontmatter 契约头 + 模板体。** frontmatter(前置元数据,写在文件顶部 `---` 之间的结构化字段)就是这条 prompt 的"契约头",声明它的身份、版本、归属和绑定的 schema/eval。模板体则是真正发给模型的文本,里面带变量槽(运行时填入)。一个示意:
|
||||
|
||||
```yaml
|
||||
---
|
||||
id: config.clicker-designer # 唯一编号(阶段.角色)
|
||||
version: 1.1.0 # 语义化版本号,改一字也要升
|
||||
owner: WS2 # 唯一负责工位(改它必须经其批准)
|
||||
tier: tier1 # tier1 | tier2 | tier3(梯队)
|
||||
stage: "04-config" # 所属生命周期阶段
|
||||
input_schema: _schemas/... # 输入格式契约
|
||||
output_schema: contracts/templates/clicker.schema.json # 输出格式契约
|
||||
constraints: # 硬约束块(产物必须满足的红线)
|
||||
- 首屏 ≤ 2MB,总包 ≤ 10MB
|
||||
- 游戏内零网络请求
|
||||
eval_set: eval/config.clicker-designer/ # 绑定的 Golden 样本集
|
||||
guardrails: [injection-detect, schema-validate] # 护栏:注入检测 / schema 校验
|
||||
---
|
||||
{{system_prompt}}
|
||||
... 模板体,带 {{变量槽}} ...
|
||||
```
|
||||
|
||||
frontmatter 里几个字段值得点名:**owner**(归属工位)规定了"谁能改这条 prompt"——本项目把工位编号为 WS1~WS5(WorkStation,工位),改一条 prompt 要经它的 owner 批准,杜绝无主乱改;**tier**(梯队)区分 prompt 的重要性,tier1 是 MVP 生产主线必须治理的,tier2/3 是更长期的增强轨;**constraints**(硬约束块)是产物必须守住的红线(如包体大小、零网络请求),**guardrails**(护栏)是运行时挂的自动检查(如注入检测、schema 校验)。
|
||||
|
||||
`registry.yaml` 是这棵树的总索引,逐条登记每个 prompt 的 id、版本、owner、绑定的 schema 和 eval 目录。它和文件系统的实际内容必须**严格一致**——启动期会做全量校验,对不上就阻止部署(见 §7)。
|
||||
|
||||
---
|
||||
|
||||
## 4. 心脏:改一条 prompt 要过的"四道闸"
|
||||
|
||||
这套体系真正的价值,在于**让"改 prompt"这件高风险的事变得可回归、可拦截**。机制是:任何 prompt 改动的 PR,都会触发一段叫 `prompt-eval` 的 CI 流程,它对这条 prompt 跑四道自动闸门,全绿才放行。"四道闸"是本体系的核心门控,逐一解释:
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
START["Prompt 改动 PR"] --> V{"version 升了吗?"}
|
||||
V -- "没升" --> REJECT1["拒绝合入<br/>(version 未变)"]
|
||||
V -- "升了" --> G1{"闸 1<br/>Schema 校验"}
|
||||
G1 -- "产物不合 schema" --> REJECT2["拦截 · 报具体字段"]
|
||||
G1 -- 通过 --> G2{"闸 2<br/>成功率 ≥ 0.8"}
|
||||
G2 -- "低于阈值" --> REJECT3["拦截 · 质量退化"]
|
||||
G2 -- 通过 --> G3{"闸 3<br/>回归 diff"}
|
||||
G3 -- "关键字段变了" --> REJECT4["拦截 · 偏离基线"]
|
||||
G3 -- 通过 --> G4{"闸 4<br/>成本 / 延迟"}
|
||||
G4 -- "超出阈值" --> REJECT5["拦截 · 变贵 / 变慢"]
|
||||
G4 -- "四闸全绿" --> HUMAN["人工抽检(HITL)"]
|
||||
HUMAN -- "批准" --> MERGE["合入 → 部署同步"]
|
||||
|
||||
style HUMAN fill:#fadbd8
|
||||
style MERGE fill:#a9dfbf
|
||||
```
|
||||
|
||||
- **闸 0 · version 必升(前置硬门)。** 改了 prompt 却没升 `version`,CI 直接卡死。这条看似琐碎,实则是整套版本化治理的地基——没有它,"按 `id@version` 加载"和"回归比对基线"全都失效。
|
||||
- **闸 1 · Schema 校验。** prompt 产出的结果必须符合它声明的 `output_schema`。不合格就拦截,并**报出具体是哪个字段不对**,而不是只说一句"失败了"。
|
||||
- **闸 2 · 成功率门(≥ 0.8)。** 拿这条 prompt 跑一遍 Golden 样本集,**成功率必须 ≥ 80%**。这个 0.8 不是随手定的——它直接对齐绘境AI 的 MVP 顶层指标"AI 生成成功率 ≥ 80%"。任何 prompt 改动一旦把成功率拖到这条线以下,就是质量退化,必须拦。
|
||||
- **闸 3 · 回归 diff(黄金比对)。** 把改动后的产物和基线快照(`baseline/`)逐字段比对,盯住一组**关键字段**(`golden_diff_fields`)。如果这些关键字段相对基线发生了不该有的偏移,说明改动产生了预期外的副作用,拦截。
|
||||
- **闸 4 · 成本 / 延迟门。** 检查这次改动有没有让生成**变得更贵或更慢**——`max_cost_delta`(成本增量上限)、`max_latency_delta`(延迟增量上限)。因为绘境AI 的整体策略是"便宜模型 + 门兜底",一条让单次生成成本翻倍的 prompt 即使质量没退化,也可能突破成本红线(MVP 基础设施成本 < ¥5000/月),必须把关。
|
||||
|
||||
四闸全绿之后,还要过**人工抽检**——这就是下一节要讲的 HITL。**机器闸拦掉的是"可量化的退化",人抽检的是"机器量不出来的味道"**(比如产物虽合规但玩起来无聊)。两者叠加,才构成完整的把关。
|
||||
|
||||
> 一个体现纪律的细节:eval 的判定阈值(成功率、关键字段、成本/延迟上限)是写进 prompt 自身契约的(`eval_set` 指向的目录),而不是写死在 CI 脚本里。这样每条 prompt 可以有自己的质量标准,治理框架统一、标准各异。
|
||||
|
||||
---
|
||||
|
||||
## 5. 接口契约:加载器与测试原子
|
||||
|
||||
这套体系对外暴露两组关键契约。第一组是 **`PromptRegistryLoader`**(prompt 加载器),它是生成链路读取 prompt 的唯一入口,接口形态(契约,非实现)如下:
|
||||
|
||||
```
|
||||
PromptTemplate load(id, version) # 取一条 prompt(含 frontmatter + 模板体);缺失抛 PromptNotFoundException
|
||||
String render(id, version, vars) # 把变量填进模板,渲染成最终文本
|
||||
List<PromptMeta> list(stageOrEngine) # 按阶段 / 引擎列出 prompt
|
||||
boolean validate(PromptTemplate) # 校验 frontmatter 与 schema 绑定是否有效
|
||||
```
|
||||
|
||||
**加载策略是"内存镜像优先、回源兜底":** 优先读数据库里的只读镜像(命中即返回,快);未命中再回源到 git 取文件。而且**启动期会全量校验** `registry.yaml` 与实际文件是否一致——索引和文件对不上就拒绝启动,把"配置漂移"挡在上线之前。
|
||||
|
||||
第二组契约值得专门点名,因为它把"prompt 治理"和"产物质量"接到了一起——这就是 **T-AGC-09 测试脚本原子**(原子 = 生成流水线里一个不可再分的处理单元;T-AGC-09 是它的工序编号):
|
||||
|
||||
```
|
||||
TestScript generate(GameConfig config) # 输入游戏配置 → 输出测试脚本(启动 / 输入响应 / 边界断言)
|
||||
// runtime 编译后执行该脚本:通过 = 准许入库;失败 = 拒绝入库 + 原因分类
|
||||
```
|
||||
|
||||
它的意义在于:生成出来的游戏**在入库发布之前,必须先被一段自动生成的测试脚本验证"真的能跑"**——能不能启动、按键有没有响应、边界条件会不会崩。跑不过就拒绝入库,并对失败原因分类。这是 prompt 治理体系在"产物侧"埋的最后一道闸:**prompt 把关的是"怎么生成",T-AGC-09 把关的是"生成出来的到底能不能玩"。** 它与生成引擎的"九门 harness"(把产物放进真实运行环境自动检验的九道确定性门)是同一种工程哲学的延伸——宁可显式拦截,绝不让坏产物进库。
|
||||
|
||||
---
|
||||
|
||||
## 6. 人在哪里介入:HITL 治理
|
||||
|
||||
把 prompt 治理做成纯自动化是危险的——机器闸能量化的退化拦得住,但"产物虽然合规却很无聊""这版文案语气不对"这类**需要人判断的灰色地带**,只能靠人。所以本体系刻意保留了 **HITL**(Human-In-The-Loop,人在环路中)节点,分两侧落地:
|
||||
|
||||
- **创作者侧 HITL** 复用生成链路里已有的"重生成 / 锁风"交互:创作者对生成结果不满意时,可以触发重新生成或锁定风格(让后续迭代保持一致的视觉/玩法风格)。这是**面向终端用户**的人在环——用户本身就是质量的一道闸。
|
||||
- **运营侧 HITL** 是 prompt 变更的批准闭环:运营/工位负责人走"改 prompt → 跑 eval → 人工批准"这条流程。在产品形态上,这最终会落成 game-admin(管理后台)里的一个 prompt 管理页(规划为 P1 优先级);在它就绪之前,**可以先用 PR 流程 + 文档化规范替代**——即四道闸跑完、人在 PR 里 review 批准后合入(正是 §4 那张图最后两步)。
|
||||
|
||||
HITL 在整条链路里的位置很明确:**它永远站在机器闸之后**。先让自动化把可量化的退化全拦掉,把通过的候选交给人;人只需在机器认证"没退化"的基础上,判断那些机器判断不了的事。这样既不让人淹没在该由机器做的重复劳动里,也不让纯机器闸放过它根本看不见的问题。
|
||||
|
||||
---
|
||||
|
||||
## 7. 它怎么坏 & 怎么修:失败路径与回滚
|
||||
|
||||
一套治理体系必须诚实回答"它自己出故障时会怎样"。下面是关键失败路径与对应处理——核心原则是**任何治理层的故障都不应该阻断生成主链路**:
|
||||
|
||||
| 失败场景 | 处理方式 |
|
||||
|---|---|
|
||||
| prompt id/version 不存在 | `load` 抛异常 → 壳层**回退到内置默认 prompt** + 告警,**不中断生成** |
|
||||
| frontmatter / schema 校验失败 | 启动期或 CI 拒绝该 prompt 上线,报出具体字段 |
|
||||
| eval 跑不通(模型限流 / 超时) | CI 标记 skip + 通知人工兜底审,**不阻塞紧急修复** |
|
||||
| 改 prompt 未升 version | CI 卡死:version 未变 → 拒绝合入(即 §4 的闸 0) |
|
||||
| registry.yaml 与文件不一致 | 启动期校验失败 → 阻止部署 |
|
||||
|
||||
回滚同样是分层设计的,**整体回滚成本极低**,因为 Registry 是一个**叠加层**——它叠在原有调用方式之上,而不是替换掉:
|
||||
|
||||
- **单条 prompt 回滚**:切换 `version`,或对该 prompt 文件 `git revert`。
|
||||
- **加载机制回滚**:加载器失败时自动回退到内置默认 prompt,生成不中断。
|
||||
- **整体体系回滚**:关闭 Registry 加载开关,回到 prompt 内嵌在节点里的旧形态——**旧内嵌 prompt 从不删除、只是不被引用**,开关一切回即恢复。
|
||||
|
||||
这套"叠加层 + 开关回切"的设计,正是 §1 那条纪律("能力增强放壳层、不动内核")在可靠性维度上的兑现:治理是给生成主线**加的一层保险**,而保险本身坏了,主线照样能跑。
|
||||
|
||||
---
|
||||
|
||||
## 8. 验收口径:这套体系怎么算"建成了"
|
||||
|
||||
最后给出这套体系的完成判据(验收时逐条核对):
|
||||
|
||||
1. **入库完整**:Tier1 全链路 prompt 都进了 `contracts/prompts/`,`registry.yaml` 索引完整、与文件一致。
|
||||
2. **加载跑通**:生成时 prompt 真实来自 Registry(而非节点内嵌),改文件即生效。
|
||||
3. **门禁生效**:`prompt-eval` CI 至少对一条 prompt 生效——**故意改坏一条 prompt,PR 应被四道闸拦下;正常改动则放行**。
|
||||
4. **产物把关**:T-AGC-09 产出测试脚本,生成流水线在入库前执行它,能拦住"不可运行"的产物。
|
||||
5. **HITL 闭环可走**:运营能完整走通"改 prompt → eval → 批准"流程(页面或文档化流程均可)。
|
||||
|
||||
一句话收尾:**Prompt 治理把生成引擎里最易变、最易失控的一环,变成了和数据库、API 同级的工程契约——可版本化、改动可自动回归、人能在关键节点把关,而这一切都以"不拖慢、不阻断生成主线"为前提。** 这是绘境AI 让一个普通模型稳定产出可上线游戏的关键支撑之一。
|
||||
|
||||
---
|
||||
|
||||
## 9. 相关文档导航
|
||||
|
||||
| 文档 | 关系 |
|
||||
|---|---|
|
||||
| [`.agents/skills/prompt-governance.md`](../../../../.agents/skills/prompt-governance.md) | 本体系的可复用操作手册(playbook):新增/修改 prompt、搭 Registry、对接 HITL 的标准步骤 |
|
||||
| [SAA 编排](SAA编排.md) | 生成主线的 16 节点编排图——prompt 正是注入到这些节点里 |
|
||||
| [生成引擎主文档](README.md) | 生成引擎整体:一句话怎么变成一款游戏,以及"LLM as 工作室"范式 |
|
||||
| [`contracts/prompts/registry.yaml`](../../../../contracts/prompts/registry.yaml) | Registry 的实际索引,所有 prompt 的权威登记 |
|
||||
|
||||
> **纪律**:本文讲 prompt 治理的**设计与契约**;具体某条 prompt 的内容在 `contracts/prompts/` 各阶段目录里,标准操作步骤在配套 playbook 里。三者各司其职、互不重复。
|
||||
375
docs/architecture/架构/生成引擎/固定游戏架构.md
Normal file
375
docs/architecture/架构/生成引擎/固定游戏架构.md
Normal file
@ -0,0 +1,375 @@
|
||||
# 固定游戏架构 + SAA 智能工作室
|
||||
|
||||
> **这是什么**:绘境AI「一句话造游戏」背后的生成引擎设计。它回答一个核心问题——**一个便宜的小模型,怎么可靠地造出一款能上线、能玩的游戏**。
|
||||
> **给谁看**:生成引擎方向的工程师、架构评审、新加入的同事。
|
||||
> **怎么读**:先读 §1 抓住"固定脚手架 + 便宜模型填槽"这一个主意,其余各节都是它的展开。
|
||||
> **品牌**:本文统一用「绘境AI」。
|
||||
|
||||
---
|
||||
|
||||
## 1. 一句话与一个主意:为什么是"固定架构"
|
||||
|
||||
让一个便宜的小模型从零发明一款游戏的完整代码结构,是不可靠的——它每次都得重新设计实体、循环、胜负判定,错一处就崩。绘境AI 的破题思路反过来:**由一个最强的模型(Opus)一次性把游戏的骨架设计好、固定下来,便宜模型不再发明结构,只在这副固定骨架的"槽位"里填东西**——填逻辑、填参数、填关卡、填资产、填胜负规则。
|
||||
|
||||
这里有两个名词先解释清楚:
|
||||
|
||||
- **Opus** 是绘境AI 用来做架构设计与最高复杂度终裁的最强模型;**便宜模型**(cheap model,文中也叫小模型)指 deepseek-v4-flash、MiniMax-M3 这类单价极低、用来批量干活的模型。
|
||||
- **填槽**(fill-the-slot):便宜模型不负责"游戏该长什么样"的结构决策,只负责把固定骨架里预留好的空位补全。
|
||||
|
||||
这么做的根本理由,是把命门从"便宜模型能不能发明出结构"降级成"便宜模型能不能在一副已知的好骨架里把内容填好"——后者远比前者可信。两个评审都曾把"便宜模型产不出结构化、可维护的源码"列为致命问题(P0,即最高优先级、必须先解决的阻断项);固定架构正是对这个 P0 的回答。
|
||||
|
||||
但光有固定骨架还不够,便宜模型需要一个"工作环境"来被榨出最大价值。这就是第二个主意——**SAA 智能工作室**:
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
A["创作者<br/>一句话 + 素材"] --> B["SAA 智能工作室<br/>多个便宜 agent 协作"]
|
||||
B --> C["源项目<br/>(可维护的结构化定义)"]
|
||||
C --> D["确定性构建<br/>→ 可玩 bundle"]
|
||||
D --> E["九门真玩<br/>(自动质量门)"]
|
||||
E --> F["GamePackage 产物<br/>(schema 不变)"]
|
||||
F --> G["游戏信息流<br/>真玩"]
|
||||
```
|
||||
|
||||
- **SAA** 是绘境AI 后端采用的生成编排底座(Spring AI Alibaba),用它的"裸状态图"(StateGraph,把生成流程画成一张节点+连线的有向图,每个节点是一个独立步骤)把多个便宜 agent 串成一个能迭代、能自检、能反馈的协作系统。文中的 **agent** 指图里的一个有专长的工作节点(分类 / 设计 / 写逻辑 / 修 bug 等)。
|
||||
- **工作室**(studio)= 由 SAA 编排的这一整套多 agent 协作系统。
|
||||
|
||||
四样东西合起来,才把便宜模型榨满:**SAA 提供编排、固定架构提供结构、便宜模型提供生成、自动质量门提供把关**。给定单款生成预算 **< $1**(便宜模型单价低,一美元容得下几十次调用),这个环境就能放心地多 agent、多迭代、多重试。
|
||||
|
||||
> 这套设计不是空想。便宜模型在固定结构上填出连贯游戏定义、再构建成真能过门的游戏,已在代码层得到双重验证(见 §9 实测证据),核心立论"改源不改包 / 可维护源项目 / 把大模型当工作室"已经成立。
|
||||
|
||||
---
|
||||
|
||||
## 2. 固定的部分 vs 可填的部分
|
||||
|
||||
整套架构的第一刀,是把"谁也不许动的固定骨架"和"每款游戏都不一样的可填内容"切开。
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
subgraph FIXED["固定架构(Opus 设计一次 · 全游戏共用)"]
|
||||
A["项目骨架 / 构建管线"]
|
||||
B["游戏运行时契约:固定生命周期"]
|
||||
C["游戏定义 schema:<br/>实体 / 组件 / 行为 / 场景 / 规则"]
|
||||
D["引擎适配器:2D LittleJS 现 / 3D Cocos 后"]
|
||||
E["可填槽定义 + 结构校验门"]
|
||||
end
|
||||
subgraph FILL["可填(便宜 agent 每款生成,符合上面契约)"]
|
||||
F["实体 / 组件 / 行为模块"]
|
||||
G["参数 / 平衡 数据"]
|
||||
H["关卡 / 内容 数据"]
|
||||
I["资产规格 / 引用"]
|
||||
J["胜负规则"]
|
||||
end
|
||||
FILL -->|符合契约| FIXED
|
||||
FIXED -->|适配器构建| K[("可玩产物 bundle<br/>宿主 / 信息流 不变")]
|
||||
```
|
||||
|
||||
- **固定的部分**:不随游戏改变,由 Opus 设计、长期按版本演进。包括项目骨架、运行时契约、游戏定义的数据结构(schema)、引擎适配器、以及把关用的结构校验门。
|
||||
- **可填的部分**:每款游戏由便宜 agent 现场生成,但**必须符合固定契约**,由结构校验门把关。
|
||||
|
||||
之所以能这样切,关键在于可填的部分是**数据驱动**的:逻辑读参数、资产用引用、行为是一个个符合契约的模块。正因为内容是数据,"修改一款游戏 = 改源里某个槽 + 重新构建"才能成立——这也是后面"改源不改产物"生命周期的地基。
|
||||
|
||||
这一刀还带来三个连锁好处,正好对上创始人定下的三条新约束:
|
||||
|
||||
1. **缓存命中高**:固定架构是一段稳定不变的上下文,可以被模型的"前缀缓存"命中,大幅降成本(详见 §7)。
|
||||
2. **便宜模型可靠**:它永远在填同一套已知结构,不必每次现学。
|
||||
3. **长期可维护**:游戏以结构化源项目存在,而不是一坨黑盒代码。
|
||||
|
||||
> **一条重要纪律:架构与玩法解耦。** 固定架构是领域模型框架,玩法是"填进去的数据 + 行为模块",两者正交。所谓"玩法模板"只是某个品类的预设(引导便宜模型怎么填),不是另一套独立架构。遇到极端不同的品类,用"通用核心 + 品类附加画像"来吸收差异,绝不分叉出第二套架构。
|
||||
|
||||
---
|
||||
|
||||
## 3. 怎么做到 2D/3D 兼容:游戏定义模型 + 引擎适配器
|
||||
|
||||
固定骨架的核心,是**一套声明式、维度无关的游戏定义模型**。"声明式"指它描述"游戏由什么构成",而不是写一段过程式代码;"维度无关"指同一份定义既能渲染成 2D 也能渲染成 3D。它故意做得轻量——是领域模型,**不是 AAA 大作那种重型 ECS**(ECS = Entity-Component-System,一种把游戏拆成实体、组件、系统调度器的架构;这套定义没有系统调度器、没有原型存储、没有查询 DSL,守住"简单"二字)。
|
||||
|
||||
模型由五类元素构成:
|
||||
|
||||
- **实体(entity)**:带一个 `transform`(位置 / 旋转 / 缩放)和一组组件。位置用同一套字段表达两个维度——2D 用 x/y(z 默认 0),3D 用 x/y/z,**同一字段两维都成立**。
|
||||
- **组件(component)**:声明式属性,如渲染(sprite / mesh)、碰撞、物理,由适配器去解释。
|
||||
- **行为(behavior)**:游戏逻辑——读参数、操作实体。它是逻辑,与维度无关。
|
||||
- **场景(scene)/ 规则(rule)**:关卡如何构成、胜负如何判定,都是数据驱动。
|
||||
|
||||
光有定义还不能跑,需要**引擎适配器**把定义"翻译"成具体引擎能执行的东西:
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
DEF["游戏定义模型<br/>(维度无关 · 一份)"]
|
||||
DEF -->|2D 适配器| L["LittleJS<br/>实体 → sprite"]
|
||||
DEF -->|3D 适配器<br/>(Phase 2)| C["Cocos<br/>实体 → mesh / node"]
|
||||
L --> P2D["可玩 2D 游戏"]
|
||||
C --> P3D["可玩 3D 游戏"]
|
||||
```
|
||||
|
||||
- **LittleJS** 是绘境AI 选定的轻量 2D 引擎(分层引擎里的 Tier1,即首选层);**Cocos** 是更重的 3D/独立引擎(Tier2-3,更长期的层级)。
|
||||
- **本期只实现 2D 适配器**,3D 适配器留到 Phase 2;但定义格式从一开始就不写死 2D 假设,所以 Phase 2 加 3D 适配器**无需改格式**。
|
||||
|
||||
最大的收益一句话:**同一份游戏定义,换适配器即换引擎**。所谓"尽量 2D/3D 兼容"的真实含义,是格式前向兼容,而非 Phase 1 就把 3D 全套建出来。
|
||||
|
||||
---
|
||||
|
||||
## 4. SAA 智能工作室:9 个生成 agent + 1 个评审 + 1 个离线
|
||||
|
||||
工作室是 SAA 裸状态图编排的多 agent 协作系统。它从现有的 11 节点 SAA 图演进而来,经 Opus 深度分析(并参考了开源项目 OpenGame 的设计),精化为 **9 个生成 agent + 1 个条件触发的叙事评审 agent + 1 个离线技能沉淀 agent**。全图 16 节点已建成并合入主干。
|
||||
|
||||
先说一个最关键的澄清:**写游戏代码和修 bug 是同一个核心代码 agent 的两面**,不是两个独立 agent。它在 `generate` 时写代码、在 `repair` 时修代码,构成一个自纠错环。其余 agent(分类 / 设计 / 资产 / 配置 / 构建 / 质检)都不写游戏代码。
|
||||
|
||||
各 agent 的职责:
|
||||
|
||||
| agent | 职责 | 备注 |
|
||||
|---|---|---|
|
||||
| **classify(分类,新)** | 把一句话 brief 判成品类原型 + tick/input/progress 三维画像 | 成功率最大的杠杆——填错品类比写错码更致命 |
|
||||
| **design(设计)** | brief / 对话 → 结构化游戏设计(机制 / 实体 / 胜负) | 产出 GDD(Game Design Document,游戏设计文档) |
|
||||
| **logic(写逻辑)** | 填实体 / 组件 / 行为模块(符合契约) | 核心代码 agent 的"写"一面 |
|
||||
| **config/balance(配置平衡)** | 产参数 / 关卡(数据驱动) | 与 logic 同源拆出 |
|
||||
| **asset(资产,新)** | 产六类资产的规格 / 引用 | provider 可插拔,默认走 mmx-cli |
|
||||
| **build(构建)** | 适配器构建 → 可玩 bundle | 调唯一构建脚本 |
|
||||
| **QA(质检)** | 九门真玩 + 结构校验 | 由 validate + play + player 三节点构成 |
|
||||
| **repair(修复)** | 门失败定位 + 回喂修复 | 核心代码 agent 的"修"一面 |
|
||||
| **modify(修改,新)** | 生命周期:在固定结构上改一处 + 重构建 | 旧 11 节点图的真空白,本期补上 |
|
||||
|
||||
另有两个特殊角色:
|
||||
|
||||
- **narrative-designer-reviewer(叙事设计评审,条件触发)**:剧情 / 叙事 / TRPG(桌上角色扮演)类游戏**保留在 MVP**(不另开独立轨道)。因为九门这类确定性测试测不了"叙事是否连贯",所以给这类游戏配一个独立的"设计 + 代码评审"agent 当质量门。这是创始人的明确裁决:**质量门按品类换机制**——动作类用确定性九门,叙事类用 agentic 评审(终判留创始人)。
|
||||
- **skill-curator(技能沉淀,离线冷路径)**:把成功经验沉淀成可复用技能,经 Git 注册表与生成热路径解耦。**MVP 只做 Debug Skill**(把"错误签名 → 验证过的修复"沉淀下来),**Template Skill(骨架萃取晋升)推迟**到远期。
|
||||
|
||||
> **演进路线(接法 A / 接法 B):** 当前 16 节点是**接法 A**——裸图确定性编排,由九门判定"做完了没"(已建七成、是躯干)。**接法 B** 是让 generate/repair 长成一个 **ReactAgent 自治工具循环**(把"写源、构建、跑九门、读错误、改配置和资产"都做成它能自己调用的工具)。九门兜底化解了"自治"与"铁律"的张力——**自治在门内、裁决在门外**。策略是先用 A 稳住地基,再用 B 增量演进,避免返工。
|
||||
|
||||
---
|
||||
|
||||
## 5. 八个契约:两条开发线的唯一事实源
|
||||
|
||||
这套架构由两条开发线并行实现:**后端线**(负责源项目存储、SAA 拓扑、救场路由、工作室编排)和**引擎+前端线**(负责 2D 适配器、资产产消、构建实现、九门质检、前端创作/修改/预览界面)。两条线要能各自独立开工而不打架,前提是先把交汇面冻结成契约——这就是**契约先行**铁律:契约没冻结,两条线都不许开工。
|
||||
|
||||
交汇面正好是 **8 个契约**:
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
subgraph 契约["8 契约(开工前冻结 = 两线交汇面)"]
|
||||
C1["① 源项目 schema"]
|
||||
C2["② 源项目 DB 存储"]
|
||||
C3["③ 源/产物分离"]
|
||||
C4["④ SAA state key 全表"]
|
||||
C5["⑤ asset 产消"]
|
||||
C6["⑥ modifyPatch"]
|
||||
C7["⑦ 唯一确定性构建 API"]
|
||||
C8["⑧ trace+cost 字段"]
|
||||
end
|
||||
BE["后端线<br/>主锁 ①②④⑥⑧"] --> 契约
|
||||
EN["引擎+前端线<br/>主锁 ①(消费)⑤⑦③"] --> 契约
|
||||
```
|
||||
|
||||
下面逐个说清每个契约**是什么、为什么这么定**,字段级细节见原始 execution 文档,这里只保留结论与硬约束。
|
||||
|
||||
### 契约① 源项目 schema —— 便宜模型填的唯一结构化产物
|
||||
|
||||
新文件 `contracts/agent-loop/source-project.schema.json`,由 Opus 设计、长期版本化升级。它是便宜 agent 在固定架构上填出来的**唯一结构化产物**,顶层包含:
|
||||
|
||||
- `schemaVersion`(源项目契约版本,独立于 GamePackage 版本)、`sourceHash`(源 JSON 规范化后的 sha256,既是可寻址键也是幂等键)、`buildInputHash`(= sourceHash + 构建画像的 sha256,作为构建缓存命中键)。
|
||||
- `profile` **三维画像**:`tickModel`(realtime / turn-based / event,游戏怎么推进时间)、`inputModel`(continuous / discrete-choice / text-command,玩家怎么输入)、`progressModel`(metric / narrative,靠数值还是靠叙事推进)。**为什么必须三维**:只有纯物理一维会让剧情 / TRPG 类绷断,三维才覆盖得住。`progressModel=narrative` 会走叙事评审质量门。
|
||||
- `gameDefinition`(§3 那套实体 / 组件 / 行为 / 场景 / 规则)、`assets`(六类资产规格)、`config`(平衡参数,确定性修改就改这里、免调用 LLM)。
|
||||
|
||||
> **一条诚实边界**:`behaviors` 是声明式的**模块描述**,真正的逻辑代码仍由核心代码 agent 产出、再由构建编译进 bundle。源项目存的是"可维护的结构化定义",**不是一坨裸 iife**(iife = 立即执行函数表达式,这里指那种逻辑只以 JSON 字符串或 `new Function` blob 形式塞着的黑盒产物)。这正是设计要兑现的关键约束:逻辑必须以可维护的多文件源项目存在。
|
||||
|
||||
### 契约② 源项目 DB 存储 —— 独立的 `game_source_project` 表
|
||||
|
||||
源项目落在一张**全新的独立表** `game_source_project`(走 Flyway 迁移 V18.0.0,接在已合入的 V17 之后;Flyway 已合入的脚本禁改,回滚只能写补偿迁移)。
|
||||
|
||||
**为什么必须独立建表、绝不塞进 `game_version`**:这是架构评审的明令。源项目(可维护、会被改)和打包产物(可玩、冻结)是两个物理解耦的存储面,塞在一起会破坏 GamePackage 产物的纯净性。这里曾与另一份 DB 评审的"game_version 双存"口径冲突,本架构采**独立表**口径胜出。
|
||||
|
||||
表里用一个 `status` 字段(0 草稿 / 1 已构建 / 2 孤儿 / 3 已发布)串起事务边界:
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
A["工作室编排 → 图产源项目"] --> B["源落库 status=0"]
|
||||
B --> C["触发确定性构建"]
|
||||
C -->|构建成功| D["建预览包(走唯一写入路径)<br/>回填 version_id + status=1"]
|
||||
C -->|构建失败| E["源 status=2(孤儿)<br/>不建包 · 不动 currentVersion"]
|
||||
```
|
||||
|
||||
关键一点:源落库和建包**不在同一个大事务里**(构建要几十秒,长事务会拖垮连接池),用 `status` 机器态串联,而非分布式事务。
|
||||
|
||||
### 契约③ 源/产物分离 —— GamePackage 维持产物-only,不改
|
||||
|
||||
`game-package.schema.json` **一个字节不改**。它的顶层是 `additionalProperties:false`(不允许出现 schema 未定义的字段),所以源项目字段**禁止**塞进 GamePackage。`engineBundle` 仍是 GamePackage 里一个可选的(additive,新增不升版本号)产物字段,由构建写入、由唯一写入路径落包。两个面的对照:
|
||||
|
||||
| 面 | 存储 | schema | 可变性 |
|
||||
|---|---|---|---|
|
||||
| **源项目(可维护)** | `game_source_project`(新) | `source-project.schema.json`(新) | 便宜模型填、确定性修改改 |
|
||||
| **打包产物(可玩)** | `game_version` + `game_runtime_package` | `game-package.schema.json`(既冻) | 构建产出、宿主消费,**不变** |
|
||||
|
||||
### 契约④ SAA state key 全表 —— 严格 additive
|
||||
|
||||
SAA 图里所有节点共享一张 state(状态表),每个键的写入策略都是"节点返回即覆盖写"。这次在现有键之上**只新增、不改语义**。新增键包括:`archetype`(品类原型)、三维画像 `tickModel/inputModel/progressModel`、`sourceProject`(新核心产物 JSON 串)、`assetSpec`、`modifyMode/modifyPatch/baseVersionId`(修改路用)、`failCount`(连续九门失败计数)、`modelTier`(当前模型档)、`escalationEvents`(升档事件)、`giveupDumpPath`(放弃前 dump 落盘路径)、`narrativeReviewVerdict`(叙事质量门裁决)。
|
||||
|
||||
**为什么安全**:严格 additive——存量节点不读新键就零影响;trace 抽取是 best-effort(尽力而为),新键缺了就省略,trace_json 字节兼容。
|
||||
|
||||
### 契约⑤ asset 产消 —— 六类资产一次定死
|
||||
|
||||
asset 节点根据品类和游戏定义产出六类资产规格,写进源项目的 `assets[]`;下游 logic / 构建消费它,最终解析成 GamePackage 的 `assets[]`,由宿主加载渲染。
|
||||
|
||||
- **六类枚举一次定死**(四处同引):`sprite / character / effect / scene / ui / music`。
|
||||
- **provider 可插拔**:默认 `mmx-cli`(投资人版默认走 mmx-cli,音乐走"记谱 → 合成"两步);换 provider 只改 asset 节点,不动消费侧。
|
||||
- **MVP 边界**:asset 节点首版可以只产规格、不真生图(用 canvas 几何兜底),真资产生成作为 additive 升级;但 `assets[]` 这条产消通道**必须打通**,否则就是个孤儿设计。
|
||||
|
||||
### 契约⑥ modifyPatch —— "改源不改产物"的载体
|
||||
|
||||
modify = 在固定结构上改一处 + 重构建。它的载荷 `modifyPatch` 指明改谁(base 版本)、怎么改(`deterministic` 确定性编辑 vs `regenerate-module` 让 LLM 重生成一个模块)、改哪个部件(资产 / 配置 / 关卡 / 行为,用 JSON 指针或部件 id 寻址)。口径锁定如下:
|
||||
|
||||
- **换美术 / 调参 / 改关卡** = 确定性编辑:直接覆写源文件 → 重构建 → 新预览版,**免 LLM、秒级**。
|
||||
- **改玩法逻辑** = 重生成模块:LLM **只重生成那一个 behavior 模块**,不是全量重出,其余部件不动。
|
||||
- **产物**:新预览版,**不动 currentVersion、不自动进信息流**,仍走发布审核;失败则不建新版、base 不动。
|
||||
- 旧设计里"manifest 可寻址 diff"的方案已废、不采用。
|
||||
|
||||
### 契约⑦ 唯一确定性构建 API —— 化解构建权威分裂
|
||||
|
||||
输入 `源项目 + 构建画像`,输出 `engineBundle + checksum + bundleSize + buildLog`。这里有一个曾经的隐患:构建到底谁说了算?裁决如下:
|
||||
|
||||
- **构建执行权在 worker / SAA build 节点共调的那一个 `scripts/build.mjs`**(esbuild 打包,产出顶层全局名 `__GameBundle`)。它现在已经真跑、九门验过,**不另造第二个构建实现**。worker 和 SAA build 节点本就调同一个脚本同一套参数,**天然统一**。
|
||||
- **后端 `RuntimeBuildServiceImpl` 只管落包**(消费构建产物 → 落 `game_runtime_package` + 回填 `game_version` 三字段),**不要求它自己跑 esbuild**。一句话:构建执行权在 worker/build 节点,落包权在后端,职责单一、不分裂。
|
||||
- **确定性**的定义:同一个 `buildInputHash` 必须构建出字节等价的 bundle(从源能重新构建出一模一样的产物,证明它"非一次性")。靠锁引擎版本和 esbuild 参数保证。
|
||||
|
||||
构建产物还携带两个硬性体积入场券(B1 发布链要求):**raw ≤ 1.5MB、gz ≤ 350KB**。
|
||||
|
||||
### 契约⑧ trace+cost 字段 —— 可观测与成本台账
|
||||
|
||||
在现有的追踪账本上扩字段:`modelTier`(终态模型档)、`escalationEvents`(升档事件)、`cacheHit`(缓存命中 token,落成本台账)、`giveupDumpPath`(放弃前完整 dump 路径)、`cost`(折算人民币)。
|
||||
|
||||
缓存命中检测要**兼容两套字段**:DeepSeek 用 `usage.prompt_cache_hit_tokens`,MiniMax 用 `usage.prompt_tokens_details.cached_tokens`,**取两者最大值**落账。命中 token 只进**成本台账**(token 计量),**不接收益结算**——这是生成侧与收益侧的硬边界。
|
||||
|
||||
---
|
||||
|
||||
## 6. 救场阶梯:便宜模型扛不住时怎么办
|
||||
|
||||
便宜模型有时就是填不出能过门的游戏。这时不能无限重试烧钱,也不能一失败就放弃。绘境AI 用一道**带限额的救场阶梯**——这是创始人的原话落地:
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
S0["便宜模型 stage1 起步"] --> P{"九门 verdict.pass?"}
|
||||
P -->|通过| OK["收口 → 产物"]
|
||||
P -->|失败 failCount++| C5{"连续 5 次失败?"}
|
||||
C5 -->|否 failCount<5| R1["repair 修复(仍 stage1)"]
|
||||
R1 --> P
|
||||
C5 -->|是 failCount=5| ESC["escalate 升档<br/>stage1 → stage2"]
|
||||
ESC --> P2{"stage2 九门?"}
|
||||
P2 -->|通过| OK
|
||||
P2 -->|再失败 3 次 failCount≥8| GU["giveup<br/>完整 dump 落盘"]
|
||||
P2 -->|未到 3 次| R2["repair(stage2)"]
|
||||
R2 --> P2
|
||||
```
|
||||
|
||||
几个要点,都是硬约束:
|
||||
|
||||
- **救场的"判定锚"是九门的 `verdict.pass=false`**——确定性结果,不是模型的主观自评。这是为了防止一种叫 **split-brain(脑裂)** 的毛病:模型一边自评"我做得很好"、一边其实没过门,自评和客观门各说各话。锚死客观门,就杜绝了脑裂。
|
||||
- **阶梯**:便宜模型(stage1)→ **连续 5 次九门失败 → 升一档模型(stage1→stage2)** → stage2 **再失败 3 次 → 放弃本次生成**。
|
||||
- **放弃前必须完整 dump**:把所有尝试的源码和裁决整包落盘(路径记在 `giveupDumpPath`),喂给 Opus 离线分析、反哺优化。这是硬观测要求,不是可选项——旧的 giveup 会丢中间源码,这次补上。
|
||||
- **recursionLimit 重算**:状态图有个"最多走多少步"的上限(recursionLimit),防止死循环。救场总轮数从原来的 5 升为 5+3=8 主轮,这个上限按新口径重算(建议 ≥90,留足余量),确保正常收口不会误触上限。对应配置项也从单一的"最多修 5 次"扩成可配,新增"stage2 额外修 3 次"。
|
||||
|
||||
### 品类原型映射表(避免孤儿分类)
|
||||
|
||||
classify 出来的 `archetype` 必须落到一张映射表的某一行,否则就是"孤儿分类"——分了类却没人接。这张表把品类锚到现有的品类画像:
|
||||
|
||||
| archetype | tickModel | inputModel |
|
||||
|---|---|---|
|
||||
| `clicker`(点击) | event | discrete-choice |
|
||||
| `dodge`(躲避) | realtime | continuous |
|
||||
| `runner`(跑酷) | realtime | continuous |
|
||||
| `bubble`(瞄准发射) | realtime | continuous |
|
||||
| `match3`/`line-clear`(点选消除) | turn-based | discrete-choice |
|
||||
| `merge`(合成) | event | discrete-choice |
|
||||
| `idle`(放置) | event | discrete-choice |
|
||||
| `tycoon`(经营) | turn-based | discrete-choice |
|
||||
| `generic`(兜底) | * | * |
|
||||
|
||||
- **物理优先分类**(借鉴 OpenGame 的 Physics-First 思路):classify 先定 tickModel(实测 100% 准),再定 archetype(实测 93% 准)。
|
||||
- **映射表本身就是契约**:新增一个 archetype,必须同时补这张表和对应的品类画像。注意这里的品类映射是用作**引导**(给 prompt / 脚手架做品类预设),**不是填参校验执行器**——废弃的是旧的"游戏模板",而"玩法模板"作为品类框架并未废弃。
|
||||
|
||||
---
|
||||
|
||||
## 7. 缓存三段式:把固定架构变成省钱的资产
|
||||
|
||||
固定架构有个隐藏红利:它是一段**永不变化的稳定上下文**,正好能被模型的"前缀缓存"命中。前缀缓存指的是——如果两次请求的开头(前缀)字节完全一致,模型对这段前缀的计算可以复用,命中部分便宜约 10 倍。绘境AI 把 prompt 切成三段来吃这个红利:
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
P1["固定前缀<br/>(契约 / few-shot / 能力面)<br/>永不变 · 最大块"] --> P2["共享中段<br/>(GDD)"] --> P3["可变后缀<br/>(brief / 反馈)"]
|
||||
P1 -.->|前缀缓存命中| SAVE["第二款起省 ~90% input"]
|
||||
```
|
||||
|
||||
- **固定前缀**(契约、few-shot 示例、能力描述)永不变,是最大的一块,第二款游戏起就能省掉约 90% 的输入 token。
|
||||
- **共享中段**是 GDD,**可变后缀**是这一款的 brief 和反馈;repair 回喂只追加后缀,不动前缀。
|
||||
|
||||
要让缓存稳定命中,前缀必须**字节级一致**。现状有个坑:few-shot 示例是从文件读的,字节不稳。**对策**:把系统前缀 + few-shot 冻结成**版本化的 Prompt Registry 资产**(放进 `contracts/prompts/`),由 CI 卡死"字节不变"。
|
||||
|
||||
> **这条缓存策略不是赌的,已实测证绿。** deepseek-v4-flash / MiniMax-M3 / M2.7 经过 new-api 网关(绘境AI 的模型调用统一网关)转发后,前缀缓存**全部透传**,第二款起命中 **76–93%**。曾经的最大单点风险"网关会不会把缓存吃掉"已经解除,剩下的只是"冻结 few-shot 字节稳 + 命中 token 落账"这点工程动作。
|
||||
|
||||
---
|
||||
|
||||
## 8. 失败路径:每一种崩法都有去处
|
||||
|
||||
一套生成系统的可靠性,体现在它对失败的处置上。下面是已经定好的边界失败路径:
|
||||
|
||||
| 失败情形 | 处置 |
|
||||
|---|---|
|
||||
| 源落库成功但构建失败 | 源标 `status=2`(孤儿),不建包,不动 currentVersion |
|
||||
| 构建产物缺 `__GameBundle` 全局名 | emit 判 failed,**不落坏包** |
|
||||
| LLM 调用超时 / 429 / 5xx | 单次 180 秒超时 + 最多 3 次重试,超限不裸退图(写 feedback) |
|
||||
| 九门连续失败 | failCount++ → 5 次升档 → 8 次 giveup + 完整 dump |
|
||||
| 叙事评审连续要求修复 | 同九门 failCount 阶梯计数 |
|
||||
| 缓存前缀不透传(已证透传,仅理论兜底) | 命中字段缺则按 miss 计价,生成不阻断,仅成本回退 |
|
||||
| modify 失败 | 不建新版、base 不动 |
|
||||
| classify 出未知 archetype | 落 `generic` 兜底 + 告警,不污染下游 |
|
||||
| trace 抽取异常 | best-effort 吞掉 + warn,trace_json 留空,不阻断生成 |
|
||||
|
||||
一个共同原则贯穿其中:**失败要么有兜底、要么标记隔离,绝不污染主链、绝不落坏数据**。
|
||||
|
||||
---
|
||||
|
||||
## 9. 实测证据与验收口径
|
||||
|
||||
这套架构的核心立论已在代码层**双重验证**,不是纸面设计:
|
||||
|
||||
1. **spike(小范围验证性实验)证地基**:便宜模型对 `source-project.schema.json` 产出连贯的游戏定义,准确率 **92–100%**,单款成本约 **¥0.011**,行为模块带真逻辑 JS。
|
||||
2. **build 段证可玩**:游戏定义经"从源构建"流程,在**真浏览器里跑过九门**——deepseek-v4-flash 满分、便宜模型 3/4 款过门,有截图实证。
|
||||
|
||||
由此"改源不改包 / 可维护源项目 / 把大模型当工作室"在代码层成立。配套产出了"运行时访问约定 v0"(便宜模型只能经一个受控的 `rt` 对象访问引擎能力)和"从源构建"的装配原型。
|
||||
|
||||
**九门**是这套质检的核心,指九道确定性的真玩自动门(serve-and-play 起服务、CDP 真浏览器玩、build 构建等组成的 harness),它们的脚本是子进程复用、**禁止重写**的既有资产。
|
||||
|
||||
最终验收口径分三层:
|
||||
|
||||
- **后端线门**:编译 + 单测绿、Flyway V18 迁移绿、救场阶梯单测(5 次升档 / 8 次 giveup + dump 落盘)、trace 字节兼容、modify 局部性。
|
||||
- **引擎+前端线门**:2D 适配器把定义渲成可玩、九门真玩绿、asset 产消打通、构建确定性(字节等价)、前端创作/修改/预览真机走查。
|
||||
- **联合 spike 门**:缓存透传 ✅ 已绿(76–93%);classify 准确率 ✅ 已绿(tickModel 100% / archetype 93%、约 $0.003/次);端到端门(待实施后验)= 一句话 → 工作室 → 源项目 → 构建 → 九门过门 → GamePackage → 信息流真玩,成功率对齐 MVP **≥80%**、单款 **< $1**(¥0.15 门)、modify 局部性达标。
|
||||
|
||||
> **核心安全垫**:GamePackage 产物 schema、宿主装载契约、回调唯一写入路径、九门 harness **全不变**。所以任一新增环节失败,生成主线都能回退到现有 worker 直产 bundle + 现 11 节点 SAA 图,发布链和信息流真玩**零回归**。
|
||||
|
||||
---
|
||||
|
||||
## 10. 范围、归属与开工前待解
|
||||
|
||||
**本期范围内**:固定游戏定义模型 + 2D 适配器、源项目契约 + 独立 DB、SAA 图扩节点(classify/asset/modify + 叙事分支 + 救场阶梯 + 完整观测)、唯一确定性构建 API、三段式缓存 + 版本化 Prompt Registry、trace+cost 扩展、modify 生命周期。
|
||||
|
||||
**本期范围外**:3D 适配器(Cocos,Phase 2,格式留前向兼容)、Template Skill(只做 Debug Skill)、per-creator 计量计费(沿用 new-api 现有对账)、渠道线(小游戏)引擎(另行竞标)、以及打包产物 schema / 宿主 / 信息流的任何改动(零改,收窄影响范围)。
|
||||
|
||||
仍需在开工门逐条收口的**待解项**(诚实列出,不糊弄):
|
||||
|
||||
1. **源项目物理归属**:`game_source_project` 归 studio 模块还是 project 模块?**倾向 studio 模块**(create/modify 编排入口在此),待创始人/两线拍板。
|
||||
2. **后端构建桩的范围**:`RuntimeBuildServiceImpl` 本期建议只接"消费构建产物落包"路(已通),不强求它自驱 esbuild。
|
||||
3. **classify→archetype 映射语义**:确认是"引导"而非"校验"。
|
||||
4. **叙事失败计入救场口径**:确认 narrative 评审的 `needsRepair=true` 等价一次九门失败计入 failCount,且评审自身 LLM 失败也计入(避免抖动卡死)。
|
||||
5. **studio 路由形态**:`/studio/{create,modify,extend}` 为净新增,`create` 建议做成 `draft+generate` 的一步式封装(additive,不破现有两步路)。
|
||||
|
||||
---
|
||||
|
||||
## 11. 相关文档
|
||||
|
||||
| 文档 | 关系 |
|
||||
|---|---|
|
||||
| [架构域主文档](../README.md) | 上层导航:本文是"生成引擎"方向的一份子设计 |
|
||||
| [产品域主文档](../../产品/README.md) | 本文实现的是产品域"一句话造游戏"那一环 |
|
||||
| `contracts/agent-loop/source-project.schema.json` | 契约① 字段级定义 |
|
||||
| `contracts/game-package.schema.json` | 契约③ 既冻产物 schema |
|
||||
| `.agents/skills/saa-graph-orchestration.md` | SAA 裸状态图编排手册 |
|
||||
| `.agents/skills/cheap-model-game-generation.md` | 便宜模型造游戏的 worker loop 与九门 harness |
|
||||
|
||||
> **纪律**:本文只讲"固定游戏架构 + SAA 工作室怎么设计、为什么";字段级 schema、逐步实现清单、过程取证留在 `contracts/` 与原始 execution 文档里,各司其职、互不重复。
|
||||
141
docs/architecture/架构/生成引擎/开闸接线.md
Normal file
141
docs/architecture/架构/生成引擎/开闸接线.md
Normal file
@ -0,0 +1,141 @@
|
||||
# 开闸接线 · 一句话入口的端到端接线
|
||||
|
||||
> **这是什么**:本文讲清楚"开闸"这件事——把绘境AI 的一句话创作主链,从用户在创作页敲下一句话,一路接到这款游戏被种子用户在游戏信息流里真人试玩。它回答的不是"生成引擎内部怎么造游戏"(那条线在[生成引擎主文档](README.md)与 [SAA 编排](SAA编排.md)里),而是"造游戏这台机器已经就绪,但用户却按不动开始键——这道门为什么关着、怎么把它打开"。
|
||||
> **给谁看**:接这条链路的后端与前端工程师、做开闸决策的创始人、想看清"护城河闭环对外开放的最后一公里卡在哪"的人。
|
||||
> **怎么读**:先读 §1 拿到结论(堵点在哪、怎么解、为什么风险低),再看 §2 那张接线图建立全局,然后按需深入 §3 的现状取证与 §4 的三处协同改动。
|
||||
|
||||
开闸是绘境AI 护城河闭环"对外迈出第一步"的动作。后端那台生成机器已经建成、也真机验过;真正卡住的,是创作页一道**已经没有钥匙的门**——它要求用户"先选一个玩法模板",而模板这一层早已被清理掉了。本文讲的就是怎么把这道门拆掉,让一句话能直接驱动生成。
|
||||
|
||||
---
|
||||
|
||||
## 1. 结论先行
|
||||
|
||||
把整件事浓缩成几句话:
|
||||
|
||||
- **后端的生成引擎和游戏信息流,已经就绪并真机验过。** 提交生成的入口(submitGenerate)、生成前的控制平面(下文 D12)、生成前的合规审查(下文 GP9)、一句话生成主路(基于 SAA 编排,已真库验)、管理员审核台、信息流翻转上架——这整条链都已打通。开闸**唯一的工程堵点,是创作页正处在一个"迁移空窗"里**。
|
||||
- **病根是一道关着、却已没有钥匙的门。** 创作页 `Create.vue` 把整条创作流**硬门控在"必须先选模板"**上:能不能点"开始生成"的判断是"选了模板 且 输入框非空",提交时也是先建一份必须带模板 ID 的草稿、再带着模板 ID 去生成。但与此同时,一项叫 **W-CLEAN** 的清理工作(把旧的"游戏模板 / 填参"产线整套拆掉)已经把玩法模板层废掉了——于是模板列表接口返回空,用户没有任何模板可选,"开始生成"按钮被永久禁用,**一句话创作主链就此结构性断裂**。
|
||||
- **接线的本质,是把创作流从"模板驱动"改成"一句话驱动"。** 这要三处协同:契约层把模板 ID 改成可选(缺省走通用生成路)、前端拆掉创作页的模板门、后端让建项目与提交生成都能接受"没有模板"的请求(通用生成路本就不依赖模板,且已真机验过)。**改动小、向后兼容、风险低**——不是要造新东西,只是拆掉一道已经没有钥匙的门。
|
||||
- **开闸放种子需要三件齐备,缺一不可。** 它们分属三类不同的资源,必须同时到位:本文这条接线(前端 + 后端)、把两个安全漏洞收进内网(运维侧)、以及一份创作者白名单(让创始人能亲自下场试玩)。本文只负责其中第一件——接线。
|
||||
|
||||
这里出现了几个内部代号,先一次性解释清楚,后文不再重复:
|
||||
|
||||
- **D12 控制平面**:生成任务唯一入口前焊的一组保护门——降级开关、配额与并发限制、背压保护、记账骨架。它的存在是为了保住后台那条**全局串行**(一次只处理一个任务)的生成流水线不被压垮。"D 系列"是这套体系里给各道质量 / 控制门排的编号,本文只涉及与开闸相关的部分。
|
||||
- **GP9 合规先行**:在生成**开始之前**,先审查用户那句话(prompt)是否违规——违规的不入队、不进信息流。"GP 系列"是合规门的编号。
|
||||
- **SAA**:Spring AI Alibaba,阿里在 Spring 生态里的 AI 编排框架;绘境AI 的生成主线用它的有向状态图原语手写编排(详见 [SAA 编排](SAA编排.md))。
|
||||
- **generic(通用生成路)**:不依赖任何玩法模板、由模型直接按一句话写码的那条生成路径。它是 W-CLEAN 清理之后的生成主线,已经过真库验证。
|
||||
- **R3 真库验**:对通用生成路做的一次真实数据库验证——确认"一句话 → 触发 SAA 生成 → 把执行轨迹(trace)与就绪度(readiness)落进数据库"这条链确实跑通。它是本文判断"通用路可靠"的证据基础。
|
||||
|
||||
---
|
||||
|
||||
## 2. 一张端到端接线图:从一句话到首局可玩
|
||||
|
||||
下面这张图是开闸后整条链路的全貌。读图时抓住三段就好:**用户在创作页敲一句话提交(去掉模板门)→ 后端层层过门后走通用生成路造出游戏(轮询进度)→ 过发布门后真入信息流、被种子用户真人试玩**。
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
A["创作页 · 一句话输入<br/>(拆掉模板门)"] -->|"输入非空即可提交"| B["POST /aigc/generate<br/>(模板 ID 省略 = 走通用路)"]
|
||||
B --> C["submitGenerate<br/>D12 配额 / 并发 + GP9 合规门"]
|
||||
C --> D["generic · SAA 生成路<br/>(R3 已真库验 · 不依赖模板)"]
|
||||
D --> E["轮询 /aigc/task/:id<br/>(进度页已接好)"]
|
||||
E -->|"成功 · 返回 gameId / versionId"| F{"发布门<br/>(待创始人裁定 · 见 §5 C1)"}
|
||||
F -->|"管理员审核台 · 建议口径"| G["上架 PUBLISHED → 游戏信息流"]
|
||||
G --> H["种子用户刷信息流 · 真人试玩"]
|
||||
|
||||
style C fill:#f5b7b1
|
||||
style D fill:#a9dfbf
|
||||
style H fill:#a9dfbf
|
||||
```
|
||||
|
||||
图里几处值得展开:
|
||||
|
||||
- **左端"拆掉模板门"是本次接线的全部工程动作所在。** 当前这里卡死的逻辑是"选了模板 且 输入非空才能提交";改完之后变成"输入非空即可提交",模板 ID 在提交时不再强制携带。
|
||||
- **中段标红的 submitGenerate,是所有保护门的汇聚点。** 请求进来先过 D12 的配额 / 并发 / 背压,再过 GP9 的合规审查,全部放行才真正入队走生成。这两道门在本次接线中**原封不动**——开闸只是让请求能走到它们面前,而不是绕过它们。
|
||||
- **生成走的是标绿的通用路(generic),不是任何新机制。** 这条路 W-CLEAN 之后就是主线,且已被 R3 真库验过。开闸不引入任何新的生成方式,这正是风险低的根本原因。
|
||||
- **进度页与轮询已经接好,不在本次改动范围内。** 进度页 `/create/task/:taskId` 走轮询 `/aigc/task/{id}` 已经能用;采用轮询而非服务端推流(SSE),是一个有意的简化——现阶段轮询足够,流式推送是后续增强项。
|
||||
- **右端的发布门是一个待裁定项(§5 C1)。** 生成成功后,游戏是经管理员审核台把关后入信息流,还是创作者自助直发,需要创始人拍板。图中按当前建议口径(管理员把关)画。
|
||||
|
||||
---
|
||||
|
||||
## 3. 现状:这道门确实关着(已取证)
|
||||
|
||||
为了让"病根"不停留在判断,下面把现状逐条落到代码位置上。这些都是已验证事实,不是推断:
|
||||
|
||||
| 环节 | 现状 | 出处 |
|
||||
|---|---|---|
|
||||
| 创作页提交条件 | "能否提交 = 选了模板 且 输入非空";提交时先建必须带模板 ID 的草稿,再带着模板 ID 去生成 | `game-studio/src/views/create/Create.vue:53,104-116` |
|
||||
| W-CLEAN 空窗 | 模板列表接口返回空 → 创作页落到"升级中"空态、没有模板可选 → 提交按钮禁用(代码诚实地区分了"升级中"与"加载失败"两种空态) | `Create.vue:162-169` |
|
||||
| 契约约束 | `/aigc/generate` 要求"输入非空 且 模板 ID 存在";生成请求体 `AigcGenerateReqVO` = 输入 + 模板 ID;建项目请求体 `ProjectCreateReqVO` 把 标题、模板 ID 列为必填 | `contracts/api-schemas/aigc.yaml:38` |
|
||||
| 后端通用生成路 | 通用路 / SAA 路**本就不依赖模板**(由模型直接写码,是 W-CLEAN 之后的主线),且已 R3 真库验(一句话 → 执行轨迹与就绪度落库) | R3 verdict |
|
||||
| 进度与发布 | 进度页轮询已接好;生成完发布进信息流走管理员审核台(这条链路已验过) | `aigc.yaml:55` |
|
||||
|
||||
把这张表读成一句话:**断点不在生成能力,而在创作页那道"必须先选模板"的提交门——而模板这把钥匙已经被 W-CLEAN 收走了。** 所以接下来要做的不是造新东西,是拆掉这道门。
|
||||
|
||||
---
|
||||
|
||||
## 4. 推荐方案:三处协同的最小接线
|
||||
|
||||
接线遵循**契约先行**——先把契约改对,前后端再各自落地。三处改动如下,合起来就是开闸所需的全部工程量:
|
||||
|
||||
### 4.1 契约层:模板 ID 改可选,缺省走通用路
|
||||
|
||||
把生成请求体与建项目请求体里的模板 ID 都从必填改为**可选**,缺省时默认取 `generic`(通用路标识);`/aigc/generate` 的描述也从"模板 ID 存在"改成"省略则走通用一句话生成"。这一步是**向后兼容**的:老的、带模板 ID 的调用依旧能正常工作,只是新增了"不带模板 ID"这条合法路径。
|
||||
|
||||
### 4.2 后端:让建项目与提交生成都能接受"没有模板"
|
||||
|
||||
`project.create` 与 `submitGenerate` 在收到不带模板 ID 的请求时,落到 `generic`,然后复用那条已经验过的通用 / SAA 生成路。**控制平面(D12)与合规门(GP9)不动**——它们照常在入口前把关。后端这一侧改动很小,且完全不触碰这两道保护门的逻辑。
|
||||
|
||||
### 4.3 前端:拆掉创作页的模板门
|
||||
|
||||
`Create.vue` 的提交条件改成"输入非空即可"(去掉模板门);原本的模板选择区,降级为**可选的品类 / 风格轻提示**(纯粹用来帮用户起步、缓解空白焦虑,不是模板,也不强制)或暂时隐藏;提交时不再强制携带模板 ID。设计体系所需的样式 token 已经就绪。
|
||||
|
||||
三处的关系可以用下面这张分层图看清——契约是中间的契约层,前后端各自向它对齐:
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
subgraph 前端["前端 · game-studio"]
|
||||
FE["Create.vue<br/>提交条件 = 输入非空<br/>模板区降级为可选轻提示"]
|
||||
end
|
||||
subgraph 契约["契约层 · contracts/(先行)"]
|
||||
CT["模板 ID 改可选<br/>缺省 = generic<br/>(向后兼容)"]
|
||||
end
|
||||
subgraph 后端["后端 · game-cloud"]
|
||||
BE["project.create / submitGenerate<br/>接受无模板 → 落 generic<br/>D12 / GP9 不动"]
|
||||
end
|
||||
FE -->|"按新契约提交"| CT
|
||||
CT -->|"按新契约实现"| BE
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 关键权衡与待裁定项
|
||||
|
||||
有四件事需要创始人拍板。它们都不是工程难点,而是产品 / 策略上的取舍:
|
||||
|
||||
- **C1 · 发布门怎么走。** 种子创作者生成游戏后,是经**管理员审核台把关后入信息流**(当前现状,合规与质量双重兜底),还是**自助直发**?建议种子期采用**管理员把关**——GP9 已在生成前挡住违规输入,审核台再过一道人 / 质把关,放种子最稳妥;后续可以加"就绪分达标即可自助发"。
|
||||
- **C2 · 模板 ID 留还是删。** 改成**可选、默认 generic**(改动最小、向后兼容),还是彻底移除(对契约是破坏性变更)?建议**保留为可选默认**。
|
||||
- **C3 · 一句话之外要不要给提示。** 保留**可选的品类 / 风格轻提示**(不是模板,纯粹是输入增强,帮用户起步、降空白焦虑),还是只留纯粹的一句话最简形态?建议**保留可选轻提示**(不强制)。
|
||||
- **C4 · 创作者白名单口径。** submitGenerate 现有一道创作者白名单校验;种子放量时"谁能创作"的白名单口径需要明确。
|
||||
|
||||
---
|
||||
|
||||
## 6. 爆炸半径 · 兼容性 · 风险
|
||||
|
||||
这次接线的风险评估很清楚,可以放心推进:
|
||||
|
||||
- **契约把模板 ID 改可选 = 向后兼容**,老调用不会被破坏;通用生成路已 R3 真库验,后端改动小、不碰控制平面与合规门。
|
||||
- **前端 `Create.vue` 的改动需要前端与契约协调落地**(本文定契约,前端接线,设计体系 token 已就绪)。
|
||||
- **整体风险低**:不引入任何新的生成机制,只是"解开模板门 + 走已验过的通用路 + 契约把模板 ID 改可选"三件事的组合。回滚也简单——契约与前端各自 revert 即可。
|
||||
|
||||
---
|
||||
|
||||
## 7. 验收口径(真机 · money-shot)
|
||||
|
||||
开闸是否成功,以一条端到端真机演示为准,而不是单测通过:
|
||||
|
||||
- **主验(护城河可演示的那一镜)**:种子创作者输入一句话 → 真实生成(通用 / SAA 路)→ 管理员发布 → 真入游戏信息流 → **真人在浏览器里试玩**——全链路打通,护城河闭环对种子开放、可演示。
|
||||
- **门验**:违规输入被 GP9 阻断;超配额请求被拒;失败路径给用户可读的提示且可重试。
|
||||
- **兼容验**:若仍存在带模板 ID 的老调用,它们依旧正常工作。
|
||||
|
||||
---
|
||||
|
||||
> **验证状态**:本文为架构策展文档,由开闸接线 review 版(2026-06-17,6c6g 出,待创始人 2 轮评审)改写而成,未改任何代码。承重硬事实——堵点 = `Create.vue` 模板门(`canSubmit = 选模板 ∧ 输入非空`,`Create.vue:53,104-116` / 空窗 `162-169`)、契约约束(`aigc.yaml:38`,模板 ID 必填 → 改可选默认 generic)、通用生成路 template-free 且 R3 真库验、进度页轮询 `/aigc/task/{id}` 已接、D12 控制平面 + GP9 合规先行两道门开闸时不动、放种子三件(接线 ∧ 2 安全洞收内网 ∧ 创作者白名单)缺一不可、四个待裁定项 C1–C4——均沿用源档结论;品牌统一为"绘境AI",无旧名残留。
|
||||
325
docs/architecture/架构/生成引擎/引擎与运行时.md
Normal file
325
docs/architecture/架构/生成引擎/引擎与运行时.md
Normal file
@ -0,0 +1,325 @@
|
||||
# 引擎与运行时 · 架构设计文档
|
||||
|
||||
> **这是什么**:绘境AI 游戏引擎层与运行时装载体系的架构设计主文档,回答「选了哪个引擎、为什么选、游戏怎么挂上去、包怎么交付」。
|
||||
> **给谁看**:后端 / 前端工程师、AI 生成链路开发者、引擎集成评审方。
|
||||
> **怎么读**:先读第 1–2 节建立全局认知(选型结论 + 一张架构图),再按模块深入第 3–5 节。
|
||||
|
||||
---
|
||||
|
||||
## 1. 一句话与一张图:引擎层做什么
|
||||
|
||||
绘境AI 的游戏以「信息流即刷即玩」的方式送达玩家,首屏冷启动时间直接决定用户是否留下来。引擎层的核心任务就是:**让 AI 生成的游戏在千元机 + 4G 网络下,从点击到可玩不超过 3.5 秒**。
|
||||
|
||||
为此,整个引擎层由三件事共同支撑:
|
||||
|
||||
1. **选一个足够轻量的游戏引擎**——这是体积与冷开速度的根本。
|
||||
2. **定义一套稳定的装载契约**——让 AI 生成的游戏代码、引擎宿主、Runner(运行时载体)三者能通过同一套接口连接,不互相耦合。
|
||||
3. **明确能力边界**——哪些能力由引擎提供、哪些需要补层,边界清晰才不会让游戏代码直接依赖引擎内部。
|
||||
|
||||
下图展示了从「AI 生成的 bundle(游戏代码包)」到「在玩家设备上渲染出可交互画面」的完整链路:
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
subgraph gen["AI 生成侧(便宜模型 agent 按装载契约产出)"]
|
||||
BD["engineBundle<br/>游戏代码包<br/>随 manifest JSON 内嵌"]
|
||||
end
|
||||
|
||||
subgraph runtime["game-runtime · Runner v2(运行时)"]
|
||||
LOAD["bootGameHost<br/>装载入口"]
|
||||
HOST["集成段 host<br/>唯一 import 引擎处<br/>(Q4 铁律)"]
|
||||
CAP["engine-caps.js<br/>能力包装层<br/>particles / audio / math"]
|
||||
CTX["PluginContext<br/>受控面 6 项<br/>game-host.d.ts 第9类契约"]
|
||||
end
|
||||
|
||||
subgraph engine["LittleJS 增强发行版(Tier1 引擎 · 55KB gz)"]
|
||||
EI["engineInit<br/>五回调驱动主循环"]
|
||||
MC["mainContext<br/>唯一绘制面(Canvas 2D)"]
|
||||
end
|
||||
|
||||
subgraph game["游戏侧(装载契约·零引擎 import)"]
|
||||
GM["game.init(ctx)<br/>game.update(dt)<br/>game.render(g)<br/>game.onInput(ev)"]
|
||||
end
|
||||
|
||||
BD -->|window.__GameBundle 全局挂载| LOAD
|
||||
LOAD --> HOST
|
||||
HOST --> EI
|
||||
EI -->|gameUpdate 回调| GM
|
||||
EI -->|gameRender → mainContext| GM
|
||||
HOST --> CAP
|
||||
CAP --> CTX
|
||||
CTX -->|ctx.getEngine() 受控面| GM
|
||||
GM -. 禁止 .-> |直接 import littlejsengine| ERR["❌ Q4 违例"]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. 引擎选型:为什么是 LittleJS
|
||||
|
||||
### 2.1 终裁结论
|
||||
|
||||
**Tier1 游戏引擎 = LittleJS 增强发行版**(55KB gz,2026-06-12 创始人终裁,对比候选引擎 Phaser,计分 85 vs 82)。
|
||||
|
||||
LittleJS(轻量级 JavaScript 游戏引擎,GitHub 开源项目)是针对极简、快速加载场景设计的 2D 引擎,体积约为 Phaser(功能更全的主流 2D 引擎)的 1/10。
|
||||
|
||||
绘境AI 选择它的核心理由只有一条:**冷启动速度**。
|
||||
|
||||
### 2.2 硬数据对比
|
||||
|
||||
「冷开」(Cold Open,即用户首次打开游戏、从零加载到可交互状态的时间)是信息流场景的决定性指标——这是玩家点击一款从未玩过的游戏时的真实体验。
|
||||
|
||||
| 指标 | LittleJS | Phaser |
|
||||
|---|---|---|
|
||||
| 裸冷开时间(千元机 + 4G) | **0.54 秒** | 2.86 秒 |
|
||||
| gz 包体积 | **55KB** | ≈ 500KB+ |
|
||||
| 综合评分(S2 实测) | **85 分** | 82 分 |
|
||||
|
||||
首屏 P75(第 75 百分位用户的加载时间)目标是 **< 3 秒**,点卡可玩(从点击到可交互)目标是 **S2 常态 ≤ 2 秒**。Phaser 在千元机 + 4G 环境下的冷开时间已占去大部分预算,3G / 弱网长尾用户的出线风险显著上升,因此不适合绘境AI 的分发场景。
|
||||
|
||||
敏感性分析的结论是:**即使把四组主观裁量维度全部拉平(双方同分),LittleJS 仍凭 S2 硬数据差(20 分 vs 12 分)领先**——选型结论对裁量分不敏感。
|
||||
|
||||
### 2.3 体积约束的演进
|
||||
|
||||
早期曾有「13KB 极限」(js13k 游戏极限分支的约束)和「15KB 红线」(srcdoc 内联嵌入架构的衍生约束)两条旧规则。这两个前提架构已废除,对应约束一并失效。**请勿再引用「13KB」或「15KB」口径。**
|
||||
|
||||
现行约束是**三层框架**:
|
||||
|
||||
- **SLO 地板**(服务质量下限,必须满足):千元机 + 4G 首屏 P75 < 3 秒;点卡可玩 S2 常态 ≤ 2 秒。
|
||||
- **预算入场券 B1**(超出则体积风险预警):gz ≤ 350KB、raw ≤ 1.5MB。
|
||||
- **S2 实测为主考**:以真机在 S2(标准 2G/4G 弱网)环境下的实测数据作为最终评判依据。
|
||||
|
||||
### 2.4 复议条件
|
||||
|
||||
终裁包保留复议权(终裁包 §4.5-3):**若后续拔高样板游戏时暴露引擎级阻塞,可复议**。切换成本仅限模板层重写,Runner(运行时载体)和装载契约层不受影响——这是两层架构设计的重要价值之一。
|
||||
|
||||
---
|
||||
|
||||
## 3. 模板哲学:模板是什么,不是什么
|
||||
|
||||
在绘境AI 的语境里,「模板」一词有严格的边界定义。厘清这一点,可以避免生成链路的实现方向走偏。
|
||||
|
||||
### 3.1 宪法级定义
|
||||
|
||||
**模板 = LittleJS 能力插件 / 二次开发件**。每个插件封装一项引擎能力(碰撞检测、粒子特效、物理模拟、手感反馈……),对外只暴露公开 API,可即插即用、可组合、版本化管理。
|
||||
|
||||
**模板不包含**:玩法设计、美术成品、关卡数据、UI 布局——这四项是 AI agent 在生成游戏时的**生成域**,由 agent 按游戏定义产出,不由模板预制。
|
||||
|
||||
### 3.2 「游戏模板」与「玩法模板」的区分
|
||||
|
||||
历史上曾有「游戏模板」概念,即填参式的整局代码、预先写好的 4 套游戏。这类模板已于 W-CLEAN(清场阶段,对应 Wave 清场任务)中删除,不再存在。
|
||||
|
||||
「玩法模板」(品类框架,用于引导 AI 生成特定品类的游戏,本身是框架性描述而非预建代码)则是有效功能,状态是**待建、非最高优先级**——最高优先级是 Tier 0 生成可靠性(即 AI 能稳定地生成可运行的游戏),玩法模板排其后。
|
||||
|
||||
这一区分由 **HJ-DEMO-AUDIT-001**(2026-06-17 创始人纠偏裁定,本档唯一权威口径)确立。历史 spec 中出现的「玩法模板永久废除」措辞属 2026-06-12 旧记录,已被本节取代。
|
||||
|
||||
### 3.3 可测性红线(硬门)
|
||||
|
||||
AI agent 生成的游戏**必须导出取证清单**:可交互几何(画布上的可点击区域)、锚点接线(事件绑定关系)、胜败可达断言(存在可以触发游戏结束的路径)。**不可机器测试的游戏不许通过评估门。**
|
||||
|
||||
好玩基线 v2 五要素(手感 / 美术统一 / 音乐 / 结构深度 / 角色壳)已从模板层迁出,挂在评估门上判结果值,不由模板预置。
|
||||
|
||||
---
|
||||
|
||||
## 4. 两层装载契约:引擎、宿主、游戏如何连接
|
||||
|
||||
这是引擎层最核心的架构设计,解决「AI 生成的游戏如何安全、稳定地挂载到引擎上」的问题。
|
||||
|
||||
### 4.1 为什么需要两层
|
||||
|
||||
历史上存在三个「断口」:
|
||||
- **真引擎宿主**:有掌帧(控制渲染循环),但没有游戏挂进去。
|
||||
- **真游戏 ref**:有游戏代码,但没接引擎。
|
||||
- **生成产物**:AI 生成的代码喂给一个掏空的旧版 runtime(运行时),实际跑不起来。
|
||||
|
||||
如果给三者各造一套接引擎的方式,就会产生三套漂移——这违反「同一职责不留两条并行路」的边界原则,并且迟早需要三次返工才能对齐。两层装载契约的设计,就是给三者提供一份共同遵守的约定,让它们通过同一套接口连接。
|
||||
|
||||
### 4.2 下层契约(已冻结)
|
||||
|
||||
下层解决「插件如何安全获取引擎能力」和「游戏包如何交付」。
|
||||
|
||||
- **`PluginContext.getEngine()`**:受控引擎能力面,共 6 个方向,定义在 `api.d.ts`。插件只能通过这个接口获取引擎能力,不能直接访问引擎内部。
|
||||
- **GamePackage `engineBundle`**:契约 #4(additive 追加字段),游戏代码包内嵌于 manifest JSON,随包交付。
|
||||
- **SDK storage 根契约**:游戏存档与状态持久化的统一接口。
|
||||
|
||||
下层于 commit `28a57b8` 冻结,不再修改。
|
||||
|
||||
### 4.3 上层契约(本弧 spike 实现)
|
||||
|
||||
上层解决「一款完整游戏(不只是插件)如何挂到引擎宿主」。
|
||||
|
||||
**游戏宿主装载契约**,定义在 `game-runtime/src/core/game-host.d.ts`,是**第 9 类契约(additive 追加)**。它落在 game-runtime 内部而非 `contracts/` 顶层目录,原因是消费方只在 game-runtime 这一个代码库内,放到跨端契约目录会污染其他消费方。
|
||||
|
||||
游戏侧只需实现四个函数:
|
||||
|
||||
```typescript
|
||||
// game-host.d.ts · 第9类契约(精简示意)
|
||||
interface GameModule {
|
||||
init(ctx: PluginContext): void; // 初始化,接受受控引擎面
|
||||
update(dt: number): void; // 每帧逻辑更新,dt=帧间隔秒
|
||||
render(g: CanvasRenderingContext2D): void; // 渲染,g=引擎 mainContext
|
||||
onInput(ev: InputEvent): void; // 输入事件
|
||||
}
|
||||
```
|
||||
|
||||
### 4.4 四条不可破约定
|
||||
|
||||
这四条约定是装载契约的护城墙,违反任何一条都会导致渲染错位、帧率失控或引擎版本耦合:
|
||||
|
||||
| 约定 | 内容 | 违例后果 |
|
||||
|---|---|---|
|
||||
| 掌帧唯一源 = 引擎 | 游戏不自起 RAF(RequestAnimationFrame,浏览器原生动画帧循环),`update/render` 由 `engineInit` 五回调驱动 | 双帧循环冲突,帧率翻倍或紊乱 |
|
||||
| 绘制面唯一 = 引擎 `mainContext` | 所有美术与粒子渲到同一张 Canvas,`setGLEnable(false)` 纯 2D 模式 | 渲染层分裂,游戏画面与引擎效果错位 |
|
||||
| 插件能力唯一面 = `ctx.getEngine()` | 引擎有的能力→薄包装提供;引擎缺的→插件补层,但都经同一接口 | 游戏代码直接依赖引擎内部,引擎升级即断 |
|
||||
| 引擎 import 唯一活点 = 集成段 host | 游戏代码和插件代码零直接 import LittleJS(Q4 铁律,即第 4 季度确立的最终规则) | 多处 import 导致引擎多实例,状态不一致 |
|
||||
|
||||
### 4.5 装载时序
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Feed as 游戏信息流
|
||||
participant Runner as Runner v2 (bootGameHost)
|
||||
participant Host as 集成段 host
|
||||
participant Engine as LittleJS engineInit
|
||||
participant Game as 游戏模块 (game.js)
|
||||
|
||||
Feed->>Runner: 用户点击游戏卡片
|
||||
Runner->>Runner: 解析 manifest JSON,取出 engineBundle
|
||||
Runner->>Host: 挂载 window.__GameBundle
|
||||
Host->>Engine: engineInit(五回调注册)
|
||||
Engine-->>Host: 引擎主循环启动
|
||||
Host->>Host: createHostDevContext → PluginContext 受控面
|
||||
loop 每帧
|
||||
Engine->>Game: gameUpdate 回调 → game.update(dt)
|
||||
Engine->>Game: gameRender 回调 → game.render(mainContext)
|
||||
Game->>Host: ctx.getEngine() 取能力(如需)
|
||||
end
|
||||
Game-->>Feed: phase==='gameover' 轮询 → 游戏结束事件
|
||||
```
|
||||
|
||||
注意最后一步:游戏结束时,游戏模块通过将 `phase` 状态字段置为 `'gameover'` 来通知宿主,宿主轮询这个字段(`game_end` 靠 latch 终态非 emit)。游戏模块没有主动推送通道,不能也不应该直接 emit(发射)事件给宿主。这一设计经 commit `238ec2d`(井字棋真玩入 feed)验证。
|
||||
|
||||
---
|
||||
|
||||
## 5. engineBundle 交付链与能力边界
|
||||
|
||||
### 5.1 包交付路径
|
||||
|
||||
AI 生成的游戏最终以 `engineBundle` 字段的形式随 manifest JSON 内嵌交付,而不是通过独立 URL 从 OSS(对象存储)加载。这个决策有两个好处:
|
||||
|
||||
- **省一条基建线**:不需要部署和维护独立的 OSS 服务来托管游戏包。
|
||||
- **整包单一校验面**:manifest JSON 本身带 sha256 校验,游戏代码随包一起验证,不存在「manifest 版本与包版本不一致」的窗口。
|
||||
|
||||
装载时序:manifest JSON 下发 → 取出 `engineBundle` 字段 → `window.__GameBundle` 全局挂载 → `bootGameHost({canvas, seed})` 装载启动。
|
||||
|
||||
`packageUrl` 外链字段(OSS 切换后用)和 `immutable` 强缓存头,是**显式标注的 future-state 占位**(未来可能启用的预留字段),当前未启用,不是孤儿设计,有明确的启用场景。
|
||||
|
||||
### 5.2 引擎能力边界三分
|
||||
|
||||
引擎能力分三类处理,边界由 `engine-plugin-boundary-model`(引擎插件边界模型)管理:
|
||||
|
||||
**引擎原生有 → 薄包装(6 件)**:
|
||||
|
||||
| 能力 | LittleJS 提供 | 包装方式 |
|
||||
|---|---|---|
|
||||
| 粒子系统 | `ParticleEmitter` | 薄包装暴露给插件 |
|
||||
| 音频合成核 | `zzfxG` / `zzfxM` | 薄包装暴露合成接口 |
|
||||
| 数学工具 | `lerp` / `smoothStep` / `easing(Ease)` | 薄包装统一门面 |
|
||||
|
||||
**引擎没有 → 自研补层(4 件)**:
|
||||
|
||||
| 能力 | 为何需要补层 |
|
||||
|---|---|
|
||||
| 碰撞检测(collision) | 引擎只返回 boolean(是否碰撞),补层返回 MTV `Manifold`(碰撞法向量)和 `RayHit`(射线命中点),给游戏更精细的物理信息 |
|
||||
| 轻量物理(physics-lite) | 引擎是全刚体仿真(精确但重),补层提供半隐式欧拉抛体 / 弹簧 / 运动学约束(够用且轻量) |
|
||||
| 后处理滤镜(palette-post) | 引擎后处理走 WebGL 语义,与纯 2D 模式不兼容;P5(第5优先级渲染层)红线规定只换色不给成品色板,补层提供 vignette / scanline / dither |
|
||||
| 音频播放补层 | 引擎合成核不烤播放层增益,补层显式补 `×0.3` master 音量 |
|
||||
|
||||
**铁律**:A2(引擎接线阶段 2)禁留 sim(模拟器存根),A3 禁 vendored(供应商打包)转正——二者均有意替代,源码可证伪。自研只限「引擎之外的补层」,不替代引擎本体。
|
||||
|
||||
### 5.3 历史兼容
|
||||
|
||||
插件公开 API(如 `EmitterConfig`)一经冻结不变,老游戏照跑。包装层和补层是实现内部事,公开接口稳定性由下层契约保证。
|
||||
|
||||
---
|
||||
|
||||
## 6. Runner v2 三阶段收口记录
|
||||
|
||||
Runner(运行时,负责把游戏 bundle 装载进引擎并呈现给玩家)经过三个阶段(P1 → P2 → P3)完成实质收口,于 2026-06-15 T1b-β(T1b 第二个 beta 迭代,引擎接线与 Runner 第二轮验证阶段)阶段完成。
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
P1["P1 · 装载契约 spike<br/>game-host.d.ts 建立<br/>实证 5 门"] --> P2["P2 · 通用宿主泛化<br/>bootGameHost 落地<br/>引擎游戏真渲入 feed<br/>真机 6 门"] --> P3["P3 · 派发面解封<br/>SUPPORTED_TEMPLATE_IDS<br/>接通生成主线"]
|
||||
|
||||
style P1 fill:#e8f4e8
|
||||
style P2 fill:#e8f4e8
|
||||
style P3 fill:#e8f4e8
|
||||
```
|
||||
|
||||
**P1 装载契约 spike**:建立 `game-host.d.ts`(第 9 类契约),实证 5 个验收门,确认契约可行。spike(尖刺,指为验证可行性做的最小实现)阶段只验证接口,不做泛化。
|
||||
|
||||
**P2 通用宿主泛化**:`bootGameHost` 通用宿主落地,引擎游戏真实渲入信息流(**不是「重构中」的占位,而是真渲染**),真机验证 6 门。视觉终局:井字棋游戏空盘 → 落子 → 「X 胜!」全程在 feed 中真渲入。
|
||||
|
||||
**P3 派发面解封**:`SUPPORTED_TEMPLATE_IDS = List.of("generic")` 解封,接通 AI 生成主线(generic 是通用模板标识,意味着 Runner 不再只接特定 ID 的游戏,而是接受 AI 生成的任意游戏 bundle)。
|
||||
|
||||
**Phase A 引擎真接线**(与 Runner v2 并行):A0 引擎掌帧接管(门0 8/8)+ A1–A4 能力包装 + A6 真机门 6/6,动态 call-ID 取证验证真实调用链(`particles.spawnEmitter` / `audio.synth.synthSfx` / `math.easing.quadIn` 均验证为真调引擎叶方法)。
|
||||
|
||||
**零灰度闭环缺口、零 split-brain**(split-brain,即「脑裂」,指系统中两个部分对同一状态持有不一致的认知,是分布式系统和前后端协作的常见问题)——这是收口的核心验收标准。
|
||||
|
||||
---
|
||||
|
||||
## 7. 当前状态与待办
|
||||
|
||||
### 7.1 已完成(截至 2026-06-15 T1b-β)
|
||||
|
||||
- 引擎选型终裁落锤(LittleJS,55KB gz,85 分)
|
||||
- Phase A 引擎真接线(A0–A6 全部通过真机门)
|
||||
- Runner v2 全弧(P1 → P2 → P3 完成)
|
||||
- 井字棋真玩入 feed 视觉终局验证(commit `238ec2d`)
|
||||
|
||||
### 7.2 当前最高优先级(在飞)
|
||||
|
||||
**W-G1 开闸验收门**(Wave G1,即便宜模型游戏生成第一波验证):便宜模型(低成本 AI 模型)agent 按装载契约产出 bundle,九门 harness(九个验收门组成的自动化测试框架)兜底,目标是全 20 款游戏 bake-off(烤箱对比,逐款对比测试)+ 四模型横比 + Claude-free judge 门(使用非 Claude 模型作为评判者,避免自评偏差)。
|
||||
|
||||
### 7.3 Backlog(不在完成线关键路径)
|
||||
|
||||
以下属于技术债审计 / 样板拔高,不阻塞当前主线:
|
||||
|
||||
| 待办项 | 说明 |
|
||||
|---|---|
|
||||
| `render.js` 切引擎 draw API | 现为 100% Canvas2D,引擎掌帧已达成「经引擎画游戏」的目标;切 API 是样板拔高,不是必须 |
|
||||
| SIZES 单口径并表 | 现「插件增量主表 + 引擎产物附录」两口径并存,可合并 |
|
||||
| 逐像素 WebGL readPixels 回归 | A0 有意降级(real 通道追帧 overshoot 不可控 → 逐像素由 stub 2D 通道独占兜),回归属优化 |
|
||||
| `outcome:'win'|'lose'` additive 字段 | `completed` 仍是闭环权威判据;outcome 是增强信号,非必须 |
|
||||
|
||||
---
|
||||
|
||||
## 8. 关键指针
|
||||
|
||||
### 代码文件
|
||||
|
||||
| 文件 | 作用 |
|
||||
|---|---|
|
||||
| `game-runtime/src/core/game-host.d.ts` | 第 9 类装载契约(上层,游戏-宿主接口) |
|
||||
| `game-runtime/src/host/boot-game-host.js` | 装载入口实现 |
|
||||
| `game-runtime/src/host-dev/engine-caps.js` | 引擎能力包装层(6 件薄包装 + 4 件补层) |
|
||||
| `contracts/game-package.schema.json` | GamePackage 契约(含 `engineBundle` / `immutable` 字段) |
|
||||
| `contracts/sdk-interface.d.ts` | SDK storage 根契约 |
|
||||
|
||||
### 关键 commit
|
||||
|
||||
| commit | 内容 |
|
||||
|---|---|
|
||||
| `204eaed` | 装入引擎 |
|
||||
| `11c8eaf` + `7efe8a4` | A0 引擎掌帧接管 |
|
||||
| `e591e9b` | A1–A4 能力包装 |
|
||||
| `3e5cab4` | A6 真机 6 门 |
|
||||
| `99b6c82` | P1 装载契约 spike |
|
||||
| `5d3f7a8` | P2 通用宿主泛化 |
|
||||
| `238ec2d` | 井字棋真玩入 feed(视觉终局) |
|
||||
|
||||
### 延伸阅读
|
||||
|
||||
- **`.agents/knowledge/tech-decisions.md §1.1`**:引擎选型权威蒸馏(已对齐现行真相)
|
||||
- **`.agents/skills/add-game-template.md`**:玩法模板 / 插件 onboarding,含能力插件库 v1 P1–P10 字节预算清单
|
||||
- **`.agents/skills/game-e2e-cdp-harness.md §5/§6`**:引擎真接线门坑与 driver 六规则(CDP = Chrome DevTools Protocol,用于驱动浏览器的自动化协议)
|
||||
- **`.agents/skills/runtime-and-multichannel.md`**:打包 / 沙箱 / SDK / 多渠道导出手册
|
||||
- **`docs/agent-specs/_archive/`**:本档取代的 4 份历史 spec(T1 引擎终裁包 / W-T1b-Runner 双层模板 / runner-v2-arc / T1b-β 收口报告)
|
||||
228
docs/architecture/架构/生成引擎/设计合理性裁决.md
Normal file
228
docs/architecture/架构/生成引擎/设计合理性裁决.md
Normal file
@ -0,0 +1,228 @@
|
||||
---
|
||||
date: 2026-06-20
|
||||
topic: 生成设计合理性 · 对抗审查裁决
|
||||
status: 裁决 · 待创始人拍板(愿景分层)
|
||||
---
|
||||
|
||||
# 生成引擎 · 设计合理性裁决
|
||||
|
||||
> **这是什么**:对绘境AI 核心生成设计"**到底合不合理**"的一次对抗式深度审查裁决。它不讲这台机器怎么搭(那在[生成引擎主文档](README.md)),只回答一个判断题——我们押注的这套生成范式,是该坚持、该修补,还是该推倒。
|
||||
> **给谁看**:创始人、生成主线的架构负责人、做尽调时想看清"护城河到底硬在哪、天花板在哪"的人。
|
||||
> **怎么读**:先读 §1 看总裁决,§2 看该护住的真功夫,§3 看天花板与裂缝,§4 看具体怎么改,§5 看收口结论。
|
||||
> **品牌**:本文统一用「绘境AI」。
|
||||
|
||||
被审查的对象,是绘境AI 当前的核心生成设计:**声明式结构化源 + curated 运行时薄适配器 + 插件后置 + 九门验收**。用更白的话说,就是让便宜模型先吐一份结构化的游戏定义,平台用一套确定性的运行时去解释它,再用九道自动门把"是不是真能玩"判定下来。这套设计是绘境AI 护城河的关键路径,所以"它合不合理"这个问题,值得被四个互相挑刺的视角各读一遍真代码、再由总架构师拍板。
|
||||
|
||||
为避免这份裁决被当成某个审查者的个人偏见,它的产生方式本身是对抗式的:**四个临界视角各自独立审查——范式视角、能力面视角、产出质量视角、第一性原理视角**,每个视角都直接读运行时和构建链的真实代码去找天花板与裂缝,再由总架构师综合;每一条 fundamental(范式级)或 serious(严重)指控,都已逐条按 `文件:行号` 核实(依据见文末附录)。整个审查由 5 个 agent 经 6c6g 文档/设计线编排并打磨产出。这里先把两个会反复出现的分级词说清楚:**fundamental** 指"范式级病灶"——病在设计的根上,补丁补不掉;**serious** 指"严重但可补的工程缺口"——方向对、只是某处没做到位。这个区分是整份裁决的骨架,因为它直接决定了创始人**该不该动刀、动哪把刀**。
|
||||
|
||||
---
|
||||
|
||||
## 1. 一句话总裁决:部分合理
|
||||
|
||||
**部分合理。** 而且四个视角的分歧可以收敛成一句话:
|
||||
|
||||
> 这是一个为"便宜模型 + 自动验收"量身打造的、聪明的 **Tier0 超休闲生成范式**,工程取舍大半是对的;但它顶着"声明式结构化源"的名号,内核却是"声明式数据壳 + 一坨未受契约约束的自由 JS",而且表达力被运行时硬编码焊死在四类几何色块玩具上。它不是某个自由工程范式的"更聪明上位替代",而是一个"低复杂度特例"。**坚持它做 Tier0 是对的;拿它去够 demo 里 Marvel/KOF 那一档愿景,是范式选错。**
|
||||
|
||||
这里先解释三个本文的核心代号,后文不再重复:
|
||||
|
||||
- **Tier0**:绘境AI 内部对游戏复杂度的分层口径。**Tier0 = 超休闲轻游戏**——单局、机制简单、可被机器自动判定"能玩"的那一档(打砖块、点击合成、躲避、跑酷)。它是这套设计真正擅长的领域。
|
||||
- **声明式结构化源**:设计对外宣称的范式名号——意思是"一款游戏 = 一份结构化、可声明、可 diff 的源描述",而非一坨硬编码代码。这个名号是否名副其实,正是裁决的争点之一。
|
||||
- **九门 harness**:把生成出来的游戏丢进真实浏览器里**自动真玩一局并判定"是否合法可玩"**的测试夹具(harness 即"测试夹具"),其中有九道确定性检查门——能不能装载、跑不跑帧、响不响应输入、到不到游戏终态等等。它是这套设计最硬的工程价值,详见[主文档](README.md) §2。
|
||||
|
||||
值得注意的是:**四个对抗视角各自独立得出了"部分合理",措辞不同,但裂缝都指向同一处。** 这本身就是个强信号——这不是某个审查者的口味问题,而是设计里**真有一道贯穿性的张力**。下面把这道张力讲透。
|
||||
|
||||
---
|
||||
|
||||
## 2. 真正合理、必须坚持的部分
|
||||
|
||||
先说该护住的。这套设计有几处取舍是经过深思的真功夫,不是凑数。**创始人不要因为后面的批评,就把这些一并推翻。**
|
||||
|
||||
### 2.1 把"确定性受控面"当地基,是全盘最聪明的一步
|
||||
|
||||
便宜模型产线的生死命门,其实只有一个问题:**能不能在没有人、也没有 VLM 的情况下,自动判定一局游戏"真的可玩"。**(VLM = Vision-Language Model,看图打分的多模态模型——同行常用它来"看截图判断游戏好不好",但它会被对抗、会漂移。)绝大多数同行栽在这里:他们能让模型吐出能跑的代码,却**无法机器化地证明它可玩**,于是只能靠截图、靠跑几帧、靠 VLM 打分,而这些都不可靠。
|
||||
|
||||
这套设计对这个命门的回答,是把三件事做进了运行时:
|
||||
|
||||
- **把随机、时间、输入全部收进受控种子**——运行时里 `rt.random`、`rt.time.now` 一律走一个统一的上下文 `ctx`,而不是各自调用系统真随机或真时钟。这样同一颗种子就能复现同一局。
|
||||
- **把胜负置成不可逆的 latch 终态**——一个叫 `latch()` 的机制一旦置定就停摆,游戏不会偷偷重开。(latch 是电路里的"锁存"概念:置位后保持,不会自己翻回去。)
|
||||
- **把整个游戏世界投影成测试侧能用命名路径读取的取证形状**——实体按 id 投影成 `ball.x`/`paddle.x`,带 tag 的实体汇成 `targets[]` 这样的数组,供自动测试程序按名字读取。
|
||||
|
||||
这三件事合起来,九门 harness 才能用确定性的对照去"真玩一局并判定"。审查者亲手核对了代码:**这些不是设计文档里的许诺,是运行时里真实存在的机制。** 这是该范式最硬的正当性——它不是给玩具加约束,而是为"自动验收"这个产品命门做的地基。换个反例就看清它的价值:自由工程里游戏状态散落在各个 Manager 的私有字段里,想做同等取证得逐个工程定制探针,**根本无法规模化**。这一步,创始人当初拍得对。
|
||||
|
||||
### 2.2 "behaviors 只写逻辑不写画" + 声明式渲染器,把约束花在了刀刃上
|
||||
|
||||
实测数据反复证明:便宜模型在三个高频面上漂移率极高——**canvas 绘制、`requestAnimationFrame` 驱帧、事件订阅**。(`requestAnimationFrame` 是浏览器逐帧驱动动画的标准接口。)这套设计干脆把"出图"整个从模型手里拿走:**渲染器根据 render 组件自动画,模型写的 behavior(行为逻辑)只能经运行时接口 `rt` 去操作游戏世界,碰不到画面。** 三个最大的 bug 源被结构性地消除了。
|
||||
|
||||
更精彩的是一个叫 `clickable` 的内置基元。实测发现,连强模型都常把"点击命中"这件事误门控在一个分离的状态或计时器上,导致自动测试程序永远点不中,连环挂掉好几道门。这套设计把"点中目标 → 翻状态 → 生成标记 → 计分 → 播特效"整条链内置成了运行时基元,**离散点击类游戏只要声明一个 `clickable` 组件就行**。这是"缩小生成域、扩大平台域"这条哲学的最佳实证——用平台的确定性代码,换整整一类游戏的可靠性。这个判断,是真正吃透了便宜模型脾性之后才做得出来的。
|
||||
|
||||
### 2.3 验收禁止 LLM 自评 + "框架可换、成本单点"的接缝,是对的工程纪律
|
||||
|
||||
这里有两条工程纪律值得点名表扬:
|
||||
|
||||
第一,**"完成"(done)钉死在真浏览器真玩的九门上,而不是模型自夸**。这比"用 VLM 给截图打分"更抗 Goodhart(Goodhart 定律:一旦把某个度量当成目标,它就会被钻空子而失真)——VLM 本身会被对抗、会漂移,而确定性门不会。
|
||||
|
||||
第二,**把生成逻辑藏在一个 dispatcher 契约之后、模型只走单一的 new-api 成本层**。(dispatcher 即"调度契约",是生成逻辑对外的统一接口;new-api 是模型调用的统一网关层,所有模型调用都从这一个口子出去。)这意味着"便宜模型 vs 专训模型""SAA 编排 vs ReAct vs 换一套框架",全都变成了**接缝后面的可替换选择,而不是架构重写**。对照那种"把能力焊死在某个专训运行时加专训权重上"的做法——它们换底座要重训,而绘境AI 的赌注是**可回退的**。在"便宜模型可能撞天花板"这个核心不确定性面前,这条留足了退路,是成熟架构师的做法。
|
||||
|
||||
### 2.4 "改源不改包"的产品基座判断对,但要打个折
|
||||
|
||||
还有一处方向性正确、但必须诚实打折的地方:**"游戏即长生命周期源项目、改源不改包"的产品基座判断是对的。** 平台化(而非工具化)确实需要可 diff、可改、可重建的源——这条范式原则的完整论述见[主文档](README.md) §3。
|
||||
|
||||
但要诚实:**这个优势目前只覆盖了"声明式数据"那一半**(实体/场景/数值——config 走数据驱动,改参数可以免 LLM),而**逻辑那一半仍是自由 JS**,可维护性红利在最关键的"有趣逻辑"处打了折。这把我们引向裂缝。
|
||||
|
||||
---
|
||||
|
||||
## 3. 真正的天花板与裂缝
|
||||
|
||||
下面按 fundamental → serious 排列每条裂缝,并明确标注它是"范式级病灶"还是"可补的工程缺口"。
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
Root["范式张力<br/>用压低输出空间换不专训"]
|
||||
Root --> F1["裂缝一(fundamental)<br/>名实不符<br/>behavior.code 没进契约"]
|
||||
Root --> F2["裂缝二(fundamental)<br/>表达力天花板<br/>焊死在四类玩具"]
|
||||
Root --> F3["裂缝三(fundamental)<br/>好玩好看无托底<br/>九门只判机制地板"]
|
||||
Root -. 同源衍生 .-> S["四条 serious 工程缺口"]
|
||||
S --> S4["裂缝四:new Function 安全押正则"]
|
||||
S --> S5["裂缝五:rt 面四处人手双写"]
|
||||
S --> S6["裂缝六:九插件对主线不可达"]
|
||||
S --> S7["裂缝七:gatespec 自产自验同源"]
|
||||
style Root fill:#f9e79f
|
||||
style F1 fill:#f5b7b1
|
||||
style F2 fill:#f5b7b1
|
||||
style F3 fill:#f5b7b1
|
||||
```
|
||||
|
||||
这张图先把结论摆出来:**三条 fundamental 不是三个独立 bug,而是同一道范式张力的三张脸**(§3.4 会收束这一点);四条 serious 则是从同一根上衍生出的工程缺口。
|
||||
|
||||
### 3.1 裂缝一(fundamental · 名实不符):承载 100% 游戏逻辑的 `behavior.code`,根本没进契约
|
||||
|
||||
这是四视角里最尖锐、也是审查者亲手验证最确凿的一条。
|
||||
|
||||
游戏定义的 schema(数据契约)里,behavior 的定义**只声明了 `id` 和 `trigger` 两个字段**,`required` 也只有这俩;而真正装着**全部玩法逻辑**的 `code` 字段,是靠 schema 上的 `additionalProperties: true`(允许任意额外字段)偷渡进来的——运行时甚至还接受一个**未文档化的 `js` 别名**。这意味着什么?
|
||||
|
||||
> **我们对外宣称这是"声明式结构化源",但它真实的结构是"声明式数据壳 + 未受契约约束的自由代码核"。**
|
||||
|
||||
entities / components / scenes / rules 那层声明式外壳是真的,可游戏的灵魂——逻辑——**依旧是一坨任意 JS 字符串**。后果是连锁的:契约对最关键的产物零约束,于是**校验落空、版本化落空、可寻址性落空**——`sourceHash`(源指纹哈希)哈希的是一个内含任意代码串的 JSON,改一个字符哈希就变,根本无法对逻辑做点对点的 diff 和结构化迭代。我们号称的"长生命周期项目"优势,在逻辑这一半上是悬空的。
|
||||
|
||||
这是范式级问题,因为它戳破了设计的自我叙事。但请注意:**它不是"这设计废了",而是"这设计名不副实"。** 补法是把名号和现实对齐,而不是推倒——§4 的改动一、改动二给具体改法。
|
||||
|
||||
### 3.2 裂缝二(fundamental · 表达力天花板):能产的复杂度被焊死在四类玩具上,且墙撞得很近
|
||||
|
||||
这不是理论推测,是代码现状:
|
||||
|
||||
- **scenes 只取 `scenes[0]`** —— 多关卡无从谈起;
|
||||
- **advance(推进)是注释写明的 v0 占位空操作**(`gd-runtime.js:256`)—— 关卡推进根本没实现;
|
||||
- **渲染只有 rect / circle / fill 三种形状**;
|
||||
- **物理只有 `vx/vy + gravity` 一条单一积分路径**;
|
||||
- **进度只有一个全局 score 加 win/lose 二元**。
|
||||
|
||||
它能撑的,就是 **pong / clicker / dodge / runner 这四类**——休闲单局小游戏。任何需要多关卡推进、库存/对话树、敌人 AI 状态机、tilemap/寻路、相机、可变 HUD 流程的品类,这套运行时**既没有对应的声明面,也没有调度器去承载**。schema 注释自己都坦白了:"非 AAA ECS:无 system 调度器、无 archetype、无 query DSL"。(ECS = Entity-Component-System,游戏业界的实体-组件-系统架构;这句话等于承认它只是个极简的数据容器,不是真正的游戏引擎内核。)
|
||||
|
||||
对照一个成熟的自由工程范式,才看得清这道墙的高度:它的 platformer(平台跳跃)模块有 **8435 行**(里面是 PlayerFSM 玩家状态机、ChaseAI 追击 AI、PatrolAI 巡逻 AI、SkillBehavior 技能、BehaviorManager 行为管理器),tower_defense(塔防)模块 **3877 行**(WaveManager 波次管理、EconomyManager 经济管理)。那一层结构化复杂度,**不是绘境AI 的模型不够强,而是运行时根本没有承载它的形状**——给再强的模型,它也只能把复杂逻辑硬塞进一个 `update` 串里,然后撞上"自由 JS 核"那道裂缝一。
|
||||
|
||||
这一条之所以是 fundamental 而非工程缺口,是因为它**和对标的产品愿景正面冲突**:demo 里放的是 **Marvel 平台动作、KOF 格斗**这种富交互游戏,而这套范式**结构性地产不出那一档**。这是产品愿景级的天花板,不是补个分支能解决的。
|
||||
|
||||
### 3.3 裂缝三(fundamental · 好玩好看无下限托底):九门只判机制地板,质量上限由便宜模型单点决定
|
||||
|
||||
把前两条往产品端推一步,就是这条。
|
||||
|
||||
九门作为"机制 CI"(持续集成式的机制校验)设计得很精良、很抗假绿:**C 门**拦单步假推进、**E 门**用哈希拦冻屏(画面卡死)、**G 门**用同种子同帧号的 A/B 对照,隔离"靠自走动画蒙混过活性检查"的把戏、**H 门**验 gameover 之后不会自动重开;驱动测试的 driver 家族还做了速度前瞻、抛物线反解这种"测试侧自己也得会玩"的真功夫。这一层审查者给高分。
|
||||
|
||||
**但九门保证的是"机制合法",中间整段"好玩"没有任何硬门或软门兜底。** 同一套九门下,一个精心设计的打砖块,和一个"摆 3 个砖块、点一下就 win"的退化品,**都能全绿**。
|
||||
|
||||
"好看"那一侧更直接撞墙:**渲染器只画色块**,而源项目里那六类资产规格(`SourceProject.assets`)在整条构建链上**零消费者**——审查者 grep 过整个宿主目录,构建白名单根本不含 assets。这意味着模型即便用 mmx(绘境AI 的素材生成工具)产出了精美 sprite,当前链路也会**原样丢弃**,渲出来永远是几何色块。而对大众用户来说,"好看"是游戏信息流第一屏的入场券——**色块画面会被直接划走**。
|
||||
|
||||
叠加上记忆里 `SaaFullGraphE2eTest`(端到端测试)实测**便宜模型成功率只有 60%**、连"机制稳定产出"都未达 **80% 这道门**——把"大众想玩"这个愿景级目标,押在一个"机制都还没稳、好玩好看零下限保护"的单点上,是当前架构**最大的产品风险**。
|
||||
|
||||
### 3.4 三条 fundamental 是同一道张力的三张脸
|
||||
|
||||
审查者必须诚实指出:**这三条 fundamental 不是三个独立 bug,而是同一个范式张力的三张脸——"用压低输出空间换不专训"。**
|
||||
|
||||
压低输出空间(声明式壳 + 窄 rt 面 + 硬编码渲染物理)确实把简单品类的可靠性和可验收性拉了上去,这是**真价值**;但同一个动作,也把可产游戏的复杂度上限、视觉质量上限、"有趣"的表达空间一起压低了。
|
||||
|
||||
> **这不是设计做错了,而是这套设计天然只能站在"可靠"这一端,够不到"复杂/精美"那一端。危险的不是它有天花板,而是把它当"通用生成范式"去卖。**
|
||||
|
||||
### 3.5 四条 serious:方向对、可补的工程缺口
|
||||
|
||||
**裂缝四(serious · 安全押在脆弱正则上):`new Function` 编译不可信模型代码,安全完全靠正则黑名单兜。** behavior 和 rule.condition 都走裸 `new Function`(把字符串当 JS 代码执行),无沙箱,安全靠构建期的 `LOGIC_BANS` / `CONDITION_BANS` 正则黑名单(禁 `process`/`eval`/`.constructor`/`while(true)` 等)。正则黑名单对 JS 是出了名的可绕——模板字符串拼接、Unicode 转义、`[].filter.constructor` 这类变体都能逃逸。作者自己在注释里也承认这是"`new Function` 不沙箱的现实缓解"。这条是 serious 而非 fundamental,因为**方向其实是对的,只是定位错了**:既然只在浏览器 CDP harness 里跑(CDP = Chrome DevTools Protocol,通过它远程驱动真实浏览器),**浏览器进程本身就是安全边界**,那个安全扫描该被诚实地定位成"质量门/确定性门"而非"安全门"。真要防逃逸,该靠 iframe sandbox + CSP + 进程隔离,而非正则。顺带一个真实的可调试性债:behavior 抛错只回灌一行 message,无行号、无 source map,模型和人都难定位——给注入代码包一层 `//# sourceURL=behavior/<id>`(让 JS 错误栈带上可读名字)就能解决,这是低成本高回报的补丁。
|
||||
|
||||
**裂缝五(serious · rt 面四处人手双写、无一致性门):** 加一个 rt 能力,要**同时手改四处**——Java 里的 prompt 字符串、`gd-runtime.js` 实现、`runtime-api-2d.md` 文档、构建禁则。当前 rt 面被刻意冻在约 **20 个函数**(YAGNI 原则:You Aren't Gonna Need It,不为没到来的需求提前造),这个成本现在可控,而且 prompt 与运行时目前**没有 split-brain**(审查者核对过——"split-brain"指"两个本该一致的副本各说各话";这里指 prompt 承诺的接口面运行时真的都有)。但这是**静默高危的演进期债**:漂移一旦发生(prompt 说有 `rt.X` 但运行时没有 → 模型据此生成 → 运行时报 undefined → 九门挂),排查成本极高。**不必上重型注册表(那是过度工程),对症解是加一道一致性测试**:从 `gd-runtime.js` 反射出真实合法的 rt 面集合,断言它与 prompt 里的 `rt.*` token 集、文档表三者相等,一旦 diff 就红线。把"四处人手同步"降级成"改任一处、测试逼你改齐"。
|
||||
|
||||
**裂缝六(serious · 九插件对主线全不可达):** 团队花整整一波(一个开发批次)建的**九个能力插件**——collision(碰撞)的 SAT/MTV 算法、physics-lite 刚体、gamefeel 的缓动/屏震/hitStop(手感插件:tween 缓动、screenshake 屏幕震动、hitStop 命中顿帧)——在结构化生成主线里**一个都用不上**。`gd-runtime.js` 零 import 那个插件注册表(PluginRegistry),只经 `getEngine()` 拿引擎三面(粒子/音频/数学)。这意味着 gameDefinition 能表达的物理只有最朴素的 `vx/vy` 积分加 AABB 重叠检测,想要"带法线的弹性反弹""缓动入场""屏震 juice"都做不到——**手感被实打实压低一档**。这是 serious 而非 fundamental,因为它有清晰的升级路径:**当某个插件能力被验证"可靠产出且九门可测"时,经 `rt.fx` 家族扩一个声明式入口**(如 `rt.fx.shake` / `rt.tween`)把它纳入 gameDefinition 面。但必须立刻把这道落差写进路线图,否则九插件会变成"为旧的 iife 老路建的、在新路里逐渐腐烂的死资产"。
|
||||
|
||||
**裂缝七(serious · gatespec 自产自验、同源风险):** H 门的"机制进展"断言由 play-spec 的 `assertAfterPlay` 逐游戏自声明,而 spec / driver / gatespec(测试规格 / 驱动脚本 / 门规格)**都是 design-agent 自产**。同一来源既定义"怎么算过"又生成"被测物",存在系统性地"把门调到刚好能过"的风险——断言写成 `score increased`、driver 又专门去触发那个 score,门必绿,但游戏未必好玩。G 门能挡"完全不响应",挡不住"只为过门而响应"。补法是对 gatespec 引入"断言最小强度"校验,或让独立评审 agent 抽样复核,**切断自产自验链**。
|
||||
|
||||
剩下两条 minor 点到为止:
|
||||
|
||||
- **D 门"真渲染"判据(有色像素 maxCh > 80)在色块时代几乎恒真**——接了 sprite 也不会自动变严,视觉维度形同虚设;接美术时必须同步升级判据。
|
||||
- **rule.condition 的"无副作用"约束只在构建期静态扫,运行时的 `new Function('return (cond)')` 并不强制**——双边界对纯净性的保证强度不一致;让两边共用同一份禁则定义即可。
|
||||
|
||||
---
|
||||
|
||||
## 4. 要让它更合理,具体改什么
|
||||
|
||||
审查者不主张推倒。这套设计的地基(确定性受控面 + 自动验收)是对的,该做的是**"对齐名实、明确分层、补上几道门"**。按优先级排,核心是四改(改动一至四),外加两条配套(改动五、改动六):
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
P0["改动一(最高优先)<br/>给范式正名<br/>+ 愿景显式分层"] --> P1a["改动二(高优先)<br/>逻辑层往结构化拽回<br/>code 串退场到长尾"]
|
||||
P0 --> P1b["改动三(高优先)<br/>assetSpec 接上消费端<br/>打通'好看'"]
|
||||
P1a --> P2a["改动四(中优先)<br/>给'好玩'立独立质量轴"]
|
||||
P1b --> P2a
|
||||
P2a --> P2b["改动五(中优先)<br/>三道一致性/调试补丁"]
|
||||
P2b --> P3["改动六(战略级)<br/>给生成产线<br/>自己的进化飞轮"]
|
||||
```
|
||||
|
||||
**改动一(最高优先 · 定性纠偏):诚实地给范式正名,并把愿景显式分层。** 别再对内对外叫它"纯声明式结构化源",它是"**结构化外壳 + 受限逻辑**"的混合范式。同时,把"便宜模型 + 约束 schema"明确锁定为 **Tier0:可发行轻游戏(超休闲/合成/挂机/答题/网格点选)的可靠产线**,并把这档的复杂度边界、视觉边界白纸黑字写进 profile 的承诺里。
|
||||
|
||||
> **最大的风险不是设计有天花板,而是产品团队拿它当"通用生成范式"去对标 demo 里的 Marvel/KOF。**
|
||||
|
||||
富交互品类要么显式推迟,要么走"受控自由工程 + 更强模型"的独立轨——**别让一套 schema 同时背"可靠"和"复杂"两个互斥目标**。这一条不花一行代码,却是整份裁决里最重要的动作。
|
||||
|
||||
**改动二(高优先 · 把逻辑层往结构化拽回来):把高频 idiom 沉淀成声明式 behavior kind,让 code 字符串退场到长尾兜底。** 当前四个原型其实都能用五六个内置 behavior kind(移动 / 积分 / 碰撞 lose / 计时 spawn / clickable 那样)表达。把这些做成可组合的声明式原语库——类似成熟范式里那种"可选可配"的 PlatformerMovement / ChaseAI 件——让便宜模型从"写任意 JS"降级成"选 + 配组合",才真正吃到约束式的可靠性红利;任意 JS 逃逸口只留给少数高阶场景。这一步同时缓解**裂缝一**(逻辑可契约化了)、**裂缝四**(逃逸面缩小)和"模型成功率 60%"(选配比手写错误率低得多)。
|
||||
|
||||
**改动三(高优先 · 让 assetSpec 从孤儿契约转成有消费端):** 这是打通"好看"的**唯一路径**,且它是一整条链、不是一个分支——① 渲染器加 sprite 分支(render 组件增 `spriteRef` → 引擎 `drawTile`);② 构建白名单纳入 assets,并在 rt 暴露资产解析;③ **同步把 D 门从"非白屏"升级成"非纯色块/有结构"的视觉判据**(颜色直方图复杂度 / 边缘密度),否则接了 sprite,门也测不出视觉退化,等于白接。在这条链打通前,**路线图必须明确标注"当前产出 = 色块原型,非可发行视觉品质"**,杜绝把"机制门全绿"误读成"产品就绪"。
|
||||
|
||||
**改动四(中优先 · 给"好玩"立独立质量轴):** 别让九门兼任"好玩"判官。补一道便宜的启发式门——动作类看 driver 真玩时分数增长曲线是否非平非爆、失败是否可达且非秒败——给 **0–100 分而非 pass/fail**,作为信息流排序与 repair 修复的信号。把"好玩"明确从九门责任里剥离:
|
||||
|
||||
> **九门 = 机制 CI,好玩 = 另一条评审/数据回灌轨。**
|
||||
|
||||
否则,机制全绿会持续制造"质量已达标"的错觉。
|
||||
|
||||
**改动五(中优先 · 三道一致性/调试补丁):** rt 面加一致性测试(对应裂缝五);注入代码包 `//# sourceURL` 让错误栈带名(对应裂缝四);把 condition 的双边界共用同一份禁则定义(对应裂缝中的 minor)。这三条都是低成本、止住静默漂移的对症解。
|
||||
|
||||
**改动六(战略级 · 给生成产线自己的复利飞轮):** 当前 schema / rt / 模板都是 Opus(绘境AI 用来做最高复杂度设计的最强模型)人工设计的**静态资产**,量上来后会成为瓶颈——而护城河四层里就有"资产沉淀"和"网络效应"两层。成熟范式有一套"模板技能"五段管线,能从每个完成的项目里**自动萃取新模板家族**,绘境AI 没有。建议:**把过门的 gameDefinition 做成生成侧的进化语料**,从高频 entity/behavior 组合聚类,半自动沉淀为品类 prompt 模板 / 行为原语,给生成产线一个**自我增强的飞轮**,而非永远靠 Opus 手动加模板。这一条不紧急,但决定了平台能否真正"越跑越强"。
|
||||
|
||||
---
|
||||
|
||||
## 5. 收口:这设计能到产品愿景吗
|
||||
|
||||
**能到一半,且那一半是真护城河;另一半它够不着,必须靠分层和另一条轨。**
|
||||
|
||||
这套设计能**可靠地、低成本地、机器可验收地批量产出超休闲轻游戏**——这正是"零门槛用户一句话造可玩游戏"这个差异化闭环里**最难、也最值钱**的一环。竞品大多停在"生成工具",死在"无法证明可玩";而绘境AI 这套确定性受控面 + 九门 harness,真的把"能生成 ≠ 能玩"这条假绿线焊死了,这是别人短期补不上的工程纵深。**作为 Tier0 产线,它合理、聪明、该坚持。**
|
||||
|
||||
但产品愿景里 demo 对标的 **Marvel 平台动作、KOF 格斗**那一档富交互游戏,这套范式**结构性地够不着**——不是模型不够强,是运行时没有承载那层复杂度的形状,逻辑核又还是没被结构化的自由 JS。**愿景必须诚实分层:Tier0 用这套吃"可靠 + 可验收 + 规模",复杂品类另开一轨。** 把这两件事混为一谈、用一套 schema 同时承诺"可靠"和"复杂",是当前**唯一的范式级误判风险**。
|
||||
|
||||
一句话给创始人:
|
||||
|
||||
> **这设计哪里都没"错",它只是被起错了名、被寄予了它够不到的期望。把名字改对(混合范式)、把愿景分层(Tier0 不碰 Marvel)、把逻辑往声明式拽回来、把 assets 链打通——做完这四件,它就是一个诚实、强壮、有护城河的 Tier0 生成范式;不做,它迟早会因为"被当通用范式卖"而在第一款复杂游戏的 demo 上当众撞墙。**
|
||||
|
||||
---
|
||||
|
||||
## 附录 · 裁决依据(已逐条代码核实)
|
||||
|
||||
本裁决的每一条 fundamental / serious 指控,均已按 `文件:行号` 核对真实代码,而非凭设计文档推断:
|
||||
|
||||
| 指控 | 代码证据 |
|
||||
|---|---|
|
||||
| 逻辑核未进契约 | `source-project.schema.json`:behavior 仅声明 `id`/`trigger` + `additionalProperties: true`;运行时另接受未文档化 `js` 别名 |
|
||||
| 渲染/物理/场景焊死 | `gd-runtime.js`:渲染器仅 circle/rect/fill;advance 为 v0 空操作(`:256`);单场景 `scenes[0]`;单一物理积分;裸 `new Function` |
|
||||
| assets 被丢弃 + 安全靠正则 | `build-from-source.mjs`:`GAMEDEF_KEYS` 五项白名单丢弃 assets;安全靠 `LOGIC_BANS`/`CONDITION_BANS` 正则黑名单 |
|
||||
| 视觉链零消费端 | host 全目录对 `sprite`/`assets`/`drawTile` 零消费者(grep 全目录) |
|
||||
| 机制成功率未达门 | `SaaFullGraphE2eTest` 实测便宜模型成功率约 60%,未达 80% 门 |
|
||||
|
||||
> 总架构师对四视角分歧的拍板:**这些不是四个孤立缺陷,而是"压低输出空间换不专训"这一个范式张力的多张脸——该坚持地基、对齐名实、显式分层,而非推倒重来。**
|
||||
|
||||
---
|
||||
|
||||
> **延伸阅读**:这台生成机器的完整架构与端到端流程见[生成引擎主文档](README.md);"游戏即长生命周期源项目、LLM 是工作室"的范式原论述见同文 §3;固定架构 + 填槽的展开见[固定游戏架构](固定游戏架构.md);运行时与九门细节见[引擎与运行时](引擎与运行时.md);SAA 编排拓扑见[SAA编排](SAA编排.md)。
|
||||
283
docs/architecture/架构/生成引擎/验收门-W-G1.md
Normal file
283
docs/architecture/架构/生成引擎/验收门-W-G1.md
Normal file
@ -0,0 +1,283 @@
|
||||
# 开闸验收门 W-G1 · 生成对外放行的 6 道门
|
||||
|
||||
> **这是什么**:绘境AI 生成引擎"对外开闸"那一刻必须先架好的 6 道验收门的设计文档。它回答的是一个很具体的问题——**当"一句话生成游戏"第一次向真实创作者放开时,我们到底要先焊死哪些门、可以并行补哪些门、又有哪两道前置必须先验过才许放行?**
|
||||
> **给谁看**:负责生成主线开闸的工程师、质量与审核台一侧的同事、做发布放行决策的产品与创始人,以及想看清"放量前的安全/可控边界"的架构评审。
|
||||
> **怎么读**:先读 §1 那张"6 门怎么排"的全景图建立整体认知,再按 §2–§4 顺着三组门看每道门具体检查什么;想确认"能不能放"读 §5 的两道开闸前置验证结果;残留待办在 §6。
|
||||
|
||||
生成引擎此前讲的是"一句话怎么变成一款游戏"那条技术主线(见[生成引擎主文档](README.md))。本文讲的是另一件事:那条主线建好之后,**怎么把它对真实用户安全地"开闸"**。开闸不是一个开关,而是一组前置条件——这份文档把这组条件钉成 6 道可验收的门。
|
||||
|
||||
---
|
||||
|
||||
## 1. 一句话与一张图:开闸要先架哪 6 道门
|
||||
|
||||
先把"开闸"这个词说清楚。**开闸 = 把"一句话生成游戏"这条能力,从内部封闭测试状态,正式对真实创作者放开。** 这是一个有风险的动作:放开之后,陌生用户会带着各种意图涌进来,既可能用一句违规的话试探合规底线,也可能用并发请求把后台那条串行的生成流水线压垮。所以开闸不能裸奔,但也不必等到所有质量门尽善尽美才放——那样窗口期早就过了。
|
||||
|
||||
我们的判断是把 6 道门按"阻塞等级"分成三类,各司其职:
|
||||
|
||||
- **2 道硬阻塞门,必须先焊死,否则一律不许放行。** 它们守的是"放开之后系统会不会被一个用户压垮"和"违规内容会不会直接进到玩家面前"这两条不可逆的红线。
|
||||
- **3 道落库门,可以和开闸并行上线,且第一版全部只观测、不拦截。** 它们守的是"放开之后我们还管得住、追得清"——把每次生成的全过程数据落库、算出就绪分、查出重复创意,但都只记录、不卡人。
|
||||
- **1 道首局体验门,和上面同批焊好一块"地板"。** 它守的是放进来的游戏"陌生人 10 秒钟觉不觉得能玩",但它判失败时不会推翻底层那套硬验收。
|
||||
|
||||
这套排布背后的设计理由,一句话就能说清:**别等 6 门全齐才开闸(只有那 2 道硬阻塞门不做,确实不能放),但也别裸奔开闸(把可控性和合规这两条焊死,其余的边放量边观测)。** 下面这张图是 6 门的全貌:
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
subgraph A["组A · 硬阻塞 · 先做 · 已落地"]
|
||||
D12["D12 控制平面 v0<br/>降级 + 配额并发 + 背压 + 记账骨架"]
|
||||
GP9["GP9 合规先行段<br/>生成前 prompt 审查<br/>10 负例 100% 阻断"]
|
||||
end
|
||||
subgraph B["组B · 落库组 · v0 并行 · 全非阻断 · 已落地"]
|
||||
N9D["9d trace 账本<br/>worker 富数据落 trace_json"]
|
||||
D11["D11 就绪评分<br/>读 trace 算 0-100 落库 + 审核台透出"]
|
||||
D9["D9 反同质化<br/>归一查重 · 撞重只告警"]
|
||||
end
|
||||
subgraph C["组C · 同批焊地板 · 已落地"]
|
||||
FIRST["首局体验子门<br/>可玩≤2s / 首反馈即时 / 60s 品类闭环"]
|
||||
end
|
||||
A -->|2 门焊死| OPEN(("对外开闸"))
|
||||
OPEN -.-> |"放了要能管 / 追溯"| B
|
||||
C -.-> |"随补 L1 driver 自产"| OPEN
|
||||
M4[["跨轨 · 随 M4:真实计费扣退<br/>(网关硬编码 UserId 阻塞,推迟)"]] -.-> D12
|
||||
|
||||
style D12 fill:#f5b7b1
|
||||
style GP9 fill:#f5b7b1
|
||||
style OPEN fill:#a9dfbf
|
||||
```
|
||||
|
||||
图里出现了几个内部代号,在这里一次解释清楚,后面各节会逐个展开:
|
||||
|
||||
- **D12 / GP9 / D11 / D9** 是这套生成体系里给各道质量与控制门排定的编号(D 系列是质量/控制门,GP 系列是合规门)。本文只涉及开闸这一批,完整门体系不在此展开。
|
||||
- **trace(轨迹)** 指一次生成任务从头到尾产生的全过程结构化记录——走了哪几道门、花了多少成本、用了哪些模型、重试了几次。"9d trace"是其中第 9 维度的全链轨迹账本。
|
||||
- **worker** 指真正跑生成的那个独立工作进程(Python 写的),它在后台一轮轮地把一句话变成游戏。
|
||||
- **落库** 就是把数据写进数据库持久化保存的口语说法。
|
||||
- **L1 / driver** 是生成侧的概念:L1 指"模板成游戏"那一档最轻量的生成能力;driver 指九门验收时用来真玩游戏的那个操作脚本(它知道这款游戏该点哪、该划哪)。
|
||||
|
||||
这张图最该记住的是那条主轴:**只有 D12 和 GP9 这两道硬阻塞门焊死了,才允许"对外开闸";开闸之后,组B 三门负责"放了还管得住"、组C 一门负责"放进来的游戏体验有地板"。** 图右下角那个虚线挂着的 M4,是一件被刻意推迟的事,§1.2 单独讲。
|
||||
|
||||
### 1.1 6 门一览
|
||||
|
||||
| 门 | 一句话:它检查什么 | 阻塞分级 | 归属轨 |
|
||||
|---|---|---|---|
|
||||
| **D12 控制平面** | 放开前先有降级开关、配额并发限制、背压保护、记账骨架,保住后台那条全局串行的生成流水线不被压垮 | **硬阻塞** | 生成引擎(计费扣退跨网关) |
|
||||
| **GP9 合规先行** | 生成开始**之前**先审查用户那句 prompt,违规的不入队、不进玩家信息流 | **硬阻塞** | 生成引擎 |
|
||||
| **9d trace** | 把 worker 已经产出的富生成轨迹结构化落库,供 D11/D9 复用 | v0 并行,不卡门 | 生成引擎 + 契约 |
|
||||
| **D11 就绪评分** | 读 trace 算出一个 0-100 的"就绪度"分数,落库并在审核台可见 | v0 并行,不卡门 | 质量 + 产品 · 审核台 |
|
||||
| **D9 反同质化** | 把游戏配置归一化后查重,撞到重复创意只告警、不拦截 | v0 并行,不卡门 | 生成引擎 / 质量 |
|
||||
| **首局体验子门** | 品类化的首局服务等级目标,3 条断言,判失败也不推翻底层九门 | 不卡门 · 开闸前补 | 质量 / harness |
|
||||
|
||||
这三组门的取舍理由,可以浓缩成三条核心决策:
|
||||
|
||||
1. **D12 和 GP9 为什么必须硬阻塞?** 因为这两件事不做,后果都不可逆。后台的生成 worker 是**全局串行**的(一次只处理一个任务),不做控制平面,单个用户的并发请求就能把它压垮,所有人都生成不了;不做合规先行,违规内容会直接生成出来进入玩家信息流,这是法务红线,一旦发生无法回收。
|
||||
2. **那 3 道落库门为什么第一版只观测、不拦截?** 因为底层那套"九门 harness"(在真实浏览器里跑游戏、用九道确定性检查判定能不能玩的测试夹具)已经是挡住坏游戏的**硬地板**了。这三道落库门是更上一层的质量观测,第一版若一上来就生效拦截,反而会**误杀好游戏**——所以先只记录数据,等积累够了再考虑收紧。
|
||||
3. **真实计费为什么不在这一批?** 见 §1.2。
|
||||
|
||||
### 1.2 真实计费扣退为什么被拆出去(随 M4)
|
||||
|
||||
图里那个虚线挂着的方块写着"M4:真实计费扣退"。M4 是后续一个独立的里程碑代号。**真实的"按量扣费 / 失败退费"被刻意从开闸这一批里拆出去,推迟到 M4 再做**,原因有两个,都很实在:
|
||||
|
||||
- **网关层有一处阻塞。** 所有模型调用统一走的 new-api 网关(new-api = 绘境AI 内部统一管理模型 key 与用量计费的网关),它的 `AddToken` 接口当前把 UserId 硬编码死了,没法按真实用户记账。
|
||||
- **MVP 阶段本就没有真支付。** 既然还收不到钱,也就谈不上真扣费。
|
||||
|
||||
所以这一批里,D12 控制平面用的是一套"额度记账"的**骨架**先顶上:配额和背压该限的限、该挡的挡,但记账只是占位字段和日志,**不是真扣费**。等 M4 把网关那处阻塞解了、真支付也接上了,再把骨架换成真账。把这两件事拆开排期,是为了让开闸不被一个网关 bug 卡住。
|
||||
|
||||
---
|
||||
|
||||
## 2. 组A:两道硬阻塞门(D12 控制平面 + GP9 合规先行)
|
||||
|
||||
这是开闸的承重墙,已落地并合入主干(命名空间 `com.wanxiang.huijing`)。两道门一道管"系统不被压垮",一道管"违规不进信息流"。
|
||||
|
||||
### 2.1 D12 控制平面:在唯一入口前焊四道门
|
||||
|
||||
D12 的核心思路是:**在生成任务的唯一入口处,入队之前,按"从最便宜到最贵"的顺序焊四道门,层层拦截,把最贵的那道(要调大模型的合规检查)放在最后**——这样超配额、超背压的请求根本走不到调模型那一步,省钱也省算力。
|
||||
|
||||
这里有一个容易被绕过的坑,先说清:生成任务的提交入口是 `AigcTaskServiceImpl.submitGenerate`,但还有一个 `retryTask`(重试)路径。如果只在 `submitGenerate` 上焊门,用户走重试就能直接插库、绕过所有控制。所以实现上把控制逻辑抽成了一个共用方法 `enqueueWithControlPlane`,让提交和重试都走它——**堵死重试绕过控制的口子**。四道门的顺序如下:
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
REQ["生成请求<br/>submitGenerate / retryTask"] --> G1{"① 降级门<br/>aigc.generate.paused?"}
|
||||
G1 -->|"已暂停"| R1["拒绝:GENERATE_PAUSED"]
|
||||
G1 -->|"放行"| G2{"② 配额 + 并发门<br/>per-creator×level 当日计数"}
|
||||
G2 -->|"超上限"| R2["拒绝:QUOTA_EXCEEDED"]
|
||||
G2 -->|"放行"| G3{"③ 背压门<br/>全局在飞 queued+running"}
|
||||
G3 -->|"超队列深度"| R3["拒绝:BACKPRESSURE_REJECTED"]
|
||||
G3 -->|"放行"| G4{"④ GP9 安全门<br/>(最贵,放最后)"}
|
||||
G4 -->|"违规"| R4["拒绝:UNSAFE_PROMPT"]
|
||||
G4 -->|"安全"| INS["insert 入队<br/>落 level=L1 + 额度记账骨架"]
|
||||
|
||||
style G4 fill:#f9e79f
|
||||
style INS fill:#a9dfbf
|
||||
```
|
||||
|
||||
逐道门的含义:
|
||||
|
||||
- **① 降级门**:读基础设施配置中心(infra `ConfigApi`)里 `aigc.generate.paused` 这个键,运营可以热改它一键暂停全站生成。读取失败时**容错放行**(fail-open on read)——配置中心抖动不该把生成全堵死。
|
||||
- **② 配额 + 并发门**:按"每个创作者 × 会员档(level)"统计当日生成计数,达到或超过(`>=`)上限就拒。
|
||||
- **③ 背压门**:统计全局在飞的任务数(排队中 queued + 运行中 running),达到或超过队列深度上限就拒。这道门守的就是那条全局串行 worker。
|
||||
- **④ GP9 安全门**:把要调大模型的合规检查放在最后,前面已被配额/背压拒掉的请求,**不会白白付一次安全检查的成本**。
|
||||
|
||||
四门全过,才 insert 入队,同时落两样东西:一个 `level` 字段(会员档,v0 阶段统一记为 L1);一套额度记账骨架(只是日志和占位字段,**不是真扣费**,见 §1.2)。
|
||||
|
||||
### 2.2 GP9 合规先行:为什么必须独立、且必须 fail-closed
|
||||
|
||||
GP9 这道门有两个设计决定很关键,都源自踩过的坑:
|
||||
|
||||
**第一,它必须用一个独立的安全检查客户端,不能复用生成执行器的那个 LLM 客户端。** 原因有两层:执行器的客户端 `ExecutorLlmClient` 被 `aigc.executor.enabled` 这个开关门控着,服务层根本注入不到它;更重要的是,如果把安全检查塞进执行器内部去做,它会卡住那条串行执行的节拍(tick),等于自己给自己制造了一个拒绝服务(DoS)。所以 GP9 在服务层用一个**独立的** `SafetyCheckClient` 调 `safety.prompt-check`,配**短超时 8 秒 + 至多 1 次重试**。
|
||||
|
||||
**第二,它必须 fail-closed(失败时从严)。** 这是一条方向性的安全判断:
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
P["用户 prompt"] --> CHK["SafetyCheckClient<br/>调 safety.prompt-check<br/>超时 8s + 至多 1 重试"]
|
||||
CHK -->|"safe=false"| REJ["同步拒绝<br/>抛 UNSAFE_PROMPT<br/>不入队"]
|
||||
CHK -->|"safe=true"| PASS["放行,继续生成"]
|
||||
CHK -->|"超时 / 调用失败"| FC["fail-closed<br/>抛中性 LLM_ERROR<br/>(安全检查暂不可用)"]
|
||||
|
||||
style REJ fill:#f5b7b1
|
||||
style FC fill:#f9e79f
|
||||
style PASS fill:#a9dfbf
|
||||
```
|
||||
|
||||
- 检查结果 `safe=false`(判定违规):同步拒绝,抛 `UNSAFE_PROMPT`,不入队。
|
||||
- 检查超时或调用失败:**fail-closed**,抛一个**中性**的 `LLM_ERROR`(提示"安全检查暂不可用")。这里有两处刻意的设计——一是合规判不出来就放过,等于让违规内容在窗口期直入信息流,风险不可逆,所以宁可从严挡掉;二是用中性提示而非复用 `UNSAFE_PROMPT`,是为了避免 new-api 网关偶尔抖动时,把一个正常用户误标成"发了违规内容"。这里**不新增错误码枚举**,直接复用既有的 `LLM_ERROR`。
|
||||
|
||||
### 2.3 组A 的错误码、回滚与契约
|
||||
|
||||
- **错误码**:在 aigc 模块的错误码里新增了 001 子段,放三个控制平面相关的码——`QUOTA_EXCEEDED`(超配额)、`BACKPRESSURE_REJECTED`(背压拒绝)、`GENERATE_PAUSED`(已暂停)。它们都是**业务码、走 HTTP 200**——验收时要断言返回的是业务码,而**不是** HTTP 429 / 503 这种协议层状态码。
|
||||
- **回滚**:整个控制平面挂在 `aigc.control-plane.enabled` 这个功能开关(feature-flag)后面,**默认关闭 = 现行逻辑逐字不变**(全部门加 GP9 都旁路)。
|
||||
- **契约与数据迁移**:合规负例语料放在契约目录 `contracts/prompts/eval/safety.prompt-check/{inputs,labels}.jsonl`,共 **10 条负例,覆盖 10 类合规红线类目**;数据库迁移 `V15.0.0__aigc_task_add_level.sql` 给任务表加 `level` 列,只增不改(additive)、默认值 1,存量数据回填为 L1。
|
||||
|
||||
---
|
||||
|
||||
## 3. 组B:三道落库门(9d trace + D11 就绪评分 + D9 反同质化)
|
||||
|
||||
组B 是开闸的"可控性"层,已落地、全部非阻断(已合入主干)。理解组B 的关键,是先理解一个反直觉的洞见。
|
||||
|
||||
### 3.1 命门洞见:不是缺数据,是富数据在边界被丢了
|
||||
|
||||
做这三道门之前,直觉会以为"我们缺数据,要从零造一套生成数据采集"。真相恰恰相反:**worker 早就产出了富数据,只是在回调边界上被丢弃了。**
|
||||
|
||||
worker 每跑完一轮,它的 `result` 里其实已经包含了非常丰富的信息——九门逐门的通过情况(guards)、成本(cost)、用了哪些模型(models)、重试了几次(attempts)、验收规格(gatespec)、裁决(verdict)、修复记录(repairs)、墙钟耗时(wall)。问题出在:worker 往后端回调时,`build_callback_payload` 这个函数只往回传了 5+2 个字段,`result` 作为一个线程内的局部变量,随着那个线程结束就消亡了,富数据再也找不回来。
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
subgraph W["worker(已产富数据)"]
|
||||
RES["result<br/>guards/cost/models/attempts<br/>gatespec/verdict/repairs/wall"]
|
||||
BCP["build_callback_payload<br/>只塞 5+2 字段"]
|
||||
RES -->|"⚠️ 富数据在此被丢弃"| BCP
|
||||
end
|
||||
subgraph DB["后端落库"]
|
||||
TJ[("trace_json")]
|
||||
RS[("readiness_score")]
|
||||
end
|
||||
BCP -.->|"修法:把被丢的富数据接过来"| TJ
|
||||
TJ --> SCORE["D11 ReadinessScorer 读 trace 算分"]
|
||||
SCORE --> RS
|
||||
|
||||
style RES fill:#f9e79f
|
||||
```
|
||||
|
||||
所以这三件事的本质,**都不是从零造数据,而是把那份在回调边界被丢弃的富数据接过来落库**。
|
||||
|
||||
### 3.2 三道门各自做什么
|
||||
|
||||
- **① 9d trace(公共底座)**:这是另外两道门的数据来源,所以**契约先行**——先改契约 `contracts/api-schemas/aigc.yaml` 里的 `DifyCallbackReqVO`,给它加一个 `trace` 字段(只增不改、可选),再改 Java 侧的值对象和 worker。worker 的 `build_callback_payload` 从 `result` 抽出 9d 子集回传,后端 `DifyCallbackTxService` 在回填段尽力(best-effort)落到 `game_aigc_task.trace_json`。这里定了一个**必填子集七项**作为"轨迹完整率"的分母:`{pass, repairs, wallS, models, attempts, gameId, stage}`;其余字段因为和具体生成路径相关、worker 走哪条路尚未定死,所以 schema 放宽、不绑死任何一条路径。
|
||||
- **② D11 就绪评分(`ReadinessScorer`)**:读 trace,加权算出一个 0-100 的就绪度分数。权重是:可玩性 playability 占 0.5、首局体验 firstPlay 占 0.25、稳定性 stability 占 0.15、效率 efficiency 占 0.1(**这套权重是占位值,待质量轨用真实数据校准**)。一个重要的容错设计:**字段缺失时取中性值 0.5,而不是直接打 0 分**——免得因为某个可选字段没采到就把分数打到地板。算出的分落到 `readiness_score` 字段,并在审核台接口 `/task/page` 的返回里透出(前端那个 aigc 列表页是产品轨的后续工作,当前 game-admin 还没有这个页)。
|
||||
- **③ D9 反同质化**:把生成早期 agent-loop-v1 编排器里的 `_norm_text`(归一化纯函数,几乎是原样复制)和 `mint_design_id` 的思路移植过来,在 worker 侧写一个 `dedup.py`,把游戏配置归一化后算出一个签名去查重。**撞到重复创意时只 `log.warn` 告警 + 把 `trace.similarity.dupHit` 落库,绝不拦截。**
|
||||
|
||||
### 3.3 组B 的非阻断硬约束、回滚与关键修复
|
||||
|
||||
- **非阻断硬约束(命门)**:这是组B 不可破的红线——上面三件事**任意一件**的落库、算分、查重失败,都必须 `try-catch` 把异常吞掉 + `log.error` 记录,主回调链照常走到 `PUBLISHED`(参照既有 `DifyCallbackServiceImpl` 的 best-effort 范式)。**落库这一组绝不允许因为观测失败,反过来拦截了主链。**
|
||||
- **回滚**:三件各有一个开关(`aigc.trace.enabled` / `aigc.dedup.enabled` 等),字段都是 additive 可空的,停止写入即等于回滚。
|
||||
- **数据迁移**:`V16.0.0__aigc_task_add_trace_readiness.sql`,加 `trace_json`(JSON 类型)和 `readiness_score`(SMALLINT 类型)两列。
|
||||
- **两处关键修复(已合入)**:
|
||||
- 提交 `703e462c` 闭合了一处 **split-brain**(直译"裂脑",指同一份事实在两条代码路径上各落各的、迟早不一致的隐患)——当派发器走 SAA 进程内路时,也要落 `trace_json` / `readiness_score`,保证 HTTP worker 路和 SAA 路两路一致,不会切了路就静默丢字段。
|
||||
- 提交 `5f6cdd0c` 修了 `ReadinessScorer.firstPlay` 读 `H_progress` 嵌套对象的 `.pass` 时的一个 bug——修好后 HTTP 和 SAA 两路的 firstPlay 分能从恒为 0.5 走到可达 1.0。
|
||||
|
||||
---
|
||||
|
||||
## 4. 组C:首局体验子门
|
||||
|
||||
组C 是和组A/B 同批焊好的那块"体验地板",已落地。它要验的是生成主线对外的那句口号——**"陌生人 10 秒钟觉得能玩"**——能不能兑现。
|
||||
|
||||
具体落地是在九门 harness 的真玩脚本(`play.cdp.cjs`)里加一道首局门,验**3 条断言**:
|
||||
|
||||
1. **可玩 ≤ 2 秒**:从打开到能真正操作,不超过 2 秒。
|
||||
2. **首反馈即时**:第一次操作要立刻有反馈。
|
||||
3. **60 秒内品类核心反馈闭环可达**:一分钟之内,能走完这个游戏品类最核心的那个反馈闭环(比如打砖块要能消掉一块砖)。
|
||||
|
||||
这里有一个边界必须说清:**首局门是 H 门的"additive 派生超集"**。H 门是九门里判"机制有进展"的那道门;首局门复用 H 门的裁决结果,但即使首局门判 FAIL,**也不会推翻** H 门的 pass/fail——H 门仍然是机制层的硬地板,首局门只是在它之上叠一层更贴近真实体验的观测。
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
GAME["生成产物"] --> H["H 门<br/>机制有进展(硬地板)"]
|
||||
H --> FIRST["首局体验子门<br/>(H 的 additive 超集)"]
|
||||
FIRST --> A1["① 可玩 ≤ 2s"]
|
||||
FIRST --> A2["② 首反馈即时"]
|
||||
FIRST --> A3["③ 60s 品类闭环可达"]
|
||||
FIRST -.->|"FAIL 不推翻 H"| H
|
||||
|
||||
style H fill:#a9dfbf
|
||||
style FIRST fill:#f9e79f
|
||||
```
|
||||
|
||||
落地上还有三个要点:
|
||||
|
||||
- **v0 走的是"路A"——零改生成侧。** 不动生成那一头,而是从现成的验收规格(gatespec)里的 `driver` 家族反推游戏品类:`tap-targets` 对应"放置类"、`+safeOnly` 对应"规避类"、`paddle-intercept` 对应"技巧类"。然后复用现成的三款游戏(打砖块 breakout / 井字棋 tictactoe / 扫雷 saolei)来验,**零新生成**。
|
||||
- **阈值在真实环境校准。** 2 秒 / 60 秒这两个阈值是在 mini-desktop 的无头(headless)环境里实测校准的——无头浏览器冷启动比真机慢,所以阈值按它来定更稳。
|
||||
- **回滚**:harness 上的一个开关,**默认开**作为验收门,但 FAIL 不阻断九门 pass。
|
||||
|
||||
---
|
||||
|
||||
## 5. G0/G1 开闸前置验证(已 PASS · 无 blocker)
|
||||
|
||||
6 道门架好之后,放行之前,还有两道"开闸前置"必须在 mini-desktop 的预发(staging)环境里真验过——这两道叫 G0、G1(G 是 gate 的首字母,这里特指"开闸前置验证项")。两道都已 **PASS、无阻塞项(blocker)**。
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
GATES["6 门已焊好"] --> G0["G0 创作页核实"]
|
||||
G0 -->|"PASS"| G1["G1 GP9 staging 真验"]
|
||||
G1 -->|"PASS · 无 blocker"| READY["具备开闸条件<br/>(放行决策待创始人)"]
|
||||
|
||||
style READY fill:#a9dfbf
|
||||
```
|
||||
|
||||
### 5.1 G0:创作页核实——空模板列表是"按设计的过渡态",不是缺陷
|
||||
|
||||
G0 验的是创作页的状态。这里有一个很容易被误判成"断点缺陷"的现象,必须说准:**创作页的模板列表是空的(返回 HTTP 200,不是故障),这是按设计的过渡态,代号 W-CLEAN。** 它的完整形态是:一个诚实的空态提示"玩法模板体系升级中",一个被置灰(disabled)的"开始生成"按钮,外加一个"去游戏流"的出口。这是**有意为之的过渡态,不是断点缺陷**。
|
||||
|
||||
背后的真相是:后端的模板白名单 `SUPPORTED_TEMPLATE_IDS=["generic"]` 是**非空**的(保住 W-G1 这条一句话生成链路能跑),只是前端的创作入口暂时不暴露模板而已。需要和技术决策口径对齐的一点是:**被废掉的是"游戏模板/填参线"那套东西(W-CLEAN),而"玩法模板"这个品类引导框架并没有被废,只是等 W-G1 落地后回填**。(W-G1 = 第一批对外开闸的游戏;W-CLEAN = 把旧的"游戏模板/填参"产线清理掉的那项工作。两者别混。)
|
||||
|
||||
### 5.2 G1:GP9 合规门 staging 真验——10 条违规负例 100% 被挡
|
||||
|
||||
G1 验的是 GP9 这道合规门在真环境里到底挡不挡得住。结论很硬:**10 条违规负例经过真实的 new-api 网关(模型用 `MiniMax-M2.7`)判定,100% 返回 `safe=false`**,进而 `submitGenerate` **100% 抛出 `AIGC_UNSAFE_PROMPT`、0 条落库入队**——这一点在日志层和数据库层做了**双重实证**。这意味着 §2.2 设计的那道合规先行门,在真环境里确实做到了"违规一条都进不来"。
|
||||
|
||||
---
|
||||
|
||||
## 6. 待办 / Follow-up(残留)
|
||||
|
||||
6 门已焊死、两道前置已验过,真正剩下的是放行决策本身和几项需要拍板的数值。这些都是已知的、被显式追踪的残留,不是隐藏缺口:
|
||||
|
||||
1. **开闸放行决策本身。** 焊死门已就绪,放行与否由创始人裁定(此决策优先于任何自动门)。
|
||||
2. **待创始人拍的数值。** 组A 只落了 `level` 列加门结构,具体数值是占位的,需要拍板:会员档 L1 / L2 / L3 的每日配额与并发数(组A 占位)、D11 的评分权重(§3.2 占位)、D9 的模糊近似阈值——v0 只做了"精确撞重 + 归一化完全相等",模糊相似度怎么量,要等真实数据观察到分布之后再定。
|
||||
3. **产品轨。** admin 的 aigc 任务列表页要不要近期就排上"就绪分"展示(后端契约已经透出了,前端需要新建 aigc 列表页,或在 review 页 join 取这个分)。
|
||||
4. **承接自 SAA 治理(round3)的两项。** F3 = SAA 的成本字段目前只落 token 数、还没折算成人民币金额;早期 boot 阶段 SAA 图的前几次派发会瞬态失败、需要预热。F5 已修复(即 §3.3 那个 `5f6cdd0c`)。
|
||||
|
||||
---
|
||||
|
||||
## 7. 源档导航
|
||||
|
||||
本文档是开闸验收门 W-G1 的策展层主档,把一份 canonical 活档(它本身又合并了 5 份历史 spec)整理成了这份给人读的全景。要看更深的门序、接线、契约细节与决策史,从下面进去:
|
||||
|
||||
| 去处 | 回答什么 |
|
||||
|---|---|
|
||||
| `.agents/skills/cheap-model-game-generation.md` | 便宜模型造游戏的 worker loop、九门真玩 harness、成本与模型选择(深落地权威源之一) |
|
||||
| `.agents/skills/saa-graph-orchestration.md` | SAA 裸图编排的拓扑、加节点、checkpoint、dispatcher 契约与门(深落地权威源之一) |
|
||||
| `.agents/skills/contract-first-development.md` | Contract-first:如何对齐 API/DB/SDK/event 契约并解耦并行工作 |
|
||||
| [生成引擎主文档](README.md) | "一句话怎么变成一款游戏"的生成主线全景(本文的上游) |
|
||||
| [SAA 编排](SAA编排.md) | 生成主线的编排基建、六条不变量、两条 split-brain 治理铁律 |
|
||||
|
||||
> **纪律**:本档是开闸验收门 W-G1 的策展层 SoT——读它 = 当前真相。门序/接线/契约的逐条落地在 `.agents/skills/`,决策史在 git;两者职责不同,不互相重复。当现行真相与某份带日期的历史 spec 冲突时,以本档与它引用的最新裁定为准。
|
||||
|
||||
---
|
||||
|
||||
> **验证状态**:本文档为架构策展文档,由开闸验收门 W-G1 canonical 活档改写而成,未改任何代码。承重硬事实——D12 四门门序、GP9 短超时 8s + 1 重试 + fail-closed、10 负例 100% 阻断、错误码 001 子段(QUOTA_EXCEEDED/BACKPRESSURE_REJECTED/GENERATE_PAUSED 走 HTTP200)、Flyway V15/V16 迁移、9d trace 必填七项、D11 权重(0.5/0.25/0.15/0.1)、首局门 3 断言(≤2s / 即时 / 60s)、G0 白名单 `["generic"]`、G1 真验 `MiniMax-M2.7` 100% safe=false / 0 落库、split-brain 修复 `703e462c` 与 firstPlay 修复 `5f6cdd0c`——均沿用源档已抽查属实的结论;品牌统一为"绘境AI",无旧名残留。
|
||||
100
docs/architecture/运维/README.md
Normal file
100
docs/architecture/运维/README.md
Normal file
@ -0,0 +1,100 @@
|
||||
# 运维域 · 设计主文档
|
||||
|
||||
> **这是什么**:绘境AI 运维域的设计主文档,回答"**这套系统部署在哪些机器上、怎么从代码变成在运行的服务、靠什么确认它还活着**"。它衔接在[架构域](../架构/README.md)(系统怎么搭)之后,讲的是"搭好之后怎么让它跑起来、并且持续跑得住"。
|
||||
> **给谁看**:负责部署与值守的工程师、做发版的人、排查线上故障的人,以及关心可用性与运维成本的创始人。
|
||||
> **怎么读**:本页只画**边界**——环境长什么样、一次部署经过哪几步、用什么门确认健康、可用性目标是多少。真正逐字节、逐命令的操作手册不在这里(原因见下文第 2 节),按文末指针去取。
|
||||
> **读这一页 = 运维域的当前真相骨架**。具体命令、踩坑红线、机器口令都在指向的脚本与 playbook 里,本页不重复。
|
||||
|
||||
---
|
||||
|
||||
## 1. 边界:环境、部署、冒烟、可用性目标
|
||||
|
||||
运维域要回答四件事:服务**跑在哪里**、代码**怎么变成运行中的服务**、**怎么知道它健康**、以及**健康到什么程度才算达标**。逐一说清。
|
||||
|
||||
### 1.1 跑在哪里:四台机器各司其职
|
||||
|
||||
绘境AI 在内网阶段不用云托管、不用 Kubernetes,而是用一组通过 Tailscale(一种把分散机器组进同一虚拟内网的工具)连起来的物理机。它们按角色分工,边界很硬:
|
||||
|
||||
- **lili-mac(创作者本地工作站)**:跑开发主会话、本地全栈 dev、快速构建迭代。它会休眠合盖,所以**不进 Tailscale 调度**,也不承担任何"门"。一条关键边界:Mac 是 ARM 架构,产出的 JAR 与前端产物架构中立可用,但 **Docker 镜像与冒烟门必须在 x86 的 mini-desktop 出**,否则镜像架构与生产不同构,等于半假的门。
|
||||
- **mini-desktop(100.64.0.7)**:**权威验收机**——staging 全栈、重型构建、浏览器端到端测试都在这里,且与生产同构。它跑着 huijing 后端单体(`:48080`)、产品端 game-studio(`:4173`)、管理后台 game-admin(`:4174`),外加一套**隔离的** MySQL / Redis 容器(只供 staging 用,不与共享基建混)。
|
||||
- **mini-infra(100.64.0.8)**:**共享基建**——Gitea(自建 Git 仓)、MySQL、Redis、MinIO(对象存储)、new-api(模型网关,生成主线经它直连便宜大模型)都在这台。它**绝不跑项目 app 或重型构建**,只做底座。
|
||||
- **6c6g**:**常驻无人值守机**——常开的 agent 编排、定时任务、后台批跑、git 操作落在这里(因为 Mac 会休眠,常驻活只能交给它)。它**禁跑重活**(重型前端构建会 OOM)。
|
||||
|
||||
> **注(三个名词):** "单体"指后端 13 个业务模块编译进**一个** Spring Boot 进程一起启动(不是微服务那样各自独立部署);"staging"指上线前的预演环境,与生产同构、供验收;"生产同构"指机器的 OS / JDK / 浏览器版本与正式环境一致,这样验收结果才可信。
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
subgraph dev["开发侧(不进调度)"]
|
||||
MAC["lili-mac · ARM<br/>主会话 / 本地 dev / 快构建<br/>(会休眠 · 不出镜像与门)"]
|
||||
end
|
||||
subgraph tnet["Tailscale 内网"]
|
||||
direction TB
|
||||
subgraph desk["mini-desktop · 100.64.0.7 · x86(权威验收 · 与生产同构)"]
|
||||
BE["huijing 后端单体<br/>:48080"]
|
||||
ST["game-studio 产品端<br/>:4173"]
|
||||
AD["game-admin 管理后台<br/>:4174"]
|
||||
DBL["隔离 MySQL / Redis<br/>(仅 staging)"]
|
||||
end
|
||||
subgraph infra["mini-infra · 100.64.0.8(共享基建 · 不跑 app)"]
|
||||
GIT["Gitea 代码仓"]
|
||||
SQL["MySQL / Redis"]
|
||||
OSS["MinIO 对象存储"]
|
||||
NAPI["new-api 模型网关"]
|
||||
end
|
||||
SIX["6c6g(常驻无人值守)<br/>编排 / cron / 批跑 / git<br/>(禁跑重活)"]
|
||||
end
|
||||
|
||||
MAC -- "git push" --> GIT
|
||||
GIT -- "HTTP 匿名 clone/pull" --> desk
|
||||
BE -- "出网调模型" --> NAPI
|
||||
BE --> SQL
|
||||
BE --> OSS
|
||||
SIX -- "调度 / 批跑" --> desk
|
||||
```
|
||||
|
||||
### 1.2 怎么部署:git push → pull → 重建 → 验门
|
||||
|
||||
内网阶段**没有 docker-compose、也没有自建 CI 流水线**——开发团队版文档里那套"push 触发 CI、自动构建镜像、滚动更新"是目标态蓝图,与现实已系统性脱节(那份文档顶部自带审计横幅说明这点)。现行的部署是**人工经标准序在 mini-desktop 上完成**的,核心三步:
|
||||
|
||||
1. **同步代码**:代码经 `git push` 推到 mini-infra 上的 Gitea,mini-desktop 再以匿名 HTTP `clone / pull` 拉取(注意是 HTTP 只读端口,不是 SSH)。这里有两条已踩过的坑:直接 `scp / rsync` 源码树会被分类器拦下,所以必须走 Gitea 中转;大资产 push 在途时再发 push 会撞 Gitea 的 ref 锁,所以**同仓 push 要串行**,等上一笔落地再发下一笔。
|
||||
2. **重建**:在 mini-desktop 上 `git reset --hard` 到目标分支,再 `mvn clean install` 出新的 fat JAR。收口前**以字节码实证**新 JAR 确实含本次改动(曾因"构建源 ≠ 运行的 JAR"翻过车)。高风险或整机变更走**隔离验证安全变体**:先用新 JAR 在隔离端口(如 `:48090`)起一个实例验全,**live 的 `:48080` 全程不动**,验全过才切;任一步失败就用 `/tmp` 里的备份 JAR 把 `:48080` 恢复回去。
|
||||
3. **验门**:重启后先验后端 `health` 返回 200、四个游戏模板就绪、Flyway(数据库迁移工具)已 up-to-date,再跑冒烟门(下一节)。
|
||||
|
||||
### 1.3 怎么知道它健康:冒烟门
|
||||
|
||||
每次 staging 部署之后,跑一遍 `deploy/smoke-test.sh`——这是"部署后是否健康"的**就绪检查**(秒级,以读为主、可反复跑),**不是**深度端到端测试(深度 e2e 由 agent-loop 编排器批跑,职责不重叠)。它逐项 PASS / FAIL,末尾给总判与退出码(0 = 全过,1 = 有 FAIL),因此可以直接挂到部署脚本里当卡口:
|
||||
|
||||
- **默认只读 12 项**:覆盖基础设施 + 鉴权,以及五条端到端闭环链路的关键 API——①创作→生成→预览、②发布→审核→游戏流、③试玩→互动→分享、④广告→收益→钱包、⑤数据回路(遥测→质量分→feed 重排)。它验的是这五条链的关键 API 在岗、鉴权 / CORS(跨域放行)/ 质量分回灌的结构正常。
|
||||
- **`--deep` 加 1 项(共 13)**:走真实写路径,触发一次生成入队 + 遥测事件落库,按需开启。
|
||||
- 一个反复出现的坑被写进了脚本注释:feed 列表项的 `packageUrl` 字段**恒为 null**,真实取游戏包要走 `GET /app-api/runtime/package/{versionId}`——别拿 `packageUrl` 当取包入口。
|
||||
- 还有一条红线:**API 全绿 ≠ UI 通**。编排器旁路会掩盖 UI 缺陷,所以用户可见的波次收口前,必须另做一次真 UI 走查。
|
||||
|
||||
### 1.4 健康到什么程度才算达标:可用性目标
|
||||
|
||||
运维域的硬指标是**服务可用性 ≥ 99.5%**(MVP 阶段目标)。与之配套的是 MVP 基础设施成本控制在 **< ¥5,000/月**(投资人版约 ¥4,300/月,约 ¥50k/年)——内网物理机 + 共享基建的选型,正是为了在这个成本盒子内撑住可用性目标。这两个数字是运维域所有取舍(不上云托管、单体而非微服务、人工部署而非重 CI)的约束来源。
|
||||
|
||||
---
|
||||
|
||||
## 2. 为什么这份主档是"薄"的
|
||||
|
||||
你可能预期一份运维主档会塞满 `docker compose up`、回滚命令、Nacos 路由权重之类的操作细节。这里**刻意不放**,原因有三:
|
||||
|
||||
- **可执行的脚本是单一真相源,散文复制只会过期**:真正会运行的部署与冒烟逻辑在 `deploy/` 目录下的脚本里(它们是机器执行的、可验证的)。把同样的命令再抄一遍到 Markdown,只会得到一份迟早与脚本不一致的影子文档——而文档治理的硬规则要求每个主题只有一份真相源。
|
||||
- **操作手册属于范围外**:本仓的文档分两层。运维主档属于"策展层",只承载**当前真相的骨架**(边界、目标、决策);逐机器的 setup、口令、踩坑红线属于可累积的"留痕 / playbook 层",归宿是 `.agents/skills/`。把它们混进主档会侵蚀主档的"读它=当前真相"这一属性。
|
||||
- **环境随实战快速演进**:机器角色、端口、构建配方在多波实战里持续被修正(例如命名空间改名、构建门从假门禁纠到真门禁)。让它们沉淀在贴近代码的 playbook 里、由实战驱动更新,比锁死在一份主档里更不容易腐化。
|
||||
|
||||
简言之:**本页负责"为什么这么部署、达标线在哪";"具体怎么敲命令"交给下面的指针。**
|
||||
|
||||
---
|
||||
|
||||
## 3. 指针:具体怎么做去这里
|
||||
|
||||
| 资源 | 回答什么 | 路径 |
|
||||
|---|---|---|
|
||||
| 冒烟门脚本 | 一次部署后跑哪 12(+1)项就绪检查、怎么传 BASE / TOKEN、`--deep` 走什么写路径 | [`deploy/smoke-test.sh`](../../../deploy/smoke-test.sh) |
|
||||
| staging 运维 playbook | 四机分工铁律、代码怎么同步(Gitea 中转 + push 串行)、后端重部署标准序、隔离验证安全变体、构建 / 测试门红线 | [`.agents/skills/staging-ops.md`](../../../.agents/skills/staging-ops.md) |
|
||||
| 真 UI 走查 playbook | API 全绿之后,怎么经 CDP 在 mini-desktop 上做一次真浏览器 UI 走查 | [`.agents/skills/ui-walkthrough-cdp.md`](../../../.agents/skills/ui-walkthrough-cdp.md) |
|
||||
| 工程规范(运维相关红线) | 与部署 / 构建 / 机器口令相关的硬约束(与 playbook 互为索引) | [`.agents/rules/engineering-conventions.md`](../../../.agents/rules/engineering-conventions.md) |
|
||||
| 当前进度与实操总账 | staging 上各模块的真实状态、历次部署踩坑实录 | [`docs/mvp/MVP进度总账.md`](../../mvp/MVP进度总账.md) |
|
||||
|
||||
> **纪律**:运维域只讲"环境边界 / 部署链 / 健康门 / 可用性目标"这层骨架。**一行命令该怎么敲,以 `deploy/` 脚本和 `.agents/skills/staging-ops.md` 为准**;两者冲突时,以会运行的脚本为准。本页与[架构域](../架构/README.md)(系统怎么搭)、[运营域](../运营/README.md)(合规上线与发行)互不重复、各管一段。
|
||||
217
docs/architecture/运营/README.md
Normal file
217
docs/architecture/运营/README.md
Normal file
@ -0,0 +1,217 @@
|
||||
# 运营域 · 设计主文档
|
||||
|
||||
> **这是什么**:绘境AI 运营域的设计主文档,回答"**一款游戏被造出来之后,怎么合规上线、怎么把钱赚回来、怎么发行到各渠道**"。它衔接在[产品域](../产品/README.md)(用户能感知什么)与[架构域](../架构/README.md)(系统怎么搭)之后,讲的是商业与合规这条"落地链"。
|
||||
> **给谁看**:运营负责人、商务/BD、合规与法务对接人、创始人,以及做投资尽调时关心商业模型与风险的读者。
|
||||
> **怎么读**:先读本页建立全局认知——三件事(上线 / 变现 / 发行)各是什么、彼此怎么咬合;需要某一块的细节(分账公式、渠道合规红线、闸门状态)时,再深入文末的三份子文档。
|
||||
> **读这一页 = 运营域的当前真相骨架**。明细数字、公式、逐项闸门状态都在子文档里,本页只做综述与导航,不重复其内容。
|
||||
|
||||
---
|
||||
|
||||
## 1. 一句话与一张图:运营域要解决什么
|
||||
|
||||
产品域把"做得出游戏、有人来玩、能赚到钱"接成了一条产品闭环。**运营域负责的是这条闭环里"赚钱"那半段能不能在现实世界里真正跑起来**——而这取决于三件相互咬合的事:
|
||||
|
||||
- **上线(合规)**:在中国做面向公众的 UGC(User Generated Content,用户生成内容)游戏平台,必须先过一串法定资质与监管闸门(ICP 备案、实名防沉迷、AIGC 标识等)。这些闸门是日历驱动的硬约束,**一秒也压不动**,是整个商业化的前置门。
|
||||
- **变现**:游戏被玩到之后,靠广告、内购、订阅、B 端定制把收入收回来,再按规则分给创作者。这里既有钱怎么流的工程问题,也有"靠它能不能回本"的单位经济问题。
|
||||
- **发行**:把游戏铺到玩家在的地方——自有的 H5 游戏流,以及微信 / 抖音小游戏渠道。不同渠道的技术能力和合规口径天差地别,决定了"什么游戏能上哪个渠道、怎么上"。
|
||||
|
||||
三者的关系可以一张图说清:合规闸门是**总闸**,没过就不能商业化;发行决定**钱从哪里进来**;变现决定**钱怎么分出去**。三条线在"现金流"这个点上汇合。
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
subgraph 上线["上线 · 合规闸门(总闸)"]
|
||||
G["12+1 法定/审计闸门<br/>ICP · 实名防沉迷 · AIGC 标识<br/>大模型登记 · 算法备案"]
|
||||
end
|
||||
subgraph 发行["发行 · 流量从哪来"]
|
||||
H5["自有 H5 游戏流<br/>即时生成发布 · UGC 主场"]
|
||||
CH["微信 / 抖音小游戏<br/>精选整包发行"]
|
||||
end
|
||||
subgraph 变现["变现 · 钱怎么分"]
|
||||
AD["广告(IAA)"]
|
||||
B2B["B 端 / IP 项目制"]
|
||||
PAY["内购 / 订阅(远期)"]
|
||||
SHARE["分账 → 创作者钱包 → 提现"]
|
||||
end
|
||||
G ==>|过闸才可商业化| 发行
|
||||
H5 --> AD
|
||||
CH --> AD
|
||||
AD --> SHARE
|
||||
B2B --> SHARE
|
||||
PAY --> SHARE
|
||||
G -. 合规定性约束 .-> 变现
|
||||
```
|
||||
|
||||
> 这里有一个**反直觉但关键的战略判定**:绘境AI 的差异化护城河 **不在"自有 feed 形态"本身**(那块早有现任者——摸摸鱼 / 233 / 4399 占着),**而在 AI 供给侧的成本结构**(用 agent 闭环把游戏的生成与质检全自动化,产能成本远低于人工)+ **IP 锁风保真**(下文 §2.3 解释)。运营域的所有安排都服务于把这个成本优势,在监管窗口期内滚成内容资产与数据飞轮。这一判定的来由与四颗"雷"的收口结论,见[战略与合规](../../agent-specs/战略与合规.md)。
|
||||
|
||||
---
|
||||
|
||||
## 2. 三件事各是什么
|
||||
|
||||
### 2.1 上线:合规闸门是日历驱动的硬约束
|
||||
|
||||
绘境AI 想做的是"普通人发布、平台分发、能接广告变现"的 UGC 游戏平台。在中国,这件事一旦面向公众、一旦接真钱,就会同时撞上几道监管:游戏需要**版号**(国家新闻出版署核发的游戏出版批文)才能商业化运营,平台需要 **ICP 备案**(网站经营许可的工信部备案)才能合法提供互联网服务,接广告变现需要相应**广告资质**,资金归集需要**支付牌照**,生成式 AI 还要做**内容标识与大模型登记**。
|
||||
|
||||
这套约束之所以特别棘手,是因为它构成一个**连环死锁**(我们内部称之为"合规死锁"):
|
||||
|
||||
1. "IAA 免版号"(IAA = In-App Advertising,纯广告变现不内购)这条豁免通道,**只在微信 / 抖音的渠道备案制内成立**;搬到自有 H5 站就不再适用,落进监管灰区(已有 Roblox 在国内停服、被罚没 111 万元的先例)。
|
||||
2. 自有端没有版号,就无法接入出版署官方的实名 / 防沉迷系统,也就**无法履行自有端本应承担的合规义务**——这是第二环锁。
|
||||
3. 平台若代收广告费再分账给个人创作者,等于"无牌照资金归集",踩到**"二清"风险**("二次清算"的简称,指无支付牌照的主体经手他人资金的清结算,属违规)——这是第三环锁。
|
||||
|
||||
绘境AI 的破解办法是**双层定性**,把"自有端"和"变现渠道"在合规上彻底分开:自有端**收敛为邀请制内测**(不公开经营、不接真钱,按"试玩 demo"定性,只用来养数据回路);真正的变现走微信 / 抖音渠道(豁免通道成立,且由平台代管实名 / 防沉迷)。这样三环锁同时解开。
|
||||
|
||||
至于具体要过哪些闸门、各自卡在哪一步,有一张**唯一跟踪面**——[A1 闸门看板](../../mvp/A1闸门看板.md)。它把闸门列成 **12 + 1 项**(代号里 A1 = 作战清单中"日历临界路径"这条任务线):8 项法定 P0(P0 = MVP 阶段必须满足的最高优先级)——C 端实名、防沉迷时长 / 宵禁、未成年充值限制、AIGC 显式标识、青少年模式、关闭个性化推荐、账号注销、提现实名税务——加上审计新增的 3 项(大模型登记、算法备案、分账 / 灵活用工选型),再加 1 项律所合规咨询。
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
M0["0 · 经营主体 ✅ 已具备"] --> DOM["1 · 域名+实名"]
|
||||
M0 --> SMS["3 · 短信签名报备"]
|
||||
M0 --> LLM["4 · LLM 实名充值"]
|
||||
M0 --> LAW["11 · 律所合规咨询"]
|
||||
DOM --> ICP["2 · ICP 备案<br/>(7-20 工作日 · 临界路径)"]
|
||||
ICP --> PAYM["5 · 支付商户进件"]
|
||||
ICP --> ADQ["6 · 广告联盟资质"]
|
||||
LAW --> SPLIT["12 · 分账/灵工选型"]
|
||||
LAW --> REG["9/10 · 大模型登记 / 算法备案"]
|
||||
```
|
||||
|
||||
这张图要读出的几条主线:**经营主体已经具备**(0 号闸门 ✅,创始人 2026-06-10 确认),所以下游各项可以立即并行起跑,没有多米诺式的等待;**ICP 备案是临界路径**(7-20 个工作日,最长),它一通过,支付进件和广告资质两条线才能动;**律所意见是另一个分叉点**,它到位后才能定分账方案、确认大模型登记与算法备案的属地路径。
|
||||
|
||||
闸门状态是会变的活数据,**本页只给骨架,不抄状态**——任何一项的当前进度、owner、最晚提交日,都以 [A1 闸门看板](../../mvp/A1闸门看板.md)为准,看板变了即真相变了。律所要问的两个核心问题(UGC 的出版物定性 / 防沉迷如何接入)整理在[律所合规咨询 brief](../../mvp/律所合规咨询brief.md)。
|
||||
|
||||
### 2.2 变现:两个平面、两套钱包,加一道单位经济检验
|
||||
|
||||
变现这件事,工程上最容易出错的是**把不同方向的钱混在一起算**。绘境AI 用三条永不混淆的硬边界把它钉死:
|
||||
|
||||
1. **生成计费平面 ≠ 收益 / 支付平面**。"生成计费"指创作者调用大模型造游戏花掉的额度,走 **new-api 网关**(一个自部署的、对外 OpenAI 兼容的多模型 LLM 网关)的 token 配额,这个配额**只减不增**;而"赚回来的钱"(广告分成、内购)必须经业务钱包结算并过合规,走后端的 trade / pay 模块。把广告分成塞进 new-api 配额是范畴错误——一个是花出去、一个是赚回来,方向相反。
|
||||
2. **创作者收益钱包 ≠ 消费钱包**。前者是创作者挣广告、提现用的(已建);后者是玩家充值、扣减内购用的(尚未接入)。两套账户、两个钱流方向。
|
||||
3. **成本侧用"元" ≠ 收益侧用"分"**。成本台账在编排器里用浮点的"元",收益账在后端数据库里用 BIGINT 的"分",两个精度域跨平面对照必须显式换算,**禁止同列求差**。
|
||||
|
||||
变现最重要的不是"能不能跑通"(逻辑早已真实落地并有单测),而是**靠它到底能不能回本**——这是单位经济问题。绘境AI 把分账规则拍成了唯一口径(创始人 2026-06-10,代号 R3 收口):
|
||||
|
||||
```
|
||||
总广告消耗 G ── 渠道扣 40%(微信 IAA 现金分成 60%)──> 平台实收净额 N = 0.6G
|
||||
├ 无 IP:创作者拿 0.8N(= 0.48G) | 平台留 0.2N(= 0.12G)
|
||||
└ 带 IP:创作者 0.55~0.65N | IP 方 0.15~0.25N(从创作者份额内出) | 平台恒留 0.2N
|
||||
```
|
||||
|
||||
这个公式的精妙之处是**平台恒留 0.2N**:带 IP 时,IP 方的分成是从创作者那一份里出的,所以无论带不带 IP,平台都稳定留 20%,而对外宣传创作者"拿 55%~65%"和内部"80% 分成"用的是同一个模型,账永远不会算成负数。
|
||||
|
||||
基于这个模型做敏感性扫描(eCPM 三档:悲观 15 / 基准 30 / 乐观 60 元每千次曝光;eCPM = 每千次广告展示的收入)得出一个**关键结论**:自有端的 IAA 广告变现,是**万级 DAU(基准约 1.33 万日活)才回本**的规模化生意,刚好覆盖 ¥4,300/月 的 MVP 基础设施成本。所以近期的现金流**只能靠 B 端 / IP 项目制**(与 DAU 无关、回款最早),IAA 是远期账。这条认知直接决定了下面 §3 的三线变现排序。
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
G["总广告消耗 G"] -->|渠道扣 40%| N["平台净额 N = 0.6G"]
|
||||
N -->|无 IP| C1["创作者 0.8N"]
|
||||
N -->|无 IP| P1["平台 0.2N"]
|
||||
N -->|带 IP| C2["创作者 0.55~0.65N"]
|
||||
N -->|带 IP| IP["IP 方 0.15~0.25N<br/>(从创作者份额内出)"]
|
||||
N -->|带 IP| P2["平台恒留 0.2N"]
|
||||
```
|
||||
|
||||
> **诚信红线**:对创作者宣传"能赚钱",必须用"头部款"口径(eCPM 30 时,单款约需每日 12 次激励视频观看才能达到 5 元提现门槛——头部款可达,长尾款达不到),**绝不做普遍性承诺**。分账的工程实现(7 个资金动作各自的幂等键、打款异步化、资金一致性红线)与完整敏感性模型,见[变现与单位经济](../../agent-specs/变现与单位经济.md)。
|
||||
|
||||
### 2.3 发行:自有 H5 流 + 双渠道精选,两条腿走路
|
||||
|
||||
游戏造出来要铺到玩家在的地方,绘境AI 走两条互补的发行路线,分工的根因是**底层技术能力的硬差异**(代号 HJ-CH-001 判定):
|
||||
|
||||
- **自有 H5 游戏流 = 海量 UGC 与"改码"的主场**。浏览器对动态生成的代码没有禁令,所以 agent 生成的每款游戏都能即时发布、即时被刷到,这是"创作→生成→发布"闭环最完整的形态。
|
||||
- **微信 / 抖音小游戏 = 精选整包过审的分发面**。这两个渠道对远程包会**自动剥离代码、明文禁止 JS 解释器**(像 eval5 这类在运行时解释字符串为代码的库一律禁用),所以渠道里只能跑"包内固定的模板代码 + 远程下发的纯数据配置"。
|
||||
|
||||
这就是内部说的"一壳多游 · L1 精选整包"模式——一个小游戏 appid(应用 ID)里装固定的模板代码,玩法的差异全靠远程下发的 **GameConfig**(纯数据的游戏配置)来表达,**不互跳、不外导**,对外口径是"主题小游戏合集"而非"开放 UGC 平台"。"L1"指这一层只跑包内白名单内的模板,是相对受限但能稳定过审的发行层级。
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
GEN["agent 生成游戏<br/>(每款独立代码)"] --> H5["自有 H5 流<br/>即时发布 · 动态代码无禁令"]
|
||||
GEN -. 精选款固化进包 .-> PKG["微信/抖音整包<br/>固定模板代码 + 远程 GameConfig"]
|
||||
PKG --> AUDIT["平台审核<br/>(备案锁:内容变更须重新备案)"]
|
||||
AUDIT --> CH["渠道游戏流<br/>一壳多游 · 主题合集"]
|
||||
H5 --> FEED["自有 feed 即刷即玩"]
|
||||
```
|
||||
|
||||
发行侧有几条不能破的**合规红线**,因为它们直接决定渠道里的游戏会不会被下架:
|
||||
|
||||
- **GameConfig 合规红线(渠道生死线)**:配置里禁止任何可执行 / 可被解释为代码的字符串(例如 `condition: "score>10"` 这种把逻辑写进数据的写法一律禁),剧情 / 关卡只能用有限状态机的枚举来表达,所有 `templateId / actionType / assetId` 都走枚举白名单,schema 关闭额外属性、限死数值与长度。这条红线转正后会成为契约里的第 9c 条。
|
||||
- **备案锁是比版号更紧的决定性闸门**:官方规定"备案完成后不支持改游戏内容 / icon / 代码,实质变更须重新备案",这让"一壳多游"从"灰区可行"降级为"高风险待裁"。因此"当日热发新游进渠道"这个叙事被收回了——热发只限既有游戏的非实质参数微调,实质变更要走"重新备案 + 整包提审"的月级节奏(具体边界待律所裁定)。注意:这条锁只约束渠道,**不影响自有 H5 流**(H5 无备案锁)。
|
||||
|
||||
至于渠道线该用哪个引擎打包(LittleJS 自研适配 vs Cocos 原生导出)、双端各出一包的工程细节、真机取证的七道验证门,见[渠道发行](../../agent-specs/渠道发行.md)。
|
||||
|
||||
---
|
||||
|
||||
## 3. 三件事怎么咬合:三线变现排序
|
||||
|
||||
把上线、变现、发行三件事合起来看,绘境AI 的现行战略主轴是**一条有先后次序的三线变现**(与所有既有裁定自洽):
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
L1["① B端/IP 营销小游戏<br/>现金线 · 首位"] -->|供血| L2["② 自有端邀请制内测<br/>数据资产线"]
|
||||
L2 -->|养飞轮种子| L3["③ 渠道线<br/>规模线 · 微信/抖音 IAA"]
|
||||
L1 -.标杆案例.-> L3
|
||||
```
|
||||
|
||||
| 线 | 定位 | 为什么是这个合规定性 | 它产出什么 |
|
||||
|---|---|---|---|
|
||||
| **① B 端 / IP 营销小游戏(现金线·首位)** | 给 IP 方 / 品牌方做推广小游戏,项目收费 + 分成 | 推广内容**不是网络出版物**,版号争议最小、回款最早(已有"王蓝莓"案例点火) | 现金流 + 标杆案例 |
|
||||
| **② 自有端邀请制内测(数据资产线)** | 内测灰度,养数据回路 / 质量评分 / agent 闭环 | 按"试玩 demo"定性,**不公开经营、不接真钱** | 行为数据 + 产品迭代(飞轮种子,诚实地讲成未来式) |
|
||||
| **③ 渠道线(规模线)** | 微信 / 抖音小游戏备案 + IAA 广告变现 | 豁免通道成立,**平台代管实名 / 防沉迷** | 规模化曝光 + 真实 eCPM |
|
||||
|
||||
这条排序对外讲给投资人的故事主轴是:**单人 + AI agent 体系的资本效率(已实证)→ 用 B 端 / IP 现金线供血 → 在监管窗口期里把供给侧的成本优势,滚成内容资产与数据飞轮**(后半段是未来式,必须诚实标注)。
|
||||
|
||||
### 这套商业模型为什么成立:资本效率与护城河
|
||||
|
||||
绘境AI 的技术主张是一句话:**用开源生态组合(而非烧钱自研)快速搭出全闭环平台,把核心资金投在竞品做不了的事上——游戏流分发、创作者收益闭环、AI 数据驱动优化**。这套打法的资本效率对比很直观:
|
||||
|
||||
| 能力 | 竞品做法 | 绘境AI 做法 | 效果 |
|
||||
|---|---|---|---|
|
||||
| AI 生成引擎 | 自研(6-12 个月,投入千万) | new-api 网关直连通用 LLM + 模板 / 插件库驱动生成,数周集成 | 节省约 **80%** 时间和成本 |
|
||||
| 后台管理系统 | 自建(3-6 个月) | Huijing Cloud 开源二开,60%+ 能力开箱即用 | 节省约 **3 个月** |
|
||||
| 多模型切换 | 绑定单一供应商 | new-api 网关原生支持多 LLM 热切换 | **零锁定风险** |
|
||||
| 游戏运行时 | 自研重型引擎 | LittleJS 增强发行版 + 成熟引擎复用 | **零许可费** |
|
||||
|
||||
> 结论:同等功能,绘境AI 的技术投入约为头部自研竞品的 **1/5**,时间约为 **1/3**。MVP 阶段 **5 人核心团队 + ¥4,300/月 基础设施(年化约 5 万)** 即可在 **11 周** 内跑通,验证核心假设后再加人,资金 runway(可支撑的现金跑道)约 **18-24 个月**。
|
||||
|
||||
而真正难被复制的护城河,**不在生成引擎本身**(通用大模型 12-18 个月就会把纯生成能力追平),而在时间沉淀出来的四层壁垒:
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
subgraph 壁垒["四层护城河 — 越往后越难复制"]
|
||||
D["数据壁垒<br/>玩家行为 + 质量评分 + 推荐信号<br/>(需真实流量积累,无法购买)"]
|
||||
N["网络效应<br/>创作者多→内容富→玩家多→创作者更多<br/>(飞轮转起后追赶成本指数级增长)"]
|
||||
A["资产壁垒<br/>模板库 + 素材市场 + IP 合约<br/>(时间沉淀型资产)"]
|
||||
C["合规壁垒<br/>ICP + 文网文 + 广告资质 + 渠道关系<br/>(牌照+关系+流程,6-12 个月才齐备)"]
|
||||
end
|
||||
```
|
||||
|
||||
这四层里,**合规壁垒恰恰是 §2.1 那串闸门的另一面**——同一套资质,对自己是必须翻过的门槛,对后来者就是 6-12 个月才能补齐的护城河。所以"先把合规闸门跑通"既是上线的前置,也是在攒护城河。绘境AI 的窗口期判断是 **6-12 个月**:在此期间积累的数据与网络效应,将构成纯工具型竞品永远追不平的长期优势。竞争路线上,绘境AI **不追求技术深度第一,追求生态完整度第一**——技术够用即可,生态无法复制。
|
||||
|
||||
---
|
||||
|
||||
## 4. 还没拍板的事(活 · 阻塞 · 不可擅改)
|
||||
|
||||
运营域里有几件事**还压在创始人手上**,agent 不可擅自改动,登记在此以防失踪(明细见[战略与合规](../../agent-specs/战略与合规.md) §6):
|
||||
|
||||
- **合规死锁的最后一公里**:Doc A(产品需求清单)里那 8 项法定 P0 的换血,要等律所就两个核心问题(UGC 的出版物定性 / 防沉迷接入方式)给出意见后才能定。这是当前最大的外部阻塞。
|
||||
- **示例模板 / 创作起步面的去留**:早期 demo 里的"模板浏览 / 玩法模板中心"等入口要删除、降级还是重定义为"示例 Prompt 库 / 灵感画廊",会直接增删 55 项 P0 的验收集。
|
||||
- **对外叙事的单值收敛**:团队表述与唯一融资口径(早期 BP 改造版里有互斥的区间),需要创始人对外定一个单值——属对外叙事,不影响工程。
|
||||
|
||||
> 这类"灰色地带判断"(哪份文档过期了、两份冲突文档哪份胜出、一个补丁是不是架构信号)按项目治理约定,由**创始人 + 文档 / 设计线**这个有状态的主体担责,机器门做不出的判断不交给无状态的临时 agent。
|
||||
|
||||
---
|
||||
|
||||
## 5. 子文档导航
|
||||
|
||||
本页是运营域的入口与骨架,讲清了三件事各是什么、怎么咬合。每一块的明细——公式、红线、逐项闸门状态——在下列子文档,需要细节时再深入:
|
||||
|
||||
| 文档 | 回答什么 |
|
||||
|---|---|
|
||||
| [变现与单位经济](../../agent-specs/变现与单位经济.md) | 两平面 / 两钱包的架构边界、广告分账→钱包→提现的收益闭环、7 个资金动作的幂等键、分账公式与 eCPM 敏感性、资金一致性红线 |
|
||||
| [渠道发行](../../agent-specs/渠道发行.md) | 自有 H5 流与微信 / 抖音渠道的分工根因、L1 一壳多游模式、GameConfig 合规红线(契约 9c)、渠道引擎竞标(LittleJS vs Cocos)、真机取证七门 |
|
||||
| [战略与合规](../../agent-specs/战略与合规.md) | 战略总判定(四颗雷收口)、三线变现排序、合规死锁与双层定性、单位经济地基、demo↔产品定位纠偏、待创始人拍板项 |
|
||||
|
||||
跨域的两个跟踪 / 计算面(状态会变,以它们为准):
|
||||
|
||||
| 跟踪面 | 用途 |
|
||||
|---|---|
|
||||
| [A1 闸门看板](../../mvp/A1闸门看板.md) | 12+1 项合规闸门的唯一跟踪面——每项的 owner、最晚提交日、阻塞状态,变了即真相变了 |
|
||||
| [单位经济敏感性模型](../../mvp/单位经济敏感性模型.md) | 分账公式、eCPM 假设档、回本 DAU 测算与埋点测量计划的唯一计算面 |
|
||||
|
||||
> **纪律**:运营域只讲"怎么合规上线、怎么变现、怎么发行";用户能感知什么在[产品域](../产品/README.md),系统怎么搭、关键技术决策为什么在[架构域](../架构/README.md)。各域各司其职、互不重复。本页的源头是两份长文档——`系统概要设计-投资人版.md`(HJ-ARCH-002,商业合理性与资本效率)与 `战略与合规.md`(子系统 SoT,战略 / 合规 / 经济活结论的汇总入口);需要逐条考据时回溯它们。
|
||||
226
docs/architecture/运营/变现与单位经济.md
Normal file
226
docs/architecture/运营/变现与单位经济.md
Normal file
@ -0,0 +1,226 @@
|
||||
# 变现与单位经济
|
||||
|
||||
> **这是什么**:绘境AI 的钱怎么进、怎么算、怎么发,以及这门生意在单位经济上到底成不成立。它把"收益闭环 + 成本来源 + 单位经济模型 + 资金一致性红线"四件事讲清楚,既回答"平台靠什么赚钱、成本花在哪",也回答"一款游戏在最细的颗粒度上是赚是赔"。
|
||||
> **给谁看**:创始人、财务、运营、负责变现链路的后端工程师,以及做融资尽调时关心资本效率的投资人。
|
||||
> **怎么读**:先读 §1 建立"两个平面、两套钱包"的全局认知,这是本域所有规则的地基;再按需深入收益闭环(§2)、成本侧(§3)、单位经济模型(§4)、资金一致性红线(§5)。具体的敏感性数字(各档 eCPM、回本 DAU、提现门槛可达性)在 `docs/mvp/单位经济敏感性模型.md`,本页只取结论。
|
||||
> **和[商业定位](../产品/商业定位.md)的区别**:商业定位讲"我们靠什么赢"(战略叙事);本页讲"账具体怎么算、钱怎么流"(可执行的财务与工程口径)。
|
||||
|
||||
---
|
||||
|
||||
## 1. 一张图看懂:两个平面、两套钱包
|
||||
|
||||
变现这件事最容易出错的地方,是把"花出去的钱"和"赚回来的钱"混在一起算。绘境AI 用一条铁律把它们隔开:**钱往外花和钱往回赚,是两个方向、两套系统,永不混淆。**
|
||||
|
||||
具体拆成三道硬边界:
|
||||
|
||||
- **生成计费平面**(钱往外花)由 **new-api** 负责。new-api 是一个开源的 LLM(大语言模型)统一网关——所有对 DeepSeek、通义千问、MiniMax 这些底层模型的调用都从它走,它顺带记账、限额、做可观测。它只管"创作者生成游戏时消耗了多少额度",配额只减不增,既不结算也不碰合规。
|
||||
- **收益与支付平面**(钱往回赚)由后端的 **game-module-trade / game-module-pay** 两个业务模块负责。广告分成入账、提现、结算、合规,全在这里。
|
||||
- 为什么不能把广告分成塞进 new-api 的配额里?因为"配额只减"是钱的反方向;赚回来的钱必须经过业务钱包结算、过合规面,把收益硬塞进一个只会扣减的计量系统是**范畴错误**。
|
||||
|
||||
在收益平面内部,还要再分两套钱包,因为它们是两个账户、两个钱流方向:
|
||||
|
||||
- **创作者收益钱包**(在 trade 模块):创作者挣到广告分成、提现到微信/支付宝。**已建成。**
|
||||
- **消费钱包**(在 pay 模块):用户充值、形成余额、扣减内购。**尚未接入单体。**
|
||||
|
||||
最后还有一道精度边界,跨平面对账时必须显式换算:**成本侧全程用"元"**(浮点数,活在编排器域),**收益侧全程用"分"**(BIGINT 整数,活在后端数据库域)。两个精度域**严禁放在同一列里直接做减法**。
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
subgraph GEN["生成计费平面 = new-api(权威成本源 · 别自建)"]
|
||||
NT["创作者 token<br/>生成调用走自己的 token 扣额"]
|
||||
NL["logs.quota 表<br/>每次调用的真实倍率成本"]
|
||||
NT --> NL
|
||||
end
|
||||
subgraph REV["收益侧 = game-module-trade/pay(业务钱包)"]
|
||||
AD["SDK 广告 → ad 计费<br/>game_ad_revenue(分 · 带 game_id)"]
|
||||
SET["SettlementJob T+1 结算<br/>recordIncome 逐笔入账"]
|
||||
TI["game_trade_income<br/>+ 创作者钱包余额"]
|
||||
WD["提现:冻结 → 审核 0→1→2/3/4<br/>→ 异步打款"]
|
||||
AD --> SET --> TI --> WD
|
||||
end
|
||||
subgraph PAY["消费侧 = pay(未接入单体)"]
|
||||
WAL["充值 → 消费钱包余额<br/>reduceWalletBalance 内购扣款"]
|
||||
end
|
||||
NL -.->|读 logs.quota 为权威| COST["成本台账<br/>newapi_cost.py"]
|
||||
classDef boundary fill:#fee,stroke:#c33,stroke-width:2px;
|
||||
class REV,PAY boundary;
|
||||
```
|
||||
|
||||
> 红框是边界铁律:**trade/pay 的钱永不写进 new-api**(已用 grep 确认 new-api 里没有任何收益写入路径);广告分成只出现在 trade。这条边界一旦破掉,成本和收益就会互相污染,账再也对不平。
|
||||
|
||||
---
|
||||
|
||||
## 2. 收益闭环:从一次广告展示到创作者提现
|
||||
|
||||
这是平台对创作者最核心的承诺——**当天创作、当天有人玩、当天看到收益**。整条链路从玩家看到一次广告开始,经过计费、T+1(次日)结算、入账,最后到创作者点击提现,每一步都已落地并有单元测试覆盖(对应交付件 HJ-M4-REAL-001,已合入主干)。
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
A["创作者发布游戏"] --> B["游戏信息流曝光<br/>玩家试玩"]
|
||||
B --> C["广告展示<br/>激励视频 / 插屏 / Banner"]
|
||||
C --> D["广告计费<br/>合规校验 + 幂等 + 归因"]
|
||||
D --> E["T+1 自动结算<br/>逐笔入账创作者钱包"]
|
||||
E --> F["满 ¥5 即可提现<br/>微信 / 支付宝"]
|
||||
```
|
||||
|
||||
按链路顺序拆解每一步在做什么:
|
||||
|
||||
1. **广告计费。** 玩家触发一次广告后,先做合规校验(屏蔽未成年人,内部叫 `blockMinor`),再用一个唯一追踪键 `uk_trace` 做幂等(保证同一次广告事件不会被重复计费),通过 `ProjectApi` 把这笔收入归因到具体项目,最后落到 `game_ad_revenue` 表。激励视频按 reward(奖励)收入口径单独计,避免和普通展示双算。
|
||||
2. **分账结算。** `SettlementService` 把广告收入逐条入账到创作者钱包,用 `uk_source` 唯一键防重,并遵循"先入账、后回标"的补偿顺序(先把钱记进去,再把这笔来源标记为已结算,即便中途失败也不会丢账)。这个结算任务挂在 XXL-Job(一个分布式定时任务调度框架)上,T+1 自动跑。
|
||||
3. **提现。** 创作者发起提现时,先看是否达到门槛(可在 Nacos 配置中心动态调,键名 `withdraw-min`,当前是满 5 元),再用 CAS(比较并交换,一种乐观锁,保证并发下余额不会被扣两次)冻结金额,然后走一个审核状态机:`0(待审)→1(审核通过/打款中)→2(成功)/3(驳回)/4(打款失败)`。
|
||||
4. **广告渠道可插拔。** 对接不同广告联盟靠一个 SPI(服务提供接口)扩展点 `AdProvider` 加上工厂 `AdProviderFactory`;没注册真实渠道时自动降级到 mock(假数据)。穿山甲(csj)、优量汇(gdt)两家只差一个实现类,业务代码零改动即可切真。
|
||||
5. **打款异步化。** 审核从 `0→1` 时发起 `PayTransferApi` 转账,但这一步**不阻塞审核事务**;真正的成功/失败由支付回调(pay notify)驱动:回调成功推 `1→2`,失败推 `1→4` 并把冻结金额退回余额。数据库里有一列 `transfer_ref`(在迁移 V14 引入)作为对账锚点。
|
||||
6. **打赏现金发放。** 用 `recordIncome(source=TIP, ...)` 入账 + 打赏记录状态 CAS `0→1`,补齐了打赏(TIP)这个唯一真实入口。
|
||||
|
||||
**每一个动钱的动作都有自己的幂等键。** 这是资金安全的命门——总共 7 个资金动作,逐个都挂了防重保护,任何重放都不会让钱多发或多记:
|
||||
|
||||
| 资金动作 | 幂等键 |
|
||||
|---|---|
|
||||
| 广告计费 | `game_ad_revenue.uk_trace(trace_id, event_type, tenant)` |
|
||||
| 分账入账 | `game_trade_income.uk_source(source, source_ref, tenant)` |
|
||||
| 结算回标 | `markSettled` 仅 `0→1`(`WHERE settle=0`) |
|
||||
| 提现申请 | `game_trade_withdraw.uk_biz_no` |
|
||||
| 审核流转 | CAS `WHERE status=expect` |
|
||||
| 打款回调 | pay `out_trade_no=withdraw.id` + 状态 CAS(`1→2` / `1→4`) |
|
||||
| 打赏发放 | `recordIncome` 的 `uk_source` + 打赏状态 CAS(`0→1`) |
|
||||
|
||||
> **对比竞品的意义。** 极逸 SOON 要做到商业级才有收益,门槛极高;TapTap 制造分成低、月结、创作者几乎感知不到。绘境AI 的差异不在"能不能赚",而在"当天就能看到收益"这个即时反馈——这正是把创作者留下来的关键。
|
||||
|
||||
---
|
||||
|
||||
## 3. 成本侧:new-api 是唯一的权威成本源
|
||||
|
||||
绘境AI 的整套技术哲学是**用开源生态组合而非烧钱自研**(详见[投资人版概要设计](../系统概要设计-投资人版.md)),把钱省下来花在竞品做不了的事上——游戏流分发和变现闭环。这一节回答"那生成一款游戏到底花多少钱"。
|
||||
|
||||
结论先行:**文本生成根本不是成本瓶颈。** 实测一款游戏的生成成本约 **¥0.031**(merge-prod-20 这一批 20 个创意、57 次调用、19 个被采纳的实测口径),单次 LLM 调用约 ¥0.0103。即便单价翻 10 倍也不到 1 元一款。真正可能咬人的成本是图片/音乐这类素材生成的 GPU 开销(目前 ComfyUI 未部署,零数据)和人工审核兜底,这两项列为后续单位经济埋点(内部代号 **W4**,即按游戏粒度的成本/收益数据回路)的测量项。
|
||||
|
||||
成本来源与口径有几条硬规定:
|
||||
|
||||
- **谁是权威源。** 成本数据**直读 new-api 的 PostgreSQL 日志表 `logs.quota`**,因为里面含每个模型的真实计费倍率(new-api 的计费引擎已经算好)。客户端用 token 数自己估算的那套(`llm_client.py`)**降级为 fallback 和交叉校验**,不再作为权威。读取工具是 `orchestrator/newapi_cost.py`。
|
||||
- **换算口径。** new-api 内部用一个抽象单位记额度,换算关系是 `QuotaPerUnit = 500000/USD`(这是 new-api 编译时的默认值,数据库里查不到这个 key),再乘 USD→¥ 的汇率假设 7.2。
|
||||
- **new-api 能力的真实边界**(2026-06-11 实查):`users / tokens / logs / channels` 这四张表已被 917 条日志的真实流量验证过,是可信的;但 `redemptions / top_ups / subscription_*`(兑换码/充值/订阅)这些表**schema 在、却从未跑过一行数据**(这个 fork 没验证过这些功能,不要当成就绪能力)。
|
||||
- **🔴 P0 级安全红线(开通任何创作者账户前必修)。** new-api 的 3000 端口当前绑在 `0.0.0.0` 且防火墙 ufw 未启用,等于 admin API 和登录页公网可达;而 `channels` 表里存着上游厂商的 API key——一旦泄露就是直接被盗刷上游账单。**必须把 3000 端口收进内网**(用 Tailscale 内网或防火墙只放行 game-cloud 访问);另外,给每个创作者发的 per-creator token 本质是支付工具,**绝不能明文落进 `game_player` 表**。
|
||||
|
||||
---
|
||||
|
||||
## 4. 单位经济模型:这门生意在数字上成不成立
|
||||
|
||||
### 4.1 分账公式(R3 拍板,唯一口径)
|
||||
|
||||
这套分成规则在 2026-06-10 由创始人在审计收口(代号 R3)中拍定,是全平台唯一口径,**账永远不会为负**:
|
||||
|
||||
```
|
||||
总广告消耗 G ──渠道扣 40%【确证:微信 IAA 现金分成 60%】──> 平台实收净额 N = 0.6G
|
||||
├ 无 IP 素材:创作者 0.8N(=0.48G) | 平台 0.2N(=0.12G)
|
||||
└ 带 IP 素材:创作者 0.55~0.65N | IP 方 0.15~0.25N(从创作者份额内出) | 平台恒 0.2N
|
||||
```
|
||||
|
||||
这里的几个术语:**IAA** 指 In-App Advertising,游戏内广告变现(区别于内购);**eCPM** 指每千次广告展示的有效收入。关键设计是:无论用不用 IP 素材,**平台永远恒留 20%**;IP 授权方的分成是从创作者那一份里出的,不额外加码。所以"创作者 80%"和"创作者 55-65% + IP 方 15-25%"说的是同一个模型的两种表述。自有端(远期、过合规闸门后)走广告联盟时,把渠道 40% 替换成联盟实际扣率即可,公式不变,基数仍是实收净额。
|
||||
|
||||
### 4.2 敏感性结论(数字明细见敏感性模型)
|
||||
|
||||
eCPM 取三档做敏感性扫描:**悲观 15 / 基准 30 / 乐观 60 元/千次**(激励视频口径)。注意这是用来摸边界的扫描,**不是收入预测**——外部核查只拿到从业者给的 20-80 区间,低置信。
|
||||
|
||||
这个模型最重要的一句话,也是它对战略的真正贡献:
|
||||
|
||||
> **自有端 IAA 是"万级 DAU 才回本"的规模游戏。** 在基准档(eCPM 30、人均 3 次展示/日)下,覆盖 ¥4,300/月基建成本需要约 **1.33 万 DAU**(日活用户)。种子期只有百级 DAU 时,月留存只有几十元量级。
|
||||
|
||||
这条结论直接决定了变现三线的排序:**近期现金只能来自 B 端定制 / IP 项目制收费(与 DAU 无关),IAA 广告是远期的规模账。** 把希望寄托在 IAA 早期回本是不现实的。
|
||||
|
||||
关于"满 5 元提现"对创作者的可达性:头部款可达(eCPM 30 时需单款约 12 次激励展示/日),长尾款不可达。因此**对创作者宣传"能赚钱"时,必须用头部款的口径,不能做普遍性承诺**——这是审计 R3 连带的诚信红线。
|
||||
|
||||
### 4.3 三个假设值的红线
|
||||
|
||||
成本侧涉及三个还没拿到真实账单、暂时占位的假设值。创始人 2026-06-11 已采纳一条红线:**在拿到真实账单前,这三个值一律标注【假设·待测】,不作为预测口径,不对外锁价。**
|
||||
|
||||
| 假设值 | 当前占位 | 含义 |
|
||||
|---|---|---|
|
||||
| `PRICE_PER_MTOKEN_YUAN` | 2.0 | 每百万 token 的人民币单价 |
|
||||
| `QUOTA_PER_UNIT` | 500000 | new-api 额度单位换算(每 USD) |
|
||||
| `USD_CNY` | 7.2 | 美元兑人民币汇率 |
|
||||
|
||||
---
|
||||
|
||||
## 5. 资金一致性红线(执行时必守)
|
||||
|
||||
下面四条是动钱代码必须遵守的不变量。它们看似细节,但每一条背后都对应一类真实会丢钱或多发钱的事故。
|
||||
|
||||
1. **打款"发起"不等于"到终态"。** 资金状态以支付回调(pay notify)为准,转账发起和 `1→2`(打款成功)必须解耦;**严禁在审核事务里同步置为"已打款"**(mock 渠道除外,但它仍走状态 CAS)。万一回调丢失,要有对账补偿任务定期扫描 `status=1` 的超时单,主动去查支付方的真实状态来补推进度(用幂等 CAS 保证不重复)。
|
||||
2. **降级要 fail-fast(快速失败),不能静默。** 广告计费降级到 mock 是可接受的(顶多少算一点收入);但**打款降级到 mock = 假装打了款却吞掉真钱**——这种情况必须 fail-fast 把提现单挂起,**绝不允许静默降级发真钱**。
|
||||
3. **逐笔向下取整,禁止用聚合总额反推。** 链路上有两次独立的向下取整(广告侧按 eCPM/1000 算、结算侧 `setScale DOWN`),导致"逐笔净额之和"不等于"总毛额 × 0.80"。所以**对账必须以逐笔为权威**(单笔净额 == `floor(毛额 × 比例)`),**禁止用"总额 × 0.80"反推**;日期键统一取上游的 `stat_date`。
|
||||
4. **mock 不变式(接真实联盟前的前置)。** 当前 MVP 下 `revenue_amount` 恒等于净额 N(渠道扣率为 0,即 G == N)。**真实接入广告联盟前,必须先定清楚 `revenue_amount` 到底存毛额 G 还是净额 N**:如果存 G,trade 就得先 ×(1−渠道扣率) 再 ×创作者份额(即先 ×0.6 再 ×0.8),否则既违反 R3 公式,历史快照也无法回算。这是支付真实化的阻塞前置项。
|
||||
|
||||
---
|
||||
|
||||
## 6. 现状与待办
|
||||
|
||||
**✅ 已落地:** 收益闭环(广告计费 / 分账 / 提现 / 打款异步 / 打赏到账 / 广告回调骨架,sandbox 与 staging 环境的端到端测试均可验)+ 成本侧 new-api 权威成本接通。对应交付件 HJ-M4-REAL-001(收益闭环)与 W4 成本薄片。
|
||||
|
||||
**⏸ 待办与推迟:**
|
||||
|
||||
- **按游戏粒度的成本/收益埋点(W4 ③④,未落)。** 广告产值侧走只读聚合 `group by game_id`(`game_ad_revenue` 已有 `game_id` 列和索引,零建表);创作者应得侧要补 `game_trade_income.game_id` 打通四步链(契约 `ad.yaml` 加字段 + DTO 加 `gameId` + 数据库 ALTER 加列 + `recordIncome` 透传,缺一即断)。**这里有一条防重计数的硬约束**:所有写入和透传只能发生在"首次真实入账"分支(`firstRecord/firstBilling`),命中幂等键的回查分支绝不能累加,否则同一来源重放会把统计虚高;验收必须包含 staging 重放后按游戏汇总不变(不能只靠单测 mock)。**这里有一条架构裁决**:否决了"往 telemetry 的统计表加 ad/income 列"的做法——那条路径实为新建跨模块写接缝(telemetry 没有对应写 API、签名已冻结、trade 也不依赖 telemetry),还会把"恰好一次"的保证从 ad/trade 错位到 telemetry,得不偿失。
|
||||
- **per-creator 计量 + 支付→配额同步 + 订阅/充值 + UI 购买流(推迟)。** 被支付通道真实化阻塞(ICP 备案 7-20 工作日 + 微信进件 1-3 周,3-5 周内无法端到端;订阅定价也未定)。还有一个可行性硬阻塞:new-api 的 admin API **无法替他人铸 token**(`AddToken` 硬编码成调用者自己的 UserId),得重新设计一套"凭证引导"流程;而且支付→配额**不是本地事务**,真实机制是三方对账(微信交易号 ↔ 平台订单 ↔ new-api 充值记录,后者的 `trade_no` 是 UNIQUE 幂等锚)。
|
||||
- **消费侧钱包最小闭环(新增范围,待创始人 go/no-go)。** pay 模块尚未接入单体;好在 admin 手动加余额的能力是现成的(底层框架自带 `PUT /pay/wallet/update-balance`,审计已实测接入后启动干净、零冲突)。最小闭环 = 接入 pay + admin 加余额 + 1 条内购扣款。**裁断:属 v2.0,非 MVP 必需**(MVP 关键路径仍是日历闸门 + IAA 广告闭环)。会员订阅同样未建。
|
||||
- **真实广告联盟(csj/gdt)+ 真实打款渠道(微信企业付款)(被合规日历闸门阻塞)。** 代码侧切真**零改业务码**:广告侧注入 `CsjAdProvider`/`GdtAdProvider` 并改配置;打款侧在 Nacos 把 `trade.payout-channel` 设为 `wxpay` 并填商户密钥即可。
|
||||
|
||||
---
|
||||
|
||||
## 7. 资本效率视角:成本、团队、Runway
|
||||
|
||||
这一节从投资人尽调的角度收口——把上面的单位经济放进整体资本效率的叙事里。源头是[投资人版概要设计](../系统概要设计-投资人版.md)(文档编号 HJ-ARCH-002)。
|
||||
|
||||
### 7.1 一句话:技术资本效率
|
||||
|
||||
绘境AI 的核心打法是**把别人烧钱自研的部分用开源组合替掉,把省下的钱投到竞品的最大空白——流量和变现上**。同等功能,绘境AI 的技术投入约为头部自研竞品的 1/5,时间约为 1/3。
|
||||
|
||||
| 能力 | 竞品做法 | 绘境AI 做法 | 节省 |
|
||||
|---|---|---|---|
|
||||
| AI 生成引擎 | 自研(6-12 月,投入千万) | new-api 网关直连通用 LLM + 模板/插件库驱动,数周集成 | 约 80% 时间和成本 |
|
||||
| 后台管理系统 | 自建(3-6 月) | Huijing Cloud 开源二次开发,60%+ 能力开箱即用 | 约 3 个月 |
|
||||
| 工作流 / 审核 | 自建 | Flowable 工作流引擎(Huijing 内置) | 零开发成本 |
|
||||
| 多模型切换 | 绑定单一供应商 | new-api 网关原生支持多 LLM 热切换 | 零锁定风险 |
|
||||
| 游戏运行时 | 自研重型引擎 | LittleJS 增强发行版(Tier1)+ Cocos Creator(远期) | 零许可费 |
|
||||
|
||||
### 7.2 MVP 阶段月度成本:¥4,300/月
|
||||
|
||||
整个 MVP 跑起来一个月只要 **¥4,300**(年化约 5 万)。这也是 §4.2 那条"覆盖基建需 1.33 万 DAU"结论里的成本基数。
|
||||
|
||||
| 项目 | 月费 | 说明 |
|
||||
|---|---|---|
|
||||
| 云服务器 | ¥2,400 | 3 台 4C16G(业务[含 SAA 进程内编排] + new-api 网关 + 中间件;图片/音乐及生成 LLM 走外部 API,不占自建算力) |
|
||||
| 数据库 | ¥400 | MySQL RDS |
|
||||
| 对象存储 + CDN | ¥300 | 游戏包 / 素材 / 封面 |
|
||||
| LLM API | ¥1,000 | 约 10,000 次生成/月 |
|
||||
| 其他(域名/SSL/短信) | ¥200 | — |
|
||||
| **合计** | **¥4,300/月** | 年化约 5 万 |
|
||||
|
||||
> 这里的 **SAA**(Spring AI Alibaba)是后端进程内的 AI 编排框架,用它的 StateGraph 编排生成流程,无需额外部署独立的编排服务,所以不占新增机器成本。
|
||||
|
||||
### 7.3 团队与 Runway
|
||||
|
||||
MVP 阶段 **5 人核心团队**(2 后端 + 1 前端 + 1 AI/生成 + 1 产品运营),年人力成本约 150-214 万。以种子轮 3000 万计算,**Runway(资金可支撑的时长)为 18-24 个月**。团队的演进路径是:
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
A["MVP 阶段 · 5 人<br/>2 后端 + 1 前端<br/>1 AI + 1 产品运营"] --> B["增长期 · 15 人<br/>补齐设计 / 安全 / 数据 / 测试"]
|
||||
B --> C["规模期 · 30-50 人<br/>分拆为<br/>创作 / 分发 / 变现 / 平台 四团队"]
|
||||
```
|
||||
|
||||
> **结论(资本效率)**:5 人团队 + ¥4,300/月基础设施 + 11 周做出 MVP,先验证核心假设(生成成功率 ≥80%、信息流首屏 P75 < 3s、当天创作当天有收益的闭环),再按数据加人。所有核心组件都是开源/可替换的,没有单点供应商锁定。
|
||||
|
||||
---
|
||||
|
||||
## 8. 关键指针
|
||||
|
||||
本页只承载结论与口径。落地细节、决策史、数字明细分别去这些地方:
|
||||
|
||||
| 你要找 | 去哪里 |
|
||||
|---|---|
|
||||
| 单位经济的全部敏感性数字(各档 eCPM、回本 DAU、提现可达性、成本结构明细) | `docs/mvp/单位经济敏感性模型.md` |
|
||||
| 变现链路的工程落地(模块、状态机、契约) | `.agents/skills/`(`staging-ops` / `add-business-module`) |
|
||||
| 契约与数据库迁移 | `contracts/api-schemas/{ad,trade}.yaml`;迁移 V6(ad)/ V7(trade)/ V14(M4 的 `transfer_ref`);**Flyway 下一个空位 = V18**(V14-V17 已被占用,给 `game_trade_income.game_id` 加列须用 V18) |
|
||||
| 成本台账工具 | `orchestrator/{newapi_cost.py, report.py}` |
|
||||
| 商业定位与竞争战略 | [商业定位](../产品/商业定位.md) |
|
||||
| 完整技术资本效率叙事 | [投资人版概要设计](../系统概要设计-投资人版.md) |
|
||||
|
||||
> **加列纪律(给后端):** 给已有表加列必须 `NOT NULL DEFAULT 0`(不进唯一键、兼容存量行);全仓零外键,数据归属隔离靠 Mapper 的 `WHERE` 条件加 `getLoginUserId()`,不靠 `@DataPermission`。同一列在多个副本间必须字节一致,禁止出现第三个副本(V7 残留三副本是反面教材)。
|
||||
237
docs/architecture/运营/合规闸门.md
Normal file
237
docs/architecture/运营/合规闸门.md
Normal file
@ -0,0 +1,237 @@
|
||||
# 运营域 · 合规闸门
|
||||
|
||||
> **这是什么**:绘境AI 在中国大陆上线前必须穿越的一组**合规闸门**(compliance gate——一道必须通过的法定或资质前置条件,没过就不能上线对应功能)的设计说明。它回答"**为什么会卡、卡在哪、按什么顺序拆**"。
|
||||
> **给谁看**:创始人、运营、法务对接人,以及任何需要理解"产品做得出来但为什么还不能公开经营"的同事。
|
||||
> **怎么读**:先读本页建立全局认知(死锁的成因、双层定性的破局思路、12+1 道闸门的全貌);**逐项的实时状态、负责人、最晚提交日**不在本页,而在唯一跟踪面 [`A1 闸门看板`](../../mvp/A1闸门看板.md)——状态会变,看板才是当下真相,本页只讲不变的设计逻辑。
|
||||
> **边界**:本页只讲"合规"这半。战略叙事(三线变现排序、护城河、经济模型的对外口径)已归到[产品域的商业定位](../产品/商业定位.md),本页不重复。
|
||||
|
||||
---
|
||||
|
||||
## 1. 一句话与一张图:合规为什么是绕不开的主线
|
||||
|
||||
绘境AI 的工程能力可以把一款小游戏从一句话生成出来,但**"做得出来"不等于"能合法地公开经营"**。在中国大陆,一款面向公众、可被陌生人玩到、并且能产生收入的网络游戏,要同时满足三组监管要求:游戏本身要有**版号**(国家新闻出版署核发的网络游戏出版物号,是游戏合法商业化运营的前提)、要接入官方的**实名与防沉迷**系统、平台代收代付的资金要有合法的归集与分账资质。绘境AI 的产品形态——用户用一句话生成的 UGC(User-Generated Content,用户生成内容)小游戏,数量多、单款轻、无版号——恰好让这三组要求互相缠成一个死结。
|
||||
|
||||
破局的办法不是硬闯,而是**双层定性**:把"自有平台"和"渠道分发"拆成两条合规路径,各自走各自能成立的通道。下面这张图是理解整份文档的主干——它说明三组监管要求如何缠成死锁,以及双层定性如何把死锁解开:
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
subgraph 死锁["合规死锁:三条约束互相锁死"]
|
||||
direction TB
|
||||
A["无版号<br/>(UGC 小游戏拿不到/拿不起版号)"] --> B["接不进官方实名/防沉迷系统<br/>(版号是接入前提)"]
|
||||
B --> C["自有端无法履行<br/>实名/防沉迷法定义务"]
|
||||
A --> D["IAA 免版号豁免<br/>仅在微信/抖音备案制内成立"]
|
||||
D -.->|"自有 H5 不适用<br/>(监管灰区·有罚没先例)"| C
|
||||
E["平台代收广告费<br/>再分账给个人创作者"] --> F["无牌照资金归集<br/>=二清风险"]
|
||||
end
|
||||
subgraph 破局["双层定性:拆成两条路径"]
|
||||
direction TB
|
||||
G["自有端<br/>收敛为邀请制内测<br/>·不公开经营·不接真钱"] -->|"试玩 demo 定性<br/>规避出版/防沉迷义务"| H["合规成立"]
|
||||
I["变现走渠道<br/>(微信/抖音小游戏备案)"] -->|"豁免通道成立<br/>渠道代管实名/防沉迷"| H
|
||||
end
|
||||
C -.->|"解"| G
|
||||
F -.->|"解"| I
|
||||
```
|
||||
|
||||
这条主线的含义很直接:**短期内,绘境AI 的"真钱"只能从渠道线(微信/抖音小游戏的 IAA 广告)和 B 端项目制收费来;自有平台先以邀请制内测养数据,等律所意见明确、资质到位后再谈公开经营。** 后面几节逐层展开这个判断。
|
||||
|
||||
> **术语补充——IAA**:In-App Advertising,应用内广告变现,即游戏不向玩家收费、靠展示广告(激励视频、插屏等)赚钱。微信/抖音对走 IAA 的小游戏开了"备案制豁免版号"的通道,但这个豁免**只在它们各自的渠道生态内成立**,搬到自营 H5 网站就不适用。
|
||||
> **术语补充——二清**:二次清算的简称。指没有支付牌照的平台,先把多方资金归集到自己账户、再二次结算分发给各收款方的行为,属监管红线。
|
||||
|
||||
---
|
||||
|
||||
## 2. 死锁的三条链(为什么不能直接公开经营)
|
||||
|
||||
死锁不是单一障碍,而是三条彼此咬合的链条。把它们讲清楚,才能理解为什么"双层定性"是唯一能同时解开三条链的解法。
|
||||
|
||||
**第一条链:版号 → 实名/防沉迷义务无法履行。** IAA 免版号的豁免只在微信/抖音的备案制框架内成立,自有 H5 平台不适用——这是监管灰区,且有先例:曾有平台(如 Roblox 在国内停服)因无版号运营被处理,绘境AI 内部记录里也明确提到过**罚没 111 万元**这一量级的先例。没有版号,就接不进出版署官方的实名认证与防沉迷系统;接不进,自有端作为运营方就**无法履行**法定的实名与防沉迷义务。义务无法履行,公开经营就站不住。
|
||||
|
||||
**第二条链:平台代收代付 → 二清风险。** 假设绘境AI 在自有平台接广告联盟,由平台统一代收广告费、再按比例分账给一个个**个人创作者**,这等于平台在做一笔无牌照的资金归集与二次清算,踩的就是二清红线。
|
||||
|
||||
**第三条链:三条约束互为前提,形成连环锁。** 上面两条不是并列的,而是连环的——无版号导致义务无法履行,而要做真实变现又必然触发资金归集,于是"想合法经营 → 需要版号 → UGC 拿不到 → 接不进官方系统 → 履行不了义务 → 还要碰二清"形成一个闭环。任何单点突破都解不开,必须换一种整体定性。
|
||||
|
||||
**解 = 双层定性。** 把平台收敛为**邀请制内测**(用邀请码注册,不开放公开注册),明确"不公开经营、不接真钱",从而落在"**试玩 demo / 内测**"的定性上,规避公开运营才会触发的出版与防沉迷义务;同时把真实变现**全部放到渠道线**,借微信/抖音的备案豁免通道成立,实名/防沉迷由渠道代管。这就是 §1 那张图右半边的两条路径。这一裁决对应内部审计 HJ-AUDIT-001(对整体战略做的一次三视角审计,代号 HJ-AUDIT-001,排查出四颗"雷")里的第二颗雷 R2"合规死锁",已收口为本节的双层定性结论。
|
||||
|
||||
> **重要边界——内测定性的成立条件尚待律所确认。** "邀请制内测"能否稳稳落在"试玩 demo"定性上,其边界(人数规模上限?链接是否可分享?是否允许有任何收入?)目前是绘境AI 的工程判断,**不是已经验证的法律结论**。这正是要约律所的核心两问之一(见 §5)。在律所书面意见到位前,自有端按"不接真钱"运行——这也是现状,所以不引入新增风险。
|
||||
|
||||
---
|
||||
|
||||
## 3. 12+1 道法定闸门的全貌
|
||||
|
||||
死锁的解法定了方向,但落地仍要逐项穿越一组具体的合规闸门。把它们汇总起来,一共是 **12+1 道**:8 项国家强制的 C 端(面向消费者)法定要求 + 审计新增的 3 项 + 1 次律所咨询。下表给出全貌与分组。**注意:本表只解释每道闸门"是什么、为什么需要",它们的实时状态、负责人和最晚提交日不在这里,而在唯一跟踪面 [`A1 闸门看板`](../../mvp/A1闸门看板.md)。**
|
||||
|
||||
> **代号说明——A1**:这组闸门在内部作战清单里编号为 A1 任务项,故其跟踪看板称"A1 闸门看板"。"A1" 不含其他含义,就是任务编号。
|
||||
|
||||
### 3.1 八项 C 端法定 P0 要求(国家强制)
|
||||
|
||||
这八项是面向终端用户、监管强制要求的基础合规义务,在 MVP 阶段即被列为 **P0**(Priority 0,最高优先级、MVP 必须验收的功能):
|
||||
|
||||
| 法定要求 | 一句话说明 |
|
||||
|---|---|
|
||||
| **C 端实名** | 用户须完成真实身份核验后方可使用,是后续防沉迷的前提 |
|
||||
| **防沉迷:时长 / 宵禁** | 未成年人游戏时长限制 + 夜间禁玩时段(宵禁) |
|
||||
| **未成年充值限制** | 按年龄分档的单次 / 单月充值上限 |
|
||||
| **AIGC 显式标识** | AI 生成内容须显著标注"AI 生成",依据《人工智能生成合成内容标识办法》(2025-09 施行) |
|
||||
| **青少年模式** | 为未成年人提供受限的内容与功能模式 |
|
||||
| **关闭个性化推荐** | 必须为用户提供一键关闭算法个性化推荐的入口 |
|
||||
| **账号注销** | 用户有权注销账号并删除个人数据 |
|
||||
| **提现实名 / 税务** | 创作者提现须实名并完成税务代扣代缴 |
|
||||
|
||||
> **为什么 AIGC 标识对绘境AI 尤其关键**:绘境AI 的核心产品就是"AI 生成游戏",平台上几乎每一款游戏都是 AIGC(AI-Generated Content,人工智能生成内容)产物。因此《人工智能生成合成内容标识办法》对绘境AI 不是边缘条款,而是直接命中主营业务的强制义务——生成的游戏必须带显式的"AI 生成"标识。
|
||||
|
||||
### 3.2 审计新增的三项(HJ-AUDIT-001 R2)
|
||||
|
||||
除了八项基础要求,内部审计 HJ-AUDIT-001 在排查 R2(合规死锁)时又识别出三项过去被忽略、但同样是硬性前置的闸门:
|
||||
|
||||
| 新增闸门 | 为什么需要 |
|
||||
|---|---|
|
||||
| **大模型登记** | 自建平台调用第三方大模型 API 对公众提供生成服务,须办理生成式 AI 服务的大模型登记。周期以"数月"计 |
|
||||
| **算法备案** | feed(游戏信息流)按质量分重排序属于算法推荐服务,须依《互联网信息服务算法推荐管理规定》做算法备案。周期同样以"数月"计 |
|
||||
| **分账 / 灵工方案选型** | 为规避 §2 第二条链的二清风险,须在三个合法通道里选定一个资金分账方案 |
|
||||
|
||||
> **为什么大模型登记 / 算法备案不能拖**:这两项的办理周期都以**数月**计,是全部闸门里最长的。绘境AI 在对外(奇绩)的口径里承诺"8 月中旬材料就绪",所以材料准备必须尽早启动——材料的拟写可以由 AI 代劳,但**提交动作只有创始人能做**。
|
||||
|
||||
### 3.3 加一:律所合规咨询(R2 修法落地件)
|
||||
|
||||
最后"+1"是一次**律所合规咨询**(千元级预算的单次会议)。它不是法定义务本身,而是"解锁其余闸门"的钥匙:很多闸门的定性边界(内测能否豁免、二清如何规避、备案是否在内测阶段就触发)需要专业法律意见才能拍板。详见 §5。
|
||||
|
||||
### 3.4 一张图看清这 12+1 道闸门
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
subgraph 法定8["八项 C 端法定 P0(国家强制)"]
|
||||
direction LR
|
||||
L1[C端实名] --- L2[时长/宵禁] --- L3[未成年充值限制] --- L4[AIGC显式标识]
|
||||
L5[青少年模式] --- L6[关闭个性化推荐] --- L7[账号注销] --- L8[提现实名/税务]
|
||||
end
|
||||
subgraph 新增3["审计新增 3 项(HJ-AUDIT-001 R2)"]
|
||||
direction LR
|
||||
N1[大模型登记<br/>·数月·] --- N2[算法备案<br/>·数月·] --- N3[分账/灵工选型<br/>·二清风险·]
|
||||
end
|
||||
subgraph 加1["+1:律所咨询(解锁钥匙)"]
|
||||
Q[律所合规咨询<br/>·两核心问·]
|
||||
end
|
||||
Q -.->|"定性意见解锁"| N3
|
||||
Q -.->|"路径/触发时点"| N1
|
||||
Q -.->|"路径/触发时点"| N2
|
||||
Q -.->|"内测豁免边界"| 法定8
|
||||
```
|
||||
|
||||
> **谁能压、谁压不动**:这组闸门的一个关键事实是——**AI 一秒也压不动它们**。材料准备(签名报备文案、备案说明、隐私协议占位稿)可以由 AI 代劳,但**提交、实名、付费这三类动作只有创始人本人能做**。这条约束是日历临界路径的本质,详见 [A1 闸门看板](../../mvp/A1闸门看板.md) 顶部的说明。
|
||||
|
||||
---
|
||||
|
||||
## 4. 闸门的依赖与临界路径(按什么顺序拆)
|
||||
|
||||
12+1 道闸门不是一字排开各自独立,而是有明确的前置依赖,形成一条**日历临界路径**(由日历时间而非工作量决定的最长依赖链——审批周期固定,人再多也压不短)。理解依赖关系,才能知道"先动哪几件能解锁最多后续"。
|
||||
|
||||
整条链的根前置是**经营主体**(开展经营所需的公司实体)。这一项是 0 号闸门,已于 2026-06-10 经创始人确认**已具备**(绘境AI 已有可用公司主体),因此全链可立即并行启动,**没有多米诺式的等待**。根前置解除后,依赖关系如下:
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
M["#0 经营主体<br/>✅ 已具备(06-10)"] --> DOM["#1 域名+实名"]
|
||||
DOM --> ICP["#2 ICP 备案<br/>7-20 工作日"]
|
||||
M --> SMS["#3 短信签名报备<br/>1-2 周不可压缩"]
|
||||
M --> LLM["#4 LLM 实名充值"]
|
||||
M --> LAW["#11 律所咨询"]
|
||||
ICP --> PAY["#5 支付商户进件<br/>1-3 周"]
|
||||
ICP --> AD["#6 广告联盟资质<br/>1-3 周"]
|
||||
LAW --> SPLIT["#12 分账/灵工选型"]
|
||||
LAW --> REG["#9 大模型登记 / #10 算法备案<br/>数月"]
|
||||
classDef week fill:#ffe0e0,stroke:#c00;
|
||||
class DOM,ICP,SMS,LAW week;
|
||||
```
|
||||
|
||||
把这条图翻译成执行语言,就是 [A1 闸门看板](../../mvp/A1闸门看板.md) 反复强调的那句话——**本周必须动的四件**:
|
||||
|
||||
1. **域名购置 + ICP 备案提交**(#1 → #2)。ICP 备案(网站开展经营前向工信部门做的互联网内容提供商备案)周期 **7–20 工作日**,是链上第一个长周期项;对外口径已承诺"8 月中旬前 ICP 完成 + 开放邀请制内测",本周不提交就吃掉全部缓冲。
|
||||
2. **短信签名报备提交**(#3)。周期 **1–2 周且不可压缩**——这一项直接关系玩家侧能不能用短信验证码登录(见 §6)。
|
||||
3. **律所约谈**(#11)。它是解锁分账选型、大模型登记 / 算法备案路径的钥匙。
|
||||
4. **LLM 实名口径确认**(#4)。确认生成所依赖的大模型网关上游各厂商 API key 的实名 / 充值主体口径与发票路径。
|
||||
|
||||
短信签名报备(#3)还隐含一个串联约束:**部分短信供应商要求域名已备案**,因此要么选不强制要求备案域名的供应商先行、要么等 #2 完成,二者择一。其余依赖一目了然:ICP 回执到位才能做支付进件(#5)与广告资质(#6);律所意见到位才能定分账选型(#12)、确认两项备案(#9/#10)的属地路径与触发时点。
|
||||
|
||||
---
|
||||
|
||||
## 5. 律所合规咨询:解锁全局的两核心问
|
||||
|
||||
整组闸门里最该尽早做的,是那次**律所咨询**——因为它的两个核心结论会反向决定其余多道闸门的走向。绘境AI 已备好一份可直接转发给律所的会前 brief([`律所合规咨询brief.md`](../../mvp/律所合规咨询brief.md)),期望律所对每个问题给出"**定性结论 + 风险等级 + 可行通道 + 触发红线的边界条件**"四件套。
|
||||
|
||||
**核心两问(必答):**
|
||||
|
||||
- **问 1 · 无版号 UGC 游戏在自有 H5 平台分发的出版定性。** "邀请制内测、不接真钱、不公开经营"这种运营形态,是否构成《网络出版服务管理规定》意义上的网络出版 / 游戏运营?"试玩 demo / 内测"定性的成立边界究竟在哪(人数规模?链接能否分享?能否有任何收入?)?以及——若未来公开经营,无版号条件下是否存在合规路径?这一问的答案直接决定**自有端是否长期保持"邀请制内测"定性、公开经营推迟到什么条件满足**。
|
||||
- **问 2 · 防沉迷义务的履行路径。** 无版号则接不进官方实名 / 防沉迷系统,自有端(内测形态)的义务如何履行?内测定性下是否有豁免空间或替代性履行(自建时长限制、宵禁等)?以及自营分发(平台作为第一责任人)与渠道分发(渠道代管)的义务边界差异。
|
||||
|
||||
**附加两问(时间允许则答):**
|
||||
|
||||
- **问 3 · 二清定性。** 平台代收广告联盟结算款再分账给个人创作者是否构成二清?三个备选通道——**持牌分账产品 / 银行存管 / 灵活用工平台代发 + 个税代扣**——各自的适用性与成本量级。这一问决定 #12 分账方案选型;**选型前绝不接真钱**。
|
||||
- **问 4 · 生成式 AI 备案义务。** 自建平台调第三方大模型 API 对公众提供生成服务,是否需办大模型登记及算法备案?邀请制内测阶段是否触发?办理路径与周期?以及 AIGC 显式标识义务对平台生成游戏的具体适用方式。这一问决定 #9/#10 两项备案的启动时点与材料准备。
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
Q1["问1 出版定性"] --> R1["自有端是否长期内测<br/>+公开经营推迟条件"]
|
||||
Q2["问2 防沉迷履行"] --> R1
|
||||
Q3["问3 二清定性"] --> R3["#12 分账方案选型<br/>(选型前不接真钱)"]
|
||||
Q4["问4 生成式AI备案"] --> R4["#9/#10 备案启动时点<br/>+材料准备"]
|
||||
```
|
||||
|
||||
> **可提供给律所的材料**:业务描述(brief 本身)、平台 demo 试玩链接(内网,可会上演示)、现有 IP 合作协议文本、内容审核闸门机制说明(可后补)。
|
||||
|
||||
---
|
||||
|
||||
## 6. 鉴权件:短信报备这道闸门如何不卡死漏斗
|
||||
|
||||
合规闸门里有一道和产品体验直接挂钩、值得单独说明——**玩家侧的短信验证码登录**。它的合规前置是 §4 的 #3 短信签名报备,而这道报备**1–2 周不可压缩、且报备完成前验证码不可用**。如果硬等它,玩家注册漏斗在内测期就被卡死了。
|
||||
|
||||
绘境AI 的解法是把"鉴权"与"短信报备"解耦,采用 **C 方案(邀请码旁路)**:内测期玩家用**邀请码**注册,不依赖短信验证码,因此报备未完成也不影响内测漏斗跑通;待报备完成后再切到验证码全量。这样既守住了合规底线,又不让一道审批闸门拖死产品验证。
|
||||
|
||||
切换到短信验证码时有一条硬规矩(出自鉴权执行规范 HJ-PASSPORT-EXEC-001 §7.2)——**切渠道 = 配置切换 + 两项验证绑定,缺一不得只切配置**:
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant U as 玩家
|
||||
participant P as 绘境AI 平台
|
||||
participant S as 短信供应商
|
||||
Note over P,S: 内测期:报备未完成
|
||||
U->>P: 用邀请码注册(C 方案旁路)
|
||||
P-->>U: 注册成功(不依赖验证码)
|
||||
Note over P,S: 报备完成后才允许全量切换
|
||||
rect rgb(255,235,235)
|
||||
Note over P: 切换前置(两项验证·缺一不可)
|
||||
P->>P: ① 确认 huijing.captcha.enable 对发码生效
|
||||
P->>P: ② 发码 IP 日/时上限已部署(Redis 计数)
|
||||
end
|
||||
U->>P: 输入手机号
|
||||
P->>S: 请求下发验证码
|
||||
S-->>U: 短信验证码
|
||||
U->>P: 提交验证码
|
||||
P-->>U: 登录成功
|
||||
```
|
||||
|
||||
这两项绑定的意义在于:配置开关(`huijing.captcha.enable`,控制验证码功能是否启用的配置项)真正对发码链路生效是一回事,而**发码频率必须有上限**(按 IP 的日 / 时配额,用 Redis 计数实现,阈值与运营确认)是另一回事——少了后者,验证码接口会被刷爆、产生短信费用黑洞。所以规范要求两项一起绑定,不允许"只切配置就上线"。
|
||||
|
||||
---
|
||||
|
||||
## 7. 经济模型对合规的一处硬约束:分账不能为负
|
||||
|
||||
合规与经济模型在一个点上交汇——**资金分账方案**(#12)。这里只讲与合规相关的那条不变量,完整的敏感性测算在[单位经济敏感性模型](../../mvp/单位经济敏感性模型.md)里。
|
||||
|
||||
绘境AI 的三层分成叠加规则(创始人 2026-06-10 拍板、对应审计 R3 收口)采用**净额基数**口径,确保账永不为负:渠道先扣 40%,平台拿到的是**实收净额 N**;创作者分 **80%**;带 IP(知识产权,如授权形象)的游戏,IP 方再从**创作者份额内**支出 15–25%,于是创作者实得 55–65%;而**平台恒留 0.2N**。这样一来,对内的"创作者 80%"与对外的"创作者实得 55–65%"是**同一个模型的两种表述**,不是两套数字,账面永远不会出现负值——这正是规避资金纠纷与二清风险的会计前提。
|
||||
|
||||
> **诚信红线**:对创作者讲"能赚钱"时,必须用"头部款"口径,**不得做普遍性的收入承诺**。这条红线既是商业伦理,也是合规防线(避免被认定为虚假宣传)。
|
||||
|
||||
---
|
||||
|
||||
## 8. 跟踪与责任:状态去看板,逻辑看本页
|
||||
|
||||
最后明确本页与跟踪面的分工,避免出现"两份都在记状态、互相打架"的影子事实源:
|
||||
|
||||
| 你要找的东西 | 去哪里 |
|
||||
|---|---|
|
||||
| 每道闸门的**实时状态 / 负责人 / 最晚提交日** | [`A1 闸门看板`](../../mvp/A1闸门看板.md)(**唯一跟踪面**,状态变化直接改它) |
|
||||
| 闸门**为什么存在、怎么缠成死锁、按什么逻辑拆**(本页) | 本文档(运营域 · 合规闸门) |
|
||||
| 律所会前材料与四问全文 | [`律所合规咨询brief.md`](../../mvp/律所合规咨询brief.md) |
|
||||
| 分账 / eCPM 等**经济模型明细与测算** | [单位经济敏感性模型](../../mvp/单位经济敏感性模型.md) |
|
||||
| **战略叙事**(三线变现排序、护城河、对外口径) | [产品域 · 商业定位](../产品/商业定位.md) |
|
||||
| 决策史与逐档证据 | git 历史 + [`战略与合规`](../../agent-specs/战略与合规.md)(canonical 活档)+ `_archive/` 审计报告 |
|
||||
|
||||
> **纪律**:本页只写"合规闸门的设计逻辑与不变量"。任何会随日历推进而变化的状态——某项是否已提交、卡在哪一步、谁负责——一律只记在 [A1 闸门看板](../../mvp/A1闸门看板.md);本页不得复制这些状态,否则就会出现两份冲突的真相。仍然阻塞全局的最大未决项是:**Doc A(产品需求清单)里的法定 8 项换血,待律所对两核心问给出书面意见后才能定稿。**
|
||||
246
docs/architecture/运营/渠道发行.md
Normal file
246
docs/architecture/运营/渠道发行.md
Normal file
@ -0,0 +1,246 @@
|
||||
# 渠道发行 · 运营设计文档
|
||||
|
||||
> **这是什么**:绘境AI 的「渠道发行」设计文档,回答"**平台上做出来的游戏,怎么走到微信/抖音这类外部渠道、又要过哪些合规关卡**"。它是渠道发行这个子系统的单一事实源(single source of truth,简称 SoT,意为"想了解这件事看这一份就够,不必再翻别处")。
|
||||
> **给谁看**:运营负责人、合规与法务、负责渠道对接的工程师、做投资尽调时关心"能不能上架、风险在哪"的人。
|
||||
> **怎么读**:先读第 1–2 节建立"为什么这么分工、卡在哪些合规关"的全局认知,再按需深入第 3 节往后的架构与引擎选型细节。内部代号(如 L1/L2、一壳多游、备案锁等)都会在首次出现时用一句话解释。
|
||||
> **本档定位**:对应内部编号 **HJ-CH-001**(渠道发行子系统的判定文档编号)。它讲清楚四件事——**一壳多游(L1)的发行模式**、**为什么 L2 不上渠道**、**有哪些合规闸门**、**两个候选游戏引擎怎么竞标定胜负**。落地的实现手册在 `.agents/skills/runtime-and-multichannel.md`,完整决策史在 git 历史里。
|
||||
|
||||
---
|
||||
|
||||
## 1. 一句话与一张图:渠道发行是什么
|
||||
|
||||
绘境AI 平台上,创作者用一句话就能生成一款轻量小游戏。这些游戏要被玩家玩到,有两条路:一条是平台**自有的 H5 流**(H5 = 直接在手机浏览器里打开的网页游戏,无需下载安装),另一条是把游戏推到**微信小游戏、抖音小游戏**这类外部渠道。这份文档讲的就是第二条路——**渠道发行**。
|
||||
|
||||
这里有一个绕不开的技术现实,它决定了整个分工:**微信和抖音的小游戏平台,对"远程下发的代码"有严格管控。** 它们会自动剥离掉远程包里的代码,并且明文禁止在小游戏里塞 JavaScript 解释器(比如 `eval5` 这类能在运行时执行任意脚本的库)。而浏览器没有这种禁令——网页天然就能跑动态生成的代码。
|
||||
|
||||
这一条差异,把两条发行路线的角色彻底分开了:
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
GEN["创作者一句话<br/>AI 生成游戏"] --> SPLIT{"走哪条<br/>发行路线?"}
|
||||
SPLIT -->|"海量 UGC · 即时发布<br/>(主线)"| H5["自有 H5 流<br/>浏览器无代码禁令<br/>任意动态生成都能跑"]
|
||||
SPLIT -->|"精选款 · 固化进包<br/>(渠道线)"| CH["微信 / 抖音小游戏<br/>一壳多游 · L1 整包过审"]
|
||||
H5 --> PLAY1["玩家在游戏信息流里<br/>即刷即玩"]
|
||||
CH --> PLAY2["玩家在微信/抖音<br/>原生入口里玩"]
|
||||
```
|
||||
|
||||
几个内部术语,在这里一次说清:
|
||||
|
||||
- **UGC**(User Generated Content,用户生成内容):指普通创作者源源不断造出来的游戏。绘境AI 的核心卖点就是让零基础用户海量造游戏,所以"海量 UGC"是平台的主战场。
|
||||
- **一壳多游**:渠道线的核心打法。在一个微信/抖音 appid(应用唯一标识)的"壳工程"里,放固定不变的模板代码,然后用远程下发的**纯数据配置**去驱动出不同的游戏——一个壳,装多款游戏。
|
||||
- **L1 / L2**:游戏的两个"层级"。**L1** 指用平台预置模板、只调数据参数就能产出的精选游戏(代码固定、只换数据);**L2** 指允许改动游戏逻辑代码的进阶玩法。下文会反复用到这组概念。
|
||||
|
||||
**一句话总结分工**:渠道线只做 **L1 精选整包过审**,对外口径是"主题小游戏合集",而不是"开放的 UGC 平台";自有 H5 流才是 UGC 海量生成、以及 L2 改码玩法的主场。两条线互不上跳、不外导(即微信渠道内的游戏之间不互相跳转、也不把用户往外部网页导流),以符合渠道对"内容边界清晰"的要求。
|
||||
|
||||
对外统一措辞,定为一句话:**「自研流即时发布 + 双渠道精选发行」**。
|
||||
|
||||
---
|
||||
|
||||
## 2. 为什么这么分工:三条根因
|
||||
|
||||
把"海量 UGC 留在 H5、只让精选款上渠道"这件事拆开,背后是三条扎实的理由。
|
||||
|
||||
**根因一:技术管控。** 如前所述,微信/抖音对远程包会自动剥离代码、禁止 JS 解释器,所以渠道里只能跑"包内固定的模板代码 + 远程下发的纯数据配置"。浏览器没这个限制,自研 H5 因此成为动态生成代码的唯一容身之处。这不是产品选择,是平台规则倒逼出来的架构。
|
||||
|
||||
**根因二:审核颗粒度。** 渠道的内容审核是针对"每一款具体游戏的内容"做的,而不是针对一个 appid 一次性放行。这意味着,如果想让成千上万款 UGC 游戏各自挤上渠道,审核根本不可行——每款都要单独过审。所以海量长尾只能留在 H5,渠道只接得住"精选"。
|
||||
|
||||
**根因三:发布节奏。** 每款游戏在绘境AI 里都是一个 agent(AI 智能体)独立生成的代码。常规的新游戏,跟着渠道"壳版本"的发布节奏整批进渠道,这与"精选整包发行"的对外口径是自洽的——不是"每出一款就单独提审",而是"攒一批、随壳版本一起提审"。
|
||||
|
||||
由此得出 L2/L3 这些进阶层级的渠道命运:**默认主线永远是自研 H5 流**(它的"即时生成—发布"闭环是完整的);上渠道只有两种特例——要么把某款精选游戏**固化进包版本去提审**,要么给某款重点游戏(B 端定制、IP 旗舰、已验证的爆款)单独配**一游一 appid**。
|
||||
|
||||
---
|
||||
|
||||
## 3. 合规闸门:能不能上架,卡在这几道关
|
||||
|
||||
渠道发行最大的不确定性不在技术,而在合规。下面把平台规则核查后的活结论逐条列出——每一条都是"上线前必须确认"的硬约束。(每条结论的置信度评级与原始政策出处 URL 已归档在 git 历史与第 8 节指针里,此处只留落地结论。)
|
||||
|
||||
### 3.1 版号与 IAA 通道
|
||||
|
||||
**版号**指游戏正式商用前需要的出版审批号。这里有个关键通道:**纯 IAA 游戏可以免版号上架。** IAA(In-App Advertising,应用内广告)指游戏只靠广告变现、不卖任何内购道具。纯 IAA 游戏走的是"软件著作权登记(软著)/ 电子版权认证 + 自审自查 + ICP 备案(网站经营性备案)"这条路上架,资质审核大约 1–3 个自然日;个人主体也可以上架并开通流量主(即接入广告分成)。版号只对**有内购**的游戏强制。
|
||||
|
||||
但有一个真实存在的风险:**审核员有裁量权,可能要求纯广告游戏补版号材料。** 已有真实判例是纯广告游戏被要求补交材料,而且 2025–2026 年是政策敏感区,落地前必须复核。(此前流传的"4–6 月集中清退无版号存量"已确认是谣言,警报解除。)
|
||||
|
||||
### 3.2 备案锁——比版号更紧的决定性闸门
|
||||
|
||||
这是整套合规里最关键、最容易踩的一道关,务必看清:
|
||||
|
||||
官方规则写明——**备案完成后,不支持再改游戏内容、icon、代码;任何实质性变更,都必须重新备案。** 我们内部把这条规则叫**「备案锁」**(意为:一旦备案,游戏内容就被"锁死"了)。
|
||||
|
||||
这条规则直接把"一壳多游"从"灰区可行"**降级为"高风险待裁"**——因为"壳合规"不等于"壳里每款游戏的内容都合规",而备案锁恰恰锁的是内容。面对它有三条出路:
|
||||
|
||||
1. **每款精选游戏各自独立备案**(最稳,但成本最高);
|
||||
2. **把壳内的游戏集合稳定下来**,任何变更都统一走"重新备案 + 整包提审",节奏放到**月级**;
|
||||
3. **并入法务/律所议题**,由律所给出权威裁决(挂在内部 W3 工作流议题下)。
|
||||
|
||||
一个必须收回的旧叙事:**「当日热发新游进渠道」这个说法被收回了。** 在渠道线里,所谓"热发"只能是对既有游戏做非实质性的参数微调(具体边界等律所结论)。注意——**这条只约束渠道线,完全不影响自有 H5 流**(H5 没有备案锁)。
|
||||
|
||||
### 3.3 其余四条合规结论
|
||||
|
||||
- **公测=运营(目前仍是草案)**:有一份《网络游戏管理办法(草案)》第 21 条,把"公测"定性为"运营行为"。但截至 2026 年 6 月它**仍是草案、未生效**。对应到我们的灰度测试,合规出口是:**测试人数 ≤ 2 万人 + 不接广告 + 报备并删档**——这条写进发布流程红线。也就是说,种子创作者做外部测试时,只要挂了广告就构成"运营",必须先备案。
|
||||
- **抖音口径**:抖音执行**「一软著一游戏」**——每款游戏对应一份软著,且备案是上架前置(平台代为申报,不是"先上线后备案")。两个平台口径一致,未见 2026 年有额外收紧。
|
||||
- **题材黑名单**:棋牌、捕鱼这类题材,即便是纯 IAA 也需要律所出具季度更新的合规报告。因此把它们**列入生成侧的题材审查**——在游戏生成之前就拦掉。
|
||||
- **运营事实(保留一行)**:流量主开通门槛是日活跃用户(UV)≥ 1000;IAA 广告分成比例,微信约 50%~90%、抖音约 70%~90%(分别来自微信流量主、以及字节的穿山甲/巨量平台)。
|
||||
|
||||
---
|
||||
|
||||
## 4. 渠道发行流程:从生成到上架
|
||||
|
||||
把上面的分工与合规串起来,一款游戏走渠道线的完整流程如下。这张图也回答了"为什么渠道发布是月级节奏、而不是即时"。
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A["创作者生成 / 平台精选<br/>一款 L1 游戏"] --> B{"题材审查<br/>(生成前闸门)"}
|
||||
B -->|"棋牌/捕鱼等黑名单"| BX["拦截 · 转律所合规报告"]
|
||||
B -->|"通过"| C["产出 GameConfig 纯数据<br/>(零构建 · Linux 工厂全自动)"]
|
||||
C --> D{"GameConfig 合规红线校验<br/>schema + hash + 静态扫描"}
|
||||
D -->|"含可执行串/远程代码<br/>命中违规"| DX["打回 · 修正配置"]
|
||||
D -->|"纯数据 · 全枚举白名单"| E["攒入精选游戏集<br/>随壳版本节奏"]
|
||||
E --> F["软著登记 + 备案<br/>(备案锁:此后内容不可改)"]
|
||||
F --> G["整包提审<br/>miniprogram-ci 无人化上传"]
|
||||
G --> H{"平台审核<br/>(内容颗粒度)"}
|
||||
H -->|"驳回"| HX["归因分类<br/>engine/package/compliance"]
|
||||
H -->|"通过"| I["上架微信 / 抖音<br/>玩家原生入口即玩"]
|
||||
I --> J["广告变现<br/>showRewarded → 平台原生广告"]
|
||||
|
||||
style F fill:#ffe6e6
|
||||
style D fill:#fff2cc
|
||||
```
|
||||
|
||||
图里两道带色的关卡是渠道线的"生死线":黄色的 **GameConfig 合规红线**(第 5 节详述)决定下发的数据是否纯净,粉色的 **备案锁** 决定上架后还能不能改。正是备案锁的存在,让"攒一批 → 统一备案 → 整包提审"成为月级节奏,而非即时热发。
|
||||
|
||||
---
|
||||
|
||||
## 5. 现行架构:Channel Runner v1 与 GameConfig 合规红线
|
||||
|
||||
渠道线在技术上**不复用平台自有 H5 流的 Runner v2**(Runner 指"游戏运行时容器",负责把游戏跑起来),而是新增一套专门的双形态架构。需要说明:信息流(feed)那一侧用 LittleJS 引擎的决定是终裁、不动的,这里只裁渠道线这一面。
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
subgraph PKG["壳工程随包提审 · 微信/抖音各一份"]
|
||||
A["渠道 SDK / 广告 / 遥测<br/>+ canvas-input-audio-storage<br/>四件 adapter"] --> B["模板注册表<br/>templateId 仅命中包内白名单"]
|
||||
B --> C["L1 模板代码 + 核心层 + SDK<br/>(全部随包,固定不变)"]
|
||||
end
|
||||
D[("远程 GameConfig<br/>gameId / versionId / configHash")] -->|"schema + hash 校验后实例化"| C
|
||||
C -->|"SDK postMessage 同上下文直连<br/>ad.showRewarded 直调 wx/tt API"| E["平台 wx / tt 运行时"]
|
||||
F["安全边界:静态代码扫描<br/>+ schema 红线 + 域名白名单<br/>+ 包 hash + 真机取证"] -.->|"替代浏览器的 iframe+CSP 隔离"| C
|
||||
```
|
||||
|
||||
读图要点:**壳工程**(随包提审的那部分)里,模板代码、核心层、SDK 全都打进包里、固定不变;真正区分出不同游戏的,是那份**远程 GameConfig**——一份纯数据配置,带 `gameId`、`versionId` 和 `configHash`(配置内容的哈希指纹,用于防篡改)。配置经过 schema 与 hash 双重校验后,才被实例化成具体游戏。由于渠道里不能用浏览器的 iframe 沙箱隔离,安全边界改由"静态扫描 + schema 红线 + 域名白名单 + 包 hash + 真机取证"这一组手段来守。
|
||||
|
||||
### 5.1 GameConfig 合规红线(渠道生死线)
|
||||
|
||||
这是渠道线最不能破的一条线,转正后会成为契约库里的 **9c 契约**(契约 = 跨端共享、不可随意改动的数据格式约定;9c 是其中第 9 类的 c 子项,专管渠道版 GameConfig)。它的硬性规定:
|
||||
|
||||
- **禁一切可执行/可解释的字符串**——比如 `condition: "score>10"` 这种"把逻辑写成字符串、运行时再解释执行"的写法,一律禁止;
|
||||
- **禁远程下发** entry / templateUrl / WASM / JS / HTML / 脚本化 SVG 等任何形式的代码;
|
||||
- **剧情和关卡逻辑必须写成有限状态机的枚举**(用 `conditionType + params`、`actionType + params` 这种"类型 + 参数"的固定结构表达),禁止使用任何通用的 DSL(领域特定语言,这里指能自由编程的脚本);
|
||||
- `templateId`、`actionType`、`assetId`、`adSlotId` 等关键字段,全部必须是**枚举白名单**里的值;
|
||||
- schema 设 `additionalProperties: false`(不允许出现未声明的字段),并对数值范围、字符串长度、文件 MIME 类型、大小全部加限制;
|
||||
- 远程素材只允许图片/音频/图集(atlas)/json,且必须带 hash、且只能来自白名单域名。
|
||||
|
||||
一句话:**渠道里的"游戏逻辑"必须是数据,不能是代码。** 这是整条渠道线能成立的根基。
|
||||
|
||||
### 5.2 广告与遥测
|
||||
|
||||
广告这条线,接口是 `showRewarded(slotId)`(展示一支激励视频广告,`slotId` 是广告位逻辑标识),它会映射到平台原生的 `wx/tt.createRewardedVideoAd`,把逻辑广告位 `slotId` 翻译成平台的 `adUnitId`;此外增补 `showBanner / hideBanner` 管理横幅广告。这里有一条不容造假的红线:**`rewarded=true`(用户完整看完广告应得奖励)只认平台返回的"完整观看"回调**;如果走兜底发奖,必须标记 `reward_fallback=true`,绝不能把兜底奖励伪装成真实广告收益。
|
||||
|
||||
遥测(telemetry,即埋点数据采集)方面,在原有的 **9g 契约**(第 9 类的 g 子项,专管遥测字段)基础上补齐渠道专属字段:`channel`、`channelAppId`、`adUnitId`、`logicalSlotId`、`packageVersion`、`configHash`、`requestId`、`ad_event_type`、`platform_callback`、`settlement_batch`。性能监测的"七锚点"在渠道版里展开为一串时序埋点:`t_launch → t_runner_boot → t_canvas_ready → t_sdk_ready → t_config_loaded → t_first_paint → t_input_bound → t_game_start`,用来精确度量从启动到游戏可玩的每一段耗时。
|
||||
|
||||
---
|
||||
|
||||
## 6. 生产管线:Win/Mac 依赖只在一个环节
|
||||
|
||||
有人会问:Cocos(一款游戏引擎,见下节)不能在 Linux 服务器上无人化构建,那渠道游戏的生产岂不是处处要人工?答案是——**对 Win/Mac 桌面环境的依赖只落在三层里的第②层,而且仅当 Cocos 这个候选最终胜出时才需要。** 把生产管线拆成三层就清楚了:
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
L1["① 每游戏生产(高频 · 主路径)<br/>GameConfig + 资产<br/>Linux 工厂全自动 · 零构建 · 不碰引擎"]
|
||||
L2["② Runner 壳版本发布(周/月 · 低频)<br/>C-A=LittleJS:esbuild 纯 Node 全 Linux ✓<br/>C-B=Cocos:须 Win/Mac 构建机"]
|
||||
L3["③ 上传提审(平台强制 · 低频)<br/>微信 miniprogram-ci 纯 Node 可 Linux<br/>抖音 tt-ide-cli 同类(待核实)"]
|
||||
L1 --> L2 --> L3
|
||||
NOTE["绕不开人工的只有<br/>平台审核节奏本身 · 与引擎无关"]
|
||||
L3 -.-> NOTE
|
||||
style L2 fill:#fff2cc
|
||||
```
|
||||
|
||||
- **第①层是高频主路径**:每款新游戏的生产,本质是产出 GameConfig 和资产,由 Linux 工厂全自动完成、热下发到线上,完全不碰引擎构建。这正是"禁动态代码"反过来带来的架构红利——逻辑都是数据,生产就能零构建。
|
||||
- **第②层是低频的壳版本发布**:只有壳工程升级时才发生(周/月级)。这一层才区分两个引擎候选:LittleJS 用 esbuild 纯 Node 即可在 Linux 全自动构建(已实证);Cocos 则需要 Win/Mac 构建机(Mac mini 或云端 runner,是真实运维成本,但不阻断流程)。
|
||||
- **第③层是平台强制的上传提审**:微信官方提供 `miniprogram-ci`(纯 Node 工具,可在 Linux 无人化上传和预览);抖音的 `tt-ide-cli` 是同类工具(待 P1 阶段核实)。
|
||||
|
||||
结论:真正绕不开人工的,只有平台审核节奏本身,这跟用哪个引擎无关。下一节提到的"三件套"(微信 AppID + Win/Mac + 安卓真机)是 **P1 人工取证阶段**的需求,不是生产常态。
|
||||
|
||||
---
|
||||
|
||||
## 7. 渠道引擎竞标:LittleJS 对决 Cocos
|
||||
|
||||
渠道线该用哪个游戏引擎来打包,没有在"层"这一级锁死,而是在"场景"级锁定默认 LittleJS、并保留创始人复议权(2026-06-12 拍板)。这件事用一场**竞标**来定胜负,内部代号 **W-CH-α**(渠道引擎对比 spike,spike 指"为验证某决策而做的小型探索性实验")。
|
||||
|
||||
先解释两个候选:
|
||||
|
||||
- **C-A = LittleJS + 自研 adapter**:LittleJS 是一款极轻量的 2D 游戏引擎(压缩后约 **55KB**),配上我们自研的微信(weapp)/抖音(tt)适配层(adapter,负责把引擎对接到各平台 API)。
|
||||
- **C-B = Cocos 原生导出**:Cocos Creator 是一款成熟的商业游戏引擎(3.8.x 版本),用它的官方"原生导出"功能直接产出小游戏包。
|
||||
|
||||
竞标在 **统一场景** 下进行,以保证公平:同一套 L1 模板(主测 tycoon「模拟经营」玩法 + 用 clicker「点击」玩法测量边际成本)、**字节级完全相同的 GameConfig**(公平性红线——禁止为某个候选单独调参),微信/抖音双端各出一个包。其中 clicker 只进"体积"与"每加一个模板的边际成本"两项计分,不进真机门(出于时间盒约束)。
|
||||
|
||||
| | C-A | C-B |
|
||||
|---|---|---|
|
||||
| 引擎 | LittleJS 1.18.x(锁定 T1 终裁版本)+ 自研 weapp/tt adapter | Cocos Creator 3.8.x(精确小版本写入报告)原生导出 |
|
||||
| 入包内容 | 壳 + adapter + engine(经 tree-shaking 摇树裁剪)+ 核心层 + 双模板 + 基础素材 | 壳(官方模板冻结基线)+ core + 同样的双模板 + 基础素材 |
|
||||
| 出包 | 微信 + 抖音各一包 | 同左 |
|
||||
|
||||
### 7.1 决策规则与翻盘条件
|
||||
|
||||
**怎么判胜负**:如果 LittleJS 的 adapter 能过全部的门、且后续维护面可控,那就**优先选单代码库方案**(C-A)——好处是"模板工艺只做一次";反过来,如果 adapter 太脆弱、或在体积/性能上出线,那就**让 Cocos 接渠道线**(C-B,复用已有的引擎投资)。无论谁赢,GameConfig 都做到引擎无关,保证 Linux 工厂不会因引擎而分叉。
|
||||
|
||||
**翻盘条件**:被判输的一方,如果在真机或审核环节出现了对方没有的"结构性阻断"(比如某平台根本跑不通),可以持证据复议。
|
||||
|
||||
**计分维度(G1–G6 等)**:通过的门数量 / 主包余量 / 真机首屏 ≤ 2s / 双实现成本三列(首个模板迁移的一次性成本 · 后续每加一个模板的边际成本〔用 clicker 增量实测〕· 平台 API 漂移的维护归属) / agent 友好度(纯代码 vs 编辑器+MCP) / 构建管线能否服务器化。为防止"单代码库"被一次性成本系统性抬分,**敏感性分析必须包含两个变体**:① 把裁量分拉平后再比一次;② 剔除首模板一次性迁移成本后再比一次。
|
||||
|
||||
### 7.2 公平性生命线
|
||||
|
||||
竞标的公平性被置于一切产出之上,具体手段:所有候选共用一份**只读的 `channel-probe`**(探针脚本,各 lane〔即各候选的独立工作目录〕禁止改动,由主会话做 diff 校验)+ 八锚点的字段级定义 + JSONL 哈希链作为主证据(每条记录带 seq 序号、单调时钟、前一条的哈希、device、configHash,形成防篡改链)+ **真机测试全程禁止状态注入**(禁用一切 evaluate/调试器手段改游戏状态)。一条铁律:**主证据必须是机器可校验的文件,录屏和截图一律降为辅证。**
|
||||
|
||||
### 7.3 LayaAir 为什么出局(写死,复评前不重问)
|
||||
|
||||
竞标本来有第三个候选 LayaAir(另一款国产 2D/3D 引擎),但已尽调出局,两条硬理由:
|
||||
|
||||
1. **没有面向 agent 的 MCP 工具面**——它宣传的"AI 引擎"其实是 IDE 内嵌的人工 AIGC,官方把"AI 代码生成"标为"规划中";反观 Cocos 的 cocos-mcp(让 agent 直接驱动引擎的工具集)已有 158 个工具、相当成熟。绘境AI 是 AI 驱动开发,引擎能不能被 agent 直接操控是硬指标。
|
||||
2. **体积比 LittleJS 重一到两个数量级**——LittleJS 压缩后约 55KB,LayaAir 远超于此。
|
||||
|
||||
**复评触发条件**(满足才重新评估 LayaAir):它出现官方或成熟社区的 MCP server,或官方公布"2D-only 小游戏核心体积"数字且远小于 Cocos。
|
||||
|
||||
---
|
||||
|
||||
## 8. 当前进展与关键指针
|
||||
|
||||
### 8.1 已完成 / 在飞 / 待办
|
||||
|
||||
**已完成(DONE)**:HJ-CH-001 判定定稿,加三项创始人拍板——
|
||||
|
||||
- **C1**:分工口径确定为「自研流即时发布 + 双渠道精选发行」;
|
||||
- **C2**:一壳多游**以渠道合规为准绳**(默认走月级"重新备案 + 整包提审";若律所判定必须每游独立备案则照办——**合规优先于经济性**);
|
||||
- **C3**:W-CH-α 升级为渠道引擎对比竞标制。
|
||||
|
||||
W-CH-α 的 **P0 先行段五件产物已交付**(adapter v0 + 体积清单 SIZES + 合规 schema + 扫描器 30 个测试用例〔15 合规 / 15 违规〕+ Channel Runner 壳骨架 + channel-probe + 操作手册 RUNBOOK),验证门 **6/6 全部 PASS**(提交 bb12d30c)。
|
||||
|
||||
**在飞(暂停中)**:W-CH-α 的 **P1 真机测试段七道门**——
|
||||
|
||||
- **G1 装载**:主包 ≤ 4MB;
|
||||
- **G2 模拟器全循环**:在平台模拟器里跑通完整游戏循环;
|
||||
- **G2b 真机真输入**:双端 × 双候选,各 3 次真实触摸,完成 tycoon 闭环(含终局);
|
||||
- **G3 真机首屏**:三开协议,判定值 = 杀进程冷启动,从 `t_launch` 到 `t_game_start` ≤ **2s**;
|
||||
- **G4 广告五路径**:success / close-before-complete(没看完就关) / load-fail / timeout / no-fill 五种情况都要正确处理;
|
||||
- **G5 审核门**:预检计分 + 真实提审观察,驳回归因分类(engine/package/compliance 计入,资质/类目/IP 类驳回入残差);
|
||||
- **G6 网络门**:只允许访问白名单域名。
|
||||
|
||||
P1 的硬前置是**创始人提供三件套**:微信 AppID + Win/Mac 构建机 + 安卓千元机(及测试号)。
|
||||
|
||||
**待办**:W-CH-β 渠道线正式建设(两个壳工程 → 分发中台〔按"一壳多游·主题合集"口径〕→ 广告 adapter + 9g 字段 → 渠道评估门),等竞标过门后开工。还有几项**非工程前置**(需法务/律所):一壳多游的类目报备 / 软著申报节奏 / H5 站资质的律所结论(挂 W3) / IAA 政策敏感区上线前复核。
|
||||
|
||||
### 8.2 关键指针(契约 / 代码 / skill / 上游)
|
||||
|
||||
- **代码资产(请勿误清理)**:`docs/agent-specs/2026-06-12-channel-spike/` 下三个 lane——`lane-littlejs-adapter/`(含 src/test 与 REPORT/SIZES/RUNBOOK)、`lane-cocos/`、`shared/`(channel-probe.js / runner-shell.js / game-config / schema,**只读区**,各 lane 不得改动)。P0 产物与 P1 执行册作为竞标资产保留。
|
||||
- **契约**:GameConfig 渠道版 schema 转正后 → 契约库 `contracts/game-package.schema.json` 的 **9c**;遥测渠道字段 → **9g**。
|
||||
- **skill 手册**:`.agents/skills/runtime-and-multichannel.md` 第 6 节"多渠道导出"。
|
||||
- **上游回写**:HJ-GEN-001(生成主线判定文档)的 D13/§3/§7,以及 `.agents/knowledge/tech-decisions.md` §1.1(导出枢纽 = 微信小游戏格式包;快手无专用接口,走"微信格式兼容转换";LayaAir 清单里没有快手)。
|
||||
- **终裁物**:渠道引擎计分终裁包(格式同 T1 引擎终裁:计分表 + 裁量分声明 + 敏感性分析 + 翻盘条件),P1 跑完后产出,经 Codex 评审、创始人终裁,再回写 HJ-CH-001 / HJ-GEN-001,最后过 wave-close 七步收口。原始政策出处 URL 索引,保留在判定文档原档的 git 历史里。
|
||||
Loading…
x
Reference in New Issue
Block a user