aiconfig-custom-metrics

Par launchdarkly · agent-skills

Créez, suivez, récupérez, mettez à jour et supprimez des métriques métier personnalisées pour les AI Configs. Couvre l'ensemble du cycle de vie : définissez les types de métriques via l'API, émettez des événements via le SDK et interrogez les résultats.

npx skills add https://github.com/launchdarkly/agent-skills --skill aiconfig-custom-metrics

Métriques personnalisées pour les configurations IA

Gestion complète du cycle de vie des métriques métier personnalisées : créez des définitions de métriques via API, suivez les événements via SDK, récupérez les données de métriques et gérez les métriques de manière programmatique.

Prérequis

  • SDK LaunchDarkly initialisé (voir aiconfig-sdk)
  • Token API LaunchDarkly avec rôle writer pour la gestion des métriques
  • Compréhension des métriques IA intégrées (voir aiconfig-ai-metrics)

Détection de la clé API

Avant de demander à l'utilisateur une clé API, essayez de la détecter automatiquement :

  1. Vérifier la configuration Claude MCP - Lisez ~/.claude/config.json et cherchez mcpServers.launchdarkly.env.LAUNCHDARKLY_API_KEY
  2. Vérifier les variables d'environnement - Cherchez LAUNCHDARKLY_API_KEY, LAUNCHDARKLY_API_TOKEN ou LD_API_KEY
  3. Demander à l'utilisateur - Uniquement si la détection échoue, demandez à l'utilisateur sa clé API
import os
import json
from pathlib import Path

def get_launchdarkly_api_key():
    """Auto-detect LaunchDarkly API key from Claude config or environment."""
    # 1. Check Claude MCP config
    claude_config = Path.home() / ".claude" / "config.json"
    if claude_config.exists():
        try:
            config = json.load(open(claude_config))
            api_key = config.get("mcpServers", {}).get("launchdarkly", {}).get("env", {}).get("LAUNCHDARKLY_API_KEY")
            if api_key:
                return api_key
        except (json.JSONDecodeError, IOError):
            pass

    # 2. Check environment variables
    for var in ["LAUNCHDARKLY_API_KEY", "LAUNCHDARKLY_API_TOKEN", "LD_API_KEY"]:
        if os.environ.get(var):
            return os.environ[var]

    return None

Aperçu du cycle de vie des métriques

Étape Méthode Objectif
1. Créer API Définir la métrique dans LaunchDarkly
2. Suivre SDK Envoyer des événements à la métrique
3. Récupérer API Récupérer la définition/les données de la métrique
4. Mettre à jour API Modifier les propriétés de la métrique
5. Supprimer API Supprimer la métrique

1. Créer une métrique (API)

Champs requis pour les métriques personnalisées numériques :

  • successCriteria - Doit être l'un de : "HigherThanBaseline", "LowerThanBaseline"
  • unit - Par exemple, "count", "percent", "milliseconds"

L'API retournera 400 Bad Request si ces champs manquent pour les métriques numériques.

import requests
import os

def create_metric(
    project_key: str,
    metric_key: str,
    name: str,
    kind: str = "custom",
    is_numeric: bool = True,
    unit: str = "count",
    success_criteria: str = "HigherThanBaseline",
    event_key: str = None,
    description: str = None
):
    """Create a new metric definition in LaunchDarkly."""
    API_TOKEN = os.environ.get("LAUNCHDARKLY_API_TOKEN")

    url = f"https://app.launchdarkly.com/api/v2/metrics/{project_key}"

    payload = {
        "key": metric_key,
        "name": name,
        "kind": kind,
        "isNumeric": is_numeric,
        "eventKey": event_key or metric_key
    }

    # Unit and successCriteria are required for numeric custom metrics
    if is_numeric and kind == "custom":
        payload["unit"] = unit
        payload["successCriteria"] = success_criteria

    if description:
        payload["description"] = description

    headers = {
        "Authorization": API_TOKEN,
        "Content-Type": "application/json"
    }

    response = requests.post(url, json=payload, headers=headers)

    if response.status_code == 201:
        print(f"[OK] Created metric: {metric_key}")
        return response.json()
    elif response.status_code == 409:
        print(f"[INFO] Metric already exists: {metric_key}")
        return None
    else:
        print(f"[ERROR] Failed to create metric: {response.status_code}")
        print(f"        {response.text}")
        return None

Types de métriques :

  • custom - Suivre n'importe quel événement (plus courant pour les métriques IA)
  • pageview - Suivre les affichages de page
  • click - Suivre les événements de clic

Critères de succès (pour les métriques numériques) :

  • HigherThanBaseline - Les valeurs plus élevées sont meilleures (par exemple, revenu, satisfaction)
  • LowerThanBaseline - Les valeurs plus basses sont meilleures (par exemple, erreurs, latence)

Unités courantes :

  • count - Comptage générique
  • milliseconds - Durée
  • percent - Valeurs en pourcentage
  • dollars - Devise

2. Suivre les événements (SDK)

Une fois la métrique créée, suivez les événements avec le SDK :

from ldclient import Context
from ldclient.config import Config
import ldclient

# Initialize (see aiconfig-sdk for details)
ldclient.set_config(Config("your-sdk-key"))
ld_client = ldclient.get()

def track_metric(ld_client, user_id: str, metric_key: str, value: float, data: dict = None):
    """Track an event to a metric."""
    context = Context.builder(user_id).build()

    ld_client.track(
        metric_key,
        context,
        data=data,
        metric_value=value
    )

Motifs de suivi courants

def track_conversion(ld_client, user_id: str, amount: float, config_key: str):
    """Track a conversion event with revenue."""
    context = Context.builder(user_id).build()

    ld_client.track(
        "business.conversion",
        context,
        data={"configKey": config_key, "category": "electronics"},
        metric_value=amount
    )

def track_task_success(ld_client, user_id: str, task_type: str, success: bool):
    """Track task completion success/failure."""
    context = Context.builder(user_id).build()

    ld_client.track(
        "task.success_rate",
        context,
        data={"taskType": task_type},
        metric_value=1.0 if success else 0.0
    )

def track_satisfaction(ld_client, user_id: str, score: float, feedback_type: str):
    """Track user satisfaction (0-100 scale)."""
    context = Context.builder(user_id).build()

    ld_client.track(
        "user.satisfaction",
        context,
        data={"feedbackType": feedback_type},
        metric_value=score
    )

    # Track negative feedback separately for alerts
    if score < 50:
        ld_client.track(
            "user.negative_feedback",
            context,
            metric_value=1.0
        )

def track_revenue(ld_client, user_id: str, revenue: float, source: str):
    """Track revenue generated after AI interaction."""
    context = Context.builder(user_id).set("tier", "premium").build()

    if revenue > 0:
        ld_client.track(
            "revenue.impact",
            context,
            data={"source": source},
            metric_value=revenue
        )

3. Récupérer les métriques (API)

Récupérer une métrique unique

def get_metric(project_key: str, metric_key: str):
    """Get a single metric definition."""
    API_TOKEN = os.environ.get("LAUNCHDARKLY_API_TOKEN")

    url = f"https://app.launchdarkly.com/api/v2/metrics/{project_key}/{metric_key}"

    headers = {"Authorization": API_TOKEN}

    response = requests.get(url, headers=headers)

    if response.status_code == 200:
        metric = response.json()
        print(f"[OK] Metric: {metric['key']}")
        print(f"     Name: {metric.get('name', 'N/A')}")
        print(f"     Kind: {metric.get('kind', 'N/A')}")
        print(f"     Numeric: {metric.get('isNumeric', False)}")
        print(f"     Event Key: {metric.get('eventKey', 'N/A')}")
        return metric
    elif response.status_code == 404:
        print(f"[INFO] Metric not found: {metric_key}")
        return None
    else:
        print(f"[ERROR] Failed to get metric: {response.status_code}")
        return None

Lister toutes les métriques

def list_metrics(project_key: str, limit: int = 20):
    """List all metrics in a project."""
    API_TOKEN = os.environ.get("LAUNCHDARKLY_API_TOKEN")

    url = f"https://app.launchdarkly.com/api/v2/metrics/{project_key}"

    headers = {"Authorization": API_TOKEN}
    params = {"limit": limit}

    response = requests.get(url, headers=headers, params=params)

    if response.status_code == 200:
        data = response.json()
        metrics = data.get("items", [])
        print(f"[OK] Found {len(metrics)} metrics:")
        for metric in metrics:
            numeric = "numeric" if metric.get("isNumeric") else "non-numeric"
            print(f"     - {metric['key']} ({metric.get('kind', 'custom')}, {numeric})")
        return metrics
    else:
        print(f"[ERROR] Failed to list metrics: {response.status_code}")
        return None

4. Mettre à jour une métrique (API)

def update_metric(project_key: str, metric_key: str, updates: list):
    """
    Update a metric using JSON Patch operations.

    Args:
        updates: List of patch operations, e.g.:
            [{"op": "replace", "path": "/name", "value": "New Name"}]
    """
    API_TOKEN = os.environ.get("LAUNCHDARKLY_API_TOKEN")

    url = f"https://app.launchdarkly.com/api/v2/metrics/{project_key}/{metric_key}"

    headers = {
        "Authorization": API_TOKEN,
        "Content-Type": "application/json"
    }

    response = requests.patch(url, json=updates, headers=headers)

    if response.status_code == 200:
        print(f"[OK] Updated metric: {metric_key}")
        return response.json()
    elif response.status_code == 404:
        print(f"[ERROR] Metric not found: {metric_key}")
        return None
    else:
        print(f"[ERROR] Failed to update metric: {response.status_code}")
        print(f"        {response.text}")
        return None

# Example: Update metric name and description
def rename_metric(project_key: str, metric_key: str, new_name: str, new_description: str = None):
    """Rename a metric and optionally update description."""
    updates = [
        {"op": "replace", "path": "/name", "value": new_name}
    ]
    if new_description:
        updates.append({"op": "replace", "path": "/description", "value": new_description})

    return update_metric(project_key, metric_key, updates)

5. Supprimer une métrique (API)

def delete_metric(project_key: str, metric_key: str):
    """Delete a metric from the project."""
    API_TOKEN = os.environ.get("LAUNCHDARKLY_API_TOKEN")

    url = f"https://app.launchdarkly.com/api/v2/metrics/{project_key}/{metric_key}"

    headers = {"Authorization": API_TOKEN}

    response = requests.delete(url, headers=headers)

    if response.status_code == 204:
        print(f"[OK] Deleted metric: {metric_key}")
        return True
    elif response.status_code == 404:
        print(f"[INFO] Metric not found: {metric_key}")
        return False
    else:
        print(f"[ERROR] Failed to delete metric: {response.status_code}")
        return False

Exemple de flux de travail complet

import os
import requests
from ldclient import Context
from ldclient.config import Config
import ldclient

# Setup
API_TOKEN = os.environ.get("LAUNCHDARKLY_API_TOKEN")
SDK_KEY = os.environ.get("LAUNCHDARKLY_SDK_KEY")
PROJECT_KEY = "support-ai"

ldclient.set_config(Config(SDK_KEY))
ld_client = ldclient.get()

# 1. Create metric
create_metric(
    PROJECT_KEY,
    "ai.task.completion",
    name="AI Task Completion Rate",
    kind="custom",
    is_numeric=True,
    description="Tracks successful AI task completions"
)

# 2. Track events
context = Context.builder("user-123").build()
ld_client.track("ai.task.completion", context, metric_value=1.0)
ld_client.track("ai.task.completion", context, metric_value=1.0)
ld_client.track("ai.task.completion", context, metric_value=0.0)  # failure
ld_client.flush()

# 3. Get metric definition
metric = get_metric(PROJECT_KEY, "ai.task.completion")

# 4. Update metric name
rename_metric(PROJECT_KEY, "ai.task.completion", "AI Task Success Rate")

# 5. List all metrics
list_metrics(PROJECT_KEY)

# 6. Delete metric (when no longer needed)
# delete_metric(PROJECT_KEY, "ai.task.completion")

Suivi des métriques de session

import time
from ldclient import Context

class SessionMetricsTracker:
    """Track metrics across an entire user session."""

    def __init__(self, ld_client):
        self.ld_client = ld_client
        self.session_data = {}

    def start_session(self, user_id: str, session_id: str):
        """Initialize session tracking."""
        self.session_data[session_id] = {
            "user_id": user_id,
            "start_time": time.time(),
            "interactions": 0,
            "successful_tasks": 0
        }

    def track_interaction(self, session_id: str, success: bool):
        """Track individual interaction within session."""
        if session_id not in self.session_data:
            return
        session = self.session_data[session_id]
        session["interactions"] += 1
        if success:
            session["successful_tasks"] += 1

    def end_session(self, session_id: str):
        """Finalize and track session metrics."""
        if session_id not in self.session_data:
            return None

        session = self.session_data[session_id]
        duration = time.time() - session["start_time"]

        context = Context.builder(session["user_id"]).build()

        # Track session duration
        self.ld_client.track(
            "session.duration",
            context,
            data={"interactions": session["interactions"]},
            metric_value=duration
        )

        # Track session success rate
        if session["interactions"] > 0:
            success_rate = session["successful_tasks"] / session["interactions"]
            self.ld_client.track(
                "session.success_rate",
                context,
                metric_value=success_rate * 100
            )

        result = dict(session)
        result["duration"] = duration
        del self.session_data[session_id]
        return result

Conventions de nommage

# Use dot notation for hierarchy
"quality.accuracy"
"quality.relevance"
"user.satisfaction"
"user.engagement"
"revenue.conversion"
"task.success_rate"
"session.duration"
"ai.task.completion"
"ai.recommendation.conversion"

Bonnes pratiques

  1. Créer avant de suivre - La métrique doit exister avant de suivre les événements
  2. Utiliser des métriques numériques - Définir isNumeric=True pour l'agrégation
  3. Clés cohérentes - Utiliser la même clé dans create_metric() et ld_client.track()
  4. Toujours vider avant de fermer - Appeler ld_client.flush() (await en Node) avant close(). Les événements restants risquent d'être perdus sinon, dans les scripts de courte durée comme dans les services de longue durée. Ce n'est pas une règle réservée au serverless ; elle s'applique à tout processus qui se termine.
  5. Limite de débit - Ne pas suivre à chaque frappe clavier

Affichage des métriques

Les métriques personnalisées apparaissent dans :

  • La page Metrics dans l'interface utilisateur de LaunchDarkly
  • L'onglet Monitoring de votre configuration IA
  • Via l'API en utilisant get_metric() ou list_metrics()

Compétences connexes

  • aiconfig-sdk - Configuration du SDK
  • aiconfig-ai-metrics - Métriques IA intégrées (tokens, durée, coût)
  • aiconfig-online-evals - Métriques de qualité via des juges

Références

Skills similaires