Skip to content

Guide: Retrofit Governance onto an Existing Agent

Package: agent-os-kernel ยท Time: 10 minutes ยท Prerequisites: Python 3.10+, existing agent


What You'll Learn

  • Add policy enforcement to any existing agent in 3 steps โ€” install, wrap, and configure
  • Blocked-pattern detection and audit trail without rewriting existing logic

Got an AI agent already running in production? This tutorial shows you how to add policy enforcement, blocked-pattern detection, and an audit trail in three steps โ€” without rewriting your existing logic.

What you'll learn:

Step What happens
1. Install Add the toolkit with one command
2. Wrap Two lines of code around your existing agent
3. Configure Drop in a single YAML file to define your rules
Verify Run the companion script and see governance in action

The Agent We're Starting With

Here's a typical "before" agent โ€” useful, but completely ungoverned. It happily executes whatever it's asked:

# before.py  โ† your existing code, untouched
def run_agent(user_input: str) -> str:
    """A simple agent with no guardrails."""
    # ... your existing LLM calls, tool calls, etc.
    return f"Agent result for: {user_input}"

# No checks, no audit trail, no blocked patterns
result = run_agent("DROP TABLE users;")
print(result)  # ๐Ÿ˜ฌ executes without question

By the end of this tutorial, the same run_agent call will be intercepted, evaluated against policy, and either allowed or blocked โ€” with every decision written to an audit trail.


Step 1: Install

pip install agent-governance-toolkit[full]

That's it. No infrastructure changes, no config files yet.

Note: [full] pulls in YAML policy support, framework integrations, and audit tooling. For a minimal install you can use pip install agent-os-kernel instead, but YAML policy loading requires the [nexus] or [full] extra.


Step 2: Wrap Your Agent

Add two imports and wrap your existing function with a governance kernel. Your original run_agent logic stays completely unchanged:

# after.py  โ† the only new code you write
from agent_os.integrations import LangChainKernel          # swap for your framework
from agent_os.integrations.base import GovernancePolicy

# --- NEW: create a kernel (one-time setup) ---
policy = GovernancePolicy(
    name="my-agent-policy",
    blocked_patterns=["DROP TABLE", "rm -rf", "os.system"],
    max_tool_calls=10,
)
kernel = LangChainKernel(policy=policy)
ctx = kernel.create_context("my-agent")

# --- your original function, unchanged ---
def run_agent(user_input: str) -> str:
    """A simple agent with no guardrails."""
    return f"Agent result for: {user_input}"

# --- NEW: check before every call ---
def governed_run(user_input: str) -> str:
    allowed, reason = kernel.pre_execute(ctx, user_input)
    if not allowed:
        print(f"๐Ÿšซ BLOCKED โ€” {reason}")
        return ""
    result = run_agent(user_input)
    print(f"โœ… ALLOWED โ€” {result}")
    return result

The key additions are:

  1. GovernancePolicy โ€” declares what's blocked and what limits apply.
  2. kernel.pre_execute(ctx, input) โ€” evaluates the input against that policy before your code runs. Returns (allowed: bool, reason: str).

Don't use LangChain? Swap LangChainKernel for OpenAIAgentsKernel, CrewAIKernel, or AutoGenKernel โ€” the pre_execute API is identical across all integrations. See Tutorial 03 โ€” Framework Integrations.


Step 3: Configure Policy with YAML

Inline Python policy is fine for quick tests, but for real projects you want policy in a file you can review, version-control, and update without touching code.

Create policies/my-agent.yaml:

# policies/my-agent.yaml
version: "1.0"
name: my-agent-policy
description: >
  Governance policy for my existing agent.
  Blocks destructive commands and PII leakage.

rules:
  - name: block-destructive-sql
    condition:
      field: input
      operator: contains_any
      value: ["DROP TABLE", "TRUNCATE", "DELETE FROM"]
    action: block
    priority: 100
    message: "Destructive SQL is not permitted."

  - name: block-shell-injection
    condition:
      field: input
      operator: regex
      value: '(rm\s+-rf|os\.system|subprocess\.call)'
    action: block
    priority: 90
    message: "Shell injection patterns are blocked."

  - name: flag-pii
    condition:
      field: input
      operator: regex
      value: '\b\d{3}-\d{2}-\d{4}\b'   # SSN pattern
    action: audit                         # log it, but don't block
    priority: 50
    message: "Possible SSN detected โ€” logged for review."

defaults:
  action: allow
  max_tool_calls: 10

Then load it with the policy evaluator instead of the inline GovernancePolicy:

from agent_os.policies import PolicyEvaluator

evaluator = PolicyEvaluator()
evaluator.load_policies("./policies/")          # loads all .yaml files in the dir

def governed_run(user_input: str) -> str:
    decision = evaluator.evaluate({"input": user_input})
    if not decision.allowed:
        print(f"๐Ÿšซ BLOCKED [{decision.action}] โ€” {decision.reason}")
        return ""
    result = run_agent(user_input)
    print(f"โœ… ALLOWED โ€” {result}")
    return result

Now your entire policy lives in YAML: easy to audit, diff in pull requests, and hand off to a security team.


Verify It Works

Run the standalone companion script included with this tutorial:

python examples/quickstart/retrofit_governed.py

Expected output:

============================================================
  Retrofit Governance โ€” Verification Demo
============================================================

[1] Dangerous SQL input โ€ฆ
    ๐Ÿšซ BLOCKED โ€” policy=my-agent-policy  reason=Blocked pattern matched: 'DROP TABLE'

[2] Shell injection attempt โ€ฆ
    ๐Ÿšซ BLOCKED โ€” policy=my-agent-policy  reason=Blocked pattern matched: 'rm -rf'

[3] Safe input โ€ฆ
    โœ… ALLOWED โ€” Agent result for: Summarise last week's sales report

โ”€โ”€ Audit Trail โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
  [1] 2025-...  input='DROP TABLE users;'     status=BLOCKED
  [2] 2025-...  input='rm -rf /data'          status=BLOCKED
  [3] 2025-...  input='Summarise last weekโ€ฆ'  status=ALLOWED

๐ŸŽ‰ Governance is working. 3 decisions logged.

If you see this output, your agent is now governed. Every decision โ€” allow or block โ€” is written to the audit trail automatically.


What Changed? (Before vs. After)

Before After
Lines of new code โ€” ~10
Original agent logic unchanged unchanged
Policy location none policies/my-agent.yaml
Blocked patterns none DROP TABLE, rm -rf, shell injection
Audit trail none every decision logged with timestamp
Deployment change โ€” none โ€” pure Python, no infra required

Next Steps