# 固定游戏架构 + SAA agentic studio — execution 版 > 类型:**execution 版**(供两开发线实现)。日期:2026-06-17 · 起草:Opus(effort=高)。 > **上游(权威,勿翻旧账)**: > - [`2026-06-17-固定游戏架构与SAA-agentic-studio-review.md`](2026-06-17-固定游戏架构与SAA-agentic-studio-review.md) —— 以 **§1.5(已定决策)+§10(深析并入)** 为权威;§4 旧 agent 表已被 §10.1 取代。 > - [`2026-06-17-生成主线-游戏生命周期项目管理-review.md`](2026-06-17-生成主线-游戏生命周期项目管理-review.md) —— modify=**改源不改打包产物** v2 口径权威;manifest 可寻址 diff 已废、不采用。 > - 命名/状态/分层规范见 [`.agents/rules/engineering-conventions.md`](../../.agents/rules/engineering-conventions.md) §10;收口走 [`.agents/skills/wave-close-checklist.md`](../../.agents/skills/wave-close-checklist.md)。 > **状态**:ACTIVE。**契约先行**——本档锁定的 8 契约 + 拓扑表是两开发线开工前必须齐的单一事实源;契约未冻结前两线不得独立开工。 --- ## 0. 结论先行(TL;DR) 1. **范式**:Opus 设计**固定游戏架构**(轻量声明式领域模型 + profile 三维),cheap 模型在 **SAA agentic studio** 里**填槽**;游戏 = **源项目**,生命周期 create/modify/extend/build/release/maintain;**改源不改打包产物**(GamePackage 产物 schema、宿主 `window.__GameBundle.bootGameHost`、feed 全不变)。 2. **8 契约**(本档 §5 逐个给字段级 schema/签名):①源项目 schema ②源项目 DB(独立 `game_source_project` 表)③GamePackage 源/产物分离 ④SAA state key 全表 ⑤asset 产消 ⑥modifyPatch ⑦唯一确定性构建 API ⑧trace+cost 字段。 3. **拓扑**(§6):现 11 节点 SAA 图 → 演进为 **render·classify(新)·design·scaffold·logic/repair(一个核心代码 agent 两面)·config/balance·asset(新)·build·QA(九门)·modify(新)** + 条件激活 **narrative-designer-reviewer** + 离线 **skill-curator**(MVP 只做 Debug Skill);救场阶梯 = cheap→连续 5 次九门失败→升档(stage1→stage2)→再 3 次失败→放弃,`recursionLimit` 按 5+3 重算。 4. **两线切分**(§8):**后端线**(6c6g 编辑 / mini-desktop 构建)锁源项目 schema+DB、SAA state/拓扑/救场路由、studio 编排、trace+cost、modifyPatch;**引擎+前端线**(Mac)锁 2D 适配器(游戏定义→LittleJS)、asset 产消、唯一构建实现、九门 QA、前端创作/修改/预览 UI。交汇=本档 8 契约。 5. **开工前待解 → ✅ 全部收口**(创始人 2026-06-17「按推荐」):①源项目表归属=**studio 模块** ②后端 RuntimeBuild **只落包**(构建留 worker/SAA build.mjs)③缓存透传 ✅ spike 绿(76-93%)+ classify ✅ 绿(tickModel 100%)④classify→archetype 映射表=**引导非校验** ⑤narrative `needsRepair=true` **等价一次九门失败计入 failCount** ⑥studio `create`=**draft+generate 一步式封装**。**契约冻结进行中**:keystone `source-project.schema.json` 已落;V18/ALL_KEYS/trace = 后端线 module 实现(V18 入 game-cloud 模块迁移,非 stale 的 contracts/db-schemas)。 --- ## 1. 目标与范围边界 ### 1.1 目标(本期 execution 要交付什么) 把"固定架构 + SAA agentic studio"由 review 决策**落成可被验证的契约与实现切分**,使两条开发线(后端 / 引擎+前端)能**契约先行、各自独立开工**,最终合流出:**一句话 → studio(cheap 填固定架构槽) → 源项目 → 确定性构建 → 九门真玩过门 → 现 GamePackage 产物 → feed 真玩** 的全链,且支持 **modify(改源不改产物,出新预览版,走发布审核)**。 ### 1.2 范围内(In Scope) - 固定游戏定义模型 schema(实体/组件/行为/场景/规则 + profile 三维)+ **2D 适配器(LittleJS)**。 - 源项目工件契约 + 独立 DB 存储(`game_source_project`)+ 源/产物分离。 - SAA 图扩展:新增 classify/asset/modify 节点 + narrative 分支 + 救场升档阶梯 + 完整观测/dump。 - 唯一确定性构建 API(化解构建权威分裂)。 - 三段式 prompt 缓存策略 + 版本化 Prompt Registry 资产(冻结 system+few-shot)。 - trace+cost 契约扩展(modelTier/escalationEvents/缓存命中两套字段/giveupDumpPath)。 - modify 生命周期(确定性编辑 / LLM 重生成单模块)。 ### 1.3 范围外(Out of Scope,本期不做) - **3D 适配器**(Cocos):定义格式留前向兼容,适配器本身 Phase 2。 - **Template Skill**(骨架萃取晋升):MVP 只做 Debug Skill(冷路径)。 - **per-creator 计量计费**:沿用现 new-api logs.quota 权威对账,不拆到人(对齐 memory `newapi-billing-plane-integration`)。 - **渠道线(小游戏)引擎**:另行竞标(HJ-CH-001),本架构不动 feed 终裁。 - **打包产物 schema / 宿主 / feed 改动**:零改(blast radius 收窄铁律)。 ### 1.4 不可破坏的硬约束(回归红线) | 约束 | 出处(已核码) | |---|---| | GamePackage 产物 schema 不变(`schemaVersion:"1.0"`,additive 新字段不升版本) | `contracts/game-package.schema.json:10-13` | | 宿主装载契约不变:`window.__GameBundle.bootGameHost({canvas,seed})` + iife 全局名 `__GameBundle` | `game-studio/src/host/inject.ts:202,241-248` | | 回调唯一写入路径不变:`DifyCallbackService.handleCallback`(succeeded 建版本→组包→落包→回填三表同事务) | `SaaGraphDispatcher.java:251`、`DifyCallbackReqVO.java:13-17` | | 九门 harness(serve-and-play.sh/play.cdp.cjs/build.mjs)**子进程复用·禁重写** | `SaaGenNodes.java:37`、`run.py:45-72` | | Flyway 已合入禁改,只新增:新表走 **V18.0.0**(接 V17 之后) | `V17.0.0__aigc_task_add_source.sql:19`(头注铁律) | | 错误码段:studio=1-112-***、aigc=1-101-***、runtime=1-103-*** | `V8.0.0__create_game_studio.sql:14` | --- ## 2. 前置条件 1. **上游决策已定**:review §1.5 + §10 全部已定(领域模型轻量声明式+三维 profile、Phase1 仅 2D、agent 列表、救场带限额、narrative 留 MVP、MVP 只 Debug Skill、写码+修 bug=一个核心代码 agent)。 2. **现行底座可用且已核码**: - SAA 全图 11 节点已建并生产收口(`SaaStudioGraph.assemble`),checkpoint(MysqlSaver)/observation/trace split-brain 已闭合(`703e462c`)。 - W-G1 worker 闭环可跑(generate→validate→build→play 九门),救场 stage1/stage2 在 `models.yaml`。 - 宿主引擎包路(P2)已真接:`bootGameHost` 引擎掌帧 + game_loaded/game_end 桥接已 e2e。 - `game-module-studio` 编排层已建(session/taskchain/asset,`/studio/{draft,generate,task,regenerate}`)。 3. **机器分工**(memory `internal-build-infra-servers`):后端编辑 6c6g、权威构建/e2e 门 mini-desktop、基建 mini-infra;**6c6g 禁起浏览器**(九门真玩在 mini-desktop)。 4. **子代理模型分流**(memory `opus-subagents-critical-tasks`):实现派 opus、机械活派 haiku、最高复杂度终裁用 opus 兜(本环境 fable 子代理不可用)。 --- ## 3. 涉及模块与文件路径 ### 3.1 后端线(game-cloud,com.wanxiang.huijing) | 关注点 | 路径 | |---|---| | 源项目 schema(新契约) | `contracts/agent-loop/source-project.schema.json`(**新建**) | | 源项目 DB(新 Flyway) | `game-cloud/huijing-server/.../db/migration/V18.0.0__create_game_source_project.sql`(**新建**)+ 同名副本入对应模块 `db/migration/`(与 V8/V17 同模式,两处一致) | | SAA 图布线唯一源 | `game-cloud/.../aigc/saa/SaaStudioGraph.java`(扩节点/边/recursionLimit) | | SAA 编排节点 + state key | `game-cloud/.../aigc/saa/SaaStudioNodes.java`(ALL_KEYS 扩、新 classify/asset/modify NodeAction) | | SAA 生成流水节点 | `game-cloud/.../aigc/saa/SaaGenNodes.java`(validate/scaffold/build/play) | | 派发器(job↔state↔回调桥) | `game-cloud/.../aigc/saa/SaaGraphDispatcher.java`(buildInputs/buildCallbackReqVO/extractTraceQuietly 扩) | | 回调入参 VO | `game-cloud/.../aigc/.../vo/DifyCallbackReqVO.java`(trace 字段扩 cost/modelTier/cacheHit) | | studio 编排层 | `game-cloud/game-module-studio/.../service/studio/StudioServiceImpl.java`(+ create/modify/extend 编排) | | studio 路由 | `game-cloud/.../studio/controller/app/studio/AppStudioController.java`(+ `/studio/{create,modify,extend}`) | | 确定性构建权威 | `game-cloud/game-module-runtime/.../service/build/RuntimeBuildServiceImpl.java`(桩转真 / 或定义构建实现归属) | | 执行器配置(救场旋钮) | `game-cloud/.../aigc/service/executor/AigcExecutorProperties.java`(`saaMaxRepairs=5`/`saaMaxPlayerRounds=1` 已在;补 stage/升档阈值) | ### 3.2 引擎+前端线(Mac) | 关注点 | 路径 | |---|---| | 2D 适配器(游戏定义→LittleJS) | `game-runtime/src/`(新增 adapter 层;`scripts/build.mjs` 不动=唯一构建脚本) | | 唯一构建脚本(共用) | `game-runtime/scripts/build.mjs`(worker 与 SAA build 节点共调,`--global-name=__GameBundle`) | | 九门 harness(禁重写) | `game-runtime/games/_wg1-gen/_shared/{serve-and-play.sh,play.cdp.cjs,entry-bundle.template.js,index.template.html}` | | 宿主装载契约 | `game-studio/src/host/{inject.ts,contract.ts,runtime/index.ts}`(消费源不变,零改) | | 前端创作/修改/预览 UI | `game-studio/src/`(创作工作坊接 `/studio/create`、修改接 `/studio/modify`、预览复用 GamePlayer) | | Prompt Registry(缓存前缀资产) | `contracts/prompts/`(冻结 system+few-shot 为版本化资产,CI 卡 byte 不变) | ### 3.3 worker(wg1,Python,引擎线复用) | 关注点 | 路径 | |---|---| | 生成编排闭环 | `wg1/gen-worker/worker/run.py`、`wg1/gen-worker/worker/agent_loop/studio.py` | | prompt 上下文包(缓存前缀来源) | `wg1/gen-worker/worker/prompt.py`(`SYSTEM` 常量 + `_few_shot()` 现从文件读=字节不稳,要固化) | | 校验门 | `wg1/gen-worker/worker/validate.py` | | 模型路由(救场阶梯) | `wg1/gen-worker/worker/models.yaml`(stage1/stage1_mmx/stage2) | | 成本折算(缓存命中字段) | `wg1/gen-worker/worker/cost.py`(现**不计缓存折扣**,`cache_ratio` 已在 `/api/pricing`,要落命中 token) | --- ## 4. 数据流与依赖 ### 4.1 全链数据流(create / build / modify) ```mermaid flowchart TB subgraph FE["前端线(Mac)"] U["创作者一句话+六类素材"] -->|POST /studio/create| ST U2["改一处(换美术/调参/改关卡/改玩法)"] -->|POST /studio/modify| ST PV["预览(GamePlayer)
window.__GameBundle.bootGameHost"] end subgraph BE["后端线(6c6g/mini-desktop)"] ST["studio 编排层
create/modify/extend"] -->|job(brief/素材/mode/baseVersion/target)| DISP DISP["SaaGraphDispatcher
job→state"] -->|invoke| G G["SAA 固定架构图
(§6 拓扑)"] -->|源项目 sourceProject| SRC SRC[("game_source_project
独立表 · 源 JSON/url")] SRC -->|确定性构建 API
sourceProject+buildProfile| BUILD BUILD["唯一确定性构建
esbuild → __GameBundle"] -->|engineBundle/checksum/bundleSize| CB CB["DifyCallbackService.handleCallback
(唯一写入路径·不变)"] -->|建版本→组包→落包| PKG PKG[("game_version + game_runtime_package
GamePackage 产物(schema 不变)")] end PKG -->|/app-api/runtime/package/{versionId}/manifest| PV PKG -->|发布审核(B1 链)| FEED["feed 真玩(不变)"] ``` ### 4.2 关键依赖与边界 - **源项目 = 新增 additive 存储面**(`game_source_project`);**GamePackage 产物 = 既冻产物面**。二者解耦:源落库成功 → 触发构建 → 构建成功才建 preview package;构建失败 → 源标孤儿、不建包(§5.2 事务)。 - **生成侧(new-api)/收益侧(trade)硬边界**不变(memory `newapi-billing-plane-integration`):缓存命中 token 只落**成本台账**(token 计量),不接收益结算。 - **modify 不动 currentVersion、不自动入 feed**:产物=新预览版,仍走发布审核;失败=不建新版、base 不动(对齐 v2 review §3.2 / 待确认⑤)。 - **classify→archetype 映射表**依赖现 `contracts/templates/*.schema.json`(8 个:clicker/dodge/match/runner/idle/merge/tycoon/generic)作为品类 profile 锚(§5.5)。 --- ## 5. 接口/数据契约(8 契约逐个给 schema/签名) > 原则:**契约 schema 具体到字段**,但不预写无法验证的实现代码。所有 schema 与现行代码兼容性已逐条核对。 ### 5.1 契约①:源项目 schema(新 `contracts/agent-loop/source-project.schema.json`) cheap agents 在固定架构上填的**唯一结构化产物**;Opus 设计、长期版本化升级。 ```jsonc { "$id": "https://wanxiang.ai/contracts/agent-loop/source-project.schema.json", "title": "SourceProject", "type": "object", "required": ["schemaVersion", "sourceHash", "profile", "gameDefinition"], "additionalProperties": false, "properties": { "schemaVersion": { "const": "1.0" }, // 源项目契约版本(独立于 GamePackage 版本) "sourceHash": { "type": "string", "pattern": "^[a-f0-9]{64}$" }, // 源 JSON 规范化后 sha256(可寻址/幂等键) "buildInputHash": { "type": "string", "pattern": "^[a-f0-9]{64}$" }, // sha256(sourceHash + buildProfile);构建缓存命中键 "profile": { // 维度无关三维(review §10.2,否则剧情/TRPG 绷断) "type": "object", "required": ["tickModel", "inputModel", "progressModel"], "additionalProperties": false, "properties": { "tickModel": { "enum": ["realtime", "turn-based", "event"] }, "inputModel": { "enum": ["continuous", "discrete-choice", "text-command"] }, "progressModel": { "enum": ["metric", "narrative"] } // narrative → 走 narrative-reviewer 质量门(§5.9) } }, "gameDefinition": { // 轻量声明式领域模型(非 AAA ECS:无 system 调度器/无 archetype 存储/无 query DSL) "type": "object", "required": ["entities", "scenes", "rules"], "additionalProperties": false, "properties": { "entities": { "type": "array", "items": { "$ref": "#/$defs/entity" } }, "components": { "type": "array", "items": { "$ref": "#/$defs/component" } }, // 组件定义(声明式属性:渲染/碰撞/物理) "behaviors": { "type": "array", "items": { "$ref": "#/$defs/behavior" } }, // 行为模块(逻辑,维度无关,读参数操作实体) "scenes": { "type": "array", "items": { "$ref": "#/$defs/scene" } }, // 关卡构成(数据驱动) "rules": { "type": "array", "items": { "$ref": "#/$defs/rule" } } // 胜负条件(数据驱动) } }, "assets": { "type": "array", "items": { "$ref": "#/$defs/assetSpec" } }, // 六类资产规格/引用(§5.5 asset 契约写入) "config": { "type": "object" } // 平衡参数(数值/难度/速度)——数据驱动,确定性 modify 改这里免 LLM }, "$defs": { "entity": { // 实体:transform(2D 用 xy/z=0,3D 用 xyz,同字段两维成立)+ 组件引用 "type": "object", "required": ["id", "transform"], "additionalProperties": false, "properties": { "id": { "type": "string", "minLength": 1 }, "transform": { "type": "object", "required": ["position"], "properties": { "position": { "type": "object", "required": ["x", "y"], "properties": { "x": {"type":"number"}, "y": {"type":"number"}, "z": {"type":"number","default":0} } }, "rotation": { "type": "number" }, "scale": { "type": "number", "default": 1 } } }, "components": { "type": "array", "items": { "type": "string" } } // 引用 components[].id } }, "component": { "type": "object", "required": ["id", "kind"], "additionalProperties": true, "properties": { "id": {"type":"string"}, "kind": { "enum": ["render","collision","physics","custom"] } } }, "behavior": { "type": "object", "required": ["id", "trigger"], "additionalProperties": true, "properties": { "id": {"type":"string"}, "trigger": { "enum": ["init","update","input","collision","timer"] } } }, "scene": { "type": "object", "required": ["id", "entityRefs"], "additionalProperties": true, "properties": { "id": {"type":"string"}, "entityRefs": { "type": "array", "items": {"type":"string"} } } }, "rule": { "type": "object", "required": ["id", "condition", "outcome"], "additionalProperties": true, "properties": { "id": {"type":"string"}, "condition": {"type":"string"}, "outcome": { "enum": ["win","lose","score","advance"] } } }, "assetSpec": { "$ref": "#/$defs/assetSpecBody" }, "assetSpecBody": { "type": "object", "required": ["id", "category", "ref"], "additionalProperties": false, "properties": { "id": { "type": "string" }, "category": { "enum": ["sprite","character","effect","scene","ui","music"] }, // 六类(对齐 v2 review C5) "ref": { "type": "string" }, // 平台 assetId/ref 为主(url 只读镜像) "url": { "type": "string", "format": "uri" }, "provider": { "type": "string", "default": "mmx-cli" } // provider 可插拔(§5.5) } } } } ``` > **诚实边界**:`gameDefinition.behaviors` 是声明式**模块描述**,真逻辑代码仍由核心代码 agent(logic)产出并经构建编译进 `__GameBundle`;源项目存"可维护的结构化定义",非裸 iife。**MVP 最小集**(对齐 v2 review 待确认①)= profile 三维 + 单 behavior + config + assets 引用,复杂结构随品类迭代。 ### 5.2 契约②:源项目 DB 存储(新 Flyway · 独立 `game_source_project` 表) **勿塞 `game_version`**(架构 review §10 明令);源项目是 additive 新存储面。 ```sql -- V18.0.0__create_game_source_project.sql(Flyway 只新增,接 V17 之后;头注铁律:已合入禁改,回滚写 V18.0.1 补偿) CREATE TABLE `game_source_project` ( `id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '源项目工件 ID', `game_id` BIGINT NOT NULL COMMENT '所属游戏 ID(project.game_project.id)', `version_id` BIGINT NULL COMMENT '关联产物版本 ID(project.game_version.id);构建成功建包后回填,失败留 NULL=孤儿', `source_json` LONGTEXT NULL COMMENT '源项目工件 JSON(SourceProject schema;M0 走 DB,大资产经 ref 外置)', `source_url` VARCHAR(512) NOT NULL DEFAULT '' COMMENT '源项目对象存储 URL(切对象存储后用,与 source_json 二选一)', `source_hash` CHAR(64) NOT NULL DEFAULT '' COMMENT '源规范化 sha256(可寻址/幂等;= SourceProject.sourceHash)', `schema_version` VARCHAR(16) NOT NULL DEFAULT '1.0' COMMENT '源项目契约版本', `build_profile` VARCHAR(64) NOT NULL DEFAULT 'default' COMMENT '构建画像(目标引擎/优化档;= buildProfile,影响 buildInputHash)', `status` TINYINT NOT NULL DEFAULT 0 COMMENT '源态:0 草稿 1 已构建 2 孤儿(构建失败) 3 已发布', `base_version_id` BIGINT NULL COMMENT 'modify 血缘:本源派生自的 base 版本(create 时 NULL)', `creator` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '创建者(Yudao 审计列)', `create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', `updater` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '更新者', `update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', `deleted` BIT(1) NOT NULL DEFAULT b'0' COMMENT '逻辑删除', `tenant_id` BIGINT NOT NULL DEFAULT 0 COMMENT '租户 ID', PRIMARY KEY (`id`), KEY `idx_game` (`game_id`, `id`) COMMENT '按游戏查源项目历史', KEY `idx_version` (`version_id`) COMMENT '按产物版本反查源', KEY `idx_source_hash` (`source_hash`) COMMENT '可寻址/幂等去重' ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COMMENT = '游戏源项目工件表(改源不改产物;与 game_version 解耦)'; ``` **事务边界(关键,化解孤儿)**: ``` ① studio 编排 → 图产源项目 → 源落库(status=0)成功 ② 触发确定性构建(§5.7) ③ 构建成功 → 建 preview package(走 handleCallback 唯一写入路径)→ 回填 version_id + status=1 ④ 构建失败 → 源 status=2(孤儿),不建 package,不动 currentVersion ``` 源落库与建包**不同一大事务**(构建数十秒级,长事务=连接池杀手,对齐 `SaaGraphDispatcher.java:55-57` 既有纪律);用 status 机器态串联,而非分布式事务。 > **归属待解见 §12-①**:`game_source_project` 物理归 studio 还是 project 模块(都引用 game_id/version_id),DB review 曾提"game_version 双存",与本独立表口径冲突,**本档采架构 review 的独立表口径,归属在 §12 收口**。 ### 5.3 契约③:GamePackage 源/产物分离(`game-package.schema.json` 维持产物-only) **不改 `game-package.schema.json`**。明确边界: | 面 | 存储 | schema | 可变性 | |---|---|---|---| | **源项目(可维护)** | `game_source_project`(新) | `source-project.schema.json`(新,§5.1) | cheap 填、确定性 modify 改 | | **打包产物(可玩)** | `game_version` + `game_runtime_package` | `game-package.schema.json`(既冻,`additionalProperties:false`) | 构建产出、宿主消费,**不变** | - `engineBundle` 仍是 GamePackage 的 **additive 可选**产物字段(`game-package.schema.json:94-97`),由构建写入、`handleCallback` 落包;**源项目不进 GamePackage**。 - `additionalProperties` 边界:GamePackage 顶层 `additionalProperties:false` → 源项目字段**禁止**塞进 GamePackage;源项目经独立表存,经 `provenance`(可选)只挂 `sourceHash` 做溯源(若需)。 ### 5.4 契约④:SAA state key 全表(`SaaStudioNodes.ALL_KEYS` 扩) 现 ALL_KEYS(已核码 `SaaStudioNodes.java:96-106`)+ 新增键。全 `ReplaceStrategy`(节点 return 覆盖写)。 **现有键(保留,不动语义)**:`brief, enriched, designText, gatespec, gatespecParseError, playSpec, factorySrc, gameId, role, repairCount, playerRound, feedback, playerFeedback, validateOk, buildOk, playPass, verdict, playerPanel, tokensIn, tokensOut, tokensByModel, attempts, status, failureReason, engineBundle`,+ SaaGenNodes 内部键 `gameDir, validateErrors, buildLog`,+ 参数化 `port, cdpPort, runId`。 **新增键(本档锁定)**: | key | 类型(state 内存形态) | 写节点 | 读/消费 | 语义 | |---|---|---|---|---| | `archetype` | String | classify | design/scaffold,映射表 | 品类原型(§5.5 枚举);填错品类比写错码更致命 | | `tickModel` | String | classify | logic/QA(选质量门机制) | realtime/turn-based/event(profile 三维之一) | | `inputModel` | String | classify | logic | continuous/discrete-choice/text-command | | `progressModel` | String | classify | playerRouter / narrative 路由 | metric/narrative;narrative→走 reviewer 门 | | `sourceProject` | String(JSON 串) | scaffold/logic/config/asset | build/emit/落库 | SourceProject 工件 JSON(§5.1);**新核心产物** | | `assetSpec` | String(JSON 串) | asset | logic/scaffold | 六类资产规格/引用(§5.5) | | `modifyMode` | String | render(modify 入口) | modify 路由 | "deterministic"\|"regenerate-module"(§5.6) | | `modifyPatch` | String(JSON 串) | modify | logic(regenerate)/scaffold(deterministic) | 改一处寻址+载荷(§5.6) | | `baseVersionId` | String | render | modify/emit | modify 的 base 版本(create 时空) | | `failCount` | int | playRouter/各失败回边 | okElseRepairOrGiveup/升档判据 | 连续九门失败计数(锚 `verdict.pass=false`) | | `modelTier` | String | render(初 stage1)/升档节点 | generate/repair 选模型 | "stage1"\|"stage2";救场升档作用面 | | `escalationEvents` | String(JSON 数组串) | 升档节点 | trace/dump | [{tierBefore,tierAfter,atFailCount,ts}] | | `giveupDumpPath` | String | giveup | trace/Opus 离线读 | 放弃前完整 dump 落盘路径 | | `narrativeReviewVerdict` | String(JSON 串) | narrative-reviewer | playerRouter/repair 路由 | narrative 质量门裁决(§5.9) | > **兼容性**:严格 additive——存量节点不读新键则零影响;`extractTraceQuietly`(`SaaGraphDispatcher.java:448`)best-effort 抽取,新键缺则省略,trace_json 字节兼容(键集仍为 `_extract_trace` 输出超集时需同步扩 worker 侧 `_extract_trace`,见 §8 后端线步骤)。 ### 5.5 契约⑤:asset 产消(asset 节点写 `assets[]`,下游消费) - **生产**:asset 节点据 `archetype`/`gameDefinition` 产六类资产规格,写 `state.assetSpec`(JSON 串)→ merge 进 `sourceProject.assets[]`(§5.1 `assetSpec` 形态)。 - **消费**:logic/scaffold 读 `sourceProject.assets[].ref`,构建时解析为 GamePackage `assets[]`(`game-package.schema.json:34-50`,类型 image/audio/...);宿主经 `assets[].url` 走 `/drawImage`(inject.ts CSP img-src 已放行)。 - **provider 可插拔**:`assetSpec.provider` 默认 `mmx-cli`(memory `mmx-asset-pipeline`:王蓝莓投资人版默认走 mmx-cli,音乐"记谱→合成"两步);切 provider 只改 asset 节点,不动消费侧。 - **六类枚举一次定死**(对齐 v2 review C5,四处同引):`sprite/character/effect/scene/ui/music`。 - **MVP 边界**:asset 节点 v0 可只产**规格(spec)不真生图**(用 canvas 几何兜底,对齐现 prompt.py 硬规则 7"资产容错"),真资产生成 provider 接入为 additive 升级;但 `assets[]` 产消通道**必须打通**(否则 v2 review §2 "assets[] 空"缺口未补)。 ### 5.6 契约⑥:modifyPatch(modify 节点输入/输出) modify = 在固定结构上改一处 + 重构建;对齐 v2 review §3.2(改源不改产物)。 ```jsonc // modifyPatch(state.modifyPatch JSON 串;modify 节点产) { "baseVersionId": "string", // 改谁(base 版本;对应 game_source_project.base_version_id) "mode": "deterministic | regenerate-module", // 确定性编辑 | LLM 重生成模块 "target": { // 寻址源项目部件 "kind": "asset | config | level | behavior", // 换美术=asset / 调参=config / 改关卡=level(scene) / 改玩法=behavior "path": "string", // 源项目内 JSON 指针(如 /config/speed、/assets/0、/gameDefinition/scenes/2;config/assets 为顶层,scenes 在 gameDefinition 下,对齐 keystone) "id": "string" // 或按部件 id 寻址(behaviors[].id / assets[].id) }, "payload": { // mode=deterministic:新值(直接覆写,免 LLM);mode=regenerate-module:重生成意图(喂 LLM) "value": {}, // deterministic 用:确定性新值 "intent": "string" // regenerate-module 用:改玩法逻辑的自然语言意图(只重生成那一个 behavior 模块) } } ``` **modify 口径(锁定,对齐 v2 review)**: - **换美术 / 调参 / 改关卡** = `mode=deterministic`:确定性编辑源文件→重构建→新预览版,**免 LLM、秒级**。 - **改玩法逻辑** = `mode=regenerate-module`:LLM **只重生成那个 behavior 模块**(非全量重出),其余部件不动。 - **产物**:新预览版(`game_version` 新行 status=2 预览就绪),**不动 currentVersion、不自动入 feed**,仍走发布审核(B1 链)。 - **失败**:不建新版、base 不动(`game_source_project` 不新增行 / 新增行标孤儿)。 - **manifest 可寻址 diff 已废**:不采用(v2 review supersede)。 ### 5.7 契约⑦:唯一确定性构建 API(化解构建权威分裂) **输入** `sourceProject + buildProfile` → **输出** `engineBundle + checksum + bundleSize + buildLog`。 ```jsonc // 构建 API 契约(语言无关签名;Java 侧 RuntimeBuild、worker 侧 build.mjs 同一实现) buildSource(input) -> output // input: { "sourceProject": { /* SourceProject §5.1 */ }, "buildProfile": "default" } // output: { "ok": true, "engineBundle": "string", // iife bundle 全文,顶层全局名 __GameBundle(契约冻结) "checksum": "string", // 整包 sha256(hex);= GamePackage.manifest.checksum "bundleSize": 123456, // raw 字节(B1 入场券:raw≤1.5MB,见 generic.schema.json) "bundleGzBytes": 12345, // gz 字节(B1 入场券:gz≤350KB) "buildLog": "string" // 失败回喂用;ok=false 时载错因 } ``` **构建权威化解(本档裁定,见 §12-②)**: - **推荐构建实现留 worker/编排器侧 esbuild**(`game-runtime/scripts/build.mjs` `--global-name=__GameBundle`,现已真跑、九门验过),**SAA build 节点与 worker 共调同一 `scripts/build.mjs`**(`SaaGenNodes.buildNode`/`run.py:build` 已是同脚本同参,**已天然统一**,无需第二实现)。 - **后端 `RuntimeBuildServiceImpl` = 落包+manifest 权威**(非第二构建器):接收构建产物(engineBundle/checksum/bundleSize)→ 经 `handleCallback` 落 `game_runtime_package` + 回填 `game_version` 三字段。当前它是桩(`RuntimeBuildServiceImpl.java:51` TODO),本期**不要求它自己跑 esbuild**,只要求它消费构建产物落包——**构建执行权在 worker/SAA build 节点,落包权在后端**,职责单一、不分裂。 - **确定性**:同 `buildInputHash`(sourceHash+buildProfile)→ 字节等价产物(v2 review M0 验收"非一次性"=从源重新构建出字节等价 bundle);锁引擎版本/依赖(`game-runtime/package.json` + esbuild 锁参)。 ### 5.8 契约⑧:trace+cost 字段(扩 `DifyCallbackReqVO.trace` + SAA trace 抽取) 现 `trace`(9d 账本,`DifyCallbackReqVO.java:68-75`,落 `game_aigc_task.trace_json`)扩字段: ```jsonc // trace(扩;snake/camel 与现 _extract_trace 口径一致;additive,缺则省略键) { // —— 现有(保留)—— "pass": true, "repairs": 0, "wallS": 12.3, "models": {}, "attempts": [], "gameId": "", "stage": "saa", "gatespec": {}, "sevenGateVerdict": {}, "tokens": { "prompt": 0, "completion": 0 }, "player": {}, // —— 新增(本档锁定)—— "modelTier": "stage1", // 终态模型档(救场升档后为 stage2) "escalationEvents": [ // 升档事件(state.escalationEvents) { "tierBefore": "stage1", "tierAfter": "stage2", "atFailCount": 5, "ts": "ISO8601" } ], "cacheHit": { // 缓存命中(成本台账;命中 token 落账) "promptCacheHitTokens": 0, // 取 max(DeepSeek prompt_cache_hit_tokens, MiniMax prompt_tokens_details.cached_tokens) "source": "deepseek | minimax | none" // 命中来源(字段口径见下) }, "giveupDumpPath": "string", // 放弃前完整 dump 落盘路径(state.giveupDumpPath;Opus 离线读) "cost": { // F3 折¥(SAA 路现只 token,本期补折¥见 §8;非阻断) "totalRmb": 0.031, "gateRmb": 0.15, "byModel": {} } } ``` **缓存命中检测(兼容两套字段,锚 review §1.5)**: - **DeepSeek**:`usage.prompt_cache_hit_tokens`。 - **MiniMax**:`usage.prompt_tokens_details.cached_tokens`。 - **取 max** 落 `cacheHit.promptCacheHitTokens`;命中 token 进**成本台账**(`cost.py` 现不计缓存折扣 `cost.py:10-12`,本期补:读 `/api/pricing` 的 `cache_ratio` + 命中 token 折真实 quota,落 trace.cost,**不阻断、仅观测**)。 ### 5.8.1 缓存策略(三段式 prompt + Prompt Registry 资产) - **三段式**(review §10.3,spike 已证绿):**固定前缀(契约/few-shot/能力面,永不变)→ 共享中段(GDD)→ 可变后缀(brief/反馈)**。现 `prompt.py:SYSTEM` 已是固定前缀骨架,但 `_few_shot()` **从文件读 → 字节不稳**(缓存命中要求前缀 byte 完全一致)。 - **动作(本档锁定)**:把 `SYSTEM` + few-shot 冻结为**版本化 Prompt Registry 资产**(`contracts/prompts/`,现有 `registry.yaml` + 04-config/07-fix 等),**CI 卡 byte 不变**(few-shot 从文件读改为读已固化的版本化常量,字节固定)。 - **L1 架构契约前缀先做**(review §1.5:最大块,第二款起省~90% input)。 - **✅ 缓存透传已 spike 证绿(2026-06-17)**:deepseek-v4-flash / MiniMax-M3 / M2.7 经 new-api 网关前缀缓存**全部透传**,第二款起命中 **76-93%**;检测兼容两套字段(DeepSeek `prompt_cache_hit_tokens` / MiniMax `prompt_tokens_details.cached_tokens` 取 max)。**缓存策略坐实、风险解除**;实施只剩"冻结 few-shot 字节稳 + 落命中台账"工程动作(B9/E5)。 ### 5.9 narrative 子系统(创始人裁定保留 MVP · 条件激活) `progressModel=narrative` 分支由 **narrative-designer-reviewer** 当质量门(替代九门——九门确定性测不了叙事连贯)。 ```jsonc // narrativeReviewVerdict(state.narrativeReviewVerdict JSON 串;narrative-reviewer 产) { "problems": [ "string" ], // 叙事连贯/设计问题列表 "needsRepair": true, // 是否需回 repair(true→走 repair 路,消费同 playerFeedback 范式) "founderFinal": false // 终判留创始人(false=未终审;质量门按品类换机制,叙事类终判人工) } ``` **接入(锁定)**: - **路由**:`progressModel=narrative` → play 节点后**不走九门 playRouter,改走 narrative-reviewer**;`needsRepair=true` → repair(同 `repairNode` 自增 repairCount + role=fix);`needsRepair=false` → emit。 - **失败计入救场 failCount(§12-⑤ 收口点)**:九门对 narrative 失效,故 narrative 的 `needsRepair=true` **等价一次"九门失败"计入 `failCount`**(同 5+3 阶梯:连续 5 次 needsRepair→升档、再 3 次→giveup);`recursionLimit` 同口径重算。 - **follow-up**:产品需求清单需补一条 **narrative P-id 与验收**(标注待补,见 §11)。 --- ## 6. 最终拓扑表(无孤儿节点 · recursionLimit 按救场 5+3 重算) ### 6.1 拓扑图(SAA 固定架构图) ```mermaid flowchart TB START([START]) --> render render --> classify classify --> design design --> generate["generate(logic 写 / role=fix 时 repair 修=同一核心代码 agent 两面)"] generate --> validate validate -->|ok| scaffold validate -->|fail & failCount<5| repair validate -->|fail & 升档窗| escalate scaffold --> asset asset --> build build -->|ok| play build -->|fail| repair play -->|metric & 九门 pass| player play -->|narrative 路| nreview["narrative-reviewer"] play -->|fail & failCount<阈| repair player -->|无体验问题/轮尽| emit player -->|有体验问题| repair nreview -->|needsRepair=false| emit nreview -->|needsRepair=true & 轮未尽| repair repair --> generate escalate["escalate(升档 stage1→stage2)"] --> generate emit --> END([END]) giveup["giveup(完整 dump)"] --> END play -.->|failCount≥5 & 已 stage2 再失败 3| giveup modify["modify(确定性编辑/regenerate-module)"] -.->|确定性路| build modify -.->|regenerate-module 路| generate ``` ### 6.2 逐节点拓扑表(入边/出边/读写 state/消费者/路由条件) | 节点 | 入边 | 出边(路由条件) | 读 state | 写 state | 消费者 | |---|---|---|---|---|---| | **render** | START / modify 入口 | →classify(create)/→generate(modify regenerate)/→build(modify deterministic) | brief, modifyMode, baseVersionId | role=code, repairCount=0, playerRound=0, failCount=0, modelTier=stage1, tokensIn/Out=0 | classify/modify | | **classify(新)** | render | →design | brief | archetype, tickModel, inputModel, progressModel | design/scaffold/logic/QA;映射表 | | **design** | classify | →generate | brief, archetype | designText, enriched, playSpec, gatespec, gatespecParseError | generate | | **generate(logic+repair 两面)** | design / repair / escalate | →validate | role, modelTier, enriched, feedback | factorySrc, sourceProject(填 behaviors/config), feedback, tokensByModel, attempts | validate | | **validate** | generate | ok→scaffold;fail→repair(failCount<5)/escalate(升档窗) | factorySrc | validateOk, validateErrors, feedback | scaffold/repair | | **scaffold** | validate(ok) | →asset | gameId, factorySrc, playSpec, sourceProject | gameDir, sourceProject(落盘结构) | asset/build | | **asset(新)** | scaffold | →build | archetype, sourceProject | assetSpec, sourceProject.assets[] | build/logic;provider=mmx-cli | | **build** | scaffold/asset / modify(确定性) | ok→play;fail→repair | gameId, gameDir, sourceProject, buildProfile | buildOk, buildLog, engineBundle, checksum, bundleSize | play/emit | | **play(QA 九门)** | build(ok) | metric&pass→player;narrative→nreview;fail→repair/giveup | gameId, port, cdpPort, playSpec, progressModel | playPass, verdict, feedback, failCount(++) | player/nreview/repair | | **player(软门)** | play(metric pass) | 无问题/轮尽→emit;有问题→repair | gameId, brief, verdict | playerPanel, playerFeedback | playerRouter/emit | | **nreview(narrative,条件)** | play(narrative 路) | needsRepair=false→emit;true&轮未尽→repair | brief, sourceProject, verdict | narrativeReviewVerdict, failCount(++) | emit/repair | | **repair** | validate/build/play/player/nreview 失败回边 | →generate | repairCount, playerFeedback | role=fix, repairCount++, playerRound++(软门), feedback | generate | | **escalate(新)** | 失败回边(failCount 命中升档阈) | →generate | failCount, modelTier | modelTier=stage2, escalationEvents+= | generate | | **emit** | player/nreview(收口) | →END | gameId(bundle), sourceProject | status=succeeded, engineBundle | handleCallback | | **giveup** | 救场耗尽(stage2 再失败 3) | →END | feedback, attempts(全), sourceProject | status=failed, failureReason, giveupDumpPath(完整 dump) | handleCallback/Opus 离线 | | **modify(新)** | render(modify 入口) | 确定性→build;regenerate-module→generate | modifyPatch, baseVersionId, sourceProject(base) | sourceProject(改一处), modifyMode | build/generate | ### 6.3 救场阶梯 + recursionLimit 重算 - **锚**:`verdict.pass=false`(确定性九门,非主观;`SaaGenNodes.playNode:409` `rc==0 ∧ verdict.pass`)。 - **阶梯**:cheap(stage1)→**连续 5 次九门失败 → escalate 升档(stage1→stage2)**→ stage2 **再 3 次失败 → giveup**。 - **state 驱动**:`failCount` 在每次九门(及 narrative `needsRepair`)失败 +1;`okElseRepairOrGiveup`/`playRouter` 判据改为: - `failCount < 5` → repair(stage1); - `failCount == 5 ∧ modelTier==stage1` → escalate(升 stage2,failCount 续计或重置由实现定,**建议续计**:stage2 阈 = 5+3=8); - `failCount ≥ 8`(stage2 再 3 次)→ giveup。 - **recursionLimit 重算**:现 `recursionLimitFor(maxRepairs) = (maxRepairs+1)*7+78`(`SaaStudioGraph.java:221`)。救场总轮 = 5(stage1)+3(stage2)=8 主轮,每轮主链 ≈ 9 节点(render→classify→design→generate→validate→scaffold→asset→build→play),+ 升档/repair 回环。**新公式建议**:`recursionLimitFor = (5+3+1)*10 + 余量` ≈ `90+余量`,确保正常收口不误触超步(余量保守取 ≥30)。`AigcExecutorProperties.saaMaxRepairs` 由 5 升为可配,新增 `saaStage2ExtraRepairs=3`。 - **全程留存(硬观测,非可选)**:每编辑步 + 日志留存;`giveup` 前**完整 dump**(所有 attempts 源码/verdict 整包落盘 → `giveupDumpPath`),喂 Opus 离线分析。 ### 6.4 classify→archetype 映射表(避免孤儿分类,锚 review §10.1) `archetype` 枚举把"消除族"拆细(review 明令);映射到现 `contracts/templates/*.schema.json` 品类 profile: | archetype | tickModel | inputModel | 品类 profile 锚(现模板 schema) | |---|---|---|---| | `clicker` | event | discrete-choice | `templates/clicker.schema.json` | | `dodge` | realtime | continuous | `templates/dodge.schema.json` | | `runner` | realtime | continuous | `templates/runner.schema.json` | | `bubble`(瞄准发射) | realtime | continuous | `templates/match.schema.json`(瞄准变体) | | `match3`/`line-clear`(点选) | turn-based | discrete-choice | `templates/match.schema.json` | | `merge` | event | discrete-choice | `templates/merge.schema.json` | | `idle` | event | discrete-choice | `templates/idle.schema.json` | | `tycoon` | turn-based | discrete-choice | `templates/tycoon.schema.json` | | `generic`(兜底) | * | * | `templates/generic.schema.json` | - **物理优先分类**(OpenGame Physics-First,review §10.5):classify 先定 tickModel(spike 已证 100%)→ 再定 archetype(93%);可选 DeepSeek 副模型双跑兜底。 - **映射表是契约**:classify 出 archetype 必落到此表的一行(否则=孤儿分类);新增 archetype 必须同时补本表 + 对应 profile 锚。 --- ## 7. 关键实现步骤(按两线) > 顺序:**先冻结 8 契约 → 两线并行**。契约冻结前任一线不得开工(契约先行铁律)。 ### 7.0 契约冻结(联合,开工门) 1. 写 `contracts/agent-loop/source-project.schema.json`(§5.1)。 2. 写 `V18.0.0__create_game_source_project.sql`(§5.2,两处副本)。 3. 扩 `SaaStudioNodes.ALL_KEYS` 新键(§5.4)。 4. 扩 `DifyCallbackReqVO.trace` 字段口径(§5.8)。 5. 收口 §12 五个待解项(尤其①归属、②构建权威、⑤narrative failCount)。 6. 两轮评审已过(review 版),实现版按 memory `impl-review-one-opus-round` **单轮 opus 评审**。 ### 7.1 后端线(6c6g 编辑 / mini-desktop 构建) | # | 步骤 | 文件 | 验证 | |---|---|---|---| | B1 | 源项目 schema + DB | `source-project.schema.json`、`V18.0.0__*.sql` | Flyway 启动迁移绿(mini-desktop) | | B2 | SAA 图扩 classify/asset/modify/escalate/nreview 节点 + 边 + recursionLimit 重算 | `SaaStudioGraph.java`、`SaaStudioNodes.java` | `SaaStudioGraphTest` 回归绿 + 拓扑无孤儿 | | B3 | state key 全表落地(新键 + KeyStrategy 注册) | `SaaStudioNodes.java:ALL_KEYS` | 单测:全键 ReplaceStrategy 注册 | | B4 | 救场阶梯 failCount/modelTier 路由 + giveup 完整 dump | `SaaStudioNodes.java`(playRouter/okElseRepairOrGiveup/giveupNode) | 单测:5 失败升档、8 失败 giveup + dump 落盘 | | B5 | trace+cost 扩(modelTier/escalationEvents/cacheHit/giveupDumpPath)+ worker 侧 `_extract_trace` 同步扩 | `SaaGraphDispatcher.java:extractTraceQuietly`、`service.py:_extract_trace` | 字节兼容:`ReadinessScorer` 两路同读 | | B6 | studio 编排 + /studio/{create,modify,extend} | `StudioServiceImpl.java`、`AppStudioController.java` | 单测 + 编译绿;modify 出新预览版不动 currentVersion | | B7 | modifyPatch 契约接线(确定性编辑路 / regenerate-module 路) | `StudioServiceImpl.java`、`SaaStudioNodes.modifyNode` | 单测:确定性免 LLM、regenerate 只重一模块 | | B8 | 源落库事务边界(源→构建→建包,失败标孤儿) | `StudioServiceImpl.java` / 落库服务 | 单测:构建失败源 status=2、不建包 | | B9 | 缓存命中字段落账(`cache_ratio` + 命中 token 折¥) | `cost.py`、`SaaGraphDispatcher` | 单测:两套字段取 max、落 trace.cost | ### 7.2 引擎+前端线(Mac) | # | 步骤 | 文件 | 验证 | |---|---|---|---| | E1 | 2D 适配器(游戏定义→LittleJS 渲染):SourceProject.gameDefinition → 可玩 | `game-runtime/src/`(adapter 层) | 单测:实体→sprite、behavior→逻辑 | | E2 | 唯一构建实现核对(SAA build 节点与 worker 共调 `scripts/build.mjs`) | `game-runtime/scripts/build.mjs`(不动) | 同源同参:`__GameBundle` 产出一致 | | E3 | asset 产消(asset 节点 spec → assets[] → 宿主消费)+ provider=mmx-cli 插桩 | `game-runtime/` + worker asset 角色 | 九门:assets[] 非空、宿主加载 | | E4 | 九门 QA harness 复用(禁重写)+ narrative 路质量门接 reviewer | `_shared/*`(harness 不动)+ reviewer 角色 | 九门真玩绿(mini-desktop) | | E5 | Prompt Registry 冻结 few-shot(byte 稳)+ CI 卡 byte | `contracts/prompts/`、`prompt.py` | CI:few-shot byte 不变 | | E6 | 前端创作(接 /studio/create)/ 修改(接 /studio/modify)/ 预览 UI | `game-studio/src/` | 真机 UI 走查(mini-desktop CDP) | ### 7.3 缓存透传 spike ✅ 已完成(绿) - **结果(2026-06-17)**:deepseek-v4-flash / MiniMax-M3 / M2.7 经 new-api 网关前缀缓存**全部透传成立**,第二款起命中 **76-93%**。 - **落地口径**:检测兼容两套字段(DeepSeek `prompt_cache_hit_tokens` / MiniMax `prompt_tokens_details.cached_tokens`,取 max);命中 token 落成本台账。 - **结论**:缓存策略(三段式 + Prompt Registry 冻结)坐实,**不再是前置阻断项**;实施只剩"冻结 few-shot 字节稳(B9/E5)+ 落命中台账(B9)"。 --- ## 8. 两线切分与交汇(谁锁谁) ```mermaid graph LR subgraph 契约交汇["8 契约(开工前冻结=两线交汇面)"] C1["①源项目 schema"]; C2["②源项目 DB"]; C3["③源/产物分离"]; C4["④SAA state 全表"] C5["⑤asset 产消"]; C6["⑥modifyPatch"]; C7["⑦唯一构建 API"]; C8["⑧trace+cost"] end BE["后端线(6c6g/mini-desktop)
锁:C1·C2·C4·C6·C8 + SAA 拓扑/救场/studio 编排"] --> 契约交汇 EN["引擎+前端线(Mac)
锁:C1(消费)·C5·C7(构建实现)·C3(产物消费) + 2D 适配器/九门 QA/前端 UI"] --> 契约交汇 ``` - **后端线主锁**:①②④⑥⑧(源项目 schema/DB、SAA state/拓扑/救场路由、studio 编排、modifyPatch、trace+cost)。 - **引擎+前端线主锁**:①(消费)⑤⑦(构建实现)③(产物消费)+ 2D 适配器、asset 产消、九门 QA、前端创作/修改/预览 UI。 - **共锁**:① 源项目 schema(后端写、引擎消费)、⑦ 构建 API(引擎实现 esbuild、后端落包)。 - **并行边界 = 模块边界**(memory `prefer-parallel-over-serial`):零共享可变态,worktree 隔离,只对真独占资源(九门端口 4320/9222)串行。 --- ## 9. 边界失败路径 | 失败 | 处置 | 出处 | |---|---|---| | 源落库成功但构建失败 | 源 status=2(孤儿),不建 package,不动 currentVersion | §5.2 事务 | | 构建产 bundle 缺 `__GameBundle` 全局名 | emit 判 failed(`bundle_missing_global_name`),不落坏包 | `SaaStudioNodes.emitNode:666` | | LLM 调用超时/429/5xx | 单次 180s 超时 + ≤3 重试,超限不裸退图(写 feedback) | `SaaStudioNodes.callWithTimeoutAndRetry:154` | | 九门连续失败 | failCount++ → 5 升档 → 8 giveup + 完整 dump | §6.3 | | narrative needsRepair 连续 true | 同九门 failCount 阶梯(§5.9/§12-⑤) | §5.9 | | 缓存前缀不透传(spike 已证透传,仅理论兜底) | 命中字段缺则按 miss 计价、生成不阻断(仅成本回退) | §7.3 ✅ | | modify 失败 | 不建新版、base 不动 | §5.6 | | classify 出未知 archetype | 落 generic 兜底 + 告警(不污染下游) | §6.4 | | trace 抽取异常 | best-effort 吞+warn,trace_json 留 NULL,不阻断生成 | `SaaGraphDispatcher.java:497` | | 图终态空(超步等) | 兜底 failed(llm_error) 回调,真因留日志 | `SaaGraphDispatcher.java:376` | --- ## 10. 验证方法(含两线各自验收门) ### 10.1 后端线验收门 1. **编译 + 单测绿**:`SaaStudioGraphTest`(图布线回归)、`SaaStudioNodesRegressionTest`、新 classify/asset/modify/救场单测。 2. **Flyway 迁移绿**(mini-desktop):V18 迁移成功,`game_source_project` 建表。 3. **救场阶梯单测**:5 失败升档(escalationEvents 落)、8 失败 giveup(giveupDumpPath 落盘+完整 dump)。 4. **trace 字节兼容**:`ReadinessScorer` 在 SAA 路与 worker 路同读 `pass/gatespec.driver/sevenGateVerdict.guards/repairs`(`SaaGraphDispatcher.java:424` 口径)。 5. **modify 局部性单测**:确定性编辑免 LLM、regenerate-module 只重一 behavior、不动 currentVersion。 ### 10.2 引擎+前端线验收门 1. **2D 适配器单测**:SourceProject.gameDefinition → 可玩(实体→sprite、behavior→逻辑)。 2. **九门真玩绿**(mini-desktop,端口 4320/9222):cheap 在固定架构填出过九门游戏,`verdict.pass=true`。 3. **asset 产消门**:`sourceProject.assets[]` 非空 → GamePackage `assets[]` → 宿主加载(非空 assetContext)。 4. **构建确定性门**(v2 review M0):同 `buildInputHash` → 字节等价 bundle(从源重新构建可复现)。 5. **前端真机 UI 走查**(memory `frontend-link-ui-walk-publish-fix`:编排器旁路掩盖 UI 缺陷 → 真 UI 走查唯一手段):create/modify/预览闭环。 ### 10.3 联合 spike 门(决定架构成不成立) 1. **缓存透传 spike ✅ 已绿**(§7.3):deepseek-v4-flash/M3/M2.7 经 new-api 透传成立(76-93% 命中)。 2. **classify 准确率 spike ✅ 已绿**(§6.4):M3 tickModel 100%/archetype 93%、$0.003/次,便宜分类不污染下游;唯一改进=archetype 枚举拆"消除族"(bubble vs match3/line-clear)。 3. **端到端门(待实施后验)**:一句话 → studio 填固定架构 → 源项目 → 构建 → 九门过门 → GamePackage → feed 真玩;成功率(对齐 MVP ≥80%)+ 单款 < $1(¥0.15 门)+ modify 局部性。 --- ## 11. 完成条件(Definition of Done) 1. **8 契约全冻结**(schema/DB/state/构建 API/trace 全落地且评审过)。 2. **后端线**:SAA 固定架构图扩节点全绿(单测+Flyway),救场阶梯/giveup dump/trace+cost 可验,studio create/modify/extend 编排通。 3. **引擎+前端线**:2D 适配器可把游戏定义渲成可玩,九门真玩绿,asset 产消通,构建确定性(字节等价),前端创作/修改/预览真机走查过。 4. **联合 spike 三门过**:缓存透传成立、classify 准确、端到端成功率+成本+modify 局部性达标。 5. **无孤儿设计**(AGENTS §7):每节点有入边/出边/消费者(§6.2);每新数据结构有产消通道。 6. **follow-up 登记**: - 产品需求清单补 **narrative P-id 与验收**(§5.9)。 - 3D 适配器(Phase 2)、Template Skill(冷路径晋升)登记为远期。 - `game_source_project` 物理归属模块最终落定(§12-①)。 7. **知识回写**(AGENTS §7):新增 skill `fixed-architecture-saa-studio`(固定架构填槽+救场阶梯+缓存前缀+classify 映射),更新 `.agents/README.md` 索引;收口走 wave-close-checklist 8 步。 --- ## 12. 开工前待解(诚实列出 · 现行代码与设计未解冲突) > 以下是我判断**现行代码/文档与本设计存在未解冲突或需创始人/两线收口的点**,不糊过去。开工门(§7.0)须逐条收口。 > > **✅ 全部待解经创始人 2026-06-17「按推荐」确认收口**(本节转为已收口记录):① 源项目表归属 = **studio 模块**(create/modify 编排入口在此;project 仍管 game_version 产物权威)· ② 后端 `RuntimeBuildServiceImpl` 本期**只接"落包"**,不自驱 esbuild(构建留 worker/SAA `build.mjs`)· ③ 缓存透传 **✅ spike 已证绿**(76-93%)· ④ classify→archetype 映射表语义 = **引导(品类预设)非校验**(对齐"玩法模板未废、游戏模板已废")· ⑤ narrative 失败 = **`needsRepair=true` 等价一次九门失败计入 `failCount`**(reviewer 自身抖动也计,防卡死)· ⑥ studio `create` = **`draft+generate` 一步式封装(additive,不破现两步路)**。**契约冻结进行中**:keystone `contracts/agent-loop/source-project.schema.json` 已落;V18 SQL + ALL_KEYS 扩 + trace 扩 = 后端线 module 实现(V18 入 game-cloud 模块迁移,**非 stale 的 `contracts/db-schemas/`〔已停于 V9〕**)。 ### ① 源项目存储权威归属(DB review 与架构 review 口径冲突) - **冲突**:v2 生命周期 review §5 说"`game_version` 内容从打包产物**升级为源项目工件+构建产物双存**";架构 review §10 + 本档定"**独立 `game_source_project` 表,勿塞 game_version**"。两者矛盾。 - **本档裁定**:采**独立表**(改源不改产物的关键=源/产物物理解耦,塞 game_version 会破 GamePackage 产物纯净 + 既冻 schema)。 - **待收口**:`game_source_project` 物理归 **studio 模块**(它是编排层、已持 game_id/version_id 引用,V8 错误码段 112)还是 **project 模块**(版本台账权威)?**倾向 studio 模块**(create/modify 编排入口在此,源项目是其编排产物;project 仍管 game_version 产物权威)。需创始人/两线拍。 ### ② 构建权威分裂(worker 私有 esbuild vs 后端 RuntimeBuild 桩) - **现状(已核码)**:worker `run.py:build`/SAA `SaaGenNodes.buildNode` **已共调** `game-runtime/scripts/build.mjs`(同脚本同参,**已天然统一**);后端 `RuntimeBuildServiceImpl.triggerCompile` 是**桩**(`:51` TODO,只落排队不跑编译)。 - **本档裁定(§5.7)**:**构建执行权 = worker/SAA build 节点的 `scripts/build.mjs`**(已真跑、九门验过,不另造第二实现);**后端 RuntimeBuild = 落包+manifest 权威**(消费构建产物落 `game_runtime_package` + 回填 `game_version`,不要求它自跑 esbuild)。 - **待收口**:`RuntimeBuildServiceImpl` 桩的 TODO 是否本期转真?**建议本期只接"消费构建产物落包"路**(经 handleCallback 已通),不强求它自驱 esbuild 执行引擎(那是渠道线/未来 CI 化才需)。需确认范围。 ### ③ 缓存前缀经 new-api 网关透传 ✅ 已 spike 证绿(已解除,非待解) - **结果(2026-06-17 spike)**:deepseek-v4-flash/M3/M2.7 经 new-api 网关前缀缓存**全部透传**,命中 76-93%;检测兼容两套字段(DeepSeek `prompt_cache_hit_tokens` / MiniMax `prompt_tokens_details.cached_tokens`)。 - **classify 准确率 spike 同绿**:M3 tickModel 100%/archetype 93%、$0.003/次。 - **剩工程动作**(非待解):冻结 few-shot 字节稳(B9/E5)+ 命中 token 落账(B9)。 ### ④ classify→archetype 映射表必须与现 8 模板 schema 对齐(否则孤儿分类) - **现状**:`contracts/templates/` 现有 8 schema(clicker/dodge/match/runner/idle/merge/tycoon/generic),但**语义已变**(`generic.schema.json` 注:P3 走引擎 bundle 路,校验 bundle 形态而非 GameConfig 玩法字段)。 - **待收口**:§6.4 映射表把 archetype 锚到这些 schema 作"品类 profile 引导",但旧模板 schema 校验对象(GameConfig)在 bundle 路已退场——映射表用它们做**引导 prompt/脚手架预设**(玩法模板=品类框架,非 pre-built 代码,对齐 memory `tiered-engine-cocos-decision` 2026-06-17 术语纠偏:废的是"游戏模板"、"玩法模板未废"),**非填参执行器**。需确认映射表语义=引导而非校验。 ### ⑤ narrative 失败如何计入救场 failCount(九门对它失效) - **冲突点**:救场阶梯锚 `verdict.pass=false`(九门),但 narrative 路**不走九门**(走 reviewer)。若不定义,narrative 路无升档/giveup 终止条件 → 可能死循环。 - **本档裁定(§5.9)**:narrative `needsRepair=true` **等价一次九门失败计入 failCount**(同 5+3 阶梯);`recursionLimit` 同口径。 - **待收口**:确认此口径(narrative reviewer 的 needsRepair 作为 failCount 源);+ narrative reviewer 自身失败(LLM 调用失败)是否也计 failCount(建议计,避免 reviewer 抖动卡死)。 ### ⑥ studio 路由 create/modify/extend 部分净新(现只有 draft/generate/task/regenerate) - **现状(已核码)**:`AppStudioController` 现有 `/studio/{template/list,draft,generate,task/{id},task/{id}/regenerate}`;**无** `/create`(统一入口)/`/modify`/`/extend`。 - **待收口**:v2 review C3 的 `/studio/{create,modify,extend}` 是**净新增**;`create` 与现 `draft+generate` 关系=统一入口归一(generic 缺省,对齐 `7534bdf9` 一句话入口),还是并存?建议 `create` = `draft+generate` 的一步式封装(additive,不破现有两步路)。 --- ## 13. 回滚策略 | 变更 | 回滚 | |---|---| | Flyway V18(`game_source_project`) | 写 **V18.0.1 补偿迁移 DROP TABLE**(不改 V18,头注铁律);additive 表删除不影响 GamePackage 产物链 | | SAA 图新节点(classify/asset/modify/escalate/nreview) | 新节点 additive;`SaaStudioGraph.assemble` 布线唯一源,删新节点+边即回现 11 节点图;state 新键不读则零影响 | | 救场阶梯(failCount/升档) | `AigcExecutorProperties` 开关:`saaStage2ExtraRepairs=0` → 退回单档 5 轮(现行);recursionLimit 回旧公式 | | trace+cost 扩字段 | additive(`DifyCallbackReqVO.trace` Map):删字段口径,缺则省略键,trace_json 字节回旧 | | modify 编排 + /studio/{create,modify,extend} | 新路由 additive;下线路由即回现 draft/generate/regenerate;modify 不动 currentVersion=天然安全 | | 缓存 Prompt Registry 冻结 | few-shot 回从文件读(`_few_shot()`);缓存命中字段不落账=现行(cost 不计缓存折扣) | | 2D 适配器 | 引擎线产物;现 worker 直产 iife 路保留=回退路 | > **核心安全垫**:GamePackage 产物 schema / 宿主装载契约 / 回调唯一写入路径 / 九门 harness **全不变** → 任一新增失败,生成主线可回退到现 W-G1 worker 直产 bundle + 现 11 节点 SAA 图,B1 发布链/feed 真玩零回归。 --- ## 14. 知识回写(收口时执行,AGENTS §7) - **skills/**:新增 `fixed-architecture-saa-studio.md`(固定架构填槽范式 + 救场 5+3 阶梯 + 三段式缓存前缀冻结 + classify→archetype 物理优先映射 + modify 改源不改产物);更新 `saa-graph-orchestration.md`(新节点/state 键/recursionLimit 重算)。 - **knowledge/**:`tech-decisions.md` 补"固定游戏架构=轻量声明式领域模型(非 AAA ECS)+ 2D 适配器(LittleJS)/ 3D Phase2";`glossary.md` 补 SourceProject/archetype/profile 三维/救场阶梯/escalate。 - **rules/**:补红线"源项目独立 `game_source_project` 表,勿塞 game_version;GamePackage 产物 schema 不变"。 - **更新 `.agents/README.md` 索引 + `docs/agent-specs/_index.md` 活地图**(本档登记 ACTIVE)。