#!/usr/bin/env python3 """Селф-ревью рига эксп-20 ДО платных вызовов (требование владельца 05.08). Проверяется исполнением, а не глазами: С1 ключи всех заявленных провайдеров присутствуют в окружении (значения НЕ печатаются); С2 все четыре контракта рендерятся без незаполненных плейсхолдеров, и блок закона одинаков; С3 `classify_ops` воспроизводит счёт отказов настоящего аппликатора на УЖЕ ОПЛАЧЕННОМ сырье эксп-19 — иначе раунд починки вернёт модели не те операции; С4 контракты действительно разные на проводе (иначе меряем один и тот же промпт дважды); С5 гардрейл: попытка погасить мышление у DeepSeek обязана падать, а не тихо проходить. """ from __future__ import annotations import json import os import re import sys from pathlib import Path REPO = Path("/home/ubuntu/projects/textmachine") HERE = Path(__file__).resolve().parent # ⚠ ПОРЯДОК ВАЖЕН (селф-ревью 05.08): в `eval/editor_contract/` лежит СВОЙ `probe.py`, и при # обратном порядке вставок он затеняет наш — `import probe` подтягивал чужой модуль. # HERE вставляется ПОСЛЕДНИМ, поэтому стоит первым в sys.path и выигрывает. sys.path.insert(0, str(REPO / "eval" / "editor_contract")) sys.path.insert(0, str(REPO / "eval" / "bank_arbitration")) sys.path.insert(0, str(HERE)) import probe as Q # noqa: E402 import editor_wire_probe as P # noqa: E402 from apply import apply_ops # noqa: E402 from inject_probe import pick_windows # noqa: E402 OLD = 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 and not ok else "")) def main() -> None: # С1 — ключи (значения наружу не идут) for m, spec in Q.MODELS.items(): check(f"С1 ключ {spec[1]} для {m}", bool(os.environ.get(spec[1])), "не найден в окружении") wins = pick_windows() _, buf, terms = wins[0] draft = P.draft_of(0) blk = P.editor_block(terms, P.HEADER_LAW_CLAUSE, None) # С2 — рендер sysmsgs = {} for c in Q.CONTRACTS: msgs = Q.messages(c, buf, draft, blk) holes = set() for mm in msgs: holes |= set(re.findall(r"\{\{(\w+)\}\}", mm["content"])) check(f"С2 {c}: плейсхолдеров нет", not holes, ", ".join(sorted(holes))) check(f"С2 {c}: блок закона на месте", any("КАНОНИЧЕСКИЕ" in mm["content"] for mm in msgs)) sysmsgs[c] = msgs[0]["content"] # С4 — контракты различны uniq = len(set(sysmsgs.values())) check("С4 все четыре контракта различны на проводе", uniq == len(Q.CONTRACTS), f"уникальных системных промптов {uniq} из {len(Q.CONTRACTS)}") for c, t in sysmsgs.items(): print(f" {c:13s} системный промпт {len(t):5d} зн.") # С6 — секция FEWSHOT обязана дропаться (дефект 05.08, пойман в ходе прогона: арм `full` # уехал с примерами перевёрстки, то есть база сравнения не была боевым контрактом). fullsys = sysmsgs["full"] check("С6 full: блок FEWSHOT дропнут, как в проде", "---FEWSHOT---" not in fullsys and "Он вышел за дверь" not in fullsys) check("С6 full: блок ДИСКУРС-ПЕРЕВЁРСТКИ на месте", "ДИСКУРС-ПЕРЕВЁРСТКА" in fullsys) check("С6 full: длина совпала с проводом эксп-19 (3071 зн.)", len(fullsys) == 3071, f"сейчас {len(fullsys)}") # С3 — классификатор против настоящего аппликатора на оплаченном сырье tot = mism = seen = 0 for k in range(6): for r in (1, 2, 3): f = OLD / f"ec-D1-w{k}-r{r}.json" if not f.exists(): continue reply = json.loads(f.read_text(encoding="utf-8"))["content"] d = P.draft_of(k) res = apply_ops(d, reply) mine = Q.classify_ops(d, reply) seen += 1 # аппликатор считает отказы notfound+ambiguous; мой классификатор — те же классы ref = res.rejected_notfound + res.rejected_ambiguous got = sum(1 for _, _, st in mine if st in ("reject_notfound", "reject_ambiguous")) tot += ref if ref != got: mism += 1 print(f" ⚠ {f.stem}: аппликатор {ref}, классификатор {got}") if len(mine) != res.ops_total and not res.malformed: mism += 1 print(f" ⚠ {f.stem}: операций аппликатор {res.ops_total}, классификатор {len(mine)}") check(f"С3 классификатор совпал с аппликатором на {seen} вызовах эксп-19 (отказов в сырье {tot})", mism == 0, f"расхождений {mism}") # С5 — гардрейл try: Q.call("deepseek-v4-pro", [{"role": "user", "content": "x"}], "selfcheck-guard", True) check("С5 гашение мышления у DeepSeek запрещено", False, "вызов ПРОШЁЛ — гардрейл дырявый") except SystemExit: check("С5 гашение мышления у DeepSeek запрещено", True, "падает, как и должно") print(f"\n{'РИГ ГОТОВ' if not FAIL else f'ПРОВАЛОВ: {FAIL}'}") sys.exit(1 if FAIL else 0) if __name__ == "__main__": main()