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

12 KiB
Raw Blame History

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/package.json 加 ws(devDep,CDP 驱动用,ref harness 亦需)。

2. 真玩取证 harness:七道假绿守卫(false-green guard)

每款游戏经下列七门,全过才判 PASS(驱动 = 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):

  • 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 · 复现

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