lili 6201f95146 feat(wg1-gen): 便宜模型生成可真玩轻游戏——L1 worker + 七门真玩 harness(HJ-GEN-001 成立)
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>
2026-06-13 22:50:24 -07:00

153 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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 )
```