Caída parcial: cuando falla parte del sistema de agentes

Una caída parcial ocurre cuando solo una parte del sistema de agentes deja de funcionar mientras el resto sigue disponible. Qué la causa y cómo detectarla.
En esta página
  1. El problema
  2. Por qué pasa
  3. Fallos más frecuentes
  4. Trampa de éxito intermitente (Intermittent success trap)
  5. Retry amplification entre capas
  6. La cola se bloquea por runs "ruidosos" (Queue starvation)
  7. Esperar respuesta "perfecta" sin degrade path
  8. Cómo detectar estos problemas
  9. Cómo distinguir partial outage de full outage
  10. Cómo frenar estos fallos
  11. Dónde se implementa en la arquitectura
  12. Autoevaluación
  13. FAQ
  14. Páginas relacionadas

El problema

La solicitud parece simple: verificar estado de pago y dar una respuesta breve al cliente.

En trazas se ve otra cosa: en 11 minutos un run hizo 33 llamadas, 10 devolvieron 200, 14 devolvieron timeout, y 9 devolvieron 502/503. Para una tarea de esta clase puede ser alrededor de ~$1.90 en lugar de ~$0.14 habitual.

El servicio está formalmente "vivo": parte de las llamadas funciona, no hay caída total. Pero la cola de runs crece, la latency salta y los usuarios reciben resultados inestables.

El sistema no cae.

Solo se atasca lentamente entre éxitos raros y fallos repetidos.

Analogía: imagina una caja donde el terminal a veces acepta tarjeta y a veces se cuelga. La tienda no está cerrada, pero la fila crece cada minuto. Una caída parcial en sistemas de agentes funciona igual: la infraestructura parece disponible, pero ya no existe un camino estable hacia la respuesta.

Por qué pasa

En producción normalmente sucede así:

  1. una dependencia empieza a comportarse de forma inestable (timeout, 5xx, a veces 200);
  2. retries arrancan en varias capas a la vez;
  3. el run retiene workers más tiempo, la cola crece;
  4. otros workflows también se ralentizan por recursos compartidos;
  5. sin fail-fast y safe-mode, el sistema multiplica costo en vez de aislar el fallo.

En trace esto aparece como patrón mixto: tool_2xx_rate todavía existe, pero al mismo tiempo suben timeout_rate, retry_attempts_per_run y queue_backlog.

El problema no es un solo timeout.

Runtime no pasa la dependencia inestable a degraded mode mientras el fallo todavía es local.

Fallos más frecuentes

En producción se ven cuatro patrones de partial outage con más frecuencia.

Trampa de éxito intermitente (Intermittent success trap)

El tool a veces devuelve 200, y eso enmascara la degradación. El agente sigue insistiendo por el mismo canal en vez de cambiar de forma controlada.

Causa típica: no hay umbral de "salud" de dependencia a nivel run.

Retry amplification entre capas

Cliente HTTP, gateway y runtime ejecutan retries por separado. Incluso un pico pequeño de errores se vuelve rápido una ola de llamadas innecesarias.

Causa típica: retry policy no centralizada.

La cola se bloquea por runs "ruidosos" (Queue starvation)

Los runs problemáticos quedan colgados mucho tiempo, ocupan worker pool y desplazan tareas sanas.

Causa típica: faltan límites de duración de run y budget-gates.

Esperar respuesta "perfecta" sin degrade path

El sistema intenta esperar un resultado "ideal", aunque la dependencia está claramente degradada.

Causa típica: no existe contrato partial/fallback para usuario.

Cómo detectar estos problemas

La caída parcial se ve bien con combinación de métricas de health, runtime y queue.

MétricaSeñal de partial outageQué hacer
degraded_dependency_rateuna dependencia da timeout/5xx con frecuenciaactivar degraded mode y reducir fan-out
tool_2xx_with_high_timeout_rateaparecen juntos 200 y alta tasa de timeoutagregar umbral de health, no mirar solo 200
retry_attempts_per_rundemasiados reintentos para un runcentralizar retries y limitar retry budget
run_duration_p95runs largos "colgados"introducir fail-fast timeout y stop reasons
queue_backlogla cola crece con tráfico normalaislar ruta degradada y activar fallback

Cómo distinguir partial outage de full outage

No toda degradación es caída total. La pregunta clave: existe camino estable de ejecución o solo "éxitos" aleatorios.

Normal para full outage si:

  • casi todas las llamadas fallan de forma uniforme (5xx o indisponibilidad total);
  • el sistema pasa rápido a fail-fast;
  • no existe ilusión de "a veces funciona".

Peligroso para partial outage si:

  • en el mismo run se mezclan 200, timeout y 5xx;
  • el agente repite llamadas porque ve éxitos raros;
  • queue/latency suben incluso sin global incident claro.

Cómo frenar estos fallos

En práctica:

  1. fijar health snapshot de dependencias al inicio del run;
  2. al romper umbral, pasar workflow a degraded mode de inmediato;
  3. mantener retries en un solo tool gateway con budget estricto;
  4. devolver partial/fallback y stop reason explícito en vez de esperar "eternamente".

Guard mínimo para partial outage:

PYTHON
from dataclasses import dataclass
import time


RETRYABLE = {408, 429, 500, 502, 503, 504}


@dataclass(frozen=True)
class OutageLimits:
    max_retry_per_call: int = 2
    max_retry_total: int = 6
    max_run_seconds: int = 45
    max_tool_calls: int = 14
    degraded_error_threshold: float = 0.35
    min_sample_size: int = 5


class PartialOutageGuard:
    def __init__(self, limits: OutageLimits = OutageLimits()):
        self.limits = limits
        self.started_at = time.time()
        self.tool_calls = 0
        self.retry_count = 0
        self.total_calls = 0
        self.error_calls = 0

    def before_tool_call(self) -> str | None:
        self.tool_calls += 1
        if self.tool_calls > self.limits.max_tool_calls:
            return "partial_outage:tool_call_budget"
        if (time.time() - self.started_at) > self.limits.max_run_seconds:
            return "partial_outage:run_timeout"
        return None

    def on_tool_result(self, status_code: int, attempt: int) -> str | None:
        self.total_calls += 1

        if status_code in RETRYABLE:
            self.error_calls += 1
            error_rate = self.error_calls / max(1, self.total_calls)
            if (
                self.total_calls >= self.limits.min_sample_size
                and error_rate >= self.limits.degraded_error_threshold
            ):
                return "partial_outage:degraded_mode"
            self.retry_count += 1
            if self.retry_count > self.limits.max_retry_total:
                return "partial_outage:retry_budget"
            if attempt >= self.limits.max_retry_per_call:
                return "partial_outage:retry_exhausted"
            return "partial_outage:retry_allowed"

        error_rate = self.error_calls / max(1, self.total_calls)
        if (
            self.total_calls >= self.limits.min_sample_size
            and error_rate >= self.limits.degraded_error_threshold
        ):
            return "partial_outage:degraded_mode"

        return None

En esta versión, retry_count cuenta todas las respuestas retryable dentro del run, y attempt es la cantidad de reintentos de una llamada concreta.

Este es un guard base. En producción normalmente se amplía con per-tool health probes, circuit breaker y ruta safe-mode separada para runs degradados. before_tool_call(...) y on_tool_result(...) se llaman en tool gateway, para que la decisión de degradación sea centralizada y no por capa.

Dónde se implementa en la arquitectura

En producción, el control de partial outage casi siempre se reparte entre tres capas del sistema.

Tool Execution Layer aporta health signals: error rate, timeout patterns, retry budget y circuit breaker. Ahí se ve que una dependencia ya está inestable, incluso si parte de llamadas aún devuelve 200.

Agent Runtime toma decisiones por run: paso a degraded mode, stop reasons y cierre controlado con fallback. Sin esta capa, el sistema sigue esperando "una llamada exitosa más".

Orchestration Topologies define cómo aislar workflow degradado del resto del sistema (bulkheads, colas, prioridades). Esto evita que degradación local se vuelva incidente compartido.

Autoevaluación

Verificación rápida antes del release. Marca los puntos y mira el estado abajo.
Este es un sanity-check corto, no una auditoría formal.

Progreso: 0/8

⚠ Hay señales de riesgo

Faltan controles básicos. Cierra los puntos clave del checklist antes del release.

FAQ

Q: ¿Por qué partial outage suele ser peor que full outage?
A: Porque se enmascara como "a veces funciona". El sistema no se detiene y sigue quemando tiempo, tokens y worker pool.

Q: ¿Hay que desactivar de inmediato el tool degradado?
A: No siempre. Lo normal es activar degraded mode: limitar retries, bajar fan-out y pasar a ruta partial/fallback.

Q: ¿Dónde conviene decidir retries y degradación?
A: En un solo tool gateway. Si no, cada capa hace sus retries y partial outage escala rápido.

Q: ¿Qué mostrar al usuario cuando la dependencia degrada?
A: Stop reason explícito, qué falló exactamente y siguiente paso controlado: respuesta parcial o nuevo intento tras recuperación.


Partial outage casi nunca parece un choque ruidoso. Es degradación silenciosa: el sistema aún se mueve, pero ya no sostiene calidad ni ritmo. Por eso los agentes en producción necesitan no solo retries, sino modo de degradación estricto y aislamiento de dependencias.

Páginas relacionadas

Si este problema aparece en producción, también conviene revisar:

⏱️ 8 min de lectura • Actualizado 12 de marzo de 2026Dificultad: ★★☆
Implementar en OnceOnly
Failure mode handling becomes repeatable: event logs, timeline, and kill switch are already available.
Usar en OnceOnly
from onceonly import OnceOnly

client = OnceOnly(api_key="once_live_...")
run_id = "run_failover_2026_04_16"

client.post_event(run_id=run_id, type="start", status="in_progress")
result = client.ai.run_tool(
    agent_id="ops-agent",
    tool="send_alert",
    args={"severity": "critical", "channel": "pager"},
    run_id=run_id,
)

if not result.allowed:
    client.gov.disable_agent("ops-agent", reason=result.policy_reason or "policy_block")

timeline = client.get_run_timeline(run_id, limit=200, offset=0)
Integrated: failure containmentOnceOnly
Handle retries and incidents with operational controls
Use built-in run events, timeline traces, and kill switch flows instead of ad-hoc failure plumbing:
  • Prevent duplicate side effects during retries.
  • Log failure context with `post_event(...)`.
  • Investigate with `get_run_timeline(...)`.
  • Stop unsafe agents immediately via kill switch.
  • Resume safely after remediation.
Mención integrada: OnceOnly es una capa de control para sistemas de agentes en producción.
Failure Handling (Python SDK)
from onceonly import OnceOnly

client = OnceOnly(api_key="once_live_...")
run_id = "run_incident_001"

try:
    res = client.ai.run_tool(
        agent_id="payments-agent",
        tool="stripe.charge",
        args={"order_id": "ord_991"},
        run_id=run_id,
        spend_usd=0.2,
    )
    if not res.allowed:
        client.post_event(run_id=run_id, type="policy_block", message=res.policy_reason)
except Exception as exc:
    client.post_event(run_id=run_id, type="tool_error", message=str(exc))
    client.gov.disable_agent("payments-agent", reason="incident:tool_error_spike")
    raise

Autor

Nick — ingeniero que construye infraestructura para agentes de IA en producción.

Enfoque: patrones de agentes, modos de fallo, control del runtime y fiabilidad del sistema.

🔗 GitHub: https://github.com/mykolademyanov


Nota editorial

Esta documentación está asistida por IA, con responsabilidad editorial humana sobre la exactitud, la claridad y la relevancia en producción.

Los ejemplos son educativos y pueden utilizar herramientas y datos simulados. Antes de usarlos en producción, verifica la fiabilidad, la seguridad y la recuperación en tu propio entorno.