aiconfig-targeting

Par launchdarkly · agent-skills

Configurez les règles de ciblage d'AI Config pour contrôler quelles variations sont servies aux différents utilisateurs. Activez les déploiements progressifs par pourcentage, les règles basées sur les attributs, le ciblage par segment et les déploiements contrôlés.

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

Ciblage des AI Configs

Configurez les règles de ciblage pour les AI Configs afin de contrôler quelles variations sont servies selon différents contextes. Fonctionne de la même manière en mode completion et en mode agent.

Prérequis

  • Compte LaunchDarkly avec AI Configs activé
  • Token d'accès API avec permissions d'écriture
  • Clé de projet et clé d'environnement
  • AI Config existant avec des variations (utilisez la skill aiconfig-create)

Détection de clé API

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

Concepts clés

Ordre d'évaluation

Les règles de ciblage s'évaluent dans cet ordre (même que les feature flags) :

  1. Cibles individuelles - Clés de contexte spécifiques (priorité maximale)
  2. Règles de segment - Segments prédéfinis
  3. Règles personnalisées - Conditions basées sur des attributs (évaluées dans l'ordre)
  4. Règle par défaut - Repli pour tous les autres
  5. Variation désactivée - Quand le ciblage est désactivé

API Semantic Patch

Le ciblage des AI Configs utilise les instructions semantic patch :

PATCH /api/v2/projects/{projectKey}/ai-configs/{configKey}/targeting
Content-Type: application/json; domain-model=launchdarkly.semanticpatch

Concepts clés

  • variationId : UUIDs, pas des clés. Récupérez toujours le ciblage d'abord pour obtenir les IDs.
  • Poids : Millièmes (50000 = 50%, 100000 = 100%)
  • Logique de clauses : Plusieurs clauses = ET, plusieurs valeurs = OU
  • Attributs nulls : Les règles avec attributs null/manquants sont ignorées

Workflow

Étape 1 : Obtenir le ciblage (avec IDs de variations)

curl -X GET "https://app.launchdarkly.com/api/v2/projects/{projectKey}/ai-configs/{configKey}/targeting" \
  -H "Authorization: {api_token}" \
  -H "LD-API-Version: beta"

La réponse inclut un tableau variations avec _id (UUID) pour chaque variation.

Étape 2 : Modifier la règle par défaut

Modifiez la règle par défaut pour servir la variation que vous avez créée.

Important : L'instruction turnTargetingOn ne fonctionne pas pour les AI Configs. Utilisez updateFallthroughVariationOrRollout à la place.

# D'abord, récupérez les IDs de variations de la réponse de l'Étape 1
# Ensuite, définissez le repli vers la variation activée (ex : variation "Default")
curl -X PATCH "https://app.launchdarkly.com/api/v2/projects/{projectKey}/ai-configs/{configKey}/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-enabled-variation-uuid"
    }]
  }'

Étape 3 : Ajouter des règles de ciblage

Règle basée sur les attributs :

curl -X PATCH "https://app.launchdarkly.com/api/v2/projects/{projectKey}/ai-configs/{configKey}/targeting" \
  -H "Authorization: {api_token}" \
  -H "Content-Type: application/json; domain-model=launchdarkly.semanticpatch" \
  -H "LD-API-Version: beta" \
  -d '{
    "environmentKey": "production",
    "instructions": [{
      "kind": "addRule",
      "clauses": [{
        "contextKind": "user",
        "attribute": "selectedModel",
        "op": "contains",
        "values": ["sonnet"],
        "negate": false
      }],
      "variation": 0
    }]
  }'

Déploiement progressif en pourcentage :

curl -X PATCH "..." \
  -d '{
    "environmentKey": "production",
    "instructions": [{
      "kind": "addRule",
      "clauses": [{
        "contextKind": "user",
        "attribute": "tier",
        "op": "in",
        "values": ["premium"],
        "negate": false
      }],
      "percentageRolloutConfig": {
        "contextKind": "user",
        "bucketBy": "key",
        "variations": [
          {"variation": 0, "weight": 60000},
          {"variation": 1, "weight": 40000}
        ]
      }
    }]
  }'

Définir le repli (règle par défaut) :

curl -X PATCH "..." \
  -d '{
    "environmentKey": "production",
    "instructions": [{
      "kind": "updateFallthroughVariationOrRollout",
      "variationId": "fallback-variation-uuid"
    }]
  }'

Implémentation Python

import requests
import os
from typing import Dict, List, Optional

class AIConfigTargeting:
    """Manager for AI Config targeting rules"""

    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"

    def get_targeting(self, config_key: str) -> Optional[Dict]:
        """Get current targeting with variation IDs."""
        url = f"{self.base_url}/projects/{self.project_key}/ai-configs/{config_key}/targeting"

        response = requests.get(url, headers={
            "Authorization": self.api_token,
            "LD-API-Version": "beta"
        })

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

    def get_variation_id(self, config_key: str, variation_key: str) -> Optional[str]:
        """Look up variation UUID from key or name."""
        targeting = self.get_targeting(config_key)
        if targeting:
            for var in targeting.get("variations", []):
                if var.get("key") == variation_key or var.get("name") == variation_key:
                    return var.get("_id")
        return None

    def update_targeting(self, config_key: str, environment: str,
                         instructions: List[Dict], comment: str = "") -> Optional[Dict]:
        """Send semantic patch instructions."""
        url = f"{self.base_url}/projects/{self.project_key}/ai-configs/{config_key}/targeting"

        payload = {"environmentKey": environment, "instructions": instructions}
        if comment:
            payload["comment"] = comment

        response = requests.patch(url, headers={
            "Authorization": self.api_token,
            "Content-Type": "application/json; domain-model=launchdarkly.semanticpatch",
            "LD-API-Version": "beta"
        }, json=payload)

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

    def enable_config(self, config_key: str, environment: str,
                      variation_key: str = "default") -> bool:
        """
        Enable an AI Config by setting fallthrough to an enabled variation.

        Note: turnTargetingOn doesn't work for AI Configs. Instead, set the
        fallthrough from the disabled variation (index 0) to an enabled one.
        """
        variation_id = self.get_variation_id(config_key, variation_key)
        if not variation_id:
            print(f"[ERROR] Variation '{variation_key}' not found")
            return False
        return self.set_fallthrough(config_key, environment, variation_id)

    def add_rule(self, config_key: str, environment: str,
                 clauses: List[Dict], variation: int,
                 description: str = "") -> bool:
        """Add targeting rule serving a specific variation index."""
        instruction = {
            "kind": "addRule",
            "clauses": clauses,
            "variation": variation
        }
        if description:
            instruction["description"] = description

        result = self.update_targeting(config_key, environment,
            [instruction], f"Add rule: {description}")
        if result:
            print(f"[OK] Rule added")
            return True
        return False

    def add_rollout_rule(self, config_key: str, environment: str,
                         clauses: List[Dict],
                         weights: List[Dict],
                         bucket_by: str = "key") -> bool:
        """
        Add percentage rollout rule.

        weights: [{"variation": 0, "weight": 50000}, {"variation": 1, "weight": 50000}]
        """
        result = self.update_targeting(config_key, environment, [{
            "kind": "addRule",
            "clauses": clauses,
            "percentageRolloutConfig": {
                "contextKind": "user",
                "bucketBy": bucket_by,
                "variations": weights
            }
        }], "Add percentage rollout")
        if result:
            print(f"[OK] Rollout rule added")
            return True
        return False

    def set_fallthrough(self, config_key: str, environment: str,
                        variation_id: str) -> bool:
        """Set default (fallthrough) variation by UUID."""
        result = self.update_targeting(config_key, environment, [{
            "kind": "updateFallthroughVariationOrRollout",
            "variationId": variation_id
        }], "Set fallthrough")
        if result:
            print(f"[OK] Fallthrough set")
            return True
        return False

    def target_individuals(self, config_key: str, environment: str,
                          context_keys: List[str], variation: int,
                          context_kind: str = "user") -> bool:
        """Target specific context keys."""
        result = self.update_targeting(config_key, environment, [{
            "kind": "addTargets",
            "variation": variation,
            "contextKind": context_kind,
            "values": context_keys
        }], f"Target {len(context_keys)} individuals")
        if result:
            print(f"[OK] Individual targets added")
            return True
        return False

    def target_segment(self, config_key: str, environment: str,
                      segment_keys: List[str], variation: int) -> bool:
        """Target a segment."""
        result = self.update_targeting(config_key, environment, [{
            "kind": "addRule",
            "clauses": [{
                "attribute": "segmentMatch",
                "contextKind": "",  # Leave blank for segments
                "op": "segmentMatch",
                "values": segment_keys,
                "negate": False
            }],
            "variation": variation
        }], f"Target segments: {segment_keys}")
        if result:
            print(f"[OK] Segment targeting added")
            return True
        return False

    def clear_rules(self, config_key: str, environment: str) -> bool:
        """Remove all targeting rules."""
        result = self.update_targeting(config_key, environment,
            [{"kind": "replaceRules", "rules": []}], "Clear all rules")
        if result:
            print(f"[OK] All rules cleared")
            return True
        return False

Référence des instructions

Note : turnTargetingOn et turnTargetingOff ne fonctionnent pas pour les AI Configs. Les AI Configs ont le ciblage activé par défaut. Pour « activer » une config, définissez le repli vers une variation activée en utilisant updateFallthroughVariationOrRollout.

Règles

Kind Description
addRule Ajouter une règle avec clauses et variation/déploiement
removeRule Supprimer par ruleId
replaceRules Remplacer toutes les règles
reorderRules Modifier l'ordre d'évaluation
updateRuleVariationOrRollout Mettre à jour ce qu'une règle sert

Repli

Kind Description
updateFallthroughVariationOrRollout Définir la variation ou le déploiement par défaut

Cibles individuelles

Kind Description
addTargets Cibler des clés de contexte spécifiques
removeTargets Supprimer des cibles spécifiques
replaceTargets Remplacer toutes les cibles

Référence des opérateurs

Operator Description Exemple
in Valeur dans une liste ["premium", "enterprise"]
contains La chaîne contient ["sonnet"]
startsWith Préfixe de chaîne ["user-"]
endsWith Suffixe de chaîne [".edu"]
matches Correspondance regex ["^user-\\d+$"]
greaterThan / lessThan Comparaison numérique [100]
before / after Comparaison de date ["2024-12-31T00:00:00Z"]
semVerEqual / semVerGreaterThan Comparaison de version ["2.0.0"]
segmentMatch Appartenance au segment ["beta-testers"]

Structure de clause

{
  "contextKind": "user",
  "attribute": "email",
  "op": "endsWith",
  "values": [".edu"],
  "negate": false
}
  • Plusieurs clauses = ET (toutes doivent correspondre)
  • Plusieurs valeurs = OU (une quelconque peut correspondre)
  • negate: true inverse l'opérateur

Types de déploiement

Déploiement progressif manuel en pourcentage

{
  "percentageRolloutConfig": {
    "contextKind": "user",
    "bucketBy": "key",
    "variations": [
      {"variation": 0, "weight": 50000},
      {"variation": 1, "weight": 50000}
    ]
  }
}

Déploiement progressif

{
  "progressiveRolloutConfig": {
    "contextKind": "user",
    "controlVariation": 1,
    "endVariation": 0,
    "steps": [
      {"rolloutWeight": 1000, "duration": {"quantity": 4, "unit": "hour"}},
      {"rolloutWeight": 5000, "duration": {"quantity": 4, "unit": "hour"}},
      {"rolloutWeight": 10000, "duration": {"quantity": 4, "unit": "hour"}}
    ]
  }
}

Déploiement sécurisé

{
  "guardedRolloutConfig": {
    "randomizationUnit": "user",
    "stages": [
      {"rolloutWeight": 1000, "monitoringWindowMilliseconds": 17280000},
      {"rolloutWeight": 5000, "monitoringWindowMilliseconds": 17280000}
    ],
    "metrics": [{
      "metricKey": "error-rate",
      "onRegression": {"rollback": true},
      "regressionThreshold": 0.01
    }]
  }
}

Motifs courants

Routage de modèle par attribut

# Router selon l'attribut de contexte selectedModel
targeting.add_rule(
    config_key="model-selector",
    environment="production",
    clauses=[{
        "contextKind": "user",
        "attribute": "selectedModel",
        "op": "contains",
        "values": ["sonnet"],
        "negate": False
    }],
    variation=0,  # Index de variation Sonnet
    description="Route sonnet requests"
)

Variation basée sur le tier

targeting.add_rule(
    config_key="chat-assistant",
    environment="production",
    clauses=[{
        "contextKind": "user",
        "attribute": "tier",
        "op": "in",
        "values": ["premium", "enterprise"],
        "negate": False
    }],
    variation=0  # Variation de modèle premium
)

Ciblage de segment

targeting.target_segment(
    config_key="chat-assistant",
    environment="production",
    segment_keys=["beta-testers"],
    variation=1  # Variation expérimentale
)

Gestion des erreurs

Status Cause Solution
400 Semantic patch invalide Vérifiez le format des instructions, les ops doivent être en minuscules
403 Permissions insuffisantes Vérifiez le token API
404 Config non trouvée Vérifiez projectKey et configKey
422 Variation invalide Utilisez l'index (0, 1, 2...) ou l'UUID de la réponse de ciblage

Étapes suivantes

Après la configuration du ciblage :

  1. Fournir l'URL de la config :
    https://app.launchdarkly.com/projects/{projectKey}/ai-configs/{configKey}
  2. Surveiller les performances avec aiconfig-ai-metrics
  3. Attacher des juges avec aiconfig-online-evals
  4. Configurer des déploiements sécurisés pour la détection automatique de régression

Skills connexes

  • aiconfig-create - Créer des AI Configs avec variations
  • aiconfig-variations - Gérer les variations
  • aiconfig-online-evals - Attacher des juges
  • aiconfig-segments - Créer des segments pour le ciblage

Références

Skills similaires