{"id":6528,"date":"2026-09-23T21:28:02","date_gmt":"2026-09-23T19:28:02","guid":{"rendered":"https:\/\/blog-david.moreno-morales.com\/?p=6528"},"modified":"2026-09-23T21:28:04","modified_gmt":"2026-09-23T19:28:04","slug":"automatizacion-con-inteligencia-artificia","status":"publish","type":"post","link":"https:\/\/blog-david.moreno-morales.com\/index.php\/2026\/09\/23\/automatizacion-con-inteligencia-artificia\/","title":{"rendered":"Automatizaciones con IA que no se rompen: reintentos, idempotencia y control de errores"},"content":{"rendered":"\t\t<div data-elementor-type=\"wp-post\" data-elementor-id=\"6528\" class=\"elementor elementor-6528\">\n\t\t\t\t\t\t<section class=\"elementor-section elementor-top-section elementor-element elementor-element-1f790ae elementor-section-boxed elementor-section-height-default elementor-section-height-default\" data-id=\"1f790ae\" data-element_type=\"section\" data-e-type=\"section\" data-settings=\"{&quot;tc_dark_mode_responsive_hide_in_dark&quot;:&quot;no&quot;,&quot;tc_dark_mode_responsive_hide_in_light&quot;:&quot;no&quot;}\">\n\t\t\t\t\t\t<div class=\"elementor-container elementor-column-gap-default\">\n\t\t\t\t\t<div class=\"elementor-column elementor-col-100 elementor-top-column elementor-element elementor-element-858b1f5\" data-id=\"858b1f5\" data-element_type=\"column\" data-e-type=\"column\" data-settings=\"{&quot;tc_dark_mode_responsive_hide_in_dark&quot;:&quot;no&quot;,&quot;tc_dark_mode_responsive_hide_in_light&quot;:&quot;no&quot;}\">\n\t\t\t<div class=\"elementor-widget-wrap elementor-element-populated\">\n\t\t\t\t\t\t<section class=\"elementor-section elementor-inner-section elementor-element elementor-element-61e0e1f elementor-section-boxed elementor-section-height-default elementor-section-height-default\" data-id=\"61e0e1f\" data-element_type=\"section\" data-e-type=\"section\" data-settings=\"{&quot;tc_dark_mode_responsive_hide_in_dark&quot;:&quot;no&quot;,&quot;tc_dark_mode_responsive_hide_in_light&quot;:&quot;no&quot;}\">\n\t\t\t\t\t\t<div class=\"elementor-container elementor-column-gap-default\">\n\t\t\t\t\t<div class=\"elementor-column elementor-col-100 elementor-inner-column elementor-element elementor-element-1cc40bf\" data-id=\"1cc40bf\" data-element_type=\"column\" data-e-type=\"column\" data-settings=\"{&quot;tc_dark_mode_responsive_hide_in_dark&quot;:&quot;no&quot;,&quot;tc_dark_mode_responsive_hide_in_light&quot;:&quot;no&quot;}\">\n\t\t\t<div class=\"elementor-widget-wrap elementor-element-populated\">\n\t\t\t\t\t\t<div class=\"elementor-element elementor-element-085053d elementor-widget elementor-widget-text-editor\" data-id=\"085053d\" data-element_type=\"widget\" data-e-type=\"widget\" data-settings=\"{&quot;tc_dark_mode_responsive_hide_in_dark&quot;:&quot;no&quot;,&quot;tc_dark_mode_responsive_hide_in_light&quot;:&quot;no&quot;}\" data-widget_type=\"text-editor.default\">\n\t\t\t\t\t\t\t\t\t<p class=\"isSelectedEnd\">Conectar un modelo de inteligencia artificial a una API y conseguir que una demostraci\u00f3n funcione suele ser relativamente sencillo. Conseguir que esa misma automatizaci\u00f3n siga funcionando cuando aparecen timeouts, l\u00edmites de uso, respuestas duplicadas, servicios ca\u00eddos o ejecuciones interrumpidas es otra historia.<\/p><p class=\"isSelectedEnd\">En producci\u00f3n, una automatizaci\u00f3n con inteligencia artificial no deber\u00eda dise\u00f1arse suponiendo que todos sus pasos terminar\u00e1n correctamente. Hay que partir de la hip\u00f3tesis contraria: alguno fallar\u00e1.<\/p><p class=\"isSelectedEnd\">La diferencia entre una automatizaci\u00f3n fr\u00e1gil y una robusta no est\u00e1 \u00fanicamente en la calidad del modelo. Est\u00e1 en c\u00f3mo responde el sistema cuando una dependencia deja de contestar, una petici\u00f3n llega dos veces, un proceso muere a mitad de ejecuci\u00f3n o no sabemos con certeza si una operaci\u00f3n lleg\u00f3 a completarse.<\/p><p class=\"isSelectedEnd\">La buena noticia es que no tenemos que inventar soluciones nuevas para todos estos problemas. Muchos de los patrones que necesitamos proceden de d\u00e9cadas de ingenier\u00eda de sistemas distribuidos: reintentos controlados, idempotencia, colas de errores, observabilidad, compensaci\u00f3n y escalado humano.<\/p><h2>El verdadero problema: una ejecuci\u00f3n no es una sola operaci\u00f3n<\/h2><p class=\"isSelectedEnd\">Imagina una automatizaci\u00f3n aparentemente sencilla:<\/p><ol start=\"1\" data-spread=\"false\"><li>llega un formulario de un cliente;<\/li><li>un modelo de IA clasifica la solicitud;<\/li><li>se consulta informaci\u00f3n en el CRM;<\/li><li>se genera una respuesta;<\/li><li>se actualiza el CRM;<\/li><li>se env\u00eda un correo;<\/li><li>se registra el resultado.<\/li><\/ol><p class=\"isSelectedEnd\">Ahora supongamos que el paso 6 devuelve un timeout.<\/p><p class=\"isSelectedEnd\">\u00bfQu\u00e9 significa realmente?<\/p><p class=\"isSelectedEnd\">Podr\u00eda significar que el correo nunca se envi\u00f3. Pero tambi\u00e9n podr\u00eda significar que el servidor s\u00ed lo envi\u00f3 y que la respuesta se perdi\u00f3 antes de llegar a nuestra automatizaci\u00f3n.<\/p><p class=\"isSelectedEnd\">Si simplemente repetimos el paso, podr\u00edamos enviar el mensaje dos veces.<\/p><p class=\"isSelectedEnd\">Este problema se repite constantemente en sistemas distribuidos: no siempre podemos distinguir entre \u201cla operaci\u00f3n fall\u00f3\u201d y \u201cla operaci\u00f3n termin\u00f3, pero no recib\u00ed confirmaci\u00f3n\u201d.<\/p><p class=\"isSelectedEnd\">Por eso una arquitectura robusta necesita responder, como m\u00ednimo, a estas preguntas:<\/p><ul data-spread=\"false\"><li>\u00bfQu\u00e9 errores merece la pena reintentar?<\/li><li>\u00bfCu\u00e1ntas veces?<\/li><li>\u00bfCon qu\u00e9 intervalo?<\/li><li>\u00bfQu\u00e9 ocurre si la misma operaci\u00f3n se ejecuta dos veces?<\/li><li>\u00bfC\u00f3mo aislamos los trabajos que nunca terminan correctamente?<\/li><li>\u00bfC\u00f3mo reconstruimos despu\u00e9s qu\u00e9 ocurri\u00f3?<\/li><li>\u00bfCu\u00e1ndo debe intervenir una persona?<\/li><\/ul><h2>1. No todos los errores deben reintentarse<\/h2><p class=\"isSelectedEnd\">Uno de los errores m\u00e1s habituales en automatizaci\u00f3n consiste en envolver cualquier fallo en un bucle de reintentos, no funciona.<\/p><p class=\"isSelectedEnd\">Hay errores transitorios, que pueden desaparecer solos, y errores permanentes, que seguir\u00e1n ocurriendo independientemente de cu\u00e1ntas veces repitamos la operaci\u00f3n.<\/p><p class=\"isSelectedEnd\">Una respuesta <code dir=\"ltr\">503 Service Unavailable<\/code>, un timeout o determinados errores <code dir=\"ltr\">429 Too Many Requests<\/code> suelen ser candidatos razonables para un nuevo intento.<\/p><p class=\"isSelectedEnd\">Una petici\u00f3n con par\u00e1metros inv\u00e1lidos normalmente no lo es. La documentaci\u00f3n de Google Cloud recomienda utilizar reintentos para errores transitorios, limitar el n\u00famero de intentos y aplicar <em>exponential backoff<\/em> en lugar de repetir las solicitudes inmediatamente.<\/p><p class=\"isSelectedEnd\">Una pol\u00edtica simplificada podr\u00eda ser:<\/p><table><tbody><tr><th>Error<\/th><th>Acci\u00f3n habitual<\/th><\/tr><tr><td><code dir=\"ltr\">400 Bad Request<\/code><\/td><td>No reintentar. Corregir los datos.<\/td><\/tr><tr><td><code dir=\"ltr\">401\/403<\/code><\/td><td>No reintentar autom\u00e1ticamente. Revisar autenticaci\u00f3n o permisos.<\/td><\/tr><tr><td><code dir=\"ltr\">408 Timeout<\/code><\/td><td>Reintentar de forma controlada.<\/td><\/tr><tr><td><code dir=\"ltr\">409 Conflict<\/code><\/td><td>Depende de la operaci\u00f3n; revisar concurrencia o idempotencia.<\/td><\/tr><tr><td><code dir=\"ltr\">429 Too Many Requests<\/code><\/td><td>Esperar y reintentar respetando los l\u00edmites del servicio.<\/td><\/tr><tr><td><code dir=\"ltr\">500\/502\/503\/504<\/code><\/td><td>Reintentar con l\u00edmite si la operaci\u00f3n lo permite.<\/td><\/tr><\/tbody><\/table><p class=\"isSelectedEnd\">Esta tabla es una gu\u00eda, no una ley universal. Cada API tiene su propio contrato y conviene seguir su documentaci\u00f3n espec\u00edfica.<\/p><h2>2. Exponential backoff: fallar sin empeorar el problema<\/h2><p class=\"isSelectedEnd\">Cuando un servicio est\u00e1 saturado, enviar otra petici\u00f3n inmediatamente puede aumentar la saturaci\u00f3n.<\/p><p class=\"isSelectedEnd\">Por eso se utiliza el exponential backoff: despu\u00e9s de cada fallo incrementamos progresivamente el tiempo antes del siguiente intento.<\/p><p class=\"isSelectedEnd\">Una secuencia sencilla podr\u00eda ser:<\/p><p class=\"isSelectedEnd\"><code dir=\"ltr\">1 s \u2192 2 s \u2192 4 s \u2192 8 s \u2192 16 s<\/code><\/p><p class=\"isSelectedEnd\">Pero todav\u00eda existe un problema.<\/p><p class=\"isSelectedEnd\">Si mil automatizaciones fallan simult\u00e1neamente y todas utilizan exactamente la misma secuencia, las mil volver\u00e1n a atacar al servicio al mismo tiempo. Para evitarlo se a\u00f1ade jitter: una peque\u00f1a componente aleatoria al tiempo de espera.<\/p><p class=\"isSelectedEnd\">Google Cloud describe precisamente este patr\u00f3n de <em>truncated exponential backoff with jitter<\/em>, que adem\u00e1s establece un l\u00edmite m\u00e1ximo en lugar de seguir reintentando indefinidamente.<\/p><p class=\"isSelectedEnd\">Conceptualmente:<\/p><pre dir=\"ltr\"><code dir=\"ltr\">delay = min(maxDelay, exponentialDelay + randomJitter)<\/code><\/pre><p class=\"isSelectedEnd\">La idea importante no es la f\u00f3rmula exacta, sino estas cuatro reglas:<\/p><ul data-spread=\"false\"><li>aumenta progresivamente la espera;<\/li><li>introduce aleatoriedad;<\/li><li>limita el n\u00famero total de intentos;<\/li><li>define qu\u00e9 errores pueden activar un reintento.<\/li><\/ul><p class=\"isSelectedEnd\">Un retry infinito no es resiliencia. Es un incidente esperando ocurrir.<\/p><h2>3. Idempotencia: repetir sin duplicar efectos<\/h2><p class=\"isSelectedEnd\">Los reintentos introducen inmediatamente otro problema: una operaci\u00f3n puede terminar ejecut\u00e1ndose m\u00e1s de una vez. Aqu\u00ed entra la idempotencia. Una operaci\u00f3n es idempotente cuando ejecutarla varias veces produce, desde el punto de vista del sistema, el mismo resultado que ejecutarla una sola vez.<\/p><p class=\"isSelectedEnd\">Consultar un registro suele ser idempotente.<\/p><p class=\"isSelectedEnd\">\u201cEstablecer el estado del pedido a <code dir=\"ltr\">Procesado<\/code>\u201d puede dise\u00f1arse como una operaci\u00f3n idempotente.<\/p><p class=\"isSelectedEnd\">\u201cIncrementar el saldo en 100 euros\u201d no lo es autom\u00e1ticamente.<\/p><p class=\"isSelectedEnd\">\u201cEnviar otro correo\u201d tampoco.<\/p><p class=\"isSelectedEnd\">Para operaciones con efectos secundarios, una estrategia habitual es asignar una clave de idempotencia \u00fanica a la operaci\u00f3n l\u00f3gica.<\/p><p class=\"isSelectedEnd\">Por ejemplo:<\/p><pre dir=\"ltr\"><code dir=\"ltr\">customer-9281-invoice-2026-09<\/code><\/pre><p class=\"isSelectedEnd\">Si el sistema recibe dos peticiones con esa misma clave, debe reconocer que ambas representan la misma operaci\u00f3n y evitar ejecutar el efecto dos veces. Stripe utiliza este patr\u00f3n expl\u00edcitamente: sus APIs permiten utilizar claves de idempotencia para repetir solicitudes sin crear accidentalmente un segundo objeto o repetir una operaci\u00f3n.<\/p><p class=\"isSelectedEnd\">En una arquitectura propia podemos aplicar algo parecido:<\/p><pre dir=\"ltr\"><code dir=\"ltr\">1. Recibir operaci\u00f3n con idempotency_key\n2. Buscar la clave en almacenamiento\n3. Si ya termin\u00f3:\n      devolver resultado anterior\n4. Si est\u00e1 en proceso:\n      evitar ejecuci\u00f3n concurrente\n5. Si no existe:\n      registrar operaci\u00f3n\n      ejecutar\n      guardar resultado<\/code><\/pre><p class=\"isSelectedEnd\">La clave de idempotencia deber\u00eda representar la intenci\u00f3n del negocio, no simplemente cada petici\u00f3n HTTP. Si generamos una clave nueva en cada reintento, hemos destruido precisamente la protecci\u00f3n que busc\u00e1bamos.<\/p><h2>4. Dise\u00f1a para \u201cal menos una vez\u201d, no para un mundo perfecto<\/h2><p class=\"isSelectedEnd\">En sistemas distribuidos es habitual trabajar con mecanismos de entrega que pueden procesar un mensaje m\u00e1s de una vez. Por ejemplo, la documentaci\u00f3n de Amazon SQS advierte de que, dependiendo del procesamiento y de los tiempos de visibilidad, un mensaje puede volver a entregarse y ser procesado de nuevo. La consecuencia arquitect\u00f3nica es importante: el consumidor tambi\u00e9n debe estar preparado para duplicados.<\/p><p class=\"isSelectedEnd\">No basta con intentar evitar que aparezcan. Un flujo robusto deber\u00eda almacenar alg\u00fan identificador estable del evento o trabajo:<\/p><pre dir=\"ltr\"><code dir=\"ltr\">event_id = 7ca1...<\/code><\/pre><p class=\"isSelectedEnd\">Antes de ejecutar un efecto irreversible:<\/p><pre dir=\"ltr\"><code dir=\"ltr\">if event_id already_processed:\n    return previous_result<\/code><\/pre><p class=\"isSelectedEnd\">Este patr\u00f3n resulta especialmente importante cuando conectamos automatizaciones, webhooks, colas y agentes de IA.<\/p><h2>5. Cuando los reintentos se agotan: Dead-Letter Queue<\/h2><p class=\"isSelectedEnd\">\u00bfQu\u00e9 hacemos con una operaci\u00f3n que ha fallado cinco veces?<\/p><p class=\"isSelectedEnd\">La peor opci\u00f3n suele ser seguir intent\u00e1ndolo eternamente. Otra mala opci\u00f3n es descartarla silenciosamente. Una soluci\u00f3n cl\u00e1sica es moverla a una Dead-Letter Queue, o DLQ.<\/p><p class=\"isSelectedEnd\">AWS define las DLQ como colas destinadas a recibir mensajes que una cola de origen no ha conseguido procesar correctamente. Esto permite aislar los casos fallidos, analizarlos y posteriormente reprocesarlos si procede.<\/p><p class=\"isSelectedEnd\">El flujo queda as\u00ed:<\/p><pre dir=\"ltr\"><code dir=\"ltr\">Trabajo\n  \u2193\nProcesamiento\n  \u2193\n\u00bf\u00c9xito?\n  \u251c\u2500\u2500 S\u00ed \u2192 Terminado\n  \u2514\u2500\u2500 No\n       \u2193\n     Retry\n       \u2193\n\u00bfSuper\u00f3 el l\u00edmite?\n  \u251c\u2500\u2500 No \u2192 Nuevo intento\n  \u2514\u2500\u2500 S\u00ed \u2192 DLQ<\/code><\/pre><p class=\"isSelectedEnd\">Una DLQ no deber\u00eda convertirse en un cementerio olvidado.<\/p><p class=\"isSelectedEnd\">Necesita, al menos:<\/p><ul data-spread=\"false\"><li>alertas cuando aumenta el n\u00famero de mensajes;<\/li><li>motivo del fallo;<\/li><li>n\u00famero de intentos;<\/li><li>timestamps;<\/li><li>identificador de correlaci\u00f3n;<\/li><li>payload suficiente para diagnosticar el problema;<\/li><li>mecanismo controlado de reprocesamiento.<\/li><\/ul><p>Si un mensaje termina en la DLQ y nadie lo observa, hemos convertido un error visible en un error silencioso.<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<\/section>\n\t\t\t\t<section class=\"elementor-section elementor-inner-section elementor-element elementor-element-26a56fa elementor-section-boxed elementor-section-height-default elementor-section-height-default\" data-id=\"26a56fa\" data-element_type=\"section\" data-e-type=\"section\" data-settings=\"{&quot;tc_dark_mode_responsive_hide_in_dark&quot;:&quot;no&quot;,&quot;tc_dark_mode_responsive_hide_in_light&quot;:&quot;no&quot;}\">\n\t\t\t\t\t\t<div class=\"elementor-container elementor-column-gap-default\">\n\t\t\t\t\t<div class=\"elementor-column elementor-col-50 elementor-inner-column elementor-element elementor-element-bf5ce92\" data-id=\"bf5ce92\" data-element_type=\"column\" data-e-type=\"column\" data-settings=\"{&quot;tc_dark_mode_responsive_hide_in_dark&quot;:&quot;no&quot;,&quot;tc_dark_mode_responsive_hide_in_light&quot;:&quot;no&quot;}\">\n\t\t\t<div class=\"elementor-widget-wrap elementor-element-populated\">\n\t\t\t\t\t\t<div class=\"elementor-element elementor-element-2c79896 elementor-widget elementor-widget-image\" data-id=\"2c79896\" data-element_type=\"widget\" data-e-type=\"widget\" data-settings=\"{&quot;tc_dark_mode_responsive_hide_in_dark&quot;:&quot;no&quot;,&quot;tc_dark_mode_responsive_hide_in_light&quot;:&quot;no&quot;}\" data-widget_type=\"image.default\">\n\t\t\t\t\t\t\t\t\t\t\t\t<figure class=\"wp-caption\">\n\t\t\t\t\t\t\t\t\t\t<img fetchpriority=\"high\" decoding=\"async\" width=\"819\" height=\"1024\" src=\"https:\/\/blog-david.moreno-morales.com\/wp-content\/uploads\/2026\/09\/automat_002-819x1024.png\" class=\"attachment-large size-large wp-image-6527\" alt=\"Qu\u00e9 ocurre cuando una operaci\u00f3n falla\" \/>\t\t\t\t\t\t\t\t\t\t\t<figcaption class=\"widget-image-caption wp-caption-text\">Qu\u00e9 ocurre cuando una operaci\u00f3n falla<\/figcaption>\n\t\t\t\t\t\t\t\t\t\t<\/figure>\n\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<\/div>\n\t\t\t\t<div class=\"elementor-column elementor-col-50 elementor-inner-column elementor-element elementor-element-5d576d9\" data-id=\"5d576d9\" data-element_type=\"column\" data-e-type=\"column\" data-settings=\"{&quot;tc_dark_mode_responsive_hide_in_dark&quot;:&quot;no&quot;,&quot;tc_dark_mode_responsive_hide_in_light&quot;:&quot;no&quot;}\">\n\t\t\t<div class=\"elementor-widget-wrap elementor-element-populated\">\n\t\t\t\t\t\t<div class=\"elementor-element elementor-element-d7256b4 elementor-widget elementor-widget-image\" data-id=\"d7256b4\" data-element_type=\"widget\" data-e-type=\"widget\" data-settings=\"{&quot;tc_dark_mode_responsive_hide_in_dark&quot;:&quot;no&quot;,&quot;tc_dark_mode_responsive_hide_in_light&quot;:&quot;no&quot;}\" data-widget_type=\"image.default\">\n\t\t\t\t\t\t\t\t\t\t\t\t<figure class=\"wp-caption\">\n\t\t\t\t\t\t\t\t\t\t<img decoding=\"async\" width=\"1024\" height=\"768\" src=\"https:\/\/blog-david.moreno-morales.com\/wp-content\/uploads\/2026\/09\/automat_003-1024x768.png\" class=\"attachment-large size-large wp-image-6525\" alt=\"Reversible vs. irreversible\" \/>\t\t\t\t\t\t\t\t\t\t\t<figcaption class=\"widget-image-caption wp-caption-text\">Reversible vs. irreversible<\/figcaption>\n\t\t\t\t\t\t\t\t\t\t<\/figure>\n\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<\/section>\n\t\t\t\t<section class=\"elementor-section elementor-inner-section elementor-element elementor-element-e8ae2ac elementor-section-boxed elementor-section-height-default elementor-section-height-default\" data-id=\"e8ae2ac\" data-element_type=\"section\" data-e-type=\"section\" data-settings=\"{&quot;tc_dark_mode_responsive_hide_in_dark&quot;:&quot;no&quot;,&quot;tc_dark_mode_responsive_hide_in_light&quot;:&quot;no&quot;}\">\n\t\t\t\t\t\t<div class=\"elementor-container elementor-column-gap-default\">\n\t\t\t\t\t<div class=\"elementor-column elementor-col-100 elementor-inner-column elementor-element elementor-element-7c91588\" data-id=\"7c91588\" data-element_type=\"column\" data-e-type=\"column\" data-settings=\"{&quot;tc_dark_mode_responsive_hide_in_dark&quot;:&quot;no&quot;,&quot;tc_dark_mode_responsive_hide_in_light&quot;:&quot;no&quot;}\">\n\t\t\t<div class=\"elementor-widget-wrap elementor-element-populated\">\n\t\t\t\t\t\t<div class=\"elementor-element elementor-element-af8d6c7 elementor-widget elementor-widget-text-editor\" data-id=\"af8d6c7\" data-element_type=\"widget\" data-e-type=\"widget\" data-settings=\"{&quot;tc_dark_mode_responsive_hide_in_dark&quot;:&quot;no&quot;,&quot;tc_dark_mode_responsive_hide_in_light&quot;:&quot;no&quot;}\" data-widget_type=\"text-editor.default\">\n\t\t\t\t\t\t\t\t\t<h2>6. Operaciones reversibles e irreversibles<\/h2><p class=\"isSelectedEnd\">Aqu\u00ed aparece una distinci\u00f3n cr\u00edtica para cualquier automatizaci\u00f3n con IA. No todas las acciones tienen el mismo nivel de riesgo.<\/p><h3>Operaciones f\u00e1cilmente reversibles<\/h3><p class=\"isSelectedEnd\">Por ejemplo:<\/p><ul data-spread=\"false\"><li>a\u00f1adir una etiqueta interna;<\/li><li>actualizar un campo temporal;<\/li><li>crear un borrador;<\/li><li>generar una clasificaci\u00f3n;<\/li><li>guardar una recomendaci\u00f3n pendiente de revisi\u00f3n.<\/li><\/ul><p class=\"isSelectedEnd\">Estas operaciones permiten m\u00e1s margen para automatizar y corregir despu\u00e9s.<\/p><h3>Operaciones dif\u00edciles o imposibles de revertir<\/h3><p class=\"isSelectedEnd\">Por ejemplo:<\/p><ul data-spread=\"false\"><li>efectuar un pago;<\/li><li>enviar definitivamente un correo;<\/li><li>borrar informaci\u00f3n;<\/li><li>cancelar un servicio;<\/li><li>modificar permisos;<\/li><li>publicar contenido;<\/li><li>comunicar una decisi\u00f3n a un cliente.<\/li><\/ul><p class=\"isSelectedEnd\">En estos casos deber\u00edamos elevar considerablemente las garant\u00edas.<\/p><p class=\"isSelectedEnd\">Una posible estrategia consiste en separar decisi\u00f3n y ejecuci\u00f3n:<\/p><pre dir=\"ltr\"><code dir=\"ltr\">IA propone acci\u00f3n\n       \u2193\nValidaciones deterministas\n       \u2193\n\u00bfAcci\u00f3n sensible?\n  \u251c\u2500\u2500 No \u2192 Ejecutar\n  \u2514\u2500\u2500 S\u00ed \u2192 Aprobaci\u00f3n \/ controles adicionales<\/code><\/pre><p class=\"isSelectedEnd\">La IA puede ser excelente generando, clasificando o proponiendo. Pero una acci\u00f3n irreversible necesita una frontera clara donde entren reglas deterministas, permisos, pol\u00edticas o supervisi\u00f3n humana.<\/p><h2>7. Las transacciones distribuidas requieren compensaci\u00f3n<\/h2><p class=\"isSelectedEnd\">Otro escenario problem\u00e1tico aparece cuando una automatizaci\u00f3n ejecuta varias operaciones y solo algunas terminan correctamente.<\/p><p class=\"isSelectedEnd\">Supongamos:<\/p><ol start=\"1\" data-spread=\"false\"><li>creamos un cliente;<\/li><li>generamos una factura;<\/li><li>realizamos un cargo;<\/li><li>actualizamos el CRM.<\/li><\/ol><p class=\"isSelectedEnd\">Si el paso 4 falla, no podemos simplemente \u201chacer rollback\u201d de todo como si se tratara de una \u00fanica transacci\u00f3n SQL.<\/p><p class=\"isSelectedEnd\">Los sistemas externos ya han cambiado. En estas situaciones podemos utilizar acciones compensatorias.<\/p><p class=\"isSelectedEnd\">Por ejemplo:<\/p><pre dir=\"ltr\"><code dir=\"ltr\">Crear reserva        \u2192 compensaci\u00f3n: cancelar reserva\nBloquear inventario  \u2192 compensaci\u00f3n: liberar inventario\nCrear recurso        \u2192 compensaci\u00f3n: eliminar recurso<\/code><\/pre><p class=\"isSelectedEnd\">Sin embargo, no todas las operaciones tienen una compensaci\u00f3n perfecta. No podemos \u201cdesenviar\u201d un correo que alguien ya ha le\u00eddo.<\/p><p class=\"isSelectedEnd\">Por eso conviene modelar expl\u00edcitamente cada paso indicando:<\/p><pre dir=\"ltr\"><code dir=\"ltr\">operaci\u00f3n\nreversible: s\u00ed\/no\nidempotente: s\u00ed\/no\nestrategia_retry\nacci\u00f3n_compensatoria\nrequiere_aprobaci\u00f3n<\/code><\/pre><p class=\"isSelectedEnd\">Esta peque\u00f1a disciplina transforma considerablemente el dise\u00f1o del flujo.<\/p><h2>8. Observabilidad: poder responder \u201cqu\u00e9 ocurri\u00f3\u201d<\/h2><p class=\"isSelectedEnd\">Una automatizaci\u00f3n que \u00fanicamente escribe mensajes como:<\/p><pre dir=\"ltr\"><code dir=\"ltr\">ERROR: Something went wrong<\/code><\/pre><p class=\"isSelectedEnd\">es casi imposible de operar.<\/p><p class=\"isSelectedEnd\">Necesitamos poder reconstruir una ejecuci\u00f3n completa. OpenTelemetry plantea un modelo vendor-neutral de observabilidad basado en se\u00f1ales como traces, metrics y logs.<\/p><p class=\"isSelectedEnd\">Para una automatizaci\u00f3n con IA resulta especialmente \u00fatil asignar un identificador de correlaci\u00f3n a cada ejecuci\u00f3n:<\/p><pre dir=\"ltr\"><code dir=\"ltr\">correlation_id = auto_9284ab...<\/code><\/pre><p class=\"isSelectedEnd\">Ese identificador deber\u00eda acompa\u00f1ar a todos los pasos:<\/p><pre dir=\"ltr\"><code dir=\"ltr\">Webhook recibido\nIA ejecutada\nCRM consultado\nRetry 1\nRetry 2\nCorreo enviado\nRegistro actualizado<\/code><\/pre><p class=\"isSelectedEnd\">As\u00ed podemos responder preguntas operativas reales:<\/p><ul data-spread=\"false\"><li>\u00bfCu\u00e1nto tarda cada tipo de ejecuci\u00f3n?<\/li><li>\u00bfQu\u00e9 API provoca m\u00e1s errores?<\/li><li>\u00bfCu\u00e1ntos reintentos necesitamos de media?<\/li><li>\u00bfQu\u00e9 porcentaje termina en la DLQ?<\/li><li>\u00bfQu\u00e9 modelos o herramientas fallan con mayor frecuencia?<\/li><li>\u00bfQu\u00e9 automatizaciones requieren m\u00e1s intervenci\u00f3n humana?<\/li><\/ul><p class=\"isSelectedEnd\">Los logs explican eventos concretos. Las m\u00e9tricas revelan tendencias. Las trazas reconstruyen el recorrido.<\/p><p class=\"isSelectedEnd\">Necesitamos las tres perspectivas.<\/p><h2>9. Escalado humano como parte de la arquitectura<\/h2><p class=\"isSelectedEnd\">En muchas automatizaciones con IA, \u201cpedir ayuda a una persona\u201d se considera un fracaso. No deber\u00eda ser as\u00ed, el escalado humano es un mecanismo de control.<\/p><p class=\"isSelectedEnd\">Deber\u00eda activarse deliberadamente cuando:<\/p><ul data-spread=\"false\"><li>se supera el n\u00famero m\u00e1ximo de reintentos;<\/li><li>existe ambig\u00fcedad sobre si una acci\u00f3n irreversible se complet\u00f3;<\/li><li>la confianza del sistema es insuficiente;<\/li><li>faltan datos;<\/li><li>aparecen situaciones no contempladas;<\/li><li>una pol\u00edtica de negocio exige aprobaci\u00f3n;<\/li><li>el coste potencial del error supera el coste de revisi\u00f3n.<\/li><\/ul><p class=\"isSelectedEnd\">Una buena automatizaci\u00f3n no intenta eliminar a la persona de todos los caminos. Intenta reservar su atenci\u00f3n para los casos donde realmente aporta valor.<\/p><h2>Una arquitectura pr\u00e1ctica<\/h2><p class=\"isSelectedEnd\">Una arquitectura robusta podr\u00eda adoptar esta secuencia:<\/p><pre dir=\"ltr\"><code dir=\"ltr\">Entrada\n  \u2193\nValidaci\u00f3n\n  \u2193\nGenerar event_id \/ idempotency_key\n  \u2193\nClasificaci\u00f3n o razonamiento con IA\n  \u2193\nValidaci\u00f3n de reglas\n  \u2193\nEjecuci\u00f3n\n  \u2193\n\u00bfFallo transitorio?\n  \u251c\u2500\u2500 S\u00ed \u2192 Backoff + jitter \u2192 Retry\n  \u2514\u2500\u2500 No\n        \u2193\n\u00bfRequiere intervenci\u00f3n?\n  \u251c\u2500\u2500 S\u00ed \u2192 Escalado humano\n  \u2514\u2500\u2500 No \u2192 DLQ<\/code><\/pre><p class=\"isSelectedEnd\">Y transversalmente:<\/p><pre dir=\"ltr\"><code dir=\"ltr\">Logs + m\u00e9tricas + trazas + correlation_id<\/code><\/pre><p class=\"isSelectedEnd\">Cada componente cubre un problema diferente:<\/p><ul data-spread=\"false\"><li><strong>Retry:<\/strong> tolera fallos temporales.<\/li><li><strong>Backoff + jitter:<\/strong> evita amplificar una ca\u00edda.<\/li><li><strong>Idempotencia:<\/strong> evita duplicar efectos.<\/li><li><strong>DLQ:<\/strong> contiene los fallos persistentes.<\/li><li><strong>Observabilidad:<\/strong> permite investigar.<\/li><li><strong>Compensaci\u00f3n:<\/strong> gestiona ejecuciones parciales.<\/li><li><strong>Escalado humano:<\/strong> protege decisiones sensibles.<\/li><\/ul><p class=\"isSelectedEnd\">Ninguno sustituye a los dem\u00e1s.<\/p><h2>Checklist antes de poner una automatizaci\u00f3n con IA en producci\u00f3n<\/h2><p class=\"isSelectedEnd\">Antes de activar un flujo importante, deber\u00edamos poder responder claramente:<\/p><ul data-spread=\"false\"><li>\u00bfQu\u00e9 ocurre si cada API deja de responder?<\/li><li>\u00bfQu\u00e9 errores reintentamos y cu\u00e1les no?<\/li><li>\u00bfTenemos un l\u00edmite m\u00e1ximo de reintentos?<\/li><li>\u00bfAplicamos backoff y jitter?<\/li><li>\u00bfPuede una petici\u00f3n repetirse sin duplicar efectos?<\/li><li>\u00bfDisponemos de claves de idempotencia o identificadores de eventos?<\/li><li>\u00bfQu\u00e9 ocurre con un trabajo que nunca termina correctamente?<\/li><li>\u00bfTenemos DLQ o un mecanismo equivalente?<\/li><li>\u00bfPodemos reconstruir una ejecuci\u00f3n completa?<\/li><li>\u00bfExiste un correlation ID?<\/li><li>\u00bfTenemos m\u00e9tricas y alertas?<\/li><li>\u00bfSabemos qu\u00e9 acciones son reversibles?<\/li><li>\u00bfQu\u00e9 ocurre si solo se completa la mitad del proceso?<\/li><li>\u00bfExiste una operaci\u00f3n compensatoria?<\/li><li>\u00bfQu\u00e9 acciones requieren revisi\u00f3n humana?<\/li><\/ul><p class=\"isSelectedEnd\">Si varias respuestas son \u201cno lo s\u00e9\u201d, la automatizaci\u00f3n probablemente todav\u00eda es un prototipo.<\/p><h2>Conclusi\u00f3n<\/h2><p class=\"isSelectedEnd\">Una automatizaci\u00f3n robusta no es aquella que nunca falla, eso no existe. Es aquella que falla de forma controlada.<\/p><p class=\"isSelectedEnd\">Sabe distinguir un error temporal de uno permanente. Puede repetir una operaci\u00f3n sin producir duplicados. A\u00edsla los trabajos problem\u00e1ticos. Conserva suficiente informaci\u00f3n para entender qu\u00e9 ocurri\u00f3. Evita repetir ciegamente acciones irreversibles y sabe cu\u00e1ndo detenerse para pedir intervenci\u00f3n humana.<\/p><p class=\"isSelectedEnd\">La inteligencia artificial puede aportar razonamiento, flexibilidad y capacidad de adaptaci\u00f3n. Pero la fiabilidad sigue dependiendo de la arquitectura.<\/p><p>Y en producci\u00f3n, dise\u00f1ar correctamente el segundo intento suele ser mucho m\u00e1s importante que celebrar que el primero funcion\u00f3.<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<\/section>\n\t\t\t\t<section class=\"elementor-section elementor-inner-section elementor-element elementor-element-4fa9b73 elementor-section-boxed elementor-section-height-default elementor-section-height-default\" data-id=\"4fa9b73\" data-element_type=\"section\" data-e-type=\"section\" data-settings=\"{&quot;tc_dark_mode_responsive_hide_in_dark&quot;:&quot;no&quot;,&quot;tc_dark_mode_responsive_hide_in_light&quot;:&quot;no&quot;}\">\n\t\t\t\t\t\t<div class=\"elementor-container elementor-column-gap-default\">\n\t\t\t\t\t<div class=\"elementor-column elementor-col-100 elementor-inner-column elementor-element elementor-element-eeaff01\" data-id=\"eeaff01\" data-element_type=\"column\" data-e-type=\"column\" data-settings=\"{&quot;tc_dark_mode_responsive_hide_in_dark&quot;:&quot;no&quot;,&quot;tc_dark_mode_responsive_hide_in_light&quot;:&quot;no&quot;}\">\n\t\t\t<div class=\"elementor-widget-wrap elementor-element-populated\">\n\t\t\t\t\t\t<div class=\"elementor-element elementor-element-6abbbf9 elementor-widget elementor-widget-heading\" data-id=\"6abbbf9\" data-element_type=\"widget\" data-e-type=\"widget\" data-settings=\"{&quot;tc_dark_mode_responsive_hide_in_dark&quot;:&quot;no&quot;,&quot;tc_dark_mode_responsive_hide_in_light&quot;:&quot;no&quot;}\" data-widget_type=\"heading.default\">\n\t\t\t\t\t<h4 class=\"elementor-heading-title elementor-size-default\">REFERENCIAS\n<\/h4>\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-4439ef8 elementor-widget elementor-widget-text-editor\" data-id=\"4439ef8\" data-element_type=\"widget\" data-e-type=\"widget\" data-settings=\"{&quot;tc_dark_mode_responsive_hide_in_dark&quot;:&quot;no&quot;,&quot;tc_dark_mode_responsive_hide_in_light&quot;:&quot;no&quot;}\" data-widget_type=\"text-editor.default\">\n\t\t\t\t\t\t\t\t\t<ul class=\"content-list\"><li>Google Cloud IAM \u2014 Retry failed requests \u2014 https:\/\/docs.cloud.google.com\/iam\/docs\/retry-strategy \u2014 Backoff exponencial truncado con jitter y l\u00edmites de reintento.<\/li><li>Stripe API \u2014 Idempotent requests \u2014 https:\/\/docs.stripe.com\/api\/idempotent_requests \u2014 Uso de claves de idempotencia para repetir solicitudes sin duplicar efectos.<\/li><li>Amazon SQS \u2014 Using dead-letter queues \u2014 https:\/\/docs.aws.amazon.com\/AWSSimpleQueueService\/latest\/SQSDeveloperGuide\/sqs-dead-letter-queues.html \u2014 Patr\u00f3n DLQ para aislar y reprocesar mensajes que fallan repetidamente.<\/li><li>OpenTelemetry \u2014 Documentation \u2014 https:\/\/opentelemetry.io\/docs\/ \u2014 Observabilidad vendor-neutral con trazas, m\u00e9tricas y logs.<\/li><li>Google Cloud Application Integration \u2014 Error handling strategies \u2014 https:\/\/docs.cloud.google.com\/application-integration\/docs\/error-handling-strategy \u2014 Estrategias de retry y manejo de errores en integraciones.<\/li><\/ul>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<\/section>\n\t\t\t\t\t<\/div>\n\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<\/section>\n\t\t\t\t<\/div>\n\t\t","protected":false},"excerpt":{"rendered":"<p>C\u00f3mo dise\u00f1ar flujos autom\u00e1ticos resistentes a fallos, duplicados, APIs inestables y ejecuciones parciales. Incluir patrones de reintento, idempotencia, dead-letter queues, observabilidad y escalado humano.<\/p>\n","protected":false},"author":1,"featured_media":6526,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[41],"tags":[55,47,49,69,52],"class_list":["post-6528","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-auto","tag-automatizacion","tag-desarrollo","tag-ia","tag-idempotencia","tag-inteligenciaartificial"],"_links":{"self":[{"href":"https:\/\/blog-david.moreno-morales.com\/index.php\/wp-json\/wp\/v2\/posts\/6528","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog-david.moreno-morales.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog-david.moreno-morales.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog-david.moreno-morales.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog-david.moreno-morales.com\/index.php\/wp-json\/wp\/v2\/comments?post=6528"}],"version-history":[{"count":6,"href":"https:\/\/blog-david.moreno-morales.com\/index.php\/wp-json\/wp\/v2\/posts\/6528\/revisions"}],"predecessor-version":[{"id":6534,"href":"https:\/\/blog-david.moreno-morales.com\/index.php\/wp-json\/wp\/v2\/posts\/6528\/revisions\/6534"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blog-david.moreno-morales.com\/index.php\/wp-json\/wp\/v2\/media\/6526"}],"wp:attachment":[{"href":"https:\/\/blog-david.moreno-morales.com\/index.php\/wp-json\/wp\/v2\/media?parent=6528"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog-david.moreno-morales.com\/index.php\/wp-json\/wp\/v2\/categories?post=6528"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog-david.moreno-morales.com\/index.php\/wp-json\/wp\/v2\/tags?post=6528"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}