Chunking
chunking — разбиение документов на фрагменты для RAG
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 помогает находить полезные части документа, а не просто уменьшать текст. Удачный фрагмент сохраняет нужный смысл, своё место в источнике и связь с важными соседними условиями. Качество видно по реальным вопросам и ответам, а не по аккуратному числу токенов.