Создание, оптимизация и обновление баз данных для LLM (RAG)

Курс о проектировании и поддержке баз знаний для Retrieval-Augmented Generation: от подготовки данных и индексации до оценки качества и непрерывных обновлений. Разберём выбор хранилищ, стратегии чанкинга/эмбеддингов, гибридный поиск, контроль релевантности и процессы эксплуатации в продакшене.

1. Архитектура RAG и требования к базе знаний

Архитектура RAG и требования к базе знаний

Зачем нужен RAG и что именно мы строим в этом курсе

RAG (Retrieval-Augmented Generation, генерация с дополнением извлечёнными данными) — это архитектура, в которой LLM отвечает не только “из памяти”, но и опирается на найденные фрагменты ваших документов.

Ключевая идея проста:

  • LLM хорошо формулирует ответы, но не хранит ваши актуальные данные
  • база знаний хранит ваши данные и позволяет быстро найти релевантные фрагменты
  • RAG связывает эти две части так, чтобы ответ был точнее, актуальнее и проверяемее
  • В этом курсе мы будем проектировать именно базу знаний для RAG: как её создавать, оптимизировать, измерять качество и обновлять без поломок.

    > В RAG “знание” — это не просто файлы в папке, а управляемый слой данных: структура, чанки, метаданные, индексы, версии и процессы обновления.

    Базовые термины без сложных формул

  • LLM — большая языковая модель, которая генерирует текст.
  • База знаний — подготовленный набор документов и их представлений (чанки, метаданные, индексы), из которого система умеет искать.
  • Чанк — небольшой фрагмент исходного текста (например, 300–800 токенов), который удобно искать и вставлять в контекст LLM.
  • Эмбеддинг — векторное представление текста, чтобы искать “по смыслу”.
  • Ретривер — компонент, который по запросу находит релевантные чанки.
  • Ранжирование — упорядочивание найденных кандидатов так, чтобы наверху были самые полезные.
  • Контекст — тексты, которые мы передаём в LLM вместе с вопросом.
  • Полезные первоисточники:

  • Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks (Lewis et al., 2020)
  • OpenAI Embeddings documentation
  • Типовая архитектура RAG

    В большинстве систем RAG есть два больших контура: ингест (подготовка базы знаний) и онлайн-ответ (поиск + генерация).

    !Общая схема: как данные попадают в базу знаний и как запрос превращается в ответ

    Контур подготовки данных (ингест)

    Что происходит до того, как пользователь задаст вопрос:

  • сбор документов из источников
  • очистка и нормализация текста
  • разбиение на чанки
  • обогащение метаданными
  • построение эмбеддингов
  • индексация (векторная и часто дополнительно текстовая)
  • Контур ответа (онлайн)

    Что происходит во время вопроса пользователя:

  • запрос превращается в эмбеддинг
  • поиск похожих чанков (обычно top-k)
  • (опционально) гибридный поиск: смысловой + ключевые слова
  • ранжирование и фильтрация по метаданным
  • сбор “пакета контекста” для LLM
  • генерация ответа и выдача ссылок/цитат на источники
  • Варианты RAG-архитектуры и когда какой нужен

    Векторный RAG (semantic search)

    Подходит, когда:

  • вопросы формулируются по-разному, но смысл один
  • важны “похожие” формулировки, синонимы, перефразы
  • Риск:

  • можно пропустить точное совпадение термина, если эмбеддинги или чанки подобраны неудачно
  • Гибридный RAG (vector + keyword)

    Сочетает:

  • смысловой поиск (эмбеддинги)
  • текстовый поиск по ключевым словам (например, BM25)
  • Полезен, когда:

  • много артикулов, кодов, названий полей, юридических формулировок
  • важны точные совпадения
  • RAG с метаданными (фильтры)

    Почти всегда нужен в продакшене. Примеры фильтров:

  • только документы конкретного клиента (мультиарендность)
  • только актуальная версия политики
  • только документы после определённой даты
  • только “утверждённые” источники
  • RAG с повторным поиском (multi-hop)

    Используется, если ответ требует нескольких шагов:

  • сначала найти сущность/документ
  • затем уточнить детали во втором поиске
  • Пример:

  • “Какие исключения у тарифа X и какие документы нужны?”
  • Что такое “база знаний” в RAG на практике

    Важно не путать:

  • “у нас есть документы”
  • “у нас есть база знаний для RAG”
  • База знаний для RAG — это минимум:

  • исходники (файлы/страницы/записи) и их версии
  • очищенный текст
  • чанки
  • метаданные
  • эмбеддинги
  • индексы для поиска
  • механизм обновления и удаления
  • наблюдаемость: метрики качества поиска и ответов
  • Требования к базе знаний

    Требование к качеству данных

    База знаний должна быть надёжной, иначе LLM будет уверенно пересказывать мусор.

    Практические правила:

  • убирайте дубликаты (один и тот же документ в нескольких источниках)
  • явно помечайте “черновики” и “утверждённые” материалы
  • исключайте навигацию, меню, “футеры”, если они не несут смысл
  • храните исходную ссылку на документ и место в документе (страница/заголовок)
  • Требование к трассируемости (источники и цитирование)

    Хороший RAG должен уметь объяснить, откуда взялась информация.

    Минимальный набор полей для каждого чанка:

  • source_id (идентификатор источника)
  • source_uri (ссылка/путь)
  • title (заголовок документа)
  • chunk_id (идентификатор чанка)
  • position (страница/смещение/раздел)
  • updated_at (время обновления)
  • Требование к “правильному размеру чанка”

    Если чанк слишком маленький:

  • теряется контекст
  • LLM видит обрывки и делает неверные выводы
  • Если слишком большой:

  • поиск становится менее точным
  • в контекст попадает много лишнего
  • растёт стоимость и задержка
  • Практический ориентир для старта:

  • делайте чанки так, чтобы один чанк содержал одну законченную мысль (правило сильнее, чем “количество символов”)
  • используйте перекрытие (overlap), если смысл часто “перетекает” через границы абзацев
  • Требование к метаданным и фильтрации

    Метаданные — это способ управлять поиском и безопасностью.

    Примеры обязательных метаданных в корпоративном RAG:

  • tenant_id или customer_id
  • access_level (уровень доступа)
  • doc_type (политика, инструкция, договор, FAQ)
  • language
  • valid_from и valid_to (если применимо)
  • Требование к индексам и типам хранилищ

    На практике вы почти всегда используете сочетание хранилищ:

  • хранилище исходников (файлы)
  • хранилище структурированных записей (метаданные, версии)
  • поисковый индекс (векторный, текстовый или гибридный)
  • Сравнение подходов:

    | Подход | Что хорошо | Ограничения | Когда выбирать | |---|---|---|---| | Только векторный индекс | Поиск по смыслу, устойчивость к перефразам | Слабее на кодах/артикулах и точных совпадениях | FAQ, поддержка, статьи, база знаний | | Только текстовый индекс (ключевые слова) | Точность по терминам, быстрые фильтры | Плохо с перефразами и “похожими смыслами” | Юридические тексты, каталоги, коды | | Гибридный поиск | Баланс смысла и точности | Сложнее настройка и оценка качества | Большинство прод-систем | | Граф знаний + RAG | Явные связи, логические цепочки | Дорого строить и поддерживать | Сложные домены, много сущностей и связей |

    Требование к обновлению и удалению (freshness)

    RAG без обновлений быстро “протухает”. Нужно уметь:

  • добавлять новые документы
  • обновлять существующие
  • удалять документы и связанные чанки
  • переиндексировать выборочно, а не “всё всегда”
  • Особенно важно:

  • удаление — чтобы устаревшая политика не продолжала попадать в ответы
  • версионирование — чтобы можно было откатиться
  • !Жизненный цикл: как документ обновляется и как изменения доходят до поиска

    Требование к наблюдаемости и качеству

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

    Минимальные артефакты наблюдаемости:

  • логи запросов и выбранных чанков
  • сохранение “контекста”, который ушёл в LLM
  • оценка релевантности найденных чанков (хотя бы выборочно)
  • метрика “ответ содержит ссылку на источник”
  • Практический принцип:

  • если модель ответила без опоры на источники, это должно быть видно и управляемо (например, “не знаю” или запрос на уточнение)
  • Требование к безопасности

    База знаний для RAG почти всегда содержит чувствительную информацию.

    Минимальные меры:

  • доступ к поиску по tenant_id/ролям
  • запрет смешивания контекста разных клиентов
  • аудит: кто и что спрашивал
  • маскирование персональных данных при необходимости
  • Минимальная “скелетная” схема данных для чанков

    Ниже — пример того, как может выглядеть запись чанка в хранилище (логическая схема, не привязана к конкретной БД):

    Что здесь важно:

  • text — то, что реально попадёт в контекст LLM
  • embedding — то, по чему будет работать смысловой поиск
  • source_uri и position — основа для цитат и доверия
  • tenant_id и access_level — основа безопасности
  • updated_at и version — основа обновлений и откатов
  • Типовые ошибки на старте и как их избежать

  • Ошибка: хранить только PDF и “искать по ним”
  • - Нужны извлечённый текст, чанки, метаданные и индексы, иначе качество и скорость будут нестабильными.
  • Ошибка: не хранить связи “чанк → источник”
  • - Без этого вы не сможете давать ссылки и отлаживать ответы.
  • Ошибка: обновлять базу знаний вручную
  • - В продакшене нужен процесс: обнаружение изменений, переиндексация затронутого, контроль качества.
  • Ошибка: игнорировать метаданные доступа
  • - RAG очень легко “утекает” данными, если фильтры и права не встроены в поиск.

    Что будет дальше в курсе

    В следующих материалах мы последовательно разберём и соберём “правильную” базу знаний:

  • подготовка и очистка данных (ингест)
  • стратегии чанкинга и метаданные
  • выбор индексов и гибридный поиск
  • обновления, версии, удаление и переиндексация
  • измерение качества: как понять, что RAG действительно работает
  • 2. Подготовка данных: очистка, чанкинг, метаданные, версии

    Подготовка данных: очистка, чанкинг, метаданные, версии

    Как эта тема связана с архитектурой 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 и управляемого обновления базы знаний.

    3. Индексация и поиск: эмбеддинги, BM25, гибрид, ранжирование

    Индексация и поиск: эмбеддинги, BM25, гибрид, ранжирование

    Связь с предыдущими темами курса

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

    В RAG обычно используют не один индекс, а сочетание:

  • векторный индекс для смыслового поиска
  • текстовый индекс (инвертированный) для поиска по словам и точным терминам
  • слой ранжирования и фильтрации, который превращает кандидатов в лучший набор контекста
  • !Как запрос проходит через гибридный поиск и ранжирование

    Базовые понятия: что такое индекс и что такое ретривер

    Индекс в поисковых системах — это структура данных, которая ускоряет поиск.

    В RAG чаще всего встречаются два типа:

  • Векторный индекс: хранит эмбеддинги чанков и позволяет быстро искать похожие по смыслу.
  • Инвертированный индекс: хранит слова и ссылки на документы/чанки, чтобы быстро искать по словам и фразам.
  • Ретривер — компонент, который по запросу возвращает список кандидатов (обычно top-k чанков) вместе со score (оценкой релевантности).

    Смысловой поиск: эмбеддинги и векторный индекс

    Что такое эмбеддинги простыми словами

    Эмбеддинг — это числовое представление текста, при котором похожие по смыслу тексты оказываются “ближе” друг к другу.

    Практически это означает:

  • запрос “как оформить возврат товара” может найти чанк с текстом “процедура возврата”
  • запрос “SLA для инцидентов P1” может найти раздел “время реакции при критическом инциденте”
  • Документация по теме:

  • Руководство по embeddings (OpenAI)
  • Как строится векторный поиск

    Типовой процесс:

  • Вы считаете эмбеддинг для каждого чанка и сохраняете его вместе с chunk_id и метаданными.
  • На запросе вы считаете эмбеддинг запроса.
  • Векторный индекс ищет ближайшие эмбеддинги чанков.
  • Вы применяете фильтры и ранжирование, чтобы получить финальный top-k.
  • Почему почти всегда нужен ANN-поиск

    Наивный поиск “сравнить запрос со всеми чанками” плохо масштабируется. Поэтому векторные базы и библиотеки используют ANN (approximate nearest neighbors, приближённый поиск ближайших соседей): вы получаете почти лучшие результаты, но гораздо быстрее.

    Популярный алгоритм для этого — HNSW (граф для быстрого поиска ближайших соседей). Если вы используете векторные БД или векторные индексы в СУБД, HNSW очень часто будет доступен “из коробки”.

    Справка:

  • Hierarchical Navigable Small World graphs (Wikipedia)
  • Практические настройки векторного поиска

    Ниже — настройки, которые чаще всего меняют качество и задержку:

    | Настройка | Что делает | Влияние на качество | Влияние на скорость | |---|---|---:|---:| | top_k | сколько кандидатов вернуть из ретривера | выше top_k обычно повышает шанс найти нужное | выше top_k обычно медленнее | | фильтры по метаданным | ограничивает область поиска | повышает точность и безопасность | чаще быстрее из-за меньшего набора | | стратегия чанкинга | влияет на “попадание смысла” | может сильно улучшить | влияет косвенно | | модель эмбеддингов | влияет на “понимание смысла” | может сильно улучшить | влияет на стоимость вычисления |

    Важный принцип из прошлой статьи:

  • фильтры tenant_id, access_level, is_active должны применяться до выдачи top-k, а не после
  • Поиск по словам: BM25 и инвертированный индекс

    Зачем нужен BM25, если есть эмбеддинги

    Эмбеддинги сильны в перефразах, но у них есть слабые места:

  • точные коды, артикулы, номера ошибок
  • юридические формулировки, где важно конкретное слово
  • запросы вида “поле customer_id” или “ошибка E413
  • Здесь обычно выигрывает классический текстовый поиск, где базовый ранжировщик — BM25.

    Справка:

  • BM25 (Wikipedia)
  • Что такое BM25 простыми словами

    BM25 — способ оценить, насколько текст подходит запросу, учитывая:

  • сколько раз слова запроса встречаются в чанке
  • насколько редкие это слова
  • насколько длинный чанк (чтобы очень длинные тексты не получали преимущество просто из-за объёма)
  • BM25 обычно работает поверх инвертированного индекса: структуры “слово → список документов/чанков, где оно есть”.

    Важные детали текстового поиска в RAG

    Текстовый поиск зависит не только от алгоритма, но и от обработки текста:

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

    Гибридный поиск: как объединять эмбеддинги и BM25

    Почему гибрид почти всегда лучше в продакшене

    Гибрид позволяет покрыть два типа запросов одной системой:

  • смысловые вопросы, где важны перефразы
  • точные вопросы, где важны совпадения слов, кодов и именований
  • При этом гибрид не означает “всё усложнить”. Обычно это 2 параллельных ретривера и аккуратное объединение.

    Два базовых способа объединения результатов

    #### Объединение по рангу (RRF)

    RRF (Reciprocal Rank Fusion) объединяет списки из разных ретриверов так, чтобы документы, высоко стоящие в любом списке, поднимались вверх.

    Плюсы:

  • не нужно калибровать score разных систем (у вектора и BM25 шкалы разные)
  • часто даёт стабильный прирост качества
  • Справка:

  • Reciprocal rank fusion (Wikipedia)
  • #### Объединение по score (взвешенная сумма)

    Вы приводите оценки к сопоставимому виду и считаете итог:

  • final_score = w1 vector_score + w2 bm25_score
  • Где:

  • vector_score — оценка смысловой близости
  • bm25_score — оценка текстового совпадения
  • w1 и w2 — веса (настройки, которые вы подбираете)
  • Этот подход может работать отлично, но требует аккуратной калибровки.

    Дедупликация и разнообразие контекста

    После объединения важно не “забить” контекст дубликатами:

  • одинаковые чанки из дублей документов
  • почти одинаковые чанки из-за большого overlap
  • много чанков из одного source_id, хотя полезнее взять разные источники
  • Практические правила:

  • ограничьте количество чанков на один source_id в финальном контексте
  • удаляйте почти одинаковые чанки по content_hash (из прошлой статьи)
  • если есть canonical_source_id, отдавайте приоритет каноническому источнику
  • !Как технически складываются два списка результатов

    Ранжирование и реранкинг: как сделать top-k действительно лучшим

    Зачем нужен реранкинг

    Первый этап (вектор/BM25) обычно оптимизирован на скорость, поэтому он возвращает кандидатов “достаточно хороших”. Но для RAG важнее другое: чтобы в контекст попали самые полезные для ответа чанки.

    Реранкер — это второй этап, который:

  • берёт кандидатов (например, top-50)
  • пересортировывает их более точной моделью или правилами
  • отдаёт финальный top-k (например, 5–10 чанков)
  • Варианты реранкеров

    | Тип реранкера | Как работает | Плюсы | Минусы | Когда применять | |---|---|---|---|---| | Правила и эвристики | бонус за doc_type, свежесть, “approved” | быстро и прозрачно | ограниченная точность | почти всегда как базовый слой | | Cross-encoder | модель читает запрос + чанк вместе | часто лучше по точности | дороже по времени | когда качество важнее задержки | | LLM-реранкер | LLM оценивает релевантность | гибко, можно учитывать нюансы | дороже и сложнее контролировать | для сложных доменов, low-traffic |

    Ключевой момент: реранкинг не отменяет хороших чанков и метаданных, он усиливает их.

    Ранжирование с учётом свежести и версий

    Чтобы не доставать устаревшее:

  • исключайте is_active=false на уровне фильтра
  • при равной релевантности повышайте документы с более новым updated_at
  • при наличии нескольких версий одного source_id разрешайте поиску видеть только актуальную версию
  • Это напрямую связывает поиск с темой прошлой статьи: версии и “мягкое удаление” должны быть встроены в ретривер.

    Метаданные в поиске: фильтры, поля, бусты

    Метаданные влияют на поиск тремя способами:

  • фильтры: жёсткое ограничение (например, tenant_id=tenantA)
  • бусты: мягкое предпочтение (например, doc_type=policy чуть выше, чем doc_type=chat)
  • фасеты и диагностика: понять, откуда приходит выдача (какие типы документов доминируют)
  • Практические примеры фильтров:

  • tenant_id
  • access_level
  • language
  • doc_type
  • is_active
  • valid_from и valid_to (если документы имеют сроки действия)
  • Практический шаблон: как выглядит запрос в продакшен-RAG

    Ниже — логика, которую полезно реализовать независимо от выбранного движка (векторная БД, поисковый движок, СУБД).

    Пайплайн

  • Нормализовать пользовательский запрос (минимально: пробелы, язык).
  • Применить фильтры безопасности: tenant_id, access_level.
  • Выполнить параллельно:
  • - векторный поиск по эмбеддингу запроса - BM25-поиск по тексту запроса
  • Объединить результаты (например, RRF).
  • Удалить дубликаты и ограничить чанки по одному источнику.
  • Применить реранкер.
  • Собрать контекст:
  • - добавить title, source_uri, position - следить за лимитом токенов
  • Отдать контекст в LLM и потребовать ответ с опорой на источники.
  • Псевдокод

    Как понять, что поиск хороший: минимальные метрики и тесты

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

    Что логировать

  • запрос пользователя
  • применённые фильтры
  • список выбранных chunk_id и их source_uri
  • scores (хотя бы для диагностики)
  • финальный контекст, который ушёл в LLM
  • Как тестировать ретривер

    Практичный старт для команды:

  • Собрать 30–100 типовых вопросов.
  • Для каждого вопроса руками отметить 1–3 “правильных” источника или раздела.
  • Проверять, попадает ли правильный источник в top-k.
  • Метрики можно держать простыми:

  • попадание в top-k: “нужный источник найден или нет”
  • доля дублей в выдаче
  • доля устаревших/неактивных чанков в выдаче (должна быть нулевой)
  • Типовые ошибки при индексации и поиске

  • Ошибка: только векторный поиск без BM25 в домене с кодами и терминами
  • Ошибка: фильтры доступа применяются после top-k
  • Ошибка: нет дедупликации, контекст забит копиями
  • Ошибка: нет учёта is_active и версий, попадает устаревшее
  • Ошибка: нет логов выбранных чанков, невозможно отлаживать качество
  • Чеклист внедрения

  • Векторный индекс построен по эмбеддингам чанков.
  • Текстовый индекс построен по нормализованному тексту чанков.
  • Фильтры tenant_id, access_level, is_active встроены в поиск.
  • Гибрид настроен через RRF или понятную схему объединения.
  • Есть дедупликация по content_hash и лимит на source_id.
  • Есть реранкер (хотя бы эвристический слой).
  • Логи позволяют восстановить: “какие чанки привели к ответу”.
  • Что дальше в курсе

    Следующий шаг после настройки индексов и поиска — сделать качество управляемым:

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

    Оптимизация качества: дедупликация, мультиязычность, оценка и мониторинг

    Зачем нужна оптимизация качества после индексации и поиска

    В предыдущих статьях курса мы построили основу RAG:

  • спроектировали базу знаний и требования к ней
  • подготовили данные: очистка, чанкинг, метаданные, версии
  • настроили индексацию и поиск: эмбеддинги, BM25, гибрид, ранжирование
  • На практике после запуска почти всегда всплывают четыре “невидимые” причины плохих ответов:

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

    !Цикл управления качеством RAG: от данных до мониторинга и улучшений

    Дедупликация: как убрать повторы и противоречия

    Что такое дубликаты в RAG

    Дубликат в RAG — это повторяющийся контент, который приводит к тому, что:

  • в top-k попадают почти одинаковые чанки, “съедая” контекст
  • модель видит две версии одного правила и начинает противоречить сама себе
  • растут задержка и стоимость (больше токенов в контексте)
  • Важное уточнение: дубликаты бывают “полезными” (например, одно и то же правило в политике и в FAQ) и “вредными” (точная копия или старая версия). Задача дедупликации — не удалить всё повторяющееся, а сделать выдачу разнообразной, актуальной и без конфликтов.

    Уровни дедупликации

    | Уровень | Что считаем дубликатом | Типичный источник проблемы | Что делаем | |---|---|---|---| | Источник (документ) | один и тот же документ в двух местах | wiki + PDF + почта | выбираем канон, связываем дубликаты с каноном | | Версии | старая редакция vs новая | обновления без удаления | фильтр is_active=true, управление версиями | | Чанк | одинаковый текст в разных чанках | overlap, повторяющиеся блоки | content_hash, удаление повторов | | Почти-дубликат | текст отличается на мелочах | шаблоны, даты, подписи | “похожесть” (MinHash/LSH), правила очистки |

    Полезные справочные понятия:

  • MinHash — техника для поиска похожих текстов по “отпечаткам” MinHash
  • LSH — семейство методов для быстрого поиска похожих объектов Locality-sensitive_hashing
  • Практический пайплайн дедупликации

    Ниже — минимальная схема, которая хорошо работает в большинстве корпоративных баз знаний.

  • Канонизация источников.
  • - Выберите “главный” источник для каждого типа документов (например, политики только из wiki). - Сохраните в метаданных связь canonical_source_id для копий.

  • Хэширование нормализованного текста.
  • - Для документа и/или каждого чанка считайте content_hash. - Если content_hash совпал, это точный дубликат.

  • Поиск почти-дубликатов.
  • - Применяйте MinHash/LSH, когда есть много шаблонного контента. - Объединяйте в “кластеры похожести”, выбирайте лучший представитель (обычно канон).

  • Дедупликация на выдаче.
  • - Даже если вы чистите данные, overlap и близкие формулировки всё равно дадут повторы. - На шаге сборки контекста удаляйте чанки с одинаковым content_hash. - Ограничивайте количество чанков на один source_id.

    Дедупликация на выдаче: два простых правила

    | Правило | Зачем | Типичная настройка | |---|---|---| | Лимит на source_id | контекст становится разнообразнее | максимум 1–2 чанка с одного документа | | Удаление по content_hash | убирает “копии” из overlap и дублей | не добавлять второй такой же чанк |

    Это особенно важно для гибридного поиска, где один и тот же чанк может прийти и из вектора, и из BM25.

    Как не “перечистить” базу знаний

    Опасные случаи, где дедупликация может навредить:

  • разные документы содержат одинаковое правило, но с разными условиями (регион, продукт, роль)
  • правило одинаковое, но один источник — “официальный”, а второй — “упрощённое объяснение”
  • Чтобы не потерять смысл:

  • учитывайте метаданные doc_type, tenant_id, access_level, valid_from, valid_to
  • удаляйте дубликаты только внутри сопоставимых “сегментов” (например, внутри одного tenant_id и языка)
  • Мультиязычность: как сделать поиск стабильным на нескольких языках

    Какие проблемы приносит мультиязычность

    Если в базе знаний несколько языков, без явной стратегии возникают ошибки:

  • запрос на русском вытаскивает английский чанк, хотя есть русский
  • эмбеддинги “склеивают” языки, и близость становится непредсказуемой
  • BM25 начинает хуже ранжировать из-за неверной токенизации
  • Поэтому мультиязычность — это не “добавим ещё язык”, а отдельный слой дизайна: метаданные, индексы, правила выдачи и оценка.

    Минимальные требования к данным для мультиязычного RAG

    | Что нужно | Как хранить | Зачем | |---|---|---| | Язык чанка | language=ru/en/... | фильтры, правильная обработка | | Язык запроса | определять на входе | выбрать индекс/правила | | Язык источника | в метаданных language + source_uri | цитирование, доверие |

    Стабильный базовый принцип:

  • фильтр по language применяется до top-k, так же как tenant_id и access_level
  • Три стратегии мультиязычного поиска

    | Стратегия | Как работает | Плюсы | Минусы | Когда выбирать | |---|---|---|---|---| | Раздельные индексы по языкам | ru ищем в ru, en в en | предсказуемо, проще отлаживать | больше индексов | 2–5 языков, строгая изоляция | | Один индекс с фильтром language | общий индекс, но фильтр обязателен | проще инфраструктура | ошибки, если забыли фильтр | когда движок хорошо поддерживает фильтры | | Кросс-языковой поиск | запрос на ru может найти en | помогает, если перевода нет | сложнее качество и UX | когда база неполная по языкам |

    Практическое правило для продакшена:

  • сначала ищите в языке пользователя
  • только если результатов мало или они слабые — включайте кросс-языковой “fallback”
  • Эмбеддинги и мультиязычность

    Есть два рабочих подхода:

  • использовать мультиязычную модель эмбеддингов, чтобы запрос на одном языке находил контент на другом
  • использовать отдельные эмбеддинги/индексы по языкам и не смешивать языки без необходимости
  • Независимо от подхода, вам нужна проверка качества на каждом языке (см. раздел про оценку).

    Справочная документация по эмбеддингам: Руководство по embeddings

    BM25 и язык

    BM25 опирается на то, как текст разбивается на слова и нормализуется. Для разных языков могут быть разные настройки токенизации и нормализации.

    Справка: BM25

    Практический вывод:

  • если у вас BM25 по нескольким языкам, проверьте, что поисковый движок корректно обрабатывает эти языки
  • если не уверены, начните с раздельных индексов или чётких фильтров по language
  • Оценка качества: как доказать, что ретривер стал лучше

    Почему “кажется, стало лучше” не работает

    После изменения чанкинга, модели эмбеддингов, гибрида или реранкера система может:

  • улучшить ответы на частых вопросах
  • ухудшить ответы на редких, но критичных сценариях
  • Без измерений вы увидите проблему слишком поздно.

    Что именно оцениваем в RAG

    Разделите качество на два слоя:

  • качество поиска (retrieval): нашли ли мы правильные чанки
  • качество ответа (generation): правильно ли модель использовала найденное
  • Эта статья сфокусирована на первом слое, потому что база знаний и индексы напрямую влияют именно на retrieval.

    Как собрать тестовый набор вопросов

    Минимальный практичный подход:

  • Соберите 30–100 реальных пользовательских вопросов (по логам или интервью).
  • Для каждого вопроса отметьте 1–3 правильных источника.
  • - Лучше отмечать не “идеальный ответ”, а где это написано (документ/раздел).
  • Разделите набор по языкам, продуктам, типам документов.
  • Заморозьте набор как версию: eval_set_v1.
  • Если у вас мультиязычность:

  • сделайте отдельное покрытие для каждого языка (иначе улучшения на одном языке спрячут деградацию на другом)
  • Простые метрики, которые можно внедрить быстро

    | Метрика | Вопрос, на который отвечает | Как трактовать | |---|---|---| | Попадание в top-k | “Нашёлся ли правильный источник в первых k чанках?” | чем выше, тем лучше | | Доля дублей в top-k | “Сколько повторов мы показали?” | чем ниже, тем лучше | | Доля неактивных | “Попало ли устаревшее?” | должна быть нулевой | | Разнообразие источников | “Сколько разных source_id в контексте?” | обычно выше — лучше |

    Чтобы метрики были честными:

  • фиксируйте k (например, 5 или 10)
  • фиксируйте фильтры (tenant_id, access_level, language, is_active)
  • не меняйте тестовый набор вместе с настройками (иначе вы “подгоните” результат)
  • Бенчмарки и идеи, если нужен ориентир

    Если вам нужно посмотреть, как вообще устроены наборы задач для retrieval:

  • MTEB (Massive Text Embedding Benchmark) — большой набор задач для эмбеддингов: MTEB
  • BEIR — коллекция retrieval-наборов: BEIR
  • Обычно вы не можете “в лоб” применить эти наборы к корпоративным документам, но они помогают понять дизайн оценки.

    Мониторинг в продакшене: как заметить деградацию раньше пользователей

    Что такое мониторинг в RAG

    Мониторинг — это регулярный контроль сигналов качества и надёжности. Он отвечает на вопросы:

  • поиск стал хуже после обновления?
  • в выдачу попадает устаревшее?
  • контекст раздувается дубликатами?
  • увеличилась задержка или стоимость?
  • Что логировать обязательно

    Логи — это основа диагностики. Минимальный набор:

  • исходный запрос пользователя
  • применённые фильтры (tenant_id, access_level, language, is_active)
  • список выбранных chunk_id и source_uri
  • признаки дедупликации (сколько удалили по content_hash, лимит по source_id)
  • размер контекста (например, символы или токены)
  • задержка retrieval и общая задержка ответа
  • Важно:

  • не логируйте чувствительный текст без необходимости
  • если нужно — маскируйте персональные данные до записи в логи
  • Сигналы, которые стоит превратить в алерты

    | Сигнал | Почему важен | Типичный алерт | |---|---|---| | Рост доли дублей в top-k | контекст “забивается”, качество падает | “дубликаты > X% за час” | | Появление is_active=false в выдаче | пользователи получат устаревшее | “неактивные чанки обнаружены” | | Падение попадания в top-k на контрольном наборе | деградация поиска | “качество ниже порога после релиза” | | Рост задержки retrieval | индекс/фильтры/нагрузка | “p95 latency выросла” |

    Контроль после обновлений

    В прошлых статьях мы обсуждали версии (source_version, pipeline_version) и выборочную переиндексацию. Для качества важно добавить правило релиза:

  • Перед выкаткой: прогон оффлайн-оценки на контрольном наборе.
  • После выкатки: сравнение метрик “до/после” на том же наборе.
  • В первые часы: усиленный мониторинг дублей, неактивных чанков и задержки.
  • План отката: возможность быстро вернуть предыдущий pipeline_version.
  • !Пример того, как может выглядеть мониторинг качества RAG

    Итоговый чеклист “качество под контролем”

    Данные и дедупликация

  • Есть канонические источники и связь с дубликатами (canonical_source_id).
  • Есть content_hash для чанков.
  • На выдаче работает дедупликация по content_hash.
  • Есть лимит чанков на один source_id.
  • Мультиязычность

  • У каждого чанка заполнен language.
  • Фильтр по языку применяется до top-k.
  • Есть стратегия fallback, если контента на языке пользователя нет.
  • Оценка

  • Есть контрольный набор вопросов с разметкой источников.
  • Метрики фиксированы (например, попадание в top-k, доля дублей).
  • Оценка запускается перед релизами пайплайна.
  • Мониторинг

  • Логи позволяют восстановить: “какие чанки привели к ответу”.
  • Есть алерты на устаревшие чанки, дубли и рост задержки.
  • Есть процедура отката версии пайплайна.
  • Что дальше

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

  • стратегии частичной переиндексации (по документу, по типу, по языку)
  • обновление модели эмбеддингов без остановки системы
  • контроль “дрейфа” качества и плановые пересборки индексов
  • 5. Обновления и эксплуатация: инкрементальные загрузки, реиндексация, безопасность

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

    Как эта тема связана с предыдущими статьями курса

    Ранее мы построили основу базы знаний для RAG:

  • определили требования к базе знаний и трассируемости (источники, позиции, права)
  • подготовили данные (очистка, чанкинг, метаданные, версии)
  • настроили поиск (эмбеддинги, BM25, гибрид, ранжирование)
  • добавили слой качества (дедупликация, мультиязычность, оценка и мониторинг)
  • Теперь нам нужен эксплуатационный контур: как обновлять базу знаний регулярно, безопасно и без деградации качества.

    В RAG почти всегда правда такая:

  • качество “вчера” не гарантирует качество “завтра”, потому что документы меняются
  • обновления пайплайна (чанкинг, очистка, эмбеддинги) неизбежны
  • безопасность должна быть встроена в обновления и поиск, иначе появятся утечки
  • !Жизненный цикл обновлений и эксплуатации базы знаний для RAG

    Что значит “обновлять базу знаний” в RAG

    Обновление — это не “залить новые файлы”. Это гарантировать, что:

  • новые и изменённые документы попали в индексы
  • устаревшее перестало попадать в поиск
  • права доступа применяются всегда и до top-k
  • можно откатиться, если качество или безопасность ухудшились
  • Важная связка с предыдущими статьями:

  • если у вас нет стабильных source_id, chunk_id, content_hash, is_active, source_version, pipeline_version, то безопасных обновлений не получится
  • если фильтры (tenant_id, access_level, language, is_active) не встроены в retrieval, то любая эксплуатация будет потенциальной дырой
  • Инкрементальные загрузки: как обновлять только то, что изменилось

    Почему инкрементальность важнее “пересобрать всё”

    Полная пересборка (все документы → все чанки → все эмбеддинги → все индексы) быстро становится дорогой и рискованной:

  • долго выполняется и блокирует релизы
  • сложнее понять, что именно сломалось
  • выше шанс “потерять” документы из-за временной ошибки парсинга
  • Инкрементальная загрузка делает иначе:

  • обнаруживает изменения
  • переобрабатывает только затронутые источники
  • переиндексирует только затронутые чанки
  • Шаг 1. Обнаружение изменений в источниках

    Есть несколько рабочих способов понять, что источник изменился.

    | Способ | Как работает | Плюсы | Минусы | Где чаще применяют | |---|---|---|---|---| | updated_at из источника | берём поле “обновлён” из CMS/wiki/БД | просто и быстро | иногда дата меняется без смысла или наоборот не меняется | wiki, CMS, базы | | Хэш содержимого | считаем хэш нормализованного текста документа | точнее, чем дата | нужно заново скачать/прочитать текст | файлы, HTML | | Список событий | источник присылает события “создан/изменён/удалён” | почти идеально | нужна интеграция | корпоративные системы | | CDC | читаем изменения из журнала БД | надёжно для таблиц | это про БД, не про PDF | CRM/ERP |

    Если вы используете CDC, типовой ориентир — концепция Change Data Capture (как класс подходов): Change data capture.

    Практическое правило:

  • для “живых” систем (wiki/CRM) обычно достаточно updated_at + периодической сверки хэшей
  • для файлов и HTML лучше иметь хэш, потому что даты и метаданные часто ненадёжны
  • Шаг 2. Идемпотентность: один и тот же документ можно обработать дважды без ущерба

    В продакшене задачи будут иногда повторяться (ретраи, падения воркеров, таймауты). Поэтому обновление должно быть идемпотентным:

  • повторная обработка того же source_id приводит к тому же состоянию
  • На практике это достигается сочетанием:

  • стабильного source_id
  • версий (source_version, pipeline_version)
  • “мягкого удаления” старых чанков через is_active=false
  • Шаг 3. Разделение типов изменений: документ изменился или пайплайн изменился

    Это ключ к экономии.

    | Что изменилось | Признак | Что нужно пересчитать | Что обычно не трогаем | |---|---|---|---| | Изменился документ | новый source_version или новый хэш документа | чанки и эмбеддинги этого source_id | остальные документы | | Изменилась очистка/чанкинг | новый pipeline_version | чанки (и эмбеддинги) выбранного набора документов | индексы для нетронутых типов | | Изменилась модель эмбеддингов | новый embedding_model | только эмбеддинги (и векторный индекс) | BM25-индекс (часто) |

    Шаг 4. Хранение состояния загрузки (watermark)

    Чтобы не гадать “что обновлять”, храните таблицу состояния по источникам. Минимальная идея:

  • что мы видели в источнике
  • что мы уже успешно проиндексировали
  • какие версии были применены
  • Пример логической схемы состояния (упрощённо):

    Как это используется:

  • Коннектор читает “что изменилось” из источника.
  • Сравнивает с ingestion_state.
  • В очередь обработки попадают только изменённые source_id.
  • Шаг 5. Очереди и батчи: как обновлять равномерно и не убивать систему

    Типовые режимы обновлений:

  • микробатчи: раз в N минут забираем изменения пачкой
  • стриминг: обрабатываем события почти в реальном времени
  • Важно не разогнать нагрузку на эмбеддинги и индекс:

  • лимитируйте параллелизм
  • делайте backpressure (если индекс “задыхается”, снижайте скорость)
  • разделяйте приоритеты: критичные документы быстрее, архивы медленнее
  • Если вам нужна оркестрация задач, часто используют планировщики вроде Apache Airflow: Apache Airflow.

    Шаг 6. Обновление и удаление: “тумбстоуны” и мягкое удаление

    Удаление в RAG важно не меньше загрузки, иначе устаревшее продолжит появляться в ответах.

    Рекомендуемая схема:

  • При изменении документа создайте новую партию чанков.
  • Старую партию пометьте is_active=false.
  • В индексы допускайте только is_active=true.
  • Для физического удаления используйте отдельный процесс “компакции” (например, раз в сутки).
  • Если источник сообщает “удалено”, полезно завести запись тумбстоуна:

  • source_id существует как факт
  • но контента больше нет
  • поиск ничего не должен вернуть
  • Реиндексация: когда она нужна и как делать безопасно

    Реиндексация бывает двух типов:

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

    Частые триггеры:

  • вы улучшили очистку и удалили шум (и качество retrieval стало другим)
  • вы поменяли стратегию чанкинга (и chunk_id/границы изменились)
  • вы обновили модель эмбеддингов (и векторный индекс нужно пересчитать)
  • вы изменили схему метаданных и фильтров (например, добавили valid_to)
  • Самая безопасная стратегия: shadow index и переключение

    Идея: построить новый индекс параллельно, проверить, и только потом переключить трафик.

    Плюсы:

  • можно сравнить качество “до/после” на контрольном наборе
  • можно откатиться мгновенно
  • вы не ломаете текущий поиск во время пересборки
  • Минусы:

  • нужно больше места на хранение
  • сложнее эксплуатация
  • !Безопасная реиндексация через теневой индекс и переключение

    Варианты переключения индекса

    | Подход | Как переключаем | Плюсы | Минусы | |---|---|---|---| | Blue-green (A/B) | переключаем “роутер поиска” на новый индекс | быстрый откат | нужно держать два индекса | | Dual-read | читаем из двух индексов и сравниваем | лучше диагностика | дороже по задержке | | In-place | обновляем существующий индекс | проще инфраструктура | риск сломать прод и сложно откатывать |

    Практическая рекомендация для RAG:

  • для обновления эмбеддингов и крупных изменений ранжирования используйте blue-green
  • для маленьких инкрементальных обновлений документов оставьте in-place, но с is_active и версионированием
  • Что делать с chunk_id, если чанкинг поменялся

    Если chunk_id зависит от позиции, а границы чанков изменились, старые и новые chunk_id будут разными. Это нормально, но важно:

  • удалить (или деактивировать) все старые чанки для source_id
  • записать новую партию чанков как активную
  • не допускать смешивания партий в retrieval
  • Минимальный рабочий механизм:

  • группировать чанки по (source_id, source_version, pipeline_version)
  • в поиске разрешать только последнюю разрешённую комбинацию
  • Реиндексация эмбеддингов без остановки системы

    Обновление модели эмбеддингов — частый и дорогой кейс. Безопасная схема:

  • Зафиксируйте новую версию: embedding_model=new.
  • Начните фоновый пересчёт эмбеддингов по очереди (с троттлингом).
  • Записывайте новые векторы в теневой индекс.
  • Прогоните оценку retrieval на контрольном наборе.
  • Переключите поиск на новый векторный индекс.
  • Старый индекс оставьте на “окно отката”, затем удалите.
  • Если вы используете HNSW, полезно понимать, что это за класс ANN-индекса: Hierarchical navigable small world.

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

    Безопасность в RAG — это не только “логин-пароль”. В продакшене важны три слоя:

  • безопасность данных в базе знаний
  • безопасность поиска (retrieval)
  • безопасность эксплуатации (логи, доступы, ключи, деплой)
  • Слой 1. Права доступа должны применяться до top-k

    Это повторим как правило эксплуатации:

  • фильтры tenant_id, access_level, language, is_active должны применяться на этапе поиска кандидатов
  • Если фильтровать “после”, вы рискуете:

  • утечкой данных в логах и метриках ретривера
  • появлением чужих чанков в реранкере
  • кэшированием небезопасных результатов
  • Это напрямую связано с предыдущими статьями про метаданные и мониторинг.

    Слой 2. Мультиарендность и изоляция

    Есть два рабочих уровня изоляции:

  • логическая изоляция: общий индекс, но обязательный фильтр tenant_id
  • физическая изоляция: отдельные индексы и/или базы на арендатора
  • Выбор зависит от риска и требований, но эксплуатационно важно следующее:

  • тесты должны проверять, что запрос из tenantA никогда не получает tenantB
  • мониторинг должен алертить на любые попытки retrieval без фильтра tenant_id
  • Слой 3. Управление секретами и доступами пайплайна

    Пайплайн ингеста обычно имеет доступ к:

  • источникам (wiki, диск, CRM)
  • хранилищу чанков
  • индексам
  • ключам к API эмбеддингов
  • Минимальные практики:

  • принцип минимальных привилегий: у коннектора только чтение, у индексатора только запись в индексы
  • хранение секретов в секрет-хранилище, а не в конфиге
  • короткоживущие токены, ротация ключей
  • Если нужна базовая “шпаргалка” по контролю доступа в веб-системах, у OWASP есть практические материалы: OWASP Access Control Cheat Sheet.

    Слой 4. Логи без утечек

    Логи нужны для отладки retrieval и обновлений, но они же часто становятся источником утечек.

    Что логировать безопаснее:

  • chunk_id, source_id, source_uri, doc_type, updated_at
  • применённые фильтры
  • размеры контекста (токены/символы)
  • технические ошибки парсинга и индексации
  • Чего избегать по умолчанию:

  • полный текст чанков
  • персональные данные из запросов
  • “сырой контекст”, отправленный в LLM
  • Если вам нужны общие принципы безопасного логирования, полезен материал OWASP: OWASP Logging Cheat Sheet.

    Слой 5. Удаление по требованиям комплаенса

    Если у вас есть персональные данные или регулируемые данные, эксплуатационно важно:

  • уметь удалить документ и все его чанки по source_id
  • уметь удалить по “субъекту” (если данные привязаны к пользователю)
  • гарантировать, что удалённое не остаётся в индексах, кэшах и логах
  • Практический подход:

  • “быстрое удаление” через is_active=false
  • “полное удаление” через фоновую компакцию + зачистку кэшей + политику логов
  • Проверки и регламенты: что должно быть в runbook

    Чтобы обновления не превращались в ручную магию, заведите runbook (регламент) как часть системы.

    Минимальные проверки перед релизом пайплайна

  • Прогон оффлайн-оценки retrieval на контрольном наборе (из предыдущей статьи).
  • Проверка, что фильтры безопасности применяются на retrieval (включая тесты).
  • Проверка отсутствия is_active=false в выдаче.
  • Проверка доли дублей в top-k (не должна расти).
  • План отката: какой индекс/версия возвращается и как быстро.
  • Минимальные алерты для эксплуатации

    | Алерт | Что означает | Типичная причина | |---|---|---| | В retrieval попали неактивные чанки | в ответы может прийти устаревшее | сломан фильтр is_active | | Рост доли дублей в top-k | контекст “забивается” | overlap, дубли источников | | Retrieval без tenant_id | риск утечки | ошибка в коде/конфиге | | p95 задержки retrieval вырос | ухудшение UX и стоимость | перегрузка индекса, плохие фильтры | | Очередь обновлений растёт | база знаний отстаёт | не хватает воркеров или квот |

    Пример псевдокода инкрементального обновления

    Что здесь важно концептуально:

  • обновляем только изменённые source_id
  • держим состояние и версии
  • операция допускает повтор (идемпотентность)
  • Итоговый чеклист эксплуатации обновлений

    Инкрементальные загрузки

  • Есть механизм обнаружения изменений (updated_at, хэши, события или CDC).
  • Есть ingestion_state и watermark, чтобы обновлять только изменённое.
  • Обработка идемпотентна (повтор не ломает состояние).
  • Удаление поддержано (тумбстоуны, is_active=false, зачистка индексов).
  • Реиндексация

  • Различаете: изменение документа, изменение пайплайна, изменение эмбеддингов.
  • Для крупных изменений есть shadow index и план переключения.
  • Есть окно отката и хранимые версии (pipeline_version, embedding_model).
  • Безопасность

  • Фильтры tenant_id, access_level, language, is_active применяются до top-k.
  • Логи не содержат лишнего текста и персональных данных по умолчанию.
  • Доступы пайплайна минимальны, секреты не в конфиге.
  • Что дальше

    Теперь база знаний не только “собрана”, но и эксплуатируется как продукт: обновляется инкрементально, переиндексируется безопасно и защищена от типовых утечек.

    Следующий практический шаг (если вы развиваете систему дальше) — углубить управление качеством релизов:

  • A/B сравнение retrieval на прод-трафике
  • автоматическая диагностика причин деградации (чанки, язык, дубли, фильтры)
  • плановые пересборки индексов и контроль дрейфа качества