Module 8: Capstone Project — Deployed AI System

8. Final Project: Deployed Production AI System

Project description

This is the integrative project for the whole guide — not just Module 8, but all 8 complete modules. You'll integrate everything you built and deliver two artifacts: an AI system deployed in real production (with a URL accessible from the internet) and professional operational documentation. It's not an exercise — it's a real deployment. The result is the most portfolio-worthy artifact of the whole guide.

Why it matters: This project proves you don't just know how to deploy — you know how to decide how to deploy, document your decisions, validate that it works, and operate the system when something fails. It's exactly what an employer wants to see: an engineer who can take a system from zero to production with professional judgment.


Project goal

Produce a Deployed Production AI System that includes:

  1. An AI system deployed on a real platform, accessible from the internet
  2. A CI/CD pipeline that deploys automatically from git push
  3. Health checks and smoke tests that validate inference post-deploy
  4. A decision matrix v_final with justification of strategy and platform
  5. An operational runbook with procedures for common incidents
  6. A performance baseline with latency, cost, and error rate targets
  7. A deployment guide that another engineer can follow to replicate the setup

Module recap

Before you start, make sure you have the artifacts from each capsule:

CapsuleArtifactYou use it for
02Integration diagramUnderstand how the pieces connect
03GitHub Actions workflowAutomate test → build → deploy → validate
04Smoke test scriptValidate inference post-deploy
05Runbook templateDocument incident procedures
06Decision matrix v_finalJustify the strategy and platform
07Performance baselineEstablish reference metrics

If you're missing any, go back to the corresponding capsule and complete it. The project integrates ALL of these artifacts.


Deliverable Specifications

Deliverable 1: Deployed System (50%)

MANDATORY REQUIREMENTS:
├── ✅ Public URL accessible from any browser
│   Example: https://your-app.railway.app
│
├── ✅ Health check endpoint
│   GET /health → {"status": "healthy", "version": "1.0.0"}
│
├── ✅ Readiness check with dependency verification
│   GET /health/ready → {"status": "ready", "checks": {...}}
│
├── ✅ Working AI inference endpoint
│   POST /api/inference → LLM response
│
├── ✅ CI/CD: git push to main → automatic deploy
│   GitHub Actions workflow with test + build + deploy + validate
│
├── ✅ Environment variables configured (not hardcoded)
│   OPENAI_API_KEY, ENVIRONMENT, etc. in platform secrets
│
└── ✅ Working Docker image
    Multi-stage Dockerfile, .dockerignore configured

Deliverable 2: Operational Documentation (50%)

MANDATORY REQUIREMENTS:
├── ✅ Decision Matrix v_final (docs/decision-matrix.md)
│   Strategy + platform + justification + trade-offs
│
├── ✅ Operational Runbook (docs/runbook.md)
│   At least 4 procedures with diagnosis and resolution
│
├── ✅ Performance Baseline (docs/performance-baseline.md)
│   Latency, costs, error rate, uptime with targets
│
├── ✅ Deployment Guide (docs/deployment-guide.md)
│   Instructions to replicate the setup from scratch
│
└── ✅ Post-deploy validation report
    Result of smoke tests against production

Case Study (if you don't have your own AI app)

DocuSearch AI — The same case from the whole guide

If you don't have your own AI app, use the DocuSearch AI case you've been developing. Here's the minimum code needed:

# src/main.py
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel, field_validator
from openai import AsyncOpenAI
from src.config import get_settings
import time

app = FastAPI(title="DocuSearch AI", version="1.0.0")
settings = get_settings()
client = AsyncOpenAI(api_key=settings.openai_api_key)

class InferenceRequest(BaseModel):
    prompt: str

    @field_validator("prompt")
    @classmethod
    def prompt_not_empty(cls, v):
        if not v or not v.strip():
            raise ValueError("Prompt cannot be empty")
        return v.strip()

class InferenceResponse(BaseModel):
    response: str
    model: str
    latency_ms: int

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

@app.get("/health/ready")
async def readiness():
    checks = {}
    try:
        await client.models.list()
        checks["openai"] = {"status": "connected"}
    except Exception as e:
        checks["openai"] = {"status": "error", "detail": str(e)[:100]}

    all_ok = all(c.get("status") == "connected" for c in checks.values())
    return {"status": "ready" if all_ok else "degraded", "checks": checks}

@app.post("/api/inference", response_model=InferenceResponse)
async def inference(request: InferenceRequest):
    start = time.time()
    try:
        completion = await client.chat.completions.create(
            model=settings.openai_model,
            messages=[
                {"role": "system", "content": "You are a helpful documentation assistant. Answer concisely."},
                {"role": "user", "content": request.prompt},
            ],
            max_tokens=500,
            temperature=0.7,
        )
        elapsed_ms = int((time.time() - start) * 1000)
        return InferenceResponse(
            response=completion.choices[0].message.content,
            model=settings.openai_model,
            latency_ms=elapsed_ms,
        )
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"Inference failed: {str(e)[:200]}")
# src/config.py
from pydantic_settings import BaseSettings
from functools import lru_cache

class Settings(BaseSettings):
    environment: str = "development"
    openai_api_key: str = ""
    openai_model: str = "gpt-4o-mini"
    debug: bool = False
    version: str = "1.0.0"

    class Config:
        env_file = ".env"

@lru_cache()
def get_settings() -> Settings:
    return Settings()
# Dockerfile
FROM python:3.11-slim AS builder
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir --user -r requirements.txt
COPY src/ ./src/

FROM python:3.11-slim AS runtime
WORKDIR /app
RUN apt-get update && apt-get install -y --no-install-recommends curl && rm -rf /var/lib/apt/lists/*
COPY --from=builder /root/.local /root/.local
COPY --from=builder /app/src ./src/
ENV PATH=/root/.local/bin:$PATH
ENV PYTHONUNBUFFERED=1
EXPOSE 8000
HEALTHCHECK --interval=30s --timeout=10s --retries=3 \
    CMD curl -f http://localhost:8000/health || exit 1
CMD ["uvicorn", "src.main:app", "--host", "0.0.0.0", "--port", "8000"]
# requirements.txt
fastapi>=0.109.0
uvicorn>=0.27.0
openai>=1.12.0
pydantic>=2.6.0
pydantic-settings>=2.1.0

Step by Step: The Deployment

Step 1: Verify that everything works locally (15 min)

# Build Docker image
docker build -t docusearch-ai:latest .

# Run locally
docker run -d --name local-test \
    -p 8000:8000 \
    -e OPENAI_API_KEY=$OPENAI_API_KEY \
    -e ENVIRONMENT=development \
    docusearch-ai:latest

# Verify health
curl http://localhost:8000/health
# {"status":"healthy","version":"1.0.0","environment":"development"}

# Verify readiness
curl http://localhost:8000/health/ready
# {"status":"ready","checks":{"openai":{"status":"connected"}}}

# Verify inference
curl -X POST http://localhost:8000/api/inference \
    -H "Content-Type: application/json" \
    -d '{"prompt": "What is Docker?"}'
# {"response":"Docker is a platform for...","model":"gpt-4o-mini","latency_ms":1856}

# Cleanup
docker stop local-test && docker rm local-test

If something fails here, do NOT continue. Fix the local problem before trying to deploy.

Step 2: Configure the platform (10-15 min)

Railway:

# Install the CLI
npm install -g @railway/cli

# Login and create a project
railway login
railway init

# Configure environment variables
railway variables set OPENAI_API_KEY=sk-...
railway variables set ENVIRONMENT=production

# Deploy
railway up

Render:

1. render.com → New → Web Service
2. Connect GitHub repo
3. Settings:
   - Environment: Docker
   - Plan: Free
   - Health Check Path: /health
4. Environment Variables:
   - OPENAI_API_KEY: sk-...
   - ENVIRONMENT: production
5. Deploy

Fly.io:

# Install the CLI
curl -L https://fly.io/install.sh | sh

# Login and create the app
fly auth login
fly launch --name docusearch-ai

# Configure secrets
fly secrets set OPENAI_API_KEY=sk-...
fly secrets set ENVIRONMENT=production

# Deploy
fly deploy

Step 3: Verify the deployment (5 min)

# Replace with your real URL
export PRODUCTION_URL="https://your-app.railway.app"

# Health check
curl $PRODUCTION_URL/health

# Readiness
curl $PRODUCTION_URL/health/ready

# Inference
curl -X POST $PRODUCTION_URL/api/inference \
    -H "Content-Type: application/json" \
    -d '{"prompt": "What is deployment?"}'

# Complete smoke tests
python scripts/smoke_test.py $PRODUCTION_URL

Step 4: Configure CI/CD (15 min)

# .github/workflows/deploy.yml
name: Deploy AI System

on:
  push:
    branches: [main]
  workflow_dispatch:

concurrency:
  group: deploy-${{ github.ref }}
  cancel-in-progress: true

jobs:
  test:
    runs-on: ubuntu-latest
    timeout-minutes: 10
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: "3.11"
          cache: 'pip'
      - run: pip install -r requirements.txt && pip install pytest httpx
      - run: pytest tests/ -v --tb=short
        env:
          OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
          ENVIRONMENT: test

  deploy:
    if: github.ref == 'refs/heads/main'
    needs: test
    runs-on: ubuntu-latest
    timeout-minutes: 10
    environment: production
    steps:
      - uses: actions/checkout@v4
      # ADAPT TO YOUR PLATFORM:
      # Railway:
      - run: npm install -g @railway/cli
      - run: railway up --detach --service ${{ vars.RAILWAY_SERVICE_ID }}
        env:
          RAILWAY_TOKEN: ${{ secrets.RAILWAY_TOKEN }}

  validate:
    needs: deploy
    runs-on: ubuntu-latest
    timeout-minutes: 5
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: "3.11"
      - name: Wait for deployment
        run: sleep 60
      - name: Run smoke tests
        run: python scripts/smoke_test.py "${{ vars.PRODUCTION_URL }}"

Configure in GitHub:

  • Secrets: OPENAI_API_KEY, RAILWAY_TOKEN (or your platform's)
  • Variables: PRODUCTION_URL, RAILWAY_SERVICE_ID

Step 5: Verify CI/CD end-to-end (10 min)

# Make a minor change and push
echo "# Updated $(date)" >> README.md
git add -A
git commit -m "test: verify CI/CD pipeline"
git push origin main

# Go to GitHub → Actions → watch the pipeline
# It should pass: test ✅ → deploy ✅ → validate ✅

Step 6: Configure monitoring (5 min)

UptimeRobot:
1. New Monitor → HTTP(s)
2. URL: https://your-app.railway.app/health
3. Interval: 5 minutes
4. Alert contacts: your email

Step 7: Create documentation (30-45 min)

Create the 4 documents in the docs/ folder:

docs/decision-matrix.md — Your decision matrix v_final (capsule 06) docs/runbook.md — Your operational runbook (capsule 05) docs/performance-baseline.md — Your performance baselines (capsule 07) docs/deployment-guide.md — See template below


Template: Deployment Guide

# Deployment Guide — [System Name]

## Prerequisites
- Docker 24.0+
- Python 3.11+
- GitHub account
- [Your platform] account
- OpenAI API key

## Step 1: Clone and Setup
git clone https://github.com/[your-user]/[your-repo].git
cd [your-repo]
cp .env.example .env
# Edit .env with your API keys

## Step 2: Run Locally
docker build -t [app-name]:latest .
docker run -d --name local \
    -p 8000:8000 \
    --env-file .env \
    [app-name]:latest
curl http://localhost:8000/health

## Step 3: Deploy to [Platform]
[Platform-specific steps]

## Step 4: Configure CI/CD
1. Add secrets to GitHub: [list]
2. Add variables to GitHub: [list]
3. Push to main to trigger deploy

## Step 5: Verify
python scripts/smoke_test.py [production-url]

## Step 6: Monitor
- UptimeRobot: [URL]
- OpenAI Usage: platform.openai.com

## Environment Variables
| Variable | Required | Description |
|----------|----------|-------------|
| OPENAI_API_KEY | Yes | OpenAI API key |
| ENVIRONMENT | Yes | development/staging/production |
| [Others] | ... | ... |

## Troubleshooting
- [Common issue 1]: [Solution]
- [Common issue 2]: [Solution]

Final Project Structure

your-ai-project/
├── .github/
│   └── workflows/
│       ├── deploy.yml              # CI/CD pipeline
│       └── rollback.yml            # Rollback workflow (optional)
├── src/
│   ├── __init__.py
│   ├── main.py                     # FastAPI app
│   └── config.py                   # Settings with Pydantic
├── tests/
│   ├── __init__.py
│   └── test_health.py              # Basic tests
├── scripts/
│   ├── smoke_test.py               # Smoke tests
│   ├── validate-deploy.sh          # Bash validation
│   ├── measure_latency.py          # Latency measurement
│   └── verify_secrets.py           # Env var verification
├── docs/
│   ├── decision-matrix.md          # Decision matrix v_final
│   ├── runbook.md                  # Operational runbook
│   ├── performance-baseline.md     # Performance baselines
│   └── deployment-guide.md         # Deployment guide
├── Dockerfile                      # Multi-stage build
├── .dockerignore                   # Image exclusions
├── docker-compose.yml              # Local development
├── requirements.txt                # Python dependencies
├── .env.example                    # Env var template
└── README.md                       # Project overview

Completeness Checklist

Deployed System

  • Accessible public URL: https://____________
  • GET /health returns 200 with status "healthy"
  • GET /health/ready verifies dependencies (OpenAI)
  • POST /api/inference processes a prompt and returns an LLM response
  • Environment variables configured on the platform (not hardcoded)
  • Docker image works locally with docker build + docker run

CI/CD Pipeline

  • git push main triggers the pipeline automatically
  • The test job runs pytest successfully
  • The deploy job deploys to the chosen platform
  • The validate job runs smoke tests against production
  • Complete pipeline in < 10 minutes
  • Secrets configured in GitHub (API keys, platform tokens)

Documentation

  • docs/decision-matrix.md with weighted criteria, evaluation, and justification
  • docs/runbook.md with at least 4 incident procedures
  • docs/performance-baseline.md with latency, costs, error rate, uptime targets
  • docs/deployment-guide.md that another engineer can follow from scratch
  • Working smoke test script (scripts/smoke_test.py)
  • README.md updated with a project overview

Monitoring

  • UptimeRobot (or an alternative) configured with a health check every 5 min
  • Downtime alerts configured (email or Slack)
  • OpenAI spending alerts configured

Quality

  • The system works after 24+ hours without intervention
  • A git push with a minor change triggers a successful deploy
  • The smoke tests pass consistently (>95% success rate)
  • The documentation is self-sufficient (someone external understands it)
  • The runbook was tested with at least one simulated incident

Evaluation Criteria

Rubric

CriterionWeightExcellent (5)Good (3)Insufficient (1)
Functional deploy25%Public URL, health checks, working inferenceWorking URL but missing a checkNot deployed or not accessible
CI/CD20%Complete pipeline with validatePipeline with test and deployNo CI/CD or manual
Decision matrix15%v_final with real data and validationMatrix with criteria but no real dataIncomplete or generic matrix
Runbook15%4+ tested procedures3 basic procedures<3 procedures or untested
Performance baseline10%4 metrics with targets and alerts2-3 metrics documentedNo baselines or no targets
Deployment guide10%Another engineer replicates the setupPartial instructionsNo guide or incomplete
Monitoring5%UptimeRobot + alerts configuredBasic monitoringNo monitoring

Delivery levels

BASIC LEVEL (pass):
├── System deployed with a working URL
├── Health check that responds 200
├── Working inference
├── Decision matrix with justification
└── CI/CD with at least test + deploy

ADVANCED LEVEL (distinguished):
├── Everything basic +
├── Automated post-deploy smoke tests
├── Runbook with 4+ tested procedures
├── Performance baseline with 4 metrics
├── Complete deployment guide
├── External monitoring configured
└── Documented and tested rollback strategy

EXCEPTIONAL LEVEL (portfolio-worthy):
├── Everything advanced +
├── Pipeline with staging → production
├── Post-mortem of a simulated incident
├── Sensitivity analysis in the decision matrix
├── Caching implemented for frequent responses
└── Documentation that looks like a professional team's

Final Verification: Complete Validation Script

All-in-one script to verify the project

# scripts/verify_project.py
"""
Complete verification of the final project.
Run: python scripts/verify_project.py https://your-app.railway.app
"""
import sys
import os
import json
import time
import urllib.request
import urllib.error

def check(name: str, condition: bool, detail: str = ""):
    icon = "PASS" if condition else "FAIL"
    msg = f"  [{icon}] {name}"
    if detail:
        msg += f" — {detail}"
    print(msg)
    return condition

def verify_deployment(base_url: str) -> dict:
    """Verifies all the requirements of the deployed system."""
    results = {"passed": 0, "failed": 0, "tests": []}

    print(f"\n{'='*60}")
    print(f"PROJECT VERIFICATION: {base_url}")
    print(f"{'='*60}")

    # 1. Health check
    print("\n--- System Health ---")
    try:
        req = urllib.request.Request(f"{base_url}/health")
        resp = urllib.request.urlopen(req, timeout=15)
        data = json.loads(resp.read())
        ok = check("Health endpoint", resp.status == 200, f"status={data.get('status')}")
        ok2 = check("Version present", "version" in data, f"v={data.get('version')}")
    except Exception as e:
        ok = check("Health endpoint", False, str(e)[:80])
        ok2 = False

    # 2. Readiness check
    try:
        req = urllib.request.Request(f"{base_url}/health/ready")
        resp = urllib.request.urlopen(req, timeout=15)
        data = json.loads(resp.read())
        ok3 = check("Readiness endpoint", resp.status == 200, f"status={data.get('status')}")
        ok4 = check("Dependency checks", "checks" in data, f"checks={list(data.get('checks',{}).keys())}")
    except Exception as e:
        ok3 = check("Readiness endpoint", False, str(e)[:80])
        ok4 = False

    # 3. Inference
    print("\n--- AI Inference ---")
    try:
        payload = json.dumps({"prompt": "What is 2+2? Answer briefly."}).encode()
        req = urllib.request.Request(
            f"{base_url}/api/inference",
            data=payload,
            headers={"Content-Type": "application/json"},
        )
        start = time.time()
        resp = urllib.request.urlopen(req, timeout=30)
        latency = (time.time() - start) * 1000
        data = json.loads(resp.read())
        ok5 = check("Inference endpoint", resp.status == 200, f"{latency:.0f}ms")
        ok6 = check("Response has content", len(data.get("response", "")) > 0)
        ok7 = check("Latency < 10s", latency < 10000, f"{latency:.0f}ms")
    except Exception as e:
        ok5 = check("Inference endpoint", False, str(e)[:80])
        ok6 = ok7 = False

    # 4. Error handling
    print("\n--- Error Handling ---")
    try:
        payload = json.dumps({"prompt": ""}).encode()
        req = urllib.request.Request(
            f"{base_url}/api/inference",
            data=payload,
            headers={"Content-Type": "application/json"},
        )
        resp = urllib.request.urlopen(req, timeout=10)
        ok8 = check("Empty prompt rejected", False, f"Expected 422, got {resp.status}")
    except urllib.error.HTTPError as e:
        ok8 = check("Empty prompt rejected", e.code == 422, f"status={e.code}")
    except Exception as e:
        ok8 = check("Empty prompt rejected", False, str(e)[:80])

    # 5. Documentation files
    print("\n--- Documentation ---")
    doc_files = [
        "docs/decision-matrix.md",
        "docs/runbook.md",
        "docs/performance-baseline.md",
        "docs/deployment-guide.md",
    ]
    for doc in doc_files:
        exists = os.path.exists(doc)
        check(f"File: {doc}", exists)

    # 6. CI/CD
    print("\n--- CI/CD ---")
    ci_file = ".github/workflows/deploy.yml"
    check(f"File: {ci_file}", os.path.exists(ci_file))

    # 7. Scripts
    print("\n--- Scripts ---")
    scripts = ["scripts/smoke_test.py"]
    for script in scripts:
        check(f"File: {script}", os.path.exists(script))

    print(f"\n{'='*60}")
    print("Verification complete.")
    print(f"{'='*60}\n")

if __name__ == "__main__":
    url = sys.argv[1] if len(sys.argv) > 1 else "http://localhost:8000"
    verify_deployment(url)

Execution

# Verify everything at once
python scripts/verify_project.py https://your-app.railway.app

# Expected output:
# ============================================================
# PROJECT VERIFICATION: https://your-app.railway.app
# ============================================================
#
# --- System Health ---
#   [PASS] Health endpoint — status=healthy
#   [PASS] Version present — v=1.0.0
#   [PASS] Readiness endpoint — status=ready
#   [PASS] Dependency checks — checks=['openai']
#
# --- AI Inference ---
#   [PASS] Inference endpoint — 2340ms
#   [PASS] Response has content
#   [PASS] Latency < 10s — 2340ms
#
# --- Error Handling ---
#   [PASS] Empty prompt rejected — status=422
#
# --- Documentation ---
#   [PASS] File: docs/decision-matrix.md
#   [PASS] File: docs/runbook.md
#   [PASS] File: docs/performance-baseline.md
#   [PASS] File: docs/deployment-guide.md
#
# --- CI/CD ---
#   [PASS] File: .github/workflows/deploy.yml
#
# --- Scripts ---
#   [PASS] File: scripts/smoke_test.py
#
# ============================================================
# Verification complete.
# ============================================================

Project Troubleshooting

"I can't deploy because my app needs an additional service (Redis, DB)"

Solution: For the project, simplify the architecture. Use only FastAPI + OpenAI API (no Redis or DB). The complexity is in the deployment integration, not in the app. If you insist on a DB, Railway and Render let you add additional services (PostgreSQL, Redis) from the dashboard.

"The deploy works but validation fails intermittently"

Solution:

# Increase wait times and retries
- name: Wait for deployment
  run: sleep 90  # Give more time, especially on free tier

# Add a retry to the smoke test
- name: Run smoke tests with retry
  run: |
    for i in 1 2 3; do
      python scripts/smoke_test.py "$URL" && exit 0
      echo "Attempt $i failed, retrying in 30s..."
      sleep 30
    done
    exit 1

"My OpenAI API key works locally but not in production"

Solution:

# Verify that the key is configured correctly on the platform
# It shouldn't have spaces, line breaks, or quotes

# Railway
railway variables | grep OPENAI

# Fly.io
fly secrets list

# Direct test of the key
curl https://api.openai.com/v1/models \
    -H "Authorization: Bearer sk-your-key-here" | head -c 100

"The pipeline takes too long and sometimes times out"

Solution: Optimize the pipeline:

# Use pip cache
- uses: actions/setup-python@v5
  with:
    cache: 'pip'

# Parallelize test and build if they're independent
# Reduce the number of tests in CI (only the critical path)
# Use timeout-minutes on each job

"How do I do the project without spending money?"

Solution: All the recommended platforms have a free tier:

Railway: $5 credit/month free (more than enough for a project)
Render: 750 hours/month of free instances
Fly.io: 3 free shared VMs
AWS Lambda: 1M requests/month free

OpenAI: Use gpt-4o-mini ($0.15/1M input tokens) —
        500 requests/day × 30 days = 15K requests/month ≈ $1.35/month

Final Result

On completing this project you'll have:

✅ DEPLOYED SYSTEM
   URL: https://your-app.railway.app (or your platform)
   Health: /health → healthy
   Inference: /api/inference → LLM response
   CI/CD: git push → automatic deploy

✅ PROFESSIONAL DOCUMENTATION
   Decision matrix: justification with real data
   Runbook: 4+ incident procedures
   Baselines: latency, costs, error rate, uptime
   Deploy guide: replicable instructions

✅ MONITORING
   UptimeRobot: health check every 5 min
   Alerts: email if the service goes down
   Cost alerts: OpenAI spending limits

Portfolio value

This project demonstrates:

  • Decision judgment: Decision matrix with documented trade-offs
  • Technical execution: Docker + CI/CD + deployment + validation
  • Production mindset: Runbook, baselines, monitoring
  • Communication: Documentation another engineer can use

It's the most complete artifact you can show in an infrastructure or AI engineering interview.


Connection with the Guide

What's next?

With your AI system deployed in production:

Guide #18 — Monitoring & Observability for AI: Your system is in production. Now you need to see what happens inside it: inference metrics, structured logs, distributed tracing, intelligent alerts, and dashboards. The transition is natural: "You have an AI system in production → now you need to observe it."

What you carry from this guide to #18:

  • A working AI system in production (this project)
  • Performance baselines (capsule 07)
  • Health checks and smoke tests (capsule 04)
  • A runbook for incidents (capsule 05)

What guide #18 adds:

  • Structured logging with inference context
  • Custom metrics (token usage, latency per model, cache hit rate)
  • Distributed tracing (request → preprocessing → LLM → postprocessing)
  • Anomaly-based alerts (not just static thresholds)
  • Dashboards for technical and non-technical stakeholders

Final Note

You've just put your first AI system into production — this is real.

It's not a tutorial that ends with "and that's how you'd deploy it." It's a real URL that anyone with internet can access. It's a pipeline that deploys automatically when you push. It's documentation another engineer can follow to operate the system.

This is what separates a developer who experiments from one who delivers in production. The experience of seeing your AI system responding on the internet, with professional documentation, monitoring configured, and incident procedures ready — no tutorial gives you that experience.

What you built here is the foundation for everything that comes next: monitoring, observability, scaling, multi-region. But the most important thing is that you already have something in production. And that changes everything.


Summary

  • You deployed an AI system to production with an accessible public URL, health check, and a working inference endpoint.
  • You configured a CI/CD pipeline (GitHub Actions) that deploys automatically from git push to main.
  • You implemented health checks and readiness checks that validate dependencies (OpenAI) post-deploy.
  • You documented the decision with a decision matrix v_final that justifies the strategy and platform.
  • You created an operational runbook with concrete procedures for common incidents.
  • You defined a performance baseline with latency, cost, error rate, and uptime targets.

Project Resources

  1. Architecture Decision Records — Format for documenting decisions
  2. The Twelve-Factor App — Principles of production-ready apps
  3. Google SRE Book — Reference for production operations
  4. Render Documentation — Docs for deployment on Render
  5. Railway Documentation — Docs for deployment on Railway
  6. Fly.io Documentation — Docs for deployment on Fly.io
  7. GitHub Actions Documentation — CI/CD with GitHub Actions
  8. UptimeRobot — Free uptime monitoring
  9. OpenAI API Reference — OpenAI API reference
  10. FastAPI Deployment — Official FastAPI deployment documentation