Права доступа в RAG: как не показать сотруднику чужие документы
Authorization должна ограничить retrieval до того, как модель получает context
Trusted identity, policy-derived scope, eligible evidence, revocation и fail-closed routes
Авторский ACL-RAG-8 workflow с permission-aware pseudocode и acceptance gates
права доступа в RAG, permissions RAG, контроль доступа, RAG security, document access и ACL-RAG-8

$ enforce rag-access --contract ACL-RAG-8
> resolve: session / subject / tenant / entitlements
> evaluate: purpose / collection / lifecycle / policy
> retrieve: eligible passages only
> verify: isolation / citation / revocation / trace
> route: answer / deny / clarification / reviewПрава доступа в RAG — это не инструкция в промпте «не раскрывай конфиденциальное». Модель видит только переданный ей context, поэтому границу нужно применять до того, как candidates попадут в retrieval, reranking и prompt. Если сотрудник может найти документ другого tenant, команды или lifecycle, вежливый финальный ответ уже не исправит утечку.
Материал отвечает на технический запрос «права доступа в RAG»: identity, policy, document permissions, retrieval и эксплуатационные проверки. Более широкую задачу проектирования продукта покрывает страница RAG-систем. Для архитектурного разбора команда может начать с AI engineering brief; статья поддерживает эту landing page техническими критериями, а не подменяет её.
Начните с authorization decision, а не с similarity search
Каждый запрос должен приходить в retrieval с trusted subject, сформированным на сервере. Полезный request record содержит tenant или организационную границу, immutable user ID, актуальные роли или entitlements, purpose при необходимости, session и версию policy. Название роли, document ID и фильтры из браузера — это вход для проверки, а не authority, которому можно доверять.
Оформите решение отдельным контрактом:
{
"subject": { "tenantId": "north", "userId": "u_42", "roles": ["support"] },
"resourceScope": { "collections": ["help-center"], "lifecycle": ["current"] },
"policyVersion": "access-2026-08",
"decision": "allow-retrieve"
}Identity service, application authorization layer и retrieval service могут быть разными компонентами, но должны одинаково понимать этот record. Не превращайте роль в неограниченный vector-store filter в browser code. Не заменяйте отсутствующий scope на более широкий corpus. Неполный или непроверяемый scope должен вести к controlled denial, уточнению или human route.
ACL-RAG-8: authorization-first архитектура retrieval
ACL-RAG-8 — это engineering sketch, который удерживает границу от запроса до ответа:
REQUEST + authenticated session
-> resolve server-side subject, tenant и active entitlements
-> evaluate policy для retrieval purpose и collection
-> construct trusted retrieval scope
-> filter candidates по tenant, ACL, lifecycle и source version
-> rank только eligible set
-> select citable evidence и verify claim coverage
-> answer | deny | clarification | reviewФильтрация после top-k retrieval небезопасна по умолчанию: неразрешённый контент уже может повлиять на traces, caches, reranking inputs или observability. Предпочтителен engine-level filter, полученный из trusted scope. Если storage не выражает существенный entitlement точно, поставьте deterministic policy gate до него или сузьте архитектуру corpus. Similarity score — не permission.
Не менее важен ingestion. Дайте каждому source и passage стабильные source ID, version, tenant, collection, access attributes, lifecycle и human-openable locator. Chunk наследует доступ от authoritative document record; после удаления пользователя или архивации он не должен хранить устаревшее разрешение. Почему эти поля должны ограничивать retrieval, а не украшать его, разбирает статья о метаданных и фильтрах в RAG.
Представляйте permissions как связи данных, а не только role labels
RBAC часто полезен как первый слой, но production policy обычно сочетает несколько отношений:
| Связь | Пример | Следствие для retrieval |
|---|---|---|
| Tenant | Два клиента используют один index | Candidate должен совпасть с trusted tenant boundary |
| Role | Support читает approved help articles | Роль резолвится в явную collection или action |
| Resource ACL | Design note доступна только project team | Candidate сверяется с membership документа или collection |
| Lifecycle | Черновик policy не customer-facing | Superseded, draft и quarantined versions неeligible |
| Purpose | Privileged export требует отдельного approval | Purpose проверяется на сервере, не выводится из prompt |
Не сводите все правила к долгоживущим vectors user IDs, если membership часто меняется. Сохраняйте source of truth членства и процесс reconciliation, который отзывает доступ или переиндексирует affected passages. Но и грубая роль не защищает document-level exception. Конкретная модель зависит от размера corpus, частоты изменений и стоимости ошибки; инвариант один: semantic similarity не делает candidate eligible.
Failure modes, которые нужно проверить до запуска
Пропущен cross-tenant filter. Разработчик тестирует один workspace и забывает tenant predicate в новом retrieval path. Нужны обязательный trusted-scope constructor, integration tests с двумя tenant и fail-closed default, если tenant отсутствует.
Stale entitlement после отзыва. Пользователь теряет доступ, но cache или background index держит старый ACL. Сохраняйте policy и membership versions, задайте bounded cache lifetime, reconcile changed documents и тестируйте реальный revoke с повторным запросом.
Authorization после retrieval. UI скрывает citation после ranking, хотя модель уже получила passage. Перенесите enforcement вверх: неразрешённые passages не должны появиться в results, reranking input, prompt или unredacted diagnostics.
Client-controlled scope. Запрос принимает department=finance из браузера как effective scope. Сервер должен resolve department и entitlements из authenticated session, а optional filter — только сузить уже доверенную границу.
Fallback расширяет corpus. Empty result запускает «полезный» поиск по всем документам. No-answer безопаснее. Любой fallback должен быть отдельно разрешён policy и протестирован.
Логи становятся вторичной утечкой. Retrieval traces способны сохранить title, snippet или query text. Минимизируйте payload, применяйте access control к observability и отдельно проверяйте operator roles.
Соберите authorization acceptance set
Тестовый набор доступа строится на реальных policy relationships, но с безвредными fixture documents, которые легко различить. Покройте allowed retrieval, denied retrieval, document-level exception, lifecycle change, membership removal, stale-cache simulation, empty result и пользователя с двумя разрешёнными scope. Запускайте эквивалентные prompts с разной identity: expected result меняется из-за policy, а не из-за формулировки.
| Gate | Что сохранить как evidence |
|---|---|
| Trusted identity | server-derived subject, session и policy version |
| Eligibility | tenant, ACL, lifecycle и source version до ranking |
| Isolation | denied source отсутствует в candidate, context, citation и trace |
| Revocation | changed membership наблюдается в документированной recovery boundary |
| Fallback | empty result не расширяет доступный corpus молча |
| Operations | назначен owner policy changes, incidents, audit review и rollback |
Измеряйте allow и deny outcomes отдельно. Высокий answer-quality score не доказывает isolation. Для sensitive cases сверяйте threat model и audit requirements с владельцем данных: это архитектурный материал, а не юридическая или compliance-консультация.
Bounded production rollout
Начните с одного workflow, малого набора collections и нескольких named roles. Зафиксируйте policy, ingestion и index configurations для теста. Добавьте kill switch или route, который возвращает bounded no-answer, пока расследуется policy incident. До расширения убедитесь, что revoke, re-index, cache invalidation и trace redaction проходят тем же production-like путём, который обслуживает реальные запросы.
const scope = await resolveTrustedScope(serverSession, request.purpose);
if (!scope.isComplete) return { route: "deny-or-clarify" };
const candidates = await retrieve({
query: request.query,
tenantId: scope.tenantId,
allowedCollections: scope.collections,
allowedSubjects: scope.entitlements,
lifecycle: "current",
});
if (candidates.some((item) => !policyAllows(item, scope))) throw new Error("policy breach");
return composeCitedAnswer(selectEvidence(candidates));Это control sketch, а не универсальная реализация. Содержательные production criteria — server-trusted identity, deterministic eligibility до model context, наблюдаемый revoke и безопасный refusal path. Именно эти вопросы стоит принести на RAG architecture review до расширения доступа внутреннего ассистента.
require(scope.isServerTrusted && scope.tenantId);
require(policy.allows(request.purpose, scope));
require(candidate.tenantId === scope.tenantId);
require(candidate.lifecycle === "current");
require(testSet.allowed && testSet.denied && testSet.revoked);
require(testSet.emptyResult && testSet.traceRedaction);
if (!scope.isComplete) route = "deny-or-clarify";
if (!evidence.isPermitted) route = "fail-closed";