Skip to main content

Policy engine for governing AI agent tool execution.

Project description

Guardian Angel

A lightweight Python SDK for governing AI agent tool execution.

Guardian Angel intercepts agent actions, evaluates policy, and returns allow, deny, or require_approval — before the tool runs.

Install

pip install guardian-angel

# optional CLI
pip install guardian-angel[cli]

Quickstart

# policy.yaml
rules:
  - name: block_risky_delete
    tool: resource.delete
    decision: deny
    all:
      - key: resource.environment
        op: eq
        value: prod
      - key: context.risk_level
        op: eq
        value: high
from guardian_angel import ActionRequest, DecisionStatus, GuardConfig, GuardianAngel

guard = GuardianAngel.from_yaml(
    "policy.yaml",
    config=GuardConfig(
        default_decision=DecisionStatus.ALLOW,
        on_evaluation_error=DecisionStatus.DENY,
    ),
)

decision = guard.authorize(
    ActionRequest(
        tool="resource.delete",
        attributes={
            "resource.environment": "prod",
            "context.risk_level": "high",
        },
    )
)
print(decision.status)  # "deny"

First matching rule wins. No match uses default_decision, which defaults to allow.

CLI

guardian-angel evaluate policy.yaml request.json
guardian-angel evaluate policy.yaml request.json --explain
guardian-angel --verbose evaluate policy.yaml request.json
guardian-angel --version

--explain prints the matched rule and reason. --verbose adds input context.

Features

  • Predicate ruleswhen, all, any, not with operators (eq, ne, in, not_in, contains, gt, gte, lt, lte, …)
  • Explicit failure semantics — configurable default/no-match behavior, evaluation-error behavior, protected tools, and required request fields
  • Cross-field comparisonvalue_from to compare one attribute against another
  • Approval signal — rules returning require_approval raise ApprovalRequiredError, letting the calling framework handle human-in-the-loop approval in whatever way is native to it (LangGraph interrupt, CrewAI human input, webhook, etc.)
  • Tool invocationguard.invoke() (sync) and guard.ainvoke() (async) for policy enforcement on any function without decorators
  • YAML or Python — define rules in files or construct Rule objects in code
  • CLI — evaluate policies from the command line with colored output

See examples/ for more. If you want one end-to-end reference that wires everything together, start with examples/complete_pipeline_example.py.

How It Works

Agent tool call → ActionRequest → GuardianAngel.authorize() → Decision
                                                                 ├─ allow → execute
                                                                 ├─ deny  → PolicyDeniedError
                                                                 └─ require_approval → ApprovalRequiredError

Safety Modes

Guardian Angel separates:

  • no rule matched
  • policy evaluation failed
from guardian_angel import DecisionStatus, GuardConfig, GuardianAngel

# Global allow, but protected tools require approval when no rule matches.
guard = GuardianAngel(
    rules=rules,
    config=GuardConfig(
        default_decision=DecisionStatus.ALLOW,
        on_evaluation_error=DecisionStatus.DENY,
        protected_tool_prefixes=("github.", "filesystem."),
        protected_no_match_decision=DecisionStatus.REQUIRE_APPROVAL,
    ),
)

# Full fail-closed mode.
fail_closed_guard = GuardianAngel(
    rules=rules,
    config=GuardConfig(default_decision=DecisionStatus.DENY),
)

Operator Semantics

  • Missing keys do not match ordinary comparisons such as eq, gt, in, or contains.
  • Use exists and not_exists when presence itself matters.
  • Type mismatches are converted into deterministic evaluation errors.
  • Critical request fields can be required globally with GuardConfig(required_fields=(...)).

Approval Signal

When a rule returns require_approval, Guardian Angel raises ApprovalRequiredError with the full Decision attached. It does not handle the approval workflow itself — the calling framework (LangGraph interrupt, CrewAI human input, webhook, Slack bot, etc.) decides how to obtain human approval.

from guardian_angel import ApprovalRequiredError

try:
    result = guard.invoke(
        update_resource,
        "doc-1",
        guard_ctx=GuardContext(
            tool="resource.update",
            attributes={"resource.environment": "prod", "subject.role": "developer"},
        ),
    )
except ApprovalRequiredError as exc:
    print(f"Approval needed: {exc.decision}")
    # Hand off to your framework's approval mechanism

See examples/approval_example.py (sync) and examples/async_approval_example.py (async) for full working examples.

guard.invoke() / guard.ainvoke()

invoke and ainvoke call any function under policy enforcement without decorating it. Policy context is passed via guard_ctx; the function itself stays completely clean:

from guardian_angel import GuardContext

def update_resource(resource_id):
    return {"updated": True, "resource_id": resource_id}

# Sync
result = guard.invoke(
    update_resource,
    "doc-1",
    guard_ctx=GuardContext(
        tool="resource.update",
        attributes={"resource.environment": "prod", "subject.role": "developer"},
        request_id="req-1",
    ),
)

# Async — works with both sync and async functions
async def update_resource_async(resource_id):
    return {"updated": True, "resource_id": resource_id}

result = await guard.ainvoke(
    update_resource_async,
    "doc-1",
    guard_ctx=GuardContext(
        tool="resource.update",
        attributes={"resource.environment": "prod"},
    ),
)

If guard_ctx.tool is not set, the function's __name__ is used as the policy tool name.

CLI Validation

The CLI now validates request payloads before evaluation.

  • Exit code 2: invalid request input
  • Exit code 3: invalid policy input

Roadmap

  • v0.1 — Local policy evaluation, YAML rules, decorator
  • v0.2 — Stronger validation, policy linting
  • v0.3 — CLI with evaluate, --explain, --verbose
  • v0.4 — Approval signal via ApprovalRequiredError (current)
  • v0.5 — Framework adapters (LangGraph, OpenAI, CrewAI)

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

guardian_angel-0.4.4.tar.gz (79.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

guardian_angel-0.4.4-py3-none-any.whl (20.4 kB view details)

Uploaded Python 3

File details

Details for the file guardian_angel-0.4.4.tar.gz.

File metadata

  • Download URL: guardian_angel-0.4.4.tar.gz
  • Upload date:
  • Size: 79.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.10.7 {"installer":{"name":"uv","version":"0.10.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for guardian_angel-0.4.4.tar.gz
Algorithm Hash digest
SHA256 f8e42a7e040d30413a0e2190b02cc8b43c11471545283e3755f932625d6d67a4
MD5 4f2c0e409df6bd6f9c49b8ed330a64ed
BLAKE2b-256 51bce94028e2275997bdd73854c574c048e2810a6fdfec1e0c556e3dc3a3915c

See more details on using hashes here.

File details

Details for the file guardian_angel-0.4.4-py3-none-any.whl.

File metadata

  • Download URL: guardian_angel-0.4.4-py3-none-any.whl
  • Upload date:
  • Size: 20.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.10.7 {"installer":{"name":"uv","version":"0.10.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for guardian_angel-0.4.4-py3-none-any.whl
Algorithm Hash digest
SHA256 8b0d0a120c862368fbfb79ede28f6bb607f432100235a2749e8f559b38d5fb66
MD5 bc9354a87ecfa4e467104763757762d4
BLAKE2b-256 1d7d988dc5ca864d9ed743146931796d63348c7a2a8d1360af7cea02e34123b1

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page