Contexto, restricciones, salida: una plantilla de prompt para código de IA fiable
Los prompts vagos dan código vago. Usa una plantilla en tres partes —contexto, restricciones y formato de salida— para obtener funciones listas para producción al primer intento.
Pide a un asistente de IA que «escriba una función que valide la entrada del usuario» y lo que recibirás a menudo es texto con forma de código: se ejecuta, tiene una firma y no hace nada de lo que necesitabas. No hay gestión de errores ni pista alguna de lo que se suponía que significaba «válido».
Ese resultado suele achacarse al modelo, pero la mayoría de las veces la culpa es del prompt. Trata un prompt como un contrato de API en lugar de como una petición informal y la calidad de la salida se dispara, sin que el modelo se haya vuelto más listo. Simplemente ha dejado de verse obligado a adivinar. A continuación tienes una estructura en tres partes que corrige el problema.
Por qué los prompts vagos dan código vago
Ante una petición imprecisa, el modelo rellena cada hueco con una suposición, y esas suposiciones rara vez coinciden con las tuyas. ¿Qué versión del lenguaje? ¿Qué convención de gestión de errores sigue tu equipo? ¿Tiene que ser seguro ante accesos concurrentes? ¿Un fallo debe devolver un error o provocar un panic? Una petición de una línea no responde a nada de esto, así que el modelo recurre al patrón más común que ha visto. En una base de código real, ese patrón común suele ser el equivocado, y la diferencia solo aparece en la revisión o, peor aún, en producción.
No hay ninguna frase mágica que lo arregle. El remedio es aportar lo que se le cuenta a un ingeniero nuevo el primer día: contexto, restricciones y una definición clara de «terminado».
Las tres secciones
Todo prompt de generación de código se divide en tres partes, en este orden.
- Contexto: describe dónde va a vivir el código: lenguaje y versión, framework, patrones con los que debe encajar y dependencias que ya se usan.
- Restricciones: enumeran las reglas innegociables, como el estilo de gestión de errores, los objetivos de rendimiento, la seguridad ante concurrencia, la validación de entradas y lo que hay que evitar (nada de bibliotecas externas, nada de estado global, etcétera).
- Formato de salida: indica cómo debe llegar la respuesta. ¿Solo la función, o la función más un ejemplo de uso? ¿Con comentarios o sin ellos? ¿Con tests?
Si falta una, algo sale mal. Sin contexto obtienes código que no encaja con tu stack. Sin restricciones obtienes código que «funciona» pero ignora tus convenciones. Sin formato de salida, las quince líneas que querías llegan envueltas en tres párrafos de explicación.
La misma tarea, dos prompts
Una comparación concreta hace que la diferencia salte a la vista, así que aquí tienes una tarea escrita de las dos maneras.
La versión vaga de una petición tiene este aspecto:
Escribe una función que procese un lote de pedidos.
Ahora la versión estructurada:
Context: Go 1.22 service, part of an order-processing microservice.
Uses the existing Order struct (ID string, Amount float64, Status string).
Errors are wrapped with fmt.Errorf and %w, never panicked.
Constraints:
- Process orders concurrently, max 10 goroutines at a time
- If any single order fails, log it and continue (don't abort the batch)
- Return a slice of errors for failed orders, not a single error
- No third-party libraries, standard library only
Output format: just the function and a short usage example, no explanation.
Al primer intento, produjo esto:
func ProcessOrders(ctx context.Context, orders []Order, process func(Order) error) []error {
sem := make(chan struct{}, 10)
var wg sync.WaitGroup
var mu sync.Mutex
var errs []error
for _, o := range orders {
wg.Add(1)
sem <- struct{}{}
go func(o Order) {
defer wg.Done()
defer func() { <-sem }()
if err := process(o); err != nil {
mu.Lock()
errs = append(errs, fmt.Errorf("order %s: %w", o.ID, err))
mu.Unlock()
}
}(o)
}
wg.Wait()
return errs
}
// usage
errs := ProcessOrders(ctx, orders, chargeOrder)
for _, e := range errs {
log.Println(e)
}
Se puede usar tal cual. El prompt no tenía nada de ingenioso. Simplemente eliminó cada ambigüedad que el modelo habría resuelto por su cuenta: el límite de concurrencia, el comportamiento ante fallos, el formato de los errores y el alcance de las dependencias.
Funciona en cualquier lenguaje
La plantilla no está atada a un lenguaje. Estas son las mismas tres secciones en otro stack:
Context: Python 3.11 data pipeline script, uses pandas, runs as a scheduled job.
Constraints:
- Validate that required columns exist before processing, raise ValueError with column name if missing
- Handle empty DataFrames without crashing (return empty result, log a warning)
- No mutation of the input DataFrame
Output format: function only, type hints included, no docstring needed.
Y el resultado:
import pandas as pd
import logging
def clean_transactions(df: pd.DataFrame, required: list[str]) -> pd.DataFrame:
missing = [col for col in required if col not in df.columns]
if missing:
raise ValueError(f"missing required column(s): {', '.join(missing)}")
if df.empty:
logging.warning("received empty DataFrame, returning empty result")
return df.copy()
result = df.copy()
result = result.dropna(subset=required)
return result
La sintaxis cambia, pero la disciplina es idéntica. Funciona en todos los lenguajes porque ataca el verdadero punto débil del modelo: rellenar los huecos con valores genéricos en lugar de con los tuyos.
¿De verdad ayuda la estructura?
Un recuento informal sobre un par de docenas de prompts, anotando cuántas veces una función generada necesitó retoques antes de fusionarse, dio estas cifras aproximadas:
Vague prompts |################........| ~80% needed rework
Context only |##############..........| ~55% needed rework
Context + constraints |######...................| ~25% needed rework
All three sections |##.......................| ~10% needed rework
No es un experimento controlado, solo un registro personal, pero la tendencia fue constante. Dedicar diez segundos a las restricciones cuesta menos que pasar diez minutos depurando código que dio por hecho algo equivocado sin avisar, incluso en scripts de usar y tirar.
Pequeños hábitos que compensan
Especifica el formato de salida. Si omites algo como «solo el código», la respuesta llegará cada vez envuelta en una explicación que tendrás que saltarte. Eso añade fricción cuando iteras rápido.
Usa restricciones negativas. «Sin bibliotecas de terceros» o «aquí no uses recursión» eliminan toda una categoría de respuestas que, de otro modo, tendrías que rechazar y volver a pedir. Descartar de antemano es mejor que corregir después.
Acumula restricciones en las siguientes peticiones. Si la primera respuesta se acerca, no empieces de cero. Añade una restricción más y pide una revisión. «La misma función, pero haz configurable el límite de goroutines» llega más lejos que un prompt nuevo, porque el modelo ya tiene una versión funcional como punto de partida.
Nada de esto sustituye leer el código que te devuelve. Solo deja menos que arreglar.