checkrd

OpenAI Agents SDK

Wire Checkrd into the OpenAI Agents SDK via TracingProcessor and Guardrail.

OpenAI Agents SDK (Python)

The OpenAI Agents SDK splits its extension surface into two layers, and Checkrd targets both:

  1. Tracing processors — receive on_trace_* and on_span_* events for observability. Cannot abort a run.
  2. Guardrails — run before / after the model invocation and can tripwire the agent to abort.

CheckrdTracingProcessor handles observability. CheckrdInputGuardrail and CheckrdOutputGuardrail handle enforcement.

Install

bash
pip install 'checkrd[openai-agents]'

Quickstart

python
from agents import Agent, Runner, add_trace_processor
from checkrd import Checkrd
from checkrd.integrations.openai_agents import (
    CheckrdInputGuardrail,
    CheckrdTracingProcessor,
)

with Checkrd(policy="policy.yaml") as client:
    # 1. Wire telemetry. Does NOT enforce — observation only.
    add_trace_processor(CheckrdTracingProcessor.from_checkrd(client))

    # 2. Wire enforcement on each agent.
    agent = Agent(
        name="research-agent",
        instructions="Summarize tomorrow's calendar.",
        input_guardrails=[CheckrdInputGuardrail.from_checkrd(client).as_guardrail()],
    )

    result = Runner.run_sync(agent, "Tomorrow's calendar")
    print(result.final_output)

Why two adapters

The Agents SDK's tracing processor is observation-only by design — it cannot abort a run. Trying to enforce from a tracing processor would break in the next minor version. Guardrails are the explicit enforcement seam.

The split lets you wire telemetry once globally (so the OpenAI traces dashboard keeps working) and add enforcement per-agent based on each agent's threat model.

Output guardrails

Use CheckrdOutputGuardrail when the agent's output (not just input) needs policy filtering — for example, blocking refusal-bypass attempts or schema-validating tool outputs:

python
agent = Agent(
    name="bot",
    output_guardrails=[CheckrdOutputGuardrail.from_checkrd(client).as_guardrail()],
)

Both guardrails share the same options interface (engine, agent_id, sink, enforce, dashboard_url).

What gets enforced

Each guardrail produces a synthetic URL the policy engine matches against:

GuardrailSynthetic URLBody
CheckrdInputGuardrailhttps://openai-agents.local/input/{agent_name}{"input": ...}
CheckrdOutputGuardrailhttps://openai-agents.local/output/{agent_name}{"output": ...}

Example policy:

yaml
agent: research-agent
default: allow

rules:
  - name: deny-pii-extraction
    deny:
      url: "openai-agents.local/input/*"
      body:
        input: "*ssn*|*social security*|*credit card*"

Tracing event shapes

The processor emits one telemetry event per span. The kind field comes from the span's span_data type:

Span kindSourceExtra fields
generationGenerationSpanDatatarget (model), input_tokens, output_tokens
functionFunctionSpanDatatarget (function name)
agentAgentSpanDatatarget (agent name)
handoffHandoffSpanDatatarget (target agent)
guardrailGuardrailSpanDatatarget (tripwire flag)
mcplisttoolsMCPListToolsSpanDatatarget (server)

Trace correlation: request_id is the span's trace_id, span_id is span.span_id, parent_span_id is span.parent_id.

Observation-only mode

For a deny-but-don't-block rollout, set enforce=False:

python
guard = CheckrdInputGuardrail(
    engine=engine,
    agent_id="research",
    sink=sink,
    enforce=False,
)

The guardrail returns tripwire_triggered=False even on policy deny. The deny is logged and emitted as a *_denied telemetry event, so dashboards show what would have been blocked.

Caveats

  • Package name confusion. PyPI: openai-agents. Import: agents. (Not openai_agents.)
  • shutdown() and force_flush() are mandatory on a TracingProcessor. We implement them — you do not need to.
  • Guardrail functions are async even when the agent is sync. The SDK runs them on the agent's loop.
  • The Agents SDK ships its own OpenAI traces dashboard. Use add_trace_processor() (additive) instead of set_trace_processors() (replaces) so you keep both.