Implementation:Openai Openai agents python Guardrail Attachment Pattern
Overview
The guardrail attachment pattern describes how to wire guardrail instances to the tools and agents they protect. This is a configuration pattern -- guardrails are specified as list fields on FunctionTool and Agent at construction time.
Tool-Level Attachment
Via the @function_tool Decorator
The most concise way to attach guardrails to a tool is through the @function_tool decorator:
from agents import (
function_tool,
tool_input_guardrail,
tool_output_guardrail,
ToolGuardrailFunctionOutput,
)
@tool_input_guardrail
def input_guard(data):
"""Validate tool input."""
if "dangerous" in str(data.context.tool_input):
return ToolGuardrailFunctionOutput.reject_content("Dangerous input rejected")
return ToolGuardrailFunctionOutput.allow()
@tool_output_guardrail
def output_guard(data):
"""Validate tool output."""
if "secret" in str(data.output):
return ToolGuardrailFunctionOutput.reject_content("Secret data filtered")
return ToolGuardrailFunctionOutput.allow()
@function_tool(
tool_input_guardrails=[input_guard],
tool_output_guardrails=[output_guard],
)
def my_func(x: str) -> str:
"""A guarded tool function."""
return process(x)
Via the function_tool Function
When creating tools programmatically rather than with decorators, use the function_tool function:
from agents import function_tool
def my_func(x: str) -> str:
return process(x)
my_tool = function_tool(
my_func,
tool_input_guardrails=[input_guard],
tool_output_guardrails=[output_guard],
)
FunctionTool Fields
The underlying FunctionTool dataclass defines two guardrail list fields:
# From src/agents/tool.py lines 247-251
@dataclass
class FunctionTool:
# ... other fields ...
tool_input_guardrails: list[ToolInputGuardrail[Any]]
tool_output_guardrails: list[ToolOutputGuardrail[Any]]
Source: src/agents/tool.py lines 247-251
Agent-Level Attachment
Direct Construction
Agent-level guardrails are attached via the input_guardrails and output_guardrails fields on the Agent class:
from agents import (
Agent,
InputGuardrail,
OutputGuardrail,
GuardrailFunctionOutput,
)
def check_input(ctx, agent, input):
if "blocked" in str(input).lower():
return GuardrailFunctionOutput(
output_info="Blocked content",
tripwire_triggered=True,
)
return GuardrailFunctionOutput(output_info="OK")
def check_output(ctx, agent, output):
if len(str(output)) > 10000:
return GuardrailFunctionOutput(
output_info="Too long",
tripwire_triggered=True,
)
return GuardrailFunctionOutput(output_info="OK")
agent = Agent(
name="safe_agent",
instructions="Be helpful and safe.",
input_guardrails=[
InputGuardrail(guardrail_function=check_input),
],
output_guardrails=[
OutputGuardrail(guardrail_function=check_output),
],
)
Using Decorator-Created Guardrails
Guardrails created with the @input_guardrail and @output_guardrail decorators can be passed directly into the agent's lists:
from agents import Agent, input_guardrail, output_guardrail, GuardrailFunctionOutput
@input_guardrail
def topic_filter(ctx, agent, input):
if "off_topic" in str(input).lower():
return GuardrailFunctionOutput(
output_info="Off topic",
tripwire_triggered=True,
)
return GuardrailFunctionOutput(output_info="OK")
@output_guardrail
def quality_check(ctx, agent, output):
if len(str(output)) < 10:
return GuardrailFunctionOutput(
output_info="Response too short",
tripwire_triggered=True,
)
return GuardrailFunctionOutput(output_info="OK")
agent = Agent(
name="quality_agent",
instructions="Provide thorough responses.",
input_guardrails=[topic_filter],
output_guardrails=[quality_check],
)
Agent Guardrail Fields
The underlying Agent dataclass defines two guardrail list fields:
# From src/agents/agent.py lines 269-277
@dataclass
class Agent:
# ... other fields ...
input_guardrails: list[InputGuardrail[TContext]]
output_guardrails: list[OutputGuardrail[TContext]]
Source: src/agents/agent.py lines 269-277
Stacking Multiple Guardrails
Both tool-level and agent-level guardrail fields accept lists, enabling multiple guardrails to be stacked:
from agents import (
function_tool,
tool_input_guardrail,
tool_output_guardrail,
ToolGuardrailFunctionOutput,
)
@tool_input_guardrail
def no_sql_injection(data):
input_str = str(data.context.tool_input)
if "DROP TABLE" in input_str.upper():
return ToolGuardrailFunctionOutput.reject_content("SQL injection detected")
return ToolGuardrailFunctionOutput.allow()
@tool_input_guardrail
def validate_length(data):
input_str = str(data.context.tool_input)
if len(input_str) > 10000:
return ToolGuardrailFunctionOutput.reject_content("Input too long")
return ToolGuardrailFunctionOutput.allow()
@tool_output_guardrail
def no_pii(data):
import re
if re.search(r'\b\d{3}-\d{2}-\d{4}\b', str(data.output)):
return ToolGuardrailFunctionOutput.reject_content("PII detected")
return ToolGuardrailFunctionOutput.allow()
@function_tool(
tool_input_guardrails=[no_sql_injection, validate_length],
tool_output_guardrails=[no_pii],
)
def query_database(sql: str) -> str:
"""Execute a database query with multiple guardrails."""
return execute_query(sql)
All guardrails in the list are evaluated. If any one rejects or triggers a tripwire, the operation is blocked.
See Also
- Guardrail Attachment -- The principle behind guardrail attachment
- Tool Input Guardrail Decorator -- Tool input guardrail implementation details
- Tool Output Guardrail Decorator -- Tool output guardrail implementation details
- InputGuardrail and OutputGuardrail -- Agent-level guardrail implementation details