#!/usr/bin/env python3 """СУДЕЙСТВО ПАКА «СИЛЬНЫЙ ТИР». $0 сам по себе; расходует агент-запуски (кап 60). Риг не пере-изобретается: `absjudge` эксп-21 настраивается через `configure`, как это делал эксп-22. Пак-специфика здесь — только два прохода и их армы. ПРОХОД `tier` — панель сильного тира: T1..Tn против R0 над ТОЙ ЖЕ якорной базой, 16 единиц эксп-22. Отвечает: остаётся ли `deepseek-v4-pro` рекомендацией при полной панели. ПРОХОД `border`— граница `glm-5`: R0 против R2 на 32 единицах (16 эксп-22 + 16 новых). Отвечает: разводится ли контраст −2.12 от порога при удвоении n. ⚠ Порог различимости фиксируется ОДИН — шумовой пол СВОЕГО прохода. Двусмысленность эксп-22, где пред-объявленный (2.03) и фактический (2.47) пороги дали для `glm-5` разный ответ, закрыта здесь заранее, а не после замера. """ from __future__ import annotations import json import sys from pathlib import Path REPO = Path("/home/ubuntu/projects/textmachine") ZONE = Path(__file__).resolve().parent sys.path.insert(0, str(REPO / "eval" / "tenant_panel")) sys.path.insert(0, str(REPO / "eval" / "role_topology")) import absjudge as AJ # noqa: E402 def _load(name: str, path: Path): import importlib.util # noqa: PLC0415 spec = importlib.util.spec_from_file_location(name, path) mod = importlib.util.module_from_spec(spec) sys.modules[name] = mod spec.loader.exec_module(mod) return mod PAN = _load("panel_et", ZONE / "panel.py") MONEY, MAT, P22 = PAN.MONEY, PAN.MAT, PAN.P22 OUT = MONEY.OUT KEYS = OUT / "blind-keys" RUNS = OUT / "agent-runs.json" CAP = 60 PASSES = ("tier", "border") def arm_map(pass_: str) -> dict[str, str]: if pass_ == "tier": return {"R0": PAN.BATTLE, **{f"T{i}": m for i, m in enumerate(PAN.survivors(), start=1)}} return {"R0": PAN.BATTLE, "R2": PAN.BORDER} def arms(pass_: str) -> tuple[str, ...]: return tuple(arm_map(pass_)) def family(pass_: str) -> list[tuple[str, str, str]]: """Семейство объявляется ДО выпуска и впечатывается в ключ.""" if pass_ == "tier": return [(a, "R0", f"редактор сильного тира {a} против боевого над ТОЙ ЖЕ базой") for a in arms(pass_) if a != "R0"] return [("R2", "R0", "пограничный glm-5 против боевого, n удвоено до 32")] def units(pass_: str) -> list[dict]: """Единицы прохода. У `border` их 32: 16 эксп-22 плюс 16 новых этого пака.""" if pass_ == "tier": return P22.M.units_zh() return P22.M.units_zh() + MAT.units_new() def text_of(pass_: str): new_uids = {u["uid"] for u in MAT.units_new()} def hook(arm: str, u: dict) -> str: if pass_ == "border": # единица новая → тексты фазы B; единица эксп-22 → его же клетки if u["uid"] in new_uids: return PAN.text_b(arm, u["uid"]) return P22.edit_text(PAN.BATTLE if arm == "R0" else PAN.BORDER, u["uid"]) \ if arm == "R0" else _ed22(u["uid"]) return PAN.text_c(arm, u["uid"]) return hook def _ed22(uid: str) -> str: """Клетка R2 эксп-22 — это `glm-5` над якорной базой в его фазе Ф3.""" import editors as ED # noqa: PLC0415 return ED.text_of("R2", uid) def floor_of(pass_: str): """Пол СВОЕГО прохода — две независимые генерации самого дешёвого арма панели. ⚠ Для `border` пол берётся ОТТУДА ЖЕ и это объявлено: армы обоих проходов — редакторы над одной якорной базой, судит одна и та же популяция, поэтому шум одного вызова у них общий. Покупать второй такой же пол значило бы платить за то же число дважды. """ def hook(u: dict): return PAN.floor_pair(u["uid"]) return hook def decoy_base(slots: dict) -> str: for a in ("R0", "T1", "R2"): if a in slots and (slots[a] or "").strip(): return a return next(iter(slots)) def setup(pass_: str) -> None: AJ.configure(OUT=OUT, KEYS=KEYS, PASS=pass_, ARMS=arms(pass_), ARMS_2B=(), FAMILY=family(pass_), TEXT_HOOK=text_of(pass_), UNITS_HOOK=lambda: units(pass_), FLOOR_HOOK=floor_of(pass_), DECOY_HOOK=decoy_base, SRC_LANG="китайском") KEYS.mkdir(parents=True, exist_ok=True) def runs_used() -> list[dict]: return json.loads(RUNS.read_text(encoding="utf-8")) if RUNS.exists() else [] def log_run(pass_: str, tokens: list[str], note: str = "") -> int: rs = runs_used() n = len(rs) + 1 rs.append(dict(n=n, pass_=pass_, tokens=tokens, note=note)) RUNS.write_text(json.dumps(rs, ensure_ascii=False, indent=1), encoding="utf-8") jf = KEYS / f"{pass_}-JUDGES.json" m = json.loads(jf.read_text(encoding="utf-8")) if jf.exists() else {} for t in tokens: m[t] = f"agent-{n:02d}" jf.write_text(json.dumps(m, ensure_ascii=False, indent=1), encoding="utf-8") if n > CAP: raise SystemExit(f"⛔ КАП АГЕНТ-ЗАПУСКОВ ПРОБИТ: {n} > {CAP}") return n def per_run(pass_: str) -> int: return max(1, 20 // max(1, len(arm_map(pass_)) + 3)) def cmd_plan(pass_: str) -> None: setup(pass_) us, amap = units(pass_), arm_map(pass_) print(f"проход {pass_} · единиц {len(us)} · армов {len(amap)}") for a, m in amap.items(): print(f" {a:4s} → {m}") print("семейство (в ключ ДО ответов):") for a, b, w in family(pass_): print(f" {a:4s} vs {b:4s} {w}") have = sum(1 for u in us for a in amap if (text_of(pass_)(a, u) or "").strip()) print(f"текстов готово {have} из {len(us) * len(amap)} · " f"агент-запусков {len(runs_used())} из {CAP}") if __name__ == "__main__": a = sys.argv[1:] or ["--plan", "tier"] if a[0] == "--plan": cmd_plan(a[1] if len(a) > 1 else "tier") elif a[0] == "--emit": setup(a[1]) AJ.emit() elif a[0] == "--ingest": setup(a[1]) AJ.ingest() elif a[0] == "--score": setup(a[1]) AJ.controls() print() AJ.score() else: print(__doc__)