Chunking

chunking — разбиение документов на фрагменты для RAG

Раздел
Языковые модели
Обновлено
05.09.26

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

Коротко

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

Что это такое

Представим внутреннюю библиотеку инструкций компании. Сотрудник спрашивает, как оформить возврат. Ему нужен конкретный раздел, а не все документы сразу.

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

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

Распространённые подходы:

  • Фиксированный размер. Текст делится на заданное число символов или токенов.
  • Рекурсивное разбиение. Сначала проверяются крупные границы, например абзацы, затем более мелкие, если часть не помещается.
  • По структуре. Используются заголовки, разделы, вопросы FAQ, функции в коде.
  • Семантическое разбиение. Границы выбираются по оценке смены темы.
  • Late chunking. Представления токенов вычисляются с широким контекстом, а затем объединяются в векторы отдельных фрагментов.

Последний способ описан в работе о late chunking. Это не поиск фрагментов «на лету» внутри единственного вектора всего документа.

Как это работает

Простейший пример разбиения уже готовой последовательности токенов:

def split_tokens(tokens, chunk_size=500, overlap=50):
    if chunk_size <= 0 or not 0 <= overlap < chunk_size:
        raise ValueError("Нужно: chunk_size > 0 и 0 <= overlap < chunk_size")

    chunks = []
    start = 0
    while start < len(tokens):
        end = min(start + chunk_size, len(tokens))
        chunks.append(tokens[start:end])
        if end == len(tokens):
            break
        start = end - overlap
    return chunks

Функция принимает токены, а не обычную строку. Токенизация, восстановление текста и метаданные здесь не показаны. Числа 500 и 50 — пример параметров, не рекомендуемая настройка для любого документа.

  • Размер ограничивает объём одного фрагмента.
  • Перекрытие (overlap) повторяет часть соседнего фрагмента, чтобы не потерять связь на границе.
  • Разделители помогают учитывать абзацы и предложения.
  • Метаданные сохраняют документ, раздел, версию и место, откуда взят текст.

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

Пример на практике

Допустим, в инструкции есть раздел «Возврат», а под ним подразделы «Условия» и «Исключения». Простое деление каждые несколько сотен символов может оторвать исключение от заголовка.

Вариант с разбором Markdown сохраняет структуру. Учебный пример для установленного пакета langchain-text-splitters:

from langchain_text_splitters import MarkdownHeaderTextSplitter

md = """# Возврат
## Условия
Здесь описаны общие условия возврата.
## Исключения
Здесь перечислены случаи, для которых действуют другие условия.
"""

splitter = MarkdownHeaderTextSplitter(
    headers_to_split_on=[("#", "раздел"), ("##", "подраздел")],
    strip_headers=False,
)

for part in splitter.split_text(md):
    print(part.metadata)
    print(part.page_content)

Это пример механики, не реальные правила возврата. Он показывает текст и метаданные фрагментов, но ещё не строит индекс и не измеряет качество поиска. Поведение параметров описано в документации LangChain.

Если раздел слишком длинный, его можно разбить дополнительно. При этом важно проверить единицы: например, RecursiveCharacterTextSplitter при стандартном подсчёте ограничивает символы, а не токены. Название chunk_size само по себе не сообщает единицу измерения.

В схеме с RAG-нодами ComfyUI проверяются те же вещи: как узел считает размер, сохраняет ли заголовок и что именно передаёт дальше.

С чем часто путают

  • Chunking и токенизация. Токенизация превращает текст в единицы для модели; chunking группирует текст в более крупные части.
  • Размер фрагмента и контекстное окно. В окно входят не только найденные части, но и инструкции, вопрос, история и другие данные.
  • Рекурсивное и семантическое разбиение. Первое использует заданные разделители; второе оценивает содержание. Деление по абзацам само по себе не является семантической оценкой.
  • Chunking и поиск по смыслу. Одно готовит фрагменты, другое ищет среди них.
  • Chunking и reranking. Повторное ранжирование оценивает уже найденных кандидатов.

Частые ошибки и заблуждения

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

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

«Заголовок в метаданных модель увидит сама». Нет: система должна включить нужные метаданные в поиск или в контекст ответа. Простое хранение отдельного поля не меняет embedding автоматически.

«Один размер подходит всем». Короткий FAQ, договор, исходный код и расшифровка разговора требуют разного обращения с границами.

«Семантическое разбиение всегда лучше». Оно добавляет вычисления и тоже ошибается. Для хорошо структурированной инструкции может хватить заголовков и абзацев.

Связанные термины

  • RAG — подготовка ответа по найденным материалам.
  • Embedding — векторное представление текста.
  • Vector Database — хранение и поиск векторов.
  • Semantic Search — поиск по смысловой близости.
  • Reranking — дополнительная оценка найденных фрагментов.
  • Context Window — предел контекста модели.
  • LangChain / LlamaIndex — библиотеки, в которых есть инструменты работы с документами.

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

Какой размер выбрать? Можно начать с естественных разделов и ограничения входа embedding-модели. Затем сравнить несколько размеров на вопросах к своему корпусу. Важно записать и число, и единицу: символы, слова или токены.

Как измерить качество? Отметить, какие части документа нужны для ответа, и проверить, сколько из них попало в первые k результатов. Это оценка полноты поиска, recall@k. Отдельно проверяют итоговый ответ: найденный фрагмент ещё нужно верно использовать.

Зачем перекрытие? Чтобы сохранить часть контекста на границе. Его польза зависит от документа; слишком большое перекрытие создаёт почти одинаковые результаты и увеличивает индекс.

Что такое late chunking? Подход, в котором векторы фрагментов получают после обработки более длинного текста embedding-моделью. Так представление части может учитывать соседние предложения. Для этого нужна совместимая реализация, а не просто увеличение размера обычного чанка.

Можно ли без разбиения? Да, короткий документ можно передать целиком. Для большой коллекции также возможен поиск на уровне документов. Выбор зависит от объёма, стоимости и того, какие источники должны попасть в ответ.

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

Главное

Chunking помогает находить полезные части документа, а не просто уменьшать текст. Удачный фрагмент сохраняет нужный смысл, своё место в источнике и связь с важными соседними условиями. Качество видно по реальным вопросам и ответам, а не по аккуратному числу токенов.