#!/usr/bin/env python3 """Сборка СЛЕПЫХ пакетов для судей качества (санкция владельца 05.08: «вердикты выносят отдельные чистые агенты с чистым контекстом»). Что важно в дизайне и почему: · СЛЕПОТА. Судья получает две версии как «Версия А» и «Версия Б» и НЕ знает, какая из них full-regen, а какая — точечная правка. Иначе сработает ожидание («переписанное выглядит глаже» / «диффы — наша ставка»). Раскладка А/Б чередуется по чётности окна, ключ пишется отдельным файлом, который судьям НЕ отдаётся. · ЧИСТЫЙ КОНТЕКСТ. В пакете нет ни гипотез, ни цен, ни названий контрактов, ни слова «дифф». Судья видит: исходник, глоссарий, черновик и две отредактированные версии. · ЧЕРНОВИК ТОЖЕ ДАЁТСЯ. Без него нельзя отличить «версия лучше» от «версия просто не тронула плохое место»: правка оценивается относительно того, что было. · ОСИ РАЗДЕЛЕНЫ. Верность исходнику, соблюдение глоссария и русский язык — три отдельных вердикта плюс общий. Слитая оценка прячет ровно тот случай, ради которого всё затевалось: контракт может быть дешевле и при этом хуже по одной оси. """ from __future__ import annotations import json import sys from pathlib import Path REPO = Path("/home/ubuntu/projects/textmachine") HERE = Path(__file__).resolve().parent RAW = Path.home() / "books" / "gu-zhenren" / "editor-harness" OUT = RAW / "judge" sys.path.insert(0, str(REPO / "eval" / "editor_contract")) sys.path.insert(0, str(REPO / "eval" / "bank_arbitration")) import editor_wire_probe as P # noqa: E402 from inject_probe import pick_windows # noqa: E402 def text_of(tag: str) -> str | None: """Для full — content вызова; для дифф-армов — текст ПОСЛЕ применения операций.""" ap = RAW / f"{tag}.applied.json" if ap.exists(): return json.loads(ap.read_text(encoding="utf-8"))["text"] f = RAW / f"{tag}.json" if f.exists(): return json.loads(f.read_text(encoding="utf-8"))["content"] return None def build(model_short: str, left: str, right: str) -> int: OUT.mkdir(parents=True, exist_ok=True) key = {} n = 0 for k, (_, src, terms) in enumerate(pick_windows()): a_tag, b_tag = (f"eh-{model_short}-{left}-w{k}", f"eh-{model_short}-{right}-w{k}") if k % 2: # чередование раскладки — детерминированное, не случайное a_tag, b_tag = b_tag, a_tag ta, tb = text_of(a_tag), text_of(b_tag) if not ta or not tb: print(f" w{k}: пропуск — нет {a_tag if not ta else b_tag}") continue gloss = "\n".join(f"- {t} → «{P.TERMS[t][0]}»" for t in terms) draft = json.loads((Path.home() / "books/gu-zhenren/bank-arbitration" / f"inj-w{k}-B.json").read_text(encoding="utf-8"))["content"] packet = ( f"# Материалы для оценки перевода, окно {k}\n\n" f"## Исходный текст (китайский)\n\n{src}\n\n" f"## Утверждённый глоссарий (канонические формы обязательны)\n\n{gloss}\n\n" f"## Черновой перевод ДО редактуры\n\n{draft}\n\n" f"## Версия А (после редактуры)\n\n{ta}\n\n" f"## Версия Б (после редактуры)\n\n{tb}\n") (OUT / f"packet-{model_short}-w{k}.md").write_text(packet, encoding="utf-8") key[f"w{k}"] = {"А": a_tag, "Б": b_tag} n += 1 (OUT / f"KEY-{model_short}.json").write_text(json.dumps(key, ensure_ascii=False, indent=1), encoding="utf-8") print(f"пакетов собрано: {n}; ключ раскладки — KEY-{model_short}.json (судьям НЕ отдавать)") return n if __name__ == "__main__": ms = sys.argv[1] if len(sys.argv) > 1 else "dp" left = sys.argv[2] if len(sys.argv) > 2 else "full" right = sys.argv[3] if len(sys.argv) > 3 else "diff-harness" build(ms, left, right)