textmachine/docs/experiments/21-role-topology.md

32 KiB
Raw Blame History

21. Топология ролей перевода: чем платим за второй проход и есть ли ему замена

Полигон-сессия 06.08.2026. Исполнение docs/POLYGON_ROLE_TOPOLOGY_SESSION_PROMPT.md (оркестратор №15, санкция владельца 06.08, D39.108). Зона: eval/role_topology/ + этот файл + пинг в PROGRESS «Полигон». Потолки предварительные: Ф0 ≤$0.70 · Ф1 ≤$1.00 · Ф2а ≤$2.50 · Ф2б ≤$3.00.

СТАТУС: Ф0 ИСПОЛНЕНА ЧАСТИЧНО, ПЛАТНЫЕ ФАЗЫ НЕ ЗАПУСКАЛИСЬ. Потрачено $0. Разделы §0§2 — пре-регистрация и результаты бесплатной метрологии. Блокер запуска назван в §0.2.


ИТОГ (для оркестратора)

Заполняется после Ф2. Сейчас закрыты только вопросы метрологии — см. §2.

Что уже решено данными и меняет план пака:

  1. Детектор «выдуманное слово» построен и работает (§2.1): precision 0.78 / recall 0.70 на 2177 размеченных словотипах против 1.00 / 0.10 у боевых чекеров, вне выборки 6/6. Это не починка бага, а закрытие дыры, которую движок объявил сам: sanitizer.go:429 дословно — «detection needs a morphology pass (Ф2), so it stays silent here».
  2. Детектор «смысловая инверсия» через QE-ранкер — ОТРИЦАТЕЛЬНЫЙ РЕЗУЛЬТАТ (§2.2), проверенный контрольным декоем. Следствие для плана: арм C фазы 2б в режиме «реальные детекторы» по классу инверсий питать нечем; развилка вынесена владельцу в §0.3.
  3. Три метрики из шести, которые я построил по букве промта, мерили шум и были сняты замером (§2.3). Это цена, которую платит любая батарея без прогонки на чистом тексте, и она названа числами, а не общими словами.

§0. ПРЕ-РЕГИСТРАЦИЯ

Фриз коммитом ДО первого платного вызова. Изменения после фриза = новый experiment-ID; девиации — явным списком в §5. Amend фриза запрещён.

0.1. Вопрос и что считается ответом

Какая топология целевая — «черновик + переписыватель», «черновик + гейты + точечный ремонт», «однопроходка сильной моделью», гибрид с маршрутизацией — и кто жильцы ролей.

Ответом считается таблица «арм → детерминированная батарея · MQM-lite двух судей · цена за принятую 1000 слов (p50/p95) · дрейф на связном отрезке», где различия названы ОТНОСИТЕЛЬНО порога различимости Ф0.6, а не абсолютно. Ответом НЕ считается ранжирование армов по среднему баллу судьи: эксп-20 §5.4 намерил, что судья согласен сам с собой на идентичном входе в 56% клеток и разниц меньше ~2:1 не разрешает.

Армы равноправны. «Правильного ответа», которого ждут, не существует; при неразличимости дефолт — самый дешёвый и детерминированный арм.

0.2. Материал — ОТКРЫТЫЙ БЛОКЕР

Промт заказывает невиданный срез: свежая zh-вебновелла без опубликованного ru-перевода, рассеянные окна для дисперсии + связный отрезок 810 глав для дрейф-метрик.

Такого текста в дереве нет. Инвентарь ~/books с провенансом по докам: gu-zhenren (蛊真人) — претрейн-канон, ровно тот случай, который строка 55 бэклога объявляет негодным дословно; jinpingmei — минский байхуа, PD; isekai_majutsushi_jp — ja, не zh (exp11 §41); fifty_shades_1_en, Empire_of_the_Dawn — en. Каталог eval/data/samples/webnovel/zh/, куда харнесс webnovel_slice.py:14 ждёт корпус, никогда не создавался — строка 55 висит именно поэтому. То, что exp07 называл «невиданным вебновелл-текстом», — это само 蛊真人 (07-coverage-precision.md:85), и его же хвост предписывает «перепроверить на корпусе владельца».

Норма при этом однозначна: корпус кладёт владелец (webnovel_slice.py:1-3 — «запускается по появлению корпуса владельца»). Полигон текст не добывает.

Развилка передана владельцу 06.08 (варианты: владелец кладёт файлы · санкционирует добычу полигоном · Ф2 идёт на 蛊真人 с объявленной границей). До ответа платные фазы не стартуют. Оговорка о свежести, которую важно не потерять: дата публикации контаминацию не снимает — катофы моделей разные. Чище всего берутся последние главы ИДУЩЕГО сериала (июнь–август 2026), и контаминация всё равно меряется (continuation/cloze + поиск fan-переводов), а не предполагается.

0.3. Армы

Ф2а — «переписывание как класс». Черновики фризятся и подаются идентичными всем армам-редакторам (парный дизайн, McNemar).

Арм Что
F do-nothing пол: черновик как есть через гейты. Все редакторские армы мерятся против него
A байт-боевой бейзлайн flash→dspro (без банкноты — объявленная девиация бейзлайна, не чинить)
B связка flash→glm-5-OFF full-regen
D однопроходка сильной модели С мандатом перевёрстки
D она же БЕЗ мандата — деконфаунд «модель против мандата»

Ф2б — ремонт и маршрутизация. Гейт входа: детекторы Ф0.4 валидированы.

Арм Что
C черновик (с мандатом перевёрстки) → детекторы+гейты → точечный ремонт дешёвым фиксером; два режима: oracle-флаги (потолок) и реальные детекторы (пол); после каждого фикса полный ре-гейт, кап 2 итерации
E обратная связка: сильный черновик → дешёвый фиксер по флагу
G guarded+routed: QE/гейты скорят все окна → стиль-пасс ТОЛЬКО флагнутым, правка принимается только если внешний гейт видит улучшение (max(draft, edited))

⚠ ПОПРАВКА К АРМАМ C и G ПО РЕЗУЛЬТАТУ Ф0.4 (§2.2). QE-ранкер локальную смысловую инверсию не детектирует. Следствия, объявленные ДО запуска:

  • арм C гонится в oracle-режиме и в режиме «реальные детекторы», но реальный режим питается ТОЛЬКО детектором выдуманных слов + существующими $0-гейтами; класс «инверсия» в реальном режиме остаётся ненайденным, и разрыв oracle↔реальность печатается как главный результат арма;
  • арм G маршрутизирует не по QE-баллу инверсии, а по батарее Ф0.5 + QE как ГРУБОМУ фильтру (декой доказал: рассогласование сегмента ранкер видит, Δ +7.1) — то есть G ловит «сегмент поехал целиком», а не «сегмент поехал в одном слове». Это сужение арма, и оно объявлено здесь.

Reflow-план кодом (Q4c, research/19 §C1.3) — ДИСПОЗИЦИЯ ОТКАЗА. Не гоню. Основание: эксп-20 §5.2 уже проверил гипотезу «мандат в промпте заменяет второй проход» слепым судом и ОПРОВЕРГ её (русский 3:12, вёрстка 5:10 в пользу связки), а деконфаунд мандата в этом паке несёт арм D — отдельный кодовый reflow-план добавил бы третий способ спросить то же самое при бюджете, которого нет.

0.4. Метрики и пороги

Порядок первичности задан не вкусом, а измеренным шумом инструментов.

  1. Детерминированная батарея Ф0.5 — первична. Шума не имеет: повтор даёт то же число. Состав и границы каждой проверки — §2.3.
  2. MQM-lite двух судей — счёт типизированных ошибок (спан+тип+severity на 1000 слов). Pairwise-преференс — только для стиль-осей. Судья не судит армы своего семейства-жильца; вердикты раскладываются по семействам судей. Агрегация BradleyTerry с bootstrap-CI.
  3. Цена за принятую 1000 слов — p50/p95, с кэшем/батчем/ретраями; перезаписанные вызовы тоже деньги (леджер = нижняя граница).
  4. Дрейф на связном отрезке — швы, ты/вы-стабильность, рост банка.

Порог различимости берётся из Ф0.6 и записывается ДО Ф2. Правило вердикта: «арм X бьёт Y, если перевес по оси больше порога Ф0.6»; средние исходы = «неопределённо»; при неразличимости побеждает самый дешёвый и детерминированный арм.

0.5. Прогнозы (сверка с фактом идёт в отчёт)

Записаны до запуска, чтобы отчёт мог показать, где я ошибся.

# Прогноз На чём основан
П1 F (do-nothing) НЕ будет последним; по общему вердикту он в пределах порога от A эксп-20: на dspro второй проход отрицателен (общий 5:1 в пользу одного прохода)
П2 B (glm-5) выиграет русскую прозу и проиграет верности/термам эксп-20 §5: у glm свип вёрстки 6:0, у dspro верность 5:1
П3 D (однопроходка сильной) выиграет качеством и проиграет ценой ≥2× цена сильного тира против flash
П4 G даст лучшее отношение цена/качество среди всех армов он единственный не платит за неизменённое
П5 Разрыв oracle↔реальные детекторы в арме C будет БОЛЬШЕ, чем разрыв между армами Ф2а recall детектора слов 0.70, детектора инверсий нет вовсе
П6 Батарея разведёт армы там, где судья скажет «неразличимо» у батареи шум ноль, у судьи 56% самосогласия

0.6. Смета

Ф0 ≤$0.70 (предзамер судей ≤$0.60) · вахта эффорта ≤$0.05 · Ф1 ≤$1.00 · Ф2а ≤$2.50 · Ф2б ≤$3.00. Живые стопы механизмом + проекционный гард. Подъём или пере-нарезка — только словом владельца ДО запуска фазы. Не влезает — стоп и пинг, не резать молча.

0.7. Что считается провалом фазы

Ф0.6 не даёт порога различимости в потолке → СТОП и пинг с расчётом мощности, а не молчаливое уменьшение числа окон. Детекторы Ф0.4 не валидируются → арм C идёт только oracle-режимом, и это записывается как граница, а не как результат.


§1. Что уже стоит на дереве ($0)

Файл Что делает Проверен
eval/role_topology/list_models.py живой листинг моделей по 7 ключам; слаги для пре-рега снимаются в день запуска исполнением, §2.4
eval/role_topology/detect_word.py детектор «выдуманное слово», 4 накопительных слоя selfcheck.py, 12 пинов
eval/role_topology/align.py монотонное выравнивание zh↔ru (Гейл–Чёрч), $0, без моделей selfcheck.py, 5 пинов
eval/role_topology/qe_bench.py обёртка MetricX-24 в reference-free режиме + оконный мини-бенч selfcheck.py --qe, 3 пина
eval/role_topology/qe_segment.py посегментный QE + контрольный декой исполнением, §2.2
eval/role_topology/battery.py детерминированная батарея, 9 проверок 16 пинов правил + прогон по 91 единице
eval/role_topology/selfcheck.py ревью исполнением всего харнесса; ненулевой код = харнесс не годен сам

§2. Ф0 — метрология (исполнено, $0)

2.1. Детектор «выдуманное слово»: построен, базлайн бит

Земля. ~/books/gu-zhenren/labels/labels/k3.jsonl — 2177 размеченных словотипов из шести реальных прогонов, 10 помечены defect. Разметка сделана ЧУЖОЙ сессией (пакет-6, 26.07) до и вне этого экспа ⇒ требование «разметка вторым контуром» выполнено построением, а не моей рукой.

Базлайн взят из кода, а не из заголовка. metrics.json того же набора даёт боевым чекерам на k3 precision 1.0 / recall 0.1. Чтение checks/sanitizer.go:408-436 объясняет почему: боевой detectBrokenWords ловит ровно два узких класса — невозможные кириллические биграммы с ь/ъ и кириллично-латинские гомоглифы, — а морфологический проход в нём отложен ЯВНО (:429 «needs a morphology pass (Ф2), so it stays silent here»). ⇒ формулировка «мой детектор бьёт баг» неверна; верная — он закрывает объявленную дыру.

Слой tp fp fn precision recall
боевые чекеры (их metrics.json) 1 0 9 1.000 0.100
С0 нет в словаре pymorphy3 9 34 1 0.209 0.900
С1 + вайтлист банка/канона книги 9 28 1 0.243 0.900
С2 + палладиевская форма (со склонением) 8 11 2 0.421 0.800
С3 + разложимость на известные части 7 2 3 0.778 0.700

Слои накопительные и печатаются по одному — норма D39.46(а) («у каждого фактора обязан быть арм без него») применена к слоям детектора.

Выделенная валидация: 6/6 на посадках k1 пробы 18 — независимый набор чужой сессии, в настройке не участвовал. ⚠ Набор ЛЕГЧЕ живого: посадки сконструированы заведомо не-словами. Читать как верхнюю границу.

Проверка на подгонку. Свободный параметр слоя С2 (глубина усечения окончания) прогнан по диапазону: k∈{(1,), (1,2), (1,2,3)} при пороге 2 слогов дают ОДНИ И ТЕ ЖЕ 0.778/0.700. Вердикт по параметру не двигается ⇒ подгонки по нему нет.

Ущерб от ложного флага — отдельным числом, как требует промт: из 9 флагов ложных 2 (22%); столько починок арма C правили бы здоровое слово.

Дефекты САМОЙ ЗЕМЛИ, найденные проверкой посылки (пинятся selfcheck.py, чтобы отчёт не считал recall по номинальным 10 позитивам):

  • вперди и силённый в corpus.jsonl ОТСУТСТВУЮТ вовсе, даже подстрокой, хотя README набора объявляет этот файл источником ID («смещения считаны по строкам corpus.jsonl»);
  • ной — валидное русское слово (императив «ныть»); в класс попало из-за разреза токена на границе с иероглифом (伤ной), то есть это дефект утечки, а не выдуманное слово. Один из трёх моих «пропусков» — ошибка класса в метке, а не промах детектора. В классе остаётся 9 позитивов;
  • два выживших ложных срабатывания (льшим, льшую) — фантомы сборщика пула: комбинирующий знак ударения U+0301 в «бо́льшим» разрезал токен (51 из 53 вхождений идут после «о», 2 — после ударения).

⚠ Сужение собственного вывода по итогам Go-оверлея. Первая формулировка была «диакритика ломает любой словный чекер». Проверка исполнением (оверлей вне репозитория, движок read-only) её сузила: ExportNormalize знак снимает корректно, а SanitizeOutput на тексте с ударением и без даёт ОДИНАКОВЫЙ вердикт (total=0 в обоих). ⇒ боевой путь не страдает; страдает наивная токенизация в полигонном коде. Моя батарея нормализует до токенизации (§2.3).

Оставшаяся слепая зона детектора, названная явно: привлеклось = «при» + «влеклось», обе части настоящие. Класс «валидная приставка + валидное слово, не образующие реального слова» морфологией не берётся — нужен корпусный или языко-модельный сигнал.

2.2. Детектор «смысловая инверсия» через QE: ОТРИЦАТЕЛЬНЫЙ РЕЗУЛЬТАТ

Инфраструктура поднята с нуля (на машине её не было): CometKiwi / xCOMET / wmt23-cometkiwi-xl — все gated: auto на HuggingFace, токена у сессии нет; unbabel-comet 2.2.7 не импортируется на Python 3.14 (functools._HashedSeq удалён). Взята незагейченная google/metricx-24-hybrid-large-v2p6 (mT5-large 1.23B, штатный reference-free режим, CPU-загрузка 7 с). Две несовместимости пойманы исполнением: protobuf 4.25 против py3.14 и обязательный use_cache=False против transformers 4.57 (с кэшем маска кросс-внимания на токен короче выхода энкодера — падает на любой длине). Отклонение от буквы промта («CometKiwi/xCOMET») объявлено здесь.

⚠ Шкала MetricX перевёрнута: больше = хуже. Ориентация запинена в selfcheck.py — спутать её значит получить детектор, флагающий ровно здоровые сегменты.

Замерено на ДВУХ размерах модели — заявлять отрицательный результат по младшей, когда доступна старшая, преждевременно.

Замер large (1.23B) XL (3.7B) Чтение
уровень окна, 6 окон × 2 класса Δ +0.035 (4/6) порчу не видит
сегмент, k1 выдуманное слово Δ +0.073 (4/6) Δ +0.981 (4/6) чувствительность растёт с моделью
сегмент, k2 смысловая инверсия Δ 0.001 (3/6) Δ +1.147 (5/6) то же, но n=6 ⇒ p≈0.11, НЕ установлено
абсолютный порог, 81 здоровый сегмент лучшая precision 0.17 лучшая precision 0.16 детектором быть не может НИ НА ОДНОЙ
ДЕКОЙ (D39.46б), n=69 Δ +7.107 (65/69) Δ +7.000 (59/69) ранкер на этом материале ЧУВСТВИТЕЛЕН

Декой — несущая часть замера: без него «не увидел посадку» и «слеп на этом материале» неразличимы, а это разные вердикты. Он показывает, что модель работает и формат входа верен (сверен с авторским metricx24/predict.py:_make_input).

Главный вывод — не «ранкер плох», а «два режима расходятся». Абсолютный порог и парное сравнение отвечают на разные вопросы, и это ровно граница между двумя армами фазы 2б:

  • арм C ищет дефект в ОДНОМ тексте, образца нет ⇒ ему нужен абсолютный порог. Порога нет: precision ≤0.17 на обеих моделях, баллы здоровых сегментов (медиана 6.1, p90 11.4, макс 1618) полностью перекрывают баллы испорченных. Арм C по классу «инверсия» не питаем.
  • арм G сравнивает ДВЕ версии одного сегмента ⇒ ему нужна парная дельта. Она состоятельна: на механической инверсии полярности (72 пары, построены снятием/вставкой отрицания при личном глаголе) медиана Δ +1.490, направление 66/72 = 92%, знаковый тест p < 1e-5. Арм G питаем.

⚠ Граница парного замера объявлена заранее и остаётся в силе: механическая инверсия ЛЕГЧЕ ручной посадки (голое «не» — сильный поверхностный сигнал, а посадки пробы 18 меняли смысл переписыванием оборота). Поэтому 92% читается как ВЕРХНЯЯ граница парного режима; на реальном классе оценка — те самые 5/6 при p≈0.11, то есть не установлено.

Следствия для пака (внесены в §0.3 ДО запуска): арм C в реальном режиме не покрывает класс инверсий и печатает разрыв oracle↔реальность как главный результат; арм G сохраняет свой QE-гейт, но его пропускная способность обязана быть откалибрована на РЕАЛЬНЫХ правках — §2.5.

2.3. Детерминированная батарея: построена, и три её метрики сняты собственным замером

Девять проверок по спецификациям docs/research/12-failure-modes-ru-target.md (у каждого из 12 режимов отказа русской цели там есть раздел «Детекция» — правила взяты оттуда, а не придуманы). 16 пинов правил на синтетике с известным ответом + прогон по 91 реальной единице.

Главное, что дал прогон по чистому тексту: правила, написанные по букве спецификации, в трёх случаях из шести мерили шум. Числа до и после — на 91 единице, на единицу:

Метрика было стало что было не так
нарушений типографики 3.87 0.18 ёлочка в начале строки считалась нарушением (228 срабатываний) — это легитимная цитата/мысль; прописная после атрибуции (119) — в основном имена собственные, которым прописная положена
единиц с транслит-междометиями 0.64 0.00 подстрочный матчинг: 77 «ара» внутри «барабан», 13 «ауч» внутри «паучьей». Чинится границами слова
потеряно / появилось величин 0.70 / 5.56 0.25 / 0.27 не разбирались русские числительные словами («пятьсот»); малые числительные в русском живут местоимениями («один из них»), которым в 一个 нет величины. Пол величины 100 выбран свипом 8 конфигураций
не-палладиевских имён 1.05 0.07 склонённые транскрипции («Чжэна» — 24 срабатывания) и ПЕРЕВЕДЁННЫЕ имена («Первопредок», «Кровавокрылая»), которым палладиевская форма не положена
омографов без ё 1.67 снята 130 из 152 — обычное множественное «все», 22 — «небо» в прямом значении. Различить «все/всё» без контекста нельзя; вместо счёта меряется политика ё и её единообразие

Живые остатки на том же корпусе: ханьцзы в финале 4 знака на 91 единицу (1 единица); не-палладиевских имён 6 срабатываний / 5 различных слов на 2184 кандидата, из них 2 — настоящие сигналы; смешение ты/вы в 0.31 единицы (нижняя граница: смешение между разными собеседниками легитимно); медиана доли слов черновика, доживших до финала, 0.942.

(Расхождение 5↔6 поймано верификатором verify_report.py, а не глазами: в первую редакцию отчёта попало число РАЗЛИЧНЫХ слов, посчитанное дедуплицирующим путём, при 6 фактических срабатываниях. Ровно тот класс ошибки, ради которого верификатор и написан.)

Объявленные границы батареи (в отчёт, не в примечание): величины 199 вне охвата; «длины предложений против нативного ру-корпуса» не считаются абсолютно — нативного русского референс-корпуса в дереве нет, и подставлять чужую константу нельзя (тот же класс ошибки, что снятый жанровый словарь D39.47), поэтому распределение сравнивается МЕЖДУ АРМАМИ; ты/вы без speaker-ID — сигнал к просмотру, не счёт ошибок; род прош. времени работает только при явной близости имени и глагола.

2.4. Живой листинг моделей

Снят по всем семи ключам, все отвечают. Против 00-provider-quirks.md появились новые жильцы, и они пойдут в кандидаты Ф1 слагами дня запуска: grok-4.5 · kimi-k3 · glm-5-turbo, glm-5.2 · gemini-3.5-flash, gemini-3.6-flash. У DeepSeek листинг прежний (два слага) — но урок календаря D39.61 в силе: «слаг живой ≠ модель та же», и вахта реестра 108 (пере-проба маппинга эффорта deepseek-v4-pro) остаётся первым платным шагом Ф1.


§3. Девиации от промта

# Девиация Основание
1 QE-ранкер — MetricX-24 вместо CometKiwi/xCOMET все варианты CometKiwi/xCOMET gated, токена нет; unbabel-comet не встаёт на py3.14
2 Reflow-план кодом (Q4c) не гонится §0.3: гипотеза уже опровергнута эксп-20 §5.2, деконфаунд несёт арм D
3 Арм C реального режима не покрывает класс инверсий; арм G сужен §2.2, объявлено ДО запуска
4 Счёт омографов ё снят из метрик §2.3, снят собственным замером как ложный

§4. Открыто

  • Материал Ф0.2 — на владельце (§0.2). Блокирует все платные фазы.
  • Ф0.3 банк среза, Ф0.6 предзамер судей, Ф1, Ф2а, Ф2б — не запускались.