MLOps Fundamentals: CI/CD para Modelos de IA
¿Qué es MLOps / LLMOps?
MLOps aplica prácticas de DevOps al ciclo de vida de modelos de Machine Learning. LLMOps es su evolución para aplicaciones basadas en LLMs, donde el "modelo" no se entrena desde cero sino que se orquesta, se configura con prompts, y se conecta con herramientas.
DevOps: Code ──► Build ──► Test ──► Deploy ──► Monitor
MLOps: Data ──► Train ──► Eval ──► Deploy ──► Monitor
LLMOps: Prompt ──► Chain ──► Eval ──► Deploy ──► Monitor
CI/CD Pipeline para Aplicaciones de IA
GitHub Actions: Pipeline Completo
# .github/workflows/ai-service-ci.yml
name: AI Service CI/CD
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
env:
AWS_REGION: us-east-1
ECR_REPOSITORY: rag-api
ECS_CLUSTER: ai-production
jobs:
# ── 1. Lint y Tests Unitarios ──────────────────────────
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Install dependencies
run: |
pip install -r requirements.txt
pip install -r requirements-dev.txt
- name: Lint
run: |
ruff check .
mypy src/ --ignore-missing-imports
- name: Unit tests
run: pytest tests/unit/ -v --cov=src --cov-report=xml
- name: Upload coverage
uses: codecov/codecov-action@v4
# ── 2. Tests de Integración ────────────────────────────
integration:
needs: test
runs-on: ubuntu-latest
services:
qdrant:
image: qdrant/qdrant:latest
ports: ["6333:6333"]
redis:
image: redis:7
ports: ["6379:6379"]
steps:
- uses: actions/checkout@v4
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Install dependencies
run: pip install -r requirements.txt -r requirements-dev.txt
- name: Integration tests
env:
QDRANT_HOST: localhost
REDIS_HOST: localhost
run: pytest tests/integration/ -v --timeout=60
# ── 3. Evaluación de Calidad de IA ────────────────────
evaluation:
needs: integration
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Install dependencies
run: pip install -r requirements.txt -r requirements-dev.txt
- name: Run AI evaluation suite
env:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
run: |
python -m pytest tests/evaluation/ -v \
--tb=short \
--junitxml=eval-results.xml
- name: Check quality gates
run: |
python scripts/check_quality_gates.py eval-results.xml \
--min-faithfulness 0.85 \
--min-relevancy 0.80 \
--max-hallucination 0.05
# ── 4. Build y Push Docker ─────────────────────────────
build:
needs: [integration, evaluation]
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
outputs:
image_tag: ${{ steps.meta.outputs.tags }}
steps:
- uses: actions/checkout@v4
- name: Configure AWS
uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ${{ env.AWS_REGION }}
- name: Login to ECR
uses: aws-actions/amazon-ecr-login@v2
- name: Build and push
uses: docker/build-push-action@v5
with:
push: true
tags: |
${{ env.ECR_REGISTRY }}/${{ env.ECR_REPOSITORY }}:${{ github.sha }}
${{ env.ECR_REGISTRY }}/${{ env.ECR_REPOSITORY }}:latest
# ── 5. Deploy ──────────────────────────────────────────
deploy:
needs: build
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
environment: production
steps:
- name: Deploy to ECS
uses: aws-actions/amazon-ecs-deploy-task-definition@v1
with:
task-definition: task-definition.json
service: rag-api
cluster: ${{ env.ECS_CLUSTER }}
wait-for-service-stability: true
Quality Gates para IA
# scripts/check_quality_gates.py
import sys
import xml.etree.ElementTree as ET
def check_gates(results_file, thresholds):
tree = ET.parse(results_file)
root = tree.getroot()
failures = []
for testcase in root.iter("testcase"):
name = testcase.get("name", "")
# Extraer métricas de los test results
properties = {p.get("name"): float(p.get("value", 0))
for p in testcase.findall(".//property")}
if "faithfulness" in properties:
if properties["faithfulness"] < thresholds["min_faithfulness"]:
failures.append(
f"FAIL: faithfulness {properties['faithfulness']:.2f} "
f"< {thresholds['min_faithfulness']}"
)
if failures:
print("❌ Quality gates FAILED:")
for f in failures:
print(f" {f}")
sys.exit(1)
print("✅ All quality gates passed")
Versionado de Prompts
# Prompts versionados junto al código
"""
prompts/
├── v1/
│ ├── rag_system.txt
│ └── agent_system.txt
├── v2/
│ ├── rag_system.txt # Mejorado tras eval
│ └── agent_system.txt
└── current -> v2/ # Symlink a versión activa
"""
class PromptManager:
def __init__(self, prompts_dir: str = "prompts/current"):
self.dir = prompts_dir
self._cache = {}
def get(self, name: str) -> str:
if name not in self._cache:
path = f"{self.dir}/{name}.txt"
with open(path) as f:
self._cache[name] = f.read()
return self._cache[name]
def render(self, name: str, **kwargs) -> str:
template = self.get(name)
return template.format(**kwargs)
Rollback Strategy
# Canary deployment para IA
# 1. Desplegar la nueva versión al 10% del tráfico
# 2. Monitorear métricas de calidad durante 30 min
# 3. Si calidad OK → progresivamente subir a 100%
# 4. Si calidad cae → rollback automático
# ECS con CodeDeploy traffic shifting
deploy:
type: CodeDeployDefault.ECSCanary10Percent5Minutes
alarm:
- AIQualityAlarm # faithfulness < 85%
- HighLatencyAlarm # p99 > 30s
- HighErrorRateAlarm # error rate > 5%
Resumen
MLOps/LLMOps para IA en producción:
- CI/CD pipeline — lint, unit tests, integration tests, eval, build, deploy
- Quality gates — métricas mínimas de calidad antes de deploy
- Versionado de prompts — tratar prompts como código
- Canary deploys — despliegue gradual con rollback automático
- Monitoreo post-deploy — alertas de calidad, latencia y costo
🧠 Preguntas de Repaso
1. ¿Cuál es la diferencia fundamental entre MLOps tradicional y LLMOps?
- A) LLMOps es más barato que MLOps
- B) En MLOps se entrena el modelo (Data → Train → Eval → Deploy); en LLMOps el modelo no se entrena, se orquesta con prompts y herramientas (Prompt → Chain → Eval → Deploy)
- C) MLOps solo funciona con Python, LLMOps con cualquier lenguaje
- D) No hay diferencia, LLMOps es otro nombre para MLOps
Respuesta: B) — MLOps se centra en el ciclo de vida de entrenamiento de modelos. LLMOps es la evolución para apps con LLMs donde el modelo (GPT-4, Claude) ya viene pre-entrenado y lo que se gestiona son prompts, chains, herramientas y evaluaciones.
2. En un pipeline CI/CD para IA, ¿qué son los "Quality Gates" y qué pasa si no se cumplen?
- A) Son tests de velocidad de la aplicación
- B) Son umbrales mínimos de calidad (ej: faithfulness ≥0.85, hallucination ≤5%) que se verifican automáticamente, y si no se cumplen el pipeline falla y no se despliega
- C) Son aprobaciones manuales del equipo de QA
- D) Son indicadores de uso de memoria
Respuesta: B) — Los quality gates son umbrales automáticos:
--min-faithfulness 0.85,--min-relevancy 0.80,--max-hallucination 0.05. Si alguna métrica no pasa, el script retornasys.exit(1)y el pipeline falla, evitando deployment de versiones de baja calidad.
3. ¿Qué significa el canary deployment CodeDeployDefault.ECSCanary10Percent5Minutes?
- A) Se despliega al 10% de los usuarios durante 5 minutos, y si no hay alarmas se completa el deployment al 100%
- B) Se reinicia el 10% de los containers cada 5 minutos
- C) Se reduce la capacidad al 10% durante 5 minutos de mantenimiento
- D) Se prueban 10 endpoints diferentes en 5 minutos
Respuesta: A) — Canary deployment envía 10% del tráfico a la nueva versión durante 5 minutos. Si las alarmas (faithfulness <85%, latencia p99 >30s, error rate >5%) no se activan, se completa el deployment. Si alguna alarma se activa, se hace rollback automático.
4. ¿Por qué se recomienda tratar los prompts como código con versionado en directorio?
- A) Porque los prompts cambian con más frecuencia que el código y necesitan rollback rápido
- B) Para que el editor los resalte con syntax highlighting
- C) Porque Git no puede versionar strings en código Python
- D) Solo por convención, no hay beneficio real
Respuesta: A) — Los prompts son el componente que más cambia en sistemas LLM. Versionarlos en directorio (prompts/v1/, prompts/v2/, prompts/current → symlink) permite rollback instantáneo, auditoría de cambios, y desvincula los cambios de prompts del deployment de código.