Capítulo 7 — Implementando Controle de Acesso
Sétimo post do passeio capítulo a capítulo pelo LLM Primer III: Aprimorando a IA Empresarial com RAG. Modelos de permissão desenhados para bancos relacionais e sistemas de arquivos não encaixam direito em recuperação. A unidade de acesso não é mais uma linha ou um arquivo, e sim um embedding — e um embedding pode vazar o original via similaridade mesmo quando o próprio documento está bloqueado.
Por que este capítulo existe
O Capítulo 6 produziu o modelo de ameaças. O controle mais importante que ele implica — e o que a maioria dos sistemas em produção iniciais erra — é controle de acesso na camada de recuperação, para que o LLM nunca veja conteúdo que o usuário não possa ver. A alternativa ingênua, "filtrar na hora da geração", constrói um deputado confuso: o modelo já leu os documentos bloqueados e vai vazar a substância em paráfrase.
Este capítulo percorre os quatro mecanismos que compõem uma pilha funcional de controle de acesso — ACLs em nível de documento como fundação, RBAC integrado com os rótulos de sensibilidade existentes da empresa, ReBAC para a realidade em formato de relação do conhecimento empresarial, e a disciplina de pré-filtro versus pós-filtro que corre por baixo de todos eles.
7.1 ACLs em nível de documento e filtragem por metadado
Cada chunk sabe quem tem permissão de ver. A implementação é direta na descrição; os modos de falha são sutis o bastante para que quase todo sistema em produção inicial erre pelo menos uma vez. Três detalhes importam mais. Granularidade: um relatório longo pode ter um sumário público e um apêndice confidencial, e uma única ACL em nível de documento copiada uniformemente para os chunks ou compartilha demais o apêndice ou compartilha de menos o sumário. O padrão que escala é carregar permissões em nível de seção via parsing consciente de layout.
Frescor: permissões mudam. Cozinhar a ACL no chunk em tempo de indexação e nunca reavaliar dá um sistema que mente. Armazene um identificador estável nos metadados do chunk e resolva a ACL viva contra o sistema-fonte em tempo de query, atrás de um cache com TTL curto. Espaço negativo: se a resposta mora num documento bloqueado, o sistema não deveria alucinar nem dizer com confiança "não sei" — deveria dizer "há material sobre esse tema que você não está autorizado a ver". Isso exige ou uma segunda chamada sem filtro ou um banco vetorial que distingue "sem match" de "houve match mas foi filtrado", e a maioria das implementações se esquiva.
7.2 RBAC e rótulos de sensibilidade do Microsoft Purview
RBAC comprime o espaço de permissões — em vez de milhões de arestas usuário-para-documento, a política reduz a algumas centenas de arestas papel-para-classificação, o que é tanto auditável quanto manutenível. Encaixa em RAG limpamente quando a empresa já roda nele. Em ambientes Microsoft, isso significa grupos do Entra ID e rótulos de sensibilidade do Purview: Público, Geral, Confidencial, Altamente Confidencial, com sub-rótulos opcionais. O rótulo se move com o documento; o parser o lê em tempo de indexação e escreve o ID estável do rótulo nos metadados do chunk.
A integração é direta, o drift não. Se o indexador roda como uma conta de serviço que pode decriptar tudo, mas o sistema de recuperação impõe um filtro baseado em papel por cima do índice, um documento rerrotulado de Geral para Confidencial não terá seus chunks já indexados rerrotulados a menos que o indexador perceba a mudança. Os sistemas que acertam isso rodam reconciliação contínua contra a fonte. Os que erram descobrem o drift durante uma auditoria, e o achado é severo.
7.3 ReBAC com Zanzibar e SpiceDB
RBAC não consegue expressar "qualquer pessoa em Vendas que esteja também alocada ao deal da Acme Corp". Isso exige raciocinar sobre uma relação entre o usuário e o recurso, não só sobre um papel. Controle de acesso baseado em relação, formalizado no paper do Zanzibar no Google e disponível open-source como SpiceDB e OpenFGA, armazena um grafo: "Alice é membro de Engenharia", "Engenharia é viewer da pasta Specs", "Spec-101 está em Specs". Verificações de permissão viram travessias de grafo.
O padrão de integração com RAG é limpo. SpiceDB recebe a pergunta quais documentos este usuário pode ver? e devolve uma lista de IDs de documento; o sistema de recuperação passa essa lista como filtro de metadado para a busca vetorial. Os zookies do Zanzibar permitem que a chamada de recuperação insista em consistência pelo menos tão recente quanto uma permissão recém-concedida — um usuário adicionado a um projeto às 10:00 e perguntando às 10:01 verá os documentos novos. O custo operacional é que o SpiceDB vira uma dependência crítica no caminho da query que pede HA e cache agressivo com TTL curto das listas de documentos por usuário. Sistemas maduros frequentemente usam RBAC e ReBAC juntos — RBAC para a política ampla de sensibilidade, ReBAC para a política granular de relação, combinados como interseção dos conjuntos permitidos.
7.4 Pré-filtro, pós-filtro e a disciplina que corre por baixo
O pré-filtro aplica o predicado de autorização antes da busca vetorial — o índice restringe o conjunto de candidatos primeiro, depois roda similaridade sobre a restrição. É conceitualmente limpo e o default mais seguro, mas seu desempenho depende da estrutura do índice. HNSW com um filtro altamente seletivo pode degradar abruptamente conforme a travessia do grafo passa por muitos nós que não casam; variantes filterable-HNSW no Weaviate e Qdrant e namespaces por tenant no Pinecone e Milvus mitigam mas não eliminam o custo.
O pós-filtro inverte a ordem. Velocidade plena do HNSW, segurança mais fraca: o vazamento via top-K, o vazamento de informação por timing, e o vazamento de corretude quando o top-K inteiro é filtrado fora. A resposta pragmática de produção é empilhar os dois — pré-filtrar no predicado mais grosso e rápido (tenant, papel amplo), pós-filtrar nos predicados precisos e caros (listas do SpiceDB, rótulos do Purview), e fazer over-fetch do top-50 em vez de top-10 para que o pós-filtro ainda deixe um conjunto ranqueado completo. Dois lugares a mais vazam: o template de prompt que cita o título de um documento confidencial, e o cache de respostas indexado só pela string da query. Os dois precisam fazer parte da superfície de autorização.
O que o Capítulo 7 prepara
Controle de acesso responde quem pode ver o quê. Assume que há algo a bloquear. Não pergunta se o chunk deveria ter sido embedado na forma em que foi — se os nomes de cliente, os números de CPF, os caminhos de código proprietário deveriam estar sentados no vector store, esperando a autorização certa para virem à tona. Essa é a pergunta da anonimização, e é o assunto do próximo capítulo.
Próximo — Capítulo 8: Anonimização de Dados no Pipeline RAG. Pré-geração versus pós-geração, mascaramento versus substituição sintética versus privacidade diferencial, e o tradeoff utilidade-privacidade que toda escolha tem que navegar.