> ## 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.

# Trace and session evals

> Score a full request or a full conversation with one evaluator. How Arize AX assembles data at trace and session scope, and how to set up each.

export const AskAlyx = ({children}) => {
  const gradientId = `askAlyxGradient-${Math.random().toString(36).slice(2)}`;
  return <div style={{
    display: "flex",
    alignItems: "flex-start",
    gap: "0.625rem",
    margin: "1rem 0",
    padding: "0.75rem 1rem",
    borderRadius: "10px",
    border: "1px solid rgba(120, 115, 245, 0.25)",
    background: "linear-gradient(135deg, rgba(255, 110, 196, 0.08), rgba(120, 115, 245, 0.08))"
  }}>
      <svg width="18" height="18" viewBox="0 0 21 17" xmlns="http://www.w3.org/2000/svg" style={{
    flexShrink: 0,
    marginTop: "0.2rem"
  }}>
        <defs>
          <linearGradient id={gradientId} x1="0%" y1="100%" x2="100%" y2="0%">
            <stop offset="0%" stopColor="#ff6ec4" />
            <stop offset="100%" stopColor="#7873f5" />
          </linearGradient>
        </defs>
        <path d="M6.28906 12.7223C6.28889 11.3831 5.24007 10.3385 3.98926 10.3385C2.73859 10.3387 1.68963 11.3832 1.68945 12.7223C1.68945 14.0616 2.73849 15.1059 3.98926 15.1061C5.24018 15.1061 6.28906 14.0617 6.28906 12.7223ZM7.81152 0.557254C9.70554 -0.563135 12.1081 0.0645388 13.2607 1.91468L13.3691 2.09827V2.09925L20.7266 15.402C20.8713 15.6637 20.8667 15.9823 20.7148 16.2399C20.5629 16.4975 20.2864 16.6559 19.9873 16.6559H14.5459C13.0953 16.6474 11.7648 15.848 11.0469 14.5748V14.5739L6.33301 6.19104C5.22656 4.2273 5.87813 1.706 7.80957 0.558231L7.81152 0.557254ZM11.8906 2.91761C11.2374 1.74047 9.78961 1.34962 8.67188 2.01038L8.67285 2.01136C7.61521 2.64 7.19477 3.99924 7.69336 5.13733L7.80566 5.36194V5.36292L12.5186 13.7448C12.9466 14.5038 13.7274 14.9616 14.5557 14.9664H18.5547L11.8906 2.91663V2.91761ZM7.97949 12.7223C7.97949 14.9527 6.21527 16.7965 3.98926 16.7965C1.7634 16.7963 0 14.9526 0 12.7223C0.000173728 10.4921 1.76351 8.64923 3.98926 8.64905C6.21516 8.64905 7.97932 10.492 7.97949 12.7223Z" fill={`url(#${gradientId})`} />
      </svg>
      <span>{children}</span>
    </div>;
};

A span eval scores one span. A trace eval scores every span in a single request, and a session eval scores every trace that shares a `session.id`. Each returns one result for the thing it scores, so a trace eval returns one label, score, and explanation for the whole request, and a session eval returns one label, score, and explanation for the whole conversation.

This guide covers how Arize AX assembles the data a trace or session evaluator sees, and how to configure a trace eval and a session eval. For the conceptual distinction between span, trace, and session, see [Evaluation levels](/docs/ax/concepts/evaluators/evaluation-levels).

## When to use each scope

| Scope       | Unit evaluated                     | Questions it answers                                                                                |
| :---------- | :--------------------------------- | :-------------------------------------------------------------------------------------------------- |
| **Span**    | One span                           | Was the right tool selected? Was this retrieval relevant? Was this response correct?                |
| **Trace**   | Every span in one request          | Was the sequence of tool calls correct? Did the agent loop? Did it skip a step?                     |
| **Session** | Every trace sharing a `session.id` | Did the agent keep context across turns? Did the user reach their goal? Did the conversation stall? |

Pick the narrowest scope that can answer your question. A session eval passes far more context to the judge than a span eval does, and that extra context raises cost and gives the judge more text to weigh.

Throughout this page, **unit** means whatever one evaluator run scores: a single trace for a trace eval, and a single session for a session eval.

## How trace and session evals work

Every evaluator has a scope, set with the **Scope** dropdown on the evaluator form, and that scope determines whether the evaluator scores a span, a trace, or a session. You run an evaluator by attaching it to a task, which connects it to a data source and defines what to score and how often. Every evaluator on one task must share the same scope, so a task is span-level, trace-level, or session-level, not a mix. To run evaluators at different scopes against the same project, create a separate task for each scope.

<Frame caption="Set Scope to Trace or Session on the New Evaluator form">
  <img src="https://storage.googleapis.com/arize-assets/doc-images/evaluate/evaluator-scope-selector.png" alt="New Evaluator form for a hallucination evaluator with the Scope dropdown open and highlighted, listing Span for evals on an individual span, Trace for evals across an entire trace, and Session for evals across traces in a session, with Session selected, and a Configuration column on the right showing Pick a Data Source, Filter Sessions, Preview Matching Data, and Map Variable to Data" />
</Frame>

### Session-only variables

Session evaluators have two variables that Arize AX assembles for you rather than reading from a span attribute, one entry per turn, where a turn is one trace in the session. The two are mutually exclusive, and you choose between them with the **Conversation** and **Turn data** radio buttons on the evaluator form.

* **`{conversation}`** is the session transcript Arize AX builds as a JSON array with one `input` / `output` pair per turn, taken from each trace's root span in chronological order. It needs no variable mapping, and it is valid only at session scope.
* **`{turn_data}`** is the string Arize AX produces by rendering a **Turn Definition** template you write once per turn, then joining the rendered turns with `TURN 1`, `TURN 2`, and so on as separators. Use it when the default input/output transcript leaves out something the judge needs, such as tool results or retrieved context.

<Frame caption="Choosing Conversation or Turn data on the New Evaluator form">
  <img src="https://storage.googleapis.com/arize-assets/doc-images/evaluate/conversation-and-turn-data.png" alt="New Evaluator form with Scope set to Session and the Conversation and Turn data radio buttons highlighted, Conversation selected and captioned evaluate the full session transcript with input and output per turn, Turn data captioned evaluate a custom per-turn layout you define below, and the Configuration column on the right showing Task Configuration with a name, sampling rate, Run Continuously and One-Time Backfill toggles, and an Advanced section" />
</Frame>

### How variables are filled in

At span scope, each template variable reads one value from one span. At trace and session scope, a variable can match many spans, so the matching values are concatenated with commas in chronological order. For trace evals, spans are ordered by span timestamp. For session evals, spans are grouped into traces first, and traces are ordered by earliest span timestamp.

Each value is truncated at 100,000 characters, and after that truncation a session's fully assembled value is capped at 100,000 characters. Some judge models raise both limits.

### Where results land

Each unit produces one result, written to that unit's root span. Results are stored under a prefixed column name so span, trace, and session evals with the same name never collide:

| Scope   | Column prefix         | Written to                                 |
| :------ | :-------------------- | :----------------------------------------- |
| Span    | `eval.<name>`         | The evaluated span                         |
| Trace   | `trace_eval.<name>`   | The trace's root span                      |
| Session | `session_eval.<name>` | The root span of the session's first trace |

Sampling on a trace or session task applies per unit, so 10% sampling on a session task evaluates 10% of sessions rather than 10% of spans.

<Note>
  When a task runs, Arize AX collects each unit's spans from a window that extends 24 hours before the time window the task itself evaluates, so a session that has been idle for longer than that contributes only its recent traces. Arize AX searches back 7 days to find a unit's root span, so results still land on the correct span for long-running sessions.
</Note>

## Set up a trace or session eval

Trace and session evaluators are built on the same form, which has two halves. The left pane defines the evaluator itself: its name, scope, judge model, template, and choices. The numbered **Configuration** column on the right points that evaluator at data and sets up the task that runs it. The steps below follow the form in order. Session evaluators add one further decision about how each turn reaches the judge.

<Tabs>
  <Tab title="Trace eval">
    <AskAlyx>Ask [Alyx](/docs/ax/alyx), the Arize AX assistant, to build a trace-scoped evaluator for you: try *"Create a trace-scoped evaluator that checks whether the agent called tools in a sensible order."*</AskAlyx>

    <Steps>
      <Step title="Open the New Evaluator form">
        Open **Evaluators** in the left navigation and click **New Evaluator**, or open a trace from the traces table and click **Add Trace Eval** in the trace header, an action that pre-sets the scope for you.
      </Step>

      <Step title="Name it and set Scope to Trace">
        In the left pane, enter a **Name** and set **Scope** to **Trace**. The **Result Column Name** is auto-populated from the name and becomes the `trace_eval.<name>` column.
      </Step>

      <Step title="Pick the judge model and write the template">
        Choose the judge model above the template, then write the template, referencing the variables the judge needs, for example `{question}`, `{tool_calls}`, and `{output}`. Because the eval sees the whole trace, write criteria about the path taken, not about a single response.
      </Step>

      <Step title="Define Choices and Optimization Direction">
        Below the template, define **Choices** and their scores, then set the **Optimization Direction** so Arize AX knows whether a higher or a lower score is better.
      </Step>

      <Step title="Pick a Data Source">
        In the **Configuration** column, choose **Project** and select the project whose traces you want scored. The picker also offers **Dataset**, but a dataset is scored one example at a time whatever the scope, so trace grouping applies only to a project.
      </Step>

      <Step title="Filter Traces">
        Narrow the traces the evaluator runs on. This step accepts a [multi-span query](/docs/ax/observe/tracing/view-and-manage-traces#multi-span-filters), which combines several named span filters with operators such as `AND` and "followed by" to describe a pattern of spans within one trace. Multi-span queries are available only at trace and session scope.
      </Step>

      <Step title="Preview Matching Data">
        Read the sample the filter returns to confirm it admits the traces you expect before you map any variables.
      </Step>

      <Step title="Map Variable to Data">
        Point each template variable at a column. Arize AX joins the values from every matching span with commas, in chronological order, and **Data preview** under each mapping shows the value it pulled from the latest matching trace.
      </Step>

      <Step title="Set Task Configuration">
        Name the task, set the **Sampling Rate (%)**, and turn on either **Run Continuously** to score new traces as they arrive or **One-Time Backfill** to score history once. **Advanced** holds an optional **LLM Override** and the **Enable tracing** toggle. See [Run online evals on traces](/docs/ax/evaluate/run-evals-on-traces) for how to pick a sampling rate.
      </Step>

      <Step title="Create the evaluator">
        Click **Create Evaluator** to save it to the [Evaluator Hub](/docs/ax/evaluate/create-evaluators#evaluator-hub) for reuse, or **Create and Run Evaluator** to save it and start the task you just configured.
      </Step>
    </Steps>

    <Frame caption="A trace-scoped evaluator, its matching traces, and its variable mapping">
      <img src="https://storage.googleapis.com/arize-assets/doc-images/evaluate/trace-scoped-evaluator.png" alt="Use Evaluator(s) Task Configuration form for a hallucination evaluator scoped to Trace, showing the gpt-4o judge model and a template referencing input, context, and output variables on the left, and on the right Preview Matching Data listing chain, LLM, embedding, and retriever spans of matching traces, then Map Variable to Data mapping input to the input column with a data preview of the latest matching trace" />
    </Frame>
  </Tab>

  <Tab title="Session eval">
    <AskAlyx>Ask [Alyx](/docs/ax/alyx), the Arize AX assistant, to build a session-scoped evaluator for you: try *"Create an evaluator that checks whether the agent maintained context across the whole session."*</AskAlyx>

    <Steps>
      <Step title="Open the New Evaluator form">
        Open **Evaluators** in the left navigation and click **New Evaluator**, or use the **Add Session Eval** action next to the session ID in the session header. Sessions exist only when your application sets `session.id` on its spans; see [OpenInference context managers](/docs/ax/concepts/otel-openinference/context-managers) if that attribute is missing.
      </Step>

      <Step title="Name it and set Scope to Session">
        In the left pane, enter a **Name** and set **Scope** to **Session**. The **Result Column Name** is auto-populated from the name and becomes the `session_eval.<name>` column.
      </Step>

      <Step title="Choose Conversation or Turn data">
        Pick **Conversation** to have Arize AX build the transcript from each turn's input and output. Picking it inserts `{conversation}` into your template. Pick **Turn data** when the judge needs more than input and output; that option inserts `{turn_data}` and reveals the **Turn Definition** field.
      </Step>

      <Step title="Write the Turn Definition (Turn data only)">
        Lay out one turn using `{variable}` references, for example:

        ```
        User: {user_msg}
        Assistant: {assistant_msg}
        Tool result: {tool_result}
        ```

        Arize AX renders this template once per turn and joins the results with `TURN 1`, `TURN 2` markers. Variables you leave unmapped render empty rather than failing the turn.
      </Step>

      <Step title="Write the template, then define Choices and Optimization Direction">
        Choose the judge model, write the template around `{conversation}` or `{turn_data}`, then define **Choices** and their scores and set the **Optimization Direction** so Arize AX knows whether a higher or a lower score is better.
      </Step>

      <Step title="Pick a Data Source">
        In the **Configuration** column, choose **Project** and select the project whose sessions you want scored. The picker also offers **Dataset**, but a dataset is scored one example at a time whatever the scope, so session grouping applies only to a project.
      </Step>

      <Step title="Filter Sessions">
        Narrow the sessions the evaluator runs on, then read **Preview Matching Data** to confirm the filter admits the sessions you expect. This step accepts a [multi-span query](/docs/ax/observe/tracing/view-and-manage-traces#multi-span-filters), which combines several named span filters with operators such as `AND` and "followed by" to describe a pattern of spans within one session.
      </Step>

      <Step title="Map Variable to Data and read the Data Preview">
        A `{conversation}` eval needs no mapping, since Arize AX assembles the transcript from the matched session spans. For a `{turn_data}` eval, map each Turn Definition variable to a column; Arize AX reads each variable only from the turn it belongs to, so turn 2's values never appear in turn 1. The **Data Preview** shows the full prompt the judge will receive for the latest matching session, with every variable and every assembled turn filled in. Iterate on the Turn Definition and the mappings until the preview reads the way you want.
      </Step>

      <Step title="Set Task Configuration">
        Name the task, set the **Sampling Rate (%)**, and turn on either **Run Continuously** to score new sessions as they arrive or **One-Time Backfill** to score history once. **Advanced** holds an optional **LLM Override** and the **Enable tracing** toggle.
      </Step>

      <Step title="Create the evaluator">
        Click **Create Evaluator** to save it to the [Evaluator Hub](/docs/ax/evaluate/create-evaluators#evaluator-hub) for reuse, or **Create and Run Evaluator** to save it and start the task you just configured.
      </Step>
    </Steps>

    <Frame caption="A session-scoped evaluator using Conversation, with its matching sessions">
      <img src="https://storage.googleapis.com/arize-assets/doc-images/evaluate/session-scoped-evaluator.png" alt="New Evaluator form for a hallucination evaluator scoped to Session with Conversation selected, showing the gpt-4o judge model and template on the left, and on the right Preview Matching Data with Traces, Spans, and Sessions tabs listing agent, retriever, and LLM spans from a trip planning agent, then Map Variable to Data noting that the eval uses the conversation variable assembled from the matched session spans" />
    </Frame>
  </Tab>
</Tabs>

<Note>
  The steps above describe the **New Evaluator** form. Attaching an evaluator that already exists in the [Evaluator Hub](/docs/ax/evaluate/create-evaluators#evaluator-hub) opens the same Configuration column under the title **Use Evaluator(s) – Task Configuration**, where the primary button reads **Run Evaluator(s)** and the Configuration steps are numbered from the point you enter.

  **Test Evaluator On Spans** is disabled for trace- and session-scoped evaluators when the data source is a project, so use the data preview under **Map Variable to Data** to confirm what the judge receives before you run the task.
</Note>

## View results

Trace and session results appear alongside your traces once the task runs.

* **Trace detail:** open a trace from the traces table and read the **Trace Evals** row in the header. Each result appears as a colored label, and hovering over one shows its score and explanation.
* **Session detail:** open a session from the **Sessions** tab and read the **Session Evals** row in the header, where the labels behave the same way.
* **Traces table:** add the **Trace Evaluations** or **Session Evaluations** column from the column picker to scan results across many units, and filter on `trace_eval.<name>.label` or `session_eval.<name>.score` to isolate failures.

<Frame caption="Session Evals in the session header, with one result expanded">
  <img src="https://storage.googleapis.com/arize-assets/doc-images/evaluate/session-eval-results.png" alt="Session detail view showing total duration, total tokens, and total traces alongside a Session Evals row with a hallucination label and a Session Coherence incoherent label, the latter expanded into a panel giving the label incoherent, a score of 0, and an explanation of why the conversation was repetitive and unfocused" />
</Frame>

## Log trace and session evals from code

If you run evals in your own environment, log results back with the prefix that matches the scope. Every result needs a `context.span_id` column, and for trace and session evals that column holds the id of the unit's root span, which is the trace's root span for a trace eval and the root span of the session's first trace for a session eval.

```python theme={null}
# Trace eval: one row per trace, keyed on that trace's root span id
trace_eval_df["context.span_id"] = trace_eval_df["root_span_id"]
trace_eval_df = trace_eval_df.rename(columns={
    "label": "trace_eval.trajectory.label",
    "score": "trace_eval.trajectory.score",
    "explanation": "trace_eval.trajectory.explanation",
})

# Session eval: one row per session, keyed on the root span of the session's first trace
session_eval_df["context.span_id"] = session_eval_df["first_root_span_id"]
session_eval_df = session_eval_df.rename(columns={
    "label": "session_eval.correctness.label",
    "score": "session_eval.correctness.score",
    "explanation": "session_eval.correctness.explanation",
})

client.spans.update_evaluations(
    space_id=os.environ["ARIZE_SPACE_ID"],
    project_name="your-project-name",
    dataframe=trace_eval_df,
)

client.spans.update_evaluations(
    space_id=os.environ["ARIZE_SPACE_ID"],
    project_name="your-project-name",
    dataframe=session_eval_df,
)
```

For the full export, evaluate, and log flow, including how far back evals can be applied, see [Run online evals on traces](/docs/ax/evaluate/run-evals-on-traces#create-a-task).

## Further reading

<CardGroup cols={2}>
  <Card title="Run online evals on traces" icon="sliders" href="/docs/ax/evaluate/run-evals-on-traces">
    Task filters, sampling rates, cadence, and run logs in full.
  </Card>

  <Card title="Agent trajectory evaluations" icon="route" href="/docs/ax/evaluate/evaluators/trace-and-session-evals/trace-level-evaluations/agent-trajectory-evaluations">
    The canonical trace eval: judging the path an agent took.
  </Card>

  <Card title="Trace evals for a recommendation agent" icon="book-open" href="/docs/ax/cookbooks/evaluate/trace-level-evaluations-for-a-recommendation-agent">
    A worked trace-scoped example, end to end.
  </Card>

  <Card title="Session evals for an AI tutor" icon="messages-square" href="/docs/ax/cookbooks/evaluate/session-level-evaluations-for-an-ai-tutor">
    A worked session-scoped example across a multi-turn conversation.
  </Card>
</CardGroup>
