agent-guardrail
Firewall de política determinística para agentes de IA que avalia chamadas de ferramentas antes da execução e retorna decisões ALLOW, WARN ou BLOCK.
Documentação
Guardrail
Um firewall de políticas para chamadas de ferramentas de agentes de IA.
Seu agente quer executar um comando de shell, enviar um e-mail ou mover dinheiro. O Guardrail verifica essa solicitação contra as regras que você escreveu, antes que ela aconteça, e ou permite, ou pede a um humano, ou bloqueia — com uma explicação em inglês simples a cada vez.
Início rápido em 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"}'
Ou o pip install guardrail-mcp oferece um comando guardrail
diretamente — mesma saída, sem necessidade de clonar o repositório (recorre à
política incluída no pacote se você não apontar o --policy para o seu
próprio arquivo):
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)"}
]
}
É isso — sem servidor, sem conta, sem chave de API. O policies/default.yaml é
o arquivo que decidiu isso; abra-o e altere os números para corresponder às suas
próprias regras.
Por que isso, e não outra ferramenta de "pontuação de risco de IA"
A maioria dos projetos de "segurança de agentes de IA" (incluindo um projeto anterior meu) depende de pontuações de risco estatísticas calculadas a partir de dados que ninguém consegue verificar de fato no momento da compilação — idade da carteira, "reputação", "risco" do contrato — que ou exige feeds de dados pagos que você ainda não tem, ou silenciosamente se torna dados simulados fingindo ser reais. Bom para prototipagem, desonesto para lançar.
O Guardrail só faz afirmações que pode comprovar. Cada verificação é uma regra determinística — uma entrada de lista de bloqueio, uma correspondência de regex, um limite numérico, um limite de taxa — avaliada contra um arquivo de política que você escreve e pode auditar por conta própria, respaldado por um log de auditoria real e persistente (SQLite) que você pode consultar. Nada aqui finge saber algo que não sabe.
Também não é específico de blockchain. Execução de shell, e-mail, solicitações HTTP, exclusão de arquivos, gravações em banco de dados, transações de criptomoedas — o mesmo mecanismo, o mesmo arquivo de política, as mesmas regras.
Quatro maneiras de usar
1. CLI — para testar uma política manualmente
Mostrado acima. Sem configuração, feedback instantâneo enquanto você escreve regras.
2. Servidor MCP (mcp_server.py) — o caminho fácil, consultivo
Expõe guardrail_check, guardrail_record_outcome e
guardrail_agent_history como ferramentas MCP que qualquer agente compatível com MCP (Claude
Desktop, Claude Code, clientes MCP personalizados) pode chamar.
{
"mcpServers": {
"guardrail": {
"command": "python3",
"args": ["/absolute/path/to/agent-guardrail/mcp_server.py"],
"env": { "GUARDRAIL_POLICY": "/absolute/path/to/agent-guardrail/policies/default.yaml" }
}
}
}
Depois diga ao seu agente (no prompt do sistema) para sempre chamar
guardrail_check antes de gastar dinheiro, excluir dados, enviar mensagens
externamente ou executar código.
Seja claro sobre seu limite: como qualquer ferramenta MCP, nada impede o modelo chamador de simplesmente não invocá-la. Isso só ajuda se o agente for instruído a sempre verificar primeiro — para uma garantia que ele não pode pular, veja #3.
3. guardrail.decorator.enforce — a garantia real
Encapsula a função Python real que executa o efeito colateral de uma ferramenta. A verificação ocorre no seu código, antes de essa função ser executada — o modelo nunca tem a chance de chamar a função real diretamente.
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
Use isso se você estiver construindo seu próprio loop de agente (LangChain, CrewAI, um
host MCP personalizado, um bot do Slack com acesso a ferramentas). Execute python3 examples/example_agent_usage.py para ver bloquear uma chamada de função real.
4. guardrail.mcp_enforced_server.EnforcedGuardrailMCPServer — a garantia real, via MCP
O servidor MCP em #2 acima é honesto sobre ser consultivo: o modelo
recebe uma ferramenta guardrail_check, mas nada impede que ele chame a
ferramenta real (exposta por outro servidor MCP, ou pelo acesso direto do próprio
modelo) sem verificar primeiro, ou verificar uma coisa e fazer outra.
Se o modelo fala com sua infraestrutura apenas via MCP — sem decorador Python
possível — esta é a mesma garantia #3 para esse caso:
o operador registra executores de ações reais (o código que detém credenciais
reais e executa o efeito colateral real) como a única maneira de o
modelo invocar essa ação.
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()
O modelo recebe exatamente uma ferramenta MCP chamada wallet.transfer — não há
nenhuma maneira separada e sem proteção de mover fundos por este servidor. Uma decisão BLOCK
significa que do_transfer nunca é executado. Tanto este quanto o enforce() compartilham
uma implementação de "verificar, talvez rotear WARN para um humano, executar apenas se
não bloqueado, relatar o resultado real de volta" (guardrail/enforcement.py) —
não duas cópias mantidas independentemente da mesma garantia.
Obtendo confirmação humana real para um WARN
on_warn é o gancho — o Guardrail inclui duas implementações prontas:
Interface web local (guardrail/confirmation/web_ui.py) — um pequeno servidor
integrado (apenas stdlib, sem Flask) com botões Aprovar/Rejeitar. A função
encapsulada bloqueia até que alguém clique em um, ou expira (falha fechada —
expiração significa rejeitar, não "permitir por padrão").
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(...): ...
Experimente ao vivo: python3 examples/example_web_confirmation.py, depois abra
http://localhost:8787.
Prompt de terminal (guardrail/confirmation/cli_ui.py) — para scripts e
testes locais onde um navegador é exagero:
from guardrail.confirmation.cli_ui import cli_confirm
@enforce(engine, tool_name="wallet.transfer", on_warn=cli_confirm)
def transfer(...): ...
Nenhum é obrigatório — on_warn é apenas uma função (decision) -> bool,
então uma mensagem do Slack, um ticket ou qualquer outra coisa que você já usa também funciona.
Escrevendo uma política
Políticas são YAML simples — veja policies/default.yaml para um ponto de partida real e
funcionante (11 ferramentas com confirmação obrigatória, 10 verificações de padrões
destrutivos, limites numéricos, regras de domínio, limites de taxa, tudo comentado).
| Tipo de regra | O que verifica |
|---|---|
blocked_tools | Nomes de ferramentas que nunca são permitidos |
confirmation_required_tools | Nomes de ferramentas que sempre produzem WARN |
argument_patterns | Regex contra os argumentos de chamada serializados em JSON — comandos de shell destrutivos, SQL, credenciais vazadas, path traversal, SSRF, force-pushes, independentemente de qual ferramenta os carrega |
numeric_caps | Limites numéricos de campos por ferramenta, mais rígidos para agentes sem histórico |
aggregate_caps | Um limite compartilhado entre várias ferramentas, rastreado como um total contínuo por agente — veja abaixo |
domain_rules | Listas de permitir/negar em um campo de URL ou destinatário de e-mail, por ferramenta |
rate_limits | Limites de chamadas em janela deslizante por (agente, ferramenta), respaldados por SQLite |
numeric_caps limita cada ferramenta independentemente — wallet.transfer limitado
a 1000/dia e wallet.approve limitado a 1000/dia separadamente significa que um
agente usando ambos ainda pode mover 2000/dia combinados. aggregate_caps
fecha isso: cada ferramenta listada no mesmo grupo usa um total contínuo
compartilhado, por exemplo
aggregate_caps:
daily_money_movement:
tools:
wallet.transfer: amount
wallet.approve: amount
window_seconds: 86400
max_unknown_agent: 5
max_known_agent: 1000
Apenas gastos confirmados contam para o total: uma solicitação BLOCKada nunca
adiciona nada, e uma solicitação registrada provisoriamente (porque sua
própria verificação passou) é reembolsada se a ação real depois não tiver
sido bem-sucedida — engine.record_outcome(request_id, "error"), chamado
automaticamente tanto pelo enforce() quanto pelo servidor MCP forçado (eles
compartilham uma implementação disso, guardrail/enforcement.py) quando o
executor real levanta uma exceção, ou quando um WARN que um humano rejeita resulta em
um BlockedActionError. A aplicação real disso, portanto, tem a mesma
ressalva que tudo o mais que depende de record_outcome ser chamado:
funciona totalmente sob enforce() e o servidor MCP forçado (veja
abaixo); sob o servidor MCP apenas consultivo (#2 acima), um
valor registrado provisoriamente apenas permanece registrado, já que nada
relata de volta se a ação realmente aconteceu. Veja
o docstring do módulo de guardrail/storage/aggregate_spend.py para o quadro
completo.
Nenhuma mudança de código é necessária para ajustar nada disso — edite o YAML, reinicie o processo (ou o servidor MCP).
Executando os testes
pip install -r requirements.txt
PYTHONPATH=. python3 -m unittest discover -s tests -v
134 testes: avaliação de regras, o pipeline completo do mecanismo (limite de taxa real
respaldado por SQLite, rastreamento de gastos agregados e persistência de auditoria), o
decorador enforce e o servidor MCP forçado (ambos provando que um BLOCK
genuinamente impede a ação real de ser executada, compartilhando uma
implementação dessa garantia), o tratamento JSON-RPC do servidor MCP consultivo,
a interface web de confirmação sobre solicitações HTTP reais contra um
servidor ativo, e uma suíte dedicada que verifica se o policies/default.yaml enviado —
não apenas políticas de teste sintéticas — realmente
captura o que afirma.
O que honestamente ainda falta
- SQLite de processo único por padrão. Bom para um processo de agente; para múltiplas réplicas compartilhando limites de taxa/histórico de auditoria, aponte cada processo para o mesmo arquivo em armazenamento compartilhado, ou troque por um banco de dados real (as classes de armazenamento são pequenas e fáceis de redirecionar).
- Redação de segredos/PII no log de auditoria está ativada por padrão.
AuditLogredige valores cuja chave parece sensível (password,api_key,authorization, ...) e alguns formatos de valor de alta confiança (blocos de chave privada PEM, strings em formato JWT) independentemente do nome da chave, recursando em dicts/listas aninhados — vejaguardrail/storage/redaction.pypara exatamente o que é e não é capturado, e por que heurísticas de entropia de propósito geral foram deliberadamente deixadas de fora (muitos falsos positivos em UUIDs/hashes comuns). PasseAuditLog(redact=False)para armazenar argumentos como enviados, ouextra_sensitive_keys={...}para redigir nomes de campos adicionais específicos das suas ferramentas. - A política padrão é um ponto de partida razoável, não um modelo de ameaça
completo. Ela captura padrões destrutivos bem conhecidos de shell/SQL
e formatos óbvios de credenciais — estenda
argument_patternspara o que seus agentes realmente tocam. - A interface web de confirmação não tem autenticação. Ela vincula a
127.0.0.1por design (não exposta na rede), mas qualquer pessoa com acesso local a essa porta pode aprovar/rejeitar. Bom para a máquina de um único desenvolvedor; coloque atrás da sua própria autenticação se várias pessoas compartilharem o host.
Nenhum desses é simulado ou falso — eles simplesmente ainda não foram construídos, e são os próximos passos honestos se você adotar isso.
Publicando isso / fazendo as pessoas realmente usarem
Veja PUBLISHING.md para uma lista de verificação concreta: diretórios MCP para enviar,
o que uma listagem precisa, e como é o "pronto".
Projetos relacionados
Mesmo autor, mesmo princípio aplicado em outro lugar:
- agentic-wallet-guardian-v3 — uma camada de decisão de segurança para agentes de IA transacionando on-chain. MIT, 112 testes.
- x402-attest — atestações assinadas criptograficamente (Ed25519), verificáveis de forma independente para decisões de política de pagamento entre agentes. Prova de conceito inicial.
- open-agent-attestation — especificação aberta neutra de fornecedor (JWT+EdDSA) para assinar decisões de política de agentes, verificável por qualquer pessoa. O x402-attest acima usa um formato personalizado; esta é a versão generalizada. Rascunho v0.1.
Estrutura do projeto
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)