Перейти к содержимому
06 июн. 2025 г.·7 мин чтения

Архивные документы в RAG: как не пускать их в топ

Архивные документы в RAG не должны перебивать новые правила. Разберем понижение веса, статусы, даты действия и проверки выдачи на практике.

Архивные документы в RAG: как не пускать их в топ

Почему архив ломает ответы

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

Проблема становится заметнее, когда новая версия короче старой. Свежий регламент может занимать две страницы, а архивный - десять, с примерами, исключениями и ссылками на прежние процессы. Для поиска такой текст часто выглядит "полнее", и он выигрывает ранжирование просто за счет плотности совпадений.

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

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

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

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

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

Какие статусы нужны документам

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

Лучше ввести короткий набор статусов и не расширять его без необходимости. Четырех обычно хватает:

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

Статус "действует" нужен только для того, что команда реально считает текущей нормой. Не стоит ставить его автоматически всем загруженным файлам. Этот статус должен означать одно: по документу сейчас работают.

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

Какие поля хранить рядом с текстом

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

Минимальный набор полей лучше хранить у каждого документа и прокидывать в каждый чанк при индексации:

  • published_at - когда документ выпустили
  • effective_from - когда он начал действовать
  • effective_to - когда он перестал действовать, если это произошло
  • version_id и previous_version_id - номер редакции и ссылка на прошлую
  • is_primary_version и status_source - текущая ли это опорная версия и чем это подтверждено

Пара published_at и effective_from нужна почти всегда. Документ часто публикуют в один день, а применять начинают позже. Для поиска это разные вещи. Если пользователь спрашивает, какое правило действовало 15 марта, система должна смотреть не на дату публикации, а на период действия.

effective_to многие команды не заполняют, и зря. Пустое значение должно означать только одно: документ все еще действует или дата окончания неизвестна. Если срок известен, сохраните его явно. Иначе старая редакция будет выглядеть "живой" и начнет выигрывать поиск по свежести текста или по частоте совпадений.

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

Флаг is_primary_version помогает, когда в цепочке есть одна версия, которую нужно считать основной для обычных запросов. Это особенно удобно для внутренних регламентов, приказов и тарифных правил. Исторические версии остаются доступны, но не лвезут в топ без прямого запроса.

status_source хранит не чье-то мнение, а источник статуса. Например, карточку в СЭД, запись из реестра, приказ о вводе новой редакции или отметку из compliance-системы. Тогда можно объяснить, почему документ считается действующим, архивным или замененным.

На практике этого уже достаточно. Допустим, инструкция v2 все еще хорошо совпадает по словам с запросом, но у нее effective_to = 2024-12-31, а у v3 стоит is_primary_version = true и есть подтверждение статуса. В таком наборе метаданных старый текст не пропадет, но и не перебьет актуальный ответ.

Как понижать вес старых документов

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

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

Как считать итоговый score

Удобно добавить к обычному score несколько множителей:

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

На практике это часто выглядит так: семантический score равен 0.82, документ в архиве получает множитель 0.6, и его итоговый score падает до 0.49. Новый документ со score 0.76, но со статусом "действует" и множителем 1.1 поднимается до 0.84. Поиск выбирает правильный источник, хотя по чистому смысловому совпадению архив был выше.

Свежесть не стоит делать глобальным правилом. Для исторических справок, договоров с фиксированной датой или старых тарифов дата сама по себе не делает документ хуже. А вот для политик безопасности, инструкций, лимитов и регламентов свежесть почти всегда должна влиять на ранжирование.

Архив не нужно удалять полностью. Он нужен для аудита, разбора старых кейсов, ответа на вопросы вроде "что действовало в марте" и проверки, откуда взялась старая формулировка. Но в обычном поиске архив должен проигрывать, если пользователь не просил историю явно.

Полезно и другое: писать причину понижения в логи ранжирования. Не просто хранить финальный score, а фиксировать что-то вроде status=archive, multiplier=0.6 или freshness_penalty=true. Тогда команда видит, почему документ опустился ниже, и может быстро проверить логику.

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

Как настроить систему по шагам

Соберите пилот в рублях
Для B2B команд есть инвойсинг в рублях без наценки на API.

Если настроить это только в одном месте, система все равно будет тянуть архив наверх. Нужна простая цепочка: сначала пометить документы, потом развести типы поиска, затем отрезать лишнее до retrieval и только после этого штрафовать старые тексты при rerank.

  1. Начните с метаданных. У каждого документа должен быть понятный статус: active, deprecated, archived, draft. Рядом храните даты публикации, последнего обновления и, если есть, дату окончания действия. Без этих полей поиск не понимает, что правило от 2022 года уже не равно правилу от 2025 года.

  2. Разделите боевой и справочный контур. В боевом поиске система ищет ответ для пользователя и почти всегда должна брать active, иногда deprecated с низким приоритетом. Справочный контур нужен юристам, аналитикам и аудиту, когда старые версии тоже важны. Если смешать оба режима в одном индексе без правил, архив начнет выигрывать просто из-за точного совпадения слов.

  3. Добавьте фильтр по статусу до ранжирования. Это важнее, чем кажется. Если архивный документ попал в shortlist, даже хороший reranker может поднять его выше свежей версии, потому что текст у него длиннее или формулировки ближе к запросу. Для обычных пользовательских запросов фильтр чаще всего должен пропускать только active.

  4. После фильтра введите штраф на этапе rerank. Не удаляйте архив полностью, если он иногда нужен. Лучше снизить его итоговый балл на фиксированную величину или через коэффициент. Например, active остается без штрафа, deprecated получает небольшой минус, archived - заметный. Так вы не ломаете поиск по истории, но и не даете старым правилам побеждать по умолчанию.

  5. Проверьте спорные запросы на реальных примерах. Возьмите 30-50 запросов, где архив раньше мешал: "лимиты по карте", "срок возврата", "правила KYC для юрлиц". Для каждого запроса вручную отметьте, какой документ должен быть первым, и сравните выдачу до и после настройки.

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

Пример на простом сценарии

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

Сотрудник задает вопрос привычной формулировкой: "Кто согласует договор после финансового отдела?" Запрос почти слово в слово совпадает с текстом прошлогоднего регламента. Если поиск смотрит только на совпадение фраз, архив легко поднимется выше новой версии.

Нормальная настройка ведет себя иначе. Поиск находит обе версии, но новая получает приоритет за счет статуса active, более свежего периода действия и большего веса. Архивная версия остается в выборке, но система снижает ее итоговый балл, потому что у нее статус archived и связь replaced_by с новым документом.

Выдача может выглядеть так:

  • Порядок согласования договоров, редакция 2025, статус active
  • Регламент согласования договоров, редакция 2024, статус archived, заменен новой версией

Дальше генерация ответа опирается на первый документ. Модель пишет актуальный маршрут согласования и, если это полезно, коротко упоминает, что раньше порядок был другим. Старый текст не исчезает совсем, но работает как фон, а не как главный источник.

Это особенно важно, когда пользователь сам не знает, что документ уже заменили. Он задает вопрос привычными словами, а получает ответ по текущим правилам. Так архивные документы в RAG не пропадают из базы, но и не забирают первый ответ у актуальной версии.

В интерфейсе это лучше показать явно. Рядом со старым документом должна стоять метка "Архив" и пометка "Заменен редакцией 2025". Тогда сотрудник видит, почему файл попал в результаты поиска, и не путает его с действующим регламентом.

Если человек спросит "как было раньше", система все равно достанет архив и использует его по делу. Но для обычного вопроса про текущий процесс первой всегда должна идти новая версия. Это простое правило снимает много ошибок еще до ответа модели.

Где команды ошибаются чаще всего

Сравните модели без миграции
Маршрутизируйте запросы к 500+ моделям через один совместимый эндпоинт.

Проблемы обычно начинаются не в модели, а в учете документов. Поиск видит архив и действующие правила как почти равные источники, если команда не описала их статус явно. Тогда старый приказ 2022 года может обогнать новую редакцию 2024 года просто потому, что в нем чаще встречаются нужные слова.

Первая частая ошибка - хранить архив без отдельной метки. Если у документа нет поля вроде active, archived или superseded, система не понимает, что перед ней история, а не норма для ответа. Для архивных документов в RAG это почти всегда заканчивается одинаково: поиск находит текст, который хорошо совпал по словам, но уже не действует.

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

Есть и более грубые проблемы:

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

Склейка версий особенно вредна. В одном куске текста оказываются старые лимиты, новые исключения и несколько дат. Модель потом собирает ответ из смеси фраз и звучит уверенно, хотя опирается на противоречивый фрагмент.

Полное удаление старых файлов тоже плохая идея. История нужна для аудита, разбора инцидентов и вопросов вида "что действовало на дату X". Архив лучше не прятать навсегда, а оставлять в индексе с низким весом и понятным статусом.

Хуже всего, когда команда меряет поиск только на общих запросах вроде "какой порядок согласования". На них система часто выглядит нормально. Настоящие сбои всплывают на спорных вопросах: "что действует сейчас", "какая версия была актуальна в марте", "есть ли более новая редакция". Если тестов на такие кейсы нет, ошибка почти наверняка уйдет в прод.

Быстрая проверка перед запуском

Смените только base_url
Оставьте SDK, код и промпты как есть и переведите вызовы на RU LLM.

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

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

Что проверить вручную

  • Возьмите 5-10 вопросов, где новая и старая редакция противоречат друг другу. В выдаче действующее правило должно быть выше архива.
  • Откройте карточку любого документа и проверьте метаданные. У него должны быть статус, дата начала действия, дата окончания или дата архивации.
  • Найдите пару "старая версия - новая версия". Между ними должна быть явная связь: какой документ заменен и чем именно.
  • Убедитесь, что архив не удален из истории поиска. Он нужен для разбора спорных ответов, аудита и вопросов вроде "что действовало в прошлом году".
  • Посмотрите на тестовый набор. В нем должны быть вопросы по отмененным правилам: "это еще можно?", "какой лимит сейчас?", "какая версия действует после обновления?"

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

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

Нормальный результат перед запуском выглядит просто: новые версии идут первыми, архив доступен для истории, а вопросы по отмененным правилам не уводят поиск в прошлое. Если хотя бы 2-3 теста из десятка дают старую норму в топ-3, риск уже слишком высокий.

Что делать дальше

Начните с документов, где ошибка стоит дорого. Обычно это регламенты, тарифы, договорные шаблоны, внутренние политики и инструкции с датой действия. Если старая версия такого текста попадет в верх выдачи, модель ответит уверенно, но неверно. Для архивных документов в RAG это самый опасный режим.

Полезнее не пытаться сразу разобрать весь массив, а собрать короткий приоритетный список. Достаточно 20-50 документов, по которым команда уже видела путаницу или спорные ответы. На этом наборе быстро видно, где ломается поиск по актуальности и как часто архив обгоняет действующую версию.

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

  • архивная версия не должна выигрывать у действующей по одному и тому же вопросу
  • документ со статусом "заменен" должен вести к новой версии, а не конкурировать с ней
  • дата действия должна влиять на ранжирование, если вопрос касается правил на сегодня
  • ответ должен ссылаться на ту редакцию, которая действовала в нужный период

Без такого набора тестов команда часто видит "нормальный" средний score и пропускает дорогие промахи в реальных запросах.

Еще один шаг, который быстро окупается, - аудит выдачи по статусам и версиям. Смотрите не только на итоговый ответ модели, но и на то, какие документы попали в retrieval: сколько среди них архивных, сколько замененных, сколько без даты окончания. Если архив регулярно оказывается в топ-3, проблему лучше чинить в данных и ранжировании, а не в промпте.

Минимальный аудит можно вести по четырем полям: status, version_id, effective_from, effective_to. Этого уже хватает, чтобы поймать частые ошибки и понять, где индекс или reranker игнорирует смысл документа.

Если вы строите такой контур в РФ, удобно сразу смотреть и на среду выполнения. Например, RU LLM дает единый OpenAI-совместимый эндпоинт и хранит логи и бэкапы в РФ. Для команд, которым важны журналирование, аудит-трейлы и работа внутри российского регуляторного контура, это упрощает разбор спорных ответов и проверку того, где архив все еще просачивается в выдачу.