DEV M4 · Production Engineering, Evals & Security — Resumen de estudio

Curso 4 de 5 · 22 pantallas

Cómo probar que una feature aguanta tráfico real: evals, tests y tracing, manejo de fallos, selección de modelo, costo y orquestación, y seguridad. Cada capa cierra una forma en que el desarrollo esconde lo que producción revela.


1. Evals y jueces

El grader según la forma del output:

TareaMétodoDetectaFalla en
Una sola respuesta correctaExact/string matchRespuesta incorrecta, costo casi ceroCualquier paráfrasis o reordenamiento
Output estructurado o códigoCode-graded (parsear JSON o Python, rangos, campos requeridos)Formato y sintaxisNo dice si el contenido es bueno
Calidad abiertaLLM-as-judgeFidelidad, seguimiento de instrucciones, completitud, tonoRuidoso y caro; el número no significa nada hasta que lo calibras

⚠️ Watch out: "I placed my order on March 3 but did not receive it until April 12" → extrajo April 12. La validación confirma la forma, no el valor correcto. No había un set calificado con mensajes de dos fechas. Pide al modelo que enumere edge cases (dos fechas, ninguna fecha, "next Tuesday") y conviértelos en casos calificados.

Checkpoint (eval de resúmenes):


2. Testing y tracing

NivelAíslaNo detecta
UnitUna función (parser, wrapper de una tool)Cómo encajan las piezas
FunctionalQue una llamada a Claude devuelva la forma esperadaEl sistema alrededor
IntegrationEl punto de unión entre dos componentes (retrieval → modelo). Aquí se esconden la mayoría de los fallos silenciososEl comportamiento de todo el flujo
End-to-endEl flujo completo como lo usa un usuarioDónde está el fallo

⚠️ Watch out: el unit y el functional pasaban y el e2e fallaba. retrieve() devolvía [{"content": ...}] y build_prompt() esperaba un string, así que el modelo respondía de memoria. Nunca se definió el contrato de formato en esa unión.

Checkpoint: C. Extraer c["content"], alinear la unión y añadir un integration test sobre retrieve() → build_prompt().


3. Manejo de fallos

La primera pregunta: ¿esperar y reintentar podría funcionar?

RETRIABLE = {429, 529, 500, 502, 503, 504}   # rate limit, overload, transitorios
TERMINAL  = {400, 401, 403, 404}             # bad request, auth, permisos, no existe
ErrorTipoBackoffFallback
429 rate limitRetriableExponencial + jitter, respeta retry-after, intentos con topeError limpio o resultado cacheado o más simple
529 overloadedRetriableBackoff (carga del lado de Anthropic, no es rate limit)Ruta alternativa o error elegante
400Falla rápidoNingunoArreglar o rechazar el input
Error de toolDependeSolo si la causa es transitoriais_error de vuelta a Claude
Refusal (200)Falla rápidoNingunoSubirlo y registrarlo

⚠️ Watch out: un loop sin manejo de errores cayó con el primer 429. Luego se pusieron retries inmediatos, que agravaron el rate limit.

Checkpoint (reparar el retry): el defecto es time.sleep(0) (sin backoff) y que reintenta cualquier excepción. Fix:


4. Selección de modelo en producción

Checkpoint:

  1. Millones de mensajes cortos y Haiku cumple en el eval → Haiku (costo a volumen).
  2. Refactor de varios pasos, un error temprano es caro y Sonnet falla los casos difíciles → Opus (calidad cuando equivocarse cuesta mucho).
  3. Tráfico mixto → router: Sonnet o Haiku por defecto + override a Opus en los requests complejos.

5. Costo y orquestación

⚠️ Watch out: un fan-out a subagents en una tarea secuencial → la factura se triplicó y la calidad casi no cambió. Los subagents se esperaban entre sí.

Checkpoint: lookup puntual en un corpus estable = C (single agent, modelo chico, fetch once) · investigación amplia divisible = A (orchestrator-worker) · respuesta que debe sentirse instantánea = D (streaming) · batch no urgente y sensible al costo = B (Batches + caching).


6. Seguridad

Prompt injection

Identidad y acceso

Hooks como guardrails

Revisión regulada

3 preguntas tempranas: ¿dónde se procesan los datos?, ¿cómo se registra el acceso? (audit log del hook), ¿se puede controlar la configuración centralmente? (managed config).

Sandbox a nivel de SO (el control residual)

Aislamiento de filesystem (solo el directorio de trabajo) y de red (solo endpoints permitidos), impuesto por el sistema operativo. Se mantiene aunque falte un hook o esté mal configurado. Es lo primero que pregunta un revisor de seguridad. Se configura en los settings de Claude Code.

AmenazaEntra porControlLog
Prompt injectionPáginas, docs o tool resultsContenido como datos + hook que rechaza acciones disparadas por input no confiableLa fuente, la acción intentada y el bloqueo
JailbreakEl prompt del usuarioValidar la entrada + limitar lo que el modelo puede hacerEl prompt marcado y el rechazo
Acceso demasiado amplioIdentidad con más permisos de los necesariosLeast privilege, secret manager, config de auth bloqueadaCada acción privilegiada y su identidad
Escape del sandboxAcceso de red o archivos fuera del límiteSandbox de SOCada intento denegado

⚠️ Watch out: "los usuarios son internos, no validamos las páginas". La instrucción venía de la página, no del usuario, y el agente escribió un archivo que nadie pidió. Fix: contenido como datos + hook delante de la tool de escritura.

Checkpoint (configuración mínima segura):

  1. Hook PreToolUse que deniega escrituras fuera de /workspace/output.
  2. Deny paths explícitos (/etc, /secrets, ~/.aws).
  3. Secreto desde os.environ.
  4. Audit log en cada acción privilegiada.

Tarea acumulativa (3 defectos, 3 capas)

  1. Eval: answer() no tiene un set calificado → ninguna regresión se detecta al cambiar el prompt o el modelo. Fix: holdout set calificado en cada cambio.
  2. Errores y costo: loop de 5 intentos con time.sleep(0) que reintenta todo, incluidos los 400. Fix: call_with_retry con backoff, retry-after y fallo rápido en terminales.
  3. Seguridad: write_file(page.suggested_path, ...) usa una ruta sacada del contenido no confiable. Fix: ruta fija /workspace/output/summary.txt + hook que deniega y audita.

Para recordar

  1. Fija el estándar (eval) antes de construir; el grader según el output; calibra al juez.
  2. El test según el fallo (integration para los puntos de unión) y el trace para saber dónde ocurrió.
  3. Clasifica cada fallo (retriable vs terminal), backoff con tope, is_error y fallbacks con nombre.
  4. Mide costo y latencia por llamada; fan-out solo si la tarea de verdad se divide (~15×).
  5. Contenido traído de fuera = datos; least privilege; hooks que bloquean y registran.