🤖 Developer Cookbook - FASE 6: Ingeniería de Prompts
Recetas prácticas para diseñar prompts efectivos y usar AI coding assistants
📚 Tabla de Contenidos
- Receta 6.5: Zero-shot, Few-shot, Chain-of-Thought
- Receta 6.6: Optimización de Prompts para Código
- Receta 6.7: GitHub Copilot, Cursor, ChatGPT - Uso Efectivo
Ingeniería de Prompts
Receta 6.5: Zero-shot, Few-shot, Chain-of-Thought
Definiciones:
| Técnica | Definición | Cuándo usar |
|---|---|---|
| Zero-shot | Sin ejemplos, solo instrucciones | Tareas simples y claras |
| Few-shot | Con 1-5 ejemplos | Formato específico o patrón |
| Chain-of-Thought (CoT) | Razonamiento paso a paso | Problemas complejos, matemáticas |
Ejemplos prácticos:
from anthropic import Anthropic
client = Anthropic(api_key="your-api-key")
# 1. ZERO-SHOT: Sin ejemplos
def zero_shot_example():
"""Clasificación sin ejemplos"""
prompt = """
Clasifica el siguiente comentario como POSITIVO, NEGATIVO o NEUTRAL:
"El producto llegó a tiempo pero la calidad es mediocre"
"""
response = client.messages.create(
model="claude-sonnet-4-20250514",
max_tokens=50,
messages=[{"role": "user", "content": prompt}]
)
return response.content[0].text
# 2. FEW-SHOT: Con ejemplos
def few_shot_example():
"""Clasificación con ejemplos"""
prompt = """
Clasifica comentarios de productos como POSITIVO, NEGATIVO o NEUTRAL.
Ejemplos:
Comentario: "¡Excelente producto! Superó mis expectativas"
Clasificación: POSITIVO
Comentario: "Terrible, se rompió al primer uso"
Clasificación: NEGATIVO
Comentario: "Es un producto estándar, nada especial"
Clasificación: NEUTRAL
Comentario: "Buen precio pero la entrega tardó mucho"
Clasificación: NEUTRAL
Ahora clasifica este:
Comentario: "El producto llegó a tiempo pero la calidad es mediocre"
Clasificación:
"""
response = client.messages.create(
model="claude-sonnet-4-20250514",
max_tokens=50,
messages=[{"role": "user", "content": prompt}]
)
return response.content[0].text
# 3. CHAIN-OF-THOUGHT: Razonamiento paso a paso
def chain_of_thought_example():
"""Resolver problema con razonamiento"""
prompt = """
Resuelve el siguiente problema PASO A PASO:
"Si un tren viaja a 120 km/h y necesita recorrer 360 km, pero hace una parada
de 30 minutos a mitad de camino, ¿cuánto tiempo total tardará el viaje?"
Piensa paso a paso:
1. Primero, calcula el tiempo de viaje sin paradas
2. Luego, suma el tiempo de la parada
3. Finalmente, da la respuesta total
"""
response = client.messages.create(
model="claude-sonnet-4-20250514",
max_tokens=300,
messages=[{"role": "user", "content": prompt}]
)
return response.content[0].text
# Ejecutar ejemplos
print("=== ZERO-SHOT ===")
print(zero_shot_example())
print("\n=== FEW-SHOT ===")
print(few_shot_example())
print("\n=== CHAIN-OF-THOUGHT ===")
print(chain_of_thought_example())
Comparación de efectividad:
import time
def benchmark_prompting_techniques(test_cases: list):
"""
Comparar efectividad de diferentes técnicas
"""
results = {
'zero_shot': {'correct': 0, 'total': 0, 'avg_time': 0},
'few_shot': {'correct': 0, 'total': 0, 'avg_time': 0},
'cot': {'correct': 0, 'total': 0, 'avg_time': 0}
}
for test_case in test_cases:
question = test_case['question']
expected = test_case['answer']
# Zero-shot
start = time.time()
zero_shot_answer = get_zero_shot_answer(question)
results['zero_shot']['total'] += 1
results['zero_shot']['avg_time'] += time.time() - start
if zero_shot_answer == expected:
results['zero_shot']['correct'] += 1
# Few-shot
start = time.time()
few_shot_answer = get_few_shot_answer(question)
results['few_shot']['total'] += 1
results['few_shot']['avg_time'] += time.time() - start
if few_shot_answer == expected:
results['few_shot']['correct'] += 1
# Chain-of-Thought
start = time.time()
cot_answer = get_cot_answer(question)
results['cot']['total'] += 1
results['cot']['avg_time'] += time.time() - start
if cot_answer == expected:
results['cot']['correct'] += 1
# Calcular métricas
for technique in results:
if results[technique]['total'] > 0:
results[technique]['accuracy'] = (
results[technique]['correct'] / results[technique]['total']
)
results[technique]['avg_time'] /= results[technique]['total']
return results
Cuándo usar cada técnica:
class PromptingStrategy:
"""
Seleccionar estrategia de prompting automáticamente
"""
@staticmethod
def select_strategy(task_type: str, complexity: str) -> str:
"""
Recomendar técnica de prompting basada en tarea
"""
strategies = {
# Tareas simples
('classification', 'low'): 'zero_shot',
('translation', 'low'): 'zero_shot',
('summarization', 'low'): 'zero_shot',
# Tareas con formato específico
('extraction', 'medium'): 'few_shot',
('formatting', 'medium'): 'few_shot',
('style_transfer', 'medium'): 'few_shot',
# Tareas complejas
('math', 'high'): 'chain_of_thought',
('reasoning', 'high'): 'chain_of_thought',
('planning', 'high'): 'chain_of_thought',
('analysis', 'high'): 'chain_of_thought',
}
key = (task_type, complexity)
return strategies.get(key, 'few_shot') # Default
@staticmethod
def build_prompt(strategy: str, task: str, examples: list = None) -> str:
"""
Construir prompt según estrategia
"""
if strategy == 'zero_shot':
return f"Realiza la siguiente tarea:\n\n{task}"
elif strategy == 'few_shot':
prompt = "Aquí hay algunos ejemplos:\n\n"
for ex in examples:
prompt += f"Input: {ex['input']}\nOutput: {ex['output']}\n\n"
prompt += f"Ahora realiza:\nInput: {task}\nOutput:"
return prompt
elif strategy == 'chain_of_thought':
return f"""
Resuelve paso a paso:
{task}
Muestra tu razonamiento:
Paso 1:
Paso 2:
...
Respuesta final:
"""
return task
# Uso
task_type = 'math'
complexity = 'high'
strategy = PromptingStrategy.select_strategy(task_type, complexity)
print(f"Estrategia recomendada: {strategy}")
prompt = PromptingStrategy.build_prompt(
strategy,
"Si compro 3 productos a $15 cada uno y tengo un descuento del 20%, ¿cuánto pago?"
)
print(f"\nPrompt generado:\n{prompt}")
Receta 6.6: Optimización de Prompts para Código
Técnicas para mejorar generación de código:
# ❌ PROMPT MALO: Vago e impreciso
bad_prompt = "Escribe código para API"
# ✅ PROMPT BUENO: Específico y estructurado
good_prompt = """
Crea un endpoint REST API en Python usando Flask con las siguientes especificaciones:
Endpoint: POST /api/users
Funcionalidad: Crear nuevo usuario en base de datos
Input JSON:
{
"name": "string (requerido)",
"email": "string (requerido, debe ser email válido)",
"age": "integer (opcional, debe ser > 0)"
}
Requisitos:
1. Validar input con pydantic
2. Hash password con bcrypt
3. Guardar en SQLite usando SQLAlchemy
4. Retornar 201 con ID del usuario creado
5. Manejar errores (email duplicado, validación)
6. Incluir docstring y type hints
Formato de respuesta exitosa:
{
"id": 123,
"message": "Usuario creado exitosamente"
}
"""
# Ejecutar con LLM
from anthropic import Anthropic
client = Anthropic(api_key="your-api-key")
response = client.messages.create(
model="claude-sonnet-4-20250514",
max_tokens=2000,
messages=[{"role": "user", "content": good_prompt}]
)
print(response.content[0].text)
Template para prompts de código:
class CodePromptBuilder:
"""
Constructor de prompts estructurados para generación de código
"""
def __init__(self):
self.template = """
Genera código {language} con las siguientes especificaciones:
📋 DESCRIPCIÓN:
{description}
🎯 FUNCIONALIDAD:
{functionality}
📥 INPUT:
{input_spec}
📤 OUTPUT:
{output_spec}
✅ REQUISITOS:
{requirements}
📚 DEPENDENCIAS:
{dependencies}
⚠️ MANEJO DE ERRORES:
{error_handling}
📝 ADICIONAL:
{additional_notes}
"""
def build(self,
language: str,
description: str,
functionality: str,
input_spec: str,
output_spec: str,
requirements: list,
dependencies: list = None,
error_handling: str = "Manejar excepciones comunes",
additional_notes: str = "Incluir type hints y docstrings") -> str:
"""
Construir prompt estructurado
"""
requirements_str = "\n".join([f"{i+1}. {req}" for i, req in enumerate(requirements)])
dependencies_str = ", ".join(dependencies) if dependencies else "Librería estándar"
return self.template.format(
language=language,
description=description,
functionality=functionality,
input_spec=input_spec,
output_spec=output_spec,
requirements=requirements_str,
dependencies=dependencies_str,
error_handling=error_handling,
additional_notes=additional_notes
)
# Uso
builder = CodePromptBuilder()
prompt = builder.build(
language="Python",
description="Sistema de cache con expiración automática",
functionality="Almacenar valores en memoria con TTL configurable",
input_spec="key (str), value (Any), ttl_seconds (int)",
output_spec="None al guardar, valor o None al recuperar",
requirements=[
"Usar threading para expiración automática",
"Thread-safe con locks",
"Método get() que retorna None si expiró",
"Método set() para guardar con TTL",
"Método clear() para limpiar todo"
],
dependencies=["threading", "time"],
error_handling="Validar tipos de datos, manejar race conditions",
additional_notes="Incluir tests unitarios con pytest"
)
print(prompt)
Prompts para debugging:
def generate_debug_prompt(buggy_code: str, error_message: str, expected_behavior: str) -> str:
"""
Generar prompt para debugging de código
"""
prompt = f"""
🐛 DEBUGGING ASSISTANCE
CÓDIGO CON ERROR:
```python
{buggy_code}
```
❌ ERROR RECIBIDO:
{error_message}
✅ COMPORTAMIENTO ESPERADO:
{expected_behavior}
Por favor:
1. Identifica el error exacto
2. Explica POR QUÉ ocurre el error
3. Proporciona el código corregido
4. Sugiere cómo prevenir este error en el futuro
5. Si es posible, añade un test para verificar la corrección
"""
return prompt
# Uso
buggy_code = """
def calculate_average(numbers):
total = sum(numbers)
return total / len(numbers)
result = calculate_average([])
print(result)
"""
error_msg = "ZeroDivisionError: division by zero"
debug_prompt = generate_debug_prompt(
buggy_code=buggy_code,
error_message=error_msg,
expected_behavior="Debería manejar listas vacías sin error"
)
# Enviar a LLM para debug
response = client.messages.create(
model="claude-sonnet-4-20250514",
max_tokens=1000,
messages=[{"role": "user", "content": debug_prompt}]
)
print("🔧 Solución:\n")
print(response.content[0].text)
Prompts para refactoring:
def generate_refactor_prompt(code: str, issues: list, style_guide: str = "PEP 8") -> str:
"""
Generar prompt para refactorización
"""
issues_str = "\n".join([f"- {issue}" for issue in issues])
prompt = f"""
♻️ REFACTORING REQUEST
CÓDIGO ACTUAL:
```python
{code}
```
🔍 PROBLEMAS IDENTIFICADOS:
{issues_str}
📏 GUÍA DE ESTILO: {style_guide}
Por favor refactoriza el código:
1. Aplica principios SOLID
2. Mejora nombres de variables/funciones
3. Reduce complejidad ciclomática
4. Añade type hints
5. Documenta con docstrings
6. Elimina código duplicado
7. Mejora legibilidad
Explica los cambios realizados y por qué mejoran el código.
"""
return prompt
# Uso
messy_code = """
def f(x):
r = []
for i in x:
if i > 0:
r.append(i * 2)
return r
"""
refactor_prompt = generate_refactor_prompt(
code=messy_code,
issues=[
"Nombres de variables no descriptivos",
"Falta documentación",
"No hay type hints",
"Lógica puede simplificarse"
]
)
print(refactor_prompt)
Receta 6.7: GitHub Copilot, Cursor, ChatGPT - Uso Efectivo
GitHub Copilot:
# TIPS para usar Copilot efectivamente:
# 1. Comentarios descriptivos
# ✅ BUENO: Copilot entiende lo que quieres
def validate_email(email: str) -> bool:
"""
Validate email address using regex.
Returns True if valid, False otherwise.
Must check for @ symbol, domain, and TLD.
"""
# Copilot generará una regex apropiada
pass
# ❌ MALO: Comentario vago
def check(x):
# validate
pass
# 2. Usar docstrings como guía
def calculate_compound_interest(
principal: float,
rate: float,
time: int,
compounds_per_year: int = 12
) -> float:
"""
Calculate compound interest using the formula:
A = P(1 + r/n)^(nt)
Args:
principal: Initial amount
rate: Annual interest rate (as decimal, e.g., 0.05 for 5%)
time: Time in years
compounds_per_year: Number of times interest compounds per year
Returns:
Final amount after compound interest
Example:
>>> calculate_compound_interest(1000, 0.05, 10, 12)
1647.01
"""
# Copilot generará implementación basada en docstring
pass
# 3. Nombrar funciones descriptivamente
def fetch_user_by_email_and_cache_result(email: str):
"""
Fetch user from database by email, cache the result in Redis
with 1 hour expiration, and return user object
"""
# Copilot entenderá: fetch DB -> cache Redis -> return
pass
# 4. Usar context de archivos relacionados
# Si tienes User model definido en otro archivo:
# from models import User
#
# Copilot usará el contexto del modelo para sugerir mejor código
Cursor AI:
# TIPS para Cursor:
# 1. Usar Ctrl+K para edits en contexto
# Ejemplo: Selecciona función y pide "Add error handling and logging"
# 2. Usar @-mentions para contexto
# @web "Latest FastAPI best practices 2024"
# @docs "SQLAlchemy relationship examples"
# @codebase "How do we handle authentication?"
# 3. Composer mode para archivos múltiples
# Cursor puede editar varios archivos simultáneamente:
# "Refactor user authentication to use JWT tokens across routes.py,
# models.py, and auth.py"
# 4. Chat para explicaciones
# Selecciona código complejo -> Cmd+L -> "Explain this algorithm step by step"
# Ejemplo de uso efectivo:
class UserService:
def __init__(self, db_session):
self.db = db_session
# Cursor command: "Add method to get user by email with caching"
# Cursor generará:
async def get_user_by_email(self, email: str) -> Optional[User]:
"""Fetch user by email with Redis caching"""
cache_key = f"user:email:{email}"
# Check cache first
cached = await redis_client.get(cache_key)
if cached:
return User.from_json(cached)
# Query database
user = await self.db.query(User).filter(User.email == email).first()
if user:
# Cache for 1 hour
await redis_client.setex(cache_key, 3600, user.to_json())
return user
ChatGPT/Claude para desarrollo:
# Estrategias de prompting para desarrollo:
# 1. ITERACIÓN: Empieza simple, refina
# Primera iteración:
prompt_v1 = "Create a function to validate passwords"
# Segunda iteración (más específica):
prompt_v2 = """
Create a Python function to validate passwords with these rules:
- At least 8 characters
- At least one uppercase letter
- At least one lowercase letter
- At least one number
- At least one special character (!@#$%^&*)
Return tuple (is_valid: bool, errors: list[str])
"""
# 2. PROPORCIONAR CONTEXTO
context_prompt = """
I have a Flask app with SQLAlchemy. I need to add a feature for users to
upload profile pictures. The images should be:
- Stored in AWS S3
- Resized to 200x200
- Original filename preserved
- URL saved in User model
Here's my current User model:
```python
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80), unique=True)
email = db.Column(db.String(120), unique=True)
```
Generate the complete solution including:
1. Updated User model
2. Upload endpoint
3. S3 integration
4. Image resizing logic
"""
# 3. PEDIR EXPLICACIONES
explanation_prompt = """
Explain this code line by line in simple terms:
```python
@lru_cache(maxsize=128)
def fibonacci(n):
if n < 2:
return n
return fibonacci(n-1) + fibonacci(n-2)
```
Focus on:
- What is @lru_cache?
- How does it improve performance?
- What are the trade-offs?
"""
# 4. COMPARAR ALTERNATIVAS
comparison_prompt = """
Compare these two approaches for handling async file uploads in FastAPI:
Approach 1: BackgroundTasks
Approach 2: Celery queue
For each, explain:
- Pros and cons
- When to use
- Sample code
- Performance implications
"""
# 5. DEBUGGING ASISTIDO
debug_prompt = """
This code is giving me unexpected results:
```python
def remove_duplicates(numbers):
return list(set(numbers))
result = remove_duplicates([3, 1, 4, 1, 5, 9, 2, 6, 5])
print(result) # Expected [3,1,4,5,9,2,6] but order is random
```
Why is the order random and how do I preserve original order while removing duplicates?
Provide solution with explanation.
"""
Framework para preguntas efectivas a AI assistants:
class AIAssistantQuery:
"""
Template para hacer preguntas efectivas a AI coding assistants
"""
@staticmethod
def build_query(
task: str,
context: str = "",
constraints: list = None,
examples: list = None,
format_preference: str = "code + explanation"
) -> str:
"""
Construir query estructurada
"""
query = f"🎯 TASK:\n{task}\n\n"
if context:
query += f"📋 CONTEXT:\n{context}\n\n"
if constraints:
query += "⚠️ CONSTRAINTS:\n"
query += "\n".join([f"- {c}" for c in constraints])
query += "\n\n"
if examples:
query += "📚 EXAMPLES:\n"
for i, ex in enumerate(examples, 1):
query += f"Example {i}:\n{ex}\n\n"
query += f"📄 FORMAT: {format_preference}\n"
return query
@staticmethod
def code_generation(description: str, language: str, **kwargs) -> str:
"""Helper para generación de código"""
return AIAssistantQuery.build_query(
task=f"Generate {language} code for: {description}",
format_preference="Working code with comments and example usage",
**kwargs
)
@staticmethod
def code_review(code: str, focus_areas: list = None) -> str:
"""Helper para code review"""
focus = focus_areas or ["bugs", "performance", "readability", "security"]
return AIAssistantQuery.build_query(
task="Review this code",
context=f"```\n{code}\n```",
constraints=[f"Focus on: {', '.join(focus)}"],
format_preference="Issues found + suggested improvements"
)
@staticmethod
def explain_code(code: str, level: str = "intermediate") -> str:
"""Helper para explicaciones"""
return AIAssistantQuery.build_query(
task=f"Explain this code for {level} developer",
context=f"```\n{code}\n```",
format_preference="Line-by-line explanation + high-level summary"
)
# Uso
query = AIAssistantQuery.code_generation(
description="Rate limiter decorator",
language="Python",
constraints=[
"Use Redis for distributed rate limiting",
"Support sliding window algorithm",
"Thread-safe",
"Must work with async functions"
],
examples=[
"@rate_limit(max_calls=100, window_seconds=60)\nasync def api_endpoint():\n pass"
]
)
print(query)