textmachine/docs/research/27-chapter-detection.md

35 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 27. Определение глав в загружаемых книгах: стандарты · практика · алгоритмы
**Оркестратор №15, 09.08.2026.** Веб-ресёрч по заказу владельца 09.08 (контекст: фактограф приёмки пака блокеров показал, что ingest-слой границ глав захардкожен под CJK-форму, выведенную из одной стенд-книги, — не-CJK txt молча становится одной главой, epub режется по spine с игнором оглавления, print-TOC порождает главы-огрызки). Три веб-агента: стандарты форматов · практика инструментов · алгоритмы; каждый факт с URL. **Статус: ФАКТУРА СОБРАНА; дизайн-решение владельцем НЕ принято — этот док не ратифицирует стройку.** Носители работ — строки бэклога (заводятся лендингом пака блокеров).
## §1. Ответы на вопросы владельца
1. **«Есть ли алгоритмы, умеющие парсить главы, или всё эвристика?»** Надёжного не-эвристического алгоритма для голого txt НЕ существует, и это доказано числами: Chapter Captor (EMNLP 2020, 9126 романов Gutenberg) — распознавание ЗАГОЛОВКОВ гибридом regex+BERT даёт F1=0.77, а предсказание границ БЕЗ заголовков (семантическая сегментация) — F1=0.453 [arxiv.org/abs/2011.04163]. Главы — авторская разметка, а не тематический сдвиг; TextTiling/C99/BERT-сегментация спроектированы под другое и как основа непригодны. Все зрелые инструменты (Calibre, chapterize, kaf-cli, AozoraEpub3) — каскад «статистика формата строк → языковые паттерны → структурные fallback'и → пороги правдоподобия».
2. **«У epub по стандарту оглавления нет?»** ЕСТЬ И ОБЯЗАТЕЛЬНО. EPUB 3: публикация «MUST contain an EPUB navigation document» с toc nav — иерархия ol/li/a с заголовками и ссылками файл#якорь [w3.org/TR/epub-33/#sec-nav-toc]. EPUB 2: обязательный NCX (navMap/navPoint, вложенность томов/глав, navLabel+ с xml:lang — единственный стандартный носитель МУЛЬТИЯЗЫЧНЫХ названий глав) [idpf.org/epub/20/spec/OPF_2.0.1_draft.htm]. FB2 (жив, критичен для ru-рынка): дерево глав = сама разметка body/section с рекурсией и title [github.com/gribuser/fb2]. То есть для epub/fb2 границы И НАЗВАНИЯ глав достаются из стандарта вообще без эвристик — наш текущий разрез «по spine с игнором nav» выбрасывает гарантированную спекой структуру.
3. **«Что с печатным оглавлением?»** Отдельное устоявшееся направление (ICDAR Book Structure Extraction 20092013): детект TOC-блока → парсинг записей → линковка в тело матчем заголовков. Print-TOC — не помеха, а бесплатный источник границ и КАНОНИЧЕСКИХ названий; TOC-гейты дёшевы (chapterize: кандидаты ближе 4 строк = блок оглавления).
## §2. Несущие факты по слоям
**EPUB (уточнения сверх §1):** резать по spine неверно ПО СТАНДАРТУ — якоря nav легально ведут внутрь файла (несколько глав в одном xhtml) и глава легально живёт в нескольких файлах; правильный алгоритм: поток = spine linear="yes", границы = якоря nav/NCX, спроецированные на поток. Порядку/полноте nav верить нельзя (с EPUB 3.3 порядок — SHOULD, полнота не требовалась никогда) — нормализация: сортировка целей по позиции в spine, непокрытые документы прикреплять к предыдущей главе. Служебные страницы фильтруются каскадом штатных сигналов: manifest properties (nav, cover-image) → spine linear="no" → landmarks (epub:type обязателен: cover/titlepage/toc/bodymatter) → epub:type/DPUB-ARIA в контенте (добровольный — сигнал, не гарантия). Однофайловый epub и битый nav → фолбэк на txt-эвристики.
**TXT — состояние искусства (Calibre как эталон):** (1) статистический детект формата абзацев (line histogram, доли пустых/отступных строк); (2) списки словесных паттернов — у Calibre только en+de в коде(!), CJK-миры держат свои инструменты: kaf-cli zh «第.{1,8}章» + том «第…[卷部]», AozoraEpub3 ja — богатый список 話/章/篇/部/節/幕/プロローグ/序章/終章/間章; (3) пороги правдоподобия: схема принимается только при min_chapters=ceil(wordcount/7000) ≤ hits < 150, заголовок 35 симв. (kaf-cli), 3 глав (chapterize); (4) сцены «* * *» отдельный класс, не главы; (5) конвенция Gutenberg: 4 пустые строки перед главой пустострочные раны как вторичный сигнал [pgdp.net DP Formatting Guidelines; W3C WCAG T3]. **Готовой мультиязычной библиотеки паттернов-как-данных не существует** собирать свою в langpack из проверенных списков этих инструментов (сид: kaf-cli+AozoraEpub3+Calibre+chapterize; regex-набор Chapter Captor сид для en); прецедент конфиг-схемы ParserConfig oomol-lab (chapter_patterns/volume_patterns/section_patterns).
**Иерархия:** том/часть над главой отдельный уровень паттернов у всех зрелых инструментов; плоский список ломается на «Том 2, Глава 1» (нумерация перезапускается) в модель структуры нужен уровень volume/part, иначе якоря и банк едут на многотомниках.
**LLM-место:** полная LLM-разметка книги (LumberChunker, EMNLP 2024) дороже/медленнее по признанию авторов, цена линейна от объёма, недетерминизм ломает снапшоты ОТВЕРГНУТЬ как основной путь. Доказанный в проде паттерн (LILAC FSE'24, лог-парсинг): **LLM один раз ВЫВОДИТ шаблон, дальше режет детерминированный код** шаблонов на порядки меньше экземпляров; перенос на книги естественен (один дешёвый вызов на семплах строк-кандидатов regex книги в снапшот перепрогоны $0 и воспроизводимы). Для книг такой паттерн не опубликован будем первыми.
## §3. Рекомендуемый класс решения (предложение оркестратора, НЕ ратифицировано)
Каскад по цене источника, LLM никогда не режет сама:
1. **Структурные источники ($0, без эвристик):** epub nav NCX fb2 section/title; скрейп-epub (WebToEpub/FanFicFare и т.п.) границы точны по построению, доверять. Названия глав на языке оригинала из тех же источников (закрывает и вопрос heading/К-3).
2. **TXT:** детект print-TOC блока (плотность кандидатов) использовать его как источник имён и сверки; языко-НЕзависимый статистический скорер строки-заголовка (короткая · изолирована пустыми строками · монотонная нумерация · повторяющийся префикс-шаблон · равномерность по файлу · коридор hits/wordcount) + словари маркеров per-language как ДАННЫЕ langpack; несколько конкурирующих схем-кандидатов, выбор по скору.
3. **Fallback:** один дешёвый LLM-вызов выводит шаблон книги, шаблон артефакт снапшота (LILAC-паттерн).
4. **Всегда:** громкие WARN вместо молчаливой деградации 1 глава на 20 МБ», «скачок нумерации», «глава-огрызок»).
**Требование к дизайну (урок владельца 09.08):** модель «что бывает заголовком» выводится из КОРПУСА разнообразных книг (языки·авторы·форматы), не из стенд-книги; приёмка детекта прогоном по корпусу с замером точности границ (полигон). Схема данных проектируется от общего кейса, а не наращиванием заплаток к CJK-форме (история вопроса: пак-2 завёл CJK-сплит до канона общности, фазы общности вынесли маркеры в данные, но форма шаблона осталась в Go и слой выпал из карты общности D39.64 честно не включал его в критерий).
**Цена итераций:** правки детекта двигают нарезку снапшот реснапшот-класс на прогнанных книгах. Проектировать один раз от корпуса и строить ДО открытия интейка книг пользователям; сейчас единственная платная книга стенд, окно дешёвое.
## §4. Что НЕ брать
Тематическая сегментация как основа (F1=0.453) максимум tie-breaker · полная LLM-разметка (цена/недетерминизм) · вебновелльные скрейперы как образец детекта (у них глава = страница сайта, задачи нет) · универсальный код без языковых данных (ни один инструмент так не решил).