StackPractices
intermediate Por Mathias Paulenko

Plantilla de Control de Versiones de Prompts de IA

Versiona tus prompts de LLM con puntuaciones de evaluación, historial de cambios, reversión y pruebas A/B. Incluye esquema de metadatos de prompts, formato de changelog, seguimiento de evaluación e integración CI/CD para gestión de prompts.

Temas: ai

Overview

Esta plantilla te da una estructura para versionar prompts de LLM. Cada versión del prompt tiene metadatos, un changelog, puntuaciones de evaluación y una ruta de reversión. Úsala para gestionar cambios de prompts con el mismo rigor que los cambios de código. El enfoque refleja la guía de prompt engineering de OpenAI y se integra con plataformas de evaluación como LangSmith.

flowchart diagram: Desarrollador crea<br/>nueva versión

Cuándo Usar Esta Plantilla

Usa esta plantilla cuando tu equipo gestiona prompts que afectan el comportamiento en producción. No la necesitas para scripts de un solo uso o trabajo exploratorio, pero sí la necesitas cuando:

  • Un prompt impulsa una funcionalidad visible para el usuario (clasificación de soporte, moderación de contenido, extracción).
  • Múltiples personas editan el mismo prompt a lo largo del tiempo.
  • Necesitas comparar versiones de prompts con métricas, no con intuición.
  • Una regresión en el comportamiento del prompt perjudicaría a los usuarios o costaría dinero.
  • Quieres enviar cambios de prompts a través del mismo flujo de revisión que el código.

Si tu prompt es un único system message que nunca cambia, omite esta plantilla. Si envías actualizaciones de prompts semanalmente o diariamente, esta plantilla se paga sola con la primera reversión.


1. Prompt Metadata Schema

# prompt_metadata.yaml
prompt_id: "customer-support-classifier"
name: "Customer Support Ticket Classifier"
description: "Classifies incoming support tickets into categories"
current_version: "3.2.0"
status: "production"
owner: "ai-team@company.com"

versions:
  - version: "3.2.0"
    date: "2026-07-04"
    author: "jane@company.com"
    status: "production"
    model: "gpt-4o-mini"
    temperature: 0
    change_type: "patch"
    changes:
      - "Reduced system prompt from 450 to 280 tokens"
      - "Removed redundant category descriptions"
    eval_scores:
      accuracy: 0.92
      precision: 0.91
      recall: 0.89
      f1: 0.90
      latency_p95: 280
      cost_per_1k: 0.53
    rollback_to: "3.1.0"
    
  - version: "3.1.0"
    date: "2026-06-20"
    author: "jane@company.com"
    status: "archived"
    model: "gpt-4o-mini"
    temperature: 0
    change_type: "minor"
    changes:
      - "Added 'BILLING' category"
      - "Updated category descriptions for clarity"
    eval_scores:
      accuracy: 0.90
      precision: 0.89
      recall: 0.88
      f1: 0.88
      latency_p95: 320
      cost_per_1k: 0.55
    rollback_to: "3.0.0"
    
  - version: "3.0.0"
    date: "2026-06-01"
    author: "john@company.com"
    status: "archived"
    model: "gpt-4o-mini"
    temperature: 0
    change_type: "major"
    changes:
      - "Migrated from GPT-4o to GPT-4o-mini (15x cost reduction)"
      - "Rewrote system prompt for mini model"
      - "Added UNCLEAR category for ambiguous tickets"
    eval_scores:
      accuracy: 0.88
      precision: 0.87
      recall: 0.86
      f1: 0.86
      latency_p95: 300
      cost_per_1k: 0.53
    rollback_to: "2.1.0"

2. Prompt File Structure

prompts/
├── customer-support-classifier/
│   ├── prompt.md                 # Prompt de producción actual
│   ├── metadata.yaml             # Metadatos de versión y puntuaciones de evaluación
│   ├── versions/
│   │   ├── 3.2.0.md
│   │   ├── 3.1.0.md
│   │   ├── 3.0.0.md
│   │   └── 2.1.0.md
│   ├── eval/
│   │   ├── test_set.jsonl        # 200 casos de prueba etiquetados
│   │   ├── eval_results_3.2.0.json
│   │   ├── eval_results_3.1.0.json
│   │   └── eval_results_3.0.0.json
│   └── ab_tests/
│       ├── 3.1.0_vs_3.2.0.json
│       └── 3.0.0_vs_3.1.0.json

3. Version Numbering

MAJOR.MINOR.PATCH

MAJOR: Cambios disruptivos
  - Cambio de modelo (ej: GPT-4o → Claude 3.5)
  - Cambio de esquema de salida
  - Cambio de conjunto de categorías (añadir/eliminar categorías)
  - Requiere re-evaluación y aprobación de stakeholders

MINOR: Adiciones de funcionalidades
  - Nueva categoría añadida
  - Nuevo ejemplo few-shot añadido
  - System prompt reestructurado
  - Requiere evaluación pero no aprobación de stakeholders

PATCH: Optimizaciones
  - Reducción de tokens
  - Ajustes de redacción
  - Cambios de formato
  - Requiere evaluación, aprobación rápida

4. Changelog Format

## [3.2.0] — 2026-07-04

### Changed
- Reduced system prompt from 450 to 280 tokens (37% reduction)
- Removed redundant category descriptions for COMPLAINT and FEEDBACK
- Consolidated overlapping instructions into a single rules section

### Impact
- Cost per 1000 queries: $0.55 → $0.53 (4% savings)
- Latency p95: 320ms → 280ms (12% faster)
- Accuracy: 0.90 → 0.92 (+2%)
- F1: 0.88 → 0.90 (+2%)

### Rollback
- Revert to 3.1.0: `cp versions/3.1.0.md prompt.md`
- No data migration needed

5. Evaluation Protocol

Evaluación Pre-Despliegue

El protocolo de evaluación sigue el patrón del framework OpenAI Evals: ejecuta cada versión del prompt contra un conjunto de prueba fijo y registra precisión, exhaustividad y F1.

import json
from openai import OpenAI

client = OpenAI()

def evaluate_prompt_version(prompt_path: str, test_set_path: str, model: str):
    with open(prompt_path) as f:
        prompt_template = f.read()
    
    with open(test_set_path) as f:
        test_cases = [json.loads(line) for line in f]
    
    correct = 0
    total = len(test_cases)
    
    for case in test_cases:
        prompt = prompt_template.replace("{{input_text}}", case["input"])
        response = client.chat.completions.create(
            model=model,
            messages=[{"role": "user", "content": prompt}],
            temperature=0,
        )
        prediction = response.choices[0].message.content.strip()
        
        if prediction == case["expected"]:
            correct += 1
    
    accuracy = correct / total
    
    return {
        "version": prompt_path.split("/")[-1].replace(".md", ""),
        "accuracy": accuracy,
        "test_cases": total,
        "correct": correct,
        "incorrect": total - correct,
    }

# Ejecuta la evaluación en la nueva versión
results = evaluate_prompt_version(
    "prompts/classifier/versions/3.2.0.md",
    "prompts/classifier/eval/test_set.jsonl",
    "gpt-4o-mini",
)

print(json.dumps(results, indent=2))
# {
#   "version": "3.2.0",
#   "accuracy": 0.92,
#   "test_cases": 200,
#   "correct": 184,
#   "incorrect": 16
# }

Regression Check

def compare_versions(old_results: dict, new_results: dict) -> dict:
    accuracy_delta = new_results["accuracy"] - old_results["accuracy"]
    
    verdict = "PASS"
    if accuracy_delta < -0.02:
        verdict = "FAIL — accuracy dropped more than 2%"
    elif accuracy_delta < 0:
        verdict = "WARN — accuracy decreased slightly"
    
    return {
        "old_version": old_results["version"],
        "new_version": new_results["version"],
        "old_accuracy": old_results["accuracy"],
        "new_accuracy": new_results["accuracy"],
        "delta": accuracy_delta,
        "verdict": verdict,
    }

6. Pruebas A/B

Para significancia estadística, usa una prueba chi-cuadrado o la prueba t de Welch antes de declarar un ganador. La clase siguiente enruta tráfico y registra resultados.

import random
import json
from collections import defaultdict

class PromptABTest:
    def __init__(self, prompt_a: str, prompt_b: str, traffic_split: float = 0.5):
        self.prompt_a = prompt_a
        self.prompt_b = prompt_b
        self.traffic_split = traffic_split
        self.results = defaultdict(lambda: {"correct": 0, "total": 0})
    
    def route(self) -> str:
        if random.random() < self.traffic_split:
            return self.prompt_a, "A"
        return self.prompt_b, "B"
    
    def record(self, variant: str, correct: bool):
        self.results[variant]["total"] += 1
        if correct:
            self.results[variant]["correct"] += 1
    
    def report(self) -> dict:
        a = self.results["A"]
        b = self.results["B"]
        
        return {
            "variant_a": {
                "accuracy": a["correct"] / a["total"] if a["total"] > 0 else 0,
                "samples": a["total"],
            },
            "variant_b": {
                "accuracy": b["correct"] / b["total"] if b["total"] > 0 else 0,
                "samples": b["total"],
            },
            "winner": "A" if a["correct"] / max(a["total"], 1) > b["correct"] / max(b["total"], 1) else "B",
        }

# Uso
ab_test = PromptABTest(
    prompt_a="prompts/classifier/versions/3.1.0.md",
    prompt_b="prompts/classifier/versions/3.2.0.md",
)

# Después de 1000 muestras
report = ab_test.report()
print(json.dumps(report, indent=2))

7. CI/CD Integration

# .github/workflows/prompt-eval.yml
name: Prompt Evaluation

on:
  pull_request:
    paths:
      - "prompts/**"

jobs:
  evaluate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - name: Setup Python
        uses: actions/setup-python@v5
        with:
          python-version: "3.12"
      
      - name: Install dependencies
        run: pip install openai
      
      - name: Run prompt evaluation
        env:
          OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
        run: |
          python scripts/eval_prompt.py \
            --prompt prompts/classifier/prompt.md \
            --test-set prompts/classifier/eval/test_set.jsonl \
            --model gpt-4o-mini \
            --threshold 0.88
      
      - name: Check regression
        run: |
          python scripts/check_regression.py \
            --old-results prompts/classifier/eval/eval_results_3.1.0.json \
            --new-results eval_output.json \
            --max-drop 0.02

8. Approval Process

1. El desarrollador crea una nueva versión del prompt en prompts/[name]/versions/
2. El desarrollador ejecuta la evaluación: python scripts/eval_prompt.py
3. El desarrollador crea un PR con:
   - Archivo de la nueva versión del prompt
   - metadata.yaml actualizado
   - Resultados de la evaluación
4. CI ejecuta la evaluación automatizada y la verificación de regresión
5. El revisor verifica:
   - Las puntuaciones de evaluación cumplen el umbral
   - Sin regresión más allá del máximo permitido
   - El changelog es claro y preciso
6. El revisor aprueba el PR
7. PR fusionado → la nueva versión pasa a producción
8. La versión anterior se archiva en el directorio versions/
9. Si la nueva versión causa problemas en producción → reversión a la versión anterior

Errores Comunes

Omitir el conjunto de prueba

Los equipos suelen enviar un cambio de prompt sin un conjunto de prueba etiquetado, y luego descubren una regresión días después. Construye el conjunto de prueba antes de la primera versión, no después del primer incidente. Incluso 50 casos etiquetados detectan la mayoría de las regresiones; 200 te da confianza para deltas de precisión de 2 puntos.

Usar la precisión como única métrica

La precisión oculta el desbalance de clases. Un clasificador que predice “OTHER” para el 90% de las entradas puede alcanzar 90% de precisión siendo inútil. Registra precisión, exhaustividad y F1 por clase, además de latencia y coste. Un prompt que es 2% más preciso pero 3x más lento suele ser una pérdida neta.

Tratar los prompts como código sin el paso de revisión

Versionar prompts en Git es un buen comienzo, pero fusionar sin revisión derrota el propósito. La revisión del PR es donde detectas casos límite faltantes, changelogs poco claros y resultados de evaluación que no coinciden con lo afirmado. Haz la revisión obligatoria, incluso para cambios de parche.

Revertir sin documentar por qué

Cuando reviertes, el changelog debería explicar qué se rompió. Una razón de reversión como “la precisión bajó” es inútil seis meses después. Escribe el fallo específico: “3.2.0 clasificó tickets de BILLING como COMPLAINT cuando el ticket mencionaba un reembolso — caso límite no en el conjunto de prueba.”

Pruebas A/B sin significancia estadística

Declarar un ganador después de 100 muestras es ruido, no señal. Usa los tamaños de muestra del FAQ (3000 por variante para un delta del 2% al 95% de confianza). Si no puedes alcanzar ese tamaño de muestra, ejecuta la prueba más tiempo o acepta que el resultado es inconcluso.

See Also

Preguntas frecuentes

¿Cómo creo un conjunto de prueba para la evaluación de prompts?

Reúne más de 200 entradas reales que tu prompt procesa. Para cada entrada, verifica manualmente la salida esperada correcta. Incluye casos límite: entrada vacía, entrada muy larga, entrada ambigua, entrada adversarial. Guárdalo como JSONL con campos input y expected. Actualiza el conjunto de prueba cuando añadas nuevas categorías o cambies el esquema de salida. Nunca incluyas PII en el conjunto de prueba.

¿Qué umbral de precisión debería usar?

Depende de la tarea. Para clasificación: objetivo > 0.90 de precisión. Para extracción: objetivo > 0.85 F1. Para resumen: objetivo > 4.0/5 en relevancia y fidelidad. Define el umbral según el impacto de negocio — si una clasificación errónea cuesta $100, necesitas mayor precisión que si cuesta $1. Siempre define un umbral mínimo que bloquee el despliegue si no se cumple.

¿Cómo revierto un cambio de prompt?

Conserva todas las versiones anteriores del prompt en el directorio versions/. Para revertir, copia la versión anterior al archivo de prompt de producción: cp versions/3.1.0.md prompt.md. Actualiza metadata.yaml para establecer la versión revertida como actual. No necesitas despliegue de código — solo un cambio de archivo. Documenta la razón de la reversión en el changelog.

¿Cuántas muestras necesito para pruebas A/B?

Para una diferencia de precisión del 2% con 95% de confianza, necesitas aproximadamente 3000 muestras por variante. Para una diferencia del 5%, 500 muestras por variante. Usa una prueba de significancia estadística (chi-cuadrado o t-test) antes de declarar un ganador. Ejecuta la prueba durante al menos 7 días para tener en cuenta la variación diaria en los tipos de entrada.

¿Deberían los prompts estar en Git o en un sistema separado?

Git funciona bien para equipos con menos de 50 prompts. Almacena los archivos de prompt, metadatos y conjuntos de prueba en Git junto al código de tu aplicación. Para equipos más grandes o productos con muchos prompts, considera una plataforma dedicada de gestión de prompts (LangSmith, PromptLayer, Humanloop) que tiene versionado, evaluación, pruebas A/B y monitoreo listos para usar. Los principios de esta plantilla aplican independientemente de la herramienta.