Build GenAI metrics from spans using the Observe Agent
Private preview feature (Invite only)This feature is enabled per tenant by Observe by Snowflake and is not generally available. To participate, contact your Observe by Snowflake representative. You may access and use this preview feature solely for internal evaluation purposes. Previews are provided as-is. Your use of this preview is subject to the Snowflake Preview Terms located at https://www.snowflake.com/en/legal.
The Observe Agent can turn GenAI spans into GenAI metrics, such as model call duration and token usage, as the spans pass through it. The metrics follow the OpenTelemetry GenAI semantic conventions, so they read the same way as the metrics a GenAI SDK records itself.
The Observe GenAI SDKs for JavaScript and Python already record GenAI metrics, so apps that use them do not need this. Use it when an app uses some other instrumentation that writes GenAI spans but no metrics, and you still want those metrics in Observe by Snowflake.
Before you start
Verify the following conditions are met before you start.
- The spans must follow the GenAI semantic conventions. The agent only reads spans that have
gen_ai.operation.name, and takes token counts from thegen_ai.usage.*attributes. It ignores every other span. - The spans must go through the Observe Agent. Point the app's OTLP exporter at the agent, not straight at Observe by Snowflake. The endpoint for each setup is given below.
- Each call must be counted only once. If the instrumentation also records GenAI metrics, turn those off. If some services use the Observe GenAI SDK and others don't, use
include(see Options) to build metrics only for the ones that don't. - Versions. Kubernetes needs the
agentchart from observeinc/helm-charts, version 0.97.0 or later. A host install needs Observe Agent 2.19.0 or later.
Turn it on in Kubernetes
Add this to your Helm values:
application:
GenAIMetrics:
enabled: trueThen upgrade the release, keeping the values you already set:
helm upgrade observe-agent observe/agent -n observe --reuse-values -f genai-metrics-values.yamlSend the app's spans to the agent's forwarder service. With a release named observe-agent in the observe namespace, that is:
OTEL_EXPORTER_OTLP_ENDPOINT=http://observe-agent-forwarder.observe.svc.cluster.local:4318The metrics are built on the gateway when gatewayDeployment.enabled is true, and on the forwarder otherwise. They carry k8s.namespace.name and k8s.pod.name as well. Spans longer than node.forwarder.traces.maxSpanDuration (1 hour by default) are dropped, so they are not counted.
Turn it on for a host
Add this to the Observe Agent's configuration file. On Linux, that is /etc/observe-agent/observe-agent.yaml:
application:
genai_metrics:
enabled: trueThen restart the Observe Agent:
sudo systemctl restart observe-agentSend the app's spans to the Observe Agent on the same host:
OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318Options
The Helm chart and the Observe Agent's configuration file take the same options, spelled differently. In Helm they go under application.GenAIMetrics (see the chart's values reference); in the configuration file, under application.genai_metrics.
| Helm | Config file | Default | What it does |
|---|---|---|---|
enabled | enabled | false | Builds GenAI metrics from spans. |
includeConversationId | include_conversation_id | false | Adds gen_ai.conversation.id to the duration and token metrics. Every conversation gets its own series, so the number of series grows with the number of conversations. |
includeUserId | include_user_id | false | Adds user.id to the duration metrics. The number of series grows with the number of users. |
include | include | [] | Builds metrics only from spans that match at least one entry. Empty means every GenAI span. |
Each include entry sets a service name, an instrumentation scope name, or both. A span matches an entry when it has every field the entry sets. For example, to build metrics only for support-bot, and for the spans of one instrumentation in search-api:
# Helm
application:
GenAIMetrics:
enabled: true
include:
- serviceName: support-bot
- serviceName: search-api
instrumentationScopeName: <instrumentation scope name># Config file
application:
genai_metrics:
enabled: true
include:
- service_name: support-bot
- service_name: search-api
instrumentation_scope_name: <instrumentation scope name>Metrics it builds
This table summarizes the metrics built from GenAI spans:
| Metric | Type | Built from |
|---|---|---|
gen_ai.client.inference.duration | Histogram | Model calls: chat, generate_content, and text_completion spans. |
gen_ai.client.operation.duration | Histogram | Other client calls, such as embeddings, retrieval, and calls to remote agents. |
gen_ai.client.inference.usage.input_tokens | Counter | Model calls. Includes cached tokens. |
gen_ai.client.inference.usage.output_tokens | Counter | Model calls. Includes reasoning tokens. |
gen_ai.client.inference.usage.cache_read.input_tokens | Counter | Model calls. Input tokens served from the provider's cache. |
gen_ai.client.inference.usage.cache_write.input_tokens | Counter | Model calls. Input tokens written to the provider's cache. |
gen_ai.client.inference.usage.reasoning.output_tokens | Counter | Model calls. Output tokens spent on reasoning. |
gen_ai.client.inference.operation.input_tokens | Histogram | Model calls. Input tokens per call. |
gen_ai.client.inference.operation.output_tokens | Histogram | Model calls. Output tokens per call. |
gen_ai.execute_tool.duration | Histogram | Tool calls. |
gen_ai.invoke_agent.duration | Histogram | Agents that run inside the app. |
gen_ai.invoke_workflow.duration | Histogram | Workflow runs. |
Model call metrics are broken down by gen_ai.operation.name, provider, requested and returned model, server address and port, agent name, and, on the duration and per-call metrics, error.type. Every metric also carries service.name, service.namespace, service.version, and deployment.environment.name.
A model call span with no token counts adds 0 to the token metrics. Spans don't split token counts by input type, such as text or image, so gen_ai.token.modality is always unknown.
Updated 34 minutes ago