Skip to content

spydra-tech/policy-agent

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

11 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

openagentpolicy

Policy enforcement runtime for AI and agentic applications.

The problem

Agentic systems (LangGraph, CrewAI, custom orchestrators, copilots with tools) do not just generate text — they invoke code that changes the world: approve loans, place trades, update records, send messages. When something goes wrong, it is rarely because the model “didn’t know” the rule. It is because the rule was never bound to execution.

Failure modes that show up in production

1. Valid tool calls, invalid business outcomes

An agent calls approve_loan(approved_amount=7500, approval_mode="auto"). The function runs, the side effect happens, and only later does someone notice the business rule (“no auto-approval above $5,000”) was violated. The LLM had the right intent in the prompt; the runtime had no gate on the tool boundary.

2. Shared tools, different roles — no consistent access control

loan-agent, compliance-agent, and a retail copilot may all call the same approve_loan or place_trade_order function. Without a single enforcement layer, every framework path reimplements if agent_id != ... (or forgets to). One code path blocks; another bypasses the wrapper and calls the underlying callable directly.

3. Compliant tools, non-compliant user-facing text

Tool usage can be perfect while the final answer says “guaranteed approval” or “guaranteed returns.” Regulated domains (lending, healthcare, investments) care about what reaches the user, not only which APIs were called. Prompt instructions alone do not reliably prevent that.

4. Rules live in documents, not in runtime

Policies are written in Confluence, compliance playbooks, or English paragraphs in a UI — but engineers ship scattered if checks (or nothing at all). Updates require code deploys; auditors cannot replay “which policy fired on this call?” from a single trace. Typos in field names can fail open (policy never matches → default allow).

5. Framework churn hides the real boundary

Teams switch orchestrators or add MCP servers and remote tools. Guardrails tied to one framework’s callback shape do not transfer. The durable boundary is simpler: the decorated tool call in-process, the agent identity for that turn, and the text you return to the user — but most stacks do not standardize those three.

Why “just add checks in the agent” is not enough

  • Duplication: the same threshold, allowlist, and workflow flag copied into every node, service, and example script.
  • Drift: production allows what the demo blocked because one path never got the new if.
  • No compile-time safety: English policy text is not validated against inventory (unknown tools, misspelled fields) until something fails silently at runtime.
  • Weak audit story: logs show “tool failed” without structured policy id, matched conditions, and decision type.

The solution

openagentpolicy is a policy enforcement runtime that sits on those boundaries. Rules are declared as inventory + policies (YAML or HTTP), optionally authored in English and compiled to structured form. At runtime, only compiled structured policies are enforced — deterministically, with traces and audit events.

You add a small amount of integration code; governance owns the policy files.

Three enforcement boundaries

Boundary Integration What it enforces
Tool @policy_tool on functions before_tool_call (can block or modify args before side effects), after_tool_call (redact/warn/log — cannot undo side effects)
Agent agent_session(agent_id, metadata=...) around a turn or graph node Who is acting; workflow flags (e.g. human_reviewed, compliance_cleared)
Output check_final_response(...) before returning text before_final_response (e.g. forbidden phrases)

See Event semantics for what each trigger can and cannot do.

English policies → compiled → enforced

Compliance and product teams write rules in natural language (or structured YAML). The compiler:

  • Resolves terms against inventory (tools, arguments, agents, aliases).
  • Produces a structured policy or a clear not_enforceable / needs_review result.
  • In AI/hybrid mode, requires deterministic corroboration before auto-activation.

Production runtimes should load compiled policies only (policies.require_compiled: true), with English compilation done in authoring/UI — not on every request.

How the solution maps to the failure modes

Costly automation mistake — A before_tool_call policy blocks approved_amount > 5000 when approval_mode is auto. The tool never runs; PolicyViolation forces an escalation path.

Shared tools, role-specific control — One policy: only loan-agent may call approve_loan (or only trading-desk-agent may call place_trade_order). Every orchestrator path that uses @policy_tool + agent_session gets the same decision.

Non-compliant final textbefore_final_response blocks phrases like “guaranteed approval” before the user sees them (literal contains / regex — not semantic classification; see Scope and limitations).

Rules in runtime, not only in docs — Policies are versioned files or API payloads; reload without rewriting agent logic. Decisions land in traces/audit logs with matched policy ids.

Framework-agnostic at the boundary — LangGraph, a custom loop, or a CLI can all call the same decorated tools inside agent_session. What matters is that tools are not bypassed.

What you add in code (typical integration)

from openagentpolicy import configure, policy_tool, agent_session, check_final_response

configure("openagentpolicy.yaml")  # once at startup

@policy_tool(id="approve_loan", risk_level="high", side_effect=True)
def approve_loan(application_id: str, approved_amount: float):
    ...

with agent_session("loan-agent", metadata={"human_reviewed": True}):
    approve_loan(...)

safe = check_final_response(draft_reply, agent_id="loan-agent")

Policies themselves live in policies/*.yaml — not as Python branches.

Engineering outcomes

  • One place for guardrails instead of N copies in graph nodes and microservices.
  • Faster policy change — edit YAML, reload; optional HTTP policy provider.
  • Auditability — structured decisions in traces (tool, agent, action, message).
  • Safer multi-agent scale — agent id and session metadata drive allowlists and workflow gates without per-agent code forks.

Examples in this repository

Event semantics: before vs after tool call

Tool enforcement runs at two points, and they have different capabilities:

Event Runs Can prevent side effects? Allowed actions
before_tool_call before the tool executes Yes block, modify_args, warn, log_only, allow
after_tool_call after the tool has returned No redact_result, warn, log_only, escalate

Important: by the time after_tool_call runs, the tool has already executed and any side effects (writes, payments, emails) have already happened. An after_tool_call policy therefore cannot block, modify args, or redirect the call — those decisions are ignored (with a warning in logs).

What after_tool_call can do:

  • redact_result — actually redacts the value returned to the caller using the configured privacy redactor (key/schema rules + optional PII scanning). This sanitizes what flows downstream, but does not undo the side effect.
  • warn / log_only — record the condition.
  • escalate — emit an escalation signal for out-of-band review.

If you need to prevent an action (the Example A failure mode — "don't approve above 5000 in auto mode"), the policy must trigger on before_tool_call. Put side-effecting work behind a before_tool_call guard; use after_tool_call only to sanitize or flag results you cannot stop.

Authoring vs Runtime Modes

openagentpolicy now separates policy authoring from runtime enforcement.

  • Authoring / compilation mode

    • Input can be English policy text.
    • Compiler resolves terms against inventory.
    • Compiler returns either:
      • compiled structured/compiled policy object, or
      • clear not_enforceable / needs_review result with errors.
    • Only compiled structured policy should be saved/activated.
  • Runtime enforcement mode

    • Runtime enforces deterministic structured/compiled policies only.
    • English is an authoring format, not runtime enforcement format.
    • In production, recommended config:
      • compiler.enabled: false
      • policies.require_compiled: true
      • enforcement.fail_on_unresolved_fields: true
      • enforcement.fail_on_unenforceable: true

Correct production flow

English policy from UI/API -> compile against inventory -> validate enforceability -> generate deterministic compiled policy -> review/approve -> save compiled policy -> runtime enforces compiled policy only

Quickstart

pip install -e ".[dev]"
from openagentpolicy import configure, policy_tool

configure("openagentpolicy.yaml")

@policy_tool(id="approve_loan", risk_level="high", side_effect=True)
def approve_loan(application_id: str, approved_amount: float):
    return {"status": "approved", "application_id": application_id}

Run the Example Demo

The examples/agentic_loan_desk example shows English policies compiled and enforced at both tool and agent scope.

  1. Install the package from the repo root:
pip install -e ".[dev]"
  1. Run the scripted demo (no LLM required — uses the rule-based compiler):
cd examples/agentic_loan_desk
python demo.py

You'll see five phases: English-policy compilation, tool threshold enforcement ($4k allowed / $7k blocked), agent allowlist (compliance-agent blocked), the human-review workflow gate, and the final-response guard. An audit trail is written to openagentpolicy_traces/events.jsonl.

  1. (Optional) Run the same flow through the AI compiler:
pip install openai
export OPENAI_API_KEY=sk-...
cd examples/agentic_loan_desk
python ai_demo.py

This uses openagentpolicy.ai.yaml (english_compiler: ai). The LLM runs only at load time, and its output is enforced only when the rule-based compiler independently agrees (corroboration). Without a key the English policies show as needs_review and are not enforced; ai_demo.py prints which policies loaded.

  1. (Optional) Inspect compilation without running scenarios:
cd examples/agentic_loan_desk
openagentpolicy compile-policy policies/01_tool_high_value_english.yaml --inventory inventory.yaml
openagentpolicy explain --config openagentpolicy.yaml --format json

Run the scripts from inside examples/agentic_loan_desk/: the configs use relative paths resolved against the config file, and the scripts import the local tools.py / agent_session.py. For a smaller, tool-only starting point see examples/loan_approval_basic/.

How It Fits Agent Frameworks

openagentpolicy is framework-agnostic. You keep your existing framework and add policy hooks at boundaries:

  • Tool boundary: decorate callable tools with @policy_tool.
  • Agent boundary: wrap a run/turn with agent_session(agent_id, metadata=...).
  • Output boundary: call check_final_response(...) or check_final_response_any(...) before returning user-visible text.

Custom orchestrator

from openagentpolicy import agent_session, check_final_response

def run_turn(agent_id: str, session_meta: dict, messages: list[str]) -> str:
    with agent_session(agent_id, metadata=session_meta):
        draft = agent_generate_reply(messages)
    return check_final_response(draft)

LangGraph / LangChain style

from openagentpolicy import agent_session, check_final_response_any

def invoke_graph(graph, state: dict) -> dict:
    with agent_session(state["agent_id"], metadata=state.get("metadata", {})):
        out = graph.invoke(state)
    out["final_message"] = check_final_response_any(out["final_message"])
    return out

Crew-style multi-agent run

from openagentpolicy import agent_session

def run_agent(agent, task: str, metadata: dict) -> str:
    with agent_session(agent.id, metadata=metadata):
        return agent.run(task)  # tools called inside remain policy-enforced

Scope and Limitations

Read these before relying on openagentpolicy as a control. They are deliberate design boundaries, not bugs.

Enforcement is in-process, at the boundaries you wrap

Policies only fire when a tool is invoked through the decorated function in the same process. The "framework-agnostic" claim holds at those boundaries and no further:

  • If a framework, planner, or scheduler invokes the underlying callable directly (bypassing the @policy_tool wrapper), it is invisible to enforcement.
  • Remote/out-of-band tools (HTTP services, MCP servers, other processes) are not intercepted unless the call into them is itself wrapped.
  • The output guard only sees text you explicitly pass to check_final_response(...) / check_final_response_any(...).

Treat the decorated callable and the explicit output check as the trust boundary. Anything reaching a tool by another path is unenforced.

before_final_response is a string check, not semantic filtering

Final-response policies match on the response text using contains (case- insensitive by default) and regex operators. This is literal string matching:

  • It catches "guaranteed approval" but not "approval is guaranteed", "we guarantee you'll be approved", or other paraphrases.
  • It is not a semantic classifier and does not understand intent or meaning.

Use it for known forbidden phrases and patterns. Do not mistake it for an output-safety/LLM-judge layer; pair it with one if you need semantic coverage.

Default posture is "permit unless explicitly blocked"

With default_action: allow (the default) plus fail-open field resolution, the system's safety stance is allow by default, block only on an explicit match. A policy that never matches (or whose tool is never wrapped) results in the action being permitted.

For regulated deployments (BFSI, healthcare), this default is a deliberate decision to revisit. Many such buyers will want default_action: block as the baseline — at minimum for side_effect: true, high-risk tools — so that the posture becomes "deny unless explicitly allowed." Load-time validation already hard-fails on unresolved fields when default_action: allow (see Unresolved field safety), but that only protects against typos, not against unwrapped or out-of-band calls.

Configuration

application:
  id: my_app
  environment: production

runtime:
  default_agent_id: loan-agent

inventory:
  provider: file
  path: ./inventory.yaml

policies:
  provider: directory
  path: ./policies
  support_english: true
  compile_on_startup: true

traces:
  enabled: true
  store:
    provider: local
    path: ./openagentpolicy_traces

enforcement:
  default_action: allow
  on_policy_error: allow_with_warning
  audit_enabled: true
  fail_on_unresolved_fields: false  # see note below

privacy:
  redact_keys: [password, token, secret, pan, aadhaar, ssn]
  pii_detection:
    enabled: false          # opt-in; off by default
    engine: regex           # regex | presidio
    languages: [en]
    entities: [CREDIT_CARD, EMAIL_ADDRESS, PHONE_NUMBER, IN_PAN, IN_AADHAAR, US_SSN]
    scan_fields: [final_response, tool_result, tool_args]

Unresolved field safety (fail-open protection)

Every policy condition field is validated against the event context and inventory at load time. A field that does not resolve (a typo like tool_args.amount instead of tool_args.approved_amount, or a field not available for the trigger event) is handled as follows:

  • fail_on_unresolved_fields: true — startup fails on any unresolved field.
  • fail_on_unresolved_fields: false (default):
    • If default_action: allow (fail open), unresolved condition fields are still rejected at startup. A misspelled field would otherwise silently never match and allow the guarded action — the worst outcome for a governance tool, so this is treated as a hard error.
    • If default_action: block (fail closed), unresolved fields are logged as warnings, because a non-matching policy still blocks.

Action-shape problems (e.g. a block action on after_tool_call, which the runtime cannot honor) only fail startup under fail_on_unresolved_fields: true, since they do not fail open.

Privacy and PII Redaction

Redaction runs in two complementary layers before anything is written to audit logs or traces:

  1. Key/schema redaction (always on, deterministic).

    • Any field whose name matches privacy.redact_keys is masked.
    • Any tool argument flagged sensitive: true in inventory is masked.
    • This layer never inspects values, so it is fast and predictable.
  2. Value-level PII detection (optional, opt-in).

    • Scans free-text values (e.g. final_response, string args/results) for PII that is not captured by key names.
    • Controlled by privacy.pii_detection.

Engines

  • engine: regex (default when enabled): deterministic, local, dependency-free. Uses regex plus checksum validation — Luhn for credit cards, Verhoeff for Aadhaar — to keep false positives low. Recommended for production because it is reproducible and needs no model calls.
  • engine: presidio: value-level NER + recognizers via Microsoft Presidio. Best when you need broad free-text PII (names, addresses, locations). Requires the optional dependency:
pip install openagentpolicy[pii]

Presidio is imported lazily, so the core runtime stays dependency-free. Because its NER recognizers are model-dependent, prefer regex when you only need structured identifiers (PAN, Aadhaar, SSN, cards).

scan_fields controls which event fields are scanned for PII values. Key/schema redaction always applies regardless of scan_fields.

Programmatic use

from openagentpolicy.pii import RegexPiiDetector, redact_spans

detector = RegexPiiDetector.from_entities(["EMAIL_ADDRESS", "IN_PAN"])
text = "Mail john@example.com, PAN ABCDE1234F"
spans = detector.detect(text)
safe = redact_spans(text, spans)

Inventory: File and API Examples

Static file inventory (inventory.provider: file)

application:
  id: loan-approval-app
  name: Loan Approval App
  environment: demo

agents:
  - id: loan-agent
    name: Loan Agent
  - id: compliance-agent
    name: Compliance Agent

tools:
  - id: approve_loan
    name: Approve Loan
    risk_level: high
    side_effect: true
    arguments:
      approved_amount:
        type: number
        aliases: [approved amount]
      approval_mode:
        type: string
        allowed_values: [auto, manual]
  - id: send_to_human_review
    name: Send to Human Review
    risk_level: medium
    side_effect: true
    arguments:
      reason:
        type: string

Dynamic inventory from API (inventory.provider: http)

inventory:
  provider: http
  url: https://policy-service.example.com/v1/inventory

Expected API response shape (JSON or YAML body):

{
  "agents": [{"id": "loan-agent"}],
  "tools": [
    {
      "id": "approve_loan",
      "arguments": {
        "approved_amount": {"type": "number"}
      }
    }
  ]
}

Policies: File and API Examples

Static policy directory (policies.provider: directory)

policies:
  provider: directory
  path: ./policies

Single policy file (policies.provider: file)

policies:
  provider: file
  path: ./policies/auto_approval.yaml

Dynamic policies from API (policies.provider: http)

policies:
  provider: http
  url: https://policy-service.example.com/v1/policies
  support_english: true

Expected API response can be one policy object or an array of policy objects (same schema as local policy YAML files).

To refresh policies after an API-side update:

from openagentpolicy import get_runtime

get_runtime().reload()

English Policy Compiler Modes (Rule-Based, AI, Hybrid)

English policies can be compiled using three strategies:

  • hybrid (default; recommended for free-form authoring): AI first, then deterministic fallback. Use this when authors write policies in natural language. If the AI translator is unavailable (no API key/SDK/response), it degrades gracefully to the rule-based result — no network dependency is required for startup.
  • rule_based (locked template grammar): deterministic matching of a small, fixed set of sentence shapes against inventory. It does not attempt to parse arbitrary English — anything outside the documented grammar is reported as not_enforceable/needs_review rather than guessed. Choose this when you want a fixed, auditable grammar with no model calls.
  • ai: LLM conversion to structured policy, still schema/inventory validated and corroborated by the rule-based compiler before activation.

The rule-based grammar is intentionally small and is not meant to grow to cover natural English. If you find yourself wanting another sentence pattern, prefer hybrid rather than expanding the regex set. The exact supported grammar is documented below under "Rule-based grammar (locked templates)".

Config

policies:
  provider: directory
  path: ./policies
  support_english: true
  english_compiler: hybrid        # default; one of: hybrid | rule_based | ai
  ai:
    model: gpt-4.1-mini
    api_key_env: OPENAI_API_KEY
    base_url: null                # optional custom endpoint

Note: there is no confidence threshold. A model's self-reported confidence is not a calibrated probability, so it is not used as a safety gate. AI output is auto-compiled only when the deterministic rule-based compiler independently produces an equivalent structured policy (see Safety model below).

Mode behavior

  • rule_based

    • Uses deterministic parsing from openagentpolicy/policies/compiler.py
    • Best for controlled policy templates and strict reproducibility
    • Supports only the fixed grammar documented below; out-of-grammar text is not guessed
  • ai

    • Uses an LLM translator to generate structured policy JSON
    • Validates generated policy with schema + inventory alignment checks
    • Corroborates against the deterministic rule-based compiler
    • Returns:
      • compiled when valid, inventory-aligned, AND the deterministic compiler independently produces an equivalent structured policy
      • needs_review when output is uncorroborated (the deterministic compiler disagrees or cannot independently compile the rule), or shape is invalid
      • not_enforceable when policy references unknown tools/fields
  • hybrid

    • Tries AI first
    • If AI does not produce compiled, runs the rule-based compiler
    • Uses deterministic output when the fallback succeeds
    • Surfaces a genuine AI candidate (schema-valid, inventory-aligned, but uncorroborated) as needs_review; if there is no AI signal at all (e.g. the translator is unavailable), it defers to the deterministic result instead of masking it with a generic review status

Runtime requirements for AI mode

  • Install OpenAI SDK in your environment (pip install openai)
  • Set API key in env variable configured by policies.ai.api_key_env (default OPENAI_API_KEY)
  • Keep support_english: true

The translator constrains the model with the OpenAI Responses structured-output (json_schema) format so it must emit the exact Policy shape — valid operator symbols, leaf/group conditions — instead of free-form JSON. If the SDK/endpoint rejects that argument, it falls back to an unconstrained call (the prompt also describes the schema). The compiled policy's id/name are always pinned to the source document, not the model's output, so enforced policies stay traceable.

If AI is unavailable (no key/dependency/response), AI mode returns needs_review; hybrid mode will attempt deterministic fallback automatically.

Safety model

Even in AI mode, generated policy is never enforced blindly:

  1. Parse model output as JSON
  2. Validate against Policy schema
  3. Validate tool_id and condition field paths against inventory
  4. Require deterministic corroboration: the rule-based compiler must independently produce a semantically equivalent structured policy
  5. Enforce only compiled policies

The trust signal is agreement between an LLM and an independent deterministic compiler — not a self-reported confidence score. Uncorroborated AI output is surfaced as needs_review for a human to approve, never auto-activated. This keeps natural-language flexibility while preserving enforcement safety.

Rule-based grammar (locked templates)

The rule-based compiler recognizes only the following sentence shapes. A leading if is optional for the comparison templates. Terms in <...> are resolved against inventory (argument names, aliases, descriptions, and allowed_values); unresolved terms make the policy not_enforceable.

English template Compiles to
<field> is greater than|more than|above <number> field > number
<field> is less than|below <number> field < number
<field> is equal to <number> field == number
if <field> is missing field not_exists
value phrase from allowed_values (e.g. auto approval) field == "<value>"
only <agent> may call|use <tool> agent_id != <agent>

Action is taken from the policy's consequent clause only:

  • block intent — not allowed, not permitted, may not, must not, should not, cannot, can'taction: block
  • warnaction: warn
  • otherwise supply an explicit hints.action; without one the action is undetermined and the policy is not_enforceable (it will not silently block).

Anything not matching the above is out of grammar. Do not extend this set to chase natural English — switch the policy (or the runtime) to hybrid instead. The authoritative definition lives in the module docstring of openagentpolicy/policies/compiler.py.

Policy Ladder: Simple to Complex

1) Simple tool threshold (structured)

id: block_large_auto
enabled: true
policy_type: structured
trigger:
  event: before_tool_call
  tool_id: approve_loan
conditions:
  all:
    - field: tool_args.approved_amount
      operator: ">"
      value: 5000
    - field: tool_args.approval_mode
      operator: "=="
      value: auto
action:
  type: block
  message: Auto approval is not allowed above 5000.

2) Same rule in English (compiled at load-time)

id: block_large_auto_english
enabled: true
policy_type: english
english: >
  If approved amount is greater than 5000 and approval mode is auto,
  auto approval is not allowed.
hints:
  action:
    type: block
    message: Auto approval is not allowed above 5000.

3) Agent + tool guardrail

id: agent_allowlist_for_approve
enabled: true
policy_type: structured
trigger:
  event: before_tool_call
  tool_id: approve_loan
conditions:
  field: agent_id
  operator: "!="
  value: loan-agent
action:
  type: block
  message: Only loan-agent may approve loans.

4) Workflow policy using session metadata

id: require_human_review_for_large_manual
enabled: true
policy_type: structured
trigger:
  event: before_tool_call
  tool_id: approve_loan
conditions:
  all:
    - field: tool_args.approved_amount
      operator: ">"
      value: 5000
    - field: metadata.human_reviewed
      operator: "!="
      value: true
action:
  type: block
  message: Human review required before large approval.

5) Final response safety policy

id: block_guaranteed_claims
enabled: true
policy_type: structured
trigger:
  event: before_final_response
conditions:
  field: final_response
  operator: contains
  value: guaranteed approval
action:
  type: block
  message: Do not promise guaranteed approval.

English Compilation API

Use the UI/backend-facing compile API:

from openagentpolicy.compiler import compile_english_policy

result = compile_english_policy(
    english="If approved amount is greater than 5000, auto approval is not allowed.",
    inventory=inventory,
    policy_id="policy_auto_approval_threshold",
    action_hint={"type": "block", "message": "Auto approval is not allowed above 5000."},
)

if result.status.value == "compiled":
    compiled_policy = result.compiled_policy
    # persist compiled policy
else:
    # show result.errors / result.missing_terms / result.suggested_fixes
    pass

Compiled policy shape example:

id: policy_auto_approval_threshold
policy_type: compiled
trigger:
  event: before_tool_call
  tool_id: approve_loan
conditions:
  all:
    - field: tool_args.approved_amount
      operator: ">"
      value: 5000
    - field: tool_args.approval_mode
      operator: "=="
      value: auto
action:
  type: block
source:
  english: If approved amount is greater than 5000, auto approval is not allowed.
  compiler_version: "1.0"
validation:
  enforceability: enforceable

Not-enforceable example:

English: If KYC is not verified, do not approve the loan.

If kyc_status is not in inventory, compile returns not_enforceable with missing terms and actionable fixes.

CLI

All commands use the openagentpolicy entrypoint (stdlib argparse, no extra CLI dependency).

1) Validate config

openagentpolicy validate-config openagentpolicy.yaml

2) Validate inventory

openagentpolicy validate-inventory inventory.yaml

3) Validate policy

openagentpolicy validate-policy policies/auto_approval_structured.yaml --inventory inventory.yaml

4) Compile policy

openagentpolicy compile-policy policies/english.yaml --inventory inventory.yaml
openagentpolicy compile-policy policies/ --inventory inventory.yaml

Compile one English policy string to deterministic compiled policy:

openagentpolicy compile-english \
  --inventory inventory.yaml \
  --policy-id policy_auto_approval_threshold \
  --english "If approved amount is greater than 5000, auto approval is not allowed." \
  --output compiled_policy.yaml

Explain one policy against inventory:

openagentpolicy explain-policy \
  --inventory inventory.yaml \
  --policy policies/policy.yaml

5) Test policy against an event

Exit code 0 means allow. Exit code 1 means block or restrictive decision.

openagentpolicy test-policy policies/auto_approval_structured.yaml \
  --event events/high_value_loan.json \
  --inventory inventory.yaml

Event file format:

{
  "event_type": "before_tool_call",
  "tool_id": "approve_loan",
  "tool_args": {
    "approved_amount": 50000,
    "approval_mode": "auto"
  },
  "agent_id": "loan-agent",
  "metadata": {
    "human_reviewed": false
  }
}

6) Generate inventory from traces

openagentpolicy generate-inventory \
  --traces ./openagentpolicy_traces/events.jsonl \
  --output generated_inventory.yaml

Imperative engine check

Run a single tool-call decision against the configured runtime, without wrapping any code:

openagentpolicy check --config openagentpolicy.yaml --tool approve_loan \
  --args '{"approved_amount": 5000, "approval_mode": "auto"}'

Development

pip install -e ".[dev]"
pytest

License

Apache-2.0

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages