lili c6b9569033 docs(architecture): 修复审查发现的过时/错误内容(57 项 · 18 档)
25 档 opus 审查(对照代码/契约/git)→ 9C+23M+26m 真过时/错误 → 18 档 opus 修复(各自先按 commit/file:line 复核再改,1 项审查误判经复核拒绝)。主要簇:① gameDefinition 终态对齐——固定架构/SAA编排把'真结构化已成立'降级为'通往 src/ 的中间脚手架 + behavior.code 内嵌串=已知债',跨档对齐 README §3.2/设计裁决(06-20 创始人定调);② WG1 门面缺口按 06-14 scale-20 实测修订(预判几乎全推翻);③ 开闸接线改完成时(generic 桥接 7534bdf9 + 5 品类模板回填 046c061d 已落地);④ Flyway 数字 V17/V18→V25;⑤ 会员订阅(V21 ca53a0db)/community(U3 3f35acb4)/资产渲染(U1 73d33916)状态更新;⑥ prompt治理(classpath 加载)/引擎(Phaser gz)/前端(token 名·prefers-color-scheme)/运维(mini-infra=PostgreSQL·push=阿里云Gitea)细节订正。全 26 档过门(≤2000/有图/品牌绘境AI)。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 20:21:03 -07:00

32 KiB
Raw Blame History

固定游戏架构 + SAA 智能工作室

这是什么绘境AI「一句话造游戏」背后的生成引擎设计。它回答一个核心问题——一个便宜的小模型,怎么可靠地造出一款能上线、能玩的游戏给谁看:生成引擎方向的工程师、架构评审、新加入的同事。 怎么读:先读 §1 抓住"固定脚手架 + 便宜模型填槽"这一个主意,其余各节都是它的展开。 品牌:本文统一用「绘境AI」。


1. 一句话与一个主意:为什么是"固定架构"

让一个便宜的小模型从零发明一款游戏的完整代码结构,是不可靠的——它每次都得重新设计实体、循环、胜负判定,错一处就崩。绘境AI 的破题思路反过来:由一个最强的模型(Opus)一次性把游戏的骨架设计好、固定下来,便宜模型不再发明结构,只在这副固定骨架的"槽位"里填东西——填逻辑、填参数、填关卡、填资产、填胜负规则。

这里有两个名词先解释清楚:

  • Opus 是绘境AI 用来做架构设计与最高复杂度终裁的最强模型;便宜模型(cheap model,文中也叫小模型)指 deepseek-v4-flash、MiniMax-M3 这类单价极低、用来批量干活的模型。
  • 填槽(fill-the-slot):便宜模型不负责"游戏该长什么样"的结构决策,只负责把固定骨架里预留好的空位补全。

这么做的根本理由,是把命门从"便宜模型能不能发明出结构"降级成"便宜模型能不能在一副已知的好骨架里把内容填好"——后者远比前者可信。两个评审都曾把"便宜模型产不出结构化、可维护的源码"列为致命问题(P0,即最高优先级、必须先解决的阻断项);固定架构正是对这个 P0 的回答。

但光有固定骨架还不够,便宜模型需要一个"工作环境"来被榨出最大价值。这就是第二个主意——SAA 智能工作室:

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、多迭代、多重试。

这套设计不是空想——但要把"成立到哪一步"说准。便宜模型在固定结构上填出连贯的游戏定义中间表示(gameDefinition)、再构建成真能过门的游戏,这一段已在代码层得到双重验证(见 §9 实测证据):双证证明的是"便宜模型能可靠产出连贯的 gameDefinition 中间表示",并未、也不能证明"gameDefinition 就是合格的终态产物"。 创始人 2026-06-20 已就产物终态定调:终态产物必须是 src/ 多文件代码工程,gameDefinition 只是"通往 src/ 的中间脚手架";当前实现把玩法逻辑塞进 behavior.code JSON 串、运行时用 new Function 解释执行,属"已知偏离终态的债",缺的正是"gameDefinition → src/"产物侧那一段展开。所以本档凡言"可维护源项目 / 把大模型当工作室",均指方向已经走通、终态尚待兑现,不是已闭合的终局——口径以同目录 README §3.2 终态硬约束设计合理性裁决(Tier0 混合范式 · 名实分层)为准。


2. 固定的部分 vs 可填的部分

整套架构的第一刀,是把"谁也不许动的固定骨架"和"每款游戏都不一样的可填内容"切开。

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):关卡如何构成、胜负如何判定,都是数据驱动。

光有定义还不能跑,需要引擎适配器把定义"翻译"成具体引擎能执行的东西:

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 个契约:

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 产出。设计的终态目标是——逻辑必须以可维护的多文件 src/ 源项目存在,而不是一坨裸 iife(iife = 立即执行函数表达式,这里指那种逻辑只以 JSON 字符串或 new Function blob 形式塞着的黑盒产物)。但这条目标当前并未兑现:gameDefinition 仍把整段玩法逻辑塞进 behavior.code 这个 JSON 字符串字段,运行时用 new Function 直接解释执行(见 game-runtime/src/host/gd-runtime.js:236/251)——这恰恰是设计明令反对的"硬编码 blob",是已知偏离终态的债(创始人 2026-06-20 定调,见 README §3.2)。缺的正是"gameDefinition → src/ 工程"这一段产物侧展开:gameDefinition 只是通往 src/ 的中间脚手架,必须再经一道"编译/展开成 src/ 工程"的构建,而不是停在 JSON 里被 new Function 跑掉。这条债要由一道 doc↔code 兑现门(断言产物存在 src/ 多文件结构、逻辑落在真实源码文件、不存在"逻辑只以 JSON 串形态存在 / 运行时 new Function 跑内嵌串")来守住,属后续 plan,不在本期展开。

契约② 源项目 DB 存储 —— 独立的 game_source_project

源项目落在一张全新的独立表 game_source_project(建表走 Flyway 迁移 V18.0.0,接在已合入的 V17 之后;后续 V20.0.0 追加并发兜底唯一键 uk_game_source_hash,见 commit d57032a6 U2 数据完整性,属 additive 完整性补丁。Flyway 已合入的脚本禁改,回滚只能写补偿迁移)。

为什么必须独立建表、绝不塞进 game_version:这是架构评审的明令。源项目(可维护、会被改)和打包产物(可玩、冻结)是两个物理解耦的存储面,塞在一起会破坏 GamePackage 产物的纯净性。这里曾与另一份 DB 评审的"game_version 双存"口径冲突,本架构采独立表口径胜出。

表里用一个 status 字段(0 草稿 / 1 已构建 / 2 孤儿 / 3 已发布)串起事务边界:

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/progressModelsourceProject(新核心产物 JSON 串)、assetSpecmodifyMode/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 用一道带限额的救场阶梯——这是创始人的原话落地:

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 切成三段来吃这个红利:

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 的模型调用统一网关)转发后,前缀缓存全部透传,第二款起命中 7693%。曾经的最大单点风险"网关会不会把缓存吃掉"已经解除,剩下的只是"冻结 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. 实测证据与验收口径

这套架构的地基已在代码层双重验证,不是纸面设计——但要把验证范围说准:双证证明的是"便宜模型能可靠产出连贯的 gameDefinition 中间表示",而非"它已是合格的终态产物":

  1. spike(小范围验证性实验)证地基:便宜模型对 source-project.schema.json 产出连贯的游戏定义(gameDefinition 中间表示),准确率 92100%,单款成本约 ¥0.011,行为模块带真逻辑 JS。
  2. build 段证可玩:游戏定义经"从源构建"流程,在真浏览器里跑过九门——deepseek-v4-flash 满分、便宜模型 3/4 款过门,有截图实证。口径限定:这是 spike 阶段的证据,不是现行产线成绩——gamedef 路尚未切为默认产线(U7 cutover 按门未切),现默认产线仍为 factory/iife 路;SAA 真全图 e2e(SaaFullGraphE2eTest)当前成功率 3/5=60% < 80% 门,端到端达标与 cutover 尚未完成(见 MVP进度总账 §2 M2 行生成主线架构演进路线 cutover 阶段)。

由此方向已经走通:"改源不改包 / 可维护源项目 / 把大模型当工作室"的中间表示一段在代码层成立,配套产出了"运行时访问约定 v0"(便宜模型只能经一个受控的 rt 对象访问引擎能力)和"从源构建"的装配原型。但终态尚待兑现——产物侧"gameDefinition → src/ 工程"那一段展开仍缺(创始人 2026-06-20 定调,见 §1、README §3.2设计合理性裁决),当前 new Function 内嵌串属已知债,不是已闭合的终局。

九门是这套质检的核心,指九道确定性的真玩自动门(serve-and-play 起服务、CDP 真浏览器玩、build 构建等组成的 harness),它们的脚本是子进程复用、禁止重写的既有资产。

最终验收口径分三层:

  • 后端线门:编译 + 单测绿、Flyway V18/V20 迁移绿(V18 建表 + V20 唯一键)、救场阶梯单测(5 次升档 / 8 次 giveup + dump 落盘)、trace 字节兼容、modify 局部性。
  • 引擎+前端线门:2D 适配器把定义渲成可玩、九门真玩绿、asset 产消打通、构建确定性(字节等价)、前端创作/修改/预览真机走查。
  • 联合 spike 门:缓存透传 已绿(7693%);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. 源项目物理归属:已收口 = 归 studio 模块(已落地——game-module-studio 下有 SourceProjectApi / SourceProjectLandReqDTO / SourceProjectStatusEnum,落库挂在 DifyCallbackServiceImpl 外层回调,commit d57032a6)。create/modify 编排入口本就在 studio,归属与之一致,不再悬而未决。
  2. 后端构建桩的范围:RuntimeBuildServiceImpl 本期建议只接"消费构建产物落包"路(已通),不强求它自驱 esbuild。
  3. classify→archetype 映射语义:确认是"引导"而非"校验"。
  4. 叙事失败计入救场口径:确认 narrative 评审的 needsRepair=true 等价一次九门失败计入 failCount,且评审自身 LLM 失败也计入(避免抖动卡死)。
  5. studio 路由形态:已收口 = 三路由已建(/studio/{create,modify,extend} 均已存在于 AppStudioController,见 line 89/97/105;create 一步式封装与既有 /draft+/generate 两步路并存,additive 未破存量)。残留子问题仅一处:create 一步式与两步式的职责边界(何时用哪条)随产线化沉淀,不必整条当未决。

11. 相关文档

文档 关系
架构域主文档 上层导航:本文是"生成引擎"方向的一份子设计
产品域主文档 本文实现的是产品域"一句话造游戏"那一环
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 文档里,各司其职、互不重复。