En este modulo

  1. MLOps for LLMs: what changes
  2. Model registry: model versioning
  3. Experiment tracking
  4. Prompt versioning: the forgotten component
  5. CI/CD pipeline for AI models
  6. A/B testing of models
  7. Canary deployments and rollback
  8. Pipeline automation with GitHub Actions
  9. Ejercicio practico
  10. Puntos clave

MLOps for LLMs: what changes

Classic MLOps was designed for traditional ML models (random forest, XGBoost, small neural networks). The pipeline was: data -> training -> validation -> deploy -> monitoring. With LLMs, the pipeline changes in several fundamental ways.

Key differences from classic MLOps

Model registry: model versioning

A model registry is a centralized store for models with metadata, versions and lineage. For LLMs, it stores: model weights (or reference), configuration, LoRA adapters, eval results, and associated prompts.

Model registry options

# DIY model registry structure (MinIO + PostgreSQL)
# Storage (MinIO)
models/
  qwen3.5-27b-soc/
    v1.0/
      model/           # Merged weights (or LoRA adapter)
      tokenizer/
      config.json
      eval_results.json
      training_config.yml
    v1.1/
      ...

# Metadata (PostgreSQL)
CREATE TABLE model_versions (
    id SERIAL PRIMARY KEY,
    model_name VARCHAR(255) NOT NULL,
    version VARCHAR(50) NOT NULL,
    base_model VARCHAR(255),
    adapter_type VARCHAR(50),           -- 'lora', 'qlora', 'full', 'none'
    training_dataset VARCHAR(255),
    training_examples INT,
    eval_score FLOAT,
    eval_dataset VARCHAR(255),
    status VARCHAR(50) DEFAULT 'staging', -- 'staging', 'canary', 'production', 'retired'
    storage_path VARCHAR(500),
    created_at TIMESTAMP DEFAULT NOW(),
    deployed_at TIMESTAMP,
    notes TEXT,
    UNIQUE(model_name, version)
);

Experiment tracking

Experiment tracking records everything that happens during training and evaluation: hyperparameters, metrics, loss curves, eval results, artifacts. Without tracking, you can't reproduce or compare experiments.

# Experiment tracking with MLflow
import mlflow

mlflow.set_experiment("soc-alert-classifier")

with mlflow.start_run(run_name="qwen27b-qlora-r16-e3"):
    # Log hyperparameters
    mlflow.log_params({
        "base_model": "Qwen/Qwen3.5-27B",
        "adapter": "qlora",
        "lora_r": 16,
        "lora_alpha": 32,
        "learning_rate": 2e-4,
        "epochs": 3,
        "batch_size": 8,
        "dataset_size": 3500,
        "max_seq_len": 4096,
    })

    # Train (your Unsloth/Axolotl code here)
    trainer.train()

    # Log training metrics
    mlflow.log_metric("train_loss_final", trainer.state.log_history[-1]["loss"])

    # Evaluate
    eval_results = evaluate_model(model, "eval_dataset.jsonl")
    mlflow.log_metrics({
        "eval_accuracy": eval_results["accuracy"],
        "eval_f1": eval_results["f1"],
        "eval_latency_p50": eval_results["latency_p50"],
        "eval_latency_p99": eval_results["latency_p99"],
    })

Prompt versioning: the forgotten component

Prompts are executable code. A system prompt change can improve or destroy system quality. They need the same versioning rigor as source code.

# Prompt versioning structure
docs/prompts/
  soc_triage/
    system_v1.0.txt       # "You are a security analyst..."
    system_v1.1.txt       # Added structured output format
    system_v2.0.txt       # Complete rewrite with chain-of-thought
    CHANGELOG.md          # What changed and why in each version
    eval_results/
      v1.0_eval.json      # Eval results with v1.0
      v1.1_eval.json
      v2.0_eval.json

Prompts in Git, not in the database

Prompts should live in the code repository, not in a database or admin panel. This way they have: versioning (git log), code review (pull requests), rollback (git revert), and traceability (who changed what, when, and why).

CI/CD pipeline for AI models

A CI/CD pipeline for AI has 6 stages. The first 3 are CI (validation). The last 3 are CD (deployment).

# CI/CD Pipeline for AI models
# ================================

# CI Stage 1: Lint + format validation
# CI Stage 2: Automated eval (gate: eval_score >= production_score * 0.98)
# CI Stage 3: Smoke test (start vLLM, send 10 requests, verify)

# CD Stage 4: Deploy to staging
# CD Stage 5: Canary deployment (5% traffic, monitor 24h)
# CD Stage 6: Full rollout (5% -> 25% -> 50% -> 100%)

A/B testing of models

A/B testing compares two models (or two prompts) by serving real traffic to both and measuring quality and performance metrics.

# A/B testing with percentage routing
import random
from dataclasses import dataclass

@dataclass
class ModelVariant:
    name: str
    endpoint: str
    weight: float  # Traffic percentage (0.0 - 1.0)

class ABRouter:
    def __init__(self, variants: list[ModelVariant]):
        self.variants = variants

    def route(self, request_id: str) -> ModelVariant:
        # Deterministic routing based on request_id
        hash_val = hash(request_id) % 1000 / 1000
        cumulative = 0.0
        for variant in self.variants:
            cumulative += variant.weight
            if hash_val < cumulative:
                return variant
        return self.variants[-1]

Canary deployments and rollback

A canary deployment sends a small percentage of traffic to the new model. If metrics degrade, it reverts automatically.

# Canary deployment with automatic health checks
class CanaryDeployer:
    async def deploy_canary(self, new_model: str, stages=[0.05, 0.25, 0.50, 1.0]):
        for weight in stages:
            self.router.set_weight("treatment", weight)
            await asyncio.sleep(3600)  # 1 hour per stage

            metrics = await self.metrics.get_comparison("control", "treatment", window_minutes=60)

            if self.should_rollback(metrics):
                self.router.set_weight("treatment", 0.0)
                return False

        return True

    def should_rollback(self, metrics) -> bool:
        if metrics.treatment_p99 > metrics.control_p99 * 1.2:
            return True
        if metrics.treatment_error_rate > metrics.control_error_rate + 0.01:
            return True
        return False

Pipeline automation with GitHub Actions

# .github/workflows/model-deploy.yml
name: Model CI/CD Pipeline

on:
  push:
    paths:
      - 'models/**'
      - 'prompts/**'
      - 'eval/**'

jobs:
  validate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Validate dataset format
        run: python scripts/validate_dataset.py eval/eval_dataset.jsonl

  evaluate:
    needs: validate
    runs-on: [self-hosted, gpu]
    steps:
      - uses: actions/checkout@v4
      - name: Run eval suite
        run: python scripts/run_evals.py --model models/latest --eval-dataset eval/eval_dataset.jsonl
      - name: Check eval gate
        run: python scripts/check_eval_gate.py --results eval_results.json --min-score 0.85

  deploy-canary:
    needs: evaluate
    runs-on: ubuntu-latest
    environment: production
    steps:
      - name: Start canary
        run: python scripts/canary_deploy.py --weight 0.05 --duration 24h

Ejercicio practico

Ejercicio TE05: MLOps pipeline
  1. Create a simple model registry: directory on disk + PostgreSQL (or SQLite) table with version metadata, eval_score, status.
  2. Configure MLflow (or W&B free) for experiment tracking. Record hyperparameters and results from TE04 fine-tuning.
  3. Version your prompts in a docs/prompts/ directory with CHANGELOG.md. Write v1.0 and v1.1 of your use case system prompt.
  4. Create a run_evals.py script that evaluates a model against your eval dataset and produces a JSON with results.
  5. Implement an eval gate: script that compares candidate model results vs production and fails if regression >2%.
  6. Write a GitHub Actions workflow that runs validation + eval + gate on each push to the models or prompts folder.

Bonus: Implement basic A/B testing with the router from the example. Serve two versions of your model and compare metrics for 1 hour.

Puntos clave

Puntos clave from TE05

  1. MLOps for LLMs has key differences from classic MLOps: prompts are code, models are enormous, and evaluation is subjective. Adapt your pipelines.
  2. Model registry + experiment tracking are not optional. Without them, you can't reproduce, compare or audit. MLflow or W&B Free are enough to start.
  3. Prompts are versioned in Git, not in a database. Code review, rollback and traceability included for free.
  4. Eval gates in CI/CD prevent regressions. A new model that doesn't beat the current one should not reach production.
  5. Canary deployments are mandatory for production models. 5% of traffic for 24h before full rollout.
Guia de estudio — Conceptos clave de TE05

MLOps para LLMs: que cambia

  • El modelo base no se entrena, se elige.En ML clasico, entrenas tu modelo desde cero. Con LLMs, eliges un modelo pre-entrenado y lo adaptas (fine-tuning) o lo usas directamente (prompting). El "training" se reemplaza parcialmente por "prompt engineering".
  • Los prompts son codigo.Un cambio en el system prompt cambia el comportamiento del sistema tanto como un cambio en los pesos del modelo. Los prompts necesitan versionado, testing y rollback igual que el codigo.
  • Los modelos son enormes.Un modelo de 27B ocupa 14-54GB. No puedes versionarlo en Git. Necesitas un model registry especializado.
  • La evaluacion es subjetiva.En clasificacion, accuracy es objetiva. En generacion de texto, "es buena la respuesta" es subjetiva. Necesitas evals automatizados (LLM-as-judge) y humanos.
  • El coste es variable.Un modelo via API tiene coste por token. Un modelo self-hosted tiene coste fijo pero capacidad limitada. El CI/CD necesita incluir metricas de coste.

Model registry: versionado de modelos

  • MLflow Model Registry:open-source, maduro, se integra con todo. Soporta modelos grandes via artifact storage (S3, GCS, MinIO).
  • Weights & Biases (W&B):experiment tracking + model registry. Excelente UX. Plan gratuito para uso personal.
  • HuggingFace Hub:almacen de modelos con versionado Git LFS. Ideal si tu ecosistema es HuggingFace.
  • DIY con S3/MinIO + metadata DB:lo mas simple. Carpeta con versionado + tabla en PostgreSQL con metadatos.
  • 'lora', 'qlora', 'full', 'none'
  • 'staging', 'canary', 'production', 'retired'

Prompt versioning: el componente olvidado

  • # version: 2.0
  • Eres un analista de seguridad SOC nivel 2. Tu tarea es realizar
  • Prompts en Git, no en la base de datos: Los prompts deben vivir en el repositorio de codigo, no en una base de datos o en un panel de admin. Asi tienen: versionado (git log), code review (pull requests), rollback (git revert), y trazabilidad (quien cambio que, cuando, y por que).

CI/CD pipeline para modelos IA

  • Dataset: validar formato JSONL, schema correcto
  • Prompts: validar que no hay placeholders sin rellenar
  • Config: validar YAML de entrenamiento
  • Ejecutar eval dataset contra el modelo candidato
  • Comparar con el modelo en produccion
  • Gate: eval_score >= production_score * 0.98 (no permitir regresion >2%)

A/B testing de modelos

  • 1.0) < 0.001
  • Latencia (TTFT, total):el modelo nuevo no debe ser significativamente mas lento.
  • Tasa de errores:respuestas vacias, formato incorrecto, tool calls fallidos.
  • Calidad percibida:sampling aleatorio de respuestas evaluadas por LLM-as-judge o humanos.
  • Engagement:los usuarios aceptan la respuesta (thumbs up), la editan, o la rechazan.
  • Coste por request:tokens consumidos, tiempo de GPU.

Pipeline automation con GitHub Actions

  • 'models/'
  • 'prompts/'

Siguiente: TE06 - AI Systems Observability

Your MLOps pipeline is set up. Now you need to see what happens in production: metrics, traces, alerts and dashboards for LLMs.

Ir al modulo TE06