Annotate Traces from API
Annotations are currently in preview. The interface is subject to change.
Annotations in Agenta let you enrich the traces created by your LLM applications. You can add scores, comments, expected answers and other metrics to help evaluate your application's performance.
What You Can Do With Annotations
- Collect user feedback on LLM responses
- Run custom evaluation workflows
- Measure application performance in real-time
Understanding How Annotations Work
Annotations link to specific points (spans) in your application traces. You can link them to:
- Parent spans (like an agent invocation)
- Child spans (like a single LLM call within a larger process)
Each annotation can include:
- Numbers (scores, ratings)
- Categories (labels, classifications)
- Text (comments, reasoning)
Annotation Structure
Here's what an annotation looks like:
{
"data": {
"outputs": {
"score": 80,
"normalized_score": 0.8,
"reasoning": "The answer is mainly correct"
}
},
"references": {
"evaluator": {
"slug": "my_evaluator"
}
},
"links": {
"invocation": {
"trace_id": "47cb4a13b42a47bab2198509543ff14d",
"span_id": "1a9130f6ad398e55"
}
},
"metadata": {
"someattr": "somevalue"
}
}
An annotation has four main parts:
- Data: The actual evaluation content (scores, comments)
- References: Which evaluator to use (will be created automatically if it doesn't exist)
- Links: Which trace and span you're annotating
- Metadata (optional): Any extra information you want to include
What You Can Include in Annotations
Annotations are flexible - you can include whatever makes sense for your evaluation:
// Just a simple score
{"score": 3}
// A score with explanation
{"score": 3, "comment": "The response is not grounded"}
// Multiple metrics with reference information
{"score": 3, "normalized_score": 0.5, "comment": "The response is not grounded", "expected_answer": "The capital of France is Paris"}
You can use numbers, text, booleans, or categorical values.
Connecting Annotations to Traces
Each annotation links to a specific span in your trace using a trace_id
and span_id
. These IDs follow the OpenTelemetry format.
Agenta doesn't check if the trace and span IDs exist. Make sure they're valid before sending.
Working with Evaluators
Every annotation must reference an Evaluator
, which:
- Defines what your annotation should contain
- Provides context for interpreting the annotation
If you reference an evaluator that doesn't exist yet, Agenta will automatically create one based on your first annotation.
You can add multiple annotations from the same evaluator to a single span, which is useful for collecting feedback from different sources.
Using Metadata
The metadata
field lets you add extra information to your annotation. Use it to store things like user IDs, session information, or other context.
Creating Annotations
- Python
- JavaScript
import requests
base_url = "https://cloud.agenta.ai"
headers = {
"Content-Type": "application/json",
"Authorization": "ApiKey YOUR_API_KEY"
}
# Annotation data
annotation_data = {
"annotation": {
"data": {
"outputs": {
"score": 40,
"reasoning": "Response contains some inaccuracies",
"labels": ["Partially Correct", "Needs Improvement"]
}
},
"references": {
"evaluator": {
"slug": "content_quality"
}
},
"links": {
"invocation": {
"trace_id": "47cb4a13b42a47bab2198509543ff14d",
"span_id": "1a9130f6ad398e55"
}
}
}
}
# Make the API request
response = requests.post(
f"{base_url}/api/preview/annotations",
headers=headers,
json=annotation_data
)
# Process the response
if response.status_code == 200:
print("Annotation created successfully")
print(response.json())
else:
print(f"Error: {response.status_code}")
print(response.text)
async function createAnnotation() {
const baseUrl = 'https://cloud.agenta.ai';
const headers = {
'Content-Type': 'application/json',
'Authorization': 'ApiKey YOUR_API_KEY'
};
const annotationData = {
annotation: {
data: {
outputs: {
score: 40,
reasoning: "Response contains some inaccuracies",
labels: ["Partially Correct", "Needs Improvement"]
}
},
references: {
evaluator: {
slug: "content_quality"
}
},
links: {
invocation: {
trace_id: "47cb4a13b42a47bab2198509543ff14d",
span_id: "1a9130f6ad398e55"
}
}
}
};
try {
const response = await fetch(`${baseUrl}/api/preview/annotations`, {
method: 'POST',
headers: headers,
body: JSON.stringify(annotationData)
});
if (response.ok) {
const data = await response.json();
console.log("Annotation created successfully");
console.log(data);
} else {
console.error(`Error: ${response.status}`);
console.error(await response.text());
}
} catch (error) {
console.error("Request failed:", error);
}
}
createAnnotation();
If the evaluator doesn't exist yet, Agenta will create it automatically based on your first annotation.
Understanding the Response
When you create an annotation, you'll receive a response that includes the annotation data and its ID:
{
"annotation": {
"created_at": "2025-05-13T16:54:52.245664Z",
"created_by_id": "019315dc-a332-7ba5-a426-d079c43ab776",
"span_id": "a1713ea8f59291f6",
"trace_id": "bb39070937f74f169b60dfc420729ad3",
"kind": "custom",
"source": "api",
"data": {
"outputs": {
"score": 0,
"reasoning": "The answer is totally false.",
"expected_answer": "The capital of France is Paris",
"normalized_score": 0
}
},
"metadata": {
"user_id": "miamia"
},
"references": {
"evaluator": {
"id": "0196ca64-9cbf-7ee3-b507-2b559dd13090",
"slug": "main-scorer"
}
},
"links": {
"invocation": {
"span_id": "09383e1124b264d0",
"trace_id": "5d52a529b48135fd58790f8aa3e33fb8"
}
}
}
}
Annotations themselves are stored as traces with their own trace_id
and span_id
, which is different from the invocation they're linked to.
Viewing Annotations in the UI
You can see all annotations for a trace in the Agenta UI. Open any trace and check the Annotations tab to see detailed information. The right sidebar shows average metrics for each evaluator.
Querying Annotations
- Python
- JavaScript
You can query annotations in several ways:
1. Query by annotation ID:
import requests
base_url = "https://cloud.agenta.ai"
headers = {
"Content-Type": "application/json",
"Authorization": "ApiKey YOUR_API_KEY"
}
# Query by annotation ID
query_data = {
"annotation": {
"trace_id": "bb39070937f74f169b60dfc420729ad3",
"span_id": "a1713ea8f59291f6"
}
}
response = requests.post(
f"{base_url}/api/preview/annotations/query",
headers=headers,
json=query_data
)
if response.status_code == 200:
print("Query successful")
print(response.json())
else:
print(f"Error: {response.status_code}")
print(response.text)
2. Query all annotations for an invocation:
# Query all annotations for an invocation
query_data = {
"annotation": {
"links": {
"invocation": {
"trace_id": "5d52a529b48135fd58790f8aa3e33fb8",
"span_id": "09383e1124b264d0"
}
}
}
}
response = requests.post(
f"{base_url}/api/preview/annotations/query",
headers=headers,
json=query_data
)
You can query annotations in several ways:
1. Query by annotation ID:
async function queryAnnotation() {
const baseUrl = 'https://cloud.agenta.ai';
const headers = {
'Content-Type': 'application/json',
'Authorization': 'ApiKey YOUR_API_KEY'
};
// Query by annotation ID
const queryData = {
annotation: {
trace_id: "bb39070937f74f169b60dfc420729ad3",
span_id: "a1713ea8f59291f6"
}
};
try {
const response = await fetch(`${baseUrl}/api/preview/annotations/query`, {
method: 'POST',
headers: headers,
body: JSON.stringify(queryData)
});
if (response.ok) {
const data = await response.json();
console.log("Query successful");
console.log(data);
} else {
console.error(`Error: ${response.status}`);
console.error(await response.text());
}
} catch (error) {
console.error("Request failed:", error);
}
}
queryAnnotation();
2. Query all annotations for an invocation:
async function queryAnnotationsForInvocation() {
const baseUrl = 'https://cloud.agenta.ai';
const headers = {
'Content-Type': 'application/json',
'Authorization': 'ApiKey YOUR_API_KEY'
};
// Query all annotations for an invocation
const queryData = {
annotation: {
links: {
invocation: {
trace_id: "5d52a529b48135fd58790f8aa3e33fb8",
span_id: "09383e1124b264d0"
}
}
}
};
try {
const response = await fetch(`${baseUrl}/api/preview/annotations/query`, {
method: 'POST',
headers: headers,
body: JSON.stringify(queryData)
});
if (response.ok) {
const data = await response.json();
console.log("Query successful");
console.log(data);
} else {
console.error(`Error: ${response.status}`);
console.error(await response.text());
}
} catch (error) {
console.error("Request failed:", error);
}
}
queryAnnotationsForInvocation();
Removing Annotations
- Python
- JavaScript
import requests
base_url = "https://cloud.agenta.ai"
headers = {
"Content-Type": "application/json",
"Authorization": "ApiKey YOUR_API_KEY"
}
# Define the trace_id and span_id of the annotation to delete
trace_id = "bb39070937f74f169b60dfc420729ad3"
span_id = "a1713ea8f59291f6"
# Make the delete request
response = requests.delete(
f"{base_url}/api/preview/annotations/{trace_id}/{span_id}",
headers=headers
)
# Process the response
if response.status_code == 200:
print("Annotation deleted successfully")
else:
print(f"Error: {response.status_code}")
print(response.text)
async function deleteAnnotation() {
const baseUrl = 'https://cloud.agenta.ai';
const headers = {
'Content-Type': 'application/json',
'Authorization': 'ApiKey YOUR_API_KEY'
};
// Define the trace_id and span_id of the annotation to delete
const traceId = "bb39070937f74f169b60dfc420729ad3";
const spanId = "a1713ea8f59291f6";
try {
const response = await fetch(`${baseUrl}/api/preview/annotations/${traceId}/${spanId}`, {
method: 'DELETE',
headers: headers
});
if (response.ok) {
console.log("Annotation deleted successfully");
} else {
console.error(`Error: ${response.status}`);
console.error(await response.text());
}
} catch (error) {
console.error("Request failed:", error);
}
}
deleteAnnotation();