Skip to Content
Rius

How do I see every tool call in an agent built with the OpenAI Agents SDK?

Rius shows each tool call in an OpenAI Agents SDK agent as its own tool span once you wrap the tool body with observe. Model calls are captured on their own, with tokens and cost. Tool executions and the run itself need one observe wrapper each, and the tool spans then sit under the run in the trace tree. This applies to the Python SDK. For the wider picture of watching agents in production, see Monitor AI agents in production.

How it works

The OpenAI Agents SDK drives the OpenAI client underneath, so Rius’s openai integration records every model call without framework-specific code. Each call arrives as a generation with model, tokens, cost and time to first token.

The agent loop, tool executions and handoffs run inside the framework, and auto-instrumentation only sees the client calls that come out of it. That is why tool calls need a span of your own:

  • Recorded on its own: every model call, as an LLM generation.
  • Needs your wrapper: the entrypoint around Runner.run(), which becomes the root agent span, and each function tool body, which becomes a tool span.
  • Not recorded as a span: a handoff between agents. It shows only as consecutive generations that belong to different agents.

The tool span parents under the run through ambient context, so you do not pass anything between the wrapper and the tool. See the OpenAI Agents SDK integration for the full recipe.

Set it up

  1. Install the instrumentation and the framework. The openai extra installs the instrumentation, not the library itself.
pip install "glassflow-rius[openai]" openai-agents
  1. Wrap each tool body with observe and mark it as a tool. Wrap the entrypoint as the agent root. Put @function_tool outermost and observe on the function body, because the framework reads the function signature for its tool schema.
import rius from agents import Agent, Runner, function_tool, set_tracing_disabled from rius import SpanKind rius.init(api_key="ri_...", service_name="my-agent") set_tracing_disabled(True) @function_tool @rius.observe(kind=SpanKind.TOOL) def account_status(email: str) -> str: """Fetch a customer's account standing by email.""" return lookup(email) support_agent = Agent( name="support_agent", instructions="Answer support questions; check accounts with the tool.", tools=[account_status], model="gpt-5-mini", ) @rius.observe(name="handle-ticket", kind=SpanKind.AGENT) def handle_ticket(ticket: str) -> str: result = Runner.run_sync(support_agent, ticket) return str(result.final_output)
  1. Repeat the wrapper on every tool you want to see. A tool without it makes no span of its own.
  2. Use underscores in agent names. Hyphens are rewritten in the generated handoff tool name, and the framework warns on every run.
  3. Run one request that calls a tool, then open it on the Traces page and check for the root agent span, the LLM generations and your named tool spans. Streamed runs carry token counts only when usage is enabled on the underlying client, as the integrations overview notes for OpenAI streams.

What you get

Open a trace and the waterfall shows the run as a tree on a timeline. Spans are badged by kind, so your tool spans are easy to tell from the LLM generations around them. A run that crosses a handoff reads like this:

handle-ticket AGENT 9.8s ├─ Response LLM 2.3s triage decides to hand off ├─ Response LLM 2.8s billing agent decides to call the tool ├─ execute_tool account_status TOOL 0ms └─ Response LLM 4.6s billing agent answers

To see tool calls across many runs, open the Spans tab on the Traces page and choose Tool in the All kinds menu. You get one list of every tool call, which you can sort by duration and filter by status. On an open trace, back-to-back calls of the same tool collapse into one row such as fetch_article · 3 attempts · recovered, which you expand for the individual attempts, and a failed tool call is highlighted along the path from the root. The span panel has previous and next buttons, so you can step through the calls one by one. See Exploring traces for the views and filters.

When something else fits

  • If handoff boundaries matter, add an observe wrapper per agent entry, or use the framework’s RunHooks to open spans at agent boundaries, since a handoff has no span of its own.
  • Every model call is named Response, so the span name does not tell you which agent spoke. Read the order of generations and your tool spans instead.
  • There is no TypeScript integration for this framework today, so trace those calls with manual generations.

Still stuck? Get help

Last updated on