mcpField
C
configs-targeting

configs-targeting

github.com/launchdarkly開発281.8k 回インストール1 日前に更新
リポジトリ

configs-targeting について

LaunchDarkly AgentControl の設定にターゲティングルールを構成するスキルです。個別ターゲット、セグメント、属性ルール、各種ロールアウトを扱います。

主な機能

  1. 01個別およびセグメントのターゲット管理
  2. 02属性ルールの追加と並べ替え
  3. 03フォールスルーのバリエーション設定
  4. 04割合およびガード付きロールアウト
  5. 05多様な属性演算子の利用

活用シーン

  1. 01ユーザー階層ごとのバリエーション割り当て
  2. 02テストセグメントへの設定ターゲティング
  3. 03バリエーションの段階的な公開
  4. 04モデル属性による設定の振り分け

MCP 対応

認証とターゲティング操作には LaunchDarkly MCP サーバーのツールを優先します。MCP に相当するツールがない手順では REST API を使用できます。

インストール

コマンドをコピーしてターミナルで実行します。

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

インストール後、Claude Code で/configs-targetingを入力して読み込まれたか確認します。

同梱ファイルREADME.md

詳細

設定のターゲティング

設定のターゲティングルールを構成して、さまざまなコンテキストにどのバリエーションを配信するかを制御します。完了モードとエージェントモードの両方で同じように動作します。

前提条件

  • AgentControl が有効な LaunchDarkly アカウント
  • 書き込み権限を持つ API アクセストークン
  • プロジェクトキーと環境キー
  • バリエーションを持つ既存の設定(configs-create スキルを使用)

API アクセス

LaunchDarkly MCP サーバーのツールを優先してください。これらのツールは OAuth で認証されるため、トークンは不要です。MCP に相当するものがない手順があり、REST API を直接呼び出す必要がある場合は、このセッション用の API アクセストークンを貼り付けるようユーザーに依頼し、以下の例の {api_token} をそれに置き換えてください。認証情報を環境変数、.env ファイル、エージェント/MCP 設定ファイルから検索しないでください。

基本概念

評価順序

ターゲティングルールは次の順序で評価されます(フィーチャーフラグと同じです)。

  1. 個別ターゲット - 特定のコンテキストキー(最優先)
  2. セグメントルール - 事前定義されたセグメント
  3. カスタムルール - 属性ベースの条件(順番に評価)
  4. デフォルトルール - その他すべてに対するフォールスルー
  5. オフバリエーション - ターゲティングが無効な場合
セマンティックパッチ API

設定のターゲティングではセマンティックパッチ命令を使用します。

PATCH /api/v2/projects/{projectKey}/ai-configs/{configKey}/targeting
Content-Type: application/json; domain-model=launchdarkly.semanticpatch
主な概念
  • variationId: キーではなく UUID です。常に最初にターゲティングを取得して ID を入手してください。
  • 重み: 1000分の1単位(50000 = 50%、100000 = 100%)
  • 句のロジック: 複数の句 = AND、複数の値 = OR
  • null 属性: null/欠落した属性を持つルールはスキップされます

ワークフロー

ステップ 1: ターゲティングを取得する(バリエーション ID を含む)
curl -X GET "https://app.launchdarkly.com/api/v2/projects/{projectKey}/ai-configs/{configKey}/targeting" \
  -H "Authorization: {api_token}" \
  -H "LD-API-Version: beta"

レスポンスには、各バリエーションの _id(UUID)を含む variations 配列が含まれます。

ステップ 2: デフォルトルールを編集する

作成したバリエーションを配信するようにデフォルトルールを編集します。

重要: turnTargetingOn 命令は設定では機能しません。代わりに updateFallthroughVariationOrRollout を使用してください。

# 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"
    }]
  }'
ステップ 3: ターゲティングルールを追加する

属性ベースのルール:

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

パーセンテージロールアウト:

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

フォールスルー(デフォルトルール)を設定する:

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

Python 実装

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

命令リファレンス

注記: turnTargetingOn と turnTargetingOff は設定では機能しません。設定はデフォルトでターゲティングが有効になっています。設定を「有効化」するには、updateFallthroughVariationOrRollout を使用してフォールスルーを有効なバリエーションに設定してください。

ルール
Kind説明
addRule句とバリエーション/ロールアウトを持つルールを追加する
removeRuleruleId で削除する
replaceRulesすべてのルールを置き換える
reorderRules評価順序を変更する
updateRuleVariationOrRolloutルールが配信する内容を更新する
フォールスルー
Kind説明
updateFallthroughVariationOrRolloutデフォルトのバリエーションまたはロールアウトを設定する
個別ターゲット
Kind説明
addTargets特定のコンテキストキーをターゲットにする
removeTargets特定のターゲットを削除する
replaceTargetsすべてのターゲットを置き換える

演算子リファレンス

演算子説明例
in値がリストに含まれる["premium", "enterprise"]
contains文字列が含まれる["sonnet"]
startsWith文字列のプレフィックス["user-"]
endsWith文字列のサフィックス[".edu"]
matches正規表現の一致["^user-\\d+$"]
greaterThan / lessThan数値の比較[100]
before / after日付の比較["2024-12-31T00:00:00Z"]
semVerEqual / semVerGreaterThanバージョンの比較["2.0.0"]
segmentMatchセグメントのメンバーシップ["beta-testers"]

句の構造

{
  "contextKind": "user",
  "attribute": "email",
  "op": "endsWith",
  "values": [".edu"],
  "negate": false
}
  • 複数の句 = AND(すべてが一致する必要があります)
  • 複数の値 = OR(いずれかが一致すればよい)
  • negate: true は演算子を反転させます

ロールアウトの種類

手動パーセンテージロールアウト
{
  "percentageRolloutConfig": {
    "contextKind": "user",
    "bucketBy": "key",
    "variations": [
      {"variation": 0, "weight": 50000},
      {"variation": 1, "weight": 50000}
    ]
  }
}
プログレッシブロールアウト
{
  "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"}}
    ]
  }
}
ガード付きロールアウト
{
  "guardedRolloutConfig": {
    "randomizationUnit": "user",
    "stages": [
      {"rolloutWeight": 1000, "monitoringWindowMilliseconds": 17280000},
      {"rolloutWeight": 5000, "monitoringWindowMilliseconds": 17280000}
    ],
    "metrics": [{
      "metricKey": "error-rate",
      "onRegression": {"rollback": true},
      "regressionThreshold": 0.01
    }]
  }
}

一般的なパターン

属性によるモデルルーティング
# 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"
)
ティアベースのバリエーション
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
)
セグメントターゲティング
targeting.target_segment(
    config_key="chat-assistant",
    environment="production",
    segment_keys=["beta-testers"],
    variation=1  # Experimental variation
)

エラー処理

ステータス原因解決策
400無効なセマンティックパッチ命令の形式を確認してください。ops は小文字である必要があります
403権限が不足していますAPI トークンを確認してください
404設定が見つかりませんprojectKey と configKey を確認してください
422無効なバリエーションインデックス(0、1、2...)またはターゲティング応答の UUID を使用してください

次のステップ

ターゲティングを構成した後:

  1. 設定 URL を指定します:
    https://app.launchdarkly.com/projects/{projectKey}/ai-configs/{configKey}
    
  2. built-in-metrics でパフォーマンスを監視します
  3. online-evals でジャッジをアタッチします
  4. 自動回帰検出のためにガード付きロールアウトを設定します

関連スキル

  • configs-create - バリエーションを含む設定を作成します
  • configs-variations - バリエーションを管理します
  • online-evals - ジャッジをアタッチします
  • segments - ターゲティング用のセグメントを作成します

その他のリソース

コメント