AI-инжиниринг

Chunking: как правильно нарезать документы для RAG

Цели урока

  • Понять влияние chunking на качество RAG и выбрать стратегию
  • Реализовать fixed-size и recursive character splitting
  • Освоить semantic chunking на основе embeddings
  • Применять document-aware chunking для Markdown, кода и FAQ
  • Настроить evaluation pipeline для сравнения chunking стратегий

Предварительные знания

  • RAG pipeline, embeddings, vector search
  • RAG Fundamentals

Chunking - самое скучное название для самого важного решения в RAG. Размер chunk определяет precision, recall и стоимость индексации. Ошибка в chunk size = вся база переиндексируется заново. Два идентичных RAG pipeline, одна LLM, один pgvector. Первый - 52% accuracy, второй - 84%. Единственная разница: chunking strategy. И это бесплатная оптимизация.

  • LangChain RecursiveCharacterTextSplitter - самый используемый chunker в production (100K+ проектов): разбивает по \n\n, \n, точкам - иерархически
  • Pinecone: semantic chunking даёт +30% retrieval precision для enterprise RAG с длинными разнородными документами
  • GitHub Copilot нарезает код по функциям и классам - целыми смысловыми единицами, не по символам
  • RAPTOR (Stanford, 2024): иерархическое chunking - сначала мелкие chunks, потом суммаризируются в крупные - лучший recall на длинных документах

Chunking появился вместе с RAG

Разбиение текста на мелкие фрагменты для поиска - старая идея информационного retrieval, но chunking как самостоятельная практика вырос из эпохи RAG после 2020 года, когда retrieval начали пристыковывать к генеративным моделям. Инструменты, сделавшие это рутиной, появились в конце 2022-го: Harrison Chase выпустил **LangChain** в октябре 2022 года, и его `RecursiveCharacterTextSplitter` стал де-факто дефолтом - разбивает по границам параграфов, строк и предложений по порядку. Примерно тогда же Jerry Liu выпустил **GPT Index**, позже переименованный в **LlamaIndex** (ноябрь 2022), который популяризировал node-based и document-aware разбиение. Semantic chunking - резка там, где падает similarity embedding-ов - и иерархические методы вроде RAPTOR (Stanford, 2024) пришли позже, когда команды осознали, что границы чанков незаметно ограничивают качество поиска.

Почему chunking определяет качество RAG

Chunking - самое скучное название для самого важного решения в RAG. Размер chunk определяет precision, recall и стоимость индексации. Ошибка в chunk size = вся база переиндексируется заново.

Embedding model принимает текст и возвращает один вектор. Вся книга в одном chunk - вектор усредняется по всем темам и теряет смысл. Одно предложение - контекст испаряется. Задача chunking: найти **правильный размер фрагмента**, который embedding model может представить точно.

text-embedding-3-small (`0.02/1M` токенов) оптимален для 256-512 токенов. Дай ему 1500 токенов - качество вектора деградирует. Вот почему chunk size - инженерное решение, а не настройка по умолчанию.

Chunk sizeProsCons
Маленький (100-200 tokens)Точный retrieval, каждый chunk = одна мысльПотеря контекста, фрагмент вырван из окружения
Средний (300-600 tokens)Баланс точности и контекстаМожет разрезать мысль посередине
Большой (800-1500 tokens)Полный контекст, самодостаточный фрагментШум - chunk содержит много нерелевантного текста

Эмпирическое правило: chunk size = 1/5 context window модели, но не больше 1000 tokens. Для GPT-4o (128K context) это 512-1000 tokens. Для моделей с 4K context - 200-400 tokens.

Три фактора, определяющие оптимальный chunk size:

  1. **Тип контента** - код требует больших chunks (функция целиком), FAQ - маленьких (один вопрос-ответ)
  2. **Embedding model** - text-embedding-3-small оптимален для 256-512 tokens, большие chunks деградируют
  3. **Тип запросов** - конкретные вопросы → маленькие chunks, аналитические → большие

Chunk size 100 tokens для технической документации. Пользователь спрашивает 'как настроить Redis кеширование'. Наиболее вероятная проблема?

Fixed-size и Recursive Character Splitting

Fixed-size Chunking

Простейший подход: нарезать текст на фрагменты фиксированного размера с overlap (перекрытием). 512 токенов, 10-20% overlap - стандартные стартовые параметры. Overlap нужен, чтобы не терять смысл на стыке chunks.

Fixed-size chunking режет предложения посередине. "Сервер работает на порту" | "3000 и принимает WebSocket соединения" - два бессмысленных chunk. Для production нужен более умный подход.

Recursive Character Splitting

Стандарт индустрии - LangChain RecursiveCharacterTextSplitter, используется в 100K+ production проектов. Идея: сначала разделить по крупным boundary (\n\n - граница параграфа), если chunk слишком большой - по (\n), потом по предложениям (. ), потом по словам. Каждый следующий уровень - менее чистый разрез, но всё лучше, чем рубить посередине фразы.

Рекомендуемые параметры для начала: **chunkSize = 512**, **overlap = 50-100 tokens** (10-20% от chunk size). Для кода - overlap по целым функциям, а не по символам.

Recursive character splitting пробует разделители в порядке: \n\n → \n → . → пробел. Зачем такая иерархия?

Semantic Chunking: разбиение по смыслу

Recursive splitting честно признаётся: он не понимает смысл. Просто уважает синтаксис. Но параграф может содержать два разных topic - и наоборот, два параграфа могут быть об одном. **Semantic chunking** определяет границы там, где меняется смысл, а не там, где стоит перенос строки.

Алгоритм: разбить текст на предложения, вычислить embedding каждого через text-embedding-3-small, найти точки резкого падения cosine similarity - там тема переключается. Это и есть смысловые границы для нового chunk.

МетодСтоимостьКачество boundariesКогда применять
Fixed-sizeБесплатноПлохое - режет посерединеПрототипы, очень однородные тексты
RecursiveБесплатноХорошее - по синтаксическим границамProduction default, 80% случаев
Semantic0.01-0.1 на документ (embedding calls)Отличное - по смысловым границамДлинные тексты с переключением тем

Semantic chunking требует embedding call для каждого предложения. Документ на 1000 предложений = 1000 embedding calls при `0.02/1M` tokens. Для больших корпусов - batch processing и кеширование обязательны.

Semantic chunking определяет границы chunks по...

Document-aware Chunking и Metadata Enrichment

Документы - не plain text. Markdown имеет заголовки, код имеет функции, HTML имеет секции. Recursive splitter это не видит - он режет по символам. **Document-aware chunking** читает структуру документа и уважает её.

GitHub Copilot не режет исходный код по 512 символов. Он нарезает по функциям и классам - целыми смысловыми единицами. Notion AI использует блочную структуру документа как естественные границы. Это и есть document-aware подход.

Markdown-aware Chunking

Metadata Enrichment

Chunk без metadata - как книга без оглавления. Embedding фрагмента "Каждый сотрудник имеет право на 28 дней" будет далёк от запроса "политика отпусков". Добавить перед embedding "Документ: HR Policy, Раздел: Отпуска" - и вектор становится точным. Бесплатная оптимизация, которую пропускают в 90% проектов.

**Prepending metadata** в текст перед embedding - проверенный приём. Embedding "Документ: HR Policy, Раздел: Отпуска. Каждый сотрудник имеет..." будет релевантнее для запроса "политика отпусков", чем embedding голого текста.

Зачем добавлять metadata (заголовок документа, раздел) в текст chunk ПЕРЕД embedding?

Tuning и Evaluation: как подобрать оптимальную стратегию

Идеального chunk size не существует. Оптимальная стратегия зависит от данных, запросов и модели. Единственный способ найти оптимум - **эмпирическое тестирование** с метриками.

A/B-тестирование chunking стратегий

Рекомендации по типам контента

Тип контентаСтратегияChunk sizeOverlap
Техническая документацияMarkdown-aware500-800 tokens50-100 tokens
FAQ / Knowledge BaseПо вопросу-ответу (каждый Q&A = chunk)VariesНет
Исходный кодПо функциям / классамЦелая функцияСигнатура + docstring
Юридические документыПо параграфам + semantic300-500 tokens100 tokens
Чат-логи / тикетыПо сообщению или тредуЦелый тредНет
Научные статьиПо секциям (Abstract, Methods...)600-1000 tokens100 tokens

Overlap: сколько нужно?

Overlap решает проблему потери информации на границах chunks. Правило: **10-20% от chunk size**. Слишком большой overlap создаёт дублирование (одна мысль в трёх chunks), слишком маленький - теряет контекст.

Golden dataset для evaluation chunking - 50-100 вопросов с правильными ответами и ссылками на конкретные фрагменты документации. Создание golden dataset - инвестиция, которая окупается при каждой итерации улучшения RAG.

Существует универсально лучший размер chunk - например, 512 токенов с 64 overlap. Эти числа можно взять из туториала и больше не трогать.

Оптимальный chunk size зависит от типа контента, типа запросов и embedding-модели. Цифры 512/64 - это разумная стартовая точка, а не финальный ответ. Замер на golden dataset из 50-100 пар вопрос-ответ обычно сдвигает оптимум на 30-50%.

Туториалы фиксируют одну конфигурацию, потому что её надо с чего-то начать. Студенты воспринимают её как «оптимум», потому что у них нет метрики, чтобы оспорить. На практике recall@k и MRR падают на 15-30 пунктов, если применить «универсальные» 512 к коду или к юридическим документам.

Документ содержит 10 000 tokens. Используется fixed-size chunking: chunk size = 500 tokens, overlap = 100 tokens. Сколько chunks будет создано?

Больше размер chunk = больше контекста = лучше результат RAG

Большой chunk снижает precision: embedding вектор усредняется по всем темам, релевантный фрагмент тонет в шуме

text-embedding-3-small оптимален для 256-512 токенов - это подтверждено бенчмарками MTEB. На 1500 токенах embedding quality деградирует: модель не может адекватно представить весь контент в одном векторе. Retrieval находит chunk, но LLM получает 1400 нерелевантных токенов вместе с 100 нужными - precision падает, стоимость генерации растёт.

Итоги

  • Chunk size - самое влиятельное и самое дешёвое решение в RAG: от него зависит precision, recall и стоимость
  • Fixed-size (512 tokens, 10-20% overlap) - старт для прототипа. Recursive character splitting - production default
  • Semantic chunking: boundaries по падению cosine similarity между embeddings соседних предложений
  • Document-aware: Markdown по заголовкам, код по функциям - GitHub Copilot и Notion AI именно так
  • Metadata prepending перед embedding - бесплатный буст retrieval accuracy, который пропускают в 90% проектов
  • Больший chunk не значит лучше: embedding деградирует, precision падает, стоимость LLM растёт

Вопросы для размышления

  • Какая chunking-стратегия подойдёт для базы из 10 тысяч PDF-договоров на 200 страниц каждый, и почему фиксированный размер 512 здесь даст низкий recall?
  • Что произойдёт с retrieval-качеством, если overlap поднять с 20% до 50% - выиграет ли точность пограничных фактов или проиграет precision из-за дублирования?
  • Как построить golden dataset для оценки chunking, не имея заранее правильных ответов: какие свойства должны быть у вопросов и кто их размечает?

Что дальше

Документы нарезаны, RAG pipeline работает. Но в реальном чатботе нужна ещё одна составляющая - память о предыдущих сообщениях.

  • Conversation Memory — Buffer, summary, vector memory - как LLM помнит контекст разговора
  • Advanced RAG — Hybrid search и re-ranking для улучшения retrieval поверх chunking
  • Document Processing — Как извлекать текст из PDF, HTML, DOCX перед chunking

Связанные уроки

  • aie-11-document-processing — Парсинг документов предшествует chunking
  • aie-12-rag-fundamentals — Chunking - первый шаг любого RAG пайплайна
  • aie-13-advanced-rag — Без правильного chunking advanced RAG не работает
  • aie-09-embeddings — Размер чанка определяет качество embedding
  • calc-16-taylor — Оба метода ищут локальные аппроксимации большого объекта
  • db-26-caching
Chunking: как правильно нарезать документы для RAG

0

1

Войти