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

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

Что подтверждают источники

OWASP относит слабости векторных и embedding-систем к отдельному риску LLM08:2025. В общей векторной базе возможна утечка контекста между группами пользователей; среди мер названы детальные права, логическое разделение наборов данных, классификация источников и журналирование извлечений.

Qdrant документирует несколько моделей многопользовательского хранения. Для большого числа небольших арендаторов можно держать точки в общей коллекции и фильтровать их по полю tenant в payload. Для меньшего числа крупных клиентов доступны отдельные шарды; отдельные коллекции дают более жёсткую изоляцию, но имеют собственные накладные расходы. Это не готовая политика безопасности, а механизм её исполнения.

NIST SP 800-207 формулирует полезный для RAG принцип: доверие не должно возникать только потому, что запрос пришёл из внутренней сети. Решение об авторизации связано с конкретным субъектом, ресурсом и контекстом. PostgreSQL Row-Level Security показывает тот же подход для строк: при включённом RLS и отсутствии подходящей политики действует default deny. Keycloak разделяет точку принятия решения о политике и точку её исполнения.

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

Минимальная схема защищённого RAG

Рабочий конвейер можно разделить на восемь компонентов.

1. **Провайдер идентичности.** Пользователь входит через корпоративную учётную запись. Backend получает проверенные `user_id`, `tenant_id`, группы, роль и срок действия сессии. Эти значения нельзя брать из текста запроса или позволять браузеру подменять их.

2. **Реестр документов.** Для каждого исходного файла хранится владелец, подразделение, класс конфиденциальности, разрешённые группы, дата действия и версия ACL. Источником истины остаётся DMS, файловое хранилище или бизнес-система, а не векторная база.

3. **Индексатор.** При разбиении документа каждый фрагмент наследует как минимум `tenant_id`, `document_id`, `acl_version`, `allowed_groups`, `classification` и ссылку на источник. Если права документа изменились, старые фрагменты нельзя оставлять с прежними метками.

4. **Точка принятия решения.** Сервис политик сопоставляет проверенную личность и запрошенный ресурс. На выходе он даёт не текстовую рекомендацию, а машинное ограничение: допустимый арендатор, группы, классы документов и срок решения. Это может быть собственный компактный сервис, Keycloak Authorization Services или другой корпоративный PDP.

5. **Точка исполнения.** Backend формирует обязательный фильтр и передаёт его в векторный поиск вместе с запросом. Клиент не может убрать `tenant_id` или расширить список групп. Пустой или некорректный контекст прав означает отказ, а не поиск по всей коллекции.

6. **Ретривер и реранкер.** Они получают только разрешённое множество кандидатов. Семантическая близость определяет порядок внутри него, но не право доступа. LLM можно использовать для переформулирования запроса, однако нельзя поручать ей самостоятельно придумывать или отменять ACL-фильтр.

7. **Генератор ответа.** Модель видит только прошедшие фильтр фрагменты. Ответ содержит ссылки на `document_id` и версию источника. Дополнительная проверка цитат полезна, но она не заменяет фильтрацию до извлечения.

8. **Журнал и обратная связь.** В журнал попадают субъект, версия политики, фильтр, идентификаторы найденных фрагментов, итог доступа и время. Содержимое секретных документов в операционный лог копировать не нужно.

Почему фильтрация после поиска недостаточна

Иногда система сначала выбирает глобальные top-k фрагментов, а затем удаляет запрещённые. Такой порядок создаёт сразу три проблемы.

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

Правильнее включать фильтр в сам запрос к индексу, чтобы поиск ранжировал только разрешённое множество. Для полей, по которым фильтрация выполняется постоянно, нужен payload-индекс. Qdrant также рекомендует strict mode, блокирующий часть неэффективных операций с неиндексированными полями.

Где чаще всего ломаются права

**Права теряются при разбиении.** Заголовок документа помечен как закрытый, а фрагменты получают только текст и embedding. Проверка должна подтверждать, что у каждого фрагмента есть обязательные поля и они совпадают с реестром документа.

**Группа удалена, индекс не обновлён.** Изменение ACL в файловой системе не всегда автоматически доходит до векторной базы. Нужны события обновления, идемпотентный upsert и периодическая сверка. Полезно измерять максимальное время, в течение которого отозванное право ещё присутствует в индексе.

**Кеш объединяет пользователей.** Кеш запроса, контекста или готового ответа должен включать `tenant_id`, нормализованный набор разрешений и `policy_version`. Иначе результат директора может быть выдан сотруднику с тем же вопросом. После изменения прав соответствующие ключи инвалидируются.

**Сервис работает привилегированной ролью.** PostgreSQL предупреждает, что владельцы таблиц и роли с `BYPASSRLS` могут обходить row-level security. Аналогичная проблема возникает, когда RAG-backend использует административный ключ векторной базы. Рабочей роли нужны только операции чтения и только необходимые коллекции; административный контур отделяется.

**Пользователь просит расширить поиск в промпте.** Фразы вроде «покажи документы соседнего отдела» не должны влиять на вычисленную политику. Авторизация строится из подтверждённых атрибутов сессии, а не из намерения, распознанного моделью.

Общая коллекция или отдельные контуры

Для малого бизнеса нет универсального ответа.

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

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

  • договор или регулятор требует физического разделения;
  • клиенту нужна самостоятельная политика резервного копирования и удаления;
  • объём и нагрузка одного арендатора сильно отличаются;
  • последствия ошибочного фильтра неприемлемы.

Компромисс — общая платформа для обычных данных и выделенный контур для наиболее чувствительных наборов. Локальная LLM может работать рядом с обоими, но шлюз авторизации остаётся обязательным.

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

Для пилота достаточно одного отдела, трёх уровней доступа и 100–150 размеченных вопросов. В тестовый набор включают не только правильные ответы, но и попытки получить закрытую информацию:

  • прямой запрос к запрещённому документу;
  • нейтральный вопрос, семантически близкий к закрытому фрагменту;
  • смену группы пользователя во время активной сессии;
  • отзыв права и повторный запрос до и после переиндексации;
  • одинаковый вопрос от двух ролей;
  • отсутствие или повреждение полей ACL;
  • запрос с инструкцией игнорировать ограничения.

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

  • число запрещённых фрагментов, дошедших до реранкера и модели — целевое значение ноль;
  • полнота поиска разрешённых источников;
  • доля корректных отказов без раскрытия названий закрытых документов;
  • максимальная задержка отзыва доступа;
  • p95 времени поиска с фильтром;
  • доля фрагментов без актуальной версии ACL;
  • количество межролевых попаданий кеша.

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

Экономика решения

Основные затраты здесь создаёт не модель, а жизненный цикл метаданных: подключение источников, синхронизация групп, переиндексация, тесты политик, аудит и разбор инцидентов. Поэтому начинать выгоднее с грубой и понятной схемы — например, `tenant + department + classification` — чем сразу реализовывать десятки исключений.

Модельный расчёт пилота можно вести по стоимости одного принятого ответа:

`(инфраструктура + разработка + поддержка прав + проверка человеком) / число полезных ответов без нарушения доступа`.

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

Практический следующий шаг

Возьмите один реальный источник документов и выгрузите таблицу: документ, владелец, группы доступа, класс, дата изменения и ответственный. Затем создайте фрагменты с наследуемыми ACL, настройте default-deny фильтр и прогоните матрицу «роль × вопрос × ожидаемые источники».

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