119 lines
6.4 KiB
Python
119 lines
6.4 KiB
Python
#!/usr/bin/env python3
|
||
"""Сухая проверка ПОСЫЛКИ четырёх армов: что реально уедет на провод. Ни одного платного вызова.
|
||
|
||
Инварианты, которые обязаны держаться (иначе арм измерит не то, что заявлено):
|
||
И1 диффовый и полный контракты одного арма получают ОДИН И ТОТ ЖЕ блок закона и ОДИН И ТОТ ЖЕ
|
||
черновик — различаться должен ТОЛЬКО системный контракт, иначе сравнение не контролировано;
|
||
И2 ни в одном сообщении не осталось незаполненных `{{плейсхолдеров}}`;
|
||
И3 у арма E инструкции и черновик АНГЛИЙСКИЕ (латиница > кириллицы) — иначе повтор дефекта 05.08;
|
||
И4 у арма L reasoning_effort='low' стоит ровно на обоих контрактах, и больше нигде;
|
||
E5 у арма N системный контракт НЕ содержит стиль-мандата, а у арма D (проба 19) — содержит.
|
||
"""
|
||
from __future__ import annotations
|
||
import re
|
||
import sys
|
||
from pathlib import Path
|
||
|
||
REPO = Path("/home/ubuntu/projects/textmachine")
|
||
sys.path.insert(0, str(REPO / "eval" / "editor_contract"))
|
||
sys.path.insert(0, str(REPO / "eval" / "bank_arbitration"))
|
||
|
||
import final_probes as F # noqa: E402
|
||
|
||
FAIL = 0
|
||
|
||
|
||
def check(name: str, ok: bool, note: str = "") -> None:
|
||
global FAIL
|
||
if not ok:
|
||
FAIL += 1
|
||
print(f" [{'OK ' if ok else 'ПРОВАЛ'}] {name}" + (f" — {note}" if note else ""))
|
||
|
||
|
||
def cyr_lat(s: str) -> tuple[int, int]:
|
||
return (sum(1 for c in s if "а" <= c.lower() <= "я"),
|
||
sum(1 for c in s if "a" <= c.lower() <= "z"))
|
||
|
||
|
||
def plan_of(arm: str):
|
||
if arm == "L":
|
||
return F.plan_L()
|
||
if arm == "N":
|
||
return F.plan_N()
|
||
if arm == "G":
|
||
return F.plan_G({f"G{u}": "ЧЕРНОВИК-G" + str(u) for u in range(2)})
|
||
return F.plan_E({f"E{k}": "DRAFT-E" + str(k) for k in range(6)})
|
||
|
||
|
||
def main() -> None:
|
||
for arm in ("L", "N", "G", "E"):
|
||
plan = plan_of(arm)
|
||
print(f"\n=== арм {arm}: {len(plan)} вызовов ===")
|
||
tags = [p[0] for p in plan]
|
||
check("теги уникальны", len(set(tags)) == len(tags))
|
||
|
||
# И2 — плейсхолдеры
|
||
holes = set()
|
||
for tag, msgs, kind, draft, extra in plan:
|
||
for m in msgs:
|
||
holes |= set(re.findall(r"\{\{(\w+)\}\}", m["content"]))
|
||
check("И2 нет незаполненных плейсхолдеров", not holes, ", ".join(sorted(holes)))
|
||
|
||
# И1 — парность diff/full на одном материале
|
||
bykey = {}
|
||
for tag, msgs, kind, draft, extra in plan:
|
||
key = re.sub(r"^fp-.?.?", "", tag)
|
||
bykey.setdefault(key, {})[kind] = (msgs, draft)
|
||
pairs = [v for v in bykey.values() if len(v) == 2]
|
||
if pairs:
|
||
same_block = all(
|
||
[m["content"] for m in v["diff"][0] if m["role"] == "system"][1:]
|
||
== [m["content"] for m in v["full"][0] if m["role"] == "system"][1:]
|
||
for v in pairs)
|
||
same_draft = all(v["diff"][1] == v["full"][1] for v in pairs)
|
||
differ_sys = all(v["diff"][0][0]["content"] != v["full"][0][0]["content"] for v in pairs)
|
||
check(f"И1 блок закона одинаков в diff/full ({len(pairs)} пар)", same_block)
|
||
check("И1 черновик одинаков в diff/full", same_draft)
|
||
check("И1 системный контракт РАЗЛИЧАЕТСЯ", differ_sys)
|
||
else:
|
||
check("И1 арм одноконтрактный (пар нет)", arm == "N", "N — только дифф, это по дизайну")
|
||
|
||
# И4 — режим разума
|
||
efforts = {str(p[4]) for p in plan}
|
||
if arm == "L":
|
||
check("И4 reasoning_effort=low на всех вызовах",
|
||
efforts == {"{'reasoning_effort': 'low'}"}, str(efforts))
|
||
else:
|
||
check("И4 режим разума не задаётся (дефолт)", efforts == {"None"}, str(efforts))
|
||
|
||
# И3 — язык арма E
|
||
if arm == "E":
|
||
sysmsg = plan[0][1][0]["content"]
|
||
c, l = cyr_lat(sysmsg)
|
||
check("И3 инструкции английские", l > c, f"лат={l} кир={c}")
|
||
blk = plan[0][1][1]["content"]
|
||
c2, l2 = cyr_lat(blk)
|
||
check("И3 блок закона английский", l2 > c2, f"лат={l2} кир={c2}")
|
||
|
||
# E5 — стиль-мандат. ⚠ Подстрочный регекс `стил|благозвуч` здесь НЕ работает: fidelity-промпт
|
||
# называет стиль в ЗАПРЕТЕ («Стиль… НЕ ТРОГАЙ ВООБЩЕ»), и наивная проверка считала запрет
|
||
# мандатом. Отличаем по полярности: у N обязан быть явный запрет и сужение мандата.
|
||
sysmsg = plan[0][1][0]["content"]
|
||
if arm == "N":
|
||
forbids = bool(re.search(r"(стил|благозвуч|лексик)[^.]{0,80}не трогай", sysmsg, re.I))
|
||
narrow = bool(re.search(r"мандат\s+узкий|только\s+ВЕРНОСТЬ", sysmsg, re.I))
|
||
full_sys = [p[1][0]["content"] for p in F.plan_L() if p[2] == "full"][0]
|
||
positive_elsewhere = bool(re.search(r"Правь стиль", full_sys))
|
||
check("E5 у N стиль назван ЗАПРЕТОМ", forbids)
|
||
check("E5 у N мандат сужен до верности", narrow)
|
||
check("E5 у полного контракта стиль-мандат ЕСТЬ (контраст)", positive_elsewhere)
|
||
print(f" системный контракт, первая строка: {sysmsg.splitlines()[0][:100]!r}")
|
||
print(f" сообщений в вызове: {len(plan[0][1])} (роли: "
|
||
f"{[m['role'] for m in plan[0][1]]})")
|
||
|
||
print(f"\n{'ПОСЫЛКА ЧИСТА' if not FAIL else f'ПРОВАЛОВ: {FAIL}'}")
|
||
sys.exit(1 if FAIL else 0)
|
||
|
||
|
||
if __name__ == "__main__":
|
||
main()
|