LeetLLM
My PlanLearnGlossaryTracksPracticeBlog
LeetLLM

Your go-to resource for mastering AI & LLM systems.

Product

  • Learn
  • Glossary
  • Tracks
  • Practice
  • Blog
  • RSS

Legal

  • Terms of Service
  • Privacy Policy

© 2026 LeetLLM. All rights reserved.

All Topics
Your Progress
0%

0 of 196 articles completed

🛠️Computing Foundations0/9
Git, Shell, Linux for AIDocker for Reproducible AIPython for AI EngineeringNumPy and Tensor ShapesCUDA for ML TrainingMPS & Metal for ML on MacData Structures for AISQL and Data ModelingAlgorithms for ML Engineers
📊Math & Statistics0/9
Gradients and BackpropVectors, Matrices & TensorsLinear Algebra for MLAdam, Momentum, SchedulersProbability for Machine LearningBayesian Inference from EvidenceStatistics and UncertaintyDistributions and SamplingHypothesis Tests, Intervals, and pass@k
📚Preparation & Prerequisites0/13
Neural Networks from ScratchCNNs from ScratchTraining & BackpropagationSoftmax, Cross-Entropy & OptimizationRNNs, LSTMs, GRUs, and Sequence ModelingAutoencoders and VAEsThe Transformer Architecture End-to-EndLanguage Modeling & Next TokensFrom GPT to Modern LLMsPrompt Engineering FundamentalsCalling LLM APIs in ProductionFirst AI App End-to-EndThe LLM Lifecycle
🧮ML Algorithms & Evaluation0/15
Linear Regression from ScratchLogistic Regression and MetricsDecision Trees, Forests, and BoostingReinforcement Learning BasicsValidation and LeakageStatistical Learning and GeneralizationConformal Prediction and UncertaintyClustering and PCAProbabilistic Generative ModelsCore Retrieval AlgorithmsDecoding AlgorithmsExperiment Design and A/B TestingCausal Inference and CounterfactualsPyTorch Training LoopsDataset Pipelines and Data Quality
📦Production ML Systems0/6
Feature Engineering for Production MLBatch and Streaming Feature PipelinesGradient Boosted Trees in ProductionRanking and Recommendation SystemsForecasting and Anomaly DetectionMonitoring Predictive Models
🧪Core LLM Foundations0/8
The Bitter Lesson & ComputeBPE, WordPiece, and SentencePieceStatic to Contextual EmbeddingsPerplexity & Model EvaluationFile Ingestion for AIChunking StrategiesLLM Benchmarks & LimitationsInstruction Tuning & Chat Templates
🧰Applied LLM Engineering0/25
Dimensionality Reduction for EmbeddingsCoT, ToT & Self-Consistency PromptingFunction Calling & Tool UseMCP & Tool Protocol StandardsContext EngineeringPrompt Injection DefenseResponsible AI GovernancePrivacy-Preserving Machine LearningData Labeling and Human FeedbackEvaluating AI AgentsProduction RAG PipelinesHybrid Search: Dense + SparseReranking and Cross-Encoders for RAGRAG Evaluation for Reliable AnswersLLM-as-a-Judge EvaluationBias & Fairness in LLMsHallucination Detection & MitigationLLM Observability & MonitoringExperiment Tracking with MLflow and W&BPrompt Optimization with DSPyModel Versioning & DeploymentSemantic Caching & Cost OptimizationLLM Cost Engineering & Token EconomicsModel Gateways, Routing, and FallbacksDesign an Automated Support Agent
🎓Portfolio Capstones0/9
Capstone: Delivery ETA PredictionCapstone: Product RankingCapstone: Demand ForecastingCapstone: Image Damage ClassifierCapstone: Production ML PipelineCapstone: Document QACapstone: Eval DashboardCapstone: Fine-Tuned ClassifierCapstone: Reproducible ML Study
🧠Transformer Deep Dives0/8
Sentence Embeddings & Contrastive LossEmbedding Similarity & QuantizationScaled Dot-Product AttentionVision Transformers and Image EncodersPositional Encoding: RoPE & ALiBiLayer Normalization: Pre-LN vs Post-LNMechanistic InterpretabilityDecoding Strategies: Greedy to Nucleus
🧬Advanced Training & Adaptation0/17
Scaling Laws & Compute-Optimal TrainingPre-training Data at ScaleBuild GPT from Scratch LabJAX for PyTorch ResearchersContinued Pretraining for Domain ShiftSynthetic Data PipelinesSupervised Fine-Tuning PipelineMixed Precision TrainingDistributed Training: FSDP & ZeROLoRA & Parameter-Efficient TuningTraining Run OperationsReward Modeling from Preference DataRLHF & DPO AlignmentConstitutional AI & Red TeamingRLVR & Verifiable RewardsKnowledge Distillation for LLMsModel Merging and Weight Interpolation
🤖Advanced Agents & Retrieval0/16
Vector DB Internals: HNSW & IVFAdvanced RAG: HyDE & Self-RAGGraphRAG & Knowledge GraphsRAG Security & Access ControlStructured Output GenerationReAct & Plan-and-ExecuteGuardrails & Safety FiltersCode Generation & SandboxingComputer-Use / GUI / Browser AgentsHuman-in-the-Loop Agent ArchitectureAI Coding Workflow with AgentsAgent Memory & PersistenceAgent Failure & RecoveryRecursive Language Models (RLM)Multi-Agent OrchestrationCapstone: Production Agent
⚡Inference & Production Scale0/31
Inference: TTFT, TPS & KV CacheMulti-Query & Grouped-Query AttentionKV Cache & PagedAttentionPrefix Caching and Prompt CachingFlashAttention & Memory EfficiencyContinuous Batching & SchedulingScaling LLM InferenceModel Parallelism for LLM InferenceAccelerator Architecture Field GuideCorrect GPU Profiling and BenchmarkingGPU Kernel Performance Engineering LabGPU Parallel Primitives LabGPU Compilers and Kernel LanguagesGEMM From Naive CUDA to Tensor CoresLow-Precision GPU ProgrammingGPU Runtime Execution LabAI-Generated Kernel EngineeringModel Quantization: GPTQ, AWQ & GGUFLocal LLM DeploymentSLM Specialization & Edge DeploymentSpeculative DecodingLong Context Window ManagementMixture of Experts ArchitectureMamba & State Space ModelsReasoning & Test-Time ComputeAdvanced MLOps & DevOps for AIGPU Serving & AutoscalingGPU Failure OperationsDistributed Inference Data PlaneServing Benchmark EngineeringA/B Testing for LLMs
🏗️System Design Capstones0/9
Content Moderation SystemCode Completion SystemMulti-Tenant LLM PlatformLLM-Powered Search EngineVision-Language Models & CLIPMultimodal LLM ArchitectureDiffusion Models: Images & TextReal-Time Voice AI AgentReasoning Agent System Design
🎤AI Lab Interviewing0/4
AI Lab Coding Interview: Python SystemsAI Lab System Design InterviewAI Lab Behavioral InterviewAI Lab Technical Presentation
🔬Project Deep Dives0/17
Deep Dive - vLLMDeep Dive - SkyRLDeep Dive - FlashAttentionDeep Dive - FlashInferDeep Dive - DeepGEMMDeep Dive - NCCLDeep Dive - MegatronDeep Dive - DeepSpeedDeep Dive - RayDeep Dive - MLflowDeep Dive - PyTorchDeep Dive - TransformersDeep Dive - SGLangDeep Dive - slimeDeep Dive - DeepEPDeep Dive - TinkerDeep Dive - Light-PEFT
Back to Topics
LearnSystem Design CapstonesLLM-Powered Search Engine
🏗️HardSystem Design

LLM-Powered Search Engine

Design CodeAtlas search around one parser-v2 query: freshness routing, hybrid retrieval, evidence packing, citation checks, and streaming synthesis.

41 min read
Learning path
Step 170 of 196 in the full curriculum
Multi-Tenant LLM PlatformVision-Language Models & CLIP

Personalize this lesson

Adapt explanations and teaching visuals to your background and preferred voice.

CodeAtlas has a search failure that looks like a win. A developer asks which deprecation policy applies to parser-v2 SDK migration for service accounts and the large language model (LLM) returns a fluent answer based on an old policy or workspace-b's private roadmap. Search was fast; it still failed the user.

Multi-tenant serving made tenant identity, cost, and visibility part of every request. Search carries those boundaries into evidence: a response can be fast and fluent yet still be wrong if it uses an old policy, cites a private roadmap, or merges documents that apply to different workspaces.

You're the Staff AI Engineer at CodeAtlas. Developers ask, "can service accounts rotate OAuth tokens every 90 days?" They also ask, "what happens if parser-v2 deprecation affects our SDK migration?" For each query, the product must find allowed API docs, release notes, and workspace policies, then write a cited answer without letting private roadmaps cross a tenant boundary.

Before looking at components, predict the hard case. If a private roadmap is semantically closer than the public deprecation policy, which candidate is allowed to influence ranking? If two allowed documents disagree, what should the user see while the system checks their citations? Keep those answers in mind; each later stage owns one part of that contract.

If embeddings or chunking feel rusty: embeddings turn sentences into dense vectors so similar meanings sit close together. Chunking splits long documents into short passages so retrieval can select a useful span. You'll reuse both, plus the hybrid retriever from Hybrid Search, inside an interactive latency budget.

What makes LLM-powered search different from a chatbot over documents?

Answer

It must retrieve fresh, permission-safe evidence, rank it, synthesize only supported claims, attach citations, and stream an answer within an interactive search latency budget.

The parser-v2 query isn't a list of links

The old CodeAtlas product returned ranked hits for "parser-v2 deprecation." A developer still had to open the policy page, the service-account exception, and the SDK migration note, then decide which sentence governed their case.

The new product must retrieve those documents, remove private-roadmap from workspace-a's evidence, and show how each answer sentence is supported. That's Retrieval-Augmented Generation (RAG)[1]Reference 1Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks.https://arxiv.org/abs/2005.11401 in product terms: generation reads retrieved evidence instead of treating model memory as the source of truth.

Once synthesis enters the product, citation binding, abstention, and authorization failures enter with it. The pipeline therefore retrieves with keyword and embedding signals, reranks candidates, applies visibility and freshness policy, and only then writes a short answer whose claims can be checked.

What CodeAtlas used to return vs what it returns now

FeatureOld keyword searchGenerative search for parser-v2
OutputRanked links to policy and SDK pagesAnswer plus cited evidence
InteractionQuery and result navigationAnswer and optional follow-up
User validationDeveloper opens each sourceDeveloper inspects cited spans
Source trackingThe page is the resultClaim-to-source binding is an application duty

What does the LLM add after traditional retrieval?

Answer

The LLM performs synthesis: it reads retrieved evidence, resolves conflicts, explains the answer in user language, and binds claims to citations. It should not replace retrieval or source verification.

What the parser-v2 answer has to hit

These are example SLOs for CodeAtlas, not universal constants. They exist so the later stages can argue about the same budget.

DimensionConstraintExample target
LatencyTime to first token (TTFT)~0.8-1.5 seconds
ThroughputPeak queries per second (QPS)1K-10K QPS per region
QualityAnswer accuracyHigh citation precision, low unsupported claims
CostAverage query costLow single-digit cents at scale
FreshnessInformation latencySeconds to minutes for hot updates, hours for stable docs
IsolationTenant visibilityRetrieval, caches, and citations stay inside workspace isolation

Why must "workspace isolation" be a first-class requirement in this design?

Answer

Search evidence can include private roadmaps, incident notes, contracts, and policy exceptions. Retrieval, caching, ranking, and citations must enforce tenant visibility before the LLM ever sees context.

How the pipeline works end to end

The architecture has four query-time layers: plan the query, retrieve candidate evidence, pack the best evidence into context, and generate an answer while verifying citations. Each layer narrows what the next one is allowed to see.

Separate from this online path, a background ingestion system crawls or receives source updates, cleans them, chunks them, and keeps sparse and dense indexes fresh. Without that path, an "AI search engine" becomes a prompt wrapper around stale data. With it, query-time code can spend its budget on choosing and checking evidence instead of parsing documents.

Two-lane CodeAtlas search architecture for the parser-v2 SDK query. Offline: parser-v2.md is chunked to "within 30 days" and written into a hybrid BM25, vector, and ACL index. Online: the workspace-a question retrieves only authorized docs, a cross-encoder reranks 50 passages, packing keeps 8 source IDs, and the answer cites [1] and [2] with a support check.
parser-v2.md is indexed offline with ACL tags. At query time, workspace-a retrieval drops private-roadmap before rerank packs eight cited chunks.

Crawl and index before query time

Start with one update: parser-v2.md changes its migration window and arrives with an ACL tag for workspace-a. Before looking at query code, predict what must survive ingestion. The crawler needs the new revision, the parser needs stable text and source offsets, and the indexer needs chunks that carry tenant, document version, and freshness metadata alongside sparse and dense representations.

That metadata is part of searchable evidence, not a side table to consult after generation. If the crawler drops the ACL, an unauthorized chunk can enter candidate generation. If the index keeps the old revision, a fast answer can still be stale. Store enough lineage to trace each chunk back to its source and version, then publish the new index view only when those fields are present.

Tracing a concrete query

Use one request as the debugging case: "Which deprecation policy applies to parser-v2 SDK migration for service accounts?" Before reading the trace, predict two things: which sources should survive authorization, and which single passage you'd keep if the answer had room for only one. This request needs both policy and exception, so it exposes every shortcut that loses one.

StageWhat the system does
Query planThe router classifies the question as a comparison needing both API deprecation policy and service-account migration rules. It decomposes the query into sub-queries for "parser-v2 deprecation policy" and "service-account SDK migration exceptions."
RetrievalBM25 (Best Match 25), a lexical ranking function from the probabilistic relevance framework, pulls the exact "Parser v2 Deprecation" and "Service Account Migration" pages.[2]Reference 2The Probabilistic Relevance Framework: BM25 and Beyond.https://doi.org/10.1561/1500000019 Dense retrieval finds a related FAQ about legacy SDK workflows. Together the first stage recalls about 1,000 candidates.
RerankingAfter authorization, a cross-encoder scores a 50-passage slice. Packing then keeps 8 chunks with source IDs.
SynthesisThe LLM reads those packed passages and writes a two-sentence answer that cites [1] and [2].
VerificationA verifier returns support labels for answer claims against cited text. Product policy decides whether pending or unsupported claims are hidden, marked, or regenerated.

Keep this trace as a debugging breadcrumb. When a final answer fails, we can ask whether the planner chose the wrong route, retrieval missed a source, reranking buried it, packing dropped it, or verification rejected the claim.


Why separate offline ingest from the online query path?

Answer

Ingest normalizes, chunks, indexes, and refreshes source material before users ask questions. The online path then stays focused: plan, retrieve, rerank, pack evidence, synthesize, and verify. That boundary also gives each freshness decision a home: source updates belong to ingest, while query urgency belongs to planning.

Plan the parser-v2 question before you retrieve

Query understanding

The first hop answers an operational question: what kind of truth does this query need? Classify intent, choose a route, and decide whether retrieval needs rewriting. A large synthesis model can do that work, but using it for every CodeAtlas query adds measured latency and cost that "find the parser-v2 deprecation policy" doesn't need.

Predict the route before naming the machinery. "What is build-1042 doing right now?" should bypass document search. "Compare service-account exceptions" needs several sources. "What changed in the latest guide?" needs freshness. A small router or encoder-class classifier can make those choices, then attach hints such as "prefer exact matches" or "fresh data required." Benchmark it on your hardware and traffic shape because routing errors become retrieval errors downstream.

If a query is vague, HyDE (Hypothetical Document Embeddings) is one recall lever: generate a hypothetical relevant document, embed that document, and use its vector as an alternate dense-search query.[3]Reference 3Precise Zero-Shot Dense Retrieval without Relevance Labels.https://arxiv.org/abs/2212.10496 That generated text may contain false details. Treat HyDE as a way to find candidates, never as answer evidence, and validate whether it improves recall for your query mix.

Routing targets

Not every query should hit the same retriever. Exact, structured questions often route to APIs, SQL, or an entity store first, then optionally use the LLM for verbalization. Treating every request like unstructured RAG adds latency and can make numeric answers less reliable because prose is standing in for live state.

Query patternBest first backendWhy
"What is deploy job build-1042 doing right now?"Live APIFreshness and exact values matter more than semantic similarity
"Find the parser-v2 deprecation policy"BM25 + doc storeExact title and keyword match dominate
"Compare OAuth rotation exceptions for service accounts"Hybrid sparse + denseNeeds multi-document synthesis and terminology overlap
"What changed in the latest SDK migration guide?"Fresh web / hot indexRecency matters, then reranking decides quality
Diagram showing Incoming CodeAtlas query, Planner, build-1042 / quota, and Structured API.
Incoming CodeAtlas query, Planner, build-1042 / quota, and Structured API.

The planner chooses the branch, not the answer. Live numeric state goes to an API, exact titles go to BM25, and comparisons pay for hybrid retrieval. A "latest" query starts with the hot tier, where freshness can be measured instead of guessed from wording.

ComponentPurposeExample
Intent ClassifierRoute to appropriate pipeline"build-1042 status" -> direct API call
Query DecomposerBreak complex queries into sub-queries"Compare service-account rotation exceptions" -> 3 sub-queries
Safety FilterBlock harmful queries earlyContent policy enforcement
Freshness DetectorDetermine if real-time data is needed"latest SDK migration guide" -> force fresh crawl

The planner below is a fast stand-in for a small classifier. Give it a raw query and inspect the contract it returns: intent, route, sub-queries, filters, and whether live data is required. Those fields aren't decoration; downstream retrieval uses them to enforce the route we just chose.

routing-targets.py
1import json 2from dataclasses import asdict, dataclass, field 3 4@dataclass(frozen=True) 5class QueryPlan: 6 intent: str 7 route: str 8 sub_queries: list[str] = field(default_factory=list) 9 filters: dict[str, str | bool] = field(default_factory=dict) 10 needs_fresh_data: bool = False 11 complexity: str = "simple" 12 13def understand_query(user_query: str) -> QueryPlan: 14 """Plan with fast routing logic; swap rules for a small classifier in production.""" 15 text = user_query.lower() 16 17 if any(term in text for term in ("build-", "quota", "runner status")): 18 return QueryPlan( 19 intent="factual", 20 route="structured_api", 21 filters={"requires_live_data": True}, 22 needs_fresh_data=True, 23 ) 24 25 if "parser-v2" in text and "service account" in text: 26 return QueryPlan( 27 intent="comparison", 28 route="hybrid_search", 29 sub_queries=[ 30 "parser-v2 deprecation policy", 31 "service-account SDK migration exceptions", 32 ], 33 filters={"doc_type": "policy", "tenant_scoped": True}, 34 complexity="moderate", 35 ) 36 37 if "latest" in text or "right now" in text: 38 return QueryPlan( 39 intent="exploratory", 40 route="hot_index", 41 sub_queries=[user_query], 42 needs_fresh_data=True, 43 complexity="moderate", 44 ) 45 46 return QueryPlan( 47 intent="exploratory", 48 route="hybrid_search", 49 sub_queries=[user_query], 50 complexity="moderate", 51 ) 52 53plan = understand_query("Which deprecation policy applies to parser-v2 SDK migration for service accounts?") 54assert understand_query("What changed in the latest SDK migration guide?").route == "hot_index" 55print(json.dumps(asdict(plan), indent=2))
Output
1{ 2 "intent": "comparison", 3 "route": "hybrid_search", 4 "sub_queries": [ 5 "parser-v2 deprecation policy", 6 "service-account SDK migration exceptions" 7 ], 8 "filters": { 9 "doc_type": "policy", 10 "tenant_scoped": true 11 }, 12 "needs_fresh_data": false, 13 "complexity": "moderate" 14}

When should a query planner route to a structured API instead of generic RAG?

Answer

Use a structured API for exact, live, or numeric facts such as deploy status, quota remaining, entitlement, or current feature-flag state. Generic semantic retrieval is weaker for those values and can return stale text.

Multi-stage retrieval

Now predict what broad recall should return for our parser-v2 question. BM25 should find the exact policy title and service-account phrase. Dense retrieval should add the FAQ that describes the same workflow in different words. Neither list is the answer; each is a candidate set with different blind spots.

We use hybrid retrieval to broaden that candidate recall before refining for precision. Keyword search can miss synonyms. Dense vector embeddings, which place queries and documents near one another when their meanings are related, can miss exact names, product IDs, or acronyms. Hybrid Search built this lane; this capstone adds freshness routing, workspace isolation, evidence packing, and a search latency budget.

At query time, run sparse retrieval and dense retrieval in parallel, then fuse their ranked lists with Reciprocal Rank Fusion (RRF).[4]Reference 4Reciprocal Rank Fusion Outperforms Condorcet and Individual Rank Learning Methods.https://dl.acm.org/doi/10.1145/1571941.1572114 A second stage applies a more expensive reranker, often a cross-encoder or late-interaction model,[5]Reference 5ColBERT: Efficient and Effective Passage Search via Contextualized Late Interaction over BERT.https://arxiv.org/abs/2004.12832 to 50 passages. Packing then keeps 8 chunks with source IDs. The order matters: cheap methods widen the funnel, while expensive methods spend compute only on candidates that survived the first pass.

Hybrid retrieval funnel for the parser-v2 query. Fan-out recalls 1000 BM25, dense, and hot-index candidates including private-roadmap. Access control drops private-roadmap before Reciprocal Rank Fusion. A cross-encoder reranks 50 passages, then packing keeps 8 chunks with source IDs for citations [1] and [2].
workspace-a fan-out includes private-roadmap. ACL drops that page before RRF, then the reranker cuts 50 passages down to 8 packed chunks.

Why hybrid retrieval?

BM25 is strong at exact keyword matching; dense retrieval captures semantic similarity.[2]Reference 2The Probabilistic Relevance Framework: BM25 and Beyond.https://doi.org/10.1561/1500000019 Dense retrieval typically uses bi-encoders, which encode queries and documents separately so document vectors can be indexed ahead of time. Some systems instead use ColBERT (Contextualized Late Interaction over BERT): query and document tokens interact after each side has been encoded.[5]Reference 5ColBERT: Efficient and Effective Passage Search via Contextualized Late Interaction over BERT.https://arxiv.org/abs/2004.12832 On mixed traffic, the combination can surface candidates that either retriever would miss alone.

In practice, hybrid search is more than running two retrievers. Chunking decides what can be found, rank fusion decides how lists meet, and evidence assembly decides what the model can quote. A useful pattern embeds short child chunks for recall, then fetches their larger parent passage for synthesis so surrounding context survives.

Fuse on rank instead of raw score because BM25 and dense-similarity scores use different scales. Adding them without calibration is brittle. Reciprocal Rank Fusion scores documents from rank positions: score(d) = sum over lists of 1 / (k + rank(d)). Cormack et al. evaluated RRF with k = 60; use that as a starting point to validate, not a universal optimum.[4]Reference 4Reciprocal Rank Fusion Outperforms Condorcet and Individual Rank Learning Methods.https://dl.acm.org/doi/10.1145/1571941.1572114 Apply authorization filtering before fusion. Otherwise, a high-ranked forbidden document can change ranks even if you remove it later.

authorized-rrf-fusion.py
1from collections import defaultdict 2 3tenant = "workspace-a" 4acl = {"deprecation": {"workspace-a"}, "rotation": {"workspace-a"}, "private-roadmap": {"workspace-b"}} 5sparse_ranked = ["deprecation", "private-roadmap", "rotation"] 6dense_ranked = ["private-roadmap", "rotation", "deprecation"] 7 8def allowed_ranked(document_ids: list[str]) -> list[str]: 9 return [document_id for document_id in document_ids if tenant in acl[document_id]] 10 11def reciprocal_rank_fusion(rankings: list[list[str]], k: int = 60) -> list[str]: 12 scores: defaultdict[str, float] = defaultdict(float) 13 for ranking in rankings: 14 for rank, document_id in enumerate(ranking, start=1): 15 scores[document_id] += 1 / (k + rank) 16 return sorted(scores, key=scores.get, reverse=True) 17 18fused_raw = reciprocal_rank_fusion([sparse_ranked, dense_ranked]) 19fused = reciprocal_rank_fusion([allowed_ranked(sparse_ranked), allowed_ranked(dense_ranked)]) 20assert fused_raw[0] == "private-roadmap" 21assert "private-roadmap" not in fused 22print("fused_without_acl:", fused_raw) 23print("authorized_fused_docs:", fused)
Output
1fused_without_acl: ['private-roadmap', 'deprecation', 'rotation'] 2authorized_fused_docs: ['deprecation', 'rotation']

Freshness and index tiers

Freshness isn't one global ranking switch. Real search products rarely use one monolithic index, because a changelog and a stable API reference need different update paths. A useful pattern is a hot-warm split:

TierTypical contentUpdate patternQuery behavior
HotNews, changelogs, live API snapshotsSeconds to minutesQueried when freshness detector fires
WarmProduct docs, internal wikis, help center contentMinutes to hoursDefault hybrid search tier
ColdStable archive and long-tail corpusBatch compactionBackfill recall when warm tier misses

Freshness should influence ranking, not replace relevance. Combine retrieval score with time decay, then let the reranker decide whether the newer document is better. If you rank by recency alone, a low-quality post from five minutes ago can outrank the canonical source. For "latest migration guide," the hot tier narrows the time window; it doesn't get to skip authorization or evidence checks.

Why does hybrid retrieval beat dense-only retrieval for developer-doc search?

Answer

Dense retrieval captures semantic paraphrases, but BM25 preserves exact identifiers such as API versions, issue IDs, feature flags, and policy titles. Rank fusion gives the reranker both kinds of candidates.

Latency budget

TTFT is a critical constraint of this system. The values below are an illustrative budget, not a promise. Before reading the total, predict the critical path: if planning overlaps retrieval, which stage contributes its full duration, and which stage is hidden under another? Validate the answer against product objectives and measured stage timings.

StageExample latencyMethodParallelism
Query planning10-50msSmall router / rewriterCan overlap with initial retrieval
Retrieval and fetch150-500msSparse, dense, API, or web callsFan-out within allowed routes
Reranking50-150msCross-encoder GPU batchBatch allowed candidates together
Evidence packing10-30msDedupe, trim, retain source IDsSequential after ranking
LLM prefill to first token300-800msStreaming generationSequential after context exists
Total to first token~0.8-1.5 secondsExample product objectiveDepends on overlap and tail latency
Time-to-first-token timeline for the parser-v2 query on one 800 ms axis. A 260 ms retrieval bar overlaps a 40 ms planner bar. After 260 ms, rerank (90 ms), a 20 ms pack sliver, and 430 ms of prefill run in sequence. First token is at 800 ms: max(40, 260) + 90 + 20 + 430, not the 840 ms sum of every stage.
On this query, planning hides under the 260 ms retrieval bar. The remaining serial path is rerank, a 20 ms pack, and 430 ms of prefill, so first token is at 800 ms rather than 840 ms.

Note: If you support speculative retrieval, cheap lexical or dense search can begin before rewrite finishes. If you don't, planning and retrieval stack sequentially. Make that overlap decision explicit in the TTFT budget; otherwise a mean stage time can hide the real critical path.

ttft-critical-path.py
1planner_ms = 40 2initial_retrieval_ms = 260 3rerank_ms = 90 4pack_ms = 20 5prefill_to_first_token_ms = 430 6 7sequential_ttft_ms = planner_ms + initial_retrieval_ms + rerank_ms + pack_ms + prefill_to_first_token_ms 8overlapped_ttft_ms = max(planner_ms, initial_retrieval_ms) + rerank_ms + pack_ms + prefill_to_first_token_ms 9 10assert sequential_ttft_ms == 840 11assert overlapped_ttft_ms == 800 12print("sequential_ttft_ms:", sequential_ttft_ms) 13print("overlapped_ttft_ms:", overlapped_ttft_ms)
Output
1sequential_ttft_ms: 840 2overlapped_ttft_ms: 800

What is the main latency tradeoff in query planning?

Answer

A better rewrite can improve recall, but waiting for it serially delays retrieval. Speculative retrieval starts cheap searches from the raw query while the planner finishes, then merges or cancels results.

Grounded synthesis with citations

The synthesis prompt can require citations and abstention, but prompting can't prove grounding. The product must keep source IDs attached to evidence, check generated claims, and decide what may be shown while support is pending or missing.

Before packing, predict the failure. If the correct exception lands in the middle of 40 passages, will the model use it? If a citation points to a document title but not to the sentence that supports the claim, can a reviewer verify it? These questions make context layout and citation binding part of the system, not prompt decoration.

Grounding has a context problem too. Put the strongest chunks at the start and end of the prompt, leave room for the answer, and avoid stuffing mediocre passages into the middle of a huge context. Long-context models still show "lost in the middle" behavior: Liu et al. measured a U-shaped use of context, where evidence in the center of the context window is used less reliably than evidence at the edges.[6]Reference 6Lost in the Middle: How Language Models Use Long Contextshttps://aclanthology.org/2024.tacl-1.9/ Reranking and tight top-K therefore protect both quality and latency.

Visible citations can be a subset of retrieved evidence, but each one must bind to a specific claim and source span. A source list is provenance metadata, not proof that every generated sentence is supported.

Before generation, pack allowed passages into a bounded context while preserving source IDs:

evidence-packing-boundary.py
1from dataclasses import dataclass 2 3@dataclass(frozen=True) 4class Passage: 5 source_id: int 6 tenant: str 7 text: str 8 tokens: int 9 rerank_score: float 10 11def pack_allowed(passages: list[Passage], tenant: str, budget: int) -> list[Passage]: 12 selected: list[Passage] = [] 13 used = 0 14 for passage in sorted(passages, key=lambda item: item.rerank_score, reverse=True): 15 if passage.tenant != tenant or used + passage.tokens > budget: 16 continue 17 selected.append(passage) 18 used += passage.tokens 19 return selected 20 21candidates = [ 22 Passage(1, "workspace-a", "Parser-v2 migration starts within 30 days.", 7, 0.95), 23 Passage(2, "workspace-b", "Private roadmap milestone.", 4, 0.99), 24 Passage(3, "workspace-a", "Service-account exceptions require security approval.", 5, 0.82), 25] 26packed = pack_allowed(candidates, tenant="workspace-a", budget=12) 27assert [passage.source_id for passage in packed] == [1, 3] 28print("packed_source_ids:", [passage.source_id for passage in packed])
Output
1packed_source_ids: [1, 3]

Compression after selection, not instead of it

Sometimes authorized, reranked evidence is still too large for the prefill budget. Query-aware prompt compression is a packing lever after access filtering, deduplication, and passage selection. It isn't permission filtering, retrieval, or a license to keep every weak result.

LLMLingua uses a smaller language model's cross-perplexity signal to estimate which prompt tokens carry less information for the target model, then applies coarse-to-fine pruning.[7]Reference 7LLMLingua: Compressing Prompts for Accelerated Inference of Large Language Models.https://arxiv.org/abs/2310.05736 The paper reports compression ratios as high as 20× on GSM8K, BBH, ShareGPT, and Arxiv-March23 with limited performance loss. That result doesn't guarantee safe compression for cited search evidence. Those tasks don't test claim-to-span verification, so evaluate support and answer quality at each target ratio.

Protect exact spans before compression: source IDs, document titles, code symbols, version numbers, dates, quantities, negation, and quoted policy language. Keep a mapping from compressed text back to original source offsets. The synthesizer may read compressed evidence, but citation verification should resolve claims against the original passage.

Packing stepInputFailure if skipped
Authorize and rerankRaw retrieved candidatesCompression can retain forbidden or irrelevant text
Select and deduplicateAllowed ranked passagesRepeated weak evidence consumes the budget
Protect evidence spansIDs, numbers, quotations, and source anchorsCompression can change the fact the answer must preserve
Compress remaining textQuery-aware candidate contextPrefill stays larger than the latency and cost envelope
Verify against originalsDraft claims plus source-offset mapA fluent answer can cite wording that compression distorted

Track raw_tokens, selected_tokens, compressed_tokens, protected-span survival, retrieval recall, citation support, and TTFT together. If the correct exception or negation disappears, a smaller prompt is a regression.

Where does LLMLingua-class compression belong in evidence packing?

Answer

After authorization, reranking, deduplication, and passage selection. Protect exact evidence spans, compress only the remaining text, retain source-offset mappings, and verify generated claims against original passages.

Keeping temperature low reduces answer variance, but it doesn't create factuality. Predict what should happen when one of the two required passages is absent: the synthesizer may draft, but publication must wait for evidence checks. The next deterministic example turns that support gate into code.

grounded-answer-contract.py
1from dataclasses import dataclass 2 3@dataclass(frozen=True) 4class Source: 5 source_id: int 6 title: str 7 content: str 8 9def contains_all(source: Source | None, required_terms: tuple[str, ...]) -> bool: 10 if source is None: 11 return False 12 text = source.content.lower() 13 return all(term in text for term in required_terms) 14 15def grounded_answer(sources: list[Source]) -> str: 16 by_id = {source.source_id: source for source in sources} 17 if not contains_all(by_id.get(1), ("parser-v2", "within 30 days")): 18 return "Insufficient cited evidence to answer." 19 if not contains_all(by_id.get(2), ("security approval", "service-account")): 20 return "Insufficient cited evidence to answer." 21 return ( 22 "Start the parser-v2 SDK migration within 30 days [1]. " 23 "Service-account OAuth rotation exceptions require security approval [2]." 24 ) 25 26sources = [ 27 Source( 28 source_id=1, 29 title="Parser v2 Deprecation Policy", 30 content="Parser-v2 SDK migrations must start within 30 days.", 31 ), 32 Source( 33 source_id=2, 34 title="Service Account Rotation Exceptions", 35 content="Service-account OAuth rotation exceptions require security approval.", 36 ), 37] 38 39answer = grounded_answer(sources) 40assert grounded_answer(sources[:1]) == "Insufficient cited evidence to answer." 41assert grounded_answer(sources[:1] + [Source(2, "Service Account Rotation Exceptions", "No approval rule.")]) == "Insufficient cited evidence to answer." 42print(answer)
Output
1Start the parser-v2 SDK migration within 30 days [1]. Service-account OAuth rotation exceptions require security approval [2].

Why is low temperature not enough to make generated search factual?

Answer

Low temperature reduces variance, but factuality comes from retrieved evidence, source IDs, evidence packing, and claim-level verification. A deterministic unsupported answer is still unsupported.

Streaming with pending support

Once evidence is selected, the user still has to wait for synthesis and verification. For one-way progressive delivery, Server-Sent Events (SSE) work through the browser EventSource API over HTTP and are easy to render.[8]Reference 8Using server-sent eventshttps://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events The transport is simple; the policy isn't. Hold factual claims until verification completes, or label streamed text as provisional and update or retract unsupported claims.

SSE event path for the parser-v2 answer. The live stream emits plan, sources, support_state=pending, then provisional text. A parallel NLI check later attaches either support_state=supported or blocked. High-risk claims can stay hidden until the supported event arrives.
The parser-v2 stream can render "within 30 days [1]" as pending. NLI runs in parallel and later marks that claim supported or blocked.

Streaming endpoint

Predict the safe event sequence for the parser-v2 answer: plan and sources can appear early, but a policy claim should remain visibly pending until its cited span passes. This event contract fits a low-risk path that permits provisional rendering. It emits the plan and sources, declares pending support, streams text, then attaches claim-check results. A higher-risk workflow can omit text frames until support becomes supported.

streaming-endpoint.py
1import json 2 3def sse(event_type: str, data: object) -> str: 4 payload = {"type": event_type, "data": data} 5 return f"data: {json.dumps(payload, separators=(',', ':'))}\n\n" 6 7def event_stream() -> list[str]: 8 return [ 9 sse("plan", {"intent": "comparison", "route": "hybrid_search"}), 10 sse("sources", [{"title": "Parser v2 Deprecation Policy", "source_id": 1}]), 11 sse("support_state", {"state": "pending", "display": "provisional"}), 12 sse("text", "For parser-v2 SDK migration"), 13 sse("text", ", start within 30 days [1]."), 14 sse( 15 "support_state", 16 {"state": "supported", "claim_ids": ["migration-window"]}, 17 ), 18 "data: [DONE]\n\n", 19 ] 20 21for frame in event_stream(): 22 print(frame, end="")
Output
1data: {"type":"plan","data":{"intent":"comparison","route":"hybrid_search"}} 2 3data: {"type":"sources","data":[{"title":"Parser v2 Deprecation Policy","source_id":1}]} 4 5data: {"type":"support_state","data":{"state":"pending","display":"provisional"}} 6 7data: {"type":"text","data":"For parser-v2 SDK migration"} 8 9data: {"type":"text","data":", start within 30 days [1]."} 10 11data: {"type":"support_state","data":{"state":"supported","claim_ids":["migration-window"]}} 12 13data: [DONE]

A live CodeAtlas stream also needs periodic heartbeat events and upstream proxies that don't buffer. Log support-state transitions so a provisional parser-v2 sentence that later gets blocked can be audited. The audit trail should show which claim, source span, and verifier decision changed the UI state.


Why use SSE for search-style answer streaming?

Answer

SSE fits one-way progressive token delivery over HTTP when the browser needs plan, source, text, and support-state events from the server. It doesn't decide whether unverified facts may be displayed.

A fluent parser-v2 sentence can still be unsupported

Suppose CodeAtlas writes, "Start the migration within 30 days," while its citation only says that migration planning has begun. The sentence sounds plausible, and its citation exists, but the cited span doesn't support its deadline. RAG reduces this risk; it doesn't remove it.

FActScore makes the remaining error visible by splitting long-form text into atomic facts and scoring the fraction supported by a knowledge source.[9]Reference 9FActScore: Fine-grained Atomic Evaluation of Factual Precision in Long Form Text Generation.https://arxiv.org/abs/2305.14251 On biography generation, Min et al. reported FActScores of 42% for InstructGPT, 58% for ChatGPT, and 71% for search-augmented PerplexityAI. Retrieval helped, yet nearly three in ten atomic facts remained unsupported on that task.

CodeAtlas can reuse that decomposition at request time: extract claims, bind each one to a cited span, then verify entailment before the UI treats the text as final. Predict the failure response now: a weak claim should be blocked, marked provisional, or regenerated, not hidden behind a correct-looking source list.

Diagram showing Atomic claim, Lexical precheck, missing terms, and Block.
Atomic claim, Lexical precheck, missing terms, and Block.

Strong systems stack several checks rather than betting on one verifier. Cheap checks can reject obvious misses; more expensive checks decide whether meaning, numbers, and negation survive.

MethodWhat it checksRelative costMain weakness
Quote / span matchingDoes the cited span appear in the source?Very lowMisses paraphrases and logical contradictions
Natural Language Inference (NLI)Does the cited passage entail the claim?MediumCan struggle on ambiguous or very long passages
LLM-as-judge / fallback passShould unsupported claims be flagged or regenerated?HighRequires measured latency, cost, and calibration

NLI-based verification estimates whether a cited passage entails a claim rather than merely sharing words. TRUE[10]Reference 10TRUE: Re-evaluating Factual Consistency Evaluation.https://arxiv.org/abs/2204.04991 found large-scale NLI methods and question-generation-plus-answering methods to be strong automatic factual-consistency checks on its benchmarks. NLI is therefore a useful measured layer, not proof of correctness.

The function below is deliberately only a lexical precheck. It can cheaply reject a missing phrase; it can't label entailment. Claims that pass still need a calibrated verifier or a policy that keeps them provisional.

lexical-citation-precheck.py
1import json 2import re 3 4def lexical_precheck(claim: str, source_text: str) -> dict[str, str | bool]: 5 claim_terms = set(re.findall(r"[a-z0-9]+", claim.lower())) 6 source_terms = set(re.findall(r"[a-z0-9]+", source_text.lower())) 7 term_match = claim_terms.issubset(source_terms) 8 9 return { 10 "claim": claim, 11 "term_match": term_match, 12 "next_step": "entailment_check" if term_match else "block_claim", 13 } 14 15result = lexical_precheck( 16 claim="Parser-v2 migrations must start within 30 days", 17 source_text="Parser-v2 SDK migrations must start within 30 days.", 18) 19 20print(json.dumps(result, indent=2))
Output
1{ 2 "claim": "Parser-v2 migrations must start within 30 days", 3 "term_match": true, 4 "next_step": "entailment_check" 5}

Why can lexical matching not replace NLI or another calibrated verifier?

Answer

Lexical matching checks whether words appear in a source. Entailment evaluation estimates whether the passage supports the meaning of the claim, including paraphrases or contradictions.

Verification is another billable stage. At search QPS, even a one-cent synthesizer plus a claim checker becomes a budget problem. That's why quality gates and cost gates have to be designed together.

What running this costs

At scale, every extra candidate, token, and claim becomes a cost decision. Exact dollar figures drift with vendor pricing, model choice, and answer length, so reason from units of work rather than one fixed price table. Before looking at the scenario, predict which route should be cheapest: a live structured lookup, a cache hit, or full synthesis with verification.

Worked cost envelope for one parser-v2 synthesis query: retrieval $0.002, rerank $0.001, synthesis $0.0057, verification $0.001, totaling $0.0097. At 1000 queries per second that scenario is $582 per minute, which is why cache hits and structured API routes must stay off the synthesis path.
This parser-v2 synthesis path costs $0.0097. At 1,000 QPS that's $582 per minute. A cache hit or structured API route never enters this bar.
Cost centerBillable unitWhat makes it expensive
Web retrievalAPI calls and fetched documentsMore sub-queries, more URLs fetched
RerankingQuery-document pairsLarger candidate set or heavier reranker
SynthesisPrompt + completion tokensLong context windows and long answers
VerificationClaims checked against passagesDense citation requirements, many claims

Use scenario inputs rather than presenting provider pricing as fixed:

  • Web retrieval: ~$0.002
  • Reranking 50 query-document pairs: ~$0.001
  • 1,500 prompt tokens + 400 output tokens at $0.003 per 1K tokens: ~$0.006
  • Verifying 5 claims: ~$0.001

For these inputs, total cost is roughly $0.01 per query. At 1,000 QPS, that scenario approaches $600 per minute. Routing and caching are therefore not polish; they keep avoidable requests off the synthesis path.

query-cost-envelope.py
1retrieval_usd = 0.002 2rerank_usd = 0.001 3prompt_tokens = 1500 4output_tokens = 400 5token_usd_per_thousand = 0.003 6verification_usd = 0.001 7queries_per_second = 1000 8 9query_usd = ( 10 retrieval_usd 11 + rerank_usd 12 + (prompt_tokens + output_tokens) / 1000 * token_usd_per_thousand 13 + verification_usd 14) 15per_minute_usd = query_usd * queries_per_second * 60 16 17assert round(query_usd, 4) == 0.0097 18print("scenario_query_usd:", round(query_usd, 4)) 19print("scenario_per_minute_usd:", round(per_minute_usd, 2))
Output
1scenario_query_usd: 0.0097 2scenario_per_minute_usd: 582.0
cost/query≈  Csearch+Npairs⋅Crerank+(Tprompt+Toutput)⋅Ctoken+Nclaims⋅Cverify\begin{aligned} \text{cost/query} \approx\;& C_{\text{search}} + N_{\text{pairs}} \cdot C_{\text{rerank}} \\ &+ (T_{\text{prompt}} + T_{\text{output}}) \cdot C_{\text{token}} \\ &+ N_{\text{claims}} \cdot C_{\text{verify}} \end{aligned}cost/query≈​Csearch​+Npairs​⋅Crerank​+(Tprompt​+Toutput​)⋅Ctoken​+Nclaims​⋅Cverify​​

Which stage dominates variable cost depends on provider rates, fan-out, prompt length, and verification policy. Measure the breakdown first. Then route cacheable or structured queries away from unnecessary synthesis and trim top-K only where retrieval evaluation allows it.

In the worked 1,000 QPS cost scenario, why does routing matter?

Answer

The scenario costs hundreds of dollars per minute. Cache hits, structured routes, smaller models, and evaluated top-K reduction can change that envelope before traffic reaches synthesis.

Serving-side latency levers

TTFT can be dominated by retrieval, prompt assembly, or prefill; long answers can shift pressure toward decode. Before choosing an optimization, identify which bar is actually long in a trace. A serving fix aimed at decode can't shorten a slow reranker.

PagedAttention improves KV-cache memory management and can increase concurrency when KV allocation is the binding constraint.[11]Reference 11Efficient Memory Management for Large Language Model Serving with PagedAttention.https://arxiv.org/abs/2309.06180 If decode is the bottleneck, speculative decoding can reduce latency when a draft model is accepted often enough; benchmark it for answer shapes and the target model in this service.[12]Reference 12Fast Inference from Transformers via Speculative Decoding.https://arxiv.org/abs/2211.17192

These serving optimizations matter after retrieval depth and prompt length are under control. If 40 mediocre passages still reach the model, no serving trick can rescue TTFT. First reduce work that should never have entered the context; then optimize the work that remains.

GPU capacity planning

Split concurrency by which latency you mean. Product SLOs here are often TTFT (about 0.8 to 1.5 seconds to first token). End-to-end generation is longer because it includes first token plus the rest of the answer. A dashboard that reports only TTFT can hide contexts that remain resident during decode.

For a 1K QPS design point, the low end of the 1K to 10K QPS per-region range, use the overlapped 800 ms TTFT as about 1.0 s in round numbers and budget remaining decode separately. A 120-token answer at roughly 80 tokens/s after the first token needs about 1.5 s more, so end-to-end is about 2.5 s:

LTTFT≈1.0 sstreaming sessions at first token≈1000×1.0=1000\begin{aligned} L_{\text{TTFT}} &\approx 1.0\,\text{s} \\ \text{streaming sessions at first token} &\approx 1000 \times 1.0 = 1000 \end{aligned}LTTFT​streaming sessions at first token​≈1.0s≈1000×1.0=1000​ Le2e≈2.5 sfull in-flight answers and KV held≈1000×2.5=2500\begin{aligned} L_{\text{e2e}} &\approx 2.5\,\text{s} \\ \text{full in-flight answers and KV held} &\approx 1000 \times 2.5 = 2500 \end{aligned}Le2e​full in-flight answers and KV held​≈2.5s≈1000×2.5=2500​

Little's Law still holds (L=λWL = \lambda WL=λW). Use W=LTTFTW = L_{\text{TTFT}}W=LTTFT​ for admission of streaming sessions and W=Le2eW = L_{\text{e2e}}W=Le2e​ for decode concurrency and KV residency. The 2500 figure is not first-token-only concurrency: it's the number of answers still in flight across their full 2.5-second lifetime.

capacity-envelope.py
1from math import ceil 2 3queries_per_second = 1000 4ttft_seconds = 1.0 5average_e2e_latency_seconds = 2.5 6average_output_tokens = 120 7effective_decode_tokens_per_second_per_gpu = 6000 8kv_gib_per_2k_tokens = 0.61 # half of 1.22 GiB @ 4K from the multi-tenant lesson 9usable_kv_fraction = 0.6 10gpu_hbm_gib = 80 11 12in_flight_ttft = queries_per_second * ttft_seconds 13in_flight_e2e = queries_per_second * average_e2e_latency_seconds 14decode_gpus = ceil( 15 queries_per_second 16 * average_output_tokens 17 / effective_decode_tokens_per_second_per_gpu 18) 19raw_kv_gib = in_flight_e2e * kv_gib_per_2k_tokens 20kv_gpus = ceil(raw_kv_gib / (usable_kv_fraction * gpu_hbm_gib)) 21 22assert in_flight_ttft == 1000 23assert in_flight_e2e == 2500 24assert decode_gpus == 20 25assert kv_gpus == 32 26print("in_flight_ttft_sessions:", int(in_flight_ttft)) 27print("in_flight_e2e_requests:", int(in_flight_e2e)) 28print("decode_gpus_before_headroom:", decode_gpus) 29print("kv_gpus_before_headroom:", kv_gpus)
Output
1in_flight_ttft_sessions: 1000 2in_flight_e2e_requests: 2500 3decode_gpus_before_headroom: 20 4kv_gpus_before_headroom: 32

Decode throughput alone is a lower bound. Twenty GPUs at 6k tok/s can emit the tokens, but 2500 concurrent contexts may first exhaust high-bandwidth memory (HBM) reserved for KV state. That's a different constraint from tokens per second.

Reuse the multi-tenant lesson's roughly 1.22 GiB per 4K-token request: 2K tokens is about 0.61 GiB each. If packing leaves 60% of each 80 GiB GPU usable for KV, then 2500×0.61/(0.6×80)≈322500 \times 0.61 / (0.6 \times 80) \approx 322500×0.61/(0.6×80)≈32 GPUs before weight residency and headroom. Formula sketch:

BKV=concurrent×average tokens×KV bytes/tokenNGPU, KV≈⌈BKVusable KV HBM/GPU⌉\begin{aligned} B_{\text{KV}} &= \text{concurrent} \times \text{average tokens} \\ &\quad \times \text{KV bytes/token} \\ N_{\text{GPU, KV}} &\approx \left\lceil \frac{B_{\text{KV}}}{\text{usable KV HBM/GPU}} \right\rceil \end{aligned}BKV​NGPU, KV​​=concurrent×average tokens×KV bytes/token≈⌈usable KV HBM/GPUBKV​​⌉​

PagedAttention improves packing; it doesn't remove byte accounting. Don't assume a universal "requests per GPU" number. Measure your model, context length, batching, and quantization. The decode line and KV line can disagree, as they do here: 20 GPUs can emit the tokens while 32 GPUs are needed to hold 2500 contexts.

Rdecode=QPS×average output tokensNGPU, decode≈⌈Rdecodeeffective decode tok/s/GPU⌉\begin{aligned} R_{\text{decode}} &= \text{QPS} \times \text{average output tokens} \\ N_{\text{GPU, decode}} &\approx \left\lceil \frac{R_{\text{decode}}}{\text{effective decode tok/s/GPU}} \right\rceil \end{aligned}Rdecode​NGPU, decode​​=QPS×average output tokens≈⌈effective decode tok/s/GPURdecode​​⌉​

For long prompts, budget prefill separately too:

Prefill capacity≈QPS×avg prompt tokenseffective prefill tok/s/GPU\text{Prefill capacity} \approx \frac{\text{QPS} \times \text{avg prompt tokens}}{\text{effective prefill tok/s/GPU}}Prefill capacity≈effective prefill tok/s/GPUQPS×avg prompt tokens​

At the same 1K QPS design point, split pools explicitly (illustrative shares; measure yours):

PoolRoleSketch share of workSizing note
Planner / routerQuery plan, rewrite~all queries, CPU-msStateless CPU replicas
Sparse retrievalBM25 / lexical~all authorized queriesShard by corpus
Dense ANNVector search~all hybrid queriesIndex RAM + QPS
Rerank GPUCross-encoderTop-KKK pairs onlyBatch GPU workers
Synthesis GPUPrefill + decodeAnswer-generating routesDecode + KV lines above

Peak 10K QPS is an order of magnitude above this worked example. Shard regions, raise cache hit rates, or replicate pools only after remeasuring stage hit rates. The 1K QPS GPU count doesn't cover the top of the requirements range.

Size the system against whichever pool saturates first: retriever GPUs, reranker GPUs, prefill workers, or decode workers. The answer model may be the expensive tier, but measured retrieval or reranking can still be the first bottleneck. When a request misses its TTFT SLO, locate that pool before adding model capacity.

Why can reranking or retrieval be the bottleneck even when the LLM is expensive?

Answer

Large fan-out multiplies query-document pairs, web/API calls, and cleanup work before synthesis starts. If retrieval depth or reranker batches saturate first, faster decoding won't fix TTFT.


How to know it's working

A fluent answer can cite two passages and still omit the service-account exception. Which metric should fail first? RAGAS[13]Reference 13RAGAS: Automated Evaluation of Retrieval Augmented Generation.https://arxiv.org/abs/2309.15217 introduced reference-free evaluation around faithfulness, answer relevance, and context relevance. That's one offline layer when gold answers are scarce, not a replacement for labeled retrieval metrics, citation checks, or online product metrics. A response can be consistent with mediocre evidence and still fail the user.

LayerMetricWhat it catchesTypical requirement
Reference-free offlineFaithfulnessUnsupported claimsLLM/NLI judge over answer vs context
Reference-free offlineAnswer relevanceMissed user intentGenerated-question similarity or judge
Reference-free offlineContext relevanceNoisy, redundant retrieved contextJudge over question vs retrieved passages
Labeled offlineRecall@k / nDCG / MRRRetriever missed or buried the right sourceGold passages or reliable click labels
Citation qualityCitation precision / unsupported-claim rateWrong citations or uncited claimsClaim extraction + source verification
OnlineReformulation rate / citation CTR / abandonmentReal user dissatisfactionProduction logging and experiments

This example wires a scorecard together with simple token-overlap proxies so it stays runnable. Read the output as a diagnosis prompt, not a quality badge: citation resolution only checks whether a visible citation ID maps to an available passage; it doesn't prove claim support. These proxies aren't RAGAS or calibrated faithfulness judges. Replace them in an evaluation service while keeping labeled retrieval metrics separate.

how-to-know-its-working.py
1import json 2import re 3from dataclasses import dataclass 4from collections.abc import Mapping 5from math import log2 6 7@dataclass(frozen=True) 8class Passage: 9 id: str 10 text: str 11 12def tokens(text: str) -> set[str]: 13 return set(text.lower().replace(",", "").replace(".", "").split()) 14 15def overlap(a: str, b: str) -> float: 16 left = tokens(a) 17 right = tokens(b) 18 return len(left & right) / max(1, len(left)) 19 20def citation_resolution_precision(answer: str, cited_passages: list[Passage]) -> float: 21 available_ids = {passage.id for passage in cited_passages} 22 cited_ids = re.findall(r"\[([^\[\]]+)\]", answer) 23 return sum(cited_id in available_ids for cited_id in cited_ids) / max(1, len(cited_ids)) 24 25def recall_at_k(retrieved_ids: list[str], gold_ids: set[str], k: int) -> float: 26 return len(set(retrieved_ids[:k]) & gold_ids) / max(1, len(gold_ids)) 27 28def ndcg_at_k(retrieved_ids: list[str], gold_ids: set[str], k: int) -> float: 29 gains = [1.0 if item in gold_ids else 0.0 for item in retrieved_ids[:k]] 30 dcg = sum(gain / log2(rank + 2) for rank, gain in enumerate(gains)) 31 ideal = sum(1.0 / log2(rank + 2) for rank in range(min(len(gold_ids), k))) 32 return dcg / ideal if ideal else 0.0 33 34def score_search_response( 35 query: str, 36 answer: str, 37 retrieved_passages: list[Passage], 38 cited_passages: list[Passage], 39 gold_passage_ids: set[str] | None = None, 40) -> Mapping[str, float]: 41 context = " ".join(passage.text for passage in retrieved_passages) 42 scorecard = { 43 "answer_context_token_overlap": overlap(answer, context), 44 "query_answer_token_overlap": overlap(query, answer), 45 "query_context_token_overlap": overlap(query, context), 46 "citation_resolution_precision": citation_resolution_precision(answer, cited_passages), 47 } 48 49 if gold_passage_ids is not None: 50 retrieved_ids = [p.id for p in retrieved_passages] 51 scorecard["recall_at_20"] = recall_at_k(retrieved_ids, gold_passage_ids, k=20) 52 scorecard["ndcg_at_10"] = ndcg_at_k(retrieved_ids, gold_passage_ids, k=10) 53 54 return {metric: round(value, 2) for metric, value in scorecard.items()} 55 56retrieved = [ 57 Passage("1", "Parser-v2 SDK migrations must start within 30 days."), 58 Passage("2", "Service-account OAuth rotation exceptions require security approval."), 59 Passage("3", "Legacy parser-v1 migrations follow a separate workflow."), 60] 61answer = "Parser-v2 SDK migrations must start within 30 days [1]." 62 63print(json.dumps( 64 score_search_response( 65 query="Which deprecation policy applies to parser-v2 SDK migration for service accounts?", 66 answer=answer, 67 retrieved_passages=retrieved, 68 cited_passages=retrieved[:1], 69 gold_passage_ids={"1", "2"}, 70 ), 71 indent=2, 72))
Output
1{ 2 "answer_context_token_overlap": 0.89, 3 "query_answer_token_overlap": 0.18, 4 "query_context_token_overlap": 0.18, 5 "citation_resolution_precision": 1.0, 6 "recall_at_20": 1.0, 7 "ndcg_at_10": 1.0 8}

Why keep reference-free RAGAS-style metrics separate from labeled retrieval metrics?

Answer

They answer different questions. Reference-free metrics judge answer/context consistency; labeled metrics reveal whether the retriever found and ranked known relevant passages.

Use the scorecard to locate the failing handoff. Low Recall@k points upstream to crawling, chunking, query planning, or candidate retrieval. Good recall with poor context relevance points to selection or packing. Good context with unsupported claims points to synthesis or citation verification. This turns an evaluation result into an owner and a next experiment.

Release only when the relevant gates agree: authorized retrieval is recalled, citations resolve to source spans, claims pass support checks, and TTFT and cost stay inside their budgets. A green answer-quality score can't waive an authorization failure, and a fast stream can't waive a missing source.


Scaling without blowing the budget

As traffic grows, evaluate each latency or cost lever against quality and authorization tests:

StrategyImplementation DetailsPrimary Benefit
Semantic response cacheKey answers by authorization scope and evidence version; reuse only after similarity and freshness thresholds pass.Can avoid synthesis work for eligible repeat queries.
Model routingRoute exact live facts to structured backends and measured simple paths to smaller models.Can lower cost without weakening answer routes that require synthesis.
Speculative retrievalStart an allowed cheap retriever while rewrite or decomposition runs, then merge or cancel.Can shorten measured critical path when early retrieval is useful.
Asynchronous verificationMark streamed claims pending, or hold high-risk facts until checks return.Trades display latency against unsupported-claim exposure explicitly.
Geographic distributionPlace permitted retrieval and caches near users when data residency policy allows it.Can reduce network latency for eligible data paths.
Feedback integrationLog interactions with privacy controls and use labeled review before training rerankers.Supplies evidence for retriever or reranker changes.

Why is semantic response caching risky without freshness and permission checks?

Answer

Similar queries can have different tenant visibility, quota state, policy version, or time sensitivity. Cache reuse must satisfy similarity, freshness, and authorization thresholds.

Failure patterns worth replaying

Replay the same parser-v2 query with one fault at a time: remove the fresh revision, admit private-roadmap, drop the exception during packing, or flip a citation's negation. Observe which metric and trace span change. This isolates the broken handoff instead of blaming "the LLM" for every bad answer.

SymptomCauseFix
TTFT stays above 2 secondsA frontier model runs on every query, including live lookupsRoute factual queries to a small model, structured API, or cache
Answers cite the wrong sourcePacking stuffs too many mediocre passages into the promptCut top-K after rerank and put the strongest chunks at both edges of context
Hallucinations persist despite RAGPrompt instructions are the only grounding checkStack span matching, NLI, and selective LLM-as-judge checks
Cost per query spikesEvery query hits the hot web tierRoute only freshness-marked queries there and cache stable answers
Retrieval misses API versions or build IDsDense search runs without a keyword laneHybrid sparse + dense with BM25 for exact matches
Decode GPUs look fine, HBM is fullCapacity used TTFT concurrency for KV residencySize KV from end-to-end in-flight answers, not first-token sessions
Complete the lesson

Mastery Check

Answer every question, then check your score. Score 75% or higher to mark this lesson complete.

1.A user asks, 'Is job 884 still running, and what does the current GPU quota policy allow?' Which search plan keeps source-of-truth boundaries intact?

Correct answer: Call the live jobs and quota API for exact current facts, retrieve the published policy with lexical search, and fuse only where comparison needs it.

Exact mutable facts belong to structured live APIs. Published policy text is a lexical retrieval problem, while a comparison can use hybrid retrieval after each source is authorized and represented with its evidence contract.

2.A hybrid search returns 1,000 candidates from sparse BM25 and dense retrieval across multiple workspaces. At what point should tenant and document authorization occur?

Correct answer: Before fusion and reranking, so unauthorized candidates cannot influence reciprocal ranks, scores, or the final evidence pack.

Authorization is part of retrieval correctness, not a final display filter. Filtering before RRF and reranking prevents a private candidate from changing ranking positions or leaking through the evidence and synthesis path.

3.Sparse BM25 scores and dense cosine scores use different scales for the same query. Which fusion method avoids pretending their raw values are directly comparable?

Correct answer: Use reciprocal rank fusion over authorized ranked lists, then rerank the merged candidates with a calibrated cross-encoder.

RRF combines rank positions rather than incompatible score magnitudes. After authorization and fusion, a reranker can inspect the top candidates more deeply, which is a separate decision from raw sparse or dense score arithmetic.

4.An evidence pack has selected eight allowed chunks, but a compressor wants to shorten them before synthesis. Which sequence preserves citation integrity?

Correct answer: Select authorized chunks first, retain IDs, numbers, negations, and source offsets, compress afterward, and verify claims against originals.

Selection establishes the allowed evidence set. Compression can reduce context cost but may alter wording, so source-offset maps and original-span verification are needed to ensure a concise evidence pack still supports each released claim.

5.A generated answer repeats a number found in a retrieved chunk, but the number's negation is reversed. A lexical overlap check passes. What grounding control should block release?

Correct answer: Run claim-level entailment or an equivalent verifier against the original span, including negation and numeric preservation, then abstain on failure.

Lexical overlap detects shared words, not whether a claim preserves meaning. Grounding needs claim-level checks that protect numbers, identifiers, negation, and source spans. A failed check should block or label the answer rather than hide the contradiction in prose.

6.A search UI is ready to stream a high-risk policy claim before claim-level verification finishes. Which event contract is safest?

Correct answer: Emit plan, sources, and a pending support state, but hold the high-risk claim until verification marks it supported.

Plan and source events can show progress without publishing an unsupported fact. For a high-risk claim, the text frame waits until claim-to-source verification succeeds; a failed check blocks or regenerates the claim instead of silently retracting a final answer.

7.For one query, planner time is 40 ms, retrieval takes 260 ms in parallel, reranking 90 ms, packing 20 ms, and model prefill 430 ms. What first-token budget does this schedule imply?

Correct answer: 800 ms, because the planner and retrieval contribute max(40, 260)=260 ms, followed by 90 + 20 + 430 ms.

The overlapped front half is max(40, 260) = 260 ms. Adding reranking, packing, and prefill gives 260 + 90 + 20 + 430 = 800 ms. Treating parallel stages as serial would overstate latency, while omitting prefill would understate time to first token.

8.A semantic-cache hit for a query would return a prior answer citing a document version that has since changed. Which cache key and release check prevent stale or cross-scope evidence?

Correct answer: Bind the key to authorization scope, model and prompt contract, evidence version or freshness tier, and policy version before reuse.

A semantically similar question can have a different authorized evidence set or current answer. Cache reuse must include trust scope and the versions or freshness guarantees that made the original evidence valid, then still pass the release checks.

9.A search service handles 1,000 QPS with 1-second TTFT and 2.5-second end-to-end latency. Capacity planning estimates 20 GPUs for decode and 32 GPUs for KV residency. Which interpretation is correct?

Correct answer: Budget about 1,000 first-token sessions and 2,500 end-to-end sessions; the 32-GPU KV line binds before the 20-GPU decode lower bound.

Little's Law gives about 1,000 sessions in the one-second TTFT window and 2,500 active contexts across 2.5 seconds end to end. Decode throughput needs 20 GPUs, but KV residency needs 32 before weights and headroom, so memory is the binding estimate in this scenario.

9 questions remaining.

Next Step
Continue to Vision-Language Models & CLIP

You can now route, retrieve, pack, cite, and verify text evidence under a search latency budget. The next gap is visual evidence: screenshots, diagrams, and photos that never made it into the BM25 index. CLIP is how those images become searchable alongside the docs you just ranked.

PreviousMulti-Tenant LLM Platform
Share this article
XFacebookLinkedInBlueskyRedditHacker NewsEmail
References

Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks.

Lewis, P., et al. · 2020 · NeurIPS 2020

https://arxiv.org/abs/2005.11401

The Probabilistic Relevance Framework: BM25 and Beyond.

Robertson, S., & Zaragoza, H. · 2009 · Foundations and Trends in Information Retrieval

https://doi.org/10.1561/1500000019

Precise Zero-Shot Dense Retrieval without Relevance Labels.

Gao, L., Ma, X., Lin, J., & Callan, J. · 2022 · arXiv preprint

https://arxiv.org/abs/2212.10496

Reciprocal Rank Fusion Outperforms Condorcet and Individual Rank Learning Methods.

Cormack, G. V., Clarke, C. L. A., & Buettcher, S. · 2009 · SIGIR '09

https://dl.acm.org/doi/10.1145/1571941.1572114

ColBERT: Efficient and Effective Passage Search via Contextualized Late Interaction over BERT.

Khattab, O., & Zaharia, M. · 2020 · SIGIR 2020

https://arxiv.org/abs/2004.12832

Lost in the Middle: How Language Models Use Long Contexts

Liu, N.F., et al. · 2024 · TACL 2024

https://aclanthology.org/2024.tacl-1.9/

LLMLingua: Compressing Prompts for Accelerated Inference of Large Language Models.

Jiang, H., et al. · 2023 · EMNLP

https://arxiv.org/abs/2310.05736

Using server-sent events

MDN Web Docs · 2026

https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events

FActScore: Fine-grained Atomic Evaluation of Factual Precision in Long Form Text Generation.

Min, S., et al. · 2023 · EMNLP 2023

https://arxiv.org/abs/2305.14251

TRUE: Re-evaluating Factual Consistency Evaluation.

Honovich, O., et al. · 2022 · NAACL 2022

https://arxiv.org/abs/2204.04991

Efficient Memory Management for Large Language Model Serving with PagedAttention.

Kwon, W., et al. · 2023 · SOSP 2023

https://arxiv.org/abs/2309.06180

Fast Inference from Transformers via Speculative Decoding.

Leviathan, Y., Kalman, M., & Matias, Y. · 2023 · ICML 2023

https://arxiv.org/abs/2211.17192

RAGAS: Automated Evaluation of Retrieval Augmented Generation.

Es, S., et al. · 2023 · arXiv preprint

https://arxiv.org/abs/2309.15217

Discussion

Questions and insights from fellow learners.

Discussion loads when you reach this section.