PII Detection

Reject or mask the PII patterns recognized by PIIDetectionGuardrail.

Use PIIDetectionGuardrail to reject requests containing PII or replace detected values with masked placeholders.

This example catches InputCheckError around print_response(), but The audited main source converts that guardrail exception into a run with RunStatus.error. Its exception handlers therefore do not identify rejected runs. Use the status-checking pattern in the PII Detection guide instead.

pii_detection.py
"""
Pii Detection
=============================

Example demonstrating how to use PII detection guardrails with Agno Agent.
"""

import asyncio

from agno.agent import Agent
from agno.exceptions import InputCheckError
from agno.guardrails import PIIDetectionGuardrail
from agno.models.openai import OpenAIResponses


# ---------------------------------------------------------------------------
# Create Agent
# ---------------------------------------------------------------------------
async def main():
    """Demonstrate PII detection guardrails functionality."""
    print("PII Detection Guardrails Demo")
    print("=" * 50)

    # Create an agent with PII detection protection
    agent = Agent(
        name="Privacy-Protected Agent",
        model=OpenAIResponses(id="gpt-5-mini"),
        pre_hooks=[PIIDetectionGuardrail()],
        description="An agent that helps with customer service while protecting privacy.",
        instructions="You are a helpful customer service assistant. Always protect user privacy and handle sensitive information appropriately.",
    )

    # Test 1: Normal request without PII (should work)
    print("\n[TEST 1] Normal request without PII")
    print("-" * 30)
    try:
        agent.print_response(
            input="Can you help me understand your return policy?",
        )
        print("[OK] Normal request processed successfully")
    except InputCheckError as e:
        print(f"[ERROR] Unexpected error: {e}")

    # Test 2: Request with SSN (should be blocked)
    print("\n[TEST 2] Input containing SSN")
    print("-" * 30)
    try:
        agent.print_response(
            input="Hi, my Social Security Number is 123-45-6789. Can you help me with my account?",
        )
        print("[WARNING] This should have been blocked!")
    except InputCheckError as e:
        print(f"[BLOCKED] PII blocked: {e.message}")
        print(f"   Trigger: {e.check_trigger}")

    # Test 3: Request with credit card (should be blocked)
    print("\n[TEST 3] Input containing credit card")
    print("-" * 30)
    try:
        agent.print_response(
            input="I'd like to update my payment method. My new card number is 4532 1234 5678 9012.",
        )
        print("[WARNING] This should have been blocked!")
    except InputCheckError as e:
        print(f"[BLOCKED] PII blocked: {e.message}")
        print(f"   Trigger: {e.check_trigger}")

    # Test 4: Request with email address (should be blocked)
    print("\n[TEST 4] Input containing email address")
    print("-" * 30)
    try:
        agent.print_response(
            input="Please send the receipt to john.doe@example.com for my recent purchase.",
        )
        print("[WARNING] This should have been blocked!")
    except InputCheckError as e:
        print(f"[BLOCKED] PII blocked: {e.message}")
        print(f"   Trigger: {e.check_trigger}")

    # Test 5: Request with phone number (should be blocked)
    print("\n[TEST 5] Input containing phone number")
    print("-" * 30)
    try:
        agent.print_response(
            input="My phone number is 555-123-4567. Please call me about my order status.",
        )
        print("[WARNING] This should have been blocked!")
    except InputCheckError as e:
        print(f"[BLOCKED] PII blocked: {e.message}")
        print(f"   Trigger: {e.check_trigger}")

    # Test 6: Mixed PII in context (should be blocked)
    print("\n[TEST 6] Multiple PII types in one request")
    print("-" * 30)
    try:
        agent.print_response(
            input="Hi, I'm John Smith. My email is john@company.com and phone is 555.987.6543. I need help with my account.",
        )
        print("[WARNING] This should have been blocked!")
    except InputCheckError as e:
        print(f"[BLOCKED] PII blocked: {e.message}")
        print(f"   Trigger: {e.check_trigger}")

    # Test 7: Edge case - formatted differently (should still be blocked)
    print("\n[TEST 7] PII with different formatting")
    print("-" * 30)
    try:
        agent.print_response(
            input="Can you verify my credit card ending in 4532123456789012?",
        )
        print("[WARNING] This should have been blocked!")
    except InputCheckError as e:
        print(f"[BLOCKED] PII blocked: {e.message}")
        print(f"   Trigger: {e.check_trigger}")

    print("\n" + "=" * 50)
    print("PII Detection Demo Complete")
    print("All sensitive information was successfully blocked!")

    # Create an agent with PII detection which masks the PII in the input
    agent = Agent(
        name="Privacy-Protected Agent (Masked)",
        model=OpenAIResponses(id="gpt-5-mini"),
        pre_hooks=[PIIDetectionGuardrail(mask_pii=True)],
        description="An agent that helps with customer service while protecting privacy.",
        instructions="You are a helpful customer service assistant. Always protect user privacy and handle sensitive information appropriately.",
    )

    # Test 8: Request with SSN (should be masked)
    print("\n[TEST 8] Input containing SSN (masked mode)")
    print("-" * 30)
    agent.print_response(
        input="Hi, my Social Security Number is 123-45-6789. Can you help me with my account?",
    )


# ---------------------------------------------------------------------------
# Run Agent
# ---------------------------------------------------------------------------
if __name__ == "__main__":
    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.

Current runner

Add the helper, then replace main() with this version. The first agent rejects matching input; the second masks recognized values. Detection covers the implemented patterns, not every form of personal data.

async def main():
    agent = Agent(name='Privacy-Protected Agent', model=OpenAIResponses(id='gpt-5-mini'), pre_hooks=[PIIDetectionGuardrail()], description='An agent that helps with customer service while protecting privacy.', instructions='You are a helpful customer service assistant. Always protect user privacy and handle sensitive information appropriately.')
    prompts = (
        "Can you help me understand your return policy?",
        "My Social Security Number is 123-45-6789.",
        "My card number is 4532 1234 5678 9012.",
        "Please send the receipt to john.doe@example.com.",
        "My phone number is 555-123-4567.",
        "My email is john@company.com and phone is 555.987.6543.",
        "Can you verify 4532123456789012?",
    )
    for prompt in prompts:
        show_checked_response(await agent.arun(input=prompt))
    agent = Agent(name='Privacy-Protected Agent (Masked)', model=OpenAIResponses(id='gpt-5-mini'), pre_hooks=[PIIDetectionGuardrail(mask_pii=True)], description='An agent that helps with customer service while protecting privacy.', instructions='You are a helpful customer service assistant. Always protect user privacy and handle sensitive information appropriately.')
    show_checked_response(await agent.arun(input="My SSN is 123-45-6789. Can you help?"))

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 pii_detection.py, then run:

python pii_detection.py

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