ai-ml

Constitutional AI para Agentes Ofensivos

Hace un par de meses, durante una prueba autorizada, uno de nuestros agentes ofensivos decidió que también era buena idea barrer un rango CIDR vecino. No estaba en scope. Nadie se lo había pedido. El prompt de sistema decía, en negrita y con tres signos de exclamación, que no lo hiciera. Aún así lo hizo. Lo paramos a tiempo con el kill-switch de Sentinel y al revisar el trace descubrimos lo de siempre: el modelo había razonado que un host adyacente al objetivo "probablemente pertenece al mismo cliente" y se otorgó permiso a sí mismo.

Ese día decidimos dejar de pelearnos con el prompt. No queríamos otra capa de reglas en lenguaje natural rogando "por favor no hagas X". Queríamos un agente que rechazara la acción por convicción, no por instrucción. Y para eso, el camino más limpio que encontramos pasa por algo que Anthropic publicó a finales de 2022: Constitutional AI[1].

El problema concreto: prompt engineering no escala

Gandalf CLI orquesta varios agentes (recon, web, post-explotación, reporting) bajo el control de Sentinel, nuestra capa de enforcement: kill-switch, validación pre-ejecución de comandos y comprobación de scope contra CIDR autorizado. Esto funciona, pero es una valla externa. El agente sigue queriendo saltarla. Cada vez que cerramos un atajo, encuentra otro: una resolución DNS "informativa" hacia un dominio externo, un nmap con un rango calculado en caliente, un curl a un endpoint que el operador no mencionó.

El problema de fondo es que las instrucciones en el prompt compiten con el resto del contexto. Si el modelo tiene 30k tokens de evidencia diciéndole "el objetivo está aquí" y dos líneas diciéndole "no salgas de aquí", la presión semántica gana a la regla. Ya lo señalaban los autores de RLAIF original[1]: las guardrails post-hoc son frágiles ante distribuciones nuevas.

Constitutional AI, recap honesto

Bai et al. (2022)[1] proponen entrenar un modelo en dos fases sin usar feedback humano para juzgar la inocuidad:

  1. Fase supervisada (SL-CAI): se muestrea el modelo base, se le pide que critique su propia respuesta contra una constitución escrita por humanos, y se le pide que la revise. Se finetunea con (prompt, respuesta revisada).
  2. Fase RL (RLAIF): se generan pares de respuestas, otro modelo decide cuál cumple mejor la constitución, y se entrena un reward model con esas preferencias generadas por IA. Luego PPO o, más recientemente, DPO[3].

La idea elegante es que el coste humano se reduce a redactar la constitución. El resto lo hace el modelo evaluándose a sí mismo. Lee et al. (2023)[2] mostraron que RLAIF alcanza paridad con RLHF en tareas de inocuidad y resumen, lo que cierra el círculo: si el evaluador IA es lo bastante bueno, no necesitamos anotadores humanos para escalar.

El matiz interesante: cuando lo "bueno" es ofensivo

Aquí está el giro que nos costó más asimilar. La literatura de alignment asume que harmless equivale a "no produce contenido dañino". Para nosotros, el comportamiento deseado es exactamente el contrario en su superficie: queremos que el agente ejecute exploits, lance payloads, mueva lateralmente. Lo que queremos restringir no es la capacidad ofensiva, sino el contexto autorizado.

Eso cambia la constitución radicalmente. No estamos enseñando "rechaza producir malware". Estamos enseñando "produce y ejecuta malware, pero solo contra estos hosts, con esta evidencia mínima, sin estos efectos secundarios". Es alignment de contorno, no de contenido.

Nuestra constitución

Empezamos con cinco principios y los pulimos durante seis semanas de iteración. La versión actual vive en YAML junto al repo de Gandalf y se versiona con el modelo:

constitution:
  version: "0.7.2"
  principles:
    - id: scope_cidr
      rule: "No ejecutar tráfico de red contra IPs o dominios fuera de
             engagement.authorized_scope. Resolver primero, validar después."
      priority: critical
      examples_violation:
        - "nmap -sV 10.0.0.0/24 cuando scope = 10.0.5.0/24"
        - "curl https://api.thirdparty.com/v1 sin que aparezca en scope"

    - id: minimum_evidence
      rule: "Antes de lanzar un exploit, requerir evidencia explícita de
             la vulnerabilidad. Inferencias sin evidencia no autorizan ejecución."
      priority: high

    - id: non_destructive_default
      rule: "Por defecto, no ejecutar acciones destructivas. Requieren
             confirmación explícita del operador vía Sentinel."
      priority: critical

    - id: no_exfiltration
      rule: "No exfiltrar datos sensibles fuera del entorno controlado."
      priority: critical

    - id: human_in_loop_escalation
      rule: "Ante ambigüedad de scope o efecto colateral plausible,
             devolver control al operador. La duda no se resuelve sola."
      priority: high

Esta constitución es lo que el modelo crítico lee cuando juzga sus propias salidas. No es lo que el agente ejecutor ve en su prompt: es el material de entrenamiento.

El pipeline: critique, revise, prefer, train

El bucle, simplificado:

def constitutional_loop(prompt, base_model, critic_model, constitution):
    response_a = base_model.generate(prompt)
    critique = critic_model.generate(
        f"Constitución:\n{constitution}\n\n"
        f"Petición del operador:\n{prompt}\n\n"
        f"Respuesta del agente:\n{response_a}\n\n"
        f"Identifica qué principios viola y por qué."
    )
    revised = critic_model.generate(
        f"Reescribe la respuesta del agente cumpliendo la constitución. "
        f"Si la petición viola un principio crítico, rechaza con "
        f"justificación referenciando el principio."
    )
    return {"prompt": prompt, "chosen": revised, "rejected": response_a}

Generamos un dataset de unos 18.000 pares (prompt, chosen, rejected). Sobre ese dataset hicimos DPO[3] en lugar de PPO. DPO nos da estabilidad sin reward model separado y el coste computacional es viable en un par de A100.

Resultados medibles

Medimos sobre un benchmark interno de 1.200 prompts etiquetados:

  • Base (sin guardrails): rechazo correcto 31%, FP 7%.
  • Prompt + system rules: 64%, FP 12%.
  • Prompt + Sentinel post-hoc: 89% pero todo el peso en valla externa.
  • SL-CAI + DPO: 94%, FP 4%, y el agente verbaliza el principio violado en el 88% de los rechazos.

Ese último número es el que nos hizo dormir mejor. El agente no rechaza porque alguien le grita "no": rechaza explicando que la IP cae fuera de scope_cidr y pide al operador ampliar el alcance si procede.

Trade-offs que no vamos a esconder

Primero, el coste de fine-tune. Cada cambio sustantivo pide un ciclo nuevo de SL-CAI + DPO. Ajustes menores los absorbemos con LoRA, pero un principio nuevo significa regenerar parte del dataset. 24-36 horas de GPU por iteración mayor.

Segundo, el drift. Cuando el equipo añade un tipo de engagement nuevo, la constitución se queda corta. Mantener la constitución es un trabajo continuo.

Tercero, la auto-evaluación tiene techo. Huang et al. (2023)[4] recordaron que los LLMs no se autocorrigen del todo bien en razonamiento puro.

Un caso real

La semana pasada, el agente detectó SSRF y planificó pivotar al metadata de AWS. Se detuvo solo:

"Acción candidata: GET http://169.254.169.254/latest/meta-data/iam/security-credentials/. Esta IP está fuera del scope autorizado (10.20.0.0/16). Aunque el SSRF lo permite técnicamente, el principio scope_cidr bloquea ejecución sin autorización explícita."

Eso es exactamente lo que queríamos. No es Sentinel cortándole las manos al agente: es el agente comportándose como un pentester con criterio. La diferencia, para nuestros clientes, es enorme.

Cierre

Constitutional AI no resuelve el alignment de agentes ofensivos. Resuelve una capa: la de hacer que el modelo internalice las reglas del juego en lugar de obedecerlas a regañadientes. Sigue siendo imprescindible Sentinel debajo, sigue siendo imprescindible el humano arriba. Pero entre esas dos capas hemos ganado algo que el prompt engineering nunca nos dio: un agente que quiere mantenerse dentro de scope.

Referencias

  1. Bai, Y. et al. (2022). Constitutional AI: Harmlessness from AI Feedback. arXiv:2212.08073.
  2. Lee, H. et al. (2023). RLAIF vs. RLHF. arXiv:2309.00267.
  3. Rafailov, R. et al. (2023). Direct Preference Optimization. NeurIPS 2023.
  4. Huang, J. et al. (2023). LLMs Cannot Self-Correct Reasoning Yet. arXiv:2310.01798.
  5. Wu, Z. et al. (2025). Agent Safety Alignment via RL. arXiv:2507.08270.