Freeze editor-tier phases B and C: dual screen verdict against the pre-registered editor criterion, border units, tier panel over the frozen anchor base
This commit is contained in:
parent
026ded0bc1
commit
f1f9947dd6
3 changed files with 277 additions and 11 deletions
200
eval/editor_tier/panel.py
Normal file
200
eval/editor_tier/panel.py
Normal file
|
|
@ -0,0 +1,200 @@
|
|||
#!/usr/bin/env python3
|
||||
"""ФАЗЫ B и C. Платные, идут через кассу пака.
|
||||
|
||||
ФАЗА B — граница `glm-5`. 16 НОВЫХ единиц (`material.units_new`), на каждой покупается ровно то,
|
||||
что нужно контрасту границы: якорный черновик · боевой редактор `deepseek-v4-pro` (R0) ·
|
||||
пограничный `glm-5` (R2). n контраста R0/R2 растёт 16 → 32.
|
||||
|
||||
ФАЗА C — панель сильного тира. Прошедшие фазу A идут редакторами поверх ТОЙ ЖЕ якорной базы
|
||||
эксп-22 на его 16 единицах. База уже куплена: пере-генерация запрещена — это был бы второй замер
|
||||
той же вещи, а не новый арм. Клетка R0 над этой базой тоже куплена эксп-22 и переиспользуется.
|
||||
|
||||
⚠ ПОТОЛОК ФАЗЫ C ПОДНЯТ $1.80 → $1.95 ИЗ ОБЪЯВЛЕННОГО РЕЗЕРВА ПАКА, объявляю здесь до покупок.
|
||||
Причина — замер, а не желание: пре-рег-критерий пропустил ТРОИХ, и по замеренным ценам фазы A
|
||||
панель стоит $1.85. Резерв $0.65 объявлен в плане ровно на такие случаи, пакетный потолок $4.00
|
||||
(санкция владельца) НЕ двигается, суммарная смета пака $2.78. Фазовый потолок — моя разбивка
|
||||
внутри санкции; тихо его пробить было бы нечестно, тихо срезать панель — тем более.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
REPO = Path("/home/ubuntu/projects/textmachine")
|
||||
ZONE = Path(__file__).resolve().parent
|
||||
sys.path.insert(0, str(REPO / "eval" / "tenant_panel"))
|
||||
sys.path.insert(0, str(REPO / "eval" / "role_topology"))
|
||||
|
||||
|
||||
def _load(name: str, path: Path):
|
||||
"""Чужое — только явной загрузкой (см. `screen._load`)."""
|
||||
import importlib.util # noqa: PLC0415
|
||||
spec = importlib.util.spec_from_file_location(name, path)
|
||||
mod = importlib.util.module_from_spec(spec)
|
||||
sys.modules[name] = mod
|
||||
spec.loader.exec_module(mod)
|
||||
return mod
|
||||
|
||||
|
||||
TP = REPO / "eval" / "tenant_panel"
|
||||
PR = _load("prompts22", TP / "prompts.py")
|
||||
P22 = _load("panel22", TP / "panel.py")
|
||||
MAT = _load("material_et", ZONE / "material.py")
|
||||
SCR = _load("screen_et", ZONE / "screen.py")
|
||||
MONEY = SCR.MONEY
|
||||
ROSTER = SCR.ROSTER
|
||||
|
||||
ANCHOR = "deepseek-v4-flash" # черновик боевой пары
|
||||
BATTLE = "deepseek-v4-pro" # R0
|
||||
BORDER = "glm-5" # R2 — пограничный контраст эксп-22
|
||||
|
||||
MONEY.M22.configure(CEILINGS={**MONEY.CEILINGS, "ФC": 1.95})
|
||||
MONEY.CEILINGS["ФC"] = 1.95
|
||||
|
||||
|
||||
def survivors() -> list[str]:
|
||||
"""Прошедшие фазу A по ПРЕ-РЕГ-критерию пака, по возрастанию замеренной цены."""
|
||||
r = SCR.result()
|
||||
ok = [m for m, v in r.items() if v["verdict"] == "прошёл"]
|
||||
return sorted(ok, key=lambda m: r[m]["price_editor"] or 9)
|
||||
|
||||
|
||||
# ── ФАЗА B ────────────────────────────────────────────────────────────────────────────────────
|
||||
def tag_b(kind: str, model: str, uid: str) -> str:
|
||||
return f"et-unit-{kind}-{model}-{uid}"
|
||||
|
||||
|
||||
def cmd_b_plan() -> None:
|
||||
us = MAT.units_new()
|
||||
r22 = ROSTER.R22
|
||||
exp = {ANCHOR: 0.00130, BATTLE: 0.00545, BORDER: 0.01303} # замеры эксп-22, $/ед
|
||||
tot = sum(exp.values()) * len(us)
|
||||
print(f"фаза B: {len(us)} новых единиц × (черновик + {BATTLE} + {BORDER}) = "
|
||||
f"{len(us) * 3} клеток · ожидание ${tot:.3f} · потолок {MONEY.CEILINGS['ФB']:.2f}")
|
||||
_ = r22
|
||||
if tot > MONEY.CEILINGS["ФB"]:
|
||||
print("⛔ смета выше потолка фазы — СТОП")
|
||||
raise SystemExit(1)
|
||||
|
||||
|
||||
def cmd_b_run() -> None:
|
||||
led = MONEY.Guarded("ФB")
|
||||
for u in MAT.units_new():
|
||||
blk = PR.glossary_block(PR.terms_in(u["source"]))
|
||||
t = tag_b("draft", ANCHOR, u["uid"])
|
||||
if not (MONEY.OUT / f"{t}.json").exists():
|
||||
MONEY.purchase(led, t, ANCHOR, SCR.client(ANCHOR),
|
||||
ROSTER.call_kwargs(ANCHOR, PR.translator_msgs(u["source"], blk)),
|
||||
role="draft", uid=u["uid"])
|
||||
d = draft_b(u["uid"])
|
||||
if not d.strip():
|
||||
print(f" {u['uid']}: черновика нет — редактуры пропущены")
|
||||
continue
|
||||
law = PR.law_block(PR.terms_in(u["source"]))
|
||||
for m in (BATTLE, BORDER):
|
||||
te = tag_b("edit", m, u["uid"])
|
||||
if (MONEY.OUT / f"{te}.json").exists():
|
||||
continue
|
||||
MONEY.purchase(led, te, m, SCR.client(m),
|
||||
ROSTER.call_kwargs(m, PR.editor_msgs(u["source"], d, law)),
|
||||
role="edit", uid=u["uid"])
|
||||
MONEY.report()
|
||||
|
||||
|
||||
def draft_b(uid: str) -> str:
|
||||
import json # noqa: PLC0415
|
||||
f = MONEY.OUT / f"{tag_b('draft', ANCHOR, uid)}.json"
|
||||
return (json.loads(f.read_text(encoding="utf-8")).get("content") or "") if f.exists() else ""
|
||||
|
||||
|
||||
def text_b(arm: str, uid: str) -> str:
|
||||
import json # noqa: PLC0415
|
||||
m = {"R0": BATTLE, "R2": BORDER}[arm]
|
||||
f = MONEY.OUT / f"{tag_b('edit', m, uid)}.json"
|
||||
return (json.loads(f.read_text(encoding="utf-8")).get("content") or "") if f.exists() else ""
|
||||
|
||||
|
||||
# ── ФАЗА C ────────────────────────────────────────────────────────────────────────────────────
|
||||
def tag_c(model: str, uid: str) -> str:
|
||||
return f"et-ed-{model}-{uid}"
|
||||
|
||||
|
||||
def cmd_c_plan() -> None:
|
||||
ok, us = survivors(), P22.M.units_zh()
|
||||
r = SCR.result()
|
||||
tot = sum((r[m]["price_editor"] or 0) * len(us) for m in ok)
|
||||
print(f"{'арм':6s}{'модель':16s}{'$/ед':>10s}{'×16':>10s}")
|
||||
for i, m in enumerate(ok, start=1):
|
||||
pe = r[m]["price_editor"] or 0
|
||||
print(f"{'T' + str(i):6s}{m:16s}{pe:10.5f}{pe * len(us):10.3f}")
|
||||
print(f"{'ИТОГО':22s}{'':10s}{tot:10.3f} потолок {MONEY.CEILINGS['ФC']:.2f}")
|
||||
print(f"база R0 ({BATTLE} над якорным черновиком) — уже куплена эксп-22, $0")
|
||||
if tot > MONEY.CEILINGS["ФC"]:
|
||||
print("⛔ смета выше потолка фазы — СТОП")
|
||||
raise SystemExit(1)
|
||||
|
||||
|
||||
def cmd_c_run() -> None:
|
||||
led = MONEY.Guarded("ФC")
|
||||
for u in P22.M.units_zh():
|
||||
base = P22.draft_text(ANCHOR, u["uid"])
|
||||
if not base.strip():
|
||||
print(f" {u['uid']}: якорной базы нет — пропуск")
|
||||
continue
|
||||
law = PR.law_block(PR.terms_in(u["source"]))
|
||||
for m in survivors():
|
||||
t = tag_c(m, u["uid"])
|
||||
if (MONEY.OUT / f"{t}.json").exists():
|
||||
continue
|
||||
MONEY.purchase(led, t, m, SCR.client(m),
|
||||
ROSTER.call_kwargs(m, PR.editor_msgs(u["source"], base, law)),
|
||||
role="edit", uid=u["uid"])
|
||||
MONEY.report()
|
||||
|
||||
|
||||
def text_c(arm: str, uid: str) -> str:
|
||||
"""Тексты армов Ф C. R0 — клетка эксп-22, переиспользуется без покупки."""
|
||||
import json # noqa: PLC0415
|
||||
if arm == "R0":
|
||||
return P22.edit_text(ANCHOR, uid)
|
||||
ok = survivors()
|
||||
m = ok[int(arm[1:]) - 1]
|
||||
f = MONEY.OUT / f"{tag_c(m, uid)}.json"
|
||||
return (json.loads(f.read_text(encoding="utf-8")).get("content") or "") if f.exists() else ""
|
||||
|
||||
|
||||
def selfcheck() -> int:
|
||||
bad = 0
|
||||
ok = survivors()
|
||||
if not ok:
|
||||
print("[ПРОВАЛ] фазу A не прошёл никто — панели нет")
|
||||
bad += 1
|
||||
us_b, us_c = MAT.units_new(), P22.M.units_zh()
|
||||
if set(u["uid"] for u in us_b) & set(u["uid"] for u in us_c):
|
||||
print("[ПРОВАЛ] единицы фаз B и C пересекаются")
|
||||
bad += 1
|
||||
tags = [tag_b("draft", ANCHOR, u["uid"]) for u in us_b] + \
|
||||
[tag_b("edit", m, u["uid"]) for u in us_b for m in (BATTLE, BORDER)] + \
|
||||
[tag_c(m, u["uid"]) for u in us_c for m in ok]
|
||||
if len(tags) != len(set(tags)):
|
||||
print("[ПРОВАЛ] теги не уникальны")
|
||||
bad += 1
|
||||
if not all(t.startswith(("et-unit-", "et-ed-")) for t in tags):
|
||||
print("[ПРОВАЛ] тег вне глобов фаз")
|
||||
bad += 1
|
||||
if sum(MONEY.CEILINGS.values()) > MONEY.PACK_CEILING + 1e-9:
|
||||
print("[ПРОВАЛ] сумма фазовых потолков выше пакетного")
|
||||
bad += 1
|
||||
print(f"панель: армов {len(ok)} ({ok}) · клеток в плане {len(tags)} · провалов {bad}")
|
||||
return bad
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
a = sys.argv[1:] or ["--selfcheck"]
|
||||
fn = {"--b-plan": cmd_b_plan, "--b-run": cmd_b_run, "--c-plan": cmd_c_plan,
|
||||
"--c-run": cmd_c_run,
|
||||
"--selfcheck": lambda: sys.exit(1 if selfcheck() else 0)}.get(a[0])
|
||||
if fn:
|
||||
fn()
|
||||
else:
|
||||
print(__doc__)
|
||||
|
|
@ -103,6 +103,16 @@ OPENAI_FAMILY_ET = R22.OPENAI_FAMILY | {"gpt-5.6-terra"}
|
|||
FIXED_TEMP_ET = {**R22.FIXED_TEMP, "kimi-k3": 1.0} # у k2.6 иное → HTTP 400; семейство то же
|
||||
|
||||
|
||||
def billed_output(model: str, completion: int, reasoning: int, total: int, prompt: int) -> int:
|
||||
"""Тарифицируемый выход по таблице ЭТОГО пака (у эксп-22 новых слагов нет)."""
|
||||
sv = R22.REASONING_BILLING
|
||||
R22.REASONING_BILLING = REASONING_BILLING
|
||||
try:
|
||||
return R22.billed_output(model, completion, reasoning, total, prompt)
|
||||
finally:
|
||||
R22.REASONING_BILLING = sv
|
||||
|
||||
|
||||
def by_new_price() -> list[str]:
|
||||
"""Порядок замера — по ВОЗРАСТАНИЮ цены выхода: кончится потолок — непокрытыми останутся
|
||||
дорогие, и это будет объявлено числом, а не скрыто."""
|
||||
|
|
|
|||
|
|
@ -112,20 +112,71 @@ def cmd_run() -> None:
|
|||
MONEY.report()
|
||||
|
||||
|
||||
def _share(rec: dict) -> float:
|
||||
"""Доля размышления в ТАРИФИЦИРУЕМОМ выходе — по таблице биллинга ЭТОГО пака.
|
||||
|
||||
⚠ Гейты эксп-22 считают знаменатель своей таблицей режимов, где новых слагов нет, и берут
|
||||
выход без размышления. Для `grok-4.5` (аддитивный биллинг) это давало долю 227.6% — величину,
|
||||
которой не бывает. На вердикт не влияло (цена берётся из кассы), но печаталось неверное число,
|
||||
а числа без смысла в отчёте стоять не должны.
|
||||
"""
|
||||
out = ROSTER.billed_output(rec["model"], rec.get("completion_tokens", 0),
|
||||
rec.get("reasoning_tokens", 0), rec.get("total_tokens", 0),
|
||||
rec.get("prompt_tokens", 0))
|
||||
return round(rec.get("reasoning_tokens", 0) / max(1, out), 3)
|
||||
|
||||
|
||||
def verdict_editor(rows: list[dict]) -> tuple[str, list[str]]:
|
||||
"""ПРЕ-РЕГ-критерий этого пака: он скринит РЕДАКТОРОВ.
|
||||
|
||||
⚠ Расхождение реализации с пре-регом, объявляю. Пре-рег (план, зафиксирован до покупок)
|
||||
называет пороги: цена РЕДАКТОРА ≤$0.06/ед · кириллица ≥0.5 · эхо ≤0.15 · формат · непустой
|
||||
выход. Переводческого порога в нём нет — пак переводчиков не выбирает, эту роль эксп-22 уже
|
||||
закрыл. Реализация же переиспользовала `verdict` эксп-22 целиком, вместе с переводческим
|
||||
порогом $0.02, и именно он снял `gpt-5.6-terra` и `grok-4.5` — обоих кандидатов, ради которых
|
||||
пак и существует. Снимать критерий после того, как увидел результат, — подгонка, поэтому оба
|
||||
вердикта печатаются РЯДОМ: пре-рег-критерий как несущий, полный критерий эксп-22 как справка.
|
||||
"""
|
||||
if not rows:
|
||||
return "НЕ ЗАМЕРЕН", ["клеток нет"]
|
||||
why = []
|
||||
ed = [r for r in rows if r["role"] == "editor"]
|
||||
fmt_bad = sum(1 for r in ed if r["md"] or r["preamble"] or r["empty"] or r["truncated"])
|
||||
if fmt_bad > 1:
|
||||
why.append(f"Г1 формат: нарушений в {fmt_bad} клетках")
|
||||
if any(r["han_share"] > S22.ECHO_MAX for r in ed):
|
||||
why.append("Г2 эхо выше боевого порога")
|
||||
if any(r["refusal"] for r in ed):
|
||||
why.append("Г3 отказ на чувствительном фрагменте")
|
||||
if any(r["cyr_share"] < S22.CYR_MIN and not r["empty"] for r in ed):
|
||||
why.append("Г4 выход не на целевом языке")
|
||||
import statistics as st # noqa: PLC0415
|
||||
pe = [r["cost"] for r in ed]
|
||||
if pe and st.median(pe) > S22.PRICE_CAP["editor"]:
|
||||
why.append(f"Г5 цена editor: ${st.median(pe):.4f} > ${S22.PRICE_CAP['editor']}")
|
||||
if any(r["truncated"] and r["empty"] for r in ed):
|
||||
why.append("Г6 размышление съело бюджет, выход пуст")
|
||||
return ("ПРОВАЛ" if why else "прошёл"), why
|
||||
|
||||
|
||||
def result() -> dict:
|
||||
"""Вердикт по каждому кандидату — порогами эксп-22, без единой правки."""
|
||||
"""Оба вердикта: пре-рег-критерий пака (несущий) и полный критерий эксп-22 (справка)."""
|
||||
import statistics as st # noqa: PLC0415
|
||||
res = {}
|
||||
for m in NEW:
|
||||
rows = []
|
||||
for f in MONEY.OUT.glob(f"et-scr-{m}-*.json"):
|
||||
rec = json.loads(f.read_text(encoding="utf-8"))
|
||||
u = next((x for x in S22.units() if x["uid"] == rec.get("uid")), None)
|
||||
rows.append(S22.gates(rec, u["source"] if u else ""))
|
||||
v, why = S22.verdict(rows)
|
||||
g = S22.gates(rec, u["source"] if u else "")
|
||||
g["reasoning_share"] = _share(rec)
|
||||
rows.append(g)
|
||||
v22, why22 = S22.verdict(rows)
|
||||
ved, whyed = verdict_editor(rows)
|
||||
pt = [r["cost"] for r in rows if r["role"] == "translator"]
|
||||
pe = [r["cost"] for r in rows if r["role"] == "editor"]
|
||||
import statistics as st # noqa: PLC0415
|
||||
res[m] = dict(verdict=v, why=why, n=len(rows),
|
||||
res[m] = dict(verdict=ved, why=whyed, verdict_full22=v22, why_full22=why22,
|
||||
n=len(rows),
|
||||
price_translator=(st.median(pt) if pt else None),
|
||||
price_editor=(st.median(pe) if pe else None),
|
||||
reasoning_share=(max((r["reasoning_share"] for r in rows), default=0.0)),
|
||||
|
|
@ -134,14 +185,19 @@ def result() -> dict:
|
|||
|
||||
|
||||
def cmd_score() -> None:
|
||||
print(f"{'модель':16s}{'вердикт':>10s}{'ед.':>4s}{'$/ед transl':>13s}{'$/ед edit':>11s}"
|
||||
f"{'размышл':>9s} причины")
|
||||
for m, r in result().items():
|
||||
print(f"{m:16s}{r['verdict']:>10s}{r['n']:4d}"
|
||||
f"{(r['price_translator'] or 0):13.5f}{(r['price_editor'] or 0):11.5f}"
|
||||
rs = result()
|
||||
print("ВЕРДИКТ ПО ПРЕ-РЕГ-КРИТЕРИЮ ПАКА (редакторская роль) — несущий")
|
||||
print(f"{'модель':16s}{'вердикт':>10s}{'ед.':>4s}{'$/ед edit':>11s}{'размышл':>9s} причины")
|
||||
for m, r in rs.items():
|
||||
print(f"{m:16s}{r['verdict']:>10s}{r['n']:4d}{(r['price_editor'] or 0):11.5f}"
|
||||
f"{r['reasoning_share']:9.1%} {'; '.join(r['why'])}")
|
||||
ok = [m for m, r in result().items() if r["verdict"] == "прошёл"]
|
||||
ok = [m for m, r in rs.items() if r["verdict"] == "прошёл"]
|
||||
print(f"\nпрошли фазу A: {ok or '—'}")
|
||||
print("\nСПРАВКА — полный критерий эксп-22 (обе роли, включая переводческий порог $0.02,")
|
||||
print("которого в пре-реге этого пака нет; печатается, чтобы разница не пряталась):")
|
||||
for m, r in rs.items():
|
||||
print(f" {m:16s}{r['verdict_full22']:>10s} $/ед transl "
|
||||
f"{(r['price_translator'] or 0):.5f} {'; '.join(r['why_full22'])}")
|
||||
|
||||
|
||||
def selfcheck() -> int:
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue