textmachine/eval/dovodka/sessii.py

87 lines
5.3 KiB
Python
Raw 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
"""РАСКЛАДКА СУДЕЙСКИХ ПАЧЕК ПО СЕССИЯМ и печать готовых промтов. $0.
Зачем скриптом, а не руками. Состав сессии — часть прибора, а не оформление: от него зависит,
(1) не попадут ли обе половины одной пары шумового пола к ОДНОМУ судье — тогда пол не поймает
межсессионную компоненту, и порог будет занижен, как в паке 23 на 19%; (2) одинаково ли
нагружены семейства — если Sol судит по 10 пачек за сессию, а Claude по 4, разница усталости
станет разницей семейств; (3) воспроизводима ли раскладка при пере-прогоне.
Обвязка берётся из `judge-prompts/{claude,sol}.md`, которые собраны из ОДНОГО ядра и сверяются
гейтом `paritet.py` (норма Д0.13 П-4). Здесь подставляется только список файлов.
⚠ Наборы `p1` и `p2` НИКОГДА не попадают в одну сессию: это разные половины пары пола.
Запуск: sessii.py план раскладки
sessii.py --write записать готовые промты файлами
"""
from __future__ import annotations
import sys
from pathlib import Path
ZONE = Path(__file__).resolve().parent
PROMPTS = ZONE / "judge-prompts"
# ⚠ Ось — параметр. Пачки разных осей НЕЛЬЗЯ класть в один каталог и нельзя отдавать одной
# сессии: судья, увидевший обе, перестаёт быть слепым к паре.
AXIS = next((a.split("=", 1)[1] for a in sys.argv if a.startswith("--axis=")), "d4")
FAM = {
"claude": dict(root=Path.home() / "books" / "judging" / f"sud-{AXIS}", tpl=PROMPTS / "claude.md"),
"sol": dict(root=Path.home() / "books" / "judging" / f"sol-{AXIS}-work", tpl=PROMPTS / "sol.md"),
}
PER_SESSION = 4 # как у харнесса Sol в паке 23; одинаково для обоих семейств
def batches(root: Path, half: str) -> list[list[str]]:
"""Пачки набора, разложенные по сессиям. Порядок — от имени файла, значит воспроизводим."""
toks = sorted(p.stem for p in (root / f"{half}-tasks").glob("*.txt"))
return [toks[i:i + PER_SESSION] for i in range(0, len(toks), PER_SESSION)]
# Язык исходника — из провайдера пары, а не из текста шаблона: судья английской пачки, которому
# сказано «сверяй с китайским исходником», сверяет не с тем, что видит.
LANG = {"d4": "китайском", "d3": "английском", "d6": "японском", "d1": "китайском"}
# ⚠ Творительный падеж — ОТДЕЛЬНАЯ подстановка. Первая редакция просто вычёркивала язык из фразы
# «сверяй смысл с китайским исходником», потому что склонение не подставлялось. Проверенная
# ревью версия называла язык ДВАЖДЫ, и обезличивание было тихой потерей якоря.
LANG_INS = {"d4": "китайским", "d3": "английским", "d6": "японским", "d1": "китайским"}
def render(fam: str, half: str, toks: list[str]) -> str:
m = FAM[fam]
body = m["tpl"].read_text(encoding="utf-8")
body = (body.replace("{ЯЗЫКЕ}", LANG[AXIS]).replace("{КАТАЛОГ}", m["root"].name)
.replace("{НАБОР}", half).replace("{ЯЗЫКОМ}", LANG_INS[AXIS]))
lst = "\n".join(f" ~/{m['root'].name}/{half}-tasks/{t}.txt" for t in toks)
return body.replace("{СПИСОК ФАЙЛОВ}", lst)
def main() -> int:
write = "--write" in sys.argv
total = 0
for fam, m in FAM.items():
root = m["root"]
if not root.exists():
print(f"{fam}: каталога {root} нет — пропуск")
continue
out = root / "prompts"
if write:
out.mkdir(exist_ok=True)
print(f"\n=== {fam.upper()} ({root}) ===")
n = 0
for half in ("p1", "p2"):
for i, toks in enumerate(batches(root, half), 1):
n += 1
name = f"{half}-session-{i:02d}.md"
print(f" {name:22s} пачек {len(toks)}: {' '.join(toks)}")
if write:
(out / name).write_text(render(fam, half, toks), encoding="utf-8")
total += n
print(f" сессий у семейства: {n}" + (f" · записано в {out}" if write else ""))
print(f"\nвсего сессий: {total} · по {PER_SESSION} пачки · кап агент-сессий 80")
print("⚠ наборы p1 и p2 — разные половины пары пола, одному судье их отдавать НЕЛЬЗЯ")
return 0
if __name__ == "__main__":
sys.exit(main())