> ## Documentation Index
> Fetch the complete documentation index at: https://arize-ax.mintlify.site/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# OpenInference Span Kinds

> The eleven OpenInference span kinds — LLM, Tool, Agent, Chain, Retriever, and the rest — with the canonical attributes Arize AX expects on each.

A **span kind** is the category of operation a span represents. OpenTelemetry has its own span kinds for network calls (`SERVER`, `CLIENT`, `PRODUCER`, ...). OpenInference adds an AI-specific set: LLM, Tool, Agent, Chain, Retriever, and more.

Span kind is what drives the span-kind icons in the Arize AX UI. It's also how Arize AX knows which attributes to expect — an LLM span gets a different visual treatment from a tool span.

## Setting the Span Kind

Span kind is set via the `openinference.span.kind` attribute. **The value is ALL CAPS** — `"TOOL"`, not `"Tool"`. Casing matters: the canonical OpenInference Python enum is `OpenInferenceSpanKindValues.TOOL = "TOOL"`.

```python theme={null}
from openinference.semconv.trace import (
    SpanAttributes,
    OpenInferenceSpanKindValues,
)

span.set_attribute(
    SpanAttributes.OPENINFERENCE_SPAN_KIND,
    OpenInferenceSpanKindValues.TOOL.value,
)
```

<Info>
  **Arize AX UI behavior**: span-kind icons in the Arize AX trace tree come directly from the `openinference.span.kind` attribute. A span without that attribute renders with a blank icon. Auto-instrumentors set this for you — set it explicitly on every manual span.
</Info>

## The Eleven Span Kinds

OpenInference defines eleven span-kind values. The first four (LLM, Chain, Agent, Tool) are the ones you'll encounter most often:

|                                                                                                                                  Icon                                                                                                                                 | Span kind       | Description                                                                                          |
| :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :-------------- | :--------------------------------------------------------------------------------------------------- |
|                  <img src="https://storage.googleapis.com/arize-phoenix-assets/assets/images/arize-docs-images/concepts/otel/llm.png" width="32" alt="LLM span-kind icon" style={{ display: "inline-block", margin: 0, verticalAlign: "middle" }} />                  | **`LLM`**       | A call to a large language model.                                                                    |
|                <img src="https://storage.googleapis.com/arize-phoenix-assets/assets/images/arize-docs-images/concepts/otel/chain.png" width="32" alt="Chain span-kind icon" style={{ display: "inline-block", margin: 0, verticalAlign: "middle" }} />                | **`CHAIN`**     | A starting point or a link between different LLM application steps.                                  |
|                <img src="https://storage.googleapis.com/arize-phoenix-assets/assets/images/arize-docs-images/concepts/otel/agent.png" width="32" alt="Agent span-kind icon" style={{ display: "inline-block", margin: 0, verticalAlign: "middle" }} />                | **`AGENT`**     | A span that encompasses calls to LLMs and tools — typically the top-level wrapper for an agent loop. |
|                 <img src="https://storage.googleapis.com/arize-phoenix-assets/assets/images/arize-docs-images/concepts/otel/tool.png" width="32" alt="Tool span-kind icon" style={{ display: "inline-block", margin: 0, verticalAlign: "middle" }} />                 | **`TOOL`**      | A span that represents a call to an external tool, API, or function on behalf of an LLM.             |
|            <img src="https://storage.googleapis.com/arize-phoenix-assets/assets/images/arize-docs-images/concepts/otel/retriever.png" width="32" alt="Retriever span-kind icon" style={{ display: "inline-block", margin: 0, verticalAlign: "middle" }} />            | **`RETRIEVER`** | A data retrieval query for context from a datastore (e.g., a vector DB query).                       |
|            <img src="https://storage.googleapis.com/arize-phoenix-assets/assets/images/arize-docs-images/concepts/otel/embedding.png" width="32" alt="Embedding span-kind icon" style={{ display: "inline-block", margin: 0, verticalAlign: "middle" }} />            | **`EMBEDDING`** | An encoding of unstructured data into a vector.                                                      |
|             <img src="https://storage.googleapis.com/arize-phoenix-assets/assets/images/arize-docs-images/concepts/otel/reranker.png" width="32" alt="Reranker span-kind icon" style={{ display: "inline-block", margin: 0, verticalAlign: "middle" }} />             | **`RERANKER`**  | A relevance-based re-ordering of documents.                                                          |
|            <img src="https://storage.googleapis.com/arize-phoenix-assets/assets/images/arize-docs-images/concepts/otel/guardrail.png" width="32" alt="Guardrail span-kind icon" style={{ display: "inline-block", margin: 0, verticalAlign: "middle" }} />            | **`GUARDRAIL`** | A validation of LLM input or output for safety, policy, or compliance.                               |
| <img src="https://storage.googleapis.com/arize-phoenix-assets/assets/images/arize-docs-images/concepts/otel/unknown.png" width="32" alt="Evaluator span-kind icon (falls back to Unknown)" style={{ display: "inline-block", margin: 0, verticalAlign: "middle" }} /> | **`EVALUATOR`** | An evaluation process — the type, configuration, and results.                                        |
|   <img src="https://storage.googleapis.com/arize-phoenix-assets/assets/images/arize-docs-images/concepts/otel/unknown.png" width="32" alt="Prompt span-kind icon (falls back to Unknown)" style={{ display: "inline-block", margin: 0, verticalAlign: "middle" }} />  | **`PROMPT`**    | A span representing prompt construction or templating.                                               |
|              <img src="https://storage.googleapis.com/arize-phoenix-assets/assets/images/arize-docs-images/concepts/otel/unknown.png" width="32" alt="Unknown span-kind icon" style={{ display: "inline-block", margin: 0, verticalAlign: "middle" }} />              | **`UNKNOWN`**   | Default when no kind is set explicitly.                                                              |

## Common Attributes Across All Kinds

These attributes apply to every span kind. Auto-instrumentors set them automatically; set them yourself on every manual span:

| Attribute                 | Purpose                                                                                      |
| :------------------------ | :------------------------------------------------------------------------------------------- |
| `openinference.span.kind` | The span kind (ALL CAPS — e.g., `"LLM"`).                                                    |
| `input.value`             | The span's input. Free-form string or serialized JSON.                                       |
| `input.mime_type`         | `"text/plain"` (default) or `"application/json"`. The TS semconv also defines `"audio/wav"`. |
| `output.value`            | The span's output.                                                                           |
| `output.mime_type`        | `"text/plain"` (default) or `"application/json"`. The TS semconv also defines `"audio/wav"`. |
| `metadata`                | Arbitrary JSON-stringified metadata.                                                         |
| `session.id`              | Groups traces into a session. See [Sessions](/docs/ax/instrument/set-up-sessions).                |
| `user.id`                 | The user the request belongs to.                                                             |
| `tag.tags`                | A list of tags for filtering and search.                                                     |

<Info>
  **Arize AX UI behavior**: Arize AX reads the trace-level input and output from the **root span's** `input.value` and `output.value`. If your root span doesn't set them, the trace list view shows those columns as blank even if child spans have data.
</Info>

## Per-Kind Attributes

The four most-used kinds have a richer attribute set. The full list lives in the [OpenInference semantic conventions](https://github.com/Arize-ai/openinference/blob/main/python/openinference-semantic-conventions/src/openinference/semconv/trace/__init__.py) — the tables below are a summary.

### LLM

A call to a large language model.

| Attribute                                | Description                                                             |
| :--------------------------------------- | :---------------------------------------------------------------------- |
| `llm.model_name`                         | The model identifier (e.g., `gpt-4o`, `claude-3-5-sonnet-20241022`).    |
| `llm.provider`                           | The provider (e.g. `openai`, `anthropic`, `mistralai`).                 |
| `llm.system`                             | The underlying LLM system, when distinct from `provider`.               |
| `llm.input_messages`                     | The chat history sent to the model.                                     |
| `llm.output_messages`                    | The model's response messages.                                          |
| `llm.invocation_parameters`              | Temperature, top-p, max tokens, etc. (JSON-stringified).                |
| `llm.tools`                              | Tool definitions exposed to the model.                                  |
| `llm.function_call`                      | Function call returned by the model (legacy).                           |
| `llm.prompts`                            | The raw prompts sent (for completion-style APIs).                       |
| `llm.choices`                            | The choices returned by the model.                                      |
| `llm.prompt_template.template`           | The prompt template before variable substitution.                       |
| `llm.prompt_template.variables`          | The variables substituted into the template.                            |
| `llm.prompt_template.version`            | A version identifier for the template.                                  |
| `llm.token_count.total`                  | Total tokens.                                                           |
| `llm.token_count.prompt`                 | Prompt tokens.                                                          |
| `llm.token_count.completion`             | Completion tokens.                                                      |
| `llm.token_count.prompt_details.*`       | Breakdown of prompt tokens (cached, audio, etc.).                       |
| `llm.token_count.completion_details.*`   | Breakdown of completion tokens.                                         |
| `llm.cost.total`                         | Total cost.                                                             |
| `llm.cost.prompt`, `llm.cost.completion` | Per-direction cost.                                                     |
| `llm.cost.*_details.*`                   | Breakdown of cost by token type.                                        |
| `llm.finish_reason`                      | Why the model stopped generating (`stop`, `length`, `tool_calls`, ...). |

### TOOL

A call to an external tool, API, or function on behalf of an LLM.

| Attribute          | Description                                                                                 |
| :----------------- | :------------------------------------------------------------------------------------------ |
| `tool.id`          | A unique identifier for the tool invocation (often correlated with the LLM's tool-call ID). |
| `tool.name`        | The tool's name.                                                                            |
| `tool.description` | The tool's description (often matches what was shown to the model).                         |
| `tool.parameters`  | The JSON-serialized parameters passed to the tool.                                          |
| `tool.json_schema` | The tool's full JSON schema as exposed to the model.                                        |

<Info>
  Tool calls are one of the clearest cases where you must instrument manually when making an LLM calls directly, instead of via a framework. When you call OpenAI directly, the auto-instrumentor traces the model's *request* for a tool call — but the actual execution of the tool function happens in your Python code, where the instrumentor can't see it. Wrap the function in a tool span yourself.
</Info>

### AGENT

A span that encompasses calls to LLMs and tools — typically the top-level wrapper for an agent loop.

| Attribute              | Description                                                 |
| :--------------------- | :---------------------------------------------------------- |
| `agent.name`           | The agent's name.                                           |
| `graph.node.id`        | Node ID for graph-based agent frameworks (e.g., LangGraph). |
| `graph.node.name`      | Node display name.                                          |
| `graph.node.parent_id` | Parent node ID, for graph traversal visualization.          |

### CHAIN

A starting point or a link between different application steps. Use a chain span to group pre/post-processing logic that doesn't fit any of the more specific kinds.

Chain spans use only the common attributes — `input.value`, `output.value`, `metadata`, `session.id`, etc.

### Other Kinds

The remaining seven span kinds — RETRIEVER, EMBEDDING, RERANKER, GUARDRAIL, EVALUATOR, PROMPT, UNKNOWN — each have their own attribute schemas. Consult the [canonical source](https://github.com/Arize-ai/openinference/blob/main/python/openinference-semantic-conventions/src/openinference/semconv/trace/__init__.py) for the full per-kind attribute list.

Some auto-instrumentors also emit additional kinds that aren't (yet) in the canonical enum — see [Voice Extensions](#voice-extensions) below for the AUDIO and USER kinds used by the realtime/voice tracing path.

## Voice Extensions

The [OpenAI Agents realtime instrumentor](https://github.com/Arize-ai/openinference/tree/main/python/instrumentation/openinference-instrumentation-openai-agents) introduces two additional span-kind values for voice tracing — `AUDIO` and `USER` — that aren't (yet) in the canonical `OpenInferenceSpanKindValues` enum. They're set as string-literal values on `openinference.span.kind`, and Arize AX renders them with audio-aware UI: a play button, waveform, and the input/output transcripts inline.

A single conversational turn in a Realtime API session produces this span tree:

```
AUDIO  "conversation.turn"     ← parent; aggregated transcripts, llm.model_name, llm.invocation_parameters
├─ USER "user"                 ← input.audio.url, input.audio.transcript
├─ LLM  "assistant"            ← output.audio.url, output.audio.transcript, token counts, time_to_first_token_ms
│  └─ TOOL "<tool_name>"        ← one per function call within the turn
└─ ...                          ← additional siblings for split input or tool round-trips
```

### AUDIO

The parent span for one conversational turn. Wraps the USER input span, the assistant LLM span, and any TOOL spans the turn calls into.

| Attribute                   | Description                                               |
| :-------------------------- | :-------------------------------------------------------- |
| `input.audio.transcript`    | Aggregated transcript of the user input for this turn.    |
| `output.audio.transcript`   | Aggregated transcript of the assistant response.          |
| `llm.model_name`            | The Realtime model used (e.g. `gpt-4o-realtime-preview`). |
| `llm.invocation_parameters` | Session config as JSON.                                   |

### USER

A single user utterance inside an AUDIO turn — emitted when server-side VAD detects the user has finished speaking.

| Attribute                | Description                                                                                                                                          |
| :----------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------- |
| `input.audio.url`        | URL of the user audio for this utterance. A `data:audio/wav;base64,...` URI by default, or any HTTPS URL if an external storage backend is wired in. |
| `input.audio.mime_type`  | MIME type of the input audio (e.g. `audio/wav`).                                                                                                     |
| `input.audio.transcript` | Transcript of just this utterance.                                                                                                                   |

The corresponding **assistant** span inside an AUDIO turn uses the canonical `LLM` kind, with the standard LLM attributes plus a few realtime-specific extras:

| Attribute                                  | Description                                                                                                                                                   |
| :----------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `output.audio.url`                         | URL of the assistant audio for this response.                                                                                                                 |
| `output.audio.mime_type`                   | MIME type of the output audio.                                                                                                                                |
| `output.audio.transcript`                  | Transcript of the assistant response.                                                                                                                         |
| `time_to_first_token_ms`                   | Latency from the user's audio commit to the first byte of the model's audio response — the metric that matters most for perceived voice-agent responsiveness. |
| `llm.token_count.prompt_details.audio`     | Audio input tokens consumed.                                                                                                                                  |
| `llm.token_count.completion_details.audio` | Audio output tokens generated.                                                                                                                                |

<Info>
  AUDIO and USER are emitted as string-literal `openinference.span.kind` values rather than via the `OpenInferenceSpanKindValues` enum. Treat them as instrumentor-introduced extensions used by the realtime/voice tracing path, pending promotion into the canonical enum.
</Info>

For an end-to-end example that produces these spans and runs evaluations against the captured audio, see the [voice tracing guide](/docs/ax/cookbooks/evaluate/tracing-and-evaluating-audio).

## Multimodal Message Content

Inputs and outputs in modern AI applications aren't always plain text. OpenInference defines a `message_content` attribute family for multimodal content:

| Attribute                           | Description                                                                                       |
| :---------------------------------- | :------------------------------------------------------------------------------------------------ |
| `message_content.type`              | One of `text`, `image`, `audio`, `reasoning`, `tool_use`.                                         |
| `message_content.text`              | The text payload (when `type` is `text` or `reasoning`).                                          |
| `message_content.image`             | The image payload — URL or inline base64 (when `type=image`).                                     |
| `message_content.id`                | Provider-assigned identifier for the message content item (e.g., OpenAI Responses reasoning IDs). |
| `message_content.signature`         | Opaque vendor-issued signature (e.g., Gemini `thoughtSignature`).                                 |
| `message_content.data`              | Opaque vendor-issued data (e.g., Anthropic `redacted_thinking.data`).                             |
| `message_content.encrypted_content` | Encrypted payload for sensitive content (e.g., OpenAI `encrypted_content`).                       |

For audio specifically, OpenInference exposes a separate set of attributes — `audio.url`, `audio.mime_type`, `audio.transcript` — see the [canonical source](https://github.com/Arize-ai/openinference/blob/main/python/openinference-semantic-conventions/src/openinference/semconv/trace/__init__.py).

## GenAI semantic convention mapping

Arize AX ingests spans instrumented with the OpenTelemetry GenAI conventions (`gen_ai.*`) and normalizes them into the OpenInference attributes documented above, so they render with the correct span kind, messages, token counts, tool I/O, and retrieved documents. Raw `gen_ai.*` attributes are preserved on the span, and any explicit OpenInference attribute already present always wins. For the conceptual overview and the normalization rules, see [How Arize AX ingests GenAI spans](/docs/ax/concepts/otel-openinference/semantic-conventions#how-arize-ax-ingests-genai-spans).

The tables below are the exact mapping applied at ingestion.

### Span kind

Arize AX derives the OpenInference span kind from `gen_ai.operation.name`:

| `gen_ai.operation.name`                       | OpenInference span kind             |
| :-------------------------------------------- | :---------------------------------- |
| `chat`, `text_completion`, `generate_content` | `LLM`                               |
| `embeddings`                                  | `EMBEDDING`                         |
| `execute_tool`                                | `TOOL`                              |
| `retrieval`                                   | `RETRIEVER`                         |
| `invoke_agent`, `create_agent`                | `AGENT`                             |
| `invoke_workflow`                             | `CHAIN`                             |
| any other value                               | Unclassified (span kind left unset) |

When `gen_ai.operation.name` is absent, Arize AX falls back to inferring the kind from which attributes are present:

| Attributes present                                                           | Inferred span kind |
| :--------------------------------------------------------------------------- | :----------------- |
| `gen_ai.tool.call.id` or `gen_ai.tool.name`                                  | `TOOL`             |
| `gen_ai.retrieval.documents` or `gen_ai.retrieval.query.text`                | `RETRIEVER`        |
| `gen_ai.embeddings.dimension.count`                                          | `EMBEDDING`        |
| `gen_ai.input.messages`, `gen_ai.output.messages`, or `gen_ai.request.model` | `LLM`              |

### Core attributes

| GenAI attribute                                        | OpenInference attribute                                                           |
| :----------------------------------------------------- | :-------------------------------------------------------------------------------- |
| `gen_ai.provider.name` (falls back to `gen_ai.system`) | `llm.provider` and `llm.system` — see [Provider and system](#provider-and-system) |
| `gen_ai.conversation.id`                               | `session.id`                                                                      |
| `gen_ai.request.model`                                 | `llm.model_name` (`embedding.model_name` on `EMBEDDING` spans)                    |

### Request parameters

These are collected into a single JSON `llm.invocation_parameters` object (`embedding.invocation_parameters` on `EMBEDDING` spans):

| GenAI attribute                         | Key in invocation parameters                                      |
| :-------------------------------------- | :---------------------------------------------------------------- |
| `gen_ai.request.temperature`            | `temperature`                                                     |
| `gen_ai.request.top_p`                  | `top_p`                                                           |
| `gen_ai.request.top_k`                  | `top_k`                                                           |
| `gen_ai.request.max_tokens`             | `max_tokens`                                                      |
| `gen_ai.request.frequency_penalty`      | `frequency_penalty`                                               |
| `gen_ai.request.presence_penalty`       | `presence_penalty`                                                |
| `gen_ai.request.seed`                   | `seed`                                                            |
| `gen_ai.request.stream`                 | `stream`                                                          |
| `gen_ai.request.choice_count`           | `n`                                                               |
| `gen_ai.request.stop_sequences`         | `stop`                                                            |
| `gen_ai.request.encoding_formats`       | `encoding_format` (single value) or `encoding_formats` (multiple) |
| `gen_ai.output.type` (`json` or `text`) | `response_format`                                                 |

### Token usage

| GenAI attribute                            | OpenInference attribute                      |
| :----------------------------------------- | :------------------------------------------- |
| `gen_ai.usage.input_tokens`                | `llm.token_count.prompt`                     |
| `gen_ai.usage.output_tokens`               | `llm.token_count.completion`                 |
| `input_tokens` + `output_tokens`           | `llm.token_count.total`                      |
| `gen_ai.usage.cache_read_input_tokens`     | `llm.token_count.prompt_details.cache_read`  |
| `gen_ai.usage.cache_creation_input_tokens` | `llm.token_count.prompt_details.cache_write` |

### Messages and tool definitions

Messages are flattened into the standard indexed OpenInference message attributes, including text content, multimodal content parts, tool calls, and tool-call responses.

| GenAI attribute              | OpenInference attribute                                 |
| :--------------------------- | :------------------------------------------------------ |
| `gen_ai.system_instructions` | Prepended to `llm.input_messages` as a `system` message |
| `gen_ai.input.messages`      | `llm.input_messages.*`                                  |
| `gen_ai.output.messages`     | `llm.output_messages.*`                                 |
| `gen_ai.tool.definitions`    | `llm.tools.{i}.tool.json_schema`                        |

### LLM response

On `LLM` spans:

| GenAI attribute                                  | OpenInference attribute                      |
| :----------------------------------------------- | :------------------------------------------- |
| `gen_ai.response.finish_reasons` (single value)  | `llm.finish_reason`                          |
| `gen_ai.response.id` and `gen_ai.response.model` | `output.value` (JSON) and `output.mime_type` |

<Note>
  When `gen_ai.response.finish_reasons` contains more than one entry, none is written. OpenInference has a single finish-reason field, so Arize AX leaves it unset rather than choosing one arbitrarily.
</Note>

### Tool spans

On `TOOL` spans (`execute_tool`):

| GenAI attribute              | OpenInference attribute                                                                                         |
| :--------------------------- | :-------------------------------------------------------------------------------------------------------------- |
| `gen_ai.tool.name`           | `tool.name`                                                                                                     |
| `gen_ai.tool.description`    | `tool.description`                                                                                              |
| `gen_ai.tool.call.id`        | `tool.id`                                                                                                       |
| `gen_ai.tool.call.arguments` | `tool.parameters` and `input.value` (with `input.mime_type` set to `application/json` for structured arguments) |
| `gen_ai.tool.call.result`    | `output.value` (with `output.mime_type` for structured results)                                                 |

### Retriever spans

On `RETRIEVER` spans (`retrieval`):

| GenAI attribute                          | OpenInference attribute                                    |
| :--------------------------------------- | :--------------------------------------------------------- |
| `gen_ai.retrieval.query.text`            | `input.value` (with `input.mime_type` set to `text/plain`) |
| `gen_ai.retrieval.documents[i].id`       | `retrieval.documents.{i}.document.id`                      |
| `gen_ai.retrieval.documents[i].score`    | `retrieval.documents.{i}.document.score`                   |
| `gen_ai.retrieval.documents[i].content`  | `retrieval.documents.{i}.document.content`                 |
| `gen_ai.retrieval.documents[i].metadata` | `retrieval.documents.{i}.document.metadata`                |

### Provider and system

`gen_ai.provider.name` (or `gen_ai.system` when the provider name is absent) is mapped to the OpenInference `llm.provider` and `llm.system` attributes:

| GenAI provider or system   | `llm.provider` | `llm.system`             |
| :------------------------- | :------------- | :----------------------- |
| `openai`                   | `openai`       | `openai`                 |
| `anthropic`                | `anthropic`    | `anthropic`              |
| `cohere`                   | `cohere`       | `cohere`                 |
| `mistral_ai`               | `mistralai`    | `mistralai`              |
| `deepseek`                 | `deepseek`     | —                        |
| `groq`                     | `groq`         | —                        |
| `perplexity`               | `perplexity`   | —                        |
| `x_ai`                     | `xai`          | —                        |
| `azure.ai.openai`          | `azure`        | `openai`                 |
| `azure.ai.inference`       | `azure`        | —                        |
| `aws.bedrock`              | `aws`          | —                        |
| `gcp.vertex_ai`            | `google`       | `vertexai`               |
| `gcp.gen_ai`, `gcp.gemini` | `google`       | —                        |
| any other value            | —              | passed through unchanged |

<Note>
  **Cost tracking uses a finer-grained alias.** The `llm.provider` value above is intentionally collapsed — for example, both Vertex AI and Gemini map to `google`. For per-model cost lookups, Arize AX resolves a separate cost provider from the raw `gen_ai.provider.name`: `aws` becomes `bedrock`, `gcp.vertex_ai` becomes `vertex_ai`, and `gcp.gemini` becomes `gemini`. This lets GenAI spans match the correct pricing even though the displayed provider stays collapsed.
</Note>

## How a Trace Tree Uses Span Kinds

A typical agent trace nests span kinds inside each other:

```
AGENT span                         (agent.name = "research-assistant")
├── LLM span                       "decide next step"
├── TOOL span                      "search knowledge base"
│   └── RETRIEVER span             "vector search"
│       └── EMBEDDING span         "embed query"
└── LLM span                       "answer with retrieved docs"
```

Each level has its own kind, its own attributes, and its own rendering in the Arize AX UI. The combination is what makes agent behavior legible.

![Trace in Arize AX from the OpenAI Agents SDK example, showing agent, chain, tool, and LLM spans nested together](https://storage.googleapis.com/arize-phoenix-assets/assets/images/arize-docs-images/concepts/otel/agent-trace.png)

***

### Next step

You know what attributes go on a span. Next, the three ways to actually emit spans — auto, manual, and hybrid:

<Card title="Next: Instrumentation Approaches" icon="arrow-right" href="/docs/ax/concepts/otel-openinference/instrumentation-approaches" />
