docs(生成引擎): SoT① Step4 — 退役 17 份被并文档(tombstone 指针)+ 图引用归位

16 份图说/详设 + 设计合理性裁决,内容已并入 SoT①,替换为 tombstone 指针
(保留路径=保入链,各指向 SoT① 对应章节;原文存 git 历史):
- 并入①:agentic集成架构/自治富游戏引擎/tier2四层工程架构/tier2实现详设/
  tier2细节图说 A-F·H/SAA编排/SAA能力API-dossier/开闸接线/引擎与运行时/固定游戏架构
- 结论并入①后退役:设计合理性裁决
图引用归位:07全局两线→§四、09廉价线端到端→§4.1、08长生命周期→§5.6、
00-06 tier2总览→§4.2(消孤儿);修 typo gamefdef→gamedef。
SoT① 现引用 36 张 svg、全部存在、无 dead 链。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
lili 2026-06-24 22:36:27 -07:00
parent 2aa2ffec2f
commit bcde9acee2
18 changed files with 105 additions and 3271 deletions

View File

@ -1,334 +1,9 @@
# SAA 编排 · 生成引擎基建设计
> 🚧 **架构演进中** —— 生成引擎子树正处于 gameDefinition→`src/` 终态迁移 + 产线化(plan `2026-06-18-001` U1–U4)中,文中标注的现行结论可能随推进变化;以子树 [README](README.md) 与最新裁定 / plan 为准。
> **这是什么**:绘境AI 生成引擎的 AI 编排基建设计文档,回答"**用什么框架把一句话编排成一款游戏、为什么这样分层、有哪些不能破的约束**"。
> **给谁看**:负责生成主线的后端工程师、架构评审、新加入参与生成模块的同事。
> **怎么读**:先读第 1 节建立决策锚点(为什么是 SAA、它和"自治型 agent"是两种问题),再看第 2 节那张编排拓扑图建立全局;想了解硬约束读第 4 节六条不变量,想了解过渡期治理风险读第 5 节 split-brain 防线,想看清和旧编排器的职责边界读第 7 节。实现级的坑清单与逐键源码旁证不在本页,通过文末第 10 节的指针跳转。
这份文档讲的是生成引擎最底层的那台"编排机器"——不是讲它生成出来的游戏长什么样(那在[固定游戏架构](固定游戏架构.md)),也不是讲游戏跑在什么引擎上(那在[引擎与运行时](引擎与运行时.md)),而是讲**用什么框架、按什么拓扑、在什么约束下,把生成流程一步步驱动起来**。
---
status: 已退役(2026-06-24 收敛进 SoT)
canonical: false
date: 2026-06-24
---
## 1. 一句话与决策锚点
# (已退役)SAA 编排
绘境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 事件……),没有任何主观评分。只有九门**全部通过**,这款游戏才被认为"机制可玩"。这一层是整套架构最大的工程价值,也是绝不能动的地板——它是"机制能不能玩"这件事唯一的裁判。
**但要点清九门的有效性边界**:九门是**机制 CI**(判能不能玩),**不是质量 oracle**(不判"好玩 / 好看")——中间整段"是不是题面要的那个游戏""好不好看",九门没有任何门兜底。它当前还有两个已知 Goodhart 口子:**latch 终态语义不分胜负**(latch 由系统强加恒期望为真,空壳游戏弃守排空也能逼出一个 gameover 蹭过门),以及**出题与被考同源**(classify 自产品类、design 自产验收规格,而便宜模型恰恰连 `ball.x`、`driver:none` 这种基本写法都会反复写错)。这两件事的修法——"player 软门做厚 / oracle 去自评"——是**在飞演进项**,详见 [设计合理性裁决](设计合理性裁决.md) §3.3(裂缝三)与 [生成主线架构演进路线](../../../agent-specs/生成主线架构演进路线.md)。所以读者不要据此高估验收闭环对"质量"的鉴别力:九门兜的是机制地板,质量上限当前由便宜模型单点决定。
**engineBundle 是什么?** 它是最终的打包产物:build 节点把 gameDefinition(声明式的游戏描述)和 LittleJS(绘境AI 选用的 Tier-1 游戏引擎,详见[引擎与运行时](引擎与运行时.md))装配在一起,产出一个可以直接在浏览器里运行的 bundle。
**render 节点的输入端不止"一句话"——还有确认目标和六类素材(创始人 2026-06-22 历史回收判定捡回)。** 图的入口画的是 render"解析创作意图 → PromptContext",这里要把它的输入端讲全,否则会让人以为生成永远只凭一句话起步。真实的创作输入有两条额外通道,都在 render 这一层被解析进 PromptContext,再随图往下流:**一是智能确认目标**——系统先分析创作者那句话,清晰的直接直通生成,模糊的才弹一张确认卡问清楚,确认结果以一个显式的 `confirmedGoal` 随上下文携带(不留隐藏共享态),而不是每次都打断、也不是永远不确认;**二是六类素材驱动(assetContext)**——创作者上传或选用的图元 / 角色 / 特效 / 场景 / 界面 / 音乐六类素材,透传进生成上下文(图片走多模态、音乐与脚本作结构化上下文),让生成有据可依而不是只凭文字臆测。这两条通道在编排里的落点就是 render→generate 这一段的 state(`PromptContext` 加素材引用),它们的完整交互设计(`/studio/analyze` 出 `{parsedGoal, needsConfirm, clarifyQuestions[]}` 的口径、六类素材契约、以及创作期数据回灌)以 [固定游戏架构 §对话式生成闭环](固定游戏架构.md) 为现行 SoT;本档只钉死它们"在 render 节点入图"这一编排接点,不重复展开。
**为什么 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(行为逻辑)。需要先把它的真实范式说准:它是一种**混合范式**——entities / components / scenes / rules 那层是声明式结构化外壳,但承载 100% 玩法逻辑的 `behavior.code` 字段当前**并未进契约**(schema 里 behavior 的 `required` 只有 `id` / `trigger`,逻辑核靠 `additionalProperties: true` 偷渡,运行时还接受未文档化的 `js` 别名)。所以它的准确定性是"声明式数据壳 + 受限逻辑的自由 JS 核",而不是名副其实的"真结构化源"——这一点与同目录 [设计合理性裁决](设计合理性裁决.md) §3.1(裂缝一·名实不符)、[README](README.md) §3.2 口径一致。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 款通过门,有截图实证。
但这里要把"双证"证明了什么、没证明什么说准——**创始人已于 2026-06-20 拍下终态定调**(见 [README](README.md) §3.2 与 [生成主线架构演进路线](../../../agent-specs/生成主线架构演进路线.md) ★ 段):生成游戏的**终态产物必须是一个 `src/` 多文件代码工程**(结构化、模块化、agent 能快速定位),这条不可妥协。据此:gameDefinition **不是**合格终态产物,只是**通往 src/ 的中间妥协态**——妥协只允许在"输入端"(让便宜模型先产一个极简声明式描述),绝不允许妥协在"产物端"。所以"双证"证明的是一件真实但**有限**的事:**便宜模型能可靠产出连贯的 gameDefinition 中间表示,且该中间表示能 build-from-source 过九门**;它没有、也不能证明 gameDefinition 就是合格终态产物。当前实现把整段玩法逻辑塞进 `behavior.code` JSON 字符串、运行时用 `new Function`(把字符串当代码执行)解释跑掉,正是设计明令反对的"硬编码 blob"——**这是已知偏离终态的债**,缺的正是"gameDefinition → src/ 工程"产物侧的展开段(具体怎么补属后续 plan,不在此展开)。目标产线正在产线化(执行计划 `docs/plans/2026-06-18-001`,分 U1–U4 四个阶段)。
> 这里有两个内部代号要点清:**接法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`。
### 整图串行约束:当前一次只能跑一条生成图(创始人 2026-06-22 历史回收判定捡回)
有一条架构现状约束必须显式记下,否则后人提"并行抬成功率 / 抬吞吐"方案时会重新发现它、白绕一圈:**当前整条生成图是串行的,一次只能跑一款游戏。** 成因有两处叠加——`SaaGraphDispatcher` 是单线程的,并且用一个 `Semaphore(1)` 把整条 `graph.invoke()` 包住,同时图里 build / play 两步调的子进程(esbuild 构建在 4320、CDP 真玩在 9222)用的是**固定端口**。`Semaphore(1)` 保证任一时刻只有一条图在跑,正是为了让这两个固定端口不被并发任务抢占冲突。
这条约束的影响面比它看起来宽:它不只卡某一个功能,而是**任何"同时跑多条生成图"的方案的共同前置**。两个具体后果——
- **best-of-N 落不了**(同一 brief 跑 K 个候选挑最优交付):K 个候选要并行才有意义,但整图串行下它们只能一个接一个排队跑,墙钟成本退化成 K 倍、失去并行优势。所以 best-of-N 在现行档里只作远期方案记一笔(见 [生成引擎 README §6 / 待补](README.md)),它的硬前置就是先解掉这里的端口绑定。
- **单局墙钟长这件事也与它同根**:走高质量路(M3 thinking-on 异步产线)时单局可达数十分钟,这是异步产线可接受的代价、但它和整图串行叠加,意味着"排队效应"会被进一步放大——前一款没跑完,后一款连起跑都排不上。
要解这条约束,真正要动的是"把固定端口换成每任务动态分配的端口池 + 放开 `Semaphore` 的并发度",这是一项独立的架构改造,不在 MVP 范围;在它落地之前,任何并行方案都先别排期。
> 口径归属:`Semaphore(1)` 串行守端口这条坑的逐键旁证在 `.agents/skills/saa-graph-orchestration.md`;本节把它从"实现坑"提升为一条"架构现状约束"记进架构档,因为它是 best-of-N 与一切并行生成方案落不了的共同根因,值得在提方案之前就被看见。同口径在 [agentic 集成架构](agentic集成架构.md) 也有一句对应的登记。
---
## 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。
- **gameDefinition 中间表示双证(2026-06-18)**:证明的是一件真实但**有限**的事——便宜模型能可靠产出连贯的 gameDefinition 中间表示(Spike 段质量 92–100%,¥0.011/款),且该中间表示能 build-from-source 过九门(Build 段:deepseek-v4-flash 满分,便宜模型 3/4 过门,有截图实证)。它**不等于**"真结构化终态产物已落地"(终态 = src/ 工程,见下方【待办】与 §3.2)。同时产出"运行时访问约定 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**的产线化收尾(注:它收的是中间表示这条线;产物侧"展开成 src/ 工程"那段是另立的债,见【待办】)。
- **接法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
- **产物侧 gameDefinition → src/ 工程展开段(偏离终态的债)**:终态产物必须是 `src/` 多文件工程(创始人 2026-06-20 拍板,见 §3.2 与 [生成主线架构演进路线](../../../agent-specs/生成主线架构演进路线.md) ★)。当前实现停在 JSON 内嵌 JS 串 + `new Function` 解释执行,缺的正是"gameDefinition 编译/展开成 src/ 工程"这一段;且须配一道 doc↔code 兑现门(断言产物存在 src/ 多文件结构、逻辑落在真实源码文件里)。具体怎么补属后续 plan。
- **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. 深度参考
本文档聚焦架构全景。以下内容不在此展开,通过指针跳转:
| 主题 | 位置 |
|---|---|
| 生成主线"往哪走、先做什么"(现行 SoT:现状诊断 / OpenGame 对标 / 统一演进路线 / 产物终态=src/ 定调) | [生成主线架构演进路线](../../../agent-specs/生成主线架构演进路线.md) |
| 范式合理性裁决(名实不符 / 表达力天花板 / 九门质量边界 / 终态定调 / Tier0 分层) | [设计合理性裁决](设计合理性裁决.md) |
| 完整落地坑清单(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` |
| gameDefinition 中间表示产线化执行计划(U1–U4) | `docs/plans/2026-06-18-001-feat-gen-lifecycle-and-tier1-breadth-plan.md` |
| 游戏跑在什么引擎上 / 运行时访问约定 | [`引擎与运行时.md`](引擎与运行时.md) |
本文档的设计内容已并入生成运行时架构单一事实源 [`agentic运行时架构图说.md`](agentic运行时架构图说.md)(§4.1 实例一、§5.5 能力面、§5.6 装载左支)。请以该 SoT 为准;本文件原始内容见 git 历史。

View File

@ -1,244 +1,9 @@
# SAA 能力 / API / 接入 · 逐键源码证据底座
> 🚧 **架构演进中** —— 生成引擎子树正处于 gameDefinition→`src/` 终态迁移 + 产线化(plan `2026-06-18-001` U1–U4)中,文中标注的现行结论可能随推进变化;以子树 [README](README.md) 与最新裁定 / plan 为准。
> **这是什么**:绘境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 源码里能不能落地、落在哪一行"。两者一上一下:看设计读编排文档,照代码写实现、或要复核某条结论的源码出处,读本文档。
---
status: 已退役(2026-06-24 收敛进 SoT)
canonical: false
date: 2026-06-24
---
## 调查方法与基线(先讲清证据从哪来)
# (已退役)SAA 能力 API dossier
本文档的全部结论,来自一轮**多路独立源码取证**:四个 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"。
本文档的设计内容已并入生成运行时架构单一事实源 [`agentic运行时架构图说.md`](agentic运行时架构图说.md)(§5.5 能力面 · 廉价线侧)。请以该 SoT 为准;本文件原始内容见 git 历史。

View File

@ -137,6 +137,8 @@ B 类对接上面某几条 A 协议,换引擎 / 换框架 / 换渠道时被换
两条生成线是同一套 adapter 协议的两个实例,运行期零耦合,只在公共组件(计费 / 送审 / feed / 验收基线)交汇。
> 全局两线与 tier 体系总览:`assets/07-全局两线与tier体系.svg`。
### 4.1 实例一:SAA · LittleJS 廉价线(Tier0/1)
**范式**:便宜模型先吐一份结构化游戏定义,平台用确定性运行时去解释它,再用九门把"是不是真能玩"判定下来。产物落 LittleJS 增强发行版。
@ -147,7 +149,9 @@ B 类对接上面某几条 A 协议,换引擎 / 换框架 / 换渠道时被换
**它的天花板,要诚实**:对抗式裁决判定这套范式"部分合理"——它是为"便宜模型 + 自动验收"量身打造的聪明 Tier0 超休闲范式,但顶着"声明式结构化源"的名号,内核却是"声明式数据壳 + 一坨未受契约约束的自由 JS"(承载全部玩法逻辑的 `behavior.code` 没进契约),表达力被运行时焊死在四类几何色块玩具上,九门只判机制地板、"好玩好看"无下限托底。结论很清楚:**坚持它做 Tier0 是对的;拿它去够 Marvel / KOF 那一档富交互愿景,是范式选错。**
**怎么补**:不是推倒,是"对齐名实、显式分层、把逻辑往结构化拽回"。真正补差距的工程载体是 2026-06-21 的 **A-model 转向**——LLM 写真 `src/` 多文件、调 L2 十一插件(juice / gamefeel / palettePost 给手感卖相),而不是 gamefdef 色块。所以 tier1 高质轻游戏的载体是 **L2 能力库 + L3 自由写**。这条把"轻量≠简单"落到工程上:轻量是用公共素材、玩法模板、工程模板低成本生成相对不复杂、又不易同质化的高质小游戏,不是产没人会玩的简单玩具。
**怎么补**:不是推倒,是"对齐名实、显式分层、把逻辑往结构化拽回"。真正补差距的工程载体是 2026-06-21 的 **A-model 转向**——LLM 写真 `src/` 多文件、调 L2 十一插件(juice / gamefeel / palettePost 给手感卖相),而不是 gamedef 色块。所以 tier1 高质轻游戏的载体是 **L2 能力库 + L3 自由写**。这条把"轻量≠简单"落到工程上:轻量是用公共素材、玩法模板、工程模板低成本生成相对不复杂、又不易同质化的高质小游戏,不是产没人会玩的简单玩具。
> 廉价线端到端闭环图:`assets/09-廉价线端到端.svg`。
### 4.2 实例二:AgentScope · Phaser 富游戏线(tier2)
@ -161,7 +165,7 @@ B 类对接上面某几条 A 协议,换引擎 / 换框架 / 换渠道时被换
**这条线尚未落代码**:整轨待 0 号 spike 验证,Phaser 一行未落,锁 AgentScope 2.0.2。验证方式不是 n≥30 统计批跑,而是收敛环:并发跑 5 个,错 > 1 个就读日志、分析、修复、并发重跑 5 个。
> tier2 这条线的完整图集(生成两阶段、系统全景、调用时序、运行时内部结构、ReAct 循环、编排与配置、复用边界、产物与契约、关键类图,共 9 张)见 §5 各 facet,资产在 `assets/`。
> tier2 这条线另有一套总览图,与 §5 各 facet 的细化图互补:生成两阶段 `assets/00-生成两阶段.svg`、系统全景 `assets/01-系统全景.svg`、调用时序 `assets/02-调用时序.svg`、运行时内部结构 `assets/03-运行时内部架构.svg`、ReAct 循环 `assets/04-ReAct循环流程.svg`、复用边界 `assets/06-reuse边界全景.svg`。
---
@ -260,7 +264,7 @@ tier2 与 A-model(廉价线那条 LittleJS 轻量经营档)**两线分立、运
右支的装载比左支多出一段:入库的是一个真 Phaser 工程(多源文件加构建脚本加依赖锁),取回之后要先按构建 profile 用 esbuild 打包出可玩 bundle,再在沙箱里跑——这一段构建是左支即取即跑所没有的。`finish` 工具交付的形状与落库取回认的形状**共用同一份 schema**:agent 自治跑完经 finish 吐出的那个工程,和落库取回认的那个工程本是两处定义,共用一份则"契约即工具签名,改一处即两处一起改",从源头消除交付与落库漂移这个失败面。装载终态仍服从 latch 轮询:游戏跑到结束态时不主动发事件,而是把状态焊成可轮询的终态、宿主每帧轮询读。整条线的产物是可维护的源工程而非死 bundle,改源、重新构建、按内容哈希长期取回重建——这正是"改源不改包、游戏即长生命周期项目"在装载面的兑现。
> **现 / 建**:左支 ECS-lite 装载契约现行已落、是唯一的对照锚;右支七要素契约、第二装载分支、finish 共用 schema 全部待建(tier2 整轨待 spike)。
> 图:`assets/t2-F-01-源项目契约七要素.svg`、`assets/t2-F-02-第二装载分支.svg`、`assets/t2-F-03-finish共用schema.svg`。
> 图:长生命周期源项目 `assets/08-长生命周期源项目.svg`;`assets/t2-F-01-源项目契约七要素.svg`、`assets/t2-F-02-第二装载分支.svg`、`assets/t2-F-03-finish共用schema.svg`。
### 5.7 观测与成本(OTel)

View File

@ -1,133 +1,9 @@
# agentic 集成架构:控制面与管理面
---
status: 已退役(2026-06-24 收敛进 SoT)
canonical: false
date: 2026-06-24
---
> 🚧 设计中 · 控制面是 tier2 与现有 SAA 线共用的治理层;部分组件已有地基(D12、infra_config 热改通路),部分还是蓝图。
# (已退役)agentic 集成架构 · 控制面与管理面
> **这是什么**:让两条生成线(SAA 廉价线、tier2 自治富游戏)被配置、观测、审计、管起来的治理层设计 —— 配置驱动(改配置不发版)、可追踪、可审计。
> **给谁看**:生成主线后端、做平台 / 中台的工程师、想"像 Dify 一样管 agent 平台"的产品与创始人。
## 为什么要这一层
让 agent 自治生成游戏,这件事本身不够。平台还得能配置它(改 prompt、换模型、调 skill 不用发版)、看见它(每次生成到底发生了什么)、审计它(谁改了哪条配置)、在入口拦住它(配额、并发、降级)。这是创始人最早提的"像 Dify 一样可视化、可追踪、可审计地管 agent 平台"。这一层就是控制面和管理面,它同时管两条生成线 —— SAA 廉价线和 tier2 自治富游戏。
```mermaid
flowchart TB
subgraph G[控制面 / 管理面]
R[配置注册表<br/>prompt · model · skill · tool · mcp]
O[观测 / 审计仓<br/>运行轨迹 + 配置审计]
D[D12 运行治理门<br/>配额 · 并发 · 背压 · 降级]
UI[管理面 UI<br/>视图 + 编辑器]
end
SAA[SAA · Tier0/1 廉价线]
T2[tier2 · 自治富游戏]
R -->|读配置 id+版本| SAA
R -->|读配置 id+版本| T2
SAA -->|写轨迹| O
T2 -->|写轨迹| O
SAA --> D
T2 -. 待接 .-> D
UI --> R
UI --> O
```
它的内核是:配置即数据,全链可观测。不是从零造一个 Dify。能复用现成件就复用,能用一道一致性门兜住的就不上重型框架 —— 同目录的对抗审查给过一个对症范例:某个运行时能力本来要四处人手同步,正确解法不是上重型注册表,而是加一道一致性测试逼你改齐(从实现反射真实签名、断言它和注册表相等、对不上就红)。
## 它从属于线A,只做净增量
仓里已经有一份《生成主线架构演进路线》(下称线A),它是配置外置那层地基的分期主计划:把 prompt 做成两端同源、解决 split-brain(同一份 prompt 被 Java 和 Python 各存一份会漂移),是它的阶段2;把模型名外置成 models.yaml 加跨语言一致性校验,是它的阶段3。配置外置那层地基,本设计不另立分期,只认领并引用线A。控制面真正的净增量收窄到几件:配置审计日志、管理面 UI、把两条线的轨迹收成一份统一契约、把配置注册表从 prompt 推广到 skill / tool / mcp、热加载机制选型。
## Agent 框架可替换:反锁死原则(创始人 2026-06-22 历史回收判定捡回)
控制面对外只暴露"读配置、写轨迹、过治理门"三件事,这背后还有一条更根本的架构原则,得显式立住:**平台不绑死在任何单一 agent 框架上。** 当下生产线是 SAA-only(决策 HJ-AGI-002),这是经双评审与源码核验后选定的实现;但"选了 SAA"不等于"被 SAA 锁死"。AgentScope / LangGraph / AutoGen 这些框架可以被借鉴、组合、在不同轨上分别采用(比如 long-term 的 tier2 premium 轨就走 AgentScope),平台真正固化、不随框架走的,是协议层那几件东西——把它们钉牢,框架就被压到"只租用不拥有"的位置,换框架时业务不被连根拔起。
固化在协议层、与框架解耦的是这五件:
1. **任务协议**——生成任务怎么提交、怎么回调,边界就是 job/callback 契约 #6 暴露的 `GenerationDispatcher` 接口(见 [SAA编排 §4 不变量五](SAA编排.md));换框架只换 dispatcher 实现,调用方一行不改。
2. **状态模型**——一次生成的状态怎么表达、怎么续跑(checkpoint 的语义),是平台自己的口径,不是某个框架的私有结构。
3. **工具接口**——agent 能调哪些工具、工具的入参出参长什么样,以契约定义,不绑某框架的工具注册机制。
4. **验收门禁**——"完成"由确定性九门裁定、禁止 LLM 自评,这条是平台铁律(同 §4 不变量二),无论底下换成哪个框架,出题与被考不同源这条都不让步。
5. **遥测事件**——每次生成发生了什么,落进统一 trace 契约(`contracts/trace/`,公共核心子集对称、扩展段各写各的),采集机制可以两条线各按各的框架来,但数据口径是平台的,不随框架漂移。
这条原则不是空谈"将来好换",它有具体落点:它正是 long-term 要不要把某条轨从 SAA 切到 AgentScope 时的**判据**——只要那五件仍稳稳钉在协议层,切换就只是换一个被治理的执行后端,而不是推倒重来。反过来,如果哪天发现某个框架的私有结构悄悄渗进了上面五件中的任何一件(比如轨迹格式被某框架的 span 结构绑死、任务协议泄漏了框架的内部状态),那就是锁死的开始,要立刻在协议层把它隔离回去。这也解释了本档为什么反复强调"管理面只渲染运行时自报的拓扑、绝不另持一份拓扑模型"——那同样是反锁死:管理面里硬编码的框架拓扑,换框架时就是阻力。
**一条配套的架构现状约束登记(037,与 [SAA编排 §整图串行约束](SAA编排.md) 同口径)**:SAA 这条线当前整图串行——`SaaGraphDispatcher` 单线程 + `Semaphore(1)` 包住整条 `graph.invoke()`、build/play 子进程用固定端口(4320 构建 / 9222 真玩),一次只能跑一款。它是 best-of-N 与任何并行生成方案落不了的共同根因;要解得先把固定端口换成每任务动态分配的端口池、再放开并发度。控制面将来若要给生成线排"并行抬吞吐"的能力,这条端口绑定是硬前置,提方案前先看见它。逐键旁证见 SAA编排,这里只登记一句,避免后人在治理层重新发现。
## tier2 接入面:官方 Agent Service + Agent Team
在讲控制面四个组件怎么接 tier2 之前,得先说清 tier2 这条线的接入面长什么样 —— 它不是我们自造的,是 AgentScope 2.0.2 的官方 service 层,源码逐条核过(`agentscope/app/`)。这层既是 cloud 调 tier2 的入口(控制面在这之上加治理),也是 tier2 内部多 agent 协作的底座(管理面对它的拓扑做可视化)。把它讲清,控制面/管理面才不会去重复造轮子。
**service 化 / 控制面接入**:tier2 不自造 API,直接用官方 **Agent Service**。它由 `create_app` 拉起一个 FastAPI 应用,对外是 REST + SSE,内置八个 router 全部核过 —— `/sessions`(会话)、`/chat`(对话)、`/schedule`(定时任务)、`/credential`(托管密钥)、`/workspace`(执行环境)、`/model`、`/tts_model`、外加 agent 管理面。它天生多租户、durable session(会话状态 `AgentState` 落库可恢复),底下用一套 **MessageBus(Redis 实现)** 撑起分布式协作:Redis Stream 做事件 replay 日志、pub/sub 做唤醒广播、还有分布式锁(`acquire_lock`)和取消信令(`session_publish_cancel` / `task_publish_cancel`)。`/schedule` 提供 cron 式定时触发,`/credential` 把各类模型密钥托管起来。所以 cloud 接 tier2 的真实姿势是:经 REST `POST /sessions` 建一个 durable 会话、再经 SSE 收事件流;SSE 流可恢复 —— 断线后靠 MessageBus 的 replay 日志补发,不丢中间过程。这正好兜住了"外部服务/异步任务要考虑超时、失败、断线"的可靠性要求。
会话初始化有三条路径(对应"游戏=长生命周期项目"的三种入场):① 续接之前的会话 —— 取 `SessionRecord.state` 历史 resume;② 加载一个已有工程 —— `Workspace.workdir` 指向已存在的游戏目录,迭代它;③ 新建 —— 用模板初始化工具铺出工程骨架。
**多 agent 管理面 / Agent Team**:这里有个关键的版本事实必须钉死,否则会照着过时的认知去设计。AgentScope 2.0.2 已经删掉了进程内的 `MsgHub` / `pipeline` 那套同步编排原语 —— 多 agent 协作不再是"在一个进程里串 pipeline",而是部署态的 **Agent Team**:由一个 leader session 用 `TeamCreate` / `AgentCreate` / `TeamSay` / `TeamDelete` 这几个工具,星形地调度若干 worker session(源码 `agentscope/app/_tools/` 核过)。这套调度有两条硬约束源自框架本身:worker 之间不互相评审、只对 leader 汇报(星形,非网状);worker 被 leader 创建后立即开干,干完经 MessageBus 的 inbox/wakeup 机制异步通知 leader,leader 不必轮询等待。tier2 的两阶段流程(阶段 1 工作室多 agent 发散设计 → 阶段 2 单写 agent 实现)就跑在这套 Agent Team 上 —— 阶段 1 的 leader 用 AgentCreate 拉起玩法/关卡/数值/UI 等设计 worker、TeamSay 收敛,阶段 2 收敛成单写以防并行写冲突。管理面要可视化的"多 agent 拓扑",渲染的就是这套部署态 Team 的运行时自报结构,而不是另持一份拓扑模型。
## 四个组件
控制面四个组件对两条生成线暴露同一套契约:生成线只管"从注册表读配置、向观测仓写轨迹",不感知管理面长什么样。
### 配置注册表:配置的唯一事实源
配置注册表解决创始人那句"改 prompt、模型、skill、mcp 还要重新部署"。内核是:凡是现在"要改就得发版"的东西,都搬进一个版本化、可回滚的注册表。
prompt 和 models 这两层是线A的阶段2-3,本设计只引用、不重排,但给 models.yaml 补一句线A没细化的范围:它要覆盖"SAA 的逐角色路由加 tier2 的 agent 路由",并说清现有两个配置入口怎么并进去 —— `AigcExecutorProperties.llmModel` 是 worker 的单模型默认值,`SaaGraphDispatcher.saaForceModel` 是一个全局覆盖开关(创始人那次"只用 M3"加的,非空就把全部角色统一成那个模型)。models.yaml 落地时,前者降为 fallback,后者作为"全局强制覆盖"保留在配置里,逐角色那档从 Java 工厂迁进 yaml。
把注册表从 prompt 推广到 skill / tool / mcp,是净增量。但有一条硬纪律:别在裂的地基上盖更大的注册表。今天的 Prompt Registry 不是"现成可热加载"的地基 —— 它的加载器类注释写着,prompt 是构建期被 maven 插件复制进 classpath 的快照,改 prompt 等于改 `contracts/` 原文件、升版本号、过四道闸,下次构建部署才带新版,是 Git 版本化加构建期注入,不是运行时热加载。它自身还有缺口:注册表里有的条目标着"STUB 占位",对应 prompt 文件通篇是占位提纲;加载器里硬编码了一批模板路径,未必都登记进注册表。所以推广前先补一道一致性 CI:注册表列的每条 prompt 都有对应文件、每个硬编码加载的 id 都在注册表登记、STUB 要么补正要么标为不可上线。地基自洽了,推广才有意义。
### 观测 / 审计仓:发生了什么的唯一事实源
这块回答两个独立问题,对应两条留痕线。
运行轨迹记的是每次生成发生了什么 —— 每步推理、每次工具调用、每次 LLM 输入输出、每道门裁决、每次成本。消灭 split-brain(轨迹分散在各条派发路上)的真口径是"每条派发路诚实镜像它真有的字段、没有的绝不编造",不是"强求两条线字段对齐" —— tier2 的 ReAct"想-做-看"和 SAA 16 个节点的阶段裁决本就不同构,强求对齐是错的。统一轨迹的目标是:同一张表,一个公共核心子集(traceId、step、cost、verdict、timestamp),各轨把独有的放进一个 JSON 扩展列(SAA 放阶段、修复轮次、门裁,tier2 放推理、动作、观察)。它要落成一份真契约,放 `contracts/trace/`,作为契约组的新一类 additive 立位(这一位当前还没建,随 spike 或控制面 phase-1 落地时再新立、别当现成),内容含字段定义、schema 版本、脱敏规则、两条线 adapter 怎么映射,以及一条要选边的策略 —— 轨迹写不进去时阻塞生成还是 best-effort 丢弃告警(默认 best-effort 不阻塞主流程,但计入告警)。
这份 trace 契约是数据口径,不是采集机制;采集机制两条线各按自己的框架来。tier2 这条线尤其不必自己埋点,因为 AgentScope 2.0.2 本身就有一套完整的 typed Event System:Agent 每跑一步都吐出强类型事件流 —— `TextBlock*`(文本)、`ThinkingBlock*`(思考)、`ToolCall*`(工具调用)、`ToolResult*`(工具结果)、`ModelCallStart/End`(模型调用,带 token 用量)、`ReplyStart/End`(一轮回复边界)等,源码逐条核过(`agentscope/event/_event.py`)。这条事件流经官方 `TracingMiddleware` 转成 OpenTelemetry 的 span(`agentscope/middleware/_tracing/_trace.py` 直接调真 OTel SDK),最终汇进 AgentScope Studio 可视化。也就是说,tier2 写轨迹的真实接法是:订阅 Event System、用 TracingMiddleware→OTel 这条官方管道,再加一层 adapter 把这些事件按公共核心子集+扩展段映射进 `contracts/trace/` 那张统一表。SAA 那条 Java 线则按它自己的节点裁决埋点、走同一份契约的 adapter。两条线机制各异、契约一致,正是"接口对称、内容不对称"在可观测面的落点。
配置审计日志记的是谁、什么时候、改了哪条配置、为什么。这条线现在完全没有,是新建的。它的最小数据模型至少含:谁改的、从哪改的(UI 还是直接改 Git / DB)、改了什么(前后 diff)、配置版本号、谁批的、何时生效、回滚到哪个版本、影响了哪些生成任务。这里有一个要选边、不能既要又要的决策:UI 改配置,直接写运行时(DB 直写)还是建 Git PR(GitOps)?
```mermaid
flowchart TD
C[UI 改配置] --> Q{哪类配置?}
Q -->|prompt · models<br/>版本化资产| GO[GitOps 建 PR<br/>审计天然 · 可回滚 · 有构建延迟]
Q -->|运营开关<br/>降级 · 配额数值| DB[DB 直写 infra_config<br/>即时生效 · 审计自补]
```
分类处理:prompt、models 这类版本化资产走 GitOps(它们本就在 `contracts/` 里、线A的 CI 也以 Git 为源),运营开关类(降级、配额数值)走 DB 直写、复用现有 `infra_config` 通路(它们本就该即时生效、且有现成通路)。
把两条线钉一起的价值是:任何一次生成都能反查"它当时用的哪几条配置的哪个版本",才能回答"那批游戏质量掉了,是不是上周改的那条 prompt 干的"。存储复用已部署的 MySQL 加对象存储,不新起重型可观测中间件(链路追踪那套是 future-state,等真有第一个消费者再上)。
### D12 运行治理门:入口前的保护门,已有件复用
D12 是已经存在的件,本设计只复用、不改。它把配额、并发限制、背压保护、降级开关、记账骨架,焊在生成任务入口前 —— 代码里 SAA 的提交和重试两条路都过它,默认关闭、零行为变更进主干,开闸前创始人显式打开。它有两点边界:现在只覆盖 SAA,tier2 接进来的入口、预算扣减、并发释放、失败补偿还要补;它的记账是骨架不是真计费(只是门结构加配置键,不是成本核算)。
这接上引擎设计里那道"花到上限就拒绝执行"的预算闸 —— 它现在不是现成能力。要先讲清官方给了什么、缺什么:AgentScope 2.0.2 自带 `ReplyBudgetControlMiddleware`,但它只做软刹 —— 累计加权 token 到阈值后,往 Agent 上下文注入一句提示、并把下一步的 `tool_choice` 强制成 `none` 让它收尾(源码 `agentscope/middleware/_budget.py` 核过),它只数 token、不换算金额、更不会硬拒。这对失控成本不够:tier2 自治多轮 ReAct 的成本风险是真的(一次失控循环能烧掉数美元),软刹只是"提醒它别再调工具",刹不住一个已经在烧钱的循环。
所以硬熔断(fail-closed)和 ¥ 金额台账都得自建,而且自建有现成载体 —— 同样是 Middleware。AgentScope 的中间件洋葱能在 `on_model_call` 这类钩子上拦截,自建一个预算 Middleware:它订阅 `ModelCallEndEvent`(源码已确认这事件带每次模型调用的 token 用量),把 token 乘上 new-api 的单价换算成 ¥ 累进台账,一旦越过硬上限就直接抛错 fail-closed、终止本次生成,而不是像官方那样只注入提示。要落三层强制:worker 本地的 token / 成本预测先闸、网关配额二闸(D12 那道)、任务 deadline 三闸,并定义取价不可达时的策略(直接失败,还是降级到纯 token 上限)。这套强制预算建成前,tier2 不规模化跑、只在严格时间盒的实验里跑。
### 管理面 UI:注册表与观测仓的视图与编辑器
管理面 UI 是注册表和观测仓的视图与编辑器,不是真相本身。这是创始人最在意的那半边 —— "Dify 式可视化 agent 平台管理":改 prompt / 模型 / skill / tool / mcp 不发版、可视化、可追踪、可审计、不要死代码。
一个设计选择先定下来:从零造一个 Dify 式可视化建图器,还是让配置成为真相、UI 只做配置加轨迹的视图与编辑器?选后者,三个理由:前期平台调研已拍板"维持现有 SAA / AgentScope、不迁可视化平台";裸图的节点是 Java 代码、ReAct 的工具也是代码,真相在代码和配置里,靠 UI 拖拽生成代码是另一套不可靠的范式;还有那条别过度工程的纪律。但"配置是真相、UI 是视图"不等于"第一期什么都不做、把可视化全推远期"。管理面按三档落,而且诚实命名:
```mermaid
flowchart LR
P1[phase-1 配置管理 + 运行可观测<br/>只读看得见 · 配置编辑落 Git] --> P2[phase-2 限定范围图编辑<br/>节点启停 · 参数 · 版本 diff · 回放]
P2 --> P3[phase-3 完整可视化建图<br/>拖拽改拓扑 —— 远期不投]
```
phase-1 叫"配置管理加运行可观测",不叫"可视化编排"(这期还没有真的图编辑,叫编排名不副实)。它先含一个不依赖任何前置、纯只读的"看得见"切片:读现有轨迹和模型配置,呈现三件事 —— 看各角色当前用什么模型、回放任意一次生成的全轨迹、按 new-api 的 quota 口径对账成本。这件事现在就能做,不改配置、不画图、不依赖线A任何一步,是立刻能给创始人看见的最小兑现。其上再加配置编辑:在 UI 改 prompt 文本、换模型、调阈值,改的是注册表条目,改完落 Git 加审计日志,不是直接热生效。
phase-2 叫"配置编辑 UI 完整化加限定范围的图编辑"。把图编辑排进来:节点启停、边开关、节点的模型 / 工具绑定、配置版本 diff、按轨迹回放。"限定范围"指它编辑的是节点参数和启停,渲染的拓扑来自运行时自报,不是让用户拖拽新增节点改结构。
phase-3 叫"完整可视化建图",是远期。拖拽改拓扑、可视化编 agent flow、跨编排复用,要等真有多编排、多 agent 团队的需求,而且"拓扑配置化"那个深坑(运行时重建图)被填了才上。现在不投。
一条贯穿管理面的约束:管理面对拓扑只渲染框架运行时自报的结构(数据源是运行时轨迹实际走到哪个节点),绝不在管理面这侧另持一份拓扑模型。因为 SAA 是静态 StateGraph、tier2 的 ReAct 没有静态图,两套异构范式要用同一个管理面呈现,只能靠"渲染运行时自报"这个共同口径;将来换框架时,管理面里硬编码的拓扑模型反而会成为阻力。这也对应配置注册表那处推迟:phase-1 只把节点参数外置(用哪个 prompt、哪个模型、什么阈值),拓扑结构本身的配置化推迟 —— 把结构变成运行时配置就是运行时重建图,那是真正的平台工程深坑。
## 关于 workflow 编排:tier2 不需要,但登记一条未来需求
这里要先澄清一个容易混的概念。AgentScope 2.0.2 里没有显式的 workflow DAG 或编排原语 —— tier2 的运行范式是自治 loop(怎么走)+ task/goal 清单(`AgentState.tasks_context`,走向哪、走到哪)+ Agent Team 动态调度(谁来走),没有一张预先画死的工作流图。所以前面管理面 phase-3 那个"完整可视化建图、拖拽改拓扑"指的是给我们自己运维用的、对运行时拓扑的可视化,不是给用户编 workflow 的产品能力,两者别混。
在此登记一条**未来产品需求**(2026-06-22 记):未来要给用户 / 创作者提供 workflow 编排能力(可视化地编排生成场景)。这是一个面向 C 端创作者的产品场景,跟控制面/管理面这层平台治理是两回事。明确一点:**当前 tier2 自身不需要 workflow** —— 自治 agent 范式靠 loop + task/goal + Team 调度就够了,不靠预定义 DAG。这条只作登记、占位,具体形态以后单独讨论,本设计不展开、不投入。
## 接口对称,内容不对称
控制面和两条生成线的接法,是接口层对称、内容层不对称 —— 这才是"一份管理面管两条异构线"能成立的条件。读配置:两条线的节点和 agent 都按"id 加版本"从注册表加载,接口一样,但 SAA 读 16 个节点的参数、tier2 读 ReAct 的工具集和轮数预算,内容不同。写轨迹:都按统一契约写,公共核心子集对称,扩展段各写各的。过治理门:都先过 D12,但 D12 现在只覆盖 SAA、tier2 要补。配置安全门尤其要分清:tier2 的 eval / 灰度门是 tier2 实验的产物、现在不存在,所以"配置改动要过 eval 门"对 tier2 现在是空头支票 —— 在 tier2 自己的 eval 门建成前,tier2 的配置改动只有"下次任务生效加轨迹留痕"两层保护,没有 eval 门拦截。这不是设计缺陷,是 tier2 成熟度的现实,但要写明,否则会让人以为 tier2 配置改动有它实际没有的安全网。
本文档的设计内容已并入生成运行时架构单一事实源 [`agentic运行时架构图说.md`](agentic运行时架构图说.md)(§一 反锁死五件、§5.2 控制面与配置热取、§5.7 观测与成本)。请以该 SoT 为准;本文件原始内容见 git 历史。

View File

@ -1,468 +1,9 @@
# tier2 四层工程架构
> 🚧 评审版 · **已纳入 AGENTS.md §6.8 Codex+Opus 双评审发现并修订**;**并据《[agentic 运行时架构图说](agentic运行时架构图说.md)》(基于 agentscope 2.0.2 源码实证)做了一次架构校正(2026-06-22),用现行真相 supersede 了若干旧表述,见各节内说明**;残留跨档项见 §11。基于《[tier2 实现详设](tier2实现详设.md)》,尚未落代码;过了 0号 spike 才进建设。
> **这是什么**:tier2 自治富游戏轨作为一个**独立 AgentScope 模块**的内部工程架构 —— 拆成 **environment / harness / context / prompt** 四层,逐层说清职责、边界、复用 AgentScope 的哪些能力、**哪些是硬代码、哪些可配置**、是复用/启用/新建。
> **重要校正(2026-06-22)**:这四层**不是 AgentScope 的真实对象结构,而是一个"职责视角"** —— 它叠在 AgentScope 2.0.2 的真实结构(Agent / Workspace / Toolkit / Middleware / AgentState / Agent Service)之上。读本档时请始终带着这层对应关系:environment 这个职责对应的真实载体是 **Workspace**(它沿两轴注入 Agent,Agent 持有它的引用、并不嵌在它里面);harness 这个职责对应的是 **Agent 的 ReAct 循环原语 + Middleware 洋葱 + 自建验收门**;context / prompt 则是组装进模型窗口的运行时载荷与离线指令。术语逐项映射见图说 §6。
> **给谁看**:要搭这个独立模块的工程师;拿它去 ce-plan 排执行的人;评审四层切分、硬/软边界、复用契约是否成立的人。
> **与邻档关系**:《[自治富游戏引擎](自治富游戏引擎.md)》给内核范式(O2 约束自治、Phaser/Pixi 选型、效果验收),《[tier2 实现详设](tier2实现详设.md)》给源项目契约+0号 spike runbook+建设五步+退路树,《[agentic 集成架构](agentic集成架构.md)》给两线共用的控制/管理面,《[agentic 运行时架构图说](agentic运行时架构图说.md)》给整条生命周期的看图入口 + agentscope 2.0.2 源码实证的术语映射。**本档只回答:这个独立模块内部怎么按四层职责把地基搭起来** —— 是建设五步第一、二步(底座 + 配置外置)的工程落点,不重排那几份。
---
status: 已退役(2026-06-24 收敛进 SoT)
canonical: false
date: 2026-06-24
---
## 1. 三个定调:独立模块 + 复用契约 + 四层职责视角
# (已退役)tier2 四层工程架构
### 1.1 独立模块,SAA 可删,独立锁定 AgentScope 2.0.2
tier2 是一个**独立的 AgentScope 模块**:自己的运行环境、依赖、工具、演进节奏,对现有 SAA 廉价线(game-cloud 里 Java 写的 16 节点 StateGraph)**运行期零耦合**。这把《自治富游戏引擎》那条最硬边界("tier2 任何改动不许碰、不许回归现有廉价线")推到逻辑终点。
因为是独立 env、独立锁版,tier2 **把 AgentScope 版本钉死在 2.0.2**,与 SAA 线不强制同版,规避破坏式升级的版本耦合(2.0 是对 1.x 的破坏式重写)。本档后文一切 AgentScope API 都以 **2.0.2 源码实证**为准(`/root/oss/agentscope`,逐条核过):**没有独立的 `ReActAgent` 类**——统一是 `Agent` + `ReActConfig`(`max_iters` 默认 20);拦截扩展点是 **`MiddlewareBase` 洋葱**(6 个 hook:`on_reasoning` / `on_reply` / `on_acting` / `on_model_call` / `on_compress_context` / `on_system_prompt`),**不是 agent hooks**;状态载体是 **pydantic `AgentState`**(含 `cur_iter` / `context` / `tasks_context`),**不是 1.x 的 `state_dict` / Session**;**`agentscope.init(studio_url=…)` 不存在**——可观测走纯 OTel(`TracingMiddleware`)。⚠️ 这几条 supersede 了本档早稿里"2.0.1"、"`ReActAgent`"、"`state_dict` checkpoint"、"`Studio` trace / `agentscope.init`"的旧假设。
**对外暴露方式 = service 化、用官方 Agent Service,不自造 API**(2026-06-22 校正):tier2 是一个独立 service,game-cloud 经它调用;但它**不自己手搓 REST/会话层**,而是直接用 AgentScope 官方的 **Agent Service** —— `create_app` 起 FastAPI,自带 **REST + SSE**(流式 event,断线可 replay)、多租户、durable 会话。会话初始化有三条加载入口:① 续接之前会话(取历史 resume)② 加载已有游戏工程(`Workspace.workdir` 指向已有目录 → 迭代)③ 新建(阶段 2 模板初始化工具铺模板)。这条与"Agent 非常驻"一致:Service 收到一次 run 请求才现组装 Agent,状态落 `AgentState`。
要钉死一处口径(双评审 M1/M2 指出本档曾与《详设》冲突):tier2 **以现有 `wg1/gen-worker/worker/agent_loop/studio.py` 为种子,一次性拷贝/借鉴其已验证的 AgentScope 用法,然后独立演进、运行期零依赖、不回写**。这既吃到"躯干七成已在"的复用红利(《详设》语),又不破"独立模块、SAA 可删"——**它是 fork-为-起点,不是 in-place 演进共享的 studio.py**。《详设》第一步"演进 studio.py"的措辞需据此对齐(§11 跨档 TODO)。
### 1.2 复用契约:二维(来源 × 共享)+ 四类获取边界
双评审最重的一组发现(Opus B1/M2/M4、Codex M1/M6)是:本档曾把"上游库有某 API"或"框架理论提供某能力"笼统写成"复用",夸大了地基的现成度。**2026-06-22 校正进一步把复用判断收敛成一张二维坐标**(supersede 早稿那种把"复用/边界"扁平列举的画法):
- **维度一·来源**:官方现成(AgentScope 2.0.2 自带) vs 自建(tier2 或两线团队自己写)。
- **维度二·共享**:两线公共(SAA 廉价线与 tier2 都用) vs tier2 私有。
由这两维交叉出关键结论:**"公共组件 = 自建 ∩ 共享" —— 它们是团队自建的、被两线共用的件(九门 / 三层校验 / CDP / 计费 / 送审 / feed / trace),不是现成的官方能力**。别把"框架里有"误当成"公共组件"。完整二维全景见图说图 6,本节只点出对四层地基影响最大的一条校正:
**6 项早稿打算自建、现改用 AgentScope 官方现成**(这是本次校正对地基现成度的最大上修):
| 能力 | 早稿口径 | 校正后用官方 |
|---|---|---|
| token 计量 | 拟自建台账 | `ChatUsage`(model response 自带) |
| 软预算控制 | 拟自建预算 middleware | `ReplyBudgetControlMiddleware`(官方) |
| 结构化输出 | 拟自建 schema 校验 | `generate_structured_output`(model 层) |
| 可观测 trace | 早稿写 `Studio` trace | `TracingMiddleware`(纯 OTel) |
| 经验召回 | 拟自建案例库 | `ReMe`(经验/记忆框架) |
| 多 agent 总线 | 拟自建协调 | `Agent Team`(`TeamCreate`/`AgentCreate`/`TeamSay`) |
> ⚠️ 一处实证差异、需创始人确认:在 `/root/oss/agentscope` 2.0.2 源码里,**树内捆绑的长期记忆后端是 `mem0` middleware**;"经验召回 `ReMe`"是 AgentScope 生态里独立的记忆框架,本档沿用图说(SoT)的 `ReMe` 口径登记。落地选 `ReMe` 还是树内 `mem0`,留 spike 前小验证 + 创始人定。
**官方"没有"的(别误指望):无 RAG / 向量检索的现成检索管线(只有 `embedding/` 模型封装,不含 retriever);无 `evaluate` 评测模块;A2A 仅有基座、非成品。** 这三项要么自建、要么按需引第三方。
复用的**获取方式**仍分四类(这是"怎么拿到手"的工程口径,与上面二维"是谁的/给谁用"正交):
```mermaid
flowchart TB
subgraph A["① 外部包复用(仓内已在跑 · 显式参数)"]
A1["cost.py 计费 · _client.py new-api 客户端"]
end
subgraph B["② 一次性种子拷贝(运行期零依赖)"]
B1["studio.py 的 AgentScope 用法 → 拷贝起步、独立演进、不回写"]
end
subgraph C["③ tier2 自有依赖(独立 env · 锁 2.0.2)"]
C1["agentscope core 2.0.2(Agent/ReActConfig/AgentState/Middleware/Workspace/skills)"]
C2["agentscope-runtime 沙箱 = 待验证候选(见 §3 · 当前全仓零引用)"]
end
subgraph D["④ 运行期禁止依赖(forbidden-import 守门)"]
D1["SAA 配置 · 现有 studio.py 运行态 · L2 模型路由/预算表"]
end
style C2 stroke-dasharray: 5 5
style D stroke:#f5b7b1
```
- **① 外部包复用**:`cost.py`、`_client.py` 是稳定 IO 边界,真·仓内现成。但复用要**纯函数/显式传参** —— endpoint / key / model / pricing / quota / retry / timeout 全显式传入,**禁止沿用共享全局状态**(否则会暗中继承 SAA/L2 的模型路由与预算表,"SAA 可删"就成空话)。
- **② 种子拷贝**:`studio.py` 的写法拷来起步,但运行期不 import 现有 worker。
- **③ tier2 自有依赖**:agentscope core 是 tier2 **自己 env 里独立安装、锁 2.0.2** 的(用户定调"独立周边环境")——所以它**不是"与 SAA 共享的复用底座"**。`agentscope-runtime` 是**独立 PyPI 包、当前全仓零引用**,降级为**待验证候选**(§3)。
- **④ 禁止依赖**:加一道 **forbidden-import 检查**(§11 跨档 TODO,CI 侧),从机制上保证运行期不碰 SAA。
> 这条契约直接消解了 v1 的内部不一致("§1.1 说复用三样、§8 却列四样"):**真·外部复用只有 cost.py + _client.py;agentscope 2.0.2 是 tier2 自有依赖;runtime 是候选;studio.py 是种子拷贝;且上表 6 项从"自建"上修为"用官方"。**
### 1.3 四层是"职责视角",叠在 AgentScope 真实对象结构上
诚实说清两层意思,后者是 2026-06-22 的关键校正:
其一,**"environment / harness / context / prompt 四层"不是业界主流原话**。主流(2025-2026)是**三层嵌套**:`prompt ⊂ context ⊂ harness`,environment 通常并进 harness 当"对外数据面"(Deepset:*"Prompt engineering is a subset of context engineering"*;Anthropic context engineering 定义见 §验证状态 来源)。本档把它拆四层,是为了对"单写者 ReAct + 跑确定性门"这种模块讲得更清晰。
其二(**校正 · supersede 早稿"把 harness 劈成数据面 + 控制面、两面互相 act/观测"的画法**):这四层**不是 AgentScope 的真实对象,而是一个职责视角**,叠在 2.0.2 的真实结构之上。早稿那张"harness 全域里 harness 控制面 ↔ environment 数据面互相调用"的图,容易让人误以为 environment 是一个被 harness 调用的并列数据面 —— **这与源码不符**。真实结构是:
- **environment 这个职责的真实载体 = `Workspace`**(Local/Docker/E2B),它是 **Agent 的执行环境**,沿**两轴注入 Agent**:① 把资源(tools / MCP / skills)经 `get_toolkit` 汇成 **`Toolkit`** 交给 Agent;② 它本身作为 **offloader**(上下文/工具结果卸载)挂在 Agent 上。
- 所以 **Agent 持有 Workspace 的引用,Agent 并不嵌在 Workspace 里、Workspace 也不"调用"Agent**。真正"跑在 Workspace 里"的是 MCP 进程 / skills / 文件这些资源。
- harness 这个职责的真实载体 = **Agent 的 ReAct 循环原语(`Agent` + `ReActConfig`)+ `MiddlewareBase` 洋葱 + tier2 自建验收门**;context / prompt 则是每步组装进模型窗口的运行时载荷与离线指令(仍是 `prompt ⊂ context` 的嵌套)。
带这层"职责 → 真实载体"的对应关系读下文,而不是把四层当四个平行筒仓、更不是当互相调用的对等模块:
```mermaid
flowchart TB
AG["Agent(无状态 ReAct 引擎)<br/>= harness 职责的载体"]
subgraph WS["Workspace = environment 职责的载体(Agent 的执行环境)"]
RES["资源:tools / MCP / skills / 文件 / 世界状态 / 观测"]
end
CTX["context 运行时载荷<br/>(含 prompt 离线指令 · prompt ⊂ context)"]
AG -->|"持有引用 · 轴②作 offloader"| WS
WS -->|"轴① get_toolkit → Toolkit"| AG
CTX -->|formatter 组装进窗口| AG
```
> 一句话:**Agent 持有 Workspace 引用、非嵌套;Workspace 经 Toolkit + offloader 两轴接入 Agent;四层只是给这套真实结构分职责看。**
---
## 2. 一张总图:整条生命周期怎么转起来
先框两个全局结构(2026-06-22 校正补入,它们决定了下面这台机器的形状):
- **两阶段**:tier2 不是上来就一个 agent 闷头写。**阶段 1「工作室」用 Agent Team 星形多 agent 做设计**(leader 拆解用户意图、`AgentCreate` 出玩法/关卡/数值/UI/音乐/特效/资产设计 worker 发散,`TeamSay` 汇 leader 收敛;这阶段**只读**——设计 worker 用 `PermissionMode.EXPLORE` 只读已有工程代码 + 工程内设计文档,并与用户**跨 session 多轮对话**)。**阶段 2「单写实现」**才把设计落地:模板代码初始化(阶段 2 的工具)→ 把阶段 1 各设计结论**写成工程内文档** → 单写 agent 写代码 → 三层校验 → 产出。**下面这张"单写者 ReAct 循环"图只刻画阶段 2 的"写代码"那一步**;设计发散在阶段 1、用多 agent。
- **Agent 非常驻**:Agent 是**无状态 ReAct 引擎**,Agent Service **每 run 现组装、跑完即弃**,状态全在可持久化的 `AgentState`(落 Redis)。所以"循环"不是一个常驻进程在转,而是一次 run 的生命周期;长程一致性靠 `AgentState` 持久化 + checkpoint。
阶段 2 的实现内核是一个**单写者 ReAct 循环**:只有一个 agent 持全局视图、一个人写整个工程(经营游戏多系统共享状态,拆并行几乎必然不一致 —— 曾把 Flappy 拆并行,背景跑成马里奥)。每步从 context 组装载荷(含 prompt)喂模型,模型决定动作,动作经 Toolkit 作用到 Workspace(写多文件 `src/` → 构建 → 真跑),Workspace 回吐观测,在**三层校验**上裁决"过 / 修 / 熔断"。**goal/进度的承载**:目标 = 输入(brief / play_spec / GDD),进度 = `AgentState.tasks_context` 把目标拆成 TODO、逐项 `TaskCreate/Update` 追踪(像 todo 清单),**没有 workflow DAG**(自治 loop + task + Agent Team 动态调度,详见图说 §5b)。
```mermaid
flowchart TB
GOAL["目标 = brief / play_spec / GDD<br/>进度 = AgentState.tasks_context(拆 TODO · TaskCreate/Update)"] --> ASM
subgraph H["阶段2 单写者 ReAct 循环(Agent 非常驻 · 每 run 现组装)"]
ASM["组装 context"] --> CALL["调模型 M3 · 经 new-api"]
CALL --> ACT["经 Toolkit 写多文件 src/ → 构建 → 真跑"]
ACT --> OBS["收观测(Workspace ground truth)"]
OBS --> GATE{"三层校验"}
GATE -->|L1 可修| FIX["repair / checkpoint(AgentState→Redis)"] --> ASM
GATE -->|全绿| EMIT["产出富游戏 src/ 工程"]
end
CTX["context:prompt + 源项目视图/GDD + 记忆/错误史/预算"]
WS["Workspace:Filesystem(src/) · 构建 · 真跑+截图"]
ASM -.读.- CTX
ACT -.作用.- WS
WS -.观测.- OBS
GATE --> L1["L1 编译/运行错误 · 必须 · 循环"]
GATE --> L2["L2 设计符合 · 尽量"]
GATE --> L3["L3 效果 · 只评分(M3)· 绝不阻塞"]
BREAK["四熔断:步数硬顶 · 预算闸(fail-closed) · 卡死探测 · 双超时"]
BREAK -. 任一触发即停 .-> H
style L1 fill:#a9dfbf
style L2 fill:#d6eaf8
style L3 fill:#f9e79f
style BREAK fill:#f5b7b1
```
> 一处现状必须诚实(双评审 M1):仓内 `studio.py:159` 是 `ReActConfig(max_iters=1)` —— **当前是单轮、靠外层 repair 循环兜**,不是多轮自治 ReAct。2.0.2 里 `ReActConfig.max_iters` 默认 20,所以"门内任意迭代 + 步数硬顶"这层**是要放开 max_iters + 新建循环控制的,不是复用现成**;复用的只是 `Agent` + `ReActConfig` 这个原语。
---
## 3. environment 层(载体 = Workspace)—— agent 的工作站(候选底座待验)
**校正(2026-06-22)**:environment 这个职责在 AgentScope 2.0.2 里的真实载体是 **`Workspace`** —— 它是 Agent 的执行环境,沿两轴注入 Agent(① 资源经 `get_toolkit` → `Toolkit`;② 本身作 offloader),`WorkspaceBase` 已有 **Local / Docker / E2B** 三实现。所以本层不是凭空"自建一个数据面",而是**在 `Workspace` 这个官方抽象上挂 tier2 要的工具与资源**;Agent 持有它的引用、并不嵌在里面。下面讨论的"给 agent 一台游戏工作站"(写多文件源码、构建、真浏览器跑、截图、查资产与文档),落地就是配 / 扩 `Workspace` 及其 Toolkit。
`Workspace` 自带的三实现解决"在哪跑、文件/shell 隔离"的问题,但**确定性硬门要的"真浏览器低层探针"是否够用,仍是承重前提、需先验**(双评审 B1/M7):一个候选是引 `agentscope-runtime` 的 `SandboxService`(CODE/Filesystem/Browser 三沙箱 + `sandbox_tool_adapter`)补浏览器探针;但 ——`agentscope-runtime` **当前全仓零引用、是独立 PyPI 包**,且 `requirements-l2.txt` 注释明写"本期只引 base、不引 service/storage"。所以它是**新增的待验证候选依赖,不是现成复用底座**;"上游有这个 API"(Context7 可查)**不等于**"装得进 tier2、挂得成 Toolkit、跑得了 esbuild + headless、且撑得住确定性探针"。
确定性硬门要的是**低层探针**,而 Browser 沙箱给的是高层 API,够不够要先验:
| 硬门探针 | 需要的浏览器能力 | navigate/screenshot/snapshot 够吗 |
|---|---|---|
| 真跑 + 截图(boot / 视觉软检喂图) | navigate / screenshot / snapshot | ✔ 够 |
| 帧 delta(`window.__engine.snapshot().frame` 类) | `page.evaluate` / raw CDP | ✘ 需 evaluate |
| canvas 像素回读(亮像素阈值) | screenshot + `page.evaluate` | △ 截图有、阈值判定需 evaluate |
| activity hash 比对 | `page.evaluate` | ✘ |
| 输入改状态(注入输入再读态) | input dispatch / raw CDP | ✘ 需低层注入 |
**结论 + 退路**:Browser 沙箱够"跑+看",不够"低层确定性探针"。**spike 前先跑一个小验证**(§10 升格为 spike 前置阻断项):结论为"BrowserSandbox 暴露足够 `page.evaluate`/CDP"→ 复用它;否则 **environment 回落到"自建薄沙箱 + 复用 `play.cdp.cjs` 的运行/截图/证据骨架(但其探针硬编码 `#game-engine`、`window.__engine.snapshot().frame`,是 LittleJS 专属、**非引擎无关** —— selector/frame source 必须为 Phaser 参数化或重写,不是"直接复用")"**。这条退路对"薄做地基"的工作量影响要计入排期。
```mermaid
flowchart LR
AG["单写者 Agent(持 Workspace 引用)"] -->|工具调用| TK["Toolkit(get_toolkit 汇成)"]
subgraph WS["Workspace(Local/Docker/E2B · environment 载体)"]
FS["Filesystem:多文件 src/ 读写"]
CODE["CODE:esbuild 构建 / shell"]
BR["Browser:真跑 + 截图(低层探针待验)"]
PACK["引擎能力包工具(见 §6.1 manifest)"]
end
TK --- FS & CODE & BR & PACK
FS & CODE & BR -->|观测 ground truth| AG
NOTE["浏览器探针底座二选一(spike 前验):<br/>agentscope-runtime 沙箱 ┃ 自建薄沙箱+现有 CDP"] -.- WS
style BR stroke-dasharray: 5 5
```
| environment | 内容 |
|---|---|
| **硬代码(机制)** | 沙箱接线、工具适配器、动作/观测空间的**结构**(枚举级);**权限硬上限**(配置只可降不可升) |
| **版本化安全/构建配置**(审计 · 不热改) | 依赖锁版本、引擎版本、构建 profile、启用哪些沙箱类型 —— 这些是**可信边界 + 可复现构建输入**,走版本化审计,不像阈值那样热改(双评审 M5) |
| **热配置(改即生效)** | 资源软配额等可热调项 |
> 引擎选型(Phaser/Pixi)**两态**(双评审 M3):**第一版 = Phaser 硬编码进工具(硬代码,换引擎需改适配器、发版)**;目标态 = 能力包接口抽出后才可配。第一版**薄做**,等第二个引擎(Pixi)落地有两个实现再抽接口(承《自治富游戏引擎》)。
---
## 4. harness 层 —— 控制面与三层校验(本模块重心)
harness 是把模型变成 agent 的循环与脚手架:控制流、工具分发、校验门、熔断、checkpoint、可观测。tier2 的 harness **启用 AgentScope 2.0.2 的循环原语**(`Agent` + `ReActConfig`),拦截 / 预算 / 可观测**尽量挂在官方 `MiddlewareBase` 洋葱与官方 middleware 上**,**自己重写的只是验收门**。
> 用词分级与 API 校正(双评审 M2 + 2026-06-22):checkpoint、记忆、skills、trace 这些**仓内代码一个都没用过**,是 agentscope 2.0.2 **框架提供、tier2 首次接通、行为待验**(2.0 是对 1.x 破坏式重写),本档一律标"**启用(待验)**",区别于 cost.py/_client.py 的"复用(已在跑)"。同时把早稿的 API 假设按 2.0.2 源码纠正:拦截扩展点是 **`MiddlewareBase` 洋葱(6 hook)**、不是 agent hooks;checkpoint 状态是 **pydantic `AgentState`**(`cur_iter`/`context`/`tasks_context`)落 StorageBase(Redis)、**不是 `state_dict`**;可观测是 **`TracingMiddleware`(纯 OTel)**、**不是 `Studio` trace / `agentscope.init(studio_url)`(后者不存在)**。
### 4.1 三层校验:L1 硬约束 + L2 设计符合 + L3 效果软检(创始人定义 · M3 绝不阻塞)
**校正(2026-06-22)**:早稿的"双层验收(确定性硬门 + M3 视觉软检)"**升级为创始人定义的三层校验** —— 在原"硬门 / 软检"之间补出一层"设计符合":
- **L1 硬约束**:编译 / 启动 / 运行错误日志。**必须解决 · 循环**;工具是确定性的(构建日志 / console / CDP 错误捕获)。**现有九门多数落这一层**,少数(机制进展类)落 L2。
- **L2 设计符合**:玩法 / 关卡实现 vs 设计、UI 缺组件等。**尽量解决**;靠设计符合度校验。
- **L3 效果**:特效 / 美观 / 好不好玩。**只评分、不解决**;由 M3 多模态软检,**绝不阻塞、绝不拒发**。
现有九门(《详设》更精确叫"九门 + 三联动门 + 经济门 + latch")只判机制、且为 LittleJS 单文件壳而建、不适配 Phaser、还带病灶。tier2 把它**抽象重写**进 L1/L2(本档新增联动/经济/类校验),三层职责绝不混:
```mermaid
flowchart TB
PLAY["真跑产物 + 截图(Workspace 观测)"] --> L1
PLAY --> L2
PLAY --> L3
subgraph L1["L1 硬约束 · 必须解决·循环 · 零 LLM 判定 · 定 pass/fail"]
G1["引擎无关重写(多数九门):能装载 / 不报错 / 帧 delta /<br/>像素回读 / 活动 hash / 引擎调用前缀 / 输入改状态 / 控制跟手"]
G2["tier2 富游戏专属:跨表联动门(订单引物品可达·合成 DAG 无环)+ 经济门(可盈利可破产)"]
G3["修旧病灶:latch 分『胜利/弃守』(空壳不再蹭过)"]
end
subgraph L2["L2 设计符合 · 尽量解决 · 确定性信号为准"]
S1["机制进展类九门 + 玩法/关卡实现 vs 设计 + UI 缺组件"]
G4["品类独立交叉校验:用『题面关键词(确定性)』比对 design 自报品类,<br/>不一致→降权/拒发(拒发权只来自确定性信号);<br/>同其他新增门:默认 observe→enforce,达标后才赋拒发权"]
end
subgraph L3["L3 效果 · 只评分不解决 · M3 绝不阻塞/拒发"]
V1["M3 多模态看截图:好不好看 / 像不像题面 / 明显劣化(空内容·布局崩)"]
V2["只进:质量趋势 · 告警 · 人工终审升级。绝不参与『算不算完成』、绝不单独拒发(防 Goodhart)"]
end
L1 -->|全绿| PASSED["机制地板通过(可发)"]
L2 -->|确定性信号| GATE2["降权 / 拒发(observe→enforce)"]
L3 -->|软信号| TREND["趋势 / 告警 / 给人工终审减负"]
style L1 fill:#a9dfbf
style L2 fill:#d6eaf8
style L3 fill:#f9e79f
```
三条铁律,承创始人定义与《自治富游戏引擎》/生成引擎设计主文档:
- **确定性的归机器,且绝不让 LLM 给自己打分。** L1/L2 的拒发权全来自确定性信号,是"机制地板",零理由动它的判定权。
- **L3(M3 视觉)严格只评分、绝不阻塞(双评审 B1·Codex + 创始人定义)。** v1 曾写"从 M3 视觉反推品类…不一致即拒发",这等于把 VLM 提成硬门,违反 Goodhart 红线。**已改正**:M3 只产观测 / 告警 / HITL 升级,**绝不单独拒发、绝不阻塞真问题**;G4 的品类交叉校验以"题面关键词(确定性)"为拒发依据,M3 至多作软佐证喂趋势。"出题与被考解耦"由这个确定性交叉校验达成,不靠 M3。
- **迭代原则:初期只识别硬问题(L1),L2/L3 渐进;绝不让效果问题阻塞真问题;用户可经 HITL 要求继续解决任意层。**
### 4.2 循环、熔断、checkpoint、预算闸语义
单写者 agent 在门内迭代,被**四道熔断**关着、任一先触发即停。其中**预算闸要硬、要定义失败语义**(双评审 M8):
```mermaid
flowchart LR
subgraph BRK["四熔断"]
K1["步数硬顶(每系统构建-修复)"]
K2["预算闸(见下,fail-closed)"]
K3["卡死探测(语义层)"]
K4["双层超时"]
end
subgraph BUD["预算闸语义(承 agentic 集成架构 · 现 cost.py 仅 best-effort 取价)"]
P1["软层:官方 ReplyBudgetControlMiddleware(token 软预算)"]
P2["硬层:每轮 preflight 预测 + per-call 扣减 + deadline + fail-closed"]
P3["取价/扣账/quota 写失败 → 默认 fail-closed;纯 token 上限=显式硬兜底状态(进 trace),非二选一"]
end
K2 --> BUD
```
注意分两层:**软预算用官方 `ReplyBudgetControlMiddleware`**(token 计量靠 model response 自带的 `ChatUsage`),挂在 `MiddlewareBase` 洋葱上即可,这是 2026-06-22 校正"6 项改用官方"之一。但 **`cost.py` 是 best-effort 取价、取不到也不阻断**,只是观测台账口径,**不等于网关层强制拒绝**;tier2 自治多轮 ReAct 的成本风险是真的(一次失控循环能烧数美元),所以**强制 fail-closed 预算闸仍要自建**,且与《agentic 集成架构》的 D12/tier2 预算治理对齐(§11 跨档 TODO)。长程一致性靠 **`AgentState` checkpoint(pydantic·落 Redis·启用待验)** + 半轮副作用幂等。可观测用 **`TracingMiddleware`(纯 OTel)→ Studio 看板**(启用待验);统一 trace 契约推迟到 spike 或控制面 phase-1。
---
## 5. context 层 —— 运行时载荷
context 是某步推理在窗口里组装的一切经策展的动态信息(prompt 是其子集)。它在 2.0.2 真实结构里就是 `AgentState.context`(工作记忆)+ 每步经 `formatter` 逐模型组装进窗口的载荷。tier2 这层**启用 AgentScope 的记忆/组装能力(待验)**:短期工作记忆(`AgentState.context`)、长期**经验召回用官方 `ReMe`**(2026-06-22 校正:经验/debug 案例库从"自建"改用官方记忆框架;⚠️树内捆绑后端实为 `mem0`,选型见 §1.2 注 + spike 前定)、`formatter` 逐模型组装;结构化输出走官方 `generate_structured_output`(model 层),不自建 schema 校验。工程上要设计的是**装什么、何时压**:
```mermaid
flowchart LR
subgraph CTX["context · 每步组装的载荷"]
SYS["system prompt(来自 prompt 层)"]
TOOLS["工具/skill schema"]
SRC["源项目视图(当前相关 src/)"]
GDD["GDD(玩法意图/机制/胜负)"]
HIST["动作-观测史 / 错误轨迹"]
BUDGET["预算与成本状态"]
RAGIN["能力包检索结果注入(见 §6.1)"]
end
MEM["长期记忆:debug 案例库(签名→修复)+ template 家族"] -->|按需检索| CTX
CTX -->|formatter 组装| MODEL["模型窗口"]
CTX -.超窗.-> COMP["压缩/摘要(保关键状态)"] --> CTX
```
边界 vs prompt:**prompt 是离线写好的静态指令,context 是运行时组装的载荷**。这层防的是 context rot 与溢出截断丢关键状态 —— 12+ 文件富游戏尤其靠压缩 + checkpoint 守长程一致性。debug 案例库(签名匹配纯算法、命中即省一次 LLM)挂长期记忆上,是对便宜模型最划算的杠杆。
| context | 内容 |
|---|---|
| **硬代码(机制)** | 记忆/组装/压缩机制;可入上下文的内容**类型(枚举级)** |
| **热配置(策略)** | 上下文预算分配、压缩触发阈值、启用哪些 RAG 源(枚举内取值)、长期记忆模式、检索参数 |
> 颗粒度澄清(双评审 M3):**新增/删除一类内容 = 改枚举 = 硬代码**;**在已有类里增减取值(如多挂一个 RAG 源)= 可配置**。
---
## 6. prompt 层 —— 离线编写的指令(⊂ context)
prompt 是塑造模型行为的**编写指令文本**:静态、离线写、版本化。tier2 这层**启用 AgentScope 2.0.2 的 `sys_prompt` 增广、`formatter`、以及原生 skills 机制(均待验)**。校正(2026-06-22):2.0.2 的 skills **不是 agent 级 `register_agent_skill` 注册**(早稿 API 名有误,源码无此函数),而是**挂在 `Workspace` 上的资源** —— `WorkspaceBase.add_skill(skill_path)` / `list_skills()` 加载带 `SKILL.md`(frontmatter 含 name)的技能目录,再经 `get_toolkit` 注入 Agent。也就是说 prompt 层的"skill"这一面,落地是配 Workspace 的 skill 目录,与 §3 environment 同源(都在 Workspace 上),正好对应 §6.1 的"一个 manifest、三层各持一面"。
### 6.1 引擎能力包:一个 manifest,三层各持一面(消除跨层泄漏)
双评审 M3(Codex)指出:同一个"引擎能力包"在 environment(工具/RAG)、context(RAG/记忆)、prompt(skills)三层都出现,**没有唯一 owner**,会形成三份配置源、引擎切换/回滚/trace 归因无主。改正 —— 立一个**轻量能力包 manifest(id + version)作单一 owner**,三层各持其一面、共同引用同一 id/version:
```mermaid
flowchart TB
MANI["引擎能力包 manifest<br/>capability-pack id + version(单一 owner)"]
MANI --> EF["environment 面:可执行工具(Phaser 写/构建)"]
MANI --> CF["context 面:检索结果(Phaser API RAG → 注入)"]
MANI --> PF["prompt 面:文本 skill(SKILL.md:Phaser-API / debug / template)"]
style MANI fill:#fde68a
```
**v1 范围(防孤儿抽象)**:第一版 manifest 只做 **Phaser facet 归因 + 版本锁定**,三面仍按 §3 的 Phaser 硬编码(不由 manifest 驱动接线);**"换引擎/回滚 = 换 id/version" 是目标态,待第二引擎(Pixi)抽出接口后才配置化** —— 避免"一上来就建满、接口被唯一实现反向决定"(承《自治富游戏引擎》)。trace 始终按 id/version 归因。
### 6.2 角色与铁律(分两阶段)
tier2 的"角色"要按 §2 的**两阶段**分开说(2026-06-22 校正,补出阶段 1 工作室):
- **阶段 1「工作室设计」用 Agent Team 星形多 agent**:leader 拆解用户意图、`AgentCreate` 出玩法 / 关卡 / 数值 / UI / 音乐 / 特效 / 资产设计 worker 发散,`TeamSay` 汇 leader 收敛成设计结论。**这阶段只读** —— 设计 worker 用 `PermissionMode.EXPLORE` 只读已有工程代码 + 工程内设计文档(迭代已有游戏时),并与用户**跨 session 多轮对话**。用多 agent 是为了发散与专业分工。
- **阶段 2「单写实现」用单写者**(双评审 m2):一个写者 agent 持全局、独自写(防并行写冲突);旁边配**只读探路 agent + 一道独立评审 + M3 player(L3 效果)**。**单写约束只管"写代码"这一步**,不约束阶段 1 的设计发散。模型路由是 **per-agent**(leader/各设计 worker/写者/探路/评审/player 各自可配),**不是**把现有 studio.py 的 design/code/fix 三角色照搬进来。
```mermaid
flowchart TB
subgraph S1["阶段1 工作室(Agent Team 星形 · 只读 EXPLORE · 跨 session 对话)"]
LEAD["leader:拆解意图 + 收敛"] -->|AgentCreate| W["玩法/关卡/数值/UI/音乐/特效/资产 设计 worker"]
W -->|TeamSay| LEAD
end
subgraph PROMPT["阶段2 prompt 层(几乎全是可配置数据)"]
ROLE["角色 prompt:单写者 + 只读探路 + 独立评审 + M3 player"]
LAW["三铁律:数值只进配置 · 只用清单已有行为 · 绝不编造清单外 hook"]
OUT["输出契约:终态必须是 src/ 多文件工程(非 iife 串)"]
end
S1 -->|"设计结论写成工程内文档"| PROMPT
SKILLS["skills(SKILL.md · 挂 Workspace)= 能力包 prompt 面(§6.1)"] -->|增广进| ROLE
ROLE --> SYS["sys_prompt → context"]
```
prompt/skill 走 **tier2 自己的版本治理**,**不并入** SAA 的 prompt-同源工程。三铁律里"绝不编造清单外 hook"能约束便宜模型,前提是有那份 Phaser API 清单当填空靶子。
| prompt | 内容 |
|---|---|
| **硬代码(机制)** | `formatter` 机制、skill 加载机制、输出 schema 校验逻辑 |
| **热配置(策略)** | 全部 prompt 文本、per-agent 模型路由、few-shot、agent skills 内容、prompt 版本 —— 这层几乎全是数据 |
---
## 7. 硬代码 vs 可配置:三类门 + 三类配置
这是本模块的组织原则,继承生成引擎设计主文档对旧债的诊断("策略被焊进不该重编译的机制代码")。双评审(Opus M3、Codex M4/M5)指出 v1 这张表有误分类,**修正为三类门 + 三类配置**:
**门分三类(双评审 Codex M4)** —— 配置不可绕过机制地板;括号内是它落在 §4.1 三层校验的哪层:
| 门类 | 例 | 档位 |
|---|---|---|
| **基线确定性硬门(L1)** | boot / 帧 / 输入改状态 / latch / 控制手感 | **永远强制,配置不可关** |
| **新增确定性门(L1/L2)** | 跨表联动门 / 经济门 / **品类交叉校验(G4,L2)** | observe→enforce(默认只观测,达标率稳后才赋强制/拒发权) |
| **M3 视觉软检(L3)** | 好不好看 / 劣化 | **永远软观察,绝不放行、绝不拒发** |
**配置分三类(双评审 Codex M5)** —— 安全/构建输入不可热改:
```mermaid
flowchart LR
subgraph HARD["硬代码 · 机制(改它=改代码、重部署)"]
H1["确定性门断言 · 四熔断机制 · 基线硬门强制"]
H2["循环/记忆/组装/压缩机制结构 · 权限硬上限"]
H3["铁律:绝不让 LLM 自评 · M3 绝不拒发"]
end
subgraph VER["版本化安全/构建配置(审计·不热改)"]
V1["依赖锁 · 引擎版本 · 构建 profile · 启用沙箱类型"]
V2["权限上限:配置只可降不可升"]
end
subgraph HOT["热配置 · 策略(改配置不发版)"]
C1["prompt 文本 · per-agent 模型路由 · few-shot · skills"]
C2["max_iters · 熔断阈值 · 新增门 observe/enforce 档 · 过门率阈值"]
C3["上下文预算 · 压缩阈值 · RAG 源(枚举内)· 记忆模式"]
end
HARD -. 策略绝不下沉进机制 .- HOT
VER -. 安全/可复现绝不当热配置 .- HOT
```
判定经验:"现在要改就得发版"的东西默认挪进**热配置**;但**安全边界(权限上限)、可复现构建输入(依赖锁/引擎版本/构建 profile/沙箱类型)走版本化审计、不热改**;唯机制留硬代码 —— 确定性门断言、四熔断、基线硬门强制、绝不让 LLM 自评/M3 拒发。
---
## 8. 复用 / fork / 新建 边界(对齐 §1.2 复用契约)
```mermaid
flowchart LR
subgraph REUSE["复用(已在跑)"]
R1["cost.py · _client.py(显式传参 · 禁读 SAA 配置)"]
end
subgraph OFF["官方现成(2.0.2 自带 · 6 项改用官方)"]
F1["ChatUsage 计量 · ReplyBudgetControlMiddleware 软预算"]
F2["generate_structured_output · TracingMiddleware(OTel)"]
F3["ReMe 经验召回 · Agent Team 多 agent 总线"]
end
subgraph OWN["tier2 自有依赖(独立锁 2.0.2)"]
O1["agentscope core(Agent/ReActConfig/AgentState/Middleware/Workspace · 启用待验)"]
O2["agentscope-runtime(浏览器探针候选·待验·见 §3)"]
end
subgraph SEED["种子拷贝(运行期零依赖)"]
S1["studio.py 的 AgentScope 用法"]
end
subgraph NEW["新建 / 重写(= 自建∩私有或∩公共)"]
N1["九门→引擎无关重写(L1/L2)+ L3 M3 软检"]
N2["多轮循环控制(放开 max_iters)+ 四熔断 + 强制 fail-closed 预算闸"]
N3["引擎能力包 manifest(Phaser)"]
N4["mini-肥鹅 spike fixtures · tier2 源项目契约"]
end
style O2 stroke-dasharray: 5 5
```
全程**运行期零碰 SAA**,由 forbidden-import 守门(§11)。注意"官方现成 / 自建"是 §1.2 二维的"来源"轴 —— 上面 NEW 框里的件多数是**自建∩共享(公共组件)或自建∩私有**,而 OFF 框是官方现成、非 tier2 资产。
---
## 9. 衔接《tier2 实现详设》:spike-grade 地基 vs 完整地基
本档是建设五步**第一步(AgentScope 底座)+ 第二步(配置外置)** 的工程落点。但双评审(Opus M5、Codex M9)指出 v1 的"完成定义"是循环论证、且容易把"完整配置外置/记忆/skill 治理"全压在 spike 前,**推迟最该便宜验证的 56% 赌注**。改正 —— 区分两档地基,**spike 只需 spike-grade**:
```mermaid
flowchart LR
SG["spike-grade 地基(spike 前必须有)"] --> SPIKE{"0号 spike<br/>(便宜模型写 56%?)"}
SPIKE -->|过 go| FULL["完整地基(配置外置/记忆/skill 治理 全做)"] --> S3["第三步 铺 Phaser 引擎"]
SPIKE -->|不过| RT["退路树(绝不滑成无限调参)"]
FULL -.也服务.-> REUSE2["将来 SAA 退场时接棒"]
style SPIKE fill:#f9e79f
style RT fill:#f5b7b1
```
**spike-grade 地基 = 逐项映射《详设》第五节六样前置物 + 一项新增**(可勾选;硬编码 prompt/config 可接受 —— 《详设》明说"最小 AgentScope + 硬编码配置即可先验"):
| 《详设》前置物 | 落在四层哪层 |
|---|---|
| ① mini-肥鹅 经营骨架工程 | environment(Filesystem src/ 种子)+ prompt(骨架契约) |
| ② 经营 harness driver + 九门对 Phaser 两钩子参数化 | harness(确定性门 + 引擎钩子参数化) |
| ③ 三联动门 + 经济门 + latch 的 assertAfterPlay 规格 | harness(tier2 专属硬门) |
| ④ studio.py 演进版(放开 max_iters + 写/build/headless/读 verdict/资产注册) | harness(循环新建)+ environment(工具) |
| ⑤ 数据表 schema(12 物品/6 链/5 订单)+ 资产池占位图 | context(数据)+ environment(资产) |
| ⑥ 成本计量接 new-api quota + 轨迹仓最小版 | harness(预算/观测;复用 cost.py) |
| ➕ 沙箱底座小验证(§3) | environment —— **不在《详设》六样内,需补进《详设》(§11 T3)** |
**spike-grade 预算 = 只需时间盒手动上限**(token cap / deadline,承《集成架构》"强制预算建成前只在严格时间盒实验里跑");**完整 fail-closed 预算闸强制是 pre-scale(非 pre-spike)项**。**其余后置**:完整配置外置、长期记忆治理、skill 家族、管理面 —— 全留 spike 过后。
**完成定义的两把尺子(改成可判定)**:① 凡不在《详设》六样前置物因果链上的,推迟(替代 v1 抽象的"足够");② 用 **§7 硬代码侧的机制地板**当"该建"代理(机制=该建、策略=可推迟到 spike 后随实测再配),**不再诉诸"将来 SAA 能否接棒"这个不可证伪的未来命题**。**冲突时 spike-first 优先**,守住《详设》那句"别等平台和引擎都搭漂亮了,才发现便宜模型生成不出富游戏"。
---
## 10. 开放项 / 待校准
- **★ 阈值校准**:¥3/款、50%/70% 过门率 —— 需创始人 + 实测定(承《详设》)。
- **【spike 前置阻断】沙箱底座小验证**:跑 §3 那张 probe→API 矩阵,定 `agentscope-runtime` BrowserSandbox 是否暴露足够 `page.evaluate`/CDP;不够则回落自建薄沙箱 + 现有 CDP。**这是 spike 前必须出结论的阻断项,要补进《详设》前置物清单(当前六样未含)。**
- **引擎能力包接口**:第一版薄做(Phaser 硬编码 + manifest),第二个引擎(Pixi)再抽满。
- **trace 契约时机**:先用官方 `TracingMiddleware`(纯 OTel)→ Studio 看板,`contracts/trace/` 推迟到 spike 或控制面 phase-1。
- **M3 视觉软检校准**:rubric + calibrate vs 创始人 labels —— 这是校准 M3 这把尺子的准度;它有结构性天花板(判不出物理/碰撞/控制手感),**不可替代对产物的人工终审**(两个独立环节,别混)。
---
## 11. 跨档收口 TODO(§6.8 口径:档内已修,跨档列此)
| # | 事项 | 落到哪 | 来源 |
|---|---|---|---|
| T1 | "演进 studio.py" vs "fork 独立"二选一对齐 —— 统一为"以 studio.py 为种子拷贝、运行期零依赖、独立演进";**改一处必改另一处** | 《tier2 实现详设》§建设五步第一步 | Opus M1 / Codex M2 |
| T2 | 被复用低层库(cost.py/_client.py)在 **SAA 退役后的维护归属** + tier2 预算闸与 **D12** 治理对齐 | 《agentic 集成架构》 | Opus M4 / Codex M8 |
| T3 | 把"沙箱底座可行性小验证"**升格为 spike 前置阻断项**写进前置物清单(现六样未含) | 《tier2 实现详设》第五节 | Opus B1 / Codex M7 |
| T4 | tier2 **forbidden-import CI 检查**(禁运行期 import SAA 配置 / studio.py 运行态) | ce-plan 执行(CI/代码侧) | Codex M6 |
| T5 | 确认 tier2 的 latch/解耦重写与生成主文档 §6.3 同名修复**独立、不共享实现** | 与《生成引擎设计主文档》对账 | Opus m3 |
| T6 | README §6.3(SAA 线 player 软门)仍写"视觉反推品类不一致即降权乃至拒发";tier2 已改 M3 永远软 —— 确认 SAA 线是否同步收紧,还是两线有意不同(不同线、不同决策) | 《生成引擎 README》§6.3 / 横切一致性主人 | Codex R2 |
---
> **验证状态**:评审版,未落代码,**已纳入 Codex+Opus §6.8 双评审发现并修订**,并据《agentic 运行时架构图说》(基于 **agentscope 2.0.2 源码实证**,`/root/oss/agentscope` 逐条核过)做了 2026-06-22 架构校正(B1+5/9 MAJOR + minors 档内已修,跨档见 §11)。本次校正的 API 事实均经源码复核:`_version.py = 2.0.2`;无 `ReActAgent`(`agent/_agent.py:93 class Agent` + `agent/_config.py:123 class ReActConfig`,`max_iters` 默认 20);`MiddlewareBase` 6 hook(`middleware/_base.py`);`AgentState(BaseModel)`(`state/_state.py:141`,含 `tasks_context`/`cur_iter`/`context`);`WorkspaceBase`(Local/Docker/E2B)+ `Offloader` + `get_toolkit`(`app/_service/_toolkit.py`)证 Workspace 两轴注入、Agent 持引用非嵌套;`create_app→FastAPI`(`app/_app.py:33`)= 官方 Agent Service;`ReplyBudgetControlMiddleware`/`TracingMiddleware`/`generate_structured_output`/`ChatUsage`/Agent Team(`_team_create`/`_agent_create`/`_team_say`)均在树;skills 经 `WorkspaceBase.add_skill`(无 `register_agent_skill`);`__init__.py` 无 `init(studio_url)`。**一处差异**:树内长期记忆后端是 `mem0` middleware,图说(SoT)以 `ReMe` 口径登记经验召回——选型待 spike 前定(§1.2 注)。"主流四层"经 web 研究核(主流实为 `prompt⊂context⊂harness` 三层嵌套、environment 并入 harness;本档四层据校正重定位为**叠在 AgentScope 真实对象结构上的职责视角**,来源 Anthropic context-engineering、Deepset、SWE-bench harness 等,完整 dossier 见研究留痕)。仓内实证:`studio.py:159 ReActConfig(max_iters=1)`、`requirements-l2.txt` 只引 agentscope base、`agentscope-runtime` 全仓零引用、`contracts/trace/` 不存在。硬/软、机制/策略边界承《自治富游戏引擎》与生成引擎设计主文档既有裁定。
本文档的设计内容已并入生成运行时架构单一事实源 [`agentic运行时架构图说.md`](agentic运行时架构图说.md)(§5.1 运行时形态、§5.8 四层职责)。请以该 SoT 为准;本文件原始内容见 git 历史。

View File

@ -1,256 +1,9 @@
# tier2 实现详设
---
status: 已退役(2026-06-24 收敛进 SoT)
canonical: false
date: 2026-06-24
---
> 🚧 设计中 · tier2 是待 0号 spike 验证的假设,尚未落代码;过了 spike 才进建设。
# (已退役)tier2 实现详设
> **这是什么**:tier2 自治富游戏轨的实现详设 —— 怎么对接、怎么建、怎么部署、怎么跑、怎么测、怎么验。
> **给谁看**:要动手搭 tier2 的工程师,以及拿这份去跑 0号 spike、据结果裁 go/no-go 的人。
> **看图入口**:整轨的生命周期与对象结构画在《[agentic 运行时架构图说](agentic运行时架构图说.md)》,本档与它同步。**框架机制以 AgentScope 2.0.2 源码实证为准**(`/root/oss/agentscope`,逐条核过):tier2 独立锁 2.0.2、从 fork 起步独立演进。
## 第一批工程定义:源项目契约
先看现在的游戏怎么进浏览器,因为 tier2 要在这条路上接第二种产物。现在一款生成好的游戏不是仓库里的代码,而是一份存进库的数据:游戏代码被打成 `engineBundle`,内嵌在它那份 GamePackage 的 manifest JSON 里(整包带一个 sha256 校验)。玩家在 feed 点开,后端把这份 manifest 下发,浏览器取出 `engineBundle`、挂到 `window.__GameBundle`,`bootGameHost` 在沙箱 canvas 上把它跑起来,之后每帧回调游戏的 update / render。每款游戏就是库里一条 GamePackage,不是仓库里一份源码。
tier2 的产物也走"存进库、按需取来跑"这条路,但形态不同。它不是 Tier0/1 那种声明式数据壳 + 固定运行时,而是一个真 Phaser 引擎工程 —— 多个源文件、一份构建脚本、一套依赖,要先 esbuild 构建出 bundle 才能玩。这两条装载路不通用、解耦并存,所以 tier2 立项要补的第一件工程,就是给真引擎工程新写一套源项目契约。
这套契约要钉住七样东西,分四组。先是身份:一个**源项目类型标记**,让装载侧一眼分得清这是 ECS-lite 数据壳还是 tier2 引擎工程、走对应的装载分支。再是工程骨架:**文件树 manifest** 记下有哪些文件、各自什么角色(入口、场景、资产、配置),**入口文件**标出 build 从哪个文件起手,装载和寻址都按它们走。然后是构建可复现:**构建 profile** 固定这一款用什么命令、什么 esbuild 配置打包,**依赖锁**钉死引擎和插件的版本,免得上游一升级历史游戏就构建不出来。最后是落库取回:**内容哈希**给整个工程算一个指纹,做缓存命中和完整性校验;**落库与寻址 API** 定义工程怎么存进 MySQL 加对象存储、怎么按 id 取回来重建。
这条边界要再讲死一遍:这套源项目契约是 tier2 自己的,绝不碰 Tier0/1 廉价线在生产上跑着的 ECS-lite 装载契约。两条装载路解耦并存,tier2 这边怎么改都不许回归现有产线。这也正好接上项目早定下的产品基座 —— 游戏是长生命周期的结构化源项目,改源不改包、重新构建,真·多文件 Phaser 工程本身就是一个可维护的源项目。
命名上要小心别撞:仓里现有的 `contracts/agent-loop/source-project.schema.json` 虽然也叫"源项目",但那是 Tier0/1 那条 ECS-lite 数据壳的契约;tier2 这套是另立的一份独立 schema、编为契约组新一类,绝不扩在那份上——共用一份会把本该解耦的两条线又焊一起,还可能污染在产线。
还有一处要从一开始就对齐、免得日后漂移:tier2 的 agent 自治跑完那一刻,是经一个 **finish 工具**(AgentScope 里以 `Tool` 工具函数为载体的收尾接线)把这份结构化游戏定义吐出来的。建议这个 finish 工具的参数 schema 与上面这套源项目契约**共用同一份定义**——agent 调 finish 交付的形状,就是落库取回时认的那个形状。两边各写各的迟早会对不上(改了契约忘了改工具、或反过来),共用一份从源头杜绝这种漂移。
## 统一 trace 契约的实现接点
tier2 这条 ReAct 轨每跑一次,要把"想一步、调一个工具、看结果"的全过程写进观测仓:每一步推理、每一次工具调用、每一次模型输入输出、每一道门的裁决、每一笔成本。这跟 SAA 那 16 个节点的阶段裁决不同构,所以两条线的轨迹不强求字段对齐 —— 强求对齐是错的。正确的做法是同一张表,一个公共核心子集加各轨自己的扩展段。
落到实现,接点有三处。**公共核心子集**是两条线都必有的最小集 —— traceId、step、cost、verdict、timestamp,tier2 老老实实把这五样填上。**扩展段**是一个 JSON 列,tier2 把自己独有的推理、动作、观察塞进去,SAA 那边塞它的阶段、修复轮次、门裁,各写各的、互不编造。**写不进去时的策略**默认 best-effort —— 轨迹写失败不阻塞主生成流程,但计入告警,不能让一次落库抖动把整局生成废掉。
这份契约落在 `contracts/trace/` 下,作为契约组的新一类 additive 立位(这一位当前还没建,随 spike 或控制面 phase-1 落地时再新立、别当现成),内容包括字段定义、schema 版本、敏感字段脱敏规则、两条线各自的 adapter 怎么映射。tier2 这边只管"按这份契约把自己的轨迹写进去",它的 adapter 把 ReAct 三段映射成契约字段。轨迹契约的完整治理口径 —— 为什么消灭 split-brain 指的是"诚实镜像各自字段"而非"强求对齐"、两条线接口层对称内容层不对称怎么成立 —— 写在《agentic 生成架构:tier2 富游戏自治轨 + 控制/管理面治理层》(下文简称集成架构档),本档只交代 tier2 这一侧的实现接点。
## 运行时形态:两阶段、service 化与 session 三路径
要动手搭 tier2,先得在脑子里钉死它跑起来长什么样——下面这几条是照 AgentScope 2.0.2 源码实证过的实现契约,完整的看图入口在《agentic 运行时架构图说》,本节只交代建设要落到代码上的那几条硬事实。
**先是 service 化:tier2 不自造 API,直接用官方 Agent Service。** AgentScope 2.0.2 自带一个 FastAPI 工厂 `create_app`,给的就是 REST + SSE + durable session + MessageBus 这一整套——建会话 `POST /sessions`、流式回放、跨进程消息总线都现成。game-cloud 经它调 tier2,断线还能 replay。所以这层别自己写,接官方的。**与之配套的一条关键事实:Agent 实例非常驻。** 它不是一个长跑的进程,而是每次 run 现组装、跑完即弃,所有状态都落在可持久化的 `AgentState`(pydantic 模型,含 `context` 工作记忆、`cur_iter` 当前轮次、`tasks_context` 任务清单)里。这直接决定了 checkpoint 怎么做——不是去序列化一个活对象,而是存取这份 `AgentState`(配 StorageBase,落 Redis)。
**session 初始化有三条加载路径,建设时三条都要支持。** 一是续接之前的会话:从 `SessionRecord.state` 取回历史,resume 着往下跑;二是加载一份已有的游戏工程:把 `Workspace.workdir` 指向那个已存在的目录,进入迭代模式(对应"游戏=长生命周期项目,改源不改包");三是新建:由阶段 2 的模板初始化工具从模板铺一套工程骨架起手。三条路径汇到同一个 Agent 装配点,只是初始 state / workdir 不同。
**再是两阶段,以及"单写者"到底指哪一阶段——这点容易误读。** tier2 的生成分两阶段。**阶段 1 是工作室设计,用 Agent Team 星形多 agent**:一个 leader 分析并拆解用户意图,经 `AgentCreate` 拉起玩法 / 关卡 / 数值 / UI / 音乐 / 特效 / 资产等设计 worker 去发散,再经 `TeamSay` 把各路结论汇回 leader 收敛成设计结论。这一阶段是**只读 + 对话**——设计 worker 用 `PermissionMode.EXPLORE` 只读已有工程代码和工程内设计文档(迭代已有游戏时),并与用户**跨 session 多轮对话**,不写代码。**阶段 2 才是单写实现**:模板初始化工具铺骨架 → 把阶段 1 每个设计 agent 的结论写成工程内文档 → 单写 agent(单个 Agent 跑 ReAct 多轮)写代码 → 三层校验 → 产出。设计用多 agent 是要发散和专业分工,实现收敛到单写是要防并行写冲突。**所以"建设/spike 里说的单写者"专指阶段 2 的写代码环节,不是说整轨只有一个 agent。**
这里要特别记一条 2.0.2 的多 agent 边界,免得照旧版资料踩坑:**2.0.2 删掉了进程内的 MsgHub / pipeline 编排,进程内多 agent 协作那套没有了**;留下的只有部署态的 Agent Team——`TeamCreate` / `AgentCreate` / `TeamSay` 这套工具,且是 leader-worker 星形拓扑:worker 只拿到 `TeamSay`(只能把结果报回 leader),**禁止 worker 之间互评 / 互相喊话**,所有协调都过 leader 这个中心。阶段 1 的工作室就建在这套 Agent Team 上,而不是去找已经不存在的 MsgHub。
## 三层校验(L1/L2/L3)
tier2 自治循环里的"过没过",按创始人定的三层校验判,而不是旧表述里笼统的"九门 / 双层"。绘境现行那套"在真浏览器里真玩一遍判能不能玩"的确定性门(历史上叫九门),在 tier2 里归入最底下的 L1。三层各管一档、处置力度不同:
| 层 | 内容 | 处置 | 工具包 |
|---|---|---|---|
| **L1 硬约束** | 编译 / 启动 / 运行错误日志 | **必须解决 · 循环** | 确定性:构建日志 / console / CDP 错误捕获(现行九门多数落此) |
| **L2 设计符合** | 玩法 / 关卡实现 vs 设计、UI 缺组件 | **尽量解决** | 设计符合度校验(部分门:机制进展) |
| **L3 效果** | 特效 / 美观 / 好不好玩 | **只评分不解决** | M3 视觉软检(绝不阻塞 / 拒发) |
**迭代原则:初期只识别并解决硬问题(L1),L2/L3 渐进做;绝不让效果问题阻塞真问题;用户可经 HITL 要求继续解决任意层。** 下文 spike 的过门阈值、采集字段、退路树里凡提"九门 / 确定性门",指的都是这套 L1 层;spike 初期也只焊死 L1。
## 建设五步与 0号 spike 生死门
建设按创始人给的五步走,在第三步之前插一道便宜的 0号 spike 门。一条贯穿的原则定调:最深的赌注要在最便宜的时候验,别等平台和引擎都搭漂亮了,才发现便宜模型生成不出富游戏。
```mermaid
flowchart TD
S1["第一步 · 搭 AgentScope 完成集成<br/>(从 L2 studio 编排演进,非 L1 裸 openai 主线)"]
S2["第二步 · 配置外置做实<br/>(prompt/编排/模型可配不发版 · 归线A阶段2-3 · 本档引用不重排)"]
SPIKE{"0号 spike 生死门<br/>便宜模型能否自治写出那 56% 表现层?<br/>最小 AgentScope + 硬编码配置即可先验"}
RETREAT["按退路树走<br/>(绝不滑成无限调参)"]
S3["第三步 · 以 Phaser 为范例搭生成引擎<br/>边搭边反哺第一二步"]
S4["第四步 · Phaser 自治生成一款过人审<br/>(对标肥鹅美食街)"]
S5["第五步 · 完善 Cocos 生态 + 推进 Phaser 渠道 adapter"]
S1 --> S2 --> SPIKE
SPIKE -- "过(go)" --> S3 --> S4 --> S5
SPIKE -- "不过(no-go)" --> RETREAT
H3["隐藏硬工作:验收地板 / Goodhart 安全 / 成本强制 / 源项目契约 / 可观测早建"]
S3 -.补齐.-> H3
H4["隐藏硬工作:终审判据 / 终审吞吐模型 / player panel 减负"]
S4 -.补齐.-> H4
```
**第一步,搭起 AgentScope、完成集成。** 这是 agent 和循环的运行时底座,锁定 AgentScope 2.0.2 的真实 API:一个统一的 `Agent` 类(无状态 ReAct 引擎),构造时持有 model / toolkit / middlewares / state,自治多轮由它内建的 reasoning-acting 循环驱动,轮数上限走 `ReActConfig.max_iters`(2.0.2 默认 20)。tier2 是真自治富游戏轨,要的正是这种"在 Agent 内部跑多轮、自己 reason→act→读结果→再 reason"的形态。
这点上 tier2 要和仓内 WG1 那条便宜线划清:WG1 的 `agent_loop/studio.py` 当前是 `ReActConfig(max_iters=1)` 的单轮调用 + 外层 `for attempt in range(max_repairs)` 的 repair 重试循环——单写、单轮、靠外层喂错重跑,那是 L1/L2 廉价档为压单价采的形态(L1 生产主线明确不引 AgentScope,框架 token 膨胀会吃掉便宜档单价)。tier2 复用的是 studio 那套多角色 prompt 与生成域(design/code/fix)的躯干,但把"多轮自治"从外层 repair 循环搬进 Agent 内部的 ReAct 循环(`max_iters` 放开),是 fork 起步、不是照搬。
**tier2 独立锁 2.0.2,从 fork 起步、独立演进**:它在 AgentScope 2.0.2 上立轨,与 WG1 廉价线各自演进,不强求两条线跟同一份 AgentScope 版本走。这层地基值得建,而且它和下一步的配置外置回头还能服务现有 Tier0/1 线。
**第二步,把配置外置做实。** 让 prompt、编排、模型这些模型的全部输入都可配置、不发版可改(声明式为主、带脚本逃生口),并把一切挂上可回溯的轨迹。这一步的实现归线A的阶段2-3,本档引用、不重排。
**第三步之前,先插 0号 spike 门。** 原来的顺序是先搭引擎(大投入)再出肥鹅人审过(才验它能用),但便宜模型真正要现写的是那约 56% 的表现层,这是整轨最深、也最没证过的风险。所以在把全引擎生态铺开之前,先用一个最小、可丢弃的 spike 把这个赌注便宜地证一遍。关键是这道门不必等第一二步全做完 —— 最小的 AgentScope 加硬编码配置就能先验。它的可跑工程规格在下文第四节展开。spike 不过,就按退路树走,绝不滑成无限调参。
**第三步,spike 证成后,以 Phaser 为范例搭起生成引擎,边搭边反哺第一二步。** 这一步表面是搭引擎,底下藏着五块必须显式排进去、不补就埋雷的硬工作。验收地板 —— 把现行那套确定性验收门(绘境现行的"在真浏览器里真玩一遍判能不能玩",即下文的 L1 硬约束层)泛化到 Phaser、为经营品类补确定性 driver、加跨表语义门,没有它"人审通过"是空的。Goodhart 安全 —— agent 自产的取证 driver 必须过平台白名单加独立评审,写者不能写判自己游戏的那张卷子(Goodhart 指优化代理指标反而偏离真目标)。成本强制 —— 用官方 `ReplyBudgetControlMiddleware` 做软刹(在 `on_reasoning`/`on_reply` 钩子里按预算优雅收尾),叠一层自建的硬熔断(超时 / 卡死 / 硬杀,各做成 `MiddlewareBase` 子类挂进洋葱),现在还只是 best-effort,自治多轮不强制会烧钱,规模化前必须落地。真 Phaser 工程的源项目契约 —— 也就是本档开头那套,文件树 manifest、构建 profile、依赖锁、落库寻址,这步要真接上。可观测要早建 —— 用官方 `TracingMiddleware` 把每步推理/动作/观察吐成 typed event 流进 Studio,叠成本台账,spike 调试和对账当下就要,这也是复利中台唯一该在这个阶段建的部分。
**第四步,用 Phaser 自治生成一款质量极高的游戏,对标肥鹅美食街,过确定性地板加人工终审。** 这里要把"人审"做实,而不是创始人亲玩一票:要有终审判据(把"好玩"拆成可复核的几条、压个人品味方差)、一个终审吞吐模型,并让 player panel 去判有没有明显劣化信号(可达性、空内容这些确定性可逼近的)给人门减负,否则 premium 量一上来,人审会变成瓶颈或橡皮图章。产物是一个可维护、可回头改和扩的 Phaser 源工程(改源不改包),不是生成完就完的一次性产物。
**第五步,完善 Cocos 生态,同步推进 Phaser 的渠道 adapter。** Cocos 放在 Phaser 证成之后是对的 —— 它是另一条轴(编辑器、人在环,服务 3D、复杂、渠道导出),进不了 tier2 的自治循环,不该和 Phaser 并级、更不该在核心赌注证成前就铺。渠道 adapter 走仓内 W-CH-α 已实证到 P0 的那条"轻 H5 引擎加自研 adapter"路,待补是 P1 真机门,卡在创始人的三件套那道日历闸门上,不是技术阻断。
## 0号 spike 可跑 runbook
这个 spike 是为了在最便宜的时候,把"便宜模型能不能自治写出那 56% 的表现层"这个最深的赌注证伪或证成。集成架构档的相关章节写的是判据哲学 —— 测什么、为什么分两段、退路怎么走,不是可跑的工程规格。一个工程师拿那份跑不起来这个 spike:靶子游戏没有确切规格、模型没点名、过门没有数字阈值、两段没有步骤、采集指标没有字段。补齐这五块,spike 才能照着跑。
### 靶子游戏:mini-肥鹅
spike 的对照组("人工预建骨架加人工预建 driver")得有一个确切的靶子游戏才能预建。这里把它钉死 —— 一个砍到能证、又保住三个本质难点(多系统耦合、重 UI 表现层、数值经济闭环)的最小经营游戏,代号 mini-肥鹅,结构参照仓内 `wanglanmei-ref` 但更小。砍掉任务弹窗、背包深度、上百物品,留三个联动系统。
```mermaid
flowchart LR
subgraph 合成系统["合成系统(重 UI 表现层主承载)"]
M["3×3 棋盘 · 点两个相同物品合成上一级<br/>mergeChains 6 条链 / 覆盖 12 物品"]
end
subgraph 资源系统["资源系统(读写交汇点)"]
R["金币 + 食材库存<br/>{ coins, ingredients }"]
end
subgraph 订单系统["订单系统"]
O["3~5 个顾客订单 · 要物品给金币<br/>orders 5 个模板 / 有 patience"]
end
M -- "合成产出进库存<br/>consumeIngredient 扣食材" --> R
O -- "完成订单扣物品<br/>addCoins 加金币" --> R
R -- "金币门控解锁第 4/5 摊位、更高合成链" --> M
M -- "产出物品须满足订单 requires<br/>(跨表可达性)" --> O
WIN["赢 = 金币达 100(开局 20 攒上去)"]
LOSE["输 = 连续 3 个订单流失"]
R --> WIN
O --> LOSE
```
资源系统维护两种货币 —— 金币和食材库存,它是其余两个系统的读写交汇点:合成消耗食材、订单产出金币、解锁花金币,状态就是一张极小的表 `{ coins, ingredients: { [id]: number } }`。合成系统是一块 3×3 棋盘,玩家点两个相同物品合成上一级,合成链是数据表 `mergeChains: [{ from, to, cost }, ...]`,给 6 条链覆盖 12 个物品,产出进资源系统的库存,这块是重 UI 表现层的主要承载(棋盘格渲染、点击命中、合成动画)。订单系统是一个面板列 3~5 个顾客订单,每个要一组物品给一笔金币,数据表 `orders: [{ id, requires, reward, patience }]`,完成订单从库存扣物品、给资源系统加金币,耐心耗尽订单流失。
系统间的确切耦合点是这 spike 的考点,不是三个孤岛:订单的 `requires` 必须能被合成系统产出的物品满足(跨表可达性);完成订单调资源系统的 `addCoins`;合成消耗调资源系统的 `consumeIngredient`;金币门控解锁(攒够金币解锁第 4、5 个摊位和更高合成链)。胜负条件 —— 赢是金币从开局 20 攒到 100(经"合成→交单→收金币"的正循环),输是连续 3 个订单流失(只合成不交单、或经济崩盘),两条路径都要能被 harness 真输入驱动跑到终态并 latch。内容量定在 12 个物品、6 条合成链、5 个订单模板、2 种货币,足够证数据驱动而不至于让 spike 本身变重。这份规格就是第一段里"人工预建骨架"和"人工预建 driver"的施工图 —— 骨架按这三个系统加耦合点预建(平台代码),driver 按"点合成格→凑齐订单物品→交单→看金币涨"的确定性序列预建。
### 模型矩阵与样本量
"便宜/中等模型"是个集合不是一个值,过门率、成本、收敛步数这三个产出数必须有归属对象,否则"60% 是哪个模型的 60%"无法回答。spike 跑这个矩阵:
| 角色档 | 模型 | 说明 |
|---|---|---|
| 主力便宜档 | deepseek-v4-flash | 现行 Tier0/1 打底模型,tier2 自治的主力候选 |
| 强便宜档 | deepseek-v4-pro | 现行救场档,测"贵一档便宜模型"是否显著抬过门率 |
| 中等 agentic 档 | MiniMax-M3 | 这批最能打,**先用它证路**(见下"跑序")。接法走官方 `AnthropicChatModel` + `AnthropicChatFormatter`(构造模型时不传 formatter 即自动配对一个 `AnthropicChatFormatter`,无需手接),`AnthropicCredential.base_url` 指向 new-api 的 Anthropic 端点(即 `/v1/messages`),从而吃到原生的 Anthropic 协议 + agentic 工具循环;开 `thinking_enable` / `thinking_budget` 拿 thinking 分离,**注意 Anthropic 强约束 `max_tokens` 须严格大于 `thinking_budget`**(`thinking_budget` 缺省取 `max_tokens // 2`;若设得 ≥ `max_tokens`,2.0.2 的 `AnthropicChatModel` 会把 `max_tokens` 自动抬到 `budget + 1024`——别依赖这个兜底,显式设对) |
| 强模型基线 | Opus / Fable | 只跑 1~2 款作"路通不通"的上限基线,不进成本评估 |
模型这一层统一从 new-api 出口走(`baseUrl` + `NEWAPI_KEY`,凭据见 `docs/内网凭据与端点.md`),协议 / SDK 不锁死——M3 这档走 Anthropic 原生端点是因为"用对它"要的就是原生 thinking + 工具循环,便宜档照旧走各自端点即可;all roads 收口到 new-api 一个计费平面。
样本量上每个便宜/中等档跑 n ≥ 30 真跑(承袭"先跑 n≥30 真基线、别拿单次当基线"的硬教训),强模型基线 n = 2~3。题面用 5 个一句话变体(美食、水果、咖啡、面包、糖水店),每变体每档跑 6 次凑够 n≥30。跑在 mini-desktop —— 权威构建加 e2e 门、x86 同构;禁本机 6c6g 跑 chrome。
**跑序:先用 M3 证路,再用便宜档比成本(2026-06-21 创始人定)。** 不一上来就把整张矩阵 × n≥30 全铺开。先用 MiniMax-M3 把路跑通 —— 它是这批便宜/中等档里最能打的一个(能力比 Sonnet 略强、且接得通),用它在全流程上自治产出一款 mini-肥鹅,创始人亲玩、判它确实是个有肥鹅味的可玩雏形。这一步证的是"便宜模型自治这条路到底走不走得通";走不通,就别在更便宜的模型上空耗。证通之后,再用 deepseek-v4-flash 和 deepseek-v4-pro 各跑一轮(n≥30)做对比,看把模型降到主力便宜档、过门率和单款成本各掉多少 —— 这一步证的是"便宜到什么程度还守得住"。Opus/Fable 那档仍只作路通不通的上限基线,不进成本评估。
### 过门判据与精确阈值
没有数字阈值,go/no-go 必然滑成"再调一轮",退路树也悬空。标 ★ 的是建议值、需创始人和实测校准,其余是承袭现行线的硬约束:
| 维度 | 阈值 | 来源 |
|---|---|---|
| 过门率(L1 确定性门 + 三联动门 + 经济门全绿 + latch) | ★ ≥ 50% 起评、≥ 70% 算强信号 | 对照现行 factory 路 60%、cutover 门 80%;tier2 富游戏更难,spike 阶段先看是否显著大于 0、能否随档升 |
| 单款成本 | ★ ≤ ¥3 / 成功款 | 现行 L1 ≤¥0.15、L2 ≤¥5;tier2 premium 取 L2 偏下,留自治多轮空间 |
| 收敛步数 | ≤ max_iters(★ 单系统 ≤ 8 轮、整局 ≤ 40 轮) | 防靠运气擦边;超 max_iters 即判不收敛 |
| 长程一致性 | 12+ 文件工程过程中无 checkpoint 丢失、半轮副作用幂等无脏 | 采集字段 |
| 人锚 | 创始人试玩判一句"是不是个有肥鹅味的可玩雏形" | 软门,不可替代 |
### 两段实施步骤
变量隔离的逻辑落成可执行的两段,每段有输入、跑法、采集、gate。
```mermaid
flowchart TD
subgraph stage1["第一段 · 纯模型变量(driver 是人预建的、不让 agent 碰)"]
P1["预建(人投入):mini-肥鹅 经营骨架工程(三系统+耦合点,先天过 boot)<br/>+ 确定性 harness driver(点合成/凑单/交单序列)<br/>+ 数据表 schema(留空给 LLM 填)+ 资产池占位图"]
P2["跑:便宜模型在骨架上填数据表 + 写表现层<br/>(场景渲染/事件接线/特色规则)<br/>Agent 内 ReAct 多轮(max_iters 放开)写源→build→headless 快检→L1 门→读 verdict→改"]
P3["采集 + gate:过门率/成本/收敛是否达阈值"]
P1 --> P2 --> P3
end
G1{"第一段 gate"}
P3 --> G1
G1 -- "纯模型变量就崩 → 不开第二段(开放自治只会更糟)" --> R["进退路树"]
subgraph stage2["第二段 · 开放自产 driver(测自治上限 + Goodhart 安全)"]
Q1["放开:让 agent 自产/扩 driver<br/>走三层隔离 ①agent 提议 driver →②平台 schema/白名单编译 →③独立 adversarial 评审<br/>(查它是否覆盖真实玩家路径、是否读作弊字段)"]
Q2["跑 + 采集:额外采 自产 driver 被打回比例 / driver 修改次数<br/>(防它用改 driver 逃避卡死探测)"]
Q1 --> Q2
end
G1 -- "过" --> stage2
G2{"第二段 gate:自产 driver 不降过门可信度、自治不烧穿成本闸"}
Q2 --> G2
G2 -- "挂在 driver 安全" --> RG["判 Goodhart 防线设计问题(不是模型问题)"]
```
第一段是纯模型变量,固定人工骨架加人工 driver。先预建(人投入,Opus 或人写):按上面 fixture 规格,预建 mini-肥鹅 经营骨架工程(三系统加耦合点的平台代码,先天过 boot)加经营品类的确定性 harness driver(点合成/凑单/交单序列)加 12 物品/6 链/5 订单的数据表 schema(留空给 LLM 填值)加共用资产池占位图。再跑(便宜模型自治填那 56%):对每个模型档乘每个题面变体,跑 `worker/agent_loop/studio.py` 的演进版——把它当前 `ReActConfig(max_iters=1)` + 外层 repair 循环的单轮形态,改成 `max_iters` 放开的 Agent 内 ReAct 多轮自治(便宜模型在骨架上填数据表、写表现层:场景渲染、事件到表现的接线、特色规则),自治循环写源→build→headless 快检→L1 确定性门→读 verdict→改。spike 最小版直接最小 AgentScope + 硬编码配置即可,先不上 service / Agent Team / 配置外置那些。然后采集加 gate:按字段表采每款的 pass/repairs/¥/wall/出错系统/长程指标,第一段 gate 就是过门率、成本、收敛是否达阈值。这一段只测"填差异"的纯模型变量,driver 是人预建的、不让 agent 碰。
第二段开放自产 driver,测自治上限加 Goodhart 安全。在第一段证通的基础上放开,让 agent 自产或扩 harness driver,但走三层隔离:agent 提议 driver,平台按 schema 和白名单编译,独立 adversarial 评审查它是否覆盖真实玩家路径、是否读作弊字段(adversarial 评审指一个不向着被评对象、专找漏洞的独立评审)。跑加采集同上,额外采集"自产 driver 被独立评审打回的比例"和"driver 修改次数"(防它用改 driver 逃避卡死探测)。第二段 gate 是自产 driver 不降过门可信度(独立评审通过率高)、自治不烧穿成本闸。
两段之间的 gate 卡得很清:第一段不过(纯模型变量就崩)直接进退路树,不开第二段(开放自治只会更糟);第一段过、第二段挂在 driver 安全,判 Goodhart 防线设计问题,不是模型问题。step 模板复用仓内 `game-e2e-cdp-harness.md` 的四件套证据编排(render→截图时序→驱动→verdict),每款产出四件套证据可审计。
### 采集指标字段表
spike 的产出就是这几个数,无字段定义则跑完拿不到可对比的数。轨迹仓加成本台账最小版记这些列,每款一行(run 级):
| 字段 | 定义 / 取处 |
|---|---|
| run_id / model / stage(1 或 2)/ brief_variant | 标识 |
| pass | L1 确定性门 + 三联动门 + 经济门 + latch 是否全绿(确定性,取 verdict.pass) |
| repairs | 自治循环轮数(取 studio 的 attempt 计数) |
| fail_stage | 挂在哪(extract/validate/build/seven_gate/三联动门/经济门;承袭 studio 的 stage_fail) |
| fail_system | 挂在哪个系统(resource/merge/order/表现层;经营品类特有) |
| cost_rmb / tokens_by_model | per-model token 折¥(取 new-api quota 口径,cost.py) |
| wall_s | 墙钟 |
| file_count / total_loc | 产物工程的文件数、总行数(长程一致性) |
| ctx_compressed / checkpoint_recovered / idempotency_clean | 长程:是否触发上下文压缩、checkpoint 恢复是否无损、半轮副作用是否幂等无脏 |
| driver_authored / driver_rejected_by_review / driver_edits(仅第二段) | 自产 driver 的安全指标 |
汇总到矩阵级,每个模型档的过门率 = pass 款数 / n、¥/成功款 = Σcost / pass 款数、收敛中位数、fail_system 分布 —— 这三张图就是 go/no-go 的直接输入,对照阈值与退路树触发线判。
### 前置物清单
spike 开跑前这六样必须就位:mini-肥鹅 经营骨架工程(按 fixture 规格,Opus 或人预建);经营品类 harness driver(点合成/凑单/交单,确定性)加 L1 确定性门对 Phaser 的两钩子参数化(canvas 选择器、帧计数源);三联动门加经济门加 latch 的 assertAfterPlay 规格(订单引物品可达、合成 DAG 无环、经济可盈利可破产);studio.py 演进版(`max_iters` 放开为 Agent 内多轮自治,写源 / build / headless 快检 / 跑 L1 门 / 读 verdict / 查资产注册进 toolkit);数据表 schema(12 物品/6 链/5 订单,留空给 LLM 填)加共用资产池占位图;成本计量接 new-api quota(per-model 折¥)加轨迹仓最小版。spike 阶段的成本熔断可先挂官方 `ReplyBudgetControlMiddleware` 软刹,硬熔断(超时/卡死/硬杀)与可观测 `TracingMiddleware`、记忆 ReMe 这些到建设期再补全,spike 最小版不强求。
## 退路树
spike 跑出来的过门率和失败集中处,按三条数字触发线分流到不同退路,让退路树真能被触发、不悬空。
```mermaid
flowchart TD
START["spike 跑完,看矩阵级的 过门率 + fail_system 分布"]
Q1{"最强便宜档 v4-pro 过门率<br/>是否仍 < 40%?"}
Q2{"失败是否集中在表现层<br/>(render / 事件接线)?"}
Q3{"是否失败集中在某个系统装不出<br/>(如合成棋盘命中几何反复错)、<br/>其余系统能过?"}
Q4{"是否便宜档全线 < 20%、<br/>而强模型基线 Opus/Fable 能过?"}
GO["go:过门率达阈值 → 转第三步以 Phaser 铺引擎"]
R1["退向更模板化:更多预制表现模板、少 LLM 写<br/>(判 56% 自治承重太重)"]
R2["补骨架再试:补该系统的骨架/driver 缺口<br/>(判骨架缺口,不是范式问题)"]
R3["便宜模型天花板:换更贵模型重估经济性<br/>(撞 ¥3/款 上限就缓行),或整轨缓行"]
KEEP["留观:既非全线崩、也非天花板 → 据具体数微调前置物再跑"]
START --> Q1
Q1 -- "是(< 40%)" --> Q2
Q2 -- "是,集中表现层" --> R1
Q2 -- "否" --> Q3
Q1 -- "否(≥ 40%,达阈值)" --> GO
Q3 -- "是" --> R2
Q3 -- "否" --> Q4
Q4 -- "是" --> R3
Q4 -- "否" --> KEEP
```
第一条:最强便宜档 v4-pro 过门率仍 < 40%、且失败集中在表现层(render 和事件接线),判 56% 自治承重太重,退向更模板化 —— 更多预制表现模板、少 LLM 写。第二条:失败集中在某个系统装不出(比如合成棋盘命中几何反复错)、但其余系统能过,判骨架或 driver 缺口,补骨架再试,不是范式问题。第三条:便宜档全线 < 20%、而强模型基线(Opus/Fable)能过,判便宜模型天花板,要么换更贵模型重估经济性(撞 ¥3/款 上限就缓行)、要么整轨缓行。
## 现在证到哪,下一步真动作
已证的是产物形态:`game-runtime/games/wanglanmei-ref/` 是一款真存在过的、12 文件约 180KB 的多系统经营游戏,过了真输入 harness、赢和输双路径都可达,它证伪了"声明式数据壳之外的真引擎工程装不下肥鹅级富游戏"。Phaser 这条路能过确定性门也有独立证据 —— 2026-06-11 那次走廊小卖部样本在 Phaser 上过了五门。这两份证据各钉死一个点:前者钉死产物形态(与引擎无关),后者钉死 Phaser 这条路能跑真 harness。
没证的是 tier2 真正押的那一半 —— 这两次都是最贵的模型(Fable)加重人工编排做到的,不是便宜模型自治。最大的赌注是:便宜或中等模型的自治 agent,在经营骨架、玩法模板、资产池、RAG、L1 确定性门兜底这套约束下,能不能稳定地把那 56% 的表现层写出来、过确定性门。赌注的来龙去脉在《自治富游戏引擎》那份引擎设计里,本档只接"怎么验"。
下一步三件:备齐上面第五节的前置物清单;在 mini-desktop 上跑两段 runbook;据采集字段与退路树触发线裁 go/no-go,决定 tier2 的正式投入与建设顺序。
## 验证状态
本档是 tier2 的实现详设(评审版),未落代码。它把 0号 spike 的 runbook(靶子规格、模型矩阵、阈值、两段步骤、采集字段、退路树)与 tier2 立项要补的源项目契约、统一 trace 契约实现接点收成一份可照跑的工程详设;阈值标 ★ 的需创始人和实测校准(尤其 ¥3/款 premium 预算、50%/70% 过门率门)。轨迹契约与控制面的完整治理口径在集成架构档,本档交叉引用、不重排。
本文档的设计内容已并入生成运行时架构单一事实源 [`agentic运行时架构图说.md`](agentic运行时架构图说.md)(§5.3 单写 ReAct、§5.8 四层职责)。请以该 SoT 为准;本文件原始内容见 git 历史。

View File

@ -1,93 +1,9 @@
---
date: 2026-06-23
topic: tier2 细节图说 · A 族——运行时形态(Agent Service / 非常驻 + AgentState / Workspace 双轴 / Middleware 洋葱)
status: 设计中(tier2 待 spike) · 每图映射源档见该图脚注 / frontmatter 记 hash 作防漂移门
映射源档:
- { doc: "docs/architecture/架构/生成引擎/tier2实现详设.md", hash: "d5efe45f" }
- { doc: "docs/architecture/架构/生成引擎/agentic集成架构.md", hash: "1b3e375a" }
- { doc: "docs/architecture/架构/生成引擎/agentic运行时架构图说.md", hash: "32adda95" }
- { doc: "docs/architecture/架构/生成引擎/tier2四层工程架构.md", hash: "d5efe45f" }
status: 已退役(2026-06-24 收敛进 SoT)
canonical: false
date: 2026-06-24
---
# tier2 细节图说 · A 族 —— 运行时形态
# (已退役)tier2 细节图说 · A 运行时形态
> 本篇是绘境AI 架构图集 **生成引擎子树** 下的 tier2 细节图说,只覆盖 **A 族(运行时形态)** 四张图。生成引擎主干、tier2 总览、自治富游戏验收各有自己的图说;这里专门把「tier2 这条富游戏自治线跑起来到底长什么样」这件事画清楚——cloud 怎么接进去、agent 实例怎么活、执行环境怎么挂上来、横切关注点挂在哪。
---
## 0 阅读约定与同步纪律
A 族讲的是 tier2 富游戏自治生成线的运行时骨架——从 cloud 怎么经官方 Agent Service 接进来,到 agent 实例非常驻、状态全外置到 AgentState,再到 Workspace 沿两轴注入 agent,最后到成本与观测怎么统一挂在 middleware 洋葱上。这四件事是同一条运行轨的四个切面:图 A1 画接入面(cloud 怎么进、session 三条路怎么汇),图 A2 画 agent 的生命周期与断点续跑(非常驻怎么靠 AgentState + checkpoint 落实),图 A3 纠一个最容易踩的误解(Workspace 不是包着 agent、而是注入 agent),图 A4 画横切关注点的统一挂载点(成本刹车 / 硬熔断 / 观测挂在哪)。
这四张图映射四份属主设计档加 AgentScope 2.0.2 源码。运行时形态的总口径出自《tier2 实现详设》的「运行时形态:两阶段、service 化与 session 三路径」一节,接入面的逐 router 与 MessageBus 细节出自《agentic 集成架构》的「tier2 接入面」与「D12 运行治理门」两节,所有结构性事实——八个 router、AgentState 三字段、WorkspaceBase 三实现、MiddlewareBase 五钩子——都按 AgentScope 2.0.2 源码逐条核过(`agentscope/app/`、`state/_state.py`、`workspace/`、`middleware/`)。frontmatter 记了这四份的当前 commit hash,作为防漂移门——其中任一份动了运行时口径,本图说与对应 SVG 必须同步改。
整条 tier2 富游戏线现在 **整体待建**:0号 spike 还没跑,引擎没搭,代码一行没落。所以读 A 族时,「待建」是默认状态,绝不能因为图画得齐整就误以为运行时已经搭起来了。但「待建」不等于「全凭空想」——A 族里有一大块是 AgentScope 2.0.2 已经提供的现成件:官方 Agent Service(`create_app` 拉起的 FastAPI)、MessageBus(Redis 实现)、AgentState 模型与 StorageBase、WorkspaceBase 三实现、官方两个 middleware(预算软刹与 tracing),这些都现成、不用自造,图里用实线画。tier2 自己要做的是把这些现成件接成一条富游戏自治线:接官方 service、做非常驻装配与每轮 checkpoint、在官方 Workspace 抽象上挂 tier2 要的工具与资源、把硬熔断三件做成 MiddlewareBase 子类——这部分用虚线画。看图时记住这条对照:**实线 = 现行已落 / AgentScope 2.0.2 官方现成件,可以直接信;虚线(stroke-dasharray 6 4)= tier2 专属、待 0号 spike 验证后才落代码的接线。** 个别图上还有远期紫,标的是「设计已出但排在更长期层级」的元素(如 E2B 远端强隔离),不是 spike 期的投入项。
A 族是《agentic 运行时架构图说》的放大层,不是它的替代。那份总览图说里的图 1(系统全景)、图 2(调用时序)、图 3(运行时内部)、图 5(编排配置)、图 8(关键类图)给的是整体结构,A 族只把「运行时形态」这一面逐字段放大——总览图 3 把 Agent 与 Workspace 的整体结构画在一处,A 族则把非常驻装配、AgentState 三字段、双轴注入的两条轴、middleware 五钩子各自挂什么,一项一项展开。凡总览图已画清的整体结构,A 族不重画,只在脚注里指回去。这是 deepen 而非 duplicate:总览看轮廓,A 族看施工。
## 1 全图通用图例
A 族四张图共用一套视觉语言,先在这里讲清,后面每张图不再重复。
**线型承担状态语义。** 实线框 / 实线箭头 = 现行已落或 AgentScope 2.0.2 官方现成件(官方 Agent Service、MessageBus、AgentState、StorageBase、WorkspaceBase 三实现、官方 ReplyBudgetControlMiddleware 与 TracingMiddleware、D12 运行治理门),这些可以直接复用、不用自造。虚线框 / 虚线箭头(`stroke-dasharray 6 4`)= tier2 专属待建机制(非常驻装配、每轮 checkpoint 落 Redis、session 三路接线、注入工具集、D5 沙箱底座选型、硬熔断三件子类),还要写还要验。远期紫(紫框 / 紫底)= 设计已出但排在更长期层级的元素,典型是 E2B 远端强隔离对齐的多租户 / premium 层,不是 spike 期投入项。
**徽章标的是这块内容服务哪个生产维度。** 绿色的 **可靠**(断点续跑不丢、半轮副作用幂等)、**成本**(压住自治多轮的烧钱)、蓝色的 **可观测**(每步推理 / 工具调用可对账)、灰色的 **数据**(状态可持久化落库)、红色的 **安全**(沙箱隔离 = 写码真跑的信任边界)、紫色的 **伸缩**(远端沙箱撑多租户)。这些维度落到 A 族的具体接点上:可靠落在 SSE 可恢复与 resume 续跑,成本落在 middleware 洋葱的预算软刹与硬熔断,可观测落在 Workspace 统一面与官方 Event→OTel 管道,数据落在 AgentState→StorageBase 落库,安全落在 Workspace 三实现的隔离强度梯度。
**状态码四个,贯穿生成引擎子树。** **现** = 现行已建在跑或 AgentScope 2.0.2 官方现成;**接** = 设计已出、代码待接线;**建** = tier2 立项后要新建、当前待 spike;**缓** = 收窄后缓做 / 更长期层级。A 族绝大多数 tier2 自有元素是「建」,官方现成件是「现」,E2B 远端强隔离这类更长期项是「缓」。
## 2 图集
### 图 A1 · Agent Service 与 session 三路径
![图 A1 Agent Service 与 session 三路径](assets/t2-A-01-AgentService与session三路.svg)
cloud 这边要调 tier2 生成一款富游戏,第一个要回答的问题是「接口在哪、长什么样」。这张图给的答案是:tier2 不自造这层 API,直接用 AgentScope 2.0.2 的官方 Agent Service。它是 `create_app` 拉起的一个 FastAPI 应用,对外是 REST 加 SSE,天生多租户、天生 durable session,源码在 `agentscope/app/` 里逐条核过。图中央那条横向三段链路就是 cloud 接 tier2 的真实姿势——game-cloud 经 REST `POST /sessions` 建一个会话,再经 SSE 收事件流;tier2 的单写 ReAct agent 跑在这层 service 之上。这么接的理由很直接:一个现成、已经把多租户和可恢复会话都做好的 service 摆在那里,自己重写一遍既慢又容易漏,不如直接用官方的,把力气省下来花在富游戏怎么造上。
图中段那排八个 router 和底下那行 MessageBus,是把「这层现成到什么程度」摆出来给人看清。八个 router——`/sessions`、`/chat`、`/schedule`、`/credential`、`/workspace`、`/model`、`/tts_model`、外加 agent 管理面——覆盖了一条生成线要用到的会话、对话、定时触发、模型密钥托管、执行环境、模型选择这一整套,全是框架现成件。底座是 MessageBus 的 Redis 实现,它撑起四样分布式协作能力:Redis Stream 做事件 replay 日志(晚到的订阅者能回放、不丢中间过程),pub/sub 做唤醒广播(worker 干完异步通知 leader、不必轮询),分布式锁保证同一会话同一时刻只被一个进程跑,取消信令支持中途叫停。这套底座直接决定了那条绿色的「SSE 可恢复」要点带能成立——cloud 收事件流时掉了线,重连后从 replay 日志续读、不必从头重跑,这正好兜住了「外部服务 / 异步任务要考虑超时、失败、断线」这条可靠性要求。一次富游戏生成可能跑很久,断线重连不丢进度是必须的,而这件事官方已经给了。
图底那三个并排的 session 加载路径,是这张图最该让人看出的设计取舍。tier2 把「游戏 = 长生命周期软件项目」这条基座裁定落到了运行时入口上:一款游戏不是一次性产物,所以进场方式不止「从零新建」一种。路径①是续接之前的会话,从 `SessionRecord.state` 取回历史工作记忆与轮次,接着往下 reason;路径②是加载一份已有游戏工程,把 `Workspace.workdir` 指向那个已存在的目录,进迭代模式(改源不改包、重新构建);路径③才是新建,由阶段 2 的模板初始化工具铺一套空骨架,再交单写 agent 写代码。关键在于这三条路最终汇到同一个 Agent 装配点——它们不是三套独立流程,只是初始 state / workdir 不同。把三种入场收敛到同一个装配点,而不是为每种入场各写一条链路,是这张图背后的简化决策。要注意的边界:整条 tier2 轨待 spike,这三条加载路本身都是 tier2 待建(虚线),而它们脚下的官方 Agent Service 与 MessageBus 是现成实线——接线待建,底座现成,这条虚实之分是读这张图时最该先盯住的一层。
### 图 A2 · Agent 非常驻 + AgentState + checkpoint
![图 A2 Agent 非常驻 + AgentState + checkpoint](assets/t2-A-02-Agent非常驻与AgentState.svg)
这张图回答「tier2 的 agent 实例到底怎么活、断了怎么续」。结论先放在最前面:agent 是一个无状态的 ReAct 引擎,实例非常驻——它不是一个长跑的进程,而是每次 run 现组装、跑完即弃,不留活对象。图左侧那条「现组装 → 跑 ReAct 多轮 → 跑完即弃」的生命周期三态画的就是这件事:构造时持有 model / toolkit / middlewares / state,跑若干轮 ReAct(`cur_iter` 自增),跑完对象就销毁了。这不是一个随意的实现选择,而是 AgentScope 2.0.2 service 化的天然形态——service 要多租户、要能横向起多个进程处理不同会话,就不能让 agent 在某个进程的内存里长期活着。
图左下角那条「关键推论」是整张图的逻辑枢纽:既然没有常驻进程可以序列化,所有「下一轮还要用」的东西就都得外置到一份可持久化的状态里。这就引出图中段那个实线框的 AgentState——它是 AgentScope 2.0.2 自带的 pydantic 模型(源码 `agentscope/state/_state.py`),三个核心字段把一次生成的全部活记忆装了进去:`context` 是工作记忆,即喂进 LLM 的未压缩对话上下文;`cur_iter` 是当前轮次,记 ReAct 循环走到了第几轮;`tasks_context` 是任务清单,即拆解后的子任务表。图里那条蓝色说明带点出了 goal 和进度是怎么追踪的——目标来自输入(brief / play_spec / GDD),进度靠 `tasks_context` 逐项 TaskCreate / TaskUpdate 追踪,像一张会随生成推进不断勾选的 todo list。AgentState 是官方现成的(实线),tier2 直接用它装状态,不自己另造一套状态模型。
图右侧那个虚线框的 checkpoint,是这张图最该破除的一个直觉误解:checkpoint 不是去序列化一个活对象。直觉上「保存进度」像是把正在跑的 agent 冻起来存盘,但 agent 非常驻、根本没有活进程可存——checkpoint 在 tier2 里的真实含义是存 / 取那份 AgentState。它配 AgentScope 2.0.2 现成的 StorageBase 抽象、用 RedisStorage 落 Redis(StorageBase 实线现成,每轮 checkpoint 这件 tier2 接线虚线待建)。两条硬约束写在右下角:一是每轮 checkpoint,`cur_iter` 每自增一轮就落一次 state,粒度细到能从任意一轮断点续上;二是半轮副作用须幂等无脏,因为续跑可能在一轮的中间断掉重来,如果半轮里那些副作用不幂等,重跑就会脏数据,所以这是一条要在长程一致性里专门采集字段去验的可靠性约束。
图底那条蓝带把「非常驻 + AgentState」这条机理直接接到了实用价值上:它决定了 tier2 怎么做断点续跑。resume 不是什么特殊机制,就是图 A1 里 session 路径①的具体落实——从 StorageBase 读回上次的 AgentState,把它作为初始 state 注入新组装的非常驻引擎,引擎据此重建工作记忆与 `cur_iter`,然后从那一轮续接 ReAct 循环往下跑,已经完成的 TODO 不重做。一句话:非常驻不是缺陷,而是把「断点续跑」从难题变成了「读回 state 再装配」这件平常事。本图是总览图 3(运行时内部)和图 8(类图)的逐字段放大——总览把 Agent 与 Workspace 的整体结构画在一处,本图只取非常驻、AgentState 三字段、checkpoint 存 state 非存活对象这一面展开,Agent 与 Workspace 的双轴关系见图 A3,本图不重画。
### 图 A3 · Workspace 双轴注入
![图 A3 Workspace 双轴注入](assets/t2-A-03-Workspace双轴注入.svg)
这张图存在的唯一目的是纠一个会带歪整个 tier2 设计的误解:很多人第一眼会把 Workspace 理解成「一个 environment,agent 跑在它里面、被它包着」,照这个理解去设计,数据面和注入关系全会接反。实情正相反——Workspace 是执行环境,它沿两条轴注入 agent;agent 持有 Workspace 的引用,不嵌在它里面。图的布局本身就在说这件事:左边是 agent 本体(无状态 ReAct 引擎,官方现成),右边是 Workspace(执行环境的真实载体,官方现成),中间两条箭头都是从 Workspace 指向 agent——注入的方向是 Workspace → Agent,不是 Agent 进 Workspace。
两条轴是这张图的主干。轴①是资源注入:Workspace 经 `get_toolkit` 把它内建的工具、加上 `list_skills()` 列出的 skills、加上 `list_mcps()` 列出的 MCP,汇成一个 Toolkit,注入到 agent 的构造参数里,成为 agent 能调用的工具集(源码 `app/_service/_toolkit.py:27`)。轴②是 offloader:Workspace 本身被当作 offloader 挂在 agent 上,agent 的上下文与工具结果卸载就挂到这个引用上(源码 `agent/_agent.py:104` 的 offloader 参数)。看清这两条轴,就能回答「那到底什么东西是『跑在 Workspace 里』的」——是 MCP 进程、是 skills(SKILL.md)、是 workdir 里那套多文件源工程,这些才真正在 Workspace 里运行;agent 本体不在里面,它只持一个引用,每 run 现组装、跑完即弃。这个区分不是咬文嚼字:它决定了 tier2 该把工具和资源挂到哪、该在哪埋观测点、隔离边界画在哪条线上。
图右下那排 WorkspaceBase 三实现,是这套抽象给 tier2 的隔离强度梯度。LocalWorkspace 跑本地进程,隔离最弱、最快;DockerWorkspace 用容器隔离,中等强度;E2BWorkspace 是远端沙箱,隔离最强。三者都是官方现成抽象,tier2 只是在其上挂自己要的工具与资源,不凭空自建一个 environment 数据面。图里把 spike 期的取舍标得很清楚:spike 就近用 Local / Docker 起手(最快、好调试),E2B 那种远端强隔离对齐的是更长期的多租户 / premium 层,用远期紫画、不是 spike 期投入项。
图左下角那个虚线框是这张图里唯一一处真正卡住 spike 的待裁项——D5 沙箱底座选型,标着「spike 前置阻断」。它要先回答一个承重前提:确定性硬门要的「真浏览器低层探针」(page.evaluate、navigate、screenshot、输入注入)在选定的沙箱上够不够用。候选 A 是 agentscope-runtime 的 BrowserSandbox,如果它把这些能力逐项暴露够了,就直接用它跑探针矩阵(当前全仓零引用,要先验);候选 B 是回落到自建薄沙箱加复用现行的 `play.cdp.cjs` 运行 / 截图 / 证据骨架(但那套探针硬编码了 LittleJS,要为 Phaser 重写)。这道结论必须在 spike 探针矩阵开跑前出来——出不来,矩阵根本跑不起来,所以它是属主标记的前置阻断项。图底那条蓝带把整张图收口成一句话:Workspace 注入 agent,不是 agent 嵌进 Workspace;tier2 不凭空自建 environment 数据面,在官方 Workspace 抽象上挂工具与资源即可,而「在哪跑、隔离多强」由沙箱底座(Local / Docker / E2B 与 D5 选型)解决。本图放大的是总览图 3 的双轴注入和图 8 的 WorkspaceBase 类图,不重画类图本身。
### 图 A4 · Middleware 洋葱
![图 A4 Middleware 洋葱(横切关注点挂载点)](assets/t2-A-04-Middleware洋葱.svg)
让 agent 自治生成游戏,光有 agent 和执行环境还不够——还得能在它跑的过程中刹住烧钱、超时硬切、把每一步都记下来,而且这些横切的事不能散落进业务逻辑里各写各的。这张图回答的就是这些横切关注点统一挂在哪。答案是 AgentScope 2.0.2 的 MiddlewareBase 洋葱:它在 agent 执行的若干钩子上层层拦截,每一层用 `next_handler()` 把下一层包在中间,外层裹内层,所以叫洋葱。tier2 把成本刹车、硬熔断、可观测全挂在这套洋葱上,不另起机制、不散落进业务代码——这是框架给的扩展点,直接用。
图左侧那组同心环画的是四个 onion 钩子,从外到内各裹住 agent 执行的一层。最外环 `on_reply` 是一整次回复的进出边界;往里 `on_reasoning` 裹推理 / 模型调用阶段;再往里 `on_acting` 只裹单次工具执行那层 I/O(`toolkit.call_tool`);最内环 `on_model_call` 拦截裸模型 API 调用——这一环是钱算在哪的地方,因为它能读到 `ModelCallEndEvent` 带的 token 用量(`input_tokens` / `output_tokens`),成本台账和 trace 的 token 都从这一环取。洋葱框外下方那个单独的框是第五个钩子 `on_system_prompt`,它和前四个不同,不是前后包裹,而是顺序变换——多个 middleware 串成一条管线,各拿上一个的输出去改 system prompt 串,所以它是 transformer 而非 onion。四个 onion 加一个 transformer 共五个挂载点,按源码 `_base.py` 逐条核过,未实现的钩子运行时自动跳过。看懂这五个钩子各裹哪一层,才能回答「某个横切关注点该挂哪个钩子」。
图右侧那三类挂在洋葱上的东西,是这张图最该让人一眼分清的现成与自建之别。第一类是官方 ReplyBudgetControlMiddleware,做预算软刹(实线现成):它挂 `on_reply` 加 `on_reasoning`,在 `on_reply` 里按 ModelCallEndEvent 累计加权 token,越阈值后在 `on_reasoning` 里注入收尾提示、并强制 `tool_choice=none` 让它收尾。但要看清它的天花板——它只数 token、不换算金额、更不硬拒,刹不住一个已经在烧钱的失控循环。第二类正是为补这个缺口而设的自建硬熔断(虚线待建):TimeoutMiddleware(超时硬杀本次生成)、StuckDetectMiddleware(卡死 / 无进展硬杀)、预算硬闸(token 乘 new-api 单价换算成 ¥ 累进台账,越硬上限即 fail-closed 抛错终止,不只注入提示)。它们同挂这套洋葱、不另起机制,官方软刹刹不住时由它们硬切;在这套硬熔断建成前,tier2 不规模化跑、只在严格时间盒的实验里跑。第三类是官方 TracingMiddleware,做可观测(实线现成):它把 typed Event 流(ThinkingBlock / ToolCall / ModelCallStart / End 等)转成 OpenTelemetry span 汇进 AgentScope Studio,tier2 写轨迹不必自己埋点,复用这条官方 Event→OTel 管道、再加一层 adapter 映射进统一 trace 契约即可。
图底那条与 D12 运行治理门的分工带,讲清了「洋葱」和「门」各管哪一段,避免把两者混成一回事或重复造。洋葱在 agent 进程内,管 per-run 的成本 / 超时 / 卡死 / 观测;D12 在生成任务入口前,管配额 / 并发 / 背压 / 降级——一个在进程里逐轮拦,一个在入口处先闸,层次不同。两者之间有一个清晰的衔接点:D12 那道「花到上限就拒绝执行」的预算闸,在 agent 内的落点正是同口径自建一个预算 Middleware、挂 `on_model_call`、订阅 ModelCallEndEvent。这就构成了图底那句三层成本强制——worker 本地预测先闸、D12 网关配额二闸、任务 deadline 三闸。要记一条现行边界:D12 现在只覆盖 SAA,tier2 接入的入口、预算扣减、并发释放、失败补偿还待补,这是 D12 的已知边界。本图放大的是总览图 8 关键类图里 MiddlewareBase 那一支——把 ReplyBudgetControlMiddleware(现成)、TimeoutMiddleware / StuckDetectMiddleware(自建)各自挂哪个钩子、做什么逐项展开,不重画类图。
## 3 图清单与状态表
| # | 图名 | 形式 | 状态 | 覆盖内容 |
|---|---|---|---|---|
| A1 | Agent Service 与 session 三路径 | SVG · 横向链路 + router 网格 + 三路汇聚 | 现(官方 Agent Service / MessageBus = 现成)/ 建(tier2 接线待 spike) | cloud 经 REST POST /sessions 建会话 + SSE 收流;八个 router;MessageBus 四能力(Redis Stream replay / pub-sub / 分布式锁 / 取消信令);SSE 可恢复;session 三加载路(续接 / 加载已有工程 / 新建)汇到同一 Agent 装配点 |
| A2 | Agent 非常驻 + AgentState + checkpoint | SVG · 三段结构 + resume 链路带 | 现(AgentState / StorageBase = 官方现成)/ 建(tier2 非常驻装配 + 每轮 checkpoint + resume) | agent 非常驻三态生命周期;AgentState 三字段(context / cur_iter / tasks_context)+ goal/进度追踪;checkpoint = 存 state 非存活对象、配 StorageBase 落 Redis;每轮 checkpoint 与半轮幂等两条硬约束;resume = 取回 state 装配续跑(对应 session 路径①) |
| A3 | Workspace 双轴注入 | SVG · 左右双轴 + 三实现 + D5 二分 | 现(Agent / Workspace / Toolkit / 三实现 = 官方现成)/ 建(tier2 注入工具集 + D5 沙箱底座)/ 缓(E2B 远端强隔离 = 更长期层) | 纠误解:Workspace 注入 agent、非嵌套;轴① get_toolkit 汇 Toolkit、轴② Workspace 作 offloader;真跑在 Workspace 里的是 MCP / skills / 文件;WorkspaceBase 三实现隔离强度梯度;D5 沙箱底座 spike 前置阻断(BrowserSandbox vs 回落 CDP) |
| A4 | Middleware 洋葱 | SVG · 同心环 + 右侧三类 + D12 分工 | 建(tier2 洋葱挂载待 spike;软刹 / trace 官方现成,硬熔断自建) | MiddlewareBase 4 onion 钩子(on_reply / on_reasoning / on_acting / on_model_call)+ 1 transformer(on_system_prompt);挂洋葱三类(官方预算软刹 / 自建硬熔断三件 / 官方 TracingMiddleware);三层成本强制;与 D12 运行治理门的进程内 vs 入口前分工 |
本文档的设计内容已并入生成运行时架构单一事实源 [`agentic运行时架构图说.md`](agentic运行时架构图说.md)(§5.1 运行时形态)。请以该 SoT 为准;本文件原始内容见 git 历史。

View File

@ -1,97 +1,9 @@
---
date: 2026-06-23
topic: tier2 细节图说 · B 族——控制面与管理面(生成引擎子树·tier2)
status: 设计中(tier2 待 spike) · 每图映射源档见该图脚注 / frontmatter 记 hash 作防漂移门
映射源档:
- path: docs/architecture/架构/生成引擎/agentic集成架构.md
hash: 1b3e375a
- path: docs/architecture/架构/生成引擎/SAA编排.md
hash: 1b3e375a
status: 已退役(2026-06-24 收敛进 SoT)
canonical: false
date: 2026-06-24
---
# tier2 细节图说 · B 族 —— 控制面与管理面
# (已退役)tier2 细节图说 · B 控制面与管理面
> 本篇是绘境AI 架构图集 **生成引擎子树** 下的 tier2 细节图说,只覆盖 **B 族(控制面四组件、反锁死五协议、预算三层强制、管理面三 phase)** 四张图。它是 [agentic 运行时架构图说](agentic运行时架构图说.md) 里「治理层」那一格的放大层——运行时图说把控制面当成一个盒子带过,这里把盒子拆开,逐组件、逐协议讲清它怎么同时管住两条异构生成线。
---
## 0 阅读约定与同步纪律
B 族讲的是平台拿什么治理两条生成线——SAA 廉价线(Tier0/1)和 tier2 自治富游戏线。治理这件事被拆成四个面:配置怎么管(配置注册表)、运行过程怎么看怎么审(观测/审计仓)、入口怎么拦(D12 与预算闸)、人怎么操作(管理面 UI)。四张图分别从四个角度切这同一层:B1 是四个组件的全貌加它们对两条线暴露的同一套契约,B2 是支撑「一份治理管两条异构线」的根本原则——反锁死五协议,B3 把四个组件里最容易被忽视的预算闸单独深挖(官方软刹刹不住烧钱循环、要自建硬熔断),B4 把管理面 UI 这一个组件展开成三 phase 的演进路线。
四张图映射两份属主设计档。控制面四组件、反锁死原则、预算闸、管理面三 phase、接口对称内容不对称,这五块的口径全部出自《agentic 集成架构:控制面与管理面》;反锁死五协议里的任务协议这一件,其精确边界(job/callback 契约 #6 暴露的 `GenerationDispatcher` 接口)出自《SAA 编排》第 4 节六条不变量的第五条与整图串行约束那节。frontmatter 记了这两份的当前 commit hash,作为防漂移门——其中任一份动了治理口径,本图说与对应 SVG 必须同步改。
B 族的状态分布比 D 族复杂,因为控制面不是 tier2 的专属物,它从属于「生成主线架构演进路线」(档内称线A),只在线A 之上做净增量,并且同时服务 SAA 和 tier2 两条线。所以同一张图里会并存三种成熟度:SAA 廉价线那部分(读配置、写轨迹、过 D12)是现行已落,D12 治理门和 Prompt Registry 的构建期快照部分是已有件复用,配置审计日志 / 注册表推广到 skill-tool-mcp / 统一 trace 契约 / tier2 整条接入则是待建。tier2 整轨本身待 0号 spike 验证、尚未落代码,这个前提对 B 族同样成立。
看图时认线型和颜色:实线框 = 现行已落或已有件复用;虚线框(短划线)= 待建净增量,或 tier2 待接;远期紫 = 明确「现在不投」的能力(典型是管理面 phase-3 的完整可视化建图),要等真有多编排 / 多 agent 团队需求、且某个深坑被填后才上。
## 1 全图通用图例
B 族用一套统一的徽章、线型和状态码,先在这里讲清,后面每张图不再重复。
**生产维度徽章**标的是一道能力服务哪个非功能维度:蓝色 **可观测**(看得见运行过程)、红色 **安全**(入口拦截 / fail-closed 截断)、灰色 **数据**(唯一事实源、数据口径)、绿色 **成本**(预算核算面)。一个组件可以同时挂多个徽章——比如观测/审计仓既是可观测面也是数据事实源。
**线型**承担状态语义。实线框 = 现行已落或已有件复用,实线箭头 = 现行已接的转移 / 关系;虚线框(短划线)= 待建净增量或 tier2 待接,虚线箭头 = 待建的流程或待接线。**远期紫块** = 明确登记「现在不投」的远期能力。
**状态码**四档,贯穿整个生成引擎子树:**现** = 现行已建在跑;**接** = 设计已出、代码待接线(或他分支已落、待合并对账);**建** = tier2 立项后要新建、当前待 spike;**缓/future** = 收窄后缓做或远期不投。B 族里 SAA 三件事和 D12 是「现」,Prompt Registry 构建期快照部分是「现 部分」,配置审计 / trace 契约 / skill-tool-mcp 推广 / tier2 接入是「建」,管理面 phase-2 是「缓」、phase-3 是「future」。
## 2 图集
### 图 B1 · 控制面四组件
![图 B1 控制面四组件](assets/t2-B-01-控制面四组件.svg)
让 agent 自治生成游戏这件事本身不够,平台还得能改 prompt 不发版、看见每次生成到底干了什么、查出谁改了哪条配置、在入口拦住失控的生成。控制面就是干这几件事的那一层,它同时管两条生成线。这张图把控制面的四个组件并排铺开,核心要让人看出一件事:四个组件对两条线暴露的是**同一套契约**——读配置、写轨迹、过门,生成线只管照契约办事,不感知管理面长什么样。
第一个组件是**配置注册表**,它是配置的唯一事实源,直接回应创始人那句「改 prompt、模型、skill、mcp 还要重新部署」。内核是把凡是现在「要改就得发版」的东西,都搬进一个版本化、可回滚的注册表。这里的现行真相要说清:今天的 Prompt Registry 不是「现成可热加载」的地基,它是构建期被 maven 插件复制进 classpath 的快照,改 prompt 等于改 `contracts/` 原文件、升版本号、过四道闸,下次构建部署才带新版——是 Git 版本化加构建期注入,不是运行时热加载,而且注册表里还有标着 STUB 占位的条目和一批硬编码加载、未必登记的模板路径。所以图里给它标的是「现 部分」,并且把「推广到 skill / tool / mcp」框成虚线净增量,旁边压着一条硬纪律:推广前先补一道一致性 CI(每条条目都有对应文件、每个硬编码 id 都登记、STUB 补正或标为不可上线)。这条纪律是有出处的——同目录的对抗审查给过一个对症范例:某个运行时能力本要四处人手同步,正确解法不是上重型注册表,而是加一道一致性测试逼你改齐。别在裂的地基上盖更大的注册表,是这个组件最该守住的设计取舍。
第二个组件是**观测 / 审计仓**,它回答两个独立问题,对应两条留痕线。一条是运行轨迹——每步推理、每次工具调用、每次 LLM 输入输出、每道门裁决、每次成本,落进统一的 `contracts/trace/` 契约。另一条是配置审计日志——谁、什么时候、改了哪条配置、前后 diff,这条线现在完全没有,是全新建的。把这两条线钉一起的价值很具体:任何一次生成都能反查它当时用的哪几条配置的哪个版本,才能回答「那批游戏质量掉了,是不是上周改的那条 prompt 干的」。配置审计还带一个要选边的分类决策(图里画出来了):prompt / models 这类版本化资产走 GitOps(改配置 = 建 PR,审计天然、可回滚),运营开关类(降级、配额数值)走 DB 直写 `infra_config`(即时生效、复用现成通路)。
第三个组件是 **D12 运行治理门**,它把配额、并发、背压、降级、记账骨架焊在生成任务入口前。它是已经存在的件,本设计只复用不改、默认关闭、零行为变更进主干。它有两点边界图里点明了:现在只覆盖 SAA,tier2 接进来的入口、预算扣减、并发释放、失败补偿还要补;它的记账是骨架不是真计费。这道门接上引擎那道「花到上限就拒绝执行」的预算闸,那是 B3 单独深挖的事。
第四个组件是**管理面 UI**,它是注册表和观测仓的视图与编辑器,不是真相本身——配置才是真相。它按三 phase 落,是 B4 单独展开的事,这里只在图里留一个三 phase 带做指引。
四个组件下方那条绿带,讲的是这套设计能成立的真正条件:接口对称、内容不对称。两条线都按「id 加版本」从注册表读配置(接口同),但 SAA 读 16 个节点的参数、tier2 读 ReAct 的工具集和轮数预算(内容异);两条线都按统一 trace 契约写轨迹,公共核心子集对称、扩展段各写各的;两条线都先过 D12,只是现在只覆盖 SAA。这条对称/不对称的边界,是「一份管理面管两条异构线」能成立的根。还有一处现实图里写明了,不写会误导人:tier2 的 eval / 灰度门现在不存在,所以它的配置改动当前只有「下次生效加轨迹留痕」两层保护、没有 eval 拦截——这不是设计缺陷,是 tier2 成熟度的现状。
### 图 B2 · 反锁死五协议(框架可替换)
![图 B2 反锁死五协议](assets/t2-B-02-反锁死五协议.svg)
当下生产线是 SAA-only(决策 HJ-AGI-002,经双评审与源码核验后选定),但选了 SAA 不等于被 SAA 锁死。这张图立的是一条比四个组件更根本的架构原则:平台不绑死在任何单一 agent 框架上。AgentScope / LangGraph / AutoGen 这些框架可以被借鉴、组合、在不同轨上分别采用(long-term 的 tier2 premium 轨就走 AgentScope),平台真正固化、不随框架走的,是协议层那五件东西——把它们钉牢,框架就被压到「只租用不拥有」的位置,换框架时业务不被连根拔起。
固化在协议层、与框架解耦的是五件。**任务协议**是生成任务怎么提交、怎么回调,边界就是 job/callback 契约 #6 暴露的 `GenerationDispatcher` 接口,这件是现行已落的实线锚点——换框架只换 dispatcher 实现,调用方一行不改。**状态模型**是一次生成的状态怎么表达、怎么续跑,checkpoint 的语义是平台自己的口径,不是某框架的私有结构。**工具接口**是 agent 能调哪些工具、入参出参长什么样,以契约定义,不绑某框架的工具注册机制。**验收门禁**是「完成」由确定性九门裁定、禁止 LLM 自评,这条是平台铁律(防 Goodhart:把度量当目标去优化,度量即失效),无论底下换哪个框架,出题与被考不同源这条都不让步——它也是现行已落的实线锚点(九门由廉价线已建)。**遥测事件**落进统一 trace 契约,公共核心子集对称、扩展段各写各的,采集机制各框架来、数据口径是平台的。这五件里,任务协议和验收门禁是「现」,状态模型、工具接口、遥测事件是「建」,随 0号 spike 与控制面分期落地。
这张图最该让人记住的不是这五件本身,而是它给出的**正反两个判据**,图中间两条带画的就是这个。正向判据:long-term 要不要把某条轨从 SAA 切到 AgentScope,就看这五件是不是仍稳稳钉在协议层——是,切换就只是换一个被治理的执行后端,不是推倒重来;SAA(裸 StateGraph)和 AgentScope(自治 ReAct)这两套异构范式,正是靠「协议层对称、执行后端可换」才能共存。反向判据:如果哪天发现某个框架的私有结构悄悄渗进了这五件中的任何一件(比如轨迹格式被某框架的 span 结构绑死、任务协议泄漏了框架的内部状态),那就是锁死的开始,要立刻在协议层把它隔离回去。这也解释了为什么整个治理层反复强调「管理面只渲染运行时自报的拓扑、绝不另持一份拓扑模型」——那同样是反锁死,管理面里硬编码的框架拓扑,换框架时就是阻力。这条原则不是空谈「将来好换」,它有具体落点,就是上面那条切轨判据。
### 图 B3 · 预算三层强制(软刹 vs 硬熔断)
![图 B3 预算三层强制](assets/t2-B-03-预算三层强制.svg)
引擎设计里那道「花到上限就拒绝执行」的预算闸,现在不是现成能力,这张图把它单独深挖。要先看清官方给了什么、缺什么,图上半部分就是这个对照。AgentScope 2.0.2 自带 `ReplyBudgetControlMiddleware`,但它只做软刹——累计加权 token 到阈值后,往 Agent 上下文注入一句提示(别再调工具、收尾)、并把下一步的 `tool_choice` 强制成 `none` 让它结束,它只数 token、不换算金额、更不会硬拒。这对失控成本不够:tier2 自治多轮 ReAct 一次失控循环能烧掉数美元,软刹只是「提醒它别再调工具」,刹不住一个已经在烧钱的循环——提示不等于截断。
所以硬熔断和金额台账都得自建,而且自建有现成载体——同样是 AgentScope 的中间件洋葱。图右半部分画的是这个自建件:在 `on_model_call` 这类钩子上拦截,订阅 `ModelCallEndEvent`(源码已确认这事件带每次模型调用的 token 用量),把 token 乘上 new-api 的单价换算成金额累进台账,一旦越过硬上限就直接抛错 fail-closed、终止本次生成,而不是像官方那样只注入提示。金额台账加硬拒这两件,正是官方软刹做不到、故必须自建的。软刹和硬熔断这两种处置力度叠在同一个中间件洋葱里——一个数 token 提醒,一个折金额硬拒。
图下半部分是三层强制,讲的是成本不靠单点,三道闸纵向叠、任一道先到上限即拦。第一道是 worker 本地预测闸:动手前先估这一步要花多少,越本局预算就不发起调用,是最便宜的一道,在调模型之前就拦下,位置在 tier2 worker 进程内。第二道是网关配额闸,就是 D12 那道已有件,配额 / 并发 / 背压 / 降级 / 记账骨架焊在入口前,现仅覆盖 SAA,tier2 接入面和「记账从骨架变真计费」是待补的。第三道是任务 deadline 闸:超过墙钟时间盒即停,兜住「token 没烧穿但卡在长循环里」这种纯耗时的失控,和 token / 金额上限互补。这三道之间还有一个要选边、不能既要又要的决策图里写明了:token 折金额这一步若取价通路不可达,要么直接失败(保守、宁停不超支),要么降级到纯 token 上限兜底,落地时显式定一条、不留模糊。
图底那条红铁律是整张图的落点:这套三层强制预算落地之前,tier2 只在严格时间盒的实验里跑,绝不规模化——自治多轮不强制就会烧钱,软刹刹不住。需要留意一处分期边界:这道预算闸只是 C 族「四道熔断」总览里的其中一道,本图只深挖预算这一道加 D12 网关配额;spike 阶段成本熔断可以先只挂官方软刹,硬熔断与三层强制到建设期再补全。
### 图 B4 · 管理面三 phase + 接口对称内容不对称
![图 B4 管理面三 phase](assets/t2-B-04-管理面三phase.svg)
管理面 UI 是注册表和观测仓的视图与编辑器,不是真相本身——这是创始人最在意的那半边,「Dify 式可视化 agent 平台管理」:改 prompt / 模型 / skill / tool / mcp 不发版、可视化、可追踪、可审计、不要死代码。这张图先把一个设计选择定下来(顶部那条蓝带),再把管理面按三 phase 铺开。
设计选择是:不从零造一个 Dify 式可视化建图器,而让配置成为真相、UI 只做配置加轨迹的视图与编辑器。三个理由图里列了:前期平台调研已拍板「维持现有 SAA / AgentScope、不迁可视化平台」;裸图的节点是 Java 代码、ReAct 的工具也是代码,真相在代码和配置里,靠 UI 拖拽生成代码是另一套不可靠的范式;还有那条别过度工程的纪律。但「配置是真相、UI 是视图」不等于「第一期什么都不做、把可视化全推远期」——管理面按三档落,而且诚实命名。
phase-1 叫「配置管理加运行可观测」,不叫「可视化编排」,因为这期还没有真的图编辑,叫编排名不副实。这一档最该被看见的是它含一个**纯只读、不依赖线A 任何一步、立即就能兑现**的切片:看各角色当前用什么模型、回放任意一次生成的全轨迹、按 new-api 的 quota 口径对账成本。这三件事现在就能做,是立刻能给创始人看见的最小兑现,图里把它画成实线、挂可观测徽章,正是这个意思。其上再加配置编辑(虚线、待建):在 UI 改 prompt / 换模型 / 调阈值,改的是注册表条目,改完落 Git 加审计日志,不是直接热生效。phase-2 是「限定范围图编辑」(缓),把节点启停、边开关、模型 / 工具绑定、配置版本 diff、按轨迹回放排进来;「限定范围」指它只编辑节点参数和启停,渲染的拓扑来自运行时自报,不让用户拖拽新增节点改结构。phase-3 是「完整可视化建图」(远期紫、不投),拖拽改拓扑要等两个前置条件齐了才上:真有多编排 / 多 agent 团队的需求,而且「拓扑配置化」那个深坑(运行时重建图)被填了。图里特地点明,phase-3 这个拓扑可视化是给我们自己运维用的,不是给 C 端创作者编 workflow 的产品能力——后者另登记一条未来需求,tier2 自身靠 loop 加 task/goal 加 Team 调度即可,不靠 DAG。
图下半部分是两条贯穿管理面的约束。左边那条是「只渲染运行时自报的拓扑」——数据源是运行时轨迹实际走到哪个节点,绝不在管理面这侧另持一份拓扑模型。这条约束的根在于 SAA 是静态 StateGraph、tier2 的 ReAct 没有静态图,两套异构范式要用同一个管理面呈现,只能靠「渲染运行时自报」这个共同口径;它同时也是反锁死(换框架时,管理面里硬编码的拓扑模型反而成阻力),和 B2 那条原则是一回事。右边那条把「接口对称、内容不对称」逐件拆给了三行:读配置(接口按 id 加版本加载,内容 SAA 读 16 节点参数、tier2 读 ReAct 工具集加轮数预算)、写轨迹(接口按统一契约写,公共核心子集对称、扩展段各写各的 JSON 列)、过治理门(都先过 D12,SAA 已覆盖、tier2 入口待补)。图底那条红带写明了那处不写会误导人的现实:tier2 的 eval / 灰度门是 tier2 实验的产物、现在不存在,所以「配置改动要过 eval 门」对 tier2 现在是空头支票——eval 门建成前,tier2 配置改动只有「下次任务生效加轨迹留痕」两层保护,没有 eval 门拦截;SAA 廉价线则已落 D12 加 Git CI 校验,保护更全。
## 3 图清单与状态表
| # | 图名 | 形式 | 状态 | 覆盖内容 |
|---|---|---|---|---|
| B1 | 控制面四组件 | SVG · 两线 lane + 四组件容器 + 契约带 | 现(D12 / Prompt Registry 部分已落)+ 建·接(配置审计 · skill-tool-mcp 推广 · tier2 接入待建) | 配置注册表(唯一事实源 · 现 STUB 缺口)/ 观测审计仓(运行轨迹 + 配置审计 GitOps vs DB 直写)/ D12 治理门(已有件复用)/ 管理面 UI(三 phase 指引);接口对称内容不对称;tier2 无 eval 门现状 |
| B2 | 反锁死五协议(框架可替换) | SVG · 五协议卡片 + 正反判据带 | 建(原则 · 协议层固化;契约 #6 已存在,余随分期落地) | 任务协议 / 状态模型 / 工具接口 / 验收门禁 / 遥测事件五件与框架解耦;切轨 = 换被治理后端(正判据)/ 私有结构渗入 = 锁死开始(反判据);框架被压到「只租用不拥有」 |
| B3 | 预算三层强制(软刹 vs 硬熔断) | SVG · 两框对照 + 三闸纵向 | 现(官方软刹 + D12 已有)/ 建(硬熔断 + 三层强制自建待落) | 官方 ReplyBudgetControlMiddleware 软刹(只数 token、不折金额、不硬拒)vs 自建预算 Middleware 硬熔断(订阅 ModelCallEnd 折金额、越上限 fail-closed);三层强制(worker 本地预测 / D12 网关配额 / 任务 deadline);取价不可达策略;强制建成前 tier2 不规模化 |
| B4 | 管理面三 phase + 接口对称内容不对称 | SVG · 三 phase 横向 + 两贯穿约束 | 接(phase-1)/ 缓(phase-2)/ future 不投(phase-3) | 不造 Dify 建图器的设计选择;phase-1 配置管理加可观测(含纯只读立即兑现切片)/ phase-2 限定范围图编辑 / phase-3 完整建图远期不投;只渲染运行时自报拓扑;接口对称内容不对称三件;tier2 无 eval 门现状 |
本文档的设计内容已并入生成运行时架构单一事实源 [`agentic运行时架构图说.md`](agentic运行时架构图说.md)(§5.2 控制面与配置热取)。请以该 SoT 为准;本文件原始内容见 git 历史。

View File

@ -1,91 +1,9 @@
---
date: 2026-06-23
topic: tier2 细节图说 · C 族——单写 ReAct 循环 / M3 原生接法 / 四道熔断 / 多轮范式承 amodel
status: 设计中(tier2 待 0号 spike 验证、尚未落代码)
映射源档:
- { doc: "docs/architecture/架构/生成引擎/自治富游戏引擎.md", hash: "4858e6cf" }
- { doc: "docs/architecture/架构/生成引擎/tier2实现详设.md", hash: "d5efe45f" }
- { doc: "docs/plans/2026-06-22-003-feat-tier2-富游戏自治生成线-plan.md", hash: "ccb00bb9" }
- { doc: "tier2/HANDOFF.md", hash: "ccb00bb9" }
status: 已退役(2026-06-24 收敛进 SoT)
canonical: false
date: 2026-06-24
---
# tier2 细节图说 · C 族 — 谁在写富游戏
# (已退役)tier2 细节图说 · C 单写 ReAct 循环
C 族四张图围着一个问题转:tier2 要造现有廉价线做不出来的多系统富游戏,那么到底是谁在写、用什么模型写、写的时候被什么关着、这套写法又是从哪条已经跑通的路演进来的。图 C1 画清写者的形态——一个单写者 ReAct agent 在三层校验门内自治迭代;图 C2 画清这个 agent 怎么把中等档的 MiniMax-M3 接对;图 C3 画清自治多轮被四道熔断关在笼子里;图 C4 把这套写法回溯到它的两个 working reference,说明它是 fork 演进而非推倒重来。
整个 C 族对应的是一条尚未落代码的设计轨。tier2 的中心赌注——便宜或中等模型的自治 agent 能不能稳定写出富游戏里那一大块表现层——要靠一个叫 0号 spike 的最小验证来证伪或证成,过了这道生死门才进建设。所以读这四张图时,要把它们当成「计划怎么干」的施工图,而不是「已经在跑」的现状图。
## 0 阅读约定与同步纪律
C 族把生成引擎子树里三份设计档画成图:写者形态与四道熔断出自《自治富游戏引擎》的「谁在写:单写者 ReAct + 四道熔断」一节;运行时形态、建设五步、模型矩阵出自《tier2 实现详设》;与 A-model 分支的复用边界出自 tier2 富游戏自治生成线的实现计划与 `tier2/HANDOFF.md`。frontmatter 记了这四份的当前 commit hash 作为防漂移门——设计一变动,本图说与对应 SVG 必须同步更新,hash 对不上就说明图已落后于源档。
四张图整体状态都是「建」:tier2 整轨待 0号 spike 验证、尚未落代码。但「待建」不等于「全凭空想」。这套写法承袭了两条已经在跑的真实参照系——现行 WG1 廉价线的 `studio.py`(单轮调用加外层重试)和 A-model 分支的 `amodel-gen/gen.mjs`(在 M3 上已跑通的多轮 ReAct)——所以图里凡是承袭这两条已落机制的部分画实线,tier2 专属、要新写的部分画虚线。这条虚实之分贯穿全族,是读图时最该先盯住的一层:实线 = 现行已落或官方现成、可以直接信;虚线 = tier2 待建、还要写还要验。状态码就四个——现(现行已落)、接(待接线)、建(待建)、缓(收窄后缓做),C 族绝大多数元素是「建」。
## 1 全图通用图例
四张图共用一套视觉语言。**实线箭头/实线框**表示承袭现行已落的机制:九门 harness、new-api 计费平面、`studio.py` 单轮形态、`amodel-gen` 多轮范式都属此类,它们是已经跑过的 working reference。**虚线箭头/虚线框**(`stroke-dasharray 6 4`)表示 tier2 专属的待建新机制:Agent 内多轮 ReAct、M3 原生接法、thinking 分离、三道自建硬熔断、Phaser 富游戏产物,都还没落代码。**红线**专用于四道熔断——任一道先触发即停本局生成。
徽章标的是这块内容服务哪个生产维度:绿色的**质量**(造出真能玩的富游戏)、**可靠**(稳定收敛不靠运气擦边)、**成本**(压住自治多轮的烧钱)、蓝色的**可观测**(每步推理动作观察可对账)。状态徽章里,紫底的「待建」「tier2 待建」标 tier2 自己要写的部分,深灰底的「现行已落」「working ref」标可直接复用的参照系,橙底的「best-effort 非硬闸」标一处已知没焊死的设计缝。
## 2 图集
### 图 C1 · 单写 ReAct 循环〔SVG·建〕
![图 C1 单写 ReAct 循环](assets/t2-C-01-单写ReAct循环.svg)
这张图回答「谁在写富游戏、以什么节奏写」。写者是一个单写者 ReAct agent。ReAct 指 agent「想一步、调一个工具、看结果、再想下一步」的循环——图中央那个 reason→act→observe 的三角就是这个节奏,它的反面是「一次把整个游戏的答案写完」。富游戏工程跨十几个源文件、多个系统互相接线,一次写完几乎不可能对,所以必须让 agent 每观测一次就回头重想,在循环里把游戏逼出来。
图左侧的两态并列是这张图最该让人看出的东西:tier2 和现行 WG1 廉价线的真正差别,不在 prompt、不在生成域,而在「多轮自治放在哪一层」。WG1 现行用的是 `ReActConfig(max_iters=1)` 的单轮调用,加一个外层 `for attempt in range(max_repairs)` 的重试循环——生成一次,外层把错误喂回去重跑一次,单写、单轮。它故意这么做,是为了压住便宜档的单价:L1 生产主线明确不引 AgentScope,因为框架本身的 token 膨胀会吃掉便宜档那点利润空间。tier2 则把 `max_iters` 放开(AgentScope 2.0.2 默认 20),把「多轮」从外层 repair 循环搬进 Agent 内部的 ReAct 循环——同一套多角色 prompt 和生成域躯干复用,但自治的位置变了。这就是为什么左下角那块 tier2 态画虚线、还标着「独立锁 AgentScope 2.0.2、独立演进」:它从 WG1 fork 起步,但锁自己的框架版本、独立往前走,不强求两条线跟同一份 AgentScope。
中下那个虚线笼子是另一处要点——agent 的自治不是放任。它被关在一圈验收门里迭代:读引擎文档和资产 → 写源文件 → esbuild 构建 → headless 无头快检 → 九门 L1 真浏览器真玩 → 读 verdict → 没过就改、改完回到写源。这里有一条画成实线的关键节点:九门 L1 那一格。一款游戏算不算过关,不靠模型自己说,靠在真浏览器里真玩一遍——这套确定性判定继承自现行九门,铁律是绝不让 LLM 给自己打分。LLM 一旦进验收当裁判,会学会把游戏优化成「让裁判说好」而不是「真的好」,门就废了。所以图里九门那一格是实线(承袭现行已落),而它前后的写源、改、快检都是虚线(tier2 内循环待建)——确定性的判定承袭旧机制,自治的写法是新的。
右上角那个红框是四道熔断,任一触发即停:步数硬顶、预算闸、卡死探测、双层超时。其中预算闸现在还不是强制硬闸——这是一处已知设计缝,详见图 C3。
底部那条铁律带讲清「单写者」这三个字的边界,很容易误读。单写者只指阶段 2 写代码这一环:只有一个 agent 持有整个工程的全局视图,它一个人写,不并行拆给多个 agent。坚持这点是因为经营游戏的多个系统共享同一套状态和约定,拆给并行 agent 几乎必然不一致——一手教训是曾经把「造一个 Flappy Bird」拆给并行子 agent,结果背景跑成了马里奥。但阶段 1 的工作室设计仍然用 Agent Team 星形多 agent 去发散(玩法、关卡、数值、UI、音乐、特效各路 worker),收敛成设计结论后才进阶段 2 的单写。设计要发散和专业分工用多 agent,实现要防写冲突收敛到单写——所以「单写」专指写代码那一段,不是说整轨只有一个 agent。
### 图 C2 · M3 Anthropic 原生接法〔SVG·建〕
![图 C2 M3 Anthropic 原生接法](assets/t2-C-02-M3原生接法.svg)
这张图回答「中等 agentic 档的 MiniMax-M3 怎么接进 tier2 的 agent」。主链路从左到右一条线:tier2 agent → AnthropicChatModel → AnthropicCredential.base_url → MiniMax-M3。读这条链要抓两个事实。一是接法本身可信——它走 AgentScope 2.0.2 的官方模型类 AnthropicChatModel,构造时不传 formatter 就会自动配对一个 AnthropicChatFormatter,无需手接,这套已经逐条核过 2.0.2 源码。二是出口统一——AnthropicCredential 的 base_url 指向 new-api 的 Anthropic 端点(即 `/v1/messages`),模型计费统一从 new-api 这一个平面走。链路上的箭头里,agent 到模型那几跳画虚线(tier2 待建),但 new-api 计费出口画实线(现行已落)。
中间三个要点框讲「用对 M3」要满足的三件事,它们合起来定义了 M3 的正确用法。其一,走 Anthropic 原生协议加 agentic 工具循环——不是 OpenAI 式的单次 JSON 填空,而是让 agent 在循环里调工具写源、build、跑门、读 verdict、改,多轮自治收敛。其二,thinking 分离:开 `thinking_enable` / `thinking_budget` 拿到推理与答案分离,这里有一条硬约束——max_tokens 必须严格大于 thinking_budget,budget 缺省取 max_tokens 的一半;如果把 budget 设得不小于 max_tokens,2.0.2 会自动把 max_tokens 抬到 budget+1024,但别依赖这个兜底,显式设对。其三,完整 response 和历史保留:每轮把 thinking / text / tool_use 所有块原样回传入历史,不剥、不丢、不只抠 JSON——这样 M3 的交错思维(Interleaved Thinking)才不失效,才发挥得出最佳 agentic 性能。
图中间那条红框是这张图的命门,也是为什么它值得单独画一张:旧用法是 60% 失败的最深根因,而且是用法错、不是模型天花板。错用法等于三连违反——关掉 thinking、单次 JSON 填空、失败就从头重生成。把一个 agentic 加交错思维的模型当成一次性填空机来用,等于把它最强的那部分能力全关掉了。这处误读还连着另一处:max_tokens 是「输出上限」、不是 512K 的上下文窗——输出可以抬到小于 128K 给 thinking 加答案用,输入窗 512K 不受它限。
底部那条绿带收口一个设计原则:模型统一从 new-api 出口走,但协议和 SDK 不锁死,每档走它各自最优端点。M3 之所以特意走 Anthropic 原生端点,是因为「用对它」要的就是原生 thinking 加 agentic 工具循环;便宜档(deepseek-v4-flash / v4-pro)照旧各自端点。不论走哪条端点,计量都收口到 new-api 的 quota 口径,折算成每款多少钱——一个计费平面,成本可对账。这是「all roads 收口到一个计费平面」的具体含义:端点可以多样,账只有一本。
### 图 C3 · 四道熔断 + 预算闸〔SVG·建〕
![图 C3 四道熔断 + 预算闸](assets/t2-C-03-四道熔断.svg)
这张图回答「自治多轮会不会失控、靠什么关住」。agent 在三层校验门内可以任意迭代,但被四道熔断关在笼子里,任一道先触发即停本局生成。四道分别是:步数硬顶、预算闸、卡死探测、双层超时。读这张图最该看出的,是它们的来源和成熟度并不一致——这正是图里实线虚线混用的原因。
步数硬顶防的是「靠运气擦边」。它给每系统的构建-修复定上限、给整局定 max_iters(建议单系统不超过 8 轮、整局不超过 40 轮,超 max_iters 即判不收敛),走的是 AgentScope 的 `ReActConfig.max_iters`。它要防的不是慢,而是模型靠多轮反复试错把门擦边混过去——擦边过门和真收敛是两回事,没有步数顶就分不开。这道是 tier2 自建、待落,画虚线。
预算闸是四道里唯一画实线的——它用官方的 ReplyBudgetControlMiddleware,是现成中间件,在 on_reasoning / on_reply 钩子里按预算优雅收尾。但这恰恰是这张图最该被 review 的设计缝:它是软刹,不是强杀,现在还只是 best-effort,不强制截断。图里专门给它配了橙底「best-effort 非硬闸」徽章,又在底部拉了一整条警示带说清后果——自治多轮一旦不强制,会在那 56% 的表现层上烧钱发散。强制硬闸的完整口径归《agentic 集成架构》那份,规模化(premium 量起来)之前必须落地,否则成本不可控。这条缝在 spike 阶段可以容忍:先只挂官方软刹把路跑通,三道自建硬熔断加强制硬闸到建设期再补,不阻塞 spike。
卡死探测和双层超时都是 tier2 自建、待落(虚线)。卡死探测在语义层判 agent 是不是原地打转、无实质进展,而不是单纯计步——单纯计步拦不住「换汤不换药」的空转。它还要防一种更刁的逃避:agent 改 driver 来绕过卡死探测,这是 Goodhart 问题的一个具体形态(优化代理指标反而偏离真目标)。双层超时是两层墙钟上限——单步钉死一次工具调用或一轮推理,整局钉死本局总时长,兜住单步挂起和整局拖死两种失控。
机制上,这四道不是散落各处,而是叠在 AgentScope 的中间件洋葱里:官方软刹加三道自建硬熔断(各做成 MiddlewareBase 子类)一层层套进 ReAct 循环,软刹优雅收尾、硬熔断强制截断。底部那条退路带把 spike 阶段的可行起点钉死——先只挂官方 ReplyBudgetControlMiddleware 软刹跑通验证就够了,强制硬闸和三道自建硬熔断到建设期再补,不让一道还没建的硬熔断挡住最该先证的那个赌注。
### 图 C4 · 多轮自治范式(承 amodel-gen)〔SVG·建〕
![图 C4 多轮自治范式 承 amodel-gen](assets/t2-C-04-多轮范式承amodel.svg)
这张图回答「tier2 这套多轮自治写法是凭空发明的吗」——不是,它是从两条已经跑通的真实参照系演进来的。图上方那条三态演进带从左到右画清这条血脉:① WG1 的 `studio.py` 是现行已落的单轮加外层 repair 形态(实线);② A-model 分支的 `amodel-gen/gen.mjs` 是已经在 M3 上跑通的多轮 ReAct(实线,working reference);③ tier2 是要在 Phaser 上造富游戏的多轮自治(虚线,待建)。①到②标「参照系」、②到③标「fork 演进」——tier2 是 fork 起步、独立演进,不是返工。
中间那三块是 amodel-gen 留给 tier2 的三件可复用范式,每件都配了一个紫色虚线小条,标明 tier2 在哪个工作单元复用它。done 门——agent 自己 reason 判「够了」才终止,不靠外层固定轮数,tier2 在 U2 复用为多轮 ReAct(finish 工具收尾吐出 src/ 源工程)。check/build 快反馈——每轮先跑便宜的检查再决定下一步,拿快反馈喂下一轮而不是等重门才纠错,tier2 在 U7 复用为「写源 → build → headless 快检 → L1 门」的闭环。历史压缩——多文件富游戏迭代轮数多、上下文长,压缩历史让长程多轮不撑爆窗口,tier2 在 U7 复用为长程一致性(12 个以上文件的工程过程中无 checkpoint 丢失)。
图底那条蓝带是这张图最该读透的部分,它讲清一处极易被误导的边界——「fork 演进」不等于「整轨是平移」。tier2 复用 studio 那套多角色 prompt 和生成域躯干,把「多轮」从外层 repair 搬进 Agent 内部 ReAct,这层确实是 fork 加版本钉级别的轻活:种子 `studio.py` 用到的 AgentScope 框架接缝(6 个符号)在 2.0.2 全兼容,零签名改。但别被「6 符号兼容」误导——studio.py 拖着的 `validate.py` / `run.py` / `prompt.py` / `roles.py` 是 LittleJS 专属,按 Phaser 重写、工作量约等于全新写(net-new)。最典型的是 validate 的硬规则:它对 LittleJS 要求「禁 import、单 export default 工厂」,这套规则对 Phaser 多文件源工程完全反转。所以真正的功夫不在框架接缝,而在这些重写面加 net-new 的多轮 ReAct / Workspace / Service(Phase B)。
这条边界还连着一处口径的精确化(创始人 2026-06-23 裁):两线分立、不收敛。旧的边界叫「能力档(声明式 vs 自治)」,但 A-model 分支已经让廉价线也走向自治写真 JS,所以旧口径过时了。新口径是「引擎 / 表现层复杂度档」——A-model 吃 LittleJS 轻量经营档,tier2 吃 Phaser 重表现富游戏档。两条线各吃各的市场、各跑各的运行时,运行期零依赖。amodel-gen 合并 dev/2.0.0 之后,tier2 的循环范式以合并后版本为参照系重取,种子仍是 Python 的 `studio.py`,amodel-gen 只作 JS 参照、不照搬。
## 3 图清单与状态表
| # | 图名 | 形式 | 状态 | 覆盖内容 |
|---|---|---|---|---|
| C1 | 单写 ReAct 循环 | SVG | 建(演进自 WG1 studio.py) | reason→act→observe 三角;WG1 单轮+repair vs tier2 内多轮两态并列;验收门笼子(读文档→写源→build→快检→九门 L1→verdict→改);九门实线承袭、自治内循环虚线待建;单写者只指阶段 2、阶段 1 仍多 agent;底部四道熔断红框引子 |
| C2 | M3 Anthropic 原生接法 | SVG | 建(接法走官方 AnthropicChatModel,2.0.2 已核) | 主链路 agent→AnthropicChatModel→base_url 指 new-api→MiniMax-M3;用对 M3 三要点(原生协议+工具循环 / thinking 分离 / 完整 response 保留);红框警示旧用法=60% 失败最深根因;new-api 统一计费平面、协议不锁死 |
| C3 | 四道熔断 + 预算闸 | SVG | 建(软刹官方现成、三道硬熔断+强制硬闸自建待落) | 四道熔断(步数硬顶 / 预算闸 / 卡死探测 / 双层超时);预算闸=官方软刹实线 best-effort、非强制硬闸(设计缝);卡死探测防 Goodhart;中间件洋葱叠加机制;spike 阶段先只挂官方软刹的退路 |
| C4 | 多轮自治范式(承 amodel-gen) | SVG | 建(范式承 A-model 已落、tier2 fork 演进待建) | 三态演进带(studio.py 单轮 → amodel-gen 多轮 → tier2 Phaser 富游戏);三件可复用范式(done 门 / check-build 快反馈 / 历史压缩)映射 U2/U7;fork 演进≠平移(6 符号兼容是版本钉、validate/run/prompt/roles 是 net-new 重写);两线分立不收敛、边界精确为引擎/表现层复杂度档 |
本文档的设计内容已并入生成运行时架构单一事实源 [`agentic运行时架构图说.md`](agentic运行时架构图说.md)(§5.3 单写 ReAct 循环)。请以该 SoT 为准;本文件原始内容见 git 历史。

View File

@ -1,100 +1,9 @@
---
date: 2026-06-23
topic: tier2 细节图说 · D 族——三层校验与九门(生成引擎子树·tier2)
status: 设计中(tier2 待 spike) · 每图映射源档见该图脚注 / frontmatter 记 hash 作防漂移门
映射源档:
- path: docs/architecture/架构/生成引擎/tier2实现详设.md
hash: d5efe45f
- path: docs/architecture/架构/生成引擎/自治富游戏引擎.md
hash: 4858e6cf
- path: docs/architecture/架构/生成引擎/tier2四层工程架构.md
hash: d5efe45f
- path: game-runtime/games/_wg1-gen/_shared/play.cdp.cjs
hash: 9d463f08
- path: docs/plans/2026-06-22-003-feat-tier2-富游戏自治生成线-plan.md
hash: ccb00bb9
status: 已退役(2026-06-24 收敛进 SoT)
canonical: false
date: 2026-06-24
---
# tier2 细节图说 · D 族 —— 三层校验与九门
# (已退役)tier2 细节图说 · D 三层校验与九门
> 本篇是绘境AI 架构图集 **生成引擎子树** 下的 tier2 细节图说,只覆盖 **D 族(三层校验与九门验收)** 五张图。生成引擎主干、tier2 总览、agentic 运行时各有自己的图说;这里专门把「一款生成出来的富游戏算不算过关」这件事画清楚。
---
## 0 阅读约定与同步纪律
D 族讲的是 tier2 富游戏自治生成线的验收骨架——从最顶层的三层校验框架,一路下到现行九门、富游戏专属新门、advisory 分级机制,再到把这套门从 LittleJS 搬到 Phaser 要付的工程代价。五张图映射四份属主设计档加一份探针源码:三层校验的口径出自《tier2 实现详设》和《自治富游戏引擎》(后者已升到三层口径),九门的精确判据和阈值出自《tier2 实现详设》的「过门判据」与「靶子 mini-肥鹅」两节,advisory 分级的真实落地证据出自现行九门探针 `play.cdp.cjs`,Phaser 重写与沙箱底座的工程序列出自 `2026-06-22-003` 计划的 U3/U6。frontmatter 记了这五份的当前 commit hash,作为防漂移门——其中任一份动了验收口径,本图说与对应 SVG 必须同步改。
整条 tier2 富游戏线现在 **整体待建**:0号 spike 还没跑,引擎没搭,门没重写,代码一行没落。所以 D 族里几乎所有「新机制」都是设计已出、尚未落地的状态,绝不能因为图画得齐整就误以为已经建好。有承袭、不全是新建的部分,用实线画,但这里要把两层承袭分清,别一概当「主干现行」。现行九门 harness 在 LittleJS 廉价线上判了大量游戏,是真跑了很久的底座,这部分确是主干现行。它内置的「driven 感知 advisory 分级」却要单独说清出处:这是 A-model 分支(`feat/mac-amodel-foundation`)近期给九门加的增强,在那条分支上已实现,但尚未合并到 dev/2.0.0 主干——本仓 docs/tier2-plan 的 `play.cdp.cjs`(frontmatter 记的 9d463f08)还是用各门 SKIP 标志达成同义降级,字面 advisory 块要等 A-model 合并后才进主干。图里 advisory 用实线,表的是「它已被实现」(A-model 线),不是「已在主干」;严格按状态码,从主干看它是「接」(待合并对账),tier2 实现时以 A-model 合并后的版本为准。tier2 自己要做的全是把这套哲学搬到 Phaser、再在九门之上补富游戏专属的几道门,这部分用虚线画。看图时记住这条对照:**实线 = 已实现(主干现行的九门,或 A-model 分支已落待合并的 advisory);虚线 = tier2 专属、待 0号 spike 验证后才落代码的新机制。**
## 1 全图通用图例
D 族用一套统一的徽章和线型,先在这里讲清,后面每张图不再重复。
**层级徽章**标的是一道门落在三层校验的哪一层:绿色 **L1** = 硬约束层(编译 / 启动 / 运行错误,必须解决、循环);蓝色 **L2** = 设计符合层(玩法 / 关卡 / UI vs 设计,尽量解决);橙色 **L3** = 效果层(美观 / 好不好玩,只评分、绝不阻塞)。**处置徽章**标的是这道门怎么处置:红色 **硬** = 硬门(不过即拒发),橙色 **无 driver 降 advisory** = 条件门(被驱动时致命、没被驱动时只报告)。另有几个生产维度徽章在个别图上点到:**质量**(绿)、**可靠**(绿)、**安全**(红)、**可观测**(蓝)、**数据**(灰)。
**线型**承担状态语义。实线框 = 现行已落,虚线框(短划线)= tier2 专属待建。实线箭头 = 现行已有的转移 / 关系,虚线箭头 = tier2 待建的流程或重写映射。
**状态码**四个,贯穿生成引擎子树:**现** = 现行已建在跑;**接** = 设计已出、代码待接线;**建** = tier2 立项后要新建、当前待 spike;**缓** = 收窄后缓做。D 族绝大多数元素是「建」,九门 harness 与 advisory 分级是「现」。
## 2 图集
### 图 D1 · 三层校验全景
![图 D1 三层校验全景](assets/t2-D-01-三层校验全景.svg)
一款生成出来的富游戏算不算过关,在 tier2 里不是一个笼统的「过了九门就行」,而是按创始人定的三层校验来判,三层各管一档、处置力度截然不同。这张图把三层并排铺开,关键在于让人一眼看出它们的处置差异,而不是把它们当成同一种门的三个名字。
最底下的 L1 是硬约束层,管的是编译、启动、运行错误这类客观事实——游戏 boot 不起来、跑着跑着抛异常、画面是死的,这些都是真问题,**必须解决,而且循环逼到解决为止**。判它的全是确定性信号:构建日志、浏览器 console、CDP 错误捕获、九门探针,机器说了算,零 LLM 参与判定。绘境现行那套「在真浏览器里真玩一遍判能不能玩」的九门,绝大多数落在这一层。中间的 L2 是设计符合层,管的是玩法和关卡实现得对不对、UI 有没有缺组件、品类约定有没有违反,处置是「尽量解决」而非死循环,靠的是确定性的设计符合度信号(九门里的机制进展 H_progress 就落这层)。L2 的拒发权不是天生就有,而是 observe → enforce ——先只观测、积累信号,等这道门在真实数据上证明自己判得准,才赋予它拒发权。最上面的 L3 是效果层,管特效、美观、好不好玩,处置是 **只评分、绝不解决、绝不阻塞拒发**,工具是 M3 多模态视觉软检(看截图打分),产出只进质量趋势、告警、给人工终审减负。
为什么 L3 死活不让它当门,是这张图最该让人记住的设计取舍,底部那条铁律带说的就是这件事。两个极端都不能取:把确定性门扩到能自动判「好不好玩」,会得到一堆能被刷的代理指标——同一套机制门下,精心做的打砖块和「摆三块砖点一下就赢」的退化品都能全绿,门就废了;反过来纯靠 LLM 当玩家裁判判好玩,又踩了 Goodhart 红线(优化代理指标反而偏离真目标),因为模型一旦进验收当裁判,会学会把游戏优化成「让裁判说好」而不是「真的好」。所以效果只评分、不当门,好不好玩最终归人工终审。还有一条迭代原则同样要紧:**初期只焊死 L1,L2/L3 渐进做,绝不让效果问题阻塞真问题**——一款刚生成的富游戏,先把「能不能运行」这件硬事判死,美不美观是后话;用户也可以经 HITL 主动要求继续解决任意层。整套三层在 tier2 里都还是待建状态,spike 初期只焊 L1。
### 图 D2 · 九门逐门
![图 D2 九门逐门](assets/t2-D-02-九门逐门.svg)
L1 那层「确定性归机器」的主体就是九门。这张图把九道门逐道拆开,每道门讲四件事:测什么、用什么探针手段、落 L1 还是 L2、是硬门还是条件门。读这张图的正确方式是把它当成 L1 的施工细节——D1 给的是三层的处置哲学,D2 给的是这套哲学在最底层具体怎么落成九个可机器执行的检查。
九门覆盖的是一款游戏从「能不能起来」到「玩起来正不正常」的完整链条。A_boot 判游戏能不能 boot 起来(navigate 后轮询 boot 信号);B_uncaught 判运行期有没有未捕获异常(console 加 CDP 错误监听);C_frame 判帧在不在推进、有没有卡死(前后两次取帧号差大于零);D_render 判画面有没有真实内容(截图回读亮像素阈值);E_live 判画面在不在变、是不是静止帧(对多帧取 FNV hash 去重);F_wiring 判逻辑是真调引擎还是空桩(查引擎 API 的调用前缀有没有出现);G_input 判注入输入后状态有没有变化(注入 input 再读 gameState 前后对比);H_progress 判核心机制有没有真进展(driver 驱动加玩后断言,含 latch 终态);I_control 判控制响应跟不跟手(输入到反应的时序在阈值内)。九门里只有 H_progress 落 L2(它判的是机制实现 vs 设计,属设计符合),其余八门都落 L1;E_live 和 H_progress 还带一个「无 driver 降 advisory」的条件标记,这正是下面 D4 要展开的分级机制。九门全绿等于机制地板通过、可发。
这套九门为什么对 tier2 重要,在于它把「做完了」这件事钉死在客观证据上,绝不让 LLM 给自己打分——这是防 Goodhart 的机制地板。但要看清一条现行与远期的边界:**现在这九门是为 LittleJS 廉价线建的,所有探针钩子都硬编码了 LittleJS 专属取法**(`#game-engine` 选择器、`window.__engine.snapshot().frame` 帧源、`__gameHostEngineInitFired` boot 信号)。tier2 复用的是「真玩判定加零自评」这套哲学,不是这套代码——探针要为 Phaser 整个重写,并且在九门之上再补富游戏专属的几道门(跨表联动、经济、latch,见 D3)。图底那条脚带把这件事点明:这是「重写适配层、不是参数化一键切」,而沙箱底座选型(直接复用现有 CDP,还是用 agentscope-runtime 的 BrowserSandbox)是 spike 开跑前必须先出结论的前置阻断项。
### 图 D3 · 富游戏专属确定性门
![图 D3 富游戏专属确定性门](assets/t2-D-03-富游戏专属门.svg)
九门是为「超休闲单局」量身定的机制地板——它确定性地判一款单场景小游戏能不能玩,但它不查跨系统接线。富游戏的本质难点恰恰在跨系统:像 mini-肥鹅 这样的经营游戏是合成、资源、订单三个系统耦合在一起,光过九门远远不够,还得确定性地证明「三个系统真接上了」「经济的盈利和破产两条路都真能跑到终态」「赢输的终态真落定」。这张图就是在九门之上补的三道富游戏专属门,整图全是 tier2 待建(虚线),待 0号 spike 验证后才落代码。
第一道是 **三联动门**,管跨系统接线,要证三个系统是真耦合而非三座孤岛。它的判据是确定性的、静态加真玩混合:订单的 requires 必须能被合成系统产出的物品满足(这是一次跨表可达性检查,静态地从 mergeChains 推到 orders),合成链必须是有向无环图(对 mergeChains 做拓扑检查、不许成环),完成订单时必须真调资源系统的 addCoins,合成消耗时必须真调资源系统的 consumeIngredient。第二道是 **经济门**,管数值经济闭环——一个真正的经营游戏既要能赢也要能输,所以这道门要 harness 用真输入把两条路都驱动到终态:可盈利的路径是金币从开局 20 经「合成→交单→收金币」的正循环攒到 100 判赢,可破产的路径是连续三个订单流失(只合成不交单或经济崩盘)判输。关键约束是这两条路都得能被 harness 的真输入驱动跑到终态,不能只是「在数据表里能算出来」——能算和能玩到是两回事。第三道是 **latch 门**,管终态落定:赢或输一旦落定就不能回弹,而且宿主要能读到终局。这道门承接现行宿主装载那条已经验过的约束——游戏没有 emit 通道,所以终态必须焊成一个可轮询的 latch,由宿主轮询去读,而不是靠游戏主动 emit。
这三道门和九门的关系,图中间那条「九门(实) + 三门(虚) = tier2 的 L1 机制地板」的式子讲得最清楚:九门是超休闲地板,三门是富游戏接线地板,两者合起来全绿,才算富游戏的机制地板通过。底部那条带把这三道门钉到了一个确切的靶子上——mini-肥鹅。它是 spike 对照组的施工图:3×3 合成棋盘加资源系统(金币加食材库存)加 3~5 个带耐心的顾客订单,内容量定在 12 个物品、6 条合成链、5 个订单模板、2 种货币,砍到能证、又保住多系统耦合 / 重 UI 表现层 / 数值经济闭环这三个本质难点。这三道门的 assertAfterPlay 规格就是照这个靶子写的,driver 也按「点合成格→凑齐订单物品→交单→看金币涨」的确定性序列预建,产出复用 game-e2e-cdp-harness 的四件套证据(render→截图时序→驱动→verdict)。
### 图 D4 · driven 感知 advisory 分级 + Goodhart 隔离
![图 D4 driven 感知 advisory 分级与 Goodhart 隔离](assets/t2-D-04-advisory分级与Goodhart隔离.svg)
九门里有两道门——E_live(画面在变)和 H_progress(机制有进展)——只有在游戏真被人驱动起来的前提下才判得公平。一款游戏如果根本没人给它输入,画面本来就该是静的、机制本来就没进展,这时候硬判它 E_live / H_progress 不过,是冤枉它。这张图的上半部分画的就是解决这个冤案的机制:**driven 感知 advisory 分级**,它承接 A-model 分支、创始人 2026-06-22 裁定,在那条分支上已实现(实线表其已落)。按 §0 那条 branch 说明,它还没合并到主干——从 dev/2.0.0 看是「接」(待合并对账),tier2 以 A-model 合并后的版本为准。
机制是一个两态的判断:当 spec 既没有 driver 也没有非空 inputs 时,driven 为 false——游戏没被驱动,无法公平评判可玩性,于是 E_live 和 H_progress 降为 advisory(仍然跑、仍然报告,但不计入 pass),硬门只剩七道(A/B/C/D/F/G/I);一旦 spec 给了 driver 或给了 inputs 序列,driven 变 true,这两道门自动恢复致命、九门全部计入 pass,而且不用改一行代码。一句话概括就是「driver 缺位则降 advisory,driver 到位则恢复致命」。这个范式对 tier2 不只是搬过来用——tier2 修 latch 门和经营门时复用的正是它:先 observe(只观测、积累信号),等 driver 到位、判据证准了再 enforce(卡死)。把「先观测后卡死」做成可控的状态转移,而不是一上来就硬卡,是这套分级真正的价值。
图的下半部分是 tier2 的第二段「开放自产 driver」,整段待建(虚线),它要解决一个比上半部分更危险的问题。第一段 spike 的 driver 是人预建的,纯测模型变量;第二段要放开,让自治 agent 自己产或扩 harness 的 driver,以测它的自治上限。但这里埋着一颗 Goodhart 雷,就是图底那条红线铁律说的:**写者不能写判自己游戏的那张卷子**。agent 既造游戏又造判它的 driver,会自然而然地把 driver 写成只盖它走过的路、只读不暴露真实力的字段,然后全绿——假绿。隔离手段是把「写卷人」和「判卷人」拆开,走三层:agent 提议 driver 后,平台先按 schema 和白名单编译(只许声明式合法形态过,非白名单或越界字段直接拒),再交独立 adversarial 评审(一个不向着被评对象、专找漏洞的评审,查它是否覆盖真实玩家路径、是否读了作弊字段以避开真考验),三层全过这个 driver 才入验收,任一层不过就退回 agent 重提。为了让这道隔离可观测,第二段还专门采集两个指标:自产 driver 被独立评审打回的比例、driver 被修改的次数(防它靠反复改 driver 来逃避卡死探测)。
### 图 D5 · CDP 探针 Phaser 重写 + 沙箱底座
![图 D5 CDP 探针 Phaser 重写与沙箱底座](assets/t2-D-05-CDP探针Phaser重写.svg)
九门的探针机制(真浏览器真玩加确定性判定)在 tier2 里不变,但它的引擎钩子全是 LittleJS 专属的,要为 Phaser 重写。这张图最该破除的一个误解是「换引擎只要改个参数」——它不是。整图待建(虚线),核心是一张「引擎钩子重写映射」表,左边是探针现在硬编码的 LittleJS 取法(实线、已落),右边是 tier2 要为 Phaser 重写的等价钩子(虚线、待建),每一行都要各自重写,中间那条虚线箭头标的就是「重写」而不是「切换」。
要重写的钩子有四条。第一条是 canvas 选择器:现行用 `#game-engine` 做像素回读和触摸坐标映射,Phaser 自己挂 canvas、id 不同,选择器和坐标映射都得按 Phaser 视口重写。第二条是帧源:现行 C 门读帧 delta、G 门把对照实例推进到同帧号,都读 `window.__engine.snapshot().frame`,Phaser 的帧驱动机制不同,得另找一个单调递增的帧计数替代它。第三条是 boot 信号:现行 A 门轮询 `__gameHostEngineInitFired` 判引擎接管完成,Phaser 的 boot 序列独立(scene create 完成),得另置一个「引擎已起、可真玩」的就绪标志。第四条是输入注入加状态读取:现行 tap/key 注入加 `__gameState` 读取(经 _forensicsView 导出)支撑 G 门和 H 门,Phaser 的输入投递路径和可测性接口要重接,而富游戏的 state 还要走经营门那套约定导出。这四条逐条重写,不是一个参数切过去——这是这张图反复强调的判断。
图底两个框讲的是这件事的两个边界。左下角的沙箱底座框是一道 **spike 前置阻断验证**:先做个小验证,看 agentscope-runtime 的 BrowserSandbox 暴露的 page.evaluate / CDP 能力够不够(navigate / screenshot / 输入注入逐项可用),够就直接用它跑探针矩阵,不够就回落到自建薄沙箱加复用现有 `play.cdp.cjs` 的路。这道结论必须在 U7 生成迭代开跑前出来——出不来,整个 spike 跑不起来,所以它是属主标记的前置阻断项。右下角的「第一版克制」框讲的是另一种纪律:第一版只参数化九门 harness 的引擎钩子(canvas 选择器、帧计数源),把 Phaser 的 RAG 和脚手架硬编码进 worker,通用能力包接口(skill / mcp / tool / rag / 脚手架)暂不建满。理由很实在——现在只有 Phaser 一个真实现,接口形状会被它一个人反向决定,等第二个引擎(Pixi)落地、有两个实现做依据,再抽真正的能力包接口才不会白抽。这是一个有意的「先别抽象」决策,不是没做完。
## 3 图清单与状态表
| # | 图名 | 形式 | 状态 | 覆盖内容 |
|---|---|---|---|---|
| D1 | 三层校验全景 | SVG · 分层卡片 | 建(tier2 待建 · 初期只焊 L1) | L1 硬约束 / L2 设计符合 / L3 效果三层的内容、处置、工具包、谁判;三条铁律(机器判确定性、L3 只评分防 Goodhart、初期只焊 L1) |
| D2 | 九门逐门 | SVG · 九宫格 | 现(九门=LittleJS 现行)/ 建(tier2 Phaser 重写探针) | A_boot/B_uncaught/C_frame/D_render/E_live/F_wiring/G_input/H_progress/I_control 逐门的测什么 / 探针 / 落 L1 还是 L2 / 硬门 or advisory |
| D3 | 富游戏专属确定性门 | SVG · 三门卡片 + 关系式 | 建(tier2 待建 · 经营品类专属) | 三联动门(跨表可达 + DAG 无环) / 经济门(盈利破产双路径到终态) / latch 门(终态落定不回弹);九门 + 三门 = tier2 L1 机制地板;靶子 mini-肥鹅 与 assertAfterPlay 规格 |
| D4 | driven 感知 advisory 分级 + Goodhart 隔离 | SVG · 状态机 + 流程 | 接(advisory 分级=A-model 分支已落·待合并 dev/2.0.0)/ 建(自产 driver 三层隔离=第二段待建) | 上:无 driver 两门降 advisory、有 driver 恢复致命的两态转移(observe→enforce 范式源);下:自产 driver 走编译 / 独立 adversarial 评审 / 通过才用的三层隔离防 Goodhart + 两项采集指标 |
| D5 | CDP 探针 Phaser 重写 + 沙箱底座 | SVG · 重写映射表 + 二分 | 建(tier2 Phaser 重写探针 + 沙箱底座 spike) | 四条引擎钩子(canvas 选择器 / 帧源 / boot 信号 / 输入加 state)的 LittleJS→Phaser 重写映射;沙箱底座 spike 前置阻断(BrowserSandbox vs 回落 CDP);第一版克制(只参数化钩子、不抽满能力包接口) |
本文档的设计内容已并入生成运行时架构单一事实源 [`agentic运行时架构图说.md`](agentic运行时架构图说.md)(§5.4 三层校验与九门)。请以该 SoT 为准;本文件原始内容见 git 历史。

View File

@ -1,110 +1,9 @@
---
date: 2026-06-23
topic: tier2 细节图说 · E 族——能力面(skills 清单 / 引擎能力包 / prompt 两阶段角色 / A-model 插件复用)
status: 设计中(tier2 待 spike)· 每图映射源档见脚注 · 通用读法见本篇 §0/§1
映射源档:
- docs/architecture/架构/生成引擎/tier2实现详设.md @ d5efe45f
- docs/architecture/架构/生成引擎/自治富游戏引擎.md @ 4858e6cf
- docs/plans/2026-06-22-003-feat-tier2-富游戏自治生成线-plan.md @ ccb00bb9
- tier2/HANDOFF.md @ ccb00bb9
status: 已退役(2026-06-24 收敛进 SoT)
canonical: false
date: 2026-06-24
---
# tier2 细节图说 · E 族 · 能力面:skills 与 prompt
# (已退役)tier2 细节图说 · E 能力面 skills 与 prompt
> 🚧 设计中 · tier2 是待 0号 spike 验证的假设,尚未落代码;过了 spike 才进建设。
> 本族四张图回答同一个层面的问题:**单写 agent 手里握着什么、引擎以什么形态交给它、谁在哪个阶段写、上一条廉价线已经造好的资产怎么接住。** 整轨的生命周期与对象结构画在《[agentic 运行时架构图说](agentic运行时架构图说.md)》,本族只放大其中的能力面。
---
## 0 阅读约定与同步纪律
E 族映射四份源档:tier2 实现详设的 §「运行时形态:两阶段、service 化与 session 三路径」和 §「前置物清单」,自治富游戏引擎的 §「引擎是能力包,不是底座」和 §「换一种生成方式」,实现计划的 §「与 A-model 分支的关系」,以及 tier2/HANDOFF.md 的 §「与 A-model 分支的协调」。frontmatter 里记了这四份的当前 commit hash,作为防漂移的对账锚——设计一变动,本图说与对应 SVG 必须连同 hash 一起同步更新。
整族都还没落代码,所以"状态"这一维度贯穿每张图,绝不能把待建的东西画成已建。tier2 的整条轨待 0号 spike 验证才决定要不要正式投入,因此本族凡是 tier2 专属的新机制一律画虚线、标"建";凡是承袭现行廉价线已经跑着的东西(九门 harness 的钩子机制、A-model 改过的探针分级范式、A-model 已造的插件契约)画实线、标"现"或"接"。看图时先认线型再读内容:实线 = 有真实现可承袭,虚线 = 还要照这份设计去建。
E 族里只有图 E4 涉及第三种状态。它讲的是 tier2 怎么接住 Mac 那条 A-model 分支已经造好的资产,所以整图基调是"接"——既不是 tier2 从零新建,也不是现行产线已落,而是把一份在飞分支的产出对账过来复用。这个区别在图里用底色和线型分开标。
## 1 全图通用图例
**状态码**(贯穿全族,决定线型):**现** = 现行廉价线已落、tier2 直接承袭(实线);**接** = 复用 A-model 已造资产、合并后对账(实线框 + 接的底色);**建** = tier2 专属新机制、待 spike 后落代码(虚线框 + "建"小标);**缓** = 排在更后、本族未出现。
**工具角色徽章**(图 E1 用):**核心环**(`#2563eb`)= ReAct 写-构-验主回路上的工具;**取证**(`#b45309`)= 只喂软检与人审、绝不进硬门判定;**起手收尾**(`#16a34a`)= 一次 session 的开头铺骨架、结尾吐源工程。
**五件套维度徽章**(图 E2 用):**质量**(`#15803d`)标在 skill 与 rag 上,因为这两件直接抬高模型写对的概率。
**两阶段维度徽章**(图 E3 用):**伸缩·多 agent**(`#7c3aed`)= 阶段 1 工作室靠多个设计 agent 发散;**数据·只读探**(`#475569`)= 设计 worker 只读不写;**质量·单写防冲突**(`#15803d`)= 阶段 2 收敛到单写。
**复用维度徽章**(图 E4 用):**数据**(`#475569`)= 复用引擎无关的契约与命名;**质量**(`#15803d`)= 渲染实现按 Phaser 重做、不降级;**成本**(`#16a34a`)= 不重新发明、省掉 Mac 那边重复造轮子的工时。
实线箭头(承袭现行)与虚线箭头(待建机制)在图 E3 的拓扑连线里也用同一套语义。
## 2 图集
### 图 E1 · 单写 agent 的 toolkit(阶段 2 · skill 清单)
![图 E1 单写 agent 的 toolkit skill 清单](assets/t2-E-01-skills清单.svg)
这张图把阶段 2 单写 agent 的工具箱摊开,逐个交代每个工具的目的、边界、产出。看它要先抓住一个形态:这不是一条流水线,而是九个工具围成的一圈自治内循环——写源文件、构建、跑一道便宜快检、跑真玩门、读门的裁决、据裁决再改,绕回写源,直到收敛或熔断触发为止。agent 不按固定顺序走完九步,而是每一轮"想一步、调一个工具、看结果",由它自己决定下一个调谁。所以这九个工具是 ReAct 循环的工具面,不是阶段图。
工具按角色分三类,这层分类才是设计要点。核心环的六个(写源、构建、快检、跑门、读裁决,以及作为收尾的 finish)是主回路上真正驱动迭代的;取证类的截图和查资产是喂给软检与人工的辅助;起手收尾类的模板初始化和 finish 各管一次 session 的开头和结尾。把截图单独划成取证、不让它进 L1 判定,是一条故意设的边界:一旦让模型看截图来给自己的游戏打分、又拿这个分当过关依据,模型会很快学会把游戏优化成"在截图里好看"而不是"真的能玩"。验收要靠 run-gates 在真浏览器里真玩一遍、机器确定性地判,截图只能用来给人减负和 debug。
九个工具里 write-source 是整轨最深的赌注所在。它写的是那 56% 的表现层——逐像素手画的场景和 UI、点击命中、这款游戏独有的规则,既压不成数据表也压不成骨架,只能 LLM 现写,也最容易崩。其余八个工具大半是在给这一件兜底:build 把源工程打成可玩 bundle,headless 在跑全套真玩门之前先做一道便宜快筛、能早断就别浪费一整轮真玩,run-gates 才上确定性的九门加三联动门加经济门加 latch,read-verdict 把裁决喂回循环让 agent 知道该改哪。
脚带那三条边界铁律是这张图最该让人记住的设计约束。第一条单写独占——整个工程只允许一个 agent 写、绝不并行拆,因为经营游戏的多个系统共享同一套状态,拆给并行 agent 几乎必然写出互相矛盾的结果(一手教训是曾经把"造 Flappy Bird"拆并行,背景跑成了马里奥)。第二条 finish 与源项目契约共用一份 schema——agent 交付时吐出来的形状,必须就是落库取回时认的那个形状,两边各写各的迟早对不上。第三条验收零自评,就是前面说的那条防 Goodhart 红线。
图底那条虚线点明本族最大的待建项:这九个工具现在只是概念清单,它们的 Phaser 实现(esbuild 构建 profile、Phaser harness 的钩子)都还没写。第一版打算先把 Phaser 硬编码进 worker,等第二个引擎落地、有两个实现做依据,再去抽通用的能力包接口——这正是图 E2 要讲的克制。
*(图源:tier2实现详设.md §运行时形态 / §前置物清单 · 自治富游戏引擎.md §引擎是能力包 · plan U2/U5)*
### 图 E2 · 引擎能力包 manifest(五件套)
![图 E2 引擎能力包 manifest 五件套](assets/t2-E-02-引擎能力包manifest.svg)
这张图回答一个容易想偏的问题:引擎在 tier2 里到底是什么。直觉会把引擎当成"一次选定、所有游戏都长在上面的固定底座",像现行廉价线选了 LittleJS 那样。tier2 故意不这么做。在这套架构里引擎是动态加载的能力包:每个引擎对应一套给 agent 用的五件套——skill、tool、mcp、rag,外加一份起手脚手架。换引擎,对 agent 来说就是换掉"我手里有哪些工具能调"的那一组,不是架构重写。这个设计的好处是它顺着 agent 的工作方式来——agent 本来就是按"我能调什么工具"来干活的,把引擎做成一套工具集,换引擎的代价就被压到了换工具集而非重写主干。
五件套各管一面。skill 是这一引擎下"如何做某一类事"的固化 playbook,把对的写法沉淀下来、少让模型现场摸索;tool 就是图 E1 那九个工具,它们的引擎实现按引擎重写;mcp 经模型上下文协议把引擎侧能力标准化地暴露成可调接口;rag 是引擎文档加范例的检索增强,补模型训练数据里这个引擎的密度——模型见这个引擎的代码越多,自治循环里就越少漂、越少编不存在的 API;脚手架就是模板初始化铺的那套工程骨架,等于平台预建的那 25%,给单写 agent 一个先天能过 boot 的起手点。skill 和 rag 标了质量徽章,因为这两件直接决定模型写得对不对。
图里那个紫色块"换引擎 = 换一套加载的工具集"是核心论点的落点:Phaser 能力包和 Pixi 能力包对 agent 只是两套不同的工具集,切换它不动两阶段角色、也不动三层校验主干。首批两个引擎里 Phaser 有真实现,Pixi 当前留桩。
这张图整体状态是"建",而且它最该让人看清的是那个克制警示——第一版不把五件套建满。如果一上来就把 skill、tool、mcp、rag、脚手架五件全建成抽象接口,而实际只有 Phaser 一个真实现、Pixi 只是个桩,那接口的形状会被 Phaser 这唯一的实现反向决定,等真去接第二个引擎时多半得改。真正的能力包接口要等第二个引擎落地、有两个实现做依据再抽,不为一个实现先建抽象。所以实验阶段只做两件最小的:把九门 harness 的引擎钩子(canvas 选择器、帧计数源)参数化,把 Phaser 的 rag 和脚手架硬编码进 worker,先把这一个引擎跑通。
图底那条绿条标出承袭现行已落的实线部分:九门 harness 的钩子机制本身、A-model 那套 driver 感知分级,都是现行 LittleJS 廉价线已经建好的,tier2 复用它们的哲学、为 Phaser 重写实现,而不是从零新造。
*(图源:自治富游戏引擎.md §引擎是能力包不是底座 · tier2实现详设.md §运行时形态)*
### 图 E3 · prompt 两阶段角色
![图 E3 prompt 两阶段角色](assets/t2-E-03-prompt两阶段角色.svg)
这张图回答"谁在哪个阶段写",并破除一个常见误读:tier2 不是只有一个 agent。它分两阶段,两阶段的 agent 形态故意相反。阶段 1 是工作室设计,用 Agent Team 星形多 agent:一个 leader 分析并拆解用户意图,经 AgentCreate 拉起玩法、关卡、数值、UI、音乐、特效、资产等设计 worker 去发散,再经 TeamSay 把各路结论汇回 leader 收敛成一份设计结论。这一阶段只读加对话——设计 worker 用 PermissionMode.EXPLORE 只读已有工程代码和工程内的设计文档(迭代已有游戏时会用到),并能与用户跨 session 多轮对话,但不写代码。阶段 2 才是单写实现:模板初始化工具铺骨架,把阶段 1 每个设计 agent 的结论写成工程内文档,然后单写 agent 跑 ReAct 多轮写代码,过三层校验,产出一个真 Phaser 源工程。
为什么设计用多 agent、实现用单写,这是两阶段角色最该讲清的道理。设计阶段要的是发散和专业分工,多个 worker 各管一个方面、把可能性铺开,leader 再收敛,这时候多 agent 是优势。实现阶段恰恰相反:经营游戏的多个系统共享同一套状态和约定,如果拆给并行 agent 同时写,几乎必然写出互相不一致的结果,所以实现必须收敛到单写、由一个 agent 持有整个工程的全局视图。图里那个红框拓扑铁律点出了星形的硬约束——worker 只拿到 TeamSay、只能把结果报回 leader,禁止 worker 之间互评或互相喊话,所有协调都过 leader 这个唯一的中心。
红框里还钉了一条容易踩坑的事实:AgentScope 2.0.2 删掉了进程内的 MsgHub 和 pipeline 编排,进程内多 agent 协作那套已经没有了,留下的只有部署态的 Agent Team(TeamCreate / AgentCreate / TeamSay)。工作室必须建在这套 Agent Team 上,而不是去找一个已经不存在的 MsgHub。图里阶段 1 和阶段 2 的内部拓扑都是照 2.0.2 源码逐条核过画的(`/root/oss/agentscope`),就是为了免得照旧版资料把架构画错。
底部那条蓝带补一句血缘:prompt 生成域承袭现行 studio.py 的多角色 prompt(design / code / fix),tier2 复用它的多角色 prompt 和生成域躯干,是 fork 起步、独立演进,不是照搬。整图的 tier2 专属机制(Agent Team 星形、单写 ReAct 多轮)都画虚线待建,承袭现行的(九门 harness、A-model)画实线。
*(图源:tier2实现详设.md §运行时形态:两阶段、service 化与 session 三路径 · 自治富游戏引擎.md §换一种生成方式 + §谁在写:单写者 ReAct)*
### 图 E4 · A-model 4 插件复用
![图 E4 A-model 4 插件复用](assets/t2-E-04-Amodel插件复用.svg)
这张图讲 tier2 怎么接住 Mac 那条 A-model 分支已经造好的资产,别重新发明。先要理清两线的关系。A-model 是 Mac 在做的 Tier1 廉价线 agentic 优化,它把廉价线从"声明式数据壳"推进到了"便宜模型自治写真 JS",所以两线的边界不再是过去那个"声明式 vs 自治"的能力档,而是创始人 2026-06-23 重定的引擎/表现层复杂度档:A-model 吃 LittleJS 轻量经营档,tier2 吃 Phaser 重表现富游戏档(肥鹅美食街那一档)。两线分立、不收敛,但 A-model 已经造好的几样东西 tier2 该接住,省掉重复造轮子。
复用的逻辑落在一条论点上:A-model 的 api.d.ts 是引擎无关、零语义的契约,跨得了引擎;而渲染实现绑死在 Canvas 上、跨不了引擎。所以图里那四个插件(scene-fsm 场景状态机、hud-ui 界面、session-score 局内计分、timer-scheduler 定时调度)tier2 都复用它们的契约和命名,只把渲染实现按 Phaser 重做。这就是每张插件卡上那两个徽章的含义——契约复用是数据维度的承袭,渲染按 Phaser 重做是质量维度不降级。横在下面的 sim-business 经营配方卡同理,它是经营和合成的系统骨架配方(数值与系统结构,不是渲染件),tier2 复用配方、用 Phaser 落地数值。
图里还有两块讲接缝怎么对账。九门基线那块:A-model 改过 play.cdp.cjs,加了 SAA-mode 的 __GameBundle 信封、driven 感知的 advisory 分级("driver 缺位就降为只提示、driver 到位就恢复致命")、还有取证用的 _forensicsView。tier2 在 U3/U6 复用它那套分级范式来修 latch 与经营门的 observe→enforce,但 fork 的源和行号锚点要钉到 A-model 合并进 dev/2.0.0 之后的版本重取,而探针本身要为 Phaser 重写——#game-engine 选择器、__engine.frame 帧源、boot 信号全换,这部分是虚线待建。源项目契约那块:A-model 把 source-project.schema.json 升成了 oneOf(1.0/2.0),但它的 2.0 绑死了 __GameBundle 加九门加 ECS-lite 那条 LittleJS 装载路;tier2 的 Phaser 工件走的是另一条装载序列(第二装载分支),所以另立一份独立 schema、不把 3.0 变体加进同一个 oneOf——这是为了避免把 Phaser 的差异焊进 LittleJS 的 keystone 契约。
整图基调是"接":实线框是 A-model 现行已落、承袭即用,虚线框是 tier2 专属待建。最该记住的一条在脚注里——A-model 是 Mac 在飞的分支、还没并进 dev/2.0.0,合并之后这两个接缝(play.cdp.cjs、source-project.schema.json)会刷新,所以实现 tier2 时要以合并后的版本为准对账,别拿当前在飞版本去钉行号。
*(图源:plan §与 A-model 分支的关系 · tier2/HANDOFF.md §与 A-model 分支的协调)*
## 3 图清单与状态表
| # | 图名 | 形式 | 状态 | 覆盖内容 |
|---|---|---|---|---|
| E1 | 单写 agent 的 toolkit(阶段 2 · skill 清单) | SVG | 建(演进自 wg1 studio.py) | 九个工具的目的/边界/产出 · 三类角色(核心环 / 取证 / 起手收尾) · 自治内循环形态 · 三条工具边界铁律(单写独占 / finish 共用 schema / 验收零自评) |
| E2 | 引擎能力包 manifest(五件套) | SVG | 建(第一版 Phaser 硬编码 · 通用接口待第二引擎) | 引擎=动态加载的能力包而非固定底座 · skill/tool/mcp/rag/脚手架五件套各管一面 · 换引擎=换工具集非架构重写 · 克制警示(不为一个实现先建抽象) |
| E3 | prompt 两阶段角色 | SVG | 建(阶段 1 Agent Team / 阶段 2 单写 · 2.0.2 API 已核) | 阶段 1 工作室 Agent Team 星形(多 agent 发散+专业分工·只读+对话) · 阶段 2 单写 ReAct(防并行写冲突) · leader-worker 拓扑铁律 · 2.0.2 删 MsgHub 只剩部署态 Agent Team |
| E4 | A-model 4 插件复用 | SVG | 接(复用 A-model 资产 · 合并后对账) | 两线边界(引擎/表现层复杂度档) · 四插件契约复用+渲染按 Phaser 重做 · sim-business 配方复用 · 九门基线 driver 感知分级范式复用 · 源项目契约维持独立 schema |
本文档的设计内容已并入生成运行时架构单一事实源 [`agentic运行时架构图说.md`](agentic运行时架构图说.md)(§5.5 能力面)。请以该 SoT 为准;本文件原始内容见 git 历史。

View File

@ -1,84 +1,9 @@
---
date: 2026-06-23
topic: tier2 细节图说 · F 族——源项目契约与第二装载分支(生成引擎子树·tier2)
status: 设计中(tier2 待 spike) · 每图映射源档见该图脚注 / frontmatter 记 hash 作防漂移门
映射源档:
- path: docs/architecture/架构/生成引擎/tier2实现详设.md
hash: d5efe45f
- path: docs/architecture/架构/生成引擎/agentic运行时架构图说.md
hash: 32adda95
status: 已退役(2026-06-24 收敛进 SoT)
canonical: false
date: 2026-06-24
---
# tier2 细节图说 · F 族 —— 源项目契约与第二装载分支
# (已退役)tier2 细节图说 · F 源项目契约与装载
> 本篇是绘境AI 架构图集 **生成引擎子树** 下的 tier2 细节图说,只覆盖 **F 族(源项目契约与装载)** 三张图。生成引擎主干、tier2 总览、agentic 运行时各有自己的图说;这里专门把「tier2 那个真 Phaser 工程怎么定义、怎么进浏览器、怎么和 agent 收尾接线对齐」这件事画清楚。
---
## 0 阅读约定与同步纪律
F 族讲的是 tier2 富游戏自治生成线在"产物形态"这一面要补的工程地基——给真 Phaser 引擎工程新写的一套源项目契约(钉七样、分四组),这套工程怎么经第二条装载路进浏览器,以及 agent 自治跑完时收尾的 finish 工具为什么要和这套契约共用同一份 schema。三张图共映射两份属主设计档:契约的七要素四组、命名边界、finish 共用 schema 这三段口径全部出自《tier2 实现详设》开头的「第一批工程定义:源项目契约」;第二装载分支与现行 ECS-lite 装载的对照、以及"放大图 7"这层关系出自《agentic 运行时架构图说》的图 7「产物与契约」。frontmatter 记了这两份的当前 commit hash 作防漂移门——任一份动了产物形态或契约口径,本图说与对应 SVG 必须同步改。
整条 tier2 富游戏线现在 **整体待建**:0号 spike 还没跑,引擎没搭,这套源项目契约一行代码没落。所以 F 族里几乎所有元素都是设计已出、尚未落地的状态(状态码"建"),绝不能因为图画得齐整就误以为契约已经写好。三张图里唯一是现行已落的对照锚,是 Tier0/1 廉价线那条 ECS-lite 数据壳装载路——它在生产上跑着(Runner v2 P1/P2 实证),用实线画;tier2 要新写的第二装载分支、源项目契约、finish 工具,全用虚线画。看图时记住这条对照:**实线 = 现行已落(ECS-lite 装载、MySQL 底座、bootGameHost 沙箱这条已验过的装载范式);虚线 = tier2 专属、待 0号 spike 验证后才落代码的新机制。** 紫色徽标和紫色卡片底色统一表示"待建"。
本族是《agentic 运行时架构图说》图 7 的放大层。图 7 把 tier2 源项目契约画成一条扁平的五要素链(类型标记 → 文件树 manifest 加入口 → 构建 profile 加依赖锁 → 内容哈希 → 落库寻址),只到"有这几样"的粒度;F 族把这条链逐项展开成七要素四组,补上第二装载分支的完整步序,再补上图 7 没画的 finish 共用 schema 防漂移机制。是 deepen,不是 duplicate——同一套设计,图 7 给总览扁平链,F 族给逐项施工图。
## 1 全图通用图例
F 族用一套统一的徽章和线型,先在这里讲清,后面每张图不再重复。
**状态徽章**标的是某件东西落在生命周期的哪个阶段:紫色 **待建** = 此件尚未落代码,是 tier2 立项要补的新机制;绿色 **现** = 现行已建在跑(只有 ECS-lite 那条对照锚带它);灰色 **数据** = 这一要素沿用现行 GamePackage 那套"存进 MySQL 加对象存储、带 sha256 落库取回"的数据范式,不是凭空新造存取面。少数图上还点到几个生产维度徽章:**可靠**(绿,指 sha256 / 内容哈希这类完整性校验)、**伸缩·并存**(紫,指两条装载路解耦并存可各自演进)。
**线型**承担状态语义。实线框 = 现行已落(只有 ECS-lite 装载路与 MySQL 底座);虚线框(短划线 stroke-dasharray 6 4)= tier2 专属待建机制(源项目契约、esbuild 构建、第二装载路、finish 工具)。实线箭头 = 现行已有的流程或关系,虚线箭头 = tier2 待建的流程或映射。**红线**(红框)是不许碰的硬边界:tier2 的契约与装载路绝不回归 Tier0/1 在产线跑着的那套。
**状态码**四个,贯穿生成引擎子树:**现** = 现行已建在跑;**接** = 设计已出、代码待接线;**建** = tier2 立项后要新建、当前待 spike;**缓** = 收窄后缓做。**future** 用远期紫表示更长期、当前不排的项;F 族里没有用到 future 项,全族集中在"现"(ECS-lite 对照锚)与"建"(tier2 七要素 / 第二装载 / finish)两档。
## 2 图集
### 图 F1 · 源项目契约七要素四组
![图 F1 源项目契约七要素四组](assets/t2-F-01-源项目契约七要素.svg)
tier2 立项要补的第一件工程,是给真 Phaser 引擎工程新写一套源项目契约。这张图把这套契约要钉死的七样东西按职责分成四组铺开,关键是让人看清:这七样不是随手列的字段清单,而是覆盖了一个真工程从"是什么"到"怎么构建出来、怎么存回来"的完整链条,缺一样这条链就断在某处。
先要弄明白为什么非得新写一套契约。现行 Tier0/1 廉价线上,一款游戏就是库里一条 GamePackage——代码打成 engineBundle 内嵌在 manifest JSON 里(整包带 sha256),feed 下发后挂上 window.__GameBundle、由 bootGameHost 在沙箱 canvas 里跑起来,每帧回调 update / render。tier2 的产物也走"存进库、按需取来跑"这条路,但形态根本不同:它不是声明式数据壳加固定运行时,而是一个真 Phaser 工程——多个源文件、一份构建脚本、一套依赖,要先 esbuild 构建出 bundle 才能玩。形态不同,认它的契约就必须另写。
四组七要素是这样落的。第一组**身份**只有一样——源项目类型标记,它的作用是让装载侧一眼分得清这是 ECS-lite 数据壳还是 tier2 引擎工程、走对应的装载分支,是两条装载路分流的第一道闸(F2 整张图就建在这道闸上)。第二组**工程骨架**两样:文件树 manifest 记下有哪些文件、各自什么角色(入口、场景、资产、配置),入口文件标出 build 从哪个文件起手——装载和寻址都按它们走,入口文件还是整条构建链的源头锚点。第三组**构建可复现**两样,要保证同一款游戏永远构得出来:构建 profile 固定这一款用什么命令、什么 esbuild 配置打包,把构建步骤随款冻结不漂;依赖锁钉死引擎和插件的版本,免得上游一升级历史游戏就构建不出来——这一条直接服务于"游戏是长生命周期项目"这个产品基座,一款两年前生成的游戏今天还得能原样构建出来。第四组**落库取回**两样:内容哈希给整个工程算一个指纹,既做缓存命中(同指纹直接跳过重建)又做完整性校验;落库与寻址 API 定义工程怎么存进 MySQL 加对象存储、怎么按 id 取回来重建。落库取回这一组带"数据"徽标,因为它沿用的正是现行 GamePackage 那套落库取回的数据范式,不是另起炉灶。
这张图最该让人记住的是底部那条红线:这套契约绝不碰 Tier0/1 在产线跑着的 ECS-lite 装载契约。命名上有个真实的撞名坑——仓里已经有一份 `contracts/agent-loop/source-project.schema.json`,名字也叫"源项目",但那是 Tier0/1 那条 ECS-lite 数据壳的契约。tier2 这套必须另立一份独立 schema、编为契约组新一类(additive),绝不能图省事扩在那份现有同名 schema 上——共用一份会把本该解耦的两条线又焊一起,改 tier2 时还可能污染在产线跑着的廉价线。两条装载路解耦并存,tier2 这边怎么改都不许回归现有产线。图中段还点出一处与 finish 工具的对齐建议(完整道理见 F3):finish 工具的参数 schema 应当和这套契约共用同一份定义,因为 agent 调 finish 交付的形状就该是落库取回时认的那个形状。
### 图 F2 · 第二装载分支
![图 F2 第二装载分支](assets/t2-F-02-第二装载分支.svg)
游戏怎么进浏览器,在 tier2 立项后变成了两条路而不是一条。这张图把两条"存进库、按需取来跑"的装载路并排画出来,左支是现行 ECS-lite 数据壳(实线、已落在生产),右支是 tier2 真 Phaser 工程的第二装载分支(虚线、待建),顶上一个分流框由源项目类型标记决定走哪条。读这张图的正确方式是盯住两支的差异点——它们前半段(存进库、按需取回)是同一个范式,真正分岔在"取回之后要不要先构建"。
左支是现行装载路,五步连成一条已经验过的流水线。第一步存库:代码打成 bundle 文本,作 GamePackage 的 additive 字段嵌进 DB-manifest(出于 CSP 不放外域),整包带 sha256。第二步取回:玩家在 feed 点开某款,后端把这份 manifest 下发到浏览器。第三步挂载:iframe 内联 script 取出 engineBundle 挂到 window.__GameBundle,不引外域。第四步启动:bootGameHost 在沙箱 canvas 上跑,这是通用宿主泛化装载、一套固定运行时,引擎掌帧、受控面经 ctx.getEngine 注入。第五步跑:引擎是唯一掌帧源、绘制面唯一是引擎 mainContext,每帧回调游戏 update / render,游戏不自起 RAF。这条路的特征是"不构建、即取即跑"——bundle 本身就是产物,而且固定运行时一套、所有数据壳共用。它是 Tier0/1 廉价线在生产上跑着的装载契约,Runner v2 P1/P2 已实证。
右支是 tier2 要补的第二装载分支,前半段一样"存进库、按需取回",但形态不同导致中间多出一段现行根本没有的步骤。tier2 入库的不是一个 bundle,而是一个真 Phaser 工程——多源文件加构建脚本加依赖锁,是个可维护的源项目,改源不改包、重新构建。取回这个源工程之后,必须先按构建 profile 用 esbuild 打包,产出可玩 bundle,然后才轮到落库取回走自己那套寻址 API、在沙箱里跑构建产物。这里有一处明确的设计取舍:右支的固定运行时不复用左支那套——两条装载路不通用、解耦并存,正是为了让它们各自演进时互不牵连(底部"伸缩·并存"徽章标的就是这一点)。右支中段嵌着 F1 那七要素四组,因为这条装载路的每一步都靠那套契约支撑:分流靠类型标记、取回寻址靠落库 API、构建靠 profile 加依赖锁、缓存与完整性靠内容哈希。
两条路对照成一句话就是图底那条带:现行是 bundle 即产物、即取即跑、固定运行时、一款一条 GamePackage、sha256 整包校验;tier2 是源工程入库、取回须先 esbuild 构建、独立源项目契约寻址、内容哈希做缓存与完整性。这张图同样压着 F1 那条红线——tier2 的第二装载分支绝不碰左支那条在产线跑着的 ECS-lite 装载路,tier2 怎么改都不许回归现有产线。
### 图 F3 · finish 工具 ↔ 源项目契约共用 schema
![图 F3 finish 工具与源项目契约共用 schema](assets/t2-F-03-finish共用schema.svg)
这张图讲的是一处从一开始就要对齐、不然日后必漂的源头设计:agent 调 finish 交付的形状,和落库取回时认的那个形状,必须是同一份 schema。整图待建(虚线),核心约定框居中放大画的就是这个等式——finish 工具的参数 schema 与 F1 那套源项目契约共用同一份定义,而不是各写一份靠人盯着保持一致。
要看懂这处设计,得先弄清 finish 工具是什么。tier2 的 agent 是单写的自治体,在 AgentScope 里以 ReAct 多轮自治运行(reasoning → action → observation 循环,这是 ReAct 的标准范式),过三层校验收敛到产出。它自治跑完那一刻,经一个 finish 工具——AgentScope 里以 Tool 工具函数为载体的收尾接线——把这份结构化游戏定义吐出来。这份结构化游戏定义就是 F1 说的那个真 Phaser 工程:多源文件加构建脚本加依赖,不是声明式数据壳。所以问题来了:agent 经 finish 交付的形状,和落库取回时按 F1 契约认的形状,本来是两个角色在两处定义的东西。
为什么非共用一份不可,红框那条警示讲得最直白:两边各写各的迟早对不上——改了契约忘改工具,或者反过来改了工具忘改契约,一旦错位,agent 交付的形状落不进库、取回时认不出。共用一份从源头消除这种漂移:契约即工具签名,改一处就是两处一起改,根本不存在"两份 schema 不一致"这个失败面。这不是为优雅而优雅,是把一类必然会发生的接缝错位提前焊死。
图下半部分把这件事铺成一条横向链路,让人看清共用 schema 在整条交付链里卡在哪:agent ReAct 收敛 → 调 finish 工具(参数就是源项目契约形状)→ 吐出结构化游戏定义(真 Phaser 工程)→ 按 F1 落库 API 存进 MySQL 加 OSS(内容哈希做完整性)→ 下次按 id 取回重建,认的就是 finish 交付的那个形状。链路末端的"取回重建"和起点的"finish 交付"认的是同一份形状,这条链才闭得上。最底下那条带把 F1 七要素四组逐项重列一遍,点明 finish 的参数 schema 与之共用的就是这一份定义——身份(类型标记)、工程骨架(文件树 manifest 加入口文件)、构建可复现(构建 profile 加依赖锁)、落库取回(内容哈希加落库寻址 API),七样一字不差地既是契约也是 finish 的签名。
## 3 图清单与状态表
| # | 图名 | 形式 | 状态 | 覆盖内容 |
|---|---|---|---|---|
| F1 | 源项目契约七要素四组 | SVG · 四组卡片网格 | 建(tier2 待建 · 契约组新一类 additive) | 身份(① 类型标记)/ 工程骨架(② 文件树 manifest、③ 入口文件)/ 构建可复现(④ 构建 profile、⑤ 依赖锁)/ 落库取回(⑥ 内容哈希、⑦ 落库寻址 API);现行 GamePackage 装载对照锚;红线=另立独立 schema 不撞 source-project.schema.json、绝不碰 ECS-lite 产线契约;finish 共用建议 |
| F2 | 第二装载分支 | SVG · 双支对照流水 | 现(ECS-lite 装载已落)/ 建(tier2 Phaser 第二装载分支待建) | 类型标记分流;左支现行五步(存库 engineBundle 内嵌 manifest → feed 下发 → 挂 window.__GameBundle → bootGameHost 启动 → 每帧 update/render);右支 tier2 工程形态加 esbuild 构建步加独立寻址;两条路解耦并存红线;两路对照一句话 |
| F3 | finish 工具 ↔ 契约共用 schema | SVG · 等式 + 横向链路 + 七要素条带 | 建(finish 与契约共用一份 schema · tier2 待 spike) | 核心等式(finish 参数 schema = 源项目契约定义);为什么共用(从源头杜绝交付↔落库漂移);横向链路(agent ReAct 收敛 → finish 交付 → 落库 → 下次取回重建);F1 七要素四组逐项条带 |
---
> 放大《agentic 运行时架构图说》图 7「产物与契约」:图 7 给 tier2 源项目契约一条扁平五要素链(类型标记 / 文件树 manifest 加入口 / 构建 profile 加依赖锁 / 内容哈希 / 落库寻址),本族逐项展开为七要素四组(F1),补全第二装载分支完整步序与现行 ECS-lite 装载对照(F2),再补 finish 工具与契约共用 schema 的防漂移机制及交付→落库→取回链路(F3)——deepen 不 duplicate。设计变动须同步本图说与对应 SVG。
本文档的设计内容已并入生成运行时架构单一事实源 [`agentic运行时架构图说.md`](agentic运行时架构图说.md)(§5.6 源项目契约与装载)。请以该 SoT 为准;本文件原始内容见 git 历史。

View File

@ -1,90 +1,9 @@
---
date: 2026-06-23
topic: tier2 细节图说 · H 族——观测与成本(生成引擎子树·tier2)
status: 设计中(tier2 待 spike) · 每图映射源档见该图脚注 / frontmatter 记 hash 作防漂移门
映射源档:
- path: docs/architecture/架构/生成引擎/tier2实现详设.md
hash: d5efe45f
- path: docs/architecture/架构/生成引擎/agentic集成架构.md
hash: 1b3e375a
- path: tier2/HANDOFF.md
hash: ccb00bb9
- path: docs/architecture/架构/生成引擎/agentic运行时架构图说.md
hash: 32adda95
status: 已退役(2026-06-24 收敛进 SoT)
canonical: false
date: 2026-06-24
---
# tier2 细节图说 · H 族 —— 观测与成本
# (已退役)tier2 细节图说 · H 观测与成本
> 本篇是绘境AI 架构图集 **生成引擎子树** 下的 tier2 细节图说,只覆盖 **H 族(观测与成本)** 三张图。生成引擎主干、tier2 总览、agentic 运行时各有自己的图说;这里专门把「一次生成到底干了什么、花了多少钱、这些数据怎么收成一份口径」这件事画清楚。
---
## 0 阅读约定与同步纪律
H 族讲的是 tier2 富游戏自治生成线怎么被看见、怎么被算账。让 agent 自治写游戏只是第一步,平台还得能反查每一次生成的全过程,还得能把每一款的成本对到一笔权威账上。三张图分管三件事:统一 trace 契约怎么让两条异构的生成线写进同一张表(H1)、tier2 这条线靠 AgentScope 官方管道怎么把轨迹吐出来(H2)、成本怎么从 token 折算成每款的人民币并收口到 new-api 一个计费平面(H3)。
这三张图映射四份源档。统一 trace 契约的实现接点出自《tier2 实现详设》的「统一 trace 契约的实现接点」节,它讲 tier2 这一侧怎么把 ReAct 三段填进契约。契约的完整治理口径——为什么消灭 split-brain 是「诚实镜像各自字段」而不是「强求对齐」、接口对称内容不对称怎么成立、观测管道的官方件链路、D12 预算闸缺什么——出自《agentic 集成架构》的「观测/审计仓」与「D12 运行治理门」两节。采集指标的字段定义(`cost_rmb` / `tokens_by_model`)出自《tier2 实现详设》的「采集指标字段表」。成本取证那条要补 `RecordingChatModel(AnthropicChatModel)` 变体的接点出自 `tier2/HANDOFF.md` 的「U7 成本取证」。frontmatter 记了这四份的当前 commit hash,作为防漂移门——其中任一份动了观测或成本的口径,本图说与对应 SVG 必须同步改。
整条 tier2 富游戏线现在 **整体待建**:0号 spike 还没跑,引擎没搭,代码一行没落。所以 H 族里凡是 tier2 专属的机制——统一 trace 契约的 `contracts/trace/` 立位、tier2 这条线的 adapter、Anthropic 路的录制变体——都是设计已出、尚未落地的状态,用虚线画。但这三张图都不是纯虚的,各有一段实打实的现行底座撑着。可观测面的官方件(Event System、TracingMiddleware、OTel、Studio)是 AgentScope 2.0.2 的现成件,源码已逐条核过,tier2 直接订阅、不必自造,这部分用实线画,表的是「官方现成、直接可用」。成本面的 new-api quota 计费平面是早已部署在跑的权威成本源,`cost.py` 读 `logs.quota` 折人民币是仓内 `newapi-billing-plane-integration` 已落的能力,这部分也用实线,表的是「现行已落」。SAA 廉价线那条 Java 线的节点裁决埋点同样是现行已跑的。看图时记住这条对照:**实线 = 现行已落或官方现成件(SAA 节点裁决埋点 / AgentScope 官方观测管道 / new-api quota 计费平面);虚线(短划线)= tier2 专属、待 0号 spike 验证后才落代码的新机制。** 个别图上还有一抹远期紫,标的是 `contracts/trace/` 这一类还没建的 additive 契约立位——它不是已有件、也不只是 tier2 私有,而是随 spike 或控制面 phase-1 才新立的契约位,别当现成。
本族是 [agentic运行时架构图说](agentic运行时架构图说.md) 的放大层,deepen 不 duplicate。运行时图说的图 7「产物与契约」只点了 trace 契约的两段结构(公共核心子集 + tier2 扩展段),它的术语映射节(图 6)只把「可观测 = Event System → TracingMiddleware → Studio」点了一行。H 族把这两点逐项展开:H1 把对称核心五字段加两轨扩展段逐项摊开、画出 adapter 双轨怎么汇进契约;H2 把官方观测管道的四节点链路加七类强类型事件逐个画清;H3 把运行时图说没细讲的成本折算链路独立成图。要看 tier2 整轨的全貌从运行时图说进,要看观测与成本这一面的细节看这里。
## 1 全图通用图例
H 族用一套统一的徽章和线型,先在这里讲清,后面每张图不再重复。
**生产维度徽章**标的是这块东西服务于哪个非功能目标:蓝色 **可观测** = 让一次生成的全过程能被看见、被反查;绿色 **成本** = 让每款的花费能被算准、被对账;灰色 **数据** = 落进采集字段的那些列。H 族主要落在可观测和成本这两维上。
**状态徽章**标的是单块组件的成熟度,跟整图状态条配合读:绿色 **现行 / 已落 / 廉价线已落 / 承现行** = 这块东西现在就在跑(new-api quota 平面、SAA 节点裁决埋点、`cost.py` 读 quota);蓝色 **官方 / 官方 typed / 标准** = AgentScope 2.0.2 或 OpenTelemetry 的现成件,直接订阅即用;紫色 **待建 / 待补 / 待立 / 契约位待建 / additive 待立** = tier2 专属、尚未落代码的新机制或新契约位。
**线型**承担状态语义。实线框 = 现行已落或官方现成件,虚线框(短划线 `stroke-dasharray 6 4`)= tier2 专属待建。实线箭头 = 现行已有的链路或官方件之间的转移,虚线箭头 = tier2 待建的订阅 / 映射流程。
**状态码**四个,贯穿生成引擎子树:**现** = 现行已建在跑;**接** = 设计已出、代码待接线;**建** = tier2 立项后要新建、当前待 spike;**future / 远期** = 更长期才铺、不在本期。H 族里 new-api quota 平面与 cost.py 是「现」,AgentScope 官方观测管道是官方现成(可直接接,记「接」),`contracts/trace/` 立位与两条 tier2 adapter 与 Anthropic 录制变体都是「建」。
## 2 图集
### 图 H1 · trace 统一契约
![图 H1 trace 统一契约](assets/t2-H-01-trace统一契约.svg)
两条生成线长得不一样,却要把轨迹写进同一张表,这件事一旦做错就会变成两套谁也对不上的日志。这张图要让人看清的,是「一份契约管两条异构线」凭什么能成立——它不靠强求两条线长成一个样,而靠一个对称的公共核心子集加各轨一个不对称的 JSON 扩展段。
先得把一个常见的错判扭过来。把轨迹收成一份,直觉上像是要消灭 split-brain,而消灭 split-brain 听起来就该是「让两条线的字段对齐」。这是错的。tier2 这条线是 ReAct 的「想一步、做一个动作、看一次结果」,SAA 廉价线是 16 个节点的阶段裁决,两者本就不同构——强求它们字段对齐,等于逼一条线编造它根本没有的字段。图顶那条红带说的就是这个真口径:**消灭 split-brain = 每条派发路诚实镜像它真有的字段、没有的绝不编造,不是强求对齐。** 正确解是把同一张表切成两部分:一个对称的公共核心子集,加各轨自己的不对称扩展段——接口层对称、内容层不对称,这才是一份契约管两条线的成立条件。
表结构这块是本图的核心,也是对运行时图说图 7 的放大。图 7 只点了一句「公共核心子集 + tier2 扩展段」,这里把五个对称字段逐项摆出:`traceId`(一次生成的轨迹主键)、`step`(第几步 / 第几节点)、`cost`(这一步折成人民币的成本)、`verdict`(这一步或这道门的裁决)、`timestamp`(发生时刻)——两条线都必须老老实实填上这五样,字段同名同义。扩展段则各写各的:SAA 那一列(实线,因为它在廉价线上已落)塞它的阶段、修复轮次、门裁;tier2 那一列(虚线待建)塞它独有的推理、动作、观察。两列虽然同在一张表里,内容互不编造、互不要求对齐。
两条线的采集机制差别很大,但这恰恰是设计成立的地方,不是缺陷。图中间那两个 adapter 框画的就是这件事:SAA adapter(Java 线、实线承现行)按 16 节点的阶段裁决埋点,经 adapter 映射进契约;tier2 adapter(Python 线、虚线待建)走 `Event System → TracingMiddleware → OTel` 这条官方管道汇进 Studio,再加一层 adapter 把事件按「公共核心子集 + 扩展段」映射进表。**trace 契约约束的是数据口径,不是采集机制**——采集两条线各按各的框架来,数据口径不随框架漂移。
这份契约的落处是 `contracts/trace/`,作为契约组的新一类 additive 立位(图右下那个远期紫框),内容含四样:字段定义、schema 版本、敏感字段脱敏规则、两条线 adapter 怎么映射。要特别说清的现行边界是:**这一位当前还没建**——它随 spike 或控制面 phase-1 落地时再新立,现在别把它当现成件引用。最后是写失败时的策略,图底那条黄带把它钉成一个必须选边、不能既要又要的决策:轨迹写失败时默认 **best-effort 不阻塞主生成流程,但落一条告警**。理由很直接——不能让一次落库抖动把整局已经跑出来的生成废掉,但也不能让它无声丢失,所以计入告警。这套观测真正的价值,是任何一次生成都能反查「它当时用的哪几条配置的哪个版本」,这样才回答得了「那批游戏质量掉了,是不是上周改的那条 prompt 干的」这种问题。
### 图 H2 · tier2 观测管道(Event System → OTel → Studio)
![图 H2 tier2 观测管道](assets/t2-H-02-观测管道.svg)
H1 给了 tier2 adapter 走官方管道这一句结论,这张图把那条管道逐节点展开,要破除的误解是「tier2 写轨迹得自己埋点」。事实正相反:AgentScope 2.0.2 本身带一套完整的 typed Event System,tier2 只订阅、不自造,再走官方的 TracingMiddleware → OTel 管道汇进 Studio,最后才加一层自建 adapter 映射进统一契约。
横向那条主链路是四个官方现成件接成的(图里全用实线、蓝色官方徽章,源码已核)。第一节 **Agent Event System**:ReAct agent 每跑一步就吐出一套强类型事件流,框架自带,不必自己埋点(源码 `agentscope/event/_event.py`)。第二节 **TracingMiddleware**:这个中间件直接调真 OTel SDK 的 `start_as_current_span`,把事件转成 span、挂上 attributes 和 status(源码 `middleware/_tracing/_trace.py`)。第三节 **OpenTelemetry span**:标准链路追踪 span,分 agent / llm / tool 三类 span_name,带 OK / ERROR 状态——这是真 OTel SDK 而非 mock,所以可以对接任何标准后端。第四节 **AgentScope Studio**:开箱即用的可视化,逐步呈现推理、工具调用、模型用量、一轮回复的边界,不必自造前端。一句话:tier2 写轨迹 = 订阅事件 + 官方 OTel 管道 + 一层 adapter。
中段把 Event System 吐的事件流逐类摊开,这是源码 `agentscope/event/_event.py` 逐条核过的七类。`TextBlock*` / `ThinkingBlock*` / `ToolCall*` 三类各带 Start / Delta / End 三相,分别对应文本、思考、工具调用;`ToolResult*` 是工具结果;`ModelCallStart / End` 圈住一次模型调用,而且 End 那一下带 token 用量(input / output_tokens)——这一条对 H3 的成本台账是关键,token 就是从这里抓;`ReplyStart / End` 圈住一轮回复,正好框住一步完整的 reason → act → observe。把这七类事件画清,是为了让人看出 tier2 的 ReAct 三段扩展段(推理 / 动作 / 观察)在事件流里都有现成对应——所以 tier2 adapter 的活只是「订阅 + 映射」,不是「埋点 + 采集」。
底部那段 adapter 层把两条线的对照画完整,正是「接口对称、内容不对称」在可观测面的落点。tier2 adapter(虚线待建)订阅上面的 Event System,机制是 **订阅 typed 事件流**;SAA adapter(实线、廉价线已落)按它自己的 16 节点裁决埋点,机制是 **节点裁决埋点**——两条机制各异,却写进中间同一张 `contracts/trace` 统一表(那个契约位仍是待建的紫框)。图底蓝带重申两件现行边界:写失败默认 best-effort 不阻塞、计入告警;存储复用已部署的 MySQL 加对象存储,不上重型可观测中间件——观测要早建是为了 spike 调试和对账当下就用得上,但不等于要为它铺一套独立基建。
### 图 H3 · 成本台账(RecordingChatModel → new-api quota)
![图 H3 成本台账](assets/t2-H-03-成本台账.svg)
成本对账这件事,绘境的口径一直很硬:成本不是估的,是从 new-api 的 quota 权威口径折出来的每款人民币。这张图要讲清的,是 tier2 接进这套口径时缺了一块、不补就会让 spike 的关键一档算不出账——M3 那一档走的是 Anthropic 原生端点,现有的成本记录只覆盖 OpenAI 路,抓不到它。
缺口在图左上和右边那个红框里讲得最直白。M3 是 spike 里 A 门那一段用来「先证路」的模型(先用 M3 证明这条自治路走得通,再用便宜档比成本),它走 `AnthropicChatModel`、用 Anthropic 原生端点 `/v1/messages`。但现有的成本记录只覆盖 OpenAI 路——便宜档(deepseek-v4-flash / v4-pro)走各自端点,由现有 OpenAI 路的记录覆盖,唯独 Anthropic 这一路没有对应的录制件包住调用。后果是确定的:M3 这档的 token 用量采不到,`¥/成功款` 这个数对 M3 直接缺,全矩阵成本没法对比。所以接点很明确——补一个 `RecordingChatModel(AnthropicChatModel)` 变体(图左上虚线待补框),它包住模型调用、抓每次的 per-model token 用量,把 Anthropic 这一路补齐。
折算链路是三步,横向铺开(图中段)。第一步 **抓 token**(待补):`RecordingChatModel` 包住每次模型调用,逐次记 token、按模型分列成 `tokens_by_model`。第二步 **new-api quota 折¥**(现行已落、绿框):用 new-api 的 quota 口径折人民币,`cost.py` 读 new-api 的 `logs.quota`——每次调用的 quota 就是真实倍率成本,这是权威源,不是按公开单价估的。第三步 **落采集字段**(数据):`cost_rmb` 和 `tokens_by_model` 落 run 级、每款一行(字段定义对到 G 族 G4 的采集字段表),再汇到矩阵级算出 `¥/成功款 = Σcost / pass 款数`,这个数直接喂给 spike 的 go/no-go 三张图。token → ¥ → 采集字段,一条链就把每款的成本钉到了权威账上。
底部那条计费平面带讲的是这套口径能成立的根本原因:**模型统一从 new-api 出口走,不论走哪条端点,计量都收口到 new-api quota 一个计费平面。** M3 走 Anthropic 端点、便宜档走各自端点,协议和 SDK 不锁死、每档走它各自最优的端点,但所有路最终都收口到 quota 这一个口径上 per-model 折¥。这是「协议自由 + 计费收口」的设计:上层任各档自由选最划算的接法,下层用一个统一的权威口径把成本拢回来对账。要查 NEWAPI_KEY、端点和机器,去 `docs/内网凭据与端点.md`——那是内网凭据的单一事实源。
## 3 图清单与状态表
| # | 图名 | 形式 | 状态 | 覆盖内容 |
|---|---|---|---|---|
| H1 | trace 统一契约 | SVG · 表结构 + adapter 双轨 | 建(tier2 待建 · contracts/trace additive 待立;SAA 廉价线节点裁决已落) | 消灭 split-brain 的真口径(诚实镜像非强求对齐);公共核心子集五字段(traceId/step/cost/verdict/timestamp)对称 + SAA / tier2 两轨 JSON 扩展段不对称;SAA adapter(实)+ tier2 adapter(虚)汇进 contracts/trace;best-effort 写失败计入告警 |
| H2 | tier2 观测管道(Event System → OTel → Studio) | SVG · 横向链路 + 事件流逐类 | 接(官方现成件可直接接)/ 建(tier2 adapter + contracts/trace 自建待建) | 官方四节点管道(Event System → TracingMiddleware → OTel span → Studio);七类强类型事件(TextBlock/ThinkingBlock/ToolCall/ToolResult/ModelCallStart-End 带 token/ReplyStart-End);两线 adapter 机制各异契约一致;best-effort 策略 + 复用 MySQL/对象存储不上重型中间件 |
| H3 | 成本台账(RecordingChatModel → new-api quota) | SVG · 折算链路 + 计费平面 | 现(new-api quota 平面 / cost.py 读 quota 已落)/ 建(RecordingChatModel(Anthropic) 变体待补) | 红线缺口(不补 Anthropic 录制变体则 A 门 M3 成本抓不到);折算链路 token → new-api quota 折¥ → 采集字段(cost_rmb / tokens_by_model);计费平面收口(M3 Anthropic 端点 / 便宜档各自端点 all roads 收口到 new-api quota 一个口径) |
本文档的设计内容已并入生成运行时架构单一事实源 [`agentic运行时架构图说.md`](agentic运行时架构图说.md)(§5.7 观测与成本)。请以该 SoT 为准;本文件原始内容见 git 历史。

View File

@ -1,389 +1,9 @@
# 固定游戏架构 + SAA 智能工作室
> 🚧 **架构演进中** —— 生成引擎子树正处于 gameDefinition→`src/` 终态迁移 + 产线化(plan `2026-06-18-001` U1–U4)中,文中标注的现行结论可能随推进变化;以子树 [README](README.md) 与最新裁定 / plan 为准。
> **这是什么**:绘境AI「一句话造游戏」背后的生成引擎设计。它回答一个核心问题——**一个便宜的小模型,怎么可靠地造出一款能上线、能玩的游戏**。
> **给谁看**:生成引擎方向的工程师、架构评审、新加入的同事。
> **怎么读**:先读 §1 抓住"固定脚手架 + 便宜模型填槽"这一个主意,其余各节都是它的展开。
> **品牌**:本文统一用「绘境AI」。
---
status: 已退役(2026-06-24 收敛进 SoT)
canonical: false
date: 2026-06-24
---
## 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、多迭代、多重试。
> 这套设计不是空想——但要把"成立到哪一步"说准。便宜模型在固定结构上填出连贯的**游戏定义中间表示**(gameDefinition)、再构建成真能过门的游戏,这一段已在代码层得到双重验证(见 §9 实测证据):**双证证明的是"便宜模型能可靠产出连贯的 gameDefinition 中间表示",并未、也不能证明"gameDefinition 就是合格的终态产物"。** 创始人 2026-06-20 已就产物终态定调:**终态产物必须是 `src/` 多文件代码工程,gameDefinition 只是"通往 src/ 的中间脚手架"**;当前实现把玩法逻辑塞进 `behavior.code` JSON 串、运行时用 `new Function` 解释执行,属"**已知偏离终态的债**",缺的正是"gameDefinition → src/"产物侧那一段展开。所以本档凡言"可维护源项目 / 把大模型当工作室",均指**方向已经走通、终态尚待兑现**,不是已闭合的终局——口径以同目录 [README §3.2 终态硬约束](README.md) 与 [设计合理性裁决](设计合理性裁决.md)(Tier0 混合范式 · 名实分层)为准。
---
## 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 引擎,服务 3D / 渠道导出轴(编辑器 + 人在环),与 tier2 自治生成轨正交、不是 tier2 富游戏轨的引擎(tier2 富游戏自治轨用 Phaser/Pixi,见同目录 [自治富游戏引擎](自治富游戏引擎.md))。
- **本期只实现 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 增量演进,避免返工。
### 4.5 对话式生成闭环:输入端不止一句话,还有确认目标、六类素材与创作期回灌(创始人 2026-06-22 历史回收判定捡回)
前面几节把镜头对在"工作室怎么把输入造成游戏"上,把输入默认成了"一句话"。但创始人口径里的"真创作能力"是两半——"一句话快"只是其中一半,另一半是"确认目标准、有素材依据、能据试玩反馈越调越准"。这后一半此前散在几份 review 里、生成引擎档没有落点,这里把它作为现行 SoT 立住。它由三个互相咬合的机制构成,共同把生成从"一次猜"变成"一段对话"。
**一是对话式生成 + 智能确认目标。** 让便宜小模型从一句话直接开造,风险在于模糊的需求会被它各自脑补、跑偏。但反过来"每次都弹一堆问题让用户确认"又会把"一句话快"这个卖点毁掉。正确的折中是让系统先判一判:创作者那句话清晰、信息够,就直接直通生成;只有当它**模糊**、缺了关键决策点时,才弹一张确认卡问清楚。落地接口是 `/studio/analyze`,它产出 `{parsedGoal, needsConfirm, clarifyQuestions[]}`——`parsedGoal` 是系统对这句话的结构化理解,`needsConfirm` 标记要不要追问,`clarifyQuestions[]` 是模糊时要问的那几个问题。确认的结果以一个**显式的 `confirmedGoal`** 随上下文往下携带,而不是塞进某个隐藏的共享态里偷偷影响后续——这条"无隐藏共享态"很重要,它让"这一轮到底按什么目标生成的"始终可追溯。这套 analyze→(可选)确认→生成,在编排里的入图接点是 render 节点(见 [SAA编排 §2](SAA编排.md) render 输入端那段),`confirmedGoal` 随 `PromptContext` 流进 design / generate。
**二是六类素材驱动生成(assetContext)。** "素材驱动创作"是产品侧明确的卖点:创作者不止能打一句话,还能上传或选用图元 / 角色 / 特效 / 场景 / 界面 / 音乐**六类素材**,让生成有真实素材作依据,而不是只凭文字臆测。这里要分清一个极易混的边界——本档 §5 契约⑤讲的 `assets[]` / `assetSpec` 是**生成产物侧**的六类资产规格(generate 出来的、要装进 GamePackage 的),而这里说的 assetContext 是**生成输入侧**的六类素材透传(用户带进来、喂给模型作上下文的)。两者品类枚举同名(sprite/character/effect/scene/ui/music)但方向相反:一个是"用户给的料",一个是"工作室产的活"。透传时图片类走多模态通道(让视觉模型看见),音乐与脚本类作结构化上下文(进 prompt)。这条输入通道同样在 render 节点入图,随上下文驱动 design 与 generate;它和上面 020 那条"每类资产一个生成 workflow"是两件事——020 是"怎么产六类资产",这里是"怎么吃用户带来的六类素材",别合并。
**三是创作期数据回灌。** 数据回灌有两个闭环,极易只记住一个。一个是**发布后**的:游戏上线后,据玩家遥测与反馈,工作室回到项目继续演进(对应 §1 那张图的下游、以及 README 范式里的 maintain 步)——这一半生成引擎档已经讲了。另一个是**创作期**的,此前没落点:创作者在发布前**自己试玩**这款半成品时,会暴露出流失点(玩到哪里就不想玩了)、难度卡点(卡在哪一关过不去)。把这些信号**结构化地喂回对话调整**,让下一轮生成或修改据此校准——这就把"改"也变成了有数据依据的对话,而不是创作者凭感觉描述"太难了"。它和发布后那条是两条不同的闭环:一条用真实玩家数据驱动长期运营演进,一条用创作者自己的试玩数据驱动当下这次创作的收敛。三者合起来,对话式生成才真正成"闭环"——确认目标定准了起点,素材喂厚了依据,试玩回灌让每一轮调整都更准。
> **contract-first 落地清单(待落地,勿当已建)**:上述三个机制大多仍是设计、尚未在生成引擎子树成 code SoT,落地前要先把契约面定清——① `/studio/analyze` 的请求/响应契约(`{parsedGoal, needsConfirm, clarifyQuestions[]}` + `confirmedGoal` 的回传),进 API schema;② SAA state key 增 `confirmedGoal` / `assetContext`(输入侧素材引用),严格 additive,与 §5 契约④口径一致;③ 输入侧素材透传若需落库,核对是否复用 `game_material`(P-MAT 素材中心)而非新表;④ 创作期回灌的信号结构(流失点 / 卡点的字段)与它喂回 design/generate 的接点,以及它与发布后遥测回灌的去重边界。这些都标"待落地",别写成现成。
---
## 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 产出。设计的终态目标是——逻辑必须以**可维护的多文件 `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](README.md))。缺的正是"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 已发布)串起事务边界:
```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. 实测证据与验收口径
这套架构的**地基**已在代码层**双重验证**,不是纸面设计——但要把验证范围说准:**双证证明的是"便宜模型能可靠产出连贯的 gameDefinition 中间表示",而非"它已是合格的终态产物"**:
1. **spike(小范围验证性实验)证地基**:便宜模型对 `source-project.schema.json` 产出连贯的游戏定义(gameDefinition 中间表示),准确率 **92–100%**,单款成本约 **¥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 行](../../../mvp/MVP进度总账.md) 与 [生成主线架构演进路线 cutover 阶段](../../../agent-specs/生成主线架构演进路线.md))。
由此**方向已经走通**:"改源不改包 / 可维护源项目 / 把大模型当工作室"的中间表示一段在代码层成立,配套产出了"运行时访问约定 v0"(便宜模型只能经一个受控的 `rt` 对象访问引擎能力)和"从源构建"的装配原型。但**终态尚待兑现**——产物侧"gameDefinition → `src/` 工程"那一段展开仍缺(创始人 2026-06-20 定调,见 §1、[README §3.2](README.md)、[设计合理性裁决](设计合理性裁决.md)),当前 `new Function` 内嵌串属已知债,不是已闭合的终局。
**九门**是这套质检的核心,指九道确定性的真玩自动门(serve-and-play 起服务、CDP 真浏览器玩、build 构建等组成的 harness),它们的脚本是子进程复用、**禁止重写**的既有资产。
最终验收口径分三层:
- **后端线门**:编译 + 单测绿、Flyway V18/V20 迁移绿(V18 建表 + V20 唯一键)、救场阶梯单测(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. **源项目物理归属**:**已收口 = 归 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. 相关文档
| 文档 | 关系 |
|---|---|
| [架构域主文档](../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 文档里,各司其职、互不重复。
本文档的设计内容已并入生成运行时架构单一事实源 [`agentic运行时架构图说.md`](agentic运行时架构图说.md)(§5.6 源项目契约与装载、§4.1 实例一)。请以该 SoT 为准;本文件原始内容见 git 历史。

View File

@ -1,144 +1,9 @@
# 开闸接线 · 一句话入口的端到端接线
> 🚧 **架构演进中** —— 生成引擎子树正处于 gameDefinition→`src/` 终态迁移 + 产线化(plan `2026-06-18-001` U1–U4)中,文中标注的现行结论可能随推进变化;以子树 [README](README.md) 与最新裁定 / plan 为准。
> **这是什么**:本文讲清楚"开闸"这件事——把绘境AI 的一句话创作主链,从用户在创作页敲下一句话,一路接到这款游戏被种子用户在游戏信息流里真人试玩。它回答的不是"生成引擎内部怎么造游戏"(那条线在[生成引擎主文档](README.md)与 [SAA 编排](SAA编排.md)里),而是"造游戏这台机器已经就绪,创作页那道『必须先选模板』的门又是怎么被拆掉、让一句话能直接驱动生成的"。
> **给谁看**:接这条链路的后端与前端工程师、做开闸决策的创始人、想看清"护城河闭环对外开放的最后一公里走到哪了"的人。
> **怎么读**:先读 §1 拿到结论(接线已落地、为什么风险低),再看 §2 那张接线图建立全局,然后按需深入 §3 的取证与 §4 的三处协同改动(均已上线,标注落地 commit)。
开闸是绘境AI 护城河闭环"对外迈出第一步"的动作。后端那台生成机器已经建成、也真机验过;曾经卡住的,是创作页一道**要求用户"先选一个玩法模板"的门**——而旧的"游戏模板 / 填参"产线已随 W-CLEAN 清理退役,这道门一度失去可执行模板。本文讲的就是怎么把这道门拆掉、让一句话直接驱动生成,以及这件事**已经在 2026-06-17 落地**(commit `7534bdf9`)。
---
status: 已退役(2026-06-24 收敛进 SoT)
canonical: false
date: 2026-06-24
---
## 1. 结论先行
# (已退役)开闸接线 · 一句话入口端到端接线
把整件事浓缩成几句话:
- **后端的生成引擎和游戏信息流,已经就绪并真机验过。** 提交生成的入口(submitGenerate)、生成前的控制平面(下文 D12)、生成前的合规审查(下文 GP9)、一句话生成主路(基于 SAA 编排,已真库验)、管理员审核台、信息流翻转上架——这整条链都已打通。
- **曾经的堵点是创作页一道"必须先选模板"的门——现已拆除。** 旧版创作页 `Create.vue` 把整条创作流**硬门控在"必须先选模板"**上(能不能点"开始生成" = 选了模板 且 输入框非空)。而一项叫 **W-CLEAN** 的清理工作把旧的"游戏模板 / 填参"产线整套拆掉后,这道门一度失去可执行模板,一句话创作主链曾结构性断裂。**这道门已于 2026-06-17(commit `7534bdf9`)拆除**:`Create.vue` 的提交条件改为"输入非空即可"(`canSubmit = prompt.trim().length > 0 && !submitting`,见 `Create.vue:70-72`),缺省 `templateId` 归一为 `generic`。
- **"玩法模板层"并未被废,只是品类框架而非可执行壳。** 要分清两个概念:W-CLEAN 废的是旧的"游戏模板 / 填参"产线(同质化的根源);而绘境AI 语义里的"玩法模板"= **品类引导框架**(影响生成 prompt 与脚手架,不是可执行的游戏壳),未废、有效。它已于 2026-06-18(commit `046c061d`,U7 R-TPL)以 **5 个品类**回填到模板选单(经营模拟 / 剧情互动 / 解谜闯关 / TRPG / 非遗科普),叠在 `generic` 之上(品类区分留 prompt 层 = agent 按品类写代码)。所以创作页**既能一句话直接生成、也能显式选一个品类模板**。
- **接线的本质,是把创作流从"模板驱动"改成"一句话驱动"——这件事已落地。** 它由三处协同构成(均见 §4,已上线):契约层把模板 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
```
图里几处值得展开:
- **左端"拆掉模板门"是本次接线的全部工程动作所在,且已完成。** 旧逻辑是"选了模板 且 输入非空才能提交";改后(commit `7534bdf9`)变成"输入非空即可提交",模板 ID 在提交时不再强制携带(缺省归一 `generic`),创作者也可显式从 5 个品类模板里选一个。
- **中段标红的 submitGenerate,是所有保护门的汇聚点。** 请求进来先过 D12 的配额 / 并发 / 背压,再过 GP9 的合规审查,全部放行才真正入队走生成。这两道门在本次接线中**原封不动**——开闸只是让请求能走到它们面前,而不是绕过它们。
- **生成走的是标绿的通用路(generic),不是任何新机制。** 这条路 W-CLEAN 之后就是主线,且已被 R3 真库验过。开闸不引入任何新的生成方式,这正是风险低的根本原因。
- **进度页与轮询已经接好,不在本次改动范围内。** 进度页 `/create/task/:taskId` 走轮询 `/aigc/task/{id}` 已经能用;采用轮询而非服务端推流(SSE),是一个有意的简化——现阶段轮询足够,流式推送是后续增强项。
- **右端的发布门是一个待裁定项(§5 C1)。** 生成成功后,游戏是经管理员审核台把关后入信息流,还是创作者自助直发,需要创始人拍板。图中按当前建议口径(管理员把关)画。
---
## 3. 取证:门曾关着、现已拆除(逐条落到代码)
下面把"门曾经怎么关、现在怎么开"逐条落到代码位置上。这些都是已验证事实,不是推断(行内同时给出**改前**与**改后落地**):
| 环节 | 改前(W-CLEAN 后的空窗) | 改后(已落地) | 出处 |
|---|---|---|---|
| 创作页提交条件 | "能否提交 = 选了模板 且 输入非空";提交时先建必须带模板 ID 的草稿,再带模板 ID 去生成 | `canSubmit = prompt.trim().length > 0 && !submitting`(去掉"必须选中模板");缺省 `templateId` 传 `generic` | `Create.vue:70-72,124-144`(commit `7534bdf9`) |
| 模板选单 | W-CLEAN 后 `getTemplateList()` 返空 → 创作页落"升级中"空态(代码诚实地区分了"升级中"与"加载失败") | `getTemplateList()` 已回填 **5 品类玩法模板**(经营模拟 / 剧情互动 / 解谜闯关 / TRPG / 非遗科普);空态仅在接口异常时兜底 | `AigcTaskServiceImpl.java:87-110`(commit `046c061d`,U7 R-TPL) |
| 契约约束 | (蓝图态)`/aigc/generate` 要求"输入非空 且 模板 ID 存在";建项目把 标题、模板 ID 列为必填 | `AigcGenerateReqVO` `required:[prompt]`、`templateId` 可选缺省 `generic`;`ProjectCreateReqVO` `required:[title]`、`templateId` 同样可选 | `contracts/api-schemas/aigc.yaml:200,203` / `project.yaml:200,203` |
| 后端通用生成路 | — | 通用路 / SAA 路**本就不依赖模板**(由模型直接写码,是 W-CLEAN 之后的主线),且已 R3 真库验(一句话 → 执行轨迹与就绪度落库);缺省 `templateId` 在 `submitGenerate` 归一 `generic` | `AigcTaskServiceImpl.java:140-144` / R3 verdict |
| 进度与发布 | — | 进度页轮询已接好;生成完发布进信息流走管理员审核台(这条链路已验过) | `aigc.yaml`(task 轮询接口) |
把这张表读成一句话:**断点从来不在生成能力,而在创作页那道"必须先选模板"的提交门;这道门已于 06-17 拆除、模板选单已于 06-18 回填 5 品类,一句话创作主链已贯通。**
---
## 4. 接线方案:三处协同(已落地 · commit `7534bdf9`)
接线遵循**契约先行**——先把契约改对,前后端再各自对齐。三处改动如下,合起来就是开闸所需的全部工程量;**这三处已于 2026-06-17 同一批次落地(commit `7534bdf9`)、并随后(06-18,`046c061d`)叠加 5 品类模板回填**。
### 4.1 契约层:模板 ID 改可选,缺省走通用路(已改)
生成请求体与建项目请求体里的模板 ID 已从必填改为**可选**,缺省时默认取 `generic`(通用路标识):`AigcGenerateReqVO` 现为 `required:[prompt]`、`ProjectCreateReqVO` 现为 `required:[title]`,二者的 `templateId` 描述均已注明"可选,缺省 generic"(`aigc.yaml:200,203` / `project.yaml:200,203`)。这一步是**向后兼容**的:老的、带模板 ID 的调用依旧能正常工作,只是新增了"不带模板 ID"这条合法路径。
### 4.2 后端:让建项目与提交生成都能接受"没有模板"(已改)
`project.create` 与 `submitGenerate` 在收到不带模板 ID 的请求时,落到 `generic`(`AigcTaskServiceImpl.java:140-144`:`if (!hasText(templateId)) setTemplateId("generic")`),然后复用那条已经验过的通用 / SAA 生成路。**控制平面(D12)与合规门(GP9)不动**——它们照常在入口前把关。后端这一侧改动很小,且完全不触碰这两道保护门的逻辑。
### 4.3 前端:拆掉创作页的模板门(已改)
`Create.vue` 的提交条件已改成"输入非空即可"(去掉模板门,`canSubmit = prompt.trim().length > 0 && !submitting`);模板选择区保留为**可选**——回填 5 品类后,创作者可显式选一个品类模板,也可不选直接一句话生成(缺省走 `generic`);提交时不再强制携带模板 ID(`Create.vue:124-144` 缺省传 `generic`)。设计体系所需的样式 token 已就绪。
三处的关系可以用下面这张分层图看清——契约是中间的契约层,前后端各自向它对齐:
```mermaid
flowchart TB
subgraph 前端["前端 · game-studio"]
FE["Create.vue<br/>提交条件 = 输入非空<br/>模板区 = 可选 5 品类(不选走 generic)"]
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. 关键权衡与待裁定项
下面四件事是产品 / 策略上的取舍,不是工程难点。**截至 2026-06-17 开闸 review,放行决策本身仍待创始人裁定**(口径见 canonical[《开闸验收门-W-G1》](验收门-W-G1.md)"开闸放行决策本身");其中 C2 / C3 的**工程缺省已随接线落地**(下文标注),开放的只剩产品姿态。如已有更新裁决,回填此处结论:
- **C1 · 发布门怎么走(仍开放)。** 种子创作者生成游戏后,是经**管理员审核台把关后入信息流**(当前现状,合规与质量双重兜底),还是**自助直发**?建议种子期采用**管理员把关**——GP9 已在生成前挡住违规输入,审核台再过一道人 / 质把关,放种子最稳妥;后续可以加"就绪分达标即可自助发"。
- **C2 · 模板 ID 留还是删(工程已按"可选默认"落地)。** 已实现为**可选、默认 generic**(改动最小、向后兼容,commit `7534bdf9`),而非彻底移除(那对契约是破坏性变更)。剩余开放项仅是"是否将来彻底移除"的长期姿态——建议维持可选默认。
- **C3 · 一句话之外要不要给提示(工程已按"保留可选"落地)。** 现状是**保留可选的品类模板**(U7 R-TPL 回填 5 品类,commit `046c061d`):创作者可显式选品类、也可不选直接一句话。剩余开放项仅是"模板选单的呈现形态/是否进一步简化为轻提示"的产品取舍。
- **C4 · 创作者白名单口径(仍开放)。** submitGenerate 现有一道创作者白名单校验;种子放量时"谁能创作"的白名单口径需要明确。
---
## 6. 爆炸半径 · 兼容性 · 风险
这次接线的风险评估很清楚,且已上线、未见回退:
- **契约把模板 ID 改可选 = 向后兼容**,老调用不会被破坏;通用生成路已 R3 真库验,后端改动小、不碰控制平面与合规门。
- **前端 `Create.vue` 的改动已与契约协调落地**(契约定义在先,前端按新契约提交,设计体系 token 已就绪)。
- **整体风险低**:不引入任何新的生成机制,只是"解开模板门 + 走已验过的通用路 + 契约把模板 ID 改可选"三件事的组合。这三处已随 commit `7534bdf9` 落地;若需回退,契约与前端各自 revert 即可(向后兼容,回退面很小)。
---
## 7. 验收口径(真机 · money-shot)
开闸是否成功,以一条端到端真机演示为准,而不是单测通过:
- **主验(护城河可演示的那一镜)**:种子创作者输入一句话 → 真实生成(通用 / SAA 路)→ 管理员发布 → 真入游戏信息流 → **真人在浏览器里试玩**——全链路打通,护城河闭环对种子开放、可演示。
- **门验**:违规输入被 GP9 阻断;超配额请求被拒;失败路径给用户可读的提示且可重试。
- **兼容验**:若仍存在带模板 ID 的老调用,它们依旧正常工作。
---
> **验证状态**:本文为架构策展文档,脱胎于开闸接线 review 版(2026-06-17,6c6g 出);承重硬事实已对照当前仓库代码复核并更新到现状——**接线三处已落地**(commit `7534bdf9`,2026-06-17):`Create.vue:70-72` 提交门改为 `canSubmit = prompt.trim().length > 0 && !submitting`(`124-144` 缺省传 `generic`)、契约 `aigc.yaml:200,203` 与 `project.yaml:200,203` 把 `templateId` 改可选缺省 generic、`AigcTaskServiceImpl.java:140-144` 缺省归一 generic;**模板选单已回填 5 品类**(commit `046c061d`,2026-06-18,`AigcTaskServiceImpl.java:87-110`);通用生成路 template-free 且 R3 真库验、进度页轮询 `/aigc/task/{id}` 已接、D12 控制平面 + GP9 合规先行两道门开闸时不动、放种子三件(接线 ∧ 2 安全洞收内网 ∧ 创作者白名单)缺一不可。待裁定项 C1–C4 截至 2026-06-17 review 仍待创始人裁定(其中 C2/C3 工程缺省已落地)。术语口径:W-CLEAN 废的是"游戏模板/填参线","玩法模板"=品类框架,未废、已回填。品牌统一为"绘境AI",无旧名残留。
本文档的设计内容已并入生成运行时架构单一事实源 [`agentic运行时架构图说.md`](agentic运行时架构图说.md)(§4.1 实例一 · 创作入口开闸)。请以该 SoT 为准;本文件原始内容见 git 历史。

View File

@ -1,332 +1,9 @@
# 引擎与运行时 · 架构设计文档
> 🚧 **架构演进中** —— 生成引擎子树正处于 gameDefinition→`src/` 终态迁移 + 产线化(plan `2026-06-18-001` U1–U4)中,文中标注的现行结论可能随推进变化;以子树 [README](README.md) 与最新裁定 / plan 为准。
> **适用范围(tier2 上线后收窄)** —— 本档讲的 `engineBundle` 单包装载路,是 Tier0/1 超休闲廉价线的交付层。绘境AI 另起了第二条生成轨 tier2(agent 自治造多系统富游戏),它的产物是真 Phaser 多文件工程,走自己的一套源项目契约和第二装载分支,与这里的单包路解耦并存、互不替换。所以本档凡言「唯一」都限定在 Tier0/1 这一条线内,不覆盖 tier2。tier2 的装载设计见同目录 [自治富游戏引擎](自治富游戏引擎.md) 与 [tier2 实现详设](tier2实现详设.md)。
> **这是什么**:绘境AI 游戏引擎层与运行时装载体系的架构设计主文档,回答「选了哪个引擎、为什么选、游戏怎么挂上去、包怎么交付」。
> **给谁看**:后端 / 前端工程师、AI 生成链路开发者、引擎集成评审方。
> **怎么读**:先读第 1–2 节建立全局认知(选型结论 + 一张架构图),再按模块深入第 3–5 节。
---
status: 已退役(2026-06-24 收敛进 SoT)
canonical: false
date: 2026-06-24
---
## 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** | 326.7KB(raw 1.24MB) |
| 综合评分(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 交付链与能力边界
本节讲的是 Tier0/1 超休闲线的交付层。tier2 富游戏走另一套源项目契约,不复用这条单包路(见 §1 适用范围横幅)。
### 5.1 包交付路径
Tier0/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 是增强信号,非必须 |
| tier2 第二装载分支 | tier2 Phaser 多文件工程的源项目契约 + 第二装载分支为待开项(见 [tier2 实现详设](tier2实现详设.md)),不在 Runner v2 现弧内 |
---
## 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-β 收口报告)
本文档的设计内容已并入生成运行时架构单一事实源 [`agentic运行时架构图说.md`](agentic运行时架构图说.md)(§5.4 三层校验与九门、§5.6 源项目契约与装载)。请以该 SoT 为准;本文件原始内容见 git 历史。

View File

@ -1,125 +1,9 @@
# 自治富游戏引擎
---
status: 已退役(2026-06-24 收敛进 SoT)
canonical: false
date: 2026-06-24
---
> 🚧 设计中 · tier2 是待 0号 spike 验证的假设,尚未落代码;过了 spike 才进建设。
# (已退役)自治富游戏引擎
> **这是什么**:绘境AI 第二条游戏生成轨的设计 —— 用一个能自治工作的 AI agent,造现有廉价流水线做不出来的多系统富游戏(合成、经营、挂机这类)。
> **给谁看**:生成主线的工程师、做技术选型与评审的人、想判断"富游戏能不能自动生成"的产品与创始人。
## 现有生成线的天花板
绘境AI 现在的生成线只擅长一件事:超休闲小游戏 —— 单场景、轻 UI、一个核心循环。它靠一套固定脚手架:强模型搭出声明式结构,便宜模型往里填逻辑,九门 harness 在真浏览器里真玩一遍判它能不能玩。pong、点击器、躲避、跑酷这类几何色块的单局游戏,它做得又快又稳。
合成、经营、挂机、经济养成这些品类它做不出来。最典型的是《肥鹅美食街》那一档:屏幕上同时摆着资源栏、合成棋盘、店铺列表、订单面板、商店背包,内容上百件物品、一整套经济数值。难点不在画面,在三件事 —— 重 UI、多个系统互相接线、大内容量。固定脚手架的运行时本身装不下多关卡、库存、对话树、敌人状态机、可变 HUD;它的生成方式是一次性填一组固定的槽,而富游戏的系统之间是一张稠密的依赖图(合成产出进库存、订单读库存、解锁扣金币),一次填空填不出来。
2026-06-20 的对抗审查把这条路判死:声明式数据壳加一坨不受约束的自由 JS,结构上只能到几何色块单局游戏这一档。这不是"还不够好",是范式的天花板 —— 让同一套 schema 同时背"可靠"和"复杂"两个互斥目标,正是审查点名的误判。富游戏必须另起一轨,不是给现有产线加分支。
## 换一种生成方式
造富游戏要把生成方式整个换掉:给一个 agent 一台完整的游戏工作站 —— 读引擎文档、写多个源文件、构建、跑真玩验收门、看截图、查资产 —— 让它在一圈确定性验收门的笼子里反复迭代,把游戏逼出来。
这条轨叫 tier2,面向价值更高、产量更低的 premium 场景。它和现有超休闲廉价线解耦并存:两条轨各吃各的市场、各跑各的运行时。这是整条设计最硬的边界 —— tier2 的任何改动都不许碰、不许回归现有廉价线。
## 三层拆分:数据表、骨架、表现层
给 agent 一台工作站不等于放任它乱写。tier2 的范式叫"约束自治"(内部代号 O2,是"纯填充"和"纯自治"之间的折中)。它先把一款游戏拆三层,用三层框住 agent 的自由度:
| 层 | 谁写 | 占比 | 内容 |
|---|---|---|---|
| 数据表 | LLM 填值 | 19% | 关卡参数、商品、合成链、订单、经济数值 |
| 系统骨架 | 平台预建 | 25% | 资源结算、合成规则、订单状态机 —— 每个经营游戏都长一样 |
| 表现层 | LLM 现写 | 56% | 场景画面、点击反馈、这款游戏独有的规则 |
占比那列是实测的,而且这个数推翻了立项时的判断。立项时拍的是 80/10 —— 80% 复杂度能压成数据表加骨架、绕过 LLM,只 10% 要 LLM 真写。后来拿仓里一款真做出来的王蓝莓经营游戏量了一遍:12 个源文件、180KB,最贵的模型 Fable 配三轮人工编排造的,过了真输入 harness、赢和输两条路都能走到。逐文件按字节归类,结果是 19/25/56 —— 绕过 LLM 的只有 44%,LLM 必须真写的是 56%,是原估的五倍。最大的一块是 render.js 那种逐像素手画的场景和 UI,近两百处直接调底层 canvas,既压不成数据表也压不成骨架。
44/56 没给 O2 判死刑,但它说清了真实的成本结构。约束这条杠杆,对"系统加手感"那 44% 真有效,而那 44% 恰恰是便宜模型最容易崩的部分:经营游戏的状态机(顾客队列、耐心、经济账本、结算循环)是可复用的品类骨架;手感、物理、粒子、音频、存档全走受控的插件 API(王蓝莓那款逻辑文件里几乎没有裸 canvas 调用,却有近百处插件 API 调用)。便宜模型最不会的事 —— 发明对的系统结构并正确接线 —— 被骨架和插件 API 接管了。
真正烧钱和翻车的是那 56% 的表现层:每款都得 LLM 现写,体量最大,最容易出错 —— 布局乱、命中几何和渲染几何对不上、事件接错。所以 O2 的自治那一侧,担子比立项设想的重得多。这把成本和可靠性的赌注抬高了,也把后面那个 0号 spike 从"锦上添花"变成了"不证就不能投"。
两端的选项也是这么排除的。一端是纯填充(O1),模型只填不写 —— 实测直接排掉,纯填充产不出那 56% 的手画场景,只能做换皮。另一端是纯自治(O3),不加约束 —— 它会在那 44% 上烧钱发散,富游戏工程的搜索空间太大,便宜模型会反复打转。O2 取中间,只比立项设想更靠自治一点:用骨架、数据表、插件 API 把系统层最难的 44% 锁死,把 LLM 的火力集中到必须写的 56%,再用门和熔断把自治关在笼子里。
## 谁在写:单写者 ReAct + 四道熔断
写游戏的是一个单写者 ReAct agent。ReAct 指 agent"想一步、调一个工具、看结果、再想下一步"的循环,不是一次把答案写完。单写者指只有一个 agent 持有整个工程的全局视图,它一个人写,不并行拆给多个 agent;旁边配几个只读的探路 agent 和一道独立评审。
坚持单写者,是因为经营游戏多个系统共享同一套状态和约定,拆给并行 agent 几乎必然不一致。一个一手教训:曾经把"造一个 Flappy Bird"拆给并行子 agent,背景跑成了马里奥。
单写者 ReAct 循环都在验收门内:
```mermaid
flowchart LR
A[读文档 / 资产] --> B[写源文件]
B --> C[构建]
C --> D[九门 harness 真玩]
D --> V{verdict}
V -->|未过| E[改]
E --> B
V -->|过| G[产出富游戏]
K[四道熔断:步数硬顶 · 预算闸 · 卡死探测 · 双超时]
K -. 任一触发即停 .-> B
```
agent 在门内能任意迭代,但被四道熔断关着,任一道先触发就停:每系统构建-修复的步数硬顶、网关的预算闸、语义层的卡死探测、双层超时。其中预算闸现在还不是强制硬闸,这条边界归《agentic 集成架构》那份。
## 验收:三层校验(确定性归机器,效果只评分,好不好玩归人)
验收按创始人定的三层校验,分三档、处置力度不同:**L1 硬约束**——编译/启动/运行错误日志,必须解决、循环,工具是确定性的(现行九门多数落此);**L2 设计符合**——玩法/关卡实现 vs 设计、UI 缺组件,尽量解决,靠确定性的设计符合度信号;**L3 效果**——特效/美观/好不好玩,只评分、不解决。初期只焊死 L1,L2/L3 渐进;绝不让效果问题阻塞真问题。
确定性的归机器(L1+L2):能不能运行、玩起来正不正常、产物体积小不小,由这套确定性门在真浏览器里真玩一遍判。这里有一条铁律 —— 绝不让 LLM 给自己打分。LLM 一旦进验收当裁判,会学会把游戏优化成"让裁判说好"而不是"真的好",门就废了。这条继承自现有九门:把"做完了"钉死在真浏览器真玩一遍,不是模型自夸。
效果只评分(L3):好不好看由 M3 多模态软检看截图打分,**绝不阻塞、绝不拒发**,只进质量趋势/告警/给人工终审减负。两个极端都不取 —— 把确定性门扩到自动判好玩,会得到一堆能刷的代理指标(同一套机制门下,精心做的打砖块和"摆三块砖点一下就赢"的退化品都能全绿);纯靠 LLM 当玩家裁判判好玩,又踩了 Goodhart 红线。所以 L3 只评分、不当门。
好不好玩最终归人:机器判不了的主观体验交人工终审,配 L3 的 player panel 辅助减负。人工终审这一环的吞吐和判据怎么定,是一笔待补的设计债,归《tier2 实现详设》。
## 引擎选型:先过无头硬筛
tier2 的产物是真引擎工程,引擎首批选 Phaser 和 PixiJS,过程是一道硬筛加一个判断。
```mermaid
flowchart TD
E[引擎候选] --> F{全无头?<br/>纯代码 / CLI 可建、不依赖图形编辑器}
F -->|否| X1[Cocos Creator · LayaAir<br/>强制编辑器 —— 出局]
F -->|是,但已停滞| X2[Cocos2d-x · Egret<br/>社区断档 —— 出局]
F -->|是且活跃| J{AI 作者镜头:<br/>生态厚 = 训练数据密 = 模型写得出?}
J -->|富游戏零先例 · 冷门| X3[LittleJS —— 留给超休闲档]
J -->|近 4 万星 · 先例多 · 有无头模式| P([Phaser + PixiJS])
```
硬筛:引擎必须能全无头驱动 —— 纯代码或 CLI 就能建出游戏,不依赖任何图形编辑器。生成游戏的是 agent 不是人,从生成到构建到验收没有一步有人坐在编辑器前点鼠标;任何要开编辑器拖拽才能出工程的引擎,agent 无从下手。
这道筛子筛掉了中文生态最厚的两个 —— Cocos Creator 和 LayaAir,它们都强制依赖图形编辑器、没有成熟的无头 CLI。更老的纯代码引擎 Cocos2d-x 和 Egret 过得了无头门,但已经社区停滞、维护断档,不能拿一条要长期演进的轨去押死引擎。
排除 Cocos 要正面处理一处冲突,绕不过去:仓里有一份锁定结论写着 Tier2/3 = Cocos Creator 加 MCP,理由是"158 工具的 MCP 让 AI 驱动可行",并据此否决了 Phaser。这条结论站不住。核实过一手资料:Cocos Creator 官方 3.8《命令行发布》明写"从命令行运行仍需 GUI 环境""不存在独立于编辑器的 CLI";被当成核心理由的那个 MCP,上游 README 自述是 Cocos Creator 的编辑器扩展插件,要编辑器进程开着才能跑,根本不是 headless。所以本设计推翻这条锁定结论:tier2 自治生成引擎定为 Phaser/Pixi;Cocos 退回它真正合适的地方 —— 3D、复杂场景、渠道导出,人在环离线作者,进不了无人值守的生成循环。两件事活在两条正交的轴上,Cocos 当初为后一条轴被选没有错,只是进不了 tier2 的循环。
```mermaid
flowchart LR
subgraph AX1[自治生成轨 · 无人值守循环]
P[Phaser / Pixi<br/>全无头 · 纯代码]
end
subgraph AX2[3D · 渠道导出轴 · 人在环]
C[Cocos<br/>编辑器 · 离线作者]
end
```
选 Phaser/Pixi 的判断,是从 AI 作者的角度看引擎,不是从人类工程师的角度。对人,引擎好不好用看 API 顺不顺手;对 AI,第一位的是生态丰不丰富,因为生态丰富约等于训练数据密集 —— 模型见过这个引擎的真实代码越多,写出来越对、越不容易在自治循环里漂。Phaser 近四万星、2013 年至今持续演进、有现成的挂机经营先例、3.2 以上官方支持无头模式、用 esbuild 构建,正好和现有 Tier1 那条全程 Node/CLI 的路同构,能直接塞进现有 worker 循环和九门 harness。Pixi 是 Web 2D 渲染的事实标准,本身是纯渲染层,适合当能力包加载。
这一档不用 LittleJS,两个理由:富游戏品类零先例,没人拿它做过有分量的经营、挂机;太冷门,训练语料里几乎没有它的代码,模型不会写。LittleJS 留给超休闲极小包那一档。
两份证据别搅混:王蓝莓那款样本用的是 LittleJS,它证的是"真多文件引擎工程装得下经营富游戏",与具体引擎无关;Phaser 这条路能跑通另有证据 —— 2026-06-11 的引擎 spike 把同一款王蓝莓小卖部在 Phaser 上做了一遍,过了五门(真输入跑通赢和输两条路、39 次真点击、金币从 20 涨到 100、倒闭终态齐全)。两次成功都是最贵的模型加重人工编排做到的,不是便宜模型自治 —— 它们证伪了"形状做不出",把"模型降下来、自治换掉人工"这一半留给了 0号 spike。
## 引擎是能力包,不是底座
引擎在这套架构里不是一次选定、所有游戏都长在上面的固定底座,而是动态加载的能力包:每个引擎对应一套给 agent 用的 skill、tool、mcp、rag,换引擎对 agent 就是换一套加载的工具集,不是架构重写。这正合 agent"我有哪些工具能调"的工作方式。
第一版不把这套接口建满。一上来就建满(skill / mcp / tool / rag / 脚手架五件套)而只有 Phaser 一个真实现、Pixi 留桩,接口形状会被那唯一的实现反向决定,接第二个引擎时多半得改。实验阶段只做两件最小的:把九门 harness 的引擎钩子(canvas 选择器、帧计数源)参数化,把 Phaser 的 rag 和脚手架硬编码进 worker。真正的能力包接口,等第二个引擎(Pixi)落地、有两个实现做依据,再抽。
## 渠道:能发,只是从热发降为精选
放弃 Cocos 不等于丢渠道。Phaser/Pixi 的游戏能发微信、抖音、快手小游戏,这是一项能建的能力,不是墙。所谓 Cocos 的渠道优势,核实下来大半蒸发:微信官方说 weapp-adapter 只是参考实现、不再维护,钦定"开发者基于自己的引擎实现自己的 adapter" —— 自研 adapter 本来就是正路,Cocos 的"一键"只是把这步在引擎内部替你做了,省掉的几步都是一次性能固化进产线的脚手架;变现 SDK 是 wx.* 原生调用,所有引擎都得手动接;快手没有任何引擎的专用导出,全平权。甚至有一处 Phaser 反占优:Cocos 的渠道构建要 Windows / Mac 机,纯代码引擎全 Linux 就能打包,对一条要服务器化的产线是反向收益。仓里 W-CH-α 这个 spike 已经为结构相同的 LittleJS 把这条路验到 P0(adapter 启动、收到合成触摸、6 门全过)。
一处真约束、与引擎无关:渠道侧禁止动态生成代码(微信、抖音剥离远程代码、禁 eval 类解释器),只许"包内固定模板加远程纯数据配置"。所以 tier2 的富游戏上渠道,得从 web feed 那种"热发任意生成游戏"降为"精选款固化进壳、走平台提审"。这定义了产品形态:web feed 拿热发的长尾,渠道拿精选的爆款。Cocos 同样撞这堵墙,不是 Phaser 的劣势。
## 现在证到哪,赌注在哪
已证的是产物形态:王蓝莓那款 12 文件、180KB 的多系统经营游戏真做出来过、过了 harness,证伪了"声明式数据壳之外的真引擎工程装不下肥鹅级富游戏";Phaser 这条路能过确定性门也有 2026-06-11 的五门证据。
没证的是 tier2 真正押的那一半 —— 这两次都是最贵的模型加重人工编排,不是便宜模型自治。最大的赌注是:便宜或中等模型的自治 agent,在经营骨架、玩法模板、资产池、RAG、九门兜底这套约束下,能不能稳定地把那 56% 的表现层写出来、过确定性门。整条 tier2 的 go/no-go 全悬在这上面,架构论证替代不了实测。这个赌注押在 0号 spike 上,怎么跑、过门阈值、退路树在《tier2 实现详设》。
本文档的设计内容已并入生成运行时架构单一事实源 [`agentic运行时架构图说.md`](agentic运行时架构图说.md)(§4.2 实例二、§5.1 运行时形态、§5.3 单写 ReAct)。请以该 SoT 为准;本文件原始内容见 git 历史。

View File

@ -1,233 +1,9 @@
---
status: 已退役(2026-06-24 收敛进 SoT)
canonical: false
date: 2026-06-24
---
> 🚧 **架构演进中** —— 生成引擎子树正处于 gameDefinition→`src/` 终态迁移 + 产线化(plan `2026-06-18-001` U1–U4)中,文中标注的现行结论可能随推进变化;以子树 [README](README.md) 与最新裁定 / plan 为准。
# (已退役)设计合理性裁决 · 对抗式审查
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:265`,evalRules 内 advance 分支)—— 关卡推进根本没实现;
- **渲染只有 rect / circle / fill / sprite 四种形状**(sprite 系 U1 新增,见 §3.3)—— 而 sprite 之外仍只有三类几何色块;
- **物理只有 `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"的退化品,**都能全绿**。
"好看"那一侧也撞墙,但要先把现状说准——这条在审查者初稿之后已被 **U1 资产渲染工作**(commits 73d33916+db14845d+81c1b2cf,2026-06-19)推进过一截:**资产消费端的地基已打通**——runtime 多了第四种形状 `sprite`(`gd-runtime.js:368` 走 `drawSprite`→真 `drawImage`,见 `:387`),构建链也从源项目顶层 `sp.assets` 把那六类资产规格(`assetSpec`)抽进运行时 `GameDefinition.assets` 供 `drawSprite` 消费(`build-from-source.mjs:160`),`d.ts` 与单测均已对齐。所以"渲染器只画色块 / assets 零消费者 / 白名单根本不含 assets"这三句**在现行代码里已不成立**。
**但"好看"的方向性结论仍未翻盘**,缺口只是从"链路根本不存在"下移成了"链路打通、宿主预载未端到端 wire":真实 sprite 显示依赖宿主 `boot.assets` 预先载入图源,而这一步当前尚未端到端接上,所以 `drawSprite` 在没有图源时会**降级成类目色占位 rect**——**当前实测产出仍以几何色块为主**。这意味着模型即便用 mmx(绘境AI 的素材生成工具)产出了精美 sprite,在宿主预载 wire 落地前,渲出来基本还是占位色块。而对大众用户来说,"好看"是游戏信息流第一屏的入场券——**占位色块画面会被直接划走**。
叠加上记忆里 `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 从孤儿契约转成有消费端):** 这是打通"好看"的**关键路径**,且它是一整条链、不是一个分支。其中 ①②的地基已由 **U1**(commits 73d33916+db14845d+81c1b2cf,2026-06-19)落地——① 渲染器已加 `sprite` 分支(render 组件 `shape:'sprite'` 引 `asset` → 引擎 `drawImage`);② 构建链已从源项目顶层抽 `assets` 并入运行时 `GameDefinition.assets`、`d.ts` 与单测对齐。**剩余两块尚未兑现**:一是 **宿主 `boot.assets` 预载尚未端到端 wire**(未 wire 时 `drawSprite` 降级类目色占位,故当前实测仍多为色块);二是 ③ **D 门视觉判据仍停在"非白屏"、未升级成"非纯色块/有结构"**(颜色直方图复杂度 / 边缘密度),否则接了真图,门也测不出视觉退化,等于白接。在这两块补齐前,**路线图必须明确标注"当前产出 = 色块原型,非可发行视觉品质"**,杜绝把"机制门全绿"误读成"产品就绪"。
**改动四(中优先 · 给"好玩"立独立质量轴):** 别让九门兼任"好玩"判官。补一道便宜的启发式门——动作类看 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 同时承诺"可靠"和"复杂",是当前**唯一的范式级误判风险**。这条"复杂品类另开一轨"现在已落档:它就是 tier2 富游戏自治轨,设计见同目录 [自治富游戏引擎](自治富游戏引擎.md)、[agentic 集成架构](agentic集成架构.md)、[tier2 实现详设](tier2实现详设.md)。
一句话给创始人:
> **这设计哪里都没"错",它只是被起错了名、被寄予了它够不到的期望。把名字改对(混合范式)、把愿景分层(Tier0 不碰 Marvel)、把逻辑往声明式拽回来、把 assets 链打通——做完这四件,它就是一个诚实、强壮、有护城河的 Tier0 生成范式;不做,它迟早会因为"被当通用范式卖"而在第一款复杂游戏的 demo 上当众撞墙。**
---
## 附录 · 裁决依据(已逐条代码核实)
本裁决的每一条 fundamental / serious 指控,均已按 `文件:行号` 核对真实代码,而非凭设计文档推断:
| 指控 | 代码证据 |
|---|---|
| 逻辑核未进契约 | `source-project.schema.json`:behavior 仅声明 `id`/`trigger` + `additionalProperties: true`;运行时另接受未文档化 `js` 别名 |
| 渲染/物理/场景焊死 | `gd-runtime.js`:渲染器仅 rect/circle/fill + sprite(U1 新增,sprite 之外仍三类几何);advance 为 v0 空操作(`:265`,evalRules 内 advance 分支);单场景 `scenes[0]`;单一物理积分;裸 `new Function` |
| 安全靠正则 | `build-from-source.mjs`:安全靠 `LOGIC_BANS`/`CONDITION_BANS` 正则黑名单(注:assets 已不再被丢弃——U1 后从顶层 `sp.assets` 单独抽入 `cleaned.assets`,误入 `GAMEDEF_KEYS` 才会丢) |
| 视觉链(U1 后)| host 目录对 `sprite`/`assets` **非零消费者**(`gd-runtime.js` `drawSprite`、`runtime-api-2d.d.ts`、两份单测均消费;`drawTile` 路已改 `drawImage`);剩余缺口=宿主 `boot.assets` 预载未端到端 wire,故实测仍多为占位色块 |
| 机制成功率未达门 | `SaaFullGraphE2eTest` 实测便宜模型成功率约 60%,未达 80% 门 |
> 总架构师对四视角分歧的拍板:**这些不是四个孤立缺陷,而是"压低输出空间换不专训"这一个范式张力的多张脸——该坚持地基、对齐名实、显式分层,而非推倒重来。**
---
> **延伸阅读**:这台生成机器的完整架构与端到端流程见[生成引擎主文档](README.md);"游戏即长生命周期源项目、LLM 是工作室"的范式原论述见同文 §3;固定架构 + 填槽的展开见[固定游戏架构](固定游戏架构.md);运行时与九门细节见[引擎与运行时](引擎与运行时.md);SAA 编排拓扑见[SAA编排](SAA编排.md);复杂品类"另一轨"的落地设计见[自治富游戏引擎](自治富游戏引擎.md)、[agentic 集成架构](agentic集成架构.md)、[tier2 实现详设](tier2实现详设.md)。
本文档的设计内容已并入生成运行时架构单一事实源 [`agentic运行时架构图说.md`](agentic运行时架构图说.md)(§4.1 实例一:Tier0 部分合理裁决与 A-model 转向;完整对抗式裂缝分析见 git 历史)。请以该 SoT 为准;本文件原始内容见 git 历史。