Tenant-Isolated RAG with pgvector
Design tenant-safe multilingual RAG with Django, PostgreSQL, pgvector, JWT authentication, embeddings, retrieval thresholds, and answer grounding.
How can a RAG backend avoid retrieving another customer's documents?
Derive tenant identity from validated authentication, apply that boundary to every retrieval query and data access path, and test that cross-tenant documents never reach the model context. Embedding and prompt design do not substitute for authorization.
1. Treat RAG as a data-access system before a chatbot
A retrieval system ingests documents, selects relevant passages and passes them to a generation model. Every one of those stages has ownership and privacy implications. I begin by separating organizations, user roles, document lifecycle and source attribution. If teams cannot state which user may read which document, the index architecture is not ready.
Implementation checklist
- Define tenant and document ownership
- List ingest and deletion responsibilities
- Design unauthorized-access test fixtures
2. Build the identity boundary into the query
Never accept a tenant identifier from an unauthenticated query parameter as proof of access. Validate JWT or session identity, derive the tenant from server-side membership and filter vector search before retrieving passages. A service role that bypasses normal policies needs explicitly scoped operations and audit logging. Caches and background jobs also need tenant separation.
Implementation checklist
- Derive scope from verified user identity
- Filter retrieval using tenant ownership
- Test caches, exports and asynchronous ingestion
3. Keep embeddings consistent with their model
Vectors produced by different models cannot safely be compared merely because they have the same length. The model, dimensions, normalization and query/passage formatting form a versioned contract. In my multilingual project, locally generated E5 embeddings serve retrieval while generation runs separately. This separation supports evaluation and controlled model changes.
Implementation checklist
- Record embedding model/version per index
- Apply model-specific text prefixes
- Plan re-embedding during migrations
4. Evaluate retrieval and refusal with realistic questions
Retrieval top-k is a parameter, not a measure of relevance. I create questions with known supporting passages, ambiguous wording, multiple languages and no matching sources. Distance thresholds and conservative generation instructions help refuse unsupported requests; they need to be measured against real data rather than guessed once. Source references allow users to inspect the original text.
Implementation checklist
- Measure retrieval against curated questions
- Test multilingual terms and ambiguous queries
- Record when the correct answer is 'not enough evidence'
5. Make the ingestion lifecycle recoverable
Documents change, get deleted, arrive with poor formatting or fail halfway through embedding. Keep identifiers, source timestamps and extraction status so reprocessing does not silently duplicate chunks. Data retention and deletion requirements should propagate to embeddings and caches where applicable. Provider quotas should be isolated from authorization decisions.
Implementation checklist
- Make imports repeatable
- Track source-to-chunk lineage
- Support deletion and re-indexing
6. Publish limitations instead of invented accuracy
My published RAG case studies show API structure, local embeddings, PostgreSQL search and scoped access. They do not establish universal retrieval accuracy or audited compliance. Production readiness depends on the actual documents, permission model, attack testing, latency, cost and feedback loops of a specific deployment.
Implementation checklist
- Report evaluation scope and missing cases
- Review access and prompt-injection risks
- Plan monitoring for cost, latency and wrong answers
Architecture decision
Start with secure retrieval and measurable grounding. A more capable generative model cannot repair a broken tenant filter.
Technical references
Primary documentation for the design principles in this guide. Project evidence above describes the implementation.
- PostgreSQL: row security policies
Database-level row policies, default-deny behavior, and owner bypass considerations.
- pgvector: filtering and vector indexes
How filters interact with exact and approximate vector search.
Apply this to a real product
Explore related engineering capabilities, all technical notes, or contact me about an engineering challenge.
Let’s talk
I like working through complex problems with people who care about the details. Have a product to build, an engineering role, or an interesting challenge? Let’s start a conversation.
SaaimOpen to full-time roles, contract work, and conversations about things worth building.