Módulo 5: ReAct, Self-Consistency y Patrones Avanzados
7. Decision Framework: Qué Técnica Usar
Descripción
Conocer las técnicas es solo la mitad del trabajo. La otra mitad es saber cuándo aplicar cada una. Este framework de decisión te da un proceso sistemático para elegir la técnica correcta basándote en las características de tu tarea, tus restricciones de presupuesto y los requisitos de calidad.
El Árbol de Decisión Principal
¿Tu tarea necesita datos en tiempo real o herramientas externas?
├── SÍ → ReAct (+ Structured Output si el output debe ser parseable)
│ └── ¿Accuracy crítica? → ReAct + verificación adicional
│
└── NO → ¿Es una tarea de razonamiento (math, lógica, multi-paso)?
├── SÍ → ¿Accuracy crítica (errores tienen alto costo)?
│ ├── SÍ → CoT + Self-Consistency (N=3-5)
│ └── NO → CoT solo (suficiente en la mayoría de casos)
│
└── NO → ¿Hay múltiples estrategias válidas posibles?
├── SÍ → Tree-of-Thought (solo si CoT falla sistemáticamente)
└── NO → ¿Es clasificación, extracción, o respuesta directa?
├── SÍ → Zero-shot o Few-shot
└── NO → ¿Necesitas optimizar el prompt o la calidad de la respuesta?
├── Prompt subóptimo → Meta-prompting
└── Calidad mejorable → Self-Refine
Tabla de Referencia Rápida
| Tarea | Técnica recomendada | Llamadas API | Latencia | Costo relativo |
|---|---|---|---|---|
| Clasificación de texto | Few-shot | 1 | Baja | 1x |
| Extracción de entidades | Zero-shot + JSON mode | 1 | Baja | 1x |
| Traducción | Zero-shot o Few-shot | 1 | Baja | 1x |
| Resumen de texto | Zero-shot | 1 | Baja | 1x |
| Math word problem | CoT | 1 | Media | 1.5x |
| Math crítico (no errores) | CoT + Self-Consistency N=5 | 5 | Alta | 5x |
| QA con búsqueda web | ReAct | 3-8 | Alta | 5-10x |
| QA con base de datos | ReAct | 2-5 | Media-Alta | 3-7x |
| Planificación con alternativas | Tree-of-Thought | 10-30 | Muy alta | 10-30x |
| Código con alta calidad | Self-Refine (2-3 iter) | 4-8 | Alta | 4-8x |
| Optimizar prompt en producción | Meta-prompting + evaluación | 10-20 | Alta | 10-20x |
Framework Detallado por Dimensiones
Dimensión 1: Tipo de tarea
from openai import OpenAI
from enum import Enum
from dataclasses import dataclass
from typing import Optional
client = OpenAI()
class TipoTarea(Enum):
CLASIFICACION = "clasificacion" # Asignar a categoría
EXTRACCION = "extraccion" # Extraer información estructurada
RAZONAMIENTO_MATH = "math" # Operaciones numéricas/lógica
RAZONAMIENTO_LOGICO = "logico" # Deducciones, inferencias
GENERACION_TEXTO = "generacion" # Escritura creativa o técnica
QA_FACTUAL = "qa_factual" # Preguntas sobre hechos conocidos
QA_TIEMPO_REAL = "qa_tiempo_real" # Preguntas que necesitan datos actuales
PLANIFICACION = "planificacion" # Diseñar estrategias, roadmaps
CODIGO = "codigo" # Escribir o revisar código
MULTIMODAL = "multimodal" # Combina varios tipos
@dataclass
class AnalisisTarea:
tipo: TipoTarea
accuracy_critica: bool # ¿Los errores tienen alto costo?
necesita_datos_externos: bool # ¿Necesita APIs, base de datos?
output_estructurado: bool # ¿El output debe ser JSON/parseable?
presupuesto_llamadas: int # Máximo de llamadas API permitidas
latencia_maxima_seg: float # Máximo tiempo de respuesta aceptable
def analizar_tarea_automatico(descripcion: str) -> AnalisisTarea:
"""
Analiza automáticamente una descripción de tarea para clasificarla.
"""
prompt = f"""Analiza esta tarea y clasifícala. Responde en JSON:
Tarea: {descripcion}
{{
"tipo": "clasificacion|extraccion|math|logico|generacion|qa_factual|qa_tiempo_real|planificacion|codigo",
"accuracy_critica": true/false,
"necesita_datos_externos": true/false,
"output_estructurado": true/false,
"presupuesto_llamadas": 1-20,
"latencia_maxima_seg": 5-60
}}
Reglas para accuracy_critica: true si errores implican pérdida económica, riesgos legales, o impacto en usuarios.
Reglas para necesita_datos_externos: true si necesita precios, datos en tiempo real, consultas a bases de datos."""
response = client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": prompt}],
temperature=0,
response_format={"type": "json_object"}
)
import json
datos = json.loads(response.choices[0].message.content)
tipo_map = {
"clasificacion": TipoTarea.CLASIFICACION,
"extraccion": TipoTarea.EXTRACCION,
"math": TipoTarea.RAZONAMIENTO_MATH,
"logico": TipoTarea.RAZONAMIENTO_LOGICO,
"generacion": TipoTarea.GENERACION_TEXTO,
"qa_factual": TipoTarea.QA_FACTUAL,
"qa_tiempo_real": TipoTarea.QA_TIEMPO_REAL,
"planificacion": TipoTarea.PLANIFICACION,
"codigo": TipoTarea.CODIGO
}
return AnalisisTarea(
tipo=tipo_map.get(datos.get("tipo", "logico"), TipoTarea.RAZONAMIENTO_LOGICO),
accuracy_critica=datos.get("accuracy_critica", False),
necesita_datos_externos=datos.get("necesita_datos_externos", False),
output_estructurado=datos.get("output_estructurado", False),
presupuesto_llamadas=datos.get("presupuesto_llamadas", 5),
latencia_maxima_seg=datos.get("latencia_maxima_seg", 30.0)
)
La Función de Selección de Técnica
@dataclass
class RecomendacionTecnica:
tecnica_principal: str
tecnica_secundaria: Optional[str]
parametros_sugeridos: dict
justificacion: str
llamadas_estimadas: int
costo_relativo: float
alternativas: list[str]
def seleccionar_tecnica(analisis: AnalisisTarea) -> RecomendacionTecnica:
"""
Selecciona la técnica óptima basándose en el análisis de la tarea.
Args:
analisis: Resultado de analizar_tarea_automatico o creado manualmente
Returns:
Recomendación con técnica, parámetros y justificación
"""
tipo = analisis.tipo
accuracy = analisis.accuracy_critica
externos = analisis.necesita_datos_externos
estructurado = analisis.output_estructurado
budget = analisis.presupuesto_llamadas
# Regla 1: Datos externos → ReAct
if externos:
if accuracy and budget >= 8:
return RecomendacionTecnica(
tecnica_principal="ReAct",
tecnica_secundaria="verificacion_adicional",
parametros_sugeridos={"max_steps": 6, "output_format": "json" if estructurado else "text"},
justificacion="Necesita datos externos. Con accuracy crítica, añadir verificación del resultado.",
llamadas_estimadas=8,
costo_relativo=8.0,
alternativas=["react_con_fallback"]
)
return RecomendacionTecnica(
tecnica_principal="ReAct",
tecnica_secundaria="structured_output" if estructurado else None,
parametros_sugeridos={"max_steps": 5},
justificacion="Datos externos requieren ReAct para herramientas.",
llamadas_estimadas=5,
costo_relativo=5.0,
alternativas=["react_simplificado"]
)
# Regla 2: Math/Lógica con accuracy crítica → CoT + SC
if tipo in [TipoTarea.RAZONAMIENTO_MATH, TipoTarea.RAZONAMIENTO_LOGICO]:
if accuracy and budget >= 5:
n = min(5, budget)
return RecomendacionTecnica(
tecnica_principal="CoT + Self-Consistency",
tecnica_secundaria=None,
parametros_sugeridos={"n": n, "temperatura": 0.7},
justificacion=f"Razonamiento con accuracy crítica. N={n} muestras para consenso.",
llamadas_estimadas=n,
costo_relativo=float(n),
alternativas=["cot_solo", "react" if accuracy else None]
)
return RecomendacionTecnica(
tecnica_principal="CoT",
tecnica_secundaria=None,
parametros_sugeridos={"temperatura": 0},
justificacion="Razonamiento matemático/lógico. CoT con step-by-step.",
llamadas_estimadas=1,
costo_relativo=1.5,
alternativas=["cot_sc_n3"]
)
# Regla 3: Planificación → ToT
if tipo == TipoTarea.PLANIFICACION:
if budget >= 15:
return RecomendacionTecnica(
tecnica_principal="Tree-of-Thought",
tecnica_secundaria="Self-Consistency" if accuracy else None,
parametros_sugeridos={"breadth": 3, "depth": 2},
justificacion="Planificación tiene múltiples estrategias. ToT explora alternativas.",
llamadas_estimadas=15,
costo_relativo=15.0,
alternativas=["cot", "tot_simplificado"]
)
return RecomendacionTecnica(
tecnica_principal="ToT simplificado",
tecnica_secundaria=None,
parametros_sugeridos={"breadth": 2, "depth": 1},
justificacion="Budget limitado. ToT simplificado: 2 enfoques, 1 nivel.",
llamadas_estimadas=6,
costo_relativo=6.0,
alternativas=["cot"]
)
# Regla 4: Código → Self-Refine
if tipo == TipoTarea.CODIGO:
iter_max = 2 if budget >= 4 else 1
return RecomendacionTecnica(
tecnica_principal="Self-Refine",
tecnica_secundaria=None,
parametros_sugeridos={"max_iteraciones": iter_max, "criterios": ["correctitud", "eficiencia", "legibilidad"]},
justificacion=f"Código requiere revisión iterativa. {iter_max} iteraciones de refine.",
llamadas_estimadas=iter_max * 2,
costo_relativo=float(iter_max * 2),
alternativas=["cot_con_tests"]
)
# Regla 5: Clasificación/Extracción → Few-shot/Zero-shot
if tipo in [TipoTarea.CLASIFICACION, TipoTarea.EXTRACCION]:
return RecomendacionTecnica(
tecnica_principal="Few-shot",
tecnica_secundaria="structured_output" if estructurado else None,
parametros_sugeridos={"n_ejemplos": 2 if tipo == TipoTarea.CLASIFICACION else 1, "temperatura": 0},
justificacion="Clasificación/extracción simple no necesita técnicas complejas.",
llamadas_estimadas=1,
costo_relativo=1.2,
alternativas=["zero_shot", "cot_para_casos_ambiguos"]
)
# Regla 6: QA Factual → Zero-shot
if tipo == TipoTarea.QA_FACTUAL:
return RecomendacionTecnica(
tecnica_principal="Zero-shot",
tecnica_secundaria="few_shot" if accuracy else None,
parametros_sugeridos={"temperatura": 0},
justificacion="QA factual con conocimiento del modelo. Zero-shot suficiente.",
llamadas_estimadas=1,
costo_relativo=1.0,
alternativas=["rag" if externos else "cot"]
)
# Default: CoT
return RecomendacionTecnica(
tecnica_principal="CoT",
tecnica_secundaria=None,
parametros_sugeridos={"temperatura": 0},
justificacion="Técnica general de razonamiento aplicable a la mayoría de tareas.",
llamadas_estimadas=1,
costo_relativo=1.5,
alternativas=["few_shot", "zero_shot"]
)
# Función principal para uso fácil:
def recomendar_tecnica(descripcion_tarea: str, verbose: bool = True) -> RecomendacionTecnica:
"""
Dado la descripción de una tarea, recomienda la técnica óptima.
"""
analisis = analizar_tarea_automatico(descripcion_tarea)
recomendacion = seleccionar_tecnica(analisis)
if verbose:
print(f"=== ANÁLISIS DE TAREA ===")
print(f"Tipo: {analisis.tipo.value}")
print(f"Accuracy crítica: {analisis.accuracy_critica}")
print(f"Datos externos: {analisis.necesita_datos_externos}")
print(f"\n=== RECOMENDACIÓN ===")
print(f"Técnica: {recomendacion.tecnica_principal}")
if recomendacion.tecnica_secundaria:
print(f"Combinada con: {recomendacion.tecnica_secundaria}")
print(f"Parámetros: {recomendacion.parametros_sugeridos}")
print(f"Justificación: {recomendacion.justificacion}")
print(f"Llamadas estimadas: {recomendacion.llamadas_estimadas}")
print(f"Costo relativo: {recomendacion.costo_relativo}x")
print(f"Alternativas: {[a for a in recomendacion.alternativas if a]}")
return recomendacion
Análisis Cost vs Accuracy vs Latency
import time
def benchmark_tecnicas_en_tarea(
problema: str,
respuesta_correcta: Optional[str] = None
) -> dict:
"""
Benchmarks múltiples técnicas en un problema específico.
Útil para calibrar qué técnica usar en un caso de uso particular.
"""
resultados = {}
# Zero-shot
t0 = time.time()
resp_zero = client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": problema}],
temperature=0, max_tokens=200
).choices[0].message.content
resultados["zero_shot"] = {
"respuesta": resp_zero[:100],
"latencia": time.time() - t0,
"llamadas": 1,
"costo_relativo": 1.0
}
# CoT
t0 = time.time()
resp_cot = client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": f"{problema}\nPiensa paso a paso."}],
temperature=0, max_tokens=400
).choices[0].message.content
resultados["cot"] = {
"respuesta": resp_cot[:100],
"latencia": time.time() - t0,
"llamadas": 1,
"costo_relativo": 1.5
}
# Self-Consistency N=3
t0 = time.time()
from collections import Counter
import re
def extraer_num(t):
nums = re.findall(r'-?\d+\.?\d*', t)
return nums[-1] if nums else t.strip()[-20:]
resps_sc = []
for _ in range(3):
r = client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": f"{problema}\nPiensa paso a paso. Respuesta: [valor]"}],
temperature=0.7, max_tokens=300
).choices[0].message.content
resps_sc.append(extraer_num(r))
ganadora_sc = Counter(resps_sc).most_common(1)[0][0]
resultados["sc_n3"] = {
"respuesta": ganadora_sc,
"latencia": time.time() - t0,
"llamadas": 3,
"costo_relativo": 3.0
}
# Agregar evaluación si se provee respuesta correcta
if respuesta_correcta:
def es_correcto(pred, correcta):
try:
return abs(float(re.sub(r'[^\d.-]', '', pred)) - float(correcta)) < 0.01
except Exception:
return pred.strip().lower() == correcta.strip().lower()
for tecnica in resultados:
resultados[tecnica]["correcto"] = es_correcto(
resultados[tecnica]["respuesta"], respuesta_correcta
)
return resultados
# Uso en caso real:
problema_test = "Si invierto $1000 al 7% anual durante 10 años con interés compuesto, ¿cuánto tendré?"
resultados = benchmark_tecnicas_en_tarea(problema_test, respuesta_correcta="1967.15")
print("Técnica | Respuesta | Latencia | Llamadas | ¿Correcto?")
print("-" * 75)
for t, datos in resultados.items():
correcto = datos.get("correcto", "N/A")
print(f"{t:<15} | {datos['respuesta'][:15]:<15} | {datos['latencia']:.2f}s | {datos['llamadas']} | {correcto}")
Guías Prácticas por Industria
Fintech / Análisis Financiero
GUIA_FINTECH = {
"calculos_riesgo": {
"tecnica": "CoT + Self-Consistency N=5",
"razon": "Errores en cálculos de riesgo tienen alto impacto económico",
"ejemplo": "Value at Risk, opciones financieras, scoring crediticio"
},
"datos_mercado": {
"tecnica": "ReAct + Structured Output",
"razon": "Precios, tipos de cambio, indices requieren datos en tiempo real",
"ejemplo": "Precios de acciones, volatilidad, correlaciones"
},
"clasificacion_transacciones": {
"tecnica": "Few-shot",
"razon": "Clasificación de fraude/normal no requiere técnicas avanzadas",
"ejemplo": "Detectar transacciones sospechosas por categoría"
},
"reportes_regulatorios": {
"tecnica": "Self-Refine (3 iteraciones)",
"razon": "La calidad del lenguaje y precisión son críticas en reportes regulatorios",
"ejemplo": "Basel III, MIFID II compliance reports"
}
}
Legal / Compliance
GUIA_LEGAL = {
"analisis_contratos": {
"tecnica": "Self-Refine + criterios legales",
"razon": "Precisión y exhaustividad son críticas",
"ejemplo": "Identificar cláusulas problemáticas"
},
"busqueda_jurisprudencia": {
"tecnica": "ReAct",
"razon": "Necesita buscar en bases de datos de casos",
"ejemplo": "Encontrar precedentes relevantes"
},
"clasificacion_documentos": {
"tecnica": "Few-shot con ejemplos legales",
"razon": "Clasificación por tipo de documento es relativamente directa",
"ejemplo": "Distinguir contratos de facturas, resoluciones"
}
}
E-commerce / Customer Support
GUIA_ECOMMERCE = {
"clasificacion_tickets": {
"tecnica": "Zero-shot o Few-shot",
"razon": "Alta velocidad necesaria; clasificación simple",
"ejemplo": "Devolver vs. consulta vs. queja vs. información"
},
"consulta_estado_pedido": {
"tecnica": "ReAct",
"razon": "Requiere consultar el sistema de pedidos",
"ejemplo": "¿Dónde está mi paquete?"
},
"respuestas_complejas": {
"tecnica": "Self-Refine",
"razon": "Respuestas a quejas complejas deben ser empáticas y precisas",
"ejemplo": "Gestión de devoluciones complejas, reclamaciones"
}
}
Diagrama de Flujo Completo con Código
def framework_decision_completo(
descripcion_tarea: str,
budget_usd_por_request: float = 0.005,
latencia_max_seg: float = 30.0,
accuracy_minima: float = 0.80
) -> dict:
"""
Framework de decisión completo que considera presupuesto, latencia y accuracy.
Returns:
Configuración completa: técnica, parámetros, costo estimado, estrategia de fallback
"""
# Clasificar tarea
analisis = analizar_tarea_automatico(descripcion_tarea)
# Costo estimado por técnica (simplificado)
COSTOS_ESTIMADOS = {
"zero_shot": 0.0002,
"few_shot": 0.0003,
"cot": 0.0004,
"cot_sc_n3": 0.0012,
"cot_sc_n5": 0.002,
"react": 0.002,
"tot_simplificado": 0.003,
"self_refine_n2": 0.0008,
"self_refine_n3": 0.0012
}
# Seleccionar recomendación base
recom = seleccionar_tecnica(analisis)
# Verificar si la técnica recomendada cabe en el presupuesto
tecnica = recom.tecnica_principal.lower().replace(" + ", "_").replace(" ", "_")
costo_estimado = COSTOS_ESTIMADOS.get(tecnica, COSTOS_ESTIMADOS["cot"])
dentro_presupuesto = costo_estimado <= budget_usd_por_request
if not dentro_presupuesto:
# Buscar alternativa más barata
alternativas_por_costo = sorted(
[(t, c) for t, c in COSTOS_ESTIMADOS.items() if c <= budget_usd_por_request],
key=lambda x: x[1], reverse=True
)
if alternativas_por_costo:
tecnica_alternativa = alternativas_por_costo[0][0]
advertencia = f"Técnica óptima ({recom.tecnica_principal}) supera presupuesto. Usando {tecnica_alternativa}"
else:
tecnica_alternativa = "zero_shot"
advertencia = "Budget muy ajustado. Usando zero-shot."
else:
tecnica_alternativa = tecnica
advertencia = None
return {
"tecnica_recomendada": recom.tecnica_principal,
"tecnica_dentro_presupuesto": tecnica_alternativa,
"parametros": recom.parametros_sugeridos,
"justificacion": recom.justificacion,
"costo_estimado_usd": costo_estimado,
"dentro_presupuesto": dentro_presupuesto,
"advertencia": advertencia,
"fallback": recom.alternativas[0] if recom.alternativas else "zero_shot",
"analisis_tarea": {
"tipo": analisis.tipo.value,
"accuracy_critica": analisis.accuracy_critica,
"necesita_externos": analisis.necesita_datos_externos
}
}
Casos de Estudio
Caso 1: Sistema de tutoring adaptativo
Tarea: Sistema de tutoring de matemáticas para bachillerato.
Requisitos:
- Respuestas siempre correctas (accuracy crítica)
- Explicaciones paso a paso
- Detectar si el estudiante cometió un error específico
Análisis:
- Tipo: RAZONAMIENTO_MATH
- Accuracy crítica: TRUE (no puedes enseñar matemáticas incorrectas)
- Datos externos: FALSE
- Budget por consulta: $0.01
→ RECOMENDACIÓN: CoT + Self-Consistency N=5
Parámetros: n=5, temperatura=0.7
Justificación: Accuracy crítica, math → SC para validar, CoT para explicaciones
Costo estimado: $0.002 (dentro del presupuesto)
IMPLEMENTACIÓN:
```python
def tutoring_matematicas(ejercicio: str, respuesta_estudiante: str) -> dict:
"""
Evalúa la respuesta del estudiante y genera retroalimentación.
"""
from collections import Counter
import re
# Resolver correctamente con CoT + SC
respuestas = []
for _ in range(5):
resp = client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": f"{ejercicio}\nResuelve paso a paso. Respuesta: [número]"}],
temperature=0.7, max_tokens=400
).choices[0].message.content
nums = re.findall(r'-?\d+\.?\d*', resp)
respuestas.append(nums[-1] if nums else resp.strip()[-10:])
respuesta_correcta = Counter(respuestas).most_common(1)[0][0]
# Evaluar respuesta del estudiante
try:
es_correcto = abs(float(respuesta_estudiante) - float(respuesta_correcta)) < 0.01
except ValueError:
es_correcto = respuesta_estudiante.strip() == respuesta_correcta.strip()
# Generar retroalimentación
feedback = client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": f"""
Ejercicio: {ejercicio}
Respuesta correcta: {respuesta_correcta}
Respuesta del estudiante: {respuesta_estudiante}
¿Correcto?: {es_correcto}
{'¡Correcto! Explica por qué la solución es correcta en 2-3 oraciones.' if es_correcto else 'Incorrecto. Identifica el error probable del estudiante y explica el proceso correcto paso a paso.'}
"""}],
temperature=0
).choices[0].message.content
return {
"es_correcto": es_correcto,
"respuesta_correcta": respuesta_correcta,
"retroalimentacion": feedback
}
Caso 2: Análisis de sentimiento para app de reviews
Tarea: Clasificar reviews de productos como POSITIVO/NEGATIVO/MIXTO/NEUTRAL
Volumen: 10,000 reviews/día
Requisitos:
- Alta velocidad (<2 seg por review)
- Bajo costo (presupuesto limitado)
- 85%+ accuracy (errores ocasionales aceptables)
→ RECOMENDACIÓN: Few-shot con 2-3 ejemplos
Justificación: Clasificación simple, alto volumen → economizar
Costo: 1x (vs 5x si usáramos SC)
NO usar: CoT + Self-Consistency (sería 5x más caro sin mejora significativa para clasificación)
Troubleshooting del Framework
Problema 1: La técnica recomendada da malos resultados
Síntoma: El framework recomienda CoT para una tarea que el modelo falla consistentemente.
Diagnóstico: La tarea requiere conocimiento fuera del rango de entrenamiento, o el problema es de planificación compleja.
Solución:
def diagnóstico_rápido(problema: str) -> str:
"""
Ejecuta CoT simple. Si falla, sugiere técnica alternativa.
"""
resp = client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": f"{problema}\nPiensa paso a paso."}],
temperature=0, max_tokens=400
).choices[0].message.content
# Señales de que CoT está fallando:
señales_fallo = [
"no sé", "no puedo", "not sure", "I don't know",
"necesito más información", "requires more context",
"impossible", "imposible"
]
falla_cot = any(s in resp.lower() for s in señales_fallo)
if falla_cot:
print("⚠ CoT falla. Posibles soluciones:")
print(" 1. ReAct: si el problema necesita datos externos")
print(" 2. ToT: si hay múltiples estrategias posibles")
print(" 3. Few-shot con ejemplos: si el modelo no entiende el formato")
print(" 4. Reformular el problema: hacerlo más explícito")
return resp
Problema 2: El presupuesto es muy ajustado
Síntoma: Cada técnica avanzada supera el presupuesto permitido.
Solución: Usar routing para aplicar técnicas avanzadas solo cuando sea necesario:
def routing_presupuesto(
problema: str,
presupuesto_extra: bool = False # True si el cliente/caso lo justifica
) -> str:
"""
Aplicar técnicas avanzadas solo para casos que lo necesitan.
"""
# Primero, intentar con la técnica más barata
respuesta_simple = client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": problema}],
temperature=0, max_tokens=200
).choices[0].message.content
# Detectar si la respuesta simple es de baja calidad
necesita_mas = any(palabra in respuesta_simple.lower() for palabra in
["no sé", "unclear", "depends", "complex", "multiple"])
if necesita_mas and presupuesto_extra:
# Escalar a técnica más avanzada
return client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": f"{problema}\nPiensa muy cuidadosamente paso a paso."}],
temperature=0, max_tokens=600
).choices[0].message.content
return respuesta_simple
Ejercicios
Ejercicio 1: Clasificar 10 tareas
Para cada una de las siguientes tareas, usa el árbol de decisión para clasificar qué técnica usarías:
- Traducir un contrato legal del español al inglés
- Responder "¿Cuánto vale Bitcoin ahora?"
- Generar variantes de un email de marketing
- Verificar si un número es primo
- Analizar el tono de un email de soporte
- Calcular la ruta más eficiente entre 5 ciudades
- Resumir un informe de 50 páginas
- Escribir código para ordenar un array
- Responder preguntas sobre un documento específico cargado
- Detectar si un tweet viola las políticas de uso
Ver solución
- Traducción: Few-shot con ejemplos del dominio legal → Zero-shot/Few-shot (1x)
- Precio Bitcoin: Datos en tiempo real → ReAct con tool de precio (5x)
- Variantes de email: Generativo, sin respuesta única → Zero-shot con temperature 0.8 (1x)
- ¿Es primo?: Math puro → CoT (no necesita SC para número simple) (1.5x)
- Tono de email: Clasificación → Few-shot (1x)
- Ruta entre ciudades: Planificación con múltiples estrategias → ToT o CoT (depende de complejidad)
- Resumen 50 páginas: Texto largo → Map-Reduce (chunking) + Zero-shot por chunk (N llamadas)
- Código ordenar array: Código → Self-Refine con criterios de correctitud/eficiencia (3-5x)
- QA sobre documento: RAG (búsqueda + generación) → ReAct con tool de búsqueda en doc (3-6x)
- Moderation: Clasificación → Few-shot con ejemplos de políticas (1x)
Ejercicio 2: Diseñar el sistema completo
Tienes un sistema de análisis de contratos donde:
- Recibes 500 contratos/día
- Cada contrato tiene ~20 páginas (~15K tokens)
- Necesitas: resumen ejecutivo, cláusulas de riesgo, partes involucradas
- Budget: $0.10 por contrato
Diseña la arquitectura completa indicando qué técnica usas en cada etapa.
Ver solución
Arquitectura para análisis de contratos:
Etapa 1: Preprocessing (no LLM)
- Dividir en chunks de 4K tokens (manteniendo contexto de sección)
- Identificar secciones por headers
Etapa 2: Extracción por chunk (Few-shot, 1 llamada por chunk)
- ~4 chunks por contrato = 4 llamadas
- Extraer: partes, fechas, obligaciones por sección
- Costo: ~4 × $0.0002 = $0.0008
Etapa 3: Análisis de riesgo (CoT, 1 llamada)
- Input: outputs concatenados de etapa 2
- Identificar cláusulas problemáticas con razonamiento
- Costo: ~$0.0006
Etapa 4: Resumen ejecutivo (Self-Refine, 2 iteraciones)
- Input: análisis de riesgo + extracción
- 2 iteraciones para calidad
- Costo: ~2 × $0.0004 = $0.0008
Total estimado: ~$0.002 por contrato (muy por debajo de $0.10)
→ Puedes permitirte más iteraciones en Self-Refine o usar Self-Consistency N=3 para análisis de riesgo
Ejercicio 3: Construir el router
Implementa una función router_automatico(tarea: str, texto: str) -> str que:
- Clasifica la tarea
- Selecciona la técnica
- Ejecuta la técnica
- Retorna la respuesta
Ver solución
def router_automatico(tarea: str, texto: str) -> str:
"""Router que selecciona y ejecuta la técnica correcta automáticamente."""
problema = f"{tarea}\n\nTexto/Input:\n{texto}"
# Clasificar tarea
analisis = analizar_tarea_automatico(tarea)
recom = seleccionar_tecnica(analisis)
tecnica = recom.tecnica_principal.lower()
print(f"[Router] Técnica seleccionada: {tecnica}")
# Ejecutar según técnica
if "self-consistency" in tecnica or "sc" in tecnica:
from collections import Counter
import re
resps = []
for _ in range(3):
r = client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": f"{problema}\nPiensa paso a paso."}],
temperature=0.7, max_tokens=400
).choices[0].message.content
nums = re.findall(r'-?\d+\.?\d*', r)
resps.append(nums[-1] if nums else r.strip()[-20:])
return Counter(resps).most_common(1)[0][0]
elif "few-shot" in tecnica or "clasificacion" in analisis.tipo.value:
return client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": problema}],
temperature=0, max_tokens=200
).choices[0].message.content
else: # CoT default
return client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": f"{problema}\nPiensa paso a paso."}],
temperature=0, max_tokens=500
).choices[0].message.content
Resumen
El framework de decisión se resume en estas preguntas clave:
- ¿Necesitas datos externos? → Sí: ReAct. No: continúa.
- ¿Es razonamiento (math/lógica)? → Sí: CoT. Si accuracy crítica: CoT + SC.
- ¿Hay múltiples estrategias posibles? → Sí: ToT (si budget lo permite).
- ¿Es clasificación/extracción? → Few-shot o Zero-shot.
- ¿La calidad del prompt o respuesta es subóptima? → Meta-prompting / Self-Refine.
La regla de oro: empieza con la técnica más simple que funcione para tu caso de uso. Escala a técnicas más complejas solo cuando sea necesario.