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:
Claude (backend session) 2026-08-09 03:21:57 +03:00
parent 026ded0bc1
commit f1f9947dd6
3 changed files with 277 additions and 11 deletions

200
eval/editor_tier/panel.py Normal file
View 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__)

View file

@ -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; семейство то же 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]: def by_new_price() -> list[str]:
"""Порядок замера — по ВОЗРАСТАНИЮ цены выхода: кончится потолок — непокрытыми останутся """Порядок замера — по ВОЗРАСТАНИЮ цены выхода: кончится потолок — непокрытыми останутся
дорогие, и это будет объявлено числом, а не скрыто.""" дорогие, и это будет объявлено числом, а не скрыто."""

View file

@ -112,20 +112,71 @@ def cmd_run() -> None:
MONEY.report() 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: def result() -> dict:
"""Вердикт по каждому кандидату — порогами эксп-22, без единой правки.""" """Оба вердикта: пре-рег-критерий пака (несущий) и полный критерий эксп-22 (справка)."""
import statistics as st # noqa: PLC0415
res = {} res = {}
for m in NEW: for m in NEW:
rows = [] rows = []
for f in MONEY.OUT.glob(f"et-scr-{m}-*.json"): for f in MONEY.OUT.glob(f"et-scr-{m}-*.json"):
rec = json.loads(f.read_text(encoding="utf-8")) rec = json.loads(f.read_text(encoding="utf-8"))
u = next((x for x in S22.units() if x["uid"] == rec.get("uid")), None) 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 "")) g = S22.gates(rec, u["source"] if u else "")
v, why = S22.verdict(rows) 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"] pt = [r["cost"] for r in rows if r["role"] == "translator"]
pe = [r["cost"] for r in rows if r["role"] == "editor"] pe = [r["cost"] for r in rows if r["role"] == "editor"]
import statistics as st # noqa: PLC0415 res[m] = dict(verdict=ved, why=whyed, verdict_full22=v22, why_full22=why22,
res[m] = dict(verdict=v, why=why, n=len(rows), n=len(rows),
price_translator=(st.median(pt) if pt else None), price_translator=(st.median(pt) if pt else None),
price_editor=(st.median(pe) if pe else None), price_editor=(st.median(pe) if pe else None),
reasoning_share=(max((r["reasoning_share"] for r in rows), default=0.0)), reasoning_share=(max((r["reasoning_share"] for r in rows), default=0.0)),
@ -134,14 +185,19 @@ def result() -> dict:
def cmd_score() -> None: def cmd_score() -> None:
print(f"{'модель':16s}{'вердикт':>10s}{'ед.':>4s}{'$/ед transl':>13s}{'$/ед edit':>11s}" rs = result()
f"{'размышл':>9s} причины") print("ВЕРДИКТ ПО ПРЕ-РЕГ-КРИТЕРИЮ ПАКА (редакторская роль) — несущий")
for m, r in result().items(): print(f"{'модель':16s}{'вердикт':>10s}{'ед.':>4s}{'$/ед edit':>11s}{'размышл':>9s} причины")
print(f"{m:16s}{r['verdict']:>10s}{r['n']:4d}" for m, r in rs.items():
f"{(r['price_translator'] or 0):13.5f}{(r['price_editor'] or 0):11.5f}" 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'])}") 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(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: def selfcheck() -> int: