How do I see which step was slow in an agent built with the Claude Agent SDK?
Rius shows each Claude Agent SDK task as a trace with a root AGENT span and one generation for the runtime turn, and the duration on each tells you where the time went. The integration is manual, so you get one root span per task and one generation per turn, plus any steps you wrap in observe yourself. The runtime’s inner work (tool calls, sub-turns, file operations) produces no spans, so wrap your own code around it to split the time further.
How it works
The Claude Agent SDK drives the Claude Code runtime in a separate process. No model client runs in your process, so auto-instrumentation has nothing to hook and coverage is explicit:
- Recorded by the pieces you add: an
observewrapper makes each task the root AGENT span, and a manual generation wraps thequery()loop. You fill the generation from the run’sResultMessagewith token usage, the real model id and the collected answer. Rius computes cost from those, as for any generation. - Not recorded: the runtime’s internal steps. Tool calls, sub-turns and file operations happen inside the Claude Code process, and the generation holds the turn’s totals only.
- Recorded only if you wrap them: any step in your own code, such as loading context before the call or post-processing the answer afterwards.
A task looks like this in the trace:
handle-task AGENT 5.0s
└─ claude_agent_turn LLM 5.0s claude-haiku-4-5, tokens + costIf the generation takes nearly all of the root span’s time, the runtime turn is the slow step. If the root is much longer than the generation, the time is in your own code around it, and wrapped steps show which part.
Set it up
- Install the SDK next to the Claude Agent SDK. The base package is enough, and the
claudeCLI must be installed and authenticated. Integrations covers install and enabling for every framework.
pip install glassflow-rius claude-agent-sdk-
Wrap the task and the
query()loop as shown on the Claude Agent SDK page. Use a full model id such asclaude-haiku-4-5, not an alias, or the generation has no cost. Keepprovider="anthropic"on the generation so the cache counts are added toinput_tokensfor you. -
Wrap each step of your own that you want timed. A tool function that no integration records gets the TOOL kind, so it appears as a Tool span. A bare
observemakes a CHAIN span instead.
import rius
from rius import SpanKind
@rius.observe(name="load-context", kind=SpanKind.TOOL)
def load_context(task: str) -> str:
... # your own code, for example a lookup before the model call
@rius.observe(name="handle-task", kind=SpanKind.AGENT)
async def handle_task(task: str) -> str:
context = load_context(task)
# then the manual generation around query(), as on the Claude Agent SDK page
...-
Run one task and open the trace. The root AGENT span and the generation with a full model id, tokens and cost should be there, along with a span for each step you wrapped.
-
Group tasks by conversation by calling
handle_taskinsiderius.session(), opened before the call so the root span carries it too. The SDK’s ownsession_idsuits a resumed conversation, but it only arrives with the first task’s messages, after the root span has started, so a new conversation needs an id you choose up front.
What you get
In the console, Exploring traces shows where the time went at three levels:
- Trace list: each row has a latency column, so you can spot slow runs first. Sort or filter to the agent you care about.
- Waterfall view: open a trace to see its spans as a tree on a timeline, badged by kind. The bar lengths show whether the generation or one of your wrapped steps dominates.
- Map view: the Map tab has a latency lens, one node per span. Click a node to land on the tree filtered to that span.
- Spans tab: lists individual spans. Filter with the All kinds menu (for example Tool) and click the Duration header to sort, which ranks your wrapped steps across many runs.
On the generation, you also get the real model id, token counts and cost for the turn. Costs are list-price estimates in US dollars. For aggregate views across agents, see Monitor AI agents in production.
When something else fits
- If you need each tool call or sub-turn inside the Claude Code runtime as its own span, the Claude Agent SDK integration does not provide it. It records the turn’s totals only, so timing inside the runtime is not visible.
- If your agent also calls models through a client library running in your process, check Integrations for the ones that record calls automatically.