Un mapa de fallos correcto permite asignar responsabilidades de manera concreta. El modelo generaba texto, pero era el backend el que decidía si se ejecutaba la operación POST /v1/tools/refund.create; es en el backend donde podemos verificar el alcance del actor (principal), exigir aprobación para acciones irreversibles e incluir una clave de idempotencia para evitar cargos duplicados en caso de reintentos. Al establecer este límite, los intentos de inyección de prompts pasan a ser entradas que no superan la validación y autorización, en lugar de instrucciones que derivan en movimientos de dinero.
Damos por sentado un conocimiento práctico de los códigos de estado HTTP; es decir, ya sabemos qué implica una respuesta de rechazo en la comunicación de red.
Asumimos conocimientos básicos de JWT, lo que implica saber verificar firmas y comprobar el emisor, la audiencia y la fecha de caducidad utilizando una biblioteca fiable.
Damos por hecho el uso de la inyección de dependencias de FastAPI, lo que nos permite implementar comprobaciones de autenticación, autorización y cuotas como dependencias con ámbito de solicitud (request scope), en lugar de dispersar estas verificaciones por los distintos handlers.
El producto que desarrollaremos a lo largo del curso es una canalización (pipeline) de backend de referencia, en la que cada etapa cuenta con un punto de rechazo claro, cada registro de log posee campos estables que facilitan el análisis y cada caso de regresión está vinculado a un control específico. El objetivo no es crear un prompt ingenioso, sino un backend capaz de realizar RAG de forma segura sobre el historial de la cuenta, invocar refund.create únicamente cuando el usuario u_123 tenga los permisos y la aprobación necesarios, y ejecutar scripts de análisis sin conceder acceso a la red ni a secretos. El siguiente diagrama ilustra la ruta de la solicitud que reforzaremos paso a paso.
El backend de un copiloto de soporte basado en FastAPI suele fallar de manera convencional. El backend lee el historial de la cuenta acct_987, un modelo sugiere los siguientes pasos y una llamada a una herramienta emite un reembolso para la transacción txn_456 porque el texto del ticket de soporte transmitía seguridad. No ocurrió nada inusual. La aplicación aceptó texto no confiable del ticket, trató la sugerencia como una autorización y ejecutó refund.create con los privilegios del servicio, en lugar de aplicar los permisos del usuario final y un paso de aprobación.
La labor de seguridad en este curso consiste en implementar las etapas de control faltantes en los únicos lugares donde pueden aplicarse. La redacción del prompt no puede impedir que un enrutador de herramientas envíe una solicitud, y un modelo no puede demostrar que el usuario u_123 tiene permiso para reembolsar la transacción txn_456. O bien el backend rechaza la solicitud antes de ejecutar la herramienta, o bien ejecuta la herramienta y desembolsa dinero real.
Mantenemos una referencia concreta constante en cada lección para que cada control se aplique a los mismos elementos del sistema. El tenant es t_acme, el usuario final es u_123, la cuenta es acct_987 y la transacción candidata a reembolso es txn_456. La única herramienta de reembolso se llama refund.create, y la única ruta que accede a ella es POST /v1/tools/refund.create.
También definimos el único nombre de ámbito (scope) que permite realizar un reembolso. Un sujeto (principal) que carece del permiso support:refund puede conversar sobre políticas, resumir el historial de la cuenta y redactar una respuesta, pero no debe ejecutar refund.create. La elección del modelo se mantiene explícita pero abstracta: la aplicación invoca un modelo de chat mediante $CHAT_MODEL y utiliza $MODERATION_MODEL cuando una lección posterior requiere una llamada específica de moderación.
Una vez establecidos los nombres, podemos trazar la ruta de una solicitud e identificar dónde ingresan datos no confiables. El mensaje del emisor no es confiable, incluso si proviene de un agente de soporte autenticado, ya que puede contener texto copiado del cliente o contenido del ticket. Los fragmentos recuperados del historial de la cuenta no son confiables, ya que la recuperación devuelve todo lo que contiene el almacén de datos, incluidas notas obsoletas y texto libre que podría intentar influir en el comportamiento del modelo. Los resultados de las herramientas no son confiables porque los sistemas posteriores pueden devolver cadenas o campos inesperados, y la salida del modelo tampoco lo es, ya que este optimiza la generación de texto verosímil, no el cumplimiento de políticas.
El curso implementa únicamente aquellos puntos de control capaces de transformar entradas no confiables en una acción acotada y auditable. Autenticamos y vinculamos al sujeto (principal) de modo que cada solicitud incluya t_acme y u_123 como datos verificados por el servidor, y no como encabezados proporcionados por quien realiza la llamada. Medimos el consumo de cuota antes de invocar al proveedor para evitar que un único punto final agote en una sola noche los tokens asignados para todo un mes. Autorizamos la recuperación de datos en el punto de llamada para garantizar que el backend solo obtenga fragmentos que u_123 tiene permiso para ver, tratando además cada fragmento recuperado como contenido no confiable. Validamos la salida estructurada del modelo antes de que sea procesada por cualquier enrutador, analizador sintáctico, plantilla o herramienta. Autorizamos y aprobamos las llamadas a herramientas para impedir que refund.create se ejecute simplemente porque el modelo emitió una sugerencia persuasiva. Ejecutamos los scripts de análisis en entornos aislados (sandboxes) para evitar que el código generado acceda a la red o a secretos. Auditamos cada rechazo y cada acción ejecutada, registrando información suficiente para explicar quién realizó la acción, para qué inquilino (tenant) y por qué el backend la permitió.
Cuando el texto de un ticket de soporte intenta iniciar un reembolso, ¿qué etapa podría haber rechazado dicho reembolso antes de que se ejecutara ninguna herramienta?