Freeze two ablation variants with the verification tail cut from both and opt-in trace capture

This commit is contained in:
Claude (backend session) 2026-08-20 18:52:19 +03:00
parent eb49b651d4
commit 3075566104
2 changed files with 102 additions and 5 deletions

View file

@ -119,8 +119,84 @@ PAIRS["zh"]["restore"] = None
# первую клетку второй раз.
ARMS = {"RN": ("deepseek-v4-pro", 1), "RN2": ("deepseek-v4-pro", 2),
"RG": ("glm-5", 1), "RK": ("grok-4.3", 1),
"RA": ("deepseek-v4-pro", 1)} # тот же промт, ВОССТАНОВЛЕННЫЕ абзацы исходника
"RA": ("deepseek-v4-pro", 1), # тот же промт, ВОССТАНОВЛЕННЫЕ абзацы исходника
"RC": ("deepseek-v4-pro", 1), # промт МИНУС рамка оценки, см. CUT
"RB": ("deepseek-v4-pro", 1)} # рамка оценки ПЕРЕВЁРНУТА: брак = чужой язык
RESTORE_ARMS = ("RA",)
# ⚠ РЕЗАНЫЕ ВАРИАНТЫ ПРОМТА. Замер `RN` дал ТРИ независимых показания одного сдвига: размышление
# ×28 против прежней склейки при входе ×1.17, слепое чтение — «самый точный по деталям, но проза
# самая серая: гладкопись подстрочника», перевёрстка 1.60 против 1.771.79 у обеих старых схем.
# Модель стала аккуратнее и трусливее по всем осям сразу.
# Гипотеза: пять напечатанных осей, «по которым проверят результат», заставляют оптимизировать
# ЧЕК-ЛИСТ, а не текст — держаться ближе к букве есть самый дешёвый способ не нарушить пять
# критериев разом. Ровно этот отказ предсказывали и приёмка Fable-5, и документация Anthropic
# (перенесённые инструкции самопроверки дают избыточную проверку).
# ⚠ РЕЖЕТСЯ РОВНО ОДИН БЛОК, остальное побайтно то же — иначе замер не скажет, что подействовало.
# Селфтест это ПРОВЕРЯЕТ: разница промтов обязана быть равна вырезанному куску и ничему больше.
# ⚠ ПЕРВАЯ РЕДАКЦИЯ РЕЗА БЫЛА НЕГОДНОЙ, и поймала её проверка исполнением ДО покупки: вырезание
# блока целиком уносило 1871 знак — вместе с ним исчезали ВСЕ запреты и рамка РЕГИСТРА, лучшая
# часть промта по мнению приёмки. Это был бы замер «промт без правил», а не «промт без чек-листа».
# ⇒ Режется РОВНО РАМКА ОЦЕНКИ, содержание остаётся побайтно: «по чему будет проверен результат,
# это же и есть список ошибок» → «чего нельзя». Одна переменная: знает ли модель, что её ОЦЕНЯТ
# по перечню. Убирается и мета-строка про «вольность ошибкой не считается» — она сама ссылается
# на оценку и без рамки повисает.
# ⚠ RB — ВТОРАЯ ФОРМУЛИРОВКА, И ОНА ОТ ВЛАДЕЛЬЦА. Разбор Fable-5 показал, ПОЧЕМУ экзамен
# переиграл свободы: все пять осей сформулированы как оси ИЗБЕГАНИЯ, и серая проза проходит все
# пять — «ни кальки, ни канцелярита» гладкий подстрочник проходит, факт/полноту/канон проходит
# лучше всех. Весь риск объявленного экзамена лежал на смелости и ни грамма на осторожности,
# значит держаться буквы было доминантной стратегией.
# Лечение — не убрать экзамен, а ПЕРЕВЕРНУТЬ его риск. Формулировка владельца 20.08 дословно:
# «не прям серость, я бы сказал присутствие одного языка в другом. То есть полное отсутствие
# художественности и родного звучания текста. Машинный перевод грубо говоря. Всё же перевод это
# отчасти какое-то но творчество, без перевирания смысла нужно написать текст, чтоб он звучал
# естественно». Это уточняет его же решение 16.08 («штрафовать только за перевирание смысла»):
# вторым браком объявляется не вольность, а ПРОСВЕЧИВАЮЩИЙ ИСХОДНЫЙ ЯЗЫК.
_BRAK = (
"БРАКУЮТСЯ ДВА ИСХОДА, и второй так же строго, как первый.\n"
"Первый — перевранный смысл: факт, смысловой ход, канон.\n"
"Второй — текст, сквозь который просвечивает исходный язык: чужой синтаксис, переведённо\n"
"звучащая фраза, отсутствие живого русского. Машинный перевод бракуется наравне с враньём.\n"
"Перевод — отчасти творчество: не переврав смысла, напиши так, чтобы звучало по-русски\n"
"естественно. Вольность не проверяет никто.\n\n"
"Ниже — то, что относится к первому браку и к границам второго:")
# ⚠⚠ ХВОСТ РЕЖЕТСЯ В ОБОИХ АРМАХ, И ЭТО ПОЧИНКА ДИЗАЙНА, А НЕ ФОРМУЛИРОВКИ. Приёмка Fable-5
# поймала: после того как императив самопроверки оправдан замером (его не было на момент покупки),
# ведущим подозреваемым становится ХВОСТОВАЯ СТРОКА «не нарушает ни одной из осей» — единственное
# оставшееся место, где смелость ОБУСЛОВЛЕНА полной верификацией пяти осей. В первой редакции
# обоих резов она стояла НЕТРОНУТОЙ, то есть при исходе «оба не сработали» вывод «рамка ни при
# чём» был бы неинтерпретируем: ведущий подозреваемый не вырезан нигде.
_TAIL_OLD = ("Вольность в пределах этих пяти ошибкой НЕ СЧИТАЕТСЯ. Текст, который читается лучше "
"и не нарушает\nни одной из осей, лучше текста, который следует за буквой.")
# ⚠ Формулировка второго брака — СЛОВА ВЛАДЕЛЬЦА 20.08: «присутствие одного языка в другом …
# полное отсутствие художественности и родного звучания … машинный перевод грубо говоря … перевод
# это отчасти какое-то но творчество, без перевирания смысла нужно написать текст, чтоб он звучал
# естественно». Правки по приёмке: убран дубль «так же строго»/«наравне с враньём»; «отсутствие
# живого русского» → «мёртвый русский» (проверка СВОЙСТВА, а не отсутствия — последняя глобальна
# по построению); строка-мост исправлена (ЯЗЫК и РЕГИСТР — СУЩЕСТВО второго брака, а не «границы»).
_BRAK = (
"БРАКУЮТСЯ ДВА ИСХОДА, и второй так же строго, как первый.\n"
"Первый — перевранный смысл: факт, смысловой ход, канон.\n"
"Второй — текст, сквозь который просвечивает исходный язык: чужой синтаксис, переведённо\n"
"звучащая фраза, мёртвый русский. Машинный перевод — брак, даже когда в нём всё верно.\n"
"Перевод — отчасти творчество: не переврав смысла, напиши так, чтобы звучало по-русски\n"
"естественно. Вольность не проверяет никто.\n\n"
"Первому браку служат СМЫСЛ, ПОЛНОТА и КАНОН; второму — ЯЗЫК и РЕГИСТР:")
CUT = {
# RB: риск ПЕРЕВЁРНУТ. Хвост заменён не условием, а ПЕРЕОПРЕДЕЛЕНИЕМ: смелость перестаёт быть
# разрешённым исключением и становится проверяемым требованием.
"RB": [("ПО ЧЕМУ БУДЕТ ПРОВЕРЕН РЕЗУЛЬТАТ. Это же и есть полный список того, что считается "
"ошибкой —\nдругих запретов нет:", _BRAK),
(_TAIL_OLD,
"Живая фраза вместо буквы — не вольность, а исполнение второго требования.")],
# RC: экзамена нет вовсе. Хвост тоже без условия верификации и без слова «оси», которое в RC
# повисало бы без референта — выше ни одна вещь осью не названа.
"RC": [("ПО ЧЕМУ БУДЕТ ПРОВЕРЕН РЕЗУЛЬТАТ. Это же и есть полный список того, что считается "
"ошибкой —\nдругих запретов нет:", "ЧЕГО НЕЛЬЗЯ:"),
(_TAIL_OLD,
"Всё, что за пределами этих пяти, — твоя воля; живая фраза вместо буквы — норма, "
"а не риск.")],
}
FREE = ("ZD", "Z0", "ZP")
PRIMARY = ("RN", "ZP") # единственный несущий контраст, см. шапку
@ -155,7 +231,7 @@ def pair_blocks(p: str) -> tuple[str, str]:
return _block(tr_core, UNITS_HEAD), _block(ed_core, REFLOW_HEAD)
def rol_msgs(p: str, src: str) -> list[dict]:
def rol_msgs(p: str, src: str, arm: str = "RN") -> list[dict]:
"""Однопроходка НОВЫМ промтом: роль, рамки, регистр, критерии проверки, пар-блоки, глоссарий."""
cfg = PAIRS[p]
cfg["wire"]()
@ -165,6 +241,11 @@ def rol_msgs(p: str, src: str) -> list[dict]:
raise SystemExit(f"у пары {p} нет блока «{UNITS_HEAD}» либо «{REFLOW_HEAD}» — "
"промт собрать нечем, арм не строится")
core, _few, user = PR.load_template(CORE)
for was, now in CUT.get(arm, []):
if was not in core:
raise SystemExit(f"у арма {arm} не найден заменяемый кусок «{was[:40]}…» — "
"промт изменился под замером, СТОП")
core = core.replace(was, now)
core = (core.replace("{{ПАР_ЕДИНИЦЫ}}", units_blk)
.replace("{{ПАРЕРЕВЁРСТКА}}", reflow_blk))
brief = getattr(PR, PR.PAIR_BRIEF[key])
@ -359,12 +440,19 @@ def call_kwargs(model: str, msgs: list[dict]) -> dict:
# ── ПОКУПКА ──────────────────────────────────────────────────────────────────────────────────
# ⚠ АБЛЯЦИИ НЕ НУЖНА ВСЯ ПАНЕЛЬ. Эффект, который ищется, огромен (×28 размышления), и виден он
# на трёх главах; тратить на него шестнадцать значит выбрать между двумя вариантами вместо того,
# чтобы купить оба. Ограничитель берёт ПЕРВЫЕ N глав отбора — тот же детерминированный порядок,
# значит абляция сравнима с уже купленным `RN` глава в главу.
N_LIMIT = next((int(a.split("=", 1)[1]) for a in sys.argv if a.startswith("--n=")), 0)
def buy(p: str, which: list[str]) -> None:
ZK.price_gate()
cfg = PAIRS[p]
cfg["wire"]()
ZK.preflight([ARMS[a][0] for a in which], cfg["phase"], cfg["letter"])
us = chosen(p)
us = chosen(p)[:N_LIMIT] if N_LIMIT else chosen(p)
led = MONEY.Guarded(cfg["phase"], default_expect=0.03)
print(f"\nпара {p} · глав {len(us)} · армы {' '.join(which)} · касса {cfg['phase']}")
for arm in which:
@ -380,7 +468,7 @@ def buy(p: str, which: list[str]) -> None:
# часами, и пересёкший 16:00 UTC цикл писал бы вызовы по вчерашнему прайсу.
ZK.price_gate(quiet=True)
rec = MONEY.purchase(led, tg, model, ED22.client(model),
call_kwargs(model, rol_msgs(p, src_of(p, arm, u))),
call_kwargs(model, rol_msgs(p, src_of(p, arm, u), arm)),
role="onepass", uid=u["uid"], arm=arm, gen=gen, pair=p,
podacha="абзацы" if arm in RESTORE_ARMS else "плоско")
if rec.get("skipped"):

View file

@ -19,6 +19,7 @@ from __future__ import annotations
import json
import time
import os
from pathlib import Path
from prices import cost
@ -78,7 +79,15 @@ def usage_of(u, ch) -> dict:
return dict(prompt_tokens=u.prompt_tokens or 0, cached_tokens=cached,
completion_tokens=u.completion_tokens or 0, reasoning_tokens=reasoning,
total_tokens=getattr(u, "total_tokens", 0) or 0,
reasoning_chars=len(getattr(msg, "reasoning_content", None) or ""))
reasoning_chars=len(getattr(msg, "reasoning_content", None) or ""),
# ⚠ САМ ТРЕЙС РАЗМЫШЛЕНИЯ — ТОЛЬКО ПО ЯВНОМУ ФЛАГУ, и это не осторожность, а вес:
# у dspro он бывает 30+ тыс. знаков на клетку, то есть удваивает файл сырья, а
# нужен он редко. Провайдер отдаёт его в ответе БЕСПЛАТНО, но `usage_of` до 20.08
# сохранял одну ДЛИНУ — и когда замер промта дал ×28 размышления, прочитать,
# на что оно потрачено, оказалось нечем: пришлось бы пере-покупать клетки.
# Флаг `TM_KEEP_TRACE=1` включает сохранение для абляционных прогонов.
**({"reasoning_text": getattr(msg, "reasoning_content", None) or ""}
if os.environ.get("TM_KEEP_TRACE") == "1" else {}))
def priced(model: str, us: dict) -> float: