Enterprise RAG Search System Design Template
Enterprise RAG search design: connectors, document and chunk ACL filtering, permission sync, hybrid retrieval, RRF, reranking, citation verification, tenancy
使い方: Use it when you design search and answers across workplace systems that must respect each source's permissions. Fill the permission guarantee (1.2) and the permission filtering deep dive (7.2) first, then retrieval and verification. The main pitfall is filtering after retrieval or after generation, which lets restricted content reach the model context.
プレビュー
System Design: [Enterprise AI search name]
Reference design for an enterprise search and answer service that retrieves from many workplace systems and answers with citations, while showing each user only what that user may read in the source system. It covers connectors, document-level and chunk-level permission filtering, permission sync, hybrid retrieval and reranking, citation grounding and answer verification, freshness, multi-tenant isolation and evaluation. Threats are mapped to the OWASP Top 10 for LLM Applications 2026, in particular LLM08:2026 Hidden Context Exposure and LLM09:2026 Vector and Embedding Weaknesses. It follows the 12-section skeleton of the System Design Document template and is pre-filled with a worked design for a multi-tenant SaaS to adapt. Replace every
[bracketed]value and every row marked Example; all numbers are illustrative examples, not benchmarks.
Document control
| Field | Value |
|---|---|
| Document ID | [SDD-ERAG-001] |
| System / feature | [Enterprise search and answer service] |
| Version | [0.1] |
| Status | [Draft / In review / Approved / Implemented / Superseded] |
| Author(s) | [@author] |
| Reviewers | [@reviewer-1], [@reviewer-2] |
| Last updated | YYYY-MM-DD |
| Classification | [Internal] |
| Deployment model | [Multi-tenant SaaS / single-tenant / on-premises] |
| Connectors in scope | [Document storage, wiki, ticketing, chat, email, code hosting, CRM] |
| Data classes in scope | [Public, internal, confidential; excluded: regulated health or card data] |
| Related | [RAG system design], [Threat model], [Privacy impact assessment], [LLM evaluation plan] |
Approvals
| Role | Name | Decision | Date |
|---|---|---|---|
| Tech lead, search platform | [@name] | [Approved / Changes requested] | YYYY-MM-DD |
| Staff / principal engineer | [@name] | [Decision] | YYYY-MM-DD |
| Security and privacy | [@name] | [Decision] | YYYY-MM-DD |
| Data protection officer | [@name] | [Decision] | YYYY-MM-DD |
| Product owner | [@name] | [Decision] | YYYY-MM-DD |
Revision history
| Version | Date | Author | Change summary |
|---|---|---|---|
| 0.1 | YYYY-MM-DD | [@name] | First draft for design review |
| 0.2 | YYYY-MM-DD | [@name] | Example: Added late permission check against the source for confidential connectors |
| 0.3 | YYYY-MM-DD | [@name] | Example: Dedicated shards for tenants above 50 million chunks |
| 1.0 | YYYY-MM-DD | [@name] | Example: Approved |
Contents
1. Overview
1.1 Problem statement
[Example: Employees search eight systems separately and cannot find the current policy, the latest design review or who owns a service. A pilot chatbot built on a single shared index answered well, but during testing it quoted a restricted board document to an intern because the index ignored source permissions. Customers of the SaaS product now ask for the same capability across their own workplace systems, with a contractual promise that no user ever sees content they cannot open in the source.]
1.2 Proposed solution
[Example: Connectors pull content and permissions from each source system into a per-tenant index. Every chunk carries the principals allowed to read it. At query time the service resolves the user's identity and group memberships, retrieves only chunks whose principals intersect that set, fuses lexical and vector results, reranks, and passes the top chunks to a model that must cite them. A verifier checks that every sentence in the answer is supported by a cited chunk before the answer is shown. For confidential sources, the top documents are re-checked against the source system's permission API at query time. Each tenant's data lives in its own index partition with its own encryption key.]
Permission guarantee: [Example: An answer or result is built only from chunks the requesting user could open in the source system at the time of the last permission sync, and for confidential sources at the time of the query.]
1.3 Decisions requested from reviewers
| ID | Question | Recommended option | Section |
|---|---|---|---|
| Q-01 | Example: Filter by permissions before or after retrieval? | Before (pre-filter in the index), plus late check for confidential sources | 7.2 |
| Q-02 | Example: Per-tenant index or shared index with tenant filter? | Pooled shards for small tenants with tenant key in every query; dedicated shards above a size threshold | 7.5 |
| Q-03 | Example: Show an answer when verification fails? | Show search results with an explanation instead of the answer | 7.4 |
| Q-04 | Example: Permission sync lag target? | p95 at or below 15 min; revocations on webhook within 2 min | 2.2 |
| [Q-nn] | [Question] | [Option] | [Section] |
1.4 45-minute interview path
| Minutes | Cover | Output on the whiteboard |
|---|---|---|
| 0 to 5 | Users, sources, the permission guarantee, single or multi-tenant | 2.1 and 2.3 |
| 5 to 10 | Corpus size, query rate, permission change rate | 3.5 with consequences |
| 10 to 15 | Search and answer API; chunk and ACL data model with access patterns | 4.1 and 5.2 |
| 15 to 25 | Diagram: connectors, sync, index, query service, reranker, generator, verifier | Section 6 |
| 25 to 40 | One deep dive: permission filtering and sync (7.2) is the expected one; pick verification (7.4) for an ML panel or tenant isolation (7.5) for a SaaS panel | Options, choice and reason |
| 40 to 45 | Failure modes, 10x row, how to prove no leakage (9.2, 12.1) | 9.1 and 10 |
2. Requirements
2.1 Core functional requirements
| ID | Requirement | Priority |
|---|---|---|
| FR-01 | Example: Connectors for [document storage, wiki, ticketing, chat, email, CRM] with content, metadata and permissions | Must |
| FR-02 | Example: Search results and generated answers filtered by the user's source permissions | Must |
| FR-03 | Example: Chunk-level filtering where the source restricts parts of a document (page restrictions, protected ranges, message recipients) | Must |
| FR-04 | Example: Answers cite the source chunks with links that open the original item | Must |
| FR-05 | Example: Answers withheld or downgraded to results when verification fails | Must |
| FR-06 | Example: Incremental sync of content and permissions, with deletions honoured | Must |
| FR-07 | Example: Tenant administrators choose connectors, exclude sites and set retention | Must |
| FR-08 | Example: Feedback (helpful, wrong, should not see this) on each answer | Should |
| FR-09 | Example: Filters by source, author, date and content type | Should |
| [FR-nn] | [Requirement] | [Must / Should / Could] |
2.2 Non-functional requirements
| ID | Quality | Target | Measured as |
|---|---|---|---|
| NFR-01 | Permission correctness | Example: Zero results from documents the user cannot read, on the canary suite in 9.2 | Daily automated leakage test |
| NFR-02 | Permission freshness | Example: p95 sync lag at or below 15 min; revocation by webhook at or below 2 min | Sync lag metric per connector |
| NFR-03 | Content freshness | Example: p95 at or below 30 min from source edit to searchable | Canary edits per connector |
| NFR-04 | Latency | Example: Search results p95 at or below 800 ms; first answer token p95 at or below 2.5 s | Gateway timing |
| NFR-05 | Answer quality | Example: Citation precision at or above 0.9 and answer faithfulness at or above 0.9 on the evaluation set | Offline evaluation per release |
| NFR-06 | Tenant isolation | Example: No query can read another tenant's partition; separate encryption key per tenant | Penetration test, key audit |
| NFR-07 | Availability | Example: 99.9% monthly for search, 99.5% for answers | SLO |
| [NFR-nn] | [Quality] | [Target] | [Measurement] |
2.3 Below the line (out of scope)
- [Example: actions in source systems (creating tickets, sending email); see Customer Support AI Agent System Design (
system-design/design-customer-support-agent) for an agent with tools] - [Example: model training on tenant data]
- [Example: field-level redaction inside a chunk (for example, masking a salary column); chunks are visible or not]
- [Example: legal hold and e-discovery exports]
- [Item deferred, with the release that will take it]
3. Capacity estimation
3.1 Assumptions
All values are Example planning inputs.
| Input | Value | Source |
|---|---|---|
| Tenants | 400 | Sales forecast |
| Users across tenants | 1.5 million | Contracts |
| Daily active share | 30% | Pilot |
| Queries per active user per day | 6 | Pilot |
| Busy window | 10 h | Main time zones |
| Peak to average | 3 | Pilot |
| Documents across tenants | 600 million | Connector inventory |
| Chunks per document | 8 | Chunking policy |
| Embedding | 768 dimensions, int8 after quantization | Model choice |
| Documents whose permissions change per day | 2% | Connector audit logs |
| Candidates reranked per query | 50 | Retrieval design |
| Prompt / output tokens per answer | 6 000 / 300 | Ten chunks of about 500 tokens plus instructions |
3.2 Corpus and index
Chunks: 600 million x 8 = 4.8 billion. Vectors at 768 B each (int8): 4.8 billion x 768 B, about 3.7 TB. HNSW links at 64 per vector and 4 B each add 4.8 billion x 256 B, about 1.2 TB. Lexical index and stored text add roughly the size of the text again.
Consequence: about 5 TB of vector data plus text cannot sit on one node. Shard by tenant: the largest tenants get dedicated shards, the long tail shares pooled shards keyed by tenant. Filtered vector search must work inside a shard, so pick an engine whose filters apply during graph traversal, not after.
3.3 Query load
Daily queries: 1.5 million x 0.3 x 6 = 2.7 million. Over 36 000 s that is 75 queries/s on average and about 225 queries/s at peak. Reranking: 225 x 50 = 11 250 query and passage pairs per second at peak.
Consequence: a cross-encoder reranker over 11 250 pairs/s needs its own GPU pool; cap candidates at 50 and skip reranking for navigational queries (exact title or person look-ups) detected by the query classifier.
3.4 Generation tokens and cost
Input tokens per day: 2.7 million x 6 000 = 16.2 billion; output: 2.7 million x 300 = 810 million. At Example prices of USD 1 per million input and USD 4 per million output: 16 200 + 3 240 = USD 19 440 per day.
Consequence: generation is the dominant cost. Answer only when the query is a question (classifier), show results without generation for navigational queries, and cache answers per (tenant, normalised query, permission set hash, index version) for a short time, invalidating any cached answer that cites a document with a revocation event.
3.5 Permission change rate
Documents with permission changes: 600 million x 0.02 = 12 million per day, about 139/s on average. If principals are copied onto each chunk, that is 12 million x 8 = 96 million chunk updates per day, about 1 100/s.
Consequence: permission updates must be partial updates of a small filterable field, not re-indexing of text and vectors. Group membership changes are handled at query time by expanding the user's groups, so a user joining a group never touches the index.
3.6 Summary
| Quantity | Estimate | Design consequence |
|---|---|---|
| Chunks | 4.8 billion, about 5 TB of vectors and links | Tenant-keyed shards, filtered ANN search |
| Peak queries | About 225/s | Stateless query service, regional replicas |
| Rerank pairs | About 11 250/s at peak | Dedicated reranker pool, skip for navigational queries |
| Generation | About 16 billion input tokens/day, about USD 19 000/day (Example) | Classify before generating; short-lived answer cache keyed by permission set |
| Permission updates | About 1 100 chunk updates/s if denormalised | Partial updates of the ACL field; group expansion at query time |
4. API design
4.1 Endpoints
| Method and path | Purpose |
|---|---|
POST /v1/search | Ranked results with snippets, filtered by permissions |
POST /v1/answer | Generated answer with citations, streamed; falls back to results |
GET /v1/documents/{id}/open | Redirect to the item in the source system after a permission check |
POST /v1/feedback | Helpful, wrong, or should-not-see report on a result or answer |
POST /v1/admin/connectors | Tenant admin configures a connector and its scope |
GET /v1/admin/connectors/{id}/status | Sync state, lag and errors |
POST /v1/admin/purge | Remove a source or an item and all derived chunks |
4.2 Request and response example
Identity comes from the validated token; the service resolves groups itself. Every citation carries the chunk ID, source link and the source's last modified time so the user can judge freshness.
POST /v1/answer
Authorization: Bearer [user token]
Content-Type: application/json
{"query": "What is the travel approval limit for directors?", "filters": {"sources": ["wiki", "policies"]}, "stream": true}
{"answer": "Directors can approve travel up to [amount] per trip [1].", "citations": [{"id": 1, "chunk_id": "[chunk id]", "title": "Travel policy", "url": "[source link]", "modified": "YYYY-MM-DD"}], "verification": "supported", "index_version": "[version]"}
4.3 Errors
| Status | Problem type | Meaning |
|---|---|---|
| 403 | identity-unresolved | Token valid but the user is not mapped to source identities |
| 403 | connector-forbidden | User may not query this connector |
| 422 | answer-unverified | Answer failed verification; results are returned instead |
| 429 | rate-limited | Tenant or user rate limit reached |
5. Data model
5.1 Entities
Documents and chunks are derived data that can be rebuilt from the source. Identity mappings and ACLs are security data: they need the same review as an authorisation system.
5.2 Access patterns
| Query | Served by | Partition key and rationale |
|---|---|---|
| Vector and lexical search for a user | Shard for the tenant, filter allowed_principals intersects the user's principal set and denied_principals does not | tenant_id: every query is tenant-scoped, so a tenant never scans another tenant's shard |
| Expand a user's groups, including nested groups | Membership graph cached per tenant, flattened per user | tenant_id plus user: computed once per session, refreshed on change events |
| Apply a permission change to a document | Partial update of ACL fields on all chunks of the document | tenant_id plus doc_id: chunks of one document co-located for one batched update |
| Delete a document and its chunks | Delete by doc_id across lexical and vector indexes | Same co-location |
| Late permission check for top documents | Source system permission API, with a short cache | Per connector; only for sources marked confidential |
| Citation link resolution | Document table by doc_id | tenant_id |
| Connector sync cursor | Connector state table | connector_id: one sync worker owns one cursor |
5.3 Storage choices
| Data | Store | Notes |
|---|---|---|
| Chunks, vectors, lexical index, ACL fields | Search engine with filtered vector search, sharded by tenant | ACL fields filterable and not returned in results |
| Documents and connector state | Relational database per region | Sync cursors, source timestamps |
| Identity and membership graph | Graph or relational store, cached in memory | Rebuilt from directory sync |
| Raw content | Object storage, encrypted with the tenant key | Needed for re-chunking and re-embedding |
| Answers and feedback | Event log with tenant retention settings | Answers contain source content: same retention as the source |
6. High-level design
| Component | Responsibility | Scales by |
|---|---|---|
| Connectors | Fetch content, metadata, permissions; handle source rate limits | Workers per connector instance |
| Permission sync | Map source identities to principals; compute document and chunk ACLs; push partial updates | Change events per second |
| Parse, chunk, embed | Extract text, split on structure, embed with the pinned model | GPU workers |
| Tenant shards | Lexical and filtered vector search | Shards and replicas |
| Query service | Classify query, expand groups, hybrid retrieval, fusion | Horizontally |
| Reranker | Cross-encoder scoring of candidates | GPUs |
| Late check | Re-verify top documents with the source for confidential connectors | Horizontally, with cache |
| Generator and verifier | Answer with citations; check support per sentence | Model pools |
7. Detailed design
7.1 Query flow
The permission filter applies inside retrieval, before any ranking or generation step sees a chunk; the late check narrows further and never widens.
7.2 Deep dive 1: permission filtering and permission sync
| Option | How it works | Strengths | Weaknesses |
|---|---|---|---|
| A. Post-filter | Retrieve top k, then drop chunks the user cannot read | Simple index | Returns too few results; the model may see dropped chunks if the order of steps slips; leaks through counts and timings |
| B. Pre-filter in the index (early binding) | Principals stored on each chunk; query filter by the user's principals | Correct top k from permitted content only; fast | Index must be updated on every permission change; large principal sets |
| C. Query the source at query time (late binding) | Ask each source whether the user can read each candidate | Always current | Slow, source rate limits, many sources have no batch API |
| D. B plus C for sensitive sources | Pre-filter for all; re-check the top documents against the source for connectors marked confidential | Correct and fresh where it matters | Extra latency on confidential sources |
Choice: D. Reason: pre-filtering guarantees that nothing outside the user's permissions enters the candidate set or the model context, and the late check closes the sync-lag window for the few sources where a minute of staleness is unacceptable.
Rules for the permission path:
| Rule | Detail |
|---|---|
| ACL-01 (Example) | Map every source identity (email, source user ID, group ID) to one tenant principal; unmapped identities get no access |
| ACL-02 (Example) | Store allow and deny principals on each chunk; deny wins |
| ACL-03 (Example) | Chunk ACL is the intersection of the document ACL and any section-level restriction (page restriction, protected range, message recipients) |
| ACL-04 (Example) | Public-within-tenant content uses a tenant-wide principal, never an empty ACL; an empty ACL means nobody |
| ACL-05 (Example) | Expand nested groups at query time from the cached membership graph; cap expansion depth and alert on cycles |
| ACL-06 (Example) | Revocation events (user removed, document unshared) are processed ahead of content updates in the sync queue |
| ACL-07 (Example) | On any error resolving permissions, return no results from that source (fail closed) |
Chunk-level permissions need care at chunking time: never let one chunk span two sections with different restrictions. Split on permission boundaries first, then on structure.
Large principal sets (users in thousands of groups) make filters slow. Use the engine's terms filter on a keyword field, keep the field out of the returned payload, and precompute a per-user principal list when it exceeds a threshold.
7.3 Deep dive 2: hybrid retrieval and reranking
| Option | Strengths | Weaknesses |
|---|---|---|
| A. Lexical only (BM25) | Exact names, codes, acronyms | Misses paraphrases |
| B. Vector only | Paraphrases and intent | Weak on identifiers and rare terms |
| C. Hybrid with reciprocal rank fusion (RRF), then cross-encoder rerank | Combines both, robust without score calibration | Two retrievals plus a rerank stage |
Choice: C. Retrieve 50 from each retriever within the ACL filter, fuse with RRF, rerank the top 50 with a cross-encoder, keep 10 for generation. Reason: enterprise queries mix identifiers ("PRJ-2231 status") and intent ("how do I expense a conference"); RRF needs no score normalisation between retrievers.
RRF scores a document by summing, over each ranked list, one divided by a constant plus its rank:
The constant 60 is the value used by Cormack, Clarke and Buettcher (SIGIR 2009); tune it on your evaluation set. Add freshness and authority signals (for example, official policy space, recent modification) as features in the reranker, not as hand-edited boosts.
7.4 Deep dive 3: citation grounding and answer verification
| Option | Strengths | Weaknesses |
|---|---|---|
| A. Instruct the model to cite | Cheap | Citations can point to chunks that do not support the sentence |
| B. Constrained citation format plus post-hoc check of every cited ID | Catches invented IDs | Does not catch unsupported claims with a real ID |
| C. B plus an entailment verifier per sentence | Catches unsupported sentences | Extra latency and cost |
Choice: C. Reason: an enterprise answer is acted on (policy, numbers, owners); unsupported sentences are the main quality failure.
Verification steps:
- The generator receives chunks with short IDs and must cite at least one ID per factual sentence.
- The verifier rejects any citation ID not in the provided set.
- For each sentence, a small verifier model judges whether the cited chunks support it (supported, partially supported, unsupported).
- If any sentence is unsupported, the service removes it when the remaining answer still stands, otherwise returns results with "No verified answer found" and the top documents.
- Verification outcome is stored with the answer for evaluation.
The generator's instructions tell it to answer only from the chunks, to say when they disagree, and to prefer the most recently modified source while naming both.
7.5 Deep dive 4: multi-tenant isolation
| Option | Strengths | Weaknesses |
|---|---|---|
| A. One shared index, tenant ID filter | Cheapest | A missing filter is a cross-tenant breach |
| B. Index per tenant | Strong isolation | Thousands of small indexes; operational overhead |
| C. Pooled shards for small tenants, dedicated shards above a threshold, tenant key enforced by the query service (not the caller) | Balance of cost and isolation | Two operating modes |
Choice: C, with a separate encryption key per tenant for raw content
and backups, and a query builder that adds the tenant filter itself and
rejects any query without one. Tenant routing follows the cell pattern in
Resilience and Load Shedding Design Template
(system-design/resilience-load-shedding-design).
7.6 How real systems do it
How real systems do it (paraphrased from Microsoft Learn, "Security filter pattern" for Azure AI Search, page dated August 2026). The documented pattern stores the user and group identifiers that may read a document in a filterable string collection field that is not returned in results, and applies a filter at query time that matches any of the caller's identifiers using a set-membership function rather than a long chain of equality tests, which the page notes is slower and error-prone with many identifiers. The page also warns that hiding the field from results is not itself a security control: authorisation comes from applying the filter on every query. The same documentation points to built-in document-level access control support for sources where it is available. Design lesson: pre-filter by principals on every query and never rely on field visibility for protection (7.2).
8. Scalability and performance
| Concern | Approach | Limit before redesign |
|---|---|---|
| Corpus growth | Tenant-keyed shards, dedicated shards for large tenants | Largest single tenant in one shard group |
| Filtered vector search | Engine with in-traversal filtering; fall back to brute force for very selective filters | Recall drop when filters are very selective |
| Permission churn | Partial ACL updates, revocations prioritised | Search engine update throughput |
| Connector throughput | Respect source rate limits, back-off, per-tenant concurrency | Source API quotas |
| Generation cost | Classifier, answer cache keyed by permission set hash | Share of question-type queries |
Answer latency budget (target first token p95 2.5 s, Example):
| Step | Budget |
|---|---|
| Gateway, identity, group expansion | 60 ms |
| Lexical and vector retrieval in parallel | 150 ms |
| Fusion and rerank of 50 | 200 ms |
| Late permission check (confidential sources only) | 300 ms |
| Generator time to first token | 1 200 ms |
| Headroom | 590 ms |
Verification needs complete sentences: buffer the generator stream, release each sentence to the user only after it passes, or show the answer after verification when the interface does not stream. The first-token target therefore applies to the first verified sentence, and the verifier's time for that sentence comes out of the headroom.
9. Reliability, security and operations
9.1 Failure-mode table
| Component | Failure | Detection | Blast radius | Mitigation | RTO |
|---|---|---|---|---|---|
| Permission sync | Stalled for one connector | Sync lag above 15 min | Revoked users may still see content from that source | Fail closed: exclude the connector from results when lag exceeds a hard limit | 30 min |
| Identity mapping | User not mapped after directory change | Unmapped identity count | User sees fewer results | Re-sync directory; alert tenant admin | 1 h |
| Tenant shard | Node loss | Shard health | Slower or partial results for tenants on that shard | Replicas; rebuild from object storage | 30 min |
| Reranker pool | Saturated | Queue depth | Lower ranking quality | Skip rerank, use fused order, mark degraded | 10 min |
| Generator | Outage | Error rate | No answers | Return results only | Per provider SLO |
| Verifier | Outage | Error rate | Unverified answers | Do not show answers; results only | 15 min |
| Connector | Source API change | Parse error rate | Stale content for a source | Version pinning of connector, contract tests | 1 day |
9.2 Security
Threat model per Threat Model Template (security/threat-model); privacy
per Privacy Threat Model Template (security/privacy-threat-model).
| ID | Threat | OWASP 2026 | Control |
|---|---|---|---|
| SEC-01 | User receives content they cannot read in the source | LLM02:2026 Sensitive Information Disclosure | Pre-filter (7.2), late check, fail closed, canary leakage tests |
| SEC-02 | Content the user may not see reaches the model context and is revealed by follow-up prompts | LLM08:2026 Hidden Context Exposure | Treat the whole context as visible to the user; only permitted chunks enter it; no other tenant or admin content in system prompts |
| SEC-03 | Cross-tenant retrieval, embedding inversion of stored vectors, poisoned documents that rank for many queries | LLM09:2026 Vector and Embedding Weaknesses | Tenant-scoped shards and query builder; vectors encrypted at rest and never returned by the API; anomaly detection on chunks that appear in unusually many answers |
| SEC-04 | Indexed document contains instructions that steer the answer | LLM01:2026 Prompt Injection | Chunks marked as data in the prompt; no tools available to the generator; verifier checks support |
| SEC-05 | Answer states wrong policy with confidence | LLM07:2026 Misinformation | Citation verification, "no verified answer" path, source dates shown |
| SEC-06 | Answer rendered as HTML with active content | LLM10:2026 Improper Output Handling | Sanitised Markdown rendering, link allowlist to source domains |
| [SEC-nn] | [Threat] | [OWASP ID] | [Control] |
Leakage testing: each tenant environment used for testing holds canary documents restricted to a canary group, with unique marker strings. A daily job queries as users outside that group and fails the build if any marker appears in results, answers or citations. Run the same test after every permission-related change.
9.3 Evaluation
Follow LLM Evaluation Plan Template (ai-data/llm-evaluation-plan).
| Metric | Definition | Target (Example) |
|---|---|---|
| Recall at 10 | Share of labelled relevant chunks in the top 10 after ACL filtering | At or above 0.85 |
| nDCG at 10 | Ranking quality on graded relevance labels | Tracked per release |
| Citation precision | Share of citations that support their sentence | At or above 0.9 |
| Citation recall | Share of factual sentences with a supporting citation | At or above 0.9 |
| Faithfulness | Share of answers with no unsupported sentence | At or above 0.9 |
| Leakage | Canary markers returned to unauthorised users | Zero |
| Freshness | Time from source edit to searchable | p95 at or below 30 min |
Labelled sets are built per tenant only with tenant consent; a synthetic cross-industry set covers releases.
9.4 Observability
Trace each query with retrieval, rerank, late check, generation and
verification spans per LLM and Agent Observability Specification Template
(ai-data/llm-observability-spec). Log chunk IDs, not chunk text.
| Signal | Alert threshold (Example) |
|---|---|
| Permission sync lag per connector | p95 above 15 min |
| Late-check disagreements (pre-filter allowed, source denied) | Any sustained rate above 0.1% |
| Unverified answer rate | Above 10% |
| "Should not see this" feedback | Any report pages security on call |
10. Trade-offs and alternatives
| Decision | Options | Chosen | Why | What we give up |
|---|---|---|---|---|
| Permission enforcement | Post-filter; pre-filter; late binding; pre-filter plus late check | Pre-filter plus late check for confidential sources | Correct candidate set, fresh where it matters | Extra latency on confidential sources |
| Retrieval | Lexical; vector; hybrid | Hybrid with RRF and rerank | Mixed identifier and intent queries | More components |
| Verification | None; ID check; per-sentence entailment | Per-sentence entailment | Unsupported claims are the main failure | Latency and cost |
| Tenancy | Shared index; per tenant; pooled plus dedicated | Pooled plus dedicated | Cost and isolation balance | Two modes to operate |
| What changes at 10x (6 billion documents, 2 250 queries/s) | Regional cells per tenant group; ACL stored as compact principal bitmaps; reranker distilled to a smaller model | Filters and rerank become the bottleneck | More engineering in the search engine | |
| What changes at 0.1x (40 tenants, 60 million documents) | Single cluster with per-tenant indexes; managed search service; no dedicated reranker pool, hosted rerank API | Fixed costs dominate | Less control over latency | |
| [Decision] | [Options] | [Chosen] | [Why] | [Cost] |
11. Delivery plan and risks
11.1 Milestones
| Milestone | Scope | Exit criterion | Target date |
|---|---|---|---|
| M1 | Two connectors, permission sync, search results only | Leakage canary suite passes for 14 days | YYYY-MM-DD |
| M2 | Hybrid retrieval and reranking | Recall at 10 target on evaluation set | YYYY-MM-DD |
| M3 | Answers with citations and verification | Citation precision and faithfulness targets | YYYY-MM-DD |
| M4 | Late check for confidential connectors; dedicated shards | Penetration test of tenant isolation | YYYY-MM-DD |
| M5 | Remaining connectors, admin controls | Per-connector contract tests in CI | YYYY-MM-DD |
11.2 Rollout and rollback
[Example: Enable per tenant and per connector. Answers are off by default for a tenant until its leakage tests pass. Ranking and model changes roll out behind an index version alias; rollback switches the alias.]
11.3 Risks
| ID | Risk | Likelihood | Impact | Mitigation | Owner |
|---|---|---|---|---|---|
| R-01 | Example: A source exposes permissions incompletely through its API | Medium | High | Connector certification; mark source confidential and use late check or exclude | [@name] |
| R-02 | Example: Users trust unverified answers | Medium | Medium | Verification, source dates, feedback | [@name] |
| R-03 | Example: Generation cost above forecast | Medium | Medium | Classifier, caching, model routing | [@name] |
| R-04 | Example: Oversharing in the source becomes visible through search | High | Medium | Tenant admin report of widely shared sensitive items before launch | [@name] |
| [R-nn] | [Risk] | [L / M / H] | [L / M / H] | [Mitigation] | [@name] |
R-04 deserves attention: search does not create oversharing, but it makes existing oversharing easy to find. Offer tenants a pre-launch report.
12. Appendices
12.1 Traceability
| Requirement | Design element | Verification |
|---|---|---|
| FR-02, NFR-01 | 7.2 pre-filter and late check | Canary leakage suite |
| FR-03 | 7.2 ACL-03, chunking on permission boundaries | Section-restriction test documents |
| FR-04, FR-05 | 7.4 verification | Citation precision and faithfulness |
| NFR-02 | 7.2 ACL-06, sync design | Sync lag metric |
| NFR-06 | 7.5 tenancy | Penetration test |
| [ID] | [Element] | [Test] |
12.2 Open questions
| ID | Question | Owner | Due |
|---|---|---|---|
| OQ-01 | Example: Do we index email at all, given per-message recipients? | [@name] | YYYY-MM-DD |
| OQ-02 | Example: Should tenants bring their own encryption keys? | [@name] | YYYY-MM-DD |
| [OQ-nn] | [Question] | [@name] | YYYY-MM-DD |
12.3 Glossary
| Term | Meaning |
|---|---|
| ACL | Access control list: principals allowed or denied |
| Principal | User, group or tenant-wide identity used in ACLs |
| Early binding | Permissions stored in the index and filtered at query time |
| Late binding | Permissions checked against the source at query time |
| RRF | Reciprocal rank fusion of several ranked lists |
| Cross-encoder | Model that scores a query and passage together for reranking |
| Faithfulness | Answer claims supported by the provided sources |
12.4 References
| Reference | Use |
|---|---|
| OWASP Top 10 for LLM Applications 2026, OWASP GenAI Security Project, https://genai.owasp.org/ | Threat mapping in 9.2 |
| Cormack, Clarke and Buettcher, "Reciprocal Rank Fusion outperforms Condorcet and individual Rank Learning Methods", SIGIR 2009 (doi:10.1145/1571941.1572114) | RRF in 7.3 |
| Lewis et al., "Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks", NeurIPS 2020 (arXiv:2005.11401) | RAG pattern |
| Gao et al., "Enabling Large Language Models to Generate Text with Citations", EMNLP 2023 (arXiv:2305.14627) | Citation quality metrics in 9.3 |
| Microsoft Learn, "Security filter pattern", Azure AI Search, https://learn.microsoft.com/en-us/azure/search/search-security-trimming-for-azure-search | Box in 7.6 |
RAG System Design Template (ai-data/rag-system-design) | Single-application pipeline details |
Guardrails Specification Template (ai-data/guardrails-spec) | Input and output guardrails |
LLM Inference Serving System Design (system-design/design-llm-inference-serving) | Model and vector index serving |
Standard and licence note
Based on the OWASP Top 10 for LLM Applications 2026 by the OWASP GenAI Security Project (https://genai.owasp.org/), licensed under Creative Commons Attribution-ShareAlike 4.0 (https://creativecommons.org/licenses/by-sa/4.0/), and on the published research and public documentation in 12.4. The OWASP IDs and short names are used for the mapping in 9.2; all descriptions and controls are original text. Because that mapping adapts the OWASP structure, the adapted structure is shared under CC BY-SA 4.0 and any further adaptation must carry the same licence. The box in 7.6 paraphrases public documentation and copies no text. All numbers are illustrative examples, not benchmarks. Check the current official versions of each source before use. Apart from the adapted OWASP structure, the original text is under the template licence. Template licence: MIT.
関連テンプレート
Full-Text Search Engine System Design Template
Inverted index, segments, real-time ingestion vs queries, document and time sharding, replicas, BM25 plus reranker, autocomplete boundary, relevance evaluation
システム設計Search Autocomplete System Design Template (Reference Design)
Typeahead: trie/FST/edge n-gram index, popularity and recency ranking, query-log pipeline, edge cache, latency budget, safety
システム設計Zanzibar-Style Authorization Service System Design Template
Relationship-based authorization service: relation tuples, namespace config, Check/Expand/List/Watch APIs, consistency tokens, caching, sharding, failure modes