Implementation:Arize ai Phoenix Add Span Annotation
| Knowledge Sources | |
|---|---|
| Domains | AI Observability, Quality Assessment, Span Evaluation |
| Last Updated | 2026-02-14 00:00 GMT |
Overview
Concrete tool for attaching a single structured annotation or free-text note to a traced span, provided by the arize-phoenix-client package.
Description
The Spans.add_span_annotation() method creates a single annotation on a span identified by its OpenTelemetry span ID. It constructs a SpanAnnotationData dictionary internally (via the _create_span_annotation() helper) and delegates to log_span_annotations(span_annotations=[anno]) for the actual API call. The annotation consists of a name, annotator kind, and a result containing any combination of label, score, and explanation.
The Spans.add_span_note() method is a specialized variant for adding free-text notes to spans. Unlike regular annotations, notes support multiple entries per span through auto-generated timestamp-based identifiers. Notes are posted to a dedicated v1/span_notes endpoint.
Both methods operate on the Spans sub-client, which is accessed through client.spans.
Usage
Use add_span_annotation() when:
- Attaching a single quality assessment (label, score, or explanation) to a specific span.
- Performing human review and recording judgments one span at a time.
- Running an LLM judge that evaluates spans individually.
- Upserting an annotation (same
span_id+annotation_name+identifiercombination updates the existing record).
Use add_span_note() when:
- Adding free-text commentary to a span without structured label or score.
- Recording multiple observations on the same span (each note gets a unique identifier).
Code Reference
Source Location
- Repository: Phoenix
- File:
packages/phoenix-client/src/phoenix/client/resources/spans/__init__.py - Lines: 605-721 (add_span_annotation), 723-775 (add_span_note)
Signature
def add_span_annotation(
self,
*,
span_id: str,
annotation_name: str,
annotator_kind: Literal["LLM", "CODE", "HUMAN"] = "HUMAN",
label: Optional[str] = None,
score: Optional[float] = None,
explanation: Optional[str] = None,
metadata: Optional[dict[str, Any]] = None,
identifier: Optional[str] = None,
sync: bool = False,
) -> Optional[InsertedSpanAnnotation]:
...
def add_span_note(
self,
*,
span_id: str,
note: str,
) -> InsertedSpanAnnotation:
...
Import
from phoenix.client import Client
client = Client()
# Access via: client.spans.add_span_annotation(...)
# Access via: client.spans.add_span_note(...)
I/O Contract
Inputs (add_span_annotation)
| Name | Type | Required | Description |
|---|---|---|---|
| span_id | str |
Yes | The OpenTelemetry span ID of the span to annotate. |
| annotation_name | str |
Yes | The name of the annotation dimension (e.g., "relevance", "toxicity", "correctness"). |
| annotator_kind | Literal["LLM", "CODE", "HUMAN"] |
No | The source of the annotation. Defaults to "HUMAN".
|
| label | Optional[str] |
No | A categorical label for the annotation (e.g., "positive", "hallucinated"). At least one of label, score, or explanation must be provided. |
| score | Optional[float] |
No | A numerical score for the annotation. At least one of label, score, or explanation must be provided. |
| explanation | Optional[str] |
No | A free-text explanation of the annotation result. At least one of label, score, or explanation must be provided. |
| metadata | Optional[dict[str, Any]] |
No | Arbitrary key-value metadata associated with the annotation. |
| identifier | Optional[str] |
No | Deduplication key. Annotations are uniquely identified by (span_id, annotation_name, identifier). A None identifier is equivalent to "". Use non-empty identifiers to store multiple annotations with the same name on the same span.
|
| sync | bool |
No | If True, the request is processed synchronously and returns the inserted annotation with its ID. If False (default), the request is queued asynchronously and returns None.
|
Inputs (add_span_note)
| Name | Type | Required | Description |
|---|---|---|---|
| span_id | str |
Yes | The OpenTelemetry span ID of the span to annotate. Must not be empty after stripping whitespace. |
| note | str |
Yes | The text content of the note. Must not be empty after stripping whitespace. |
Outputs
| Name | Type | Description |
|---|---|---|
| (add_span_annotation return) | Optional[InsertedSpanAnnotation] |
When sync=True, returns an InsertedSpanAnnotation dict containing the id of the created/updated annotation. When sync=False, returns None.
|
| (add_span_note return) | InsertedSpanAnnotation |
Always returns synchronously with the id of the created note annotation.
|
Usage Examples
Add a Human Annotation with Label and Score
from phoenix.client import Client
client = Client()
annotation = client.spans.add_span_annotation(
span_id="051581bf3cb55c13",
annotation_name="relevance",
annotator_kind="HUMAN",
label="relevant",
score=0.95,
explanation="The response directly addresses the user's question.",
sync=True,
)
print(f"Created annotation with ID: {annotation['id']}")
Add an LLM Judge Annotation
from phoenix.client import Client
client = Client()
# Asynchronous (fire-and-forget) annotation from an LLM evaluator
client.spans.add_span_annotation(
span_id="a1b2c3d4e5f67890",
annotation_name="hallucination",
annotator_kind="LLM",
label="no_hallucination",
score=0.1,
explanation="All claims in the response are supported by the retrieved context.",
metadata={"model": "gpt-4o", "prompt_version": "v2"},
)
Add Multiple Annotations with Identifiers
from phoenix.client import Client
client = Client()
# Two different annotators can create separate annotations with the same name
client.spans.add_span_annotation(
span_id="051581bf3cb55c13",
annotation_name="correctness",
annotator_kind="HUMAN",
label="correct",
score=1.0,
identifier="reviewer-alice",
)
client.spans.add_span_annotation(
span_id="051581bf3cb55c13",
annotation_name="correctness",
annotator_kind="HUMAN",
label="partially_correct",
score=0.7,
identifier="reviewer-bob",
)
Add a Free-Text Note
from phoenix.client import Client
client = Client()
result = client.spans.add_span_note(
span_id="051581bf3cb55c13",
note="This span shows unusually high latency due to rate limiting.",
)
print(f"Note created with ID: {result['id']}")