aiconfig-online-evals

Par launchdarkly · agent-skills

Associez des juges aux variations d'AI Config pour une évaluation automatique par LLM-as-a-judge. Créez des juges personnalisés, configurez les taux d'échantillonnage et surveillez les scores de qualité.

npx skills add https://github.com/launchdarkly/agent-skills --skill aiconfig-online-evals

Évaluations en ligne AI Config

Attachez des judges aux variations AI Config pour un scoring automatique de la qualité en utilisant la méthodologie LLM-as-a-judge. Les judges évaluent les réponses et retournent des scores entre 0,0 et 1,0.

Conditions préalables

  • Compte LaunchDarkly avec AI Configs activé
  • Token d'accès API avec permissions d'écriture
  • AI Config existant avec variations (utilisez la skill aiconfig-create)
  • Pour l'enregistrement automatique des métriques et l'API consolidée des résultats de judge : Python AI SDK v0.18.0+ ou Node.js AI SDK v0.17.0+

Détection de la clé API

  1. Vérifier les variables d'environnement - LAUNCHDARKLY_API_KEY, LAUNCHDARKLY_API_TOKEN, LD_API_KEY
  2. Vérifier la configuration MCP - Claude : ~/.claude/config.json -> mcpServers.launchdarkly.env.LAUNCHDARKLY_API_KEY
  3. Demander à l'utilisateur - Seulement si la détection échoue

Concepts clés

Que sont les judges ?

Les judges sont des AI Configs spécialisés en mode judge qui évaluent les réponses d'autres AI Configs. Ils utilisent un LLM pour scorer les outputs et retourner des résultats structurés :

{
  "score": 0.85,
  "reasoning": "Answered correctly with one minor omission"
}

Judges intégrés

LaunchDarkly fournit trois judges préconfigurés :

Judge Clé métrique Mesure
Accuracy $ld:ai:judge:accuracy Exactitude et fondement de la réponse
Relevance $ld:ai:judge:relevance Pertinence par rapport à la demande utilisateur
Toxicity $ld:ai:judge:toxicity Formulation nuisible ou dangereuse (inférieur = plus sûr)

Mode Completion uniquement

Les judges ne peuvent être attachés que aux AI Configs en mode completion dans l'interface utilisateur. Pour le mode agent ou les pipelines personnalisés, utilisez l'évaluation programmatique via le SDK.

Restrictions

  • Impossible d'attacher des judges à des judges (pas de récursion)
  • Impossible d'attacher plusieurs judges avec la même clé métrique à une même variation
  • Impossible de voir/modifier les paramètres du modèle ou les tools sur les variations judge

Workflow

Étape 1 : Créer des judges personnalisés (optionnel)

Pour une évaluation spécifique au domaine, créez des AI Configs judge :

# Create judge config
curl -X POST "https://app.launchdarkly.com/api/v2/projects/{projectKey}/ai-configs" \
  -H "Authorization: {api_token}" \
  -H "Content-Type: application/json" \
  -H "LD-API-Version: beta" \
  -d '{
    "key": "security-judge",
    "name": "Security Judge",
    "mode": "judge",
    "evaluationMetricKey": "security",
    "isInverted": false
  }'

Note : Définissez isInverted: true pour les métriques comme toxicity où 0,0 est préférable.

Puis ajoutez une variation avec le prompt d'évaluation :

curl -X POST "https://app.launchdarkly.com/api/v2/projects/{projectKey}/ai-configs/security-judge/variations" \
  -H "Authorization: {api_token}" \
  -H "Content-Type: application/json" \
  -H "LD-API-Version: beta" \
  -d '{
    "key": "default",
    "name": "Default",
    "messages": [
      {
        "role": "system",
        "content": "You are a security auditor. Score from 0.0 to 1.0:\n- 1.0: No security issues\n- 0.7-0.9: Minor issues\n- 0.4-0.6: Moderate issues\n- 0.1-0.3: Serious vulnerabilities\n- 0.0: Critical vulnerabilities\n\nCheck for: SQL injection, XSS, hardcoded secrets, command injection."
      }
    ],
    "modelConfigKey": "OpenAI.gpt-4o-mini",
    "model": {
      "parameters": {
        "temperature": 0.3
      }
    }
  }'

Étape 2 : Attacher les judges aux variations

Utilisez l'endpoint PATCH de variation :

curl -X PATCH "https://app.launchdarkly.com/api/v2/projects/{projectKey}/ai-configs/{configKey}/variations/{variationKey}" \
  -H "Authorization: {api_token}" \
  -H "Content-Type: application/json" \
  -H "LD-API-Version: beta" \
  -d '{
    "judgeConfiguration": {
      "judges": [
        {"judgeConfigKey": "security-judge", "samplingRate": 1.0},
        {"judgeConfigKey": "api-contract-judge", "samplingRate": 0.5}
      ]
    }
  }'

Important : Le tableau judges remplace tous les attachements de judges existants. Un tableau vide supprime tous les judges.

Étape 3 : Définir le fallthrough sur les judges

Chaque AI Config judge doit avoir son fallthrough défini à la variation activée. Les AI Configs utilisent par défaut la variation « disabled » (index 0).

Note : turnTargetingOn ne fonctionne pas pour les AI Configs. Utilisez updateFallthroughVariationOrRollout à la place.

# First get the variation ID for "Default" from GET targeting response
curl -X PATCH "https://app.launchdarkly.com/api/v2/projects/{projectKey}/ai-configs/security-judge/targeting" \
  -H "Authorization: {api_token}" \
  -H "Content-Type: application/json; domain-model=launchdarkly.semanticpatch" \
  -H "LD-API-Version: beta" \
  -d '{
    "environmentKey": "production",
    "instructions": [{
      "kind": "updateFallthroughVariationOrRollout",
      "variationId": "your-default-variation-uuid"
    }]
  }'

Implémentation Python

import requests
import os
from typing import Optional

class AIConfigJudges:
    """Manager for AI Config judge attachments"""

    def __init__(self, api_token: str, project_key: str):
        self.api_token = api_token
        self.project_key = project_key
        self.base_url = "https://app.launchdarkly.com/api/v2"
        self.headers = {
            "Authorization": api_token,
            "Content-Type": "application/json",
            "LD-API-Version": "beta"
        }

    def attach_judges(self, config_key: str, variation_key: str,
                      judges: list[dict]) -> dict:
        """
        Attach judges to a variation.

        Args:
            config_key: AI Config key
            variation_key: Variation key
            judges: List of {"judgeConfigKey": str, "samplingRate": float}
        """
        url = f"{self.base_url}/projects/{self.project_key}/ai-configs/{config_key}/variations/{variation_key}"

        response = requests.patch(url, headers=self.headers, json={
            "judgeConfiguration": {"judges": judges}
        })

        if response.status_code == 200:
            print(f"[OK] Attached {len(judges)} judges to {config_key}/{variation_key}")
            return response.json()
        print(f"[ERROR] {response.status_code}: {response.text}")
        return {}

    def create_judge(self, key: str, name: str, metric_key: str,
                     system_prompt: str, model: str = "OpenAI.gpt-4o-mini",
                     is_inverted: bool = False) -> dict:
        """
        Create a judge AI Config.

        Args:
            key: Judge config key
            name: Display name
            metric_key: Metric key for scoring (appears as $ld:ai:judge:{metric_key})
            system_prompt: Evaluation instructions
            is_inverted: True if lower scores are better (e.g., toxicity)
        """
        # Create config
        config_url = f"{self.base_url}/projects/{self.project_key}/ai-configs"
        response = requests.post(config_url, headers=self.headers, json={
            "key": key,
            "name": name,
            "mode": "judge",
            "evaluationMetricKey": metric_key,
            "isInverted": is_inverted
        })

        if response.status_code not in [200, 201]:
            print(f"[ERROR] Creating config: {response.text}")
            return {}

        # Create variation
        var_url = f"{self.base_url}/projects/{self.project_key}/ai-configs/{key}/variations"
        response = requests.post(var_url, headers=self.headers, json={
            "key": "default",
            "name": "Default",
            "messages": [{"role": "system", "content": system_prompt}],
            "modelConfigKey": model,
            "model": {"parameters": {"temperature": 0.3}}
        })

        if response.status_code in [200, 201]:
            print(f"[OK] Created judge: {key}")
            return response.json()
        print(f"[ERROR] Creating variation: {response.text}")
        return {}

    def set_fallthrough(self, config_key: str, environment: str,
                        variation_key: str = "default") -> bool:
        """
        Set fallthrough to enable a judge config.

        Note: turnTargetingOn doesn't work for AI Configs. Instead, set the
        fallthrough from disabled (index 0) to the enabled variation.
        """
        # Get variation ID
        url = f"{self.base_url}/projects/{self.project_key}/ai-configs/{config_key}/targeting"
        response = requests.get(url, headers=self.headers)

        if response.status_code != 200:
            print(f"[ERROR] {response.status_code}: {response.text}")
            return False

        targeting = response.json()
        variation_id = None
        for var in targeting.get("variations", []):
            if var.get("key") == variation_key or var.get("name") == variation_key:
                variation_id = var.get("_id")
                break

        if not variation_id:
            print(f"[ERROR] Variation '{variation_key}' not found")
            return False

        # Set fallthrough
        response = requests.patch(url, headers={
            **self.headers,
            "Content-Type": "application/json; domain-model=launchdarkly.semanticpatch"
        }, json={
            "environmentKey": environment,
            "instructions": [{
                "kind": "updateFallthroughVariationOrRollout",
                "variationId": variation_id
            }]
        })

        if response.status_code == 200:
            print(f"[OK] Fallthrough set for {config_key}")
            return True
        print(f"[ERROR] {response.status_code}: {response.text}")
        return False

SDK : Évaluation automatique

Quand vous utilisez create_chat() + invoke(), les judges attachés évaluent automatiquement :

import os
import json
import asyncio
import ldclient
from ldclient import Context
from ldclient.config import Config
from ldai import LDAIClient, AICompletionConfigDefault

sdk_key = os.getenv('LAUNCHDARKLY_SDK_KEY')
ai_config_key = os.getenv('LAUNCHDARKLY_AI_CONFIG_KEY', 'sample-ai-config')

async def async_main():
    ldclient.set_config(Config(sdk_key))
    aiclient = LDAIClient(ldclient.get())

    context = (
        Context.builder('example-user-key')
        .kind('user')
        .name('Sandy')
        .build()
    )

    default_value = AICompletionConfigDefault(enabled=False)

    # create_chat() initializes with judges from AI Config
    chat = await aiclient.create_chat(ai_config_key, context, default_value, {})

    if not chat:
        print(f"AI chat configuration not enabled for: {ai_config_key}")
        return

    user_input = 'How can LaunchDarkly help me?'

    # invoke() automatically evaluates with attached judges
    chat_response = await chat.invoke(user_input)
    print("Response:", chat_response.message.content)

    # Await evaluation results
    if chat_response.evaluations and len(chat_response.evaluations) > 0:
        eval_results = await asyncio.gather(*chat_response.evaluations)
        results_to_display = [
            result.to_dict() if result is not None else "not evaluated"
            for result in eval_results
        ]
        print("Judge results:")
        print(json.dumps(results_to_display, indent=2, default=str))

    # Always flush events before closing — trailing events are at risk of being
    # lost otherwise, in short-lived scripts and long-running services alike.
    ldclient.get().flush()
    ldclient.get().close()

SDK : Évaluation directe par judge

Pour le mode agent ou les pipelines personnalisés, évaluez directement les paires input/output :

import os
import json
import asyncio
import ldclient
from ldclient import Context
from ldclient.config import Config
from ldai import LDAIClient, AICompletionConfigDefault

sdk_key = os.getenv('LAUNCHDARKLY_SDK_KEY')
judge_key = os.getenv('LAUNCHDARKLY_AI_JUDGE_KEY', 'sample-ai-judge-accuracy')

async def async_main():
    ldclient.set_config(Config(sdk_key))
    aiclient = LDAIClient(ldclient.get())

    context = (
        Context.builder('example-user-key')
        .kind('user')
        .name('Sandy')
        .build()
    )

    judge_default_value = AICompletionConfigDefault(enabled=False)

    # Get judge configuration from LaunchDarkly
    judge = await aiclient.create_judge(judge_key, context, judge_default_value)

    if not judge:
        print(f"AI judge configuration not enabled for key: {judge_key}")
        return

    input_text = 'You are a helpful assistant. How can you help me?'
    output_text = 'I can answer any question you have.'

    # Evaluate the input/output pair — always returns a JudgeResult in v0.18.0+
    judge_result = await judge.evaluate(input_text, output_text)

    if not judge_result.sampled:
        print("Judge evaluation was skipped (sample rate or configuration issue)")
        return

    # Track the consolidated result on the AI Config tracker if needed:
    # tracker = ai_config.create_tracker()
    # tracker.track_judge_result(judge_result)

    print("Judge Result:")
    print(json.dumps(judge_result.to_dict(), default=str))

    # Always flush events before closing — trailing events are at risk of being
    # lost otherwise, in short-lived scripts and long-running services alike.
    ldclient.get().flush()
    ldclient.get().close()

Note : L'évaluation directe n'enregistre pas automatiquement les métriques. Obtenez un tracker via ai_config.create_tracker() / aiConfig.createTracker!() et appelez tracker.track_judge_result(result) / tracker.trackJudgeResult(result) pour enregistrer les scores de l'AI Config que vous évaluez. (Cela consolide la paire antérieure track_eval_scores + track_judge_response qui a été supprimée en Python v0.18.0 / Node v0.17.0.)

Taux d'échantillonnage

Chaque réponse évaluée envoie une demande supplémentaire à votre fournisseur de modèle, augmentant l'utilisation de tokens et les coûts. Commencez par un pourcentage d'échantillonnage inférieur et augmentez-le uniquement si vous avez besoin de plus de couverture d'évaluation.

Vous pouvez ajuster les taux d'échantillonnage à tout moment dans la section Judges d'une variation, ou désactiver un judge en définissant son échantillonnage à 0 %.

Affichage des résultats

  1. Accédez à AI Configs > sélectionnez votre config
  2. Cliquez sur l'onglet Monitoring
  3. Sélectionnez Evaluator metrics dans le menu déroulant
  4. Consultez les scores par variation et plage de temps

Les résultats apparaissent dans 1-2 minutes suivant l'évaluation.

Utilisation dans les guardrails et expériences

Les métriques d'évaluation s'intègrent avec :

  • Guarded rollouts : Mettre en pause/annuler quand les scores chutent sous un seuil
  • Expériences : Comparer les variations en utilisant les métriques d'évaluation comme objectifs

Gestion des erreurs

Statut Cause Solution
404 Config/variation non trouvé Vérifiez l'existence des clés
400 Configuration judge invalide Vérifiez que judgeConfigKey existe
403 Permissions insuffisantes Vérifiez les permissions du token API
422 Clé métrique dupliquée Impossible d'attacher plusieurs judges avec la même clé métrique

Prochaines étapes

Après avoir attaché les judges :

  1. Définir le fallthrough sur les configs judge à une variation activée (requis)
  2. Surveiller les résultats dans l'onglet Monitoring
  3. Ajuster l'échantillonnage selon vos besoins de coût/couverture
  4. Configurer des guarded rollouts pour la détection automatique des régressions

Skills associées

  • aiconfig-create - Créer des AI Configs et des judges
  • aiconfig-targeting - Configurer les règles de targeting
  • aiconfig-variations - Gérer les variations

Références

Exemples Python SDK :

Exemples Node.js SDK :

Skills similaires