Agent Map
The Agent Map answers a question the raw span waterfall can’t: who calls whom, and which node dominates cost and latency? It rolls the spans and tool calls of a run — or an entire cohort of runs — into a compact topology: each agent, tool, and LLM model becomes a single node, the hand-offs between them become weighted edges, and the one node responsible for the most time gets flagged as the bottleneck. Use it to see the shape of a multi-agent run at a glance and find the node worth optimizing first.
There are two ways in: the Agent Map tab on a single trace or conversation, and the standalone Agent Map page that aggregates a whole cohort.
At a glance
| Where | The Agent Map tab in the trace/conversation inspector (Observe → Traces / Sessions), and the standalone Agent Map page (sidebar → Diagnose) |
| Key API routes | GET /sessions/{id}/agent-graph (one run), GET /conversations/{id}/agent-graph (one conversation), GET /agent-graph (a cohort) |
| Scope | Always scoped to the current agent; you can only map traces in agents you belong to |
| Needs | Ingested trace data — nothing else. No LLM connection required |
Map a single run
Open any trace from the Traces list and pick the Agent Map tab in the span explorer (alongside Spans, Conversation, Graph, and Raw).
Where the Graph tab shows every span grouped by turn, the Agent Map collapses the run into its distinct actors and draws the flow between them, laid out left→right — a supervisor on the left flowing out to the workers, tools, and models it drives.
- Nodes are colored by kind: agent, tool, LLM (keyed by model), plus retrieval and guardrail spans where present.
- Edges are the observed hand-offs. Use the Edge weight toggle in the toolbar to drive edge thickness by Calls, Latency, or Tokens.
- The bottleneck is highlighted. The single node responsible for the most time gets a red ring and a ⚡ Bottleneck badge, and it’s named in the toolbar.
- Click any node for its full breakdown: invocations, self time and self-time share, total (inclusive) time, model, tokens, cost, and errors. Node cost is tokens × that model’s price; a node whose model has no price shows — and the map’s totals are flagged as partial — see Cost & model pricing.
Self time, not wall-clock, decides the bottleneck. Neens attributes each node its self time — its own time minus the time spent inside the children it called. That way the honest culprit (usually a model or a slow tool) is flagged, not the outer agent that merely wraps everything below it.
Map a conversation
A session is a conversation — one or more traces sharing a
conversation_id. Open a session from the Sessions page and its Agent Map tab stitches every
member trace into one topology, so a multi-turn interaction reads as a single flow rather than one map
per turn. Because a conversation is genuinely several runs, this view is cohort-shaped — it carries
per-run (here, per-turn) averages and the Show toggle described below.
Map a cohort
A single run is an anecdote. To see whether a bottleneck is systemic — which node dominates cost and latency across the last 7 days, a failure cluster, or an agent version — open the standalone Agent Map page under Diagnose in the sidebar.
Pick the cohort
The page uses the same filters as the Traces and Sessions lists — time window, agent, agent version, failure cluster, model, tool, span filters, tags, scores, and full-text search. The cohort is exactly the set the Sessions list would show for those filters.
You can also arrive pre-filtered: from a failure Clusters detail, click View agent map for this scope to jump straight to the aggregate topology for that cluster.
Read per-run averages
By default node and edge numbers read as a typical run — the cohort total divided by the number of runs — so cohorts of different sizes stay comparable. Use the Show toggle to switch between Per-run avg and Totals (where the whole cohort spent its time and money).
Drill into a node
Click any node to see how many of the cohort’s runs it appears in (Appears in N of M runs — prevalence), its per-run and total invocations, self time, cost, and its Error rate — the share of its calls that errored, so a handful of errors over thousands of calls doesn’t masquerade as a hot spot.
Versions split by model. LLM nodes are keyed by model, so a model change across the cohort shows up as two distinct nodes — the honest way to compare, say, opus vs. sonnet on the same workload.
How it works
Neens reads a session’s spans and tool calls and folds them into nodes and edges in-process — no LLM call is involved, so the Agent Map works whether or not an agent has a connection configured.
- Nodes are the run’s distinct actors. Agent, tool, and retrieval spans each become a node; LLM spans are keyed by model. Every invocation of the same actor rolls into one node.
- Edges are the parent→child hand-offs between those actors, summed across the runs in scope.
- Self time is a node’s own duration minus the time spent inside its children. The node with the greatest self time is the bottleneck.
Aggregating a cohort
The cohort page and the conversation tab share one aggregation. Each node carries values summed across runs, alongside per-run means, its prevalence (in how many runs it appears), and its error rate (share of calls that errored) — the same for each edge. That’s why per-run averages are the default: totals reward big cohorts, but a per-run mean reads as one representative run.
The cohort cap
A cohort filter — a 30-day window, a large failure cluster — can select tens of thousands of runs, and each contributes spans and tool calls Neens reads and merges. To keep the read tier safe, the matched set is capped at the newest 2,000 runs. When a cohort exceeds the cap, the page shows an amber banner:
This cohort matched N runs; showing the newest 2,000 (cap 2,000). N−2,000 older runs were left out — narrow the time window or filter to include them.
Narrow the time window or tighten the filters to bring the whole cohort under the cap. The per-run and per-conversation maps are never capped — their run counts are small.
Aggregate maps are cache-eligible on the read tier, so a repeated cohort view is served from a short-lived, tenant-keyed cache — toggling Edge weight or Show stays fast.
Reference
API endpoints
| Endpoint | What it returns |
|---|---|
GET /sessions/{id}/agent-graph | The topology for one trace: nodes, weighted edges, and the bottleneck node id |
GET /conversations/{id}/agent-graph | The topology for a whole conversation, rolling up its member traces (per-run/per-turn means + prevalence + error rate) |
GET /agent-graph | The aggregate topology for a cohort, driven by the full GET /sessions filter vocabulary; includes a cohort block with matched_total, rolled_up, cap, capped, and dropped |
The cohort endpoint accepts the same query parameters as GET /sessions (see
Traces & Sessions) — status, agent_name, version, cluster_id,
failure_set, model, tool_name, span_kind, started_after/started_before, score_metric,
enrichment, q, and the numeric ranges.
Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
| ”No agents or tools to graph for this run/conversation.” | The trace has no agent, tool, or LLM spans to connect | Instrument agent/tool spans with a supported semantic convention (see Send traces) |
| The cohort map shows an amber “showing the newest 2,000” banner | The cohort matched more runs than the cap | Narrow the time window or tighten the filters to bring the whole cohort under the cap |
| A model change isn’t obvious in the map | LLM nodes are keyed by model, so a version that switched models shows as two separate nodes | Filter by Agent Version to compare one version’s map at a time |
| The bottleneck isn’t the node I expected | The bottleneck is chosen by self time, not inclusive time — the outer agent’s total includes its children | Click the flagged node to confirm its self-time share; the wrapping agent’s time is mostly its children’s |