test(wg1): gamedef 快走查加 repair 回环——证 repair 抬升率(runner 7/9→9/9 真过)

plan 2026-06-18-001 U3 capstone:快走查加 repair 回环(mirror 真图:验证/真玩失败→verdict 回喂→重生成,≤2 重试),
证 ≥60% cutover 的真机制(repair)在本机可抬首攻失败到过门。

with-repair 早读(design+gamedef 全图镜像,deepseek-v4-flash):
- gd-runner  9/9 真过(att=3)——首攻 7/9,repair 回喂后重生成补齐 → 满九门。**repair 回环端到端证实有效。**
- gd-breakout 8/9(att=3)——H_progress(砖 remaining 未降):paddle-intercept 接球清砖精度,3 轮便宜模型 repair 未完全攻克
  (R3「九门是机制地板」难类;仍真渲染/构建/玩、挡板接指针)。
- 1/2 小样硬类:与 ≥60%(全图+更大样, authoritative=mini-desktop)目标一致——runner 类过、paddle 精度类待更多 repair/更强档。

总:gamedef 路本机已证 ①端到端可玩(pong/runner 满门)②repair 回环真抬率。≥60% cutover 量化门 authoritative=
  mini-desktop SaaFullGraphE2eTest -Dsaa.e2e=1(全图+repair+大样)。密钥经 _client 读 .env 不入码;throwaway 已清。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
lili 2026-06-18 01:18:33 -07:00
parent da4801828d
commit 099fb8106d

View File

@ -27,6 +27,7 @@ SAA_PROMPTS = (REPO_ROOT / "game-cloud/game-module-aigc/game-module-aigc-server/
GAME_RUNTIME = REPO_ROOT / "game-runtime"
GEN_DIR = GAME_RUNTIME / "games" / "_wg1-gen"
BRIEFS = Path(__file__).resolve().parent / "briefs"
MAX_RETRIES = 2 # repair 回环上限mirror studio.py max_repairs 默认;总尝试=MAX_RETRIES+1
def extract_block(name):
@ -102,33 +103,39 @@ def run_one(game_id, gamedef_system, design_system, model):
out["driver"] = (gs.get("driver") or {}).get("type")
except Exception as e:
out["err"] = "design 降级:" + str(e)[:80] # design 失败不致命,凭 brief 继续 generate
# ② generate gamedef用 enriched=brief+设计稿)
user = "请为下面这款游戏产出一份 gameDefinition只输出一个 JSON 对象,不要 ```代码块、不要解释):\n\n" + enriched
try:
resp = _client.chat(model, gamedef_system, user, max_tokens=16000)
except Exception as e:
out["stage"] = "generate"; out["err"] = str(e)[:200]; return out
out["tokens"] = (resp.get("prompt_tokens"), resp.get("completion_tokens"))
gd = extract_json_obj(resp["content"])
if gd is None:
out["stage"] = "parse"; out["err"] = "未解析出 gameDefinition JSON"; return out
ok, err = assemble_via_cli(gid, gd)
if not ok:
out["stage"] = "validate/assemble"; out["err"] = err[:400]; return out
# 复用 run.py 的 scaffold 余下(拷模板 + play-spec+ build + playfactory 已由 CLI 写好)。
# ② generate→assemble→build→play **repair 回环**mirror 真图失败→verdict/错误回喂→重生成,≤max_retries 次)。
# 这是 ≥60% cutover 门的真机制(单发首攻 ≠ 终率design 只跑一次,repair 只重 generate。
gdir = GEN_DIR / gid
shutil.copyfile(GEN_DIR / "_shared" / "entry-bundle.template.js", gdir / "entry-bundle.js")
shutil.copyfile(GEN_DIR / "_shared" / "index.template.html", gdir / "index.html")
(gdir / "play-spec.json").write_text(json.dumps(play_spec, ensure_ascii=False, indent=2), encoding="utf-8")
bok, blog = run.build(gid)
if not bok:
out["stage"] = "build"; out["err"] = blog[:400]; return out
rc, _, verdict = run.play(gid)
out["stage"] = "play"
out["guards"] = {k: g.get("pass") for k, g in ((verdict or {}).get("guards") or {}).items()}
out["pass"] = bool(rc == 0 and verdict and verdict.get("pass"))
if not out["pass"]:
out["err"] = run._verdict_feedback(verdict)[:300]
feedback = None
for attempt in range(MAX_RETRIES + 1):
out["attempts"] = attempt + 1
user = "请为下面这款游戏产出一份 gameDefinition只输出一个 JSON 对象,不要 ```代码块、不要解释):\n\n" + enriched
if feedback:
user += "\n\n———\n上一次生成【未通过】,原因如下,请针对性修正后重新产出完整 gameDefinition JSON\n" + feedback
try:
resp = _client.chat(model, gamedef_system, user, max_tokens=16000)
except Exception as e:
out["stage"] = "generate"; feedback = "模型调用失败:" + str(e)[:160]; continue
gd = extract_json_obj(resp["content"])
if gd is None:
out["stage"] = "parse"; feedback = "未能解析出 gameDefinition JSON。请只输出一个 JSON 对象(顶层 entities/components/behaviors/scenes/rules"; continue
ok, err = assemble_via_cli(gid, gd)
if not ok:
out["stage"] = "validate/assemble"; feedback = "校验/装配未过:\n" + err[:500]; continue
shutil.copyfile(GEN_DIR / "_shared" / "entry-bundle.template.js", gdir / "entry-bundle.js")
shutil.copyfile(GEN_DIR / "_shared" / "index.template.html", gdir / "index.html")
(gdir / "play-spec.json").write_text(json.dumps(play_spec, ensure_ascii=False, indent=2), encoding="utf-8")
bok, blog = run.build(gid)
if not bok:
out["stage"] = "build"; feedback = "打包失败esbuild\n" + blog[:500]; continue
rc, _, verdict = run.play(gid)
out["stage"] = "play"
out["guards"] = {k: g.get("pass") for k, g in ((verdict or {}).get("guards") or {}).items()}
if rc == 0 and verdict and verdict.get("pass"):
out["pass"] = True
break
feedback = run._verdict_feedback(verdict)
out["err"] = feedback[:300]
return out
@ -146,7 +153,8 @@ def main():
gpass = sum(1 for v in r["guards"].values() if v)
gtot = len(r["guards"])
drv = r.get("driver") or "-"
print(f" {r['game_id']:14s} {mark:16s} drv={drv:16s} gates={gpass}/{gtot} {('' if r['pass'] else r['err'][:110])}")
att = r.get("attempts", 1)
print(f" {r['game_id']:14s} {mark:16s} drv={drv:14s} att={att} gates={gpass}/{gtot} {('' if r['pass'] else r['err'][:100])}")
npass = sum(1 for r in results if r["pass"])
print(f"\n[gamedef-qc] 过门 {npass}/{len(results)} (cutover 门基线=iife 60%; 本为 lili-mac 小样早读, authoritative=mini-desktop)")
Path(__file__).resolve().parent.joinpath("results", "gamedef-quickcheck.json").write_text(