# Как чанкинг документов влияет на контекст в RAG

Автор: Ермолаев Тимофей Владимирович  
Оригинал: https://timofei-ermolaev.ru/llm/rag/chanking-dokumentov/  
Опубликовано: 2026-10-23  
Обновлено: 2026-10-23

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

И если пользователь задаёт вопрос агенту, системе не нужно заново просматривать всю базу знаний. Она находит подходящие фрагменты и передаёт их языковой модели вместе с вопросом. Модель составляет ответ, опираясь на найденный текст. Такой подход называют **RAG — генерацией, дополненной поиском**.

Поэтому при подготовке базы знаний важно, где проходят границы фрагментов: от них зависит, какие сведения останутся рядом, а какие окажутся в разных частях. В документах общее правило часто идет в одном предложении, а важное исключение или условие следом за ним. Если граница разрежет эту связку, отдельный фрагмент сохранит только половину картины. При этом делить нужно не любой текст: если документ совсем небольшой и посвящен одной задаче, его допустимо передавать модели целиком, о чем прямо напоминает [Anthropic в обзоре Contextual Retrieval (2024)](<https://www.anthropic.com/research/contextual-retrieval>).

В этой статье разберем, какое место занимает нарезка текста в работе поисковой системы, как разные способы выбора границ и перекрытия влияют на сохранность правил и почему ошибка в итоговом ответе далеко не всегда означает ошибку чанкинга. Настройки и базовые принципы такого разбиения подробно описаны в документации [Cloudflare AI Search](<https://developers.cloudflare.com/ai-search/configuration/indexing/chunking/>) и [Microsoft Azure AI Search](<https://learn.microsoft.com/en-us/azure/search/vector-search-how-to-chunk-documents>).

## Коротко

- Чанкинг делит исходные документы на фрагменты (чанки), чтобы система могла находить и передавать генератору нужные части текста вместо всей базы знаний. Если документ небольшой и посвящен одной теме, делить его не обязательно.
- Границы фрагмента определяют, какие правила, исключения и поясняющие заголовки останутся внутри одной части. Достаточно ли одного фрагмента для ответа, зависит от конкретного вопроса: узкому вопросу может хватить одной фразы, а сравнительному понадобятся оба условия.
- Фиксированный размер, рекурсивное деление по разделителям, опора на структуру заголовков и семантическая группировка по-разному проводят границы, а перекрытие (overlap) лишь повторяет соседний участок текста. Универсального размера чанка или процента перекрытия не существует.
- То, что попало в один чанк при нарезке, то, что система извлекла при поиске, и то, что в итоге передали языковой модели для ответа — разные наборы данных. Ошибка в ответе не всегда означает ошибку в границах фрагментов.

## Зачем в RAG выделяют фрагменты

Работа с документами в RAG делится на два этапа, разнесенных по времени: предварительная подготовка базы и обработка каждого нового вопроса. Если вы хотите освежить общую картину того, [как RAG находит сведения в документах](<https://timofei-ermolaev.ru/llm/chto-takoe-rag/>), полезно держать в голове это разделение.

Заранее, еще до вопросов пользователя, система получает из исходных файлов текст и делит его на фрагменты. В распространенном векторном варианте RAG специальная модель преобразует каждый фрагмент в набор чисел — вектор. Эти представления сохраняют в поисковом индексе: подготовленном хранилище для поиска. Для каждого представления сохраняют связь с исходным текстом, чтобы найденный результат можно было вернуть в виде читаемого фрагмента.

Когда поступает вопрос, система выполняет сопоставление: как показано в [справочнике API LangChain (`similaritySearch`, v1.2.20)](<https://reference.langchain.com/javascript/langchain-core/vectorstores/VectorStore/similaritySearch>), текстовый запрос тоже преобразуется в вектор, после чего система ищет фрагменты с числовыми представлениями, наиболее близкими к вектору вопроса. Векторный поиск — частый, но не единственный вариант архитектуры: отбор материала может опираться и на прямое совпадение слов без векторизации запроса. Главное, что в конце этого шага языковой модели — генератору ответа — передают не поисковые векторы, а исходный текст выбранных фрагментов вместе с самим вопросом и системными инструкциями.

Когда процесс разложен по шагам, соседние понятия перестают смешиваться:

- **Чанк (фрагмент)** — кусок исходного текста, который система выделила при подготовке документа и который может быть целиком вставлен в запрос к генератору.
- **Токены** — мелкие текстовые единицы (слова, части слов или символы), на которые любая языковая модель разбивает входной текст для внутренней обработки. Длина чанка в символах и его объем в токенах — разные величины.
- **Эмбеддинг** — вектор из чисел, отражающий содержание текста для поискового сравнения. О том, [как эмбеддинги помогают сравнивать тексты](<https://timofei-ermolaev.ru/llm/embeddingi/>), мы подробно говорили в отдельной статье; здесь важно помнить, что размерность вектора зависит от выбранной модели и не равна длине фрагмента.
- **Контекст генерации** — всё финальное обращение к языковой модели: системные правила, текст вопроса и все подставленные текстовые фрагменты.

Делить длинные документы приходится по нескольким причинам, которые выделяет документация [Microsoft по векторизации и чанкингу](<https://learn.microsoft.com/en-us/azure/search/vector-search-how-to-chunk-documents>). Во-первых, свой предел входного объема есть у модели, вычисляющей эмбеддинги, и отдельный предел — у языковой модели, генерирующей ответ. Эти бюджеты относятся к разным моделям и этапам: лимит входа при создании вектора не обязан совпадать с размером контекстного окна генератора, куда помимо найденных отрывков должны поместиться вопрос и инструкции. Разбиение позволяет обрабатывать документ частями, каждая из которых укладывается в предел первой модели. Затем поиск помогает выбрать для генератора нужные части, оставив место для вопроса и инструкций. Во-вторых, если документ объединяет сразу много тем, он может плохо представляться одним общим вектором даже при допустимой длине текста. Один вектор в таком случае представляет весь документ, а вопрос может касаться одного небольшого пункта внутри него.

Векторный вариант RAG

Заранее: подготовка документов

**Исходный текст**

Документы для базы знаний.

выделить части

**Текстовые фрагменты**

Чанки исходного документа.

вычислить векторы

**Представления**

Числовые векторы для поиска.

сохранить в индекс

**Поисковый индекс**

Представления со связью с текстом.

**Текст сохраняется.** У каждого представления есть связь с исходным материалом: по результату поиска можно получить читаемый фрагмент.

**Предел модели эмбеддингов:** входной текст должен помещаться в её лимит. Небольшой документ можно использовать целиком.

При каждом вопросе

Используется подготовленный индекс; нарезка не повторяется из-за каждого вопроса.

**Вопрос пользователя**

Два пути: в поиск и напрямую в сборку обращения.

вопрос → вычислить представление

**Представление вопроса**

Вектор для сопоставления.

вектор вопроса → сопоставление

**Сопоставление**

**Из подготовленного индекса:** представления фрагментов.

Найти близкие представления.

по сохранённой связи вернуть текст

**Найденные фрагменты**

Результаты поиска — читаемый текст.

**Возможен дополнительный отбор.** Состав текста для генератора может отличаться от результатов поиска.

выбранный текст → в сборку обращения

Прямые входы в сборку

**Тот же вопрос**

Исходный текст вопроса.

Напрямую в сборку обращения

**Системные инструкции**

Правила для генератора.

Напрямую в сборку обращения

**Если подставляют родительский раздел**

Его текст берут по сохранённой связи с исходным материалом.

Текст раздела → в сборку вместо найденных дочерних частей

**Сборка обращения**

Контекст генерации

- Инструкции
- Вопрос
- Выбранный текст

**Бюджет генератора:** всё обращение должно поместиться в его контекст. Это отдельное ограничение, а не лимит модели эмбеддингов.

собранный текст → генератору

**Генератор**

Составляет ответ по обращению.

сформировать ответ

**Ответ**

Наличие нужного текста само по себе не гарантирует правильного ответа.

Поисковые векторы служат сопоставлению. Генератор получает текст, вопрос и инструкции. Сохранённое поле metadata само по себе не определяет, попадёт ли оно в эмбеддинг или обращение.

## Что меняется, когда граница проходит между правилами

Когда этап подготовки понятен, можно проверить, какие именно сведения остаются внутри одного фрагмента после разреза. Чтобы сделать границы наглядными, рассмотрим короткую вымышленную библиотечную памятку:

```
## Выдача книг
Книги из общего фонда выдают на четырнадцать дней. Альбомы с красной наклейкой тоже относятся к общему фонду. Для альбомов с красной наклейкой срок выдачи составляет три дня.
## Продление
Книгу можно продлить один раз, если на неё нет заявки другого читателя. Альбомы с красной наклейкой продлевать нельзя.
## Читальный зал
Газеты читают только в зале. В читальном зале есть стол у окна и полка с новыми журналами.
```

Такой небольшой документ в реальной задаче не требует нарезки и поиска, его проще целиком передать в запрос к языковой модели. Здесь эта короткая памятка нужна для другой цели: на тексте из 430 кодовых точек Unicode (включая переносы строк; далее для простоты будем называть их символами) мы проверили четыре способа разбиения и посмотрели, где именно проходят границы. Токенизация, вычисление эмбеддингов, поиск и генерация ответа в этой локальной проверке не выполнялись — мы наблюдаем только за составом самих текстовых фрагментов.

Зададим к этой памятке первый сравнительный вопрос: *«На сколько дней выдают обычную книгу из общего фонда, а на сколько альбом с красной наклейкой?»* Для полного ответа нужны два предложения:

1. «Книги из общего фонда выдают на четырнадцать дней.»
2. «Для альбомов с красной наклейкой срок выдачи составляет три дня.»

Посмотрим, что происходит с этим текстом в детерминированной проверке при строгом делении по 120 символов без перекрытия. Дальше будем обозначать режим двумя числами: размер фрагмента / длина перекрытия с соседним фрагментом. Поэтому запись 120/0 означает размер 120 символов и отсутствие перекрытия; в нашей памятке получается 4 части. Первая граница проходит прямо внутри переходной фразы на слове «фо|нду».

**Фрагмент 1:**

```
## Выдача книг
Книги из общего фонда выдают на четырнадцать дней. Альбомы с красной наклейкой тоже относятся к общему фо
```

**Фрагмент 2:**

```
нду. Для альбомов с красной наклейкой срок выдачи составляет три дня.
## Продление
Книгу можно продлить один раз, если н
```

Общее правило о четырнадцати днях осталось в первом фрагменте, а специальный срок в три дня для альбома ушел во второй. Если же увеличить размер фрагмента до 220 символов без перекрытия (режим 220/0, делящий памятку на 2 части), оба предложения о сроках выдачи целиком попадают в первый фрагмент.

Значит ли это, что разрез в режиме 120/0 испортил фрагмент? Это зависит от того, оцениваем ли мы один изолированный чанк или всю подборку, которую поиск передаст генератору. Если читатель спрашивает только: *«На сколько дней выдают альбом с красной наклейкой?»*, второго фрагмента из схемы 120/0 достаточно: предложение «Для альбомов с красной наклейкой срок выдачи составляет три дня.» самостоятельно отвечает на этот узкий вопрос. Нехватка первого предложения возникает именно тогда, когда вопрос требует сравнить обычную книгу и альбом, а мы проверяем наличие обоих условий внутри одного фрагмента.

При этом простое увеличение размера с 120 до 220 символов не дает универсального выигрыша. Возьмем второй сравнительный вопрос: *«Если заявок других читателей нет, можно ли продлить обычную книгу и альбом с красной наклейкой?»* В режиме 220/0 граница первого фрагмента падает ровно на 220-й символ и разрезает ключевой глагол в правиле о продлении: первая часть обрывается на словах «Книгу можно продл», а вторая начинается с «ить один раз, если на неё нет заявки другого читателя. Альбомы с красной наклейкой продлевать нельзя.». Слова не исчезли из документа целиком, но ни один из двух фрагментов больше не содержит целого предложения об условии продления обычной книги.

*Примечание к иллюстрации границ: в режимах 120/0 и 220/0 проверено точное вхождение подстрок в полуоткрытые символьные интервалы \[0, 120), \[120, 240) и \[0, 220) исходного файла длиной 430 кодовых точек. Как такие разрезы повлияют на реальный векторный поиск или ответ модели, без запуска всего конвейера RAG на конкретной системе утверждать нельзя.*

## Как выбирают границы и зачем делают перекрытие

Если простая смена длины сдвигает разрез с одного правила на другое, возникает вопрос: можно ли подстраховать границы повторением соседнего текста или выбирать места разреза по другим признакам?

Для защиты фраз на стыках используют **перекрытие (overlap)** — следующий фрагмент начинается не строго после предыдущего, а захватывает его последние символы или токены. В документации [LangChain по рекурсивному сплиттеру](<https://docs.langchain.com/oss/python/integrations/splitters/recursive_text_splitter>) и в руководстве [Microsoft Azure AI Search](<https://learn.microsoft.com/en-us/azure/search/vector-search-how-to-chunk-documents>) перекрытие описывается как способ сохранить связность текста у границ. Проверим на нашей памятке режим 120/30: размер фрагмента остается 120 символов, но каждые два соседних участка имеют 30 общих символов (всего получается 5 частей). Начало каждого следующего фрагмента сдвигается на 90 символов: 120 минус 30. Поэтому размер фрагмента и шаг, с которым начинается следующий, здесь различаются.

В схеме 120/0 предложение «Книгу можно продлить один раз, если на неё нет заявки другого читателя.» распадалось между вторым и третьим фрагментами на союзе «если н|а неё». При шаге 90 символов третий фрагмент начинается с позиции 180 вместо 240, поэтому захватывает начало этого предложения. В режиме 120/30 его текст (включая ведущий пробел перед словами «три дня») выглядит так:

```
 три дня.
## Продление
Книгу можно продлить один раз, если на неё нет заявки другого читателя. Альбомы с красной наклейк
```

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

- **Вместимость фрагмента:** чтобы захватить и общее правило выдачи (14 дней), и исключение для альбомов (3 дня), нужен непрерывный участок текста длиной 174 символа — от начала общего правила до конца специального. Между ними стоит промежуточная фраза об общем фонде, которая тоже входит в этот участок. Во фрагмент длиной 120 символов такой участок не поместится при любом перекрытии.
- **Положение границ:** два предложения о продлении книги и запрете для альбомов идут подряд и занимают вместе 118 символов. По длине они могли бы поместиться в один фрагмент длиной 120 символов, но в режиме 120/30 третий фрагмент занимает позиции с 180 до 300, а четвертый — с 270 до 390. В итоге третий фрагмент оборвался на словах «Альбомы с красной наклейк», а полное предложение «Альбомы с красной наклейкой продлевать нельзя.» оказалось в четвертом.

Перекрытие повторяет приграничный текст и увеличивает общий объем индексируемых данных, о чем предупреждает [документация Cloudflare AI Search](<https://developers.cloudflare.com/ai-search/configuration/indexing/chunking/>), но не гарантирует, что все нужные для вопроса условия соберутся в одном чанке.

Вместо отсчета символов границы можно проводить по разметке документа. Когда мы разделили ту же памятку по заголовкам второго уровня `##` (принцип работы таких инструментов описан в [документации `MarkdownHeaderTextSplitter` от LangChain](<https://docs.langchain.com/oss/python/integrations/splitters/markdown_header_metadata_splitter>)), получилось 3 фрагмента неравной длины: 190, 132 и 108 символов. В первом разделе остались оба правила о сроках выдачи, во втором — оба правила о продлении. Здесь сработало не «понимание смысла» алгоритмом, а совпадение структуры заголовков с тем, как сформулированы наши вопросы.

На практике подходы к выбору границ делят на четыре группы, которые часто комбинируют друг с другом:

| Подход к разбиению | На что опирается граница | Что важно учитывать |
| --- | --- | --- |
| **Фиксированное (Fixed-size)** | Заданный целевой размер (в символах или токенах), часто с фиксированным перекрытием. | Простая предсказуемая длина, но граница легко разрезает слово или предложение посередине. |
| **Рекурсивное (Recursive)** | Список разделителей по убыванию крупности (например, абзацы, строки, пробелы): слишком длинный кусок делится следующим разделителем. | Стремится резать по естественным знакам препинания и абзацам, но опирается на синтаксис, а не на смысл. |
| **Структурное (Structure / Content-aware)** | Разметка документа: заголовки Markdown или HTML, разделы, элементы списков или кода. | Сохраняет авторскую рубрикацию документа; если раздел слишком велик, его дополнительно дробят по длине. |
| **Семантическое (Semantic)** | Оценка смысловой близости соседних предложений или абзацев (например, через сравнение их представлений). | Пытается удерживать одну микротему внутри фрагмента, но требует дополнительных вычислений и не гарантирует полноты всех условий. |

*Примечание о параметрах и единицах:* у этой таблицы нет «победителя», а число получившихся в нашем примере частей (4, 5, 2 и 3) служит только подписью к четырем режимам на тексте в 430 символов, а не оценкой качества. Сравнивать настройки разных платформ напрямую по числам нельзя: в базовом примере LangChain параметр `chunk_size` считает символы через функцию длины строки `len`, в Cloudflare AI Search размер задается в токенах, а перекрытие — в процентах, тогда как Microsoft Azure AI Search предлагает отдельные символьные и токенные режимы с выбором токенизатора.

Какие правила остаются в одном фрагменте

Одна вымышленная памятка — **430 кодовых точек Unicode**. Буква в полосе означает целое предложение; номер — часть документа.

- А**Выдача книги**

  Книги из общего фонда выдают на четырнадцать дней.

  \[15, 65)
- Б**Выдача альбома**

  Для альбомов с красной наклейкой срок выдачи составляет три дня.

  \[125, 189)
- В**Продление книги**

  Книгу можно продлить один раз, если на неё нет заявки другого читателя.

  \[203, 274)
- Г**Продление альбома**

  Альбомы с красной наклейкой продлевать нельзя.

  \[275, 321)

Полосы пропорциональны общей шкале 0–430. Координаты относятся к исходному тексту, не к токенам или ширине строки на экране. Знак «—» означает: ни одного из этих четырёх предложений целиком.

**120/0**

4 части

**А — в части 1, Б — в части 2.** Сроки выдачи разделены; первый разрез проходит внутри слова фо|нду.

1: \[0, 120) · 2: \[120, 240) · 3: \[240, 360) · 4: \[360, 430)

**120/30**

5 частей

Размер 120 · перекрытие 30 · шаг 90

Штриховка — 30 общих символов соседних частей.

**В целиком в части 3, Г — в части 4.** Предложение о продлении книги сохранилось, но оба правила продления вместе не попали ни в одну часть.

1: \[0, 120) · 2: \[90, 210) · 3: \[180, 300) · 4: \[270, 390) · 5: \[360, 430)

**220/0**

2 части

**А и Б вместе в части 1.** Оба срока выдачи сохранены; предложение В разрезано на продл|ить.

1: \[0, 220) · 2: \[220, 430)

**По заголовкам ##**

3 части

**А и Б — в разделе «Выдача книг», В и Г — в разделе «Продление».** Здесь структура совпала с вопросами; это не доказательство понимания смысла алгоритмом.

1: \[0, 190) · 2: \[190, 322) · 3: \[322, 430)

**Вместимость: 174 > 120**

От начала А до конца Б — **174 символа**, включая промежуточную фразу: \[15, 189). Такой участок не поместится в чанк 120 при любом перекрытии.

**Положение: 118 ≤ 120**

От начала В до конца Г — **118 символов**: \[203, 321). Длины хватает, но ни окно \[180, 300), ни окно \[270, 390) не содержит всю связку.

## Какие сведения необходимо сохранять вокруг фрагмента

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

В нашей текстовой памятке каждое предложение само называет свой предмет: фраза «Для альбомов с красной наклейкой срок выдачи составляет три дня.» понятна и без заголовка `## Выдача книг`. Но в реальных документах те же сведения часто оформляют компактнее. Представим отдельную иллюстрацию — то же самое библиотечное правило, записанное в виде таблицы, где на пересечении строки «Альбомы с красной наклейкой» и столбца «Срок выдачи, дней» стоит просто ячейка со значением «3» (в исходный текст памятки и расчеты интервалов эта таблица не входит, и работу парсеров мы здесь не испытываем). Если при нарезке число «3» отделится от названия строки или шапки столбца, фрагмент потеряет однозначность: три чего и для какого фонда? Как отмечает [Anthropic (2024)](<https://www.anthropic.com/research/contextual-retrieval>), отрывок без указания объекта или периода становится двусмысленным, поэтому при работе с таблицами и списками проверяют не просто длину чанка, а сохранность связей между значением и его обозначениями.

Отдельный вопрос — куда попадают заголовки разделов, такие как «Выдача книг» и «Продление». В документации [`MarkdownHeaderTextSplitter` от LangChain](<https://docs.langchain.com/oss/python/integrations/splitters/markdown_header_metadata_splitter>) показано, что у фрагмента есть две разные области хранения: основной текст (`page_content`) и словарь сопроводительных полей (`metadata`). По умолчанию этот инструмент разбиения переносит текст заголовка в поле `metadata`, удаляя его из основного текста чанка, хотя настройка `strip_headers=False` позволяет оставить заголовок и внутри текста.

Различие между текстом чанка и полем `metadata` напрямую влияет на поиск и генерацию:

- **Сохранение в поле не означает автоматического использования.** В одних конфигурациях система индексирует и передает в промпт только основной текст фрагмента, а поля `metadata` использует лишь для фильтров или отображения в интерфейсе. В других системах выбранные поля метаданных автоматически подклеиваются к тексту перед вычислением эмбеддинга или сборкой запроса. Поэтому сам факт наличия заголовка в `metadata` ничего не гарантирует: нужно проверять, как именно ваш конвейер обрабатывает эти поля.
- **Связь с первоисточником.** Как рекомендует руководство [Microsoft Azure AI Search](<https://learn.microsoft.com/en-us/azure/search/vector-search-how-to-chunk-documents>), к фрагменту полезно привязывать имя исходного документа и номер страницы. Сама по себе текстовая метка в `metadata` не делает ответ достоверным, но при рабочей ссылке на оригинал позволяет человеку или системе открыть исходный документ и проверить контекст.

*Короткая ремарка о контекстуализации до индексации:* помимо сохранения заголовков и обычного перекрытия соседних фраз существуют методы, которые дополняют чанк перед отправкой в индекс. Например, в подходе [Contextual Retrieval от Anthropic (сентябрь 2024 года)](<https://www.anthropic.com/research/contextual-retrieval>) к каждому фрагменту до вычисления эмбеддингов добавляют краткое сгенерированное пояснение о том, к какой части всего документа относится этот отрывок.

## Как найденные фрагменты становятся материалом для ответа

До сих пор мы смотрели, помещаются ли нужные правила в один изолированный фрагмент. Но что получит генератор, если в выбранной схеме разбиения срок выдачи обычной книги остался в первом чанке, а срок для альбома с красной наклейкой во втором?

Поисковый шаг в RAG может возвращать не один, а сразу несколько подходящих фрагментов. Если бы на нашей памятке запускался поиск по сравнительному вопросу о двух сроках, система могла бы извлечь оба фрагмента и передать их в общий запрос к генератору. Именно поэтому разнесение правил по разным чанкам на этапе нарезки еще не означает, что система обязательно ошибется при ответе.

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

- **Отбор и [переупорядочивание](/llm/rag/reranking/):** система может найти несколько фрагментов-кандидатов, но оставить для генератора только наиболее подходящие.
- **Подстановка родительского раздела:** некоторые архитектуры ищут совпадения по мелким фрагментам, но при сборке контекста заменяют их более крупным разделом документа. «Родительским» здесь называют раздел, из которого получены эти фрагменты; связь между ними сохраняют при подготовке базы. Благодаря этой связи после поиска можно обратиться к тексту исходного раздела. Например, в документации [LlamaIndex по механизму `AutoMergingRetriever`](<https://developers.llamaindex.ai/python/framework/integrations/retrievers/auto_merging_retriever/>) описана иерархия узлов: векторный поиск находит мелкие дочерние фрагменты (листовые узлы), но если из одного раздела найдено достаточно таких частей, система объединяет их и передает модели текст целого родительского узла. Это возможность конкретного механизма, а не свойство любого RAG.

Сколько бы фрагментов ни нашел поиск и как бы система ни расширяла их до крупных разделов, в конце действует жесткое ограничение. Весь собранный материал должен поместиться в бюджет обращения к модели, подробнее о том, [что занимает контекстное окно модели](<https://timofei-ermolaev.ru/llm/context-engineering/context-window/>), рассказано в отдельном материале. Крупные чанки или дублирующиеся участки из-за перекрытия быстрее расходуют этот бюджет, а само присутствие нужных фраз в переданном тексте еще не гарантирует, что генератор правильно сопоставит их в ответе. Как именно распределяется контекстный бюджет при индексации и передаче чанков, также разбирается в [документации Cloudflare AI Search](<https://developers.cloudflare.com/ai-search/configuration/indexing/chunking/>).

## Что проверить, если в ответе потерялось условие

Когда RAG-система дает неполный ответ — например, называет общий срок выдачи книги, но забывает про трехдневное ограничение для альбомов с красной наклейкой, — легко сразу обвинить неудачный размер чанка. Однако на пути от документа к ответу данные проходят несколько самостоятельных этапов, о чем напоминают руководства [Microsoft](<https://learn.microsoft.com/en-us/azure/search/vector-search-how-to-chunk-documents>) и [Anthropic](<https://www.anthropic.com/research/contextual-retrieval>).

Прежде чем менять настройки разбиения, полезно последовательно проверить пять контрольных точек:

1. **Исходный документ.** Есть ли нужное условие или исключение в самом загруженном тексте и не потерялось ли оно еще при чтении файла?
2. **Подготовленные фрагменты и их поля.** Как именно граница разделила правило и исключение при чанкинге? Сохранились ли внутри текста чанка (а не только в неиспользуемом поле `metadata`) предмет, единица измерения и заголовок, без которых фраза теряет смысл?
3. **Результаты поиска (извлечение).** Попал ли фрагмент с нужным условием в число кандидатов, которые вернул поиск по данному вопросу?
4. **Переданный генератору контекст.** Оказался ли этот фрагмент в финальном тексте обращения к модели после всех фильтров, ограничений бюджета или подстановки родительских разделов?
5. **Ответ генератора.** Учла ли языковая модель оба условия, если и общее правило, и исключение фактически присутствовали в переданном ей тексте?

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

Чтобы увидеть влияние границ без программирования и настройки баз данных, сделайте одну простую проверку на своем материале. Возьмите знакомый рабочий регламент или инструкцию, сформулируйте один конкретный сравнительный вопрос и выделите маркером все фразы, заголовки и ячейки таблиц, без которых ответ будет неполным или двусмысленным. Затем проведите условные границы, например, по абзацам, разделам или фиксированному числу строк и посмотрите, какие условия остались рядом, а какие разъехались по разным частям. Главное при этом, разделять наблюдение за тем, что написано внутри отдельного фрагмента, и выводы о том, что найдет поиск и как ответит модель.

## Частые вопросы

### Обязательно ли делить на чанки каждый документ для RAG?

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

### Можно ли перенести размер чанка из одного инструмента в другой без изменений?

Сначала нужно проверить единицы и способ подсчёта. В разобранном примере LangChain длина считается в символах, в Cloudflare AI Search размер задаётся в токенах, а перекрытие — в процентах. Microsoft Azure AI Search описывает отдельные символьные и токенные режимы. Одинаковое число в настройках поэтому может означать разный объём текста. Даже при одинаковых единицах подходящий размер зависит от документов, вопросов и используемых моделей.

### Почему перекрытие не всегда сохраняет правило и исключение в одном чанке?

Перекрытие повторяет часть соседнего текста, но вместимость отдельного чанка остаётся ограниченной. Если участок от начала правила до конца исключения длиннее чанка, целиком он туда не поместится. Даже если длины хватает, фактическая граница может пройти между нужными фразами. В библиотечном примере статьи показаны обе причины. Увеличение перекрытия также добавляет повторяющийся текст; оно не гарантирует правильного поиска или ответа.

### Достаточно ли сохранить заголовок раздела в метаданных чанка?

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

### Как понять, из-за чанкинга ли RAG пропустил важное условие?

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

## По теме

- [Как организовать контекст LLM](<https://timofei-ermolaev.ru/llm/context-engineering/>) — Как выбирать сведения для конкретного обращения и проверять, что они действительно переданы модели.
- [Что такое CAG](<https://timofei-ermolaev.ru/llm/chto-takoe-cag/>) — Как заранее подготовленные вычисления по документам используют для новых ответов и чем это отличается от поиска в RAG.

## Первоисточники

1. [Splitting recursively - Text splitter integration guide](<https://docs.langchain.com/oss/python/integrations/splitters/recursive_text_splitter>) — LangChain
2. [Chunk large documents for RAG and vector search in Azure AI Search](<https://learn.microsoft.com/en-us/azure/search/vector-search-how-to-chunk-documents>) — Microsoft
3. [Chunking · Cloudflare AI Search docs](<https://developers.cloudflare.com/ai-search/configuration/indexing/chunking/>) — Cloudflare
4. [Introducing Contextual Retrieval](<https://www.anthropic.com/research/contextual-retrieval>) — Anthropic
5. [Auto Merging Retriever](<https://developers.llamaindex.ai/python/framework/integrations/retrievers/auto_merging_retriever/>) — LlamaIndex
6. [Split markdown - text splitter integration](<https://docs.langchain.com/oss/python/integrations/splitters/markdown_header_metadata_splitter>) — LangChain
7. [Что такое эмбеддинги и как они помогают находить текст](<https://timofei-ermolaev.ru/llm/embeddingi/>) — timofei-ermolaev.ru
8. [similaritySearch | @langchain/core | LangChain Reference](<https://reference.langchain.com/javascript/langchain-core/vectorstores/VectorStore/similaritySearch>) — LangChain
