Чанкинг

13

Чанкинг — разбиение исходных документов на фрагменты, каждый из которых превращается в вектор и попадает в поисковый индекс. Поиск работает не по документу целиком, а по этим фрагментам, и в ответ модели передаются именно они.

Это не техническая мелочь: в проектах, где «система отвечает неточно», причина чаще в нарезке, чем в модели.

Почему нельзя просто резать по числу символов

Самый простой способ — резать текст каждые N символов — ломает документ в произвольных местах. Условие договора разрывается посередине, и в ответ попадает половина условия — формально релевантная, фактически вводящая в заблуждение. Таблица теряет заголовки строк. Пункт регламента отделяется от исключения к нему.

Механизм потери точности — усреднение смысла. Вектор фрагмента — сжатое представление всего его содержимого. Фрагмент, разрезанный посередине, захватывает хвост одной мысли и начало другой: его вектор «в среднем» относится к обеим сразу и точно не находится ни по одной. Чем произвольнее граница, тем больше таких размытых векторов в индексе — об эмбеддингах подробнее отдельная статья.

Ошибка при этом незаметна: система отвечает уверенно, ссылается на документ, и неверность обнаруживается только при сверке с оригиналом.

Способы нарезки

По структуре документа. Основной способ для регламентов, инструкций и справочных материалов: границы фрагментов совпадают с границами пунктов или подзаголовков. Фрагмент получается смысловой единицей, а не отрезком текста.

Рекурсивная по разделителям. Компромисс: сначала пробуют резать по абзацам, если фрагмент велик — по предложениям, и только в крайнем случае по символам. Разумное умолчание, когда структура неизвестна заранее.

С перекрытием. Соседние фрагменты частично повторяют друг друга — обычно на 10–20% длины. Снижает риск разрыва смысла на границе, но увеличивает объём индекса и приводит к выдаче почти одинаковых фрагментов.

По смыслу. Граница ставится там, где меняется тема. Дороже при индексации и сложнее в отладке, оправдано на слабо структурированных текстах.

С контекстуализацией. К каждому фрагменту добавляют шапку: название документа, путь по разделам, иногда сгенерированное пояснение, о чём этот кусок. Фрагмент «в этом случае срок составляет 5 дней» без шапки не находится и не понимается; с шапкой «Регламент обработки обращений, раздел 3, гарантийный ремонт» — и находится, и читается. Плата — рост объёма индекса и токенов в контексте, поэтому шапку держат в одну-две строки.

Мелкий фрагмент для поиска, крупный для генерации. Индексируют мелкие куски, а модели передают не сам найденный кусок, а окружающий его пункт или раздел целиком. Поиск получается точным — мелкий вектор однозначен, — а генерация получает законченный контекст. Требует хранить связь «фрагмент — родительский блок» в метаданных.

Что делать с разными типами документов

Регламенты и инструкции — по пунктам, с сохранением номера пункта в метаданных фрагмента: он понадобится, чтобы дать ссылку в ответе.

Таблицы — не резать. Таблица без заголовков превращается в набор чисел. Если таблица велика, её сериализуют построчно, повторяя заголовки в каждой строке.

Переписка и обращения — по сообщениям, а не по диалогу целиком; при этом пара «вопрос — ответ» держится вместе, иначе ответ без своего вопроса теряет смысл.

Презентации — по слайдам, с подписью раздела.

Код и конфигурации — по функциям и блокам, а не по строкам.

Сканы и PDF со сложной вёрсткой. Сначала извлечение текста с сохранением структуры — колонок, заголовков, таблиц — и только потом нарезка; колонтитулы и номера страниц вычищаются до нарезки, иначе они попадут в половину фрагментов. Проблемы этого этапа разобраны в статье про извлечение данных из документов.

Размер фрагмента

Универсального значения нет, и цифры из чужих руководств переносить не стоит. Ориентир такой: фрагмент должен содержать законченную мысль и при этом не тащить за собой посторонний текст.

Слишком мелкие фрагменты теряют контекст — «в этом случае срок составляет 5 дней» без указания, о каком случае речь. Слишком крупные размывают вектор: один фрагмент про всё сразу плохо находится по конкретному вопросу и занимает место в контексте модели.

Типичные рабочие диапазоны. Для связного русского текста это обычно 200–500 слов, или порядка 300–700 токенов; для структурированных документов размер задаёт не число, а единица — пункт, слайд, сообщение. Выходить за 1000 токенов на фрагмент стоит только для «мелкий для поиска — крупный для генерации», где большой фрагмент в индекс не идёт.

Проверяется это не рассуждением, а замером: берётся набор реальных вопросов с известными правильными ответами, и сравнивается доля случаев, когда нужный фрагмент оказался в выдаче.

Методика подбора замером. Возьмите сетку из двух-трёх значений — например 256, 512 и 1024 токена с перекрытием 10–15% — и прогоните один и тот же проверочный набор для каждого варианта. Сравнивают recall при рабочей глубине выдачи; победителя проверяют ещё и по стоимости: крупнее фрагмент — дороже контекст каждого вызова генерации, мельче — больше векторов в индексе и больше кандидатов, из которых собирается контекст. Прирост меньше пары процентных пунктов — внутри шума, дальше можно не гонять сетку. Как считать метрики — в статье про метрики качества RAG.

Оптимум зависит от пары «документы плюс эмбеддинг-модель». Смена модели эмбеддинга смещает и оптимальный размер фрагмента, поэтому после переиндексации на новую модель замер размера стоит повторить.

Считайте бюджет контекста генератора. Размер фрагмента и число фрагментов в выдаче связаны: их произведение плюс вопрос и промпт должны укладываться в контекстное окно модели с запасом на ответ. Фрагменты по 800 токенов при выдаче в десять штук — уже впритык для средних моделей; правило то же: не подбирать по максимуму окна, а мерить, после какого объёма контекста качество ответа перестаёт расти.

Метаданные фрагмента

Вместе с текстом стоит хранить источник, раздел, дату и права доступа. Это даёт три вещи: возможность сослаться на источник в ответе, фильтрацию по актуальности и разграничение доступа — чтобы сотрудник не получил фрагмент документа, который ему не положен.

Дата и версия — часть правды ответа. Если в базе лежат две редакции регламента, метаданные позволяют отдавать фрагменты только действующей редакции; без этого поиск честно возвращает обе, и генератор собирает ответ из устаревшей и новой вперемешку.

Права доступа проверяются до выдачи. Фильтрация по правам должна происходить при поиске, а не после ответа модели; как это устроено — в статье про разграничение доступа.

Типичные ошибки

Одна стратегия нарезки на все типы документов сразу.

Потеря заголовков: фрагмент не содержит указания, из какого он документа и раздела.

Избыточное перекрытие — выдача забивается почти одинаковыми фрагментами, и до модели не доходят другие релевантные.

Отсутствие переиндексации после изменения документов: система отвечает по устаревшей редакции.

Подбор параметров без замера, по ощущению «стало лучше».

Нарезка неочищенного текста: колонтитулы, номера страниц и служебные пометки остаются в фрагментах и попадают в векторы — индекс зашумляется служебным текстом.

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

Частые вопросы

Какой размер фрагмента выбрать?

Универсального значения нет — он зависит от структуры документов. Ориентир: фрагмент должен содержать законченную мысль и не тащить посторонний текст. Проверяется замером на наборе реальных вопросов с известными ответами, а не выбором числа из руководства.

Нужно ли перекрытие между фрагментами?

Оно помогает, когда документ плохо структурирован и смысл рвётся на границах. Обычно берут 10–20% длины. При хорошо структурированных документах, нарезанных по пунктам, перекрытие чаще вредит: выдача заполняется почти одинаковыми фрагментами.

Как быть с таблицами?

Не резать. Таблица, разорванная посередине, теряет заголовки и превращается в набор чисел без смысла. Крупные таблицы сериализуют построчно, повторяя заголовки столбцов в каждой строке.

Как понять, что проблема именно в нарезке?

Проверить, попадает ли нужный фрагмент в выдачу поиска вообще. Если правильный текст в кандидатах есть, но не на первых местах — вопрос к ранжированию и реранкингу. Если его нет в выдаче совсем — виновата нарезка или индексация.

Зависит ли размер фрагмента от модели эмбеддинга?

Да. У каждой модели свой предел длины входа и своё «сжатие» смысла, поэтому оптимальный размер фрагмента у разных моделей отличается. Практическое следствие: при смене эмбеддинг-модели вместе с переиндексацией пересматривают и размер — старый оптимум на новой модели не гарантируется.

Что дороже — мелкая или крупная нарезка?

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

Нужно ли переиндексировать базу при смене параметров нарезки?

Да, полностью: изменить размер фрагмента «на лету» нельзя — старые фрагменты уже лежат в индексе со своими векторами. Поэтому параметры нарезки держат в конфигурации индексации вместе с версией эмбеддинг-модели, а переиндексацию планируют как обычную операцию с оценкой времени и объёма.

Что почитать по теме