Freeze phase 2: panel composition, noise-floor design, declared contrast family and criteria, before its first purchase

This commit is contained in:
Claude (backend session) 2026-08-08 19:54:07 +03:00
parent d123526cd5
commit 43381fe766
3 changed files with 195 additions and 8 deletions

View file

@ -209,3 +209,177 @@ OpenAI (Cloudflare 403), xAI (308→403; Wayback свежее эксп-21 не
`eval/tenant_panel/` целиком (`material.py` · `checks21.py` · `roster.py` · `money.py` ·
`prompts.py` · `bank.py` · `contam.py` · `screen.py`) + эта секция отчёта. Дальнейшие фазы
получают свои секции и свои фриз-коммиты; amend фриза запрещён, девиации объявляются в отчёте.
---
## §1 Ф0 — ИСПОЛНЕНО
### 1.1 Контаминация среза: чисто на двух моделях при живом положительном контроле
Проба узнавания + клоуза, 5 отрывков на книгу, две модели (`tp0-cont-*`, $0.005709):
```
книга модель узнано клоуз вердикт (порог: узнавание ≥3/5)
slice22 gpt-5.6-luna 0/5 0/5 следов нет
slice22 deepseek-v4-flash 0/5 0/5 следов нет
gu-zhenren gpt-5.6-luna 1/5 0/5 следов нет
gu-zhenren deepseek-v4-flash 3/5 3/5 НЕПРИГОДЕН
jinpingmei gpt-5.6-luna 4/5 4/5 НЕПРИГОДЕН
jinpingmei deepseek-v4-flash 5/5 5/5 НЕПРИГОДЕН
```
**Материал среза чист по объявленному порогу**, и проба при этом работает: положительный контроль
опознан 4/5 и 5/5.
**Находка, которой у эксп-21 не было: КОНТАМИНАЦИЯ ЗАВИСИТ ОТ МОДЕЛИ.** На 蛊真人 `luna` даёт 1/5
(«следов нет»), а `deepseek-v4-flash` — 3/5 узнавания И 3/5 клоуза, то есть знает конкретику.
Эксп-21 мерил ОДНОЙ моделью и оставил это открытым пунктом (§4 п.9); здесь видно, что одна модель
занижает оценку — и занижает её как раз для той модели, которая населяет черновую роль. Практика:
пробу контаминации гонять на ЖИЛЬЦАХ, а не на удобной дешёвой модели.
### 1.2 Банк среза
Терминолог-прогон (`gpt-5.6-luna`, $0.002444) по 26 частотным кандидатам майнинга → 26 строк
`термин ⇥ перевод`. Ручная правка сессии: **выброшены четыре** (`瞬间` «мгновенно», `声音` «голос`,
`仿佛` «словно», `庭院` «двор») — это общеязыковая лексика, канон на неё превратил бы метрику
покрытия в замер словарного совпадения. Остальные 22 приняты как есть, ручных ПОДМЕН нет.
Банк: **22 термина, знаменатель 1124 упоминания на 16 единицах** (у эксп-21 — 8 терминов,
228 упоминаний). Помечен `signed: false` — ручной канон сессии, **НЕ подпись владельца** (D39.47).
⚠ Девиация: регексы канонных форм расширены под русское словоизменение ПОСЛЕ фриза Ф0 и ПОСЛЕ
покупки терминолога (то есть $0 и на результат прогона не влияет). Причина — короткие склоняемые
слова («род Цинь») не находились в форме «рода Цинь»: метрика занижалась бы у всех армов
одинаково, но занижалась. Регрессии на реальные формы банка — в `bank.py --selfcheck`.
### 1.3 Sol-пакет (а) выпущен
20 пакетов (`~/books/tenant-panel/sol/`), парный протокол, слепые метки, ОБА порядка, две
контрольные пары с посаженной деградацией, инструкция владельцу отдельным файлом. Контрасты и
основание отбора — в шапке `sol.py`: `A/F` (заказан промтом), `B/F` (тот же заголовочный вопрос,
разрыв судей 1.90) и `A_law/A` — единственный контраст, где судьи расходятся **знаком**
(+0.91 против 0.20), а расхождение знака меняет вердикт, а не точность.
## §2 Ф1 — ПРОФИЛЬ-СКРИН: ЧТО ПОКАЗАЛ И ЧТО ПОЧИНИЛ
### 2.1 Таблица по КАЖДОМУ кандидату — и вошедшему, и нет
```
модель вердикт $/ед transl $/ед edit ханьцзы размышл причины
deepseek-v4-flash прошёл 0.00089 0.00264 14 21.4%
deepseek-v4-pro прошёл 0.00384 0.00545 6 15.7%
gpt-5.6-luna прошёл 0.00367 0.00437 16 5.0%
gemini-3.1-flash-lite прошёл 0.00398 0.00490 12 0.0%
glm-4.7 прошёл 0.00656 0.00853 6 0.0%
glm-5 прошёл 0.00959 0.01303 4 0.0%
grok-4.3 прошёл 0.00984 0.01452 0 20.9%
kimi-k2.6 ПРОВАЛ 0.05661 0.06773 0 100.0% Г1 (3 клетки) · Г5 обе роли · Г6 пустой выход
mistral-large-latest ПРОВАЛ 0.02016 0.02661 0 0.0% Г5 цена translator $0.0202 > $0.02
gemini-3.6-flash ПРОВАЛ 0.07320 0.06354 0 0.0% Г5 цена обеих ролей
```
**Скрин убил 3 из 11** — против 2 из 12 у эксп-21, и убил по СОДЕРЖАТЕЛЬНЫМ порогам, а не по
удобству состава.
**`mistral-large-latest` провалился с запасом в 1%** ($0.02016 против порога $0.02). Порог
объявлен до замера и не двигается, но честная форма вердикта — «на границе», а не «дорог».
**`kimi-k2.6` — единственный, кто отдал ПУСТОЙ выход**: 3 клетки из 4 пришли `finish=length`
с нулевым `content` при 100% доли размышления, каждая по $0.0650.070. Это ровно тот класс,
который уже стоил проекту волны (quirks §10), и он воспроизвёлся на новом вендоре.
### 2.2 Три починки, найденные ИСПОЛНЕНИЕМ по ходу Ф1
| Что сломалось | Как поймано | Правка |
|---|---|---|
| **эхо-мина на ПЕРВОЙ же покупке**: черновик единицы `474f822806` вернулся полным китайским исходником (`finish=stop`, ханьцзы 1785, кириллица 0.00) | вскрытие единицы глазами сразу после покупки | в скрин добавлен боевой гейт годности + N ре-генов; цена ре-гена ложится в леджер |
| **касса считала цену по таблице эксп-21**: `buy.priced` звал `role_topology/prices.cost`, где новых моделей нет — прогон упал `KeyError: kimi-k2.6` на середине | падение прогона | прайс-таблица пака подставлена в кассу явно; заведена проверка «цена считается для ВСЕХ моделей ростера»; сверка уже купленных записей новой таблицей — **0 расхождений** |
| **детектор markdown ловил разделитель сцены**: `***` (стандартный русский типографский разделитель) считался протёкшей разметкой ⇒ ПРОВАЛ получали `glm-5` и `gemini-3.1-flash-lite` | вскрытие клеток перед выводом (D39.61) | детектор приведён к БОЕВОМУ (`checks/sanitizer.go` + D33.2: декоративный разделитель не заголовок); порог «нарушений более чем в одной клетке» НЕ менялся — менялось, что считается нарушением |
⚠ Третья правка сделана ПОСЛЕ того, как первые вердикты были видны. Это признак подгонки, и
защита от него одна: правка меняет ДЕТЕКТОР, а не ПОРОГ; она приводит его к боевому гейту, а не
к желаемому составу; и она проверена регрессиями на девяти входах с известным ответом
(`***`, `* * *`, `# # #`, `## ***`, `**жирный**`, `### Глава 1`, …).
### 2.3 Провод-факты для quirks
* **`gemini-3.1-pro-preview` отдаёт HTTP 503 «This model is currently experiencing high demand»
сериями.** Четыре вызова подряд — четыре 503 за 35 секунд, $0; через минуту тот же слаг
ответил с первой попытки. Страница статуса Google инцидентов не показывала. ⇒ адаптер обязан
иметь отступ на 5xx, иначе клетка теряется по причине, которая замером не является.
* **`kimi-k2.6` без ручки эффорта выжигает бюджет**: 3 клетки из 4 `finish=length`, `content`
пуст, 100% выхода — размышление. Ручки управления размышлением у Kimi в quirks не описано.
* **`gemini-3.6-flash` дороже в роли ПЕРЕВОДЧИКА ($0.0732), чем в роли РЕДАКТОРА ($0.0635)** —
при том, что редакторский вход длиннее вдвое. То есть размышление у него растёт на задаче
перевода с нуля, а не на объёме входа.
* **DeepSeek объявил на прайс-странице повышение цен** («We plan to raise the overall pricing for
DeepSeek API services in the near future, with a significant increase expected»). Проекция
себестоимости книги в ИТОГе едет с этой оговоркой.
---
## §0-Ф2 ПРЕ-РЕГ ФАЗЫ 2 (зафризен своим коммитом ДО покупок Ф2)
**Панель (6 клеток = якорь + 5 жильцов, потолок промта «до 5 + якорь»).** Правило состава
объявлено в §0.6 и применено механически: прошедших скрин кандидатов черновой роли шесть, слотов
пять, поэтому сохранена ЦЕНОВАЯ ОСЬ — обязательны якорь, самый дешёвый и самый дорогой прошедшие,
середина добирается по возрастанию цены.
```
D0 deepseek-v4-flash $0.00089/ед ЯКОРЬ (боевой черновик)
D1 gpt-5.6-luna $0.00367/ед самый дешёвый прошедший
D2 deepseek-v4-pro $0.00384/ед
D3 gemini-3.1-flash-lite $0.00398/ед
D4 glm-4.7 $0.00656/ед
D5 grok-4.3 $0.00984/ед самый дорогой прошедший
```
**Снят правилом:** `glm-5` ($0.00959/ед, скрин ПРОШЁЛ) — шестой по цене из шести, вытеснен
границей «до 5 жильцов». Он не выбывает из пака: в Ф3 он входит как ратифицированный резерв.
Ценовая ось панели — **11× от края до края**, то есть вопрос «покупает ли дорогой черновик
качество» на ней задаваем.
**Что покупается:** черновик каждого жильца на 16 zh + 6 en единицах (с банкнотой на zh; пара en
идёт БЕЗ блока — подписанного глоссария для книги Kristoff не существует, девиация унаследована
от эксп-21) + боевой редактор `A_law`-конфига (`deepseek-v4-pro`, full-regen + закон-блок) поверх
КАЖДОГО черновика + шумовой пол.
**ШУМОВОЙ ПОЛ.** Якорный арм гоняется ДВАЖДЫ end-to-end (`p1`, `p2`) на тех же 16 zh-единицах:
свой черновик и своя редактура в каждом прогоне. Истинная разница — ноль по построению, поэтому
измеренный разброс есть шум ПАЙПЛАЙНА (генерация черновика + генерация редактуры + судья), а не
только редактора: у эксп-21 пол мерил повтор редактора над замороженным черновиком и потому был
нижней границей. Пол применяется к Ф2 И Ф3; перевес ниже пола печатается вердиктом
**«ниже порога различимости»** — это статус «не разрешено ригом», запрета печатать числа нет.
⚠ Пол кладётся ОТДЕЛЬНОЙ парой `p1`/`p2`, а не «боевая клетка + её повтор»: иначе одна половина
пола побайтно совпала бы с армом `E0` в том же судейском пакете, судья увидел бы один текст
дважды, а контроль «близнец» сработал бы на боевом арме.
**СЕМЕЙСТВО КОНТРАСТОВ Ф2 (zh и en раздельно), объявляется ЗДЕСЬ и впечатывается в ключ судейского
прохода ДО первого ответа:**
```
E1 vs E0 · E2 vs E0 · E3 vs E0 · E4 vs E0 · E5 vs E0 — финал поверх черновика жильца против
финала поверх якоря (5 гипотез)
E0 vs D0 — КОНТРОЛЬ: покупает ли боевой редактор
поверх якорного черновика
```
Поправка Холма — по шести. Контрасты `D_i vs D_0` (черновик как ТЕКСТ) и разложение по осям
объявляются **описательными**: поправку не несут и решений не определяют. Основание такого сужения
— продуктовое: решение владельца принимается по ФИНАЛУ (что уедет в книгу), а не по черновику;
эксп-21 §9 показал, чем кончается семейство, объявленное задним числом.
**КРИТЕРИИ (объявляются ДО покупок, как требует промт).**
* «X бьёт Y» засчитывается, если перевес **больше шумового пола** (порог различимости = 2.8·SE
пола при 80% мощности) И `p` Холма < 0.05 по объявленному семейству.
* Перевес ниже пола печатается как **«ниже порога различимости»** с числом, а не замалчивается.
* **Правило ничьей (ратифицировано D39.117): при неразличимости рекомендация = самый дешёвый
жилец.** Это правило ОТЧЁТА, а не ожидание результата.
* Детерминированные оси (эхо, батарея, покрытие канона, цена) читаются НЕЗАВИСИМО от судьи: они
шума не имеют, и расхождение с судейским порядком — находка (строка 152 бэклога).
**СМЕТА Ф2 по фактическим ценам скрина: $1.4533** (черновики 132 вызова + редактуры 132 + пол 32)
при потолке $2.00. Не влезет — стоп и пинг, не резать молча.
**АГЕНТ-ЗАПУСКИ Ф2:** 16 (zh, по одной единице на сессию: 6 черновиков + 6 редактур + декой +
пара пола = 14 текстов) + 3 (en, по две единицы: 6 черновиков + 6 редактур + декой = 13 текстов).

View file

@ -57,18 +57,31 @@ def client(model: str) -> OpenAI:
return OpenAI(api_key=os.environ[env], base_url=base, timeout=900)
def panel() -> list[str]:
"""R0 = боевой редактор · затем обязательный кандидат · затем прошедшие скрин по цене.
RESERVE = "glm-5" # ратифицированный резерв D39.22 = арм B эксп-21
Порядок фиксирован, потому что имена армов (R0, R1, ) впечатываются в ключ судейского
прохода: сдвиг порядка после выпуска заданий сделал бы семейство неповторимым.
def panel() -> list[str]:
"""Состав панели Ф3 по ОБЪЯВЛЕННОМУ (во фризе Ф3, ДО покупок) правилу.
R0 боевой редактор база всех контрастов;
R1 обязательный кандидат промта (гипотеза эксп-04 «gemini-pro аутлаер прозы»);
R2 резерв эксп-21 `glm-5` ПРЕЕМСТВЕННОСТЬ: только он позволяет пере-снять контраст
`A_law/B` на неконтаминированном материале, а без него эксп-22 не связан с эксп-21 ничем;
R3 самый ДЕШЁВЫЙ прошедший редактор отвечает «хватает ли дешёвого»;
R4 самый ДОРОГОЙ прошедший отвечает «покупает ли дорогой качество».
Порядок фиксирован: имена армов R0..R4 впечатываются в ключ судейского прохода, и сдвиг
порядка после выпуска заданий сделал бы семейство неповторимым.
"""
res = P2.screen_result()
out = [BATTLE, MANDATORY]
out = [BATTLE, MANDATORY, RESERVE]
ok = [m for m, r in res.items()
if r["verdict"] == "прошёл" and m not in out and r.get("price_editor") is not None]
ok.sort(key=lambda m: res[m]["price_editor"])
return (out + ok)[:MAX_EDITORS]
for m in ([ok[0]] if ok else []) + ([ok[-1]] if len(ok) > 1 else []):
if m not in out:
out.append(m)
return out[:MAX_EDITORS]
def base_text(uid: str) -> str:

View file

@ -84,11 +84,11 @@ def tenants() -> list[str]:
ok.sort(key=lambda m: res[m]["price_translator"])
if ANCHOR in ok:
ok.remove(ANCHOR)
if len(ok) <= MAX_TENANTS - 1:
if len(ok) <= MAX_TENANTS:
return [ANCHOR, *ok]
keep = [ok[0], ok[-1]]
for m in ok[1:-1]:
if len(keep) >= MAX_TENANTS - 1:
if len(keep) >= MAX_TENANTS:
break
keep.append(m)
return [ANCHOR, *sorted(keep, key=lambda m: res[m]["price_translator"])]