Skip to content

Commit 55c949a

Browse files
committed
removed docs. Added non-blocking behavior
1 parent 0434264 commit 55c949a

File tree

11 files changed

+419
-273
lines changed

11 files changed

+419
-273
lines changed

docs/guardrails.md

Lines changed: 1 addition & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -151,84 +151,4 @@ async def main():
151151
1. This is the actual agent's output type.
152152
2. This is the guardrail's output type.
153153
3. This is the guardrail function that receives the agent's output, and returns the result.
154-
4. This is the actual agent that defines the workflow.
155-
156-
## Tool guardrails
157-
158-
Tool guardrails provide fine-grained control over individual tool calls, allowing you to validate inputs and outputs at the tool level. This is particularly useful for:
159-
160-
- Blocking sensitive data in tool arguments
161-
- Preventing unauthorized access to certain tools
162-
- Sanitizing tool outputs before they're returned
163-
- Implementing custom validation logic for specific tools
164-
165-
There are two types of tool guardrails:
166-
167-
1. **Tool input guardrails** run before a tool is executed, validating the tool call arguments
168-
2. **Tool output guardrails** run after a tool is executed, validating the tool's output
169-
170-
### Tool input guardrails
171-
172-
Tool input guardrails run in 3 steps:
173-
174-
1. First, the guardrail receives the tool call data including arguments, context, and agent information
175-
2. Next, the guardrail function runs to produce a [`ToolGuardrailFunctionOutput`][agents.tool_guardrails.ToolGuardrailFunctionOutput]
176-
3. Finally, we check if [`.tripwire_triggered`][agents.tool_guardrails.ToolGuardrailFunctionOutput.tripwire_triggered] is true. If true, a [`ToolInputGuardrailTripwireTriggered`][agents.exceptions.ToolInputGuardrailTripwireTriggered] exception is raised
177-
178-
### Tool output guardrails
179-
180-
Tool output guardrails run in 3 steps:
181-
182-
1. First, the guardrail receives the tool call data plus the tool's output
183-
2. Next, the guardrail function runs to produce a [`ToolGuardrailFunctionOutput`][agents.tool_guardrails.ToolGuardrailFunctionOutput]
184-
3. Finally, we check if [`.tripwire_triggered`][agents.tool_guardrails.ToolGuardrailFunctionOutput.tripwire_triggered] is true. If true, a [`ToolOutputGuardrailTripwireTriggered`][agents.exceptions.ToolOutputGuardrailTripwireTriggered] exception is raised
185-
186-
### Implementing tool guardrails
187-
188-
You can create tool guardrails using the `@tool_input_guardrail` and `@tool_output_guardrail` decorators:
189-
190-
```python
191-
from agents import (
192-
ToolGuardrailFunctionOutput,
193-
ToolInputGuardrailData,
194-
ToolOutputGuardrailData,
195-
tool_input_guardrail,
196-
tool_output_guardrail,
197-
)
198-
199-
@tool_input_guardrail
200-
def block_sensitive_words(data: ToolInputGuardrailData) -> ToolGuardrailFunctionOutput:
201-
"""Block tool calls that contain sensitive words in arguments."""
202-
# Check arguments for sensitive content
203-
if "password" in data.tool_call.arguments.lower():
204-
return ToolGuardrailFunctionOutput(
205-
tripwire_triggered=True,
206-
model_message="🚨 Tool call blocked: contains sensitive word",
207-
output_info={"blocked_word": "password"},
208-
)
209-
return ToolGuardrailFunctionOutput(tripwire_triggered=False, output_info="Input validated")
210-
211-
@tool_output_guardrail
212-
def block_sensitive_output(data: ToolOutputGuardrailData) -> ToolGuardrailFunctionOutput:
213-
"""Block tool outputs that contain sensitive data."""
214-
if "ssn" in str(data.output).lower():
215-
return ToolGuardrailFunctionOutput(
216-
tripwire_triggered=True,
217-
model_message="🚨 Tool output blocked: contains sensitive data",
218-
output_info={"blocked_pattern": "SSN"},
219-
)
220-
return ToolGuardrailFunctionOutput(tripwire_triggered=False, output_info="Output validated")
221-
222-
# Apply guardrails to tools
223-
my_tool.tool_input_guardrails = [block_sensitive_words]
224-
my_tool.tool_output_guardrails = [block_sensitive_output]
225-
```
226-
227-
For a complete working example, see [tool_guardrails.py](https://github.com/openai/openai-agents-python/blob/main/examples/basic/tool_guardrails.py).
228-
229-
### Key differences from agent guardrails
230-
231-
- **Scope**: Tool guardrails operate on individual tool calls, while agent guardrails operate on the entire agent input/output
232-
- **Timing**: Tool guardrails run immediately before/after tool execution, while agent guardrails run at the beginning/end of agent execution
233-
- **Data access**: Tool guardrails have access to the specific tool call arguments and outputs, plus the tool context
234-
- **Application**: Tool guardrails are applied directly to function tools via the `tool_input_guardrails` and `tool_output_guardrails` attributes
154+
4. This is the actual agent that defines the workflow.

docs/ref/tool_guardrails.md

Lines changed: 0 additions & 3 deletions
This file was deleted.

examples/basic/tool_guardrails.py

Lines changed: 69 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
Runner,
77
ToolGuardrailFunctionOutput,
88
ToolInputGuardrailData,
9-
ToolInputGuardrailTripwireTriggered,
109
ToolOutputGuardrailData,
1110
ToolOutputGuardrailTripwireTriggered,
1211
function_tool,
@@ -33,36 +32,44 @@ def get_user_data(user_id: str) -> dict[str, str]:
3332
"phone": "555-1234",
3433
}
3534

35+
@function_tool
36+
def get_contact_info(user_id: str) -> dict[str, str]:
37+
"""Get contact info by ID."""
38+
return {
39+
"user_id": user_id,
40+
"name": "Jane Smith",
41+
"email": "[email protected]",
42+
"phone": "555-1234",
43+
}
44+
3645

3746
@tool_input_guardrail
38-
def block_sensitive_words(data: ToolInputGuardrailData) -> ToolGuardrailFunctionOutput:
39-
"""Block tool calls that contain sensitive words in arguments."""
47+
def reject_sensitive_words(data: ToolInputGuardrailData) -> ToolGuardrailFunctionOutput:
48+
"""Reject tool calls that contain sensitive words in arguments."""
4049
try:
41-
args = json.loads(data.tool_call.arguments)
50+
args = json.loads(data.context.tool_arguments) if data.context.tool_arguments else {}
4251
except json.JSONDecodeError:
43-
return ToolGuardrailFunctionOutput(
44-
tripwire_triggered=False, output_info="Invalid JSON arguments"
45-
)
52+
return ToolGuardrailFunctionOutput(output_info="Invalid JSON arguments")
4653

4754
# Check for suspicious content
4855
sensitive_words = [
4956
"password",
5057
"hack",
5158
"exploit",
5259
"malware",
53-
"orange",
54-
] # to mock sensitive words
60+
"ACME",
61+
]
5562
for key, value in args.items():
5663
value_str = str(value).lower()
5764
for word in sensitive_words:
58-
if word in value_str:
59-
return ToolGuardrailFunctionOutput(
60-
tripwire_triggered=True,
61-
model_message=f"🚨 Tool call blocked: contains '{word}'",
65+
if word.lower() in value_str:
66+
# Reject tool call and inform the model the function was not called
67+
return ToolGuardrailFunctionOutput.reject_content(
68+
message=f"🚨 Tool call blocked: contains '{word}'",
6269
output_info={"blocked_word": word, "argument": key},
6370
)
6471

65-
return ToolGuardrailFunctionOutput(tripwire_triggered=False, output_info="Input validated")
72+
return ToolGuardrailFunctionOutput(output_info="Input validated")
6673

6774

6875
@tool_output_guardrail
@@ -72,57 +79,71 @@ def block_sensitive_output(data: ToolOutputGuardrailData) -> ToolGuardrailFuncti
7279

7380
# Check for sensitive data patterns
7481
if "ssn" in output_str or "123-45-6789" in output_str:
75-
return ToolGuardrailFunctionOutput(
76-
tripwire_triggered=True,
77-
model_message="🚨 Tool output blocked: contains sensitive data",
78-
output_info={"blocked_pattern": "SSN", "tool": data.tool_call.name},
82+
# Use raise_exception to halt execution completely for sensitive data
83+
return ToolGuardrailFunctionOutput.raise_exception(
84+
output_info={"blocked_pattern": "SSN", "tool": data.context.tool_name},
7985
)
8086

81-
return ToolGuardrailFunctionOutput(tripwire_triggered=False, output_info="Output validated")
87+
return ToolGuardrailFunctionOutput(output_info="Output validated")
88+
89+
90+
@tool_output_guardrail
91+
def reject_phone_numbers(data: ToolOutputGuardrailData) -> ToolGuardrailFunctionOutput:
92+
"""Reject function output containing phone numbers."""
93+
output_str = str(data.output)
94+
if "555-1234" in output_str:
95+
return ToolGuardrailFunctionOutput.reject_content(
96+
message="User data not retrieved as it contains a phone number which is restricted.",
97+
output_info={"redacted": "phone_number"},
98+
)
99+
return ToolGuardrailFunctionOutput()
82100

83101

84102
# Apply guardrails to tools
85-
send_email.tool_input_guardrails = [block_sensitive_words]
103+
send_email.tool_input_guardrails = [reject_sensitive_words]
86104
get_user_data.tool_output_guardrails = [block_sensitive_output]
105+
get_contact_info.tool_output_guardrails = [reject_phone_numbers]
87106

88107
agent = Agent(
89108
name="Secure Assistant",
90109
instructions="You are a helpful assistant with access to email and user data tools.",
91-
tools=[send_email, get_user_data],
110+
tools=[send_email, get_user_data, get_contact_info],
92111
)
93112

94113

95114
async def main():
96115
print("=== Tool Guardrails Example ===\n")
97116

98-
# Example 1: Normal operation - should work fine
99-
print("1. Normal email sending:")
100117
try:
118+
# Example 1: Normal operation - should work fine
119+
print("1. Normal email sending:")
101120
result = await Runner.run(agent, "Send a welcome email to [email protected]")
102-
print(f"✅ Success: {result.final_output}\n")
103-
except Exception as e:
104-
print(f"❌ Error: {e}\n")
121+
print(f"✅ Successful tool execution: {result.final_output}\n")
105122

106-
# Example 2: Input guardrail triggers - should block suspicious content
107-
print("2. Attempting to send email with suspicious content:")
108-
try:
109-
result = await Runner.run(
110-
agent, "Send an email to [email protected] with the subject: orange"
111-
)
112-
print(f"✅ Success: {result.final_output}\n")
113-
except ToolInputGuardrailTripwireTriggered as e:
114-
print(f"🚨 Input guardrail triggered: {e.output.model_message}")
115-
print(f" Details: {e.output.output_info}\n")
123+
# Example 2: Input guardrail triggers - function tool call is rejected but execution continues
124+
print("2. Attempting to send email with suspicious content:")
125+
result = await Runner.run(agent, "Send an email to [email protected] introducing the company ACME corp.")
126+
print(f"❌ Guardrail rejected function tool call: {result.final_output}\n")
127+
except Exception as e:
128+
print(f"Error: {e}\n")
116129

117-
# Example 3: Output guardrail triggers - should block sensitive data
118-
print("3. Attempting to get user data (contains SSN):")
119130
try:
131+
# Example 3: Output guardrail triggers - should raise exception for sensitive data
132+
print("3. Attempting to get user data (contains SSN). Execution blocked:")
120133
result = await Runner.run(agent, "Get the data for user ID user123")
121-
print(f"✅ Success: {result.final_output}\n")
134+
print(f"✅ Successful tool execution: {result.final_output}\n")
122135
except ToolOutputGuardrailTripwireTriggered as e:
123-
print(f"🚨 Output guardrail triggered: {e.output.model_message}")
124-
print(f" Details: {e.output.output_info}\n")
136+
print("🚨 Output guardrail triggered: Execution halted for sensitive data")
137+
print(f"Details: {e.output.output_info}\n")
125138

139+
140+
try:
141+
# Example 4: Output guardrail triggers - reject returning function tool output but continue execution
142+
print("4. Rejecting function tool output containing phone numbers:")
143+
result = await Runner.run(agent, "Get contact info for user456")
144+
print(f"❌ Guardrail rejected function tool output: {result.final_output}\n")
145+
except Exception as e:
146+
print(f"Error: {e}\n")
126147

127148
if __name__ == "__main__":
128149
asyncio.run(main())
@@ -133,13 +154,15 @@ async def main():
133154
=== Tool Guardrails Example ===
134155
135156
1. Normal email sending:
136-
Success: I've sent a welcome email to [email protected] with an appropriate subject and greeting message.
157+
Successful tool execution: I've sent a welcome email to [email protected] with an appropriate subject and greeting message.
137158
138159
2. Attempting to send email with suspicious content:
139-
🚨 Input guardrail triggered: 🚨 Tool call blocked: contains 'orange'
140-
Details: {'blocked_word': 'orange', 'argument': 'subject'}
160+
❌ Guardrail rejected function tool call: I'm unable to send the email mentioning ACME Corp as it was blocked by security guardrails.
141161
142-
3. Attempting to get user data (contains SSN):
143-
🚨 Output guardrail triggered: 🚨 Tool output blocked: contains sensitive data
162+
3. Attempting to get user data (contains SSN). Execution blocked:
163+
🚨 Output guardrail triggered: Execution halted for sensitive data
144164
Details: {'blocked_pattern': 'SSN', 'tool': 'get_user_data'}
165+
166+
4. Rejecting function tool output containing sensitive data:
167+
✅ Successful tool execution: User data retrieved (phone number redacted for privacy)
145168
"""

examples/basic/tools.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ def get_weather(city: Annotated[str, "The city to get the weather for"]) -> Weat
1818
print("[debug] get_weather called")
1919
return Weather(city=city, temperature_range="14-20C", conditions="Sunny with wind.")
2020

21-
2221
agent = Agent(
2322
name="Hello world",
2423
instructions="You are a helpful agent.",

mkdocs.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,6 @@ plugins:
101101
- ref/usage.md
102102
- ref/exceptions.md
103103
- ref/guardrail.md
104-
- ref/tool_guardrails.md
105104
- ref/model_settings.md
106105
- ref/agent_output.md
107106
- ref/function_schema.md

src/agents/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,8 +89,10 @@
8989
ToolGuardrailFunctionOutput,
9090
ToolInputGuardrail,
9191
ToolInputGuardrailData,
92+
ToolInputGuardrailResult,
9293
ToolOutputGuardrail,
9394
ToolOutputGuardrailData,
95+
ToolOutputGuardrailResult,
9496
tool_input_guardrail,
9597
tool_output_guardrail,
9698
)
@@ -221,7 +223,9 @@ def enable_verbose_stdout_logging():
221223
"ToolOutputGuardrail",
222224
"ToolGuardrailFunctionOutput",
223225
"ToolInputGuardrailData",
226+
"ToolInputGuardrailResult",
224227
"ToolOutputGuardrailData",
228+
"ToolOutputGuardrailResult",
225229
"tool_input_guardrail",
226230
"tool_output_guardrail",
227231
"handoff",

0 commit comments

Comments
 (0)