#!/usr/bin/env python3 """Проверка, что ПРОГОН прошёл правильно (владелец 05.08: «после прогона понять, что он прошёл верно»). Проверяется не смысл результатов, а их санитария — то, что молча портит выводы: П1 множество тегов ровно то, что запланировано: ни пропусков, ни лишних (пере-прогон чужого); П2 ни одного обрыва по потолку вывода (`finish` ≠ stop) — обрыв даёт «модель не нашла правок»; П3 модель и режим на проводе те, что заявлены армом (L — low, остальные — дефолт); П4 у КАЖДОГО диффового вызова есть файл применения, и применение не молчит об отказах; П5 деньги сходятся двумя путями: сумма записей против напечатанного скриптом итога; П6 дифф-выходы не выродились: пустой/односимвольный ответ ≠ «NO_CHANGE по делу». """ from __future__ import annotations import json import sys from collections import Counter from pathlib import Path RAW = Path.home() / "books" / "gu-zhenren" / "bank-arbitration" FAIL = 0 def check(name: str, ok: bool, note: str = "") -> None: global FAIL if not ok: FAIL += 1 print(f"[{'OK ' if ok else 'ПРОВАЛ'}] {name}" + (f" — {note}" if note else "")) def expected() -> set[str]: t = set() for k in range(6): for r in (1, 2, 3): t |= {f"fp-LD-w{k}-r{r}", f"fp-LF-w{k}-r{r}", f"fp-N-w{k}-r{r}"} for r in (1, 2): t |= {f"fp-ED-w{k}-r{r}", f"fp-EF-w{k}-r{r}"} for u in range(2): for r in (1, 2, 3): t |= {f"fp-GD-u{u}-r{r}", f"fp-GF-u{u}-r{r}"} return t def main() -> None: recs = {} for f in RAW.glob("fp-*.json"): if f.name.endswith(".applied.json") or f.name.startswith("fp-draft"): continue recs[f.stem] = json.loads(f.read_text(encoding="utf-8")) exp = expected() got = set(recs) check("П1 все запланированные вызовы есть", not (exp - got), f"нет: {sorted(exp - got)[:8]}" if exp - got else f"{len(got)} шт.") check("П1 лишних вызовов нет", not (got - exp), f"лишние: {sorted(got - exp)[:8]}") fin = Counter(r["finish"] for r in recs.values()) check("П2 нет обрывов по потолку вывода", set(fin) == {"stop"}, str(dict(fin))) models = Counter(r.get("model_returned", "?") for r in recs.values()) check("П3 модель одна на всех вызовах", len(models) == 1, str(dict(models))) # П3а — режим НА ПРОВОДЕ, прямо из артефакта. ⚠ Инструмент добавлен по адверсариальному # ревью 05.08: строка П3а стояла в отчёте как выполненная проверка, а кода под ней не было. want = {t: ('{"reasoning_effort": "low"}' if t.startswith(("fp-LD", "fp-LF")) else "{}") for t in recs} bad = {t: json.dumps(recs[t].get("extra_body"), ensure_ascii=False) for t in recs if json.dumps(recs[t].get("extra_body"), ensure_ascii=False) != want[t]} check("П3а режим на проводе совпадает с заявленным армом", not bad, f"расходится у {len(bad)}: {list(bad)[:5]}" if bad else f"low на {sum(1 for t in want.values() if 'low' in t)} · {{}} на " f"{sum(1 for t in want.values() if t == '{}')}") # ⚠ П3а проверяет, что параметр УШЁЛ, но НЕ что он ПОДЕЙСТВОВАЛ. Отличимость `low` от дефолта # на этой модели пере-счётом 05.08 НЕ подтверждена (отчёт 19 §6.2) — держать это в виду. print(" ⚠ уход параметра ≠ его действие: отличимость low от дефолта не установлена") diffs = [t for t in recs if t.startswith(("fp-LD", "fp-GD", "fp-ED", "fp-N-"))] noapp = [t for t in diffs if not (RAW / f"{t}.applied.json").exists()] check("П4 у каждого диффа есть файл применения", not noapp, f"нет у: {noapp[:6]}") rej = Counter() for t in diffs: f = RAW / f"{t}.applied.json" if f.exists(): a = json.loads(f.read_text(encoding="utf-8")) for k in ("rejected_notfound", "rejected_ambiguous", "rejected_overlap", "rejected_empty", "malformed"): rej[k] += a[k] print(f" отказы аппликатора по всем диффам: {dict(rej)}") tot = sum(r["cost_usd"] for r in recs.values()) drafts = sum(json.loads(f.read_text(encoding="utf-8"))["cost_usd"] for f in RAW.glob("fp-draft-*.json")) print(f" деньги: вызовы ${tot:.6f} + черновики ${drafts:.6f} = ${tot + drafts:.6f}") check("П5 итог сошёлся с напечатанным скриптом ($0.349566)", abs(tot + drafts - 0.349566) < 1e-5, f"расхождение {tot + drafts - 0.349566:+.6f}") tiny = [(t, len(recs[t]["content"])) for t in diffs if len(recs[t]["content"]) < 20] nc = [] for t, _ in tiny: a = json.loads((RAW / f"{t}.applied.json").read_text(encoding="utf-8")) nc.append((t, a["no_change"])) check("П6 короткие дифф-ответы объяснены как NO_CHANGE", all(v for _, v in nc), f"короткие БЕЗ no_change: {[t for t, v in nc if not v]}") if nc: print(f" коротких дифф-ответов {len(nc)}, из них NO_CHANGE {sum(v for _, v in nc)}") print(f"\n{'ПРОГОН ЧИСТ' if not FAIL else f'ПРОВАЛОВ: {FAIL}'}") sys.exit(1 if FAIL else 0) if __name__ == "__main__": main()