Part 10: Cancel on tripwire

Parallel execution removes the extra wait when guardrails pass. We also want cancellation when a guardrail fails, because the answer will be blocked anyway.

Create a guardrail exception

Start with a dataclass for the guardrail result:

from dataclasses import dataclass

@dataclass
class GuardrailResult:
    """Result from a guardrail check."""
    reasoning: str
    triggered: bool

Create an exception that carries the result:

class GuardrailException(Exception):
    """Raised when a guardrail trips."""
    def __init__(self, result: GuardrailResult):
        self.result = result
        super().__init__(result.reasoning)

This mirrors the SDK tripwire idea with plain Python objects. A failed guardrail raises a specific exception, and the runner handles that exception.

Create a failing guardrail

The failing guardrail sleeps for 1.5 seconds, then raises:

async def failing_guardrail(input: str) -> str:
    """Simulates a guardrail that takes time to process."""
    print(f"[Failing Guardrail] Starting work on: {input}")
    await asyncio.sleep(1.5)
    raise GuardrailException(GuardrailResult(
        reasoning="Content is not appropriate",
        triggered=True
    ))

If we run this with mock_agent, the guardrail fails before the agent's two-second sleep finishes.

See the failure with gather

Run both coroutines through asyncio.gather:

results = await asyncio.gather(
    failing_guardrail("hello"),
    mock_agent("hello"),
)

You should see both start messages, then the guardrail exception interrupts the cell. Depending on scheduling, the mock agent can still finish unless you cancel it explicitly.

Cancel the agent task

Create tasks so the agent can be cancelled:

guard_task = asyncio.create_task(failing_guardrail("hello"))
agent_task = asyncio.create_task(mock_agent("hello"))

Catch the guardrail exception and cancel the agent:

try:
    await asyncio.gather(agent_task, guard_task)
except GuardrailException:
    agent_task.cancel()
    try:
        await agent_task
    except asyncio.CancelledError:
        print("Agent was cancelled - saved tokens!")

Expected output:

[Failing Guardrail] Starting work on: hello
[Agent] Starting work on: hello
Agent was cancelled - saved tokens!

This is where guardrails save work. Once the policy fails, continuing the model call has no value for the user.

Continue with Part 11: DIY runner for the FAQ agent to package this into a reusable helper.

Questions & Answers (0)

Sign in to ask questions