textmachine/eval/dovodka/sysmsg.py

111 lines
6.4 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
"""КАКОЕ СИСТЕМНОЕ СООБЩЕНИЕ ВЫЖИВАЕТ У GEMINI — ДЕТЕРМИНИРОВАННАЯ УЛИКА. $0.
⚠ ЗАЧЕМ ОТДЕЛЬНЫЙ ФАЙЛ. Вопрос стоял открытым в `docs/experiments/00-provider-quirks.md`
дословно: «КАКОЕ именно выживает — НЕ УСТАНОВЛЕНО … Расхождение открыто». Закрыт он не новой
живой пробой (она стоит денег и протухает вместе со слагом), а АРИФМЕТИКОЙ уже купленных клеток.
Такой вывод обязан иметь исполняемого носителя: иначе через месяц он останется утверждением в
отчёте, которое никто не может пере-снять.
ЛОГИКА ДОКАЗАТЕЛЬСТВА, и она не требует ни одного вызова модели:
у арма `RE` на провод уходят ТРИ сообщения —
system[0] ролевой промт ПОБАЙТНО ОДИН И ТОТ ЖЕ на всех главах
system[1] глоссарий РАЗНЫЙ на каждой главе (термины берутся из текста главы)
user исходник разный
карантинные клетки `RQ` — тот же вызов, у которого вендор что-то выбросил.
⇒ если разность `RE.prompt_tokens RQ.prompt_tokens` ПОСТОЯННА по главам, выброшено
сообщение постоянного размера, а таково только system[0] — ролевой промт.
Будь выброшены ОБА системных, разность росла бы вместе с глоссарием и постоянной быть
не могла; будь выброшено только system[1], постоянной была бы не разность, а сам `RQ`.
⛔ ГЕЙТ ПАДАЕТ, ЕСЛИ ВЫВОД НЕ ДЕРЖИТСЯ. Он проверяет ТРИ вещи, и каждая может уронить:
1. system[0] действительно побайтно постоянен, а system[1] действительно варьирует
(иначе рассуждение неприменимо — и это надо увидеть, а не предположить);
2. разность постоянна с точностью до нуля;
3. остаток `RQ` разложим на глоссарий + исходник: он обязан коррелировать с длиной ГЛОССАРИЯ
сильнее, чем с длиной исходника.
Запуск: eval/.venv/bin/python eval/dovodka/sysmsg.py
"""
from __future__ import annotations
import json
import statistics as st
import sys
from pathlib import Path
ZONE = Path(__file__).resolve().parent
sys.path.insert(0, str(ZONE))
import rol # noqa: E402
OUT = Path.home() / "books" / "dovodka"
def _corr(a: list[float], b: list[float]) -> float:
ma, mb = st.mean(a), st.mean(b)
num = sum((x - ma) * (y - mb) for x, y in zip(a, b, strict=True))
den = (sum((x - ma) ** 2 for x in a) * sum((y - mb) ** 2 for y in b)) ** 0.5
return num / den if den else 0.0
def main() -> int:
rows = []
for u in rol.chosen("en"):
uid = u["uid"]
pair = OUT / f"dv-n-re-{uid}.json", OUT / f"dv-n-re-{uid}.БЕЗ-ПРОМТА.json"
if not all(p.exists() for p in pair):
print(f"⛔ нет пары клеток для {uid[:10]} — улика неполна")
return 1
re_, rq_ = (json.loads(p.read_text(encoding="utf-8")) for p in pair)
msgs, _model, _kw = rol.FOREIGN["RQ"]["wire"](uid)
if [m["role"] for m in msgs] != ["system", "system", "user"]:
print(f"⛔ форма вызова не «system·system·user»: {[m['role'] for m in msgs]}")
return 1
rows.append({
"uid": uid[:10],
"re": re_["prompt_tokens"], "rq": rq_["prompt_tokens"],
"sys0": len(msgs[0]["content"]), "sys1": len(msgs[1]["content"]),
"user": len(msgs[2]["content"]),
})
print(f"{'глава':12}{'RE_pt':>7}{'RQ_pt':>7}{'RE-RQ':>7}{'sys0':>7}{'sys1':>6}{'user':>7}")
for r in rows:
print(f"{r['uid']:12}{r['re']:>7}{r['rq']:>7}{r['re'] - r['rq']:>7}"
f"{r['sys0']:>7}{r['sys1']:>6}{r['user']:>7}")
bad = 0
sys0 = {r["sys0"] for r in rows}
sys1 = {r["sys1"] for r in rows}
diff = {r["re"] - r["rq"] for r in rows}
def ck(ok: bool, what: str) -> None:
nonlocal bad
print(f"[{'OK ' if ok else 'ПРОВАЛ'}] {what}")
bad += 0 if ok else 1
ck(len(sys0) == 1, f"ролевой промт постоянен по главам: вариантов длины {len(sys0)} ({sys0})")
ck(len(sys1) > 1, f"глоссарий по главам РАЗНЫЙ: вариантов длины {len(sys1)}")
ck(len(diff) == 1, f"разность RERQ постоянна: вариантов {len(diff)} ({diff})")
d = next(iter(diff)) if len(diff) == 1 else -1
tok_per_char = d / next(iter(sys0)) if len(sys0) == 1 and d > 0 else 0.0
c_gl = _corr([float(r["rq"]) for r in rows], [float(r["sys1"]) for r in rows])
c_src = _corr([float(r["rq"]) for r in rows], [float(r["user"]) for r in rows])
ck(c_gl > c_src, f"остаток RQ идёт за ГЛОССАРИЕМ, а не за исходником: "
f"corr {c_gl:+.2f} против {c_src:+.2f}")
print(f"\nвыброшено {d} токенов на {next(iter(sys0))} знаков ролевого промта "
f"= {tok_per_char:.3f} ток/знак русского текста")
print("⇒ ВЫЖИВАЕТ ПОСЛЕДНЕЕ СИСТЕМНОЕ, ВЫБРАСЫВАЕТСЯ ПЕРВОЕ."
if not bad else "\n⛔ ВЫВОД НЕ ДЕРЖИТСЯ — пере-открывать, а не подгонять.")
print("⚠ Это утверждение о ПОВЕДЕНИИ СЛОЯ на дату покупки клеток (21.08.2026). "
"Слаг ≠ модель, поведение слоя тоже имеет срок годности.")
return 1 if bad else 0
if __name__ == "__main__":
sys.exit(main())