textmachine/eval/editor_harness/judge_packets.py

80 lines
4.9 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/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)