Módulo 5: ReAct, Self-Consistency y Patrones Avanzados
5. Meta-Prompting y Self-Refine
Descripción
Las técnicas anteriores (ReAct, Self-Consistency, ToT) se enfocan en cómo el modelo razona para resolver un problema. Meta-prompting y Self-Refine abordan otro problema fundamental: ¿cómo mejoramos los prompts y las respuestas de forma sistemática?
Meta-prompting usa un LLM para generar, optimizar o evaluar prompts para otra tarea. Es como contratar a un experto en comunicación para que escriba las instrucciones de tu sistema.
Self-Refine (Madaan et al., 2023) implementa un ciclo iterativo: el modelo genera una respuesta, luego la critica desde la perspectiva del problema, y finalmente la mejora. Es el equivalente de que un estudiante escriba un ensayo, lo revise con ojos críticos, y lo reescriba con esa perspectiva.
Meta-Prompting: El LLM que Escribe Prompts
La Intuición
Escribir buenos prompts es difícil. Requiere entender:
- Cómo se comporta el modelo ante ciertos patrones de lenguaje
- Qué nivel de detalle es necesario
- Qué ejemplos son más útiles
- Cómo formular la salida esperada
¿Y si en lugar de hacerlo manualmente, le pedimos al propio LLM que diseñe el prompt?
Implementación Base
from openai import OpenAI
import json
client = OpenAI()
def meta_prompt_generator(
tarea: str,
contexto: str = "",
formato_salida: str = "",
restricciones: list[str] = None,
ejemplos: list[dict] = None
) -> str:
"""
Genera un prompt optimizado para una tarea específica.
Args:
tarea: Descripción de la tarea (ej: "clasificar sentimiento de tweets")
contexto: Contexto adicional sobre el dominio o usuarios
formato_salida: Cómo debe formatearse la salida
restricciones: Lista de restricciones o requisitos
ejemplos: Ejemplos de input/output deseados
Returns:
Un prompt optimizado para la tarea
"""
restricciones_str = "\n".join([f"- {r}" for r in (restricciones or [])])
ejemplos_str = ""
if ejemplos:
ejemplos_str = "\nEjemplos de input/output deseados:\n"
for ej in ejemplos:
ejemplos_str += f"Input: {ej.get('input', '')}\nOutput: {ej.get('output', '')}\n\n"
meta_prompt = f"""Eres un experto en prompt engineering. Tu tarea es crear el prompt ÓPTIMO para el siguiente caso de uso.
TAREA A RESOLVER: {tarea}
CONTEXTO ADICIONAL:
{contexto if contexto else "Ninguno"}
FORMATO DE SALIDA ESPERADO:
{formato_salida if formato_salida else "No especificado"}
RESTRICCIONES/REQUISITOS:
{restricciones_str if restricciones_str else "Ninguna"}
{ejemplos_str}
Crea un prompt que:
1. Sea claro y específico sobre la tarea
2. Incluya formato de salida explícito
3. Anticipe y maneje edge cases
4. Use el nivel correcto de detalle (ni muy vago ni sobrespecificado)
5. Si aplica, incluya 1-2 ejemplos (few-shot)
IMPORTANTE: Devuelve SOLO el prompt, sin explicaciones ni metadatos adicionales.
El prompt debe estar listo para ser usado directamente."""
response = client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": meta_prompt}],
temperature=0.3,
max_tokens=800
)
return response.choices[0].message.content.strip()
# Ejemplo de uso:
if __name__ == "__main__":
prompt_generado = meta_prompt_generator(
tarea="Clasificar el sentimiento de reviews de restaurantes",
contexto="Los reviews pueden mezclar aspectos positivos y negativos. Son en español.",
formato_salida="JSON con: sentiment (POSITIVO/NEGATIVO/MIXTO/NEUTRAL), score (0-1), aspecto_principal",
restricciones=[
"Considerar tanto comida como servicio y ambiente",
"Detectar sarcasmo e ironía",
"Manejar reviews muy cortos (1-2 palabras)"
],
ejemplos=[
{"input": "La comida estaba buenísima pero tardaron 45 minutos en traerla.",
"output": '{"sentiment": "MIXTO", "score": 0.5, "aspecto_principal": "comida+servicio"}'},
{"input": "Normalito.",
"output": '{"sentiment": "NEUTRAL", "score": 0.5, "aspecto_principal": "general"}'}
]
)
print("Prompt generado por meta-prompting:")
print("=" * 50)
print(prompt_generado)
Meta-Prompting Avanzado: Múltiples Candidatos
def meta_prompt_con_evaluacion(
tarea: str,
n_candidatos: int = 3,
golden_set: list[dict] = None
) -> dict:
"""
Genera N prompts candidatos y evalúa cuál es el mejor
usando un golden set de ejemplos con respuestas correctas.
Args:
tarea: La tarea a resolver
n_candidatos: Número de prompts candidatos a generar
golden_set: Lista de {"input": ..., "expected": ...}
Returns:
dict con el mejor prompt y sus métricas
"""
# Paso 1: Generar N candidatos con diferentes estilos
estilos = [
"muy conciso y directo",
"detallado con ejemplos few-shot",
"con instrucciones de razonamiento paso a paso"
]
candidatos = []
for i, estilo in enumerate(estilos[:n_candidatos]):
meta = f"""Crea un prompt para esta tarea: {tarea}
El prompt debe ser: {estilo}
Devuelve SOLO el prompt."""
resp = client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": meta}],
temperature=0.4,
max_tokens=500
)
candidatos.append({
"estilo": estilo,
"prompt": resp.choices[0].message.content.strip()
})
# Si no hay golden set, retornar todos los candidatos
if not golden_set:
return {"candidatos": candidatos, "mejor": candidatos[0], "sin_evaluacion": True}
# Paso 2: Evaluar cada candidato en el golden set
for candidato in candidatos:
aciertos = 0
for ejemplo in golden_set:
# Usar el prompt candidato para resolver el ejemplo
resp = client.chat.completions.create(
model="gpt-4o-mini",
messages=[{
"role": "user",
"content": f"{candidato['prompt']}\n\nInput: {ejemplo['input']}"
}],
temperature=0,
max_tokens=200
)
prediccion = resp.choices[0].message.content.strip()
# Evaluación simplificada: comparar si expected está en prediccion
if ejemplo.get("expected", "").lower() in prediccion.lower():
aciertos += 1
candidato["accuracy"] = aciertos / len(golden_set)
candidato["aciertos"] = aciertos
# Paso 3: Seleccionar el mejor
mejor = max(candidatos, key=lambda c: c["accuracy"])
return {
"mejor_prompt": mejor["prompt"],
"mejor_accuracy": mejor["accuracy"],
"todos_candidatos": candidatos
}
Self-Refine: Generar → Criticar → Mejorar
El Ciclo de Refinamiento
┌─────────────────────────────────────────────────────┐
│ SELF-REFINE LOOP │
│ │
│ Input → [GENERATE] → Respuesta inicial │
│ ↓ │
│ [CRITIQUE] → "Problemas: X, Y, Z" │
│ ↓ │
│ [REFINE] → Respuesta mejorada │
│ ↓ │
│ ¿Suficiente? ─── SÍ ──→ Output final │
│ │ │
│ NO │
│ └──→ [CRITIQUE] → ... │
└─────────────────────────────────────────────────────┘
Implementación Completa
from dataclasses import dataclass
from typing import Callable, Optional
@dataclass
class IteracionRefine:
"""Registra una iteración del ciclo self-refine."""
numero: int
respuesta: str
critica: str
score_antes: float
score_despues: float
def criticar_respuesta(
problema: str,
respuesta: str,
criterios: list[str] = None
) -> dict:
"""
Critica una respuesta identificando problemas y áreas de mejora.
Args:
problema: El problema original
respuesta: La respuesta a criticar
criterios: Lista de criterios específicos para evaluar
Returns:
dict con 'critica', 'score', 'problemas', 'puede_mejorar'
"""
criterios_default = [
"precisión factual",
"completitud (¿responde todo lo pedido?)",
"claridad y estructura",
"relevancia al problema"
]
criterios_usados = criterios or criterios_default
criterios_str = "\n".join([f"- {c}" for c in criterios_usados])
prompt = f"""Problema original: {problema}
Respuesta a evaluar:
{respuesta}
Evalúa esta respuesta según estos criterios:
{criterios_str}
Responde en JSON:
{{
"score": 0.0-1.0,
"problemas": ["problema1", "problema2", ...],
"fortalezas": ["fortaleza1", ...],
"mejoras_concretas": ["mejora específica 1", "mejora específica 2", ...],
"puede_mejorar": true/false
}}
Sé específico en las mejoras. Si score >= 0.85, puede_mejorar = false."""
response = client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": prompt}],
temperature=0,
max_tokens=400,
response_format={"type": "json_object"}
)
try:
critica = json.loads(response.choices[0].message.content)
return {
"score": float(critica.get("score", 0.5)),
"problemas": critica.get("problemas", []),
"fortalezas": critica.get("fortalezas", []),
"mejoras": critica.get("mejoras_concretas", []),
"puede_mejorar": bool(critica.get("puede_mejorar", True))
}
except (json.JSONDecodeError, KeyError) as e:
return {
"score": 0.5,
"problemas": ["Error al parsear crítica"],
"fortalezas": [],
"mejoras": ["Reformatear la respuesta"],
"puede_mejorar": True
}
def refinar_respuesta(
problema: str,
respuesta_actual: str,
critica: dict
) -> str:
"""
Mejora una respuesta basándose en una crítica específica.
"""
mejoras_str = "\n".join([f"- {m}" for m in critica.get("mejoras", [])])
problemas_str = "\n".join([f"- {p}" for p in critica.get("problemas", [])])
prompt = f"""Problema original: {problema}
Tu respuesta anterior:
{respuesta_actual}
Problemas identificados:
{problemas_str if problemas_str else "Ninguno crítico"}
Mejoras específicas requeridas:
{mejoras_str if mejoras_str else "Mejorar claridad general"}
Genera una respuesta MEJORADA que:
1. Corrija todos los problemas identificados
2. Implemente las mejoras sugeridas
3. Mantenga las fortalezas de la respuesta anterior
4. Sea más completa y precisa
Respuesta mejorada:"""
response = client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": prompt}],
temperature=0.3,
max_tokens=600
)
return response.choices[0].message.content.strip()
def self_refine(
problema: str,
max_iteraciones: int = 3,
umbral_score: float = 0.85,
criterios: list[str] = None,
verbose: bool = True
) -> dict:
"""
Ciclo completo de Self-Refine.
Args:
problema: El problema a resolver
max_iteraciones: Máximo número de ciclos refinar
umbral_score: Si score >= umbral, parar (ya es suficientemente bueno)
criterios: Criterios de evaluación específicos
verbose: Si True, imprime el proceso
Returns:
dict con respuesta_final, iteraciones, score_final
"""
# Paso 1: Generar respuesta inicial
resp_inicial = client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": f"Resuelve este problema de forma detallada:\n\n{problema}"}],
temperature=0.3,
max_tokens=600
).choices[0].message.content.strip()
respuesta_actual = resp_inicial
iteraciones = []
if verbose:
print(f"[Iteración 0 - Inicial]\n{respuesta_actual[:200]}...\n")
# Ciclo de refinamiento
for i in range(max_iteraciones):
# Criticar la respuesta actual
critica = criticar_respuesta(problema, respuesta_actual, criterios)
score_actual = critica["score"]
if verbose:
print(f"[Crítica iteración {i+1}]")
print(f" Score: {score_actual:.2f}")
print(f" Problemas: {critica['problemas']}")
print(f" Mejoras: {critica['mejoras'][:2]}")
# Si ya es suficientemente bueno, parar
if not critica["puede_mejorar"] or score_actual >= umbral_score:
if verbose:
print(f" ✓ Score suficientemente alto ({score_actual:.2f}). Parando.")
iteraciones.append(IteracionRefine(
numero=i+1,
respuesta=respuesta_actual,
critica=str(critica),
score_antes=score_actual,
score_despues=score_actual
))
break
# Refinar la respuesta
respuesta_mejorada = refinar_respuesta(problema, respuesta_actual, critica)
critica_post = criticar_respuesta(problema, respuesta_mejorada, criterios)
score_nuevo = critica_post["score"]
if verbose:
print(f"[Refinada iteración {i+1}]")
print(f" Score: {score_actual:.2f} → {score_nuevo:.2f}")
print(f" {respuesta_mejorada[:150]}...\n")
iteraciones.append(IteracionRefine(
numero=i+1,
respuesta=respuesta_mejorada,
critica=str(critica),
score_antes=score_actual,
score_despues=score_nuevo
))
# Si el refinamiento empeoró, quedarse con la versión anterior
if score_nuevo < score_actual - 0.05:
if verbose:
print(" ⚠ El refinamiento empeoró la respuesta. Revirtiendo.")
break
respuesta_actual = respuesta_mejorada
return {
"respuesta_final": respuesta_actual,
"respuesta_inicial": resp_inicial,
"iteraciones": iteraciones,
"n_iteraciones": len(iteraciones),
"score_final": critica["score"] if iteraciones else 0.0,
"mejora_total": critica["score"] - 0.5 # vs baseline asumido 0.5
}
Meta-Prompting + Self-Refine: La Combinación
def meta_refine_pipeline(
tarea_descripcion: str,
inputs_prueba: list[str],
n_iteraciones_meta: int = 2
) -> dict:
"""
Pipeline completo: generar prompt con meta-prompting,
luego evaluar y refinar el prompt usando self-refine.
"""
# Paso 1: Generar prompt inicial con meta-prompting
prompt_actual = meta_prompt_generator(tarea=tarea_descripcion)
print(f"Prompt inicial:\n{prompt_actual[:200]}...\n")
historial_mejoras = []
for iteracion in range(n_iteraciones_meta):
# Paso 2: Evaluar el prompt en inputs de prueba
resultados = []
for input_prueba in inputs_prueba[:3]: # Limitar a 3 para ahorrar llamadas
resp = client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": f"{prompt_actual}\n\nInput: {input_prueba}"}],
temperature=0,
max_tokens=300
).choices[0].message.content
resultados.append({"input": input_prueba, "output": resp})
# Paso 3: Usar meta-prompting para mejorar el prompt
critica_prompt = f"""Tarea: {tarea_descripcion}
Prompt actual:
{prompt_actual}
Resultados de usar este prompt en ejemplos reales:
{json.dumps(resultados, ensure_ascii=False, indent=2)}
Identifica problemas en el prompt y genera una versión MEJORADA.
¿El prompt produce resultados consistentes? ¿El formato es correcto? ¿Hay ambigüedad?
Devuelve SOLO el prompt mejorado, sin explicaciones."""
resp_mejora = client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": critica_prompt}],
temperature=0.3,
max_tokens=600
)
prompt_mejorado = resp_mejora.choices[0].message.content.strip()
historial_mejoras.append({
"iteracion": iteracion + 1,
"prompt_anterior": prompt_actual,
"prompt_nuevo": prompt_mejorado,
"resultados_evaluacion": resultados
})
prompt_actual = prompt_mejorado
print(f"Iteración {iteracion+1}: Prompt mejorado")
return {
"prompt_final": prompt_actual,
"historial": historial_mejoras,
"n_iteraciones": n_iteraciones_meta
}
Casos de Uso Reales
Caso 1: Mejorar prompts de extracción de entidades
tarea = "Extraer entidades nombradas (personas, organizaciones, lugares, fechas) de texto en español"
texts = [
"El CEO de Google, Sundar Pichai, visitó Madrid el 15 de marzo de 2026.",
"La reunión entre Apple y Samsung fue en Seúl ayer.",
"Elon Musk fundó SpaceX en 2002 en California."
]
resultado = meta_refine_pipeline(
tarea_descripcion=tarea,
inputs_prueba=texts,
n_iteraciones_meta=2
)
print(f"\nPrompt final optimizado:\n{resultado['prompt_final']}")
Caso 2: Self-Refine para Código
problema_codigo = """
Escribe una función Python que:
1. Reciba una lista de diccionarios con 'nombre', 'edad', 'salario'
2. Filtre empleados con salario > 50000 y edad > 30
3. Retorne la lista ordenada por salario descendente
4. Maneje el caso de lista vacía y claves faltantes
"""
resultado = self_refine(
problema=problema_codigo,
max_iteraciones=2,
umbral_score=0.9,
criterios=[
"correctitud del código (sin bugs)",
"manejo de casos edge (lista vacía, claves faltantes)",
"eficiencia (evitar operaciones innecesarias)",
"legibilidad y documentación"
],
verbose=True
)
print(f"\n=== Código Final ===")
print(resultado["respuesta_final"])
print(f"\nMejora de score: +{resultado['mejora_total']:.2f}")
Caso 3: Self-Refine para Contenido Educativo
problema_educativo = """
Explica el concepto de 'transformer architecture' para alguien que sabe Python
básico pero nunca ha trabajado con ML/DL. Incluye una analogía y ejemplo de código simple.
"""
resultado = self_refine(
problema=problema_educativo,
max_iteraciones=3,
criterios=[
"claridad para audiencia no técnica en ML",
"analogía comprensible y precisa",
"código ejecutable y simple",
"progresión lógica del concepto"
],
verbose=True
)
Integración con Anthropic
import anthropic
client_anthropic = anthropic.Anthropic()
def self_refine_claude(problema: str, max_iter: int = 2) -> str:
"""
Self-Refine usando Claude. Claude tiende a ser más autocrítico.
"""
# Generación inicial
mensaje_inicial = client_anthropic.messages.create(
model="claude-3-5-haiku-20241022",
max_tokens=600,
messages=[{"role": "user", "content": f"Resuelve: {problema}"}]
)
respuesta = mensaje_inicial.content[0].text
for i in range(max_iter):
# Crítica
critica_msg = client_anthropic.messages.create(
model="claude-3-5-haiku-20241022",
max_tokens=300,
messages=[{
"role": "user",
"content": f"""Problema: {problema}
Tu respuesta: {respuesta}
Identifica 2-3 mejoras específicas que harían esta respuesta más precisa y completa.
Si ya es excelente (nada significativo que mejorar), di solo "ÓPTIMO"."""
}]
)
critica = critica_msg.content[0].text
if "ÓPTIMO" in critica.upper():
break
# Refinamiento
refine_msg = client_anthropic.messages.create(
model="claude-3-5-haiku-20241022",
max_tokens=600,
messages=[{
"role": "user",
"content": f"""Problema: {problema}
Respuesta anterior: {respuesta}
Crítica: {critica}
Genera una versión mejorada que incorpore las críticas."""
}]
)
respuesta = refine_msg.content[0].text
return respuesta
Troubleshooting
Problema 1: El meta-prompt genera prompts vagos o genéricos
Síntoma: El prompt generado podría aplicar a cualquier tarea, no es específico.
Causas:
- Descripción de tarea muy ambigua
- No se proveen restricciones ni ejemplos
Solución:
# MAL: descripción vaga
prompt_malo = meta_prompt_generator("clasificar texto")
# BIEN: descripción específica con contexto
prompt_bueno = meta_prompt_generator(
tarea="Clasificar tickets de soporte técnico en: BUG_CRITICO, BUG_MENOR, FEATURE_REQUEST, PREGUNTA, DUPLICADO",
contexto="Los tickets son de usuarios de software B2B, escritos en español o inglés mezclado (Spanglish)",
formato_salida="JSON: {categoria: str, confianza: float 0-1, razon: str corta}",
restricciones=[
"BUG_CRITICO: solo si menciona pérdida de datos o caída del sistema",
"Detectar si hay urgencia implícita en el lenguaje",
"Si es ambiguo, preferir PREGUNTA sobre otras categorías"
]
)
Problema 2: Self-Refine no mejora (la crítica es demasiado permisiva)
Síntoma: El crítico siempre da score > 0.8 y dice "puede_mejorar = false" aunque la respuesta sea mediocre.
Causas:
- El crítico usa el mismo modelo que es "demasiado amable consigo mismo"
- Los criterios de evaluación son vagos
Solución:
# Usar un crítico más estricto con criterios específicos y ejemplos de qué cuenta como "mal"
critica_estricta = f"""Evalúa CRÍTICAMENTE esta respuesta.
Sé exigente: busca activamente errores, omisiones e imprecisiones.
Criterio: Una respuesta de 0.9+ debe cumplir TODO:
- Sin ningún error factual
- Completamente responde TODAS las partes de la pregunta
- Clara y bien estructurada
- Sin redundancias innecesarias
Problema: {problema}
Respuesta: {respuesta}
Si hay CUALQUIER problema, incluso pequeño, score debe ser < 0.85."""
Problema 3: Cada iteración de Self-Refine empeora la respuesta
Síntoma: La respuesta se va haciendo más larga y confusa con cada iteración.
Solución:
def self_refine_con_rollback(problema: str, max_iter: int = 3) -> dict:
"""Self-Refine que revierte si una iteración empeora."""
respuesta_actual = generar_inicial(problema)
score_actual = criticar_respuesta(problema, respuesta_actual)["score"]
mejor = {"respuesta": respuesta_actual, "score": score_actual}
for _ in range(max_iter):
critica = criticar_respuesta(problema, respuesta_actual)
if not critica["puede_mejorar"]:
break
nueva = refinar_respuesta(problema, respuesta_actual, critica)
nuevo_score = criticar_respuesta(problema, nueva)["score"]
if nuevo_score > mejor["score"]:
mejor = {"respuesta": nueva, "score": nuevo_score}
if nuevo_score < score_actual - 0.05:
# Empeoró: revertir
break
respuesta_actual = nueva
score_actual = nuevo_score
return mejor
def generar_inicial(problema: str) -> str:
return client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": problema}],
temperature=0.3, max_tokens=500
).choices[0].message.content
Ejercicios
Ejercicio 1: Meta-prompt para un clasificador de spam
Usa meta-prompting para generar un prompt que clasifique emails como SPAM o NO_SPAM. Luego evalúa el prompt generado con 10 ejemplos conocidos. ¿Supera el 85% de accuracy?
Ver solución
emails_test = [
("¡GANASTE $1,000,000! Haz clic aquí para reclamar tu premio.", "SPAM"),
("Reunión del equipo mañana a las 10am.", "NO_SPAM"),
("Oferta especial: Viagra al 90% de descuento", "SPAM"),
("Tu factura de enero está disponible.", "NO_SPAM"),
("Estimado usuario, su cuenta será bloqueada en 24h. Verifique ahora.", "SPAM"),
("¿Puedes revisar el PR que subí ayer?", "NO_SPAM"),
("Crypto trading bot - gana $500 diarios sin esfuerzo", "SPAM"),
("Adjunto el informe trimestral del Q4.", "NO_SPAM"),
("Usted ha sido seleccionado para una oferta exclusiva.", "SPAM"),
("¿Tienes disponibilidad para una llamada el viernes?", "NO_SPAM"),
]
# Generar prompt con meta-prompting
prompt_spam = meta_prompt_generator(
tarea="Clasificar emails como SPAM o NO_SPAM",
contexto="Emails pueden ser en español o inglés",
formato_salida="Solo responde: SPAM o NO_SPAM",
restricciones=[
"Detectar urgencia artificial ('AHORA', '24h', 'INMEDIATAMENTE')",
"Detectar promesas de dinero fácil",
"Ignorar emails de trabajo legítimos"
]
)
# Evaluar
aciertos = 0
for email, esperado in emails_test:
resp = client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": f"{prompt_spam}\n\nEmail: {email}"}],
temperature=0
).choices[0].message.content.strip().upper()
if esperado in resp:
aciertos += 1
print(f"Accuracy del prompt generado: {aciertos/len(emails_test):.0%}")
Ejercicio 2: Self-Refine para un ensayo
Aplica Self-Refine para mejorar un ensayo corto (200 palabras) sobre "el impacto de la IA en el empleo". Define 3 criterios de calidad relevantes para un ensayo académico. ¿En cuántas iteraciones converge?
Ver solución
problema_ensayo = """
Escribe un ensayo corto (200 palabras) sobre el impacto de la IA en el empleo.
Debe incluir: argumentos a favor y en contra, datos si los conoces, y una conclusión equilibrada.
"""
criterios_ensayo = [
"Argumentación balanceada: presenta ambas perspectivas con igual peso",
"Datos y ejemplos concretos que respaldan las afirmaciones",
"Estructura clara: introducción, desarrollo con datos, conclusión",
"Conclusión matizada: no excesivamente optimista ni alarmista"
]
resultado = self_refine(
problema=problema_ensayo,
max_iteraciones=3,
umbral_score=0.88,
criterios=criterios_ensayo,
verbose=True
)
print(f"\n=== Ensayo Final (iter {resultado['n_iteraciones']}) ===")
print(resultado["respuesta_final"])
print(f"\nScore final: {resultado['score_final']:.2f}")
Ejercicio 3: Comparar Meta-Prompting Manual vs Automático
Escribe manualmente el mejor prompt que puedas para "resumir artículos de noticias en 3 puntos clave". Luego genera uno con meta-prompting. Evalúa ambos en 5 artículos reales. ¿Cuál tiene mejor calidad percibida?
Ver solución
# Prompt manual
prompt_manual = """Resume el siguiente artículo de noticias en exactamente 3 puntos clave.
Formato:
1. [Hecho principal]
2. [Contexto o causa]
3. [Impacto o consecuencia]
Sé conciso (máx 20 palabras por punto)."""
# Prompt con meta-prompting
prompt_auto = meta_prompt_generator(
tarea="Resumir artículos de noticias en 3 puntos clave",
formato_salida="Lista numerada con 3 puntos, máx 20 palabras cada uno",
restricciones=["No incluir opiniones del autor", "Incluir datos numéricos si están disponibles"]
)
articulo_test = """La empresa Tesla anunció hoy la construcción de una nueva Gigafactory
en España, específicamente en Valencia, con una inversión de 4 mil millones de euros.
La planta dará empleo a 3,000 trabajadores directos y se espera que empiece a operar
en 2028. El Gobierno español ha ofrecido incentivos fiscales de 500 millones de euros."""
def evaluar_resumen(prompt: str, articulo: str) -> str:
return client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": f"{prompt}\n\nArtículo: {articulo}"}],
temperature=0
).choices[0].message.content
print("=== MANUAL ===")
print(evaluar_resumen(prompt_manual, articulo_test))
print("\n=== META-PROMPTING ===")
print(evaluar_resumen(prompt_auto, articulo_test))
Resumen
- Meta-prompting: Usar un LLM para generar el prompt óptimo de otra tarea. Útil cuando el espacio de prompts es grande o el dominio es complejo.
- Meta-prompting + evaluación: Generar N candidatos, evaluar en golden set, elegir el mejor. Más robusto que un solo candidato.
- Self-Refine: Ciclo iterativo Generar → Criticar → Mejorar. 1-3 iteraciones suelen ser suficientes antes de rendimientos decrecientes.
- Crítico estricto: La clave del Self-Refine es tener un crítico que sea genuinamente exigente. Un crítico demasiado permisivo no mejorará nada.
- Rollback: Si una iteración empeora la respuesta, revertir a la mejor versión hasta ahora.
- Cuándo usar: Meta-prompting para optimización de sistemas, Self-Refine para respuestas críticas que deben tener alta calidad.
Recursos adicionales
- Self-Refine: Iterative Refinement with Self-Feedback (Madaan et al., 2023) - Paper original
- Large Language Models as Optimizers (Yang et al., 2023) - Prompts como optimización
- Automatic Prompt Engineer (APE) (Zhou et al., 2022) - Meta-prompting sistemático
- Constitutional AI (Anthropic) - Self-critique para alineación
- OpenAI Prompt Engineering Guide
- Reflexion: Language Agents with Verbal Reinforcement - Variante de self-refine