Пока RAG-система живёт в прототипе, обновление корпуса выглядит просто: перезалить всё заново. На тысяче фрагментов это минуты.
В эксплуатации так не получается. Полная переиндексация означает время, деньги на векторизацию и период, когда система работает на неполном индексе. А документы меняются постоянно: правится прайс, выходит новая редакция регламента, добавляется инструкция.
Задача формулируется так: изменение одного документа должно стоить обработки одного документа. Это достижимо, если заложить в конструкцию заранее — и почти неисправимо, если не заложить.
Что должно быть заложено
Устойчивый идентификатор документа. Не имя файла и не путь: они меняются. Нужен ключ, по которому вы всегда найдёте все фрагменты этого документа, чтобы удалить или заменить их.
Связь фрагмент → документ. Каждый фрагмент помнит, откуда он. Без этого удалить фрагменты одного документа нельзя, и остаётся только полная пересборка.
Отпечаток содержимого. Хеш текста документа. Позволяет за секунды определить, изменился ли он, и не перерабатывать неизменившиеся файлы.
Версия схемы обработки. Записывается вместе с фрагментом: какой моделью векторизован, какими правилами нарезан. При смене правил видно, какие фрагменты устарели, а какие нет.
Метки времени. Дата редакции документа — и как поле для фильтрации, и как признак при разборе конфликтов.
Сценарии обновления
Документ изменился
Обычный случай. Порядок: удалить все фрагменты старой редакции, нарезать и векторизовать новую, записать.
Именно удалить, а не добавить рядом. Самая частая и самая дорогая ошибка эксплуатации — оставить старую редакцию в индексе.
Почему это хуже, чем кажется: старый документ часто написан подробнее — его дорабатывали годами. Поиск находит его чаще нового, потому что в нём больше формулировок, совпадающих с вопросами. Система честно отвечает по документу — по неактуальному. И симптом выглядит как «система стала хуже отвечать», хотя ничего в ней не менялось.
Умный вариант: сравнить фрагменты по отпечаткам и переработать только изменившиеся. Если в регламенте на 40 страниц правился один пункт, векторизуется один фрагмент. Заметная экономия на больших документах.
Документ добавлен
Просто нарезать и записать. Единственная тонкость — не забыть проставить метки доступа и дату.
Документ удалён
Удалить все его фрагменты. Тоже кажется очевидным, а на практике удалённые документы регулярно продолжают жить в индексе месяцами — потому что удаление происходит в системе-источнике, а в индекс приходит только загрузка.
Отсюда правило: обновление должно быть синхронизацией, а не догрузкой. То есть отвечать на вопрос «какие документы есть сейчас», а не «какие появились».
Изменились правила нарезки
Нужно перенарезать корпус, но векторизовать заново — только то, что реально изменилось. По отпечаткам фрагментов часть останется прежней.
Сменилась модель векторизации
Единственный случай, когда переиндексация обязательна целиком. Векторы разных моделей несравнимы, старые и новые в одном индексе не работают — «Эмбеддинги для русского».
Делается через параллельный индекс: строим новый, проверяем на тестовом наборе, переключаем. Система не простаивает.
Как запускать обновление
По событию. Документ изменился в системе-источнике — приходит уведомление, обрабатывается один документ. Самый экономный вариант, требует поддержки со стороны источника.
По расписанию со сверкой. Регулярно сравниваем список и отпечатки документов с индексом, обрабатываем расхождения. Проще в реализации, работает с любым источником, задержка равна интервалу запуска.
Вручную. Кнопка «переиндексировать этот документ». Нужна всегда как запасной путь, даже если есть автоматика.
Разумная схема — сверка по расписанию плюс ручной запуск. По событию добавляют, когда задержка становится критичной.
Разбор: «система отвечает по прошлогодним ценам»
Задача. База знаний торговой компании: прайс, условия, регламенты. Обновление — раз в неделю выгрузкой из учётной системы.
Симптом. Через несколько месяцев работы система начала выдавать неактуальные цены. Не всегда — примерно в трети случаев.
Что показал разбор.
Обновление было устроено как догрузка: новый прайс добавлялся в индекс. Старые версии никто не удалял, потому что процедура удаления не была предусмотрена вовсе.
К моменту разбора в индексе лежало семнадцать редакций прайса. Поиск находил среди них ту, чьи формулировки ближе к вопросу, — и это далеко не всегда была последняя.
Хуже того: у части позиций цена не менялась, и фрагменты были идентичны. Они конкурировали между собой, вытесняя из выдачи фрагменты по другим темам.
Что сделали.
Ввели идентификатор документа и связь фрагментов с ним. Раньше фрагмент не знал, из какого файла он взят, — поэтому и удалить их выборочно было нельзя.
Переделали загрузку в синхронизацию: сверяется список документов и отпечатки, отсутствующие в источнике удаляются из индекса.
Добавили дату редакции как поле и фильтр, отсекающий всё, кроме актуальной, — страховка на случай, если удаление не отработает.
Провели разовую чистку: удалили всё и загрузили заново, уже с правильной схемой.
Добавили в еженедельный отчёт число фрагментов по документам — резкий рост сразу виден.
Что изменилось. Проблема неактуальных ответов исчезла. Побочный эффект: размер индекса сократился более чем вдвое, поиск ускорился.
Вывод. Механизм удаления надо проектировать одновременно с механизмом загрузки. Если этого не сделано, деградация наступает не сразу, а через месяцы — и выглядит как загадочное ухудшение качества, не связанное ни с какими изменениями.
Что проверить в своей системе
- Можно ли удалить все фрагменты одного документа? Если нет — это первое, что нужно исправить.
- Что происходит с документом, удалённым в источнике? Если ничего — он живёт в индексе вечно.
- Сколько редакций одного документа сейчас в индексе? Проверяется запросом. Больше одной — уже проблема.
- Сколько времени занимает обновление одного документа? Если «переиндексируем всё за ночь» — механизм не заложен.
- Есть ли отчёт по числу фрагментов? Резкий рост — признак накопления дублей.
Частые вопросы
Нужно ли переиндексировать весь корпус при изменении одного документа?
Нет, если система спроектирована правильно: у документа есть устойчивый идентификатор, фрагменты помнят, откуда взяты, и удаляются выборочно. Полная переиндексация обязательна только при смене модели векторизации — старые и новые векторы несравнимы.
Что будет, если не удалять старые редакции?
Система начнёт отвечать по устаревшим сведениям, причём непредсказуемо. Старая редакция часто написана подробнее и потому находится чаще новой. Симптом выглядит как постепенное ухудшение качества без видимых причин, и это одна из самых частых проблем эксплуатации.
Как часто нужно обновлять базу знаний?
Не по календарю, а по событию: документ изменился — фрагменты обновлены. Если уведомлений от источника нет, подойдёт регулярная сверка списка документов и их отпечатков с содержимым индекса. Интервал определяется тем, насколько быстро устаревают ваши сведения.
Можно ли обновлять только изменившуюся часть документа?
Да, если хранить отпечатки фрагментов: сравнением видно, какие изменились, и векторизуются только они. На больших документах, где правится один пункт из сотни, экономия существенна.
Что делать при смене модели эмбеддингов?
Строить параллельный индекс новой моделью, проверять его на тестовом наборе и переключаться, когда результаты устраивают. Так система не простаивает и есть возможность вернуться назад, если качество окажется хуже.
Как понять, что в индексе накопился мусор?
Посмотреть число фрагментов по каждому документу и общее число фрагментов в динамике. Если оно растёт, а объём документов нет, значит старые редакции не удаляются. Полезно завести такой отчёт заранее, а не искать причину постфактум.
Что дальше
Обновление тесно связано с нарезкой — «Чанкинг документов» — и с выбором хранилища: в единой базе удаление документа и его векторов происходит в одной транзакции, «pgvector или Qdrant». Если ответы стали хуже без явных причин, начните с проверки редакций — «Почему RAG не находит документ».
Организация обновления корпуса и сопровождение системы — часть работы по разработке и внедрению ИИ и сопровождению решений.