53 KiB
Полигон, пакет-6: размеченный набор для чекеров — методика, объёмы, precision/recall
Сессия: полигон, 26.07.2026. Промт: docs/POLYGON_PACKAGE6_CHECKER_LABELS_SESSION_PROMPT.md (D39.38).
Стоимость: $0 — ни одного вызова провайдера. Сессия НЕ коммитит.
Набор: на стенде, /home/ubuntu/books/gu-zhenren/labels/ (содержит текст книги → ВНЕ git).
В git — только этот отчёт и методика.
Порядок исполнения (протокол «ровно так»). Разделы §1 (определения) и §2 (объёмы) записаны в файл ДО того, как была размечена хоть одна строка, и ДО того, как был прочитан хоть один вердикт чекера. Вердикты (
retrieval_state.n_style_flags,style_detail,postcheck_detail, детали изreport-final.txt) в этой сессии не открывались до §4. Колонки вердиктов физически не выбирались SQL-запросами сборки корпуса — см.build_corpus.py.
Ревью-шапка (оркестратор №8, 26.07): ПРИНЯТО, D39.39. Приёмка исполнением:
metrics.jsonвоспроизведён ПОБАЙТНО ре-раномmeasure.py(харнесс вызывает настоящиеinternal/checksчерез replace-модуль; md57ee20d67…до и после); разметка пересчитана независимо поlabels/*.jsonl(3015 позиций, дефекты по классам сходятся, кросс-таб K4 100/46/4/9 совпадает); баг U+0301 воспроизведён живьём наTokenizeCyrillic(«бо́льшим» → «бо»+«льшим»); инверсия K5c подтверждена по сырому корпусу (acceptance:10:0: «Один процент, два процента, три процента» для 一成,两成,三成 при шести вхождениях «процент», глушащих подчекер —checkers.go:355); код-якоря дефектов №1, 5–8, 11–12 сверены с кодом (в т.ч.cheng_reбез 两 при两=2вcjk_numeral; draft внутри cheap-гейтов потребляет только регресс-гард —cheapgates.go:182). Нит: в §3 бакет «CJK 9» — этоcurly-quote; подклассcjk-leak= 11 строк, как в §6 (счётчики консистентны).
§1. Определения классов — зафиксированы ДО разметки
Определения выведены из КОДА (код первичен), а не из названий классов. Каждое — моими словами,
с якорем file:line. Рядом — что чекер, по коду, НЕ считает дефектом (это важнее определения:
именно тут живут расхождения разметки и вердикта).
K1. DC1 — время (时辰 / счётные единицы)
Якорь: checkers.go:207-245, данные пары — dc-checkers.txt:41-54.
Моё определение. Дефект = в источнике стоит длительность, измеренная в 时辰 (китайский «большой
час» = 2 астрономических часа), а в переводе та же длительность названа в русских часах с ЧИСЛОМ,
взятым из источника без пересчёта. 三个时辰 → «три часа» вместо ~«шесть часов»: длительность
занижена вдвое. Отдельная подформа — дробная: 半个时辰 (≈1 час) → «полчаса», длительность тоже
занижена вдвое.
Что кодом НЕ считается дефектом (и я размечаю так же, иначе меряю не тот детектор):
- перевод без явного счёта часов («долго», «через время», «полдня») — валидный парафраз, не дефект
(
checkers.go:230-232: нет совпаденияru_hours_re→ 0); - рендер с ПРАВИЛЬНЫМ числом (三个时辰 → «шесть часов») — не дефект;
- рендер с числом, которое не равно ни исходному счёту, ни удвоенному (三个时辰 → «четыре часа») —
по коду НЕ дефект: условие срабатывания
ruNum == n && ruNum != expectedHours(checkers.go:239). Такую строку я размечаю как дефект перевода, но помечаю подклассомoutside-rule— она обязана попасть в recall-гэп, а не в precision-ошибку.
Единица разметки. Одно вхождение 时辰 в исходнике юнита × прогон (один и тот же исходник,
переведённый разными армами, даёт разные рендеры → разные items). ID: run:chapter:chunk_idx:src_offset.
K2. Латиница-остаток
Якорь: checkers.go:378-411.
Моё определение. Дефект = в русском выходе осталось целое ЛАТИНСКОЕ слово, которое там быть не должно: непереведённый кусок исходного/промежуточного английского («открыл их again»), утечка служебной лексики промпта/разметки, английский термин вместо русского.
Что кодом НЕ считается дефектом:
- токен с заглавной буквой — код отбрасывает (
checkers.go:391): «сигнал имени собственного/бренда». Я размечаю такие строки отдельно: заглавная ≠ легитимность (TM,BANK,Cultivation— утечки); - токен короче 3 букв, токен с цифрой внутри (
v1), римская цифра — отбрасывается; - токен в per-project allowlist.
Единица разметки. Одно вхождение максимального латинско-цифрового токена в целевом тексте
(финал или черновик). ID: run:chapter:chunk_idx:field:byte_offset.
K3. Битые словоформы
Якорь: checkers.go:437-454,
данные цели — target-ru.txt:9 (broken_suffix йть).
Моё определение. Дефект = русское слово, которого в языке нет как формы: искажённая морфология («войть» вместо «войти»), склеенные/разорванные основы, обрубленное окончание, слово с несуществующим суффиксом. Не относится: редкие, но правильные слова; авторские неологизмы книги; транслит имён по Палладию; окказионализмы жанра.
Что кодом НЕ считается дефектом: ВСЁ, кроме слов длиной ≥4, заканчивающихся на «-йть».
Код это и заявляет прямо: «Zero false-positive by construction (only a structural signature, no
dictionary)» (checkers.go:436). То есть у класса заведомо один-единственный обнаруживаемый шаблон,
а класс «битые словоформы» шире. Это и надо измерить.
Единица разметки. Один русский словотип (тип, не токен) + позиция первого вхождения.
ID: type:<слово>, плюс список вхождений.
K4. dialogue_dash
Якорь: cheapgates.go:199-241,
форма реплики — chevronSpeechShape (:250), «это речь вслух?» — isSpokenChevronLine
(checkers.go:467-483),
данные цели — target-ru.txt:16-53.
Класс распадается на два независимых правила; меряю их РАЗДЕЛЬНО, потому что и живут они врозь.
K4a — неверный маркер реплики (строчное правило). Дефект = строка открывает прямую речь
маркером, которого в русской типографике нет: прямая ASCII-кавычка ", дефис - или короткое
тире – вместо длинного —. Условие формы: после маркера пробел и буква (для кавычки пробел
необязателен) — dialogueShape/quoteShape (:279,:293).
НЕ дефект по коду: — 15 минут спустя (тире + пробел + цифра — это врезка, не реплика,
:210); дефис, приклеенный к слову (перенос).
K4b — смешение стилей прямой речи (правило уровня чанка). Дефект = внутри одного чанка речь
вслух оформлена И длинным тире, И кавычками-ёлочками. Ёлочная строка засчитывается, только если
(1) имеет форму атрибуции «Реплика», — глагол (chevronSpeechShape, :250) И (2) её атрибуция
содержит глагол ГОВОРЕНИЯ из закрытого списка цели и НЕ содержит маркера внутренней речи
(«про себя», «мысленно», …) — isSpokenChevronLine.
НЕ дефект по коду и по русской норме: ёлочки для МЫСЛИ рядом с тире для речи — это правильно
набранная проза, а не столкновение стилей (правка v5, cheapgates.go:68-81). Именно тут стоит
цифра «8→0», которую надо проверить честно.
Сознательные промахи кода: восклицательная реплика «Реплика!» — (без запятой) и безатрибутивная
«Реплика.» не распознаются как ёлочная речь (:248-249).
Единица разметки. K4a/K4b — одна непустая строка целевого текста.
ID: run:chapter:chunk_idx:field:line_no. Дополнительно размечаю строки, начинающиеся с БУКВЫ, —
там живёт прямая речь вообще без маркера (это невидимо для чекера by construction, идёт в recall-гэп).
K5. Число-дрейф / магнитуды
Четыре разных детектора; меряю раздельно.
- K5a — 万/億-магнитуда (cheapgates.go:421-462).
Дефект = порядок величины из источника (
三万= 3·10⁴) не представлен в переводе ни числом, ни словом-магнитудой нужного порядка. НЕ дефект по коду: магнитуда, пересказанная прозой без слов-магнитуд (:453-459— молчит); идиомы万一/万分/千万/亿万без цифры перед 万 (:491); голые единицы十万/百万(там нет цифры перед маркером). - K5b — DC2 千万 / 数十万 (checkers.go:281-302).
Дефект = 千万 (10⁷) отдано как «тысячи», 数十万 (несколько ×10⁵) — как «десятки тысяч».
Код сам признаёт неустранимую двусмысленность: 千万 сплошь и рядом ГИПЕРБОЛА («тьма», «мириады»),
и её «тысячи» — в регистре (§5-A4,
cheapgates.go:90-93). Размечаю гиперболу отдельным подклассомhyperbole, решение по ней — не моё (идёт владельцу). - K5c — 成-процент (checkers.go:346-370).
Дефект = 成 (десятая доля: 六成 = 60%) отдано десятичной ДОЛЕЙ вместо процента.
Внимание: код считает дефектом ФОРМУ, а не значение (
cheapgates.go:84-89— сознательно оставлено). Значит «три десятых» для 三成 — по коду дефект, хотя значение верное. Размечаю по коду (form-defect), а «значение верно/неверно» веду ВТОРОЙ колонкой. - K5d — дрейф чисел черновик→финал
(regressionguard.go:64-76).
Дефект = многозначное арабское число есть в черновике и пропало в финале (или появилось из
ниоткуда). НЕ дефект по коду: однозначные числа (их штатно пишут словом); число, переписанное
словами при редактуре, — код сам называет это своим ложным флагом (
:24-28). Гейт включён только в мини-прогоне (minirun/pipeline.yaml: regression_guard.enabled: true); в rerun2 его нет.
Единица разметки. K5a/K5b/K5c — вхождение триггера в исходнике × прогон. K5d — (юнит, числовой токен, сторона).
K6. Глоссарий-промахи
Якорь: mempostcheck.go:61-122.
Моё определение. Дефект = термин глоссария, чей ИСХОДНЫЙ вид встретился в исходнике юнита, а в переводе не появилось ни одной принятой формы его перевода: термин выброшен, переведён по-другому или искажён.
Что кодом НЕ считается дефектом: sticky-записи (их src в этом чанке и не должен быть, :69);
записи без dst; записи со статусом ambiguous — они не идут в счёт (CountConfirmedMisses, :49).
Принятое множество = базовый dst ∪ сохранённые склонённые формы (decl.forms), сравнение —
по целому слову с границами по буквам (containsWholeWord, :128).
Единица разметки. (юнит, термин глоссария, чей src встречается в исходнике юнита).
ID: run:chapter:chunk_idx:src.
Общая шкала меток
Каждый item получает одну метку:
defect · ok · disputable (спорные — владельцу, я их не решаю) ·
плюс необязательный подкласс (outside-rule, hyperbole, form-defect, unmarked-speech, …).
disputable в знаменатель precision/recall НЕ входит и считается отдельно.
§2. Материал и объёмы — зафиксировано ДО разметки
Корпус: /home/ubuntu/books/gu-zhenren/labels/raw/corpus.jsonl — 91 юнит из шести реальных
прогонов на стенде (никакой синтетики).
| прогон | юнитов | исходник, зн. | черновик, зн. | финал, зн. | что это |
|---|---|---|---|---|---|
acceptance |
53 | 72 646 | 239 534 | 239 522 | приёмка 25 глав, 10.07 (старый стек) |
minirun |
14 | 28 837 | 90 882 | 94 490 | мини-прогон 10 глав, 25.07 (шиппинговая форма) |
rerun2-dspro |
7 | 14 127 | 46 142 | 46 080 | пере-прогон гл.1–5, арм deepseek-v4-pro |
rerun2-glm |
7 | 14 127 | 46 142 | 45 956 | тот же исходник, арм glm |
rerun2-mistral |
7 | 14 127 | 46 142 | 41 875 | тот же исходник, арм mistral |
verify2 |
3 | 5 368 | 17 778 | 17 423 | двухглавая верификация, 26.07 |
| итого | 91 | 149 232 | 486 620 | 485 346 |
Исходники восстановлены детерминированной пере-нарезкой текущим кодом
(chunk.IngestEncoded + chunk.SplitChunks + группировка по EditUnitID — 1:1 с
status.go:184 и wave.go:41), черновики — из checkpoints.response_text через
checks.ExportNormalize, финалы — из экспортов прогонов.
Самопроверка сборки: восстановленный исходник сверен побайтно с колонкой source готового
экспорта rerun2 --pairs. Совпадение полное на юнитах без заголовка главы; на 5 юнитах-началах
главы расхождение ровно 4 руны — это 第N节:, который текущий код снимает в манифест-заголовок
(политика заголовков пака-13, export.go:213-220), а экспорт 23.07 ещё оставлял в колонке.
Расхождение объяснено и ожидаемо, а не молча проигнорировано.
Физический потолок объёма по классам (посчитан ДО разметки)
Промт задаёт ориентир 200–300 строк на класс. Четыре класса из шести физически столько не содержат — вот честные потолки на этом корпусе:
| класс | что считалось | потолок items | 200–300 достижимо? |
|---|---|---|---|
| K1 DC1 时辰 | вхождений 时辰 в исходниках юнитов | 15 (4 в уникальном исходнике) | нет |
| K2 латиница | вхождений латинско-цифровых токенов в целевом тексте | 92 (32 типа; из них попадают под правило кода 7 типов) | нет |
| K3 битые формы | русских словотипов | 12 946 типов (1 999 hapax) | да, выборкой |
| K4 dialogue_dash | непустых строк целевого текста | 6 863 (тире 1 259 · ёлочки 478 · прочие 5 088 · дефис/кавычка 0) | да, выборкой |
| K5a магнитуда 万 | числовых пробегов с 万 в исходнике | 37 уникальных (×армы) | нет |
| K5b DC2 | 千万 3 · 数十万 2 уникальных (×армы) | ~15 | нет |
| K5c 成-процент | цифра+成 в исходнике |
35 уникальных (×армы) | нет |
| K5d дрейф чисел | многозначных арабских чисел во всём корпусе | 19 | нет |
| K6 глоссарий | (юнит × термин, чей src есть в исходнике) | сотни | да |
Синтетикой я эти классы не добиваю (см. §6 — там отдельная помеченная секция о том, чего набор не покрывает и что для этого нужно).
Отдельный факт, который стоит прочитать до всех цифр: строк с дефисом или ASCII-кавычкой в начале
во ВСЁМ корпусе — ноль. То есть ветка K4a (hyphenLike, cheapgates.go:213-222) на реальном
материале не срабатывает ни разу и её precision на этом наборе неизмерим — измеряется только K4b.
§3. Разметка: что сделано и как
Разметил я, читая исходник и перевод; вердикты чекеров были получены ТОЛЬКО после того, как все
файлы labels/*.jsonl были записаны. Сети-кандидаты выведены из определений §1, а не из реализации
чекера (иначе recall меряется сам против себя) и сознательно ШИРЕ правил кода.
| класс | единица разметки | размечено | из них дефект | спорных |
|---|---|---|---|---|
| K1 DC1 时辰 | вхождение 时辰 × прогон | 15 (исчерпывающе) | 6 | 0 |
| K2 латиница | вхождение лат.-цифрового токена | 92 (исчерпывающе) | 40 | 0 |
| K3 битые формы | русский словотип | 2 177 (весь hapax-слой 1 999 + 178 из сетей) | 10 | 0 |
| K4 dialogue_dash | строка целевого текста | 348 (ёлочки 150 · тире 80 · буквенные 80 · markdown 29 · CJK 9) | 4 | 9 |
| K5a магнитуда 万 | вхождение × прогон | 67 (исчерпывающе) | 1 | 3 |
| K5b DC2 | вхождение × прогон | 11 (исчерпывающе) | 1 | 3 |
| K5c 成-процент | вхождение × прогон | 40 (исчерпывающе) | 7 | 4 |
| K5d дрейф чисел | (юнит, число, сторона) | 5 (исчерпывающе) | 0 | 0 |
| K6 глоссарий | (юнит × сработавший термин) | 260 из 1 257 (выборка) | 1 | 2 |
| итого | 3 015 | 70 | 21 |
Ориентир 200–300 строк выдержан там, где материал это позволяет (K3, K4, K6). Четыре класса физически меньше — размечены ЦЕЛИКОМ и цифра названа честно; синтетикой не добивал (см. §6).
Две проверки собственной работы исполнением, обе поймали мою ошибку, не чужую:
- Восстановленный исходник сверен побайтно с колонкой
sourceготового экспортаrerun2 --pairs(§2) — расхождение оказалось ровно в 4 руны заголовка и объяснено. - Первый прогон дал по латинице «чекер не сработал ни разу» при том, что пер-юнитный прогон
показывал
latin_residue=1. Причина — в моей измерялке: Go отдаёт БАЙТОВЫЕ смещения, а пул считан в символах. После починки — precision 1.000 / recall 0.750. Цифра «0» была моей, не кода.
§4. Первое измерение: precision/recall ТЕКУЩИХ чекеров
Вердикты получены вызовом НАСТОЯЩИХ функций пакета internal/checks (харнесс-модуль вне репо,
replace на репозиторный backend; ноль правок в репозитории). Спорные строки в знаменатель не
входят. Единица измерения выбрана та, на которой чекер реально выносит вердикт: для src↔tgt
подчекеров это ЮНИТ (одно срабатывание на единицу редактуры), для остальных — строка/токен/словотип.
| класс | уровень | N | TP | FP | FN | TN | precision | recall |
|---|---|---|---|---|---|---|---|---|
| K1 DC1 时辰 | юнит | 15 | 6 | 0 | 0 | 9 | 1.000 | 1.000 |
| K2 латиница (детектор) | токен | 92 | 30 | 0 | 10 | 52 | 1.000 | 0.750 |
| K2 латиница (боевой охват: только финалы) | токен | 31 | 1 | 0 | 1 | 29 | 1.000 | 0.500 |
| K3 битые словоформы | словотип | 2 177 | 1 | 0 | 9 | 2 167 | 1.000 | 0.100 |
| K4a неверный маркер реплики | строка | 348 | 0 | 0 | 0 | 348 | н/д | н/д |
| K4b ёлочная речь вслух (смешение) | строка | 146 | 5 | 1 | 41 | 99 | 0.833 | 0.109 |
| K5a магнитуда 万/億 | юнит | 27 | 0 | 0 | 1 | 26 | н/д (0 срабатываний) | 0.000 |
| K5b DC2 千万/数十万 | юнит | 8 | 1 | 0 | 0 | 7 | 1.000 | 1.000 |
| K5c 成-процент (читательская мерка) | юнит | 15 | 1 | 5 | 1 | 8 | 0.167 | 0.500 |
| K5c 成-процент (конвенция кода: форма=дефект) | юнит | 16 | 7 | 0 | 1 | 8 | 1.000 | 0.875 |
| K5d дрейф чисел | позиция | 5 | 0 | 4 | 0 | 1 | 0.000 | н/д |
| K6 глоссарий (все сработавшие термины) | юнит×термин | 258 | 1 | 14 | 0 | 243 | 0.067 | 1.000 |
| K6 глоссарий (только реально инъецированные) | юнит×термин | 215 | 0 | 8 | 0 | 207 | 0.000 | н/д |
«н/д» — метрика не определена: либо чекер не сработал ни разу (нет знаменателя precision), либо положительных по разметке ноль (нет знаменателя recall). Это тоже результат, а не пропуск.
Что стоит за цифрами (механика, с якорями)
K1 (DC1 时辰) — 6/6 и ни одного ложняка. Единственный класс, где линейка работает ровно как
описана. Шесть подлинных дефектов: 三个时辰 → «три часа» (три арма rerun2 из четырёх) и
半个时辰 → «полчаса» (dspro, glm, acceptance). Девять корректных рендеров, включая два
транслитерированных («чуть больше одного шичэня») — код на них молчит, и это правильно.
K5c (成-процент) — самая тяжёлая находка пакета. Пять из шести срабатываний пришлись на юниты,
где ЗНАЧЕНИЕ передано верно («две-три десятых» для 两三成 = 20–30%, «сорок четыре сотых» для
四成四 = 0.44). А ЕДИНСТВЕННЫЙ юнит с настоящими ошибками шкалы — acceptance:10:0, где
一成 (10%) отдано как «один процент», 一成,两成,三成 как «Один процент, два процента, три
процента», а 一成四 (14%) как «одна доля и четыре части», — пропущен: в этом же юните где-то
есть слово «процент», и супрессор процентной формы гасит подчекер целиком
(checkers.go:355). То есть на реальном материале
правило сработало ровно наоборот замыслу: промолчало там, где шкала действительно сломана в 10 раз,
и заговорило там, где всё верно. По конвенции самого кода (форма-дробь = дефект) precision=1.000 —
цифры расходятся не из-за реализации, а из-за того, ЧТО считать дефектом. Это вопрос владельцу (§5).
K4b (dialogue_dash, смешение) — «8→0» проверено. Правка v5 в основном работает: из 100
размеченных ёлочных МЫСЛЕЙ чекер флагнул одну (99 из 100 — молчание). Но ёлочных реплик ВСЛУХ
в корпусе 46 (31% ёлочных строк), и из них поймано 5. Разбор 41 пропуска:
| причина | строк |
|---|---|
безатрибутивная реплика — связки «…», — нет вообще |
23 |
реплика оканчивается на ! / ? / …, запятой после » нет — сознательный промах кода (cheapgates.go:248-249) |
12 |
закрывающая » дальше 400 знаков (длинная реплика) |
3 |
| связка есть, но глагол говорения не из закрытого списка («раздался голос», «покинули его») | 3 |
Единственное ложное срабатывание вскрывает механику: строка
«Месть не входит в мои планы…», — подумав так, Фан Юань усмехнулся … и равнодушно произнёс: —
— мысль, но isSpokenChevronLine ищет глагол говорения по ВСЕЙ строке
(checkers.go:471-481), а «произнёс»
стоит в другом предложении и относится к следующей реплике.
K4a — правило не проверяемо на этом корпусе. Строк, начинающихся с дефиса, короткого тире или
ASCII-кавычки, во всём корпусе (6 863 непустых строки) — ноль. Ветка hyphenLike
(cheapgates.go:213-222) ни разу не
получила входа. Precision/recall неизмеримы, и это надо знать: половина класса живёт непроверенной.
Класс, которого у чекера нет: ОБРАТНАЯ ошибка маркера. 4 строки из 80 размеченных тире-строк —
внутренняя речь, набранная тире как речь вслух: «— Жаль только этого хорошего гу, — вздохнул ПРО
СЕБЯ Фан Юань», «— …, — недовольно хмыкнул ПРО СЕБЯ старейшина», «— Как же так… — бормотал СЕБЕ ПОД
НОС Фан Чжэн». Маркеры «про себя» / «себе под нос» уже есть в данных цели как inner_marker, но
применяются только к ёлочкам. По конструкции чекер считает любую тире-строку правильной, поэтому
recall здесь 0.000 из 4.
K5a (магнитуда 万) — ноль срабатываний на всём корпусе, и это структурно. Из 67 вхождений 55 —
идиомы (万物之灵, 万一, 千辛万苦, 万家灯火, 成千上万, 罪该万死, 十万八千里…). Оставшиеся реальные
магнитуды (数十万, 千万, 数万) не проходят предохранитель «цифра перед большим маркером»
(cheapgates.go:513-526): 数 и 十 — не
цифры класса Digit, 千 — единица, а не цифра. Предохранитель отсекает идиомы правильно, но вместе
с ними — и все реальные магнитуды этого корпуса. Единственный подлинный дефект класса
(数十万 → «десятки тысяч» у mistral) пойман ДРУГИМ подчекером, DC2.
K5d (дрейф чисел) — 4 срабатывания, все безобидные. Три — номера глав в заголовке, которого не
было в черновике; одно — 66% → «шестьдесят шесть процентов, то есть ровно тот ложный флаг, который
код за собой признаёт (regressionguard.go:24-28).
Подлинного дрейфа в корпусе нет: многозначных арабских чисел во всех 91 юните — 19.
K6 (глоссарий) — 14 ложных на 1 подлинный. Разбор:
- 6 — однознаковый ключ
转сработал на ГЛАГОЛЕ (转身 «обернулся», 转过转角 «за поворотом», 辗转 «мыкался», 转为 «стать», 脑筋转不快 «мысль ворочается медленно»). Ранга в исходнике нет, переводить нечего, но условие «src сработал» — подстрочное вхождение. - 7 — целевая форма есть, но во МНОЖЕСТВЕННОМ числе, которого нет в
decl.forms: «первокамней» при формахпервокамня/первокамню/первокамень, «смертных» присмертного/…, «гу-тварей» пригу-твари/гу-тварь. Это ровноinflection_gap, который код называет пробелом СИДА, а не кода (mempostcheck.go:109-111) — теперь он измерен: 7 из 258 пар, ~2.7%. - 1 —
甲等 → «разряд А», в тексте «разряда „А“»: типографские кавычки внутри формы рвут сравнение по целому слову.
Единственный подлинный промах (minirun:8:1, 古月方正 ×6 → «Фан Чжэна», клановое имя исчезло)
пойман. Но на подмножестве, которое боевой пост-чек РЕАЛЬНО проверял (217 из 260 пар — остальные не
прошли отбор/бюджет инъекции), подлинных промахов нет вовсе, а ложных 8: precision 0.000.
K2 и K3 — линейки честные, но узкие. Ни одного ложного срабатывания в 92 и 2 177 items
соответственно. Recall ограничен конструкцией: латиница пропускает заглавные («Cultivation» в
ФИНАЛЕ acceptance — checkers.go:391), токены
короче трёх («гy» с латинской y вместо кириллической у — гомоглиф) и токены с цифрой; битые
формы ловят ровно один шаблон «-йть» из десяти найденных мной битых слов.
Зафиксированные дефекты чекеров (фиксация, НЕ правка)
Я не автор чекеров и ничего здесь не чинил. Каждый пункт — с якорем и с тем, чем он подтверждён на наборе.
| # | якорь | что именно | подтверждение на наборе |
|---|---|---|---|
| 1 | checkers.go:355 | процентный супрессор гасит подчекер по ВСЕМУ юниту, если слово «процент» встретилось где угодно | единственный юнит с настоящими 10×-ошибками шкалы (acceptance:10:0) пропущен |
| 2 | checkers.go:471-481 | глагол говорения ищется по всей строке, а не в атрибуции ёлочки | 1 из 1 ложных срабатываний K4b |
| 3 | cheapgates.go:250-275 | форма «…», — — единственный признак ёлочной речи |
38 из 46 реплик вслух не опознаны (23 безатрибутивных + 12 с !/?/… + 3 длинных) |
| 4 | cheapgates.go:209-233 | тире-строка засчитывается корректной ВСЕГДА | 4 мысли, набранные тире («вздохнул ПРО СЕБЯ»), невидимы, хотя inner_marker уже есть в данных цели |
| 5 | cheapgates.go:513-526 | «цифра перед большим маркером» отсекает 数十万 / 千万 / 十万八千 вместе с идиомами | подчекер number_magnitude не сработал ни разу на 149 232 знаках исходника |
| 6 | checkers.go:391 | заглавная буква = «имя собственное» → токен отброшен | «Cultivation» в ФИНАЛЕ acceptance:3:0 невидим; маркеры утечки банка TM, BANK — тоже |
| 7 | checkers.go:398 | minLatinResidueLen = 3 |
гомоглиф «гy» (латинская y в слове «гу») невидим — 2 вхождения |
| 8 | dc-checkers.txt:65 | класс символов cheng_re не содержит 两, хотя cjk_numeral содержит (两=2) |
两成 в ряду 一成,两成,三成 (minirun ch10) не виден подчекеру процентов |
| 9 | mempostcheck.go:112-122 | принятое множество = базовый dst ∪ decl.forms; множественного числа в сиде нет |
7 ложных из 14: «первокамней», «смертных», «гу-тварей» |
| 10 | mempostcheck.go:61-80 | «src сработал» = подстрочное вхождение; однознаковый ключ 转 совпадает с частым глаголом |
6 ложных из 14 |
| 11 | runes.go:32-50 | TokenizeCyrillic рвёт слово на комбинирующем ударении U+0301 (оно не кириллица) |
«бо́льшим» → «бо» + «льшим» в финале acceptance:25:0; затрагивает и ёфикатор, и битые формы, и пост-чек |
| 12 | waverun.go:486 (охват, не баг) | draft уходит ТОЛЬКО в регресс-гард; остальные подчекеры видят лишь финал |
38 из 40 дефектов латиницы живут в черновиках и по конструкции вне охвата |
Пункты 1–4 и 6–8 — это правила, которые можно поменять. Пункты 9–10 код сам называет пробелом СИДА, а не кода; пункт 12 — сознательный контракт. Что из этого чинить и чинить ли — не моё решение.
Побочные наблюдения вне шести классов (не размечал, только фиксирую факт срабатывания на 91 юните):
dc6_register — 4 юнита, yo — 1 юнит («тёмно»/«темно» в rerun2-glm:1:0). Ёфикатор на корпусе
недоразмечен: пар типа «вошел/вошёл», «шепот/шёпот», «отчетливо/отчётливо» в тексте заметно больше
одной — это отдельный класс и отдельный пакет.
§5. Спорные строки — пачкой владельцу, я их не решал
21 item помечен disputable и в знаменатель метрик не входит. Все сводятся к четырём решениям.
Р1. 千万 как гипербола (3 item, класс K5b/K5a). 杀了千万人的性命 в проклятии-обвинении: три
арма дали «тысячи и тысячи людей» / «тысячи жизней», один — «миллионы жизней». Довод в пользу
гиперболы, которого нет в коде: тот же автор в главе 2 называет то же самое деяние
屠杀了数十万人 (~10⁵), то есть 10⁷ в главе 1 — риторика персонажа, а не число. Если владелец
признаёт гиперболу не-дефектом, precision DC2 остаётся 1.000; если признаёт дефектом — правило
недосчитывает 3 из 4 армов, и вопрос переходит в «чем отличать гиперболу от магнитуды офлайн».
Код прямо пишет, что офлайн не отличает (cheapgates.go:90-93).
Р2. Что считать дефектом в классе 成 (4 item + вся расходящаяся метрика). Рендер «сорок четыре
сотых» для 四成四 несёт ВЕРНОЕ значение (0.44) в форме дроби. Конвенция кода — форма и есть дефект;
читательская мерка — дефект только когда сломано значение. От выбора зависит precision 1.000 или
0.167. Отдельно: 4 item в minirun:10:0 — «доля» в одном абзаце обозначает и 分 (1%), и 成 (10%)
(«потеряло целую долю», «истощилась на целых три доли»); тут я не смог решить, что имел в виду
переводчик, а не что имел в виду код.
Р3. Сокращённая форма термина (2 item, K6). 蛊虫 при одобренном dst «гу-тварь» переведено просто
«гу» («отличительная гу клана Гуюэ»). Понятие передано и внутри юнита последовательно, но
утверждённой формы нет. Это дефект перевода, пробел полноты сида или норма — решать владельцу; от
ответа зависит, считать ли такие срабатывания пост-чека ложными.
Р4. Неоднозначная речь/мысль (9 item, K4). Строки, где по тексту нельзя решить, произнесено это вслух или подумано: «— Пятьсот лет жизни — словно сон» (один арм ставит тире, другой ёлочки на той же фразе); «— Хе, по срокам как раз он. — Старейшина ... пробормотал себе под нос: —» (бормочет, но при слушателе); «Пф...» — кто-то не сдержал смешка». Если владелец решит, что такие случаи принципиально неразрешимы офлайн, то потолок recall у правила смешения ниже 1.0 by construction, и это надо записать в контракт правила, а не гнаться за ним правками.
Полный список спорных с контекстом — в наборе на стенде: labels/labels/*.jsonl, фильтр
label == "disputable".
§6. Чего набор НЕ покрывает
Это главный раздел для того, кто будет пользоваться цифрами §4. Каждая цифра оттуда верна ТОЛЬКО внутри своих границ.
1. Одна книга, одна пара, один жанр. Всё — 蛊真人, zh→ru, сянься. Ни одного среза на другой паре или книге. Любое утверждение вида «чекер имеет precision X» — это «на этой книге».
2. Четыре класса физически малы, и метрика на них хрупкая. K1 = 15 items (4 уникальных вхождения
在 исходнике), K5b = 11, K5d = 5, K2 = 92. precision = 1.000 на шести положительных K1 — это
«не ошибся шесть раз», а не «работает». Один контрпример уронит цифру на 0.14. Доверительных
интервалов я не считал сознательно: при N=6 они шире самой цифры.
3. Recall меряется против МОЕЙ сети, а не против истины. Дефект, который не попал ни в одну из моих сетей, невидим и для разметки, и для метрики. Где сеть заведомо дырявая:
- K3: сплошь просмотрен только hapax-слой (1 999 типов). Битая форма, встретившаяся дважды, в набор не попала. Вторая независимая сеть (сосед по Левенштейну у частого слова, 117 попаданий) не добавила ни одного нового дефекта — но это проверка, а не доказательство.
- K4: буквенных строк размечено 80 из 5 088. Прямой речи вообще без маркера я в этой выборке не нашёл, но выборка — 1.6% слоя.
- K6: 260 пар из 1 257.
- K2/K5a/K5b/K5c/K5d/K1: сплошь, дырок нет.
4. Ветка K4a непроверяема на этом корпусе. Ноль строк с дефисом/короткой чертой/ASCII-кавычкой в начале на 6 863 строках. Её precision/recall не измерены НИКАК — ни хорошо, ни плохо.
5. Классов чекеров больше, чем шесть. Не размечены и не измерены: ёфикатор, транслит-междометия, DC6-регистр, эксцизия-покрытие, коллапс длины, весь санитайзер (преамбулы, markdown-заголовки, CJK-утечки), хотя markdown-заголовки (29 строк) и CJK-утечки (11 строк) в наборе видны как подклассы K4 и ждут своего пакета.
6. Черновики размечены, но боевыми гейтами не проверяются. 38 из 40 дефектов латиницы и почти вся
утечка инъекционного блока банка (⟦TM-BANK-v1⟧ с колонками name/title/term/place прямо в
тексте verify2) живут в ЧЕРНОВИКАХ. Строка «K2 детектор» в §4 меряет линейку, строка «боевой
охват» — то, что реально доезжает до вердикта. Это не одно и то же, и путать их нельзя.
7. Набор — не оракул качества перевода. Он отвечает ровно на вопрос «сработал ли чекер там, где человек видит дефект ЭТОГО класса». Он ничего не говорит о верности, гладкости, регистре и о том, что перевод вообще хороший.
8. Метрики K5a/K5b/K5c/K1 — на уровне ЮНИТА. Юнит с двумя вхождениями, где одно верно, а другое
нет, засчитывается как один положительный. Пер-вхожденческая разметка в наборе есть (labels/*.jsonl),
и по ней можно пересчитать иначе — но это будет ДРУГАЯ метрика, не сравнимая с этой.
9. Синтетики в наборе нет вообще. Ни одной строки я не дописывал. Там, где класса физически мало, в §3 стоит настоящее число, а не добитое до 200.
10. Приёмка воспроизводима, но зависит от стенда. Набор и харнесс — вне git (текст книги).
labels/README.md описывает, как ре-ранить; ключевая ловушка при повторе — снимать детерминированный
заголовок с финала, иначе номер главы читается как «появившееся число».
Итог одной таблицей
| линейка | вердикт по итогам первого честного замера |
|---|---|
| K1 DC1 时辰 | работает как описана: 6/6, ложных нет. Материала мало (15) |
| K5b DC2 | 1/1, ложных нет. Материала мало (11), 3 спорных упираются в вопрос о гиперболе |
| K2 латиница | ложных нет; recall 0.75 как детектор и 0.50 в боевом охвате; заглавные и гомоглифы вне класса |
| K3 битые формы | ложных нет; ловит 1 битую форму из 10 — один шаблон вместо класса |
| K4b смешение стилей | «8→0» подтверждено на мыслях: 99 из 100 ёлочных мыслей молча (1 ложняк). Но 41 реплика вслух из 46 не опознана |
| K4a неверный маркер | не измерено: на корпусе нет ни одного входа |
| K5a магнитуда 万 | ноль срабатываний на всём корпусе; предохранитель отсекает и идиомы, и все реальные магнитуды |
| K5c 成-процент | инвертировано на реальном материале: промолчал на единственном юните с 10×-ошибками, сработал на пяти с верными значениями |
| K5d дрейф чисел | 4 срабатывания, все безобидные; подлинного дрейфа в корпусе нет |
| K6 глоссарий | 1 подлинный промах против 14 ложных; на реально инъецированном подмножестве — 0 против 8 |
Заявление = команда. Приёмка: ре-ранить чекеры против набора по labels/README.md и сверить
metrics.json. Ожидаемый результат — побайтно те же цифры: и корпус, и харнесс, и чекеры детерминированы.