Study interactive :: Progress tools open in the Study Hub reader.

GenAI Production Deployment Guide

Deploying Generative AI applications to production at scale.

Table of Contents


Introduction

What is GenAI Production Deployment?

Production deployment of Generative AI involves:

Key Challenges

  1. High Latency: LLMs can be slow
  2. High Costs: GPU infrastructure is expensive
  3. Scalability: Need to handle variable load
  4. Model Management: Versioning and updates
  5. Context Management: Handling long contexts
  6. Rate Limiting: Preventing abuse

GenAI Architecture Patterns

1. RAG Architecture

Retrieval Augmented Generation combines retrieval and generation:

User Query → Embedding → Vector Search → Context Retrieval → LLM → Response

Production RAG Architecture

from langchain_community.vectorstores import Pinecone
from langchain_openai import OpenAIEmbeddings, ChatOpenAI
from langchain.chains import RetrievalQA

class ProductionRAGSystem:
    def __init__(self):
        # Vector store
        self.embeddings = OpenAIEmbeddings()
        self.vectorstore = Pinecone.from_existing_index(
            index_name="production-index",
            embedding=self.embeddings
        )
        
        # Chat LLM (temperature=0 lowers randomness; not a correctness guarantee)
        self.llm = ChatOpenAI(
            model="gpt-4o",
            temperature=0,
            max_tokens=1000,
        )
        
        # RAG chain
        self.qa_chain = RetrievalQA.from_chain_type(
            llm=self.llm,
            chain_type="stuff",
            retriever=self.vectorstore.as_retriever(
                search_kwargs={"k": 5}
            ),
            return_source_documents=True
        )
    
    def query(self, question: str):
        """Query with caching and error handling"""
        try:
            result = self.qa_chain({"query": question})
            return {
                "answer": result["result"],
                "sources": result["source_documents"]
            }
        except Exception as e:
            return {"error": str(e)}

2. Multi-Agent Architecture

Coordinated multi-agent systems:

from crewai import Agent, Task, Crew
from langgraph.graph import StateGraph

class MultiAgentSystem:
    def __init__(self):
        self.agents = self._create_agents()
        self.workflow = self._create_workflow()
    
    def _create_agents(self):
        return {
            "researcher": Agent(
                role="Researcher",
                goal="Research topics",
                backstory="Expert researcher"
            ),
            "writer": Agent(
                role="Writer",
                goal="Write content",
                backstory="Skilled writer"
            ),
            "reviewer": Agent(
                role="Reviewer",
                goal="Review content",
                backstory="Expert reviewer"
            )
        }
    
    def _create_workflow(self):
        workflow = StateGraph(State)
        workflow.add_node("research", self.research_node)
        workflow.add_node("write", self.write_node)
        workflow.add_node("review", self.review_node)
        workflow.set_entry_point("research")
        workflow.add_edge("research", "write")
        workflow.add_edge("write", "review")
        return workflow.compile()

3. Streaming Architecture

Stream responses for better UX:

from fastapi import FastAPI
from fastapi.responses import StreamingResponse
import asyncio

app = FastAPI()

@app.post("/chat/stream")
async def chat_stream(query: str):
    """Stream LLM responses"""
    async def generate():
        llm = OpenAI(streaming=True)
        async for chunk in llm.astream(query):
            yield f"data: {chunk}\n\n"
    
    return StreamingResponse(generate(), media_type="text/event-stream")

4. Caching Architecture

Cache responses to reduce costs:

from functools import lru_cache
import hashlib
import json

class GenAICache:
    def __init__(self, redis_client):
        self.redis = redis_client
        self.ttl = 3600  # 1 hour
    
    def _cache_key(self, query: str, model: str) -> str:
        """Generate cache key"""
        c>f"{query}:{model}"
        return hashlib.md5(content.encode()).hexdigest()
    
    def get(self, query: str, model: str):
        """Get from cache"""
        key = self._cache_key(query, model)
        cached = self.redis.get(key)
        if cached:
            return json.loads(cached)
        return None
    
    def set(self, query: str, model: str, response: dict):
        """Set in cache"""
        key = self._cache_key(query, model)
        self.redis.setex(
            key,
            self.ttl,
            json.dumps(response)
        )

# Usage
cache = GenAICache(redis_client)

def query_with_cache(query: str, model: str):
    # Check cache
    cached = cache.get(query, model)
    if cached:
        return cached
    
    # Query LLM
    resp>
    
    # Cache response
    cache.set(query, model, response)
    return response

Scaling Strategies

1. Horizontal Scaling

Scale out with multiple instances:

# Load balancer configuration
# nginx.conf
upstream genai_backend {
    least_conn;  # Use least connections
    server app1:8000;
    server app2:8000;
    server app3:8000;
}

server {
    listen 80;
    location / {
        proxy_pass http://genai_backend;
        proxy_set_header Host $host;
    }
}

2. Model Parallelism

Split models across GPUs:

import torch
from torch.nn.parallel import DataParallel

class ParallelLLM:
    def __init__(self, model, device_ids=[0, 1, 2, 3]):
        self.model = DataParallel(model, device_ids=device_ids)
    
    def generate(self, input_ids):
        return self.model.generate(input_ids)

3. Batch Processing

Process multiple requests together:

from queue import Queue
import threading

class BatchProcessor:
    def __init__(self, batch_size=32, timeout=0.1):
        self.batch_size = batch_size
        self.timeout = timeout
        self.queue = Queue()
        self.results = {}
    
    def add_request(self, request_id: str, input_data: dict):
        """Add request to batch"""
        self.queue.put((request_id, input_data))
    
    def process_batch(self):
        """Process batch of requests"""
        batch = []
        while len(batch) < self.batch_size:
            try:
                item = self.queue.get(timeout=self.timeout)
                batch.append(item)
            except:
                break
        
        if batch:
            # Process batch
            inputs = [item[1] for item in batch]
            outputs = self.model.generate_batch(inputs)
            
            # Store results
            for (request_id, _), output in zip(batch, outputs):
                self.results[request_id] = output
    
    def get_result(self, request_id: str):
        """Get result for request"""
        return self.results.pop(request_id, None)

4. Async Processing

Process requests asynchronously:

from fastapi import FastAPI, BackgroundTasks
import asyncio

app = FastAPI()

@app.post("/generate/async")
async def generate_async(prompt: str, background_tasks: BackgroundTasks):
    """Async generation"""
    task_id = str(uuid.uuid4())
    
    # Start async task
    background_tasks.add_task(
        process_generation,
        task_id,
        prompt
    )
    
    return {"task_id": task_id, "status": "processing"}

async def process_generation(task_id: str, prompt: str):
    """Process generation in background"""
    result = await llm.agenerate(prompt)
    # Store result
    store_result(task_id, result)

Hyperscaler Deployment

AWS Deployment

SageMaker Endpoints

import sagemaker
from sagemaker.huggingface import HuggingFaceModel

# Deploy model
huggingface_model = HuggingFaceModel(
    model_data="s3://bucket/model.tar.gz",
    role=sagemaker.get_execution_role(),
    transformers_version="4.26",
    pytorch_version="1.13",
    py_version="py39"
)

predictor = huggingface_model.deploy(
    initial_instance_count=1,
    instance_type="ml.g4dn.xlarge"
)

# Invoke endpoint
resp>
    "inputs": "What is AI?"
})

Lambda for GenAI

# lambda_function.py
import json
import boto3

bedrock = boto3.client('bedrock-runtime')

def lambda_handler(event, context):
    prompt = event['prompt']
    
    resp>
        modelId='anthropic.claude-v2',
        body=json.dumps({
            "prompt": prompt,
            "max_tokens_to_sample": 1000
        })
    )
    
    result = json.loads(response['body'].read())
    return {
        'statusCode': 200,
        'body': json.dumps(result)
    }

Google Cloud Deployment

Vertex AI

from google.cloud import aiplatform

# Deploy model
endpoint = aiplatform.Endpoint.create(
    display_name="genai-endpoint"
)

model = aiplatform.Model.upload(
    display_name="genai-model",
    artifact_uri="gs://bucket/model"
)

model.deploy(
    endpoint=endpoint,
    machine_type="n1-standard-4",
    min_replica_count=1,
    max_replica_count=10
)

# Predict
resp>instances=[{"prompt": "What is AI?"}])

Cloud Run

# Dockerfile
FROM python:3.9
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8080"]

# Deploy
gcloud run deploy genai-service \
    --source . \
    --platform managed \
    --region us-central1 \
    --memory 4Gi \
    --cpu 2

Azure Deployment

Azure ML

from azure.ai.ml import MLClient
from azure.ai.ml.entities import Model, ManagedOnlineEndpoint

# Deploy model
endpoint = ManagedOnlineEndpoint(
    name="genai-endpoint",
    auth_mode="key"
)

ml_client.online_endpoints.begin_create_or_update(endpoint)

# Deploy to endpoint
deployment = ManagedOnlineDeployment(
    name="genai-deployment",
    endpoint_name="genai-endpoint",
    model=model,
    instance_type="Standard_NC6s_v3",
    instance_count=1
)

ml_client.online_deployments.begin_create_or_update(deployment)

Production Best Practices

1. Error Handling

from fastapi import FastAPI, HTTPException
import logging

app = FastAPI()
logger = logging.getLogger(__name__)

@app.post("/generate")
async def generate(prompt: str):
    try:
        result = await llm.generate(prompt)
        return {"result": result}
    except Exception as e:
        logger.error(f"Generation error: {e}")
        raise HTTPException(
            status_code=500,
            detail="Generation failed"
        )

2. Rate Limiting

from slowapi import Limiter, _rate_limit_exceeded_handler
from slowapi.util import get_remote_address

limiter = Limiter(key_func=get_remote_address)
app.state.limiter = limiter
app.add_exception_handler(RateLimitExceeded, _rate_limit_exceeded_handler)

@app.post("/generate")
@limiter.limit("10/minute")
async def generate(request: Request, prompt: str):
    return await llm.generate(prompt)

3. Input Validation

from pydantic import BaseModel, validator

class GenerationRequest(BaseModel):
    prompt: str
    max_tokens: int = 1000
    temperature: float = 0.7
    
    @validator('prompt')
    def validate_prompt(cls, v):
        if len(v) > 10000:
            raise ValueError('Prompt too long')
        if not v.strip():
            raise ValueError('Prompt cannot be empty')
        return v
    
    @validator('temperature')
    def validate_temperature(cls, v):
        if not 0 <= v <= 2:
            raise ValueError('Temperature must be between 0 and 2')
        return v

4. Health Checks

@app.get("/health")
async def health_check():
    """Health check endpoint"""
    try:
        # Check LLM availability
        test_resp>await llm.generate("test", max_tokens=1)
        return {
            "status": "healthy",
            "llm": "available"
        }
    except Exception as e:
        return {
            "status": "unhealthy",
            "error": str(e)
        }, 503

Monitoring and Observability

1. Logging

import logging
from datetime import datetime

logger = logging.getLogger(__name__)

def log_generation(prompt: str, response: str, latency: float):
    """Log generation request"""
    logger.info({
        "event": "generation",
        "prompt_length": len(prompt),
        "response_length": len(response),
        "latency_ms": latency * 1000,
        "timestamp": datetime.utcnow().isoformat()
    })

2. Metrics

from prometheus_client import Counter, Histogram, Gauge

# Metrics
generati>
    'genai_requests_total',
    'Total generation requests'
)

generati>
    'genai_latency_seconds',
    'Generation latency'
)

active_c>
    'genai_connections_active',
    'Active connections'
)

@app.post("/generate")
async def generate(prompt: str):
    generation_requests.inc()
    active_connections.inc()
    
    with generation_latency.time():
        result = await llm.generate(prompt)
    
    active_connections.dec()
    return result

3. Tracing

from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider

tracer = trace.get_tracer(__name__)

@app.post("/generate")
async def generate(prompt: str):
    with tracer.start_as_current_span("generate") as span:
        span.set_attribute("prompt_length", len(prompt))
        result = await llm.generate(prompt)
        span.set_attribute("response_length", len(result))
        return result

Cost Optimization

1. Model Selection

2. Caching

3. Batching

4. Auto-Scaling

# Auto-scaling configuration
# Scale based on queue depth
if queue_depth > threshold:
    scale_up()
elif queue_depth < low_threshold:
    scale_down()

Security and Compliance

1. API Keys

from fastapi import Security, HTTPException
from fastapi.security import APIKeyHeader

api_key_header = APIKeyHeader(name="X-API-Key")

async def verify_api_key(api_key: str = Security(api_key_header)):
    if api_key != valid_api_key:
        raise HTTPException(status_code=403, detail="Invalid API key")
    return api_key

2. Input Sanitization

import re

def sanitize_input(text: str) -> str:
    """Sanitize user input"""
    # Remove potentially harmful patterns
    text = re.sub(r'<script.*?>.*?</script>', '', text, flags=re.DOTALL)
    # Limit length
    text = text[:10000]
    return text

3. Data Privacy


Resources

Official Documentation

Tools

Best Practices

  1. Start Small: Deploy simple version first
  2. Monitor Early: Add monitoring from day one
  3. Test Thoroughly: Load test before production
  4. Document Everything: Keep deployment docs updated
  5. Plan for Scale: Design for growth from start

Try next: Add latency, cost-per-query, and failure logging to one GenAI endpoint before you add features.