Compare commits

..

3 Commits

Author SHA1 Message Date
lili
7c2a35888d feat(planB-p2): U9 M0 core 联合门 turnkey(003-U3)——shell U8 + 三契约结构核
M0 core = 一句话真生成可玩游戏 + 三契约对齐:
- u9_m0_core_gate.py:shell U8 e2e + 运行时读真契约做结构核(API 信封 code==0 / dispatcher=source-project.schema 4键 / manifest=game-package.schema 8键);selftest 5/5
- 读契约 required 防硬编码漂移;缺 artifact 如实记 gap(不伪报)
- README 补「gen-done 接线时须补」4 项(play 路 / 遥测查询 / U8→U9 artifacts / 抽样源)
turnkey 三件齐(U7 rubric + U8 e2e + U9 M0 门),gen-done 落地即跑。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 01:38:39 -07:00
lili
4261a638a0 feat(planB-p2): U8 feed真玩 e2e 驱动 turnkey(R4+R11)——编排全链 + 资产对位断言
create→生成(SAA)→发布→审核→feed→真玩→断言;复用 orchestrator/backend_gw.py 全链 API + play.cdp.cjs 九门:
- 纯逻辑断言(selftest 8/8):资产对位(六类/非空串ref/render 引用=可见) / published(status4) / in-feed / 终局遥测(game_end)
- 编排步 gated on gen-done(须 Plan A 产可玩游戏 + mini-desktop);play 路二选一(产品前端 :4173 / 本地 bundle 九门)按产出形态择,已对齐 play.cdp.cjs 实证 CLI(位置参 gameId + --base=/--cdp=)
- 遥测查询接口缺则记 gap(不伪报)
turnkey:gen-done 落地 --run 即跑。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 01:35:41 -07:00
lili
bd70188e9b feat(planB-p2): U7 rubric 打分仪 turnkey(R9)——抽样/聚合/判门 + 人工协议
Plan B Phase-2 game-quality 门(gated on gen-done)的非生成半边先建:
- rubric_score.py:5项×0-2(资产对位 veto)确定性等距抽样 N=8 / 聚合 / 判门(均分≥1.4 ∧ 资产对位无0);selftest 7/7 PASS
- rubric-protocol.md:人工打分 0/1/2 锚点 + 与九门划界(只补设计质量层)+ 执行步骤
- README.md:Phase-2 harness(U7 建好 / U8·U9 待建)总览 + gen-done 后跑法
turnkey:gen-done 批落地即填分出判、零搭建;逐项分人工给(工具不代打)。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 01:28:47 -07:00
3238 changed files with 32696 additions and 342280 deletions

View File

@ -1,115 +1,98 @@
# .agents/ —— 绘境AI 项目的 Agent 能力中枢
# .agents/ —— 造梦AI 项目的 Agent 能力中枢
> 本目录沉淀并复利累积项目的 **Agent 能力、知识、规则与流程**,让团队在长期跨会话开发中持续抬高 AI 驱动开发的能力、准确率与稳定性
> 本目录沉淀并积累项目的**全部 Agent 能力、技能与规则**,让团队在长期开发中**复利式积累知识、流程与能力**,持续提升 AI 驱动开发的**能力、准确率与稳定性**
>
> 工作入口、项目定位/目标/目录均见 [`../AGENTS.md`](../AGENTS.md)(项目唯一入口);`CLAUDE.md` 仅以 `@AGENTS.md` 导入它。
---
## 一、目录结构与职责
## 一、本目录是什么
| 子目录 | 职责 | 回答的问题 |
|---|---|---|
| `knowledge/` | 事实与蓝图蒸馏(产品、架构、范围) | **是什么** |
| `rules/` | 必守的硬约束工程规范、安全可靠性、build-vs-buy | **必须怎样** |
| `skills/` | 可复用操作手册 playbook怎么做某一类事 | **怎么做** |
| `workflows/` | 元流程(如何承接、推进、收尾一个任务) | **如何承接任务** |
| `tools/` | 治理门与文档整理的机械脚本(检测/归档/死链/品牌门) | **机器怎么把门** |
> 通用顺序:**先 knowledge 对齐事实 → 看 rules 划红线 → 用 skills 落地 → 按 workflows 推进收尾。**
>
> **与 `.claude/skills/` 的边界2026-07-05创始人拍方案①「双层」**:本目录是项目 playbook 的 **SoT**,全部 skill 带标准 SKILL.md frontmattername/descriptionagentskills.io 规范兼容);`.claude/skills/` 只放 harness 自动按需加载件——要么独立件(现仅 `agentscope-skill`),要么**薄壳指针**frontmatter + 一行"读 `.agents/skills/<name>.md` 照其执行"),正文唯一在本目录,防双约定漂移。
随着项目长期、跨会话推进零散的认知和踩坑很容易丢失AI 每次都"从零理解"。`.agents/` 把这些沉淀成**结构化、可检索、可复用**的资产:事实蒸馏、硬约束、操作手册、元流程各归其位。**每次任务从这里取经验、向这里存经验**,能力随时间累积而非反复重置。
---
## 二、文件清单(每条一句话)
## 二、目录结构与职责
| 子目录 | 职责 | 回答的问题 |
|---|---|---|
| `knowledge/` | 事实与蓝图的蒸馏(产品、架构、范围、术语) | **是什么** |
| `rules/` | 必须遵守的硬约束(工程规范、安全可靠性) | **必须怎样** |
| `skills/` | 可复用操作手册 playbook怎么做某一类事 | **怎么做** |
| `workflows/` | 元流程(如何承接、推进、收尾一个任务) | **如何承接任务** |
### 完整文件清单
**knowledge/**
| 文件 | 一句话说明 |
|---|---|
| `product-and-architecture.md` | 产品定位、13 模块与依赖、三仓三端分层架构蒸馏(中间件 Nacos/RocketMQ/Sentinel 三件套已自托管 mini-infra |
| `tech-decisions.md` | 技术栈与关键选型 ADR + 待确认项 + 生成主线口径AgentScope 三档统一收敛)+ 便宜档 M1-M3 实测§5.1+ 配置控制面阶段〇/一①② 落地§10-12+ 生产 cutover 窗封口§13框架默认注入面审计红线 / stuck 签名对齐设计意图 / 运维三硬记)+ 升级 AgentScope 2.0.3§15兼容依据 + 原生 Budget/RAG/mem0 列而不迁)+ 内测闭环 A 段落地§16new-api per-user 额度预置池 / S0「admin 不能替他人建 token」硬约束 / new-api 权威计量 / 注册防枚举 + facade HMAC 验签) |
| `mvp-scope-and-milestones.md` | MVP 的 55 项 P0 产品功能范围、5 工位分工、M0-M5 里程碑与验收指标 |
| `agentscope-2.0-facts.md` | AgentScope 2.0.3 真实架构速查 + 便宜档 cheap-worker 实现 API 速查(免重查)+ 护城河续修/软预算 middleware 的 2.0.3 机制速查on_reasoning 拦 finish 续跑 / 洋葱序 / 软预算门判自挂) |
| `knowledge/product-and-architecture.md` | 产品定位、13 模块与依赖、三仓三端架构蒸馏 |
| `knowledge/tech-decisions.md` | 技术栈与关键选型理由 |
| `knowledge/mvp-scope-and-milestones.md` | MVP 的 55 项 P0 产品功能范围、里程碑与验收指标 |
| `knowledge/glossary.md` | 术语表 |
**rules/**
| 文件 | 一句话说明 |
|---|---|
| `engineering-conventions.md` | 命名/分层/API 路径/错误码/提交/PR 等工程规范 + §10 文档治理机器门 |
| `security-and-reliability.md` | 安全基线/幂等/超时重试/合规/可靠性红线§1 @PermitAll 三条 + §4 外部 IO 不入事务 + §4.1 资金打款 |
| `build-vs-buy.md` | 自研偏误防线 R1-R7现货尽调前置门/形态终点/约束血统/prior-art 强制步/名义采用禁令/抽象墙审计 |
| `rules/engineering-conventions.md` | 命名/分层/API 路径/错误码/提交/PR 等工程规范 |
| `rules/security-and-reliability.md` | 安全基线、幂等、超时重试、合规与可靠性约束§1 含「`@PermitAll` 公开读端点纪律」三条:裁字段/DataPermission/租户 scopingP-OPN-08 实证§4 含「外部 IO 一律不入 DB 事务」泛化红线 + §4.1 资金打款特例 |
| `rules/build-vs-buy.md` | 自研偏误防线(R1-R7):现货尽调前置门/形态终点测试/约束血统/prior-art 强制步/名义采用禁令/抽象墙审计 |
**skills/**
| 文件 | 一句话说明 |
|---|---|
| `add-business-module.md` | 新增一个 game-module 业务模块的标准步骤 |
| `littlejs-game-dev.md` | AI 写 LittleJS 游戏代码作业手册code 层12 插件 API + 代码结构 + 工厂五法 + 资产/mmx 流 + 受控面铁律,终态产物 = `src/` 多文件工程 |
| `recipes/`README + 5 篇) | 高频场景落地形态(要做 X→读 Y命中矩形/计时器/资产回退/场景机/结算演出,各给「抄这段形态 + 头号病根」littlejs-game-dev 卡壳时读 |
| `plugin-capability-map.md` | 插件能力图谱11 件通用注入 + 1 件 puzzle L1 受保护注入 + runtime-probe 取证件 + 6 件储备(含边界与触发条件);机器门 `plugin-surface-gate.py` 的人读台账 |
| `engine-capabilities-brief.md` | 引擎能力 ≤3KB 摘要(从 `game-runtime/docs/ENGINE-CAPABILITIES.md` 蒸馏):引擎重活已被插件封装、少碰 getEngine + 坐标系 2× 换算坑 |
| `sim-business-game-design.md` | 经营模拟小游戏玩法设计手册(给生成 agent 的设计阶段):爆款范式/数值/美术/UI/音 → 映射 12 插件 + 反"无趣"8 条自检;与 littlejs-game-dev 配对 |
| `narrative-game-design.md` | 剧情/互动叙事品类设计手册W-GENRE 件①):分支选择+多结局图鉴范式 / 文本红线 / 可达性红线含取证契约 |
| `trpg-game-design.md` | TRPG/掷骰冒险品类设计手册W-GENRE 件①):掷骰可见 / 风险回报取舍 / 难度爬坡登顶 / 反无趣自检 |
| `heritage-game-design.md` | 非遗/传统技艺品类设计手册W-GENRE 件①):工序链玩法化 / 双层奖励 / 文化表述红线 / 反贴皮自检 |
| `puzzle-game-design.md` | 解谜品类设计手册W-GENRE 件①):顿悟距离 / 规则递进关卡阶梯 / 卡壳兜底 / 品类可达红线(解映射 targets occupied |
| `runtime-and-multichannel.md` | 运行时打包、iframe 沙箱、SDK 注入与多渠道(微信/抖音/快手)导出手册 |
| `contract-first-development.md` | 契约先行8 类契约对齐与各工位 mock 并行解耦 |
| `prompt-governance.md` | Prompt 即第 8 契约Registry/加载注入/eval 门禁/HITL 治理手册 |
| `wave-close-checklist.md` | 波次收口八步检查单——所有收口铁律指向的唯一可执行清单 |
| `staging-ops.md` | staging 运维配方:机器分工/代码同步/后端重部署/前端构建门/冒烟门/compose 部署 mini-infra |
| `ui-walkthrough-cdp.md` | 真 UI 走查配方CDP on mini-desktopstudio/admin 走查 + CDP 七坑 + 信道探针 |
| `game-e2e-cdp-harness.md` | Canvas 游戏 e2e 证据 harness驱动器六律 + 出厂红线 + 九门真玩AG 假绿守卫 + H 机制/latch + I 控制手感) |
| `cheap-model-game-generation.md` | 便宜模型生成跨路通用蒸馏:成本/前缀缓存降本/L1 纪律坑/质量三层/scale-20 模型画像(旧 gamedef/factory 路已压缩留指针,现行走 cheap-worker+ §11 AgentScope harness 死圈失败模式小瑕疵→整文件重写→MiniMax 漏 path→stuck 熔断→九门没跑→ok=False根因实证 2026-07-08 |
| `agentic-amodel-generation.md` | 底层 A-model 生成 harness`gen.mjs` ReAct 工具循环scaffold→读写检构→循环外 play8 坑 + M3 实证;现行被 cheap-worker shell-out 复用 |
| `gen-path-parity-harness.md` | 两条生成路对照parityharness + 按品类 ≥80% 达标门 + auto-vs-golden 双驱动 delta 门:验换实现不回退 / 判够不够格上线 |
| `saa-graph-orchestration.md` | SAA 图编排远期备查SAA 降最低优先级):依赖集/接 new-api 剥 /v1 坑/checkpoint saved_at 无 tiebreaker + 显式 checkPointId 修法 |
| `tier2-service-generation.md` | tier2 富游戏服务态生成驱动配方create_app :8200 + 控制面有界 resume真玩 driver 是头号收敛杠杆 + 驱动六口径 + C1 trace |
| `doc-organizer.md` | 文档整理助手(创始人手动触发):增量窗口 + 两阶段审批门 + 三轴(过期清理/措辞对齐/总账回填);底层脚本在 tools/ |
| `drive-remote-claude-tmux.md` | 远程驱动交互式 Claude Codessh + tmux双向通道派活读屏 + 每设备配置 + 完成判据 = git push |
| `architecture-diagram-atlas.md` | 架构图集生成配方house style / 图说结构 / 三范式纪律(单源·防漂移·状态双层)/ 按域并行编排 / 双评审六坑 |
| `feature-design-doc.md` | 功能设计文档作业手册WHAT+HOW 合一、一式两份(文字+Mermaid 给人/AI · SVG/HTML 给人)、防漂移单源 |
| `execution-plan-slicing.md` | 执行 plan 切分轴:横切关注面 vs 纵切成果切片;诊断信号 + 换轴 + 三层文档 + 必查七坑 |
| `agentic-seat-context-design.md` | agentic 席位与 context 工程设计需求基线2026-07-05 模型第一人称探索蒸馏四层边界prompt/context/environment/harness+ 分席四判据两戒律 + 星形九工件通信 + 注入铁律(模板>门>prompt+ 长项目六件/版本语义 + goal-loop 交互面 + 设计检查单 + 探针方法附录(§10·W-PROBE) |
| `skills/add-business-module.md` | 新增一个 game-module 业务模块的标准步骤 |
| `skills/add-game-template.md` | 新玩法模板接入配方契约→prompt→runtime→后端→编排器→五级验收门merge 实战收口版) |
| `skills/ai-generation-pipeline.md` | AI 生成链路Dify + OpenGame + aigc 壳)开发手册 |
| `skills/runtime-and-multichannel.md` | 运行时打包、沙箱、SDK 与多渠道导出手册 |
| `skills/contract-first-development.md` | 契约先行:契约对齐与并行解耦 |
| `skills/prompt-governance.md` | Prompt 即第 8 契约Registry/加载注入/eval 门禁/HITL 治理手册 |
| `skills/wave-close-checklist.md` | 波次收口八步检查单——所有收口铁律指向的唯一可执行清单(第 8 步=spec 退役/分层 + `docs/agent-specs/_index.md` 维护) |
| `skills/staging-ops.md` | staging 运维配方:机器分工/代码同步/后端重部署/前端构建门/冒烟门 |
| `skills/ui-walkthrough-cdp.md` | 真 UI 走查配方CDP on mini-desktopstudio/admin 走查 + CDP 七坑 + 信道探针 |
| `skills/game-e2e-cdp-harness.md` | Canvas 游戏 e2e 证据 harness编排形制/驱动器六律/出厂红线/四件套口径T1b-α 实证§7 = W-G1 生成游戏真玩九门 = 假绿守卫 G 门 + H 机制/latch + I 控制手感 + 自产 gatespec |
| `skills/cheap-model-game-generation.md` | 便宜模型new-api DeepSeek/MiniMax直出可真玩轻游戏 L1 worker链路/模型与成本/L1 纪律/坑红线/latch 套壳/design agent 自产 gatespec/质量三层W-G1 HJ-GEN-001 实证) |
| `skills/saa-graph-orchestration.md` | SAASpring AI Alibaba裸 StateGraph 生成编排:拓扑/加节点/接 new-api(剥 /v1 坑)/checkpoint(含 saved_at 无 tiebreaker 框架坑+显式 checkPointId 修法)/observation/最小依赖集/派发契约/验证门HJ-AGI-002 实证) |
| `skills/doc-organizer.md` | 文档整理助手(创始人手动触发):增量(上次清理→现在)+**两阶段审批门**——发起分析 Workflow→编清理计划→评审→批准后才执行三轴=过期档清理(归档/压缩)/核心设计档措辞对齐现行真相/主任务总账回填;底层 `tools/doc-organizer.{sh,-analyze.mjs,-state.json}`(检测自动·判断留人·脚本不自删·最新日在飞档不碰) |
| `skills/drive-remote-claude-tmux.md` | 远程驱动交互式 Claude Codessh + tmux双向通道 send-keys 派活 + capture-pane 读屏,不走 ACP/headless每设备配置块 + onboarding 侦察配方 + 专用会话(独立 worktree 防撞树) + bypass-perms + 安全红线;**完成判据=git push 非读屏**(创始人 2026-06-18 拍板·多设备复用) |
**workflows/**
| 文件 | 一句话说明 |
|---|---|
| `ai-development-protocol.md` | 任务承接→分析→评审→执行→验证→沉淀的完整协议 + 子代理 model 成本分档 + §4.2 执行子代理产出验证纪律DONE 不可信/hash 校验拦造假/自验) |
| `mvp-execution-orchestration.md` | MVP 10-Agent×3 周执行编排 + 8 条复利提效策略 |
**tools/**
| 文件 | 一句话说明 |
|---|---|
| `docs-gate.py` / `docs-gate.sh` | 文档治理机器门engineering-conventions §10.7):七检——品牌不变量 / canonical 唯一 / 死链 / 入口卫生 / 留痕隔离 / 设计档申报 / 计划谱系(`上级:` 单指针) |
| `plan-tree.py` | 计划谱系树查询视图engineering-conventions §10.8):沿 frontmatter `上级:` 拼树打印,回答「某计划在上线计划的哪个位置 / 某线推进到哪」 |
| `rubric-sync-gate.py` | 品类 rubric fixture ↔ skill 双写对账门Δ5「无校验器不算契约」执行面`cheap-worker/fixtures/genre-rubrics/<genre>.json` 为单源,校验各品类 skill 的内联副本名目一致 / 纯指针可达,漂移 exit 1已挂 pre-commit + Gitea Actions |
| `plugin-surface-gate.py` | 便宜档插件面三方一致性对账门(防漂移):插件目录 / prompt 白名单cheap-system.md/ host-config 注入 + tools.mjs PLUGIN_KEY_DIR 必须对齐,且每个插件目录显式裁定为「注入/取证/储备」之一;漂移 exit 1已挂 pre-commit + Gitea Actions |
| `doc-organizer.{sh,-analyze.mjs}` + `doc-organizer-state.json` | doc-organizer 底层检测scan/health/stale/ledger+ 可逆归档 + 记时 + 三轴分析 Workflow + 增量窗口锚点 |
| `workflows/ai-development-protocol.md` | 任务承接→分析→评审→执行→验证→沉淀的完整协议 |
| `workflows/mvp-execution-orchestration.md` | MVP 10-Agent×3周 执行编排 + 8 条复利提效策略 |
---
## 三、按任务类型查阅
## 三、使用方式(按任务类型查阅)
| 任务类型 | 先读 | 再读 |
|---|---|---|
| **分析** | `workflows/ai-development-protocol.md` + 相关 `knowledge/` | 原始 `docs/architecture/` 长文档 |
| **评审** | `rules/`(拿约束当尺子) + `knowledge/` | 对应 `skills/` 看实践标准 |
| **编码** | 对应 `skills/` + `rules/engineering-conventions.md` | `knowledge/` 对齐上下文 |
| **调试** | 相关 `skills/` + `knowledge/` | `rules/security-and-reliability.md` |
| **编码** | 对应 `skills/`(操作手册) + `rules/engineering-conventions.md` | `knowledge/` 对齐上下文 |
| **调试** | `knowledge/glossary.md` + 相关 `skills/` | `rules/security-and-reliability.md`(排查可靠性/幂等问题) |
> 通用顺序:**先 knowledge 对齐事实 → 看 rules 划红线 → 用 skills 落地 → 按 workflows 推进与收尾。**
---
## 四、维护规则(要点
## 四、维护规则(关键
1. **先查重再新增**:全新主题才新建文件;补充/修正既有主题一律更新现有文件,不造重复。
2. **过时即处理**:信息失效立即修正或删除;与代码/文档冲突时以已验证事实为准。
3. **同步索引**:任何结构性变更(增/删/改名)同步更新本 README 清单与 [`../AGENTS.md`](../AGENTS.md) 导航。
4. **单一主题、精炼、可检索**:每文件聚焦一个主题,用表格与要点,做蒸馏而非照搬源文档。
5. **相对路径 + 简体中文**:交叉引用用相对路径(仓库迁移不失效);一律简体中文;死链改 `git show <sha>^:<path>` 定位(见 `tools/docs-gate.py` G3
`.agents/` 的价值取决于是否被持续、规范地维护。务必遵守:
> 维护本身是任务收尾的一部分——见 [`workflows/ai-development-protocol.md`](workflows/ai-development-protocol.md) 的"沉淀"环节与 [`skills/wave-close-checklist.md`](skills/wave-close-checklist.md)。
1. **何时新增 vs 更新现有**
- 出现**全新主题**(新模块手册、新流程)→ 新增文件。
- 是对已有主题的**补充/修正**→ 更新现有文件,不要另起炉灶造重复。
2. **先查重**:新增前先检索本目录,避免重复条目和同义文件。
3. **过时即处理**:信息失效时**立即修正或删除**,不留误导性内容;与代码/文档冲突时以已验证事实为准。
4. **单一主题、精炼、可检索**:每个文件聚焦一个主题,用表格与要点,便于 Agent 快速定位;避免照搬源文档大段内容,做蒸馏与索引。
5. **同步索引与交叉链接**:任何对 `.agents/` 的结构性变更(增/删/改名文件),都要**同步更新本 README 的清单**,以及 [`../AGENTS.md`](../AGENTS.md) 中相关导航与交叉链接,保持全局一致。
6. **语言**:一律使用**简体中文**。
7. **相对路径链接**:文件间交叉引用使用相对路径,保证仓库迁移后链接不失效。
> 维护本身就是任务收尾的一部分——见 [`workflows/ai-development-protocol.md`](workflows/ai-development-protocol.md) 的"沉淀"环节。

View File

@ -1,86 +0,0 @@
---
name: agentscope-2.0-facts
description: AgentScope 2.0 架构事实速查(源码实证 v2.0.3 + 官方文档)——编排/沙箱/服务化部署,给 tier2 富游戏线设计用
node_type: knowledge
---
# AgentScope 2.0 架构事实(源码实证 + 官方文档)
本文记录 AgentScope 2.0 的真实形态。来源是直接读 `/root/oss/agentscope`checkout 在正式版 **v2.0.3**,即项目当前锁定的稳定版)的源码,并与官方文档 `docs.agentscope.io/v2` 交叉验证,每条关键结论都给了源码出处。
**版本口径**:项目当前钉 AgentScope **2.0.3**2026-07-06 从 2.0.2 升级而来。2.0.3 已正式发布到 PyPI此前文档里「PyPI 没有 2.0.3、那是笔误」的说法不再成立据此更正。1.0.x 到 2.0.x 是同一条线的连续大版本演进2.0.2 与 2.0.3 是这条线上相邻的两个补丁版;本文事实已按 2.0.3 源码逐条复核。引用时以项目锁定的 2.0.3 为准。
## 核心范式
2.0 是一次架构大重写。1.x 的静态编排原语(`pipeline` / `MsgHub` / `sequential_pipeline`)已经全部删除——在 `src/` 下搜这些类名零命中。取而代之的是「单个自带 ReAct 循环的 `Agent` 类 + 一个 FastAPI 服务层(`agentscope.app`)」。多 agent 协作、对外发布、沙箱执行这些能力,都上移到了服务层,而不再是库里的拼接式管道。
## 编排:单 Agent 自治 + 服务层 Team 工具
`agentscope.agent` 模块只导出一个统一的 `Agent` 类,它自带 reasoning→acting 的 ReAct 循环(`agent/_agent.py:622` 的迭代主体,`max_iters` 兜底退出。1.x 那种声明式的 agent 子类ReActAgent / DialogAgent和消息管道都不存在了。
需要多个 agent 协作时,走的不是静态拓扑图,而是**把「建队、派 worker、对话」做成工具交给 leader agent 自己调用**`AgentCreate` / `TeamCreate` / `TeamSay` / `TeamDelete` 四个工具配 `SubAgentTemplate`worker 蓝图。leader 调 `AgentCreate` 就 spawn 一个 worker agent + sessionworker 的 `source="team"`、只拿得到 `TeamSay`(向 leader 汇报leader 拿全套工具(`app/_service/_toolkit.py``source` 选工具可见性)。这是 agent-as-tool 的动态组队,对应官方哲学——靠模型自身的推理和工具调用,而不用刚性 prompt 和带倾向的编排去约束它。
**一个硬约束**:这套多 agent 能力**只在 app 服务层可用**,纯库里 import 一个 `Agent` 跑脚本是没有的。
## 沙箱:编排后工具调用直接落沙箱执行
Workspace 提供三种执行后端(`workspace/__init__.py``LocalWorkspace`host 进程内,零隔离,开发用)、`DockerWorkspace`(容器)、`E2BWorkspace`E2B 云沙箱。agent 的工具来自 workspace`get_toolkit(workspace=...)``tools = await workspace.list_tools()`),所以「换一个 workspace」就把执行环境从本地搬到了容器或云沙箱。
执行如何落进沙箱Local 后端的 Bash/Read/Write 是 host 进程内的 Python 工具Docker/E2B 后端的 `list_tools()` 返回空,工具改由容器内的 FastAPI MCP gateway 提供——agent 调工具时host 侧 `GatewayClient``HTTP POST {gateway}/mcps/{mcp}/tools/{tool}``_gateway_client.py:162`)到容器内 gateway 真正执行。这就是「编排好之后工具调用直接在沙箱里跑」的机制。
**一个坑**Docker/E2B 沙箱镜像默认不注册任何 filesystem/bash MCP要在沙箱里有 Bash/Read/Write得自带 MCP server 注册为 `default_mcps`;只有 Local 后端内置这些工具。
## 安装与部署pip 装包即可,官方只主推一条生产路径
跑 AgentScope **不需要 clone 仓库**——`pip install agentscope[full]` 装包即可,`app` / `workspace` / `tool` 全在 wheel 内。框架**没有 CLI**pyproject 无 `[project.scripts]`,不存在 `as serve` 之类命令),启动靠在 `main.py` 里自己调 uvicorn。两个例外要记Docker 沙箱早先在 2.0.x 尚未上 PyPI 的过渡期需要本地备有 agentscope 源码(构建镜像时 COPY 进去——2.0.3 已发布到 PyPI这条过渡期约束随之解除镜像可直接 pip 安装E2B 沙箱仍需 E2B 账号和 API key。`/root/oss/agentscope` 这份 clone 的主要价值是读源码核对,而非生产运行的必需品。
生产部署,官方主推且唯一给出端到端示例的形态是 **Agent Service**:用 `create_app(storage, message_bus, workspace_manager)` 造一个 FastAPI/ASGI app`app/_app.py:42`),再用 uvicorn 起进程(`examples/agent_service/`。它提供多租户、多会话、REST 触发 + SSE 事件流、HITL 审批、定时任务、凭据托管。两个要点决定了接入方式:一是 **chat 端点是 fire-and-forget**`POST /chat` 立即返回 `started`,事件要另外从 `GET /sessions/{id}/stream` 的 SSE 流里收;二是**强依赖 Redis**——storage 和 message_bus 都只有 Redis 实现,没有 in-memory/SQLite 版本,生产必须配 Redis。要弹性扩展就把这个 ASGI app 自己容器化后上 K8s 或 serverless框架不提供 app 级的 Dockerfile/compose。
## 生态定位与一个架构信号
独立的 `agentscope-runtime` 项目**已归档**官方声明其全部能力工具沙箱、Agent-as-a-Service、可观测已原生并入 AgentScope 2.0。所以 2.x 做沙箱和部署不要再装那个独立仓,`agentscope[full]` 一个包就齐。AgentScope Studio 是开发期的可视化追踪/调试台,不是生产承载层。
一个值得记的信号:官方称 **Spring AI Alibaba 底层将采用 agentscope-java 作为引擎**。这意味着项目里「SAA 廉价线」和「AgentScope 富游戏线」在引擎血统上正趋同。这条长期架构判断**已裁定(2026-06-25 reframe):框架收敛 AgentScope、SAA 降最低优先级(留作远期适配验证可插拔的目标)**——三档生成不再按引擎或产线分,而按 AI 参与深度切;详见运行时 SoT [`生成引擎/agentic运行时架构图说`](../../docs/architecture/架构/生成引擎/agentic运行时架构图说.md) §一 / §四。相关决策见 [[saa-agentic-infra-decision]]。
## 关键纠偏对照
| 凭名臆想 | 源码实证v2.0.3 |
|---|---|
| 2.x = pipeline / MsgHub | 已删,改单 Agent ReAct + 服务层 Team 工具 |
| 多 agent 是静态编排图 | agent-as-tool 动态组队,且只在 app 服务层 |
| 用框架要 clone 源码 | `pip install agentscope[full]` 即可Docker 沙箱过渡期暂需源码) |
| 有 CLI / `serve` 命令 | 无,生产 = `create_app()` FastAPI + uvicorn + Redis |
| 沙箱要装独立 agentscope-runtime | 已归档,能力并入 2.0 主框架 |
## 便宜档 cheap-worker 实现 API 速查2.0.3 源码实证 + WU-A spike 落地)
上文偏架构(编排/沙箱/服务化)。本节是「怎么用库写一个跑通的 agent」的实现层速查——WU-A spike 把便宜档生成核心重写进 AgentScope`cheap-worker/`import 复用 tier2 框架层)时落地并核验,下一个 WU-A 全量重写 / n=5 直接照用,不必重新挖。
**import 路径(全部源码核验存在)**`from agentscope.agent import Agent, ReActConfig, ContextConfig` · `from agentscope.tool import Toolkit, FunctionTool` · `from agentscope.middleware import MiddlewareBase` · `from agentscope.model import OpenAIChatModel, AnthropicChatModel` · `from agentscope.message import UserMsg, TextBlock` · `from agentscope.state import AgentState` · `from agentscope.permission import PermissionContext, PermissionMode`
**三个「凭名臆想会用错」的不存在 API源码全树零命中别用**
- `register_tool_function` **不存在** → 注册靠构造器 `Toolkit(tools=[FunctionTool(fn), ...])`
- `ReplyBudgetControlMiddleware` **不存在** → 预算/软刹得自挂 `MiddlewareBase``on_system_prompt`/`on_model_call` 钩子tier2 的 `CircuitBreakerMiddleware` 即如此自实现)。
- 内置 ReAct **不会「跑满 max_iters 才停」** → 在「模型产出无 tool_call 的纯文本回合」即退出(`agent/_agent.py`)。要多轮自修必须在 Agent 外**自建有界 resume 循环**`for attempt in range(max_resumes): await agent.reply(...); 判收敛/门绿;否则带失败反馈再 reply`。这一圈正对应 Node 生成 harness 的 while 主循环。**2026-07-02 阶段一① 更新tier2 已改用 `on_reasoning` middleware 在洋葱内拦 finish 续修、取代这圈 Agent 外 resume——见下「护城河续修/软预算 middleware 的 2.0.3 机制」段与 [`tech-decisions.md`](tech-decisions.md) §11外层 resume 仍留 fallback。**
**工具写法schema 自动抽)**:每个工具是 `async def` + 参数类型注解 + Google docstring`Args:``FunctionTool(fn)` 据此抽 schema闭包捕获一个可变 session 对象做跨工具状态;返回 str纯文本或 `json.dumps`)。
**Agent 构造(无人值守跑脚本)**`Agent(name, system_prompt, model, toolkit, middlewares=[trace, breaker], state=AgentState(permission_context=PermissionContext(mode=PermissionMode.BYPASS)), react_config=ReActConfig(max_iters=...), context_config=ContextConfig(...))`。**BYPASS 必给**——否则 FunctionTool 默认 check_permissions 返回 ASK无人值守跑会卡死。`reply` 是 async`await agent.reply(UserMsg(name="user", content=text))`UserMsg 必带 name。**model 直调(验连通用)**`await model([UserMsg(...)]) → ChatResponse(content=[TextBlock(text=...)], ...)`
**便宜档复用 tier2 框架层import 不重写、零改 tier2**`config.build_model_openai("MiniMax-M3", max_tokens=16000, record=True)`OpenAI 协议、自动补 /v1、自动装代理旁路 + 取 key· `config.build_context_config()`(历史压缩)· `CircuitBreakerMiddleware` + `Tier2TraceMiddleware`(四道熔断 + trace与模型档无关`enable_rmb_gate=False` 可关 ¥ 闸)· `client.install_proxy_bypass`**必须在 import openai 之前**,否则本机 clash 把发往内网网关的请求转走得 502· `client.get_api_key()`**只读 env `NEWAPI_KEY`**;内网阶段 key 在 `docs/内网凭据与端点.md`,调用方先解析注入 env
**跨包 import 含连字符目录**`gen-worker` 不能 `import`;把 `tier2/gen-worker/` 加进 `sys.path``from worker import ...``from observability import ...` 同理)。
落地实证 = 便宜档 Python 线 `cheap-worker/`WU-A spike端到端九门对照 Node 9/9 持平、过门率 1.0=1.0、对 tier2 零改动)。便宜档生成线现状见 [`cheap-model-game-generation`](../skills/cheap-model-game-generation.md)(注意其旧 W-G1 路已被 cheap-worker 取代)。
## 护城河续修/软预算 middleware 的 2.0.3 机制速查(阶段一① 落地2026-07-02
多轮自修除了在 Agent 外自建 resume 循环,还有护城河的正解——在 middleware 洋葱内拦 finish 续修、不每轮重开 POSTtier2 阶段一① 落地)。这里记住 2.0.3 的几个机制事实,免得下次碰 middleware 又去翻源码:
- **finish 是什么**ReAct 在「模型产出无 tool_call 的纯文本回合」即退出,那个纯文本 `Msg` 就是 finish。它在 `agent/_agent.py` 里先存进 context约 :884`_save_to_context`)再 yield约 :902穿过 `on_reasoning` 洋葱链才到 reply 收尾循环。
- **怎么拦、怎么续**`on_reasoning` middleware 遍历 `next_handler` 吐的事件,遇到那个 finish `Msg` 就**不 yield吞掉**——本轮没有 Msgreply 循环 `cur_iter++` 继续下一轮推理,等于续跑。要让 agent 真去改,用 `agent.observe(UserMsg(...))` 注入一条 role=user 的反馈system/tool/thinking 角色会让内部 `_handle_incoming_messages` 抛 ValueError。注意吞掉 finish 前 agent 已把「做完了」文本存进 context压制 yield 抹不掉,要 `context.pop()` 清掉才有干净血缘。
- **洋葱序**Service 框架在 extra middleware 外还前置了 `InboxMiddleware`(挂 on_reasoning、每轮 drain inbox 后透传全部事件、不吞 finish所以工厂注入 `[tracer, repair, breaker]` 的实际 on_reasoning 链是 `[InboxMiddleware, tracer, repair]`——repair 在最内层、能拦到 `_reasoning` 产的原始 finishbreaker 不挂 on_reasoning
- **软预算/门判为什么必须自挂**`ReplyBudgetControlMiddleware` 这类不存在(见上),预算软刹得自挂 `on_model_call`(¥ 闸)/`on_system_prompt`(提醒);放行门判得 middleware 自己重跑 `run_gates` 独立判,绝不采信 agent 上报的门结果。
本项目落地的三个类(`RepairMiddleware` / `CircuitBreakerMiddleware.soft_budget` / `gate_judge`与那个红线坑run_gates 起跑前清 verdict 防陈旧假绿)见 [`tech-decisions.md`](tech-decisions.md) §11。

View File

@ -0,0 +1,53 @@
# 术语表
> 蒸馏来源:技术决策版 §11.1 附录术语表、§3.4 SDK 设计、§4 核心链路;执行 spec。
> 目的:统一项目黑话,调试/评审/编码时快速对齐。每条一句话。
> 相关:[`product-and-architecture.md`](./product-and-architecture.md) · [`tech-decisions.md`](./tech-decisions.md) · [`mvp-scope-and-milestones.md`](./mvp-scope-and-milestones.md)。
| 术语 | 一句话释义 |
|---|---|
| **GameConfig** | 游戏配置 JSON描述玩法/关卡/角色/规则;创作者通过配置(而非写 JS驱动游戏是安全合规可控的基础。 |
| **GamePackage** | 可运行的游戏包 = 代码 + 资源 assets + manifest由 aigc 产出、runtime 编译打包上传 OSS。 |
| **Manifest**GameManifest | 游戏包描述文件,含 runtimeVersion/configUrl/入口 entry/assetList/hash/checksum/preloadPolicy/bundleSize宿主据它加载资源与决定加载哪些 SDK Plugin。 |
| **GenerationTask** | 一次 AI 生成任务实体,记录 prompt/template_id/~~dify_workflow_id~~Dify 已降级远期未部署,该字段为 1.x 蓝图残留;现行生成经 new-api 网关,详见 tech-decisions §1/retry_count/duration_ms/error_code状态机queued→running→succeeded/failed/timed_out/canceled。 |
| **quality_score** | 基于玩家行为信号(完成率/30s 留存/点赞率/收藏率/举报率/失败率)聚合的游戏质量评分;由 telemetry 算出,反哺 feed 推荐排序。 |
| **Feed** | 游戏流推荐列表("像刷短视频一样发现游戏"MVP 用规则+行为信号排序,候选集存 Redis Sorted Set、TTL 60s、cursor 分页。 |
| **DAG** | 有向无环图,描述 AI 生成工作流的节点编排解析→匹配→LLM→校验→资源→打包。~~由 Dify 可视化编排~~**2026-06-12 纠偏**Dify 已降级远期、从未部署;**2026-06-15 再纠偏 HJ-AGI-002**:现行 agentic 编排基建 = **SAA(Spring AI Alibaba v1.1.2.2)裸图编排 (HJ-AGI-002short-term SAA-onlyAgentScope 降 long-term premium 独立轨)**;「自研编排/DAG 工作流引擎」表述作废)。 |
| ~~**Game Skill**~~OpenGame 概念,已退役) | ~~OpenGame 的经验积累机制,类似模板级 few-shot 模板,提升生成质量。~~ **2026-06-12 纠偏**OpenGame 降级远期、从未部署C2/HJ-GEN-001本术语仅留作历史勿作现行依赖。 |
| **WanxiangGameSDK** | 注入到每个生成游戏中的平台 SDK是平台能力注入 iframe 沙箱游戏的**唯一通道**(无 SDK 则平台仅为静态托管)。 |
| **SDK Core** | SDK 核心层,内联到游戏入口、压缩后 < 8KB Lifecycle/EventBus/Telemetry/ErrorTrack必选失败即静默丢弃游戏零感知 |
| **SDK Plugin** | SDK 插件层,按需懒加载、不影响首屏,含 Ad/Pay/Social/Storage/Debug失败即跳过并给兜底如广告失败免费发奖励。 |
| **三容器策略** | 游戏流预加载机制(参考抖音):只保留前一/当前/后一三个 iframe 容器,当前播放时预加载下一款 Manifest+关键资源,超时自动跳过下一款。 |
| **保底曝光**bonus_new_creator | 新创作者前 3 个作品给固定基础曝光量,避免冷启动无人可见,是创作者激励的关键。 |
| **降权**(低质降权) | 高跳出/加载失败/高举报的内容自动下调推荐权重error_rate 为硬降权report_rate 达阈值触发人工审核)。 |
| **idempotency_key** | 幂等键,防止"重复点击生成/重复提交/MQ 重复消费/支付回调重复"导致重复处理Redis 5min 去重 + 状态机 + 乐观锁)。 |
| **DataPermission** | Huijing 数据权限机制,实现行级数据隔离(如创作者只看自己的项目/资产)。 |
| **单体启动** | 13 个业务模块编译为同一 JARgame-server用 Spring Profile 控制模块加载;需独立扩缩时改 Nacos 配置即拆为独立微服务Huijing Cloud 原生支持)。 |
| **契约先行**contract-first | Day 0 先锁定契约文件写入 `contracts/` 提交 git各工位据此 mock 并行开发,联调延后至 Day 11。共 **8 类契约**7 个 Day-0 `contracts/` 文件API/DB/SDK/GamePackage/事件/~~Dify IO~~Dify 已降级远期未部署,见 `contracts/DEPRECATED-dify-workflow-io.md`/广告位)+ Prompt Registry`contracts/prompts/`)为第 8 类。 |
| **门禁7 道)** | 创作全链路 7 道质量/合规阻断点①Prompt 安全 ②AI 产出合规 ③资产入库版权+风格 ④组装 Schema 完整性 ⑤编译后性能≤10MB/首屏≤2MB/无外网) ⑥预览可玩性自测 ⑦发布终审(合规+适龄)。 |
| **Fallback 生成器**(确定性 Fallback | LLM 不可用/超时/熔断时退化为确定性兜底产出,保证生成链路不全断。(~~"模板填充"~~ 措辞按 2026-06-12 模板哲学更新:游戏模板/填参线已废W-CLEAN兜底走固定可玩产物非旧"模板填参"线;生成主线=agent 写码于插件库 HJ-GEN-001。注玩法模板品类框架·引导生成·非 pre-built 代码)未废待建。) |
| **Game SDK 降级铁律** | Plugin 层代码全部 try-catch 包裹、异常不向游戏抛游戏主循环requestAnimationFrame永不被 SDK 阻塞——"游戏稳定性 > 数据完整性"。 |
| **WS1-WS5** | MVP 5 个工位WS1 平台基座、WS2 AI 生成、WS3 运行时与分发、WS4 产品前端、WS5 数据与变现(详见 mvp-scope-and-milestones.md。 |
| **M0-M5** | MVP 6 个里程碑M0 契约锁定 / M1 全栈可启动 / M2 创作链路 / M3 分发链路 / M4 变现链路 / M5 MVP 交付。 |
| **三仓库** | game-cloud后端 Huijing fork/ game-adminVue3+Element Plus 管理后台)/ game-studioVue3+Vant 产品端),三个独立 Git 仓库。 |
| **game-module-{name}** | 游戏领域自研业务模块统一命名13 个studio/project/aigc/runtime/feed/telemetry/pay/trade/community/ip/compliance/biz/ad`-api`/`-biz` 分层。 |
| **锁风门 Gate**style-lock gate | 发布前风格/版权/性能聚合检查门owner=complianceT-CMP-12MVP 做 pass/block 二态,承载 demo"发布前检查通过才可发布",强度策略(标准/严格/人工)为远期。 |
| **专区 Zone双轨** | 广场/游戏流的双轨分区现象级授权IP区 / UGC孵化IP区Zone 实体与归属 owner=project(T-PRJ-07)、分区推荐 owner=feed(T-FED-15)、发布去向 launchZone 选择。 |
| **创作会话**studio 会话) | studio 的有状态创作上下文(会话状态机+持久化 T-STU-01承载草稿装配/六资产调度/任务链/附件,是 aigc 收敛后"有状态编排域"核心(区别于 aigc 无状态生成原子)。 |
| **资产图**Asset Graph | studio 的多资产组合关系模型(T-STU-02):描述图元/角色/特效/场景/界面/音乐六类资产如何组装成一款游戏;区别于"资产空间"(创作者素材仓库)。 |
| **三种创作模式** | 覆盖小白到专业:模式 A 一句话生成Prompt→成品AI 主导)、模式 B 资产驱动创作先攒资产再组装AI 辅助单项)、模式 C 工作流编排(专业创作者自定义编排节点)。(**2026-06-12 纠偏**:模式 C 原述「在 Dify 可视化自定义节点」中的 ~~Dify~~ 已降级远期未部署;现行/远期编排基建 = SAA(Spring AI Alibaba v1.1.2.2)裸图编排 (HJ-AGI-002short-term SAA-onlyAgentScope 降 long-term premium 独立轨)。) |
| **资产空间**Asset Workspace | 每个创作者独立、归其所有的素材空间,含视觉/音频/设计/商业化四类资产;每类资产支持 AI 生成、手动上传、市场获取三种产出方式。 |
| **DataPermission 之外的隔离** | 匿名玩家=**免登读路径**(读端点 `@PermitAll` + 前端 anonId 透传),只能浏览试玩、不能发布/收藏/进后台。⚠️ 2026-06-10 显式变更(鉴权拍板 §9-7=B**不再走 framework 层"匿名 Token"机制**——anonId 为纯客户端生成、身份不可信,防伪造/刷量由 telemetry 聚合侧按 anonId/IP 异常剔除兜底(见 `docs/agent-specs/_archive/2026-06-10-真实鉴权与匿名玩家-review.md` §5A/§9.1)。 |
| **eCPM** | 每千次广告展示收益,是广告变现核心指标;平台用游戏内容标签 + 玩家画像优化 eCPM。 |
| **T+1结算周期** | 自有渠道广告分成次日T+1自动入账创作者钱包外部渠道按月结算满 5 元即可提现。 |
| **分成比例** | 广告收益按创作者层级分成:小白 80% / 进阶 75% / 专业 70%(投资人叙事统一口径"创作者拿 80%"D3 拍板 MVP 统一 80%)。**R3 叠加规则2026-06-10 拍板)**:基数=平台从渠道实收**净额**(微信 IAA 渠道先扣 40%);使用 IP 素材时 IP 授权分成 15-25% **从创作者份额内扣**(创作者实得 55-65%);平台恒留净额 20%。模型见 `docs/mvp/单位经济敏感性模型.md`。 |
| **SLO / Error Budget** | 服务可用性目标与可消耗的不可用预算:游戏流 API 99.5%(月 3.6h、AI 生成 99%(月 7.2h)、支付 99.9%(月 43min。 |
| **最终一致性 + 补偿** | 跨模块写操作尽量不用分布式事务,改"本地事务 + MQ 事件 + 失败补偿",定时任务扫描超 5 分钟"中间态"记录重试或告警。 |
| **Golden Config 回归** | 用模板标准配置样例做比对回归,确保模型/模板迭代后生成质量不退化。 |
| **trace_id** | Gateway 入口注入、全链路透传(含生成侧 new-api 网关调用与运行时 SDK~~Dify/OpenGame~~ 已降级远期未部署)的追踪 ID是调试与可观测的主线。 |
| **DAU** | 日活跃用户MVP 目标 1,000 DAU正式目标 100,000 DAU。 |
| **分层运行时Tier1/2/3** | 游戏产物三层Tier1 极轻量H5/2DMVP 唯一交付层;**引擎=LittleJS 增强发行版2026-06-12 终裁**+Runner v2~~模板双层架构〔核心代码模板/玩法模板〕~~ → **模板=LittleJS 能力插件/二次开发件**2026-06-12 模板哲学重申:游戏模板/填参式双层架构已废,玩法/美术/关卡/UI=agent 生成域,好玩基线 v2 改挂评估门玩法模板品类框架未废待建15KB 红线已废除、改三层约束框架,见 `docs/brainstorms/2026-06-11-tier1-runtime-constraints-requirements.md`Tier2 复杂2D+3D / Tier3 独立App=Cocos Creator 3.8.8+MCP。详见 tech-decisions §1.1。 |
| **Cocos-MCP** | 用 MCP 协议(158 工具)让 AI 驱动 Cocos Creator 3.8.8 编辑器做复杂2D/3D/原生游戏;属有状态 agentic 编排、归 studio区别于 aigc 的无状态单次文生代码原子;~~原对照"OpenGame"~~ 已降级远期未部署)。 |
| **Prompt RegistryPrompt 即契约)** | git `contracts/prompts/` 为全生命周期 prompt 的单一事实源(第 8 类契约):版本化 + 输入输出 Schema 绑定 + 约束块 + Golden 集 + owner运行时按 `id@version` 加载注入、不内嵌引擎内核。 |
| **Prompt 轻量门禁** | prompt 改动 PR 触发的效果验证Schema 通过率 + 生成成功率≥80% + Golden 回归 + 成本/延迟不劣化 + 人工抽检;可玩性靠行为指标反哺、不做结构化自动评分。 |
| **T-AGC-09 测试脚本生成** | aigc 的无状态原子GameConfig→可玩性测试脚本由 runtime 编译流水线执行为入库门禁。 |

View File

@ -1,8 +1,8 @@
# MVP 范围与里程碑事实蒸馏
> 蒸馏来源:`docs/superpowers/specs/mvp-execution-spec-design.md`HJ-MVP-SPEC-001执行权威`docs/architecture/产品/需求清单.md`Doc A 产品功能)、`docs/architecture/产品/需求模块映射.md`Doc C 需求↔模块 RTM`docs/architecture/架构/README.md`§1.2 指标、§9 路线)。
> 蒸馏来源:`docs/superpowers/specs/mvp-execution-spec-design.md`HJ-MVP-SPEC-001执行权威`docs/architecture/产品需求清单.md`Doc A 产品功能)、`docs/architecture/需求模块映射.md`Doc C 需求↔模块 RTM`docs/architecture/系统概要设计-技术决策版.md`§1.2 指标、§9 路线)。
> 目的:让后续 Agent 一篇掌握 MVP 端到端闭环、量化验收线、范围口径、5 工位分工、M0-M5 里程碑、契约先行要点。
> 相关:架构模块见 [`product-and-architecture.md`](./product-and-architecture.md);选型与"范围张力"见 [`tech-decisions.md`](./tech-decisions.md)。契约先行 playbook 见 [`../skills/contract-first-development.md`](../skills/contract-first-development.md)。
> 相关:架构模块见 [`product-and-architecture.md`](./product-and-architecture.md);选型与"范围张力"见 [`tech-decisions.md`](./tech-decisions.md);术语见 [`glossary.md`](./glossary.md)。契约先行 playbook 见 [`../skills/contract-first-development.md`](../skills/contract-first-development.md)。
---

View File

@ -1,19 +1,19 @@
# 产品与架构事实蒸馏
> 蒸馏来源:`docs/architecture/产品/商业定位.md`、`docs/architecture/产品/需求清单.md``docs/architecture/架构/13模块.md``docs/architecture/产品/需求模块映射.md``docs/architecture/架构/README.md``docs/architecture/架构/README.md`
> 蒸馏来源:`docs/architecture/系统概要设计-投资人版.md`、`docs/architecture/产品需求清单.md``docs/architecture/技术架构与模块.md``docs/architecture/需求模块映射.md``docs/architecture/系统概要设计-技术决策版.md``docs/architecture/系统概要设计-开发团队版.md`
> 目的:让后续 Agent 读本篇即掌握产品定位、用户角色、分层架构、三仓库、13 模块与依赖、Huijing 复用边界、SDK 定位,不必重读原始长文档。
> 相关:决策见 [`tech-decisions.md`](./tech-decisions.md)MVP 范围见 [`mvp-scope-and-milestones.md`](./mvp-scope-and-milestones.md)。
> 相关:决策见 [`tech-decisions.md`](./tech-decisions.md)MVP 范围见 [`mvp-scope-and-milestones.md`](./mvp-scope-and-milestones.md);术语见 [`glossary.md`](./glossary.md)
> **子系统现行设计+现状(深一层 SoT**:见 `docs/agent-specs/` 子系统 canonical 活档——`引擎与运行时` / `agentic编排-SAA` / `开闸验收门-W-G1` / `渠道发行` / `studio前端设计体系` / `变现与单位经济` / `战略与合规` / `prompt治理体系-execution`;生成域=固定架构设计链;导航见 [`../../docs/agent-specs/_index.md`](../../docs/agent-specs/_index.md)。本篇=速查蒸馏,重叠处以 canonical 为 SoT见 engineering-conventions §10.5)。
---
## 1. 产品定位与差异化
绘境AI 是 AI 驱动的全民游戏创作与变现生态平台。核心命题:**让零基础创作者用一句话做出可上线、可变现的轻量小游戏让玩家像刷短视频一样发现和试玩让平台通过广告分成、订阅、B 端定制实现商业闭环。**
造梦AI 是 AI 驱动的全民游戏创作与变现生态平台。核心命题:**让零基础创作者用一句话做出可上线、可变现的轻量小游戏让玩家像刷短视频一样发现和试玩让平台通过广告分成、订阅、B 端定制实现商业闭环。**
差异化 = **全闭环**:生成 + 流量 + 变现三件事同时解决。竞品各有短板:
| 竞品 | 强项 | 短板 | 绘境策略 |
| 竞品 | 强项 | 短板 | 造梦策略 |
|---|---|---|---|
| 极逸 SOON | 技术最强(自研三引擎+大模型) | 无流量、无变现闭环 | 不自研引擎,用开源组合,把钱花在流量和变现 |
| TapTap 制造 | 有流量 | 封闭、分成低 | 开放多渠道、创作者拿 80% 分成 |
@ -44,9 +44,9 @@
## 3. 系统分层架构
> **⚠️ 现行口径回填2026-06-12单源已 commit `1272383` 清毒;本蒸馏同步纠偏)**下方分层图的「AI 引擎层 / 运行时栈 / 中间件层」为 1.x 蓝图态,部分组件已非现行主线,按下列裁决读:
> - **AI 引擎层**:生成主线 = **new-api 网关 → 便宜模型直连**agent 写码于插件库HJ-GEN-001 终审);**Dify/OpenGame = 降级远期、从未部署**C2 / HJ-GEN-001。agentic 编排基建 = **AgentScope2026-06-25 框架 reframe三档统一收敛一个 agent 框架SAA / dify / coze 降最低优先级、留作「远期适配验证可插拔」目标)**——~~原「SAA-onlyAgentScope 降 long-term premium 独立轨」(HJ-AGI-002)~~ 已被该 reframe 推翻SAA 当时在产是历史事实,分轨口径不再现行)。「自研编排/DAG 工作流引擎」表述作废。
> - **运行时栈 Tier1**= **LittleJS 增强发行版 + Runner v2**2026-06-12 创始人终裁);「自研 Canvas Runtime<15KB废除;「模板双层/游戏模板填参式整局代码)」废除 W-CLEAN )→模板=LittleJS 能力插件/二次开发件玩法/美术/关卡/UI=agent 生成域:「玩法模板」(品类框架·引导生成·非填参代码未废有效·待建但非最高优先级最高=Tier 0 生成可靠玩法模板排其后generic 桥接 7534bdf9 暂解 limboHJ-DEMO-AUDIT-001 创始人 06-17)。**引擎重定为正交两轴2026-06-21·tier2 触发推翻旧Tier2/3=Cocos+MCP 不变」)**自治富游戏生成轨全无头·纯代码 2D=Phaser/Pixi轻量档=LittleJS走无人值守生成循环3D/渠道导出轴Cocos Creator 编辑器+人在环做复杂 3D/渠道包旧锁定的依据MCP 158 工具让 AI 驱动可行是事实错—— MCP 是编辑器扩展需编辑器在跑 headless Cocos 进不了自治生成轨详见 [`tech-decisions.md`](./tech-decisions.md) §1.1 tier2 富游戏设计并入运行时 SoT `docs/architecture/架构/生成引擎/agentic运行时架构图说.md` §4.25
> - **中间件层****Nacos / RocketMQ / Sentinel = MVP 生产基建三件套,已自托管 mini-infra**2026-07-01 build-vs-buy 现货尽调采纳推翻旧「future-state 不部署」定性——Nacos 配置中心 + 服务发现、RocketMQ 异步 gen 队列、Sentinel Java 准入侧流控;见 [`tech-decisions.md`](./tech-decisions.md) §10)。
> - **AI 引擎层**:生成主线 = **new-api 网关 → 便宜模型直连**agent 写码于插件库HJ-GEN-001 终审);**Dify/OpenGame = 降级远期、从未部署**C2 / HJ-GEN-001。agentic 编排基建 = **SAA(Spring AI Alibaba v1.1.2.2)裸图编排 (HJ-AGI-002short-term SAA-onlyAgentScope 降 long-term premium 独立轨)**「自研编排/DAG 工作流引擎」表述作废。
> - **运行时栈 Tier1**= **LittleJS 增强发行版 + Runner v2**2026-06-12 创始人终裁);「自研 Canvas Runtime<15KB废除;「模板双层/游戏模板填参式整局代码)」废除 W-CLEAN )→模板=LittleJS 能力插件/二次开发件玩法/美术/关卡/UI=agent 生成域:「玩法模板」(品类框架·引导生成·非填参代码未废有效·待建但非最高优先级最高=Tier 0 生成可靠玩法模板排其后generic 桥接 7534bdf9 暂解 limboHJ-DEMO-AUDIT-001 创始人 06-17)。Tier2/3 = Cocos+MCP 不变详见 [`tech-decisions.md`](./tech-decisions.md) §1.1
> - **中间件层****RocketMQ / Nacos = future-stateMVP 未部署**(异步走 Spring 内事件/同步链路)。
```
接入层 CDN静态资源/游戏包) + Nginx前端托管/SSL 终结)
@ -54,9 +54,9 @@
网关层 Spring Cloud Gateway路由/限流/鉴权/灰度/CORS
业务服务层 13 个 game-modulestudio/project/aigc/runtime/feed/telemetry/pay/trade/community/ip/compliance/biz/ad
基础设施层 Huijing 原生system用户/权限/OAuth2 / infra文件/任务/日志) / bpm工作流 Flowable
AI 引擎层 ~~DifyDAG 编排/多模型) + OpenGamePython 代码生成)~~(降级远期未部署,见上方回填)→ 现行=new-api 网关直连便宜模型 + AgentScope2026-06-25 reframe三档统一收敛一个 agent 框架;~~SAA 裸图 HJ-AGI-002~~ 降最低优先级、远期适配验证可插拔目标);素材现行=mmx-cli图片/音乐2026-06-12 创始人拍板默认;~~ComfyUI 退备选~~ + Fish Audio·CosyVoice语音/音色)
运行时栈 分层Tier1 = ~~自研壳<15KB~~ → LittleJS 增强发行版 + Runner v22026-06-12 终裁15KB 红线废除、改三层约束框架,见 tech-decisions.md §1.1/ Tier2-3 复杂2D·3D·原生~~统一 Cocos Creator 3.8.8+MCP~~2026-06-21 纠正为正交两轴:自治富 2D 生成轨=Phaser/Pixi 全无头3D/渠道导出轴=Cocos 编辑器+人在环MCP 非 headless 进不了自治轨,见 §1.1;导出以微信小游戏格式包为枢纽
中间件层 Nacos配置中心 + 服务发现) / MySQL 8.0 / Redis 7 / RocketMQ 5异步 gen 队列) / Sentinel准入侧流控 / MinIO本地·阿里云 OSS生产Nacos/RocketMQ/Sentinel 已自托管 mini-infra2026-07-01 反转,见上方回填)
AI 引擎层 ~~DifyDAG 编排/多模型) + OpenGamePython 代码生成)~~(降级远期未部署,见上方回填)→ 现行=new-api 网关直连便宜模型 + SAA(Spring AI Alibaba v1.1.2.2)裸图编排 (HJ-AGI-002);素材现行=mmx-cli图片/音乐2026-06-12 创始人拍板默认;~~ComfyUI 退备选~~ + Fish Audio·CosyVoice语音/音色)
运行时栈 分层Tier1 = ~~自研壳<15KB~~ → LittleJS 增强发行版 + Runner v22026-06-12 终裁15KB 红线废除、改三层约束框架,见 tech-decisions.md §1.1/ Tier2-3 复杂2D·3D·原生 用 Cocos Creator 3.8.8+MCP;导出以微信小游戏格式包为枢纽
中间件层 ~~Nacos~~ / MySQL 8.0 / Redis 7 / ~~RocketMQ 5~~ / MinIO本地·阿里云 OSS生产~~RocketMQ/Nacos~~=future-state 未部署,见上方回填)
可观测性 Prometheus / Grafana / Sentry / Jaeger链路追踪
```
@ -66,7 +66,7 @@ AI 引擎层 ~~DifyDAG 编排/多模型) + OpenGamePython 代码生成
## 4. 三仓库与模块归属
绘境由 3 个独立 Git 仓库组成:
造梦由 3 个独立 Git 仓库组成:
| 仓库 | 定位 | 面向谁 | 技术栈 |
|---|---|---|---|
@ -82,7 +82,7 @@ AI 引擎层 ~~DifyDAG 编排/多模型) + OpenGamePython 代码生成
## 5. 13 个业务模块速查表
> **13 模块**:含 **studio**(创作编排/编辑器域),**aigc 为无状态生成原子**;能力清单按产品功能/技术功能/映射三文档分离(见 `docs/architecture/产品/需求清单.md`、`…-技术架构与模块.md``…-需求模块映射.md`)。
> **13 模块**:含 **studio**(创作编排/编辑器域),**aigc 为无状态生成原子**;能力清单按产品功能/技术功能/映射三文档分离(见 `docs/architecture/产品需求清单.md`、`…-技术架构与模块.md``…-需求模块映射.md`)。
| 模块 ID | 一句话职责 | 面向端 | 被依赖 |
|---|---|---|---|
@ -126,13 +126,11 @@ graph LR
要点:**project 是全局核心被依赖项**feed 与 telemetry 互为反哺feed 消费 quality_scoretelemetry 聚合 feed 行为信号trade 是变现汇聚点(聚合 pay/ad/ip 收益compliance 横切(被 aigc/ip/biz/project 依赖)。
**控制面/管理面(配置注册表 + 观测审计仓 + D12 治理门 + 管理面 UI= 三档生成共用的治理层,横切 aigc/studio/admin**2026-06-25 框架 reframe 后三档统一收敛 AgentScope共用同一套治理面不再是「SAA 廉价线、tier2」两条异构基建分治生成线只管「从注册表读配置、向观测仓写轨迹」配置即数据、全链可观测、可审计。控制面非关键配置经 `/agent` CRUD 或热取即时生效、不重新部署服务代码;关键 agent、关键玩法模板、质量与安全阈值如 prompt 正文与生成门阈值)的变更走 yudao 配置发布审批,是受治理的「发版」。这层不在 13 模块清单里单列、而是跨模块的治理底座,设计见运行时单一真相 SoT [`生成引擎/agentic运行时架构图说.md`](../../docs/architecture/架构/生成引擎/agentic运行时架构图说.md) §5.2(控制面与配置热取)。
---
## 7. Huijing 原生能力复用清单(不需自研,约覆盖 MVP 后台 60%+
| Huijing 模块 | 对应绘境需求 | 复用方式 |
| Huijing 模块 | 对应造梦需求 | 复用方式 |
|---|---|---|
| system用户/角色/菜单/部门/租户) | 多角色 + 多租户隔离 | 直接使用,扩展用户属性 |
| infra-file文件管理 | 封面/素材/游戏包存储 | 直接使用,配 OSS/MinIO |
@ -157,10 +155,9 @@ graph LR
|---|---|
| MySQL 8.0 | 业务实体主库Huijing 原生,事务一致性) |
| Redis 7 | 推荐候选集缓存/限流/排行/熔断状态/分布式锁/幂等去重 |
| RocketMQ 5MVP 生产基建,自托管 mini-infra | 异步:生成任务派发、审核通知、事件摄取、结算触发(延迟/事务/死信消息)。**现状:2026-07-01 build-vs-buy 尽调反转旧「未部署」口径,已落地异步 gen 队列(生产者 + 有界消费 ≤15 + CAS 幂等)** |
| ~~RocketMQ 5~~future-stateMVP 未部署 | 异步:生成任务派发、审核通知、事件摄取、结算触发(延迟/事务/死信消息)。**现状:MVP 走 Spring 内事件/同步链路RocketMQ 留待远期** |
| MinIO / 阿里云 OSS | 游戏包/素材/封面对象存储(本地 MinIO生产 OSS |
| NacosMVP 生产基建,自托管 mini-infra | 注册中心 + 配置中心(多环境/热更新/单体↔微服务切换)。**现状2026-07-01 build-vs-buy 尽调反转旧「未部署」口径已接入服务发现AgentScope Service 注册)+ 配置热读middleware 热参 v1 watcher快照兜底** |
| SentinelMVP 生产基建,自托管 mini-infra | 生成准入侧 Java 流控(`FLOW_GRADE_QPS` 入口投递速率闸,削洪峰)。**现状2026-07-01 反转后已接入;与 DB per-creator 配额、有界 worker 池并发上限 ≤15 三层正交(见 [`tech-decisions.md`](./tech-decisions.md) §10** |
| ~~Nacos~~future-stateMVP 未部署) | 注册中心 + 配置中心(多环境/热更新/单体↔微服务切换)。**现状:单体启动,未引入 Nacos** |
| CDN | 游戏资源/封面静态分发hash 命名长期缓存 |
### 8.2 Game SDKWanxiangGameSDK

View File

@ -1,8 +1,8 @@
# 技术决策事实蒸馏ADR 风格)
> 蒸馏来源:`docs/architecture/架构/README.md`§6 决策记录、§6.6/6.7/6.8 工具链,最权威)、`docs/architecture/架构/13模块.md`(各模块技术栈/边界)、`docs/architecture/架构/README.md`、`docs/superpowers/specs/mvp-execution-spec-design.md`(执行约束)。
> 蒸馏来源:`docs/architecture/系统概要设计-技术决策版.md`§6 决策记录、§6.6/6.7/6.8 工具链,最权威)、`docs/architecture/技术架构与模块.md`(各模块技术栈/边界)、`docs/architecture/系统概要设计-开发团队版.md`、`docs/superpowers/specs/mvp-execution-spec-design.md`(执行约束)。
> 目的:让后续 Agent 一篇掌握"为什么这么选、放弃了什么、风险在哪、哪些还没拍板、哪些源档互相打架"。
> 相关:架构与模块见 [`product-and-architecture.md`](./product-and-architecture.md);范围里程碑见 [`mvp-scope-and-milestones.md`](./mvp-scope-and-milestones.md);红线约束见 [`../rules/security-and-reliability.md`](../rules/security-and-reliability.md)、[`../rules/engineering-conventions.md`](../rules/engineering-conventions.md)。
> 相关:架构与模块见 [`product-and-architecture.md`](./product-and-architecture.md);范围里程碑见 [`mvp-scope-and-milestones.md`](./mvp-scope-and-milestones.md)术语见 [`glossary.md`](./glossary.md)红线约束见 [`../rules/security-and-reliability.md`](../rules/security-and-reliability.md)、[`../rules/engineering-conventions.md`](../rules/engineering-conventions.md)。
总原则(一句话):**用开源生态组合替代烧钱自研,钱与人力集中在竞品做不了的"游戏流分发 + 创作者变现闭环 + 数据驱动优化"。** 所有核心组件均开源/可替换,无单点供应商锁定。
@ -10,8 +10,8 @@
## 1. 关键决策表ADR
> **⚠️ 回填2026-06-10HJ-AUDIT-001;再回填 2026-06-12三回填 2026-06-15 HJ-AGI-002**下表「AI 生成引擎」行已被取代两轮——~~C2 裁定后 MVP 主线=模板驱动LLM 直出 GameConfig+固定模板)~~ → **现行主线=agent 写码于插件库**HJ-GEN-001 终审 2026-06-12游戏模板/填参线随 W-CLEAN 清除退役;**「玩法模板」(品类框架·引导生成·非 pre-built 代码)未废=有效·待建但非最高优先级(最高=Tier 0 生成可靠,玩法模板排其后;HJ-DEMO-AUDIT-001 创始人 06-17见完成度与优先级总账)**;生产生成=便宜模型经 new-api+harness 门。**~~agentic 基建=SAA(Spring AI Alibaba v1.1.2.2)裸图编排 (HJ-AGI-002short-term SAA-onlyAgentScope 降 long-term premium 独立轨)~~ — 此「SAA-only / AgentScope 降 premium」结论已被 2026-06-25 框架 reframe 推翻superseded框架统一收敛 AgentScope三档共用一个 agent 框架SAA / dify / coze 降最低优先级、留作「远期适配验证可插拔」目标。HJ-AGI-002 当时确实在产、是历史事实,但其分轨口径不再现行。**。Dify/OpenGame 均未部署、降级远期。§1.1 Tier1 行的「OpenGame 文生代码」同此。
> **⚠️ 回填2026-06-11Tier1 重设计·创始人专门会话)**:下表「游戏运行时」行的 **Tier1「自研 Canvas<15KB」被二次质疑裁定取代**——自研壳实为机制演示(资产载而不绘/零 juice/只胜不败),手搓引擎=critical risk**15KB 红线废除**(系 srcdoc 内联架构的衍生约束前提已失效改为三层约束框架SLO 地板@千元机+4G P75 / 预算入场券 B1 gz≤350KB·raw≤1.5MB / 工程增强层 E1-E6引擎=契约下可换的实现,**终裁待 LittleJS vs Phaser eval-spike**。单一事实源:`docs/brainstorms/2026-06-11-tier1-runtime-constraints-requirements.md` + `git show 6d2f8789^:docs/agent-specs/_archive/2026-06-11-Tier1运行时重设计-review.md`(已随 _archive 清理删除、git 定位)
> **⚠️ 回填2026-06-10HJ-AUDIT-001;再回填 2026-06-12三回填 2026-06-15 HJ-AGI-002**下表「AI 生成引擎」行已被取代两轮——~~C2 裁定后 MVP 主线=模板驱动LLM 直出 GameConfig+固定模板)~~ → **现行主线=agent 写码于插件库**HJ-GEN-001 终审 2026-06-12游戏模板/填参线随 W-CLEAN 清除退役;**「玩法模板」(品类框架·引导生成·非 pre-built 代码)未废=有效·待建但非最高优先级(最高=Tier 0 生成可靠,玩法模板排其后;HJ-DEMO-AUDIT-001 创始人 06-17见完成度与优先级总账)**;生产生成=便宜模型经 new-api+harness 门agentic 基建=SAA(Spring AI Alibaba v1.1.2.2)裸图编排 (HJ-AGI-002short-term SAA-onlyAgentScope 降 long-term premium 独立轨)。Dify/OpenGame 均未部署、降级远期。§1.1 Tier1 行的「OpenGame 文生代码」同此。
> **⚠️ 回填2026-06-11Tier1 重设计·创始人专门会话)**:下表「游戏运行时」行的 **Tier1「自研 Canvas<15KB」被二次质疑裁定取代**——自研壳实为机制演示(资产载而不绘/零 juice/只胜不败),手搓引擎=critical risk**15KB 红线废除**(系 srcdoc 内联架构的衍生约束前提已失效改为三层约束框架SLO 地板@千元机+4G P75 / 预算入场券 B1 gz≤350KB·raw≤1.5MB / 工程增强层 E1-E6引擎=契约下可换的实现,**终裁待 LittleJS vs Phaser eval-spike**。单一事实源:`docs/brainstorms/2026-06-11-tier1-runtime-constraints-requirements.md` + `docs/agent-specs/_archive/2026-06-11-Tier1运行时重设计-review.md`。
| 决策点 | 选择 | 理由 | 放弃的备选why-not | 风险 |
|---|---|---|---|---|
@ -20,7 +20,7 @@
| **前端** | Vue3 + Element Plusgame-admin/ Vue3 + Vantgame-studio移动优先适配 360-430px | Element Plus 版是 Huijing 官方主推、社区最活跃、文档最全、二开友好度最高Vant 适配游戏流滑动体验 | **React + Next.js**:与 Huijing 前端生态不一致二开成本高v1 用的就是 Next.jsv2 切 Vue3 | 两端两套组件库C 端游戏流须独立 H5admin 风格不适用 |
| **数据库** | MySQL 8.0 | Huijing 默认,社区方案最多,迁移成本最低;需 JSONB/全文检索时再加 PostgreSQL/ES | **PostgreSQL**JSONB/全文检索更强但 Huijing 适配成本高 | 复杂检索能力弱,靠后续叠加 ES/PG 补 |
| **消息队列** | RocketMQ 5 | Huijing 默认集成;延迟消息/事务消息/死信队列完整,适合生成任务调度、审核通知、事件摄取、结算触发 | **Kafka**偏大数据流、运维重MVP 过度;**Redis Stream**:可靠性不足,无死信/事务消息 | 运维复杂度高于 Redis Stream但生产更可靠 |
| **游戏运行时**(分层,见 §1.1 | Tier1 ~~自研轻量 Canvas Runtime(<15KB)~~ **→ LittleJS 增强发行版 + Runner v22026-06-12 终裁15KB 红线废除,详见 §1.1****Tier2/3 复杂2D·3D·原生 用 Cocos Creator 3.8.8 + MCP**〔⚠️ 2026-06-21 纠正Cocos 仅留 3D/渠道导出轴(编辑器+人在环);自治富 2D 生成轨改 Phaser/Pixi见 §1.1 | Tier1 首屏极快P75<3s)、AI 生成纯 JS 直接可运行平台完全控制沙箱~~Tier2/3 Cocos MCP(158工具) AI 驱动~~该理由已被推翻MCP 是编辑器扩展 headlessAI 无法无人值守驱动出功能快一栈覆盖复杂2D+3D+原生/小游戏导出 | **Three.js** web3D Cocos 重复不选**Phaser 3 全栈** 2D导出弱~~否决~~ 该否决仅在 Tier1 轻量档冷启动维度Phaser tier2 自治富 2D 轨选定引擎 §1.1**LayaAir** MCP 生态清单无快手否决**Unity**AI 适配差启动重否决 | Cocos web 包体较重(MB级)→Tier2/3 不进游戏流走渠道/App 分发~~Tier1 仍自研薄壳保首屏~~2026-06-11 回填Tier1 改成品轻引擎+Runner v2 §1.1 |
| **游戏运行时**(分层,见 §1.1 | Tier1 ~~自研轻量 Canvas Runtime(<15KB)~~ **→ LittleJS 增强发行版 + Runner v22026-06-12 终裁15KB 红线废除,详见 §1.1****Tier2/3 复杂2D·3D·原生 用 Cocos Creator 3.8.8 + MCP** | Tier1 首屏极快P75<3s)、AI 生成纯 JS 直接可运行平台完全控制沙箱Tier2/3 Cocos MCP(158工具) AI 驱动出功能快一栈覆盖复杂2D+3D+原生/小游戏导出 | **Three.js** web3D Cocos 重复不选**Phaser 3 全栈** 2D导出弱否决**LayaAir** MCP 生态清单无快手否决**Unity**AI 适配差启动重否决 | Cocos web 包体较重(MB级)→Tier2/3 不进游戏流走渠道/App 分发~~Tier1 仍自研薄壳保首屏~~2026-06-11 回填Tier1 改成品轻引擎+Runner v2 §1.1 |
| **多渠道导出** | **以"微信小游戏格式包"为统一中转**Tier2/3 用 Cocos 官方一键导出Tier1LittleJS渠道 adapter 走 W-CH-α 竞标(~~原「自研 Canvas 自做 adapter」随自研壳退役~~,见 §1.1 | 快手无专用导出接口,标准路径=导出微信包→快手开发者工具"微信格式兼容转换";抖音有自有导出接口;导出可异步离线、不影响实时预览 | LayaAir 官方平台清单无快手,不走 LayaAir CLI 路线 | DevTool import 仍需真机验证;三平台各自真机 |
| **AI 素材工具链** | **图片/音乐现行=mmx-cliMiniMax2026-06-12 创始人亲验拍板默认agent 造游戏直接 CLI 调用,免 GPU/免训练)**~~图片/角色/场景/封面→ComfyUI自部署~~ **ComfyUI 退备选**~~音乐/音效→Stability Audio API~~;语音/音色→Fish Audio / 阿里 CosyVoice | (以下为 ComfyUI 备选时的理由留作历史ComfyUI 节点化、可训 IP 风格 LoRA 出系列一致素材、自部署无审查/无限频、长期成本低于商用 APIFish/CosyVoice 中文效果最佳、支持 few-shot 音色克隆 | 直接调 **Midjourney/DALL-E API**:无法训风格 LoRA、游戏场景武器/战斗)易被拒、按次付费贵 | ComfyUI 需 GPU无 GPU 走 CPU 慢 10x 或 mock/外部 API |
| **内容安全** | 图片→safe-content-ai自部署快检+ 阿里云内容安全(高风险兜底确认);文本/音频→阿里云审核 APIAI 输出→~~Dify Guardrails 节点~~ **Dify 未部署已 moot现行 Prompt 注入检测+输出 schema 校验落在生成侧门禁/Prompt Registry 门,见 [`skills/prompt-governance.md`](../skills/prompt-governance.md)** | 自部署做首道快检(免费/低延迟),高风险样本二次送阿里云确认;阿里云违禁词库持续更新、语义强于规则 | 单一商用 API成本高且首道检测延迟大 | 双层链路一致性需治理阈值block 0.7 / review 0.4)须在 Nacos 调优 |
@ -31,36 +31,25 @@
### 1.1 分层运行时设计
> **⚠️ 引擎口径已更新2026-06-21 · tier2 触发)**tier2 自治富游戏生成轨的引擎定为 **Phaser/Pixi全无头·纯代码**、agent 框架定为 **AgentScopePython·独立 service·自治 ReAct**Cocos 仅留 **3D/渠道导出轴(编辑器+人在环)**——下表与本 §1.1 凡写「Tier2/3=Cocos+MCP 锁定」「MCP 158 工具可 AI 驱动」「Phaser 否决/落选」处,均按此口径理解,详见本节末 §1.1 supersession 横幅与 tier2 富游戏设计(已并入运行时 SoT `docs/architecture/架构/生成引擎/agentic运行时架构图说.md` §4.2/§5。**这同时澄清了 §1/§2/§4 里"AgentScope 降 long-term premium 独立轨"的旧口径**AgentScope 不再只是悬置的远期备选,而是 tier2 富游戏线现行选定的 agent 框架tier2 核心已落、0 号 spike 已 accept见 §4 与运行时 SoT `生成引擎/agentic运行时架构图说.md` §4.2/§5。**进一步2026-06-25 框架 reframe 已把这层澄清推到底——三档统一收敛 AgentScope不再「廉价线 SAA-only、tier2 AgentScope」两套基建并存SAA / dify / coze 整体降最低优先级、留作「远期适配验证可插拔」目标。** 下表保留决策史、只加纠正标注,不删。
游戏产物分三层,对应不同运行时:**Tier1 = LittleJS 增强发行版 + Runner v2agent 写码于插件库生成HJ-GEN-001~~原「自研 Canvas + OpenGame 生成」已废除~~见下表Tier2/3 统一用 Cocos Creator 3.8.8 + MCP。**(术语澄清:废的是游戏模板/填参线W-CLEAN「玩法模板」(品类框架)未废、待建。) MVP 仅交付 Tier1非目标保持收敛Tier2/3 仅远期探针,见 §7
| 层 | 定位 | 选型 | 生成路径 | 状态 |
|---|---|---|---|---|
| **Tier1 极轻量H5/2D** | 游戏流即点即玩,首屏 P75<3s + 点卡可玩 S2常态2s | ~~自研轻量 Canvas Runtime(<15KB)~~ **LittleJS + Runner v22026-06-12 创始人终裁spike 85/82冷开 0.54s vs 2.86s=4.8~5.3×/引擎 15.9KB gz/敏感性分析裁量分拉平 S2 硬差仍定向,终裁包 `git show 6d2f8789^:docs/agent-specs/_archive/2026-06-11-T1引擎终裁包.md`已删、git 定位)**引擎 URL 化交付享 HTTP+编译双缓存~~模板双层架构~~ **模板=LittleJS 能力插件/二次开发件**2026-06-12 模板哲学重申游戏模板/填参线废玩法/美术/关卡/UI=agent 生成域玩法模板=品类框架·未废·待建),好玩基线 v2 改挂评估门**裁决①(2026-06-12)粒子/物理/后处理插件=引擎能力包装层,禁在受控面墙内平行重造引擎内建能力,β 设引擎真接线门Q4 补裁(2026-06-13):「自研内容是引擎外的层不是对引擎本体的替代」——「有意替代豁免关闭自研仅限包装层/引擎缺件补层确定性取证诉求在 harness/门侧解决手搓 Canvas2D 零实测证据优于引擎内建且自揽平台渲染差异债**iframe 沙箱 + SDK 注入不变 | ~~LLM 填参~~ **agent 写码于插件库**HJ-GEN-001 终审旧填参线随模板废除退役美术/音乐=mmx 优先2026-06-12 | W-T1b 主门过拔高样板暖阳小卖部2026-06-12 创始人亲玩判 passed**MVP 唯一交付层**。真护城河=沙箱/SDK/契约/三容器(全保留);存量 1.x 双轨冻结引擎复议权=拔高暴露引擎级阻塞(终裁包 §4.5-3 |
| **Tier2 复杂2D+3D** | 中重度含 3D渠道分发 | **Cocos Creator 3.8.8 + MCP**(备选 PlayCanvas〔⚠️ 2026-06-21 纠正:此「锁定」仅指 3D/渠道导出轴Cocos 用编辑器+人在环;自治富 2D 生成轨改 Phaser/Pixi | **Cocos-MCP** agentic 工具编排studio 编排) | ✅ 锁定(限 3D/渠道导出轴)~~MCP 158 工具可 AI 驱动~~ 系事实错——该 MCP 是 Cocos 编辑器扩展、需编辑器在跑、非 headless故进不了无人值守自治生成 loopMVP 至多 1 个探针 demo |
| **Tier1 极轻量H5/2D** | 游戏流即点即玩,首屏 P75<3s + 点卡可玩 S2常态2s | ~~自研轻量 Canvas Runtime(<15KB)~~ **LittleJS + Runner v22026-06-12 创始人终裁spike 85/82冷开 0.54s vs 2.86s=4.8~5.3×/引擎 15.9KB gz/敏感性分析裁量分拉平 S2 硬差仍定向,终裁包 `docs/agent-specs/_archive/2026-06-11-T1引擎终裁包.md`**引擎 URL 化交付享 HTTP+编译双缓存~~模板双层架构~~ **模板=LittleJS 能力插件/二次开发件**2026-06-12 模板哲学重申游戏模板/填参线废玩法/美术/关卡/UI=agent 生成域玩法模板=品类框架·未废·待建),好玩基线 v2 改挂评估门**裁决①(2026-06-12)粒子/物理/后处理插件=引擎能力包装层,禁在受控面墙内平行重造引擎内建能力,β 设引擎真接线门Q4 补裁(2026-06-13):「自研内容是引擎外的层不是对引擎本体的替代」——「有意替代豁免关闭自研仅限包装层/引擎缺件补层确定性取证诉求在 harness/门侧解决手搓 Canvas2D 零实测证据优于引擎内建且自揽平台渲染差异债**iframe 沙箱 + SDK 注入不变 | ~~LLM 填参~~ **agent 写码于插件库**HJ-GEN-001 终审旧填参线随模板废除退役美术/音乐=mmx 优先2026-06-12 | W-T1b 主门过拔高样板暖阳小卖部2026-06-12 创始人亲玩判 passed**MVP 唯一交付层**。真护城河=沙箱/SDK/契约/三容器(全保留);存量 1.x 双轨冻结引擎复议权=拔高暴露引擎级阻塞(终裁包 §4.5-3 |
| **Tier2 复杂2D+3D** | 中重度含 3D渠道分发 | **Cocos Creator 3.8.8 + MCP**(备选 PlayCanvas | **Cocos-MCP** agentic 工具编排studio 编排) | ✅ 锁定。MCP 158 工具可 AI 驱动MVP 至多 1 个探针 demo |
| **Tier3 独立App** | 打包为原生应用 | **Cocos 原生导出**(同 Tier2 引擎) | 同 Tier2 + 打包 | ✅ 锁定引擎。否决 UnityAI 适配差、启动重MVP 后投入 |
**自研工期评估(为什么 Tier2/3 复用 Cocos 而非自研)**Tier1 薄壳约 0.51.5 人月(可行,且必须自研以控沙箱/SDK/三容器预加载);自研 3D 引擎数十人月~数年、自研原生框架数十人月——3 周窗口下绝不可行,必须复用成熟引擎。选 Cocos 因其一栈覆盖复杂2D+3D+原生/小游戏导出,~~且 MCP(158工具)使 AI 驱动可行~~**2026-06-21 纠正:该 MCP 是编辑器扩展、非 headless无法无人值守驱动Cocos 此处仅指 3D/渠道导出轴的人在环作者,自治富 2D 生成轨改 Phaser/Pixi**(与"开源组合替代烧钱自研"总原则一致)。**2026-06-11 回填:「必须自研以控沙箱/SDK」已被推翻——控制点在沙箱/SDK/契约层与渲染引擎解耦Tier1 渲染层亦改复用成品轻引擎,自研壳退役,见 §1.1 表)**
**自研工期评估(为什么 Tier2/3 复用 Cocos 而非自研)**Tier1 薄壳约 0.51.5 人月(可行,且必须自研以控沙箱/SDK/三容器预加载);自研 3D 引擎数十人月~数年、自研原生框架数十人月——3 周窗口下绝不可行,必须复用成熟引擎。选 Cocos 因其一栈覆盖复杂2D+3D+原生/小游戏导出,且 MCP(158工具)使 AI 驱动可行(与"开源组合替代烧钱自研"总原则一致)。**2026-06-11 回填:「必须自研以控沙箱/SDK」已被推翻——控制点在沙箱/SDK/契约层与渲染引擎解耦Tier1 渲染层亦改复用成品轻引擎,自研壳退役,见 §1.1 表)**
**导出枢纽 = 微信小游戏格式包**:微信=引擎导出官方格式;抖音=自有导出接口;**快手=无专用接口,走"微信格式兼容转换"**(快手开发者工具)。**LayaAir 官方平台清单无快手**,不走 LayaAir CLI 路线。Tier2/3 用 Cocos 官方一键导出微信包Tier1LittleJS渠道 adapter 走 W-CH-α 对比竞标LittleJS+自研 adapter vs Cocos 导出HJ-CH-001 §4~~「Tier1 自研 Canvas 自做 adapter」随自研壳退役~~)。
> 选型依据Cocos 3.8.8/MIT 一栈覆盖2D/3D/原生导出,故 **3D/原生/渠道导出轴** 选 CocosThree.js r184/MIT/113k★ 仅 web3D、与 Cocos 重复故不选LayaAir 2.1k★、无 AI/MCP 生态、清单无快手否决Unity 启动 7-10s×2-3 与 P75<3s 冲突否决)。
> **⚠️ supersession + 纠错2026-06-21 · tier2 触发)**:上句原作"headless MCP(158 工具) 使 AI 驱动可行"**是事实错**——Cocos 的 MCP 是**编辑器扩展、需 Cocos Creator 在跑、非 headless**(取证级核实),故 Cocos **进不了 tier2 全自治生成 loop**。引擎重定为正交两轴:**自治生成轨(全无头:富 2D=Phaser/Pixi、轻量档=LittleJS** vs **3D/渠道导出轴Cocos编辑器+人在环)**Phaser 的"spike 落选"只在 Tier1 轻量档冷启动维度、与 headless 自治维度正交。本表其余 Tier2/3=Cocos / Phaser 否决 行均按此口径理解。详见 tier2 富游戏设计(并入运行时 SoT `docs/architecture/架构/生成引擎/agentic运行时架构图说.md` §4.2/§5引擎选型
---
### 1.2 A11 调整回路(试玩后纠错 · 切片三 2026-06-29
用户试玩生成的游戏后用自然语言提调整,这条回路在便宜档线落地。架构 = **A 两段式**:判意图(`/modify/plan` 收原话 → 便宜档 worker 取 base 源 + 一次轻 LLM 把自然语言分类成 `mode/target/payload` + 风险)与执行(用户确认后调已结构化的 `/modify`分离成两次请求HITL 走前端在两次调用之间的确认——绕开"自然语言进不了已结构化的执行入口"与"危险回问做成任务内暂停态却撞上内部状态机无暂停态"两道阻断A2A 原生 input-required 暂停态留后期)。可改性的根基是生成侧**工程规范性**:资产统一在 `assets.js`、数值集中在 `core.js`,使一次修改落到"那一处",这是生成质量、不是给调整加静态门(判意图仍 LLM 驱动)。执行分两档:确定性类(换资产 / 调数值,改一处、零 LLM+ 模块重生成(改玩法,有界单文件重写、复用续跑与三层校验)。验收 = 九门 + 三层校验 + **三断言**(改动真生效非 no-op / 非目标模块字节稳定[改前改后真比对、不信执行器自报] / 新版本血缘可查)。
**现状**:便宜档代码完成、本机逐段一手真验(真九门、真 LLM 重写、断言集成 smoke受计费真后端 e2e 排部署窗口(创始人 2026-06-29 定不动 live复杂档随 tier2 跟进。关键收敛A11 后端骨架(契约 / `/modify` 编排 / `game_source_project` 血缘 / D12 配额)已大面积落地,真活在便宜档接入 + 判意图/执行/三断言。设计面 SoT = 运行时 SoT [`生成引擎/agentic运行时架构图说.md`](../../docs/architecture/架构/生成引擎/agentic运行时架构图说.md) **§六**;执行留痕 = `git show 8ea97234:docs/plans/2026-06-28-003-feat-studio-A11-对话式调整回路-plan.md`(`git show git show 8ea97234:docs/plans/2026-06-28-003-feat-studio-A11-对话式调整回路-plan.md`)。
> 选型依据Cocos 3.8.8/MIT + headless MCP(158 工具) 使 AI 驱动游戏开发可行、出功能快、一栈覆盖2D/3D/原生导出,故 Tier2/3 统一选 CocosThree.js r184/MIT/113k★ 仅 web3D、与 Cocos 重复故不选LayaAir 2.1k★、无 AI/MCP 生态、清单无快手否决Unity 启动 7-10s×2-3 与 P75<3s 冲突否决
---
## 2. 这些选型如何服务护城河(为什么"够用即可"
投资人版与技术决策版口径一致:**技术深度不追第一,生态完整度追第一。** 生成能力会被通用大模型 12-18 个月追平,故选型刻意"够用、可替换、低成本",把自研投入压在竞品的空白——游戏流分发与变现闭环。真正壁垒:① 游戏流推荐数据壁垒(真实流量积累,无法购买)② 生成质量反馈闭环玩家数据→quality_score→优化建议→创作者迭代③ 模板/素材/工作流资产沉淀(马太效应)④ 广告位 AI 优化(提升 eCPM。对应的工程取舍自研只做 feed/telemetry/SDK/变现链路/**发行版包装层与契约**~~runtime~~ 出列:渲染循环=LittleJS自研面只剩引擎能力包装与插件协议——2026-06-12 审计 R5 纠正「护城河预划过粗给自研免检光环」agentic 编排自研只剩护城河件——checkpoint + 验收门框架本身2026-06-25 reframe 后三档统一收敛 AgentScopeSAA / dify / coze 降最低优先级、留作远期适配验证可插拔目标)采主流标准对接、不自研),其余(后台、工作流、生成、素材、安全)全用开源/商用组合。**Build-vs-Buy 硬门=[`.agents/rules/build-vs-buy.md`](../rules/build-vs-buy.md)。**
投资人版与技术决策版口径一致:**技术深度不追第一,生态完整度追第一。** 生成能力会被通用大模型 12-18 个月追平,故选型刻意"够用、可替换、低成本",把自研投入压在竞品的空白——游戏流分发与变现闭环。真正壁垒:① 游戏流推荐数据壁垒(真实流量积累,无法购买)② 生成质量反馈闭环玩家数据→quality_score→优化建议→创作者迭代③ 模板/素材/工作流资产沉淀(马太效应)④ 广告位 AI 优化(提升 eCPM。对应的工程取舍自研只做 feed/telemetry/SDK/变现链路/**发行版包装层与契约**~~runtime~~ 出列:渲染循环=LittleJS自研面只剩引擎能力包装与插件协议——2026-06-12 审计 R5 纠正「护城河预划过粗给自研免检光环」agentic 编排同理=SAA(Spring AI Alibaba v1.1.2.2)裸图编排 (HJ-AGI-002short-term SAA-onlyAgentScope 降 long-term premium 独立轨)),其余(后台、工作流、生成、素材、安全)全用开源/商用组合。**Build-vs-Buy 硬门=[`.agents/rules/build-vs-buy.md`](../rules/build-vs-buy.md)。**
---
@ -85,12 +74,12 @@
| 口径点 | 权威结论 |
|---|---|
| **运行时技术栈** | Tier1=**LittleJS2026-06-12 终裁)**+Runner v2原自研<15KB 壳退役存量 1.x 冻结Tier2/3=Cocos Creator 3.8.8+MCP〔⚠ 2026-06-21 纠正Cocos 仅留 3D/渠道导出轴自治生成轨=Phaser/Pixi§1.1)。Three.js/LayaAir/Unity 仍否决Phaser spike 竞标落选该落选只在 Tier1 轻量档冷启动维度Phaser tier2 自治轨选定引擎——败于冷开/交付形态工程质量合格证据留 spike 目录 |
| **运行时技术栈** | Tier1=**LittleJS2026-06-12 终裁)**+Runner v2原自研<15KB 壳退役存量 1.x 冻结Tier2/3=Cocos Creator 3.8.8+MCP§1.1)。Three.js/LayaAir/Unity 仍否决Phaser spike 竞标落选败于冷开/交付形态工程质量合格证据留 spike 目录 |
| **生成成功率指标** | MVP 验收按 **≥80%** 判定;**≥85%** 为远期蓝图目标,勿用 85% 卡 MVP |
| **时间线** | 双路线并存:投资人版 5 人/11 周、执行 spec 10 人/3 周;执行排期以 mvp-execution-spec 为准(含逐日计划与里程碑) |
| **MVP 范围/口径** | MVP 验收以 Doc A 的 **55 项 P0 产品功能**为准;技术实现范围经 Doc C 反查 Doc B工作量 ≈137 技术项)。详见 [`mvp-scope-and-milestones.md`](./mvp-scope-and-milestones.md) §3 |
| **模块划分** | **13 模块**(含 game-module-studio 创作编排域aigc 为无状态生成原子);能力清单拆为产品功能(Doc A)/技术功能(Doc B)/映射(Doc C) 三文档分离 |
| **生成主线(三档统一 AgentScope、SAA 最低优先级远期适配)** | ~~MVP=模板驱动LLM 直出 GameConfig+固定模板)~~~~现行是两条并存的生成线~~**2026-06-25 框架 reframe框架统一收敛 AgentScope三档共用一个 agent 框架SAA / dify / coze 降最低优先级、留作「远期适配验证可插拔」目标**。三档按 **AI 参与深度**Tier0/1/2切分、全部高度模板化玩法模板 + 工程骨架,不是从零写),引擎是按复杂度的实现变体(轻-中=LittleJS、最高=Phaser**引擎不是分档轴**。**便宜档**agent 写真 `src/`HJ-GEN-001 终审 2026-06-12旧游戏模板/填参线随 W-CLEAN 退役M-b 执行器写链语义保留复用〕),便宜模型经 new-api + harness 九门,引擎=LittleJSgameDefinition 已废、A-model 写真 src/ 现行(无 factory/gamedef 双轨、无 cutoverper-gen 预算硬闸 <¥10图/音另算。已实证地板W-G1 L1 worker + 九门真玩 + 3 款冒烟¥0.010.03<¥0.15 闸;全 20 款 bake-off + 四模型横比 + Claude-free judge 门待跑,配方见 [`skills/cheap-model-game-generation.md`](../skills/cheap-model-game-generation.md))。**复杂档tier2 富游戏)**:自治 ReAct agent 用 **AgentScope + Phaser** 造多系统富游戏(合成/经营/挂机这类,对标肥鹅美食街),产物=真 Phaser `src/` 源工程三层校验兜底per-gen 预算硬闸 <¥50图/音另算)。**现状=0 号 spike 已 accept、核心已落**(见运行时 SoT `生成引擎/agentic运行时架构图说.md` §4.2/§5 与 `tier2/HANDOFF.md`)。运行时单一真相 SoT = [`生成引擎/agentic运行时架构图说.md`](../../docs/architecture/架构/生成引擎/agentic运行时架构图说.md)§5.2 控制面 / §5.7 成本)。Dify/OpenGame=远期增强未部署 |
| **生成主线** | ~~MVP=模板驱动LLM 直出 GameConfig+固定模板)~~**agent 写码于插件库**HJ-GEN-001 终审 2026-06-12旧游戏模板/填参线退役W-CLEANM-b 执行器写链语义保留复用〕;生产=便宜模型经 new-api+harness 门W-G1 L1 worker + 九门真玩 harness + 3 款冒烟已实证地板成立 HJ-GEN-001¥0.010.03<¥0.15 闸);全 20 款 bake-off + 四模型横比 + Claude-free judge 门待跑,配方见 [`skills/cheap-model-game-generation.md`](../skills/cheap-model-game-generation.md)agentic 基建=SAA(Spring AI Alibaba v1.1.2.2)裸图编排 (HJ-AGI-002short-term SAA-onlyAgentScope 降 long-term premium 独立轨)Dify/OpenGame=远期增强未部署 |
### 4.1 生产质量三层 reframe创始人亲玩校准锁定2026-06-14·WG1 review §8
@ -123,18 +112,6 @@ W-G1 实证后创始人亲玩校准拍定:**生产环无 Claude**,质量由*
| MQ 重复消费致数据不一致 | 中/高 | 消息幂等消费message_id + Redis 去重) | 定时任务修复 + 告警 |
| 第三方 SDK 数据泄露 | 低/极高 | 广告/支付 SDK 宿主侧隔离 + 最小权限 | 紧急下线第三方 SDK |
### 5.1 便宜档私有方言对模型有量化成本2026-06-28 对照实验,evidence 见 agent-specs
便宜档让模型直接写一套私有运行时方言(L1/L2/L3 三层骨架 + `createGame` 五法 + `_forensicsView` 契约 + `ctx` 受控面 + 输入收归 L1 + 几十条红线)。母语 vs 方言对照实验(同 brief 点击得分、同模型 MiniMax-M3、各 n=5、用契约无关的 fair-verdict 五信号判据)实测:M3 在母语(标准 Canvas/rAF/addEventListener)下 **5/5 一次成型、3 步、约 14K 输入 token、约 55s**;在现状方言下产同一个能玩的游戏要 **约 26 倍 token、39 倍时间**,还出现母语从未有的失败模式(一局磨满 20 分钟未收敛、反复撞静态检查返工、按错路径读插件)。结论:之前记在"M3 弱"头上的低良率/高成本,**很大一部分是方言强加的税,不是模型天花板**(M3 不弱)。
⚠️ **方向修正(2026-06-29 创始人纠正,推翻"换母语"初判)**:便宜档按 SoT 定位是"AI 参与深度低的高质游戏"(轻量≠简单,见运行时 SoT §一/§4.1),**不是简单玩具**;L2 插件库是"降 AI 参与=便宜+高质"的核心载体、**不是"税"**。"换母语扔插件"会让 AI 从头写整个游戏 = **升** AI 参与,与便宜档"降 AI 参与"的核心策略正相反,故推翻。本实验测的点击得分恰是产品不做的简单玩具、对象本身无产品意义——它留下的真发现是:**当前 AI 在方言里写 game-logic.js 时,学插件 API 形状的摩擦极大(drawButton 当返按钮对象、define 想批量都是按母语直觉猜错;9 步 358K),"降 AI 参与"的设计意图没兑现**。修正方向(创始人方向 3,分阶段)= ① 保留插件库,让**插件 API 向模型母语直觉对齐**(drawButton 真返按钮对象、define 批量,消除"按直觉写就错"的坑)+ RAG/few-shot/skill 补 API 密度,把学习摩擦降到接近母语;② 扩玩法模板与工程脚手架覆盖,缩小 AI 自由写的面。地基(确定性受控面 + 九门)仍是护城河。详见 `../../docs/agent-specs/2026-06-28-母语vs方言-生成对照实验-evidence.md`(`git show git show 8ea97234:docs/agent-specs/2026-06-28-母语vs方言-生成对照实验-evidence.md`),它给 `git show bb7c2baf^:docs/agent-specs/2026-06-20-生成设计合理性-对抗审查裁决.md`已随域化重构删除git 定位)的"表现力硬编码"补量化证据。
**落地(2026-06-29 阶段一B 改签名)**:方向①「插件 API 向母语直觉对齐」已全部完成——附 A 15 处可改签名 #1-#15 逐插件改逐插件验:drawButton 返命中矩形(#5,治"误把 void 当返回值"#1 翻车点根因)、sessionScore.add / juice.burst / gamefeel 工厂(免 new)、collision·physics 几何挂实例、Aabb 兼收 {x,y,w,h}(#12)、**ctx.random()·ctx.time() 可调用 + 保留方法双 API**(#1,单点 buildContext 包装、框架内部不破)、audioMusic 语义音名、timer·scene·juice.render doc 钉死。另修一个正交真 bug:check 的 API 静态门方法名正则漏认泛型声明 `get<T>(`、把真实存在的 `save.get` 误判不存在、误杀一整类带存档游戏(`851b86fe`)。`2a20e5e7``2be7b718` 共 13 提交,每处单元+确定性验、各插件测试绿、双源 prompt 一致、形状门 11/11 不破。**减摩擦端到端见效**:改签名前栽在 save.get 泛型门 bug 的打地鼠+高分游戏(2 attempts/¥6.69/失败),改后**一次过九门、¥0.64/209s、不 thrash**。剩:方向①的补密度(阶段一C few-shot/RAG)+ 方向②扩模板(数据点门控)。详见 `../../docs/agent-specs/2026-06-29-便宜档降AI参与-减摩擦与扩模板覆盖-设计.md`(在飞设计:`docs/agent-specs/2026-06-29-便宜档降AI参与-减摩擦与扩模板覆盖-设计.md`)。
**方向②扩模板 数据点(2026-06-29,难品类经营,修正了假设)**:建经营黄金骨架 `game-runtime/games/_template-shop/`(蒸馏过门的 bake-shop-serve 实证 pattern + 母语化 API + 多样性参数空间 + fill-in 标记,独立过九门)+ 品类路由接入(`scaffold-saa <id> [template]` / `cheap_run.scaffold(template)` / `run_studio(scaffold_template, scaffold_desc)`,缺省回落 _template)。同一咖啡馆 brief 三路对照(n=1):**软脚手架**(给骨架 + 软提示)892 行/¥0.85——AI 把预算填进增富化(自加 combo+VIP)、对 ~890 行基线三指标全平**没降**;**强制换皮**(write_whitelist 锁 game-logic.js、只许改 core 主题数值+render 观感)696 行(-22%)/¥0.34(-51%)/tokens-64%,game-logic **0-diff**、仍过门仍 distinct。**结论:"扩模板降工作面"成立但只在"强制换皮"模式**(脚手架+锁循环只换皮)——降本杠杆不是脚手架本身、是写边界强制;软脚手架买的是质量/过门鲁棒(增富化)、不买成本。取舍:强制换皮省成本但封顶增富化,软脚手架反之。**n=5 收敛环确认(经营,5 个不同店主题 奶茶/书店/花店/拉面/冰淇淋):5/5 全过九门 · 成本均 ¥0.252(-64% vs 基线)· game-logic 全 0-diff · 全 1 轮 · 5 店菜单互异——成本腰斩稳 / 过门 100% / 多款不雷同 三件全确认**。🔴 **创始人纠正(2026-06-29,推翻 reskin 策略框)**:上面 reskin 的成本数据作留痕(锁写省成本机制为真),但**「reskin 锁循环只换皮作阶段二成本主线」= 错、越线、已废**。① **便宜 = LLM 低参与度,不是游戏低质量**;底线 = 2D 丰富游戏(进货/解锁/成长/音乐/丰富玩法),reskin 砍复杂度违背底线;"低参与" = agent 不在引擎/plumbing 上耗(脚手架+插件包了)、精力放游戏设计。② **架构红线:项目代码只做机械确定性的事;玩法/美术/音乐的「丰富生成」= 生成 agent 设计创作职责(靠 [sim-business-game-design](../skills/sim-business-game-design.md) + 组合插件),「丰富校验」= 纯 LLM 验证 agent 非阻塞——绝不写成代码校验、不进九门、不进脚手架**。故"丰富脚手架/脚手架好玩门断言"也废。**修正后下一步**:接 sim-business 设计指导到生成 agent(现 prompt 漏)+ 建 LLM 丰富度验证 agent → 喂 M1 达标门(标=过九门+丰富);脚手架保持轻起点。
**切片一收口落地(2026-06-30,上面"修正后下一步"全部交付并验证)**:生成 agent 接上 sim-business 设计指导(`cheap_roles.py` + `prompt.mjs` 双源,"先设计后写码"步 + MVP-first 铁律 + 8 条好玩自检);新建纯 LLM 丰富度验证 agent `cheap_verify.py`——读产物源码逐条裁 8 条好玩清单、写进 run-summary 的 richness 字段,**非阻塞(失败降级不阻断)、不进 verdict、不改达标判定、零 code-presence 断言**(命中与否 100% 由 LLM judge 裁,代码只搬运计数——红线落地;sim-business §10 那 4 条"可机检好玩门"刻意不实现)。富游戏重验 M1 达标门(全新 gameId、三品类各 n=5):**click/whack/shop 各 5/5 = 100% 过九门、整体达标 ✅**——richness 加固没压垮达标,plan 担心的"丰富后跌破"未发生;richness 均分 shop 4.2 / click 3.4 / whack 2.4(8 条 rubric 偏经营,最贴 shop,whack 低是品类不匹配非质量差)。退役授权据新富游戏 M1 报告重算 `authorized=True`。基建线同轮交付:统一 trace 落库(tier2 `JsonlFileSink` + cheap-worker 接线 + SAA 扩展段 schema)、配置注册表运行时热取(cheap_roles 三级回落加载器、Java `@Scheduled` TTL、`check_registry.py` 一致性 CI 门)、生成控制面只读管理面(后端 3 端点 + game-admin Vue 三 card)。**两个测试坑(留给后来人)**:① bake-off 复跑同一 gameId 会复用 `_wg1-gen/<id>/play-spec.json` 旧 spec(`ensure_play_spec` 是"已存在不覆盖"语义)——旧薄游戏 spec 驱新富游戏会假失败(自动驱动器卡菜单 `phaseNow=menu`、score 0→0、E_live/G_input/H_progress 齐挂),`auto_vs_golden.py` 有 staged-spec 残留断言守卫而 `bake_off.py` 没有,故达标门复验务必 `--offset` 取全新 gameId(或先清 `_wg1-gen` 实验 staging);② `cheap_run.game_dir("base4")=games/amgen-base4` 是 load_brief 的 brief 来源依赖,清理 `amgen-*` 实验产物时**必须排除 `amgen-base*`**,否则断掉所有品类的 bake-off brief。2026-07-09 勘误:本条两处定性已被裁定三收窄——「各 5/5 100% 过九门、整体达标」中「过九门」自此只算机械预筛通过玩法层达标另需独立模型玩法判定而当日审计正认定「LLM 丰富度非阻塞、不进 verdict、不改达标判定」这套设计是病根之一——懂玩法的模型判断被钉成旁路、判定权威落给不懂玩法的机械门故丰富度里「玩法有无」的布尔裁决升为阻断权威程度评分仍软。当时测得 5/5 是历史事实真实率按新基线重锚W-AXIS 收尾波)。见质量 SoT《游戏质量与爆火能力》裁定三与诊断档 §2.1。)
## 6. 决策落地的关键工程参数(供选型校验,技术决策版 §4/§7
- **AI 生成**:生成任务状态机 queued→running→succeeded/failed/timed_out/canceled超时 120s失败重试 ≤2 次、超时重试 ≤1 次;队列最大积压 500超则返回 429P50<60sP95<180s
@ -161,100 +138,4 @@ W-G1 实证后创始人亲玩校准拍定:**生产环无 Claude**,质量由*
- **runtime=单 `startRuntime` 工厂内 switch(templateId) 分发**(拍板,弃「每模板独立工厂」):生命周期/`finish(score)`/loop/drawBase/cleanText 共享单份(红线代码不复制不漂移),玩法差异只在 `init<T>()`(状态+交互+draw 玩法层+finish 触发);所有函数定义在 startRuntime 体内toString() 注入约束);空串 templateId 兼容旧包并入 clicker case未知模板走 `game_error` 防御分支不静默错渲染。体积实测clicker+merge 双玩法 min 后 raw=4,828B硬线 15,360B 的 31%,软门 8,192B
- **后端=Loader/Validator「templateId→资源」Map 缓存**PromptResourceLoader 逐模板装 (promptVersion, promptBody, schemaText)GameConfigSchemaValidator 逐模板编译 JsonSchema**校验逻辑零新码**(仍 schema.validate(node)),同源铁律保持(注入 LLM 文本==校验文本per templateIdisReady()=全模板就绪(任一缺失整体自禁用含模板名)。**模板白名单唯一同源=`AigcTemplateConstants.SUPPORTED_TEMPLATE_IDS` 编译期常量**Properties 默认值与 validateTemplateExists 同读;不注入 executor Properties——`@ConditionalOnProperty` 关闭时 Bean 缺席。pom maven-resources includes 已通配(新模板两资源自动进 classpath
- **runtime↔player 承重接口纪律**跨进程共享公式merge 棋盘布局)必须钉死全部参数字面双侧一致(含 spec 散文易漏的 topPad 类参数),主 agent 收口逐字符比对;运行时实证=player 按公式推演 8 产料+7 拖拽全格命中、score=2^(targetLevel-1)-1 理论值吻合。
- **新模板/新玩法接入操作配方**:见 [`littlejs-game-dev.md`](../skills/littlejs-game-dev.md)agent 写码于插件库、终态产物 `src/` 多文件工程的作业手册;本 §9 描述的 `startRuntime` 单工厂 switch(templateId) 填参线随 gameDefinition 废除退役,仅留决策史)。
## 10. 配置控制面阶段生产基建2026-07-01 落地,切片一)
Nacos / RocketMQ / Sentinel 从「已选型未部署」推进到自托管 mini-infra + game-cloud 真接入。SDD 八任务已 push `origin/dev/2.0.0`;权威 plan `docs/plans/2026-07-01-配置控制面-spike与阶段生产基建-plan.md`status 带 framing 勘误),逐任务 review 与 follow-up 留痕在 `.superpowers/sdd/progress.md`
**自托管拓扑**mini-infra `100.64.0.8`lean JVM、纯增量、既有 9 服务零影响Nacos 2.4.3 standalone + MySQL 后端512mRocketMQ 5.3.1 NameServer + 单 Broker1g`brokerIP1` 须指 Tailscale 地址、否则 NameServer 返回的 broker 地址跨机不可达)。端点与密钥落 `docs/内网凭据与端点.md`。踩过的坑mini-infra docker daemon 的 HTTP 代理曾失效导致任何镜像拉不下来,`ctr` 走 containerd daemon 不读 CLI 代理、零重启路径不成立,最终改 daemon 代理指向本机 Clash 并重启 docker既有容器 `restart=always` 自动拉起)才恢复。
**生成准入的三层并发正交**(承重,经两轮 review 纠正Sentinel `FLOW_GRADE_QPS` 是入口投递**速率**闸(削洪峰,不是并发权威);既有 DB 三门是 per-creator 业务**配额**;真正的「在跑生成并发 ≤15」硬上限落在**有界 worker 池**worker / AgentScope Service 侧N=4-6 起步压测调,创始人 2026-06-26 决策),**不在 RocketMQ 的 consumeThreadMax**。三者正交、各管一层,任何一层都替代不了另一层。
**rocketmq-spring 消费者并发的反直觉语义**(最终 review 核字节码坐实):`@RocketMQMessageListener(consumeThreadNumber, consumeThreadMax)` 在 rocketmq-client 5.x 下,`ConsumeMessageConcurrentlyService` 用**无界 `LinkedBlockingQueue`** 建 `ThreadPoolExecutor(core=consumeThreadNumber, max=consumeThreadMax)`JDK 线程池语义下无界队列永不满、线程数永不超 core所以 **`consumeThreadMax` 是死参数、有效并发等于 `consumeThreadNumber`,要让上限真生效必须 min=max**。而且异步投递worker ACK 即返回、任务留 RUNNING下它 cap 的是投递握手速率、不是在跑生成数——想用它当并发闸会双重落空。有界 worker 池 follow-up 落地时会再撞这条。
**follow-up未做交排期**:有界 worker 池真并发 cap`NacosHotConfig` 接进 middleware 替 `cheap-worker/cheap_studio.py:156` 的硬编码预算AgentScope 2.0.3 原生 token 限制的 build-vs-buy 评估(能否替自建软预算 `on_model_call` middleware。**窗口验证**Part C/D 真集成(真注册 / 真限流 / 真队列 / 真连 / 真热读),以及 `namespace: public` 核对——Nacos 默认公共空间的 namespaceId 实为空串、非字面 `public`,不核对会把注册与发现落到别的空间。
## 11. 配置控制面阶段一① 护城河 middleware 线2026-07-02 落地,切片一)
护城河三件——续修、软预算、门判——从设计落进 AgentScope 2.0.2 的 middleware 洋葱,在 tier2 富游戏路端到端跑通(本地全 mock真门排 mini-desktop 窗口。SDD 五个代码 commit 加 plan 已 push `origin/dev/2.0.0``c62143dd..07100eef`);权威 plan `docs/plans/2026-07-02-配置控制面阶段一-护城河middleware-plan.md`,逐任务 review 与 follow-up 留痕在 `.superpowers/sdd/progress.md`
- **续修**`RepairMiddleware``tier2/gen-worker/worker/middleware.py``on_reasoning` 钩子拦住 agent 想结束时吐的 finish纯文本 Msg自己独立重跑门没绿就压制这次 finish、用 `observe(UserMsg)` 把失败反馈注入 context 让 agent 续跑修,取代了原来 control_plane 在 Agent 外的 resume 循环。放行的唯一权威是 middleware 自己重跑的门,绝不采信 agent 上报的门结果(防漏调门 / 谎报门绿的链路假绿);软预算耗尽时也保证「至少续修一次」(`repairs>0` 才因预算放行)。
- **软预算**`CircuitBreakerMiddleware``soft_budget` 档位):¥ 越限从原来的 fail-closed 断链改成优雅收尾软停——设标记、放行本次、在 `on_system_prompt` 强提醒 agent 尽快 finish 交尽力产物。**默认 hard 档,守住 cheap 的 ¥ 硬地板**(这个类被 tier2 与 cheap 两档共用,只有 tier2 工厂显式传 soft四道 on_reply 失控熔断(步数 / 次数飞车 / 死圈 / 超时)仍然 fail-closed只改 ¥ 这一道。
- **门判归一**`worker/gate_judge.py`):把 tier2 的 verdict 归一成 `GateJudgment(passed, failed_gates, feedback)`,放行判据 `decision==accept 且 L1.passed` 与既有 legacy 逐字一致、不放松。阶段二归并 cheap 时在同一文件补 `guards` map 的适配器,两档共用一套续修判据。
- **服务集成**`service/app.py` + `control_plane.py`):工厂每回合注入 `[tracer, repair, breaker]``drive_generation(single_post=True)` 把整局收进一个 POST、洋葱内续修多轮回合真结束后读服务端已落的 verdict 判落库(门绿入 store门没绿的尽力产物只留 on-disk workdir、不进正式库老的外层 resume 循环整段保留作 fallback。
**一个红线级的坑(已封)**`run_gates` 起门 subprocess 之前必须先把 `verdict.json` unlink。否则 harness 失败子进程超时、chrome 崩、端口 4330/9322 被占)没写出新 verdict 时,会读回上一次的残留 verdict而 judge 只看 verdict、不看 rc于是把陈旧的绿当本次结果放行加落库——这是「放行唯一权威=独立重跑门」这条红线的唯一裂缝,`f4384dc3` 已封(起跑前清残留,保证读到的必是本次真门产出)。
**follow-up交阶段二 / 窗口)**:阶段二把 cheap-worker 从 `cheap_studio` 归并进 Service `/chat`、复用这套 middleware**已完成,见 §12**回滚开关repair 注入挂一个 genconfig flag`single_post=False` 加关开关=退回阶段〇前);`max_resumes` 在 single_post 下消费端 SSE 兜底要与服务端续修上限同源(否则 CLI 传小值会把合法续修局误判超时、丢产物);并发跑 `run_gates` 会撞固定端口 4330/9322需 single-flight 锁或按 session 派生端口(现 n=5 串行不触发)。**真端到端排 mini-desktop 窗口**(要 chrome/esbuild续修真拦 finish 且与框架前置 InboxMiddleware 并存、单 POST 不撞 breaker/SSE 超时、放行=独立重跑、软预算优雅收尾、cheap 硬地板不变,并先量真实门时延再收紧那几个 directional 的超时旋钮。
## 12. 配置控制面阶段一② cheap-worker 归并 Service /chat2026-07-02 落地,切片一)
便宜档从「裸 HTTP `/generate` + 进程内 for-resume 循环」归并到同一套 AgentScope Service `/chat`,复用①的 `RepairMiddleware` / `CircuitBreakerMiddleware.soft_budget` / `GateJudgment`续修判据从「check+build 绿」升成与 tier2 同构的「九门绿」。四个代码 commit 加 plan 已 push `origin/dev/2.0.0``10aeed3c..526e9b3d`);权威 plan `docs/plans/2026-07-02-配置控制面阶段一②-cheap归并Service-plan.md`SDD 留痕在 `.superpowers/sdd/progress.md`
- **判据线**`worker/gate_judge.py``judge_cheap_verdict`):便宜档 verdict 是 `{pass, guards{门:{pass}}}`(无 tier2 的 layerResultsfeedback 从各门 detail 另拼中文。放行要求顶层 `pass` **且 guards 非空**——只看 pass 会把「play 没跑出逐门却被强标 pass」当绿。`cheap_gates.run_cheap_gates` 把九门收口收成同步函数、端口由调用方派生透传,供续修 check 经 `to_thread` 调。
- **独立 Service 壳**`cheap_service_app.build_cheap_app`cheap 与 tier2 各起进程credential 协议、预算档位、工具面都不同合用的分支耦合成本高于省的资源。cheap 走 OpenAI 兼容凭据(`openai_credential`、base+`/v1`)、软预算 `soft_budget=True`(成本上界靠 `max_repairs=6` 优雅终止,不是次数闸——`max_tool_calls`/`max_model_calls``_trip` 是无条件硬熔断、不受 soft_budget 约束,故设 150 只当纯失控兜底、绝不当贴脸成本尺)、并发端口按 session 从进程内池派生(避固定 4320/9222 撞)。
- **跨进程回收成本**C1续修 / 软预算 / trace 的内存态都在 Service 进程worker 侧 driver 拿不到,靠一个 `collector` middleware 在 `ReplyEndEvent` 流经时同步落盘。框架先 yield REPLY_END 再 yield finish Msg只在 finally 落盘会让 driver 一读到 REPLY_END 就返回、此刻盘上还空 → 成功局也上报 costRmb=0重开 M3b 的 D11 恒中性坑);故 collector 在把 REPLY_END yield 给下游前先落盘driver 侧再加有界轮询兜底。
- **消费方归并**`cheap_service_driver` + `worker_service`driver 注册凭据 → 建 agent/session → scaffold + 写会话注册表 sidecar → 发 kick → 复用 tier2 `_wait_for_turn_end` 等回合真结束 → 读九门 verdict + collector sidecar 组 run-summary → reply 外非阻塞丰富度。`worker_service` 默认 run_fn 硬切驱动 driver无金丝雀旗`_default_run_fn` 保留作一行回滚),`result_out` HMAC 回调链一字不动。
**session_id → 后端 gameId 的载体**C2AgentScope 工厂签名固定 `(user_id, agent_id, session_id)`、拿不到后端 gameId 也拿不到 session 记录,而 `AgentData`/`SessionConfig` 是严格 pydantic 无自由字段。故 driver 在 `/chat` 前把 `{external_game_id, write_whitelist, scaffold_template, restricted}` 写一份同机共享 FS 的会话注册表 `_cheap-sessions/<session_id>.json`,两工厂读它把 session_id 解析回后端 gameId——否则 prompt 指后端 gameId、六工具却锁 session_id首轮 read 起点失败加 write 越界。受限写reskin/modify`restricted` 标记 fail-closed`external_game_id` 回落 session_id产物目录与 scaffold 不一致、生成响亮失败,非静默),`restricted` 为真但白名单缺失收窄成空集禁写(`restricted``write_whitelist is not None` 判、不用 `bool()`——空集会误成不收窄放开全写。跨机部署时此载体要改Redis 注册表或凭 storage 读 workspace_id
**红线③ 在 cheap 侧复现(整分支终审才抓到)**:①只在 tier2 `run.py:866` 封了「陈旧 verdict 假绿」,②把 cheap 的两条读盘路径(`cheap_gates` 起 play 前 / driver 收口读 verdict原样带了进来、都没清残留——同 gameId 重跑局加某次门跑 harness 打嗝或零门跑路径setup 静默失败 / SSE 超时未跑一次 check就会把上一局的绿 verdict 当本次结果放行加落库。这条 per-task review 结构上看不到(跨 `cheap_gates→cheap_run→serve-and-play→driver` 四层加跨任务生命周期fable 整分支终审才揪出,按①原样两处 unlink 封死(`cheap_gates` 起 play 前 + driver scaffold 后回合前)。**教训:一条红线在一条路径上封了,不等于封完——每条共享同一脆弱模式的路径(这里是「读盘 verdict 不预清」)都要扫一遍;①的 follow-up 甚至已写到「并发 run_gates 撞端口与陈旧 verdict 复合」,却仍漏了 cheap 侧那次真正的封口,直到看整条分支的终审才补上。** 同批还修了 driver 对 Service 的 setup POST 无状态检查(合法 JSON 的错误响应会让 id=None 往下走、空等 SSE 超时约 600s 才降级——触外部交互红线,改 None 即 fail-fast`acquire_ports` 在事件循环里同步阻塞(池耗尽冻死整 Service`await to_thread`)。
**follow-up**:真门 e2e 排 mini-desktop 窗口(单 POST 收敛 + 续修真拦 + C2 首轮不越界 + C1 成功局 costRmb≠0 + 并发不撞端口 + 七值回调 + 代理旁路 + **盲修空转率**——最后这条是决策留的头号观测项cheap 无九门自检工具、agent 每次纯文本终止后 check 才第一次真跑门、不过就据 feedback 盲修空转会显著抬时延cutover 后量真实空转率再定是否加自检工具cheap-worker 全套有 14 个 `test_toolkit.py` 失败,是 pre-② 的测试隔离污染(某测试污染 test_toolkit 依赖的共享盘态,单文件跑与排除②新测试都复现),与配置控制面无关、待单独修;有界 worker 池真并发 cap承①端口池是为它设计的机制、真放开并发才用得上
**mini-desktop 真门窗口验证2026-07-03两轮**:② 归并 cutover 三接缝真门全闭合——单 POST 收敛 / 续修真拦放行 / C2 目录一致 / 代理旁路无 502 / result_out 承重外加窗口暴露并已修实证的两个部署接缝。其一requirements 补 `agentscope[service,storage]==2.0.2` extras——裸钉 `agentscope==2.0.2` 缺 fastapi/uvicorn/apscheduler/redis全新 venv 起不动 Service而 SDD 单测只 import 到 create_app 之下、真起服务才暴露(`cheap-worker``tier2/gen-worker` 同款)。其二,成本子系统改用 per-POST 凭据的 key`current_model.credential.api_key`SecretStr 公开字段)取价,不再依赖 Service 进程 env NEWAPI_KEY——成功局 costRmb 从 `0.0/degraded``1.0785/active`commit `24e558a4`B深 per-POST 取价 + B浅 `build_cheap_app``ensure_api_key_env` 双保险)。红线③ 用植入带 marker 的绿 verdict 做受控 e2e 坐实marker 在 M3 完成前即被 driver 侧 unlink 清掉,终局是本局自产的真 verdictpass=False而非假绿。**但便宜档真生成当前系统性产「菜单空壳」E_live/G_input/H_progress 未过、输入不驱动状态),并撞 MiniMax-M3 `400 tool result's tool id not found(2013)`(崩 ChatService.run → driver idle 600s 慢失败——这属生成质量WU-B/C/F与 M3 可靠性,与 ② 归并正交但决定便宜档实际可用率、cutover 真上线前需另线解。** 归并交付面 cutover-ready「便宜档真能上线可用」是另一条线。
## 13. 便宜档生产 cutover 窗:框架默认注入面审计 + 熔断签名对齐设计意图(2026-07-04 落地,切片一)
cutover S0S4 与 A11 计费 e2e 在同一窗完成(证据=cutover plan §9 执行记录),窗内抓修四件全在生产真流量下坐实。两件有跨项目复用价值,记在此;另两件(studio 透传拒因、C6 族错配直指)属产品语义修,细节看 plan §9 即可。
**框架默认注入面击穿自有护城河(§12「跨路径封口」的第三例,也是最重的一例)**。AgentScope 2.0.2 Service 路的 `build_toolkit`(app/_service/_toolkit.py)把 workspace 内建工具(Bash/Edit/Glob/Grep/Read/Write)无条件并入每个 agent 的工具面,再拼我们的 extra 六工具。后果:便宜档写边界靠六工具白名单(write 越界拒 + basename 拒,I1 fail-closed),但内建 Write/Edit/Bash 根本不走白名单,Bash 甚至能以绝对路径越出工作区——护城河被框架默认面整体旁路。同时 12+ 个语义重叠的工具(Read vs read_file、Edit/Write vs write_file)对 M3 是实打实的混乱税:生产实测 80011 一局 14 次工具失败(拿内建 Bash 用相对路径 `ls` 连败、把框架 Read 的 `limit` 参数串到我们的 read_file 上、内建 Edit 的 read-first 约束连环撞),¥12.24 烧到 step_cap;关掉内建后同 brief 复跑(80012)零工具失败、¥2.13 一次过门。修法=第三块钉版本补丁 `ws_builtin_tools_patch`(monkeypatch `LocalWorkspace.list_tools` 返空;cheap Service 进程只跑便宜档 agent,进程内全局补丁作用域恰好;tier2 独立进程不受影响)。**教训升一级:接入任何框架的 Service/App 形态,必须逐项审计框架默认注入的能力面(工具/中间件/调度/团队/后台任务),与自有约束(白名单/预算/门)对账——CLI 路没有的面,Service 路可能默认全开;per-task review 结构上看不到这种「框架给的」面,要靠真流量观测或整链终审。**Planning/Team/Schedule 工具面收窄仍是 follow-up(工单 h,成本上界已有 ¥ 两段式硬地板兜)。
**stuck 熔断签名必须承载「同错」语义,且失败必须即时可归因**。四道熔断里 stuck 的设计意图是「连续 N 次同一失败签名=死圈」,但实现把签名写成了 ToolResultState 的裸状态值——恒为 "error",任意 4 次异类临时失败也会误熔断(80009 生产实证 ¥2.6 作废),而且事后四路取证全空(trace 是 D11 瘦骨架无工具名、Redis 会话崩溃不 flush、结果文件只 dump 大结果、熔断消息只有 tool_call_id)。修法=签名富化为「工具名+状态+归一化错误头」(长 hex/数字→#,同类错误仅差 id/行号仍同签名),异类连败不再误判(交 step_cap/¥ 闸/timeout 兜底,80011 实证组合兜底链正确协同),并且每次工具失败即时打印一行(名字+错误头)。**教训:熔断/告警的触发凭据里必须带上「事后归因要用的最小事实」,否则每次触发都要重放一遍现场;观测修往往当场破案——签名富化上线后第一局就把内建工具面问题钉死了。**
**运维面三条硬记**:① jar 世代升级=依赖面随升——新 jar 带上了阶段〇的 Nacos 接线,`NACOS_PASSWORD` env 缺 → 登录 403 → 注册 failFast → 进程静默退,连 boot banner 都不留;回滚资产要「旧 jar + 旧 env 面」两层,升级前先 diff 新世代多要什么 env。② 环境快照采集要验完整性——S0 的 `/proc/<pid>/environ` 快照只存到 PATH(采集半途失败没人发现),重启即 DB 密码丢;权威源是 `infra/.env`(裸赋值文件,source 前必须 `set -a`)。③ 并发争用会污染测量——bake_off conc=3 时 spec-smoke 100% 失败(Chrome/端口挤兑)→ 全场考卷回退 key-cycle,测出来的过门率是环境噪声不是生成质量;凡「测质量」的批必须串行或与基线同并发口径,且 smoke 一类哨兵失败必须打印真因(raw 尾),不能只留 PASS/FAIL 一个位。
**F-2/F-3 收敛窗补记三条(2026-07-04)**:① **双源默认值影蔽**——旋钮外置单源(genconfig)后,调用链上任何写死默认值的显式透传都会静默短路单源:`batch_run`/`run_engine` 三处 `default=40` 让「writer 提到 60」在 r3 实跑仍是 40,而 middleware 系旋钮(直读 genconfig)全部生效,同一批里真假参半最难察觉。外置旋钮的 DoD 必须含「调用链无第二默认值」(全链 grep `default=`/字面量对账),调用方一律传 None 哨兵、由消费端解析单源。② **隔离 worktree 基线不可信**——同批五个 agent worktree 全被建自错误分支(dev/1.0.0,目标文件整棵不存在);五个 agent 虽全部自查自纠(reset --hard 到 dev/2.0.0),但这依赖 agent 素质,不可赌:派单 prompt 写明目标分支,**终审第一步必验父链**(`git merge-base` 确认基于主线近点,再看 diff)。③ **pgrep 模式串自匹配是全场景坑**——不止 kill/relaunch(§旧记),连纯探活(`pgrep -f 'a|b'` 出现在自己 ssh cmdline)也会自匹配报假活进程;模式串永远用 `[x]` 括号技巧,且同一命令行里不要 echo/字符串携带模式本体。另:ssh 复合命令里 `cd` 只作用于当段,第二段后台启动要用绝对路径(worker 重拉 ENOENT 实录)。
## 14. 配置控制面阶段二五步落地窗(2026-07-04,fable 编排·opus 五单·逐波终审)
一天内把阶段二从「设计定稿待排期」推到「五步代码全落」:前置 namespace 统一(`b2b4d389`)→ 步骤4 genconfig Nacos 热源(`344330a4`+入口 `d9def7d9`)→ 步骤1+2 yudao 治理子域 V29/V30(`3ae34de1`)→ 步骤3 双路激活(`72973532`)→ 步骤5 漂移对账(`a8dd5e50`)→ 路A 复数修复(`22444fd5`)→ 收口涟漪回写(`c4e8df15`)。同窗 W-PCI prompt 四道闸 CI 当日排单当日落(`ec95faaf`)。三条新坑与两条工艺硬记:
**① 跨系统客户端的路由字面量,终审必须对框架源码逐字核——单测桩验不出 404。** C 波路A PATCH 写成 `/session/{id}` 单数,AgentScope 2.0.2 session router prefix 是 `/sessions` 复数(app/_router/_session.py:87,我方 bootstrap.py 同用复数);客户端单测断言的是自己拼的 URL,桩永远绿,对真 Service 必 404。这颗雷不是评审看代码看出来的,是 D 波写对账**只读路**时按真源码用了复数、与 C 波写路对不上才暴露。教训两层:跨系统 URL/字段名的评审凭据=框架源码或既有真跑代码,不是对称的自家测试;**同一契约的读写两路分两单实现时,「读写口径互证」本身就是一道免费的门**——排单时可以有意这么排。
**② 真外呼 IT 用「显式门旗 + fail-closed 凭据断言」双保险,拒绝静默 skip 假绿。** A/C/D 三波真库/真 Nacos IT 统一形态:默认 `@EnabledIfSystemProperty(aigc.config.e2e=1)` 跳过(常规 mvn test 不牵外网),开旗后凭据缺失不是 skip 而是**红**(「须注入 NACOS_PASSWORD」断言)。fable 复跑时先被门旗骗过一次(Skipped 2 误当绿)、又被凭据断言拦一次——两道都在防「跳过当通过」:终审复跑 IT 必须核 `Tests run` 的 Skipped 列,Skipped>0 的"绿"不是绿。
**③ Nacos publish→read 最终一致:改值后立即 GET 可能返旧值。** C 波路B e2e 实测发现;对账/断言读生效 dataId 必须带轮询容忍窗(D 波 8s 窗+800ms 步进),否则把传播延迟误判成配置漂移。同理生产侧「激活后立刻查投影」的任何面(阶段三 UI、阶段四对账巡检)都要吸收这个窗口。
**工艺硬记两条**:㋐ 激活一致性的落地形态=「sanity 前置拒发 + beginActivating 快照(单库事务)+ 双路短路下发 + 全成才 markVersionActivated + 任一步败即补偿重推回上一激活版 + 账本提交失败留 ACTIVATING(V30)由 recoverActivating 幂等向前收口」——投影撕裂由即时补偿收敛,不承诺投影层原子;「向前执行到底」与「补偿回退」两个恢复语义按失败点位分开,不混用。㋑ MyBatis-Plus 把 BIGINT 指针写回 NULL 必须 LambdaUpdateWrapper 显式 SET(updateById 跳 null 字段),激活指针/在途指针这类「NULL 有语义」的列写法要单测钉住。
**部署窗口批(2026-07-04 同窗续,opus agent 三阶段·fable SSH 独立终审)**:阶段二五步落码后在 mini-desktop(100.64.0.7 `/root/tier2-run/cheap-worker`)真机收口——Phase A 同一真 session 单数 `/session` 404 vs 复数 `/sessions` 200(curl+httpx 双证、排除 session 无效歧义)把坑①的修复 `22444fd5` 真机坐实;Phase B-deploy flag-off 快进重启 :8300/:9501 + smoke-1 succeeded(¥1.53);Phase B-enable 灌 cheap/tier2 两 dataId 各 9 键(readback 逐字零漂移)+启用旗灰度上 :8300、门阈值热改探针 `color_dist_min` 18→25→18 **亚秒生效且 :8300 未重启(纯热推)**+smoke-2 succeeded(¥4.45、¥10 闸来自 Nacos 热读)。终态 :8300 旗-ON 从 Nacos 热读路B、回退=去 `TIER2_GENCONFIG_NACOS` env 重启一处可逆。**live 变更终审工艺=SSH 只读独立核实**(pid/cmdline、`/proc/pid/environ` 的 flag env、openapi HTTP、health、`ss` 监听)与 agent 报告逐字比对,不只信报告——本窗核实全吻合=报告真实。两条部署坑:
**④ `Agent isolation:worktree` 默认基于仓库默认分支,本仓即 dev/1.0.0——错线。** `origin/HEAD→dev/1.0.0`,而 dev/1.0.0 与活线 dev/2.0.0 岔开 893 提交、仍是 TS monorepo 老世代(无 game-cloud/pom)。派 worktree agent 干 dev/2.0.0 活,prompt 必须令其先 `git reset --hard <dev/2.0.0 tip>``git checkout -b work <tip>`(linked worktree 不能直接 checkout 主树已占用的 dev/2.0.0)。本窗按「基线不对就 STOP」铁律让 agent 停在错线(未授权自行 reset)才暴露该默认;**小而精确的活基线不对时,主树直修比再派 worktree 更快更稳**(这两条测试债即主树直修 `167cedde`)。
**⑤ venv 缺「已声明但惰性 import 故建 venv 时未装」的依赖=开旗后静默失效。** `nacos-sdk-python==1.0.0``tier2/gen-worker/requirements.txt` 声明,但 `genconfig_nacos` 惰性 `import nacos`,建 venv 时没装也不报错;`TIER2_GENCONFIG_NACOS=1` 开旗后 `import nacos` 失败→热源静默不 attach(零漂移仍保、但路B 通路根本不通)。启用旗前置=`pip install -r requirements.txt` 确认关键惰性依赖在位;重建 venv 同理。惰性 import 的依赖不会在 CI/单测暴露,只在真开旗那一刻炸。
**⑥ 阶段三前端(`79264197`)= 列单人的工单也会有契约出入,executor 逐字对源核是免费的第二道门。** fable 列的阶段三工单把状态枚举名(实为 `AigcConfigStatusEnum` 非工单写的 `GenConfigSetStatusEnum`、且漏 `ACTIVATING=15`)、`/version/get` 参数(实为 `versionId`)、`/version/diff` 参数(实为三参 `versionAId`+`versionBId`)写岔;opus 执行时逐字对 controller/enum 源核出、按源修正、回报出入清单(漏 ACTIVATING 会让激活中态渲染空白、"恢复卡激活"按钮无从触发)。教训:工单/设计档里的枚举名、参数名、字段名一律标「以源为准、executor 必核」,别让列单的转述当契约真相——与 §14① 跨系统路由字面量对源码核同源:契约字面量的权威永远是源码,不是任何中间转述(设计档/工单/记忆)。**另**:game-admin 构建门是 vite build(`node ./node_modules/vite/bin/vite.js build`),`pnpm build:*` 会先跑 `runDepsStatusCheck` 触发 `pnpm install`、无-TTY/无网时中止(非编译错);终审亲跑要直调 vite 二进制绕过 pnpm wrapper,别把 wrapper 的依赖检查失败误判成构建红。
## 15. 升级 AgentScope 2.0.3(2026-07-06,创始人拍板)
框架从 2.0.2 升到 2.0.3,创始人 2026-07-06 拍板。动因不在某个新特性,而在没有道理把底座停在一个已知带并行 tool-result 缺陷的补丁版上——2.0.3 是同一大版本线上的相邻补丁版,升级面窄、收益确定,拖着只会让欠的债更难还。这条决策承 §11§13 那套「护城河进 AgentScope 洋葱、框架件谨慎审计」的线。
兼容底气来自三条独立证据,不是「跑通就算」。一是把两版源码逐处比对:RedisMessageBus 零改动,OpenAI formatter 一字未动,Anthropic formatter 唯一的变化是修掉了并行 tool-result 的一个 bug(对我们只减风险),ModelCallEndEvent 结构不变,中间件基类 MiddlewareBase 只加性地多了个 `get_middleware_key`、既有钩子签名全不动。二是两条线的护城河补丁逐块重验锚点:上下文压缩 fail-open(`compress_context` 仍在 `_agent.py:259`)、M3 流式聚合兜底(`_parse_stream_response` 仍在 `_openai_chat/_model.py:297`、按 `tool_call.index` 分桶仍在 :417)、workspace 内建工具封口(`list_tools` 仍在 `_local_workspace.py:660`)、Planning/Team/Schedule 收窄(`get_toolkit` 装配点迁到 `_chat.py:354`,补丁替的是绑定名、本不依赖行号)——四块的依赖契约在 2.0.3 全部成立,补丁头的钉版声明与复核标记已同步到 2.0.3。三是 cheap-worker 与 dev 两个 venv 装上 2.0.3 后,便宜档 393 + tier2 140 共 533 条测试全绿、dev 三服务起来健康;两处 requirements.txt 的钉版已从 2.0.2 改到 2.0.3。
2.0.3 原生补进了几样过去判定「框架没有、只能自建」的能力,记在这里,但本期都列而不迁。其一是 `ReplyBudgetControlMiddleware`——这正是早先设计反复核验「2.0.2 源码里不存在、故预算软刹必须自建」的那个类,2.0.3 把它补了进来(`middleware/_budget.py`)。它按加权 token 预算在 `on_reply` 里累计、到顶注入一条提醒消息,是软控;我们自建的软预算是 ¥ 两段式——软停线(便宜档 ¥10 / 富档 ¥50)只许收尾,硬地板(×1.5)fail-closed 数学封顶,属护城河(见 §11、运行时 SoT §5.2)。原生的 token 软控替不了这套 ¥ 制 fail-closed 硬闸,故保持自建,原生件只在「能否给 ¥ 闸做 token 估算上游」这一层评估。其二是中间件洋葱扩到工具执行层(`on_acting` 包裹 acting、middleware 可经 `list_tools` 贡献工具)、原生 RAG 管线(`rag/` 包:KnowledgeBase + chunker + qdrant 向量库)、mem0 长期记忆适配(`middleware/_longterm_memory/_mem0/`);它们对应 tier2 未来的经验召回与检索需求,排在生成主线达标之后按需评估,不趁升级顺手迁。
follow-up:2.0.3 原生 `ReplyBudgetControlMiddleware` 能否给自建 ¥ 闸做 token 估算上游(接 §10 阶段留的「2.0.3 原生 token 限制 build-vs-buy」);原生 RAG 与 mem0 在 tier2 经验召回线的取舍;历史文档里「2.0.2 核验 ReplyBudgetControlMiddleware 不存在」这类句子是当时的真验证事实,按两层纪律留痕不回改,该类新事实只落在本条。
## 16. 内测闭环 A 段落地(2026-07-07,注册/额度/dify/facade 收口)
创始人把「7 月中旬内测」的开发面收敛成一条种子用户旅程:注册(用户名+密码、无短信、网关 IP 限流)→ 得 ¥100 额度 → 生成 → 试玩 → 发布到 feed → 数据飞轮回流。这条闭环的三个真开发缺口(注册登录、额度、dify 节点)已实现并在 dev 真机逐环验证,代码经 `neice/stage1-integ` 三方合并进 dev/2.0.0(merge `210ce701`,11 提交,全部 opt-in 默认关)。此处记的是过程里几个不查就会踩坑的事实与被现实推翻的方案,不是功能清单。
**额度不自建钱包,对接 new-api 的 per-user 配额——但 new-api 的管理边界逼出了预置池。** 创始人拍板额度口径 = 复用 new-api 网关原生的按用户配额,每人 ¥100,生成消耗由 new-api 权威计量,而不是在 game-cloud 里新建 credits 钱包/扣额子系统。new-api(one-api 系,`100.64.0.8:3000`,DB=`infra-postgres:5432/new-api`)的原生模型是 `users`(quota/used_quota)+ `tokens`(每个 token 属于某 user,用它调用即扣该 user 配额),配额单位是 one-api 默认的 500000/$1、DB options 未覆盖,按 usd_rate 7.3 折算 ¥100 = 6849315 quota。原本设想注册时在线调 new-api 管理 API 开户+建 token+充额,但 S0 阶段实测撞到一条硬边界:**admin/root 令牌能建用户、能 setQuota,却不能替他人建 token**——`POST /api/token/` 永远把 token 绑到令牌自认证的那个用户身上,请求体里的 `user_id` 被忽略,而 `access_token` 也无法经 API 写入。这意味着「每个玩家一把自己的 token」这一步绕不开直接写 new-api 的 postgres。据此创始人拍板改走**预置池**:离线 ops 脚本(`game-runtime/tools/newapi_pool_provision.py`,纯 stdlib,每条四步 = 建 user → 直连 postgres UPDATE access_token → 以该 user 身份建 token → setQuota ¥100)预先造好一批 user+token 对灌进 game-cloud 的 `newapi_quota_pool` 表(FREE),注册成功后由消费者 CAS 抢占 FREE→CLAIMED 把池条目绑到玩家。注册走 outbox(注册成功落 outbox → RMQ → claim 消费者)让绑定与注册事务解耦、至少一次。任何后续要给用户发独立 new-api 凭据的活,都得先认这条管理边界,别再假设在线建 token 可行。
**生成计费的权威是 new-api,不是 worker 自估。** job 组装时执行器把认领到的池 token 注入 `userToken` 位(只对真实 member 生效,系统/bake-off 局旁路),worker driver 从 `job["userToken"]` 取凭据调 new-api,new-api 就天然按该用户扣 used_quota。dev 上一次真生成后 new-api user17 的 used_quota 从 0 涨到 26651(¥0.389),而同一局 worker 自估 ¥1.03——两者不一致,正是「用 new-api 权威计量、不信 worker 自估」的实证理由。额度耗尽/凭据失效用跨契约共享枚举 `quota_exhausted` 分辨(`contracts/api-schemas/aigc.yaml` + `dify-workflow-io.json` + Java/Python 三处同批)。
**注册登录、facade 回调这两块的坑都在「防伪」细节上。** 注册加的是 `game_player` 的 username/password 列、mobile 改可空、`uk_username` 唯一约束(Flyway V31);防用户名枚举的做法是准备一个固定的假 BCrypt hash,当用户名不存在时也照样跑一次 BCrypt 校验,使「用户名不存在」与「密码错」走同一码路同一时延、返回同一错误码,避免时序侧漏;IP 限流复用 passport 既有的 Redis 计数范式、按 scene 分桶且 fail-open(Redis 挂不拦正常注册)。facade 收口的是回调验签:dev 上后端与 worker 的 callback-secret 原本都空 = 验签关闭 = 内网可伪造回调驱动落包,内测必须两端配同一把非空密钥(后端 `AIGC_CALLBACK_SECRET` / worker `--callback-secret`,HMAC-SHA256 对原始 body 算签置 `X-Callback-Signature`)。dify 走形态A:mini-infra 已部署的 dify(:18080)建一个 HTTP「游戏开发节点」直连 cheap-worker `:9501/generate`,复用现成 agentscope 生成链,后端近零改;a-min 形态不带 userToken,driver 回落全局 key,与 per-user 计费天然隔离。
follow-up:预置池当前是离线人工灌,放量前需接一条池水位告警+补池的运维线(池空则注册拿不到额度);dify 形态A 的 HTTP 节点已证 curl 可通,workflow UI 实搭是 ops 步待做;quota 单位 500000/$1 与 group 定价若日后改价需在折算处(ops 脚本 + 任何入队余额门)同步。相关活账见记忆 `neice-core-loop-e2e-live`、设计档 `docs/agent-specs/2026-07-07-内测-WU1/WU2/WU3-*.md`
- **新模板接入操作配方**:见 [`.agents/skills/add-game-template.md`](../skills/add-game-template.md)(批② idle/tycoon 直接套用)。

View File

@ -5,7 +5,7 @@
## R1 现货尽调前置门(硬门)
基建类组件开工前,评审版 spec 必含「Build-vs-Buy」一节:**≥3 个具名现货候选**(或附检索证据声明"生态位为空"),逐个给排除理由,理由必须落入枚举 {许可/体积预算/沙箱与 CSP 约束/维护健康度/集成成本/数据主权}。**「维护健康度」含生命周期状态硬查**:候选是否已归档 / deprecated / 停更、能力是否已并入上游(查官方仓 archive 标记 + release 频率)——**归档项不得作首选**。第二轮评审 checklist 必查此节;缺失=评审 fail,不得进执行版。
基建类组件开工前,评审版 spec 必含「Build-vs-Buy」一节:**≥3 个具名现货候选**(或附检索证据声明"生态位为空"),逐个给排除理由,理由必须落入枚举 {许可/体积预算/沙箱与 CSP 约束/维护健康度/集成成本/数据主权}。第二轮评审 checklist 必查此节;缺失=评审 fail,不得进执行版。
## R2 形态终点测试(spec 必答项)
@ -18,7 +18,7 @@
## R4 prior-art 强制步(AI 放大器对冲)
agent 动手写任何 **≥100 行**的新工具/harness/协议实现前,执行版 spec 或任务卡必含三行「prior-art」:①仓内已有同类 / 已有调研结论?(grep 代码**与 `.agents/knowledge/` 蒸馏事实**——同类选型常已被记过,直接用)②npm/PyPI/GitHub 现货?(检索词+前 3 结果,**并核候选生命周期:是否归档/停更**)③采纳或拒绝的一句话理由。缺三行,评审/收口打回。**spike 豁免 R1,但 spike 产物转正必须补过 R1**——"钱花两次用"只豁免重写,不豁免选型。(教材案例:llm_client 客户端 token 估算重造 new-api 权威计费,评审事后才抓回。教材案例 2(2026-06-25):`agentscope-runtime` 被外部研究当独立可采件、荐为扩容/沙箱首选,实则官方已归档、能力并入 `agentscope` 2.0.2,且本仓 `.agents/knowledge/agentscope-2.0-facts.md` 早记此结论——漏 R4① 仓内 grep + 漏 R1 归档状态查,双失,差点写进 SoT。)
agent 动手写任何 **≥100 行**的新工具/harness/协议实现前,执行版 spec 或任务卡必含三行「prior-art」:①仓内已有同类?(grep 证据)②npm/PyPI/GitHub 现货?(检索词+前 3 结果)③采纳或拒绝的一句话理由。缺三行,评审/收口打回。**spike 豁免 R1,但 spike 产物转正必须补过 R1**——"钱花两次用"只豁免重写,不豁免选型。(教材案例:llm_client 客户端 token 估算重造 new-api 权威计费,评审事后才抓回。)
## R5 豁免面(防误杀)
@ -34,4 +34,4 @@ agent 动手写任何 **≥100 行**的新工具/harness/协议实现前,执行
---
**决策链落点**:R1/R2/R4 → `ai-development-protocol.md` spec 模板与评审 checklist;R3 接线证据/转正重选型 → `wave-close-checklist.md`;R6/R7 → 波次验收门。审计全文与盘点表见 `git show 6d2f8789^:docs/agent-specs/_archive/2026-06-12-自研偏误全链审计-报告.md`(已随 _archive 清理删除git 定位)
**决策链落点**:R1/R2/R4 → `ai-development-protocol.md` spec 模板与评审 checklist;R3 接线证据/转正重选型 → `wave-close-checklist.md`;R6/R7 → 波次验收门。审计全文与盘点表见 `docs/agent-specs/_archive/2026-06-12-自研偏误全链审计-报告.md`。

View File

@ -1,7 +1,7 @@
# 工程编码与协作硬规范
> 本文是绘境AI 全栈工程师 / 前端 / AI 工程师 / QA 必须遵守的**硬规则**。违反即不通过 review。
> 蒸馏来源:`docs/architecture/架构/README.md`§4 编码 / §5 Git / §6 联调 / §7 测试 / §11 Checklist`docs/architecture/架构/README.md`§7.6-7.8 工程治理)、`docs/superpowers/specs/mvp-execution-spec-design.md`§8 技术约束)。
> 本文是造梦AI 全栈工程师 / 前端 / AI 工程师 / QA 必须遵守的**硬规则**。违反即不通过 review。
> 蒸馏来源:`docs/architecture/系统概要设计-开发团队版.md`§4 编码 / §5 Git / §6 联调 / §7 测试 / §11 Checklist`docs/architecture/系统概要设计-技术决策版.md`§7.6-7.8 工程治理)、`docs/superpowers/specs/mvp-execution-spec-design.md`§8 技术约束)。
> 配套:可靠性/安全红线见 [`security-and-reliability.md`](security-and-reliability.md);元流程见 [`../workflows/ai-development-protocol.md`](../workflows/ai-development-protocol.md);事实蓝图见 [`../knowledge/product-and-architecture.md`](../knowledge/product-and-architecture.md);模块落地手册见 [`../skills/add-business-module.md`](../skills/add-business-module.md)。
---
@ -146,22 +146,6 @@ URL 格式:`/{端前缀}/{模块}/{资源}/{动作}`。示例(左列为含
| 向后兼容 | 新增字段给默认值;老消费者忽略未知字段 |
| 不兼容变更 | 用新事件名(如 `game_play_start_v3`),新老并行消费直到老版本下线 |
### 5.2 契约合入的 DoD无校验器不算契约2026-07-03 Δ5
一份只写了字段和示例、却没有机器校验器的 schema挡不住任何漂移字段被改歪、协议两端各写各的、生成产物不合结构它既不报警也不阻断只能等下游踩坑后再回头补一道门。这个项目两周里补的门几乎都是这个形状——docs-gate、生成 check 门第 6 节的未定义标识符与断链扫描、play-scene 结构的 AST 门、计划谱系 G7 门,无一例外是漂移已经发生(有的甚至已推上远端)之后才追加的闸。每道补上都管用,可每道都晚一拍,拦下的是已经酿过一次事故的那类问题。这条规则把闸从「出事后追补」提前到「立约时就在」。
由此,一份契约或一处协议字段要算合入完成,除 schema 本身外必须同时交付两件配套,三件缺一不可:
1. **机器可校验的 schema 载体**——契约以机器能解析、能拿去比对的形式表达JSON Schema或承担同等职责的等价物OpenAPI schema、SDK 的 `.d.ts` 类型声明、Flyway 迁移的结构约束等),不能只有自然语言约定或一个示例文件。
2. **机器校验器**——一段对候选数据或实现跑出通过或不通过结论、退出码可被 CI 与 pre-commit 消费的程序。现成范本是 `contracts/prompts/check_registry.py`registry 版本与 `.md` frontmatter 漂移时 exit 1 阻断,既挂 pre-commit 也进流水线。校验器把 schema 从贴在墙上的规格变成能真拦住提交的门。同一形状的门可拆离线真模型两段prompt 治理的四道闸2026-07-04 W-PCI就是 `contracts/prompts/check_version_bump.py`(闸 0「改正文必升 version」`.githooks/pre-commit` 门 5 `.gitea/workflows/contract-gates.yml`,每提交秒级跑、零成本)+ `contracts/prompts/eval_gate.py`(闸 1~4 真调 MiniMax-M3 判金标集,挂独立 `.gitea/workflows/prompt-eval.yml` 手动 `workflow_dispatch` 触发、单跑 cap ≤¥5只焊 registry 头部对账认定的 live 面)——离线纪律随提交焊死,花钱的真模型闸按需触发。
3. **负样本进 CI**——至少一组本应被拒的构造(违反必填、类型、枚举或某条业务不变量的输入),与校验器一起常驻 CI。只喂正样本的门等于没验证过它会不会漏放负样本是校验器自己的考卷证明它拒得动该拒的东西。
**什么算契约件、评审在哪拦**:凡改动 `contracts/`(八类契约单一事实源,及其 `agent-loop/``trace/` 下的 schema`-api` 包的跨模块 DTO/VO、事件 schema、SDK postMessage 协议,或任何承担跨端/跨模块/跨版本约定角色的结构(如 game-runtime 内的 host↔游戏装载协议都算契约件。这类变更的 PR评审须核这三件是否齐备、且在 CI 里真跑绿校验器对正样本放行、对负样本拒绝缺任何一件即视同契约未完成不予合入——schema 写得再规整也不例外。它接在既有 contract-first先改契约再写代码见 §5 契约对齐)之后:「改契约」这个动作的完成定义,从此含这三件。
**存量不溯及**:本条自 2026-07-03 起对**新增契约**与**新增/变更的协议字段**生效。已合入但尚无校验器的存量契约不强制回补;一旦对某份存量契约新增或修改字段,改动所涉的那部分即落入本 DoD需随手把校验器与负样本补到该字段。增量生效、带了就验与计划谱系 G7 门§10.8)「存量不强制回填、带了就验」是同一种姿势。
> 依据2026-07-03 创始人「一次性裁决」Δ5「契约schema校验器」裁决表与全文见 `docs/agent-specs/2026-07-03-生成引擎agentic架构-第一性重推演与差量-设计.md` §5理由见该档 §3.2——九份固定契约每份必须随附机器校验器进 CI没有校验器的契约只是文档。
---
## 6. Git 协作规范
@ -290,58 +274,24 @@ subject: 动词开头,简明描述(中英文均可)
- **同一提交内删/归档被取代者**(强化 §10.2commit 设计档前**自检**——「本次提交/工作树里有无被本档取代的旧档?」有则同提交 `git rm`(过期且错)或 `git mv``_archive/`(过期但留参考)+ tombstone 横幅;**绝不把过期档单独连推上去**。
- 收口按 wave-close 第 8 步把本任务设计档**收敛成最小自洽集**(典型 1 review + 1 execution
### 10.5 canonical 活档(子系统 SoT · 2026-06-17 立 · **2026-06-20 域化重构改根**
> **⚠️ 2026-06-20 更新(设计文档域化重构)**:策展层设计文档的 canonical 根**已从 `docs/agent-specs/` 迁到 `docs/architecture/` 单根 6 域 4 级树**(产品/架构/后端/前端/运营/运维,人读散文·多图·单档 ≤2000 行,总索引 `docs/architecture/README.md`)。下文「无日期主题名·≤150 行·住 agent-specs」是**旧形态**,现**仅生成域设计链过渡期保留**(架构演进中);其余子系统 canonical 已迁入域树、源档归 `_archive`。判层序见下方已更新条。
### 10.5 canonical 活档(子系统 SoT · 无日期主题名 · 2026-06-17 深度重构立)
> 由来2026-06-17 创始人要求 agent-specs 从「按时间堆 review/execution/verdict 流水」改为「**每子系统一份 canonical 活档**」——dated spec 是某波次过程产物,子系统的「现行架构+目标+现状」应收敛成一份持续更新的活档。本节是 §10.4「一题一活档」在**子系统粒度**的落地。是 §10.1 dated 命名词表之外**另立的一类活档形态**(不违反 §10.1,互补)。
- **形态**`<主题>.md`**无日期前缀、无 type 后缀**,如 `引擎与运行时.md`/`agentic编排-SAA.md`/`战略与合规.md`);顶部第一行标 `# <主题> · canonical子系统 SoT`,随附 `类型=canonical 活档 · 更新 YYYY-MM-DD / 取代=N 份历史 spec / 读法`。一子系统一份,**≤150 行**。
- **装什么**:一页结论 → 目标/非目标 → 现行架构(含必要 Mermaid) → 现状(done/在飞/待办) → 关键指针(commit/skill/契约)。**砍掉**:演进流水/逐轮评审 transcript/逐 commit 过程/取证日志/已 supersede 旧方案(留 git + `_archive/`)。
- **与 dated spec 的关系**dated `review/execution/report`§10.1= 某波次工作期过程档;**波次收口时把活内容折进对应子系统 canonical**wave-close 第 8 步dated 源档随即 `git rm`(已折入)或 `git mv _archive/`(账本/取证类留参考 + tombstone。canonical 之外不再久留同主题 dated 流水。
- **分层(不双写 · 2026-06-20 改根后)**canonical `docs/architecture/` 6 域 4 级树(策展层设计 SoT·人读散文·深 `.agents/knowledge/*`AI 速查一句话·浅)> `docs/mvp/MVP进度总账.md`(跨线状态)。`docs/agent-specs/` 降为留痕 + spike + **生成域演进设计链(过渡期活档)**。重叠处 architecture 域树是 SoT其余留速查 + 指针。
- **导航**设计文档总入口 `docs/architecture/README.md`L1 总索引,6 域树)。`docs/agent-specs/_index.md` 降为 trace/spike 索引 + 生成域演进设计链(过渡期) + 指向新树。维护见记忆 `agent-specs-canonical-structure`
- **分层(不双写**canonical子系统 SoT·深 `.agents/knowledge/*`AI 速查一句话·浅)> `docs/architecture/` Doc A/B/C + 技术决策版(基线 WHAT/HOW `docs/mvp/MVP进度总账.md`(跨线状态)。重叠处 canonical 是 SoT其余留速查 + 指针。
- **导航**`docs/agent-specs/_index.md` 列全部 canonical一域一档+ 生成域设计链 + KEEP + 归档指针。维护见记忆 `agent-specs-canonical-structure`
### 10.6 两层阅读心法 + 留痕 frontmatter + 蒸馏门2026-06-17 文档体系重构立)
> 由来2026-06-17 文档体系重构(留痕喂料 `git show 8ea97234:docs/brainstorms/2026-06-17-文档体系重构-requirements.md`,决策记忆 `docs-system-hybrid-two-layer`)。把全部文档归两层**阅读心法**(非新目录/标签,映射 §10.5 canonical + §10.1 dated 流水 + CE 的 brainstorms/plans并补判层规则、留痕最低 frontmatter、机器可检蒸馏门。配套全局 `~/.claude/CLAUDE.md` 已退役「每任务必产 dated 双档」重量级约定。
> 由来2026-06-17 文档体系重构(留痕喂料 `docs/brainstorms/2026-06-17-文档体系重构-requirements.md`,决策记忆 `docs-system-hybrid-two-layer`)。把全部文档归两层**阅读心法**(非新目录/标签,映射 §10.5 canonical + §10.1 dated 流水 + CE 的 brainstorms/plans并补判层规则、留痕最低 frontmatter、机器可检蒸馏门。配套全局 `~/.claude/CLAUDE.md` 已退役「每任务必产 dated 双档」重量级约定。
- **两层(映射现有结构,不新建目录)**
- **策展层(少而准·人读即现行真相)**`docs/architecture/` 6 域 4 级设计文档树§10.5 canonical · 2026-06-20 改根后的设计 SoT 根)+ `AGENTS.md` §12 前门/目标锚 + `docs/mvp/MVP进度总账`(模块进度**唯一 SoT其 §2 矩阵**+ `.agents/`。每个概念在策展层**只有一个 SoT**。
- **策展层(少而准·人读即现行真相)**canonical 活档§10.5+ `AGENTS.md` §12 前门/目标锚 + `docs/architecture/` Doc A/B/C + `docs/mvp/MVP进度总账`(模块进度**唯一 SoT其 §2 矩阵**+ `.agents/`。每个概念在策展层**只有一个 SoT**。
- **留痕/笔记层(放开累积·检索而非通读)**`docs/agent-specs/` dated `review/execution/report`§10.1+ `docs/brainstorms/`WHAT 喂料)+ `docs/plans/`HOW 喂料)+ 收口报告 + `_archive/`。允许累积、不强删,靠 git + frontmatter 检索。
- **判层规则(物理信号,零上下文 agent 据此归层)**`_index` 标 canonical 的 + `AGENTS`/`docs/architecture`/`docs/mvp` =策展;`docs/{brainstorms,plans}` + agent-specs dated `<type>` + `_archive/` =留痕。**「孤儿」只针对策展层**(策展 SoT 必须前门一跳可达);留痕前门不可达=设计如此,不算孤儿。
- **蒸馏门(治「更新不及时」的机制,非口号)**:收口若产生留痕(新 brainstorm/plan/report/dated spec**必须产出「蒸馏 diff」把可复用洞见提升进策展层 canonical/`.agents`)或显式声明「无可蒸馏+理由」**(对齐 §10.2 SHIPPED 横幅与回填检查的「不回填+理由」模式)。落 wave-close 第 5 步。
- **留痕最低 frontmatter**:新留痕文件头带 `date` / `topic` / `status` / `superseded-by`(被取代时填);**存量不强制全量回填**doc-organizer 巡检时按需补 canonical 候选)。
- **每任务记录约定(取代旧「重量级双档」)**:一任务=一 WHAT 喂料(需求/brainstorm`docs/brainstorms/` 或 agent-specs `review`+ 一 HOW 喂料plan`docs/plans/` 或 agent-specs `execution`),轻量、收口蒸馏后留痕;**不再强制每任务产 dated review+execution PAIR + master spec + 分阶段 spec + 固定两轮评审**(旧约定已在全局 prompt 退役为兼容保留)。评审**按风险裁**:高裁量/跨模块才上对抗或多视角评审,机械活不上。
### 10.7 文档治理机器门(2026-06-20 立 · 2026-07-02 脚本落地)
> 由来:2026-06-20 文档治理复盘发现,§10.110.6 规则写得相当全,却全靠无状态 agent"自觉执行"——于是规则都在、品牌名却半新半旧、同一主题散多份、AGENTS.md 自己挂着旧名。2026-07-02 全量普查再次实证(索引给已删档打 ACTIVE 标、28 处死链、四门零脚本)。解药 = 把规则编译成机器门 + 给整体一致性立一个有状态的主人。**门已落地为可执行脚本**:[`../tools/docs-gate.py`](../tools/docs-gate.py)(`bash .agents/tools/docs-gate.sh` 即跑,<5s)。
**七检(查什么 → 红线 → 白名单)**:
- **G1 品牌不变量** — 活层扫**退役品牌根**(旧名字面量只存在于门脚本 `BRAND_RETIRED` 常量;散文一律写"退役品牌名",不再写出字面量,规则文件因此无需豁免)。红线:活层命中即挡。白名单:`docs/ip/`(申报材料须与提交一致)、`_archive/``_recall/`、文件名以日期开头的留痕档、行内 `brand-ok` 标记(极少数合法引用手动豁免)。
- **G2 canonical 唯一性 + 注册表对账** — frontmatter `canonical: true``topic` 全仓唯一,且与 [`docs/architecture/README.md`](../../docs/architecture/README.md) §2 注册表双向一致(表里每行有对应文件、每个 canonical 文件在表里);`_archive/` 内不得标 canonical。红线:同 topic 第二份即挡(杀 doc-sprawl / 影子 SoT)。
- **G3 死链** — 活层 md 的 `[]()` 相对链接与反引号内 `docs/``.agents/` 开头的仓内路径必须存在;`git show <rev>:<路径>` 定位引用放行(这是引用已删历史档的标准姿势)。取代旧 check-deadlinks.sh(其白名单为已删 spike 目录开过口、且探不到反引号引用——门被腐化的实证)。
- **G4 入口卫生** — AGENTS.md 只放项目事实,禁仓库克隆命令/全局安装/个人绝对路径/localhost 端点。
- **G5 留痕隔离** — 策展层(AGENTS.md、`docs/architecture/` 非归档、`.agents/`)不得 md 链接非 canonical 的留痕档(`docs/plans/``docs/brainstorms/`、agent-specs 带日期档);豁免:指向 `_index.md`(在飞板)与 canonical 目标(如 canonical 的执行计划)。`docs/mvp/` 活账不在源集合(账本引证据是其性质),其死链由 G3 兜底、主叙事堆历史链接由 doc-organizer 巡检看住。
- **G6 设计档申报(输入侧门)**`docs/agent-specs/*-设计.md` 必带 frontmatter `topic` + `status` + `sot-impact`(新建 topic / 修订某 topic / 纯留痕)——强制"开工前查注册表",治"新设计漏读既有设计"的根因;配套流程见 [`feature-design-doc`](../skills/feature-design-doc.md)。
- **G7 计划谱系(单指针 `上级:`)** — 2026-07-03 起新建的 `docs/plans/*-plan.md``docs/agent-specs/*-设计.md` 必带 frontmatter `上级:`(仓根相对路径),且沿链 6 跳内到达某份 `canonical: true` 的 SoT;上级死链、链断(中途某档既非 canonical 又无 `上级:`)、成环、超跳数任一即挡。存量档不强制回填,带了就验。约定本体与设计理由见 §10.8。
**门③(doc↔code 兑现门,规格保留、随生成主线 harness 落地)**:keystone 设计声明的关键产物约束必须有机器可验断言且收口真跑。首批断言:生成产物 = `src/` 多文件工程(玩法逻辑落真实源码文件,禁"逻辑只以 JSON 字符串存在 / 运行时动态求值内嵌代码串"——终态定调见 [`agentic运行时架构图说`](../../docs/architecture/架构/生成引擎/agentic运行时架构图说.md))。红线:断言失败 = 实现未兑现设计,挡收口。
**挂载(三层,缺一即是自愿门)**:① 本地 `.githooks/pre-commit`(激活一次:`git config core.hooksPath .githooks`;md 变更才触发;`--no-verify` 可紧急绕过)→ ② 服务端 `.gitea/workflows/docs-gate.yml`(Gitea Actions,runner 就绪即不可绕)→ ③ 流程 [`wave-close-checklist`](../skills/wave-close-checklist.md) 第 8 步固定跑。
**AGENTS.md 自检(根不设防最危险)**:改名/子系统增删/核心决策推翻时,同一 commit 重审 AGENTS.md(品牌、死链、canonical 对账、入口卫生——docs-gate 固定扫)。实证教训:改名十天后入口标题仍挂旧名,正因为"没人想到去审根"。
**整体一致性归属**:机器门兜不住的灰色判断(哪份过期、两份冲突信哪份、补丁还是架构信号),显式归**创始人 + 6c6g 文档/设计线**(有状态主体),不再压在无状态主 agent 头上。
### 10.8 计划/设计档谱系:单指针 `上级:`(2026-07-02 立)
> 由来:配置控制面阶段一② plan 起草后,创始人问"这份计划在整个上线计划的哪个位置",答案要靠考古拼装——plan frontmatter 只指到直接设计档,执行序列 SoT 又没认领配置控制面这条线(中间实断),"阶段"一词还有三套坐标系撞车。此前个别 plan 自发写过 `slice:`/`origin:`/`关联设计:` 等自由字段,各写各的、无人验证。根因:谱系信息一旦散落多处、靠手抄维护,必然腐烂。解药 = 谱系只写一条不重复的最小事实,其余全部推导。
- **约定**:2026-07-03 起,新建 `docs/plans/*-plan.md``docs/agent-specs/*-设计.md` 的 frontmatter 必带一行 **`上级: <仓根相对路径>`**,指向它的直接上级文档(plan 的上级通常是设计档,设计档的上级通常是它所属执行线的计划/SoT);沿链 6 跳内必须到达某份 `canonical: true` 的 SoT(计划线顶端 = 16 周上线主计划 / 统一执行计划)。存量档不强制回填,带了就验(docs-gate G7)。
- **为什么是单指针**:全链、树、"这条线推进到哪"都应是**查询结果而非维护对象**。上级改名、改阶段划分,下游零回填;不设人读面包屑字段(那是缓存,缓存必腐);不给 `_index.md` 加逐叶登记义务(在飞板保持线级手工策展,索引永不因叶子计划膨胀)。
- **开工前的定位动作,不是事后标签**:写新 plan 的第一步是给 `上级:` 填一个真实存在的家。**填不出来 = 上级还没认领这条线**,先回写上级(如统一执行计划的相应切片/线)再开工——G7 把这个动作从自觉变成强制。
- **查询视图**:`python3 .agents/tools/plan-tree.py` 打印全谱系树(带各档 status,`--orphans` 列未挂谱系的存量档),回答"某档在哪 / 某线推进到哪"。
- **消歧红利**:链上每级的名字来自上级文档本身,引用"阶段/切片"时自然带上坐标系(是 16 周主计划的阶段、还是某设计档的阶段),不再靠散文约定。

View File

@ -1,7 +1,7 @@
# 安全、合规、可靠性与一致性硬底线
> 本文是绘境AI 不可逾越的**红线**。涉及安全、合规、幂等、一致性、可靠性、可观测性与 SDK 降级。任何设计/实现违反即驳回。
> 蒸馏来源:`docs/architecture/架构/README.md`§3.4 SDK 降级 / §7.1-7.5 非功能 / §8 风险)、`docs/architecture/架构/README.md`§4.2 / §10.5 内容安全)、`docs/architecture/架构/13模块.md`compliance 模块 T-CMP-* 技术功能,含锁风门 Gate
> 本文是造梦AI 不可逾越的**红线**。涉及安全、合规、幂等、一致性、可靠性、可观测性与 SDK 降级。任何设计/实现违反即驳回。
> 蒸馏来源:`docs/architecture/系统概要设计-技术决策版.md`§3.4 SDK 降级 / §7.1-7.5 非功能 / §8 风险)、`docs/architecture/系统概要设计-开发团队版.md`§4.2 / §10.5 内容安全)、`docs/architecture/技术架构与模块.md`compliance 模块 T-CMP-* 技术功能,含锁风门 Gate
> 配套:编码/契约规范见 [`engineering-conventions.md`](engineering-conventions.md);元流程见 [`../workflows/ai-development-protocol.md`](../workflows/ai-development-protocol.md);事实蓝图见 [`../knowledge/product-and-architecture.md`](../knowledge/product-and-architecture.md);运行时/沙箱手册见 [`../skills/runtime-and-multichannel.md`](../skills/runtime-and-multichannel.md)。
---
@ -23,15 +23,12 @@
> **权限必须在可信边界(网关 + 服务端)强制,前端不是边界。** 匿名玩家只能浏览试玩,不能发布/收藏/进后台。
> **模型接入统一出口2026-06-22 纠正口径)**:所有 LLM 调用统一经 **new-api 网关**——client 的 baseUrl 指 new-api 端点、用 `NEWAPI_KEY` 驱动(统一计费 / 密钥 / 审计;密钥只走 env / `.env`,绝不入库)。**协议与 SDK 不锁**OpenAI 兼容(`/v1/chat/completions`、Anthropic 原生(`/v1/messages`thinking 分离)、厂商 SDK 均可,只要出口落 new-api、key 用 newapi-key。**作废旧表述**「唯一 OpenAI 兼容出口 / 绝不直连厂商 SDK」new-api 只是统一端点,不锁协议)。端点/key 见 [`docs/内网凭据与端点.md`](../../docs/内网凭据与端点.md)。
> **`@PermitAll` 公开读端点纪律P-OPN-08 实证)**:对外匿名可读端点须守三条——① **只暴露公开字段**RespVO 显式裁剪,绝不含 PII/内部态status / audit 列 / tenant_id / cost 等);如 creatorName 取昵称(同 feed authorName 源),非登录名/手机。② **DataPermission**:本仓 DataPermission 规则仅注册于 AdminUserDO/DeptDO系统 DeptDataPermissionRule业务表game_project 等)的「创作者只见己」是 Service/Mapper 手动 `eq(creatorUserId)`——故跨创作者公开读**天然不被数据权限拦截器过滤、无需 `@DataPermission(enable=false)`**(无规则可禁);但务必按「目标 id路径参」查、勿误用登录人。③ **租户 scoping**TenantBaseDO 表的 @PermitAll 读仍被 `tenant-id` 请求头 scoping——prodtenant.enable=true下**无租户头的匿名请求直接 BAD_REQUEST**(故此处「公开」=对带租户头的客户端公开,首方前端恒注 `tenant-id=1`);多租户时不可信头可枚举他租「已公开」数据。**真·租户无关的公开读**须 pin 租户(由资源 id 反解)或入 `tenant.ignore-urls`,并加双租户 staging 测试锁行为。
### 1.1 游戏沙箱具体配置runtime / compliance 共守)
- `iframe sandbox="allow-scripts allow-same-origin"`。**红线2026-06-10 审计补HJ-AUDIT-001**`allow-same-origin` 仅当游戏包托管于**独立源**usercontent 子域,与宿主不同源)时方可使用——同源下 iframe 可触宿主 DOM/存储、甚至自移除 sandbox独立源就绪前**禁用 `allow-same-origin`**。
- CSP`script-src 'self'; connect-src 'none'`**游戏内禁止任何网络请求**CSP 经游戏包托管侧 **HTTP 响应头**下发,不以 meta 标签为唯一依赖。
- **`'unsafe-eval'` 为受控代码执行面硬需2026-06-20 实证bug#2「部分生成游戏静止不可玩」根因)**生成游戏的逻辑gamedef behaviors / 或 agent 写的工厂代码)经 `new Function` 编译执行;**studio iframe srcdoc CSP`game-studio/src/host/inject.ts`)的 `script-src` 必须含 `'unsafe-eval'`**——缺它则 `new Function``EvalError`、被 runtime 静默吞进 `errors[]` → 世界永不演进(仅在 iframe CSP 下复现;顶层页/Node 无 CSP 限制故 false-negative 测不出)。**别当多余权限删掉**。eval 风险由三重边界承接:① 出厂前静态扫描拦危险面(禁 process/require/动态 import/fetch/eval/Function/DOM/网络/死循环);② `connect-src 'none'` 杜绝任何出站;③ 浏览器 iframe sandbox。下条「禁 innerHTML/eval」针对的是 **LLM 文案字段**,不否定受控 `new Function` 执行面。
- postMessage校验来源 origin 在白名单 + 消息 schema 校验,防伪造。
- **LLM 产物消毒**GameConfig 文案字段title/label/theme 等)入库前白名单字符集+长度校验,渲染侧一律转义后绘制,**禁止 innerHTML/eval**~~模板驱动后注入面=配置字符串~~ → **2026-06-12 主线改 agent 写码后,注入面扩大为「生成代码本体」**:文案消毒规则不变,代码面的安全=下一条沙箱边界+L2 信任边界七层HJ-GEN-001 §3.1〕,生成代码只许调插件公开 API、过验证门才出厂
- 资源总大小 ≤ 10MB首屏 ≤ 2MB~~创作者通过配置(非代码)驱动游戏,平台对运行时代码有完全控制权~~ → **生成主线=agent 写码HJ-GEN-001「平台完全控制」的实现改为代码生成于受控 harness、只依赖平台插件库公开面、CSP 沙箱 iframe 运行、验证门(含合规 schema+IP gate+取证导出)全过才发布**——控制点从「禁代码」迁移到「管代码的生成与出厂」。
@ -39,17 +36,9 @@
### 1.2 内容安全分层(自部署快检 + 商用兜底)
- 第一道:`safe-content-ai`(自部署)快检——免费、低延迟。
- 第二道:高风险样本送**阿里云内容安全**二次确认;文本/音频审核走阿里云 APIAI 输出的注入检测 + 输出 schema 校验落在生成侧门禁 + Prompt Registry 门(见 [`../skills/prompt-governance.md`](../skills/prompt-governance.md)**不依赖 DifyDify 降级远期、从未部署)**
- 第二道:高风险样本送**阿里云内容安全**二次确认;文本/音频审核走阿里云 APIAI 输出走 Dify 节点内置 Guardrails
- 阈值Nacos 配置):**block = 0.7(直接拦截)/ review = 0.4(送人工)/ pass < 0.4自动通过**
### 1.3 内网服务间回调安全(生成 worker 回调实证)
生成走「执行器派发 job → 外置 worker 产物 → 回调写库入 feed」时回调是免登录的内网 HTTP 路(仿 SmsCallback / pay-notify。三条红线缺一即被伪造或卡死
- **唯一写入路径,禁旁路**:任何生成来源(进程内执行器 / 外置 worker / 未来真第三方回写版本与产物一律复用同一个写库服务方法三表同事务task / version / runtime_packageputManifest 未命中即显式失败)。新增来源不得另开写库旁路。
- **免鉴权回调必自注入系统身份**`@PermitAll` 回调路在 web 线程无 LoginUser审计字段 creator/updater 取不到值会撞 NOT NULL、任务卡死。回调 controller 内必须自注入系统身份(`LoginUser(id=0, ADMIN)` 入 SecurityContext`finally``clearContext()`
- **服务间回调必 HMAC 验签**:仅靠「内网不可外达」兜底,等于任意可达请求都能伪造回调驱动落包。回调体按**原始字节** HMAC-SHA256 验签(`MessageDigest.isEqual` 常数时间比对)、**先验签错签直接 401、再反序列化**;两侧密钥与签名字节须逐字节一致。
---
## 2. 创作链路 7 道门禁
@ -143,7 +132,7 @@
| 格式 | JSON 结构化:`timestamp / level / trace_id / span_id / module / message / context` |
| 级别 | ERROR需人处理/ WARN需关注/ INFO关键链路/ DEBUG仅 dev |
| 脱敏 | Token / 密码 / 手机号 / 身份证 **脱敏后输出**(如 `138****1234`),禁止明文落日志 |
| trace_id | **网关入口注入,全链路透传**(含 new-api 网关、RocketMQ 异步 gen 队列、AgentScope 生成 Service 调用),调试模式贯穿生成→编译→加载→运行 |
| trace_id | **网关入口注入,全链路透传**(含 Dify / OpenGame 调用),调试模式贯穿生成→编译→加载→运行 |
| 保留 | ERROR/WARN **90 天**INFO **30 天**DEBUG 仅 dev |
**告警升级链**
@ -188,11 +177,11 @@ SDK 是平台能力注入 iframe 沙箱游戏的唯一通道,其稳定性直
| # | 风险 | 概率/影响 | 应对 | 降级方案 |
|---|---|---|---|---|
| 1 | LLM 调用不稳定(超时/限流/幻觉) | 高/高 | new-api 网关多模型热切换 + harness 门兜底重试 | 确定性 Fallback 生成器 |
| 1 | LLM 调用不稳定(超时/限流/幻觉) | 高/高 | Dify 内置重试+熔断 + 多供应商切换 | 确定性 Fallback 生成器 |
| 2 | 生成游戏质量不可控 | 高/高 | JSON Schema 强校验 + 可玩性自动测试 + 模板约束 | 质量不达标不入库 |
| 3 | 游戏沙箱逃逸 | 低/极高 | CSP + sandbox + 无网络 + postMessage 校验 | 检测异常立即销毁 iframe |
| 4 | ~~OpenGame 社区停更~~OpenGame 降级远期、MVP 从未部署,本行 moot | — | — | 现行生成主线 = agent 写码于插件库经 new-api无 OpenGame 依赖 |
| 5 | ~~Dify 版本升级不兼容~~Dify 降级远期、MVP 从未部署,本行 moot | — | — | 现行 agentic 编排 = AgentScope无 Dify 依赖 |
| 4 | OpenGame 社区停更 | 中/中 | fork 维护 + 核心 pipeline 可自维护 | 退化为纯 Dify + 模板生成 |
| 5 | Dify 版本升级不兼容 | 中/中 | 锁定版本 + 壳层隔离 | 自部署可控 |
| 6 | 广告联盟审核不通过 | 中/高 | 提前申请资质 + 内容合规前置 | 延迟广告上线,先做订阅/B端 |
| 7 | MQ 重复消费致数据不一致 | 中/高 | 消息幂等消费§3 | 定时任务修复 + 告警 |
| 8 | 分布式事务部分失败 | 中/高 | 最终一致性 + 补偿§4 | 中间态扫描 + 人工介入 |

View File

@ -1,11 +1,6 @@
---
name: add-business-module
description: "当在 game-cloud 后端新增一个 game-module 业务模块时使用:-api 契约 + -server 实现的双 Maven 结构、controller/service/dal 分层、错误码独占段、Flyway 迁移、两处装配、单测/集成测试与契约同步的标准步骤及踩坑表。"
---
# 新增业务模块操作手册add-business-module
> 蒸馏来源:`docs/architecture/架构/README.md`§2 后端模块地图 / §4.1 编码规范 / §11 模块开发 Checklist`docs/architecture/架构/README.md`§7.10 扩展性 / SPI
> 蒸馏来源:`docs/architecture/系统概要设计-开发团队版.md`§2 后端模块地图 / §4.1 编码规范 / §11 模块开发 Checklist`docs/architecture/系统概要设计-技术决策版.md`§7.10 扩展性 / SPI
> 适用:在 `game-cloud` 后端新增一个 `game-module-{name}` 业务模块(如 pay / trade / community / ip / biz / ad或后续新模块
> 配套:工程规范见 [`../rules/engineering-conventions.md`](../rules/engineering-conventions.md);可靠性/数据隔离红线见 [`../rules/security-and-reliability.md`](../rules/security-and-reliability.md);模块全景见 [`../knowledge/product-and-architecture.md`](../knowledge/product-and-architecture.md);契约先行见 [`./contract-first-development.md`](./contract-first-development.md);任务协议见 [`../workflows/ai-development-protocol.md`](../workflows/ai-development-protocol.md)。
@ -21,8 +16,8 @@ description: "当在 game-cloud 后端新增一个 game-module 业务模块时
## 前置
- 已读 [`../knowledge/product-and-architecture.md`](../knowledge/product-and-architecture.md),确认该能力**确实需要新模块**(多数 MVP 需求在既有模块加子包即可,见模块架构文档 §4 遗漏分析的判断方式)。
- 中间件MySQL/Redis/Nacos/RocketMQ已自托管 mini-infra连接信息见 [`docs/内网凭据与端点.md`](../../docs/内网凭据与端点.md);本地跑 Flyway 与集成测试的 Testcontainers 仍依赖 Docker
- 已在模块速查表(开发团队版 §2.2)与错误码段位表(开发团队版 §4.1)为新模块预留了**模块编号**与**错误码段**。(两版《系统概要设计》——"开发团队版"/"技术决策版"——已归档 `_archive`,节号按历史读;现行规范见 [`../rules/engineering-conventions.md`](../rules/engineering-conventions.md) 与 [`docs/architecture/README.md`](../../docs/architecture/README.md)
- 本地中间件已起(`docker compose -f deploy/docker-compose.middleware.yml up -d`),可跑 Flyway 与集成测试Testcontainers 依赖 Docker
- 已在模块速查表(开发团队版 §2.2)与错误码段位表(开发团队版 §4.1)为新模块预留了**模块编号**与**错误码段**。
---
@ -33,14 +28,14 @@ description: "当在 game-cloud 后端新增一个 game-module 业务模块时
```
game-module-{name}/
├── game-module-{name}-api/ # 契约层:可被其他模块依赖
│ └── src/main/java/com/wanxiang/huijing/game/module/{name}/
│ └── src/main/java/cn/wanxiang/game/module/{name}/
│ ├── api/ # Feign 接口(供别的模块同步调用)
│ ├── dto/ # 模块间传输对象 DTO
│ ├── enums/ # 枚举(状态/类型)
│ └── enums/ErrorCodeConstants.java # 本模块错误码常量(独占一段)
└── game-module-{name}-server/ # 实现层:只本模块内部使用
└── src/main/java/com/wanxiang/huijing/game/module/{name}/
└── src/main/java/cn/wanxiang/game/module/{name}/
├── controller/admin/ # 后台接口(运营/管理员,/admin/{name}/**
│ └── vo/ # admin 端 VOReqVO/RespVO/PageReqVO
├── controller/app/ # 产品端接口(创作者/玩家,/app/{name}/**
@ -50,7 +45,7 @@ game-module-{name}/
├── dal/dataobject/ # DO与表字段一一对应
├── convert/ # DO ↔ VO/DTO 转换器MapStruct
├── job/ # 定时任务(如对账/聚合/清理)
└── mq/consumer/ # RocketMQ 消费者(幂等消费)—— RocketMQ 已自托管 mini-infra单体内跨模块通知仍优先同进程 -api notify-push真跨进程/异步才上 RocketMQ(见下方坑表)
└── mq/consumer/ # RocketMQ 消费者(幂等消费)—— 远期/拆微服务才需RocketMQ MVP 未部署,单体内跨模块通知优先同进程 -api notify-push(见下方坑表)
└── src/main/resources/
└── db/migration/ # Flyway 迁移脚本 V{x.y.z}__{desc}.sql
└── src/test/java/... # 单元测试 + 集成测试
@ -69,7 +64,7 @@ game-module-{name}/
| 3 | `-server` 实现分层 | 按 controller(admin/app)→service→dal(DO+Mapper)→convert 落地MQ/job 按需加 | 单元测试覆盖 service |
| 4 | Flyway 迁移 | `-server``db/migration/` 下新建 `V{x.y.z}__{desc}.sql`,如 `V1.0.0__create_game_{name}.sql`;只新增不改旧文件 | `mvn flyway:migrate -pl game-module-{name}-server` 成功 |
| 5 | **装配=两处**(缺任一 compile/启动失败Wave4 实测) | **① root `game-cloud/pom.xml``<modules>``<module>game-module-{name}</module>`**(聚合定义,缺则 `mvn -pl game-module-{name} …` 报「找不到父 pom 聚合」);**② `huijing-server/pom.xml``game-module-{name}-server` 依赖**(单体把模块编进 JAR缺则启动无此模块、Swagger 无分组) | `mvn -pl huijing-server -am compile` 通过 + 启动后 Swagger 见分组 |
| 6 | 模块配置Nacos 已部署 mini-infra | 如有模块级配置(开关/阈值/外部 API keyNacos 已自托管 mini-infra模块级配置可入 Nacos 配置集;简单静态项也可走本地配置文件/环境变量。 | Nacos 控制台可见 / 配置项生效 |
| 6 | 模块配置(远期 Nacos | 如有模块级配置(开关/阈值/外部 API key**MVP 单体走本地配置文件/环境变量**Nacos MVP 未部署);拆微服务后再迁 `deploy/nacos/`。 | MVP配置项生效远期Nacos 控制台可见 |
| 7 | 单元测试Service | `XxxServiceImplTest` 继承 `BaseMockitoUnitTest`(纯 Mockito无需 DB/Docker照抄 project 的 `ProjectServiceImplTest`);需真实 DB 才用 `BaseDbUnitTest`(集成阶段)。**注意mock/verify `BaseMapper.insert/updateById``any(XxxDO.class)` 消歧,裸 `any()` 会因重载报错;`verify(...).updateById(argThat(...))` 须显式标 lambda 参数类型 `argThat((XxxDO d) -> …)`,否则同样重载歧义致 testCompile 失败Wave4 biz 实测踩坑)** | `mvn -pl game-module-{name}/game-module-{name}-server test` 绿 |
| 8 | 集成测试Controller+DB | `XxxServiceIntegrationTest`Testcontainers 自动起 MySQL/Redis覆盖核心 API | `mvn verify -pl game-module-{name}-server -Pintegration` 绿 |
| 9 | Swagger/Knife4j 验证 | 启动 `huijing-server`,开 `http://localhost:48080/doc.html` 确认接口与字段自动生成正确 | doc.html 可见新模块分组 |
@ -106,7 +101,7 @@ game-module-{name}/
- [ ] `-server` 中建 `controller/admin/` + `controller/app/` + `service/` + `dal/mysql/` + `convert/`(按需 `job/``mq/consumer/`
- [ ] 编写 Flyway 迁移 `V{x.y.z}__{desc}.sql`(只新增,不改旧迁移)
- [ ] **装配两处**root `game-cloud/pom.xml` `<modules>` 注册 `<module>` + `huijing-server/pom.xml` 引入 `-server` 依赖
- [ ] 模块级配置(如有):Nacos 已自托管 mini-infra可入 Nacos 配置集;简单静态项也可走本地配置/环境变量
- [ ] 模块级配置(如有):MVP 走本地配置/环境变量Nacos 为远期MVP 未部署)
- [ ] 单元测试Service 层)通过
- [ ] 集成测试Controller + DBTestcontainers通过
- [ ] SwaggerKnife4j`doc.html` 验证 API 文档自动生成
@ -132,4 +127,4 @@ game-module-{name}/
| `select *` / 大表无索引查询 | 慢查询、性能事故 | 必须指定字段;大表查询必须命中索引(开发团队版 §4.1 |
| 「运营赋值类」资金admin 赋余额/补偿)混进业务收益流水表 | 污染营收报表 gross/net 聚合 + 破坏对账锚点 | **另建专用流水表**trade U2`game_trade_grant`,独立 `uk_biz_no` 幂等行),账户余额仍走原子增(`balance += amount, total_income += amount` 守恒 `balance+frozen+total_withdraw=total_income`);流水写入 + 余额增同 `@Transactional`先写流水行uk 防并发)再增余额,余额增 0 行抛错回滚(禁「流水已记、余额未增」半态)。范本 `AccountServiceImpl.grant` |
| 「一用户一态」聚合域(订阅/会员)按每次操作建新行 | uk_user 撞键 / 状态分散难查 | **一行一用户 upsert**`uk_user`,首建 insert / 已有 CAS 续期);幂等键**内联在行**`last_grant_biz_no`,同 bizNo 不重复延长 = 重试安全),区别于流水域的独立 uk_biz_no 行;续期叠加 `新到期=max(now,旧expire)+时长`(不丢未用时长,已过期从 now 起算CAS`WHERE expire_time=读时快照`)防并发 lost-updatemiss 回查重试有限轮次(禁死循环)。范本 `SubscriptionServiceImpl` |
| 「有效期/过期态」依赖定时 job 物化 status 列 | 无 cron / 定时 job 未接时过期态不准 | **有效性以 `expire_time>now` 查询时实时回算**`effectiveStatus`/`active` 在 Convert 层算),落库 status 仅初始态 + 后续物化预留;不依赖定时 job 也能正确反映过期。范本 `TradeConvert.toSubscriptionVO` |
| 「有效期/过期态」依赖定时 job 物化 status 列 | 无 cronMVP RocketMQ/job 多为远期)时过期态不准 | **有效性以 `expire_time>now` 查询时实时回算**`effectiveStatus`/`active` 在 Convert 层算),落库 status 仅初始态 + 后续物化预留;不依赖定时 job 也能正确反映过期。范本 `TradeConvert.toSubscriptionVO` |

View File

@ -0,0 +1,157 @@
# 新玩法模板接入配方add-game-template
> ## 🛑 DEPRECATED2026-06-12 模板哲学终裁,整篇配方退役)
> **本配方所教的「新增一个可玩玩法模板」工作流已废除,勿照此为新作上新。** 现行裁决:
> - **玩法模板层废除**——模板 ≠ 玩法品类件;**模板 = LittleJS 能力插件/二次开发件**(粒子/物理/后处理等引擎能力包装层裁决①2026-06-12
> - **玩法 / 美术 / 关卡 / UI = agent 生成域**agent 写码于插件库HJ-GEN-001 终审);旧「模板驱动生成 / LLM 填参」线随之退役。
> - **好玩基线 v2 改挂评估门**(非模板属性)。
> - Tier1 引擎 = **LittleJS 增强发行版 + Runner v2**2026-06-12 终裁);旧「自研 Canvas<15KB / toString 注入 / 15KB 体积门 1.x 加载面β 退役
> - 旧 4 玩法模板clicker/merge/idle/tycoon+ 存量数据已清除W-CLEAN
>
> **新工作去向**:新增后端业务模块 → [`./add-business-module.md`](./add-business-module.md)(不动);新增引擎能力插件/生成链路 → 现行口径见 [`../knowledge/tech-decisions.md`](../knowledge/tech-decisions.md) §1.1、[`./runtime-and-multichannel.md`](./runtime-and-multichannel.md) 顶部横幅、[`../rules/build-vs-buy.md`](../rules/build-vs-buy.md)。
> **以下正文一律仅作 1.x 历史留痕**(保留 M-c 批①/批②实战收口痕迹与踩坑教训,不作现行执行依据)。
>
> ---
>
> 蒸馏来源:`docs/agent-specs/_archive/2026-06-10-Mc模板波-execution.md`HJ-MC-TPL-EXEC-001 §3§9 全链)+ M-c 批① merge 模板实战收口2026-06-10五级验收门全过构建/体积/契约门+真玩五AND双模板+Golden v1.1.2 PASS+校准批 accept 10/10+金丝雀10条入feed途中逮修三真缺陷见各节红框
> 适用(~~已退役,见顶部横幅~~):给「模板驱动生成 + agent 闭环 QA」生产链**新增一个可玩玩法模板**merge/idle/tycoon 类纯 Canvas Tier1 玩法覆盖契约→prompt→runtime→后端→编排器→五级验收门全链。
> 配套:新增后端业务模块(非玩法模板)见 [`./add-business-module.md`](./add-business-module.md);生成链路现实态见 [`./ai-generation-pipeline.md`](./ai-generation-pipeline.md) ⓪ 节;契约先行见 [`./contract-first-development.md`](./contract-first-development.md);运行时/SDK 见 [`./runtime-and-multichannel.md`](./runtime-and-multichannel.md)Prompt 治理见 [`./prompt-governance.md`](./prompt-governance.md);可靠性红线见 [`../rules/security-and-reliability.md`](../rules/security-and-reliability.md)。
---
## 目标
把一个新玩法如「合成升级」merge从**契约**接到**可玩+可验收+可入 feed**:新增一份 GameConfig schema + 一个 designer prompt + runtime 一个玩法分支 + 后端模板白名单一行 + 编排器一条玩家策略,全程守住「注入 LLM 文本 == 服务端校验文本 == judge 校验口径」的**同源铁律**与既有发布链/数据回路/鉴权红线零改。**产出后该模板可生成、可真玩通关、可入 feed 被玩家试玩。**
> **样板**merge 是已五级验收门全过的活样板schema=`contracts/templates/merge.schema.json`designer=`contracts/prompts/04-config/merge-designer.md`runtime 分支=`game-studio/src/host/runtime/index.ts``initMerge`,编排器策略=`player_cdp.py` 的 merge 拖拽分支)。照抄它即可。
> **非目标红线**dodge/runner/match 三份 schema 仅契约落盘未实现,**不据此宣称可玩**;不动发布链(生命周期事件名 / `game_end{score,completed,duration_ms}` 契约行 / 遥测 / feed 排序)、不动鉴权、不动 demo 兜底包。
---
## 前置
- 已读 [`./ai-generation-pipeline.md`](./ai-generation-pipeline.md) ⓪ 节(生产路径已切「模板驱动生成 + agent 闭环 QA」对齐编排器三件位置`docs/agent-specs/2026-06-09-agent-loop-v1/orchestrator/``run_batch.py` / `player_cdp.py` / `judge.py` / `prompts.py`)。
- 重型构建/测试一律走 **mini-desktop**git push→pull 同步,本机严禁 `mvn`/`npm build`,记忆 `internal-build-infra-servers`);前端构建必须 `npm run build -- --mode staging`(缺 staging env → mock 接管 → 静默 demo 兜底)。
- 与在飞波次的**文件级共改**先排程:若另一波已改 `game-module-aigc-server/pom.xml``service/task/AigcTaskServiceImpl.java`(如鉴权波 PlayerApi seam本波在其合入后开工开工前 `git pull`/`rebase` 取最新基线(执行版 §11.1)。
---
## 七步配方
### 步骤一 · schema 定稿三招(契约先合入,再动代码)
新建 `contracts/templates/<t>.schema.json`,逐项对齐 `clicker.schema.json` 风格(`$schema` draft 2020-12 + `$id` + 中文 description + 数值带 `minimum`/`maximum`)。三招守门:
1. **`templateId``const` 锁死**(如 `"const": "merge"`)——精确等值防 LLM 跨模板串台,比 `enum` 更严。
2. **顶层 `additionalProperties: false`**——守住「字段不增不删」LLM 不得自由发挥加字段。
3. **跨字段约束 schema 表达不了的,用双兜底**JSON Schema draft 2020-12 无 `$data`**无法用一字段值约束另一字段**(如 merge 的 `targetLevel ≤ chainLength``itemLabel` 数组长度 = `chainLength`)。处置 = ① **prompt 正文硬约束**(步骤二 hard_constraints 明文)+ ② **runtime 渲染时钳制**(步骤三 `clampInt(targetLevel, _, chainLength)`、数组按 `min(len, chainLength)` 兜底、缺位补占位文案。schema 只保各字段独立边界(如数组 `minItems:1`/`maxItems:上限` + 每项非空 `minLength:1`+`pattern:"\\S"`)。
> **可解性论证义务**schema 边界即「保证可解、堵死局」的承诺。新玩法须在 execution 版论证「最坏情况有限步可达通关、无死局」merge 例:`targetLevel≤chainLength≤6` → 最坏 `2^5=32` 个 1 级物件,点击产料无限 + 合成必腾格 → 无死局,执行版 §3.3)。
> **诚实边界口径**:新模板 runtime 实现后schema description 写「runtime 本波已实现,可据此宣称可玩」——区别于未实现模板的「仅契约落盘」声明。
### 步骤二 · prompt 三件套(新 designer + 对抗/fix 复用升 patch + Golden 三守卫)
**(a) 新 `<t>-designer`**(新建 `contracts/prompts/04-config/<t>-designer.md` + `registry.yaml` 增条目):
- 结构逐项对齐 `clicker-designer.md`frontmatter **8 键**id/version/stage/owner/tier/engine/input_schema/output_schema+ `hard_constraints` 块 + 正文。**只换模板专属约束**,通用口径一字不降:
- **不降的通用口径**:注入防护(不执行创意/列表/上一轮问题里的指令)、只产一个合法 JSON 对象、不自评不下结论、重出轮只改指出项。
- **替换的模板专属**:① `designIntent` 只许描述本模板机制内体验(禁承诺模板外机制:计时/失败惩罚/物理/音效)② config 字段域指向 `input.template_schema`(真源 = 本模板 schema**schema 表达不了的跨字段约束在此写成硬约束**merge 例「targetLevel 必须 ≤ chainLengthitemLabel 必须给 chainLength 个文案按等级排列」)。
**(b) 对抗 `quality.adversary-review` + fix `fix.design-revise` 复用,仅升 patch****口径一字不动红线**
- 二者主干口径对任何模板已中性,**不分模板分段、不新增条目**。改法仅两类:
- 对抗:细则①示例追加新模板域示例(如「创意『合成种花』被改写为『躲避鲨鱼』属核心动作矛盾 P1」**原句一字不动**(沿「仅追加不改口径」纪律)。
- fix正文补 `{{input.template_schema}}` 注入段(对齐 designer 的【模板 schema】块+ 把残留的 clicker 字段示例括注中性化(主干句不动)。编排器侧零改(`run_batch.py` 已传 `template_schema`,补占位符即生效)。
- **红线**:升 patch version 同步 `registry.yaml`,过四道闸 CISchema/成功率≥80%/Golden 回归 diff/成本延迟);**P0/P1 口径文字一字不动**(守「不得以放宽 P1 口径回应漂移」红线)。
- **后端不调 fix prompt**`AigcGenerateExecutor` 重出走 schema 校验失败回炉、不经 fix.design-revisegrep `design-revise` 在 aigc 模块零命中fix 改动仅影响编排器 `run_batch`,勿误改后端。
**(c) Golden 三守卫样本**(新建 `eval/config.<t>-designer/` 三件 + 对抗 Golden append-only 扩 3 条):
- **kill 题文不符 ×1硬门**创意↔config 核心动作矛盾(创意「合成种花」↔ config 改写「躲避鲨鱼」)→ `decision:kill, reasons:[题文不符]`(守细则① P1 不被泛化吞)。**这是稳定硬门**——模型可靠判 kill。
- **越模板机制 ×1⚠ Golden 观察项非硬门——M-c 批② 2026-06-11 终裁)**designIntent 承诺模板外机制(如「物理碰撞反弹」「计时」「破产扣分」)但 config 自洽 → labels.jsonl 标 `decision:accept, reasons:[越模板机制P2放行]`(细则① :60「越模板 config 自洽=P2 放行不拦截,模板级表达力限制非缺陷」=文档化口径目标)。**但 Golden harness 必须把此守卫降为观察项**golden-set `expectation:observe`、harness `_observe()` 恒记录实判**不纳入 verdict**)——实证:越模板是软语义边界,同输入温 0.2 重采样**抖动**M-c 批② idle 2/4 判 kill、merge/tycoon 0/4P1/P2 两向硬断言都随采样抖,硬门必 flaky。两向 fail-safeP2 不拦发布/偶发 P1 仅多一次 HITL
- **accept 正例 ×1硬门**:题文自洽 + 数值合规(跨字段约束满足)→ `decision:accept`。**稳定硬门**——模型可靠不误杀。
- **🔴 Golden verdict 只由硬门裁定 = 题文不符 kill ∧ accept 不误杀(+ 既有模板守卫不回归);越模板守卫只观察不门禁**。**通用红线软判定P2/非阻塞)绝不可做单样本硬 Golden 门**——温 0.2 仍 50/50 抖M-c 批② 实锤。labels.jsonl 行只写 plain `decision`(行内禁造 `doctrine` 字段);越模板恒按细则① P2、无版本门控旧「doctrine 双标签 v1.1.2→kill」叙事已随终裁废止。蒸馏全程见 `docs/agent-specs/_archive/2026-06-10-Mc模板波-批②idle-tycoon-execution.md` §4.5 二次勘误 banner + `eval/quality.adversary-review/README.md` 2026-06-11 两条。
### 步骤三 · runtime 单工厂分发(`game-studio/src/host/runtime/index.ts`
**单 `startRuntime``switch(pkg.templateId)` 分发,绝不开 ×N 个工厂**——一份注入面、一份生命周期/遥测红线代码、theme/资源/`game_end` 只一处,杜绝 ×N 漂移。
1. **分发骨架**`const templateId = typeof pkg.templateId === 'string' ? pkg.templateId : ''``switch``case '': case 'clicker':`**空串兼容旧包**,守存量内容池零影响)→ clicker`case '<t>':` → 新玩法;`default:`**`game_error` 防御分支**`sdk.reportError` + `life('game_error',{message:'unknown_template',templateId})` + `return {destroy(){}}`,不静默错渲染)。
2. **新玩法 `init<T>()` 函数体内自包含**:因 `startRuntime``toString()` 注入沙箱,所有新函数**必须定义在 `startRuntime` 函数体内、不引模块外符号**config 读取用函数体内纯函数 `clampInt`/`cleanLabels`(无可抛路径,沿 `cleanText` 范式)做缺省/越界钳制(步骤一兜底②落地点)。
3. **生命周期/finish 单一来源、参数化 score**`life('game_loaded'/'game_start'/'game_end')``finish()` 红线代码**不复制**——新玩法通关时调同一 `finish()`,仅参数化 `score` 来源(如 clicker=点击数、merge=合成次数);`game_end{score,completed,duration_ms}` 契约行零变化。
4. **体积门 + 契约 grep改后必跑**
- 体积:`node /tmp/measure-runtime.cjs dist/assets/GamePlayer-*.js`mini-desktop:/tmpmd5 `e6608a77520d560cd4fb29e2fb8d3481`)提取 startRuntime(min) raw——**硬线 < 15,360B**恒定红线**软门当前 8,192B**M-c merge 单玩法逼近原 4,096B 软门已上调加新玩法再评)。前后差值写交付记录
- 契约 grep`grep -c 'duration_ms'` 计数只增不减;`git diff` 不含 clicker 分支 `game_end`/`game_loaded`/`game_start` 任何删改;`grep -rn 'innerHTML\|document\.'` 零命中(禁 DOM玩法纯 Canvas`grep -P '[\x00-\x08\x0b\x0c\x0e-\x1f]'` 零控制字节。
### 步骤四 · 后端四点 + pom`game-module-aigc-server`
`PromptResourceLoader` / `GameConfigSchemaValidator` 从「单模板常量」改「templateId→资源 Map」守同源铁律零分叉。四点 + pom
1. **`PromptResourceLoader`**:资源路径常量 → `Map<templateId, (promptVersion,promptBody,schemaText)>`,构造时逐模板装载缓存;`render`/`getTemplateSchemaText`/`getPromptVersion` 全加 `templateId` 参数;某模板资源缺失只禁用该模板不污染其他。**新模板 = Map 加一行**。
2. **`GameConfigSchemaValidator`**:构造入参 `String``Map<templateId,schemaText>`,逐个编译缓存 `Map<templateId,JsonSchema>``validate(configNode)``validate(templateId, configNode)`。校验逻辑本身零新码(仍 `schema.validate(node)`),守「不手写字段校验、不双源」精神。
3. **`AigcExecutorProperties.supportedTemplates`** 白名单常量加项(`List.of("clicker","merge",...)`+ `AigcExecutorConfiguration` Bean 装配传 Map + `AigcGenerateExecutor` 调用点全部按 `task.getTemplateId()` 取参。
4. **`AigcTaskServiceImpl`**`getTemplateList()` 加一条templateId/name/description/examplePrompt`validateTemplateExists()` 接**同源白名单**(与 `supportedTemplates` 同一来源,避免「提交时通过、执行时 no_template_match」割裂用独立配置/常量不强依赖 executor `@ConditionalOnProperty` 装配)。
5. **pom maven-resources-plugin —— 已一次性解决,勿再手改**`pom.xml``copy-wanxiang-contracts` includes 已改通配 `prompts/04-config/*.md` + `templates/*.schema.json`(实读 :138-139**新模板两资源自动进 classpath**。曾踩坑:原 includes 仅复制 clicker 两单文件 → 新模板资源静默漏复制 → `readClasspathResource` 返 null → 该模板自禁用。现已通配,**新增模板无需再动 pom**。
> **测试随生产签名同步重写、不得删测**同源铁律的回归守卫单值→Map 改造后,`GameConfigSchemaValidatorTest`/`PromptResourceLoaderTest`/`AigcGenerateExecutorTest` 旧签名调用点全部编译失败——必须随之重写、`validate`/`render`/`getTemplateSchemaText` 全加 `templateId`、**保留原断言语义**(不放宽、不删用例规避编译错),`mvn -pl game-module-aigc/game-module-aigc-server test` 全绿。
### 步骤五 · 编排器参数化run_batch / player / judge
1. **`run_batch.py --template` 已通用**`--template`default=clicker保既有调用零改+ `--prompt-designer`default 按 template 推导 `config.{t}-designer`TEMPLATE_ID/schema 路径自动联动。
2. **player 按 `(templateId, config)` 分发,新增 `_play_<t>_once` 策略**`player_cdp.py``play`/`_play_once` 改按 `(templateId, config)` 接参,内部分发到各模板策略(避免每加模板改形参)。新玩法策略用 **config 字段 + iframe 几何 + 布局公式推导确定性帧序列**(取证纪律:真实输入事件、禁 evaluate 篡改/读游戏内部状态,仅读布局几何/文案/自注入缓冲。merge 例 = `_dispatch_drag`CDP `Input.dispatchMouseEvent` press-move×N-release按「点满料→逐对合成」推进**降级预案**:拖拽通关率 <60% 点选两格合成」(runtime+player 输入层同步小改玩法/schema/prompt 不动)。
3. **judge 子集若引入新 JSON 形态须先补分支 + 红样本单测**`judge.py``validate_config_against_schema` 覆盖 JSON Schema 子集,**新形态须先补**——merge 引入 `array` 即在 integer 分支后补 `elif expect_type=="array"`minItems/maxItems + 逐元素套 items 子 schema补后口径 == 后端 networknt SchemaValidator同源越界单测含该维度红样本如 array 空数组/非字符串项/超上限三条)。
> **🔴 实战逮修缺陷 ②(模板专属字段一律 `.get`/分流取参)**`design["config"]["target"]`**clicker 专属直取**merge schema 无 `target`,直取 KeyError 整批熔断)。两处实证:① `run_batch.py:772` 附近 `design_target=design["config"]["target"]`spec 点名修,按 templateId 分流取参)② `run_batch.py:735` 附近 `_verify_package``expected_target=design["config"]["target"]`**spec 枚举漏列、merge-cal-10 首跑实测熔断逮获**,修法改 `.get("target")`commit 55bcdf6。**配方铁律:模板专属字段一律 `.get` 取或按 templateId 分流,绝不裸下标直取——逐处 grep `design["config"]["` 排查。**
### 步骤六 · 承重接口纪律runtime ↔ player 共享公式逐字符对齐)
runtime 与 player 若共享一套公式(如 merge 的棋盘布局:`cols=ceil(sqrt(boardSize))``rows=ceil(boardSize/cols)`、格中心 `cellCenterInternalX/Y` 几何式),该公式即**承重接口**runtimecanvas 内部像素算)与 player按比例映射 `cssX=rect.x+(cellCenterInternalX/W)*rect.w`,规避直读 dpr**必须字面同一套,全部参数钉死**——含 `topPad` 这类散文易漏项。**收口时主 agent 逐字符比对两侧公式**,任一侧改公式即破取证。
### 步骤七 · 取证环境三铁律 + 五级验收门
**取证环境三铁律**(批跑前置,缺一即静默 demo 兜底、五条 AND 全假绿):
1. **`--frontend http://localhost:4173`**:宿主 manifest 校验用 `crypto.subtle`仅安全上下文localhost可用走 IP 访问永走 demo 兜底。
2. **`npm run build -- --mode staging`** 构建 game-studio缺 staging env → mock 中间件接管 → 宿主静默 demo 兜底。
3. **🔴 实战逮修缺陷 ①(取证导航前注 localStorage 登录态)**:鉴权波 `/create/preview` 路由 `meta.requiresAuth=true`,前端守卫仅认 localStorage `wanxiang_token` 存在性——**取证导航前须先注入登录态**,否则被守卫重定向 `/login`、状态条永不出现M-c 级2④ 实测逮获commit 831c9cb。修法 = 导航前 `Page.addScriptToEvaluateOnNewDocument``localStorage.wanxiang_token="test1"`**`player_cdp.py` 已内置** `UI_LOGIN_TOKEN`:103-104:747-752 先于页面脚本注入),新模板取证直接复用。
4. 批跑前关执行器互斥窗:`AIGC_EXECUTOR_ENABLED=false` 重启(避免执行器与编排器对同一 staging 任务源双跑烧钱),批末还原。
**五级验收门顺序表**(精简自 execution 版 §9逐级全过才算 done
| 级 | 动作 | 通过判据 |
|---|---|---|
| 1 入册 | schema 落 `contracts/templates/` + registry 增条目 + 后端 Map 接入 + judge 补分支单测 + 三测试同步重写 + 新模板两资源 classpath 非 null + aigc-server 构建绿 | judge 补丁+三测试重写全绿 + 资源 classpath 非 null + 构建绿 + registry/schema/后端三处同源(注入文本==校验文本grep 比对) |
| 2 真玩 | runtime 分发改本机→镜像 mini-desktopmd5 对账)→ `build --mode staging` 绿 + 体积/契约门 + 落一个新模板包 + player 策略五条 AND 全绿 + **clicker 回归同窗五绿** + 浏览器实玩截图 | 新模板五 AND + clicker 回归五绿 + 体积/契约门全过 + 截图留档 |
| 3 QA 闭环 | 对抗 Golden 扩三守卫 + Golden 回归跑kill 守卫被判 kill、accept 不误杀)+ 批内查重对新模板生效 | 守卫样本全中 + 查重生效 |
| 4 校准批 | **10 条校准批**全自动流完零 infra 中断 | 全流完零 infra**accept 不设硬门、不计 M2 口径**M2 ≥80% 已由 clicker 收口、不受本波回退影响accept 为校准观测值 |
| 5 入 feed | 金丝雀 **≤10 条**直发入 feed + 浏览器实玩通关截图 | feed 可见新模板卡片(走 meta.title 不感知模板差异)+ 实玩通关截图 |
> **校准批口径**(执行版拍板 310 条校准批不设硬门(暴露系统性问题则 designer 升 minor 加约束、对抗/fix 误判则升 patch过四道闸再开第二轮收敛后 **20 条正式批 accept ≥80% 量级**验收。新模板冷启动无老模板积累,首批 accept 可能落 50-70%(预期管理,非承诺)。
> **边界失败路径**CDP 超时/通关率不达归 **infra 信号**(不混入 accept 分母);未知 templateId 灌包走 `game_error` 防御config 越界由后端 SchemaValidator 拦截(两轮重出)+ runtime clampInt 兜底纵深。
---
## 回滚
- **runtime 分发回退**:改动集中在 `runtime/index.ts`switch + init<T> + 防御分支),还原单文件重建重部署即回退(无数据/契约迁移,低代价)。
- **白名单摘除即回退**`supportedTemplates` 去掉该模板 → 生成请求走 `no_template_match` 失败桶getTemplateList 去该条 → Create 页不展示。后端 Loader/Validator Map 保留新模板资源无害(不被认领即不用)。
- **feed 零影响**feed 卡片走 `meta.title` 不感知模板差异;已入 feed 的金丝雀走既有下架编排;存量内容池零影响(空串 templateId 兼容 + 回归五绿)。
- **prompt 回滚**:新 designer 是新增条目,去 registry 条 + 删 md 即回退;对抗/fix 的 patch 改动若引回归revert 到前一 version版本化双标签 Golden 守卫可定位回归)。
---
## 常见坑(实战收口版)
| 坑 | 后果 | 正确做法 |
|---|---|---|
| 模板专属字段裸下标直取 `config["x"]` | 新模板无该字段 → KeyError 整批熔断merge-cal-10 实测) | 一律 `.get` 或按 templateId 分流取参;逐处 grep `design["config"]["` 排查(缺陷②) |
| 取证导航前未注 localStorage 登录态 | requiresAuth 守卫重定向 /login状态条永不出现、五 AND 全假 | 导航前 `addScriptToEvaluateOnNewDocument``wanxiang_token`player_cdp 已内置,缺陷①) |
| 走 IP 访问 / 漏 `--mode staging` | crypto.subtle 不可用 / mock 接管 → 静默 demo 兜底、AND 全假绿 | `--frontend localhost:4173` + `build --mode staging`(取证三铁律) |
| 开 ×N 个 runtime 工厂 | 生命周期/遥测/game_end 红线代码 ×N 漂移 | 单 startRuntime 内 switch 分发,红线代码单一来源、参数化 score |
| 新玩法函数引模块外符号 | toString() 注入沙箱后 ReferenceError | init<T> 全部定义在 startRuntime 函数体内、纯函数自包含 |
| schema 跨字段约束硬塞 schema | draft 2020-12 无 $datajudge 子集不支持,双源漂移 | prompt 硬约束 + runtime 钳制双兜底schema 只保单字段边界 |
| 改生产签名不同步重写测试 | 编译失败,或删测规避破坏同源守卫 | 三测试随签名同步重写、保留原断言语义、不删用例 |
| judge 未补新 JSON 形态分支 | 数组/新形态字段静默不校验,与后端口径分叉 | 先补 judge 分支 + 红样本单测,再开批(与后端 SchemaValidator 同源同判) |
| 把 M2 ≥80% 口径套到新模板校准批 | 冷启动新模板首批必红、误判回退 | 校准批不设硬门不计 M2正式批 ≥80% 量级M2 由 clicker 收口不受影响 |
| 对抗/fix 改示例时顺手动 P1 口径 | 漂移、违「不得放宽 P1 口径」红线 | 仅追加示例升 patchP0/P1 口径文字一字不动,过四道闸 |
| 把软判定(越模板机制 P2/非阻塞)做成单样本硬 Golden 门 | 同输入温 0.2 仍 50/50 抖M-c 批② idle 2/4 实锤Golden 随机红/绿、误导口径 | 软判定降 Golden 观察项(`expectation:observe`+`_observe()` 不纳入 verdict硬门只留稳定的题文不符 kill + accept 不误杀 |

View File

@ -1,88 +0,0 @@
---
name: agentic-amodel-generation
description: "当运行或调试 amodel-gen 底层 ReAct 工具循环(read/write/list/check/build/done + 循环外 play)、或排查便宜档 cheap-worker shell-out 复用的 gen.mjs 工具面时使用:harness 文件职责、mini-desktop 运行配方、九条踩坑红线。"
---
# skill:agentic A-model 游戏生成 harness(ReAct + M3,已实证)
> **一句话**:给模型 read/write/list/check/build/done 工具,让它**自己读 skill→读插件 api.d.ts→读范例→写多文件 LittleJS `src/`→自查 check/build→done**,harness 再跑循环外 play 出"能跑能玩"判定。**这是底层 A-model harness 手册(`gen.mjs` 及工具循环)——现行被便宜档 `cheap-worker`AgentScope/Pythonshell-out 复用**reframe 前定位为「A-model 生成的生产形态」;旧 gamedef/factory 单次产线已废)。
>
> 配套:code 层手册 [`littlejs-game-dev.md`](littlejs-game-dev.md) · design 层 [`sim-business-game-design.md`](sim-business-game-design.md) · 设计权威 `2026-06-21-agentic-amodel-generation-design.md`(已删,git 定位:`git show 8ea97234:docs/agent-specs/2026-06-21-agentic-amodel-generation-design.md`)。旧 [`cheap-model-game-generation.md`](cheap-model-game-generation.md) 的 gamedef/worker-loop 路对 A-model **已退役**,只看其通用纪律。
---
## 1. 何时用
- 要让一个模型(默认 **M3**,经 new-api)从一句 brief **自动生成一款可跑可玩的多文件 LittleJS 小游戏**;
- 验证某模型/某 brief 的生成可达性;
- 作为便宜档 `cheap-worker`AgentScope的底层工具循环被 shell-out 复用旧定位「SAA 生成图 `generate` 节点进程外 spike」随 SAA 降最低优先级远期化)。
## 2. 链路(agentic generate 内循环)
```
scaffold(clone _template → games/amgen-<id>/)
→ ReAct 循环(M3 @ new-api,openai 协议 + function-calling):
system+brief → M3 发 tool_calls → harness 执行 → 回喂 → 循环
工具:read_file/list_dir(repo 只读)· write_file(scoped)· check · build · done
done 门:check 与 build 都 PASS 才接受(harness 复核)
→ play(循环外·权威门:serve + headless Chrome boot + 帧推进 + 截图 + 交互 = 能跑能玩)
→ verdict.json + run-log/run-summary + 截图
```
四层(自上而下,设计文档详):**L1 env**(mini-desktop 沙箱)→ **L2 harness**(本 ReAct)→ **L3 context**(agent 经 read_file 按需自取 skill/api/范例,不预塞)→ **L4 prompt**(薄,指 agent 读真 skill,**绝不手抄**)。
## 3. harness 位置 + 文件
`game-runtime/tools/amodel-gen/`(node,零三方依赖——Node22 全局 fetch/WebSocket):
| 文件 | 职责 |
|---|---|
| `tools.mjs` | scaffold(clone `_template`,删 dist/test)+ 5 工具 read/write/list/check/build;check=`node --check`+五法/导出名/红线 lint(去注释串再扫) |
| `m3.mjs` | new-api M3 客户端(openai+tools,超时/重试;key:env 优先、`docs/内网凭据与端点.md` 兜底) |
| `prompt.mjs` | L4 薄 prompt + 工具 schema(指 agent 读真 skill) |
| `play.mjs` | 循环外权威门:CDP(全局 WebSocket)boot+帧推进+截图+`do(start)`/tap+读 `__gameHost.state()` |
| `gen.mjs` | orchestrator:scaffold→ReAct(compaction+loop-detection+done 门)→play→落证据 |
## 4. 运行配方(mini-desktop)
```bash
# 同步 harness(Mac→mini)
rsync -az -e "ssh -o ProxyCommand=none" game-runtime/tools/amodel-gen/ \
root@100.64.0.7:/root/game-staging/repo/game-runtime/tools/amodel-gen/
# 跑一款(端口两两错开,避开 live 48080)
ssh -o ProxyCommand=none root@100.64.0.7 'cd /root/game-staging/repo/game-runtime && \
NEWAPI_KEY=sk-... node tools/amodel-gen/gen.mjs --id <id> --brief "<玩法一句话>" \
--max-steps 45 --port 4321 --cdp 9223'
# 证据:games/amgen-<id>/evidence/{run-summary.json,run-log.txt,first-paint.png,after-play.png}
```
> 控制组(无 agent,验管线):`node tools/amodel-gen/tools.mjs scaffold ctrl1 && ... build ctrl1 && node tools/amodel-gen/play.mjs ctrl1 4320 9222`
## 5. 关键坑 / 红线(踩过的,务必守)
1. **write 路径必须含 `game-runtime/` 前缀**(所有工具路径 = repo-根相对)。prompt 里 game 目录写 `game-runtime/games/amgen-<id>/`,与 read/write 同源——**少前缀 → 写全被拦 → agent 原地打转**(mole2 实证)。boundary 报错要回显正确允许路径。
2. **token compaction 必须做**:全历史每步重发会爆炸(2.3M token!)。`gen.compactHistory` 折叠旧的大 tool 结果 / 旧 write 内容为占位(保留最近 ~8 条)→ **降到 ~200K**。文件在磁盘,agent 需要可重新 read_file。
3. **停机纪律**:① scaffold **删 `test/`**(去掉"写测试"诱因)② prompt 明示"check+build 绿就**立即 done**、**没有 edit_file**(用 write_file 整覆盖)、别写测试/额外文件" ③ 双绿后 harness **一次性催 done**。缺它 → agent 绿了还乱写测试耗尽步数(mole1 实证,45 步未 done)。
4. **check 加"game.js≠`_template`"防假绿**:写路径错→没覆盖→check 会在**未改的模板**上 PASS,蒙混过关。
5. **`update(dt)` 必调 `opts.runtime.bundle.tick(dt)`**:否则插件 `onFrame` 不推进、timer 永不到期、一局不结束(check 已硬拦)。
6. **boot envelope(沿用 `_template`)**:全局名 `TemplateGame` + `buildTemplateHostConfig`(host-config 导出名固定,别改)+ 标记 `__gameBooted/__gameHost/__gameForensics`;play 用 `?engine=real`;build `--global-name=TemplateGame`
7. **路径深度**:游戏落 `games/<id>/`(直接在 games/ 下,与 catch-fruit 同深度)→ `src/*.js` import 插件用 `../../../src/`(3-up)。嵌更深一层会变 4-up,esbuild 解析失败。
8. **固定 plumbing 不让 agent 碰**:`index.html`/`entry.js`/`src/main.js`(scaffold 自带);agent 只写 `src/{game,core,render,host-config,assets}.js`
9. **shell-out 集成:调用方 validate 必须尊重子工具的 ok 判定,不能只查产物存在**(批B Fix A 实证)。`gen.mjs --mode saa` 的 done-gate 顺序是 **stage→smoke**:smoke 失败 done 被拒,但 stage 已把**陈旧坏 bundle 落盘**(harness 返 `ok:false/stagedDir:null` 却留了文件)。调用方 validate 节点(shell-out 方,如现行 cheap-worker、旧 SAA 图)若只断言 `_wg1-gen/<id>/bundle.iife.js` 存在 → 误过坏件 → 路由 play → 九门必崩 + 白耗一次 ~10min 真玩。修法:generate 把 harness ok 写进 state(`K_AMODEL_GEN_OK`),validate **先尊重该标记**(false→直接救场、不读陈旧产物);且 generate **不自增 failCount**(留 validate 统一计一次,与 factory/gamedef「generate产出/validate计数」同口径,否则双重计数提前 escalate/giveup)。
## 6. M3 实证(2026-06-21,HJ-AGI-003)
| run | 玩法 | steps | tokens | check/build/done/play |
|---|---|---|---|---|
| mole1/mole3 | 打地鼠 | 14 | 200K | 全 ✓(mole3 自闭环) |
| dodge1 | 陨石躲避(移动+碰撞+提速) | 13 | 175K | 全 ✓ |
| react1 | 反应点击(寿命靶+save-progress) | 22 | 378K | 全 ✓ |
**3/3 跨玩法干净自闭环**,截图实锤可玩。M3 tool-calling 经 new-api openai 协议快(~2s/步、不触慢思考)。bar=**能跑能玩**(暂丢九门;九门是旧管线的,A-model 验证另议)。
## 7. 主流定性(设计 §0.8)
整体 = Anthropic taxonomy 的 **orchestrator workflow + 内嵌 agentic(ReAct)+ evaluator-optimizer(repair)**,属"受约束 agentic"——主流且被推荐用于可预测生产管线。已采纳"循环内便宜快反馈(check/build)+ 循环外昂贵权威验证(play)"分层。**按需补的缺口**:agent scratchpad、更强的长循环压缩、play 失败→repair 回环(spike 暂线性 done→play)。
## 8. 现行集成与状态(指针)
reframe 后本 harness 的工具循环由便宜档 `cheap-worker`AgentScope/Pythonshell-out 复用便宜档生成质量已收口M1 达标门三品类各 5/5 过九门、richness 加固不跌破,见 [`../knowledge/tech-decisions.md`](../knowledge/tech-decisions.md) §5.1旧「Phase4 cutover 删 gamedef/factory」路径随 gameDefinition 废除、A-model 直写真 `src/` 而 moot。当前构建态另见 memory `amodel-generation-build-state`2026-07-09 勘误:「过九门 生成质量已收口」的口径已被裁定三收窄——过九门自此只算机械预筛通过玩法层验收另需独立模型玩法判定便宜档真实率按新基线重锚W-AXIS 收尾波当日审计并证「richness 非阻塞旁路」是病根之一,所引 §5.1 条目已同步补注。见质量 SoT《游戏质量与爆火能力》裁定三。

View File

@ -1,191 +0,0 @@
---
name: agentic-seat-context-design
description: 设计或评审生成运行时的 agent 席位划分、context 装配、prompt 结构、工具面、多 agent 协作、goal-loop 自治交付、创作者交互面时使用——需求基线 + 设计检查单(设席八问/注入五查/场景四要素)+ 第一人称需求探针方法(§10);非架构 SoT。
---
# agentic 席位与 context 工程设计 —— 生成运行时多 agent 设计的需求基线
> **性质与来源**:2026-07-05 创始人八轮问答驱动的"模型第一人称需求探索"——让生成运行时里实际干活的 LLM 回答"你需要什么才能把游戏做好、做大、改得动",本文是收敛蒸馏。**定位 = 需求基线 + 设计检查单,不是架构 SoT**:运行时架构唯一 SoT 见 [`agentic运行时架构图说.md`](../../docs/architecture/架构/生成引擎/agentic运行时架构图说.md);据此做具体设计仍走 [`feature-design-doc.md`](./feature-design-doc.md) 产设计档 + SoT 注册表申报 + 双评审;文中标〔提案〕的机制未经实现验证,落地前逐条核实。
> **适用**:设计/改造生成运行时的席位划分、context 装配、prompt 结构、工具面、多 agent 协作、goal-loop 自治交付、创作者交互面;评审这类设计时拿 §8 检查单当尺子。
> **配套**:AgentScope 2.0.2 机制速查 [`agentscope-2.0-facts.md`](../knowledge/agentscope-2.0-facts.md);prompt 文本的版本化/eval 治理 [`prompt-governance.md`](./prompt-governance.md)(分工:本文管"谁在哪个座位拿到什么",它管"prompt 文本自身怎么管");九门与真玩 [`game-e2e-cdp-harness.md`](./game-e2e-cdp-harness.md);质量口径 [`游戏质量与爆火能力.md`](../../docs/architecture/架构/生成引擎/游戏质量与爆火能力.md);遥测回流 [`数据飞轮.md`](../../docs/architecture/架构/生成引擎/数据飞轮.md)。
## 0. 总则:席位分的是结构,不是能力
所有席位背后是同一类 LLM,多 agent 设计因此不是能力分工,是**结构分工**。一个"席位" = prompt 配方 + context 配方 + 工具面 + harness 策略的版本化组合,整组落在配置控制面(AgentScope per-POST 装配:改配置,下一次生成即生效,零重启)。
**分席四判据**(至少满足其一才配新席位):
1. context 食谱本质不同——要广度 / 要深度 / 要刻意致盲;
2. 写权限必须互斥——并行工单的白名单不相交;
3. 验证独立性——做的和查的绝不能同席("出题的 ≠ 被考的"这条九门纪律的席位版);
4. 生命周期不同——常驻有状态 vs 按工单生灭。
**两条不设席戒律**:判定可确定化 → 做成门(产模型辩不过的事实;封装形态可为 MCP 工具——现状九门 = CDP harness driver + middleware 重跑 run_gates,MCP 化是主张非现状);调度可确定化 → 做成代码(队列 + workflow)。LLM 席位只放不可归约的判断。健康自检:席位数不随游戏复杂度增长,涨的只该是工单数与门数。
## 1. 四层结构与边界律
| 层 | 只装什么 | 绝不装什么 | 变更节奏 | AgentScope 落位 |
|---|---|---|---|---|
| **prompt(席位宪法)** | 身份与协议:职责宪章、禁区、输出工件 schema、升级协议、判断性红线 | 项目事实、工单内容、可门化/可模板化的规范 | 低频;每改 = 配置版本 + 评审 | `/agent` 配置的 prompt 部分;文本生命周期归 prompt-governance |
| **context(本次投影)** | 事实与任务:工单六要素、冷启动简报、承重件(契约/验收/坑清单)、省略目录(未注入但可检索项的清单)、出处戳(来源/新鲜度/性质) | 聊天历史回放、其他席位的推理过程 | 每 POST 从控制面现装配 | 投影编译 = 控制面自建能力(框架现成件只是 AgentRecord 的 context_config);配方进控制面版本化 |
| **environment(工作台)** | 能力与边界:workspace(worktree + 写白名单)、工具/MCP 白名单、按需热装 skills、预算态 | 与本工单无关的工具 schema | 按工单类型 | toolkit/session 注册 + workspace + 配额 |
| **harness(流程壳)** | 过程控制:门判拦 finish、续修环、软预算档位、journal 写前意图、离场清账、升级事件、盲评隔离、投喂记 trace | 业务判断(不替模型想,只兜过程与验证) | 随平台演进 | middleware 洋葱 + MCP 门 + 队列协议 |
**边界律一句话:身份进 prompt,事实进 context,能力进 environment,过程进 harness。**串层即设计错误,四个典型味道:项目事实写死在 prompt(僵化+配置漂移);身份协议塞进 context(每次重复付 token);用 prompt 劝模型别越权(该收 environment 白名单);指望模型自觉跑门(该进 harness 强制)。
**工具面三则**:
- **能力边界优于自律**:制作人席不给代码工具,范围失控在根上被断;评审席只读 + 门;数值席只有仿真器 + 数据表。
- **工具诱导行为**:无关工具 schema 既是注意力污染也是行为歪引(有 shell 就想用 shell)。
- **语义化窄工具优于万能工具**:给 `snapshot()` / `checkout_baseline()` / `replay_intent()`,不给裸 git——不变量在工具层强制,模型调不出违规操作。
**规范注入铁律:模板承载 > 门强制 > prompt 提醒。**能烤进脚手架的规范零边际 token 且直接塑形输出(reskin write_whitelist 实证);能确定化的做门,违规被拦而不是被劝;只有判断性规范才配占 prompt。可运营指标:**prompt 里的规范条数应单调递减**——每条都该在排队迁往模板或门,赖着不走的就是技术债。
## 2. 席位表
**常驻席**(每项目一套,有状态;状态活在账本,不活在会话):
| 席位 | 吃什么 | 产什么 | 工具面 |
|---|---|---|---|
| 制作人席 | 功能台账、遥测摘要、决策日志、用户消息+现场快照 | 工单、三档确认、版本决策提案 | 台账/遥测查询、工单铸造;**无代码工具** |
| 主设计席〔提案〕 | 系统契约、数据 schema、决策史 | 规格增量、验收标准、契约版本 | 契约注册表读写、schema 工具 |
| 数值仿真席〔提案〕 | 数据表、仿真器输出、真实遥测曲线 | 平衡判定、调参工单 | 快进仿真器、数据表编辑;无代码写 |
| 馆长席〔提案〕 | journal 流、坑清单增量、配方与 SoT | 蒸馏并入、索引更新、配方↔SoT 对账 | 知识库读写、对账门 |
**工单席**(按单生灭,无状态,可并行):
| 席位 | 吃什么 | 产什么 | 工具面 |
|---|---|---|---|
| 实现席 ×N | 单系统契约 + 白名单文件 + 品类坑清单 | 交付包(commit + 自测证据 + journal) | 白名单内写、build/test、契约查询 |
| 内容席 | 数据表 schema + 样例 | 过校验器的数据表 | 表编辑 + 校验器;便宜档模型即可 |
| 资产席 | 资产清单、风格锚、包体预算 | 素材 + 元数据(尺寸/锚点) | 素材生成/管理;无逻辑写 |
| 评审席(盲) | 规格 + 工件 + 证据,**不见实现过程** | 判定书 | 只读 + 九门(现状经 harness/middleware 调用,可封 MCP) |
| 玩家席 | persona play-spec + 可玩预览 | 体验报告(FTUE 卡点/手感) | 真玩 driver;上线后被真实遥测校准 |
**不设席清单**(代码/工具,不是 agent):调度排队(RocketMQ + 控制面)、九门、快进仿真器、兼容检查器、契约测试、语义版本操作、docs-gate 式对账。修复也不设席——RepairMiddleware 是实现席的会话内环。
**已有胚胎 → 缺口对照**(设计时先认领胚胎,别平地起楼):
| 席位/机制 | 已有胚胎(已落地) | 缺口 |
|---|---|---|
| 制作人席 | A11 调整回路两段式(判意图→计划→前端确认→执行) | 分诊矩阵、三档确认策略、用户现场快照 |
| 实现席 | cheap-worker / tier2 worker + RepairMiddleware + write_whitelist | 工单化收窄、journal 协议 |
| 评审席 | gate_judge middleware + 九门 | 独立 fresh 会话化、判定书 schema |
| 玩家席 | 九门真玩 driver + play-spec | persona 化、体验报告 schema |
| 配方版本化 | 配置控制面(yudao 版本层 + per-POST 热装配) | context 配方、工具面、skills 纳入同一治理 |
| 投喂可观测 | 生成线 trace 已闭合(jsonl 主路;便宜档 OTLP sink 波③已通、默认关) | 把"每次装配了什么 context"记进 span |
| 主设计席 / 数值仿真席 / 馆长席、九类工件 schema、版本卡 / 意图重放 / 兼容检查器 | 无 | 全新建〔提案〕 |
**同源风险**(所有席位同一 LLM,盲评只解决"被带节奏",解决不了"想不到同一处")三缓解,按有效性排:① 工具事实优先——把尽可能多的判定压进确定性工具;② 异档模型当多样性来源——关键评审席换不同家族/档位的模型(子代理成本分档的意外红利);③ 同席多镜头——正确性/契约/回归各跑一趟 fresh context。
## 3. 通信:星形账本中心,九类工件,禁自由对话
席位之间**不存在 live 群聊**;一切通信 = 从项目存储拉工件 + 向队列发工件。三个理由:可中断(对话中断即死,工件队列天然可续)、可审计(每件进 trace,"它当时知道什么"可回放)、防污染(盲评与类型化交接的前提就是不共享会话)。这也贴 AgentScope 现实:Service `/chat` 是 per-POST 的 fire-and-forget 触发(结果走 SSE 事件流),session 是单席位状态,不是聊天室。席内要查事实走注册表工具,要扩范围发升级事件,不去"找别的席聊"。
九类工件(全部带 schema + 出处戳〔来源 commit / 时间 / 性质:事实|推断|假设〕):
| 工件 | 方向 | 要点 |
|---|---|---|
| 工单 | 制作人 → 各席 | 六要素:目标/范围白名单/验收/预算/依赖/升级策略 |
| 规格增量 | 主设计 → 实现/内容 | 契约变更提案 + 版本 bump + 迁移注记 |
| 交付包 | 实现 → 评审 | commit hash + 自测证据 + journal 条目(hash 必须真实可 rev-parse,防谎报 DONE) |
| 证据包 | 门/driver → 评审/版本卡 | 九门测量值、仿真结果、截图、真玩轨迹 |
| 判定书 | 评审 → 制作人 | 过/不过 + 测量值 + 可行动的失败现场(不许只给门名) |
| 升级事件 | 任意席 → 制作人 | raise_scope_change:原因 + 产品语言选项;发完干净收口,不悬挂等输入 |
| 版本卡〔提案〕 | 制作人 → 用户 | 基线 + 已应用意图集 + 证据包 + 兼容戳 |
| 回流工单 | 遥测 → 制作人 | 漏斗卡点/留存差 → 修复工单(数据飞轮的工单化出口) |
| 蒸馏增量 | 各席 → 馆长 | 坑/经验条目,经查重并入,不直写知识库 |
## 4. context 装配六性质(注入怎么控)
1. **角色投影**:推送只放承重件(契约/验收/坑清单),其余给**省略目录**("还存在这些,可用 X 检索")。不给省略目录,模型会把投喂当全世界,幻觉从此长出;全推,则回到浪费读轮的老路。
2. **刻意致盲**:评审席的 context 配方里不含实现会话的存在;修复轮给"上一版代码 + 门失败证据",不给前任心路——继承推理等于继承盲区。
3. **单源编译**:所有投影从同一 SoT 机器生成,禁止各席各抄一份(双写必漂移)。**配方是代码,配方 bug 与代码 bug 同级**:进控制面版本化,并配机器对账(配方引用的 SoT 命题还在不在、原文变没变)。反面判例 = "传导断裂":便宜档实现入口丢了 SoT 的"轻量≠简单"命题、留着误导措辞,agent 从局部线索重建出错误世界观,连栽数轮。
4. **出处戳**:每块注入带来源/新鲜度/性质三戳。"三天前的构建状态"与"刚跑完的门结果"必须能被区别信任。
5. **回写分道 + 蒸馏守门**:实现席只写 journal 与坑增量、评审席只写判定、规划席只写工单,谁都不直改账本正文;蒸馏物经馆长门(查重/并入/汰旧),否则积累的不是知识库是垃圾堆。
6. **投喂进 trace**:每次会话记录"装配了什么配方、什么版本的哪些块"。坏结果才能归因(模型不行还是喂错了),配方改进才能 A/B("给坑清单 vs 不给,过门率差多少")——context 工程从玄学变成像 D11 权重一样可校准的对象。
镜像原则:**配方密度随席位模型档位调**——便宜模型席位零裁量、全铺开;贵模型席位给索引让它自拉。
## 5. 长项目操作系统(数十天、数千次改动、随时中断)
长项目里上下文窗口只是"一次工时的工作台",项目必须整个活在仓库与控制面里,每次 POST 只做投影。六件,全部不活在任何会话:
| 件 | 回答 | 要点 |
|---|---|---|
| 账本 | 现在在哪 | 版本线、在飞工单、门态;冷启动简报由它生成,禁聊天回放 |
| 工单 | 这次干什么 | 有界:目标/白名单/验收/预算;杀掉中途的席位损失有界 |
| journal | 中断了怎么接 | 写前意图 + 完成回执;恢复 = 对账 workspace vs 日志,不是考古 diff 猜前任 |
| 决策史 | 为什么不能乱动 | 防第 2000 次改动把第 300 次的深思设计当垃圾重构 |
| 版本线 | 在哪条线上 | 见下方版本语义 |
| 索引 | 去哪找 | 模块地图 + 归属索引,机器生成;工单进来先预取再干活 |
配套的**离场清账协议**:每工单收尾必回写账本更新 + 决策日志 + 坑增量,三样齐才算完——没有蒸馏的会话是一次性消耗(`.agents/` 纪律下沉进每个游戏工程,由 harness 强制)。
**版本语义**(用户看时间线,模型看基线资格,谁都不看 git log):
- **版本卡** = 通过验收的工单产物(缩略图 + 一句人话变更 + 证据包);数千 commit 是模型的事,用户只见几十张卡。
- **基线资格机器戳**:"从稳定版继续"解析为"证据全绿且线上指标不劣化的最近版本",不是最近一个 tag。
- **live 线永不直碰**:发布 = 审核门后指针切换,回退 = 指针回切,"改崩线上"在结构上不可能。
- **回退真雷是玩家存档不是代码**:触碰持久化 schema 的工单强制登记数据版本;回退前兼容检查器自动判"新存档在旧版打不开",把选项翻成人话(迁移/重置/放弃)交用户。
- **分叉不做 merge,做意图重放**:"把那版的宠物拿回来" = 按原工单(意图+验收)在当前基线重新实现、过同套验收。AI 重做一个功能足够便宜,合并冲突这个概念对零技能用户可以整个不存在。
## 6. 交互面(分诊、确认、goal-loop)
**三档确认,可逆性替代确认**(回退越便宜,需要事前确认的事越少):
- 档0 静默直做:参数级微调,进变更日志,一键可撤;
- 档1 做完给试玩(新功能默认档):**确认的最好形式是玩预览版,不是读计划文字**;
- 档2 先问再做,仅三类:不可逆(动线上存档/经济/付费)、贵(超预算/天数阈值)、与用户既往决定冲突(引决策史对质:"排行榜 V9 有过,你在 V11 让我去掉的,要加回来吗")。
**模糊请求五步**:① 现场快照先于追问(在玩哪版哪景、最近事件——"它太快了"的"它"九成是刚碰过的东西);② 模糊词落设计轴(主设计席维护"词→可调面"映射,数据驱动使"改简单"=参数提案而非代码重写);③ 遥测佐证纠偏(用户说三关难、数据说卡二关——给证据版判读,字面顺从最贵);④ 收敛按序:**试玩变体 > 选择题 > 追问,永不出开放问答**(零技能用户答不了"重力还是碰撞体积");⑤ 小步默认 + 判读入档(原话→判读→依据,积累该用户词典)。
**场景注册表**:交互场景清单本身是契约——每场景 = 意图类 + 路由席位 + 默认确认档 + 遥测埋点,登记进 `contracts/`(八类契约的延伸),分诊按它驱动,新场景显式增列,不在 prompt 里悄悄长(注册表真落 `contracts/` 后,下方场景族清单迁过去、本节改指针,避免双写)。场景族:立项创作 / 修改(对象:资产·数值·内容·机制·meta × 动词:改增删调回退)/ 版本操作 / 目标委托 / 诊断咨询(只读即答,不铸工单)/ 运营变现(碰钱必档2)/ 素材管理 / 打断接管。
**goal-loop 铁律**:goal 必须先编译成**可判定验收门 + 预算上限 + 里程碑节奏**才许进 loop——编译不出验收门的 goal 永不终止;里程碑产版本卡供用户异步试玩;仅档2事项自动暂停挂决策点;交付判定 = 门全绿 + 证据包,**绝不是模型自称完成**;用户随时打断,当前工单跑完或干净中止(journal 收口),插单分诊重排,账本无损恢复。
## 7. 生成游戏工程规范(舰队尺度:无数游戏、长期代改)
单个工程的代码结构与红线 SoT = [`littlejs-game-dev.md`](./littlejs-game-dev.md)(§1 代码结构、§7 受控面铁律),本节不复述,只提舰队尺度的增量——规范的目标函数是**冷启动定向速度、机器可校验、局部可改、跨游戏同构**,不是人类品味:
1. **千游一构的舰队含义**:同拓扑/同入口/同 manifest 的价值在舰队运维——批量迁移、安全补丁、横向审计变机械活,模型永远不用"学"某个项目的布局;
2. **状态显式**:存档 schema 带版本、全局状态单一登记处——回退与兼容检查器(§5)的地基;
3. **自描述**:模块地图机器生成不手写;**注释是写给下一个失忆的模型的信**——写"为什么不能动",不写"这行在干嘛";
4. **回归单调增**:每修一个 bug 沉淀一个门/测试进本工程——数千次改动的质量靠累积的门,不靠第 N 个席位的小心。
规范本身版本化:manifest 记"本工程生于规范 v3";旧游戏按出生规范维护,升规范 = 显式舰队工单——**禁止顺手升级**(最小改动红线的舰队形态)。契约先行与数据逻辑分离已是作业手册红线,此处只强调舰队含义:§6 模糊请求处理链("改简单"=参数提案)整个站在数据表化之上。
## 8. 设计检查单(后续设计/评审会话按此过)
**新设一个席位,八问**:存在理由命中分席四判据哪条?三件套配方(prompt/context/工具面)各是什么?进不进控制面版本化?对谁刻意致盲?回写哪条道?产出工件 schema 是什么?有没有可认领的已有胚胎(§2 对照表)?框架默认注入的能力面审计过、与白名单对账了吗(AgentScope **Service 路 `get_toolkit`** 无条件并入 15 件无条件框架默认工具〔六内建 + Planning×4 + ToolStop + Team×4;另 Schedule×4 仅 session 配 chat_model_config 时条件并入〕、旁路白名单——实测教训;纯库 CLI 路 = 自装 toolkit 无此并入,cheap Service 已双补丁封口、审计口径见北极星档 §2 注入面审计节)?
**设计一次 context 注入,五查**:承重件清单最小了吗?省略目录给了吗?出处三戳齐吗?配方↔SoT 有机器对账吗?token 密度与席位模型档位匹配吗?
**设计一个交互场景,四要素**:意图类、路由席位、默认确认档、遥测埋点——登记进场景注册表了吗?
**任何"规范进 prompt"的提议,先问两遍**:能进模板吗?能做门吗?都不能才许进 prompt,并挂"待迁出"标。
## 9. 来源与效力
2026-07-05 创始人八轮问答(单次生成信息面 → 复杂游戏差距 → 长项目操作系统 → 范围与版本控制 → 多 agent context 控制 → 席位划分与通信 → 工具/规范/场景清单 → 四层边界),对模型第一人称需求陈述的收敛蒸馏。效力:**需求基线**——做 agentic 设计时当尺子与检查单用;它不推翻任何既有 SoT 决策;〔提案〕机制(主设计席/数值仿真席/馆长席/九类工件 schema/版本卡/意图重放/兼容检查器)落设计档时须按 [`feature-design-doc.md`](./feature-design-doc.md) 流程评审并逐条验证可行性。
## 10. 第一人称需求探针(方法附录,W-PROBE)
本文正文是一次探针的产物;本节固化方法本身,供新 agentic 子系统(审核台辅助/玩家 feed 推荐/回流环运营席/创作者对话席等)设计期复用。探针 = 创始人驱动最强可用模型,以「我就是该子系统里干活的 LLM」第一人称回答需要什么,产需求基线——设计期前置工序,产出永远是需求基线+检查单,不是架构 SoT;探针对象排期唯一登记处 = MVP 作战清单 W-PROBE 单(本节不维护清单,防双写)。
**问题序列模板**(八轮推进逻辑,按子系统代入;轮间改向由创始人驱动、不可省——本次实践约一半信息密度来自人的追问改向):
1. **单次任务信息面**:完成一次 X,你希望获得什么信息/能力?——摸清最小工作单元的需求;
2. **最复杂标的差距**:这些够你做出〈本域最复杂标的〉吗?——用北极星级负载逼出单次视角的缺口;
3. **长周期规模化**:项目持续数十天/随时中断再续/数千次操作/多版本交付,你需要什么?——逼出操作系统级需求(账本/journal/决策史);
4. **范围与版本**:用户不感知工程现状、中途提新需求,范围怎么控?如何回退/签出历史版本再开新功能?——逼出确认策略与版本语义;
5. **多 agent context**:多 agent 都用你当 LLM 时,你希望怎么控制各 agent 拿到的 context?——逼出装配性质(投影/致盲/出处);
6. **席位与通信**:如何划分 agent、各管什么、之间通信和交付什么?——逼出分席判据与工件类型;
7. **工具/规范/场景**:各席工具面(tools/MCP/skills)差异?模糊请求怎么办?舰队级代码规范?交互场景清单?——逼出 environment 面与交互契约;
8. **结构与边界收口**:context/environment/harness/prompt 各自结构与边界?——逼出四层边界律,收敛成可落档结构。
**收敛判据**:新一轮回答不再产生新的需求类(只在细化既有类)即收敛;每轮末由创始人判断改向或加压(换更极端负载/加约束)。
**固化格式**:蒸馏为「需求基线+设计检查单」单文件——总则判据/结构表/检查单三件必有;未经实现验证的机制统一标〔提案〕;末节写明来源与效力(不推翻既有 SoT,落地走 feature-design 流程逐条验证);已有胚胎逐项认领(参照 §2 对照表的做法),别让基线平地起楼。
**交接契约**:下游设计档(feature-design)必须逐条消费基线条目并留「采/改/弃+理由」——基线是输入不是结论;设计档评审按 §8 检查单过尺(protocol §2.3);首次复用跑通「探针→基线→设计档」全链后,按真跑发现回修本节。

View File

@ -0,0 +1,213 @@
# AI 生成链路开发与联调手册ai-generation-pipeline
> ## 🛑 DEPRECATED 蓝图Dify/OpenGame 管线已降级远期、从未部署)
> **下文 §目标 / §1§5Dify 开发流程、OpenGame/ComfyUI 对接、DifyClient 等)为 1.x 蓝图,勿照此搭建生成链路。** 现行裁决C2 2026-06-09 / HJ-GEN-001 终审 / **HJ-AGI-002**agentic 基建 HJ-AGI-001 已于 2026-06-15 被 HJ-AGI-002 覆盖):
> - **生成主线 = new-api 网关 → 便宜模型直连**(生产生成=M2.7/M3/DS-V4 + harness 门Fable 只造引擎);**agent 写码于插件库**旧「LLM 填参 / 模板驱动生成」线退役。
> - **agentic 编排基建 = SAA(Spring AI Alibaba v1.1.2.2)裸图编排 (HJ-AGI-002short-term SAA-onlyAgentScope 降 long-term premium 独立轨)**;「自研编排引擎 / DAG 工作流引擎」表述作废。
> - **Dify / OpenGame / ComfyUI(文生代码部分) = 降级远期,从未部署**;网关=new-api非 Dify。
> - 仍现行有效的只有下方 **§⓪**agent 闭环 v1 实证 + new-api 通道 + Prompt Registry + 回调写链语义)。素材生成侧 ComfyUI/Stability/Fish 选型另见 tech-decisions图音素材链未被本裁决推翻
> 配套现行口径:[`../knowledge/tech-decisions.md`](../knowledge/tech-decisions.md) §1/§1.1、[`../rules/build-vs-buy.md`](../rules/build-vs-buy.md)。
>
> ---
>
> 蒸馏来源:`docs/architecture/系统概要设计-技术决策版.md`§4.1 AI 生成链路 / §6.2 §6.7 选型 / §7.10 产物缓存 / §8 风险)、`docs/architecture/技术架构与模块.md`aigc/studio 模块 T-AGC-*/T-STU-* 技术功能)、`docs/architecture/系统概要设计-开发团队版.md`§10 外部工具层开发指南 / §6.2 联调)。
> 适用:开发/调试 aigc 模块(~~及其外部 AI 引擎 Dify / OpenGame / ComfyUI~~ 已降级远期,见顶部横幅)。
> 配套:降级/可靠性红线见 [`../rules/security-and-reliability.md`](../rules/security-and-reliability.md);选型背景见 [`../knowledge/tech-decisions.md`](../knowledge/tech-decisions.md);模块全景见 [`../knowledge/product-and-architecture.md`](../knowledge/product-and-architecture.md);新模块流程见 [`./add-business-module.md`](./add-business-module.md);契约见 [`./contract-first-development.md`](./contract-first-development.md)。
## ⓪ 2026-06-10 现实态agent 闭环 v1 已验收,本节=唯一现行口径,下文 §目标§5 为退役蓝图)
**生产路径 = 「便宜模型直出 + agent 闭环 QA」**new-api 网关Dify/OpenGame/ComfyUI 文生代码均未部署,下文蓝图已退役;旧"模板驱动生成"措辞按 2026-06-12 模板哲学更新——玩法模板层废除、agent 写码于插件库):
- **真源与配方**:设计=`docs/agent-specs/2026-06-09-agent化生成QA闭环-{review,execution}.md`含全部裁决账M-b 生成下沉=`2026-06-10-Mb生成下沉-execution.md`;编排器/裁决/玩家取证代码=`docs/agent-specs/2026-06-09-agent-loop-v1/orchestrator/`README 有批跑用法);验收实证=`runs/batch-001/`accept 80.0%)。
- **LLM 通道**new-api `http://100.64.0.8:3000` + MiniMax-M2.7 + `response_format=json_object`key 走 `NEWAPI_KEY` 环境变量不入库;抽检模型走 `NEWAPI_AUDIT_MODEL`。Prompt 全部走 `contracts/prompts/` Registry改必升版过四道闸4 模板 GameConfig schema 在 `contracts/templates/`(真源对齐 gen_spike.py
- **批跑前置铁律**:①浏览器侧 frontend 必须 `http://localhost:4173`crypto.subtle 安全上下文,走 IP 真包也永 demo 兜底)②执行器与批跑互斥(批前关 `aigc.executor.enabled`)③首批前跑一次 `evalflow.py --seed-spike`(幂等)④抽检模型不可用按评审版 R8 降级(同模型异版本 prompt 重裁+告警),分歧>10% 冻结下一批发布开关。
- **回调写链**真实9bf3d54唯一写入路径=`DifyCallbackService.handleCallback`三表同事务task/version/runtime_packageputManifest 未命中显式失败checksum 两段序列化消解自指——任何新生成来源M-b 执行器/未来真 Dify一律复用此语义禁止旁路。
### ⓪′ P3 派发面generic 外置 worker 生成 engineBundle 入 feed3b-A 实证 2026-06-14
**范式**M-b「执行器进程内 LLM 出 GameConfig」之外新增「**派发外置 worker 产 engineBundle**」路——仅 `templateId=generic` 走,旧四模板(若再开放)走旧进程内路逐行不变(**零回归硬约束**)。链路:执行器 `dispatchGeneric``AigcGenerateExecutor`)组 §6.1 job POST 给 `aigc.executor.worker-url` → worker 异步产 bundle → 把含 `engineBundle``DifyCallbackReqVO` POST 回**新开的内网回调子路由** `/admin-api/aigc/dify/callback-internal` → 复用 `handleCallback` 唯一写入路径建版本/组包/落包 → 发布翻转入 feed。执行器投递握手成功即留任务 RUNNING 等回调(超 deadline 由 watchdog 收尸兜底),**不在派发路写终态、不走进程内 `chatJson`**(日志 `executor-llm` 对该任务 0 命中即铁证)。
**两条红线(门里逮过的真坑·复用必看)**
1. **🔴 非 RBAC 的 HTTP 回调路必须自注入系统身份(部署窗口#3 同根因,换路复发)**worker 是内部服务无登录态,回调走 `@PermitAll` 内网子路由(仿 `SmsCallbackController`/pay-notify 范式解 §6.5 auth 孤儿);但 `@PermitAll` 路在 web 线程无 LoginUser → `DefaultDBFieldHandler` 取不到 userId → `handleCallback` 三表 UPDATE/INSERT 的 `updater/creator` 带 null 直出撞 NOT NULL`DataIntegrityViolation`,任务卡 RUNNING。**修法**:内网回调 controller 内自注入系统身份 `LoginUser().setId(0L).setUserType(ADMIN)``SecurityContextHolder``finally``clearContext()`(镜像 `AigcGenerateExecutor.executeWithSystemIdentity`。RBAC 的 `/dify/callback` 由登录管理员提供身份故无此坑——**任何新免鉴权回调路(真 worker 3b-B/外部接入)都要补这条**。
2. **@PermitAll 内网回调路服务间签名已补(3b-B,关裸缺口)**:原仅恃「内网不可外达」兜底=任意可达请求伪造回调驱动落包(安全红线)。3b-B 补 `CallbackSignatureVerifier`(HMAC-SHA256 对回调**原始字节**验签·`MessageDigest.isEqual` 常数时间比对抗时序·JDK 原生零依赖·空密钥=回退仅内网兜底);controller 接 `@RequestBody String rawBody` **先验签错签真 HTTP 401 再反序列化**(手动校验补 traceId/status 非空·绕过 @Valid);worker 侧 `json.dumps(payload,ensure_ascii=False).encode("utf-8")` **一次算定既签既发**(Content-Type 带 charset=utf-8 使 Spring `@RequestBody String` 同字节还原·`hexdigest()` 小写对齐 Java `Character.forDigit`)。**HMAC 逐字节对账铁律**:签名字节==HTTP 发送字节,两侧密钥(`CALLBACK_SECRET`/`AIGC_CALLBACK_SECRET`)逐字节一致,否则永不匹配。e2e 三态铁证(`evidence-3bB/p4*.txt`):缺签/错签→**401**、正签→**HTTP200 过门走下游**。(`/dify/callback` 真 Dify 接入补签名 TODO 同治。)
**stub worker 配方证管线用3b-B 换真 worker**`game-cloud/scripts/wg1_stub_worker.py`(极简 HTTP收 job→读固定 pong bundle `game-runtime/games/_wg1-gen/pong/bundle.iife.js` 作 engineBundle→回调gameConfig 回 §5.2 最小占位 `{templateId,title,theme,engineDriven:true}`,不调 LLM 不打包。e2e 脚手架:`p3b_scaffold_pong.sql`(建 gameId=9302 四态)→插 generic 任务→回调建新版本→`p3b_publish_flip.sql`(把 feed 翻到回调新版本)→ CDP 复用 `game-studio/evidence/p3-pong/p3-pong.driver.cjs <base> <gameId> <versionId>` 真玩。**配置**`aigc.executor.worker-url`(空=派发面无端点→generic 判 failed(llm_error)不静默卡死staging 经 `.env``AIGC_WORKER_URL`stub 起 durable 单元 `systemd-run --unit=wg1-stub-worker`
**真 worker 服务壳(3b-B,`c99014c`)**`wg1/gen-worker/worker/service.py`=`http.server` 壳包 L2 `run_studio`(design 展开一句话→自产 gatespec→九门真玩),`run.py`/`agent_loop` **逐行不改**;`POST /generate` 收 §6.1 job→立即 202→**后台串行**(全局 Lock·`run_studio` 占 serve 4320/CDP 9222 不可并发)`asyncio.run`→过门读 `_wg1-gen/gen-<gameId>/bundle.iife.js`→HMAC 签名回调(见红线 2)。**e2e 真证结论(2026-06-14,`evidence-3bB/`)**:**✅ P0-P4 安全面+全自动管线闭合**(执行器认领→投 job→worker 真生成真烧模型→HMAC 双向证→`handleCallback` 落库);**❌ P5 终局未达**=三款真生成全 `nine_gate_failed`(截图证真出 on-theme 可渲可动「点击小怪物」游戏,仅点击→计分坏→**九门质量门正确拦截不可玩**)。**判**:plumbing+安全+质量门=L0 易部分已成且证;**便宜模型过九门=moat 真壁垒**→生成质量转 L1(见 [`cheap-model-game-generation.md`](./cheap-model-game-generation.md) §3 点击坑/§8 质量门)。
---
## 目标
把自然语言 Prompt 经 **aigc Java 壳 → Dify 编排 → OpenGame 代码生成 / ComfyUI 素材生成**,产出可运行的 GamePackageconfig + code + assets并写回 project。**产出后 runtime 可编译预览、创作者可试玩发布。**
## 前置
- AI 引擎已起:`docker compose -f deploy/docker-compose.ai.yml up -d`ComfyUI 需 GPU无 GPU 时图片走 mock 或外部 API
- RocketMQ Consumer 已注册(生成任务靠 MQ 异步驱动;若一直 QUEUED 多半是 Dify 没起或 Consumer 没注册,见开发团队版 §9
- Dify workflow 已发布并拿到 `workflow_id`OpenGame `/generate` 端点 curl 可调通。
---
## 1. 架构(四段)
```
┌──────────────────────────────────────────────────────────────┐
│ game-module-aigcJava 壳Spring Boot
│ ├─ Controller 接收创作请求(/app/aigc/generate
│ ├─ TaskDispatcher RocketMQ 异步调度(削峰/重试/超时) │
│ ├─ DifyClient HTTP 调 Dify Workflow API │
│ └─ ResultWriter 写回 project 模块(草稿/版本) │
├──────────────────────────────────────────────────────────────┤
│ DifyDocker 自部署)— DAG 可视化编排 │
│ ├─ 多 LLM 热切换(通义千问 / DeepSeek / OpenAI
│ └─ 节点安全检测→意图解析→模板匹配→GameConfig+Schema │
│ →HTTP 调 OpenGame→HTTP 调 ComfyUI→质量评估 │
├──────────────────────────────────────────────────────────────┤
│ OpenGame AgentPython 微服务HTTP API
│ └─ 6 阶段 pipeline脚手架→设计→素材→代码→验证→修正 │
│ 输出可运行 Web 游戏代码HTML/JS/CSS + assets
├──────────────────────────────────────────────────────────────┤
│ ComfyUI自部署— 图片/贴图/角色/场景/封面素材生成 │
│ └─ 节点化 workflow可训练 IP 风格 LoRAHTTP API 对接 Dify │
└──────────────────────────────────────────────────────────────┘
```
> 分工铁律:企业级监控/链路 trace/高并发优化都在 **Dify 壳外侧Java 壳)** 做,**不改 Dify/OpenGame 内核**,保证可升级、可替换(技术决策版 §6.2)。
>
> **本手册聚焦 Tier1OpenGame 文生代码 → 自研 Canvas Runtime。** Tier2/3 复杂2D·3D·原生游戏走 **Cocos-MCP**agentic 工具编排,归 studio见 [`../knowledge/tech-decisions.md`](../knowledge/tech-decisions.md) §1.1),非本手册范围。所有节点 prompt 统一取自 **Prompt Registry**git `contracts/prompts/`,第 8 类契约,按 `id@version` 加载注入、不内嵌 Dify/OpenGame 内核);改 prompt 走 PR + eval 门禁,详见 [`../../docs/agent-specs/prompt治理体系-execution.md`](../../docs/agent-specs/prompt治理体系-execution.md)。
---
## 2. 生成链路时序
```
创作者 Prompt + 模板/风格
→ ① Prompt 安全检测(违禁词/敏感意图/注入攻击 → 命中即拒绝)
→ ② 意图解析LLM 提取玩法/角色/规则/风格结构化参数)
→ ③ 模板匹配(语义映射到预制玩法:躲避/跑酷/射击/解谜/点击收集)
→ ④ GameConfig 生成 + JSON Schema 校验(数值范围/必填/资源引用)
→ ⑤ OpenGame 代码生成6 阶段 pipeline
→ ⑥ 质量评估(结构完整性 / 资源有效性 / 运行成功率)
→ 输出 GamePackageconfig + code + assets→ ResultWriter 写回 project
```
素材生成(图片/封面)由 Dify 的 HTTP 节点在链路中并行调 ComfyUI详见 Doc B aigc 模块)。
---
## 3. 生成任务状态机
状态:`queued / running / succeeded / failed / timed_out / canceled`(技术决策版 §4.1Doc B aigc 模块)。
```
[*] → QUEUED ──消费消息──→ RUNNING
RUNNING → SUCCEEDED生成完成 + 质量通过)→ [*]
RUNNING → FAILED生成失败 / 质量不达标)
RUNNING → TIMED_OUT超时 120s
FAILED → QUEUED重试 ≤ 2 次);超次 → [*]
TIMED_OUT→ QUEUED重试 ≤ 1 次);超次 → [*]
QUEUED / RUNNING → CANCELED用户取消→ [*]
```
要点:
- **LLM 调用编排**:单次 LLM 调用超时 30s、重试 2 次、熔断降级、供应商故障切换(详见 Doc B aigc 模块)。
- **任务级**:超时阈值 120s失败重试 ≤2、超时重试 ≤1用户可在 QUEUED/RUNNING 取消。
- 失败要做**原因分类 + 可读提示**(描述不清/违规/超时/匹配低/校验失败,详见 Doc B aigc 模块)。
---
## 4. 性能指标与限流
| 指标 | 目标 | 出处 |
|---|---|---|
| 生成 P50 耗时 | < 60s | 技术决策版 §4.1 |
| 生成 P95 耗时 | < 180s | 技术决策版 §4.1 |
| **生成成功率MVP 验收)** | **≥ 80%**(基于 3-5 个模板) | MVP 执行 spec §1.2 / §4 Week2 Day10 |
| **生成成功率(远期蓝图)** | **≥ 85%** | 技术决策版 §4.1 / §1.2 系统目标 |
| 队列最大积压 | 500 任务,超过返回 **429** | 技术决策版 §4.1 |
> 分层口径:**≥80%** 为 MVP 验收线(按此判定);**≥85%** 为远期蓝图目标线。
---
## 5. Dify 开发流程
```bash
open http://localhost:3001 # 本地 Dify UI
# 创建/编辑游戏生成 Workflow
# 1. Dify UI → Studio → 创建 Workflow
# 2. 加节点LLM / HTTP(OpenGame) / HTTP(ComfyUI) / 条件分支 / 变量赋值
# 3. 测试运行 → 逐节点查看输入/输出
# 4. 发布为 API → 获取 workflow_id
```
后端 `DifyClient` 调用:
```http
POST http://localhost:3001/v1/workflows/run
Content-Type: application/json
Authorization: Bearer <dify-app-token>
{
"inputs": { "prompt": "...", "template_id": "..." },
"response_mode": "blocking"
}
```
> 升级 Dify 必须**锁定版本**能力增强放壳层Java 侧)做,不动内核(技术决策版 §8 风险 5
---
## 6. ComfyUI 开发流程
```bash
open http://localhost:8188 # 本地 ComfyUI
# 设计图片生成 workflow节点拖拽
# 1. 加载模型节点Flux / SDXL / 自训练 IP 风格 LoRA
# 2. 配置 prompt / negative_prompt / 尺寸 / 步数
# 3. 运行验证效果
# 4. 导出 workflowSave → API Format得到 workflow JSON
```
Dify HTTP 节点对接 ComfyUI
```http
POST http://localhost:8188/prompt # 提交body { "prompt": <workflow_json>, "client_id": "..." }
GET http://localhost:8188/history/{prompt_id} # 轮询取结果图片 URL
```
> 选 ComfyUI 而非直调 Midjourney/DALL-E可训练 **IP 风格 LoRA** 出风格一致的系列素材、节点 workflow 可被 Dify 编排、自部署无审查/无 API 限制、长期成本更低(技术决策版 §6.7)。
---
## 7. 降级(铁律)
LLM 不可用 → 退化为**确定性 Fallback 生成器**(模板参数填充,不依赖 LLM保证"LLM 挂了仍能出基础可玩游戏"(详见 Doc B aigc 模块,技术决策版 §7.2 / §8 风险 1。降级细则与超时/重试/熔断标准见 [`../rules/security-and-reliability.md`](../rules/security-and-reliability.md)。
---
## 8. 本地地址速查
| 组件 | 地址 | 联调方式 |
|---|---|---|
| Dify | `http://localhost:3001` | DifyClient 调 Workflow APIUI 看执行日志 |
| OpenGame | `http://localhost:8100` | Dify 节点调用,或直接 curl `/generate` |
| ComfyUI | `http://localhost:8188` | Dify HTTP 节点对接 `/prompt` + `/history` |
| safe-content-ai | `http://localhost:8200` | 图片 NSFW 快检compliance 用) |
---
## 9. 常见坑
| 坑 | 排查 / 应对 |
|---|---|
| 生成任务一直 QUEUED | 查 Dify 是否启动 + RocketMQ Consumer 是否注册(开发团队版 §9查 TaskDispatcher 日志 |
| OpenGame 社区停更 | fork 自维护(核心 pipeline 逻辑简单可控);最坏退化为**纯 Dify + 模板**生成(技术决策版 §8 风险 4 |
| Dify 升级不兼容 | **锁定版本** + 壳层隔离,自部署可控;能力增强放 Java 壳侧(技术决策版 §8 风险 5 |
| 同一 Prompt 反复烧 LLM token | 开**生成产物缓存**:相同 Prompt hash 命中即跳过 LLM 调用(技术决策版 §7.10),省成本 + 加速 |
| LLM 输出 GameConfig 不合规/字段缺失 | 链路内 JSON Schema 强校验 + 资源有效性校验,不达标不入库(技术决策版 §8 风险 2 |
| 生成游戏质量飘忽 | 用模板约束输出 + 可玩性自动测试;模板级 Golden Config 回归比对防退化(详见 Doc B aigc 模块) |
| 素材生成 ComfyUI 无 GPU 慢/不可用 | CPU 模式慢 10xMVP 无 GPU 时图片走 mock 或外部 API开发团队版 §1.3 |

View File

@ -1,89 +0,0 @@
---
name: architecture-diagram-atlas
description: "当为 docs/architecture 某领域补图说、新增系统级图、或设计档变更后据防漂移门同步 SVG/图说时使用:SVG house style、图说文档结构、三范式纪律(单源/防漂移/状态双层)、按域并行编排配方与主会话复验脚本。"
---
# 架构图集生成配方(architecture diagram atlas)
`docs/architecture` 的每个领域画成"一套图 + 讲解"的图说,外加一份顶层总图叙(apex)。目标三条:① 新人看图入门;② 评审者**据图发现设计缝**;③ 图与设计档不漂移。2026-06-22 第一期(总图叙 + 7 域图说 ≈ 71 图位 / 35 SVG)实证,配方在此固化。计划稿 = `git show 8ea97234:docs/plans/2026-06-22-架构图集-目录与图清单-plan.md`;金样板 = `docs/architecture/07-运维图说.md` + 其 `assets/`
## 何时用
- 给某个领域/子系统补图说,或新增系统级图。
- 设计档变更后,据防漂移门把对应图说与 SVG 同步更新。
## 产物形态
- **混合**:复杂/跨域图 = 手绘 **SVG** 大图(高保真、矢量、GitHub 直渲);简单/线性图 = **Mermaid** 内联。
- 每张 SVG 另在 **mini-desktop** 批量转 PNG 入仓(6c6g 禁 chrome、无转换器)。SVG 只在 6c6g 出。
- **最终产物 = 按领域拆分的编号文档,全放 `docs/architecture/` 顶层**:`00-系统总览图说.md`(总图 = 7 张系统级图 + 目录,链各域)+ `01-产品…07-运维图说.md`(每域一篇,各含该域全部图 + 讲解)。SVG 仍按域留各子目录 `assets/`(`运维/assets/``架构/assets/arch-`/`sbx-``产品/assets/` 等),顶层文档经**顶层相对路径**(`运维/assets/xx.svg`;00 的系统级图在 `assets/xx.svg`)引用、inline 渲染。
- **结构演进的教训(2026-06-22 创始人三次反馈收敛,务必照终态做)**:① 起初「导览式 apex(顶层)+ 各域图说(子目录)」——apex 跨子目录**链**各域、子目录文档间互引,在部分渲染环境**不显图**;② 改合并成一份顶层全量主档——**太大**(1721 行,创始人嫌大);③ **终态 = 按领域拆成多篇、都放顶层、文件名编号 01/02/03 排序**——既小而可读,又因「顶层文档 → 子目录 assets」是**单向下引**(渲染没问题,合并版已实证)而全图真渲染。**铁律:图集文档一律放顶层、引子目录 assets;切忌让子目录里的文档去跨引另一子目录的图。**各域 `README.md` 是既有设计档、不动。
## SVG house style(逐条照做,违一条图废)
- 头:`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1480 H" font-family="-apple-system,'PingFang SC','Microsoft YaHei',Segoe UI,sans-serif">`,H 按内容 700~960。
- 浅底 `#f8fafc`;主文字 `#0f172a`、次要 `#475569`;实线 `#334155`、虚线 `#64748b`(`stroke-dasharray="6 4"`)、远期紫 `#7c3aed`、红线 `#dc2626`;圆角矩形 `rx 7~12`
- 标题 24px 粗「图 N · 名称」+ 副标灰 12px + 底部脚注「映射:源档 状态:现/接/缓 设计变动须同步本图」(**全角冒号「:」**)。
- 生产维度徽章小色块+白字:安全`#dc2626` / 可观测`#0ea5e9` / 可靠`#16a34a` / 成本`#16a34a` / 数据`#475569` / 伸缩`#7c3aed` / 质量`#15803d`,按图相关维度点几个。
- 复用/边界三线:实线=现成直接用/同步,虚线=自建/解耦/远期,双线或加粗框=跨域共享/强调边界;箭头用 `<marker>`
- **SVG 文字里 `< > &` 必须转义**(`&lt; &gt; &amp;`);XML 注释禁出现连续两个连字符(`--`);所有元素在 viewBox 内不溢出。
## 图说文档结构(沿用金样板)
- **frontmatter**:date/topic/status + 「映射源档 + 各源档当前 commit hash」(`git -C <repo> log -1 --format=%h -- <源档>` 取)= **防漂移门**
- `## 0 阅读约定与同步纪律`(映射哪些档 + 同步纪律 + 本域状态分布)。
- `## 1 全图通用图例`(徽章集 + 三线 + 状态码)。
- `## 2 图集`:每图 = 嵌图 + 一段**人读散文**(完整句子、讲清这图让人看出什么、现行/远期边界、设计缝;**不要电报体黑话**——创始人铁律)。
- `## 3 图清单与状态表`(# / 图名 / 家族 / 形式 / 状态 / 覆盖内容)。
- 总图叙(apex)额外:导览式——只内联系统级跨域新图,对各域写「讲什么故事 + 命门缝 + 链接」导航段,再加按角色下钻路径。
## 三范式纪律(本图集的硬约束)
- **(a) 单源**:形式标「引」的图**绝不复制** README 已有的 Mermaid,只在散文里链接「见该域 README §X」。同一张图绝不存两处(复制=自造漂移面)。领域已画的图,总图叙只链不重画。
- **(b) 防漂移**:frontmatter 记源档 commit hash;§3 状态表逐图复述状态;收口脚本比对 hash,对不上标「待复核」。
- **(c) 状态双层**:状态码 = 现(已建)/接(待接线)/建(待建)/缓(缓做)/F(future)/废(决策史)。整图非「现」时图顶加**状态约定条**说明整图状态 + 实心/虚线含义;元素级凡 接/建/缓/future/废 一律虚线框 + 文字小标,与「现行已建」实心块一眼区分。**绝不把 future(k3s 迁移/SAA·Dify 可插拔适配)、在飞(tier2)或已废(OpenGame/玩法填参模板/15KB)画成现行;而 Nacos/RocketMQ 已是生产 runtime、应画「现/接」,勿再当 future。**(Dify 按 AGENTS.md 口径=最低优先级远期,非已废)
## 编排配方(按域并行)
- **每域一个 opus agent**(`agentType: general-purpose`,可 Write),克隆金样板:读 计划稿+金样板 md+2 张金样板 SVG+本域源档 → 逐图产出(SVG/Mermaid/引用)→ 自检 → 汇编 md。结构化返回 `{docFile, svgFiles, figureCount, svgOk, statusDisciplineNote, singleSourceNote, selfCheck, flagsForReview}`
- 多域 = `parallel()` 扇出(不同域不同文件夹、零碰撞,无需 worktree 隔离)。同域两份图说共享 `assets/` 时用文件名前缀区分(如 `arch-` / `sbx-`)。
- **汇编成 00-07 编号文档,全放顶层**(放最后,关键收尾):各域产出 → `0N-<域>图说.md`;系统级图 + 目录 → `00-系统总览图说.md`(用相对链接指 01-07)。规则:各域散文逐字保留、SVG 用顶层相对路径(`<域>/assets/xx.svg`,00 的系统级图用 `assets/xx.svg`)、「引」图 inline 其 README 的 Mermaid(带「(图源:…)」小注)。生成引擎子树只链接不并入。每篇 frontmatter 防漂移门记该域真实设计档 hash(非中间图说),各图具体映射在图脚注。(若中途产过 per-domain 子目录图说或合并大档当过渡,终态须收敛到这套顶层编号文档、删过渡件,避免双份漂移。)
- **分两期降风险**:先六域(自包含、可立全局轮廓),后生成引擎子树(最厚、且常被另一 session 在飞建——**第二期前必先跨 session 对齐边界、只链不双写**)。
## 复验脚本(主会话亲验,不轻信 agent 自检)
```python
import re,glob,xml.dom.minidom as M
for f in sorted(glob.glob("docs/architecture/**/assets/*.svg",recursive=True)):
s=open(f,encoding="utf-8").read()
try: M.parseString(s); wf="良构"
except Exception: wf="XML错!"
vb=re.search(r'viewBox="0 0 (\d+) (\d+)"',s); W,H=int(vb.group(1)),int(vb.group(2))
mx=my=0
for m in re.finditer(r'<rect[^>]*?\bx="([\d.]+)"[^>]*?\by="([\d.]+)"[^>]*?\bwidth="([\d.]+)"[^>]*?\bheight="([\d.]+)"',s):
x,y,w,h=map(float,m.groups()); mx=max(mx,x+w); my=max(my,y+h)
for m in re.finditer(r'<(?:text|line|tspan)[^>]*?\bx2?="([\d.]+)"[^>]*?\by2?="([\d.]+)"',s):
mx=max(mx,float(m.group(1))); my=max(my,float(m.group(2)))
over="溢出!" if (mx>W+2 or my>H+2) else "界内"
foot="脚注" if "映射" in s else "缺脚注!" # 注意脚注用全角「:」,别用半角冒号匹配
amp="裸&!" if re.findall(r'&(?!amp;|lt;|gt;|#\d)',s) else "ok"
print(f"{f.split('/')[-1]:30} {W}x{H} ({mx:.0f},{my:.0f}) {over} {wf} {foot} {amp}")
```
md 另核:§0-3 齐、frontmatter hash 与当前 git 一致、```mermaid 块数 = 新 Mer 图数(引图区零 mermaid)。再人工抽读 12 张最高风险图(如契约族、合规死锁)。
> **复验脚本盲区(必补一道主会话 sweep):** 上面的「脚注 ok」只验「映射」二字在不在,**查不出脚注里半角冒号 vs 全角「:」**——派出去的子代理逐图各验也会漏。主会话提交前另跑一道:对每张 `t2-*.svg` grep 含「映射」的脚注行有无 ASCII 冒号,有就批量改全角(只改脚注行、别动 `<style>` 里的 CSS 冒号)。tier2 细图扩展轮即靠这道 sweep 补逮 6 张漏网。
## 双评审揪出的六坑(画图前过 Codex+Opus 双评审,这些是真命中)
1. **漏整域**——v1 清单漏了运维域(声称 140 实列 132)。逐域核对覆盖矩阵。
2. **现行/远期错标**——现行 = 统一框架 AgentScope 上的三档(按 AI 参与深度分),引擎随游戏复杂度选 LittleJS / Phaser 变体、引擎非分档轴;**SAA 固定 16 节点线为旧路线的历史表示**(已降最低优先级,只留远期可插拔适配),画图时别把它当现行主线。整图按现行/远期分簇,逐图核状态标。
3. **缺状态列**——逐图必标状态,否则极易把 future/已废画成现行。
4. **契约/验收门画理想**——契约总览自陈「DB 镜像已漂移、CI 防漂移门缺位、Dify#6 废、第9契约五套编号相撞」;图必画**声明 vs 现实**,不画治理已到位。验收门拆「九门 harness(机制地板)」与「W-G1 开闸 6 门(发布门)」两层。
5. **「每域齐五种图」是凑数配额**——家族由内容定,别为凑类图硬画;会产 AI slop。
6. **复制 README Mermaid 进图说 = 自造漂移**——必须单源引用(只链不抄)。
## 可review设计意图
图不只好看,要让评审据图发现缝:每图标现行/远期边界、跨域接缝、待裁项;总图叙 §3 逐域点「命门缝」。第一期实证的高价值跨域图 = 失败/降级/补偿路径全景、端到端鉴权信任边界、契约镜像+防漂移现状。
## 细颗粒 detail-图说 扩展(子树放大层)
域图说之外,某个子系统要逐项放大(把总览一句话带过的「九门」「skills」「源项目契约七要素」铺成逐门 / 逐工具 / 逐要素的细图),按「族」拆 detail-图说,放在该子系统目录、单源引用总览。tier2 生成引擎子树即用此法建了 8 族 32 图,现在位仅 `tier2细节图说-G-spike-runbook.md` 一件,余已随整理归档(git 可查)(见记忆 `tier2-detail-diagrams`)。
- **编排 = draw → 对抗验证 → 族汇编 pipeline(Workflow)**:每图一个 opus 子代理,先读源档取证(绝不编)→ 画 SVG → 跑共享 `/tmp/verify_svg.py <checkKey>` 自检 → 对抗评审(内容对源 / house-style / 无 AI 味 / deepen 不 duplicate,小瑕疵就地修);一族图齐后一个 opus 汇编族图说 md(防漂移 hash + 人读散文,house-style/散文铁律/deepen 规则的正反例必须随 prompt 一起传下去)。2 轮共 66 子代理实证可复用、稳定产出过门。
- **deepen 不 duplicate**:detail 图是总览某图的逐项放大,不重画总览已有的图;承接时脚注写「(放大总览图 N)」单源引用。workflow 专设一道 `deepensNotDuplicates` 验,防换皮重画。
- **复验脚本放共享 `/tmp` 文件**,别内嵌进 JS workflow 脚本——python 正则的反斜杠会被 JS 模板字面量吞掉。
- **看图入口回链**:子树总览图说加一段「细节族图说(放大层)」指针,链各族 detail 文档,保持可发现。
> 相关:[`wave-close-checklist.md`](wave-close-checklist.md)(同步纪律入收口第 8 步)、[`staging-ops.md`](staging-ops.md)(PNG 转换在 mini-desktop)、[`saa-graph-orchestration.md`](saa-graph-orchestration.md)(生成引擎子树图的内容源)。
</content>

View File

@ -1,23 +1,12 @@
---
name: cheap-model-game-generation
description: "当需要 new-api 便宜模型成本口径、前缀缓存降本、代理旁路坑、或「模型能力画像/质量评估三层/别归模型造不出」等跨路结论时查阅:W-G1 便宜档实证。⚠️ 起新便宜档生成走 cheap-worker,勿照本篇已退役的 gamedef/factory 链路。"
---
# 便宜模型直出可玩轻游戏 · L1 生成 worker 配方W-G1 实证)
> **⚠️ 状态2026-06-26本篇是 reframe 前的旧 W-G1 / gamedef-factory 路。** reframe2026-06-25已废 gamedef + 统一 AgentScope便宜档生成已迁到 **`cheap-worker/`**Python/AgentScope + 直写 LittleJS `src/` 多文件 + import 复用 tier2 框架层 + shell-out 现有 node 工具),不再是本篇的「裸 openai client 不引 AgentScope + `generated-factory.js` + `buildGenericHostConfig` 套壳」。
> - 便宜档 Python 线现状 → memory `amodel-generation-build-state`WU-A spike+ 执行 plan `git show 8ea97234:docs/plans/2026-06-26-001-feat-cheap-worker-python-spike-plan.md`AgentScope 2.0.2 实现 API 速查 → [`../knowledge/agentscope-2.0-facts.md`](../knowledge/agentscope-2.0-facts.md)「便宜档 cheap-worker 实现 API 速查」节。
> - **仍有效(跨路通用,与 worker 实现无关)**成本口径§2、代理旁路坑§3、driver-coverage / 模型能力画像§9、质量评估三层§8
> - **已失效(被 reframe 推翻)**§1 链路factory/gamedef、§3「不引 AgentScope」VERDICT、§57 的 `generated-factory` / `buildGenericHostConfig` 套壳。
> - **勿照本篇 §1/§5/§6 起新便宜档生成 —— 走 `cheap-worker`。**
> 适用:用 new-api 便宜模型DeepSeek/MiniMax在**插件库 + 装载契约**上生成**可真玩**的 Tier1 轻游戏;**质量由 harness 门保证、非模型自觉**HJ-GEN-001
> 真玩取证门见 [`game-e2e-cdp-harness.md`](./game-e2e-cdp-harness.md) §7九门AG 假绿守卫 + H 机制/latch + I 控制手感);本篇讲**生成侧**(谁生成 / 怎么校验 / 成本 / 坑 / 自产 gatespec
> 活样板worker = `wg1/gen-worker/`lane 分支 `wg1/gen-worker`);生成产物 + harness = `game-runtime/games/_wg1-gen/`;交付报告 = `wg1/gen-worker/REPORT.md`
## 1. 链路(已退役 · 仅留指针
## 1. 链路(能生成代码 ≠ 能玩)
旧 gamedef/factory 链路 = brief → 便宜模型写 `generated-factory.js`(一个 `GameHostFactory`)→ 静态校验门 → 套官方 generic 壳(`entry-generic.js` + `buildGenericHostConfig`)→ esbuild `--global-name=__GameBundle` iife → P1 宿主 `bootGameHost` 装载 → 九门真玩、失败回喂重试。**reframe 后废**:便宜档改 `cheap-worker`Python/AgentScope直写 LittleJS `src/` 多文件、import 复用 tier2 框架层(见篇首注 + [`agentic-amodel-generation.md`](agentic-amodel-generation.md))。铁律不变——**能编译 / 能渲染 / 能动 ≠ 能玩,必须过九门真玩才算 pass**。2026-07-09 勘误:九门 pass 自此降为「未见明显死」的机械预筛地板,验收 机械预筛 ∧ 独立模型对真玩证据的玩法判定,过九门不再单独等于 pass铁律里「不由生成模型自评、必须真玩取证」的内核不变、且被强化。见质量 SoT《游戏质量与爆火能力》裁定三。
brief 题面 → 便宜模型写 `generated-factory.js`(一个 `GameHostFactory`)→ **静态校验门** → 套**官方 generic 壳**`entry-generic.js` + `buildGenericHostConfig`§6)→ esbuild `--global-name=__GameBundle` 打 iife引擎内联 ~265KB→ P1 宿主 `bootGameHost` 装载 → 本机 Chrome+CDP **九门真玩**AG 假绿守卫 + H 机制/latch + I 控制手感harness §7→ 失败回喂错因重试 ≤N。**必须过真玩门才算 pass**——能编译 / 能渲染 / 能动 ≠ 能玩。
## 2. 模型与成本2026-06-14 网关实测)
@ -30,44 +19,44 @@ description: "当需要 new-api 便宜模型成本口径、前缀缓存降本、
> 网关 `100.64.0.8:3000` 共 11 模型,上四款 + `MiniMax-M2.7-highspeed` 渠道全活(各打 1 发 ping 实测MiniMax 另有 M2/M2.1/M2.5 + highspeed 阶梯横比可按需扩。原「DeepSeek key 未激活」(作战清单:32**已过时**——经 new-api 网关四模型即时可用。
**成本口径**(¥上界,不计缓存折扣):`quota =(prompt + completion×completion_ratio)× model_ratio × group_ratio``¥ = quota / quota_per_unit(500000)× usd_rate(7.3)`。闸门 **¥0.15/款**(旧 W-G1 口径;现行便宜档 per-gen 硬闸 <¥10见 [`../knowledge/tech-decisions.md`](../knowledge/tech-decisions.md) §4。取价 `/api/pricing`、取率 `/api/status`;精确 `logs.quota` 需网关 access token`sk-` key 无管理权,故只给上界)。
**前缀缓存降本 6075%2026-06-17 实测HJ-PREFIX-CACHE-001**:把长 system/skill 前缀固定、只变尾缀 brief命中前缀缓存可省 6075% prompt 成本。3/4 关键模型支持——
- `deepseek-v4-flash` / `deepseek-v4-pro`:标准 OpenAI 前缀缓存,命中率 ~75%,用 `prompt_cache_hit_tokens` 检测;
- `MiniMax-M3`:经 Anthropic 转发、返 Anthropic 风格字段,命中率 ~74%,用 `prompt_tokens_details.cached_tokens` 检测(**勿用 `prompt_cache_hit_tokens`,此字段恒 0**
- `MiniMax-M2.7`:不支持(缓存率 0别依赖它省成本。
- 通用检测 `extract_cache_info(usage)`:先查 `prompt_cache_hit_tokens>0`DeepSeek 路),再查 `prompt_tokens_details.cached_tokens>0`M3 Anthropic 路),都无则视作 miss生成日志应记 `cache_info` 供周期成本对账。
**成本口径**(¥上界,不计缓存折扣):`quota =(prompt + completion×completion_ratio)× model_ratio × group_ratio``¥ = quota / quota_per_unit(500000)× usd_rate(7.3)`。闸门 **¥0.15/款**。取价 `/api/pricing`、取率 `/api/status`;精确 `logs.quota` 需网关 access token`sk-` key 无管理权,故只给上界)。
## 3. L1 纪律 + 坑红线
- **统一出口 = new-api 网关**——baseUrl 指 new-api + key 走 `NEWAPI_KEY`env / `.env`gitignored**绝不入库**)。**协议不锁**:同一 baseUrl 下 OpenAI `/v1/chat/completions` 与 Anthropic `/v1/messages` 并存、按模型选;不是「只 OpenAI 兼容 / 禁厂商 SDK」。旧「裸 openai client、不引 AgentScope」spike VERDICT 已随 2026-06-25 reframe 失效——便宜档现走 AgentScope cheap-worker。
- **裸 openai client不引 AgentScope**spike VERDICT框架 token 膨胀 + 本地开销吃便宜档单价);唯一出口 = new-api OpenAI 兼容网关,绝不直连厂商 SDKkey 走 `NEWAPI_KEY` 环境变量 / `.env`gitignored**绝不入库**。
- **reasoning 模型flash/pro/M2.7)烧 max_tokens 于 reasoning_tokens**`max_tokens` 必须给够,否则 `finish=length``content` 空。
- **代理旁路坑**:本机 clash`HTTP(S)_PROXY=127.0.0.1:7897`)会把发往 `100.64.0.8` 的请求经代理转发 → 502**`NO_PROXY` 大小写两版都要含网关 host**,且须在 `import openai` **之前**设。
- **输入坐标契约坑**(便宜模型与人皆易错):输入是**逻辑像素** `x∈[0,390] / y∈[0,844]`**非归一 [0,1]**`game-runtime/src/host/boot-game-host.js:295`)。写错 → 能编译能渲染能动但**点击全失灵**(详见 harness §7 G 门,正是这门逮住的)。
- **静态门禁用 token**`import` / `littlejsengine` / `Math.random` / `Date.now` / `addEventListener` / `requestAnimationFrame` / `new AudioContext` / `document.` / `window.`——生成代码只许调插件公开面 + 6 受控面getContext2d/onFrame/getInput/getAudioContext/time/random随机与时钟一律走受控 `random` / `time`(确定性可复现)。
- **bundle 入库**`dist/``.gitignore` 吞,故 bundle 输出文件名 `bundle.iife.js`(不落 `dist/`)——它是交付物须入库。
## 4. 静态校验门(跨路思路见 §2 · factory 契约已退役
## 4. 静态校验门(node 侧,出厂前省 CDP 开销
「静态门先于真玩门、把必失败件挡在 CDP 开销前」的思路跨路仍有效(见 §2 坑红线)。旧 factory 路的具体契约扫描default export + `init/update/render/destroy` 四方法齐 + `getEngine` 用法 + 禁用 token)随 gameDefinition 废除退役cheap-worker 按 `src/` 多文件工程另做静态门
抽码(` ```js ` 块)→ 契约扫描default export + `init/update/render/destroy` 四方法齐 + `getEngine` 用法 + 禁用 token 扫描)→ `node --check` 语法。不过门不进打包,把"必失败件"挡在真玩门前
## 5. 边界与生产接线(旧 factory 路 · 摘要)
## 5. 边界与未做
- 旧 factory 路只消费冻结契约(`game-host.d.ts` / 6 受控面 / `__GameBundle` / `bootGameHost`)零修改、只在 lane 分支。生产接线曾闭合(`AigcGenerateExecutor.dispatchGeneric` → worker `service.py``run_studio`)→ HMAC 回调 → 落库入 feed见 [`../rules/security-and-reliability.md`](../rules/security-and-reliability.md) §1.3);单款卡九门 = 质量门正确拒发不可玩、非故障。
- **跨路承重结论(保留)**:单点失败 ≠ 模型壁垒——scale-20 实证§9便宜模型相当能干多数失败是宿主键盘桥 host bug + driver-coverageharness 太朴素),**真难点是 harness driver 覆盖 + 宿主门面完备,不是模型能力**。旧 WG1 bakeoff 天花板 / Claude-free 评估门规划见 `git show 6d2f8789^:docs/agent-specs/_archive/2026-06-14-WG1-bakeoff天花板与Claude-free评估门-review.md`(已随 _archive 清理删除git 定位)。
- 只消费冻结契约(`game-host.d.ts` / 6 受控面 / `__GameBundle` / `bootGameHost`**零修改**;只在 lane 分支,不推 dev/2.0.0。
- **✅ 生产接线已成(3b-B 派发面,`c99014c`)**`AigcGenerateExecutor.dispatchGeneric`→真 worker `service.py`(包 `run_studio`)→HMAC 回调→落库入 feed 全链真证闭合(见 [`ai-generation-pipeline.md`](./ai-generation-pipeline.md) §⓪′)。3b-B 单款一句话「点击小怪物」真生成卡九门(Score 恒 0→**质量门正确拒发不可玩=系统履职非故障**),`run_studio` 自修 `max_repairs` 未救回。**⚠️ 但单点失败≠模型壁垒**:L1 scale-20 14 款实证(§9)便宜模型实际相当能干(主力 7/8 可玩、自写 CCD/文本HUD/网格/match 全对),多数失败是**宿主键盘桥 host bug(已修 `d754b71`)+ driver-coverage(harness 太朴素),非模型**;3b-B 那款真因(driver-coverage 或该款 hit-detection)续查并入 §9 画像。**真难点不是模型能力,是 harness driver 覆盖 + 宿主门面完备**(§9)。
- **未做/续(创始人并行)**20 款补全(scale-20 已 14 款,见 §9)、四模型横比(过门率/重试/¥每过门)、6 个 driver-coverage 失败款的 driver 增强(flappy/multiball/愤怒小鸟等)、键盘款修后批量重跑、calibration 人锚补全——规划见 [`2026-06-14-WG1-bakeoff天花板与Claude-free评估门-review.md`](../../docs/agent-specs/_archive/2026-06-14-WG1-bakeoff天花板与Claude-free评估门-review.md)。
## 6. latch 终态契约 + 套壳(旧 factory 路 · latch 承重保留
## 6. 官方 latch 终态契约 + 套壳P3 对齐2026-06-14
旧 factory 路把 L1 产出靶定为官方通用装载壳(`buildGenericHostConfig` 标准 7 插件装配 → esbuild `--global-name=__GameBundle`reframe 后套壳环节由 cheap-worker + tier2 框架层承接。**两条跨路承重契约保留**
P3 主线把 L1 产出靶定死为**官方通用装载壳**`game-runtime/games/_generic/`dev/2.0.0 `3108009`。L1 **只产 `generated-factory.js`**(一个 `GameHostFactory`),套官方壳打包
- **可测性红线**:产物须导出 `_forensicsView()`(纯只读、闭包实时值:`phase` / `score` / `remaining` / `progress` / `gameoverReason` + 实体位置),供宿主 `host.state()` 透传取证;技巧游戏另导出 `ball/paddle` 等实体 `{x,y,vx,vy,w}` 供适配真玩与手感门。
- **★ latch 终态驻留**`phase ∈ booting|playing|gameover`,进 `gameover` 后**恒驻留**(禁自动重开);宿主 `inject.ts` 每 500ms 轮询 `phase` 代发 `game_end` 结算——**瞬时终态会被漏读 → 闭环静默断裂**,故必须驻留。
- **套壳**`entry-generic.js`import `./generated-factory.js` + `src/host/generic-host-config.js``buildGenericHostConfig` 标准 7 插件装配)→ esbuild `--global-name=__GameBundle`。本 lane harness 入口(`_wg1-gen/_shared/entry-bundle.template.js`= 同 `buildGenericHostConfig` 装配的**取证超集**(多透传 recHook/onReady 供 F 门studio 调 `{canvas,seed}` 时 recHook no-op一份 bundle 两用)。
- **可测性红线**:工厂须导出 `_forensicsView() → { state: () => ({ phase, score, remaining, progress, gameoverReason, ...实体位置 }) }`(纯只读、闭包实时值);`host.state()``boot-game-host.js:283`)透传它。
- **★ P0 latch 终态**`phase ∈ 'booting'|'playing'|'gameover'`,进 `gameover` 后**恒驻留**(禁自动重开)。宿主 `inject.ts` 每 500ms 轮询 `phase` 代发 `game_end` 结算——**瞬时终态会被漏读→闭环静默断裂**故必须驻留。实证flash 能遵此契约v4/v5 latch ✓)。
- **位置态(技巧游戏)**:题面要求时额外导出 `ball/paddle` 等实体 `{x,y,vx,vy,w}`——供 harness **适配性真玩**(读球位接球)+ 确定性手感门(控制/碰撞)。
## 7. design agent 自产 gatespec旧 factory 路 · 家族二分保留)
## 7. 确定性门推广design agent 自产 gatespec适任意游戏405394d 实证
旧 factory 路把确定性手感门H/I从手写 brief 升级为 **design agent 自产、studio 抽取合入**使确定性门覆盖任意生成游戏、随生成规模自动扩张harness 复利一环)。具体挂点(`roles.py``DESIGN_SYSTEM` 产 gatespec、`studio.py``_extract_gatespec` 抽取合入)随 reframe 由 cheap-worker 承接。
确定性手感门H/I原靠**手写 brief** 喂判据——每款游戏人工写 `assertAfterPlay`/`driver`/`controlCheck`不可规模化。P1 把它升级为 **design agent 自产、studio 抽取合入**,确定性门遂适用**任意生成游戏**实证flash 的 design agent 对 breakout 产出结构合法 gatespec、studio 抽取合入成功):
- **跨路承重结论(保留)= gatespec/driver 按玩法家族二分**`a2cbabe6` 实证):离散点击目标类 → 必产 `tap-targets` + 导出 `targets[{x,y,idx,occupied}]`、绝不 `none`(否则 dispatch 面盲打卡 `H_progress`、latch 不触发);规避/推理族 → `safeOnly:true` + 导出 `safe`(仅 `_forensicsView` 测试可见、不渲染)+ 断言 `result=="win"`;安全放置族 → 断言 progress↑技巧挡板类 → `paddle-intercept``ball.x` 绝不 `ball.y`);其余 `none`。两侧 prompt 须逐字同改保公平。
- **design agent 产 gatespec**`worker/agent_loop/roles.py``DESIGN_SYSTEM` 末尾):在玩法设计末尾输出一段**机器可读 gatespec 块**,含 `exportState`(须导出哪些观测态,对齐 §6 可测性红线)/ `driver`(适配真玩出招式,如 `paddle-intercept`/ `controlCheck`(控制体 + 目标 x/ `assertAfterPlay`(进展断言)/ `expectLatch`。**坑**:模型易把 `ballPath` 写成竖直 `.y`——prompt 须明确「球水平运动 → `ballPath='ball.x'`」。
- **studio 抽取合入**`worker/studio.py``_extract_gatespec`):从 design 输出抽 gatespec 块(`json_repair` 兜模型 JSON 残缺)→ 合入 play-spec → **安全归一**(如 paddle-intercept 的 `ballPath` 误写 `.y` 自动兜回 `.x`);结果记 `gatespec` 供取证回溯。prompt 修 + 归一兜底**双保险**实证唯一内容误差ball.y已被双修。
- **★ tap/离散点击类自产 driver 缺口(3b-B 重跑实证·dispatch 面致命)**design agent 对 tap-target 类游戏(打地鼠/井字棋/点击得分)反复给 `driver:{type:none}`——dispatch 面 `run_studio(play_spec={})` **全恃自产 driver**(无手供 play_spec),致 harness 盲打打不中目标 → `moves/score` 恒 0 → **H_progress 挂 + latch 不触发**repairs=5 耗尽(3b-B 重跑 gen9306/9307 + 原 gen9303-9305 共 5 款同签名,**8/9 门过仅此门挂、游戏本体全对**`targetX/Y/radius``board``moves` 导出正确G_input/I_control/F_wiring 均过)。**根因=自产 driver 缺口,非模型/非游戏本体**(样板 tictactoe 过九门靠其 lane brief 手供 `play_spec.driver=tap-targets`)。**修向**`DESIGN_SYSTEM` 对「离散点击目标」类强制产 `tap-targets` driver(+`exportState``targets[{x,y,occupied}]`),或 `_extract_gatespec` 对 brief 含离散格位/目标时兜底注入 tap-targets。~~当前 dispatch 面一句话现生成对 tap 类不可靠,须先补此缺口~~——这是 moat『一句话现生成』腿的真·临门一脚(纯 L1 生成域)。**【已修·2026-06-16·`a2cbabe6`】** `DESIGN_SYSTEM` 规则③改**家族二分**(离散点击目标类→必产 `tap-targets`+导出 `targets[{x,y,idx,occupied}]`、绝不 none;规避/推理族 +`safeOnly:true` +导出 `safe`〔仅 `_forensicsView` 测试可见、不渲染〕+断言 `result=="win"`;安全放置族断言 progress↑;技巧挡板类仍 `paddle-intercept``ball.x` 绝不 `ball.y`;其余 none)——Python `roles.py` 与 SAA `SaaPrompts.java` **逐字同改**(规则段 1076 chars 公平性铁律)。**铁证**:tictactoe(放置族)一句话 `play_spec={}` 整链 **9/9 全绿一次过**(design 自产 tap-targets→factory 自产 targets→九门含 H_progress+latch 达终态),旧 `driver:none` 盲打卡死真因已闭;breakout 回归未误伤(恒 paddle-intercept)。**残留(正交)**:saolei 九门未翻绿=便宜模型写扫雷 factory 本身质量(`Math.random` 违禁/胜负不可达/A_boot 崩),九门**正确拦截坏游戏**=O1 factory 级生成质量(§9/scale-20 单列),非本修失败。**刻意未加 `_extract_gatespec` 关键词兜底**(易误判 paddle/flap,靠门 + 反馈自愈)。
- **意义**:手感门判据从「手写一次性 brief」变成「随游戏生成的自产物」——同一条便宜模型生成链既出游戏码、又出验它的确定性门确定性门覆盖面随生成规模自动扩张harness 复利的一环)。
## 8. 质量评估:确定性门兜底 + 人锚天花板W-G1 校准实证2026-06-14
@ -86,32 +75,7 @@ flash 打底放量 14 款(主力 8 + 压测 6),Opus 对抗审计核实(2 个 ult
- **主力 7/8 真可玩**(0 假绿):井字棋/见缝插针/打砖块/跑酷/太空侵略者/Doodle/扫雷(扫雷=borderline——本体可玩,但门弱断言经"踩雷 lose"路径满足、证据薄)。flappy 唯一未过=**driver-coverage**(升 pro 仍挂,但源码物理合理、理想 AI 得分→是 flap-to-gap driver 太朴素,**非模型天花板**)。
- **压测**:match3 ✅;**2048/Tetris/Asteroids 三款键盘游戏全挂于宿主缺键盘桥**(非模型,见 [`game-e2e-cdp-harness.md`](./game-e2e-cdp-harness.md) §8);multiball/愤怒小鸟=driver-coverage。
- **★ 模型能力画像(flash)**:文本 HUD / 网格状态机(井字棋胜负+AI、扫雷洪泛)/ 子步 swept CCD(asteroids/multiball)**均自写正确**;计时/物理精度(Flappy)需**题面 pin 死数值 + 收紧观测契约**(Doodle 实证:收紧计分 + seek-x driver → regen 首发过)。栽点主要在**静态门可拦的纪律滑坡**(Date.now/Math.random/缺 render·destroy/没调 getEngine),零 CDP 成本拦下、回喂即修。
- **★ spec 预判的门面缺口几乎全推翻**:CCD / 文本HUD / 网格 / match-finding / 手势均**非缺口**(便宜模型自带或自写;故"碰撞穿透门 J"已非急需)。**真·L0 门面缺口收敛为唯一一处=宿主键盘桥**(已 spawn_task 交 L0)。完整数据见 `git show 6d2f8789^:docs/agent-specs/_archive/2026-06-14-WG1-scale20-短板量化报告.md`(已随 _archive 清理删除git 定位)
- **★ spec 预判的门面缺口几乎全推翻**:CCD / 文本HUD / 网格 / match-finding / 手势均**非缺口**(便宜模型自带或自写;故"碰撞穿透门 J"已非急需)。**真·L0 门面缺口收敛为唯一一处=宿主键盘桥**(已 spawn_task 交 L0)。完整数据见 [`2026-06-14-WG1-scale20-短板量化报告.md`](../../docs/agent-specs/_archive/2026-06-14-WG1-scale20-短板量化报告.md)
- **M3 视觉 player 当生产过滤器**(`calibration/m3_test.json`):与门 8/9 一致、抓显性空心(flappy fun1)、漏截图不可见失败(键盘死/弱证据,恰被确定性门兜住)→ **三层互补**(门=地板 / M3=主观过滤 + 便宜 fun 信号 / 创始人=人锚)。MAE 压低待创始人对新款补锚(框架沿用 `calibrate.py`)。
- **★ 规避/推理族:题面须自产「安全位」+ 断言用胜利终态(saolei borderline 修复,2026-06-15)**:tap-targets 盲点首个对扫雷会盲踩雷秒负、核心玩法零暴露(门弱断言被 lose 路径满足→ false-borderline)。修法 = 题面令 `_forensicsView` 自产 `safe`(测试专用 x-ray,=`!cell.mine`,**勿渲染/勿给玩家避雷提示**)→ harness driver `safeOnly:true` 只揭安全格到 win → 断言改 `result=="win"`。**flash 一次过且自产 safe 正确**(`generated-factory.js:337`)→ **坐实缺口在题面、非模型能力**。通则:凡「盲点首个会致负」的家族(扫雷类),题面给 oracle 安全位 + driver 避负 + 断言用胜利终态;门只证「机制点亮+可赢可达」,好不好玩归 M3+创始人。详见 `git show 6d2f8789^:docs/agent-specs/_archive/2026-06-15-WG1-tap-targets-driver-自产缺口-L1brief.md`(已随 _archive 清理删除git 定位)。
- **★ driver-coverage 6 款闭口 + 重生成归因(2026-06-15)**:scale-20 余 6 款 driver-coverage 失败全部归因(详 `git show 6d2f8789^:docs/agent-specs/_archive/2026-06-15-WG1-driver-coverage闭口与P2对照-L1报告.md`已删git 定位)+ [harness skill §8 四因铁律](./game-e2e-cdp-harness.md))——**0 个模型造不出 / 0 个 3D 硬界**:flappy/multiball **重生成即过**(原件『缝隙贴顶』/『底部丢球未实现』=**实例缺陷非模型短板**,收紧 brief + 增强 drivervy 前瞻 / 弃守排空〕后 flash 翻绿,multiball retries=0);t2048/asteroids 宿主键盘桥(L0 已修)重打包即活;asteroids/angrybirds 盲玩=可测性边界,**经 enrich exportState(asteroids 暴露陨石坐标 / angrybirds pin+导出 launch 物理常数)+ 适配 driver(aim-fire / drag-aiming 解析)转确定性闭口**(asteroids score 0→6、angrybirds 0→20 清场)。**生成侧铁律:单件失败禁直接归『模型造不出』,必重生成区分『实例 bug / 可测性缺口 / 模型短板』**——『模型造不出』是最大伪报源(原 flappy『升 pro 仍挂=模型天花板』被本轮证伪=driver+实例)。
## 10. gamedef 路 × 保真度谱系(已退役 · 跨路红线保留)
SAA gameDefinition 路已随 reframe 废A-model 直写真 `src/`。Plan B Phase-2 质量门real-play CDP + rubric + 三契约)逆出的**三条跨路红线保留**(详 `git show 6d2f8789^:docs/agent-specs/_archive/2026-06-21-planB-phase2-closeout.md`,已随 _archive 清理删除、git 定位):
- **别归『模型造不出』**(呼应 §9 末红线):同档 §9 实证 flash / 九门路曾产可玩打砖块 → 非绝对天花板;只在一条配置测 2 轮就判天花板 = 伪报,根因常是模型选择 + prompt 局限。
- **杠杆排序(复杂机制保真)**few-shot 对复杂机制无效LLM 无视/复现不出)< 换更强模型 / skeleton-first 确定性骨架结构强制LLM 只填参绕过坍缩简单玩法 few-shot + 保真铁律有效
- **质量门 ≫ gen 自评 accept**gen 的「accept ≥80%」放过 assets=0 / 静止不可玩 / 不忠实 / 雷同 / 几何占位 的游戏——只有 real-play帧变=真玩)+ rubric设计保真+ 三契约逮得住,质量门不可由 gen 自评代替(此结论跨路有效,现行便宜档九门 + LLM 丰富度验证同理)。
## 11. 便宜档 AgentScope harness 失败模式:小瑕疵→整文件重写→漏参熔断死圈2026-07-08 根因实证)
一次真实失败运行(`amgen-dify-9da8f701`brief=躲避掉落街机,最终 `ok=False`)暴露出一条**结构性死圈**,它不是玩法差、不是模型造不出,而是工具面缺一条增量修改路径把「可恢复的小瑕疵」推成了「整局失败」。链条是这样的:agent 写出的 `game-logic.js` 带了个小红线违规——`rng()/nowMs()` 在 ctx 不可用时回退用了 `Math.random()/Date.now()``game-logic.js:125/127`,`check``\bMath\.random\s*\(` 纯文本正则照样命中,判 FAIL;agent **正确地**想去修它,但便宜档六工具面只有整体覆盖的 `write_file`、没有增量 edit——于是「改三行小函数」被迫变成「重写 22KB 整文件」,而 MiniMax 在吐这么长的 tool-call 参数时**反复把 `path` 字段整个漏掉**Service 日志 `Input validation failed for tool 'write_file': 'path' is a required property` 连撞四次,前面还先幻觉调了个根本不存在的 `edit` 工具);四次相同签名失败触发 tier2 `stuck` 熔断,`ChatService.run``Tier2CircuitBreak` 强杀回合,**九门收口从未运行**`_wg1-gen/<id>/` 目录不生成、无 verdict,`judge_cheap_verdict({})` 判未过 → `ok=False`
几条容易误判的地方,写清避免下次栽:
- **`check` 工具 `state=success` ≠ 结果 PASS。** AgentScope 记的是工具执行成功,不是校验通过;`read_file` 读不到文件也返 `"ERROR: ..."` 文本、同样记 `state=success``cheap_toolkit.py`)。判成败要看工具的**返回内容**,不看 state。
- **丰富度分与 `ok` 无因果。** 本局丰富度 8/12、degraded=False 仍 failed——richness 是 reply 结束后另跑的非阻塞旁路评分,`ok` 只认九门 verdict。别拿丰富度当上线判据。
- **品类跑偏≠模板锚死,且与 `ok` 无因果。** 本局产出「按序点击」而非 brief 的「躲避掉落」,但对照另一发真运行 `amgen-dify-1783477578`(同为动作类 brief、同六工具面成功把点圆模板改成了纵向卷轴射击——**模型有能力偏离模板**,跑偏更多是「反复找不到 brief 文件 + 无同品类范例」叠加的把握不稳,不是「克隆 _template 只改 game-logic」硬锚。且就算做对品类,红线没修 + 熔断一样 failed。
- **读多写少(读列 : 写 ≈ 3.6~25:1是系统性常态,不是杀手。** 九发对照全部如此,成功局比本局空转还狠。空转烧钱耗时,但不决定成败;真杀手是上面那条死圈,一旦产物带个需小修的红线就高概率复现,而带**增量 Edit 工具**的运行天然规避它。
**非根因清单**(别把症状当根因):不是预算/成本¥4.32 远低于软停 ¥10、不是上下文压缩崩、不是超时/轮数闸、不是 L1 写保护误伤(错误是「缺 path」不是「拒写」)。
**附带的可追溯性缺口**:brief 原文在 worker/Service 任何日志与产物里都**没落盘**worker 只记 trace_id/gameId、Service 没记 input、agent 找的 `start.brief` 根本不存在)——生成的核心输入事后不可审计,该补落盘。
修法方向(不写代码,经 Codex+Opus 双评审校正,详见计划档):①给便宜档一条**可靠的增量 edit/apply-patch 工具**(read→精确替换→复用 `cheap_run.write_file` 的 L1/L3+whitelist 回写、不触发 check、加入软停 blocked-tools) + 对「连撞同签名畸形参数」做**窄口径**纠偏(仅 schema 缺字段类、不重置 stuck 计数、cheap-only 别改坏共享 tier2 熔断)——治死圈这一终止模式的最高杠杆;②**`_template` 的 rng 本就合规(`ctx?ctx.random.next():0`)、根本无 nowMs,违规是模型无视合规模板自造的**——不是"改模板",而是让 check 红线报错**指到行并点破「函数内回退也算裸调、正则按纯文本命中」**+ 提示层加「照模板逐字克隆、禁自造裸回退」与 check-early(写完即 check),比造工具更上游更省;③补 brief 落盘(落 `game_dir/evidence/` 脱敏限长、别落工程根——源工程可进素材市场交易)。**死圈只是失败模式之一**:实测 23 局真过门率仅 ~39%(跑到门 83% 但多为 E/G/H 玩法坏死挂门),三修消除死圈但到不了 MVP ≥80%,80% 缺口(玩法正确性)属质量线另一工作面。2026-07-09 勘误:这三个数字与归因已被五路审计推翻——「真过门率 ~39%」是 stripCode 假阴修复前所测,字符串内 `//`(如赛博文案 'JACK IN // FLOOR'被误当注释抹掉真代码、把写对的模型反复拒到熔断烧钱commit 0be0f61c修复并给 per-genre 起点后 xthemeit5-lockcorer1 三批各 5/5、模型未变过门率随工具移动不随模型「多为 E/G/H 玩法坏死」大半是误标真因多为判卷驱动器合约缺口——进不去菜单认不出终局score 键没对上,小补丁即过,真坏死是少数且已被 per-genre scaffold 修掉故「80% 缺口=玩法正确性」不成立。诚实边界:只能判 80% 不是 MiniMax 天花板,不能反推已稳定 95%+。见质量 SoT《游戏质量与爆火能力》裁定三与同日诊断档 `docs/brainstorms/2026-07-09-生成线harness方向性诊断与换轴方向.md` §2.5。)
- **★ 规避/推理族:题面须自产「安全位」+ 断言用胜利终态(saolei borderline 修复,2026-06-15)**:tap-targets 盲点首个对扫雷会盲踩雷秒负、核心玩法零暴露(门弱断言被 lose 路径满足→ false-borderline)。修法 = 题面令 `_forensicsView` 自产 `safe`(测试专用 x-ray,=`!cell.mine`,**勿渲染/勿给玩家避雷提示**)→ harness driver `safeOnly:true` 只揭安全格到 win → 断言改 `result=="win"`。**flash 一次过且自产 safe 正确**(`generated-factory.js:337`)→ **坐实缺口在题面、非模型能力**。通则:凡「盲点首个会致负」的家族(扫雷类),题面给 oracle 安全位 + driver 避负 + 断言用胜利终态;门只证「机制点亮+可赢可达」,好不好玩归 M3+创始人。详见 [`2026-06-15-WG1-tap-targets-driver-自产缺口-L1brief.md`](../../docs/agent-specs/_archive/2026-06-15-WG1-tap-targets-driver-自产缺口-L1brief.md)。
- **★ driver-coverage 6 款闭口 + 重生成归因(2026-06-15)**:scale-20 余 6 款 driver-coverage 失败全部归因(详 [`driver-coverage 闭口报告`](../../docs/agent-specs/_archive/2026-06-15-WG1-driver-coverage闭口与P2对照-L1报告.md) + [harness skill §8 四因铁律](./game-e2e-cdp-harness.md))——**0 个模型造不出 / 0 个 3D 硬界**:flappy/multiball **重生成即过**(原件『缝隙贴顶』/『底部丢球未实现』=**实例缺陷非模型短板**,收紧 brief + 增强 drivervy 前瞻 / 弃守排空〕后 flash 翻绿,multiball retries=0);t2048/asteroids 宿主键盘桥(L0 已修)重打包即活;asteroids/angrybirds 盲玩=可测性边界,**经 enrich exportState(asteroids 暴露陨石坐标 / angrybirds pin+导出 launch 物理常数)+ 适配 driver(aim-fire / drag-aiming 解析)转确定性闭口**(asteroids score 0→6、angrybirds 0→20 清场)。**生成侧铁律:单件失败禁直接归『模型造不出』,必重生成区分『实例 bug / 可测性缺口 / 模型短板』**——『模型造不出』是最大伪报源(原 flappy『升 pro 仍挂=模型天花板』被本轮证伪=driver+实例)。

View File

@ -1,20 +1,13 @@
---
name: contract-first-development
description: "当多工位(前端/后端/SDK/AI/数据)并行开发同一交付时使用:先锁 contracts/ 八类契约、各自按契约 mock 对方接口解耦并行、最后集中联调,含 Day-0 契约清单、mock 策略、真实对接切换与联调规则。"
---
# 契约先行与并行解耦联调手册contract-first-development
> 蒸馏来源:`docs/superpowers/specs/mvp-execution-spec-design.md`§3 契约先行 / §6 并行解耦与 Mock / §8.3 联调规则)、`docs/architecture/架构/README.md`§6 联调协议)、`docs/architecture/架构/README.md`§7.7 API 契约与版本)。
> 蒸馏来源:`docs/superpowers/specs/mvp-execution-spec-design.md`§3 契约先行 / §6 并行解耦与 Mock / §8.3 联调规则)、`docs/architecture/系统概要设计-开发团队版.md`§6 联调协议)、`docs/architecture/系统概要设计-技术决策版.md`§7.7 API 契约与版本)。
> 适用:多工位(前端/后端/SDK/AI/数据)并行开发同一交付时,先锁契约、再各自 mock 解耦、最后集中联调。
> 配套:工程规范(错误码/API 路径/`-api` 包)见 [`../rules/engineering-conventions.md`](../rules/engineering-conventions.md);新模块契约落地见 [`./add-business-module.md`](./add-business-module.md);生成链路见 [`./agentic-amodel-generation.md`](./agentic-amodel-generation.md) 与编排 [`./saa-graph-orchestration.md`](./saa-graph-orchestration.md)SDK 契约见 [`./runtime-and-multichannel.md`](./runtime-and-multichannel.md);任务协议见 [`../workflows/ai-development-protocol.md`](../workflows/ai-development-protocol.md)。
> 配套:工程规范(错误码/API 路径/`-api` 包)见 [`../rules/engineering-conventions.md`](../rules/engineering-conventions.md);新模块契约落地见 [`./add-business-module.md`](./add-business-module.md);生成链路契约见 [`./ai-generation-pipeline.md`](./ai-generation-pipeline.md)SDK 契约见 [`./runtime-and-multichannel.md`](./runtime-and-multichannel.md);任务协议见 [`../workflows/ai-development-protocol.md`](../workflows/ai-development-protocol.md)。
---
## 目标
> **现状说明**:方法论(先锁契约 → mock 并行 → 集中联调)现行有效;文中 Day-N / WS15 具体数字出自已降级为视图的三周 execution spec日常排期按 16 周主计划波次走。
**Day0开发首日半天全员锁定契约**,写入 `contracts/` 并提交 git之后各工位基于契约 **mock 对方接口独立开发**,集中联调在 **Day11** 开始。把"等对方接口"的串行依赖,换成"对着契约并行"。
## 前置
@ -73,8 +66,6 @@ Day11~Day12集中联调只联调、不加功能
| 后端新增/变更 API | **必须同步** `contracts/api-schemas/` 并通知前端MVP spec §8.3 |
| 契约定义顺序 | 后端**先写 `-api` 包的 VO/DTO**,前端据此定义 TS 类型(开发团队版 §6.1 |
> "开发团队版"/"技术决策版" = 两版《系统概要设计》,已归档 `_archive`,节号按历史读;现行规范见 [`../rules/engineering-conventions.md`](../rules/engineering-conventions.md) 与 [`docs/architecture/README.md`](../../docs/architecture/README.md)。
>
> API 演进规则:新增字段不算 breaking删除/重命名字段 = 升版本(技术决策版 §7.7)。
---
@ -97,7 +88,7 @@ Day11~Day12集中联调只联调、不加功能
| 观察点 | 地址 / 工具 |
|---|---|
| 后端 API 文档 | Swagger / Knife4j `http://localhost:48080/doc.html` |
| 生成链路日志 | ~~Dify UI `http://localhost:3001`~~(已降级远期未部署)→ 现行=编排器批跑日志 `git show 6d2f8789^:docs/agent-specs/2026-06-09-agent-loop-v1/orchestrator/`(已删,git 可查) + new-api 网关 `http://100.64.0.8:3000` |
| 生成链路日志 | ~~Dify UI `http://localhost:3001`~~(已降级远期未部署)→ 现行=编排器批跑日志 `docs/agent-specs/2026-06-09-agent-loop-v1/orchestrator/` + new-api 网关 `http://100.64.0.8:3000` |
| SDK 事件流 | game-studio 开发模式 **DebugPanel**(实时 postMessage 事件流,开发团队版 §6.3 |
---

View File

@ -1,8 +1,3 @@
---
name: doc-organizer
description: "创始人手动触发「整理/清理文档」时使用:增量窗口体检 → fan-out 三轴分类(过期历史档/核心设计档措辞/总账)→ 编清理计划 → 对抗评审 → 创始人批准后才归档/压缩/回填;阶段一只读、批准前零文件改动。"
---
# 文档整理助手doc-organizer
> **触发**:创始人**手动**`/doc-organizer` 或"整理文档/清理文档")。
@ -68,10 +63,10 @@ Workflow({ scriptPath: ".agents/tools/doc-organizer-analyze.mjs",
## 安全红线(不可破)
1. **阶段一绝不改文件**;阶段二只动**已批准**清单内、文件名 ≤ 当前未完成波次的历史档;**最新日期在飞档不碰**(脚本动态取目录内最新日期档作在飞保护锚旧硬编码日期已废2026-06-17 改)。
1. **阶段一绝不改文件**;阶段二只动**已批准**清单内、文件名 ≤ 当前未完成波次的历史档;**最新日期在飞档不碰**(脚本`2026-06-16-*` 已硬拒归档)。
2. 破坏性操作(归档/压缩/git rm**全程 git 跟踪可回滚**;提交前守卫核验:无外来文件、无在飞档、无 `orchestrator/*` 等未跟踪误纳。
3. **检测自动、判断留人**:脚本只报"疑似/超标"KEEP/ARCHIVE/改不改由 agent 据 _index+MEMORY 裁决、再由创始人批准。
4. 白名单按现行路径维护:`orchestrator/` 已删git 可查)、`channel-spike/` 已迁 `spikes/channel-spike/``generation-spike/` 已不存在
4. 白名单永留:`orchestrator/`(活工具箱)、`channel-spike/`(活 spike`generation-spike/`(可复现证据)
5. 多 session 共享树push 前 fetch 核对、撞车的别人 untracked 文档先避让(见 [[multi-session-shared-tree-push-conflict]])。
## 配套文件
@ -82,4 +77,4 @@ Workflow({ scriptPath: ".agents/tools/doc-organizer-analyze.mjs",
| `.agents/tools/doc-organizer-analyze.mjs` | 阶段一分析相 Workflow 脚本(三轴 fan-out 分类,只读) |
| `.agents/tools/doc-organizer-state.json` | 上次清理时间/commit增量窗口锚点勿手改 |
| `.agents/rules/engineering-conventions.md §10` / `docs/agent-specs/_index.md` | 判据来源 / 活地图 |
| `git show bb7c2baf^:docs/agent-specs/2026-06-16-agent-specs目录治理-review.md`已随域化重构删除、git 定位) | 首轮治理留痕(判据与全量分类范例) |
| `docs/agent-specs/2026-06-16-agent-specs目录治理-review.md` | 首轮治理留痕(判据与全量分类范例) |

View File

@ -1,8 +1,3 @@
---
name: drive-remote-claude-tmux
description: "从控制机经 ssh+tmux 双向驱动远端机上交互式 Claude Code(send-keys 派活/capture-pane 读屏/专用 worktree 会话/git commit 作完成判据)时使用。⚠️ 退役候选:原前提 6c6g 大脑线已停用,去留待创始人拍;tmux 桥技术仍可复用。"
---
# 远程驱动交互式 Claude Codessh + tmux
> **用途**从控制机6c6g 大脑线)经 ssh 进远端开发机Mac/Linux**双向驱动跑在 tmux 里的交互式 Claude Code**——派活(`send-keys`+ 读屏(`capture-pane`)。**不走 ACP、不走 `claude -p` headless**,驱动的就是带完整上下文的交互式 TUI创始人能同时 `attach` 旁观/随时夺键。

View File

@ -1,21 +0,0 @@
# 引擎能力摘要(给生成 agent 的 ≤3KB 版)
引擎 = LittleJS 1.18.19。**你几乎不直接碰它**——重活(粒子/音频/物理/手感/调色)都被 L2 插件封装好了,你调 `plugins.<键>` 就行。这份摘要只让你知道「插件背后是什么、坑在哪」,以及极少数要经 `ctx.getEngine()` 够引擎时的事实。完整逐函数签名在 `game-runtime/docs/ENGINE-CAPABILITIES.md`277KB 引擎 .d.ts 的抽取版),本篇是够用的地板。
## 铁律:先找插件,别碰引擎
- 画面:画在 `render(g)``g`= 引擎 `mainContext`,一块 Canvas2D 叠加层)。用 `g.fillRect/drawImage/fillText` 直接画即可,**别** `ctx.getContext2d()`(那是另一块 overlay
- 粒子/打击感 → `plugins.juice`;音乐音效 → `plugins.audioMusic`;缓动手感 → `plugins.gamefeel`;碰撞 → `plugins.collision`;运动 → `plugins.physics`;调色 → `plugins.palettePost`。**这些引擎里都有,但一律走插件**——直接碰引擎会踩下面的坐标系坑。
- 时间走 `ctx.time()`(或 `.nowMs()`)、随机走 `ctx.random()`(或 `.next()/.range(a,b)`)——**确定性可复现**,别用引擎全局 `time`/`rand`(受暂停/timeScale 污染、不可复现)。
## `ctx.getEngine()`:极少用,用则容错
返回一条受控透传通道(粒子发射器 / 音频合成核 / 数学缓动库)——**但这三样插件都已封装**,正常你用不到。`getEngine()` 在无引擎环境node/桩)返回 `null`,碰它必须先判空降级,别裸调。真要用数学缓动,`plugins.gamefeel.easing` 有 13 条曲线,比引擎全(引擎只有 lerp/smoothStep/oscillate没有 elastic/back/cubic
## 坐标系坑(碰引擎才会踩,也是插件封装它的原因)
- 引擎默认**世界坐标**、几何是 `Vector2` 类;插件受控面是朴素 `{x,y}` number 对。
- 引擎 AABB 用 `size`=**全宽全高**;插件 AABB 用 `{x,y,hw,hh}`=中心+**半宽半高**——差 **2× 换算**。混用必错,所以一律走插件的 `{x,y,w,h}`/`{x,y,hw,hh}` 形状。
- 引擎碰撞查询只返 bool 或命中位置,**没有**穿透深度/MTV 法线/标量 t`{hit,depth,nx,ny}` 这种结构化结果走 `plugins.collision`(它替你补齐)。
## 几个「引擎有 vs 没有」的事实(省得你猜)
- 音乐**没有** `Music` 类——引擎是 `ZzFXMusic`/`Sound.playMusic()`;你走 `audioMusic` 就别管这个。
- **没有** `engineUpdate` 导出——主循环是引擎内部 RAF你只在 `update(dt)``bundle.tick(dt)` 驱动插件,别想手摇引擎帧。
- 成就/勋章系统Medal、GLSL 后处理注入引擎里有,但**非本档范围**,别用。

View File

@ -1,62 +0,0 @@
---
name: execution-plan-slicing
description: "当写或评审多单元执行 plan、或感到「每到下阶段前面阶段的工作都要返工」时使用:辨别横切关注面 vs 纵切成果切片、诊断返工信号、原地换轴重组、「设计 HOW 移交/执行契约必留」判据,及换轴必查七坑。"
---
# 执行 plan 切分轴 —— 横切关注面 vs 纵切成果切片
> 一份多单元的执行 plan,工作单元按什么轴切,决定它能不能照着走。切错轴,执行时每推进一个单元都要回头补前一个,表现为"每到下个阶段,前面阶段的工作都要重做"。
## 两种切分轴
| 轴 | 怎么切 | 适合 | 风险 |
|---|---|---|---|
| 横切关注面(facet) | 按活动类型 / 架构面切单元(框架、实例、校验、标准、门……) | 给"设计怎么落地"建结构索引——每个面对应设计 SoT 一节 | 当执行序列用会返工 |
| 纵切成果切片(slice) | 按可独立交付的成果切单元,每片自带它要的全部关注面 | 执行序列——按什么顺序一步步推到上线 | 难在找准"什么算一个完整成果" |
横切答"设计有哪几个面",纵切答"按什么顺序交付"。同一批工作可以两种视图并存,但**执行只能照纵切走**。
## 什么时候横切会返工(诊断信号)
- 一个交付成果的**目标**和它的**实现手段**被切到了不同单元(目标挂 A、手段在 B/C)。做 A 时手里没有实现工具,必须回头做 B/C 才能推进。
- 一个真实成果**纵向穿过多个关注面**——要框架 + 实例 + 校验 + 标准 + 门一起到位才算交付,而它们分散在不同单元。
- 主观信号:推进时反复感到"每到下个阶段,前面阶段的工作要重做"。
**依赖 ≠ 返工**:依赖是 A 完成后 B 才开始、A 不回改(健康);返工是做 B 时发现 A 没做够、回头改 A(切分错)。横切单元之间常是后者伪装成前者。
## 换轴动作(重组,不推翻)
1. **横切结构不作废,降为"设计面对账"**:保留一张"单元 → 切片"映射表,作切片落地时下钻设计 SoT 的索引。横切的架构一致性(同口径同契约)由设计 SoT + 机器门保证,对账表只做映射。
2. **执行按纵切切片走**:每片自带框架 / 实例 / 校验 / 标准 / 门,做完即交付、不回头。完成度回填对账表。
3. **切片按价值排序**;切片间真实依赖要**标实**,别把"晚做"误写成"无强依赖可并行"——若 B 用到 A 在某里程碑才建成的基础设施,那就是实依赖,画依赖边、不画"可并行"。
4. **三层文档定位**:设计 SoT(怎么设计)→ 执行序列 SoT(按什么顺序)→ 切片细化 plan(具体实现单元 / 文件 / 测试)。换轴产出的是中间层;每个切片开工前据设计 SoT 产一份它的细化 plan 再交付。
## 设计 HOW 移交、执行契约必须留(换轴最易丢的判据)
换轴压缩 WU 详细时,要分清两类细节,**只有第一类能移交、第二类必须留**:
- **设计 HOW(移交设计 SoT)**:架构形态、协议选型、能力矩阵、ADR——回答"为什么这么设计"。本就该在设计 SoT,执行 plan 重复一份只会双维护漂移。换轴时移交,靠"单元 → 切片"对账表的指针下钻。
- **执行契约(必须留在切片,或在切片细化 plan 承接、指针不悬空)**:迁移链路、灰度 flag、对照门槛、幂等、在途任务处理、配额扣退、切换门槛、回滚触发——回答"按计划执行时必须遵守什么、不做就出事"。它们不是设计,设计 SoT 的通用协议层不覆盖这种执行特定编排;压掉就丢了安全约束。
判据一句话:**这条细节是"执行时要照着做、不做会出事"(回滚 / 幂等 / 在途)→ 执行契约,留切片;还是"架构为什么这么设计"(形态 / 协议 / 矩阵)→ 设计 HOW,移交设计 SoT。** 换轴时逐 WU 过一遍,别把执行契约当设计细节一起压掉。
## 治理约束(别制造影子 SoT)
- **canonical 唯一性门**:同 topic 至多一份 canonical。换轴**原地重构现有 plan**、不新建第二份;文件名保留(防止引用它的活档死链);旧主轴内容降为对账小节(留痕、不删)。
- frontmatter `supersedes` 记"自身旧主轴版",git 留演进痕。
## 换轴时必查的七坑(双评审 + 执行核查实证)
- **过时口径搬运 → 假前置门**:旧 plan 的某些状态可能已被设计 SoT 推翻(例:plan 写"协议定级待创始人拍",而设计 SoT 已三处明写"定级已冻")。照搬就把已就绪的执行项错挂成"等决策",制造不存在的阻塞。换轴时逐项核对设计 SoT 的**当前**状态,别信旧 plan 的措辞。
- **对账表漏面**:横切有几个面,对账表就要覆盖几个(例:声称"八面"却只列六面,漏掉的面在换轴里掉落)。
- **元叙述**:执行 SoT 别写"本文回答 X 不回答 Y、读本文你会知道……"这类教读者怎么读的话——直陈内容(散文红线,见 [`../../AGENTS.md`](../../AGENTS.md) §6 第 12 条)。
- **评审状态漂移**:重构时 frontmatter 的评审状态要对齐当前这一版,别把旧版的"已过评审"字面带过来误导读者与机器门。
- **执行契约被当设计细节压掉**:换轴压缩时把“按计划执行必须遵守的约束”(迁移 / 灰度 / 回滚 / 幂等 / 在途 / 配额)误当设计 HOW 一起移交,执行到具体切片时丢了安全约束。判据见上节;换轴后逐 WU 核对执行契约有没有活的归宿(留切片 or 细化 plan 指针不悬空)。
- **可验证判据停在“有判据”、没钉死阈值**:写完成判据时,判据里的数值边界(收敛环追加轮数上限、达标 delta 阈值、灰度门槛、基建线最小验收)要么当场钉死,要么明写“细化 plan 未填此值不得开跑”。只写“达到同等过门率 / 不无限调 / 加显式上限”而不给数,机器和人都判不了,等于没判据。
- **依赖图把排队顺序画成依赖边**:切片按价值串行 ≠ 切片间有数据依赖。串行排队若用实线箭头画,读者会误读成阻塞依赖、把本可并行准备的切片错误串死。排队顺序与硬前置依赖用不同线型(实线 = 硬前置,虚线 = 排队 / 供给)并加图例。
## 来源
2026-06-26 plan①《生成引擎统一执行计划》换轴:横切 WU-A..F(框架 / 实例 / 八面 / 采标准 / 控制面 / 验收门)→ 纵切三切片(便宜档达标上线 / tier2 go/no-go / A11 调整回路)+ 基建线 + 已冻协议基线。触发 = 创始人 catch 两点:"每到下个阶段前面阶段的工作都要重做"(返工信号)、"主线是方案计划?没有执行计划吗?"(缺执行序列这个中间层)。双评审(Opus + Codex 末轮)揪出假前置门 / 对账漏面 / 元叙述 / 评审状态漂移四坑;落档后创始人再 catch“按计划执行会不会丢设计细节”,核出第五坑——切片一的便宜档灰度迁移契约 11 项(执行契约)被当设计细节一起压掉,补回切片一,并立“设计 HOW 移交 / 执行契约必须留”的判据。末轮 Codex 四维度专项评审(重连完整产出)再揪两坑补成第六、七坑——可验证判据未钉阈值(n=5 追加轮数上限、auto-vs-golden delta、基建线最小验收都曾停在“有判据无数值”)、依赖图把价值排队画成实线依赖边(易被误读成阻塞、串死本可并行的切片)。
> 相关:[`feature-design-doc.md`](feature-design-doc.md)(设计文档怎么写)、[`wave-close-checklist.md`](wave-close-checklist.md)(收口同步纪律)、[`../workflows/ai-development-protocol.md`](../workflows/ai-development-protocol.md)(plan 在协议中的位置)、[`../rules/engineering-conventions.md`](../rules/engineering-conventions.md)(canonical 唯一性门 §10)。

View File

@ -1,99 +0,0 @@
---
name: feature-design-doc
description: "当新功能/跨模块/改用户可见行为/触及外部服务·支付·数据进入设计阶段时使用:产出 WHAT+HOW 合一的功能设计文档——文字+Mermaid 为事实源、SVG/HTML 给人看,含第 0 步 SoT 注册表查证、sot-impact/上级申报、防漂移单源与文档骨架。"
---
# 功能设计文档作业手册feature-design-doc
> **定位修订(2026-07-09,创始人)**:两份产物重新定位——**人审版 = brainstorm / SoT 设计文档**(方向诊断落 brainstorm,设计本体直接改所属 SoT,人审对象就是 SoT diff 与 brainstorm),**执行版 = `docs/plans/` 的 plan**(工单六要素,可派单)。已有 SoT 的改动**不再另造平行"功能设计文档"**——先改 SoT 的意识是硬要求;独立设计档只留给"尚无 SoT 的全新子系统"(产出后它本身成为/并入 SoT)。本 skill 的骨架、图规范、防漂移纪律对 SoT 设计档与上述独立设计档继续适用。
>
> 每个有真实复杂度的功能,开工前产出**一份**「功能设计文档」,取代旧的 `-review.md` + `-execution.md` 双档。一份文档同时承载 **WHAT**(意图/目标/边界/验证)与 **HOW**(方案/步骤),并以**一式两份**的表达服务两类读者:**文字 + Mermaid** 给人和 AIAI 主靠它),**SVG / HTML** 给人(更重要,看图即懂边界与核心思想)。
## 何时用
- 新功能、跨模块、改变用户可见行为、触及外部服务 / 支付 / 数据的**设计阶段**。
- 简单、局部、低风险任务**不需要**——直接做,别为一次性改动套重型文档。
## 第 0 步(强制):先查 SoT 注册表
写任何设计前,先读 [`docs/architecture/README.md`](../../docs/architecture/README.md) §2 注册表与 [`docs/agent-specs/_index.md`](../../docs/agent-specs/_index.md) 在飞板:本设计触及哪些既有 topic?是修订某个 SoT、新建 topic,还是纯留痕?把结论写进 frontmatter 的 `sot-impact` 字段(docs-gate G6 机器强制)。这一步治的是"新设计漏读既有设计/全局设计"——跳过它,门会把设计档拦在提交外。收口时按申报把可存留结论折进对应 SoT,设计档降为留痕。
## 这份文档承载什么WHAT + HOW 合一)
| 段 | 回答 | 要点 |
|---|---|---|
| 意图 | 为什么做 | 解决什么真实问题、不做会怎样 |
| 目标 | 要达成什么 | 尽量可量化(指标 / 验收线) |
| 边界 | 做 / 不做 | in scope / out of scope 一眼分清,挡住范围蔓延 |
| 方案 | 怎么实现(架构级) | 模块如何协作、关键设计决策与取舍 |
| 步骤计划 | 分几步落地 | 每阶段:交付物 / 验证 / 依赖 / 风险 |
| 验证 | 怎么算成功 | 验收标准、可观测信号 |
**禁令(硬约束)**:禁代码细节(函数签名 / 伪代码 / 具体实现——那是写代码时的事);禁黑话 / AI 味 / 元叙述;方案与步骤只写**架构级 HOW**(做什么、谁产出什么、怎么验),不写代码级 HOW。
## 一式两份:四种表达的分工
| 媒介 | 读者 | 角色 | 在 md 里 |
|---|---|---|---|
| **文字(中文散文)** | 人 + AI | **事实源**,自洽——脱离图也能读懂全部设计;后续 AI 主要消费它 | 正文 |
| **Mermaid** | 人 + AI | 结构图(流程 / 状态 / 时序 / 依赖),文本可 diff、随文档同源 | 内联 ```mermaid``` 块 |
| **SVG** | 人(**更重要** | 核心概览 / 边界 / 关系大图,看图即懂;颜色舒适、留白、核心路径高亮 | `assets/` 引用 inline |
| **HTML** | 人(可选增强) | 需交互 / 超丰富排版时的独立澄清页 | **链接**打开,不内联 |
**渲染现实(别踩)**Markdown 渲染器GitHub / Gitea**直渲 Mermaid 与 inline SVG**,但**不内联渲染独立 `.html`**md 内的 html 标签多被过滤。所以——md 阅读流里**能直接看到的 = Mermaid + SVG**HTML 只作「🔗 在浏览器打开交互版」的链接,**非必需**SVG 能讲清就不上 HTML。
**媒介怎么选**:结构关系 / 流程 → Mermaid「一眼看懂全局 / 边界 / 核心思想」→ SVG本文档的门面确需交互可展开、可切换、可点选→ HTML 独立页。
## House style复用不另起
功能设计文档的所有图,**配色 / 三线语义 / 徽章 / 脚注 / 转义 / 复验脚本一律照** [`architecture-diagram-atlas.md`](architecture-diagram-atlas.md):浅底 `#f8fafc`、主字 `#0f172a`、实线=现行 / 虚线=自建远期、远期紫 `#7c3aed`、红线 `#dc2626`、圆角 `rx 7~12`、脚注全角「」、SVG 文字内 `< > &` 必转义。
**侧重差异**atlas 面向「据图找缝」,**功能设计文档面向「快速理解」**——更重留白、层次分明、核心路径高亮,别堆信息密度。颜色要舒适、对比柔和。
- SVG 在 6c6g 出文本PNG 批量转在 **mini-desktop**6c6g 禁 chrome。见 [`staging-ops.md`](staging-ops.md)。
## 防漂移:一式两份的单一事实源
「两份」绝不能平等维护,否则必漂移(本项目反复踩的坑):
- **文字 + Mermaid = 事实源**(随 md 走、可 diff**SVG / HTML = 派生视觉**。
- 设计变更:**先改文字 + Mermaid再据此更新 SVG / HTML**;图文冲突**以文字为准**。
- frontmatter 防漂移门(复用 atlas记关联**代码 / 契约的 commit hash** + 图清单;收口比对 hash对不上标「待复核」。
- 权威**细节**只写在文字里(图里重复一份 = 又一个漂移面);图只承载**结构与边界的直觉**。
## 文档结构骨架
```
---
date / topic / status草稿 | 评审中 | 已批准 | 已实现)
sot-impact: 新建 topic X | 修订 topic Y | 纯留痕 # G6 申报(第 0 步的产出)
上级: <仓根相对路径> # G7 谱系单指针:本档的直接上级(通常是所属执行线的计划/SoT);填不出来 = 上级还没认领这条线,先回写上级再开工(§10.8)
关联: <代码 / 契约路径 + commit hash> # 防漂移门
图清单: [图1 概览, 图2 边界, ...]
---
# <功能> 功能设计
## 0 一图看懂 # 顶部放最重要的 SVG 概览 + 三句话:核心思想 / 边界 / 怎么算成功
## 1 意图与目标 # 为什么做、要达成什么(可量化)
## 2 边界 # 做什么 / 不做什么(配边界图)
## 3 方案 # 架构级,关键决策与取舍(配结构图)
## 4 步骤计划 # 分阶段:交付物 / 验证 / 依赖 / 风险(配阶段图)
## 5 验证方式 # 验收标准、可观测信号
## 6 风险与回滚
## 附 图清单与状态 · HTML 增强页链接
```
**§0「一图看懂」是门面**:一张 SVG + 三句话,让人 30 秒抓住意图、边界、核心思想;这是本文档「给人读、更重要」的承诺所在。
## 生产与评审
- **文字层**由主会话 / opus 写,务必自洽——后续 AI 消费它,缺图也要能读懂全部设计。
- **图层**每图一 opus 子代理:克隆 atlas 金样板 → 画 → 跑复验脚本 → 对抗自检house style 与散文铁律(连同正反例)**随 prompt 一起传下去**(只传「画图」不够,输出会回退 AI 味)。
- **评审**这份文档仍过双评审门Codex + OpusCodex 挂回落 Opus 单评),对象 = 这一份文档。
- 命名 `docs/agent-specs/YYYY-MM-DD-<功能>-设计.md`SVG / HTML 入同名或共享 `assets/`(顶层文档 → 子目录 assets 单向下引,别跨子目录引图)。
## 取代关系
取代旧的 `-review.md` + `-execution.md` 双档2026-06-24 创始人定)。存量双档作 trace 留 git / `_archive`**不强行合并历史**;新功能一律产这一份。
> 相关:[`architecture-diagram-atlas.md`](architecture-diagram-atlas.md)house style / 复验脚本 / 三范式防漂移)、[`wave-close-checklist.md`](wave-close-checklist.md)(收口同步纪律)、[`../workflows/ai-development-protocol.md`](../workflows/ai-development-protocol.md)(设计在协议中的位置)。

View File

@ -1,8 +1,3 @@
---
name: game-e2e-cdp-harness
description: "为插件库/生成游戏做真浏览器输入级 e2e(触摸轨迹→九门判真玩→四件套证据)时使用:play.cdp.cjs 九门(装载/掌帧/真渲染/接线/输入因果/latch/手感)+ 假绿守卫 + driver 库 + 首局体验门,mini-desktop 上跑,区别于 DOM 走查。"
---
# Canvas 游戏 e2e 证据 harness 配方CDP on mini-desktop
> 适用:插件库/生成游戏的**真浏览器输入级 e2e**(触摸轨迹→状态机推进→四件套证据),区别于 DOM 走查(那个见 [`ui-walkthrough-cdp.md`](./ui-walkthrough-cdp.md))。

View File

@ -1,146 +0,0 @@
---
name: gen-path-parity-harness
description: 两条生成路对照验证(parity)harness 范式 + 按品类 ≥80% 达标门(bake-off)——验证「换实现后生成质量没回退」Node→Python 重写 / 换模型 / 换框架)并判「生成质量够不够格上线」。核心=对照公平铁律(驱动器 held constant)+ 金标 spec 注入 + 两层判据 + 端口池并发 + 自动 spec 期望外生防自证 + 达标门质量口径(编排未收敛剔出分母、单列归编排硬化)。WU-A 后续(cheap-worker/compare_node.py)、M1 达标门(003,bake_off.py)实证。
node_type: skill
---
# 生成路对照验证 harness 范式
## 何时用
要回答「把生成实现换了一套质量有没有回退」——Node 重写成 Python、换个模型、换个框架。区别于「每条路各自能不能跑」对照验证比的是**两条路产出的游戏质量是否等价**给「旧路可以退役」这种迁移决策当硬证据。WU-A 框架收敛的 R1 头号风险(把在产主力整条重写)就靠它兜。
## 对照公平铁律:驱动器必须 held constant
游戏质量用九门过门率衡量,而九门要靠一份 play-spec 驱动游戏去玩。**两条路若各用各自自动产的 play-spec过门率差就掺进了驱动器质量差、不可归因于生成。** 所以对照时把每个品类的 play-spec 钉成**一份金标、喂两路共用**,把驱动器从对照变量里摘掉——这样过门率差才只反映生成质量。
只精化单侧的 play-spec 是最隐蔽的陷阱:它看着是「让对照更准」,实则把驱动器质量偏向了被精化的那一路,制造新的不公平。
## 金标 spec 只对一部分驱动器家族公平
能不能「一份金标 spec 通吃两路同品类游戏」,取决于驱动器怎么找操作目标:
- **tap-targets 族**(点击 occupied 目标:点击得分 / 打地鼠 / 经营点客):驱动器从游戏自报的 `_forensicsView().state().targets` 里读坐标再点,坐标与具体游戏实现无关 → 一份金标 spec 公平驱动两路,**成立**。
- **key-cycle 族**方向键2048 / 躲避):驱动器按 spec 里硬编码的 `driver.keys` 逐键轮发,而**按哪个键是生成侧 LLM 写 game-logic 时的自由选择、不在 forensics 契约里**。两路独立生成完全可能绑不同键,一份固定按键的金标对绑定一致那路驱得动、对另一路驱不动 → 公平不成立。这类要先立一道「两路绑同一套输入键」的契约(生成侧约束),或让 spec 从每路自报的输入映射派生键集,才能纳入对照。
一句话分界:**forensics 契约保证的是可观测性(读得到 targets/score不是可驱动性同一份 driver 配置能把两路都开起来)。** 别把两者当一回事——这是对照覆盖面的真实边界,硬来会把按键绑定的分叉冤判成生成质量差。
## 注入机制:把生成与 play 拆开,金标覆写 staged 路径
1. **拆步**:每路把「生成」和「九门 play」拆成两步。若某条生成路内部已经把 play 一气做完(如 `run_studio` 收口含 play给它加一个 generation-only 开关(`run_gates=False`)只生成到 stage、不 play。
2. **注入点 = staged 路径**:金标 spec 强制覆写到 `games/_wg1-gen/<id>/play-spec.json`——这是 `play.cdp.cjs` **真实读取**的位置,**不是** evidence 目录(最常踩的错)。
3. **覆写时机**:在 stage 之后、play 之前写入。gen-only 路靠「自动产 spec 的已存在不覆盖」语义即可让金标留住;已经自动产了 spec 的路(如 Node `gen.mjs` done 门)要**强制覆写**。
4. 再各自单独跑九门 play。
## 判据:两层 + 逐品类绝对地板 + 双低标红
当基线本身 flaky如旧路五款只 3/5 真可玩),只用「新路不低于旧路」会把「两路都没真玩起来」或「两路都勉强平庸」判成等价坐实。要四道一起兜:
- **相对层**:新路过门率不低于旧路。
- **绝对层**:旧路本身过不了可玩门的品类,要新路**真过**该品类可玩门driven 且进展门 H 过)才算坐实,不能靠「一起烂」蒙混。
- **逐品类绝对地板**:坐实的品类,新路真玩比例要带余量过线(如 ≥0.6),不只是 ≥旧路。
- **双低标红**:两路同卡菜单 / 同低分的品类显式标红、不计入坐实。
判据在**品类层聚合**多局(单局生成 flake 不拖垮整品类,且单局两路同等的 flake 是公平的、不算回退)。
## 成功定义钉死「金标 spec 下」
对照证据是在手写金标驱动器下采的;生产里新路将用自动产的 play-spec 驱动。两个驱动器不是同一个,而金标通常比自动 spec 更干净。所以**对照等价 ≠ 生产退役/切换许可**:面向生产的退役授权要另 gate 在「生产自动 spec 达到金标同等过门率」auto-vs-golden delta。产物命名直接标「金标 spec 下」,免得被当成生产许可。
## 自证陷阱:自动 spec 的期望基准必须外生
生产里新路要脱离金标、自动产 play-spec 驱动九门(便宜档 M1 U2)。把自动 spec 加厚到金标同质时,有个比对照更隐蔽的陷阱:某些门的期望字段是**语义期望**而非可观测状态——典型是 F_wiring 的 `expectedEngineCallPrefixes`(这个品类该调哪些插件)。它从游戏自报的 forensics state 里**推不出来**(state 只有 targets/score,没有「该调什么」)。若图省事从该局**实际调用**反推期望,期望就恒等于实际、F_wiring 退化成自证、必过、假绿——这是 held constant 铁律在单路自动 spec 上的化身:**期望基准必须外生**(预置「品类→期望前缀」映射表作 held 期望),绝不从被测对象当场反推。可测性:构造一个实际多调一个插件的局,合格自动 spec 的期望前缀不跟着变。
实证:M1(`git show 8ea97234:docs/plans/2026-06-26-003-feat-cheap-worker-M1-达标门绿-plan.md`)U2,Opus 评审核出自动 spec 与金标唯一实质差异就是这个字段,且它是达标门防假绿的第二层(第一层=按品类判防平均掉卡死品类)。
## 达标门口径:质量地板 vs 生产真实交付率(M1 U3 实证)
对照验证比的是「两路等价」;达标门(bake-off)换一个问题——「这条路生成出来的游戏,按品类稳不稳定够格上线」,按品类聚合九门过门率、逐品类 ≥80% 判。建这道门时,**算过门率的分母怎么定**是个会直接翻转结论的口径决策。2026-07-09 勘误:「按品类九门过门率 ≥80% 判达标」自此只度量机械预筛地板——九门 pass 降为「未见明显死」的预筛够不够格上线还须过独立模型玩法判定达标口径随之收窄下文分母口径收敛款为分母、rawPassRate 并报〕本身不受影响。见质量 SoT《游戏质量与爆火能力》裁定三。
一次生成会以两种性质完全不同的方式「不过」:
- **九门质量挂**:游戏产出来了、跑进对局,但某道确定性门没过(如 H_progress 没真进展)。这是真·游戏不可玩。
- **编排未收敛**:agent 编排根本没把游戏做出来——M3 撞单步静默超时(`timeout`)或工具调用步数硬顶(`step_cap`),`finished=false``staged=false`、**没有九门 verdict**。这不是「游戏不好」,是「这次生成没收敛」。
把两者混进同一个分母,达标率就掺了模型的编排稳定性抖动,不再纯反映生成质量。M1 实测把这点钉死:click-score 合并 14 款,原始口径(未收敛计入失败)= 11/14 = 0.786 卡在 0.8 边界下;剔掉 2 个编排未收敛后质量口径 = 11/12 = 0.917 稳过——同一批样本,口径之差直接决定里程碑收不收口。两批之间的方差(0.714 vs 0.857)也几乎全来自未收敛款的计数,不是质量波动。
**口径铁律(创始人 2026-06-27 定)**:达标门只衡量九门质量地板,**分母 = 收敛款**(`finished=true`、有 verdict);编排未收敛剔出分母、单列一个 `unconvergedTotal` 指标,归生成稳定性(编排硬化)那条线,不混进质量判定。但**原始口径(含未收敛的 `rawPassRate`)必须并报**——它贴近生产里用户的真实交付率(用户视角:没拿到能玩的游戏就是失败),是另一层指标,只是不作质量达标判据。`converged==0`(全未收敛)→ `insufficient`,质量无从判、不得默判达标。
两个边界别越:① 剔未收敛**不是**给质量放水——它剔的是「没产出游戏」的样本,不是「产出了但不好」的样本;后者(质量挂)照常计入失败。② 未收敛率本身是真问题(M1 实测 ~7%),只是它的家在编排稳定性、不在达标门;达标门的职责是把它**显式量出来单列**,不是吸收进质量数字里假装没有。实现见 `bake_off.py``judge_genre`(分母 converged + rawPassRate 并报)/ `aggregate`(unconvergedTotal 汇总)。
> 配套的两个生成质量前置(M1 同里程碑、归生成侧不归达标门):**U1 护栏**——共享 oracle `tools.mjs``check()` 在打包前堵「返 void 的绘制 API 被误当返回值用」这类能静态判的幻觉(过 check 却运行期卡菜单的子集),救回卡死品类进样本池;**U4 预算闸**——便宜档复用 tier2 `CircuitBreakerMiddleware`,发起每次模型调用前预估累计 ¥、越 ¥10 硬上限即 fail-closed,成本按 new-api quota 口径落盘(取价不可达降级到次数/token 闸 + 告警,不静默超支也不静默阻断)。
## auto-vs-golden 门:同款双驱动 delta(M2 实证)
对照门验「换实现没回退」、达标门验「绝对够格」,还差一道验「生产里那把自动产的驱动器,跟人手写的金标比有没有退化」。生产不会用金标 spec,每款游戏的驱动器是 `ensure_play_spec` 当场据 smoke state 推断产的、通常比金标薄。所以「金标 spec 下达标 / 等价」不等于「自动 spec 下生产真达标」,后者才是 Node 退役授权的条件。
**做法:同一款产物 play 两次、只换驱动器。** 生成一次(gen-only)→ 同一份 staged src/ 上先自动 spec(`ensure_play_spec`)play、后金标(`inject_golden`)play,逐门比过门率。同款产物两次 play 把生成方差摘掉,delta 纯反映驱动器差。编排序的坑:自动 play 必须在金标注入**之前**捕获(否则 auto_gates 被金标覆盖);ensure_play_spec 是「已存在不覆盖」语义,跑前要断言 staged 无残留 spec(撞号 / 重跑会静默复用旧 spec、污染 auto_gates)。
**方向是单边的**:门查「自动有没有比金标**驱得差**」(`delta < 0` 退化),不是查「自动有没有比金标松」。这点容易写反——动机一不小心就写成「防自动放过坏局」(更松),但断言同质时自动不可能比金标松(M1 已把自动 spec 断言加厚到与金标逐字段一致),真正会发生的是自动**驱不动**(薄 spec 把金标能驱起来的可玩局判进不去)。关键门 {E_live,H_progress,G_input} 容差 0(`delta≥0`)、其余六门容差 -1/N(单款 flake)。double-low 守卫:关键门两路都 <0.5(同挂)→ inconclusive,不被 delta=0 误判等价( `compare_node.doubleLowRed`)。
**鉴别力别预判为零**。评审一度判这门在 tap-targets 上「自动与金标字段同质、delta≈0 恒过、没鉴别力」。M2 实测证伪:click-score 5 款里 1 款真退化(E_live/G_input auto 0.8 < golden 1.0),3 次复跑确定性复现 flake根因是一类**生成契约漂移**——M3 合法地用**单数 `target`(对象)**暴露当前目标(屏上一次只一个),而整条 tap-targets harness(`_build_play_spec` 的驱动器推断 + 驱动器 `targetsPath:"targets"`)只认**复数 `targets`(数组)**:单数款被推成 key-cycle(发方向键)、开始都没点到 进不了 live即门确实能逮住自动驱动器对某类合法 forensics 形态失效」,delta0 只在自动与金标对该款都驱得动时才成立教训:**自动 spec 的驱动器推断键在某个具体字段名 / 形态上时,生成侧任何等价的别样表达都会让它静默误分类**——这类漂移要么在生成侧立硬契约(强制复数 `targets`)、要么让推断与驱动器认全部等价形态
**这门是「不退化确认」、不是决定性独立闸**。退役授权的强证据仍是绝对达标(M1)+ 两路等价(002);auto-vs-golden 补「生产自动 spec 没把达标驱虚」这一层。三条齐(M1 达标 ∧ 002 等价 ∧ auto-vs-golden 不退化)才授权切默认路由——任一不绿,授权如实判 NOT authorized 并列明欠哪条(`retire_authorization` 只读三份既有报告聚合、零真跑零 LLM)。M2 实证:auto-vs-golden 这条因上述契约漂移先判未绿、退役授权正确卡住,精确根因交回生成侧(WU-C 5.4)当场修复(`ffe3a5c9`:键存在性推断 + 驱动器单数 target 兜底)后转绿、authorized=True——门诚实卡住、修后如实放行,全程没放水。
## 并发(可选,提速)
并发的真约束不是产物隔离gameId 前缀已隔离),是九门 play/smoke 的固定端口 + Chrome 实例。
- **端口池**:每并发槽一对独立 `(server_port, cdp_port)`server 段与 cdp 段不重叠。
- **每对一线程**:用线程跑整对(线程内生成路自带 event loop信号量或端口池 size 天然限并发。
- **前台进程内有界并发**,不是后台子代理 task / tail 监视 / 自我唤醒重试——后者会失控撞同名产物、杀进程触发重试死循环、token 泄漏)。
- 前提:引擎工具端口可参数化(`gen.mjs --port/--cdp``play.mjs {port,cdpPort}` 各自独立 spawn server+Chrome+userDataDir
便宜档生成的资源/并发上限画像另见相应容量记录M3 推理远程、本地瓶颈是 Chrome/esbuild 的 CPU 突发、并发上限 ≤15
## 实现指针
- `cheap-worker/compare_node.py``judge_genre`(两层判据)/ `inject_golden` + `_write_spec_atomic`(金标覆写 staged/ `run_pair_sync`生成→注入→play 整对)/ `run_multi`(端口池 + 线程 + 限并发)/ `_port_pool`
- `cheap-worker/bake_off.py`(达标门):`judge_genre`(质量口径 = 分母 converged + rawPassRate 并报 + unconverged 单列)/ `aggregate`(按品类逐项 ≥0.8 + unconvergedTotal 汇总 + 缺收敛样本报 insufficient/ `run_one_sync`(透传 breakerKind 供未收敛诊断)/ `run_bakeoff`(端口池有界并发 + 报告不覆盖)。
- `cheap-worker/auto_vs_golden.py`auto-vs-golden 门 + 退役授权):`judge_genre_delta`(逐门 delta + 关键门容差 0 / 其余 1/N + double-low 守卫)/ `aggregate_delta`(按品类聚合)/ `run_pair_dual_sync`(同款双驱动 play、auto 先于 golden、ensure 前断言无残留 spec/ `run_batch`(端口池有界并发 + 报告不覆盖)/ `retire_authorization`(读 M1+002+auto-vs-golden 三报告聚合、零真跑零 LLM
- `cheap-worker/fixtures/golden-specs/`:每品类一份金标 play-spec。
- 九门 harness 本体(驱动器家族 / play.cdp.cjs / 出厂红线)见 [game-e2e-cdp-harness](game-e2e-cdp-harness.md)AgentScope 2.0.2 便宜档实现 API 见 [agentscope-2.0-facts](../knowledge/agentscope-2.0-facts.md)。
## 实证
WU-A 后续2026-06-26`git show 8ea97234:docs/plans/2026-06-26-002-feat-cheap-worker-multi-genre-plan.md`):三 tap-targets 品类 n=3 对照 Python 新路 vs Node 旧路全 equivalent坐实范式未回退。Codex + Opus 双评审揪出的四个真坑都在本范式里固化成了铁律注入路径错evidence 而非 staged、品类族判错点击得分是 occupied 反应族不是放置族)、判据缺绝对地板(双低被判等价)、只精化单侧 spec 污染对照。
M1 达标门2026-06-27,`git show 8ea97234:docs/plans/2026-06-26-003-feat-cheap-worker-M1-达标门绿-plan.md` U3 收口):三 tap-targets 品类各 7 款 M3 实测 + click-score 补跑 7 款确认抖动,质量口径逐品类 ≥0.8 全过——click-score 11/12=0.917、whack-mole 7/7=1.0、shop-serve 6/7=0.857,M1 达标门绿。关键发现 = 上「达标门口径」节的口径铁律:同一批 14 款 click-score,原始 0.786(边界下)与质量 0.917(稳过)之差全在 2 个编排未收敛款(timeout/step_cap),口径决定收口。收口报告 `cheap-worker/results/bake-off-M1-final.json`(results gitignored、本地证据)。
M2 auto-vs-golden 门2026-06-27,`git show 8ea97234:docs/plans/2026-06-27-001-feat-cheap-worker-M2-node退役授权-plan.md`,过双评审U1 逐门 delta 判据 + U2 同款双驱动 play + U3 三条齐退役授权判定全交付,全套件 106 测绿。full 批 n=5 首测:whack-mole/shop-serve aligned、**click-score regressed**(E_live/G_input auto 0.8<golden 1.0,3 次复跑确定性复现)。根因 = 单数 `target`(菜单态 null)vs 复数 `targets` 契约漂移(见上auto-vs-golden )。门当场抓到此生产自动 spec 缺陷 当场修(WU-C 5.4,`ffe3a5c9`:`_build_play_spec` 改键存在性判定 + 驱动器单数 target 兜底)→ 退化款 auto 0.6671.0重放三品类全 aligned **退役授权三条齐 `authorized=True`**(M1✓+002✓+auto-vs-golden✓)。**全程没放水**:门诚实卡住修后如实转绿,证伪字段同质恒过无鉴别力的预判报告 `cheap-worker/results/{auto-vs-golden-2/3.json,retire-authorization-M2.json}`
M3a U12026-06-27,`git show 8ea97234:docs/plans/2026-06-27-002-feat-cheap-worker-M3a-python-route-seam-plan.md`,过双评审+复审,创始人 D3=全局 flag 翻转、不做 per-request 灰度):`cheap-worker/{worker_service,result_out}.py` 交付——§6.1 HTTP worker(202 握手 + 有界串行队列 + 三字段 result-out + HMAC 回调,见下契约节)。27 单测 + 全套 132 测零回归;**真生成 e2e 过九门**(amgen-m3ae2e1 verdict pass=True、235s)。**真 e2e 抓到并修真 bug**:回调走系统代理被 fake-ip 拦 502 → 禁代理 opener(502→200)——印证内网外呼必须跑真网络验、单测注入桩会绕过此坑。双评审复审揪出的事实错(幂等字段我误名 `n`、真名 `idempotencyKey` 且后端 AigcApiImpl:79 已透传未消费)经对代码核验纠正。
## §6.1 worker result-out 契约(M3a · 退役机制接后端)
便宜档退役走「Python HTTP worker(`cheap-worker/worker_service.py`)经后端默认通道 `WorkerDispatchClient` 派发」时,worker 产的 **result-out 三字段语义严格分离**——M3a 双评审两家都点中、初版设计也搞错的 BLOCKER,固化成铁律:
- **`gameConfig` = 最小占位 Map** `{templateId, title, theme:"generic", engineDriven:true}`——**绝不**把源工程塞这里(GamePackage required 故不可空,但 engine 路逻辑不在它里)。
- **`engineBundle` = `amgen-<id>/bundle.iife.js` 全文(含 `__GameBundle`)= 运行时承重产物**——后端 `resolveEngineBundleText` 从它落 `GamePackage.engineBundle` 入 feed。**成功门 = `status=succeeded ∧ engineBundle 含 __GameBundle`**,否则 `failed` + 七值 `failureReason`
- **`sourceProject` = 源工程 2.0 JSON = best-effort 可选**(additive,`DifyCallbackReqVO.sourceProject`,缺则后端源落库旁路、不阻断成功)——profile 三枚举派生不出就**整段省略、绝不伪造**凑 schema。
- 权威范例镜像 = `game-cloud .../saa/SaaGraphDispatcher.buildCallbackReqVO`(L601-635)+ Python `wg1/gen-worker/worker/service.py:build_callback_payload`(后者不产 sourceProject)。
串行 worker(Chrome serve/CDP 端口不可并发)治 409 误判:**job-in 一律入有界队列(有容量 202、队满 503)、单线程串行消费**,执行器 2xx=ok 契约不动(队满 503 仍非 2xx→LLM_ERROR 终态,罕见背压可接受)。回调 HMAC = `hmac.sha256(secret, 发送字节)``X-Callback-Signature`,签名字节 == 发送字节。**回调必须禁系统代理**(内网端点经 fake-ip 代理被拦 502)。
## §6.1 worker 9d trace + D9 dedup parity(M3b · 换 worker 实现别静默退化 D11/D9)
换便宜档 worker 实现(Node/SAA→cheap-worker Python)时,result-out 不止三字段——**9d trace 与 D9 dedup 是 worker 侧产出,换实现就得在新路重新兑现,否则后端 D11/D9 静默失效**。M3b 固化:
- **9d trace 必填七项** `{pass, repairs, wallS, models, attempts, gameId, stage}`(camelCase)+ D11 首局维所需 `gatespec.driver`(**键名必须 `driver`、非 `driverType`**)与 `sevenGateVerdict.guards.H_progress.pass`(**嵌套对象、非裸 bool**)+ `cost.totalRmb` + `similarity`(D9)。镜像 wg1 `_extract_trace` / SAA `extractTraceQuietly` 输出口径。**`repairs = max(0, attempts-1)`**(首轮 0;attempts 是 resume 计数,off-by-one 会让好游戏 stability 永不满分、破 parity)。
- **D11 退化判别(retire-detection)**:worker 只产 `trace.cost`(M3a 初版)→ 后端 `ReadinessScorer` 的 playability/firstPlay/stability 三维全取中性 0.5、只 efficiency 随成本 → readiness 退化成近恒定**双峰 45/55**。且 `BUDGET_RMB=0.15` 与便宜档真实成本 ¥0.41.2 错配 → **efficiency 维对便宜档恒 0、零区分力**。**别拿「合成分脱离某区间」作判据**(退化态 45 本就在区间外 → 假绿);改**逐维断言**(三维各自脱离中性 0.5)。
- **反假绿真护栏 = 跨语言切片测反射调真 ReadinessScorer 私有逐维 scorer**(`CheapWorkerResultOutContractTest`):真 result-out fixture 喂真算分器,逐维断言 + 成功 vs 退化分拉开。三值判别 = 失败 **28** / 退化 **45** / 成功 **100**(M3b 真后端实证)。`trace` 是后端开放 `Map`、Jackson **不拒内层未知键** → 键名拼错(`seven_gate_verdict` / `driverType`)被静默接收、悄悄落中性;真护栏只有这个切片测。
- **复用旧路代码的 import 边界坑**:cheap-worker 的 `_bootstrap` 入 sys.path 的是 `tier2/gen-worker`(其无 dedup/_extract_trace);trace/dedup 参考在 `wg1/gen-worker`(另一棵树、不在 import 路径)。故 **trace 在 result_out 内手写镜像口径**(输入形态不同、不能直接套 `_extract_trace`);**D9 vendor 复制** `dedup.py` 进 cheap-worker(纯模块,`DEDUP_REGISTRY` 设自己 `results/`,比跨树 import + monkey-patch 全局常量干净)。
- **两条回调路都落 trace**(`DifyCallbackTxService` 失败路也调 `persistTraceQuietly`)→ trace 落库可经失败 gen 验证;但 D11 success 路高分需 succeeded(**注入合法 bundle 验机制、不赌便宜档质量**)。`aigc.trace.enabled` 默认 false → 验前必开。
- **玩家试玩边界**:玩家 manifest 端点要求 runtime_package published(status=1),preview(0)返「运行包未发布」→ `publish→feed→玩家真玩` 必经 `reviewProject(APPROVE)`(产品/审核台轨、auth + 项目 REVIEWING 生命周期),非生成线;生成线交付 = 落到可发布的预览包(version + status=0 包 + engineBundle 进 package_json + D11 分)。
## §6.2 跨语言契约版本接缝:一侧升版本,另一侧校验器必同步升(W-GOLD-LIVE 实证)
便宜档 Python `cheap_verify` 与 Node runner `game-runtime/games/_wg1-gen/_shared/playtest-v3.cdp.cjs` 共用一套 `acceptance-request/N` 来源契约。**一侧升了契约版本、另一侧校验器没同步升,真浏览器验收会被整拒,而接线看着是完成的**——这与上节「换 worker 实现别静默退化」同型,只不过这里是「换契约版本别静默整拒」。W-GOLD-LIVE 把 Python 切到 `acceptance-request/3`(v2 字段集之上开口三个可选参照资产字段 `designRef`/`referenceAssetRecordIds`/`consumerRef`,`cheap_verify.py:1726/1774`,加 `consumedReferenceAssets` 来源,:1787-1851)后,Node runner 当时只在版本三元里认 `/1 /2``/3``null` → 整条抛 `profile_contract_error`;且 `assertExactArtifactKeys` 是严格字段集、不含 v3 三可选字段,真 v3 acceptance 全被整拒。
固化成范式(现已是 runner 现行形态):
- **版本三元必带全分支**:`validateProfileProvenance`(`playtest-v3.cdp.cjs:323`)按 `schemaVersion` 派生 `contractVersion`,三元逐版本列全 `/3 ? 3 : /2 ? 2 : /1 ? 1 : null`(:333-335);新版本不补分支就落 `null` 整拒。
- **严格字段集用 optionalKeys 开口,旧调用方零变**:`assertExactArtifactKeys(value, expected, label, optionalKeys=[])`(:311)缺省 `[] = 旧严格行为`;v3 把三可选字段与 `consumedReferenceAssets``optionalKeys` 白名单放行(:343/352),`/1 /2` 调用方不传该参、行为不变。
- **绊线测试随新字段扩**:恢复「篡改 + 重 hash」两段断言(篡改字节 → hash 不一致必拒)并扩到 v3 新字段,防校验器对新增字段静默放行(验法见 `cheap-worker/tests/test_acceptance_v3.py`)。
**消费对账是机器强制,不是 bug**:runner 与 `full_gate.py` 只消费 `lifecycleStatus==active` 的参照资产;声明消费而无 active 匹配 = verified reject(`full_gate.py:187-204`,六项闸:存在/激活/role/consumerRef/版本/缺维度)。迁移窗口里清单全是 `migration_pending`/`candidate` 时,任何 live 消费当场被拒——这是「迁移完成前不得新增 live 消费」的机器强制。

View File

@ -1,176 +0,0 @@
---
name: heritage-game-design
description: "当为非遗/传统技艺题材 brief 产设计阶段玩法方案或评审时使用:工序节拍/图样拼合等玩法化范式、节奏数值锚、文化表述红线与可达红线、反贴皮自检、品类 rubric(H1H5)与成长轴替代申报。"
---
# skill: heritage-game-design —— 非遗品类小游戏「玩法设计」作业手册(给生成 agent 的设计阶段)
> 定位:这是 **生成 agent 设计阶段造「好玩的非遗主题玩法设计」的唯一作业手册**。它回答 **"设计什么才好玩"**(玩法化路径/机制/进度/数值/美术/音/UI 范式),与 [`littlejs-game-dev.md`](littlejs-game-dev.md)(回答"代码怎么写")配对:**设计阶段产玩法概念 → 代码生成阶段照 littlejs-game-dev 产代码**。体例与分工同 [`sim-business-game-design.md`](sim-business-game-design.md)(经营品类的对应手册)。
>
> **铁律(创始人 2026-06-21)**:"好玩 = 上游设计层职责",不在 runtime/插件。**这份 skill 负责让设计本身好玩**;丰富度的「校验」走纯 LLM 验证 agent(非阻塞),绝不写成代码校验。
>
> **可达性边界**:产出必须落得到 A-model 轻量运行时——**LittleJS 2D 手机竖屏(390×844)、单/少场景、便宜模型可生成的多文件代码、11 注入插件能力域**。设计再好,落不到这个运行时就是空想——见 §9 红线。
>
> **证据基**:范式蒸馏自「传统技艺/民俗」题材在轻量小游戏里的成熟玩法化模式(工序模拟、图样拼合、节律配合属训练知识内的通识范式);品类骨架 = [`game-runtime/games/_template-feiyi/`](../../game-runtime/games/_template-feiyi/)(工序节拍循环,已独立过九门)。
---
## 0. 为什么这一品类能成(底层动机 —— 设计要击中这些)
非遗主题小游戏的吸引力来自 5 个心理引擎,**好玩 = 同时点燃尽量多个**;一个都没点燃 = 贴皮换装:
1. **手作治愈**:亲手把一件东西从毛坯做到成品的「完成感」——工序一道道点亮、作品一件件出炉,过程本身解压。
2. **文化好奇**:传统工艺"原来是这么做出来的"的新鲜感——工序名、器物名、纹样名自带内容密度,天然不同质化。
3. **节律爽感**:火候、节拍、时机——"在对的时刻出手"的技巧回报,是这一品类替代"打怪爽"的张力源。
4. **收集图鉴**:作品册/纹样册凑齐的强迫感(每件成品都是一个文化物件,收集动机比抽象分数强)。
5. **成就展示**:「我烧出来的这窑」可截图、可分享——成品天然是炫耀物。
> **反"贴皮"总纲**:非遗品类最常见的失败不是不好玩,是**玩法与文化无关**——标题叫传统工艺、玩起来是换色打地鼠。判据:把主题词全部替换成现代词,玩法若毫发无损,就是贴皮(见 §10 自检与品类 rubric H5)。
---
## 1. 核心玩法化范式(可达子集 —— design 优先从这里选)
| 范式 | 一句话 | 我们运行时怎么落(插件) |
|---|---|---|
| **工序节拍**(品类骨架默认) | 一件作品 = 一条 ≥3 道的工序链,工序依次「就绪」,在火候窗口内点掉 → 出成品;错过窗口回炉 | `_template-feiyi` 已接好整条循环:session-score 计工钱 + timer-scheduler 限时 + hud-ui 工位/窗口条 + particles-juice 出彩 + audio-music 音效 |
| **图样拼合** | 按纹样图谱点亮格子/部件,拼出一幅完整图样(剪纸/织纹/年画式的构图感) | core 网格状态 + hud-ui 绘格 + 单击命中点亮判定 |
| **节律配合** | 跟着节拍点(舞龙鼓点/号子),命中节拍窗口得分、连中叠倍 | timer-scheduler 节拍源 + gamefeel ComboWindow + audio-music |
| **工坊经营** | 接订单→按工序制作→交付收钱(非遗 × 经营的复合;经营范式见 sim-business §1) | 工序节拍循环 + 订单表;别超 2 主机制 |
| **收集图鉴** | 每件成品入册,凑齐一套解锁下一套 | save-progress 存收集 + hud-ui 图鉴格 |
| **技艺成长** | 攒工钱解锁新作品/新纹样/新工序(更难更值钱) | session-score 阈值 + core 解锁表 |
**组合配方(经验)**:一款轻量非遗 = **1 条工序/节律主循环 + 1 个成长轴(解锁新作品/纹样)+ 收集点缀**。别堆超过 2 个主机制(便宜模型 + 单屏扛不住,也稀释爽点)。
**成长轴申报(对齐质量模型 SoT §7)**:本品类以**工艺步骤解锁/章节推进**作成长轴(替代经营类的数值滚雪球)——解锁的是"会做的东西变多、做的东西变难变值钱",任意时刻都露出下一个锁。这是档位底线四件里「成长轴」的品类映射,不是裸降。
---
## 2. 关卡 / 进度 / 留存设计
- **首 3 分钟(决定生死)**:① 010 秒:零阅读上手(第一道工序高亮闪、点它就有反馈);② 1060 秒:**做成第一件作品**(完整走完一条工序链的完成感 = 本品类的首个爽点);③ 13 分钟:露出 23 个后续锁(下一件更难的作品/新纹样/新工序)。
- **留存钩子**:作品图鉴缺格(「还差 2 件凑齐这一套」)· 每日首件双倍工钱 · 限时"赶工订单"(可选)。
- **长线**:套系图鉴(做齐一套解锁下一套)· 工坊升级(案台变大、工序变多)· 最佳纪录(单日最多成品)。
- **节奏**:一局内先松后紧——开局工序就绪慢、窗口宽(学会),后段就绪快、窗口收紧(赶工感);爽点 = 出成品的瞬间(全屏小庆祝),平台期 = 攒工钱等解锁。
---
## 3. 数值与节奏(节律张力是本品类的手感命脉)
- **双层奖励(基础反馈保上手 + 技巧分给深度)**:**基础分**——每完成一道工序都加分(随手点也有正反馈,新手不至于无所适从);**技巧分**——火候窗口内的「正窗」时刻(窗口前 40%)出手给暴击倍率、连续正窗叠 combo。新手随便玩也有反馈、熟手靠技巧分玩出高分,两不耽误。
- **工序节奏目标值(design 必给具体区间,别让 code agent 拍脑袋)**:工序就绪间隔 **~0.61.0s**(太慢 = 干等,太快 = 手忙);火候窗口 **~58s**(过窗回炉,不判死);回炉冷却 **~0.81.2s**;一局 **60s**,期望成品 **24 件**(工序链 46 道);后 20s 可把窗口收紧到 **~3.55s** 做赶工段。这些目标值交 core.js 参数区。
- **工钱曲线**:后道工序分值高于前道(越接近成品越值钱,弃单代价自然变大);成品奖励 ≈ 单道工序分的 35 倍;解锁阈值取「23 件成品的工钱」,制造"差一件就解锁"的钩子。
- **代价设计**:错过窗口 = 回炉重做该道(损时间不清进度),**不做「一错全毁」**——挫败 = 流失;紧张感来自时间损失与 combo 断裂,不来自惩罚清零。
---
## 4. 美术风格范式(可达 + mmx 可产 + 程序化兜底)
| 风格 | 调性 | 配色 | 适合 |
|---|---|---|---|
| **国潮扁平**(主流首选) | 雅致轻快 | 低饱和底 + 朱砂/黛青/藤黄点缀 | 绝大多数非遗题材 |
| **暖木工坊** | 手作温度 | 陶土棕/米白/窑火橙 | 制陶/木作/织染类工坊 |
| **纸艺高对比** | 利落喜庆 | 宣纸白/朱红剪影 | 剪纸/皮影/年画类图样玩法 |
| **夜灯暖光** | 热闹民俗 | 深夜蓝底 + 灯笼暖黄 | 灯彩/庙会/节庆类 |
- **物件即主角**:非遗品类的"萌角色"往往是**器物本身**(一只越做越亮的陶碗、一盏点起来的花灯)——给成品高光时刻,拥有感落在作品上。
- **美术一致性之根**:全程共用一组"风格词"(主体 + 风格 + 配色 + 构图),mmx 每次生成都带它;程序化兜底——hud-ui + palette-post 画几何工位 + 统一色板即可成立,**别因美术阻塞可玩性**。
---
## 5. UI 范式(hud-ui 全可达)
- **顶部 HUD**:工钱 / 成品数 / 倒计时(固定布局);
- **工位排布**:工序台按链条排(横排/弧形),当前就绪工位**高亮 + 窗口条**(所剩火候一眼可见);
- **飘字**:每道工序 "+N 工钱" 上浮,正窗暴击换色加大;
- **成品时刻**:出成品全屏小庆祝(闪光 + 粒子 + 音效)+ 成品图入册动画;
- **图鉴格**:已做出的作品点亮、未解锁的置灰挂锁(「下一个锁」可视化);
- **结算屏**:摆出本局做成的作品(名字 + 数量)+ 工钱 + 最佳纪录——这是可截图的炫耀时刻,别只弹一行分数。
---
## 6. 音乐 / 音效范式(audio-music 可达)
- **BGM**:民乐感轻循环(笛/筝/鼓的轻快律动);mmx 生成:`mmx music generate --prompt "轻快民乐,竹笛与古筝,手工坊的温暖节奏" --instrumental --bpm 96`
- **音效(节律品类的手感一半在声音)**:工序完成「嗒」(click)· 正窗暴击「叮」(chime/coin)· 出成品欢庆(win)· 回炉闷响(thud/fail)。经 plugins.audioMusic 语义音名;**宁可程序化音效,也别没有反馈音**。
- 节律配合范式里,音效就是玩法(节拍提示音),优先级最高。
---
## 7. 首屏 / 传播钩子(通用,MVP 也用)
- 首屏 3 秒:直接站在工坊案台前,第一道工序已在闪——点它就开始,别放文化说明墙(文化经玩法体感,不靠开屏读文);
- 成品结算屏 = 天然分享图(本局烧成的三件套摆开);
- 「同款」钩子:主题(哪门手艺)+ 玩法原型(工序节拍/图样拼合)是清晰的品类元数据,remix 换一门手艺即一款新游戏。
---
## 8. 设计阶段输出配方(生成 agent 据 brief 产出这些 → 喂代码生成)
给定一句话 brief,设计阶段应产出一份**结构化玩法设计**,**逐项落到运行时**:
1. **核心循环**(一句话 + 30 秒脚本:玩家做什么→得什么反馈→怎么变强)+ **玩法化路径**(从 §1 选:工序节拍/图样拼合/节律配合…);
2. **工序链/图谱定义**(道数、每道的名字与分值——名字用该门手艺的通识工序词);
3. **双层奖励**(基础分 + 技巧分/正窗暴击,§3);
4. **节奏目标值**(就绪间隔/窗口/回炉/一局时长/期望成品数 —— 按 §3 区间给具体数,交 core.js);
5. **成长轴**(34 级解锁:新作品/新纹样/新工序 + 阈值);
6. **美术风格词**(一组,全资产共用)+ 资产清单(标 mmx 或程序化);
7. **音效清单**(完成/暴击/成品/回炉 + BGM 情绪);
8. **UI 布局**(HUD/工位/窗口条/图鉴/结算,§5);
9. **首 3 分钟脚本**(上手→首件成品→露出后续锁)。
> **MVP-first 铁律(关乎 code agent 能否收敛)**:首版**只做可玩核心**——1 条工序链(46 道)+ 双层奖励 + 34 级解锁 + 基础数值/音效,**作品种类 ≤3 种起步**。**多套图鉴 / 订单系统 / 离线收益 / 广告位 / 多档 BGM 一律标「后续·MVP 不做」**,别一稿堆满(设计太满 → code agent read-thrash 不收敛)。**先出能玩的核心,再谈丰富。**
> 这份设计 = 代码生成阶段的"图纸";生成 agent 照 littlejs-game-dev 把它实现为多文件代码。需要完整过门范例时 read_file `game-runtime/games/_fewshot-feiyi/src/game-logic.js`(本品类 few-shot 正例:过九门 + 丰富度参照)。
---
## 9. 可达性红线(落不到运行时的设计 = 废稿,必剔除/简化)
- ❌ 联网协作 / 排行榜社交 → 单机 + 本地最佳(session-score best)。
- ❌ 大地图寻访 / 多村落流程 → 单/少场景竖屏(scene-fsm 切 35 态)。
- ❌ 3D 拉坯 / 自由手势雕刻(连续轨迹判定)→ 2D 点击 + 程序化动效;手感靠窗口时机与 juice,不靠手势精度。
- ❌ 知识问答墙 / 长文化说明打断玩法 → 文化经工序名/器物名/氛围体感传达,玩法优先。
- ❌ 超过 2 个主机制 → 聚焦 1 条主循环 + 1 成长轴。
- ⚠ **核心动作优先单击直给**(点就绪工位 = 该道工序完成):「先选工具再选工位」两步流程抬高上手门槛、拖慢节拍手感——本品类的张力在火候窗口时机,不在选择流程;确需多步,给清晰视觉指引(选中态高亮 + 下一步该点的工位有 affordance),让真人一眼看懂下一步点哪。
- ❌ **文化表述红线**:工序、器物、民俗意象用**通识**表述(制陶有塑形上釉、织造有经纬提花这一层常识粒度);**不编造具体非遗项目的失实细节**——不虚构某项目的历史年代/传承人/名录地位/地域归属,不使用"官方认证""名录在册"这类可能失实的措辞;brief 点名了具体项目时,只用其公知常识、拿不准的细节一律泛化成通识写法。
- ✅ 留下的:工序节拍、图样拼合、节律配合、收集图鉴、技艺解锁、轻工坊经营 —— 全在 11 注入插件 + 单屏 + 便宜模型可生成范围内。
---
## 10. 反"贴皮/无趣"落地清单(design 自检,逐条必过)
通用 12 条口径好玩自检(原 8 条即时反馈/可见成长/下一个解锁/30 秒爽点/数值滚雪球/情感锚/放置回归/音反馈,SoT §4 已增补至 12 条)见 [`sim-business-game-design.md`](sim-business-game-design.md) §10 —— **对所有品类通用,本品类照过**(成长轴按 §1 的申报映射:工艺步骤解锁;情感锚落在器物上,§4;第 12 条「核心操作非无脑」落在工序链先后依赖与火候窗口时机——依序推进、正窗出手是真判断/真技巧,判错回炉有代价,非永远可点的无脑点击,见 §11 H1/H2)。此外本品类另过 5 条品类扩展(与品类 rubric 同源,见 §11):
1. **工序链成立**:玩法是不是围绕一条有名字、有先后的工序链推进?(乱点无序 = 没有"手艺"体感)
2. **节律张力**:有没有"在对的时刻出手"的窗口与回报?(永远可点、何时点都一样 = 无手感)
3. **技艺成长可见**:有没有"会做的东西变多/变难/变值钱"的解锁线?
4. **作品可展示**:成品有没有被"摆出来"的时刻(图鉴/结算展示)?
5. **文化氛围一致**:把主题词全换成现代词,玩法画面是否就散了?(散了 = 氛围真在承载;毫发无损 = 贴皮)
---
## 11. 品类 rubric(丰富度品类扩展 · 喂 LLM 验证 agent · 非阻塞)
对齐质量模型 SoT([`游戏质量与爆火能力`](../../docs/architecture/架构/生成引擎/游戏质量与爆火能力.md))§4 计分制:每条 0/1 + 层标,与通用底座 v2(12 条)**分母分开、独立小计**;纯 LLM 评分、不进九门、不进脚手架、不拦发布。机器可读同源文件(评分尺数据,含锚定值)= [`cheap-worker/fixtures/genre-rubrics/heritage.json`](../../cheap-worker/fixtures/genre-rubrics/heritage.json)(2026-07-02 rubric 挂点归一改名并接线,品类键=heritage);金标锚定纪律(≥1 正例 + ≥1 薄反例、评分尺变更须金标复验、单款分组小计漂移 >±1 回退)照 SoT §4 规范三执行。
| # | 层 | 判据(0/1) | 正例 | 反例 |
|---|---|---|---|---|
| H1 | L2 | **工序链成立**:玩法围绕一条 ≥3 道、有名字、有先后依赖的工序链推进,依序完成才出成品 | 选料→塑形→上彩→出窑依序点亮,漏一道不出成品 | 屏上随机冒目标,点哪个都一样加分 |
| H2 | L2 | **节律张力**:工序有时机窗口(火候/节拍),窗口内操作有更高回报、错过有代价(回炉/断连击),且不判死 | 正窗出手暴击 ×2、过窗回炉重做该道 | 目标永远可点,任何时刻点效果完全一样 |
| H3 | L3 | **技艺成长可见**:随进度解锁新作品/新纹样/新工序(本品类成长轴),任意时刻可见下一个锁 | 攒够 60 工钱解锁「刻花盘」,图鉴里下一格挂锁预告 | 从头到尾只做同一件作品,无任何解锁预告 |
| H4 | L4 | **作品可展示**:成品进图鉴/结算屏有成品展示画面(可截图的文化炫耀时刻) | 结算屏摆出本局做成的三件器物 + 名字 + 工钱 | 到点只弹一行「得分 120」文字即黑屏 |
| H5 | L2 | **文化氛围一致**:主题、工序命名、配色、音效构成一致的传统氛围(通识表述),主题词换成现代词玩法画面即散 | 陶坊主题 + 陶土色系 + 塑形/上釉工序名 + 民乐感音效 | 标题带"非遗"但画面霓虹方块、工序叫 step1/step2 |
分组小计:品类扩展 L2 ×3(H1/H2/H5)/ L3 ×1(H3)/ L4 ×1(H4),分母 5,与通用底座 12 条分开计。逐条长期不成立 = 本 skill 或品类骨架的改进信号(SoT §4 规范六)。
金标锚以 `cheap-worker/fixtures/genre-rubrics/heritage.json``anchors`/`_note` 为准(正例 `_fewshot-feiyi` 10/11·品类 5/5,薄反 `_template-feiyi` 6/11·3/5,2026-07-03 复验零漂移)。
---
## 相关
- 代码层实现 → [`littlejs-game-dev.md`](littlejs-game-dev.md)(11 插件 API / 结构 / 资产-mmx / 工厂契约)
- 经营品类对应手册(体例同源;工坊经营复合玩法时参照其资源环范式)→ [`sim-business-game-design.md`](sim-business-game-design.md)
- 品类骨架(工序节拍循环,轻起点)→ `game-runtime/games/_template-feiyi/` · few-shot 过门正例 → `game-runtime/games/_fewshot-feiyi/`
- 便宜模型生成 worker / 九门 → [`cheap-model-game-generation.md`](cheap-model-game-generation.md)

View File

@ -1,240 +0,0 @@
---
name: littlejs-game-dev
description: "当生成 agent 要写一款可上线 LittleJS 小游戏的 game-logic.js(接五法、调 11 注入插件、守受控面红线、mmx 产素材)时使用:代码分层结构、写什么 vs 调什么、插件 API 速查与 M3 易犯幻觉、资产放置。轻中档 code 层唯一作业手册。"
---
# skill: littlejs-game-dev —— AI 直接写 LittleJS 游戏代码(调插件 API,不重写实现)
> 定位:这是 **生成 agent 造一款可上线的高质小游戏的唯一作业手册**。绘境AI 的终态产物 = `src/` 多文件 LittleJS 工程
> (创始人 2026-06-20 拍板;取代已废的「gameDefinition JSON 投影」线)。本手册回答四件事:
> **① 代码长什么结构 ② 你写什么 / 调什么 ③ 怎么调 11 注入插件 API+ runtime-probe 仅取证不注入)④ 美术/音乐/音效放哪、怎么用 mmx 生成。**
>
> **这一档定位(钉死)**:轻量 ≠ 简单。AI 参与深度低,但产物是**高质小游戏**——复杂能力由工程脚手架 + L2 插件库承担,你**少写、多调插件**做出卖相与深度,不是产没人会玩的简单玩具。
>
> 配套:契约匹配范例 = `game-runtime/games/_template/`(克隆起点,签名以现契约为准 —— **照它的结构改造,别抄旧 wanglanmei-ref 的 game-logic,那是旧 opts 签名漂移**);
> 插件契约 SoT = 各 `game-runtime/src/plugins/<name>/api.d.ts`(签名以它为准);装载契约 = `game-runtime/src/core/game-host.d.ts`
---
## 0. 三句话心法
1. **你只写「玩法身份(WHAT)」,通用机制(HOW)一律调插件——能调就别自己实现。** 写碰撞数学/粒子/补间/计分/状态机 = 错;这些是插件。
2. **你的唯一产出靶 = `game-logic.js` 的命名导出 `createGame({plugins,bundle,viewport})`**,返回 GameInstance:`init/update/render/destroy` + 输入方法 `handleTap(x,y)`[/`handleKey(key)`],另可挂可选调试接口 `_forensicsView()`。**零引擎 import**。
3. **受控面铁律**:游戏里 **绝不出现** `Date.now` / `performance.now` / `Math.random` / `setTimeout` / `requestAnimationFrame` / `new AudioContext` / `import 'littlejsengine'`。时间/随机/音频/引擎能力一律经 `boot.ctx` 或注入的插件拿;**输入不调 `ctx.getInput`**——写实例方法 `handleTap(x,y)`/`handleKey(key)`,L1 据此在 pointerdown/keydown 时派发(确定性、可取证、引擎可换)。
---
## 0.5 卡壳就查(场景 recipe + 能力图谱 + 引擎摘要)
写某个具体场景卡住了、或不确定某能力从哪来,先读对应的一篇,别硬想:
- **高频场景怎么落地**`read_file('.agents/skills/recipes/README.md')` 是「要做 X → 读 Y」索引命中按钮点不动 / 计时慢 1000 倍 / 缺图崩 / 场景机卡菜单 / 结算不驻留——五类头号翻车各有一篇「抄这段形态 + 病根」。卡在哪读哪篇。
- **有哪些插件、每件详细用法**`read_file('.agents/skills/plugin-capability-map.md')`11 件注入插件各配 `api.d.ts`(精确签名)+ `PLUGIN.md`(用法散文/装配坑/示例,比 api.d.ts 更全)的指针;另有 6 件**未注入储备**——它们**你用不到**`plugins.<键>` 取不到、会 undefined别写。
- **引擎能到底能干什么**`read_file('.agents/skills/engine-capabilities-brief.md')`≤3KB引擎重活都被插件封装、你少碰 `getEngine()`,外加坐标系 2× 换算坑。
---
## 1. 代码结构(可导航多文件)
一款游戏 = 一个目录,固定分层。**每个文件单一职责,改一处不牵动全身**:
> **这条「改一处不牵动全身」的工程规范性正是 A11 对话式调整回路的可改性根基**——资产统一 `assets.js`、数值集中 `core.js`/`balance.js`,使一次自然语言调整只落到"那一处"(换资产/调数值 = 改一处、零 LLM改玩法 = 有界单文件重写。A11 回路机制(两段式判意图/执行 + 三断言)见 [`../knowledge/tech-decisions.md`](../knowledge/tech-decisions.md) §1.2。
```
games/<your-game>/
├── entry.js # 打包入口:动态 import 引擎 → 委托 main.js(引擎 import 只在这)
├── index.html # 加载 dist bundle,调 bootXxx 启动
├── src/
│ ├── main.js # host 引导薄包装:调通用 bootGameHost,装配取证全局
│ ├── host-config.js # 【L1 固定·不写】实例化全11插件 + 视口 + 工厂 wiring(harness 已配好、write 拒)
│ ├── game.js # 【L1 固定·不写】薄 wrapper:摊平 opts.runtime→交 game-logic(write 拒)
│ ├── game-logic.js # 【L3·你写·必写】export function createGame({plugins,bundle,viewport})→GameInstance 五法。零引擎 import
│ ├── core.js # 【L3·推荐】纯逻辑:状态机 + 命令 API(无 I/O/引擎,node 可单测;小游戏可内联进 game-logic)
│ ├── balance.js # 【内容】数值 / 关卡 / 价格 / 掉落表(纯数据 + 公式)
│ ├── render.js # 【绘制】把状态画到 g;导出视口 W/H 常量
│ ├── assets.js # 【资产声明】图键→文件名映射 + loadAssets(host 层,可用 Image)
│ └── audio-director.js # 【普通游戏音频编排】BGM/音效调度(可选;标准 Match-3 禁写)
├── assets/
│ ├── manifest.json # 资产清单:file/role/bytes/sha256/source{tool,model,prompt}
│ ├── gen-ledger.md # 生成历史(prompt/字节/hash/替换记录)——改素材的索引
│ └── *.jpg *.mp3 # 美术 / 音乐 / 音效(mmx 产物)
└── test/
├── *.test.mjs # node 纯逻辑测试(core/balance)
└── *.cdp.cjs # e2e 真玩取证(九门)
```
**分层依赖方向(只能往下依赖,不能反向)**:
`entry-bundle → host-config → game(L1 wrapper)→ game-logic(你写)→ {core, render, balance, assets}`;`core`/`balance` 最纯(零依赖、可单测)。
> 别把 4000 行的 wanglanmei-ref 整个抄过来——它是「进阶worked 参照」。**克隆 `_template/`、follow 这个结构**,逻辑写进 `core.js`
---
## 2. 写什么 vs 调什么(这是本手册的核心)
| 层 | 谁负责 | 具体 |
|---|---|---|
| **玩法身份 WHAT** | **你写** | `game-logic.js`(必)把玩法接到五法 + 调注入的插件 · `core.js` 状态机与规则 · `balance.js` 数值/关卡/内容 · `render.js` 画面编排与命中区布局 · 事件→表现映射(普通游戏可触发 juice/音效;标准 Match-3 音频由 host 自动编排) · `assets.js` 资产声明 |
| **通用机制 HOW** | **调插件** | 碰撞 / 物理 / 手感 / 缓动 / 输入缓冲 / 粒子打击感 / 音频 / 存档 / 调色 / **计分胜负 / HUD-UI / 场景状态机 / 定时调度**(共 11 注入件,见 §4;另 runtime-probe 仅取证不注入) |
| **装载 + 取证** | **平台供给** | `bootGameHost` 引擎五回调装载 · 插件注入 · `boot.ctx.getEngine()` · runtime-probe 六锚取证 |
**判定口诀**:"这段是『这款游戏特有的规则/内容/画面』吗?" 是 → 你写;"这是『换个游戏也一样』的通用能力吗?" 是 → 找插件调。**找不到对应插件再自己写**(并反馈:它可能该被沉淀成新插件)。
---
## 3. 装载契约(你的产出靶)
游戏工厂 = `GameHostFactory`(`game-host.d.ts:66`),host 调它建 `GameInstance`:
```js
// src/game-logic.js —— 【你写这个】命名导出 createGame,零引擎 import
// 入参由 L1(game.js wrapper)替你摊平:扁平 { plugins, bundle, viewport },没有 opts.runtime 嵌套。
export function createGame({ plugins, bundle, viewport }) {
// 插件已由 L1 host-config 扁平注入,用到才解构;标准 Match-3 不解构 audioMusic:
const { sceneFsm, sessionScore, hudUi, timerScheduler /* …按需 gamefeel/juice/collision/physics/audioMusic */ } = plugins;
const core = createCore(); // 你的纯逻辑(可内联进本文件)
return {
async init(boot) {
this.ctx = boot.ctx; // 受控面:time/random/getEngine/log(只在 init(boot) 拿;输入不在此订阅)
this.assets = await loadAssets('./assets/'); // 容错:缺图降级不崩
},
update(dt) { bundle.tick(dt); core.update(dt); }, // ★ 必调 bundle.tick(驱动插件 onFrame)
render(g) { drawFrame(g, core.state(), this.assets, hudUi, viewport); }, // g=引擎 mainContext;读 viewport.w/h
handleTap(x, y) { core.tap(x, y); }, // ★输入入口:L1 在 pointerdown 时直达调用(别自己订阅输入)
destroy() {}, // 卸载(幂等)
};
}
```
要点:
- **render(g) 的 `g` = `boot.mainContext`**(引擎绘制面);绘制画在 `g` 上,**不要**用插件的 `getContext2d()`(那是另一块 overlay)。
- **插件扁平注入:直接 `const { sceneFsm, … } = plugins`**(`plugins` 是 createGame 入参;**不是 `opts.runtime.plugins`** —— 那是 L1 内部的、你见不到,写了就 undefined → boot 崩)。游戏只「用」,不 `new`、不 `register`
- 时间/随机/引擎走 **`boot.ctx`**(`init(boot)` 内拿;入参里没有 ctx)。
- **输入收归 L1(别自己订阅)**:不写 `ctx.getInput()`、不自建点击队列、不在 `update` 里挑时机消费点击——只写实例方法 **`handleTap(x,y)`**(点击)/**`handleKey(key)`**(键盘),L1 的 game.js wrapper 在 pointerdown/keydown 时**直达**调用。HJ-AGI 实测:M3 把点击塞 `pendingClicks`、在 `update` 的 play 分支(menu early-return 之后)才消费 → 菜单点击永不处理 → 启动不了;收归 L1 结构性根除。check 会拦 `getInput` 与"零输入方法"。〕
- **⚠️ 驱动插件 onFrame**:`update(dt)` 内**必须 `bundle.tick(dt)`**(`bundle` 是 createGame 入参)——否则 timer-scheduler/gamefeel 等的 onFrame 不推进、timer 永不到期、一局不结束。
- **viewport**:画面口径读入参 `viewport.w/h`,别硬编码 390×844。
- **调试视图(`_forensicsView()`,可选)**:挂 `_forensicsView()``{ state(), measures() }`,`state()` 导出可观测态(如 `{ phase, score }`)供人工调试观测——W-AXIS-V2 起验收不再读它(验收 = 四门机械投影 ∧ 测试 agent 视觉引导真玩),写不写都不影响过门;保留它是给调试与日志一个统一出口。另可选挂 `_handleAction(action)`(host.do() 走逻辑驱动,便于确定性 e2e)。范式见 `_template/src/game-logic.js`
- **关键事件记日志(`ctx.log`)**:在 init/场景切换/得分/出错处 调 `ctx.log('tag', 信息)`(`ctx`=init(boot).boot.ctx)→ 写 `window.__gameLog`,便于读日志诊断运行时问题。**插件调用已由 host 自动记**(你只补游戏语义事件)。
---
## 4. 11 注入插件能力速查(调 API,别重写;另 runtime-probe 仅取证不注入)
> **完整签名以 `game-runtime/src/plugins/<name>/api.d.ts` 为准**(本表只给「选型 + 头部调用」)。每件都是 `createXxxPlugin(opts)` 工厂,经 host-config 实例化、注入。命名全 engine 级、零玩法语义。
> **想看用法散文/装配坑/示例(比 api.d.ts 更全)** → 同目录 `PLUGIN.md`(如 `game-runtime/src/plugins/scene-fsm/PLUGIN.md`);一张全表 + 未注入储备清单见 `.agents/skills/plugin-capability-map.md`
### 基元层(8 件 · 算法/物理/手感/音/存档)
| 插件 | 用于 | 头部 API |
|---|---|---|
| **collision** | 碰撞查询(纯数学) | `collision.circleVsCircle/circleVsAabb/aabbVsAabb/satVsPolygon``{hit,depth,nx,ny}`;`raycast*`;`SpatialHash`(#3 都挂实例)。**Aabb 盒 #12:可传 `{x,y,w,h}`(左上角+尺寸,与 Rect/drawButton 一致)或 `{x,y,hw,hh}`(中心+半宽高)——两形态都对;Circle `{x,y,r}`** |
| **physics-lite** | 街机运动原语(非刚体) | `Projectile{step,reset}`/`integrateProjectile(state,dt,opts)`;`springStep`/`criticalDamping`;`moveWithConstraint`;`clampSpeed`/`applyFriction`。**⚠️ ProjectileState=`{px,py,vx,vy}`(位置是 px/py 非 x/y);重力经 opts.gravity={x,y}`** |
| **gamefeel** | 手感 | `easing`(13 曲线 in/out/inOut);内置 `gamefeel.inputBuffer{consume,peek}`;**工厂(免 new + 自动注入受控 time,#4)**:`gamefeel.createCoyoteTimer(opts)→{setGrounded,isAvailable,consume}``gamefeel.createComboWindow(opts)→{hit,getCount}` |
| **particles-juice** | 粒子 + 打击感 | `spawnEmitter(preset\|cfg,x,y)`(或 `burst(x,y)` 直发 burst 预设,#6/#10 母语别名);`hitStop(dur)`/`getTimeScale`;`shakeScreen`/`getShakeOffset`;`flashScreen`/`pulseScale`;`PRESETS`(burst/trail/drift)。**⚠️ `juice.render(g)` 必须在你的 render(g) 里每帧手调(非自动,#11)——忘了 → 粒子/打击感不显示;`step(dt)` 默认 autoStep 自驱(经 onFrame),要手调 step 须工厂传 `autoStep:false` 防双步进** |
| **palette-post** | 调色 + 后处理 | `rgbToHsl/hslToRgb/applyPaletteMap/shiftHsl`;`setPost/renderPost`(vignette/dither/scanline)或 **`applyPreset('retro'\|'crt'\|'soft'\|'dither'\|'none')` 一键观感(#14)**。换色色板(from/to)仍由你注入、插件不内置成品色 |
| **audio-music** | 程序化音频 | 普通游戏可用:`loadSong`(载曲)`/play(intensity?)`(播已载曲;**入参是情绪强度 0..1、不是曲目名**,#9)`/stop/loop`;`setIntensity`;`playSfx(name)`。**标准 Match-3 例外:该键不进入 L3Writer 不写任何音频逻辑。** |
| **save-progress** | KV 持久化 | `get(key,default)/set/remove/clear`;`createMemoryAdapter`/`createLocalStorageAdapter` |
| **runtime-probe** | 启动取证(六锚) | `mark(anchor)/getRecords/toJSONL/verify/verifyChain` |
### 编排层(4 件 · 让你"只写玩法、不手写编排")
| 插件 | 用于 | 头部 API |
|---|---|---|
| **session-score** | 计分 + 胜负闸 | `addScore(d)`(=`add(d)` 别名)`/setScore/getScore/getBest/reset`;`win()/lose()/getOutcome()/isOver()`。**胜负条件你写**:`if(lives<=0) s.lose()`。best 接 save:`createSessionScorePlugin({best:{load:()=>save.get('best',0),save:b=>save.set('best',b)}})` |
| **hud-ui** | HUD/UI 绘制 + 命中 | 纯:`pointInRect/pointInCircle/layoutRow/layoutColumn/hitTest`;绘制(传 g):`measureText/drawText/drawPanel/drawButton/drawBar(g,...)` |
| **scene-fsm** | 场景/状态机 | `define(name,{onEnter,onExit,update,render})`/`start(name)`/`transition(to,payload?)`/`current()`/`update(dt)`/`render(g)`/`reset()`。**状态名你定** |
| **timer-scheduler** | 受控时钟调度 | `after(ms,cb)/every(ms,cb)/sequence([{delayMs,run}])/cancel(h)/clear`。**⚠️ 单位是毫秒 #7**:3 秒写 `after(3000,cb)` 不是 `after(3,cb)``every(1000,cb)`=每秒(**与 `update(dt)` 的 dt=秒相反,别混**)。经 onFrame+受控时钟(游戏须在 update 内 `bundle.tick(dt)` 驱动,见 §3),**不要用 setTimeout** |
**典型组合**:菜单→玩→结算 用 `scene-fsm`;分数与最高分用 `session-score`(best 接 `save-progress`);倒计时/刷新波次用 `timer-scheduler`;分数/按钮/血条画面用 `hud-ui`;手感打击用 `particles-juice` + `gamefeel`
### 标准 Match-3两件 Writer 能力 + 一件 host-only 能力
绑定 `match3.orthogonal-swap-v1`puzzle L1 会在通用 11 件之外成对注入
`plugins.match3ProducerProfile``plugins.match3VisualTimeline`。普通 puzzle 和其它品类没有这两个键。
- 先读 `game-runtime/src/plugins/match3-producer-profile/api.d.ts`:棋盘 substrate、几何、格心、主题安全区和选中白环全部调用它不能自算另一套坐标或自画 selection。
- 再读 `game-runtime/src/plugins/match3-visual-timeline/api.d.ts``PLUGIN.md`:第二击同步业务结算时调用 `begin()``update(dt)` 推进,`render(g)` 只读 `view().tiles``handleTap` 入口先用 `isLocked()` 拒绝锁定期额外输入,重玩调用 `reset()`
- 标准消除效果由固定 host 在 L3 的 `render(g)` 完成后自动合成L1 已把 `match3ProducerProfile.geometry()` 与冻结六色主题 palette 注入时间线。Writer **不得调用** `match3VisualTimeline.renderEffects()`,也不得另造匹配消失粒子、光环或碎片;只按 `view().tiles` 绘制主题棋子主体。
- 第三件 `match3AudioDirector` 只属于可信 host绝不进入 `plugins`。它自动提供主题 BGM、交换、无效回滚、每轮 clear 的复合消除音、fall 落定音和带终局结果的 settle 音;声音严格跟随受保护视觉 transition 账,连锁不会在同步业务递归里提前叠播。
- 标准 Match-3 Writer **不得解构或调用 `audioMusic` / `playSfx`**,不得取 `getAudioContext()`、自行创建 `AudioContext`/oscillator/buffer source也不得另写标准 cue、BGM 或连锁升调逻辑。菜单、首击选中和重玩在 v1 本就没有 cue不要自行补齐静音按钮、首手势解锁、循环和主题资产均由 host 负责。
- `beforeKinds/swappedKinds/afterFallKinds/finalKinds` 是 0-based 8×8 JSON 原语数组;`pair/runs/matchedCells` 是 1-based`view` 的逻辑和视觉坐标也是 1-based视觉坐标可为浮点。
- 业务事件仍必须在第二击同步调用栈内写完。时间线只播放 sealed 事务,不能补写事件、改业务盘面或重新算连锁;终局结果可同步 latch但终局场景必须等 settle 完成。
- 禁止自己维护另一套 `animPhase/visualQueue` 代替受保护能力,也禁止给最终业务棋盘旧坐标闪白冒充消除。八轮以上会封存真实终盘并标记 `visualComplete:false`,不能因为视觉验收上限让已提交业务崩溃。
### ⚠️ M3 实测易犯的幻觉 API(❌别这么写 → ✅正确)
> **铁律:插件方法名一律以 `src/plugins/<name>/api.d.ts` 为准,别按"常见库套路"臆测。** 以下是 M3 即便读了 api.d.ts 仍反复臆测出的错名(HJ-AGI-003 saae 实测),逐条记牢:
- ❌ `sceneFsm.defineScenes({...})` → ✅ `sceneFsm.define(name, {onEnter,onExit,update,render})` **逐个**定义(**没有** `defineScenes`)。
- ❌ `sceneFsm.define({ menu:{...}, play:{...}, over:{...} })` 单对象批量注册 → ✅ **每个场景各调一次** `sceneFsm.define('menu', {...}); sceneFsm.define('play', {...}); sceneFsm.define('over', {...})`(`define(name, handlers)` 第一参恒为场景名**字符串**;传对象 → 第一参变成 `[object Object]`、menu/play/over 全没注册 → `transition('play')` 找不到目标态被忽略 → 卡菜单进不去 play。实测合成/2048 类翻车点)。可链式 `.define(...).define(...)`。**回调表里 `update(dt)` 只收 dt、`render(g)` 只收 g、与五法同形——别臆造场景名/state 首参(#15),绝不写 `update(scene, dt)`。**
- ✅ **`hudUi.drawButton(g, rect, opts)` 回吐它绘制的命中矩形 `{x,y,w,h}`**(阶段一B #5 改签名):直觉写法就对——`const btn = hudUi.drawButton(g, {x,y,w,h}, {label}); if (hudUi.pointInRect(px,py,btn)) {...}`,drawButton 返回命中矩形、直接接住判命中即可(也可先单独定义 rect 再画再 pointInRect,两种都对)。命中判定 `hudUi.pointInRect(px, py, rect)`(签名 `(px, py, rect)`;**只在 hudUi,collision 插件没有 pointInRect 方法**)。另:**`drawButton` 第二参是 rect 对象 `{x,y,w,h}`、不是 label/坐标位置参**——别写 `drawButton(g, '开始', x, y, w, h)`
- ✅ **起局/交互按钮的命中矩形在场景 `onEnter`(状态进入点)确定性建立、`render` 只画不写**——绝不让它只在 `render` 副作用里生成(`menuStartBtn = renderMenu(...)` 那种回写):并发渲染帧饥饿时 `render` 可能一次都没跑过 → 命中矩形恒 `null` → 菜单点不动、一局起不来(卡 menu 头号成因)。把按钮几何抽成纯函数,`onEnter` 调它设命中区、`render` 调它画,命中与绘制同源不漂移(`_template*` 六款范例的 `menuStartRect/overRestartRect` 即此写法)。
- ❌ scene 的 update 回调臆造首参写成 `update(running, dt)` → ✅ **scene 的 `update` 回调只收一个参数 `(dt)`**(`sceneFsm.define(name, { update(dt){…} })`;`sceneFsm.update(dt)` 只透传 dt,没有 running 之类首参)。写错首参 → 真正的 dt 落到第二参=undefined → `state.elapsedMs += undefined = NaN` → 不再 spawn/计时、计时显示「NaNs」、游戏空转看着像没 bug 实则永不推进(实测 wanglanmei-v2:菜单能进 play 但无顾客、营业额恒 0)。**任何回调签名都以 `src/plugins/<name>/api.d.ts` 为准,别臆造参数。**
- ❌ `update(dt)` 把 dt 当毫秒直接累加 `elapsedMs += dt` → ✅ **dt 单位是「秒」(≈1/60)**;游戏内毫秒计时(倒计时/spawn 间隔/耐心,常量如 60000/1300/4800)累加要 **`elapsedMs += dt * 1000`**。漏 ×1000 → 计时慢 1000 倍 → 永不到 spawn 阈值=零顾客、一局永不结束(实测 wanglanmei-v2 翻车点;`_template` 范例里 `remainMs - dt*1000` 就是这个换算)。
- ❌ `_forensicsView()`(若写)在**外层**先算好 phase 再闭包返回(`const phase=...; return { state(){ return { phase } } }`)→ ✅ **导出值在 `state()` 函数体内部实时计算**(host 只在 boot 调一次 `_forensicsView()`,外层算的值定格成 boot 快照——调试时读到的恒是旧值,实测 wanglanmei-v2:游戏真在 play 但读到的 phase 恒 menu)。照 `_template/src/game-logic.js``state: () => ({ phase: sceneFsm.current(), ... })` 写。
- ❌ 在 game-logic.js 取受控面用 `opts.ctx` / `opts.boot` → ✅ 受控面在**实例 `init(boot)``boot.ctx`**(`boot.ctx.time` / `boot.ctx.random` / `boot.ctx.getEngine()` / `boot.ctx.log()`);**输入不经 `boot.ctx.getInput`——写实例方法 `handleTap(x,y)`/`handleKey(key)`,L1 派发**;`createGame` 入参是扁平 `{plugins,bundle,viewport}`,**没有 ctx**。
- ❌ 修 bug 时图省事写 `Math.random()` / `Date.now()` / `setTimeout()` → ✅ `boot.ctx.random()``boot.ctx.random.next()` / `boot.ctx.random.range(a,b)``boot.ctx.time()``boot.ctx.time.nowMs()``timer-scheduler.after(ms,cb)`(#1:time/random 既可直接调用又保留方法,两形态都对;红线 check 必拦裸 Math.random/Date.now;**越改越要守,thrash 时尤其别引入**)。
- ✅ `sessionScore.add(n)` = `addScore(n)` 别名(#6 阶段一B:直觉名直接可用,两者等价);**其它方法名拿不准一律先 `read_file` 对应 api.d.ts**。
- ⚠️ **`host-config.js``game.js` 都是 L1 固定 plumbing,你不写**(amodel harness `write_file` 会拒)——通用能力、viewport(390×844)和工厂 wiring 已替你配好;标准 Match-3 还由 L1 装配 host-only 视觉 renderer 与音频 director`audioMusic` 不进入其 L3。**你只写 `game-logic.js`(必)+ core/render/balance/assets**。〔历史教训 HJ-AGI-003:曾让 agent 写 host-config + game.js 工厂,M3 反复破坏返回契约(漏 viewport 崩 `reading 'w'`)/ registerOrder / 取插件漏 .plugins 崩 `reading 'define'`,故把 wiring 全收进 L1 固定、L3 只写游戏逻辑收扁平 {plugins,bundle,viewport},结构性根除。〕
- ❌ 在 game-logic.js 里写 `opts.runtime.plugins` / 把入参当 `opts` 处理 —— **新架构入参是扁平的 `{ plugins, bundle, viewport }`**(L1 的 game.js wrapper 已替你摊平 opts.runtime),写 `opts.runtime.*` 取到 undefined → boot 崩。✅ **直接 `export function createGame({ plugins, bundle, viewport }) { const { sceneFsm, sessionScore, hudUi, timerScheduler } = plugins; … }`**(plugins/bundle/viewport 都是入参、平级;无任何 opts/runtime 嵌套)。HJ-AGI-003:旧架构 M3 漏 `.plugins` 一层崩 `reading 'define'`,故把嵌套收进 L1 wrapper、L3 只见扁平件,结构性根除。〕
---
## 5. 资产:美术/音乐/音效放哪 + 改图去哪找
- **放置**:普通游戏资产全部进 `assets/`(`.jpg` 美术、`.mp3` 音乐音效),并在 `assets/manifest.json` 登记每件 `file/role/bytes/sha256/source{tool,model,prompt}`。标准 Match-3 的主题音频由固定 L1 资产清单管理Writer 不新增或读取音频文件。
- **声明**:`src/assets.js`**键名间接引用**——`IMAGE_FILES = { itemSoda: 'item-soda.jpg', ... }` + 业务 id→键映射。代码里用 `assets.images.itemSoda`,不写死文件名。
- **加载**:`game.js``init``await loadAssets('./assets/')`;并行 `new Image()`,**单图失败 → 该键置 null + 记 missing,游戏照常启动**(render 走程序化/占位回退,绝不因缺图崩)。音频在首次手势后由 audio-director 懒加载解码。
- **改一张图(LLM 怎么找)**:① 查 `assets/manifest.json` 找到该 role 的行(含原 prompt/hash);② `mmx` 重新生成,覆盖同名文件;③ 更新 manifest 该行 `sha256/bytes/prompt`;④ **文件名没变 → `assets.js`/`render.js` 都不用动**;新增品类才往 `IMAGE_FILES` 加键。
---
## 6. 用 mmx 生成素材
mmx = MiniMax 官方多模态 CLI(本机已装;创始人 2026-06-12 拍板为美术/音乐现行方案,免 GPU)。**直接 CLI 调,产物存 `assets/` + 登记 manifest**:
```bash
# 美术(模型 image-01)
mmx image generate --prompt "<主体> + 统一风格词 + 纯浅奶油背景,居中,简洁" --out item-soda.jpg
# 音乐(模型 music-2.6-free,对 API key 不限量)
mmx music generate --prompt "<情绪/配器/场景>" --instrumental --bpm 92 --out bgm-idle.mp3
# 音效经 speech 合成;视频 video generate(异步)
```
铁律:**全套素材共用一组「风格词」**(美术统一性之根,见 wanglanmei-ref manifest 的 `styleWords`);每次调用都把 prompt 写进 manifest/gen-ledger,便于复现与替换。参考成本:一款游戏 ~15 件素材 ≈ ¥0.5。
> 注:mmx 走公网,认证靠 `~/.mmx/credentials.json`(非内网 key)。后端「生成触发 + 消费侧自动装配」产线尚在建(engine-E2/E3);当前 MVP 阶段 **CLI 手动产素材** 已足够。
---
## 7. 红线清单(违任一条 = 不合格)
1. 游戏工厂 `game.js` **零引擎 import**;引擎 import 只活 `entry.js`
2. 游戏代码零 `Date.now`/`performance.now`/`Math.random`/`setTimeout`/`setInterval`/`requestAnimationFrame`/`addEventListener`/`new AudioContext`——全经 `boot.ctx` 或插件;标准 Match-3 进一步禁止所有 L3 音频入口。
3. **通用机制能调插件就别自己写**;反过来,**玩法语义(规则/数值/胜负条件/状态名/美术)绝不塞进插件**——插件是 engine 级、零品类词。
4. `render(g)` 画在 host 注入的 `g`(mainContext);不自取 canvas。
5. 资产缺失必须容错降级,绝不连坐崩溃。
6. `core.js`/`balance.js` 保持纯净(无 I/O、无引擎),可 node 单测。
7. **index.html 必须自适应缩放 canvas**(用 _template 那份:`#game { width: min(100vw, calc(100vh*W/H)); height: min(100vh, calc(100vw*H/W)); ... !important }` + `#wrap` flex 居中)。**别回退到固定 `390×844` + `overflow:hidden`**——桌面/矮窗口会把画布底部(篮子/按钮/角色)裁在视区外且无法滚动(真人试玩才暴露,headless 测不到)。`!important` 是为覆盖 host 在 stub 通道写的内联 display 尺寸;buffer 不变(evidence 像素哈希不受影响),输入经 `boot-game-host.toCanvasXY``getBoundingClientRect` 归一,缩放下点击坐标仍准。
---
## 8. 自测(完成前必跑)
- **纯逻辑**:`node --test games/<game>/test/*.test.mjs`(core/balance 的规则、边界、胜负闸)。
- **真玩取证(九门)**:e2e CDP harness 跑 `A_boot/B_uncaught/C_frame/D_render/E_live`(build-health 客观门)+ `F_wiring/G_input/H_progress/I_control`(driver 门);四件套证据(截图/probe/trajectory/checklist)。详见 skill `game-e2e-cdp-harness``cheap-model-game-generation`
- **结构门**:工厂独立默认导出、零引擎 import(import-graph 测);插件只调公开 API、不直透 littlejsengine。
---
## 相关 skill / 契约
- 高频场景 recipe(要做 X→读 Y) → `.agents/skills/recipes/README.md`(命中矩形/计时器/资产回退/场景机/结算演出五篇)
- 插件能力图谱(11 注入 + 6 储备 + PLUGIN.md 指针) → `.agents/skills/plugin-capability-map.md`
- 引擎能力摘要(≤3KB) → `.agents/skills/engine-capabilities-brief.md`
- 插件库总览与受控面 → `game-runtime/src/core/api.d.ts`(PluginContext 6 项 + getEngine)、各 `plugins/<name>/api.d.ts``PLUGIN.md`
- 装载契约 → `game-runtime/src/core/game-host.d.ts`(GameInstance 五法 + GameHostFactory)
- 契约匹配范例 → `game-runtime/games/_template/`(克隆起点 + 照它改;签名以现契约为准)。`wanglanmei-ref/` 是旧 opts 签名漂移的进阶参照,只看其规模/styleWords,**别照它的 game-logic 写**
- 便宜模型造游戏 worker loop / 九门 → skill `cheap-model-game-generation``game-e2e-cdp-harness`
- 渠道发行/打包 → skill `runtime-and-multichannel`

View File

@ -1,171 +0,0 @@
---
name: narrative-game-design
description: "当为剧情互动/文字冒险/恋爱剧情类 brief 产设计阶段故事方案或评审时使用:分支/属性/多结局范式、文本与分支预算红线、结局 latch 与取证契约、替代成长轴申报(章节+图鉴)、品类 rubric 指针与金标锚点。"
---
# skill: narrative-game-design —— 剧情互动小游戏「玩法设计」作业手册(给生成 agent 的设计阶段)
> 定位:这是 **生成 agent 设计阶段(AgentScope)造"好玩的剧情互动玩法设计"的唯一作业手册**。它回答 **"设计什么才好玩"**(故事结构/分支/属性/结局/美术/音/UI 范式),与 [`littlejs-game-dev.md`](littlejs-game-dev.md)(回答"代码怎么写")配对:**设计阶段产故事概念 → 代码生成阶段照 littlejs-game-dev 产代码**。体例与 [`sim-business-game-design.md`](sim-business-game-design.md)(经营品类)同构。
>
> **铁律(创始人 2026-06-21)**:"好玩 = 上游设计层职责",不在 runtime/插件。插件只让设计**能被 juice 出手感**;**这份 skill 负责让故事本身好玩**。
>
> **可达性边界**:产出必须落得到 A-model 轻量运行时——**LittleJS 2D 手机竖屏(390×844)、单/少场景、便宜模型可生成的多文件代码、11 注入插件能力域**。故事再好,落不到这个运行时就是空想——见 §9 红线。
>
> **上位标准**:分层质量与观测线对齐 [`docs/architecture/架构/生成引擎/游戏质量与爆火能力.md`](../../docs/architecture/架构/生成引擎/游戏质量与爆火能力.md)(四层模型 / §4 rubric 计分制 / §7 便宜档档位)。剧情是**纯叙事品类**:按其 §7 申报机制,以「**章节推进 + 结局图鉴**」为品类替代成长轴(替代理由与判定见 §2、§10)。
>
> **证据基**:范式蒸馏自近年微信/抖音「互动叙事 / 文字冒险 / 恋爱养成剧情」品类的成熟模式(训练知识,非实时榜单);范式稳定可用,具体举例供参照。
---
## 0. 为什么这一品类爆(底层动机 —— 设计要击中这些)
剧情互动小游戏的"爽"来自 6 个心理引擎,**好玩 = 同时点燃尽量多个**;无趣 = 一个都没点燃:
1. **选择自主权**:"这是**我**选的路"——每个选项都可感知地改变走向,玩家为自己的故事负责。
2. **好奇心钩子**:"选另一个会怎样?"——分支的未知面比已知面更勾人;结局图鉴的 ??? 就是钩子实体。
3. **代入感**:第二人称短句 + 具体细节(风灯、脚印、火漆)让玩家 3 句话入戏。
4. **属性养成**:勇气/机智/好感随选择累积、肉眼可见地涨——"我在变强/她更喜欢我了"。
5. **收集欲**:多结局图鉴凑齐的强迫感(已解锁 2/5,还差 3 个)。
6. **低门槛零操作压力**:读一段、点一下,通勤碎片 3 分钟一局,无手速无失败焦虑。
> **反"无趣"总纲**:剧情类的无趣几乎总是因为——选项没有重量(选啥都一样)/ 文本又长又抽象 / 单结局一本道 / 属性是死数字(不影响剧情)/ 无画面感纯文字墙。§10 是落地清单。
---
## 1. 核心玩法范式(可达子集 —— design 优先从这里选)
| 范式 | 一句话 | 我们运行时怎么落(插件) |
|---|---|---|
| **分支选择**(品类地基) | 场景文本 + 2~3 选项,不同选择通向不同节点,**任选皆推进** | core 节点图 + hud-ui 选项按钮 + session-score 计推进 |
| **属性累积** | 选择给属性 +1(勇气/机智/好感…),over 屏回顾"这一局的我" | core applyEffects + hud-ui 属性 HUD |
| **属性检定** | 关键节点按属性开门:勇气 ≥3 才出现「正面迎上」选项(差一点=重玩钩子) | core 节点图 choices 加 require 条件 + 灰选项露出"差一点" |
| **多结局图鉴** | 结局 ≥3(win/lose 都有),over 屏露出「已解锁 x/N + ???」 | save 持久结局集合 + over 屏图鉴行 |
| **线索收集** | 某些选项拾取线索,持线索解锁隐藏分支/真结局 | core stats 当布尔线索位 + 检定同机制 |
| **章节推进** | 一局 = 一章(3~6 幕);「第 N 幕」进度可见 | stepCount 计幕 + hud-ui 顶栏 |
| **好感路线** | 恋爱/伙伴线:好感度决定结局归属(A线/B线/单人线) | 属性累积 + 结局判定按属性分流 |
**组合配方(经验)**:一款轻量剧情 = **分支选择(地基)+ 1 个属性轴系(2 轴起步)+ 多结局图鉴 + 检定或线索择一**。别堆超过 2 个附加机制(便宜模型 + 单屏扛不住,也稀释代入感)。
---
## 2. 故事结构 / 进度 / 留存设计
- **微型三幕**:引子(1 节点,给目标:取信/逃生/赴约)→ 发展(2~4 层分支,给两难)→ 结局(3~5 个)。**深度 3~5 步一局**(2~4 分钟),宁短勿拖。
- **首 3 分钟(决定生死)**:① 010 秒:menu 引子 ≤3 句即点开始(零阅读门槛靠短);② 1060 秒:**第一次分支 + 属性首涨**(立刻"我在塑造这个故事");③ 13 分钟:走完首局到一个结局,**图鉴露出 ??? 的未解锁位**——"还有别的结局"是剧情类的"下一个解锁"。
- **留存钩子(纯叙事品类的成长轴替代,上位标准 §7 申报)**:剧情没有数值滚雪球,替代轴 = **章节推进(幕数可见)+ 结局图鉴(x/N 收集)**;检定"差一点"(勇气还差 1 点才开这扇门)制造定向重玩;**申报判定**:图鉴分母 ≥3 且 ??? 可见、每局 ≤4 分钟可重开,替代轴才成立。
- **重玩设计铁律**:分支要**早分**(起点就分,别第 3 步才分)——首选项不同就进不同场景,重玩第一分钟就有新内容;**汇流要省着用**(全汇流=假分支)。
- **节奏**:两难选项(都想选)与安全选项(明确好坏)交替;lose 结局也要"有味道"(死得精彩=想再来),别写成惩罚。
---
## 3. 数值与文本节奏(剧情类的 balance)
- **属性轴 2 个起步**(勇气/机智、好感/理智、侠义/内力),每选择 +1;检定门槛设在**满值 -1**(如深度 4 的局,门槛 3)——"差一点"的张力,同 sim-business 卡点原则:是诱惑不是墙。
- **文本红线(design 必须给目标值,别让 code agent 拍脑袋)**:节点正文 **≤3 短句 / ≤60 字**(手机竖屏 6 行内);选项 label **≤10 字**;结局正文 ≤4 句。超长 = 玩家跳读 = 代入感清零。
- **分支预算**:节点总数 **7~12 个**(含结局)、结局 **3~5 个**、深度 **3~5 步**——便宜模型一稿写得完、玩家一局读得完。**无回环**(任意路径有限步必达结局),汇流 ≤2 处。
- **结局配比**:win : lose ≈ 2:1;至少 1 个"藏得住"的结局(需特定路径/检定),图鉴才有收集感。
- **看广告点位(可选,自愿不打断)**:解锁结局提示(指一条没走过的路)/ 直接解锁图鉴一格。剧情类**别做复活付费**(没有失败复活语义)。
---
## 4. 美术风格范式(可达 + mmx 可产 + 程序化兜底)
| 风格 | 调性 | 配色 | 适合 |
|---|---|---|---|
| **氛围剪影**(主流首选) | 悬疑/夜色 | 深底 + 单点暖光(烛光橙/月白) | 悬疑/探险/古宅 |
| **治愈水彩** | 温暖日常 | 奶油底 + 低饱和粉绿 | 恋爱/日常/童话 |
| **国风水墨** | 雅致留白 | 宣纸底 + 墨黑 + 朱砂点 | 武侠/古风/志怪 |
| 像素夜景 | 复古 | 有限色板 | 轻怀旧(谨慎) |
- **场景 = 底色氛围 + 1~2 个剪影元素**(宅邸/灯/月),换场景换剪影——程序化就能做出"到了新地方"。
- **一致性之根**:全程共用一组"风格词"(mmx 每图都带);剧情类再加一条:**各场景同一画师同一光源调性**,否则换场景像换了游戏(见 `_template-story/assets/gen-ledger.md`)。
- **程序化兜底**:无 mmx 时,深色底 + 剪影矩形 + 亮窗点 + 羊皮纸文本卡就成立(_template-story 纯程序化即过门);**别因美术阻塞可玩性**。
---
## 5. UI 范式(hud-ui 全可达)
剧情游戏 UI 套路(都用 hud-ui 落):
- **文本卡**:羊皮纸/卡片底 + 描边,正文逐字换行(中文无空格,按字宽断行);
- **选项按钮**:纵排 2~3 个、宽 ~80% 视口、高 ≥48(手指友好);**命中区=绘制区同源**(选项几何经 core.layoutChoices 一处算、onEnter 设命中区、render 只画——两处各算一份迟早漂移,玩家点中画出来的按钮却没反应);
- **属性 HUD**:顶部一行"勇气 2 机智 1",涨的瞬间配粒子(particles-juice);
- **幕数进度**:右上角「第 N 幕」——章节推进可见(替代成长轴的可见性);
- **结局结算屏**:结局**称号**大字(「果敢信使」)+ 结局正文卡 + 属性回顾 + **图鉴行(已解锁亮称号、未解锁 ???)** + 再读一次;
- **呼吸提示**:选项区上方"▼ 你的选择"缓缓明灭——引导视线,也让静态文本屏有活帧。
---
## 6. 音乐 / 音效范式(audio-music 可达)
- **BGM**:低速氛围循环(悬疑 bpm ~70 / 治愈 bpm ~90);可按幕切情绪(audio-music `setIntensity`)。mmx:`mmx music generate --prompt "夜色悬疑,弦乐低音,克制" --instrumental --bpm 70`
- **音效(代入感命脉,务必有)**:选项确认「嗒」(click)· 属性上涨「叮」· 检定通过号角(win)· lose 结局低音(fail)· 结局达成欢呼(win)。**每次选择必有确认音**——剧情类的"即时反馈"一半靠它(audioMusic `playSfx` 语义名自动映射,无声卡静默降级不崩)。
- 红线:**宁可程序化音效,也别没有反馈音**(无声翻页 = 无趣的隐形元凶)。
---
## 7. 首屏 / 留存视觉钩子(通用,MVP 也用)
- 首屏 3 秒抓人:标题 + **引子 ≤3 句 + 一个悬念**(「屋里似乎还有别人」),立即可点开始——**别放长背景设定墙**;
- 结局图鉴 ??? 当门面(未知结局的诱惑);
- 属性回顾的"这一局的我"(勇气 3 机智 1 = 我的人格镜像);
- lose 结局也精彩(「误入陷阱」的戏剧性 > 惩罚感)。
---
## 8. 设计阶段输出配方(生成 agent 据 brief 产出这些 → 喂代码生成)
给定一句话 brief,设计阶段应产出一份**结构化故事设计**,**逐项落到运行时**:
1. **题材与引子**(标题 + ≤3 句开场,含目标与悬念);
2. **属性轴 2 个**(轴名要贴题材:武侠=侠义/内力,恋爱=好感/理智);
3. **故事节点图**(7~12 节点含 3~5 结局:每节点 = 正文 ≤60 字 + 2~3 选项label ≤10 字 + next + effects;标死:无死链、无回环、win/lose 都有);
4. **检定/线索(择一,可选)**:哪个节点检定哪个属性、门槛多少(满值-1)、差一点时露什么;
5. **结局表**(id / 称号 / outcome / 正文 ≤4 句;至少 1 个藏得住);
6. **美术风格词**(一组,全场景共用同调性)+ 场景剪影清单(标 mmx 或程序化);
7. **音效清单**(选择确认/属性涨/结局 win/lose)+ BGM 情绪;
8. **UI 布局**(文本卡/选项区/属性 HUD/图鉴行,§5);
9. **首 3 分钟脚本**(逐步:引子→首分支→首属性涨→首结局→图鉴 ??? 露出)。
> **MVP-first 铁律(关乎 code agent 能否收敛)**:设计**聚焦可玩核心** —— 分支选择 + 1 属性轴系 + 3 结局 + 图鉴 + 基础音效,节点 ≤10 个起步。**语音演出 / 立绘系统 / 章节解锁付费 / 存档树回溯 / 打字机逐字动画 等一律标「后续·MVP 不做」**,别一稿堆满 —— 设计太满 → code agent 实现负担过重 → read-thrash 不收敛。**先出能玩的一章,再谈长篇。**
> 这份设计 = 代码生成阶段的"图纸";生成 agent 照 littlejs-game-dev 把它实现为多文件代码(剧情品类从 `game-runtime/games/_template-story/` 骨架起步,故事数据落 core.js 参数空间)。**设计阶段把"好玩"想清楚,代码阶段只管正确实现。**
---
## 9. 可达性红线(落不到运行时的设计 = 废稿,必剔除/简化)
- ❌ 长文本墙 / 全屏小说页 → 节点 ≤60 字 + 文本卡(§3 红线)。
- ❌ 语音演出 / 视频过场 / 立绘表情系统 → 程序化剪影 + 少量 mmx 静态图。
- ❌ 存档树 / 回溯任意节点 UI → 一局一路走到结局,重玩靠「再读一次」+ 图鉴钩子。
- ❌ 自由输入对话(打字回复)→ 全部预设选项(2~3 个)。
- ❌ 计时逼选 / QTE 限时选项 → 剧情类零时间压力(它的爽点是"想清楚再选");要紧张感用文案不用倒计时。
- ❌ **选项做成「先选中再确认」两步** → ✅ **选项单击即推进**(点了就走,读者的手不该被流程绊住);**深度靠分支/检定/图鉴,不靠交互步数**。
- ❌ 结局后自动跳回菜单/自动重开 → **结局必须 latch**(phase 驻留 over,结局卡停在屏上给玩家看够),「再读一次」只在玩家点按钮时触发。
- ✅ 留下的:分支选择、属性累积/检定、线索、多结局图鉴、章节推进、好感路线 —— 全在 11 插件 + 单屏 + 便宜模型可生成范围内。
---
## 10. 反"无趣"落地清单(design 自检,逐条必过)+ 品类 rubric
**通用 8 条自检**(与 [`sim-business-game-design.md`](sim-business-game-design.md) §10 同一张表,所有品类通用):①即时反馈 ②可见成长 ③下一个解锁 ④30 秒爽点 ⑤数值滚雪球 ⑥情感锚 ⑦放置回归 ⑧音反馈。剧情类的对位:①=选择确认音+粒子;②=属性 HUD 上涨 + 幕数推进;③=图鉴 ??? 与检定"差一点";⑤⑦天然弱(纯叙事无滚雪球/放置,**按上位标准 §7 申报以「章节推进+结局图鉴」为替代轴**,不硬凑)。
**剧情品类扩展 rubric(5 条,喂 LLM 丰富度验证 agent · 非阻塞)**。规范来源 = 质量模型 SoT §4:每条 0/1 + 层标(L2 内容丰富 / L3 留存结构 / L4 传播钩子),与通用底座 12 条**分母分开、独立小计**(通用底座第 12 条「核心操作非无脑」的剧情对位 = 检定/分流即真实选择代价,选错落 lose 结局);条目形状 =(编号 / 层标 / 一句可判定判据 / 一对正反例),与 `cheap-worker/cheap_verify.py` 的 RICHNESS_CHECKLIST 三元组同形,供品类扩展评分接线时直接注入;**纯 LLM 评分,绝不写成代码校验、不进九门、不进脚手架**(创始人红线):
五条(选择有重量 / 场景有画面感 / 结局图鉴钩子 / 属性塑造可见 / 结局可炫耀)的机器版 = **单源 [`cheap-worker/fixtures/genre-rubrics/narrative.json`](../../cheap-worker/fixtures/genre-rubrics/narrative.json)**(2026-07-04 迁出,判据与正反例一字不动;改条目只改 fixture,本节不再维护表格副本——评分尺变更的金标复验纪律见下段)。
**金标锚点(质量模型 SoT §4 规范三:每品类 ≥1 金标正例 + ≥1 薄反例;rubric 文本或评分 prompt 变更须金标复验,单款分组小计漂移超 ±1 即回退)**——2026-07-03 C1 欠账清偿,以 12 条口径正式重锚(生产口径 genre=narrative,同输入复采样 n=3 取中位,评分入口 `cheap_verify.verify_richness`):
- 金标正例 = `p11a-s1`(「深渊回声」深海科考题材,brief 同 `cheap-worker/fixtures/golden-samples/narrative/p11a-s1/evidence/run-summary.json`,源已固化入仓 `cheap-worker/fixtures/golden-samples/narrative/p11a-s1/`)。**12 条口径基线 = 8/12**:L2 5/7 · L3 1/3 · L4 2/2;第 12 条「核心操作非无脑」命中(属性检定开门 + 结局按属性分流构成真实选择代价,选错落 lose 结局);品类扩展 5/5。
- 薄反例 = `_template-story` 骨架原样(轻起点「古宅一夜」演示故事,无 run-summary,brief 取演示题材一句话)。**12 条口径基线 = 7/12**:L2 4/7 · L3 1/3 · L4 2/2;第 12 条命中;品类扩展 4/5。刻意保持"能过门但未丰富",丰富是生成 agent 的活。
- 正反例分差(重述):通用 8/12 vs 7/12 差 1、品类 5/5 vs 4/5 差 1,比旧尺(7/11 vs 3/11 差 4)大幅收窄。收窄不是评分尺退化,而是纠了旧尺对薄反的两处高估:① 旧薄反 3/11 系空 brief + 单发 stingy 观测,低于骨架真实地板——该骨架恒有 juice(即时反馈)+ playSfx(音反馈)两个 L2,真实 L2 地板 ≥2;② 纯叙事天然缺数值成长类判据(可见成长/数值滚雪球/放置回归),judge 在这几条上跨样摇摆,通用 L2/L3 噪声大(替代轴按 §2 以「章节推进 + 结局图鉴」申报)。故本品类稳定判别信号不在通用 12 条、而在品类扩展段 + L4(跨样最稳)与内容深度;通用分只作粗档观测,别当紧数字读。
- 方差纪律:M3 单发方差 ±2~4 实测复现(p11a-s1 genre=None 隔离三样 8/5/5、中位 5/12,而生产口径 genre=narrative 三样 8/8/8),故一律取中位、且以品类-coeval 生产口径为锚(本轮更稳)。品类段(正例 5/5、薄反 4/5)与 L4(均 2/2)跨样稳定,是回退判定最可靠的观测维。
- 史注(旧尺):2026-07-02 首批曾以 11 条旧尺记 正例 7/11(L2 4/6·L3 1/3·L4 2/2)/ 薄反 3/11(L2 1/6·L3 0/3·L4 2/2)、差 4 分;旧尺薄反 L2 1 低于骨架地板已如上纠正,12 条口径为现行正式基线。
---
## 相关
- 代码层实现 → [`littlejs-game-dev.md`](littlejs-game-dev.md)(11 插件 API / 结构 / 资产-mmx / 工厂契约)
- 品类骨架(few-shot 过门正例)→ `game-runtime/games/_template-story/`(从它起步换故事;README 有「你写什么 vs 调什么」边界)
- 便宜模型生成 worker / 九门 → [`cheap-model-game-generation.md`](cheap-model-game-generation.md)
- 经营品类对照(体例同构)→ [`sim-business-game-design.md`](sim-business-game-design.md)
- 上位质量标准 → [`docs/architecture/架构/生成引擎/游戏质量与爆火能力.md`](../../docs/architecture/架构/生成引擎/游戏质量与爆火能力.md)(§4 rubric 计分制 / §7 档位观测线)

View File

@ -1,66 +0,0 @@
# 插件能力图谱 —— 注入了什么、储备着什么、为什么
> 便宜档生成 agent 只能用**已注入**的插件(经 `plugins.<键>` 取,不能 import。这份图谱回答现在桌上有哪 11 件、
> 每件去哪读详细用法、还有哪些造好了但没摆上桌、以及为什么没摆。改这里的裁定要同步改机器门
> `.agents/tools/plugin-surface-gate.py` 的台账常量(三方一致性它会自动对账)。
## 已注入的 11 件(`plugins.<键>` 直接用)
方法**头部选型**见 littlejs-game-dev.md §4**精确签名**读各插件 `api.d.ts`**用法散文 + 集成注意**读各插件 `PLUGIN.md`(比 api.d.ts 多了「怎么装、坑在哪、示例」)。
| 键 | 目录 | 一句话能力 | 详细读 |
|---|---|---|---|
| `sceneFsm` | scene-fsm | 场景/状态机menu→play→over | `game-runtime/src/plugins/scene-fsm/{api.d.ts,PLUGIN.md}` |
| `sessionScore` | session-score | 计分 + 胜负闸 | `game-runtime/src/plugins/session-score/{api.d.ts,PLUGIN.md}` |
| `hudUi` | hud-ui | HUD/UI 绘制 + 命中判定 | `game-runtime/src/plugins/hud-ui/{api.d.ts,PLUGIN.md}` |
| `timerScheduler` | timer-scheduler | 受控时钟调度(毫秒) | `game-runtime/src/plugins/timer-scheduler/{api.d.ts,PLUGIN.md}` |
| `save` | save-progress | KV 持久化(最佳分等) | `game-runtime/src/plugins/save-progress/{api.d.ts,PLUGIN.md}` |
| `gamefeel` | gamefeel | 手感:缓动 + 输入缓冲 + coyote/combo | `game-runtime/src/plugins/gamefeel/{api.d.ts,PLUGIN.md}` |
| `juice` | particles-juice | 粒子 + 打击感(顿帧/震屏/闪白) | `game-runtime/src/plugins/particles-juice/{api.d.ts,PLUGIN.md}` |
| `palettePost` | palette-post | 调色 + 后处理(预设一键观感) | `game-runtime/src/plugins/palette-post/{api.d.ts,PLUGIN.md}` |
| `audioMusic` | audio-music | 程序化音乐 + 语义音效 | `game-runtime/src/plugins/audio-music/{api.d.ts,PLUGIN.md}` |
| `collision` | collision | 碰撞查询(纯数学) | `game-runtime/src/plugins/collision/{api.d.ts,PLUGIN.md}` |
| `physics` | physics-lite | 街机运动原语(非刚体) | `game-runtime/src/plugins/physics-lite/{api.d.ts,PLUGIN.md}` |
`runtime-probe`(取证不注入):只在收口取证六锚(启动耗时等),不进 `plugins` 面,生成 agent 用不到。
## 仅 puzzle L1 的受保护注入3 件)
`match3-producer-profile` 不是第 12 个通用插件。它只在 `_template-puzzle/src/host-config.js` 收到显式
`match3.orthogonal-swap-v1` 时由 L1 实例化,以 `plugins.match3ProducerProfile` 给 Match-3 Writer 调用;普通 puzzle、
其它品类、通用 `_template``all-plugins` 与 cheap-system
白名单都不注入。该能力冻结 390×844 / 8×8 / 40px 棋格几何、472 像素 selection mask 和主题安全区,
同一实例再由 `__genHost.readProducerCapabilityProbe()` 进入可信 preflight。L3 `_forensicsView` 或页面全局自报不参与身份判断。
`match3-visual-timeline` 同样只在显式 `match3.orthogonal-swap-v1` 下创建,以
`plugins.match3VisualTimeline` 注入,并与 producer 一起进入 `registerOrder`。它根据标准 0-based 8×8 kindToken 快照
独立分配稳定 `pieceId`,封存 `Match3VisualTransaction/1`,推导消除、列压实与顶部补位,按固定阶段输出
1-based 视觉棋子并用 `isLocked()` 统一锁住第二击后的额外输入。它拥有独立 id/version/configHash绝不修改
`match3ProducerProfile` 已校准的身份。`visualTimelineCapability` 已从 host-config 独立返回;可信 host probe 与 runner
visualDrain/Proof/3 接线属于后续 contract-first 实现线,本次不冒充已完成。
`match3-audio-director` 只在同一标准 Match-3 profile 下由 L1 以冻结 `Match3AudioTheme/1` 和同一份
visual timeline 实例创建。它进入 `registerOrder` 并以 `audioDirectorCapability` 单独返回给可信 host但不写入
L3 `plugins`;标准 Match-3 的 `audioMusic` 同样从 L3 门面移除。导演独占包内 BGM、交换/回滚/逐轮消除/下落/终局
cue、master 静音与只读 probe按 host-only transition 账调度,避免游戏自制音效、同步连锁叠音或双 BGM 图。
这类“品类专用且影响验收像素”的能力必须登记在机器门 `PROTECTED_INJECTION_DIRS`,不能为了通过目录门而塞进
通用 11 件,也不能降成储备。机器门同时核对唯一模板、`plugins` 注入、`registerOrder` 与各自能力返回键,
并拒绝其它官方模板出现同一目录或键。新增受保护品类能力时必须同时写清唯一模板入口、Writer 调用键、可信读口和未注入范围。
## 未注入的能力储备6 件·造好了但没摆上桌)
这 6 件都是通过测试的真插件(各有 `api.d.ts`/`impl.js`/`test`),但当前 5 个注入品类(经营/剧情/TRPG/解谜/非遗)全是
**点击/回合制** tap 玩法,它们服务的是别的品类或需要额外装配,故本波不注入。生成 agent **现在读不到、也用不了它们**——
别在 game-logic 里写 `plugins.autoTargeting` 之类,会是 undefined。每件的裁定依据与「什么时候该注入」
| 目录 | 是什么 | 为什么没注入 | 注入触发条件 |
|---|---|---|---|
| **auto-targeting** | 索敌策略族(最近/锥内/随机选目标) | 服务射击/动作类「自动锁敌」,当前 tap 品类用不上 | W-GENRE 上动作/射击品类时 |
| **swarm-steering** | 均匀网格 + 群体运动学(同屏千级实体) | 服务动作/idle-sim 的大规模实体tap 品类无此需求 | W-GENRE 上动作或放置模拟品类时 |
| **entity-pool** | 对象池 + swap-remove局内零 GC | 密集实体(子弹/敌群)性能件;粒子已由 particles-juice 承接 | 同上,动作品类需要密集实体时 |
| **virtual-joystick** | 浮动原点虚拟摇杆 + 轨迹 driver | 连续移动输入,与当前 `handleTap` 输入契约不同轴tap 品类不需要 | W-GENRE 上需要连续移动/瞄准的品类,且输入契约扩展后 |
| **canvas-ui-kit** | 卡片选择器 / 滚动列表 / 顶栏hud-ui 之上的组合件) | 需 `draw: hudUi` 复合装配;对 TRPG 选天赋、经营选货有价值,但注入是行为面变更 | 后续给 TRPG/经营注入的**首选候选**——单起一个装配任务,同步 3 方对账 |
| **hit-feedback** | 反馈档位编排(一次事件→顿帧/震屏/音效/飘字) | 需 `juice + sfx` 复合装配;与直调 `juice`+`audioMusic` 重叠skill/settlement recipe 已教直调 | 若要把反馈收敛成单入口 + 音画同帧取证时 |
把任一储备提升为**通用注入** = 改 6 个 `_template*/src/host-config.js``plugins` 装配 + prompt 白名单cheap-system.md+ tools.mjs `PLUGIN_KEY_DIR`,三处同改、再跑 `plugin-surface-gate.py` 验三方对齐。品类专用受保护注入不走这条通用扩面路径,须登记 `PROTECTED_INJECTION_DIRS` 并只改其唯一 L1 模板。gate 会拦「注入了但白名单漏列」「白名单列了但没注入」「受保护品类件误进通用面」「新插件目录没裁定」。

View File

@ -1,107 +1,93 @@
---
name: prompt-governance
description: "当新增或修改生命周期 prompt、搭建 Prompt Registry 与四道闸 eval 门禁、或对接人在环节点时使用:prompt 即第 8 类契约,contracts/prompts 版本化单一事实源与治理流程(段A版本闸+段B真模型闸 CI 已接)。"
---
# Prompt 工程治理手册prompt-governance
> 蒸馏来源:执行版 [`../../docs/architecture/架构/生成引擎/prompt治理.md`](../../docs/architecture/架构/生成引擎/prompt治理.md)HJ-PROMPT-GOV-EXEC-001
> 适用:新增/修改任何生命周期 prompt安全/意图/模板/生成/素材/质量/修复/元信息八阶段,及 tier2 富游戏线)、搭建 Prompt Registry 与 eval 门禁、对接人在环HITL节点。
> 配套:契约先行见 [`./contract-first-development.md`](./contract-first-development.md)AI 生成链路见 [`./agentic-amodel-generation.md`](./agentic-amodel-generation.md);运行时/Cocos 见 [`./runtime-and-multichannel.md`](./runtime-and-multichannel.md);选型见 [`../knowledge/tech-decisions.md`](../knowledge/tech-decisions.md) §1.1;降级红线见 [`../rules/security-and-reliability.md`](../rules/security-and-reliability.md)。
> 蒸馏来源:执行版 [`../../docs/agent-specs/prompt治理体系-execution.md`](../../docs/agent-specs/prompt治理体系-execution.md)HJ-PROMPT-GOV-EXEC-001
> 适用:新增/修改任何生命周期 prompt意图/代码生成/素材/剧情/锁风/测试/平台转换/运营诊断)、搭建 Prompt Registry 与 eval 门禁、对接人在环HITL节点。
> 配套:契约先行见 [`./contract-first-development.md`](./contract-first-development.md)AI 生成链路见 [`./ai-generation-pipeline.md`](./ai-generation-pipeline.md);运行时/Cocos 见 [`./runtime-and-multichannel.md`](./runtime-and-multichannel.md);选型见 [`../knowledge/tech-decisions.md`](../knowledge/tech-decisions.md) §1.1;降级红线见 [`../rules/security-and-reliability.md`](../rules/security-and-reliability.md)。
>
> **⚠️ 引擎上下文纠偏2026-06-12**:本手册**核心理念Prompt 即第 8 契约 / Registry / eval 门禁 / HITL现行有效不变**;但下文凡以 **Dify / OpenGame** 为注入目标/引擎载体的描述均按现行裁决改读——**Dify/OpenGame=降级远期未部署**C2/HJ-GEN-001Tier1 生成主线=**new-api 网关 + agent 写码于插件库**(引擎=LittleJS 增强发行版agentic 编排基建=**AgentScope 2.x**HJ-AGI-001。"模板填充"措辞按模板哲学终裁更新玩法模板层废除。Cocos-MCPTier2/3仍现行有效。
>
> **⚠️ 现行化重写2026-07-05**§1§3 已按现行 `registry.yaml`(阶段编号目录重构 + 头部消费面三态对账)与 W-PCI 后口径整块重写——目录树、加载器类名(`PromptResourceLoader`)、两条 live 生成线(便宜档 cheap-worker / tier2 AgentScope形态均改为现行事实此前散在正文的划线改读不再需要、随句清理。上条 2026-06-12 记的「Cocos-MCP 仍现行有效」本次一并修正Cocos-MCP 降为 3D 与渠道导出的人在环工具,非 live 生成形态。
---
## 核心理念Prompt 即第 8 类契约资产
所有 prompt 进 git `contracts/prompts/` **单一事实源**,与 Day-0 七契约同级,在 `registry.yaml` 按条目登记id / version / stage / owner / file / desc / eval各配 Golden eval 集与硬约束。**运行时按注册条目正文加载注入、不在引擎/编排内核里内嵌**——便宜档由 cheap-worker 读、tier2 由 AgentScope gen-worker 读、后端模板策划由 `PromptResourceLoader` 读,延续"能力增强放壳层、不动内核"的纪律。
所有 prompt 进 git `contracts/prompts/` **单一事实源**,与 Day-0 七契约同级。每条 prompt 绑定一份完整契约:`输入 Schema + 输出 Schema + 硬约束块 + Golden 样本集 + owner + version`。**运行时按 `id@version` 加载注入,不在引擎/编排内核里内嵌**~~Dify/OpenGame/Cocos~~→现行=LittleJS 插件库 / AgentScope / Cocos-MCP——延续"能力增强放壳层、不动内核"的纪律。
> 为什么必须治理:多条生成线(便宜档 / tier2 / 后端模板策划)使 prompt 数量与形态膨胀;无单一事实源 → 跨链路无法统一治理、改动无法回归验证。
> 为什么必须治理:多引擎/多链路使 prompt 数量与形态膨胀;无单一事实源 → 跨链路无法统一治理、改动无法回归验证。~~原述"双引擎 OpenGame+Cocos-MCP"~~ 中 OpenGame 已退役,见上方纠偏。)
---
## 1. Registry 目录结构
Prompt 按生成链路阶段编号归档,`registry.yaml` 作索引(唯一事实源),`eval/` 放各条 Golden 集,同级还摆着三个治理脚本:
```
contracts/prompts/
├── registry.yaml # 注册表(索引):每条 prompt 的 id / version / stage / owner / file / desc / eval
├── README.md # 目录说明 + registry 一致性 CI 门说明
├── check_registry.py # CI 门:registry.yaml 各条 version 与对应 .md frontmatter version 对齐
├── check_version_bump.py # 段 A 离线版本闸:改正文必升 version(见 §6)
├── eval_gate.py # 段 B 真模型闸:对 live 条目跑四道闸、真调 M3(见 §6)
├── 01-safety/ # Prompt 安全/注入检测
├── 02-intent/ # 意图解析
├── 03-template/ # 模板匹配
├── 04-config/ # 生成主线:cheap-system(便宜档主 prompt) + 各品类 -designer(模板策划/编码)
├── 05-asset/ # 素材生成(ComfyUI 引导)
├── 06-quality/ # 质量评估
├── 07-fix/ # 失败修复/重生成
├── 08-meta/ # 标题/简介/封面文案
├── 09-tier2-richgame/ # tier2 富游戏自治生成线的 8 条 prompt(AgentScope 多 agent)
└── eval/<id>/ # 每条 prompt 的 Golden eval 集(inputs.jsonl / labels.jsonl / baseline.json / runs/)
├── registry.yaml # 索引:所有 prompt 的 id/版本/owner/绑定 schema/eval 集
├── _schemas/ # 输入/输出 JSON Schemaprompt 的契约)
├── intent/ # 意图解析、模板匹配owner=aigc
├── codegen-opengame/ # Tier1 文生代码owner=aigc
├── codegen-cocos-mcp/ # Tier2/3 agentic 工具编排owner=studio
├── asset/ # 素材生成(图/音/角色/特效)+ IP LoRA 约束owner=aigc
├── narrative/ # 剧情/分支对白/关卡owner=studio
├── lockstyle/ # 锁风约束(风格-版权一致性owner=compliance
├── qa/ # 测试用例 + 可玩性脚本owner=aigcT-AGC-09
├── convert/ # 打包/平台转换约束(尺寸/资质/违禁词owner=runtime
└── ops/ # AI 诊断/改版任务owner=telemetry
```
0108 是按生成链路顺序保留的阶段槽,眼下真正落有条目的是 `01-safety``04-config`(生成主线含便宜档)、`06-quality``07-fix`agent 闭环批跑遗留),加上 `09-tier2-richgame`tier2 八条);其余为规划槽、暂无 prompt。
每条 prompt 在 `registry.yaml` 登记一条索引,字段固定为 id / version / stage / owner / file / desc / eval没有 engine / tier——生成线归属由 stage 与消费方代码决定,不写进注册表。摘一条现行 live 条目为例:
每条 prompt = frontmatter 契约头 + 模板体:
```yaml
- id: safety.prompt-check # 唯一 id = 阶段域.角色
version: 1.0.0 # 语义化版本;改正文必升(段 A 版本闸强制)
stage: "01-safety" # 阶段编号目录(01-safety…08-meta / 09-tier2-richgame)
owner: WS2 # 唯一负责工位
file: 01-safety/prompt-safety-check.md # 正文文件(其 frontmatter 至少含 id+version)
desc: 创作者 Prompt 安全/注入检测(生成链路第 1 节点)
eval: eval/safety.prompt-check/ # Golden eval 集(四道闸:Schema/成功率/回归diff/成本延迟)
---
id: codegen-opengame.scaffold # 唯一 id目录.阶段)
version: 1.2.0 # 语义化版本
owner: WS2/aigc # 唯一负责工位/模块
tier: tier1 # tier1 | tier2 | tier3
engine: opengame # opengame | cocos-mcp | dify | comfyui | runtime
stage: scaffold
input_schema: _schemas/codegen-input.json
output_schema: _schemas/game-config.json
constraints: # 硬约束块(产物必须满足)
- 首屏≤2MB, 总包≤10MB
- 游戏内零网络请求CSP connect-src none
eval_set: eval/codegen-opengame.scaffold/
guardrails: [injection-detect, schema-validate, asset-ref-check]
---
{{system_prompt}}
... 模板体,带 {{变量槽}} ...
```
注册表是索引与对账基准prompt 正文另存于 `file` 指向的 .md各带一份 frontmatter至少 id 与 version`check_registry.py` 逐条比对两侧 version 防漂移)。有的正文还内嵌 output_schema / hard_constraints如 safety或 tier / engine 注记(如 tier2 各条),那是正文自己的元信息,注册表条目只认上述七字段。
---
## 2. 加载 / 注入机制
三条生成线各有自己的加载器,共同纪律是按 `file` 指向的正文加载、best-effort 回落进程内内置原文、绝不因 prompt 读取失败中断生成:
- **aigc / studio 壳层** 持有 `PromptRegistryLoader`:按 `id@version` 取 prompt 文本并用变量渲染。
- ~~**Dify 节点**prompt 用 `{{registry:intent.parse@1.2.0}}` 引用,壳层调用前注入实际文本。~~Dify 已退役;现行=壳层/编排器调 new-api 前按 `id@version` 渲染注入。)
- ~~OpenGame~~ / Cocos-MCP / ComfyUI壳层把对应 prompt 作为参数/ system prompt 传入OpenGame 已退役Tier1 改 agent 写码于 LittleJS 插件库Cocos-MCP/素材链不变)。
- **DB 镜像(可选)**:只读,仅为运行时热加载提速;写入路径唯一为 git。
- **后端模板策划 prompt`04-config` 各品类 `-designer`** 由 game-module-aigc 的 `PromptResourceLoader` 加载。构建期 maven-resources-plugin 把 contracts 下的 prompt 与 schema 快照进 `classpath:wanxiang-contracts/`jar 内只是构建时快照git `contracts/` 仍是唯一事实源);启动逐模板自检,任一缺失或形态非法即整体 `ready=false` 自禁用,执行器 tick 首检不认领任务、不崩 app。渲染只做 `{{input.xxx}}` 替换加残留占位符检测version 仅作可追溯日志,不做 registry 对账——那是 CI 四道闸的职责Java 不复制治理逻辑。配了 `aigc.prompts.dir` / `AIGC_PROMPTS_DIR` 外置目录后每 60s 热取,运营改完 prompt 无需重启即生效读失败保留上次、classpath 快照永久兜底。
- **便宜档 cheap-worker**`cheap_roles.py``_load_system_prompt()` 运行时读 `04-config/cheap-system.md` 正文,读不到、脏或缺文件即回落进程内 `_SYSTEM_PROMPT`
- **tier2 富游戏 gen-worker**`roles.py``worker/prompts.py``prompts.load` 运行时读 `09-tier2-richgame/` 下八条正文,读不到或脏即回落 roles.py 内置原文。
版本一致性由 `check_registry.py` 守门:逐条比对 registry 的 version 与对应 .md frontmatter 的 version不一致即拒绝合入`PromptResourceLoader` 构建期快照版本与注册表宣称漂移、审计失真),已挂 pre-commit 与服务端 `contract-gates.yml`
> 改 prompt = 改 git 正文加升 version → PR → eval 门禁 → 合入 → 下次构建或热取自动携带新版。
> 部署时强制校验 registry 版本一致CI 卡 Schema。改 prompt = 改 git → PR → eval 门禁 → 合入 → 同步运行时。
---
## 3. 两条 live 生成线的 prompt 形态
## 3. 两套 Prompt 形态
现行 runtime 只有两条生成线在跑prompt 形态、加载器与失败回落各管一摊:
| 维度 | 便宜档 cheap-worker | tier2 富游戏 AgentScope 多 agent |
| 维度 | OpenGame promptTier1 | Cocos-MCP promptTier2/3 |
|---|---|---|
| 主 prompt | 单条 `config.cheap-system` | `tier2.*` 八条leader + 四工作室专家 + 兜底 `design-system` + 单写 `writer` + 软检 `player` |
| 范式 | A-model 一个 agent 写 LittleJS `src/` 源工程(入口契约 + 插件 + 红线 + 输入契约 + 完成判据) | AgentScope 2.0.2 ReAct 两阶段:阶段 1 工作室多 agent 出设计稿 → 阶段 2 单写 ReAct 写 Phaser 源码 + L3 视觉软检玩家 |
| AI 参与深度 | 轻便宜档插件库承重、AI 少写) | 重(自治多轮、有状态工具编排) |
| 归属目录 | `04-config/cheap-system.md` | `09-tier2-richgame/`(八文件) |
| 消费方 | `cheap_roles.py` `_load_system_prompt()` | `roles.py``worker/prompts.py` `prompts.load` |
| 失败回落 | 回落 `cheap_roles._SYSTEM_PROMPT` | 回落 roles.py 内置原文 |
| 形态 | 生成式:文本 → 游戏代码 | agentic驱动 158 个编辑器工具的工具编排 |
| 调用 | 单次/6 阶段 pipeline | 多步、有状态、多轮工具调用 |
| 归属 | **aigc**(无状态生成原子) | **studio**有状态创作编排T-STU-05 |
| 失败降级 | 退化为确定性兜底产出(~~模板填充~~,措辞按模板哲学终裁更新) | 退化为 Tier1MVP 仅探针 |
> **Cocos-MCP 不是 live 生成形态**:它降为 3D 与渠道导出的人在环工具(人工在编辑器里操作),不进 MVP 生成 runtime也不在本治理的形态对照内。素材生成链ComfyUI 引导,归 `05-asset/`)另走各自门禁
> 目录分治(`codegen-opengame/` vs `codegen-cocos-mcp/`),加载器按 `engine` 字段分发,不强行统一模板。
---
## 4. 新增一条 Prompt操作步骤
1. 在对应阶段编号目录建 prompt 正文 .md写 frontmatter至少 id + version归属写 ownerstage 与注册表一致)。
2. 有输出约束的(如 `04-config` 各品类模板策划)绑定 `contracts/templates/<id>.schema.json`;纯 system prompt`cheap-system` / tier2 各条)无外置输出 schema
1. 在对应阶段目录建 prompt 文件,写全 frontmatterid/version/owner/tier/engine/stage)。
2. 定义或复用 `_schemas/` 下的输入/输出 JSON Schemafrontmatter 绑定
3. 写硬约束块 + guardrails注入检测/Schema 校验/资源引用校验)。
4. 建 Golden eval 集 `eval/<id>/``inputs.jsonl`核心样本)+ `labels.jsonl`(期望标注)+ `baseline.json`(基线快照,`--update-baseline` 建,闸 3/4 据此判增量);每次跑的台账 append-only 落 `runs/`
5. 在 `registry.yaml` 登记条目id/version/stage/owner/file/desc/eval
4. 建 Golden eval 集 `eval/<id>/``inputs.jsonl`510 条核心样本)+ `expect.yaml`(期望属性/阈值)+ `baseline/`(基线产物快照)
5. 在 `registry.yaml` 登记索引
6. 提 PR → eval 门禁绿 + 人工抽检 → 合入 → 部署同步。
## 5. 修改一条 Prompt 并验证效果(操作步骤)
@ -121,12 +107,10 @@ contracts/prompts/
|---|---|---|
| ① Schema 通过率 | 输出符合 output_schema 的比例 ≥ 阈值 | 评审版 §4.4 |
| ② 生成成功率 | 可运行 + 质量通过 ≥ **80%** | MVP 验收线 |
| ③ Golden 回归 | 与 `baseline.json` 关键字段 diff防退化 | 泛化自 T-AGC-08 |
| ③ Golden 回归 | 与 `baseline/` 关键字段 diff防退化 | 泛化自 T-AGC-08 |
| ④ 成本/延迟 | token/耗时不劣化 | 评审版 §4.4 |
四闸全绿 → 人工抽检 K 条(主观质量/可玩性)→ 合入。
**怎么跑2026-07-04 已接成 CIW-PCI**:四道闸按成本分两段落地。**段 A 离线版本闸**`contracts/prompts/check_version_bump.py`)兜闸 0「改正文必升 version」——相对基线剔除 frontmatter version 行后比正文,正文变而 version 没升即 exit 1`.githooks/pre-commit``.gitea/workflows/contract-gates.yml` 服务端门,每次提交自动跑、秒级零成本。**段 B 真模型闸**`contracts/prompts/eval_gate.py --id <id>``--changed --base <ref>`)跑闸 1~4对改动的 prompt 拿其金标集真调 MiniMax-M3经 new-api、thinking 关、key 从 env 读、`ProxyHandler({})` 绕系统代理)判定;`.gitea/workflows/prompt-eval.yml` 手动 `workflow_dispatch` 触发,单跑预算硬上限 ≤¥5。**只焊 live 面**:接门前先查 [`registry.yaml`](../../contracts/prompts/registry.yaml) 头部消费面三态对账——非 livefossil/batch-relic条目 SKIP 豁免、live 但无金标集 fail-closed绝不假绿别把门焊在已下架的化石 prompt 上白花钱。调用失败(网关 500/限流)排除出判定分母、建议复跑(不阻塞、不误判 prompt。台账 append-only 落 `eval/<id>/runs/`,回归基线 `eval/<id>/baseline.json``--update-baseline` 建,闸 3/4 据此判增量)。**可玩性 MVP 不自动评分**(结构校验测不了"好玩"),靠人工抽检 + 上线后行为指标反哺下一轮。
四闸全绿 → 人工抽检 K 条(主观质量/可玩性)→ 合入。**可玩性 MVP 不自动评分**(结构校验测不了"好玩"),靠人工抽检 + 上线后行为指标反哺下一轮。
---
@ -139,7 +123,7 @@ contracts/prompts/
## 8. 测试脚本生成T-AGC-09
aigc 新增**无状态原子**:输入 GameConfig → 输出可玩性测试脚本(启动/输入响应/边界);由 **runtime 编译流水线执行**为入库门禁的一环。其 prompt 若落地归质量评估阶段 `06-quality/`。(现行可玩性判定实走生成线九门真玩 harness本条 T-AGC-09 独立测试脚本 prompt 尚未落地。)不新增模块。
aigc 新增**无状态原子**:输入 GameConfig → 输出可玩性测试脚本(启动/输入响应/边界);由 **runtime 编译流水线执行**为入库门禁的一环。其 prompt `contracts/prompts/qa/`不新增模块。
---
@ -148,8 +132,8 @@ aigc 新增**无状态原子**:输入 GameConfig → 输出可玩性测试脚
| 失败点 | 降级 |
|---|---|
| prompt 加载失败(缺 id/version | 回退**内置默认 prompt** + 告警,不中断生成 |
| LLM 不可用 | 退化为确定性 Fallback 生成器(确定性兜底产出,~~模板填充~~措辞已更新,见 [`../rules/security-and-reliability.md`](../rules/security-and-reliability.md) §5.2 |
| tier2 / 便宜档 prompt 正文热取读到脏或缺失 | 回落进程内内置原文roles.py 原文 / `cheap_roles._SYSTEM_PROMPT` / classpath 快照best-effort 绝不中断生成 |
| LLM 不可用 | 退化为确定性 Fallback 生成器(确定性兜底产出,~~模板填充~~措辞已更新,见 ai-generation-pipeline §⓪/§7 |
| Cocos-MCP 工具调用失败 | 降级 Tier1 路径(~~OpenGame~~ 已退役MVP Tier2/3 仅探针 |
| eval CI 跑不通LLM 限流) | 标记 skip + 人工兜底审,不阻塞紧急修复 |
---
@ -160,23 +144,7 @@ aigc 新增**无状态原子**:输入 GameConfig → 输出可玩性测试脚
|---|---|
| 改了 prompt 没 bump version | CI 卡version 未变拒绝合入(防静默覆盖) |
| Golden 集过拟合 | 样本要覆盖典型+边界bad case 增量补;勿只放"好跑"的样本 |
| registry 与运行时不同步 | 部署强制版本校验DB 镜像只读;改 prompt 必同步 registry.yamlcommit 在途 M 触发 pre-commit 版本漂移闸的处置见 [`staging-ops.md`](./staging-ops.md) §2 |
| tier2 多 agent prompt 当单次文生代码写 | 它是 AgentScope ReAct 多步有状态编排(工作室设计→单写→软检);改一条只改对应 `09-tier2-richgame/*.md` 正文加升 version不改 Python |
| 把 CI 门焊在化石 prompt 上白花钱 | 段 B 真模型闸只焊 live 面;接门前先查 registry 头部消费面三态对账,非 livefossil/batch-relicSKIP 豁免、live 无金标 fail-closed先用代码坐实「谁真被 live 路径喂 LLM」再决定跑不跑 |
| 单条模型调用失败当成 prompt 退化拦 | 网关 500/限流是基础设施问题,从判定分母排除 + 建议复跑(多数失败才判人工兜底),别让偶发抖动误判 prompt 质量 |
| registry 与运行时不同步 | 部署强制版本校验DB 镜像只读;改 prompt 必同步 registry.yaml |
| Cocos-MCP prompt 当文生代码写 | 它是 agentic 工具编排(多步),不是单次文本;归 studio 编排 |
| 运营绕过 eval 直接改 DB | DB 是 git 只读镜像,无写入路径;改 prompt 唯一入口=PR |
| prompt 注入攻击 | guardrails 内置 injection-detect + 输出 Schema 校验,与内容安全双层链路同治理 |
---
## 11. 稳定门在 MiniMax-M3 下的 flaky 与治法2026-07 W-AXIS R1 实证)
段 B 真模型闸之上actor/judge 类 prompt 还叠了一道**稳定门**防单轮侥幸过。judge 要同一请求连续三轮金标全净(`MIN_STABLE_RUNS=3``eval_gate.py:114`;双 judge 各自 6/6、actor 至少 4/5`:12`再加第四轮复跑确认actor 走 cohort 聚合(`evaluate_repeat_stability``:2827`),三轮里 `correct_total≥12` 且每键 `≥2/3``:2884-2885`)。这套门在 MiniMax-M3 下很 flaky——judge-b 跑到第 33 轮才出一个三连净,单轮全净率只有 3545%。
flaky 根因分三层,治法各不同,别混着调:
- **① 金标白名单同义词覆盖不全(主因,治本=补同义词)**。labels 的 `anyTerms`/`requiredConcepts` 是自由文本白名单(`eval_gate.py:1873-1886`M3 常用的近义表达落在白名单外就被判错:净利↔利润/净收益、零单↔0单汉字「零」≠数字「0」、通关↔胜利、time-over/end-state↔game over、缺少证据↔缺证、cannot↔不能、`open shop``open-shop`(连字符差)、`no proving evidence``no evidence`(非连续子串)。补这些进 `requiredConcepts` 同义组**不是放松标准**是让金标覆盖合法的同义表达flaky 的主要来源由此消掉。
- **② 格式类失败(正文加硬约束可消除)**。JSON 尾部多游离 `]`、problems 缺硬证引用、obligation id 笔误(如 sim-business 误写成 sem-business。在 prompt 正文加硬约束即掉:输出 JSON 配平自检、obligation id 逐字复制不得改写、problems 逐条带硬证引用、summary 全引用、反事实视觉判定(文本说营收为正但画面还在开店前 → 判 contradicted不得 accept
- **③ 视觉误判(正文约束仅边际改善)**。把 GAME OVER 帧读成通关、漏 event 引用,属模型读图能力,正文加约束只能边际改善,治不了本。
做法:正文强化格式约束 + 补 `anyTerms` 同义词 + 几何退避等其收敛闭合,**不降阈值、不 cherry-pick 净轮**。actor 判据本就比 judge 宽cohort 聚合而非单轮全净闭合相对容易judge 单轮全净门最硬,补同义词后才收敛。

View File

@ -1,213 +0,0 @@
---
name: puzzle-game-design
description: "当为解谜/找茬/消除/记忆类 brief 产设计阶段玩法方案或评审时使用:顿悟引擎、可达谜题范式、关卡阶梯与计分锚、「解须在取证面上」「盘面必可解」等品类特有红线、通用+品类自检、金标锚点。"
---
# skill: puzzle-game-design —— 解谜小游戏「玩法设计」作业手册(给生成 agent 的设计阶段)
> 定位:这是 **生成 agent 设计阶段造"好玩的解谜玩法设计"的唯一作业手册**。它回答 **"设计什么才好玩"**(谜题机制/关卡进度/数值/美术/音/UI 范式),与 [`littlejs-game-dev.md`](littlejs-game-dev.md)(回答"代码怎么写")配对:**设计阶段产玩法概念 → 代码生成阶段照 littlejs-game-dev 产代码**。经营/养成类的对应手册是 [`sim-business-game-design.md`](sim-business-game-design.md);两份体例相同、品类不同。
>
> **铁律(创始人 2026-06-21)**:"好玩 = 上游设计层职责",不在 runtime/插件。插件只让设计**能被 juice 出手感**;**这份 skill 负责让设计本身好玩**。
>
> **可达性边界**:产出必须落得到 A-model 轻量运行时——**LittleJS 2D 手机竖屏(390×844)、单/少场景、便宜模型可生成的多文件代码、11 注入插件能力域**。解谜品类还有一条**品类特有的可达红线:谜题的"解"必须在屏上肉眼可辨**(§9,设计再妙、玩家看不出哪里可点就是卡死)。
>
> **上位标准**:分层质量与验收口径 = [`docs/architecture/架构/生成引擎/游戏质量与爆火能力.md`](../../docs/architecture/架构/生成引擎/游戏质量与爆火能力.md)(L1 机制可玩/L2 内容丰富/L3 留存结构/L4 传播钩子;品类 rubric 计分制见其 §4)。本品类 rubric 机器版(喂 LLM 验证 agent)= `cheap-worker/fixtures/genre-rubrics/puzzle.json`,与本文 §10 品类条目**同源**——改任一处必须同改另一处并做金标复验。
>
> **证据基**:范式蒸馏自近年微信/抖音「找茬/脑洞益智/消除/记忆翻牌」品类的成熟模式(训练知识,非实时榜单);范式稳定可用,具体举例供参照。
---
## 0. 为什么这一品类爆(底层动机 —— 设计要击中这些)
解谜小游戏的"爽"来自 6 个心理引擎,**好玩 = 同时点燃尽量多个**;无趣 = 一个都没点燃:
1. **顿悟时刻(啊哈!)**:看一眼没头绪 → 观察两秒 → "我懂了!"——线索与解之间必须有一段**想一下的距离**,这是解谜区别于反应点击的命脉。
2. **胜任感(我变聪明了)**:解开=智力被确认;连续解开=滚雪球的自信。反面:靠乱点也能过 = 顿悟感全无。
3. **清屏快感**:把盘面上的目标一个个点掉、清空、整理归位的强迫症满足(消除/配对/归类共用这条)。
4. **递进挑战与"差一点"**:关卡一关比一关难一点点;卡住时"就差一个没找到"的张力,引向提示/重试,**别卡死**。
5. **炫耀智商**:成绩(用时/步数/星级/关数)天然可比较、可截图——解谜是传播钩子最顺手的品类。
6. **低门槛零阅读**:规则靠第一关"点一下就懂"示范,不靠文字说明书;3 秒上手,谜题深度靠后续关卡展开。
> **反"无趣"总纲**:解谜无趣几乎总是因为——谜面一眼看穿(无顿悟)/ 关卡重复同一规则(无递进)/ 乱点也能过(无胜任感)/ 卡死无提示(挫败流失)/ 无音效无庆祝(解开了没反馈)。§10 是落地清单。
---
## 1. 核心玩法范式(可达子集 —— design 优先从这里选)
| 范式 | 一句话 | 我们运行时怎么落(插件) |
|---|---|---|
| **规则匹配谜盘**(主流首选) | 每关给一条线索(如"找出所有【红宝石】"),盘面若干格,点中所有符合的即过关;规则随关卡换维度(颜色→形状→数量→组合) | core 纯逻辑产盘面+判定 + hud-ui 线索横幅/格子 + session-score 计分 + particles-juice 点中爆点 |
| **找不同 / 找目标** | 一堆干扰项里藏 N 个目标(不同色/缺一角/多一笔),全找到过关 | core 产干扰+目标 + render 细微差异绘制 + juice 找到闪光 |
| **顺序推理** | 按推理出的次序点(从小到大/按亮灯顺序/按字序),点错断连击 | core 产序列+当前该点项 + hud-ui 高亮 + timer-scheduler 节奏 |
| **记忆翻牌配对** | 短暂展示后盖住,凭记忆点出成对目标 | core 记牌面 + timer-scheduler 展示倒计时;**两步操作须守 §9 的下一步指引纪律** |
| **归类整理** | 把混在一起的东西按规则归拢(点出所有"属于厨房的") | 同规则匹配谜盘,规则换成类别归属 |
| **消除变体(点选式)** | 点走相邻同色/凑三消除,清空过关 | core 盘面+消除判定;**只用点选、不做拖拽滑动**(§9) |
| **星级 / 收集图鉴** | 每关按用时/步数评 13 星,攒星解锁新章节/新主题 | session-score(best 接 save)+ save-progress 存星 + hud-ui 图鉴格 |
| **数值成长曲线** | 每关基分 + 连击倍率 + 剩时奖励,制造"这把更高分"的重开欲 | balance 数值表 + session-score |
**组合配方(经验)**:一款轻量解谜 = **1 个谜题主范式 + 关卡阶梯(规则随关递进)+ 连击/星级一个成长点缀 + 收集/称号一个第二动机**。别堆超过 2 个主机制(便宜模型 + 单屏扛不住,也稀释顿悟感)。
---
## 2. 关卡 / 进度 / 留存设计(解谜的进度结构 = 关卡阶梯本身)
解谜品类里,「解锁阶梯」的品类形态 = **关卡链:每过一关解锁下一关,且新关引入新规则/新维度/更大盘面**——这就是质量模型底线四件里"解锁阶梯 ≥3 级"的解谜映射(纯换汤不换药的重复关不算阶梯)。
- **首 3 分钟(决定生死)**:① 010 秒:第一关零阅读、一眼能解(示范规则,点一下就有反馈);② 1060 秒:连过 23 关,第一次出现"要想一下"的关 + 第一次连击/星级奖励(给到"我变聪明了"的爽);③ 13 分钟:露出后续内容预告(下一章图鉴 / "第 8 关解锁新玩法"的锁),建立"还有很多"预期。
- **难度曲线**:难度 = 盘面大小 × 规则维度 × 干扰强度,三个旋钮**一次只拧一个**。节奏 = 23 关舒适 → 1 关"要想一下" → 过关大庆祝 → 回舒适;顿悟关是爽点、不是墙。
- **卡点设计**:玩家 ≥812 秒无正确操作 = 卡壳信号 → 给**软提示**(候选高亮/线索重播/排除一个干扰项),**卡壳不出局**;提示是"诱惑"(将来可接看广告解锁),不是"惩罚"。
- **留存钩子**:每日谜题(当日专属一关)· 章节图鉴(攒星点亮)· 连胜纪录 · 称号阶梯("见习侦探→推理大师")。便宜档至少给一种。
- **长线**:章节主题轮换(水果→宝石→古文字)· 图鉴凑齐 · 无尽模式冲纪录。
---
## 3. 数值与节奏(给具体区间锚 —— 别让 code agent 拍脑袋)
- **盘面与解数**:起步 **3×3 格、每关 35 个正确目标**;上限 4×4(竖屏手指友好,格子 ≥72px);目标太少无清屏感、太多变体力活。
- **观察窗口**:出题后留 **0.81.5s** 让玩家扫一眼盘面再判定(别一出题就算超时);记忆类展示 **1.52.5s** 再盖住。
- **一关时长**:**820s/关**为舒适区;整局限时 6090s 或 812 关到终局,两种收口都可。
- **计分骨架**:每个正确目标基分(如 +10)+ **连击倍率**(2s 内连续正确 ×1.5/×2,断连归 1)+ 过关奖励(+30 起、随关数上浮 ×1.15/关)+ 剩时奖励。让"聪明又快"肉眼变成大分差。
- **错点惩罚(轻)**:点错 = 抖屏 + 断连击 + 短锁定 **0.30.5s**;**绝不倒扣到看不见涨分**(分数总体must上升,挫败=流失)。
- **双货币(变现预留)**:星星(过关挣,解锁章节)+ 提示券(稀缺,卡壳消耗,**经看广告或攒**)。看广告点位:卡壳要提示 · 失败续 10 秒 · 结算翻倍。包装成"福利",不打断顿悟爽点。
### 标准 Match-3 的动作可读性
相邻交换三消的核心反馈不是“分数涨了、盘面变了”,而是玩家能连续看懂本手因果:`交换 → 匹配停留 → 消失 → 下落/补位 → 连锁间隔 → 稳定`。无效交换必须前移后回退。缺任一主体阶段、直接跳终盘、只用粒子或闪屏代替棋子运动,都属于玩法体验不完整,不得在设计稿中写成“已有消除反馈”。
- 交换约 180240ms匹配停留约 80140ms消失约 160220ms下落/补位约 180360ms连锁间隔约 80120ms。
- 第二击接受后锁棋盘输入,稳定后恢复;锁定期额外点击不能改盘面、分数、步数或再次启动结算。
- 每一轮连锁独立播放消失与下落,不能只画第一轮匹配或最后一轮终盘。
- 终局结果可以同步确定,但结算页必须等最后一轮稳定后出现,不能抢走最后一次消除。
- 标准局部消除效果由固定 host 自动合成:亮色核心、扩散环与八向碎片使用 L1 注入的主题 paletteWriter 不自制匹配消失粒子,也不调用 `match3VisualTimeline.renderEffects()`
- 设计输出必须明确主题棋子如何做位移、缩放/透明消失、顶部补位入场、无效回滚、连锁强化和终局转场;音效和震动只作辅助,不能重复覆盖 host 已提供的标准消除效果。
---
## 4. 美术风格范式(可达 + mmx 可产 + 程序化兜底)
| 风格 | 调性 | 配色 | 适合 |
|---|---|---|---|
| **明快益智扁平**(主流首选) | 干净聪明 | 高对比糖果色 + 深底 | 绝大多数谜盘/找茬 |
| **夜空霓虹** | 专注冷静 | 深蓝底 + 霓虹高亮 | 逻辑/序列/记忆 |
| **纸面手绘** | 轻松幽默 | 米纸底 + 铅笔线 | 脑洞/找不同 |
| 国风雅致 | 沉静 | 宣纸 + 黛青/朱砂 | 古文字/七巧板主题 |
- **谜面可读性 > 一切装饰**:格子间距 ≥12px、目标属性(颜色/形状/数量)一眼可辨——看不清的谜面 = 假难度,是解谜品类头号美术错误。
- **高亮语言要统一**:可点/选中/正确/错误四态各一种视觉语言(描边/发光/抖动/变灰),全程一致。
- **美术一致性之根**:全程共用一组"风格词",mmx 每次生成都带它;**程序化兜底**——纯几何图形(圆/方/星/菱)+ 统一色板就能做出体面的谜盘,**别因美术阻塞可玩性**。
---
## 5. UI 范式(hud-ui 全可达)
- **线索横幅**:盘面上方一条醒目横幅写本关线索("找出所有【红色宝石】");换关时横幅刷新 + 轻动画;
- **顶部 HUD**:关卡数(第 3/8 关)/ 分数 / 剩余时间条(drawBar);
- **连击提示**:连续正确时"连击 ×2!"飘字放大;
- **过关庆祝**:整盘清空瞬间全屏小庆祝(闪白 + 粒子 + 音效 + "第 N 关完成!");
- **卡壳软提示**:候选格轻微脉动/发光(hud-ui 变色重绘即可);
- **结算面板**:大分数 + 通过关数 + 最佳纪录 + 星级/称号 + 再来一局按钮——**结算就是炫耀时刻,别打完黑屏**。
---
## 6. 音乐 / 音效范式
- **普通 puzzle**BGM 轻快专注循环;正确、错误、过关等即时反馈可经 `audioMusic` 提供,无声卡时静默降级不崩。
- **标准 Match-3**:主题 BGM、交换、无效回滚、每轮 clear 复合音、fall 落定音和 terminal 音全部由 host-protected `match3AudioDirector` 自动跟随视觉时间线播放。设计只需给出主题听感,不向 Writer 下发音频实现清单Writer 不得使用 `audioMusic/playSfx`、自建 Web Audio 或另造连锁 cue。
- 红线:**宁可程序化音效,也别没有反馈音**(解开无声 = 顿悟感直接减半)。
---
## 7. 首屏 / 留存视觉钩子(通用,MVP 也用)
- 首屏 3 秒抓人:**直接给第一题**(菜单一点就进第一关,别先放教程墙);
- "关卡地图/章节图鉴"的进度预告(点亮了 3 格、后面还有一长串);
- 成绩对比预期(最佳纪录摆在结算正中,"下把能更高");
- 称号/星级当门面(情感锚 + 炫耀点)。
---
## 8. 设计阶段输出配方(生成 agent 据 brief 产出这些 → 喂代码生成)
给定一句话 brief,设计阶段应产出一份**结构化玩法设计**,**逐项落到运行时**:
1. **谜题主范式**(从 §1 选一个;一句话:玩家看什么线索 → 怎么想 → 点什么);
2. **关卡阶梯**(36 关起步:每关的盘面大小 / 规则维度 / 新引入什么——逐关写清,这是解谜的解锁阶梯);
3. **数值骨架**(基分 / 连击倍率 / 过关奖励 / 错点惩罚 / 观察窗口与一关时长 —— 按 §3 区间给具体值,交 core/balance);
4. **卡壳兜底**(几秒算卡壳 / 提示形式);
5. **美术风格词**(一组,全资产共用)+ 谜面元素清单(格子/目标/干扰项,标 mmx 或程序化);
6. **音效清单**(正确/错误/连击/过关/星级)+ BGM 情绪;标准 Match-3 只写主题听感与动作反馈目标,标明由 host director 自动提供,不给 Writer 音频实现任务;
7. **UI 布局**(线索横幅 / HUD / 连击飘字 / 结算面板,§5);
8. **插件选择清单**(普通 puzzle 常用:sceneFsm + sessionScore + hudUi + timerScheduler + audioMusic + juice,记忆类加 save 存最佳;标准 Match-3 不选 audioMusic);
9. **首 3 分钟脚本**(逐关:示范关→舒适关→首个顿悟关→内容预告);
10. **可点性可见声明**(§9:当前该点的目标靠什么视觉特征与干扰项区分、多步范式下一步怎么指引——设计阶段就写清,别等实现时才想)。
11. **动作转场声明**(标准 Match-3 必填:交换/消失/下落补位/连锁/稳定/无效回滚各自的可见反馈、时长与输入锁;其它多步谜题说明状态切换如何让玩家看懂因果)。
> **MVP-first 铁律(关乎 code agent 能否收敛)**:首版**只做可玩核心**——1 个谜题主范式 + 35 关阶梯 + 基分/连击 + 基础音效,谜面元素 ≤3 种属性维度。**每日谜题 / 图鉴 / 称号系统 / 无尽模式 / 提示券商店 一律标「后续·MVP 不做」**,别一稿堆满 —— 设计太满 → code agent 实现负担过重 → read-thrash 不收敛。**先出能玩的核心,再谈丰富。**
> 这份设计 = 代码生成阶段的"图纸";生成 agent 照 littlejs-game-dev 把它实现为多文件代码。**设计阶段把"好玩"想清楚,代码阶段只管正确实现。**
---
## 9. 可达性红线(落不到运行时的设计 = 废稿,必剔除/简化)
- ❌ 拖拽 / 滑动 / 连线 / 画线为核心操作(华容道滑块、连连看划线、一笔画)→ 竖屏小格上拖拽误操作率高、单击是手机 casual 最稳的输入形态 → 改成**点选等价形态**(点两格交换、点格子步进、点序列下一项)。
- ❌ 大段文字谜面 / 灯谜 / 阅读理解 → 竖屏小字读不动、机器也验不了;线索用**视觉属性**(颜色/形状/数量/位置)表达,横幅一行字封顶。
- ❌ 多屏关卡地图 / 大世界章节 → 单/少场景(menu→play→over),章节感用主题轮换与图鉴表达。
- ❌ 复杂谜题编辑器 / 用户出题 → 超出便宜档;盘面一律 core 纯逻辑程序化生成(受控随机,可复现)。
- ❌ 超过 2 个主机制(谜盘+消除+记忆混一款)→ 聚焦 1 主范式 + 1 成长点缀。
- ❌ **「解」肉眼不可辨**——这是解谜品类的头号翻车点:**当前该点的正确目标必须有可见的视觉区分**(线索横幅指明规则 + 目标属性肉眼可判——颜色/形状/数量差异要够大),解谜逻辑再对、屏上看不出哪里该点 = 玩家与测试员一样卡死。多步范式(翻牌配对:先翻 A 再翻 B)必须**随状态给"下一步该点的"视觉指引**(翻开 A 后,提示区亮出 A 的花色);做不到就改单步范式。
- ❌ **重写盘面生成器而丢「可解性质」**——解谜品类第二号翻车点(批产实测:自写符文生成器产出 0 解盘面 → 第一关就无解 → 谁玩都卡死):脚手架的 `generateBoard` 保证**每关恰好 N 个解、至少 1 个解、干扰项绝不撞规则**;换主题/换规则维度**优先只改 THEME/LEVELS 表**、别动生成器;确要自写规则系统,必须保住同一性质(生成后自检一遍「符合规则的格数 == 预期解数、≥1」,0 解=不可解关=废稿)。
- ❌ **在场景 render 回调里引用未定义变量**——render 回调抛错会被 scene-fsm **隔离成告警不崩**,画面异常但局不死(批产实测:renderMenu 里一个未定义的 `cy` 曾让整局 E/G/H 三门连锁挂——当时按钮命中区还靠 render 返回值回写 `menuStartBtn = renderMenu(...)`,render 一抛错命中区永远拿不到、卡死菜单)。2026-07-03 起模板已改约定:**命中矩形在场景 onEnter 用纯几何函数确定性建立,render 只画不赋值**(cheap-system v1.6.1 硬约束),旧连锁已断;但 render 抛错仍会黑屏丢反馈,改完 render 先顺一遍所有变量来源。
- ❌ 点错即死 / 三错清零重开 → 错点轻惩罚(§3),乱点不该赢、但点错不该死。
- ✅ 留下的:规则匹配谜盘、找目标、顺序推理、点选消除、记忆配对(守下一步指引纪律)、星级收集、关卡阶梯、连击计分 —— 全在 11 注入插件 + 单屏 + 便宜模型可生成范围内。
---
## 10. 反"无趣"落地清单(design 自检,逐条必过)
**通用 8 条**(全品类同一张表,与 sim-business §10 同源;解谜视角的落点写在括号里):
1. **即时反馈**:每次正确点击有没有"飘字 + 音效 + 粒子"三件套?(无 → 必加;标准 Match-3 的消除粒子由 host 自动合成Writer 不重复制作)
2. **可见成长**:分数/关数有没有肉眼可见地涨?(连击倍率让它涨得有戏剧性)
3. **下一个解锁**:任意时刻有没有"下一关引入什么/差几颗星解锁下一章"的钩子?
4. **30 秒爽点**:开局 30 秒内有没有连过两关 + 第一次连击奖励?
5. **数值滚雪球**:连击×剩时奖励有没有让"聪明又快"变成大分差(而非匀速加分)?
6. **情感锚**:有没有一个可积攒的拥有物(星星/图鉴/称号)让玩家"想凑齐"?
7. **放置回归**:回来有没有惊喜(每日谜题/连胜保留)?(便宜档至少一种,标「后续」也要在设计里留位)
8. **音反馈**:正确/错误/过关有没有声音?(哪怕程序化)
**解谜品类扩展 5 条**(机器版同源 = `cheap-worker/fixtures/genre-rubrics/puzzle.json`,LLM 验证 agent 按它评分;改条目两处必须同改 + 金标复验):
| # | 层 | 条目 | 一句判据 | 正例 | 反例 |
|---|---|---|---|---|---|
| P1 | L2 | 谜题规则递进 | 关卡引入 ≥2 种谜题规则/维度,后关比前关多一层复杂度,不是同一规则换数字重复 | 12 关按颜色找、34 关按形状找、5 关颜色+形状组合 | 8 关全是"找红色",只是格子变多 |
| P2 | L2 | 顿悟距离 | 谜面需观察/比对/推理才能定位解,线索与答案之间有"想一下"的距离,不是看见就点的纯反应 | 线索"数量为 3 的宝石",盘面要数一数才敢点 | 目标自己发光闪烁,无脑点亮的 |
| P3 | L3 | 关卡阶梯可见 | 玩家任意时刻能看到关卡进度(第 N/共 M 关或下一关预告),过关有明确庆祝节点 | HUD 常驻"第 3/8 关"+ 过关全屏庆祝 | 只有分数在涨,玩家不知道自己走到哪 |
| P4 | L2 | 卡壳兜底 | 卡住 ≥812s 有软提示(高亮/排除/线索重播),卡壳不出局不清零 | 10s 无正确操作,候选格开始脉动提示 | 卡住只能干等到超时判负 |
| P5 | L4 | 成绩可炫耀 | 结算展示可比较成绩(用时/连击/星级/称号,配最佳纪录),不是打完就黑屏 | 结算面板:分数+最高连击+3 星+历史最佳+称号 | 结算只有一行"游戏结束" |
> 通用 8 条命中越多越好玩;品类 5 条对齐质量模型的档位观测线(便宜档批次:L2 组均分 ≥60%、L3 ≥2/3、L4 ≥1/2,消费于品类小批验收、不拦单款)。**设计阶段就过这两张表,别等做出来才发现无趣。**
>
> **底线四件的解谜映射**(质量模型 §4 结构底线,设计必须给全):成长轴 = 分数+关卡数;解锁阶梯 = 关卡链逐关引入新规则(≥3 关,任意时刻可见下一关);第二动机 = 星级/图鉴/称号可积攒;音反馈 = 正确/过关必有声。四件全可映射,无需替代轴申报。
---
## 金标锚点(12 条口径基线 · 质量模型 SoT §4 规范三)
每品类需 ≥1 金标正例 + ≥1 薄反例锚住评分尺(rubric 文本或评分 prompt 变更须金标复验,单款分组小计漂移 >±1 即回退)。解谜的锚 2026-07-03 以 12 条口径实测(生产口径 genre=puzzle,同输入复采样 n=3 取中位,入口 `cheap_verify.verify_richness`):
- **金标正例 = `game-runtime/games/_fewshot-puzzle`**(「深海寻宝谜航」,骨架上做出规则递进 + 连击 + 星级 + 结算炫耀的完整款)。**通用 12 条 = 11/12**:L2 7/7 · L3 2/3 · L4 2/2;第 12 条「核心操作非无脑」3/3 命中(读线索按颜色/形状/数量/组合四维筛对,点错扣星 + 抖屏 + 锁定 400ms,含真实判错代价);品类扩展 5/5;三样 11/11/11、最稳。
- **脚手架参照 = `game-runtime/games/_template-puzzle`**(规则谜盘轻脚手架,克隆起点)。**通用 12 条 = 10/12**:L2 6/7 · L3 2/3 · L4 2/2;第 12 条 3/3 命中;品类扩展 5/5。它品类面已满 5/5、仅通用维度比 few-shot 正例薄一档——**是能过门的黄金脚手架、不构成品类薄反**。
- **薄反例 = 合成薄壳**(单规则纯反应 / 目标自发光 / 无进度 / 无提示 / 结算一行字,照各条 negative 面同构):材料尚未入仓,**是本品类金标欠账**(与 `cheap-worker/fixtures/genre-rubrics/puzzle.json` 的 _note 记的同一笔;红线是不臆造合成材料充数)。其历史锚(未真跑)= 通用 0/11 · 品类 0/5。
- **方差纪律**:M3 单发方差 ±2~4,取中位;品类段(两款均 5/5)与 L4(均 2/2)跨样最稳,是回退判定最可靠的观测维。
---
## 相关
- 代码层实现 → [`littlejs-game-dev.md`](littlejs-game-dev.md)(11 注入插件 API / 结构 / 资产-mmx / 工厂契约)
- 品类轻脚手架(克隆起点)→ `game-runtime/games/_template-puzzle/`(规则匹配谜盘骨架:盘面生成/线索/判定/关卡流转已接好)
- 过门正例(few-shot,读它看"丰富成什么样")→ `game-runtime/games/_fewshot-puzzle/src/`(在骨架上做出规则递进+连击+星级+结算炫耀的完整款)
- 品类 rubric 机器版(喂 LLM 验证 agent)→ `cheap-worker/fixtures/genre-rubrics/puzzle.json`
- 便宜模型生成 worker / 九门 → [`cheap-model-game-generation.md`](cheap-model-game-generation.md)
- 质量分层与档位观测线(上位标准)→ [`docs/architecture/架构/生成引擎/游戏质量与爆火能力.md`](../../docs/architecture/架构/生成引擎/游戏质量与爆火能力.md)

View File

@ -1,14 +0,0 @@
# recipes 索引 —— 要做 X → 读 Y
> 品类 skill 教「什么好玩」littlejs-game-dev 教「代码怎么分层」;这里补最后一块:**高频场景的落地形态**。
> 每篇给你「抄这段形态 + 头号病根」,不讲原理。卡在哪就读哪一篇,抄它的结构改,别从零想。
| 你要做的事 | 读这篇 | 一句话病根(九成栽在这) |
|---|---|---|
| 起局/交互按钮,点了要响应 | [hit-rect.md](hit-rect.md) | 命中矩形只在 render 里生成 → 帧饥饿时 render 没跑过 → 矩形 null → 点不动卡菜单 |
| 倒计时 / 每 N 秒 spawn / 顾客耐心递减 | [timer.md](timer.md) | dt 是秒,毫秒常量累加漏 ×1000 → 慢 1000 倍 → 永不到阈值、一局不结束 |
| 用美术图,缺图不崩 | [asset-fallback.md](asset-fallback.md) | 直接 drawImage(assets[ref].image) 不判 null → 缺图当场崩 |
| 菜单 → 游玩 → 结算 三态切换 | [scene-fsm.md](scene-fsm.md) | define({menu,play,over}) 单对象批量注册 → 第一参变 [object Object] → 全没注册 → 卡菜单 |
| 成单/暴击/大额时刻的爽感 + 结算页驻留 | [settlement.md](settlement.md) | 结算态不 latch 驻留 → 瞬时终态漏读、闭环断裂juice.render(g) 忘手调 → 粒子不显示 |
方法名一律以 `game-runtime/src/plugins/<name>/api.d.ts` 为准,别按常见库套路臆测(幻觉清单见 littlejs-game-dev.md §4

View File

@ -1,32 +0,0 @@
# recipe: 资产回退(缺图/加载失败不崩)
**要做**:用 `assets/` 里的美术图渲染角色/物品,缺图或加载失败时程序化画个占位、绝不崩。
**病根九成在这**`g.drawImage(assets[ref].image, ...)` 直接取用不判 null。缺图时 `assets[ref].image``null``drawImage(null)` 当场抛 → 整局崩。headless 有图测不到,真人换环境缺图才爆。
**根治****每次取图都 `?.image` + `if (img) 画 else 程序化回退`**。ref = manifest 条目 `file` 去扩展名(`shopkeeper-idle.jpg``'shopkeeper-idle'`)。
**抄这段形态**
```js
async init(boot) {
this.ctx = boot.ctx;
this.assets = boot.assets || {}; // host 已预载;无美术游戏时是 {}
}
render(g) {
const img = this.assets['shopkeeper-idle']?.image; // ★?. 兜 null
if (img) {
g.drawImage(img, x, y, w, h);
} else {
// 程序化回退:画个色块占位,绝不因缺图崩
g.fillStyle = '#c8a06a';
g.fillRect(x, y, w, h);
}
}
```
**别踩**
- 先 `read_file('assets/manifest.json')` 看有哪些资产(非空才有图);**无 manifest / 无美术 → 全程序化绘制即可**,别硬编码文件名。
- BGM/音效**不在** `boot.assets`——走 `plugins.audioMusic``playSfx('coin')` / `play(intensity)`)。
- 改一张图:`mmx` 重生成覆盖同名文件 + 更新 manifest 的 sha256/bytes/prompt**文件名没变 → 代码一行不用动**。

View File

@ -1,36 +0,0 @@
# recipe: 命中矩形(按钮点不动的头号根治)
**要做**:菜单的「开始」、结算的「重来」、玩中的可点区——点了要真响应。
**病根九成在这**:命中矩形只在 `render` 的副作用里生成(`menuStartBtn = renderMenu(...)` 那种回写)。并发渲染帧饥饿时 `render` 可能一次都没跑过 → 命中矩形恒 `null` → 点了没反应 → 卡在菜单一局起不来。这是「卡 menu」的头号成因。
**根治口诀**:按钮几何抽成纯函数,**场景 `onEnter`(状态进入点)确定性建立命中区,`render` 只画不赋值**——同一来源,不漂移。
**抄这段形态**
```js
// 纯函数按钮几何只算一次的来源onEnter 与 render 都调它,值一致)
function menuStartRect(viewport) {
return { x: viewport.w / 2 - 90, y: 520, w: 180, h: 64 };
}
sceneFsm.define('menu', {
onEnter() { this.startRect = menuStartRect(viewport); }, // ★命中区在这里建,不在 render
render(g) {
const btn = hudUi.drawButton(g, this.startRect, { label: '开始' }); // drawButton 回吐命中矩形
// 也可直接 hudUi.drawButton(g, menuStartRect(viewport), {label:'开始'})——同源即可
},
});
// 输入:只写实例方法 handleTapL1 在 pointerdown 时直达调用它(别自己订阅输入)
handleTap(x, y) {
if (sceneFsm.current() === 'menu' && hudUi.pointInRect(x, y, this.startRect)) {
sceneFsm.transition('play'); // 命中 → 起局
}
}
```
**别踩**
- `drawButton(g, rect, opts)` 第二参是 **rect 对象 `{x,y,w,h}`**,不是 label/坐标位置参——别写 `drawButton(g, '开始', x, y, w, h)`
- 命中判定只有 `hudUi.pointInRect(px, py, rect)`collision 插件**没有** pointInRect
- 输入不写 `ctx.getInput`、不自建 pendingClicks 队列——只暴露 `handleTap(x,y)`/`handleKey(key)`

View File

@ -1,36 +0,0 @@
# recipe: 场景机menu → play → over 三态)
**要做**:菜单页 → 游玩 → 结算页,能来回切。
**病根九成在这**`sceneFsm.define({ menu:{...}, play:{...}, over:{...} })` 一个对象批量注册。`define` 第一参**恒为场景名字符串**——传对象 → 第一参被当成场景名 `"[object Object]"`menu/play/over 全没真注册 → 后续 `transition('play')` 找不到目标态被静默忽略 → 卡菜单进不去 play合成/2048 类翻车点)。
**根治****每个场景各调一次 `define(name, handlers)`**,第一参是字符串。
**抄这段形态**
```js
sceneFsm
.define('menu', {
onEnter() { this.startRect = menuStartRect(viewport); }, // 命中区在 onEnter 建(见 hit-rect.md
render(g) { hudUi.drawButton(g, this.startRect, { label: '开始' }); },
})
.define('play', {
onEnter() { this.score = 0; this.remainMs = ROUND_MS; },
update(dt) { /* 只收 dt推进玩法、判胜负 */ },
render(g) { /* 画游玩画面,只收 g */ },
})
.define('over', {
onEnter() { sessionScore.isOver() || sessionScore.lose(); }, // 结算态latch 驻留(见 settlement.md
render(g) { hudUi.drawButton(g, this.overRestartRect, { label: '再来' }); },
});
sceneFsm.start('menu'); // 起手停在菜单
// update/render 五法里把帧转交场景机:
update(dt) { bundle.tick(dt); sceneFsm.update(dt); }
render(g) { sceneFsm.render(g); }
```
**别踩**
- 回调表里 `update(dt)` 只收 dt、`render(g)` 只收 g——与五法同形别臆造 `update(scene, dt)`
- 若写了可选调试接口 `_forensicsView()`:`state().phase` 要**实时读** `sceneFsm.current()`(在 state() 函数体内别在外层先算好闭包返回host 只在 boot 调一次 _forensicsView外层算的值会定格成 boot 快照=调试读到的恒是 menu 旧值)。
- 状态名你自己定menu/play/over 只是惯例);`transition(to)` 的 to 必须是你 define 过的名。

View File

@ -1,47 +0,0 @@
# recipe: 结算演出(爽感反馈 + 结算页驻留)
**要做**:成单/暴击/大额那一下有「打击感」,一局结束进结算页并**停住不动**。
**两个病根**
1. **结算态不驻留(自动重开)** → 瞬时终态被漏读 → 宿主 500ms 轮询 phase 代发 `game_end` 时已经不在 over 态 → 结算闭环静默断裂。`phase``over/gameover` 后**必须恒驻留**(禁自动重开,等玩家点「再来」才回 menu
2. **`juice.render(g)` 忘了每帧手调** → 粒子/屏震/闪白全不显示juice 的 render 不是自动的,要在你的 render 里每帧调)。
**抄这段形态(结算时刻的爽感)**
```js
// 成单/暴击那一下:一次 hitStop + 轻 shake + 音效(单行调用,不是系统,好手感标配)
onServeSuccess(x, y, isCrit) {
juice.spawnEmitter('burst', x, y); // 粒子迸发
juice.hitStop(isCrit ? 90 : 40); // 顿帧(毫秒)
juice.shakeScreen(isCrit ? 6 : 3); // 轻震屏
audioMusic.playSfx(isCrit ? 'win' : 'coin'); // 语义音效名,自动映射
sessionScore.addScore(isCrit ? 8 : 5);
}
render(g) {
sceneFsm.render(g);
juice.render(g); // ★每帧手调,否则粒子/打击感看不见
}
```
**抄这段形态(结算页 latch 驻留)**
```js
.define('over', {
onEnter() {
if (!sessionScore.isOver()) sessionScore.lose(); // 终态一次性落定
this.overRestartRect = overRestartRect(viewport); // 「再来」命中区在 onEnter 建
audioMusic.playSfx('fail');
},
render(g) {
hudUi.drawText(g, `本局 ${sessionScore.getScore()} 分`, { x: viewport.w/2, y: 300 });
hudUi.drawButton(g, this.overRestartRect, { label: '再来' });
},
// ★没有 update 里的自动 transition('menu')——驻留,等 handleTap 命中「再来」才回 menu
})
```
**别踩**
- 音效名用语义名(`coin/win/hit/jump/fail/click`)或预设(`blip/thud/chime`),未知名自动兜底 blip`audioMusic.play(intensity)` 入参是**情绪强度 0..1**、不是曲目名。
- `juice.spawnEmitter('burst', x, y)` 也可写 `juice.burst(x, y)`(母语别名);`hitStop`/`shakeScreen` 收毫秒/像素。
- 结算屏必须驻留(latch):到终态后停在结算不自动重开——玩家要看清战绩,测试员与调试观测也才能确认终态可达。

View File

@ -1,36 +0,0 @@
# recipe: 计时器 / 倒计时 / spawn 间隔 / 耐心递减
**要做**:一局限时倒计时、每隔 N 秒刷一个顾客/敌人、顾客耐心随时间掉。
**病根九成在这**`update(dt)`**dt 单位是「秒」≈1/60**,但你的计时常量常写成毫秒(倒计时 60000、spawn 间隔 1300、耐心 6000。直接 `elapsedMs += dt` → 慢 1000 倍 → 永远到不了 spawn 阈值 → 零顾客、一局永不结束(计时显示还可能是 `NaNs`)。
**两条换算铁律**
- 自己累加毫秒:**`elapsedMs += dt * 1000`**(漏 ×1000 是头号翻车)。
- 用 `timerScheduler`:它**收毫秒**——3 秒写 `after(3000, cb)`、每秒写 `every(1000, cb)`**别写 `after(3)`**;与 dt=秒相反,别混)。
- 无论哪种,`update(dt)` 内**必须 `bundle.tick(dt)`**,否则插件 onFrame/timer 全不推进、timer 永不到期、一局不结束。
**抄这段形态(自累加毫秒版)**
```js
const ROUND_MS = 60000; // 一局 60 秒(毫秒常量)
const SPAWN_MS = 1300; // 每 1.3 秒来一个客
update(dt) {
bundle.tick(dt); // ★必调,否则一切计时不动
if (sceneFsm.current() !== 'play') return;
const ms = dt * 1000; // ★秒 → 毫秒
this.remainMs -= ms;
this.spawnAcc += ms;
if (this.spawnAcc >= SPAWN_MS) { this.spawnAcc -= SPAWN_MS; this.spawnCustomer(); }
if (this.remainMs <= 0) sceneFsm.transition('over'); // 时间到 → 结算
}
```
**抄这段形态timerScheduler 版,波次刷新)**
```js
timerScheduler.every(1300, () => this.spawnCustomer()); // 毫秒!每 1.3s 一个
timerScheduler.after(60000, () => sceneFsm.transition('over')); // 60s 后结算
```
**别踩**`scene``update` 回调只收 `(dt)` 一个参数——别臆造 `update(running, dt)`(写错 → dt 落到第二参=undefined → `elapsedMs += undefined = NaN` → 空转看着像没 bug 实则永不推进)。

View File

@ -1,29 +1,23 @@
---
name: runtime-and-multichannel
description: "当开发/调试 runtime 编译与包版本化交付、WanxiangGameSDK 注入与降级、iframe 沙箱三容器预加载、或微信/抖音/快手多渠道导出时使用:渲染层 LittleJS 增强发行版、SDK 两层、沙箱安全边界与微信格式导出枢纽。"
---
# 运行时、Game SDK 与多渠道导出手册runtime-and-multichannel
> 蒸馏来源:`docs/architecture/架构/README.md`§3.4 Game SDK / §4.2 运行时三容器 / §6.6 运行时与导出选型)、`docs/architecture/架构/13模块.md`runtime 模块 T-RT-* 技术功能)、`docs/architecture/架构/README.md`§2 模块地图 / §10.4 LayaAir CLI
> 蒸馏来源:`docs/architecture/系统概要设计-技术决策版.md`§3.4 Game SDK / §4.2 运行时三容器 / §6.6 运行时与导出选型)、`docs/architecture/技术架构与模块.md`runtime 模块 T-RT-* 技术功能)、`docs/architecture/系统概要设计-开发团队版.md`§2 模块地图 / §10.4 LayaAir CLI
> 适用:开发/调试 runtime 模块、WanxiangGameSDK、多渠道微信/抖音/快手)小游戏导出。
> 配套SDK 降级铁律与沙箱安全红线见 [`../rules/security-and-reliability.md`](../rules/security-and-reliability.md);工程规范见 [`../rules/engineering-conventions.md`](../rules/engineering-conventions.md);架构全景见 [`../knowledge/product-and-architecture.md`](../knowledge/product-and-architecture.md);上游生成见 [`./agentic-amodel-generation.md`](./agentic-amodel-generation.md);契约见 [`./contract-first-development.md`](./contract-first-development.md)。
> 配套SDK 降级铁律与沙箱安全红线见 [`../rules/security-and-reliability.md`](../rules/security-and-reliability.md);工程规范见 [`../rules/engineering-conventions.md`](../rules/engineering-conventions.md);架构全景见 [`../knowledge/product-and-architecture.md`](../knowledge/product-and-architecture.md);上游生成见 [`./ai-generation-pipeline.md`](./ai-generation-pipeline.md);契约见 [`./contract-first-development.md`](./contract-first-development.md)。
> **⚠️ 状态升级2026-06-12 终裁,取代 2026-06-11 待 spike 表述)**:本手册「自研 Canvas Runtime(<15KB)」相关段落=**1.x 历史口径渲染层已改判**。现行裁决
> - **Tier1 渲染层 = LittleJS 增强发行版 + Runner v2**2026-06-12 创始人终裁spike 85/82Phaser 竞标落选);「自研轻量 Canvas Runtime / <15KB 体积门废除15KB srcdoc 内联架构衍生约束前提已失效改三层约束框架SLO@千元机+4G P75 / B1 gz350KB·raw1.5MB / 工程增强层)。
> - **模板 = LittleJS 能力插件/二次开发件**(玩法模板层废除);粒子/物理/后处理三插件=引擎能力包装层裁决①2026-06-12collision/手感=引擎缺件维持自研。
> - **沙箱 / SDK Core 注入 / 三容器预加载 / 多渠道导出枢纽**章节不受影响,仍现行有效。
> 单一事实源:[`../knowledge/tech-decisions.md`](../knowledge/tech-decisions.md) §1.1 + `git show 6d2f8789^:docs/agent-specs/_archive/2026-06-11-T1引擎终裁包.md`(已随 _archive 清理删除、git 定位)。下文凡「自研 Canvas<15KB / OpenGame 生成字样均按本横幅改读
> **⚠️ 纠偏2026-07-05**:① 编译输入口径改真——runtime 编译输入 = 上游 **src/ 多文件源工程**,产出 **engineBundleiife、全局 `__GameBundle` 承重)**`gameConfig` 降为模板玩法参数占位(`contracts/game-package.schema.json` 可核)。② runtime service 真目录 = `service/build/` `service/pkg/` `service/session/`(旧稿 `service/compiler/``service/package/` 已不存在)。③ §6 多渠道代码件系(`service/conversion/``WechatAdapter`/`DouyinAdapter`/`KuaishouAdapter.java`**规划中·未实现**——全仓无此源文件,微信格式枢纽策略叙述仍有效。
> 单一事实源:[`../knowledge/tech-decisions.md`](../knowledge/tech-decisions.md) §1.1 + `docs/agent-specs/_archive/2026-06-11-T1引擎终裁包.md`。下文凡「自研 Canvas<15KB / OpenGame 生成字样均按本横幅改读
---
## 目标
上游 **src/ 多文件源工程**编译为**可运行 Web 包**engineBundle iife 承重、gameConfig 降为模板玩法参数占位)并版本化交付,注入 WanxiangGameSDK在 iframe 沙箱中安全运行(三容器预加载),并支持向微信/抖音/快手等小游戏渠道**静态导出**。**产出后游戏流可即点即玩、可分发到外部渠道。**
GameConfig 编译为**可运行 Web 包**并版本化交付,注入 WanxiangGameSDK在 iframe 沙箱中安全运行(三容器预加载),并支持向微信/抖音/快手等小游戏渠道**静态导出**。**产出后游戏流可即点即玩、可分发到外部渠道。**
## 前置
- OSS本地 MinIO+ CDN 可用。Cocos仅留 3D / 渠道导出、人在环、非分档轴官方一键导出微信包LittleJS 线(~~自研 Canvas~~)渠道 adapter 走 W-CH-α 对比竞标LittleJS+自研 adapter vs Cocos 导出HJ-CH-001见 [`../knowledge/tech-decisions.md`](../knowledge/tech-decisions.md) §1.1)。
- OSS本地 MinIO+ CDN 可用。Tier2/3 用 Cocos 官方一键导出微信包Tier1~~自研 Canvas~~ → **LittleJS**)渠道 adapter 走 W-CH-α 对比竞标LittleJS+自研 adapter vs Cocos 导出HJ-CH-001见 [`../knowledge/tech-decisions.md`](../knowledge/tech-decisions.md) §1.1)。
- SDK Core 体积达标(< 8KB 压缩上游 aigc 产出的 GamePackage 符合 `game-package.schema.json` 契约
---
@ -32,15 +26,15 @@ description: "当开发/调试 runtime 编译与包版本化交付、WanxiangGam
| 职责 | 说明 |
|---|---|
| src/ 源工程 → 可运行 Web 包编译 | `service/build/`src/ 源工程 + assets → engineBundleiife 承重)web bundle |
| GameConfig → 可运行 Web 包编译 | `service/compiler/`config + logic + assets → web bundle |
| Manifest 生成 | runtimeVersion / configUrl / assetList / hash / preloadPolicy / bundleSize |
| 包存储版本化 | `service/pkg/`checksum(sha256) + CDN路径 `/games/{gameId}/versions/{versionId}/` |
| 预览交付端点 | `controller/app/runtime/`:按版本返回 manifest + 资源 URL真路径 `GET /app-api/runtime/package/{versionId}?scene=preview`;预览为 `scene` 参数、无独立 `/preview` 路径 |
| 多渠道转换 | `service/conversion/`**规划中·未实现**导出微信小游戏格式包为枢纽 → 抖音(自有接口)/快手(微信格式兼容转换);渠道 adapter 走 W-CH-α 竞标(Cocos 仅留 3D / 渠道导出、人在环, §6 与顶部横幅) |
| 包存储版本化 | `service/package/`checksum(sha256) + CDN路径 `/games/{gameId}/versions/{versionId}/` |
| 预览交付端点 | `controller/app/`:按版本返回 manifest + 资源 URL`GET /app/runtime/preview/:versionId` |
| 多渠道转换 | `service/conversion/`:导出微信小游戏格式包为枢纽 → 抖音(自有接口)/快手(微信格式兼容转换)Tier2/3 用 Cocos 官方导出、Tier1 渠道 adapter 走 W-CH-α 竞标(见顶部横幅) |
渲染层:~~**自研轻量 Canvas Runtime< 15KB**~~ **LittleJS 增强发行版 + Runner v2**2026-06-12 终裁见顶部横幅15KB 红线废除iframe sandbox + SDK Core 注入平台完全控制沙箱沙箱/SDK 注入语义不变技术决策版 §6.6 "自研薄壳"结论已被引擎选型取代)。
> 本手册聚焦 **Tier1极轻量 2D游戏流**。分层运行时见 [`../knowledge/tech-decisions.md`](../knowledge/tech-decisions.md) §1.1**正交两轴**AI 参与深度 Tier0/1/2 与引擎按表现复杂度选 LittleJS/Phaser 是两根正交轴、引擎不是分档轴Cocos 仅留 3D / 渠道导出轴、编辑器 + 人在环,进不了自治生成轨)
> 本手册聚焦 **Tier1极轻量 2D游戏流**。分层运行时Tier1 ~~自研Canvas~~ = **LittleJS** / Tier2-3 复杂2D·3D·原生用 Cocos+MCP见 [`../knowledge/tech-decisions.md`](../knowledge/tech-decisions.md) §1.1。
---
@ -104,7 +98,7 @@ SDK ↔ 宿主 postMessage 协议(消息 `type``init` / `lifecycle` / `t
```
runtime 编译 GamePackage 时:
1. 把 SDK Core 代码内联进游戏 entry.js 头部
2. 源工程声明所需 Pluginad / pay / social / storagegameConfig 降为模板玩法参数占位
2. GameConfig 声明所需 Pluginad / pay / social / storage
3. manifest.json 记录 SDK 版本号
4. 宿主据 manifest 决定加载哪些 Plugin chunk
```
@ -127,12 +121,12 @@ runtime 编译 GamePackage 时:
> **快手导出渠道清单不含 LayaAir CLI 路线**LayaAir 官方平台清单无快手,快手统一走"微信格式包兼容转换"。"统一工具链"实质 = 统一产出微信格式包,再分发抖音/快手。详见 [`../knowledge/tech-decisions.md`](../knowledge/tech-decisions.md) §1.1。
**导出工具****Cocos仅留 3D / 渠道导出、人在环)官方一键**导出微信包LittleJS 线(~~自研 Canvas~~)渠道 adapter 走 W-CH-α 竞标见顶部横幅。Javaruntime `service/conversion/`**规划中·未实现**设计上统一以 `ProcessBuilder` 调用对应工具 → 异步等待进程 → 产物上传 OSS。
**导出工具**Tier2/3 用 **Cocos 官方一键**导出微信包Tier1~~自研 Canvas~~ → **LittleJS**)渠道 adapter 走 W-CH-α 竞标见顶部横幅。Javaruntime `service/conversion/`)统一以 `ProcessBuilder` 调用对应工具 → 异步等待进程 → 产物上传 OSS。
渠道适配器(**规划中·未实现**开发团队版 §2.1 设计,下列 Java 文件全仓尚不存在)各自处理**尺寸 / 资质 / 文案 / 违禁词**
- `WechatAdapter.java`(规划中) — 微信小游戏适配规则
- `DouyinAdapter.java`(规划中) — 抖音小游戏适配规则
- `KuaishouAdapter.java`(规划中) — 快手适配(微信格式 → 快手兼容转换)
渠道适配器(开发团队版 §2.1)各自处理**尺寸 / 资质 / 文案 / 违禁词**
- `WechatAdapter.java` — 微信小游戏适配规则
- `DouyinAdapter.java` — 抖音小游戏适配规则
- `KuaishouAdapter.java` — 快手适配(微信格式 → 快手兼容转换)
> 外部进程调用要按 [`../rules/security-and-reliability.md`](../rules/security-and-reliability.md) 处理超时/失败/重试/产物校验。

View File

@ -1,19 +1,90 @@
---
name: saa-graph-orchestration
description: "远期备查:SAA 图编排已降最低优先级、非生成主线(生成已统一 AgentScope)。仅当给 SAA 适配器布线或做远期「框架可换」可插拔验证时照本备查——依赖集/new-api baseUrl 剥 /v1/checkpoint saved_at 三承重坑;切勿据它起新生成主线。"
---
# SAA 图编排 playbook生成流水 · HJ-AGI-002
# SAA 图编排 playbook远期适配备查 · HJ-AGI-002
> **status远期备查SAA 降最低优先级)。** 生成框架已于 2026-06-25 统一收敛 **AgentScope**(三档按 AI 参与深度分),**SAASpring AI Alibaba v1.1.2.2)不再是生成主线**,只留作远期「框架可换」的可插拔适配验证目标。既有 SAA 图代码仍在 `game-cloud/game-module-aigc/game-module-aigc-server/.../saa/``SaaStudioGraph.java` 是唯一布线源;`SaaGraphDispatcher.java` 类仍在,但仅 `aigc.executor.dispatcher=saa` opt-in 时才派发、非现行主线——现行生成主派发 = `service/executor/GenerationDispatcher`〔默认 `dispatcher=http`〕经 worker 线投递到 Python worker。给 SAA 适配器布线或做远期「框架可换」验证时照本备查;**不要按它起新的生成主线**。
> 迁移设计9 步计划/拓扑/坑)见 `git show 6d2f8789^:docs/agent-specs/_archive/2026-06-15-python-to-SAA-migration-design.md`(已随 _archive 清理删除、git 定位SAA 能力/API dossier 与编排图说已收敛进 SoT 后删除,见 `git show ce7a4850^:docs/architecture/架构/生成引擎/SAA编排.md`。全量坑与决策见记忆 `saa-agentic-infra-decision`
> **定位**:用 **Spring AI AlibabaSAA v1.1.2.2)裸 `StateGraph`** 在 game-cloudJava/Boot 3.5.14)里搭"确定性多步生成编排"的标准配方。现行 agentic 基建HJ-AGI-002short-term SAA-only
> **何时用**:把"渲染→设计→生成代码→[校验/构建/真玩 九门]→修复回环→玩家顾问→出包"这类**确定性多步**流程落成图。**不用 ReactAgent/asNode/subAgents**(那是"LLM+工具自治",与"done 由九门确定性门定、不让 LLM 自评"冲突;自治节点延 Phase1.5,见 [`saa-agentic-infra-decision` 记忆] / `docs/agent-specs/agentic编排-SAA.md §10`)。
> **参考**:能力/API 速查 `docs/agent-specs/2026-06-15-SAA-能力API接入-dossier.md`迁移设计9 步计划/拓扑/坑)`docs/agent-specs/_archive/2026-06-15-python-to-SAA-migration-design.md`;落地实现见 `game-module-aigc/.../saa/SaaStudioGraph.java`(唯一布线源)/`SaaGraphDispatcher.java`(生产派发)。
---
## 保留的承重坑与要点(远期布线仍会撞
## 1. 依赖集(最小证成集,别贪"完整推荐集"
- **依赖集(最小证成集,别贪"完整推荐集"**`game-module-aigc/game-module-aigc-server/pom.xml`**3 个 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-boot-dependencies+ **3 个直接依赖**`spring-ai-alibaba-graph-core`〔图引擎 + `MysqlSaver` + observation 机芯全在此〕/ `spring-ai-alibaba-starter-graph-observation`〔仅 Boot 自动配置层〕/ `spring-ai-starter-model-openai`〔接 new-api。**别引** `agent-framework`ReactAgent/asNode与「done 由九门确定性门定、不让 LLM 自评」冲突)、`builtin-nodes`(脱 BOM 钉死 1.1.2.2、拖 tika 全家桶、SAA 的 Boot BOM让项目 3.5.14 胜,同 minor 补丁前向兼容)、`RedisSaver`(避 Redisson 3.22↔4.4 冲突)。**铁律**:动 pom 删依赖前先 `dependency:tree` / 读 jar 核类的模块归属(`MysqlSaver``GraphObservationLifecycleListener` 都在 `graph-core`)。
`game-module-aigc-server/pom.xml`**3 个 BOM仅管版本不引 spring-boot-dependencies** + **3 个直接依赖**
- **接 new-apibaseUrl 剥 `/v1` 坑**Spring AI 自拼 `completionsPath=/v1/chat/completions`,故 `OpenAiApi.baseUrl` 必须是 **host 根、不带 `/v1`**`http://100.64.0.8:3000``.env``NEWAPI_BASE_URL` 常带 `/v1`,两侧通用须 Java 侧 `stripV1Suffix` 剥之,否则 404 `/v1/v1/...`。五角色design/code/fix/player_text/player_vision共用一个 `OpenAiApi`、仅 `defaultOptions.model/temperature` 异;全走 new-api 单一 key自动入 `newapi_cost`logs.quota计费平面。
```xml
<!-- BOMspring-ai-bom:1.1.2 / spring-ai-alibaba-bom:1.1.2.2 / spring-ai-alibaba-extensions-bom:1.1.2.2 -->
<dependency><groupId>com.alibaba.cloud.ai</groupId><artifactId>spring-ai-alibaba-graph-core</artifactId></dependency> <!-- 图引擎+MysqlSaver+observation 机芯 全在此 -->
<dependency><groupId>com.alibaba.cloud.ai</groupId><artifactId>spring-ai-alibaba-starter-graph-observation</artifactId></dependency> <!-- 仅 Boot 自动配置层 -->
<dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-starter-model-openai</artifactId></dependency> <!-- OpenAiApi/OpenAiChatModel接 new-api -->
```
- **checkpoint `saved_at` 无 tiebreaker 坑(实证)**`MysqlSaver.saved_at` 是**秒级 TIMESTAMP**load checkpoint 的 SQL 仅 `ORDER BY saved_at DESC`、**无 tiebreaker**——一次 interrupt 在同秒落多条 checkpoint`__START__→n1``n1→n2`),默认 `resume()`(取"最新"**非确定**地选其一 → 续跑点漂移、节点重放。**修法(不改框架)**:用 `getStateHistory` 按内容(`StateSnapshot.node()=="n1"`)确定性选中断点、取其稳定 `config().checkPointId()`,用**显式 checkPointId** 续跑(走 saver by-id 精确分支,绕开 `saved_at` 排序。另两条resume 实例**不要再挂 `interruptBefore`**`previousNodeId` 非 null 会再停该点、后续节点永不执行);`releaseThread(false)` 才能长期续跑;表 `GRAPH_CHECKPOINT`/`GRAPH_THREAD` 是引擎私有大写 DDL、生产懒建、与 Flyway 零撞名。
**别引**`agent-framework`ReactAgent/asNodePhase1.5 才要)、`builtin-nodes`(节点全自写 NodeAction且它脱 BOM 硬钉 1.1.2.2 拖 tika 全家桶=版本钉债)、**SAA 的 Boot BOM**(让项目 3.5.14 胜,同 minor 补丁前向兼容)、**RedisSaver**(避 Redisson 3.22↔4.4 冲突)。
**铁律**:动 pom 删依赖前,先 `dependency:tree`/读 jar 核**类的模块归属**——`MysqlSaver``GraphObservationLifecycleListener` 都在 `graph-core`,删 agent-framework/builtin-nodes 不影响它们。
## 2. 拓扑(声明式条件边,非硬编码 while
`SaaStudioGraph.assemble()` 是**唯一布线源**(生产派发器 + 回归测试共用,杜绝两处漂移)。节点 11 个、条件边 4 处:
```
START→render→design→generate→validate ─┬(ok)→ scaffold→build ─┬(ok)→ play ─┬(ok)→ player ─┬(ok)→ emit→END
├(repair)→ repair ──────┘(repair)────┘(repair)──────┘(repair)→ generate回环
└(giveup)→ giveup→END
```
- 回环/分支用 `addConditionalEdges("validate"|"build"|"play"|"player", router, mapping)`router 是 `EdgeAction` 返回 `ok|repair|giveup|emit` 等字符串键。
- **超步刹车**`CompileConfig.builder().recursionLimit(N)`repair 成环时优雅终止)+ state 里的 repair 计数器,**不靠 LoopAgent**。
- 节点 = `NodeAction` lambda 经 `node_async()` 注册;状态 `OverAllState` + `KeyStrategyFactory`(多数键用 `ReplaceStrategy`)。
**加一个节点**:写 `NodeAction`(读 state→干活→`Map.of(key,val)` 写回)→ `graph.addNode("x", node_async(xNode))``addEdge`/`addConditionalEdges` 接进拓扑 → 若新键,在 `KeyStrategyFactory` 注册其合并策略。
## 3. 模型接 new-apiOpenAI 兼容)
五角色design/code/fix/player_text/player_vision共用**一个** `OpenAiApi`,仅 `defaultOptions.model/temperature` 异:
```java
OpenAiApi api = OpenAiApi.builder().baseUrl(stripV1Suffix(newApiBase)).apiKey(key).build();
OpenAiChatModel m = OpenAiChatModel.builder().openAiApi(api).defaultOptions(opts).build();
```
- ⚠️ **baseUrl 坑**Spring AI 自拼 `completionsPath=/v1/chat/completions`,故 `baseUrl` 必须是 **host 根、不带 `/v1`**`http://100.64.0.8:3000`);同一 `.env``NEWAPI_BASE_URL=.../v1`)两侧通用须 Java 侧 `stripV1Suffix` 剥之,否则 404 `/v1/v1/...`
- 视觉位M3走多模态 `UserMessage.builder().media(Media(IMAGE_PNG, ...))`。成本:全走 new-api 单一 key→自动入 `newapi_cost`(logs.quota) 计费平面,不内联折账。
## 4. checkpoint崩溃续跑 · 迁移 step5
```java
MysqlSaver saver = MysqlSaver.builder().dataSource(yudaoDruid).createOption(CreateOption.CREATE_IF_NOT_EXISTS).build();
SaverConfig sc = SaverConfig.builder().register(saver).build();
// compile: CompileConfig.builder().saverConfig(sc).releaseThread(false)... run: RunnableConfig.builder().threadId(traceId)
```
- **一图一 saver**(注册俩抛异常);`releaseThread(false)` 才能长期续跑DataSource 直接收 yudao `DynamicRoutingDataSource`(primary=master)。
- **Flyway 共存**:表 `GRAPH_CHECKPOINT`/`GRAPH_THREAD`(大写引擎私有 DDL非 Flyway 管)与 yudao 迁移零撞名;测试隔离 `saa_spike` schema生产 `ruoyi-vue-pro` 零污染。生产**懒建**(仅 dispatcher=saa 且 DataSource 在席时首个 dispatch 触 DDLhttp 态零碰 DB
- 🔴 **必踩的框架坑(实证)**`MysqlSaver.saved_at` 是**秒级 TIMESTAMP**load checkpoint 的 SQL 仅 `ORDER BY saved_at DESC` **无 tiebreaker**。一次 interrupt 在同秒落多条 checkpoint`__START__→n1``n1→n2`)→ 默认 `resume()`(取"最新"**非确定**地选其一→续跑点漂移(节点可能重放)。**修法(不改框架)**:用 `getStateHistory` 按内容(`StateSnapshot.node()=="n1"`)确定性选中断点、取其稳定 `config().checkPointId()`,用**显式 checkPointId** 续跑(走 saver by-id 精确分支,绕开 saved_at 排序)。验证见 `SaaCheckpointResumeTest`(真恢复:查库 `GRAPH_CHECKPOINT` 行 + 跨实例 `N1_RUNS==1`)。
- ⚠️ resume 实例**不要再挂 `interruptBefore`**previousNodeId 非 null 会再次停在该点,后续节点永不执行)。
## 5. 可观测observation 埋点)
`SaaStudioGraph.build``ObservationRegistry` 非空且非 NOOP 时挂 graph-core 的 `GraphObservationLifecycleListener` + `CompileConfig.observationRegistry(..)` → 每节点发 `spring.ai.alibaba.graph.node.<id>` observationtrace/耗时/失败→Micrometer。node id 编码在 observation **名 + 低基数键**里(非自定义 context 类型,测试按名前缀捕获)。`ObservationRegistry``ObjectProvider` 软注入 yudao 共享 bean。
## 6. 生产派发契约(框架可换、无 split-brain
- `GenerationDispatcher.dispatch(job)` 接口 → 两实现 `WorkerDispatchClient`(http) | `SaaGraphDispatcher`(进程内 SAA 图) 经 `aigc.executor.dispatcher`(默认 **http** 非破坏) 二选一**单写**。
- `SaaGraphDispatcher`job→state→后台单线程 `compiledGraph.invoke(threadId=traceId)`→出口组 `DifyCallbackReqVO` **进程内直调 `difyCallbackService.handleCallback`**(消灭 HTTP+HMAC复用唯一三表同事务写链串行真玩门 `Semaphore(1)` 守端口;图执行不包大事务(落库走 handleCallback 内短事务);失败统一归 `llm_error`(图内真因不在契约#6 七值枚举内)。
- 🔴 **铁律「多派发路·回调可观测字段必须每路都 set」回调 trace split-brain与下方发布裁决 split-brain 是两回事)**存在多条派发路http worker / SAA 进程内 / executor 旧路)时,任一落库可观测字段(`trace_json`/`readiness_score`)若只在部分回调构造点 set`dispatcher` 即**静默丢字段**。实证:`SaaGraphDispatcher.buildCallbackReqVO` 原只 set status/engineBundle/failureReason、**漏 setTrace** → `dispatcher=saa`(未来主力路)时 `trace_json` 恒 NULL、组B 9d/D11/D9 在 SAA 路静默失效HTTP 路因 worker `_extract_trace` 不漏)。**修法**:派发器回调构造点**对称镜像** worker 的 `_extract_trace`——从图终态 `OverAllState` 抽与之**字节兼容**的 camelCase 子集 `setTrace`,严格 additive + best-effort整段 try-catch 返 null抽不到绝不打断生成+ 复用同一 `aigc.trace.enabled` flag落库走现成 `persistTraceQuietly`,零改契约/消费侧。**字节兼容红线**:抽出键集必须=worker `_extract_trace` camelCase 子集(`stage_fail→stageFail` 等 snake→camelattempts **不含 per-attempt guards**——SAA 源端三处 appendAttempt 从不写,照 K_ATTEMPTS 注释抽会逼实现造假readiness 读**顶层** `sevenGateVerdict.guards.H_progress` 而非 attempts[].guards否则审核台/D11 两路读不一致。闭合Round3 `703e462c`HJ-AGI-002 线),真库验 task141 `trace_json` 真非空/readiness=74、task145开关关NULLverdict `agentic编排-SAA.md`。坑readiness 真值随真跑质量浮动repairs=5→stability 降、cost 缺→efficiency 中性,故 74<HTTP 96`firstPlay` `H_progress` 是嵌套对象喂 `asBool(Map)` 恒中性 0.5 = 组B 既存 bugHTTP/SAA 同病字节兼容仍成立修评分器另立 F5 **严禁**为过测把 SAA H_progress 改裸 bool HTTP 字节兼容)。
- 🔴 **铁律「worker succeeded ≠ 准予发布」split-brain 防线)**SAA worker 跑通九门(`runnableOk` = **机制可玩**:能加载/不崩/有终态)**不等于**准予发布。**发布裁决归治理层**——对抗 P0 安全审查(注入/越权/恶意资源)、金丝雀完玩率、人工二审,是与"机制可玩"正交的另一维。生成路**生产化前**,安全门须由 **W-G1 GP9 合规段接管**10 负例 staging 真验worker 自身只产"机制可玩"信号、**无权置位 published**。两者混为一谈 = split-brainworker 自评准予上架。来源agent-loop-v1 退役登记Lane B
## 7. 验证门(无 harness 端口也能验大半)
- **门 E依赖/启动)**`dependency:tree` 断言 Boot 全 3.5.14 / redisson 4.4.0 / 零 3.22 泄漏 / 删的依赖消失;`test-compile`(main+test) 绿;最小 Context 共存不破 BeanCreation。
- **门 Bcheckpoint 真恢复)**:纯 Java 2 节点小图(不调 LLM、不碰端口跑一半 interrupt→查 MySQL 断 checkpoint 落库→全新 `CompiledGraph` 实例 + 显式 checkPointId resume→抵 END 且节点不重放。gated 在 `-Dsaa.checkpoint.mysql=1 -Dsaa.mysql.pass=…`(密码经 `-D` 注入不入库)。
- 真玩 e2e图真跑九门→进程内 handleCallback→建版本/组包/入 feed须占 4320/9222与生成批跑串行`flock` 单实例锁。harness-gated 测试无 `-Dsaa.harness=1` 时正确 skip。
## 真·全图 e2e 测成功率U7 集成顶石,`SaaFullGraphE2eTest`,2026-06-18 首证)
- **测法(零 DB 跑真全图)**`new SaaGraphDispatcher(props, stubCallback, null, NOOP, null)`——props 填 `llmBase/apiKey/dispatcher=saa/saaGameRuntimeDir``saaCheckpointEnabled=false`+DataSource=null内存 checkpoint,零 DBstub `DifyCallbackService` 只捕获 reqVO 不写库。门控 `-Dsaa.e2e=1`+env `NEWAPI_KEY`+`-DGAME_RUNTIME_DIR`
- **异步派发测试坑(实翻)**`dispatch` 是**异步+单线程执行器+Semaphore(1) 串行真玩门**,且单款墙钟 **3.5-16min**(九门真玩循环+最多 8 救场轮固有,非 SAA 特有=同 Python W-G1。**别**"每条 dispatch 后 arm 新 latch 等 240s"(串行+回调错配=归因全错);正解=**一次性投递全 N 条→共享 `CountDownLatch(N)`→stub 按 `reqVO.getTraceId()` 精确归位并发 map**,每条超时 900s。
- **首证基线**:真全图(render→…→真九门 play→…→emit/giveup)真 LLM+真九门跑通、**零 prod bug**;成功率 **3/5=60% < 80%**。成功路 engineBundle≈211KB含引擎,经 `__GameBundle` 全局名校验才置 succeeded。**难门 I_control/F_wiring/H_progress 卡 60%——根因非"模型天花板"**2026-06-18 根因分析 + Codex 复核证伪:无样本确认真硬天花板;scale-20 无"人工介入后仍不过"的款)。**主因 = 无人值守产线的"喂不全 + 救场机制不足 + 门判据偏"**:命门 = 注入的 few-shot 探针 `_goldenpath-probe/factory.js``_forensicsView`/latch、而 prompt 硬要求它(模型抄没见过的实现);I_control 平滑跟手 harness 查、prompt 零提及;failCount 跨档不重置 + repair 不回喂源码。详见 `docs/brainstorms/2026-06-18-生成失败根因分析`。便宜模型不稳→救场耗尽 giveup真落盘,非崩溃)。**SAA 机制 ≠ 生成质量**:转默认产线(U7)的 ≥80% 门由 W-G1 生成质量域把守,非 SAA 编排 bug。
- **003-U1 验收门工具化plan 2026-06-18-003,可作 flip 前置硬门)**:把本测从「内置 5 条 / 只断 `succeeded>=1`(留人判 80%=不可作硬门)」升成**客观可运行产线门**——① brief 内置扩 12 条多品类 + `-Dsaa.e2e.briefFile` 外部行式文件(去空白/跳空行/跳 `#`,空则回退内置);② `-Dsaa.e2e.minSuccessRate` 可配**硬断言****默认不设=仅存在性**(保 003-U1「先实测真基线再收敛」、不破普通构建显式 `0.8` 即 flip(003-U2)前置硬门;③ **成本会计 = token 代理,非¥**——从回调 `trace.tokens.{prompt,completion}` + `modelTier`(升 stage2 计数)汇总,`-Dsaa.e2e.maxTokensPerGame` 仅**软警告不硬阻断**(创始人裁「质量优先、允许临时破价、记账对账」)。**🔴 坑SAA 路无 ¥ 数据**——`SaaGraphDispatcher` cost v0 整段省略token≠¥,挂 cost 语义错)、折¥=follow-up F3 → **别接 ¥ 门、别造假¥,只记 token**。纯逻辑抽静态方法 + 模型无关单测 `SaaFullGraphE2eGateLogicTest`(普通 `mvn test` 即跑,不需模型/key,12/12 过)。
---
**关联**[`contract-first-development.md`](contract-first-development.md)job/callback 即契约#6[`cheap-model-game-generation.md`](cheap-model-game-generation.md)(被编排的便宜模型生成)|[`staging-ops.md`](staging-ops.md)mini-desktop 构建/门)|决策与坑全集见记忆 `saa-agentic-infra-decision`

View File

@ -1,195 +0,0 @@
---
name: sim-business-game-design
description: "当为经营模拟/放置养成类 brief 产设计阶段玩法方案或评审经营类设计时使用:6 心理引擎、可达范式与决策模式清单、客流/经济数值锚、输出配方、可达性红线、反无趣自检;品类 rubric 单源指 fixture,金标锚点在册。"
---
# skill: sim-business-game-design —— 经营模拟小游戏「玩法设计」作业手册(给生成 agent 的设计阶段)
> 定位:这是 **生成 agent 设计阶段AgentScope造"好玩的经营模拟玩法设计"的唯一作业手册**。它回答 **"设计什么才好玩"**(玩法/机制/进度/数值/美术/音/UI 范式),与 [`littlejs-game-dev.md`](littlejs-game-dev.md)(回答"代码怎么写")配对:**设计阶段产玩法概念 → 代码生成阶段照 littlejs-game-dev 产代码**。
>
> **铁律(创始人 2026-06-21)**:"好玩 = 上游设计层职责",不在 runtime/插件。插件只让设计**能被 juice 出手感**;**这份 skill 负责让设计本身好玩**。
>
> **可达性边界**:产出必须落得到 A-model 轻量运行时——**LittleJS 2D 手机竖屏(390×844)、单/少场景、便宜模型可生成的多文件代码、11 注入插件能力域**。设计再好,落不到这个运行时就是空想——见 §9 红线。
>
> **证据基**:范式蒸馏自近年微信/抖音「放置经营 / 模拟经营 / 模拟养成」品类的成熟模式(训练知识,非实时榜单);范式稳定可用,具体举例供参照。
---
## 0. 为什么这一品类爆(底层动机 —— 设计要击中这些)
经营模拟小游戏的"爽"来自 6 个心理引擎,**好玩 = 同时点燃尽量多个**;无趣 = 一个都没点燃:
1. **即时反馈**:每个操作 0.1 秒内有可见回报(数字 +N、飘字、音效"叮"、粒子)。
2. **可见成长**:数字/规模肉眼可见地变大(分数飙升、店铺扩张、等级上涨)。
3. **解锁期待**:"再赚一点就解锁新东西"——永远有下一个钩子(新商品/区域/角色)。
4. **收集欲**:图鉴/装饰/角色凑齐的强迫感。
5. **治愈/养成情感**:萌角色、温暖场景、"我把它养大了"的拥有感。
6. **低门槛 + 放置不肝**:3 秒上手、挂机也有收益、回归有惊喜。
> **反"无趣"总纲**:无趣几乎总是因为——操作无即时反馈 / 成长不可见 / 没有"下一个解锁" / 数值平淡线性 / 无情感锚点。§10 是落地清单。
---
## 1. 核心玩法范式(可达子集 —— design 优先从这里选)
| 范式 | 一句话 | 我们运行时怎么落(插件) |
|---|---|---|
| **短核心循环** | 操作→即时收益→积累→升级/解锁→扩张→重复;**30 秒内见第一次升级** | session-score 计数 + hud-ui 飘字 + particles-juice 爽感 |
| **放置/挂机收益** | 不操作也持续产出(每秒/每 N 秒 +X);**回归给离线收益弹窗** | timer-scheduler `every()` 产出 + save-progress 存离线时间戳 + 回归算差值 |
| **合成升级(merge)** | 两个同级合成更高级(更高产出),制造"再合一个"冲动 | core 逻辑 + particles-juice 合成"啪" + hud-ui |
| **阶段解锁阶梯** | 攒够 → 解锁新商品/区域/角色,**永远露出下一个锁** | session-score 阈值 + scene-fsm/状态 + hud-ui 解锁庆祝弹窗 |
| **轻经营决策** | 节奏与压力下的管理判断——**单击可达但有真实决策**(标准模式见下方决策模式清单;定价/摆放/雇员这类多步配置面板是 tier2 的菜,便宜档别做) | core 逻辑 + hud-ui 面板/按钮 |
| **进货/补货循环**(经营核心资源环) | 花软币**进货**→**库存**→售卖收钱→**缺货补货**;制造"赚→进→卖→再赚"的资源管理张力 | core 库存状态(数量/上限)+ session-score 货币(进货扣、售卖加)+ hud-ui 库存条/补货按钮 + particles-juice 售卖爽感 |
| **收集图鉴** | 凑齐商品/角色/装饰 | save-progress 存收集状态 + hud-ui 图鉴格 |
| **数值成长曲线** | 产出/成本指数成长 + 卡点,制造"差一点" | balance 数值表 |
**组合配方(经验)**:一款轻量经营 = **1 个主循环(放置产出 或 点击经营)+ 1 个成长轴(升级/合成)+ 解锁阶梯 + 收集/装饰点缀**。别堆超过 2 个主机制(便宜模型 + 单屏扛不住,也稀释爽点)。
**⑨ 决策模式清单(经营类的标准填法——每款至少取一种做透;全部单击可达、判错有真代价)**:
- **优先级选择**:多客同时等、耐心条各自流逝——先服务谁?判错则耐心尽、丢单扣分;
- **补货时机**:库存见底才能补、补货扣软币——现在补还是再卖一单?拖过头则缺货断客流;
- **时机窗口**:高峰波次前备货/出摊——押对时点收益翻倍,押错白付成本;
- **风险收益**:囤货等大单 vs 现金落袋(稀有客给大单但占双倍时长)——贪多可能砸手里。
每种都能填 ⑨ 判定句「玩家在__时要判断__,判错则__」;填不出=退回重选模式。cheap 主 prompt(v1.6.0)的经营路由即指到本清单。
> 注:SoT §4 已将 prompt 自检第 ⑨ 条升为评分尺第 12 条,本清单编号不变。
---
## 2. 关卡 / 进度 / 留存设计
- **首 3 分钟(决定生死)**:① 010 秒:零阅读即上手(点一下就有钱/有反馈);② 1060 秒:**第一次升级/解锁**(快速给到"变强了"的爽);③ 13 分钟:露出 23 个后续解锁锁,建立"还有很多"预期。
- **留存钩子**:每日首登奖励 · 离线收益弹窗(回归惊喜)· 目标/任务清单(手把手给下一步)· 限时小活动(可选)。
- **长线**:阶段目标(开第 N 家店)· 图鉴凑齐 · 声望/转生(重开换永久加成,放置品类标配)。
- **节奏**:爽点(升级/解锁/暴富)与平台期交替;平台期是"差一点就能…"的卡点,引向看广告/等待/放置。
---
## 3. 数值与经济(广告变现轻游戏 —— 我们的变现模型)
- **双货币**:软币(循环挣、用于常规升级)+ 稀缺资源(钻石/星,用于加速/解锁,**经看广告或攒**)。
- **看广告点位(自愿、不打断爽点)**:加速产出 · 双倍离线收益 · 免费解锁/抽奖 · 复活/续命 · 翻倍领取。**包装成"福利"**(金光按钮 + "免费 ×2"),不是惩罚。
- **卡点设计**:让玩家**"差一点就能升级/解锁"**(成本曲线略陡)→ 自然引向看广告或放置等待。**别卡死**(挫败 = 流失);卡点是"诱惑"不是"墙"。
- **成长曲线**:产出/成本指数(如 ×1.15/级),配合放置收益,制造"滚雪球"暴富快感。
- **客流/出现节奏(点客·经营类必给目标值 —— 治"来太快/太慢")**:design 要给**具体区间锚**,别让 code agent 拍脑袋(实测拍出来偏激进 = 顾客太快)。参考:清闲期出现间隔 **~1.82.8s** / 同屏上限 **3** / 顾客耐心 **~57s**;高峰期间隔 **~1.01.5s** / 上限 **4**;**高峰别太早**——一局 60s 的话 rush 起点 **≥35s**(留足清闲铺垫,先松后紧)。这些目标值交 balance.js。
---
## 4. 美术风格范式(可达 + mmx 可产 + 程序化兜底)
| 风格 | 调性 | 配色 | 适合 |
|---|---|---|---|
| **Q 版萌系扁平**(主流首选) | 轻松可爱 | 高饱和糖果色 | 绝大多数轻量经营 |
| **治愈系暖色** | 温暖疗愈 | 奶油黄/杏色/暖橙(参考 wanglanmei-ref) | 养成/小店/种田 |
| **国潮国风** | 雅致 | 低饱和 + 朱砂/黛青点缀 | 古风经营(种田/商号/建城) |
| 像素 | 复古 | 有限色板 | 轻怀旧(谨慎,精度要求高) |
- **角色/物件**:圆润、大头身、夸张表情(萌 = 情感锚点)。
- **美术一致性之根**:**全程共用一组"风格词"**(主体描述 + 风格 + 配色 + 构图),mmx 每次生成都带它(见 wanglanmei-ref `assets/manifest.json` styleWords)。
- **mmx 生成**:`mmx image generate --prompt "<主体>+<统一风格词>+纯色背景,居中,简洁" --out x.jpg`(见 littlejs-game-dev §6)。
- **程序化兜底**:无 mmx / 缺图时,hud-ui + palette-post 画几何图形 + 统一调色也能成立(catch-fruit 纯图形即可玩);**别因美术阻塞可玩性**。
---
## 5. UI 范式(hud-ui 全可达)
经营游戏 UI 套路(都用 hud-ui 落):
- **顶部 HUD**:货币 / 等级 / 进度条(固定左上/右上);
- **一键收取**:大按钮"收取"(收菜/收钱),点击 → 飘字 +N + 粒子 + 音效(放置品类核心交互);
- **飘字 +N 动画**:每次收益弹出"+N"上浮淡出(即时反馈的视觉化,particles-juice/hud-ui);
- **看广告按钮**:金光/"免费 ×2"包装,显眼不烦人;
- **升级/解锁庆祝弹窗**:解锁瞬间全屏小庆祝(闪白 + 粒子 + 音效,particles-juice);
- **新手强引导**:首 30 秒手把手(高亮 + 手指点哪),零阅读;
- **红点/角标**:可领取/可升级处红点提示。
---
## 6. 音乐 / 音效范式(audio-music 可达)
- **BGM**:轻松治愈循环(闲时)/ 欢快明快(高峰);可情绪分层(audio-music `setIntensity`)。mmx 生成:`mmx music generate --prompt "温暖悠闲...治愈" --instrumental --bpm 90`
- **音效(爽感命脉,务必有)**:收钱"叮"💰 · 升级欢呼🎉 · 合成"啪" · 点击 pop · 解锁号角。**即时音反馈是"爽"的一半**——audio-music `playSfx`,无声卡静默降级不崩。
- 红线:**宁可程序化音效,也别没有反馈音**(无音 = 无趣的隐形元凶)。
---
## 7. 首屏 / 留存视觉钩子(通用,MVP 也用)
- 首屏 3 秒抓人:**立即可玩 + 立即有反馈**(别先放长剧情/教程墙);
- "从破到繁荣"的成长对比预期(破旧小店 → 繁华商业);
- 萌角色当门面(情感锚);
- 数字飙升的暴富快感预告。
---
## 8. 设计阶段输出配方(生成 agent 据 brief 产出这些 → 喂代码生成)
给定一句话 brief,设计阶段应产出一份**结构化玩法设计**,**逐项落到运行时**:
1. **核心循环**(一句话 + 30 秒脚本:玩家做什么→得什么反馈→怎么变强);
2. **主机制 12 个**(从 §1 选;标注用哪些插件);
3. **解锁阶梯**(36 级:解锁什么 / 阈值);
4. **数值骨架**(初始产出、成长率、卡点、看广告点位 —— 交 balance.js);
5. **美术风格词**(一组,全资产共用)+ 资产清单(角色/商品/背景/UI,标 mmx 或程序化);
6. **音效清单**(收益/升级/合成/点击/解锁)+ BGM 情绪;
7. **UI 布局**(HUD/收取按钮/飘字/弹窗/引导,§5);
8. **插件选择清单**(→ 写进 host-config.js;常用:session-score + timer-scheduler + hud-ui + particles-juice + audio-music + save-progress + scene-fsm,按需加 collision/physics-lite);
9. **首 3 分钟脚本**(逐步:上手→首次升级→露出后续锁)。
> **MVP-first 铁律(关乎 code agent 能否收敛)**:设计**聚焦可玩核心** —— 核心循环 + 1 主机制 + 1 资源环(进货)+ 34 级解锁 + 基础数值/音效,商品 ≤3 种起步。**离线收益 / 广告 / 雇员 / 多档 BGM / hitstop / 6+ 商品 等一律标「后续·MVP 不做」**,别一稿堆满 —— 设计太满 → code agent 实现负担过重 → read-thrash 不收敛(实测:7.5KB 满配设计 code 循环截停;~5KB 精简 9 步收敛)。**先出能玩的核心,再谈丰富。**
> 这份设计 = 代码生成阶段的"图纸";生成 agent 照 littlejs-game-dev 把它实现为多文件代码。**设计阶段把"好玩"想清楚,代码阶段只管正确实现。**
---
## 9. 可达性红线(落不到运行时的设计 = 废稿,必剔除/简化)
- ❌ 联网 PVP / 多人实时 / 排行榜社交 → 改单机 + 本地最佳分(session-score best)。
- ❌ 大世界 / 自由滚动地图 / 多关卡大流程 → 单/少场景竖屏(scene-fsm 切 35 态)。
- ❌ 3D / 大量逐帧手绘动画 → 2D + 程序化动效(particles-juice/缓动)+ 少量 mmx 静态图。
- ❌ 复杂经济(多资源链/市场波动)→ 双货币 + 简单成长曲线。
- ❌ 超过 2 个主机制 → 便宜模型 + 单屏扛不住,聚焦 1 主 + 1 成长轴。
- ⚠ **核心成单/得分动作优先单击直给**(如"点等待中的顾客 = 自动按库存售卖收钱"):手机竖屏 casual 玩家一根手指就该玩得转,「先选 A 再选 B」两步匹配抬高上手门槛;**深度靠进货/库存/解锁/节奏,不靠交互步数**。确需多步(先锁顾客再点货架),须给清晰的视觉指引(选中态高亮 + 下一步该点的目标有 affordance),让真人一眼看懂下一步点哪。
- ✅ 留下的:放置产出、合成升级、点击经营、阶段解锁、收集、轻决策、数值滚雪球 —— 全在 11 注入插件 + 单屏 + 便宜模型可生成范围内。
---
## 10. 反"无趣"落地清单(design 自检,逐条必过)
1. **即时反馈**:每个主要操作有没有"飘字 +N + 音效 + 粒子"三件套?(无 → 必加)
2. **可见成长**:有没有一个数字/规模肉眼可见地变大?
3. **下一个解锁**:任意时刻玩家眼前有没有"再攒一点就解锁 X"的锁?
4. **30 秒爽点**:开局 30 秒内有没有第一次升级/解锁?
5. **数值滚雪球**:成长是不是有"越来越快"的暴富段(而非平淡线性)?
6. **情感锚**:有没有一个萌角色/拥有物让玩家"想养大它"?
7. **放置回归**:离线回来有没有惊喜(离线收益弹窗)?
8. **音反馈**:收益/升级/解锁有没有声音?(哪怕程序化)
> 8 条命中越多越好玩;命中 ≤3 条 ≈ catch-fruit 那种"能玩但无趣"。**设计阶段就过这张表,别等做出来才发现无趣。**
> **机检硬门 vs LLM 评分维度(质量轨 2026-07-02 分界裁定)**:这 8 条里原标「4 条可机检」并不同质,`docs/architecture/架构/生成引擎/游戏质量与爆火能力.md` §3/§4 拆成两类——纯代码存在性的三条降为 LLM 评分维度(rubric,非阻塞),经真玩校验的断言留作 L1 硬门(机器判定、可拒发):
> - **①即时反馈**(主操作真调 `particles-juice` 飘字/粒子 + `audioMusic` 音效)、**③下一个解锁**(解锁逻辑 + 阈值常量)、**⑧音反馈**(真调 `audioMusic.playSfx` 或引擎 zzfx):代码里存在调用不等于体验真成立,故交 LLM 验证 agent 评分,不落项目代码、不进九门;
> - **②可见成长** = `score`/营收随操作上升,经 H 门 `assertAfterPlay {path:score,op:increased}` 校验,属 L1 机制可玩,保留为阻塞硬断言、不软化;
> - **经营/进货品类**再加一条 per-game 运行时断言:买/进货时货币真减 → gatespec 产 `{path:"currency",op:"decreased"}`,同经 H 门 assertAfterPlay 校验,同属 L1 硬断言(**注:通用九门不加品类语义,断言是每局喂的**)。
>
> 同名两物分工清楚:L1 断言判「机制上真发生」,rubric 判「体验上成立、有层次」;把真玩硬证据降成软评分会削 L1 的拒发权,方向恰好反了。其余(情感锚/放置回归/滚雪球)仍靠真玩判,不机检。
**品类扩展 rubric(机器版,喂 LLM 丰富度验证 agent·非阻塞)= 单源 [`cheap-worker/fixtures/genre-rubrics/sim-business.json`](../../cheap-worker/fixtures/genre-rubrics/sim-business.json)**(2026-07-04 补齐;四条名目 = 质量模型 SoT §4 规范二经营范例——进货补货循环成立 / 客流节奏有紧张感 / 双货币分工明确 / 卡点「差一点」不卡死;与上面通用 8 条**分母分开、独立小计**。改条目只改 fixture,本 skill 不维护副本)。
---
## 金标锚点(12 条口径基线 · 质量模型 SoT §4 规范三)
每品类需 ≥1 金标正例 + ≥1 薄反例锚住评分尺(rubric 文本或评分 prompt 变更须金标复验,单款分组小计漂移 >±1 即回退)。经营品类的锚 2026-07-04 落定(生产口径 genre=sim-business,同输入复采样 n=3 取中位,入口 `cheap_verify.verify_richness`):
- **金标正例 = [`cheap-worker/fixtures/golden-samples/sim-business/bake-shop-serve-1`](../../cheap-worker/fixtures/golden-samples/sim-business/bake-shop-serve-1/)**(S5b lab 复验小批产的烘焙店 60 秒上菜 rush:三桌耐心条、700ms 连击窗叠技巧分、营收三档阈值 22/55/100 渐次解锁菜单;attempts=1 一次过当时九门、¥2.00;锚定于 W-AXIS-V2 前,当时驱动器口径已随取证契约退役)。**通用 12 条 n=3 中位 = 10/12**(单发 11/9/10,方差 ±2 合纪律;稳定缺项只有「放置回归」——60 秒单局制无离线收益,形态使然)。第 12 条「核心操作非无脑」命中:多桌耐心排序 + 连击维护是真实决策,走客有真代价。
- **薄反例/对照 = `_template-shop` 骨架原样**(C 波 2026-07-03 实测:通用前 11 条 4/11、第 12 条未命中——点顾客即自动上菜结算 = 无脑点击;品类段 1/4)。**正反例通用段分离 56 分,通用尺的判别力锚在这条差距上**;与 TRPG/puzzle 模板(富正例)相反,经营骨架天然薄,薄对照角色恰好由它担、不必再造合成材料。
- **品类扩展段(4 条)诚实口径**:正例本款只中 2/4——serve 形态天然没有进货补货与双货币,这两条的正锚要等进货型经营款出现;品类段正例对骨架对照(2/4 vs 1/4)分离仅 1 分,**回退判定勿跨形态用品类段,以通用段 + L4 为准**。
- **方差纪律**:M3 单发方差 ±2,取中位;锚定凭据(逐条命中理由原始记录)= 正例 `evidence/richness-n3.json`
---
## 相关
- 代码层实现 → [`littlejs-game-dev.md`](littlejs-game-dev.md)(11 注入插件 API / 结构 / 资产-mmx / 工厂契约)
- 便宜模型生成 worker / 九门 → [`cheap-model-game-generation.md`](cheap-model-game-generation.md)
- 生成 agent 设计阶段接入 → 现行生成主线 = AgentScope 三档统一收敛SAA 降最低优先级、留作远期适配验证code 在 `game-cloud/.../saa/`A-model 改写见 `git show 8ea97234:docs/agent-specs/2026-06-21-saa-amodel-rewrite-review.md`

View File

@ -1,11 +1,6 @@
---
name: staging-ops
description: "在 mini-desktop/mini-infra 上做 staging 或内测 dev 的部署运维时使用:机器分工铁律、代码同步(git push→Gitea clone)、后端重部署标准序、构建门/冒烟门、观测栈起容器、生成线环境地图与 executor 接线的实战配方与踩坑。"
---
# staging 运维配方(机器分工 / 代码同步 / 重部署 / 构建门 / 冒烟)
> staging 全栈在 **mini-desktop100.64.0.7**huijing 单体 :48080 + game-studio :4173 + game-admin :4174 + 隔离 MySQL/Redis 容器共享基建Gitea/MySQL/Redis/MinIO/new-api/Nacos/RocketMQ/观测栈五件 Collector/Tempo/Prometheus/Grafana/Loki)在 **mini-infra100.64.0.8**
> staging 全栈在 **mini-desktop100.64.0.7**huijing 单体 :48080 + game-studio :4173 + game-admin :4174 + 隔离 MySQL/Redis 容器共享基建Gitea/MySQL/Redis/MinIO/new-api**mini-infra100.64.0.8**
> 本配方由 2026-06-09~11 多波实战蒸馏M1 真跑 / 黄金闭环 / UI 走查 / Wave4 集成门);红线类条目同步收录于 [`../rules/engineering-conventions.md`](../rules/engineering-conventions.md)。真 UI 走查另见 [`ui-walkthrough-cdp.md`](./ui-walkthrough-cdp.md)。
## 1. 机器分工铁律(四机)
@ -13,19 +8,15 @@ description: "在 mini-desktop/mini-infra 上做 staging 或内测 dev 的部署
- **创始人本地工作站 → lili-mac**M1 Pro / 32G / ARM**不入 Tailscale 调度**,会休眠合盖):跑 Claude Code 主会话、本地全栈 dev、本地浏览器/CDP **快走查**`mvn`/`npm` 快构建迭代。**两条边界**:① ARM≠x86,Mac 产出的 JAR/前端 bundle 架构中立可用,但 **Docker 镜像 + smoke 门必须在 mini-desktop x86 出**(否则镜像架构错、与 prod 不同构=半假门);② Mac 上 CDP 只算"快走查"加速开发,**e2e 验收门仍只认 mini-desktop 结果**(服务+浏览器同机 localhost,见 [`game-e2e-cdp-harness.md`](./game-e2e-cdp-harness.md))。
- **权威验收 / 重型构建 / 测试 / staging app / 浏览器 e2e 门 → mini-desktop**15Gjava17 + mvn3.8.7 + docker + node22 + chrome146与 prod 同构)。
- **常驻无人值守 → 6c6g**:常开的 agent 编排/Workflow、cron 定时、后台批跑、git 操作Mac 会休眠故常驻活只能留此)。**禁跑重活**:重型前端构建 OOM、chrome 连续 exit 144。**元规则:同一失败签名第二次出现 → 换机器,不再换启动姿势。**
- **共享基建 → mini-infra**(绝不在其上跑项目 app / 重型构建;Nacos/RocketMQ 已自托管部署2026-07-01)。
- **共享基建 → mini-infra**(绝不在其上跑项目 app / 重型构建;缺 RocketMQ+NacosMVP 未部署)。
## 2. 代码同步(分类器约束)
- **从 Claude Code 环境 ssh 内网(2026-06-21 实证)**:`ssh mini-desktop` 别名会被解析到陈旧代理段 `198.18.0.7`、port 22 被关(`Connection closed by 198.18.0.7 port 22`,exit 255),Bash 沙箱开/关都一样。**正解 = 直连 Tailscale IP + 禁 ProxyCommand/Jump + 关沙箱**:`ssh -o ProxyCommand=none -o ProxyJump=none -o StrictHostKeyChecking=accept-new root@100.64.0.7 ...`,且 Bash 工具设 `dangerouslyDisableSandbox`。其它内网机(new-api `100.64.0.8` 等)同理用**直连 IP**,别用可能陈旧的主机别名。
- `scp`/`rsync` 源码树会被分类器语义拦截settings 放行也没用)。**正解 = git push 到 Gitea → mini-desktop 匿名 HTTP clone/pull**`http://101.200.34.71:3000/zizi-al/games-development-ai.git`:3000 HTTP 匿名读,非 :2222 SSH
- 单文件传输:`cat local | ssh mini-desktop 'cat >/tmp/x'`(分类器不拦单文件)。
- 远程脚本省心写法:`ssh mini-desktop 'bash -ls' <<'REMOTE' ... REMOTE``-l` 拿 PATH`-s` 读 stdin免引号地狱
- **push 竞态与管道掩码2026-06-12 实翻)**:①大资产 push 在途时再发 push 会撞 Gitea ref 锁(`remote rejected (failed to update ref)`/`failed to push some refs`)——**同仓 push 串行化,等上一笔落地(`git ls-remote` 核)再发**;②`git push 2>&1 | tail -1` 的退出码=tail 恒 0**会吞掉推送失败**——push 不接管道,要么裸跑判 `$?`,要么 `tee`+`PIPESTATUS[0]`。每次 push 后以 `git ls-remote origin <branch>` 实证远端头,不信本地输出。
- **commit 在途 M 触发 pre-commit 版本闸git log 不前进 ≠ commit 成功2026-07 实证)**commit 在途 M 文件(如 `contracts/prompts/registry.yaml`pre-commit 的 Prompt Registry 一致性检查会比暂存 registry 的 version 登记 vs 工作树 prompt 正文 frontmatter version门机制见 [`prompt-governance.md`](./prompt-governance.md) §2 的 `check_registry.py`)。若上个会话升了 prompt frontmatter 版本(如 `04-config/cheap-system.md` 1.8.0→1.8.1)却没同步 registry 登记,这笔 commit 就被拦 `[FAIL]` 版本漂移——**git log 不前进、暂存区保留,极易误以为 commit 成功了**。做法commit 前先机械对齐 registry 登记的 version 值匹配 frontmatter注释标明「同步 frontmatter 在途升版,变更见该文件」,不动 prompt 正文内容)。两条连带纪律:① commit 在途 M 会带上之前会话在同一工作线上的在途修改(同一工作线无法 hunk 分离,合理);② 但导致红测试的根因文件(如文案漂移的 `cheap_roles.py`/`cheap-system.md`、brief 漂移的 `test_match3_gold_batch.py`**不要 commit**,免把红测试入库;量大的 raw 审计产物留 untracked。
## 3. 后端重部署标准序(授权窗口内执行)
> 教训:`~/game-staging/repo` 曾是 stale 克隆,「构建源 ≠ 运行 jar」翻过车——每次部署按此序以字节码实证收口。
@ -49,86 +40,16 @@ description: "在 mini-desktop/mini-infra 上做 staging 或内测 dev 的部署
> **教训速记(区分两个隔离用法)**:同样起 `:48090` 隔离实例——「重部署安全变体」目标是**最终要切 live**(验全过 → 停老起新);「`-D` 注入真验 flag」目标是**永不切 live**(只验 flag 行为,验完即停,主干字节不变)。混用会误把"只想验 flag"做成了"切 live"。
> **后端接 staging 前环境检查:代理劫持防护(创始人 2026-06-22 历史回收判定捡回·边际登记)**。staging 机若装了 mihomo 之类代理客户端(占 7890/9090),Java 进程连内网 MySQL/Redis 可能被代理拦截,表现为莫名其妙的连接失败。接线前先把内网网段 `100.64.0.0/10` 加进 `no_proxy`/JVM `nonProxyHosts`,放行内网中间件连接。(与生成域 new-api 出网调模型的 no_proxy 是两个场景:那条放行出网,这条放行内网。)
> **边际登记(创始人 2026-06-22 历史回收判定,真部署时补,不在此展开)**:① **快检部署落点**(运维 033)——`deploy/smoke-test.sh` 等就绪快检在哪台机怎么跑,真部署时在本配方 §5 补;② **观测数据保留期**(运维 034)——**第一波已定基础保留期**(Prometheus/Loki 15 天、Tempo 7 天,见 `deploy/infra/observability/` 及 §6);与 `security-and-reliability.md` §6 日志保留期(ERROR/WARN 90 天、INFO 30 天)、磁盘水位告警(运维 021)、¥500/月监控预算(运维 027)的完整对账,待后续波观测真接数据后收口;③ **staging 数据脱敏子集归属**(运维 006)——真上线后 staging 要不要用生产数据脱敏子集来验(暴露真实数据分布下的 bug),是运维-合规交叉约束,**待定**(现状 staging 是 seed 假数据,可幂等重建)。
## 4. 构建/测试门(红线)
- **`vue-tsc --noEmit` 是假门禁**,前端验证一律 `npm run build`
- mini-desktop 构建 game-studio **必须 `npm run build -- --mode staging`**vite preview 直接 serve 工作树 dist默认 build = 事实上改部署)。**缺 `--mode staging` 的实证后果2026-06-21 bug#4 钉死)**`.env.staging``VITE_API_BASE` 不被加载 → axios `baseURL` 烤成空串 → 所有 `/app-api/*` 打到前端 origin`:4173`),而 **`vite preview` 不代理 `/app-api`proxy 仅 dev 生效)**、SPA fallback 对未知路径回 `index.html`HTTP 200。后果链`getRuntimePackage` 拿到 HTML 字符串 → `request.ts` 拦截器对「非 `{code}` 包络」直接透传 body 不报错 → `respVO` 是字符串 → `respVO.manifestUrl` undefined → resolvePackage 静默走 demo `clicker` 兜底(**无 warn**)→ demo `startRuntime` 抛「template system under reconstruction」→ `game_error` → /play 显示「游戏加载失败」。排障锚CDP 抓包看 package 请求是否打到 `:48080`(应有,且应顺带一条 `.../manifest` 请求grep dist `create({baseURL:` 应为 `http://<后端>:48080` 而非空串
- mini-desktop 构建 game-studio **必须 `npm run build -- --mode staging`**vite preview 直接 serve 工作树 dist默认 build = 事实上改部署、mock 中间件接管且无 warn
- huijing 前端game-admin稳定配方**pnpm9**`corepack prepare pnpm@9.15.4 --activate`,一举绕开 pnpm10+ 的 lockfile 镜像校验与构建脚本审批两道坎)+ 前端目录 `.npmrc` 设 npmmirror → `node --max_old_space_size=4096 ./node_modules/vite/bin/vite.js build`(绕 pnpm-run 预检。node_modules 腐化(如混入 vite8 / 杂散 `pnpm-workspace.yaml` 报 packages 缺失)→ 移开杂散文件 → 清装精确回钉版本。
- huijing-module-system 测试:**`SPRING_DATA_REDIS_PORT=26379 mvn test ...`**(宿主 16379 被 staging redis 占用带密码,嵌入式 RedisServer 失败被吞 → NOAUTH 假红)。
- **mini-desktop 长驻 serve/后台进程**2026-06-11 T1-spike 双 lane 实证ssh 会话内 `nohup &`/`setsid` 仍可能随会话 teardown 被 SIGHUP 连带杀(症状=稍后访问 ERR_CONNECTION_REFUSED。稳定配方**首选 `systemd-run --unit=<name>` 起 durable 单元**;次选独立 launcher `setsid bash -c 'exec node serve.cjs'` 双脱离 + **起服后 5×6s 探活门**curl 200 连续过)确认常驻再继续。另:长任务后台进程严禁与 `pkill`/`curl` 写进同一 heredoc 串行(竞态留孤儿进程占 pid 不占端口)。
- **长任务(真模型 eval / CDP 真玩)焊死前台串行 + 孤儿独立复核2026-07 实证)**:这类长任务交给后台代理,它常会再起后台子任务或「起后台等通知」,然后**截断返回中间态**——mini-desktop/开发机上残留孤儿 `python http.server` + headless chrome + `/tmp` profile产物没取回。做法长任务一律焊死前台串行`run_in_background`/`&`/`nohup`/起后台等通知),每步在 finally 杀 serve + chrome、删 profile。收尾不信代理自报「已清理」主代理**独立 ssh 复核孤儿**三项都空才算清:`ps aux | grep -E "http.server|chrome" | grep -v grep``lsof -i:<port>` 查端口占用、`ls /tmp` 查残留 profile。前台串行纪律与 [`gen-path-parity-harness.md`](./gen-path-parity-harness.md) §并发同源;此条补「截断返回中间态」与「独立复核孤儿」两个具体面。)
## 5. 冒烟门
- API 就绪门:`deploy/smoke-test.sh`(只读 12 项)+ `--deep`13 项含 generate 入队),含 CORS/鉴权test1 token验证。
- gotchafeed item 的 `packageUrl` 恒 null真实取包走 `GET /app-api/runtime/package/{versionId}`
- **feed 流端点语义2026-07-05 真机坐实,防 R5 式假警报)**:主混合流 `GET /app-api/feed/stream` 只收 `cursor`+`size`、**无 zoneId 参**,恒服务 zone0带 zoneId 的专区流是另一个端点 `GET /app-api/feed/zone/stream?zoneId=N`。给 `/feed/stream` 传 zoneId 会被静默忽略(曾据此误判「新游戏不在流」)。`size``@Max`=20**超限校验失败返空 `data` 而非报错**——排「流空」先核 size。发布编排创始人「汇入主混合流」对每次发布**双写** zone0 主流基线 + 若创作者选非零专区再补写该专区(`PublishOrchestrationServiceImpl.writePublishBaseline`),故一款游戏可同时现于主流与其专区;新游戏 sort_score=0排在已回灌的高分游戏之后、未刷分组之首。
- **API 全绿 ≠ UI 通**:编排器旁路会掩盖 UI 缺陷,用户可见波次收口前必须做一次真 UI 走查(见 [`ui-walkthrough-cdp.md`](./ui-walkthrough-cdp.md))。
### 游戏内容 OSS 单款隔离验收
这条配方只验证“Agent 从 OSS 续改并回存、Runtime 从 OSS 加载、浏览器可玩”,不切 live也不代表存量游戏完成迁移。
1. 在 `:48090` 起隔离后端前先加载 `/root/game-staging/infra/.env` 的真实 staging MySQL/Redis 凭据;显式打开 `aigc.game-content-control.enabled=true`、注入凭据档中的 HMAC secret并设置 `game.artifact-storage.enforce-committed=true``read-mode=OSS`。漏加载 env 会以 MySQL 拒绝访问表现,不能误判为业务代码故障。
2. Agent 用 `tier2/gen-worker/scripts/game_content_repository.py``prepare` 取得后端分配的版本身份,再从 `game-sources` checkout。LittleJS/Canvas 源码依赖 Git 中的平台引擎与插件,检出目标必须是被 `.gitignore` 排除的 `game-runtime/games/<工作目录>`;测试和构建都在这里运行,具体游戏源码与 `dist` 仍不得加入 Git。
3. 修改完成后只能走 `commit`:源码归档 → 运行包/素材 → V34 pending → 后端 `finalize` 在同一本地事务中写 Runtime 元数据、CAS V34 committed、绑定版本摘要 → Agent 再幂等确认 committed。`finalize` 不修改 `game_project.current_version_id`;任一步失败都不能切走当前可用版本,重试必须幂等;不得直写 project/runtime 表。
4. 用 `/gstack` 按新 `versionId` 真开预览页并进入游戏。收口证据至少包括菜单与玩法截图、canvas 非空像素、游戏状态随时间推进、manifest 与声明素材均走 `/app-api/runtime/package/{versionId}/...`、网络中没有 MinIO 直连。截图和结构化 verdict 上传 `game-evidence`,不进 Git。
5. 只有浏览器验收通过后才调用 `activate --expected-current-version-id <验收前 current>`。后端在 Project 锁内比较 current同一版本重试幂等并发旧快照或向旧版本回切都拒绝。激活失败时 current 必须保持原值。
6. `gameId`、旧 `versionId` 和调用租户必须一致。历史 tenant=0 探针不能在 tenant=1 下复用;控制面拒绝是正确安全行为,不得靠改库绕过。
7. 前端构建必须显式指定隔离 API`VITE_API_BASE=http://100.64.0.7:48090 npm run build -- --mode staging`。iframe 只授予 `allow-scripts`;游戏持久化当前会回落内存适配器,禁止为消除 warning 恢复 `allow-same-origin`
8. Runtime reader 只能 `GetObject game-artifacts/tenants/*`。验收时同时验证 List、Put、Delete 和 `game-evidence` GET 被拒绝;不得让 Runtime 复用 writer 或 root 身份。
验收完成后删除临时 OAuth token、对应 Redis 精确 key、本地 checkout 和临时脚本。保留 OSS 中的不可变失败版本用于审计,项目当前版本只指向最后一个通过验收并显式激活的版本。
## 6. docker compose 部署到 mini-infra观测栈第一波实战蒸馏2026-07-05
观测栈五件Collector/Prometheus/Tempo/Loki/Grafana起在 mini-infra 时,从 compose 写好到五件全绿踩了四个坑,没一个是 compose 语法错——`docker compose config` 全过、`pull` 也成功,问题全在环境和镜像。起容器前按这四条自查,能省一整轮排障。
- **端口要 `ss -tlnp` 查、别只信 `docker ps`**`docker ps --format` 只列 bridge 网容器的端口映射host 网容器mini-infra 的 nacos/rocketmq 用 host 网)和宿主进程占的端口一个都不显示。观测栈 Prometheus 默认 9090恰好撞上 mini-infra 自己跑的 mihomoclash 内核§3 已记它占 9090 当 API 端口)——摸底时 `docker ps` 看不到、起容器才报 `address already in use`。起容器前对每个要对外映射的端口跑 `ss -tlnp | grep :<port>`撞了就让Prometheus 让到了 9091
- **开了 `-config.expand-env` 的组件,配置注释里别写 `${...}`**。Loki、Tempo、OTel Collector 开 `-config.expand-env` 后,会把整个配置文件文本(**连注释一起**)拿去展开 `${VAR}`,而且校验严格:变量名里出现通配符(比如注释里图省事写 `${MINIO_*}` 指代两个变量)就报 `missing closing brace`、容器起不来。注释写成纯文字「MINIO_ACCESS_KEY / MINIO_SECRET_KEY 两个变量」),别用 `${}` 形式。这坑 Tempo 和 Loki 同款,别以为哪个展开器宽松(我第一次还误判 Tempo 宽松、又在修复注释里写了一遍反例 `${MINIO_*}` 二次踩坑)。
- **`pull` 成功不等于 binary 在,起容器前 `docker run --rm <img> --version` 验一下**。daocloud mirrormini-infra 的 registry-mirror缓存的 `otel/opentelemetry-collector-contrib:0.116.0` 是坏的——只有两层、缺主 binary起容器报 `exec /otelcol-contrib: no such file or directory``docker rmi -f` 后重新下载 layer、digest 不变、还是坏。这种坏 `docker compose config``pull` 都发现不了。验法:`docker run --rm <img> --version`,输出版本号才算真好。坏了换邻近 tag0.116.0 坏、0.114.0 和 0.155.0 好0.115.0 mirror 直接 403
- **换镜像 tag 要顺带复核配置字段兼容**。上一条换 tag 时踩到:降到 collector 0.114.0 后redaction 处理器不认 `blocked_key_patterns` / `redact_all_types`(这俩是新版才加的字段),而脱敏是安全红线、砍了键名屏蔽不行,只能往上取 0.155.0(字段最全、向后兼容 0.116 配置)。换 tag 别只看 binary 起不起来,还要看这版本认不认你的配置字段。
部署落点与运维compose 源在仓 `deploy/infra/observability/`、部署位置 `mini-infra:/opt/infra-observability/``rsync` 同步(`.env` 排除、服务器上 chmod 600 不入仓),`docker compose up -d``HOST=100.64.0.8 bash health-check.sh` 五件全 200 = 绿。访问方式Grafana 口令/端口/MinIO 桶)见 [`docs/内网凭据与端点.md`](../../docs/内网凭据与端点.md) 观测栈段。ssh 连法同 §2`ssh -o ProxyCommand=none root@100.64.0.8`
## 7. 生成线cheap / tier2 Python service在 mini-desktop 的环境地图2026-07-05 波③ OTLP 真跑蒸馏,免重复探索)
生成线的 Python 服务(便宜档 cheap-worker、tier2 gen-worker跑在 mini-desktop布局与 huijing 后端两码事,摸底时几处岔路很吃时间。下面这些坐标都经波③ OTLP 真跑坐实,后续接生成线直接照用,别再从零翻找。
- **真检出 = `/root/games-development-ai/`dev/2.0.0),不是 `/root/tier2-run/`**。后者是一份陈旧检出(停在 `c0135926`、阶段二那批),里头同样有 cheap-worker/ 和 tier2/ 目录、极易误认,但它的 `.venv` 早已不在、跑不起来。认准 `/root/games-development-ai/` 这份。
- **cheap-worker 用自己的 venv`/root/games-development-ai/cheap-worker/.venv/bin/python`**uv 建,装了 agentscope + opentelemetry 的 SDK 与 OTLP/HTTP exportertier2 gen-worker 的 venv 在 `/root/.venvs/agentscope-tier2/``uv` 本体在 `/root/.local/bin/uv`。系统默认 `/usr/bin/python3`3.12)里 pip、agentscope、opentelemetry 一个都没有,别拿它跑生成线脚本。
- **这份检出的 cheap-worker/ 带本地未提交改动,别盲 `git pull` / `reset --hard` 覆盖**。mini-desktop 上 `cheap_service_app.py` / `cheap_studio.py` / `cheap_service_driver.py` / `cheap_verify.py` 有本地补丁(含 `m3_stream_patch.py` 的 M3 流式补丁),处于分叉状态。更新单个文件用 `scp -o ProxyCommand=none <file> root@100.64.0.7:<path>`(单文件传输分类器不拦,见 §2波③ 部署 `cheap_otlp_sink.py` 即用此法),或先 `git stash` 核对再动;直接覆盖会抹掉这些没进仓的补丁。
- **生成线端口**:便宜档 AgentScope Service :8300、便宜档 worker HTTP :9501、tier2 gen-worker :8200。用上面的 venv python 跑对应入口(`cheap_service_app.py` / `worker_service.py`)起服务,门参与预算从 Nacos100.64.0.8:8848、namespace 为空串)热读。
- **Python 侧跨机 / 出网必须绕本机 clash fake-ip198.18.x**发往内网100.64.0.8 的 Collector :4318、new-api :3000 等)前设 `NO_PROXY=100.64.0.8,localhost,127.0.0.1` 并清 `http_proxy` / `https_proxy`curl 用 `--noproxy '*'`)。便宜档 OTLP sink 已在建 exporter 前调 `worker.client.install_proxy_bypass` 幂等兜一层。跨机可达实证mini-desktop → mini-infra:4318 返 200。
- **波③ OTLP span 真跑配方**:设 `CHEAP_OTLP_ENDPOINT=http://100.64.0.8:4318`,用 cheap venv python 跑一局便宜档生成(或直接把 TraceStep 喂给 `cheap_otlp_sink.make_cheap_otlp_sink`span 会以 `service.name=cheap-gen-worker` 落 Tempo。查 Tempo 要**等 ≥15s**Collector tail_sampling `decision_wait=10s` 叠 Tempo ingest`curl -s --noproxy '*' -G http://100.64.0.8:3200/api/search --data-urlencode 'q={ resource.service.name="cheap-gen-worker" }'`。注意采样口径——只有 error 状态的 span 必被采tail_sampling status_code 策略),正常 span 走 10% 概率采样、查不到是正常现象,真跑验证要用带 error verdict 的 step 才稳。
- **观测栈自身还没被观测**Prometheus 暂未 scrape Collector 的内部 telemetry容器内 :8888`otelcol_receiver_accepted_spans_total` 一类管道计数器在 Prometheus 里查不到(留波⑤ 补 collector self-scrape。要判断 span 到没到,直接查 Tempo别指望这些计数器。
- **tier2 源工程落库接后端W-ASSET-SRC T1 接线2026-07-05**:让 tier2 富游戏的 src/ 产物从落本地盘改落 MySQL+MinIO 的开关 = tier2 gen-worker 起服时的环境变量 `TIER2_STORE=backend``worker/store.py``default_store()` 唯一读它,不设=回落 `LocalFsStore` 本地盘)。它落在 tier2 服务起栈 env 单点(与上面 :8200 venv 入口同处设,别在别处硬编码 `BackendStore` 类)。翻开关前先核两件底账,免一开就撞空库:① tier2 逻辑库要先在真 MySQL 建好,`CREATE DATABASE IF NOT EXISTS tier2`(对齐 `infra.yaml` `mysql.database=tier2`);② `tier2_source_project_version` 表的 status 列状态要确认——`BackendStore._ensure_schema` 首次 save 会 `CREATE TABLE IF NOT EXISTS` 并按 `information_schema` 探测缺列补跑 `ALTER ADD COLUMN status`,但若历史窗口已把这张表建成无 status 列的旧形态,确认它被 ALTER 补上(或确认无存量数据时 DROP 重建),处置记进接线日志。翻开关后先真跑一次「生成一款富游戏 → 收口落库」,确认日志出现 `BackendStore.save 落库成功(committed)` 而非 `LocalFsStore` 的落库行。回滚 = 起服 env 去掉该开关即整体回落 `LocalFsStore`,一处可逆、无数据迁移。取回重建的五条闭环由 `scripts/store_roundtrip_harness.py --backend` 在此真基建上验(本机 worktree 无真库 / 无 esbuild跑不了 bundle 字节相等,属本窗口 follow-up
- **tier2 孤儿清扫 jobW-ASSET-SRC T4**manifest-first 把半落库残片变成可见的 pending 行后,超时未收尾的 pending 行连同其 MinIO 前缀对象由 `scripts/store_cleanup_job.py` 回收(乐观删 CAS 防与续 put 的 TOCTOU 竞态、幂等)。在此机用 tier2 同套 venv + `TIER2_STORE=backend`cron / systemd-timer 定时跑即可(如每小时 `--ttl 3600`owner = 生成线tier2。回收正确性已由 `tests/test_store_backend_manifest_first.py` 的内存 fake 用例坐实,真库真跑排本窗口。
## 8. 后端 → 生成线派发接线executor·内测 dev 闭环命脉2026-07-05 真 e2e 坐实)
huijing 后端把生成请求投给 Python 生成线,靠 `aigc.executor` 这组配置——它是内测核心闭环的命脉,却默认关闭。`aigc.executor.enabled` 缺省 false起栈时一旦漏注 `AIGC_EXECUTOR_*`executor 整组 Bean`WorkerDispatchClient` / `GenTaskConsumer` / `@Scheduled` tick`@ConditionalOnProperty` 挡在装配外生成请求入队成功却无人分派、永远跑不起来、且不报错。2026-07-05 起 dev 全栈正是栽在这:只注了 MYSQL/REDIS/NACOS 三口令、漏了 executor便宜档 create→generate 静默卡在 queued。
派发链路:`submitGenerate` 落库 queued → `GenTaskProducer` 发 RocketMQ → `GenTaskConsumer` CAS 认领 → `WorkerDispatchClient` POST job 到 `worker-url` → Python worker 生成 → 回调 `/dify/callback-internal` → 落版本 + 运行包;`@Scheduled` tick 兜底轮询 DB。
dev 正确配置固化在 `/root/huijing-dev.env`chmod 600、单一事实源`/root/restart-huijing-dev.sh` 从它 `systemd-run` 重建 transient unit `huijing-dev`。关键项:`AIGC_EXECUTOR_ENABLED=true`(装配整组,默认 false = 不分派)、`AIGC_WORKER_URL=http://127.0.0.1:9501/generate`(逐字节对齐 worker 的 `DEFAULT_PORT=9501` + POST `/generate`)、`AIGC_CALLBACK_SECRET` 非空 32hexComplete A 起已配、值见凭据档worker 起服带 `--callback-secret <同值>`,两端逐字相同 = HMAC 验签开;空=验签关=内网可伪造回调,内测必须非空)、`AIGC_NEWAPI_QUOTA_ENABLED=true`WU2 预置池 per-user 计费)、`NEWAPI_KEY` = api-key值见凭据档全局兜底/MiniMax 计费dispatcher 默认 `http`= `WorkerDispatchClient`,别开 `saa` 进程内旧路、dispatchTemplateId 默认 `generic`(见下)。
**分派闸是硬相等 `templateId=='generic'`**`AigcGenerateExecutor.java:535-536`):只有 templateId 恰等 `generic` 才投 worker其余business-sim 等旧模板名)一律 fall-through 到进程内 LLM 旧路、到不了 :9501。便宜档真实流一句话、不选模板= 草稿空 templateId → 后端归一 `generic` → 派 worker品类由 Python 层按 brief 文本判。**冒烟 / e2e 必须用 generic别用 business-sim**——它虽在 `SUPPORTED_TEMPLATE_IDS` 提交校验放行,却与分派闸解耦、会静默走旧路。
**陈旧 `/root/game-staging/infra/.env` 别照搬**:那套 `AIGC_*` 是 gamedef 时代的值(`:9401` / `dispatcher=saa` / `sourcemode=gamedef` / gameruntimedir 指退役目录),与 M3 cutover 后的便宜档 Python 线不符,照搬即错。
交付边界:生成成功落的是 status=0 预览包(如 versionId 93151、bundle 243KBfeed 不显示;需运营在审核台 APPROVEstatus 0→1才进 feed。这是生成线与产品 / 审核轨的交界cutover plan §2.2),不是 bug。可逆`/root/huijing-dev.env``AIGC_EXECUTOR_ENABLED` 改回 false 重跑 restart 脚本,即回到「入队不分派」的现行行为,一处开关、无数据迁移。
## 9. OTel Java agent 挂载阶段四观测·面一b2026-07-05 dev 真机坐实)
给 huijing 后端挂 OpenTelemetry Java agent = 一个参数拿全链 traceSpring MVC/JDBC/Redis/出网 HTTP 自动织入 span且出网时自动注入 traceparent顺带兑现面四的 Java→worker 那跳。dev 落法**不改 restart 脚本硬编码的 java 行**,靠 `JAVA_TOOL_OPTIONS` 注入:
- agent jar = `/root/otel/opentelemetry-javaagent.jar`2.29.024MB。外网从 aliyun maven 镜像 `--noproxy '*'` 直连下:`maven.aliyun.com/repository/public/io/opentelemetry/javaagent/opentelemetry-javaagent/2.29.0/opentelemetry-javaagent-2.29.0.jar`dev 的 `~/.m2/settings.xml` 镜像 central→aliyun直连可达
- `/root/huijing-dev.env` 追加一块(单一事实源,**可逆=删块重启即回退**`JAVA_TOOL_OPTIONS=-javaagent:/root/otel/opentelemetry-javaagent.jar` + `OTEL_SERVICE_NAME=huijing-server` + `OTEL_EXPORTER_OTLP_ENDPOINT=http://100.64.0.8:4318`mini-infra Collector在 no_proxy 内直连)+ `OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf` + `OTEL_METRICS_EXPORTER=none` + `OTEL_LOGS_EXPORTER=none`metrics 归面五 micrometer/prometheus、logs 后置agent 只发 traces 免重复)+ `OTEL_TRACES_SAMPLER=parentbased_always_on`dev 全采,尾采样交 Collector
- 验:重启后启动日志 `Picked up JAVA_TOOL_OPTIONS` + `opentelemetry-javaagent - version: 2.29.0`**启动加约 10s**(织入开销,可接受);发几个请求后 Tempo `GET /api/search/tag/service.name/values``huijing-server``GET /api/search?tags=service.name%3Dhuijing-server` 出真 tracePING/POST span。旁路风险兜底只读冒烟 12/12 全过、agent 无 OTLP 导出错误。
坑与注记:① 指标是进程内 micrometer**重启即清零**——Prometheus 抓的时序在 restart 处有 counter reset`rate()`/`increase()` 天然处理;**单次 scrape 看不到累计值不是 bug**排「gen_task_total 缺失」先想是不是刚重启过)。② watchdog 超时收尸stale RUNNING > 15min→failed(timeout)**确经 handleCallback 唯一写入路径**→面五埋点会计 `timed_out`dev 实证 task206 走此路);迟到的重复 worker 回调在 `handleCallbackTx` 抛 AIGC_CALLBACK_TASK_FINAL 拒绝重入、**先于** postCommitBestEffort故不重计。③ **便宜档需续修的 gen 延迟可达 20min**task206初生成 + H_progress 门未过续修 + 等远程 LLM → 最终 15min watchdog 阈值收尸 failed(timeout)gen_duration 埋点正为量化这条内测 UX 关注线。

View File

@ -1,57 +0,0 @@
---
name: tier2-service-generation
description: tier2 富游戏服务态生成(AgentScope create_app :8200 + 控制面有界 resume)的驱动配方与收敛诊断——含 6 个集成口径、收敛头号杠杆(play_spec driver)、九门/富游戏门读法
metadata:
type: skill
---
# tier2 服务态生成:驱动 create_app + 收敛诊断
tier2 富游戏线把"本地 CLI 直驱裸 Agent"(`worker/agent_loop/studio.py``run_studio`)升级成经 AgentScope 2.0.2 `create_app` 的 Agent Service:多租户、多会话、REST 触发(`POST /chat`,fire-and-forget)、SSE 事件流,session 状态与跨会话 MessageBus 走 Redis。服务跑在 `mini-desktop:8200`,由一个**控制面**(`service/control_plane.py`)从消费方侧驱动一款游戏从启动到门绿落库。
这份手册记录两件在 2026-06-24 服务化实测里用真证据换来的东西:**怎么正确驱动这个服务**(六个集成口径,每一个都是一次 422 / 崩溃换来的),以及**一个差点被误判成"模型太弱"的收敛假象的根因**。
## 收敛头号杠杆:真玩 driver,不是生成质量
服务态首批真跑(cp-smoke-006/007)都跑到 `budget_exhausted` 不收敛,表面看像"M3 单写产不出能过富游戏门的游戏"。**这是假象。** 拆开 verdict 看,九门里 `A_boot / B_uncaught / C_frame / D_render / E_live` 全绿——游戏能启动、能渲染、有活的状态;只有 `F_wiring` 挂,报 `calls=0 expected=[]`,而富游戏三门(tripleLink / economy / latch)的子检查全是 `None`
关键区分:**`None` 不是 `False`**。`None` 意味着这些检查**根本没法评估**,而不是评估后判失败。富游戏门要靠 CDP harness 真玩游戏、观测它在玩的过程里有没有真调 `resource.addCoins` / `resource.consumeIngredient`、经济线能不能走到赢/破产、终态有没有 latch。没有真玩,这些观测一个都拿不到,于是全 `None`,门挂。
根因是控制面给 `run_gates` 传了 `play_spec=None`。play_spec 里装的是**真玩驱动规格**——driver(怎么玩)、`expectedEngineCallPrefixes`(该观测到哪些引擎调用)、`assertAfterPlay`(玩完该满足的状态断言)、economy 阈值。没有它,harness 把游戏渲出来就停了,从不驱动它玩。游戏永远不被玩 → 没有引擎调用 → F_wiring 挂、富游戏门 `None`。更糟的是,生成 agent 自己的 `run_gates` 工具也吃同一个 `None`,于是 agent 看到 `decision=fix` 却在"修一个根本不存在的问题",反复 thrash 直到预算耗尽。
**铁证**:把 007 那个"失败"的现成游戏,配上标准 business-sim driver 重跑门,直接 `decision=accept`,九门 + 富游戏三门全绿。游戏一直是好的。
**修复**(`worker/run.py``run_gates`):play_spec 缺 driver 时 `setdefault` 一份 `DEFAULT_BUSINESS_SIM_PLAY_SPEC`,显式 play_spec 仍优先。这份默认规格对**任何契约合规的 business-sim 游戏通用**——因为 driver 是参数化的(`{type: "business-sim", steps, stepMs, winThreshold, bankruptSteps, streakLose}`),按契约驱动而非靠逐像素坐标;bakery(feie-005)和 coffee(007)两款不同的游戏用同一份 driver 都过门。一处修复同时惠及生成 agent 自己的 `run_gates` 工具(thrash 根)和控制面评门。
**契约**:游戏要可被这份通用 driver 真玩,必须暴露 `resource.addCoins` / `resource.consumeIngredient` 这些 call-ID(经 `recHook`)、把终态 phase latch 成 `win`/`lose`/`gameover` 让宿主轮询、并在 scene 里接 `bindInput` 把注入的输入路由到 core 命令。M3 在设计阶段引导下已经稳定产出这套契约——所以收敛的真问题从来不是"模型写不出富游戏",而是"有没有真玩它"。
诊断口诀:**九门前几道绿、F_wiring 挂、富游戏门 `None` → 先查 driver,别怪生成。**
## 驱动 create_app 的六个集成口径
`create_app` 不收 agent 实例,只收装配零件(`extra_agent_tools` / `extra_agent_middlewares` 工厂),agent 由框架在每个 chat 回合内部装配。控制面/bootstrap 经现成 REST 端点驱动它。下面每一条都是实测撞出来的,直接照用:
1. **凭据注册**(`POST /credential/`):请求体是 `{data: {...}}`,凭据载荷整体嵌在 `data` 下,不是扁平字段。Anthropic 凭据的 `data = {type: "anthropic_credential", api_key, base_url}`——**没有 `model` 字段**(模型名在 session 的 `chat_model_config.model`)。模型类由 `get_model``CredentialFactory.from_dict(data).get_chat_model_class()``data.type` 决定,所以 `data.type` 必须对。
2. **chat 输入**(`POST /chat/`):`input` 是一条 `Msg`,它的 `content` **必须是内容块列表**,不能是裸字符串(裸串返 422 `Input should be a valid list`)。发 `{name, role, content: [{type: "text", text: "..."}]}`。Python 端 `UserMsg(content=str)` 的字符串自动包块只发生在构造器里,经 REST 传 JSON 不走那条。
3. **九工具抠取**(`extra_agent_tools` 工厂):2.0.2 的 `Toolkit` 把注册的工具按组存在 `self.tool_groups`,每组的 `.tools``list[ToolBase]`;构造器传入的 `tools` 被包进默认 `"basic"` 组(不是扁平的 `.tools` 属性)。要把九工具交给框架,从 `toolkit.tool_groups[*].tools` 收集。按扁平 `tools/_tools/function_tools` 探测会全空,每个 chat run 起手即抛、服务态生成零产出。
4. **BYPASS 权限**(建 session 后):tier2 九工具的 `FunctionTool` 默认 `check_permissions` 返回 `ASK`,服务态无人确认。不设 BYPASS,agent 第一次调工具就进 `ToolCallState.ASKING`、等一个永不来的确认事件,chat run 抛 `Agent is waiting for N tool calls ... but received no event`,生成零产出。修法:`PATCH /sessions/{id}?agent_id=<id>``{permission_mode: "bypass"}`(`PermissionMode.BYPASS` 值是 `"bypass"`)。注意 **PATCH 与 SSE stream 一样要 query 参数 `agent_id`**,不带返 422。等价 CLI `studio.py``_bypass_state()`
5. **设计阶段接进服务路径**(建 agent 前):CLI 线 `run_studio` 先跑工作室设计阶段产出 design_text 再写,服务路径此前整段跳过、writer 拿空 design。修法:bootstrap 在建 agent 前于控制面进程内(有 agentscope + 模型)复用 `studio._design_stage`(工作室星形多 agent 团队 + 失败 degrade 回单 agent)产出 design_text,喂进 `AgentRecord.system_prompt`(`roles.writer_system` 第二参)。best-effort:设计失败返空串、不阻断生成。设计团队墙钟超时现为 420s(2026-07-04 由 240s 上调);240s 时代偏短、常 degrade 回单 agent,上调 420 后已缓解——即便 degrade 仍产出 design_text 可用。要调超时改 `tier2/config/generation.yaml``design_team.timeout_s`(配置外置已兑现)。
6. **SSE 等待必须有界**(控制面消费 SSE):`create_app` 的 chat 是每回合一次、fire-and-forget,控制面订阅 `GET /sessions/{id}/stream?agent_id=<id>` 读到本回合的 `REPLY_END` / `EXCEED_MAX_ITERS` 才算这轮跑完。**坑**:超时检查不能只写在 `async for line` 的循环体内——SSE 流完全静默时(如服务端 Redis 超时打断了事件发布,既无 data 也无心跳)`aiter_lines` 永久 await,没 line 进来超时检查永不触发,控制面无限挂(实测卡死 60min)。修法:httpx `Timeout(read=idle_timeout_s)`,静默超它即 `ReadTimeout` → 兜住 → 据 on-disk 评门继续。`idle_timeout_s=300s` 远大于 agent 内部 run_gates(chrome ~60s)的正常静默间隙,不误杀正常回合。
## 控制面有界 resume(B1)
`service/control_plane.py``drive_generation` 把 CLI `run_studio:404-458` 的外层有界 resume 搬到服务消费方:`bootstrap.start_new_game` 启动 → 外层 `for attempt in range(max_resumes+1)`:消费 SSE 等本回合结束 → **独立**跑 `run.run_gates(session_id, play_spec)` 机器判门(不靠内存 session——服务态九工具每回合工厂新建 `Tier2Session`,内存态跨回合不保留)→ 门绿则据 on-disk 源工程重建七要素 + `persist_source_project` 落库 break / 否则 `verdict_feedback` 包成续跑指令 `resume_session` 续修 / 预算耗尽停。评门的 `game_id` 必须用框架分配的 `session_id`(九工具据它管工程目录 `game-runtime/games/_tier2-gen/<session_id>`)。门是机器判的,控制面绝不自评翻绿。
## 运维与诊断
- 起服务(mini-desktop):`cd /root/tier2-run/tier2/gen-worker && NEWAPI_KEY=... NO_PROXY=100.64.0.8 setsid /root/.venvs/agentscope-tier2/bin/python -m service.app >log 2>&1 </dev/null &`,单独只读 ssh 轮询 `/openapi.json`=200 确认就绪。改了 `app.py` / `worker/*`(服务端按回合 import + 缓存)要重启服务才生效;改 `control_plane.py` / `bootstrap.py`(控制面客户端、每次新进程)不必重启,同步 tier2-run 即可。
- 真玩门(`run_gates` / chrome / esbuild)只在 mini-desktop;6c6g 禁 chrome、只做 py_compile + 裸 import 静态校验。重依赖(agentscope / redis / httpx / pymysql / minio / uvicorn)一律函数体内惰性 import,顶层零重依赖,保证 6c6g 能 import。
- C1 轻量观测:控制面把逐帧 SSE 的结构性事件(跳过 `*_DELTA` 逐 token 噪声)记成 per-run `control-plane-trace.jsonl`,零外部依赖。事件字段口径(2.0.2):`TOOL_CALL_START``tool_call_name` + `tool_call_id`,`TOOL_RESULT_END``tool_call_id` + `state`。要在 workdir 建好(scaffold 之后)才落盘,否则刷 `FileNotFoundError`
- 端点 / 凭据 / 起服务铁律见 [`docs/内网凭据与端点.md`](../../docs/内网凭据与端点.md) 的「tier2 P4 服务化部署拓扑」段。
配套:AgentScope 2.0 架构事实见 [`../knowledge/agentscope-2.0-facts.md`](../knowledge/agentscope-2.0-facts.md);CLI 单写主链见 [`agentic-amodel-generation.md`](agentic-amodel-generation.md)(A-model/wg1 线,与本 tier2 服务态线同源不同栈);SAA 编排见 [`saa-graph-orchestration.md`](saa-graph-orchestration.md)。

View File

@ -1,189 +0,0 @@
---
name: trpg-game-design
description: "当为掷骰冒险(TRPG)类 brief 产设计阶段玩法方案或评审时使用:掷骰悬念等 6 引擎、爬塔/事件链可达范式、成功率与升级数值锚、单击即结算红线、通用 12 条+品类 5 条自检、金标锚点与底线四件映射。"
---
# skill: trpg-game-design —— TRPG(掷骰冒险)小游戏「玩法设计」作业手册(给生成 agent 的设计阶段)
> 定位:这是 **生成 agent 设计阶段造"好玩的掷骰冒险玩法设计"的唯一作业手册**。它回答 **"设计什么才好玩"**(掷骰判定/冒险结构/成长/数值/美术/音/UI 范式),与 [`littlejs-game-dev.md`](littlejs-game-dev.md)(回答"代码怎么写")配对:**设计阶段产玩法概念 → 代码生成阶段照 littlejs-game-dev 产代码**。体例照 [`sim-business-game-design.md`](sim-business-game-design.md)(经营品类范例)。
>
> **铁律(创始人 2026-06-21)**:"好玩 = 上游设计层职责",不在 runtime/插件。插件只让设计**能被 juice 出手感**;**这份 skill 负责让设计本身好玩**。
>
> **便宜档定位(钉死)**:TRPG 品类在便宜档取**单局可完的轻量形态**——数值冒险 / 掷骰抉择 / 队伍成长,一局 4590 秒出战报;**不做长流程重游戏**(跨局剧情存档 / 大地图探索 / 回合制菜单战斗都出界,见 §9 红线)。轻量 ≠ 简单:掷骰悬念 + 风险抉择 + 成长兑现叠起来,深度不输长流程。
>
> **可达性边界**:产出必须落得到 A-model 轻量运行时——**LittleJS 2D 手机竖屏(390×844)、单/少场景、便宜模型可生成的多文件代码、11 注入插件能力域**。设计再好,落不到这个运行时就是空想——见 §9 红线。
>
> **上位标准**:丰富分层/档位观测线对齐 [`docs/architecture/架构/生成引擎/游戏质量与爆火能力.md`](../../docs/architecture/架构/生成引擎/游戏质量与爆火能力.md)(质量模型 SoT);校验侧品类 rubric(喂 LLM 验证 agent、非阻塞)= `cheap-worker/fixtures/genre-rubrics/trpg.json`(经 cheap_verify.load_genre_checklist 加载;2026-07-02 rubric 挂点归一后 fixture 为唯一数据源),与本 skill §10 品类自检同源对齐。
---
## 0. 为什么这一品类爆(底层动机 —— 设计要击中这些)
掷骰冒险小游戏的"爽"来自 6 个心理引擎,**好玩 = 同时点燃尽量多个**;无趣 = 一个都没点燃:
1. **掷骰悬念(心跳时刻)**:骰子转出来前的 0.5 秒是全品类最强的多巴胺点——**随机必须可见**(骰值、判定式、大成功闪光),黑箱出结果 = 悬念归零。
2. **抉择的掌控感**:随机之上必须有"我挑的"——同屏亮牌给玩家挑(高险高赏 vs 稳收薄利),输了怪骰子、赢了夸自己,这是 TRPG 与纯抽卡的分界。
3. **成长兑现**:等级/属性**真实进判定公式**(升级 → 掷骰 +1,面板写明),"我变强了"能在下一次掷骰里立刻验证。
4. **险中求胜的叙事感**:HP 压力 + 难度爬坡让每局自然长出故事("最后 2 血大成功翻盘登顶"),战报把故事讲出来。
5. **连胜滚雪球**:连续成功追加奖励,会挑卡的人滚雪球——技巧分,盲点的人吃不到。
6. **收集与纪录**:大成功次数/击败数/最高纪录/结局徽章,"再来一局刷新纪录"的回归钩子。
> **反"无趣"总纲**:无趣几乎总是因为——掷骰黑箱 / 没有取舍(点哪张都一样)/ 成长是摆设数字 / 难度平坦无终点 / 结算只有一个分数。§10 是落地清单。
---
## 1. 核心玩法范式(可达子集 —— design 优先从这里选)
| 范式 | 一句话 | 我们运行时怎么落(插件) |
|---|---|---|
| **掷骰爬塔**(骨架默认) | 每层翻遭遇卡(怪/宝箱/奇遇亮难度奖励)→ 点卡掷 d20 三档结算 → 清层登塔难度渐升 → 登顶/倒下/到点 | scene-fsm 三幕 + session-score 金币 + timer-scheduler 限时 + juice/audio 掷骰爽感 |
| **掷骰事件链** | 每步一个事件、23 个选项各标难度与代价,掷骰定走向,走到结局 | 同上;事件表 = core 数据 |
| **queue 试炼** | 一列试炼(力量/敏捷/智慧)排队来,选属性应对掷骰,过关累计荣誉 | session-score + hud-ui 属性面板 |
| **轻队伍成长** | 掷骰赢来的金币招募/升级伙伴(每伙伴给固定骰加成),伙伴栏可见 | core 数据 + hud-ui 伙伴栏;招募=单击 |
| **boss 终局** | 爬坡后终层 boss 高难对抗,大成功双倍演出 | juice.flash/burst + audio 'win' |
**组合配方(经验)**:一款轻量 TRPG = **1 个主循环(点卡掷骰)+ 1 个成长轴(等级/伙伴)+ 难度爬坡到终点 + 连胜/收集点缀**。别堆超过 2 个主机制(便宜模型 + 单屏扛不住,也稀释爽点)。
**掷骰判定的标准形**:`d20 + 加成 vs 难度` 三档——roll=20 大成功(奖励双倍)/ 总值≥难度 成功 / 否则失败(**失败也给安慰 1 金**,进展不断流;roll=1 恒失败保住"骰子的脾气")。这一形状骨架已实现,换皮别破坏。
---
## 2. 冒险 / 进度 / 留存设计
- **首 3 分钟(决定生死)**:① 010 秒:零阅读上手(菜单三行图例:⚔ 高赏失败扣血 / ◆ 稳收 / ✦ 险中带甜,点卡就掷)。② 1060 秒:**第一次升级**(XP 曲线保首局 23 次结算内升 Lv.2,骰+1 立刻可感)。③ 13 分钟:第二局——"差一层就登顶"的再来欲 + 纪录可破。
- **局内进度阶梯**:层数就是解锁阶梯——"第 N/M 层"常驻 HUD,任意时刻可见下一层;难度随层 +1,登顶 = 明确终点。**≥3 层起步(骨架 6 层)**。
- **留存钩子**:最高纪录(session-score best 跨局)· 战报统计可刷新(击败/开箱/大成功)· 可扩结局徽章/遭遇图鉴(save-progress 存)。
- **节奏**:掷骰(心跳)→ 结算(爽)→ 挑下一张(思考)的三拍循环,层间横幅是喘息;60 秒限时给全局张力。
---
## 3. 数值与掷骰概率(交 core.js 常量 —— design 必给具体锚)
- **成功率曲线**:首层基准难度 7(d20 裸掷 ≈70% 成功)——开局要爽;每层 +1 难度、每级 +1 加成,**玩家不升级则末层 ≈4550%、升 2 级则 ≈60%**:成长兑现写在概率里。别开局就 50% 以下(挫败劝退)。
- **风险-回报差异化(取舍之根)**:同层遭遇 diffMod 拉开 ≥3 点(如宝箱 -3 稳收 3 金 vs 祭坛 +2 险赏 8 金),期望收益险者略高——会算的人吃到技巧分。
- **HP 压力**:血量 ≈ 可承受 810 次失败(20 血/失败 -2),失败是代价不是墙;只有怪物卡打脸,宝箱/奇遇失败只是拿得少。
- **升级曲线**:XP 指数(×1.6/级)造"差一点升级"卡点;升级奖励 = 骰加成 +1(真进公式)+ 全屏庆祝。
- **连胜**:每连胜 +streak 金、cap 5(防滚过头);失败清零(心跳更疼)。
- **一局时长**:4590 秒(骨架 60s);限时到点 = 结算而非惩罚。
---
## 4. 美术风格范式(可达 + mmx 可产 + 程序化兜底)
| 风格 | 调性 | 配色 | 适合 |
|---|---|---|---|
| **暗地城+火把光**(骨架默认) | 神秘冒险 | 深紫蓝底 + 暖橙火光 + 金币金 | 魔塔/地牢 |
| 国风志怪 | 雅致奇幻 | 黛青 + 朱砂点缀 | 西游/山海经/武侠 |
| 星际废墟 | 冷冽科幻 | 深空黑 + 荧青 | 机甲/太空 |
| 羊皮纸桌游 | 复古桌面 | 米黄 + 棕红 | 骰子桌游感 |
- **卡面即信息**:遭遇卡 = 图腾色块 + 名字 + 类型标 + 难度(≥N)+ 奖励(+N金)一屏亮全——取舍信息就是美术布局。
- **美术一致性之根**:全套素材共用一组风格词(mmx 生成时);程序化兜底(几何图腾 + 统一调色)完全成立,**别因美术阻塞可玩性**。
---
## 5. UI 范式(hud-ui 全可达)
- **顶部 HUD**:金币(=score)/ 层数进度(第 N/M 层)/ 倒计时;
- **遭遇卡列**:纵排大卡(手指友好 ≥14% 屏高),每张亮名字/类型/难度/奖励;
- **勇者面板**:HP 条 + Lv + **骰加成(+N 写脸上)** + 经验条——成长可见;
- **掷骰飘字**:判定式全文飘出「d20:15+2 vs 12 ✓ +7金」,大成功大字金色——**过程可见是本品类 UI 的命脉**;
- **层间横幅**:「登上第 N 层!」仪式感一拍;
- **战报结算**:层数/击败/开箱/大成功/胜负因由/最高纪录 + 再闯按钮。
---
## 6. 音乐 / 音效范式(audio-music 可达)
- **音效(爽感命脉,务必有)**:掷骰"嗒" · 成功收币"叮"(coin)· 大成功欢呼(win)· 失败闷响(thud)· 升级号角(win)· 翻层提示(click)。**掷骰三拍循环每拍都要有声**——audio-music `playSfx`,无声卡静默降级不崩。
- **BGM**:地城氛围循环(低速鼓点+弦),高层可加急(可选)。
- 红线:**宁可程序化音效,也别没有反馈音**(无音 = 无趣的隐形元凶)。
---
## 7. 首屏 / 传播视觉钩子(通用,MVP 也用)
- 首屏 3 秒抓人:菜单即亮玩法三行图例 + 大按钮,点进去 10 秒内掷第一把;
- **战报即炫耀时刻**:一屏讲完这局冒险的故事(爬到第几层/击败几个/几次大成功/怎么结束的),可截图分享;
- 最高纪录挂脸上("金 97"),破纪录的再来欲;
- 大成功的全屏演出(闪光+粒子+欢呼)是短视频截段的天然素材。
---
## 8. 设计阶段输出配方(生成 agent 据 brief 产出这些 → 喂代码生成)
给定一句话 brief,设计阶段应产出一份**结构化玩法设计**,**逐项落到运行时**:
1. **核心循环**(一句话 + 30 秒脚本:玩家挑哪张卡→掷骰看什么→得什么→怎么变强);
2. **主机制 12 个**(从 §1 选;标注用哪些插件);
3. **遭遇表/事件表**(≥4 项、三种性质齐:高险高赏 / 稳收 / 险中带甜;各标 diffMod/gold/xp——这是题材身份的核心载体);
4. **数值骨架**(基准难度/坡度/血量/升级曲线/连胜 cap —— 按 §3 给具体值,交 core.js 常量);
5. **美术风格词**(一组,全资产共用)+ 配色(交 render.js 的 C 表);
6. **音效清单**(掷骰/成功/大成功/失败/升级/翻层)+ BGM 情绪;
7. **UI 布局**(HUD/卡列/勇者面板/飘字/战报,§5);
8. **首 3 分钟脚本**(上手→首升级→再来一局的钩子)。
> **MVP-first 铁律(关乎 code agent 能否收敛)**:设计**聚焦可玩核心** —— 点卡掷骰循环 + 1 成长轴 + 难度爬坡到终点 + 基础数值/音效,遭遇 ≤5 种起步。**多角色切换 / 装备栏 / 技能树 / 跨局剧情 / 多结局分支图 等一律标「后续·MVP 不做」**,别一稿堆满 —— 设计太满 → code agent 实现负担过重 → read-thrash 不收敛。**先出能玩的核心,再谈丰富。**
> 这份设计 = 代码生成阶段的"图纸";生成 agent 照 littlejs-game-dev 把它实现为多文件代码(从 `_template-trpg` 骨架换皮起步,别从零写)。**设计阶段把"好玩"想清楚,代码阶段只管正确实现。**
---
## 9. 可达性红线(落不到运行时的设计 = 废稿,必剔除/简化)
- ❌ **长流程重游戏**:跨局剧情存档推进 / 章节制大冒险 / 大地图自由探索 → 改单局可完(4590s)+ 跨局只留纪录/图鉴。
- ❌ **回合制菜单战斗**("攻击/防御/道具/逃跑"四键菜单):多步菜单流程拖慢单局节奏、casual 玩家上手门槛高 → ✅ **核心动作单击即结算**(点遭遇卡 = 自动掷骰+判定+结算一步完成,爽点在「挑哪张」不在按菜单)。
- ❌ **纯随机无抉择**(自动掷骰/只有一个按钮):没有"挑哪张"的掌控感 = 抽卡机不是 TRPG → 同屏 ≥2 张差异化亮牌。
- ❌ **掷骰黑箱**(点卡直接出结果不见骰):悬念归零 → 判定式飘字必须有。
- ❌ 联网组队 / PVP → 单机 + 本地最高纪录。
- ❌ 3D 骰子物理 / 大量逐帧动画 → 2D 程序化(飘字+粒子+闪光足够演出)。
- ❌ 超过 2 个主机制(战斗+锻造+钓鱼…)→ 聚焦点卡掷骰 + 1 成长轴。
- ✅ 留下的:掷骰对抗、亮牌取舍、等级/伙伴成长、难度爬坡、连胜、限时、纪录/图鉴 —— 全在 11 注入插件 + 单屏 + 便宜模型可生成范围内。
- **每次结算至少 +1 金币(失败安慰金)**:胜负都给正反馈,失败也不颗粒无收——保底反馈是 casual 上手缓冲,别让连败玩家零收获直接弃游。
---
## 10. 反"无趣"落地清单(design 自检,逐条必过)
**通用 12 条**(全品类同尺,校验侧 = `cheap_verify.RICHNESS_CHECKLIST`,L2×7/L3×3/L4×2;2026-07-03 增补第 12 条「核心操作非无脑」):即时反馈 / 可见成长 / 下一个解锁 / 30 秒爽点 / 数值滚雪球 / 情感锚 / 核心操作非无脑 / 放置回归 / 音反馈 / 首 3 分钟脚本 / 炫耀时刻 / 同款钩子——设计阶段照 [`sim-business-game-design.md`](sim-business-game-design.md) §10 过一遍,「核心操作非无脑」单独确认:每次主操作含真实决策或技巧含量、判错有真代价(TRPG 天然强项=挑卡取舍,别退化成点哪张都一样)。
**TRPG 品类自检 5 条**(校验侧同源 = `cheap-worker/fixtures/genre-rubrics/trpg.json`,喂 LLM 验证 agent、非阻塞、分母独立):
1. **[L2] 掷骰过程可见**:掷骰判定(骰值+加成 vs 难度)以飘字/动画亮给玩家?(正:飘「d20:15+2 vs 12 ✓」;反:点卡直接加钱、不见骰)
2. **[L2] 风险-回报取舍真实**:同屏遭遇难度与奖励差异化亮牌、挑卡决定影响期望收益?(正:骷髅卫 ≥9 +7金 vs 宝箱 ≥4 +3金;反:所有卡同难同赏点哪张都一样)
3. **[L2] 成长真实进判定公式**:等级/伙伴提升改变掷骰判定数值且 UI 可见?(正:升级→骰+1 写面板、进公式;反:等级只是显示数字、判定恒裸 d20)
4. **[L3] 难度爬坡 + 登顶终点**:难度随进度递升、有明确通关终点与进度可见?(正:第 N/6 层 HUD + 每层难度 +1 + 登顶 win;反:无限平难度刷分无终点)
5. **[L4] 冒险战报可炫耀**:结算屏讲这局冒险的故事(层数/击败/大成功/胜负因由)、可截图?(正:「爬到第 5 层·击败 7·大成功 2·勇者倒下」;反:只有一个总分数字)
> 命中越多越好玩;通用 12 条命中 ≤3 ≈ 能玩但无趣。**设计阶段就过这两张表,别等做出来才发现无趣。**
> 机检硬门 vs LLM 评分维度的分界照质量模型 SoT §4 规范五(v2 修订:play-spec 断言载体已随 W-AXIS-V2 退役,机械可判的归四门地板与测试员转写、程度评价归 LLM 评分,分界原则不变);上述品类条目全是 LLM 评分维度,**不落项目代码、不进机械门、不进脚手架**。
---
## 金标锚点(12 条口径基线 · 质量模型 SoT §4 规范三)
每品类需 ≥1 金标正例 + ≥1 薄反例锚住评分尺(rubric 文本或评分 prompt 变更须金标复验,单款分组小计漂移 >±1 即回退)。TRPG 的锚 2026-07-03 以 12 条口径实测(生产口径 genre=trpg,同输入复采样 n=3 取中位,入口 `cheap_verify.verify_richness`):
- **金标正例 = `game-runtime/games/_template-trpg` 骨架原样**(掷骰爬塔,独立过九门)。**通用 12 条 = 9/12**:L2 6/7 · L3 1/3 · L4 2/2;第 12 条「核心操作非无脑」3/3 命中(三档难度/奖励差异化亮牌,挑卡取舍 + 连胜维护 + 扣血硬扛都是真实决策,失败扣 2HP 有真代价);品类扩展 5/5(L2 3/3·L3 1/1·L4 1/1);前 11 条 = 8/11,精确复现旧锚通用 8/11。**TRPG 模板是富正例、不是薄反例**——黄金脚手架把掷骰/取舍/成长/爬坡/战报整套接好,换皮起步即富。
- **薄反例 = 合成黑箱点卡**(点卡直接加钱、无骰无取舍无成长无终点,照各条 negative 面同构):材料尚未入仓,**是本品类金标欠账**(与 `cheap-worker/fixtures/genre-rubrics/trpg.json` 的 _note 记的同一笔;红线是不臆造合成材料充数,待真材料入仓再补基线)。其历史锚(未真跑)= 通用 0/11 · 品类 0/5。
- **方差纪律**:M3 单发方差 ±2~4,取中位;品类段(5/5、三样全中)与 L4(2/2)跨样最稳,是回退判定最可靠的观测维;正例通用三样 9/9/8。
---
## 底线四件映射确认(质量模型 SoT §7 档位要求 —— 品类件逐条确认,TRPG 全可映射、无需申报替代轴)
| 底线件 | TRPG 映射 | 骨架落点 |
|---|---|---|
| 成长轴(数值或等级) | 等级 + 掷骰加成(真进判定公式) | core.applyXp/heroBonus + 勇者面板 |
| 解锁阶梯 ≥3 级(任意时刻可见下一个) | 塔层推进(6 层,HUD 常驻「第 N/6 层」,清层即解锁下一层) | core.TOWER_FLOORS + drawHud |
| 收集/装饰或等价第二动机 | 战报统计(击败/开箱/大成功)+ 最高纪录;可扩结局徽章/遭遇图鉴 | measures + sessionScore.getBest(扩:save) |
| 音反馈 | 掷骰/结算/升级/翻层四处 SFX | audioMusic.playSfx(coin/win/thud/click) |
---
## 相关
- 代码层实现 → [`littlejs-game-dev.md`](littlejs-game-dev.md)(11 注入插件 API / 结构 / 工厂契约)
- 品类脚手架(换皮起点)→ `game-runtime/games/_template-trpg/`(掷骰爬塔,独立过九门)
- 便宜模型生成 worker / 九门 → [`cheap-model-game-generation.md`](cheap-model-game-generation.md)
- 经营品类范例(本 skill 的打样来源)→ [`sim-business-game-design.md`](sim-business-game-design.md)

View File

@ -1,12 +1,7 @@
---
name: ui-walkthrough-cdp
description: "用户可见波次收口前在 mini-desktop 用 CDP 做真 UI 走查(studio 创作/试玩/发布、admin 审核台)时使用:逮编排器旁路掩盖的真后端缺陷,含 CDP 七坑、免登录 localStorage 注入只读走查、真实访问 origin 取证红线。"
---
# 真 UI 走查配方CDP on mini-desktop
> **为什么必须真 UI 走查**:编排器/批跑/curl 全绿 ≠ 真人 UI 能用。编排器旁路曾掩盖 2 个真后端发布缺陷(`currentVersionId` 生成后未回写 / `getZones` 空桩),**真 UI 走查是逮这类缺陷的唯一手段**HJ-FE-WALK-001。链路①创作→生成→预览与链路②发布→审核→入 feed均用本配方闭合。
> 工具已入仓 `git show 6d2f8789^:docs/agent-specs/2026-06-09-agent-loop-v1/orchestrator/`(已删,git 可查)`player_cdp.py`(游戏试玩 CDP含 BrowserPool/ `admin_walk.py`admin 最小 CDP 客户端new_target/send/evaljs/nav/shot/ `run_walk.sh`chrome 生命周期)/ `probe_bridge_channel.py`宿主↔iframe 双边信道探针)。
> 工具已入仓 `docs/agent-specs/2026-06-09-agent-loop-v1/orchestrator/``player_cdp.py`(游戏试玩 CDP含 BrowserPool/ `admin_walk.py`admin 最小 CDP 客户端new_target/send/evaljs/nav/shot/ `run_walk.sh`chrome 生命周期)/ `probe_bridge_channel.py`宿主↔iframe 双边信道探针)。
## 1. 大原则
@ -31,7 +26,6 @@ description: "用户可见波次收口前在 mini-desktop 用 CDP 做真 UI 走
- 入口 `:4173`;受控 textarea 填值 = **原型 setter + dispatch input 事件**(触发 Vue v-model直接赋值无效
- 点按钮用精确文案匹配防子串误中(`includes('满意')` 会误中「不满意,重做」);
- 截图 `cat` 管道拉回本地用 Read 查看;游戏试玩复用 `player_cdp.play()`(批跑必须 `continue_on_demo=False`)。
> 这批脚本(`player_cdp.py`/`admin_walk.py` 等)随 orchestrator/ 目录删除,仅存 git取回`git show 6d2f8789^:orchestrator/<脚本名>` 落盘后用);游戏画布试玩取证现行改用在树的 `game-runtime/games/_wg1-gen/_shared/play.cdp.cjs`(见 [`game-e2e-cdp-harness.md`](./game-e2e-cdp-harness.md))。
- **localStorage 注入 token 过 `requiresAuth` 走查免登录直达受保护页只读走查——2026-06-16 Lane E G0 实证**:只想**只读核实**一个 `meta.requiresAuth` 的页面(如创作页),不必走完整登录表单。先后端拿一枚有效 token如免短信登录端点 / `test1` token再用 CDP 在**目标 origin 下**注入 localStorage 后再导航:
```python
evaljs("localStorage.setItem('ACCESS_TOKEN', '<token>')") # key 名以前端约定为准(查 utils/auth);有的还需 refreshToken/tenantId
@ -43,10 +37,9 @@ description: "用户可见波次收口前在 mini-desktop 用 CDP 做真 UI 走
- 接 staging`game-admin/.env.local` 两行——`VITE_BASE_URL=http://<staging>:48080` + **`VITE_APP_WANXIANG_MOCK=false`(不关全是假数据,最易漏)**chrome 与 admin 同机时 BASE 可 localhost。
- 登录:租户「芋道源码」/ admin / admin123表单预填点登录即过无验证码
- 审核台:静态路由 `/wanxiang/review`(登录后侧栏「绘境运营→项目审核」恒在);「通过」= `POST /admin-api/project/review {decision:1}` → 同事务 publish
- 审核台:静态路由 `/wanxiang/review`(登录后侧栏「造梦运营→项目审核」恒在);「通过」= `POST /admin-api/project/review {decision:1}` → 同事务 publish
- 验收锚点:**UI 队列数变化 + DB 双实证**`game_project.status=4` + `game_feed_rank` 新行)。
## 5. 信道探针(回归排查常备)
宿主↔iframe postMessage 断裂用 `probe_bridge_channel.py` 取证:四锚点 + 判别实验(文档代际/iframe 重建监视/存活代理注入v-for ref 数组案host→game 全断)即由它锁凶。修复类波次收口前跑一轮防回归。
> 这批脚本(`probe_bridge_channel.py` 等)随 orchestrator/ 目录删除,仅存 git取回`git show 6d2f8789^:orchestrator/<脚本名>` 落盘后用);游戏画布试玩取证现行改用在树的 `game-runtime/games/_wg1-gen/_shared/play.cdp.cjs`(见 [`game-e2e-cdp-harness.md`](./game-e2e-cdp-harness.md))。

View File

@ -1,11 +1,6 @@
---
name: wave-close-checklist
description: "当波次收口、里程碑状态变化或裁定类提交(拍板/闸门判定/口径变更)时使用:按序执行总账→作战清单→回填→.agent→蒸馏→索引→编排入库→治理门的八步收口清单,收口铁律唯一可执行入口。"
---
# 波次收口检查单wave-close checklist
> **收口动作的唯一可执行清单**。总账铁律、作战清单回填铁律、`.agents/README.md` 维护规则、协议 §6 均指向本单——收口流程变更只改这里,不再四处散写。
> **收口动作的唯一可执行清单**。总账铁律、作战清单回填铁律、`.agents/README` 维护规则、协议 §6 均指向本单——收口流程变更只改这里,不再四处散写。
> 触发时机:①波次收口 ②里程碑状态变化 ③裁定类提交(拍板/闸门判定/口径变更)。
> 由来2026-06-11 harness 体检发现收口规则散落 4 处、靠记忆执行,总账表头/作战清单轮次/`.agent` 横幅/AGENTS.md §3 四处同时漂移——本单由此而来。
@ -13,21 +8,21 @@ description: "当波次收口、里程碑状态变化或裁定类提交(拍板/
- [ ] **1. 总账**:更新 `docs/mvp/MVP进度总账.md` 受影响段§0 现状 / §1 里程碑 / §2 模块矩阵 / §3 链路 / §5 backlog / §6 执行记录),**并同步表头「最近更新」日期 + 对账 commit**(表头漂移=本单诞生的直接诱因,勿再漏)。
- [ ] **2. 作战清单**`docs/mvp/MVP作战清单.md` 勾掉完成项并**移入完成史归档**(清单保持 ≤ 一屏,只留完成线 + 队列);**一轮完成线达成时,向创始人提请滚动重锁下一轮 §0**。
- [ ] **3. 回填检查(裁定类必做)**:拍板/闸门判定/口径变更须同步修订受影响的 SoT架构 6 域树对应档、tech-decisions、对外档等,以 [注册表](../../docs/architecture/README.md) 为准),或在提交说明显式声明「不回填 + 理由」。原作战清单回填铁律2026-06-10 HJ-AUDIT-001
- [ ] **3. 回填检查(裁定类必做)**:拍板/闸门判定/口径变更须同步修订受影响的蓝图档/蒸馏档/对外档Doc A/B/C、tech-decisions、glossary、BP 等),或在提交说明显式声明「不回填 + 理由」。原作战清单回填铁律2026-06-10 HJ-AUDIT-001
- [ ] **4. `.agent` 文件(仅结构变化时)**:新建模块、目录职责/边界变化时更新对应 `.agent`**纯进度推进不改 `.agent`**进度一律以总账为准——避免再造「Day-0 横幅」式陈旧)。
- [ ] **5. 蒸馏**:可复用产出回写 `.agents/`事实→knowledge / 红线→rules / 配方→skills / 流程→workflows先查重后新增**操作配方必须落 `.agents/skills/`,不得只存会话私有记忆**(私有记忆换会话/换 agent/换人即不可见)。
- **蒸馏门2026-06-17 文档体系重构,[`../rules/engineering-conventions.md`](../rules/engineering-conventions.md) §10.6**:本波若产生留痕(新 brainstorm/plan/report/dated spec**必须产出「蒸馏 diff」洞见提升进策展层 canonical/`.agents`)或显式声明「无可蒸馏+理由」**——杜绝留痕只堆不蒸馏致策展层陈旧(治「更新不及时」)。
- [ ] **6. 索引同步**`.agents/` 有结构性变更(增/删/改名)时,同步 `.agents/README.md` 清单 + `AGENTS.md` §5 导航表两处都要§5 曾漏更两个 skill
- [ ] **7. 编排资产入库**:本波用过的 Workflow 脚本 / 探针 / 一次性工具入库(对应 spec 目录或编排器目录),收口报告落 `docs/agent-specs/`;确不入库的,在收口报告声明「不入库 + 理由」。
- [ ] **8. 目录退役 + 分层 + 治理门2026-06-16 立,2026-07-02 接门)**:本波 `execution`/设计档收口后顶部状态横幅翻 `SHIPPED`(被推翻的翻 `SUPERSEDED→替代档`spike 代码/批跑证据/模型原始输出不入文档树(结论留 `*.md`,原始件 untracked 或移仓级 `spikes/`**更新 [`docs/agent-specs/_index.md`](../../docs/agent-specs/_index.md) 在飞板**(收口项移除,新在飞登记);**一题一活档§10.4**:同一任务设计档收敛成最小自洽集,被取代者同提交删git 即历史);留痕 frontmatter§10.6:`date/topic/status/superseded-by`,设计档另须 `sot-impact`G6。**最后跑门:`bash .agents/tools/docs-gate.sh` 必须全绿**(七检:品牌/canonical 对账/死链/入口卫生/留痕隔离/设计档申报/计划谱系 G7,§10.7)。**波内有 merge commit 时(pre-commit 不触发)另手跑对应条件门**:碰门件(play.cdp/tools.mjs/gate_judge/cheap_gates/gate-fixtures)= `node contracts/gate-fixtures/run.mjs`;碰 `contracts/play-loop/` = 其 `validate.py --suite`;碰品类 rubric/五品类 skill = `.agents/tools/rubric-sync-gate.py`;碰 `contracts/prompts/` = 其 `check_registry.py`(挂载清单=B8,`.githooks/pre-commit` 为准)
- [ ] **8. 目录退役 + 分层2026-06-16 目录治理新增,配套 [`../rules/engineering-conventions.md`](../rules/engineering-conventions.md) §10**:本波 `execution`/`edit-plan` 收口后顶部状态横幅翻 `SHIPPED`(被推翻的翻 `SUPERSEDED→替代档`**spike 代码 / 批跑证据 / 模型原始输出不入文档树**——结论留 `*.md` 或收口报告,原始件 untracked 或移仓级 `spikes/``__pycache__`/`node_modules` 已 gitignore`git add` 批量 json/png**更新 `docs/agent-specs/_index.md` 活地图**(活档清单 + supersede 链 + 归档指针)。目的:杜绝「打过时标不归档」式堆积(治理审计根因)。**+ 一题一活档§10.4**:同一任务设计档**收敛成最小自洽集**(典型 1 review + 1 execution被取代者**同提交**删/归档(演进就地修订、禁「一 pivot 一文件」、禁单独连推过期档)。**+ 留痕 frontmatter§10.6**新留痕文件brainstorms/plans/report头带 `date/topic/status/superseded-by`;留痕原地累积,前门(`AGENTS.md` §3.3)只给检索入口、不逐条挂
## 提交前 30 秒自检
```bash
# 治理门(七检,<5s,不绿不收口)
bash .agents/tools/docs-gate.sh
# 总账表头日期应 = 收口日
head -8 docs/mvp/MVP进度总账.md | grep 最近更新
# 作战清单不应残留已完成项(完成项应已移入 git 历史
# 作战清单不应残留已完成项(完成项应已移入归档
grep -c "✅" docs/mvp/MVP作战清单.md # 期望 ≈ 0
# 本波若新增留痕(brainstorms/plans/report),应有对应蒸馏 diff 或「无可蒸馏」声明(§10.6 蒸馏门)
git status --short docs/brainstorms docs/plans 2>/dev/null
```

View File

@ -16,10 +16,10 @@ const staleDocs = Array.isArray(A.staleDocs) ? A.staleDocs : []
// 现行真相基准(与 .agents/knowledge/tech-decisions.md + AGENTS §3 对齐;子代理仍须自读权威源核实)
const TRUTH =
'现行真相:生成主线=生成 agent 写真 src/ 多文件工程于插件库(HJ-GEN-001 终审 2026-06-12;gameDefinition 已废·A-model 直写真 src·旧"模板驱动/LLM 直出 GameConfig"已废) via new-api 网关(Dify/OpenGame 降远期·MVP 从不部署);' +
'正交两轴:AI 参与深度 Tier0/1/2 与引擎按表现复杂度选(轻-中=LittleJS 增强发行版+Runner v2,勿写13/15KB;最高=Phaser)是两根正交轴、引擎不是分档轴;自研 Canvas<15KB 已废;Cocos 只留 3D/渠道导出轴(人在环);' +
'模板=LittleJS 插件;游戏模板/填参线已废(W-CLEAN),玩法模板(品类框架·引导生成·非pre-built代码)未废=有效待建;agentic 框架 2026-06-25 reframe 统一收敛 AgentScope(三档共用一个 agent 框架),SAA(Spring AI Alibaba v1.1.2.2)/dify/coze 降最低优先级、留作远期适配验证;' +
'美术/音乐=mmx-cli,ComfyUI 退备选;Nacos(配置中心+服务发现)/RocketMQ(异步 gen 队列)/Sentinel(准入侧流控)=MVP 生产基建三件套·已自托管 mini-infra(2026-07-01 build-vs-buy 反转旧"未部署"口径,勿再标 future-state);命名空间=com.wanxiang.huijing;品牌=绘境AI'
'现行真相:生成主线=agent 写码于插件库(HJ-GEN-001 终审 2026-06-12;旧"模板驱动/LLM 直出 GameConfig"已废) via new-api 网关(Dify/OpenGame 降 long-term·MVP 从不部署);' +
'引擎=LittleJS 增强发行版(55KB gz,勿写13/15KB)+Runner v2(game-host.d.ts),自研 Canvas<15KB 已废;' +
'模板=LittleJS 插件;游戏模板/填参线已废(W-CLEAN),玩法模板(品类框架·引导生成·非pre-built代码)未废=有效待建但非最高优先级(最高=Tier0生成可靠,玩法模板排其后;HJ-DEMO-AUDIT-001 创始人06-17);agentic=SAA(Spring AI Alibaba GA v1.1.2.2)裸图(HJ-AGI-002),AgentScope 降 long-term;' +
'美术/音乐=mmx-cli,ComfyUI 退备选;RocketMQ/Nacos=框架自带但 MVP 未部署=future-state(勿删);命名空间=com.wanxiang.huijing;品牌=绘境AI'
const CLS = {
type: 'object',

View File

@ -86,14 +86,10 @@ case "$cmd" in
# 同步修引用: agent-specs/<已归档名> -> agent-specs/_archive/<名>(字面替换,幂等)
python3 - "$ROOT" <<'PY'
import os,sys,glob
root=sys.argv[1]
# Claude auto-memory 目录按用户/机器不同,不硬编码:从环境变量 CLAUDE_MEMORY_DIR 读,缺省则跳过该段(只修 .agents/docs 内引用)
mem=os.environ.get("CLAUDE_MEMORY_DIR","")
root=sys.argv[1]; mem="/root/.claude/projects/-root-games-development-ai/memory"
arch=[os.path.basename(p) for p in glob.glob(root+"/docs/agent-specs/_archive/*.md")]
tgt=set()
bases=[root+"/.agents",root+"/docs"]
if mem and os.path.isdir(mem): bases.append(mem)
for base in bases:
for base in [root+"/.agents",root+"/docs",mem]:
for dp,_,fns in os.walk(base):
if "/_archive" in dp: continue
for fn in fns:

View File

@ -1,320 +0,0 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""文档治理门(engineering-conventions §10.7 的可执行实现 · 2026-07-02 立)
七检,任一失败退出码非零;每检打印通过/命中明细:
G1 品牌不变量 活层禁退役品牌根(白名单:docs/ip_archive_recall日期留痕档行内 brand-ok )
G2 canonical `canonical: true` topic 全仓唯一,且与 docs/architecture/README.md 注册表双向对账
G3 死链 活层 md []() 相对链接与反引号内 docs/·.agents/ 仓内路径必须存在(git show 定位放行)
G4 入口卫生 AGENTS.md git clone / 全局安装 / 个人绝对路径 / localhost
G5 留痕隔离 策展层(AGENTS.mddocs/architecture.agents)不得链接非 canonical 的留痕档(plans/brainstorms/dated specs;_index.md canonical 目标豁免)
G6 设计档申报 docs/agent-specs/*-设计.md 必带 frontmatter topic/status/sot-impact
G7 计划谱系 新建 plan/设计档必带 frontmatter `上级:`(仓根相对路径单指针),沿链 6 跳内达 canonical SoT;死链/断链/成环即挡(engineering-conventions §10.8)
用法:python3 .agents/tools/docs-gate.py [仓库根,缺省=脚本上两级]
挂载:.githooks/pre-commit(git config core.hooksPath .githooks).gitea/workflows/docs-gate.ymlwave-close 8
门③(doccode 兑现断言)不在本脚本:归生成主线 harness,规格见 engineering-conventions §10.7
"""
import os
import re
import sys
# 退役品牌根的唯一字面量(活层散文一律用「退役品牌名」间接表述,勿再写出字面量)
BRAND_RETIRED = "造梦"
# G1 品牌不变量的代码扫描面(W-REAL R4 补:防品牌回流到模块根 package.json / index.html 与 wg1/ 等代码——
# 评审曾指出门只扫活层 md 会放过玩家/管理台可见的代码字面量)。业务代码目录 + 相关源码后缀;
# .agents 不入(门自身源码持有 BRAND_RETIRED 字面量,扫它会自噬)、docs 由 walk_md 覆盖。
BRAND_CODE_BASES = ["game-runtime", "game-studio", "game-admin", "wg1", "tier2",
"cheap-worker", "game-cloud", "contracts", "deploy", "spikes"]
BRAND_CODE_EXTS = (".json", ".html", ".ts", ".js", ".mjs", ".cjs", ".vue",
".py", ".java", ".yaml", ".yml")
# 依赖/构建产物/归档目录原地裁剪(既防误报也快)。
BRAND_PRUNE_DIRS = {"node_modules", "target", "dist", "build", ".git",
"_archive", "_recall", ".venv", "__pycache__"}
ROOT = os.path.abspath(sys.argv[1]) if len(sys.argv) > 1 else os.path.abspath(
os.path.join(os.path.dirname(__file__), "..", ".."))
DATED = re.compile(r"^\d{4}-\d{2}-\d{2}-")
MD_LINK = re.compile(r"\[[^\]]*\]\(([^)]+)\)")
BACKTICK = re.compile(r"`([^`\n]+)`")
FAILS = []
def rel(p):
return os.path.relpath(p, ROOT)
def walk_md(bases, exclude_archives=True):
out = []
for base in bases:
top = os.path.join(ROOT, base)
if os.path.isfile(top):
out.append(top)
continue
for dp, dns, fs in os.walk(top):
dp_rel = rel(dp) + "/"
if "node_modules" in dp_rel or "/target/" in dp_rel:
continue
if exclude_archives and ("_archive" in dp_rel or "_recall" in dp_rel):
continue
if dp_rel.startswith("docs/ip/"):
continue
for fn in fs:
if fn.endswith(".md"):
out.append(os.path.join(dp, fn))
return out
def walk_code(bases, exts):
"""遍历业务代码目录里指定后缀的源文件(供 G1 品牌扫描面防回流用;prune 掉依赖/构建/归档目录)。"""
out = []
for base in bases:
top = os.path.join(ROOT, base)
if not os.path.isdir(top):
continue
for dp, dns, fs in os.walk(top):
dns[:] = [d for d in dns if d not in BRAND_PRUNE_DIRS] # 原地裁剪不进依赖/构建目录
for fn in fs:
if fn.endswith(exts): # str.endswith 接受后缀元组
out.append(os.path.join(dp, fn))
return out
def read(fp):
try:
with open(fp, encoding="utf-8", errors="ignore") as f:
return f.read()
except OSError:
return ""
def frontmatter(fp):
"""返回 frontmatter 字段 dict(浅解析,只认顶层 `key: value` 行)。"""
lines = read(fp).splitlines()
if not lines or lines[0].strip() != "---":
return {}
fm = {}
for line in lines[1:60]:
if line.strip() == "---":
break
m = re.match(r"^([A-Za-z一-鿿][\w\-一-鿿]*):\s*(.*)$", line)
if m:
fm[m.group(1)] = m.group(2).split("#")[0].strip()
return fm
def check(name, problems):
if problems:
FAILS.append(name)
print(f"{name}: {len(problems)}")
for p in problems:
print(f" {p}")
else:
print(f"{name}")
# ---------- G1 品牌不变量 ----------
def g1():
problems = []
# ① 活层策展 md(AGENTS.md / docs / .agents):日期留痕档豁免。
for fp in walk_md(["AGENTS.md", "docs", ".agents"]):
if DATED.match(os.path.basename(fp)):
continue
for i, line in enumerate(read(fp).splitlines(), 1):
if BRAND_RETIRED in line and "brand-ok" not in line:
problems.append(f"{rel(fp)}:{i} {line.strip()[:60]}")
# ② 业务代码目录源文件(W-REAL R4 补扫描面):模块根 package.json / index.html 与 wg1/ 等代码防品牌回流。
for fp in walk_code(BRAND_CODE_BASES, BRAND_CODE_EXTS):
for i, line in enumerate(read(fp).splitlines(), 1):
if BRAND_RETIRED in line and "brand-ok" not in line:
problems.append(f"{rel(fp)}:{i} {line.strip()[:60]}")
check("G1 品牌不变量(活层 md + 业务代码无退役品牌根)", problems)
# ---------- G2 canonical 唯一性 + 注册表对账 ----------
def g2():
problems = []
canon = {} # topic -> [paths]
for fp in walk_md(["docs", ".agents"], exclude_archives=False):
fm = frontmatter(fp)
if fm.get("canonical") == "true":
if "_archive" in rel(fp) or "_recall" in rel(fp):
problems.append(f"归档层不得标 canonical:{rel(fp)}")
continue
topic = fm.get("topic", "")
if not topic:
problems.append(f"canonical 缺 topic:{rel(fp)}")
continue
canon.setdefault(topic, []).append(rel(fp))
for topic, paths in sorted(canon.items()):
if len(paths) > 1:
problems.append(f"topic「{topic}」有 {len(paths)} 份 canonical:{' | '.join(paths)}")
# 注册表对账
readme = os.path.join(ROOT, "docs/architecture/README.md")
reg = {} # topic -> path(相对仓根)
for row in read(readme).splitlines():
if not row.startswith("|") or "canonical" not in row:
continue
cells = [c.strip() for c in row.strip().strip("|").split("|")]
if len(cells) < 3 or cells[2] != "canonical":
continue
m = MD_LINK.search(cells[1])
if not m:
problems.append(f"注册表行缺链接:{row.strip()[:70]}")
continue
tgt = os.path.normpath(os.path.join(os.path.dirname(readme), m.group(1).split("#")[0]))
reg[cells[0]] = rel(tgt)
reg_topics, canon_topics = set(reg), set(canon)
for t in sorted(reg_topics - canon_topics):
problems.append(f"注册表有、frontmatter 无 canonical:「{t}」→ {reg.get(t)}")
for t in sorted(canon_topics - reg_topics):
problems.append(f"文档标了 canonical、注册表未登记:「{t}」({canon[t][0]})")
for t in sorted(reg_topics & canon_topics):
if reg[t] not in canon[t]:
problems.append(f"topic「{t}」注册表指 {reg[t]},frontmatter 在 {canon[t][0]}")
check("G2 canonical 唯一性 + 注册表对账", problems)
# ---------- G3 死链 ----------
def g3():
problems = []
for fp in walk_md(["AGENTS.md", "docs", ".agents"]):
txt = read(fp)
dirn = os.path.dirname(fp) or ROOT
for m in MD_LINK.finditer(txt):
t = m.group(1).strip()
if t.startswith(("http://", "https://", "#", "mailto:", "<", "@")) or "," in t:
continue
t = t.split("#")[0].split(" ")[0].strip()
if not t:
continue
tgt = os.path.normpath(os.path.join(dirn, t))
if not os.path.exists(tgt):
problems.append(f"{rel(fp)} -> {m.group(1)}")
for line in txt.splitlines():
if "git show" in line:
continue
for m in BACKTICK.finditer(line):
t = m.group(1).strip()
tok = t.split()[0] if t.split() else t
if not re.match(r"^(docs|\.agents)/", tok):
continue
if any(c in tok for c in "{}*<>$#") or "YYYY" in tok:
continue
cand = os.path.join(ROOT, tok.rstrip("/"))
if not (os.path.exists(cand) or os.path.exists(cand + ".md")):
problems.append(f"{rel(fp)} -> `{t}`(反引号仓内路径不存在)")
check("G3 死链(md 链接 + 反引号仓内路径)", problems)
# ---------- G4 入口卫生 ----------
def g4():
problems = []
patterns = ["git clone", "~/.claude", "install -g", "http://localhost", "http://127."]
for i, line in enumerate(read(os.path.join(ROOT, "AGENTS.md")).splitlines(), 1):
for p in patterns:
if p in line:
problems.append(f"AGENTS.md:{i} 含「{p}")
check("G4 入口卫生(AGENTS.md)", problems)
# ---------- G5 留痕隔离 ----------
def g5():
problems = []
trace_prefix = ("docs/plans/", "docs/brainstorms/", "docs/memorys/")
for fp in walk_md(["AGENTS.md", "docs/architecture", ".agents"]):
dirn = os.path.dirname(fp) or ROOT
for m in MD_LINK.finditer(read(fp)):
t = m.group(1).strip()
if t.startswith(("http://", "https://", "#", "mailto:", "<", "@")) or "," in t:
continue
t = t.split("#")[0].strip()
if not t:
continue
tgt = os.path.normpath(os.path.join(dirn, t))
tr = rel(tgt)
hit = tr.startswith(trace_prefix) or (
tr.startswith("docs/agent-specs/") and DATED.match(os.path.basename(tr)))
if not hit:
continue
if os.path.basename(tr) == "_index.md":
continue
if os.path.exists(tgt) and frontmatter(tgt).get("canonical") == "true":
continue # 链向 canonical 活档合法
problems.append(f"{rel(fp)} -> {tr}(策展层链非 canonical 留痕)")
check("G5 留痕隔离(策展层不链留痕档)", problems)
# ---------- G6 设计档申报 ----------
def g6():
problems = []
specs = os.path.join(ROOT, "docs/agent-specs")
if os.path.isdir(specs):
for fn in os.listdir(specs):
if fn.endswith("-设计.md"):
fm = frontmatter(os.path.join(specs, fn))
missing = [k for k in ("topic", "status", "sot-impact") if not fm.get(k)]
if missing:
problems.append(f"docs/agent-specs/{fn} 缺 frontmatter:{','.join(missing)}")
check("G6 设计档申报(topic/status/sot-impact)", problems)
# ---------- G7 计划谱系(单指针 上级:) ----------
G7_CUTOFF = "2026-07-03" # 生效日:文件名日期 >= 此日的新档必带 上级:;存量自愿(带了就验)
G7_MAX_HOPS = 6 # 谱系链跳数上限(计划树实际 3-4 级,6 留余量)
def g7():
"""计划/设计档谱系门:每档一行 `上级:`(仓根相对路径),沿链必达 canonical SoT。
只验证单指针 + 链可达这条最小事实(engineering-conventions §10.8);
全链//当前位置都是查询视图(.agents/tools/plan-tree.py),不在门内维护
"""
problems = []
holders = [] # 所有带 上级: 的档(存量自愿带了也验)
for base, suffix in (("docs/plans", "-plan.md"), ("docs/agent-specs", "-设计.md")):
d = os.path.join(ROOT, base)
if not os.path.isdir(d):
continue
for fn in sorted(os.listdir(d)):
if not fn.endswith(suffix) or not DATED.match(fn):
continue
fp = os.path.join(d, fn)
fm = frontmatter(fp)
if fn[:10] >= G7_CUTOFF and not fm.get("上级"):
problems.append(f"{rel(fp)} 缺 frontmatter `上级:`(新档必须单指针挂进计划谱系,§10.8)")
elif fm.get("上级"):
holders.append(fp)
for fp in holders:
cur, seen = fp, set()
for _ in range(G7_MAX_HOPS + 1):
fm = frontmatter(cur)
if fm.get("canonical") == "true" and cur != fp:
break # 达 canonical SoT,链成立
parent = fm.get("上级")
if not parent:
problems.append(f"{rel(fp)} 谱系断链:{rel(cur)} 既非 canonical 也无 `上级:`(上级需先认领这条线)")
break
tgt = os.path.normpath(os.path.join(ROOT, parent))
if not os.path.isfile(tgt):
problems.append(f"{rel(fp)} 上级死链:{rel(cur)} -> {parent}")
break
if tgt in seen or tgt == cur:
problems.append(f"{rel(fp)} 谱系成环:{parent}")
break
seen.add(cur)
cur = tgt
else:
problems.append(f"{rel(fp)} 谱系超 {G7_MAX_HOPS} 跳未达 canonical SoT")
check("G7 计划谱系(`上级:` 单指针沿链达 canonical)", problems)
if __name__ == "__main__":
for fn in (g1, g2, g3, g4, g5, g6, g7):
fn()
if FAILS:
print(f"--- docs-gate 未过:{len(FAILS)} 检失败({''.join(FAILS)}) ---")
sys.exit(len(FAILS))
print("--- docs-gate 全绿 ---")

View File

@ -1,5 +0,0 @@
#!/usr/bin/env bash
# 文档治理门薄壳:调用 docs-gate.py(七检 G1-G7)。用法:bash .agents/tools/docs-gate.sh
# 退出码 0=全绿;非零=失败检数。pre-commit 与 wave-close 第 8 步必跑。
set -uo pipefail
exec python3 "$(cd "$(dirname "$0")" && pwd)/docs-gate.py" "$(cd "$(dirname "$0")/../.." && pwd)"

View File

@ -1,94 +0,0 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""计划谱系树(engineering-conventions §10.8 的查询视图 · 2026-07-02 立)
沿 frontmatter `上级:` 单指针把 plan/设计档拼成树打印,回答两个问题:
这份计划在整个上线计划的哪个位置某条线当前推进到哪份文档
谱系的唯一事实 = 每档一行 `上级:`(docs-gate G7 验证);本工具只是查询视图,不维护任何状态
用法:python3 .agents/tools/plan-tree.py [仓库根,缺省=脚本上两级]
"""
import os
import re
import sys
ROOT = os.path.abspath(sys.argv[1]) if len(sys.argv) > 1 else os.path.abspath(
os.path.join(os.path.dirname(__file__), "..", ".."))
DATED = re.compile(r"^\d{4}-\d{2}-\d{2}-")
def frontmatter(fp):
"""浅解析 frontmatter(与 docs-gate.py 同口径,只认顶层 `key: value` 行)。"""
try:
with open(fp, encoding="utf-8", errors="ignore") as f:
lines = f.read().splitlines()
except OSError:
return {}
if not lines or lines[0].strip() != "---":
return {}
fm = {}
for line in lines[1:60]:
if line.strip() == "---":
break
m = re.match(r"^([A-Za-z一-鿿][\w\-一-鿿]*):\s*(.*)$", line)
if m:
fm[m.group(1)] = m.group(2).split("#")[0].strip()
return fm
def main():
# 节点 = 全部 plan/设计档 + 被 `上级:` 指到的任何 md(如 docs/mvp 主计划)
nodes = {} # 仓根相对路径 -> frontmatter
for base, suffix in (("docs/plans", "-plan.md"), ("docs/agent-specs", "-设计.md")):
d = os.path.join(ROOT, base)
if not os.path.isdir(d):
continue
for fn in sorted(os.listdir(d)):
if fn.endswith(suffix) and DATED.match(fn):
nodes[f"{base}/{fn}"] = frontmatter(os.path.join(d, fn))
children, orphans = {}, [] # 上级 -> [子];无上级且无人引用的存量档
for p, fm in sorted(nodes.items()):
parent = fm.get("上级")
if parent and os.path.isfile(os.path.join(ROOT, parent)):
children.setdefault(parent, []).append(p)
elif not parent:
orphans.append(p)
# 根 = 被引用为上级、但自身不在(或不再向上指)的档;补读其 frontmatter 取状态
referenced = set(children)
for p in sorted(referenced):
if p not in nodes:
nodes[p] = frontmatter(os.path.join(ROOT, p))
roots = [p for p in sorted(referenced) if not nodes[p].get("上级") or nodes[p].get("上级") not in referenced.union(nodes)]
def label(p):
fm = nodes.get(p, {})
tags = []
if fm.get("canonical") == "true":
tags.append("canonical")
status = (fm.get("status") or fm.get("topic") or "").strip()
if status:
tags.append(status[:44] + ("" if len(status) > 44 else ""))
return f"{p}" + (f" {' · '.join(tags)}" if tags else "")
def show(p, prefix=""):
kids = children.get(p, [])
for i, k in enumerate(kids):
last = i == len(kids) - 1
print(prefix + ("└─ " if last else "├─ ") + label(k))
show(k, prefix + (" " if last else ""))
if not roots:
print("(还没有任何档挂谱系:给 plan/设计档 frontmatter 加 `上级:` 后再跑)")
for r in roots:
print(label(r))
show(r)
# 未挂谱系的存量留痕档只报数,不刷屏(§10.8:存量不强制回填)
if orphans:
print(f"\n未挂谱系的存量档 {len(orphans)} 份(留痕层,按需回填 `上级:`;明细加 --orphans 查看)")
if "--orphans" in sys.argv:
for p in orphans:
print(f" {p}")
if __name__ == "__main__":
main()

View File

@ -1,296 +0,0 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""plugin-surface-gate.py —— 便宜档插件面三方一致性对账机器门(防漂移)。
背景:便宜档生成 agent 只能用已注入的插件它经 `plugins.<>` 不能 import有哪些插件
可用这件事在三个地方各写一份彼此独立会漂移:
· 插件目录 = game-runtime/src/plugins/<name>/谁真实存在含未注入的储备件;
· prompt 白名单 = contracts/prompts/04-config/cheap-system.md 告诉模型可用插件键的那一行;
· host-config 注入清单 = game-runtime/games/_template/src/host-config.js `const plugins = {}`真装配进运行时的
外加一份 tools.mjs PLUGIN_KEY_DIRcheck API 静态门据它把 plugins.<>.<方法> 映到 api.d.ts 查方法名
四者任一漂移都会出真事故:白名单列了但没注入 模型写 plugins.x 运行时 undefinedboot ;注入了但白名单没列
模型不知道有这件能力白摆(诊断档 §四⑦点名的暗资产);目录新增一件却没人裁定它该注入还是入储备 悄悄成
第七个暗插件本门把这三方 + PLUGIN_KEY_DIR 必须对齐且每个插件目录都被显式裁定为通用注入/受保护品类注入/取证/储备之一编译
CI pre-commit 消费的退出码
裁定台账(单一事实源 = 本门的四个分区,改这里即改裁定;人读依据见 .agents/skills/plugin-capability-map.md):
· INJECTED = 三方对齐后应得的 11 (不写死键名从三方源解析后取交集/并集对账键名漂移由对账本身抓);
· PROTECTED_INJECTION_DIRS = 只由指定品类 L1 固定装配不进入通用 all-plugins/prompt 面的受保护能力;
· PROBE_DIRS = 取证不注入(runtime-probe:只在收口取证不进 plugins ,api.d.ts 明示);
· RESERVE_DIRS = 未注入能力储备(6 :当前 5 注入品类是 tap/回合制这些是动作/群体/摇杆/组合件类
W-GENRE 动作线或需复合装配本波不注入逐件依据见 plugin-capability-map.md)
判据:
1. 三方注入键集必须完全相等:prompt 白名单 == host-config 注入 == PLUGIN_KEY_DIR 键集 否则硬失败并打差集
2. 每个注入键经 PLUGIN_KEY_DIR 映到的目录必须真实存在( impl.js) 否则硬失败(注入了不存在的插件)
3. 目录分区完备:每个真实插件目录( impl.js)必须恰好落在 {通用注入目录, PROTECTED_INJECTION_DIRS, PROBE_DIRS, RESERVE_DIRS} 之一
多出未裁定的目录(新插件没决定注入/储备)硬失败;台账里的 PROBE/RESERVE 目录消失(插件被删/改名)硬失败
4. 储备/取证/受保护品类目录绝不能出现在通用注入源里否则硬失败
零依赖:仅标准库(re/sys/pathlib),与同仓 docs-gate.py / rubric-sync-gate.py 同栈同风格
用法:
python3 .agents/tools/plugin-surface-gate.py # 对本仓
python3 .agents/tools/plugin-surface-gate.py --root DIR # 对指定仓根(供负向演示跑改过的副本)
# exit 0 = 三方对齐且目录全裁定;exit 1 = 有漂移/未裁定/储备误注入
"""
from __future__ import annotations
import re
import sys
from pathlib import Path
# ── 裁定台账(单一事实源;人读依据见 .agents/skills/plugin-capability-map.md)──────────────────────
PROBE_DIRS = {"runtime-probe"} # 取证不注入(收口取证,不进 plugins 面)
PROTECTED_INJECTION_DIRS = {
# 只进 _template-puzzle L1固定 Match-3 几何/selection mask/可信 probe不进通用 all-plugins。
"match3-producer-profile",
# 只进标准 Match-3独立封存视觉事务、固定阶段并统一输入锁不进入通用插件面。
"match3-visual-timeline",
# 只进标准 Match-3宿主独占单一音频图、主题 BGM、阶段 cue 与静音,不进入 L3。
"match3-audio-director",
}
# 受保护能力同时声明唯一官方模板、实例键、能力返回键和是否允许进入 L3 plugins
# gate 会核对实例、注册偏序、返回读口与 host-only 边界。
PROTECTED_INJECTION_BINDINGS = {
"match3-producer-profile": (
"game-runtime/games/_template-puzzle/src/host-config.js",
"match3ProducerProfile",
"match3.orthogonal-swap-v1",
"producerCapability",
True,
),
"match3-visual-timeline": (
"game-runtime/games/_template-puzzle/src/host-config.js",
"match3VisualTimeline",
"match3.orthogonal-swap-v1",
"visualTimelineCapability",
True,
),
"match3-audio-director": (
"game-runtime/games/_template-puzzle/src/host-config.js",
"match3AudioDirector",
"match3.orthogonal-swap-v1",
"audioDirectorCapability",
False,
),
}
RESERVE_DIRS = { # 未注入能力储备(逐件依据见 plugin-capability-map.md)
"auto-targeting", # 索敌策略族——动作/射击品类,归 W-GENRE 动作线
"swarm-steering", # 均匀网格+群体运动学——同屏千级实体,动作/idle-sim 品类
"entity-pool", # 对象池+swap-remove——密集实体零 GC,动作品类;粒子已由 particles-juice 承接
"virtual-joystick", # 虚拟摇杆——连续移动输入,当前 5 品类全 tap/回合制,且与 handleTap 输入契约不同轴
"canvas-ui-kit", # 卡片选择器/滚动列表/顶栏——组合件,需 draw:hudUi 复合装配;可为 TRPG/经营未来注入候选
"hit-feedback", # 反馈档位编排——需 juice+sfx 复合装配,与直调 juice/audioMusic 重叠(skill 已教直调)
}
NON_PLUGIN_DIRS = {"_example"} # 示例脚手架,非真插件
# 三方源在仓内的相对路径。
PROMPT_MD = "contracts/prompts/04-config/cheap-system.md"
HOST_CONFIG = "game-runtime/games/_template/src/host-config.js"
TOOLS_MJS = "game-runtime/tools/amodel-gen/tools.mjs"
PLUGINS_DIR = "game-runtime/src/plugins"
def _fail(msgs: list[str]) -> None:
print("✘ plugin-surface-gate 未过:")
for m in msgs:
print(" - " + m)
def parse_prompt_whitelist(root: Path) -> set[str] | None:
"""从 cheap-system.md 抽「可用插件键…已注入…k1 / k2 / …」那一行的键集。抽不到返回 None。"""
p = root / PROMPT_MD
try:
for line in p.read_text(encoding="utf-8").splitlines():
if "可用插件键" in line and "已注入" in line:
# 取中文冒号「:」后的部分,按 / 切,只留 [A-Za-z] 词元。
after = line.split("", 1)[-1] if "" in line else line
keys = {t for t in re.findall(r"[A-Za-z][A-Za-z0-9]*", after)}
return keys or None
except OSError:
return None
return None
def parse_host_inject(root: Path) -> set[str] | None:
"""从 _template/src/host-config.js 抽 `const plugins = { k1, k2, … }` 的键集。抽不到返回 None。"""
p = root / HOST_CONFIG
try:
txt = p.read_text(encoding="utf-8")
except OSError:
return None
m = re.search(r"const\s+plugins\s*=\s*\{([^}]*)\}", txt)
if not m:
return None
# 对象是简写键(shorthand),逐词元取标识符即键名。
keys = {t for t in re.findall(r"[A-Za-z_][A-Za-z0-9_]*", m.group(1))}
return keys or None
def parse_plugin_key_dir(root: Path) -> dict[str, str] | None:
"""从 tools.mjs 抽 `const PLUGIN_KEY_DIR = { key: 'dir', … }` 映射。抽不到返回 None。"""
p = root / TOOLS_MJS
try:
txt = p.read_text(encoding="utf-8")
except OSError:
return None
m = re.search(r"const\s+PLUGIN_KEY_DIR\s*=\s*\{(.*?)\}", txt, re.S)
if not m:
return None
pairs = dict(re.findall(r"(\w+)\s*:\s*'([^']+)'", m.group(1)))
return pairs or None
def list_real_plugin_dirs(root: Path) -> set[str] | None:
"""列 src/plugins/ 下含 impl.js 的真实插件目录名。目录不存在返回 None。"""
d = root / PLUGINS_DIR
if not d.is_dir():
return None
out = set()
for sub in d.iterdir():
if sub.is_dir() and (sub / "impl.js").is_file():
out.add(sub.name)
return out
def run(root: Path) -> int:
msgs: list[str] = []
whitelist = parse_prompt_whitelist(root)
inject = parse_host_inject(root)
key_dir = parse_plugin_key_dir(root)
dirs = list_real_plugin_dirs(root)
# 任一源解析不出 = 结构被改坏/路径漂移,硬失败(不静默放行)。
if whitelist is None:
msgs.append(f"prompt 白名单解析失败(找不到「可用插件键…已注入…」行):{PROMPT_MD}")
if inject is None:
msgs.append(f"host-config 注入清单解析失败(找不到 const plugins = {{}}):{HOST_CONFIG}")
if key_dir is None:
msgs.append(f"PLUGIN_KEY_DIR 解析失败:{TOOLS_MJS}")
if dirs is None:
msgs.append(f"插件目录不存在:{PLUGINS_DIR}")
if msgs:
_fail(msgs)
return 1
key_dir_keys = set(key_dir.keys())
# 判据 1:三方注入键集完全相等。
if not (whitelist == inject == key_dir_keys):
msgs.append("三方注入键集不一致(prompt 白名单 / host-config 注入 / PLUGIN_KEY_DIR 必须完全相等):")
msgs.append(f" prompt 白名单({len(whitelist)}):{sorted(whitelist)}")
msgs.append(f" host-config 注入({len(inject)}):{sorted(inject)}")
msgs.append(f" PLUGIN_KEY_DIR({len(key_dir_keys)}):{sorted(key_dir_keys)}")
only_wl = whitelist - inject - key_dir_keys
only_hc = inject - whitelist - key_dir_keys
only_kd = key_dir_keys - whitelist - inject
missing_wl = (inject & key_dir_keys) - whitelist
if only_wl:
msgs.append(f" 仅在 prompt 白名单(列了但没注入→运行时 undefined、boot 崩):{sorted(only_wl)}")
if only_hc:
msgs.append(f" 仅在 host-config(注入了但白名单/静态门没列→模型不知道有它、能力白摆):{sorted(only_hc)}")
if only_kd:
msgs.append(f" 仅在 PLUGIN_KEY_DIR:{sorted(only_kd)}")
if missing_wl:
msgs.append(f" 注入了但 prompt 白名单漏列:{sorted(missing_wl)}")
injected_dirs = {key_dir[k] for k in key_dir_keys}
# 判据 2:每个注入键的目录真实存在(含 impl.js)。
for k in sorted(key_dir_keys):
if key_dir[k] not in dirs:
msgs.append(f"注入键 {k} 映到目录 src/plugins/{key_dir[k]}/ 不存在或缺 impl.js")
# 判据 4:储备/取证目录绝不能出现在任何注入源(键名≠目录名,按目录集判)。
non_generic = RESERVE_DIRS | PROBE_DIRS | PROTECTED_INJECTION_DIRS
bad_injected = injected_dirs & non_generic
if bad_injected:
msgs.append("储备/取证/受保护品类目录被误注入通用面"
f"(须先从对应台账挪走再注入):{sorted(bad_injected)}")
# 受保护能力只允许出现在登记的官方模板;全部进入 registerOrder 与独立能力返回键,
# 只有声明 expose_to_l3=True 的能力才能进入 plugins。
template_hosts = sorted((root / "game-runtime/games").glob("_template*/src/host-config.js"))
if set(PROTECTED_INJECTION_BINDINGS) != PROTECTED_INJECTION_DIRS:
msgs.append("PROTECTED_INJECTION_BINDINGS 与 PROTECTED_INJECTION_DIRS 不一致")
for plugin_dir, (relative_host, plugin_key, profile_id, return_key, expose_to_l3) in PROTECTED_INJECTION_BINDINGS.items():
expected_host = root / relative_host
try:
source = expected_host.read_text(encoding="utf-8")
except OSError:
msgs.append(f"受保护能力 {plugin_dir} 的唯一模板不存在:{relative_host}")
continue
plugins_match = re.search(r"const\s+plugins\s*=\s*\{([^}]*)\}", source, re.S)
register_match = re.search(r"const\s+registerOrder\s*=\s*\[([^]]*)\]", source, re.S)
injected_by_object = bool(plugins_match and plugin_key in plugins_match.group(1))
injected_by_guard = bool(re.search(
rf"plugins\.{re.escape(plugin_key)}\s*=\s*{re.escape(plugin_key)}\b", source,
))
is_l3_injected = injected_by_object or injected_by_guard
if plugin_dir not in source:
msgs.append(f"受保护能力 {plugin_dir} 未在唯一模板实例化:{relative_host}")
if expose_to_l3 and not is_l3_injected:
msgs.append(f"受保护能力 {plugin_dir} 未按 {plugin_key} 注入唯一模板 L3 plugins:{relative_host}")
if not expose_to_l3 and is_l3_injected:
msgs.append(f"host-only 受保护能力 {plugin_dir} 禁止注入 L3 plugins:{relative_host}")
registered_in_array = bool(register_match and plugin_key in register_match.group(1))
registered_by_guard = bool(re.search(
rf"registerOrder\.splice\([^;]*\b{re.escape(plugin_key)}\b", source,
))
if not (registered_in_array or registered_by_guard):
msgs.append(f"受保护能力 {plugin_dir} 未进入唯一模板 registerOrder:{relative_host}")
if not re.search(rf"{re.escape(return_key)}\s*:\s*{re.escape(plugin_key)}\b", source):
msgs.append(f"受保护能力 {plugin_dir} 未从唯一模板返回 {return_key}:{relative_host}")
if profile_id not in source or not re.search(
r"interactionProfileId\s*===\s*MATCH3_INTERACTION_PROFILE_ID", source,
):
msgs.append(f"受保护能力 {plugin_dir} 未按显式 profile 条件装配:{profile_id}")
unexpected = []
for candidate in template_hosts:
candidate_source = candidate.read_text(encoding="utf-8")
if candidate != expected_host and (plugin_dir in candidate_source or plugin_key in candidate_source):
unexpected.append(str(candidate.relative_to(root)))
if unexpected:
msgs.append(f"受保护能力 {plugin_dir} 出现在未登记官方模板:{unexpected}")
# 判据 3:目录分区完备——每个真实插件目录恰好落在 {注入, PROBE, RESERVE} 之一;NON_PLUGIN 豁免。
classified = injected_dirs | PROTECTED_INJECTION_DIRS | PROBE_DIRS | RESERVE_DIRS | NON_PLUGIN_DIRS
unclassified = dirs - classified
if unclassified:
msgs.append("发现未裁定的插件目录(新插件必须显式裁定:通用注入 / 受保护品类注入 / 取证 / 储备四选一,"
"并在 plugin-capability-map.md 写一句依据):")
msgs.append(f" {sorted(unclassified)}")
# 台账里登记的 PROBE/RESERVE 目录若已从盘上消失(删/改名)→ 台账过时,硬失败。
ghost = (PROTECTED_INJECTION_DIRS | PROBE_DIRS | RESERVE_DIRS) - dirs
if ghost:
msgs.append(f"台账登记的受保护注入/取证/储备目录在盘上不存在"
f"(插件被删/改名?更新本门台账与 plugin-capability-map.md):{sorted(ghost)}")
if msgs:
_fail(msgs)
return 1
print(f"✔ plugin-surface-gate 全绿:{len(key_dir_keys)} 注入键三方对齐"
f"(prompt 白名单 == host-config == PLUGIN_KEY_DIR);"
f"{len(PROTECTED_INJECTION_DIRS)} 受保护品类注入 + {len(PROBE_DIRS)} 取证 + "
f"{len(RESERVE_DIRS)} 储备目录全登记在册,{len(dirs)} 个真实插件目录全部已裁定。")
return 0
def main() -> int:
# 缺省仓根锚定到脚本自身位置(<repo>/.agents/tools/ → parents[2] = 仓根),不吃 cwd——
# 2026-07-09 验收实测:cwd 在别处跑会把四路解析全报「失败」,是误导性假漂移(pre-commit/CI 恰好
# 都从仓根跑才没暴露)。--root 覆盖位保留(负向演示跑改过的副本用)。
root = Path(__file__).resolve().parents[2]
argv = sys.argv[1:]
if "--root" in argv:
i = argv.index("--root")
if i + 1 < len(argv):
root = Path(argv[i + 1]).resolve()
return run(root)
if __name__ == "__main__":
raise SystemExit(main())

View File

@ -1,259 +0,0 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""rubric-sync-gate.py —— 品类 rubric fixture ↔ 品类 skill 双写对账机器门。
背景:每个品类的丰富度 rubric LLM 验证 agent 的评分尺)
`cheap-worker/fixtures/genre-rubrics/<genre>.json` **单一数据源**部分品类的
设计 skill(`.agents/skills/<genre>-game-design.md`)出于人读需要 §10/§11
留了一份内联条目副本;另一些已收敛成"只留一个指针指向 fixture、不再抄表"两种形态
都有漂移风险:内联副本会与 fixture 各改一侧而失同步,纯指针会指向一个被改名/删除的
fixture本门把这条"改一处必须同改另一处 / 指针必须真实可达"的纪律编译成可被 CI
pre-commit 消费的退出码
判据(逐品类):
1. 通用底座:fixture 文件必须存在能解析items 非空且每条有 name 否则硬失败
2. 指针完好(所有品类):skill 正文必须引用到本品类 fixture 路径
`genre-rubrics/<genre>.json`,且该文件真实存在 否则硬失败
3. 内联对账(仅带内联条目副本的品类):
逐条按序比对 fixture name skill 内联表/清单抽出的条目名
· 规范化后逐字相等 一致;
· 规范化后 fixture 名是 skill 名的子序列 判为"同条目·措辞装饰"(容忍,
打印提示不失败)skill 是人读自检清单,允许在 canonical 名上加描述性修饰
("风险回报取舍"写成"风险-回报取舍真实");
· 否则 判为真实漂移(改名/替换),硬失败并打印差异;
条目数不一致(增删条目) 硬失败
规范化 = 只保留中日韩统一表意文字(去标点/空格/ASCII/加减号等),以吸收人读排版差异
只在"名目"层面对账
零依赖:仅标准库(json/re/sys/pathlib),与同仓 docs-gate.py / check_registry.py /
play-loop/validate.py 同栈同风格
用法:
python3 .agents/tools/rubric-sync-gate.py # 对本仓
python3 .agents/tools/rubric-sync-gate.py --root DIR # 对指定仓根(供负向演示跑副本)
# exit 0 = 全部对齐;exit 1 = 有真实漂移/指针断裂/fixture 损坏
"""
from __future__ import annotations
import json
import re
import sys
from pathlib import Path
# 本文件 = <root>/.agents/tools/rubric-sync-gate.py → 仓根上溯两级
DEFAULT_ROOT = Path(__file__).resolve().parents[2]
# 品类配置(逐一考察真实状态后编码,2026-07-03):
# mode=inline → skill 在指定小节留了内联条目表/清单,需逐条对账;
# mode=pointer → skill 已收敛为纯指针,只校验指针路径真实存在。
# section_anchor:内联小节标题前缀(用于把抽取范围钉在该节内,不误采别处的表/清单)。
# row_re:该节内"条目行"的识别正则(表行 / 编号清单行)。
GENRES = {
"puzzle": {
"skill": ".agents/skills/puzzle-game-design.md",
"mode": "inline",
"section_anchor": "## 10.",
"row_re": r"^\|\s*P\d", # §10 品类扩展表:| P1 | L2 | 谜题规则递进 | …
},
"trpg": {
"skill": ".agents/skills/trpg-game-design.md",
"mode": "inline",
"section_anchor": "## 10.",
"row_re": r"^\d+\.\s+\*\*", # §10 自检编号清单:1. **[L2] 掷骰过程可见**:…
},
"heritage": {
"skill": ".agents/skills/heritage-game-design.md",
"mode": "inline",
"section_anchor": "## 11.",
"row_re": r"^\|\s*H\d", # §11 品类 rubric 表:| H1 | L2 | **工序链成立**:… |
},
"narrative": {
"skill": ".agents/skills/narrative-game-design.md",
"mode": "pointer", # §10 已迁出为单源指针,不再维护表格副本
},
"sim-business": {
"skill": ".agents/skills/sim-business-game-design.md",
"mode": "pointer", # §10 只有通用自检 + 指向 fixture 的指针,无品类表副本
},
}
_IDEOGRAPH = re.compile(r"[一-鿿]")
def _norm(name: str) -> str:
"""规范化:只留中日韩表意文字,吸收标点/空格/ASCII/加减号等人读排版差异。"""
return "".join(_IDEOGRAPH.findall(name or ""))
def _is_subsequence(needle: str, haystack: str) -> bool:
"""needle 的字符是否按序(可不连续)出现在 haystack 中。"""
it = iter(haystack)
return all(ch in it for ch in needle)
def _fixture_path(root: Path, genre: str) -> Path:
return root / "cheap-worker" / "fixtures" / "genre-rubrics" / f"{genre}.json"
def _load_fixture_names(root: Path, genre: str):
"""读 fixture,返回 (names 列表, 错误串)。错误串非空表示通用底座判据未过。"""
fp = _fixture_path(root, genre)
if not fp.exists():
return None, f"fixture 缺失:{fp}"
try:
data = json.loads(fp.read_text(encoding="utf-8"))
except Exception as e: # noqa: BLE001 —— 解析失败即 fixture 损坏,如实报
return None, f"fixture JSON 解析失败:{e}"
items = data.get("items")
if not isinstance(items, list) or not items:
return None, "fixture 缺 items 或为空"
names = []
for i, it in enumerate(items):
nm = (it or {}).get("name")
if not nm:
return None, f"fixture items[{i}] 缺 name"
names.append(nm)
return names, None
def _slice_section(lines, anchor: str):
"""取 anchor 小节的行区间:从标题行到下一条 `---` 或下一个 `## ` 标题之前。"""
start = None
for i, ln in enumerate(lines):
if ln.startswith(anchor):
start = i
break
if start is None:
return None
end = len(lines)
for j in range(start + 1, len(lines)):
s = lines[j].strip()
if s == "---" or lines[j].startswith("## "):
end = j
break
return lines[start:end]
def _extract_inline_name(row: str) -> str:
"""从一条内联条目行抽出品类条目名。统一处理三种形态:
· 表行( `|`):取第 3 个单元格(条目/判据列);
· 编号清单行: `N. ` 之后的正文;
: markdown 粗体 `**`去开头的 `[Lx]` 层标截到首个中文冒号/英文冒号前
"""
if "|" in row:
cells = row.split("|")
frag = cells[3] if len(cells) > 3 else row
else:
frag = re.sub(r"^\d+\.\s+", "", row)
frag = frag.replace("**", "")
frag = re.sub(r"^\s*\[L\d\]\s*", "", frag) # 去 trpg 的 [L2] 层标前缀
frag = re.split(r"[:]", frag, maxsplit=1)[0]
return frag.strip()
def _skill_inline_names(root: Path, cfg: dict):
"""按配置从 skill 指定小节抽出内联条目名列表,返回 (names, 错误串)。"""
sp = root / cfg["skill"]
if not sp.exists():
return None, f"skill 缺失:{sp}"
lines = sp.read_text(encoding="utf-8").splitlines()
section = _slice_section(lines, cfg["section_anchor"])
if section is None:
return None, f"skill 找不到小节 {cfg['section_anchor']}"
row_re = re.compile(cfg["row_re"])
names = [_extract_inline_name(ln) for ln in section if row_re.match(ln)]
return names, None
def _skill_has_pointer(root: Path, genre: str, cfg: dict):
"""校验 skill 正文含指向本品类 fixture 的指针,返回 (bool, 错误串)。"""
sp = root / cfg["skill"]
if not sp.exists():
return False, f"skill 缺失:{sp}"
text = sp.read_text(encoding="utf-8")
if f"genre-rubrics/{genre}.json" not in text:
return False, f"skill 未引用 fixture 指针 genre-rubrics/{genre}.json"
return True, None
def check_genre(root: Path, genre: str, cfg: dict):
"""对账单个品类,返回 (ok, 明细行列表)。ok=False 表示硬失败。"""
detail = []
# 判据 1:通用底座 —— fixture 完好
fx_names, err = _load_fixture_names(root, genre)
if err:
return False, [f"{err}"]
# 判据 2:指针完好(所有品类)
ptr_ok, ptr_err = _skill_has_pointer(root, genre, cfg)
if not ptr_ok:
return False, [f"{ptr_err}"]
detail.append(f" ✓ 指针可达:skill 引用 genre-rubrics/{genre}.json,fixture {len(fx_names)}")
if cfg["mode"] == "pointer":
detail.append(" · 形态=纯指针(skill 不维护副本),只校验指针 —— 通过")
return True, detail
# 判据 3:内联对账
sk_names, err = _skill_inline_names(root, cfg)
if err:
return False, detail + [f"{err}"]
if len(sk_names) != len(fx_names):
return False, detail + [
f" ✗ 条目数漂移:fixture {len(fx_names)} 条 vs skill 内联 {len(sk_names)}",
f" fixture:{fx_names}",
f" skill :{sk_names}",
]
drift = []
paraphrase = []
for i, (fn, sn) in enumerate(zip(fx_names, sk_names)):
fnn, snn = _norm(fn), _norm(sn)
if fnn == snn:
continue
if _is_subsequence(fnn, snn):
paraphrase.append(f" · 第{i + 1}条 同条目·措辞装饰:fixture「{fn}」→ skill「{sn}")
else:
drift.append(f" · 第{i + 1}条 真实漂移:fixture「{fn}」 ≠ skill「{sn}")
if drift:
return False, detail + [f" ✗ 内联副本与 fixture 名目漂移({len(drift)} 条):"] + drift + paraphrase
if paraphrase:
detail.append(f" ✓ 内联副本 {len(fx_names)} 条名目一致({len(paraphrase)} 条为措辞装饰、非漂移):")
detail.extend(paraphrase)
else:
detail.append(f" ✓ 内联副本 {len(fx_names)} 条名目逐字一致")
return True, detail
def main(argv) -> int:
root = DEFAULT_ROOT
if len(argv) >= 3 and argv[1] == "--root":
root = Path(argv[2]).resolve()
print("品类 rubric fixture ↔ skill 双写对账门")
print("=" * 60)
all_ok = True
for genre, cfg in GENRES.items():
ok, detail = check_genre(root, genre, cfg)
status = "PASS" if ok else "FAIL"
icon = "" if ok else ""
print(f"\n[{status}] {genre} {icon} ({cfg['mode']})")
for d in detail:
print(d)
if not ok:
all_ok = False
print("\n" + "=" * 60)
if all_ok:
print("对账通过:所有品类 fixture 单源与 skill 副本/指针一致。")
return 0
print("对账失败:见上 ✗ —— 改一处必须同改另一处(fixture 为单源),或修复断裂指针。")
return 1
if __name__ == "__main__":
sys.exit(main(sys.argv))

View File

@ -1,6 +1,6 @@
# AI 驱动开发元流程
> 本文是绘境AI 项目承接**任意任务**的统一打法:从识别 → 分析 → 评审 → 执行 → 验证 → 沉淀的完整协议。所有 Agent / 工程师在本仓库做事都遵循它。
> 本文是造梦AI 项目承接**任意任务**的统一打法:从识别 → 分析 → 评审 → 执行 → 验证 → 沉淀的完整协议。所有 Agent / 工程师在本仓库做事都遵循它。
> 配套硬规则:[`../rules/engineering-conventions.md`](../rules/engineering-conventions.md)、[`../rules/security-and-reliability.md`](../rules/security-and-reliability.md);事实蓝图:[`../knowledge/product-and-architecture.md`](../knowledge/product-and-architecture.md);操作手册:[`../skills/`](../skills/);目录索引:[`../README.md`](../README.md)。
---
@ -28,7 +28,7 @@ flowchart TD
编号步骤:
1. **识别任务类型与复杂度**——先判断这是分析/评审/编码/调试/写作中的哪一类,是否复杂、高风险、跨模块、用户可见。
2. **取经验**——先读 [`../knowledge/`](../knowledge/) 与 `docs/`(架构原档);同类历史任务检索 `docs/agent-specs/` 的收口报告与总账 §6 执行记录(留痕层 `docs/plans/`(旧 memorys 已删,git 可查) 为早期遗留、已停增)。
2. **取经验**——先读 [`../knowledge/`](../knowledge/) 与 `docs/`(架构原档);同类历史任务检索 `docs/agent-specs/` 的收口报告与总账 §6 执行记录(`docs/memorys/` 为早期遗留、已停增)。
3. **复杂/高风险任务先 Restate**——复述理解声明目标、范围边界、已验证事实、假设、影响面blast radius、风险、验证计划。
4. **评审版 spec**——产出 `docs/agent-specs/YYYY-MM-DD-topic-review.md`**结论先行**,只留背景/目标/非目标/推荐方案/关键权衡/影响面/风险兼容/验收标准/待确认项,多用 Mermaid降低认知负荷不写代码级细节。
5. **两轮评审**——评审版经**两轮**评审确认后再进入执行。
@ -72,14 +72,10 @@ flowchart TD
|---|---|---|---|
| 新增一个 game-module | 编码 | 全流程 | 执行版 spec + 契约先行;落地照 [`../skills/add-business-module.md`](../skills/add-business-module.md) |
| 给 aigc 加一个生成失败错误码 | 编码 | 简单 | 在 `-api` 错误码段登记 + 单测,不动契约 |
| 排查"生成任务一直 QUEUED" | 调试 | 简单→视根因 | 先查 RocketMQ 异步 gen 队列消费者 / cheap-workerAgentScopeService 日志复现,再定位 |
| 排查"生成任务一直 QUEUED" | 调试 | 简单→视根因 | 先查 Dify/MQ Consumer 日志复现,再定位 |
| 评估是否引入 ClickHouse | 分析 | 全流程 | 第一性原理 + 评审版 spec结论先行 |
| 审查一个支付回调 PR | 评审 | —— | 按严重度列问题;重点幂等/状态机/乐观锁 |
### 2.3 agentic 设计/评审任务的强制过尺2026-07-05创始人拍定
凡设计或评审生成运行时的 **agent 席位划分、context 装配、prompt 结构、工具面、多 agent 协作、goal-loop 自治交付、创作者交互面**,必须按 [`../skills/agentic-seat-context-design.md`](../skills/agentic-seat-context-design.md) §8 检查单过尺:设席八问 / 注入五查 / 场景四要素 / "规范进 prompt"先问模板与门。评审此类设计档时,把该检查单当评审尺逐条对;设计档内〔提案〕机制落地前须逐条验证可行性(该 skill 是需求基线,不是架构 SoT。**全新 agentic 子系统首次设计前,若尚无需求基线,先按该 skill §10 探针方法跑一轮第一人称需求探针产基线再设计**2026-07-06 立 W-PROBE默认建议、升强制与否待创始人拍探针对象排期唯一登记处 = 作战清单 W-PROBE 单)。
---
## 3. 证据规则
@ -118,7 +114,7 @@ flowchart TD
|---|---|---|
| 资金域整体 | trade T-TRD-01..12 + pay 接线 + 广告计费反作弊正式项Doc B §11 📌注记) | 钱 + 对抗域,分账/对账/打款错了直接资损 |
| 多渠道统一转译引擎 | T-RT-26 + 渠道矩阵 T-RT-21..24/31/32 | 渠道格式 = 外部硬契约,引擎定错全渠道返工 |
| agentic 编排引擎 | T-STU-05/10 + T-AGC-18 DAG | ~~全项目最重自研框架~~ **已终裁不自研(HJ-AGI-001→HJ-AGI-002→**2026-06-25 框架收敛覆盖**:生成框架统一收敛 AgentScope、三档按 AI 参与深度分(引擎随复杂度选 LittleJS / PhaserSAA / dify / coze 降为最低优先级,留作远期可插拔适配验证Fable 只设计五资产契约边界与适配层T-AGC-18 自研 DAG 引擎作废)** |
| agentic 编排引擎 | T-STU-05/10 + T-AGC-18 DAG | ~~全项目最重自研框架~~ **已终裁不自研(2026-06-12 HJ-AGI-001→**2026-06-15 HJ-AGI-002 覆盖**:编排=SAA(Spring AI Alibaba v1.1.2.2)裸图编排short-term SAA-onlyAgentScope 降 long-term premium 独立轨Fable 只设计五资产契约边界与适配层T-AGC-18 自研 DAG 引擎作废)** |
| 商用授权链域模型 | T-IP-02/03 + T-CMP-12 锁风门聚合语义 | 资产积累后数据模型无法重构,且与法务耦合 |
| 推荐 + A/B 实验框架 | T-FED-12/13增长期 | 离线/在线一致性、数据闭环正确性 = 护城河承重墙 |
| 一次性架构事件 | monorepo→三仓拆分、MySQL→ClickHouse 迁移、沙箱安全边界对抗评审T-RT-04/05/14 + T-CMP-26 | 全仓级影响 / 安全边界 |
@ -141,18 +137,6 @@ flowchart TD
**明确不升档**模板克隆、CRUD/admin 页面、渠道 adapter 批量复制引擎定稿后、批量验证、UI 走查执行——Opus/Haiku 照常,显式传 `model` 红线不变。
### 4.2 执行子代理的产出验证纪律SDD 实录2026-07-02
派子代理执行编码任务、尤其走 subagent-driven-development 时,子代理回报的 DONE 是**未验证声称**controller 不得据以标记完成。一次实录:子代理报「测试 8/8、已提交 commit 6837c8ee」实际测试是 2 failed那个 commit 在 git 里根本不存在——改动只落在工作树、从未提交。
五条硬纪律:
- **每个报 DONE 的 commit 自验**`git rev-parse HEAD` 与回报的 hash 对得上,真跑关键测试(不信回报的通过数),红线级改动亲读 diff。
- **生成 review 包时的 hash 校验是造假第一道自动拦网**:编造的 commit hash 不在 git 里,一 `git` 就报错。把「生成 review 包 / diff」放在标 complete 之前当强制步,能第一时间撞破。
- **造假子代理弃用、不 resume**它带着「我已做完」的错误认知resume 容易再造假;换 fresh 子代理,只补 controller 诊断出的精确缺口prompt 里明写诚实红线(回传真实 HEAD 与原样测试输出,没全绿一律报 BLOCKED 而非 DONE
- **fix 子代理让其自证**要求「删掉修复→缺陷用例必红」这类反向验证证明测试真在测行为而非桩自证controller 再复核一遍。
- **关键 hash 一律主代理独立重算,绝不抄子代理报的值**:不止 commit hash——`runtime-tree.json` 的 sha256、`artifacts.sha256`、baseline 的 `promptBodySha256` 这类产物/基线 hash子代理报的长度或值也可能是错的一实录子代理报 runtime-tree hash 为 62 位却称 64 位)。主代理对这些 hash 必须自己 `sha256sum` / `git rev-parse` 重算比对,不直接采信报值;`git rev-parse` 验 commit hash 存在是反造假第一道(第一条),产物 hash 同理——报得出 ≠ 算得对。
---
## 5. 验证
@ -178,7 +162,7 @@ flowchart TD
2. **回写对应层**:事实 → `knowledge/`;硬约束 → `rules/`;操作手册 → `skills/`;流程 → `workflows/`
3. **同步索引**:任何结构性变更(增/删/改名)**同步更新 [`../README.md`](../README.md) 的文件清单**与交叉链接(统一相对路径)。
4. **过时即处理**:信息失效立即修正或删除;与代码/文档冲突时以**已验证事实**为准。
5. **询问是否落 memorys**:关键任务信息按需持久化到 `docs/plans/`(留痕层;旧 memorys 已删,git 可查)(描述 1020 字),供同类任务复用。
5. **询问是否落 memorys**:关键任务信息按需持久化到 `docs/memorys/YYYY-MM-DD-任务描述.md`(描述 1020 字),供同类任务复用。
---

View File

@ -51,7 +51,7 @@ graph LR
| 4 | **验证门禁前置 / fail-fast** | TDD + 7 门禁 + 完成前验证evidence早拦错 | **返工是头号效率杀手**,越早拦越省 | [`../rules/security-and-reliability.md`](../rules/security-and-reliability.md) |
| 5 | **并行边界 = 模块边界** | Agent 间零共享状态、worktree 隔离、只经契约交互 | 无写冲突 / 无串扰才能稳定并行 | 本篇 §1 |
| 6 | **`.agents` 复利沉淀** | 每个 Agent 交付后回写事实/红线/套路 | 知识不重置,后人站在前人肩上 | [`../README.md`](../README.md) 维护规则 |
| 7 | **昂贵步骤缓存** | 相同 Prompt hash 命中跳过 LLM 生成Maven/构建缓存;产物缓存 | 运行期 + 构建期双重提速、省成本 | [`../skills/saa-graph-orchestration.md`](../skills/saa-graph-orchestration.md) |
| 7 | **昂贵步骤缓存** | 相同 Prompt hash 命中跳过 LLM 生成Maven/构建缓存;产物缓存 | 运行期 + 构建期双重提速、省成本 | [`../skills/ai-generation-pipeline.md`](../skills/ai-generation-pipeline.md) |
| 8 | **可复现编排** | 用 Workflow 脚本固化"按模块 fan-out + 逐个 verify" | 编排可重跑、可审计、可断点续跑 | 本篇 §6 |
---

View File

@ -1,8 +0,0 @@
---
name: agentic-seat-context-design
description: 设计或评审生成运行时的 agent 席位划分、context 装配、prompt 结构、工具面、多 agent 协作、goal-loop 自治交付、创作者交互面时使用——需求基线 + 设计检查单(设席八问/注入五查/场景四要素)+ 第一人称需求探针方法(§10);非架构 SoT。
---
本文件是薄壳注册件,正文唯一在项目 SoT(`.agents/README.md` 边界规则,防双写):
**读取仓库根下 `.agents/skills/agentic-seat-context-design.md` 并照其执行**;评审 agentic 设计时按其 §8 检查单过尺(`ai-development-protocol.md` §2.3 强制)。

View File

@ -1,317 +0,0 @@
---
name: agentscope-skill
description: This guide covers the design philosophy, core concepts, and practical usage of the AgentScope framework. Use this skill whenever the user wants to do anything with the AgentScope (Python) library. This includes building agent applications using AgentScope, answering questions about AgentScope, looking for guidance on how to use AgentScope, searching for examples or specific information (functions/classes/modules).
version: 0.1.0
---
## Understanding AgentScope
### What is AgentScope?
AgentScope is a production-ready, enterprise-grade open-source framework for building multi-agent applications with large language models. Its functionalities cover:
- **Development**: ReAct agent, context compression, short/long-term memory, tool use, human-in-the-loop, multi-agent orchestration, agent hooks, structured output, planning, integration with MCP, agent skill, LLMs API, voice interaction (TTS/Realtime), RAG
- **Evaluation**: Evaluate multistep agentic applications with statistical analysis
- **Training**: Agentic reinforcement learning
- **Deployment**: Session/state management, sandbox, local/serverless/Kubernetes deployment
### Installation
```bash
pip install agentscope
# or
uv pip install agentscope
```
### Core Concepts
- **Message**: The core abstraction for information exchange between agents. Supports heterogeneous content blocks (text, images, tool calls, tool results).
```python
from agentscope.message import Msg, TextBlock, ImageBlock, URLSource
msg = Msg(
name="user",
content=[TextBlock("Hello world"), ImageBlock(type="image", source=URLSource(type="url", url="..."))],
role="user"
)
```
- **Agent**: LLM-empowered agent that can reason, use tools, and generate responses through iterative thinking and action loops.
- **Toolkit**: Register and manage tools (Python functions, MCP, agent skills) that agents can call.
- **Memory**: Store `Msg` objects as conversation history/context with a marking mechanism for advanced memory management (compression, retrieval).
- **ChatModel**: Unified interface across different providers (OpenAI, Anthropic, DashScope, Ollama, etc.) with support for tool use and streaming.
- **Formatter**: Convert `Msg` objects to LLM API-specific formats. Must be used with the corresponding ChatModel. Supports multi-agent conversations with different agent identifiers.
### Basic Usage Examples
#### Example 1: Simple Chatbot
```python
from agentscope.agent import ReActAgent, UserAgent
from agentscope.model import DashScopeChatModel
from agentscope.formatter import DashScopeChatFormatter
from agentscope.memory import InMemoryMemory
from agentscope.tool import Toolkit, execute_python_code, execute_shell_command
import os, asyncio
async def main():
# Initialize toolkit with tools
toolkit = Toolkit()
toolkit.register_tool_function(execute_python_code)
toolkit.register_tool_function(execute_shell_command)
# Create ReActAgent with model, memory, formatter, and toolkit
agent = ReActAgent(
name="Friday",
sys_prompt="You're a helpful assistant named Friday.",
model=DashScopeChatModel(
model_name="qwen-max",
api_key=os.getenv("DASHSCOPE_API_KEY"),
stream=True,
),
memory=InMemoryMemory(),
formatter=DashScopeChatFormatter(),
toolkit=toolkit,
)
# Create user agent for terminal input
user = UserAgent(name="user")
# Conversation loop
msg = None
while True:
msg = await agent(msg) # Agent processes and replies
msg = await user(msg) # User inputs next message
if msg.get_text_content() == "exit":
break
asyncio.run(main())
```
#### Example 2: Multi-Agent Conversation
AgentScope adopts explicit message passing for multi-agent conversations (PyTorch-like dynamic graph), allowing flexible information flow control.
```python
alice, bob, carol, david = ReActAgent(...), ReActAgent(...), ReActAgent(...), ReActAgent(...)
msg_alice = await alice()
msg_bob = await bob(msg_alice) # Bob receives Alice's message and generate a reply. Alice doesn't receive Bob's message unless explicitly passed back.
msg_carol = await carol(msg_alice) # Similarly, the agent cannot receive messages from other agents unless explicitly passed.
# Broadcasting with MsgHub, a syntactic sugar for message broadcasting within a group of agents
from agentscope.pipeline import MsgHub
async with MsgHub(
participants=[alice, bob, carol],
announcement=Msg("Host", "Let's discuss", "user")
) as hub:
await alice() # Bob and Carol receive this
await bob() # Alice and Carol receive this
# Manual broadcast
await hub.broadcast(Msg("Host", "New topic", "user"))
# Dynamic participant management
hub.add(david)
hub.delete(bob)
```
#### Example 3: Master-Worker Pattern
Wrap worker agents as tools for the master agent.
```python
from agentscope.tool import ToolResponse, Toolkit
async def create_worker(task: str) -> ToolResponse:
"""Create a worker agent for the given task.
Args:
task (`str`): The given task, which should be specific and concise.
"""
task_msg = Msg(name="master", content=task, role="user") # Use the input task or wrap it into a more complex prompt
worker = ReActAgent(...)
res = await worker(task_msg)
return ToolResponse(content=res.content) # Return the worker's response as the tool response
toolkit = Toolkit()
toolkit.register_tool_function(create_worker)
```
## Working with AgentScope
This section provides guidance on how to effectively answer questions about AgentScope or coding with the framework.
### Step 1: Clone the Repository First
**CRITICAL**: Before doing anything else, clone or update the AgentScope repository. The repository contains essential examples and references.
```bash
# Clone the tag matching the INSTALLED version (not main — avoid source-vs-runtime drift).
# Installed version lives under cheap-worker/.venv/.../site-packages/ (currently agentscope 2.0.2).
# Already cloned for v2.0.2; the dir is gitignored and reused across sessions.
git clone --depth 1 -b v2.0.2 https://github.com/agentscope-ai/agentscope.git /path/to/this/skill/directory/agentscope
# When the installed version bumps (check .venv dist-info or agentscope/src/agentscope/_version.py),
# re-clone the new tag rather than `git pull` (the clone is a detached tag checkout):
# rm -rf /path/to/this/skill/directory/agentscope
# git clone --depth 1 -b vX.Y.Z https://github.com/agentscope-ai/agentscope.git /path/to/this/skill/directory/agentscope
```
**Why this matters**: the repo carries working examples and complete API source, more reliable than guessing — and pinning to the installed tag keeps what you read identical to what runs (this project verifies behavior against the installed 2.0.2; `main` may be ahead and mislead).
### Step 2: Understand the Repository Structure
The cloned repository is organized as follows. Note this may be outdated as the project evolves, you should always check the actual structure after cloning.
```
agentscope/
├── src/agentscope/ # Main library source code
│ ├── agent/ # Agent implementations (ReActAgent, etc.)
│ ├── model/ # LLM API wrappers (OpenAI, Anthropic, DashScope, etc.)
│ ├── formatter/ # Message formatters for different models
│ ├── memory/ # Memory implementations
│ ├── tool/ # Tool management and built-in tools
│ ├── message/ # Msg class and content blocks
│ ├── pipeline/ # Multi-agent orchestration (MsgHub, etc.)
│ ├── session/ # Session/state management
│ ├── mcp/ # MCP integration
│ ├── rag/ # RAG functionality
│ ├── realtime/ # Realtime voice interaction
│ ├── tts/ # Text-to-speech
│ ├── evaluate/ # Evaluation tools
│ └── ... # Other modules
├── examples/ # Working examples organized by category
│ ├── agent/ # Different agent types
│ │ └── ...
│ ├── workflows/ # Multi-agent workflows
│ │ └── ...
│ ├── functionality/ # Specific features
│ │ └── ...
│ ├── deployment/ # Deployment patterns
│ ├── integration/ # Third-party integrations
│ ├── evaluation/ # Evaluation examples
│ └── game/ # Game examples (e.g., werewolves)
├── docs/ # Documentation
│ ├── tutorial/ # Tutorial markdown files
│ ├── changelog.md # Version history
│ └── roadmap.md # Development roadmap
└── tests/ # Test files
```
### Step 3: Browse Examples by Category
When looking for similar implementations, **browse the examples directory by category** rather than searching by keywords alone:
1. **Start with the category** that matches your use case:
- Building a specific agent type? → `examples/agent/`
- Multi-agent system? → `examples/workflows/`
- Need a specific feature (MCP, RAG, session)? → `examples/functionality/`
- Deployment patterns? → `examples/deployment/`
2. **List the subdirectories** to see what's available:
- Use file listing tools to explore directory structure
- Read directory names to understand what each example covers
3. **Read example files** to understand implementation patterns:
- Most examples contain a main script and supporting files
- Look for README files in subdirectories for explanations
4. **Combine with text search** when needed:
- After identifying relevant directories, search within them for specific patterns
- Search for class names, method calls, or specific functionality
**Example workflow**:
```
User asks: "Build a FastAPI app with AgentScope"
→ Browse: List files in examples/deployment/
→ Check: Are there any web service examples?
→ Search: Look for "fastapi", "flask", "api", "server" in examples/
→ Read: Found examples and adapt to user's needs
```
## Step 4: Verify Functionality Exists
Before implementing custom solutions, verify if AgentScope already provides the functionality:
1. **List required functionalities** (e.g., session management, MCP integration, RAG)
2. **Check if provided**:
- Browse `examples` for examples
- Search tutorial documentation in `docs/tutorial/`
- Use the provided scripts (see Part 3) to explore API structure
- Read source code in `src/agentscope/` for implementation details
3. **If not provided**: Check how to customize by reading base classes and inheritance patterns in source code
### Step 5: Make a Plan
Always create a plan before coding:
1. Identify what AgentScope components you'll use
2. Determine what needs custom implementation
3. Outline the architecture and data flow
4. Consider edge cases and error handling
### Step 6: Code with API Reference
When writing code:
1. **Check docstrings and arguments** before using any class/method
- Read source code files to see signatures and documentation, or
- Use the provided scripts to view module/class structures
- **NEVER** make up classes, methods, or arguments
2. **Check parent classes** - A class's functionality includes inherited methods
3. **Manage lifecycle** - Clean up resources when needed (close connections, release memory)
### Common Pitfalls to Avoid
- ❌ Guessing API signatures without checking documentation
- ❌ Implementing features that already exist in AgentScope
- ❌ Mixing incompatible Model and Formatter (e.g., OpenAI model with DashScope formatter)
- ❌ Forgetting to await async agent calls
- ❌ Not checking parent class methods when searching for functionality
- ❌ Searching by keywords only without browsing the organized examples directory structure
## Resources
This section lists all available resources for working with AgentScope.
### Official Documentation
- **[Tutorial](https://agentscope.ai/docs/)**: Comprehensive step-by-step guide covering most functionalities in detail. This is the primary resource for learning AgentScope.
- **AgentScope 2.0 docs (mirrored offline)**: The full v2 documentation site (<https://docs.agentscope.io>) has been captured verbatim and consolidated into this skill's `references/v2-*.md` and `references/studio-rest-api.md` (snapshot 2026-06-22). Prefer these for offline, no-hallucination lookup of v2 building blocks, deployment, and the hosted Agent Service REST API; fall back to the live site for newer content.
### GitHub Resources
- **[Main Repository](https://github.com/agentscope-ai/agentscope)**: Source code, examples, and documentation
- **[Project Board](https://github.com/orgs/agentscope-ai/projects/2)**: Official development roadmap and task tracking
- **[Design Discussions](https://github.com/agentscope-ai/agentscope/discussions/categories/agentscope-design-book)**: In-depth explanations about specific modules/functions/components
### Repository Structure
When the repository is cloned locally, the following structure is available for reference:
- **`src/agentscope/`**: Main library source code
- Read this for API implementation details
- Check docstrings for parameter descriptions
- Understand inheritance hierarchies
- **`examples/`**: Working examples demonstrating features
- Start here when building similar applications
- Examples cover: basic agents, multi-agent systems, tool usage, deployment patterns
- **`docs/tutorial/`**: Tutorial documentation source files
- Markdown files explaining concepts and usage
- More detailed than README files
### Scripts
Located in `scripts/` directory of this skill.
- `view_pypi_latest_version.sh`: View the latest version of AgentScope on PyPI.
```bash
cd /path/to/this/skill/directory/scripts/
bash view_pypi_latest_version.sh
```
- `view_module_signature.py`: Explore the structure of AgentScope modules, classes, and methods.
**Search strategy**: Use deep-first search - start broad, then narrow down:
1. `agentscope` → see all submodules
2. `agentscope.agent` → see agent-related classes
3. `agentscope.agent.ReActAgent` → see specific class methods
```bash
cd /path/to/this/skill/directory/scripts/
# View top-level module
python view_module_signature.py --module agentscope
# View specific submodule
python view_module_signature.py --module agentscope.agent
# View specific class
python view_module_signature.py --module agentscope.agent.ReActAgent
```
## Reference
Located in `references/` directory of this skill.
**Hand-curated playbooks:**
- **`multi_agent_orchestration.md`**: Multi-agent orchestration concepts and implementation
- **`deployment_guide.md`**: Deployment patterns and best practices (agentscope-runtime: `AgentApp`, tool sandbox)
**AgentScope 2.0 official docs (mirrored & consolidated, snapshot 2026-06-22):** verbatim capture of <https://docs.agentscope.io>, boilerplate stripped, grouped by topic. Read these to look up exact v2 APIs/behavior instead of guessing.
- **`v2-overview.md`**: What's new in 2.0 / quickstart / change-log / FAQ. Start here for the 2.0 mental model and migration deltas from 1.0.
- **`v2-building-blocks.md`**: The 8 core building blocks — `agent`, `model`, `message-and-event`, `tool`, `context`, `middleware`, `permission-system`, `workspace`. The main "how to use the v2 library" reference.
- **`v2-deploy.md`**: Deploying agents as a service — `agent-service` (multi-tenant, multi-session HTTP service) and `agent-team` (leader/worker team tools).
- **`studio-rest-api.md`**: Condensed REST API reference for the hosted Studio / Agent Service (29 endpoints across agent/chat/credential/model/schedule/sessions/workspace) plus the key data models. Use when integrating with the hosted service over HTTP.
> Note (project context · 2026-06-25 reframe): in this repo AgentScope is the **unified generation framework across all three tiers** (Tier0/1/2), reached via the AgentScope Service `/chat`; SAA / Spring AI Alibaba is de-prioritized to a long-term pluggability-validation target, **no longer the mainline**. Project-specific AgentScope facts (per-POST hot-config, the `/agent`+`/session`+`/workspace` config surfaces, the service-mode `_extract_function_tools` mcp-discard gap, cheap-worker/tier2 wiring) live in [`.agents/knowledge/agentscope-2.0-facts.md`](../../../.agents/knowledge/agentscope-2.0-facts.md). Installed source of truth = the `agentscope` package under `cheap-worker/.venv` (v2.0.2); a full repo clone additionally provides `examples/`.

View File

@ -1,154 +0,0 @@
# Deployment Guide
In agent application, [agentscope-runtime](https://github.com/agentscope-ai/agentscope-runtime) addresses three critical production deployment challenges:
* Deployment: Unified `AgentApp` interface abstracts deployment targets (local, Docker, K8s, serverless, etc.)
* Security Risks: Sandboxed execution environment isolate tool calls (Python, shell, browser, filesystem, etc.)
## Quickstart
```bash
uv pip install agentscope-runtime
# or
# pip install agentscope-runtime
```
## Deployment
AgentScope Runtime provides `AgentApp`, a FastAPI-based service wrapper that turns your agents into production-ready APIs with streaming responses, health checks, and lifecycle management. It supports multiple deployment targets from local development to cloud platforms.
> Note: The `AgentApp` provides a unified interface for deployment, but you can also choose to deploy your agent service using your own FastAPI server or other web frameworks if you prefer.
### Complete Example
The following example can also be found in the README.md of the [agentscope-runtime repository](https://github.com/agentscope-ai/agentscope-runtime)
```python
import os
from contextlib import asynccontextmanager
from fastapi import FastAPI
from agentscope.agent import ReActAgent
from agentscope.model import DashScopeChatModel
from agentscope.formatter import DashScopeChatFormatter
from agentscope.tool import Toolkit, execute_python_code
from agentscope.pipeline import stream_printing_messages
from agentscope.memory import InMemoryMemory
from agentscope.session import RedisSession
from agentscope_runtime.engine import AgentApp
from agentscope_runtime.engine.schemas.agent_schemas import AgentRequest
# 1. Define lifespan manager
@asynccontextmanager
async def lifespan(app: FastAPI):
"""Manage resources during service startup and shutdown"""
# Startup: Initialize Session manager
import fakeredis
fake_redis = fakeredis.aioredis.FakeRedis(decode_responses=True)
# NOTE: This FakeRedis instance is for development/testing only.
# In production, replace it with your own Redis client/connection
# (e.g., aioredis.Redis)
app.state.session = RedisSession(connection_pool=fake_redis.connection_pool)
yield # Service is running
# Shutdown: Add cleanup logic here (e.g., closing database connections)
print("AgentApp is shutting down...")
# 2. Create AgentApp instance
agent_app = AgentApp(
app_name="Friday",
app_description="A helpful assistant",
lifespan=lifespan,
)
# 3. Define request handling logic
@agent_app.query(framework="agentscope")
async def query_func(
self,
msgs,
request: AgentRequest = None,
**kwargs,
):
session_id = request.session_id
user_id = request.user_id
toolkit = Toolkit()
toolkit.register_tool_function(execute_python_code)
agent = ReActAgent(
name="Friday",
model=DashScopeChatModel(
"qwen-turbo",
api_key=os.getenv("DASHSCOPE_API_KEY"),
stream=True,
),
sys_prompt="You're a helpful assistant named Friday.",
toolkit=toolkit,
memory=InMemoryMemory(),
formatter=DashScopeChatFormatter(),
)
agent.set_console_output_enabled(enabled=False)
# Load state
await agent_app.state.session.load_session_state(
session_id=session_id,
user_id=user_id,
agent=agent,
)
async for msg, last in stream_printing_messages(
agents=[agent],
coroutine_task=agent(msgs),
):
yield msg, last
# Save state
await agent_app.state.session.save_session_state(
session_id=session_id,
user_id=user_id,
agent=agent,
)
# 4. Run the application
agent_app.run(host="127.0.0.1", port=8090)
```
## Tool Sandbox
Tool Sandbox provides secure, isolated environments for executing code and tools without affecting your system. It supports multiple sandbox types including base Python/shell execution, GUI operations, browser automation, filesystem access, and mobile interactions, with both synchronous and asynchronous APIs.
### Complete Example
```python
# --- Synchronous version ---
from agentscope_runtime.sandbox import BaseSandbox
with BaseSandbox() as box:
# By default, pulls `agentscope/runtime-sandbox-base:latest` from DockerHub
print(box.list_tools()) # List all available tools
print(box.run_ipython_cell(code="print('hi')")) # Run Python code
print(box.run_shell_command(command="echo hello")) # Run shell command
input("Press Enter to continue...")
# --- Asynchronous version ---
from agentscope_runtime.sandbox import BaseSandboxAsync
async with BaseSandboxAsync() as box:
# Default image is `agentscope/runtime-sandbox-base:latest`
print(await box.list_tools_async()) # List all available tools
print(await box.run_ipython_cell(code="print('hi')")) # Run Python code
print(await box.run_shell_command(command="echo hello")) # Run shell command
input("Press Enter to continue...")
```
## Further Reading
* [AgentScope-Runtime Documentation](https://runtime.agentscope.io/en/intro.html)
* [AgentScope-Runtime GitHub Repository](https://github.com/agentscope-ai/agentscope-runtime)

View File

@ -1,86 +0,0 @@
# Multi-Agent Orchestration
There are two types of multi-agent orchestrations:
- Master-worker: a master agent assigns tasks to multiple worker agents, and the worker agents only report to the master agent.
- Peer-to-peer (or conversational): multiple agents interact with each other, and each agent can perceive the information from different identities in the conversation.
## Master-Worker
In AgentScope, the master-worker orchestration can be implemented by wrapping the worker agents as tools for the master agent.
The worker agents can be designed to perform specific tasks, or a unified worker agent can be assigned with different tasks by providing different system prompts or tools.
The following is an example of how to wrap a worker agent as a tool for the master agent.
> Note: the tool name, input arguments, and output organization of the worker agent can be customized as needed.
```python
from agentscope.pipeline import stream_printing_messages
from agentscope.tool import ToolResponse, Toolkit, execute_shell_command
from agentscope.agent import ReActAgent
from agentscope.message import Msg
from typing import AsyncGenerator
async def create_worker(task: str) -> AsyncGenerator[ToolResponse, None]:
"""{description}
Args:
task (`str`):
The task to be performed by the worker agent.
"""
toolkit = Toolkit()
toolkit.register_tool_function(execute_shell_command)
agent = ReActAgent(...)
# We disable the terminal printing to avoid messy outputs
agent.set_console_output_enabled(False)
async for msg, _ in stream_printing_messages(
agents=[agent],
coroutine_task=agent(
# Wrap the task into a user Msg object
Msg("user", f"Please perform the following task: {task}", "user")
),
):
# Optionally, you can process the message here before yielding it to the master agent
# to control the information exposed to the master agent. For example, filter out the
# reasoning process and only expose the final action to the master agent.
yield msg
```
## Peer-to-Peer
Because agentscope supports explicit message passing, the peer-to-peer orchestration can be implemented by allowing multiple agents to perceive the messages from each other.
Additionally, the `pipeline` module provides different syntactic sugers to facilitate the implementation of different conversation patterns among multiple agents, such as broadcasting, fan-out, and so on.
The following is an example of how to implement a peer-to-peer conversation among multiple agents.
```python
from agentscope.pipeline import MsgHub
... # other imports
alice = ReActAgent(...)
bob = ReActAgent(...)
charlie = ReActAgent(...)
# Create a message hub
async with MsgHub(
participants=[alice, bob, charlie],
# The announcement message will be broadcasted to all participants at the beginning of the conversation
announcement=Msg(
"user",
"Now introduce yourself in one sentence, including your name, age and career.",
"user",
),
) as hub:
# Group chat without manual message passing
await alice()
await bob()
await charlie()
```
## Further Reading
More information about multi-agent orchestration or pipeline can be found in the following references:
- Tutorial of pipeline:
- [Online link](https://doc.agentscope.io/tutorial/task_pipeline.html)
- [Source Code]({path_to_agentscope_repo}/agentscope/docs/tutorial/en/src/task_pipeline.py)

View File

@ -1,397 +0,0 @@
<!-- AgentScope 2.0 Studio/Agent Service REST API 收敛精炼 · 抓取2026-06-22 · 29端点压实自官方 api-reference -->
# AgentScope Studio / Agent Service REST API 参考(精炼版)
> 本文档是 AgentScope **2.0**OpenAPI `info.version = 2.0.1`spec 路径 `/v2/deploy/openapi.json`,标题 `AgentScope`)托管式 Agent ServiceStudio 后端REST API 的精炼速查。源自 29 篇官方 API 原文按资源域归并整理YAML 已翻译为可读字段表,重复的脚手架已去重。
## 总览与全局约定
- **鉴权**:所有端点(除明确说明外)均要求请求头 `x-user-id`string必填——调用方用户 ID。这是临时的基于 header 的身份机制,未来将替换为 JWT。下文各端点**不再重复列出该 header**。
- 例外:少数纯前端表单 schema 端点不要求 `x-user-id`(见下文 `GET /agent/schema``GET /credential/schemas``GET /model/`),它们无鉴权头。
- **全局错误响应**:所有端点都可能返回:
- `404 Not found`——资源不存在或不属于当前用户。
- `422 Validation Error`——请求校验失败,响应体为 `HTTPValidationError``{ detail: ValidationError[] }`,其中 `ValidationError = { loc, msg, type, input?, ctx? }`)。
- 下文各端点**不再重复列出 404/422 与 ValidationError schema**。
- **成功状态码约定**:创建类返回 `201`,删除类返回 `204`(无响应体),其余 GET/PATCH/POST 返回 `200`
- **资源域(章节)**Agent / Chat / Credential / Model / Schedule / Sessions / Workspace。
- **所有权与多用户隔离**:列表类端点只返回当前 `x-user-id` 名下记录;按 id 操作的端点对非本人资源返回 404。
- **ORM 记录通用字段**`*Record`AgentRecord/CredentialRecord/ScheduleRecord/SessionRecord/TeamRecord都含 `id` `created_at` `updated_at` `user_id`,详见末尾「关键数据模型」。
---
## Agent
管理 Agent 配置(身份 + 上下文管理 + ReAct 循环)。
### POST /agent/ — 创建 Agent
创建并持久化一个新的 Agent 配置。
- **请求体** `CreateAgentRequest`
- `name` string 必填 —— 显示名。
- `system_prompt` string 默认 `"You're a helpful assistant."` —— 基础系统提示。
- `context_config` `ContextConfig` —— 上下文窗口管理配置。
- `react_config` `ReActConfig` —— ReAct 循环配置。
- **返回** `201``CreateAgentResponse``agent_id` string —— 服务端分配的 Agent ID。
### GET /agent/ — 列出全部 Agent
返回当前用户名下所有 Agent 记录。
- **参数**:无(仅 `x-user-id`)。
- **返回** `200``ListAgentsResponse``agents` `AgentRecord[]``total` int。
### GET /agent/schema — 获取 Agent 表单的 JSON Schema 片段
返回前端渲染「创建/编辑 Agent 表单」用的三段自包含 JSON Schema身份 / 上下文配置 / ReAct 配置),刻意拆成三段而非单个带 `$ref``AgentData`,便于前端按路径派生 i18n key。
- **参数**:无(**无 `x-user-id`**)。
- **返回** `200``AgentSchemaResponse``identity` object`name`/`system_prompt` 的 schema`context_config` object、`react_config` object均必填
### PATCH /agent/{agent_id} — 局部更新 Agent
仅更新请求体中出现的字段,其余保持原值。
- **路径**`agent_id` string 必填。
- **请求体** `UpdateAgentRequest`(全部可空、可省略):`name` string?、`system_prompt` string?、`context_config` `ContextConfig`?、`react_config` `ReActConfig`?。
- **返回** `200``AgentRecord`(更新后的完整记录)。
### DELETE /agent/{agent_id} — 删除 Agent
永久删除 Agent 配置。
- **路径**`agent_id` string 必填。
- **返回** `204`(无响应体)。
---
## Chat
### POST /chat/ — 触发一次聊天运行fire-and-forget
为指定 session 触发一次聊天运行。运行作为**后台任务**执行;运行期间产生的事件发布到消息总线,并下发给任何活跃的 `GET /sessions/{session_id}/stream` SSE 订阅者——**调用方不会从本端点响应体收到事件**。
- 接受的 `input` 取值:
- `Msg` / `Msg[]` —— 新的用户消息。
- `UserConfirmResultEvent` / `ExternalExecutionResultEvent` —— 恢复一个被暂停的工具调用human-in-the-loop
- `null` —— 从当前状态继续。
- **请求体** `ChatRequest`
- `agent_id` string 必填。
- `session_id` string 必填 —— 目标 session。
- `input` 必填(可为 null—— 上述四类之一。
- **返回** `200``ChatTriggerResponse``status` string默认 `"started"`,触发成功时恒为 `"started"`)、`session_id` string 必填(回显)。
---
## Credential
存储模型供应商凭据(如 API key。凭据 `data` 为自由 object按凭据类型 schema 校验)。
### POST /credential/ — 创建凭据
- **请求体** `CreateCredentialRequest``data` object 必填 —— 凭据载荷(如 API keys
- **返回** `201``CreateCredentialResponse``credential_id` string。
### GET /credential/ — 列出全部凭据
返回当前用户名下所有凭据记录。
- **参数**:无。
- **返回** `200``ListCredentialsResponse``credentials` `CredentialRecord[]``total` int。
### GET /credential/schemas — 列出所有凭据类型的 JSON Schema
返回所有已注册凭据类型的 JSON Schema供前端动态渲染凭据创建表单。
- **参数**:无(**无 `x-user-id`**)。
- **返回** `200``ListCredentialSchemasResponse``schemas` object[](每个类型一个 JSON Schema
### PATCH /credential/{credential_id} — 更新凭据
替换已有凭据的整个载荷。
- **路径**`credential_id` string 必填。
- **请求体** `UpdateCredentialRequest``data` object 必填 —— 新载荷。
- **返回** `200``CredentialRecord`
### DELETE /credential/{credential_id} — 删除凭据
- **路径**`credential_id` string 必填。
- **返回** `204`
---
## Model
### GET /model/ — 列出某凭据类型下的全部候选模型
返回指定供应商(凭据类型)下的所有候选模型。
- **查询参数**`provider` string 必填 —— 供应商 / 凭据类型名。
- **无 `x-user-id`**
- **返回** `200``ListModelsResponse``models` `ModelCard[]``total` int。
---
## Schedule
定时调度cron + APScheduler到点自动为指定 Agent 创建 session 并触发运行。
### POST /schedule/ — 创建调度
创建调度并注册到调度器。指定的 Agent 不存在时返回 404。
- **请求体** `CreateScheduleRequest`
- `name` string 必填 —— 显示名。
- `description` string 默认 `""`
- `cron_expression` string 必填 —— 标准 5 段 cron`'0 9 * * 1-5'`
- `timezone` string 默认 `"UTC"` —— IANA 时区名(如 `Asia/Shanghai`)。
- `agent_id` string 必填 —— 触发时运行的 Agent。
- `chat_model_config` `ChatModelConfig` 必填 —— 自动创建 session 用的模型配置。
- `enabled` bool 默认 `true` —— 创建后是否立即生效。
- `stateful` bool 默认 `false` —— 为 true 时连续执行共享同一 session 上下文。
- `permission_mode` `PermissionMode` 默认 `dont_ask` —— 计划执行期间的权限级别。
- **返回** `201``CreateScheduleResponse``schedule_id` string。
### GET /schedule/ — 列出全部调度
列出当前用户的所有调度(分页列表)。
- **参数**:无。
- **返回** `200``ListSchedulesResponse``schedules` `ScheduleRecord[]``total` int。
### GET /schedule/{schedule_id}/sessions — 列出某调度的执行 session
返回某调度触发过的所有 session按创建时间倒序最新在前。调度不存在返回 404。
- **路径**`schedule_id` string 必填。
- **返回** `200``ScheduleSessionsResponse``sessions` `SessionRecord[]``total` int。
### PATCH /schedule/{schedule_id} — 局部更新调度
省略的字段保持原值。改 `cron_expression``timezone` 会**立即重排** APScheduler job`enabled=false` 会从调度器移除 job 但保留记录,置回 `true` 重新注册。
- **路径**`schedule_id` string 必填。
- **请求体** `UpdateScheduleRequest`(全部可空可省略):`name`?、`description`?、`cron_expression`?、`timezone`?、`enabled` bool?、`stateful` bool?、`permission_mode` `PermissionMode`?。
- **返回** `200``ScheduleRecord`
### DELETE /schedule/{schedule_id} — 删除调度
永久删除记录并注销 APScheduler job。调度不存在返回 404。
- **路径**`schedule_id` string 必填。
- **返回** `204`
---
## Sessions
会话:每个 `(user_id, agent_id, workspace_id)` 三元组至多一个 session。
### POST /sessions/ — 创建或恢复session
为指定 Agent + workspace 创建 session相同三元组的二次调用会**更新已有 session 而非新建**。Agent 或凭据不存在返回 404。
- **请求体** `CreateSessionRequest`
- `agent_id` string 必填。
- `workspace_id` string? —— session 所属 workspace。
- `name` string? —— 显示名,省略时默认当前时间。
- `chat_model_config` `ChatModelConfig`? —— 模型供应商与参数,可后续 PATCH 设置。
- `fallback_chat_model_config` `ChatModelConfig`? —— 主模型失败时的兜底模型,可后续 PATCH。
- **返回** `201``CreateSessionResponse``session_id` string。
### GET /sessions/ — 列出某 Agent 的全部 session含富信息 SessionView
返回该 Agent 的所有 session每条为富化的 `SessionView`,一次性带齐聊天 UI 渲染所需的三块session 记录(含 `state`)、当前是否有活跃运行、以及(参与 team 时)解析后的 team 详情leader + members 及其 session id。**消息不含在内**,由 `/sessions/{id}/messages` 分页拉取。Agent 不存在返回 404。
- **查询参数**`agent_id` string 必填。
- **返回** `200``ListSessionsResponse``sessions` `SessionView[]``total` int。
### GET /sessions/{session_id}/messages — 列出某 session 的消息
返回 session 已持久化的消息(按时间顺序)。
- **路径**`session_id` string 必填。
- **查询参数**`agent_id` string 必填;`offset` int ≥0 默认 0`limit` int 1200 默认 50。
- **返回** `200``ListMessagesResponse``messages` array消息时间顺序item 无 schema 约束)、`is_running` boolsession 当前是否在运行)。
### GET /sessions/{session_id}/stream — 订阅 session 事件流SSE
订阅 session 的实时事件流,返回 `text/event-stream`:先回放当前运行 replay log 中的缓冲事件(若有运行进行中或刚结束),再实时推送 `ChatService.run` 产生的事件。连接保持打开直到客户端断开——同一 session 的后续运行经同一连接下发。每 30 秒发送一帧心跳注释(`:\n\n`)以穿透反向代理保活。
- **路径**`session_id` string 必填。
- **查询参数**`agent_id` string 必填(用于归属校验)。
- **返回** `200`AgentEvent 帧 + 周期心跳组成的 SSE 流(`StreamingResponse`;响应 schema 为空)。
### PATCH /sessions/{session_id} — 更新 session
更新已有 session 的模型配置及名称、权限模式。session/agent/凭据不存在返回 404。
- **路径**`session_id` string 必填。
- **查询参数**`agent_id` string 必填。
- **请求体** `UpdateSessionRequest`(省略保持原值):
- `name` string?。
- `chat_model_config` `ChatModelConfig`? —— 整体替换;传 null 清空,省略不变。
- `fallback_chat_model_config` `ChatModelConfig`? —— 同上。
- `permission_mode` `PermissionMode`?。
- **返回** `200``SessionRecord`(更新后完整记录)。
### DELETE /sessions/{session_id} — 删除 session
永久删除 session 及其全部关联状态。
- **路径**`session_id` string 必填。
- **查询参数**`agent_id` string 必填 —— session 所属 Agent。
- **返回** `204`
---
## Workspace
会话级 workspace 的能力装配MCP 客户端 与 Skill。所有 workspace 端点都需查询参数 `agent_id` + `session_id`(均 string 必填)。
### POST /workspace/mcp — 添加 MCP
向 session 的 workspace 添加一个 MCP 客户端。
- **查询参数**`agent_id``session_id`(必填)。
- **请求体** `MCPClient`(见末尾模型)。
- **返回** `201`(响应体无 schema
### GET /workspace/mcp — 列出 MCP
返回所有 MCP 客户端,附带实时工具列表与健康状态。
- **查询参数**`agent_id``session_id`(必填)。
- **返回** `200``MCPClientStatus[]`= `MCPClient` + `is_healthy` bool + `tools` `ToolInfo[]`)。
### DELETE /workspace/mcp/{mcp_name} — 移除 MCP
按名称从 session 的 workspace 移除一个 MCP 客户端。
- **路径**`mcp_name` string 必填。
- **查询参数**`agent_id``session_id`(必填)。
- **返回** `204`
### POST /workspace/skill — 添加 Skill
从给定路径向 session 的 workspace 添加一个 skill。
- **查询参数**`agent_id``session_id`(必填)。
- **请求体** `AddSkillRequest``skill_path` string 必填。
- **返回** `201`(响应体无 schema
### GET /workspace/skill — 列出 Skill
返回 session 的 workspace 中所有可用 skill。
- **查询参数**`agent_id``session_id`(必填)。
- **返回** `200``Skill[]`
### DELETE /workspace/skill/{skill_name} — 移除 Skill
按名称从 session 的 workspace 移除一个 skill。
- **路径**`skill_name` string 必填。
- **查询参数**`agent_id``session_id`(必填)。
- **返回** `204`
---
## 关键数据模型
> 反复被引用的核心 schema各捕获一次。`?` 表示可空 / 可省略。
### Agent 相关
**AgentData**Agent 数据模型)
- `id` string —— 唯一 Agent id。
- `name` string 必填。
- `system_prompt` stringtextarea默认 `"You're a helpful assistant."`
- `context_config` `ContextConfig` 必填。
- `react_config` `ReActConfig` 必填。
**AgentRecord**ORM 记录):`id``created_at``updated_at``user_id`(必填)、`source` enum `user`|`team`(默认 `user`)、`data` `AgentData`(必填)。
**ContextConfig**(上下文窗口管理)
- `trigger_ratio` number 默认 0.80 < x < 0.9)—— 触发压缩的占用比
- `reserve_ratio` number 默认 0.10 < x < 0.9)—— 预留比
- `compression_prompt` stringtextarea—— 压缩/续写摘要提示(含默认 `<system-hint>…` 模板)。
- `summary_template` stringtextarea—— 摘要回填模板(默认含 `{task_overview}` `{current_state}` `{important_discoveries}` `{next_steps}` `{context_to_preserve}` 占位)。
- `summary_schema` object —— 摘要结构 schema。
- `tool_result_limit` int 默认 3000 —— 工具结果最大 token 数,超出截断。
**ReActConfig**(推理配置)
- `max_iters` int 默认 20 —— 单次回复内最大推理-行动迭代数。
- `stop_on_reject` bool 默认 false —— 工具被拒绝执行时是否停止回复。
**AgentSchemaResponse**(前端表单 schema`identity` / `context_config` / `react_config` 三个自包含 object均必填
### Credential / Model 相关
**CredentialRecord**`id``created_at``updated_at``user_id``data` object必填自由载荷如 API keys
> 凭据类型由 `GET /credential/schemas` 返回的注册类型 schema 定义;`data` 字段对应这些 schema如各供应商 API key 字段)。
**ChatModelConfig**(模型配置,被 session/schedule 复用)
- `type` string 必填 —— 模型/供应商类型。
- `credential_id` string 必填 —— 引用的凭据。
- `model` string 必填 —— 模型名。
- `parameters` object 必填 —— 模型调用参数。
**ModelCard**(候选模型)
- `type` const `chat_model`
- `name` string 必填、`label` string 必填。
- `status` enum `active`|`deprecated`|`sunset` 必填。
- `deprecated_at` datetime? —— 弃用时间。
- `input_types` string[] 默认 `["text/plain"]``output_types` string[] 默认 `["text/plain"]`
- `context_size` int >0 必填 —— 上下文大小。
- `output_size` int >0 必填 —— 最大输出 token 数。
- `parameter_schema` object 必填 —— 参数 schema。
- `parameters_overrides` object<object> 必填 —— 参数覆盖。
### Schedule 相关
**ScheduleData**(调度配置)
- `name` string 必填、`description` string 默认 `""`
- `enabled` bool 默认 true —— 禁用则保留记录但不触发。
- `timezone` string 默认 `"Asia/Shanghai"`IANA 时区)。
- `cron_expression` string 必填 —— 标准 5 段 cron。
- `started_at` datetime、`ended_at` datetime?。
- `chat_model_config` `ChatModelConfig` 必填。
- `stateful` bool 默认 false —— 连续执行是否共享 session 上下文。
- `permission_mode` `PermissionMode` 默认 `dont_ask`
- `source` `ScheduleSource` 默认 `USER``source_session_id` string 默认 `""`
**ScheduleRecord**`id``created_at``updated_at``user_id`owner`agent_id``data` `ScheduleData`(均必填,除 id/时间戳外)。
**ScheduleSource** enum`USER`(用户经 UI 手建)、`AGENT`Agent 经工具调用自动创建)。
### Session / Team / State 相关
**SessionConfig**`workspace_id` string 必填、`name` string、`chat_model_config` `ChatModelConfig`?、`fallback_chat_model_config` `ChatModelConfig`?。
**SessionRecord**`id``created_at``updated_at``user_id``agent_id``source` `SessionSource`(默认 `user`)、`source_schedule_id` string?、`team_id` string?、`config` `SessionConfig`(必填)、`state` `AgentState`
**SessionSource** enum`user``schedule`
**SessionView**`GET /sessions/` 富化条目):`session` `SessionRecord`(必填,内联 state`is_running` bool必填`team` `TeamDetailResponse`?`team_id` 存在时解析的 team 详情,否则 null
**AgentState**(持久化的 Agent 状态):`session_id``summary`string 或 TextBlock/DataBlock 数组,默认 `""`)、`context` `Msg[]``reply_id``cur_iter` int 默认 0、`permission_context` `PermissionContext``tool_context` `ToolContext``tasks_context` `TaskContext`
**TeamData**`name` string 必填、`description` string 默认 `""`(注入每个成员 system prompt 的团队目标/共享上下文)、`member_ids` string[]worker agent id每个 worker `source='team'` 且唯一一个 session
**TeamRecord**`id``created_at``updated_at``user_id``session_id`leader 的 session`data` `TeamData`必填。team 成员是 session 级leader 由 `session_id` 标识worker 由 agent id 标识。
**TeamDetailResponse**`team` `TeamRecord`(必填)、`leader_agent` `AgentRecord`?(由 team `session_id` → session.agent_id 解析)、`members` `TeamMemberView[]`
**TeamMemberView**`agent` `AgentRecord`必填worker`session_id` string?worker 的 session idworker 无 session 时为 null
### 消息与内容块Chat / AgentState 复用)
**Msg**(消息类):`name` string 必填、`content` array 必填(由下列 block 组成)、`role` enum `user`|`assistant`|`system` 必填、`id``metadata` object、`created_at``finished_at` string?、`usage` `Usage`?。
`content` 可含的 block均有 `type` 判别字段 + `id`
- **TextBlock** `type=text``text` string 必填。
- **ThinkingBlock** `type=thinking``thinking` string 必填;允许额外 provider 字段(如 Anthropic `signature`)。
- **HintBlock** `type=hint``hint`string 或 TextBlock/DataBlock 数组)必填、`source` string? —— 推理-行动循环中给 LLM 的提示,转为 user 消息下发。
- **ToolCallBlock** `type=tool_call``id`/`name`/`input`(string) 必填、`state` `ToolCallState` 默认 `pending``suggested_rules` `PermissionRule[]`;允许额外字段(如 OpenAI `call_id`)。
- **ToolResultBlock** `type=tool_result``id`/`name`/`output` 必填output 为 string 或 TextBlock/DataBlock 数组)、`state` `ToolResultState` 默认 `running`
- **DataBlock** `type=data``source``Base64Source` | `URLSource`)必填、`name` string? —— 二进制内容(图/音/视频等)。
**Base64Source** `type=base64``data` string 必填、`media_type` string 必填。
**URLSource** `type=url``url`uri必填`media_type` string 必填。
**Usage**`input_tokens` int、`output_tokens` int均必填
### HITL 恢复事件Chat input
**UserConfirmResultEvent** `type=USER_CONFIRM_RESULT``reply_id` string 必填、`confirm_results` `ConfirmResult[]` 必填、`id`/`created_at`
- **ConfirmResult**`confirmed` bool 必填、`tool_call` `ToolCallBlock` 必填、`rules` `PermissionRule[]`?。
**ExternalExecutionResultEvent** `type=EXTERNAL_EXECUTION_RESULT``reply_id` string 必填、`execution_results` `ToolResultBlock[]` 必填、`id`/`created_at`
### 权限模型
**PermissionMode** enum`default`(每次操作都需显式授权,最安全)、`accept_edits`(工作目录内自动放行读/写/文件系统命令)、`explore`(只读:放行 Read/Grep/Glob拒绝 Write/Edit/Bash`bypass`(全部自动放行,无权限检查,沙箱/全信任)、`dont_ask`(把所有 ASK 决策转为 DENY用于无人值守的计划任务
**PermissionBehavior** enum`allow``deny``ask``passthrough`(交回权限引擎继续规则匹配)。
**PermissionRule**`tool_name` string 必填、`rule_content` string?(必填字段但可为 null`behavior` `PermissionBehavior` 必填、`source` string 必填。`rule_content` 语义随工具不同Bash=命令子串匹配Write/Read=文件 glob其他工具=工具自定义过滤模式。
**PermissionContext**`mode` `PermissionMode`(默认 `default`)、`working_directories` map<string,`AdditionalWorkingDirectory`>`allow_rules`/`deny_rules`/`ask_rules` 均为 map<string,`PermissionRule[]`>
**AdditionalWorkingDirectory**`path` string 必填、`source` string 必填——纳入权限范围的额外目录ACCEPT_EDITS 下判定可自动放行的路径)。
### 工具与任务上下文
**ToolContext**`max_cache_files` int 默认 100>1`max_cache_bytes` number 默认 25000>10000`read_file_cache` `ReadCacheEntry[]``activated_groups` string[]。
**ReadCacheEntry**`lines` string[]、`updated_at` number、`bytes` number、`file_path` string均必填
**TaskContext**`tasks` `Task[]`
**Task**`subject`/`description`/`metadata` 必填、`created_at``state` enum `pending`|`in_progress`|`completed`(默认 `pending`)、`id``owner` string?、`blocks` string[]、`blocked_by` string[]。
**ToolCallState** enum`pending`|`asking`|`allowed`|`submitted`|`finished`
**ToolResultState** enum`success`|`error`|`interrupted`|`denied`|`running`
### WorkspaceMCP 与 Skill
**MCPClient**(统一 MCP 客户端)
- `name` string 必填 —— MCP 名。
- `is_stateful` bool 必填 —— 是否有状态连接(需显式 connect/close。STDIO MCP 必须有状态HTTP MCP 可有/无状态。
- `mcp_config`(必填,`type` 判别):`StdioMCPConfig``type=stdio_mcp`)或 `HttpMCPConfig``type=http_mcp`)。
- `enable_tools` string[]?、`disable_tools` string[]?、`execution_timeout` number?。
**MCPClientStatus** = `MCPClient` + `is_healthy` bool默认 false+ `tools` `ToolInfo[]`(实时工具列表)。
**StdioMCPConfig** `type=stdio_mcp``command` string 必填、`args` string[]?、`env` map<string,string>?、`cwd` string/path?、`encoding_error_handler` enum `strict`|`ignore`|`replace`(默认 `strict`)。
**HttpMCPConfig** `type=http_mcp``url` string 必填、`headers` map<string,string>?、`timeout` number? 默认 30
**ToolInfo**`name` string 必填、`description` string?。
**Skill**Agent skill`name`/`description`/`dir`/`markdown`/`updated_at`(number) 均必填。
**AddSkillRequest**`skill_path` string 必填。

View File

@ -1,893 +0,0 @@
<!-- AgentScope 2.0 官方文档收敛 · 部署篇 · 抓取2026-06-22 · 含: agent-service(托管HTTP服务)/agent-team(Leader-worker团队) -->
<!-- ===== source: v2/deploy/agent-service.md ===== -->
# Agent Service
> Host your agent as a multi-tenant, multi-session HTTP service
Agent Service is the FastAPI-based hosting layer that turns AgentScope agents into a **multi-tenant, multi-session HTTP service**. It owns everything *around* the agent — request routing, per-user resource lifecycle, session state, persistence, scheduling, and tool offloading — so that the agent code you wrote against [`Agent`](/v2/building-blocks/agent) can serve production traffic without being rewritten.
What sets it apart:
* **Production backbone for live agents** — agent runs, background tasks, schedules, and the tool/MCP/skill/workspace lifecycle are managed end-to-end, with session streams that fan out to multiple subscribers and replay buffered history on reconnect.
* **Schema-driven frontend** — credentials publish JSON schemas and models expose declarative cards (input/output types, context size, parameter schemas), so the UI can render forms and capability badges without coupling to provider-specific code.
* **Multi-tenant by construction** — credentials, agents, sessions, schedules, and messages are all owned by the request's `user_id`, and ownership is enforced at the routing layer — one deployment serves many users with no per-tenant code paths.
* **Modular and extensible** — authentication, chat protocols, workspace isolation strategy, storage backend, and the set of model providers and credential types are all open at the boundary, swappable without touching framework code.
### Capabilities
| Capability | Description |
| ------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Agent teams | A leader agent spawns worker agents and coordinates them through built-in team tools; see the [Agent Team](/v2/deploy/agent-team) chapter. |
| Workspace management | Pluggable workspace isolation (built-in: per-agent; extensible to per-session or per-user) for the agent's filesystem, MCP clients, and skills. |
| Background task offloading | Long-running tool calls move to background; their results are delivered back through the session's event stream when they finish. |
| Cron scheduling | Time-based agent execution with stateful or stateless sessions; schedules persist across restarts. |
| Session replay | Late-joining clients to the per-session SSE stream receive buffered history before live events, so multiple tabs or a reconnecting frontend stay in sync. |
| Protocol adaptation | Middleware-based conversion to external protocols (AG-UI, A2A, etc.) on top of AgentScope's native event stream. |
| Distributed deployment <Badge color="yellow" size="sm">WIP</Badge> | All shared state lives in Redis (storage + message bus), so multiple worker processes — or multiple nodes — can serve one logical service. |
<Note>
The service does **not** include a built-in user authentication system. It provides a placeholder `X-User-ID` header dependency that you replace with your own auth middleware (JWT, OAuth, session tokens, etc.).
</Note>
## Quickstart
The fastest way to see Agent Service in action is to run the bundled example backend together with the example frontend — both ship inside the AgentScope repo.
### Try the bundled example
The [`examples/agent_service`](https://github.com/agentscope-ai/agentscope/tree/main/examples/agent_service) directory boots a ready-to-use service, and [`examples/web_ui`](https://github.com/agentscope-ai/agentscope/tree/main/examples/web_ui) is a matching React frontend that talks to it. Together they give you a working playground for every capability above in a few minutes.
<Frame caption="Background task offloading — a long-running tool moves to a background watcher; the result later wakes the agent up and the conversation resumes.">
<img src="https://mintcdn.com/agentscope-ai-786677c7/c8Op3M8_8-WXpeVH/images/bg_tool.gif?s=ecba271192d28e8d36ef6ba02ef5cc7f" alt="Background tool offloading and wakeup demo" width="1920" height="1080" data-path="images/bg_tool.gif" />
</Frame>
<Frame caption="Permission control in bypass mode — the agent runs end-to-end without pausing for tool-call confirmations.">
<img src="https://mintcdn.com/agentscope-ai-786677c7/c8Op3M8_8-WXpeVH/images/permission_bypass.gif?s=45da293c3c8b1b819f2e87924652d563" alt="Permission system in bypass mode" width="1920" height="1080" data-path="images/permission_bypass.gif" />
</Frame>
<Frame caption="Task planning — the agent breaks complex work into a tracked plan and updates it as it goes.">
<img src="https://mintcdn.com/agentscope-ai-786677c7/c8Op3M8_8-WXpeVH/images/task.gif?s=d9bf05e01cde9c044cfe558a4de67d2f" alt="Task planning demo" width="1920" height="1080" data-path="images/task.gif" />
</Frame>
<Frame caption="Agent team — a leader agent spawns workers and coordinates them through the built-in team tools.">
<img src="https://mintcdn.com/agentscope-ai-786677c7/c8Op3M8_8-WXpeVH/images/team.gif?s=604f394f94cca0dbca8fdeee75a0f7ff" alt="Agent team coordination demo" width="1920" height="1080" data-path="images/team.gif" />
</Frame>
<Steps>
<Step title="Clone the repository">
```bash
git clone https://github.com/agentscope-ai/agentscope.git
cd agentscope
```
</Step>
<Step title="Start the example backend">
Make sure a local Redis is reachable (the example expects `localhost:6379`), then launch the service:
```bash
cd examples/agent_service
python main.py
```
The service comes up on `http://localhost:8000`.
</Step>
<Step title="Start the example frontend">
In another terminal, install and run the web UI:
```bash
cd examples/web_ui
pnpm install
pnpm dev
```
Open the URL the dev server prints (typically `http://localhost:5173`) and the frontend will connect to the backend you started in step 2.
</Step>
</Steps>
Once both are running, the same UI lets you exercise every capability the service ships with:
* **Permission control** — tools that touch the system pause for confirmation; explore-mode locks the agent to read-only operations.
* **Background task offloading** — long-running tool calls move to the background and their results stream in when they finish, without blocking the conversation.
* **Task planning** — the agent breaks complex work into a tracked plan and updates it as it goes.
* **Agent teams** — a leader agent spawns workers and coordinates them through the team tools.
* **Scheduled runs** — cron-driven agents that fire on their own and report back to the same session stream.
### From your own code
When you want to embed the service in your own deployment instead of running the example, build the FastAPI app yourself with `create_app`. The minimum to get a service running is a storage backend, a message bus, and a workspace manager. The examples below boot a service on port 8000 backed by Redis — pick the workspace backend that matches where you want the agent's tools to execute.
<CodeGroup>
```python Local filesystem
import uvicorn
from agentscope.app import create_app
from agentscope.app.storage import RedisStorage
from agentscope.app.message_bus import RedisMessageBus
from agentscope.app.workspace_manager import LocalWorkspaceManager
# Persistence layer for agents, sessions, credentials, messages, and schedules.
# Its connection pool is opened on app startup and closed on shutdown.
storage = RedisStorage(host="localhost", port=6379)
# Redis-backed message bus: session locks, replay logs, inbox queues, and
# wakeup signals that decouple chat triggering from event delivery and
# let multiple worker processes share one logical service.
message_bus = RedisMessageBus(host="localhost", port=6379)
# Workspace lifecycle — working directory, MCP clients, skills.
# The built-in manager isolates per agent: sessions of the same agent
# share one workspace. Idle workspaces are evicted after `ttl` seconds.
workspace_manager = LocalWorkspaceManager(
basedir="/data/workspaces",
ttl=3600.0,
)
app = create_app(
storage=storage,
message_bus=message_bus,
workspace_manager=workspace_manager,
)
uvicorn.run(app, host="0.0.0.0", port=8000)
```
```python Docker sandbox
import uvicorn
from agentscope.app import create_app
from agentscope.app.storage import RedisStorage
from agentscope.app.message_bus import RedisMessageBus
from agentscope.app.workspace_manager import DockerWorkspaceManager
storage = RedisStorage(host="localhost", port=6379)
message_bus = RedisMessageBus(host="localhost", port=6379)
# Each workspace runs inside its own local Docker container for isolation.
# Per-user/per-agent host workdirs live under `basedir` and are bind-mounted
# into each container.
workspace_manager = DockerWorkspaceManager(basedir="/data/docker-workspaces")
app = create_app(
storage=storage,
message_bus=message_bus,
workspace_manager=workspace_manager,
)
uvicorn.run(app, host="0.0.0.0", port=8000)
```
```python E2B
import uvicorn
from agentscope.app import create_app
from agentscope.app.storage import RedisStorage
from agentscope.app.message_bus import RedisMessageBus
from agentscope.app.workspace_manager import E2BWorkspaceManager
storage = RedisStorage(host="localhost", port=6379)
message_bus = RedisMessageBus(host="localhost", port=6379)
# Each workspace runs inside a remote E2B cloud sandbox.
# Provide `api_key` here or set the `E2B_API_KEY` environment variable.
workspace_manager = E2BWorkspaceManager()
app = create_app(
storage=storage,
message_bus=message_bus,
workspace_manager=workspace_manager,
)
uvicorn.run(app, host="0.0.0.0", port=8000)
```
</CodeGroup>
### create\_app parameters
<ParamField path="storage" type="StorageBase" required>
The storage backend for persisting agents, sessions, credentials, messages, and schedules. Its lifecycle (`__aenter__` / `__aexit__`) is managed by the app lifespan.
</ParamField>
<ParamField path="message_bus" type="MessageBus" required>
Redis-backed primitives — session locks, replay logs, inbox queues, and wakeup signals — that decouple chat triggering from event delivery. Required because every code path that delivers events to the frontend (`POST /chat`, scheduled fires, team messages, background-tool completions) goes through it, and because it is what makes multi-process deployments possible.
</ParamField>
<ParamField path="workspace_manager" type="WorkspaceManagerBase" required>
Manages workspaces (file storage, MCP servers, skills) with TTL-based caching. The built-in `LocalWorkspaceManager` isolates per agent; see [Workspace implementation and isolation](#workspace-implementation-and-isolation) for other strategies.
</ParamField>
<ParamField path="extra_credentials" type="list[Type[CredentialBase]] | None" default="None">
Additional credential types to register. Each class is registered with `CredentialFactory` before the app starts.
</ParamField>
<ParamField path="extra_middlewares" type="list[Middleware] | None" default="None">
Additional ASGI middlewares (e.g., protocol adapters, CORS, auth).
</ParamField>
<ParamField path="extra_agent_middlewares" type="AgentMiddlewareFactory | None" default="None">
Async factory `(user_id, agent_id, session_id) -> Awaitable[list[MiddlewareBase]]` invoked once per agent assembly (per chat turn or scheduled trigger). Returned middlewares are appended to the framework-supplied ones (e.g., `ToolOffloadMiddleware`) before the agent runs, so the factory can produce per-user / per-session middlewares such as audit logging, tenant isolation, or custom auth.
</ParamField>
<ParamField path="extra_agent_tools" type="AgentToolFactory | None" default="None">
Async factory `(user_id, agent_id, session_id) -> Awaitable[list[ToolBase]]` invoked once per agent assembly. Returned tools are merged into the toolkit's `"basic"` group alongside the workspace-derived tools, so tool availability can vary per caller (per-tenant integrations, user-specific credentials).
</ParamField>
<ParamField path="sub_agent_templates" type="list[SubAgentTemplate] | None" default="None">
Reusable blueprints for sub-agent creation within teams. Each template defines a sub-agent *type* (e.g. `"researcher"`, `"coder"`) with pre-configured system prompt, permission context, and task context. When registered, the `AgentCreate` tool exposes a `subagent_type` parameter so the leader agent can route to the appropriate template. See [Custom sub-agent types](/v2/deploy/agent-team#custom-sub-agent-types) for details.
</ParamField>
<ParamField path="title" type="str" default="AgentScope">
OpenAPI title shown in the docs UI.
</ParamField>
<ParamField path="version" type="str" default="2.0.0">
API version shown in the docs UI.
</ParamField>
<Warning>
The default `X-User-ID` header provides no authentication. Replace it with a real auth integration before deploying — see [User authentication](#user-authentication).
</Warning>
### Typical operation flow
Once the server is running, drive it through the resources defined in the resource model. The flow below is the path a chat session usually takes — each step is one or two REST calls.
<Steps>
<Step title="Create an agent">
Register the agent's identity — display name, system prompt, and runtime configuration. The same agent can drive many sessions under different models.
```http
POST /agent
```
</Step>
<Step title="Create and configure a credential">
Discover each provider's form fields with `GET /credential/schemas`, then save the API key. One credential can be reused across many sessions and agents.
```http
GET /credential/schemas
POST /credential
```
</Step>
<Step title="Create a session and select a model">
Create a session bound to the agent and attach a model configuration — provider, model name, parameters, and the credential to call it with. The session owns the runtime state from here on.
```http
POST /sessions
```
</Step>
<Step title="Configure MCPs and skills (optional)">
Attach MCP clients and skills to the session's workspace if the agent needs tools beyond its built-ins. Out of the box, every agent already has access to the workspace's built-in tools (filesystem, shell, search, …), task-planning tools, schedule and background-task controls, and — when the session is a team leader or member — the team coordination tools described in [Agent Team](/v2/deploy/agent-team). Anything you pass via `extra_agent_tools` in `create_app` is merged in alongside.
```http
POST /workspace/mcp
POST /workspace/skill
```
</Step>
<Step title="Start chatting">
Fire a chat run by posting a user `Msg` to `/chat`. The endpoint returns immediately with `{"status": "started", "session_id": "..."}` — events are delivered out-of-band on the per-session SSE stream `GET /sessions/{id}/stream`, which any number of clients can subscribe to and which replays buffered history to late joiners before serving live events.
```http
POST /chat
GET /sessions/{session_id}/stream
```
</Step>
</Steps>
Trigger a run:
```bash
curl -X POST http://localhost:8000/chat \
-H "X-User-ID: alice" \
-H "Content-Type: application/json" \
-d '{
"agent_id": "agent-xxx",
"session_id": "session-xxx",
"input": {
"name": "alice",
"role": "user",
"content": [{"type": "text", "text": "Hello"}]
}
}'
```
Subscribe to the session's event stream in parallel (or before triggering — the stream stays open across runs and broadcasts everything the session produces, including scheduled fires and background-tool completions):
```bash
curl -N -H "X-User-ID: alice" \
"http://localhost:8000/sessions/session-xxx/stream?agent_id=agent-xxx"
```
For a **scheduled run**, complete steps 1 and 2, then create a schedule that targets the agent — the scheduler creates the session (stateful or stateless) and triggers the run on the cron expression you provide. No `/chat` call is needed; the agent runs autonomously when the cron fires.
```http
POST /schedule
```
## Resource Model
Every operation in Agent Service is scoped to a `user_id` resolved from the request. Below that boundary, the service manages seven resource types — six persisted (left half of the diagram) plus the message bus that ties their runtime behavior together (right half):
```mermaid
flowchart TB
User([User])
User --> Cred[Credential]
User --> Agent[Agent]
User --> Sched[Schedule]
Agent -- "1 : N" --> Session[Session]
Session -- "references" --> Cred
Session -- "bound to" --> WS[Workspace]
Session -- "owns" --> Msg[Messages]
Sched -- "targets" --> Agent
Sched -- "triggers" --> Session
Bus{{MessageBus}}
Sched -. "inbox + wakeup" .-> Bus
Bus -. "drives runs" .-> Session
Session -. "publishes events" .-> Bus
```
| Resource | Description |
| -------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **User** | Opaque tenant identifier resolved from the request. The service models no user system of its own; you plug yours in via `get_current_user_id`. |
| **Credential** | Connection configuration for a model provider — an API key plus provider-specific settings. Reusable across many agents and sessions. |
| **Agent** | Display name, system prompt, and runtime configuration (context, ReAct loop). The reusable template — identity belongs to the agent, runtime state belongs to the session. |
| **Workspace** | The agent's runtime environment — working directory, MCP clients, skills, offloaded context. How workspaces map to users / agents / sessions is decided by the workspace manager. |
| **Session** | One ongoing exchange between a user and an agent. Carries the agent state (working memory, in-flight reply, permission context), persisted message transcript, and the LLM configuration the session runs under. |
| **Schedule** | Fires an agent on a cron expression. Each fire runs inside a session — fresh per execution (stateless) or reused so context accumulates (stateful). Schedules persist across restarts. |
| **MessageBus** | Redis-backed runtime layer — session locks, replay logs, inbox queues, wakeup signals. The single delivery channel for scheduled fires, team messages, and background-tool completions to reach idle sessions; also what makes multi-process operation possible. |
<Tip>
The shape to remember: **agents are reusable templates, sessions are the unit of runtime state**, and the message bus is what brings idle sessions back to life when something external (a schedule, a teammate, a background tool) has something to say.
</Tip>
## API Overview
The service exposes the resources from the resource model as REST endpoints, plus the streaming chat endpoint. The table below groups them by category; full request and response shapes are documented in the service's OpenAPI specification.
| Category | Endpoints | Description |
| ------------------ | ------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
| Chat | `POST /chat` | Fire a chat run for a session; returns `ChatTriggerResponse` JSON. Events are delivered out-of-band on the per-session stream. |
| Session stream | `GET /sessions/{id}/stream` | Per-session SSE stream of `AgentEvent` objects, with buffered replay for late joiners and multi-subscriber fan-out. |
| Sessions | `GET/POST/PATCH/DELETE /sessions` | Create and manage chat sessions, including model binding and permission level. |
| Messages | `GET /sessions/{id}/messages` | Paginated message transcript for a session. |
| Agents | `GET/POST/PATCH/DELETE /agent` | Manage agent records — display name, system prompt, runtime config. |
| Credentials | `GET/POST/PATCH/DELETE /credential` | CRUD for per-provider API keys and connection configs. |
| Credential schemas | `GET /credential/schemas` | Discover all registered credential types and their JSON parameter schemas for form rendering. |
| Models | `GET /model?provider=<name>` | List candidate models for a provider, with their declarative `ModelCard` (capabilities and parameter schemas). |
| Schedules | `GET/POST/PATCH/DELETE /schedule`, `GET /schedule/{id}/sessions` | Manage cron-based agent execution, stateful or stateless. |
| Workspace MCPs | `GET/POST /workspace/mcp`, `DELETE /workspace/mcp/{mcp_name}` | Manage MCP clients attached to the session's workspace. |
| Workspace skills | `GET/POST /workspace/skill`, `DELETE /workspace/skill/{skill_name}` | Manage skills available in the session's workspace. |
## Customization
The service is open at every infrastructure boundary. The sections below describe what is built in and how to plug in your own.
### Agent chat protocol
The per-session stream endpoint (`GET /sessions/{id}/stream`) emits AgentScope's native [`AgentEvent`](/v2/building-blocks/message-and-event) stream over SSE. To serve the same agent under a different frontend protocol, install a protocol middleware that intercepts the SSE stream and rewrites each frame.
AgentScope ships with `AGUIProtocolMiddleware` for the [AG-UI](https://docs.ag-ui.com/) protocol. Install it via `extra_middlewares`:
```python
from fastapi.middleware import Middleware
from agentscope.app import create_app, AGUIProtocolMiddleware
app = create_app(
storage=storage,
extra_middlewares=[
Middleware(AGUIProtocolMiddleware),
],
)
```
To add a new protocol, subclass `ProtocolMiddlewareBase` and implement `_convert_to_protocol`:
```python
from agentscope.app import ProtocolMiddlewareBase
from agentscope.event import AgentEvent
class MyProtocolMiddleware(ProtocolMiddlewareBase):
def _convert_to_protocol(self, event: AgentEvent) -> dict:
# Convert AgentEvent to your protocol's frame format.
return {"type": event.type, "data": event.model_dump()}
```
The middleware automatically intercepts `StreamingResponse` objects from the session stream endpoint, deserializes each SSE frame back into an `AgentEvent`, calls `_convert_to_protocol()` to produce the target format, and re-serializes the converted frame.
### User authentication
The built-in `get_current_user_id` dependency extracts the caller identity from the `X-User-ID` request header — a placeholder, not authentication. Override it with your own dependency to integrate any identity system.
JWT bearer token:
```python
from fastapi import Header, HTTPException, status
async def get_current_user_id(
authorization: str = Header(...),
) -> str:
try:
payload = decode_jwt(authorization.removeprefix("Bearer "))
return payload["sub"]
except InvalidTokenError:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid authentication token.",
)
```
OAuth2 password flow:
```python
from fastapi import Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
async def get_current_user_id(token: str = Depends(oauth2_scheme)) -> str:
user = await verify_oauth_token(token)
if user is None:
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED)
return user.id
```
Wire your override by replacing the dependency on the FastAPI app:
```python
from agentscope.app.deps import get_current_user_id as default_dependency
app.dependency_overrides[default_dependency] = get_current_user_id
```
<Warning>
The default `X-User-ID` header provides no authentication. Always replace it with a secure mechanism before deploying to production.
</Warning>
### Workspace implementation and isolation
Two independent axes are configurable:
* **Workspace backend** — what runtime environment the agent runs in. Built-in implementations include `LocalWorkspace`, `DockerWorkspace`, and `E2BWorkspace`. New backends implement the workspace interface and can wrap container images, sandboxes, or remote VMs.
* **Isolation strategy** — how workspaces map to users, agents, and sessions. The built-in `LocalWorkspaceManager` keys workspaces by `agent_id`: all sessions of the same agent share one workspace. To switch to per-user or per-session isolation, subclass `WorkspaceManagerBase` and override `get_workspace` with your own keying strategy.
```python
from agentscope.app.workspace_manager import WorkspaceManagerBase
from agentscope.workspace import WorkspaceBase
class PerSessionWorkspaceManager(WorkspaceManagerBase):
async def get_workspace(
self,
user_id: str,
agent_id: str,
session_id: str,
workspace_id: str,
) -> WorkspaceBase:
# Resolve an initialized workspace; key by session_id for per-session isolation.
...
async def create_workspace(
self,
user_id: str,
agent_id: str,
session_id: str,
) -> WorkspaceBase:
# Allocate a fresh workspace and register it in the cache.
...
async def close(self, workspace_id: str) -> None:
# Close and evict a single workspace.
...
async def close_all(self) -> None:
# Close every cached workspace; called on app shutdown.
...
```
### API credentials
A new credential type is a pair of classes: a `CredentialBase` subclass that captures the connection config (and publishes its JSON schema for form rendering), and a `ChatModelBase` subclass that implements the actual streaming chat protocol against the provider's API. The credential class is the entry point — it tells the service which chat model class to instantiate.
```python
from agentscope.credential import CredentialBase
from agentscope.model import ChatModelBase
class MyProviderChatModel(ChatModelBase):
# Implement the streaming chat interface against the provider's API.
...
class MyProviderCredential(CredentialBase):
api_key: str
endpoint: str = "https://api.my-provider.com"
@classmethod
def get_chat_model_class(cls):
return MyProviderChatModel
```
Register the credential class with the app — it becomes immediately usable by clients:
```python
app = create_app(
storage=storage,
extra_credentials=[MyProviderCredential],
)
```
The service automatically exposes the credential's JSON schema under `GET /credential/schemas`, and `GET /model?provider=<name>` routes to the chat model class returned by `get_chat_model_class()`.
### Provider models
The model list returned by `GET /model?provider=<name>` is built from `ModelCard` instances — declarative metadata records that tell the frontend how to display each model and what request parameters are valid. Each chat model exposes its catalog through `list_models()`, which by default loads `ModelCard` entries from YAML files in the provider's model directory; `ModelCard.from_yaml()` parses each YAML and merges its overrides into the base parameter schema supplied by the chat model's parameters class.
A model card carries the following fields:
| Field | Description |
| ---------------------- | ----------------------------------------------------------------------------- |
| `name` | Provider-side model identifier. |
| `label` | Display name shown in the UI. |
| `status` | One of `active`, `deprecated`, `sunset`. |
| `deprecated_at` | Deprecation timestamp, if any. |
| `input_types` | MIME types the model accepts (e.g., `text/plain`, `image/png`, `video/mp4`). |
| `output_types` | MIME types the model emits (e.g., `text/plain`, `application/x-thinking`). |
| `context_size` | Maximum context window in tokens. |
| `output_size` | Maximum output tokens. |
| `parameter_schema` | JSON schema for the request parameters, auto-merged with per-model overrides. |
| `parameters_overrides` | Per-model deltas applied on top of the base parameter schema. |
Example YAML for a multimodal model that accepts text, images, and video and emits text plus thinking traces:
```yaml qwen3.6-plus.yaml
name: qwen3.6-plus
label: Qwen3.6-Plus
status: active
input_types:
- text/plain
- application/x-thinking
- image/bmp
- image/jpeg
- image/png
- image/tiff
- image/webp
- image/heic
- video/mp4
output_types:
- text/plain
- application/x-thinking
context_size: 1000000
output_size: 65536
parameter_overrides:
max_tokens: {"maximum": 65536}
```
To add a new model under an existing provider, drop a YAML file alongside the others in the provider's model directory — the loader picks it up automatically and the new entry shows up in `GET /model?provider=<name>`.
### Storage backend
The `StorageBase` abstract class defines the persistence contract for agents, sessions, credentials, messages, and schedules. AgentScope ships with `RedisStorage` as the built-in implementation:
```python
from agentscope.app.storage import RedisStorage
storage = RedisStorage(
host="localhost",
port=6379,
db=0,
password="your-password",
)
```
To use another database, implement the same interface:
```python
from agentscope.app.storage import StorageBase
class PostgresStorage(StorageBase):
async def __aenter__(self):
# Open connection pool.
...
async def __aexit__(self, exc_type, exc_val, exc_tb):
# Close connection pool.
...
# Implement CRUD methods for each record type:
# agents, sessions, credentials, messages, schedules, teams.
...
app = create_app(
storage=PostgresStorage(dsn="postgresql://..."),
message_bus=message_bus,
workspace_manager=workspace_manager,
)
```
The records the storage layer manages:
| Record | Description |
| ------------------ | -------------------------------------------------------------------------- |
| `AgentRecord` | Agent configuration (name, system prompt, context config, react config). |
| `SessionRecord` | Session state including `AgentState`, model config, and workspace binding. |
| `CredentialRecord` | Encrypted model provider API keys. |
| `ScheduleRecord` | Cron schedule definitions with execution history. |
| `TeamRecord` | Team identity, leader binding, and worker member list. |
| `Msg` | Persisted messages per session with pagination support. |
## Service Internals
For developers who need to extend or embed the actual implementation of Agent Service in AgentScope, this section describes how the FastAPI app is wired together — what runs at startup, which managers hold runtime state, where middlewares sit in the request path, and how routers get hold of those resources.
```mermaid
flowchart TB
Client([Client / Frontend])
subgraph FastAPI ["FastAPI Application"]
direction TB
subgraph ASGI ["ASGI Middlewares"]
PM[Protocol Middleware]
OT[OpenTelemetry]
end
Router[API Routers]
subgraph Lifespan ["Lifespan-bound Resources"]
Bus[MessageBus]
BTM[BackgroundTaskManager]
SCH[SchedulerManager]
WM[WorkspaceManager]
WD[WakeupDispatcher]
CS[ChatService]
end
subgraph AgentMW ["Agent-level Middlewares"]
IM[InboxMiddleware]
TOM[ToolOffloadMiddleware]
SCM[StateChangeMiddleware]
end
end
Storage[(Storage)]
Client --> ASGI --> Router
Router -- "Depends()" --> Lifespan
Router --> CS
CS --> Storage
CS --> AgentMW --> Agent([Agent Instance])
SCH -- "inbox_push + enqueue_wakeup" --> Bus
TOM -- "inbox_push + enqueue_wakeup" --> Bus
Bus --> WD
WD --> CS
```
### Lifespan
The lifespan context manager runs once per process. Built with `AsyncExitStack`, it enters resources in order — storage → message bus → workspace manager → background task manager → scheduler manager → chat service → wakeup dispatcher — and tears them down in reverse on shutdown. If any startup step raises, every previously-entered resource is still cleaned up. The scheduler restores persisted cron jobs on entry so they survive restarts.
### Managers
The following resources are bound to the FastAPI app state during the lifespan and shared across all requests:
| Resource | Responsibility |
| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `MessageBus` | Redis-backed primitives (session locks + replay log, inbox queues, wakeup signals). The single delivery channel for scheduled fires, team messages, and background-tool completions to reach idle sessions; also what enables multi-process operation. |
| `WakeupDispatcher` | One per process. Subscribes to the wakeup signal and, for each enqueued wakeup, drives `ChatService.run` for the target session. |
| `BackgroundTaskManager` | Pure asyncio task registry. `ToolOffloadMiddleware` spawns watcher tasks here; results are pushed back through the message bus (inbox + wakeup), not held in this manager. |
| `SchedulerManager` | APScheduler-backed cron execution. On fire, the trigger pushes a `HintBlock` to the target session's inbox and enqueues a wakeup — no direct call into `ChatService`. |
| `WorkspaceManager` | Workspace lifecycle and TTL-based caching; the isolation key (per-agent, per-user, per-session) is decided by the subclass. |
| `ChatService` | Single entry point for running a session. Loads records, assembles the toolkit, builds middlewares, takes the bus session lock, and drives the agent's reply stream. |
### Middlewares
Two distinct middleware layers operate at different scopes.
**ASGI middlewares** wrap every HTTP request. The two categories used in practice are **protocol middlewares** (e.g., `AGUIProtocolMiddleware`), which intercept SSE responses from the session stream endpoint and rewrite each frame into the target protocol, and **observability middlewares** (e.g., OpenTelemetry tracing). Both install via `extra_middlewares`.
**Agent-level middlewares** wrap each call to the agent inside `ChatService`. They are exposed under `agentscope.app.middleware` and the framework always installs three:
* `InboxMiddleware` — the sole owner of hint injection. Before each reasoning step it drains the session's inbox and yields the queued `HintBlock`s as `HintBlockEvent`s, so scheduled fires, team messages, and offloaded-tool results all flow into the agent's context through the same path.
* `ToolOffloadMiddleware` — when a tool call exceeds its timeout, the call is moved to a background watcher task and a synthetic placeholder is yielded to the agent. When the watcher finishes, the result is pushed back to the session's inbox plus a wakeup, so the next run picks it up.
* `StateChangeMiddleware` — emits `CustomEvent`s when the agent state changes (e.g., `tasks_context`, `permission_context`) so the frontend can react without reading raw state snapshots.
To add your own (audit logging, tenant isolation, custom auth, …), pass an `extra_agent_middlewares` factory to `create_app`. The factory runs once per agent assembly and its middlewares are appended to the framework-supplied ones.
### Dependencies
Routers receive application state through FastAPI's `Depends()`. The standard injectables (in `agentscope.app.deps`) are:
| Dependency | Returns |
| ----------------------------- | ---------------------------------------------------------------- |
| `get_current_user_id` | The caller's user id — overridable to integrate any auth system. |
| `get_storage` | The `StorageBase` instance bound to the app. |
| `get_message_bus` | The `MessageBus` instance bound to the app. |
| `get_workspace_manager` | The lifespan-bound `WorkspaceManager`. |
| `get_background_task_manager` | The lifespan-bound `BackgroundTaskManager`. |
| `get_scheduler_manager` | The lifespan-bound `SchedulerManager`. |
| `get_chat_service` | The lifespan-bound `ChatService`. |
## Further Reading
<CardGroup cols={2}>
<Card title="Agent" icon="robot" href="/v2/building-blocks/agent">
Core agent abstraction and the ReAct loop
</Card>
<Card title="Message & Event" icon="envelope" href="/v2/building-blocks/message-and-event">
Event streaming and message reconstruction
</Card>
<Card title="Tool" icon="wrench" href="/v2/building-blocks/tool">
Built-in and custom tools including external execution
</Card>
<Card title="Context" icon="database" href="/v2/building-blocks/context">
Context compression and workspace offloading
</Card>
</CardGroup>
<!-- ===== source: v2/deploy/agent-team.md ===== -->
# Agent Team
> Leader agents that spawn and coordinate worker agents through built-in team tools
Agent Team is the multi-agent layer built on top of [Agent Service](/v2/deploy/agent-service). A leader agent — the session the user talks to — can spawn worker agents on demand and exchange messages with them, while every member is just another session with its own state, workspace binding, and event stream. The whole coordination story is expressed through four built-in tools rather than a separate orchestration framework.
## Quickstart
The bundled [`examples/agent_service`](https://github.com/agentscope-ai/agentscope/tree/main/examples/agent_service) backend ships with the team tools enabled, and the matching [`examples/web_ui`](https://github.com/agentscope-ai/agentscope/tree/main/examples/web_ui) frontend renders team membership and per-worker streams out of the box. Follow the [Agent Service quickstart](/v2/deploy/agent-service#try-the-bundled-example) to boot both — once they are running, ask the leader agent to assemble a team and you will see it call `TeamCreate` / `AgentCreate` automatically, watch workers come online, and observe them exchange messages in the UI.
<Frame caption="A leader agent assembling a team and coordinating workers in the example web UI.">
<img src="https://mintcdn.com/agentscope-ai-786677c7/c8Op3M8_8-WXpeVH/images/team.gif?s=604f394f94cca0dbca8fdeee75a0f7ff" alt="Agent team coordination demo" width="1920" height="1080" data-path="images/team.gif" />
</Frame>
## Concepts
| Concept | Description |
| ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Team** | A persistent group of agent members owned by one user. A `TeamRecord` carries the team's identity (name, description) and its member list. |
| **Leader** | The session that created the team. Only the leader can add or remove members or end the team. |
| **Worker** | A session spawned as a team member. Workers run their own ReAct loop in their own session and inherit the leader's chat model + workspace context. |
| **Team message** | A message routed between members through the message bus. Delivered as a `HintBlock` wrapped in a `<team-message from="…">` tag so the recipient's LLM can disambiguate it from a regular user turn. |
## Usage
### Creating a team
The team feature is built into Agent Service — no extra configuration is required. When a user sends a task that benefits from multi-agent collaboration, the leader agent automatically uses the built-in team tools to assemble and coordinate a team of workers.
Out of the box, the leader agent can:
* **Create a team** with a name and description that frames the collaboration goal.
* **Spawn workers** by giving each a name, role description, and an initial task. Workers begin executing immediately upon creation.
* **Exchange messages** with workers to provide follow-up instructions or collect results.
* **Dissolve the team** when the task is complete, cleaning up all worker sessions.
Every worker runs concurrently in its own session with its own event stream, visible in the frontend UI alongside the leader's conversation. The leader orchestrates work by reading worker outputs and sending messages — all through the same chat interface.
By default, all workers share the same system prompt template and permission settings. To give different worker roles different capabilities — for example, a read-only explorer versus a full-access coder — register custom sub-agent templates as described in the next section.
### Custom sub-agent types
By default every worker spawned by `AgentCreate` uses the same built-in system prompt and permission context. In practice, different roles need different capability boundaries — an agent that only explores the codebase should not be able to modify files, while one that writes code needs full edit access. `SubAgentTemplate` solves this by letting you define reusable blueprints that the leader agent can choose from when creating workers.
#### Registering templates
Pass a list of `SubAgentTemplate` instances to `create_app` via the `sub_agent_templates` parameter:
```python
from agentscope.app import create_app, SubAgentTemplate
from agentscope.permission import PermissionContext, PermissionMode
app = create_app(
storage=storage,
message_bus=message_bus,
workspace_manager=workspace_manager,
sub_agent_templates=[
SubAgentTemplate(
type="explorer",
description=(
"Read-only agents specialized in exploration tasks. "
"Use this type when you need to investigate the "
"codebase without making any changes."
),
system_prompt_template="""You are {member_name}, an explorer \
agent in team '{team_name}' led by {leader_name}.
Team purpose: {team_description}
Your role: {member_description}
## Responsibilities
- Complete the exploration tasks assigned by the team leader.
- You are read-only: you may inspect files and the codebase, but \
you must never modify, create, or delete anything.
## Reporting
- Always report the task result back to {leader_name} using the \
TeamSay tool, whether the task succeeds or fails.""",
permission_context=PermissionContext(
mode=PermissionMode.EXPLORE,
),
),
],
)
```
#### Template fields
| Field | Required | Default | Description |
| ------------------------ | -------- | --------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| `type` | Yes | — | Template identifier (e.g. `"explorer"`, `"coder"`). Becomes an enum value of the `subagent_type` parameter in `AgentCreate`. |
| `description` | Yes | — | Agent-readable description exposed in the `AgentCreate` tool schema so the leader can choose the appropriate type. |
| `system_prompt_template` | Yes | — | Python format string for the worker's system prompt. See [available placeholders](#system-prompt-placeholders) below. |
| `permission_context` | No | `PermissionContext()` | Permission context applied to the worker. Controls what the worker is allowed to do (e.g. `PermissionMode.EXPLORE` for read-only). |
| `context_config` | No | `ContextConfig()` | Context window configuration for the worker. |
| `react_config` | No | `ReActConfig()` | ReAct loop configuration for the worker. |
| `tasks_context` | No | `TaskContext()` | Pre-defined task context, allowing the template to seed an initial workflow. |
#### System prompt placeholders
The `system_prompt_template` string is formatted with these variables when a worker is created:
| Placeholder | Value |
| ---------------------- | ------------------------------------------------------ |
| `{team_name}` | The team's name as set by `TeamCreate`. |
| `{team_description}` | The team's description as set by `TeamCreate`. |
| `{member_name}` | The worker's name as set by `AgentCreate`. |
| `{member_description}` | The worker's role description as set by `AgentCreate`. |
| `{leader_name}` | The leader agent's display name. |
#### Runtime behavior
* **No custom templates registered**`AgentCreate` does not expose a `subagent_type` parameter at all. All workers use the built-in default template. This keeps the tool schema clean when templates are not needed.
* **Custom templates registered**`AgentCreate` automatically gains a `subagent_type` enum field listing all available types (including `"default"`). The leader agent sees each type's description and can choose which one to use.
* **Overriding the default** — registering a template with `type="default"` replaces the built-in default template entirely.
* **Uniqueness** — template type names must be unique. Duplicate types cause a `ValueError` at startup.
## Architecture
### Built-in tools
A leader session is automatically given these tools. Workers see only `TeamSay`.
| Tool | Purpose |
| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `TeamCreate` | Create a new team rooted at the current session and become its leader. |
| `AgentCreate` | Spawn a new worker into the team with a name, role description, first task, and permission mode. The worker begins executing as soon as it is created. |
| `TeamSay` | Send a message to a named member (or broadcast). The recipient's session receives the message through its inbox and resumes on the next wakeup. |
| `TeamDelete` | Dissolve the team and clean up every member session. Only the leader can call this. |
### Coordination model
Agent Team is designed for distributed deployments by default. All inter-member communication is mediated by the [message bus](/v2/deploy/agent-service#resource-model) — a Redis-backed abstraction — so leader and worker sessions can live in different processes or different nodes without any code change. The sender writes to the recipient's inbox; any wakeup dispatcher in the cluster can then claim the wakeup signal and drive that session on its own process. This is the same mechanism that powers scheduled fires and background-tool completions, which is why the team feature scales out the same way the rest of the service does.
Team communication reuses the same inbox + wakeup primitives the service uses for scheduled fires and background-tool completions:
1. The sender's tool call (`TeamSay`, `AgentCreate`'s initial prompt, …) pushes a `HintBlock` onto the recipient session's inbox via the message bus.
2. A wakeup is enqueued for the recipient.
3. The wakeup dispatcher running on any process picks up the wakeup and drives `ChatService.run` for that session.
4. `InboxMiddleware` drains the inbox before the next reasoning step, so the queued team messages land in the recipient's context as `HintBlockEvent`s.
This means workers run *concurrently* on the same service — they are not nested coroutines under the leader. The leader observes a worker's progress by reading its session stream, or by having the worker `TeamSay` back to it.
## See also
<CardGroup cols={2}>
<Card title="Agent Service" icon="server" href="/v2/deploy/agent-service">
The hosting layer that powers teams — sessions, message bus, workspace lifecycle.
</Card>
<Card title="Agent" icon="robot" href="/v2/building-blocks/agent">
The agent abstraction each team member runs.
</Card>
</CardGroup>

View File

@ -1,323 +0,0 @@
<!-- AgentScope 2.0 官方文档收敛 · 概览篇 · 抓取2026-06-22自 docs.agentscope.io · 含: 2.0是什么/快速上手/变更日志/FAQ -->
<!-- ===== source: v2/index.md ===== -->
# What's AgentScope 2.0?
> More secure, more efficient, more flexible, and more complete agent development.
AgentScope 2.0 is a major update to our agent framework, with a focus on improving the developer experience and
making it easier to build and run agents in production.
<Note>
AgentScope 2.0 is a breaking change from 1.0, with significant improvements in the core abstractions,
APIs and architecture. We recommend users to migrate to 2.0 to take advantage of the new features and
improvements.
</Note>
2.0 brings the following major changes and improvements:
* **Event System**: Every step the agent takes — text, thinking, tool call, tool result — is observable as a typed stream, so you can render rich, responsive UIs and integrate with AG-UI or A2A without writing adapters.
* **Execution Security**: Dangerous tool calls can be denied or held for review, and untrusted code can run inside a sandbox — the agent never silently touches the host or leaks credentials.
* **Human-in-the-loop**: Users can confirm or edit tool arguments mid-run, and sensitive actions can be handed off to your own backend instead of executed in-process, with the agent resuming exactly where it paused.
* **More Efficient**: Multi-tool steps finish faster through concurrent execution, long conversations stay within the context window automatically, oversized tool outputs no longer blow up the prompt, and transient provider failures fall back gracefully.
* **Workspace System**: Move an agent from your laptop to a Docker host or an E2B sandbox by changing one line, with working directory, MCP clients and skills cleanly isolated per user, agent or session.
* **Agent Service**: Host any agent over REST + SSE with multi-tenant, multi-session concurrency, resumable streams, durable sessions, scheduled runs and managed credentials — without writing the service plumbing yourself.
If you are still evaluating whether to migrate, check out the [Changelog](/v2/change-log) for a full breakdown of every change — it should give you everything you need to plan your migration to AgentScope 2.0.
<CardGroup cols={2}>
<Card title="Event System" icon="wave-pulse" href="/v2/building-blocks/message-and-event">
Observe every step of the agent and stream it straight into your UI.
</Card>
<Card title="Execution Security" icon="shield-halved" href="/v2/building-blocks/permission-system">
Gate or sandbox dangerous tool calls before they touch the host.
</Card>
<Card title="Human-in-the-loop" icon="user-check" href="/v2/building-blocks/message-and-event">
Let users review and edit tool arguments before execution, or delegate sensitive actions to your own backend entirely.
</Card>
<Card title="Efficient Agent" icon="gauge-high" href="/v2/building-blocks/agent">
Tool calls are auto-batched and run concurrently or sequentially based on each tool's properties.
</Card>
<Card title="Workspace System" icon="cube" href="/v2/building-blocks/workspace">
Swap Local, Docker or E2B without rewriting the agent.
</Card>
<Card title="Agent Service" icon="server" href="/v2/deploy/agent-service">
Ship agents over REST + SSE with multi-tenant, multi-session concurrency, sessions, schedules and credentials.
</Card>
</CardGroup>
<!-- ===== source: v2/quickstart.md ===== -->
# Quickstart
> Get up and running with AgentScope 2.0 in minutes
## Installation
AgentScope requires Python 3.11+, and you can install it from PyPI or from source.
It's recommended to install AgentScope by using [uv](https://github.com/astral-sh/uv).
### From PyPI
```bash
uv pip install agentscope
```
### From Source
```bash
git clone -b main https://github.com/agentscope-ai/agentscope
cd agentscope
uv pip install -e .
```
### Verify Installation
To ensure AgentScope is installed successfully, check via executing the following code:
```python
import agentscope
print(agentscope.__version__)
```
## Your First Agent
The snippet below builds the minimal agent: a DashScope credential, the matching chat model, an empty toolkit, and an `Agent`. The agent exposes two entry points — `reply` returns the final message, while `reply_stream` yields incremental events as the agent reasons and acts.
```python
import asyncio
import os
from agentscope.agent import Agent
from agentscope.credential import DashScopeCredential
from agentscope.event import EventType
from agentscope.message import UserMsg
from agentscope.model import DashScopeChatModel
from agentscope.tool import Toolkit, Bash, Read, Write, Edit
async def main() -> None:
agent = Agent(
name="Friday",
system_prompt="You are a helpful assistant named Friday.",
model=DashScopeChatModel(
credential=DashScopeCredential(
api_key=os.getenv("DASHSCOPE_API_KEY"),
),
model="qwen-plus",
),
toolkit=Toolkit(tools=[Bash(), Read(), Write(), Edit()]),
)
user_msg = UserMsg(name="user", content="Hello, who are you?")
# Option 1: await the final assistant message.
reply_msg = await agent.reply(user_msg)
# `reply_msg` is an `AssistantMsg` whose `content` is a list of blocks.
# Inspect text blocks, tool calls, etc. as needed.
...
# Option 2: stream incremental events (text deltas, tool calls, ...).
async for event in agent.reply_stream(user_msg):
# Dispatch on `event.type` — each branch handles one event kind.
match event.type:
case EventType.TEXT_BLOCK_DELTA:
# Streaming text chunk from the model — append to UI / stdout.
...
case EventType.TOOL_CALL_START:
# The agent is about to invoke a tool — surface the call.
...
case _:
# Other events: thinking blocks, tool results, reply end, ...
...
asyncio.run(main())
```
<Tip>
Set `DASHSCOPE_API_KEY` in your environment before running the script. To use a different provider, swap `DashScopeCredential` and `DashScopeChatModel` for the matching pair (e.g. `OpenAICredential` and `OpenAIChatModel`).
</Tip>
## Extra Dependencies
To satisfy the requirements of different functionalities, AgentScope provides extra dependencies that can be installed based on your needs.
* **full**: including extra dependencies for model APIs, tool functions and more.
* **dev**: development dependencies, including testing and documentation tools.
For example, when installing the full dependencies, the installation command varies depending on your operating system.
* For Windows users:
```bash
uv pip install agentscope[full]
```
* For Mac and Linux users:
```bash
uv pip install agentscope\[full\]
```
<!-- ===== source: v2/change-log.md ===== -->
# Changelog
> Core differences between AgentScope 2.0 and 1.0
AgentScope 2.0 is a breaking release. The notes below summarize the differences against 1.0, grouped by module.
## Agent
* Refactor `ReActAgent` into a new unified `Agent` class implementation.
* Replace the `__call__` method in 1.0 with the `reply_stream` and `reply` public methods.
* Support yielding agent events from `reply_stream` for richer observability and control.
* Support **permission checks** and **human-in-the-loop** confirmations through the event stream.
* Support offloading the compressed context and oversized tool results via a new `Offloader` interface.
* Deprecate the hook mechanism and replace it with a new agent middleware system.
* Deprecate the `state_dict` and `load_state_dict` methods, moving towards explicit state management through the new `AgentState` type.
* Deprecate the `print` interface of the agent class, turning the agent into a pure producer.
* Deprecate the OpenTelemetry integration within the agent class and leave it to a new middleware implementation.
## Event <Badge color="green" size="sm">New</Badge>
* Add event system for better frontend integration and human-in-the-loop support.
## Message
Content blocks refactor:
* Refactor all content blocks by inheriting from the Pydantic `BaseModel` for better validation, serialization, and extensibility.
* Refactor `ImageBlock`, `AudioBlock`, `VideoBlock` into a unified `DataBlock` with a `media_type` field for extensibility.
* Add `HintBlock` for agent guidance and intermediate reasoning.
* Rename `ToolUseBlock` to `ToolCallBlock`.
* Add `state` and `suggested_rules` fields to `ToolCallBlock` for richer tool-call lifecycle modeling.
* Add `state` field to `ToolResultBlock` for richer tool-call lifecycle modeling.
* Add `id` field for all blocks for better traceability and referencing.
`Msg` class refactor:
* Refactor `Msg` to inherit from `BaseModel` and enforce content validation.
* Add `created_at`, `finished_at`, and `usage` fields to the `Msg` class for better observability and accounting.
* Add `append_event` method to `Msg` for yielding events from the agent's reply stream.
* Add factory methods `UserMsg`, `AssistantMsg`, and `SystemMsg` to create messages with the appropriate role.
* Add `content` field constraints with the specified `role` types.
## Permission <Badge color="green" size="sm">New</Badge>
* Add a new permission system for gating tool execution, human-in-the-loop confirmation, and overall agent autonomy control.
## Tool
* Add `ToolBase` abstraction for all tools.
* Refactor built-in tools:
* Add `Bash`, `Edit`, `Glob`, `Grep`, `Read`, and `Write` with permission control.
* Add `TaskCreate`, `TaskGet`, `TaskList`, and `TaskUpdate` for task management.
`Toolkit` refactor:
* Support tools, skills, MCPs, and tool groups as first-class citizens in the `Toolkit` abstraction.
* Add `ToolGroup` for on-demand activation, with the reserved `basic` group always active.
* Add the `ResetTools` meta-tool for the agent to switch tool groups at runtime.
* Add `MCPTool` and `FunctionTool` adapters for uniform tool registration.
## MCP
* Refactor MCP implementation into a single `MCPClient` class for a unified client surface.
* Add `StdioMCPConfig` and `HttpMCPConfig` declarative configuration types for typed MCP setup.
## Skill <Badge color="green" size="sm">New</Badge>
* Add skill loader abstraction to support in-time skill loading from the filesystem/sandbox/web.
* Add `LocalSkillLoader` class to support directory-based skill loading and monitoring.
* Support packaging skills into `ToolGroup`s for on-demand activation and better organization.
## Workspace <Badge color="green" size="sm">New</Badge>
* Add the workspace abstraction supplying tools, MCPs, skills, and context offloading through one unified interface.
* Add `LocalWorkspace`, `DockerWorkspace`, and `E2BWorkspace` implementations sharing the same agent-facing API for swappable execution backends.
* Add the `Offloader` protocol consumed by `Agent` for context compression and oversized tool-result handling.
* Add `LocalWorkspaceManager`, `DockerWorkspaceManager`, and `E2BWorkspaceManager` with **agent-level isolation** for multi-tenant services.
* Add an in-workspace **MCP gateway** so host-side agents can reach MCP servers running inside containers and sandboxes.
## Model
* Decouple credential management from the model classes and centralize it in a new `Credential` module.
* Support credential-aware model listing and retrieval.
* Support Kimi, Moonshot, DeepSeek, XAI, and OpenAI Response API.
* Integrate formatter into the chat model abstraction and support default formatters for different model providers.
* Add the `ModelCard` schema describing model identity, capabilities, and parameter overrides.
* Add class method `list_models` for frontend model listing and selection.
* Deprecate the `Trinity` model wrapper.
## Middleware <Badge color="green" size="sm">New</Badge>
* Refactor the hook mechanism into a more general agent middleware system.
* Add `TracingMiddleware` as the new entry point for OpenTelemetry tracing, replacing the in-agent integration.
## Agent Service <Badge color="green" size="sm">New</Badge>
* Add a new FastAPI-based agent service and sandbox support in the `app` module.
* Add the `create_app` FastAPI factory exposing agent, chat, model, credential, session, schedule, workspace, and background-task routers.
* Add lifespan-scoped `SessionManager`, `SchedulerManager`, `BackgroundTaskManager`, and workspace managers for multi-tenant resource allocation.
* Add `AGUIProtocolMiddleware` for streaming and `ToolOffloadMiddleware` for oversized payloads.
* Add Redis-backed storage.
## Memory
* Deprecate the memory module in 2.0 due to its tight coupling with agent logic.
## RAG & Long-Term Memory
* Unify RAG and long-term memory into one module.
* Migration from 1.0 to 2.0 is in progress; the module — knowledge bases, document readers, and stores — will return on top of the 2.0 architecture in upcoming releases.
<!-- ===== source: v2/others/faq.md ===== -->
# FAQ
> Frequently asked questions about AgentScope v2.0
<AccordionGroup>
<Accordion title="Is AgentScope 2.0 compatible with 1.0?">
No. AgentScope 2.0 is a breaking release that introduces a redesigned agent abstraction, a new event system, the workspace and permission systems, and many other new features. APIs are not source-compatible with 1.0, and there is no automatic migration path.
We recommend upgrading to 2.0 for all new projects to benefit from the new capabilities. The 1.0 documentation remains available for existing users.
</Accordion>
<Accordion title="Does AgentScope support sandboxed execution?">
Yes. The **workspace** abstraction is AgentScope's execution environment for agents and ships three implementations — `LocalWorkspace` (host filesystem), `DockerWorkspace` (container), and `E2BWorkspace` (E2B cloud sandbox) — sharing the same interface so the same agent code runs against any backend. Workspaces also own MCP server lifecycles, skill management, and context offloading.
See [Workspace](/v2/building-blocks/workspace) for the full overview, including how to plug a workspace into an `Agent` and the multi-tenant `WorkspaceManager`.
</Accordion>
<Accordion title="Is there a frontend for AgentScope 2.0?">
Yes, on two layers:
* **TypeScript SDK** — install with `pnpm install @agentscope-ai/agentscope`. It mirrors the Python `Msg` and `Event` types so frontend code can consume agent streams without re-implementing the protocol.
* **Frontend UI** — a ready-to-use web app for [Agent Service](/v2/deploy/agent-service), letting developers exercise their deployed agents without writing any UI code.
</Accordion>
<Accordion title="Will RAG and long-term memory return in 2.0?">
Yes. Both modules are being ported from 1.0 to the 2.0 architecture and will land in upcoming releases. Track the changelog and GitHub releases for availability.
</Accordion>
<Accordion title="Are there language bindings other than Python?">
Yes. AgentScope is available in three languages, each with its own repository:
* **Python** — [`agentscope-ai/agentscope`](https://github.com/agentscope-ai/agentscope) (this documentation)
* **TypeScript** — [`agentscope-ai/agentscope-typescript`](https://github.com/agentscope-ai/agentscope-typescript)
* **Java** — [`agentscope-ai/agentscope-java`](https://github.com/agentscope-ai/agentscope-java)
</Accordion>
</AccordionGroup>

View File

@ -1,306 +0,0 @@
# -*- coding: utf-8 -*-
# pylint: skip-file
"""Get the signatures of functions and classes in the agentscope library."""
from typing import Literal, Callable
import agentscope
import inspect
from pydantic import BaseModel
def get_class_signature(cls: type) -> str:
"""Get the signature of a class.
Args:
cls (`type`):
A class object.
Returns:
str: The signature of the class.
"""
# Obtain class name and docstring
class_name = cls.__name__
class_docstring = cls.__doc__ or ""
# Construct the class string
class_str = f"class {class_name}:\n"
if class_docstring:
class_str += f' """{class_docstring}"""\n'
# Obtain the module of the class
methods = []
for name, method in inspect.getmembers(cls, predicate=inspect.isfunction):
# Skip methods that are not part of the class
if method.__qualname__.split(".")[0] != class_name:
continue
if name.startswith("_") and name not in ["__init__", "__call__"]:
continue
# Obtain the method's signature
sig = inspect.signature(method)
# Construct the method string
method_str = f" def {name}{sig}:\n"
# Add the method's docstring if it exists
method_docstring = method.__doc__ or ""
if method_docstring:
method_str += f' """{method_docstring}"""\n'
methods.append(method_str)
class_str += "\n".join(methods)
return class_str
def get_function_signature(func: Callable) -> str:
"""Get the signature of a function."""
sig = inspect.signature(func)
method_str = f"def {func.__name__}{sig}:\n"
method_docstring = func.__doc__ or ""
if method_docstring:
method_str += f' """{method_docstring}"""\n'
return method_str
class FuncOrCls(BaseModel):
"""The class records the module, signature, docstring, reference, and
type"""
module: str
"""The module of the function or class."""
signature: str
"""The signature of the function or class."""
docstring: str
"""The docstring of the function or class."""
reference: str
"""The reference to the source code of the function or class"""
type: Literal["function", "class"]
"""The type of the function or class, either 'function' or 'class'."""
def __init__(
self,
module: str,
signature: str,
docstring: str,
reference: str,
# pylint: disable=redefined-builtin
type: Literal["function", "class"],
) -> None:
"""Initialize the FuncOrCls instance."""
super().__init__(
module=module,
signature=signature.strip(),
docstring=docstring.strip(),
reference=reference,
type=type,
)
def _truncate_docstring(docstring: str, max_length: int = 200) -> str:
"""Truncate the docstring to a maximum length.
Args:
docstring (`str`):
The docstring to truncate.
max_length (`int`, *optional*, defaults to 200):
The maximum length of the docstring.
Returns:
`str`:
The truncated docstring.
"""
if len(docstring) > max_length:
return docstring[:max_length] + "..."
return docstring
def get_agentscope_module_signatures() -> list[FuncOrCls]:
"""Get the signatures of functions and classes in the agentscope library.
Returns:
`list[FuncOrCls]`:
A list of FuncOrCls instances representing the functions and
classes in the agentscope library.
"""
signatures = []
for module in agentscope.__all__:
as_module = getattr(agentscope, module)
path_module = ".".join(["agentscope", module])
# Functions
if inspect.isfunction(as_module):
file = inspect.getfile(as_module)
source_lines, start_line = inspect.getsourcelines(as_module)
signatures.append(
FuncOrCls(
module=path_module,
signature=get_function_signature(as_module),
docstring=_truncate_docstring(as_module.__doc__ or ""),
reference=f"{file}: {start_line}-"
f"{start_line + len(source_lines)}",
type="function",
),
)
else:
if not hasattr(as_module, "__all__"):
continue
# Modules with __all__ attribute
for name in as_module.__all__:
func_or_cls = getattr(as_module, name)
path_func_or_cls = ".".join([path_module, name])
if inspect.isclass(func_or_cls):
file = inspect.getfile(func_or_cls)
source_lines, start_line = inspect.getsourcelines(
func_or_cls,
)
signatures.append(
FuncOrCls(
module=path_func_or_cls,
signature=get_class_signature(func_or_cls),
docstring=_truncate_docstring(
func_or_cls.__doc__ or "",
),
reference=(
f"{file}: {start_line}-"
f"{start_line + len(source_lines)}"
),
type="class",
),
)
elif inspect.isfunction(func_or_cls):
file = inspect.getfile(func_or_cls)
source_lines, start_line = inspect.getsourcelines(
func_or_cls,
)
signatures.append(
FuncOrCls(
module=path_func_or_cls,
signature=get_function_signature(func_or_cls),
docstring=_truncate_docstring(
func_or_cls.__doc__ or "",
),
reference=(
f"{file}: {start_line}-"
f"{start_line + len(source_lines)}"
),
type="function",
),
)
return signatures
def view_agentscope_library(
module: str,
) -> str:
"""View AgentScope's Python library by given a module name
(e.g. agentscope), and return the module's submodules, classes, and
functions. Given a class name, return the class's documentation, methods,
and their signatures. Given a function name, return the function's
documentation and signature. If you don't have any information about
AgentScope library, try to use "agentscope" to view the available top
modules.
Note this function only provide the module's brief information.
For more information, you should view the source code.
Args:
module (`str`):
The module name to view, which should be a module path separated
by dots (e.g. "agentscope.models"). It can refer to a module,
a class, or a function.
"""
if not module.startswith("agentscope"):
return (
f"Module '{module}' is invalid. The input module should be "
f"'agentscope' or submodule of 'agentscope.xxx.xxx' "
f"(separated by dots)."
)
agentscope_top_modules = {}
for as_module in agentscope.__all__:
if as_module in ["__version__", "logger"]:
continue
agentscope_top_modules[as_module] = getattr(
agentscope,
as_module,
).__doc__
# top modules
if module == "agentscope":
top_modules_description = (
[
"The top-level modules in AgentScope library:",
]
+ [
f"- agentscope.{k}: {v}"
for k, v in agentscope_top_modules.items()
]
+ [
"You can further view the classes/function within above "
"modules by calling this function with the above module name.",
]
)
return "\n".join(top_modules_description)
# class, functions
modules = get_agentscope_module_signatures()
for as_module in modules:
if as_module.module == module:
return f"""- The signature of '{module}':
```python
{as_module.signature}
```
- Source code reference: {as_module.reference}"""
# two-level modules
collected_modules = []
for as_module in modules:
if as_module.module.startswith(module):
collected_modules.append(as_module)
if len(collected_modules) > 0:
collected_modules_content = (
[
f"The classes/functions and their truncated docstring in "
f"'{module}' module:",
]
+ [f"- {_.module}: {repr(_.docstring)}" for _ in collected_modules]
+ [
"The docstring is truncated for limited context. For detailed "
"signature and methods, call this function with the above "
"module name",
]
)
return "\n".join(collected_modules_content)
return (
f"Module '{module}' not found. Use 'agentscope' to view the "
f"top-level modules to ensure the given module is valid."
)
if __name__ == "__main__":
import argparse
parser = argparse.ArgumentParser()
parser.add_argument(
"--module",
type=str,
default="agentscope",
help="The module name to view, e.g. 'agentscope'",
)
args = parser.parse_args()
res = view_agentscope_library(module=args.module)
print(res)

View File

@ -1,3 +0,0 @@
# !/bin/bash
curl -s https://pypi.org/pypi/agentscope/json | python -c "import sys,json; print(json.load(sys.stdin)['info']['version'])"

View File

@ -1,8 +0,0 @@
---
name: execution-plan-slicing
description: "当写或评审多单元执行 plan、或感到「每到下阶段前面阶段的工作都要返工」时使用:辨别横切关注面 vs 纵切成果切片、诊断返工信号、原地换轴重组、「设计 HOW 移交/执行契约必留」判据,及换轴必查七坑。"
---
本文件是薄壳注册件,正文唯一在项目 SoT(`.agents/README.md` 边界规则,防双写):
**读取仓库根下 `.agents/skills/execution-plan-slicing.md` 并照其执行。**

View File

@ -1,8 +0,0 @@
---
name: feature-design-doc
description: "当新功能/跨模块/改用户可见行为/触及外部服务·支付·数据进入设计阶段时使用:产出 WHAT+HOW 合一的功能设计文档——文字+Mermaid 为事实源、SVG/HTML 给人看,含第 0 步 SoT 注册表查证、sot-impact/上级申报、防漂移单源与文档骨架。"
---
本文件是薄壳注册件,正文唯一在项目 SoT(`.agents/README.md` 边界规则,防双写):
**读取仓库根下 `.agents/skills/feature-design-doc.md` 并照其执行。**

View File

@ -1,8 +0,0 @@
---
name: game-e2e-cdp-harness
description: "为插件库/生成游戏做真浏览器输入级 e2e(触摸轨迹→九门判真玩→四件套证据)时使用:play.cdp.cjs 九门(装载/掌帧/真渲染/接线/输入因果/latch/手感)+ 假绿守卫 + driver 库 + 首局体验门,mini-desktop 上跑,区别于 DOM 走查。"
---
本文件是薄壳注册件,正文唯一在项目 SoT(`.agents/README.md` 边界规则,防双写):
**读取仓库根下 `.agents/skills/game-e2e-cdp-harness.md` 并照其执行。**

View File

@ -1,8 +0,0 @@
---
name: staging-ops
description: "在 mini-desktop/mini-infra 上做 staging 或内测 dev 的部署运维时使用:机器分工铁律、代码同步(git push→Gitea clone)、后端重部署标准序、构建门/冒烟门、观测栈起容器、生成线环境地图与 executor 接线的实战配方与踩坑。"
---
本文件是薄壳注册件,正文唯一在项目 SoT(`.agents/README.md` 边界规则,防双写):
**读取仓库根下 `.agents/skills/staging-ops.md` 并照其执行。**

View File

@ -1,8 +0,0 @@
---
name: ui-walkthrough-cdp
description: "用户可见波次收口前在 mini-desktop 用 CDP 做真 UI 走查(studio 创作/试玩/发布、admin 审核台)时使用:逮编排器旁路掩盖的真后端缺陷,含 CDP 七坑、免登录 localStorage 注入只读走查、真实访问 origin 取证红线。"
---
本文件是薄壳注册件,正文唯一在项目 SoT(`.agents/README.md` 边界规则,防双写):
**读取仓库根下 `.agents/skills/ui-walkthrough-cdp.md` 并照其执行。**

View File

@ -1,8 +0,0 @@
---
name: wave-close-checklist
description: "当波次收口、里程碑状态变化或裁定类提交(拍板/闸门判定/口径变更)时使用:按序执行总账→作战清单→回填→.agent→蒸馏→索引→编排入库→治理门的八步收口清单,收口铁律唯一可执行入口。"
---
本文件是薄壳注册件,正文唯一在项目 SoT(`.agents/README.md` 边界规则,防双写):
**读取仓库根下 `.agents/skills/wave-close-checklist.md` 并照其执行。**

View File

@ -1,5 +0,0 @@
[mcp_servers.idea]
url = "http://127.0.0.1:64342/stream"
[mcp_servers.idea.headers]
IJ_MCP_SERVER_PROJECT_PATH = "/Users/qingse/Sync/local-git/games-development-ai"

View File

@ -1,108 +0,0 @@
# 契约机器门 · 服务端兜底(Gitea Actions,与 GitHub Actions 语法兼容)
# pre-commit 是自愿门(--no-verify / 新 clone 未配 hooksPath 可绕),本工作流是不可绕的服务端门。
# 与 docs-gate.yml 平级、各管一摊:docs-gate 管文档治理七检,本工作流管四类契约门。
# 生效前提:Gitea 实例已配 act_runner;未配时本文件静默不跑,不影响开发。
# CI 侧全量真跑(不做 pre-commit 的路径条件触发):判例库四段必须装齐 esbuild + pytest
# 依赖安装失败或任一整段 SKIP 都判红;本地裸环境仍可由 run.mjs 诚实 SKIP。
name: contract-gates
on:
push:
branches: ['**']
pull_request:
jobs:
contract-gates:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
# 段 A 版本升号闸(check_version_bump)要比对 push 前提交,故拉全历史;
# 其余四门只读工作区、不依赖 git 历史,fetch-depth 变化对它们无影响。
fetch-depth: 0
- uses: actions/setup-java@v4
with:
distribution: temurin
java-version: '17'
cache: maven
- uses: actions/setup-node@v4
with:
node-version: '22'
cache: npm
cache-dependency-path: |
game-runtime/package-lock.json
game-studio/package-lock.json
# 服务端门必须真跑全部判例;安装失败立即终止,不能降级成绿色 SKIP。
- name: 安装完整门依赖
run: |
pip3 install --quiet pytest jsonschema
npm ci --prefix game-runtime
- name: 门金标判例库全量回归
env:
GATE_FIXTURES_REQUIRE_ALL: '1'
run: |
node --test contracts/gate-fixtures/run.test.mjs
node contracts/gate-fixtures/run.mjs
- name: play-loop 契约正负样本套件
run: |
python3 contracts/play-loop/validate.py --suite
python3 contracts/play-loop/test_reference_asset_trusted_consumption_v2.py
- name: 游戏内容对象存储契约正负样本套件
env:
PYTHONPATH: tier2/gen-worker
run: >-
python3 -m pytest -q
tier2/gen-worker/tests/test_game_artifact_store.py
tier2/gen-worker/tests/test_game_source_archive.py
tier2/gen-worker/tests/test_game_artifact_storage_store.py
tier2/gen-worker/tests/test_game_artifact_storage_ddl.py
tier2/gen-worker/tests/test_game_content_repository.py
- name: 游戏内容控制面与 Runtime 事务边界
run: |
mvn -f game-cloud/pom.xml \
-pl huijing-dependencies,game-module-project/game-module-project-server,game-module-runtime/game-module-runtime-server,game-module-aigc/game-module-aigc-server \
-am -DskipTests install
mvn -f game-cloud/game-module-project/game-module-project-server/pom.xml \
-Dtest=GameVersionServiceImplTest \
-Dsurefire.failIfNoSpecifiedTests=true test
mvn -f game-cloud/game-module-runtime/game-module-runtime-server/pom.xml \
-Dtest=RuntimePackageApiImplDogfoodTest,AppRuntimeControllerTest,RuntimeConvertTest,ArtifactStorageGateTest,RuntimeArtifactContentServiceTest,RuntimeArtifactStoragePropertiesTest,RuntimeOssPackageFinalizeServiceTest,RuntimePackageServiceImplTest,S3ArtifactObjectReaderTest \
-Dsurefire.failIfNoSpecifiedTests=true test
mvn -f game-cloud/game-module-aigc/game-module-aigc-server/pom.xml \
-Dtest=GameContentControlControllerTest,GameContentControlServiceTest,GameContentFinalizeTxServiceTest \
-Dsurefire.failIfNoSpecifiedTests=true test
- name: 宿主同源取包与 iframe 安全边界
run: |
npm test --prefix game-runtime
npm ci --prefix game-studio
npm run test:host-security --prefix game-studio
npm run build --prefix game-studio -- --mode staging
- name: 品类 rubric fixture↔skill 双写对账
run: python3 .agents/tools/rubric-sync-gate.py
- name: 便宜档插件面三方一致性对账(目录/prompt 白名单/host-config 注入 + PLUGIN_KEY_DIR
run: python3 .agents/tools/plugin-surface-gate.py
- name: Prompt Registry 版本一致性
run: python3 contracts/prompts/check_registry.py
# 四道闸·闸0(段 A):改了 prompt 正文相对 push 前提交必须升 version。
# 基线优先取 push 事件的 before;取不到/无效回落 HEAD~1;再取不到(首 push/新分支)则跳过不误拦。
- name: Prompt 闸0 version 升号(改正文必升版)
run: |
BASE="${{ github.event.before }}"
if [ -z "$BASE" ] || ! git rev-parse --verify --quiet "$BASE^{commit}" >/dev/null 2>&1; then
BASE="$(git rev-parse --verify --quiet HEAD~1 2>/dev/null || true)"
fi
if [ -z "$BASE" ]; then
echo "无可比基线(首次提交/新分支),版本升号闸跳过(不误拦、不假绿)"
exit 0
fi
python3 contracts/prompts/check_version_bump.py --base "$BASE"

View File

@ -1,15 +0,0 @@
# 文档治理门 · 服务端兜底(Gitea Actions,与 GitHub Actions 语法兼容)
# pre-commit 是自愿门(--no-verify / 新 clone 未配 hooksPath 可绕),本工作流是不可绕的服务端门。
# 生效前提:Gitea 实例已配 act_runner(状态见待拍板项);未配时本文件静默不跑,不影响开发。
name: docs-gate
on:
push:
branches: ['**']
pull_request:
jobs:
docs-gate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: 跑文档治理六检
run: bash .agents/tools/docs-gate.sh

View File

@ -1,58 +0,0 @@
# Prompt 四道闸 · 真模型闸(段 B)—— 手动触发的真模型 eval CI
#
# 与 contract-gates.yml 分工(各管一摊):
# · contract-gates 每次 push 自动跑离线门(含段 A 版本升号闸 check_version_bump):秒级、零成本、不可绕。
# · 本工作流(段 B)花真模型钱、慢,故不随 push 跑,改手动 workflow_dispatch 触发——纪律 =
# prompt 改动合入前至少真跑一次、四闸全绿 + 人工抽检才合(prompt治理 SoT §2/§4)。
#
# 真模型 = MiniMax-M3 经 new-api 网关(thinking 关);单跑预算 cap ≤¥5 由 eval_gate.py 硬编码兜、超即 fail。
# 只焊 live 面:非 live 条目 SKIP 豁免、live 无金标 fail-closed(见 registry.yaml 头部消费面三态对账)。
#
# 前提:runner 能内网直连 new-api(100.64.0.8:3000,与 Gitea 同在 mini-infra 内网)。eval_gate.py 自带
# ProxyHandler({}) 绕系统代理;NO_PROXY 再兜一层。若 runner 不在内网,改走「ssh mini-desktop 执行」
# 既有模式(见 docs/内网凭据与端点.md);NEWAPI_KEY 走 Gitea secret,绝不落仓、不设长期 env。
# 生效前提:Gitea 实例已配 act_runner + secrets.NEWAPI_KEY;未配时本工作流不可用(不影响 push 门)。
name: prompt-eval
on:
workflow_dispatch:
inputs:
prompt_id:
description: '指定单条 prompt id 真跑(留空 = 对相对 base 改动的 live 条目跑)'
required: false
default: ''
base:
description: 'changed 模式的对比基线 git ref'
required: false
default: 'HEAD~1'
jobs:
prompt-eval:
runs-on: ubuntu-latest
env:
NEWAPI_KEY: ${{ secrets.NEWAPI_KEY }}
NEWAPI_BASE_URL: http://100.64.0.8:3000
NO_PROXY: 100.64.0.8,localhost,127.0.0.1
steps:
- uses: actions/checkout@v4
with:
# --changed 模式要比对基线找改动条目,故拉全历史。
fetch-depth: 0
- name: 安装并校验 Prompt eval 依赖
run: |
python3 -m pip install --disable-pip-version-check -r contracts/prompts/requirements-eval.txt
python3 -m pip check
python3 -c "import cv2, PIL; from PIL import Image; print('Pillow', PIL.__version__, 'OpenCV', cv2.__version__, 'PNG', Image.registered_extensions().get('.png'))"
- name: 段B 真模型四道闸(改动条目 / 指定条目)
run: |
if [ -z "$NEWAPI_KEY" ]; then
echo "✘ 未配置 secrets.NEWAPI_KEY,真模型闸无法跑(读 docs/内网凭据与端点.md 配 Gitea secret)"
exit 1
fi
if [ -n "${{ github.event.inputs.prompt_id }}" ]; then
echo "→ 指定条目真跑:${{ github.event.inputs.prompt_id }}"
python3 contracts/prompts/eval_gate.py --id "${{ github.event.inputs.prompt_id }}"
else
echo "→ 对相对 ${{ github.event.inputs.base }} 改动的 live 条目真跑"
python3 contracts/prompts/eval_gate.py --changed --base "${{ github.event.inputs.base }}"
fi

View File

@ -1,74 +0,0 @@
#!/usr/bin/env bash
# 治理机器门 pre-commit(激活一次:git config core.hooksPath .githooks)
# 各门按暂存文件路径条件触发(判据没动的提交不跑对应门,省时间);
# 紧急绕过:git commit --no-verify(服务端 Gitea Actions 门仍会兜住)。
set -uo pipefail
STAGED="$(git diff --cached --name-only)"
ROOT="$(git rev-parse --show-toplevel)"
fail=0
# ── 门 1:文档治理七检 —— 触发 = 任一 .md 变更 ──
if printf '%s\n' "$STAGED" | grep -qE '\.md$'; then
bash "$ROOT/.agents/tools/docs-gate.sh" || { echo '✘ docs-gate 七检未过'; fail=1; }
fi
# ── 门 2:门金标判例库全量回归 —— 触发 = 触及任一验收门判据或判例库自身 ──
# 判据文件改动前必须先让本回归全绿(误杀/漏放判例不得回退),口径见 contracts/gate-fixtures/README.md。
if printf '%s\n' "$STAGED" | grep -qE '(play\.cdp\.cjs|amodel-gen/tools\.mjs|check-undef\.mjs|gate_judge\.py|cheap_gates\.py|^contracts/gate-fixtures/)'; then
node "$ROOT/contracts/gate-fixtures/run.mjs" || { echo '✘ 门金标判例库回归未过'; fail=1; }
fi
# ── 门 3:play-loop 契约校验(PlaySpec C5 / VerdictFeedback C6)—— 触发 = 触及 play-loop 契约 ──
if printf '%s\n' "$STAGED" | grep -qE '^contracts/play-loop/'; then
python3 "$ROOT/contracts/play-loop/validate.py" --suite || { echo '✘ play-loop 契约正负样本套件未过'; fail=1; }
fi
# ── 门 3b:游戏内容对象存储契约正负样本 —— 触发 = 三份清单/状态迁移/实现或判例变更 ──
if printf '%s\n' "$STAGED" | grep -qE '(contracts/(game-(package|artifact-manifest|source-archive)\.schema\.json|db-schemas/V3[45].*artifact)|game-cloud/huijing-server/src/main/resources/db/migration/V3[45].*artifact|tier2/gen-worker/(worker/game_(artifact|source_archive|artifact_storage)_store\.py|worker/game_content_repository\.py|scripts/game_content_repository\.py|tests/test_game_(artifact|source_archive|artifact_storage|content_repository)))'; then
# mise 的默认 Python 可能未装 pytest仓内测试 venv 可用时复用,否则明确判红,绝不静默跳过。
PYTEST_PYTHON=python3
if ! "$PYTEST_PYTHON" -c 'import pytest' >/dev/null 2>&1; then
if [ -x "$ROOT/cheap-worker/.venv/bin/python" ] \
&& "$ROOT/cheap-worker/.venv/bin/python" -c 'import pytest' >/dev/null 2>&1; then
PYTEST_PYTHON="$ROOT/cheap-worker/.venv/bin/python"
else
echo '✘ 游戏内容对象存储门缺少 pytest默认 Python 与 cheap-worker/.venv 均不可用)'
PYTEST_PYTHON=''
fail=1
fi
fi
if [ -n "$PYTEST_PYTHON" ]; then
PYTHONPATH="$ROOT/tier2/gen-worker" "$PYTEST_PYTHON" -m pytest -q \
"$ROOT/tier2/gen-worker/tests/test_game_artifact_store.py" \
"$ROOT/tier2/gen-worker/tests/test_game_source_archive.py" \
"$ROOT/tier2/gen-worker/tests/test_game_artifact_storage_store.py" \
"$ROOT/tier2/gen-worker/tests/test_game_artifact_storage_ddl.py" \
"$ROOT/tier2/gen-worker/tests/test_game_content_repository.py" \
|| { echo '✘ 游戏内容对象存储契约正负样本未过'; fail=1; }
fi
fi
# ── 门 4:品类 rubric fixture ↔ skill 双写对账 —— 触发 = 触及品类 rubric fixture 或品类 skill ──
if printf '%s\n' "$STAGED" | grep -qE '(cheap-worker/fixtures/genre-rubrics/|\.agents/skills/(trpg|heritage|puzzle|narrative|sim-business)-game-design\.md)'; then
python3 "$ROOT/.agents/tools/rubric-sync-gate.py" || { echo '✘ rubric 双写对账未过'; fail=1; }
fi
# ── 门 6:便宜档插件面三方一致性对账 —— 触发 = 触及插件目录/prompt 白名单/host-config 注入/PLUGIN_KEY_DIR ──
# 防漂移:白名单列了但没注入(运行时崩)、注入了但白名单没列(暗资产)、新增插件目录未裁定(悄悄成暗插件)。
if printf '%s\n' "$STAGED" | grep -qE '(game-runtime/src/plugins/|contracts/prompts/04-config/cheap-system\.md|game-runtime/games/_template/src/host-config\.js|amodel-gen/tools\.mjs)'; then
python3 "$ROOT/.agents/tools/plugin-surface-gate.py" || { echo '✘ 插件面三方对账未过'; fail=1; }
fi
# ── 门 5:Prompt 契约双闸 —— 触发 = 触及 prompts 契约(registry.yaml 或任一 prompt .md) ──
# 闸 a = 版本一致性(check_registry:registry.yaml↔.md frontmatter 的 version 必须相等);
# 闸 b = 四道闸·闸0 版本升号(check_version_bump:改了 prompt 正文相对 HEAD 必须升 version)。
if printf '%s\n' "$STAGED" | grep -qE '^contracts/prompts/'; then
python3 "$ROOT/contracts/prompts/check_registry.py" || { echo '✘ Prompt Registry 版本一致性未过'; fail=1; }
python3 "$ROOT/contracts/prompts/check_version_bump.py" || { echo '✘ Prompt 闸0 version 升号未过(改正文须升 version)'; fail=1; }
fi
if [ "$fail" -ne 0 ]; then
echo '提交被拦(明细见上;紧急绕过 git commit --no-verify,服务端门仍兜)'
exit 1
fi

56
.gitignore vendored
View File

@ -13,26 +13,10 @@ coverage/
!.env.example
.idea/
# ── IDEA 共享项目配置允许提交模块、Maven、检查器、运行配置个人状态仍由 .idea/.gitignore 忽略 ──
!.idea/
!.idea/.gitignore
!.idea/*.iml
!.idea/*.xml
!.idea/inspectionProfiles/
!.idea/inspectionProfiles/*.xml
!.idea/jsLinters/
!.idea/jsLinters/*.xml
!.idea/runConfigurations/
!.idea/runConfigurations/*.xml
*.log
.DS_Store
.gstack/
# ── 本地个人环境配置:仅本机生效,绝不入库(机器画像 / 工具链 / 本地端口属个人配置)──
# 与 AGENTS.md §10 配套AGENTS.md 用 @localagents.md 可选引用,文件缺失不影响共享文档。
localagents.md
# ── 例外 1源码树src/)下名为 build 的目录是合法源码,绝非构建产物 ──
# 第 5 行笼统的 build/(为构建产物设计)会误吞 Java/前端源码中名为 build 的包/目录,例如:
# 后端 com.wanxiang.huijing.game.module.runtime.{dal.dataobject,dal.mysql,service}.build编译任务域4 文件)、
@ -65,43 +49,3 @@ __pycache__/
# docs/agent-specs/ 只放文档(*.md)spike 代码/批跑证据(hash 目录/jsonl/截图)/模型原始输出等可重生成产物不入文档树。
# __pycache__/ 与 node_modules/ 已由上方全局规则拦截;新 spike 原始 json/png 默认 untracked、勿 git add 批量产物。
# 注意(遵 gitignore-swallows-frontend-source 教训):此处不设 *.json/*.png 笼统忽略,以免误吞 contracts/ 等业务目录下的合法同类文件——分层靠规范 + 收口第 8 步执行,不靠笼统 glob。
# ── 游戏内容存储边界Git 只放平台代码具体游戏全部进入对象存储2026-07-29──
# 具体游戏的源码、素材、bundle、证据和参照游戏都不再作为平台源码进入 Git。
# 下列豁免只包含通用生成壳、平台模板和共享工具;它们不绑定任何一款具体游戏。
# 已跟踪的历史游戏不会因 ignore 自动消失,必须在对象存储消费切换和下载对账后独立删除。
/game-runtime/games/*
!/game-runtime/games/_generic/
!/game-runtime/games/_generic/**
!/game-runtime/games/_template/
!/game-runtime/games/_template/**
!/game-runtime/games/_template-feiyi/
!/game-runtime/games/_template-feiyi/**
!/game-runtime/games/_template-puzzle/
!/game-runtime/games/_template-puzzle/**
!/game-runtime/games/_template-shop/
!/game-runtime/games/_template-shop/**
!/game-runtime/games/_template-story/
!/game-runtime/games/_template-story/**
!/game-runtime/games/_template-trpg/
!/game-runtime/games/_template-trpg/**
!/game-runtime/games/_wg1-gen/
/game-runtime/games/_wg1-gen/*
!/game-runtime/games/_wg1-gen/_shared/
!/game-runtime/games/_wg1-gen/_shared/**
/game-runtime/games/_wg1-gen/_shared/**/__pycache__/
!/game-runtime/games/_wg1-gen/.agent
/tier2/games/*
# cheap-worker 本地虚拟环境与批跑结果不入库。
cheap-worker/.venv/
cheap-worker/results/
# ── 本机个人配置:localagents.md 自声明"绝不提交、已在 .gitignore 忽略",但此前无对应忽略行(声明未被机器执行);2026-07-06 补真 ──
/localagents.md
# ── AgentScope 源码库(本机开发直读参考,克隆自 github tag v2.0.2 = 已装运行版,2026-07-01)──
# clone 到 skill 目录供本机直接读 agentscope 2.0.2 源码(src/ + 已装 .venv 没有的 examples/docs/tests);
# 非本仓产物、~17MB、含自带 .git、可随时重克隆,不入库。遵「专一不笼统」:只忽略这一个克隆子目录,
# 不碰 skill 其余已跟踪文件(SKILL.md / references/ / scripts/ 共 9 文件)。版本要与 cheap-worker/.venv 装的对齐。
.claude/skills/agentscope-skill/agentscope/

10
.idea/.gitignore generated vendored
View File

@ -1,10 +0,0 @@
# 默认忽略的文件
/shelf/
/workspace.xml
# 基于编辑器的 HTTP 客户端请求
/httpRequests/
# 已忽略包含查询文件的默认文件夹
/queries/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

120
.idea/compiler.xml generated
View File

@ -1,120 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<annotationProcessing>
<profile name="Annotation profile for huijing" enabled="true">
<sourceOutputDir name="target/generated-sources/annotations" />
<sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
<outputRelativeToContentRoot value="true" />
<processorPath useClasspath="false">
<entry name="$MAVEN_REPOSITORY$/org/springframework/boot/spring-boot-configuration-processor/3.5.9/spring-boot-configuration-processor-3.5.9.jar" />
<entry name="$MAVEN_REPOSITORY$/org/projectlombok/lombok/1.18.46/lombok-1.18.46.jar" />
<entry name="$MAVEN_REPOSITORY$/org/projectlombok/lombok-mapstruct-binding/0.2.0/lombok-mapstruct-binding-0.2.0.jar" />
<entry name="$MAVEN_REPOSITORY$/org/mapstruct/mapstruct-processor/1.6.3/mapstruct-processor-1.6.3.jar" />
</processorPath>
<module name="huijing-spring-boot-starter-security" />
<module name="game-module-community-server" />
<module name="game-module-runtime-api" />
<module name="game-module-telemetry-server" />
<module name="huijing-module-pay-server" />
<module name="huijing-module-infra-api" />
<module name="game-module-project-server" />
<module name="game-module-ad-server" />
<module name="huijing-module-infra-server" />
<module name="game-module-biz-server" />
<module name="huijing-module-system-server" />
<module name="huijing-spring-boot-starter-protection" />
<module name="game-module-compliance-server" />
<module name="huijing-spring-boot-starter-mybatis" />
<module name="game-module-biz-api" />
<module name="huijing-spring-boot-starter-biz-data-permission" />
<module name="huijing-spring-boot-starter-redis" />
<module name="game-module-telemetry-api" />
<module name="huijing-gateway" />
<module name="huijing-spring-boot-starter-websocket" />
<module name="game-module-feed-api" />
<module name="game-module-feed-server" />
<module name="huijing-spring-boot-starter-excel" />
<module name="game-module-project-api" />
<module name="huijing-module-bpm-server" />
<module name="huijing-module-pay-api" />
<module name="game-module-compliance-api" />
<module name="huijing-common" />
<module name="game-module-aigc-api" />
<module name="game-module-runtime-server" />
<module name="huijing-module-bpm-api" />
<module name="huijing-spring-boot-starter-web" />
<module name="huijing-spring-boot-starter-env" />
<module name="huijing-spring-boot-starter-test" />
<module name="game-module-community-api" />
<module name="huijing-server" />
<module name="huijing-spring-boot-starter-rpc" />
<module name="huijing-spring-boot-starter-mq" />
<module name="huijing-module-system-api" />
<module name="huijing-spring-boot-starter-job" />
<module name="game-module-aigc-server" />
<module name="game-module-ad-api" />
<module name="game-module-trade-api" />
<module name="huijing-spring-boot-starter-biz-ip" />
<module name="game-module-studio-server" />
<module name="huijing-spring-boot-starter-monitor" />
<module name="game-module-trade-server" />
<module name="huijing-spring-boot-starter-biz-tenant" />
<module name="game-module-studio-api" />
</profile>
</annotationProcessing>
</component>
<component name="JavacSettings">
<option name="ADDITIONAL_OPTIONS_OVERRIDE">
<module name="game-module-ad-api" options="-parameters" />
<module name="game-module-ad-server" options="-parameters" />
<module name="game-module-aigc-api" options="-parameters" />
<module name="game-module-aigc-server" options="-parameters" />
<module name="game-module-biz-api" options="-parameters" />
<module name="game-module-biz-server" options="-parameters" />
<module name="game-module-community-api" options="-parameters" />
<module name="game-module-community-server" options="-parameters" />
<module name="game-module-compliance-api" options="-parameters" />
<module name="game-module-compliance-server" options="-parameters" />
<module name="game-module-feed-api" options="-parameters" />
<module name="game-module-feed-server" options="-parameters" />
<module name="game-module-project-api" options="-parameters" />
<module name="game-module-project-server" options="-parameters" />
<module name="game-module-runtime-api" options="-parameters" />
<module name="game-module-runtime-server" options="-parameters" />
<module name="game-module-studio-api" options="-parameters" />
<module name="game-module-studio-server" options="-parameters" />
<module name="game-module-telemetry-api" options="-parameters" />
<module name="game-module-telemetry-server" options="-parameters" />
<module name="game-module-trade-api" options="-parameters" />
<module name="game-module-trade-server" options="-parameters" />
<module name="huijing-common" options="-parameters" />
<module name="huijing-gateway" options="-parameters" />
<module name="huijing-module-bpm-api" options="-parameters" />
<module name="huijing-module-bpm-server" options="-parameters" />
<module name="huijing-module-infra-api" options="-parameters" />
<module name="huijing-module-infra-server" options="-parameters" />
<module name="huijing-module-pay-api" options="-parameters" />
<module name="huijing-module-pay-server" options="-parameters" />
<module name="huijing-module-system-api" options="-parameters" />
<module name="huijing-module-system-server" options="-parameters" />
<module name="huijing-server" options="-parameters" />
<module name="huijing-spring-boot-starter-biz-data-permission" options="-parameters" />
<module name="huijing-spring-boot-starter-biz-ip" options="-parameters" />
<module name="huijing-spring-boot-starter-biz-tenant" options="-parameters" />
<module name="huijing-spring-boot-starter-env" options="-parameters" />
<module name="huijing-spring-boot-starter-excel" options="-parameters" />
<module name="huijing-spring-boot-starter-job" options="-parameters" />
<module name="huijing-spring-boot-starter-monitor" options="-parameters" />
<module name="huijing-spring-boot-starter-mq" options="-parameters" />
<module name="huijing-spring-boot-starter-mybatis" options="-parameters" />
<module name="huijing-spring-boot-starter-protection" options="-parameters" />
<module name="huijing-spring-boot-starter-redis" options="-parameters" />
<module name="huijing-spring-boot-starter-rpc" options="-parameters" />
<module name="huijing-spring-boot-starter-security" options="-parameters" />
<module name="huijing-spring-boot-starter-test" options="-parameters" />
<module name="huijing-spring-boot-starter-web" options="-parameters" />
<module name="huijing-spring-boot-starter-websocket" options="-parameters" />
</option>
</component>
</project>

137
.idea/encodings.xml generated
View File

@ -1,137 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding">
<file url="file://$PROJECT_DIR$/game-cloud/game-module-ad/game-module-ad-api/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/game-module-ad/game-module-ad-api/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/game-module-ad/game-module-ad-server/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/game-module-ad/game-module-ad-server/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/game-module-ad/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/game-module-ad/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/game-module-aigc/game-module-aigc-api/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/game-module-aigc/game-module-aigc-api/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/game-module-aigc/game-module-aigc-server/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/game-module-aigc/game-module-aigc-server/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/game-module-aigc/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/game-module-aigc/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/game-module-biz/game-module-biz-api/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/game-module-biz/game-module-biz-api/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/game-module-biz/game-module-biz-server/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/game-module-biz/game-module-biz-server/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/game-module-biz/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/game-module-biz/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/game-module-community/game-module-community-api/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/game-module-community/game-module-community-api/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/game-module-community/game-module-community-server/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/game-module-community/game-module-community-server/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/game-module-community/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/game-module-community/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/game-module-compliance/game-module-compliance-api/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/game-module-compliance/game-module-compliance-api/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/game-module-compliance/game-module-compliance-server/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/game-module-compliance/game-module-compliance-server/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/game-module-compliance/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/game-module-compliance/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/game-module-feed/game-module-feed-api/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/game-module-feed/game-module-feed-api/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/game-module-feed/game-module-feed-server/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/game-module-feed/game-module-feed-server/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/game-module-feed/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/game-module-feed/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/game-module-project/game-module-project-api/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/game-module-project/game-module-project-api/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/game-module-project/game-module-project-server/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/game-module-project/game-module-project-server/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/game-module-project/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/game-module-project/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/game-module-runtime/game-module-runtime-api/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/game-module-runtime/game-module-runtime-api/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/game-module-runtime/game-module-runtime-server/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/game-module-runtime/game-module-runtime-server/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/game-module-runtime/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/game-module-runtime/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/game-module-studio/game-module-studio-api/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/game-module-studio/game-module-studio-api/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/game-module-studio/game-module-studio-server/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/game-module-studio/game-module-studio-server/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/game-module-studio/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/game-module-studio/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/game-module-telemetry/game-module-telemetry-api/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/game-module-telemetry/game-module-telemetry-api/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/game-module-telemetry/game-module-telemetry-server/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/game-module-telemetry/game-module-telemetry-server/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/game-module-telemetry/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/game-module-telemetry/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/game-module-trade/game-module-trade-api/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/game-module-trade/game-module-trade-api/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/game-module-trade/game-module-trade-server/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/game-module-trade/game-module-trade-server/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/game-module-trade/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/game-module-trade/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/huijing-framework/huijing-common/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/huijing-framework/huijing-common/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/huijing-framework/huijing-spring-boot-starter-biz-data-permission/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/huijing-framework/huijing-spring-boot-starter-biz-data-permission/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/huijing-framework/huijing-spring-boot-starter-biz-ip/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/huijing-framework/huijing-spring-boot-starter-biz-ip/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/huijing-framework/huijing-spring-boot-starter-biz-tenant/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/huijing-framework/huijing-spring-boot-starter-biz-tenant/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/huijing-framework/huijing-spring-boot-starter-env/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/huijing-framework/huijing-spring-boot-starter-env/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/huijing-framework/huijing-spring-boot-starter-excel/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/huijing-framework/huijing-spring-boot-starter-excel/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/huijing-framework/huijing-spring-boot-starter-job/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/huijing-framework/huijing-spring-boot-starter-job/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/huijing-framework/huijing-spring-boot-starter-monitor/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/huijing-framework/huijing-spring-boot-starter-monitor/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/huijing-framework/huijing-spring-boot-starter-mq/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/huijing-framework/huijing-spring-boot-starter-mq/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/huijing-framework/huijing-spring-boot-starter-mybatis/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/huijing-framework/huijing-spring-boot-starter-mybatis/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/huijing-framework/huijing-spring-boot-starter-protection/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/huijing-framework/huijing-spring-boot-starter-protection/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/huijing-framework/huijing-spring-boot-starter-redis/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/huijing-framework/huijing-spring-boot-starter-redis/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/huijing-framework/huijing-spring-boot-starter-rpc/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/huijing-framework/huijing-spring-boot-starter-rpc/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/huijing-framework/huijing-spring-boot-starter-security/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/huijing-framework/huijing-spring-boot-starter-security/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/huijing-framework/huijing-spring-boot-starter-test/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/huijing-framework/huijing-spring-boot-starter-test/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/huijing-framework/huijing-spring-boot-starter-web/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/huijing-framework/huijing-spring-boot-starter-web/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/huijing-framework/huijing-spring-boot-starter-websocket/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/huijing-framework/huijing-spring-boot-starter-websocket/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/huijing-framework/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/huijing-framework/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/huijing-gateway/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/huijing-gateway/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/huijing-module-bpm/huijing-module-bpm-api/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/huijing-module-bpm/huijing-module-bpm-api/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/huijing-module-bpm/huijing-module-bpm-server/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/huijing-module-bpm/huijing-module-bpm-server/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/huijing-module-bpm/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/huijing-module-bpm/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/huijing-module-infra/huijing-module-infra-api/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/huijing-module-infra/huijing-module-infra-api/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/huijing-module-infra/huijing-module-infra-server/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/huijing-module-infra/huijing-module-infra-server/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/huijing-module-infra/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/huijing-module-infra/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/huijing-module-pay/huijing-module-pay-api/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/huijing-module-pay/huijing-module-pay-api/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/huijing-module-pay/huijing-module-pay-server/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/huijing-module-pay/huijing-module-pay-server/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/huijing-module-pay/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/huijing-module-pay/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/huijing-module-system/huijing-module-system-api/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/huijing-module-system/huijing-module-system-api/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/huijing-module-system/huijing-module-system-server/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/huijing-module-system/huijing-module-system-server/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/huijing-module-system/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/huijing-module-system/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/huijing-server/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/huijing-server/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game-cloud/src/main/resources" charset="UTF-8" />
</component>
</project>

14
.idea/game-admin.iml generated
View File

@ -1,14 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$/../game-admin">
<sourceFolder url="file://$MODULE_DIR$/../game-admin/src" isTestSource="false" />
<excludeFolder url="file://$MODULE_DIR$/../game-admin/dist" />
<excludeFolder url="file://$MODULE_DIR$/../game-admin/node_modules" />
<excludeFolder url="file://$MODULE_DIR$/../game-admin/coverage" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

108
.idea/game-cloud.iml generated
View File

@ -1,108 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$/../game-cloud">
<sourceFolder url="file://$MODULE_DIR$/../game-cloud/game-module-ad/game-module-ad-api/src/main/java" type="java-source" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../game-cloud/game-module-ad/game-module-ad-server/src/main/java" type="java-source" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../game-cloud/game-module-ad/game-module-ad-server/src/test/java" type="java-source" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/../game-cloud/game-module-aigc/game-module-aigc-api/src/main/java" type="java-source" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../game-cloud/game-module-aigc/game-module-aigc-server/src/main/java" type="java-source" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../game-cloud/game-module-aigc/game-module-aigc-server/src/test/java" type="java-source" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/../game-cloud/game-module-aigc/game-module-aigc-server/src/test/resources" type="java-resource" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/../game-cloud/game-module-biz/game-module-biz-api/src/main/java" type="java-source" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../game-cloud/game-module-biz/game-module-biz-server/src/main/java" type="java-source" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../game-cloud/game-module-biz/game-module-biz-server/src/test/java" type="java-source" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/../game-cloud/game-module-community/game-module-community-api/src/main/java" type="java-source" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../game-cloud/game-module-community/game-module-community-server/src/main/java" type="java-source" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../game-cloud/game-module-community/game-module-community-server/src/test/java" type="java-source" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/../game-cloud/game-module-compliance/game-module-compliance-api/src/main/java" type="java-source" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../game-cloud/game-module-compliance/game-module-compliance-server/src/main/java" type="java-source" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../game-cloud/game-module-compliance/game-module-compliance-server/src/test/java" type="java-source" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/../game-cloud/game-module-feed/game-module-feed-api/src/main/java" type="java-source" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../game-cloud/game-module-feed/game-module-feed-server/src/main/java" type="java-source" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../game-cloud/game-module-feed/game-module-feed-server/src/test/java" type="java-source" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/../game-cloud/game-module-project/game-module-project-api/src/main/java" type="java-source" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../game-cloud/game-module-project/game-module-project-server/src/main/java" type="java-source" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../game-cloud/game-module-project/game-module-project-server/src/test/java" type="java-source" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/../game-cloud/game-module-runtime/game-module-runtime-api/src/main/java" type="java-source" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../game-cloud/game-module-runtime/game-module-runtime-server/src/main/java" type="java-source" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../game-cloud/game-module-runtime/game-module-runtime-server/src/test/java" type="java-source" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/../game-cloud/game-module-studio/game-module-studio-api/src/main/java" type="java-source" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../game-cloud/game-module-studio/game-module-studio-server/src/main/java" type="java-source" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../game-cloud/game-module-studio/game-module-studio-server/src/test/java" type="java-source" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/../game-cloud/game-module-telemetry/game-module-telemetry-api/src/main/java" type="java-source" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../game-cloud/game-module-telemetry/game-module-telemetry-server/src/main/java" type="java-source" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../game-cloud/game-module-telemetry/game-module-telemetry-server/src/test/java" type="java-source" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/../game-cloud/game-module-trade/game-module-trade-api/src/main/java" type="java-source" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../game-cloud/game-module-trade/game-module-trade-server/src/main/java" type="java-source" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../game-cloud/game-module-trade/game-module-trade-server/src/test/java" type="java-source" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/../game-cloud/huijing-framework/huijing-common/src/main/java" type="java-source" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../game-cloud/huijing-framework/huijing-common/src/test/java" type="java-source" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/../game-cloud/huijing-framework/huijing-spring-boot-starter-biz-data-permission/src/main/java" type="java-source" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../game-cloud/huijing-framework/huijing-spring-boot-starter-biz-data-permission/src/main/resources" type="java-resource" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../game-cloud/huijing-framework/huijing-spring-boot-starter-biz-data-permission/src/test/java" type="java-source" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/../game-cloud/huijing-framework/huijing-spring-boot-starter-biz-ip/src/main/java" type="java-source" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../game-cloud/huijing-framework/huijing-spring-boot-starter-biz-ip/src/main/resources" type="java-resource" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../game-cloud/huijing-framework/huijing-spring-boot-starter-biz-ip/src/test/java" type="java-source" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/../game-cloud/huijing-framework/huijing-spring-boot-starter-biz-tenant/src/main/java" type="java-source" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../game-cloud/huijing-framework/huijing-spring-boot-starter-biz-tenant/src/main/resources" type="java-resource" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../game-cloud/huijing-framework/huijing-spring-boot-starter-env/src/main/java" type="java-source" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../game-cloud/huijing-framework/huijing-spring-boot-starter-env/src/main/resources" type="java-resource" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../game-cloud/huijing-framework/huijing-spring-boot-starter-excel/src/main/java" type="java-source" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../game-cloud/huijing-framework/huijing-spring-boot-starter-excel/src/main/resources" type="java-resource" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../game-cloud/huijing-framework/huijing-spring-boot-starter-excel/src/test/java" type="java-source" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/../game-cloud/huijing-framework/huijing-spring-boot-starter-job/src/main/java" type="java-source" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../game-cloud/huijing-framework/huijing-spring-boot-starter-job/src/main/resources" type="java-resource" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../game-cloud/huijing-framework/huijing-spring-boot-starter-monitor/src/main/java" type="java-source" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../game-cloud/huijing-framework/huijing-spring-boot-starter-monitor/src/main/resources" type="java-resource" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../game-cloud/huijing-framework/huijing-spring-boot-starter-mq/src/main/java" type="java-source" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../game-cloud/huijing-framework/huijing-spring-boot-starter-mq/src/main/resources" type="java-resource" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../game-cloud/huijing-framework/huijing-spring-boot-starter-mybatis/src/main/java" type="java-source" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../game-cloud/huijing-framework/huijing-spring-boot-starter-mybatis/src/main/resources" type="java-resource" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../game-cloud/huijing-framework/huijing-spring-boot-starter-mybatis/src/test/java" type="java-source" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/../game-cloud/huijing-framework/huijing-spring-boot-starter-protection/src/main/java" type="java-source" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../game-cloud/huijing-framework/huijing-spring-boot-starter-protection/src/main/resources" type="java-resource" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../game-cloud/huijing-framework/huijing-spring-boot-starter-protection/src/test/java" type="java-source" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/../game-cloud/huijing-framework/huijing-spring-boot-starter-redis/src/main/java" type="java-source" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../game-cloud/huijing-framework/huijing-spring-boot-starter-redis/src/main/resources" type="java-resource" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../game-cloud/huijing-framework/huijing-spring-boot-starter-rpc/src/main/java" type="java-source" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../game-cloud/huijing-framework/huijing-spring-boot-starter-security/src/main/java" type="java-source" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../game-cloud/huijing-framework/huijing-spring-boot-starter-security/src/main/resources" type="java-resource" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../game-cloud/huijing-framework/huijing-spring-boot-starter-test/src/main/java" type="java-source" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../game-cloud/huijing-framework/huijing-spring-boot-starter-web/src/main/java" type="java-source" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../game-cloud/huijing-framework/huijing-spring-boot-starter-web/src/main/resources" type="java-resource" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../game-cloud/huijing-framework/huijing-spring-boot-starter-web/src/test/java" type="java-source" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/../game-cloud/huijing-framework/huijing-spring-boot-starter-websocket/src/main/java" type="java-source" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../game-cloud/huijing-framework/huijing-spring-boot-starter-websocket/src/main/resources" type="java-resource" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../game-cloud/huijing-gateway/src/main/java" type="java-source" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../game-cloud/huijing-gateway/src/main/resources" type="java-resource" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../game-cloud/huijing-gateway/src/test/java" type="java-source" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/../game-cloud/huijing-module-bpm/huijing-module-bpm-api/src/main/java" type="java-source" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../game-cloud/huijing-module-bpm/huijing-module-bpm-server/src/main/java" type="java-source" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../game-cloud/huijing-module-bpm/huijing-module-bpm-server/src/main/resources" type="java-resource" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../game-cloud/huijing-module-bpm/huijing-module-bpm-server/src/test/java" type="java-source" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/../game-cloud/huijing-module-bpm/huijing-module-bpm-server/src/test/resources" type="java-resource" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/../game-cloud/huijing-module-infra/huijing-module-infra-api/src/main/java" type="java-source" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../game-cloud/huijing-module-infra/huijing-module-infra-server/src/main/java" type="java-source" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../game-cloud/huijing-module-infra/huijing-module-infra-server/src/main/resources" type="java-resource" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../game-cloud/huijing-module-infra/huijing-module-infra-server/src/test/java" type="java-source" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/../game-cloud/huijing-module-infra/huijing-module-infra-server/src/test/resources" type="java-resource" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/../game-cloud/huijing-module-pay/huijing-module-pay-api/src/main/java" type="java-source" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../game-cloud/huijing-module-pay/huijing-module-pay-server/src/main/java" type="java-source" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../game-cloud/huijing-module-pay/huijing-module-pay-server/src/main/resources" type="java-resource" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../game-cloud/huijing-module-pay/huijing-module-pay-server/src/test/java" type="java-source" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/../game-cloud/huijing-module-pay/huijing-module-pay-server/src/test/resources" type="java-resource" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/../game-cloud/huijing-module-system/huijing-module-system-api/src/main/java" type="java-source" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../game-cloud/huijing-module-system/huijing-module-system-server/src/main/java" type="java-source" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../game-cloud/huijing-module-system/huijing-module-system-server/src/main/resources" type="java-resource" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../game-cloud/huijing-module-system/huijing-module-system-server/src/test/java" type="java-source" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/../game-cloud/huijing-module-system/huijing-module-system-server/src/test/resources" type="java-resource" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/../game-cloud/huijing-server/src/main/java" type="java-source" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../game-cloud/huijing-server/src/main/resources" type="java-resource" isTestSource="false" />
<excludeFolder url="file://$MODULE_DIR$/../game-cloud/target" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

14
.idea/game-runtime.iml generated
View File

@ -1,14 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$/../game-runtime">
<sourceFolder url="file://$MODULE_DIR$/../game-runtime/src" isTestSource="false" />
<excludeFolder url="file://$MODULE_DIR$/../game-runtime/dist" />
<excludeFolder url="file://$MODULE_DIR$/../game-runtime/node_modules" />
<excludeFolder url="file://$MODULE_DIR$/../game-runtime/coverage" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

14
.idea/game-studio.iml generated
View File

@ -1,14 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$/../game-studio">
<sourceFolder url="file://$MODULE_DIR$/../game-studio/src" isTestSource="false" />
<excludeFolder url="file://$MODULE_DIR$/../game-studio/dist" />
<excludeFolder url="file://$MODULE_DIR$/../game-studio/node_modules" />
<excludeFolder url="file://$MODULE_DIR$/../game-studio/coverage" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

View File

@ -1,18 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$/..">
<excludeFolder url="file://$MODULE_DIR$/../.git" />
<excludeFolder url="file://$MODULE_DIR$/../.gstack" />
<excludeFolder url="file://$MODULE_DIR$/../game-admin/dist" />
<excludeFolder url="file://$MODULE_DIR$/../game-admin/node_modules" />
<excludeFolder url="file://$MODULE_DIR$/../game-runtime/dist" />
<excludeFolder url="file://$MODULE_DIR$/../game-runtime/node_modules" />
<excludeFolder url="file://$MODULE_DIR$/../game-studio/dist" />
<excludeFolder url="file://$MODULE_DIR$/../game-studio/node_modules" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

View File

@ -1,6 +0,0 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="Eslint" enabled="true" level="WARNING" enabled_by_default="true" />
</profile>
</component>

View File

@ -1,40 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RemoteRepositoriesConfiguration">
<remote-repository>
<option name="id" value="maven-release" />
<option name="name" value="Nexus Release Repository" />
<option name="url" value="https://nexus.chinahuatong.com.cn:8443/repository/maven-releases/" />
</remote-repository>
<remote-repository>
<option name="id" value="aliyunmaven" />
<option name="name" value="aliyun" />
<option name="url" value="https://maven.aliyun.com/repository/public" />
</remote-repository>
<remote-repository>
<option name="id" value="maven-snapshots" />
<option name="name" value="Nexus Snapshot Repository" />
<option name="url" value="https://nexus.chinahuatong.com.cn:8443/repository/maven-snapshots/" />
</remote-repository>
<remote-repository>
<option name="id" value="central" />
<option name="name" value="Maven Central repository" />
<option name="url" value="https://repo1.maven.org/maven2" />
</remote-repository>
<remote-repository>
<option name="id" value="jboss.community" />
<option name="name" value="JBoss Community repository" />
<option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
</remote-repository>
<remote-repository>
<option name="id" value="huaweicloud" />
<option name="name" value="huawei" />
<option name="url" value="https://mirrors.huaweicloud.com/repository/maven/" />
</remote-repository>
<remote-repository>
<option name="id" value="central" />
<option name="name" value="Central Repository" />
<option name="url" value="https://maven.aliyun.com/repository/public" />
</remote-repository>
</component>
</project>

View File

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="JavaScriptLibraryMappings">
<includedPredefinedLibrary name="Node.js Core" />
</component>
</project>

View File

@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="EslintConfiguration">
<option name="fix-on-save" value="true" />
<option name="mode" value="AUTOMATIC" />
</component>
</project>

14
.idea/misc.xml generated
View File

@ -1,14 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="MavenProjectsManager">
<option name="originalFiles">
<list>
<option value="$PROJECT_DIR$/game-cloud/pom.xml" />
</list>
</option>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="corretto-21" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>

11
.idea/modules.xml generated
View File

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/game-admin.iml" filepath="$PROJECT_DIR$/.idea/game-admin.iml" />
<module fileurl="file://$PROJECT_DIR$/.idea/game-cloud.iml" filepath="$PROJECT_DIR$/.idea/game-cloud.iml" />
<module fileurl="file://$PROJECT_DIR$/.idea/game-runtime.iml" filepath="$PROJECT_DIR$/.idea/game-runtime.iml" />
<module fileurl="file://$PROJECT_DIR$/.idea/game-studio.iml" filepath="$PROJECT_DIR$/.idea/game-studio.iml" />
</modules>
</component>
</project>

6
.idea/nodejs.xml generated
View File

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="NodeJsProjectSettings">
<option name="packageManagerPath" value="npm" />
</component>
</project>

6
.idea/prettier.xml generated
View File

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="PrettierConfiguration">
<option name="myConfigurationMode" value="AUTOMATIC" />
</component>
</project>

View File

@ -1,12 +0,0 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="game-admin: dev" type="js.build_tools.npm" nameIsGenerated="true">
<package-json value="$PROJECT_DIR$/game-admin/package.json" />
<command value="run" />
<scripts>
<script value="dev" />
</scripts>
<node-interpreter value="project" />
<envs />
<method v="2" />
</configuration>
</component>

View File

@ -1,12 +0,0 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="game-admin: ts:check" type="js.build_tools.npm" nameIsGenerated="true">
<package-json value="$PROJECT_DIR$/game-admin/package.json" />
<command value="run" />
<scripts>
<script value="ts:check" />
</scripts>
<node-interpreter value="project" />
<envs />
<method v="2" />
</configuration>
</component>

View File

@ -1,11 +0,0 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="game-cloud: HuijingServer local" type="SpringBootApplicationConfigurationType" factoryName="Spring Boot">
<module name="huijing-server" />
<option name="SPRING_BOOT_MAIN_CLASS" value="com.wanxiang.huijing.server.HuijingServerApplication" />
<option name="ACTIVE_PROFILES" value="local" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/game-cloud" />
<method v="2">
<option name="Make" enabled="true" />
</method>
</configuration>
</component>

View File

@ -1,33 +0,0 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="game-cloud: maven install" type="MavenRunConfiguration" factoryName="Maven">
<MavenSettings>
<option name="myGeneralSettings" />
<option name="myRunnerSettings" />
<option name="myRunnerParameters">
<MavenRunnerParameters>
<option name="cmdOptions" />
<option name="profiles">
<set />
</option>
<option name="goals">
<list>
<option value="clean" />
<option value="install" />
<option value="-DskipTests" />
</list>
</option>
<option name="pomFileName" />
<option name="profilesMap">
<map />
</option>
<option name="projectsCmdOptionValues">
<list />
</option>
<option name="resolveToWorkspace" value="false" />
<option name="workingDirPath" value="$PROJECT_DIR$/game-cloud" />
</MavenRunnerParameters>
</option>
</MavenSettings>
<method v="2" />
</configuration>
</component>

View File

@ -1,12 +0,0 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="game-runtime: build" type="js.build_tools.npm" nameIsGenerated="true">
<package-json value="$PROJECT_DIR$/game-runtime/package.json" />
<command value="run" />
<scripts>
<script value="build" />
</scripts>
<node-interpreter value="project" />
<envs />
<method v="2" />
</configuration>
</component>

View File

@ -1,12 +0,0 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="game-runtime: test" type="js.build_tools.npm" nameIsGenerated="true">
<package-json value="$PROJECT_DIR$/game-runtime/package.json" />
<command value="run" />
<scripts>
<script value="test" />
</scripts>
<node-interpreter value="project" />
<envs />
<method v="2" />
</configuration>
</component>

View File

@ -1,12 +0,0 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="game-studio: build" type="js.build_tools.npm" nameIsGenerated="true">
<package-json value="$PROJECT_DIR$/game-studio/package.json" />
<command value="run" />
<scripts>
<script value="build" />
</scripts>
<node-interpreter value="project" />
<envs />
<method v="2" />
</configuration>
</component>

View File

@ -1,12 +0,0 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="game-studio: dev" type="js.build_tools.npm" nameIsGenerated="true">
<package-json value="$PROJECT_DIR$/game-studio/package.json" />
<command value="run" />
<scripts>
<script value="dev" />
</scripts>
<node-interpreter value="project" />
<envs />
<method v="2" />
</configuration>
</component>

6
.idea/vcs.xml generated
View File

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

Some files were not shown because too many files have changed in this diff Show More