AI · 8 мин чтения

Подготовка документов для RAG: чанкинг, чистка, метаданные

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

Зачем вообще готовить документы перед загрузкой в RAG

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

На одном проекте для B2B-каталога с базой из 400 PDF-прайсов я увидел, что без предобработки бот в трети случаев путал цены разных товарных позиций - чанки резались строго по 1000 символов без учёта границ таблиц, и цена от одной строки таблицы попадала в один чанк с названием товара из другой строки. После пересборки чанкинга с учётом структуры таблиц количество таких ошибок упало до единичных случаев за неделю.

Чанкинг: как резать текст, чтобы не терять смысл

Размер чанка - это компромисс между точностью поиска и полнотой контекста. Мелкие чанки (100-200 токенов) точнее находятся по запросу, но модель может не увидеть соседний контекст, нужный для ответа. Крупные чанки (800‑1000 токенов) дают больше контекста, но размывают релевантность - в векторе смешиваются несколько тем, и поиск начинает промахиваться.

Размер чанка Когда использую Минусы
150-300 токенов FAQ, короткие карточки товаров, юридические пункты Теряется контекст между связанными абзацами
300-500 токенов Базовый вариант для большинства баз знаний Иногда режет таблицы и списки посередине
500-800 токенов Документация с длинными пояснениями, инструкции В топ‑5 результатов поиска попадает больше нерелевантного текста

Фиксированный размер vs семантический чанкинг

Простой вариант - резать по количеству токенов с оверлапом 10-15%: конец одного чанка дублируется в начале следующего, чтобы не потерять мысль, разорванную границей. Это быстро реализуется и подходит для однородного текста вроде статей блога.

Для структурированных документов - регламентов, договоров, технической документации - я режу по смысловым границам: заголовкам, пунктам списка, ячейкам таблиц. Библиотеки вроде unstructured или langchain-text-splitters умеют резать по markdown-заголовкам и HTML-тегам, сохраняя иерархию раздела. На практике семантический чанкинг снижает число нерелевантных попаданий в топ‑5 результатов поиска примерно на 20-30% по сравнению с чанкингом по фиксированной длине - проверял на одной и той же базе документов с одинаковым эмбеддером.

Оверлап между чанками

Оверлап 10-15% от размера чанка - рабочий диапазон для большинства задач. Больше 20% раздувает индекс без ощутимого прироста качества, меньше 5% - риск потерять смысл на стыке двух чанков, особенно в текстах с длинными сложноподчинёнными предложениями, как в договорах или регламентах.

def chunk_text(text, chunk_size=400, overlap=60):
    words = text.split()
    chunks = []
    start = 0
    while start  len(words):
        end = start + chunk_size
        chunk = " ".join(words[start:end])
        chunks.append(chunk)
        start = end - overlap
    return chunks

Это упрощённый вариант для черновой нарезки - на реальных проектах я добавляю проверку на границы предложений через регулярки или nltk, чтобы не резать посреди слова или числа в цене.

Бесплатный материал

🎁 Полезный скрипт в подарок

Подпишитесь на Telegram - пришлю готовый скрипт по этой теме.

Без спама. Отписка в 1 клик.

Чистка документов перед индексацией

До чанкинга текст нужно вычистить, иначе мусор просто расползётся по всем кускам. Что обычно убираю:

  • Повторяющиеся футеры и хедеры - колонтитулы PDF, меню сайта при парсинге HTML, подписи вроде «страница 3 из 12»
  • Артефакты OCR - если документ пришёл сканом, распознавание оставляет разорванные слова и случайные символы, их нужно фильтровать регулярками или повторным прогоном через более качественный OCR
  • Дублирующиеся куски - один и тот же абзац политики конфиденциальности встречается в десяти файлах, и без дедупликации по хешу текста он займёт треть индекса
  • HTML-теги и скрипты - если источник это страницы сайта на Tilda или WordPress, в сыром HTML остаются классы, инлайн-стили и куски JS, которые нужно вырезать до превращения в чанки

Дедупликация особенно важна, когда база собирается из разных источников - Confluence, Google Docs и прайсы в Excel одновременно. Я обычно считаю хэш нормализованного текста чанка (нижний регистр, убраны пробелы и пунктуация) и просто не индексирую повтор, если хэш уже встречался. Это экономит место в векторной базе и не даёт модели по пять раз находить одну и ту же формулировку в топе результатов.

Метаданные: что добавлять к каждому чанку

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

Поле Зачем нужно
source / file_name Показать пользователю, откуда взят ответ, и куда смотреть за подробностями
section / heading Понять, к какому разделу документа относится чанк - критично для длинных регламентов
updated_at Отсекать устаревшие версии документов при повторной загрузке
access_level Разграничить, какие чанки можно показывать конкретной роли пользователя
doc_type Фильтровать поиск по типу - договор, инструкция, прайс, FAQ

Поле access_level стало обязательным на проекте с внутренней базой знаний компании: часть документов (зарплатные регламенты, HR-политики) должна была быть видна только определённым ролям в Telegram-боте на aiogram. Без метаданных пришлось бы городить отдельные индексы под каждую роль - с полем access_level в метаданных того же чанка фильтрация делается одним условием при поиске, и векторная база (в том проекте - pgvector) отдаёт только разрешённые куски ещё до того, как текст попадёт в промпт модели.

Форматы источников и подводные камни

PDF - самый капризный формат. Текстовые PDF извлекаются нормально, но таблицы часто разваливаются построчно без сохранения столбцов, а сканы требуют OCR с последующей ручной проверкой на артефакты. Для таблиц в PDF я обычно использую отдельный парсер (camelot или tabula) вместо общего текстового экстрактора - так столбцы цен не перемешиваются со столбцами названий.

Экспорт из Confluence и Notion обычно приходит в виде HTML или markdown с сохранённой иерархией заголовков - это удобно для семантического чанкинга, но нужно вычищать инлайн-комментарии и упоминания пользователей вида @Иванов, которые не несут смысла для поиска.

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

Инструменты и стек для препроцессинга

Для разовой обработки набора файлов хватает Python-скрипта: unstructured или pypdf для извлечения текста, собственная функция чанкинга с учётом markdown-заголовков, dedup по хэшу, запись в JSON с полями метаданных - и дальше загрузка в векторную базу (Qdrant, pgvector, Weaviate).

Когда документы обновляются регулярно - например, каталог товаров или база знаний поддержки пополняется каждую неделю - весь пайплайн имеет смысл автоматизировать в n8n: триггер на новый файл в папке Google Drive или CRM, нода на очистку текста, нода на чанкинг через HTTP-запрос к своему Python-сервису, запись в векторную базу и уведомление в Telegram, что индекс обновлён. Я собирал такую схему клиенту с базой из 1200+ документов поддержки - переиндексация только новых и изменённых файлов вместо полного пересчёта сократила время обновления базы с полутора часов до 10-15 минут.

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

Частые ошибки при подготовке базы знаний для RAG

  • Резать все документы одним и тем же размером чанка независимо от типа контента - прайс-лист и юридический договор требуют разной нарезки
  • Не чистить HTML и не убирать повторяющиеся блоки при парсинге сайтов - модель эмбеддингов начинает считать релевантным футер с телефоном компании
  • Игнорировать метаданные о дате обновления - при повторной загрузке в индексе остаются одновременно старая и новая версия документа, и поиск может отдать устаревшую
  • Хранить персональные данные клиентов из истории обращений в зарубежных облачных таблицах вместо серверов с размещением в РФ - для базы поддержки с контактами это прямое нарушение требований к локализации персональных данных
  • Тестировать RAG только на «удобных» вопросах из презентации, не проверяя пограничные случаи - например, вопрос, который затрагивает сразу два документа с противоречивой информацией

Если нужна помощь с полным циклом - от чистки исходников до сборки бота с базой знаний под ключ, у меня есть отдельная услуга по разработке AI-интеграций и RAG-систем, где я беру на себя весь пайплайн, а не только модель.

Чат-бот с базой знаний

AI / RAG

от 50 000 ₽

Подробнее →

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

Какой размер чанка оптимален для RAG?

Универсального числа нет, но для большинства текстовых баз знаний рабочий диапазон - 300-500 токенов с оверлапом 10-15%. Для FAQ и коротких карточек беру меньше, 150-300 токенов, для документации с длинными пояснениями - до 800. Точный размер я всегда подбираю тестами на реальных вопросах пользователей, а не беру наугад.

Нужно ли обучать отдельную модель эмбеддингов под свою базу знаний?

В подавляющем большинстве проектов - нет. Готовые модели эмбеддингов (от OpenAI, Cohere или открытые вроде BGE) справляются с русскоязычными и англоязычными корпоративными текстами без дообучения. Дообучение embedding-модели оправдано только для узкоспециализированной терминологии - медицина, юриспруденция с редкими формулировками - и это отдельная по объёму задача, а не часть базовой подготовки документов.

Как обновлять базу документов без переиндексации всего с нуля?

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

Можно ли использовать RAG без разметки метаданных?

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

Есть задача?

Обсудим в мессенджере

Расскажите, что нужно сделать — отвечу в течение 4 часов в рабочее время. Первая консультация бесплатно.

Самозанятый Калинкин Н. А. · работаю с физлицами и юрлицами

Продолжая пользование настоящим сайтом Вы выражаете своё согласие на обработку Ваших персональных данных (файлов куки) с использованием Yandex.Metrika.
Понятно