Part 3: Input guardrail

The base FAQ agent can answer course questions, but it also tries to help with off-topic prompts. An input guardrail checks the user's message before the FAQ agent runs, so blocked input never reaches the assistant.

In the OpenAI Agents SDK, input guardrails run alongside the main agent call. The guardrail sees the same initial user input, decides whether the run should stop, and triggers a tripwire when the input should be blocked.

Define structured output

Guardrails use LLMs to make pass/fail decisions. We define a Pydantic model so the decision is explicit:

from pydantic import BaseModel

class TopicGuardrailOutput(BaseModel):
    reasoning: str
    fail: bool

reasoning is the short explanation. fail is the tripwire flag.

Create the topic classifier

The topic guardrail agent has one job: decide whether the user's question belongs in a Data Engineering Zoomcamp FAQ assistant.

topic_guardrail_instructions = """
You are a topic guardrail for a data engineering course FAQ assistant.

Your job is to check if the user's question is related to:
- The course (content, schedule, requirements)
- Data engineering topics
- Technical setup and installation
- Homework and assignments
- Certificates and completion

If the question is about these topics, set fail=False.
If it's about something unrelated (like cooking, sports, celebrity gossip, medical advice, etc.), set fail=True.

Keep your reasoning under 15 words.
""".strip()

Create the guardrail agent with structured output:

topic_guardrail_agent = Agent(
    name="topic_guardrail",
    instructions=topic_guardrail_instructions,
    model="gpt-4o-mini",
    output_type=TopicGuardrailOutput,
)

This is still an agent, but it is not a user-facing assistant. It is a small classifier that gives the rest of the system a decision.

Test the guardrail directly

Before attaching the guardrail to the FAQ agent, run it on allowed and blocked examples:

result = await Runner.run(topic_guardrail_agent, "How do I install Docker?")
print(f"Relevant: {result.final_output}")

The Docker question should pass because technical setup belongs in the course domain.

Run an unrelated example:

result = await Runner.run(topic_guardrail_agent, "What's the best pizza recipe?")
print(f"Irrelevant: {result.final_output}")

A typical failed classification for "How do I cook pizza?" looks like this:

reasoning='Relates to cooking, not data engineering or course topics.' fail=True

Test the classifier on its own first. If it is wrong here, attaching it to the assistant will only make debugging harder.

Convert the classifier into an input guardrail

The SDK input guardrail function runs the classifier and returns GuardrailFunctionOutput:

from agents import input_guardrail, GuardrailFunctionOutput
from agents.exceptions import InputGuardrailTripwireTriggered

@input_guardrail
async def topic_guardrail(ctx, agent, input):
    """Check if the user's question is about the course."""
    result = await Runner.run(topic_guardrail_agent, input)
    output = result.final_output

    return GuardrailFunctionOutput(
        output_info=output.reasoning,
        tripwire_triggered=output.fail,
    )

output_info is the message you can use later when you tell the user why the request was blocked. tripwire_triggered=True is the signal that stops the agent run.

The term tripwire is useful as an analogy: something detects a bad condition and stops the line. Here the guardrail stops the main agent run instead of letting it continue to an unsafe or irrelevant answer.

Attach it to the FAQ assistant

Create a guarded version of the FAQ agent:

guarded_faq_agent = Agent(
    name="guarded_faq_assistant",
    instructions=faq_instructions,
    tools=[search_faq],
    model="gpt-4o-mini",
    input_guardrails=[topic_guardrail],
)

The assistant instructions and tools are unchanged. The difference is that the SDK now runs topic_guardrail before the FAQ assistant.

Try an off-topic prompt:

prompt = "how do I eat salami?"
result = await Runner.run(guarded_faq_agent, prompt)
result.final_output

That cell raises InputGuardrailTripwireTriggered when the guardrail fails. The next file turns that exception into normal application flow.

Continue with Part 4: Tripwire handling.

Questions & Answers (0)

Sign in to ask questions