146 lines
25 KiB
Markdown
146 lines
25 KiB
Markdown
# Эксперимент 07. Precision (доля ложных срабатываний) excision coverage-гейта
|
||
|
||
**Дата:** 2026-07-05 · **Зона:** полигон · **Гейтит:** боевой флип `gates.coverage.enabled:true` (D12/Q4).
|
||
|
||
## TL;DR
|
||
|
||
- **0 ложных срабатываний excision_suspect на 57 хороших переводах** (реальные zh/ja→ru чанки), в обоих слоях: **26 нарративных + 31 «диалоговый»** (по маркер-прокси), три источника «хорошего перевода» (LLM × 2 модели + канон Рогова/Фельдмана). **Оговорка широты:** источники пересекаются — zh-диалог = один автор (Лу Синь), переведённый 2–3 стеками на пересекающихся главах; ja — один draft-модель + одна человеческая пара. Distinct-источников мало (см. §методика). 0 флагов — надёжный факт; **широта — узкая**.
|
||
- **Диалого-плотный страх (§3.7) НЕ воспроизведён на классической прозе** — но и **не «опровергнут»**: русский **дробит** длинные CJK-предложения → `sent_cov ≥ 1.0` на «диалоговых» чанках (пул-медиана 1.375; разброс 1.05–1.66). НО «диалоговый» страт здесь — в основном **классическая проза Лу Синя с цитируемыми ТЕРМИНАМИ** (напр. 序-предисловие с 『』-цитатами литературоведческих терминов), НЕ вебновелл-диалог из терсных реплик/звукоподражаний. **Вебновелл-режим остаётся непроверенным** (см. просьбу владельцу). Ближайший к порогу — `sent_cov=0.886` (ja-нарратив), запас 18%.
|
||
- **Коридоры len_ratio не слишком узкие:** zh хорошие 2.64–3.74 (пол 2.2, запас ≥20%), ja хорошие 1.59–2.35 (пол 1.4, запас ≥14%). Ложных срабатываний по нижней границе len_ratio — ноль.
|
||
- **Рекомендация: флип МОЖНО (по precision).** Порог главы: **≥2 флага/главу = «fail», 1 флаг = «attention»** — консервативно к невиданному диалого-плотному вебновелл-корпусу. Коридоры/пороги менять не нужно.
|
||
- **Побочно (важно бэкенду): draft-эхо на классическом zh — не единичный баг, а системный.** deepseek-v4-flash эхнул **13/24** zh-чанков Лу Синя, grok-4.20-non-reasoning — **10/24** (лёгкая правка исходника вместо перевода, cjk_share 78–85%). Это ловит **интринсик echo-гейт** (не coverage) — но означает, что классическая/литературная zh требует эскалации черновика.
|
||
- **Побочно (D§85): `finish_reason=content_filter` не эмитит НИ ОДИН** из 5 провайдеров (deepseek/glm/kimi/gemini/grok) на SFW-violence — все `finish=stop`, 200, перевели. Ветка диспозиции по content_filter практически мертва на переводимом контенте → реальная страховка — refusal-blacklist (подтверждает D2.4).
|
||
- **Оракул↔Go:** Go-тесты паритета (`Oracle|Coverage|Split`) зелёные на HEAD → замер Python-оракулом Go-верен.
|
||
|
||
**Ограничение:** корпус — PD-классика (нарратив-смещённая проза), не современные вебновеллы/ранобэ (плотнее диалогом, звукоподражания, терсные реплики). Механизм («ru дробит → sent_cov растёт») должен держаться и там, но **решающий диалого-плотный срез — на реальных главах владельца** (просьба ниже).
|
||
|
||
## Вопрос
|
||
|
||
Из D12/Q4: recall гейта (ловит ли реальные вырезания ≥90%) бэкенд уже провалидировал на мини-сете выпиленных предложений. Флип упирается в **precision — долю ХОРОШИХ переводов, которые гейт ложно метит `excision_suspect`**, особенно на диалого-плотных главах, где наивная сегментация может недосчитать русских предложений против CJK-исходника (`sent_cov` ложно < 0.75).
|
||
|
||
Гейт (`backend/internal/pipeline/coverage.go`, порт `eval/refusal_bench.py::classify_output`): флаг, если `sent_cov < 0.75` ИЛИ `len_ratio < нижняя_граница_коридора` (zh<2.2, ja<1.4, en<0.70). Метрика — символы БЕЗ пробелов. Только нижняя граница. Чанки с исходником < `min_chunk_chars=500` (без пробелов) не гейтятся.
|
||
|
||
## Методика
|
||
|
||
**Принцип:** флаг на заведомо ХОРОШЕМ (полном, неусечённом) переводе = ложное срабатывание по построению. Три независимых источника хороших переводов:
|
||
|
||
1. **LLM продакшн-стек, deepseek-v4-flash draft** (реальная Ф1-модель черновика, thinking-ON). 49 чанков zh+ja.
|
||
2. **LLM, grok-4.20-non-reasoning** — второй переводчик zh (deepseek эхает классику; grok даёт полный перевод) → **робастный zh-LLM-слой**.
|
||
3. **Канонический человеческий перевод** (Рогов «А-кью», Акутагава «Нос»), выровненный **по главам** (выравнивание подтверждено сверкой заголовков 1:1: 第一章 序 ↔ Ⅰ Введение, 第四章 恋爱的悲剧 ↔ Ⅳ Трагедия любви, offset-4 для ch5-9). **⚠ Гранулярность: человеческий слой гейтит ГЛАВЫ, не продакшн-чанки** — каждая zh-глава «А-кью» (est 1515–2577 ток.) в проде разбилась бы на 2 чанка, ja «Нос» — на 4. → человеческие 10 точек — **глава-гранулярны, слабее для пер-чанкового гейта** (усреднение смягчает вариацию), поэтому острый чанковый тест несёт LLM-слой, человеческий — подтверждающий на реальной published-прозе.
|
||
|
||
**Distinct-источники (честно):** zh-диалог — 3 файла Лу Синя (ah-q ch1-4, ch5-9, zhufu), переведённые deepseek(8 ok)+grok(11 ok)+канон(9 глав) = **один автор, 2–3× перевод на пересекающихся главах**; ja — Акутагава/Дазай/Сосэки (нарратив-смещены) на одном draft-модели. Так что 31 «диалоговая» точка = НЕ 31 независимый источник.
|
||
|
||
**Чанкер** — верный порт `chunker.go`: абзацы (пустая строка) пакуются до ≤1500 est-токенов (`est = cjk + other/3`, пол 16), при абзаце сверх бюджета спуск к предложениям. Размеры чанков 500–1700 симв. (в калибровочной полосе 500–2500).
|
||
|
||
**Стратификация нарратив/диалог:** плотность диалог-маркеров `[「」『』“”《》:]` на предложение исходника; ≥0.5 → «диалог». **Грубый прокси:** считает отдельные МАРКЕРЫ (не пары кавычек) и не отличает цитируемый термин от многоклаузной реплики — потому «диалоговый» страт включает и цитат-плотную нарративную прозу (序-предисловие). Точнее было бы считать пары и исключать короткие цитаты; здесь — как ориентир, не строгая метка.
|
||
|
||
**Гейт-оракул:** `classify_output` (санкционирован D12/Q1 как источник истины; Go — порт 1:1, паритет-тест зелёный). `min_chunk_chars=500` применён (продакшн пропускает короче; исключено 2+2 коротких чанка, ни один не был бы FP). Из знаменателя coverage-FP исключены не-coverage диспозиции (echo/empty/refusal). **Это исключение НЕ прячет промахов гейта:** оракул проверяет cjk_share>0.15 (echo) ДО coverage-ветки, а сам эхо (78–86% CJK) всё равно провалил бы и len_ratio<коридор — так что ни один чанк, который ДОЛЖЕН флагаться, не потерян.
|
||
|
||
Харнесс: `eval/coverage_precision.py`; данные: `eval/data/coverage_precision/`.
|
||
|
||
## Результаты
|
||
|
||
### Доля ложных срабатываний по слоям и стратам
|
||
|
||
| Слой (модель) | n гейтнутых | флагов | нарратив | диалог | len_ratio диапазон | sent_cov диапазон |
|
||
|---|---|---|---|---|---|---|
|
||
| deepseek-v4-flash (ja+zh) | 35 | **0** | 24 | 11 | 1.59–3.53 | 0.89–1.72 |
|
||
| grok-4.20-non-reason (zh) | 12 | **0** | 1 | 11 | 3.03–3.74 | 1.05–1.66 |
|
||
| человеческий канон | 10 | **0** | 1 | 9 | 2.07–4.02 | 0.94–1.65 |
|
||
| **ИТОГО** | **57** | **0** | **26** | **31** | — | — |
|
||
|
||
**Доля ложных срабатываний = 0/57 = 0.0%**, в т.ч. **0/31 на «диалоговых» (маркер-прокси) чанках**. §3.7-страх **НЕ воспроизведён на классической прозе с цитируемыми терминами, но и НЕ опровергнут** — вебновелл-режим (терсные реплики/звукоподражания) непроверен; настоящее опровержение §3.7 требует реального вебновелл-корпуса владельца (рекомендация #4), не этих данных.
|
||
|
||
### Почему диалог не роняет sent_cov
|
||
|
||
Русский литературный перевод **дробит** длинные CJK-периоды на несколько предложений (и разворачивает диалог в тире-реплики с точками), поэтому на диалого-плотных главах `sent_cov` **растёт выше 1**, а не проседает:
|
||
- человеческие диалоговые главы «А-кью»: `sent_cov` 1.36–1.65;
|
||
- grok диалоговые zh-чанки: 1.05–1.66;
|
||
- 6 самых близких к полу 0.75 — все **ja-НАРРАТИВ** (Акутагава «Нос», Дазай), где японский из коротких 。-предложений и русский их слегка сливает: минимум **0.886** — запас 18% до порога. **Оговорка:** этот приграничный ja-страт — только deepseek-draft (второй модели нет; ×2-корроборация — только zh); запас 18% — наблюдение одного переводчика.
|
||
|
||
### Запас коридоров len_ratio (нижняя граница)
|
||
|
||
| Пара | хорошие переводы, len_ratio min–max | пол коридора | запас минимума |
|
||
|---|---|---|---|
|
||
| zh→ru | 2.64–3.74 (LLM), 3.05–4.02 (канон) | 2.2 | +20% |
|
||
| ja→ru | 1.59–2.35 (LLM), 2.07 (канон) | 1.4 | +14% |
|
||
|
||
Ни один хороший перевод не подошёл к нижней границе len_ratio ближе 14%. **Коридоры не слишком узкие** — источник ложных флагов по len_ratio не обнаружен. (Пороги — из эксп.02 на этом же классе текстов, самосогласованно.)
|
||
|
||
### Флагов на главу
|
||
|
||
Максимум флагов на любую главу (по всем слоям) = **0**. Даже порог «1 флаг/главу = fail» на этом корпусе не сработал бы ложно ни разу.
|
||
|
||
## 蛊真人 терсный диалог — ВЕБНОВЕЛЛ-режим закрыт (пакет-2 Task 5, 2026-07-09)
|
||
|
||
Прошлый вебновелл-срез НЕ закрыл диалого-плотный кейс (маркер-эвристика считала только строки, НАЧИНАЮЩИЕСЯ с кавычки, а вебновелл атрибутирует реплику ВСТРОЕННО — `方源道:“…”` — так диалого-плотные чанки читались как проза и терсный бакет не набирался). Починка (`eval/dialogue_precision.py`): детектор считает **цитируемый спан в ЛЮБОМ месте строки** (`“…”/「…」/『…』`), сегментация уважает строки исходника (реплики на отдельных строках НЕ склеиваются).
|
||
|
||
**Бакет:** 24 терсных чанка из 蛊真人 (dialogue_density=1.0 — каждая строка несёт реплику; terse_len 19–80 симв.) + 10 прозаических контрольных. Перевод — `grok-4.20-0309-non-reasoning` *(слаг из providers.json; исправлено оркестратором по внешнему ревью 09.07 — в первой редакции ошибочно «grok-4.3»)*, **completeness-судья кросс-семейный** gemini-2.5-flash (D13.3).
|
||
|
||
| Бакет | n | flagged excision | судья: complete | **FALSE excision** (flagged & complete) |
|
||
|---|---|---|---|---|
|
||
| терсный диалог | 24 | **0** | 16 | **0** |
|
||
| проза (контроль) | 10 | **0** | — | **0** |
|
||
|
||
- **Доля ложных excision_suspect на терсном диалоге = 0/24 (0%)** — воспроизводит классический бейз exp07 (0 FP/57) на **невиданном вебновелл-тексте**. §3.7-страх **не воспроизводится и на вебновелл-диалоге**.
|
||
- **Механизм подтверждён на терсном диалоге:** min `len_ratio=2.69` (пол 2.2 — запас 22%), min `sent_cov=0.75` (ровно порог, `<` строгий → не флагается). Даже терсные реплики zh→ru расширяются ≥2.69× — русский дробит/разворачивает, `sent_cov` не проседает. Страх «терсный диалог → низкий len_ratio → ложный флаг» **не подтвердился**.
|
||
- **⚠ Recall-оговорка (важно):** судья пометил **8/24** терсных чанка с 1–2 мелкими пропусками, а гейт НЕ флагнул НИ ОДНОГО из них → на терсном диалоге гейт **precision-safe, но recall-слаб для МЕЛКИХ пропусков** (высокий zh→ru len_ratio маскирует одну выпавшую короткую реплику). Согласуется с дизайном (excision-детект = нижняя граница, D12/Q3; полный recall — entity-счёт + судья, Ф2). Пропуски — судейские, не сверены вручную (могут включать строгость судьи к сжатию реплик).
|
||
- Побочно: `grok-4.20-0309-non-reasoning` эхнул 1/24 (`untranslated_echo`) — тот же класс поведения «reasoning-off + плотный CJK», что у grok-4.3 c `reasoning_effort:none` в exp10 §3b, но **другой слаг** (исправлено оркестратором 09.07: кросс-ссылка корроборирует класс, не конкретную модель).
|
||
|
||
**Вывод для флипа гейта (D12/Q4):** снятие 1-флаг-толерантности терсным диалогом **не угрожается ложными срабатываниями** (0/24 на 蛊真人). Пороги/коридоры менять не нужно. Recall на мелких пропусках терсного диалога — известное ограничение (Ф2). Провенанс: `eval/data/dialogue_precision/`.
|
||
|
||
## Побочные находки
|
||
|
||
### 1. Draft-эхо на классическом zh — системное, не единичное (→ бэкенду)
|
||
|
||
deepseek-v4-flash вернул исходник (лёгкая модернизация: 耳朶→耳朵, 『』→"") вместо перевода на **13 из 24** zh-чанков Лу Синя; grok-4.20-non-reasoning — на **10/24**. Это НЕ усечение (reasoning_tokens 64–195, completion не упёрся в лимит) — модель «почистила» классику, а не перевела. cjk_share 78–86% → ловит интринсик **untranslated_echo**-гейт (правильно; это его работа, не coverage). Уроки:
|
||
- эхо-мина deepseek воспроизводится **на масштабе** на литературной/классической zh, не только на редких фрагментах (эксп.02 её не видел на коротких refusal-фрагментах);
|
||
- эхо не уникально для deepseek — **grok-non-reasoning тоже эхает** классику (частично другие чанки) → это свойство класса «плотная классическая zh», вероятно усиленное присутствием текста в претрейне;
|
||
- **следствие для Ф1:** zh-черновик классики требует single-hop эскалации на эхо (уже реализовано, Веха 2.5); для современных вебновелл (не в претрейне) риск ниже, но перепроверить на корпусе владельца.
|
||
|
||
### 2. content_filter не эмитится (D§85, → бэкенду)
|
||
|
||
`eval/content_filter_probe.py`, самый refusal-близкий SFW-фрагмент (l2-violence, Говард, 1470 симв.), 5 ядровых провайдеров:
|
||
|
||
| Провайдер | модель | finish_reason | http | итог |
|
||
|---|---|---|---|---|
|
||
| deepseek | deepseek-v4-flash | stop | 200 | перевёл |
|
||
| grok | grok-4.20-non-reason | stop | 200 | перевёл |
|
||
| glm | glm-4.5-air | stop | 200 | перевёл |
|
||
| kimi | kimi-k2.6 | stop | 200 | перевёл |
|
||
| gemini | gemini-2.5-flash | stop | 200 | перевёл |
|
||
|
||
**Ни один не эмитит `finish_reason=content_filter`** на переводимом (даже насильственном) контенте — все `stop`, перевели целиком. Подтверждает D2.4: ветка диспозиции по content_filter практически мертва; реальная страховка на SFW-диапазоне — **refusal-blacklist гейт**, не finish_reason.
|
||
|
||
**Нюанс по Gemini-3.1-pro (апекс/судья, отдельно от 2.5-flash):** в apex-пробе (эксп.08) gemini-3.1-pro-preview **единожды** вернул `finish=content_filter: PROHIBITED_CONTENT` на одном ja-черновике, но **НЕ воспроизвёл** на 4 контрольных (l2-violence / rashomon / hashire / ah-q — все `stop`, перевели). → на Gemini-апексе ветка content_filter **изредка срабатывает** (в отличие от 5 ядровых), но не системна — как надёжный сигнал не годится, refusal-blacklist остаётся страховкой. Определяющий 18+-тест — на explicit-фрагментах владельца (18+ рука эксп.02).
|
||
|
||
## Рекомендация (владельцу/оркестратору → бэкенду)
|
||
|
||
1. **Флип `gates.coverage.enabled:true` по precision безопасен** — 0/57 ложных, оба страта. Гейтит только остаточный риск диалого-плотных вебновелл (см. ограничение).
|
||
2. **Порог главы (паспорт качества, D12):** **≥2 флага/главу → chapter-verdict `fail`; 1 флаг → `attention`** (не fail). Обоснование: на PD-корпусе max флагов/главу = 0 с большим запасом, но держим 1-флаг-толерантность как страховку под непроверенный вебновелл-корпус. Одиночный флаг всё равно даёт single-hop эскалацию чанка (D12) — это дёшево при FP≈0.
|
||
3. **Коридоры/пороги НЕ менять:** sent_cov 0.75 (запас 18%), zh 2.2 / ja 1.4 (запас 14–20%) — не источник ложных срабатываний.
|
||
4. **Пере-снять precision на 25–35 главах реальных вебновелл/ранобэ владельца** перед снятием 1-флаг-толерантности (диалог-плотность выше классики).
|
||
|
||
## Ограничения
|
||
|
||
- Корпус — PD-классика начала XX в. (Лу Синь, Акутагава, Дазай, Сосэки): нарратив-смещённая, плотная проза; современные вебновеллы/ранобэ диалого-плотнее (быстрый обмен репликами, звукоподражания, терсные строки). Механизм «ru дробит CJK → sent_cov растёт» должен держаться и усиливаться на коротких репликах, но **терсные междометия/звукоподражания-строки** — единственный неизмеренный остаточный путь к низкому sent_cov. Нужен реальный корпус.
|
||
- zh-LLM-слой частично держится на grok (deepseek эхал половину) — но человеческий канон покрывает 9 диалого-плотных глав «А-кью» независимо, так что zh-диалог-вывод не висит на одной модели.
|
||
- Один переводчик канона на пару; порядок величин надёжен.
|
||
|
||
## Просьба владельцу
|
||
|
||
Для решающего диалого-плотного среза (и довалидации токен-калибровки эксп.01) — **3–5 глав реальной вебновеллы/ранобэ zh или ja с любым русским ориентиром** (даже черновым), файлы в `eval/data/samples/<lang>/`; харнесс пересчитает автоматически. Идеально — то, что реально пойдёт в продукт (для zh — что-то вне претрейна, чтобы обойти эхо-мину).
|
||
|
||
## Расхождение с планом
|
||
|
||
Нет расхождений с архитектурой. Одно уточнение к D2.4 (content_filter) — подтверждено эмпирически (ветка мертва на SFW), выше.
|
||
|
||
## Воспроизведение
|
||
|
||
```bash
|
||
eval/.venv/bin/python eval/coverage_precision.py --arm both --workers 6 # deepseek draft + human canon
|
||
eval/.venv/bin/python eval/coverage_precision.py --arm llm --translator grok --langs zh # robust zh LLM arm
|
||
eval/.venv/bin/python eval/content_filter_probe.py # D§85 finish_reason probe
|
||
cd backend && go test ./internal/pipeline/ -run 'Oracle|Coverage|Split' # Go↔oracle parity
|
||
```
|
||
Данные: `eval/data/coverage_precision/{results.json,results_grok_zh.json,content_filter_probe.json}`.
|