<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Prompt-Versioning on lo0 — Blog Técnico</title><link>https://blog.lo0.es/tags/prompt-versioning/</link><description>Recent content in Prompt-Versioning on lo0 — Blog Técnico</description><generator>Hugo -- gohugo.io</generator><language>es</language><lastBuildDate>Fri, 22 May 2026 07:30:00 +0200</lastBuildDate><atom:link href="https://blog.lo0.es/tags/prompt-versioning/index.xml" rel="self" type="application/rss+xml"/><item><title>Prompt versioning: el contrato que evita que un cambio de cinco palabras hunda tu sistema</title><link>https://blog.lo0.es/posts/prompt-versioning-langfuse-mlflow/</link><pubDate>Fri, 22 May 2026 07:30:00 +0200</pubDate><guid>https://blog.lo0.es/posts/prompt-versioning-langfuse-mlflow/</guid><description>&lt;h2 id="tldr">TL;DR&lt;/h2>
&lt;p>En un sistema de software clásico, la línea más peligrosa que un equipo puede cambiar es una migración SQL. En un sistema LLM, es una línea de prompt. El prompt determina la salida tanto o más que el modelo, no se ve en los tests unitarios, no aparece en los logs por defecto, y si se cambia sin dejar rastro no hay forma de saber qué versión generó qué respuesta. &lt;strong>Prompt versioning es la disciplina que convierte el prompt en un artefacto de primera clase&lt;/strong>: con identificador único, historial, labels de despliegue, suite de evals asociada, y trazabilidad por petición. El campo ha consolidado tres primitivas (versión inmutable, label mutable, cache de lectura) y dos herramientas dominantes (Langfuse OSS con UI built-in, MLflow Prompts integrado en el registry desde MLflow 3.10). Este artículo cubre el patrón a primer nivel: por qué importa, cómo se materializa, qué herramienta elegir, y cómo encaja con Eval, Deploy y Observe.&lt;/p>
&lt;h2 id="estás-aquí-transversal-toca-data-tune-eval-deploy-y-observe">Estás aquí: transversal (toca Data, Tune, Eval, Deploy y Observe)&lt;/h2>
&lt;p>Prompt versioning no vive en una etapa sino que &lt;strong>atraviesa cinco&lt;/strong>. Aparece como componente transversal en el mapa maestro del &lt;a href="https://blog.lo0.es/posts/pipeline-llmops-seis-etapas/">pipeline LLMOps de seis etapas&lt;/a> precisamente por eso: la versión del prompt es metadato necesario en cada etapa, no responsabilidad de una sola.&lt;/p>
&lt;div class="diagram" style="max-width:780px;margin:1rem auto;">
&lt;svg viewBox="0 0 780 130" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="estás aquí: transversal prompt versioning">
&lt;style>.box{stroke:#444;stroke-width:1.4;rx:6}.cross{fill:#ffe9d6;stroke-width:3;stroke:#c66;rx:6}.idle{fill:#f4f4f4}.lbl{font:600 12px sans-serif;fill:#222}.sm{font:11px sans-serif;fill:#444}.arr{stroke:#666;stroke-width:1.4;fill:none;marker-end:url(#pvm)}.cyc{stroke:#888;stroke-width:1.2;fill:none;stroke-dasharray:4 2;marker-end:url(#pvm)}&lt;/style>
&lt;defs>&lt;marker id="pvm" 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í: TRANSVERSAL · prompt versioning atraviesa todas las etapas activas&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 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;rect x="30" y="95" width="735" height="25" class="cross"/>
&lt;text x="397" y="112" text-anchor="middle" class="sm">Prompt registry (Langfuse / MLflow Prompts) · versioning · labels · cache · trace por request&lt;/text>
&lt;/svg>
&lt;/div>
&lt;h2 id="la-analogía-maestra-el-prompt-es-una-migración-sql-invisible">La analogía maestra: el prompt es una migración SQL invisible&lt;/h2>
&lt;p>Un equipo de backend serio nunca aceptaría que alguien modificara directamente una columna en producción sin pasar por una migración versionada. Aunque el cambio &amp;ldquo;funcione&amp;rdquo; en el momento, sin migración no hay forma de:&lt;/p>
&lt;ul>
&lt;li>Reproducir el estado anterior si algo falla.&lt;/li>
&lt;li>Saber quién y cuándo aplicó el cambio.&lt;/li>
&lt;li>Aplicar el mismo cambio en staging antes de prod.&lt;/li>
&lt;li>Probar la nueva versión contra una suite automatizada antes de promocionar.&lt;/li>
&lt;li>Saber, dos meses más tarde, por qué la tabla tiene el shape que tiene.&lt;/li>
&lt;/ul>
&lt;p>El prompt LLM ocupa exactamente esa posición en un sistema de inferencia. Cambiar &lt;code>&amp;quot;Eres un asistente útil.&amp;quot;&lt;/code> por &lt;code>&amp;quot;Eres un asistente útil y conciso. Responde en menos de 3 frases.&amp;quot;&lt;/code> puede:&lt;/p>
&lt;ul>
&lt;li>Reducir el coste medio por respuesta un 30 % (las respuestas son más cortas).&lt;/li>
&lt;li>O degradar la calidad en un segmento donde la concisión rompe matices necesarios.&lt;/li>
&lt;li>O cambiar la distribución de tools que el agente decide invocar.&lt;/li>
&lt;li>O alterar el comportamiento del judge LLM downstream que asume cierta longitud.&lt;/li>
&lt;/ul>
&lt;p>Y lo más importante: si el cambio se hace &lt;strong>editando una constante en el código de la app y desplegando&lt;/strong>, cuando dos semanas después alguien pregunta &lt;em>&amp;quot;¿por qué subió la tasa de queja en el segmento financiero?&amp;quot;&lt;/em>, &lt;strong>no hay forma de saber qué prompt servía en cada momento&lt;/strong>. Los logs guardan la respuesta y, con suerte, el modelo invocado; el prompt rara vez se guarda explícitamente.&lt;/p>
&lt;p>Prompt versioning resuelve el mismo problema que resolvió Flyway/Liquibase/Alembic para SQL: convertir un cambio invisible en un artefacto auditable.&lt;/p>
&lt;h2 id="las-tres-primitivas-del-patrón">Las tres primitivas del patrón&lt;/h2>
&lt;p>Sin importar la herramienta, los sistemas que funcionan en 2026 comparten &lt;strong>tres primitivas operativas&lt;/strong> que conviene fijar antes de mirar productos.&lt;/p>
&lt;h3 id="1-versión-inmutable">1. Versión inmutable&lt;/h3>
&lt;p>Cada vez que el contenido del prompt cambia (template, system message, variables disponibles, parámetros recomendados de model como temperature), se genera &lt;strong>una versión nueva&lt;/strong> con identificador único. La versión es &lt;strong>inmutable&lt;/strong>: una vez creada, no se sobrescribe; si se quiere cambiar algo, se crea v+1.&lt;/p>
&lt;pre tabindex="0">&lt;code>prompt_id: customer_support_v3
versions:
v1 (2026-03-12): &amp;#34;Eres un asistente de soporte...&amp;#34;
v2 (2026-04-08): &amp;#34;Eres un asistente de soporte... formato JSON...&amp;#34;
v3 (2026-05-21): &amp;#34;Eres un asistente de soporte... formato JSON... 3 frases máx...&amp;#34;
&lt;/code>&lt;/pre>&lt;p>La inmutabilidad es lo que permite que un trace de hace dos meses se pueda reproducir: si el trace dice &amp;ldquo;se sirvió &lt;code>customer_support_v3@v2&lt;/code>&amp;rdquo;, la versión v2 existe &lt;strong>literalmente&lt;/strong> y se puede recargar.&lt;/p>
&lt;h3 id="2-label-mutable-alias-de-despliegue">2. Label mutable (alias de despliegue)&lt;/h3>
&lt;p>Las versiones son inmutables, pero &lt;strong>qué versión está en producción cambia&lt;/strong>. Esa decisión se materializa en &lt;strong>labels&lt;/strong>: punteros con nombre semántico (&lt;code>production&lt;/code>, &lt;code>staging&lt;/code>, &lt;code>canary&lt;/code>) que apuntan a una versión concreta y pueden re-apuntarse.&lt;/p>
&lt;pre tabindex="0">&lt;code>prompt_id: customer_support_v3
labels:
production → v2 (servida al 100% del tráfico)
canary → v3 (servida al 5% del tráfico via gateway)
staging → v3
&lt;/code>&lt;/pre>&lt;p>Promocionar una versión es &lt;strong>mover un label&lt;/strong>, no editar el prompt. Rollback es &lt;strong>mover el label hacia atrás&lt;/strong>, no copiar texto. La operación se reduce a una mutación atómica de una tupla &lt;code>(label, version)&lt;/code>.&lt;/p>
&lt;h3 id="3-cache-de-lectura">3. Cache de lectura&lt;/h3>
&lt;p>El prompt se lee en &lt;strong>cada request al modelo&lt;/strong>. Si cada lectura llama al servicio de prompt registry, añades latencia y dependencia. La solución estándar es un &lt;strong>cache local&lt;/strong> en el cliente (TTL del orden de minutos) que invalida cuando el label cambia o cuando expira el TTL.&lt;/p>
&lt;p>Langfuse implementa cache de cliente nativo con TTL configurable y invalidación lazy; MLflow Prompts deja la responsabilidad al cliente o a una capa de gateway. En ambos casos, en producción el cliente sirve el prompt desde memoria con un overhead despreciable (&amp;lt;1 ms), y sólo va al registry cuando refresca.&lt;/p>
&lt;pre tabindex="0">&lt;code>┌──────────────────┐
│ Cliente (app) │
│ - cache local TTL=60s
│ - lookup label &amp;#34;production&amp;#34;
│ - obtiene template
│ - renderiza variables
│ - envía a LLM
└─────────┬────────┘
│ (cuando TTL expira o evento de cambio)
▼
┌──────────────────┐
│ Prompt registry │
│ - Langfuse / MLflow
│ - GET label=&amp;#34;production&amp;#34;
│ - response: version_id + template
└──────────────────┘
&lt;/code>&lt;/pre>&lt;p>Con estas tres primitivas, &lt;strong>cualquier herramienta razonable es equivalente&lt;/strong> en lo esencial. Lo que distingue una de otra son UI, integraciones, RBAC, integración con eval, etc.&lt;/p>
&lt;h2 id="las-dos-herramientas-dominantes-en-2026">Las dos herramientas dominantes en 2026&lt;/h2>
&lt;p>El campo ha convergido en dos opciones principales. Cualquier despliegue serio en producción usa una de las dos (a veces ambas, para distintos equipos).&lt;/p>
&lt;h3 id="langfuse-oss-prompt-management-ui-built-in">Langfuse (OSS, prompt-management UI built-in)&lt;/h3>
&lt;p>Langfuse es el sistema &lt;strong>prompt-first&lt;/strong>: nació para tracing y observabilidad, y el prompt management es una de sus capas centrales. Características clave para versionado:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>UI built-in&lt;/strong> para crear, editar, versionar prompts. Las versiones se generan automáticamente al guardar; el historial es visible y diffable.&lt;/li>
&lt;li>&lt;strong>Labels arbitrarios&lt;/strong> además de los típicos (&lt;code>production&lt;/code>, &lt;code>latest&lt;/code>). Puedes definir &lt;code>eu-prod&lt;/code>, &lt;code>internal-only&lt;/code>, &lt;code>customer-a&lt;/code> para enrutado fino.&lt;/li>
&lt;li>&lt;strong>Cache de cliente nativo&lt;/strong> en los SDKs oficiales (Python, JS), con TTL configurable, invalidación por evento y fallback al last-known-good si el registry está caído.&lt;/li>
&lt;li>&lt;strong>Integración nativa con tracing&lt;/strong>: cuando registras una llamada al LLM, Langfuse asocia automáticamente la &lt;code>prompt_id@version&lt;/code> que sirvió. En la UI ves: este trace, este span, este prompt versión X.&lt;/li>
&lt;li>&lt;strong>Integración con evals&lt;/strong>: Langfuse permite registrar suites de eval que se disparan al crear una versión nueva del prompt. Los resultados quedan vinculados al &lt;code>prompt_id@version&lt;/code> y son el gating natural para promocionar &lt;code>staging → production&lt;/code>.&lt;/li>
&lt;li>&lt;strong>Self-hosted o cloud&lt;/strong>: el core es OSS (MIT), corre en Docker compose o Helm; la versión cloud añade SLA, SSO y soporte.&lt;/li>
&lt;/ul>
&lt;p>Cuándo conviene Langfuse:&lt;/p>
&lt;ul>
&lt;li>Equipos que quieren UI rica para que product/PM/analyst gestionen prompts sin tocar código.&lt;/li>
&lt;li>Despliegues OSS-first donde el control del runtime y de la persistencia es requisito (on-premise, ENS).&lt;/li>
&lt;li>Cuando la observabilidad de LLM ya está en Langfuse: el prompt management es marginal en setup.&lt;/li>
&lt;/ul>
&lt;h3 id="mlflow-prompts-incluido-en-mlflow-310-marzo-2026">MLflow Prompts (incluido en MLflow 3.10, marzo 2026)&lt;/h3>
&lt;p>MLflow Prompts es la respuesta del ecosistema MLOps clásico para LLMs. Características:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Integrado en el Model Registry de MLflow&lt;/strong>: los prompts son artefactos primera clase del registry, con la misma semántica de stages (&lt;code>Staging&lt;/code>, &lt;code>Production&lt;/code>, &lt;code>Archived&lt;/code>) que ya conocen los equipos MLOps.&lt;/li>
&lt;li>&lt;strong>API consistente con el resto de MLflow&lt;/strong>: &lt;code>mlflow.register_prompt()&lt;/code>, &lt;code>mlflow.load_prompt(name, stage=&amp;quot;Production&amp;quot;)&lt;/code>. La curva de aprendizaje para equipos que ya usan MLflow para modelos es nula.&lt;/li>
&lt;li>&lt;strong>Versionado automático&lt;/strong> con &lt;code>version_id&lt;/code> numérico (1, 2, 3, &amp;hellip;) y comentarios opcionales al promocionar.&lt;/li>
&lt;li>&lt;strong>Sin UI built-in dedicada a prompts&lt;/strong> (la UI de MLflow sirve, pero está pensada para modelos; el flujo es menos pulido que en Langfuse).&lt;/li>
&lt;li>&lt;strong>Sin tracing GenAI-aware nativo&lt;/strong> (lo aporta MLflow Tracing en GenAI dashboard de la 3.10, pero la integración trace↔prompt es más manual que en Langfuse).&lt;/li>
&lt;li>&lt;strong>Compatible con cualquier model registry backend&lt;/strong> que MLflow soporta (filesystem, Postgres, MySQL, S3, GCS, Azure Blob).&lt;/li>
&lt;/ul>
&lt;p>Cuándo conviene MLflow Prompts:&lt;/p>
&lt;ul>
&lt;li>Equipos que ya operan MLflow para ML clásico y quieren extender la misma disciplina a LLMs sin añadir vendors.&lt;/li>
&lt;li>Despliegues donde el centro de gravedad es el model registry y el prompt es un artefacto más.&lt;/li>
&lt;li>Pipelines de CI/CD que ya hablan MLflow (CLI, REST API).&lt;/li>
&lt;/ul>
&lt;h3 id="comparativa">Comparativa&lt;/h3>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Característica&lt;/th>
&lt;th>Langfuse&lt;/th>
&lt;th>MLflow Prompts&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Licencia core&lt;/td>
&lt;td>MIT (OSS)&lt;/td>
&lt;td>Apache 2.0 (OSS)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>UI prompt-first&lt;/td>
&lt;td>✅&lt;/td>
&lt;td>⚠️ vía Model Registry&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Versionado inmutable&lt;/td>
&lt;td>✅&lt;/td>
&lt;td>✅&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Labels mutables&lt;/td>
&lt;td>✅ (arbitrarios)&lt;/td>
&lt;td>✅ (Staging/Production/Archived)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Cache de cliente nativo&lt;/td>
&lt;td>✅&lt;/td>
&lt;td>❌ (DIY)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Tracing integrado&lt;/td>
&lt;td>✅ nativo&lt;/td>
&lt;td>⚠️ vía MLflow Tracing&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Eval gating al promocionar&lt;/td>
&lt;td>✅&lt;/td>
&lt;td>⚠️ DIY con MLflow Recipes&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Self-host fácil&lt;/td>
&lt;td>✅ Docker/Helm&lt;/td>
&lt;td>✅ standard MLflow&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Curva si vienes de MLOps&lt;/td>
&lt;td>media&lt;/td>
&lt;td>nula&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Curva si vienes de DevOps&lt;/td>
&lt;td>nula&lt;/td>
&lt;td>media&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>En mayo de 2026, &lt;strong>el patrón híbrido más extendido&lt;/strong> es usar MLflow para el registry de modelos+adapters y Langfuse para prompts+tracing, conectados por &lt;code>trace_id&lt;/code> y &lt;code>prompt_id&lt;/code> que viajan en los span attributes de OpenTelemetry. Cubierto en &lt;a href="https://blog.lo0.es/posts/evals-llm-la-capa-despues-de-tracing/">evals&lt;/a> y &lt;a href="https://blog.lo0.es/posts/mcp-observability-otel/">MCP observability&lt;/a>.&lt;/p>
&lt;h2 id="schema-mínimo-de-un-prompt-versionado">Schema mínimo de un prompt versionado&lt;/h2>
&lt;p>Sin importar la herramienta, lo que el registry guarda en cada versión tiene un schema mínimo razonable:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="c"># prompt_id: customer_support_v3, version: 3&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">template&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">system&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">|&lt;/span>&lt;span class="sd">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sd"> Eres un asistente de soporte de {{company_name}}.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sd"> Responde en español neutral, máximo 3 frases.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sd"> Formato de respuesta: JSON {&amp;#34;answer&amp;#34;: &amp;#34;...&amp;#34;, &amp;#34;needs_human&amp;#34;: bool}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">user&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">|&lt;/span>&lt;span class="sd">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sd"> Pregunta del cliente: {{user_message}}
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sd"> Contexto del ticket: {{ticket_context}}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">variables&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">required&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="l">company_name, user_message, ticket_context]&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">defaults&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>{}&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">recommended_params&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">model&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;llama-3-70b-instruct&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">temperature&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="m">0.3&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">max_tokens&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="m">300&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">response_format&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;json_object&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">metadata&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">author&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;jose.roman@fibercli.com&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">created_at&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;2026-05-21T14:23:00Z&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">commit_message&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;Añade límite de 3 frases tras feedback ticket #1842&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">eval_suite&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;customer_support_v3_evals&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">related_traces&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;trace_id_x&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;trace_id_y&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Esto es &lt;strong>el contrato mínimo&lt;/strong>. Lo que diferencia a un despliegue serio:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>&lt;code>variables.required&lt;/code>&lt;/strong> se valida en el cliente antes de enviar al modelo. Una variable faltante explota en tiempo de cliente, no en una respuesta del modelo confusa.&lt;/li>
&lt;li>&lt;strong>&lt;code>recommended_params.model&lt;/code>&lt;/strong> liga la versión del prompt a un modelo. Cambiar de modelo abre debate (¿la nueva versión funciona con Llama 3 70B y con GPT-4o?). Si no se liga, el modelo es una variable más que descontrola la reproducibilidad.&lt;/li>
&lt;li>&lt;strong>&lt;code>metadata.eval_suite&lt;/code>&lt;/strong> es lo que las suites de eval enganchan: al crear v3, MLflow/Langfuse dispara &lt;code>customer_support_v3_evals&lt;/code> automáticamente.&lt;/li>
&lt;/ul>
&lt;h2 id="integración-con-eval-gates-promoción-gobernada">Integración con eval gates: promoción gobernada&lt;/h2>
&lt;p>El verdadero valor de prompt versioning aparece cuando se integra con eval. El patrón canónico:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Developer edita prompt en UI&lt;/strong> (Langfuse) o &lt;strong>API&lt;/strong> (MLflow). Se crea &lt;code>v4&lt;/code>.&lt;/li>
&lt;li>&lt;strong>Trigger automático&lt;/strong>: el evento &lt;code>prompt_created&lt;/code> dispara la suite de eval asociada (&lt;code>eval_suite&lt;/code> del metadata).&lt;/li>
&lt;li>&lt;strong>La suite corre&lt;/strong> contra el golden dataset (preguntas+respuestas etiquetadas por humano). Cubierto a primer nivel en &lt;a href="https://blog.lo0.es/posts/evals-llm-la-capa-despues-de-tracing/">el post de evals&lt;/a>.&lt;/li>
&lt;li>&lt;strong>Resultados se anexan a la versión&lt;/strong>: &lt;code>v4&lt;/code> ahora tiene &lt;code>eval_score: 0.84, regression_vs_v3: -0.03&lt;/code>.&lt;/li>
&lt;li>&lt;strong>Gate de promoción&lt;/strong>: si &lt;code>eval_score &amp;gt;= threshold&lt;/code> y &lt;code>regression &amp;lt; tolerance&lt;/code>, el label &lt;code>staging&lt;/code> se mueve a &lt;code>v4&lt;/code> automáticamente. Si no, alerta al developer.&lt;/li>
&lt;li>&lt;strong>Promoción manual a &lt;code>production&lt;/code>&lt;/strong>: con eval pasada, alguien con permiso mueve &lt;code>production&lt;/code> de &lt;code>v3&lt;/code> a &lt;code>v4&lt;/code>. Atómico, auditable, reversible.&lt;/li>
&lt;/ol>
&lt;pre tabindex="0">&lt;code>Developer edita prompt → v4 creada
│
▼
[eval suite trigger]
│
▼
Golden dataset 200 ejemplos
│
▼
score = 0.84 (vs 0.87 de v3)
│
├── Si pasa threshold → label staging → v4
│ └── Promoción manual a production tras revisión
└── Si no pasa → bloqueo + alerta al developer
&lt;/code>&lt;/pre>&lt;p>Este flujo convierte el prompt change de &amp;ldquo;alguien tocó el código y rezamos&amp;rdquo; a &amp;ldquo;un cambio de prompt es un PR que pasa CI&amp;rdquo;. Es la misma disciplina que MLOps clásico aplicó a modelos.&lt;/p>
&lt;h2 id="trazabilidad-por-petición-qué-versión-sirvió-cada-respuesta">Trazabilidad por petición: qué versión sirvió cada respuesta&lt;/h2>
&lt;p>La última pieza es &lt;strong>trazabilidad operativa&lt;/strong>: dada una respuesta del modelo en producción, ¿qué versión del prompt la generó?&lt;/p>
&lt;p>El patrón es propagar la versión como &lt;strong>span attribute&lt;/strong> en OpenTelemetry, siguiendo las semantic conventions &lt;code>gen_ai.*&lt;/code> que cubrimos en &lt;a href="https://blog.lo0.es/posts/mcp-observability-otel/">MCP observability&lt;/a>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># En el cliente (pseudo-código común)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">prompt&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">registry&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">load&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;customer_support_v3&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">label&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;production&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="c1"># v3 → v_id=14&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">with&lt;/span> &lt;span class="n">tracer&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">start_as_current_span&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;llm_call&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">as&lt;/span> &lt;span class="n">span&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">span&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">set_attribute&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;gen_ai.prompt.id&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;customer_support_v3&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">span&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">set_attribute&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;gen_ai.prompt.version&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;14&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">span&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">set_attribute&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;gen_ai.prompt.label&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;production&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">span&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">set_attribute&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;gen_ai.request.model&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">prompt&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">params&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">model&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">response&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">llm&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">complete&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">prompt&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">render&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">user_message&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">msg&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="o">**&lt;/span>&lt;span class="n">prompt&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">params&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">span&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">set_attribute&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;gen_ai.usage.input_tokens&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">response&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">usage&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">input&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">span&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">set_attribute&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;gen_ai.usage.output_tokens&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">response&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">usage&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">output&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>En cualquier trace (Langfuse, Phoenix, Jaeger, Honeycomb) se ve qué versión exacta sirvió esa respuesta. En un incidente — &amp;ldquo;el cliente X recibió esto el 22 de mayo&amp;rdquo; — se reproduce &lt;strong>literalmente&lt;/strong> la versión y el modelo que generaron la salida.&lt;/p>
&lt;p>Sin esta trazabilidad, el incidente queda como anécdota; con ella, es debuggable.&lt;/p>
&lt;h2 id="aplicado-a-hardware-on-premise-típico">Aplicado a hardware on-premise típico&lt;/h2>
&lt;p>Prompt versioning es una capa &lt;strong>ligera computacionalmente&lt;/strong> comparada con el motor de inferencia o el pipeline de fine-tuning. Sus requisitos:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Storage&lt;/strong>: el prompt registry pesa típicamente megabytes (cientos a miles de prompts con sus versiones). Postgres con un esquema &lt;code>prompts(id, version, template, params jsonb, metadata jsonb, created_at)&lt;/code> es más que suficiente. Langfuse usa Postgres por defecto; MLflow lo usa para metadata (los blobs van a object storage o filesystem).&lt;/li>
&lt;li>&lt;strong>Compute del registry&lt;/strong>: una pequeña instancia (1-2 vCPU, 2 GB RAM) atiende decenas de miles de lecturas por minuto si el cache de cliente está activado. Sin cache, escala linealmente con QPS pero sigue siendo trivial.&lt;/li>
&lt;li>&lt;strong>Compute de eval triggered&lt;/strong>: aquí sí hay coste. Cada vez que se crea una versión nueva, la suite de eval corre. Si la suite hace LLM-as-judge sobre 200 ejemplos y cada eval cuesta 4 K tokens, una promoción cuesta del orden de 1 M tokens — minutos en un cluster decente, segundos si la suite ya tiene su cache de prefijos calientes.&lt;/li>
&lt;/ul>
&lt;p>Para una &lt;strong>RTX 4090&lt;/strong> sirviendo Llama 3 8B con prompt registry self-hosted (Langfuse o MLflow): el registry corre en el mismo nodo en un contenedor sidecar, la app local cachea en RAM, los eval triggers corren contra el mismo motor de inferencia con baja prioridad. Setup completo en una mañana.&lt;/p>
&lt;p>Para un &lt;strong>cluster 4×H100 SXM&lt;/strong> sirviendo modelo grande a varios tenants: registry en pod K8s dedicado con Postgres replicado, suites de eval corren en pods con priority class &lt;code>spot&lt;/code> (cubierto en &lt;a href="https://blog.lo0.es/posts/cluster-h100-plataforma-multi-tenant/">cluster como plataforma&lt;/a>), tracing OTel propaga &lt;code>prompt_id+version&lt;/code> a Langfuse central.&lt;/p>
&lt;h2 id="trampas-y-cosas-que-no-son-lo-que-parecen">Trampas y cosas que no son lo que parecen&lt;/h2>
&lt;p>&lt;strong>Prompts hardcodeados en el código de la app.&lt;/strong> El antipatrón más común. El prompt vive en un fichero &lt;code>prompts.py&lt;/code> o &lt;code>templates/customer.txt&lt;/code> que se desploya con la app. No hay versionado real (el git history no es el sustituto: no liga commit ↔ trace de producción de forma operacional). Migrar a un registry es trabajo de 1-2 sprints; vale cada hora.&lt;/p>
&lt;p>&lt;strong>Cache mal calibrado.&lt;/strong> TTL de horas con label mutable significa que un rollback tarda en propagarse. TTL de segundos sobrecarga el registry. El default razonable es &lt;strong>60-300 segundos&lt;/strong> con invalidación por evento (el registry emite un mensaje a Kafka/Redis cuando un label cambia, los clientes invalidan inmediatamente).&lt;/p>
&lt;p>&lt;strong>Variables no validadas.&lt;/strong> El template usa &lt;code>{{user_name}}&lt;/code> pero la app pasa &lt;code>{{username}}&lt;/code>. El render produce un prompt con &lt;code>{{user_name}}&lt;/code> literal. El modelo responde algo bizarro y nadie sabe por qué. Validar &lt;strong>variables required en el cliente&lt;/strong> antes de enviar al modelo es la disciplina mínima.&lt;/p>
&lt;p>&lt;strong>Prompts dentro de chains evaluados en runtime.&lt;/strong> Si tu stack usa LangChain, LlamaIndex o similar con chains que componen prompts en runtime, el prompt &lt;strong>final&lt;/strong> que ve el modelo puede no estar en el registry porque se compuso de varios fragmentos. Soluciones: o se registran las chains como artefactos, o se loggea el prompt compuesto efectivo en cada trace.&lt;/p>
&lt;p>&lt;strong>Eval suite no enganchada al &lt;code>prompt_id&lt;/code>.&lt;/strong> Sin esta unión, un cambio de prompt promociona sin pasar evals. La integración tiene que ser &lt;strong>un campo en el metadata del prompt&lt;/strong> (&lt;code>eval_suite: ...&lt;/code>) que el sistema lee y dispara automáticamente. Si depende de que el developer &amp;ldquo;se acuerde&amp;rdquo;, el patrón fallará.&lt;/p>
&lt;p>&lt;strong>Roles RBAC inexistentes.&lt;/strong> Cualquiera con acceso a la UI puede mover &lt;code>production&lt;/code> a cualquier versión. Sin separación &lt;code>editor&lt;/code> (crea versiones) vs &lt;code>releaser&lt;/code> (mueve labels production), un developer junior puede romper producción con una promoción accidental. Langfuse Enterprise tiene RBAC granular; MLflow lo tiene vía el server backend con permisos por experimento/registry.&lt;/p>
&lt;p>&lt;strong>Prompts con datos sensibles inline.&lt;/strong> El prompt template incluye ejemplos few-shot con nombres reales, direcciones, IDs de cliente. El registry guarda eso indefinidamente. Bajo GDPR, hay derecho al olvido aplicable también al registry. Buena práctica: &lt;strong>variables para datos sensibles&lt;/strong>, no inline; auditoría periódica del contenido del registry.&lt;/p>
&lt;h2 id="patrón-operativo-recomendado-el-ciclo-en-una-pantalla">Patrón operativo recomendado: el ciclo en una pantalla&lt;/h2>
&lt;p>Un equipo serio con prompt versioning bien montado tiene el siguiente ciclo, repetible y barato:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Developer abre PR en repo&lt;/strong>: cambia el código de la app si es necesario, pero &lt;strong>no toca el prompt&lt;/strong> allí.&lt;/li>
&lt;li>&lt;strong>Edita prompt en Langfuse/MLflow UI&lt;/strong>: crea &lt;code>v_new&lt;/code>. Añade commit message (&amp;ldquo;añade límite de 3 frases tras feedback ticket #1842&amp;rdquo;).&lt;/li>
&lt;li>&lt;strong>Suite de eval dispara automáticamente&lt;/strong>: corre contra golden dataset, resultados aparecen en la UI en minutos.&lt;/li>
&lt;li>&lt;strong>Si pasa eval&lt;/strong>: label &lt;code>staging&lt;/code> se mueve a &lt;code>v_new&lt;/code> automáticamente. Developer puede testear staging con tráfico controlado.&lt;/li>
&lt;li>&lt;strong>Revisión humana&lt;/strong> (1-2 personas, opcional según severidad): aprobación.&lt;/li>
&lt;li>&lt;strong>Promoción a &lt;code>production&lt;/code>&lt;/strong>: mover el label, atómico. El cliente cachea durante 60-300 s, después sirve la nueva versión.&lt;/li>
&lt;li>&lt;strong>Observe&lt;/strong>: en Langfuse/Phoenix, métricas y eval scores en producción se segmentan por versión del prompt. Si el score se cae con &lt;code>v_new&lt;/code>, alerta.&lt;/li>
&lt;li>&lt;strong>Si hay regresión seria&lt;/strong>: rollback es &lt;strong>mover el label hacia atrás&lt;/strong>. Operación de 5 segundos.&lt;/li>
&lt;/ol>
&lt;p>Cada paso está auditado, cada decisión deja rastro, cada rollback es operación atómica. Esto es lo que separa un sistema GenAI de &amp;ldquo;demos que funcionaron una vez&amp;rdquo; de un sistema operable durante años.&lt;/p>
&lt;h2 id="lo-que-no-hemos-cubierto-próximos-posts">Lo que no hemos cubierto (próximos posts)&lt;/h2>
&lt;ul>
&lt;li>&lt;strong>Prompt optimization automática&lt;/strong>: técnicas como DSPy, TextGrad, PromptBreeder que generan candidatos de prompt y los optimizan contra un objetivo medible. La extensión del versioning donde el &amp;ldquo;developer&amp;rdquo; puede ser un optimizador.&lt;/li>
&lt;li>&lt;strong>Prompt injection y red teaming&lt;/strong>: integrar el versionado con el flow de evaluación adversarial. Cubierto parcialmente en &lt;a href="https://blog.lo0.es/posts/guardrails-safety-llm/">guardrails&lt;/a>.&lt;/li>
&lt;li>&lt;strong>Diferentes versiones por tenant&lt;/strong>: cuando el mismo &lt;code>prompt_id&lt;/code> necesita variantes por cliente (i18n, branding, dominio). Patrón de fork + override.&lt;/li>
&lt;/ul>
&lt;h2 id="ver-también">Ver también&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://blog.lo0.es/posts/pipeline-llmops-seis-etapas/">El pipeline LLMOps de seis etapas&lt;/a> — el mapa maestro donde prompt versioning aparece como componente transversal en la banda de &amp;ldquo;todas las etapas&amp;rdquo;. Este post entra al detalle del componente.&lt;/li>
&lt;li>&lt;a href="https://blog.lo0.es/posts/mlops-llms-panorama-2026/">MLOps específico para LLMs en 2026: el panorama&lt;/a> — apertura de la serie con el contexto de herramientas y las diferencias estructurales con MLOps clásico.&lt;/li>
&lt;li>&lt;a href="https://blog.lo0.es/posts/evals-llm-la-capa-despues-de-tracing/">Evals: la capa después del tracing que decide si tu LLM rinde o sólo parece rendir&lt;/a> — las suites de eval que se enganchan a &lt;code>prompt_id&lt;/code> para el gate de promoción descrito aquí.&lt;/li>
&lt;li>&lt;a href="https://blog.lo0.es/posts/mcp-observability-otel/">MCP por dentro y su observabilidad profunda&lt;/a> — las semantic conventions &lt;code>gen_ai.*&lt;/code> y la propagación de trace context que llevan &lt;code>prompt_id+version&lt;/code> por todos los spans.&lt;/li>
&lt;li>&lt;a href="https://blog.lo0.es/posts/agentsight-tracing-llm/">AgentSight y el nuevo tracing de LLMs&lt;/a> — cómo el tracing observa los prompts en runtime, incluyendo prompts compuestos en chains.&lt;/li>
&lt;li>&lt;a href="https://blog.lo0.es/posts/fine-tuning-continuo-produccion/">Fine-tuning continuo en producción&lt;/a> — el ciclo de Tune+Retrain produce adapters cuyo system prompt convive con el versionado descrito aquí.&lt;/li>
&lt;/ul>
&lt;h2 id="referencias">Referencias&lt;/h2>
&lt;ul>
&lt;li>Langfuse documentation, &lt;em>Prompt Management&lt;/em>: &lt;a href="https://langfuse.com/docs/prompts/get-started">https://langfuse.com/docs/prompts/get-started&lt;/a>.&lt;/li>
&lt;li>MLflow 3.10 release notes, &lt;em>Prompts in Model Registry&lt;/em> (marzo 2026): &lt;a href="https://mlflow.org/releases/3.10">https://mlflow.org/releases/3.10&lt;/a>.&lt;/li>
&lt;li>OpenTelemetry, &lt;em>Semantic Conventions for Generative AI&lt;/em> (estables desde 1.36 de OTel): &lt;a href="https://opentelemetry.io/docs/specs/semconv/gen-ai/">https://opentelemetry.io/docs/specs/semconv/gen-ai/&lt;/a>.&lt;/li>
&lt;li>Google Cloud, &lt;em>Prompt Management: Best Practices for Production LLM Systems&lt;/em> (2025).&lt;/li>
&lt;li>Chip Huyen, &lt;em>Designing Machine Learning Systems&lt;/em> — capítulo sobre model registry y prompt-as-artifact (2ª edición, marzo 2026).&lt;/li>
&lt;li>Eugene Yan, &lt;em>Prompt Engineering as Software Engineering&lt;/em> (blog, 2025).&lt;/li>
&lt;/ul></description></item></channel></rss>