
Agent Skills
- 1.3k installs
- 147 repo stars
- Updated July 29, 2026
- datadog-labs/agent-skills
agent-skills provides documented workflows for Datadog skills for AI agents. Essential monitoring, logging, tracing and observability.
About
The agent-skills skill datadog skills for AI agents. Essential monitoring, logging, tracing and observability. # Datadog Skills Essential Datadog skills for AI agents. ## Core Skills | Skill | Description | |-------|-------------| | **dd-pup** | Primary CLI - all pup commands, auth, PATH setup | | **dd-monitors** | Create, manage, mute monitors and alerts | | **dd-logs** | Search logs, pipelines, archives | | **dd-apm** | Traces, services, performance analysis | | **dd-docs** | Search Datadog documentation | | **dd-llmo** | LLM Observability traces, experiments, evals | | **dd-browser-sdk** | Browser SDK setup, RUM, Logs, Session Replay, version migration | ## Install ```bash # Install core skills npx skills add datadog-labs/agent-skills \ --skill dd-pup \ --skill dd-monitors \ --skill dd-logs \ --skill dd-apm \ --skill dd-docs \ --full-depth -y ``` ## Prerequisites See [Setup Pup](https://github.com/datadog-labs/agent-skills/tree/main?tab=readme-ov-file#setup-pup) for installation and authentication. ## Command Execution Policy Use this order for scoped commands: 1. Check context first (conversation, prior outputs, known values). Run discovery commands when required values are mi.
- Check context first (conversation, prior outputs, known values).
- Run discovery commands when required values are missing.
- Ask the user only when values remain ambiguous.
- Run the target command after required inputs are known.
- Avoid speculative commands likely to fail.
Agent Skills by the numbers
- 1,344 all-time installs (skills.sh)
- +43 installs in the week ending Aug 5, 2026 (Skillselion tracking)
- Ranked #160 of 1,435 DevOps & CI/CD skills by installs in the Skillselion catalog
- Security screen: MEDIUM risk (skills.sh audit)
- Data as of Aug 5, 2026 (Skillselion catalog sync)
agent-skills capabilities & compatibility
- Capabilities
- check context first (conversation, prior outputs · run discovery commands when required values are · ask the user only when values remain ambiguous. · run the target command after required inputs are · avoid speculative commands likely to fail.
- Use cases
- documentation
What agent-skills says it does
# Datadog Skills Essential Datadog skills for AI agents.
## Command Execution Policy Use this order for scoped commands: 1.
npx skills add https://github.com/datadog-labs/agent-skills --skill agent-skillsAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 1.3k |
|---|---|
| repo stars | ★ 147 |
| Security audit | 2 / 3 scanners passed |
| Last updated | July 29, 2026 |
| Repository | datadog-labs/agent-skills ↗ |
How do I use agent-skills for the task described in its SKILL.md triggers?
Datadog skills for AI agents. Essential monitoring, logging, tracing and observability.
Who is it for?
Teams invoking agent-skills when the user request matches documented triggers and prerequisites.
Skip if: Skip when cached docs are missing, the request is a negative trigger, or another sibling skill owns the workflow.
When should I use this skill?
Datadog skills for AI agents. Essential monitoring, logging, tracing and observability.
What you get
Step-by-step guidance grounded in agent-skills documentation and reference files.
- monitor configurations
- log query results
- apm trace analysis
By the numbers
- Datadog agent-skills metadata version 1.0.2
- Bundles 7 core Datadog skills: dd-pup, dd-monitors, dd-logs, dd-apm, dd-docs, dd-llmo, dd-browser-sdk
Files
Install the Datadog Agent on Kubernetes
Before doing anything else: Fully resolve all variables in ## Context to resolve before acting. Do not begin Step 1 until every variable has a concrete value.Phase 0: Load Credentials
[ -f environment ] && source environment
echo "DD_API_KEY set: $([ -n "${DD_API_KEY:-}" ] && echo yes || echo no)"
echo "DD_SITE: ${DD_SITE:-not set}"
echo "helm: $(helm version --short 2>/dev/null || echo NOT FOUND)"If `helm` is not found — tell the user:
helm is required for this skill. Install it with:```bash
brew install helm # macOS
# or see https://helm.sh/docs/intro/install/ for other platforms
```
Once installed, let me know and I'll continue.
Do not proceed until helm is available.
If `DD_API_KEY` is already set — proceed to Prerequisites.
If `DD_API_KEY` is not set — tell the user:
I need two things to continue:
>
1. Datadog API Key — used to authenticate the Agent with your Datadog account. You can find or create one at: https://app.datadoghq.com/organization-settings/api-keys
>
2. Datadog Site — the region your Datadog account is on. Most accounts usedatadoghq.com. Check your Datadog URL to confirm (e.g.app.datadoghq.eu→ site isdatadoghq.eu). Other options:us3.datadoghq.com,us5.datadoghq.com,ap1.datadoghq.com.
>
Please run the following in this chat to set your credentials (the ! prefix executes it in this session):```
! export DD_API_KEY=your-api-key-here
! export DD_SITE=datadoghq.com
```
Wait for the user to run the commands, then re-run the check above before continuing.
---
Prerequisites
- [ ] Kubernetes v1.20+ —
kubectl version - [ ] helm v3+ —
helm version - [ ] kubectl configured to target cluster —
kubectl config current-context - [ ] pup-cli installed — check with
pup --version; if missing, install it now:
if [[ "$(uname)" == "Darwin" ]]; then
brew tap datadog-labs/pack && brew install pup
else
PUP_VERSION=$(curl -s https://api.github.com/repos/datadog-labs/pup/releases/latest | grep '"tag_name"' | cut -d'"' -f4)
curl -L "https://github.com/datadog-labs/pup/releases/download/${PUP_VERSION}/pup_linux_amd64.tar.gz" | tar xz -C /usr/local/bin pup
chmod +x /usr/local/bin/pup
fi
pup --versionDo not skip — proceed only once pup --version succeeds.
---
Context to resolve before acting
| Variable | How to resolve |
|---|---|
CLUSTER_NAME | Check repo IaC, scripts, or kubectl config current-context |
DD_SITE | Ask the user. Default: datadoghq.com. Common options: datadoghq.eu, us3.datadoghq.com, us5.datadoghq.com, ap1.datadoghq.com. Full list: https://docs.datadoghq.com/getting_started/site/ |
AGENT_NAMESPACE | Use datadog unless the repo already uses datadog-agent consistently |
CHART_VERSION | Run `helm search repo datadog/datadog-operator --versions \ |
---
Step 1: Check for an Existing Agent Installation
Claude runs
helm list -A | grep -i datadogIf a release shows deployed — Agent already installed. Skip to Step 5 to confirm health, then exit.
If there is no output — no existing install. Continue to Step 2.
---
Step 2: Install the Datadog Operator
Claude runs
helm repo add datadog https://helm.datadoghq.com
helm repo update
helm upgrade --install datadog-operator datadog/datadog-operator \
--namespace <AGENT_NAMESPACE> \
--create-namespace \
--version <CHART_VERSION>
kubectl wait --for=condition=Ready pod \
-l app.kubernetes.io/name=datadog-operator \
-n <AGENT_NAMESPACE> \
--timeout=120sIf the Operator pod is Running — continue to Step 3.
ERROR: Pod not ready after 120s — check image pull: kubectl describe pod -l app.kubernetes.io/name=datadog-operator -n <AGENT_NAMESPACE>.
---
Step 3: Create the API Key Secret
What you need to do in a terminal
export DD_API_KEY=<your-api-key>
kubectl create secret generic datadog-secret \
--from-literal api-key=$DD_API_KEY \
--namespace <AGENT_NAMESPACE>If secret/datadog-secret created — continue to Step 4.
ERROR: AlreadyExists — confirm which key it holds via Step 5 before deciding whether to recreate.
---
Step 4: Deploy the DatadogAgent Resource
[DECISION: cluster type]
- Self-hosted (minikube, kind): include
kubelet.tlsVerify: falseinsidespec.global - Managed (GKE, EKS, AKS): omit
kubelet.tlsVerifyentirely
[DECISION: APM/SSI also being enabled in this session]
- If yes: do not create a separate
DatadogAgentfor APM — extend this same manifest withfeatures.apmperenable-ssi. One manifest, not two. - If no: use the manifest below as-is.
Save the following as datadog-agent.yaml:
apiVersion: datadoghq.com/v2alpha1
kind: DatadogAgent
metadata:
name: datadog
namespace: <AGENT_NAMESPACE>
spec:
global:
clusterName: <CLUSTER_NAME>
site: <DD_SITE>
credentials:
apiSecret:
secretName: datadog-secret
keyName: api-key
# Self-hosted clusters only (minikube, kind):
# kubelet:
# tlsVerify: false
features:
orchestratorExplorer:
enabled: true
clusterChecks:
enabled: true
logCollection:
enabled: true
containerCollectAll: falseClaude runs
kubectl apply -f datadog-agent.yaml
kubectl wait --for=condition=Ready pod \
-l app.kubernetes.io/component=agent \
-n <AGENT_NAMESPACE> \
--timeout=120s 2>/dev/null || true---
Step 5: Verify the API Key
Claude runs
kubectl logs -l app.kubernetes.io/component=agent \
-n <AGENT_NAMESPACE> \
--tail=50 2>/dev/null \
| grep -iE "invalid.*api\.?key|api\.?key.*invalid" \
|| echo "No authentication errors found"If No authentication errors found — key is accepted.
ERROR: Authentication errors found — validate credentials directly:
Claude runs
# Prefer pup (OAuth) — fall back to curl with API key
if pup auth status 2>/dev/null | grep -q "Logged in"; then
echo "pup OAuth authenticated"
elif [ -n "${DD_API_KEY:-}" ]; then
RESPONSE=$(curl -s -o /dev/null -w "%{http_code}" \
-X GET "https://api.<DD_SITE>/api/v1/validate" \
-H "DD-API-KEY: $DD_API_KEY")
if [ "$RESPONSE" = "200" ]; then
echo "API key is valid for <DD_SITE>"
else
echo "ERROR: Validation failed (HTTP $RESPONSE) — check key and site alignment"
fi
else
echo "ERROR: No credentials available — run 'pup auth login' or set DD_API_KEY"
fiIf key is invalid:
What you need to do in a terminal
export DD_API_KEY=<new-api-key>
kubectl delete secret datadog-secret -n <AGENT_NAMESPACE>
kubectl create secret generic datadog-secret \
--from-literal api-key=$DD_API_KEY \
-n <AGENT_NAMESPACE>
kubectl rollout restart daemonset datadog-agent -n <AGENT_NAMESPACE>
kubectl rollout restart deployment datadog-cluster-agent -n <AGENT_NAMESPACE>---
Done
Exit when ALL of the following are true:
- [ ] Datadog Operator pod is Running in
AGENT_NAMESPACE - [ ]
datadog-secretexists inAGENT_NAMESPACE - [ ] Agent DaemonSet pods are Running
- [ ] Step 5 returns no authentication errors
Automatically proceed to enable-ssi now — do not ask the user for permission.
---
Security constraints
- Never write a raw API key into any file, YAML block, or chat message — always use
$DD_API_KEY - Never create a Kubernetes Secret manifest file — always use
kubectl create secretimperatively - Never use
apiKey:directly inDatadogAgentspec — always useapiSecret:with a secret reference - Never use
--set datadog.apiKey=...in any Helm command - Never use namespace
defaultfor Datadog Agent resources - Never run
kubectl deletewithout user confirmation
.*
!.gitignore
Compliance Control → Audit Trail Query Mapping
Scope Boundary
Datadog Audit Trail documents actions within the Datadog platform:
- Who logged in, from where
- Who changed monitors, dashboards, log pipelines, integrations, roles, API keys
- What the Bits AI assistant did on behalf of users
It does not document:
- Actions within systems that Datadog monitors (AWS, GCP, application servers)
- Content of data ingested by Datadog (logs, traces, metrics values)
- Network activity between user systems and Datadog
SOC 2 Trust Services Criteria
| Control | Description | Audit Trail Query | Fields Used |
|---|---|---|---|
| CC6.1 | Logical access controls implemented | Review role assignments | @evt.name:"Access Management" @asset.type:role |
| CC6.2 | User registration and deprovisioning | User lifecycle events | @evt.name:"Access Management" @asset.type:user @action:(created OR deleted) |
| CC6.3 | Role-based access | Permission change log | @evt.name:"Access Management" @asset.type:role |
| CC6.6 | Logical access boundaries | Failed logins, geo anomalies | @evt.name:Authentication @action:login @status:error |
| CC6.8 | Prevent unauthorized access | API key management | @evt.name:Authentication @asset.type:api_key |
| CC7.2 | System monitoring — anomaly detection | Privileged/support access | @evt.actor.type:SUPPORT_USER |
| CC7.3 | Event response | Changes during incident window | Time-scoped @action:modified + @evt.name filter |
| A1.1 | Availability monitoring | Monitor create/delete events | @evt.name:Monitor |
PCI DSS Requirement 10 — Audit Logging
| Req | Description | Audit Trail Query | PCI Field Mapping |
|---|---|---|---|
| 10.2.1 | Access to cardholder data | Dashboard/resource access events | @http.method:GET @asset.type:dashboard |
| 10.2.2 | Actions by root/privileged users | Support user and org admin events | @evt.actor.type:SUPPORT_USER |
| 10.2.3 | Access to audit trail | Audit Trail config events | @evt.name:"Audit Trail" |
| 10.2.4 | Invalid access attempts | Failed authentication events | @evt.name:Authentication @status:error |
| 10.2.5 | Use of identification/auth mechanisms | All login events | @evt.name:Authentication @action:login |
| 10.2.6 | Initialization/stopping of audit logs | Audit retention setting changes | @evt.name:"Audit Trail" @action:modified |
| 10.2.7 | Creation/deletion of system objects | All create/delete events | @action:(created OR deleted) |
| 10.3.1 | User identification | @usr.email field | Present on all user-initiated events |
| 10.3.2 | Event type | @action, @evt.name fields | Present on all events |
| 10.3.3 | Date and time | timestamp field | ISO 8601 UTC on all events |
| 10.3.4 | Success/failure indication | @status field | info/error/warn |
| 10.3.5 | Origination of event | @network.client.ip field | Present on most events |
| 10.3.6 | Identity of affected data/component | @asset.type, @asset.id fields | Present on resource events |
| 10.7 | Retain audit logs ≥12 months | Check archive config | Default 90 days — must configure archive |
Retention Requirements by Framework
| Framework | Required retention | Datadog default | Gap? |
|---|---|---|---|
| SOC 2 | Auditor discretion (typically 12 months) | 90 days | Yes — configure archive |
| PCI DSS | 12 months minimum | 90 days | Yes — configure archive |
| ISO 27001 | 3 years typical | 90 days | Yes — configure archive |
| HIPAA | 6 years | 90 days | Yes — configure archive |
To configure archive: Datadog UI > Security > Audit Trail > Configure > Archive to S3/GCS/Azure Blob.
from .executor import ExperimentAnalyzerExecutor
from .evaluator import ExperimentAnalyzerEvaluator
PROJECT_CONFIG = {
"name": "llmo-bits-ai-eng-skill-evals",
"executor": ExperimentAnalyzerExecutor,
"evaluator": ExperimentAnalyzerEvaluator,
"description": "Evaluates the llm-obs-experiment-analyzer Claude Code skill",
}
__all__ = ["ExperimentAnalyzerExecutor", "ExperimentAnalyzerEvaluator"]
[
{
"input": {
"llmo_project_name": "cmd-i-skill-evals",
"baseline": "a4b9d02e-203c-4d62-b8ec-ae986e83cb06",
"candidate": "a05b32f1-351b-44f0-bf4a-efa85c910de7"
},
"labels": {
"question": "Are there specific scenarios where the notebook skill was loaded in one experiment but not the other?",
"answer": "Yes, there are 4 scenarios where the candidate loaded the skill and the baseline didn't, and 3 where the baseline loaded the skill and the candidate didn't.",
"key_points": [
"2 ValueError crashes in baseline → 0 in candidate, eval pipeline fixed.",
"Metric recording was silently broken in baseline — 4/6 metrics now visible for the first time (combined_score 72.1%).",
"Skill loading flat overall (~43%), but candidate loses 3 SQL scenarios it shouldn't — worth watching.",
"Core task performance unchanged — 1/3 of runs still fail to produce a notebook at all."
]
},
"metadata": {
"created_at": "2026-02-25",
"labeler": "mbl"
},
"record_id": "0edcb42a-b3b1-451f-9db5-ac21f8e40ede"
},
{
"input": {
"llmo_project_name": "bits-sre-judge-alignment",
"baseline": "21ad232f-b485-40fb-8a73-cf473ddc2c3b",
"candidate": "2c2c1525-400e-4d6b-9dd2-110fb0036274"
},
"labels": {
"question": "Which judge gives answers that are better aligned with human preferences, as represented in the eval set, the candidate judge or the baseline judge?",
"answer": "4.1 (the candidate) is better aligned. I think it's because we're giving it pretty heavy and specific instructions, which the 4.1 series is built to follow, rather than more open ended or \"goal oriented\" prompting that reasoning models are better at.",
"key_points": [
"IC was way up.",
"Mean error down.",
"Bias a little worse than we'd like still.",
"Significantly better than baseline overall."
]
},
"metadata": {
"created_at": "2026-02-25",
"labeler": "mbl"
},
"record_id": "e8810544-348e-4340-bad2-c3d57e4e0b5f"
},
{
"input": {
"llmo_project_name": "cmdi_o2_tv2_test_trunc",
"baseline": "0bf91646-55e5-47b8-bb25-e3efeab93154",
"candidate": "7da2caa0-d0f5-4f21-bd24-002c57a17882"
},
"labels": {
"question": "What's the improvement of adding a warning about truncated content out of tools in the prompt?",
"answer": "No statistically significant improvement",
"key_points": [
"The metrics are better on the surface.",
"When running deeper analysis these results are not significant.",
"Context truncation is not a significant factor in the differing results."
]
},
"metadata": {
"created_at": "2026-02-26",
"labeler": "mbl"
},
"record_id": "7a857d04-e9d8-4c88-b441-c85983d69ce1"
},
{
"input": {
"llmo_project_name": "assistant-code-reading",
"baseline": "f13a28ab-a10e-444c-b598-31b4596f7311",
"candidate": "fd6bc13b-6e7d-40df-843a-4576dd4b0e2b"
},
"labels": {
"question": "Does the tool \"code inspection\" help getting better results?",
"answer": "Yes, but the tool is not called, leading to a suspicion that the better results are due to informing the model that it CAN call code.",
"key_points": [
"Overall results slightly improve, though not significantly, across all categories. Most gains, suspiciously, occurred in non-sandbox scenarios, suggesting the improvement is due to simply informing the model it can investigate code.",
"Sandbox use is primarily for code investigation, but results vary significantly from previous experiments, indicating high run-to-run variance. These improvements aren't correlated with sandbox usage."
]
},
"metadata": {
"created_at": "2026-03-05",
"labeler": "mbl"
},
"record_id": "08209254-9600-4dab-9ce1-d4301afa95a2"
}
][
{
"input": {
"llmo_project_name": "cmd-i-skill-evals",
"baseline": "a4b9d02e-203c-4d62-b8ec-ae986e83cb06",
"candidate": "a05b32f1-351b-44f0-bf4a-efa85c910de7",
"question": "Are there specific scenarios where the notebook skill was loaded in one experiment but not the other?"
},
"labels": {
"answer": "Yes, there are 4 scenarios where the candidate loaded the skill and the baseline didn't, and 3 where the baseline loaded the skill and the candidate didn't.",
"key_points": [
"2 ValueError crashes in baseline → 0 in candidate, eval pipeline fixed.",
"Metric recording was silently broken in baseline — 4/6 metrics now visible for the first time (combined_score 72.1%).",
"Skill loading flat overall (~43%), but candidate loses 3 SQL scenarios it shouldn't — worth watching.",
"Core task performance unchanged — 1/3 of runs still fail to produce a notebook at all."
]
},
"metadata": {
"created_at": "2026-03-06",
"labeler": "mbl",
"mode": "comparative_qa"
},
"record_id": "64877688-a38f-4a81-a19e-89b2a603d208"
},
{
"input": {
"llmo_project_name": "bits-sre-judge-alignment",
"baseline": "21ad232f-b485-40fb-8a73-cf473ddc2c3b",
"candidate": "2c2c1525-400e-4d6b-9dd2-110fb0036274",
"question": "Which judge gives answers that are better aligned with human preferences, as represented in the eval set, the candidate judge or the baseline judge?"
},
"labels": {
"answer": "4.1 (the candidate) is better aligned. I think it's because we're giving it pretty heavy and specific instructions, which the 4.1 series is built to follow, rather than more open ended or \"goal oriented\" prompting that reasoning models are better at.",
"key_points": [
"IC was way up.",
"Mean error down.",
"Bias a little worse than we'd like still.",
"Significantly better than baseline overall."
]
},
"metadata": {
"created_at": "2026-03-06",
"labeler": "mbl",
"mode": "comparative_qa"
},
"record_id": "794981d3-ce25-4cd3-971e-40d75e2adbed"
},
{
"input": {
"llmo_project_name": "assistant-code-reading",
"baseline": "f13a28ab-a10e-444c-b598-31b4596f7311",
"candidate": "fd6bc13b-6e7d-40df-843a-4576dd4b0e2b",
"question": "Does the tool \"code inspection\" help getting better results?"
},
"labels": {
"answer": "Yes, but the tool is not called, leading to a suspicion that the better results are due to informing the model that it CAN call code.",
"key_points": [
"Overall results slightly improve, though not significantly, across all categories. Most gains, suspiciously, occurred in non-sandbox scenarios, suggesting the improvement is due to simply informing the model it can investigate code.",
"Sandbox use is primarily for code investigation, but results vary significantly from previous experiments, indicating high run-to-run variance. These improvements aren't correlated with sandbox usage."
]
},
"metadata": {
"created_at": "2026-03-06",
"labeler": "mbl",
"mode": "comparative_qa"
},
"record_id": "e7dd93c6-67d6-4704-bb77-d64ed542e4fc"
}
]"""LLM judge evaluator for the llm-obs-experiment-analyzer skill."""
import threading
from pathlib import Path
from typing import Any
from ddtrace.llmobs._evaluators import BaseEvaluator, EvaluatorContext, EvaluatorResult
from ddtrace.llmobs._evaluators.llm_judge import LLMJudge
from ddeval import BaseProjectEvaluator, Evaluator
from ddeval.evaluators._ai_gateway import DEFAULT_MODEL, create_ai_gateway_client
# Metrics scored 0–10 by the judge, normalized to 0.0–1.0
_STRUCTURED_OUTPUT = {
"type": "object",
"properties": {
"answer_accuracy": {"type": "number"},
"key_points_coverage": {"type": "number"},
"evidence_quality": {"type": "number"},
"reasoning": {"type": "string"},
},
"required": ["answer_accuracy", "key_points_coverage", "evidence_quality", "reasoning"],
"additionalProperties": False,
}
class _JudgeCache:
"""Thread-safe cache: one LLM call per unique row, shared across all metric evaluators."""
def __init__(self):
self._cache: dict[str, dict[str, Any]] = {}
self._lock = threading.Lock()
def get(self, key: str) -> dict[str, Any] | None:
with self._lock:
return self._cache.get(key)
def set(self, key: str, scores: dict[str, Any]) -> None:
with self._lock:
self._cache[key] = scores
class _SkillJudge(BaseEvaluator):
def __init__(self, metric_name: str, cache: _JudgeCache):
super().__init__(name=metric_name)
self.metric_name = metric_name
self._cache = cache
rubric_path = Path(__file__).parent / "prompts" / "judge_rubric.txt"
self._judge = LLMJudge(
user_prompt=rubric_path.read_text(),
client=create_ai_gateway_client(),
structured_output=_STRUCTURED_OUTPUT,
model=DEFAULT_MODEL,
)
def evaluate(self, context: EvaluatorContext) -> EvaluatorResult:
if not context.output_data or not context.expected_output:
return EvaluatorResult(value=None)
# Fields that uniquely identify a row — prevents redundant LLM calls across metrics
cache_key = "|".join(
str(context.input_data.get(f, "")) for f in ["baseline", "candidate", "question"]
)
scores = self._cache.get(cache_key)
if scores is None:
result = self._judge.evaluate(context)
scores = result.value
self._cache.set(cache_key, scores)
raw = scores.get(self.metric_name)
return EvaluatorResult(
value=float(raw) / 10.0 if raw is not None else None,
reasoning=scores.get("reasoning"),
)
class ExperimentAnalyzerEvaluator(BaseProjectEvaluator):
def get_evaluators(self) -> list[Evaluator]:
cache = _JudgeCache()
return [
_SkillJudge("answer_accuracy", cache),
_SkillJudge("key_points_coverage", cache),
_SkillJudge("evidence_quality", cache),
]
def get_summary_evaluators(self) -> list:
return []
{
"input_parameters": {},
"executor_config": {
"timeout_seconds": 900
}
}
"""Executor for the llm-obs-experiment-analyzer Claude Code skill."""
import os
import sys
from typing import Any
_eval_lib_path = os.path.abspath(os.path.join(os.path.dirname(__file__), "../.."))
if _eval_lib_path not in sys.path:
sys.path.insert(0, _eval_lib_path)
from _eval_lib import DEFAULT_TIMEOUT_SECONDS, invoke_skill # noqa: E402
from ddeval import BaseProjectExecutor, Config # noqa: E402
class ExperimentAnalyzerExecutor(BaseProjectExecutor):
def execute_single(
self, input_data: dict[str, Any], config: Config | None
) -> dict[str, Any]:
cfg = config.executor_config if config else {}
timeout_seconds = cfg.get("timeout_seconds", DEFAULT_TIMEOUT_SECONDS)
mcp_env = cfg.get("mcp_env", "prod")
baseline = input_data.get("baseline")
candidate = input_data.get("candidate")
experiment_id = input_data.get("experiment_id")
if not baseline and not candidate and not experiment_id:
raise ValueError(
"input_data must contain 'baseline', 'candidate', or 'experiment_id'"
)
parts = ["/llm-obs-experiment-analyzer"]
if baseline:
parts.append(baseline)
if candidate:
parts.append(candidate)
if not baseline and not candidate and experiment_id:
parts.append(experiment_id)
if input_data.get("question"):
parts.append(input_data["question"])
parts.append("--output agent")
prompt = " ".join(parts)
# prompt is passed to the Claude Agent SDK, not to a shell — not a command injection risk
report = invoke_skill(prompt, timeout_seconds=timeout_seconds, mcp_env=mcp_env)
return {"report": report, "word_count": len(report.split()) if report else 0}
You are an expert judge evaluating the quality of an LLM experiment analysis report.
## Experiment Context
Baseline experiment ID: {{input_data.baseline}}
Candidate experiment ID: {{input_data.candidate}}
Question asked: {{input_data.question}}
## Expected Answer
{{expected_output.answer}}
Key points that should be covered:
{{expected_output.key_points}}
## Generated Report
{{output_data.report}}
---
## Scoring Instructions
Score each dimension from 0 to 10. Be strict — reserve 9–10 for exceptional quality.
### 1. answer_accuracy (0–10)
Does the report's conclusion or verdict align with the expected answer?
- 9–10: The report reaches the same conclusion as the expected answer with correct supporting evidence (specific metrics, counts, percentages). Nuances match.
- 7–8: The verdict is correct but misses some nuance or uses approximate rather than exact numbers.
- 5–6: The report is partially correct — gets the direction right but is vague, hedged, or misses a key part of the answer.
- 3–4: The report is mostly wrong or contradicts the expected answer in a meaningful way.
- 0–2: The report does not answer the question, produces no output, or gives the opposite conclusion.
### 2. key_points_coverage (0–10)
Does the report surface the key findings listed in the expected key points?
- 9–10: All key points are explicitly addressed with matching evidence and correct interpretation.
- 7–8: Most key points are covered; one is missing or has incorrect detail.
- 5–6: Half or more key points are present but some are missing or shallow.
- 3–4: Only one or two key points are touched, others are absent or wrong.
- 0–2: Key points are not addressed or the report is empty/trivial.
### 3. evidence_quality (0–10)
Are the report's claims grounded in specific, quantified evidence from the experiment data?
- 9–10: Every major claim cites specific numbers (metric pass rates, event counts, percentages, delta values). Reasoning is traceable to data.
- 7–8: Most claims are quantified; a few are qualitative without supporting numbers.
- 5–6: Some quantification present, but significant portions rely on vague language ("some events", "a few cases", "seems better").
- 3–4: Claims are mostly qualitative with minimal data reference.
- 0–2: No quantification; report is purely speculative or empty.
### Weighted aggregate (for your reasoning only — do not output this score)
Suggested weights: answer_accuracy × 0.5 + key_points_coverage × 0.3 + evidence_quality × 0.2
---
Return a JSON object with exactly these keys:
{
"answer_accuracy": <integer 0–10>,
"key_points_coverage": <integer 0–10>,
"evidence_quality": <integer 0–10>,
"reasoning": "<2–4 sentences explaining the scores, referencing specific parts of the report>"
}
[]
"""LLM judge evaluator for the llm-obs-trace-rca skill."""
import threading
from pathlib import Path
from typing import Any
from ddtrace.llmobs._evaluators import BaseEvaluator, EvaluatorContext, EvaluatorResult
from ddtrace.llmobs._evaluators.llm_judge import LLMJudge
from ddeval import BaseProjectEvaluator, Evaluator
from ddeval.evaluators._ai_gateway import DEFAULT_MODEL, create_ai_gateway_client
# Metrics scored 0–10 by the judge, normalized to 0.0–1.0
_STRUCTURED_OUTPUT = {
"type": "object",
"properties": {
"diagnosis_accuracy": {"type": "number"},
"evidence_grounding": {"type": "number"},
"actionability": {"type": "number"},
"completeness": {"type": "number"},
"reasoning": {"type": "string"},
},
"required": ["diagnosis_accuracy", "evidence_grounding", "actionability", "completeness", "reasoning"],
"additionalProperties": False,
}
class _JudgeCache:
"""Thread-safe cache: one LLM call per unique row, shared across all metric evaluators."""
def __init__(self):
self._cache: dict[str, dict[str, Any]] = {}
self._lock = threading.Lock()
def get(self, key: str) -> dict[str, Any] | None:
with self._lock:
return self._cache.get(key)
def set(self, key: str, scores: dict[str, Any]) -> None:
with self._lock:
self._cache[key] = scores
class _LlmObsTraceRcaJudge(BaseEvaluator):
def __init__(self, metric_name: str, cache: _JudgeCache):
super().__init__(name=metric_name)
self.metric_name = metric_name
self._cache = cache
rubric_path = Path(__file__).parent / "prompts" / "judge_rubric.txt"
self._judge = LLMJudge(
user_prompt=rubric_path.read_text(),
client=create_ai_gateway_client(),
structured_output=_STRUCTURED_OUTPUT,
model=DEFAULT_MODEL,
)
def evaluate(self, context: EvaluatorContext) -> EvaluatorResult:
if not context.output_data or not context.expected_output:
return EvaluatorResult(value=None)
# Fields that uniquely identify a row — prevents redundant LLM calls.
cache_key = "|".join(
str(context.input_data.get(f, ""))
for f in ["ml_app", "eval_name", "timeframe", "mode", "failure_filter"]
)
scores = self._cache.get(cache_key)
if scores is None:
result = self._judge.evaluate(context)
scores = result.value
self._cache.set(cache_key, scores)
raw = scores.get(self.metric_name)
return EvaluatorResult(
value=float(raw) / 10.0 if raw is not None else None,
reasoning=scores.get("reasoning"),
)
class LlmObsTraceRcaEvaluator(BaseProjectEvaluator):
def get_evaluators(self) -> list[Evaluator]:
cache = _JudgeCache()
return [
_LlmObsTraceRcaJudge("diagnosis_accuracy", cache),
_LlmObsTraceRcaJudge("evidence_grounding", cache),
_LlmObsTraceRcaJudge("actionability", cache),
_LlmObsTraceRcaJudge("completeness", cache),
]
def get_summary_evaluators(self) -> list:
return []
{
"input_parameters": {},
"executor_config": {
"timeout_seconds": 1000
}
}
"""Executor for the llm-obs-trace-rca Claude Code skill."""
import os
import sys
from typing import Any
_eval_lib_path = os.path.abspath(os.path.join(os.path.dirname(__file__), "../.."))
if _eval_lib_path not in sys.path:
sys.path.insert(0, _eval_lib_path)
from _eval_lib import DEFAULT_TIMEOUT_SECONDS, invoke_skill # noqa: E402
from ddeval import BaseProjectExecutor, Config # noqa: E402
class LlmObsTraceRcaExecutor(BaseProjectExecutor):
def execute_single(
self, input_data: dict[str, Any], config: Config | None
) -> dict[str, Any]:
cfg = config.executor_config if config else {}
timeout_seconds = cfg.get("timeout_seconds", DEFAULT_TIMEOUT_SECONDS)
mcp_env = cfg.get("mcp_env", "prod")
if input_data.get("from") and input_data.get("to"):
time_spec = f"from {input_data['from']} to {input_data['to']}"
else:
timeframe = input_data.get("timeframe", "now-24h")
time_spec = f"over the last {timeframe}"
ml_app = input_data.get("ml_app")
eval_name = input_data.get("eval_name")
mode = input_data.get("mode")
failure_filter = input_data.get("failure_filter")
# Append explicit mode/filter hints so the skill skips inference when needed.
hints = []
if mode:
hints.append(f"mode={mode}")
if failure_filter:
hints.append(f"filter={failure_filter}")
suffix = f" [{', '.join(hints)}]" if hints else ""
if ml_app and eval_name:
prompt = f"/llm-obs-trace-rca Analyze eval failures for {eval_name} on {ml_app} {time_spec}{suffix}"
elif eval_name:
prompt = f"/llm-obs-trace-rca Analyze eval failures for {eval_name} {time_spec}{suffix}"
elif ml_app:
prompt = f"/llm-obs-trace-rca What's wrong with {ml_app} {time_spec}{suffix}"
else:
raise ValueError("input_data must contain either 'ml_app' or 'eval_name'")
report = invoke_skill(prompt, timeout_seconds=timeout_seconds, mcp_env=mcp_env)
return {"report": report, "word_count": len(report.split()) if report else 0}
You are evaluating an AI-generated root cause analysis (RCA) report for a production LLM application.
The report was generated by analyzing LLM observability traces for the following application:
**Application**: {{input_data.ml_app}} / {{input_data.eval_name}}
**Timeframe**: {{input_data.timeframe}}
**Mode**: {{input_data.mode}} (eval, errors, or generic — determines what signal was used)
---
## Expected Findings
The following key findings and recommendations were identified by a human analyst as ground truth for this scenario:
**Key findings** (root causes and failure patterns that should be diagnosed):
{{expected_output.key_findings}}
**Expected recommendations** (actionable changes that should be proposed):
{{expected_output.expected_recommendations}}
---
## Generated RCA Report
{{output_data.report}}
---
## Scoring Instructions
Score each dimension from 0 to 10. Use the full range.
### 1. diagnosis_accuracy (0–10)
Does the report correctly identify the root causes described in the expected findings?
- **10**: All key root causes from expected findings are identified, correctly named, and traced to the right span or system component. No significant false diagnoses.
- **7–9**: Most key root causes identified. Minor misattributions or missing one secondary cause.
- **4–6**: Some root causes identified but important ones are missing, or the diagnosis is at the symptom level ("model gave bad answer") rather than tracing to a specific root cause (e.g., "system prompt lacks date format instructions in the parent agent span").
- **1–3**: Very few root causes identified. Report mostly describes what failed without diagnosing why.
- **0**: No root causes identified, or diagnoses are entirely wrong.
### 2. evidence_grounding (0–10)
Are claims backed by trace evidence — span IDs, quoted judge reasoning, specific content from trace fields?
- **10**: Every significant claim references a span ID with a trace link. Judge reasoning is quoted verbatim (Eval Signal) or stack traces are cited (Error Signal). Trace deep-dives show what was fetched (system prompt content, tool parameters, retrieved documents, etc.).
- **7–9**: Most claims are grounded. A few assertions are unsupported.
- **4–6**: Some evidence cited, but key claims are asserted without trace references. Or evidence is generic ("the trace showed issues") rather than specific.
- **1–3**: Little to no trace evidence. Report is mostly assertions.
- **0**: No evidence cited at all.
### 3. actionability (0–10)
Are recommendations specific and immediately actionable, with before/after content from the traces?
- **10**: Each recommendation specifies exactly what to change (system prompt text, tool parameter, routing logic), includes a before/after quote from the actual trace, and explains the causal link to the failure.
- **7–9**: Most recommendations are specific. One or two are somewhat vague or lack before/after text.
- **4–6**: Recommendations are present but generic (e.g., "improve the system prompt") without specific text changes or references to actual trace content.
- **1–3**: Recommendations are vague advice (e.g., "improve retrieval quality") with no specifics.
- **0**: No recommendations, or recommendations are irrelevant.
### 4. completeness (0–10)
Does the report cover all required sections and address the breadth of failure modes?
- **10**: Report includes all expected sections (Signal Summary, Failure Taxonomy, Detailed Analysis per top failure mode, Prioritized Action Plan, Limitations). The Signal Summary matches the mode used (eval health table for Eval Signal, error type table for Error Signal, anomaly table for Generic). Covers all key failure modes from expected findings.
- **7–9**: All major sections present. One failure mode underexplored or one section thin.
- **4–6**: Core sections present but several are shallow. Failure taxonomy is incomplete (e.g., only 1–2 modes when 4+ were expected).
- **1–3**: Large sections missing or severely truncated. Failure analysis is superficial.
- **0**: Report is empty, extremely brief, or entirely off-topic.
---
## Output
Return a JSON object with the following fields:
- `diagnosis_accuracy`: integer 0–10
- `evidence_grounding`: integer 0–10
- `actionability`: integer 0–10
- `completeness`: integer 0–10
- `reasoning`: 2–4 sentences explaining the scores, citing specific strengths and weaknesses in the report
id,preference_type,tag_key,tag_value,owner,confidence,owner_type,handle,exclusion_type,exclusion_resource_type,prompt_text,priority
1,tag_mapping,cost-center,CC-100,team-platform,high,team,,,,,
2,tag_mapping,cost-center,CC-200,team-data-eng,high,team,,,,,
3,tag_mapping,cost-center,CC-300,team-security,high,team,,,,,
4,tag_mapping,project,atlas,team-atlas,medium,team,,,,,
5,tag_mapping,project,hermes,alice@example.com,medium,user,,,,,
6,tag_mapping,env,production,sre-team,low,team,,,,,
7,tag_mapping,managed-by,,team-infra,low,team,,,,,
8,exclusion,,,,,,deploy-bot,,,,
9,exclusion,,,,,,ci-runner,service,,,
10,exclusion,,,,,,github-actions,service,,,
11,exclusion,,,,,,legacy-ops,team,aws_ec2_instance,,
12,prompt_text,,,,,,,,,Our organization assigns ownership by cost center. The cost-center tag is the primary ownership signal for all cloud resources. Team identifiers always use the team- prefix followed by the team name (e.g. team-platform team-data-eng).,high
13,prompt_text,,,,,,,,,Shared infrastructure accounts (deploy-bot ci-runner github-actions) are automation accounts and should never be assigned as resource owners. Look for the human or team that configured the automation instead.,medium
14,prompt_text,,,,,,,,,For container images the repository owner in GitHub is a reliable secondary signal when cost-center tags are missing.,low
k9_ownership_preferences Schema Reference
Schema (12 columns, all STRING)
| Column | Used By | Required | Description |
|---|---|---|---|
id | All | Yes | Unique row identifier (sequential integer) |
preference_type | All | Yes | Row discriminator: tag_mapping, exclusion, or prompt_text |
tag_key | tag_mapping | Yes | Tag key to match |
tag_value | tag_mapping | No | Tag value to match. Empty = matches any value for that key (wildcard) |
owner | tag_mapping | Yes | Owner handle to assign |
confidence | tag_mapping | Yes | high, medium, or low |
owner_type | tag_mapping | Yes | Owner type: team, user, or service |
handle | exclusion | Yes | Owner handle to exclude |
exclusion_type | exclusion | No | Owner type filter. Empty = all types |
exclusion_resource_type | exclusion | No | Resource type filter. Empty = all resource types |
prompt_text | prompt_text | Yes | Custom guidance text for the ownership engine |
priority | prompt_text | No | Ordering: high, medium, or low |
CSV Header
id,preference_type,tag_key,tag_value,owner,confidence,owner_type,handle,exclusion_type,exclusion_resource_type,prompt_text,priorityEach row gets a unique sequential id and fills columns relevant to its preference_type, leaving the rest empty.
Preference Types
Tag Mappings
A tag mapping says: _"When a resource has tag X:Y, it belongs to this owner."_
The agent checks cloud resource tags against your mappings. When a match is found, the specified owner is added as a candidate. Multiple mappings can match the same resource, producing multiple candidates ranked alongside other data sources.
Tag mappings complement existing data sources — they do not override a direct ownership tag (like dd-team) already on the resource.
Columns: id (required), preference_type=tag_mapping, tag_key (required), tag_value (optional, empty=wildcard), owner (required), confidence (required: high/medium/low), owner_type (required: team/user/service).
Owner type guidance:
| Value | When to use |
|---|---|
team | The owner is a team handle (e.g., team-platform, sre-team) |
user | The owner is an individual (e.g., alice@example.com) |
service | The owner is a service or automation account (e.g., payment-svc) |
Confidence guidance:
| Level | When to use |
|---|---|
high | The tag reliably identifies the owner. Example: a cost-center tag that maps 1:1 to a team |
medium | The tag is a good indicator but may not always be correct. Example: a project tag shared across teams |
low | The tag provides a hint but needs corroboration. Example: an env tag that loosely correlates with a team |
Matching behavior:
- Tag key and value matching is case-insensitive.
Cost-Centermatchescost-center. - An empty
tag_valuematches any value for that tag key (wildcard). - If multiple mappings match, all produce candidates. The agent ranks them by confidence.
Example rows:
1,tag_mapping,cost-center,CC-100,team-platform,high,team,,,,,
2,tag_mapping,managed-by,,team-infra,low,team,,,,,Exclusions
An exclusion says: _"Never assign this handle as a resource owner."_
Bot accounts, CI runners, and shared service accounts often appear in cloud resource metadata. Exclusions remove these from ownership results.
Columns: id (required), preference_type=exclusion, handle (required), exclusion_type (optional), exclusion_resource_type (optional).
Matching behavior:
- The
handleis matched case-insensitively. - Optional filters use AND logic. All non-empty fields must match for the exclusion to apply.
- Leave
exclusion_typeandexclusion_resource_typeempty to exclude from all results (most common).
Example rows:
1,exclusion,,,,,,deploy-bot,,,,
2,exclusion,,,,,,ci-runner,service,,,
3,exclusion,,,,,,k8s-node-controller,service,aws_ec2_instance,,Custom Prompt Text
Custom prompt text provides free-form guidance to the AI inference engine. Use it to share organizational context: naming conventions, team structures, which data sources to prioritize.
Up to 3 entries, one per priority level (high, medium, low). Entries with the same priority are concatenated.
Columns: id (required), preference_type=prompt_text, prompt_text (required, up to 4096 bytes), priority (optional, default: low).
Tips for effective guidance:
- Be specific and actionable: "The cost-center tag is our most reliable ownership signal" > "Use tags"
- Use plain, declarative sentences — describe facts, not instructions to the AI
- Avoid special formatting: Markdown, HTML, XML tags are stripped during processing
Example rows:
1,prompt_text,,,,,,,,,Our organization assigns ownership by cost center.,high
2,prompt_text,,,,,,,,,Shared infrastructure accounts should never be resource owners.,mediumValidation Rules
All-or-nothing: If any row fails validation, the entire preference set is rejected for that sync cycle. Preferences are left empty until a valid set is uploaded.
Allowed Characters
| Field type | Allowed characters | Applies to |
|---|---|---|
| Structured fields | Letters, digits, - _ . : / @ | tag_key, owner, handle, exclusion_type, exclusion_resource_type, owner_type, confidence, priority |
| Tag values | Same as structured fields, plus spaces | tag_value |
| Prompt text | Same as above, plus # , ; ! ? ( ) ' " backticks, spaces, tabs, newlines | prompt_text |
Not allowed in any field: Angle brackets (< >), curly braces ({ }), pipe characters (|).
Size Limits
| Limit | Value |
|---|---|
| Max tag mappings | 50 rows |
| Max exclusions | 20 rows |
| Max prompt text entries | 3 (one per priority: high, medium, low) |
| Max field length | 1,024 bytes |
| Max prompt text per entry | 4,096 bytes |
Duplicate Detection
The agent rejects the entire set if it contains conflicts:
- Tag mappings: Same
tag_key+tag_valuewith differentowner= conflict. Same key+value+owner with differentconfidence= conflict. Exact duplicates are allowed. - Exclusions: Same
handle+exclusion_type+exclusion_resource_type= duplicate. Case-insensitive.
MIT License
Copyright (c) 2026, Datadog <info@datadoghq.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
Datadog Skills for AI Agents
Datadog skills for Claude Code, Codex CLI, Gemini CLI, Cursor, Windsurf, OpenCode, and other AI agents.
Skills
| Skill | Description |
|---|---|
| dd-pup | Primary CLI - commands, auth, PATH setup |
| dd-monitors | Create, manage, mute monitors |
| dd-logs | Search logs |
| dd-apm | Traces, services, performance, Single-Step Instrumentation |
| dd-docs | Search Datadog documentation |
| dd-llmo | LLM Observability: experiments, eval RCA, evaluator generation, session classification |
| dd-browser-sdk | Browser SDK: RUM, Logs, Session Replay, profiling, product analytics, error tracking, version migration |
| dd-audit | Audit Trail investigations: who changed what, key compromise, cost spike root cause, compliance evidence (SOC 2/PCI), AI activity auditing |
Install
Setup Pup
# Homebrew (macOS/Linux) — recommended
brew tap datadog-labs/pack
brew install datadog-labs/pack/pup
# Or build from source
git clone https://github.com/datadog-labs/pup.git && cd pup
cargo build --release
cp target/release/pup ~/.local/binPre-built binaries are also available from the latest release.
# Authenticate
pup auth loginAdd Skill(s)
For JUST dd-pup:
npx skills add datadog-labs/agent-skills \
--skill dd-pup \
--full-depth -yFor ALL skills:
npx skills add datadog-labs/agent-skills --full-depth -yLLM Observability (LLMO)
The dd-llmo directory contains six skills for working with LLM Observability data:
| Skill | Purpose |
|---|---|
llm-obs-experiment-analyzer | Analyze and compare offline LLM experiments |
llm-obs-experiment-py-bootstrap | Generate self-contained Python experiment code using the ddtrace.llmobs SDK |
llm-obs-trace-rca | Root-cause production failures using eval judge signal or runtime errors |
llm-obs-eval-bootstrap | Generate evaluator code from traces, optionally seeded by RCA output |
llm-obs-eval-pipeline | End-to-end pipeline: classify sessions → RCA → bootstrap evaluators |
llm-obs-session-classify | Classify whether user intent was satisfied in a session (trace + RUM signals) |
Eval pipeline flow:
llm-obs-session-classify llm-obs-trace-rca → llm-obs-eval-bootstrap
(classify sessions) (diagnose why) (build evals)Run llm-obs-trace-rca to understand why an app is failing by analyzing eval judge verdicts or runtime errors across production traces. Then run llm-obs-eval-bootstrap to generate evaluator code that captures those failure patterns. Pass the RCA output directly to llm-obs-eval-bootstrap to seed it with the discovered failure taxonomy.
Use llm-obs-eval-pipeline to run all three steps in sequence with checkpoints between each phase.
Use llm-obs-session-classify independently to evaluate whether individual assistant sessions satisfied user intent, combining LLM Obs trace data with RUM behavioral signals.
Use llm-obs-experiment-py-bootstrap to generate a self-contained Python experiment client that uses the ddtrace.llmobs SDK — runnable as a .py script or .ipynb notebook, with inline records, a CSV path, or a named Datadog dataset as the input.
Install
# Claude Code — copy any or all skills
cp -r dd-llmo/llm-obs-experiment-analyzer ~/.claude/skills
cp -r dd-llmo/llm-obs-experiment-py-bootstrap ~/.claude/skills
cp -r dd-llmo/llm-obs-trace-rca ~/.claude/skills
cp -r dd-llmo/llm-obs-eval-bootstrap ~/.claude/skills
cp -r dd-llmo/llm-obs-eval-pipeline ~/.claude/skills
cp -r dd-llmo/llm-obs-session-classify ~/.claude/skillsMCP Requirements
All six skills require the LLMO toolset:
claude mcp add --scope user --transport http "datadog-llmo-mcp" 'https://mcp.datadoghq.com/api/unstable/mcp-server/mcp?toolsets=llmobs'experiment-analyzer uses the core toolset for notebook export (optional). eval-session-classify requires it for RUM behavioral analysis and efficient batched fetches of trace session spans:
claude mcp add --scope user --transport http "datadog-mcp-core" 'https://mcp.datadoghq.com/api/unstable/mcp-server/mcp?toolsets=core'Usage
# Analyze experiments
experiment-analyzer <experiment_id> # single experiment
experiment-analyzer <baseline_id> <candidate_id> # compare two experiments
experiment-analyzer <id(s)> <question> # ask a specific question
experiment-analyzer <id(s)> [question] --output notebook # export to Datadog notebook
# Root-cause why an app is failing
What's wrong with <ml_app> based on its evals over the last 24h
Analyze eval failures for <eval_name> over the last week
Look at the errors on <ml_app> over the last 24h
# Generate evaluator code from production traces
/eval-bootstrap <ml_app> # cold start
/eval-bootstrap <ml_app> [paste eval-trace-rca output here] # seeded from RCA
/eval-bootstrap <ml_app> --data-only # emit JSON spec instead of Python SDK code
# Generate a Python experiment client using the ddtrace.llmobs SDK
/llm-obs-experiment-py-bootstrap # 3-record inline sample
/llm-obs-experiment-py-bootstrap --dataset ./data/qa.json --format ipynb # local JSON dataset, notebook
/llm-obs-experiment-py-bootstrap --dataset-name qa_v3 --project-name customer-qa # existing Datadog dataset
/llm-obs-experiment-py-bootstrap --evaluator-style remote # server-side RemoteEvaluator stubs
# Classify a session
/eval-session-classify <session_id>Audit Trail (dd-audit)
The dd-audit directory contains five skills for investigating Datadog Audit Trail data:
| Skill | Purpose |
|---|---|
security-investigation | Who changed what, user activity, login geo, deletions, permission changes |
key-compromise | Investigate a potentially compromised API key — timeline, geo/IP, endpoints called |
cost-spike-investigation | Correlate usage spike (Usage Metering) with config changes (Audit Trail) to find root cause |
compliance-report | Generate SOC 2 / PCI DSS evidence from audit data |
ai-activity-audit | Audit what the Bits AI / MCP assistant did in your org |
Prerequisites
These skills use the Datadog Audit REST API directly (no pup audit command exists yet). You need an API key + App key with audit_logs_read scope:
export DD_API_KEY=<your-api-key>
export DD_APP_KEY=<your-app-key>
export DD_SITE=datadoghq.com # or us3/us5/eu/ap1/ap2Install
# Claude Code — copy any or all skills
cp -r dd-audit/security-investigation ~/.claude/skills
cp -r dd-audit/key-compromise ~/.claude/skills
cp -r dd-audit/cost-spike-investigation ~/.claude/skills
cp -r dd-audit/compliance-report ~/.claude/skills
cp -r dd-audit/ai-activity-audit ~/.claude/skillsUsage
# Security investigation
Who deleted monitors in the last 24 hours?
What did user@example.com do this week?
Show login activity from unexpected locations
# Key compromise
Was API key <key_id> used from unexpected locations?
Investigate this API key: <key_id>
# Cost spike
Why did our LLM Observability usage spike on May 1?
What caused the cost increase this week?
# Compliance
Generate SOC 2 evidence for CC6.2 and CC6.3 for Q1 2026
Create a PCI DSS Requirement 10 report for the last 90 days
# AI activity
What did the Bits AI assistant do in my org this week?
Show me a governance report for AI tool calls in AprilQuick Reference
| Task | Command |
|---|---|
| Search error logs | pup logs search --query "status:error" --from 1h |
| List monitors | pup monitors list |
| Schedule monitor downtime | pup downtime create --file downtime.json |
| Find slow traces | pup traces search --query "service:api @duration:>500ms" --from 1h |
| Query metrics | pup metrics query --query "avg:system.cpu.user{*}" |
| List services for an env (required) | pup apm services list --env <env> --from 1h --to now |
| Check auth | pup auth status |
| Refresh token | pup auth refresh |
More commands for pup are found in the official pup docs.
Auth
# Check auth first (includes token time remaining)
pup auth status
# If commands fail with 401/403, try refresh first
pup auth refresh
# If refresh fails or no session exists, do full OAuth login
pup auth login
# Non-default site/org
pup auth login --site datadoghq.eu --org <org>If the browser opens the wrong profile/window, use the one-time URL printed by pup auth login and open it manually in the correct session.
More Skills
Additional skills available soon.
# List all available
npx skills add datadog-labs/agent-skills --list --full-depthLicense
MIT
Related skills
How it compares
Pick agent-skills when Datadog is the observability platform; use generic logging skills for local-only debugging.
FAQ
What does agent-skills do?
Datadog skills for AI agents. Essential monitoring, logging, tracing and observability.
When should I use agent-skills?
Datadog skills for AI agents. Essential monitoring, logging, tracing and observability.
What are common prerequisites?
--- name: agent-skills description: Datadog skills for AI agents.
Is Agent Skills safe to install?
skills.sh reports 2 of 3 security scanners passed. Review the Security Audits panel on this page before installing in production.