# `eval/pkg7/` — терминологическая ресёрч-программа (полигон, пакет-7) Инструменты фазы A. Отчёт с цифрами и вердиктами — `docs/archive/reports/POLYGON_TERM_RESEARCH_A_2026-07-26.md`. Всё здесь **$0**: ни одного вызова LLM-провайдера, ни одного чтения ключей. | Скрипт | Что делает | Заметки | |---|---|---| | `split_book.py` | книга → JSONL-субстрат `{chapter, chunk_idx, source}` | правила глав — ДАННЫЕ (`RULES`), добавить книгу = добавить строку; epub парсится stdlib (bs4/ebooklib в venv нет) и чистится от колонтитулов | | `minerharness/` | вызов БОЕВОГО `internal/miner` из модуля ВНЕ репо | `build.sh [commit]`; `replace` смотрит в `git archive`-копию коммита, а НЕ в рабочее дерево | | `mine_nonhan.py` | кандидаты-термы для en/ja тремя раздельными каналами | ORTHO · DISP · CONTRAST; кандзи сознательно без контраста (см. шапку) | | `kwic.py` | KWIC-контексты вхождения | разметка по тексту, а не по памяти; `--stats` даёт объём контекстов для сметы | | `seed_recall.py` | recall WHICH-канала против подписанного сида | майнер обязан гоняться с ПУСТЫМ сидом, иначе метрика вырождается | | `rubric_probe.py` | измеримость критериев рубрики на подписанном сиде | M1 морфология (pymorphy3) · M2 транскрипция · M3 коллизии банка | ## Правила, выученные на своих ошибках в этом пакете 1. **Пин обязателен.** Харнесс `replace`-ит копию коммита, не рабочее дерево: параллельная бэкенд-сессия правит `backend/` под руками, и прогон стал бы невоспроизводимым. Все цифры отчёта — на `996bade`. 2. **Ссылки `файл:строка` — по коммиту пина.** В рабочем дереве те же якоря уже уехали; первая редакция отчёта содержала номера строк рабочего дерева. 3. **Порог частоты и алфавит кандидатов — свойство ЯЗЫКА, не движка.** Японские названия техник живут на частотах 2–10 и в катакане/руби; ханьский n-граммный канал их не видит вовсе. 4. **Правило вершины словосочетания зависит от типа терма** (у имени вершина последняя, у нарицательного — первая). Морфо-чекер без этого правила меряет не то: первая версия `rubric_probe.py` склоняла «Вина» в «Монах Цветочного Вина». 5. **Контраст без опоры вырождается в частоту.** Ранжирование кандзи против словаря другого языка даёт log частоты, а не над-представленность; канал понижен до инвентаря. 6. **Отклонение методики — замерять, а не декларировать.** Нарезка здесь не боевая; свип целевого размера чанка (800/2000/6000) дал Жаккар 1.000 — только после этого цифры можно было предъявлять. ## Фаза B (добавлено 26.07) | Скрипт | Что делает | |---|---| | `bkrs_lookup.py` | словарные статьи БКРС списком; воспроизводит куки-челлендж `ca=`, из-за которого страница считалась JS-рендеримой | | `build_termset.py` | набор термов со входом терминолога (KWIC + пары «исходник↔черновик») из реального выровненного корпуса шести прогонов | | `terminologist_arms.py` | шесть армов промпта по пре-регистрации §A5; батчи, леджер денег из фактического `usage`, сырьё сохраняется целиком | | `blind_judge.py` | слепая Best-Worst оценка; соль перемешивания по терму, подпись владельца участвует как отдельный кандидат `GOLD` | | `repair_judge.py` | терпимый пере-разбор судейских ответов ИЗ СЫРЬЯ (модель отдаёт JSON с недостающей скобкой) — $0, без повторных вызовов | | `score_bws.py` | BWS-счета и вердикты F1–F4 по порогам, названным до трат | | `audit_phaseB.py` | **адверсариальная ревизия фазы B**: пере-счёт объективного слоя, состав промптов по сырью, вырожденные айтемы, пере-скоринг BWS без них, деньги из сырья | | `blind_judge2.py` | слепая оценка на ПОЧИНЕННОМ наборе (фаза B′): дедуп кандидатов · только расходящиеся термы · **ДЕКОЙ** — заведомо неверный вариант, без которого контроль «отличает ли судья годное от случайного» неизмерим | | `score_b2.py` | вердикты фазы B′ по порогам §0: T (контроль правила транскрипции) · C1–C3 (годность судьи) · R (разброс прогона) | **Урок фазы B — в редакции ПОСЛЕ ревизии (первая редакция была неверной).** Сперва я записал сюда «слепые LLM-судьи не имеют разрешающей способности». Проверка исполнением (`audit_phaseB.py`) это опровергла: у 35 термов из 80 все семь кандидатов оказались ОДНОЙ И ТОЙ ЖЕ строкой, а на 45 невырожденных судьи ставят арм без контекстов последним — то есть разрыв они видят. Настоящий урок другой и жёстче: 7. **Айтем без вариативности — не измерение, а шум с ценником.** Сравнительный протокол обязан собираться так, чтобы кандидаты РАСХОДИЛИСЬ: дедуплицируй варианты, требуй уникальности эталона (иначе контроль «отличает ли судья подпись» неизмерим по построению), выкидывай термы, где все ответы совпали. 8. **Фактор, который есть во всех армах, не измеряется ни одним из них.** Правило транскрипции лежало в общей части промпта — значит контроля не было, и любые «армы с правилом против армов без» были иллюзией чтения собственного кода. 9. **Считай улики, а не абзацы.** «Три независимых наблюдения» под рекомендацией свелись к одному терму, посчитанному дважды, плюс непроверенная поисковая сводка. Перед словом «сходится» — проверь, не один ли это сигнал в трёх обёртках. **Чем кончилась ревизия (фаза B′, замер за $0.14).** Правы оказались обе стороны спора, но каждая наполовину, и без замера этого было не разделить: 10. **Слепой судья — катастроф-экран, а не ранжировщик.** Подложенный декой ловится 43–45 раз из 45 (счёт −0.93/−0.98), подпись владельца не помечена катастрофой ни разу, но ранговое согласие с объективным слоем ничтожно (ρ = +0.43 и −0.11). Грубое видит уверенно, тонкое не разрешает — и роль ему надо давать по этой способности, а не по желаемой. 11. **Одна строка промпта может стоить канона — и одновременно ничего не менять в среднем.** Снятие правила транскрипции вернуло `春秋蝉` в точный канон владельца, но агрегатный сдвиг оказался +0.013 при пороге 0.05: правило помогает на антропонимах ровно столько же, сколько вредит на названиях предметов. Единичный яркий пример и агрегат — разные улики, и путать их нельзя даже когда пример красив. 12. **Повторы дёшевы и меняют статус вывода.** Три прогона на 19 термах стоили $0.024 и превратили «P0b хуже P1» из одной точки в измерение: разрыв 6 термов против размаха 1. Заодно объяснили аномалию «случайный глоссарий дал 19/19» — 19/19 лежит внутри шума прогона. **Итог пакета (26.07): жанровый словарь ОТМЕНЁН владельцем как класс** — решение о переводе принадлежит подписи на банк памяти, а пар-словарь навязывал бы одно видение всем книгам пары. Замеры силы не теряют и перепрогонов не требуют (главный результат `P0b`↔`P1` идёт по армам без словаря; `P5`↔`P2` несут одинаковые инертные строки), беспредметен только арм `P0`. Отсюда последний и самый дорогой урок: 13. **Замер может подорвать ПОСЫЛКУ задачи, а не только её исполнение — и тогда деливерабл надо оспорить, а не исполнить.** Мы намерили ровно основание для отмены (V0 на центральном терме, ложная сходимость русских глоссариев, покрытие 0/80, четыре строки из шести отсутствуют в книге) — и всё равно принесли «таблицу на подпись», потому что она стояла в промте. Право сессии сказать «этого делать не надо» существует, канал вопросов для этого и заведён; не воспользоваться им дороже, чем не измерить вовсе, потому что так наш вкус едва не стал контрактом пары. ## Пакет-8 (пре-замеры перед расширением эмиссии, 26.07) Отчёт — `docs/archive/reports/POLYGON_PREMEASURE_2026-07-26.md`. Инструменты живут здесь же по указанию промта пакета-8 (зона записи `eval/pkg7/`), хотя относятся к следующему пакету. | Скрипт | Что делает | |---|---| | `termharness/` | собирает запрос БОЕВОЙ роли терминолога вне репо (`Merge`→`AttachKWIC`→`ScoreVariants`→`Batch`→`RenderCanonAnchor`→`MessagesWithInjection`); режимы `build` · `norm` (боевой `NormalizeSourceKey`) · `parse` (боевой `ParseReply`) | | `joint_recall.py` | recall майнера · банкноты · объединения против подписанного сида; парсер ⟦TM-BANK-v1⟧ — реплика `parseBanknote`, сверенная с боевой телеметрией | | `bank_profile.py` | цензура инъекцией по парам (терм × глава) + объективный профиль эмиссии банкноты боевыми порогами майнера | | `role_probe.py` | драйвер платных вызовов: смета до трат, $0-резюм по уже оплаченным батчам, сырьё с `usage`/`finish_reason`, деньги из `models.yaml` | | `score_probe2.py` | доля выдуманных dst по классам разметки, разброс по повторам | | `score_grid.py` | сетка kwic × подача: точность против подписи, внутриточечный размах, расходящиеся позиции ПОКАЖДОМУ повтору | Уроки, купленные пакетом-8: 14. **«Данные лежат там-то» — проверяй, прежде чем строить на этом замер.** Промт сказал, что блоки банкноты лежат в `corpus.jsonl` пакета-6. Их там 2 из 91; сырьё жило в трёх базах прогонов, и нашлось только сканированием всех 21 базы стенда. Полминуты проверки против замера, построенного не на том корпусе. 15. **Реплика формата запроса — третья копия и первый кандидат на расхождение.** Всё, что можно собрать боевым кодом, надо собирать боевым кодом: `termharness` вызывает те же функции, что рантайм, и вопрос «а точно ли модель видела то же самое» просто не возникает. 16. **Свою же рекомендацию проверяй замером ДО того, как подать её в дизайн.** Я предложил дисциплинировать банкноту частотным порогом («$0, снимает 82% лишнего») — и, посчитав частоты по всей книге, обнаружил, что самое частое слово выборки не термин (`少年`, 1149), а центральный концепт `光阴之河` — 15. Порога не существует; рекомендация снята собственной проверкой, а не ревью. 17. **Считай язык ответа, а не только его правильность.** Арм без якоря ⟦TM-CANON⟧ в одном прогоне из трёх ответил по-английски (22 строки из 40), и ни парсер, ни эхо-гейт этого не ловят. Находка появилась только потому, что я посчитал скрипт письма по всем 126 вызовам — метрика точности её не показывала.