Land stage 0 item 1: the bank holds its terms, and the audit found on disk a production-profile run, a July measurement of the same question, and a ratified editor-price input this phase never executed

This commit is contained in:
heaven 2026-08-30 20:10:54 +03:00
parent 62f8f6bef9
commit cdc5a5f156
4 changed files with 626 additions and 1 deletions

View file

@ -7534,3 +7534,177 @@ ln 38 = 3.64, то есть **примерно 3.1 sd — событие ред
компонента одна — наклон раскладки: он в тексте не живёт по построению. Корреляция — справочно.
* **Объём правки не предсказывает место `Z0` у читателя** (0.18, незначимо): «больше правил — лучше
прочли» не наблюдается.
---
## Д50. Стадия 0 п.1 исполнена: банк держит термины — но главное найдено НЕ в замере, а на диске
**Дата:** 30.08.2026, полигон. **Стоимость: $0** (только артефакты на диске). Носитель прибора —
`eval/dovodka/bank.py` (пре-регистрация в шапке файла, записана ДО первого числа).
### Д50.1. ⭐ Четыре вещи, которых консилиум не знал, и все они лежали на диске
Консилиум 30.08 назначил аудит прогона `acceptance`. Исполнение показало, что заказ был построен
на неполной карте собственного архива:
1. **Рядом лежит более боевой прогон `rerun`** (11.07): промты `v1-reflow` вместо `v0-draft`,
редактор БИЛИНГВ (D30.1), переведены 57/57 чанков против 54/57. Движковых промахов 27 против 55.
2. **Лежит и сам БОЕВОЙ ПРОФИЛЬ — `rerun2-dspro`** (23.07, 5 глав): черновик `deepseek-v4-flash` +
редактор **`deepseek-v4-pro`**, плюс арм `mistral-large-2512`, которого нет ни в одной нашей
таблице моделей. ⇒ Утверждение фазы «боевой профиль недоступен без Шага 7 (~$2)» верно только
про ОБЪЁМ (5 глав против 15), а не про наличие.
3. **Эту работу уже делали в июле, и сильнее.** `docs/archive/PROGRESS-2026-07-10-13.md:149` — замер
B5 D10 независимым скриптом: presence 91.0% (647/711), occurrence 95.8%, по типам (титулы —
слабейший класс, 78.7%), классы промахов `decl_gap 0` · `inflection_gap 54` · `genuine_absent 10`,
эффективная консистентность 98.6%.
4. **Три редактора уже отсужены на боевой китайской паре**`rerun2/RERUN_REPORT.md`: 56 игр на арм,
судья `gemini-3.1-pro` + `grok-4.3`-фолбэк, шумовой пол замерен (14/14 tie на идентичном тексте).
Ранг: **dspro 0.679 > glm-5 0.589 >> mistral 0.232**. ⇒ Оговорка консилиума §4 «вся наша фактура
по дешёвым моделям АНГЛИЙСКАЯ» неверна для ВЫБОРА РЕДАКТОРА: он мерен на китайской паре.
⛔ **Класс ошибки один и он не про эти четыре пункта, а про фазу: «фаза не знала собственный архив».**
Вчера тем же классом было «фаза не знала собственный бэкенд». Дешёвое лекарство очевидно и стоит $0:
**прежде чем строить прибор — смотреть, не построен ли он.**
**ПЯТАЯ ТИХАЯ ПОЛОМКА ПЕРЕЕЗДА.** Июльские скрипты (`d10_consistency.py`, `audit.sh`, `reflow.py`,
`extract.py`) НЕ ПЕРЕЖИЛИ переезд машины: `PROGRESS-2026-07-10-13.md:190` перечисляет их поимённо с
пометкой «Всё ВНЕ git», и в каталоге остались только ДАННЫЕ. После путей `~/books`, обнулённых mtime
и подчёркиваний в `.env` — четвёртая известная, а по счёту находок пятая. **Класс: что вне git, то
переезд убивает молча.** `bank.py` заведён в git намеренно.
### Д50.2. Результат замера: гипотеза «банк держит» НЕ ОПРОВЕРГНУТА
Единица — пара (термин, чанк) со сработавшим ключом; принятое множество форм = `dst` `decl.forms`
(ратифицированное D24.4). Эндпойнт — CONFIRMED (`approved`); с AMBIGUOUS спрашивать нельзя, движок
её таковой не объявлял (`mempostcheck.go:52-64`).
| прогон / режим | пар | presence | взвешенная доля промахов | эффективная консистентность |
|---|---|---|---|---|
| `acceptance`, native | 375 | 98.9% | **0.23%** | 100.0% |
| `acceptance`, substring | 390 | 98.5% | **0.62%** | 99.5% |
| `rerun`, native | 531 | 97.4% | **0.91%** | 99.6% |
| `rerun`, substring (методика июля) | 548 | 96.9% | **1.29%** | 99.3% |
⚠ Числа сняты с ФИНАЛЬНОЙ версии прибора. Промежуточная, до починки регистра, давала presence на
0.50.9 пункта ниже; таблица едва не ушла в коммит с ними — см. Д50.4 п.4.
**Метрика A (порог 2%) — в пределах во всех четырёх сечениях.** Метрика B пробита в одном сечении
единственным термином `炼化` и разобрана ниже как ЛОЖНАЯ.
**Взаимная верификация двух независимых реализаций через полтора месяца.** Мой прибор
самостоятельно воспроизвёл июльские `decl_gap = 0`, `genuine_absent = 10` (до починки эвристики) и —
поимённо — редчайший класс `古月 → Гуюэ` ровно в главах **18/0 и 20/1**, названных июльским отчётом.
Совпадение поимённое, а не по величине.
### Д50.3. ⭐ Слепое пятно, которого не видит НИ ОДИН из двух приборов
Чтение кандидатов глазами дало находку, не сводимую к обоим счётчикам. Гл.18/ч0: оригинал
`古月方源`, в переводе — **«Гу Юэ Фан Юань»**, тогда как канон банка — слитное «Гуюэ».
Почему молчат оба прибора: `古月方源` заведён **алиасом полного имени** (`glossary_aliases`,
`alias_type=fullname`) к записи `方源 → Фан Юань`. Автомат движка берёт длиннейший ключ, ждёт в
выходе «Фан Юань» — и находит его. Родовая приставка канону не соответствует, но её никто не
проверяет: ожидаемая часть на месте. ⇒ **Дрейф ВНУТРИ составного имени не наблюдаем ни пост-чеком
движка, ни `presence`-метрикой.** Дёшево чинится: проверять алиас-ключ на собственную канонную
форму, а не только dst записи-хозяина.
### Д50.4. Четыре ложные тревоги МОЕГО прибора — все пойманы чтением, ни одна не опубликована
Прибор строился и правился в один заход, и каждая правка рождала новую ошибку — ровно вчерашний урок
«правка правки не безопаснее правки», подтверждённый четырежды за один пункт плана:
1. ⛔ **Зелёный вердикт на ПУСТОМ знаменателе.** Парсер не понял формат `rerun` (там `--- чанк 0 ---`
без флага), разобрал 0 чанков — и прибор напечатал «порог не пробит, гипотеза не опровергнута».
Лечение — не только формат: заведён гейт `assert_measured`, отказывающий с кодом 2 при нулевом
знаменателе. **Нулевой знаменатель — не «в пределах», а отсутствие замера.**
2. ⛔ **Промахи на непереведённых чанках.** 4 чанка `acceptance` гейт покрытия не переводил вовсе;
отсутствие термина там — дефект ПОКРЫТИЯ, не банка.
3. ⛔ **Односимвольные ханьские ключи** `蛊`/`转`: `memory.go:319` удаляет такой ключ из автомата —
записи не инжектировались НИКОГДА. Мой счёт спрашивал за них и дал ⛔ по метрике B.
4. ⛔ **Регистр и чередование согласных.** `开窍 → «открытие апертуры»` считалось промахом при
«Открытие апертуры удалось!» в тексте; `炼化 → «очистить и подчинить»` уезжало в кандидаты дрейфа,
потому что «очистить»[:4] = «очис», а в тексте «очищает».
**Все семь «промахов» `炼化` — верный перевод** («очищает и подчиняет», «очистил и подчинил», «по
очистке и подчинению») при **ПУСТОМ `decl.forms`**. Метрика B по нему пробита формально и по существу
ложна: это дефект ЗАСЕВА глагольного термина, а не дрейф.
### Д50.5. ⛔ Что из этого — долг полигона (моя зона)
* **Глагольные и многословные термины засеяны без форм.** `炼化 → «очистить и подчинить»`,
`forms=[]` — пост-чек по такому термину не может не врать. Требуется засев форм либо явное
исключение класса из пост-чека.
* **`inflection_gap` живёт** (1617 CONFIRMED на `rerun`): мн.ч. дописано сидом v2 не всюду.
* ⚠ **Осторожно, поправка к самому себе:** первая редакция этого разбора объявила июльскую задачу
«дописать мн.ч.» НЕВЫПОЛНЕННОЙ — по чтению `books/gu-zhenren/guzhenren-seed.yaml`. Это неверно:
рядом лежит **`guzhenren-seed-v2.yaml`**, и мн.ч. там есть («первобытные камни», «гу-мастеров»,
«смертные»), плюс 8 новых записей. Класс ошибки: **вывод по первому найденному файлу без проверки,
единственный ли он.** Поймано до публикации.
---
## Д51. ⭐⭐ Вход ратификации, висевший на фазе Д, исполнен: выбор редактора держится на цене, а цена перевернулась
**Дата:** 30.08.2026, полигон. **Стоимость: $0.** Носитель — пересчёт оплаченных клеток `rerun2`.
### Д51.1. Задача была поставлена ратифицированной нотой ЭТОЙ фазе и не исполнена
Цепочка, восстановленная по журналу решений (не по пересказу):
* **D39.22** (23.07): редактор = `deepseek-v4-pro`, интерим. Основания: судья №1 · лучшая проза и
глоссарий · дефекты чинибельного класса · **«×2 дешевле glm»**.
* **D39.117 п.3, строка 65 ЗАКРЫТА** (08.08): «движки dspro/glm-5 **неразличимы** (`A/B` +0.06
p=0.95; `A_law/B` 0.69 порога не проходит) ⇒ **жилец редактора — вопрос ЦЕНЫ, то есть конфиг**».
* **D39.137** (15.08): пере-пин вендора. «Посылка интерим-редактора D39.22 в ПИКЕ ПЕРЕВЁРНУТА —
dspro пик ×1.26 ДОРОЖЕ glm-5… **Вход ратификации жильцов при фазе Д**» (`05-decisions-log.md:380`).
**Ратификация пере-выбора редактора формально висит на фазе Д с 15.08 и не исполнена.** Консилиум
2930.08 обсуждал «резать ли редакторскую стадию», не зная, что настоящий открытый вопрос —
не «резать», а «кто в ней живёт по нынешней цене», и что он адресован именно нам.
### Д51.2. Замер: одна нагрузка, оба редактора, СОБСТВЕННЫЕ токены каждого
D39.137 считала контрфактику по оси цены **на токенах glm** и сама назвала это оговоркой. В `rerun2`
есть лучшее: три арма редактора **на одном и том же оплаченном черновике**, каждый со своим расходом.
**Валидация метода — прежде результата:** пересчёт по СТАРОМУ прайсу воспроизводит записанный
движком `cost_usd` **бит-в-бит на всех трёх армах** (glm $0.076583, dspro $0.040555, mistral
$0.040093 — расхождение 0.0%). Значит прайс-таблица и формула те самые, которыми считал движок.
| редактор | prompt / cached / completion | нынешний прайс, 5 глав | книга 1500 глав | к glm |
|---|---|---|---|---|
| `glm-5` | 33 831 / 3 456 / 14 224 | $0.0766 | **$22.97** | — |
| `deepseek-v4-pro`, пик | 39 795 / 8 192 / 30 779 | $0.1640 | **$49.19** | **×2.14** |
| `deepseek-v4-pro`, офф-пик | те же | $0.0820 | $24.59 | ×1.07 |
| `mistral-large-2512` | 39 119 / 1 232 / 13 689 | $0.0401 | $12.03 | ×0.52 ⛔ исключён D39.20 |
**Оговорка, снимающая часть разрыва:** из 11 записей арма dspro три — записи санитайзера по $0, а
одна оборвалась по `finish=length` и пере-генерировалась. Обрыв — артефакт флора `min_max_tokens`
8000, поднятого позже до 16000. Без него: пик $0.1238 → **книга $37.14 (×1.62)**, офф-пик $0.0619 →
**$18.57 (×0.81)**.
### Д51.3. Что из этого следует и чего НЕ следует
* ⭐ **В ПИКЕ dspro дороже glm-5 при любом обращении с артефактом — в 1.62.1 раза.** Экономическое
основание D39.22 в пиковые часы не просто ослабло, а сменило знак.
* ⚠ **Уточнение к D39.137, и оно в сторону ХУДШЕГО для dspro.** Нота дала «офф-пик дешевле ×1.59»;
на собственных токенах офф-пик даёт от ×1.07 ДОРОЖЕ до ×0.81 дешевле. Причина проста и видна в
таблице: **dspro тратит вдвое больше completion-токенов** (30 779 против 14 224), чего контрфактика
на чужих токенах увидеть не могла.
* ⛔ **Из этого НЕ следует «менять редактора».** Три причины, и все три — не мои полномочия:
(1) D39.117 закрыл строку 65 на «неразличимы», но неразличимость мерена ригом фазы, а судья
`rerun2` ставил dspro первым (0.679 против 0.589) — приборы согласны лишь в том, что зазор мал;
(2) пик у DeepSeek — только будни 0104 и 0610 UTC, то есть ответ зависит от РАСПИСАНИЯ прогона,
а не только от модели; (3) выбор жильца — конфиг `backend/`, зона бэкенда и лендинг оркестратора.
* ⛔ **Носитель решения врёт прямо сейчас.** `backend/configs/pipeline-c1.yaml:65` и
`backend/configs/pipeline-arm-glm.yaml:6` до сих пор обосновывают выбор словами «×2 дешевле glm».
Это устаревшее обоснование в БОЕВОМ конфиге. Правка — зона бэкенда; **пинг сдан этой секцией.**
### Д51.4. Дешёвый ход, который виден из этих же данных
Если жилец — вопрос цены (D39.117), то у вопроса есть ответ, не требующий смены модели:
**гнать редакторскую волну в офф-пик.** Для DeepSeek офф-пик — это все выходные целиком и будни вне
двух окон; скидка ровно ½ и не требует ни решения о модели, ни правки Go. На книге это разница
между $49 и $25 у того же самого редактора, с которым фаза уже согласилась по качеству.
⚠ Тарифная погода не климат: несущую архитектуру на скидочное окно не ставят, но ПЛАНИРОВЩИК волн —
ровно то место, где расписание законно.

View file

@ -6,6 +6,31 @@
# КОНСИЛИУМ 30.08 — АРХИТЕКТУРА ГЕНЕРАЦИИ, МОДЕЛИ, УБОРКА
> ⚠⚠ **ПОПРАВКИ ОТ 30.08 ПОСЛЕ ИСПОЛНЕНИЯ СТАДИИ 0 п.1 — ЧИТАТЬ ПРЕЖДЕ ТЕЛА.** Разбор — **Д50 и
> Д51** отчёта `docs/experiments/23-editor-tier.md`. Консилиум строился на неполной карте
> собственного архива, и четыре его положения этим затронуты:
>
> 1. ⛔ **§5 п.1 назначал аудит `acceptance`. Объект выбран неверно:** рядом лежит более боевой
> `rerun` (11.07, промты `v1-reflow`, редактор-билингв, 57/57 чанков), а также **`rerun2-dspro`
> (23.07) — настоящий БОЕВОЙ профиль с редактором `deepseek-v4-pro`** на 5 главах. «Боевого
> профиля нет без Шага 7» верно про ОБЪЁМ, не про наличие.
> 2. ✅ **Сам п.1 ИСПОЛНЕН, вывод: гипотеза «банк держит» в терминной половине НЕ ОПРОВЕРГНУТА**
> взвешенная доля промахов 0.231.29% при пороге 2%, эффективная консистентность 99.3100%.
> Прибор `eval/dovodka/bank.py` независимо воспроизвёл июльский замер, включая поимённое
> совпадение редчайшего класса. **Работа уже делалась в июле; её скрипты погибли при переезде
> (были вне git) — ПЯТАЯ тихая поломка того же класса.**
> 3. ⛔ **§4 «вся наша фактура по дешёвым моделям АНГЛИЙСКАЯ» — неверно для ВЫБОРА РЕДАКТОРА.**
> Три редактора отсужены на боевой китайской паре (`rerun2/RERUN_REPORT.md`, 56 игр на арм,
> шумовой пол замерен): dspro 0.679 > glm-5 0.589 >> mistral 0.232.
> 4. ⛔⛔ **§7 и §6 п.3 ставят вопрос не там.** Открытый вопрос не «резать ли редактора», а «кто в
> нём живёт по нынешней цене» — и он адресован ФАЗЕ Д ратифицированной нотой **D39.137 от
> 15.08** («вход ратификации жильцов при фазе Д»), не исполненной. Замер сделан (Д51):
> **в пике `deepseek-v4-pro` дороже `glm-5` в 1.62.1 раза**, в офф-пик — от ×1.07 дороже до
> ×0.81 дешевле. ⚠ Это ХУЖЕ для dspro, чем оценка самой D39.137 (×1.59 дешевле в офф-пик),
> потому что та считала на чужих токенах, а dspro тратит вдвое больше своих.
> ⛔ Боевой конфиг `backend/configs/pipeline-c1.yaml:65` до сих пор обосновывает выбор словами
> «×2 дешевле glm» — устаревшее обоснование в живом файле; пинг бэкенду сдан в Д51.3.
**Заказ владельца дословно:** (1) насколько сложной делать архитектуру харнесса — качество без
взлёта цены, с учётом банка памяти; что делает черновая модель; нет ли сдвигов в ландшафте;
(2) на каких моделях выгодно ехать по цене/качеству; (3) нет ли ошибок в замерах и ошибок в выводах

View file

@ -7,7 +7,36 @@
> ⚠ Персист, не scratch (норма владельца 01.08): файл лежит в зоне и коммитится, потому что
> эфемерный scratchpad умирает вместе с сессией.
## 0-БИС. ⭐⭐ ГЛАВНОЕ ПОСЛЕ КОНСИЛИУМА 30.08 — ЧИТАТЬ ПЕРВЫМ, ОСТАЛЬНОЕ НИЖЕ ИСТОРИЧНО
## 0-ТРИ. ⭐⭐ СОСТОЯНИЕ НА ВЕЧЕР 30.08 — САМОЕ СВЕЖЕЕ, ЧИТАТЬ ПЕРВЫМ
**Стадия 0 п.1 ИСПОЛНЕНА.** Разбор — **Д50 и Д51** в `docs/experiments/23-editor-tier.md`, прибор —
`eval/dovodka/bank.py` (в git намеренно), поправки к курсу — врезка в шапке `KONSILIUM-30-08.md`.
1. ✅ **Банк держит термины.** Взвешенная доля промахов CONFIRMED 0.231.29% при пороге 2%;
эффективная консистентность 99.3100%. Гипотеза в терминной половине НЕ ОПРОВЕРГНУТА.
2. ⭐⭐ **Главное найдено не замером, а на диске.** Лежат: более боевой прогон `rerun`; **БОЕВОЙ
профиль `rerun2-dspro` (редактор `deepseek-v4-pro`, 5 глав)**; июльский замер того же вопроса;
**отсуженная на КИТАЙСКОЙ паре тройка редакторов** (dspro 0.679 > glm 0.589 >> mistral 0.232).
3. ⭐⭐ **Ратифицированный вход фазы Д, висевший с 15.08, исполнен (Д51).** D39.117: жилец редактора —
вопрос ЦЕНЫ. D39.137: посылка «dspro ×2 дешевле glm» перевёрнута. Замер на одной нагрузке
собственными токенами: **в пике dspro дороже glm в 1.62.1×**, в офф-пик от ×1.07 дороже до ×0.81
дешевле. Дешёвый ход без смены модели — **гнать редакторскую волну в офф-пик** (÷2).
4. ⛔ **Пинги, сданные секцией (чужие зоны, сам не правил):** `pipeline-c1.yaml:65` и
`pipeline-arm-glm.yaml:6` обосновывают выбор устаревшим «×2 дешевле glm»; `docs/PROGRESS.md:610`
несёт снятую сильную форму «полей пола в банке нет вовсе, механизма не существовало».
5. ⛔ **ПЯТАЯ тихая поломка переезда:** июльские скрипты замера (`d10_consistency.py` и др.) были ВНЕ
git и погибли. Класс: **что вне git, то переезд убивает молча.**
6. ⚠ **Четыре ложные тревоги собственного прибора, пойманные чтением, — Д50.4.** Из них главная:
**прибор напечатал «гипотеза не опровергнута» на НУЛЕВОМ знаменателе**, когда парсер не понял
формат. Заведён гейт `assert_measured` (код 2). Плюс: непереведённые чанки · односимвольные
ханьские ключи вне автомата · регистр и чередование согласных.
⚠ И поправка к себе: «долг полигона по мн.ч. не выполнен» — НЕВЕРНО, есть `guzhenren-seed-v2.yaml`;
читал первый найденный файл, не проверив, единственный ли он.
**Остаток Стадии 0:** п.2 (разбор диффов по категориям) · п.3 (позиционный аудит панелей + латинский
квадрат) · п.5 (базис Жаккара) · п.6 (уборка). П.4 — греп исполнен, находка в п.4 выше.
## 0-БИС. ⭐⭐ ГЛАВНОЕ ПОСЛЕ КОНСИЛИУМА 30.08 — ОСТАЛЬНОЕ НИЖЕ ИСТОРИЧНО
**Твой курс лежит в `eval/dovodka/KONSILIUM-30-08.md`.** Там итог шести раундов с архитектором,
четырёх со скептиком и одного с разведкой: что установлено, что СНЯТО, план по стадиям, и что

397
eval/dovodka/bank.py Normal file
View file

@ -0,0 +1,397 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
bank.py АУДИТ УДЕРЖАНИЯ БАНКА на движковых прогонах 蛊真人 (zhru, 25 глав).
Стадия 0 п.1 плана `eval/dovodka/KONSILIUM-30-08.md` §5. Стоимость $0: только артефакты на диске.
ПОПРАВКА КУРСА, НАЙДЕННАЯ ИСПОЛНЕНИЕМ 30.08 ЧИТАТЬ ПРЕЖДЕ ЧИСЕЛ
Консилиум назначил аудит прогона `acceptance`, НЕ ЗНАЯ ДВУХ ВЕЩЕЙ:
1. РЯДОМ ЛЕЖИТ БОЛЕЕ ПОЗДНИЙ И БОЛЕЕ БОЕВОЙ ПРОГОН `rerun` (11.07, `~/books/gu-zhenren/rerun/`).
Он собран из боевого `pipeline-c1.yaml` post-D33 байт-в-байт: промты `v1-reflow` (не `v0-draft`),
редактор БИЛИНГВ (D30.1), переведены все 57 чанков (у `acceptance` 54 из 57). Движковых
промахов там 27 против 55. ОБЪЕКТ АУДИТА ОБА, и `rerun` важнее.
Боевым он всё равно НЕ является: редактор `glm-5`, а не `deepseek-v4-pro`.
2. ЭТУ РАБОТУ УЖЕ ДЕЛАЛИ В ИЮЛЕ, и результат сильнее плановой заявки. `docs/archive/
PROGRESS-2026-07-10-13.md:149` (замер B5 D10, независимый скрипт `rerun/d10_consistency.py`):
presence 91.0% (647/711) · occurrence 95.8% (3072/3207)
по типам: имена 98.6% · клички 100% · места 96.0% · термины 94.9% · ТИТУЛЫ 78.7%
промахи: decl_gap 0 · inflection_gap 54 · genuine_absent 10 эффективная 98.6%
Сами СКРИПТЫ не пережили переезд машины лежали ВНЕ git (`PROGRESS-2026-07-10-13.md:190`
перечисляет их поимённо; в каталоге остались только данные). Это ЧЕТВЁРТАЯ тихая поломка
переезда после путей `~/books`, обнулённых mtime и подчёркиваний в `.env`. Класс один:
ЧТО ВНЕ GIT, ТО ПЕРЕЕЗД УБИВАЕТ МОЛЧА. Этот файл в git намеренно.
Июльские числа здесь не пересказываются как свои: они ЭТАЛОН СВЕРКИ. Совпадение = две
независимые реализации через полтора месяца согласились. Расхождение = находка, а не шум.
ПРЕ-РЕГИСТРАЦИЯ ПРАВИЛО РЕШЕНИЯ ЗАПИСАНО ДО ПЕРВОГО ЧИСЛА
ЧТО МЕРЯЕМ. Вопрос 0 в его ТЕРМИННОЙ половине: удерживает ли банк канонные формы через 25 глав.
Голосовая половина им НЕ закрывается ПО ПОСТРОЕНИЮ колонки `first_person`/`speech` пусты 0/49
(Д32.16, консилиум §5 п.1).
ЕДИНИЦА пара (термин, чанк), в которой ключ термина СРАБОТАЛ в китайском оригинале чанка. Ровно
это условие включает инжекцию записи в промт, поэтому только такие пары и подсудны.
ДИСПОЗИЦИИ (`backend/internal/membank/memory.go:121,650-663` прочитано, не пересказано):
approved CONFIRMED (авторитетно) · auto/draft AMBIGUOUS (кандидат, обязательности нет).
Блок ограничений редактора CONFIRMED-only (`memory.go:745-750`).
ОСНОВНОЙ ЭНДПОЙНТ CONFIRMED. С AMBIGUOUS-записи спрашивать соблюдения нельзя: движок её
таковой не объявлял, и `mempostcheck.go:52-64` прямо зовёт наказание за неё инверсией контракта.
ПРИНЯТОЕ МНОЖЕСТВО ФОРМ = базовый `dst` `decl.forms`. Это РАТИФИЦИРОВАННОЕ D24.4, а не мой
выбор: проверка одних косвенных давала 37 ложных промахов из 55 на этих же данных (nominative_gap).
ТРИ СТРУКТУРНЫХ ИСКЛЮЧЕНИЯ каждое установлено кодом или данными, ни одно не «для красоты»:
1. ЧАНКИ БЕЗ ПЕРЕВОДА. Гейт покрытия пометил их флагом и не переводил вовсе; в параллельном
файле у них нет секции `[ПЕРЕВОД]`. Отсутствие термина там дефект ПОКРЫТИЯ, не банка.
Печатается отдельной строкой, а не заминается.
2. ОДНОСИМВОЛЬНЫЕ ХАНЬСКИЕ КЛЮЧИ без `allow_short` (, ). `memory.go:319` УДАЛЯЕТ такой ключ
из автомата записи не инжектировались НИКОГДА, спрашивать за них нельзя. (В живом коде есть
ещё и демот таких промахов, `mempostcheck.go:122`, но он от D39.39 ПОЗЖЕ этих прогонов.)
3. STICKY (scene-inertia, A5) ключа в чанке нет по построению, `mempostcheck.go:31-33`
исключает их сознательно. Тоже печатается числом.
ПРАВИЛО РЕШЕНИЯ (порог консилиума §5 п.1; двусмысленность его второй половины разрешена ЗДЕСЬ,
до чисел, и разрешение объявлено явно):
A. взвешенная доля промахов CONFIRMED > 2% «банк держит» ложна в сильной форме;
B. «любая сущность топ-50» двусмысленна записей всего 49, и буквальное чтение убивает гипотезу
одной случайной строкой. Читаю как проверку на СИСТЕМАТИЧЕСКИЙ провал ядра: термин из ТОП-10
по весу ключа с долей промахнутых чанков >= 20%.
Обе печатаются всегда и не пересматриваются после просмотра чисел.
КЛАССИФИКАЦИЯ ПРОМАХА (июльская номенклатура, чтобы числа были сравнимы):
decl_gap принятая форма есть, но её не увидели бы БЕЗ union D24.4 (контроль: должен быть 0);
inflection_gap основа dst присутствует в форме, которой в сиде НЕТ (обычно мн.ч.) читатель
видит верный термин, это дефект ЗАСЕВА, а не дрейф;
genuine_absent основы нет вовсе КАНДИДАТ дрейфа, требующий чтения глазами. НЕ дрейф сам по себе.
Граница признана честно: определитель основы здесь ЭВРИСТИКА общего префикса, а не морфология
(в движке стеммер настоящий). Поэтому `genuine_absent` объявляется КАНДИДАТОМ и подлежит чтению.
ДВА РЕЖИМА МАТЧИНГА, потому что эталоны построены по-разному:
native longest-match, как автомат движка (古月 внутри 古月漠北 не считается);
substring независимая подстрока на термин, как июльский скрипт: он этим и обошёл alias-слепое
пятно D24.2, поймав 古月Гуюэ ×2. Числа июля сравнивать нужно с ЭТИМ режимом.
ВТОРОЙ КОНТУР СВЕРКА С ДВИЖКОВЫМ ПОСТ-ЧЕКОМ (`postcheck_gate: false` флаггер пишет промахи в
`retrieval_state`). Расхождение ЛЮБОГО знака находка:
движок нашёл, я нет ложная тревога гейта (ожидаемо: базы писаны ДО фикса D24.4);
я нашёл, движок нет слепота гейта то, чего он не смотрит.
ЧЕГО ПРИБОР НЕ УМЕЕТ: он не читает смысл термин в правильной форме не на своём месте он засчитает.
"""
import argparse
import json
import re
import sqlite3
import sys
from collections import defaultdict
from pathlib import Path
RUNS = {
"acceptance": {
"db": Path.home() / "books/gu-zhenren/acceptance/guzhenren-acc-a.db",
"parallel": Path.home() / "books/gu-zhenren/acceptance/guzhenren-25ch-parallel.txt",
"book_id": "guzhenren-acc-a",
"note": "10.07, промты v0-draft, редактор glm-5 монолингв, 54/57 чанков переведены",
},
"rerun": {
"db": Path.home() / "books/gu-zhenren/rerun/guzhenren-rerun.db",
"parallel": Path.home() / "books/gu-zhenren/rerun/rerun-parallel.txt",
"book_id": None, # берётся из базы: имя книги у пере-прогона своё
"note": "11.07, промты v1-reflow, редактор glm-5 БИЛИНГВ, 57/57 чанков — ближе к боевому",
},
}
WEIGHTED_MISS_MAX = 0.02
TOP_N = 10
CORE_MISS_MAX = 0.20
# Эвристика основы для КЛАССИФИКАЦИИ промаха (на сам вердикт не влияет — метрики A и B считают
# промахи, а не классы, поэтому это число ничего подогнать не может).
# ⚠ 4 знака ЛОМАЮТСЯ НА ЧЕРЕДОВАНИИ СОГЛАСНЫХ: «очистить»[:4] = «очис», а в тексте стоит «очищает» —
# и верно переведённый термин уезжает в `genuine_absent`, то есть в кандидаты дрейфа. Поймано
# чтением текста 30.08 на 炼化 → «очистить и подчинить»: все 7 его «промахов» суть верный перевод
# при ПУСТОМ `decl.forms`. 3 знака чередование переживают; цена — более грубое сито.
STEM_MIN = 3
CHAPTER_RE = re.compile(r"^【 Глава (\d+) 】", re.M)
# ⚠ Флаг в скобках есть у `acceptance` и ОТСУТСТВУЕТ у `rerun` (`--- чанк 0 ---`). Первая редакция
# требовала скобок — и на `rerun` разобрала НОЛЬ чанков, после чего прибор напечатал «порог не
# пробит» и «гипотеза не опровергнута» НА ПУСТОМ ЗНАМЕНАТЕЛЕ. Отсюда же гейт `assert_measured`.
CHUNK_RE = re.compile(r"^--- чанк (\d+)(?: \[([^\]]*)\])? ---$", re.M)
HAN_RE = re.compile(r"[一-鿿]")
def load_glossary(con, book_id):
aliases = defaultdict(list)
for term_id, alias in con.execute(
"select term_id, alias from glossary_aliases where book_id=?", (book_id,)
):
aliases[term_id].append(alias)
rows = []
for (tid, src, dst, typ, status, gender, decl, allow_short) in con.execute(
"select id, src, dst, type, status, gender, decl, allow_short "
"from glossary where book_id=? order by id",
(book_id,),
):
forms = []
if decl:
try:
forms = [f for f in json.loads(decl).get("forms", []) if f]
except json.JSONDecodeError:
pass
keys = [src] + aliases[tid]
# Структурное исключение 2: односимвольный ханьский ключ без allow_short в автомат не попал.
eligible = [k for k in keys if allow_short or not (len(k) == 1 and HAN_RE.match(k))]
rows.append(
{
"id": tid, "src": src, "dst": dst, "type": typ, "status": status,
"gender": gender, "keys": eligible, "banned": [k for k in keys if k not in eligible],
"decl_forms": forms, "accepted": sorted({dst, *forms}, key=len, reverse=True),
"confirmed": status == "approved",
"stem": dst[:STEM_MIN] if len(dst) >= STEM_MIN else dst,
}
)
return rows
def split_chunks(text):
out = []
chapters = list(CHAPTER_RE.finditer(text))
for i, cm in enumerate(chapters):
ch = int(cm.group(1))
body = text[cm.end(): chapters[i + 1].start() if i + 1 < len(chapters) else len(text)]
marks = list(CHUNK_RE.finditer(body))
for j, km in enumerate(marks):
seg = body[km.end(): marks[j + 1].start() if j + 1 < len(marks) else len(body)]
translated = "[ПЕРЕВОД]" in seg
zh, ru = (seg.split("[ПЕРЕВОД]", 1) if translated else (seg, ""))
out.append((ch, int(km.group(1)), km.group(2) or "", zh.replace("[ОРИГ]", ""), ru,
translated))
return out
def assert_measured(what, n, ctx):
"""ГЕЙТ ПРОТИВ ЗЕЛЕНИ НА ПУСТОТЕ. Прибор, ничего не измеривший, обязан ОТКАЗАТЬ, а не напечатать
«порог не пробит»: пустой знаменатель читается как успех и ровно так и был прочитан 30.08, когда
парсер не понял формат `rerun` и вердикт «гипотеза не опровергнута» встал на нуле наблюдений."""
if n <= 0:
print(f"\n⛔⛔ ПРИБОР ОТКАЗЫВАЕТ: {what} = 0 ({ctx}). Вердикт НЕ выносится: "
"нулевой знаменатель — это не «в пределах», это отсутствие замера.")
sys.exit(2)
def count_keys(zh, terms, mode):
"""mode=native — longest-match (автомат движка); mode=substring — независимо на термин (июль)."""
hits = {}
if mode == "substring":
for t in terms:
n = sum(zh.count(k) for k in t["keys"])
if n:
hits[t["id"]] = n
return hits
occupied = bytearray(len(zh))
ordered = sorted(
(t for t in terms if t["keys"]),
key=lambda t: max(len(k) for k in t["keys"]), reverse=True,
)
for t in ordered:
n = 0
for key in sorted(t["keys"], key=len, reverse=True):
start = 0
while (p := zh.find(key, start)) >= 0:
if not any(occupied[p:p + len(key)]):
occupied[p:p + len(key)] = b"\x01" * len(key)
n += 1
start = p + 1
if n:
hits[t["id"]] = n
return hits
def engine_state(con, book_id):
st = {}
for ch, idx, n_miss, detail, sticky, spoil, evict, exact in con.execute(
"select chapter, chunk_idx, n_postcheck_miss, postcheck_detail, n_sticky, "
"n_spoiler_blocked, n_evicted, n_exact_hits from retrieval_state where book_id=?",
(book_id,),
):
items = json.loads(detail) if detail else []
st[(ch, idx)] = {
"confirmed_miss": {d["src"] for d in items if d.get("disp") == "confirmed"},
"ambiguous_miss": {d["src"] for d in items if d.get("disp") == "ambiguous"},
"n_miss": n_miss, "sticky": sticky, "spoiler": spoil,
"evicted": evict, "exact": exact,
}
return st
def classify(term, ru):
"""Промах уже установлен: ни одной принятой формы нет. Чем он является?"""
ru_low = ru.lower()
if any(f and f.lower() in ru_low for f in term["decl_forms"]):
return "decl_gap" # контроль: обязан быть 0 при union D24.4
if term["stem"] and term["stem"].lower() in ru_low:
return "inflection_gap" # основа есть, формы в сиде нет — дефект ЗАСЕВА
return "genuine_absent" # КАНДИДАТ дрейфа, читать глазами
def audit(run, mode, verbose):
cfg = RUNS[run]
con = sqlite3.connect(f"file:{cfg['db']}?mode=ro", uri=True)
book_id = cfg["book_id"] or con.execute("select book_id from glossary limit 1").fetchone()[0]
terms = load_glossary(con, book_id)
by_id = {t["id"]: t for t in terms}
chunks = split_chunks(cfg["parallel"].read_text(encoding="utf-8"))
engine = engine_state(con, book_id)
print("=" * 98)
print(f"АУДИТ УДЕРЖАНИЯ БАНКА — прогон `{run}` ({book_id}), режим матчинга: {mode}")
print(f" {cfg['note']}")
print(" правило решения — в шапке файла, записано ДО чисел")
print("=" * 98)
banned = sorted({k for t in terms for k in t["banned"]})
print(f"записей {len(terms)}: confirmed {sum(t['confirmed'] for t in terms)}, "
f"ambiguous {sum(not t['confirmed'] for t in terms)} | "
f"ключей вне автомата (single-Han ban): {', '.join(banned) or ''}")
print(f"чанков {len(chunks)}, из них переведено {sum(c[5] for c in chunks)}; "
f"строк retrieval_state {len(engine)}")
assert_measured("разобрано чанков параллельного файла", len(chunks),
f"формат {cfg['parallel'].name} не распознан")
assert_measured("переведённых чанков", sum(c[5] for c in chunks), "нечего проверять")
fired, fired_w, miss_ch, miss_w = (defaultdict(int) for _ in range(4))
klass = defaultdict(int)
kandidaty, rows = [], []
agree = only_engine = only_mine = 0
for ch, idx, flag, zh, ru, translated in chunks:
if not translated:
continue # структурное исключение 1
hits = count_keys(zh, terms, mode)
ru_low = ru.lower()
eng = engine.get((ch, idx), {})
eng_miss_all = eng.get("confirmed_miss", set()) | eng.get("ambiguous_miss", set())
for tid, n in hits.items():
t = by_id[tid]
fired[tid] += 1
fired_w[tid] += n
# ⚠ РЕГИСТР: сравнение регистро-НЕчувствительное. Прямое `in` дало ложный промах на
# 开窍 → «открытие апертуры» (гл.5/ч1): в тексте «Открытие апертуры удалось!» — термин
# верен, заглавная лишь начинает предложение. Поймано чтением 30.08.
held = any(f and f.lower() in ru_low for f in t["accepted"])
if not held:
miss_ch[tid] += 1
miss_w[tid] += n
k = classify(t, ru)
klass[(k, t["confirmed"])] += 1
if k == "genuine_absent" and t["confirmed"]:
kandidaty.append((ch, idx, t["src"], t["dst"], t["type"], n))
eng_miss = t["src"] in eng_miss_all
if not held and eng_miss:
agree += 1
elif eng_miss and held:
only_engine += 1
rows.append(("ЛОЖНАЯ ТРЕВОГА ГЕЙТА", ch, idx, t["src"], t["dst"], t["status"], n))
elif not held and not eng_miss:
only_mine += 1
rows.append(("СЛЕПОТА ГЕЙТА", ch, idx, t["src"], t["dst"], t["status"], n))
def block(conf, label):
ids = [i for i in fired if by_id[i]["confirmed"] == conf]
c_all, c_miss = sum(fired[i] for i in ids), sum(miss_ch[i] for i in ids)
w_all, w_miss = sum(fired_w[i] for i in ids), sum(miss_w[i] for i in ids)
print(f"\n--- {label} ---")
print(f" пар (термин, чанк) со сработавшим ключом : {c_all}")
print(f" из них промах (presence-провал) : {c_miss}"
+ (f" ⇒ presence {1 - c_miss / c_all:.1%}" if c_all else ""))
print(f" вхождений ключа всего / промахнуто : {w_all} / {w_miss}"
+ (f" ⇒ occurrence {1 - w_miss / w_all:.1%}" if w_all else ""))
return (w_miss / w_all) if w_all else 0.0, c_all, c_miss
weighted, conf_all, conf_miss = block(True, "CONFIRMED — ОСНОВНОЙ ЭНДПОЙНТ")
assert_measured("пар CONFIRMED со сработавшим ключом", conf_all,
"ни один подписанный ключ не сработал — матчер или данные не те")
block(False, "AMBIGUOUS — справка: обязательности движок не объявлял")
print("\n--- классификация промахов (июльская номенклатура, для сверки) ---")
for k in ("decl_gap", "inflection_gap", "genuine_absent"):
c, a = klass[(k, True)], klass[(k, False)]
note = {"decl_gap": "контроль union D24.4 — обязан быть 0",
"inflection_gap": "дефект ЗАСЕВА (мн.ч.), не дрейф",
"genuine_absent": "КАНДИДАТ дрейфа — читать глазами"}[k]
print(f" {k:<16} confirmed {c:>4} ambiguous {a:>4}{note}")
eff_num = conf_all - klass[("genuine_absent", True)] - klass[("decl_gap", True)]
if conf_all:
print(f" эффективная консистентность CONFIRMED = {eff_num}/{conf_all} = {eff_num / conf_all:.1%}"
" (читатель видит верный термин: инфлексия вне сида ему не мешает)")
print(f"\n--- метрика B: ядро (топ-{TOP_N} по весу ключа среди CONFIRMED) ---")
core = sorted((i for i in fired if by_id[i]["confirmed"]),
key=lambda i: fired_w[i], reverse=True)[:TOP_N]
core_fail = []
print(f" {'термин':<34}{'вес':>6}{'чанков':>8}{'промах':>8}{'доля':>7}")
for i in core:
share = miss_ch[i] / fired[i]
if share >= CORE_MISS_MAX:
core_fail.append(by_id[i]["src"])
print(f" {by_id[i]['src'] + '' + by_id[i]['dst']:<34}{fired_w[i]:>6}{fired[i]:>8}"
f"{miss_ch[i]:>8}{share:>6.0%}{'' if share >= CORE_MISS_MAX else ''}")
print("\n--- ВТОРОЙ КОНТУР: сверка с движковым пост-чеком ---")
print(f" согласие : {agree}")
print(f" ⚠ ложная тревога гейта (форма В ТЕКСТЕ есть): {only_engine}"
" ← ожидаемо: базы писаны ДО фикса D24.4")
print(f" ⚠ слепота гейта (промах, гейт молчит) : {only_mine}")
if verbose and rows:
print(f" {'класс':<24}{'гл':>4}{'чанк':>6} {'src':<12}{'dst':<30}{'статус':<10}{'вес':>5}")
for kind, ch, idx, src, dst, status, n in rows:
print(f" {kind:<24}{ch:>4}{idx:>6} {src:<12}{dst:<30}{status:<10}{n:>5}")
if kandidaty:
print(f"\n--- КАНДИДАТЫ ДРЕЙФА (genuine_absent, confirmed): {len(kandidaty)} — ЧИТАТЬ ГЛАЗАМИ ---")
print(f" {'гл':>4}{'чанк':>6} {'src':<12}{'dst':<30}{'тип':<10}{'вес':>5}")
for ch, idx, src, dst, typ, n in kandidaty:
print(f" {ch:>4}{idx:>6} {src:<12}{dst:<30}{typ:<10}{n:>5}")
print("\n--- зоны, которые прибор НЕ судит (печатаются, а не заминаются) ---")
print(f" чанков без перевода (гейт покрытия) : {sum(1 for c in chunks if not c[5])}")
print(f" sticky-переносов (инерция сцены, A5) : {sum(e['sticky'] for e in engine.values())}")
print(f" вытеснено бюджетом токенов : {sum(e['evicted'] for e in engine.values())}")
print(f" спойлер-блокировок : {sum(e['spoiler'] for e in engine.values())}")
print("\n" + "=" * 98)
a_fail = weighted > WEIGHTED_MISS_MAX
print(f"МЕТРИКА A: взвешенная доля промахов CONFIRMED = {weighted:.2%} при пороге "
f"{WEIGHTED_MISS_MAX:.0%}{'⛔ ПОРОГ ПРОБИТ' if a_fail else 'в пределах'}")
print(f"МЕТРИКА B: терминов ядра с долей промахов >= {CORE_MISS_MAX:.0%}: {len(core_fail)}"
f"{'' + ', '.join(core_fail) if core_fail else ''}"
f"{'⛔ ПОРОГ ПРОБИТ' if core_fail else 'в пределах'}")
print("ВЫВОД (строгий presence): гипотеза «банк держит» в СИЛЬНОЙ форме "
+ ("⛔ ЛОЖНА" if (a_fail or core_fail) else "не опровергнута"))
print("⚠ Границы: терминная половина Вопроса 0 · июльский профиль · редактор glm-5, не боевой "
"deepseek-v4-pro.")
print("=" * 98)
def main():
ap = argparse.ArgumentParser(description="аудит удержания банка, $0")
ap.add_argument("--run", choices=[*RUNS, "both"], default="both")
ap.add_argument("--match", choices=["native", "substring"], default="native",
help="native — автомат движка; substring — как независимый июльский скрипт")
ap.add_argument("--verbose", action="store_true", help="печатать построчную сверку с гейтом")
a = ap.parse_args()
for run in ([*RUNS] if a.run == "both" else [a.run]):
audit(run, a.match, a.verbose)
print()
return 0
if __name__ == "__main__":
sys.exit(main())