---
name: mlops-engineer
type: reference
description: "Provides MLOps patterns for ML CI/CD pipelines, model registries, monitoring, and data drift detection. Use when setting up ML infrastructure or when the user mentions MLOps, model deployment, ML pipeline, or model monitoring."
paths: ["/*.py", "/Dockerfile", "/requirements.txt", "/mlflow", "**/*.yaml"]
effort: 4
allowed-tools: Read, Glob, Grep, Write, Edit, Bash
user-invocable: true
when_to_use: "When building ML pipelines, experiment tracking systems, or model registries with MLflow or Kubeflow"
MLOps Engineer
Tool selection matrix
| Need |
Tool |
When to use |
| Experiment tracking |
MLflow |
Open-source, self-hosted |
| Experiment tracking |
W&B |
Cloud, rich visualization |
| Pipeline orchestration |
Kubeflow |
Kubernetes-native |
| Pipeline orchestration |
Prefect |
Python-first, dynamic |
| Data version control |
DVC |
Git-based datasets & models |
| Feature store |
Feast |
Open-source, online+offline |
| Model serving |
KServe |
K8s serverless inference |
| Model serving |
SageMaker Endpoints |
AWS managed |
| Monitoring / drift |
Evidently |
Open-source, alerting |
| CI/CD for ML |
GitHub Actions + DVC |
Lightweight |
MLflow: experiment tracking + model registry
import mlflow
import mlflow.sklearn
mlflow.set_tracking_uri("http://mlflow-server:5000")
mlflow.set_experiment("model-training")
with mlflow.start_run():
# Log params
mlflow.log_param("n_estimators", 100)
mlflow.log_param("max_depth", 5)
# Train
model = train(X_train, y_train)
metrics = evaluate(model, X_test, y_test)
# Log metrics
mlflow.log_metric("accuracy", metrics["accuracy"])
mlflow.log_metric("f1", metrics["f1"])
# Log model + register
mlflow.sklearn.log_model(
model, "model",
registered_model_name="fraud-detector",
)
# Promote to production via API
client = mlflow.tracking.MlflowClient()
client.transition_model_version_stage(
name="fraud-detector", version=3, stage="Production"
)
GitHub Actions: ML CI/CD pipeline
name: ML Pipeline
on:
push:
paths: ["data/**", "src/**", "params.yaml"]
jobs:
train-and-validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: iterative/setup-dvc@v1
- name: Pull data
run: dvc pull
- name: Run training pipeline
run: dvc repro
- name: Validate model metrics
run: |
python scripts/check_metrics.py \
--min-accuracy 0.92 \
--min-f1 0.88
- name: Register model if metrics pass
if: github.ref == 'refs/heads/main'
run: python scripts/register_model.py
env:
MLFLOW_TRACKING_URI: ${{ secrets.MLFLOW_URI }}
Model serving: FastAPI + model registry
from fastapi import FastAPI
import mlflow.pyfunc
import os
app = FastAPI()
MODEL_NAME = os.environ["MODEL_NAME"]
MODEL_STAGE = os.environ.get("MODEL_STAGE", "Production")
# Load once on startup (cold start cost paid once)
model = mlflow.pyfunc.load_model(f"models:/{MODEL_NAME}/{MODEL_STAGE}")
@app.post("/predict")
async def predict(features: dict):
import pandas as pd
df = pd.DataFrame([features])
predictions = model.predict(df)
return {"predictions": predictions.tolist()}
@app.get("/health")
async def health():
return {"status": "healthy", "model": MODEL_NAME, "stage": MODEL_STAGE}
Data drift monitoring (Evidently)
from evidently.report import Report
from evidently.metric_preset import DataDriftPreset
import pandas as pd
def check_drift(reference_data: pd.DataFrame, production_data: pd.DataFrame) -> dict:
report = Report(metrics=[DataDriftPreset()])
report.run(reference_data=reference_data, current_data=production_data)
result = report.as_dict()
drift_detected = result["metrics"][0]["result"]["dataset_drift"]
drifted_features = [
f for f, v in result["metrics"][0]["result"]["drift_by_columns"].items()
if v["drift_detected"]
]
return {"drift_detected": drift_detected, "drifted_features": drifted_features}
# Trigger retraining if drift detected
if check_drift(ref, prod)["drift_detected"]:
trigger_retraining_pipeline()
Critical rules (non-obvious)
- Separate training and serving environments — training deps (torch, cuda) bloat serving images by 10x; use multi-stage Dockerfiles or separate images
- Pin all dependencies — ML stack changes break reproducibility; pin Python + all packages, freeze with
pip freeze not just requirements.txt
- Log everything before filtering — never decide what metrics to log during training; log all, filter in dashboards
- Separate model config from code —
params.yaml (DVC) or config.yaml for hyperparameters; never hardcode in training scripts
- Shadow mode before cutover — run new model version in parallel (shadow traffic), compare outputs before switching production
DVC pipeline (dvc.yaml)
stages:
preprocess:
cmd: python src/preprocess.py
deps: [src/preprocess.py, data/raw/]
outs: [data/processed/]
params: [params.yaml:preprocess]
train:
cmd: python src/train.py
deps: [src/train.py, data/processed/]
outs: [models/model.pkl]
params: [params.yaml:train]
metrics: [metrics/train.json]
evaluate:
cmd: python src/evaluate.py
deps: [src/evaluate.py, models/model.pkl, data/processed/]
metrics: [metrics/eval.json]
1---2name: mlops-engineer3description: ---4---5---6name: mlops-engineer7type: reference8description: "Provides MLOps patterns for ML CI/CD pipelines, model registries, monitoring, and data drift detection. Use when setting up ML infrastructure or when the user mentions MLOps, model deployment, ML pipeline, or model monitoring."9paths: ["**/*.py", "**/Dockerfile", "**/requirements*.txt", "**/mlflow*", "**/*.yaml"]10effort: 411allowed-tools: Read, Glob, Grep, Write, Edit, Bash12user-invocable: true13when_to_use: "When building ML pipelines, experiment tracking systems, or model registries with MLflow or Kubeflow"14---1516# MLOps Engineer1718## Tool selection matrix1920| Need | Tool | When to use |21|---|---|---|22| Experiment tracking | MLflow | Open-source, self-hosted |23| Experiment tracking | W&B | Cloud, rich visualization |24| Pipeline orchestration | Kubeflow | Kubernetes-native |25| Pipeline orchestration | Prefect | Python-first, dynamic |26| Data version control | DVC | Git-based datasets & models |27| Feature store | Feast | Open-source, online+offline |28| Model serving | KServe | K8s serverless inference |29| Model serving | SageMaker Endpoints | AWS managed |30| Monitoring / drift | Evidently | Open-source, alerting |31| CI/CD for ML | GitHub Actions + DVC | Lightweight |3233## MLflow: experiment tracking + model registry3435```python36import mlflow37import mlflow.sklearn3839mlflow.set_tracking_uri("http://mlflow-server:5000")40mlflow.set_experiment("model-training")4142with mlflow.start_run():43 # Log params44 mlflow.log_param("n_estimators", 100)45 mlflow.log_param("max_depth", 5)4647 # Train48 model = train(X_train, y_train)49 metrics = evaluate(model, X_test, y_test)5051 # Log metrics52 mlflow.log_metric("accuracy", metrics["accuracy"])53 mlflow.log_metric("f1", metrics["f1"])5455 # Log model + register56 mlflow.sklearn.log_model(57 model, "model",58 registered_model_name="fraud-detector",59 )6061# Promote to production via API62client = mlflow.tracking.MlflowClient()63client.transition_model_version_stage(64 name="fraud-detector", version=3, stage="Production"65)66```6768## GitHub Actions: ML CI/CD pipeline6970```yaml71name: ML Pipeline72on:73 push:74 paths: ["data/**", "src/**", "params.yaml"]7576jobs:77 train-and-validate:78 runs-on: ubuntu-latest79 steps:80 - uses: actions/checkout@v481 - uses: iterative/setup-dvc@v18283 - name: Pull data84 run: dvc pull8586 - name: Run training pipeline87 run: dvc repro8889 - name: Validate model metrics90 run: |91 python scripts/check_metrics.py \92 --min-accuracy 0.92 \93 --min-f1 0.889495 - name: Register model if metrics pass96 if: github.ref == 'refs/heads/main'97 run: python scripts/register_model.py98 env:99 MLFLOW_TRACKING_URI: ${{ secrets.MLFLOW_URI }}100```101102## Model serving: FastAPI + model registry103104```python105from fastapi import FastAPI106import mlflow.pyfunc107import os108109app = FastAPI()110MODEL_NAME = os.environ["MODEL_NAME"]111MODEL_STAGE = os.environ.get("MODEL_STAGE", "Production")112113# Load once on startup (cold start cost paid once)114model = mlflow.pyfunc.load_model(f"models:/{MODEL_NAME}/{MODEL_STAGE}")115116@app.post("/predict")117async def predict(features: dict):118 import pandas as pd119 df = pd.DataFrame([features])120 predictions = model.predict(df)121 return {"predictions": predictions.tolist()}122123@app.get("/health")124async def health():125 return {"status": "healthy", "model": MODEL_NAME, "stage": MODEL_STAGE}126```127128## Data drift monitoring (Evidently)129130```python131from evidently.report import Report132from evidently.metric_preset import DataDriftPreset133import pandas as pd134135def check_drift(reference_data: pd.DataFrame, production_data: pd.DataFrame) -> dict:136 report = Report(metrics=[DataDriftPreset()])137 report.run(reference_data=reference_data, current_data=production_data)138 result = report.as_dict()139140 drift_detected = result["metrics"][0]["result"]["dataset_drift"]141 drifted_features = [142 f for f, v in result["metrics"][0]["result"]["drift_by_columns"].items()143 if v["drift_detected"]144 ]145 return {"drift_detected": drift_detected, "drifted_features": drifted_features}146147# Trigger retraining if drift detected148if check_drift(ref, prod)["drift_detected"]:149 trigger_retraining_pipeline()150```151152## Critical rules (non-obvious)153154- **Separate training and serving environments** — training deps (torch, cuda) bloat serving images by 10x; use multi-stage Dockerfiles or separate images155- **Pin all dependencies** — ML stack changes break reproducibility; pin Python + all packages, freeze with `pip freeze` not just `requirements.txt`156- **Log everything before filtering** — never decide what metrics to log during training; log all, filter in dashboards157- **Separate model config from code** — `params.yaml` (DVC) or `config.yaml` for hyperparameters; never hardcode in training scripts158- **Shadow mode before cutover** — run new model version in parallel (shadow traffic), compare outputs before switching production159160## DVC pipeline (dvc.yaml)161162```yaml163stages:164 preprocess:165 cmd: python src/preprocess.py166 deps: [src/preprocess.py, data/raw/]167 outs: [data/processed/]168 params: [params.yaml:preprocess]169170 train:171 cmd: python src/train.py172 deps: [src/train.py, data/processed/]173 outs: [models/model.pkl]174 params: [params.yaml:train]175 metrics: [metrics/train.json]176177 evaluate:178 cmd: python src/evaluate.py179 deps: [src/evaluate.py, models/model.pkl, data/processed/]180 metrics: [metrics/eval.json]181```