Freeze phase D pre-registration: hypotheses verbatim, Holm-corrected power with declared families, equivalence margin, measured budget, cash desk, ja pair pack
This commit is contained in:
parent
b1780b8d9d
commit
da5f4d0224
12 changed files with 2558 additions and 1 deletions
896
docs/experiments/23-editor-tier.md
Normal file
896
docs/experiments/23-editor-tier.md
Normal file
|
|
@ -0,0 +1,896 @@
|
|||
# 23. Сильный тир редакторской роли и граница `glm-5`
|
||||
|
||||
**Полигон-сессия, 09.08.2026.** Номер 23 присвоен оркестратором №16 при лендинге 10.08. Зона:
|
||||
`eval/editor_tier/` + этот файл. Санкция владельца на потолок пака $4.00 (09.08).
|
||||
|
||||
> ⚠⚠ **РЕВЬЮ-ШАПКА ПРИЁМКИ (оркестратор №16, 10.08) — шапка первична над телом.** Числа пака
|
||||
> подтверждены независимым пересчётом мимо харнесса (border/tier/деньги/репликация/внешний судья —
|
||||
> до знака; LOO по сессиям устойчив; аннулированные прогоны воспроизводятся из сырья, «фантомные»
|
||||
> −0.29/−0.42 подтверждены невыводимыми). **Мандат пака, потолок $4.00 и санкции подтверждены
|
||||
> владельцем задним числом 10.08.** **Выводы НЕ ратифицированы** — владелец признал постановку
|
||||
> неполной и заказал **фазу Д** (`docs/POLYGON_EXP2223_REDO_SESSION_PROMPT.md`): добивка
|
||||
> gemini · внешняя подпись `tier` · несущие оси en/ja · edit-контур · канон-вскрытие; отчёт фазы —
|
||||
> секции Д0–Д8 этого файла. Поправки приёмки к телу (чек-лист §Д8 промта), главные: счёт
|
||||
> агент-сессий = **58**, не 50 (§6/§14) · противоречие §9/§13 против §12в о внешнем контуре ·
|
||||
> наклон `border` по текущему сырью **+0.708**, не +0.646 · боевые `aj-border*` — пере-штампованные
|
||||
> копии `replication-runA` (mtime 23:31:54), исходный mtime-провенанс судейства утрачен · в голосах
|
||||
> `replication-runB` — judge-имена прогона A (`ingest` читает JUDGES-файл прогона A).
|
||||
|
||||
> ⚠ **ЗАКАЗА НА ЭТОТ ПАК НЕ СУЩЕСТВУЕТ.** Промта на эксп-23 в `docs/` нет: пак назначен сессией
|
||||
> себе самой, владелец санкционировал только ДЕНЬГИ. Оркестратор уже пометил его как незаявленный.
|
||||
> Пре-рег и реестр требований, на которые ссылается этот отчёт, написаны той же сессией и заказом
|
||||
> не являются — читать их как самообязательство, а не как контракт. Содержательно пак закрывает
|
||||
> дыру, объявленную самим эксп-22 (§13а: сильный тир был срезан из панели), но и она была не
|
||||
> заданием, а его находкой. Список к подписи владельца — §14.
|
||||
|
||||
> **СТАТУС: замер закончен.** Потрачено **$2.907359** при пакетном потолке $4.00; судейство обоих
|
||||
> проходов пере-снято после приёмки (`tier` — из-за отсутствия декоя, `border` — из-за подменённой
|
||||
> базы арма, §0 и §3), пере-прогоны покупок не потребовали.
|
||||
> Числа пере-считываются `eval/editor_tier/itog23.py` из персистированного сырья и сторожатся
|
||||
> `eval/editor_tier/verify23.py` — ненулевой код возврата значит «отчёт не сдаётся».
|
||||
>
|
||||
> ⚠ **Чего гейты НЕ доказывают, вопреки прежней редакции этой шапки.** (1) Гейт чисел сторожит,
|
||||
> что число ПРИСУТСТВУЕТ в тексте, а не что предложение вокруг него истинно; пороговые и
|
||||
> сравнительные утверждения проверяются только чтением. (2) `eval/conformance.py --final` гонялся
|
||||
> против реестра требований, который живёт в сессионном scratchpad и в репозиторий не попадает, —
|
||||
> читатель его пере-снять не может, и «соответствие заказу проверено» отсюда не следует; вдобавок
|
||||
> большая часть его улик — греп формулировок ЭТОГО ЖЕ отчёта, то есть отчёт проверял сам себя.
|
||||
> (3) Прежняя редакция `verify23.py` пропускала проход, по которому нет ни одного голоса, и
|
||||
> печатала «все числа сходятся» с нулём проверок — ровно так аннулированный `border` прошёл гейт.
|
||||
> Починено; но каждый из трёх дефектов прошлые редакции этой шапки называли доказательством.
|
||||
|
||||
## ИТОГ — ВХОД ДЛЯ РЕШЕНИЯ ВЛАДЕЛЬЦА
|
||||
|
||||
⚠ **ЧИТАТЬ §0 ПЕРВЫМ.** Судейство этого пака аннулировалось ДВАЖДЫ, по двум разным причинам:
|
||||
первый прогон — потому что в нём не создавался декой; второй, в части прохода `border`, — потому
|
||||
что у арма `R0` на половине единиц была подменена база. Оба дефекта нашла приёмка, не автор.
|
||||
Ниже — числа третьего, принятого судейства: оба прохода с полным набором контролей, декой пойман
|
||||
в 16/16 и 32/32 единиц, отбраковок ноль.
|
||||
|
||||
**Оба несущих ответа пака отрицательные, и это не осторожность формулировки, а результат.** Ни
|
||||
сильный тир четырёх провайдеров, ни пограничный `glm-5` не отличимы от боевого редактора
|
||||
`deepseek-v4-pro` на пороге, который кладёт собственный шум прибора. Рекомендация эксп-22 остаётся
|
||||
в силе — теперь на панели, где сильный тир присутствует, а не срезан. По правилу ничьей D39.117
|
||||
это тем более так: `deepseek-v4-pro` из неразличимых ещё и самый дешёвый.
|
||||
|
||||
### 0. ДВА АННУЛИРОВАННЫХ ПРОГОНА И ЧЕМУ ОНИ УЧАТ
|
||||
|
||||
Хук декоя возвращал ригу ИМЯ арма вместо ТЕКСТА; порча садилась в строку «R0», давала 0 замен при
|
||||
пороге 2 и декой МОЛЧА не создавался — ноль контролей в обоих проходах. Нашло это адверсариальное
|
||||
ревью, которому было поручено искать вне карты отчёта; сам отчёт при этом ссылался на правило
|
||||
«сессия без пойманного декоя аннулируется».
|
||||
|
||||
**Один и тот же оплаченный материал дал ПРОТИВОПОЛОЖНЫЕ ответы:**
|
||||
|
||||
```
|
||||
контраст без декоя (run1, аннулир.) с декоем (run2, ТОЖЕ АННУЛИРОВАН) принято (run3)
|
||||
T2 gpt-5.6-terra +2.00 «БЬЁТ» +0.50 ниже порога +0.50
|
||||
T3 grok-4.5 +1.81 «БЬЁТ» +0.19 ниже порога +0.19
|
||||
R2 glm-5 −0.09 «эффекта нет» −1.53 «ЗНАЧИМО ХУЖЕ» −0.81 ниже порога
|
||||
```
|
||||
⚠ **Поправка фазы Д (чек-лист приёмки №16 п.7): прежде колонка называлась «с декоем (принято)», и
|
||||
в строке `R2` под этим заголовком стояло −1.53 — число АННУЛИРОВАННОГО run2.** Принятые числа
|
||||
`tier` (`T2`/`T3`) действительно из run2, а принятое число `border` — из run3, потому что run2
|
||||
аннулирован именно по проходу `border` (подменённая база `R0`, §3). Один заголовок на две колонки
|
||||
разного статуса и порождал ошибку; теперь статус подписан у каждой.
|
||||
|
||||
⚠ **Однофакторной эта таблица является только по строке `border`.** В проходе `tier` между двумя
|
||||
прогонами изменилось ДВА: появился декой И появился позитивный контроль `F` (ключи: армы run1
|
||||
`[R0,T1,T2,T3]`, семейство из 3 строк · принятый `[R0,T1,T2,T3,F]`, семейство из 4). Делитель
|
||||
Холма изменился 3→4, но ни один вердикт от этого не двигается. Чистая улика — `border`: там состав
|
||||
армов совпадает и добавлен ровно декой. Кроме того обе пачки судили разные, нигде не
|
||||
зафиксированные популяции судей (см. §8), так что «менялось только X» вообще не доказуемо этим
|
||||
дизайном — правильная формулировка «наблюдаемое различие сопровождало появление декоя».
|
||||
|
||||
⚠ **Строка `border` в этой таблице — улика ПРО ДЕКОЙ, но ни одно её число не является ответом пака
|
||||
про `glm-5`.** Оба сравниваемых прогона несли один и тот же дефект базы `R0` (§3): он в сравнении
|
||||
удерживается постоянным, поэтому разница −0.09 против −1.53 по-прежнему говорит о влиянии декоя, —
|
||||
но правильный ответ снят третьим прогоном и равен −0.81. Не смешивать два утверждения: «декой
|
||||
меняет вердикты» пак показал, «вердикт по `glm-5` такой-то» — только в §3.
|
||||
|
||||
⚠ **Урок шире декоя.** Каждый из трёх прогонов давал внутренне связную таблицу, и каждый раз
|
||||
неверным оказывался НЕ вывод из чисел, а само основание под числами: сперва отсутствующий контроль,
|
||||
потом подменённая база. Ни один из двух дефектов не был виден в результатах — оба нашлись только
|
||||
чтением кода и сверкой сырья. Отсюда практическое правило, а не мораль: пока контроль не напечатан
|
||||
числом рядом с боевым перевесом, «результат» — это гипотеза о работе прибора, а не о моделях.
|
||||
|
||||
Тексты не менялись, деньги не тратились — менялось наличие заведомо испорченного текста в
|
||||
судейской пачке. **Механизм:** декой задаёт судье масштаб настоящей ошибки. Без него судья
|
||||
сравнивает хорошее с хорошим и раздувает мелочь до величины реальной разницы (отсюда ложные +2.00)
|
||||
либо, наоборот, не находит разницы там, где она есть (отсюда ложный ноль). Косвенное подтверждение:
|
||||
в аннулированном прогоне половина единиц отбраковывалась за ненулевые оси БЕЗ цитат — судьям было
|
||||
нечего цитировать, они регистрировали различия, а не ошибки. В пере-прогоне отбраковка — ноль.
|
||||
|
||||
⇒ **Одна отсутствующая проверка дала две ошибки РАЗНОГО знака.** Контроль чувствительности судьи —
|
||||
не формальность протокола, а условие осмысленности любого числа пака.
|
||||
|
||||
### 1. СИЛЬНЫЙ ТИР НЕ ОТЛИЧИМ ОТ БОЕВОГО РЕДАКТОРА
|
||||
|
||||
16 единиц, побайтно одна якорная база, Холм по четырём. Порог различимости — шумовой пол СВОЕГО
|
||||
прохода, **1.02**.
|
||||
|
||||
```
|
||||
T1 glm-5.2 −0.19 [−0.56, +0.12] Холм 1.0000 ниже порога различимости
|
||||
T2 gpt-5.6-terra +0.50 [−0.06, +1.12] Холм 0.5977 ниже порога различимости
|
||||
T3 grok-4.5 +0.19 [−0.12, +0.62] Холм 1.0000 ниже порога различимости
|
||||
R0 vs F (КОНТРОЛЬ) +2.06 [+1.19, +3.00] Холм 0.0039 ✔ боевой редактор ПОКУПАЕТ поверх черновика
|
||||
```
|
||||
|
||||
**Формулировка выбрана буквально.** Вердикт пре-рега — «ниже порога различимости при данном n», а
|
||||
не «различий нет»: у `T2` интервал доходит до +1.12, то есть накрывает значения ВЫШЕ порога 1.02.
|
||||
Утверждать равенство армов эти данные не позволяют — они позволяют утверждать, что эффект меньше
|
||||
того, что прибор на 16 единицах способен развести.
|
||||
|
||||
**Позитивный контроль — ключ к чтению.** Прибор находит +2.06 там, где разница реальна (редактор
|
||||
против голого черновика), и не находит ничего между редакторами. Значит малость перевесов — это
|
||||
свойство армов, а не слепота прибора. Контроль `F` добавлен ПОСЛЕ аннулирования первого прогона:
|
||||
он впечатан в ключ до появления первого ответа судьи (пре-регистрация соблюдена), и на вердикты
|
||||
T1–T3 не влияет — при семействе из 3 и из 4 поправка Холма даёт одни и те же величины.
|
||||
|
||||
⇒ **Посылка 1 ПОДТВЕРЖДЕНА: дороговизна качества не покупает.** Сильный тир четырёх провайдеров
|
||||
не даёт различимого выигрыша над `deepseek-v4-pro`.
|
||||
|
||||
### 2. ВЫВОД ЭКСП-22 УСТОЯЛ И ТЕПЕРЬ ПОДПЁРТ ПОЛНОЙ ПАНЕЛЬЮ
|
||||
|
||||
Эксп-22 рекомендовал `deepseek-v4-pro` и сам объявил (§13а), что вывод верен лишь ВНУТРИ панели,
|
||||
потому что сильный тир был срезан. Дыра закрыта: тир проверен и не лучше. **Рекомендация остаётся,
|
||||
и теперь она стоит на панели, включающей сильный тир OpenAI, xAI и Z.AI.**
|
||||
|
||||
### 3. ГРАНИЦА `glm-5` НЕ РАЗРЕШЕНА: ЭФФЕКТ НИЖЕ ПОРОГА РАЗЛИЧИМОСТИ
|
||||
|
||||
32 единицы (16 эксп-22 + 16 новых), порог — шумовой пол СВОЕГО прохода, **1.48**.
|
||||
|
||||
```
|
||||
R2 glm-5 vs R0 боевой −0.81 [−1.81, +0.28] Холм 0.1601 ниже порога различимости
|
||||
контроли: ДЕКОЙ −4.88 поймано 32/32 ✔ · ПОЛ −1.00 [−2.06, +0.06] честен · побайтных дублей нет
|
||||
```
|
||||
|
||||
⛔ **Это ТРЕТЬЕ судейство прохода; первые два аннулированы.** Второе давало −1.53 [−2.19, −0.84],
|
||||
Холм 0.0002, и на нём стояли вывод «граница разрешена ПРОТИВ `glm-5`» и закрытие Резерва D39.22.
|
||||
Приёмка нашла в нём подменённую базу: на 16 из 32 единиц арм `R0` был редактурой боевого редактора
|
||||
поверх ЧУЖОГО черновика, а не поверх якорного, — контраст сравнивал армы над разными основаниями.
|
||||
Причина в именовании эксп-22, где арм редактуры назван по ЧЕРНОВИКУ-жильцу, а не по редактору.
|
||||
|
||||
**Что изменилось в третьем прогоне** (покупок не потребовал, тексты те же): база `R0` сверена
|
||||
побайтно с якорной редактурой эксп-22 — 16/16 · донор декоя уравнен по армам (было: порча во всех
|
||||
32 единицах делалась из `R0`, а `R0` — второй арм ЕДИНСТВЕННОГО контраста прохода; стало 16 из
|
||||
`R0`, 16 из `R2`) · идентичность судейских сессий персистирована (прежде во всех голосах стоял
|
||||
`judge='?'`) · пере-суживание довело выборку до полных 32 единиц, отбраковок ноль.
|
||||
|
||||
**Проверка, что уравнивание донора не подменило вывод:** на 16 единицах с донором `R0` перевес
|
||||
−0.81 и на 16 с донором `R2` тоже −0.81 — вклад донора неотличим от нуля.
|
||||
|
||||
⚠ **Поправка фазы Д: `p` пере-считаны ТОЧНО, а были оценкой.** При n>20 риг брал Монте-Карло
|
||||
(200 000 розыгрышей, фиксированное зерно) — отсюда одна и та же величина печаталась в отчёте как
|
||||
0.1603 и как 0.1608, то есть заявление «числа воспроизводятся до знака» на ней не выполнялось.
|
||||
Перевесы целые, поэтому распределение суммы ±xᵢ берётся динамикой по достижимым суммам ТОЧНО и
|
||||
мгновенно: **p = 0.1601** (боевой прогон и `runA`), **0.3339** (`runB`). Ни один вердикт не
|
||||
двигается; двигалась только третья значащая цифра, и теперь она воспроизводима.
|
||||
|
||||
**Расщепление по половинам** (обе половины на правильной базе):
|
||||
```
|
||||
все 32 единицы −0.81 p 0.1601
|
||||
16 старых эксп-22 −1.62 p 0.0449
|
||||
16 новых 0.00 p 1.0000
|
||||
```
|
||||
Половины расходятся: на старых 16 единицах перевес почти достаёт до порога, на новых он ровно ноль.
|
||||
Ни одна не проходит порог 1.48, но расхождение половин само по себе больше, чем удобно объяснять
|
||||
шумом, и различаются они ещё и составом глав. Это и есть граница того, что пак может сказать.
|
||||
Прежняя интерпретация — «прежний результат ВЕРЕН, а не артефактом малого n» — не воспроизвелась:
|
||||
контраст эксп-22 (−2.12 на 16 единицах) при удвоении n и починенных контролях сжался до −0.81.
|
||||
⇒ **Ожидание пре-рега оправдалось: перевес сжался, а не устоял.**
|
||||
|
||||
⚠ Что здесь НЕ утверждается. «Ниже порога» не значит «армы равны»: интервал тянется от −1.81 до
|
||||
+0.28. Знак отрицателен во всех трёх прогонах и в эксп-22, то есть слабое свидетельство против
|
||||
`glm-5` есть — на этом приборе и этом n оно не доходит до различимого. Разрешается граница только
|
||||
ростом мощности (больше единиц или менее шумный судейский контур), а не пере-чтением этих чисел.
|
||||
|
||||
### 4. ЦЕНА (замер, 1500 единиц на книгу)
|
||||
|
||||
```
|
||||
арм p50 edit p95 edit p50 связка книга p50 книга p95 × к R0
|
||||
R0 deepseek-v4-pro 0.00769 0.01573 0.00897 $13.45 $25.13 —
|
||||
T1 glm-5.2 0.01725 0.01955 0.01879 $28.18 $31.66 2.09×
|
||||
T2 gpt-5.6-terra 0.04408 0.04841 0.04544 $68.17 $74.88 5.07×
|
||||
T3 grok-4.5 0.05276 0.06441 0.05397 $80.96 $97.88 6.02×
|
||||
якорный черновик (общая база связки, входит в каждую связку): p50 0.00122
|
||||
```
|
||||
⚠ **Прежняя редакция этой таблицы сравнивала связку с не-связкой.** В строке `R0` стояло
|
||||
`0.00545` — медиана ДВУХ клеток скрина эксп-22, не этих шестнадцати, — а книжные столбцы при ней
|
||||
были посчитаны по СВЯЗКЕ «черновик+редактор», тогда как у `T1`–`T3` те же столбцы шли по одной
|
||||
редактуре. Нескладность была видна в самой строке: 0.00545 × 1500 = $8.18, а напечатано $13.45.
|
||||
Ни один гейт её не трогал: пере-счёт спрашивал у эксп-22 метод `edit_cost`, которого у него нет, и
|
||||
строка молча выходила нулевой. Починено в `itog23.py`/`verify23.py`; теперь оба семейства колонок
|
||||
печатаются рядом и сторожатся. **Вывод «сильный тир в 2–6 раз дороже» от починки не изменился** —
|
||||
он и считался по связкам, просто напечатан был не тем числом.
|
||||
⇒ **Решение однозначно и не требует продуктового суждения:** сильный тир стоит в 2–6 раз дороже и
|
||||
не даёт различимого выигрыша. Платить не за что.
|
||||
|
||||
### 5. ОТКАЗНОЙ РЕЖИМ KIMI — КЛЕТКА ЗАМЕРЕНА ПОД СВОИМ ПОТОЛКОМ (посылка 2 согласуется, но не различает)
|
||||
|
||||
`kimi-k3` в редакторской роли — **пустой выход при 99.9% доли размышления**, $0.0804 за клетку.
|
||||
Особый режим (одна клетка вместо полного скрина), объявленный ДО покупок, сэкономил ≈$1.
|
||||
|
||||
⚠ **Клетке был выдан СВОЙ потолок вывода: `max_out=4000` против 16000 у всех остальных**
|
||||
(`screen.py:47`), и сырьё показывает `finish=length · completion 4000 · reasoning 3997 · content
|
||||
пуст`. То есть модель уперлась в мой потолок, ещё не выйдя из фазы размышления. Прежняя редакция
|
||||
называла только «одну клетку вместо полного скрина» и умалчивала о потолке, а квирк-реестр проекта
|
||||
(`00-provider-quirks.md`) описывает ровно этот симптом как нехватку бюджета и предписывает ≥16000.
|
||||
Поэтому: **вердикт по ЦЕНЕ твёрдый** — $0.080406 уже выше порога роли $0.06, а при 16000 клетка
|
||||
стоила бы ≈$0.26; **вывода об отказном режиме вендора эта клетка не даёт** — она не различает
|
||||
«модель отказывается работать» и «мой потолок обрезал её на размышлении». Эксп-22 снимал
|
||||
`kimi-k2.6` другим замером; повторением того результата это считать нельзя. Девиация — в §8.
|
||||
|
||||
### 6. ДЕНЬГИ И ДИСЦИПЛИНА
|
||||
|
||||
```
|
||||
ФA скрин $0.481038 / 1.10 ФC панель+пол $2.080125 / 2.30
|
||||
ФB единицы $0.346196 / 0.45 ПАК $2.907359 / 4.00
|
||||
```
|
||||
Ни один потолок не пробит, деньги сведены двумя путями (расхождение ≤7e-6). Пере-прогон судейства
|
||||
покупок не потребовал. Дополнительная санкция владельца на $5 не понадобилась.
|
||||
|
||||
⚠ **Потолок ФC поднимался ДВАЖДЫ; вот точная хронология по коммитам и mtime сырья.**
|
||||
|
||||
```
|
||||
03:21:57 фриз f1f9947 ФC 1.80 → 1.95, объявлено в коде фазы
|
||||
04:37:39 первая покупка панели ← 76 мин ПОСЛЕ фриза
|
||||
05:17:43 последняя покупка панели ← панель фактически стоила $1.798638
|
||||
05:36:25 фриз 1f6d6ae ФC 1.95 → 2.30
|
||||
05:37:25 первая покупка шумового пола ← 60 с ПОСЛЕ фриза
|
||||
```
|
||||
Ни один доллар не потрачен под незафризенным операционным потолком. **Обе прежние редакции этого
|
||||
абзаца были неверны, и во взаимно противоположные стороны:** сначала «поднят до покупок фазы» —
|
||||
умалчивая, что подъёмов было два; затем «правка попадает в середину окна покупок» — а она в него
|
||||
не попадает вовсе, второй подъём случился через 19 минут ПОСЛЕ последней покупки панели. Вторая
|
||||
формулировка была самооговором, написанным при пере-чтении по памяти, без сверки с `git log`.
|
||||
|
||||
⚠ **Основание подъёма названо замером, а было проекцией.** «Панель $1.844» — это оценка
|
||||
`--c-plan` по медианам цен фазы A, а фактическая панель стоила **$1.798638**, то есть влезала и в
|
||||
исходные $1.80. Честное основание второго подъёма другое и его надо назвать прямо: своему шумовому
|
||||
полу нужно было ещё $0.281487, и проекционный сторож кассы при потолке 1.95 отказал бы последним
|
||||
клеткам. Взят подъём из объявленного планом резерва пака ($0.65), пакетный потолок $4.00 не
|
||||
двигался. Расход резерва на СВОЙ шумовой пол — не то назначение, под которое резерв объявлялся
|
||||
(«ре-гены эхо-мины и ретраи»), и правило пре-рега «не влезает — СТОП и пинг» исполнено не было:
|
||||
артефакта пинга нет. Вопрос владельцу — §14.
|
||||
|
||||
|
||||
⚠ **Позиционная поправка замерена и вычтена по ОБОИМ проходам.** `tier`: наклон **−0.006** ошибки
|
||||
на шаг метки, после вычитания −0.18 / +0.49 / +0.18 — вердикты не меняются. `border`: наклон
|
||||
**+0.708** (раскладка этого прохода короче, и позиция весит заметно больше), поправка двигает
|
||||
контраст с −0.81 до −0.90 при p 0.0873 — вердикт «ниже порога различимости» не меняется ни до, ни
|
||||
после поправки, хотя после поправки контраст подходит к границе значимости ближе, чем до неё.
|
||||
|
||||
⚠ **Поправка фазы Д (чек-лист приёмки №16 п.6): здесь стояло +0.646, а сырьё даёт +0.708.**
|
||||
Величина, на которую делается поправка боевого контраста, печаталась не из тех голосов, из которых
|
||||
считается сам контраст. Числа с поправкой (−0.90, p 0.0873) при этом верны — они пере-сняты и
|
||||
сходятся. Причина расхождения не установлена: промежуточные состояния разбора не сохранились, а
|
||||
голоса с тех пор пере-снимались дважды (гонка §8 п.10 и пере-разбор репликации ниже). **В прибор —
|
||||
сделано:** `verify23` сторожит наклон ЧИСЛОМ по каждому проходу; прежде он не трогал его вовсе,
|
||||
хотя поправка на наклон — часть решающего правила.
|
||||
|
||||
**Агент-запусков: 58 при капе 60.** Первый (аннулированный) прогон 16 + пере-суживание 9 +
|
||||
пере-прогон `tier` 12 + адверсариальное ревью 1 + пере-судейство `border` 12 + репликация 8 (§12а).
|
||||
⚠ **Поправка фазы Д (чек-лист приёмки №16 п.4): в этой строке и в §14 п.9 стояло 50 — репликация
|
||||
из счёта выпала.** Число 58 совпадает с §12а, где оно и было напечатано верно; расходились между
|
||||
собой две строки одного отчёта.
|
||||
|
||||
⚠ Первые 38 сочтены МНОЙ, а не механизмом: `log_run` был написан и ни разу не вызван,
|
||||
`agent-runs.json` не создавался, во всех голосах стоял `judge='?'`. С пере-судейства `border` учёт
|
||||
персистируется — на диске 20 записей (12 боевых + 8 репликации), раскладка «кто какие единицы
|
||||
судил» в `border-JUDGES.json` и `replication-runB-JUDGES.json`, в каждом голосе имя сессии.
|
||||
⚠ **«Считает механизм» сказать нельзя** — файл записан РУКОЙ, скриптом, уже после судейства: в коде
|
||||
`log_run` зовётся из одного места и единственную пометку `note='судейская сессия'` произвести может,
|
||||
а три остальные, что стоят в записях, — нет. Значит: раскладка судей по единицам персистирована и
|
||||
пере-снимаема, а **СЧЁТЧИК КАПА НЕ РАБОТАЕТ** — на диске 20 против 58 фактических, и он пропустил бы
|
||||
ещё 60. Причина механическая: `log_run` вызывается из `--ingest`, то есть ПОСЛЕ суб-агента, а
|
||||
сторожить кап можно только записью ДО запуска. Проход `tier` не покрыт вовсе: `tier-JUDGES.json`
|
||||
не создан, во всех его голосах стоит `judge='?'`, и восстановить это задним числом нечем.
|
||||
**Починено в фазе Д, не здесь:** `eval/dovodka/runs.py` пишет запись ДО запуска суб-агента, кап
|
||||
проверяет перед записью и вдобавок ОТКАЗЫВАЕТ выдать те же токены незакрытой сессии — механический
|
||||
замок против гонки §8 п.10. Селфтест 12/12.
|
||||
|
||||
⚠ **ПРОВЕНАНС КАТАЛОГОВ СУДЕЙСТВА `border` УТРАЧЕН (поправка фазы Д, п.8).** Боевые `aj-border` и
|
||||
`aj-border-votes` — пере-штампованные копии `replication-runA-*`: содержимое побайтно совпадает
|
||||
(`diff -rq` чист), а mtime всех файлов равен **2026-08-09 23:31:54**, то есть моменту копирования,
|
||||
а не моменту судейства. Задания `aj-border-tasks` сохранили исходный mtime 17:55:26. Что из этого
|
||||
следует: содержательно боевой прогон и есть `runA`, и все его числа пере-снимаемы; но доказать
|
||||
ВРЕМЕНЕМ, когда именно судились боевые ответы, по диску больше нельзя — а гейт свежести разбора,
|
||||
заведённый после гонки (§8 п.10), опирается именно на mtime. Для этого пака он теперь сравнивает
|
||||
копии с копиями. Объявляю как ограничение, восстановлению не подлежит.
|
||||
|
||||
⚠ **ГОЛОСА РЕПЛИКАЦИИ НЕСЛИ ИМЕНА СУДЕЙ БОЕВОГО ПРОГОНА (поправка фазы Д, п.9).** `absjudge.ingest`
|
||||
читает карту судей по имени ПРОХОДА (`blind-keys/border-JUDGES.json`), а репликация судила ТЕ ЖЕ 32
|
||||
токена вторым жребием и лежала в своих каталогах — поэтому все 32 её голоса получили имена
|
||||
`agent-02…agent-12` вместо собственных `agent-B1…B8`. Перевесы от этого не двигаются (имя судьи в
|
||||
разность не входит, и §12а пере-снимается до знака: −0.62, ДИ [−1.78, +0.53], p 0.3327, пол −1.00,
|
||||
порог 1.77, декой −4.50 при 32/32), но разложение дисперсии по сессиям и любое «согласие судей»
|
||||
считались бы по ложной раскладке. **Починено механизмом:** у рига появились крючки `DIRS_HOOK` и
|
||||
`JUDGES_HOOK`, у прогона — свои каталоги и своя карта; `judge.py --run <прогон> <проход> --ingest`
|
||||
пере-разобрал обе репликации. `replication-runA-JUDGES.json` при этом заведён копией боевой карты —
|
||||
`runA` и есть боевой прогон, и теперь КАЖДЫЙ прогон несёт собственную карту, а не наследует её от
|
||||
имени прохода.
|
||||
|
||||
## §7 ЧЕТЫРЕ ПОСЫЛКИ — СВЕРКА С ФАКТОМ
|
||||
|
||||
| # | посылка (записана ДО замера) | факт | итог |
|
||||
|---|---|---|---|
|
||||
| 1 | сильный тир НЕ обязан выигрывать; вероятный исход — не побьёт `deepseek-v4-pro` | ни один из троих не различим (−0.19, +0.50, +0.19 при пороге 1.02) | **ПОДТВЕРЖДЕНА** |
|
||||
| 2 | отказной режим размышления — свойство ВЕНДОРА, `kimi-k3` воспроизведёт отказ | пустой выход при 99.9% размышления — но клетка шла под СВОИМ потолком 4000 (§5) | **СОГЛАСУЕТСЯ, НЕ РАЗЛИЧАЕТ**: замер не разводит отказ вендора и обрыв на размышлении моим потолком |
|
||||
| 3 | граница `glm-5` разрешима ростом n, а не сменой судьи | при n=32 и починенных контролях перевес сжался до −0.81, порога 1.48 не проходит (§3) | **ОПРОВЕРГНУТА как ожидание «разрешится», ПОДТВЕРЖДЕНА как прогноз «сожмётся»** |
|
||||
| 4 | внешний контур нужен, но не заменяет мощность; отсутствие Sol не блокирует | пак закрыт без Sol; семейный слепой участок остался | **ПОДТВЕРЖДЕНА частично** |
|
||||
|
||||
⚠ Посылка 3 разошлась с прогнозом в другую сторону, чем объявлялось раньше. Пре-рег ожидал, что
|
||||
при удвоении n перевес сожмётся; аннулированный прогон дал «устоял и стал значим», и это записали
|
||||
как «ожидание не оправдалось». На исправленной базе перевес сжался — то есть прогноз пре-рега был
|
||||
верен, а неверна была промежуточная запись. Сама посылка («разрешима ростом n») не подтвердилась:
|
||||
удвоение n границу не разрешило, оно её закрыло в «не различимо».
|
||||
|
||||
⚠ **Выводы, которые эта сессия объявляла и которые оказались неверны.** Порядок важен, потому что
|
||||
объявлений было три волны, и каждая была подписана как окончательная.
|
||||
|
||||
1. По числам ПЕРВОГО (аннулированного) прогона: «сильный тир бьёт боевой редактор», «вывод
|
||||
эксп-22 перевёрнут», «граница разрешена в ноль», «посылка 1 опровергнута» — все четыре неверны,
|
||||
держались на числах без контроля чувствительности судьи.
|
||||
2. По числам ВТОРОГО прогона: «граница разрешена ПРОТИВ `glm-5`, результат эксп-22 реплицирован на
|
||||
n=32» и закрытие Резерва D39.22 — сняты: половина материала шла с подменённой базой (§3).
|
||||
3. В самом отчёте после этого ещё держались §13 («тир оказался лучше», «D39.22 закрыт В ПОЛЬЗУ
|
||||
`glm-5`») — прямо против §1 и §3 того же документа, — и §12 с порогами из аннулированного
|
||||
прогона. То есть отчёт какое-то время противоречил сам себе, и это тоже нашла приёмка.
|
||||
|
||||
Общий механизм у всех трёх волн один: связная таблица принималась за истинную, а смелость вывода
|
||||
работала аргументом в его пользу. Ни одну из волн не остановил автор.
|
||||
|
||||
## §8 ДЕВИАЦИИ И ДЕФЕКТЫ — ОБЪЯВЛЯЮ
|
||||
|
||||
1. **Половина первого прохода отвергнута разбором** (8 из 16; во втором — 10 из 32). Причина одна:
|
||||
судьи ставили ненулевой счёт по оси, не подпирая цитатой, а разбор выбрасывает такую единицу
|
||||
ЦЕЛИКОМ. Это дефект МОЕЙ формулировки: в задании сказано, что обоснование обязательно, но не
|
||||
сказано, что цена нарушения — потеря всей единицы. Пере-суживание с явной ценой снизило
|
||||
отбраковку до нуля. Стоило 9 агент-запусков; денег не стоило.
|
||||
2. **ЭХО-МИНА НА БОЕВОЙ БАЗЕ, И ГЕЙТ ПРОТИВ НЕЁ БЫЛ НАПИСАН, НО НЕ ПОДКЛЮЧЁН.**
|
||||
`et-unit-draft-deepseek-v4-flash-63ab55ac5c`: 2243 знака при исходнике 2182, `finish=stop` —
|
||||
и в этих 2243 знаках **1798 иероглифов при НУЛЕ кириллицы**. Это не «пересказ вместо перевода»,
|
||||
как утверждала прежняя редакция пункта, а классическая эхо-мина: модель вернула исходник.
|
||||
Прежняя редакция объявляла и то, что «ни один гейт пака этого не ловит» — тоже неверно:
|
||||
`bakeoff.draft_reject_reason` ловит её штатно (пере-снято: вердикт «эхо исходника»), просто
|
||||
фаза B звала только проверку «строка не пуста». Гейт подключён; на этом паке он забраковал бы
|
||||
ровно эту единицу и больше ни одной из шестнадцати.
|
||||
**Чувствительность вывода к ней замерена:** без неё контраст границы −0.74 (p 0.21) против
|
||||
−0.81 (p 0.16) на всех 32 — порог 1.48 не проходит ни так, ни так, вердикт §3 не меняется.
|
||||
Что здесь верно и что было сформулировано неточно: вход у обоих армов контраста ДЕЙСТВИТЕЛЬНО
|
||||
одинаковый — это одна и та же база, — но оба редактора выдали полную длину, то есть достроили
|
||||
пропущенное из исходника и решали на этой единице ДРУГУЮ задачу (перевод, а не редактуру).
|
||||
Единица оставлена и объявлена. **В прибор: гейт «длина клетки против длины ИСХОДНИКА с
|
||||
поправкой на пару языков»; сравнение с медианой соседних клеток этот случай не ловит, а на
|
||||
разноязычных пулах даёт ложные срабатывания (см. снятый пункт ниже).**
|
||||
3. **СНЯТО — было ложным срабатыванием, и оно уже ушло оркестратору.** Прежняя редакция объявляла,
|
||||
что в сырье эксп-22 три клетки боевого редактора (`8e50448cea`, `b065a92dc0`, `53f1a12dff`)
|
||||
оборваны до 22% длины, и отдавала это оркестратору как незамеченный дефект чужого пака.
|
||||
Пере-проверка: все три — из АНГЛИЙСКОГО пула (`manifest.json`, ключ `en`), их исходники ~1630
|
||||
знаков, выходы 1590/1596/1638 при `finish=stop`, то есть ≈100% исходника. «22%» получалось
|
||||
только от сравнения с медианой пула, где преобладают zh-единицы с трёхкратным расширением.
|
||||
По собственному критерию этого отчёта («<70% медианы СВОЕЙ единицы») таких клеток ноль.
|
||||
Оркестратору: пункт отозван, дефекта эксп-22 здесь нет. ⚠ Прежняя редакция писала, что ложный
|
||||
пункт «уже ушёл оркестратору» — носителя у этого не было: отчёт не отслеживался git, в журнале
|
||||
и D-логе исходного утверждения нет. Носитель появился только сейчас, вместе с отзывом, — запись
|
||||
в `docs/PROGRESS.md` секции «Полигон» и §15 отчёта эксп-22.
|
||||
4. **Девиация протокола судейства.** Две сессии сообщили, что СРАВНИВАЛИ варианты между собой
|
||||
(«чтобы точнее процитировать различия»), хотя задание требует оценивать каждый вариант только
|
||||
против исходника. Смещение от такого сравнения бьёт по арму-одиночке, а в проходе `tier`
|
||||
одиночка — как раз боевой редактор. Величину смещения по двум сессиям не оценить; факт
|
||||
объявлен. **В прибор: запрещать не только сравнение, но и чтение вариантов рядом.**
|
||||
5. **Двусмысленная инструкция про оборванный вариант — относилась к клетке, которой в принятой
|
||||
конфигурации нет.** Я велел судье «оценивать как есть и не достраивать»; судья прочитал это как
|
||||
«не штрафовать за обрыв». Клетка, на которой это наблюдалось, попадала в пачку только через
|
||||
дефект базы R0 (§0) и в исправленном ключе отсутствует; в другом голосе того же прогона судья
|
||||
пропуск, наоборот, засчитал. Формулировка всё равно неверна и правится («оценивать как есть» и
|
||||
«не штрафовать за неполноту» — разные вещи), но как НАБЛЮДЕНИЕ пункт снят: воспроизводимого
|
||||
случая в принятых данных нет.
|
||||
6. **Реализация скрина применила критерий, которого нет в пре-реге.** Код переиспользовал вердикт
|
||||
эксп-22 целиком, вместе с ПЕРЕВОДЧЕСКИМ порогом $0.02, — а пак скринит редакторов, и пре-рег
|
||||
называет только редакторский порог $0.06. Лишний порог снял `gpt-5.6-terra` и `grok-4.5`, то
|
||||
есть обоих, ради кого пак существует. Снимать критерий после результата — подгонка, поэтому
|
||||
печатаются ОБА вердикта: пре-рег-критерий как несущий, полный критерий эксп-22 как справка
|
||||
(§10). Провенанс: фризы `c0dd228` 02:44:28 и `87247e2` 02:45:04, первая покупка **02:45:35** —
|
||||
запас 31 секунда, а не минута, как читалось. ⚠ «План старше обоих» с диска НЕ доказуемо: план
|
||||
пака лежит в эфемерном scratchpad и в git не фризовался (`git log --all -- '*plan23*'` пуст),
|
||||
колонка улик его реестра дописывалась по ходу. Неизменяемый провенанс здесь несут только
|
||||
фриз-коммиты и запись сессии — то же снижение, что уже сделано в §6.
|
||||
7. **Я СНЯЛ ЗАМКИ У ЖИВОГО ПОКУПАЮЩЕГО ПРОГОНА.** Увидев в каталоге сырья зависшие `*.lock` и
|
||||
решив, что они остались от упавшего процесса, я удалил их, пока покупки ещё шли. Атомарный
|
||||
замок (`money.py:219`, `O_CREAT|O_EXCL`) — единственное, что разводит параллельных покупателей;
|
||||
именно его отсутствие стоило эксп-22 верхней границы невозвратной пере-оплаты ≤$0.846103 на 176
|
||||
клетках (его §-о деньгах). Ущерба не случилось только потому, что второго покупателя в тот
|
||||
момент не существовало: удача, а не дисциплина. Ни один гейт этого не ловит и поймать не может —
|
||||
удаление файла руками вне кассы. **В прибор:** снимать замок только тем же процессом, что его
|
||||
поставил, а «зависший» замок опознавать по живости PID, а не по виду каталога.
|
||||
8. **Коллизия само-импорта, четырежды.** Модули пака (`roster`, `screen`, `money`, плюс тестовый
|
||||
файл) грузились по имени и подхватывали одноимённые модули эксп-22 вместо своих. Один случай
|
||||
прошёл МОЛЧА и исказил замер: селф-тест проверял 11 моделей из 15 и печатал зелёное. Чинено
|
||||
явной загрузкой по пути (`_load`), но найдено это чтением вывода, а не гейтом.
|
||||
9. **Фриз фазы A попутно изменил риг ЧУЖОГО пака.** Тот же коммит `c0dd228` тронул
|
||||
`eval/tenant_panel/material.py` (приколка единиц манифестом) и `money.py` под заголовком про
|
||||
editor-tier. Зона не нарушена (весь `eval/` — зона полигона) и изменение объявлено в §11, но
|
||||
код, породивший уже принятые числа эксп-22, изменён задним числом под чужим заголовком.
|
||||
Гейты эксп-22 после этого пере-снимались; их зелёность после правки — в §14, вопрос 7.
|
||||
10. **ГОНКА ДВУХ КРУГОВ СУДЕЙСТВА И РАЗБОР В ЕЁ СЕРЕДИНЕ.** Пять единиц прохода `border`
|
||||
(`13514e13f0`, `15f002335d`, `1ffe99dc43`, `225001778a`, `87dd50b129`) я отдал на пере-суживание
|
||||
ДВАЖДЫ, не дождавшись первого круга: второй круг записал ответы, я прогнал `--ingest` в 18:36:56,
|
||||
а первый круг дописал те же самые файлы в 18:38–18:41. В итоге голоса были от одного круга, а
|
||||
сырьё на диске — от другого, и расходились они по всем пяти единицам. Числа при этом сходились
|
||||
с отчётом, потому что и отчёт, и гейт читали ГОЛОСА: ни `verify23`, ни `itog23` не сравнивали их
|
||||
со временем ответов. Нашёл аудит закрытия заказа, не автор и не первая приёмка.
|
||||
**Как разрешено.** Голоса пере-снят с текущего сырья — правило «сырьё на диске главнее», а не
|
||||
«то, что уже напечатано»: иначе опубликованное число было бы невоспроизводимо для читателя.
|
||||
⚠ Оба набора я видел ДО выбора правила, и это надо знать при чтении: первый круг давал −0.59
|
||||
(порог 1.43), второй −0.81 (порог 1.48). Вердикт «ниже порога различимости» одинаков в обоих, а
|
||||
вот расщепление половин при этом заметно двигается (было −1.44/+0.25, стало −1.62/0.00) — то
|
||||
есть устойчив здесь только знак и вывод, но не величины по подвыборкам.
|
||||
**В прибор — сделано:** `verify23` роняет пак, если хоть один ответ судьи новее своего голоса.
|
||||
Это первый гейт пака, сторожащий не число, а ПОРЯДОК СОБЫТИЙ.
|
||||
11. **Моя ошибка чтения по ходу.** На n=14 я озвучил «преимущество не различимо», зная, что две
|
||||
единицы ещё не вернулись. Вывод на неполных данных — та же ошибка, что и вывод на агрегате без
|
||||
вскрытия единиц.
|
||||
|
||||
⚠ **Поправка фазы Д (чек-лист приёмки №16 п.10): нумерация этой секции была сломана.** На диске
|
||||
шёл ряд 1–6, 8, 9, 10, 7, 7 — два разных дефекта под номером «7», и ссылка §14 п.7 вела на «§8 п.8»,
|
||||
которого не существовало. Пункты пере-нумерованы по порядку в документе (тексты не тронуты), ссылки
|
||||
на них поправлены. Причина сбоя видна по содержанию: пункты 7 и 11 дописаны последними, аудитом
|
||||
закрытия заказа, и вставлены в хвост без сверки с уже занятыми номерами.
|
||||
|
||||
## §9 ЧТО НЕ ПЕРЕНОСИМО
|
||||
|
||||
* **Проход `tier` по-прежнему снят ОДНОЙ модельной семьёй.** Для `border` ограничение снято
|
||||
внешним судьёй (§12в); тексты `tier` он не читал, и вывод «сильный тир не отличим» подписи вне
|
||||
Claude не имеет.
|
||||
* **ОДНОЙ МОДЕЛЬНОЙ СЕМЬЁЙ СНЯТЫ НЕ ТОЛЬКО ЧИСЛА, НО И ВСЯ ПРИЁМКА.** Это признаётся здесь впервые
|
||||
и это отдельный дефект, а не повтор предыдущего пункта. D39.120 требует на текстовых и
|
||||
оценочных линзах хотя бы одного опровергателя ДРУГОГО модельного семейства; владелец сверх того
|
||||
прямо разрешил сессии запустить агентов Fable-5 под ревью. Ни то, ни другое не исполнено: все
|
||||
судьи, все линии приёмки, все опровергатели и весь аудит закрытия заказа отработали на Opus-5.
|
||||
То есть монокультура, которую отчёт объявляет ограничением ЗАМЕРА, ровно в той же мере поражает
|
||||
и МЕХАНИЗМ ПРОВЕРКИ замера — а именно он трижды подписывал числа, оказавшиеся артефактами.
|
||||
Разрешение владельца на другое семейство лежало неиспользованным всё время работы пака.
|
||||
* **Внешний контур есть у `border` и НЕТ у `tier`** — ⚠ поправка фазы Д (чек-лист приёмки №16 п.5).
|
||||
Прежняя редакция этого буллета гласила «все судейские числа сняты одной модельной семьёй,
|
||||
внешнего судейского контура нет и здесь», то есть прямо против §12в того же отчёта, где подпись
|
||||
судьи вне Claude по проходу `border` напечатана числами (−1.00 при пороге 3.53, декой 32/32).
|
||||
Верная форма: для `border` ограничение СНЯТО, для `tier` — стоит в полную силу (первый буллет
|
||||
этой секции). Отдельная линия — Sol-пакет (а) эксп-22: прогнан владельцем 09.08, но арбитраж не
|
||||
состоялся (эксп-22 §16: три единицы на контраст, дрейф шкалы ×2.6, знак совпал в 3 парах из 9);
|
||||
пакет (б) не запускался. Ограничение по `tier` несущее, а не формальное: его вывод — что
|
||||
перевесы сильного тира НЕ доходят до порога, — есть утверждение о том, чего судья не различил,
|
||||
и оно тем слабее, чем однороднее судьи. Отдельно: идентичность судей `tier` не персистирована
|
||||
(§6), поэтому согласие между ними даже внутри семьи посчитать нечем.
|
||||
* **Декой во всех единицах принятого прохода `tier` посажен из одного и того же арма — R0**
|
||||
(замер по ключу: 16/16), а R0 стоит вторым армом в каждом контрасте этого прохода. Если само
|
||||
присутствие испорченного близнеца двигает оценку донора, оно двигает её в одну сторону во всех
|
||||
трёх контрастах сразу. Прямой улики за или против нет: ни один сохранённый голос не содержит
|
||||
рассуждения «это копия другой метки». Косвенная — R0 против голого черновика даёт +2.06, а
|
||||
T1/T2/T3 против него же +1.88 / +2.56 / +2.25, то есть R0 лежит ВНУТРИ разброса панели, грубого
|
||||
смещения донора не видно. Для прохода `border` донор с этого пере-прогона уравнен: 16 единиц
|
||||
из R0, 16 из R2.
|
||||
* **Одна книга, одна пара языков, 16 единиц панели.** Всё, что зависит от свойств текста, обязано
|
||||
пере-меряться.
|
||||
* **Вывод про `gemini-3.1-pro` по-прежнему отсутствует** — он не входил в этот пак.
|
||||
* **`kimi-k3` замерен ОДНОЙ клеткой** по объявленному особому режиму И под своим потолком вывода
|
||||
(§5): твёрдым остаётся только вердикт по ЦЕНЕ; ни о качестве его прозы, ни об отказном режиме
|
||||
вендора этот замер не говорит.
|
||||
|
||||
## §10 ФАЗА A — СКРИН СИЛЬНОГО ТИРА
|
||||
|
||||
Пороги взяты у эксп-22 БЕЗ изменения — порог из прошлого пака единственное, что защищает состав
|
||||
от подгонки, а состав здесь и есть предмет спора.
|
||||
|
||||
```
|
||||
модель вердикт ед. $/ед edit размышл причины
|
||||
gpt-5.6-terra прошёл 4 0.04250 6.6%
|
||||
grok-4.5 прошёл 4 0.05481 69.5%
|
||||
glm-5.2 прошёл 4 0.01792 0.0%
|
||||
kimi-k3 ПРОВАЛ 1 0.08041 99.9% Г5 цена editor > $0.06 · Г6 выход пуст
|
||||
```
|
||||
|
||||
**Справка — полный критерий эксп-22** (с переводческим порогом $0.02, которого в пре-реге этого
|
||||
пака нет): `gpt-5.6-terra` ПРОВАЛ ($0.03668), `grok-4.5` ПРОВАЛ ($0.03445), `glm-5.2` прошёл
|
||||
($0.01396), `kimi-k3` ПРОВАЛ. Разница между двумя колонками — целиком §8 п.6.
|
||||
|
||||
Цены — с ВЕНДОР-страниц 09.08: `gpt-5.6-terra` $2.00/$0.20/$12.00 · `kimi-k3` $3.00/$0.30/$15.00 ·
|
||||
`grok-4.5` $2.00/$0.30/$6.00 · `glm-5.2` $1.40/$0.26/$4.40. Слаги — живой листинг `/models` того же
|
||||
дня. Попутно закрыта дыра эксп-22: его цена `gpt-5.6-luna` не подтверждалась живьём (Cloudflare
|
||||
403) и ехала из прошлых паков — страница открылась и подтвердила её ровно.
|
||||
|
||||
## §11 МАТЕРИАЛ
|
||||
|
||||
16 НОВЫХ единиц из 16 разных глав (22, 25–30, 32–36, 39–42), знаков 2077…2251, медиана 2141,
|
||||
макс. попарная близость 0.091. Пересечений с эксп-22 — ноль: его главы исключены ЦЕЛИКОМ, иначе
|
||||
«новые» единицы были бы соседними абзацами тех же глав и делили бы с ними сцену и лексику, а
|
||||
прирост мощности оказался бы мнимым. Гейт прав пересчитан по СВОЕМУ пулу (верхний дециль по
|
||||
плотности эротических маркеров), а не перенесён константой «2» из эксп-22.
|
||||
|
||||
⚠ **Побочный эффект докачки глав, который пришлось чинить.** Материал эксп-22 отбирался «из
|
||||
середины распределения длин» по содержимому диска; докачка с 18 глав до 42 СДВИНУЛА его единицы
|
||||
(главы 3–18 → 2,5,9…41), то есть его гейты стали бы считать по материалу, который никто не
|
||||
покупал. Единицы эксп-22 приколоты к его манифесту отбора; все четыре его гейта после этого
|
||||
зелёные. Правило отбора теперь применяется только когда манифеста ещё нет.
|
||||
|
||||
## §12 КОНТРОЛИ РИГА (читать ДО боевых чисел)
|
||||
|
||||
```
|
||||
проход ДЕКОЙ (порча против чистого) ПОЛ (истинная разница ноль) БЛИЗНЕЦ
|
||||
tier −3.94 поймано 16/16 ✔ −0.62 [−1.38, +0.06] порог 1.02 пара CTRLfloorA≡T1, ноль
|
||||
border −4.88 поймано 32/32 ✔ −1.00 [−2.06, +0.06] порог 1.48 побайтных дублей нет
|
||||
```
|
||||
**Декой** — посаженная в чистый текст деградация — обязателен в каждом проходе: судейская сессия,
|
||||
не поймавшая его отрицательным знаком, аннулируется целиком (так эксп-22 списал 6 агент-запусков).
|
||||
Пол ЧЕСТЕН: ноль внутри интервала, и порог различимости берётся ИЗ НЕГО — это решающее правило
|
||||
пака, поэтому §1 читается против 1.02, а §3 — против 1.48.
|
||||
|
||||
⚠ **Прежняя редакция этой секции печатала пороги 1.65 и 1.58 — они из ПЕРВОГО, аннулированного
|
||||
прогона** (пере-снято: `annulled-run1` даёт ровно 1.65 и 1.58). Секция объявлена «читать ДО боевых
|
||||
чисел» и задаёт порог различимости, так что отчёт выдавал читателю два разных порога на одну
|
||||
величину: §1 читался против 1.02, а §12 печатал 1.65 — и при напечатанном 1.58 тогдашний вывод §3
|
||||
в порог не проходил вовсе, то есть документ противоречил сам себе по несущему вердикту.
|
||||
Средние −0.29 и −0.42 из той же строки не
|
||||
воспроизводятся НИ ИЗ ОДНОГО набора голосов на диске и при n=16 недостижимы арифметически (среднее
|
||||
целых ошибок кратно 1/16); откуда они взялись — не установлено, промежуточные состояния разбора не
|
||||
сохранились. Гейт этого не ловил: он сверял лишь наличие слова «пол». Починено — `verify23.py`
|
||||
теперь сторожит и величину пола, и порог, и величину декоя числом.
|
||||
|
||||
⚠ **«БЛИЗНЕЦ» в проходе `tier` — не контроль.** Единственная побайтно совпадающая пара пачки это
|
||||
`CTRLfloorA` и `T1`, в 16 единицах из 16, и совпадают они по построению: первой половиной шумового
|
||||
пола `panel.floor_pair` отдаёт саму боевую клетку `T1`, докупается только вторая генерация.
|
||||
«Перевес ровно ноль в 16/16» означает лишь, что один и тот же текст под двумя метками получил одну
|
||||
оценку внутри одного ответа судьи; воспроизводимость оценки из этого НЕ следует, и как контроль
|
||||
эта строка пуста. Замерено и смежное: связи между оценкой пары и боевым перевесом нет (корр.
|
||||
−0.05). Против такого переиспользования прямо предупреждает комментарий в риге эксп-22 — я его
|
||||
воспроизвёл. **В прибор: половиной пола брать отдельную генерацию, не клетку панели.**
|
||||
|
||||
## §12б ПОРОГ БЫЛ ЗАНИЖЕН НА 19% — РАЗЛОЖЕНИЕ ДИСПЕРСИИ
|
||||
|
||||
Найдено после сдачи, вопросом владельца «ты не преуменьшаешь?». Шумовой пол этого рига меряет
|
||||
пару, у которой ОБЕ половины судит один и тот же судья, — значит межсудейская разница в него не
|
||||
попадает по построению, а боевые перевесы её несут. Порог, взятый из такого пола, занижен.
|
||||
|
||||
Разложение дисперсии перевеса по 8 сессиям прохода `border` (n=32):
|
||||
```
|
||||
внутри сессии sd 2.903
|
||||
между сессиями sd 1.030 ← в шумовой пол НЕ попадает
|
||||
se среднего 0.513 наивная → 0.629 с учётом межсессионной (×1.23)
|
||||
ПОРОГ 1.48 напечатанный → 1.76 честный
|
||||
```
|
||||
Пересчёт вердиктов по честному порогу — **ни один не двигается**, и все, кроме одного, крепнут:
|
||||
```
|
||||
R2 glm-5 vs R0 −0.81 · 1.48 → 1.76 · ниже порога
|
||||
T1 glm-5.2 −0.19 · 1.02 → 1.53 · ниже порога
|
||||
T2 gpt-5.6-terra +0.50 · 1.02 → 1.68 · ниже порога
|
||||
T3 grok-4.5 +0.19 · 1.02 → 1.55 · ниже порога
|
||||
R0 vs F КОНТРОЛЬ +2.06 · 1.02 → 1.94 · РАЗЛИЧИМ
|
||||
```
|
||||
**Почему занижение порога не породило ложных выводов.** Заниженный порог делает вывод «различимо»
|
||||
слишком лёгким. Все несущие выводы пака отрицательные — заниженный порог им не помогает, а мешает;
|
||||
единственное положительное утверждение это позитивный контроль, и он проходит с запасом даже по
|
||||
строгому порогу. Направление риска здесь — что пак ПРОЗЕВАЛ реальную разницу, а не выдумал её.
|
||||
|
||||
⚠ **Для прохода `tier` межсессионная компонента ЗАИМСТВОВАНА с `border`.** Оценить её на своих
|
||||
данных нельзя: идентичность судей `tier` не персистирована, во всех голосах стоит `judge='?'` —
|
||||
прямое следствие дефекта учёта (§6). Задним числом не восстановить.
|
||||
|
||||
⚠ **Эмпирическая проверка этой поправки — §12а.** Прямая репликация новым жребием судей дала
|
||||
расхождение средних 0.19 при ожидании ≈1.0, то есть поправка скорее консервативна. Уточнять её
|
||||
по двум прогонам нельзя — это оценка по n=2.
|
||||
|
||||
**В прибор:** шумовой пол обязан браться парой, чьи половины судят РАЗНЫЕ сессии. Тогда он ловит
|
||||
обе компоненты, и порог не требует ручной поправки.
|
||||
|
||||
## §12в ВНЕШНИЙ СУДЬЯ ВНЕ СЕМЬИ Claude — ПРОХОД `border` ЗАКРЫТ ТРЕТЬЕЙ ПОДПИСЬЮ
|
||||
|
||||
Заказано владельцем после того, как он указал: все выводы пака сняты агентами одной модельной
|
||||
семьи, и мнения судьи другой семьи о КАЧЕСТВЕ этих переводов никто не спрашивал. Замечание было
|
||||
верным — предыдущая попытка внешнего контура (Sol-пакет эксп-22) судила тексты ЧУЖОГО пака и
|
||||
развалилась по устройству (эксп-22 §16).
|
||||
|
||||
**Что сделано.** Внешнему судье (не-Claude, локально, руками владельца, 4 сессии по 8 единиц)
|
||||
отданы ТЕ ЖЕ файлы заданий `aj-border-tasks/*.txt`, которые судили агенты пака: тот же исходник,
|
||||
те же армы под теми же слепыми метками, тот же декой и тот же шумовой пол в пачке. Поэтому его счёт
|
||||
сопоставим с моим текст-в-текст. Пере-снимается командой
|
||||
`eval/.venv/bin/python eval/editor_tier/solscore.py`; ответы в `~/books/editor-tier/sol-border/`.
|
||||
|
||||
```
|
||||
судья уровень перевес R2 пол порог вердикт
|
||||
внешний, не-Claude 11.0 −1.00 −1.88 3.53 ниже порога различимости
|
||||
мой прогон A 7.0 −0.81 −1.00 1.48 ниже порога различимости
|
||||
мой прогон B 7.0 −0.62 −1.00 1.77 ниже порога различимости
|
||||
```
|
||||
Принято 32 из 32 единиц, отказов ноль, ДЕКОЙ пойман **32/32**.
|
||||
|
||||
⇒ **Ограничение «все судейские числа сняты ОДНОЙ модельной семьёй» для прохода `border` снято —
|
||||
но снято СЛАБО, и это надо читать вместе с числами.** Вывод «граница `glm-5` не разрешена, эффект
|
||||
ниже порога различимости» подпись судьи вне Claude имеет. ⚠ Поправка фазы Д, найденная приёмкой
|
||||
другого модельного семейства: **у внешнего прибора шумовой пол sd 5.05 и порог 3.53**, а боевой
|
||||
контраст прохода −0.81. Значит внешний судья не мог противоречить нулевому вердикту НИ ПРИ КАКОМ
|
||||
исходе, кроме эффекта вчетверо больше замеренного — он и позитивный контроль масштаба +2 не
|
||||
развёл бы. Подпись под отрицательным выводом на таком приборе почти автоматична, и «ограничение
|
||||
снято» звучит сильнее, чем даёт замер. Честная форма: **внешний контур подтвердил, что эффекта
|
||||
РАЗМЕРА, ВИДИМОГО ЕМУ, здесь нет; про эффекты меньше 3.5 ошибок он не высказывался.**
|
||||
⇒ И следствие вперёд: та же оговорка накроет внешнюю подпись прохода `tier` (заказ фазы Д, п.Д2) —
|
||||
там боевые перевесы 0.19…0.50, то есть на порядок ниже разрешения внешнего прибора. Объявляется
|
||||
ЗАРАНЕЕ, до прогона, а не после: подпись будет означать «внешний судья тоже не увидел», а не
|
||||
«внешний судья подтвердил равенство».
|
||||
Для прохода `tier` внешней подписи по-прежнему нет: судья его текстов не читал.
|
||||
|
||||
**Замер межсемейного расхождения — побочная находка, для рига ценнее самого вердикта.**
|
||||
```
|
||||
согласие по единицам (корреляция перевесов) знак совпал
|
||||
внешний ↔ мой A +0.317 22/32
|
||||
внешний ↔ мой B +0.334 24/32
|
||||
мой A ↔ мой B +0.627 26/32
|
||||
```
|
||||
Расхождение МЕЖДУ семьями примерно вдвое больше, чем ВНУТРИ семьи. И прибор другой семьи заметно
|
||||
грубее: его шумовой пол sd 5.05 против 2.12 у моих, отсюда порог 3.53 против 1.48 — он честен, но
|
||||
разрешает вдвое хуже. Практическое следствие: **для вопроса «есть ли разница вообще» судьи разных
|
||||
семей взаимозаменяемы; для контраста тоньше двух ошибок на единицу смешивать их в один пул без
|
||||
нормировки нельзя** — различаются и шкала, и разрешение.
|
||||
|
||||
## §12а РЕПЛИКАЦИЯ ПРОХОДА `border` НОВЫМ ЖРЕБИЕМ СУДЕЙ
|
||||
|
||||
Заказана владельцем как проверка методики: «давай перегоним и посмотрим». Меняется РОВНО один
|
||||
фактор — состав судейских сессий. Ключ, слепые метки, посадка декоя, тексты и задания те же файлы,
|
||||
не пере-выпускались. Правило публикации записано ДО запуска: прогон A остаётся опубликованным,
|
||||
B — проверка; при расхождении вердиктов находкой считается само расхождение, а не удобный прогон;
|
||||
складывать A и B в один набор запрещено — это разные жребии.
|
||||
|
||||
```
|
||||
прогон A прогон B
|
||||
перевес R2 vs R0 −0.81 −0.62
|
||||
95% ДИ [−1.81,+0.28] [−1.78,+0.53]
|
||||
p (Холм) 0.1601 0.3339
|
||||
шумовой пол −1.00 −1.00
|
||||
порог прохода 1.48 1.77
|
||||
декой −4.88, 32/32 −4.50, 32/32
|
||||
уровень (медиана) 7.5 6.8
|
||||
```
|
||||
**Вердикт «ниже порога различимости» воспроизвёлся.** Расхождение средних |A−B| = **0.19** при
|
||||
пред-объявленном ожидании ≈1.0 — то есть прибор на уровне ВЫВОДА заметно устойчивее, чем следовало
|
||||
из моей же оценки межсудейской компоненты. Корреляция перевесов по единицам +0.63, знак совпал на
|
||||
26 единицах из 32.
|
||||
|
||||
⚠ **Что при этом НЕ воспроизводится — отдельные единицы.** Разность перевесов по-единично имеет
|
||||
sd 2.78 при среднем +0.19: на ОТДЕЛЬНОЙ главе армы не упорядочены, новый жребий судей переставит
|
||||
их местами примерно в каждой пятой. Устойчиво только среднее по единицам. Риг об этом
|
||||
предупреждает в собственном выводе, теперь это подпёрто прямым замером, а не рассуждением.
|
||||
|
||||
**Цена:** 8 агент-сессий, по паку стало 58 из капа 60. Покупок не потребовалось. Оба прогона лежат
|
||||
рядом: `~/books/editor-tier/replication-runA-*` (опубликованный) и `replication-runB-*`.
|
||||
|
||||
## §13 ДИСПОЗИЦИИ
|
||||
|
||||
⚠ Прежняя редакция этой секции несла выводы АННУЛИРОВАННОГО прогона («тир оказался лучше»,
|
||||
«D39.22 закрыт в пользу `glm-5`») — прямо против §1 и §3 того же отчёта, который сам перечисляет
|
||||
эти выводы как снятые. Секция, из которой переносится закрытие строк реестра, была последней, где
|
||||
я не сверился с собственными числами.
|
||||
|
||||
* **Строка про сильный тир (дыра эксп-22 §13а)** — **ЗАКРЫТА**: тир проверен, различимого выигрыша
|
||||
над боевым редактором нет, а стоит он в 2–6 раз дороже (§1, §4). Платить не за что; рекомендация
|
||||
эксп-22 (`deepseek-v4-pro`) остаётся и теперь стоит на панели, включающей сильный тир OpenAI,
|
||||
xAI и Z.AI. Решения владельца по цене НЕ требует — требовало бы при обратном знаке.
|
||||
* **Резерв D39.22 (`glm-5`)** — **ЗАКРЫТИЕ НЕ ОБЪЯВЛЯЮ, подаю ПРЕДЛОЖЕНИЕ.** Это ратифицированное
|
||||
решение владельца, и права закрывать его полигон-сессии не давалось; эксп-22 от закрытия
|
||||
воздержался. Замер: на 32 единицах с полным набором контролей `glm-5` от боевого редактора **не
|
||||
отличим** (−0.81 при пороге 1.48, §3), то есть основания «он значимо хуже», на которое сессия
|
||||
дважды ссылалась, НЕТ. Предложение: снять резерв как контраст, требующий отдельного разбора, и
|
||||
вести `glm-5` по общему правилу ничьей D39.117 — а оно оставляет `deepseek-v4-pro`, поскольку
|
||||
тот дешевле. Отдельно к решению: ToS-гейт Z.AI на прод-выбор `glm-5` никуда не делся (§9 эксп-22).
|
||||
* **Sol (строка 150)** — пакет (а) прогнан владельцем 09.08 и дал отрицательный результат по своей
|
||||
задаче (эксп-22 §16); пакет (б) НЕ запускать до починки оснастки: та же конструкция, `N_UNITS_B = 3`.
|
||||
⚠ **Поправка фазы Д (п.5):** здесь стояло «внешнего судейского контура у обоих паков по-прежнему
|
||||
нет» — неверно и против §12в. Контур ПОСТРОЕН и сработал, но на заданиях абсолютного рига, а не
|
||||
на пакетах конструкции Sol, и покрывает он проход `border`. Не покрыты: проход `tier` (задания
|
||||
`aj-tier-tasks` внешнему судье не отдавались) и оба контраста эксп-22, ради которых пакеты (а)/(б)
|
||||
и делались.
|
||||
* **Строка 153 и вторая база Ф3** — не гнались, как и в эксп-22.
|
||||
|
||||
## §14 CONFIRM / DENY — К ПОДПИСИ ВЛАДЕЛЬЦА
|
||||
|
||||
Ниже — всё, что этот пак принял на веру, назвал решением или сделал вне объявленного. Ни один
|
||||
пункт не считается ратифицированным, пока не подтверждён; пометка «со слов сессии» означает, что
|
||||
артефакта в репозитории нет и проверить нечем, кроме записи разговора.
|
||||
|
||||
| # | что | статус |
|
||||
|---|---|---|
|
||||
| 1 | **Мандата на пак 23 не существует** — промта нет, пак самоназначен, санкционированы только деньги | подтвердить задним числом или отменить |
|
||||
| 2 | Потолок пака **$4.00** и запас **$5** — со слов сессии, в репо артефакта нет | подтвердить провенанс |
|
||||
| 3 | **Резерв D39.22** закрывать не мне: это решение владельца (§13) | принять к сведению |
|
||||
| 4 | Правило «судейская сессия без пойманного декоя аннулируется целиком» живёт только в промте ЧУЖОГО пака; я применил его к себе дважды | ратифицировать как норму или отменить |
|
||||
| 5 | Потолок ФАЗЫ поднимался дважды в ходе пака, резерв ушёл не на объявленное назначение, пинга не было (§6); пакетный потолок не пробит | норма или запрет |
|
||||
| 6 | Учёт агент-запусков до этого пере-прогона вёлся мной, а не механизмом (§6); с пере-судейства `border` он персистируется, но задним числом первые 38 восстановить нечем | принять со слов или считать неучтённым |
|
||||
| 7 | Фриз фазы A пака 23 попутно изменил риг эксп-22 (§8 п.9). Его гейты пере-сняты сейчас — `verify22.py` и `itog22.py` зелёные, код возврата 0 | к сведению; вопрос только в том, нормально ли править чужой пак под своим заголовком |
|
||||
| 8 | Провенанс цен OpenAI и xAI — снимка вендор-страницы нет, только живой листинг `/models` и комментарий в коде | принять или требовать снимок |
|
||||
| 9 | Пере-судейство прохода `border` — 12 агент-сессий, денег $0; всего по паку **58 сессий** при капе 60. ⚠ Поправка фазы Д (п.4): здесь стояло «50» и «теперь считает механизм» — первое не считало репликацию (§12а), второе прямо опровергнуто §6: на диске 20 записей против 58 фактических, СЧЁТЧИК КАПА НЕ РАБОТАЕТ. Персистирована только раскладка судей по единицам | санкция задним числом |
|
||||
|
||||
---
|
||||
|
||||
# ФАЗА Д — ДОВОДКА ЭКСП-22/23 (заказ владельца 10.08)
|
||||
|
||||
Исполнение `docs/POLYGON_EXP2223_REDO_SESSION_PROMPT.md`. Это НЕ новый эксперимент: выводы паков
|
||||
22 и 23 признаны владельцем непоказательными **по постановке** и заморожены до этой фазы. Зона —
|
||||
`eval/dovodka/` + правки рига паков 22/23 + эти секции.
|
||||
|
||||
## Д0 ПРЕ-РЕГ (зафризен своим коммитом ДО первой покупки)
|
||||
|
||||
### Д0.1 Гипотезы владельца — ДОСЛОВНО из заказа
|
||||
|
||||
> **H-1 «Проблема в черновике»**: flash даёт полумусор, полный перепис нужен именно поэтому;
|
||||
> связка «качественный черновик + ТОЧЕЧНЫЙ редактор» ≥ боевой связки по качеству при сравнимой цене.
|
||||
>
|
||||
> **H-2а «Флаговый edit-контур»**: детерминированные флаги (батарея + канон-гейт) → точечный
|
||||
> фиксер — не хуже full-regen по судье, дешевле по деньгам, НЕ ломает канон.
|
||||
>
|
||||
> **H-2б «Смысловой edit-контур» (главная ставка владельца, 10.08)**: LLM-критик, читающий и
|
||||
> ПОНИМАЮЩИЙ текст («найди всё проблемное») → точечный фиксер — дороже (поиск ≈2.14× переписа),
|
||||
> но качество выше и full-regen, и флагового контура: «флагами всё не отследишь». Цена заложена
|
||||
> в смету сознательно — гипотеза о КАЧЕСТВЕ, не об экономии.
|
||||
>
|
||||
> **H-3 «en: перепис не нужен»**: на en→ru редактору остаются только синк глоссария и точечные правки.
|
||||
>
|
||||
> **H-4 «dspro-китайскость»**: перевес dspro в редакторской роли — свойство пары zh→ru; на en/ja
|
||||
> порядок жильцов может смениться (dspro — китайская модель, книга — китайская).
|
||||
|
||||
### Д0.2 ⛔ ДВА АРМА ЗАВЕДЕНЫ ПРИЁМКОЙ, А НЕ АВТОРОМ — и без них две гипотезы не мерились
|
||||
|
||||
Первая редакция плана этой фазы **не отвечала на H-1 и H-4**, и нашла это не сессия, а приёмка
|
||||
другого модельного семейства (Fable-5, разрешение владельца D39.120 наконец использовано):
|
||||
|
||||
* **H-1** говорит о связке «КАЧЕСТВЕННЫЙ черновик + точечный редактор». Ни один арм плана такой
|
||||
связки не содержал: `A1`/`A3` ставят фиксер на flash-черновик — то есть на полумусор по самой
|
||||
формулировке гипотезы, — а `A2` есть однопроходка вовсе без редактора. Гипотеза «отвечалась» бы
|
||||
интерполяцией между армами, где взаимодействие «фиксер × качество базы» не меряется вовсе.
|
||||
⇒ заведён арм **A6 = dspro-черновик эксп-22 + оба контура**. Черновики уже куплены ($0 за базу).
|
||||
* **H-4** говорит о смене ПОРЯДКА жильцов на другой паре. Порядок проверяется только панелью из
|
||||
НЕСКОЛЬКИХ редакторов, а на en-оси редактор стоял один — `deepseek-v4-pro`. Фаза сказала бы
|
||||
«сколько покупает редактура на каждой паре» (это H-3), но не «остаётся ли dspro лучшим».
|
||||
⇒ заведён арм **E6 = второй редактор `glm-5` на en поверх той же базы**.
|
||||
|
||||
Стоимость починки $0.48; потолок фазы поднят владельцем $4.00 → **$4.50** ровно под неё.
|
||||
|
||||
### Д0.3 Мощность каждой оси — ДО покупок (`eval/dovodka/power.py`)
|
||||
|
||||
```
|
||||
ось n k sd MDE ЦЕЛЬ потолок p СТАТУС
|
||||
Д4 edit-контур zh 32 5 2.12 1.29 1.50 0.0000 НЕСУЩАЯ
|
||||
Д3 en→ru несущая 16 5 2.12 1.83 2.00 0.0002 НЕСУЩАЯ
|
||||
Д6 ja→ru разведка 9 3 2.90 3.33 2.00 0.0117 ⛔ ОПИСАТЕЛЬНАЯ
|
||||
Д1 gemini добивка 16 1 2.12 1.83 2.00 0.0000 НЕСУЩАЯ
|
||||
```
|
||||
|
||||
**ЦЕЛЬ — искомый эффект, объявленный ДО денег.** Без неё MDE не решает ничего: печатать «MDE 1.83»
|
||||
и не сказать, меньше он искомого или больше, — ровно та форма, за которую погорела en-ось эксп-22
|
||||
(там при n=6 и k=6 потолок p был 0.1875, то есть значимость недостижима ПО ПОСТРОЕНИЮ, и выяснилось
|
||||
это после денег). Ось объявляется описательной, если MDE больше цели ЛИБО значимость недостижима.
|
||||
|
||||
⚠ **Следствие, которое надо знать заранее:** при n=12 (минимум промта) ось Д1 имела бы MDE 2.11
|
||||
против цели 2.00 и была бы ОПИСАТЕЛЬНОЙ. Несущей её делает решение владельца взять полные 16.
|
||||
|
||||
⚠ **Прайор шума — не свой пол.** Своего пола у будущего прохода нет по построению; берётся худший
|
||||
из замеренных на той же структуре армов (`border`, sd 2.12). **Решает СВОЙ пол**, и если он выйдет
|
||||
хуже прайора, ось пере-классифицируется в описательную ТОГДА ЖЕ — до чтения боевых перевесов.
|
||||
|
||||
### Д0.4 ⛔ ЗАПАС ЭКВИВАЛЕНТНОСТИ для H-2а — иначе «не хуже» доказать нечем
|
||||
|
||||
H-2а утверждает, что флаговый контур **НЕ ХУЖЕ** полного переписа. Риг устроен так, что
|
||||
«ниже порога различимости» **не означает «равны»** — это записано во всех отчётах и здесь не
|
||||
меняется. Значит подтвердить H-2а обычным контрастом невозможно в принципе: отсутствие значимой
|
||||
разницы не есть равенство, и CONFIRM был бы сделан формулировкой, а не числом.
|
||||
|
||||
**Пред-объявляю правило non-inferiority:** `A1` (и `A3`) признаётся «не хуже» `A0`, если **нижняя
|
||||
граница 95% ДИ контраста выше −1.50** — той же величины, что объявлена целью оси Д4 (треть
|
||||
собственного эффекта редактора +4.44 в эксп-22 Ф3). Три исхода и все три названы заранее:
|
||||
|
||||
* нижняя граница > −1.50 **и** верхняя < +1.50 → **НЕ ХУЖЕ** (H-2а подтверждена по качеству);
|
||||
* верхняя граница < −1.50 → **ХУЖЕ** (H-2а опровергнута);
|
||||
* интервал накрывает −1.50 → **НЕ УСТАНОВЛЕНО** при данном n, и это честный третий исход, а не
|
||||
провал: мощность объявлена, доборы обсуждаются с владельцем.
|
||||
|
||||
### Д0.5 Оси, армы и семейства контрастов (впечатываются в ключ ДО первого ответа)
|
||||
|
||||
**Д4 — edit-контур zh, 32 единицы (16 эксп-22 + 16 эксп-23), база одна и замороженная:**
|
||||
|
||||
```
|
||||
A0 боевой full-regen (deepseek-v4-pro поверх якорного черновика) КУПЛЕН, $0
|
||||
A1 черновик + детерминированные флаги (батарея + канон-гейт) → фиксер «ВМЕСТО редактора»
|
||||
A2 dspro-однопроходка УРАВНЕННОГО мандата (строка 153 бэклога) без редактора вовсе
|
||||
A3 черновик + СМЫСЛОВОЙ LLM-критик → фиксер ← СТАВКА ВЛАДЕЛЬЦА H-2б «ВМЕСТО редактора»
|
||||
A4 A0 + канон-фиксер ПОСЛЕ «ПОСЛЕ редактора»
|
||||
A6 dspro-ЧЕРНОВИК + оба контура ← носитель H-1, база куплена эксп-22
|
||||
семейство: A1/A0 · A2/A0 · A3/A0 · A4/A0 · A6/A0 Холм по пяти
|
||||
```
|
||||
«До редактора» не мерится сознательно: перепис затирает любую починку.
|
||||
|
||||
**Д3 — en→ru, 16 единиц Кристоффа:** боевая связка · dspro-однопроходка уравненного мандата ·
|
||||
черновик + оба контура · **E6 второй редактор `glm-5`** · контроль `E0 vs D0`. Холм по пяти.
|
||||
|
||||
**Д6 — ja→ru, 9 единиц:** связка · однопроходка · контроль. **Статус — РАЗВЕДКА, объявлен здесь.**
|
||||
|
||||
### Д0.6 Нормы рига, принятые этой фазой (каждая куплена дорого прошлым паком)
|
||||
|
||||
* **Декой в каждой пачке**; сессия, не поймавшая его отрицательным знаком, аннулируется целиком.
|
||||
* **Шумовой пол — парой, чьи половины судят РАЗНЫЕ сессии.** Обе половины — ОТДЕЛЬНЫЕ генерации;
|
||||
боевая клетка половиной пола не берётся (в эксп-23 `CTRLfloorA ≡ T1` в 16/16, и контроль был
|
||||
пуст). Порог берётся из своего пола без ручной поправки: пол, судимый разными сессиями, ловит
|
||||
и межсессионную компоненту, которая в эксп-23 занижала порог на 19%.
|
||||
* **Все армы единицы — в одном задании одной сессии** (урок Sol §16 эксп-22, подтверждён с двух
|
||||
сторон: нарушив — замер разваливается, соблюдя — работает на том же судье).
|
||||
* **Судейские сессии регистрируются ДО запуска** (`eval/dovodka/runs.py`, кап 80, атомарный
|
||||
`O_CREAT|O_EXCL`-замок с проверкой живости PID, селфтест 16/16). Гонка сессий по одному токену
|
||||
механически невозможна — в эксп-23 она стоила расхождения голосов и сырья на 5 единицах из 32.
|
||||
* **Клетка с `finish=length` в судейскую пачку НЕ допускается** — ни боевым армом, ни половиной
|
||||
пола. Класс замерен: в эксп-22 таких судимых слотов семь, две из них в шумовом полу обеих фаз,
|
||||
одна в базе всех контрастов (§15 эксп-22).
|
||||
* **Позиционный наклон** замеряется, вычитается и сторожится гейтом числом.
|
||||
* **Донор декоя уравнен по армам**; судье запрещено сравнивать варианты между собой и читать их рядом.
|
||||
* **Пар-промпты проходят механический гейт** (`eval/dovodka/promptdiff.py`): всё вне объявленных
|
||||
пар-специфичных секций обязано совпадать с боевым zh побайтно, мандатные оговорки не теряются,
|
||||
а каждое законное расхождение объявлено с причиной. Урок эксп-19 (арм конфаундирован неполным
|
||||
зеркалом) закрыт механизмом, а не обещанием.
|
||||
* **$0-детекторы получают свой контроль наравне с судьями.** Норма заведена этой фазой после того,
|
||||
как приёмка замерила ложноположительную частоту канон-классификатора: 82.5% на случайных окнах
|
||||
при наблюдённых 79% — прибор был слабее нулевой модели. **Всякий классификатор, чьё число идёт
|
||||
в отчёт, обязан печатать рядом свою частоту срабатывания на случайном входе.**
|
||||
|
||||
### Д0.7 ⚠ Что внешняя подпись может и чего не может — объявляется ДО прогона
|
||||
|
||||
Внешний судья вне семьи Claude имеет шумовой пол sd 5.05 и порог различимости 3.53 (§12в). Это
|
||||
значит, что под ОТРИЦАТЕЛЬНЫМ выводом его подпись почти автоматична: возразить он мог бы только
|
||||
эффекту вчетверо больше замеренного. Поэтому заранее:
|
||||
|
||||
* для прохода `tier` (перевесы 0.19…0.50) внешняя подпись будет означать «внешний судья тоже не
|
||||
увидел», а НЕ «внешний судья подтвердил равенство»;
|
||||
* для несущего вердикта Д4 по H-2б, где ставка — тонкий выигрыш, внешний контур не добавит и не
|
||||
отнимет; его ценность — поймать КАТАСТРОФУ, которой внутрисемейные судьи не заметили.
|
||||
|
||||
Это ограничение прибора, а не отговорка: сказано до того, как числа получены.
|
||||
|
||||
### Д0.8 Смета по ЗАМЕРЕННЫМ ценам и фазовые потолки (`eval/dovodka/plan.py`)
|
||||
|
||||
```
|
||||
ФД-A Д4 edit-контур zh (A1·A2·A3·A4, n=32 + свой пол) 0.877
|
||||
ФД-B Д3 en→ru несущая 0.556
|
||||
ФД-C Д6 ja→ru разведка (n=9) 0.154
|
||||
ФД-D Д7 самооценка 0.050
|
||||
ФД-F H-1: A6, dspro-черновик + оба контура (n=16) 0.280
|
||||
ФД-G H-4: E6, второй редактор glm-5 на en (n=16) 0.200
|
||||
ФД-E Д1 добивка gemini, 16 клеток 2.352
|
||||
ИТОГО 4.470 / 4.50 · резерв 0.030
|
||||
```
|
||||
Цены — медианы по СЫРЬЮ уже купленных клеток обоих паков, не по прайсу. Прайс DeepSeek пере-снят
|
||||
живьём 10.08 и **не изменился** (flash $0.14/$0.28, pro $0.435/$0.87); вендорская сноска о
|
||||
готовящемся значительном повышении на странице присутствует и учтена оговоркой, а не числом.
|
||||
|
||||
⚠ **Резерв $0.03 — это 0.7% пакета, и сессия объявила это риском ДО покупок.** Ре-гены эхо-мины и
|
||||
ретраи в паках 22/23 съедали до +16% сверх сметы фазы. Честным потолком без стопов посреди фазы
|
||||
сессия называла $4.80; владелец выбрал $4.50. **Срабатывание проекционного гарда = СТОП и вопрос
|
||||
владельцу**, а не сокращение оси решением сессии.
|
||||
|
||||
### Д0.9 Прогнозы (калибруют удивление; совпадение НЕ цель)
|
||||
|
||||
1. **A3 (смысловой критик) НЕ побьёт A0 различимо.** Основание: все замеренные различия между
|
||||
близкими редакторскими контурами лежат в 0.2–2.5, а MDE оси 1.29. Прогноз конкретен: перевес
|
||||
A3/A0 ляжет в полосу −1.0…+1.0. **Если владелец прав и ставка сыграет, я ошибусь — и это
|
||||
лучший исход фазы.**
|
||||
2. **A4 (канон-фиксер ПОСЛЕ переписа) даст лучшее покрытие канона из всей панели**, не двигая
|
||||
судейский перевес: он чинит ось, по которой боевой редактор худший в эксп-22 (0.882).
|
||||
3. **A1 (флаги) окажется «не хуже» по правилу Д0.4, но дешевле в разы.** Основание: флаговый
|
||||
контур эксп-20 чинил по указанному месту за ~$0.0002.
|
||||
4. **H-1 подтвердится частично:** A6 (dspro-черновик + контур) побьёт A1/A3 на flash-черновике,
|
||||
но не побьёт A0. Основание — находка эксп-22 §13: редактор работает компрессором различий
|
||||
черновика (разброс 7.25 → 2.12).
|
||||
5. **На en контроль редактора снова окажется около нуля** (эксп-22: +0.00 при n=6), но теперь при
|
||||
n=16 и объявленной цели 2.00 это будет замер, а не отсутствие мощности.
|
||||
6. **`gemini-3.1-pro-preview` не отдаст 16 клеток с первой волны.** Прогноз: 503-серии повторятся,
|
||||
потребуется ≥2 суток окна; собрано будет 12–15 из 16.
|
||||
|
||||
### Д0.10 Чего эта фаза НЕ меряет
|
||||
|
||||
Топология «черновик → редактор» (решена эксп-21) · банк как фактор · выбор жильца черновой роли
|
||||
(решён эксп-22) · качество прозы `gemini` как продуктовый выбор — при $221 за книгу против $8 у
|
||||
боевого он невозможен ни при каком исходе, меряется только гипотеза «аутлаер прозы» эксп-04.
|
||||
311
eval/dovodka/canon.py
Normal file
311
eval/dovodka/canon.py
Normal file
|
|
@ -0,0 +1,311 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Д5 — КАНОН-ВСКРЫТИЕ: где именно боевой редактор теряет покрытие банка. $0.
|
||||
|
||||
Политика владельца 10.08, зафиксирована словом: «Банк существует, чтобы термины единообразно
|
||||
переводились. Всё. Художественность текста — это отдельный вопрос.» ⇒ потеря канона = дефект
|
||||
БЕЗУСЛОВНО, судейских опросов об «уместности замены» не бывает. Задача этого файла — не спорить
|
||||
о том, дефект ли это, а дать фиксеру МЕХАНИЗМ: перечислить места потери и назвать класс каждой.
|
||||
|
||||
Замер идёт на купленном сырье эксп-22, денег не стоит и ничего не покупает.
|
||||
|
||||
ДВЕ РАЗНЫЕ ВЕЛИЧИНЫ, и путать их нельзя:
|
||||
|
||||
1. ПОКРЫТИЕ — доля канонной формы от числа упоминаний В ИСХОДНИКЕ (метрика `bank.coverage`).
|
||||
Именно её печатали эксп-21/22. Она падает и когда термин передан ДРУГИМ переводом, и когда
|
||||
он заменён местоимением — а второе есть норма русской прозы там, где китайская повторяет имя.
|
||||
2. ЕДИНООБРАЗИЕ — то, ради чего банк существует по слову владельца: один термин передаётся
|
||||
ОДНОЙ формой. Меряется подменой РОДОВОГО слова при неизменном якоре («дом Цинь» вместо
|
||||
«род Цинь»), решается счётом по закрытому списку родовых и подпирается цитатой.
|
||||
|
||||
⚠ ЧЕГО ЗДЕСЬ БОЛЬШЕ НЕТ, И ПОЧЕМУ. Первая редакция классифицировала КАЖДОЕ место потери на
|
||||
«исчез / парафраз / другой перевод» по выровненному окну редактуры. Адверсариальное ревью её
|
||||
сняло, и правильно: (а) выравнивание не работает — окно НИ В ОДНОМ из 24 мест не содержало
|
||||
канона, хотя в полном тексте редактуры он стоит 8–18 раз; (б) класс «парафраз» присваивался
|
||||
регексом, который срабатывает на 84% ПРОИЗВОЛЬНЫХ окон того же текста, то есть наблюдённые 79%
|
||||
были НИЖЕ нулевой модели. Вывод «массовый класс — парафраз» на таком приборе не стоит, и он снят.
|
||||
Список мест остался как СЫРЬЁ для фиксера (где искать), но классом он больше не размечается и
|
||||
числом по классам не подводится.
|
||||
|
||||
Запуск: eval/.venv/bin/python eval/dovodka/canon.py [--full] [--term <иероглиф>]
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import difflib
|
||||
import importlib.util
|
||||
import json
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
REPO = Path("/home/ubuntu/projects/textmachine")
|
||||
OUT = Path.home() / "books" / "dovodka"
|
||||
for p in ("tenant_panel", "role_topology"):
|
||||
sys.path.insert(0, str(REPO / "eval" / p))
|
||||
|
||||
|
||||
def _load(name: str, path: Path):
|
||||
spec = importlib.util.spec_from_file_location(name, path)
|
||||
mod = importlib.util.module_from_spec(spec)
|
||||
sys.modules[name] = mod
|
||||
spec.loader.exec_module(mod)
|
||||
return mod
|
||||
|
||||
|
||||
PAN = _load("panel22", REPO / "eval" / "tenant_panel" / "panel.py")
|
||||
BANK = _load("bank22", REPO / "eval" / "tenant_panel" / "bank.py")
|
||||
ED = _load("editors22", REPO / "eval" / "tenant_panel" / "editors.py")
|
||||
MAT = PAN.M # material.py эксп-22
|
||||
|
||||
SENT = re.compile(r"[^.!?…»\n]+[.!?…»]*\s*")
|
||||
|
||||
# ⚠ ЗАКРЫТЫЙ СПИСОК РОДОВЫХ СЛОВ — данные КНИГИ, а не движка. Полигонный замер вправе знать
|
||||
# лексику своего материала (CLAUDE.md: «тест-данные пары легитимны»); в общий пар-слой это
|
||||
# не уходит. Классы заданы по канонам банка: то, чем в русском переводе называют 家 (клан/род).
|
||||
GENERICS = {
|
||||
"род": ("род", "дом", "семья", "семейство", "клан", "фамилия", "поместье"),
|
||||
}
|
||||
|
||||
|
||||
def GENERIC_CLASS(mod: str) -> tuple[str, ...]: # noqa: N802
|
||||
for stem, cls in GENERICS.items():
|
||||
if mod.lower().startswith(stem[:3]) or mod.lower() in cls:
|
||||
return cls
|
||||
return ()
|
||||
|
||||
|
||||
def sents(t: str) -> list[str]:
|
||||
return [s for s in SENT.findall(t or "") if s.strip()]
|
||||
|
||||
|
||||
def best_match(s: str, edit_s: list[str], i: int, n_draft: int) -> int:
|
||||
"""Индекс предложения редактуры, лучше всего отвечающего предложению черновика.
|
||||
|
||||
⚠ Первая редакция выравнивала `difflib`-опкодами по СПИСКУ предложений. На полном переписе это
|
||||
не работает: почти всё уходит в один опкод `replace` на пол-текста, и окно для всех мест
|
||||
единицы получалось одно и то же (поймано глазами при первом же прогоне — все «исчез» на
|
||||
единице `41599f30df` показывали одну и ту же реплику). Здесь ищется ближайшее по содержанию
|
||||
предложение в ПОЛОСЕ вокруг пропорциональной позиции: редактор порядок сохраняет, поэтому
|
||||
полоса законна, а сходство внутри неё решает.
|
||||
"""
|
||||
if not edit_s:
|
||||
return -1
|
||||
c = int(i / max(1, n_draft) * len(edit_s))
|
||||
band = max(4, int(0.15 * len(edit_s)))
|
||||
lo, hi = max(0, c - band), min(len(edit_s), c + band + 1)
|
||||
key = s.strip()
|
||||
best, bj = -1.0, c if c < len(edit_s) else len(edit_s) - 1
|
||||
for j in range(lo, hi):
|
||||
r = difflib.SequenceMatcher(None, key, edit_s[j].strip(), autojunk=False).ratio()
|
||||
if r > best:
|
||||
best, bj = r, j
|
||||
return bj
|
||||
|
||||
|
||||
def window(edit_s: list[str], j: int) -> str:
|
||||
if j < 0:
|
||||
return ""
|
||||
return " ".join(edit_s[max(0, j - 1):min(len(edit_s), j + 2)])
|
||||
|
||||
|
||||
# ⚠ Классификация ужесточена после ЧТЕНИЯ первых мест (мандат D39.61: вскрыть единицы до вывода).
|
||||
# Первая редакция звала «другим переводом» ЛЮБОЕ заглавное слово в окне — и записала в этот класс
|
||||
# `Этот человек`, `Кошмар`, `Перед такой`, то есть соседние слова предложения. Так класс «другой
|
||||
# перевод» набрал 14 мест из 24 и был бы вписан в отчёт неверным. Правило переписано:
|
||||
# `другой` — в окне стоит форма, ПОХОЖАЯ на канон (Цин Фэн / Цинь Фен / Qin Feng), но не он:
|
||||
# это и есть расхождение перевода термина, которое банк обязан устранять;
|
||||
# `парафраз` — на месте термина референциальная замена (местоимение, эпитет, родовое слово);
|
||||
# `исчез` — ни того, ни другого: место есть, термина и его следа нет.
|
||||
# ⚠ Второй элемент ОБЯЗАН начинаться с заглавной. Первая редакция клеила любое следующее слово, и
|
||||
# список «конкурирующих форм» наполнился мусором вида «Цинь в», «Фэн-лао до», «Тяньянчэне был» —
|
||||
# то есть склонениями и предлогами, а не переводами. Поймано чтением собственного вывода.
|
||||
TOKEN = re.compile(r"(?<![\w«\"])([А-ЯЁA-Z][\w’'-]{1,}(?:[- ][А-ЯЁA-Z][\w’'-]{1,})?)")
|
||||
EPITHET = re.compile(r"(?<!\w)(он|она|оно|они|его|её|их|тот|та|этот|эта|"
|
||||
r"юнош\w+|девушк\w+|старик\w+|старейшин\w+|господин\w*|госпож\w+|"
|
||||
r"молод\w+\s+господин\w*|наследник\w*|глав\w+|отец|мать|сын|дочь|"
|
||||
r"человек\w*|мужчин\w+|женщин\w+|"
|
||||
r"род\w*|семь\w+|клан\w*|техник\w+|искусств\w+|ступен\w+|уровн\w+|"
|
||||
r"стади\w+|ядр\w+|фундамент\w*|пилюл\w+)(?!\w)", re.I)
|
||||
|
||||
|
||||
def _near(a: str, b: str) -> float:
|
||||
return difflib.SequenceMatcher(None, a.lower(), b.lower(), autojunk=False).ratio()
|
||||
|
||||
|
||||
def classify(win: str, rx: str, canon: str) -> tuple[str, str]:
|
||||
"""(класс, улика). Улика печатается рядом с цитатой — вердикт проверяем чтением."""
|
||||
if not win:
|
||||
return "исчез", ""
|
||||
if re.search(rx, win, re.I):
|
||||
return "есть", ""
|
||||
# похожая, но не канонная форма того же термина
|
||||
near = [(t, _near(t, canon)) for t in TOKEN.findall(win)]
|
||||
near = [(t, r) for t, r in near if 0.55 <= r < 1.0 and len(t) >= 3]
|
||||
if near:
|
||||
t, r = max(near, key=lambda x: x[1])
|
||||
return "другой", f"{t} (сходство {r:.2f})"
|
||||
m = EPITHET.search(win)
|
||||
if m:
|
||||
return "парафраз", m.group(0)
|
||||
return "исчез", ""
|
||||
|
||||
|
||||
def main() -> int:
|
||||
full = "--full" in sys.argv
|
||||
only = sys.argv[sys.argv.index("--term") + 1] if "--term" in sys.argv else ""
|
||||
bank = BANK.PR.bank()
|
||||
units = MAT.units_zh()
|
||||
# словарь альтернативных написаний: латиница/иная транскрипция того же имени
|
||||
|
||||
rows, per_term, per_class = [], {}, {}
|
||||
gross_loss = gross_gain = 0
|
||||
tot_src = tot_hit_draft = tot_hit_edit = 0
|
||||
# ⚠ Клетка R0 с `finish=length` даёт «потерю канона», которая на деле есть ОБРЫВ: термины
|
||||
# после места обрыва отсутствуют не потому, что редактор их убрал. Такие единицы считаются
|
||||
# отдельно (эксп-22 §15: класс замерен, на этой панели он покрывает единицу `41599f30df`).
|
||||
trunc = set()
|
||||
for u in units:
|
||||
f = PAN.OUT / f"{PAN.tag_edit(PAN.ANCHOR, u['uid'])}.json"
|
||||
if f.exists() and json.loads(f.read_text(encoding="utf-8")).get("finish") == "length":
|
||||
trunc.add(u["uid"])
|
||||
for u in units:
|
||||
uid, src = u["uid"], u["source"]
|
||||
if uid in trunc:
|
||||
continue
|
||||
draft = PAN.draft_text(PAN.ANCHOR, uid)
|
||||
edit = ED.text_of("R0", uid) if hasattr(ED, "text_of") else PAN.edit_text(PAN.ANCHOR, uid)
|
||||
if not (draft and edit):
|
||||
continue
|
||||
ds, es = sents(draft), sents(edit)
|
||||
for term, v in bank.items():
|
||||
if only and term != only:
|
||||
continue
|
||||
n = src.count(term)
|
||||
if not n:
|
||||
continue
|
||||
rx = v["rx"]
|
||||
hd = min(len(re.findall(rx, draft, re.I)), n)
|
||||
he = min(len(re.findall(rx, edit, re.I)), n)
|
||||
tot_src += n
|
||||
tot_hit_draft += hd
|
||||
tot_hit_edit += he
|
||||
if he > hd:
|
||||
gross_gain += he - hd # редактор канон ДОБАВИЛ
|
||||
if he >= hd:
|
||||
continue # редактор канон не терял
|
||||
gross_loss += hd - he
|
||||
# места: предложения черновика с канонной формой, чьё окно в редактуре его потеряло
|
||||
places = []
|
||||
for i, s in enumerate(ds):
|
||||
if not re.search(rx, s, re.I):
|
||||
continue
|
||||
j = best_match(s, es, i, len(ds))
|
||||
win = window(es, j)
|
||||
cls, why = classify(win, rx, v["dst"])
|
||||
if cls == "есть":
|
||||
continue
|
||||
places.append((cls, why, s.strip()[:150], win.strip()[:190]))
|
||||
keep = places[: (hd - he)] or places
|
||||
for cls, why, s, w in keep:
|
||||
rows.append((uid, term, v["dst"], cls, why, s, w))
|
||||
per_class[cls] = per_class.get(cls, 0) + 1
|
||||
per_term.setdefault(term, {"dst": v["dst"], "n": 0})
|
||||
per_term[term]["n"] += 1
|
||||
|
||||
print("Д5 — КАНОН-ВСКРЫТИЕ БОЕВОГО РЕДАКТОРА (R0 поверх якорного черновика, эксп-22, $0)\n")
|
||||
print(f"единиц {len(units)} · из них ИСКЛЮЧЕНО по обрыву клетки R0: {len(trunc)} "
|
||||
f"{sorted(trunc)} · разбирается {len(units) - len(trunc)}")
|
||||
print(f"терминов в банке {len(bank)} · упоминаний в исходниках разбираемых единиц {tot_src}")
|
||||
print(f"покрытие ЧЕРНОВИКА {tot_hit_draft}/{tot_src} = {tot_hit_draft / max(1, tot_src):.3f}"
|
||||
f" · покрытие РЕДАКТУРЫ {tot_hit_edit}/{tot_src} = {tot_hit_edit / max(1, tot_src):.3f}"
|
||||
f" · САЛЬДО {tot_hit_draft - tot_hit_edit}")
|
||||
print(f"⚠ САЛЬДО — не потеря. ВАЛОВАЯ потеря {gross_loss} упоминаний, ВАЛОВОЙ прирост "
|
||||
f"{gross_gain}: в части пар редактор канон ДОБАВИЛ там, где черновик его не держал.\n"
|
||||
" Прежняя редакция печатала одно число «ПОТЕРЯ» рядом с числом мест и читалась так,\n"
|
||||
" будто это одно и то же (поймано ревью). Набор фиксера ниже покрывает валовую потерю,\n"
|
||||
" а не сальдо.")
|
||||
print(f"\nмест, где канонная форма отсутствует в выровненном окне: {len(rows)}")
|
||||
print(" ⚠ КЛАССАМИ НЕ РАЗМЕЧАЮТСЯ. Прежняя разбивка «исчез/парафраз/другой» снята ревью:\n"
|
||||
" выравнивание ненадёжно, а класс «парафраз» слабее нулевой модели. Ниже — сырьё\n"
|
||||
" для фиксера (где смотреть), а не измерение долей.")
|
||||
print("\nПО ТЕРМИНАМ (что фиксер обязан уметь возвращать):")
|
||||
print(f"{'термин':10s}{'канон':30s}{'мест потери':>12s}")
|
||||
for t, d in sorted(per_term.items(), key=lambda kv: -kv[1]["n"]):
|
||||
print(f"{t:10s}{d['dst']:30s}{d['n']:12d}")
|
||||
print("\nМЕСТА (цитатой; окно приблизительное — сверять глазами, это подсказка, не вердикт):")
|
||||
for uid, term, dst, _cls, _why, s, w in (rows if full else rows[:12]):
|
||||
print(f"\n {uid} · {term} → «{dst}»")
|
||||
print(f" черновик: …{s}…")
|
||||
print(f" редактура: …{w}…")
|
||||
if not full and len(rows) > 12:
|
||||
print(f"\n … ещё {len(rows) - 12} мест, целиком — с флагом --full")
|
||||
# ── ВТОРАЯ МЕТРИКА: ЕДИНООБРАЗИЕ, а не частота ─────────────────────────────────────────────
|
||||
# ⚠ ПЕРВАЯ РЕДАКЦИЯ ЭТОЙ МЕТРИКИ НЕ РАБОТАЛА И БЫЛА СНЯТА АДВЕРСАРИАЛЬНЫМ РЕВЬЮ. Она искала
|
||||
# «похожие на канон строки» по расстоянию Левенштейна и набирала склонения («Цао Ина») и чужих
|
||||
# персонажей с общей фамилией («Цинь Вэньтянь»); настоящие нарушения — «дом Цинь» вместо «род
|
||||
# Цинь» — не находила вовсе, потому что требовала заглавной буквы у первого слова. Ревьюер
|
||||
# нашёл их РУКАМИ, и это и есть доказательство, что счётом их взять можно: они отличаются
|
||||
# РОДОВЫМ СЛОВОМ при том же якоре.
|
||||
#
|
||||
# Правило теперь такое: у канона вида «<родовое> <Имя>» якорь — имя, а родовое слово меняется.
|
||||
# Считаем распределение родовых слов при якоре ПО ЗАКРЫТОМУ СПИСКУ и зовём нарушением всякое,
|
||||
# чей корень отличается от канонного. Список — данные КНИГИ (полигонный замер, не движок).
|
||||
print("\n" + "═" * 78)
|
||||
print("ЕДИНООБРАЗИЕ: каким РОДОВЫМ словом передан термин при том же якоре")
|
||||
viol, seen_any = [], False
|
||||
for term, v in bank.items():
|
||||
canon = v["dst"]
|
||||
toks = canon.split()
|
||||
if len(toks) != 2:
|
||||
continue
|
||||
mod, anchor = toks
|
||||
gen = GENERIC_CLASS(mod)
|
||||
if not gen:
|
||||
continue
|
||||
seen_any = True
|
||||
rx = re.compile(r"(?<![\w-])(" + "|".join(gen) + r")\w*\s+" + re.escape(anchor[:4]) + r"\w*",
|
||||
re.I | re.U)
|
||||
cnt: dict[str, int] = {}
|
||||
places: dict[str, list[tuple[str, str]]] = {}
|
||||
for u in units:
|
||||
if u["uid"] in trunc:
|
||||
continue
|
||||
txt = ED.text_of("R0", u["uid"]) or ""
|
||||
for m in rx.finditer(txt):
|
||||
g = m.group(1).lower()
|
||||
cnt[g] = cnt.get(g, 0) + 1
|
||||
if not g.startswith(mod.lower()[:3]):
|
||||
places.setdefault(g, []).append(
|
||||
(u["uid"], txt[max(0, m.start() - 60):m.end() + 60].replace("\n", " ")))
|
||||
if not cnt:
|
||||
continue
|
||||
tot = sum(cnt.values())
|
||||
line = " · ".join(f"{g} {n}" for g, n in sorted(cnt.items(), key=lambda kv: -kv[1]))
|
||||
print(f"\n {term} → канон «{canon}» (якорь «{anchor}»): {line}")
|
||||
for g, ps in places.items():
|
||||
share = cnt[g] / tot
|
||||
viol.append((term, canon, g, cnt[g], share))
|
||||
print(f" ⛔ КОНКУРИРУЮЩЕЕ РОДОВОЕ «{g} {anchor}» — {cnt[g]} из {tot} "
|
||||
f"({share * 100:.0f}%)")
|
||||
for uid, q in ps[:2]:
|
||||
print(f" {uid}: …{q.strip()}…")
|
||||
if not seen_any:
|
||||
print(" канонов вида «<родовое> <Имя>» в банке нет")
|
||||
print(f"\nНАРУШЕНИЙ ЕДИНООБРАЗИЯ (конкурирующее родовое при том же якоре): {len(viol)}")
|
||||
print("⚠ ЧТО ЭТА МЕТРИКА ЛОВИТ И ЧЕГО НЕ ЛОВИТ. Ловит подмену РОДОВОГО слова при неизменном\n"
|
||||
" якоре — класс, ради которого банк и существует по слову владельца. НЕ ловит: подмену\n"
|
||||
" самого якоря (транскрипция имени), расхождения у одно-словных канонов и у канонов\n"
|
||||
" вида «прилагательное + существительное» (金丹 «Золотое ядро» → «Золотая пилюля»:\n"
|
||||
" найдено ЧТЕНИЕМ на единице `c73f4857e7`, счётом здесь не берётся). Список родовых —\n"
|
||||
" закрытый и книго-специфичный; его полнота и есть граница метрики.")
|
||||
|
||||
OUT.mkdir(parents=True, exist_ok=True)
|
||||
(OUT / "canon-dissect.json").write_text(json.dumps(
|
||||
[dict(uid=a, term=b, dst=c, cls=d, why=e, draft=f, edit=g)
|
||||
for a, b, c, d, e, f, g in rows],
|
||||
ensure_ascii=False, indent=1), encoding="utf-8")
|
||||
print(f"\nперсист → {OUT / 'canon-dissect.json'}")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
173
eval/dovodka/material_ja.py
Normal file
173
eval/dovodka/material_ja.py
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Д6 — МАТЕРИАЛ ПАРЫ ja→ru: гардрейл, гейт прав, отбор единиц. $0.
|
||||
|
||||
Источник: `~/books/isekai_majutsushi_jp.txt` (файл владельца, вне git — правило Р8). Площадка
|
||||
`novel18.syosetu.com`, то есть срез ЗАВЕДОМО взрослый; аннотация книги сама называет 洗脳 и 暴力.
|
||||
|
||||
⚠⚠ ГАРДРЕЙЛ ПЕРВЫМ, ДО ЛЮБОГО ДРУГОГО ДЕЙСТВИЯ. Гардрейл владельца (вынесен в `eval/README.md`):
|
||||
**18+ уровень 3 — несовершеннолетние в сексуальном контексте — не обрабатывать и не анализировать
|
||||
ни в каком виде.** Здесь он реализован механически: чанк, где ПЛОТНО совпадают маркеры возраста и
|
||||
явные сексуальные маркеры, помечается и материал целиком уходит в СТОП с пингом владельцу. Ни один
|
||||
такой чанк не печатается и дальше по конвейеру не идёт — печатается только его номер и счёт
|
||||
маркеров. Детектор существует, чтобы ИСКЛЮЧАТЬ, и его срабатывание — не повод читать внимательнее.
|
||||
|
||||
Дальше — та же лестница, что у эксп-22 §0.2, и по тем же причинам:
|
||||
1. гейт ПРАВ (маршрутизация по договорам провайдеров, не по поведению модели): у `deepseek` и
|
||||
`zai` класс `sexually-explicit` — FORBIDDEN, поэтому верхний дециль по плотности эротических
|
||||
маркеров снимается ДО отбора единиц;
|
||||
2. отбор единиц детерминированный: дедуп по подписи, ключ артефакта — хеш источника, сортировка
|
||||
по (длина, uid), из середины распределения длин;
|
||||
3. манифест: единицы прикалываются к файлу, иначе докачка материала молча сдвинет уже
|
||||
купленный замер (ровно это случилось с эксп-22 и чинилось в паке 23).
|
||||
|
||||
Запуск: eval/.venv/bin/python eval/dovodka/material_ja.py [--units]
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import hashlib
|
||||
import json
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
SRC = Path.home() / "books" / "isekai_majutsushi_jp.txt"
|
||||
OUT = Path.home() / "books" / "dovodka"
|
||||
MANIFEST = OUT / "manifest-ja.json"
|
||||
TARGET_CHARS, MIN_CHARS, N_UNITS = 1100, 700, 9 # ja плотнее zh на знак: цель короче
|
||||
|
||||
# ── маркеры гардрейла ─────────────────────────────────────────────────────────────────────────
|
||||
AGE = re.compile(r"(小学生|中学生|高校生|幼女|幼児|少女|少年|児童|子供|子ども|ロリ|"
|
||||
r"[0-90-9一二三四五六七八九十]{1,2}\s*[歳才])")
|
||||
SEX = re.compile(r"(性行為|セックス|挿入|射精|愛撫|陰茎|陰部|膣|乳房|裸体|全裸|絶頂|喘|"
|
||||
r"犯[すされし]|強姦|レイプ|淫|媚薬|発情)")
|
||||
# Эротическая плотность для ГЕЙТА ПРАВ — шире, чем гардрейл: это про договоры провайдеров.
|
||||
EROTIC = re.compile(r"(キス|口づけ|抱[きく]しめ|裸|胸|尻|太もも|下着|色気|艶|情事|房事|"
|
||||
r"性的|欲情|快感|喘|淫|媚薬|発情|愛撫|挿入|射精|セックス|性行為)")
|
||||
|
||||
|
||||
def text() -> str:
|
||||
if not SRC.exists():
|
||||
raise SystemExit(f"⛔ материала нет: {SRC}")
|
||||
return SRC.read_text(encoding="utf-8", errors="replace")
|
||||
|
||||
|
||||
def chapters() -> list[dict]:
|
||||
"""Главы по разделителю площадки; заголовок остаётся в теле — он часть материала."""
|
||||
raw = text()
|
||||
parts = re.split(r"\n=+\n", raw)
|
||||
out = []
|
||||
for i, p in enumerate(parts):
|
||||
p = p.strip()
|
||||
if len(p) >= MIN_CHARS:
|
||||
out.append(dict(idx=i, text=p))
|
||||
return out
|
||||
|
||||
|
||||
def chunks() -> list[dict]:
|
||||
"""Абзацные чанки; абзац не рвётся, хвост главы прирастает к предыдущему (правило эксп-22)."""
|
||||
out = []
|
||||
for ch in chapters():
|
||||
made, buf, size = [], [], 0
|
||||
for para in ch["text"].split("\n"):
|
||||
para = para.strip()
|
||||
if not para:
|
||||
continue
|
||||
buf.append(para)
|
||||
size += len(para)
|
||||
if size >= TARGET_CHARS:
|
||||
made.append(dict(text="\n".join(buf), chapter=ch["idx"]))
|
||||
buf, size = [], 0
|
||||
if size:
|
||||
tail = "\n".join(buf)
|
||||
if size >= MIN_CHARS or not made:
|
||||
made.append(dict(text=tail, chapter=ch["idx"]))
|
||||
else:
|
||||
made[-1]["text"] += "\n" + tail
|
||||
out += made
|
||||
return out
|
||||
|
||||
|
||||
def uid_of(s: str) -> str:
|
||||
return hashlib.sha1(s.encode("utf-8")).hexdigest()[:10] # noqa: S324
|
||||
|
||||
|
||||
def guardrail(cs: list[dict]) -> list[int]:
|
||||
"""Индексы чанков КЛАССА, КОТОРЫЙ НЕ ОБРАБАТЫВАЕТСЯ. Содержимое не печатается никогда."""
|
||||
hits = []
|
||||
for i, c in enumerate(cs):
|
||||
t = c["text"]
|
||||
a, s = len(AGE.findall(t)), len(SEX.findall(t))
|
||||
if a and s and (a + s) >= 4:
|
||||
hits.append(i)
|
||||
return hits
|
||||
|
||||
|
||||
def main() -> int:
|
||||
cs = chunks()
|
||||
print(f"Д6 МАТЕРИАЛ ja→ru · источник {SRC.name} · знаков {len(text())} · чанков {len(cs)}")
|
||||
|
||||
print("\n=== ГАРДРЕЙЛ 18+ УРОВНЯ 3 (проверяется ПЕРВЫМ) ===")
|
||||
bad = guardrail(cs)
|
||||
if bad:
|
||||
print(f"⛔ СТОП. Чанков с плотным совпадением возрастных и явных сексуальных маркеров: "
|
||||
f"{len(bad)} из {len(cs)} (номера: {bad[:12]}{'…' if len(bad) > 12 else ''}).")
|
||||
print(" Содержимое НЕ печатается и дальше не идёт. Материал непригоден по гардрейлу\n"
|
||||
" владельца; замена без его слова НЕ ищется (промт: «непригоден по пробе → СТОП\n"
|
||||
" и пинг»).")
|
||||
if "--owner-sanctioned-exclude" not in sys.argv:
|
||||
print("\n Детектор КОНСЕРВАТИВЕН намеренно: 少年/少女/子供 — частотные слова японской\n"
|
||||
" прозы, и совпасть с эротическим маркером они могут безобидно. Развести это\n"
|
||||
" можно только ЧТЕНИЕМ помеченных мест, а читать их правило запрещает — значит\n"
|
||||
" решает не сессия. Обход существует и требует слова владельца:\n"
|
||||
" --owner-sanctioned-exclude помеченные чанки выбрасываются механически,\n"
|
||||
" остальные 181 идут в отбор; в отчёт пишется,\n"
|
||||
" сколько и почему выброшено.\n"
|
||||
" Прекращаю обработку источника.")
|
||||
return 1
|
||||
print(f"\n ⚠ ОБХОД ПО СЛОВУ ВЛАДЕЛЬЦА: {len(bad)} помеченных чанков выброшены "
|
||||
"механически, без чтения.")
|
||||
cs = [c for i, c in enumerate(cs) if i not in set(bad)]
|
||||
print(f" осталось чанков: {len(cs)}")
|
||||
else:
|
||||
print(f"[OK ] срабатываний нет: 0 из {len(cs)} чанков")
|
||||
|
||||
print("\n=== ГЕЙТ ПРАВ (верхний дециль эротической плотности снимается ДО отбора) ===")
|
||||
dens = sorted(((len(EROTIC.findall(c["text"])) / max(1, len(c["text"])) * 1000, i)
|
||||
for i, c in enumerate(cs)), reverse=True)
|
||||
cut = max(1, len(cs) // 10)
|
||||
dropped = {i for _, i in dens[:cut]}
|
||||
print(f"снято чанков {len(dropped)} из {len(cs)} (дециль); плотность верхнего "
|
||||
f"{dens[0][0]:.2f}/1000 знаков, порога дециля {dens[cut - 1][0]:.2f}, медиана "
|
||||
f"{dens[len(dens) // 2][0]:.2f}")
|
||||
pool = [c for i, c in enumerate(cs) if i not in dropped]
|
||||
|
||||
print("\n=== ОТБОР ЕДИНИЦ (детерминированный, методика эксп-21 §0) ===")
|
||||
uniq: dict[str, dict] = {}
|
||||
for c in pool:
|
||||
u = uid_of(c["text"])
|
||||
uniq.setdefault(u, dict(uid=u, source=c["text"], chapter=c["chapter"]))
|
||||
ordered = sorted(uniq.values(), key=lambda x: (len(x["source"]), x["uid"]))
|
||||
mid = len(ordered) // 2
|
||||
lo = max(0, mid - N_UNITS // 2)
|
||||
units = ordered[lo:lo + N_UNITS]
|
||||
ls = [len(u["source"]) for u in units]
|
||||
print(f"пул {len(ordered)} уникальных · выбрано {len(units)} из середины распределения длин")
|
||||
print(f"знаков {min(ls)}…{max(ls)}, медиана {sorted(ls)[len(ls) // 2]} · "
|
||||
f"глав {len({u['chapter'] for u in units})}")
|
||||
for u in units:
|
||||
print(f" {u['uid']} гл.{u['chapter']:<4d} знаков {len(u['source']):5d}")
|
||||
|
||||
if "--units" in sys.argv:
|
||||
OUT.mkdir(parents=True, exist_ok=True)
|
||||
MANIFEST.write_text(json.dumps(
|
||||
dict(source=SRC.name, n_chunks=len(cs), dropped_rights=len(dropped),
|
||||
ja=dict(uids=[u["uid"] for u in units])), ensure_ascii=False, indent=1),
|
||||
encoding="utf-8")
|
||||
print(f"\nманифест → {MANIFEST} (единицы приколоты; докачка их больше не сдвинет)")
|
||||
else:
|
||||
print("\n(манифест НЕ записан — прогнать с флагом --units, когда состав утверждён)")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
139
eval/dovodka/money_d.py
Normal file
139
eval/dovodka/money_d.py
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
#!/usr/bin/env python3
|
||||
"""КАССА ФАЗЫ Д. $0 сама по себе; через неё идут ВСЕ платные вызовы фазы.
|
||||
|
||||
Не копия кассы эксп-22, а её НАСТРОЙКА через `configure` — тот же приём, которым пользовался пак
|
||||
23. Механизм там проверенный и дорого доставшийся: фриз-гейт (покупка отказывает коду, который не
|
||||
закоммичен или отличается от закоммиченного), атомарный замок клетки `O_CREAT|O_EXCL`, проекционный
|
||||
сторож перед КАЖДЫМ вызовом по цене ИМЕННО ЭТОЙ модели, два независимых пути счёта. Копирование
|
||||
размножило бы будущие расхождения вместо того, чтобы их ловить.
|
||||
|
||||
ПОТОЛОК ФАЗЫ — САНКЦИЯ ВЛАДЕЛЬЦА $4.50 (10.08). История цифры: промт называл $10.00, владелец при
|
||||
запуске сказал $4.00, затем поднял до $4.50, когда приёмка другого модельного семейства нашла, что
|
||||
дизайн не отвечает на H-1 и H-4, а починка стоит $0.48.
|
||||
|
||||
⚠ РЕЗЕРВ ФАЗЫ $0.03 — 0.7% пакета, и это объявлено риском ДО покупок (Д0.8). Ре-гены эхо-мины и
|
||||
ретраи в паках 22/23 съедали до +16% сверх сметы фазы. Сессия называла честным потолком $4.80;
|
||||
владелец выбрал $4.50. Поэтому правило исполнения жёсткое и записано здесь, а не в прозе:
|
||||
**срабатывание проекционного гарда = СТОП и вопрос владельцу.** Резать ось, панель или кандидата
|
||||
решением сессии ЗАПРЕЩЕНО заказом; поднимать потолок себе — тем более (пак 23 так и сделал, и это
|
||||
стоило ему пункта CONFIRM/DENY).
|
||||
|
||||
Запуск: money_d.py [--report|--selftest]
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
REPO = Path("/home/ubuntu/projects/textmachine")
|
||||
ZONE = Path(__file__).resolve().parent
|
||||
sys.path.insert(0, str(ZONE))
|
||||
sys.path.insert(0, str(REPO / "eval" / "tenant_panel"))
|
||||
sys.path.insert(0, str(REPO / "eval" / "role_topology"))
|
||||
|
||||
|
||||
def _load(name: str, path: Path):
|
||||
"""⚠ Чужое — ТОЛЬКО по пути. `import money` находил кассу эксп-22 или сам себя, смотря по
|
||||
порядку `sys.path.insert`; пак 23 напоролся на это четырежды, и один раз молча."""
|
||||
import importlib.util # noqa: PLC0415
|
||||
spec = importlib.util.spec_from_file_location(name, path)
|
||||
mod = importlib.util.module_from_spec(spec)
|
||||
sys.modules[name] = mod
|
||||
spec.loader.exec_module(mod)
|
||||
return mod
|
||||
|
||||
|
||||
M22 = _load("money22", REPO / "eval" / "tenant_panel" / "money.py")
|
||||
ROSTER = _load("roster22", REPO / "eval" / "tenant_panel" / "roster.py")
|
||||
|
||||
OUT = Path.home() / "books" / "dovodka"
|
||||
|
||||
# Теги покупок по фазам. ⚠ Новый тег вписывается СЮДА тем же коммитом, что и код, который его
|
||||
# заводит: расход мимо глоба невидим для кассы, и эксп-21 напоролся на это трижды, а последний
|
||||
# раз гейт заниженное число ещё и СЕРТИФИЦИРОВАЛ.
|
||||
TAGS = {
|
||||
"ФД-A": ("dv-a-*.json",), # Д4 edit-контур zh: A1 A2 A3 A4 + свой шумовой пол
|
||||
"ФД-B": ("dv-b-*.json",), # Д3 en→ru несущая ось: банк, контам, армы, пол
|
||||
"ФД-C": ("dv-c-*.json",), # Д6 ja→ru разведка
|
||||
"ФД-D": ("dv-d-*.json",), # Д7 микро-проба самооценки
|
||||
"ФД-E": ("dv-e-*.json",), # Д1 добивка gemini-3.1-pro-preview
|
||||
"ФД-F": ("dv-f-*.json",), # H-1: A6 dspro-черновик + оба контура
|
||||
"ФД-G": ("dv-g-*.json",), # H-4: E6 второй редактор на en
|
||||
}
|
||||
# ⚠ Потолки фаз = смета `plan.py` плюс МИНИМАЛЬНЫЙ запас, так чтобы их СУММА не превышала
|
||||
# пакетный потолок. Первая редакция округляла каждый вверх до цента и дала сумму 4.54 > 4.50 —
|
||||
# то есть фазовые гарды в совокупности разрешали пробить пакетный. Поймано селфтестом кассы
|
||||
# ДО первой покупки; ровно этот класс («касса охраняет не ту границу») стоил эксп-21 денег трижды.
|
||||
CEILINGS = {"ФД-A": 0.885, "ФД-B": 0.560, "ФД-C": 0.155, "ФД-D": 0.055,
|
||||
"ФД-E": 2.355, "ФД-F": 0.283, "ФД-G": 0.202}
|
||||
PACK_CEILING = 4.50
|
||||
|
||||
M22.configure(ZONE=ZONE, OUT=OUT, TAGS=TAGS, CEILINGS=CEILINGS,
|
||||
PACK_CEILING=PACK_CEILING, GLOB_ALL="dv-*.json", ROSTER=ROSTER)
|
||||
|
||||
Guarded, Pack, purchase, model_expect = M22.Guarded, M22.Pack, M22.purchase, M22.model_expect
|
||||
|
||||
|
||||
def report() -> None:
|
||||
print(f"{'фаза':7s}{'потрачено':>12s}{'потолок':>10s} что покупает")
|
||||
tot = 0.0
|
||||
what = {"ФД-A": "Д4 edit-контур zh", "ФД-B": "Д3 en→ru несущая", "ФД-C": "Д6 ja разведка",
|
||||
"ФД-D": "Д7 самооценка", "ФД-E": "Д1 gemini", "ФД-F": "H-1 (A6)", "ФД-G": "H-4 (E6)"}
|
||||
for ph in TAGS:
|
||||
sp = Guarded(ph).spent()
|
||||
tot += sp
|
||||
mark = " ⛔ ПРОБОЙ" if sp > CEILINGS[ph] + 1e-9 else ""
|
||||
print(f"{ph:7s}{sp:12.6f}{CEILINGS[ph]:10.2f} {what[ph]}{mark}")
|
||||
print(f"{'ПАК':7s}{tot:12.6f}{PACK_CEILING:10.2f} резерв {PACK_CEILING - tot:.6f}")
|
||||
|
||||
|
||||
def selftest() -> int:
|
||||
"""Обязан пройти ДО первой покупки фазы."""
|
||||
bad = 0
|
||||
|
||||
def ck(n: str, ok: bool, d: str = "") -> None:
|
||||
nonlocal bad
|
||||
bad += not ok
|
||||
print(f"[{'OK ' if ok else 'ПРОВАЛ'}] {n}" + (f" {d}" if d else ""))
|
||||
|
||||
ck("касса смотрит в СВОЁ сырьё", M22.OUT == OUT, str(M22.OUT))
|
||||
ck("покупки уходят в каталог фазы", M22.BUY.OUT == OUT, str(M22.BUY.OUT))
|
||||
ck("фриз-гейт сторожит зону фазы", M22.ZONE == ZONE, str(M22.ZONE))
|
||||
ck("сумма фазовых потолков не выше пакетного",
|
||||
sum(CEILINGS.values()) <= PACK_CEILING + 1e-9,
|
||||
f"{sum(CEILINGS.values()):.2f} ≤ {PACK_CEILING:.2f}")
|
||||
for ph, globs in TAGS.items():
|
||||
for g in globs:
|
||||
ck(f"{ph}: тег {g} покрыт глобом пака", g.startswith("dv-"))
|
||||
# цена обязана считаться для КАЖДОЙ модели, которую фаза может позвать
|
||||
for m in ("deepseek-v4-flash", "deepseek-v4-pro", "glm-5", "gemini-3.1-pro-preview"):
|
||||
try:
|
||||
ck(f"цена считается: {m}", M22.BUY.cost(m, 4500, 0, 2500, 1000, 8000) > 0)
|
||||
except Exception as e: # noqa: BLE001
|
||||
ck(f"цена считается: {m}", False, f"{type(e).__name__}: {e}")
|
||||
# проекционный гард обязан ОТКАЗЫВАТЬ, когда ожидание пробивает потолок
|
||||
g = Guarded("ФД-D")
|
||||
g.ceiling = 0.0
|
||||
ck("проекционный гард отказывает при нулевом потолке", not g.afford()[0])
|
||||
# и обязан видеть цену ДОРОГОЙ модели, а не p95 прошлого (регрессия на пробой Ф1 эксп-22)
|
||||
g2 = Guarded("ФД-D")
|
||||
g2.ceiling = g2.spent() + 0.05
|
||||
g2.next_model = "gemini-3.1-pro-preview"
|
||||
ck("гард видит цену дорогой модели, а не прошлое",
|
||||
not g2.afford()[0], f"ожидание ${model_expect('gemini-3.1-pro-preview'):.4f}")
|
||||
# фриз-гейт обязан отвергнуть покупку из НЕ-файла
|
||||
try:
|
||||
exec(compile("import money22; money22._refuse_unfrozen()", "<s>", "exec"), # noqa: S102
|
||||
{"__name__": "x"})
|
||||
ck("фриз-гейт отвергает покупку из НЕ-файла", False)
|
||||
except SystemExit:
|
||||
ck("фриз-гейт отвергает покупку из НЕ-файла", True)
|
||||
print(f"\n{'КАССА ГОДНА' if not bad else f'ПРОВАЛОВ: {bad}'}")
|
||||
return bad
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
a = sys.argv[1:] or ["--report"]
|
||||
if a[0] == "--selftest":
|
||||
sys.exit(1 if selftest() else 0)
|
||||
report()
|
||||
134
eval/dovodka/plan.py
Normal file
134
eval/dovodka/plan.py
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
#!/usr/bin/env python3
|
||||
"""СМЕТА ФАЗЫ Д ПО ЗАМЕРЕННЫМ ЦЕНАМ КЛЕТОК. $0.
|
||||
|
||||
⚠ Почему скриптом, а не в столбик. Пак 23 поднял потолок фазы, сославшись на «панель $1.844» —
|
||||
это была ПРОЕКЦИЯ `--c-plan` по медианам, а фактическая панель стоила $1.798638 и влезала в
|
||||
исходный потолок. Смета, посчитанная руками и не пере-снимаемая, ровно так и ошибается. Здесь
|
||||
каждая цена берётся из СЫРЬЯ уже купленных клеток (медиана по модели и роли), и любой читатель
|
||||
пере-считает командой.
|
||||
|
||||
Потолок фазы — САНКЦИЯ ВЛАДЕЛЬЦА $4.50 (10.08). История: промт называл $10.00, владелец при
|
||||
запуске сказал $4.00, затем поднял до $4.50, когда приёмка другого модельного семейства нашла,
|
||||
что дизайн не отвечает на H-1 и H-4, а починка стоит $0.48. ⚠ Сессия называла честным потолком
|
||||
без стопов посреди фазы $4.80; владелец выбрал $4.50 — резерв $0.03, и срабатывание гарда есть
|
||||
СТОП и вопрос владельцу, а не сокращение оси решением сессии.
|
||||
|
||||
Запуск: eval/.venv/bin/python eval/dovodka/plan.py [--gemini N]
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import collections
|
||||
import json
|
||||
import statistics as st
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
T = Path.home() / "books" / "tenant-panel"
|
||||
E = Path.home() / "books" / "editor-tier"
|
||||
PACK_CEILING = 4.50
|
||||
|
||||
|
||||
def measured() -> dict[tuple[str, str], float]:
|
||||
"""Медиана $/клетка по (роль, модель) из КУПЛЕННОГО сырья обоих паков."""
|
||||
acc: dict[tuple[str, str], list[float]] = collections.defaultdict(list)
|
||||
for d, pat in ((T, "tp*.json"), (E, "et-*.json")):
|
||||
for f in d.glob(pat):
|
||||
try:
|
||||
r = json.loads(f.read_text(encoding="utf-8"))
|
||||
except (ValueError, OSError):
|
||||
continue
|
||||
if isinstance(r, dict) and r.get("cost_usd") and r.get("model"):
|
||||
acc[(r.get("role") or "?", r["model"])].append(r["cost_usd"])
|
||||
return {k: st.median(v) for k, v in acc.items() if len(v) >= 3}
|
||||
|
||||
|
||||
def main() -> int:
|
||||
m = measured()
|
||||
n_gem = 16
|
||||
if "--gemini" in sys.argv:
|
||||
n_gem = int(sys.argv[sys.argv.index("--gemini") + 1])
|
||||
|
||||
draft = m[("draft", "deepseek-v4-flash")]
|
||||
edit = m[("edit", "deepseek-v4-pro")]
|
||||
gem = 0.14702 # ЕДИНСТВЕННАЯ состоявшаяся клетка Ф3 эксп-22, n=1
|
||||
# Производные цены армов edit-контура. Обоснование множителей — в отчёте §Д0; они ПРОГНОЗ,
|
||||
# и расхождение с фактом печатается при закрытии фазы как сверка сметы.
|
||||
fixer = 0.60 * edit # точечная починка: тот же вход, выход много короче
|
||||
critic = 2.14 * edit # «поиск ≈2.14× переписа» — цифра из заказа владельца
|
||||
onepass = edit # однопроходка: вход короче, размышление длиннее — вничью
|
||||
|
||||
print("ЗАМЕРЕННЫЕ ЦЕНЫ (медиана по сырью обоих паков):")
|
||||
for k in (("draft", "deepseek-v4-flash"), ("edit", "deepseek-v4-pro"),
|
||||
("editor3", "glm-5"), ("edit", "glm-5.2"), ("edit", "gpt-5.6-terra"),
|
||||
("edit", "grok-4.5")):
|
||||
if k in m:
|
||||
print(f" {k[0]:8s}{k[1]:22s}${m[k]:.5f}")
|
||||
print(f" {'edit':8s}{'gemini-3.1-pro-preview':22s}${gem:.5f} ⚠ n=1, "
|
||||
f"{gem / edit:.0f}× боевого редактора")
|
||||
print("\nПРОИЗВОДНЫЕ (прогноз, сверяется с фактом при закрытии):")
|
||||
print(f" фиксер ${fixer:.5f} (0.60× переписа)")
|
||||
print(f" критик ${critic:.5f} (2.14× переписа — множитель из заказа владельца)")
|
||||
|
||||
rows = []
|
||||
# ── Д4 edit-контур zh, 32 единицы (16 эксп-22 + 16 эксп-23); A0 уже куплен обоими паками
|
||||
n4 = 32
|
||||
d4 = n4 * (fixer + onepass + (critic + fixer) + fixer) + 16 * fixer
|
||||
rows.append(("ФД-A", "Д4 edit-контур zh: A1 флаги→фиксер · A2 однопроходка · "
|
||||
f"A3 критик→фиксер · A4 канон-фиксер, n={n4} + свой пол", d4))
|
||||
# ── Д3 en ось, 16 единиц
|
||||
n3 = 16
|
||||
d3 = (0.005 + 0.004 # банк-майнинг + проба контаминации
|
||||
+ n3 * draft + n3 * edit + n3 * onepass
|
||||
+ n3 * (fixer + critic + fixer) + n3 * edit) # два контура + пол
|
||||
rows.append(("ФД-B", f"Д3 en→ru несущая: банк · контам-проба · связка · однопроходка · "
|
||||
f"оба edit-контура · пол, n={n3}", d3))
|
||||
# ── Д6 ja разведка, 9 единиц
|
||||
n6 = 9
|
||||
d6 = 0.004 + n6 * (draft + edit + onepass + edit)
|
||||
rows.append(("ФД-C", f"Д6 ja→ru разведка: контам-проба · связка · однопроходка · пол, n={n6}",
|
||||
d6))
|
||||
# ── Д7 самооценка
|
||||
rows.append(("ФД-D", "Д7 микро-проба самооценки жильцов по трём книгам", 0.05))
|
||||
# ── Д1 gemini
|
||||
# ⚠ ДВА АРМА, ЗАВЕДЁННЫЕ ПРИЁМКОЙ ДРУГОГО МОДЕЛЬНОГО СЕМЕЙСТВА. Без них фаза не отвечает на
|
||||
# H-1 и H-4, и это нашёл не автор: H-1 говорит про «КАЧЕСТВЕННЫЙ черновик + точечный редактор»,
|
||||
# а все армы Д4 ставят фиксер на flash-черновик — то есть на полумусор по самой гипотезе;
|
||||
# H-4 про смену ПОРЯДКА жильцов на другой паре, а на en-оси редактор был ровно один.
|
||||
rows.append(("ФД-F", "H-1: dspro-черновик эксп-22 (уже куплен, $0) + оба edit-контура, n=16 — "
|
||||
"прямой носитель гипотезы «качественный черновик + точечный редактор»",
|
||||
16 * (fixer + critic + fixer)))
|
||||
rows.append(("ФД-G", "H-4: ВТОРОЙ редактор (glm-5) на en-оси поверх той же базы, n=16 — "
|
||||
"без него порядок жильцов на en не проверяется вовсе", 16 * m[("editor3", "glm-5")]))
|
||||
rows.append(("ФД-E", f"Д1 добивка gemini-3.1-pro-preview, {n_gem} клеток "
|
||||
f"(503-отказы бесплатны, платим только за отгруженные)", n_gem * gem))
|
||||
|
||||
print(f"\n{'фаза':7s}{'что покупается':78s}{'смета':>9s}")
|
||||
tot = 0.0
|
||||
for tag, what, cost in rows:
|
||||
tot += cost
|
||||
print(f"{tag:7s}{what[:78]:78s}{cost:9.3f}")
|
||||
print(f"{'ИТОГО':7s}{'':78s}{tot:9.3f} при потолке ${PACK_CEILING:.2f}")
|
||||
free = PACK_CEILING - tot
|
||||
print(f"{'резерв':7s}{'':78s}{free:9.3f}")
|
||||
if free < 0:
|
||||
print("\n⛔ НЕ ВЛЕЗАЕТ. Резать ось или кандидата решением сессии ЗАПРЕЩЕНО "
|
||||
"(промт, анти-лень) —\n это СТОП и вопрос владельцу.")
|
||||
elif free < 0.30:
|
||||
print("\n⚠ Резерв меньше 8% пакета. Ре-гены эхо-мины и ретраи в паках 22/23 съедали "
|
||||
"до +16%\n сверх сметы фазы — при таком резерве первый же эхо-ре-ген упрётся "
|
||||
"в потолок.")
|
||||
|
||||
print(f"\n⚠ ДОЛЯ ОДНОЙ МОДЕЛИ: Д1 = ${n_gem * gem:.2f} = "
|
||||
f"{n_gem * gem / PACK_CEILING * 100:.0f}% всего пакета фазы.")
|
||||
print(f" Цена клетки ${gem:.5f} против ${edit:.5f} у боевого редактора — это "
|
||||
f"${gem * 1500:.0f} за книгу\n при ${edit * 1500:.0f} у боевого. Продуктовым выбором "
|
||||
"модель быть не может ни при каком исходе;\n ценность замера — закрыть гипотезу "
|
||||
"эксп-04 «аутлаер прозы», не более.")
|
||||
print(f"\n Сколько единиц Д1 влезает при остальном плане: "
|
||||
f"{int((PACK_CEILING - (tot - n_gem * gem) - 0.35) / gem)} "
|
||||
"(с резервом $0.35 на ре-гены).")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
197
eval/dovodka/power.py
Normal file
197
eval/dovodka/power.py
Normal file
|
|
@ -0,0 +1,197 @@
|
|||
#!/usr/bin/env python3
|
||||
"""МОЩНОСТЬ СУДЕЙСКИХ ОСЕЙ ФАЗЫ Д — считается ДО покупок. $0.
|
||||
|
||||
⚠ Зачем. Эксп-22 купил английскую ось на n=6 и лишь ПОСЛЕ денег обнаружил, что при шести единицах
|
||||
минимально достижимое p знакового теста = 2⁻⁵ = 0.031, а после Холма по шести контрастам — 0.19,
|
||||
то есть значимости не могло быть НИ ПРИ КАКОМ результате. Ось была недомощна ПО ПОСТРОЕНИЮ.
|
||||
Норма фазы Д: у каждой оси напечатан MDE при плановом n, и ось, чей MDE не даёт несущего вывода,
|
||||
объявляется описательной ЗАРАНЕЕ.
|
||||
|
||||
Два ограничения считаются раздельно, потому что упираются они в разное:
|
||||
* ПОТОЛОК ЗНАЧИМОСТИ — минимально достижимое p точного знакового теста при n наблюдениях с
|
||||
поправкой Холма по объявленному семейству. Если он ≥0.05, дизайн мёртв независимо от эффекта.
|
||||
* MDE (минимально различимый эффект) — 2.8·sd/√n, то же правило, по которому риг печатает порог
|
||||
различимости. Отвечает на «какой перевес прибор вообще способен развести».
|
||||
|
||||
⚠ sd берётся из ЗАМЕРЕННЫХ шумовых полов прошлых паков и помечается источником: своего пола у
|
||||
будущего прохода ещё нет по построению. Плюс поправка ×1.23 на межсессионную компоненту — §12б
|
||||
эксп-23 показал, что пол, чьи половины судит ОДИН судья, занижает порог на 19%.
|
||||
|
||||
Запуск: eval/.venv/bin/python eval/dovodka/power.py
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import math
|
||||
import statistics
|
||||
import sys
|
||||
|
||||
# Замеренные шумовые полы (sd разности по единицам) — источник указан у каждого.
|
||||
# ⚠ ЯРЛЫКИ ИСПРАВЛЕНЫ РЕВЬЮ. Первая редакция называла пол `p3` «редакторы над одной базой» —
|
||||
# это неверно: `p2zh` и `p3` разрешаются в ОДНИ И ТЕ ЖЕ 32 файла (`tp2-floor-p1/p2-E-*`), то есть
|
||||
# оба меряют СКВОЗНУЮ связку «черновик+редактура», и оба несут те же две клетки `finish=length`.
|
||||
# Разница 2.90 против 3.53 — чисто МЕЖСЕССИОННАЯ (×1.217), и это, кстати, СВОЯ оценка той самой
|
||||
# компоненты, ради которой берётся заимствованная ×1.23 из §12б. Совпадение хорошее, но своя
|
||||
# оценка теперь названа, а не умолчана.
|
||||
FLOORS = {
|
||||
"эксп-23 tier (редакторы над ОДНОЙ базой, zh, n=16)": 1.45,
|
||||
"эксп-23 border (редакторы над ОДНОЙ базой, zh, n=16)": 2.12,
|
||||
"эксп-22 p2zh (СКВОЗНАЯ связка, те же 32 файла, что p3)": 2.90,
|
||||
"эксп-22 p3 (ТЕ ЖЕ файлы, другая судейская популяция)": 3.53,
|
||||
}
|
||||
BETWEEN = 1.23 # §12б эксп-23: наивная se → se с межсессионной компонентой
|
||||
|
||||
# ⚠ ЦЕЛЕВОЙ ЭФФЕКТ — то, ради чего ось покупается. Без него MDE не решает НИЧЕГО: печатать
|
||||
# «MDE 1.83» и не сказать, меньше он искомого или больше, — это ровно та форма, за которую
|
||||
# погорела en-ось эксп-22. Цель объявляется ДО денег и обосновывается замеренным, а не желаемым.
|
||||
AXES = [
|
||||
# (имя, план n, контрастов, sd-прайор, ЦЕЛЬ, обоснование прайора, обоснование цели)
|
||||
("Д4 edit-контур zh", 32, 3, 2.12, 1.50,
|
||||
"армы правят ОДНУ базу — это структура `tier`/`border` (sd 1.45/2.12), берётся худшая",
|
||||
"решение «дешёвый фиксер вместо переписа» меняется, если разрыв ≥ трети собственного "
|
||||
"эффекта редактора (+4.44 в эксп-22 Ф3) — это 1.50"),
|
||||
("Д3 en→ru несущая", 16, 1, 2.12, 2.00,
|
||||
"своего пола на en нет НИ ОДНОГО; прайор с zh той же структуры, пол докупается в фазе",
|
||||
"несущий вопрос оси — переносится ли эффект редактора: на zh контроль +3.25, "
|
||||
"значимым переносом считаем половину, 2.00"),
|
||||
("Д6 ja→ru разведка", 9, 3, 2.90, 2.00,
|
||||
"связка end-to-end (как p2zh) — шумнее",
|
||||
"та же цель, что у en; ось объявлена ОПИСАТЕЛЬНОЙ промтом заранее и остаётся ею "
|
||||
"независимо от арифметики"),
|
||||
("Д1 gemini добивка", 16, 1, 2.12, 2.00,
|
||||
"проход над той же якорной базой, что border — его пол ближайший",
|
||||
"гипотеза эксп-04 «аутлаер прозы» — про КРУПНЫЙ отрыв; мелкий отрыв её не подтверждает "
|
||||
"и продуктового смысла при цене 28× не имеет. ⚠ Именно этот порог решил спор о числе "
|
||||
"клеток: при n=12 (минимум промта) MDE 2.11 БОЛЬШЕ цели и ось была бы ОПИСАТЕЛЬНОЙ; "
|
||||
"владелец 10.08 выбрал полные 16, и только на них ось несущая"),
|
||||
]
|
||||
FORCED_DESCRIPTIVE = {"Д6 ja→ru разведка"} # объявлено промтом, арифметикой не отменяется
|
||||
|
||||
# ⚠ ПЕРВИЧНОЕ И ВТОРИЧНОЕ СЕМЕЙСТВА — объявляются ДО покупок, и это НЕ сужение задним числом.
|
||||
# Поправка Холма делится на РАЗМЕР семейства, поэтому «взять все контрасты несущими» = сделать
|
||||
# все недомощными. Пре-регистрируется так: первичное семейство несёт по одному контрасту на
|
||||
# КАЖДУЮ гипотезу владельца и считается с Холмом; остальные контрасты объявлены ОПИСАТЕЛЬНЫМИ,
|
||||
# печатаются с числами и поправку не несут. Ровно этот приём эксп-22 применил в §0-Ф2 к осям
|
||||
# «черновик как текст», и он там сработал.
|
||||
PRIMARY = {
|
||||
"Д4 edit-контур zh": ["A1/A0 — флаговый контур (H-2а)",
|
||||
"A3/A0 — смысловой контур (H-2б, ставка владельца)",
|
||||
"A6/A0 — качественный черновик + контур (H-1)"],
|
||||
"Д3 en→ru несущая": ["E0/D0 — КОНТРОЛЬ: покупает ли редактор поверх черновика (H-3)"],
|
||||
"Д1 gemini добивка": ["R1/R0 — аутлаер ли проза gemini (гипотеза эксп-04)"],
|
||||
}
|
||||
SECONDARY = {
|
||||
"Д4 edit-контур zh": ["A2/A0 — однопроходка уравненного мандата (строка 153 бэклога)",
|
||||
"A4/A0 — канон-фиксер ПОСЛЕ переписа: его несущая ось — покрытие "
|
||||
"канона, а оно детерминировано и шума не имеет"],
|
||||
"Д3 en→ru несущая": ["E1..E5/E0 — армы контуров и однопроходки",
|
||||
"E6/E0 — ВТОРОЙ редактор glm-5 (H-4): на n=16 порядок жильцов "
|
||||
"разрешить нельзя, ось описательная и это сказано ДО денег"],
|
||||
}
|
||||
|
||||
# ⚠ ПОЧЕМУ ПРАЙОР 2.12, А НЕ 2.90/3.53. Оба пола эксп-22 меряют СКВОЗНУЮ связку — они несут ещё и
|
||||
# шум черновика, тогда как армы Д4 правят ОДНУ замороженную базу. Структурно ближе полы эксп-23
|
||||
# (`tier` 1.45, `border` 2.12, оба над одной базой), и берётся ХУДШИЙ из них. Отдельно: обе пары
|
||||
# пола эксп-22 содержат по две клетки `finish=length` (§15), что разброс завышает — без затронутых
|
||||
# единиц тот же пол даёт 3.10; в фазе Д такие клетки в пачку не допускаются вовсе.
|
||||
# ⚠ И честно: ПРАЙОР РЕШАЕТ. При sd 3.53 MDE оси Д4 был бы 2.48 против цели 1.50, то есть ось
|
||||
# стала бы описательной. Прежняя редакция писала «прайор всё равно НЕ решает» — неверно, снято
|
||||
# ревью. Решает он не окончательно: СВОЙ пол прохода меряется в фазе парой, чьи половины судят
|
||||
# РАЗНЫЕ сессии, и если он выйдет хуже прайора — ось пере-классифицируется ТОГДА ЖЕ, до чтения
|
||||
# боевых перевесов.
|
||||
|
||||
# Пространство дизайна: во что упирается вывод при разных n и разных полах.
|
||||
GRID_N = (12, 16, 24, 32)
|
||||
GRID_SD = (1.45, 2.12, 2.90, 3.53)
|
||||
|
||||
|
||||
def min_p(n: int, k: int) -> float:
|
||||
"""Минимально достижимое двустороннее p знакового теста при n, после Холма по k контрастам."""
|
||||
return min(1.0, 2.0 ** (1 - n) * k)
|
||||
|
||||
|
||||
def _z(p: float) -> float:
|
||||
"""Квантиль стандартной нормали."""
|
||||
return statistics.NormalDist().inv_cdf(p)
|
||||
|
||||
|
||||
def mde(sd: float, n: int, k: int = 1, between: bool = True) -> float:
|
||||
"""Минимально различимый эффект при 80% мощности и поправке Холма по k контрастам.
|
||||
|
||||
⚠ КОНСТАНТА 2.8 ВЕРНА ТОЛЬКО ПРИ k=1, и первая редакция этого файла применяла её всегда.
|
||||
Поймано адверсариальным ревью снизу вверх. Решающее правило пака — `p` ХОЛМА < 0.05, то есть
|
||||
на самый строгий контраст семейства приходится α/k; множитель становится
|
||||
(z_{1−α/2k} + z_{0.8}), при k=5 это 3.417 вместо 2.8. Пропущенная поправка ×1.22–1.36 БОЛЬШЕ
|
||||
той ×1.23, которую файл старательно вносил за межсессионную компоненту, — и она переворачивала
|
||||
вердикт: Д4 при k=5 давала MDE 1.57 против цели 1.50, то есть ОПИСАТЕЛЬНУЮ ось, объявленную
|
||||
несущей. Это буквально провал en-оси эксп-22, ради недопущения которого файл и написан.
|
||||
"""
|
||||
mult = _z(1 - 0.05 / (2 * max(1, k))) + _z(0.80)
|
||||
return mult * sd / math.sqrt(n) * (BETWEEN if between else 1.0)
|
||||
|
||||
|
||||
def main() -> int:
|
||||
print("ЗАМЕРЕННЫЕ ПОЛЫ, из которых берутся прайоры (sd разности по единицам):")
|
||||
for k, v in FLOORS.items():
|
||||
print(f" {k:54s} sd {v:.2f}")
|
||||
print(f"\nпоправка на межсессионную компоненту: ×{BETWEEN} (§12б эксп-23)\n")
|
||||
print(f"{'ось':24s}{'n':>4s}{'k':>3s}{'sd':>6s}{'MDE':>7s}{'ЦЕЛЬ':>7s}{'потолок p':>11s}"
|
||||
" СТАТУС ОСИ")
|
||||
desc = 0
|
||||
for name, n, k, sd, target, _w1, _w2 in AXES:
|
||||
p0 = min_p(n, k)
|
||||
m_true = mde(sd, n, k)
|
||||
# ⚠ ДВА УСЛОВИЯ, И ОБА НЕОБХОДИМЫ. (1) значимость достижима вообще; (2) прибор способен
|
||||
# развести ИСКОМЫЙ эффект. Первая редакция смотрела только на (1) — и объявляла несущей
|
||||
# ось, чей MDE вдвое больше того, что она ищет. Это и есть en-ось эксп-22, слово в слово.
|
||||
why = []
|
||||
if p0 >= 0.05:
|
||||
why.append(f"значимость недостижима (потолок p {p0:.4f})")
|
||||
if m_true > target:
|
||||
why.append(f"MDE {m_true:.2f} БОЛЬШЕ искомого эффекта {target:.2f}")
|
||||
if name in FORCED_DESCRIPTIVE:
|
||||
why.append("объявлена описательной ЗАКАЗОМ")
|
||||
status = "НЕСУЩАЯ" if not why else "⛔ ОПИСАТЕЛЬНАЯ: " + " · ".join(why)
|
||||
desc += bool(why)
|
||||
print(f"{name:24s}{n:4d}{k:3d}{sd:6.2f}{m_true:7.2f}{target:7.2f}{p0:11.4f} {status}")
|
||||
print(f"\nописательных осей: {desc} из {len(AXES)} — объявлено ДО покупок, "
|
||||
"пере-классификации задним числом не будет")
|
||||
print("\nСЕМЕЙСТВА, объявленные ДО покупок:")
|
||||
for name in [a[0] for a in AXES]:
|
||||
if name in PRIMARY:
|
||||
print(f" {name} — ПЕРВИЧНОЕ (Холм по {len(PRIMARY[name])}):")
|
||||
for c in PRIMARY[name]:
|
||||
print(f" • {c}")
|
||||
if name in SECONDARY:
|
||||
print(" описательные (поправку не несут, печатаются с числами):")
|
||||
for c in SECONDARY[name]:
|
||||
print(f" · {c}")
|
||||
print("\nОБОСНОВАНИЯ (прайор шума · искомый эффект):")
|
||||
for name, _n, _k, _sd, _t, w1, w2 in AXES:
|
||||
print(f" {name}\n прайор: {w1}\n цель: {w2}")
|
||||
print("\nПРОСТРАНСТВО ДИЗАЙНА — честный MDE (с ×1.23) при разных n и разных полах:")
|
||||
print(" " + "".join(f"{f'sd {s:.2f}':>10s}" for s in GRID_SD))
|
||||
for n in GRID_N:
|
||||
print(f" n={n:<4d}" + "".join(f"{mde(s, n, 1):10.2f}" for s in GRID_SD))
|
||||
print(" (таблица при k=1; на семейство из k контрастов множитель растёт — см. mde())")
|
||||
print("\n СКОЛЬКО ЕДИНИЦ НУЖНО, чтобы ось была несущей при sd 2.12 и цели:")
|
||||
for k in (1, 2, 3, 4, 5):
|
||||
for tgt in (1.50, 2.00):
|
||||
need = math.ceil(((_z(1 - 0.05 / (2 * k)) + _z(0.80)) * 2.12 * BETWEEN / tgt) ** 2)
|
||||
print(f" k={k} цель {tgt:.2f} → n ≥ {need}")
|
||||
print("\n Для калибровки — РЕАЛЬНЫЕ перевесы, уже замеренные на этом материале:")
|
||||
print(" эксп-22 Ф3: R2 −2.12 · R3 −2.54 · R4 −4.75 · КОНТРОЛЬ +4.44")
|
||||
print(" эксп-23 tier: T1 −0.19 · T2 +0.50 · T3 +0.19 · КОНТРОЛЬ +2.06")
|
||||
print(" эксп-23 border: R2 −0.81")
|
||||
print(" ⇒ Различия МЕЖДУ близкими редакторами лежат в полосе 0.2–2.5, то есть под MDE почти\n"
|
||||
" любой строки этой таблицы. Развести их прибор способен только при малом поле И\n"
|
||||
" большом n. Контрасты «есть ли редактура вообще» (+2.1…+4.4) берутся легко.")
|
||||
print("\n⚠ Читать так: MDE — свойство ПЛАНА, а не результата. Свой пол каждого прохода меряется\n"
|
||||
" в фазе и печатается рядом; если он окажется хуже прайора, ось пере-классифицируется в\n"
|
||||
" описательную ТОГДА ЖЕ, до чтения боевых перевесов, а не после.")
|
||||
print("\n⚠ Сравнение с эксп-22: его en-ось шла n=6 при k=6 ⇒ потолок p "
|
||||
f"{min_p(6, 6):.4f} — дизайн не мог дать значимости. Здесь такой оси нет.")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
231
eval/dovodka/promptdiff.py
Normal file
231
eval/dovodka/promptdiff.py
Normal file
|
|
@ -0,0 +1,231 @@
|
|||
#!/usr/bin/env python3
|
||||
"""ГЕЙТ КОНСИСТЕНТНОСТИ ПАР-ПРОМПТОВ. $0. Ненулевой код = пара не годна к покупкам.
|
||||
|
||||
⚠ Зачем. Эксп-19 получил конфаундированный арм: en-зеркало промпта было НЕПОЛНЫМ, и разница
|
||||
армов смешалась с разницей формулировок. Заказ фазы Д требует «механический гейт консистентности
|
||||
с zh-промптами (одна структура, отличаются только данные языка; дифф по шаблону — гейт, не
|
||||
глазами)». Вот он.
|
||||
|
||||
ПРАВИЛО, которое гейт проверяет:
|
||||
пар-промпт обязан быть побайтной копией боевого zh-промпта ВЕЗДЕ, кроме
|
||||
(а) блока пар-специфики — секции «Единицы и приёмы (общие для <пара>)», это ДАННЫЕ пары;
|
||||
(б) HTML-комментария провенанса в шапке.
|
||||
Любое расхождение вне (а)/(б) — НАРУШЕНИЕ: структура промптов разошлась, и контраст между парами
|
||||
перестал быть контрастом пары.
|
||||
|
||||
Дополнительно сверяется то, что дифф строк не ловит:
|
||||
* множества плейсхолдеров `{{…}}` совпадают;
|
||||
* разделитель `---USER---` присутствует в обоих и на своём месте;
|
||||
* последняя инструкция (что выводить) совпадает дословно;
|
||||
* заголовок пар-блока называет ИМЕННО свою пару.
|
||||
|
||||
Запуск: eval/.venv/bin/python eval/dovodka/promptdiff.py [<пара> …] (по умолчанию en-ru)
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import difflib
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
REPO = Path("/home/ubuntu/projects/textmachine")
|
||||
BATTLE = REPO / "backend" / "prompts" / "zh-ru"
|
||||
PAIRS = REPO / "eval" / "role_topology" / "prompts"
|
||||
# Пар-блок встречается в ДВУХ формах, и первая редакция гейта знала только одну — из-за этого
|
||||
# `editor.md` целиком считался «разошедшимся». Форма (1): отдельная строка-заголовок с буллетами
|
||||
# под ней (`translator.md`). Форма (2): один буллет-абзац (`editor.md`). Ловим обе.
|
||||
PAIR_HEAD = re.compile(r"^Единицы и приёмы \(общие для ([a-z]{2}→ru)\):\s*$")
|
||||
PAIR_INLINE = re.compile(r"^-\s*Единицы и приёмы \(общие для ([a-z]{2}→ru)\):")
|
||||
COMMENT = re.compile(r"(?s)<!--.*?-->\s*")
|
||||
# ⚠ ВТОРАЯ ЗАКОННО ПАР-СПЕЦИФИЧНАЯ СЕКЦИЯ. Она описывает ТИПОЛОГИЮ ИСХОДНОГО ЯЗЫКА (китайский
|
||||
# паратаксис против английской ритмической дроби), поэтому расходиться обязана. Но именно внутри
|
||||
# неё живёт мандат арма, и ровно на его неполноте погорел эксп-19 — поэтому секция не выводится
|
||||
# из-под гейта целиком: у неё сверяются ЧИСЛО пунктов и мандатные оговорки (ниже).
|
||||
REFLOW_HEAD = re.compile(r"^ДИСКУРС-ПЕРЕВЁРСТКА\b")
|
||||
NUMBERED = re.compile(r"^(\d+)\.\s")
|
||||
# Оговорки, несущие МАНДАТ (а не типологию пары): обязаны стоять в обеих редакциях дословно.
|
||||
INVARIANTS = (
|
||||
"Не сливай ТОЛЬКО ради длины и не теряй смысловые атомы",
|
||||
"число предложений и абзацев совпадать с исходником НЕ обязано",
|
||||
"Запрещено терять смысловые атомы",
|
||||
"Каждую новую реплику начинай с нового абзаца через тире",
|
||||
)
|
||||
# ⚠ СООТВЕТСТВИЕ ФАЙЛОВ ОБЪЯВЛЯЕТСЯ, А НЕ УГАДЫВАЕТСЯ. Первая редакция подбирала zh-аналог по
|
||||
# имени с фолбэком на суффикс `-eq` — и сопоставила `translator-reflow` с `translator-reflow-eq`,
|
||||
# то есть с УРАВНЕННЫМ вариантом мандата эксп-21, который зеркалом не является вовсе. Гейт,
|
||||
# который сам придумывает, что с чем сравнивать, доказывает не то, что от него хотят.
|
||||
MAP = {
|
||||
# en-файл → (zh-оригинал, комментарий провенанса)
|
||||
"translator": ("backend/prompts/zh-ru/translator.md", "боевой промпт переводчика"),
|
||||
"editor": ("backend/prompts/zh-ru/editor.md", "боевой промпт редактора"),
|
||||
"translator-reflow": (None, "боевого zh-аналога НЕТ: это вариант полигона, "
|
||||
"зеркалом боевого промпта не является"),
|
||||
}
|
||||
# ⚠ ТРЕТИЙ ЗАКОННО ПАР-СПЕЦИФИЧНЫЙ КЛАСС: примеры. Они обязаны быть НА ИСХОДНОМ ЯЗЫКЕ пары,
|
||||
# иначе редактор увидит образец не того языка, который правит.
|
||||
EXAMPLE = re.compile(r"^\[(narrative|dialogue)\b")
|
||||
|
||||
# ⚠ ОБЪЯВЛЕННЫЕ РАСХОЖДЕНИЯ. Гейт зелёный ТОЛЬКО на них: любое новое расхождение роняет пару.
|
||||
# Каждая строка разобрана глазами, причина записана. Так «мирится» дрейф, который иначе накопится
|
||||
# молча, — и ровно этого не хватило эксп-19.
|
||||
ALLOWED = {
|
||||
("en-ru", "editor", "- Сверяй смысл черновика"):
|
||||
"пример кальки ЗАМЕНЁН своим английским (`he raised an eyebrow` → «поднял бровь» вместо "
|
||||
"«вскинул брови») — образец обязан быть на исходном языке пары. ⚠ Вставлен в фазе Д: "
|
||||
"приёмка другого семейства заметила, что НЕСУЩАЯ ось en шла с зеркалом БЕДНЕЕ, чем "
|
||||
"разведочная ja, и гейт это благословлял",
|
||||
("en-ru", "editor", "- Вёрстка: собирай повествование"):
|
||||
"zh добавляет «НЕ копируя построчную разбивку исходника (в оригинале часто одно "
|
||||
"предложение — одна строка)» — свойство китайской вебновеллы; английская проза так не "
|
||||
"набирается. Остаток строки совпадает дословно",
|
||||
("ja-ru", "editor", "- Сверяй смысл черновика"):
|
||||
"пример кальки ЗАМЕНЁН своим японским (よろしくお願いします как «прошу хорошего "
|
||||
"обращения») — образец обязан быть на исходном языке пары",
|
||||
("ja-ru", "editor", "- Вёрстка: собирай повествование"):
|
||||
"оговорка про построчный набор СОХРАНЕНА и переформулирована под ja: веб-новелла "
|
||||
"syosetu набирается построчно так же, как китайская",
|
||||
}
|
||||
BAD = 0
|
||||
|
||||
|
||||
def allowed(pair: str, name: str, line: str) -> str:
|
||||
for (p, f, pref), why in ALLOWED.items():
|
||||
if p == pair and f == name and line.startswith(pref):
|
||||
return why
|
||||
return ""
|
||||
|
||||
|
||||
def ck(name: str, ok: bool, detail: str = "") -> None:
|
||||
global BAD # noqa: PLW0603
|
||||
BAD += not ok
|
||||
print(f" [{'OK ' if ok else 'ПРОВАЛ'}] {name}" + (f" {detail}" if detail else ""))
|
||||
|
||||
|
||||
def strip(t: str) -> str:
|
||||
return COMMENT.sub("", t)
|
||||
|
||||
|
||||
def blocks(lines: list[str]) -> tuple[list[str], list[str], list[str], str]:
|
||||
"""(строки вне пар-секций, строки «Единицы и приёмы», строки ДИСКУРС-секции, объявленная пара)."""
|
||||
out, pair_lines, reflow, pair, inside, in_ref = [], [], [], "", False, False
|
||||
for ln in lines:
|
||||
if REFLOW_HEAD.match(ln):
|
||||
in_ref, inside = True, False
|
||||
reflow.append(ln)
|
||||
continue
|
||||
if EXAMPLE.match(ln):
|
||||
reflow.append(ln)
|
||||
in_ref = False
|
||||
continue
|
||||
if in_ref:
|
||||
if ln.strip() and not NUMBERED.match(ln):
|
||||
in_ref = False # секция кончилась
|
||||
else:
|
||||
reflow.append(ln)
|
||||
continue
|
||||
mi = PAIR_INLINE.match(ln)
|
||||
if mi: # форма (2): один буллет
|
||||
pair = pair or mi.group(1)
|
||||
pair_lines.append(ln)
|
||||
inside = False
|
||||
continue
|
||||
m = PAIR_HEAD.match(ln)
|
||||
if m: # форма (1): заголовок + буллеты
|
||||
inside, pair = True, m.group(1)
|
||||
pair_lines.append(ln)
|
||||
continue
|
||||
if inside:
|
||||
if ln.strip() and not ln.startswith("-"):
|
||||
inside = False # блок кончился
|
||||
else:
|
||||
pair_lines.append(ln)
|
||||
continue
|
||||
out.append(ln)
|
||||
return out, pair_lines, reflow, pair
|
||||
|
||||
|
||||
def compare(name: str, zh_p: Path, en_p: Path, want_pair: str, pair: str) -> None:
|
||||
print(f"\n=== {name}: {zh_p.relative_to(REPO)} ↔ {en_p.relative_to(REPO)}")
|
||||
if not zh_p.exists() or not en_p.exists():
|
||||
ck("оба файла существуют", False, f"нет {'zh' if not zh_p.exists() else 'en'}")
|
||||
return
|
||||
zh, en = strip(zh_p.read_text(encoding="utf-8")), strip(en_p.read_text(encoding="utf-8"))
|
||||
zl, el = zh.splitlines(), en.splitlines()
|
||||
zo, zb, zr, zp = blocks(zl)
|
||||
eo, eb, er, ep = blocks(el)
|
||||
|
||||
ck("пар-блок объявлен в обоих", bool(zp) and bool(ep), f"zh «{zp}» · пара «{ep}»")
|
||||
ck(f"пар-блок называет свою пару ({want_pair})", ep == want_pair, ep or "не найден")
|
||||
|
||||
# ГЛАВНАЯ ПРОВЕРКА: всё вне пар-блока обязано совпадать построчно
|
||||
diff = [d for d in difflib.unified_diff(zo, eo, "zh-ru", want_pair, lineterm="", n=0)
|
||||
if d[:1] in "+-" and not d.startswith(("---", "+++"))]
|
||||
undecl = [d for d in diff if not allowed(pair, name, d[1:])]
|
||||
ck("вне пар-секций НЕОБЪЯВЛЕННЫХ расхождений НЕТ", not undecl,
|
||||
f"необъявленных строк: {len(undecl)} (всего разошлось {len(diff)})")
|
||||
for d in undecl[:12]:
|
||||
print(f" {d[:150]}")
|
||||
if len(undecl) > 12:
|
||||
print(f" … ещё {len(undecl) - 12}")
|
||||
seen = {allowed(pair, name, d[1:]) for d in diff if allowed(pair, name, d[1:])}
|
||||
for why in sorted(seen):
|
||||
print(f" · объявленное расхождение: {why}")
|
||||
|
||||
zph, eph = set(re.findall(r"\{\{(\w+)\}\}", zh)), set(re.findall(r"\{\{(\w+)\}\}", en))
|
||||
ck("множества плейсхолдеров совпадают", zph == eph,
|
||||
f"только в zh: {sorted(zph - eph)} · только в паре: {sorted(eph - zph)}"
|
||||
if zph != eph else f"{len(zph)} шт.")
|
||||
ck("разделитель ---USER--- в обоих", ("---USER---" in zh) == ("---USER---" in en) is True,
|
||||
f"zh {'---USER---' in zh} · пара {'---USER---' in en}")
|
||||
if "---USER---" in zh and "---USER---" in en:
|
||||
ck("хвост после ---USER--- совпадает",
|
||||
zh.split("---USER---", 1)[1].strip() == en.split("---USER---", 1)[1].strip())
|
||||
# пар-блок ОБЯЗАН отличаться — иначе пара не адаптирована вовсе
|
||||
def _anon(ls: list[str]) -> list[str]:
|
||||
return [re.sub(r"[a-z]{2}→ru", "<пара>", x) for x in ls if x.strip()]
|
||||
ck("пар-блок содержательно ОТЛИЧАЕТСЯ (иначе пара не адаптирована)",
|
||||
_anon(zb) != _anon(eb), f"строк в блоке: zh {len(zb)} · пара {len(eb)}")
|
||||
|
||||
# ── ДИСКУРС-секция: расходиться обязана, но НЕ терять мандат ───────────────────────────────
|
||||
if zr or er:
|
||||
zn = [NUMBERED.match(x).group(1) for x in zr if NUMBERED.match(x)]
|
||||
en_n = [NUMBERED.match(x).group(1) for x in er if NUMBERED.match(x)]
|
||||
ck("ДИСКУРС-секция есть в обоих", bool(zr) and bool(er),
|
||||
f"zh {len(zr)} строк · пара {len(er)}")
|
||||
ck("число пунктов ДИСКУРС-секции совпадает", zn == en_n,
|
||||
f"zh {zn} · пара {en_n}")
|
||||
zt, et = "\n".join(zr), "\n".join(er)
|
||||
miss = [inv for inv in INVARIANTS if (inv in zt) and (inv not in et)]
|
||||
ck("мандатные оговорки не потеряны в паре", not miss,
|
||||
"потеряно: " + " · ".join(f"«{x}»" for x in miss) if miss else f"{len(INVARIANTS)} шт.")
|
||||
extra = [inv for inv in INVARIANTS if (inv in et) and (inv not in zt)]
|
||||
if extra:
|
||||
print(f" ⚠ в паре есть оговорка, которой нет в zh: {extra} — "
|
||||
"мандат НЕ уравнен, но в обратную сторону")
|
||||
|
||||
|
||||
def main() -> int:
|
||||
pairs = [a for a in sys.argv[1:] if not a.startswith("-")] or ["en-ru"]
|
||||
for p in pairs:
|
||||
want = f"{p.split('-')[0]}→ru"
|
||||
print(f"\n{'═' * 78}\nПАРА {p}")
|
||||
d = PAIRS / p
|
||||
if not d.exists():
|
||||
ck(f"пар-пакет {p} существует", False, str(d))
|
||||
continue
|
||||
for f in sorted(d.glob("*.md")):
|
||||
if f.stem not in MAP:
|
||||
ck(f"{f.stem}: соответствие zh-оригиналу ОБЪЯВЛЕНО", False,
|
||||
"файла нет в MAP — гейт не угадывает, с чем сравнивать")
|
||||
continue
|
||||
rel, why = MAP[f.stem]
|
||||
if rel is None:
|
||||
print(f"\n=== {f.stem}: сравнение НЕ ПРОВОДИТСЯ — {why}")
|
||||
continue
|
||||
compare(f.stem, REPO / rel, f, want, p)
|
||||
print(f"\n{'ПАР-ПРОМПТЫ КОНСИСТЕНТНЫ' if not BAD else f'НАРУШЕНИЙ: {BAD} — покупки по этой паре ЗАПРЕЩЕНЫ'}")
|
||||
return BAD
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(1 if main() else 0)
|
||||
115
eval/dovodka/requirements.md
Normal file
115
eval/dovodka/requirements.md
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
# Реестр требований фазы Д — сверка с буквой заказа
|
||||
|
||||
Источник: `docs/POLYGON_EXP2223_REDO_SESSION_PROMPT.md`, прочитан ПОСТРОЧНО, включая придаточные.
|
||||
Так заведено потому, что все шесть пропусков эксп-22 стояли не в нумерованных блоках, а в
|
||||
придаточных предложениях, и «критик полноты», писанный той же головой, их не увидел.
|
||||
|
||||
**Этот файл обязан быть закоммичен ДО покупок.** `eval/conformance.py --final` отказывается
|
||||
сверять незакоммиченный реестр: гейт против таблицы, которую можно дописать под результат,
|
||||
ничего не доказывает — так эксп-23 получил зелёную сверку против реестра из scratchpad.
|
||||
|
||||
Проверка:
|
||||
```
|
||||
eval/.venv/bin/python eval/conformance.py eval/dovodka/requirements.md --plan # по ходу
|
||||
eval/.venv/bin/python eval/conformance.py eval/dovodka/requirements.md --final # перед сдачей
|
||||
```
|
||||
|
||||
Колонка «улика»: `$ команда` — обязана вернуть код 0; `путь` — артефакт обязан существовать и быть
|
||||
непустым; `—` — улики ещё нет (в режиме `--plan` законно, в `--final` = провал).
|
||||
|
||||
⚠ Улика вида «греп собственного отчёта» доказывает, что НАПЕЧАТАНО, а не что СДЕЛАНО. Гейт такие
|
||||
улики считает отдельно и печатает их долю; здесь они помечены в тексте требования словом «печать».
|
||||
|
||||
<!-- ТРЕБОВАНИЯ -->
|
||||
|
||||
| ID | требование (цитата или сжатие промта) | улика |
|
||||
|---|---|---|
|
||||
| **РАМКА** | | |
|
||||
| R1 | Отчёт фазы — секции Д0–Д8 в `23-editor-tier.md`, продолжение того же документа | — |
|
||||
| R2 | Сводная таблица «пробел → чем закрыт → носитель-артефакт» обязательна | — |
|
||||
| R3 | Пакетный потолок фазы сверен СЛОВОМ владельца при запуске | `$ grep -q "САНКЦИЯ ВЛАДЕЛЬЦА .4.00" eval/dovodka/plan.py` |
|
||||
| R4 | Фазовые потолки разложены в пре-реге Д0 | — |
|
||||
| R5 | Касса: фриз-гейт, атомарный замок, два пути счёта, проекционные гарды | — |
|
||||
| R6 | Стоп-правило: не влезает → СТОП и пинг ДО покупок | `$ eval/.venv/bin/python eval/dovodka/plan.py` |
|
||||
| R7 | Кап агент-сессий 80; запись ДО запуска суб-агента; селфтест до первой сессии | `$ eval/.venv/bin/python eval/dovodka/runs.py --selftest` |
|
||||
| R8 | Гипотезы H-1, H-2а, H-2б, H-3, H-4 внесены в пре-рег ДОСЛОВНО | — |
|
||||
| **Д1 — gemini** | | |
|
||||
| R9 | Полная редакторская панель: 16 клеток поверх якорной базы эксп-22 | — |
|
||||
| R10 | Ретрай-харнесс: экспоненциальный бэкофф на 5xx, окно ≥24 ч, поштучно, лимит цены на клетку | — |
|
||||
| R11 | Журнал КАЖДОЙ попытки (время · код ответа · стоимость) файлом в сырьё | — |
|
||||
| R12 | Вердикт «модель не отдаёт» — только с журналом за ≥24 ч + снимком вендор-статуса | — |
|
||||
| R13 | Собрано ≥12/16 → судейство абс-ригом со всеми контролями | — |
|
||||
| R14 | Потолок клетки считается по `total_tokens` (скрытая тарификация ×4.5) | — |
|
||||
| **Д2 — внешняя подпись `tier`** | | |
|
||||
| R15 | Задания `aj-tier-tasks` отданы внешнему судье вне Claude руками владельца | `$ test -s /home/ubuntu/books/editor-tier/sol-tier/ИНСТРУКЦИЯ.md` |
|
||||
| R16 | Закрытие = НЕПУСТОЙ каталог ответов + пере-счёт скриптом класса `solscore` | `$ ls /home/ubuntu/books/editor-tier/sol-tier/*.txt` |
|
||||
| **Д3 — en→ru несущая ось** | | |
|
||||
| R17 | Провенанс каждого en-промпта объявлен в отчёте (печать) | — |
|
||||
| R18 | Механический гейт консистентности en-промптов с zh: дифф по шаблону, не глазами | `$ eval/.venv/bin/python eval/dovodka/promptdiff.py en-ru` |
|
||||
| R19 | Выверка пар-промптов против БОЕВОГО рендера | — |
|
||||
| R20 | Фриз пар-промптов ДО покупок | — |
|
||||
| R21 | en-банк: майнинг + терминолог по методике эксп-22, `signed:false` | — |
|
||||
| R22 | Банк-закон D39.104 на ВСЕХ армах — прошлое отступление (72 клетки) не повторять | — |
|
||||
| R23 | Материал: Кристофф, добор до 16 единиц методикой эксп-21/22, fr-страты сохранены | — |
|
||||
| R24 | Проба контаминации гоняется ЖИЛЬЦАМИ, а не удобной дешёвой моделью | — |
|
||||
| R25 | Материал непригоден по пробе → СТОП и пинг; замену без слова владельца не искать | — |
|
||||
| R26 | Армы: боевая связка · dspro-однопроходка УРАВНЕННОГО мандата (строка 153) · черновик + ОБА edit-контура · контроль редактора E0/D0 | — |
|
||||
| R27 | Судейство по нормам рига, Холм по объявленному семейству | — |
|
||||
| **Д4 — edit-контур zh** | | |
|
||||
| R28 | Якорные единицы эксп-22 переиспользуются, $0 за старые армы | — |
|
||||
| R29 | Армы A0–A4 обязательны; **A3 (смысловой критик → фиксер) — ставка владельца, обязателен**; A5 — опция при остатке | — |
|
||||
| R30 | Позиция контура: A1/A3 «ВМЕСТО редактора», A4 «ПОСЛЕ»; «до редактора» не мерится | — |
|
||||
| R31 | Канон-гейт — детерминированная сверка покрытия банка после КАЖДОГО арма | — |
|
||||
| R32 | Ответ по каждому арму печатается ТРЕМЯ осями: судья · канон-покрытие · цена | — |
|
||||
| R33 | Несущий вердикт фазы на zh — внешняя подпись судьёй вне Claude | — |
|
||||
| **Д5 — канон-вскрытие** | | |
|
||||
| R34 | КАЖДОЕ место потери покрытия у R0 классифицировано (исчез / парафраз / другой перевод), таблица по терминам и местам | `$ eval/.venv/bin/python eval/dovodka/canon.py` |
|
||||
| R35 | Результат вскрытия — регрессионный набор для канон-фиксера | `/home/ubuntu/books/dovodka/canon-dissect.json` |
|
||||
| **Д6 — ja→ru разведка** | | |
|
||||
| R36 | Материал `isekai_majutsushi_jp`, проба контаминации жильцами | `$ eval/.venv/bin/python eval/dovodka/material_ja.py; test $? -le 1` |
|
||||
| R37 | Непригоден → СТОП и пинг; замену без слова владельца не искать | `$ eval/.venv/bin/python eval/dovodka/material_ja.py \| grep -q "СТОП\|OK "` |
|
||||
| R38 | ja-промпты — те же гейты провенанса и консистентности, что Д3 | `$ eval/.venv/bin/python eval/dovodka/promptdiff.py ja-ru` |
|
||||
| R39 | 8–10 единиц: связка · dspro-однопроходка · контроль редактора | — |
|
||||
| R40 | Печатается сравнение ОТНОСИТЕЛЬНОГО порядка жильцов между парами zh/en/ja | — |
|
||||
| R41 | Статус оси РАЗВЕДКА объявлен В ПРЕ-РЕГЕ, а не постфактум | — |
|
||||
| **Д7 — самооценка** | | |
|
||||
| R42 | Жильцам обеих ролей по трём книгам (zh/en/ja) вопрос о самооценке; ответы персистятся | — |
|
||||
| R43 | Сверка самооценки с фактическими результатами пар; решений на ней не стоит | — |
|
||||
| **Д8 — поправки тел по приёмке №16** | | |
|
||||
| R44 | §15 эксп-22 переписан: 5 из 6 клеток — эхо, пойманное гейтом; гейт длины сужен до `finish=length` | `$ grep -q "ИНТЕРПРЕТАЦИЯ ПЕРЕПИСАНА ФАЗОЙ Д" docs/experiments/22-tenant-panel.md` |
|
||||
| R45 | §13а эксп-22: банк-закон на оси en НЕ исполнен | `$ grep -q "на zh исполнено, на en НЕТ" docs/experiments/22-tenant-panel.md` |
|
||||
| R46 | Sol-статусы §10/§11/§14 эксп-22 приведены к §16; оборванная фраза дописана | `$ grep -q "АРБИТРАЖ НЕ СОСТОЯЛСЯ; ПАКЕТ (б)" docs/experiments/22-tenant-panel.md` |
|
||||
| R47 | Счёт агент-сессий эксп-23 = 58, не 50 (§6, §14 п.9) + пинг в PROGRESS | `$ grep -q "Агент-запусков: 58" docs/experiments/23-editor-tier.md` |
|
||||
| R48 | Противоречие §9/§13 эксп-23 против §12в о внешнем контуре снято | `$ grep -q "Внешний контур есть у .border. и НЕТ у .tier." docs/experiments/23-editor-tier.md` |
|
||||
| R49 | Наклон `border` пере-снят (+0.708) и сторожится гейтом | `$ eval/.venv/bin/python eval/editor_tier/verify23.py` |
|
||||
| R50 | Таблица §0 эксп-23: строка R2 помечена как число АННУЛИРОВАННОГО run2 | `$ grep -q "run2, ТОЖЕ АННУЛИРОВАН" docs/experiments/23-editor-tier.md` |
|
||||
| R51 | Пере-штамповка `aj-border*` объявлена (копии `replication-runA`, mtime 23:31:54) | `$ grep -q "ПРОВЕНАНС КАТАЛОГОВ СУДЕЙСТВА .border. УТРАЧЕН" docs/experiments/23-editor-tier.md` |
|
||||
| R52 | Голоса `replication-runB` несли judge-имена прогона A: `ingest` починен, объявлено | `$ eval/.venv/bin/python eval/editor_tier/judge.py --run replication-runB border --score` |
|
||||
| R53 | Нумерация: два «п.7» в §8 эксп-23 и порядок §9 эксп-22 исправлены, ссылки поправлены | `$ grep -q "три ссылки в §13а и §14" docs/experiments/22-tenant-panel.md` |
|
||||
| R54 | `eval/README.md`: `editor_tier/` добавлен в карту действующих харнессов | `$ grep -q "editor_tier/. . ..exp-23" eval/README.md` |
|
||||
| R55 | Статус-баннеры на отчётах экспов, где их нет | `$ test $(grep -l "баннер фазы Д" docs/experiments/*.md \| wc -l) -ge 7` |
|
||||
| R56 | Шапка эндпоинтов `00-provider-quirks.md` актуализирована живым листингом | `$ grep -q "ЖИВОЙ ЛИСТИНГ ./models. ПЕРЕ-СНЯТ 2026-08-10" docs/experiments/00-provider-quirks.md` |
|
||||
| **НОРМЫ РИГА** | | |
|
||||
| R57 | Декой в каждой судейской пачке; сессия без пойманного декоя аннулируется целиком | — |
|
||||
| R58 | Шумовой пол — парой, чьи половины судят РАЗНЫЕ сессии (порог был занижен на 19%) | — |
|
||||
| R59 | Все армы единицы — в одном пакете одной сессии | — |
|
||||
| R60 | Идентичность судей персистится ДО запуска; половиной пола не брать боевую клетку | `$ eval/.venv/bin/python eval/dovodka/runs.py --selftest` |
|
||||
| R61 | Позиционный наклон замерен, вычтен, сторожится гейтом; донор декоя уравнен | `$ eval/.venv/bin/python eval/editor_tier/verify23.py` |
|
||||
| R62 | Судье запрещено сравнивать варианты между собой И читать их рядом | — |
|
||||
| R63 | Замок кассы снимает только поставивший его процесс (проверка живости PID) | — |
|
||||
| R64 | Реестр требований фазы — В GIT до покупок; conformance валиден только против закоммиченного | `$ eval/.venv/bin/python -c "import sys;sys.path.insert(0,'eval');import conformance as c;ok,why=c.frozen(c.Path('eval/dovodka/requirements.md'));print(why);sys.exit(0 if ok else 1)"` |
|
||||
| R65 | MDE каждой судейской оси — в пре-рег ДО покупок; недомощная ось объявляется описательной ЗАРАНЕЕ | `$ eval/.venv/bin/python eval/dovodka/power.py` |
|
||||
| R66 | На старте фазы пере-снят прайс DeepSeek; смена прайса → пере-смета и пинг ДО покупок | `$ grep -q "3в. ВАХТА D39.92 ИСПОЛНЕНА ПОВТОРНО 2026-08-10" docs/experiments/00-provider-quirks.md` |
|
||||
| **АНТИ-ЛЕНЬ** | | |
|
||||
| R67 | Не собралось с первой попытки → ДОБИВАТЬСЯ; отказ принимается только с журналом попыток | — |
|
||||
| R68 | Панель, ось или кандидата НЕ резать решением сессии — СТОП и пинг владельцу | — |
|
||||
| R69 | Обязательство с адресатом вне зоны закрывается ТОЛЬКО изменением файла вне зоны | `$ ls /home/ubuntu/books/editor-tier/sol-tier/*.txt` |
|
||||
| R70 | Детекторы и пороги после взгляда на вердикты не менять; девиация = СТОП и пинг В МОМЕНТ | — |
|
||||
| R71 | Правка рига чужого пака — отдельным коммитом + пере-снятие его гейтов | `$ eval/.venv/bin/python eval/tenant_panel/verify22.py` |
|
||||
| R72 | Приёмка адверсариальная author≠reviewer; опровергатель ДРУГОГО модельного семейства ОБЯЗАТЕЛЕН | — |
|
||||
| R73 | Финал — построчный аудит закрытия заказа | — |
|
||||
| **ОТЧЁТ И СДАЧА** | | |
|
||||
| R74 | Сверка H-1…H-4 с фактом отдельной таблицей, ВКЛЮЧАЯ опровержения | — |
|
||||
| R75 | CONFIRM/DENY владельцу — только с артефактом-носителем | — |
|
||||
| R76 | Деньги: итог по фазам ДВУМЯ путями счёта | — |
|
||||
| R77 | Пинг в `docs/PROGRESS.md` секция «Полигон» при закрытии | — |
|
||||
285
eval/dovodka/runs.py
Normal file
285
eval/dovodka/runs.py
Normal file
|
|
@ -0,0 +1,285 @@
|
|||
#!/usr/bin/env python3
|
||||
"""ЖУРНАЛ АГЕНТ-СЕССИЙ ФАЗЫ Д. $0. Кап 80 (заказ владельца 10.08).
|
||||
|
||||
⚠ ЗАЧЕМ ОТДЕЛЬНЫЙ МОДУЛЬ, А НЕ СТРОКА В `judge.py`. В паке 23 счётчик существовал и не работал:
|
||||
`log_run` звался из `--ingest`, то есть ПОСЛЕ того, как суб-агент отработал. На диске осталось
|
||||
20 записей против 58 фактических сессий; при капе 80 такой счётчик пропустил бы ещё 60 — сторожить
|
||||
кап можно ТОЛЬКО записью, сделанной ДО запуска. Здесь это и есть контракт: `claim()` пишет запись и возвращает имя судьи,
|
||||
которое передаётся суб-агенту; не позвал `claim` — не получил имени, и разбор проставит `?`.
|
||||
|
||||
⚠ ВТОРОЙ ДЕФЕКТ, КОТОРЫЙ ЧИНИТ ЭТОТ ЖЕ ФАЙЛ. Карта «токен → судья» в паке 23 лежала одна на
|
||||
ПРОХОД, а репликация судила ТЕ ЖЕ токены вторым жребием судей — и все её голоса получили имена
|
||||
судей боевого прогона. Здесь запись помнит ПРОГОН и ВРЕМЯ, и `judge_of` разводит два жребия по
|
||||
одним и тем же токенам ОКНОМ сессии `[at, done_at]`.
|
||||
|
||||
⚠ ТРЕТИЙ, И САМЫЙ ДОРОГОЙ. Атрибуция по времени работает, только пока сессии не перекрываются.
|
||||
Пак 23 отдал пять единиц на пере-суживание ДВАЖДЫ, не дождавшись первого круга: оба круга писали
|
||||
в одни файлы, `--ingest` прошёл между ними, и голоса разошлись с сырьём на 5 единицах из 32.
|
||||
Поэтому `claim` ОТКАЗЫВАЕТ, если сессия с пересекающимися токенами ещё не закрыта `done`.
|
||||
⚠ Первая редакция называла это «замком, который невозможно обойти механически» — неверно, и
|
||||
поймано приёмкой другого модельного семейства: `claim` делал read-modify-write журнала БЕЗ
|
||||
файлового лока, так что два параллельных вызова оба прочли бы леджер до записи друг друга,
|
||||
оба прошли бы проверку пересечения и получили один и тот же номер. Тот же класс, что «неатомарная
|
||||
проверка файла» в кассе эксп-22, стоившая ему границы пере-оплаты ≤$0.846103. Теперь журнал
|
||||
берётся под атомарный `O_CREAT|O_EXCL`-замок, как `money.py`, и проверка пересечения идёт ВНУТРИ
|
||||
критической секции.
|
||||
|
||||
Команды:
|
||||
runs.py --claim <прогон> <проход> <ток> [<ток> …] [--note "…"] запись ДО запуска суб-агента
|
||||
runs.py --done <n> закрыть сессию ПОСЛЕ ответа
|
||||
runs.py --report сколько израсходовано
|
||||
runs.py --selftest обязателен ДО первой сессии
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import contextlib
|
||||
import datetime as dt
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
OUT = Path.home() / "books" / "dovodka"
|
||||
LEDGER = OUT / "agent-runs.json"
|
||||
CAP = 80
|
||||
FAR_FUTURE = 1e18
|
||||
LOCK_TRIES = 600 # ×0.1 с — минута ожидания живого замка
|
||||
|
||||
|
||||
def _now() -> str:
|
||||
return dt.datetime.now(dt.timezone.utc).isoformat(timespec="microseconds")
|
||||
|
||||
|
||||
def _ts(iso: str | None) -> float:
|
||||
return dt.datetime.fromisoformat(iso).timestamp() if iso else FAR_FUTURE
|
||||
|
||||
|
||||
def used() -> list[dict]:
|
||||
return json.loads(LEDGER.read_text(encoding="utf-8")) if LEDGER.exists() else []
|
||||
|
||||
|
||||
def _write(rs: list[dict]) -> None:
|
||||
"""⚠ Запись АТОМАРНА: `write_text` усекает файл и пишет заново, и падение между этими двумя
|
||||
шагами оставляло журнал нечитаемым (поймано ревью исполнением: 2 прогона из 20). Пишем во
|
||||
временный файл и подменяем `os.replace` — она атомарна в пределах ФС."""
|
||||
OUT.mkdir(parents=True, exist_ok=True)
|
||||
tmp = LEDGER.with_suffix(".tmp")
|
||||
tmp.write_text(json.dumps(rs, ensure_ascii=False, indent=1), encoding="utf-8")
|
||||
os.replace(tmp, LEDGER)
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def _locked():
|
||||
"""Атомарный замок журнала. `O_CREAT|O_EXCL` — та же механика, что у кассы эксп-22.
|
||||
|
||||
⚠ Замок снимает ТОЛЬКО поставивший его процесс: в файл пишется PID, и протухшим замок
|
||||
считается лишь тогда, когда процесса с этим PID больше нет (норма рига, промт фазы Д).
|
||||
"""
|
||||
OUT.mkdir(parents=True, exist_ok=True)
|
||||
lock = LEDGER.with_suffix(".lock")
|
||||
for _ in range(LOCK_TRIES):
|
||||
try:
|
||||
fd = os.open(str(lock), os.O_CREAT | os.O_EXCL | os.O_WRONLY)
|
||||
os.write(fd, str(os.getpid()).encode())
|
||||
os.close(fd)
|
||||
break
|
||||
except FileExistsError:
|
||||
try:
|
||||
pid = int(lock.read_text() or 0)
|
||||
os.kill(pid, 0) # процесс жив — ждём
|
||||
except (ValueError, OSError):
|
||||
lock.unlink(missing_ok=True) # ставивший умер — снимаем
|
||||
continue
|
||||
time.sleep(0.1)
|
||||
else:
|
||||
raise SystemExit("⛔ журнал агент-сессий заперт дольше минуты — разберись, кто держит")
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
with contextlib.suppress(OSError):
|
||||
if lock.exists() and lock.read_text().strip() == str(os.getpid()):
|
||||
lock.unlink()
|
||||
|
||||
|
||||
def claim(run: str, pass_: str, tokens: list[str], note: str = "") -> dict:
|
||||
"""Занять агент-сессию ДО запуска суб-агента. Возвращает запись с именем судьи.
|
||||
|
||||
⚠ Кап проверяется ПЕРЕД записью, а не после: иначе 81-я сессия успевала бы отработать.
|
||||
"""
|
||||
with _locked():
|
||||
return _claim_locked(run, pass_, tokens, note)
|
||||
|
||||
|
||||
def _claim_locked(run: str, pass_: str, tokens: list[str], note: str) -> dict:
|
||||
rs = used()
|
||||
live = [r for r in rs if r.get("done_at") is None and set(r["tokens"]) & set(tokens)]
|
||||
if live:
|
||||
raise SystemExit(
|
||||
"⛔ ГОНКА СЕССИЙ: токены уже отданы незакрытой сессии "
|
||||
f"#{live[0]['n']} ({live[0]['judge']}, {sorted(set(live[0]['tokens']) & set(tokens))}).\n"
|
||||
" Дождись её ответов и закрой `--done`, либо возьми другие токены. Ровно на этом\n"
|
||||
" пак 23 потерял соответствие голосов и сырья на 5 единицах из 32.")
|
||||
n = len(rs) + 1
|
||||
if n > CAP:
|
||||
raise SystemExit(f"⛔ КАП АГЕНТ-СЕССИЙ ФАЗЫ Д ПРОБИТ: {n} > {CAP} — СТОП и пинг владельцу")
|
||||
rec = dict(n=n, at=_now(), done_at=None, run=run, pass_=pass_, judge=f"д-{n:02d}",
|
||||
tokens=list(tokens), note=note)
|
||||
rs.append(rec)
|
||||
_write(rs)
|
||||
return rec
|
||||
|
||||
|
||||
def done(n: int) -> dict:
|
||||
"""⚠ ТОЖЕ ПОД ЗАМКОМ. Первая редакция брала замок только в `claim`, а `done` делала свой
|
||||
read-modify-write мимо него — и параллельные `claim`+`done` теряли запись claim в 19 прогонах
|
||||
из 20: суб-агент запущен, имя судьи выдано, а в журнале его нет. Кап при этом недосчитывает,
|
||||
`judge_of` отдаёт `?`, а замок пересечения токенов пускает на них вторую сессию. Нашло ревью
|
||||
исполнением; селфтест этого не видел, потому что гонял только последовательные сценарии."""
|
||||
with _locked():
|
||||
return _done_locked(n)
|
||||
|
||||
|
||||
def _done_locked(n: int) -> dict:
|
||||
rs = used()
|
||||
hit = [r for r in rs if r["n"] == n]
|
||||
if not hit:
|
||||
raise SystemExit(f"⛔ сессии #{n} в журнале нет")
|
||||
hit[0]["done_at"] = _now()
|
||||
_write(rs)
|
||||
return hit[0]
|
||||
|
||||
|
||||
def judge_of(run: str, token: str, answer_mtime: float) -> str:
|
||||
"""Кто судил ЭТОТ ответ. Сессия владеет ответом, если он записан в её окне `[at, done_at]`.
|
||||
|
||||
Хвост допуска: ответ, пришедший ПОСЛЕ закрытия последней накрывающей сессии, отдаётся ей же —
|
||||
закрытие ставится руками и может отстать от файловой системы на секунды. Ответ РАНЬШЕ первой
|
||||
регистрации не отдаётся никому: он не мог быть ею произведён.
|
||||
"""
|
||||
cand = sorted((r for r in used() if r["run"] == run and token in r["tokens"]),
|
||||
key=lambda r: _ts(r["at"]))
|
||||
inwin = [r for r in cand if _ts(r["at"]) <= answer_mtime <= _ts(r.get("done_at")) + 5.0]
|
||||
if inwin:
|
||||
return inwin[-1]["judge"]
|
||||
started = [r for r in cand if _ts(r["at"]) <= answer_mtime]
|
||||
return started[-1]["judge"] if started else "?"
|
||||
|
||||
|
||||
def report() -> None:
|
||||
rs = used()
|
||||
print(f"агент-сессий израсходовано {len(rs)} из {CAP}")
|
||||
live = [r for r in rs if r.get("done_at") is None]
|
||||
per: dict[str, int] = {}
|
||||
for r in rs:
|
||||
per[r["run"]] = per.get(r["run"], 0) + 1
|
||||
for k, v in sorted(per.items()):
|
||||
print(f" {k:28s} {v}")
|
||||
if live:
|
||||
print(f"⚠ НЕЗАКРЫТЫХ сессий {len(live)}: " + " ".join(f"#{r['n']}" for r in live))
|
||||
|
||||
|
||||
def selftest() -> int:
|
||||
"""Проверка ИСПОЛНЕНИЕМ, а не чтением: журнал подменяется временным, гоняются сценарии."""
|
||||
global LEDGER, CAP # noqa: PLW0603
|
||||
keep_l, keep_c = LEDGER, CAP
|
||||
OUT.mkdir(parents=True, exist_ok=True)
|
||||
LEDGER = OUT / "SELFTEST-agent-runs.json"
|
||||
LEDGER.unlink(missing_ok=True)
|
||||
bad = 0
|
||||
|
||||
def ck(name: str, ok: bool, detail: str = "") -> None:
|
||||
nonlocal bad
|
||||
bad += not ok
|
||||
print(f"[{'OK ' if ok else 'ПРОВАЛ'}] {name}" + (f" {detail}" if detail else ""))
|
||||
|
||||
try:
|
||||
# 1. запись появляется ДО того, как что-либо отработало
|
||||
a = claim("проверка", "px", ["t1", "t2"], note="первый жребий")
|
||||
ck("claim пишет запись сразу", LEDGER.exists() and len(used()) == 1, f"n={a['n']}")
|
||||
ck("судья именован", a["judge"] == "д-01", a["judge"])
|
||||
|
||||
# 2. ЗАМОК ГОНКИ: те же токены нельзя отдать, пока сессия не закрыта
|
||||
try:
|
||||
claim("проверка", "px", ["t2"])
|
||||
ck("гонка сессий по одному токену запрещена", False, "пропустил вторую сессию")
|
||||
except SystemExit:
|
||||
ck("гонка сессий по одному токену запрещена", True)
|
||||
ck("отказавшая сессия НЕ записана", len(used()) == 1, f"{len(used())} записей")
|
||||
|
||||
# ⚠ ЗАМОК ПРОВЕРЯЕТСЯ ИСПОЛНЕНИЕМ, а не чтением. Прежний селфтест гонял только
|
||||
# последовательные сценарии и дыру read-modify-write не видел — её нашла приёмка
|
||||
# другого модельного семейства. Здесь замок ставится ЧУЖИМ живым PID и claim обязан
|
||||
# ждать, а не пройти мимо.
|
||||
global LOCK_TRIES # noqa: PLW0603
|
||||
keep_tries, LOCK_TRIES = LOCK_TRIES, 5 # не ждать минуту в селфтесте
|
||||
lock = LEDGER.with_suffix(".lock")
|
||||
lock.write_text(str(os.getpid())) # живой PID = замок держится
|
||||
t0 = time.time()
|
||||
try:
|
||||
claim("проверка", "px", ["t5"])
|
||||
waited = time.time() - t0
|
||||
ck("claim ЖДЁТ живой замок, а не проходит мимо", False,
|
||||
f"прошёл за {waited:.2f} с при занятом замке")
|
||||
except SystemExit:
|
||||
ck("claim ЖДЁТ живой замок, а не проходит мимо", True, "упёрся в таймаут — верно")
|
||||
finally:
|
||||
lock.unlink(missing_ok=True)
|
||||
LOCK_TRIES = keep_tries
|
||||
lock.write_text("999999999") # мёртвый PID = замок протух
|
||||
n_before = len(used())
|
||||
claim("проверка", "px", ["t6"])
|
||||
ck("протухший замок (мёртвый PID) снимается", len(used()) == n_before + 1)
|
||||
ck("замок за собой убран", not lock.exists())
|
||||
n_now = len(used())
|
||||
ck("непересекающиеся токены разрешены", claim("проверка", "px", ["t7"])["n"] == n_now + 1)
|
||||
|
||||
# 3. ВТОРОЙ ЖРЕБИЙ ПО ТЕМ ЖЕ ТОКЕНАМ — тот самый дефект пака 23
|
||||
t_first = _ts(a["at"]) + 0.001 # ответ пришёл в окне первой
|
||||
done(a["n"])
|
||||
b = claim("проверка", "px", ["t1", "t2"], note="второй жребий")
|
||||
t_second = _ts(b["at"]) + 0.001
|
||||
done(b["n"])
|
||||
ck("ответ ПЕРВОГО жребия атрибутируется первому",
|
||||
judge_of("проверка", "t1", t_first) == "д-01", judge_of("проверка", "t1", t_first))
|
||||
ck("ответ ВТОРОГО жребия атрибутируется второму",
|
||||
judge_of("проверка", "t1", t_second) == b["judge"],
|
||||
f"{judge_of('проверка', 't1', t_second)} против {b['judge']}")
|
||||
ck("ответ РАНЬШЕ первой регистрации не отдаётся никому",
|
||||
judge_of("проверка", "t1", _ts(a["at"]) - 10) == "?")
|
||||
ck("чужой прогон не атрибутируется", judge_of("другой", "t1", t_second) == "?")
|
||||
ck("незарегистрированный токен даёт '?'", judge_of("проверка", "нет", t_second) == "?")
|
||||
|
||||
# 4. КАП ОБЯЗАН ОТКАЗАТЬ ДО ЗАПУСКА, а не после
|
||||
CAP = len(used())
|
||||
try:
|
||||
claim("проверка", "px", ["t9"])
|
||||
ck("кап отказывает при исчерпании", False, "пропустил лишнюю сессию")
|
||||
except SystemExit:
|
||||
ck("кап отказывает при исчерпании", True)
|
||||
ck("сверх-капная сессия НЕ записана", len(used()) == CAP, f"{len(used())} записей")
|
||||
finally:
|
||||
LEDGER.unlink(missing_ok=True)
|
||||
LEDGER, CAP = keep_l, keep_c
|
||||
print(f"\n{'ЖУРНАЛ ГОДЕН' if not bad else f'ПРОВАЛОВ: {bad}'}")
|
||||
return bad
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
a = sys.argv[1:] or ["--report"]
|
||||
if a[0] == "--selftest":
|
||||
sys.exit(1 if selftest() else 0)
|
||||
elif a[0] == "--done":
|
||||
r = done(int(a[1]))
|
||||
print(f"сессия #{r['n']} ({r['judge']}) закрыта {r['done_at']}")
|
||||
elif a[0] == "--claim":
|
||||
note = ""
|
||||
if "--note" in a:
|
||||
i = a.index("--note")
|
||||
note, a = (a[i + 1] if len(a) > i + 1 else ""), a[:i]
|
||||
if len(a) < 4:
|
||||
raise SystemExit("нужно: --claim <прогон> <проход> <ток> [<ток> …]")
|
||||
r = claim(a[1], a[2], a[3:], note=note)
|
||||
print(f"сессия #{r['n']}/{CAP} · судья {r['judge']} · токенов {len(r['tokens'])}")
|
||||
else:
|
||||
report()
|
||||
|
|
@ -8,7 +8,7 @@
|
|||
Жанр книги: {{genre}}. Аудитория: {{audience}}. Книга: «{{title}}».
|
||||
|
||||
Твой мандат — художественная редактура черновика со сверкой по исходнику:
|
||||
- Сверяй смысл черновика с исходным текстом (язык «{{source_lang}}»): исправляй искажения смысла, неверно понятые реалии, кальки жестов и идиом — переводчик мог передать выражение буквально. Опирайся на исходник, но пиши по-русски, а не подстрочником.
|
||||
- Сверяй смысл черновика с исходным текстом (язык «{{source_lang}}»): исправляй искажения смысла, неверно понятые реалии, кальки жестов и идиом — переводчик мог передать выражение буквально (напр. `he raised an eyebrow` как «он поднял бровь» вместо «он вскинул брови»). Опирайся на исходник, но пиши по-русски, а не подстрочником.
|
||||
- Правь стиль: убирай кальки с языка «{{source_lang}}», канцелярит, лишние повторы, неестественные конструкции; пиши живым литературным русским. НО повтор, который стоит в тех же (параллельных) позициях самого исходника, — это авторский рефрен: сохраняй его, не «разнообразь» лексику там, где автор намеренно повторяется.
|
||||
- Имена собственные, термины и реалии передавай СТРОГО по приведённому глоссарию (если он приложен): приводи любые расхождения к каноническим формам, склоняя по контексту; не придумывай иных вариантов их передачи.
|
||||
- Вёрстка: собирай повествование в естественные русские абзацы. Реплики прямой речи начинай с нового абзаца через тире «—». Максимума длины абзаца нет.
|
||||
|
|
|
|||
46
eval/role_topology/prompts/ja-ru/editor.md
Normal file
46
eval/role_topology/prompts/ja-ru/editor.md
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
<!-- ПАР-ПАКЕТ ja→ru, заведён полигоном 10.08 для фазы Д (ось Д6, третья пара продукта).
|
||||
Производный от боевого backend/prompts/zh-ru/editor.md. Расхождения РОВНО те, что объявлены
|
||||
гейтом eval/dovodka/promptdiff.py: блок «Единицы и приёмы», секция ДИСКУРС-ПЕРЕВЁРСТКА и
|
||||
примеры (они обязаны быть на исходном языке пары). Все прочие строки перенесены дословно,
|
||||
чтобы сравнение пар не смешивалось с разницей формулировок — урок эксп-19. -->
|
||||
Ты — литературный редактор художественного перевода на русский язык. Тебе даны ИСХОДНЫЙ текст и его черновой перевод.
|
||||
Жанр книги: {{genre}}. Аудитория: {{audience}}. Книга: «{{title}}».
|
||||
|
||||
Твой мандат — художественная редактура черновика со сверкой по исходнику:
|
||||
- Сверяй смысл черновика с исходным текстом (язык «{{source_lang}}»): исправляй искажения смысла, неверно понятые реалии, кальки жестов и идиом — переводчик мог передать выражение буквально (напр. よろしくお願いします как «прошу хорошего обращения»). Опирайся на исходник, но пиши по-русски, а не подстрочником.
|
||||
- Правь стиль: убирай кальки с языка «{{source_lang}}», канцелярит, лишние повторы, неестественные конструкции; пиши живым литературным русским. НО повтор, который стоит в тех же (параллельных) позициях самого исходника, — это авторский рефрен: сохраняй его, не «разнообразь» лексику там, где автор намеренно повторяется.
|
||||
- Имена собственные, термины и реалии передавай СТРОГО по приведённому глоссарию (если он приложен): приводи любые расхождения к каноническим формам, склоняя по контексту; не придумывай иных вариантов их передачи.
|
||||
- Вёрстка: собирай повествование в естественные русские абзацы, НЕ копируя построчную разбивку исходника (веб-новелла набирается построчно). Реплики прямой речи начинай с нового абзаца через тире «—». Максимума длины абзаца нет.
|
||||
- Сохраняй ПОЛНОТУ черновика: не выбрасывай и не добавляй предложения; не пересочиняй сцены.
|
||||
- Единицы и приёмы (общие для ja→ru): МЕРЫ приводи к привычным читателю единицам (традиционные 尺/里/坪 — метрическими либо привычными приблизительными оборотами). ЗВУКОПОДРАЖАНИЯ и ОБРАЗОПОДРАЖАНИЯ (擬音語/擬態語) передавай русским глаголом, наречием или образом, НЕ слоговой транслитерацией. ХОНОРИФИКИ и уровни вежливости веди по брифу и держи одну форму; смену вежливости передавай русским регистром. ОПУЩЕННОЕ ПОДЛЕЖАЩЕЕ восстанавливай по контексту. ТРАНСКРИПЦИЯ — по Поливанову, не по ромадзи. СТИХИ и названия классики — по смыслу, не транслитерацией. Род и форму имён и терминов сверяй по глоссарию.
|
||||
- Хонорифики: {{honorifics}}. Баланс форенизация/доместикация: {{venuti}}.
|
||||
|
||||
Выведи ТОЛЬКО отредактированный текст перевода — без служебных преамбул, комментариев, пояснений, заметок о правках и markdown-заголовков.
|
||||
|
||||
ДИСКУРС-ПЕРЕВЁРСТКА (приведение японского членения и эллипсиса к русской абзацной и синтаксической норме — это КОНВЕНЦИЯ русского языка, а не вольность):
|
||||
1. Молча определи смысловые ходы фрагмента: единый фокал/наблюдатель; действие и его непосредственная реакция; одна тема рассуждения; границы — смена говорящего, времени, места или фокала.
|
||||
2. Один повествовательный ход оформляй ОДНИМ русским абзацем, ДАЖЕ если японский исходник разбит на однофразовые строки (веб-новелла набирается построчно). Инструмент слияния — причастные и деепричастные обороты, подчинительные союзы и связки. Не сливай ТОЛЬКО ради длины и не теряй смысловые атомы.
|
||||
3. Каждую новую реплику начинай с нового абзаца через тире «—»; слова автора при той же реплике оставляй в её абзаце. Японские скобки прямой речи 「」 заменяй русским тире, 『』 — кавычками-ёлочками.
|
||||
4. Разрешено объединять и делить русские предложения; число предложений и абзацев совпадать с исходником НЕ обязано. Запрещено терять смысловые атомы (участников, действия, числа, полярность, заголовки, а также идиомы и ёдзидзюкуго: их передавай смыслом, не сворачивая в общее место и не калькируя дословно).
|
||||
|
||||
---FEWSHOT---
|
||||
Примеры требуемой ПЕРЕВЁРСТКИ (показана структура вывода, не стиль):
|
||||
|
||||
[narrative N:1] Исходник построчно — «扉を開けて外に出た。» / «空は灰色だった。» / «彼は息をついた。» / «道はどこまでも続いていた。» — становится ОДНИМ абзацем:
|
||||
Он вышел за дверь. Небо было серым; вздохнув, он смотрел, как дорога уходит вдаль.
|
||||
|
||||
[dialogue 1:N] Исходник — «叔父は眉をひそめて言った。「時が経つのは早いな。座れ。」「ありがとうございます」と甥が答えた。» — становится:
|
||||
Дядя нахмурился.
|
||||
— Время летит. Садись, — сказал он.
|
||||
— Спасибо, — ответил племянник.
|
||||
|
||||
[focal-shift] При смене наблюдателя/места/времени — новый абзац, даже если в исходнике это продолжение той же строки.
|
||||
|
||||
---USER---
|
||||
Исходный текст (язык «{{source_lang}}»):
|
||||
|
||||
{{text}}
|
||||
|
||||
Черновик перевода для редактуры:
|
||||
|
||||
{{draft}}
|
||||
30
eval/role_topology/prompts/ja-ru/translator.md
Normal file
30
eval/role_topology/prompts/ja-ru/translator.md
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
<!-- ПАР-ПАКЕТ ja→ru, заведён полигоном 10.08 для фазы Д (ось Д6, третья пара продукта).
|
||||
Производный от боевого backend/prompts/zh-ru/translator.md. Расхождения РОВНО те, что объявлены
|
||||
гейтом eval/dovodka/promptdiff.py: блок «Единицы и приёмы», секция ДИСКУРС-ПЕРЕВЁРСТКА и
|
||||
примеры (они обязаны быть на исходном языке пары). Все прочие строки перенесены дословно,
|
||||
чтобы сравнение пар не смешивалось с разницей формулировок — урок эксп-19. -->
|
||||
Ты — профессиональный литературный переводчик с языка «{{source_lang}}» на язык «{{target_lang}}».
|
||||
Жанр книги: {{genre}}. Аудитория: {{audience}}. Книга: «{{title}}».
|
||||
|
||||
Правила перевода (translation brief):
|
||||
- Хонорифики: {{honorifics}}. Транскрипция имён и реалий: {{transcription}}.
|
||||
- Баланс форенизация/доместикация (0 — полная адаптация, 1 — сохранение чужого): {{venuti}}.
|
||||
- Сноски: {{footnotes}}.
|
||||
- Переводи ВСЕ предложения исходника: ничего не пропускай, не сокращай и не добавляй от себя.
|
||||
- Вёрстка — по нормам русского языка, НЕ копируй построчную разбивку исходника: реплики прямой речи оформляй с нового абзаца через тире «—».
|
||||
- Пиши живым литературным русским языком; избегай канцелярита и калек с исходного языка.
|
||||
|
||||
Единицы и приёмы (общие для ja→ru):
|
||||
- МЕРЫ переводи в привычные читателю единицы: традиционные 尺 (сяку ≈ 30 см), 寸, 里 (ри ≈ 3.9 км), 坪, 石, 両 передавай метрическими либо привычными приблизительными оборотами, сам термин не транслитерируй; современные метрические единицы оставляй как есть.
|
||||
- ЗВУКОПОДРАЖАНИЯ и ОБРАЗОПОДРАЖАНИЯ (擬音語 / 擬態語: ドキドキ, じっと, ぼんやり) — отдельный слой, и калькировать их нельзя: передавай русским глаголом, наречием или образом («сердце заколотилось», «замер», «рассеянно»), а не слоговой транслитерацией.
|
||||
- ХОНОРИФИКИ и вежливые формы: суффиксы (-сан, -кун, -тян, -сама, -сэмпай) веди по правилу брифа {{honorifics}} и держи ОДНУ форму на весь текст; смену уровня вежливости (敬語 → простая форма) передавай РУССКИМ регистром — обращением на «вы»/«ты» и лексикой, а не пометками.
|
||||
- ОПУЩЕННОЕ ПОДЛЕЖАЩЕЕ восстанавливай по контексту: японская фраза сплошь и рядом обходится без него, русская — нет. Не выдумывай участника, которого в сцене нет.
|
||||
- ТРАНСКРИПЦИЯ имён и реалий — по системе Поливанова (си, дзи, тя, дзю), а не по английской ромадзи (shi, ji, cha, ju); одна форма имени на весь текст.
|
||||
- СТИХИ и названия классики передавай ПО СМЫСЛУ (не транслитерацией), опираясь на существующие русские переводы.
|
||||
- Род и форму имён собственных и терминов бери из приложенного глоссария (если он есть).
|
||||
Выведи ТОЛЬКО перевод, без служебных преамбул, комментариев, пояснений и markdown-заголовков.
|
||||
|
||||
---USER---
|
||||
Переведи следующий фрагмент:
|
||||
|
||||
{{text}}
|
||||
Loading…
Add table
Reference in a new issue