agent-guardrail
Firewall de políticas determinista para agentes de IA que evalúa las llamadas a herramientas antes de la ejecución y devuelve decisiones ALLOW, WARN o BLOCK.
Documentación
Guardrail
Un firewall de políticas para llamadas de herramientas de agentes de IA.
Tu agente quiere ejecutar un comando de shell, enviar un correo electrónico o mover dinero. Guardrail verifica esa solicitud contra las reglas que escribiste, antes de que ocurra, y la permite, la envía a un humano, o la bloquea — con una razón en lenguaje claro cada vez.
Inicio rápido en 60 segundos
git clone <this repo> && cd agent-guardrail
pip install -r requirements.txt
python3 cli.py check --agent trading-agent-001 --tool wallet.transfer \
--args '{"amount": 9999, "to": "0xabc"}'
O pip install guardrail-mcp te da un comando guardrail
directamente — misma salida, sin necesidad de clonar el repositorio (usa la
política incluida en el paquete si no apuntas --policy a tu
propio archivo):
guardrail check --agent trading-agent-001 --tool wallet.transfer \
--args '{"amount": 9999, "to": "0xabc"}'
{
"decision": "BLOCK",
"matched_rules": [
{"rule": "numeric_cap_exceeded", "severity": "BLOCK",
"message": "amount=9999.0 exceeds cap 5 for 'wallet.transfer' (unknown agent)"}
]
}
Eso es todo — sin servidor, sin cuenta, sin clave API. policies/default.yaml es
el archivo que tomó esta decisión; ábrelo y cambia los números para que coincidan con tus
propias reglas.
Por qué esto, y no otra herramienta de "puntuación de riesgo de IA"
La mayoría de los proyectos de "seguridad de agentes de IA" (incluido un proyecto anterior mío) se apoyan en puntuaciones de riesgo estadísticas calculadas a partir de datos que nadie puede verificar realmente en tiempo de compilación — antigüedad de la billetera, "reputación", "riesgo" del contrato — lo cual requiere fuentes de datos pagadas que aún no tienes, o silenciosamente se convierte en datos simulados que pretenden ser reales. Bien para prototipos, deshonesto para publicar.
Guardrail solo hace afirmaciones que puede respaldar. Cada verificación es una regla determinista — una entrada en lista negra, una coincidencia de regex, un límite numérico, un límite de tasa — evaluada contra un archivo de políticas que escribes y puedes auditar tú mismo, respaldado por un registro de auditoría real y persistente (SQLite) que puedes consultar. Nada aquí pretende saber algo que no sabe.
Tampoco es específico de blockchain. Ejecución de shell, correo electrónico, solicitudes HTTP, eliminación de archivos, escrituras en bases de datos, transacciones criptográficas — el mismo motor, el mismo archivo de políticas, las mismas reglas.
Cuatro formas de usarlo
1. CLI — para probar una política manualmente
Mostrado arriba. Sin configuración, retroalimentación instantánea mientras escribes reglas.
2. Servidor MCP (mcp_server.py) — la vía de entrada fácil, consultivo
Expone guardrail_check, guardrail_record_outcome y
guardrail_agent_history como herramientas MCP que cualquier agente compatible con MCP (Claude
Desktop, Claude Code, clientes MCP personalizados) puede llamar.
{
"mcpServers": {
"guardrail": {
"command": "python3",
"args": ["/absolute/path/to/agent-guardrail/mcp_server.py"],
"env": { "GUARDRAIL_POLICY": "/absolute/path/to/agent-guardrail/policies/default.yaml" }
}
}
}
Luego dile a tu agente (en su prompt del sistema) que siempre llame a
guardrail_check antes de gastar dinero, eliminar datos, enviar mensajes a alguien
externo o ejecutar código.
Sé claro sobre su límite: como cualquier herramienta MCP, nada impide que el modelo que llama simplemente no la invoque. Esto solo ayuda si el agente está instruido para verificar siempre primero — para una garantía que no puede omitir, ver #3.
3. guardrail.decorator.enforce — la garantía real
Envuelve la función real de Python que realiza el efecto secundario de una herramienta. La verificación se ejecuta en tu código, antes de que esa función se ejecute — el modelo nunca tiene la oportunidad de llamar a la función real directamente.
from guardrail.decorator import enforce, BlockedActionError
@enforce(engine, tool_name="send_email")
def send_email(agent_id: str, to: str, subject: str, body: str):
... # only runs if the decision is ALLOW, or WARN-and-confirmed
Usa esto si estás construyendo tu propio bucle de agente (LangChain, CrewAI, un
host MCP personalizado, un bot de Slack con acceso a herramientas). Ejecuta python3 examples/example_agent_usage.py para verlo bloquear una llamada de función real.
4. guardrail.mcp_enforced_server.EnforcedGuardrailMCPServer — la garantía real, a través de MCP
El servidor MCP en #2 arriba es honesto sobre ser consultivo: el modelo
obtiene una herramienta guardrail_check, pero nada impide que llame a la
herramienta real (expuesta por otro servidor MCP, o por el acceso directo del propio
modelo) sin verificar primero, o verificar una cosa y hacer otra. Si el modelo se comunica
con tu infraestructura solo a través de MCP — sin decorador de Python posible — esta es la misma
garantía #3 para ese caso: el operador registra ejecutores de acciones reales (el código que tiene
credenciales reales y realiza el efecto secundario real) como la única forma en que el
modelo puede invocar esa acción.
from guardrail.mcp_enforced_server import EnforcedGuardrailMCPServer
def do_transfer(request):
wallet = get_wallet_for(request.agent_id) # real credentials, held here - never exposed to the model
tx_hash = wallet.transfer(to=request.arguments["to"], amount=request.arguments["amount"])
return {"tx_hash": tx_hash}
server = EnforcedGuardrailMCPServer(policy_path="policies/default.yaml")
server.register_action(
"wallet.transfer", "Transfer funds from the agent's wallet.",
input_schema={"type": "object", "properties": {"to": {"type": "string"}, "amount": {"type": "number"}}, "required": ["to", "amount"]},
executor=do_transfer,
)
server.serve_stdio()
Al modelo se le da exactamente una herramienta MCP llamada wallet.transfer — no hay
una forma separada y sin protección de mover fondos a través de este servidor. Una decisión de BLOQUEO
significa que do_transfer nunca se ejecuta. Tanto esto como enforce() comparten
una implementación de "verificar, quizás enrutar ADVERTENCIA a un humano, ejecutar solo si
no está bloqueado, informar el resultado real" (guardrail/enforcement.py) —
no dos copias mantenidas independientemente de la misma garantía.
Conseguir que un humano confirme realmente una ADVERTENCIA
on_warn es el gancho — Guardrail incluye dos implementaciones listas:
Interfaz web local (guardrail/confirmation/web_ui.py) — un pequeño servidor
integrado (solo stdlib, sin Flask) con botones Aprobar/Rechazar. La función
envuelta se bloquea hasta que alguien hace clic en uno, o se agota el tiempo (falla cerrado —
el tiempo de espera significa rechazo, no "permitir por defecto").
from guardrail.confirmation.web_ui import ConfirmationServer
confirmation = ConfirmationServer(port=8787, timeout_seconds=300)
confirmation.start(open_browser=True)
@enforce(engine, tool_name="wallet.transfer", on_warn=confirmation.request_confirmation)
def transfer(...): ...
Pruébalo en vivo: python3 examples/example_web_confirmation.py, luego abre
http://localhost:8787.
Prompt de terminal (guardrail/confirmation/cli_ui.py) — para scripts y
pruebas locales donde un navegador es excesivo:
from guardrail.confirmation.cli_ui import cli_confirm
@enforce(engine, tool_name="wallet.transfer", on_warn=cli_confirm)
def transfer(...): ...
Ninguno es obligatorio — on_warn es solo una función (decision) -> bool,
así que un mensaje de Slack, un ticket o cualquier otra cosa que ya uses también funciona.
Escribiendo una política
Las políticas son YAML simple — ver policies/default.yaml para un punto de partida real y
funcionante (11 herramientas con confirmación, 10 verificaciones de patrones destructivos,
límites numéricos, reglas de dominio, límites de tasa, todo comentado).
| Tipo de regla | Qué verifica |
|---|---|
blocked_tools | Nombres de herramientas que nunca están permitidos |
confirmation_required_tools | Nombres de herramientas que siempre producen WARN |
argument_patterns | Regex contra los argumentos de llamada serializados en JSON — comandos de shell destructivos, SQL, credenciales filtradas, traversal de rutas, SSRF, force-pushes, independientemente de qué herramienta los lleve |
numeric_caps | Límites numéricos de campos por herramienta, más estrictos para agentes sin historial |
aggregate_caps | Un límite compartido entre varias herramientas, rastreado como un total acumulado por agente — ver abajo |
domain_rules | Listas de permitidos/denegados en un campo de URL o destinatario de correo, por herramienta |
rate_limits | Límites de llamadas con ventana deslizante por (agente, herramienta), respaldados por SQLite |
numeric_caps limita cada herramienta de forma independiente — wallet.transfer con un límite
de 1000/día y wallet.approve con un límite de 1000/día por separado significa que un
agente que usa ambas aún puede mover 2000/día combinados. aggregate_caps
cierra eso: cada herramienta listada en el mismo grupo extrae de un total acumulado
compartido, por ejemplo
aggregate_caps:
daily_money_movement:
tools:
wallet.transfer: amount
wallet.approve: amount
window_seconds: 86400
max_unknown_agent: 5
max_known_agent: 1000
Solo el gasto confirmado cuenta para el total: una solicitud BLOCKda nunca
agrega nada, y una solicitud registrada provisionalmente (porque su propia
verificación pasó) se reembolsa si la acción real luego resulta no haber
tenido éxito — engine.record_outcome(request_id, "error"), llamado
automáticamente tanto por enforce() como por el servidor MCP forzado (comparten
una implementación de esto, guardrail/enforcement.py) cuando el
ejecutor real lanza una excepción, o cuando un WARN que un humano rechaza resulta en un
BlockedActionError. La aplicación real de esto por lo tanto tiene la misma
advertencia que todo lo demás que depende de que record_outcome sea llamado:
funciona completamente bajo enforce() y el servidor MCP forzado (ver
abajo); bajo el servidor MCP solo consultivo (#2 arriba), un
monto registrado provisionalmente simplemente permanece registrado, ya que nada
informa si la acción realmente ocurrió. Ver
el docstring del módulo de guardrail/storage/aggregate_spend.py para el panorama
completo.
No se necesitan cambios de código para ajustar nada de esto — edita el YAML, reinicia el proceso (o el servidor MCP).
Ejecutando las pruebas
pip install -r requirements.txt
PYTHONPATH=. python3 -m unittest discover -s tests -v
134 pruebas: evaluación de reglas, el pipeline completo del motor (límite de tasa real respaldado por SQLite,
seguimiento de gasto agregado y persistencia de auditoría), el
decorador enforce y el servidor MCP forzado (ambos demuestran que un BLOCK
genuinamente impide que la acción real se ejecute, compartiendo una
implementación de esa garantía), el manejo JSON-RPC del servidor MCP consultivo,
la interfaz web de confirmación sobre solicitudes HTTP reales contra un
servidor en vivo, y una suite dedicada que verifica que el policies/default.yaml enviado —
no solo políticas de prueba sintéticas — realmente
detecta lo que afirma.
Lo que honestamente aún falta
- SQLite de un solo proceso por defecto. Está bien para un proceso de agente; para múltiples réplicas que comparten límites de tasa/historial de auditoría, apunta cada proceso al mismo archivo en almacenamiento compartido, o cambia a una base de datos real (las clases de almacenamiento son pequeñas y fáciles de reorientar).
- La redacción de secretos/PII en el registro de auditoría está activada por defecto.
AuditLogredacta valores cuya clave parece sensible (password,api_key,authorization, ...) y un par de formas de valores de alta confianza (bloques de claves privadas PEM, cadenas con forma de JWT) independientemente del nombre de la clave, recursando en dicts/listas anidadas — verguardrail/storage/redaction.pypara exactamente qué se detecta y qué no, y por qué las heurísticas de entropía de propósito general se omitieron deliberadamente (demasiados falsos positivos en UUIDs/hashes ordinarios). PasaAuditLog(redact=False)para almacenar argumentos tal como se enviaron, oextra_sensitive_keys={...}para redactar nombres de campos adicionales específicos de tus herramientas. - La política predeterminada es un punto de partida razonable, no un modelo de amenazas
completo. Detecta patrones destructivos conocidos de shell/SQL
y formatos obvios de credenciales — extiende
argument_patternspara lo que tus agentes realmente toquen. - La interfaz web de confirmación no tiene autenticación. Se vincula a
127.0.0.1por diseño (no expuesta en la red), pero cualquiera con acceso local a ese puerto puede aprobar/rechazar. Está bien para la máquina de un solo desarrollador; ponla detrás de tu propia autenticación si varias personas comparten el host.
Nada de esto está simulado o falsificado — simplemente aún no está construido, y son los próximos pasos honestos si adoptas esto.
Publicar esto / lograr que la gente realmente lo use
Ver PUBLISHING.md para una lista de verificación concreta: directorios MCP a los que
enviar, qué necesita un listado y cómo se ve "terminado".
Proyectos relacionados
Mismo autor, mismo principio aplicado en otro lugar:
- agentic-wallet-guardian-v3 — una capa de decisión de seguridad para agentes de IA que transaccionan en cadena. MIT, 112 pruebas.
- x402-attest — atestaciones firmadas criptográficamente (Ed25519), verificables de forma independiente para decisiones de política de pago entre agentes. Prueba de concepto temprana.
- open-agent-attestation — especificación abierta neutral al proveedor (JWT+EdDSA) para firmar decisiones de política de agentes, verificable por cualquiera. x402-attest arriba usa un formato personalizado; esta es la versión generalizada. Borrador v0.1.
Estructura del proyecto
guardrail/
__main__.py CLI implementation — also the `guardrail` console command
mcp_server.py MCP stdio server — also the `guardrail-mcp-server` console command
core/
models.py ActionRequest, RuleMatch, GuardrailDecision (stdlib only)
policy.py Policy loader (the one place PyYAML is used)
rules.py Deterministic rule evaluators
storage/
rate_limiter.py SQLite-backed sliding-window rate limiter
audit.py SQLite-backed persistent audit log
engine.py GuardrailEngine — orchestrates rules + rate limit + audit
decorator.py enforce() — the unbypassable integration point
confirmation/
web_ui.py Local web UI for human approve/reject (stdlib http.server)
cli_ui.py Terminal-prompt confirmation
policies/default.yaml Copy of the default policy bundled into the installed package
policies/default.yaml Canonical, editable default policy (git-clone workflow)
cli.py Thin shim -> guardrail/__main__.py (for `python3 cli.py`)
mcp_server.py Thin shim -> guardrail/mcp_server.py (for `python3 mcp_server.py`)
pyproject.toml Package metadata — `pip install .` gives you `guardrail` + `guardrail-mcp-server`
.github/workflows/ci.yml Runs the test suite + policy validation + package build on every push
examples/
example_agent_usage.py Decorator basics
example_web_confirmation.py Real browser-based approve/reject, live
tests/ 46 unit tests, all runnable with just PyYAML installed
CONTRIBUTING.md How to add a rule type, ground rules
CHANGELOG.md Version history
PUBLISHING.md How to actually get this in front of people
landing/index.html Static one-page site (open directly or host on GitHub Pages)