ИТ-специалист подключает закрытую базу знаний к RAG-поиску. В одном корпусе лежат общие инструкции, финансовые документы и материалы отдельных подразделений. Недостаточно узнать пользователя при входе: система должна отсеять запрещённые документы до retrieval, не передавать их в контекст модели и не вернуть старый ответ из общего кэша после отзыва прав.
Ниже — схема требований и учебная проверка на синтетических данных. Это не отчёт о клиентском внедрении и не результат живого испытания конкретного продукта. Названия S, F, F_user, FIN-01, номера версий и содержимое запросов условны.
Какую задачу решает этот процесс
Читатель этой статьи — ИТ-специалист или владелец закрытой базы знаний. Его результат сформулирован так: пользователь видит только разрешённые фрагменты, включая после изменения доступа.
Для этого недостаточно скрыть ссылку в интерфейсе или добавить в промпт фразу «не раскрывай секреты». Закрытый фрагмент не должен появиться ни на одном из следующих этапов:
- в результатах поиска;
- в контексте, который получает модель;
- в цитатах и ссылках ответа;
- в общем или ранее заполненном кэше.
Граница руководства — авторизация каждого найденного документа и тесты изоляции между ролями. Здесь не рассматриваются маскирование отдельных полей внутри разрешённого документа и роли администраторов n8n. Управление доступом к самому n8n — соседняя задача; её можно разобрать в руководстве о ролях и доступе команды.
Практический критерий прост: если сотруднику запрещён FIN-01, ни точная цитата из него, ни прямая ссылка, ни повтор старого запроса не должны вернуть его текст. Если система не может установить актуальные права, безопасный результат — deny, а не попытка угадать доступ.
Какие данные и правила подготовить
Права RAG-поиска зависят не от одного поля. Нужно связать пользователя, его эффективные группы, ACL исходного документа, метаданные фрагмента, состояние индекса и кэш. У каждого значения должен быть источник и ответственный.
Ниже — заполненный синтетический образец. Все идентификаторы и числа условны, а неизвестные значения нужно определить при обследовании конкретной системы.
| Поле | Условное значение | Источник | Ответственный | Статус |
|---|---|---|---|---|
user_id | S | Каталог пользователей или IdP | Владелец управления доступом | Обязательное |
groups(S) | ∅ | IdP или сервис групп | Владелец управления доступом | Обязательное |
document_id | FIN-01 | Система-источник документа | Владелец корпуса документов | Обязательное |
ACL(FIN-01) | {F} | Права в системе-источнике | Владелец корпуса и владелец доступа | Обязательное |
chunk_id | FIN-01-C1 | Конвейер разбиения документов | Разработчик интеграции | Обязательное |
index_acl | {F} | Метаданные записи в индексе | Разработчик интеграции | Обязательное |
permission_version | 7 | Сервис прав или журнал их изменений | Владелец доступа | Обязательное |
| Состояние отзыва | членство действует | IdP и журнал изменений | Владелец доступа | Обязательное |
cache_key | хеш пользователя, запроса, корпуса и версии прав | Конфигурация приложения и кэша | Разработчик интеграции | Условное: если ответы кэшируются |
| TTL | определяется проектом | Конфигурация кэша | Владелец эксплуатации | Условное: не заменяет отзыв |
| Задержка синхронизации | неизвестна | Замер выбранной интеграции | Разработчик и владелец доступа | Неизвестное до проверки |
| Способ получения групп | неизвестен | API или токен выбранного IdP | Владелец доступа | Неизвестное до проверки |
| Серверная ACL-фильтрация | неизвестна | Документация и тест выбранного индекса | Разработчик интеграции | Неизвестное до проверки |
Если таблица не помещается, прокрутите её вправо. С клавиатуры используйте стрелки.
Обязательное поле нельзя молча заменить пустым значением. Например, отсутствие index_acl не означает, что фрагмент публичный. Безопасное правило: не индексировать такой chunk или исключать его из выдачи до разбора причины.
Условные поля возникают из архитектуры. Если система не кэширует готовые ответы, ей не нужен ключ кэша, но всё равно нужно проверить кэши поискового слоя и промежуточных сервисов. Неизвестные параметры нельзя закрыть общей оговоркой: их превращают в вопросы обследования и критерии приёмки.
Где должны применяться права доступа
Аутентификация отвечает на вопрос «кто пользователь?». Авторизация отвечает на другой вопрос: «какие документы этому пользователю разрешено получить сейчас?». Успешный вход не даёт права читать весь индекс.
Для простой групповой модели учебное правило можно записать так:
allowed(u, d) = true, если effective_groups(u) пересекается с allow_groups(d) и ни одно применимое правило deny не сработало.
Если правила конфликтуют, приоритет deny нужно зафиксировать явно. Реальная модель может учитывать пользователя, группу, подразделение, владельца документа и наследование, но итоговое решение всё равно должно быть вычислено до передачи текста модели.
Безопасная последовательность выглядит так:
- Сервер устанавливает
user_idиз доверенного механизма аутентификации. - Получает актуальные группы и версию разрешений из согласованного источника.
- Добавляет ACL-фильтр к поисковому запросу, чтобы retrieval возвращал только разрешённые документы и chunks.
- Передаёт в контекст модели только результат этого отфильтрованного retrieval.
- Строит цитаты и кэш из того же разрешённого набора.
Фильтр должен применяться сервером. Нельзя принимать список групп из произвольного поля клиентского запроса без проверки. Нельзя сначала получить закрытые фрагменты, передать их модели, а затем просить модель не цитировать их: секрет уже оказался в контексте, а поведение модели не является механизмом контроля доступа.
Официальная документация Azure AI Search описывает document-level access control и security trimming: идентификатор пользователя или группы передают как фильтр, а несовпадающие документы исключаются из результатов. Для встроенных ACL-, RBAC-, SharePoint- и Purview-вариантов в документации указаны отдельные условия, причём часть возможностей имеет статус preview. Это пример документированного подхода конкретной платформы, а не доказательство совместимости с выбранной системой заказчика.
Как перенести ACL в поисковый индекс
Индекс должен хранить связь фрагмента с исходным документом и его правами. Если документ FIN-01 разрешён только группе F, каждый созданный из него chunk наследует это ограничение:
ACL(FIN-01-C1) = ACL(FIN-01) = {F}.
Для сотрудника S в учебном примере:
groups(S) = ∅;ACL(FIN-01-C1) = {F};∅ ∩ {F} = ∅;- следовательно,
allowed(S, FIN-01-C1) = false.
При подготовке записи индекса полезно хранить как минимум document_id, chunk_id, разрешённые субъекты или группы, применимые запреты, источник ACL и версию разрешений. Конкретная схема зависит от поискового движка и модели прав.
Разбиение документа создаёт отдельный риск. Текст может успешно перейти в chunk, а ACL — потеряться. Если пустая ACL трактуется как public, закрытый фрагмент попадёт в retrieval. Это не ошибка модели: дефект возник в конвейере индексации.
Поэтому проверка переноса должна отвечать на четыре вопроса:
- Каждый ли chunk связан с исходным
document_id? - Перенесён ли эффективный ACL с учётом наследования и
deny? - Совпадает ли версия прав в индексе с ожидаемой?
- Что делает система, если ACL отсутствует или не разбирается?
Безопасный ответ на четвёртый вопрос — исключить фрагмент из поиска. После исправления метаданных его можно переиндексировать. Автоматически превращать неизвестное право в public нельзя.
В Azure AI Search документация отдельно отмечает перенос полей разрешений в index projections, если skillset разбивает документы на фрагменты. При выборе этой платформы нужно сверить источник данных, схему индекса, API или SDK и статус функции. Для другого индекса тот же принцип следует реализовать его средствами и проверить отдельно.
Как отозвать доступ и проверить кэш
Отзыв не заканчивается изменением группы в каталоге. Новое состояние должно дойти до сервиса авторизации, индекса или фильтра запроса, цитат и всех слоёв кэша. До завершения этого пути ранее разрешённый ответ может остаться доступным.
В учебной схеме кэш привязан к субъекту и версии прав:
cache_key = hash(user_id, normalized_query, corpus_version, permission_version).
До отзыва условный пользователь F_user входит в группу F, а permission_version = 7. Его разрешённый ответ можно сохранить только в области этого пользователя или эквивалентной области доступа с версией 7.
После удаления F_user из группы F система получает groups(F_user) = ∅ и permission_version = 8. Запись версии 7 больше нельзя читать. Запрос по новому ключу версии 8 даёт miss и снова проходит серверный ACL-фильтр. Закрытый chunk не попадает ни в контекст, ни в новую запись кэша.
Один TTL эту задачу не решает: до истечения срока старый текст остаётся сохранённым. Возможны инвалидация при событии об изменении прав, версия разрешений в ключе или сочетание механизмов. Выбор зависит от доступных событий и требований процесса.
Нельзя обещать нулевую задержку синхронизации. Для конкретной интеграции нужно определить:
- откуда приложение получает группы;
- когда изменяется
permission_version; - как обновляются права в индексе;
- какие кэши существуют между запросом и ответом;
- за какое время отзыв должен стать действующим;
- как система ведёт себя, пока актуальность прав не подтверждена.
Если получить актуальные группы или версию нельзя, безопасный отказ не возвращает спорный документ. Событие фиксируется для разбора, а пользователю дают нейтральный ответ без закрытого текста и без пересказа прежней цитаты.
Отрицательные тесты нужно повторить после первой рабочей версии и после изменений в IdP, индексаторе, схеме chunks или кэше. Проверяют не только основной текст, но и цитаты, заголовки, выдержки, ссылки и сохранённые ответы.
Учебный пример: путь от входных данных до результата
Рассмотрим синтетический пример, а не клиентский кейс и не результат живого теста. Финансовый документ FIN-01 доступен группе F. Сотрудник S в неё не входит. Отдельный условный пользователь F_user сначала входит в F, а затем владелец доступа удаляет его из группы.
Исходные данные:
ACL(FIN-01) = {F};- документ разбит на условный фрагмент
FIN-01-C1; ACL(FIN-01-C1) = {F};groups(S) = ∅;- до отзыва
groups(F_user) = {F}иpermission_version = 7; - после отзыва
groups(F_user) = ∅иpermission_version = 8.
Промежуточное решение принимает сервер. Для S пересечение групп с ACL пусто, поэтому chunk исключается до контекста. Для F_user до отзыва пересечение содержит F, поэтому chunk разрешён. После удаления пользователя из группы пересечение снова пусто, а запись кэша версии 7 недоступна запросу версии 8.
| Роль | Документ | Retrieval | Контекст модели | Кэш | Ожидаемый ответ |
|---|---|---|---|---|---|
S, не состоит в F | FIN-01 | Запрещён: ∅ ∩ {F} = ∅ | FIN-01-C1 отсутствует | Miss; закрытый текст не сохраняется | Сообщить, что доступных материалов по запросу не найдено; не возвращать текст и цитату |
F_user до отзыва, версия 7 | FIN-01 | Разрешён: {F} ∩ {F} = {F} | Разрешённый FIN-01-C1 может попасть в контекст | Запись только в ключе с F_user и версией 7 | Ответ по разрешённому фрагменту с допустимой цитатой |
Тот же F_user после отзыва, версия 8 | FIN-01 | Запрещён: ∅ ∩ {F} = ∅ | FIN-01-C1 отсутствует | Версия 7 недоступна; ключ версии 8 даёт miss | Закрытый текст и прежняя цитата не возвращаются |
Если таблица не помещается, прокрутите её вправо. С клавиатуры используйте стрелки.
Теперь проверим запрос по точной условной цитате из FIN-01. Для S совпадение текста не отменяет ACL: retrieval не возвращает FIN-01-C1. Для F_user до отзыва фрагмент может быть найден и использован. После отзыва тот же запрос не получает старый ответ из кэша и не находит закрытый chunk заново.
Пример показывает важное различие: F — группа, а не пользователь. Отзыв означает удаление F_user из группы F, изменение версии разрешений и недоступность прежней области кэша. Исходный запрет для S и последующий отзыв для ранее допущенного пользователя — две разные проверки.

Как проверить результат перед запуском
До запуска составьте таблицу проверок с входом, ожидаемым результатом, сигналом ошибки и действием человека. Формулировка «модель должна соблюдать права» слишком общая: она не показывает, где возникла утечка.
Следующие сценарии — ожидаемая программа приёмки будущей реализации, а не заявление о выполненном тесте.
| Проверка | Ожидаемый результат | Сигнал ошибки | Действие человека |
|---|---|---|---|
S ищет точную цитату из FIN-01 | Документ, chunk и цитата отсутствуют | FIN-01 появляется в retrieval, контексте, цитате или ответе | Остановить запуск, включить безопасный deny, проверить группы и серверный фильтр |
S открывает прямую ссылку на FIN-01 | Сервер повторно проверяет ACL и не отдаёт содержимое | Ссылка обходит фильтр поиска и открывает текст | Закрыть маршрут, проверить авторизацию объекта и журналировать попытку |
S повторяет запрос, ранее выполненный разрешённой ролью | Общий кэш не возвращает закрытый текст | Появляется ответ, выдержка или цитата другого пользователя | Отключить проблемную область кэша, проверить ключ, разделение и инвалидацию |
F_user до отзыва обращается к FIN-01 | Доступен только разрешённый FIN-01-C1 | Разрешённый фрагмент пропал или добавились документы вне ACL | Проверить источник групп, ACL документа и метаданные chunks |
F_user удалён из F, версия стала 8 | Старый ответ версии 7 недоступен; новый поиск не возвращает документ | Любая часть прежнего текста появляется после отзыва | Закрыть выдачу, проверить обновление групп, версии прав, индекс и кэш |
| Актуальные группы или ACL недоступны | Ответ не строится из спорного документа; событие передаётся на разбор | Система считает неизвестное состояние разрешением | Перевести ветку в deny и восстановить доверенный источник прав |
| В корпусе есть заявка на предоставление доступа | Текст заявки доступен только по её собственным правилам | Модель пересказывает заявителя, причину или маршрут согласования без отдельного разрешения | Исключить заявку из контекста и проверить её ACL независимо от FIN-01 |
Если таблица не помещается, прокрутите её вправо. С клавиатуры используйте стрелки.
Для каждого сценария полезно сохранять не закрытый текст, а техническое решение: идентификатор запроса, user_id, версию прав, применённый фильтр, идентификаторы допущенных документов и причину deny. Состав журнала нужно согласовать с требованиями к чувствительным данным.
Нормальный путь подтверждает, что разрешённый пользователь получает только разрешённый chunk. Отрицательные пути подтверждают изоляцию. Исключение с недоступным источником прав проверяет безопасный отказ. Все затронутые сценарии повторяют после изменения схемы индекса, способа получения групп или кэша.
Перед выпуском критично подтвердить три обходных пути: запрещённый поиск, прямую ссылку и повтор из кэша. Фильтр должен работать на сервере. Общие заявки на доступ нельзя пересказывать только потому, что они упоминают тот же документ: у них самостоятельный объект и собственные права.
Что согласовать для внедрения
Готовый поисковый инструмент может предоставить индекс, фильтры и документированный механизм document-level access. Но он не знает автоматически, какой каталог пользователей считать доверенным, как наследуются права конкретного корпуса, когда считать отзыв действующим и как устроен кэш приложения. Эти связи относятся к собственной интеграции.
Перед выбором решения согласуйте:
- один корпус документов и его владельца;
- источник пользователей, групп, ACL и событий отзыва;
- правила
allow,denyи наследования; - перенос прав на каждый chunk;
- серверный фильтр для всех видов поиска;
- область кэша и механизм инвалидации;
- допустимую задержку синхронизации;
- набор обычных и отрицательных проверок;
- ответственного за спорные права и приёмку.
Совместимость, API, тариф, необходимые роли и статус функций проверяются отдельно для выбранной платформы. Статья не подтверждает готовую интеграцию Switch On AI с Azure AI Search или другим вендором и не доказывает выполненное клиентское внедрение. Для Azure AI Search особенно важно отделять общий security trimming на строковых фильтрах от возможностей, которые официальная документация помечает как preview.
Switch On AI разрабатывает ИИ-помощников и интеграции под конкретную операцию после проверки данных и доступных API. Для этого сценария задача формулируется так: пользователь видит только разрешённые фрагменты, включая после изменения доступа. Если системе достаточно фиксированного поиска и фильтра, агентный выбор действий может не понадобиться; если помощник выбирает источник или следующий разрешённый инструмент, его полномочия задают отдельно.
Правила финансового, бухгалтерского, юридического, кадрового или отраслевого учёта определяет и проверяет специалист заказчика. Техническая система обеспечивает согласованную обработку и контроль доступа, но не заменяет такого специалиста.
Следующий шаг — разобрать модель доступа одного корпуса документов: взять обезличенные примеры разрешённого документа, запрета и отзыва, описать источники прав и согласовать прототип ключевой операции. Прототип не равен полноценному внедрению и не подтверждает работу во всех условиях. Чтобы обсудить исходные данные и критерии проверки, расскажите о задаче Switch On AI.
Вопросы по этой задаче
Можно ли ограничить доступ одним промптом?
Нет. Промпт не заменяет серверную авторизацию. Запрещённый документ нужно исключить из retrieval до передачи фрагмента в контекст модели, а то же решение применить к цитатам, прямым ссылкам и кэшу.
Как учитывать права при разбиении документов?
Каждый chunk должен сохранить связь с исходным документом, его эффективный ACL и версию разрешений. Если обязательные метаданные потеряны, безопасное поведение — deny или отказ от индексации, а не трактовка фрагмента как публичного.
Что делать с уже закэшированным ответом?
Связать кэш с пользователем или эквивалентной областью доступа и permission_version. После отзыва нужно инвалидировать старую запись либо перестать читать её благодаря новой версии прав; затем запрос заново проходит серверный ACL-фильтр.
