Idea en 30 segundos
Tool mocking y fault injection permiten reproducir errores de API de forma controlada y comprobar cómo los maneja el agente, sin red real y sin ruido no determinista.
El valor principal es reproducir de forma controlada timeout, 5xx o respuestas rotas, y verificar retry, fallback y stop reason.
Problema
Sin mocks y fault injection, el equipo suele ver solo happy path:
- la herramienta responde rápido;
- la respuesta es válida;
- el agente termina el run sin errores.
En producción esto es raro. Las herramientas pueden devolver timeout, fallos parciales, campos vacíos o latency inestable.
Sin pruebas dedicadas a fallos, esto suele provocar:
- caídas impredecibles en escenarios críticos;
- repeticiones infinitas de llamadas;
- incidentes costosos y ruidosos, difíciles de reproducir.
Cuándo usarlo
Este enfoque es necesario si el agente trabaja con herramientas externas:
- API de pagos, CRM, búsqueda, servicios backend;
- herramientas con retry/backoff;
- escenarios donde
stop_reasoncorrecto es clave; - escenarios con fallback (por ejemplo, herramienta de respaldo o respuesta segura).
Si el fallo de herramienta puede modelarse localmente, es un buen candidato para una prueba con fault injection.
Implementación
En la práctica, esto sigue una regla simple: un tipo de fallo, una prueba, condiciones controladas. Los ejemplos de abajo son esquemáticos y no dependen de un framework concreto.
Cómo funciona en una prueba
Ciclo corto de prueba con fault injection
- Test case - un comportamiento por validar.
- Mock tool - fijar contrato de input/output.
- Inject fault - inyectar fallo concreto (
timeout,5xx,bad_payload). - Run - ejecutar un paso concreto del agente.
- Assertions - verificar retry, fallback,
stop_reasony formato de error.
1. Fijar el contrato del mock tool
class FakePaymentsAPI:
def __init__(self, mode: str = "ok"):
self.mode = mode
def refund(self, order_id: str):
if self.mode == "ok":
return {"status": "approved", "order_id": order_id}
if self.mode == "timeout":
raise TimeoutError("payments_timeout")
if self.mode == "http_500":
raise RuntimeError("payments_500")
return {"status": None}
El mock debe reproducir el contrato real de la herramienta lo más fielmente posible. Si no, las pruebas generan falsa confianza.
2. Inyectar el fallo de forma controlada
def test_timeout_fault_is_injected():
payments = FakePaymentsAPI(mode="timeout")
agent = Agent(payments_api=payments)
result = agent.handle_refund("order-8472")
assert result.stop_reason in {"tool_error_handled", "fallback_used"}
El perfil de fallo debe ser explícito y repetible: la misma prueba debe reproducir siempre el mismo perfil.
3. Verificar retry y fallback
def test_retry_then_fallback():
payments = FlakyPaymentsAPI(fail_times=2, then="timeout")
backup = FakeBackupTool()
agent = Agent(payments_api=payments, backup_tool=backup, max_retries=2)
result = agent.handle_refund("order-9001")
assert payments.calls == 2
assert result.selected_tool == "backup_tool"
assert result.stop_reason == "fallback_used"
Es importante verificar no solo que hay error, sino también la política de recuperación posterior.
En flujos de retry, hay que verificar no solo el número de intentos, sino también las condiciones en las que el sistema deja de reintentar y pasa a fallback o fail.
En herramientas con efectos secundarios (cambios de estado), hay que verificar que retry no cree operaciones duplicadas.
4. Fijar la estructura del error
def test_error_envelope_is_stable():
payments = FakePaymentsAPI(mode="http_500")
agent = Agent(payments_api=payments)
result = agent.handle_refund("order-1122")
assert result.error["code"] == "tool_error"
assert result.error["tool"] == "payments_api"
assert result.error["retryable"] is True
Un formato de error estable simplifica debug, alertas y checks de regression.
5. Ejecutar estas pruebas en CI
Estas pruebas deben correr en cada PR mediante el paso estándar pytest en CI cuando los cambios tocan lógica de herramientas, retries o reglas de fallback.
Errores típicos
El mock no coincide con el contrato real
La prueba pasa, pero en producción el agente cae por estructura de campos distinta o por otro código de error.
Causa típica: el mock devuelve payload simplificado que no se parece a la API real.
Probar solo happy path
Las pruebas tienen solo "respuesta exitosa", sin timeout, 5xx ni payload inválido.
Causa típica: no existe lista obligatoria de perfiles de fallo por herramienta crítica.
Fault injection aleatoria
La misma prueba a veces pasa y a veces falla.
Causa típica: fallos aleatorios sin seed fijo o timeouts inestables.
Sin comprobar stop_reason y error shape
El equipo comprueba solo texto final, y el comportamiento de recovery queda sin cubrir.
Causa típica: faltan assertions estructurales para stop_reason, error.code, selected_tool.
Sin comprobar efectos secundarios en retry
El retry maneja el error formalmente, pero crea operación duplicada o escritura repetida.
Causa típica: se prueba solo stop_reason y fallback, pero no la idempotencia de la capa de herramientas.
Mezclar checks unit e integración
La prueba se llama unit, pero llama a API real.
Causa típica: no hay frontera entre pruebas locales (mocks/fault injection) y capa de integración.
Resumen
- Tool mocking y fault injection validan cómo el agente maneja fallos de herramientas.
- Un tipo de fallo debe cubrirse con una prueba determinista separada.
- Verifica no solo texto, también retry, fallback,
stop_reasony formato de error. - Las pruebas fault críticas deben correr en cada PR.
FAQ
Q: ¿Se pueden probar fallos sin API real?
A: Sí. En nivel unit es estándar: fakes y mocks dan una señal estable y reproducible.
Q: ¿Qué es más importante: retry o fallback?
A: Ambos. Retry cubre fallos cortos, y fallback protege el escenario cuando la herramienta principal no está disponible por más tiempo.
Q: ¿Cuántos perfiles de fallo debe tener cada herramienta?
A: Mínimo tres: timeout, error de servidor (5xx) y payload inválido.
Q: ¿Esto reemplaza eval harness y regression?
A: No. Estas pruebas cubren comportamiento local de la capa de herramientas. El comportamiento del sistema en escenarios completos lo validan eval harness y regression.
Qué sigue
Conecta los casos de fallo a Eval Harness y fíjalos en Golden Datasets. Para controlar cambios entre versiones, añade Regression Testing, y analiza incidentes con Replay and Debugging.