Загрузчики документов и сплиттеры

Урок про первый шаг RAG: превратить файлы в управляемые куски текста.

Chunk (чанк) — небольшой фрагмент документа, на которые текст режут перед индексацией для поиска.

Зачем загрузчики и сплиттеры

Чтобы модель отвечала по вашим данным, эти данные надо сначала загрузить и нарезать. DocumentLoader читает источник (PDF, веб-страницу, CSV, Markdown) и возвращает список объектов Document с текстом и метаданными. Но целый документ слишком велик для контекста и для точного поиска, поэтому его режут на чанки сплиттером.

Почему именно чанки

Если искать по целым документам, в контекст модели попадёт много лишнего, а релевантный кусок «размоется». Маленькие чанки дают точечное попадание. Но слишком мелкие теряют смысл. Отсюда два параметра: chunk_size (размер куска) и chunk_overlap (нахлёст соседних кусков, чтобы не разрезать мысль на границе).

from langchain_text_splitters import RecursiveCharacterTextSplitter

splitter = RecursiveCharacterTextSplitter(
    chunk_size=500,
    chunk_overlap=50,
)
chunks = splitter.split_documents(documents)

Принцип нарезки с нахлёстом легко показать на чистом Python:

def split(text, size, overlap):
    chunks, start = [], 0
    while start < len(text):
        chunks.append(text[start:start + size])
        start += size - overlap
    return chunks

parts = split("abcdefghijklmno", size=6, overlap=2)
for p in parts:
    print(p)

Вывод:

abcdef
efghij
ijklmn
mno

Как работает под капотом

RecursiveCharacterTextSplitter режет текст не вслепую: он пробует разбивать по «крупным» разделителям сначала (абзацы, затем строки, затем предложения, затем слова), стараясь не рвать смысловые единицы. Нахлёст chunk_overlap повторяет конец предыдущего чанка в начале следующего — так фраза, попавшая на стык, целиком окажется хотя бы в одном чанке. Хорошая нарезка напрямую влияет на качество будущего поиска: мусорные границы дают мусорные ответы.

Частые ошибки

  • Слишком большие чанки. Поиск теряет точность, в контекст лезет лишнее.
  • Нулевой overlap на связном тексте. Мысль рвётся на границе, и ни один чанк её не содержит целиком.
  • Игнорировать метаданные. Источник и страница в metadata пригодятся для ссылок в ответе.

Итог

  • Загрузчик превращает источник в Document с текстом и метаданными.
  • Сплиттер режет документы на чанки для точного поиска.
  • Ключевые параметры — chunk_size и chunk_overlap.
  • RecursiveCharacterTextSplitter старается не рвать смысловые единицы.
Проверьте себя
1. Зачем документы режут на чанки перед индексацией?
AЧтобы уменьшить размер файла на диске
BЧтобы поиск был точечным, а в контекст не лез лишний текст
CЧтобы зашифровать данные
DЭто требование векторной базы по лицензии
2. За что отвечает параметр chunk_overlap?
AЗа размер одного чанка
BЗа нахлёст соседних чанков, чтобы не разрезать мысль на границе
CЗа число документов
DЗа температуру модели