Перейти к содержанию
Публикация AiManual

Как подобрать chunk size и overlap для RAG: настройка на своей документации

Практический гайд по выбору chunk size и overlap для RAG на своей документации: стартовые диапазоны в токенах, правила границ для кода и таблиц, метрики retriev

Коротко

Что будет в материале

  1. 01

    Что такое chunk size и overlap и почему нет универсального значения

  2. 02

    Стартовые ориентиры по chunk size и overlap для своей документации

  3. 03

    Как настроить границы разбиения: код, таблицы и структура документации

  4. 04

    Как проверить chunk size и overlap на своём наборе вопросов

Качество ответов RAG чаще ограничивает нарезка документации, а не сама модель. Чанк на 2000 токенов, в котором смешаны три разные функции, разрезанная пополам таблица параметров или код, потерявший сигнатуру, ломают поиск сильнее, чем слабая LLM. Chunk size и overlap - две ручки, которые настраивают первыми, и обе не имеют универсального правильного значения.

Прямой ответ: chunk size - размер фрагмента документа, который индексируется как отдельная единица и превращается в один вектор. Overlap - сколько текста из конца предыдущего чанка повторяется в начале следующего, чтобы смысл на стыке не терялся. Стартовые ориентиры для текстовой документации: 256-512 токенов и overlap 10-15% от размера чанка. Это отправная точка для проверки, а не финальные настройки.

Цепочка выглядит так: документ, правила разбиения, чанки, эмбеддинги, векторный индекс, top-k по запросу, контекст LLM, ответ. Результат на выходе зависит от связки chunk size + overlap + стратегия границ + модель эмбеддингов + reranking. Подбор одного параметра при слабом звене в другом месте ничего не даст. Если пайплайна еще нет, начните с общей схемы: как подключить документацию, wiki и FAQ к LLM через RAG.

Что такое chunk size и overlap и почему нет универсального значения

Универсального лучшего размера чанка не существует. Он зависит от типа документации, длины ответов, которые вы ждете, модели эмбеддингов, контекстного окна генеративной модели и того, как формулируют вопросы ваши пользователи. Совет из чужой статьи про 1000 токенов без проверки на своих документах дает случайный результат: у вас другой корпус, другой язык и другие вопросы.

Чем chunk size отличается от overlap и как они взаимодействуют

Chunk size задает длину фрагмента. Overlap задает, насколько соседние фрагменты перекрываются. Пример: чанк 500 токенов и overlap 50 токенов означают, что каждый следующий чанк начинается на 450 токенов позже предыдущего, а последние 50 токенов предыдущего фрагмента повторяются в начале нового.

Число чанков приблизительно оценивается так:

N ≈ T / (chunk_size - overlap)

T          - общее число токенов в корпусе
chunk_size - размер чанка в токенах
overlap    - перекрытие в токенах

Из формулы видно, откуда берется рост индекса: overlap 50% при чанке 500 токенов почти удваивает число векторов. Реальное значение чуть больше из-за последнего неполного чанка.

Overlap не решает проблему разрезанной таблицы или блока кода. Если граница проходит по середине сигнатуры функции, дублирование 50 токенов не вернет целостность: оба чанка получат обрывок, и запрос про эту функцию не совпадет ни с одним. Для таких блоков важны правила границ, а не размер перекрытия.

Взаимодействие параметров одностороннее: chunk size задает верхнюю границу того, что вообще может попасть в один вектор, overlap лишь сглаживает стыки. Наращивать overlap вместо исправления правил разбиения - тупик.

На что реально влияют параметры: retrieval, контекст LLM и стоимость

Retrieval. Крупный чанк смешивает несколько тем в одном векторе. Эмбеддинг получается усредненным, и близость к конкретному вопросу падает: фрагмент на 1500 токенов про установку, конфигурацию и миграцию одинаково далек от каждого из трех запросов. Мелкий чанк дает точный вектор, но теряет смысл: предложение без подлежащего или шаг инструкции без условия, при котором он выполняется, плохо отвечают на вопрос.

Контекст LLM. Top-k умножается на chunk size. k=10 при чанках 800 токенов дает до 8000 токенов только под найденный контекст, плюс системный промпт, история диалога и сам вопрос. Это упирается в окно модели и в стоимость генерации: длинный вход считает каждая модель, а в локальном варианте цена измеряется временем на prefill и памятью под KV-кэш.

Индекс и latency. Число чанков определяет число векторов. Больше векторов означает дольше индексацию, больше места в хранилище и выше нагрузка на поиск. Растет overlap - растет и число векторов, а платите вы за это запросами к модели эмбеддингов при каждой переиндексации корпуса.

Компромисс выглядит так: крупные чанки дешевле по числу векторов и дают больше контекста в одном попадании, но размывают релевантность и съедают окно. Мелкие точнее в поиске, но требуют большего k и часто режут смысл. Оптимума под все случаи нет, есть выбор под ваш корпус и ваш тип вопросов.

Стартовые ориентиры по chunk size и overlap для своей документации

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

Тип документацииСтартовый chunk sizeOverlapЧто важнее размера
Текстовая документация, статьи, руководства256-512 токенов10-15%Целостность абзаца
Технические инструкции с плотной терминологией512-800 токенов10-20%Целостность шага и его условия
FAQ, короткие справочные статьи128-256 токенов0-10%Один вопрос, один чанк
Справочники APIпо функции или эндпоинту0%Сигнатура, параметры, пример вызова
Таблицы, прайсы, матрицы совместимостистроки плюс шапка0%Сохраненная шапка таблицы
Changelog и релиз-нотыпо записи версии0%Не смешивать версии

Overlap 10-20% покрывает типичный случай, когда смысл фразы размазан по границе. Для кода, таблиц и FAQ перекрытие чаще вредит: оно порождает почти одинаковые векторы, и top-k забивается копиями одного фрагмента.

Как пересчитать ориентиры в токенах под свой корпус

Возьмите 10-20 типичных фрагментов своей документации, прогоните через токенизатор модели, которая задает ограничение, и посмотрите распределение. Короткий скрипт на Python:

from transformers import AutoTokenizer

tok = AutoTokenizer.from_pretrained("ваша-эмбеддинг-модель")
paths = ["docs/deploy.md", "docs/api.md", "docs/faq.md"]

for p in paths:
    text = open(p, encoding="utf-8").read()
    ids = tok(text, add_special_tokens=False)["input_ids"]
    print(f"{p}: {len(text)} символов, {len(ids)} токенов, "
          f"{len(text) / len(ids):.2f} символа на токен")

Считайте по той модели, которая задает ограничение. Токенизатор эмбеддинг-модели и генеративной LLM почти всегда разные: чанк измеряется в токенах эмбеддера, а в окно генеративной модели он попадает уже в своих токенах. Ошибетесь с базой - реальный размер входа окажется в полтора-два раза больше ожидаемого.

Токены не равны символам. Для английского текста один токен часто покрывает около 4 символов, для русского при BPE-токенизации один токен укладывается примерно в 2-3 символа, местами меньше. Значит, 500 токенов русского текста - это заметно меньше знаков, чем 500 токенов английского. Ориентиры из англоязычных статей, перенесенные в русскую документацию без пересчета, дают чанки мельче, чем нужно.

Отдельный фактор - мультиязычные эмбеддинг-модели. Они обучались на разных языках, и качество вектора для русского текста может отличаться от английского при том же числе токенов. Ограничение на размер чанка остается токенным, но плотность смысла на один токен у русского текста своя.

Как тип документации меняет выбор

Справочник API: единица смысла - функция или эндпоинт целиком, вместе с сигнатурой, параметрами, кодами ошибок и примером вызова. Резать такой блок по 400 токенов бессмысленно, ответ на вопрос про параметры метода разбросан по трем чанкам.

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

FAQ: единица смысла - пара вопрос-ответ. Здесь работает chunk size, равный длине ответа, а overlap не нужен вовсе.

Длинные концептуальные статьи: единица смысла - абзац или группа абзацев вокруг одного тезиса. Крупные чанки тут допустимы, поскольку в тексте мало точных терминов-якорей, по которым ищет пользователь.

Changelog и релиз-ноты: единица смысла - запись версии. Смешивать версии в одном чанке опасно: модель процитирует устаревшее поведение как актуальное.

Документация с большим объемом кода: ориентиры в токенах работают плохо, работают структурные правила. О них ниже.

Как настроить границы разбиения: код, таблицы и структура документации

Правила границ дают больше, чем тюнинг размера. Чанк на 600 токенов, нарезанный по заголовкам, работает лучше чанка на 400 токенов, разрезанного посередине абзаца.

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

Альтернативы, которые стоит рассмотреть, если базовый вариант не дает нужного качества: parent-child retrieval, где поиск идет по мелким чанкам, а в контекст LLM отдается родительский крупный блок, и semantic chunking, где границы ставятся там, где меняется смысл, по расстоянию между соседними предложениями в векторном пространстве. Обе техники не универсальны: parent-child добавляет слой логики и усложняет индексацию, semantic chunking зависит от качества эмбеддинг-модели и стоит дополнительных вычислений.

Почему код и таблицы нельзя резать по символам

Разрезанная сигнатура функции дает два чанка, каждый из которых не соответствует ни одному осмысленному запросу. Пользователь спрашивает, как вызвать метод, а в индексе лежат def method_x(self, и timeout=30) -> dict:. Поиск не находит ни то, ни другое, а LLM, получив обрывок, достраивает недостающие параметры по догадке.

Разрезанная таблица теряет шапку. Строка 12 | 8 | да без заголовков про VRAM, число слоев и поддержку бесполезна и для поиска, и для генерации. Модель либо проигнорирует фрагмент, либо припишет числа не тем колонкам.

Правила, которые снимают большую часть проблемы:

  • Код: функция, класс или логический блок целиком как атомарный чанк. Если блок превышает лимит, режьте по границам функций и сохраняйте в каждом чанке объявление класса и импорты, нужные для понимания.
  • Таблицы: одна строка или группа строк плюс заголовок таблицы в каждом чанке. Для широких таблиц дублируйте шапку текстом: Параметр: значение.
  • Списки: элемент списка вместе с вводной строкой, иначе отдельный пункт теряет смысл.
  • Формулы и схемы: целиком, с подписью и расшифровкой переменных.
  • Примеры кода внутри инструкции: пример остается привязанным к шагу, который он иллюстрирует.

Overlap в таких блоках обычно вреден. Он не восстанавливает разорванную структуру, а создает вторую копию обрывка. Уменьшайте его до нуля, когда чанки атомарны.

Метаданные чанка: что сохранять вместе с текстом

К тексту чанка добавляйте служебные поля: путь по заголовкам, тип блока (код, таблица, текст, список), имя файла, версию или дату документа, язык, идентификатор раздела. Это дает фильтрацию и переранжирование без изменения chunk size.

Пример применения: пользователь спрашивает про параметр из версии 2.0, а в индексе лежат чанки 1.x и 2.x. Фильтр по полю версии убирает половину ложных попаданий, тогда как подбор chunk size эту проблему не решает вообще. Аналогично фильтр по типу блока помогает, когда вопрос про код, а в выдаче лежат прозаические описания.

Метаданные стоит вклеивать в текст чанка, если модель эмбеддингов не поддерживает отдельные поля. Строка вида Файл: deploy.md | Раздел: Настройка GPU | Тип: инструкция в начале чанка помогает поиску по формулировкам, которые встречаются в заголовках, а не в теле документа.

Как проверить chunk size и overlap на своём наборе вопросов

Без eval-набора любое изменение настроек превращается в гадание. Разница между чанками 400 и 600 токенов не видна ни в двух ответах, ни в десяти, но на полусотне вопросов она проявляется в метриках.

Минимальный eval-набор: сколько вопросов и какие

30-50 вопросов уже дают сигнал, если они покрывают разные типы запросов. Ниже 30 выводы случайны, выше 100 растет стоимость прогонов, а прирост информации падает.

  • Фактологические: какое значение по умолчанию у параметра.
  • Процедурные: как настроить компонент с нуля.
  • Навигационные: где описана конкретная интеграция.
  • По коду: какие аргументы принимает функция.
  • По таблицам: какой объем памяти нужен для конфигурации из строки.
  • Пограничные: вопросы, ответа на которые в документации нет вообще.

У каждого вопроса должен быть явный правильный источник в документации: файл и раздел, откуда берется ответ. Это ground truth, без него считать recall нельзя. Формулируйте вопросы так, как их задают пользователи: «почему падает с ошибкой 500 при загрузке» работает лучше, чем «Обработка ошибок загрузки» из заголовка раздела.

Метрики retrieval: что считать и как интерпретировать

МетрикаЧто показываетКак читать
Recall@kДоля вопросов, где правильный чанк попал в top-kГлавная метрика для RAG: пропущенное LLM не восстановит
Precision@kДоля релевантных чанков среди top-kМусор в контексте повышает риск ошибки и расход токенов
MRRСредняя обратная позиция первого правильного чанкаПоказывает, насколько высоко в выдаче нужный фрагмент
Hit rateДоля вопросов, где найден хотя бы один правильный чанкБыстрый индикатор полного провала поиска

Крупные чанки обычно поднимают recall и снижают precision: в один фрагмент попадает больше текста, шанс задеть нужный абзац растет, но вместе с ним в top-k приезжает лишнее. Мелкие чанки дают обратную картину. Для RAG приоритет у recall при умеренном k: если правильный фрагмент не найден, генерация не поможет, а лишние чанки LLM частично отфильтрует сама.

Ищите точку, где recall@k высокий, а precision не проваливается, вместо погони за максимумом одной метрики. Если recall@5 равен 0.9 при чанках 800 токенов и 0.6 при 300, это еще не приговор мелким чанкам: проверьте, как изменился размер входа и качество финальных ответов.

Как сравнивать конфигурации корректно

Меняйте один параметр за раз. Если одновременно меняете chunk size и overlap, результат нельзя приписать ни одному из них.

Зафиксируйте перед прогоном: модель эмбеддингов, версию и способ нормализации текста, метрику расстояния, значение k, наличие и версию reranker, промпт генерации, версию документации. Любое изменение из этого списка ломает сравнимость прогонов.

Порядок прогонов: базовая конфигурация, затем 2-3 варианта чанкинга. Пример набора: 400 токенов с overlap 10%, 600 токенов с overlap 10%, 400 токенов с overlap 20%. Такой набор разделяет эффект размера и эффект перекрытия.

Если бюджет позволяет, повторяйте прогон на перемешанном порядке вопросов: кэш эмбеддингов, порядок индексации и rate limit провайдера дают разброс. Выводы по пяти вопросам не значат ничего.

Финальные ответы оценивайте отдельно от метрик поиска: вручную по шкале или через LLM-as-judge. У судьи-модели есть свои ограничения, она склонна одобрять длинные и уверенные ответы и хуже ловит фактические ошибки в цифрах. Для критичных вопросов ручная проверка обязательна. Как устроить такой цикл оценки, разобрано в материале про метрики качества и борьбу с галлюцинациями в RAG-системах.

Типичные ошибки при подборе chunk size и overlap

Семь ошибок, которые встречаются чаще всего, и признак, по которому каждую видно:

  1. Копирование чужого chunk size без проверки. Признак: настройки взяты из статьи или чужого репозитория, своих замеров нет.
  2. Огромный overlap «на всякий случай». Признак: в top-k несколько почти одинаковых фрагментов, размер индекса вырос в разы, recall не изменился.
  3. Нулевой overlap там, где смысл теряется на границе. Признак: ответы обрываются на середине условия или шага.
  4. Разбиение по символам без учета структуры. Признак: в чанках обрывки таблиц, половины функций, пункты списка без вводной строки.
  5. Путаница токенов и символов. Признак: в конфиге стоит chunk_size=1000 с мыслью о символах, а splitter считает токены, и фактический размер отличается в разы.
  6. Отсутствие eval-набора. Признак: настройки меняются после двух неудачных ответов в чате.
  7. Попытка вылечить chunk size проблему, которая в другом месте. Признак: правильный чанк есть в индексе, но никогда не попадает в top-k, либо его нет вообще из-за ошибок парсинга.

Overlap: когда он помогает, а когда вредит

Overlap помогает, когда смысл фразы или шага инструкции распределен между соседними чанками: условие в конце одного, действие в начале следующего. Здесь повтор 50-100 токенов сохраняет связку.

Overlap вредит в трех случаях. Чанки атомарны (функция, строка таблицы, пара вопрос-ответ), и перекрытие создает дубликаты. Overlap сравним с chunk size, например 400 при чанке 500: почти весь текст дублируется, индекс растет вдвое, а поиск получает пары одинаковых векторов. Top-k заполняется копиями одного фрагмента, и модель видит один и тот же текст пять раз вместо пяти разных источников.

Практическое правило: начинайте с 10-15% от chunk size и уменьшайте, если в выдаче появились дубли. Ноль - нормальное значение для структурированных блоков.

Когда проблема не в chunk size, а в архитектуре retrieval

Сигналы, что тюнинг параметров не поможет:

  • Правильный чанк есть в индексе, но не попадает в top-k ни при одном размере. Причина чаще в модели эмбеддингов или в несовпадении языка запроса и документа.
  • Пользователи используют слова, которых нет в документации: синонимы, аббревиатуры, внутренний жаргон. Помогают hybrid search на базе BM25 и вектора, расширение запроса, а не размер чанка.
  • В выдаче много похожих чанков, все релевантные, но ответа в них нет. Смотреть в сторону reranking и parent-child retrieval.
  • Вопрос требует сведения фактов из разных разделов. Здесь работают многошаговый поиск и декомпозиция запроса: например, AgenticRetrieveStream разбивает сложные запросы на подзадачи и итеративно уточняет поиск.
  • Часть документации не разобрана: PDF-таблицы превратились в шум на этапе парсинга. Чанкинг бессилен, если исходный текст испорчен.

Практический чек-лист настройки чанкинга для своей документации

  1. Определите тип документации и ожидаемые типы вопросов. От этого зависят стартовые ориентиры.
  2. Выберите стартовые chunk size и overlap: 256-512 токенов и 10-15% для текста, 512-800 токенов для плотных инструкций, 128-256 токенов для FAQ.
  3. Пересчитайте ориентиры в токенах вашей эмбеддинг-модели на 10-20 реальных фрагментах корпуса.
  4. Настройте правила границ: Markdown-aware splitting по заголовкам, код и таблицы как атомарные блоки, списки вместе с вводной строкой.
  5. Сохраните метаданные: путь по заголовкам, тип блока, имя файла, версию документа.
  6. Соберите eval-набор из 30-50 реальных вопросов с указанием правильного источника для каждого.
  7. Прогоните 2-3 конфигурации при зафиксированных модели эмбеддингов, k, промпте и версии документации.
  8. Сравните recall@k, precision@k, MRR и hit rate, затем качество финальных ответов.
  9. Зафиксируйте выбранную конфигурацию в конфиге репозитория вместе с датой и версией корпуса.
  10. Пересматривайте настройки при крупном обновлении документации, смене модели эмбеддингов или изменении типов вопросов пользователей.

Chunking - итеративный процесс. Конфигурация, которая работает на документации из 300 файлов, развалится после подключения PDF-архива с таблицами. Держите eval-набор и правила разбиения в одном репозитории с пайплайном, тогда следующая проверка займет час, а не неделю.

Подписаться на канал