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
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 size | Pros | Cons |
|---|---|---|
| Маленький (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:
- **Тип контента** - код требует больших chunks (функция целиком), FAQ - маленьких (один вопрос-ответ)
- **Embedding model** - text-embedding-3-small оптимален для 256-512 tokens, большие chunks деградируют
- **Тип запросов** - конкретные вопросы → маленькие 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% случаев |
| Semantic | 0.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 size | Overlap |
|---|---|---|---|
| Техническая документация | Markdown-aware | 500-800 tokens | 50-100 tokens |
| FAQ / Knowledge Base | По вопросу-ответу (каждый Q&A = chunk) | Varies | Нет |
| Исходный код | По функциям / классам | Целая функция | Сигнатура + docstring |
| Юридические документы | По параграфам + semantic | 300-500 tokens | 100 tokens |
| Чат-логи / тикеты | По сообщению или треду | Целый тред | Нет |
| Научные статьи | По секциям (Abstract, Methods...) | 600-1000 tokens | 100 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