Guarding what goes in
Your support agent will be asked to write essays, tell jokes and do somebody's homework. A guardrail stops that before the model is ever called.
A function that decides
@input_guardrail
async def only_support(context, agent, user_input):
"""Refuse anything that is not about an order."""
off_topic = "homework" in str(user_input).lower()
return GuardrailFunctionOutput(
output_info={"off_topic": off_topic},
tripwire_triggered=off_topic,
)It returns two things. output_info is whatever you want to record, and tripwire_triggered is the decision. True means stop.
agent = Agent(
name="Support",
instructions="Answer questions about orders.",
model=PretendModel(["Your order shipped on 3 March."]),
input_guardrails=[only_support],
)for question in ["Where is my order?", "Do my homework for me"]:
try:
result = await Runner.run(agent, question)
print(f"{question!r:28} -> {result.final_output}")
except InputGuardrailTripwireTriggered:
print(f"{question!r:28} -> blocked before the model ran")The second question never reached the model. The run raised InputGuardrailTripwireTriggered, which you catch and turn into whatever your product should say.
Only on the first agent
This is the part that surprises people, and the docs are explicit: an agent's input guardrails only run if that agent is the first agent of the run.
So in a desk built out of handoffs, only the front agent's input guardrails ever fire. Put one on a billing agent that customers only ever reach by handoff and it will never run, and nothing will tell you.
It follows from what an input guardrail is for. It guards what the user said, and the user only says something once, at the start.
Why before the model and not in the instructions
You can write "only answer questions about orders" in the instructions, and you should. But instructions are a request and a guardrail is a gate. One of them can be talked out of it.
- It costs nothing. The blocked question never became a model call.
- It cannot be argued with. There is no prompt that gets past an
if. - It is testable. A guardrail is a function, so you can call it directly in a test.
- Add a second banned word and try both.
- Return
output_infowith the matched word in it, then print it from the exception. - Make the guardrail let everything through and confirm the second question gets an answer.
Little by little, you're building something great.