---
title: "AI Agents Module"
url: https://develop.sentry.dev/sdk/telemetry/traces/modules/ai-agents/
---

# AI Agents Module | Sentry Docs

The AI Agents module is agnostic to the library used. SDKs instrument supported AI frameworks and provider clients automatically. Users instrument anything else manually with the same span conventions.

This page lists every attribute SDKs send on AI spans. Send all of them. The descriptions call out the few cases where an attribute can be omitted. Don't send attributes that aren't listed here: no part of the product reads them, or Relay derives them during ingestion.

##### Hint

The [Sentry Conventions](https://github.com/getsentry/sentry-conventions/) are the single source of truth for every `gen_ai.*` attribute and span op. They follow the [OpenTelemetry Semantic Conventions for Generative AI](https://github.com/open-telemetry/semantic-conventions-genai/blob/main/docs/gen-ai/README.md) where possible. Sentry doesn't support span events, so message content that OpenTelemetry stores in events is stored as span attributes instead.

## [Span Hierarchy](https://develop.sentry.dev/sdk/telemetry/traces/modules/ai-agents.md#span-hierarchy)

```bash
── invoke_agent Weather Agent       (gen_ai.invoke_agent)
   ├── search_memory                (gen_ai.memory.client)
   ├── chat gpt-4o                  (gen_ai.chat)          ← 1st model call
   ├── execute_tool get_weather     (gen_ai.execute_tool)  ← tool run
   ├── chat gpt-4o                  (gen_ai.chat)          ← 2nd model call
   └── decide typesafe/jev-1.13     (gen_ai.decide)        ← decision on the reply
```

* One `gen_ai.invoke_agent` span covers the whole agent run.
* Every model request gets its own AI client span, and every tool execution its own `gen_ai.execute_tool` span. Both are children of the agent span.
* Model and tool spans are siblings. End the model span before running the tool it requested, so model latency doesn't include tool time.
* When an agent hands control to another agent, a `gen_ai.handoff` span marks the transfer, followed by the receiving agent's own `gen_ai.invoke_agent` span.
* Standalone model calls create an AI client span without an agent parent.
* For streamed responses, keep the AI client span open until the stream completes or fails.

## [Common Rules](https://develop.sentry.dev/sdk/telemetry/traces/modules/ai-agents.md#common-rules)

These apply to every span on this page.

* Set `gen_ai.operation.name` on every span. Sentry uses it to recognize AI spans and to tell decision, memory, and embeddings spans apart from ordinary model calls. Relay derives `gen_ai.operation.type` from it, and every Agents insights view filters on that type.
* Set `gen_ai.operation.name`, `gen_ai.request.model`, `gen_ai.agent.name`, and `gen_ai.tool.name` when the span starts, so sampling decisions can use them.
* On failure, set the span status to error. Sentry counts errors from the span status alone.
* When the application has a conversation, chat, or session ID, set `gen_ai.conversation.id` to it on every `gen_ai.*` span. Conversations are keyed on this attribute, so spans without it don't appear in any conversation. Omit it when there is no such ID; never derive one from a trace or request ID.
* Send structured values (messages, tool definitions, records) as JSON strings. Span attributes only accept primitives, so set `'[{"foo": "bar"}]'`, not `[{"foo": "bar"}]`.
* Gate prompt, response, and tool content with the `genAI` option of the [data collection](https://develop.sentry.dev/sdk/foundations/client/data-collection.md) configuration. When collection is off, omit those attributes. Metadata such as model names, token counts, and tool names is always sent.

### [Derived by Relay](https://develop.sentry.dev/sdk/telemetry/traces/modules/ai-agents.md#derived-by-relay)

Don't send these. Relay computes them during ingestion and overwrites what the SDK sends:

* `gen_ai.operation.type`
* `gen_ai.usage.total_tokens`
* `gen_ai.response.tokens_per_second`
* `gen_ai.context.window_size` and `gen_ai.context.utilization`
* `gen_ai.cost.*`, calculated from the model and token counts. Relay keeps an existing `gen_ai.cost.total_tokens` so users can set costs for self-hosted or unpriced models, but SDKs don't calculate costs themselves.

## [Invoke Agent Span](https://develop.sentry.dev/sdk/telemetry/traces/modules/ai-agents.md#invoke-agent-span)

Describes an agent run, which can include multiple model calls, tool executions, and other work.

* Span `op`: `"gen_ai.invoke_agent"`
* Span `name`: `"invoke_agent {gen_ai.agent.name}"` (e.g. `"invoke_agent Weather Agent"`). If the library doesn't name agents, use a user-provided identifier for the invocation instead, such as `functionId` in the Vercel AI SDK.
* `gen_ai.operation.name`: `"invoke_agent"`

| Attribute                 | Type   | Description                                                                                                                                                               | Example                                                                    |
| ------------------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------- |
| `gen_ai.agent.name`       | string | The agent's name. Powers the agent filter and grouping in Agents insights, the agent flow in Conversations, and span titles.                                              | `"Weather Agent"`                                                          |
| `gen_ai.input.messages`   | string | The messages the agent received. Sentry uses them for the conversation preview and transcript when the child model spans don't carry messages. **\[0]**                   | `'[{"role": "user", "parts": [{"type": "text", "content": "..."}]}]'`      |
| `gen_ai.output.messages`  | string | The agent's final response. Used the same way as the input messages. **\[0]**                                                                                             | `'[{"role": "assistant", "parts": [{"type": "text", "content": "..."}]}]'` |
| `gen_ai.tool.definitions` | string | The tools available to the agent, as a JSON array of name and description objects. Shown as **Available Tools** in the span details. Omit it when the agent has no tools. | `'[{"name": "get_weather", "description": "..."}]'`                        |

## [AI Client Span](https://develop.sentry.dev/sdk/telemetry/traces/modules/ai-agents.md#ai-client-span)

Describes a single request to a model.

* Span `op`: `"gen_ai.{gen_ai.operation.name}"` (e.g. `"gen_ai.chat"`)
* Span `name`: `"{gen_ai.operation.name} {gen_ai.request.model}"` (e.g. `"chat gpt-4o"`)
* `gen_ai.operation.name`: `"chat"`, `"embeddings"`, `"generate_content"`, or `"text_completion"`

| Attribute                                  | Type   | Description                                                                                                                                                                                       | Example                                                                    |
| ------------------------------------------ | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------- |
| `gen_ai.request.model`                     | string | The model the SDK asked for, as the raw provider string. Drives model breakdowns, cost calculation, and model icons. Without a model, Relay doesn't classify the span as a model call.            | `"gpt-4o"`                                                                 |
| `gen_ai.response.model`                    | string | The concrete model that responded, which Models views group by. Omit it when the provider doesn't report one; Relay copies `gen_ai.request.model` instead.                                        | `"gpt-4o-2024-08-06"`                                                      |
| `gen_ai.usage.input_tokens`                | int    | Total input tokens, including cache-read and cache-write tokens. Drives token widgets and cost. **\[1]**                                                                                          | `60`                                                                       |
| `gen_ai.usage.output_tokens`               | int    | Total output tokens, including reasoning tokens. Drives token widgets and cost. **\[1]**                                                                                                          | `130`                                                                      |
| `gen_ai.usage.cache_read.input_tokens`     | int    | The part of the input tokens served from the provider's prompt cache. Shown in the token breakdown and priced at the cache-read rate. Omit it when the provider doesn't report it.                | `50`                                                                       |
| `gen_ai.usage.cache_creation.input_tokens` | int    | The part of the input tokens written to the provider's prompt cache. Priced at the cache-write rate. Omit it when the provider doesn't report it.                                                 | `20`                                                                       |
| `gen_ai.usage.reasoning.output_tokens`     | int    | The part of the output tokens spent on reasoning or thinking. Shown in the token breakdown and priced at the reasoning rate. Omit it when the provider doesn't report it.                         | `30`                                                                       |
| `gen_ai.agent.name`                        | string | The agent that made the request, so model usage can be attributed per agent. Sentry copies it from an ancestor agent span when it's missing, but only within the same segment, so send it anyway. | `"Weather Agent"`                                                          |
| `gen_ai.input.messages`                    | string | The messages sent to the model. Shown in the conversation transcript and the span's **Input** tab, and used to generate conversation titles. **\[0]** **\[2]**                                    | `'[{"role": "user", "parts": [{"type": "text", "content": "..."}]}]'`      |
| `gen_ai.output.messages`                   | string | The model's response, including any tool calls it requested. Shown in the conversation transcript and the span's **Output** tab. **\[0]**                                                         | `'[{"role": "assistant", "parts": [{"type": "text", "content": "..."}]}]'` |
| `gen_ai.system_instructions`               | string | The system prompt. Shown in the span's **Input** tab. Send system instructions here, not inside `gen_ai.input.messages`. Omit it when the request has none.                                       | `"You are a helpful assistant."`                                           |
| `gen_ai.request.reasoning.level`           | string | The reasoning or thinking effort the user configured. Shown as **Reasoning Level** in the span details. Values vary by provider. Omit it when the user didn't set one.                            | `"medium"`                                                                 |
| `gen_ai.embeddings.input`                  | string | The text that was embedded. Only for `embeddings` spans, where it's what the conversation transcript shows; without it, the embeddings row isn't shown.                                           | `"What's the weather in Paris?"`                                           |

## [Execute Tool Span](https://develop.sentry.dev/sdk/telemetry/traces/modules/ai-agents.md#execute-tool-span)

Describes a tool execution.

* Span `op`: `"gen_ai.execute_tool"`
* Span `name`: `"execute_tool {gen_ai.tool.name}"` (e.g. `"execute_tool get_weather"`)
* `gen_ai.operation.name`: `"execute_tool"`

| Attribute                    | Type   | Description                                                                                                                  | Example                   |
| ---------------------------- | ------ | ---------------------------------------------------------------------------------------------------------------------------- | ------------------------- |
| `gen_ai.tool.name`           | string | The tool's name. Powers Tools views and the tool call in the conversation transcript; without it, the tool call isn't shown. | `"get_weather"`           |
| `gen_ai.agent.name`          | string | The agent that ran the tool, so tool usage can be attributed per agent.                                                      | `"Weather Agent"`         |
| `gen_ai.tool.call.arguments` | string | The arguments the tool was called with, as a JSON string. Shown in the span's **Input** tab and as the timeline preview.     | `'{"location": "Paris"}'` |
| `gen_ai.tool.call.result`    | string | What the tool returned. Shown in the span's **Output** tab, and its size is shown in the timeline.                           | `'{"temperature": 18}'`   |

## [Handoff Span](https://develop.sentry.dev/sdk/telemetry/traces/modules/ai-agents.md#handoff-span)

Describes one agent handing control to another, as in multi-agent frameworks like the OpenAI Agents SDK. Sentry shows handoffs as their own span type in the trace and timeline.

* Span `op`: `"gen_ai.handoff"`. Relay uses the op to classify the span as a handoff.
* Span `name`: `"handoff from {source agent name} to {target agent name}"` (e.g. `"handoff from Triage Agent to Billing Agent"`)
* `gen_ai.operation.name`: `"handoff"`

Handoff spans carry no other attributes besides `gen_ai.conversation.id` (see [Common Rules](https://develop.sentry.dev/sdk/telemetry/traces/modules/ai-agents.md#common-rules)). The receiving agent's work goes in its own `gen_ai.invoke_agent` span.

## [Decision Span](https://develop.sentry.dev/sdk/telemetry/traces/modules/ai-agents.md#decision-span)

Describes a decision on generated content against a set of questions, such as grading a model's reply for accuracy or classifying a support message, for example from TypeSafe Jev. Sentry shows decisions as their own rows in the conversation transcript and timeline, with dedicated **Input** and **Output** views.

* Span `op`: `"gen_ai.decide"`
* Span `name`: `"decide {gen_ai.request.model}"` (e.g. `"decide typesafe/jev-1.13"`)
* `gen_ai.operation.name`: `"decide"`. Decisions share the `ai_client` operation type with model calls, so this attribute is what tells them apart.

| Attribute                    | Type   | Description                                                                                                                                                                                                                                                                             | Example                                                                                                    |
| ---------------------------- | ------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- |
| `gen_ai.request.model`       | string | The evaluation model the SDK asked for. Drives model breakdowns, cost, and the span title. Without a model, Relay doesn't classify the span as `ai_client`.                                                                                                                             | `"typesafe/jev-1.13"`                                                                                      |
| `gen_ai.response.model`      | string | The concrete model that responded. Omit it when the provider doesn't report one.                                                                                                                                                                                                        | `"typesafe/jev-1.13-20260917"`                                                                             |
| `gen_ai.usage.input_tokens`  | int    | Input tokens used by the evaluation. Drives token widgets and cost.                                                                                                                                                                                                                     | `154`                                                                                                      |
| `gen_ai.usage.output_tokens` | int    | Output tokens used by the evaluation. Drives token widgets and cost.                                                                                                                                                                                                                    | `28`                                                                                                       |
| `gen_ai.agent.name`          | string | The agent the decision ran under. Omit it when the decision runs outside an agent.                                                                                                                                                                                                      | `"Support Agent"`                                                                                          |
| `gen_ai.input.messages`      | string | The content being evaluated and the questions asked about it, as a one-element array holding one object. Shown in the decision **Input** view. See [Decision Input Messages](https://develop.sentry.dev/sdk/telemetry/traces/modules/ai-agents.md#decision-input-messages).             | `'[{"state": "...", "questions": {"accuracy": {"type": "score", "criteria": ["incorrect", "correct"]}}}]'` |
| `gen_ai.output.messages`     | string | The answers, as a one-element array holding one object. Shown in the decision **Output** view and as the one-line result in the transcript and timeline. See [Decision Output Messages](https://develop.sentry.dev/sdk/telemetry/traces/modules/ai-agents.md#decision-output-messages). | `'[{"answers": {"accuracy": {"type": "score", "score": 1}}}]'`                                             |

### [Decision Input Messages](https://develop.sentry.dev/sdk/telemetry/traces/modules/ai-agents.md#decision-input-messages)

`gen_ai.input.messages` holds a one-element array: `[{"state", "questions"}]`. Copy `state` and the questions as the caller passed them. If the provider uses a different layout, convert it to this layout. `<name>` is the question key.

| Field                           | Type                   | Required                 | Description                                                                                                                                                             | Example                                                 |
| ------------------------------- | ---------------------- | ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------- |
| `state`                         | any                    | Yes                      | The content being evaluated: a string, an object, or a message array, as the caller passed it. Replace binary data, including `data:` URLs, with `"[Blob substitute]"`. | `"I was charged twice and want a refund."`              |
| `questions`                     | object                 | Yes                      | The questions, keyed by question name.                                                                                                                                  | `{"urgency": {...}}`                                    |
| `questions.<name>.type`         | string                 | Yes                      | `"boolean"`, `"predicate"`, `"noul"`, `"choice"`, or `"score"`. Copy other values as they are.                                                                          | `"choice"`                                              |
| `questions.<name>.instructions` | string                 | Yes                      | The question text.                                                                                                                                                      | `"Which team should handle this?"`                      |
| `questions.<name>.criteria`     | object or string array | For `choice` and `score` | For `choice`, an object that maps each option to its description. For `score`, an ordered array of labels. The index is the score.                                      | `{"billing": "Charges and refunds"}`, `["low", "high"]` |
| `questions.<name>.descriptions` | object                 | No                       | Only for `score`. Maps a score index (as a string) to the description of that level. Omit the levels that have no description.                                          | `{"1": "Blocks the customer"}`                          |

### [Decision Output Messages](https://develop.sentry.dev/sdk/telemetry/traces/modules/ai-agents.md#decision-output-messages)

`gen_ai.output.messages` holds a one-element array: `[{"answers"}]`. `answers` uses the same keys as `questions`, and each answer has a `type` that matches its question.

| Field                          | Type   | Required                      | Description                                                                                           | Example                                |
| ------------------------------ | ------ | ----------------------------- | ----------------------------------------------------------------------------------------------------- | -------------------------------------- |
| `answers`                      | object | Yes                           | The answers, keyed by question name.                                                                  | `{"urgency": {...}}`                   |
| `answers.<name>.type`          | string | Yes                           | The type of the question.                                                                             | `"score"`                              |
| `answers.<name>.probability`   | number | For `boolean` and `predicate` | The probability that the answer is yes. Shown as Yes when it's 0.5 or higher.                         | `0.97`                                 |
| `answers.<name>.noul`          | number | For `noul`                    | The probability that the answer is yes. Jev's variant of `boolean`.                                   | `0.98`                                 |
| `answers.<name>.choice`        | string | For `choice`                  | The selected option.                                                                                  | `"billing"`                            |
| `answers.<name>.score`         | number | For `score`                   | The score. It can be a fraction between two levels.                                                   | `1.58`                                 |
| `answers.<name>.probabilities` | object | No                            | Only for `choice` and `score`. Maps each option, or each score index as a string, to its probability. | `{"billing": 0.64, "technical": 0.36}` |
| `answers.<name>.legend`        | object | No                            | Only for `score`. Maps each score index (as a string) to its label.                                   | `{"0": "low", "1": "high"}`            |
| `answers.<name>.confidence`    | number | No                            | Only for `choice` and `score`. The confidence of the answer.                                          | `0.28`                                 |

Omit optional fields when the provider doesn't report them. A field with the wrong type makes that question or answer unreadable, and Sentry shows it raw.

## [Extraction Span](https://develop.sentry.dev/sdk/telemetry/traces/modules/ai-agents.md#extraction-span)

Describes an extraction of typed fields from an input, such as the total and due date of an invoice, for example from Cloudflare Clairic. The model can extract values that are in the input, compute values from them, and estimate or forecast values that the input doesn't determine.

* Span `op`: `"gen_ai.extract"`
* Span `name`: `"extract {gen_ai.request.model}"` (e.g. `"extract @cf/cloudflare/clairic"`)
* `gen_ai.operation.name`: `"extract"`

| Attribute                    | Type   | Description                                                                                                                                                                                                      | Example                                                                                         |
| ---------------------------- | ------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- |
| `gen_ai.request.model`       | string | The extraction model the SDK asked for. Drives model breakdowns, cost, and the span title.                                                                                                                       | `"@cf/cloudflare/clairic"`                                                                      |
| `gen_ai.response.model`      | string | The concrete model that responded. Omit it when the provider doesn't report one.                                                                                                                                 | `"clairic"`                                                                                     |
| `gen_ai.usage.input_tokens`  | int    | Input tokens used by the extraction, including all passes. Drives token widgets and cost.                                                                                                                        | `655`                                                                                           |
| `gen_ai.usage.output_tokens` | int    | Output tokens used by the extraction. Drives token widgets and cost.                                                                                                                                             | `0`                                                                                             |
| `gen_ai.agent.name`          | string | The agent the extraction ran under. Omit it when the extraction runs outside an agent.                                                                                                                           | `"Billing Agent"`                                                                               |
| `gen_ai.input.messages`      | string | The input and the field specs, as a one-element array holding the request body. See [Extraction Input Messages](https://develop.sentry.dev/sdk/telemetry/traces/modules/ai-agents.md#extraction-input-messages). | `'[{"state": "Invoice INV-2291 ...", "fields": {"total": {"type": "number", "unit": "USD"}}}]'` |
| `gen_ai.output.messages`     | string | The answers, as a one-element array holding the response body. See [Extraction Output Messages](https://develop.sentry.dev/sdk/telemetry/traces/modules/ai-agents.md#extraction-output-messages).                | `'[{"answers": {"total": {"type": "number", "value": 1080}}, "truncated": false}]'`             |

Don't send the other usage counts that the provider reports (such as Clairic's `state_tokens`, `image_tokens`, `schema_tokens`, and `passes`). `gen_ai.usage.input_tokens` already includes them.

### [Extraction Input Messages](https://develop.sentry.dev/sdk/telemetry/traces/modules/ai-agents.md#extraction-input-messages)

`gen_ai.input.messages` holds a one-element array with the request body as the caller passed it, without `model`. Don't change the values, and leave out the keys that the caller didn't set. `<name>` is the field key.

| Field                          | Type             | Required                      | Description                                                                                                    | Example                                                |
| ------------------------------ | ---------------- | ----------------------------- | -------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------ |
| `state`                        | string           | When there are no `images`    | The text the model reads. Text answers are spans of this string.                                               | `"Invoice INV-2291 from Northwind Supply, issued ..."` |
| `fields`                       | object           | Yes                           | The field specs, keyed by field name. Names match `^[A-Za-z_][A-Za-z0-9_.\-]{0,63}$`.                          | `{"total": {...}}`                                     |
| `fields.<name>.type`           | string           | Yes                           | `"text"`, `"number"`, `"date"`, `"time"`, `"datetime"`, `"duration"`, `"list"`, `"estimate"`, or `"forecast"`. | `"number"`                                             |
| `fields.<name>.instructions`   | string           | No                            | What the field must contain.                                                                                   | `"Amount due including shipping"`                      |
| `fields.<name>.unit`           | string           | No                            | The unit label. For `duration`, one of `seconds`, `minutes`, `hours`, `days`, `weeks`, `months`, or `years`.   | `"USD"`                                                |
| `fields.<name>.integer`        | boolean          | No                            | Only for `number` and `duration`. Makes the value a whole number.                                              | `true`                                                 |
| `fields.<name>.decimals`       | int              | No                            | Only for `number`, `duration`, and number lists. From 0 to 6.                                                  | `2`                                                    |
| `fields.<name>.items`          | string           | For `list`                    | The type of the list items: `"text"`, `"number"`, or `"date"`.                                                 | `"text"`                                               |
| `fields.<name>.min`            | number or string | For `estimate` and `forecast` | The lowest value, or an ISO date for date estimates.                                                           | `0`                                                    |
| `fields.<name>.max`            | number or string | For `estimate` and `forecast` | The highest value, or an ISO date for date estimates.                                                          | `120`                                                  |
| `fields.<name>.step`           | number or string | No                            | The grid of an estimate: a positive number (days for date estimates), `"month"`, or `"year"`.                  | `1`                                                    |
| `fields.<name>.scale`          | string           | No                            | `"linear"` or `"log"`.                                                                                         | `"linear"`                                             |
| `fields.<name>.null_threshold` | number           | No                            | The probability of null above which the value is `null`. Greater than 0 and up to 1.                           | `0.5`                                                  |
| `fields.<name>.horizon`        | int              | No                            | Only for `forecast`. The number of steps, from 1 to 64.                                                        | `12`                                                   |
| `fields.<name>.interval`       | string           | No                            | Only for `forecast`. `"step"`, `"minute"`, `"hour"`, `"day"`, `"week"`, `"month"`, `"quarter"`, or `"year"`.   | `"month"`                                              |
| `images`                       | string array     | No                            | One `"[Blob substitute]"` string for each image. Never send the image data.                                    | `["[Blob substitute]"]`                                |
| `reference_date`               | string           | No                            | The ISO date that anchors dates written without a year.                                                        | `"2026-09-01"`                                         |
| `options`                      | object           | No                            | Request options.                                                                                               | `{"include_quantiles": true}`                          |
| `options.include_quantiles`    | boolean          | No                            | Adds 99 percentiles to estimates.                                                                              | `true`                                                 |
| `options.return_state_text`    | boolean          | No                            | Returns the text that spans index into.                                                                        | `true`                                                 |

### [Extraction Output Messages](https://develop.sentry.dev/sdk/telemetry/traces/modules/ai-agents.md#extraction-output-messages)

`gen_ai.output.messages` holds a one-element array with the response body as the provider returned it, without `model` and `usage`. Don't change the values. `answers` has one answer for each field in the input, keyed by field name.

| Field                       | Type                | Required | Description                                                                                                                          | Example                    |
| --------------------------- | ------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------ | -------------------------- |
| `answers`                   | object              | Yes      | The answers, keyed by field name.                                                                                                    | `{"total": {...}}`         |
| `answers.<name>.type`       | string              | Yes      | The type of the field.                                                                                                               | `"number"`                 |
| `answers.<name>.value`      | any                 | Yes      | The answer, typed by the field: a string, a number, an ISO date, or an array for `list`. `null` when the input doesn't determine it. | `1080`                     |
| `answers.<name>.guess`      | any                 | No       | The best guess. It is there even when `value` is `null`.                                                                             | `"INV-2291"`               |
| `answers.<name>.p_null`     | number              | No       | The probability that the value is `null`.                                                                                            | `2.3e-08`                  |
| `answers.<name>.confidence` | number              | No       | The confidence of the answer.                                                                                                        | `0.9999`                   |
| `answers.<name>.span`       | int array           | No       | Only for `text`. `[start, end]` character offsets of the value in `state` (or in `state_text`). `end` is exclusive.                  | `[8, 16]`                  |
| `answers.<name>.spans`      | array of int arrays | No       | Only for `list`. One `[start, end]` pair for each item.                                                                              | `[[120, 129], [131, 141]]` |
| `answers.<name>.mean`       | number or string    | No       | Only for `estimate`. The mean of the distribution.                                                                                   | `29.535`                   |
| `answers.<name>.median`     | number or string    | No       | Only for `estimate`. The median of the distribution.                                                                                 | `30`                       |
| `answers.<name>.p10`        | number or string    | No       | Only for `estimate`. The 10th percentile.                                                                                            | `21`                       |
| `answers.<name>.p90`        | number or string    | No       | Only for `estimate`. The 90th percentile.                                                                                            | `38`                       |
| `answers.<name>.p_below`    | number              | No       | Only for `estimate`. The probability that the value is below `min`.                                                                  | `5.9e-06`                  |
| `answers.<name>.p_above`    | number              | No       | Only for `estimate`. The probability that the value is above `max`.                                                                  | `1.2e-05`                  |
| `answers.<name>.quantiles`  | array               | No       | Only for `estimate`, when `options.include_quantiles` is set. 99 percentiles.                                                        |                            |
| `answers.<name>.steps`      | array               | No       | Only for `forecast`. One estimate for each step.                                                                                     |                            |
| `truncated`                 | boolean             | Yes      | Copy it as the provider returned it.                                                                                                 | `false`                    |
| `state_text`                | string              | No       | Only when `options.return_state_text` is set. The text that spans index into.                                                        |                            |

## [Memory Span](https://develop.sentry.dev/sdk/telemetry/traces/modules/ai-agents.md#memory-span)

Describes a read from, write to, or management operation on an agent's persistent memory store.

* Span `op`: `"gen_ai.memory.client"`. Relay uses the op to classify the span as a memory operation.
* `gen_ai.operation.name`: `"search_memory"`, `"create_memory"`, `"update_memory"`, `"upsert_memory"`, `"delete_memory"`, `"create_memory_store"`, or `"delete_memory_store"`
* Span `name`: the `gen_ai.operation.name` value (e.g. `"search_memory"`)

| Attribute                    | Type   | Description                                                                                                                                                                 | Example                                                                     |
| ---------------------------- | ------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------- |
| `gen_ai.memory.store.id`     | string | The memory store the operation targets, such as a collection, namespace, or vector index. Shown in the span details and as the preview for store operations.                | `"user-preferences-store"`                                                  |
| `gen_ai.memory.record.id`    | string | The record the operation applies to. Omit it when the operation targets more than one record. On `delete_memory`, a missing ID means every record in the store was deleted. | `"mem_5j66UpCpwteGg4YSxUnt7lPY"`                                            |
| `gen_ai.memory.record.count` | int    | The number of records returned by `search_memory`, or written or deleted by other operations. Shown as the result in the transcript.                                        | `3`                                                                         |
| `gen_ai.agent.name`          | string | The agent the operation ran under. Omit it when the operation runs outside an agent.                                                                                        | `"Support Agent"`                                                           |
| `gen_ai.memory.query.text`   | string | The search query, shown as the preview for `search_memory`. Only for `search_memory`. It can contain sensitive data, so only send it when the user opts in.                 | `"user dietary preferences"`                                                |
| `gen_ai.memory.records`      | string | The records stored or retrieved. **\[3]** They can contain sensitive data, including PII, so only send them when the user opts in.                                          | `'[{"id": "mem_123", "content": "User prefers dark mode", "score": 0.95}]'` |

## [Notes](https://develop.sentry.dev/sdk/telemetry/traces/modules/ai-agents.md#notes)

**\[0]** Messages use the format `{role, parts}`, where `role` is `"user"`, `"assistant"`, `"tool"`, or `"system"`, and `parts` is an array of typed objects:

* `text`: user-visible content.
* `reasoning`: the model's thinking. Sentry shows it as a collapsible row, separate from the reply. Never send thinking content as a `text` part.
* `tool_call` and `tool_call_response`: tool invocations, linked by a shared `id`.

Replace binary data in positions where the provider schema expects it, including `data:` URLs, with the string `"[Blob substitute]"`. Plain HTTP URLs stay as they are. See the [regex used by the Python SDK](https://github.com/getsentry/sentry-python/blob/5379537fcd2925374fa617e49bb618fc9979509b/sentry_sdk/integrations/pydantic_ai/consts.py#L6). Input messages are also subject to the [size limit](https://develop.sentry.dev/sdk/foundations/envelopes/event-payloads.md) for AI input messages.

**\[1]** `gen_ai.usage.input_tokens` is the total and includes cache-read and cache-write tokens. `gen_ai.usage.output_tokens` includes reasoning tokens. Relay subtracts the subsets from the totals to calculate cost, so reporting only the non-cached count as `input_tokens` produces negative costs.

**\[2]** Send every message the model received on that call, including the earlier turns of the conversation. Don't drop messages that previous spans already carried or keep only the most recent user message: that loses data, for example when two user messages arrive in a row.

**\[3]** A JSON array of records. Each record has a `content` (a string or JSON object) and can also carry an `id`, `metadata`, and a `score`.
