<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>DataOps on Adur</title><link>https://adurrr.github.io/categories/dataops/</link><description>Recent content in DataOps on Adur</description><generator>Hugo -- gohugo.io</generator><language>en-us</language><lastBuildDate>Sun, 15 Jun 2025 00:00:00 +0000</lastBuildDate><atom:link href="https://adurrr.github.io/categories/dataops/index.xml" rel="self" type="application/rss+xml"/><item><title>LLMOps: integrando LLMs en flujos de trabajo DevOps</title><link>https://adurrr.github.io/p/llmops-integrando-llms-en-flujos-de-trabajo-devops/</link><pubDate>Sun, 15 Jun 2025 00:00:00 +0000</pubDate><guid>https://adurrr.github.io/p/llmops-integrando-llms-en-flujos-de-trabajo-devops/</guid><description>&lt;p&gt;Los LLMs han ido más allá de chatbots. Ahora se integran en flujos de trabajo de ingeniería donde automatizan tareas tediosas, aceleran respuesta a incidentes y potencian productividad. Pero desplegar un LLM en un pipeline de DevOps en producción es fundamentalmente diferente a usar ChatGPT en un navegador.&lt;/p&gt;
&lt;p&gt;Esta guía cubre qué significa LLMOps en la práctica, dónde encajan los LLMs en DevOps, patrones de arquitectura que funcionan y trampas que debes evitar.&lt;/p&gt;
&lt;h2 id="qué-es-llmops"&gt;Qué es LLMOps
&lt;/h2&gt;&lt;p&gt;LLMOps es el conjunto de prácticas, herramientas e infraestructura necesarios para operacionalizar LLMs. Extiende MLOps pero aborda desafíos únicos de los modelos de lenguaje:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Selección de modelo vs. entrenamiento de modelo&lt;/strong&gt;: La mayoría de equipos consumen modelos pre-entrenados (mediante APIs o inferencia auto-alojada) en lugar de entrenar desde cero. El foco operacional se desplaza hacia prompt engineering, fine-tuning y generación aumentada por recuperación (RAG).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Gestión de costes&lt;/strong&gt;: La inferencia con LLMs es cara. La tarificación por tokens significa que los costes escalan con el uso de formas más difíciles de predecir que el cómputo tradicional.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;No determinismo&lt;/strong&gt;: Los LLMs producen salidas variables para la misma entrada, lo que complica las pruebas, la validación y la reproducibilidad.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Latencia&lt;/strong&gt;: Tiempos de respuesta de segundos (no milisegundos) requieren patrones arquitectónicos diferentes a los microservicios tradicionales.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;LLMOps no es una disciplina separada. Es una extensión de tus prácticas existentes de DevOps y MLOps, adaptada a las características operacionales específicas de los modelos de lenguaje.&lt;/p&gt;
&lt;h2 id="casos-de-uso-prácticos-en-devops"&gt;Casos de uso prácticos en DevOps
&lt;/h2&gt;&lt;p&gt;Aquí es donde los LLMs están aportando valor real en flujos de trabajo DevOps hoy en día:&lt;/p&gt;
&lt;h3 id="revisión-automatizada-de-código"&gt;Revisión automatizada de código
&lt;/h3&gt;&lt;p&gt;Los LLMs pueden proporcionar una primera pasada de revisión de pull requests, detectando problemas comunes como manejo de errores ausente, anti-patrones de seguridad, nomenclatura inconsistente o tests faltantes. No reemplazan a los revisores humanos, pero reducen la carga del feedback repetitivo.&lt;/p&gt;
&lt;h3 id="resumen-de-incidentes"&gt;Resumen de incidentes
&lt;/h3&gt;&lt;p&gt;Cuando un incidente salta a las 3 de la mañana, la persona de guardia necesita contexto rápido. Un LLM puede ingerir datos de alertas, logs de despliegues recientes, runbooks relacionados e informes de incidentes anteriores para producir un resumen conciso de lo que probablemente está fallando y qué se hizo la última vez.&lt;/p&gt;
&lt;h3 id="análisis-de-logs"&gt;Análisis de logs
&lt;/h3&gt;&lt;p&gt;Los LLMs son sorprendentemente efectivos en el reconocimiento de patrones en datos de logs no estructurados. Aliméntalos con un bloque de logs de error y pueden identificar la causa raíz más rápido que sesiones manuales de grep, especialmente para sistemas con los que no estás familiarizado.&lt;/p&gt;
&lt;h3 id="generación-de-documentación"&gt;Generación de documentación
&lt;/h3&gt;&lt;p&gt;Generar borradores de documentación a partir de código, esquemas de API o módulos de Terraform. El resultado necesita revisión humana, pero elimina el problema de la página en blanco y mantiene la documentación más cercana al estado actual.&lt;/p&gt;
&lt;h3 id="generación-de-infrastructure-as-code"&gt;Generación de Infrastructure as Code
&lt;/h3&gt;&lt;p&gt;Dada una descripción en lenguaje natural de la infraestructura deseada, los LLMs pueden generar manifiestos de Terraform, Ansible o Kubernetes como punto de partida. Útil para scaffolding, no para código listo para producción sin revisión.&lt;/p&gt;
&lt;h2 id="patrones-de-arquitectura-para-integración-de-llms"&gt;Patrones de arquitectura para integración de LLMs
&lt;/h2&gt;&lt;h3 id="patrón-1-api-gateway-a-llm-externo"&gt;Patrón 1: API gateway a LLM externo
&lt;/h3&gt;&lt;p&gt;El enfoque más simple. Tu aplicación llama a una API de LLM externa (OpenAI, Anthropic, etc.) a través de un gateway centralizado que gestiona autenticación, rate limiting, logging y seguimiento de costes.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[Pipeline CI/CD] --&amp;gt; [API Gateway] --&amp;gt; [API LLM Externa]
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; [Logging y Métricas]
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; [Seguimiento de Costes]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Pros&lt;/strong&gt;: Sin infraestructura que gestionar, acceso a los modelos más capaces, rápido de implementar.
&lt;strong&gt;Contras&lt;/strong&gt;: Los datos salen de tu red, dependencia del proveedor, latencia variable, costes continuos de API.&lt;/p&gt;
&lt;h3 id="patrón-2-inferencia-auto-alojada"&gt;Patrón 2: Inferencia auto-alojada
&lt;/h3&gt;&lt;p&gt;Ejecutar modelos de pesos abiertos (Llama, Mistral, etc.) en tu propia infraestructura usando servidores de inferencia como vLLM u Ollama.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[Pipeline CI/CD] --&amp;gt; [Load Balancer] --&amp;gt; [Instancia(s) vLLM / Ollama]
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; [Pool de Nodos GPU]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Pros&lt;/strong&gt;: Los datos permanecen internos, costes predecibles a escala, sin dependencia de proveedor, control total sobre versiones del modelo.
&lt;strong&gt;Contras&lt;/strong&gt;: Requiere infraestructura GPU, sobrecarga operacional, modelos más pequeños pueden ser menos capaces.&lt;/p&gt;
&lt;h3 id="patrón-3-pipeline-mejorado-con-rag"&gt;Patrón 3: Pipeline mejorado con RAG
&lt;/h3&gt;&lt;p&gt;Combinar un LLM con un sistema de recuperación que proporciona contexto relevante de tu propia base de conocimiento (runbooks, documentación, incidentes pasados). Esto mejora drásticamente la calidad de las respuestas para tareas específicas del dominio.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[Consulta] --&amp;gt; [Modelo Embedding] --&amp;gt; [Búsqueda Vector DB] --&amp;gt; [Contexto + Consulta] --&amp;gt; [LLM] --&amp;gt; [Respuesta]
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; [Tu Base de Conocimiento]
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; (runbooks, docs, etc.)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Este patrón es particularmente potente para respuesta a incidentes y tareas de documentación donde el LLM necesita el contexto específico de tu organización.&lt;/p&gt;
&lt;h2 id="consideraciones-clave"&gt;Consideraciones clave
&lt;/h2&gt;&lt;h3 id="coste"&gt;Coste
&lt;/h3&gt;&lt;p&gt;Los costes de API de LLMs pueden sorprender. Un pipeline de revisión de código que procesa 50 PRs al día con diffs grandes puede fácilmente alcanzar cientos de dólares al mes. Estrategias para controlar costes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Establecer límites de tokens por petición&lt;/li&gt;
&lt;li&gt;Cachear consultas y respuestas comunes&lt;/li&gt;
&lt;li&gt;Usar modelos más pequeños para tareas simples (triaje con un modelo pequeño, escalar a uno mayor)&lt;/li&gt;
&lt;li&gt;Monitorizar el uso de tokens por pipeline y configurar alertas&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="latencia"&gt;Latencia
&lt;/h3&gt;&lt;p&gt;Las respuestas de LLMs tardan segundos, no milisegundos. Diseña tus integraciones como procesos asíncronos:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Publicar comentarios de revisión de código después del hecho, no bloquear la PR&lt;/li&gt;
&lt;li&gt;Procesar datos de incidentes en segundo plano, enviar resultados a un canal de Slack&lt;/li&gt;
&lt;li&gt;Usar streaming de respuestas donde sea posible para mejorar el rendimiento percibido&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="alucinaciones"&gt;Alucinaciones
&lt;/h3&gt;&lt;p&gt;Los LLMs generarán con confianza información que suena plausible pero es incorrecta. Esta es una preocupación crítica para tareas de DevOps donde un mal consejo puede causar caídas.&lt;/p&gt;
&lt;p&gt;Mitigaciones:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Siempre presentar la salida del LLM como sugerencias, nunca como acciones autoritativas&lt;/li&gt;
&lt;li&gt;Requerir aprobación humana antes de aplicar cualquier cambio generado por LLM&lt;/li&gt;
&lt;li&gt;Usar RAG para anclar las respuestas en documentación verificada&lt;/li&gt;
&lt;li&gt;Implementar validación de salida (por ejemplo, lint del IaC generado antes de presentarlo)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="seguridad"&gt;Seguridad
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Exposición de datos&lt;/strong&gt;: Cualquier cosa que envíes a una API de LLM externa puede ser usada para entrenamiento o almacenada. Nunca envíes secretos, credenciales o datos sensibles de clientes.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Prompt injection&lt;/strong&gt;: Contenido malicioso en código, logs o entrada de usuario puede manipular el comportamiento del LLM. Sanitiza las entradas y valida las salidas.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cadena de suministro&lt;/strong&gt;: El código generado por LLM puede introducir vulnerabilidades. Pasa todo el código generado por tu pipeline de escaneo de seguridad existente.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="herramientas-y-plataformas"&gt;Herramientas y plataformas
&lt;/h2&gt;&lt;h3 id="langchain"&gt;LangChain
&lt;/h3&gt;&lt;p&gt;Un framework para construir aplicaciones potenciadas por LLMs. Útil para orquestar cadenas de múltiples pasos (por ejemplo, recuperar contexto, formatear prompt, llamar al LLM, parsear la salida). Soporta muchos proveedores de LLMs y tiene buen tooling para pipelines RAG.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;span class="lnt"&gt;7
&lt;/span&gt;&lt;span class="lnt"&gt;8
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;langchain.chat_models&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ChatOpenAI&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;langchain.prompts&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ChatPromptTemplate&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ChatPromptTemplate&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;from_template&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;Review this code diff for security issues and suggest fixes:&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="si"&gt;{diff}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;chain&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;ChatOpenAI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;gpt-4o&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;temperature&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;chain&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;invoke&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;diff&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;code_diff&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="vllm"&gt;vLLM
&lt;/h3&gt;&lt;p&gt;Un motor de inferencia de alto rendimiento para modelos auto-alojados. Soporta PagedAttention para gestión eficiente de memoria y continuous batching para alto throughput.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Iniciar un servidor vLLM&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;python -m vllm.entrypoints.openai.api_server &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --model mistralai/Mistral-7B-Instruct-v0.2 &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --port &lt;span class="m"&gt;8000&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Expone una API compatible con OpenAI, así que puedes cambiar entre APIs auto-alojadas y externas con cambios mínimos de código.&lt;/p&gt;
&lt;h3 id="ollama"&gt;Ollama
&lt;/h3&gt;&lt;p&gt;La forma más fácil de ejecutar LLMs localmente para desarrollo y pruebas. Ideal para prototipar pipelines antes de comprometerte con infraestructura.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;span class="lnt"&gt;7
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Descargar y ejecutar un modelo&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ollama pull llama3
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ollama run llama3 &lt;span class="s2"&gt;&amp;#34;Summarize this error log: [paste log]&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Servir como API&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ollama serve
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Luego llamar a http://localhost:11434/api/generate&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id="ejemplo-pipeline-de-revisión-automatizada-de-prs"&gt;Ejemplo: Pipeline de revisión automatizada de PRs
&lt;/h2&gt;&lt;p&gt;Aquí tienes un pipeline conceptual para revisión automatizada de PRs usando un LLM:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;span class="lnt"&gt;21
&lt;/span&gt;&lt;span class="lnt"&gt;22
&lt;/span&gt;&lt;span class="lnt"&gt;23
&lt;/span&gt;&lt;span class="lnt"&gt;24
&lt;/span&gt;&lt;span class="lnt"&gt;25
&lt;/span&gt;&lt;span class="lnt"&gt;26
&lt;/span&gt;&lt;span class="lnt"&gt;27
&lt;/span&gt;&lt;span class="lnt"&gt;28
&lt;/span&gt;&lt;span class="lnt"&gt;29
&lt;/span&gt;&lt;span class="lnt"&gt;30
&lt;/span&gt;&lt;span class="lnt"&gt;31
&lt;/span&gt;&lt;span class="lnt"&gt;32
&lt;/span&gt;&lt;span class="lnt"&gt;33
&lt;/span&gt;&lt;span class="lnt"&gt;34
&lt;/span&gt;&lt;span class="lnt"&gt;35
&lt;/span&gt;&lt;span class="lnt"&gt;36
&lt;/span&gt;&lt;span class="lnt"&gt;37
&lt;/span&gt;&lt;span class="lnt"&gt;38
&lt;/span&gt;&lt;span class="lnt"&gt;39
&lt;/span&gt;&lt;span class="lnt"&gt;40
&lt;/span&gt;&lt;span class="lnt"&gt;41
&lt;/span&gt;&lt;span class="lnt"&gt;42
&lt;/span&gt;&lt;span class="lnt"&gt;43
&lt;/span&gt;&lt;span class="lnt"&gt;44
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# .github/workflows/llm-review.yml&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;LLM Code Review&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;on&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;pull_request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;types&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="l"&gt;opened, synchronize]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;jobs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;llm-review&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;runs-on&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;ubuntu-latest&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;steps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Checkout&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;uses&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;actions/checkout@v4&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;with&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;fetch-depth&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Get diff&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;diff&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;run&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="sd"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; git diff origin/${{ github.base_ref }}...HEAD &amp;gt; diff.txt&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Run LLM review&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;LLM_API_KEY&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${{ secrets.LLM_API_KEY }}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;run&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="sd"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; python scripts/llm_review.py \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; --diff diff.txt \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; --model gpt-4o \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; --max-tokens 2000 \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; --output review.json&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Post review comments&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;uses&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;actions/github-script@v7&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;with&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="sd"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; const review = require(&amp;#39;./review.json&amp;#39;);
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; await github.rest.pulls.createReview({
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; owner: context.repo.owner,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; repo: context.repo.repo,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; pull_number: context.issue.number,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; body: review.summary,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; event: &amp;#39;COMMENT&amp;#39;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; comments: review.line_comments
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; });&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;El script de revisión:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Lee el diff&lt;/li&gt;
&lt;li&gt;Divide diffs grandes en fragmentos que quepan en la ventana de contexto del modelo&lt;/li&gt;
&lt;li&gt;Para cada fragmento, construye un prompt pidiendo problemas de seguridad, bugs y problemas de estilo&lt;/li&gt;
&lt;li&gt;Agrega resultados y formatea como comentarios de revisión de GitHub&lt;/li&gt;
&lt;li&gt;Incluye puntuaciones de confianza y siempre marca la salida como generada por IA&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="guardrails-y-uso-responsable"&gt;Guardrails y uso responsable
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Etiqueta claramente toda la salida del LLM&lt;/strong&gt; como generada por IA. Los ingenieros deben saber cuándo están leyendo salida de una máquina.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Nunca auto-mergees ni auto-apliques&lt;/strong&gt; sugerencias del LLM. Mantén un humano en el bucle para todos los cambios.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Registra todos los prompts y respuestas&lt;/strong&gt; para depuración y auditoría.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Establece límites de gasto&lt;/strong&gt; y alertas sobre el uso de API de LLMs.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Revisa las plantillas de prompts regularmente&lt;/strong&gt; para asegurarte de que no filtran información sensible.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Prueba sesgos y errores&lt;/strong&gt; con muestras representativas antes de desplegar en flujos de trabajo de producción.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="recomendaciones-para-empezar"&gt;Recomendaciones para empezar
&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Elige un caso de uso&lt;/strong&gt; - No intentes habilitar LLMs en todo. Empieza bajo riesgo: borradores, sugerencias de commits.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Empieza con API externa&lt;/strong&gt; - No inviertas en GPU hasta validar el caso. Usa OpenAI o Anthropic para prototipar.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Mide todo&lt;/strong&gt; - Registra coste por invocación, latencia, satisfacción, tasas de error desde el primer día.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Construye un framework&lt;/strong&gt; - Crea un suite de tests con entradas y salidas conocidas. Ejecútalo contra cada cambio de prompt o modelo.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Planifica tu estrategia de datos&lt;/strong&gt; - Decide qué datos enviarás a APIs externas. Documéntalo.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Itera en prompts&lt;/strong&gt; - El prompt engineering es iterativo. Versiona prompts, trátalos como código.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Los LLMs son una herramienta potente para DevOps, pero son exactamente eso: una herramienta. Funcionan mejor cuando se integran de forma reflexiva en flujos existentes, con límites claros sobre qué pueden y no pueden hacer autonomamente.&lt;/p&gt;</description></item><item><title>DataOps: construyendo pipelines de datos confiables</title><link>https://adurrr.github.io/p/dataops-construyendo-pipelines-de-datos-confiables/</link><pubDate>Sun, 18 Feb 2024 00:00:00 +0000</pubDate><guid>https://adurrr.github.io/p/dataops-construyendo-pipelines-de-datos-confiables/</guid><description>&lt;h2 id="qué-es-dataops"&gt;¿Qué es DataOps?
&lt;/h2&gt;&lt;p&gt;DataOps aplica principios de DevOps (automatización, integración continua, monitorización, colaboración) a pipelines de datos y analítica. Mientras DevOps entrega software de forma fiable, DataOps entrega &lt;em&gt;datos&lt;/em&gt; de forma fiable.&lt;/p&gt;
&lt;p&gt;La diferencia: qué fluye por el pipeline. DevOps construye, testea, despliega código. DataOps construye, testea, despliega transformaciones de datos. El objetivo: asegurar que datos lleguen a dashboards, modelos de ML y sistemas downstream correctos, frescos, confiables.&lt;/p&gt;
&lt;p&gt;Si alguna vez tuviste un dashboard roto el lunes porque un esquema cambio el fin de semana, sabes por qué DataOps importa.&lt;/p&gt;
&lt;h2 id="principios-fundamentales"&gt;Principios fundamentales
&lt;/h2&gt;&lt;h3 id="1-automatización-primero"&gt;1. Automatización primero
&lt;/h3&gt;&lt;p&gt;Cada paso en tu pipeline de datos &amp;ndash; extracción, transformación, carga, testing y despliegue &amp;ndash; debe estar automatizado. Scripts SQL manuales ejecutados desde el portátil de alguien son un riesgo. Codifica todo, versiona en Git y deja que los orquestadores se encarguen de la ejecución.&lt;/p&gt;
&lt;h3 id="2-testing-continuo"&gt;2. Testing continuo
&lt;/h3&gt;&lt;p&gt;El testing de datos no es opcional. Debes validar los datos en cada etapa:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Tests de esquema&lt;/strong&gt;: tipos de columna, restricciones de nulabilidad&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tests de volumen&lt;/strong&gt;: conteos de filas dentro de rangos esperados&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tests de frescura&lt;/strong&gt;: los datos llegaron según lo programado&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tests de reglas de negocio&lt;/strong&gt;: los ingresos nunca son negativos, las fechas no están en el futuro&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="3-monitorización-y-observabilidad"&gt;3. Monitorización y observabilidad
&lt;/h3&gt;&lt;p&gt;Necesitas saber cuándo algo se rompe antes que tus stakeholders. Instrumenta tus pipelines con métricas de latencia, conteo de filas, tasas de error y puntuaciones de calidad de datos. Configura alertas que se disparen cuando se detecten anomalías.&lt;/p&gt;
&lt;h3 id="4-colaboración-y-control-de-versiones"&gt;4. Colaboración y control de versiones
&lt;/h3&gt;&lt;p&gt;Los pipelines de datos son código. Trátalos como tal. Usa pull requests, code reviews y CI/CD para tu lógica de transformación. Cada cambio en un pipeline debe ser revisable, testeable y reversible.&lt;/p&gt;
&lt;h2 id="arquitectura-de-pipelines-etl-vs-elt"&gt;Arquitectura de pipelines: ETL vs ELT
&lt;/h2&gt;&lt;p&gt;Los dos patrones dominantes para pipelines de datos son ETL y ELT. La elección depende de tu infraestructura y caso de uso.&lt;/p&gt;
&lt;h3 id="etl-extract-transform-load"&gt;ETL (Extract, Transform, Load)
&lt;/h3&gt;&lt;p&gt;Los datos se extraen de las fuentes, se transforman en un motor de procesamiento (Spark, scripts Python) y luego se cargan en el sistema destino. Este patrón tiene sentido cuando:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Necesitas reducir el volumen de datos antes de cargar (control de costes)&lt;/li&gt;
&lt;li&gt;Las transformaciones requieren computacion pesada no adecuada para tu warehouse&lt;/li&gt;
&lt;li&gt;Tienes requisitos estrictos de gobernanza que requieren transformacion antes del almacenamiento&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="elt-extract-load-transform"&gt;ELT (Extract, Load, Transform)
&lt;/h3&gt;&lt;p&gt;Los datos se extraen y cargan en crudo en un data warehouse (BigQuery, Snowflake, Redshift), luego se transforman in situ usando SQL. Este es el enfoque moderno por defecto porque:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Los warehouses en la nube tienen capacidad de computo masiva&lt;/li&gt;
&lt;li&gt;Las transformaciones basadas en SQL son más fáciles de revisar y testear&lt;/li&gt;
&lt;li&gt;Los datos en crudo se preservan, permitiendo reprocesamiento cuando la logica cambia&lt;/li&gt;
&lt;li&gt;Herramientas como dbt hacen de las transformaciones SQL ciudadanos de primera clase&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Para la mayoría de equipos que empiezan hoy, &lt;strong&gt;ELT es el enfoque recomendado&lt;/strong&gt; a menos que tengas una razón específica para transformar antes de cargar.&lt;/p&gt;
&lt;h2 id="herramientas-clave"&gt;Herramientas clave
&lt;/h2&gt;&lt;h3 id="apache-airflow--orquestación"&gt;Apache Airflow &amp;ndash; orquestación
&lt;/h3&gt;&lt;p&gt;Airflow es el orquestador open-source más ampliamente adoptado para pipelines de datos. Te permite definir flujos de trabajo como Directed Acyclic Graphs (DAGs) en Python, con planificación integrada, reintentos, gestión de dependencias y una interfaz web para monitorización.&lt;/p&gt;
&lt;p&gt;Aquí tienes un ejemplo práctico de un DAG que orquesta un pipeline ELT:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;span class="lnt"&gt;21
&lt;/span&gt;&lt;span class="lnt"&gt;22
&lt;/span&gt;&lt;span class="lnt"&gt;23
&lt;/span&gt;&lt;span class="lnt"&gt;24
&lt;/span&gt;&lt;span class="lnt"&gt;25
&lt;/span&gt;&lt;span class="lnt"&gt;26
&lt;/span&gt;&lt;span class="lnt"&gt;27
&lt;/span&gt;&lt;span class="lnt"&gt;28
&lt;/span&gt;&lt;span class="lnt"&gt;29
&lt;/span&gt;&lt;span class="lnt"&gt;30
&lt;/span&gt;&lt;span class="lnt"&gt;31
&lt;/span&gt;&lt;span class="lnt"&gt;32
&lt;/span&gt;&lt;span class="lnt"&gt;33
&lt;/span&gt;&lt;span class="lnt"&gt;34
&lt;/span&gt;&lt;span class="lnt"&gt;35
&lt;/span&gt;&lt;span class="lnt"&gt;36
&lt;/span&gt;&lt;span class="lnt"&gt;37
&lt;/span&gt;&lt;span class="lnt"&gt;38
&lt;/span&gt;&lt;span class="lnt"&gt;39
&lt;/span&gt;&lt;span class="lnt"&gt;40
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;airflow&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;DAG&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;airflow.operators.python&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;PythonOperator&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;airflow.providers.common.sql.operators.sql&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;SQLExecuteQueryOperator&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;airflow.utils.dates&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;days_ago&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;datetime&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;timedelta&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;default_args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;owner&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;data-team&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;retries&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;retry_delay&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;timedelta&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;minutes&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;email_on_failure&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;email&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;data-alerts@company.com&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;DAG&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;dag_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;elt_sales_pipeline&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;default_args&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;default_args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;schedule_interval&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;@daily&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;start_date&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;days_ago&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;catchup&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;tags&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;elt&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;sales&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;dag&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;extract_load&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PythonOperator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;task_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;extract_and_load_raw&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;python_callable&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;extract_and_load_sales_data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;# tu funcion de extraccion&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;transform&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;SQLExecuteQueryOperator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;task_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;transform_sales&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;conn_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;warehouse_conn&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;sql&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;sql/transform_sales.sql&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;run_quality_checks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PythonOperator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;task_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;data_quality_checks&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;python_callable&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;run_great_expectations_suite&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;extract_load&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;transform&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;run_quality_checks&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Patrones clave a seguir en Airflow:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Tareas idempotentes&lt;/strong&gt;: ejecutar la misma tarea dos veces debe producir el mismo resultado&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Escrituras atomicas&lt;/strong&gt;: usa tablas staging e intercambia al completarse&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Fechas parametrizadas&lt;/strong&gt;: usa variables template &lt;code&gt;{{ ds }}&lt;/code&gt; para particionamiento por fecha&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tareas pequeñas&lt;/strong&gt;: cada tarea debe hacer una sola cosa, facilitando el diagnóstico de fallos&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="dbt--transformacion"&gt;dbt &amp;ndash; transformacion
&lt;/h3&gt;&lt;p&gt;dbt (data build tool) es el estandar para gestionar transformaciones basadas en SQL en un pipeline ELT. Proporciona:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;SQL modular&lt;/strong&gt;: divide transformaciones complejas en modelos referenciables&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Testing integrado&lt;/strong&gt;: tests de esquema, tests personalizados y chequeos de frescura&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Documentacion&lt;/strong&gt;: documentacion auto-generada a partir de las descripciones de tus modelos&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Linaje&lt;/strong&gt;: DAG visual mostrando como los modelos dependen unos de otros&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Una estructura tipica de proyecto dbt:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;span class="lnt"&gt;7
&lt;/span&gt;&lt;span class="lnt"&gt;8
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;models/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; staging/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; stg_sales.sql -- limpiar datos en crudo
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; stg_customers.sql
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; marts/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; fct_daily_revenue.sql -- agregaciones a nivel de negocio
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; dim_customers.sql
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; schema.yml -- tests y documentacion
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="great-expectations--calidad-de-datos"&gt;Great Expectations &amp;ndash; calidad de datos
&lt;/h3&gt;&lt;p&gt;Great Expectations es un framework Python para definir, ejecutar y documentar chequeos de calidad de datos. Va mas alla de simples aserciones generando documentacion de datos legible por humanos.&lt;/p&gt;
&lt;p&gt;Aqui tienes un ejemplo de configuración de expectations para una tabla de ventas:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;span class="lnt"&gt;21
&lt;/span&gt;&lt;span class="lnt"&gt;22
&lt;/span&gt;&lt;span class="lnt"&gt;23
&lt;/span&gt;&lt;span class="lnt"&gt;24
&lt;/span&gt;&lt;span class="lnt"&gt;25
&lt;/span&gt;&lt;span class="lnt"&gt;26
&lt;/span&gt;&lt;span class="lnt"&gt;27
&lt;/span&gt;&lt;span class="lnt"&gt;28
&lt;/span&gt;&lt;span class="lnt"&gt;29
&lt;/span&gt;&lt;span class="lnt"&gt;30
&lt;/span&gt;&lt;span class="lnt"&gt;31
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;great_expectations&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nn"&gt;gx&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;gx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_context&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Conectar a tu fuente de datos&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;datasource&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sources&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_or_update_pandas&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;sales_source&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;data_asset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;datasource&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_csv_asset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;daily_sales&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;filepath_or_buffer&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;data/daily_sales.csv&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;batch_request&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;data_asset&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;build_batch_request&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Crear una suite de expectations&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;suite&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_or_update_expectation_suite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;sales_quality_suite&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;validator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_validator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;batch_request&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;batch_request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;expectation_suite_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;sales_quality_suite&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Definir expectations&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;validator&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;expect_column_to_exist&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;order_id&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;validator&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;expect_column_values_to_be_unique&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;order_id&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;validator&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;expect_column_values_to_not_be_null&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;customer_id&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;validator&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;expect_column_values_to_be_between&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;amount&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;min_value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;100000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;validator&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;expect_column_values_to_be_in_set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;status&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;pending&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;completed&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;refunded&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Ejecutar validacion&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;validator&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;validator&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;save_expectation_suite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;discard_failed_expectations&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;success&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="ne"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Data quality checks failed: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;statistics&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Integra esto en tu DAG de Airflow para que las puertas de calidad se ejecuten despues de cada paso de transformacion. Si los chequeos fallan, el pipeline se detiene y las alertas se disparan.&lt;/p&gt;
&lt;h2 id="monitorizacion-y-observabilidad"&gt;Monitorizacion y observabilidad
&lt;/h2&gt;&lt;p&gt;Un pipeline de datos en producción necesita observabilidad en varias dimensiones:&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Dimension&lt;/th&gt;
 &lt;th&gt;Que Rastrear&lt;/th&gt;
 &lt;th&gt;Herramientas&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;Salud del pipeline&lt;/td&gt;
 &lt;td&gt;Tasas de exito/fallo de tareas, tendencias de duracion&lt;/td&gt;
 &lt;td&gt;Metricas de Airflow, Prometheus&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Frescura de datos&lt;/td&gt;
 &lt;td&gt;Tiempo desde la ultima carga exitosa&lt;/td&gt;
 &lt;td&gt;dbt source freshness, chequeos personalizados&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Volumen de datos&lt;/td&gt;
 &lt;td&gt;Conteo de filas por tabla por ejecucion&lt;/td&gt;
 &lt;td&gt;Great Expectations, SQL personalizado&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Calidad de datos&lt;/td&gt;
 &lt;td&gt;Tasas de tests aprobados/fallidos, puntuaciones de anomalias&lt;/td&gt;
 &lt;td&gt;Great Expectations, Monte Carlo&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Coste&lt;/td&gt;
 &lt;td&gt;Uso de computo del warehouse, crecimiento del almacenamiento&lt;/td&gt;
 &lt;td&gt;Dashboards del proveedor cloud&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Configura alertas para:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Cualquier fallo de tarea del pipeline&lt;/li&gt;
&lt;li&gt;Frescura de datos excediendo umbrales de SLA&lt;/li&gt;
&lt;li&gt;Desviaciones de conteo de filas mas alla de 2 desviaciones estandar de la media movil&lt;/li&gt;
&lt;li&gt;Fallos en tests de calidad de datos&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Envia metricas de Airflow a Prometheus y construye dashboards de Grafana que den a tu equipo una vista unificada de la salud del pipeline.&lt;/p&gt;
&lt;h2 id="buenas-prácticas"&gt;Buenas prácticas
&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Trata los pipelines como código&lt;/strong&gt;: todo el SQL, definiciones de DAGs y configuración viven en Git&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Usa entornos&lt;/strong&gt;: dev, staging, producción &amp;ndash; igual que el código de aplicacion&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Implementa CI/CD&lt;/strong&gt;: ejecuta tests de dbt y linting en cada pull request&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Disena para el fallo&lt;/strong&gt;: cada tarea debe ser reintentable e idempotente&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Documenta contratos de datos&lt;/strong&gt;: define y publica esquemas acordados entre equipos upstream y downstream&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Empieza con testing&lt;/strong&gt;: anade chequeos de calidad de datos antes de anadir nuevas funcionalidades&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Alerta sobre SLAs, no solo fallos&lt;/strong&gt;: un pipeline que tiene exito pero tarda 3x mas de lo habitual sigue siendo un problema&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Mantener los datos en crudo inmutables&lt;/strong&gt;: nunca modifiques datos fuente; transforma en tablas separadas&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;DataOps no es una herramienta. Es un conjunto de prácticas que hacen tu infraestructura fiable, testeable, mantenible. Empieza con orquestación y testing, luego añade monitorización y chequeos conforme madures.&lt;/p&gt;</description></item><item><title>Patrones de diseño para pipelines MLOps</title><link>https://adurrr.github.io/p/patrones-de-dise%C3%B1o-para-pipelines-mlops/</link><pubDate>Wed, 22 Mar 2023 10:00:00 +0100</pubDate><guid>https://adurrr.github.io/p/patrones-de-dise%C3%B1o-para-pipelines-mlops/</guid><description>&lt;h2 id="qué-es-mlops-y-por-qué-importa"&gt;Qué es MLOps y por qué importa
&lt;/h2&gt;&lt;p&gt;MLOps trata de desplegar y mantener modelos en producción de forma fiable y eficiente. Conecta la experimentación en ciencia de datos con la ingeniería de producción. Sin MLOps, chocas con los mismos problemas: modelos que funcionan en notebooks pero fallan en producción, sin reproducibilidad, traspasos dolorosos entre equipos, cero visibilidad una vez desplegados.&lt;/p&gt;
&lt;p&gt;La idea: trata sistemas de ML con el mismo rigor que software. Control de versiones, pruebas automatizadas, entrega continua, monitorización. Solo reconoce que datos y modelos introducen desafíos únicos.&lt;/p&gt;
&lt;h2 id="el-ciclo-de-vida-de-ml"&gt;El ciclo de vida de ML
&lt;/h2&gt;&lt;p&gt;Antes de entrar en los patrones, conviene entender las etapas por las que pasa todo sistema de ML:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Ingesta y validación de datos&lt;/strong&gt; &amp;mdash; Recopilar, limpiar y validar los datos de entrada.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Ingeniería de features&lt;/strong&gt; &amp;mdash; Transformar los datos en bruto en features que el modelo pueda consumir.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Entrenamiento del modelo&lt;/strong&gt; &amp;mdash; Ejecutar experimentos, ajustar hiperparámetros, seleccionar algoritmos.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Evaluación del modelo&lt;/strong&gt; &amp;mdash; Validar la calidad del modelo con datos de prueba y métricas de negocio.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Despliegue del modelo&lt;/strong&gt; &amp;mdash; Servir predicciones en producción (batch o tiempo real).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Monitorización y retroalimentación&lt;/strong&gt; &amp;mdash; Seguimiento del rendimiento, detección de drift, activación de reentrenamiento.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Cada etapa tiene sus propios modos de fallo, y los patrones que se presentan a continuación los abordan de forma sistemática.&lt;/p&gt;
&lt;h2 id="patrones-de-diseño-clave"&gt;Patrones de diseño clave
&lt;/h2&gt;&lt;h3 id="feature-store"&gt;Feature store
&lt;/h3&gt;&lt;p&gt;Un feature store es un repositorio centralizado para almacenar, compartir y servir features de ML. En lugar de que cada equipo recalcule features desde cero, un feature store proporciona:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Consistencia&lt;/strong&gt; entre entrenamiento y serving (evitando el training-serving skew).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Reutilización&lt;/strong&gt; entre equipos y modelos.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Corrección temporal&lt;/strong&gt; para valores históricos de features.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Herramientas como &lt;strong&gt;Feast&lt;/strong&gt;, &lt;strong&gt;Tecton&lt;/strong&gt; y &lt;strong&gt;Hopsworks&lt;/strong&gt; implementan este patrón. Si varios equipos están duplicando pipelines de features, un feature store probablemente merezca la inversión.&lt;/p&gt;
&lt;h3 id="model-registry"&gt;Model registry
&lt;/h3&gt;&lt;p&gt;Un model registry actúa como catálogo versionado de modelos entrenados. Almacena artefactos del modelo, metadatos (hiperparámetros, métricas, versión de los datos de entrenamiento) y etapa del ciclo de vida (staging, producción, archivado).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;MLflow Model Registry&lt;/strong&gt; es una de las soluciones más adoptadas. Permite promover modelos a través de etapas con flujos de aprobación y rastrear la trazabilidad desde el experimento hasta producción.&lt;/p&gt;
&lt;h3 id="ctcicd-para-ml"&gt;CT/CI/CD para ML
&lt;/h3&gt;&lt;p&gt;Los pipelines tradicionales de CI/CD construyen y despliegan código. Los pipelines de ML necesitan tres bucles:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Continuous Training (CT)&lt;/strong&gt; &amp;mdash; Reentrenar modelos automáticamente cuando cambian los datos o el rendimiento se degrada.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Continuous Integration (CI)&lt;/strong&gt; &amp;mdash; Validar no solo el código sino también esquemas de datos, expectativas de features y umbrales de calidad del modelo.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Continuous Delivery (CD)&lt;/strong&gt; &amp;mdash; Desplegar modelos validados a la infraestructura de serving de forma automática.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Un flujo típico podría ser: llegan datos nuevos al data lake, CT lanza el reentrenamiento, CI ejecuta las pruebas de validación y CD publica el modelo en producción si todas las verificaciones pasan.&lt;/p&gt;
&lt;h3 id="ab-testing"&gt;A/B Testing
&lt;/h3&gt;&lt;p&gt;El A/B testing para modelos consiste en dirigir un porcentaje del tráfico a un modelo nuevo mientras el resto sigue llegando al modelo actual en producción. Se miden métricas de negocio (tasa de conversión, clicks, ingresos) en lugar de solo métricas de ML (accuracy, F1). Este patrón es esencial porque un modelo que puntúa bien en offline puede funcionar mal en producción debido a bucles de retroalimentación, latencia o diferencias en la distribución.&lt;/p&gt;
&lt;h3 id="shadow-deployment"&gt;Shadow Deployment
&lt;/h3&gt;&lt;p&gt;En el modo shadow, el modelo nuevo recibe tráfico de producción y genera predicciones, pero esas predicciones &lt;strong&gt;no&lt;/strong&gt; se sirven a los usuarios. En su lugar, se registran junto a las predicciones del modelo actual para una comparación offline. Es una forma de bajo riesgo de validar un modelo con tráfico real antes de exponerlo a los usuarios.&lt;/p&gt;
&lt;h3 id="canary-releases-para-modelos"&gt;Canary releases para modelos
&lt;/h3&gt;&lt;p&gt;Similar a los canary deployments en software, se despliega un nuevo modelo a una pequeña fracción del tráfico (por ejemplo, el 5%), se monitorizan las métricas clave y se aumenta gradualmente el tráfico si todo se mantiene saludable. Si las métricas se degradan, se realiza un rollback automático. Este patrón se combina bien con A/B testing pero se centra más en la mitigación de riesgos que en la experimentación.&lt;/p&gt;
&lt;h2 id="panorama-de-herramientas"&gt;Panorama de herramientas
&lt;/h2&gt;&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Herramienta&lt;/th&gt;
 &lt;th&gt;Uso principal&lt;/th&gt;
 &lt;th&gt;Punto fuerte&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;MLflow&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;Tracking de experimentos, model registry&lt;/td&gt;
 &lt;td&gt;Flexible, independiente del proveedor&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;Kubeflow&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;Pipelines de ML de extremo a extremo en Kubernetes&lt;/td&gt;
 &lt;td&gt;Escalable, cloud-native&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;DVC&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;Versionado de datos y modelos&lt;/td&gt;
 &lt;td&gt;Flujo de trabajo similar a Git para datos&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;Weights &amp;amp; Biases&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;Tracking de experimentos, visualización&lt;/td&gt;
 &lt;td&gt;Excelente interfaz y colaboración&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;Feast&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;Feature store&lt;/td&gt;
 &lt;td&gt;Open-source, listo para producción&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;Seldon Core&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;Model serving en Kubernetes&lt;/td&gt;
 &lt;td&gt;Estrategias de despliegue avanzadas&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;No existe una herramienta que cubra todo. La mayoría de las configuraciones en producción combinan varias, eligiendo según la experiencia del equipo y las restricciones de infraestructura.&lt;/p&gt;
&lt;h2 id="ejemplo-tracking-de-experimentos-con-mlflow"&gt;Ejemplo: tracking de experimentos con MLflow
&lt;/h2&gt;&lt;p&gt;Este es un ejemplo mínimo de cómo registrar un experimento con MLflow:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;span class="lnt"&gt;21
&lt;/span&gt;&lt;span class="lnt"&gt;22
&lt;/span&gt;&lt;span class="lnt"&gt;23
&lt;/span&gt;&lt;span class="lnt"&gt;24
&lt;/span&gt;&lt;span class="lnt"&gt;25
&lt;/span&gt;&lt;span class="lnt"&gt;26
&lt;/span&gt;&lt;span class="lnt"&gt;27
&lt;/span&gt;&lt;span class="lnt"&gt;28
&lt;/span&gt;&lt;span class="lnt"&gt;29
&lt;/span&gt;&lt;span class="lnt"&gt;30
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;mlflow&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;mlflow.sklearn&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;sklearn.ensemble&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;RandomForestClassifier&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;sklearn.metrics&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;accuracy_score&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;f1_score&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;sklearn.model_selection&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;train_test_split&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Start an MLflow run&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;mlflow&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;start_run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;run_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;rf-baseline&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# Log parameters&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;n_estimators&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;max_depth&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;mlflow&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;log_param&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;n_estimators&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n_estimators&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;mlflow&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;log_param&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;max_depth&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_depth&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# Train model&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;RandomForestClassifier&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;n_estimators&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;n_estimators&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;max_depth&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;max_depth&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;random_state&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;X_train&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;X_test&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y_train&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y_test&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;train_test_split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;test_size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;X_train&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y_train&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# Log metrics&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;predictions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;predict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;X_test&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;mlflow&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;log_metric&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;accuracy&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;accuracy_score&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;y_test&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;predictions&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;mlflow&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;log_metric&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;f1_score&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;f1_score&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;y_test&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;predictions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;average&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;weighted&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# Log model artifact&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;mlflow&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sklearn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;log_model&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;random-forest-model&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Cada ejecución queda registrada con sus parámetros, métricas y artefactos, lo que facilita comparar experimentos y reproducir resultados.&lt;/p&gt;
&lt;h2 id="anti-patrones-a-evitar"&gt;Anti-patrones a evitar
&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;No versionar datos ni modelos.&lt;/strong&gt; Si no puedes reproducir un entrenamiento de hace seis meses, tienes un problema. Versiona todo: código, datos, configuración y artefactos del modelo.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Training-serving skew.&lt;/strong&gt; Cuando la lógica de cálculo de features difiere entre entrenamiento y serving, las predicciones se degradan de forma silenciosa. Un feature store o una librería compartida de cálculo de features ayuda a eliminar esto.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Despliegue manual.&lt;/strong&gt; Copiar archivos del modelo a un servidor es una receta para incidentes. Automatiza el despliegue mediante pipelines con puertas de validación adecuadas.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ignorar la monitorización del modelo.&lt;/strong&gt; Los modelos se degradan con el tiempo a medida que cambian las distribuciones de entrada. Sin monitorización, solo te enteras cuando un usuario se queja o una métrica de negocio cae. Configura alertas para cambios en la distribución de predicciones, latencia y calidad de datos.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Pipelines monolíticos.&lt;/strong&gt; Un pipeline único que hace todo desde la ingesta de datos hasta el serving del modelo es frágil y difícil de depurar. Divide los pipelines en etapas modulares e independientemente testeables.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Sobreingeniería prematura.&lt;/strong&gt; No todos los proyectos de ML necesitan Kubeflow y un feature store desde el día uno. Empieza simple, identifica cuellos de botella y adopta patrones a medida que la complejidad del sistema crece.&lt;/p&gt;
&lt;h2 id="niveles-de-madurez-mlops"&gt;Niveles de madurez MLOps
&lt;/h2&gt;&lt;p&gt;Las organizaciones suelen progresar a través de varios niveles de madurez:&lt;/p&gt;
&lt;h3 id="nivel-0-manual"&gt;Nivel 0: Manual
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;Modelos entrenados en notebooks.&lt;/li&gt;
&lt;li&gt;Despliegue manual (copia de archivos, reinicio manual de la API).&lt;/li&gt;
&lt;li&gt;Sin tracking de experimentos.&lt;/li&gt;
&lt;li&gt;Sin monitorización.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="nivel-1-automatización-del-pipeline-de-ml"&gt;Nivel 1: automatización del pipeline de ML
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;Pipelines de entrenamiento automatizados.&lt;/li&gt;
&lt;li&gt;Tracking de experimentos con herramientas como MLflow.&lt;/li&gt;
&lt;li&gt;Validación básica del modelo antes del despliegue.&lt;/li&gt;
&lt;li&gt;Algo de monitorización de las predicciones del modelo.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="nivel-2-cicd-para-ml"&gt;Nivel 2: CI/CD para ML
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;Testing automatizado de datos, features y calidad del modelo.&lt;/li&gt;
&lt;li&gt;Continuous training activado por cambios en los datos o por programación.&lt;/li&gt;
&lt;li&gt;Despliegue automatizado con canary o shadow releases.&lt;/li&gt;
&lt;li&gt;Monitorización completa con alertas y rollback automático.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="nivel-3-mlops-completo"&gt;Nivel 3: MLOps completo
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;Feature store para la gestión consistente de features.&lt;/li&gt;
&lt;li&gt;Model registry con gobernanza y flujos de aprobación.&lt;/li&gt;
&lt;li&gt;A/B testing integrado en el proceso de despliegue.&lt;/li&gt;
&lt;li&gt;Trazabilidad de datos y modelos de extremo a extremo.&lt;/li&gt;
&lt;li&gt;Pipelines auto-reparables que detectan y responden al drift automáticamente.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;La mayoría de los equipos se encuentran entre el Nivel 0 y el Nivel 1. El objetivo no es saltar al Nivel 3 de inmediato, sino progresar de forma incremental, abordando primero los cuellos de botella más dolorosos.&lt;/p&gt;
&lt;h2 id="conclusión"&gt;Conclusión
&lt;/h2&gt;&lt;p&gt;MLOps consiste en aplicar patrones de ingeniería a los desafíos únicos de ML. Empieza con tracking de experimentos y automatización básica, luego añade feature stores, registries y estrategias avanzadas conforme crezcas. La clave: trata modelos como artefactos de producción. Versiona, prueba, monitoriza, mejora.&lt;/p&gt;</description></item><item><title>Introducción a AIOps: operaciones de TI inteligentes</title><link>https://adurrr.github.io/p/introducci%C3%B3n-a-aiops-operaciones-de-ti-inteligentes/</link><pubDate>Mon, 05 Dec 2022 00:00:00 +0000</pubDate><guid>https://adurrr.github.io/p/introducci%C3%B3n-a-aiops-operaciones-de-ti-inteligentes/</guid><description>&lt;h2 id="qué-es-aiops"&gt;¿Qué es AIOps?
&lt;/h2&gt;&lt;p&gt;AIOps (Artificial Intelligence for IT Operations) aplica machine learning y analítica de datos a los datos operativos (logs, métricas, eventos, trazas) para automatizar y mejorar flujos de trabajo. Gartner acuñó el término en 2017, pero la idea es simple: usar algoritmos para gestionar el volumen y la complejidad que los humanos no pueden manejar manualmente.&lt;/p&gt;
&lt;p&gt;En términos prácticos, las plataformas AIOps ingieren datos de herramientas de monitorización, sistemas APM, agregadores de logs y fuentes de eventos. Aplican modelos de ML para detectar anomalías, correlacionar eventos, identificar causas raíz y, en algunos casos, desencadenar remediación automatizada. El objetivo es reducir el tiempo medio de detección (MTTD) y el tiempo medio de resolución (MTTR) mientras se libera a los equipos de operaciones de la fatiga por alertas.&lt;/p&gt;
&lt;h2 id="por-qué-la-monitorización-tradicional-se-queda-corta"&gt;Por qué la monitorización tradicional se queda corta
&lt;/h2&gt;&lt;p&gt;La monitorización funcionaba bien cuando los sistemas eran simples. Tenías pocos servidores, unas pocas apps y un número limitado de métricas. Un umbral estático de CPU o un regex en logs era suficiente.&lt;/p&gt;
&lt;p&gt;La infraestructura moderna rompió ese modelo:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Escala&lt;/strong&gt;: Un clúster de Kubernetes genera millones de métricas y logs por minuto. No puedes vigilar dashboards a esa escala.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Complejidad&lt;/strong&gt;: Los microservicios crean dependencias enredadas. Una petición puede atravesar docenas de servicios. Encontrar qué causó una latencia significa correlacionar datos entre todos.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Entornos dinámicos&lt;/strong&gt;: Auto-scaling, contenedores efímeros, serverless. Las baselines cambian constantemente y los umbrales estáticos explotan con falsos positivos.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Fatiga por alertas&lt;/strong&gt;: Los equipos se hunden en alertas. Cuando el 90% es ruido, ese crítico 10% desaparece. Los ingenieros empiezan a ignorar todo.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;AIOps no reemplaza la monitorización. Se sitúa encima de lo que ya tienes y lo hace más inteligente.&lt;/p&gt;
&lt;h2 id="capacidades-clave"&gt;Capacidades clave
&lt;/h2&gt;&lt;h3 id="1-detección-de-anomalías"&gt;1. Detección de anomalías
&lt;/h3&gt;&lt;p&gt;En lugar de umbrales estáticos, AIOps usa modelos de ML (frecuentemente análisis de series temporales, clustering o autoencoders) para aprender qué aspecto tiene lo &amp;ldquo;normal&amp;rdquo; para cada métrica y servicio. Cuando el comportamiento se desvía significativamente de la línea base aprendida, se marca una anomalía.&lt;/p&gt;
&lt;p&gt;Esto maneja el problema de la línea base dinámica. Si tu aplicación normalmente ve un pico de tráfico cada lunes a las 9 de la mañana, el modelo aprende ese patrón y no alerta por ello. Pero un pico inesperado a las 3 de la madrugada de un miércoles sí se marca.&lt;/p&gt;
&lt;h3 id="2-correlación-de-eventos"&gt;2. Correlación de eventos
&lt;/h3&gt;&lt;p&gt;Un único problema de infraestructura puede generar cientos o miles de alertas relacionadas en diferentes herramientas de monitorización. AIOps correlaciona estos eventos — agrupándolos por tiempo, topología y relaciones causales — para presentar un único incidente en lugar de un muro de alertas.&lt;/p&gt;
&lt;p&gt;Por ejemplo, un fallo en un switch de red podría disparar alertas en: el propio switch, todos los servidores conectados (conectividad perdida), todas las aplicaciones en esos servidores (fallos en health checks), y servicios downstream (errores de timeout). Una plataforma AIOps correlaciona todos estos en un incidente: &amp;ldquo;Switch de red X ha fallado.&amp;rdquo;&lt;/p&gt;
&lt;h3 id="3-análisis-de-causa-raíz"&gt;3. Análisis de causa raíz
&lt;/h3&gt;&lt;p&gt;Más allá de la correlación, AIOps intenta identificar la causa raíz de un incidente. Al comprender la topología de tu infraestructura y la cadena causal de eventos, puede sugerir que el fallo del switch de red es la causa raíz, en lugar de presentar el timeout de la aplicación como un problema independiente.&lt;/p&gt;
&lt;p&gt;Aquí es donde el valor se vuelve tangible. En lugar de que un ingeniero de guardia pase 30 minutos rastreando a través de dashboards y logs, la plataforma muestra la causa raíz probable inmediatamente.&lt;/p&gt;
&lt;h3 id="4-auto-remediación"&gt;4. Auto-remediación
&lt;/h3&gt;&lt;p&gt;Las implementaciones más maduras de AIOps cierran el ciclo disparando acciones de remediación automatizadas. Si se detecta un patrón conocido (disco llenándose, un pod en CrashLoopBackOff, un proceso descontrolado consumiendo memoria), la plataforma puede ejecutar runbooks predefinidos automáticamente.&lt;/p&gt;
&lt;p&gt;Ejemplos:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Reiniciar un pod o servicio caído.&lt;/li&gt;
&lt;li&gt;Escalar un deployment cuando se detecta carga anómala.&lt;/li&gt;
&lt;li&gt;Limpiar un directorio de logs cuando el uso de disco supera un umbral dinámico.&lt;/li&gt;
&lt;li&gt;Disparar un failover cuando una base de datos primaria deja de responder.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;La auto-remediación requiere un diseño cuidadoso. Empieza con acciones de bajo riesgo y amplía conforme crece la confianza.&lt;/p&gt;
&lt;h2 id="plataformas-y-herramientas-comunes"&gt;Plataformas y herramientas comunes
&lt;/h2&gt;&lt;p&gt;El panorama de AIOps incluye tanto plataformas comerciales como bloques de construcción open-source:&lt;/p&gt;
&lt;h3 id="plataformas-comerciales"&gt;Plataformas comerciales
&lt;/h3&gt;&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Plataforma&lt;/th&gt;
 &lt;th&gt;Fortalezas&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;Dynatrace&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;Auto-descubrimiento robusto, motor de IA (Davis), observabilidad full-stack&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;Datadog&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;Monitorización unificada + alertas con ML, detección de anomalías Watchdog&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;Splunk ITSI&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;Analítica de logs potente + toolkit de ML, bueno para correlación de eventos&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;Moogsoft&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;Pionero en el espacio AIOps, fuerte correlación de eventos y reducción de ruido&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;BigPanda&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;Enfocado en correlación de eventos y automatización, se integra con herramientas existentes&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;PagerDuty&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;Gestión de incidentes con reducción de ruido por ML y agrupación inteligente&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id="bloques-de-construcción-open-source"&gt;Bloques de construcción open-source
&lt;/h3&gt;&lt;p&gt;Puedes ensamblar un stack similar a AIOps con componentes open-source:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Recolección de datos&lt;/strong&gt;: Prometheus, Grafana Agent, OpenTelemetry Collector, Fluentd/Fluent Bit.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Almacenamiento de datos&lt;/strong&gt;: Prometheus (métricas), Elasticsearch/OpenSearch (logs), Jaeger/Tempo (trazas).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Detección de anomalías&lt;/strong&gt;: Facebook Prophet, Isolation Forest (scikit-learn), luminol, Grafana ML.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Correlación de eventos&lt;/strong&gt;: Lógica personalizada sobre streams de eventos, o StackStorm para automatización dirigida por eventos.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Alertas y automatización&lt;/strong&gt;: Alertmanager, Grafana OnCall, StackStorm, Rundeck.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Construir un stack AIOps personalizado es significativamente más trabajo que usar una plataforma comercial, pero te da control total y evita el vendor lock-in. Un punto medio razonable es usar una plataforma comercial para las capacidades core de AIOps mientras mantienes tu pipeline de datos en open-source.&lt;/p&gt;
&lt;h2 id="casos-de-uso-prácticos"&gt;Casos de uso prácticos
&lt;/h2&gt;&lt;h3 id="reducción-de-ruido-en-gestión-de-alertas"&gt;Reducción de ruido en gestión de alertas
&lt;/h3&gt;&lt;p&gt;Un equipo que recibe más de 500 alertas al día implementa correlación de eventos AIOps. Las alertas relacionadas se agrupan en incidentes, los duplicados se suprimen y las alertas fluctuantes se silencian. El volumen de alertas baja un 80%, y el ingeniero de guardia puede enfocarse en incidentes reales.&lt;/p&gt;
&lt;h3 id="planificación-proactiva-de-capacidad"&gt;Planificación proactiva de capacidad
&lt;/h3&gt;&lt;p&gt;Los modelos AIOps analizan tendencias históricas de uso de recursos y predicen cuándo se alcanzarán los límites de capacidad. En lugar de reaccionar a una alerta de disco lleno a las 2 de la madrugada, la plataforma predice el problema con dos semanas de antelación y crea un ticket para que el equipo lo aborde en horario laboral.&lt;/p&gt;
&lt;h3 id="respuesta-a-incidentes-más-rápida"&gt;Respuesta a incidentes más rápida
&lt;/h3&gt;&lt;p&gt;Durante una caída de producción, la plataforma AIOps correlaciona alertas de todo el stack de monitorización, identifica la causa raíz (un despliegue reciente que introdujo una fuga de memoria) y muestra el commit del despliegue relevante. El MTTR baja de 45 minutos a 10 minutos.&lt;/p&gt;
&lt;h3 id="escalado-automático"&gt;Escalado automático
&lt;/h3&gt;&lt;p&gt;La plataforma detecta patrones de tráfico anómalos que se desvían de la línea base aprendida. En lugar de esperar a que la CPU alcance el 80% (el umbral estático), dispara una acción de scale-up basada en la tasa de cambio, asegurando que la capacidad está lista antes de que los usuarios experimenten degradación.&lt;/p&gt;
&lt;h2 id="cómo-encaja-aiops-en-los-flujos-de-trabajo-devops"&gt;Cómo encaja AIOps en los flujos de trabajo DevOps
&lt;/h2&gt;&lt;p&gt;AIOps no es un reemplazo de las prácticas DevOps. Es una capa de mejora:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;Código&lt;/span&gt; &lt;span class="err"&gt;──&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;CI&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;CD&lt;/span&gt; &lt;span class="n"&gt;Pipeline&lt;/span&gt; &lt;span class="err"&gt;──&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Deploy&lt;/span&gt; &lt;span class="err"&gt;──&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Observar&lt;/span&gt; &lt;span class="err"&gt;──&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Capa&lt;/span&gt; &lt;span class="n"&gt;AIOps&lt;/span&gt; &lt;span class="err"&gt;──&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Actuar&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;│&lt;/span&gt; &lt;span class="err"&gt;│&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Monitoring&lt;/span&gt; &lt;span class="n"&gt;Stack&lt;/span&gt; &lt;span class="n"&gt;Modelos&lt;/span&gt; &lt;span class="n"&gt;ML&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;métricas&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;logs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;detección&lt;/span&gt; &lt;span class="n"&gt;de&lt;/span&gt; &lt;span class="n"&gt;anomalías&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;trazas&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;eventos&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;correlación&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;RCA&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;Los &lt;strong&gt;desarrolladores&lt;/strong&gt; se benefician de una identificación más rápida de la causa raíz cuando su código causa problemas en producción.&lt;/li&gt;
&lt;li&gt;Los equipos de &lt;strong&gt;operaciones&lt;/strong&gt; se benefician de la reducción de ruido, remediación automatizada y alertas proactivas.&lt;/li&gt;
&lt;li&gt;Los equipos &lt;strong&gt;SRE&lt;/strong&gt; se benefician del seguimiento de SLOs basado en datos y el análisis de tasa de consumo del error budget.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;AIOps funciona mejor cuando tu base de observabilidad es sólida. Si no estás recolectando buenos datos (logs estructurados, métricas significativas, trazas distribuidas), los modelos de ML no producirán insights significativos. Arregla primero tu observabilidad, luego añade AIOps encima.&lt;/p&gt;
&lt;h2 id="primeros-pasos-un-camino-pragmático"&gt;Primeros pasos: Un camino pragmático
&lt;/h2&gt;&lt;p&gt;Si estás considerando AIOps, aquí tienes un enfoque práctico:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Audita tu stack de observabilidad actual.&lt;/strong&gt; ¿Qué datos estás recolectando? ¿Los logs están estructurados? ¿Las métricas están etiquetadas de forma consistente? ¿Las trazas se propagan entre servicios? AIOps es tan bueno como los datos que ingiere.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Empieza con la reducción de ruido.&lt;/strong&gt; Esta es la fruta que cuelga más baja. Implementa agrupación y deduplicación de alertas. Incluso una correlación básica basada en reglas (antes de cualquier ML) reducirá la fatiga por alertas significativamente.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Añade detección de anomalías a métricas clave.&lt;/strong&gt; Elige 3-5 métricas críticas de negocio e infraestructura. Aplica un modelo de detección de anomalías de series temporales. Facebook Prophet o recording rules de Prometheus con ajustes estacionales son buenos puntos de partida.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Implementa remediación automatizada para problemas conocidos.&lt;/strong&gt; Identifica los 5 incidentes recurrentes principales. Escribe runbooks para ellos. Automatiza los runbooks usando StackStorm, Rundeck o el motor de automatización de tu plataforma.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Evalúa una plataforma comercial cuando la complejidad lo requiera.&lt;/strong&gt; Si tienes cientos de servicios, múltiples herramientas de monitorización y un equipo de operaciones creciente, la inversión en una plataforma AIOps comercial puede justificarse solo por la reducción en MTTR.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Mide el impacto.&lt;/strong&gt; Sigue MTTD, MTTR, ratio alerta-a-incidente y tasa de falsos positivos. Sin métricas, no puedes probar que AIOps vale la pena.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;AIOps no es magia. Es un conjunto de técnicas que, aplicadas a buenos datos operativos, pueden reducir la carga sobre los equipos y mejorar la fiabilidad. Empieza pequeño, mide todo, y escala lo que funcione.&lt;/p&gt;</description></item><item><title>Recursos de formación en MLOps y rutas de aprendizaje</title><link>https://adurrr.github.io/p/recursos-de-formaci%C3%B3n-en-mlops-y-rutas-de-aprendizaje/</link><pubDate>Mon, 15 Feb 2021 13:00:14 +0100</pubDate><guid>https://adurrr.github.io/p/recursos-de-formaci%C3%B3n-en-mlops-y-rutas-de-aprendizaje/</guid><description>&lt;h3 id="mlops-con-github"&gt;MLOps con GitHub
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://learning.oreilly.com/videos/mlops-workflow-with/50108VIDEOPAIML/50108VIDEOPAIML-c1_s0" target="_blank" rel="noopener"
 &gt;Vídeo de flujo de trabajo MLOps con Github Actions - O&amp;rsquo;Really&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item></channel></rss>