Descomponiendo un prompt funcional y eficiente

19 de noviembre de 2024
7 min lectura
By Sly & AI

Tabla de Contenidos

Secciones de esta publicación.
Haz clic en cualquiera de ellas para ir directamente a esa sección.

Escribir prompts no es magia. Es ingeniería. La diferencia entre un prompt mediocre y uno excelente puede ser la diferencia entre código que funciona y código que necesitas reescribir tres veces.

He iterado miles de prompts en producción. Estos son los patrones que funcionan consistentemente, con ejemplos reales y comparativas lado a lado.

La Anatomía de un Buen Prompt

Un prompt efectivo tiene cinco componentes:

  1. Contexto: Quién es el modelo y qué sabe
  2. Tarea: Qué necesitas que haga
  3. Formato: Cómo debe estructurar la respuesta
  4. Restricciones: Qué NO debe hacer
  5. Ejemplos: Casos concretos (cuando aplica)

Vamos componente por componente.

1. Contexto: Establecer el Rol

❌ Prompt Malo

Genera código para un componente de login

✅ Prompt Bueno

Eres un desarrollador senior de React especializado en TypeScript y Next.js 14.
Sigues las mejores prácticas de accesibilidad y seguridad.
Genera un componente de login usando:
- Next.js 14 App Router
- Server Actions para el form
- shadcn/ui para los componentes
- Zod para validación
- React Hook Form para manejo del estado

Diferencia en output:

El primero te da un componente genérico con useState y fetch. El segundo te da exactamente lo que necesitas con el stack correcto.

2. Tarea: Ser Específico Sin Ser Verboso

❌ Prompt Malo

Explícame cómo funciona React

Demasiado amplio. Obtienes una explicación genérica que no te sirve.

❌ Prompt Malo (en la otra dirección)

Necesito que me expliques el ciclo de vida completo de React, incluyendo
cada fase del render, reconciliation, commit phase, passive effects,
layout effects, y cómo funcionan los hooks en cada una de estas fases,
con detalles sobre el fiber tree y la arquitectura interna...

Demasiado específico. Terminas con información que no necesitas.

✅ Prompt Bueno

Explica cómo React renderiza componentes desde que cambias el estado
hasta que se actualiza el DOM. Usa un ejemplo concreto con useState.
Máximo 3 párrafos.

Específico, acotado, con ejemplo concreto.

3. Formato: Estructura la Respuesta

Ejemplo Real: Generando Documentación de API

❌ Sin Formato

Documenta esta función:
async function createUser(data) {
const user = await db.user.create({ data })
return user
}

Output: Párrafo desestructurado que mezcla parámetros, retornos y descripción.

✅ Con Formato

Documenta esta función usando el siguiente formato:
 
## Descripción
[Una línea]
 
## Parámetros
- `nombre` (tipo): descripción
 
## Retorna
- tipo: descripción
 
## Ejemplo de uso
```typescript
// código
```
 
## Errores posibles
- ErrorName: cuándo ocurre
 
Función a documentar:
async function createUser(data) {
  const user = await db.user.create({ data })
  return user
}
 
## Errores posibles
- ErrorName: cuándo ocurre
 
Función a documentar:
async function createUser(data) {
  const user = await db.user.create({ data })
  return user
}
```
 
Output: Documentación estructurada, consistente y fácil de leer.
 
## 4. Restricciones: Lo Que NO Debe Hacer
 
Este es el secreto que pocos usan. Decirle al modelo qué NO hacer es tan importante como decirle qué hacer.
 
### Ejemplo Real: Generando Tests
 
### ❌ Sin Restricciones
 
```
Genera tests para esta función:
 
function sum(a, b) {
  return a + b
}
```
 
Output: 20 tests incluyendo casos ridículos como "debería sumar números negativos imaginarios en sistemas no-euclidianos"
 
### ✅ Con Restricciones
 
```
Genera tests para esta función:
 
function sum(a, b) {
  return a + b
}
 
Restricciones:
- Máximo 5 tests
- Solo casos realistas
- No uses números mayores a 1000
- No incluyas edge cases de tipos (eso se maneja con TypeScript)
- Usa describe/it de Jest
```
 
Output: Exactamente lo que necesitas, sin fluff.
 
## 5. Ejemplos: Few-Shot Prompting
 
Cuando quieres consistencia, dale ejemplos concretos.
 
### Caso Real: Generando Mensajes de Commit
 
### Zero-Shot (sin ejemplos)
 
```
Genera un mensaje de commit para estos cambios:
- Agregué validación de email
- Arreglé el bug del formulario
- Actualicé estilos
```
 
Output: "Updated form with email validation and styling fixes"
 
Meh. No sigue convenciones.
 
### Few-Shot (con ejemplos)
 
```
Genera mensajes de commit siguiendo estos ejemplos:
 
Ejemplo 1:
Cambios: Agregué autenticación con Google
Output: feat(auth): add Google OAuth integration
 
Ejemplo 2:
Cambios: Arreglé memory leak en el componente
Output: fix(components): resolve memory leak in UserList
 
Ejemplo 3:
Cambios: Actualicé README con nuevas instrucciones
Output: docs(readme): update setup instructions
 
Ahora genera para estos cambios:
- Agregué validación de email
- Arreglé el bug del formulario
- Actualicé estilos
```
 
Output:
```
feat(forms): add email validation
fix(forms): resolve submit bug
style(forms): update input styling
```
 
Perfecto. Consistente. Útil.
 
## Técnicas Avanzadas
 
### Chain of Thought (Razonamiento Paso a Paso)
 
Para problemas complejos, pídele que piense en voz alta.
 
```
Tengo este código que es lento:
 
function findDuplicates(arr) {
  const duplicates = []
  for (let i = 0; i < arr.length; i++) {
    for (let j = i + 1; j < arr.length; j++) {
      if (arr[i] === arr[j] && !duplicates.includes(arr[i])) {
        duplicates.push(arr[i])
      }
    }
  }
  return duplicates
}
 
Analiza paso a paso:
1. ¿Cuál es la complejidad actual?
2. ¿Dónde están los cuellos de botella?
3. ¿Qué estructura de datos mejoraría esto?
4. Implementa la solución optimizada
```
 
Output:
```
1. Complejidad actual: O(n³)
   - Doble loop: O(n²)
   - includes() en cada iteración: O(n)
   - Total: O(n³)
 
2. Cuellos de botella:
   - Búsqueda lineal con includes()
   - Comparaciones redundantes
 
3. Estructura de datos mejor: Set
   - Búsqueda O(1)
   - Elimina duplicados automáticamente
 
4. Solución optimizada:
function findDuplicates(arr) {
  const seen = new Set()
  const duplicates = new Set()
  
  for (const item of arr) {
    if (seen.has(item)) {
      duplicates.add(item)
    }
    seen.add(item)
  }
  
  return Array.from(duplicates)
}
// Complejidad: O(n)
```
 
### Self-Consistency (Múltiples Perspectivas)
 
Para decisiones arquitectónicas:
 
```
Necesito decidir entre:
1. Monorepo con Turborepo
2. Múltiples repos independientes
 
Dame 3 análisis diferentes:
 
Análisis 1: Desde la perspectiva de un CTO preocupado por costos
Análisis 2: Desde la perspectiva de un developer que valora DX
Análisis 3: Desde la perspectiva de DevOps preocupado por CI/CD
 
Finalmente, dame una recomendación sintetizando los tres.
```
 
### Tree of Thoughts (Explorar Opciones)
 
Para generar alternativas:
 
```
Necesito nombrar una función que:
- Valida y transforma datos de usuario
- Hace sanitización
- Convierte a formato interno
 
Genera 5 nombres diferentes siguiendo cada uno de estos criterios:
 
1. Descriptivo y explícito (claridad sobre brevedad)
2. Conciso y común en la industria
3. Basado en verbos de acción
4. Siguiendo naming conventions de Domain-Driven Design
5. Tu mejor sugerencia considerando todo
 
Para cada uno explica en una línea por qué ese nombre.
```
 
Output estructurado con opciones reales para evaluar.
 
## Prompts para Casos de Uso Comunes
 
### Generación de Código
 
```typescript
// Prompt template para componentes
const componentPrompt = `
Genera un componente de React siguiendo estas especificaciones:
 
Contexto:
- Next.js 14 App Router
- TypeScript estricto
- Tailwind CSS
- Cliente component (usa 'use client')
 
Requisitos funcionales:
${requirements}
 
Requisitos técnicos:
- Props con TypeScript interface
- Manejo de estados de loading/error
- Accesibilidad (ARIA labels)
- Responsive design
 
Restricciones:
- No uses librerías externas excepto las mencionadas
- Máximo 100 líneas
- Incluye comentarios solo para lógica compleja
 
Formato de respuesta:
1. Interface de Props
2. Componente
3. Ejemplo de uso
`
 
// Uso
const code = await generateAI(
  componentPrompt.replace('${requirements}', 'Un selector de fecha con rango')
)
```
 
### Code Review
 
```typescript
const reviewPrompt = `
Revisa este código como un senior developer:
 
${code}
 
Analiza:
1. Bugs potenciales
2. Problemas de performance
3. Vulnerabilidades de seguridad
4. Code smells
5. Mejoras en legibilidad
 
Para cada issue:
- Severidad (🔴 crítico, 🟡 moderado, 🟢 sugerencia)
- Línea aproximada
- Explicación
- Código corregido
 
No menciones issues que no existan. Sé directo.
`
```
 
### Debugging
 
```typescript
const debugPrompt = `
Tengo este error:
 
Error: ${errorMessage}
 
Stack trace:
${stackTrace}
 
Código relevante:
${code}
 
Estado de la aplicación:
${state}
 
Analiza paso a paso:
1. ¿Qué está causando el error exactamente?
2. ¿Por qué ocurre en este contexto?
3. ¿Cómo lo reproduzco consistentemente?
4. Dame 2-3 soluciones posibles ordenadas por probabilidad
5. ¿Cómo prevengo esto en el futuro?
 
No especules. Si no estás seguro, dilo.
`
```
 
### Optimización
 
```typescript
const optimizationPrompt = `
Este código funciona pero es lento:
 
${code}
 
Métricas actuales:
${metrics}
 
Optimiza considerando:
1. Complejidad algorítmica
2. Uso de memoria
3. Network requests (si aplica)
4. Re-renders (si es React)
 
Para cada optimización propuesta:
- Impacto estimado (alto/medio/bajo)
- Trade-offs
- Código optimizado
- Cómo medir la mejora
 
Prioriza optimizaciones con mayor ROI.
`
```
 
## Anti-Patterns: Lo Que NO Funciona
 
### 1. Prompts Demasiado Educados
 
```
❌ Por favor, si no es mucha molestia, podrías ayudarme a generar...
 
✅ Genera un componente de...
```
 
La cortesía no mejora el output. Sé directo.
 
### 2. Preguntas Retóricas
 
```
❌ ¿No crees que sería mejor usar TypeScript aquí?
 
✅ Usa TypeScript para este código.
```
 
No hagas preguntas si quieres una acción específica.
 
### 3. Contexto Irrelevante
 
```
❌ Estoy trabajando en un proyecto muy importante para mi startup que va a
revolucionar la industria del e-commerce con blockchain y AI. Necesito que
me ayudes a generar un botón...
 
✅ Genera un botón con estas características...
```
 
El modelo no se impresiona con tu pitch. Ve al grano.
 
### 4. Múltiples Tareas en Un Prompt
 
```
❌ Genera un componente de login, explica cómo funciona OAuth, dame mejores
prácticas de seguridad y también genera tests.
 
✅ [Un prompt para cada tarea]
```
 
Una tarea por prompt. Mejor calidad en cada una.
 
## Testing de Prompts: Cómo Iterar
 
No adivines. Mide. Así itero prompts en producción:
 
```typescript
// Función para testear prompts
async function testPrompt(
  prompt: string,
  testCases: Array<{ input: string; expected: string }>,
  model: AIModel = 'claude-3-5-sonnet'
) {
  const results = []
  
  for (const testCase of testCases) {
    const output = await generateAI(
      prompt.replace('${input}', testCase.input),
      model
    )
    
    const similarity = calculateSimilarity(output.text, testCase.expected)
    
    results.push({
      input: testCase.input,
      output: output.text,
      expected: testCase.expected,
      similarity,
      passed: similarity > 0.8
    })
  }
  
  const passRate = results.filter(r => r.passed).length / results.length
  
  return { results, passRate }
}
 
// Uso
const testCases = [
  {
    input: 'función que suma dos números',
    expected: 'function sum(a: number, b: number): number'
  },
  // más casos...
]
 
const { passRate } = await testPrompt(myPrompt, testCases)
console.log(`Pass rate: ${passRate * 100}%`)
```
 
## Template de Prompt Universal
 
Esto funciona para el 80% de casos:
 
```
[CONTEXTO]
Eres un [rol] experto en [tecnologías].
Sigues [estándares/principios].
 
[TAREA]
[Verbo de acción] [objeto] que [requisitos].
 
[FORMATO]
Estructura la respuesta así:
[estructura específica]
 
[RESTRICCIONES]
- No [restricción 1]
- Máximo [límite]
- Solo [alcance]
 
[EJEMPLOS] (opcional)
Ejemplo: [input] → [output esperado]
 
[INPUT]
[Datos concretos con los que trabajar]
```
 
## Ejemplo Real: Prompt en Producción
 
Este es un prompt que uso en un SaaS real para generar copy de marketing:
 
```typescript
const marketingCopyPrompt = `
Eres un copywriter experto en SaaS B2B con 10+ años de experiencia.
Tu estilo es directo, orientado a beneficios, sin fluff.
 
Genera copy para [${section}] siguiendo estos principios:
- Enfócate en el outcome, no en features
- Usa voz activa
- Incluye números cuando sea posible
- Máximo 2 frases por párrafo
 
Target audience:
- Rol: ${targetRole}
- Pain point: ${painPoint}
- Nivel técnico: ${technicalLevel}
 
Formato:
1. Headline (máximo 60 caracteres)
2. Subheadline (máximo 120 caracteres)
3. Body (2-3 párrafos cortos)
4. CTA (máximo 30 caracteres)
 
Restricciones:
- No uses: "revolutionize", "game-changing", "cutting-edge"
- No exageres beneficios no comprobados
- No hagas comparaciones directas con competidores
 
Producto:
${productDescription}
 
Genera 3 variaciones para A/B testing.
`
 
// Este prompt tiene 95%+ de aprobación sin edición manual
```
 
## Conclusión: Principios Fundamentales
 
1. **Claridad > Brevedad:** Mejor un prompt largo y claro que uno corto y ambiguo
2. **Ejemplos > Explicaciones:** Mostrar es más efectivo que describir
3. **Restricciones > Libertad:** Los límites producen mejor output
4. **Iteración > Perfección:** El mejor prompt es el que has refinado 10 veces
5. **Medición > Intuición:** Testea, no adivines
 
El prompt engineering no es un arte. Es experimentación sistemática. Empieza con un prompt simple, mide el output, refina, repite.
 
Y recuerda: el mejor prompt es el que te hace el trabajo más rápido y mejor que si lo hicieras tú solo. Si no cumple eso, sigue iterando.