Módulo 4: Chain-of-Thought y Razonamiento

3. Manual CoT: Diseñar Cadenas de Razonamiento

Descripción

Manual CoT (también llamado Few-Shot CoT) proporciona al modelo ejemplos completos con razonamiento explícito. A diferencia de Zero-Shot CoT donde solo añades una frase, aquí diseñas cuidadosamente los pasos de razonamiento que quieres que el modelo imite. Formato estándar: Input → Reasoning → Answer.

En esta cápsula aprenderás a diseñar cadenas de razonamiento efectivas para math, lógica, debugging de código, análisis de texto y decisiones de negocio.

Tiempo estimado: 75-90 minutos


¿Por Qué Manual CoT es Más Potente que Zero-Shot?

Zero-Shot CoT activa patrones genéricos de razonamiento. Manual CoT te permite:

  1. Especificar el estilo de razonamiento exacto que quieres
  2. Definir el nivel de detalle (paso a paso vs. saltos lógicos permitidos)
  3. Establecer el vocabulario técnico apropiado para tu dominio
  4. Controlar el formato de salida de manera precisa
  5. Demostrar cómo manejar casos especiales o edge cases

Tradeoff: Manual CoT requiere más diseño inicial, pero produce razonamiento más consistente y controlado.


Formato Estándar

El formato canónico de Manual CoT es:

Q: [pregunta o problema]
A: [razonamiento explícito paso a paso] ... Respuesta: [respuesta final]

O en formato etiquetado más explícito:

Input: [problema]
Reasoning: [paso 1] → [paso 2] → ... → [paso N]
Answer: [respuesta final]

Estructura de un Buen Ejemplo

Un ejemplo de Manual CoT efectivo tiene estas características:

  • Complejidad similar al problema real: No demasiado simple, no demasiado diferente
  • Razonamiento completo: Muestra cada paso, no omite pasos intermedios importantes
  • Terminología consistente: Usa los mismos términos que usará el modelo al responder
  • Respuesta clara: La respuesta final es inequívoca, en el formato esperado
  • Longitud apropiada: 3-8 pasos según la complejidad del dominio

Ejemplo Completo: Aritmética de Palabras

from openai import OpenAI

client = OpenAI()

MATH_WORD_COT_PROMPT = """Resuelve cada problema matemático mostrando cada paso de razonamiento.

Q: Una tienda tiene 120 camisas. El lunes vendió 35. El martes recibió un envío de 50 y vendió 28. ¿Cuántas camisas tiene ahora?
A: Vamos paso a paso.
Al inicio: 120 camisas.
Lunes: vendió 35 → 120 - 35 = 85 camisas.
Martes recibió: 85 + 50 = 135 camisas.
Martes vendió: 135 - 28 = 107 camisas.
Respuesta: 107 camisas.

Q: Ana gana $2,400 al mes. Paga $720 de renta (30%), $360 de comida (15%), y ahorra el 20%. ¿Cuánto le queda después de todos estos gastos?
A: Vamos paso a paso.
Sueldo mensual: $2,400.
Renta: 30% de 2400 = 0.30 × 2400 = $720.
Comida: 15% de 2400 = 0.15 × 2400 = $360.
Ahorro: 20% de 2400 = 0.20 × 2400 = $480.
Total gastado/ahorrado: 720 + 360 + 480 = $1,560.
Dinero restante: 2400 - 1560 = $840.
Respuesta: $840.

Q: {problema}
A:"""


def resolver_math_word(problema: str) -> str:
    """Resuelve un problema de aritmética de palabras con Manual CoT."""
    response = client.chat.completions.create(
        model="gpt-4o-mini",
        messages=[
            {
                "role": "user",
                "content": MATH_WORD_COT_PROMPT.format(problema=problema)
            }
        ],
        temperature=0,
        max_tokens=600
    )
    return response.choices[0].message.content


if __name__ == "__main__":
    problema = """
    Un proyecto tiene 3 equipos. El equipo A hace 40% del trabajo. 
    El equipo B hace el doble que el equipo C. 
    Si el equipo C hace 12 horas de trabajo, ¿cuántas horas en total tiene el proyecto?
    """
    print(resolver_math_word(problema))

Ejemplo: Razonamiento Lógico Formal

LOGIC_COT_PROMPT = """Analiza la validez de cada argumento lógico, identificando premisas y la conclusión.

Q: Si llueve, entonces el suelo está mojado. El suelo está mojado. ¿Llueve?
A: Vamos paso a paso.
Premisa 1: Si llueve → suelo mojado. (P → Q)
Premisa 2: Suelo mojado. (Q es verdadero)
¿Podemos concluir que llueve? (P es verdadero?)
Análisis: Tener Q verdadero NO garantiza P. El suelo puede estar mojado por riego, derrame, etc.
Esto es la falacia de "afirmación del consecuente" (Affirming the Consequent).
Respuesta: INVÁLIDO. No se puede concluir que llueve solo porque el suelo está mojado.

Q: Todos los mamíferos tienen sangre caliente. Los delfines son mamíferos. ¿Los delfines tienen sangre caliente?
A: Vamos paso a paso.
Premisa 1: ∀x: mamífero(x) → sangre_caliente(x). (Universal)
Premisa 2: delfín es mamífero.
Conclusión deseada: delfín tiene sangre caliente.
Análisis: Aplicamos modus ponens. Para x = delfín: mamífero(delfín) es verdadero (premisa 2). Por lo tanto sangre_caliente(delfín) es verdadero por premisa 1.
Respuesta: VÁLIDO. Los delfines sí tienen sangre caliente. Argumento correcto por modus ponens.

Q: {argumento}
A:"""


def analizar_argumento(argumento: str) -> str:
    """Analiza la validez de un argumento lógico con Manual CoT."""
    response = client.chat.completions.create(
        model="gpt-4o-mini",
        messages=[
            {
                "role": "user",
                "content": LOGIC_COT_PROMPT.format(argumento=argumento)
            }
        ],
        temperature=0,
        max_tokens=500
    )
    return response.choices[0].message.content

Ejemplo: Code Debugging

CODE_DEBUG_COT_PROMPT = """Debuggea el código Python identificando el error, explicando por qué ocurre, y proponiendo la corrección.

Q: 
```python
def calcular_promedio(numeros):
    total = 0
    for n in numeros:
        total += n
    return total / len(numeros)

print(calcular_promedio([]))

A: Vamos paso a paso. Traza de ejecución:

  • Se llama calcular_promedio con lista vacía [].
  • El bucle no ejecuta (lista vacía).
  • total = 0.
  • return 0 / len([]) → 0 / 0. Error: ZeroDivisionError: division by zero. Causa raíz: No hay validación de lista vacía. Corrección:
def calcular_promedio(numeros):
    if not numeros:
        return 0  # O raise ValueError("Lista vacía")
    total = sum(numeros)
    return total / len(numeros)

Respuesta: ZeroDivisionError por lista vacía. Añadir validación if not numeros.

Q:

nombres = ["Ana", "Beto", "Carlos"]
for i in range(len(nombres) + 1):
    print(f"Hola, {nombres[i]}!")

A: Vamos paso a paso. Traza de ejecución:

  • nombres tiene 3 elementos: índices 0, 1, 2.
  • range(len(nombres) + 1) = range(4) = [0, 1, 2, 3].
  • Iteración i=0: nombres[0] = "Ana" → OK.
  • Iteración i=1: nombres[1] = "Beto" → OK.
  • Iteración i=2: nombres[2] = "Carlos" → OK.
  • Iteración i=3: nombres[3] → IndexError: list index out of range. Error: IndexError en i=3 porque el índice máximo válido es 2. Causa raíz: range usa + 1 innecesario. Corrección:
for i in range(len(nombres)):  # Eliminar el + 1
    print(f"Hola, {nombres[i]}!")
# O mejor aún:
for nombre in nombres:
    print(f"Hola, {nombre}!")

Respuesta: IndexError por range(len+1). Cambiar a range(len(nombres)) o iterar directamente.

Q: {codigo} A:"""

def debug_codigo(codigo: str) -> str: """Debuggea código Python con Manual CoT.""" response = client.chat.completions.create( model="gpt-4o-mini", messages=[ { "role": "user", "content": CODE_DEBUG_COT_PROMPT.format(codigo=codigo) } ], temperature=0, max_tokens=700 ) return response.choices[0].message.content


---

## Ejemplo: Análisis de Sentimiento Contextual

CoT no solo es para matemáticas. Funciona para clasificación ambigua:

```python
SENTIMENT_COT_PROMPT = """Clasifica el sentimiento del texto (POSITIVO, NEGATIVO, NEUTRO o MIXTO) razonando sobre cada elemento.

Q: "El hotel tenía una vista increíble, pero las habitaciones olían a humedad y el servicio de desayuno era lentísimo."
A: Vamos paso a paso.
Elemento 1: "vista increíble" → muy positivo (experiencia sensorial destacada).
Elemento 2: "habitaciones olían a humedad" → muy negativo (experiencia sensorial desagradable, afecta directamente el confort).
Elemento 3: "servicio de desayuno lentísimo" → negativo (afecta la conveniencia).
Conteo: 1 positivo vs. 2 negativos.
Peso: La humedad en habitaciones es un problema mayor que la vista es un beneficio.
Conclusión: El texto es predominantemente negativo con un elemento positivo.
Respuesta: MIXTO (tendencia NEGATIVA). Confianza: 0.75.

Q: "Entrega puntual como siempre. Gracias."
A: Vamos paso a paso.
Elemento 1: "Entrega puntual" → positivo (cumple expectativa).
Elemento 2: "como siempre" → refuerza el positivo (consistencia).
Elemento 3: "Gracias" → positivo leve (expresión de gratitud).
No hay elementos negativos.
Conclusión: Texto claramente positivo, aunque moderado (no hay entusiasmo excepcional).
Respuesta: POSITIVO. Confianza: 0.90.

Q: {texto}
A:"""


def analizar_sentimiento(texto: str) -> dict:
    """Analiza sentimiento con razonamiento explícito."""
    response = client.chat.completions.create(
        model="gpt-4o-mini",
        messages=[
            {
                "role": "user",
                "content": SENTIMENT_COT_PROMPT.format(texto=texto)
            }
        ],
        temperature=0,
        max_tokens=400
    )
    output = response.choices[0].message.content
    
    # Parsear resultado estructurado
    import re
    sentimiento_match = re.search(
        r'Respuesta:\s*(POSITIVO|NEGATIVO|NEUTRO|MIXTO)', 
        output, re.IGNORECASE
    )
    confianza_match = re.search(r'Confianza:\s*(0\.\d+|1\.0)', output)
    
    return {
        "razonamiento": output,
        "sentimiento": sentimiento_match.group(1) if sentimiento_match else "DESCONOCIDO",
        "confianza": float(confianza_match.group(1)) if confianza_match else None
    }

Ejemplo: Decisiones de Negocio

DECISION_COT_PROMPT = """Analiza la decisión de negocio evaluando factores clave paso a paso.

Q: Una startup de 10 personas tiene $200K en caja. Queman $40K/mes. Les ofrecen una ronda de inversión de $500K con 20% de dilución. El CEO estima que con ese dinero en 12 meses podrían alcanzar break-even. Sin inversión, tienen 5 meses de runway. ¿Deben tomar la inversión?
A: Vamos paso a paso.
Situación actual: $200K caja / $40K burn = 5 meses de runway.
Opción A (Tomar inversión):
- Caja post-inversión: 200K + 500K = $700K.
- Runway: 700K / 40K = 17.5 meses.
- Dilución: CEO y equipo pierden 20% de su participación.
- Upside: 12 meses para llegar a break-even; si lo logran, el 80% restante podría valer mucho más.
Opción B (No tomar inversión):
- Solo 5 meses para ser rentables o conseguir otra fuente de capital.
- Alta presión de tiempo; decisiones sub-óptimas bajo presión.
- Sin dilución, pero alto riesgo de cierre.
Análisis de riesgo: Con 5 meses, si no se logra break-even, la startup cierra. Con 17.5 meses, hay margen para pivotar si es necesario.
Factores cualitativos: Calidad de los inversores, términos del acuerdo, confianza del CEO en el plan de 12 meses.
Respuesta: TOMAR LA INVERSIÓN. El riesgo de quedarse sin dinero en 5 meses supera el costo de la dilución del 20%, especialmente si los inversores añaden valor estratégico.

Q: {situacion}
A:"""

Diseño de Ejemplos: Principios Avanzados

Principio 1: Diversidad de Tipos de Razonamiento

Tus ejemplos deben cubrir distintos "tipos" de problemas que el modelo puede encontrar:

# Para un sistema de QA sobre contratos legales
EXAMPLES_LEGAL = [
    {
        "input": "¿Puede el cliente cancelar el contrato si el proveedor entrega con 3 días de retraso?",
        "tipo": "interpretación_directa",
        "reasoning": """
Buscar cláusula de retraso en el contrato.
Cláusula 8.2: "Retrasos menores a 5 días hábiles no constituyen incumplimiento material."
3 días < 5 días → No es incumplimiento material según la cláusula.
Sin incumplimiento material, el cliente no puede cancelar sin penalidad.
Respuesta: NO, no puede cancelar sin penalidad. El retraso de 3 días no es incumplimiento material según cláusula 8.2."""
    },
    {
        "input": "El contrato dice 'precios sujetos a ajuste por IPC'. El IPC subió 8.5% este año. ¿Cuánto sube el precio de $10,000/mes?",
        "tipo": "calculo_con_interpretacion",
        "reasoning": """
Cláusula de ajuste: precio sujeto a IPC (Índice de Precios al Consumidor).
Precio actual: $10,000/mes.
Variación IPC: 8.5%.
Cálculo: 10,000 × 1.085 = $10,850/mes.
Aumento: $850/mes.
Respuesta: El nuevo precio es $10,850/mes. Aumento de $850/mes."""
    }
]

Principio 2: Consistencia en el Formato de Respuesta

# ❌ Formato inconsistente en ejemplos
ejemplo_malo = """
Q: ¿Cuánto es 5 × 7?
A: 5 veces 7 = 35

Q: ¿Cuánto es 8 × 9?
A: Vamos paso a paso. 8 × 9 = 72.
Respuesta: setenta y dos
"""

# ✅ Formato consistente
ejemplo_bueno = """
Q: ¿Cuánto es 5 × 7?
A: Paso a paso.
5 × 7 = 35.
Respuesta: 35.

Q: ¿Cuánto es 8 × 9?
A: Paso a paso.
8 × 9 = 72.
Respuesta: 72.
"""

Principio 3: Dificultad Representativa

def seleccionar_ejemplos(pool_ejemplos: list[dict], problema_nuevo: str) -> list[dict]:
    """
    Selecciona los ejemplos más representativos para un problema nuevo.
    
    En producción, podrías usar embeddings para similarity search.
    Para prototipos, la selección manual es suficiente.
    """
    # Simplificado: seleccionar por tipo de problema
    # En producción: usar sentence embeddings + cosine similarity
    
    tipo_detectado = detectar_tipo(problema_nuevo)
    ejemplos_del_tipo = [e for e in pool_ejemplos if e["tipo"] == tipo_detectado]
    
    # Seleccionar 2-3 ejemplos de distintos sub-tipos
    return ejemplos_del_tipo[:3]


def detectar_tipo(problema: str) -> str:
    """Detecta el tipo de problema para selección de ejemplos."""
    keywords = {
        "aritmetica": ["cuánto", "total", "promedio", "porcentaje", "$", "precio"],
        "logica": ["implica", "si...entonces", "por lo tanto", "válido", "todos", "algunos"],
        "codigo": ["def ", "class ", "error", "bug", "función", "python", "javascript"],
        "decision": ["debo", "debería", "mejor opción", "ventajas", "desventajas"],
    }
    
    problema_lower = problema.lower()
    for tipo, kwords in keywords.items():
        if any(kw in problema_lower for kw in kwords):
            return tipo
    return "general"

Comparativa: Zero-Shot CoT vs. Manual CoT

AspectoZero-Shot CoTManual CoT
Esfuerzo de diseñoMínimoAlto
Tokens en promptPocosMuchos (ejemplos)
Consistencia de formatoMediaAlta
Control del razonamientoBajoAlto
GeneralizaciónAltaMedia (depende de ejemplos)
Mejor paraPrototipos, dominios generalesProducción, dominios específicos
Costo por llamadaMenorMayor (más tokens de input)

Cuántos Ejemplos Necesitas

La investigación sugiere una curva de rendimientos decrecientes:

Número de ejemplosMejora típicaCosto tokens
0 (Zero-Shot)Línea base0 extra
1 ejemplo+10-15%~200-400 tokens
2-3 ejemplos+20-30%~400-800 tokens
4-5 ejemplos+30-35%~800-1500 tokens
6-8 ejemplos+35-37%~1500-2500 tokens
> 8 ejemplosRendimientos mínimosMuy costoso

Recomendación práctica: 2-3 ejemplos bien diseñados son casi siempre suficientes.


Implementación Avanzada con Pydantic

from pydantic import BaseModel, Field
from openai import OpenAI
from typing import Literal

client = OpenAI()


class EjemploCoT(BaseModel):
    """Estructura para un ejemplo de Manual CoT."""
    input_problema: str = Field(description="El problema o pregunta")
    razonamiento: str = Field(description="Los pasos de razonamiento")
    respuesta: str = Field(description="La respuesta final")
    tipo: str = Field(default="general", description="Categoría del problema")


class ManualCoTEngine:
    """Motor de razonamiento con Manual CoT configurable."""
    
    def __init__(
        self, 
        ejemplos: list[EjemploCoT],
        instruccion_sistema: str = "Eres un asistente que razona paso a paso.",
        max_tokens: int = 700
    ):
        self.ejemplos = ejemplos
        self.instruccion_sistema = instruccion_sistema
        self.max_tokens = max_tokens
    
    def construir_prompt(self, problema: str, n_ejemplos: int = 3) -> str:
        """Construye el prompt con ejemplos seleccionados."""
        # Tomar los primeros n_ejemplos (o todos si hay menos)
        ejemplos_seleccionados = self.ejemplos[:n_ejemplos]
        
        partes = []
        for ej in ejemplos_seleccionados:
            partes.append(
                f"Q: {ej.input_problema}\n"
                f"A: {ej.razonamiento}\n"
                f"Respuesta: {ej.respuesta}"
            )
        
        prompt_ejemplos = "\n\n".join(partes)
        
        return f"""{prompt_ejemplos}

Q: {problema}
A:"""
    
    def resolver(self, problema: str, n_ejemplos: int = 3) -> dict:
        """Resuelve un problema usando Manual CoT."""
        prompt = self.construir_prompt(problema, n_ejemplos)
        
        response = client.chat.completions.create(
            model="gpt-4o-mini",
            messages=[
                {"role": "system", "content": self.instruccion_sistema},
                {"role": "user", "content": prompt}
            ],
            temperature=0,
            max_tokens=self.max_tokens
        )
        
        output = response.choices[0].message.content
        
        return {
            "problema": problema,
            "razonamiento_completo": output,
            "tokens_usados": response.usage.total_tokens,
            "n_ejemplos_usados": n_ejemplos
        }


# Uso del engine
if __name__ == "__main__":
    ejemplos_matematica = [
        EjemploCoT(
            input_problema="Un café vende 150 tazas/día a $3.50 cada una. Los costos variables son $1.20/taza y los fijos $200/día. ¿Cuál es la ganancia diaria?",
            razonamiento="Ingresos: 150 × 3.50 = $525.\nCostos variables: 150 × 1.20 = $180.\nCostos fijos: $200.\nCostos totales: 180 + 200 = $380.\nGanancia: 525 - 380 = $145.",
            respuesta="$145",
            tipo="financiero"
        ),
        EjemploCoT(
            input_problema="¿Cuántos días tarda un equipo de 4 personas en completar una tarea que una persona haría en 20 días?",
            razonamiento="Trabajo total: 1 tarea = 20 días-persona.\nCapacidad del equipo: 4 personas/día.\nDías necesarios: 20 / 4 = 5 días.",
            respuesta="5 días",
            tipo="trabajo"
        )
    ]
    
    engine = ManualCoTEngine(
        ejemplos=ejemplos_matematica,
        instruccion_sistema="Eres un experto en matemáticas de negocio. Resuelve paso a paso."
    )
    
    resultado = engine.resolver(
        "Una empresa tiene 3 vendedores. En un mes vendieron $45,000, $38,000 y $52,000 respectivamente. La comisión es 6% sobre el total de ventas. ¿Cuánto cobra cada vendedor de comisión?"
    )
    
    print(resultado["razonamiento_completo"])
    print(f"\nTokens usados: {resultado['tokens_usados']}")

Troubleshooting

Problema 1: El modelo no sigue el formato de ejemplos

Síntomas: El modelo da la respuesta sin el razonamiento, o usa un formato diferente.

# ❌ Causa: Los ejemplos están en un idioma diferente al problema
prompt_malo = """
Q: What is 5 + 3?
A: Step by step. 5 + 3 = 8. Answer: 8.

Q: ¿Cuánto es 7 × 6?
A:"""  # El modelo puede responder en inglés o sin el formato

# ✅ Solución: Mismos idioma y formato en ejemplos y problema
prompt_bueno = """
Q: ¿Cuánto es 5 + 3?
A: Paso a paso. 5 + 3 = 8. Respuesta: 8.

Q: ¿Cuánto es 7 × 6?
A:"""

Problema 2: Los ejemplos son demasiado simples

Síntomas: El modelo sigue el formato pero aplica razonamiento superficial al problema real.

# ❌ Ejemplo muy simple para problemas complejos
ejemplo_inadecuado = """
Q: ¿Cuánto es 2 + 2?
A: 2 + 2 = 4. Respuesta: 4.

Q: [Problema complejo de probabilidad condicional]
A:"""
# El modelo "imita" la simplicidad del ejemplo

# ✅ Ejemplos de dificultad similar al problema real
ejemplo_adecuado = """
Q: [Problema de probabilidad moderada]
A: [Razonamiento de 5-6 pasos con probabilidades]

Q: [Problema complejo de probabilidad condicional]
A:"""

Problema 3: Razonamiento fabricado en los ejemplos

Síntomas: Hay errores en los ejemplos que escribiste y el modelo los imita.

# CRÍTICO: Verifica SIEMPRE que tus ejemplos sean correctos
def verificar_ejemplo(ejemplo: EjemploCoT) -> bool:
    """
    Verifica que un ejemplo de CoT sea correcto usando otro LLM como verificador.
    """
    prompt_verificacion = f"""
    Verifica si el siguiente razonamiento matemático es correcto.
    
    Problema: {ejemplo.input_problema}
    Razonamiento: {ejemplo.razonamiento}
    Respuesta: {ejemplo.respuesta}
    
    ¿Es correcto? Responde CORRECTO o INCORRECTO. 
    Si hay error, explica cuál.
    """
    
    response = client.chat.completions.create(
        model="gpt-4o-mini",
        messages=[{"role": "user", "content": prompt_verificacion}],
        temperature=0,
        max_tokens=200
    )
    
    resultado = response.choices[0].message.content.upper()
    return "CORRECTO" in resultado

Problema 4: Prompts demasiado largos por muchos ejemplos

Síntomas: Costos altos, latencia alta, o errores de contexto.

# ✅ Comprimir ejemplos sin perder estructura
def comprimir_ejemplo(ejemplo: EjemploCoT) -> str:
    """Versión comprimida de un ejemplo para prompts largos."""
    # Condensar el razonamiento a puntos clave
    pasos = ejemplo.razonamiento.split('\n')
    pasos_importantes = [p for p in pasos if p.strip() and '→' in p or '=' in p]
    razonamiento_comprimido = ' → '.join(pasos_importantes[:4])
    
    return f"Q: {ejemplo.input_problema[:100]}\nA: {razonamiento_comprimido} Respuesta: {ejemplo.respuesta}"

Ejercicios

Ejercicio 1: Crear ejemplo para problema de edades

Diseña un ejemplo Manual CoT completo para el siguiente tipo de problema: "Ana tiene el doble de la edad de Luis. En 5 años sus edades sumarán 40. ¿Qué edad tiene cada uno ahora?"

Ver solución
ejemplo_edades = EjemploCoT(
    input_problema="Pedro tiene el triple de la edad de Sofía. Hace 3 años, la suma de sus edades era 22. ¿Qué edades tienen ahora?",
    razonamiento="""Definir variables: Sea S = edad actual de Sofía. Pedro = 3S (el triple).
Hace 3 años: Sofía tenía (S-3) años, Pedro tenía (3S-3) años.
Ecuación: (S-3) + (3S-3) = 22
Simplificar: 4S - 6 = 22
4S = 28
S = 7
Pedro = 3 × 7 = 21.
Verificación: Hace 3 años: 4 + 18 = 22 ✓""",
    respuesta="Sofía tiene 7 años, Pedro tiene 21 años."
)

# Ahora el prompt para el problema de Ana y Luis
PROMPT_EDADES = f"""Resuelve el problema de edades paso a paso usando álgebra.

Q: {ejemplo_edades.input_problema}
A: {ejemplo_edades.razonamiento}
Respuesta: {ejemplo_edades.respuesta}

Q: Ana tiene el doble de la edad de Luis. En 5 años sus edades sumarán 40. ¿Qué edad tiene cada uno ahora?
A:"""

response = client.chat.completions.create(
    model="gpt-4o-mini",
    messages=[{"role": "user", "content": PROMPT_EDADES}],
    temperature=0,
    max_tokens=400
)
print(response.choices[0].message.content)
# Respuesta esperada: Luis = 10, Ana = 20

Ejercicio 2: Few-shot CoT para clasificación de urgencia

Diseña 3 ejemplos de Manual CoT para clasificar tickets de soporte como URGENTE, NORMAL o BAJO. Luego prueba con 5 tickets nuevos.

Ver solución
SUPPORT_COT_PROMPT = """Clasifica la urgencia de cada ticket de soporte (URGENTE, NORMAL, BAJO) razonando sobre el impacto.

Q: "El sistema de pagos está caído. Llevamos 2 horas sin poder procesar ninguna transacción."
A: Vamos paso a paso.
Impacto operativo: Sistema de pagos = función core del negocio.
Duración: 2 horas de downtime.
Usuarios afectados: Todos los que intentan pagar → ventas bloqueadas.
Pérdida económica: Alta y creciendo por minuto.
Clasificación: Impacto máximo en revenue + muchos usuarios afectados.
Respuesta: URGENTE.

Q: "El dashboard de analytics no muestra datos de las últimas 48 horas."
A: Vamos paso a paso.
Impacto operativo: Analytics = función de reporting, no core.
Duración: 48 horas sin datos actualizados.
Usuarios afectados: Equipo de marketing/analytics (interno, pocos usuarios).
Pérdida económica: Decisiones retrasadas, pero no bloquea operaciones.
Clasificación: Impacto medio (afecta decisiones pero no operaciones críticas).
Respuesta: NORMAL.

Q: "¿Podrían cambiar el color del botón de exportar? El azul actual no combina con nuestra marca."
A: Vamos paso a paso.
Impacto operativo: Cambio estético, la función exportar sigue funcionando.
Urgencia del usuario: "¿podrían?" indica solicitud no urgente.
Usuarios afectados: Preferencia personal/estética.
Pérdida económica: Ninguna.
Clasificación: Sin impacto funcional ni económico.
Respuesta: BAJO.

Q: {ticket}
A:"""

tickets_prueba = [
    "Mi contraseña no funciona y tengo una presentación en 30 minutos",
    "¿Cuándo añadirán soporte para exportar en formato Excel?",
    "La API está retornando error 500 para el 30% de las peticiones en producción",
    "El tutorial de onboarding tiene un error tipográfico en el paso 3",
    "El servidor principal cayó. Todos los usuarios están viendo página 503",
]

for ticket in tickets_prueba:
    response = client.chat.completions.create(
        model="gpt-4o-mini",
        messages=[{"role": "user", "content": SUPPORT_COT_PROMPT.format(ticket=ticket)}],
        temperature=0,
        max_tokens=300
    )
    print(f"Ticket: {ticket[:50]}...")
    print(f"Respuesta: {response.choices[0].message.content[-100:]}\n")

Ejercicio 3: Diseñar CoT para análisis de datos

Crea un prompt Manual CoT para analizar si un número de ventas es un outlier estadístico dado un conjunto de datos.

Ver solución
OUTLIER_COT_PROMPT = """Determina si un valor es un outlier estadístico usando el método IQR.

Q: Ventas diarias de los últimos 7 días: [120, 135, 128, 142, 118, 131, 890]. ¿Es 890 un outlier?
A: Vamos paso a paso.
Ordenar datos: 118, 120, 128, 131, 135, 142, 890.
Calcular cuartiles:
- Q1 (percentil 25): posición 2 = 120.
- Q2 (mediana): posición 4 = 131.
- Q3 (percentil 75): posición 6 = 142.
Calcular IQR: IQR = Q3 - Q1 = 142 - 120 = 22.
Calcular límites:
- Límite inferior: Q1 - 1.5×IQR = 120 - 33 = 87.
- Límite superior: Q3 + 1.5×IQR = 142 + 33 = 175.
Verificar: ¿890 > 175? Sí. 890 >> 175.
Respuesta: SÍ, 890 es un outlier estadístico (supera el límite superior de 175 por un factor de ~5x).

Q: {datos_y_valor}
A:"""

Ejercicio 4: Comparar accuracy de Manual CoT vs. Zero-Shot CoT

Usa los mismos 10 problemas con ambos enfoques y mide cuál es más preciso en tu dominio.

Ver solución
from openai import OpenAI

client = OpenAI()

# Diseña tus propios ejemplos para el dominio que te interese
MANUAL_EJEMPLOS = """
Q: [Ejemplo 1 relevante para tu dominio]
A: [Razonamiento 1]
Respuesta: [R1]

Q: [Ejemplo 2 relevante]
A: [Razonamiento 2]
Respuesta: [R2]
"""

problemas_benchmark = [
    ("Problema 1", "Respuesta 1"),
    # ... 10 problemas con respuestas conocidas
]

def resolver_manual_cot(p):
    prompt = MANUAL_EJEMPLOS + f"\nQ: {p}\nA:"
    r = client.chat.completions.create(
        model="gpt-4o-mini",
        messages=[{"role": "user", "content": prompt}],
        temperature=0, max_tokens=500
    )
    return r.choices[0].message.content

def resolver_zero_shot_cot(p):
    r = client.chat.completions.create(
        model="gpt-4o-mini",
        messages=[{"role": "user", "content": f"{p}\n\nPiensa paso a paso."}],
        temperature=0, max_tokens=500
    )
    return r.choices[0].message.content

manual_ok = sum(
    1 for p, correcta in problemas_benchmark
    if correcta in resolver_manual_cot(p)
)
zero_ok = sum(
    1 for p, correcta in problemas_benchmark  
    if correcta in resolver_zero_shot_cot(p)
)

n = len(problemas_benchmark)
print(f"Manual CoT: {manual_ok}/{n} ({manual_ok/n:.0%})")
print(f"Zero-Shot CoT: {zero_ok}/{n} ({zero_ok/n:.0%})")

Ejercicio 5: Detectar y corregir un ejemplo con error

El siguiente ejemplo de Manual CoT tiene un error. Encuéntralo y corrígelo:

Q: Un producto cuesta $80 con descuento del 25%. ¿Cuál es el precio original?
A: Precio con descuento: $80.
Descuento: 25%.
Precio original = 80 + 25% = 80 + 20 = $100.
Respuesta: $100.
Ver solución

Error encontrado: La operación "80 + 25% de 80" no es matemáticamente correcta para encontrar el precio original.

Razonamiento correcto:

Precio con descuento: $80.
El descuento del 25% significa que $80 representa el 75% del precio original (100% - 25%).
Precio original = 80 / 0.75 = $106.67.

Verificación: 106.67 × 0.25 = $26.67 de descuento. 106.67 - 26.67 = $80 ✓.
Respuesta: $106.67.

El error es confundir "añadir el porcentaje del precio descontado" con la operación inversa correcta. Para pasar de precio descontado a precio original se debe dividir por (1 - descuento).

# Ejemplo corregido
ejemplo_descuento_correcto = EjemploCoT(
    input_problema="Un producto cuesta $80 con descuento del 25%. ¿Cuál es el precio original?",
    razonamiento="""Precio descontado: $80.
Porcentaje que representa: 100% - 25% = 75% del precio original.
Precio original = 80 / 0.75 = 106.67.
Verificación: 106.67 × 0.25 = 26.67. 106.67 - 26.67 = 80 ✓""",
    respuesta="$106.67"
)

Resumen

  • Manual CoT usa ejemplos con Reasoning + Answer explícitos para guiar al modelo
  • Formato estándar: Input → Razonamiento paso a paso → Respuesta
  • 2-3 ejemplos suelen bastar; más no siempre mejora
  • Por dominio: Math, lógica, código, sentimiento, negocios requieren estilos distintos
  • Calidad > cantidad: Ejemplos incorrectos dañan el rendimiento; verifica siempre
  • vs. Zero-Shot: Mayor esfuerzo de diseño, mayor consistencia y control

Recursos adicionales

  1. Chain-of-Thought Prompting Elicits Reasoning (Wei et al., 2022)
  2. Complexity-Based Prompting for Multi-Step Reasoning (Fu et al., 2022)
  3. Automatic Chain of Thought Prompting (Zhang et al., 2022)
  4. Few-Shot Prompting - Learn Prompting
  5. OpenAI Cookbook: Techniques to improve reliability