configs-targeting

作成者: launchdarkly

コンフィグターゲティングルールを設定して、どのバリエーションをどのユーザーに配信するかを制御します。パーセンテージロールアウト、属性ベースのルール、セグメントターゲティングなどを有効にします。

npx skills add https://github.com/launchdarkly/ai-tooling --skill configs-targeting

Config Targeting

Configure targeting rules for configs to control which variations serve to different contexts. Works the same for both completion and agent mode.

Prerequisites

  • LaunchDarkly account with AgentControl enabled
  • API access token with write permissions
  • Project key and environment key
  • Existing config with variations (use configs-create skill)

API Key Detection

  1. Check environment variables - LAUNCHDARKLY_API_KEY, LAUNCHDARKLY_API_TOKEN, LD_API_KEY
  2. Check MCP config - Claude: ~/.claude/config.json -> mcpServers.launchdarkly.env.LAUNCHDARKLY_API_KEY
  3. Prompt user - Only if detection fails

Core Concepts

Evaluation Order

Targeting rules evaluate in this order (same as feature flags):

  1. Individual targets - Specific context keys (highest priority)
  2. Segment rules - Pre-defined segments
  3. Custom rules - Attribute-based conditions (evaluated in order)
  4. Default rule - Fallthrough for all others
  5. Off variation - When targeting is disabled

Semantic Patch API

config targeting uses semantic patch instructions:

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

Key Concepts

  • variationId: UUIDs, not keys. Always fetch targeting first to get IDs.
  • Weights: Thousandths (50000 = 50%, 100000 = 100%)
  • Clause logic: Multiple clauses = AND, multiple values = OR
  • Null attributes: Rules with null/missing attributes are skipped

Workflow

Step 1: Get Targeting (with Variation IDs)

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

Response includes variations array with _id (UUID) for each variation.

Step 2: Edit the Default Rule

Edit the default rule to serve the variation you created.

Important: The turnTargetingOn instruction does not work for configs. Use updateFallthroughVariationOrRollout instead.

# First, get variation IDs from Step 1 response
# Then set fallthrough to the enabled variation (e.g., "Default" variation)
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"
    }]
  }'

Step 3: Add Targeting Rules

Attribute-based rule:

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
    }]
  }'

Percentage rollout:

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}
        ]
      }
    }]
  }'

Set fallthrough (default rule):

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

Python Implementation

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

class AIConfigTargeting:
    """Manager for 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 a config by setting fallthrough to an enabled variation.

        Note: turnTargetingOn doesn't work for 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

Instruction Reference

Note: turnTargetingOn and turnTargetingOff do not work for configs. Configs have targeting enabled by default. To "enable" a config, set the fallthrough to an enabled variation using updateFallthroughVariationOrRollout.

Rules

KindDescription
addRuleAdd rule with clauses and variation/rollout
removeRuleRemove by ruleId
replaceRulesReplace all rules
reorderRulesChange evaluation order
updateRuleVariationOrRolloutUpdate what a rule serves

Fallthrough

KindDescription
updateFallthroughVariationOrRolloutSet default variation or rollout

Individual Targets

KindDescription
addTargetsTarget specific context keys
removeTargetsRemove specific targets
replaceTargetsReplace all targets

Operators Reference

OperatorDescriptionExample
inValue in list["premium", "enterprise"]
containsString contains["sonnet"]
startsWithString prefix["user-"]
endsWithString suffix[".edu"]
matchesRegex match["^user-\\d+$"]
greaterThan / lessThanNumeric comparison[100]
before / afterDate comparison["2024-12-31T00:00:00Z"]
semVerEqual / semVerGreaterThanVersion comparison["2.0.0"]
segmentMatchSegment membership["beta-testers"]

Clause Structure

{
  "contextKind": "user",
  "attribute": "email",
  "op": "endsWith",
  "values": [".edu"],
  "negate": false
}
  • Multiple clauses = AND (all must match)
  • Multiple values = OR (any can match)
  • negate: true inverts the operator

Rollout Types

Manual Percentage Rollout

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

Progressive Rollout

{
  "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"}}
    ]
  }
}

Guarded Rollout

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

Common Patterns

Model Routing by Attribute

# Route based on selectedModel context attribute
targeting.add_rule(
    config_key="model-selector",
    environment="production",
    clauses=[{
        "contextKind": "user",
        "attribute": "selectedModel",
        "op": "contains",
        "values": ["sonnet"],
        "negate": False
    }],
    variation=0,  # Sonnet variation index
    description="Route sonnet requests"
)

Tier-Based Variation

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

Segment Targeting

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

Error Handling

StatusCauseSolution
400Invalid semantic patchCheck instruction format, ops must be lowercase
403Insufficient permissionsCheck API token
404Config not foundVerify projectKey and configKey
422Invalid variationUse index (0, 1, 2...) or UUID from targeting response

Next Steps

After configuring targeting:

  1. Provide config URL:
    https://app.launchdarkly.com/projects/{projectKey}/ai-configs/{configKey}
    
  2. Monitor performance with built-in-metrics
  3. Attach judges with online-evals
  4. Set up guarded rollouts for automatic regression detection

Related Skills

  • configs-create - Create configs with variations
  • configs-variations - Manage variations
  • online-evals - Attach judges
  • segments - Create segments for targeting

Other Resources

launchdarklyのその他のスキル

aiconfig-online-evals
launchdarkly
非推奨のリダイレクト — このスキルはonline-evalsに名称変更されました。このスキルは使用せず、代わりにonline-evalsを呼び出してください。古い参照のためにのみ保持されています…
official
launchdarkly-experiment-setup
launchdarkly
LaunchDarklyで実験を設定して実行します。メトリクス、トリートメント、フラグ設定を用いて実験を作成し、データ収集のためのイテレーションを開始し、デザインを切り替えます…
official
custom-metrics
launchdarkly
カスタムビジネスメトリクスを作成、追跡、取得、更新、削除して設定を管理します。API経由でメトリクスの種類を定義し、SDK経由でイベントを発行するなど、完全なライフサイクルをカバーします。
official
projects
launchdarkly
LaunchDarklyプロジェクトをコードベースに設定するためのガイドです。スタックの評価、適切なアプローチの選択、プロジェクト管理の統合を支援します…
official
aiconfig-ai-metrics
launchdarkly
非推奨のリダイレクト — このスキルはbuilt-in-metricsに名称変更されました。このスキルは使用せず、代わりにbuilt-in-metricsを呼び出してください。古い参照のためにのみ保持されています…
official
aiconfig-projects
launchdarkly
非推奨のリダイレクト — このスキルはprojectsに名称変更されました。このスキルは使用せず、代わりにprojectsを呼び出してください。古いaiconfig-projectsへの参照のためにのみ保持されています…
official
aiconfig-migrate
launchdarkly
非推奨のリダイレクト — このスキルはmigrateに名称変更されました。このスキルは使用せず、代わりにmigrateを呼び出してください。古いaiconfig-migrateへの参照が引き続き機能するようにのみ保持されています…
official
aiconfig-tools
launchdarkly
非推奨のリダイレクト — このスキルはtoolsに名称変更されました。このスキルは使用せず、代わりにtoolsを呼び出してください。古い参照先であるaiconfig-toolsが引き続き機能するようにのみ保持されています。
official