81 lines
4.1 KiB
Python
81 lines
4.1 KiB
Python
#!/usr/bin/env python3
|
||
"""Разброс СУДЬИ на повторе одной и той же клетки — последняя незакрытая дыра методики.
|
||
|
||
Проблема. Во всех раундах судейства каждая пара текстов судилась ровно один раз на раскладку.
|
||
Значит любое расхождение между армами нельзя отделить от собственного шума судьи: если он на
|
||
ОДНОМ И ТОМ ЖЕ входе отвечает по-разному, то «связка 6:4» может целиком лежать внутри его дисперсии.
|
||
Отчёт это признаёт («разброс судьи на повторе одной клетки не мерен ни разу»), но не измеряет.
|
||
|
||
Здесь: те же пакеты, та же ориентация, N независимых голосов. Меряется доля клеток, где все N
|
||
голосов совпали. Это ВЕРХНЯЯ граница разрешающей способности всей судейской методики: разница между
|
||
армами меньше собственного разброса судьи не может считаться сигналом.
|
||
|
||
Температура намеренно та же, что в основном судействе, — меряется разброс РЕАЛЬНОГО инструмента,
|
||
а не гипотетического детерминированного.
|
||
"""
|
||
from __future__ import annotations
|
||
import json
|
||
import re
|
||
import sys
|
||
import time
|
||
from collections import Counter
|
||
from pathlib import Path
|
||
|
||
REPO = Path("/home/ubuntu/projects/textmachine")
|
||
HERE = Path(__file__).resolve().parent
|
||
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 judge_crossmodel as J # noqa: E402
|
||
|
||
PACK = Path.home() / "books" / "gu-zhenren" / "editor-harness" / "judge-reflow"
|
||
REPS = 3
|
||
|
||
|
||
def main(model: str, ms: str) -> None:
|
||
nothink = Q.MODELS[model][5] in ("disable", "effort_none")
|
||
votes = {}
|
||
spent = 0.0
|
||
for k in range(6):
|
||
p = PACK / f"packet-{ms}-w{k}.md"
|
||
if not p.exists():
|
||
continue
|
||
body = p.read_text(encoding="utf-8")
|
||
for r in range(1, REPS + 1):
|
||
rec = Q.call(model, [{"role": "system", "content": J.RUBRIC},
|
||
{"role": "user", "content": body}],
|
||
f"jv-{ms}-w{k}-r{r}", nothink)
|
||
spent += rec["cost_usd"]
|
||
votes.setdefault(k, []).append(J.parse(rec["content"]))
|
||
time.sleep(0.2)
|
||
|
||
print(f"\nРАЗБРОС СУДЬИ {model} на {REPS} повторах одного входа (модель материала {ms})\n")
|
||
print(f"{'ось':11s}{'клеток':>8s}{'единогласно':>13s}{'доля':>8s}")
|
||
print("-" * 42)
|
||
total_cells = total_unan = 0
|
||
for ax in J.AXES:
|
||
unan = cells = 0
|
||
for k, vs in votes.items():
|
||
picks = [v.get(ax) for v in vs if v.get(ax)]
|
||
if len(picks) < REPS:
|
||
continue
|
||
cells += 1
|
||
unan += int(len(set(picks)) == 1)
|
||
if cells:
|
||
print(f"{ax:11s}{cells:8d}{unan:13d}{unan / cells:8.0%}")
|
||
total_cells += cells
|
||
total_unan += unan
|
||
if total_cells:
|
||
print(f"\nИТОГО единогласно {total_unan}/{total_cells} = {total_unan / total_cells:.0%}")
|
||
print("⇒ разница между армами МЕНЬШЕ этого разброса сигналом считаться не может.")
|
||
print(f"потрачено ${spent:.6f}")
|
||
for k, vs in sorted(votes.items()):
|
||
c = Counter(v.get("общий") for v in vs)
|
||
print(f" w{k} общий вердикт по повторам: {dict(c)}")
|
||
|
||
|
||
if __name__ == "__main__":
|
||
main(sys.argv[1] if len(sys.argv) > 1 else "deepseek-v4-pro",
|
||
sys.argv[2] if len(sys.argv) > 2 else "dp")
|