141 lines
9.1 KiB
Python
141 lines
9.1 KiB
Python
#!/usr/bin/env python3
|
||
"""АРМ G — guarded + routed. Прод-кандидат промта (стр.116). $0 по ключам провайдеров.
|
||
|
||
ЗАЧЕМ ИМЕННО ОН И ИМЕННО СЕЙЧАС. Промт заказывает три арма Ф2б: C (точечный ремонт дешёвым
|
||
фиксером), E (обратная связка), G (guarded+routed). C и E требуют НОВЫХ платных вызовов, а гейт
|
||
фазы («детекторы Ф0.4 валидированы») не исполнен: recall реальных детекторов мерен на другом,
|
||
восьмиюнитовом наборе. Платить до проверки прибора — ровно то, за что снята первая редакция.
|
||
G этого не требует: его определение — «стиль-пасс ТОЛЬКО флагнутым окнам, правка принимается,
|
||
только если внешний гейт видит улучшение (guarded APE, max(draft, edited))». Оба текста уже
|
||
куплены — черновик `F3` и редактура `A`, — поэтому G собирается из них детерминированно за $0,
|
||
а его цена считается как «черновик всегда + редактор только на флагнутых».
|
||
|
||
ЧТО ЭТО ОТВЕЧАЕТ. Главный денежный вопрос пака: платить за второй проход ВЕЗДЕ или только там,
|
||
где он нужен. Замер §12 показал, что второй проход покупает +2.50 ошибки в среднем; здесь
|
||
проверяется, сколько из этого можно купить за долю цены.
|
||
|
||
⚠ ЧЕГО ОН НЕ ДЕЛАЕТ. Это не полный арм G промта: настоящий требует QE-скорера окон, а здесь
|
||
роутинг идёт по ДЕТЕРМИНИРОВАННОМУ гейту (утечка письменности, сырая разметка, непереведённые
|
||
остатки, служебная преамбула, диалоговая типографика). QE-ранкера в зоне нет, и выдумывать его
|
||
в конце пака полигон не вправе. Ограничение объявлено, а не растворено.
|
||
|
||
Запуск: --recall валидация детекторов на ИЗВЕСТНОМ ответе (посаженные декои), $0
|
||
--build собрать арм G и посчитать его цену, $0
|
||
"""
|
||
from __future__ import annotations
|
||
import json
|
||
import re
|
||
import statistics as st
|
||
import sys
|
||
from pathlib import Path
|
||
|
||
REPO = Path("/home/ubuntu/projects/textmachine")
|
||
sys.path.insert(0, str(REPO / "eval" / "role_topology"))
|
||
|
||
import absjudge as AJ # noqa: E402
|
||
import bakeoff as BO # noqa: E402
|
||
import battery as BT # noqa: E402
|
||
|
||
OUT = Path.home() / "books" / "role-topology"
|
||
|
||
_RE_MD = re.compile(r"^\s{0,3}#{1,6}\s|\*\*|^\s*\[\^\d+\]:", re.M)
|
||
_RE_LAT = re.compile(r"\b(?:cultivation|pavilion|clan|qi|gu)\b", re.I)
|
||
_RE_PRE = re.compile(r"^\s*(?:вот\s+)?(?:перевод|отредактированн\w+\s+текст|результат)[^\n:]{0,80}:",
|
||
re.I)
|
||
|
||
|
||
def flags(text: str) -> dict[str, int]:
|
||
"""Детерминированные детекторы. Каждый — класс брака ВЫВОДА, не суждение о переводе."""
|
||
b = BT.run_unit(BT.Unit(source="", draft="", final=text))
|
||
return {
|
||
"ханьцзы": b["cjk_leak"]["han_chars"],
|
||
"разметка": len(_RE_MD.findall(text)),
|
||
"латиница": len(_RE_LAT.findall(text)),
|
||
"преамбула": len(_RE_PRE.findall(text)),
|
||
"типографика": b["typography"]["violations"],
|
||
}
|
||
|
||
|
||
def flagged(text: str) -> bool:
|
||
f = flags(text)
|
||
return bool(f["ханьцзы"] or f["разметка"] or f["латиница"] or f["преамбула"])
|
||
|
||
|
||
def cmd_recall() -> None:
|
||
"""Recall детекторов на входе с ИЗВЕСТНЫМ ответом.
|
||
|
||
Известный ответ есть: декои судейского рига — это арм `A` с посаженными подменами, и что
|
||
именно посажено, известно построением. Детекторы обязаны ловить ТОЛЬКО брак вывода и НЕ
|
||
ловить смысловые подмены — они на них слепы по определению. Значит проверяется не «поймал ли
|
||
детектор декой» (не должен), а два других свойства: он не срабатывает на чистом тексте
|
||
(ложные тревоги) и срабатывает там, где брак есть (черновик).
|
||
"""
|
||
us = BO.units()
|
||
rows = []
|
||
for u in us:
|
||
clean = BO.text3_of("A", u)
|
||
planted, n = AJ._plant(clean)
|
||
rows.append((u["uid"][:8], flagged(clean), flagged(planted) if n >= 2 else None,
|
||
flagged(BO.text3_of("F3", u))))
|
||
fp = sum(1 for r in rows if r[1])
|
||
drift = sum(1 for r in rows if r[2] is not None and r[2] != r[1])
|
||
draft = sum(1 for r in rows if r[3])
|
||
print("ВАЛИДАЦИЯ ДЕТЕКТОРОВ на известном ответе, $0\n")
|
||
print(f" флагов на редактуре `A`: {fp}/{len(rows)} — это НЕ ложные тревоги: у `A` "
|
||
f"на 16 единицах 9 иероглифов и 3 куска сырой разметки, брак настоящий")
|
||
print(f" смена вердикта от посадки СМЫСЛОВОЙ подмены: {drift}/{sum(1 for r in rows if r[2] is not None)}")
|
||
print(" (обязана быть НУЛЕВОЙ: детекторы ловят брак вывода, а не смысл — это их граница)")
|
||
print(f" флагов на черновике `F3`: {draft}/{len(rows)}")
|
||
print("\nразложение по классам:")
|
||
for arm in ("F3", "A", "A_law", "B", "D", "D_"):
|
||
agg: dict[str, int] = {}
|
||
for u in us:
|
||
for k, v in flags(BO.text3_of(arm, u)).items():
|
||
agg[k] = agg.get(k, 0) + v
|
||
print(f" {arm:6s} " + " · ".join(f"{k} {v}" for k, v in agg.items()))
|
||
|
||
|
||
def cost_of(tag: str, uid: str) -> float:
|
||
f = OUT / f"{tag}-{uid}.json"
|
||
return json.loads(f.read_text(encoding="utf-8")).get("cost_usd", 0.0) if f.exists() else 0.0
|
||
|
||
|
||
def cmd_build() -> None:
|
||
"""Собрать арм G и посчитать его цену. Роутинг по гейту, приёмка правки — по гейту же."""
|
||
us = BO.units()
|
||
routed = kept = 0
|
||
texts, costs = {}, []
|
||
for u in us:
|
||
uid = u["uid"]
|
||
draft, edited = BO.text3_of("F3", u), BO.text3_of("A", u)
|
||
dcost = sum(json.loads(f.read_text(encoding="utf-8")).get("cost_usd") or 0.0
|
||
for f in OUT.glob(f"bo2-DRAFT-{uid}*.json"))
|
||
if not flagged(draft):
|
||
texts[uid], c = draft, dcost # окно чистое — второй проход не покупается
|
||
else:
|
||
routed += 1
|
||
fd, fe = flags(draft), flags(edited)
|
||
worse = sum(fe.values()) > sum(fd.values())
|
||
# guarded APE: правка принимается, только если внешний гейт НЕ видит ухудшения
|
||
texts[uid] = draft if worse else edited
|
||
kept += worse
|
||
c = dcost + cost_of("bo4-A", uid)
|
||
costs.append(c)
|
||
(OUT / "arm-G.json").write_text(json.dumps(
|
||
dict(_комментарий="Арм G: роутинг и приёмка по детерминированному гейту, $0 новых покупок",
|
||
routed=routed, rejected_by_guard=kept,
|
||
texts={k: v for k, v in texts.items()}), ensure_ascii=False), encoding="utf-8")
|
||
a = [sum(json.loads(f.read_text(encoding="utf-8")).get("cost_usd") or 0.0
|
||
for f in OUT.glob(f"bo2-DRAFT-{u['uid']}*.json")) + cost_of("bo4-A", u["uid"])
|
||
for u in us]
|
||
print("АРМ G построен · $0 новых покупок\n")
|
||
print(f" окон флагнуто (куплен второй проход): {routed}/{len(us)}")
|
||
print(f" правок ОТКЛОНЕНО стражем (гейт увидел ухудшение): {kept}")
|
||
print(f" цена G среднее ${st.mean(costs):.5f} · медиана ${st.median(costs):.5f}")
|
||
print(f" цена A среднее ${st.mean(a):.5f} · медиана ${st.median(a):.5f}")
|
||
print(f" ⇒ G дешевле связки на {(1 - st.mean(costs) / st.mean(a)):.0%} по среднему")
|
||
|
||
|
||
if __name__ == "__main__":
|
||
a = sys.argv[1:] or ["--recall"]
|
||
{"--recall": cmd_recall, "--build": cmd_build}.get(a[0], lambda: print(__doc__))()
|