🧠FFVAcademy
📈

LLMOps: eval harness, drift detection e canary de prompts

18 min de leitura·+90 XP
Pré-requisitos (0/2)0%

Recomendamos completar os pré-requisitos antes de seguir, mas nada te impede de continuar.

LLMOps é o que separa "demo de IA" de "sistema AI-native em produção". Este módulo fecha a trilha juntando os ingredientes operacionais: eval harness bloqueando deploy ruim, canary de prompts, drift detection (modelo, dado, RAG), cost attribution e SLO de qualidade. Como saber que o pipeline continua entregando.

LLMOps ≠ MLOps: o que muda

DimensãoMLOps clássicoLLMOps
Artefato versionadoPesos do modeloPrompts, tools, RAG config, eval sets
Quem treinaVocêProvider (Anthropic/OpenAI/Google)
Origem de driftDado ou pipelineProvider muda snapshot, dado muda, prompt muda
EvalTest set estáticoEval harness com LLM-as-judge + métricas online
CustoTreino (capex) + inferênciaInferência variável + cache + batch
RollbackVoltar pesos antigosVoltar prompt + pin de snapshot
💡
Algumas dores são as mesmas (observability, deploy gradual, SLO), mas os artefatos diferem. Um PR que muda 3 linhas de system prompt é um "modelo novo" em LLMOps — merece eval e canary como qualquer release de modelo.

Arquitetura de plataforma LLMOps

🗺️ Stack mínima de LLMOps

  ┌────────────────────────────────────────────────────┐
  │ PROMPT / CONFIG REGISTRY                           │
  │ Git + versionado: system, tools, rag_version       │
  └────────────────────┬───────────────────────────────┘
                       │ referência por hash
  ┌────────────────────▼───────────────────────────────┐
  │ EVAL HARNESS (CI)                                  │
  │ golden set (100-500) · retrieval + gen metrics     │
  │ bloqueia PR se regressão > threshold               │
  └────────────────────┬───────────────────────────────┘
                       │ passa → deploy
  ┌────────────────────▼───────────────────────────────┐
  │ CANARY ROUTER                                      │
  │ 1% → 10% → 50% → 100%    rollback automático       │
  └────────────────────┬───────────────────────────────┘
                       │
  ┌────────────────────▼───────────────────────────────┐
  │ OBSERVABILITY                                      │
  │ traces (Langfuse/LangSmith) · metrics (p50/p95,    │
  │ tokens, cost, cache ratio) · online signals        │
  └────────────────────┬───────────────────────────────┘
                       │ feedback → golden set
  ┌────────────────────▼───────────────────────────────┐
  │ DRIFT MONITORS                                     │
  │ modelo · distribuição de queries · RAG staleness   │
  └────────────────────────────────────────────────────┘

Prompt registry: versionar prompts como código

Prompts devem viver em repo, revisados em PR, versionados por hash. Nunca edite prompt direto em dashboard "sem deploy" — reproduzibilidade é perdida.

python
# Estrutura simples
#   prompts/
#     extract_ticket/
#       v3.md          ← prompt em markdown com frontmatter YAML
#       v3.json        ← schema de input/output
#       v3.eval.jsonl  ← golden set opcional específico deste prompt
from pathlib import Path
import hashlib, yaml, json

class Prompt:
    def __init__(self, path: Path):
        text = path.read_text()
        if text.startswith("---"):
            meta_block, body = text.split("---", 2)[1:]
            self.meta = yaml.safe_load(meta_block)
        else:
            self.meta, body = {}, text
        self.body = body.strip()
        self.hash = hashlib.sha256(self.body.encode()).hexdigest()[:12]

    def format(self, **kwargs) -> str:
        return self.body.format(**kwargs)

# Uso
p = Prompt(Path("prompts/extract_ticket/v3.md"))
prompt_text = p.format(ticket=ticket_text)
# Log: prompt_hash=p.hash, version=p.meta.get("version"), model=p.meta.get("model")

Eval harness em CI: bloqueie PR ruim

Coberto no módulo de RAG evaluation. Princípio operacional: golden set ≥ 100 itens, baseline committed no repo, CI compara current vs baseline, falha se regressão > threshold.

yaml
# promptfoo é uma das opções prontas — config declarativa
prompts: [file://prompts/extract_ticket/v3.md]
providers:
  - id: anthropic:messages:claude-sonnet-4-6
    config: { temperature: 0 }

tests:
  - description: ticket simples
    vars: { ticket: "Meu cartão não passou na renovação" }
    assert:
      - type: javascript
        value: output.includes("billing") && output.includes("card_declined")
      - type: latency
        threshold: 3000
      - type: cost
        threshold: 0.01

defaultTest:
  assert:
    - type: llm-rubric
      value: A resposta deve ser JSON válido com campos category, urgency (1-5), summary.
      provider: openai:chat:gpt-4o-mini
FerramentaForte emFraco em
promptfooConfig declarativa, fácil CI, múltiplos providersGolden set grande vira YAML gigante
LangSmith (LangChain)Traces + datasets integrados; UI ricaEcosistema LangChain; pricing
Langfuse (open-source)Self-host, traces, datasets, scoresMenos batteries-included que LangSmith
RAGASMétricas RAG canônicas em códigoMenos infra de dataset e CI
Custom (pytest + jsonl)Máximo controleVocê reimplementa tudo

Canary de prompt: rollout gradual e automático

Novo prompt entra em produção atendendo a uma fração de tráfego. Comparamos métricas com grupo de controle (prompt atual). Se degradação, rollback. Se OK, subimos o percentual.

python
# Rota simples com sticky assignment por user_id
import hashlib

def canary_prompt(user_id: str, percent: int, control_hash: str, canary_hash: str) -> str:
    bucket = int(hashlib.sha256(user_id.encode()).hexdigest(), 16) % 100
    return canary_hash if bucket < percent else control_hash

# Ao chamar LLM, log:
#   variant = "control" | "canary"
#   quality_score (estimado via eval online ou modelo judge)
#   cost_usd
#   latency_ms

# Regra de promoção (pseudo)
#   se canary_sample > 1000 E
#      quality_delta > -2pp E
#      cost_delta < +15% E
#      latency_p95_delta < +200ms:
#     promover percent +20 até 100
#   senão:
#     rollback para 0

📋 Mudança de system prompt em endpoint de alto tráfego (customer support)

Canary 1% → 10% → 50% → 100% em 48-72h com rollback automático

Mudança parece inofensiva mas pode quebrar edge cases invisíveis ao eval offline (ex: novas intents). Canary expõe gradualmente e rollback em minutos protege receita.

Alt: Deploy direto após eval offlineok só em endpoints de baixo risco com eval forte

Alt: A/B 50/50 fixoexperimentação estatística; não é o mesmo que safety canary

Drift detection: o sinal que aparece antes do ticket

Fonte de driftSensorAlert
Modelo (provider mudou)Pin de snapshot (não usar "latest"); re-eval semanal contra goldenDiff de quality vs baseline > 3pp
Query distributionDistância (KL/PSI) entre vetores/tópicos de queries hoje vs semana passadaDistância acima de threshold
RAG stalenessData média dos chunks usados; hit ratio em novas queriesHit ratio caindo ou data média muito antiga
Tool driftTaxa de tool_use errada ou validation errorAumento súbito de tool errors
Usuário refina muitoTaxa de follow-up/re-prompt em conversaSobe = qualidade caiu
Custo por taskTokens médios/queryJump sugere loops ou context growth
python
# Exemplo simples de drift por distribuição de tópicos (PSI)
import numpy as np

def psi(expected: np.ndarray, actual: np.ndarray, eps=1e-6) -> float:
    expected = np.clip(expected, eps, None); expected /= expected.sum()
    actual   = np.clip(actual,   eps, None); actual   /= actual.sum()
    return float(np.sum((actual - expected) * np.log(actual / expected)))

# Usa topic distribution (cluster de embeddings) em janela móvel
# PSI < 0.1 estável, 0.1-0.25 atenção, > 0.25 drift significativo

Cost attribution e FinOps de LLM

DimensãoTag / labelPra que serve
endpoint / rotaendpoint=search_ragIdentificar endpoint caro
feature / produtofeature=summarize_monthlyQuanto cada feature custa
tenant / clientetenant=acme (se multi-tenant)Chargeback ou ROI por cliente
modelmodel=sonnet-4-6Impacto de mudança de modelo
prompt_hashprompt_hash=ab12cd34Correlacionar custo com versão de prompt
variantvariant=canaryComparar canary vs control
Com essas labels em logs/traces, você responde "feature X custou quanto por usuário em abril?" em um dashboard. Sem labels, fica "uma conta só" e FinOps vira brigar com o fornecedor.

SLO de qualidade: o que prometer

SLO de LLM combina três famílias de métricas: qualidade, performance, custo. Cada uma com objetivo e janela clara. Exemplo para um assistente de suporte:

  • Qualidade: faithfulness ≥ 0.90 (LLM-as-judge, amostra diária de 50 conversas) em janela de 30 dias.
  • Qualidade online: thumbs-up rate ≥ 75% nas respostas avaliadas pelo usuário.
  • Latência: TTFT p95 ≤ 1200ms; total p95 ≤ 8s.
  • Custo: USD por conversa resolvida ≤ 0.05 (mediana mensal).
  • Disponibilidade: ≥ 99.5% (provider fail + retry transparente).
💡
SLO sem error budget é aspiracional. Defina budget mensal ("podemos ter até 1% abaixo do threshold"); quando consumir, congele deploys de prompt/config até restaurar.

Incident playbook: o que fazer quando cai

SintomaPrimeira açãoInvestigação
Quality score caiu em dashboardRollback do último prompt releaseDiff de prompt, snapshot do modelo, composição de queries
Custo pulou 2×Verificar token/query por endpointLoops? Context growth? Cache invalidado?
Latência estourouChecar provider status; fallback para outro modelo/providerRate limit? Upstream? Streaming quebrou?
Usuário reporta resposta erradaPegar trace pelo request_idReprovar em golden set; adicionar caso ao set
429 em massaLigar rate limiter local; considerar batch para jobs não-críticosSubir quota no provider

Perguntas típicas

Preciso mesmo pinar snapshot de modelo?

Sim. "claude-sonnet-4" (alias) pode passar para nova versão silenciosamente. "claude-sonnet-4-6" (snapshot) é estável até ser descontinuado com aviso. Canary de upgrade de snapshot é uma operação — não deixe a decisão com o alias.

Quando vale investir em plataforma LLMOps própria?

Quando você tem múltiplas features críticas de LLM (3+), múltiplos times mexendo em prompts, e gasta >US$5k/mês em API. Antes disso, Langfuse self-host + promptfoo + um golden set bem curado cobre 80% pelo custo de uma tarde de setup.

LLM-as-judge em produção é confiável para alertar?

Para alertar, sim, desde que calibrado com humanos e com rubrica. Para acionar rollback automático sem humano no loop, recomendo calibração mais forte e combinação com sinal online (thumbs, refinement rate). Nunca confie em um juiz só para decisão crítica.

Como convenço o time a investir em eval harness antes de feature nova?

Com um incident. Meio sério: trace o último bug crítico em LLM, calcule o custo de MTTR, mostre que eval harness teria pegado antes. Se não tiver incident, faça um exercício: aplique eval em uma versão antiga de prompt — a chance de achar regressões esquecidas é altíssima.
Take-aways. LLMOps versiona prompts/tools/configs, não pesos. Eval harness em CI bloqueia regressão conhecida; monitoring online captura drift do mundo. Canary (1→10→50→100%) com rollback automático é obrigatório em tráfego de produção. Drift vem do modelo (pin snapshot), da query (distribuição), do RAG (staleness) — três sensores separados. Cost attribution com labels responde FinOps em minutos. SLO de qualidade + error budget separa engenharia séria de "deploy and pray". Com isso a trilha fecha: do RAG fundamental ao sistema AI-native em produção, rodando com eval, canary e SLO.
🧩

Quiz rápido

4 perguntas · Acerte tudo e ganhe o badge 🎯 Gabarito

Continue lendo