Deriva silenciosa del agente (regresión de calidad) + detección + código

  • Detecta el fallo temprano antes de que suba el gasto.
  • Entiende qué se rompe en producción y por qué.
  • Copia guardrails: budgets, stop reasons, validación.
  • Sabe cuándo esto no es la causa raíz.
Señales de detección
  • Tool calls por run suben (o repiten mismo args hash).
  • Gasto/tokens suben sin mejorar el resultado.
  • Retries pasan de raros a constantes (429/5xx).
Los agentes no fallan de golpe: derivan por cambios en modelo/tool/prompt hasta que shipeas una regresión. Canary, golden tasks, replay y métricas detectan drift temprano.
En esta página
  1. El problema (en producción)
  2. Por qué esto se rompe en producción
  3. 1) El output del modelo no es estable
  4. 2) Los tools también derivan
  5. 3) Los prompts son código (pero rara vez se tratan como tal)
  6. 4) Drift aparece en coste/latencia antes que en correctness
  7. 5) El fix es un feedback loop: golden tasks + replay + canary
  8. Ejemplo de implementación (código real)
  9. Incidente real (con números)
  10. Trade-offs
  11. Cuándo NO usarlo
  12. Checklist (copiar/pegar)
  13. Config segura por defecto (JSON/YAML)
  14. FAQ (3–5)
  15. Páginas relacionadas (3–6 links)
Flujo interactivo
Escenario:
Paso 1/2: Execution

Normal path: execute → tool → observe.

El problema (en producción)

“No cambiamos nada”.

Excepto que:

  • alguien retocó un prompt “un poco”
  • un tool empezó a devolver un campo nuevo
  • el modelo tuvo un version bump
  • tu índice de retrieval se actualizó

El agente sigue “funcionando”.

Pero es más lento. Llama tools diferentes. Toma decisiones distintas. Se come edge cases. Nadie lo nota hasta que un usuario lo nota — y los usuarios no son QA amable.

Esto es drift silencioso: el comportamiento cambia en producción sin un fallo obvio.

Por qué esto se rompe en producción

1) El output del modelo no es estable

Incluso sin cambios de versión, hay varianza. Con cambios de versión, el shift es seguro.

Si no mides el shift, no lo ves.

2) Los tools también derivan

Los outputs cambian:

  • evoluciona el schema
  • cambian payloads de error
  • cambia el ordering
  • cambian defaults

Si tu agente es sensible a eso, derivará.

3) Los prompts son código (pero rara vez se tratan como tal)

Edits de prompt suelen shippearse sin:

  • tests
  • rollbacks
  • canaries
  • métricas

Así obtienes “cambiamos una frase y ahora llama http.get 10x más”.

4) Drift aparece en coste/latencia antes que en correctness

Las señales tempranas suelen ser operacionales:

  • tokens/request suben
  • tool calls/run suben
  • p95 latencia sube
  • stop reasons cambian

Si solo miras “success rate”, lo pierdes.

5) El fix es un feedback loop: golden tasks + replay + canary

Necesitas un loop de eval con forma de producción:

  • golden tasks que representan workload real
  • replay de traces reales (redactado)
  • canary para cambios de modelo/prompt/tool
  • alertas por deltas de comportamiento

Ejemplo de implementación (código real)

Un harness mínimo de “golden tasks”:

  • corre tareas contra baseline y candidato
  • compara stop reasons y tool-call counts
  • falla si el delta pasa el umbral
PYTHON
from dataclasses import dataclass


@dataclass(frozen=True)
class GoldenTask:
  id: str
  input: str


def run_agent(version: str, task: GoldenTask) -> dict:
  # Pseudo: run your agent with pinned model/prompt/tools config.
  return agent_run(version=version, input=task.input)  # (pseudo)


def score(run: dict) -> dict:
  return {
      "stop_reason": run.get("stop_reason"),
      "tool_calls": int(run.get("tool_calls", 0)),
      "tokens": int(run.get("tokens_total", 0)),
  }


def drift_check(tasks: list[GoldenTask], *, baseline: str, candidate: str) -> None:
  for t in tasks:
      b = score(run_agent(baseline, t))
      c = score(run_agent(candidate, t))

      if c["stop_reason"] != b["stop_reason"]:
          raise RuntimeError(f"[{t.id}] stop_reason drift: {b['stop_reason']} -> {c['stop_reason']}")

      if c["tool_calls"] > b["tool_calls"] + 3:
          raise RuntimeError(f"[{t.id}] tool_calls drift: {b['tool_calls']} -> {c['tool_calls']}")
JAVASCRIPT
export function score(run) {
return {
  stopReason: run.stop_reason,
  toolCalls: Number(run.tool_calls || 0),
  tokens: Number(run.tokens_total || 0),
};
}

export function driftCheck(tasks, { baseline, candidate, runAgent }) {
for (const t of tasks) {
  const b = score(runAgent(baseline, t));
  const c = score(runAgent(candidate, t));

  if (c.stopReason !== b.stopReason) {
    throw new Error("[" + t.id + "] stop_reason drift: " + b.stopReason + " -> " + c.stopReason);
  }

  if (c.toolCalls > b.toolCalls + 3) {
    throw new Error("[" + t.id + "] tool_calls drift: " + b.toolCalls + " -> " + c.toolCalls);
  }
}
}

Esto es crudo a propósito. Aun así captura el drift más común:

  • stop reasons cambian (timeouts nuevos, loops nuevos)
  • inflación de tool calls (drift de coste)

Luego añades checks de correctness específicos del dominio. Pero empieza con drift operacional — es más fácil de medir y suele ser la primera señal.

Incidente real (con números)

Upgradeamos una versión de modelo para un agente de soporte. Sin canary, sin golden tasks.

El modelo nuevo era “mejor siendo minucioso”. También llamaba search.read más a menudo.

Impacto en 24 horas:

  • tool calls/run: 2.8 → 9.6
  • p95 latencia: 2.7s → 7.4s
  • gasto: +$460 vs baseline
  • la corrección no cayó de forma obvia, así que nadie lo vio hasta que lo vio la factura

Fix:

  1. golden tasks con umbrales de drift (tool calls, stop reasons)
  2. canary (1% de tráfico) con auto-rollback por spikes
  3. replay semanal de traces reales anonimizados
  4. dashboards: tokens, tool calls, stop reasons, latencia

El drift no es emocionante. Es como se rompe producción cuando nadie mira.

Trade-offs

  • Mantener suites de golden tasks cuesta tiempo.
  • Canary añade complejidad de rollout (vale la pena).
  • Parte del drift es “bueno” (mejores respuestas). Igual necesitas medir para decidir.

Cuándo NO usarlo

  • Si tu agente es informacional y de bajo riesgo, puedes ser más laxo (igual vigila gasto).
  • Si aún no tienes distribución estable de tareas, empieza con smoke tests y crece a golden tasks.
  • Si no puedes replay traces por PII, usa tareas sintéticas + budgets estrictos.

Checklist (copiar/pegar)

  • [ ] Golden tasks representando workload real
  • [ ] Replay set de traces reales (redactados)
  • [ ] Canary con triggers de rollback
  • [ ] Umbrales: tool calls, tokens, latencia, stop reasons
  • [ ] Versiones de modelo/prompt/tool fijadas por release
  • [ ] Review semanal de “qué cambió”

Config segura por defecto (JSON/YAML)

YAML
releases:
  canary_percent: 1
  rollback_on:
    tool_calls_per_run_increase_pct: 50
    tokens_per_request_increase_pct: 50
    latency_p95_increase_pct: 50
eval:
  golden_tasks_required: true
  drift_thresholds:
    tool_calls_delta: 3
    stop_reason_changes: 0

FAQ (3–5)

¿El drift siempre es malo?
No. Pero drift no medido sí es malo. Sin métricas no sabes si es mejora o regresión lenta y cara.
¿Qué monitorizo primero?
Tool calls/run, tokens/request, latencia p95 y stop reasons. Se mueven antes que quejas de correctness.
¿Necesito canary para cada cambio de prompt?
Para agentes con tráfico o impacto: sí. Trata prompts como cambios de código.
¿Cómo hago replay de traces de prod con seguridad?
Redacta PII, guarda hashes de args cuando puedas, y reusa resultados de tools desde snapshots.

Q: ¿El drift siempre es malo?
A: No. Pero drift no medido sí es malo. Sin métricas no sabes si es mejora o regresión lenta y cara.

Q: ¿Qué monitorizo primero?
A: Tool calls/run, tokens/request, latencia p95 y stop reasons. Se mueven antes que quejas de correctness.

Q: ¿Necesito canary para cada cambio de prompt?
A: Para agentes con tráfico o impacto: sí. Trata prompts como cambios de código.

Q: ¿Cómo hago replay de traces de prod con seguridad?
A: Redacta PII, guarda hashes de args cuando puedas, y reusa resultados de tools desde snapshots.

No sabes si este es tu caso?

Disena tu agente ->
⏱️ 6 min de lecturaActualizado Mar, 2026Dificultad: ★★☆
Implementar en OnceOnly
Guardrails for loops, retries, and spend escalation.
Usar en OnceOnly
# onceonly guardrails (concept)
version: 1
budgets:
  max_steps: 25
  max_tool_calls: 12
  max_seconds: 60
  max_usd: 1.00
policy:
  tool_allowlist:
    - search.read
    - http.get
controls:
  loop_detection:
    enabled: true
    dedupe_by: [tool, args_hash]
  retries:
    max: 2
    backoff_ms: [200, 800]
stop_reasons:
  enabled: true
logging:
  tool_calls: { enabled: true, store_args: false, store_args_hash: true }
Integrado: control en producciónOnceOnly
Guardrails para agentes con tool-calling
Lleva este patrón a producción con gobernanza:
  • Presupuestos (pasos / topes de gasto)
  • Kill switch y parada por incidente
  • Audit logs y trazabilidad
  • Idempotencia y dedupe
  • Permisos de herramientas (allowlist / blocklist)
Mención integrada: OnceOnly es una capa de control para sistemas de agentes en producción.
Ejemplo de policy (concepto)
# Example (Python — conceptual)
policy = {
  "budgets": {"steps": 20, "seconds": 60, "usd": 1.0},
  "controls": {"kill_switch": True, "audit": True},
}
Autor

Esta documentación está curada y mantenida por ingenieros que despliegan agentes de IA en producción.

El contenido es asistido por IA, con responsabilidad editorial humana sobre la exactitud, la claridad y la relevancia en producción.

Los patrones y las recomendaciones se basan en post-mortems, modos de fallo e incidentes operativos en sistemas desplegados, incluido durante el desarrollo y la operación de infraestructura de gobernanza para agentes en OnceOnly.