Подготовка данных: очистка, чанкинг, метаданные, версии
Как эта тема связана с архитектурой RAG
В предыдущей статье мы разобрали, что RAG состоит из двух контуров: ингест (подготовка базы знаний) и онлайн-ответ (поиск + генерация). Эта статья целиком про ингест: как превратить разрозненные документы в надёжные чанки с метаданными и версиями, чтобы поиск был точным, а ответы — проверяемыми и актуальными.
Главная цель подготовки данных простая:
сделать текст пригодным для поиска (без мусора и дублей)
сделать фрагменты удобными для извлечения (чанки правильного размера)
сделать результаты управляемыми (метаданные, фильтры, версии, удаление)!Общая схема подготовки данных перед поиском и генерацией
Что именно мы готовим: от документа к чанку
Чтобы дальше говорить на одном языке, определим минимальные сущности:
Источник — конкретный документ или запись: PDF, страница wiki, статья, тикет, карточка товара.
Извлечённый текст — текст источника после парсинга.
Нормализованный текст — текст после очистки и приведения к единому виду.
Чанк — небольшой фрагмент нормализованного текста, который будет искать ретривер и отправлять в LLM.
Метаданные — поля, по которым мы фильтруем, обеспечиваем безопасность и строим цитирование.
Версия — состояние источника на момент времени, чтобы обновления не ломали базу знаний.Практический принцип:
LLM видит чанки, а не “документы целиком”
поиск управляется индексами и метаданными, а не только эмбеддингами
обновления делаются на уровне источник → чанки → индексыИзвлечение текста: чтобы не построить базу знаний из артефактов
До очистки нужно корректно извлечь текст из разных форматов:
PDF: часто содержит переносы строк, колонтитулы, номера страниц
HTML: много навигации, меню, скрытых блоков
DOCX: стили, таблицы, списки
тикеты и чаты: подписи, цитаты, повторяющиеся ответыТиповой стек инструментов:
Apache Tika — извлечение текста и метаданных из множества форматов
Beautiful Soup — очистка HTML от лишних элементов
Unstructured — разбор документов на структурные элементы (заголовки, абзацы, таблицы)Важно: цель извлечения — получить максимально верный смысл, а не “любой текст”. Если парсер даёт кашу, лучше заменить инструмент или источник, чем чинить это уже на уровне чанкинга.
Очистка и нормализация: что удалять, что сохранять
Очистка — это удаление шумов и приведение текста к виду, в котором поиск по смыслу и ключевым словам работает стабильно.
Базовые операции очистки
Ниже — типовые операции, которые почти всегда дают прирост качества.
| Операция | Что делаем | Зачем это в RAG | Риск, если сделать неправильно |
|---|---|---|---|
| Удаление шаблонных элементов | меню, футеры, “контакты”, “поделиться”, хлебные крошки | снижает шум в эмбеддингах и в выдаче | можно случайно удалить важный дисклеймер |
| Нормализация пробелов и переносов | склейка “переносов из PDF”, удаление лишних пробелов | улучшает читаемость чанка и качество эмбеддинга | можно сломать таблицы или код |
| Приведение кавычек, тире, символов | единый набор символов | снижает “разноформатность” похожих текстов | обычно низкий риск |
| Явное выделение структуры | заголовки, списки, подпункты | помогает чанкингу по смысловым границам | при неверной структуре чанк может стать бессмысленным |
| Удаление дублей | одинаковый контент из разных источников | снижает повторение в выдаче и риск противоречий | можно потерять “официальный” источник, если не выбрать канон |
Дедупликация: почему “один текст в двух местах” — это проблема
Если одинаковая политика лежит в wiki и в PDF на диске, RAG может:
доставать дубликаты в top-k и тратить контекст
смешивать разные версии и отвечать противоречиво
цитировать не тот источник, которому доверяютПрактика, которая хорошо работает:
выбирайте канонический источник для каждого типа документа (например, wiki как главный источник)
сохраняйте связь “дубликат → канон” в метаданных
для выявления дублей используйте:
- точные признаки: одинаковый
source_uri, одинаковый
source_id
- текстовые признаки: одинаковый нормализованный текст или его большие совпадающие части
- технические признаки: хэш нормализованного текста (например, SHA-256, как идея; см.
SHA-256)
Что не стоит “чистить” бездумно
Есть элементы, которые часто кажутся мусором, но для ответов критичны:
дисклеймеры и юридические оговорки
исключения и условия действия политики
даты вступления в силу
ограничения по ролям, регионам, продуктамЕсли вы удаляете такие блоки, LLM начнёт отвечать слишком “общо” и ошибочно.
Чанкинг: как резать текст так, чтобы поиск находил нужное
Чанкинг — это разбиение нормализованного текста на фрагменты, которые:
достаточно маленькие, чтобы поиск был точным
достаточно большие, чтобы в них сохранялся смыслПравило “одна законченная мысль”
Лучшее стартовое правило чанкинга:
один чанк должен содержать одну законченную мысль или один шаг инструкцииЭто правило важнее, чем “ровно N символов”, потому что в RAG мы ищем смысловые блоки, а не равные отрезки.
Overlap: зачем нужно перекрытие
Overlap — это небольшой повтор текста между соседними чанками.
Зачем он нужен:
смысл часто “перетекает” через границу абзацев
ключевое определение может стоять в конце предыдущего блокаПрактическая идея:
делайте небольшой overlap, если документ длинный и плотный
избегайте слишком большого overlap, иначе в выдаче будет много почти одинаковых чанков!Как перекрытие чанков сохраняет контекст на границах
Стратегии чанкинга и когда они подходят
| Стратегия | Как режем | Когда хорошо | Типичная ошибка |
|---|---|---|---|
| По фиксированному размеру | по количеству символов или токенов | быстрый старт, однотипные тексты | разрыв смысла посередине правила |
| По структуре документа | по заголовкам, разделам, пунктам | политики, регламенты, статьи | слишком большие разделы без под-деления |
| По абзацам с ограничением размера | добавляем абзацы, пока не достигли лимита | большинство wiki и инструкций | абзацы могут быть слишком короткими без склейки |
| По типам блоков | отдельные правила, таблицы, Q/A | FAQ, базы знаний поддержки | трудно реализовать без хорошего парсинга |
Практический стартовый подход для корпоративной базы знаний:
режьте по заголовкам и абзацам
ограничивайте максимальный размер чанка
добавляйте небольшой overlapСпециальные случаи: таблицы, код, списки
Таблицы и списки часто ломаются при очистке и чанкинге. Рекомендации:
таблицы: старайтесь хранить их в читаемом текстовом виде (строка за строкой) и не смешивать две разные таблицы в одном чанке
списки: держите список целиком, если он короткий; если длинный — делите по смысловым под-блокам
код и команды: не “нормализуйте” агрессивно пробелы внутри, иначе команда перестанет быть копируемойМетаданные: управление поиском, безопасностью и цитированием
Метаданные — это поля, которые вы храните вместе с чанком, чтобы:
фильтровать выдачу (регион, продукт, клиент)
соблюдать права доступа
показывать источники и строить доверие
поддерживать обновления и удалениеМинимальный набор метаданных для продакшена
| Поле | Пример | Зачем нужно |
|---|---|---|
| source_id | doc_83452 | связь чанков с исходным документом, обновления и удаление |
| source_uri | ссылка на wiki/PDF | кликабельная ссылка на источник |
| title | Политика возврата | понятное название в цитате |
| chunk_id | doc_83452#p3#c07 | уникальный идентификатор чанка |
| position | section=Исключения, page=3 | точное место для цитирования |
| updated_at | 2026-01-10T12:34:56Z | свежесть и диагностика |
| language | ru | корректный поиск и выбор модели |
| doc_type | policy, faq | фильтры и разные правила чанкинга |
| tenant_id | tenantA | мультиарендность и изоляция данных |
| access_level | internal | контроль доступа |
Формат времени лучше хранить в ISO 8601, чтобы он был однозначным и сортируемым; см. ISO 8601.
Метаданные для качества и отладки
Эти поля необязательны, но резко упрощают жизнь:
parser и parser_version — чем извлекали текст
chunking_strategy — каким алгоритмом резали
content_hash — хэш нормализованного текста чанка
is_duplicate и canonical_source_id — если нашли дубль
labels — теги вроде approved, draft, deprecatedМетаданные и фильтры должны быть частью поиска, а не “после”
Критичный продакшен-принцип:
фильтр по tenant_id и access_level применяется до выдачи top-kИначе вы рискуете:
утечками данных в логи и ранжирование
случайным смешиванием контекста разных клиентовВерсии и обновления: как не сломать базу знаний при изменениях
Без версионирования база знаний быстро становится недостоверной: старые фрагменты продолжают попадать в ответы.
Два разных понятия: версия источника и версия чанков
Полезно разделять:
версия источника — документ изменился: новая редакция политики
версия чанков — вы поменяли стратегию чанкинга или очистки, и те же документы надо переобработатьЭто разные типы изменений и они требуют разных механизмов.
Практические подходы к версионированию
| Подход | Что храним | Плюсы | Минусы |
|---|---|---|---|
| “Только актуальное” | только последнюю версию чанков | проще и дешевле | сложно расследовать, что было “вчера” |
| “Актуальное + история” | текущие чанки + архив прошлых | можно откатываться и расследовать | дороже хранение и индексация |
| “Мягкое удаление” | пометка is_active=false | безопаснее обновления | нужно следить, чтобы поиск учитывал флаг |
Стартовая рекомендация для большинства команд:
хранить актуальное + историю на уровне источника (хотя бы несколько версий)
в поисковом индексе держать только активные чанкиСтабильные идентификаторы: основа обновлений
Чтобы обновлять и удалять без хаоса, договоритесь о правилах:
source_id должен быть стабильным для “того же документа” (например, ID страницы wiki)
chunk_id должен быть предсказуемым или легко пересчитываемым (например, через source_id + позицию)Тогда операция удаления выглядит просто:
удалить все чанки по source_id
удалить их записи в индексахИнкрементальная переиндексация вместо “пересобрать всё”
В продакшене почти всегда нужно уметь переобрабатывать только затронутые данные:
документ изменился → пересчитать чанки и эмбеддинги только для него
поменялась стратегия чанкинга для doc_type=faq → переобработать только FAQ
обновилась модель эмбеддингов → переэмбеддить постепенно, по очереди!Жизненный цикл версий и выборочной переиндексации
Контроль качества после ингеста: минимальный чеклист
Подготовка данных должна заканчиваться проверками, иначе ошибки попадут в ответы.
Проверки на уровне текста и структуры
доля пустых чанков равна нулю
нет “мусорных” чанков из меню/футеров
язык определён корректно
таблицы и списки читаемыПроверки на уровне метаданных
у каждого чанка заполнены source_id, chunk_id, source_uri, updated_at
у каждого чанка есть поля доступа: tenant_id, access_level
фильтры реально применяются в поискеПроверки на уровне выдачи
по нескольким тестовым вопросам ретривер находит ожидаемые разделы
в top-k нет большого количества дублей одного и того же текста
ссылки и позиции работают, можно открыть источникПример записи чанка с версиями и качественными метаданными
Что здесь особенно важно для обновлений:
source_version меняется, когда изменился документ
pipeline_version меняется, когда вы поменяли обработку (очистку/чанкинг)
is_active помогает безопасно переключать версии (и делать “мягкое удаление”)Типовые ошибки и как их избежать
Ошибка: чистить “всё подряд”
- Делайте белые и чёрные списки блоков, особенно для HTML. Сохраняйте юридические оговорки и условия.
Ошибка: чанкинг без структуры
- Начните с заголовков и абзацев, а не с фиксированного размера.
Ошибка: отсутствие метаданных доступа
- Встройте
tenant_id и
access_level в схему и в запросы поиска с самого начала.
Ошибка: нет версий и нет удаления
- Планируйте операции “обновить документ” и “удалить документ” как обязательные, иначе база быстро станет недостоверной.
Что дальше
После того как данные очищены, нарезаны и снабжены метаданными, следующий шаг — выбрать и настроить индексы и стратегию поиска:
векторный, текстовый и гибридный поиск
ранжирование и борьба с дублями в top-k
оценка качества ретривера и контекстаЭто будет основой для стабильных ответов LLM и управляемого обновления базы знаний.