Содержание
Демонстрационный RAG ищет «похожие» фрагменты по всему индексу и уже потом спрашивает, имел ли пользователь право их видеть. В продакшене это инцидент: запрещённый текст успевает попасть в переранжировщик, кэш, трассу и контекст модели, а после отсечения выдача оказывается пустой, хотя разрешённые документы были ниже. Контроль доступа в RAG — это сужение области поиска до извлечения, а не инструкция «не цитируй чужие договоры».
Ниже — практическая архитектура для команд, которые выводят поиск по корпоративным документам в продукт с несколькими арендаторами, ролями и классами конфиденциальности. Статья развивает раздел про метаданные в промышленной инженерии RAG и не повторяет весь конвейер загрузки, гибридного поиска и оценки: здесь одна ось — права до попадания текста в модель.
Устойчивая единица в этом контуре — область доступа, а не «похожий фрагмент».
Ключевые выводы
Сначала допустимое множество, затем сходство. Арендатор, роль, группа и класс данных задают, что вообще можно искать. Релевантность ранжирует только внутри этой области. Модель не является механизмом авторизации.
Фильтр после поиска ломает и безопасность, и полноту. Глобальные первые k результатов часто заполнены чужими документами. После отсечения остаётся мало или ничего, а запрещённые кандидаты уже обработаны приложением.
Изоляция арендатора — не то же самое, что список доступа внутри арендатора. Отдельный индекс, пространство имён или грубый фильтр tenant_id закрывают межарендную утечку. Тонкие права на документ, папку и группу требуют отдельной модели и часто двухступенчатой проверки.
Скрытые каналы важнее «забытого WHERE». Переранжировщик, семантический кэш, журналы, эталонные наборы и цитаты «этот документ существует» раскрывают данные без ответа пользователю.
Права стареют быстрее индекса. Отзыв роли, увольнение, смена группы и удаление файла обязаны инвалидировать кэш и, при денормализации, обновить атрибуты точек — иначе система честно ищет по вчерашней политике.
Что такое контроль доступа до извлечения
В обычном веб-приложении авторизация стоит перед чтением строки: SQL с ограничением на уровне строк, проверка объекта в сервисе, отказ 403. RAG ломает эту привычку, потому что «чтение» выглядит как приближённый поиск по векторам. Запрос не называет идентификатор документа. Он просит похожее. Если индекс глобальный, похожее почти всегда найдётся — в том числе в чужом арендаторе.
Поэтому список управления доступом (ACL) в RAG — не декоративное поле рядом с эмбеддингом. Это часть поискового контракта. Без него векторная база отвечает на другой вопрос: «что семантически близко в мире всех точек», а не «что близко в мире этого пользователя».
Практическая граница проходит так: любой компонент, который видит текст фрагмента — первичный поиск, объединение рангов, переранжировщик, сборщик контекста, генератор, кэш, журнал, набор для оценки — находится внутри контура авторизации. Если фрагмент туда попал, считается, что его уже «прочитали». Надеяться, что модель «не станет цитировать» запрещённое, нельзя: это вероятностное поведение, а не контроль.
Тема стыкуется с организационной схемой в архитектуре корпоративного RAG: владелец источника, класс данных и роль читателя должны быть известны до индексации, а не восстанавливаться из промпта.
Чем это не является
Это не «промпт с правилами». Текст «ты не должен раскрывать персональные данные» не заменяет фильтр. Модель может ошибиться, её можно убедить соседним фрагментом, а запрещённый текст уже стоит в счёте токенов и в логе поставщика.
Это не то же самое, что защита от промпт-инъекции. Список доступа отвечает на вопрос «имел ли субъект право видеть этот источник». Инъекция отвечает на вопрос «может ли разрешённый текст заставить сценарий сделать лишнее». Оба контура нужны. Закрытый чужой договор не должен попасть в поиск; свой вредоносный PDF всё равно обязан быть ограничен правами агента.
Это не только tenant_id. Мультиарендность закрывает границу между клиентами продукта. Внутри одного клиента юрист, бухгалтер и подрядчик видят разные папки. Смешение этих задач в одном поле «организация» даёт ложное чувство безопасности.
Почему «найти, потом выкинуть» не работает
Схема выглядит соблазнительно: взять первые 50 соседей по всему корпусу, выкинуть чужие, отдать модели десяток. На маленьком демо с одним арендатором она «работает». На реальном распределении — нет.
Голод первых k результатов
Приближённый поиск оптимизирован на глобальную близость. Если 95 % индекса принадлежит другим арендаторам или закрытым папкам, топ выдачи заполняется запрещённым. После фильтра остаётся два фрагмента вместо десяти — или ноль. Пользователь видит «я не знаю», хотя в его области есть точный регламент. Команда повышает top_k, растёт задержка, в приложение попадает ещё больше чужого текста, а полнота внутри разрешённого множества так и не измеряется.
Это не теоретический край. Избирательный фильтр — норма для SaaS: маленький арендатор, узкая роль, свежий документ на фоне большого общего корпуса.
Утечка в компоненты приложения
Даже если API наружу ничего не вернул, запрещённый фрагмент уже мог уйти в:
- вход переранжировщика (в том числе внешнего
LLM); - ключ или значение семантического кэша;
- атрибуты трассы
OpenTelemetry/LangSmith; - тело запроса к поставщику модели;
- офлайн-набор «для отладки качества».
Инцидент начинается не с экрана чата, а с побочного хранилища. Поэтому проверка «пользователь не увидел чужое» недостаточна. Нужен критерий: запрещённый текст не обрабатывается за пределами доверенного контура авторизации.
Модель вне границы доверия
Любая инструкция вида «если документ не принадлежит пользователю, игнорируй его» предполагает, что модель — судья. Она им не является. Граница доверия — ваш сервис, который не передаёт запрещённое дальше. Генератор получает только уже отфильтрованный пакет доказательств.
Три модели изоляции арендаторов
Межарендная граница почти всегда грубее, чем ACL внутри клиента. Её стоит проектировать отдельно, иначе команда пытается одним полем groups закрыть и «чужой холдинг», и «чужой отдел».
Ниже — три рабочих модели. Выбор зависит от числа арендаторов, перекоса размеров, требований регулятора и цены эксплуатации, а не от любимого вендора.
| Модель | Что изолирует | Когда уместна | Чем платите |
|---|---|---|---|
| Отдельный индекс / коллекция | Физически разные данные | Мало крупных клиентов, жёсткий контур, разные регионы | Операции, миграции схемы, холодный старт |
| Пространство имён, шард, «тенант» движка | Логическая перегородка внутри кластера | Сотни–тысячи арендаторов, сопоставимый размер | Лимиты движка, горячие/холодные шарды |
Общий индекс + обязательный фильтр tenant_id |
Логическая изоляция запросом | Много мелких арендаторов, единая схема | Риск забыть фильтр, влияние соседа на граф HNSW |
Отдельный индекс даёт самую понятную историю для аудита: этот клиент не делит диск и кэш с другим. Цена — сотни коллекций, разные версии схемы, сложный деплой модели эмбеддингов. Имеет смысл, когда арендаторов мало, они крупные или обязаны жить в своём контуре.
Пространства имён (как в Pinecone), тенанты Weaviate и ключи партиций в Milvus — компромисс: движок знает, что запросы не должны смешивать шарды. Qdrant в одном индексе часто идёт через атрибут группы с индексом полезной нагрузки и флагом «это арендатор»: точки одного клиента оказываются рядом на диске, фильтр дешёвый. Это всё ещё логический контур: ошибка в клиентском коде, который забыл передать идентификатор, опаснее, чем в модели с отдельной коллекцией на каждый ключ.
Общий индекс с фильтром масштабируется на массу мелких клиентов, пока команда дисциплинированно проталкивает tenant_id на каждом пути: запись, поиск, удаление, пересчёт, выгрузка для оценки. Одна «служебная» ручка без фильтра превращает платформу в общий корпус.
На практике часто гибрид: топ-N крупных арендаторов — свои коллекции; длинный хвост — общий индекс с обязательным предварительным фильтром и проверкой в CI, что нефильтрованный поиск в коде отсутствует.
Перекос и «шумный сосед»
Даже при корректном фильтре большой арендатор влияет на граф приближённого поиска и на очереди индексации. Если один клиент заливает миллионы фрагментов, мелкий сосед получает хуже задержку и хуже полноту при том же ef. Имеет смысл резать по размеру: квота на документ, отдельный шард, отдельная очередь загрузки. Мультиарендность — ещё и экономика железа, не только ACL.
Цепочка: идентичность, область, поиск
Рабочий запрос в промышленном RAG — не «эмбеддинг → top_k». Это конвейер с явными шагами и следом решения.
1. Аутентификация субъекта
Сервис знает, кто спрашивает: пользователь, сервисный аккаунт агента, фоновая задача переиндексации. У фоновых задач своя роль: они могут читать сырьё для загрузки, но не обязаны иметь право отвечать в чате от имени человека. Смешение этих субъектов — частый источник «индексатор видел всё, чат тоже».
2. Расширение групп и атрибутов
Из поставщика идентичности (OIDC, корпоративный каталог) сервис получает роли, группы, отдел, регион, признаки подрядчика. Не тащите сырой JWT в фильтр векторной базы: срок жизни токена, вложенные группы и вложенные папки редко совпадают с тем, что лежит на точке. Постройте область доступа: нормализованный набор идентификаторов групп плюс версия политики.
Версия политики нужна кэшу и аудиту: «этот ответ получен при политике v17». После отзыва роли версия растёт, старые ключи кэша не попадают.
3. Грубый предварительный фильтр в движке
Минимум, который обязан уйти в поисковый запрос: tenant_id (или эквивалент пространства имён) и, если есть, класс конфиденциальности / контур (персональные данные, коммерческая тайна, внутреннее). Это сужает граф до обхода соседей. Без этого шага дальнейший тонкий ACL работает по отравленной выборке.
Если движок умеет планировать фильтр — оценить кардинальность предиката и при очень узком множестве перейти к полному перебору по отфильтрованным точкам — используйте это и измеряйте. Слепой HNSW по «островкам» фильтра теряет полноту; слепой полный перебор по миллионам точек убивает задержку. Планировщик нужен не как магия, а как наблюдаемое решение.
4. Поиск только в допустимой области
Лексический канал, векторный канал и их слияние рангов работают после грубого фильтра. Иначе BM25 по общему лексическому индексу легко поднимает чужой договор, в котором совпали номер и формулировка.
Переписывание запроса и история диалога не расширяют область доступа. Они уточняют намерение внутри уже вычисленной области. Если в истории мелькнул идентификатор чужого документа, это не повод искать его «для контекста».
5. Тонкая проверка перед сборкой контекста
Группы на точке стареют. Сложная политика «папка + проект + исключение подрядчика + срок NDA» плохо живёт в денормализованном массиве. Поэтому зрелый контур часто двухступенчатый: грубый фильтр в индексе (арендатор, класс, широкие группы) и окончательная проверка актуальной политики — локально или через сервис отношений (OpenFGA, SpiceDB и аналоги) — перед тем как фрагмент попадёт в переранжировщик и генератор.
Отказ проверки закрывает доступ. «Сервис авторизации не ответил, поищем без фильтра» — это открытая дверь, а не устойчивость.
Избыточная выборка здесь уместна: взять из движка в два–три раза больше кандидатов, чем нужно модели, чтобы тонкий фильтр не опустошил пакет. Но избыток считается внутри грубой области, а не по всему миру.
Списки доступа на уровне документа и фрагмента
Точка в индексе — обычно фрагмент, а не файл. Политика почти всегда живёт на документе, папке или карточке в ECM. Это расхождение — главный источник ошибок.
Документ как источник истины для прав
При загрузке копируйте на каждый фрагмент устойчивый document_id, владельца, группы чтения и признаки удаления. Поиск может фильтровать по этим полям. Отзыв доступа к файлу должен обновлять все его фрагменты или снимать их с индекса одной транзакцией загрузки. Частично обновлённый документ — дыра: старый абзац ещё доступен, новый уже нет, или наоборот.
Иерархия папок: либо материализуйте итоговый список групп на документе при каждом изменении дерева, либо проверяйте путь в тонкой стадии. Держать на фрагменте только folder_id и на каждом запросе ходить в дерево из десяти таблиц — прямой удар по задержке.
Когда нужен ACL на фрагмент
Имеет смысл, если внутри одного файла разные грифы: публичная сводка и закрытое приложение, таблица с персональными строками внутри общего отчёта. Тогда нарезка обязана резать по границам политики, а не только по длине токенов. Иначе «безопасный» абзац тащит в родителя закрытую таблицу — классика подготовки корпуса.
Если политика на весь файл одинакова, не плодите уникальные списки на тысячах фрагментов: вы только увеличите стоимость обновления прав.
Роли, атрибуты, отношения
Доступ на основе ролей удобен, пока ролей мало и они стабильны: legal, finance, contractor. Доступ на основе атрибутов лучше стыкуется с грифом, регионом и сроком действия документа. Доступ на основе отношений (ReBAC) нужен, когда право вытекает из графа: «участник проекта видит артефакты проекта, пока состоит в команде».
Для RAG опасна попытка на каждом запросе вызвать «дай все документы пользователя» по миллионному графу. Выборка полного множества идентификаторов часто дороже самого поиска. Рабочая схема: грубые атрибуты на точке + пакетная проверка выживших кандидатов, а не инверсия графа в предварительный фильтр из ста тысяч id.
Окно рассинхронизации
Денормализация групп на точке создаёт окно: IdP уже отозвал роль, индекс ещё не обновлён. Сожмите окно событиями (очередь «членство изменилось» → пересчёт затронутых документов), держите версию политики в ключе кэша и на тонкой стадии всегда спрашивайте актуальную политику для финального пакета. Для самых жёстких контуров тонкая стадия — единственный источник истины, индекс лишь отсекает заведомо чужое.
Удаление пользователя — отдельный тест: сессии убиты, кэш по его области сброшен, фоновые агенты с его токеном не продолжают ходить в поиск.
Скрытые каналы утечки
Команда чинит фильтр в запросе к Qdrant и считает задачу закрытой. Данные уходят стороной.
Переранжировщик
Перекрёстный кодировщик и тем более переранжировщик на LLM читают текст. Если в пул попал чужой фрагмент «на всякий случай, потом отфильтруем», вы уже отправили его в другой процесс или к поставщику. Правило то же, что для генератора: в переранжировщик попадает только допустимое множество.
Кэш ответов и семантический кэш
Ключ «нормализованный вопрос → ответ» без арендатора и версии политики отдаёт чужой ответ коллеге с похожей формулировкой. Даже внутри одного арендатора юрист не должен получать кэш, собранный для финансиста. Подробный разбор — в материале про семантический кэш и арендаторов. Для RAG-кэша в ключ входят: арендатор, область доступа или версия политики, версия индекса/корпуса, локаль, идентификатор маршрута генерации.
Попадание кэша при отозванном праве — отдельная метрика, не «экономия токенов».
Журналы, трассы, эталонные наборы
Отладочная трасса с полным текстом найденных фрагментов — второе хранилище персональных данных. Применяйте те же сроки хранения, маскирование и контроль доступа, что к боевым документам. Эталонный набор для оценки не должен содержать чужие для разметчика договоры «потому что так удобнее считать полноту». Размечайте в пределах роли разметчика или на синтетическом корпусе с искусственными границами.
Цитаты и факт существования
Даже отказ может утечь. «Я не могу ответить по договору №4412» сообщает, что договор есть. Для жёстких контуров политика отказа одинакова: «в доступных вам источниках этого нет», без идентификаторов, которые пользователь не имел права знать. То же для автодополнения и «похожих вопросов».
История диалога и переписывание
Многоходовый чат накапливает сущности. Переписывание «расскажи подробнее про тот отчёт» не должно расширять поиск за пределы текущей области, даже если в прошлом ходе модератор вставил исключение. История — источник намерения, не дополнительный ACL.
Поисковые движки: фильтры, полнота, компромиссы
Вендоры обещают «фильтруемый векторный поиск». За формулировкой скрываются разные алгоритмы, и от выбора зависит, получите вы пустую выдачу или дырявую.
Предварительный фильтр в наивной форме: взять все точки, удовлетворяющие предикату, и искать среди них — хоть полным перебором. Это корректно и убивает бюджет задержки на миллионах векторов.
Постфильтр: обойти HNSW как обычно, потом выкинуть неподходящее. Граф остаётся связным, полнота по глобальному сходству хорошая, полнота по разрешённому множеству — нет.
Современные движки пытаются идти по графу, не теряя связность на островках фильтра. Weaviate описывает стратегию ACORN: расширять окрестность на два шага, чтобы обходить неподходящие узлы, не требуя заранее известных предикатов. Qdrant добавляет связи графа по проиндексированным атрибутам и при узком предикате переходит к полному перебору по маленькому множеству. Lucene и Elasticsearch развивают родственные идеи для kNN с фильтром. pgvector с итеративными сканами уменьшает голод выборки в PostgreSQL — там же естественно лежит ограничение на уровне строк для транзакционного контура.
Ни один из этих механизмов не снимает с вас обязанность измерить полноту на своих фильтрах. Избирательность 1 % и 40 % ведут себя по-разному. Перекос арендаторов ломает картину, которую показали в блоге вендора на равномерных метках.
Практические следствия:
- для границы арендатора предпочитайте механизм, который движок считает изоляцией (пространство имён, тенант, отдельная коллекция, индекс с
is_tenant), а не «надеемся, что постфильтр хватит»; - для тонкого
ACLне ждите, что графHNSWидеально переживёт предикат из двадцати групп — держите грубый слой в индексе и тонкий слой после избыточной выборки; - гоняйте срез запросов с реальной кардинальностью: роль с тремя документами и роль с третью корпуса.
Сравнение шлюзов и баз — отдельное решение со сроком пересмотра, как в опорном руководстве по промышленному RAG. Здесь достаточно правила: если вы не можете объяснить, на каком этапе отсекается чужой арендатор, у вас нет контроля доступа.
Как измерять утечки и качество поиска
Безопасность без полноты превращается в вечный отказ. Полнота без безопасности — в инцидент. Оценивайте оба свойства на одном контуре.
Набор враждебных случаев
Минимум, который должен жить в релизном наборе, а не в головах:
- запрос, на который лучший глобальный сосед — чужой арендатор, а правильный ответ есть у своего;
- два арендатора с почти одинаковыми договорами (копипаст шаблона);
- пользователь без роли после отзыва группы — с задержкой 0, 30, 300 секунд;
- удалённый пользователь и живая сессия;
- документ сменил гриф, фрагменты частично переиндексированы;
- пустая область (новый сотрудник, нет файлов) — ожидаем честный отказ, не «галлюцинацию из соседнего отдела»;
- агент с сервисной ролью не отвечает в пользовательском чате полным корпусом.
Для каждого случая храните не только эталонный ответ, а идентификаторы допустимых фрагментов и признак, что недопустимые не должны появиться ни в трассе, ни в пакете доказательств.
Метрики
Полнота и точность поиска считайте внутри допустимого множества, иначе вы оптимизируете глобальный сравнительный тест и деградируете продукт. Отдельно: доля запросов с утечкой (недопустимый chunk_id в любом артефакте этапа), доля пустых выдач при ненулевом допустимом корпусе, время сходимости после смены членства, попадания кэша после отзыва.
Не просите языковую модель решать, «похоже ли это на утечку». Сравнение идентификаторов детерминировано. Судья уместен для качества формулировки после того, как контур доступа зелёный.
Связка с эталонным набором RAG: добавьте слой разметки «этот вопрос запрещён для роли X» так же явно, как слой «этот фрагмент — доказательство».
Наблюдаемость
В трассе должны быть: идентификатор субъекта, арендатор, версия политики, тип фильтра (пространство имён / атрибуты точки / отдельный индекс), кардинальность грубого множества если известна, число кандидатов до и после тонкой проверки, признак отказа авторизации. Тексты фрагментов в трассу — по грифу и с тем же списком доступа, что у продукта, либо не класть вовсе.
Типичные ошибки и план на четыре недели
Типичные ошибки
Фильтр только в приложении после top_k. Выглядит быстро в прототипе. В проде даёт пустые ответы и утечки в побочные системы.
Один tenant_id вместо модели ролей. Клиенты изолированы, сотрудники внутри клиента видят всё. Для ERP и юридического контура этого мало.
ACL как текст в промпте. Красиво в демо, бесполезно против ошибок модели и против логов поставщика.
Кэш по тексту вопроса. Экономит деньги и раздаёт чужие ответы. Ключ без области доступа считается дефектом, не оптимизацией.
Права только на загрузке, без событий отзыва. Индекс помнит уволенного дольше, чем кадровый контур.
Служебные скрипты оценки без фильтра. Аналитик выгрузил «весь продакшен», посчитал nDCG — и оставил выгрузку в объектном хранилище.
Смешение роли индексатора и роли чата. Конвейер загрузки читает всё по определению. API ответа не должен ходить тем же клиентом базы.
Четыре недели до контролируемого контура
Неделя 1 — инвентаризация и инварианты. Список источников, арендаторов, ролей, классов данных. Назовите инвариант: какой компонент никогда не видит чужой текст. Зафиксируйте правило: при отказе авторизации доступ закрывается. Нарисуйте ключ кэша. Найдите в коде все вызовы поиска без фильтра — это блокеры выпуска, не очередь «когда-нибудь».
Неделя 2 — грубая изоляция. Включите обязательный tenant_id или пространства имён на записи и чтении. Добавьте тест на межарендный запрос-двойник. Запретите нефильтрованный клиент в прикладном коде (линтер, обёртка SDK, отдельный пользователь БД без права последовательного сканирования всей таблицы — что доступно в вашем стеке).
Неделя 3 — тонкий ACL и скрытые каналы. Материализуйте группы на документе или подключите пакетную проверку кандидатов. Вычистите переранжировщик, кэш, трассы. Соберите десять враждебных случаев в эталонный набор.
Неделя 4 — измерение полноты под фильтром. Снимите полноту внутри области для узких и широких ролей. Если узкая роль «слепнет», увеличьте избыточную выборку внутри области, смените стратегию фильтра движка или вынесите крупных арендаторов. Не поднимайте глобальный top_k «на всякий случай».
Если контур нужно собрать не как ещё один прототип, а как наблюдаемый сервис с контрактами данных, услуга внедрения ИИ закрывает как раз эту связку: источники, роли, оценка, запуск.
Частые вопросы
Нужно ли фильтровать до поиска или достаточно проверки перед ответом модели?
Фильтровать нужно до извлечения и снова перед сборкой контекста, если политика сложная. Проверки «в конце» недостаточно: запрещённый текст уже мог уйти в кэш, трассу и переранжировщик, а выдача — опустеть.
Чем мультиарендность отличается от ACL?
Мультиарендность изолирует клиентов продукта друг от друга. ACL внутри арендатора разделяет роли, папки и грифы. Это разные границы; одной колонки org_id для обеих задач обычно мало.
Можно ли поручить модели не цитировать чужие документы?
Нет. Модель не является механизмом авторизации. Она не контролирует логи поставщика и не гарантирует отказ. Допустимое множество формируется кодом до вызова модели.
Что выбрать: отдельный индекс или фильтр в общем?
Мало крупных клиентов и жёсткий регулятор — отдельные коллекции. Много мелких с одной схемой — общий индекс с обязательным предварительным фильтром или тенантами движка. Часто гибрид по размеру арендатора. Решение пересматривают, когда меняется перекос нагрузки.
Почему после фильтра поиск «ничего не находит», хотя документы есть?
Типичен постфильтр поверх глобального top_k: все слоты заняли чужие соседи. Лечится предварительным фильтром, изоляцией арендатора и полнотой, посчитанной внутри области, а не увеличением k «на глаз».
Как быстро должны отзываться права?
Столько, сколько требует ваш класс данных. Для жёсткого контура тонкая проверка смотрит актуальную политику на каждый финальный пакет, кэш ключуется версией политики, событие отзыва пересчитывает денормализованные группы. «До следующей полной переиндексации» для увольнения — недопустимо.
Нужен ли ACL на каждом фрагменте?
Если право одно на весь документ — нет, достаточно полей документа на фрагменте и атомарного обновления. Если внутри файла разные грифы — да, и нарезка должна совпадать с границами политики.
Безопасен ли pgvector с ограничением на уровне строк?
RLS помогает транзакционному контуру не забыть фильтр в SQL. Это не отменяет настройки приближённого поиска с фильтрами и не заменяет модель ролей. Сочетайте ограничение строк с проверкой планов запроса и тестами на утечку.
Закрывает ли ACL промпт-инъекцию?
Нет. Он закрывает несанкционированное чтение. Разрешённый вредоносный документ всё ещё может пытаться управлять агентом. Нужны оба контура: доступ до извлечения и ограничения действий после.
Что писать в отказе, чтобы не раскрыть существование документа?
Одинаковую формулировку без идентификаторов, которых пользователь не имел права знать: в доступных источниках ответа нет. Не подтверждайте номер договора, гриф и владельца.
Дальнейшее чтение
- Промышленная инженерия RAG в 2026 — весь конвейер; эта статья закрывает ось прав.
- Архитектура корпоративного RAG — владельцы источников и карта знаний.
- Гибридный поиск и слияние рангов — лексический и векторный каналы после фильтра области.
- Эталонный набор для оценки RAG — куда добавить случаи утечки и отзыва прав.
- Семантический кэш без утечки арендаторов — ключи кэша как часть
ACL. - Промпт-инъекции в продакшене — соседняя угроза: разрешённый, но враждебный текст.
MCPв продакшене — идентичность и аудит, если поиск ходит в инструменты.
Заключение
Промышленный RAG перестаёт быть демо в тот момент, когда в индексе появляются второй арендатор и вторая роль. Дальше качество ответа неотделимо от вопроса, имел ли человек право увидеть доказательство. Этот вопрос решается до обхода соседей, ещё раз — перед сборкой контекста, и отдельно — в кэше, трассах и наборах оценки.
На этой неделе сделайте одно проверяемое действие: найдите любой поисковый вызов без обязательного идентификатора арендатора и закройте его так, чтобы обойти фильтр было нельзя из прикладного кода. Затем добавьте в эталонный набор один межарендный двойник и один отзыв роли. Остальное — наращивание той же границы: тонкие группы, скрытые каналы, полнота внутри области.
Контроль доступа до извлечения — скучная инженерия. Именно она отделяет корпоративный поиск от общей кучи эмбеддингов с вежливым промптом сверху.

