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:
- Especificar el estilo de razonamiento exacto que quieres
- Definir el nivel de detalle (paso a paso vs. saltos lógicos permitidos)
- Establecer el vocabulario técnico apropiado para tu dominio
- Controlar el formato de salida de manera precisa
- 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
+ 1innecesario. 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
| Aspecto | Zero-Shot CoT | Manual CoT |
|---|---|---|
| Esfuerzo de diseño | Mínimo | Alto |
| Tokens en prompt | Pocos | Muchos (ejemplos) |
| Consistencia de formato | Media | Alta |
| Control del razonamiento | Bajo | Alto |
| Generalización | Alta | Media (depende de ejemplos) |
| Mejor para | Prototipos, dominios generales | Producción, dominios específicos |
| Costo por llamada | Menor | Mayor (más tokens de input) |
Cuántos Ejemplos Necesitas
La investigación sugiere una curva de rendimientos decrecientes:
| Número de ejemplos | Mejora típica | Costo tokens |
|---|---|---|
| 0 (Zero-Shot) | Línea base | 0 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 ejemplos | Rendimientos mínimos | Muy 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