Skip to main content
skill CX Codex

dt-obs-genai — Claude Code skill: what it does, how to install

Generates Dynatrace DQL queries to monitor GenAI apps and agents. It helps track token consumption, p95 latencies, tool call errors, and blocked responses.

Install into Claude Code · Skills CLI (Vercel) All methods and platforms ↓
npx skills add Dynatrace/dynatrace-for-ai --skill dt-obs-genai -a claude-code
Open on GitHub

What dt-obs-genai does

The skill queries Dynatrace Grail using DQL over OpenTelemetry gen_ai.* spans. It inspects request throughput, p95 latency, error rates via span.status_code == "error", and token saturation. This reveals provider throttling, rate limits, and slow model responses across your deployments.

It also handles agent execution paths and conversation analytics. The skill looks for failing tool calls, agent loops, and finish reasons like token limits or safety filter blocks. Full session rollups depend strictly on your app emitting gen_ai.conversation.id. If your traces omit this attribute, session-level grouping fails.

Dynatrace does not store monetary costs directly. It records input and output token counts, meaning cost estimates require multiplying token sums by your specific model pricing. Note that this skill ignores standard system logs and non-GenAI service traces, which belong to separate Dynatrace skills.

What it helps with

  • inspect p95 latency and error rates across LLM models
  • calculate token throughput and consumption by provider
  • locate failing agent tool calls and runaway loops
  • aggregate token usage by individual conversation sessions
  • check finish reasons for safety-filtered or truncated outputs

Example prompts for the agent

  • What is my LLM error rate and p95 latency by model in the last 24 hours?
  • Break down token usage and cost by model and provider in the last 24 hours.
  • Show me failing agent tool calls in the last 24 hours.

How to install dt-obs-genai

1Skills CLI (Vercel)

npx skills add Dynatrace/dynatrace-for-ai --skill dt-obs-genai -a claude-code

2Manually: put the files in place

~/.claude/skills/dt-obs-genai/SKILL.md

Copy the whole skill folder; for one project use .claude/skills/dt-obs-genai/

Step by step, with commands for each platform: How to install skills in Claude Code: five ways

Questions about dt-obs-genai

How to install dt-obs-genai in Claude Code?

Run npx skills add Dynatrace/dynatrace-for-ai --skill dt-obs-genai -a claude-code. This installs the skill directly into your Claude Code environment.

What does dt-obs-genai do?

It writes DQL queries to analyze OpenTelemetry gen_ai.* spans stored in Dynatrace. You can evaluate token usage, golden signals, agent tool failures, and guardrail triggers.

How does dt-obs-genai calculate GenAI costs?

Dynatrace stores gen_ai.usage.input_tokens and gen_ai.usage.output_tokens but no native dollar amount. Cost estimates are derived by multiplying those token totals by model pricing that you supply.

Source text

SKILL.md · Apache-2.0
---
name: dt-obs-genai
description: >-
  Analyze & debug GenAI/LLM apps: token cost & caching by prompt, model &
  provider; latency/errors; agent & tool loops/failures; conversations;
  guardrails; evaluations; OpenTelemetry/dt-evals setup.
license: Apache-2.0
---

# AI Observability (GenAI) Skill

Analyze AI Observability signals from customer GenAI applications using DQL — golden
signals, LLM signals, token and cost analytics (with usage attribution and prompt-caching
economics), agent signals (including loop/runaway detection and Smartscape topology),
conversation/session-level analytics, guardrails, and evaluation quality.

## When to Use

Use this skill for observability questions about customer GenAI applications — anything
reading OpenTelemetry GenAI spans (`gen_ai.*`) or LLM evaluation bizevents. Example triggers:

- "LLM latency", "error rate by model"
- "token usage by model", "token throughput / TPM", "am I hitting rate limits", "provider throttling or 429s"
- "cost by model and provider", "who is driving token spend", "do I have prompt caching"
- "cost per conversation", "most expensive sessions"
- "failing agent tool calls", "find runaway agents"
- "responses truncated or blocked", "finish reasons"
- "failed evaluations", "quality scores"

## When NOT to Use

Davis CoPilot/MCP telemetry (dt-platform), generic service metrics (dt-obs-services),
logs (dt-obs-logs), or non-GenAI distributed tracing (dt-obs-tracing).

## Example Questions

When suggesting follow-up questions (e.g., "give me one example question per topic"),
use these canonical, ready-to-ask phrasings — one per capability. Keep suggestions
single-clause and avoid the literal phrase "content filter" (use "blocked or
safety-filtered" instead); overly long, multi-clause questions can be rejected.

- **Traffic, errors & latency:** What is my LLM error rate and p95 latency by model in the last 24 hours?
- **Token usage & cost:** Break down token usage and cost by model and provider in the last 24 hours.
- **Agent & tool activity:** Show me failing agent tool calls in the last 24 hours.
- **Conversation analytics:** What are my top 10 most expensive conversations by total token usage in the last 24 hours?
- **Guardrails:** How many responses were blocked or truncated in the last 7 days, and which model was affected most?
- **Evaluation quality:** Show me failed evaluations from the last 24 hours with the judge's explanation and the question-answer pair.

---

## Core Capabilities

### Golden Signals

The four classic observability signals — traffic, errors, latency, and saturation — apply
directly to GenAI applications. Traffic is request throughput over time; errors are spans
where `span.status_code == "error"`; latency is the `duration` field (a Grail duration
value — divide by the `1ms` literal, `duration / 1ms`, for a numeric millisecond value);
saturation is proxied by total token throughput per minute (input + output tokens combined).

```dql
fetch spans, from: now()-24h
| filter isNotNull(gen_ai.request.model)
| summarize total = count(), errors = countIf(span.status_code == "error"), by: {gen_ai.request.model}
| fieldsAdd error_rate_pct = if(total > 0, errors * 100.0 / total, else: 0.0)
| sort error_rate_pct desc
```

→ **Full traffic, latency, and saturation queries:** See [references/golden-signals.md](references/golden-signals.md)

### LLM Signals

LLM signals describe which model and provider served each request, what operation type
was invoked (`chat`, `execute_tool`, `invoke_agent`, `create_agent`), and how tokens were
consumed. Use these to benchmark provider latency, compare model performance, and
understand the token distribution across model-provider combinations.

```dql
fetch spans, from: now()-24h
| filter isNotNull(gen_ai.request.model)
| summarize p95_ms = percentile(duration, 95) / 1ms, requests = count(), by: {gen_ai.provider.name}
| sort p95_ms desc
```

→ **Slowest models, token usage by model:** See [references/llm-signals.md](references/llm-signals.md)

### Cost and Tokens

Token consumption is the primary cost driver. Dynatrace stores `gen_ai.usage.input_tokens`
and `gen_ai.usage.output_tokens` on every span — there is no stored cost field; estimated
cost must be derived by multiplying token sums by the per-model price you supply. Use these
queries to identify the highest-spend model-provider combinations and detect token-burn spikes.

```dql
fetch spans, from: now()-24h
| filter isNotNull(gen_ai.usage.input_tokens) or isNotNull(gen_ai.usage.output_tokens)
| summarize input_tokens = sum(gen_ai.usage.input_tokens), output_tokens = sum(gen_ai.usage.output_tokens), total_tokens = sum(gen_ai.usage.input_tokens) + sum(gen_ai.usage.output_tokens), by: {gen_ai.provider.name, gen_ai.request.model}
| sort total_tokens desc
```

→ **Token spikes, cost estimation, most expensive prompts, usage attribution, prompt-caching economics:** See [references/cost-and-tokens.md](references/cost-and-tokens.md)

### Agent Signals

GenAI agents emit spans for each tool invocation (`execute_tool`), agent step
(`invoke_agent`), and agent creation (`create_agent`). Use agent signals to identify
which tools are called most often and which agents are failing. For *structural*
questions — which agents, models, and providers exist and how they connect — query the
GenAI Smartscape entities (`GENAI_AGENT`, `GENAI_MODEL`, `GENAI_PROVIDER`, `GENAI_SERVICE`)
instead of scanning spans; this is a feature-flag-gated preview.

```dql
fetch spans, from: now()-24h
| filter isNotNull(gen_ai.agent.name)
| summarize total = count(), errors = countIf(span.status_code == "error"), by: {gen_ai.agent.name}
| fieldsAdd error_rate_pct = if(total > 0, errors * 100.0 / total, else: 0.0)
| sort errors desc
```

→ **Tool usage, failing agents, agent step latency, loop/runaway detection, Smartscape topology:** See [references/agent-signals.md](references/agent-signals.md)

### Conversation Analytics

Per-span and per-trace signals measure one request or one turn. When the application
propagates `gen_ai.conversation.id`, you can roll spans up to the **session** level — cost
per conversation, how deep conversations run, and which sessions are runaway-expensive or
error-prone. This is the unit that matters for chargeback and user-perceived reliability.

```dql
fetch spans, from: now()-24h
| filter isNotNull(gen_ai.conversation.id)
| summarize turns = countDistinct(trace.id), total_tokens = sum(gen_ai.usage.input_tokens) + sum(gen_ai.usage.output_tokens), errors = countIf(span.status_code == "error"), by: {gen_ai.conversation.id}
| sort total_tokens desc
```

→ **Cost/depth per conversation, session error rate:** See [references/conversation-analytics.md](references/conversation-analytics.md)

### Guardrails

Guardrails surface as `gen_ai.response.finish_reasons` on the span — `content_filter` means a
safety filter blocked or redacted output, `length` means the response was truncated at the
token limit — and as the proactive safety evaluators (`prompt-injection`, `pii-leakage`,
`toxicity`, `bias`) in the evaluation bizevents. Use these to quantify blocked and truncated
responses and tie them back to the LLM-judge safety verdicts.

```dql
fetch spans, from: now()-24h
| filter isNotNull(gen_ai.response.finish_reasons)
| fieldsAdd finish_reason = gen_ai.response.finish_reasons
| expand finish_reason
| summarize calls = count(), by: {finish_reason, gen_ai.request.model}
| sort calls desc
```

→ **Blocked (safety-filtered), truncated (length), finish-reason breakdown:** See [references/guardrails.md](references/guardrails.md)

### Evaluation Quality

Evaluation results are captured as **bizevents** (not spans) with
`event.type == "gen_ai.evaluation.result"`. Each evaluator emits one bizevent per
response, carrying the score, pass/fail label, explanation, and the exact Q&A pair.
Use evaluation queries to monitor quality dimensions and surface failed responses
with the LLM judge's reasoning. Each bizevent also carries the `trace.id` of the run that
produced the evaluated response, so you can pivot from a quality failure to the spans that
caused it.

```dql
fetch bizevents, from: now()-24h
| filter event.type == "gen_ai.evaluation.result"
| filter gen_ai.evaluation.score.label == "fail"
| fields timestamp, gen_ai.evaluation.name, gen_ai.evaluation.score.value, gen_ai.evaluation.explanation, gen_ai.evaluation.input.question, gen_ai.evaluation.input.answer
| sort timestamp desc
```

→ **Quality scores, failed evaluations, fail rates:** See [references/evaluations.md](references/evaluations.md)

### Empty-State Check

When any signal query returns no rows, do not report "no data found" — first confirm
whether the application sends GenAI telemetry at all. These two presence checks show
which signal families are present:

```dql
fetch spans, from: now()-24h
| summarize 
    has_genai = countIf(isNotNull(gen_ai.request.model)), 
    has_tokens = countIf(isNotNull(gen_ai.usage.input_tokens) or isNotNull(gen_ai.usage.output_tokens)), 
    has_agents = countIf(isNotNull(gen_ai.agent.name)), 
    has_tools = countIf(gen_ai.operation.name == "execute_tool"), 
    has_conversation = countIf(isNotNull(gen_ai.conversation.id)), 
    has_finish_reason = countIf(isNotNull(gen_ai.response.finish_reasons)), 
    has_cached_tokens = countIf(isNotNull(gen_ai.usage.cache_read.input_tokens) or isNotNull(gen_ai.usage.cache_creation.input_tokens)),
    total = count()
    
```

```dql
fetch bizevents, from: now()-24h
| filter event.type == "gen_ai.evaluation.result"
| summarize evals = count()
```

If `has_genai` is zero, report that the application appears not to be instrumented for AI
Observability yet — not "no data found". If `has_genai` is non-zero but a specific family
(`has_tokens`, `has_agents`, `has_tools`, `has_conversation`, `has_finish_reason`,
`has_cached_tokens`, `evals`) is zero, only that signal type is missing — for example
`has_conversation == 0` means session-level analytics are unavailable because the app does
not propagate a conversation id, and `has_cached_tokens == 0` means prompt-caching telemetry
is not being reported. These optional families may use different attribute names depending on
the provider/SDK; verify before reporting them absent.

---

## Agent Instructions

### Act First, Refine Later

When a user asks for analysis, proceed immediately with sensible defaults. Do not ask
for parameter values you can reasonably assume.

**Default values when not specified:**

| Parameter | Default | Rationale |
|-----------|---------|-----------|
| Timeframe | Last 24 h (`from: now()-24h`) | Covers a full operational day without being too narrow |
| Model scope | All models (no model filter) | Shows the full picture; user can narrow after seeing results |
| Provider scope | All providers | Same rationale as model scope |
| Token threshold | None | Show all — let the data reveal the outliers |

**Exception — cost prices.** Per-model prices are the one input you cannot
default (there is no cost field in the data). Ask the user for them before
estimating USD; never use prices from memory. See [cost-and-tokens.md](references/cost-and-tokens.md).

### Empty-State Rule

When any signal query returns no rows, run the two presence checks in the
**Empty-State Check** capability above before responding — never reply "no data found".
If `has_genai` is zero, report that the application appears not to be instrumented for AI
Observability yet; if only a specific family is zero, say which signal type is missing.

### Scope Boundary

This skill covers AI Observability signals for **customer GenAI applications** only.
Product documentation and configuration how-to questions (e.g., "How do I configure
the Dynatrace OTLP endpoint?") go to `ask-dynatrace-docs` — this skill does not
contain product configuration how-tos.

### Understanding User Intent

**Map user requests and prompt-starter phrasings to capabilities:**

| User Request / Prompt Starter | Capability | Reference File |
|-------------------------------|------------|----------------|
| "Understand AI Observability signals" | All signal categories overview | This SKILL.md |
| "Analyze LLM latency and errors", "LLM errors", "error rate by model" | Golden Signals | [golden-signals.md](references/golden-signals.md) |
| "Which models are slowest right now?", "compare latency across providers" | LLM Signals | [llm-signals.md](references/llm-signals.md) |
| "Show token usage by model", "token usage spikes" | Cost and Tokens | [cost-and-tokens.md](references/cost-and-tokens.md) |
| "Break down cost by model and provider", "which prompts are most expensive?" | Cost and Tokens | [cost-and-tokens.md](references/cost-and-tokens.md) |
| "Trace a failing agent run", "show failed tool calls" | Agent Signals | [agent-signals.md](references/agent-signals.md) |
| "Break down agent steps by latency" | Agent Signals | [agent-signals.md](references/agent-signals.md) |
| "Map agent topology", "which models does this agent use?", "list GenAI agents/models/providers" | Agent Signals (Smartscape) | [agent-signals.md](references/agent-signals.md) |
| "Is an agent stuck in a loop?", "find runaway agents", "what caused the token spike?" | Agent Signals (loops) | [agent-signals.md](references/agent-signals.md) |
| "Cost per conversation", "most expensive sessions", "how deep do conversations run?" | Conversation Analytics | [conversation-analytics.md](references/conversation-analytics.md) |
| "Stitch together an agent trajectory", "filter by session id", "connect traces across a session" | Conversation Analytics | [conversation-analytics.md](references/conversation-analytics.md) |
| "How often are responses blocked/filtered?", "are responses being truncated?", "finish reasons" | Guardrails | [guardrails.md](references/guardrails.md) |
| "Cost by application/user/tenant", "who is driving token spend?" | Cost and Tokens (attribution) | [cost-and-tokens.md](references/cost-and-tokens.md) |
| "Do I have prompt caching?", "cache hit rate", "caching savings" | Cost and Tokens (caching) | [cost-and-tokens.md](references/cost-and-tokens.md) |
| "Summarize evaluation quality scores", "show low-scoring responses", "show failed evaluations" | Evaluation Quality | [evaluations.md](references/evaluations.md) |
| "What signals am I missing?", "why is there no data?" | Empty-State Check | This SKILL.md |

---

## Common Workflows

### Workflow: Cost Investigation

```
1. Run token usage by model and provider (cost-and-tokens.md → "Token usage by model and provider")
2. Identify the top model-provider combinations by total_tokens
3. For the top offenders, run token usage spikes to check for abnormal time windows
4. Use the cost-estimation template in "Most expensive prompts and models" to estimate USD spend — ask the user for per-model prices first (see "Exception — cost prices" under Agent Instructions)
5. Check for prompt-size outliers: high input_tokens / output_tokens ratio indicates large context windows
6. Attribute spend to a consumer (cost-and-tokens.md → "Usage attribution") and check whether prompt caching is enabled and effective (cost-and-tokens.md → "Prompt caching economics")
```

### Workflow: Token-Spike / Runaway Investigation

```
1. Run token usage spikes (cost-and-tokens.md → "Token usage spikes") to find the abnormal time window
2. Within that window, run repeated-tool-calls and runaway-turn queries (agent-signals.md → "Agent loops and runaway detection")
3. For a flagged trace.id, open the trace to see what the agent looped on
4. If conversation ids are present, re-run the loop query grouped by gen_ai.conversation.id to catch cross-turn loops (conversation-analytics.md)
```

### Workflow: Failing Agent Run

```
1. Run failing agent activity query (agent-signals.md → "Failing agent activity")
2. Sort by errors desc to find the most error-prone agent
3. Take the trace.id from a failing span and open in Dynatrace distributed-tracing view
4. Check agent steps by latency (agent-signals.md) to see which operation type is slowest
```

### Workflow: Guardrail & Safety Review

```
1. Run the guardrails presence check (guardrails.md) to confirm finish reasons are recorded
2. Run the finish-reason breakdown, then the blocked (content_filter) and truncated (length) queries
3. Correlate safety-filter spikes with the prompt-injection evaluator and truncation with answer-completeness failures (evaluations.md)
4. For a specific block or failure, take the trace.id and pivot to the originating spans (evaluations.md → "Correlating evaluations to traces")
```

### Workflow: Evaluation Review

```
1. Run evaluation quality scores (evaluations.md → "Evaluation quality scores") to rank evaluators by avg_score asc
2. Focus on the lowest-scoring evaluator
3. Run failed evaluations (evaluations.md → "Failed evaluations") to surface the exact Q&A pairs and LLM judge explanations
4. Use the "Fail rate by evaluator" query to see how many responses fail each evaluator and the share of total evaluations
5. To root-cause a specific failure, take its trace.id and pivot to the originating spans (evaluations.md → "Correlating evaluations to traces")
```

---

## References

- [references/golden-signals.md](references/golden-signals.md) — traffic, errors, latency, saturation
- [references/llm-signals.md](references/llm-signals.md) — slowest models, provider latency, token usage by model
- [references/cost-and-tokens.md](references/cost-and-tokens.md) — token usage, spikes, cost-estimation template, usage attribution, prompt-caching economics
- [references/agent-signals.md](references/agent-signals.md) — tool usage, failing agents, step latency, loop/runaway detection, Smartscape agent topology (preview)
- [references/conversation-analytics.md](references/conversation-analytics.md) — session-level cost, depth, and error rate (`gen_ai.conversation.id`)
- [references/guardrails.md](references/guardrails.md) — blocked (safety-filtered) and truncated (length) responses via finish reasons
- [references/evaluations.md](references/evaluations.md) — quality scores, failed evals, fail-rate, trace correlation (bizevents)

© Dynatrace · Apache-2.0 · Dynatrace/dynatrace-for-ai

Similar skills

Everything for AI and ML →

From the same set29

dt-obs-gcp A skill that writes DQL queries against GCP resources in Dynatrace Smartscape: inventory, topology, access audits. Useful when you need to see what actually runs in GCP. CX Codex +1 2,000installs dt-obs-azure Teaches AI assistants to query Azure infrastructure via Dynatrace Smartscape and DQL. Useful for auditing security rules, tracking resources, and checking network topology. CX Codex +1 2,000installs dt-alerting A skill that walks the whole Dynatrace alerting lifecycle: picking an anomaly detector, storing events in Grail, grouping problems and routing notifications. CX Codex +1 1,800installs dt-js-runtime A skill that documents the Dynatrace server-side JS runtime: function contract, sandbox limits, available Web APIs and Node.js modules. Useful when writing code for the AppEngine sandbox. CX Codex +1 1,700installs dt-sec-insights A skill that writes DQL queries against Dynatrace security data: vulnerabilities, runtime attacks, compliance posture and scan coverage. CX Codex +1 1,400installs dt-obs-log-semantic-mapping A skill that maps vendor audit log fields to the Dynatrace semantic dictionary and validates the result. CX Codex +1 1,400installs dt-platform-costs Analyzes actual Dynatrace DPS consumption and spend directly from dt.system.events via DQL. Helps teams track cost drivers and chargeback. CX Codex +1 1,400installs dt-obs-ext-monitors Sends third-party test outcomes and CI monitor results to Dynatrace Grail through the events ingest API. Replaces the deprecated synthetic v1 endpoint. CX Codex +1 1,100installs

Project sponsors

Companies that keep this analytics open
Gold sponsor

Impressions, clicks and code copies — open statistics. We keep the numbers public for the same reason NeBlask itself is public. Become a sponsor