See It Flow
System Design · Trade-offs · ~24 min read

Enterprise RAG Knowledge Assistant — Trade-offs and Decisions

RAG architecture trade-offs for an enterprise assistant: 14 decisions across hybrid versus vector-only search, chunking, permission pushdown versus post-filtering, tenant index partitioning, reranking and cache keys. Each states the options, selection criteria and when another choice wins, for a 3,000-employee tenant with 750,000 chunks on a ~500-tenant platform.

Mangesh MandlikLast updated:
Decision 01

Retrieval Method: BM25 vs. Dense Vectors vs. Hybrid

Employees ask in their own words ("do we still reconcile payments by hand at month-end?") about text that says "automated nightly run with an exception queue", and they also ask about RUN-8, which must never be answered from RUN-5. Both kinds of miss produce wrong answers, and retrieval has ~80 ms of the budget.

OptionBenefitsLimits
BM25 onlyExact terms and identifiers; cheap; interpretable scores; no embedding pipelineMisses paraphrase: "by hand" and "month-end" share no terms with the policy text that answers the question
Dense vectors onlyMatches meaning without shared wordsShort identifiers blur (RUN-8 and RUN-5 embed close together); every model upgrade means a corpus re-embedding
Hybrid: BM25 ∥ ANN, reciprocal rank fusion (RRF)Covers both failure modes; RRF uses ranks only, so the two scorers never need a common scaleTwo searches per query (run in parallel), two index structures to keep consistent, a fusion constant to justify

Selection criteria. Recall@k on paraphrase and identifier questions separately; robustness when one scorer changes; latency; the cost of keeping two representations consistent.

When another option wins. BM25 only for identifier-lookup tools (ticket or part-number search) where users type the document's own vocabulary. Dense only for narrative corpora without codes, or for cross-lingual search, where queries and documents rarely share terms. Learned sparse retrieval (e.g. SPLADE) when one index matters more than an extra ingestion-time model; weighted score blending only with stable, normalised scorers.

Remaining risk. A standard tokenizer splits RUN-8 into run and 8, silently erasing the identifier advantage unless the analyzer preserves identifiers (Architecture Deep Dive, Component 04). RRF ignores score magnitude, so a clear single-list winner can lose to consensus candidates; the reranker corrects most such cases.

Decision 02

Chunking Strategy: Fixed-Size vs. Structure-Aware vs. Semantic

Chunks are the unit of retrieval, citation and permission tagging, and only four fit the context (~1,600 tokens). Sources differ in shape — headed wiki policies, drive documents with tables, chat threads without headings — and a boundary through the sentence that states a rule makes both halves useless.

OptionBenefitsLimits
Fixed-size token windowsPredictable chunk count (750,000 at 400/100); trivial for every formatCuts sentences, table rows and heading-to-rule links
Structure-aware (heading sections, capped windows)Chunks match policy sections; better embeddings, reranking and citationsA layout-aware parser per format; uneven chunk sizes (~300-400 tokens)
Semantic (topic-shift detection)Fits long unstructured text such as transcriptsAn embedding pass per sentence at ingestion; boundaries are hard to predict or debug
Parent-child (match small, return the parent section)Precise matching with wide context for the generatorTwo granularities to index; a 1,200-token parent uses three chunks' worth of budget

Selection criteria. Recall@k per source type; predictable sizes for capacity and token budgets; ingestion cost; whether a citation points at a meaningful passage.

When another option wins. Fixed-size windows for a deadline-bound bulk import, with a planned re-chunk afterwards. Parent-child when answers need whole sections (contracts, procedures) and the context budget can afford it. Semantic chunking for long transcripts with no structure at all.

Remaining risk. Any chunking change is corpus-wide: it re-chunks and re-embeds all 750,000 chunks and must be published as a staged index version (Decision 14). Boundary regressions show up only as recall@k drops per source type (Architecture Deep Dive, Component 10), not as errors.

Decision 03

Authorization Enforcement: Post-Filter vs. Pushdown vs. Pushdown plus Re-check

Authorization is zero-tolerance and fail-closed: no answer, citation or paraphrase may reveal content the requester cannot read now. Users differ widely in what they can read (a user only in grp-sales asking a payments question can read few of the relevant chunks), and permissions can change while a request is in flight.

OptionBenefitsLimits
Post-filter retrieved candidatesRetrieval stays permission-blind; simpleStarvation: 0-2 of the top 30 may survive although a readable answer exists at rank 200. (Filtering the generated answer instead is a leak: the model has already read the text.)
Pushdown only (acl_principals ANY_OF principal_set inside both searches)The top 30 are this user's best readable chunksUses tags that may lag an ACL change and a principal set up to 60 s old; nothing checks at time of use
Pushdown plus a live re-check before the promptExposure is bounded to revocations committed after the re-check~20 ms per batch; another query-path dependency, which must fail closed

Selection criteria. Whether exposure is bounded and measurable; recall for users with narrow access; behaviour when a dependency times out; added latency.

When another option wins. Never for access-controlled content: no workload makes output filtering acceptable, and no workload with per-document permissions should drop the re-check. Only the surroundings vary: a corpus every tenant member may read in full (a public handbook) has no per-document ACL, so tenant partitioning alone suffices.

Remaining risk. A revocation committed after the re-check affects only the next request; a group removal can take up to the 60 s cache TTL (Decision 05). Filtered HNSW loses recall when a user can read only a few percent of the tenant; the planner offsets it with exact scans and a wider search (Architecture Deep Dive, Component 06).

Decision 04

Sync Trigger: Webhooks vs. Polling vs. Reconciliation vs. Full Recrawl

Index lag must stay within 5 minutes p95 for small edits and 15 minutes for large documents; document-ACL removals must apply within 2 minutes. Source APIs are rate-limited, webhooks are best-effort, and a deletion that produces no event must still be found.

OptionBenefitsLimits
Webhooks (a trigger that causes a fetch)Seconds of detection latency; no wasted callsDropped, duplicated or reordered; not offered by every source; carry neither content nor permissions
Cursor-based pollingWorks with any change API; catches missed webhooksDetection latency equals the interval; a shorter interval spends rate limit
Nightly reconciliation sweep (list ids, versions and ACL hashes; compare with the documents table)Finds anything both triggers missed, including silent deletions; fetches only what differsDetection up to 24 h; a listing pass over 150,000 documents
Full recrawl (re-fetch all content)Rebuilds state with no dependency on change APIsRe-fetches 150,000 documents to find a few hundred changes

Selection criteria. Lag and revocation budgets; each source's delivery guarantees; API quotas; whether deletions and ACL edits are observable.

When another option wins. Polling alone for sources without webhooks or with low churn. A full recrawl as the primary mechanism for small sources with no change API at all. Dropping the sweep never pays: it is cheap and is the only layer that finds silent deletions.

Remaining risk. A source whose change feed omits permission edits can surface an ACL removal only through the sweep, which is outside the 2-minute budget; such connectors need a dedicated permissions poll or an explicit exception. Polls compete with user traffic for the source's rate limit during bulk edits.

Decision 05

Authorization Freshness: Live Checks vs. Cached Principals vs. Cache with Invalidation

Group membership lives in the identity provider (IdP) and is resolved per query. A group removal must take effect within 60 s, principal resolution has ~15 ms of the budget, and an IdP outage must neither leak content nor take down a 99.9%-available query path.

OptionBenefitsLimits
Live IdP lookup on every querySmallest staleness windowIdP latency on every query at 150-200 qps platform peak; an IdP outage becomes a query outage
Principal-set cache, TTL ≤ 60 s~15 ms; absorbs IdP blipsMembership up to 60 s stale
Cache plus push invalidation from IdP membership eventsTypical window of seconds; the TTL still bounds the worst caseEvent plumbing per IdP; lost events fall back to the TTL

Selection criteria. The revocation budget; added latency; coupling of query availability to the IdP; behaviour when the IdP fails, which must be closed.

When another option wins. A live check per query for a regulated tenant whose contract requires removal within seconds — applied to that tenant only, accepting its latency and availability cost. A longer TTL only if the product explicitly relaxes the revocation budget; the TTL follows the budget, never the reverse.

Remaining risk. Up to 60 s of exposure after a group removal when no event arrives. During an IdP outage users lose group-granted content, which shows up as a quality drop rather than a leak. The revocation-lag canary in Architecture Deep Dive, Component 10 verifies the bound continuously.

Decision 06

Index Partitioning: Per-Tenant vs. Shared Indexes

About 500 tenants of very different sizes share the platform; the largest has 750,000 chunks (~5 GB of vectors). Cross-tenant reads must be impossible, and one tenant's spike must not degrade others.

OptionBenefitsLimits
Shared index with a tenant_id filterOne index to run; cheap for tiny tenantsOne forgotten predicate in a feature, admin tool or script is a cross-tenant leak; a small tenant is a tiny filtered fraction of a huge HNSW graph, the worst case for filtered recall
Per-tenant partitions (index or namespace)Structural isolation; graphs capped at the tenant's size; per-tenant migration, purge and deletionMany partitions to operate; fixed overhead per small tenant
Per-tenant partitions plus dedicated capacity for the largest tenantsAlso isolates compute for the tenants most likely to spikeReserved capacity costs money when idle

Selection criteria. Isolation enforced by structure rather than convention; blast radius of a bug; per-tenant operations; compute isolation; overhead for the long tail of small tenants.

When another option wins. Only the granularity changes, never logical isolation: thousands of tiny tenants fit as namespaces on shared clusters, provided the engine enforces the namespace rather than a query predicate. A single-tenant deployment makes the question moot. A shared filterable index is not an acceptable option for mutually distrusting tenants.

Remaining risk. Partitions do not stop contention for shared rerankers or LLM quota; every shared resource needs its own per-tenant limit. Isolation also depends on deriving the tenant only from the authenticated token, never from a request parameter.

Decision 07

Search Infrastructure: One Engine or Two, Managed or Self-Hosted

Per tenant partition, retrieval needs BM25 with custom analyzers, permission-filtered ANN that keeps recall under selective filters, cheap acl_principals/status tag updates and an atomic alias switch — with BM25 and vectors never disagreeing about which chunks exist.

OptionBenefitsLimits
One engine storing text, vector and tags on the same recordA chunk is written once; no cross-store drift; one filter languageIts ANN, analyzers and filter planner must all be good enough
Separate vector database and lexical engineBest-of-breed vector featuresTwo stores need a shared commit point and a reconciliation job; a chunk deleted from one but returned by the other is a classic bug
Managed serviceThe vendor runs upgrades, sharding and replicas; faster to productionLess control over HNSW parameters and analyzers; pricing grows with storage and queries across ~500 tenants
Self-hosted engineFull control of analyzers, ef, exact-scan fallback and cost at scaleThe team owns capacity planning, upgrades and on-call for a distributed search system

Selection criteria. A checklist: filtered ANN with an exact-scan fallback; tag-only updates; identifier-preserving analyzers; atomic aliases; per-tenant namespaces; then operating capacity and cost.

When another option wins. Self-hosting when managed pricing across the platform exceeds the cost of an infrastructure team, or when a checklist item (a custom analyzer, filter-planner control) is not exposed. Two stores when a required vector capability exists only in a dedicated vector database — accepting the commit point and reconciliation job that come with it.

Remaining risk. In segment-based engines a tag update re-indexes the whole record, vector included, so a bulk ACL change is real write load (Architecture Deep Dive, Component 06). Moving between engines later is a corpus-wide migration published like Decision 14.

Decision 08

Reranking: Always vs. Fusion-Only vs. Conditional

Fusion yields ~40 candidates, but only four fit in the context. First-pass scorers judge the question and each chunk separately, so a glossary entry that mentions reconciliation can rank next to the passage describing the change. A cross-encoder costs ~120 ms per query.

OptionBenefitsLimits
Always rerank (cross-encoder over ~40 pairs)Highest precision in the four context slots~120 ms and one model inference per pair on every query
Fusion order onlySaves ~120 ms and a model dependencyVocabulary overlap can beat the chunk that actually answers
Conditional (skip when one candidate clearly dominates)Pays only when reranking is likely to change the top fourNeeds a calibrated threshold; a wrong threshold regresses precision silently

Selection criteria. The measured gap between fusion order and reranked order on the evaluation set (nDCG and context precision per question type, plus groundedness of the resulting answers); the latency budget; reranker cost relative to generation, which dominates per-query cost.

When another option wins. Conditional reranking for very high-volume tenants once reranker spend becomes material and the threshold is validated on that tenant's queries. Fusion-only as the normal mode when the evaluation gap is negligible (a narrow identifier-lookup corpus). An LLM as reranker only where its flexibility is worth a slower, costlier judgement per pair.

Remaining risk. A reranker version change can bury answers without any error; it is gated on the evaluation set like any model change (Architecture Deep Dive, Component 10). Degraded mode is lower precision by design, so its duration is an alerting metric.

Decision 09

Embeddings: Precompute, and How to Migrate Models

Every one of 750,000 chunks has a 1,536-dimension vector; steady churn is only 6,000 embeddings a day, but a model upgrade changes every vector, and vectors from different models are not comparable. Embedding the corpus at query time is not an option at this size, so the real choice is the migration strategy.

OptionBenefitsLimits
In-place re-embedding of the live indexNo extra storageOld and new vector spaces mix inside one graph mid-migration; nearest neighbours are silently wrong. Excluded
Shadow namespace: dual-write, backfill, parity check, atomic alias flip, bake periodQueries see one consistent model; instant rollbackDouble storage (~5 GB per large tenant) and a backfill of ~300 million tokens
Pin the current modelNo migration costForced migration later when the provider retires the model

Selection criteria. Correctness (one vector space per search); rollback speed; temporary storage and embedding cost; how long the migration lasts while 1,200 revisions a day keep arriving.

When another option wins. Pinning the model is right until evaluation shows a meaningful gain or the provider announces retirement. Querying both models and fusing during the transition suits a cautious rollout, at two embeddings and two ANN searches per query until the cutover.

Remaining risk. Events that arrive during the backfill must reach both namespaces through the same version-fenced writer, or the new namespace is born stale. The query embedder and the index alias must switch together; a mismatch is the in-place failure again.

Decision 10

Cache Keys vs. Leakage

Two users in the same tenant asking the identical question may be entitled to different answers. Any cache placed after authorization must therefore reuse a value only for the same entitlement and corpus state, within the 60 s revocation budget. Per-identity answer caches tend to have low hit rates.

OptionBenefitsLimits
Answer keyed by query textMaximum hit rateServes a payments engineer's answer to a sales user — a security defect, not a trade-off. Excluded
Query-embedding cache onlyShared safely: depends only on text and modelNo saving on retrieval or generation
Retrieval and answer caches keyed by tenant, query, principal fingerprint and corpus state; TTL ≤ 60 sReuse only between identical entitlements; per-document version checks on a hit let entries survive unrelated editsLow hit rate; a tenant-wide corpus counter would invalidate about every 30 s at this churn; a ~20 ms validation per hit

Selection criteria. Leakage must be impossible by construction; then hit rate, staleness bounds and the cost of validating a hit.

When another option wins. Skip answer caching entirely when principal sets are so varied that fingerprints rarely repeat — the embedding cache then captures most of the saving. Hit rates rise naturally where entitlements are uniform (most users share one fingerprint), and the same keys stay safe.

Remaining risk. The fingerprint must cover every input to authorization; adding a new entitlement dimension (say, a clearance attribute) without adding it to the key reintroduces the leak. A cached result cannot see a newly published document that would now outrank its evidence; the TTL bounds that.

Decision 11

Long-Context Stuffing vs. Retrieval

The corpus is ~225 million tokens (150,000 documents × 1,500 tokens), and each query is budgeted at ~2,200 input tokens, LLM tokens being the dominant cost. Every passage given to the model must also be one the user may read.

OptionBenefitsLimits
Put the whole corpus in contextNothing can be missedOrders of magnitude beyond any context window. Excluded
Coarse selection, long context (e.g. top 20 whole documents, ~30,000 tokens)Fewer misses from chunk boundaries~14× the input tokens and a slower first token; models use information in the middle of long contexts less reliably; per-document ACLs still required
Precise retrieval (four reranked, authorized chunks, ~1,600 tokens)Cost and latency roughly constant as the corpus grows; citations point at passagesAnswer quality depends on recall@k: the right chunk must reach the top four

Selection criteria. Token cost at 4,400 queries a day per tenant; time to first token; groundedness; citation granularity.

When another option wins. Long context wins for single-document "chat with this file" features and for small corpora of a few dozen documents with uniform access, where the retrieval machinery costs more than it saves.

Remaining risk. A question whose answer spans more than four chunks gets a partial answer; the support check and abstention keep it honest, and recall@k on multi-chunk questions is tracked separately (Architecture Deep Dive, Component 10).

Decision 12

Generator Model Size: Small and Fast vs. Large and Capable

Generation handles ~2,200 input and 350 output tokens per query with ~1,000 concurrent generations at platform peak. Retrieved text is untrusted, so the model must treat it as data, not instructions, and groundedness must stay at or above 95%.

OptionBenefitsLimits
Large generatorBetter multi-passage synthesis; more consistent instruction-following against injected textHighest per-token cost and latency; scarcer quota at peak
Small or distilled generatorLower cost and latency; more concurrency per quotaWeaker instruction-following raises injection risk; subtle mis-synthesis is harder to detect than an outright error
Routing by question complexitySpends large-model tokens only where neededA router to build and evaluate; misrouted hard questions degrade silently

Selection criteria. Measured groundedness and citation precision; an injection test suite; cost per query; time to first token under peak load.

When another option wins. A smaller generator for a tenant once its groundedness, citation precision and injection-suite results on that tenant's evaluation set meet the bar — a measured, per-deployment decision. Routing pays off at very high volume with many simple lookups.

Remaining risk. No model size makes prompt injection impossible. Structural separation of instructions and evidence, citation validation and a generator without tool privileges must hold whichever model runs (Architecture Deep Dive, Component 08).

Decision 13

Ingestion Path: Synchronous vs. Asynchronous

Connectors deliver at-least-once and in bursts: the initial sync is 750,000 chunks, and bulk edits arrive in waves. The query path must stay 99.9% available whatever ingestion is doing, and freshness is a stated lag budget rather than "immediately".

OptionBenefitsLimits
Synchronous (accepted means indexed)Simple mental model; no status to pollCouples connectors to embedder and index health; bursts stall acceptance and retries amplify the load
Asynchronous (durable accept, 202, queue, workers)Bursts become backlog, which is measurable lag; query and ingestion resources stay separateA visible window in which an accepted revision is not yet retrievable; needs a status endpoint

Selection criteria. Isolation of query availability from ingestion; behaviour under bursts; whether freshness is a monitored number; failure handling for at-least-once delivery.

When another option wins. A synchronous confirmation only as an admin "publish and verify" tool that polls revision status with a timeout — never by making the ingest path itself blocking. Small single-tenant systems with trivial volume can afford synchronous writes.

Remaining risk. A large backlog consumes the lag budget silently unless lag is measured per tenant and per lane; bootstrap syncs should run in their own lane so they cannot delay incremental edits.

Decision 14

Index Publication: In-Place Updates vs. Staged Publication

Two kinds of change reach the index. About 1,200 revisions a day each touch one document; an embedding-model, chunking or analyzer change touches all 750,000 chunks. Queries must never see a half-applied corpus-wide change.

OptionBenefitsLimits
In-place writesNo extra storage; changes visible in secondsA corpus-wide change applied this way leaves a mixed index that live queries search
Staged publication: build a new index version, check parity, flip an alias atomically, keep the old one for rollbackEvery query sees entirely the old or entirely the new versionDouble storage and compute during the build; dual-writes while it runs

Selection criteria. Whether the change touches one document or the whole corpus; consistency of what a query sees; rollback time; temporary cost.

When another option wins. Never apply a corpus-wide change in place to an index that is serving queries. In-place is acceptable for such a change only when the index is not serving — a new tenant before launch, or a planned outage short enough to fit the freshness budget.

Remaining risk. Revisions arriving during the build must be dual-written through the fenced writer; the bake period must be long enough to catch quality regressions before the old version is deleted.

Summary

Decision Map: Invariants and Workload-Dependent Choices

The 14 decisions fall into two groups. Some protect invariants and have no legitimate alternative for access-controlled enterprise content; the rest follow from this workload's numbers and should be revisited when those numbers change.

DecisionChoiceAnother option wins when
Decision 03 AuthorizationPushdown + live re-check, fail-closedNever for access-controlled content
Decision 06 PartitioningPer-tenant partitions + quotasOnly partition granularity varies
Decision 10 Cache keysFingerprint + corpus-state keys, TTL ≤ 60 sNever keyed by query text; answer caching optional
Decision 14 PublicationStaged for corpus-wide changesNever in place on a serving index
Decision 01 RetrievalBM25 ∥ ANN, RRF k = 60Pure identifier or pure narrative corpora
Decision 02 ChunkingStructure-aware, 400/100Bulk import deadline; whole-section answers
Decision 04 Sync triggerWebhooks + 2-min poll + nightly sweepSources without webhooks or change APIs
Decision 05 Auth freshnessCache ≤ 60 s + IdP events, fail closedContractual seconds-level revocation
Decision 07 InfrastructureOne managed engineCost at scale or a missing capability
Decision 08 RerankingAlways; fusion-only when degradedNegligible eval gap or very high volume
Decision 09 EmbeddingsPrecompute; shadow migrationNo measured gain from a new model
Decision 11 ContextFour retrieved chunksSingle-document or tiny corpora
Decision 12 GeneratorMid-to-large modelSmaller model passes the eval and injection suite
Decision 13 Ingestion pathAsynchronous, priority laneAdmin verification via status polling
Key takeaways
  • Every decision runs the same argument: workload constraint, options and limits, criteria, choice, and the condition under which another option wins.
  • Authorization placement, tenant isolation, cache keys and corpus-wide publication protect invariants; their alternatives are bugs, not trade-offs. Lead with them in an interview.
  • The other choices follow this workload's numbers; when the numbers change, re-derive them rather than reuse them.