<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Pipeline on lo0 — Blog Técnico</title><link>https://blog.lo0.es/tags/pipeline/</link><description>Recent content in Pipeline on lo0 — Blog Técnico</description><generator>Hugo -- gohugo.io</generator><language>es</language><lastBuildDate>Thu, 21 May 2026 06:30:00 +0200</lastBuildDate><atom:link href="https://blog.lo0.es/tags/pipeline/index.xml" rel="self" type="application/rss+xml"/><item><title>El pipeline LLMOps de seis etapas: arquitectura global y deep dive en cada componente</title><link>https://blog.lo0.es/posts/pipeline-llmops-seis-etapas/</link><pubDate>Thu, 21 May 2026 06:30:00 +0200</pubDate><guid>https://blog.lo0.es/posts/pipeline-llmops-seis-etapas/</guid><description>&lt;h2 id="tldr">TL;DR&lt;/h2>
&lt;p>Los dos primeros posts de la serie establecieron el &lt;a href="https://blog.lo0.es/posts/mlops-llms-panorama-2026/">panorama LLMOps&lt;/a> y bajaron al detalle del &lt;a href="https://blog.lo0.es/posts/rag-kafka-datalake-arquitectura/">pipeline de datos con Kafka&lt;/a>. Este post hace el zoom intermedio: dibuja &lt;strong>el mapa completo del sistema&lt;/strong> —una arquitectura global de un LLMOps moderno con todas las piezas que el campo ha estabilizado en 2026— y entra en profundidad en cada una de las &lt;strong>seis etapas canónicas del pipeline&lt;/strong>: &lt;strong>Data&lt;/strong>, &lt;strong>Tune&lt;/strong>, &lt;strong>Eval&lt;/strong>, &lt;strong>Deploy&lt;/strong>, &lt;strong>Observe&lt;/strong>, &lt;strong>Retrain&lt;/strong>. Para cada etapa damos las &lt;strong>sub-tareas operativas&lt;/strong>, las &lt;strong>herramientas dominantes&lt;/strong>, las &lt;strong>decisiones de diseño&lt;/strong> que aparecen siempre, y las &lt;strong>trampas específicas&lt;/strong> que se ven repetidamente en producción. Y, lo más importante operativamente: cada etapa lleva un &lt;strong>mini-mapa &amp;ldquo;estás aquí&amp;rdquo;&lt;/strong> sobre el ciclo, que se reutilizará en cualquier post posterior de la serie para situar al lector. La idea: que cualquiera leyendo un post sobre fine-tuning, sobre prompt versioning, sobre eval gates o sobre drift detection, pueda mirar el mini-mapa y saber inmediatamente en qué pieza del sistema más grande está pensando ese día.&lt;/p>
&lt;blockquote>
&lt;p>Este es el &lt;strong>tercer post de la serie MLOps específico para LLMs&lt;/strong>. Anteriores: &lt;a href="https://blog.lo0.es/posts/mlops-llms-panorama-2026/">Panorama 2026&lt;/a> y &lt;a href="https://blog.lo0.es/posts/rag-kafka-datalake-arquitectura/">RAG sobre Kafka&lt;/a>. Aquí pasamos de &amp;ldquo;el qué&amp;rdquo; y &amp;ldquo;una pieza&amp;rdquo; a &lt;strong>el mapa entero&lt;/strong>, con detalle por etapa.&lt;/p>
&lt;/blockquote>
&lt;h2 id="la-arquitectura-global-el-mapa-maestro">La arquitectura global: el mapa maestro&lt;/h2>
&lt;p>Antes de bajar a cada etapa, fijemos el mapa entero. Lo que sigue es el dibujo de referencia de un sistema LLMOps de producción en 2026, con todos los componentes que el campo ha estabilizado en su lugar:&lt;/p>
&lt;div class="diagram" style="max-width:780px;margin:1.5rem auto;">
&lt;svg viewBox="0 0 780 580" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Arquitectura global LLMOps 2026">
&lt;style>.title{font:700 14px sans-serif;fill:#222}.stage-title{font:700 13px sans-serif;fill:#222}.lbl{font:11px sans-serif;fill:#333}.sm{font:10px sans-serif;fill:#555}.tiny{font:9px sans-serif;fill:#666}.stage{stroke:#444;stroke-width:1.5;rx:8}.data{fill:#ffe9d6}.tune{fill:#ffd6d6}.eval{fill:#d6eaff}.deploy{fill:#d9f5d6}.obs{fill:#e9d6f5}.retrain{fill:#fff5b0}.cross{fill:#f0f0f0;stroke:#888;stroke-dasharray:4 2;rx:6}.arr{stroke:#444;stroke-width:1.6;fill:none;marker-end:url(#ar)}.cycle{stroke:#888;stroke-width:1.4;fill:none;marker-end:url(#ar);stroke-dasharray:6 3}&lt;/style>
&lt;defs>&lt;marker id="ar" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6" markerHeight="6" orient="auto">&lt;path d="M0,0 L10,5 L0,10 z" fill="#444"/>&lt;/marker>&lt;/defs>
&lt;text x="390" y="22" text-anchor="middle" class="title">Arquitectura global LLMOps 2026 — las seis etapas y los componentes transversales&lt;/text>
&lt;rect x="20" y="50" width="240" height="170" class="stage data"/>
&lt;text x="140" y="72" text-anchor="middle" class="stage-title">1 · DATA&lt;/text>
&lt;text x="35" y="92" class="sm">• Origenes: OLTP, APIs, logs, scraping&lt;/text>
&lt;text x="35" y="108" class="sm">• CDC: Debezium, Flink CDC&lt;/text>
&lt;text x="35" y="124" class="sm">• Transport: Kafka + Schema Registry&lt;/text>
&lt;text x="35" y="140" class="sm">• Stream proc: Flink SQL, RisingWave&lt;/text>
&lt;text x="35" y="156" class="sm">• Versioning: DVC + lakeFS&lt;/text>
&lt;text x="35" y="172" class="sm">• Tableflow → Iceberg/Delta&lt;/text>
&lt;text x="35" y="188" class="sm">• Vector stores: Milvus, Qdrant,&lt;/text>
&lt;text x="35" y="202" class="sm"> Weaviate, pgvector, LanceDB&lt;/text>
&lt;rect x="280" y="50" width="240" height="170" class="stage tune"/>
&lt;text x="400" y="72" text-anchor="middle" class="stage-title">2 · TUNE&lt;/text>
&lt;text x="295" y="92" class="sm">• Modalidades: fine-tune / RAG /&lt;/text>
&lt;text x="295" y="106" class="sm"> agent training&lt;/text>
&lt;text x="295" y="124" class="sm">• Frameworks: PEFT, Axolotl, TRL,&lt;/text>
&lt;text x="295" y="138" class="sm"> Unsloth, llama-factory&lt;/text>
&lt;text x="295" y="156" class="sm">• Técnicas: LoRA, QLoRA, DPO, RLHF&lt;/text>
&lt;text x="295" y="172" class="sm">• Clusters: H100/B200 + NVLink&lt;/text>
&lt;text x="295" y="188" class="sm">• Experiment tracking: MLflow, W&amp;amp;B&lt;/text>
&lt;text x="295" y="202" class="sm">• Adapter registry: HF Hub privado&lt;/text>
&lt;rect x="540" y="50" width="220" height="170" class="stage eval"/>
&lt;text x="650" y="72" text-anchor="middle" class="stage-title">3 · EVAL&lt;/text>
&lt;text x="555" y="92" class="sm">• CI frameworks: DeepEval,&lt;/text>
&lt;text x="555" y="106" class="sm"> Promptfoo, Ragas, OpenAI Evals&lt;/text>
&lt;text x="555" y="124" class="sm">• Platforms: Langfuse, LangSmith,&lt;/text>
&lt;text x="555" y="138" class="sm"> Phoenix, Braintrust&lt;/text>
&lt;text x="555" y="156" class="sm">• Judge LLM (G-Eval, Prometheus)&lt;/text>
&lt;text x="555" y="172" class="sm">• Golden dataset versionado&lt;/text>
&lt;text x="555" y="188" class="sm">• Eval gates en CI/CD&lt;/text>
&lt;text x="555" y="202" class="sm">• Calibración 85-90% vs humano&lt;/text>
&lt;rect x="20" y="245" width="240" height="170" class="stage deploy"/>
&lt;text x="140" y="267" text-anchor="middle" class="stage-title">4 · DEPLOY&lt;/text>
&lt;text x="35" y="287" class="sm">• Model registry: MLflow, OME&lt;/text>
&lt;text x="35" y="303" class="sm">• Serving: vLLM, SGLang, TRT-LLM&lt;/text>
&lt;text x="35" y="319" class="sm">• Operators K8s: vLLM Prod Stack,&lt;/text>
&lt;text x="35" y="333" class="sm"> KServe, OME, NVIDIA Dynamo, llm-d&lt;/text>
&lt;text x="35" y="349" class="sm">• Gateway / router: LiteLLM&lt;/text>
&lt;text x="35" y="365" class="sm">• Estrategias: canary, blue-green,&lt;/text>
&lt;text x="35" y="379" class="sm"> shadow, A/B versioning&lt;/text>
&lt;text x="35" y="395" class="sm">• Autoscaling: KEDA + métricas LLM&lt;/text>
&lt;rect x="280" y="245" width="240" height="170" class="stage obs"/>
&lt;text x="400" y="267" text-anchor="middle" class="stage-title">5 · OBSERVE&lt;/text>
&lt;text x="295" y="287" class="sm">• Tracing: OpenLLMetry, Langfuse,&lt;/text>
&lt;text x="295" y="301" class="sm"> Phoenix, LangSmith&lt;/text>
&lt;text x="295" y="319" class="sm">• Métricas: Prometheus, Grafana&lt;/text>
&lt;text x="295" y="335" class="sm">• Guardrails: NeMo, Llama Guard 4,&lt;/text>
&lt;text x="295" y="349" class="sm"> LLM Guard, Lakera&lt;/text>
&lt;text x="295" y="367" class="sm">• eBPF: Hubble, Tetragon, AgentSight&lt;/text>
&lt;text x="295" y="383" class="sm">• MCP observability (OTel GenAI)&lt;/text>
&lt;text x="295" y="399" class="sm">• Drift: Evidently, NannyML, WhyLabs&lt;/text>
&lt;rect x="540" y="245" width="220" height="170" class="stage retrain"/>
&lt;text x="650" y="267" text-anchor="middle" class="stage-title">6 · RETRAIN&lt;/text>
&lt;text x="555" y="287" class="sm">• Feedback explícito (thumbs)&lt;/text>
&lt;text x="555" y="303" class="sm">• Feedback implícito (latencia,&lt;/text>
&lt;text x="555" y="317" class="sm"> abandonment, retries)&lt;/text>
&lt;text x="555" y="335" class="sm">• Triaging de incidentes&lt;/text>
&lt;text x="555" y="351" class="sm">• Dataset enrichment con casos&lt;/text>
&lt;text x="555" y="365" class="sm"> donde el modelo falló&lt;/text>
&lt;text x="555" y="383" class="sm">• Cadence: trimestral o&lt;/text>
&lt;text x="555" y="397" class="sm"> incident-driven&lt;/text>
&lt;rect x="100" y="440" width="580" height="120" class="cross"/>
&lt;text x="390" y="462" text-anchor="middle" class="stage-title">Componentes transversales (atraviesan todas las etapas)&lt;/text>
&lt;text x="115" y="482" class="sm">• OpenTelemetry Collector (gen_ai.* y mcp.* semantic conventions)&lt;/text>
&lt;text x="115" y="498" class="sm">• Prompt versioning: Langfuse / MLflow Prompts (versionado v1/v2/v3 + labels + cache)&lt;/text>
&lt;text x="115" y="514" class="sm">• MCP servers + MCP Gateway (Traefik Hub, MintMCP) — interfaz herramientas-modelo&lt;/text>
&lt;text x="115" y="530" class="sm">• Model gateway: LiteLLM (100+ providers unificados como una API OpenAI-compatible)&lt;/text>
&lt;text x="115" y="546" class="sm">• Schema Registry (Avro/Protobuf/JSON Schema) compartido entre data y serving&lt;/text>
&lt;path class="arr" d="M260,135 L280,135"/>
&lt;path class="arr" d="M520,135 L540,135"/>
&lt;path class="arr" d="M650,220 L650,245"/>
&lt;path class="arr" d="M540,330 L520,330"/>
&lt;path class="arr" d="M280,330 L260,330"/>
&lt;path class="arr" d="M140,415 L140,440"/>
&lt;path class="arr" d="M400,415 L400,440"/>
&lt;path class="arr" d="M650,415 L650,440"/>
&lt;path class="cycle" d="M650,330 C780,330 780,135 760,135 L760,135"/>
&lt;text x="745" y="245" class="sm" text-anchor="middle">ciclo&lt;/text>
&lt;/svg>
&lt;/div>
&lt;p>Lo que ves: las &lt;strong>seis cajas grandes&lt;/strong> son las etapas; las &lt;strong>flechas continuas&lt;/strong> son el flujo del pipeline; la &lt;strong>flecha discontinua&lt;/strong> que va de &lt;strong>Retrain&lt;/strong> a &lt;strong>Data&lt;/strong> es el ciclo de feedback que convierte LLMOps en un proceso vivo, no en un proyecto que termina. La banda gris al pie son &lt;strong>componentes transversales&lt;/strong> —observabilidad, prompt versioning, MCP, gateway, schema— que atraviesan todas las etapas y se conectan a cada una.&lt;/p>
&lt;p>Tres lecturas rápidas del mapa:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Horizontal arriba&lt;/strong>: el camino feliz, &lt;strong>data → tune → eval&lt;/strong>. Lo que pasa cuando preparas el modelo.&lt;/li>
&lt;li>&lt;strong>Horizontal abajo&lt;/strong>: el camino de servicio, &lt;strong>deploy → observe → retrain&lt;/strong>. Lo que pasa cuando el modelo está vivo.&lt;/li>
&lt;li>&lt;strong>Vertical&lt;/strong>: la conexión entre los dos pisos. Eval gateway alimenta Deploy; Observe alimenta Retrain; Retrain devuelve a Data.&lt;/li>
&lt;/ul>
&lt;p>Cada etapa de aquí en adelante incluirá un &lt;strong>mini-mapa de navegación&lt;/strong> (&amp;ldquo;estás aquí&amp;rdquo;) para situarte en el ciclo completo. Vamos a cada una.&lt;/p>
&lt;h2 id="etapa-1--data-ingestión-transporte-versionado-indexación">Etapa 1 — Data: ingestión, transporte, versionado, indexación&lt;/h2>
&lt;div class="diagram" style="max-width:780px;margin:1rem auto;">
&lt;svg viewBox="0 0 780 90" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="estás aquí: Data">
&lt;style>.box{stroke:#444;stroke-width:1.4;rx:6}.active{fill:#ff8a4c;stroke-width:3}.idle{fill:#f4f4f4}.lbl{font:600 12px sans-serif;fill:#222}.arr{stroke:#666;stroke-width:1.4;fill:none;marker-end:url(#mn)}.cyc{stroke:#888;stroke-width:1.2;fill:none;stroke-dasharray:4 2;marker-end:url(#mn)}&lt;/style>
&lt;defs>&lt;marker id="mn" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6" markerHeight="6" orient="auto">&lt;path d="M0,0 L10,5 L0,10 z" fill="#666"/>&lt;/marker>&lt;/defs>
&lt;text x="390" y="20" text-anchor="middle" class="lbl">Estás aquí: DATA · ingestión → transporte → versionado → indexación&lt;/text>
&lt;rect x="30" y="35" width="110" height="35" class="box active"/>&lt;text x="85" y="58" text-anchor="middle" class="lbl">1 · Data&lt;/text>
&lt;rect x="155" y="35" width="110" height="35" class="box idle"/>&lt;text x="210" y="58" text-anchor="middle" class="lbl">2 · Tune&lt;/text>
&lt;rect x="280" y="35" width="110" height="35" class="box idle"/>&lt;text x="335" y="58" text-anchor="middle" class="lbl">3 · Eval&lt;/text>
&lt;rect x="405" y="35" width="110" height="35" class="box idle"/>&lt;text x="460" y="58" text-anchor="middle" class="lbl">4 · Deploy&lt;/text>
&lt;rect x="530" y="35" width="110" height="35" class="box idle"/>&lt;text x="585" y="58" text-anchor="middle" class="lbl">5 · Observe&lt;/text>
&lt;rect x="655" y="35" width="110" height="35" class="box idle"/>&lt;text x="710" y="58" text-anchor="middle" class="lbl">6 · Retrain&lt;/text>
&lt;path class="arr" d="M140,52 L155,52"/>&lt;path class="arr" d="M265,52 L280,52"/>&lt;path class="arr" d="M390,52 L405,52"/>&lt;path class="arr" d="M515,52 L530,52"/>&lt;path class="arr" d="M640,52 L655,52"/>
&lt;path class="cyc" d="M710,72 L710,82 L85,82 L85,72"/>
&lt;/svg>
&lt;/div>
&lt;h3 id="sub-tareas-operativas">Sub-tareas operativas&lt;/h3>
&lt;p>La etapa Data es la más infravalorada y la que más bloquea proyectos. Sus sub-tareas:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Ingestión&lt;/strong> desde origenes heterogéneos: bases de datos OLTP (Postgres, MySQL), APIs externas, file shares, scraping, sistemas SaaS, logs de aplicaciones, mensajería interna.&lt;/li>
&lt;li>&lt;strong>Captura de cambios&lt;/strong> (CDC) en streaming si el dato es dinámico. Debezium sobre Kafka, Flink CDC, alternativas modernas como RisingWave que lee WAL directamente.&lt;/li>
&lt;li>&lt;strong>Transformación&lt;/strong> (cleansing, dedup, normalización, sanitization de PII).&lt;/li>
&lt;li>&lt;strong>Schema management&lt;/strong>: registro de esquemas, evolución compatible, compatibilidad backward/forward.&lt;/li>
&lt;li>&lt;strong>Versionado&lt;/strong> de datasets de training y golden datasets: DVC + lakeFS (unificadas en noviembre 2025).&lt;/li>
&lt;li>&lt;strong>Indexación&lt;/strong> para RAG: chunking, embeddings, escritura a vector stores. Cubierto en profundidad en el &lt;a href="https://blog.lo0.es/posts/rag-kafka-datalake-arquitectura/">post de Kafka&lt;/a>.&lt;/li>
&lt;li>&lt;strong>Materialización&lt;/strong> a tablas analíticas: Tableflow → Iceberg/Delta, para consumo de BI y queries de baja latencia.&lt;/li>
&lt;/ul>
&lt;h3 id="herramientas-dominantes">Herramientas dominantes&lt;/h3>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Sub-tarea&lt;/th>
&lt;th>Herramientas 2026&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>CDC&lt;/td>
&lt;td>Debezium, Flink CDC, RisingWave&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Transport&lt;/td>
&lt;td>Kafka (Confluent Cloud, Redpanda, Apache puro)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Schema Registry&lt;/td>
&lt;td>Confluent Schema Registry, Apicurio&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Stream processing&lt;/td>
&lt;td>Apache Flink, RisingWave, Kafka Streams&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Versionado de datos&lt;/td>
&lt;td>DVC + lakeFS&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Vector stores&lt;/td>
&lt;td>Milvus, Qdrant, Weaviate, pgvector, LanceDB&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Tablas materializadas&lt;/td>
&lt;td>Tableflow → Iceberg/Delta&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>ETL/ELT batch (cuando aplica)&lt;/td>
&lt;td>dbt + Snowflake/Databricks&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h3 id="decisiones-de-diseño">Decisiones de diseño&lt;/h3>
&lt;p>Las tres decisiones que aparecen siempre:&lt;/p>
&lt;p>&lt;strong>Batch vs streaming&lt;/strong>: cuanto más dinámico sea el dato, más streaming. Para corpus estáticos (manuales que nunca cambian) batch nocturno basta; para datos transaccionales que el agente necesita ver minuto a minuto, streaming desde el día 1.&lt;/p>
&lt;p>&lt;strong>Embedding model&lt;/strong>: cambiar el modelo de embeddings invalida todos los vectores indexados. Decisión arquitectónica: pinning del modelo + plan explícito de migración (dual-index pattern visto en el post de Kafka).&lt;/p>
&lt;p>&lt;strong>Vector store&lt;/strong>: pgvector si ya tienes Postgres operado y eres &amp;lt;10M vectores; Qdrant si quieres simplicidad mid-scale; Milvus si necesitas billones; Weaviate si valoras hybrid search nativo.&lt;/p>
&lt;h3 id="trampas">Trampas&lt;/h3>
&lt;ul>
&lt;li>&lt;strong>Hardcodear conexiones a la fuente&lt;/strong> (sin abstracción): cuando la base de datos cambia (versión, host, esquema), rompes todo el pipeline. &lt;strong>Adapter layer&lt;/strong> desde el día 1.&lt;/li>
&lt;li>&lt;strong>Sin schema registry&lt;/strong>: los topics empiezan a romperse silenciosamente.&lt;/li>
&lt;li>&lt;strong>Reindexación full cuando algo cambia&lt;/strong>: cuesta horas o días. Diseñar &lt;strong>dual-index pattern&lt;/strong> desde el principio.&lt;/li>
&lt;li>&lt;strong>PII no sanitizada&lt;/strong>: el RAG está sirviendo datos sensibles sin querer. Anonymización en el pipeline, no en el consumo.&lt;/li>
&lt;/ul>
&lt;h2 id="etapa-2--tune-preparar-el-modelo-para-tu-caso">Etapa 2 — Tune: preparar el modelo para tu caso&lt;/h2>
&lt;div class="diagram" style="max-width:780px;margin:1rem auto;">
&lt;svg viewBox="0 0 780 90" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="estás aquí: Tune">
&lt;style>.box{stroke:#444;stroke-width:1.4;rx:6}.active{fill:#ff7777;stroke-width:3}.idle{fill:#f4f4f4}.lbl{font:600 12px sans-serif;fill:#222}.arr{stroke:#666;stroke-width:1.4;fill:none;marker-end:url(#mn2)}.cyc{stroke:#888;stroke-width:1.2;fill:none;stroke-dasharray:4 2;marker-end:url(#mn2)}&lt;/style>
&lt;defs>&lt;marker id="mn2" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6" markerHeight="6" orient="auto">&lt;path d="M0,0 L10,5 L0,10 z" fill="#666"/>&lt;/marker>&lt;/defs>
&lt;text x="390" y="20" text-anchor="middle" class="lbl">Estás aquí: TUNE · fine-tuning / RAG-as-tuning / agent training&lt;/text>
&lt;rect x="30" y="35" width="110" height="35" class="box idle"/>&lt;text x="85" y="58" text-anchor="middle" class="lbl">1 · Data&lt;/text>
&lt;rect x="155" y="35" width="110" height="35" class="box active"/>&lt;text x="210" y="58" text-anchor="middle" class="lbl">2 · Tune&lt;/text>
&lt;rect x="280" y="35" width="110" height="35" class="box idle"/>&lt;text x="335" y="58" text-anchor="middle" class="lbl">3 · Eval&lt;/text>
&lt;rect x="405" y="35" width="110" height="35" class="box idle"/>&lt;text x="460" y="58" text-anchor="middle" class="lbl">4 · Deploy&lt;/text>
&lt;rect x="530" y="35" width="110" height="35" class="box idle"/>&lt;text x="585" y="58" text-anchor="middle" class="lbl">5 · Observe&lt;/text>
&lt;rect x="655" y="35" width="110" height="35" class="box idle"/>&lt;text x="710" y="58" text-anchor="middle" class="lbl">6 · Retrain&lt;/text>
&lt;path class="arr" d="M140,52 L155,52"/>&lt;path class="arr" d="M265,52 L280,52"/>&lt;path class="arr" d="M390,52 L405,52"/>&lt;path class="arr" d="M515,52 L530,52"/>&lt;path class="arr" d="M640,52 L655,52"/>
&lt;path class="cyc" d="M710,72 L710,82 L85,82 L85,72"/>
&lt;/svg>
&lt;/div>
&lt;h3 id="sub-tareas-operativas-1">Sub-tareas operativas&lt;/h3>
&lt;ul>
&lt;li>&lt;strong>Selección de modelo base&lt;/strong>: Llama, Qwen, Mistral, Gemma, DeepSeek según licencia, tamaño, calidad en tu dominio.&lt;/li>
&lt;li>&lt;strong>Preparación del dataset&lt;/strong>: split train/val/test, formato (chat templates, JSONL), augmentación si aplica.&lt;/li>
&lt;li>&lt;strong>Configuración del adapter&lt;/strong>: LoRA rank, target modules, alpha; QLoRA si quieres entrenar en una GPU consumer; full fine-tune solo si tienes presupuesto.&lt;/li>
&lt;li>&lt;strong>Training loop&lt;/strong>: HuggingFace Transformers + PEFT + TRL como stack canónico; Axolotl o llama-factory como wrappers convenience; Unsloth si quieres 2-4× más velocidad en GPUs consumer.&lt;/li>
&lt;li>&lt;strong>Hyperparameter sweep&lt;/strong>: W&amp;amp;B Sweeps, Optuna, Ray Tune.&lt;/li>
&lt;li>&lt;strong>Checkpointing y resumability&lt;/strong>: save cada N pasos, resume desde fallo.&lt;/li>
&lt;li>&lt;strong>Promotion&lt;/strong>: el adapter promueve al registry tras pasar la siguiente etapa (Eval).&lt;/li>
&lt;/ul>
&lt;h3 id="las-tres-modalidades-de-tune">Las tres modalidades de Tune&lt;/h3>
&lt;p>Detalle del cuadro que vimos en el &lt;a href="https://blog.lo0.es/posts/mlops-llms-panorama-2026/">panorama&lt;/a>:&lt;/p>
&lt;p>&lt;strong>Fine-tuning supervisado (SFT)&lt;/strong> con LoRA/QLoRA. Recoges pares (prompt, ideal-response), aplicas SFT con cross-entropy loss. Lo más simple. La regla del pulgar: &lt;strong>300-3 000 ejemplos&lt;/strong> bien curados suelen ser más útiles que 50 000 ruidosos.&lt;/p>
&lt;p>&lt;strong>DPO (Direct Preference Optimization)&lt;/strong> y &lt;strong>RLAIF&lt;/strong>. En vez de &amp;ldquo;ideal-response&amp;rdquo;, recoges pares &lt;strong>(prompt, respuesta_buena, respuesta_mala)&lt;/strong> y entrenas al modelo a preferir la buena. Más estable que RLHF clásico, mismo objetivo. Es lo que la mayoría de equipos usa cuando van más allá de SFT.&lt;/p>
&lt;p>&lt;strong>Agent training&lt;/strong> (RFT / Reinforcement Fine-Tuning, RLHF puro). Para casos donde el modelo necesita aprender &lt;strong>trayectorias multistep&lt;/strong>: cuándo elegir tool A vs B, cuándo pedir confirmación, cómo descomponer una tarea grande. Mucho más caro y complejo. Lo de OpenAI con RFT marcó el patrón en 2024-2025; en 2026 está saliendo del experimental.&lt;/p>
&lt;p>&lt;strong>RAG como alternativa a Tune&lt;/strong>: aunque conceptualmente es otra etapa (vive en Data + Deploy), funcionalmente compite con fine-tuning para muchos casos. El veredicto 2026: &lt;strong>hybrid es default&lt;/strong> (60% de despliegues), fine-tune para behavior + RAG para conocimiento volátil.&lt;/p>
&lt;h3 id="herramientas">Herramientas&lt;/h3>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Aspecto&lt;/th>
&lt;th>Herramientas 2026&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Framework base&lt;/td>
&lt;td>HuggingFace Transformers, PEFT, TRL&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Wrappers convenience&lt;/td>
&lt;td>Axolotl, llama-factory&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Velocidad consumer&lt;/td>
&lt;td>Unsloth (2-4× speedup en GPUs RTX)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Distributed training&lt;/td>
&lt;td>DeepSpeed, FSDP, NeMo Framework&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Experiment tracking&lt;/td>
&lt;td>MLflow, W&amp;amp;B, ClearML&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Adapter registry&lt;/td>
&lt;td>HuggingFace Hub privado, MLflow registry&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Hyperparameter&lt;/td>
&lt;td>W&amp;amp;B Sweeps, Optuna, Ray Tune&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h3 id="trampas-1">Trampas&lt;/h3>
&lt;ul>
&lt;li>&lt;strong>Catastrophic forgetting&lt;/strong>: SFT muy agresivo destruye capacidades generales del modelo. Conservar small % del dataset original o usar regularización.&lt;/li>
&lt;li>&lt;strong>Overfitting al golden dataset&lt;/strong>: el modelo aprende a memorizar el set de eval. Mantener un &lt;strong>test set holdout&lt;/strong> que nadie del equipo mira hasta el release final.&lt;/li>
&lt;li>&lt;strong>Train/serve skew&lt;/strong>: prompts en training con formato distinto al de producción. &lt;strong>Mismo chat template&lt;/strong> en ambos.&lt;/li>
&lt;li>&lt;strong>Lora rank demasiado alto&lt;/strong>: parece mejorar metricas pero infla el adapter sin beneficio real. Empezar con &lt;code>r=8&lt;/code> o &lt;code>r=16&lt;/code>; subir solo si hay evidencia.&lt;/li>
&lt;/ul>
&lt;h2 id="etapa-3--eval-validar-antes-de-promover">Etapa 3 — Eval: validar antes de promover&lt;/h2>
&lt;div class="diagram" style="max-width:780px;margin:1rem auto;">
&lt;svg viewBox="0 0 780 90" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="estás aquí: Eval">
&lt;style>.box{stroke:#444;stroke-width:1.4;rx:6}.active{fill:#7aafff;stroke-width:3}.idle{fill:#f4f4f4}.lbl{font:600 12px sans-serif;fill:#222}.arr{stroke:#666;stroke-width:1.4;fill:none;marker-end:url(#mn3)}.cyc{stroke:#888;stroke-width:1.2;fill:none;stroke-dasharray:4 2;marker-end:url(#mn3)}&lt;/style>
&lt;defs>&lt;marker id="mn3" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6" markerHeight="6" orient="auto">&lt;path d="M0,0 L10,5 L0,10 z" fill="#666"/>&lt;/marker>&lt;/defs>
&lt;text x="390" y="20" text-anchor="middle" class="lbl">Estás aquí: EVAL · CI gates + platform regression + human review&lt;/text>
&lt;rect x="30" y="35" width="110" height="35" class="box idle"/>&lt;text x="85" y="58" text-anchor="middle" class="lbl">1 · Data&lt;/text>
&lt;rect x="155" y="35" width="110" height="35" class="box idle"/>&lt;text x="210" y="58" text-anchor="middle" class="lbl">2 · Tune&lt;/text>
&lt;rect x="280" y="35" width="110" height="35" class="box active"/>&lt;text x="335" y="58" text-anchor="middle" class="lbl">3 · Eval&lt;/text>
&lt;rect x="405" y="35" width="110" height="35" class="box idle"/>&lt;text x="460" y="58" text-anchor="middle" class="lbl">4 · Deploy&lt;/text>
&lt;rect x="530" y="35" width="110" height="35" class="box idle"/>&lt;text x="585" y="58" text-anchor="middle" class="lbl">5 · Observe&lt;/text>
&lt;rect x="655" y="35" width="110" height="35" class="box idle"/>&lt;text x="710" y="58" text-anchor="middle" class="lbl">6 · Retrain&lt;/text>
&lt;path class="arr" d="M140,52 L155,52"/>&lt;path class="arr" d="M265,52 L280,52"/>&lt;path class="arr" d="M390,52 L405,52"/>&lt;path class="arr" d="M515,52 L530,52"/>&lt;path class="arr" d="M640,52 L655,52"/>
&lt;path class="cyc" d="M710,72 L710,82 L85,82 L85,72"/>
&lt;/svg>
&lt;/div>
&lt;h3 id="sub-tareas-operativas-2">Sub-tareas operativas&lt;/h3>
&lt;p>Cubierto en profundidad en &lt;a href="https://blog.lo0.es/posts/evals-llm-la-capa-despues-de-tracing/">Evals: la capa después del tracing&lt;/a>. Resumen estructurado para el pipeline:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Curación del golden dataset&lt;/strong>: 100-500 ejemplos como mínimo, mantenidos activamente con casos de incidentes.&lt;/li>
&lt;li>&lt;strong>Evaluators&lt;/strong>: heurísticos (regex, length), semánticos (embeddings), LLM-as-judge (G-Eval), humanos (golden labels).&lt;/li>
&lt;li>&lt;strong>Ejecución en CI&lt;/strong>: bloquear el merge si métricas críticas caen &amp;gt;X%.&lt;/li>
&lt;li>&lt;strong>Ejecución en platform&lt;/strong>: sobre tráfico de producción muestreado, persistir resultados, detectar regresión a largo plazo.&lt;/li>
&lt;li>&lt;strong>Calibración del judge&lt;/strong>: 85-90% agreement con humanos antes de aceptar el judge como productivo.&lt;/li>
&lt;li>&lt;strong>Eval gates&lt;/strong>: thresholds explícitos por métrica (faithfulness &amp;gt; 0.85, relevancy &amp;gt; 0.80, etc.).&lt;/li>
&lt;/ul>
&lt;h3 id="herramientas-1">Herramientas&lt;/h3>
&lt;ul>
&lt;li>&lt;strong>CI gates&lt;/strong>: DeepEval (Apache 2.0, pytest-style), Promptfoo (MIT, CLI), Ragas (RAG-specific), Inspect AI (safety/capability).&lt;/li>
&lt;li>&lt;strong>Platform&lt;/strong>: Langfuse (MIT, suite completa), LangSmith (LangChain), Phoenix (ELv2, OTel), Braintrust.&lt;/li>
&lt;li>&lt;strong>Judges&lt;/strong>: GPT-4 (caro pero referencia), Claude 3.5 Sonnet, Prometheus (OSS 0.897 correlación), JudgeLM.&lt;/li>
&lt;/ul>
&lt;h3 id="trampas-2">Trampas&lt;/h3>
&lt;ul>
&lt;li>&lt;strong>Golden dataset envejecido&lt;/strong>: si no se actualiza, deja de reflejar producción.&lt;/li>
&lt;li>&lt;strong>Judge contaminado&lt;/strong>: el judge sabe del dataset (apareció en su training).&lt;/li>
&lt;li>&lt;strong>Sample size insuficiente&lt;/strong>: &amp;lt;50 ejemplos hace que diferencias parezcan ruido.&lt;/li>
&lt;li>&lt;strong>Costes runaway&lt;/strong>: G-Eval con GPT-4 sobre muchos casos cuesta miles USD/mes.&lt;/li>
&lt;li>&lt;strong>Olvidar el segmento&lt;/strong>: media 0.85 puede esconder 0.55 en alemán.&lt;/li>
&lt;/ul>
&lt;h2 id="etapa-4--deploy-poner-el-modelo-en-producción">Etapa 4 — Deploy: poner el modelo en producción&lt;/h2>
&lt;div class="diagram" style="max-width:780px;margin:1rem auto;">
&lt;svg viewBox="0 0 780 90" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="estás aquí: Deploy">
&lt;style>.box{stroke:#444;stroke-width:1.4;rx:6}.active{fill:#7adb7a;stroke-width:3}.idle{fill:#f4f4f4}.lbl{font:600 12px sans-serif;fill:#222}.arr{stroke:#666;stroke-width:1.4;fill:none;marker-end:url(#mn4)}.cyc{stroke:#888;stroke-width:1.2;fill:none;stroke-dasharray:4 2;marker-end:url(#mn4)}&lt;/style>
&lt;defs>&lt;marker id="mn4" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6" markerHeight="6" orient="auto">&lt;path d="M0,0 L10,5 L0,10 z" fill="#666"/>&lt;/marker>&lt;/defs>
&lt;text x="390" y="20" text-anchor="middle" class="lbl">Estás aquí: DEPLOY · operators + serving + canary + autoscaling&lt;/text>
&lt;rect x="30" y="35" width="110" height="35" class="box idle"/>&lt;text x="85" y="58" text-anchor="middle" class="lbl">1 · Data&lt;/text>
&lt;rect x="155" y="35" width="110" height="35" class="box idle"/>&lt;text x="210" y="58" text-anchor="middle" class="lbl">2 · Tune&lt;/text>
&lt;rect x="280" y="35" width="110" height="35" class="box idle"/>&lt;text x="335" y="58" text-anchor="middle" class="lbl">3 · Eval&lt;/text>
&lt;rect x="405" y="35" width="110" height="35" class="box active"/>&lt;text x="460" y="58" text-anchor="middle" class="lbl">4 · Deploy&lt;/text>
&lt;rect x="530" y="35" width="110" height="35" class="box idle"/>&lt;text x="585" y="58" text-anchor="middle" class="lbl">5 · Observe&lt;/text>
&lt;rect x="655" y="35" width="110" height="35" class="box idle"/>&lt;text x="710" y="58" text-anchor="middle" class="lbl">6 · Retrain&lt;/text>
&lt;path class="arr" d="M140,52 L155,52"/>&lt;path class="arr" d="M265,52 L280,52"/>&lt;path class="arr" d="M390,52 L405,52"/>&lt;path class="arr" d="M515,52 L530,52"/>&lt;path class="arr" d="M640,52 L655,52"/>
&lt;path class="cyc" d="M710,72 L710,82 L85,82 L85,72"/>
&lt;/svg>
&lt;/div>
&lt;h3 id="sub-tareas-operativas-3">Sub-tareas operativas&lt;/h3>
&lt;p>Cubierto en profundidad en &lt;a href="https://blog.lo0.es/posts/vllm-kubernetes/">vLLM en Kubernetes&lt;/a> y &lt;a href="https://blog.lo0.es/posts/operators-llm-kubernetes/">Operators LLM K8s&lt;/a>. Resumen para el pipeline:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Selección del runtime&lt;/strong>: vLLM (default), SGLang (agentes con prefix caching alto), TensorRT-LLM (latencia pura), llama.cpp (edge).&lt;/li>
&lt;li>&lt;strong>Selección del operator&lt;/strong>: vLLM Production Stack, KServe, OME (LMSYS), NVIDIA Dynamo, llm-d (CNCF).&lt;/li>
&lt;li>&lt;strong>Configuración del serving&lt;/strong>: &lt;code>--tensor-parallel-size&lt;/code>, &lt;code>--kv-cache-dtype=fp8&lt;/code>, &lt;code>--enable-prefix-caching&lt;/code>, &lt;code>--enable-chunked-prefill&lt;/code>, &lt;code>--gpu-memory-utilization=0.92&lt;/code>.&lt;/li>
&lt;li>&lt;strong>Routing entre modelos&lt;/strong>: LiteLLM como abstracción para multi-provider.&lt;/li>
&lt;li>&lt;strong>Estrategia de release&lt;/strong>: canary (1% → 10% → 100%), blue-green (todo o nada con rollback rápido), shadow (eval en paralelo sin afectar usuarios).&lt;/li>
&lt;li>&lt;strong>Autoscaling con métricas LLM&lt;/strong>: KEDA + Prometheus sobre &lt;code>vllm:num_requests_waiting&lt;/code> o equivalente.&lt;/li>
&lt;li>&lt;strong>Gateway / Inference Extension&lt;/strong>: Gateway API Inference Extension cuando esté GA.&lt;/li>
&lt;/ul>
&lt;h3 id="herramientas-dominantes-1">Herramientas dominantes&lt;/h3>
&lt;ul>
&lt;li>&lt;strong>Serving engines&lt;/strong>: vLLM, SGLang, TensorRT-LLM, llama.cpp, MLX.&lt;/li>
&lt;li>&lt;strong>Operators&lt;/strong>: OME, vLLM Production Stack, NVIDIA Dynamo, llm-d, KServe.&lt;/li>
&lt;li>&lt;strong>Routing&lt;/strong>: LiteLLM (100+ providers), OpenRouter (managed), LangChain Router.&lt;/li>
&lt;li>&lt;strong>GPU primitivas&lt;/strong>: NVIDIA GPU Operator, LeaderWorkerSet (LWS) para tensor parallel multi-pod, KEDA para autoscaling.&lt;/li>
&lt;/ul>
&lt;h3 id="trampas-3">Trampas&lt;/h3>
&lt;ul>
&lt;li>&lt;strong>Rolling update naïve&lt;/strong> que corta sesiones: &lt;code>maxUnavailable: 0, maxSurge: 1&lt;/code> y &lt;code>terminationGracePeriodSeconds: 120+&lt;/code>.&lt;/li>
&lt;li>&lt;strong>readiness probe corta&lt;/strong> que mata pods cargando: &lt;code>startupProbe&lt;/code> con &lt;code>failureThreshold: 60&lt;/code>.&lt;/li>
&lt;li>&lt;strong>HPA por CPU%&lt;/strong> sin métricas LLM: vLLM bachea internamente, una réplica atiende decenas. KEDA por queue depth.&lt;/li>
&lt;li>&lt;strong>KV cache sin cuantizar&lt;/strong>: &lt;code>--kv-cache-dtype=fp8&lt;/code> casi siempre rentable.&lt;/li>
&lt;li>&lt;strong>Tensor parallel en GPUs sin NVLink&lt;/strong>: all-reduce satura PCIe, throughput se hunde.&lt;/li>
&lt;/ul>
&lt;h2 id="etapa-5--observe-ver-lo-que-pasa-en-producción">Etapa 5 — Observe: ver lo que pasa en producción&lt;/h2>
&lt;div class="diagram" style="max-width:780px;margin:1rem auto;">
&lt;svg viewBox="0 0 780 90" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="estás aquí: Observe">
&lt;style>.box{stroke:#444;stroke-width:1.4;rx:6}.active{fill:#c47aff;stroke-width:3}.idle{fill:#f4f4f4}.lbl{font:600 12px sans-serif;fill:#222}.arr{stroke:#666;stroke-width:1.4;fill:none;marker-end:url(#mn5)}.cyc{stroke:#888;stroke-width:1.2;fill:none;stroke-dasharray:4 2;marker-end:url(#mn5)}&lt;/style>
&lt;defs>&lt;marker id="mn5" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6" markerHeight="6" orient="auto">&lt;path d="M0,0 L10,5 L0,10 z" fill="#666"/>&lt;/marker>&lt;/defs>
&lt;text x="390" y="20" text-anchor="middle" class="lbl">Estás aquí: OBSERVE · tracing + métricas + guardrails + drift + eBPF&lt;/text>
&lt;rect x="30" y="35" width="110" height="35" class="box idle"/>&lt;text x="85" y="58" text-anchor="middle" class="lbl">1 · Data&lt;/text>
&lt;rect x="155" y="35" width="110" height="35" class="box idle"/>&lt;text x="210" y="58" text-anchor="middle" class="lbl">2 · Tune&lt;/text>
&lt;rect x="280" y="35" width="110" height="35" class="box idle"/>&lt;text x="335" y="58" text-anchor="middle" class="lbl">3 · Eval&lt;/text>
&lt;rect x="405" y="35" width="110" height="35" class="box idle"/>&lt;text x="460" y="58" text-anchor="middle" class="lbl">4 · Deploy&lt;/text>
&lt;rect x="530" y="35" width="110" height="35" class="box active"/>&lt;text x="585" y="58" text-anchor="middle" class="lbl">5 · Observe&lt;/text>
&lt;rect x="655" y="35" width="110" height="35" class="box idle"/>&lt;text x="710" y="58" text-anchor="middle" class="lbl">6 · Retrain&lt;/text>
&lt;path class="arr" d="M140,52 L155,52"/>&lt;path class="arr" d="M265,52 L280,52"/>&lt;path class="arr" d="M390,52 L405,52"/>&lt;path class="arr" d="M515,52 L530,52"/>&lt;path class="arr" d="M640,52 L655,52"/>
&lt;path class="cyc" d="M710,72 L710,82 L85,82 L85,72"/>
&lt;/svg>
&lt;/div>
&lt;h3 id="sub-tareas-operativas-4">Sub-tareas operativas&lt;/h3>
&lt;p>Esta es la etapa que más profundamente hemos cubierto en series previas: toda la serie eBPF (4 posts) y la serie post-tracing (4 posts) tratan sub-tareas de Observe. Resumen estructurado:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Tracing&lt;/strong>: OpenLLMetry/Traceloop, Langfuse, Phoenix, LangSmith. Spans con OTel GenAI semantic conventions (&lt;code>gen_ai.*&lt;/code>, &lt;code>mcp.*&lt;/code>).&lt;/li>
&lt;li>&lt;strong>Métricas&lt;/strong>: Prometheus + Grafana. TTFT, TPOT, throughput, queue depth, KV cache usage, cost por tool.&lt;/li>
&lt;li>&lt;strong>Guardrails activos&lt;/strong> (no solo eval): NeMo Guardrails con rails de 5 tipos, Llama Guard 4 multimodal, Llama Prompt Guard 2 (86M/22M), LLM Guard.&lt;/li>
&lt;li>&lt;strong>eBPF observability&lt;/strong> (zero-instrumentation): Hubble (red), Tetragon (proceso/syscall), AgentSight (agente LLM con SSL uprobes + stdiocap MCP).&lt;/li>
&lt;li>&lt;strong>eBPF en motor local&lt;/strong> (inferencia): ProfInfer-style con uprobes en llama.cpp / vLLM / libcudart.&lt;/li>
&lt;li>&lt;strong>Drift detection&lt;/strong>: Evidently AI, NannyML, WhyLabs. KS, PSI, MMD sobre embeddings.&lt;/li>
&lt;li>&lt;strong>MCP observability&lt;/strong>: OpenTelemetry GenAI MCP semantic conventions, trace propagation via &lt;code>params._meta&lt;/code>, MCP Gateway centralizado.&lt;/li>
&lt;/ul>
&lt;h3 id="las-cuatro-métricas-obligatorias">Las cuatro métricas obligatorias&lt;/h3>
&lt;p>De todo lo cubierto, las cuatro que cualquier dashboard mínimo debe tener:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>TTFT p50/p95&lt;/strong> (time to first token) — lo que el usuario percibe.&lt;/li>
&lt;li>&lt;strong>TPOT p50/p95&lt;/strong> (time per output token) — velocidad de streaming.&lt;/li>
&lt;li>&lt;strong>Throughput&lt;/strong> (tokens/segundo agregados) — capacity planning.&lt;/li>
&lt;li>&lt;strong>Queue depth&lt;/strong> (&lt;code>vllm:num_requests_waiting&lt;/code>) — indicador adelantado.&lt;/li>
&lt;/ol>
&lt;p>A esto se suman, por dominio:&lt;/p>
&lt;ul>
&lt;li>Para RAG: faithfulness rolling mean, retrieval hit rate.&lt;/li>
&lt;li>Para agentes: tool call accuracy, multi-step task completion.&lt;/li>
&lt;li>Para multi-tenant: cost per tenant, p95 latency per tenant.&lt;/li>
&lt;/ul>
&lt;h3 id="trampas-4">Trampas&lt;/h3>
&lt;ul>
&lt;li>&lt;strong>Cardinalidad en Prometheus&lt;/strong>: las métricas con todos los labels K8s explotan.&lt;/li>
&lt;li>&lt;strong>Tracing sin sampling&lt;/strong>: el storage crece sin control.&lt;/li>
&lt;li>&lt;strong>Guardrails permanentemente en monitoring mode&lt;/strong>: nunca llegan a enforce.&lt;/li>
&lt;li>&lt;strong>Drift sin alertas&lt;/strong>: detectas drift en el dashboard una vez al mes; mientras tanto el problema lleva semanas.&lt;/li>
&lt;li>&lt;strong>OTel sin propagación&lt;/strong>: spans MCP, Tetragon, AgentSight desconectados.&lt;/li>
&lt;/ul>
&lt;h2 id="etapa-6--retrain-cerrar-el-bucle">Etapa 6 — Retrain: cerrar el bucle&lt;/h2>
&lt;div class="diagram" style="max-width:780px;margin:1rem auto;">
&lt;svg viewBox="0 0 780 90" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="estás aquí: Retrain">
&lt;style>.box{stroke:#444;stroke-width:1.4;rx:6}.active{fill:#ffd24a;stroke-width:3}.idle{fill:#f4f4f4}.lbl{font:600 12px sans-serif;fill:#222}.arr{stroke:#666;stroke-width:1.4;fill:none;marker-end:url(#mn6)}.cyc{stroke:#c66;stroke-width:2;fill:none;stroke-dasharray:4 2;marker-end:url(#mn6)}&lt;/style>
&lt;defs>&lt;marker id="mn6" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6" markerHeight="6" orient="auto">&lt;path d="M0,0 L10,5 L0,10 z" fill="#c66"/>&lt;/marker>&lt;/defs>
&lt;text x="390" y="20" text-anchor="middle" class="lbl">Estás aquí: RETRAIN · cerrar el bucle hacia DATA&lt;/text>
&lt;rect x="30" y="35" width="110" height="35" class="box idle"/>&lt;text x="85" y="58" text-anchor="middle" class="lbl">1 · Data&lt;/text>
&lt;rect x="155" y="35" width="110" height="35" class="box idle"/>&lt;text x="210" y="58" text-anchor="middle" class="lbl">2 · Tune&lt;/text>
&lt;rect x="280" y="35" width="110" height="35" class="box idle"/>&lt;text x="335" y="58" text-anchor="middle" class="lbl">3 · Eval&lt;/text>
&lt;rect x="405" y="35" width="110" height="35" class="box idle"/>&lt;text x="460" y="58" text-anchor="middle" class="lbl">4 · Deploy&lt;/text>
&lt;rect x="530" y="35" width="110" height="35" class="box idle"/>&lt;text x="585" y="58" text-anchor="middle" class="lbl">5 · Observe&lt;/text>
&lt;rect x="655" y="35" width="110" height="35" class="box active"/>&lt;text x="710" y="58" text-anchor="middle" class="lbl">6 · Retrain&lt;/text>
&lt;path class="arr" d="M140,52 L155,52"/>&lt;path class="arr" d="M265,52 L280,52"/>&lt;path class="arr" d="M390,52 L405,52"/>&lt;path class="arr" d="M515,52 L530,52"/>&lt;path class="arr" d="M640,52 L655,52"/>
&lt;path class="cyc" d="M710,72 L710,82 L85,82 L85,72"/>
&lt;/svg>
&lt;/div>
&lt;h3 id="sub-tareas-operativas-5">Sub-tareas operativas&lt;/h3>
&lt;p>Esta es la etapa que más se descuida en proyectos GenAI. Cerrar el bucle convierte LLMOps en una práctica viva; no cerrarlo lo deja como un proyecto que envejece.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Feedback explícito&lt;/strong>: thumbs up/down en la UI, anotaciones por usuarios power, formularios para &amp;ldquo;qué falló&amp;rdquo;.&lt;/li>
&lt;li>&lt;strong>Feedback implícito&lt;/strong>: latencia anómala, abandonment rate, retries del usuario, sesiones abortadas.&lt;/li>
&lt;li>&lt;strong>Triaging de incidentes&lt;/strong>: clasificar incidentes por causa raíz (model issue, retrieval issue, prompt issue, infra issue).&lt;/li>
&lt;li>&lt;strong>Dataset enrichment&lt;/strong>: incorporar al golden dataset los casos donde el sistema falló, con la respuesta correcta etiquetada por humano.&lt;/li>
&lt;li>&lt;strong>Cadence de retrain&lt;/strong>: trimestral por defecto, &lt;strong>incident-driven&lt;/strong> cuando un patrón problemático supera threshold.&lt;/li>
&lt;li>&lt;strong>Promotion&lt;/strong>: el nuevo modelo/adapter pasa por las etapas Tune → Eval → Deploy, con eval gates que comparan contra el modelo en producción.&lt;/li>
&lt;/ul>
&lt;h3 id="las-dos-cadencias">Las dos cadencias&lt;/h3>
&lt;p>&lt;strong>Scheduled retrain&lt;/strong> (trimestral o semestral): un proceso establecido. Permite planificar capacity, presupuesto, riesgo. El default.&lt;/p>
&lt;p>&lt;strong>Incident-driven retrain&lt;/strong>: cuando un incidente serio (drift detectado, segmento que falla, ataque de prompt injection) supera threshold, se dispara un mini-ciclo. Más caro pero necesario para casos críticos.&lt;/p>
&lt;h3 id="herramientas-dominantes-2">Herramientas dominantes&lt;/h3>
&lt;ul>
&lt;li>&lt;strong>Annotation y feedback collection&lt;/strong>: Langfuse (UI built-in), Argilla (OSS), Label Studio.&lt;/li>
&lt;li>&lt;strong>Dataset enrichment&lt;/strong>: pipelines en Airflow o Argo Workflows.&lt;/li>
&lt;li>&lt;strong>Triaging&lt;/strong>: dashboards Langfuse + filtros por traces con eval bajo.&lt;/li>
&lt;li>&lt;strong>Promoting candidate&lt;/strong>: MLflow model registry stages.&lt;/li>
&lt;/ul>
&lt;h3 id="trampas-5">Trampas&lt;/h3>
&lt;ul>
&lt;li>&lt;strong>Bucle abierto&lt;/strong>: producción no informa al dataset; el modelo nunca mejora.&lt;/li>
&lt;li>&lt;strong>Feedback humano se pierde&lt;/strong>: thumbs down sin canal de captura estructurado.&lt;/li>
&lt;li>&lt;strong>Cadence sin definir&lt;/strong>: &amp;ldquo;ya retrenamos cuando haga falta&amp;rdquo; → nunca se retrena.&lt;/li>
&lt;li>&lt;strong>Sin holdout test set&lt;/strong>: el golden dataset se enriquece con los mismos casos que se usan para evaluar; eval mide memorización.&lt;/li>
&lt;li>&lt;strong>Promotion sin gates&lt;/strong>: el nuevo modelo entra a producción sin pasar las verificaciones de los modelos anteriores.&lt;/li>
&lt;/ul>
&lt;h2 id="el-ciclo-completo-cómo-encajan-las-etapas">El ciclo completo: cómo encajan las etapas&lt;/h2>
&lt;p>Ahora que vimos cada etapa por separado, el insight clave es &lt;strong>cómo se enganchan&lt;/strong>. Cinco propiedades emergentes del ciclo:&lt;/p>
&lt;p>&lt;strong>1. Data es la materia prima de todas las etapas&lt;/strong>. Tune lee del golden dataset. Eval lee del eval dataset. Deploy lee del RAG (vector store). Observe produce nuevos datos. Retrain crea datasets nuevos. &lt;strong>El log Kafka es el evangelio del sistema entero&lt;/strong> (post 2 de la serie).&lt;/p>
&lt;p>&lt;strong>2. Eval es el gatekeeper bidireccional&lt;/strong>. Antes de Deploy: bloquea release si el modelo regresa. Después de Observe: alimenta Retrain identificando casos peor evaluados. La calidad del eval determina la calidad del ciclo entero.&lt;/p>
&lt;p>&lt;strong>3. Observe alimenta a Retrain y a Eval simultáneamente&lt;/strong>. Las traces producen métricas para Observe; las traces problemáticas se anotan y van al dataset; los nuevos casos enriquecen el eval golden. &lt;strong>Observe es la fuente de verdad operativa&lt;/strong>.&lt;/p>
&lt;p>&lt;strong>4. Los componentes transversales (banda gris del mapa) no son una etapa, son una infraestructura&lt;/strong>. OpenTelemetry, prompt versioning, MCP gateway, model gateway, schema registry. Mal configurados, cada etapa sufre por separado. Bien configurados, las etapas se integran sin fricción.&lt;/p>
&lt;p>&lt;strong>5. El ciclo no es secuencial estricto, es concurrente&lt;/strong>. En cualquier momento dado, el sistema tiene: requests siendo servidas (Deploy + Observe), una versión nueva en training (Tune), eval continuo en CI (Eval), datos llegando del CDC (Data), análisis de incidentes (Retrain). &lt;strong>Todas las etapas están vivas a la vez&lt;/strong>.&lt;/p>
&lt;h2 id="trampas-cross-etapa-cosas-que-rompen-el-sistema-entero">Trampas cross-etapa: cosas que rompen el sistema entero&lt;/h2>
&lt;p>Hay errores que no son de una etapa, sino de las interfaces entre etapas. Los más comunes:&lt;/p>
&lt;h3 id="trainserve-skew">Train/serve skew&lt;/h3>
&lt;p>El formato exacto del prompt en training es distinto al de producción. Resultado: el modelo entrenado para responder a &lt;code>&amp;lt;|im_start|&amp;gt;user\n...\n&amp;lt;|im_end|&amp;gt;&lt;/code> recibe en producción &lt;code>User: ...\nAssistant:&lt;/code> y rinde peor. &lt;strong>Solución&lt;/strong>: extraer el chat template en una librería compartida que use el pipeline de Tune &lt;strong>y&lt;/strong> el de Deploy.&lt;/p>
&lt;h3 id="eval-que-no-refleja-producción">Eval que no refleja producción&lt;/h3>
&lt;p>Tu golden dataset son preguntas cuidadas; producción es preguntas reales con errores tipográficos, idiomas mezclados, etc. Eval pasa al 95%, producción rinde al 70%. &lt;strong>Solución&lt;/strong>: enriquecer continuamente el golden con muestras reales.&lt;/p>
&lt;h3 id="drift-sin-pipeline-de-respuesta">Drift sin pipeline de respuesta&lt;/h3>
&lt;p>Detectas drift en el dashboard de Observe; nadie tiene un workflow definido sobre qué hacer. &lt;strong>Solución&lt;/strong>: cada alerta de drift debe tener un runbook claro: investiga, clasifica, actúa (retrain, ajustar prompt, ampliar retrieval).&lt;/p>
&lt;h3 id="schema-break-cascada">Schema break cascada&lt;/h3>
&lt;p>Cambias el schema en la fuente OLTP; Debezium lo refleja; Flink job se rompe; topic embedded deja de actualizarse; vector store envejece; RAG responde sobre datos viejos. Tres etapas afectadas por un cambio en Data. &lt;strong>Solución&lt;/strong>: schema evolution &lt;strong>backward-compatible&lt;/strong> obligatoria, contracts entre productores y consumidores.&lt;/p>
&lt;h3 id="sin-observabilidad-del-propio-pipeline">Sin observabilidad del propio pipeline&lt;/h3>
&lt;p>El pipeline LLMOps es un sistema complejo. Si no tiene observabilidad propia (cuánto tarda el entrenamiento, cuántos jobs fallan, cuántas re-embedding pasan), debugar fallos es un proceso de spelunking. &lt;strong>Solución&lt;/strong>: OTel sobre el pipeline mismo, no solo sobre las llamadas LLM.&lt;/p>
&lt;h3 id="vendor-lock-in-invisible">Vendor lock-in invisible&lt;/h3>
&lt;p>Pipelines escritos contra LangChain, prompts pegados en LangSmith, embeddings en Pinecone, modelo en OpenAI. Migrar es un proyecto de meses. &lt;strong>Solución&lt;/strong>: abstracciones LiteLLM, OpenLLMetry, vendor-neutral desde el principio.&lt;/p>
&lt;h2 id="lo-que-viene-en-los-siguientes-posts">Lo que viene en los siguientes posts&lt;/h2>
&lt;ul>
&lt;li>&lt;strong>Post 4 — &lt;a href="https://blog.lo0.es/posts/postgresql-qdrant-ingestion-microservicios/">PostgreSQL + Qdrant en la etapa de ingestión&lt;/a>&lt;/strong> — primer post que aplica el patrón &amp;ldquo;estás aquí&amp;rdquo; sobre la etapa Data. Patrones de sincronización (outbox + CDC), arquitectura de microservicios, manifests de despliegue.&lt;/li>
&lt;li>&lt;strong>Próximos posts&lt;/strong> — pendientes de decidir: el cluster como plataforma multi-tenant, Constitutional AI / alignment runtime, fine-tuning continuo en profundidad, edge LLMs.&lt;/li>
&lt;li>En cualquier post posterior de esta o futuras series, el &lt;strong>mini-mapa &amp;ldquo;estás aquí&amp;rdquo;&lt;/strong> te dirá en qué etapa del ciclo encaja el tema. Si lees un post sobre quantization, sabrás que estás en Deploy. Si lees uno sobre evaluator ensembles, sabrás que estás en Eval. Si lees uno sobre RAG sobre Iceberg, sabrás que estás en Data.&lt;/li>
&lt;/ul>
&lt;h2 id="referencias">Referencias&lt;/h2>
&lt;p>Foundations:&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://medium.com/@sanjeebmeister/the-complete-mlops-llmops-roadmap-for-2026-building-production-grade-ai-systems-bdcca5ed2771">The Complete MLOps/LLMOps Roadmap for 2026 (Sanjeeb Panda)&lt;/a>.&lt;/li>
&lt;li>&lt;a href="https://hyscaler.com/insights/mlops-in-2026-guide/">MLOps in 2026: Architecture, Trends &amp;amp; Strategy (Hyscaler)&lt;/a>.&lt;/li>
&lt;/ul>
&lt;p>Por etapa (entradas de la serie del blog):&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Data&lt;/strong>: &lt;a href="https://blog.lo0.es/posts/rag-kafka-datalake-arquitectura/">RAG sobre Kafka — arquitectura técnica&lt;/a>.&lt;/li>
&lt;li>&lt;strong>Tune&lt;/strong>: cubierto parcialmente en &lt;a href="https://blog.lo0.es/posts/mlops-llms-panorama-2026/">Panorama 2026&lt;/a>; profundización en post 4 si se elige fine-tuning continuo.&lt;/li>
&lt;li>&lt;strong>Eval&lt;/strong>: &lt;a href="https://blog.lo0.es/posts/evals-llm-la-capa-despues-de-tracing/">Evals: la capa después del tracing&lt;/a>.&lt;/li>
&lt;li>&lt;strong>Deploy&lt;/strong>: &lt;a href="https://blog.lo0.es/posts/vllm-kubernetes/">vLLM en Kubernetes&lt;/a> y &lt;a href="https://blog.lo0.es/posts/operators-llm-kubernetes/">Operators LLM K8s&lt;/a>.&lt;/li>
&lt;li>&lt;strong>Observe&lt;/strong>: serie eBPF entera y serie post-tracing entera.&lt;/li>
&lt;li>&lt;strong>Retrain&lt;/strong>: cubierto en este post; profundización pendiente.&lt;/li>
&lt;/ul>
&lt;p>Componentes transversales:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Prompt versioning&lt;/strong>: en deep dive de Langfuse dentro del &lt;a href="https://blog.lo0.es/posts/agentsight-tracing-llm/">post de AgentSight&lt;/a>.&lt;/li>
&lt;li>&lt;strong>MCP&lt;/strong>: &lt;a href="https://blog.lo0.es/posts/mcp-observability-otel/">MCP observability profunda&lt;/a>.&lt;/li>
&lt;li>&lt;strong>Drift detection&lt;/strong>: &lt;a href="https://blog.lo0.es/posts/ebpf-on-device-inference-drift/">eBPF + drift detection&lt;/a>.&lt;/li>
&lt;li>&lt;strong>Inferencia local&lt;/strong>: &lt;a href="https://blog.lo0.es/posts/pagedattention-deep-dive/">PagedAttention deep dive&lt;/a>, &lt;a href="https://blog.lo0.es/posts/kv-cache-fundamentos/">KV cache&lt;/a>.&lt;/li>
&lt;/ul>
&lt;p>Frameworks y herramientas referenciadas:&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://mlflow.org/">MLflow&lt;/a>, &lt;a href="https://wandb.ai/">W&amp;amp;B&lt;/a>, &lt;a href="https://www.kubeflow.org/">Kubeflow&lt;/a>, &lt;a href="https://www.zenml.io/">ZenML&lt;/a>, &lt;a href="https://www.bentoml.com/">BentoML&lt;/a>, &lt;a href="https://metaflow.org/">Metaflow&lt;/a>.&lt;/li>
&lt;li>&lt;a href="https://github.com/huggingface/peft">HuggingFace PEFT&lt;/a>, &lt;a href="https://github.com/huggingface/trl">TRL&lt;/a>, &lt;a href="https://github.com/axolotl-ai-cloud/axolotl">Axolotl&lt;/a>, &lt;a href="https://github.com/unslothai/unsloth">Unsloth&lt;/a>.&lt;/li>
&lt;li>&lt;a href="https://dvc.org/">DVC&lt;/a> + &lt;a href="https://lakefs.io/">lakeFS&lt;/a>.&lt;/li>
&lt;li>&lt;a href="https://langfuse.com/">Langfuse&lt;/a>, &lt;a href="https://www.evidentlyai.com/">Evidently AI&lt;/a>, &lt;a href="https://phoenix.arize.com/">Phoenix&lt;/a>.&lt;/li>
&lt;li>&lt;a href="https://github.com/vllm-project/production-stack">vLLM Production Stack&lt;/a>, &lt;a href="https://kserve.github.io/website/">KServe&lt;/a>, &lt;a href="https://github.com/ome-projects/ome">OME&lt;/a>, &lt;a href="https://developer.nvidia.com/dynamo">NVIDIA Dynamo&lt;/a>, &lt;a href="https://github.com/llm-d/llm-d">llm-d&lt;/a>.&lt;/li>
&lt;/ul></description></item></channel></rss>