80 lines
4.9 KiB
Python
80 lines
4.9 KiB
Python
#!/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)
|