×marble
all posts
May 31, 2026·16 min read

RAG Architecture for Personalization: What Changes When the User Is the Query

Standard RAG optimizes for query-document relevance. Personalized RAG has to solve query-user-document relevance — a three-body problem. This post maps the full architecture layer by layer: user representations, hybrid retrieval, context assembly, and where the latency actually goes.

Alex Shrestha·Founder, ×marble

RAG Architecture for Personalization: What Changes When the User Is the Query

TL;DR.

  • Standard RAG is a two-body problem: query and document. Personalized RAG is a three-body problem — user, query, document — and the architecture has to change at every layer to carry user state into the retrieval decision.
  • The failure mode isn't retrieval accuracy. It's retrieval relevance: standard RAG retrieves correctly for the query; it retrieves wrong for the person.
  • User representations must be pre-computed offline and joinable at inference time in under 5ms; bolting personalization onto a shipped RAG system requires rearchitecting, not patching.
  • Hybrid retrieval (dense + sparse + metadata-filtered ANN with user-conditioned scoring) is the production baseline in 2026; dense-only with appended user context is a prototype.
  • If you only remember one thing: the context window is the budget — how you fill it with user-relevant vs query-relevant chunks determines generation quality more than the LLM you chose.

Building a RAG system that answers questions is a solved engineering problem in 2026. Building one that answers questions differently for different people — based on who they are, what they've done, and what they actually care about — is not. The hard part isn't the generative layer. It's that standard RAG architecture has a single optimization target: query-document relevance. Personalization adds a second target — user-document relevance — and these two targets are sometimes aligned, sometimes orthogonal, and sometimes in direct conflict. A new user asking "what's a good first project in Rust?" and a staff engineer asking the same question should get different answers. Standard RAG will give them the same one.

This post is for engineers who've shipped RAG and are now being asked to make it personal. We'll walk through each layer of the architecture — offline user modeling, query construction, retrieval, context assembly, prompt construction — and explain precisely what has to change at each step.


What Is a Personalized RAG Architecture?

A personalized RAG architecture is a retrieval-augmented generation pipeline in which a user representation — pre-computed offline, joined at inference time — conditions the retrieval query, the candidate ranking, and the context assembled for the LLM. The user is not injected into the system as an afterthought in the final prompt; user state enters the system at the earliest possible retrieval stage and propagates forward.

The original RAG formulation from Lewis et al. (2020) treats retrieval as R(q) → D: given query q, return documents D, then condition generation on (q, D) (Lewis et al., 2020). Personalized RAG extends this to R(q, u) → D_u: the retrieved set D_u is a function of both the query and the user. The user representation u is the architectural delta — everything about how personalized RAG differs from standard RAG flows from what u contains and where it enters the pipeline.


Why Standard RAG Fails at Personalization

The failure is structural, not a tuning problem. Standard RAG has no mechanism to distinguish users. Two users asking the same query hit the same embedding, retrieve the same documents, and receive the same response. The only user-specific signal available is the query string itself. If the user's context, expertise, history, or preferences aren't encoded in the query text, they're invisible to the retrieval system.

The usual workaround is to append user context to the query: "[User is a Rust beginner, prefers tutorial-style content] what's a good first project in Rust?". This is better than nothing. It shifts the query embedding toward relevant content. But it has four concrete problems:

Embedding compression destroys relational structure. A single embedding vector representing "beginner, prefers tutorials, has done Python before, last session was 3 days ago" collapses all of that into one point in a high-dimensional space. The relational structure — that Python background implies different concepts need emphasis than a pure beginner — is compressed out. You're not conditioning retrieval on the user; you're conditioning it on a lossy projection of the user.

The query string has a length budget. Embedding models have token limits (512 tokens for most BERT-class encoders, 8K for newer models). A user with 6 months of interaction history cannot fit that history into a query prefix. You're forced to hand-pick which user attributes to include, which is itself a personalization decision that most teams make poorly.

User context changes between sessions. Appending context to the query means re-embedding on every request with whatever context snapshot you decide to include. If the user's context is stale, the embedding is stale. If you want freshness, you need to regenerate user context on every query — which adds latency and compute.

It's not auditable. When retrieval returns wrong results, "the embedding was shifted by the user context prefix" is a useless debugging statement. You can't inspect which part of the user context drove which retrieval decision. Personalization in the query string is a black box.

The fix isn't a better prompt prefix. It's a first-class user representation that enters the retrieval pipeline as a distinct input, not an annotation on the query.


Offline: User Representations That Retrieval Can Use

The architecture decision that determines everything downstream is how you represent the user. Three representations are common in production in 2026, with different trade-offs.

User embedding vectors

A learned dense vector representing the user's behavioral state. The canonical architecture is the two-tower model popularized at scale by YouTube in 2019 (Yi et al., 2019): one tower encodes the user (from interaction history, demographics, context), one tower encodes the item (from content features), and relevance is the dot product of the two towers' output vectors.

In a RAG context, the user tower generates a user embedding offline (updated periodically from behavioral events), and at inference time the retrieval query becomes a combination of the query embedding and the user embedding. The vector index supports approximate nearest neighbor (ANN) search that can be conditioned on user vectors — either by query vector modification or by inner product weighting.

The advantage is scale: a 128-dimensional user embedding is cheap to store, cheap to join, and ANN search over a corpus of millions of documents conditioned on a user vector is a solved engineering problem (FAISS, ScaNN, Vespa). The disadvantage is interpretability: the user embedding is a black box, its dimensions don't map to human-readable attributes, and when retrieval goes wrong you can't inspect it.

Attribute sets for metadata filtering

The user is represented as a structured set of attributes — skill level, topic interests, preferred formats, account tier, onboarding state — stored in a fast key-value store (Redis, DynamoDB) and used as metadata filters on the vector index query. The ANN search operates normally; the filter narrows the candidate pool before or during retrieval.

Most hosted vector stores (Pinecone, Weaviate, Qdrant, OpenSearch) support pre-filter and post-filter modes on metadata. Pre-filter restricts the ANN search to a subindex that matches the filter, which is precise but requires well-maintained per-attribute subindexes. Post-filter runs ANN globally and discards non-matching results, which is faster to implement but wastes retrieval budget.

Attribute-based filtering is interpretable, auditable, and easy to update — you're just writing to Redis. It handles personalization for structured domains (e-commerce, SaaS, media) extremely well. It struggles when user preferences are continuous, multi-dimensional, or implicit: "this user prefers detailed technical content but only when they have 15+ minutes of session time" is not a metadata filter.

Knowledge graph neighborhoods

The user is a node in a typed graph; their interests, behavior history, and inferred preferences are edges to entity nodes. At inference time, graph traversal extracts a structured neighborhood that conditions retrieval. We covered this architecture in depth in the RAG + knowledge graph post — the short version is that graphs preserve relational structure that both embeddings and attribute sets destroy, at the cost of higher operational complexity.

In practice, most production systems use a hybrid: attribute sets for hard filtering (exclude content the user can't access, exclude formats the user has disabled), user embeddings for semantic retrieval, and a graph or history store for reranking. Choose the representation that matches the structure of your personalization signal, not the one that's easiest to implement.


Query Construction: Where User State Enters the Retrieval Call

Once you have a user representation, the question is where it enters the retrieval pipeline. There are three injection points, each with different precision and latency characteristics.

Query vector modification. Compute a user-conditioned query vector at retrieval time: q_personalized = α * q_query + β * q_user, where q_query is the embedded query, q_user is the pre-computed user embedding, and α, β are weighting parameters (tunable per domain). This is the dual encoder pattern from DPR extended to user state (Karpukhin et al., 2020). The modified query vector biases ANN search toward documents that are both query-relevant and user-relevant. Latency overhead: 1-2ms for the vector addition, zero additional index calls.

Metadata pre-filter. Before issuing the ANN query, pull the user's attribute set (sub-5ms from Redis) and construct a metadata filter. Pass the filter to the vector store alongside the query vector. The ANN search operates within the filtered candidate pool. This is the most common approach for structured personalization signals and plays well with existing vector stores — it's a parameter change to the retrieval call, not a new system.

Late interaction scoring. Retrieve a larger candidate pool (top-100) with standard ANN, then score each candidate against a richer user representation using a cross-encoder or a ColBERT-style late interaction model (Khattab & Zaharia, 2020). Late interaction is slower than bi-encoder ANN (it computes a full interaction matrix per candidate), but it's more precise for the final top-k selection. The pattern: fast-and-recall-optimized for the broad retrieval, slow-and-precision-optimized for the personalized reranking.

The right combination depends on your latency budget and personalization signal type. A practical starting point: metadata pre-filter to restrict the candidate pool to user-relevant content, then query vector modification within that pool, then late-interaction reranking on the top-50 results. Total added latency over baseline RAG: 5-15ms.


Retrieval Strategy: Hybrid Search Conditioned on User State

Dense-only retrieval (query embedding → ANN search) is the default because it's simple to implement and works well for semantic similarity. It is not the production baseline for personalized RAG in 2026. Hybrid retrieval — combining dense embeddings, sparse term-matching (BM25), and user-signal scoring — consistently outperforms dense-only for queries with specific entity references, navigational intent, or user-specific terminology.

The standard hybrid architecture:

  1. Sparse retrieval (BM25/TF-IDF): Fast exact-term matching. Retrieves documents containing the user's specific terms and entity references. Recall-optimized. No semantic generalization — if the user types "transformer" in a music context, it doesn't retrieve "attention mechanism" documents.

  2. Dense retrieval (ANN with user-conditioned query): Semantic similarity. Handles paraphrase and concept generalization. User embedding applied as described above.

  3. Reciprocal Rank Fusion (RRF): Merge the two ranked lists into a single ranking by reciprocal rank combination. Simple, parameter-free, and empirically robust. Score: RRF(d) = Σ 1 / (k + rank_i(d)) where k = 60 is the standard smoothing constant.

  4. User signal re-scoring: Apply a user-specific score multiplier to the merged ranking — based on graph proximity, attribute match, or collaborative signal — before selecting the final top-k.

Anthropic's contextual retrieval research (2024) demonstrated that prepending chunk-level context summaries before embedding, combined with BM25 hybrid retrieval, reduces retrieval failure rates by over 49% versus standard chunked dense retrieval (Anthropic, 2024). Apply the same principle to user context: the user's representation conditions not just the query but how chunk context is summarized and matched.

ANN index design for personalization. If your personalization signal partitions users cleanly by segment (expertise level, language, product tier), maintain segment-specific subindexes and route each query to the appropriate subindex. Per-segment indexes are smaller, faster to search, and more precise for that segment's vocabulary and content preferences. The trade-off is operational overhead: you're managing N indexes instead of one, and content that spans segments needs to appear in multiple indexes.

HNSW (Hierarchical Navigable Small World) graphs remain the dominant ANN structure for production vector search in 2026 (Malkov & Yashunin, 2018). For personalization at scale, the key HNSW parameter is ef_search (the beam width during graph traversal). A higher ef_search explores more of the graph and improves recall, at the cost of latency. For personalized queries where the user-conditioned query vector is more distant from the corpus mean, increasing ef_search compensates for the reduced ANN recall — the user-biased query vector is "harder" for the index and needs more exploration budget.


Context Assembly: The Personalization Bottleneck Before the LLM

This is the layer most teams underinvest in, and it has the highest leverage on generation quality. By the time you reach context assembly, you have a ranked list of retrieved chunks. The question is which chunks to include in the context window, in what order, and how to handle the finite budget.

A typical context window budget for a RAG application in 2026:

  • System prompt (instructions, user summary): 500-1000 tokens
  • Retrieved chunks: 2000-4000 tokens
  • Conversation history: 500-1500 tokens
  • Query and response space: 500-1000 tokens

The retrieved chunks budget (2000-4000 tokens) holds 4-8 chunks at standard chunk sizes (300-500 tokens each). From a top-50 retrieval result set, you're selecting 4-8 chunks for inclusion. That selection is the context assembly problem.

Standard RAG selects chunks by retrieval rank — top-k by embedding similarity. For personalized RAG, the selection needs to account for:

Diversity vs relevance. The top-8 chunks by embedding similarity may all be variants of the same document or the same concept. A diversity-weighted selection ensures the context window covers multiple facets of the query, which produces more useful responses for users with exploratory intent. MMR (Maximal Marginal Relevance) is the standard algorithm: iteratively select the chunk that maximizes relevance minus similarity to already-selected chunks, weighted by a diversity parameter λ.

User expertise calibration. If the user representation includes an expertise signal (inferred skill level, interaction history with advanced vs introductory content), use it to select chunks at the right depth. A beginner and an expert asking the same question need different chunks, not different answers to the same chunks. This requires chunk-level metadata: each chunk should carry a difficulty/depth score derived from its source document and content structure.

Recency and novelty weighting. For users with long interaction histories, de-prioritize chunks from documents they've already engaged with (inspected, quoted, bookmarked). A user who's read the same introductory article three times doesn't need it in the context window again. Apply a seen-document discount to the chunk selection score: adjusted_score = retrieval_score * (1 - α * seen_count).

Position matters. LLMs exhibit a "lost in the middle" effect — they recall content from the beginning and end of the context window more reliably than content in the middle (Liu et al., 2023). Place the most user-relevant chunk first and the second-most relevant last. General supporting material goes in the middle.

Context assembly is where personalization has the highest return on investment per engineering hour. The retrieval pipeline can be wrong on 2-3 of the top-10 results and context assembly can compensate by selecting the right 4-6 from the remaining correct ones.


Prompt Construction: User State in the Generation Layer

Context assembly determines what the LLM sees. Prompt construction determines how it interprets what it sees. For personalized generation, the system prompt carries user context that shapes the response style, depth, and framing — complementing the personalization already applied at retrieval.

A minimal user context block in the system prompt:

User context:
- Skill level: intermediate (inferred from interaction history)
- Primary interests: distributed systems, Go, cloud-native infrastructure
- Session type: exploratory (based on query structure and session duration)
- Last active topic: gRPC service discovery (3 days ago)

This is different from stuffing user history into the query. The system prompt user context is about how to respond, not what to retrieve. It tells the model to calibrate depth, use the user's vocabulary, and connect new content to what the user already knows. It doesn't replace retrieval-side personalization; it completes it.

Keep the user context block structured, not narrative. Structured context is easier for the model to reference reliably. A bulleted list of attributes outperforms a paragraph description of the user in production prompts. Anthropic's guidance on prompt construction confirms that clearly delimited structured data in the system prompt produces more consistent instruction-following than embedded prose (Anthropic prompt engineering guide).


Production Latency: Where the Budget Goes

A fully personalized RAG pipeline has more moving parts than standard RAG, but the latency overhead is manageable if the offline/online split is executed correctly.

Offline (pre-request, no time budget):

  • User embedding update: runs on interaction events, background job, O(minutes) staleness acceptable
  • Graph neighborhood pre-computation: same cadence
  • Segment-specific index maintenance: batch job, per-deployment or per-day

Online (per-request, latency-critical):

| Layer | Operation | Typical Latency | |---|---|---| | User state join | Redis lookup of user embedding + attributes | 1–3 ms | | Query construction | Vector modification, metadata filter build | 1–2 ms | | Sparse retrieval | BM25 over pre-built index | 5–15 ms | | Dense retrieval | ANN (HNSW, ef_search=100) | 15–40 ms | | RRF merge | In-process ranking | < 1 ms | | Late interaction rerank | Cross-encoder, top-50 | 20–60 ms | | Context assembly | MMR selection, diversity scoring | 1–3 ms | | LLM generation | Claude Sonnet 4.6, streaming | 200–800 ms |

The LLM generation dominates. Everything before it — including the full retrieval and personalization pipeline — is noise against the 200-800ms generation baseline. Targeting a p50 < 30ms for the retrieval subsystem (excluding generation) is achievable with the architecture above. The retrieval pipeline without late-interaction reranking comes in under 25ms on typical hardware. Adding late-interaction reranking pushes the retrieval total to 40-60ms — acceptable for most use cases, expensive if you're targeting sub-100ms total excluding generation.

Late-interaction reranking is optional for most applications in 2026. The combination of hybrid retrieval + user-conditioned query modification + metadata filtering provides sufficient precision for most personalization use cases without the latency cost of a cross-encoder. Reserve late interaction for high-stakes retrieval where precision at k=1 matters: legal research, medical content, high-conversion product recommendation.


What to Build First

The architecture above is the target state. The path to it matters as much as the destination.

If you have a working RAG system and want to add personalization:

Start with the user state join. Before any retrieval changes, instrument your system to pull user attributes (skill level, top interests, content preferences) from Redis on every request and log them alongside retrieval results. This has zero latency impact and gives you the observability you need to diagnose retrieval mismatch. Two weeks of logs will show you which user segments are getting the worst retrieval quality — that tells you where to start.

Then add metadata pre-filtering. Map the clearest user attributes (expertise tier, language, product access) to metadata filters on your vector index. This change requires no new infrastructure and typically produces 15-25% improvement in retrieval precision for users whose preferences are well-captured by your attribute schema. Measure precision@10 per user segment, not aggregate.

Then implement hybrid retrieval. Add a BM25 index alongside your dense index. Wire up RRF merge. The engineering lift is 1-2 weeks with any modern retrieval library (Weaviate, Elasticsearch, Vespa). Hybrid consistently outperforms dense-only for personalized queries, which tend to have more specific entity references than general knowledge queries.

Then invest in context assembly. Once retrieval is working, the remaining quality gap is almost always in context assembly. Implement diversity-weighted selection (MMR) and chunk-level metadata (difficulty, topic, source). Add the seen-document discount for returning users. This is where the compounding returns are: better context assembly makes every LLM response better, not just the ones where retrieval was marginal.

User embeddings and late-interaction reranking come last. They require the most infrastructure investment and only pay off once your corpus is large enough that semantic precision at recall@50 becomes the bottleneck. Until then, metadata filtering and hybrid retrieval cover most of the gap at a fraction of the complexity.

The architecture described here is not exotic — it's the production standard that teams at Spotify, Netflix, and Notion have converged on over the last two years. What makes it feel complex is that every layer requires a user representation, and most RAG tutorials skip that layer entirely. The user model is not a database table you add at the end. It is the primitive that every other layer depends on. Build it first, even if you start with a simple attribute set in Redis. Everything else follows from having a stable answer to "who is this user, and what do I know about them?"

the product behind these notes

×marble is the personalization graph.

One API. A living knowledge graph per user. Day-zero ready, explainable by construction. We built it so you don't have to.

See ×marble