Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -110,12 +110,45 @@ These fields contain the details the dashboard aggregates. Generative AI attribu
| `attributes.gen_ai.provider.name` | Model provider |
| `attributes.gen_ai.agent.id` | Agent identifier |
| `attributes.gen_ai.conversation.id` | Conversation identifier |
| `attributes.elastic.conversation.title` {applies_to}`{stack: ga 9.6+, serverless: ga}` | Conversation title. Present on the root conversation round span only, and only when **Include real tool, agent, and conversation names in traces** is on. This is an Elastic field rather than an OpenTelemetry one, so it is not covered by the generative AI semantic conventions |
| `attributes.elastic.inference.span.kind` | The kind of work a span represents:<br>- `LLM` on `chat` spans<br>- `TOOL` on `execute_tool` spans<br>- `CHAIN` or `AGENT` on `invoke_agent` spans, where `CHAIN` is a conversation round and `AGENT` is an agent execution.<br><br>Internal spans such as `generate_title` also use `CHAIN`, so combine this field with a `span.name` filter instead of using it on its own |
| `name` | Span name. On `execute_tool` spans it is `execute_tool <tool-id>`, for example `execute_tool platform.core.list_indices`. For the bare tool id, use `attributes.gen_ai.tool.name` |
| `name` | Span name. On `execute_tool` spans it is `execute_tool <tool-id>`, for example `execute_tool platform.core.list_indices`. For the bare tool id, use `attributes.gen_ai.tool.name`.<br><br>Names in this field are anonymized along with the attributes. While the real-names [trace privacy setting](collect-traces.md#trace-privacy-settings) is off, a custom agent or tool appears as `invoke_agent custom` or `execute_tool custom`, so filters that match a real name return nothing |
| `duration` | Span duration in nanoseconds (root field). Divide by 1,000,000,000 for seconds |
| `status.code` | Span status, for example `Error` (root field) |
| `@timestamp` | When the span started |

### User attributes [user-attributes]

```{applies_to}
stack: ga 9.6+
serverless: ga
```

Traces record who ran each agent. These fields appear on the root conversation round span only, so join on `trace_id` to attribute nested spans to a user.

To select that span, combine a span name prefix with the span kind:

```esql
FROM traces-agent_builder.otel-*
| WHERE span.name LIKE "invoke_agent *" AND attributes.elastic.inference.span.kind == "CHAIN"
```

Both conditions are needed. The name prefix on its own also matches the nested agent execution spans, and `CHAIN` on its own also matches the internal `generate_title` span, which carries none of these fields. Anonymized names do not break the filter, because an anonymized round is still named `invoke_agent custom`.

| Field | Description | Required setting |
|---|---|---|
| `attributes.user.id` | User profile ID | **Include user data in traces** |
| `attributes.user.name` | Username | **Include user data in traces** |
| `attributes.user.hash` | Stable hash of the user ID, used for correlation when the real identity is withheld. Present only when the setting is off | None |

`attributes.user.hash` is stable for a given user across conversations, so you can break trace data down per user without recording anyone's identity. Group by `attributes.user.hash` to build per-user token or latency dashboards while leaving **Include user data in traces** off. Turn the setting on only when you need to attribute activity to a named person.

When the caller has no user profile, as is the case for some API key authentication, there is no `attributes.user.id` and therefore no `attributes.user.hash`. Those rounds are still traced, but they fall outside any per-user breakdown.

For users who sign in with {{ecloud}} SSO, as is standard on {{ech}} and {{serverless-full}}, `attributes.user.name` holds the numeric {{ecloud}} user ID rather than a readable username. To resolve a display name, look up `attributes.user.id` with the [user profile API](https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-security-get-user-profile) and read `user.full_name` or `user.email` from the response.

To break token usage down by user, combine spans from the same trace. Token counts are recorded on the model call spans, not on the conversation round span that carries the user fields, so a query over a single span kind returns no token totals. Both span kinds share the same `trace_id` and `attributes.gen_ai.conversation.id`, so use one of those to correlate them.

### Message content attributes [message-content-attributes]

The dashboard does not use these fields, but you can query them yourself. When an administrator opts in to capturing conversation content, prompts, responses, and tool call content are stored in the following attributes. Each one depends on a [trace privacy setting](collect-traces.md#trace-privacy-settings), and all of those settings are off by default.
Expand All @@ -139,9 +172,12 @@ Note that:
#### Other content attributes

Spans also contain other content-bearing attributes:

- The `chat` spans record the definitions of the tools offered to the model in `attributes.gen_ai.tool.definitions` (including each tool's description and parameter schema).
- The `execute_tool` spans record the tool's own description in `attributes.gen_ai.tool.description`.

Both depend on the real-names [trace privacy setting](collect-traces.md#trace-privacy-settings), which is off by default. Unlike tool and agent names, these two attributes are dropped entirely rather than anonymized, because they hold free-form text that cannot be selectively masked.

#### JSON payload structure

Each content field holds a JSON string rather than indexed text, following the [OpenTelemetry semantic conventions for generative AI](https://github.com/open-telemetry/semantic-conventions-genai):
Expand Down
25 changes: 21 additions & 4 deletions explore-analyze/ai-features/agent-builder/collect-traces.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ To change what is captured, expand **Advanced privacy settings** in the **Agent

:::{image} images/agent-builder-traces-privacy-settings.png
:screenshot:
:alt: The expanded Advanced privacy settings, showing six toggles for including sensitive content in traces, all turned off
:alt: The expanded Advanced privacy settings, showing seven toggles for including sensitive content in traces
:::

| Setting | Setting ID | Effect when enabled |
Expand All @@ -86,16 +86,31 @@ To change what is captured, expand **Advanced privacy settings** in the **Agent
| **Include LLM responses in traces** | `agentBuilder:tracing:includeLlmResponses` | Captures agent responses. |
| **Include tool call details in traces** | `agentBuilder:tracing:includeToolDetails` | Captures tool call arguments and results. |
| **Include system prompt in traces** | `agentBuilder:tracing:includeSystemPrompt` | Captures agent instructions. |
| **Include real tool and agent names in traces** | `agentBuilder:tracing:includeRealNames` | Records real tool and agent names instead of anonymized values. |
| **Include real conversation and workflow IDs in traces** | `agentBuilder:tracing:includeRealIds` | Records real conversation and workflow IDs instead of anonymized values. |
| **Include real tool, agent, and conversation names in traces** {applies_to}`{stack: ga 9.6+, serverless: ga}`<br>**Include real tool and agent names in traces** {applies_to}`stack: ga =9.5` | `agentBuilder:tracing:includeRealNames` | Records real tool, agent, and workflow names instead of anonymized values, and keeps the full tool definitions and descriptions.<br><br>{applies_to}`{stack: ga 9.6+, serverless: ga}` Also records the conversation title. |
| **Include real conversation and workflow IDs in traces** | `agentBuilder:tracing:includeRealIds` | Records real conversation and workflow IDs instead of anonymized values. Also controls the agent ID and the workflow execution ID, which the setting name does not mention. |
| **Include user data in traces** {applies_to}`{stack: ga 9.6+, serverless: ga}` | `agentBuilder:tracing:includeUserData` | Records the real user ID and username of the person who ran the agent, instead of a hash alone. |

:::{note}
Built-in tools and agents always appear under their real names. Anonymized names are replaced with the literal value `custom`, so every custom tool, agent, and workflow shares one value and you cannot tell them apart by name. Anonymized IDs are different: they are replaced with a stable hash, so you can still group and correlate traces by conversation or agent ID.
Built-in tools and agents always appear under their real names. Anonymized names are replaced with the literal value `custom`, so every custom tool, agent, and workflow shares one value and you cannot tell them apart by name. Anonymized IDs are different: they are replaced with a stable hash, so you can still group and correlate traces by conversation or agent ID. A real conversation ID is a UUID, and an anonymized one is a 16-character hash, so you can tell at a glance which you are looking at. An anonymized custom agent ID keeps a `custom-` prefix, as in `custom-10a91bfdfac987f2`.

User data is the exception to that pattern. Every other setting anonymizes a value in place, keeping the same field. The user fields swap instead. When **Include user data in traces** is off, the username is dropped and the user ID is replaced by a stable hash in `attributes.user.hash`. When the setting is on, `attributes.user.id` and `attributes.user.name` are recorded and `attributes.user.hash` is absent. Account for that if you build dashboards that group by user.
:::

On {{ech}} and {{serverless-full}}, the username recorded for a user who signs in with {{ecloud}} SSO is a numeric {{ecloud}} user ID rather than a readable name. For the field details and how to resolve a display name, refer to [User attributes](agent-traces-dashboard.md#user-attributes).

Changing a privacy setting affects only traces recorded after the change. Existing traces are not rewritten. {{agent-builder}} refreshes these settings every 30 seconds, so allow up to that long for a change to take effect, then run a new conversation round to see it.

Content is stored across different span types:
- **`chat` spans**: Store prompts, responses, and the system prompt in the `attributes.gen_ai.input.messages`, `attributes.gen_ai.output.messages`, and `attributes.gen_ai.system_instructions` attributes.
- **`execute_tool` spans**: Store tool call details in `attributes.gen_ai.tool.call.arguments` and `attributes.gen_ai.tool.call.result`.
- **The conversation round span** {applies_to}`{stack: ga 9.6+, serverless: ga}`: Stores the conversation title and the user identity. Only the root `invoke_agent` span carries these, one per conversation round. The nested agent, model, and tool spans do not. For the field names, the settings each one depends on, and how to select that span, refer to [User attributes](agent-traces-dashboard.md#user-attributes).

Two limits apply to the conversation title:

- It is recorded only for runs that create or continue a saved conversation. Chats always save, and so do runs through the conversation APIs. The [`ai.agent` workflow step](agents-and-workflows.md#use-ai-agent-workflow-step) is the exception: unless it creates a conversation or continues an existing one, nothing is saved and the run has no title attribute.
- Renaming a conversation does not update titles already recorded. Rounds recorded before the rename keep the old title, and the new one appears from the next round onward. A conversation's rounds share the same `attributes.gen_ai.conversation.id`, so to find the current title, take the title from its most recent round.

Anonymization also rewrites the span name, not only the attributes. A custom agent's round appears as `invoke_agent custom` rather than `invoke_agent <your agent name>`, and a custom tool call appears as `execute_tool custom`. Filters that match on a real name in `span.name` return nothing while the names are anonymized.

Anyone who can read the trace data stream can read this content, so review [Grant access to trace data](#grant-access-to-trace-data) before you turn these settings on. For the field-level details, refer to [Message content attributes](agent-traces-dashboard.md#message-content-attributes).

Expand All @@ -105,6 +120,8 @@ Trace data is stored in the `traces-agent_builder.otel-*` data stream. To read i

Access is granted at the index level. Any user who can read these data streams can read all collected traces, so trace access is not scoped per user. To control who can read traces, configure index privileges through roles in **Stack Management → Roles**.

{applies_to}`{stack: ga 9.6+, serverless: ga}` When **Include user data in traces** is on, traces identify the person who ran each agent. Anyone who can read the data stream can see which user ran which conversation, so review this section before you turn that setting on.

For the full privilege model, including {{kib}} feature and cluster privileges, refer to [Permissions and access control](permissions.md#read-trace-data).

## Build dashboards on trace data
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading