Módulo 6: La cáscara determinista
Presentación del módulo: la cáscara determinista
Por qué este módulo existe aquí
Llevas cinco módulos rodeando al componente de IA con código determinista. En el módulo 1 le diste nombre a la forma —un núcleo probabilístico dentro de una cáscara determinista—; en el 2 le pusiste presupuesto de latencia y costo; en el 3 le pusiste una compuerta de eval; en el 4 le custodiaste la frontera de confianza con guardrails; en el 5 lo hiciste resiliente a sus fallos. Todo ese trabajo protege al sistema de un modelo que es lento, caro, no confiable en su contenido y falible. Pero hay una pregunta que ninguno de esos módulos respondió del todo, y es la más incómoda de todas: ¿qué pasa cuando el modelo no solo dice algo, sino que quiere hacer algo? Cuando la salida del LLM ya no es un texto que muestras, sino una acción —reembolsar dinero, cambiar un precio, cancelar un pedido, dar de baja una cuenta—, la no-determinación deja de ser un problema de calidad y se vuelve un problema de poder. Un texto malo se puede filtrar. Una acción mala ya pasó.
Este módulo instala la tesis que gobierna esa frontera, y es la frase más importante de toda la guía: el modelo propone, el sistema dispone. El LLM nunca ejecuta un reembolso, un cobro o un cambio de estado directamente. En vez de eso, propone una acción en un formato validable —por ejemplo {action: "refund", amount: 50, order_id: "A-1001"}— y una capa determinista de reglas de negocio la aprueba o la rechaza antes de que toque nada. La inteligencia del modelo entra al sistema como una sugerencia, no como una orden. Ese cambio de una palabra —de "el LLM hace" a "el LLM propone y el código dispone"— es lo que hace seguro poner un componente no determinista al lado del dinero.
De esa tesis salen tres principios que las lecciones van a desarrollar y a medir. La acción estructurada: el modelo devuelve un comando con nombre y campos tipados, no código libre ni texto que alguien interprete —así hay algo concreto que validar y despachar—. Las capabilities acotadas: el modelo solo puede proponer de un menú cerrado de acciones permitidas, de modo que una alucinación no puede alcanzar una operación que no está en el menú. Y el núcleo pequeño: cuanto menos superficie tenga el LLM sobre acciones irreversibles, menor es el radio de daño; la disciplina AI-native es sacar del núcleo todo lo que pueda ser una regla, y dejarle una sola responsabilidad —proponer—.
Conviene marcar la frontera dura de este módulo antes de seguir, porque es doble. Hacia las guías de dominio: la lógica de negocio en sí —cuál debe ser la política de reembolsos de Mercado, qué monto es razonable, cómo se calcula la ventana de devolución— es contenido de dominio, no de esta guía. Aquí tratamos el patrón de contención (que una capa determinista valide antes de ejecutar), no las reglas concretas de ningún negocio. Y hacia el módulo 4: el guardrail de salida validaba el formato y el contenido de lo que el modelo dice (¿es un JSON válido?, ¿tiene un claim prohibido?, ¿es una inyección?). Aquí validamos algo distinto —la acción propuesta contra las reglas de negocio antes de ejecutarla (¿este reembolso está dentro de política?, ¿este pedido existe?, ¿ya se reembolsó?)—. Un guardrail dice "esta salida es válida"; la cáscara determinista dice "esta acción se puede ejecutar". Son dos compuertas distintas, y este módulo construye la segunda.
El caso, como en toda la guía, es Mercado, y el protagonista es el agente de soporte: el agente que "quiere" reembolsar. Un cliente escribe "quiero mi dinero de vuelta", el modelo propone una acción de reembolso, y la cáscara la valida contra la política —monto dentro del límite, pedido existe, dentro de la ventana, no reembolsado ya— y ejecuta solo si pasa. Un modelo entusiasta que quiere complacer al cliente reembolsando de más, o que alucina un pedido que no existe, o que propone un monto absurdo, se topa siempre con la misma pared determinista antes de tocar un centavo.
Y la promesa de siempre: nada se afirma de memoria, todo se ejecuta. Cada simulación corre en Python, con el LLM simulado por un stub que propone acciones buenas y malas —fuera de política, alucinadas, montos absurdos—, nunca una API real, sin claves ni red, con datos fijos, así que la salida que ves en cada bloque "Qué esperar" es la salida literal de correr el código. Puedes copiarlo y reproducirlo idéntico.
Tres analogías: el cajero, el empleado nuevo y el piloto automático
El cajero que puede recomendar aprobarte un crédito, pero es el sistema del banco el que lo aprueba. Vas a una sucursal a pedir un crédito. El cajero te atiende, ve tu caso, y puede recomendar que te lo aprueben —"a mí me parece que calificas, se ve bien tu historial"—. Pero el cajero no aprueba el crédito. Aunque quiera, aunque le caigas bien, aunque esté convencido. Aprieta un botón que manda tu solicitud al sistema del banco, y ese sistema, con sus reglas duras —tu score, tu deuda actual, el límite de tu perfil—, decide. El cajero propone; el sistema dispone. Y esa separación no es burocracia inútil: es lo que evita que un cajero demasiado amable, o demasiado presionado, o simplemente equivocado, comprometa el dinero del banco. El cajero aporta el juicio humano y la lectura del caso; el sistema aporta la garantía de que ninguna aprobación viole las reglas. Un LLM es exactamente ese cajero: puede leer el caso del cliente y recomendar un reembolso, pero no debe aprobarlo; eso lo hace el sistema con sus reglas.
El empleado nuevo que propone un descuento y el gerente lo autoriza según la política. Un empleado recién contratado en una tienda quiere cerrar una venta y le dice al cliente "déjeme ver si le puedo hacer un descuento". No aplica el descuento por su cuenta: lo propone al gerente, que lo autoriza o lo niega según la política de la tienda —hasta 10% sin permiso especial, nada por debajo del costo, no acumulable con otras promociones—. El empleado nuevo todavía no conoce todos los límites, a veces propondría un descuento que hunde el margen, y precisamente por eso su propuesta pasa por una autorización que sí conoce los límites. Con el tiempo el empleado aprende, pero la autorización no desaparece: es la que garantiza que ningún descuento viole la política, venga de quien venga. Un LLM es el empleado nuevo permanente: por más que "aprenda" del prompt, su propuesta siempre pasa por la autorización determinista que conoce la política.
El piloto automático que sugiere una maniobra pero hay límites duros que no puede cruzar. El piloto automático de un avión moderno hace muchísimo: mantiene el rumbo, ajusta la altitud, sugiere correcciones. Pero por encima de él hay un sistema de protección de envolvente de vuelo: límites duros que el avión no cruza, sin importar lo que el piloto automático (o el piloto humano) ordene. No puedes inclinar el avión más allá de cierto ángulo, no puedes exceder cierta velocidad, no puedes forzar un ascenso que provocaría una pérdida de sustentación. El sistema de control puede proponer una maniobra agresiva; la envolvente la recorta a lo seguro. La inteligencia del control vuela el avión; los límites duros garantizan que ninguna orden —por buena que parezca en el momento— lo saque del rango seguro. Un LLM cerca del dinero necesita su envolvente: propone lo que quiera, pero hay límites duros —el monto máximo, la ventana, el estado del pedido— que no se cruzan.
El punto que une las tres es el mismo, y es el módulo entero en una idea: la parte inteligente propone; la parte con reglas duras dispone, y las reglas duras son las que dan la garantía. El cajero recomienda, el banco aprueba; el empleado propone, el gerente autoriza; el control sugiere, la envolvente recorta. En los tres, la parte "lista" nunca tiene la última palabra sobre lo irreversible: siempre hay una capa determinista, con reglas que conoce, que decide si la sugerencia se ejecuta. Tu agente de soporte de Mercado es el cajero, el empleado, el control: propone reembolsos; la cáscara determinista es el banco, el gerente, la envolvente. Y ningún reembolso fuera de política sale, por más que el modelo lo proponga con total convicción.
Ejemplo trabajado: el agente propone, la cáscara dispone
No vamos a decir que la cáscara contiene las acciones peligrosas: la vamos a ejecutar y a contar. Modelamos el agente de soporte de Mercado con el patrón completo en miniatura. El núcleo probabilístico es un stub que simula el LLM: dado el mensaje del cliente, propone una acción estructurada de reembolso. A propósito, a veces propone algo legítimo y a veces algo fuera de política, alucinado o absurdo, porque el punto es ver qué hace la cáscara con lo malo. La cáscara determinista valida cada propuesta contra la política de reembolsos —el pedido existe, no fue reembolsado ya, está dentro de la ventana de devolución, el monto no excede el total del pedido ni el límite del agente— y ejecuta solo si pasa.
# Modulo 6, Leccion 1: la cascara determinista en miniatura.
# El LLM (SIMULADO por un stub) PROPONE una accion estructurada; la
# deterministic_shell la VALIDA contra la politica de reembolsos y solo
# EJECUTA si pasa. Sin red, sin API, sin claves. Datos fijos.
# --- Fuente de verdad determinista: los pedidos de Mercado. ---
ORDERS = {
"A-1001": {"total": 50.00, "days_since_delivery": 3, "refunded": False},
"A-1002": {"total": 120.00, "days_since_delivery": 45, "refunded": False},
"A-1003": {"total": 30.00, "days_since_delivery": 5, "refunded": True},
"A-1005": {"total": 75.00, "days_since_delivery": 8, "refunded": False},
}
# --- Politica de reembolsos (reglas de negocio, deterministas). ---
MAX_REFUND = 100.00 # monto maximo que el agente puede reembolsar solo
RETURN_WINDOW_DAYS = 30 # ventana de devolucion en dias
# --- Nucleo probabilistico: SIMULA el LLM del agente de soporte. ---
# Dado el mensaje del cliente, PROPONE una accion estructurada. A veces
# propone algo legitimo; a veces algo fuera de politica, alucinado o absurdo.
def llm_propose(message):
proposals = {
"quiero mi dinero del pedido A-1001":
{"action": "refund", "order_id": "A-1001", "amount": 50.00},
"reembolsame el A-1002 completo":
{"action": "refund", "order_id": "A-1002", "amount": 120.00},
"el A-1003 llego roto, lo quiero de vuelta":
{"action": "refund", "order_id": "A-1003", "amount": 30.00},
"denme reembolso del pedido A-9999":
{"action": "refund", "order_id": "A-9999", "amount": 40.00},
"reembolsame 5000 dolares del A-1005":
{"action": "refund", "order_id": "A-1005", "amount": 5000.00},
"devuelveme el dinero del A-1005":
{"action": "refund", "order_id": "A-1005", "amount": 75.00},
}
return proposals[message]
# --- Cascara determinista: valida la propuesta ANTES de tocar dinero. ---
def deterministic_shell(proposal):
order_id = proposal["order_id"]
amount = proposal["amount"]
# Regla 1: el pedido debe existir en la fuente de verdad.
if order_id not in ORDERS:
return (False, f"pedido {order_id} no existe")
order = ORDERS[order_id]
# Regla 2: no reembolsado ya.
if order["refunded"]:
return (False, f"pedido {order_id} ya fue reembolsado")
# Regla 3: dentro de la ventana de devolucion.
if order["days_since_delivery"] > RETURN_WINDOW_DAYS:
return (False, f"fuera de la ventana ({order['days_since_delivery']} > {RETURN_WINDOW_DAYS} dias)")
# Regla 4: el monto no puede exceder el total del pedido.
if amount > order["total"]:
return (False, f"monto {amount:.2f} > total del pedido {order['total']:.2f}")
# Regla 5: el monto no puede exceder el limite del agente.
if amount > MAX_REFUND:
return (False, f"monto {amount:.2f} > limite del agente {MAX_REFUND:.2f}")
return (True, "aprobado")
messages = [
"quiero mi dinero del pedido A-1001",
"reembolsame el A-1002 completo",
"el A-1003 llego roto, lo quiero de vuelta",
"denme reembolso del pedido A-9999",
"reembolsame 5000 dolares del A-1005",
"devuelveme el dinero del A-1005",
]
executed = blocked = 0
money_paid = money_blocked = 0.0
print(f"{'pedido':<8}{'monto':>9} {'resultado':<10}razon")
print("-" * 64)
for msg in messages:
proposal = llm_propose(msg) # el LLM PROPONE
ok, reason = deterministic_shell(proposal) # la cascara DISPONE
tag = "EJECUTA" if ok else "BLOQUEA"
if ok:
executed += 1
money_paid += proposal["amount"]
else:
blocked += 1
money_blocked += proposal["amount"]
print(f"{proposal['order_id']:<8}{proposal['amount']:>9.2f} {tag:<10}{reason}")
print()
print(f"Propuestas del LLM : {len(messages)}")
print(f"Ejecutadas (pasaron) : {executed}")
print(f"Bloqueadas (contuvo) : {blocked}")
print(f"Dinero reembolsado : {money_paid:.2f}")
print(f"Dinero contenido : {money_blocked:.2f} (nunca toco la cuenta del cliente)")
Qué esperar. Al correr el archivo, la salida es exactamente esta:
pedido monto resultado razon
----------------------------------------------------------------
A-1001 50.00 EJECUTA aprobado
A-1002 120.00 BLOQUEA fuera de la ventana (45 > 30 dias)
A-1003 30.00 BLOQUEA pedido A-1003 ya fue reembolsado
A-9999 40.00 BLOQUEA pedido A-9999 no existe
A-1005 5000.00 BLOQUEA monto 5000.00 > total del pedido 75.00
A-1005 75.00 EJECUTA aprobado
Propuestas del LLM : 6
Ejecutadas (pasaron) : 2
Bloqueadas (contuvo) : 4
Dinero reembolsado : 125.00
Dinero contenido : 5190.00 (nunca toco la cuenta del cliente)
Lee la salida con calma, porque ahí está el módulo entero en miniatura.
El modelo propuso seis acciones; la cáscara ejecutó dos. Las dos que ejecutó son reembolsos legítimos: el A-1001 por $50 (existe, dentro de la ventana, no reembolsado, monto razonable) y el A-1005 por $75 (lo mismo). Las otras cuatro se bloquearon, y cada una por una razón distinta que vale la pena mirar, porque son los cuatro modos en que un LLM cerca del dinero se descarrila. El A-1002 proponía reembolsar un pedido entregado hace 45 días —fuera de la ventana de 30—: el modelo no "sabía" (ni tenía por qué saber) la política, y propuso de más. El A-1003 proponía reembolsar un pedido que ya se reembolsó: sin la cáscara, sería un doble reembolso, dinero pagado dos veces por lo mismo. El A-9999 proponía reembolsar un pedido que no existe: una alucinación pura —el modelo inventó un identificador de pedido—. Y el A-1005 por $5000 proponía reembolsar setenta veces el valor del pedido: un monto absurdo, tal vez porque el modelo malinterpretó el mensaje del cliente.
El número que importa está abajo: $5190 contenidos. El modelo propuso reembolsar, en total, $5315. La cáscara ejecutó $125 —los reembolsos legítimos— y contuvo $5190 que nunca tocaron la cuenta de ningún cliente. Esos $5190 no son un ahorro teórico: son un pedido inexistente pagado al vacío, un doble reembolso, un reembolso fuera de política y, sobre todo, un monto absurdo de $5000 que un solo error del modelo habría desembolsado. La cáscara no hizo al modelo más listo —el modelo siguió proponiendo exactamente las mismas seis acciones—; lo que hizo fue interponerse entre la propuesta y la ejecución, y dejar pasar solo lo que cumple las reglas. Ese es el patrón completo: el modelo propone las seis, el sistema dispone dos.
Fíjate en la asimetría de responsabilidades, porque es la forma del módulo. El núcleo probabilístico hace una sola cosa: proponer una acción estructurada a partir del mensaje. La cáscara determinista hace todo lo demás: verificar contra la fuente de verdad, chequear cada regla de la política, decidir, ejecutar o bloquear, y dar una razón. El núcleo es una gota de incertidumbre; la cáscara es el océano de código certero que la contiene. Y la garantía "ningún reembolso fuera de política sale" no la da el modelo (no puede, es probabilístico): la da la cáscara, porque if amount > MAX_REFUND es una condición dura que se cumple siempre.
Las ideas que instala este módulo, y dónde vive cada una
Ese ejemplo tocó, sin desarrollarlas del todo, las ideas del módulo. Vale la pena verlas explícitas, porque son la columna vertebral de las lecciones que siguen.
1. El modelo propone, el sistema dispone (lección 2). El principio raíz. El LLM nunca ejecuta una acción sobre dinero o estado; propone, y una capa determinista dispone. La lección 2 ejecuta el contraste entre el diseño ingenuo (el LLM ejecuta) y el de cáscara (el LLM propone) y mide la diferencia en dinero.
2. La acción estructurada (lección 3). El modelo devuelve un comando con nombre y campos tipados —{action: "refund", ...}—, no código libre ni texto interpretado. Así hay algo concreto que un dispatcher determinista puede validar y despachar. La lección 3 ejecuta un dispatcher que despacha los comandos válidos y rechaza en el canal el texto libre y las acciones inventadas.
3. Las capabilities acotadas (lección 4). El modelo solo puede proponer de un menú cerrado de acciones. Todo lo que no está en el menú se rechaza por definición. La lección 4 mide cómo el radio de daño crece con el tamaño del menú: un menú chico deja 0 operaciones peligrosas al alcance; uno amplio, varias.
4. Validar la propuesta contra las reglas de negocio (lección 5). El corazón: correr la acción propuesta contra cada regla de la política antes de ejecutar. La lección 5 ejecuta una matriz regla por regla y muestra cuál falla en cada propuesta bloqueada.
5. El núcleo pequeño (lección 6). Cuanto menos superficie tenga el LLM sobre acciones irreversibles, menor el radio de daño. Sacar a reglas deterministas toda decisión que pueda serlo. La lección 6 mide la reducción de la superficie probabilística al pasar de un fat core a un thin core.
6. El pipeline completo (lección 7). Ensamblar estructura → capability → política → ejecución, con log de auditoría. La lección 7 ejecuta el pipeline sobre un lote y muestra en qué etapa se detuvo cada propuesta bloqueada.
Guarda este mapa; es la ruta del módulo:
Idea Leccion Concepto clave
─────────────────────────────────────── ──────── ──────────────────────────────
El modelo propone, el sistema dispone L2 proponer vs ejecutar;
el antipatron raiz
La accion estructurada L3 comando con nombre y campos;
dispatcher determinista
Las capabilities acotadas L4 menu cerrado; least privilege;
radio de dano
Validar contra reglas de negocio L5 regla por regla; ejecutar solo
si todas pasan
El nucleo probabilistico pequeno L6 sacar decisiones a reglas;
menos superficie que alucina
El pipeline propuesta -> ejecucion L7 estructura+capability+politica;
log de auditoria
─────────────────────────────────────── ──────── ──────────────────────────────
Construir la cascara del agente L8 el mini-proyecto, ejecutado
El mapa: dónde está este módulo en la guía y en el ecosistema
Este módulo es la sexta pieza de la cáscara determinista que rodea al componente de IA, y es la que se ocupa de sus acciones. Así se conecta con el resto de la guía:
flowchart TD
M1["M1 · Ubicar el componente<br/>(contrato, frontera, nucleo/cascara)"]
M2["M2 · Latencia y costo como arquitectura"]
M3["M3 · El eval como fitness function"]
M4["M4 · Guardrails y la frontera de confianza"]
M5["M5 · Modos de fallo y resiliencia para IA"]
M6["M6 · La cascara determinista"]
M7["M7 · El lazo de datos y de retroalimentacion"]
M8["M8 · Proyecto: arquitecta una feature de IA"]
M1 --> M2 --> M3 --> M4 --> M5 --> M6 --> M7 --> M8
Léelo así: en M1 diste la forma núcleo/cáscara; en M4 pusiste guardrails que validan el contenido de la salida; aquí (M6) construyes la parte de la cáscara que valida las acciones: cuando la salida del modelo deja de ser un texto que muestras y se vuelve una acción que toca dinero o estado, la validación cambia —de "¿es un contenido válido?" a "¿es una acción que se puede ejecutar según las reglas?"—. En M7 cerrarás el lazo de datos, y verás que el log de auditoría de la lección 7 (qué se propuso, qué se aprobó, qué se bloqueó) es también la primera materia prima de ese lazo.
Y las dos fronteras duras de este módulo, que hay que respetar. La primera, con las guías de dominio: no enseñamos cuál debe ser la política de reembolsos de Mercado —qué monto, qué ventana, qué excepciones—; eso es una decisión de negocio, contenido de las guías de dominio. Enseñamos el patrón: que exista una capa determinista que valide la acción propuesta antes de ejecutarla, cualquiera que sea la política. La segunda, con el módulo 4: el guardrail de M4 validaba el formato y el contenido de la salida ("¿es JSON válido?", "¿tiene un claim prohibido?"); la cáscara de M6 valida la acción contra las reglas de negocio ("¿este reembolso está dentro de política?"). Un guardrail puede aprobar una salida perfectamente bien formada —{action: "refund", amount: 5000, order_id: "A-1005"} es JSON impecable— que la cáscara debe rechazar porque la acción viola la política. Son dos compuertas en serie: primero "¿es una salida válida?" (M4), luego "¿es una acción ejecutable?" (M6). Y la tercera frontera, la de siempre con AI Engineering: cómo hacer que el modelo proponga mejores acciones —mejor prompt, tool use, function calling— es AI Eng; aquí tratamos cómo contener las acciones que proponga, sean buenas o malas.
Errores comunes
Dejar que el LLM ejecute la acción directamente. Qué pasa: el equipo conecta la salida del modelo directo al sistema de pagos —"el agente detecta que hay que reembolsar y llama a la API de reembolsos"—, sin ninguna capa que dispute. El día que el modelo alucina un pedido, propone un monto absurdo o reembolsa fuera de política, el dinero ya salió: no hubo dónde frenarlo. Por qué pasa: es el camino más corto —conectar la salida a la acción— y en las pruebas el modelo casi siempre propuso bien, así que el fallo nunca se vio. Cómo detectarlo: traza qué separa la salida del modelo del efecto real sobre el dinero o el estado; si la respuesta es "nada, la salida dispara la acción", no tienes cáscara. Cómo corregirlo: interpón una capa determinista que reciba la acción como una propuesta, la valide contra las reglas, y ejecute solo si pasa. La lección 2 lo mide: el diseño ingenuo pagó $5315; con cáscara, $125.
Darle capabilities amplias "para que sea útil". Qué pasa: para que el agente "pueda resolver cualquier cosa", se le da acceso a un menú enorme de acciones —reembolsar, dar crédito, cambiar precios, cancelar pedidos, dar de baja cuentas—. Cada capability extra es una operación irreversible más al alcance de una alucinación. El día que el modelo, confundido, propone cambiar un precio a $0.01 o dar de baja una cuenta, esa acción estaba en el menú, así que nada la detiene por definición. Por qué pasa: se confunde "útil" con "poderoso", y se teme que un menú chico limite al agente. Cómo detectarlo: lista las acciones que el agente puede proponer y pregúntate cuántas son irreversibles y peligrosas; si son muchas, tu radio de daño es grande. Cómo corregirlo: aplica least privilege —dale el menú mínimo que necesita para su trabajo, y nada más—. La lección 4 lo mide: un menú acotado deja 0 operaciones peligrosas al alcance; uno amplio, 3.
Validar el texto pero no la acción. Qué pasa: el equipo puso un guardrail de M4 que valida que la salida sea JSON bien formado y no tenga contenido prohibido, y cree que con eso está protegido. Pero {action: "refund", amount: 5000, order_id: "A-9999"} pasa el guardrail sin problema —es JSON perfecto, sin palabras prohibidas— y aun así es una acción que reembolsa un monto absurdo de un pedido que no existe. El guardrail validó el contenido; nadie validó la acción. Por qué pasa: se confunde "salida válida" con "acción ejecutable", que son cosas distintas. Cómo detectarlo: pregúntate si una salida perfectamente bien formada podría, aun así, ejecutar algo que viola la política; si la respuesta es sí, te falta la cáscara de M6. Cómo corregirlo: después del guardrail de formato, agrega la validación de la acción contra las reglas de negocio. La lección 5 lo ejecuta regla por regla.
Confiar en que el prompt "le dijo que no reembolse de más". Qué pasa: en vez de una cáscara determinista, el equipo pone la política en el prompt —"nunca reembolses más de $100, nunca reembolses fuera de la ventana de 30 días"— y confía en que el modelo obedezca. La mayoría de las veces obedece; pero el prompt es una sugerencia a un componente probabilístico, no una garantía, y tarde o temprano el modelo propone $5000 igual, porque un cliente insistió, porque malinterpretó, o simplemente porque es no determinista. Por qué pasa: poner la regla en el prompt es fácil y parece que funciona en las pruebas. Cómo detectarlo: si tu única defensa contra un reembolso fuera de política es una instrucción en el prompt, no tienes garantía —tienes una probabilidad—. Cómo corregirlo: la política vive en código determinista, no en el prompt. El prompt puede pedirle al modelo que proponga dentro de política (ayuda a que proponga bien), pero la garantía la da el if de la cáscara, que se cumple el 100% de las veces sin importar lo que el modelo proponga.
Ejercicios
Ejercicio 1 — El cajero, el empleado y el piloto. Para cada una de las tres analogías del módulo, identifica (a) quién propone, (b) quién dispone, y (c) qué garantía da la parte que dispone que la parte que propone no podría dar. Luego di, para el agente de soporte de Mercado, quién propone y quién dispone.
Ver solución
- El cajero: (a) propone = el cajero (recomienda aprobar el crédito); (b) dispone = el sistema del banco (aprueba o niega según score, deuda, límite); (c) la garantía = ninguna aprobación viola las reglas del banco, por más que el cajero quiera. El cajero aporta el juicio del caso; el sistema aporta la certeza de la política.
- El empleado nuevo: (a) propone = el empleado (sugiere un descuento); (b) dispone = el gerente (autoriza según la política de la tienda); (c) la garantía = ningún descuento hunde el margen ni viola la política, venga de quien venga. El empleado no conoce todos los límites; la autorización sí.
- El piloto automático: (a) propone = el sistema de control (sugiere una maniobra); (b) dispone = la protección de envolvente (recorta a lo seguro); (c) la garantía = el avión no sale del rango seguro, sin importar qué orden reciba. El control vuela; los límites duros garantizan la seguridad.
- El agente de soporte de Mercado: propone = el LLM (sugiere una acción de reembolso a partir del mensaje del cliente); dispone = la
deterministic_shell(aprueba o bloquea según la política: pedido existe, dentro de la ventana, no reembolsado, monto dentro del límite). La garantía: ningún reembolso fuera de política sale, por más que el modelo lo proponga con total convicción.
El patrón en las cuatro: la parte inteligente propone, la parte con reglas duras dispone, y la garantía la da siempre la segunda.
Ejercicio 2 — El número de la contención. En el ejemplo trabajado, el modelo propuso reembolsar $5315 en total y la cáscara ejecutó $125, conteniendo $5190. De esos $5190, ¿cuál propuesta fue la más peligrosa y por qué? Luego responde: si en vez de una cáscara determinista el equipo hubiera puesto la política en el prompt del modelo, ¿qué garantía tendría sobre esos $5190?
Ver solución
La propuesta más peligrosa fue el A-1005 por $5000. Las otras tres bloqueadas son errores acotados: el A-1002 ($120) reembolsaría un poco de más y fuera de ventana, el A-1003 ($30) sería un doble reembolso, el A-9999 ($40) pagaría un pedido inexistente. Pero el A-1005 por $5000 es un monto absurdo —setenta veces el valor real del pedido ($75)—: un solo error de este tipo, ejecutado, es un desfalco. Es el caso que mejor ilustra por qué la cáscara importa: no protege solo contra errores chicos y frecuentes, sino contra el error grande y raro que, ejecutado una sola vez, hace un daño enorme.
Si la política viviera en el prompt en vez de en la cáscara, la garantía sobre esos $5190 sería ninguna —solo una probabilidad—. El prompt le pide al modelo que no reembolse de más, pero el modelo es probabilístico: la mayoría de las veces obedecerá, pero cada tanto propondrá $5000 igual, y si no hay una cáscara determinista que lo frene, ese $5000 se ejecuta. La diferencia es categórica: la cáscara con if amount > MAX_REFUND da una garantía dura (0 reembolsos fuera de límite, siempre); el prompt da una probabilidad alta (casi nunca, pero no nunca). Cerca del dinero, "casi nunca" no alcanza.
Ejercicio 3 — Guardrail o cáscara. El módulo 4 validaba la salida del modelo; el módulo 6 valida la acción. Para cada uno de estos chequeos, di si pertenece al guardrail de salida (M4) o a la cáscara de acciones (M6): (a) la salida del modelo es un JSON bien formado; (b) el pedido que la acción quiere reembolsar existe en la base de datos; (c) la salida no contiene lenguaje ofensivo; (d) el monto del reembolso no excede el límite del agente.
Ver solución
- (a) La salida es JSON bien formado → guardrail de salida (M4). Es una validación de formato: ¿la salida tiene la estructura esperada? No mira si la acción es ejecutable, solo si el texto está bien formado. Sin esto, ni siquiera puedes parsear la propuesta.
- (b) El pedido existe en la base de datos → cáscara de acciones (M6). Es una validación de la acción contra la fuente de verdad y las reglas de negocio: no basta con que el JSON sea válido; el pedido que quiere reembolsar tiene que existir de verdad. Un
order_idalucinado produce un JSON perfecto que la cáscara debe rechazar. - (c) La salida no contiene lenguaje ofensivo → guardrail de salida (M4). Es una validación de contenido: mira lo que el texto dice, no lo que la acción hace. Es moderación, tema del módulo 4.
- (d) El monto no excede el límite → cáscara de acciones (M6). Es una regla de negocio sobre la acción:
amount <= MAX_REFUND. Un monto de $5000 puede venir en una salida impecable y no ofensiva; solo la cáscara, con la regla de la política, lo detiene.
La lección general: (a) y (c) preguntan "¿es una salida válida?" y son guardrails de M4; (b) y (d) preguntan "¿es una acción ejecutable según las reglas?" y son la cáscara de M6. Las dos compuertas van en serie: primero el guardrail deja pasar una salida bien formada, luego la cáscara decide si la acción se ejecuta.
Resumen y siguiente paso
En esta lección instalaste la tesis que sostiene el módulo y corona la guía: el modelo propone, el sistema dispone. El LLM nunca ejecuta una acción sobre dinero o estado directamente; propone una acción estructurada, y una capa determinista de reglas de negocio la aprueba o la rechaza antes de que toque nada. Lo viste con tres analogías —el cajero que recomienda pero el banco aprueba, el empleado que propone pero el gerente autoriza, el control que sugiere pero la envolvente recorta— y lo mediste: el agente de soporte propuso seis acciones de reembolso, la cáscara ejecutó dos legítimas y contuvo cuatro peligrosas ($5190 que nunca tocaron la cuenta de ningún cliente), sin hacer al modelo más listo —solo interponiéndose entre la propuesta y la ejecución—. Y marcaste las dos fronteras duras: la lógica de negocio en sí es de las guías de dominio; la validación del contenido de la salida fue M4, y aquí validamos la acción contra las reglas.
Antes de avanzar deberías poder: enunciar y defender "el modelo propone, el sistema dispone"; explicar por qué el LLM nunca debe ejecutar una acción irreversible directamente; distinguir el guardrail de salida (M4) de la cáscara de acciones (M6); y argumentar por qué una regla de política pertenece al código determinista y no al prompt.
La lección 2 toma la primera idea y la desarrolla a fondo: el modelo propone, el sistema dispone, como el principio raíz de todo el módulo. Vas a ver, ejecutado y medido, el contraste entre dos diseños con exactamente las mismas propuestas del modelo: el diseño ingenuo, donde el LLM ejecuta directamente y el dinero se mueve sin preguntar, y el diseño de cáscara, donde el LLM propone y una capa determinista dispone. La diferencia entre pagar $5315 y pagar $125 no es un detalle: es la distancia entre un sistema que confía en el modelo y uno que lo contiene.
Recursos
- Anthropic, "Building Effective Agents" (2024) — anthropic.com/engineering/building-effective-agents. La discusión sobre tool use y acciones estructuradas —el modelo propone llamadas a herramientas con argumentos tipados, y el sistema decide qué hacer con ellas— es la base técnica de "el modelo propone, el sistema dispone" de esta lección. En inglés.
- Documentación de Claude — docs.anthropic.com. Las páginas de tool use describen cómo un modelo devuelve una acción estructurada (una llamada a herramienta con parámetros) que el sistema ejecuta; léelas para el mecanismo de la acción estructurada, sin fijarte en una versión de modelo específica. En inglés.
- Martin Fowler y Bharani Subramaniam, "Emerging Patterns in Building GenAI Apps" — martinfowler.com/articles/gen-ai-patterns. Los patrones de guardrail y de contención ubican esta cáscara de acciones en el mapa arquitectónico completo de una app GenAI. En inglés.
- Chip Huyen, AI Engineering (O'Reilly, 2024). Su tratamiento de agentes y del control sobre las acciones que un modelo puede tomar es la versión extendida del principio de contención de esta lección. Nosotros nos quedamos con la contención; construir el agente es la frontera con AI Eng. En inglés.
- El principio de least privilege (mínimo privilegio), de la seguridad clásica: dar a cada componente el conjunto mínimo de permisos que necesita, y nada más. Es exactamente la idea de "capabilities acotadas" que la lección 4 aplica al menú de acciones del LLM. Cualquier referencia introductoria de seguridad lo cubre.