configs-targeting

Par launchdarkly · agent-skills

Configurez des règles de ciblage 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 configs-targeting

Ciblage des configurations

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

Conditions préalables

  • Compte LaunchDarkly avec AgentControl activé
  • Token d'accès API avec permissions d'écriture
  • Clé de projet et clé d'environnement
  • Configuration existante avec variations (utilisez la skill configs-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 - Seulement si la détection échoue

Concepts fondamentaux

Ordre d'évaluation

Les règles de ciblage s'évaluent dans cet ordre (identique aux feature flags) :

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

API Semantic Patch

Le ciblage de configuration 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 en premier pour obtenir les IDs.
  • Poids : Millièmes (50000 = 50%, 100000 = 100%)
  • Logique des clauses : Plusieurs clauses = AND, plusieurs valeurs = OR
  • Attributs nuls : Les règles avec attributs nuls/manquants sont ignorées

Flux de travail

Étape 1 : Récupérer le ciblage (avec IDs de variation)

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 configurations. Utilisez updateFallthroughVariationOrRollout à la place.

# D'abord, obtenez les IDs de variation de la réponse de l'étape 1
# Puis définissez le fallthrough à la variation activée (par exemple, la 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 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 fallthrough (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 pour les règles de ciblage de configuration"""

    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]:
        """Récupère le ciblage actuel avec les IDs de variation."""
        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]:
        """Cherche l'UUID de variation à partir de la clé ou du nom."""
        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]:
        """Envoie les instructions semantic patch."""
        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:
        """
        Active une configuration en définissant le fallthrough à une variation activée.

        Note : turnTargetingOn ne fonctionne pas pour les configurations. À la place,
        définissez le fallthrough de la variation désactivée (index 0) à une activée.
        """
        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:
        """Ajoute une règle de ciblage servant un index de variation spécifique."""
        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:
        """
        Ajoute une règle de déploiement en pourcentage.

        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:
        """Définit la variation par défaut (fallthrough) par 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:
        """Cible des clés de contexte spécifiques."""
        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:
        """Cible un segment."""
        result = self.update_targeting(config_key, environment, [{
            "kind": "addRule",
            "clauses": [{
                "attribute": "segmentMatch",
                "contextKind": "",  # Laissez vide pour les 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:
        """Supprime toutes les règles de ciblage."""
        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 configurations. Les configurations ont le ciblage activé par défaut. Pour « activer » une configuration, définissez le fallthrough à une variation activée en utilisant updateFallthroughVariationOrRollout.

Règles

Kind Description
addRule Ajoute une règle avec clauses et variation/déploiement
removeRule Supprime par ruleId
replaceRules Remplace toutes les règles
reorderRules Change l'ordre d'évaluation
updateRuleVariationOrRollout Met à jour ce qu'une règle sert

Fallthrough

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

Cibles individuelles

Kind Description
addTargets Cible des clés de contexte spécifiques
removeTargets Supprime des cibles spécifiques
replaceTargets Remplace toutes les cibles

Référence des opérateurs

Opérateur Description Exemple
in Valeur dans la liste ["premium", "enterprise"]
contains Contient une chaîne ["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 des clauses

{
  "contextKind": "user",
  "attribute": "email",
  "op": "endsWith",
  "values": [".edu"],
  "negate": false
}
  • Plusieurs clauses = AND (toutes doivent correspondre)
  • Plusieurs valeurs = OR (n'importe quelle valeur peut correspondre)
  • negate: true inverse l'opérateur

Types de déploiement

Déploiement en pourcentage manuel

{
  "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 gardé

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

Modèles courants

Routage de modèle par attribut

# Route basé sur 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 du 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

Statut Cause Solution
400 Semantic patch invalide Vérifiez le format de l'instruction, les ops doivent être en minuscules
403 Permissions insuffisantes Vérifiez le token API
404 Configuration 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

Prochaines étapes

Après avoir configuré le ciblage :

  1. Fournir l'URL de configuration :
    https://app.launchdarkly.com/projects/{projectKey}/ai-configs/{configKey}
  2. Surveiller les performances avec built-in-metrics
  3. Ajouter des judges avec online-evals
  4. Configurer des déploiements gardés pour la détection automatique des régressions

Skills associées

  • configs-create - Créer des configurations avec variations
  • configs-variations - Gérer les variations
  • online-evals - Ajouter des judges
  • segments - Créer des segments pour le ciblage

Références

Skills similaires