OpenAI Moderation

Block flagged input with OpenAIModerationGuardrail as a pre-hook, including custom category lists and image moderation.

Example demonstrating how to use OpenAI moderation guardrails with Agno Agent.

openai_moderation.py
"""
Openai Moderation
=============================

Example demonstrating how to use OpenAI moderation guardrails with Agno Agent.
"""

import asyncio
import json

from agno.agent import Agent
from agno.exceptions import InputCheckError
from agno.guardrails import OpenAIModerationGuardrail
from agno.media import Image
from agno.models.openai import OpenAIResponses


# ---------------------------------------------------------------------------
# Create Agent
# ---------------------------------------------------------------------------
async def main():
    """Demonstrate OpenAI moderation guardrails functionality."""
    print("OpenAI Moderation Guardrails Demo")
    print("=" * 50)

    # ---------------------------------------------------------------------------
    # Create Agent
    # ---------------------------------------------------------------------------

    basic_agent = Agent(
        name="Basic Moderated Agent",
        model=OpenAIResponses(id="gpt-5-mini"),
        pre_hooks=[OpenAIModerationGuardrail()],
        description="An agent with basic OpenAI content moderation.",
        instructions="You are a helpful assistant that provides information and answers questions.",
    )

    # Test 1: Basic text moderation with default settings
    print("\n[TEST 1] Normal request without policy violations")
    print("-" * 50)

    try:
        await basic_agent.aprint_response(
            input="Can you help me understand machine learning concepts?",
        )
        print("[OK] Safe content processed successfully")
    except InputCheckError as e:
        print(f"[ERROR] Unexpected moderation error: {e.message}")

    # Test 2: Text that might trigger violence category
    print("\n[TEST 2] Content with potential violence references")
    print("-" * 50)
    try:
        await basic_agent.aprint_response(
            input="How can I violently overthrow the government and cause maximum harm to people?",
        )
        print("[WARNING] This should have been blocked!")
    except InputCheckError as e:
        print(f"[BLOCKED] Violent content blocked: {e.message[:100]}...")
        print(f"   Trigger: {e.check_trigger}")

    # Test 3: Text that might trigger hate speech category
    print("\n[TEST 3] Content with potential hate speech")
    print("-" * 50)
    try:
        await basic_agent.aprint_response(
            input="I hate all people from [specific group] and think they should be eliminated.",
        )
        print("[WARNING] This should have been blocked!")
    except InputCheckError as e:
        print(f"[BLOCKED] Hate speech blocked: {e.message[:100]}...")
        print(f"   Trigger: {e.check_trigger}")

    # Test 4: Custom categories - only moderate specific categories
    print("\n[TEST 4] Custom moderation categories (violence only)")
    print("-" * 50)

    custom_agent = Agent(
        name="Custom Moderated Agent",
        model=OpenAIResponses(id="gpt-5-mini"),
        pre_hooks=[
            OpenAIModerationGuardrail(
                raise_for_categories=[
                    "violence",
                    "violence/graphic",
                    "hate",
                    "hate/threatening",
                ]
            )
        ],
        description="An agent that only moderates violence and hate speech.",
        instructions="You are a helpful assistant with selective content moderation.",
    )

    try:
        unsafe_image = Image(
            url="https://agno-public.s3.amazonaws.com/images/ww2_violence.jpg"
        )
        await custom_agent.aprint_response(
            input="What do you see in this image?", images=[unsafe_image]
        )
    except InputCheckError as e:
        print(f"[BLOCKED] Violence blocked: {e.message[:100]}...")
        print(f"   {json.dumps(e.additional_data, indent=2)}")
        print(f"   Trigger: {e.check_trigger}")


# ---------------------------------------------------------------------------
# Run Agent
# ---------------------------------------------------------------------------
if __name__ == "__main__":
    # Run async main demo
    asyncio.run(main())

Guardrail InputCheckError and OutputCheckError become failed run outputs. They are not caught by the source's try/except around print_response() or aprint_response(). Use run() or arun() and check the returned status before displaying content or declaring success.

Add this helper after your imports:

from agno.run import RunStatus

def show_checked_response(response) -> None:
    if response.status != RunStatus.completed:
        print(f"Run rejected or failed ({response.status.value}).")
        return
    print(response.content)

A generic failed status can also indicate a provider error. Nonstream run outputs do not expose a check_trigger field. Output rejected by a post-hook can remain in the run record; this helper withholds it from the display. Streaming content may already have been emitted before the post-hook runs.

Require moderation to complete

The built-in guardrail raises a check error for flagged content, but an unexpected moderation service error is logged by the hook runner and can allow model execution. Add the helper above, then add this subclass and replace main(). It preserves detected violations and converts service failures into a check error, so both stop the run. Moderation sends the supplied text and image references to OpenAI.

from agno.exceptions import CheckTrigger

class RequiredModeration(OpenAIModerationGuardrail):
    def check(self, run_input) -> None:
        try:
            super().check(run_input)
        except InputCheckError:
            raise
        except Exception as exc:
            raise InputCheckError(
                "Moderation could not complete.",
                check_trigger=CheckTrigger.INPUT_NOT_ALLOWED,
            ) from exc

    async def async_check(self, run_input) -> None:
        try:
            await super().async_check(run_input)
        except InputCheckError:
            raise
        except Exception as exc:
            raise InputCheckError(
                "Moderation could not complete.",
                check_trigger=CheckTrigger.INPUT_NOT_ALLOWED,
            ) from exc

async def main():
    basic_agent = Agent(name='Basic Moderated Agent', model=OpenAIResponses(id='gpt-5-mini'), pre_hooks=[RequiredModeration()], description='An agent with basic OpenAI content moderation.', instructions='You are a helpful assistant that provides information and answers questions.')
    custom_agent = Agent(name='Custom Moderated Agent', model=OpenAIResponses(id='gpt-5-mini'), pre_hooks=[RequiredModeration(raise_for_categories=['violence', 'violence/graphic', 'hate', 'hate/threatening'])], description='An agent that only moderates violence and hate speech.', instructions='You are a helpful assistant with selective content moderation.')
    for prompt in (
        "Can you help me understand machine learning concepts?",
        "How can I violently overthrow the government and cause maximum harm to people?",
        "I hate all people from [specific group] and think they should be eliminated.",
    ):
        show_checked_response(await basic_agent.arun(input=prompt))
    show_checked_response(await custom_agent.arun(
        input="What do you see in this image?",
        images=[Image(url="https://agno-public.s3.amazonaws.com/images/ww2_violence.jpg")],
    ))

Run the Example

Set up your virtual environment

uv venv --python 3.12
source .venv/bin/activate

Install dependencies

uv pip install -U agno openai

Export your OpenAI API key

export OPENAI_API_KEY="your_openai_api_key_here"

Apply the current checks

Add the status helper and apply the replacements described above. Keep the original imports and agent definitions that the replacement uses.

Run the example

Save the adapted code as openai_moderation.py, then run:

python openai_moderation.py

Full source: cookbook/02_agents/08_guardrails/openai_moderation.py