Skip to content
Development
Agent

filter-expressions

A filter condition is a Python boolean expression that Phoenix compiles to SQL. There are three filter languages, one each for spans, traces, and sessions, and the argument name picks the language. The vocabularies do not mix.

From plugin
phoenix
12k7 skills7 agents1 MCP
Install
> /plugin marketplace add arize-ai/phoenix
> /plugin install arize-phoenix@arize-phoenix

How it fires

How this agent gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.

Context preview

The summary Claude sees to decide when to auto-load this agent.

A filter condition is a Python boolean expression that Phoenix compiles to SQL. There are three filter languages, one each for spans, traces, and sessions, and the argument name picks the language. The vocabularies do not mix.

Agent definition

filter-expressions.md

Filter expressions

A filter condition is a Python boolean expression that Phoenix compiles to SQL. There are three filter languages, one each for spans, traces, and sessions, and the argument name picks the language. The vocabularies do not mix.

| Language | Argument | Keeps | Accepted on | | --- | --- | --- | --- | | Span filter | `filterCondition` | individual spans | `Project.spans`, `Trace.spans`, project aggregates (`recordCount`, `tokenCountTotal`, `costSummary`, `latencyMsQuantile`, ...), `SpanQuery().where(...)` in the Python client, the UI spans filter bar | | Trace filter | `traceFilterCondition` | every span of a matching trace | `Project.spans`, the UI traces filter bar, the `filter` query param on `GET /v1/projects/{id}/traces` | | Session filter | `sessionFilterCondition` | sessions | `Project.sessions`, project aggregates, the UI sessions filter bar |

`filterCondition` and `traceFilterCondition` compose on `Project.spans`: matching spans inside matching traces. `filterCondition` and `sessionFilterCondition` are mutually exclusive on the aggregates that accept both.

Checking a condition

| Need | GraphQL field on `Project` | | --- | --- | | Validate before running | `validateSpanFilterCondition(condition:)`, `validateTraceFilterCondition(condition:)`, `validateSessionFilterCondition(condition:)` → `{ isValid errorMessage }` | | List bindable names | `traceFilterVocabulary`, `sessionFilterVocabulary` → `{ name type category description iterableName }` | | Span filter names | no vocabulary field; the table under [Span filter](#span-filter) is exhaustive |

A span filter that returns no rows may be misspelled rather than unmatched. Check the spelling against the vocabulary before concluding there is no data.

Syntax shared by all three languages

Operators

| Category | Accepted | Rejected | | --- | --- | --- | | Comparison | `==` `!=` `<` `<=` `>` `>=`; chained: `500 < latency_ms <= 2000` | `=` | | Missing values | `is None`, `is not None` | `is null`, `null`, `is` with any other value | | Membership | `x in [...]` exact; `'text' in field` case-insensitive substring; `not in` | `None` inside a list; a literal on the left (`1 in [1, 2]`); `like` | | Logic | `and`, `or`, `not`, parentheses | `&&`, `\|\|`, `&`, `\|`, `!` | | Arithmetic | `+` `-` `*` `/` `%` | `**`, `//`, bitwise operators | | Casts | `float(x)`, `int(x)`, `str(x)` | `bool(x)`, any other function | | Comprehensions | `any`, `all`, `len`, `sum`, `max`, `min` over a declared collection: `any(d.cost > 0 for d in cost_details)`; `len` takes a list comprehension | method calls (`name.startswith(...)`), `len(name)` |

Literals

| Kind | Write | Not | | --- | --- | --- | | String | `'LLM'` or `"LLM"` | unquoted: `LLM` | | Number | `100`, `0.5` | quoted: `'100'` | | Boolean | `True`, `False` | `true`, `false` | | Missing | `None` | `null`, `nil` | | Datetime | ISO 8601 with an offset: `'2026-09-01T00:00:00Z'`, `'2026-09-01T00:00:00+00:00'` | without an offset | | List | `['LLM', 'TOOL']`, same type throughout | mixed types; `None` elements |

Rules that differ from Python

  • A missing value fails every comparison, including `!=`. A span without `metadata['tier']`

matches neither `metadata['tier'] == 'premium'` nor `metadata['tier'] != 'premium'`. Spell the missing case out: `metadata['tier'] != 'premium' or metadata['tier'] is None`.

  • `is None` is true for an absent key and for a stored JSON `null`.
  • Every operand of `and`, `or`, and `not` must itself be a condition. `name == 'x' and

metadata['flag']` is rejected; write `metadata['flag'] == True`.

  • The whole expression must be a condition. A bare `True` or a bare field is rejected.
  • `'text' in field` ignores case. `==` and list membership are exact.
  • Annotation accessors expose `.label` (string), `.score` (number), `.explanation` (string). The

bare accessor is an existence check: `annotations['quality']`.

Span filter

Root spans

There is no `traces` connection and no root-span argument. Root-span scoping is a clause in `filterCondition`, and it composes with everything else. The UI's traces table is `spans(filterCondition: "parent_span is None", traceFilterCondition: ...)`.

| Clause | Keeps | Use when | | --- | --- | --- | | `parent_id is None` | spans with no parent id | the default | | `parent_span is None` | those, plus orphans whose parent span was never received | you want every top-level span, including ones whose parent was dropped |

A root span is usually one per trace. Fragmented traces can have several.

Vocabulary

This table is exhaustive. Every identifier not in it is read as an attribute path.

| Name | Type | Notes | | --- | --- | --- | | `span_id`, `trace_id`, `parent_id` | string | OpenTelemetry hex ids | | `name` | string | span name | | `span_kind` | enum | `'CHAIN'`, `'LLM'`, `'RETRIEVER'`, `'EMBEDDING'`, `'TOOL'`, `'AGENT'`, `'RERANKER'`, `'GUARDRAIL'`, `'EVALUATOR'`, `'PROMPT'`, `'UNKNOWN'`; literals are uppercased for you | | `status_code` | enum | `'OK'`, `'ERROR'`, `'UNSET'`; literals are uppercased for you | | `status_message` | string | error text | | `latency_ms` | number | | | `start_time`, `end_time` | datetime | | | `cumulative_llm_token_count_prompt`, `cumulative_llm_token_count_completion`, `cumulative_llm_token_count_total` | number | this span plus its descendants | | `llm.token_count.prompt`, `llm.token_count.completion`, `llm.token_count.total` | number | this span alone | | `total_cost`, `prompt_cost`, `completion_cost` | number | `0` when the span has no cost row | | `cost_details` | collection | iterable only; elements have `token_type` (string), `is_prompt` (boolean), `cost`, `tokens`, `cost_per_token` (number) | | `parent_span` | reserved | only `is None` / `is not None`; `parent_span.name` is rejected | | `annotations['name']` | annotation | on the span itself; `evals['name']` is a legacy alias | | `trace_annotations['name']` | annotation | on the span's containing trace | | `a

Read more
Ships withphoenix

AI Observability & Evaluation

Get the whole plugin

Other agents on phoenix.