Spans

Pinpointing the single step behind a slow or wrong answer.

A span is one step

A span is one operation inside a trace: a single model call, tool call, retrieval, agent step, guardrail check, or evaluator run. It records its own input and output, when it started and finished, whether it succeeded, and, for model calls, the tokens and cost it ran up. Where a trace is the whole request, a span is one step inside it.

Under the hood, a span is an OpenTelemetry span. OpenTelemetry defines the shape, a named, timed unit of work with a status, a parent, and key-value attributes, and traceAI fills those attributes with LLM-specific keys: the span kind that says what ran, the prompt and completion, the token counts. So every span you see in Observe is a standard OTel span carrying traceAI’s LLM attributes.

A parent span, say an agent, holds the child spans it set off, and each of those can have children of its own. That nesting is how Future AGI works out which step triggered which, and it is what lets a trace draw itself as a tree.

flowchart TD
accTitle: Spans nest inside a trace
accDescr: An agent span contains a chain span and a tool span. The chain span contains a retriever span and an LLM span.
A["agent span"] --> B["chain span"]
A --> C["tool span"]
B --> D["retriever span"]
B --> E["llm span"]

Each box is a span with its own timing and attributes. The edges come straight from OpenTelemetry: every span carries the trace ID and its parent span’s ID, and OTel propagates that context down your call stack, so a span created deep inside nests under the one above it without you wiring it up. Those same links are what a trace is rebuilt from.

What a span isn’t

  • Not a log line. A log is a flat text event. A span is a timed unit with structured input, output, status, and attributes, linked to a parent
  • Not an event. An event is a point-in-time marker inside a span, like an exception. The span is the operation that holds it

Why it matters

A response is only as strong as its weakest step, and that step is usually where things break. The trace tells you a request was slow or wrong; the span tells you which step did it and hands you the evidence: the exact prompt sent to the model, the arguments a tool received, the chunks a retriever pulled back, or the score an evaluator gave. traceAI captures these spans for you on supported frameworks, and where it can’t reach, you can add your own so no part of your pipeline stays a black box.

Span types

Every span carries a kind that says what the operation was. Observe uses it to label the span, pick its icon, and surface the fields that matter. These are the kinds traceAI emits:

TypeWhat it representsWhat it captures
LLMA single model callModel, prompt messages, completion, token counts, cost
ToolA function or tool the model invokedTool name, arguments, and the result returned
RetrieverA lookup against a vector store or indexThe query and the documents it returned
EmbeddingText turned into vectorsThe input text and the embedding model
RerankerRetrieved documents reordered by relevanceThe documents and their new order
AgentA top-level step that coordinates othersThe child spans (tool calls, retrievals, model calls) it set off
ChainA group of steps run as one unitThe ordered child spans it runs
GuardrailA safety or policy check on an input or outputWhat was checked and the verdict
EvaluatorAn eval that scores a span or traceThe metric and the score produced

Span attributes

Every span carries key-value attributes. Some come straight from OpenTelemetry (name, status, timing, parent), and traceAI adds LLM-specific keys on top: the span kind, the prompt and completion, the model, token counts, and cost.

  • Core (OpenTelemetry): name, kind, status, start and end time, parent span ID, trace ID
  • LLM (traceAI): model, input messages, output messages, prompt and completion tokens, cost
  • Retrieval: query, retrieved documents and their scores
  • Grouping: session.id, user.id, tags

Keep exploring

Was this page helpful?

Questions & Discussion