Skip to main content

Policy & Governance

Waxell Observe provides a policy enforcement layer that checks whether an agent is allowed to execute before it runs. Policies are configured on the control plane and evaluated server-side, giving administrators centralized control over agent behavior without modifying agent code.

Getting Started with Governance

New to policies? Start with Policy Categories & Templates to see what's available, then use the Platform Assistant to create your first policy through chat.

How It Works

  1. Before an agent executes, the client sends a policy check request to the control plane
  2. The control plane evaluates all applicable policies (sorted by priority)
  3. The response contains an action that determines what happens next

Policies are defined across 26 categories -- from cost budgets and content scanning to data access boundaries, cognitive governance, and regulatory compliance. The platform also generates automated recommendations based on your agents' actual behavior.

Policy Actions

Every policy check returns one of these actions:

ActionMeaningClient Behavior
allowExecution is permittedAgent runs normally
warnExecution is permitted with a warningAgent runs; warning recorded in trace
redactSensitive content maskedContent replaced with ##TYPE## placeholders
throttleRate limitedPolicyViolationError raised
blockExecution deniedPolicyViolationError raised
skipExecution skipped silentlyRun not started, no error raised
retryExecution retriedAutomatic retry with backoff

The PolicyCheckResult object provides convenience properties:

result.action    # "allow", "block", "warn", "throttle", "redact", "skip", "retry"
result.reason # Human-readable explanation
result.metadata # Additional data from the policy engine

result.allowed # True if action is "allow" or "warn"
result.blocked # True if action is "block", "throttle", or "skip"

Pre-Execution Checks

Enable policy enforcement with the enforce_policy parameter. Combined with init(), this is the recommended pattern:

import waxell_observe as waxell
from waxell_observe.errors import PolicyViolationError

waxell.init()

@waxell.observe(agent_name="support-bot", enforce_policy=True)
async def handle_query(query: str) -> str:
# Policies checked automatically before this runs
response = await call_llm(query)
return response

try:
result = await handle_query("test")
except PolicyViolationError as e:
print(f"Blocked: {e.policy_result.action}")
print(f"Reason: {e.policy_result.reason}")
print(f"Details: {e.policy_result.metadata}")

With the Context Manager (Advanced)

from waxell_observe import WaxellContext
from waxell_observe.errors import PolicyViolationError

try:
with WaxellContext(
agent_name="support-bot",
enforce_policy=True,
) as ctx:
result = process(query)
ctx.set_result({"output": result})
except PolicyViolationError as e:
print(f"Blocked: {e}")

With LangChain

from waxell_observe.integrations.langchain import WaxellLangChainHandler
from waxell_observe.errors import PolicyViolationError

handler = WaxellLangChainHandler(
agent_name="langchain-bot",
enforce_policy=True,
)

try:
result = chain.invoke(input, config={"callbacks": [handler]})
handler.flush_sync(result={"output": result})
except PolicyViolationError as e:
print(f"Blocked: {e}")

Mid-Execution Policy Checks

For long-running agents, check policies between steps to catch budget exhaustion or policy changes during execution:

import waxell_observe as waxell

waxell.init()

@waxell.observe(
agent_name="pipeline-agent",
enforce_policy=True,
mid_execution_governance=True, # Check on each step
)
async def run_pipeline(query: str) -> str:
# Step 1
data = await retrieve(query)
waxell.step("retrieve", output={"sources": len(data)})

# Step 2 -- mid-execution check happens automatically
summary = await summarize(data)
waxell.step("summarize", output={"length": len(summary)})

return summary

You can also check manually for more control:

with WaxellContext(agent_name="pipeline-agent") as ctx:
await step_one()
ctx.record_step("step_one")

# Manual mid-execution check
policy = await ctx.check_policy()
if policy.blocked:
ctx.set_result({"stopped_at": "step_one", "reason": policy.reason})
return

await step_two()
ctx.record_step("step_two")

Approval Workflows

When a policy blocks execution, you can handle it with human approval instead of failing. Pass on_policy_block to define what happens:

@waxell.observe(
agent_name="data-manager",
workflow_name="delete",
enforce_policy=True,
on_policy_block=waxell.prompt_approval, # terminal Y/N prompt
)
async def delete_records(table: str) -> dict:
return {"deleted": 500, "table": table}

If the human approves, the function executes normally. If denied or timed out, PolicyViolationError propagates to the caller.

Built-in handlers: prompt_approval (terminal), auto_approve (testing), auto_deny (testing). You can also write custom handlers for Slack, webhooks, or any approval channel.

See Approval Workflows for the full guide — custom handlers, ApprovalDecision, and what gets traced.

Disabling Policy Checks

Skip enforcement in development or testing:

# Decorator
@waxell.observe(agent_name="my-agent", enforce_policy=False)
async def my_function():
...

# Or disable all observability
# export WAXELL_OBSERVE=false

Configuring Policies

Policies are configured on the Waxell control plane, not in agent code. This separation means:

  • Administrators define what agents are allowed to do
  • Developers write agents that respect those policies automatically
  • Policy changes take effect immediately without redeploying agents

Three ways to configure policies:

  1. Dashboard -- Governance > Policies in the UI
  2. API -- POST /waxell/v1/policies/ (see API Reference)
  3. Platform Assistant -- Ask the AI to create policies through chat (see Platform Assistant)

See Policy Categories & Templates for the full list of available policy types and pre-built templates.

Next Steps