Fun with Learning Technology
LearnCoursesQuestionsTracksToolsNewsExplorePractice
Fun with Learning Technology

A new problem, explained clearly, every day.

Subscribe
Learn
  • Lessons
  • Topics
  • News
  • Tools
  • Courses
  • Career tracks
  • Everything
Site
  • About
  • Contact
  • Support
  • Privacy
  • Terms
Get the daily one

One email per new problem. No spam.

Request a tutorial

Requests shape what gets made next.

© 2026 Fun with Learning TechnologyRSS
Home›Courses›Generative AI›GenAI System Design

Interview Prep

GenAI System Design

GenAI system design involves architecting scalable pipelines that integrate large models with external data, computation, and user interfaces to solve complex, non-deterministic problems. It matters because high-quality outputs depend not just on the model's weights, but on the robustness of the data retrieval, prompt construction, and feedback mechanisms surrounding it. You reach for these architectural patterns whenever a problem requires handling unstructured data, generating context-aware content, or automating workflows that were previously deemed impossible to define via static, rule-based logic.

The Core Foundation: Prompt Engineering and Template Management

Designing a GenAI system starts with the prompt, which acts as the 'instructions' for the model. The key to scalability is separating the prompt logic from the application code. By using templating, you can iterate on the model's instructions without redeploying your entire backend infrastructure. This allows for rapid experimentation with different personas, constraints, and output formats. When designing these systems, you must consider how input variables are safely injected to prevent prompt injection vulnerabilities, where malicious users attempt to override your system instructions. Furthermore, managing these templates in a version-controlled repository allows for A/B testing different instruction strategies against specific datasets, ensuring that as you tune the prompt, you are not inadvertently degrading performance on edge cases. This modular approach is essential because prompt engineering is inherently iterative and empirical rather than deterministic.

# Example of structured prompt management using string templating

def generate_system_prompt(user_intent: str, persona: str = "helpful assistant") -> str:
    # Using a template ensures consistent behavior and allows for easy updates
    template = "You are a {persona}. Your task is to address the following: {intent}."
    return template.format(persona=persona, intent=user_intent)

# Usage in an application flow
print(generate_system_prompt("Explain quantum computing"))

Retrieval Augmented Generation (RAG) Architecture

Most LLMs have a fixed knowledge cutoff, making them insufficient for enterprise applications that rely on real-time or private data. Retrieval Augmented Generation (RAG) bridges this gap by injecting relevant context into the prompt at runtime. The architecture involves three main stages: ingestion, retrieval, and synthesis. First, your private documents are broken into smaller chunks and converted into numerical vectors that represent semantic meaning. These are stored in a vector database for efficient similarity searching. When a user asks a question, you perform a nearest-neighbor search in this database to find the most relevant chunks of data. You then prepend this data to the user's prompt as context. This works because the model can now synthesize an answer based on specific evidence rather than just its pre-trained knowledge, drastically reducing hallucinations and providing a traceable source for the output.

# Simulated retrieval process for a RAG system
import math

def get_similarity(vec_a, vec_b):
    # Calculate cosine similarity between query and document vectors
    dot_product = sum(a * b for a, b in zip(vec_a, vec_b))
    return dot_product # Simplified for demonstration

# Mock retrieval logic
knowledge_base = [([0.1, 0.2], "Company policy on remote work."), ([0.9, 0.8], "How to submit expenses.")]
query_vector = [0.8, 0.7]

# Find the best match
relevant_doc = max(knowledge_base, key=lambda x: get_similarity(query_vector, x[0]))
print(f"Retrieved Context: {relevant_doc[1]}")

Managing State and Conversation History

GenAI models are stateless, meaning they do not 'remember' previous interactions. To create a multi-turn conversational experience, you must manage the state of the conversation by maintaining a session buffer. This involves storing recent interactions in a cache or database and including them in every subsequent request to the model. A critical design decision is the length of this 'context window.' Including too much history consumes expensive token quota and may distract the model, while including too little causes the AI to lose the thread of the discussion. Advanced systems implement summarization modules that condense older interactions into a concise history, ensuring that the model has the 'gist' of the conversation without hitting token limits. Balancing memory depth, latency, and costs is a fundamental architectural trade-off that differentiates a robust conversational application from a simple one-off request system.

# Maintaining state for a conversational loop
from collections import deque

class ConversationManager:
    def __init__(self, max_turns=3):
        self.history = deque(maxlen=max_turns)

    def add_turn(self, user_msg, ai_msg):
        self.history.append({"user": user_msg, "ai": ai_msg})

    def get_formatted_context(self):
        # Prepare history as a formatted block for the prompt
        return "\n".join([f"U: {h['user']} A: {h['ai']}" for h in self.history])

# Example usage
chat = ConversationManager()
chat.add_turn("Hi", "Hello!")
print(chat.get_formatted_context())

Implementing Guardrails and Input/Output Validation

Since GenAI models produce probabilistic, non-deterministic outputs, you cannot rely on traditional unit tests to guarantee safety or correctness. Instead, you must implement a layer of 'guardrails' that sit between the user and the model. This layer performs two critical functions: input sanitization and output verification. For inputs, you detect and block malicious content or prompt injection attempts before they reach the model. For outputs, you scan the generated text for policy violations, such as offensive language, data leakage, or factual inconsistencies. If the model produces an output that fails these checks, your system should be designed to either redact the harmful segments, request a regeneration from the model with more constrained instructions, or return a predefined fallback response to the user. This design pattern treats the LLM as an 'untrusted' component, ensuring system reliability through defensive engineering and automated inspection.

# Simple regex-based guardrail for filtering content
import re

def validate_output(text: str) -> bool:
    # Define prohibited patterns
    forbidden_patterns = [r"[Pp]assword", r"[Cc]redit [Cc]ard"]
    for pattern in forbidden_patterns:
        if re.search(pattern, text):
            return False # Reject output if sensitive data found
    return True

# Example usage
generated_text = "My password is secret123"
if not validate_output(generated_text):
    print("Validation Failed: Sensitive info detected.")

Latency Management and Asynchronous Orchestration

GenAI systems are computationally expensive and often involve significant latency as the model tokenizes, processes, and streams the response. Building a responsive user interface requires moving away from traditional synchronous request-response patterns. The standard approach is to use streaming, where the model output is sent back to the client token-by-token. This minimizes the perceived waiting time for the user. Behind the scenes, complex tasks involving multiple model calls should be orchestrated asynchronously using a task queue. This prevents your backend from blocking and allows you to retry failed generations without failing the entire user request. By separating the orchestration layer from the execution layer, you can scale the number of workers independently based on throughput needs. Ultimately, you must treat your GenAI service as a distributed system, prioritizing non-blocking I/O and resilient error handling to manage the inherent variability of AI performance.

# Simulating streaming response architecture
import time

def stream_response(content: str):
    # Breaking content into chunks to mimic streaming
    for word in content.split():
        time.sleep(0.1) # Simulate generation delay
        yield word + " "

# Usage in a simple loop
for chunk in stream_response("The quick brown fox jumps over the lazy dog."):
    print(chunk, end="", flush=True)

Key points

  • GenAI systems must separate prompt logic from core code to allow for rapid, decoupled iteration.
  • RAG architectures address the model's knowledge cutoff by retrieving relevant external data at runtime.
  • Conversational memory requires a balanced approach to context windows to manage token usage and focus.
  • Guardrails are essential for verifying outputs because model behavior is non-deterministic and potentially unreliable.
  • Streaming responses are critical for managing user-perceived latency in high-latency generative pipelines.
  • Asynchronous orchestration prevents bottlenecking when handling multi-step agentic workflows or expensive generations.
  • Vector databases serve as the backbone of retrieval, turning semantic meaning into searchable data structures.
  • Design for failure by treating AI models as untrusted components that require rigorous input/output validation.

Common mistakes

  • Mistake: Relying solely on RAG for grounding. Why it's wrong: RAG does not fix underlying model hallucinations if the context retrieved is noisy or contradictory. Fix: Implement rigorous data cleaning and a verification loop to validate retrieved chunks before generation.
  • Mistake: Overlooking latency costs of long-context windows. Why it's wrong: Just because a model supports a large context doesn't mean it is efficient or cheap to fill it with every request. Fix: Use hybrid retrieval methods to pass only high-relevance tokens rather than full documents.
  • Mistake: Treating prompt engineering as a permanent fix for model behavior. Why it's wrong: Prompts are fragile and sensitive to slight changes in input distribution. Fix: Use prompt engineering for prototyping, but switch to fine-tuning or system-level constraints for production reliability.
  • Mistake: Neglecting guardrails for input and output. Why it's wrong: Assuming the model will naturally adhere to safety policies without explicit intervention leads to prompt injection vulnerabilities. Fix: Deploy separate, specialized models to scan inputs and filter outputs for safety compliance.
  • Mistake: Using a 'one-size-fits-all' model architecture. Why it's wrong: Deploying a massive general-purpose model for simple tasks is overkill and expensive. Fix: Route queries to smaller, specialized models for common tasks and reserve larger models only for complex reasoning.

Interview questions

What is the fundamental difference between a standard retrieval-augmented generation (RAG) pipeline and a simple prompt-based generative model?

A simple prompt-based generative model relies entirely on the internal parameters learned during pre-training to generate responses, which makes it prone to hallucinations and outdated information. In contrast, RAG introduces an external knowledge retrieval step. By querying a vector database for relevant context before sending the prompt to the generative model, we provide grounded data. This improves factual accuracy, allows for domain-specific knowledge integration, and reduces the need for constant model fine-tuning, which is essential for enterprise systems.

How do you handle context window limitations when designing a system that needs to process extremely long documents?

When dealing with inputs that exceed the context window, I implement a chunking strategy combined with semantic retrieval. I break documents into smaller, overlapping segments using a sliding window or recursive splitter, then embed these chunks into a vector database. During inference, I perform similarity searches to retrieve only the most relevant chunks that fit within the token budget. This 'top-k' approach ensures the generative model receives high-quality, concise context without hitting context limits, maintaining coherence throughout the generated output.

Compare the trade-offs between RAG and fine-tuning for adapting a model to a proprietary dataset.

RAG is superior when you require high traceability and the ability to update data in real-time, as you simply refresh the vector index. It minimizes hallucinations because the model references explicit text. Fine-tuning, however, is better for changing the model's style, format, or learning specific domain jargon that is difficult to convey via prompts. The main trade-off is that fine-tuning is static and expensive to update, whereas RAG is dynamic and cheaper, though it requires a well-optimized retrieval pipeline to function effectively.

What strategies would you use to evaluate the quality of a generative system in production?

Evaluation requires a multi-layered approach. I use RAGAS metrics like Faithfulness and Answer Relevancy to programmatically assess if the output is grounded in retrieved context. I also implement a 'LLM-as-a-judge' pattern, where a more capable model evaluates the output of the production model against a rubric. Furthermore, I track latency and token usage per request, and maintain a 'golden dataset' of human-verified query-response pairs to run regression tests whenever I update the embedding model or system prompt.

How do you optimize for latency in a high-traffic generative AI system?

To reduce latency, I first optimize the retrieval path by caching frequently asked questions in a Redis layer to bypass the vector database search entirely. I also utilize speculative decoding, where a smaller, faster model drafts the initial response, and the larger model verifies it. Additionally, I enforce streaming responses, so the user sees text immediately rather than waiting for the entire generation. On the model side, I use quantization techniques like 4-bit loading to decrease memory footprint and speed up inference times.

Design an architecture for a multi-agent generative system capable of performing complex multi-step reasoning tasks.

For a multi-agent system, I define specialized agents with distinct system prompts and tools. I implement a ReAct (Reasoning and Acting) loop where a 'planner' agent breaks a complex query into sub-tasks. For example: `thought: I need to query the database, then summarize the results.` The planner calls specific tools for each step, passing output to a 'critic' agent that verifies safety and accuracy before the final response is generated. This orchestration layer requires a robust task queue to manage state transitions between agents, ensuring that data flow remains consistent and errors in one branch are caught before final synthesis.

All Generative AI interview questions →

Check yourself

1. When designing a system that requires high-fidelity adherence to internal documentation, why is fine-tuning generally inferior to RAG?

  • A.Fine-tuning is always more expensive than retrieving documents in real-time.
  • B.Fine-tuning is better at learning new formats, but RAG provides better provenance and handles rapidly changing knowledge better.
  • C.RAG allows the model to rewrite its own weights, which is safer.
  • D.Fine-tuning creates more hallucinations than RAG in every scenario.
Show answer

B. Fine-tuning is better at learning new formats, but RAG provides better provenance and handles rapidly changing knowledge better.
RAG is better for dynamic knowledge because fine-tuning weights cannot be easily updated with new data, whereas RAG fetches fresh data. Option 0 is false as fine-tuning can sometimes be cheaper at scale. Option 2 is false as RAG does not alter model weights. Option 3 is false as both can hallucinate, but RAG provides traceable sources.

2. Which architectural component is most effective at preventing prompt injection attacks in a production Generative AI system?

  • A.Increasing the temperature parameter to add randomness.
  • B.Adding 'ignore previous instructions' at the beginning of every prompt.
  • C.Implementing a dedicated validation layer that uses a separate model to audit both user input and system output.
  • D.Using a larger model that is more intelligent and naturally resistant to deception.
Show answer

C. Implementing a dedicated validation layer that uses a separate model to audit both user input and system output.
Dedicated validation layers act as a firewall. Option 0 increases instability, option 1 is susceptible to injection itself, and option 3 is insufficient because even intelligent models can be manipulated by sophisticated adversarial prompts.

3. In a RAG-based design, what is the primary risk of using an overly large retrieval chunk size?

  • A.The model will run out of memory during the embedding process.
  • B.The retriever will be unable to find the document in the vector database.
  • C.The retrieved context may contain too much irrelevant noise, leading to 'lost in the middle' phenomena or diluted focus.
  • D.The system will become too fast, preventing the model from processing the information thoroughly.
Show answer

C. The retrieved context may contain too much irrelevant noise, leading to 'lost in the middle' phenomena or diluted focus.
Large chunks dilute the signal and can overwhelm the model's attention mechanism. Option 0 is false as embedding handles chunks easily. Option 1 is false as chunk size doesn't prevent retrieval. Option 3 is incorrect because speed is generally desirable.

4. Why is it important to design for 'human-in-the-loop' (HITL) in an automated Generative AI workflow?

  • A.Because models require constant monitoring to prevent them from becoming self-aware.
  • B.Because deterministic logic handles edge cases poorly, and HITL provides a way to handle system failure or uncertainty.
  • C.Because models cannot process data without a human verifying each token.
  • D.To ensure that the system follows the rules of basic arithmetic, which models cannot do.
Show answer

B. Because deterministic logic handles edge cases poorly, and HITL provides a way to handle system failure or uncertainty.
Models operate on probabilities, making them prone to silent errors; HITL provides a safety net. Option 0 is a myth. Option 2 is false as models are fully autonomous. Option 3 is false as some models are quite capable of arithmetic.

5. When building an agentic system that calls external tools, what is the most critical design pattern to ensure robustness?

  • A.Giving the agent access to the entire file system to ensure no data is missed.
  • B.Implementing strict output parsing and schema validation for the agent's tool-call arguments.
  • C.Allowing the agent to execute code indefinitely until it finds an answer.
  • D.Hardcoding the sequence of tools so the agent never has to decide which to use.
Show answer

B. Implementing strict output parsing and schema validation for the agent's tool-call arguments.
Agent output is non-deterministic, so enforcing a strict schema (like JSON) is vital to ensure the tool receives valid inputs. Option 0 is a security risk. Option 2 is a performance risk. Option 3 defeats the purpose of an agent's reasoning capabilities.

Take the full Generative AI quiz →

← PreviousGenAI Interview Questions — RAG and Agents

Generative AI

41 lessons, free to read.

All lessons →

Track your progress

Sign in to mark lessons done, score quizzes and keep notes.

Open in the app