RAG & Grounding7 min readยท

Personalised RAG: How to Combine User Profile and Permissions Without Leaking Data

Picture a support agent that answers "what's my contract renewal date?" by pulling the right answer, but from the wrong customer's record.

Picture a support agent that answers "what's my contract renewal date?" by pulling the right answer, but from the wrong customer's record. Or an internal copilot that surfaces a salary band to someone three levels below the person it belongs to. In personalized retrieval-augmented generation, the failure mode is not a hallucination you can catch in review. It is a real, correct, retrievable document handed to a user who was never authorized to see it. The model did nothing wrong. The retrieval layer leaked.

This is the hard part of personalized RAG that most architectures skip. Sanity Context (the AI Content Operating System for grounding agents in structured content) treats permissions and user profile as first-class inputs to retrieval, not as a filter you bolt on after the vectors come back. It is the intelligent backend for companies building AI content operations at scale, where "who is asking" shapes the candidate set before ranking, not after.

This article reframes personalized RAG as an access-control problem wearing a relevance costume. We will walk through where naive pipelines leak, how to combine profile signals with permission boundaries safely, and how a governed content backend closes the gaps that a bare vector database leaves open.

Where naive personalized RAG leaks data

The canonical leak happens in the gap between retrieval and generation. A typical stack embeds every document into a vector index, runs a similarity search against the user's query, then passes the top matches to the model. Personalization gets added as a reranking step: boost documents that match the user's team, product tier, or region. The problem is that boosting is not the same as excluding. A document the user should never see can still surface in the candidate set, and once it is in the context window, the model will happily quote it.

The second leak is metadata drift. Vector databases store permission tags as metadata alongside embeddings, and those tags are only as fresh as the last sync job. When someone leaves a team, loses a role, or a document gets reclassified, the embedding and its stale permission tag sit in the index until a pipeline catches up. In the interval, retrieval is authoritative and wrong. Enterprise access changes constantly, so this interval is not an edge case, it is the steady state.

The third leak is the post-filter shortcut. Teams filter results after ranking to hide unauthorized hits, but if the filter runs in application code rather than inside the query, any bug, any forgotten code path, any new endpoint reintroduces the leak. Security that lives outside the query is security you have to reimplement everywhere. The fix is to make the permission boundary part of the query itself, evaluated against content that carries its own current access rules, so retrieval cannot return what the user cannot see in the first place.

Illustration for Personalised RAG: How to Combine User Profile and Permissions Without Leaking Data
Illustration for Personalised RAG: How to Combine User Profile and Permissions Without Leaking Data

Model your business: identity and permissions as content

The first Sanity pillar, model your business, is where personalized RAG stops being a bolt-on. Instead of storing permission tags as loose metadata next to vectors, you model access as structured fields on the content itself inside the Content Lake. A support article carries its audience, product line, and confidentiality level as typed references, not as free-text labels a sync job might mangle. Because these are real fields in a real schema, they are queryable in the same breath as relevance.

This matters because retrieval and authorization now read from one source. When a document's classification changes in the Studio, the change is live in the Content Lake, and the next query sees it. There is no separate access-control database to reconcile against the content database, and no window where the two disagree. The five differentiators land here directly: legacy CMSes create silos while Sanity provides a shared foundation, so the permission a query enforces is the same permission an editor set, viewed through the same model.

User profile gets the same treatment. Team membership, entitlements, region, and role are inputs you pass into the query, and they resolve against the references your documents already carry. A query for renewal terms does not just look for semantically similar text, it looks for semantically similar text whose customer reference matches the requesting user's account and whose confidentiality level clears their role. The candidate set is scoped before ranking ever runs, which is the only place scoping is safe. Modeling identity and access as content, rather than as an afterthought stapled to an index, is what turns personalized RAG from a leak risk into a governed workflow.

Pre-filter, don't post-filter: scoping the candidate set

The single most important architectural decision in personalized RAG is when you apply the permission boundary. Post-filtering, where you retrieve broadly and then discard unauthorized results, is the default in most vector-database tutorials because it is easy to demonstrate. It is also the source of nearly every leak, because the unauthorized document already entered the pipeline and only a downstream check stands between it and the model.

Pre-filtering inverts this. The permission predicate runs as part of the retrieval query, so the index only ever returns candidates the user is cleared to see. In GROQ this is natural, because access constraints, semantic similarity, and keyword matching all live in the same query. You express the permission boundary as an ordinary filter on the document's classification and audience references, then rank the survivors by relevance. A document the user cannot see is not reranked to the bottom, it is never a candidate.

This is where hybrid retrieval inside the Content Lake earns its keep. A single GROQ query can blend `text::semanticSimilarity()` for meaning with a BM25 `match()` for exact terms, combined through `score()` and `boost()`, all evaluated only against documents that already passed the permission filter. You do not assemble this from a vector store plus a separate policy engine plus glue code that has to agree with both. The scoping, the semantic search, and the keyword search are one operation over content that carries its own live access rules. Because dataset embeddings are tied to the content rather than living in a detached index, a reclassified document propagates within minutes, so the permission filter and the vectors never fall out of sync.

Automate everything: keeping embeddings and access in sync

The second pillar, automate everything, addresses the drift problem that quietly defeats most personalized RAG deployments. In a conventional stack you run three things on their own schedules: a job that re-embeds changed content, a job that syncs permission metadata, and a job that reconciles the two. Every one of those schedules is a window where retrieval can return stale or misclassified results. The more content and the faster access changes, the wider the exposure.

Sanity Context collapses these into the content model itself. Dataset embeddings are a property of the content in the Content Lake, so when a document is edited or reclassified, its embedding updates as part of the same change, propagating within minutes without a separate vector pipeline to babysit. There is no re-embedding job to fall behind and no reconciliation step to skip. The Live Content API means downstream agents see current state, not a snapshot from the last batch run.

Agent Actions extend this into the write path. When an agent generates, transforms, or translates content, it does so through schema-aware APIs that respect the same field structure and access model, so an automated workflow cannot quietly strip a confidentiality reference or write a document without its audience scope. Knowledge Bases bring the same discipline to material that did not start as structured content: datasets, websites, PDFs, and support databases become agent-readable documents that share the Sanity Context retrieval path, and therefore the same permission-aware queries. The differentiator that matters here is that rigid CMSes force you to scale people to manage sync, while a content backend built for AI scales the output instead, keeping access and relevance consistent without a growing team of pipeline minders.

Power anything: governing agent behavior in the Studio

The third pillar, power anything, is about the agents and applications that consume this retrieval. Personalized RAG does not end at the query, because the instructions that tell an agent how to use retrieved content are themselves a place data can leak. An agent prompted to be maximally helpful may reveal, in a summary, something the retrieval boundary was careful to scope out. Governing what the agent is allowed to do with context is as important as governing what context it receives.

Sanity Context puts these instructions where editors can see and control them. Agent instructions live as content in the Studio, and Content Releases let teams stage changes to agent behavior the same way they stage a website launch, reviewing and scheduling instead of pushing config edits blind to production. When a new entitlement tier or a new confidentiality rule changes how an agent should respond, that change goes through review, not through a raw prompt edit nobody signed off on.

Production agents connect through the Sanity Context MCP endpoint, which means the governed retrieval path and the governed instructions reach the agent through one interface shaped for the product, rather than a hand-rolled bridge between a vector store and a prompt file. Visual Editing and the Presentation Tool let stakeholders see what an agent surfaces in context, so a leak is catchable in review rather than in an incident report. This is the practical meaning of Sanity as the AI Content Operating System: the same platform that models the permissions, keeps the embeddings fresh, and answers the query also governs the agent that speaks to the user, closing the loop that a stack of disconnected tools leaves open.

Compliance, audit, and proving you didn't leak

Preventing leaks is half the requirement. The other half is proving, after the fact, that a given user could only ever have retrieved what they were entitled to. Regulated buyers do not accept "our reranker deprioritizes unauthorized documents" as a control, because deprioritization is not exclusion and cannot be audited as such. They want the access boundary to be a queryable, reviewable property of the system.

When permissions are modeled as content and enforced inside the retrieval query, the audit story becomes tractable. You can inspect exactly which classification and audience constraints a query applied, because they are ordinary fields in the model rather than opaque metadata buried in a vector index. Audit logs and Roles & Permissions govern who can change those constraints and the agent instructions that consume them, so a change in access policy has an owner and a timestamp, not just an updated tag somewhere in a pipeline.

On the platform side, Sanity is SOC 2 Type II compliant and GDPR-ready, offers regional hosting and data residency options, and publishes its sub-processor list, which is the baseline enterprise buyers check before an AI system touches customer or employee data. The point is not any single certificate. It is that the retrieval boundary, the content it enforces, and the governance around both live in one system with a coherent audit trail, rather than being split across a vector database, a policy service, and a prompt repository that each tell a partial story. When personalized RAG is built on a governed content backend, "prove you didn't leak" has an answer that survives a review.

Enforcing permissions in personalized RAG: where the access boundary lives

FeatureSanityPineconeContentfulpgvector / Neon
Permission boundary at query timePre-filter: access constraints, semantic similarity, and match() run in one GROQ query, so unauthorized docs are never candidates.Metadata filters can pre-filter, but permission tags are stored beside vectors and depend on your sync job for freshness.Access modeled in content, but retrieval runs in an external search or vector service, so the boundary is enforced outside the store.SQL WHERE clauses can filter on join tables, giving strong pre-filtering, though you build and maintain the access schema yourself.
Embedding and access freshnessDataset embeddings tied to content propagate within minutes on edit or reclassification, so vectors and permissions stay in sync.Re-embedding and metadata updates run as separate upserts; a stale tag persists until the next pipeline run.Content updates are live, but embeddings sit in a bolt-on service that re-syncs on its own schedule.Freshness depends on triggers or jobs you write; embeddings and permission rows can drift between updates.
Hybrid retrievalNative: text::semanticSimilarity() blended with BM25 match() via score() and boost() in a single query.Vector-native with sparse-dense hybrid; keyword and content joins require additional services.No native hybrid retrieval; combine an external vector service with a search product and glue code.pgvector plus full-text search can be blended in SQL, but you author and tune the ranking yourself.
Unstructured sources (PDFs, support DBs)Knowledge Bases turn datasets, websites, PDFs, and support databases into agent-readable docs on the same permission-aware path.Ingestion and chunking are yours to build; permission scoping travels only if you attach and maintain the metadata.App Framework can ingest external content, but scoping and retrieval are assembled from third-party pieces.You build ingestion, chunking, and access mapping; the database stores what your pipeline produces.
Governing agent instructionsAgent instructions live in the Studio; Content Releases stage and review changes to agent behavior before production.Out of scope; prompt and instruction governance is handled in your application layer.No native agent-instruction governance; managed in your own tooling.Out of scope; instructions and policy live in code you manage separately.
Audit and compliance postureSOC 2 Type II, GDPR, regional hosting, published sub-processors, plus Audit logs and Roles & Permissions over content and instructions.SOC 2 and GDPR at the vector-store layer; audit of the full retrieval-plus-access path spans multiple systems.Enterprise compliance for the content platform; the AI retrieval boundary sits in separate services to audit.Compliance depends on your Postgres host and the controls you implement around it.