179 lines
9.4 KiB
Python
179 lines
9.4 KiB
Python
#!/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__))()
|