Skip to main content

Trace an OpenAI Agents SDK Support-Triage System

What you'll build: a support system built with the OpenAI Agents SDK — a Triage agent that reads an incoming message and hands off to a Billing agent or a Tech Support agent, each with its own tool — run across a two-turn conversation. Every agent, handoff, tool, and model call lands in AcruxCore automatically, with no AcruxCore code anywhere in the agents themselves. The Agents SDK ships for both Python and TypeScript/Node, and this page builds the same system in either — pick a language tab wherever the code differs.

The previous tutorial covered the same integration — OpenTelemetry (OTel) wire-protocol export straight to AcruxCore's POST /api/v1/traces/otlp — for CrewAI. This page uses the same endpoint for a different framework, on purpose: the point isn't CrewAI or the Agents SDK specifically, it's that any framework speaking OTel needs nothing from AcruxCore beyond an endpoint and a key. The Agents SDK's signature feature, a handoff — one agent transferring a conversation to another — also happens to produce a genuinely different-shaped trace worth seeing land correctly.

Prefer a notebook?

triage_system.ipynb is this page's Python route as one runnable notebook, written for a first-timer: a preflight cell that prints every instrumentor version, the three agents built step by step, a live read of the handoff span next to a real tool call, and four real ways to get this wrong — including the tracing switch that silently stops every trace reaching AcruxCore. It renders on GitHub with its saved output, so you can read it through before running anything.

1. Create an AcruxCore API key

Open Account & keys → New key, name it agents-sdk-tutorial, and create it.

Copy the key the moment it's shown — this is the only time the full value appears.

2. Install the SDK's OTel helper

export ACRUXCORE_API_KEY=<your-acruxcore-key>
export ACRUXCORE_BASE_URL=https://api.acruxcore.com/api/v1

Both scripts below also load a .env file automatically, if you'd rather keep the keys out of your shell — python-dotenv for the Python script, dotenv for the Node one.

acruxcore.otel.register(), a small helper the Python SDK ships for this, reads those two variables and builds a standard OTel pipeline pointed at $ACRUXCORE_BASE_URL/traces/otlp. If you'd rather not add the SDK as a dependency, the OTLP API reference shows the same pipeline written out by hand.

3. Build the triage system

Three agents. The Triage agent never answers directly — it only decides who should:

billing_agent = Agent(
name="Billing",
handoff_description="Handles subscription, billing, and charge questions.",
tools=[check_subscription],
model="gpt-4o-mini",
)

tech_support_agent = Agent(
name="Tech Support",
handoff_description="Handles app crashes, bugs, and order/delivery status.",
tools=[lookup_order],
model="gpt-4o-mini",
)

triage_agent = Agent(
name="Triage",
instructions="Route the customer to Billing or Tech Support. Do not answer directly.",
handoffs=[billing_agent, tech_support_agent],
model="gpt-4o-mini",
)

The only AcruxCore-aware code in the whole file is one call, above the agent definitions — acruxcore.otel.register() reads the two env vars from Step 2 and turns on the Agents SDK's own OpenInference instrumentor:

provider = register(
service_name="support-triage-agents-sdk",
instrument=["openai_agents"],
)
Don't disable the Agents SDK's own tracing

The Agents SDK reports its own traces to platform.openai.com by default. It's tempting to turn that off first — Python's set_tracing_disabled(True), or Node's tracingDisabled: true run option — don't. That disables the SDK's whole tracing pipeline, which starves the OpenInference processor too, and nothing reaches AcruxCore. instrument: ["openai_agents"] already replaces the default processor with this one; no separate disable step is needed.

The full script threads a session.id across both turns of the conversation with openinference.instrumentation.using_session(session_id), and continues the conversation across Runner.run() calls with turn1.to_input_list() + [...] — see triage_system.py on GitHub for the complete source.

4. Run it

pip install openai-agents 'acruxcore[otel]' openinference-instrumentation-openai-agents python-dotenv
python triage_system.py
--- Turn 1 (expect Billing handoff) ---
You are on the Pro plan, which renewed on August 1, 2026, and your last charge was $49.00.
If you see two charges this month, it may be worth checking your account for another
subscription or contacting billing support for clarification.

--- Turn 2 (expect Tech Support handoff) ---
Order #A1234 was delivered on app version 3.4.1, which has a known crash bug. This issue is
fixed in the latest app version. I recommend updating your app to prevent further crashes.

session.id used for both turns: support-triage-demo-session

Turn 1 triaged to Billing without being told to; turn 2, continuing the same conversation, triaged to Tech Support instead — the model routed both times based only on what was asked.

5. See both turns grouped into one session

Open Observability → Sessions and click support-triage-demo-session: both turns are there, each its own trace, with real token counts and cost.

The support-triage-demo-session showing two traces, each with 12 spans, real token counts, and real cost

6. Follow the handoff span tree

Open the second trace — the one where Triage routes to Tech Support. A handoff lands as a tool span (AcruxCore has no dedicated handoff kind; OpenInference represents a handoff as the calling agent invoking a tool named for the target), sitting between the two agents' own spans:

Trace tree showing an agent span for Triage containing an LLM span and a tool span named &quot;handoff to Tech Support&quot;, followed by a sibling agent span for Tech Support containing an LLM span, a tool span for lookup_order, and a second LLM span

Click the lookup_order span to see the real argument the Tech Support agent chose and the real (mocked) result it got back:

The expanded lookup_order span showing the order_id A1234 as input and the real delivery status and crash-bug note as output

The Agents SDK's own OpenInference instrumentor — openinference-instrumentation-openai-agents in Python, @arizeai/openinference-instrumentation-openai-agents in Node — captured all of this: the two agents, the handoff between them, the tool call, and both model calls, from an app that has no idea AcruxCore exists.

What's next