Chunking
chunking — разбиение документов на фрагменты для RAG
Chunking — разбиение длинных документов на маленькие фрагменты (chunks) для индексации в RAG. От выбора размера и стратегии разбиения зависит качество поиска. Слишком маленькие chunks теряют контекст, слишком большие — теряют точность векторного поиска. Стратегии: фиксированные токены, по разделителям (абзац, заголовок), recursive, semantic. Перекрытие соседних фрагментов помогает не терять смысл на границах, но его размер лучше подбирать по структуре текста и результатам поиска между chunk'ами.
Коротко
Коротко. Chunking — это разбиение длинных документов на фрагменты для индексации в RAG. Векторная база хранит каждый chunk отдельно и при запросе ищет близкие. Маленькие фрагменты точнее указывают место, но легче теряют контекст; большие сохраняют связность, но могут смешивать несколько тем. Размер и overlap выбирают по документам и тестовым вопросам.
Что это такое
Команда строит RAG-ассистента поверх корпоративной wiki: 5000 страниц документации, 3 миллиона слов. Хочет, чтобы AI отвечал на вопросы пользователей со ссылками на конкретные страницы.
Без chunking: загнать все 5000 страниц в context window LLM. Плюс это очень дорого.
С chunking:
- Каждая страница режется на chunks по 500 токенов с overlap 50.
- Каждый chunk → вектор через embedding-модель.
- Векторы хранятся в БД.
- При запросе пользователя: его вопрос → вектор → поиск похожих → топ-5 chunks подгружаются как контекст в LLM.
- LLM отвечает по этим 5 chunk'ам (~2500 токенов вместо 4 миллионов).
Это и есть chunking — главный шаг подготовки данных для RAG. Качество всего pipeline сильно зависит от того, как документы разбиты.
- Fixed-size token splitting — самый простой, по N токенов.
- Recursive splitting — пробует разные разделители (абзацы → предложения → слова).
- Markdown-aware — режет по заголовкам, оставляет иерархию.
- Semantic chunking — определяет «смысловые границы» через embedding-similarity.
- Late chunking — индексирует длинные документы целиком, потом ретриверит фрагменты на лету.
Как это работает
Базовый алгоритм fixed-size splitting:
def chunk_text(text, chunk_size=500, overlap=50):
tokens = tokenize(text)
chunks = []
start = 0
while start < len(tokens):
end = start + chunk_size
chunks.append(tokens[start:end])
start = end - overlap # overlap для контекста
return chunks
chunk_size— максимальный объём фрагмента. Меньше — точнее место совпадения, но меньше соседнего контекста. Больше — наоборот.chunk_overlap— пересечение соседних chunk'ов. Оно помогает сохранить мысль на границе, но увеличивает индекс и число почти одинаковых результатов.separators— список разделителей (для recursive splitting):["\n\n", "\n", ". ", " ", ""]. Пробует по очереди.
Особый случай — markdown / code splitting. Для технической документации:
- Markdown — режут по
## заголовкам, сохраняя heading-цепочку в metadata каждого chunk'а. - Code — режут по функциям/классам через AST-парсер.
Это значительно улучшает качество поиска — chunk «помнит», в каком разделе он находится.
Пример на практике
Разработчик строит ChatGPT-style ассистента для документации Stripe API. 200 000 слов в Markdown.
Попытка 1: fixed-size, chunk_size=500, overlap=0.
from langchain.text_splitter import TokenTextSplitter
splitter = TokenTextSplitter(chunk_size=500, chunk_overlap=0)
chunks = splitter.split_documents(docs)
# 487 chunks
Тест: «Как принимать платежи в евро?». Топ-1 chunk — середина какого-то абзаца, теряется контекст «в каком разделе мы». Качество ответа среднее.
Попытка 2: Markdown-splitting.
from langchain.text_splitter import MarkdownHeaderTextSplitter
splitter = MarkdownHeaderTextSplitter(headers_to_split_on=[
("#", "h1"), ("##", "h2"), ("###", "h3")
])
chunks = splitter.split_text(md)
# 312 chunks с metadata.h1/h2/h3
Тот же тест. Топ-1 chunk — секция «Currency Support → EUR». Metadata показывает breadcrumb «Payments → Currency → EUR». LLM знает, в каком контексте отвечает. Качество резко лучше.
Попытка 3: recursive + semantic enhancement.
from langchain.text_splitter import RecursiveCharacterTextSplitter
splitter = RecursiveCharacterTextSplitter(
chunk_size=500, chunk_overlap=80,
separators=["\n## ", "\n### ", "\n\n", "\n", ". ", " "]
)
Для смешанных документов семантическое разбиение может быть полезно, но требует больше вычислений и отдельной проверки границ.
В ComfyUI с RAG-нодами тоже есть chunking-параметры. Стандартные настройки обычно работают для большинства случаев, но для специфичных корпусов стоит подбирать.
С чем часто путают
- Chunking и Tokenization — Tokenization режет на минимальные единицы (subwords). Chunking — на смысловые блоки (десятки-сотни токенов).
- Chunk Size и Context Window — Chunk Size — размер одного индексируемого фрагмента. Context Window — общий бюджет LLM на всё (включая retrieved chunks).
- Fixed-size и Recursive — Fixed-size режет каждые N токенов слепо. Recursive пробует естественные разделители.
- Semantic Chunking и Semantic Search — Semantic Chunking определяет границы chunk'ов по смыслу. Semantic Search ищет в готовых chunk'ах.
- Chunking и Reranking — Chunking — подготовка. Reranking — улучшение результатов после первого retrieval.
Частые ошибки и заблуждения
- «Большие chunks всегда лучше — больше контекста». Не всегда. Длинный фрагмент может смешать несколько тем и получить слишком общий embedding.
- «Overlap — только лишнее дублирование». Пересечение действительно увеличивает индекс, но иногда сохраняет ответ, оказавшийся на границе. Его пользу измеряют, а не предполагают заранее.
- «Markdown-headers сохраняют сами себя в chunk'е». В большинстве библиотек headers нужно явно сохранять в metadata. Без этого теряется навигация.
- «Один chunk_size подходит всем документам». FAQ, договор, исходный код и расшифровка разговора устроены по-разному, поэтому им могут понадобиться разные правила разбиения.
- «Semantic chunking всегда лучше». Не всегда. На структурированных markdown/code recursive может быть качественнее (быстрее и точнее).
Связанные термины
- RAG — главный сценарий применения chunking.
- Embedding — что делается с каждым chunk'ом.
- Vector Database — где хранятся векторы chunk'ов.
- Semantic Search — что использует chunked-данные.
- Reranking — улучшение результатов после первого retrieval.
- Context Window — общий бюджет LLM, ограничивающий, сколько chunks можно подгрузить.
- LangChain / LlamaIndex — главные библиотеки с chunking-функциями.
Частые вопросы
Какой chunk_size оптимальный? Стартовая точка: 500 токенов, overlap 50. Для коротких текстов — 200/30. Для длинных technical docs — 800/100. Тестируйте на ваших данных.
Как измерять качество chunking?
Через retrieval recall@k: для тестовых запросов проверяют, попадает ли нужный chunk в первые k результатов. Целевой уровень задают по риску задачи и сравнивают с простой базовой стратегией.
Зачем нужен overlap? Чтобы важная информация не потерялась на границе двух chunk'ов. Overlap позволяет связать.
Что такое late chunking? Один из подходов сначала кодирует документ с более широким контекстом, а затем получает представления отдельных фрагментов. Это может сохранить связь с соседними разделами, но требует совместимой модели и проверки retrieval на своём корпусе.
Можно ли вообще без chunking? Если документ помещается в окно модели, его можно передать целиком. Это не всегда лучший маршрут: большой вход дороже, медленнее и может размывать внимание. Chunking с retrieval полезен, когда нужны точные фрагменты, цитаты и частые обновления; для короткого связного документа полный контекст иногда проще.
Какая библиотека для chunking?
LangChain (text_splitter), LlamaIndex (NodeParser), Haystack (PreProcessor). Все имеют богатый набор стратегий.
Главное
Chunking разбивает длинные документы на фрагменты для поиска в RAG. Качество зависит от границ, размера, метаданных и того, как найденные куски собираются обратно в контекст. Для структурированного текста удобно сохранять заголовки и разделы, для смешанного корпуса может помочь семантическое разбиение. Универсального preset нет: варианты сравнивают по реальным вопросам, полноте поиска и качеству итоговых цитат.