W-G1 生成核心:brief→便宜模型写 GameHostFactory→esbuild __GameBundle iife→P1 bootGameHost
装载→本机 Chrome+CDP 七门真玩取证;失败回喂错因重试。只消费冻结契约,零改 P1/P2。
实测(deepseek-v4-flash 最便宜档):Pong/Simon/打地鼠 三款【七门全过真玩】,
单款 ¥0.01–0.03(远低于 ¥0.15 L1 闸门)。最优模型:flash 打底 + 难档升 deepseek-v4-pro(Simon 0 重试 ¥0.06)。
关键:实证「能生成代码 ≠ 能玩」——一处 prompt 输入坐标契约写错(误称 x/y∈[0,1],实为逻辑像素)
让生成游戏能编译/渲染/动但点击全失灵;Pong 因自走动画假绿混过、Simon 被正确判否。
为此加装 G 门(确定性 A/B 对照,隔离自走动画假绿)+ 修 prompt 后三款真过。
- harness:games/_wg1-gen/_shared/{entry-bundle 模板,index 模板,play.cdp.cjs 七门,serve-and-play.sh}(复用 ref buildRefHostConfig + browser-evidence.cjs)
- worker:wg1/gen-worker/worker/{_client(代理旁路),prompt(生成上下文包),validate(静态门+node --check),cost(¥折算),run(§3 编排+重试门)}
- golden-path 自检:ref 工厂 5/6(F=0 菜单态正确)+ probe 工厂 7/7 → harness 无假绿
- game-runtime/package.json:加 ws(CDP 驱动 devDep)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
153 lines
12 KiB
Markdown
153 lines
12 KiB
Markdown
# W-G1 生成核心 · 交付报告(结论先行)
|
||
|
||
> lane:`wg1/gen-worker`(从 `spike/agentscope-wg1` HEAD = dev/2.0.0 + 已落地 P1/P2/spike 切出)| 日期:2026-06-13
|
||
> 验证假设 **HJ-GEN-001**:便宜模型(new-api: DeepSeek-V4 / MiniMax)能在【插件库 + 装载契约】上写出**可真玩**的轻游戏;质量由 **harness 门**保证,非模型自觉。
|
||
|
||
---
|
||
|
||
## 0. 结论速览
|
||
|
||
**结论:成立。便宜模型能可靠产出可真玩的轻游戏。** 用最便宜档 `deepseek-v4-flash` 生成 3 款冒烟游戏,全部经 **CDP 真机 Chrome 七门真玩**通过,**单款 ¥0.01–0.03(远低于 ¥0.15 L1 闸门,5–15× 余量)**。
|
||
|
||
| 游戏 | 模型 | 真玩七门 | 重试 | ¥(上界) vs ¥0.15 |
|
||
|---|---|---|---|---|
|
||
| Pong 弹球 | deepseek-v4-flash | ✅ 7/7 | 0 | **¥0.0098** ✅ |
|
||
| 打地鼠 Whack | deepseek-v4-flash | ✅ 7/7 | 0 | **¥0.0136** ✅ |
|
||
| Simon 记忆 | deepseek-v4-flash | ✅ 7/7 | 2 | **¥0.0298** ✅ |
|
||
|
||
**最关键的一条(必须如实传达)**:**「能生成代码」≠「能玩」**。本 lane 实证了这一点——一处由**我方 prompt 写错**的输入坐标契约(声称 x/y∈[0,1],实为逻辑像素)让所有生成游戏"能编译、能渲染、能动",但**玩家点击全部失灵**;Pong 因小球自走动画**假绿混过**初版门,Simon 因无自走动画被门**正确判否**。**正是真玩门(而非"代码编译过")逮住了它。** 修复 prompt + 加装「输入有效性确定性对照门」后,三款全部真过。
|
||
|
||
---
|
||
|
||
## 1. 产物靶与全链路(只消费冻结契约,零改 P1/P2)
|
||
|
||
```
|
||
brief → 便宜模型 → factory.js(纯 GameHostFactory 模块:默认导出,零引擎 import,能力经 boot.ctx)
|
||
↓ 通用壳(复用 ref buildRefHostConfig:标准 7 插件 wiring,仅 override factory)
|
||
entry-bundle.js → esbuild --global-name=__GameBundle → bundle.iife.js(引擎内联 265KB)
|
||
↓ index.html: window.__GameBundle.bootGameHost({canvas, recHook, onReady})
|
||
static-serve + 本机 Chrome(headless) + CDP → 七门真玩取证
|
||
```
|
||
|
||
- **冻结契约零修改**:`game-host.d.ts` / `boot-game-host.js` / 6 受控面 / `__GameBundle` / `bootGameHost` 全程只消费。
|
||
- **生成游戏落 `game-runtime/games/_wg1-gen/`**(新建隔离子目录,零碰既有文件)——因生成游戏本质是 game-runtime 游戏(动态 import `littlejsengine`、import `../../../src/plugins/*`、复用 `ref-host-config.js`),esbuild 默认解析要求入口在 node_modules 可达处。worker(Python) 与报告留 `wg1/gen-worker/`。详见 [`games/_wg1-gen/.agent`](../../game-runtime/games/_wg1-gen/.agent)。
|
||
- **唯一对既有树的非生成改动**:`game-runtime/package.json` 加 `ws`(devDep,CDP 驱动用,ref harness 亦需)。
|
||
|
||
---
|
||
|
||
## 2. 真玩取证 harness:七道假绿守卫(false-green guard)
|
||
|
||
每款游戏经下列七门,**全过才判 PASS**(驱动 = [`games/_wg1-gen/_shared/play.cdp.cjs`](../../game-runtime/games/_wg1-gen/_shared/play.cdp.cjs),复用 `test/harness/browser-evidence.cjs`):
|
||
|
||
| 门 | 判据 | 防的假绿 |
|
||
|---|---|---|
|
||
| **A 装载** | `__genBooted ∧ __gameHostEngineInitFired ∧ 无 bootError` | 白屏 / 装载失败 |
|
||
| **B 未捕获** | `__genInternals.uncaught == [] ∧ 无 __gameHostBootError` | 运行期报错 |
|
||
| **C 掌帧** | `__engine.frame` 500ms 增量 ∈ [20,45](@60fps≈30) | 冻屏 / 失控 / 单步假推进 |
|
||
| **D 真渲染** | `#game-engine` readback 有色像素(maxCh>80)>50 | 白屏 / 全黑 |
|
||
| **E 活性** | 真玩全程 ≥2 个不同整帧哈希态 | 冻屏(多采样捕捉瞬时态) |
|
||
| **F 真接线** | `__engineCalls` 经 recHook 含期望 call-ID(粒子/音效/数学) | 自绘伪装、没真用插件库/引擎 |
|
||
| **G 输入有效** | **同 seed 再开无输入对照实例、推进到同一帧、整帧哈希 ≠ 有输入实例** | **靠自走动画蒙混过 E 的假绿** |
|
||
|
||
> **G 门是本 lane 关键发明**:Pong/打地鼠有自走动画(球/地鼠),E 门会被自走动画满足、掩盖「输入失灵」。G 门用**确定性 A/B 对照**(new-api 生成 bundle seed 固定→自走部分逐帧确定)隔离出「输入到底改没改变游戏走向」——挡板位置 / Simon 轮次 / 打地鼠分数都会持续到对照帧,有输入≠无输入即证真可玩。
|
||
|
||
**golden-path 自检(证 harness 可信)**:先用 ref 工厂(已知好)跑通整条链——5/6 门过、F=0(菜单态不调引擎,**正确**);再用最小 probe 工厂(确定性调 getEngine)→ 7/7 全过。**harness 在真可玩上判过、在没真调引擎/没真响应输入时判否**,无假绿。
|
||
|
||
---
|
||
|
||
## 3. 逐款四件套(真玩证据)
|
||
|
||
> 证据落 `game-runtime/games/_wg1-gen/<game>/evidence/{first-paint.png, after-play.png, verdict.json}`。
|
||
|
||
### Pong 弹球(deepseek-v4-flash · 0 重试 · ¥0.0098)
|
||
- **①截图**:竖屏弹球——白球在场、青色玩家挡板(已移到末次点击 x)、虚线中线、`Player:0 CPU:1`(真打了一回合)、撞击红粒子。
|
||
- **②掌帧**:Δframe=31(C✅)。**③渲染**:maxCh=255、有色像素满屏(D✅)。**④真接线**:`__engineCalls` 含 `particles.spawnEmitter`/`audio.synth.synthSfx`(撞击反馈,F✅)。
|
||
- **输入有效(G✅)**:有输入态 `0d7715dc` ≠ 无输入对照 `855a1c03` @frame444 → 挡板真响应点击。228 行源码,`export default createPongGame`,`ctx.random`/`ctx.getInput`/`ctx.time` 全受控。
|
||
|
||
### 打地鼠 Whack(deepseek-v4-flash · 0 重试 · ¥0.0136)
|
||
- **①截图**:`得分:0`、3×3 深色洞口、两只地鼠(棕圆+眼睛+腮红)正冒出。
|
||
- **②③④**:C✅ / D✅(maxCh255) / F✅(命中粒子 + 冒出音效)。
|
||
- **输入有效(G✅)**:`a9de479b ≠ b0584efe` @frame446 → 点击真改变游戏(命中/分数)。命中判定为自写"点到圆距"(不依赖网格/CCD)。
|
||
|
||
### Simon 记忆(deepseek-v4-flash · 2 重试 · ¥0.0298)
|
||
- **①截图**:`Round:1`、`Your turn!`、红/绿/蓝/黄 2×2 大按钮。
|
||
- **②③④**:C✅(Δ31) / D✅(maxCh255,bright272006) / F✅(序列音效 `audio.synth.synthSfx` + 缓动 `math.easing.quadOut`)。
|
||
- **输入有效(G✅)**:`a5100b7a ≠ f7530fdd` @frame400 → 点击真推进序列/轮次。状态机(INIT→SHOWING→WAIT→PAUSE/FAIL)完整。
|
||
- **重试原因**:附录 A——首版坐标错(修 prompt 前)/ 逻辑边界,靠错误回喂 3 轮内收敛。
|
||
|
||
---
|
||
|
||
## 4. 成本表 + 模型对比 → 最优模型
|
||
|
||
口径:`quota=(prompt+completion×completion_ratio)×model_ratio×group_ratio(1.0)`;`¥=quota/500000×7.3`(`/api/status` 实拉)。**¥为上界**(不计 prompt 缓存折扣;精确 `logs.quota` 需网关 access token,sk- key 无管理权——留给创始人核)。reasoning_tokens 已计入 completion。
|
||
|
||
| 游戏(难度) | 模型 | 重试 | prompt/completion tok | ¥上界 | vs ¥0.15 |
|
||
|---|---|---|---|---|---|
|
||
| Pong(易) | deepseek-v4-flash | 0 | 2861 / 4775 | 0.0098 | ✅ 15× 余量 |
|
||
| 打地鼠(易+) | deepseek-v4-flash | 0 | 2899 / 7223 | 0.0136 | ✅ 11× |
|
||
| Simon(中) | deepseek-v4-flash | 2 | 9120 / 14220 | 0.0298 | ✅ 5× |
|
||
| **Simon** | **deepseek-v4-pro** | **0** | 2889 / 9445 | 0.0625 | ✅ |
|
||
| Simon | MiniMax-M2.7 | 1 | 5582 / 11912 | 0.1166 | ✅(贴闸门) |
|
||
|
||
**最优模型结论**:
|
||
- **默认 = `deepseek-v4-flash`**:最便宜(model_ratio 0.06 / completion_ratio 1.75),易档 0 重试 ¥0.01;难档(Simon)靠重试门兜(2 重试仍 ¥0.03)。
|
||
- **难档升级 = `deepseek-v4-pro`**:Simon **0 重试**、¥0.06(2× flash 但仍极廉),可靠性最高。**建议策略:flash 打底 + 耗尽重试自动升 pro。**
|
||
- **MiniMax 不优先**:M2.7 completion_ratio=4 拉高成本(¥0.117 贴近闸门),可靠性不及 pro。
|
||
|
||
---
|
||
|
||
## 5. 五大坑(给主 agent / 创始人少踩半天)
|
||
|
||
1. **【实测·最坑】受控输入 x/y 是逻辑像素不是 [0,1]**:`boot-game-host.js:295` `x=(clientX-rect.left)/rect.width*VIEW_W(390)`。ref 直接 `hitZone(e.x,e.y)` 用像素。若 prompt 误导模型"乘视口"→命中全爆掉、点击失灵,但**代码照编译照渲染照动**——只有真玩门逮得住。**生成 prompt 的能力契约必须逐字对齐 boot-game-host 实际口径。**
|
||
2. **【实测】自走动画会制造假绿**:有自走物(球/地鼠)的游戏,"画面变了"(E)≠"输入有效"。**必须加 G 门(确定性 A/B 对照)**才能证真可玩。
|
||
3. **【实测】reasoning 模型吃 max_tokens**:deepseek-v4-flash/MiniMax 是 reasoning 模型,reasoning_tokens 计入 completion 且可烧空可见 content(`finish_reason:length` 时 content 空)。**max_tokens 须给足(本 worker 16000),否则截断→校验门拒→空转重试。**
|
||
4. **【实测】CDP 触摸要镜像 ref 范式**:`Input.dispatchTouchEvent` 裸 `{x,y}` 点、touchStart→touchEnd 背靠背、坐标经 `c.width/dpr` 映射(非硬编码 390);加 `radius/force` 或长按反而不稳。
|
||
5. **【实测·复用 spike】本机代理吃网关**:`NO_PROXY` 须并 `100.64.0.8`(_client.py 已内置),否则 SDK→502(curl 假通)。
|
||
|
||
---
|
||
|
||
## 6. 哪些游戏类型超出当前插件库能力(来自 20 基准交叉比对)
|
||
|
||
便宜模型受**插件库门面缺口**限制,非模型能力限制。当前 4 短板(详见 `docs/agent-specs/2026-06-13-WG1-20经典轻游戏基准-review.md`):
|
||
- **① 文本/分数 HUD**🟡:无文本渲染门面,每款自 `fillText`(本冒烟可兜,但风格各异)。**最高频缺口,建议 W-G1 后第一优先补门面。**
|
||
- **② 网格/棋盘 grid**🟡:无 tilemap 门面 → 2048/Tetris/Match-3/贪吃蛇/井字棋 网格态每款重写易出 bug。
|
||
- **③ 连续碰撞 CCD**❌(真缺口):collision 仅离散 MTV,无 swept/toi → 高速小球(Breakout 高速/Asteroids 子弹/多球)穿透。**冒烟刻意避开。**
|
||
- **④ 手势 swipe/drag**🟡:getInput 仅 5 类原始事件,无手势合成 → 愤怒小鸟拖拽蓄力/2048 滑动每款自缓存方向。
|
||
|
||
→ **本冒烟选 Pong/Simon/打地鼠 正是"零/最轻短板 + canvas 简单"的最干净子集**(避开 Snake/2048/Cookie Clicker——它们撞①②)。
|
||
|
||
---
|
||
|
||
## 7. worker ↔ pipeline 接缝(留给创始人集成,§3 契约形状)
|
||
|
||
worker 已暴露「job in → result out」([`worker/run.py:run_job`](worker/run.py)):
|
||
- **in**:`{game_id, brief(题面), play_spec(CDP 输入+期望 call-ID), model, max_retries}`
|
||
- **out**(`results/<game>.json`):`{pass, retries, attempts[{stage,errors,usage}], factory_path, bundle_path, verdict(七门), tokens, request_ids}`
|
||
- 闭环:generate → validate(静态契约+`node --check`) → bundle(esbuild) → play(CDP 七门) → 失败回喂错因重试 ≤N。
|
||
- 字段对齐你 spike VERDICT §3 的 REST/队列形状;待你拉取后冻结。
|
||
|
||
---
|
||
|
||
## 8. 蒸馏建议(留我 lane,待你拉进 `.agents/`,避免与你并行 P3 撞车)
|
||
|
||
1. **`skills/` 增补**:把「§2 七门真玩 harness(尤其 G 门 A/B 对照)+ §5 五坑」蒸馏进 `game-e2e-cdp-harness.md`(W-G1 复用并扩展了它)。
|
||
2. **`rules/` 红线**:「生成 prompt 能力契约必须逐字对齐 boot-game-host 实际口径(输入坐标=逻辑像素)」「能生成≠能玩:必须过真玩门+G 门」。
|
||
3. **`knowledge/`**:HJ-GEN-001 验证结论(便宜模型可产可玩轻游戏 / 最优模型 flash 打底+pro 升级 / ¥0.01–0.06)。
|
||
|
||
---
|
||
|
||
## 附录 A · 复现
|
||
|
||
```bash
|
||
# 0) 前置:wg1/gen-worker/.env 写 NEWAPI_KEY(gitignored);game-runtime 先 npm i(含 ws)
|
||
# 1) 装 worker venv
|
||
uv venv wg1/gen-worker/.venv --python 3.12
|
||
uv pip install -p wg1/gen-worker/.venv/bin/python -r wg1/gen-worker/requirements.txt
|
||
# 2) 生成+真玩一款(generate→validate→bundle→CDP 七门)
|
||
wg1/gen-worker/.venv/bin/python wg1/gen-worker/worker/run.py pong --model deepseek-v4-flash --max-retries 2
|
||
# 3) 成本表
|
||
wg1/gen-worker/.venv/bin/python wg1/gen-worker/worker/cost.py
|
||
# 仅重放某款(不重新生成):
|
||
( cd game-runtime && bash games/_wg1-gen/_shared/serve-and-play.sh pong )
|
||
```
|