Módulo 4: Bedrock Guardrails And Defense In Depth

1. Introducción: la baranda gestionada y la propia

Descripción

ADR-001-llm-as-escalation-path.md (Módulo 1) tiene una fila con el nombre exacto de este módulo: "M4 — Bedrock guardrails and defense in depth | Guards specifically the one path that touches a non-deterministic model; the default path needs no such guard". bedrock.tf (Módulo 3) dejó el guardrail de Andes Cargo con dos de cinco políticas activas —contenido con detección de ataques de prompt, información sensible— y un comentario explícito en su encabezado: "Module 4 extends the guardrail with the remaining policies... this file does not build those yet". Este módulo cumple esa promesa, y agrega algo que ningún terraform validate puede darte por sí solo: dos chequeos propios, deterministas, que corren sin importar si Bedrock existe, responde, o tiene guardrails configurados en absoluto.

Conexión con el módulo

El Módulo 3 fue el más ejecutado de la guía por una razón mecánica: declarar un recurso nuevo nunca necesita red. Este módulo hereda esa misma ventaja para las tres políticas que faltan —terraform validate/plan van a correr, de nuevo, de verdad, sobre el guardrail completo— y le agrega una segunda columna de ejecución real que no depende de Terraform en absoluto: guardrails/pre_invoke_checks.py y guardrails/post_invoke_checks.py, dos scripts de Python, corridos con pytest, que existen precisamente porque un guardrail declarado —por completo que esté— nunca es, por sí solo, la respuesta completa a la pregunta "¿esto es seguro de escribir en Shipments?".


Analogía: el banco no confía solo en la puerta blindada

Un banco con una puerta blindada de última generación —certificada, probada, cara— no despide a los guardias ni apaga las cámaras. La puerta blindada es una capa real, seria, que detiene a la enorme mayoría de los intentos obvios. Pero un banco de verdad también tiene un cajero entrenado para notar un comportamiento raro, un límite diario de retiro que no depende de la puerta en absoluto, y una alarma silenciosa que se activa por una razón completamente distinta a "¿la puerta está cerrada?". Ninguna de esas capas reemplaza a las otras. Cada una cubre algo que las demás, por diseño, no pueden ver.

Bedrock Guardrails —la puerta blindada de este módulo— es un mecanismo gestionado, probado por AWS, que evalúa contenido contra reglas generales: ¿esto es un intento de ataque de prompt? ¿Esto contiene un correo electrónico? ¿Esto toca un tema prohibido? Es una capa real y seria. Pero no sabe nada sobre el contrato exacto de ShipmentFields que Shipments espera recibir, ni sobre si una respuesta técnicamente inofensiva —sin PII, sin ataque de prompt, sin tema denegado— tiene, de todas formas, la forma correcta para escribirse en una base de datos real. Esa es la responsabilidad de la guardia propia de este módulo: pre_invoke_checks.py y post_invoke_checks.py, capas adicionales, no sustitutas, cada una viendo algo que la otra —gestionada o propia— no puede ver por sí sola.


El mapa de este módulo: las 8 lecciones

   M4 -- BEDROCK GUARDRAILS Y DEFENSA EN PROFUNDIDAD

   1  Introducción                            la baranda gestionada y la propia (esta lección)
   2  Las seis políticas explicadas            conceptual, verificado contra AWS Docs
   3  Manos a la obra: el guardrail completo   las 6 políticas en HCL, validate/plan reales
   4  Por qué el guardrail gestionado no basta  defensa en profundidad, qué se le escapa
   5  Manos a la obra: el scrubber de PII       pre_invoke_checks.py, pytest real
   6  Manos a la obra: el validador de esquema  post_invoke_checks.py, pytest real
   7  El límite exacto                         por qué el bloqueo real no se puede probar aquí
   8  Proyecto: la capa de guardrails completa  guardrail + 2 chequeos propios, integrados
#LecciónQué se ejecuta/practica
1Introducción (esta)Por qué un guardrail gestionado y uno propio son capas, no sustitutos
2Las seis políticas de Bedrock GuardrailsConceptual — verificado contra docs.aws.amazon.com/bedrock/ en el momento de escribir
3Manos a la obra: el guardrail completo en HCLEjecutado: las 6 políticas (5 bloques) en aws_bedrock_guardrail, validate/plan reales
4Por qué un guardrail gestionado no basta soloDefensa en profundidad — qué cubre Bedrock Guardrails, qué se le escapa
5Manos a la obra: el scrubber de PII propioEjecutado: pre_invoke_checks.py, regex, pytest — PASS/FAIL literal
6Manos a la obra: el validador de esquemaEjecutado: post_invoke_checks.py, pytest — PASS/FAIL literal
7El límite exactoRepresentativo: por qué el bloqueo real de Bedrock Guardrails no se puede probar aquí
8Proyecto: la capa de guardrails de Andes CargoEjecutado: guardrail HCL + dos chequeos propios, integrados en un solo entregable

Fíjate en algo que distingue a este módulo de los tres anteriores: la ejecución real de este módulo no depende de un solo mecanismo (como el Módulo 3 dependía enteramente de "validate/plan de un recurso nuevo nunca llama a la API"). Aquí hay dos fuentes de ejecución real, completamente independientes entre sí — Terraform de un lado, Python puro del otro — y esa independencia es, precisamente, el argumento completo de la lección 4: si un guardrail gestionado alguna vez fallara en aplicarse, o si Bedrock cambiara sus reglas sin aviso, pre_invoke_checks.py y post_invoke_checks.py seguirían corriendo exactamente igual, porque nunca dependieron de que el primero existiera.


Lo que este módulo hereda, sin repetir

Este módulo no vuelve a explicar qué es terraform validate, cómo funciona un bloque dynamic, ni por qué un recurso nuevo nunca necesita red — eso ya lo estableció el Módulo 3, lección 1, con precisión, y este módulo lo reutiliza directamente. Tampoco vuelve a explicar pytest desde cero: el Módulo 2, lección 7 ya construyó y corrió un suite completo de doce casos deterministas sobre bedrock_cost_estimate.py, y las lecciones 5 y 6 de este módulo siguen exactamente ese mismo patrón —sin random, sin datetime.now(), mismo input, mismo output, siempre— aplicado a un dominio distinto: seguridad, no costo.

Lo genuinamente nuevo de este módulo son cuatro piezas, todas en inglés como el resto del código de esta guía:

  • Tres políticas nuevas en modules/bedrock-guardrail/ (topic_policy_config, contextual_grounding_policy_config, word_policy_config), agregadas al módulo del Módulo 3 sin reescribir sus dos bloques ya existentes.
  • El módulo manifest_extractor_guardrail en bedrock.tf, ahora pasando las seis políticas completas.
  • guardrails/pre_invoke_checks.py — el scrubber de PII propio.
  • guardrails/post_invoke_checks.py — el validador del esquema ShipmentFields.

Errores comunes

Pensar que "defensa en profundidad" significa "el guardrail propio revisa lo mismo que el gestionado, por si acaso" (de redundancia mal entendida). Qué pasa: alguien asume que pre_invoke_checks.py existe solo para el caso en que Bedrock Guardrails "se equivoque" al detectar un correo electrónico, como una segunda oportunidad de atrapar exactamente el mismo tipo de error. Cómo detectarlo: si tu explicación de por qué existe el chequeo propio se limita a "por si el guardrail gestionado falla". Cómo corregirlo: la lección 4 de este módulo desarrolla esto con precisión — el chequeo propio no repite el trabajo del gestionado, cubre un territorio que el gestionado estructuralmente no puede ver (el contrato exacto de ShipmentFields, el contexto específico del negocio de Andes Cargo). Son capas complementarias, no una copia de seguridad la una de la otra.

Confundir "las seis políticas" con "seis bloques de HCL" (de conteo impreciso). Qué pasa: alguien, al escribir sobre este módulo, cuenta seis bloques dynamic distintos en modules/bedrock-guardrail/main.tf y se confunde cuando solo encuentra cinco. Cómo detectarlo: si tu conteo de bloques HCL no coincide con "seis mecanismos" en algún momento de este módulo. Cómo corregirlo: la lección 2 lo aclara con precisión, citado contra AWS Docs — los filtros de contenido general (HATE, INSULTS, SEXUAL, VIOLENCE, MISCONDUCT) y la detección de ataques de prompt son dos mecanismos distintos, pero AWS los modela dentro de un mismo bloque (content_policy_config), no en bloques separados. Seis mecanismos, cinco bloques de política — no es un error de conteo de esta guía, es cómo el schema real del provider lo estructura.

Adelantarse a escribir código de invocación real a Bedrock antes de la lección 7 (de anticipación fuera de orden). Qué pasa: alguien, entusiasmado con las lecciones 5 y 6 de este módulo, intenta conectar pre_invoke_checks.py y post_invoke_checks.py a una llamada real de bedrock-runtime invoke-model antes de que la guía llegue a ese punto. Cómo detectarlo: si tu código, en este momento del módulo, ya intenta importar un cliente de boto3 para Bedrock. Cómo corregirlo: esta guía, declarado desde el Módulo 1, nunca invoca un modelo real, en ningún módulo — la lección 7 de este módulo explica exactamente por qué, con la misma honestidad que el Módulo 3, lección 6 ya aplicó a apply. Los dos scripts de este módulo son deliberadamente independientes de cualquier llamada real; conectarlos a una sería trabajo válido en tu propia cuenta de Bedrock, pero no es parte de lo que este laboratorio $0 puede demostrar.


Ejercicios

Ejercicio 1 — Sin mirar el resto del módulo, escribe de memoria los nombres de los dos scripts de Python que este módulo construye, y qué evalúa cada uno. Después, verifica tu respuesta contra el mapa de lecciones de arriba.

Ver solución

pre_invoke_checks.py evalúa el texto crudo del manifiesto antes de que extract-shipment-manifest-fields invoque a Bedrock — busca PII conocida (correos, teléfonos) por expresiones regulares. post_invoke_checks.py evalúa la respuesta después de la invocación (real o representativa) — confirma que tiene exactamente la forma de ShipmentFields antes de que cualquier código intente escribirla en Shipments. Los nombres mismos codifican el orden: pre_ antes de la llamada al modelo, post_ después.

Ejercicio 2 — Explica, en una frase, por qué la independencia entre "Terraform" y "Python puro" como las dos fuentes de ejecución real de este módulo importa más aquí que en el Módulo 3. Piensa en qué pasaría si Bedrock, como servicio, dejara de existir mañana.

Ver solución

Importa más aquí porque, si Bedrock como servicio desapareciera —o si LocalStack retirara el soporte de Ultimate, o si esta guía se ejecutara en un entorno sin ningún acceso a Terraform—, pre_invoke_checks.py y post_invoke_checks.py seguirían siendo código Python válido, corriendo con pytest, sin ningún cambio: nunca dependieron de aws_bedrock_guardrail, de un provider, ni de una API. En el Módulo 3, en cambio, prácticamente toda la ejecución real dependía de un solo mecanismo (validate/plan de un recurso nuevo). Este módulo reparte su ejecución real entre dos sistemas que no comparten ninguna dependencia entre sí — la definición misma de defensa en profundidad, aplicada esta vez a la arquitectura de la propia guía.

Ejercicio 3 — Predice qué política de las seis (lección 2) es la única que Bedrock evalúa exclusivamente sobre la SALIDA del modelo, nunca sobre la entrada por sí sola. Basándote en la analogía de esta lección (capas que ven cosas distintas), ¿qué tipo de problema tendría sentido que solo pudiera detectarse después de que el modelo ya respondió?

Ver solución

Grounding contextual (contextual_grounding_policy_config). Tiene sentido que se evalúe solo sobre la salida porque su pregunta —"¿esta respuesta está basada en la fuente que le di, o el modelo inventó algo?"— no existe todavía cuando solo hay una entrada: una alucinación es, por definición, algo que aparece en la respuesta, comparada contra una fuente. La lección 2 de este módulo confirma esto con la cita exacta de AWS Docs, y el diseño de post_invoke_checks.py de la lección 6 aplica la misma lógica de fondo —aunque a un problema distinto (forma del esquema, no veracidad)— al mismo momento del flujo: después de la respuesta, antes de escribir.


Resumen y siguiente paso

Esta lección instaló la tesis central de todo el módulo: un guardrail gestionado y uno propio no son sustitutos, son capas — cada una ve algo que la otra, por diseño, no puede ver. Viste el mapa completo de las 8 lecciones, con dos fuentes de ejecución real completamente independientes entre sí (Terraform para las políticas del guardrail, Python puro para los dos chequeos propios), y confirmaste qué hereda este módulo sin repetir (validate/plan, la disciplina de pytest) frente a las cuatro piezas genuinamente nuevas que construye.

Antes de avanzar deberías poder: explicar, sin dudar, por qué "defensa en profundidad" no significa redundancia; nombrar las cuatro piezas nuevas de este módulo; y anticipar por qué la independencia entre Terraform y Python, como dos fuentes de ejecución real, es una decisión de diseño, no una casualidad.

La lección 2 pone las seis políticas de Bedrock Guardrails bajo la lupa, una por una, verificadas contra la documentación oficial de AWS en el momento de escribir esta guía — la base conceptual sobre la que la lección 3 va a declarar HCL real.

Recursos

  1. ADR-001-llm-as-escalation-path.md (Módulo 1, lección 8 de esta guía) — la fila que asigna a este módulo su responsabilidad exacta dentro del arco completo de la guía.
  2. bedrock.tf (Módulo 3, lección 8 de esta guía) — el archivo que este módulo extiende, con el comentario textual que ya anticipaba este trabajo.
  3. AWS — Amazon Bedrock Guardrails — panorama oficial de las seis políticas que la lección 2 desarrolla en detalle.
  4. Módulo 2, lección 7 de esta guía (07-hands-on-the-cost-per-token-calculator.md) — la fuente del patrón de pytest determinista que las lecciones 5 y 6 de este módulo reaplican.