textmachine/eval/role_topology/tiers.py

179 lines
9.4 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
"""ЯРУСЫ ТРЕТЬЕЙ РЕДАКЦИИ — платные прогоны, воспроизводимые ИЗ РЕПОЗИТОРИЯ.
⚠ Заведён 07.08 по собственной находке: покупки ярусов 1 и 2 были сделаны скриптами, лежавшими
во временном каталоге сессии, а не в дереве. Это ровно тот дефект, за который снята первая
редакция («$2.57 из $2.62 потрачены незафиксированным кодом»), и я его повторил. Здесь тот же
код лежит в зоне и коммитится пре-рег-фризом; все покупки кэшируются по тегу, поэтому повторный
запуск ничего не перекупает, а лишь пере-выводит числа.
Порядок ярусов задан каскадом зависимостей (§9 отчёта) и не переставляется:
--floor пол рига: арм A против собственного повтора A' на том же замороженном черновике.
Отрицательный контроль, парный к декою: декой проверяет, что судья ВИДИТ посаженную
разницу, пол — что он не видит несуществующей.
--variance разброс судьи-замены на повторе: решает, нужен ли третий судья как арбитр.
--arms3 армы третьей редакции на СОБСТВЕННОМ черновике: текст и цена из одного вызова.
--report свод всех трёх, $0.
Запуск: eval/.venv/bin/python eval/role_topology/tiers.py --floor|--variance|--arms3|--report
"""
from __future__ import annotations
import json
import statistics as st
import sys
import time
from pathlib import Path
REPO = Path("/home/ubuntu/projects/textmachine")
sys.path.insert(0, str(REPO / "eval" / "role_topology"))
import bakeoff as BO # noqa: E402
import buy as BUY # noqa: E402
import judges as J # noqa: E402
OUT = Path.home() / "books" / "role-topology"
# Потолки ярусов объявлены здесь и в §9 отчёта ДО первой покупки.
LED_FLOOR_ARMS = BUY.Ledger("пол рига: армы", 0.20, ("bo3-*.json",), default_expect=0.006)
LED_FLOOR_JUDGE = BUY.Ledger("пол рига: судьи", 0.30, ("jf-*.json",), default_expect=0.003)
LED_VARIANCE = BUY.Ledger("разброс судьи-замены", 0.15, ("jr-*.json",), default_expect=0.003)
def cmd_floor() -> None:
"""Пол рига. Второе назначение — разделение дисперсии: сколько даёт шум одного вызова,
а сколько разнородность единиц. От этого зависит, что покупать дальше: единицы или повторы."""
for u in BO.units():
spec = BO.ARMS["A"]
m = spec["model"]
kw = dict(model=m, messages=BO.build_msgs("A", u), max_tokens=16000, temperature=0.4)
r = BUY.purchase(LED_FLOOR_ARMS, f"bo3-Aprime-{u['uid']}", m, BO.client(m), kw,
arm="Aprime", uid=u["uid"])
if r.get("skipped"):
print("⛔ потолок армов пола")
return
time.sleep(0.15)
def text_ap(u: dict) -> str:
return json.loads((OUT / f"bo3-Aprime-{u['uid']}.json").read_text(encoding="utf-8"))["content"]
for u in BO.units():
ta, tb = BO.text_of("A", u), text_ap(u)
if not ta.strip() or not tb.strip():
continue
for jd in J.JUDGES:
for o in (0, 1):
v1, v2 = (ta, tb) if o == 0 else (tb, ta)
r = BUY.purchase(LED_FLOOR_JUDGE, f"jf-AvAprime-{u['uid']}-o{o}-{jd}-r1", jd,
J.client(jd), J.vote_kw(jd, J.packet(u["source"], v1, v2)),
contrast="AvAprime", uid=u["uid"], order=o, judge=jd)
if r.get("skipped"):
print("⛔ потолок судейства пола")
return
time.sleep(0.1)
report_floor()
def report_floor() -> None:
per, by_j = [], {}
for u in BO.units():
pj = []
for jd in J.JUDGES:
ms = []
for o in (0, 1):
e = BO.vote_errors(OUT / f"jf-AvAprime-{u['uid']}-o{o}-{jd}-r1.json")
if e:
ms.append((e[1] - e[0]) if o == 0 else (e[0] - e[1]))
if len(ms) == 2:
pj += ms
by_j.setdefault(jd, []).append(st.mean(ms))
if len(pj) == 2 * len(J.JUDGES):
per.append(st.mean(pj))
if not per:
print("голосов пола нет")
return
lo, hi = BO.boot_ci(per)
print(f"ПОЛ РИГА: A против A' = {st.mean(per):+.3f} 95% ДИ [{lo:+.2f}, {hi:+.2f}] "
f"n={len(per)} p={BO.sign_perm_p(per):.4f}")
print(f" sd по единицам {st.pstdev(per):.3f} ← ШУМ ОДНОГО ВЫЗОВА; истинная разница здесь НОЛЬ")
for jd, v in by_j.items():
print(f" {jd:24s} {st.mean(v):+.3f}")
def cmd_variance() -> None:
"""Разброс судьи-замены: повторяем УЖЕ КУПЛЕННЫЕ клетки тем же запросом."""
cand = "gemini-3.1-flash-lite"
pairs = []
for a, b in (("A", "F"), ("D", "A")):
for u in BO.units():
for o in (0, 1):
src = OUT / f"jv2-{a}v{b}-{u['uid']}-o{o}-{cand}-r1.json"
if not src.exists():
continue
ta, tb = BO.text_of(a, u), BO.text_of(b, u)
v1, v2 = (ta, tb) if o == 0 else (tb, ta)
rec = BUY.purchase(LED_VARIANCE, f"jr-{a}v{b}-{u['uid']}-o{o}-r2", cand,
J.client(cand), J.vote_kw(cand, J.packet(u["source"], v1, v2)),
contrast=f"{a}v{b}", uid=u["uid"], order=o, judge=cand)
if rec.get("skipped"):
print("⛔ потолок разброса")
break
e1 = BO.vote_errors(src)
e2 = BO.vote_errors(OUT / f"jr-{a}v{b}-{u['uid']}-o{o}-r2.json")
if e1 and e2:
pairs.append((sum(e1), sum(e2), e1[1] - e1[0], e2[1] - e2[0]))
time.sleep(0.1)
if not pairs:
print("пар повтора нет")
return
exact = sum(1 for x in pairs if x[0] == x[1])
mar = [abs(x[2] - x[3]) for x in pairs]
print(f"РАЗБРОС СУДЬИ-ЗАМЕНЫ: пар {len(pairs)} · ${LED_VARIANCE.spent():.5f}")
print(f" абсолютный счёт повторился точно: {exact}/{len(pairs)} ({exact / len(pairs):.0%})")
print(f" |разница перевеса|: медиана {st.median(mar):.1f} среднее {st.mean(mar):.2f} макс {max(mar)}")
print(f" sd шума перевеса на голос: {st.pstdev([x[2] - x[3] for x in pairs]) / 2 ** 0.5:.2f}")
def cmd_arms3() -> None:
"""Армы третьей редакции на собственном черновике. Черновик добирается до годного."""
for u in BO.units():
if not BO.own_draft(u["uid"]):
BO.run_draft(u)
miss = [u["uid"][:8] for u in BO.units() if not BO.own_draft(u["uid"])]
if miss:
print(f"⚠ единиц без годного черновика: {len(miss)} {miss}")
for arm in ("A", "A_law", "B"):
for u in BO.units():
r = BO.run_arm3(arm, u)
if r.get("skipped"):
print("⛔ потолок армов")
return
time.sleep(0.15)
print(f"армы третьей редакции готовы · касса ${BO.LED_ARMS.spent():.5f} из ${BO.CEIL_ARMS}")
report_econ()
def report_econ() -> None:
costs = [sum(json.loads(f.read_text(encoding="utf-8")).get("cost_usd", 0.0) or 0.0
for f in OUT.glob(f"bo2-DRAFT-{u['uid']}*.json")) for u in BO.units()]
att = [len(list(OUT.glob(f"bo2-DRAFT-{u['uid']}*.json"))) for u in BO.units()]
print(f"\nЧЕРНОВИК: медиана ${st.median(costs):.5f} · ОЖИДАЕМАЯ ${st.mean(costs):.5f} "
f"· ре-ген на {sum(1 for a in att if a > 1)}/{len(att)} единиц")
for arm in ("A", "A_law", "B"):
c = [json.loads((OUT / f"bo4-{arm}-{u['uid']}.json").read_text(encoding="utf-8"))["cost_usd"]
for u in BO.units() if (OUT / f"bo4-{arm}-{u['uid']}.json").exists()]
if c:
print(f" {arm:6s} собственный вызов ${st.median(c):.5f} · "
f"ПОЛНАЯ связка ${st.median(costs) + st.median(c):.5f}")
def cmd_report() -> None:
report_floor()
print()
cmd_variance.__doc__ # noqa: B018
report_econ()
if __name__ == "__main__":
a = sys.argv[1:] or ["--report"]
{"--floor": cmd_floor, "--variance": cmd_variance, "--arms3": cmd_arms3,
"--report": cmd_report}.get(a[0], lambda: print(__doc__))()