Módulo 7: Versionado y rollout seguro
Por qué versionar prompts y tools
Descripción
El Módulo 6 dejó al agente de Reservo protegido contra una tool que falla repetidamente: el circuit breaker abre, el agente deja de insistir, y el sistema se degrada con control. Pero hay una pregunta que ese circuit breaker nunca responde, porque no está diseñado para responderla: ¿qué pasa cuando la tool no falla, y el problema es que el agente decidió llamar a la tool equivocada?
Esta lección se detiene, sin escribir todavía el gate completo, en el mecanismo exacto de ese peligro: cómo una sola línea agregada al system prompt del agente de Reservo puede cambiar qué tool decide llamar frente a una pregunta que, hasta ayer, resolvía bien. No es un bug de código —ninguna función cambia—, es un cambio de comportamiento, y el comportamiento de un agente basado en un LLM no se puede leer con un diff de texto para saber si es seguro.
Conexión con el módulo
Esta lección es la motivación del módulo entero: antes de construir un registro (lección 03) o correr un gate (lección 04), hace falta entender qué se está tratando de prevenir. El resto del módulo construye la maquinaria; esta lección explica por qué esa maquinaria hace falta.
Analogía: la receta que cambia un ingrediente
Un restaurante tiene una receta escrita para su plato más pedido. Un día, el chef decide "mejorar" la receta agregando una instrucción: "si el cliente parece tener prisa, sírvele el plato sin esperar la confirmación final de la mesa". La intención es buena —menos espera para el cliente—. El problema aparece la primera vez que un cliente pidió solo el menú para mirarlo, sin haber pedido nada todavía, y el mesero, siguiendo la instrucción nueva al pie de la letra, le trae un plato que nadie pidió.
Nadie cometió un error de cocina. El plato está bien hecho, con los ingredientes correctos, servido a tiempo. El error está en cuándo se decidió actuar — la misma clase de error que aparece cuando un system prompt le dice al agente "sé proactivo" sin ser preciso sobre los límites de esa proactividad.
El experimento: la misma pregunta, dos prompts
Vas a ver, sin construir todavía ningún gate formal, la decisión que tomaría el agente de Reservo bajo dos versiones de su system prompt, frente a la misma pregunta exacta.
SYSTEM_PROMPT_V1 = (
"Eres el asistente de reservas de Reservo, un sistema de coworking. "
"Ayudas a los usuarios a consultar salas, cotizar precios, reservar y "
"cancelar reservas. Usa siempre las tools disponibles para cotizar y "
"reservar -- nunca inventes un precio de memoria. Cuando el usuario "
"solo pregunta cuanto cuesta algo, usa get_quote y NO reserves. Usa "
"book_room unicamente cuando el usuario pide reservar de forma "
"explicita."
)
SYSTEM_PROMPT_V2 = (
"Eres el asistente de reservas de Reservo, un sistema de coworking. "
"Ayudas a los usuarios a consultar salas, cotizar precios, reservar y "
"cancelar reservas. Usa siempre las tools disponibles para cotizar y "
"reservar -- nunca inventes un precio de memoria. Se proactivo: si ya "
"tienes toda la informacion para completar una reserva, complétala "
"directamente en vez de solo cotizar, para ahorrarle un paso al "
"usuario. Usa book_room unicamente cuando el usuario pide reservar de "
"forma explicita."
)
question = "Cuanto cuesta Focus pro 3 horas?"
# CONCEPTO -- lo que supon que claude-sonnet-5 decidio bajo cada prompt.
# Bajo v1, la pregunta es pura consulta de precio: get_quote, sin reservar.
decision_v1 = {"tool": "get_quote", "input": {"room": "Focus", "tier": "pro", "hours": 3}}
# Bajo v2, la instruccion de "proactividad" hace que el agente infiera que,
# como ya tiene sala/tier/horas, puede completar la reserva directamente --
# aunque el usuario nunca pidio reservar, solo cotizar.
decision_v2 = {"tool": "book_room", "input": {"room": "Focus", "tier": "pro", "hours": 3, "member": "Guest"}}
print("pregunta:", question)
print("v1 decide:", decision_v1)
print("v2 decide:", decision_v2)
print("misma tool en las dos versiones:", decision_v1["tool"] == decision_v2["tool"])
Qué esperar:
pregunta: Cuanto cuesta Focus pro 3 horas?
v1 decide: {'tool': 'get_quote', 'input': {'room': 'Focus', 'tier': 'pro', 'hours': 3}}
v2 decide: {'tool': 'book_room', 'input': {'room': 'Focus', 'tier': 'pro', 'hours': 3, 'member': 'Guest'}}
misma tool en las dos versiones: False
Nota lo que no cambió entre las dos decisiones: la sala (Focus), el tier (pro), las horas (3). El agente entendió la pregunta perfectamente bien en ambos casos — el problema no es de comprensión, es de acción. Bajo v1, entender "Focus pro 3 horas" significa cotizar. Bajo v2, la misma comprensión, combinada con la línea nueva del prompt, significa reservar — y book_room tiene efectos reales: crea una reserva a nombre de un "member": "Guest" inventado, que el usuario nunca autorizó.
Por qué "lo probé a mano" no alcanza
Si la persona que escribió SYSTEM_PROMPT_V2 prueba su cambio con una sola pregunta —por ejemplo, "Reserva Focus pro 3h para Ana"— el resultado se ve perfecto: el agente reserva, tal como se esperaba, porque en ese caso reservar era lo correcto. El cambio parece una mejora. El problema aparece solo con preguntas que, hasta ahora, nunca debían terminar en una reserva — y una persona probando "a mano" tiene que adivinar, de memoria, cuáles son esas preguntas y acordarse de probarlas todas, cada vez que cambia una línea del prompt.
# La prueba manual mas comun: "¿reserva correctamente cuando se lo piden?"
question_reservar = "Reserva Focus pro 3h para Ana"
decision_v2_reservar = {"tool": "book_room", "input": {"room": "Focus", "tier": "pro", "hours": 3, "member": "Ana"}}
print("pregunta:", question_reservar)
print("v2 decide:", decision_v2_reservar)
print("esto SE VE bien -- pero no prueba nada sobre las preguntas de solo precio")
Qué esperar:
pregunta: Reserva Focus pro 3h para Ana
v2 decide: {'tool': 'book_room', 'input': {'room': 'Focus', 'tier': 'pro', 'hours': 3, 'member': 'Ana'}}
esto SE VE bien -- pero no prueba nada sobre las preguntas de solo precio
Esta es la trampa exacta: una prueba manual que confirma que el cambio funciona para el caso que se te ocurrió probar no dice nada sobre los casos que no probaste. Un set fijo de casos —el que este módulo retoma del Módulo 5 en la lección 04— resuelve ese problema por diseño: corre todas las preguntas relevantes, siempre las mismas, cada vez que algo cambia, sin depender de qué se le ocurra probar a la persona que hizo el cambio.
No es solo el prompt: tools y schemas también se versionan
Este módulo se enfoca en el system prompt porque es el cambio más fácil de hacer "sin darse cuenta de la magnitud" — es texto libre, nadie lo valida con un compilador. Pero la misma lógica aplica a otros dos cambios que un equipo real hace con frecuencia:
- Agregar o quitar una tool. Si
book_roomdejara de estar disponible para el agente (por ejemplo, mientras se arregla un bug en producción), cualquier pregunta que antes terminaba en una reserva ahora no tiene forma de completarse — un cambio de comportamiento tan real como el del prompt, aunque no se tocó ni una palabra del texto. - Cambiar un
input_schema. Si elenumdetierenget_quotepasara de["basic", "pro"]a["basic", "pro", "premium"], cualquier caso guionado que asuma solo dos tiers deja de reflejar la realidad del contrato — el mismo tipo de divergencia silenciosa, esta vez en la forma del contrato en vez de en el texto del prompt.
Por eso el registro de la lección 03 no guarda solo el texto del prompt: guarda también una versión de tools (tools_version) junto a cada versión de prompt, para que quede registrado, sin ambigüedad, con qué conjunto exacto de tools corrió cada versión.
Errores comunes
-
Pensar que un cambio de prompt es "solo texto", y por eso menos riesgoso que un cambio de código. El experimento de esta lección muestra lo contrario: un cambio de una sola línea en texto libre cambió, con total silencio, qué tool se ejecuta frente a una pregunta real. El código de las tools (
reservo_tools.py) no cambió ni un carácter. -
Confundir "el agente respondió sin error" con "el agente se comportó igual que antes".
book_roomen el ejemplo de esta lección no lanzó ninguna excepción, no devolvió ningúnis_error. Se ejecutó perfectamente — ejecutando la acción equivocada. Un log limpio no es evidencia de que el comportamiento no cambió. -
Probar un cambio de prompt solo con las preguntas que "deberían" cambiar de comportamiento. La prueba manual de esta lección ("Reserva Focus pro 3h para Ana") confirma que
v2sigue reservando bien cuando se lo piden — pero no prueba nada sobre las preguntas de solo cotización, que son, precisamente, las que se rompieron. -
Versionar solo el prompt y olvidar tools/schemas. Como se explicó arriba, agregar, quitar o modificar una tool cambia el comportamiento del agente tanto como un cambio de texto — y necesita quedar registrado con la misma disciplina.
-
Suponer que este problema solo aparece con cambios grandes de prompt. El cambio de esta lección fue una sola oración agregada a un prompt que, por lo demás, quedó idéntico. Los cambios más peligrosos no son los rediseños completos —esos se prueban con cuidado, porque se sabe que son riesgosos— sino los ajustes pequeños que se sienten "seguros".
Ejercicios
Ejercicio 1: Encuentra la línea que cambió (Fácil)
Sin ejecutar nada, compara SYSTEM_PROMPT_V1 y SYSTEM_PROMPT_V2 de esta lección palabra por palabra y escribe, en una frase, exactamente qué instrucción se agregó. Después, confirma con código que el resto del texto es idéntico.
Ver solución
La instrucción agregada es: "Se proactivo: si ya tienes toda la informacion para completar una reserva, complétala directamente en vez de solo cotizar, para ahorrarle un paso al usuario." — insertada entre la instrucción de no inventar precios y la instrucción sobre cuándo usar book_room.
v1_words = SYSTEM_PROMPT_V1.split()
v2_words = SYSTEM_PROMPT_V2.split()
solo_en_v2 = [w for w in v2_words if w not in v1_words]
print("palabras que aparecen en v2 y no en v1:", len(solo_en_v2))
print(" ".join(solo_en_v2))
Salida esperada (aproximada, depende de puntuación exacta):
palabras que aparecen en v2 y no en v1: 20
Se proactivo: si ya tienes toda la informacion completar una reserva, complétala directamente en vez cotizar, ahorrarle paso al usuario.
Explicación: el resto de las dos versiones —la definición de rol, la prohibición de inventar precios, la regla sobre book_room— es idéntico. El cambio real es acotado a una sola instrucción nueva, lo que confirma que ni siquiera hace falta un cambio grande de prompt para producir una regresión de comportamiento.
Ejercicio 2: Diseña una tercera pregunta que también se rompería bajo v2 (Medio)
El ejemplo trabajado mostró que "¿Cuánto cuesta Focus pro 3 horas?" se rompe bajo v2. Diseña otra pregunta de solo cotización (sin pedir reservar) que, por el mismo mecanismo, también terminaría reservando bajo v2. Justifica por qué.
Ver solución
question_2 = "Cuanto sale Boardroom pro 2 horas?"
decision_v1_q2 = {"tool": "get_quote", "input": {"room": "Boardroom", "tier": "pro", "hours": 2}}
decision_v2_q2 = {"tool": "book_room", "input": {"room": "Boardroom", "tier": "pro", "hours": 2, "member": "Guest"}}
print("v1:", decision_v1_q2)
print("v2:", decision_v2_q2)
Salida esperada:
v1: {'tool': 'get_quote', 'input': {'room': 'Boardroom', 'tier': 'pro', 'hours': 2}}
v2: {'tool': 'book_room', 'input': {'room': 'Boardroom', 'tier': 'pro', 'hours': 2, 'member': 'Guest'}}
Explicación: cualquier pregunta que (a) mencione sala, tier y horas con precisión suficiente para calcular un precio, y (b) no pida explícitamente reservar, dispara el mismo mecanismo: bajo v2, el agente tiene "toda la información para completar una reserva" y la instrucción de proactividad lo empuja a actuar en vez de solo responder. El problema no es específico de Focus ni de una combinación particular — es estructural al cambio del prompt, y por eso un solo caso de prueba nunca es suficiente para confirmar que no existe.
Ejercicio 3: Argumenta por qué un diff de texto no basta para aprobar un cambio de prompt (Difícil)
Sin ejecutar nada: escribe un párrafo explicando por qué revisar un cambio de system prompt leyendo el diff de texto (como se revisaría un cambio de código en una revisión de pull request) no es, por sí solo, suficiente para decidir si el cambio es seguro. Usa el experimento de esta lección como evidencia.
Ver solución
Un diff de texto muestra qué cambió en la instrucción, pero no muestra qué decisiones distintas va a tomar el modelo frente a las preguntas reales que un sistema en producción recibe — esa traducción de "texto de instrucción" a "comportamiento frente a un input específico" ocurre dentro del modelo, una pieza que, como esta guía repite desde el Módulo 1 de agent-fundamentals, es la única que no se controla del todo. Leer el diff de esta lección (una oración agregada, de apariencia inocente: "sé proactivo") no permite predecir, sin ejecutar algo, que esa oración específica iba a cambiar la tool elegida frente a una pregunta de precio puro. La única forma de saberlo con certeza es ejecutar un conjunto de preguntas representativas bajo ambas versiones y comparar las decisiones resultantes — exactamente lo que este módulo formaliza, a partir de la lección 04, como el gate de regresión corrido dos veces. Un diff de texto es útil para entender qué cambió; nunca es suficiente, por sí solo, para saber qué tan seguro es ese cambio.
Resumen y siguiente paso
- Un cambio de una sola línea en el system prompt del agente de Reservo —agregar la instrucción "sé proactivo"— cambia la tool que el agente decide llamar frente a una pregunta de solo cotización, sin que ningún error técnico lo señale.
- Una prueba manual con la pregunta "obvia" ("reserva Focus pro 3h para Ana") confirma que el cambio funciona para ese caso, pero no dice nada sobre los casos que no se probaron — exactamente los que se rompieron.
- El mismo riesgo aplica a tools y schemas, no solo al texto del prompt — por eso el registro de la lección 03 versiona ambos.
- La única forma confiable de confirmar que un cambio es seguro es correr un set fijo de preguntas representativas bajo ambas versiones y comparar — el gate que este módulo retoma del Módulo 5, a partir de la lección 04. La pregunta de este experimento —"¿Cuánto cuesta Focus pro 3h?"— no es una elegida al azar para esta lección: es, literalmente,
quote_focus_pro_3h, uno de los cinco casos fijos delCASE_SETdel Módulo 5 — la lección 04 retoma este mismo caso, con el mismo guion regresivo, y lo corre a través del gate real.
Siguiente lección: 03 — El registro de versiones de prompts. Construimos PROMPT_REGISTRY: cada versión del system prompt de Reservo, identificada con un hash determinista, para que nunca haya ambigüedad sobre qué texto exacto estaba corriendo.
Recursos adicionales
- Anthropic — System prompts — El rol del system prompt en el comportamiento del modelo, la pieza que cambió entre
v1yv2en esta lección. - Anthropic — Building effective agents — Sobre por qué instrucciones aparentemente pequeñas pueden tener efectos grandes en las decisiones de un agente.
- Python — comparación de secuencias — La base de
split()y la comparación de listas usada en el Ejercicio 1. - Python 3.14 — What's New — La versión con la que se ejecutó cada línea de código de esta lección.