C
Contextología
Prompt Engineering

Cómo iterar prompts sistemáticamente: del prototipo a producción

22 de mayo de 2026· 6 min read

La mayoría de las personas iteran prompts a ojo: prueban el nuevo prompt con 2-3 casos, parece que mejora, lo usan en producción. Semanas después descubren que mejoró para esos 3 casos pero empeoró en otros 20.

La iteración sistemática de prompts es lo que separa al prompt engineering amateur del profesional. No es complicada, pero requiere disciplina.

El problema con "probar a ojo"

Cuando cambias un prompt y lo pruebas con 3 ejemplos:

  • Si los 3 funcionan bien, asumes que el prompt mejoró
  • Si 1 falla, modificas y vuelves a probar

El problema: los LLMs tienen varianza. El mismo prompt con los mismos inputs puede dar resultados ligeramente distintos. Sin suficientes casos de prueba, no puedes distinguir si mejoraste el prompt o simplemente tuviste suerte.

Además, los prompts tienen trade-offs. Arreglar el comportamiento en un tipo de input a menudo lo empeora en otro. Sin un dataset representativo, nunca sabes si el cambio fue neto positivo.

Paso 1: Construye tu dataset de evaluación

El dataset es la base de todo. Sin él, iterar es disparar en la oscuridad.

Qué incluir:

eval_cases = [
    # Caso 1: Happy path típico
    {
        "input": "¿Cómo cancelo mi suscripción?",
        "expected_output": {
            "categoria": "facturacion",
            "accion": "proporcionar_instrucciones",
        },
        "notes": "Caso más frecuente según logs de producción"
    },
    # Caso 2: Input ambiguo
    {
        "input": "Quiero hacer un cambio",
        "expected_output": {
            "categoria": "general",
            "accion": "pedir_clarificacion",
        },
        "notes": "El sistema debe pedir más información, no asumir"
    },
    # Caso 3: Edge case conocido
    {
        "input": "mi pedido #12345 llegó roto y necesito hablar con alguien AHORA",
        "expected_output": {
            "categoria": "soporte_urgente",
            "accion": "escalar_humano",
        },
        "notes": "Urgencia + pedido roto = escalar siempre"
    },
    # Caso 4: Input en idioma distinto
    {
        "input": "Hello, I need help with my order",
        "expected_output": {
            "categoria": "soporte_general",
            "idioma_detectado": "en",
        },
    },
]

Cuántos casos:

  • Mínimo para empezar: 20-30
  • Para producción: 50-100
  • Para sistemas críticos: 200+

Los primeros casos son los que más tardan en construir. El dataset crece con el tiempo: cada fallo en producción que encuentras añade un nuevo caso.

Fuentes para construir el dataset:

  1. Logs de producción (los mejores)
  2. Casos edge que identificas durante el desarrollo
  3. Casos que el producto owner considera críticos
  4. Generación automática con LLM + revisión humana

Paso 2: Define tus métricas

Sin métricas no hay comparación objetiva.

Para tareas de clasificación:

def accuracy(results: list[dict]) -> float:
    correct = sum(
        1 for r in results
        if r["actual"]["categoria"] == r["expected"]["categoria"]
    )
    return correct / len(results)

def precision_by_class(results, target_class):
    predicted_as_target = [r for r in results if r["actual"]["categoria"] == target_class]
    if not predicted_as_target:
        return 0.0
    correct = sum(1 for r in predicted_as_target if r["expected"]["categoria"] == target_class)
    return correct / len(predicted_as_target)

Para tareas de generación: Necesitas un evaluador (puede ser otro LLM):

async def evaluar_respuesta(respuesta: str, criterios: list[str]) -> dict:
    prompt = f"""Evalúa esta respuesta con estos criterios.
    Para cada criterio, da una puntuación del 1 al 5.
    
    Criterios: {criterios}
    
    Respuesta: {respuesta}
    
    Responde con JSON: {{"criterio": puntuacion, ...}}"""
    
    return json.loads(await llm.generate(prompt))

Métricas clave para la mayoría de sistemas:

  • Accuracy global: % de casos correctos
  • Accuracy por categoría: detecta si mejoras en un clase pero empeoraseen otra
  • Tasa de fallback: % de casos donde el sistema no puede dar respuesta
  • Latencia: especialmente si añades técnicas costosas como CoT

Paso 3: Establece la línea base

Antes de cualquier cambio, mide el prompt actual con tu dataset completo. Esta es tu línea base.

def run_eval(prompt: str, test_cases: list[dict]) -> dict:
    results = []
    for case in test_cases:
        actual = run_prompt(prompt, case["input"])
        results.append({
            "input": case["input"],
            "expected": case["expected_output"],
            "actual": actual,
            "match": actual == case["expected_output"],
        })
    
    return {
        "accuracy": sum(r["match"] for r in results) / len(results),
        "failures": [r for r in results if not r["match"]],
        "total_cases": len(results),
    }

baseline = run_eval(prompt_v1, test_cases)
print(f"Baseline: {baseline['accuracy']:.1%} accuracy ({len(baseline['failures'])} fallos)")

Paso 4: Itera con hipótesis

No cambies el prompt al azar. Cada cambio debe tener una hipótesis:

  • "Los fallos en categoría 'urgente' ocurren porque el prompt no define bien qué hace urgente"
  • "El sistema genera respuestas demasiado largas porque no hay instrucción de longitud"
  • "La clasificación de emails en inglés falla porque el prompt está solo en español"

Luego cambias exactamente eso, y solo eso. Si cambias tres cosas a la vez y mejora, no sabes cuál fue la responsable.

# Hipótesis: añadir ejemplos few-shot mejora la clasificación de casos de urgencia
prompt_v2 = prompt_v1 + """
EJEMPLOS DE CASOS URGENTES:
- "necesito hablar con alguien AHORA" → urgente
- "es muy urgente" → urgente  
- "ya no puedo esperar más" → urgente
"""

result_v2 = run_eval(prompt_v2, test_cases)

# Comparar
print(f"v1: {baseline['accuracy']:.1%} | v2: {result_v2['accuracy']:.1%}")
print(f"Mejora: {result_v2['accuracy'] - baseline['accuracy']:+.1%}")

# Analizar si mejoró en urgentes sin empeorar en otros

Paso 5: Versiona los prompts

Los prompts son código. Necesitan control de versiones.

Opción simple: archivo de texto en git

prompts/
  clasificador_emails/
    v1.txt  (commit: "initial version")
    v2.txt  (commit: "add few-shot examples for urgent cases")
    v3.txt  (commit: "fix false positives in billing category")

Opción avanzada: Promptfoo o Braintrust Herramientas diseñadas para versioning + evals automáticas de prompts. Integración con CI/CD: cada pull request ejecuta las evals y bloquea si el accuracy cae.

# promptfoo.yaml
prompts:
  - file://prompts/clasificador_v2.txt

providers:
  - openai:gpt-4o-mini
  - anthropic:claude-haiku-3-5

tests:
  - vars:
      input: "¿Cómo cancelo mi suscripción?"
    assert:
      - type: equals
        value: "facturacion"
        
  - vars:
      input: "necesito hablar con alguien AHORA"
    assert:
      - type: equals
        value: "urgente"

Cuándo parar de iterar

Criterio de parada claro:

  • Accuracy por encima del umbral definido por el equipo (ej: >90%)
  • Todos los casos edge conocidos en producción pasan
  • El prompt ha sido estable durante 2 semanas sin regresiones

Si llevas muchas iteraciones y no mejoras, el problema puede no ser el prompt — puede ser el modelo, la tarea mal definida, o datos de entrenamiento del dataset de eval inconsistentes.


Recursos relacionados:

Pon en práctica lo que has aprendido

Generador de Eval Set

Genera los casos de prueba para tu dataset de evaluación.

Abrir herramienta gratuita →

Recibe lo mejor de Contextología

Diseño de contexto, agentes y workflows de IA directamente en tu correo.