MLOps para Modelos Grandes

Versionamento de modelos, canary deployment, monitoring de drift, rate limiting, quota management. Atualizado em abril de 2026.


Visão Geral

Deployar um LLM em produção é diferente de deployar uma API tradicional. Os desafios específicos incluem:

  1. Modelos pesados — 7B parâmetros = 14GB em FP16, 3.5GB em INT4
  2. Estado complexo — KV cache, contexto, session state
  3. Custo variável — cada request custa GPU time
  4. Qualidade subjetiva — não há "erro 500" para resposta ruim
  5. Drift silencioso — modelo não "quebra", só fica pior gradualmente

Este documento cobre como operar LLMs em produção de forma confiável e econômica.


1. Versionamento de Modelos

O que versionar

Artefato O que é Por quê
Weights Arquivo .safetensors ou .pt O modelo em si
Config config.json, tokenizer_config.json Hiperparâmetros, vocabulário
Tokenizer Arquivo do tokenizer Mesmo tokenizer = mesma tokenização
Checkpoint Estado do treino (optimizer, scheduler) Para resume de treino
Adapter LoRA/QLoRA weights Fine-tune específico
Prompt templates Templates de sistema/instrução Mesmo prompt = mesmo comportamento
Eval results Resultados de avaliação Para comparação entre versões

Estrutura de versionamento

models/
── kode-coder/
│   ├── v0.1.0/              ← Versão base (Qwen2.5-Coder-32B)
│   │   ├── model/           ← Weights
│   │   ├── config.json
│   │   ├── tokenizer/
│   │   ├── eval/            ← Resultados de avaliação
│   │   │   ├── swe-bench.json
│   │   │   ├── human-eval.json
│   │   │   └── privado.json
│   │   └── metadata.json    ← Data, commit, notas
│   │
│   ├── v0.2.0/              ← Fine-tune SFT
│   │   ├── base: v0.1.0     ← Referência ao modelo base
│   │   ├── adapter/         ← LoRA weights
│   │   ├── config.json
│   │   ├── eval/
│   │   └── metadata.json
│   │
│   ├── v0.3.0/              ← Fine-tune SFT + DPO
│   │   ├── base: v0.2.0
│   │   ├── adapter/
│   │   ├── config.json
│   │   ├── eval/
│   │   ── metadata.json
│   │
│   ── current → v0.3.0     ← Symlink para versão ativa

Ferramentas de versionamento

Ferramenta Uso Prós
DVC Versionamento de arquivos grandes Git-like, open-source
MLflow Models Registry de modelos UI, experiment tracking
Weights & Biases Artifacts Versionamento + lineage Integração com W&B
HuggingFace Hub Host de modelos Comunidade, APIs
OCI Registry Container de modelos Padrão cloud-native

Recomendação para o Kode: W&B Artifacts + HuggingFace Hub (privado).


2. Deployment

Padrões de deployment

A. Single model (mais simples)

Usuário → Load Balancer → Modelo v1 (vLLM)

Uso: Ambiente único, sem necessidade de A/B testing.

B. Blue-Green

                    ┌── Modelo v1 (blue) ──→ Produção
Usuário → LB ───────┤
                    └── Modelo v2 (green) ──→ Staging

Fluxo:

  1. Deploy v2 em green (não recebe tráfego)
  2. Testar v2 em staging
  3. Switch: LB aponta para green
  4. v1 fica em standby para rollback

Vantagem: Rollback imediato (segundos).

C. Canary

                    ┌── Modelo v1 ─→ 90% do tráfego
Usuário → LB ───────
                    └── Modelo v2 ──→ 10% do tráfego

Fluxo:

  1. v2 recebe 10% do tráfego
  2. Monitorar métricas por 24h
  3. Se OK → 25% → 50% → 100%
  4. Se problema → rollback para 0%

Vantagem: Exposição gradual, problema afeta poucos usuários.

D. Shadow

                    ┌── Modelo v1 ──→ Responde ao usuário
Usuário → LB ──────┤
                    └── Modelo v2 ──→ Roda em shadow (sem resposta)

Uso: Testar v2 sem afetar usuários. Comparar outputs de v1 vs v2.

Implementação com vLLM

# Deploy do modelo v1
vllm serve koder/kode-coder-v0.3.0 \
    --port 8000 \
    --tensor-parallel-size 2 \
    --max-model-len 8192

# Deploy do modelo v2 (canary)
vllm serve koder/kode-coder-v0.4.0 \
    --port 8001 \
    --tensor-parallel-size 2 \
    --max-model-len 8192

Implementação com Kubernetes

apiVersion: apps/v1
kind: Deployment
metadata:
  name: kode-model-v0-3-0
spec:
  replicas: 3
  selector:
    matchLabels:
      app: kode-model
      version: v0.3.0
  template:
    metadata:
      labels:
        app: kode-model
        version: v0.3.0
    spec:
      containers:
      - name: vllm
        image: vllm/vllm-openai:latest
        args:
        - --model
        - koder/kode-coder-v0.3.0
        - --tensor-parallel-size
        - "2"
        resources:
          limits:
            nvidia.com/gpu: 2
---
apiVersion: v1
kind: Service
metadata:
  name: kode-model
spec:
  selector:
    app: kode-model
    # version: v0.3.0  ← mudar para v0.4.0 para trocar versão
  ports:
  - port: 80
    targetPort: 8000

3. Monitoring de Qualidade

Métricas de produção

Métrica Como medir Threshold
Latência p50 Tempo do primeiro token < 500ms
Latência p95 Tempo do primeiro token < 2000ms
Latência p99 Tempo do primeiro token < 5000ms
Throughput Tokens gerados / segundo > 50 tok/s por GPU
Taxa de erro Requests com erro / total < 1%
Taxa de timeout Requests timeout / total < 0.5%
GPU utilization % GPU em uso > 70%
GPU memory VRAM usada < 90%
KV cache hit rate Reuso de cache > 30% (com RadixAttention)

Monitoring de qualidade de output

Métrica Como medir
Eval score contínuo Rodar eval automático em amostra de produção
Feedback do usuário Thumbs up/down por resposta
Regeneração rate % de vezes que usuário pede "try again"
Truncation rate % de respostas cortadas (max tokens)
Empty response rate % de respostas vazias ou só whitespace

Pipeline de monitoring

Produção → Coleta de métricas (LangFuse, Prometheus) → Dashboard (Grafana)
                                        ↓
                               Alertas (Slack, email)
                                        ↓
                               Análise (semana)
                                        ↓
                               Decisão: manter, rollback, re-treinar

4. Detecção de Drift

Tipos de drift

Tipo Descrição Exemplo
Data drift Distribuição dos inputs muda Novas libs de código, novos frameworks
Concept drift O que é "bom" muda Boas práticas de código evoluem
Label drift Preferência do usuário muda Usuários preferem respostas mais curtas
Model drift Modelo degrada com uso KV cache corruption, quantização drift

Detecção automática

def detect_drift(current_eval, baseline_eval, threshold=0.05):
    """Detectar drift comparando eval atual com baseline."""
    metrics = ["code_pass_rate", "llm_judge_score", "latency_p95"]

    drifts = []
    for metric in metrics:
        current = current_eval[metric]
        baseline = baseline_eval[metric]
        change = abs(current - baseline) / baseline

        if change > threshold:
            drifts.append({
                "metric": metric,
                "change": change,
                "current": current,
                "baseline": baseline,
            })

    return drifts

# Uso
drifts = detect_drift(eval_semanal, eval_baseline)
if drifts:
    alert(f"Drift detectado: {drifts}")

Ações por tipo de drift

Drift detectado Ação
Data drift Adicionar novos dados ao dataset de treino
Concept drift Re-treinar com dados recentes
Label drift Atualizar reward model com preferências atuais
Model drift Reload do checkpoint, verificar hardware

5. Rate Limiting e Quota Management

Por que é necessário

LLMs são caros. Sem rate limiting:

  • Um usuário pode consumir toda a GPU
  • Loops infinitos de API calls esgotam quota
  • Attacks (prompt injection) geram custo

Estratégias

Estratégia Como funciona Uso
Rate limiting Máximo de requests por minuto Todos os usuários
Token quota Máximo de tokens por dia Usuários free tier
Concurrency limit Máximo de requests simultâneos Prevenir overload
Priority queue Usuários premium têm prioridade Multi-tenant
Circuit breaker Parar se erro rate > threshold Prevenir cascata

Implementação

# Rate limiting com Redis
import redis
import time

class RateLimiter:
    def __init__(self, redis_client, max_requests=100, window=60):
        self.redis = redis_client
        self.max_requests = max_requests
        self.window = window

    def is_allowed(self, user_id):
        key = f"rate_limit:{user_id}"
        current = self.redis.get(key)

        if current is None:
            self.redis.setex(key, self.window, 1)
            return True

        if int(current) >= self.max_requests:
            return False

        self.redis.incr(key)
        return True

# Token quota
class TokenQuota:
    def __init__(self, redis_client, daily_limit=100000):
        self.redis = redis_client
        self.daily_limit = daily_limit

    def check_and_consume(self, user_id, tokens):
        key = f"token_quota:{user_id}:{date.today()}"
        used = int(self.redis.get(key) or 0)

        if used + tokens > self.daily_limit:
            return False

        self.redis.incrby(key, tokens)
        return True

Tiers de quota

Tier Tokens/dia Requests/min Prioridade
Free 10K 10 Baixa
Pro 100K 50 Média
Enterprise 1M 200 Alta
Internal Ilimitado 500 Máxima

6. Rollback

Quando fazer rollback

Sinal Ação
Eval score cai > 5% Rollback imediato
Taxa de erro > 5% Rollback imediato
Latência p95 > 5s Rollback em 1h
Feedback negativo > 20% Investigar, possivelmente rollback
Drift detectado Avaliar, possivelmente rollback

Procedimento de rollback

# 1. Switch para versão anterior (Kubernetes)
kubectl set image deployment/kode-model \
    vllm=vllm/vllm-openai:latest \
    --record

# 2. Ou mudar symlink (file-based)
ln -sfn models/kode-coder/v0.3.0 models/kode-coder/current

# 3. Ou trocar weight no vLLM (hot reload)
curl -X POST http://localhost:8000/v1/reload \
    -d '{"model": "koder/kode-coder-v0.3.0"}'

# 4. Verificar health
curl http://localhost:8000/health

# 5. Notificar equipe

Tempo alvo de rollback: < 5 minutos.


7. Escalabilidade

Scaling horizontal

                   ┌── Pod 1 (vLLM) ──→ GPU 1
Load Balancer ─────┼── Pod 2 (vLLM) ──→ GPU 2
                   ┼── Pod 3 (vLLM) ──→ GPU 3
                   └── Pod N (vLLM) ──→ GPU N

Auto-scaling:

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: kode-model-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: kode-model
  minReplicas: 2
  maxReplicas: 10
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 70
  - type: Pods
    pods:
      metric:
        name: gpu_utilization
      target:
        type: AverageValue
        averageValue: 80

Scaling vertical (model parallelism)

Se o modelo não cabe em uma GPU:

Técnica Quando usar
Tensor Parallelism Modelo > VRAM de 1 GPU Dividir weights entre GPUs
Pipeline Parallelism Modelo muito grande Dividir layers entre GPUs
KV cache offloading Contexto longo Mover KV cache para CPU/disco

8. Custo por Request

Cálculo

Custo por request = (GPU-hours por request) × (custo por GPU-hour)

GPU-hours por request = (tokens gerados × tempo por token) / 3600

Exemplo:
  - 500 tokens gerados
  - 50ms por token = 25s total
  - 25s / 3600 = 0.007 GPU-hours
  - Custo A100: $2.80/hora
  - Custo por request: 0.007 × $2.80 = $0.02

Otimização de custo

Técnica Economia
Batching 30–50% (processar múltiplos requests juntos)
Speculative decoding 30–50% (menos tokens gerados)
Quantização 0% custo, mas permite GPU menor (50% economia)
KV cache reuse 20–40% (RadixAttention para prompts repetidos)
Model switching 50–80% (usar modelo menor para tasks simples)

Para o Kode

Stack de MLOps recomendado

Versionamento: W&B Artifacts + HuggingFace Hub (privado)
Deployment: Kubernetes + vLLM
Monitoring: Prometheus + Grafana + LangFuse
Alertas: Slack + PagerDuty
Rate limiting: Redis + NGINX
CI/CD: GitHub Actions → build Docker → push → deploy

Pipeline de deployment

1. Novo checkpoint → Eval automático (SWE-bench, eval privado)
2. Se eval OK → Push para HuggingFace Hub + W&B
3. Deploy canary (10% do tráfego)
4. Monitorar 24h
5. Se OK → Gradual ramp-up (25% → 50% → 100%)
6. Se problema → Rollback automático

Configuração inicial

Item Configuração
Modelos v0.1.0 (base) + v0.2.0 (fine-tune)
GPUs 2× RTX 4090 (desenvolvimento)
Serving vLLM com tensor parallelism
Monitoring LangFuse + Prometheus
Rate limit 100 requests/min por usuário
Quota 100K tokens/dia (free), 1M (pro)

Referências

Recurso Descrição
vLLM docs Serving de LLMs com alta throughput
LangFuse Observabilidade de LLMs
W&B Artifacts Versionamento de modelos
Prometheus Monitoring de métricas
Kubernetes HPA Auto-scaling horizontal