Inicio / LLMOps / LLMOps: De Prototipo a Producción / Gestión de Prompts

Gestión de Prompts

Prompt registry, versionado, A/B testing y pipeline de evaluación.

Principiante
🔒 Solo lectura
📖

Estás en modo lectura

Puedes leer toda la lección, pero para marcar progreso, hacer ejercicios y ganar XP necesitas una cuenta Pro.

Desbloquear por $9/mes

Gestión de Prompts en Producción

Prompts como Código

En producción, los prompts no son strings ad-hoc — son artefactos de ingeniería que necesitan versionado, testing, revisión y deployment controlado.


Prompt Registry

import json
import hashlib
from datetime import datetime
from pathlib import Path

class PromptRegistry:
    """Registro centralizado de prompts con versionado."""
    
    def __init__(self, storage_path: str = "prompts/"):
        self.storage = Path(storage_path)
        self.storage.mkdir(exist_ok=True)
    
    def register(self, name: str, template: str, metadata: dict = None) -> str:
        version = hashlib.sha256(template.encode()).hexdigest()[:8]
        
        prompt_data = {
            "name": name,
            "version": version,
            "template": template,
            "metadata": metadata or {},
            "created_at": datetime.utcnow().isoformat(),
        }
        
        path = self.storage / f"{name}_v{version}.json"
        path.write_text(json.dumps(prompt_data, indent=2))
        
        # Actualizar latest
        latest = self.storage / f"{name}_latest.json"
        latest.write_text(json.dumps(prompt_data, indent=2))
        
        return version
    
    def get(self, name: str, version: str = "latest") -> dict:
        path = self.storage / f"{name}_{version}.json"
        if not path.exists():
            path = self.storage / f"{name}_latest.json"
        return json.loads(path.read_text())
    
    def render(self, name: str, version: str = "latest", **kwargs) -> str:
        prompt = self.get(name, version)
        template = prompt["template"]
        for key, value in kwargs.items():
            template = template.replace(f"{{{key}}}", str(value))
        return template

# Uso
registry = PromptRegistry()
registry.register(
    "classify_ticket",
    "Clasifica el ticket en: {categories}\n\nTicket: {text}\n\nCategoría:",
    metadata={"model": "gpt-4o", "temperature": 0}
)

A/B Testing de Prompts

import random
from dataclasses import dataclass

@dataclass
class PromptVariant:
    name: str
    template: str
    weight: float = 0.5

class PromptABTest:
    def __init__(self, test_name: str, variants: list[PromptVariant]):
        self.test_name = test_name
        self.variants = variants
        self.results = {v.name: {"calls": 0, "scores": []} for v in variants}
    
    def select_variant(self) -> PromptVariant:
        """Seleccionar variante basada en pesos."""
        weights = [v.weight for v in self.variants]
        selected = random.choices(self.variants, weights=weights, k=1)[0]
        self.results[selected.name]["calls"] += 1
        return selected
    
    def record_score(self, variant_name: str, score: float):
        self.results[variant_name]["scores"].append(score)
    
    def get_stats(self) -> dict:
        stats = {}
        for name, data in self.results.items():
            scores = data["scores"]
            stats[name] = {
                "calls": data["calls"],
                "avg_score": sum(scores) / len(scores) if scores else 0,
                "min_score": min(scores) if scores else 0,
                "max_score": max(scores) if scores else 0,
            }
        return stats

# Uso
test = PromptABTest("summarize_v2", [
    PromptVariant("concise", "Resume en 2 oraciones:\n{text}"),
    PromptVariant("detailed", "Resume manteniendo puntos clave:\n{text}"),
])

Prompt Versioning con Git

prompts/
├── classify/
│   ├── v1.0.yaml
│   ├── v1.1.yaml
│   └── v2.0.yaml      # breaking change
├── summarize/
│   ├── v1.0.yaml
│   └── v1.1.yaml
└── extract/
    └── v1.0.yaml

Formato YAML para Prompts

# prompts/classify/v2.0.yaml
name: classify_support_ticket
version: "2.0"
description: "Clasifica tickets de soporte en categorías"
model: gpt-4o
temperature: 0
max_tokens: 50

system: |
  Eres un clasificador de tickets de soporte.
  Responde SOLO con la categoría, sin explicación.

user_template: |
  Categorías disponibles: {categories}
  
  Ticket del cliente:
  {ticket_text}
  
  Categoría:

variables:
  - name: categories
    required: true
    description: "Lista de categorías separadas por coma"
  - name: ticket_text
    required: true
    description: "Texto del ticket a clasificar"

test_cases:
  - input:
      categories: "bug, feature, question, billing"
      ticket_text: "No puedo iniciar sesión desde ayer"
    expected_output: "bug"
  - input:
      categories: "bug, feature, question, billing"
      ticket_text: "¿Cuánto cuesta el plan premium?"
    expected_output: "billing"

changelog:
  - version: "2.0"
    date: "2025-12-01"
    changes: "Añadido few-shot examples, mejorada precisión en billing"
  - version: "1.0"
    date: "2025-10-15"
    changes: "Versión inicial"

Prompt Evaluation Pipeline

import yaml
from dataclasses import dataclass

@dataclass
class EvalResult:
    test_name: str
    passed: bool
    expected: str
    actual: str
    latency_ms: float

class PromptEvaluator:
    def __init__(self, llm_client):
        self.client = llm_client
    
    def load_prompt(self, path: str) -> dict:
        with open(path) as f:
            return yaml.safe_load(f)
    
    def evaluate(self, prompt_path: str) -> list[EvalResult]:
        prompt_config = self.load_prompt(prompt_path)
        results = []
        
        for i, test in enumerate(prompt_config.get("test_cases", [])):
            # Renderizar template
            user_msg = prompt_config["user_template"]
            for key, value in test["input"].items():
                user_msg = user_msg.replace(f"{{{key}}}", value)
            
            # Llamar al LLM
            import time
            start = time.time()
            response = self.client.chat.completions.create(
                model=prompt_config["model"],
                messages=[
                    {"role": "system", "content": prompt_config["system"]},
                    {"role": "user", "content": user_msg},
                ],
                temperature=prompt_config["temperature"],
                max_tokens=prompt_config["max_tokens"],
            )
            latency = (time.time() - start) * 1000
            
            actual = response.choices[0].message.content.strip()
            passed = actual.lower() == test["expected_output"].lower()
            
            results.append(EvalResult(
                test_name=f"test_{i+1}",
                passed=passed,
                expected=test["expected_output"],
                actual=actual,
                latency_ms=latency,
            ))
        
        return results

Mejores Prácticas

  1. Versionar prompts igual que código (git, semantic versioning)
  2. Testear cada cambio de prompt con test cases
  3. Monitorear performance de prompts en producción
  4. Rollback rápido a versiones anteriores si hay regresión
  5. Documentar el propósito y uso esperado de cada prompt
  6. Separar configuración (model, temperature) del contenido

Resumen

Gestionar prompts como código — con versionado, testing automatizado, A/B testing y rollback — es fundamental para mantener la calidad y confiabilidad de aplicaciones LLM en producción.

🔒

Ejercicio práctico disponible

Prompt registry y versionado

Desbloquear ejercicios
// Prompt registry y versionado
// Desbloquea Pro para acceder a este ejercicio
// y ganar +50 XP al completarlo

function ejemplo() {
    // Tu código aquí...
}

¿Te gustó esta lección?

Con Pro puedes marcar progreso, hacer ejercicios, tomar quizzes, ganar XP y obtener tu constancia.

Ver planes desde $9/mes