system-design49 min read

Design a Perplexity-Style AI Search Engine: The Complete Guide — A Senior+ Guide | Ayodhyya

Design a Perplexity-Style AI Search Engine: The Complete Guide

A Senior+ Guide

Ayodhyya July 10, 2026 45 min read

1. Introduction

The search engine landscape has undergone a seismic transformation over the past several years. Traditional search engines like Google, Bing, and Yahoo have long served as the primary gateway to the internet, returning lists of blue links that users must manually scan, click, and synthesize. This model, while revolutionary in the late 1990s, has become increasingly inadequate for users who want direct, comprehensive answers rather than a scavenger hunt through dozens of web pages.

Perplexity AI emerged as a disruptor in this space by fundamentally reimagining what a search engine can be. Instead of returning ranked lists of links, Perplexity reads and synthesizes information from multiple sources, then presents a coherent, well-cited answer in natural language. Users receive the answer they seek along with precise inline citations that link directly to the source material. This approach has attracted millions of daily users and billions of dollars in valuation, proving that there is enormous demand for AI-native search experiences.

In this comprehensive guide, we will design a Perplexity-style AI search engine from the ground up. We will cover every critical subsystem — from web crawling and indexing, through Retrieval-Augmented Generation (RAG) pipelines, to citation verification and answer quality evaluation. We will examine the distributed systems challenges involved in serving millions of concurrent users with sub-second latency while maintaining answer accuracy and source integrity.

This guide is written for senior engineers and architects who need to understand not just the high-level concepts but also the implementation details: data models, API contracts, capacity planning, cost estimation, and operational considerations. We will include C# code examples throughout, along with Mermaid architecture diagrams, comparison tables, and interview preparation questions.

Who this guide is for: Backend engineers, ML engineers, platform architects, and technical leaders who are building or evaluating AI-powered search and question-answering systems at scale.

2. The AI Search Revolution

The transition from traditional keyword-based search to AI-powered answer engines represents one of the most significant shifts in information retrieval since the invention of the web search engine itself. Understanding this revolution requires examining several converging technological trends that have collectively made AI search both possible and practical.

The Limitations of Traditional Search

Traditional search engines operate on a fundamentally simple model: match user keywords against an inverted index of web pages, rank results using hundreds of signals (PageRank, freshness, click-through rate, dwell time), and present a ranked list. While this approach has served billions of users, it suffers from several structural limitations.

  • Information synthesis burden: Users must manually read and combine information from multiple sources to answer complex questions.
  • Keyword mismatch: Users often struggle to formulate the right keywords, and the search engine cannot understand the underlying intent.
  • No follow-up capability: Traditional search treats every query as independent, ignoring conversational context.
  • Ad-driven incentives: The advertising business model incentivizes showing more links rather than providing definitive answers.
  • Spam and SEO manipulation: Rank-based systems are vulnerable to search engine optimization techniques that degrade result quality.

The Foundation: Large Language Models

The breakthrough enabling AI search is the combination of several LLM capabilities that matured rapidly between 2022 and 2025. Modern large language models can comprehend complex questions, synthesize information from retrieved passages, generate fluent natural language responses, and produce inline citations that reference specific source documents. These models have been trained on vast corpora of text data and fine-tuned for instruction following, making them capable of producing answers that are both accurate and readable.

The key insight that made AI search practical was Retrieval-Augmented Generation (RAG), a technique that grounds LLM outputs in real-time retrieved documents rather than relying solely on the model's parametric memory. RAG dramatically reduces hallucination and enables the system to provide up-to-date information with verifiable source citations.

The Competitive Landscape

ProductApproachKey DifferentiatorYear Launched
Perplexity AIRAG + LLMCitations, conversational follow-up, Pro Search2022
Google SGE / AI OverviewPaLM + SearchIntegration with Google Search index2023
Bing Chat / CopilotGPT-4 + BingMicrosoft/OpenAI partnership2023
You.comCustom LLM + SearchDeveloper-focused API2023
KagiAI + traditionalSubscription-based, no ads2023
PhindCode-focused LLMDeveloper-oriented AI search2023

3. System Requirements & Scope

Before diving into architecture, we must clearly define the functional and non-functional requirements. An AI search engine has a unique set of constraints that differ significantly from traditional web search or standalone LLM applications.

Functional Requirements

  1. Query processing: Accept natural language queries and return comprehensive, synthesized answers with inline citations.
  2. Real-time web search: Search the web in real-time to retrieve current and relevant source documents for every query.
  3. Citation generation: Provide numbered inline citations that link to the specific source documents used to generate each claim.
  4. Conversational follow-up: Support multi-turn conversations where users can ask clarifying questions and refine their search.
  5. Source transparency: Display the full list of sources used, including titles, URLs, snippets, and publication dates.
  6. Thread management: Allow users to create, share, and revisit search threads.
  7. Focus modes: Support different search scopes — All, Academic, YouTube, Reddit, and other specialized sources.
  8. Image and file analysis: Allow users to upload images and documents for analysis as part of their queries.
  9. Pro Search: Offer a deeper, multi-step reasoning mode for complex queries that requires more research and computation.

Non-Functional Requirements

RequirementTargetRationale
Answer latency (P50)< 2 secondsUsers expect near-instant answers
Answer latency (P99)< 8 secondsComplex queries may require deeper research
Citation accuracy> 95%Citations must be verifiable and relevant
Hallucination rate< 2%Grounded answers must not fabricate facts
Availability99.9%Search is a critical daily tool for many users
Concurrent users10M+ daily activeScale target for a competitive product
Query throughput50K QPS peakPeak traffic during major news events
Index freshness< 5 minutes for breaking newsRecency is a core value proposition
Data retention90 days threads, indefinite indexedBalance storage cost with user experience

Design Constraints

The system must operate within several critical constraints. LLM inference is the primary cost driver, so we must implement aggressive caching, batching, and model selection strategies. The citation pipeline must be reliable because incorrect citations destroy user trust faster than any other failure mode. The system must handle adversarial queries that attempt to produce harmful outputs while maintaining utility for legitimate use cases. And the architecture must support both free-tier users with basic search and paid users who expect deeper, more comprehensive analysis.

4. Capacity Estimation

Accurate capacity estimation is critical for an AI search engine because the cost structure is fundamentally different from traditional web applications. Each query triggers not just database lookups but also embedding generation, vector search, LLM inference, and potentially web crawling — operations that are orders of magnitude more expensive than serving static content.

Query Volume Projections

MetricValueCalculation
Daily Active Users10,000,000Growth target for Year 2
Queries per user per day5Average across free and paid
Total daily queries50,000,00010M × 5
Average QPS~58050M / 86,400 seconds
Peak QPS (3× average)~1,750US morning peak concentration
Burst QPS (10× average)~5,800Breaking news events

Storage Estimation

Storage TypeSize per QueryDaily TotalAnnual Total
Raw query logs2 KB100 GB36 TB
Retrieved documents50 KB2.5 TB912 TB
Embedding vectors4 KB200 GB73 TB
Generated answers8 KB400 GB146 TB
Thread history10 KB500 GB182 TB
User profiles1 KB10 GB3.6 TB

Compute and Network

LLM inference is the dominant compute cost. Assuming an average answer requires processing approximately 4,000 input tokens and generating 800 output tokens, we need to estimate GPU requirements. A single NVIDIA A100 GPU can serve roughly 15-20 requests per second for a 7B parameter model using optimized inference. For 5,800 burst QPS, we need approximately 300-400 GPUs just for the generation step, plus additional GPUs for embedding computation and reranking.

Network bandwidth for web crawling must also be factored in. Crawling millions of pages per hour at an average of 500 KB per page requires approximately 2 Gbps of sustained bandwidth just for the crawl infrastructure, with additional bandwidth for the index update pipeline.

Cost sensitivity: At $2 per million input tokens and $6 per million output tokens for GPT-4-class models, a single query costs approximately $0.006 in LLM API fees alone. At 50M queries per day, that is $300,000 per day or roughly $9 million per month in LLM costs. Self-hosted models can reduce this by 3-5× but require significant upfront GPU investment.

5. Data Model

The data model for an AI search engine is substantially more complex than a traditional search engine because we must track not just documents and indices but also conversation threads, generated answers, citations, user preferences, and feedback signals. The following data model captures the essential entities and their relationships.

Core Entities

public class IndexedDocument
{
    public string DocumentId { get; set; }           // SHA-256 of canonical URL
    public string Url { get; set; }
    public string Domain { get; set; }
    public string Title { get; set; }
    public string Content { get; set; }              // Full extracted text
    public string Summary { get; set; }              // LLM-generated summary
    public List<float> Embedding { get; set; }       // 1536-dim vector
    public List<string> Chunks { get; set; }         // Split into chunks
    public List<List<float>> ChunkEmbeddings { get; set; }
    public DateTime CrawledAt { get; set; }
    public DateTime ContentUpdatedAt { get; set; }
    public float DomainAuthority { get; set; }
    public string ContentType { get; set; }          // article, forum, docs, etc.
    public DocumentMetadata Metadata { get; set; }
    public bool IsIndexed { get; set; }
    public long Version { get; set; }
}

public class DocumentMetadata
{
    public string Author { get; set; }
    public DateTime? PublishedDate { get; set; }
    public string Language { get; set; }
    public List<string> Topics { get; set; }
    public int WordCount { get; set; }
    public float ReadabilityScore { get; set; }
    public List<string> OutboundLinks { get; set; }
    public int InboundLinkCount { get; set; }
}

Conversation and Answer Model

public class SearchThread
{
    public string ThreadId { get; set; }             // UUID
    public string UserId { get; set; }
    public string Title { get; set; }                // Auto-generated
    public List<SearchTurn> Turns { get; set; }
    public string FocusMode { get; set; }            // all, academic, youtube, etc.
    public string ProSearchLevel { get; set; }       // none, standard, deep
    public DateTime CreatedAt { get; set; }
    public DateTime UpdatedAt { get; set; }
    public bool IsPublic { get; set; }
    public int ViewCount { get; set; }
}

public class SearchTurn
{
    public string TurnId { get; set; }
    public string Query { get; set; }
    public string RewrittenQuery { get; set; }       // After intent classification
    public List<RetrievedSource> Sources { get; set; }
    public GeneratedAnswer Answer { get; set; }
    public List<Citation> Citations { get; set; }
    public List<FollowUpSuggestion> FollowUps { get; set; }
    public QueryClassification Classification { get; set; }
    public UserFeedback Feedback { get; set; }
    public TimeSpan LatencyMs { get; set; }
    public DateTime TimestampedAt { get; set; }
}

public class GeneratedAnswer
{
    public string AnswerId { get; set; }
    public string Content { get; set; }              // Markdown formatted
    public string ModelUsed { get; set; }            // e.g., "sonar-large"
    public int InputTokens { get; set; }
    public int OutputTokens { get; set; }
    public List<Citation> Citations { get; set; }
    public AnswerQualityScores Quality { get; set; }
    public bool IsStreamed { get; set; }
    public TimeSpan GenerationTimeMs { get; set; }
}

public class Citation
{
    public int CitationNumber { get; set; }          // [1], [2], etc.
    public string SourceDocumentId { get; set; }
    public string Url { get; set; }
    public string Title { get; set; }
    public string Snippet { get; set; }              // Excerpt used
    public string Domain { get; set; }
    public DateTime? PublishedDate { get; set; }
    public float RelevanceScore { get; set; }
    public bool IsVerified { get; set; }
    public DateTime? VerifiedAt { get; set; }
}

Schema Relationships

erDiagram USER ||--o{ SEARCH_THREAD : creates SEARCH_THREAD ||--|{ SEARCH_TURN : contains SEARCH_TURN ||--o{ RETRIEVED_SOURCE : references SEARCH_TURN ||--|| GENERATED_ANSWER : produces SEARCH_TURN ||--o{ CITATION : includes SEARCH_TURN ||--o{ FOLLOW_UP_SUGGESTION : suggests RETRIEVED_SOURCE }o--|| INDEXED_DOCUMENT : links INDEXED_DOCUMENT ||--o{ DOCUMENT_CHUNK : splits_into USER ||--o{ USER_PREFERENCE : has SEARCH_TURN ||--o{ USER_FEEDBACK : receives INDEXED_DOCUMENT ||--o{ DOMAIN_AUTHORITY : scored_by

6. High-Level Architecture

The architecture of a Perplexity-style AI search engine consists of several interconnected subsystems that work together to process a user query end-to-end. The system can be decomposed into four major layers: the ingestion layer (crawling, parsing, and indexing), the retrieval layer (query understanding, vector search, and reranking), the generation layer (RAG pipeline and answer synthesis), and the serving layer (API gateway, streaming, and caching).

graph TB subgraph Client Layer WebApp[Web Application] MobileApp[Mobile App] API[External API] end subgraph Gateway Layer LB[Load Balancer] Auth[Auth Service] RateLimit[Rate Limiter] QueryRouter[Query Router] end subgraph Retrieval Layer QueryUnderstanding[Query Understanding
Intent Classification] QueryRewrite[Query Rewriting
& Expansion] VectorSearch[Vector Search
pgvector / Qdrant] BM25Search[BM25 Search
Elasticsearch] HybridFusion[Hybrid Fusion
Reciprocal Rank] Reranker[Neural Reranker
Cross-Encoder] end subgraph Generation Layer ContextBuilder[Context Builder] CitationMapper[Citation Mapper] LLMInference[LLM Inference
Sonar / GPT-4] StreamProcessor[Stream Processor
SSE / WebSocket] QualityChecker[Quality Checker
Hallucination Detection] end subgraph Ingestion Layer Crawler[Web Crawler
Distributed Workers] Parser[HTML Parser
Content Extractor] Chunker[Document Chunker] Embedder[Embedding Service
text-embedding-3-large] IndexWriter[Index Writer] end subgraph Data Layer VectorDB[(Vector DB
pgvector)] DocStore[(Document Store
PostgreSQL)] Cache[(Cache Layer
Redis Cluster)] SearchIndex[(Search Index
Elasticsearch)] KG[(Knowledge Graph
Neo4j)] ObjectStore[(Object Store
S3 / R2)] end WebApp --> LB MobileApp --> LB API --> LB LB --> Auth --> RateLimit --> QueryRouter QueryRouter --> QueryUnderstanding QueryUnderstanding --> QueryRewrite QueryRewrite --> VectorSearch QueryRewrite --> BM25Search VectorSearch --> HybridFusion BM25Search --> HybridFusion HybridFusion --> Reranker Reranker --> ContextBuilder ContextBuilder --> CitationMapper CitationMapper --> LLMInference LLMInference --> StreamProcessor StreamProcessor --> QualityChecker Crawler --> Parser --> Chunker --> Embedder --> IndexWriter IndexWriter --> VectorDB IndexWriter --> DocStore IndexWriter --> SearchIndex QueryRouter --> Cache ContextBuilder --> KG

Request Flow Walkthrough

When a user submits a query like "What are the latest developments in quantum computing?", the following sequence unfolds. First, the query passes through the API gateway where authentication, rate limiting, and routing occur. The Query Understanding module classifies the intent as informational, determines the required depth (quick answer vs. deep research), and rewrites the query to optimize retrieval. The system then executes a hybrid search combining vector similarity search with BM25 keyword matching across the indexed document corpus.

The top 20-30 candidate documents are passed through a neural reranker that scores relevance using a cross-encoder model. The top 8-12 results are selected, chunked into relevant passages, and assembled into a context window along with the user's original query and any conversational history. The LLM generates a comprehensive answer with inline citations, streaming tokens to the client in real-time. A post-generation quality check verifies citation accuracy and screens for hallucinated content before the complete response is delivered.

public class SearchOrchestrator
{
    private readonly IQueryUnderstanding _queryUnderstanding;
    private readonly IHybridRetriever _retriever;
    private readonly INeuralReranker _reranker;
    private readonly IContextBuilder _contextBuilder;
    private readonly ICitationMapper _citationMapper;
    private readonly ILLMInference _llmInference;
    private readonly IQualityChecker _qualityChecker;
    private readonly ICacheService _cache;

    public async IAsyncEnumerable<AnswerChunk> SearchAsync(
        SearchRequest request,
        [EnumeratorCancellation] CancellationToken ct)
    {
        var cached = await _cache.GetAsync<SearchResult>(request.Query);
        if (cached != null)
        {
            foreach (var chunk in cached.Chunks)
                yield return chunk;
            yield break;
        }

        var classification = await _queryUnderstanding.ClassifyAsync(request.Query);
        var rewrittenQuery = await _queryUnderstanding.RewriteAsync(
            request.Query, classification, request.ThreadHistory);

        var retrievalTask = Task.WhenAll(
            _retriever.VectorSearchAsync(rewrittenQuery, k: 30),
            _retriever.BM25SearchAsync(rewrittenQuery, k: 30));

        var (vectorResults, bm25Results) = await retrievalTask;
        var fusedResults = _retriever.ReciprocalRankFusion(
            vectorResults, bm25Results, k: 60);

        var reranked = await _reranker.RerankAsync(
            rewrittenQuery, fusedResults.Take(20).ToList(), topK: 12);

        var context = await _contextBuilder.BuildAsync(
            request.Query, reranked, request.ThreadHistory);

        var citationMap = _citationMapper.CreateMapping(reranked);

        var answerBuffer = new StringBuilder();
        await foreach (var token in _llmInference.GenerateAsync(
            context, citationMap, ct))
        {
            answerBuffer.Append(token);
            yield return new AnswerChunk
            {
                Content = token,
                CitationUpdates = citationMap.GetNewlyReferenced(token)
            };
        }

        var verification = await _qualityChecker.VerifyAsync(
            answerBuffer.ToString(), citationMap);
        if (verification.HasHallucination)
        {
            yield return new AnswerChunk
            {
                Content = "\n\n[Answer regenerated due to quality check]",
                RequiresRegeneration = true
            };
        }
    }
}

7. API Design

The API surface for an AI search engine must support streaming responses, conversation management, and various search modes. We define the primary endpoints below, using REST conventions with Server-Sent Events for streaming.

Primary Search Endpoint

// POST /api/v1/search
[ApiController]
[Route("api/v1")]
public class SearchController : ControllerBase
{
    private readonly ISearchOrchestrator _orchestrator;

    [HttpPost("search")]
    [Authorize]
    [RateLimit(QueriesPerMinute = 20)]
    public async Task StreamSearch([FromBody] SearchRequest request)
    {
        Response.ContentType = "text/event-stream";
        Response.Headers.Add("Cache-Control", "no-cache");
        Response.Headers.Add("Connection", "keep-alive");

        await foreach (var chunk in _orchestrator.SearchAsync(
            request, HttpContext.RequestAborted))
        {
            var sseData = JsonSerializer.Serialize(new
            {
                type = chunk.CitationUpdates != null ? "citation" : "token",
                content = chunk.Content,
                citations = chunk.CitationUpdates,
                done = chunk.IsComplete
            });

            await Response.WriteAsync($"data: {sseData}\n\n");
            await Response.Body.FlushAsync();
        }
    }
}

public class SearchRequest
{
    public string Query { get; set; }                 // Required
    public string ThreadId { get; set; }              // null for new thread
    public string FocusMode { get; set; } = "all";    // all, academic, youtube
    public string ProSearch { get; set; } = "off";    // off, standard, deep
    public int MaxSources { get; set; } = 10;
    public bool IncludeImages { get; set; } = false;
    public List<string> SourceFilters { get; set; }   // Domain allowlist
}

Thread Management Endpoints

MethodEndpointDescription
GET/api/v1/threadsList user's search threads
POST/api/v1/threadsCreate a new thread
GET/api/v1/threads/{id}Get thread with all turns
DELETE/api/v1/threads/{id}Delete a thread
PATCH/api/v1/threads/{id}Update thread title/settings
GET/api/v1/threads/{id}/shareGenerate share link

Follow-Up Query Endpoint

// POST /api/v1/search/follow-up
[HttpPost("search/follow-up")]
public async Task StreamFollowUp([FromBody] FollowUpRequest request)
{
    Response.ContentType = "text/event-stream";

    var context = await _threadService.GetThreadContextAsync(request.ThreadId);

    await foreach (var chunk in _orchestrator.FollowUpAsync(
        request.Query, context, HttpContext.RequestAborted))
    {
        var sseData = JsonSerializer.Serialize(new
        {
            type = chunk.Type,
            content = chunk.Content,
            citations = chunk.Citations,
            sources = chunk.NewSources,
            suggestions = chunk.FollowUpSuggestions
        });
        await Response.WriteAsync($"data: {sseData}\n\n");
        await Response.Body.FlushAsync();
    }
}

public class FollowUpRequest
{
    public string Query { get; set; }
    public string ThreadId { get; set; }
    public int PreviousTurnCount { get; set; } = 3
}

Rate Limiting Strategy

TierQueries/DayPro Search/DayBurst Limit
Free532 QPS
Pro ($20/mo)30030010 QPS
EnterpriseUnlimitedUnlimited100 QPS

8. Web Crawling & Indexing

The foundation of any AI search engine is its web index. Unlike traditional search engines that index the entire web, an AI search engine can focus on a curated set of high-quality sources while maintaining the ability to fetch any specific URL on demand. The crawl system must handle billions of URLs, respect robots.txt directives, manage politeness constraints, and keep content fresh enough to support real-time queries about current events.

Crawl Architecture

graph LR SeedURLs[Seed URL Queue] --> CrawlerWorkers[Crawler Workers
500 Instances] CrawlerWorkers --> ContentExtractor[Content Extractor
Readability + Custom] ContentExtractor --> Dedup[Deduplication
SimHash + MinHash] Dedup --> QualityFilter[Quality Filter
Spam + Low Quality] QualityFilter --> Chunker[Chunking Service
Semantic Boundaries] Chunker --> Embedder[Embedding Service
Batch API] Embedder --> IndexWriter[Index Writer
Bulk Ingestion] IndexWriter --> VectorDB[(Vector DB)] IndexWriter --> DocStore[(PostgreSQL)] IndexWriter --> SearchIndex[(Elasticsearch)] CrawlerWorkers --> URLFrontier[URL Frontier
Priority Queues] URLFrontier --> CrawlerWorkers

Crawl Priority System

Not all URLs are equally valuable. The crawler uses a priority scoring system that determines which pages to crawl first and how frequently to re-crawl them. Breaking news sources receive the highest priority, followed by popular reference sites, academic databases, and general web content. The priority score is computed from multiple signals.

public class CrawlPriorityScorer
{
    public float ComputePriority(URLRecord url)
    {
        float score = 0f;

        // Domain authority (0.0 - 0.4)
        score += url.DomainAuthority * 0.4f;

        // Freshness need (0.0 - 0.3)
        float freshnessFactor = url.AverageUpdateFrequency switch
        {
            < 1.0f => 0.1f,        // Updates yearly
            < 7.0f => 0.2f,        // Updates weekly
            < 1.0f => 0.3f,        // Updates daily
            _ => 0.4f               // Updates hourly
        };
        score += freshnessFactor * 0.3f;

        // Query relevance (0.0 - 0.2)
        float queryDemand = url.HistoricalQueryHits / MaxHits;
        score += Math.Min(queryDemand, 1.0f) * 0.2f;

        // Content quality (0.0 - 0.1)
        score += url.ContentQualityScore * 0.1f;

        // Inbound links from high-authority domains (0.0 - 0.1)
        float linkScore = url.HighAuthorityInboundLinks / 100f;
        score += Math.Min(linkScore, 1.0f) * 0.1f;

        return Math.Clamp(score, 0f, 1f);
    }

    public TimeSpan ComputeCrawlInterval(float priority, string contentType)
    {
        return contentType switch
        {
            "breaking_news" => TimeSpan.FromMinutes(1),
            "news" => TimeSpan.FromHours(1),
            "reference" => TimeSpan.FromHours(6),
            "blog" => TimeSpan.FromDays(1),
            "static" => TimeSpan.FromDays(7),
            _ => TimeSpan.FromDays(1)
        };
    }
}

Content Extraction and Chunking

Raw HTML must be processed into clean, structured text suitable for embedding and LLM consumption. The extraction pipeline removes navigation elements, advertisements, cookie banners, and boilerplate content, preserving only the main article text. The extracted content is then split into semantically meaningful chunks that respect paragraph boundaries, heading sections, and logical topic shifts.

ComponentTechnologyPurpose
HTML ParsingAngleSharp + Custom RulesParse and clean HTML
Content ExtractionReadability algorithm + CustomExtract main content
Language DetectionfastText lid.176Filter non-English content
DeduplicationSimHash + URL normalizationRemove near-duplicate pages
ChunkingSemantic splitter (512 tokens)Split for embedding
Metadata ExtractionCustom parserExtract dates, authors, topics
public class ContentChunker
{
    private const int MaxChunkTokens = 512;
    private const int OverlapTokens = 50;

    public List<DocumentChunk> ChunkDocument(IndexedDocument doc)
    {
        var chunks = new List<DocumentChunk>();
        var sentences = TokenizeIntoSentences(doc.Content);

        var currentChunk = new List<string>();
        int currentTokens = 0;

        foreach (var sentence in sentences)
        {
            int sentenceTokens = CountTokens(sentence);

            if (currentTokens + sentenceTokens > MaxChunkTokens && currentChunk.Count > 0)
            {
                chunks.Add(new DocumentChunk
                {
                    ChunkId = $"{doc.DocumentId}_chunk_{chunks.Count}",
                    DocumentId = doc.DocumentId,
                    Content = string.Join(" ", currentChunk),
                    TokenCount = currentTokens,
                    ChunkIndex = chunks.Count,
                    StartPosition = chunks.Count * (MaxChunkTokens - OverlapTokens)
                });

                // Keep overlap sentences for context continuity
                int overlapCount = 0;
                var overlapSentences = new List<string>();
                for (int i = currentChunk.Count - 1; i >= 0 && overlapCount < OverlapTokens; i--)
                {
                    overlapSentences.Insert(0, currentChunk[i]);
                    overlapCount += CountTokens(currentChunk[i]);
                }

                currentChunk = overlapSentences;
                currentTokens = overlapCount;
            }

            currentChunk.Add(sentence);
            currentTokens += sentenceTokens;
        }

        if (currentChunk.Count > 0)
        {
            chunks.Add(new DocumentChunk
            {
                ChunkId = $"{doc.DocumentId}_chunk_{chunks.Count}",
                DocumentId = doc.DocumentId,
                Content = string.Join(" ", currentChunk),
                TokenCount = currentTokens,
                ChunkIndex = chunks.Count
            });
        }

        return chunks;
    }
}

9. Query Understanding & Intent Classification

Query understanding is the brain of the AI search engine. Unlike traditional search where a keyword query is matched directly against an index, an AI search engine must deeply understand what the user is asking, determine the appropriate level of research depth, and rewrite the query to maximize retrieval quality. Poor query understanding cascades into poor retrieval, which cascades into poor answers — making this the single most impactful subsystem.

Intent Classification

Every incoming query is classified along multiple dimensions. The classification determines which retrieval strategy to use, how many sources to retrieve, whether to invoke Pro Search, and what level of citation density is expected.

Intent TypeExampleStrategyDepth
Factual Lookup"What is the population of Tokyo?"Direct answer, single authoritative sourceQuick
Explanatory"How does quantum entanglement work?"Multi-source synthesis, educational toneStandard
Comparison"React vs Vue for a large enterprise app?"Multi-perspective, structured comparisonStandard
How-To"How to deploy a .NET app to AWS Lambda?"Step-by-step, technical sourcesStandard
Current Events"What happened in the latest G7 summit?"Real-time crawl, news sources priorityStandard
Opinion/Debate"Is remote work better for productivity?"Multi-perspective, balanced presentationDeep
Code Generation"Write a C# implementation of LRU cache"Technical context, code-focusedStandard
Complex Research"Compare the regulatory frameworks for AI across the EU, US, and China"Deep research, multiple sub-queriesPro Search

Query Rewriting

Rewriting is crucial because users often ask questions ambiguously or with insufficient context. The query rewriting module uses a fine-tuned LLM to transform vague queries into precise retrieval queries while preserving the user's original intent.

public class QueryUnderstandingService
{
    private readonly ILLMInference _llm;
    private readonly IIntentClassifier _classifier;

    public async Task<QueryClassification> ClassifyAsync(string query)
    {
        var prompt = $@"Classify this search query along multiple dimensions.
Query: ""{query}""

Return JSON with:
- intent: factual | explanatory | comparison | howto | current_events | opinion | code | research
- depth_needed: quick | standard | deep
- temporal_relevance: none | recent | breaking
- topic_domain: general | technical | academic | news | code
- complexity_score: 1-10
- suggested_focus_mode: all | academic | youtube | reddit | news";

        var response = await _llm.CompleteAsync(prompt, maxTokens: 200);
        return JsonSerializer.Deserialize<QueryClassification>(response);
    }

    public async Task<RewrittenQuery> RewriteAsync(
        string originalQuery, QueryClassification classification,
        List<SearchTurn> threadHistory)
    {
        var context = threadHistory.Count > 0
            ? $"\nConversation context:\n{string.Join("\n", threadHistory.TakeLast(3).Select(t => $"User: {t.Query}\nAssistant: {t.Answer.Content[..200]}"))}"
            : "";

        var prompt = $@"Rewrite this search query for optimal web retrieval.
Original: ""{originalQuery}""
Intent: {classification.Intent}
Domain: {classification.TopicDomain}
{context}

Provide:
1. A primary retrieval query optimized for vector search
2. 2-3 alternative phrasings for diversity
3. Key entities to extract
4. Boolean operators or domain restrictions if applicable

Return as JSON.";

        var response = await _llm.CompleteAsync(prompt, maxTokens: 400);
        return JsonSerializer.Deserialize<RewrittenQuery>(response);
    }
}

Conversational Context Resolution

In multi-turn conversations, follow-up queries often contain pronouns and references that only make sense in context. "What about its performance?" after a discussion about React vs Vue requires resolving "its" to the previously discussed topic. The context resolution module uses coreference resolution and question reformulation to transform dependent queries into standalone retrieval queries.

public class ConversationalContextResolver
{
    public async Task<string> ResolveFollowUpAsync(
        string followUpQuery, List<SearchTurn> history)
    {
        if (history == null || history.Count == 0)
            return followUpQuery;

        var conversationSummary = string.Join("\n",
            history.TakeLast(5).Select(t =>
                $"Q: {t.Query}\nKey topics: {string.Join(", ", t.Classification.KeyEntities)}"));

        var prompt = $@"Given this conversation history and a follow-up question,
rewrite the follow-up as a standalone search query.

Conversation:
{conversationSummary}

Follow-up: ""{followUpQuery}""

Return the rewritten standalone query. Be concise.";

        return await _llm.CompleteAsync(prompt, maxTokens: 100);
    }
}

10. RAG Pipeline (Retrieve + Generate)

The RAG pipeline is the core of the AI search engine, combining document retrieval with LLM-based answer generation. A well-designed RAG pipeline must balance several competing objectives: retrieving the most relevant documents, fitting the maximum useful context into the LLM's context window, generating accurate and well-cited answers, and doing all of this within latency and cost constraints.

graph TB Query[User Query] --> QueryRewrite[Query Rewriting] QueryRewrite --> ParallelSearch{Parallel Retrieval} ParallelSearch --> VectorSearch[Vector Similarity
Top 30] ParallelSearch --> BM25Search[BM25 Keyword
Top 30] ParallelSearch --> KnowledgeGraph[Knowledge Graph
Entities] VectorSearch --> Fusion[Reciprocal Rank
Fusion] BM25Search --> Fusion KnowledgeGraph --> Fusion Fusion --> Reranker[Cross-Encoder
Reranker] Reranker --> Selection[Top-K Selection
K=8-12] Selection --> ContextBuild[Context Assembly] ContextBuild --> PromptConstruct[Prompt Construction
System + Context + Query] PromptConstruct --> LLM[LLM Generation
Streaming] LLM --> CitationExtract[Citation Extraction
Regex + Embeddings] CitationExtract --> QualityCheck[Quality Check
Hallucination Detection] QualityCheck --> Response[Final Response
with Citations]

Hybrid Retrieval Strategy

Neither vector search alone nor BM25 search alone is sufficient for high-quality retrieval. Vector search excels at semantic similarity — finding passages that are conceptually related to the query even when they use different words. BM25 excels at exact keyword matching — finding passages that contain specific technical terms, names, or phrases. By combining both approaches with reciprocal rank fusion, we achieve retrieval quality that exceeds either method individually.

public class HybridRetriever
{
    private readonly IVectorStore _vectorStore;
    private readonly ISearchIndex _searchIndex;
    private readonly IEmbeddingService _embedder;

    public async Task<List<RetrievedDocument>> RetrieveAsync(
        RewrittenQuery query, int topK = 12)
    {
        var embedding = await _embedder.EmbedAsync(query.PrimaryQuery);

        var vectorTask = _vectorStore.SearchAsync(embedding, topK: 30,
            filters: BuildFilters(query));
        var bm25Task = _searchIndex.SearchAsync(query.PrimaryQuery, topK: 30,
            filters: BuildFilters(query));

        await Task.WhenAll(vectorTask, bm25Task);

        var vectorResults = await vectorTask;
        var bm25Results = await bm25Task;

        // Reciprocal Rank Fusion
        float k = 60; // RRF constant
        var allDocs = new Dictionary<string, float>();

        foreach (var (doc, rank) in vectorResults.Select((d, i) => (d, i)))
        {
            allDocs.TryAdd(doc.DocumentId, 0f);
            allDocs[doc.DocumentId] += 1f / (k + rank + 1);
        }

        foreach (var (doc, rank) in bm25Results.Select((d, i) => (d, i)))
        {
            allDocs.TryAdd(doc.DocumentId, 0f);
            allDocs[doc.DocumentId] += 1f / (k + rank + 1);
        }

        var fused = allDocs
            .OrderByDescending(kvp => kvp.Value)
            .Take(30)
            .Select(kvp => new RetrievedDocument
            {
                DocumentId = kvp.Key,
                RRF_Score = kvp.Value
            })
            .ToList();

        return fused;
    }
}

Context Window Assembly

Assembling the context window is an optimization problem. We must maximize the information content within the token budget while maintaining logical structure and citation traceability. The context builder prioritizes documents by relevance score, extracts the most informative passages, and formats them with clear source markers that the LLM can reference in its output.

public class ContextBuilder
{
    private const int MaxContextTokens = 12000;
    private const int SystemPromptTokens = 500;
    private const int QueryTokens = 200;
    private const int ReserveTokens = 1000;

    public LLMContext BuildContext(
        string originalQuery,
        List<RerankedDocument> sources,
        List<SearchTurn> history)
    {
        int availableTokens = MaxContextTokens
            - SystemPromptTokens
            - QueryTokens
            - ReserveTokens;

        var context = new LLMContext();
        int usedTokens = 0;

        // Add conversation history (limited to 3 turns)
        if (history != null && history.Count > 0)
        {
            foreach (var turn in history.TakeLast(3))
            {
                var historyEntry = $"[Previous Q]: {turn.Query}\n" +
                                   $"[Previous A]: {TruncateToTokens(turn.Answer.Content, 300)}";
                int entryTokens = CountTokens(historyEntry);
                if (usedTokens + entryTokens > availableTokens * 0.2f) break;
                context.History.Add(historyEntry);
                usedTokens += entryTokens;
            }
        }

        // Add sources in relevance order
        foreach (var source in sources)
        {
            var passage = $"[Source {source.CitationNumber}]: {source.Title}\n" +
                         $"URL: {source.Url}\n" +
                         $"Content: {source.BestPassage}\n" +
                         $"Published: {source.PublishedDate?.ToString("yyyy-MM-dd") ?? "Unknown"}";

            int passageTokens = CountTokens(passage);
            if (usedTokens + passageTokens > availableTokens) break;

            context.Sources.Add(passage);
            context.CitationMap.Add(source.CitationNumber, source);
            usedTokens += passageTokens;
        }

        context.SystemPrompt = BuildSystemPrompt(originalQuery, context.Sources.Count);
        context.UserQuery = originalQuery;
        context.TokenBudget = usedTokens;

        return context;
    }
}

Streaming and Token-Level Citation Injection

One of the most distinctive features of Perplexity-style search is real-time streaming with live citation updates. As the LLM generates tokens, the system monitors for citation references (e.g., [1], [2]) and sends citation metadata to the client before the user encounters the reference number. This creates the illusion that the answer is being assembled in real-time with sources appearing as they become relevant.

11. Citation & Source Ranking

Citations are the trust mechanism of an AI search engine. Unlike traditional search where users can evaluate each result before clicking, AI search presents a synthesized answer that must be verifiable. The citation system must accurately attribute claims to sources, rank sources by reliability, and present citations in a way that builds user confidence.

Source Ranking Algorithm

Source quality is determined by a composite score that combines multiple signals. Each signal captures a different aspect of source reliability, and the weights are tuned based on the query type and domain.

SignalWeightDescription
Domain Authority0.25Computed from link graph analysis (similar to PageRank)
Content Freshness0.20Recency of publication or last update
Retrieval Score0.20Combined vector + BM25 relevance score
Citation Count0.10Number of academic/web citations pointing to this source
Source Type Bonus0.10Academic papers, government data, official docs get bonus
Readability Score0.05Clarity and structure of the content
Author Reputation0.05Known expert authors get higher scores
Engagement Signals0.05Share counts, comment quality (for forums)

Citation Extraction from LLM Output

public class CitationExtractor
{
    private static readonly Regex CitationPattern = new(
        @"\[(\d+)\]", RegexOptions.Compiled);

    public List<Citation> ExtractCitations(
        string llmOutput, Dictionary<int, RetrievedDocument> citationMap)
    {
        var citations = new List<Citation>();
        var matches = CitationPattern.Matches(llmOutput);
        var seenNumbers = new HashSet<int>();

        foreach (Match match in matches)
        {
            int citationNumber = int.Parse(match.Groups[1].Value);
            if (!seenNumbers.Add(citationNumber)) continue;
            if (!citationMap.ContainsKey(citationNumber)) continue;

            var source = citationMap[citationNumber];
            var snippet = ExtractSnippet(llmOutput, match.Index, 200);

            citations.Add(new Citation
            {
                CitationNumber = citationNumber,
                SourceDocumentId = source.DocumentId,
                Url = source.Url,
                Title = source.Title,
                Snippet = snippet,
                Domain = source.Domain,
                PublishedDate = source.PublishedDate,
                RelevanceScore = source.RerankScore,
                IsVerified = false
            });
        }

        return citations;
    }

    private string ExtractSnippet(string text, int position, int contextLength)
    {
        int start = Math.Max(0, position - contextLength / 2);
        int length = Math.Min(contextLength, text.Length - start);
        return text.Substring(start, length).Trim();
    }
}

Citation Verification Pipeline

After generation, each citation undergoes verification. The system checks that the cited URL actually contains the information attributed to it by the LLM. This is done by extracting the relevant passage from the source document and computing semantic similarity with the generated claim. Citations below a similarity threshold are flagged for potential correction or removal.

13. Conversational Follow-Up

Conversational follow-up is what transforms an AI search engine from a glorified lookup tool into a genuine research assistant. Users should be able to ask a question, receive an answer, and then drill deeper into specific aspects without repeating context. The follow-up system must maintain coherent conversation state, resolve ambiguous references, and progressively refine answers as the user's information needs become clearer.

Conversation State Management

public class ConversationManager
{
    private readonly IThreadStore _threadStore;
    private readonly IContextResolver _contextResolver;
    private readonly ISearchOrchestrator _orchestrator;

    public async Task<ConversationState> ProcessTurnAsync(
        string threadId, string query, string userId)
    {
        var thread = await _threadStore.GetThreadAsync(threadId);
        if (thread == null)
        {
            thread = await _threadStore.CreateThreadAsync(
                userId, await GenerateTitleAsync(query));
        }

        // Determine if this is a follow-up or standalone query
        var contextType = await _contextResolver.ClassifyContextAsync(
            query, thread.Turns);

        string resolvedQuery = contextType switch
        {
            ContextType.Standalone => query,
            ContextType.FollowUp => await _contextResolver.ResolveAsync(
                query, thread.Turns),
            ContextType.SpecificSource => await _contextResolver.ExpandSourceQueryAsync(
                query, thread.Turns),
            _ => query
        };

        // Execute search with appropriate context depth
        int contextDepth = contextType switch
        {
            ContextType.FollowUp => 3,
            ContextType.SpecificSource => 2,
            _ => 0
        };

        var searchResult = await _orchestrator.SearchWithHistoryAsync(
            resolvedQuery, thread.Turns.TakeLast(contextDepth).ToList());

        var turn = new SearchTurn
        {
            TurnId = Guid.NewGuid().ToString(),
            Query = query,
            RewrittenQuery = resolvedQuery,
            Classification = searchResult.Classification,
            Sources = searchResult.Sources,
            Answer = searchResult.Answer,
            Citations = searchResult.Citations,
            FollowUps = await GenerateFollowUpsAsync(searchResult, thread),
            TimestampedAt = DateTime.UtcNow
        };

        thread.Turns.Add(turn);
        await _threadStore.SaveThreadAsync(thread);

        return new ConversationState
        {
            ThreadId = thread.ThreadId,
            Turn = turn,
            SuggestedFollowUps = turn.FollowUps
        };
    }
}

Follow-Up Suggestion Generation

After each answer, the system generates 3-4 suggested follow-up questions that help users explore the topic deeper. These suggestions are generated by the LLM based on the answer content, identified knowledge gaps, and common related questions. The suggestions are ranked by likely information gain — prioritizing questions that would add new information rather than rephrase the original query.

Suggestion TypeExamplePurpose
Deep Dive"What specific quantum computing breakthroughs occurred in 2026?"Narrow focus on most relevant sub-topic
Comparison"How does this compare to the previous approach?"Add contrast and context
Counterpoint"What are the limitations of this approach?"Provide balanced perspective
Application"How can I apply this in a production environment?"Move from theory to practice
Related Topic"What about its impact on the energy sector?"Explore adjacent domains

14. Knowledge Graph Integration

While vector search and BM25 form the backbone of retrieval, a knowledge graph adds structured reasoning capabilities that neither can provide. Knowledge graphs capture entities and their relationships — people, organizations, concepts, events, and how they connect. This structured knowledge enables the AI search engine to answer questions that require traversing relationships, performing entity disambiguation, and providing contextual information that pure text retrieval might miss.

Knowledge Graph Schema

graph LR Entity[Entity Node] -->|RELATED_TO| Entity Entity -->|AUTHOR_OF| Document Document -->|MENTIONS| Entity Entity -->|SUBCLASS_OF| Category Entity -->|LOCATED_IN| Location Event[Event] -->|INVOLVES| Entity Event -->|OCCURRED_ON| DateTime Entity -->|WORKS_AT| Organization Organization -->|FOUNDED_BY| Entity Document -->|CITES| Document

Knowledge Graph Construction

public class KnowledgeGraphBuilder
{
    private readonly INeo4jDriver _graphDb;
    private readonly IEntityExtractor _entityExtractor;
    private readonly IRelationClassifier _relationClassifier;

    public async Task<GraphUpdateResult> UpdateFromDocumentAsync(
        IndexedDocument document)
    {
        // Extract entities using NER model
        var entities = await _entityExtractor.ExtractAsync(document.Content);

        // Classify relationships between entities
        var relationships = await _relationClassifier.ClassifyAsync(
            entities, document.Content);

        await using var session = _graphDb.AsyncSession();
        await session.ExecuteWriteAsync(tx =>
        {
            // Merge document node
            tx.Run(@"
                MERGE (d:Document {id: $docId})
                SET d.url = $url, d.title = $title,
                    d.crawledAt = $crawledAt",
                new { docId = document.DocumentId, url = document.Url,
                      title = document.Title, crawledAt = document.CrawledAt });

            // Merge entity nodes and relationships
            foreach (var entity in entities)
            {
                tx.Run(@"
                    MERGE (e:Entity {name: $name, type: $type})
                    SET e.aliases = $aliases, e.lastSeen = $now
                    MERGE (d:Document {id: $docId})
                    MERGE (e)-[:MENTIONED_IN]->(d)",
                    new { name = entity.Name, type = entity.Type,
                          aliases = entity.Aliases, now = DateTimeOffset.UtcNow,
                          docId = document.DocumentId });
            }

            // Create relationship edges
            foreach (var rel in relationships)
            {
                tx.Run($@"
                    MATCH (a:Entity {{name: $source}})
                    MATCH (b:Entity {{name: $target}})
                    MERGE (a)-[r:{rel.Type}]->(b)
                    SET r.confidence = $confidence,
                        r.evidence = $evidence",
                    new { source = rel.SourceEntity, target = rel.TargetEntity,
                          confidence = rel.Confidence, evidence = rel.Evidence });
            }
        });

        return new GraphUpdateResult
        {
            EntitiesAdded = entities.Count,
            RelationshipsAdded = relationships.Count
        };
    }
}

Knowledge Graph Query Integration

When a query requires structured reasoning, the knowledge graph is queried in parallel with vector and BM25 search. For example, a query like "Who are the founders of companies competing with Google in AI search?" requires traversing company-to-founder relationships and identifying AI search competitors — a task that knowledge graphs handle naturally but vector search handles poorly.

15. Answer Quality Evaluation

Answer quality is the defining metric for an AI search engine. A search engine that returns inaccurate answers or fabricated citations will lose user trust permanently. The quality evaluation system operates at multiple levels: pre-generation validation, post-generation verification, and ongoing quality monitoring through user feedback and automated evaluation.

Quality Dimensions

DimensionMeasurementThresholdImpact
Factual AccuracyCross-reference with source content> 95% claims supportedCritical
Citation ValidityVerify each citation points to relevant content> 98% validCritical
CompletenessCoverage of query aspects> 85% coverageHigh
RelevanceAnswer directly addresses the query> 90% relevanceHigh
ReadabilityClear, well-structured proseFlesch-Kincaid > 60Medium
BalancePresents multiple viewpoints when applicableScore > 0.7Medium
HallucinationClaims not in any source document< 2%Critical

Hallucination Detection

public class HallucinationDetector
{
    private readonly IEmbeddingService _embedder;
    private readonly ILLMInference _llm;

    public async Task<HallucinationReport> DetectAsync(
        string answer, List<RetrievedDocument> sources)
    {
        // Step 1: Decompose answer into individual claims
        var claims = await DecomposeClaimsAsync(answer);

        var report = new HallucinationReport();

        foreach (var claim in claims)
        {
            // Step 2: Embed the claim
            var claimEmbedding = await _embedder.EmbedAsync(claim.Text);

            // Step 3: Find most similar passage in sources
            float maxSimilarity = 0f;
            string bestMatchingSource = null;

            foreach (var source in sources)
            {
                var sourceEmbedding = await _embedder.EmbedAsync(
                    source.BestPassage);
                float similarity = CosineSimilarity(claimEmbedding, sourceEmbedding);
                if (similarity > maxSimilarity)
                {
                    maxSimilarity = similarity;
                    bestMatchingSource = source.DocumentId;
                }
            }

            // Step 4: Use LLM for fine-grained entailment check
            var entailment = await _llm.CompleteAsync($@"Does the following
claim follow from the source content?

Claim: ""{claim.Text}""
Source: ""{sources.First(s => s.DocumentId == bestMatchingSource).BestPassage}""

Answer with: SUPPORTED, PARTIALLY_SUPPORTED, or NOT_SUPPORTED",
                maxTokens: 20);

            report.Claims.Add(new ClaimVerification
            {
                Claim = claim.Text,
                Verdict = entailment.Trim(),
                SourceDocumentId = bestMatchingSource,
                SimilarityScore = maxSimilarity
            });
        }

        report.HallucinationRate = report.Claims
            .Count(c => c.Verdict == "NOT_SUPPORTED") / (float)report.Claims.Count;

        report.RequiresRegeneration = report.HallucinationRate > 0.15f;

        return report;
    }
}

16. Latency Optimization

Latency is a competitive moat in AI search. Users comparing Perplexity with Google expect similar or better response times, even though AI search performs dramatically more computation per query. Achieving sub-2-second time-to-first-token requires aggressive optimization across every layer of the stack.

Latency Budget Breakdown

StageTarget LatencyOptimization Strategy
Query Understanding< 50msCached classification model, small LLM
Query Rewriting< 100msDistilled model, streaming response
Vector Search< 80msHNSW index, GPU acceleration
BM25 Search< 30msSharded Elasticsearch, warm index
Reranking< 150msONNX Runtime, batched cross-encoder
Context Assembly< 20msIn-memory processing
LLM Time-to-First-Token< 300msSpeculative decoding, KV-cache
LLM Generation< 1500msStreaming, optimized batch sizes
Citation Verification< 100msAsync post-generation check
Network Overhead< 50msEdge deployment, CDN, WebSockets
Total (P50)< 2sParallel execution, streaming

Key Optimization Techniques

  1. KV-Cache Reuse: For conversations, the key-value cache from previous turns is preserved, so the LLM only needs to process new tokens rather than re-processing the entire context.
  2. Speculative Decoding: A smaller draft model generates candidate tokens that the larger model verifies in parallel, achieving 2-3× throughput improvement.
  3. Prefetching: While the LLM processes the query, the system prefetches likely follow-up sources based on the classified intent.
  4. Edge Caching: Popular queries (top 10% by volume) are cached at the edge for instant response.
  5. Model Routing: Simple factual queries are routed to smaller, faster models while complex queries use the full-size model.
public class LatencyOptimizedPipeline
{
    private readonly IModelRouter _router;
    private readonly IKVCacheManager _kvCache;
    private readonly IEdgeCache _edgeCache;

    public async IAsyncEnumerable<AnswerChunk> OptimizedSearchAsync(
        SearchRequest request, [EnumeratorCancellation] CancellationToken ct)
    {
        // Check edge cache first
        var cached = await _edgeCache.GetAsync(request.Query);
        if (cached != null)
        {
            foreach (var chunk in cached)
                yield return chunk;
            yield break;
        }

        // Route to appropriate model based on complexity
        var classification = await _router.ClassifyComplexityAsync(request.Query);
        var model = classification.ComplexityScore switch
        {
            < 4 => "sonar-small",      // Fast, simple queries
            < 7 => "sonar-medium",     // Balanced
            _ => "sonar-large"          // Deep reasoning
        };

        // Parallel retrieval and context preparation
        var retrievalTask = RetrieveSourcesAsync(request);
        var kvCacheTask = _kvCache.GetOrComputeAsync(
            request.ThreadId, request.Query);

        await Task.WhenAll(retrievalTask, kvCacheTask);

        var sources = await retrievalTask;
        var kvState = await kvCacheTask;

        // Stream generation with KV-cache warm start
        await foreach (var token in GenerateWithCacheAsync(
            model, request, sources, kvState, ct))
        {
            yield return token;
        }
    }
}

17. Citation Verification

Citation verification goes beyond the hallucination detection covered in the quality evaluation section. While hallucination detection checks whether claims are supported by sources, citation verification ensures that the specific citation numbers in the answer actually correspond to the source documents they reference. This is a subtle but critical distinction — an answer might contain factually accurate information that is attributed to the wrong source.

Verification Pipeline

graph LR Answer[Generated Answer] --> ExtractRefs[Extract Citation
References] ExtractRefs --> MatchSource[Match to Source
Documents] MatchSource --> SnippetVerify[Verify Snippet
in Source] SnippetVerify --> SemanticCheck[Semantic
Similarity Check] SemanticCheck --> Verdict{Verdict} Verdict -->|Verified| FlagGreen[✓ Verified] Verdict -->|Mismatch| FlagRed[✗ Mismatch] Verdict -->|Partial| FlagYellow[~ Partial] FlagRed --> AutoCorrect[Auto-Correct
Citation Mapping]
public class CitationVerifier
{
    private readonly IEmbeddingService _embedder;
    private readonly IDocumentStore _docStore;

    public async Task<VerificationResult> VerifyCitationAsync(
        Citation citation, string surroundingContext)
    {
        // Fetch the full source document
        var document = await _docStore.GetDocumentAsync(citation.SourceDocumentId);
        if (document == null)
            return new VerificationResult { Status = "DOCUMENT_NOT_FOUND" };

        // Check if the cited URL still exists and is accessible
        var isAccessible = await CheckUrlAccessibilityAsync(citation.Url);
        if (!isAccessible)
            return new VerificationResult { Status = "URL_INACCESSIBLE" };

        // Find the snippet in the source document
        var snippetFound = document.Content.Contains(citation.Snippet) ||
                          document.Chunks.Any(c =>
                              c.Contains(citation.Snippet[..100]));

        if (!snippetFound)
        {
            // Try fuzzy matching
            var bestMatch = FindBestFuzzyMatch(
                citation.Snippet, document.Chunks);
            if (bestMatch.Similarity < 0.7f)
                return new VerificationResult
                {
                    Status = "SNIPPET_NOT_FOUND",
                    Confidence = bestMatch.Similarity
                };
        }

        // Semantic verification: does the claim match the source?
        var contextEmbedding = await _embedder.EmbedAsync(surroundingContext);
        var sourceEmbedding = await _embedder.EmbedAsync(citation.Snippet);
        float semanticSimilarity = CosineSimilarity(contextEmbedding, sourceEmbedding);

        return new VerificationResult
        {
            Status = semanticSimilarity > 0.8f ? "VERIFIED" :
                     semanticSimilarity > 0.6f ? "PARTIAL" : "MISMATCH",
            Confidence = semanticSimilarity,
            VerifiedAt = DateTimeOffset.UtcNow,
            Suggestions = semanticSimilarity < 0.8f
                ? await FindBetterCitationAsync(surroundingContext, document)
                : null
        };
    }
}

18. User Personalization

Personalization in AI search must be handled carefully. Unlike social media where engagement optimization justifies heavy personalization, search personalization should focus on understanding user expertise levels, preferred content formats, and topic interests without creating filter bubbles that limit information diversity. The goal is to present answers at the right level of complexity and from sources the user finds trustworthy.

Personalization Signals

SignalSourceUsage
Expertise levelQuery history analysisAdjust answer complexity
Topic interestsBrowsing patternsPrioritize relevant sources
Preferred source typesClick-through dataRank familiar source types higher
Language preferencesExplicit setting + detectionFilter/generate in preferred language
Follow-up patternsThread analysisImprove suggestion generation
Feedback historyThumbs up/down, regenerateAdjust generation style

User Profile Model

public class UserProfile
{
    public string UserId { get; set; }
    public ExpertiseLevel Expertise { get; set; }
    public Dictionary<string, float> TopicInterests { get; set; }  // topic -> weight
    public List<string> PreferredSources { get; set; }
    public List<string> BlockedSources { get; set; }
    public string PreferredLanguage { get; set; }
    public string AnswerStyle { get; set; }           // concise, detailed, academic
    public int AverageQueryComplexity { get; set; }
    public List<string> FrequentlySearchedDomains { get; set; }
    public UserFeedbackProfile FeedbackProfile { get; set; }
    public DateTime LastActiveAt { get; set; }
}

public enum ExpertiseLevel
{
    Beginner,       // Simple explanations, more context
    Intermediate,   // Balanced depth
    Advanced,       // Technical detail, minimal hand-holding
    Expert          // Assume deep domain knowledge
}

public class PersonalizationService
{
    private readonly IUserProfileStore _profileStore;
    private readonly IBehaviorAnalyzer _behaviorAnalyzer;

    public async Task<PersonalizationContext> GetPersonalizationAsync(string userId)
    {
        var profile = await _profileStore.GetProfileAsync(userId);
        if (profile == null)
            return PersonalizationContext.Default();

        // Update expertise level based on recent queries
        profile.Expertise = await _behaviorAnalyzer.DetermineExpertiseAsync(
            userId, recentDays: 30);

        return new PersonalizationContext
        {
            ExpertiseLevel = profile.Expertise,
            PreferredSources = profile.PreferredSources,
            TopicBoosts = profile.TopicInterests
                .OrderByDescending(kvp => kvp.Value)
                .Take(5)
                .ToDictionary(kvp => kvp.Key, kvp => kvp.Value),
            AnswerStyle = profile.AnswerStyle,
            SourcePenalties = profile.BlockedSources
        };
    }
}

19. Monetization & Pro

Building an AI search engine requires significant infrastructure investment, primarily driven by LLM inference costs and web crawling operations. A sustainable business model must balance free access for user acquisition with premium features that generate revenue. Perplexity's model provides a useful template, but there are several additional monetization opportunities unique to AI search.

Tier Structure

FeatureFreePro ($20/mo)Enterprise (Custom)
Daily Queries5300Unlimited
Pro Search (Deep)3/day300/dayUnlimited
File Upload3/dayUnlimitedUnlimited
API AccessNoLimited (1000 QPM)High (100K QPM)
Data IsolationNoNoYes, dedicated instance
Custom Knowledge BaseNoNoYes, private RAG
SSO / SCIMNoNoYes
Analytics DashboardBasicAdvancedFull + Export
Priority SupportNoEmailDedicated CSM

Revenue Streams

  1. Subscriptions: Pro tier at $20/month is the primary revenue driver. At scale, this provides predictable recurring revenue.
  2. Enterprise Licensing: Annual contracts for enterprise search deployments, typically $50K-$500K per year depending on scale.
  3. API Monetization: Per-query pricing for developers building on the search API. $1-5 per 1000 queries depending on depth.
  4. Answer-Integrated Advertising: Sponsored content that appears as additional sources alongside organic answers. Must be clearly labeled to maintain trust.
  5. Data Licensing: Aggregated, anonymized search trend data licensed to market research firms.
  6. Shopping Integration: When queries have purchase intent, product recommendations with affiliate commissions.

Cost-Per-Query Economics

public class CostEstimator
{
    public QueryCostEstimate EstimateCost(QueryComplexity complexity)
    {
        return complexity switch
        {
            QueryComplexity.Simple => new QueryCostEstimate
            {
                LLMCost = 0.001m,         // Small model, short answer
                EmbeddingCost = 0.0002m,
                RetrievalCost = 0.0001m,
                TotalCost = 0.0013m
            },
            QueryComplexity.Standard => new QueryCostEstimate
            {
                LLMCost = 0.005m,         // Medium model
                EmbeddingCost = 0.0005m,
                RetrievalCost = 0.0003m,
                CrawlCost = 0.001m,       // Real-time search
                TotalCost = 0.0068m
            },
            QueryComplexity.ProSearch => new QueryCostEstimate
            {
                LLMCost = 0.025m,         // Large model, multi-step
                EmbeddingCost = 0.002m,
                RetrievalCost = 0.001m,
                CrawlCost = 0.005m,       // Multiple real-time searches
                VerificationCost = 0.003m,
                TotalCost = 0.036m
            },
            _ => throw new ArgumentException($"Unknown complexity: {complexity}")
        };
    }
}
Path to profitability: At an average blended cost of $0.008 per query and an average revenue of $0.015 per query (combining subscription and API revenue), the system achieves positive unit economics. Breaking even requires approximately 500K paying subscribers or equivalent enterprise/API revenue.

21. Content Freshness Pipeline

Content freshness is a critical competitive advantage for AI search. Users increasingly turn to AI search for current events, breaking news, and rapidly evolving topics. The freshness pipeline must detect when content changes, prioritize re-crawling of time-sensitive sources, and ensure that the index reflects the most recent version of each page.

Freshness Strategy

graph TB ChangeDetection[Change Detection
RSS, Sitemaps, Monitoring] PriorityQueue[Priority Re-crawl Queue] FreshnessClassifier[Freshness Classifier
Breaking vs Evergreen] ReCrawl[Targeted Re-crawl] DiffEngine[Content Diff Engine
Semantic Change Detection] IndexUpdate[Index Update
Incremental Re-embedding] CacheInvalidation[Cache Invalidation
Edge + CDN] ChangeDetection --> PriorityQueue PriorityQueue --> FreshnessClassifier FreshnessClassifier -->|Breaking| ReCrawl FreshnessClassifier -->|Updated| ReCrawl FreshnessClassifier -->|Evergreen| IndexUpdate ReCrawl --> DiffEngine DiffEngine --> IndexUpdate IndexUpdate --> CacheInvalidation

Change Detection Sources

SourceLatencyCoverageReliability
RSS/Atom FeedsReal-time~30% of quality sourcesHigh
Sitemap IndexMinutes~60% of major sitesMedium
Diff-based Polling1-6 hoursTargeted high-value pagesMedium
News API AggregationReal-timeMajor news outletsHigh
Social Media SignalsMinutesViral content detectionLow-Medium
Periodic Full CrawlDaily-WeeklyLong tail contentHigh
public class ContentFreshnessManager
{
    private readonly IRssMonitor _rssMonitor;
    private readonly ISitemapScanner _sitemapScanner;
    private readonly IContentDiffEngine _diffEngine;
    private readonly ICrawlPriorityQueue _priorityQueue;

    public async Task<FreshnessReport> ProcessUpdatesAsync()
    {
        var updates = new List<ContentUpdate>();

        // Collect changes from multiple sources
        var rssChanges = await _rssMonitor.GetRecentChangesAsync(
            since: TimeSpan.FromMinutes(15));
        var sitemapChanges = await _sitemapScanner.GetChangesAsync(
            since: TimeSpan.FromHours(1));

        updates.AddRange(rssChanges.Select(u => new ContentUpdate
        {
            Url = u.Url,
            DetectedAt = u.PublishedAt,
            Source = "rss",
            Priority = ClassifyUrgency(u)
        }));

        updates.AddRange(sitemapChanges.Select(u => new ContentUpdate
        {
            Url = u.Url,
            DetectedAt = u.LastModified,
            Source = "sitemap",
            Priority = ClassifyUrgency(u)
        }));

        // Deduplicate and prioritize
        var prioritized = updates
            .GroupBy(u => u.Url)
            .Select(g => g.OrderByDescending(u => u.Priority).First())
            .OrderByDescending(u => u.Priority)
            .ToList();

        // Enqueue for re-crawl
        foreach (var update in prioritized)
        {
            await _priorityQueue.EnqueueAsync(update);
        }

        return new FreshnessReport
        {
            ChangesDetected = updates.Count,
            HighPriorityUpdates = updates.Count(u => u.Priority > 0.8f),
            ProcessedAt = DateTimeOffset.UtcNow
        };
    }
}

22. Security & Privacy

An AI search engine handles extremely sensitive data — user queries reveal their interests, knowledge gaps, professional needs, and personal concerns. Security and privacy must be foundational, not afterthought. The system must protect user data at rest and in transit, prevent prompt injection attacks, and comply with global privacy regulations.

Threat Model

ThreatAttack VectorMitigation
Prompt InjectionInjected instructions in retrieved web contentContent sanitization, input/output guards
Data ExfiltrationQuery crafted to extract other users' dataTenant isolation, query sanitization
JailbreakingBypassing safety filtersMulti-layer safety classifiers
Search PoisoningGaming source ranking to inject misinformationSource reputation, cross-reference verification
Session HijackingStealing user session tokensSecure cookies, token rotation, HTTPS-only
API AbuseAutomated scraping of generated answersRate limiting, bot detection, CAPTCHA

Security Implementation

public class QuerySecurityGuard
{
    private readonly ISafetyClassifier _safetyClassifier;
    private readonly IPromptInjectionDetector _injectionDetector;
    private readonly ILogger<QuerySecurityGuard> _logger;

    public async Task<SecurityResult> ValidateQueryAsync(
        string query, string userId, string sessionId)
    {
        // Layer 1: Input length validation
        if (query.Length > 5000)
            return SecurityResult.Reject("Query exceeds maximum length");

        // Layer 2: Prompt injection detection
        var injectionResult = await _injectionDetector.DetectAsync(query);
        if (injectionResult.IsInjection)
        {
            _logger.LogWarning(
                "Prompt injection detected from user {UserId}: {Score}",
                userId, injectionResult.Confidence);
            return SecurityResult.Reject("Query contains disallowed content");
        }

        // Layer 3: Safety classification
        var safetyResult = await _safetyClassifier.ClassifyAsync(query);
        if (safetyResult.Category == SafetyCategory.Harmful)
            return SecurityResult.Reject("Query violates usage policy");

        if (safetyResult.Category == SafetyCategory.Sensitive)
        {
            return SecurityResult.AllowWithModification(
                new ContentFilter
                {
                    FilterPII = true,
                    FilterMedical = true,
                    FilterLegal = true,
                    DisclaimerRequired = true
                });
        }

        // Layer 4: Rate limiting per user
        var rateCheck = await CheckRateLimitAsync(userId, sessionId);
        if (!rateCheck.IsAllowed)
            return SecurityResult.RateLimited(rateCheck.RetryAfter);

        return SecurityResult.Allow();
    }

    public async Task<OutputGuardResult> GuardOutputAsync(
        string generatedAnswer, List<Citation> citations)
    {
        // Check for PII leakage in generated content
        var piiCheck = DetectPII(generatedAnswer);
        if (piiCheck.HasPII)
        {
            generatedAnswer = RedactPII(generatedAnswer, piiCheck.Findings);
        }

        // Validate all citations point to safe domains
        var unsafeCitations = citations
            .Where(c => IsUnsafeDomain(c.Domain))
            .ToList();
        if (unsafeCitations.Any())
        {
            citations = citations.Except(unsafeCitations).ToList();
        }

        return new OutputGuardResult
        {
            SanitizedAnswer = generatedAnswer,
            SanitizedCitations = citations,
            WasModified = piiCheck.HasPII || unsafeCitations.Any()
        };
    }
}

Privacy Architecture

User queries are encrypted at rest using AES-256 with per-user keys managed through a cloud KMS. Query logs are automatically purged after 90 days for free users and configurable retention for enterprise. No query data is shared with third-party LLM providers — all inference runs on self-hosted models or through enterprise agreements with data processing agreements (DPAs). The system supports GDPR right-to-erasure through automated data deletion pipelines that remove user data within 24 hours of request.

23. Monitoring & Observability

Operating an AI search engine requires monitoring a complex distributed system where traditional metrics (latency, throughput, error rates) must be supplemented with AI-specific quality metrics. A degradation in answer quality may not manifest as errors but rather as user dissatisfaction — measured through thumbs-down rates, query reformulation frequency, and thread abandonment.

Key Metrics Dashboard

MetricCategoryAlert Threshold
Time-to-First-Token (P95)Performance> 500ms
End-to-End Latency (P95)Performance> 8s
Answer Generation Success RateReliability< 99%
Citation Verification RateQuality< 95%
Hallucination Rate (sampled)Quality> 5%
User Thumbs-Down RateUser Experience> 10%
Query Rejection RateSafety> 2%
LLM Token UsageCost> 120% of budget
Crawl Queue DepthOperations> 1M pending
Vector DB Index LagFreshness> 1 hour

Observability Stack

public class SearchTelemetry
{
    private readonly ITelemetryClient _telemetry;

    public void TrackSearchMetrics(SearchMetrics metrics)
    {
        // Standard performance metrics
        _telemetry.TrackMetric("search.latency.total_ms", metrics.TotalLatencyMs);
        _telemetry.TrackMetric("search.latency.ttft_ms", metrics.TimeToFirstTokenMs);
        _telemetry.TrackMetric("search.latency.retrieval_ms", metrics.RetrievalLatencyMs);
        _telemetry.TrackMetric("search.latency.reranking_ms", metrics.RerankingLatencyMs);
        _telemetry.TrackMetric("search.latency.generation_ms", metrics.GenerationLatencyMs);

        // Quality metrics
        _telemetry.TrackMetric("search.citations.count", metrics.CitationCount);
        _telemetry.TrackMetric("search.citations.verified_rate", metrics.CitationVerifiedRate);
        _telemetry.TrackMetric("search.quality.similarity_score", metrics.SimilarityScore);

        // Cost metrics
        _telemetry.TrackMetric("search.cost.input_tokens", metrics.InputTokens);
        _telemetry.TrackMetric("search.cost.output_tokens", metrics.OutputTokens);
        _telemetry.TrackMetric("search.cost.total_usd", metrics.CostUSD);

        // User engagement
        _telemetry.TrackEvent("search.completed", new Dictionary<string, string>
        {
            ["intent"] = metrics.QueryClassification,
            ["model_used"] = metrics.ModelUsed,
            ["has_follow_up"] = metrics.HasFollowUp.ToString(),
            ["user_feedback"] = metrics.UserFeedback ?? "none"
        });

        // Distributed tracing
        using var span = _telemetry.StartSpan("search.request");
        span.SetAttribute("query.length", metrics.QueryLength);
        span.SetAttribute("sources.retrieved", metrics.SourcesRetrieved);
        span.SetAttribute("generation.tokens", metrics.OutputTokens);
    }
}

24. Cost Estimation

Cost estimation for an AI search engine is complex because the major cost drivers — LLM inference and embedding generation — scale non-linearly with usage. Understanding the cost structure is essential for pricing decisions, capacity planning, and identifying optimization opportunities.

Monthly Cost Breakdown (at 50M queries/month)

ComponentUnit CostMonthly Cost% of Total
LLM Inference (self-hosted)$0.003/query$150,00030%
Embedding Generation$0.0003/query$15,0003%
Vector DB (GPU-accelerated)$80K/mo cluster$80,00016%
Elasticsearch Cluster$50K/mo cluster$50,00010%
PostgreSQL$20K/mo cluster$20,0004%
Redis Cache$10K/mo cluster$10,0002%
Crawler Infrastructure$30K/mo$30,0006%
CDN / Edge$15K/mo$15,0003%
Compute (API, workers)$60K/mo$60,00012%
Monitoring & Logging$10K/mo$10,0002%
Storage (S3, block)$20K/mo$20,0004%
Bandwidth$10K/mo$10,0002%
Engineering Team (20 people)$25K/person$500,000~included
Total Infrastructure$470,000100%
Cost optimization levers: The biggest opportunities are (1) self-hosting LLMs instead of using API providers (saves 60-70% on inference), (2) model distillation for simple queries (saves 40% on average cost), (3) aggressive KV-cache reuse in conversations (saves 30% on repeat context processing), and (4) intelligent query routing to avoid unnecessary Pro Search invocations.

25. Testing Strategy

Testing an AI search engine is fundamentally different from testing traditional software. The non-deterministic nature of LLM outputs means that simple assertion-based tests are insufficient. Instead, we must use a combination of evaluation benchmarks, statistical quality checks, regression test suites, and continuous human evaluation.

Testing Levels

LevelTypeWhat We TestFrequency
UnitComponent testsQuery parser, citation extractor, chunkerEvery commit
IntegrationPipeline testsRetrieval + generation end-to-endDaily
EvaluationBenchmark testsAnswer quality on curated Q&A datasetWeekly
RegressionGolden setKnown-good answers for reference queriesPre-deploy
A/B TestingLive experimentsUser satisfaction metricsContinuous
AdversarialRed team testsPrompt injection, jailbreaking attemptsWeekly

Answer Quality Evaluation Framework

public class AnswerQualityEvaluator
{
    private readonly ILLMInference _judgeModel;
    private readonly ICitationVerifier _citationVerifier;

    public async Task<QualityReport> EvaluateAnswerAsync(
        string query, GeneratedAnswer answer, List<RetrievedDocument> sources)
    {
        var report = new QualityReport();

        // 1. Relevance scoring (1-5 scale)
        report.RelevanceScore = await _judgeModel.CompleteAsync($@"
Rate the relevance of this answer to the query (1-5):
Query: ""{query}""
Answer: ""{answer.Content}""

Respond with just the number.", maxTokens: 5);

        // 2. Citation accuracy
        var citationResults = new List<CitationCheck>();
        foreach (var citation in answer.Citations)
        {
            var verification = await _citationVerifier.VerifyCitationAsync(
                citation, answer.Content);
            citationResults.Add(new CitationCheck
            {
                CitationNumber = citation.CitationNumber,
                IsVerified = verification.Status == "VERIFIED",
                Confidence = verification.Confidence
            });
        }
        report.CitationAccuracy = citationResults
            .Count(c => c.IsVerified) / (float) citationResults.Count;

        // 3. Hallucination check
        report.HallucinationRate = await DetectHallucinationRateAsync(
            answer.Content, sources);

        // 4. Completeness
        report.CompletenessScore = await _judgeModel.CompleteAsync($@"
Rate how completely this answer addresses all aspects of the query (1-5):
Query: ""{query}""
Answer: ""{answer.Content}""

Respond with just the number.", maxTokens: 5);

        // 5. Overall quality composite
        report.OverallScore =
            (report.RelevanceScore * 0.3f) +
            (report.CitationAccuracy * 0.25f) +
            ((1 - report.HallucinationRate) * 0.25f) +
            (report.CompletenessScore * 0.2f);

        return report;
    }
}

26. Interview Q&A

System design interviews for AI search roles focus on the unique challenges of building RAG systems at scale. Below are the most frequently asked questions along with structured answer frameworks that demonstrate senior-level understanding.

Question 1: How would you design a Perplexity-style search engine from scratch?

Framework: Start by clarifying the scope (consumer vs. enterprise), then define the core pipeline: query understanding → retrieval → reranking → generation → citation mapping → quality verification. Discuss the hybrid retrieval strategy (vector + BM25), the importance of reranking, and the streaming architecture for real-time answers. Address the unique challenges: citation accuracy, hallucination prevention, and latency optimization.

Question 2: How do you handle citation accuracy and prevent hallucinations?

Framework: Explain the multi-layer approach. First, the retrieval step ensures relevant context is provided to the LLM. Second, the prompt engineering explicitly instructs the model to cite sources. Third, post-generation verification cross-references each citation claim against source content using semantic similarity. Fourth, a quality gate can reject or regenerate answers with high hallucination scores. Mention that citation accuracy degrades with context length, so strategic context truncation is important.

Question 3: How do you achieve sub-second latency for LLM-based search?

Framework: Emphasize parallel execution — retrieval, query understanding, and context preparation happen concurrently. Discuss KV-cache reuse for conversations, model routing (small models for simple queries), speculative decoding for throughput, edge caching for popular queries, and streaming to hide generation latency behind time-to-first-token. Mention that the P99 target is more relaxed (5-8 seconds) for deep research queries.

Question 4: How would you handle a breaking news event with 100× traffic spike?

Framework: Discuss auto-scaling for the API layer, priority re-crawling of news sources, edge caching of common news queries, and graceful degradation (falling back to cached or slightly stale answers). Mention the importance of the freshness pipeline — detecting breaking events via RSS and news APIs, triggering immediate re-crawls, and updating the index within minutes. Address the LLM inference scaling challenge — GPU capacity pre-provisioning with spot instances for burst handling.

Question 5: How do you evaluate the quality of generated answers?

Framework: Explain the multi-dimensional evaluation approach. Automated metrics include citation verification rate, hallucination detection (claim-level entailment checks), answer relevance scoring (using a judge model), and completeness assessment. User-facing metrics include thumbs-up/down ratios, query reformulation rates (users rephrasing suggests the first answer missed the mark), and thread continuation rates. Human evaluation provides ground truth calibration. A/B testing on answer variations helps identify which generation strategies produce the most satisfying results.

Question 6: Design the conversational follow-up system.

Framework: Start with conversation state management — maintaining thread history and using it to resolve ambiguous follow-up queries. Discuss the query rewriting pipeline that transforms context-dependent questions ("What about its performance?") into standalone retrieval queries. Explain the KV-cache optimization for conversations, where the system reuses cached key-value states from previous turns. Address follow-up suggestion generation and the importance of information gain scoring to ensure suggestions explore new dimensions of the topic.

Question 7: How would you handle adversarial prompts and prompt injection through retrieved web content?

Framework: This is a critical security concern. Explain the defense-in-depth approach. First, web content is sanitized before being included in the LLM context — HTML stripped, suspicious instruction patterns removed. Second, the system prompt explicitly instructs the LLM to ignore any instructions found within the source content. Third, an output guard checks for common prompt injection success patterns. Fourth, rate limiting and behavioral analysis detect automated probing. Mention that this is an arms race requiring continuous monitoring and adaptation.

Question 8: Compare vector search vs. BM25 for AI search. Why use both?

Framework: Vector search excels at semantic understanding — matching queries to passages that use different words but express the same concept. BM25 excels at exact keyword matching — critical for technical terms, proper nouns, code, and specific phrases. Neither alone is sufficient. Vector search misses exact matches; BM25 misses semantic similarity. Reciprocal rank fusion combines both result lists, and the cross-encoder reranker further refines the combined ranking. This hybrid approach consistently outperforms either method alone on retrieval benchmarks.

Question 9: How do you design the content freshness pipeline for a search engine?

Framework: Describe the multi-source change detection system (RSS feeds, sitemap monitoring, diff-based polling, news API aggregation). Explain the priority queue that ensures breaking news sources are re-crawled within minutes while evergreen content is refreshed daily or weekly. Discuss semantic change detection that avoids re-indexing pages with trivial changes (ad updates, timestamp shifts). Mention the cascade: change detection → priority queue → targeted re-crawl → content diff → selective re-embedding → index update → cache invalidation.

Question 10: How would you build enterprise search on top of this architecture?

Framework: Enterprise search adds access control, multi-tenancy, and compliance requirements. Explain document-level ACLs that filter search results based on user permissions. Discuss tenant isolation for data, LLM inference, and vector storage. Address compliance requirements (SOC 2 audit logging, GDPR data deletion, encryption at rest). Describe the connector architecture for integrating with enterprise data sources (SharePoint, Confluence, Slack, Google Drive). Mention custom knowledge base fine-tuning and the private deployment option for sensitive industries.

Question 11: Estimate the infrastructure cost for serving 10 million daily active users.

Framework: Work through the calculation: 10M DAU × 5 queries/day = 50M queries/day ≈ 580 average QPS, 5,800 burst QPS. LLM inference is the dominant cost — self-hosted 7B models on A100 GPUs need ~400 GPUs for burst capacity. Vector database (pgvector or Qdrant) needs GPU acceleration for the embedding search at this scale. Elasticsearch cluster for BM25 search, PostgreSQL for metadata, Redis for caching, and S3 for document storage. Total infrastructure cost: approximately $470K/month. Revenue needed to break even: approximately 500K Pro subscribers at $20/month.

Question 12: How do you handle multilingual queries?

Framework: Start with language detection on the query using fastText. For multilingual retrieval, use multilingual embedding models (e.g., multilingual-e5-large) that map queries and documents from different languages into the same vector space. The index stores documents in their original language with language metadata. For generation, the LLM must be multilingual or queries should be routed to language-specific models. Answer in the user's detected language. Cross-lingual retrieval is important — a Spanish query should find relevant English academic papers if no Spanish sources exist.

Ayodhyya — System Design Blog Series

Design a Perplexity-Style AI Search Engine: The Complete Guide — Senior+ Guide