Volver a la ficha del curso IA en producción y en la empresa

Módulo 1 Del webhook al trabajo que sobrevive a los fallos

Empieza aquí: NexoCare, el proyecto y cómo llamamos a tu Claude

14 min

Viernes de cierre de trimestre. El CRM de NexoCare tiene problemas y empieza a reenviar sus avisos: 1.800 entregas que en realidad son 620 eventos distintos. Un worker escribe en el CRM, se le acaba el tiempo de espera justo después, y el reintento termina con dos tareas contradictorias para la misma cuenta. Reconstruir qué pasó cuesta 96 minutos, porque el aviso, el flujo interno y el recibo del CRM no comparten ningún identificador.

NexoCare es un caso ficticio, pero cada fallo sale de comportamientos que documentan proveedores reales como Stripe, CloudEvents, AWS o Temporal. En este módulo construyes la cadena que evita esa tarde.

El caso

NexoCare vende software a otras empresas (un SaaS B2B). Cuando un cliente escribe en el CRM, el CRM avisa a tu aplicación con un webhook, una petición HTTP que otro sistema te envía cuando ocurre algo. Un modelo lee el mensaje y, si detecta intención de baja, la aplicación propone marcar la cuenta como at_risk (en riesgo) para que el equipo de Customer Success intervenga.

Hasta aquí, nada que no sepas hacer desde el nivel 2, «Construir con LLMs»: llamar al modelo, pedirle una salida con schema y usarla. Lo nuevo es lo que pasa alrededor cuando el sistema falla de verdad:

  • el mismo aviso llega tres veces;
  • el mensaje 19 llega antes que el 18;
  • el worker (el proceso que hace el trabajo en segundo plano) se cae a mitad;
  • un cliente con mucho volumen satura la cola y los demás esperan.

La cadena que vas a montar

bytes recibidos y firmados
  → schema versionado
  → inbox (bandeja de entrada) con clave source + id
  → cola con límites
  → reserva con plazo (claim con lease)
  → flujo con versiones
  → aprobación o cancelación

Cada pieza responde a una pregunta. ¿Esto lo ha enviado de verdad el CRM? ¿Ya lo había recibido? ¿Quién lo está procesando? ¿En qué paso estaba cuando se cayó? ¿Cuánto trabajo acepto antes de decir que no? Las verás de una en una en las lecciones siguientes. La cancelación entra en este módulo; la aprobación, en el siguiente.

Quién decide qué

El reparto es estricto. La aplicación conserva la identidad del cliente, los permisos, el estado del flujo y los efectos en otros sistemas. El modelo solo clasifica el mensaje y propone una categoría, un nivel de riesgo y la frase que lo justifica.

Esa separación evita que un texto generado se convierta en una autorización. Si el modelo devuelve "authorized": true, la aplicación rechaza la respuesta entera en lugar de obedecerla.

Dos caminos para la misma cadena

El proyecto tiene dos formas de recorrer la cadena, con el mismo dominio por debajo:

  • La simulación, con almacenes y workers en memoria. Así ves cada contrato en la terminal sin montar una base de datos ni un broker (el servicio que gestiona colas de mensajes). Es lo que usan las lecciones 2 a 9, los tests y las demos, y responde igual en cada pasada.
  • El camino real, en la lección 10. El webhook entra por HTTP, el inbox vive en PostgreSQL, la cola es BullMQ sobre Redis y el clasificador es tu Claude. Matas un worker a mitad de un trabajo y miras qué queda.

La simulación tiene un límite claro: no demuestra persistencia entre varios procesos ni sustituye a PostgreSQL, BullMQ o Temporal. Te deja saber qué estado tiene que sobrevivir. El camino real lo comprueba con procesos que mueren de verdad.

Prepara el proyecto

Proyecto de este módulo: enterprise-integration. Necesitas Node 22.12 o posterior, como pide su package.json, en macOS, Linux o Windows con WSL. Descárgalo desde la ficha del curso (apartado «El código», con tu sesión iniciada; o pídele el enlace a tu Claude conectado al curso) y, dentro de su carpeta, instala las dependencias:

npm ci

Todos los comandos del módulo se ejecutan en esa carpeta raíz. Para la simulación no necesitas clave de API ni conexión: el clasificador es determinista (responde siempre lo mismo para el mismo texto) para que los resultados se puedan repetir. Para el camino real de la lección 10 necesitas además Docker y Claude Code con tu sesión iniciada; tampoco pide clave de API. Cómo se llama a tu Claude, lo que cuesta y qué hacer si tu plan no llega está al final de esta lección, en «Cómo llamamos a tu Claude».

Comprueba que todo está en su sitio:

npm run typecheck
npm test

Qué deberías ver: el compilador de TypeScript sin errores y, al final de los tests, Test Files 12 passed (12) y Tests 102 passed (102). Ningún test sale a la red ni llama a Claude. Si algo falla aquí, revisa la instalación antes de seguir.

Estos dos comandos bastan para saber que el proyecto está sano. La comprobación completa es npm run check, la que propone el README: además del compilador y los tests, revisa el formato, ejecuta las progresiones del starter a la solución, el benchmark y las ocho demos. Tarda más y no hace falta ahora. Si lo lanzas, verás STUDENT_GREEN_INBOX_CLAIM, STUDENT_GREEN_SCHEMA_GATE y STUDENT_GREEN_EFFECT aunque no hayas tocado los ejercicios: esa parte pasa las soluciones de solucion/ por los verificadores, para comprobar que el proyecto está completo. Tus ejercicios los comprueban student:a:verify, student:schema:verify y student:b:verify.

Lo que hay dentro:

  • src/: el dominio (webhook, inbox, cola, máquina de estados), sin el SDK de ningún proveedor.
  • src/live/ y compose.yaml: el camino real, con PostgreSQL, BullMQ y tu Claude.
  • tests/: casos de fallo provocados a propósito.
  • demos/parte-1/: las cuatro demos de este módulo.
  • starter/ y solucion/: los ejercicios que vas a resolver y su referencia.

Lo que te llevas

Al terminar sabrás responder a una pregunta que vas a repetir mucho en este nivel:

Si el proceso cae ahora, ¿qué registro me dice cuál es el siguiente paso seguro, sin repetir nada?

Si la respuesta es «una variable en memoria», todavía no tienes un trabajo durable.

Las dos secciones que siguen son de referencia para todo el curso. Las lecciones te mandan aquí cuando las necesitas.

Cómo llamamos a tu Claude

Varias lecciones tienen un apartado opcional que llama a un modelo de verdad: tu Claude, el de tu suscripción de pago (Pro o superior), con Claude Code y tu sesión iniciada (claude y, dentro, /login). No hace falta clave de API. Los cuatro proyectos lo hacen igual, y por eso se explica una sola vez.

La llamada

Por cada llamada, el script del proyecto lanza Claude Code en modo no interactivo (claude -p) desde una carpeta temporal vacía:

claude -p "<petición>" --output-format json
  --model <ID completo> --effort low
  --system-prompt "<el prompt del proyecto>" --json-schema "<la forma de la respuesta>"
  --tools "" --max-turns 3 --max-budget-usd <tope>
  --safe-mode --strict-mcp-config --no-session-persistence

Cada opción tiene un motivo:

  • --output-format json devuelve, además de la respuesta, los tokens, el coste estimado (total_cost_usd) y la duración.
  • --json-schema fija la forma de la respuesta, que llega en structured_output. Fija la forma y no el contenido: lo que se ejecuta lo sigue decidiendo tu código.
  • --tools "" deja a tu Claude sin herramientas. Solo contesta o propone.
  • --max-turns 3 y --max-budget-usd acotan los turnos y el gasto de cada llamada. Con claude -p llamas a un agente entero, y un agente sin límites puede dar vueltas. El tope se compara con la estimación de Claude Code y puede pasarse un poco, así que deja margen. Si salta, la llamada falla y no se reintenta: repetirla lo volvería a superar.
  • --safe-mode, --strict-mcp-config y la carpeta vacía evitan que cargue tu CLAUDE.md, tus skills, tus plugins, tus hooks o tus servidores MCP. Si los cargara, cada alumno mediría un sistema distinto.
  • --no-session-persistence no guarda la sesión en disco.
  • No usa --bare. Ese modo nunca lee la sesión de tu plan y pide una clave de API. Por lo mismo, el script quita ANTHROPIC_API_KEY del entorno de cada llamada.

La documentación de Claude Code anuncia que --bare pasará a ser el modo por defecto de -p en una versión futura (comprobado el 15-11-2026; mira los datos con fecha). Si un día el camino real falla por falta de sesión con tu sesión iniciada, empieza por ahí.

Dónde se cambia

Todo lo que puedes tocar está en live.config.json, en la raíz de cada proyecto, con los mismos nombres en los cuatro: model, effort, maxTurns, maxBudgetUsd y timeoutMs, más lo propio de cada uno. El modelo va con su ID completo (claude-haiku-5-5, claude-sonnet-5-5) y no con un alias como sonnet: el alias pasa a otro modelo cuando sale uno nuevo, y dos pasadas dejarían de ser comparables.

Qué queda grabado

Cada pasada se graba en evidencia/live/ del proyecto, con la fecha en el nombre. La cabecera lleva la fecha, la versión de Claude Code, el modelo, el esfuerzo, el sha256 del prompt y los argumentos exactos. Cada llamada guarda la salida completa de claude -p. Lo que esa salida cuenta del estado de tu cuenta, si lo trae (credential_expires_at, rate_limit_info), aparece como "<no se graba>".

Si no lanzas nada, cada proyecto trae sus pasadas grabadas y un comando que las vuelve a leer y recalcula sus cifras sin llamar a nadie. Cada lección te dice cuál.

Lo que cuesta

total_cost_usd es una estimación a precio de lista. Con la sesión de tu plan no se cobra aparte: se descuenta del uso de tu plan. Anthropic anunció un cambio en cómo se cobra claude -p con un plan y lo tiene en pausa (mira los datos con fecha). Si un día se cobra aparte, todos los caminos reales del curso siguen siendo opcionales y cada lección enseña la pasada grabada.

Lo que más mueve el coste de una llamada es la caché del prompt. El prompt de sistema es el mismo en todas las llamadas de un script, y en nuestras pasadas Claude Code lo guardó en la caché con duración de una hora (ephemeral_1h_input_tokens en la salida). Escribir en la caché cuesta el doble que la entrada normal. Leer de ella cuesta la décima parte en Haiku 5.5 y la vigésima en Sonnet 5.5. Tres consecuencias:

  • La primera llamada de una pasada con la caché fría es la más cara, porque escribe el prompt entero.
  • Repetir dentro de la hora abarata, porque las siguientes llamadas lo leen.
  • Una entrada de la caché solo está disponible cuando empieza la primera respuesta. Si lanzas varias llamadas a la vez con la caché fría, la escriben todas. Por eso los scripts que lanzan en paralelo mandan primero una sola.

En la salida de cada llamada lo ves en cache_creation_input_tokens (lo escrito) y cache_read_input_tokens (lo leído).

Si tu plan no llega

De más a menos ahorro:

  1. Un modelo más barato. "model": "claude-haiku-5-5" en live.config.json. Es lo que más abarata. Cuando grabamos la misma pasada con Haiku, la lección te dice cuánto costó y qué cambió en el resultado.
  2. Menos llamadas. Casi todos los scripts aceptan una opción para hacer una sola vuelta (--pasadas=1 o --trials=1); cada lección dice cuál. Con la caché fría ahorra menos de lo que parece, porque cada documento distinto tiene que escribir su parte.
  3. Un tope más bajo. Baja maxBudgetUsd. Una llamada que lo supera se corta, cuenta como fallo y no se reintenta.
  4. Sin gastar nada. Lee la pasada grabada y recalcúlala con el comando de la lección.

Si se acaba el uso de tu plan a mitad, ningún script convierte el error en un dato. Según el proyecto, la cola lo reintenta más tarde como un fallo transitorio o la pasada se corta sin grabar nada.

Palabras que verás en inglés

El código del curso está en inglés y el texto en español. Cuando hay una palabra en español igual de precisa, el texto la usa, y verás la inglesa en el código y en las salidas:

En el texto En el código Qué es
flujo workflow El proceso de negocio con estados que sobrevive a caídas
reserva, con su plazo claim, lease Quién está procesando un trabajo y hasta cuándo
huella digest El hash SHA-256 de unos bytes: si cambia un byte, cambia la huella
responsable owner La persona o el sistema que responde de una frontera o de una decisión
control gate Una condición medible, con su umbral escrito antes de mirar, que hay que pasar para avanzar
manifiesto manifest La lista firmada de lo que se observó, con la huella de cada pieza
recolector collector Quien recoge unos datos: en el módulo 4, quien leyó unos bytes y dio fe de ellos; en el 5, el servicio que recibe las trazas
canario canary Un dato trampa que no debe salir nunca, y también la versión nueva que recibe solo una parte del tráfico
simulacro game day Un fallo provocado a propósito para comprobar que la recuperación funciona
traspaso handover Pasar el sistema a otra persona y comprobar que puede operarlo sin ti

Y estas se quedan en inglés porque son las que vas a encontrar en la documentación y en las herramientas:

Palabra Qué es
worker El proceso que saca trabajos de la cola y los ejecuta
tenant Cada empresa cliente de una plataforma compartida, con sus datos separados de los de las demás
inbox y outbox La tabla de lo que ha entrado (para no procesarlo dos veces) y la de lo que falta por publicar
backpressure Decir que no a tiempo cuando ya tienes más trabajo del que puedes terminar
fencing Un número que sube con cada dueño nuevo de un trabajo y deja fuera las escrituras del anterior
fallback La respuesta alternativa, más limitada, cuando la principal falla
rollback Volver a la versión anterior
baseline La versión de partida con la que comparas
red team Atacar tu propio sistema para encontrar lo que falla antes que otro
span Cada paso de una traza, con su inicio, su fin y sus atributos
SLO El objetivo de servicio que te comprometes a cumplir, por ejemplo un 99 % de tareas bien terminadas
prompt Las instrucciones y el contexto que le mandas al modelo
payload Los datos que viajan en una petición, un mensaje o un trabajo
endpoint Una ruta concreta de una API, como GET /ready o POST /tasks
timeout El plazo máximo de espera; pasado ese plazo, dejas de esperar aunque el otro lado siga trabajando
health y readiness Las dos sondas de un servicio: si el proceso está vivo y si puede recibir trabajo
dataset El conjunto de casos con el que evalúas un modelo o un sistema
benchmark Una medición repetible del rendimiento, con la misma carga cada vez
runbook El procedimiento escrito para responder a una alerta o a un incidente