🎉 75% of content is free forever — Unlock Premium from $10/mo →
CW
đŸ’ŧ Servicesâ„šī¸ Aboutâœ‰ī¸ ContactView Pricing Plansfrom $10

Deploying Agents to Production

AI AgentsAgent Deployment ProductionđŸŸĸ Free Lesson

Advertisement

Deploying Agents to Production

Production Deployment StackLoad BalancerNGINX / ALBFastAPIAsync WorkersAgent CoreBusiness LogicRedisCache / SessionsPrometheusMetricsAuto ScalerK8s HPACircuit BreakerFault ToleranceGrafanaDashboardsKubernetes ClusterMulti-AZ + Auto-Healing + Rolling Updates

What is Agent Deployment?

Agent deployment transforms development prototypes into production-ready services. It encompasses containerization, API serving, monitoring, scaling, security, and operational reliability. The goal is five-nines availability with automatic recovery.

The deployment stack typically includes: FastAPI for HTTP serving, Docker for containerization, Redis for caching and sessions, Prometheus/Grafana for monitoring, and Kubernetes or ECS for orchestration.

Why This Matters

A working prototype is not a production system. Production requires handling failures gracefully, scaling under load, monitoring for issues, and recovering from crashes automatically. Without proper deployment, agents fail at the worst possible moments — when users need them most.

Real-World Analogy

Deploying an agent to production is like opening a restaurant. Cooking a great meal (building the agent) is only part of the challenge. You also need a kitchen (infrastructure), waitstaff (API layer), a reservation system (load balancing), health inspections (monitoring), emergency protocols (circuit breakers), and the ability to handle a rush of customers (auto-scaling).

Project Overview

We will deploy an agent with:

  • FastAPI REST API with async support and health checks
  • Docker containerization with multi-stage builds
  • Redis caching and session management
  • Prometheus metrics and Grafana dashboards
  • Kubernetes auto-scaling configuration
  • Circuit breakers and graceful degradation
  • Structured logging and distributed tracing

Expected outcome: A production deployment template for any agent.

Difficulty: Advanced (requires understanding of DevOps, containerization, and production operations)

Tools & Setup

ToolVersionPurpose
Python3.11+Core language
FastAPI0.109+HTTP framework
uvicorn0.27+ASGI server
redis5.0+Caching
docker24.0+Containerization
prometheus-client0.19+Metrics

Step 1: FastAPI Application

# app/main.py
from fastapi import FastAPI, HTTPException, Request
from fastapi.middleware.cors import CORSMiddleware
from contextlib import asynccontextmanager
import redis.asyncio as redis
import logging
import time

logger = logging.getLogger(__name__)

redis_client = None


@asynccontextmanager
async def lifespan(app: FastAPI):
    global redis_client
    redis_client = redis.from_url(
        "redis://localhost:6379",
        decode_responses=True,
        max_connections=20,
    )
    logger.info("Connected to Redis")
    yield
    await redis_client.close()
    logger.info("Disconnected from Redis")


app = FastAPI(
    title="Agent API",
    version="1.0.0",
    lifespan=lifespan,
)

app.add_middleware(
    CORSMiddleware,
    allow_origins=["https://yourdomain.com"],
    allow_credentials=True,
    allow_methods=["GET", "POST"],
    allow_headers=["*"],
)


@app.get("/health")
async def health():
    return {"status": "healthy", "version": "1.0.0", "timestamp": time.time()}


@app.get("/ready")
async def ready():
    try:
        await redis_client.ping()
        return {"status": "ready", "redis": "connected"}
    except Exception:
        raise HTTPException(status_code=503, detail="Service not ready")

Step 2: Routes and Metrics

# app/routes.py
from fastapi import APIRouter, HTTPException
from pydantic import BaseModel, Field
from typing import Dict, Optional
import logging
import time

logger = logging.getLogger(__name__)


class QueryRequest(BaseModel):
    query: str = Field(..., min_length=1, max_length=5000)
    user_id: str = Field(default="anonymous", max_length=100)
    session_id: Optional[str] = None


class QueryResponse(BaseModel):
    answer: str
    request_id: str
    latency_ms: float
    tokens_used: int
    model: str


router = APIRouter()


@router.post("/query", response_model=QueryResponse)
async def query(request: QueryRequest):
    from app.agent import ProductionAgent
    agent = ProductionAgent()
    start = time.time()
    try:
        result = await agent.process(request.query, request.user_id)
        latency_ms = (time.time() - start) * 1000
        return QueryResponse(
            answer=result["answer"],
            request_id=result["request_id"],
            latency_ms=round(latency_ms, 2),
            tokens_used=result["tokens_used"],
            model=result["model"],
        )
    except Exception as e:
        logger.error("Query failed: %s", e)
        raise HTTPException(status_code=500, detail=str(e))


# app/metrics.py
from prometheus_client import Counter, Histogram, Gauge
import time
from functools import wraps
from typing import Callable

REQUEST_COUNT = Counter(
    "agent_requests_total",
    "Total requests",
    ["method", "endpoint", "status"],
)
REQUEST_LATENCY = Histogram(
    "agent_request_latency_seconds",
    "Request latency",
    ["endpoint"],
    buckets=[0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0],
)
TOKEN_USAGE = Counter(
    "agent_tokens_used_total",
    "Total tokens used",
    ["model", "type"],
)
ACTIVE_REQUESTS = Gauge(
    "agent_active_requests",
    "Currently active requests",
)


def track_request(func: Callable) -> Callable:
    @wraps(func)
    async def wrapper(*args, **kwargs):
        ACTIVE_REQUESTS.inc()
        start = time.time()
        try:
            result = await func(*args, **kwargs)
            REQUEST_COUNT.labels(method="POST", endpoint="/query", status="success").inc()
            return result
        except Exception as e:
            REQUEST_COUNT.labels(method="POST", endpoint="/query", status="error").inc()
            raise
        finally:
            latency = time.time() - start
            REQUEST_LATENCY.labels(endpoint="/query").observe(latency)
            ACTIVE_REQUESTS.dec()
    return wrapper

Step 3: Production Agent with Caching

# app/agent.py
from openai import AsyncOpenAI
import hashlib
import json
from typing import Dict, Any
import logging
import time

logger = logging.getLogger(__name__)


class ProductionAgent:
    """Production agent with Redis caching and async operations."""

    def __init__(self):
        self.client = AsyncOpenAI()
        self.model = "gpt-4o"

    async def process(self, query: str, user_id: str) -> Dict[str, Any]:
        cache_key = f"query:{hashlib.md5(query.encode()).hexdigest()}"

        cached = await self._get_cache(cache_key)
        if cached:
            logger.info("Cache hit for query: %s", query[:50])
            return cached

        start = time.time()
        response = await self.client.chat.completions.create(
            model=self.model,
            messages=[
                {"role": "system", "content": "You are a helpful assistant."},
                {"role": "user", "content": query},
            ],
            temperature=0.7,
            max_tokens=1000,
        )
        latency_ms = (time.time() - start) * 1000

        result = {
            "answer": response.choices[0].message.content,
            "request_id": f"req_{hashlib.md5(query.encode()).hexdigest()[:8]}",
            "latency_ms": round(latency_ms, 2),
            "tokens_used": response.usage.total_tokens,
            "model": self.model,
        }

        await self._set_cache(cache_key, result, ttl=300)
        return result

    async def _get_cache(self, key: str) -> Dict[str, Any] | None:
        try:
            from app.main import redis_client
            data = await redis_client.get(key)
            return json.loads(data) if data else None
        except Exception as e:
            logger.warning("Cache read failed: %s", e)
            return None

    async def _set_cache(self, key: str, value: Dict[str, Any], ttl: int = 300) -> None:
        try:
            from app.main import redis_client
            await redis_client.setex(key, ttl, json.dumps(value))
        except Exception as e:
            logger.warning("Cache write failed: %s", e)

Step 4: Docker and Kubernetes

# Dockerfile
FROM python:3.11-slim AS builder
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

FROM python:3.11-slim
WORKDIR /app
COPY --from=builder /usr/local/lib/python3.11/site-packages /usr/local/lib/python3.11/site-packages
COPY . .
EXPOSE 8000
HEALTHCHECK --interval=30s --timeout=5s --retries=3 CMD curl -f http://localhost:8000/health || exit 1
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000", "--workers", "4"]
# docker-compose.yml
version: '3.8'
services:
  api:
    build: .
    ports:
      - "8000:8000"
    environment:
      - REDIS_URL=redis://redis:6379
      - OPENAI_API_KEY=${OPENAI_API_KEY}
    depends_on:
      - redis
    deploy:
      replicas: 3
      resources:
        limits:
          memory: 512M
          cpus: "0.5"
  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"
    volumes:
      - redis_data:/data
  prometheus:
    image: prom/prometheus
    ports:
      - "9090:9090"
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml
  grafana:
    image: grafana/grafana
    ports:
      - "3000:3000"
    environment:
      - GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_PASSWORD}
volumes:
  redis_data:
# k8s/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: agent-api
spec:
  replicas: 3
  selector:
    matchLabels:
      app: agent-api
  template:
    metadata:
      labels:
        app: agent-api
    spec:
      containers:
      - name: agent-api
        image: agent-api:latest
        ports:
        - containerPort: 8000
        resources:
          requests:
            memory: "256Mi"
            cpu: "250m"
          limits:
            memory: "512Mi"
            cpu: "500m"
        livenessProbe:
          httpGet:
            path: /health
            port: 8000
          initialDelaySeconds: 10
          periodSeconds: 5
        readinessProbe:
          httpGet:
            path: /ready
            port: 8000
          initialDelaySeconds: 5
          periodSeconds: 3
---
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: agent-api-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: agent-api
  minReplicas: 2
  maxReplicas: 10
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 70

Why This Matters

The gap between a working prototype and a production system is enormous. Production systems need to handle failures, scale under load, maintain security, and provide observability. Without proper deployment, agents crash under real-world conditions.

Real-World Analogy

Production deployment is like building a highway system. A dirt road (prototype) works for one car, but a highway needs multiple lanes (scaling), traffic lights (load balancing), guard rails (circuit breakers), emergency services (health checks), and monitoring cameras (observability).

Mathematical Foundation

Capacity Planning:

Intuition: Number of pods needed to handle expected load. For 100 RPS at 200ms latency at 70% utilization: pods.

Auto-scaling Threshold:

Intuition: Scale up when CPU exceeds 120% of target to handle increasing load with headroom.

Performance Considerations

MetricValueNotes
Request Latency200ms-2sDepends on LLM
Throughput100+ RPSPer pod
Cache Hit Rate30-60%Depends on query patterns
Memory Usage256-512MBPer pod
Startup Time5-10sCold start
Health Check Interval30sConfigurable
Auto-scaling Delay60-90sTime to add new pods

Security Considerations

  • API Key Authentication: Require API keys for all non-health endpoints
  • Rate Limiting: Implement per-user and per-IP rate limits
  • TLS Encryption: Always use HTTPS in production
  • Secrets Management: Use Kubernetes secrets or Vault, never commit keys
  • CORS Restrictions: Allow only specific origins
  • Network Policies: Restrict pod-to-pod communication
  • Container Security: Use non-root users, scan images for vulnerabilities

Testing & Evaluation

import pytest
from httpx import AsyncClient
from app.main import app


@pytest.mark.asyncio
async def test_health():
    async with AsyncClient(app=app, base_url="http://test") as client:
        response = await client.get("/health")
        assert response.status_code == 200
        assert response.json()["status"] == "healthy"


@pytest.mark.asyncio
async def test_query():
    async with AsyncClient(app=app, base_url="http://test") as client:
        response = await client.post("/query", json={"query": "test"})
        assert response.status_code == 200

Interview Q&A

Q1: What is the purpose of health checks in production deployments? A: Liveness probes detect deadlocks and restart unhealthy containers. Readiness probes ensure traffic only reaches ready pods. Together they enable automatic recovery without manual intervention, achieving self-healing infrastructure.

Q2: How does Redis caching reduce LLM API costs? A: Redis caches identical or semantically similar query responses, avoiding redundant LLM API calls. For repeat queries, cache hits reduce latency from 2s to 10ms and eliminate token costs entirely.

Q3: What is the difference between horizontal and vertical scaling? A: Horizontal scaling adds more pods (scale out), improving fault tolerance and throughput. Vertical scaling increases resources per pod (scale up), improving single-instance performance. Horizontal is preferred for LLM agents due to stateless nature.

Q4: How do you handle cold starts in serverless deployments? A: Use provisioned concurrency to keep warm instances, implement connection pooling for databases, preload model weights, use container pre-warming, and design for graceful degradation during cold starts.

Q5: What is a circuit breaker and when should it be used? A: A circuit breaker stops calling failing services after a threshold, returning cached/fallback responses. It prevents cascade failures in microservice architectures. Use when external services (LLM APIs, databases) may fail.

Q6: How would you implement zero-downtime deployments? A: Use rolling updates with readiness probes, implement blue-green deployments, use canary releases for gradual traffic shifting, ensure graceful shutdown handling, and maintain backward compatibility.

Q7: What metrics should be monitored in production? A: Request latency (P50, P95, P99), error rate, throughput (RPS), token usage and cost, cache hit rate, queue depth, CPU/memory utilization, and external API latency.

Q8: How do you secure agent deployments? A: Use API key authentication, implement rate limiting per user/IP, encrypt data in transit (TLS) and at rest, use secrets management (Vault), enable CORS restrictions, and audit all requests.

Common Pitfalls & Solutions

PitfallImpactSolution
Cold start latencySlow initial responsesPre-warm containers, use provisioned concurrency
Memory leaksContainer crashesMonitor memory, implement restart policies, use leak detectors
Rate limitingService disruptionImplement per-user quotas and API key management
Cost overrunsBudget exceededSet spending limits, monitor token usage, implement caching
Single point of failureComplete outageMulti-AZ deployment, redundant components, circuit breakers
Secret exposureSecurity breachUse secrets management (Vault, K8s secrets), never commit keys
Configuration driftInconsistent environmentsUse infrastructure-as-code (Terraform, Helm)
No observabilityBlind to issuesImplement structured logging, metrics, and distributed tracing

Summary with Key Takeaways

  • FastAPI provides high-performance async HTTP serving for production agents
  • Docker ensures consistent environments across development and production
  • Redis caching reduces LLM costs and improves latency for repeat queries
  • Kubernetes auto-scaling handles traffic spikes with horizontal pod autoscalers
  • Comprehensive monitoring (Prometheus + Grafana) enables proactive issue detection
  • Health checks, circuit breakers, and graceful degradation ensure reliability
  • Zero-downtime deployments require rolling updates and readiness probes

Need Expert AI Agents Help?

Get personalized tutoring, project support, or professional consulting.

Advertisement