Capítulo 3 de 21 · básico
Fundamentos de prompting
Qué cubre esta sesión
En la semana uno conseguimos que el modelo nos devolviera una respuesta. También lo dejamos decidir todo sobre esa respuesta: qué tan larga, con qué tono, con qué forma. Esta hora se trata de recuperar ese control. El prompt es la única palanca que tienes sobre un sistema al que no puedes meterle mano por dentro, y casi toda la diferencia entre un juguete y una herramienta está en cómo la mueves.
Toda la lección cabe en un solo movimiento: separar lo que le dices al modelo en una instrucción de sistema y un mensaje de usuario. El system prompt fija el rol y las reglas. El mensaje de usuario lleva el input real. Mismo modelo, misma pregunta, pero la respuesta cambia por completo según lo que pongas en ese espacio de sistema. Escribí el ejemplo básico para hacer una misma pregunta dos veces — una a secas, otra con una instrucción de sistema — para que puedas leer las dos respuestas y ver la palanca en acción.
OpenAI
La Responses API separa las dos partes con claridad. El system prompt es el
argumento instructions; el texto del usuario va en input. Manda la pregunta
sin instructions y obtienes lo que al modelo se le antoje — normalmente uno o
dos párrafos rellenos y llenos de rodeos. Agrega una instrucción que fije el
rol y limite la longitud, y la misma pregunta regresa concisa y directa.
"""Week 2 - Prompting fundamentals (OpenAI).
One question, asked twice. First with nothing but the question. Then
with a system instruction that pins the role and the output shape. The
model is identical; only the prompt moves. Read the two answers and the
whole lesson is right there.
"""
import os
import sys
from dotenv import find_dotenv, load_dotenv
from openai import OpenAI
load_dotenv(find_dotenv())
if not os.environ.get("OPENAI_API_KEY"):
sys.exit("OPENAI_API_KEY is not set. Put it in the course-root .env file.")
client = OpenAI()
QUESTION = "Should I use a vector database for my app?"
# region: bare-call
# No system prompt. The model picks its own role, length, and format.
bare = client.responses.create(model="gpt-5.4-nano", input=QUESTION)
# endregion
# region: system-call
# The system instruction sets the role AND pins the output shape. In the
# Responses API the system prompt is the `instructions` argument; the
# user's text stays in `input`.
SYSTEM = (
"You are a senior backend engineer. Answer in at most three sentences. "
"Lead with a direct yes/no/it-depends, then the single deciding factor. "
"No preamble, no bullet points."
)
guided = client.responses.create(
model="gpt-5.4-nano", instructions=SYSTEM, input=QUESTION,
)
# endregion
print("=== bare prompt ===\n")
print(bare.output_text)
print("\n=== with a system instruction ===\n")
print(guided.output_text)
Fíjate en lo que el system prompt está haciendo en realidad. No es solo "sé un ingeniero de backend" — el rol es la parte barata. El trabajo está en las restricciones: máximo tres oraciones, empieza con el veredicto, sin listas de viñetas. Los roles vagos se desvían. Las reglas específicas se sostienen. Ese es el hábito que vale la pena construir desde ahora: escribe el system prompt como una lista corta de reglas que la respuesta tiene que cumplir, no como una vibra.
Gemini
Misma separación, una diferencia estructural. Gemini no recibe el system
prompt como argumento de primer nivel — cuelga de un GenerateContentConfig
como system_instruction, mientras que el texto del usuario viaja en
contents. El objeto de config es además donde viven el resto de las perillas,
así que esta es la forma que vas a reutilizar cada vez que necesites
max_output_tokens, temperatura o un schema de respuesta más adelante.
"""Week 2 - Prompting fundamentals (Gemini).
Same lesson as the OpenAI example, same question asked twice, so you can
read the two SDKs side by side. The only real difference is where the
system instruction goes: Gemini hangs it off a config object instead of
a top-level argument.
"""
import os
import sys
from dotenv import find_dotenv, load_dotenv
from google import genai
from google.genai import types
load_dotenv(find_dotenv())
if not os.environ.get("GEMINI_API_KEY"):
sys.exit("GEMINI_API_KEY is not set. Put it in the course-root .env file.")
client = genai.Client(api_key=os.environ["GEMINI_API_KEY"])
QUESTION = "Should I use a vector database for my app?"
# region: bare-call
# No system instruction. The model decides everything about the answer.
bare = client.models.generate_content(
model="gemini-3.1-flash-lite", contents=QUESTION,
)
# endregion
# region: system-call
# Gemini carries the system prompt on a GenerateContentConfig as
# system_instruction; the user's text stays in `contents`.
SYSTEM = (
"You are a senior backend engineer. Answer in at most three sentences. "
"Lead with a direct yes/no/it-depends, then the single deciding factor. "
"No preamble, no bullet points."
)
guided = client.models.generate_content(
model="gemini-3.1-flash-lite",
contents=QUESTION,
config=types.GenerateContentConfig(system_instruction=SYSTEM),
)
# endregion
print("=== bare prompt ===\n")
print(bare.text)
print("\n=== with a system instruction ===\n")
print(guided.text)
Dos SDKs, una sola idea, y la divergencia está únicamente en dónde se conecta el system prompt: un argumento con nombre en OpenAI, un campo de config en Gemini. El texto del prompt es idéntico byte por byte entre los dos archivos, y ese es justo el punto. El prompting es una habilidad que se transfiere entre proveedores; la fontanería del SDK es la parte que se consulta.
Ponlo a trabajar
Tres aplicaciones web pequeñas, cada una un proyecto de docker-compose
independiente bajo code/showcase/<slug>/. La rutina es la misma que la semana
pasada: bash bootstrap-secrets.sh, docker compose up --build, abrir
http://localhost:3000. Cada una aísla un movimiento de prompting para que
sientas cómo cambia el output en tiempo real, en lugar de solo leerlo.
Showcase 1 — Control de rol
Una pregunta, un dropdown de roles — maestro sencillo, ingeniero escéptico, explícalo-como-a-un-niño-de-nueve-años, lista de viñetas. El texto que escribes nunca cambia. Solo cambia el system prompt detrás, y la respuesta se reconfigura cada vez. Es la forma más rápida que conozco de interiorizar que el espacio de sistema no es decoración; es el volante.
Showcase 2 — Few-shot
Una tarea de clasificación con un checkbox que prende y apaga casos de ejemplo. Con los ejemplos en el prompt, el conjunto de etiquetas y el formato de una línea se mantienen fijos. Apágalos y el modelo empieza a inventar sus propias etiquetas y a envolverlas en prosa disculpona. El few-shot no es magia: es mostrarle al modelo la forma de una respuesta correcta en vez de describírsela y cruzar los dedos.
Showcase 3 — Plantilla de prompt
Pega una descripción de cambios desordenada y obtén una nota de release con las mismas tres secciones cada vez: Resumen, Impacto, Acción. La plantilla vive dentro del prompt, así que la estructura se vuelve una propiedad del request en lugar de algo que esperas con los dedos cruzados. Este es el movimiento que convierte una llamada al modelo en algo que puedes poner corriente abajo de otro código.
Los tres corren el mismo backend/main.py, byte por byte. Lee PROVIDER e
importa ai_openai o ai_gemini, así que PROVIDER=gemini docker compose up --build cambia el SDK sin tocar nada más. La decisión de diseño que evita
que estos sean juguetes es que el prompt hace el trabajo — ninguno va más allá
de una sola llamada al modelo, y los tres son lo bastante predecibles como para
confiar en ellos porque el prompt los dejó bien amarrados.
Córrelo
El README de esta carpeta tiene la versión de Python, la línea de instalación,
las dos variables de entorno y el comando exacto para los ejemplos básicos y
cada uno de los tres showcases. Las llaves salen del .env sin trackear en la
raíz del curso, igual que en la semana uno.
Conclusiones
El prompt es la interfaz. No puedes reentrenar el modelo ni ver sus pesos, pero sí puedes decidir qué va en el espacio de sistema, si muestras ejemplos y si le entregas una plantilla — y esas tres decisiones cubren la mayor parte de lo que "prompt engineering" significa en la práctica. Sé específico en el system prompt: reglas, no vibras. Muestra ejemplos cuando el formato importe más que la explicación. Entrégale una plantilla cuando necesites la misma estructura cada vez. Todo lo más sofisticado de los capítulos siguientes está construido encima de estas tres cosas, y el modelo nunca cambió en absoluto.