Evaluate Traces & Spans
Run online and offline evaluations on individual traces and spans on the fly
Overview
Online evaluations let you run metrics on traces and spans on-the-fly as they're ingested into Confident AI, giving you real-time production monitoring of your AI's quality.

You can also trigger evaluations retrospectively on historical traces and spans.
How It Works
Online evaluations for traces and spans follow these steps:
- You create a metric collection on Confident AI with the single-turn metrics you want to run.
- You reference that metric collection by name via the
metric_collectionparameter — on theobservedecorator/wrapper for span-level evals, or viaupdate_current_tracefor trace-level evals. - Inside your observed function, you set test case parameters on the span or trace using the update current span or update current trace function.
- When the trace is sent to Confident AI, it runs the metrics in your collection against the test case data you provided.
- Results appear on the trace/span in the Confident AI dashboard.
sequenceDiagram
participant App as Your App
participant SDK as Evals API/DeepEval
participant CAI as Confident AI
App->>SDK: Call observed function
SDK->>SDK: Create trace & span(s)
App->>SDK: Set test case parameters (input, output, etc.)
App->>SDK: Function returns
SDK->>CAI: Send trace with test case data
CAI->>CAI: Look up metric collection
CAI->>CAI: Run referenceless metrics against test case
CAI->>CAI: Store results on trace/span
Map Test Case Parameters
To run evaluations, you first need to understand how trace span parameters map to test case parameters, which is what metrics use for evaluation. These parameters provide the data that metrics evaluate against.
The parameters you pass to the update current span or update current trace function map directly to test case parameters that metrics evaluate against:
| Trace/Span Parameter | Test Case Parameter | Description |
|---|---|---|
input | input | The input to your AI app |
output | actual_output | The output of your AI app |
expected_output | expected_output | The expected output of your AI app |
retrieval_context | retrieval_context | List of retrieved text chunks from a retrieval system |
context | context | List of ideal retrieved text chunks |
tools_called | tools_called | List of ToolCall objects representing tools called |
expected_tools | expected_tools | List of ToolCall objects representing expected tools |
| Trace/Span Parameter | Test Case Parameter | Description |
|---|---|---|
input | input | The input to your AI app |
output | actualOutput | The output of your AI app |
expectedOutput | expectedOutput | The expected output of your AI app |
retrievalContext | retrievalContext | List of retrieved text chunks from a retrieval system |
context | context | List of ideal retrieved text chunks |
toolsCalled | toolsCalled | List of ToolCall objects representing tools called |
expectedTools | expectedTools | List of ToolCall objects representing expected tools |
All parameters are optional — you only need to provide the ones required by the metrics in your collection.
Evaluate Spans Online
Provide a metric collection on the span's observe decorator/wrapper and set test case parameters via the update current span function:
from deepeval.tracing import observe, update_current_span
from openai import OpenAI
client = OpenAI()
@observe(metric_collection="My Collection")
def llm_app(query: str) -> str:
res = client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": query}]
).choices[0].message.content
update_current_span(input=query, output=res)
return res
llm_app("Write me a poem.")import { observe, updateCurrentSpan } from "deepeval/tracing";
const generate = async (prompt: string): Promise<string> => {
updateCurrentSpan({ input: prompt, output: "LLM response" });
return "LLM response";
};
const observedGenerate = observe({
type: "llm",
metricCollection: "My Collection",
fn: generate,
});Evaluate Traces Online
Similar to spans, but use the update_current_trace/updateCurrentTrace function to set both the metric collection and test case parameters on the trace.
from deepeval.tracing import observe, update_current_trace
from openai import OpenAI
client = OpenAI()
@observe()
def llm_app(query: str) -> str:
res = client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": query}]
).choices[0].message.content
update_current_trace(
input=query,
output=res,
metric_collection="My Collection"
)
return res
llm_app("Write me a poem.")import { observe, updateCurrentTrace } from "deepeval/tracing";
const generate = async (prompt: string): Promise<string> => {
updateCurrentTrace({
input: prompt,
output: "LLM response",
metricCollection: "My Collection",
});
return "LLM response";
};
const observedGenerate = observe({
type: "llm",
fn: generate,
});If you specify a metric collection but don't provide sufficient test case parameters for a metric, it will show up as an error on Confident AI but won't block or cause issues in your code.
Run Evals Offline
You can also trigger evaluations on traces and spans that have already been ingested. This is useful for re-evaluating with new metrics or running evals on historical data.
Evaluate a trace
from deepeval.tracing import evaluate_trace
evaluate_trace(trace_uuid="your-trace-uuid", metric_collection="Collection Name")import { evaluateTrace } from "deepeval/tracing";
await evaluateTrace({
traceUuid: "your-trace-uuid",
metricCollection: "Collection Name",
});Your trace must already contain the necessary test case parameters — you cannot update them when evaluating retrospectively.
Evaluate a span
from deepeval.tracing import evaluate_span
evaluate_span(span_uuid="your-span-uuid", metric_collection="Collection Name")import { evaluateSpan } from "deepeval/tracing";
await evaluateSpan({
spanUuid: "your-span-uuid",
metricCollection: "Collection Name",
});The metric collection you provide must be a single-turn collection.
Examples
Quick quiz: Given the code below, which metric collection will Confident AI use for the trace, and which for the span?
from deepeval.tracing import observe, update_current_span, update_current_trace
@observe()
def outer_function():
@observe(metric_collection="Collection 2")
def inner_function():
update_current_span(input="...", output="...")
update_current_trace(
input="...",
output="...",
metric_collection="Collection 1"
)import {
observe,
updateCurrentSpan,
updateCurrentTrace,
} from "deepeval/tracing";
const innerFunction = () => {
updateCurrentSpan({ input: "...", output: "..." });
updateCurrentTrace({ input: "...", output: "..." });
};
const observedInner = observe({
metricCollection: "Collection 2",
fn: innerFunction,
});
const outerFunction = () => {
observedInner();
};
const observedOuter = observe({
metricCollection: "Collection 1",
fn: outerFunction,
});Answer: "Collection 1" runs for the trace, and "Collection 2" runs for the span.
This is because:
- The trace-level metric collection is set via
update_current_trace(metric_collection=...), which can be called from any span - The inner function's
observedecorator sets"Collection 2"as the metric collection for that span - The
update_current_spancall updates the innermost active span (the "inner" span) - The
update_current_tracecall always updates the trace regardless of where it's called
Next Steps
Now that you can evaluate individual traces and spans, learn how to evaluate entire conversations.
Evaluate Threads
Run evaluations on multi-turn conversations and understand how thread evals differ from trace evals.
Customize Traces
Add tags, metadata, and user info to your traces for filtering and analysis.
Last updated on