diff --git a/wg1/gen-worker/gamedef_quickcheck.py b/wg1/gen-worker/gamedef_quickcheck.py index f63fa1a2..6007c0e6 100644 --- a/wg1/gen-worker/gamedef_quickcheck.py +++ b/wg1/gen-worker/gamedef_quickcheck.py @@ -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 + play(factory 已由 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(