OpenAI Agents SDK Integration
Enforce Control Zero policies on OpenAI Agents SDK tool calls, handoffs, and guardrails.
Overview
The OpenAI Agents SDK (formerly Swarm) provides a lightweight framework for building multi-agent systems with tool calling, handoffs between agents, and built-in guardrails. Control Zero integrates at the tool execution layer: every tool call is checked against your policies before the function runs. Denied calls raise a PolicyDeniedError that the agent framework surfaces as a tool error.
Installation
pip install controlzero openai-agents
Setup
Tool Wrapper Approach
Wrap your agent tools with Control Zero enforcement:
from controlzero import Client
from agents import Agent, Runner, function_tool
cz = Client(api_key="cz_live_your_api_key_here")
@function_tool
def search_knowledge_base(query: str) -> str:
"""Search the internal knowledge base."""
cz.guard("search_knowledge_base", args={"agent_id": "support-agent"})
return f"KB results for: {query}"
@function_tool
def send_email(to: str, subject: str, body: str) -> str:
"""Send an email to a customer."""
cz.guard("send_email", args={"agent_id": "support-agent", "recipient": to})
return f"Email sent to {to}"
agent = Agent(
name="Support Agent",
instructions="You help customers with their questions.",
tools=[search_knowledge_base, send_email],
)
Guardrail Approach
Use Control Zero as an input or output guardrail within the OpenAI Agents SDK guardrail system:
from controlzero import Client
from agents import Agent, Runner, InputGuardrail, GuardrailFunctionOutput
cz = Client(api_key="cz_live_your_api_key_here")
async def policy_guardrail(ctx, agent, input_data) -> GuardrailFunctionOutput:
"""Check input against Control Zero DLP policies."""
try:
cz.guard(f"agent/{agent.name}", method="input", args={"input_text": str(input_data)})
return GuardrailFunctionOutput(
output_info="Policy check passed",
tripwire_triggered=False,
)
except Exception:
return GuardrailFunctionOutput(
output_info="Input blocked by Control Zero policy",
tripwire_triggered=True,
)
agent = Agent(
name="Analyst",
instructions="Analyze data and provide insights.",
input_guardrails=[
InputGuardrail(guardrail_function=policy_guardrail),
],
)
What Gets Enforced
| OpenAI Agents Event | Policy Action | Policy Resource |
|---|---|---|
| Tool call | tool.call | tool/{tool_name} |
| Agent handoff | agent.handoff | agent/{target_agent_name} |
| Input guardrail | agent.input | agent/{agent_name} |
| Output guardrail | agent.output | agent/{agent_name} |
Full Example: Multi-Agent Support System
from controlzero import Client
import controlzero
from agents import Agent, Runner, function_tool
# --- Control Zero setup ---
cz = Client(api_key="cz_live_your_api_key_here")
def guarded(tool_name: str, agent_id: str = ""):
"""Decorator for policy-enforced tools."""
def decorator(func):
original = func
def wrapper(*args, **kwargs):
cz.guard(tool_name, args={"agent_id": agent_id})
return original(*args, **kwargs)
wrapper.__name__ = original.__name__
wrapper.__doc__ = original.__doc__
return wrapper
return decorator
# --- Triage agent tools ---
@function_tool
def lookup_customer(customer_id: str) -> str:
"""Look up customer information by ID."""
cz.guard("lookup_customer", args={"agent_id": "triage-agent"})
return f"Customer {customer_id}: Premium tier, active since 2024"
@function_tool
def check_order_status(order_id: str) -> str:
"""Check the status of an order."""
cz.guard("check_order_status", args={"agent_id": "triage-agent"})
return f"Order {order_id}: Shipped, arriving tomorrow"
# --- Escalation agent tools ---
@function_tool
def issue_refund(order_id: str, amount: float) -> str:
"""Issue a refund for an order."""
cz.guard("issue_refund", args={"agent_id": "escalation-agent", "amount": str(amount)})
return f"Refund of ${amount} issued for order {order_id}"
@function_tool
def escalate_to_human(reason: str) -> str:
"""Escalate the case to a human support agent."""
cz.guard("escalate_to_human", args={"agent_id": "escalation-agent"})
return f"Escalated to human agent. Reason: {reason}"
# --- Define agents ---
escalation_agent = Agent(
name="Escalation Agent",
instructions="You handle complex cases that require refunds or human escalation.",
tools=[issue_refund, escalate_to_human],
)
triage_agent = Agent(
name="Triage Agent",
instructions=(
"You are the first point of contact. Look up customer info and order status. "
"Hand off to the Escalation Agent for refunds or complex issues."
),
tools=[lookup_customer, check_order_status],
handoffs=[escalation_agent],
)
# --- Run ---
try:
result = Runner.run_sync(
triage_agent,
"I need a refund for order ORD-12345",
)
print(result.final_output)
except controlzero.PolicyDeniedError as e:
print(f"Blocked by policy: {e.decision.reason}")
Example Policy
Allow lookup tools, block refunds without approval, and control agent handoffs:
{
"name": "support-agents-policy",
"rules": [
{
"effect": "allow",
"action": "tool:call",
"resource": "tool/lookup_customer"
},
{
"effect": "allow",
"action": "tool:call",
"resource": "tool/check_order_status"
},
{
"effect": "deny",
"action": "tool:call",
"resource": "tool/issue_refund"
},
{
"effect": "allow",
"action": "tool:call",
"resource": "tool/escalate_to_human"
},
{
"effect": "allow",
"action": "agent:handoff",
"resource": "agent/Escalation Agent"
}
]
}
What happens at runtime:
- Customer lookup and order status: ALLOWED.
- Refund issuance: DENIED. The agent receives a policy error and can escalate to a human instead.
- Escalation to human: ALLOWED.
- Handoff from Triage to Escalation: ALLOWED.
Handoff Enforcement
Control handoffs between agents by enforcing the agent.handoff action:
def enforced_handoff(source_agent: str, target_agent: str):
"""Check policy before allowing agent handoff."""
cz.guard(f"agent/{target_agent}", method="handoff", args={"source_agent": source_agent})
This prevents unauthorized agent-to-agent delegation in multi-agent workflows.
Gateway Alternative
If you do not want to modify your agent code, point the OpenAI API base URL to the Control Zero gateway instead:
from agents import Agent, Runner
import openai
# Route all OpenAI calls through the Control Zero gateway
# Pass base_url to the client constructor (module-level assignment does not work in openai SDK v1.x)
client = openai.OpenAI(
base_url="https://gateway.controlzero.ai/v1",
default_headers={
"X-ControlZero-Agent-ID": "support-agent",
"X-ControlZero-API-Key": "cz_live_xxx",
},
)
Alternatively, set the base URL via environment variable before starting your process:
OPENAI_BASE_URL=https://gateway.controlzero.ai/v1
The gateway enforces policies on all LLM calls and tool call responses at the network layer, with no SDK installation required. See the Gateway Guide for details.
Next Steps
- LangGraph Integration: Graph-based agent governance with conditional routing.
- CrewAI Integration: Multi-agent crew governance.
- Gateway Proxy: Network-level enforcement without code changes.
- Policies: Learn how to construct dashboard policies.