

# 使用目標型路由執行 A/B 測試
<a name="ab-testing-target-based"></a>

當您測試的變更涉及程式碼變更、架構升級或完全不同的代理程式實作時，請使用目標型路由模式。目標型路由會在相同 AgentCore 執行期 （具名端點） 的多個版本之間或在完全不同的 AgentCore 執行期之間路由流量。AgentCore Gateway 會將每個端點註冊為個別目標，並根據 A/B 測試的流量權重，將每個工作階段路由至一個端點或另一個端點。

目標型 A/B 測試的金鑰組態：
+ 變體組態：`variantConfiguration.target`使用 AgentCore Gateway 目標名稱
+ 評估組態：`perVariantOnlineEvaluationConfig`（每個變體一個線上評估組態，因為每個端點都有自己的日誌群組）
+ 閘道篩選條件：A/B 測試攔截的 AgentCore Gateway 路徑`gatewayFilter.targetPaths`範圍

此演練會部署兩個版本的客戶支援代理程式：一個使用 Claude Sonnet （控制），另一個使用 Claude Opus （處理），為每個版本建立具名端點、建立 A/B 測試、傳送流量、檢閱結果，以及部署獲勝者。

**注意**  
此逐步解說適用於在 AgentCore 執行時間上託管的客服人員。如果您的代理程式在 AgentCore 執行期 ****（第三方或自我託管代理程式，例如在 AWS Lambda 上執行） 之外執行，請參閱[改為對在 AgentCore 之外託管的代理程式執行 A/B 測試](ab-testing-3p-agents.md)。

如需 A/B 測試模式的詳細比較，請參閱[選擇模式](ab-testing.md#ab-testing-pattern-comparison)。

## 步驟 1：建立專案
<a name="target-based-create-project"></a>

使用 AgentCore CLI 建立專案：

```
agentcore create --name ABTestTargetBased --no-agent
cd ABTestTargetBased
```

## 步驟 2：新增執行時間
<a name="target-based-add-runtime"></a>

新增代理程式執行時間。您將部署此執行時間的兩個版本，一個用於控制，另一個用於處理，然後建立具名端點以別名每個版本。

```
agentcore add agent \
  --name csAgent \
  --language Python \
  --framework Strands \
  --model-provider Bedrock \
  --memory none \
  --build CodeZip
```

專案結構：

```
ABTestTargetBased/
├── agentcore/
│   ├── agentcore.json
│   ├── aws-targets.json
│   └── cdk/
└── app/
    └── csAgent/
        ├── main.py
        └── pyproject.toml
```

## 步驟 3：部署控制和處理版本
<a name="target-based-agent-code"></a>

將 取代`app/csAgent/main.py`為控制版本 （使用 Claude Sonnet)：

```
"""Customer support agent — control variant."""
from strands import Agent, tool
from strands.models.bedrock import BedrockModel
from bedrock_agentcore.runtime import BedrockAgentCoreApp

app = BedrockAgentCoreApp()
MODEL_ID = "global.anthropic.claude-sonnet-4-5-20250929-v1:0"
SYSTEM_PROMPT = "You are a helpful customer support assistant for Acme Store."


@tool
def lookup_order(order_id: str) -> str:
    """Look up an order by ID."""
    orders = {
        "ORD-1001": {"status": "delivered", "item": "Blue T-Shirt", "total": "$29.99"},
        "ORD-1002": {"status": "in_transit", "item": "Running Shoes", "est_delivery": "2026-04-05"},
        "ORD-1003": {"status": "delayed", "item": "Wireless Headphones", "days_late": 5},
    }
    return str(orders.get(order_id, {"error": f"Order {order_id} not found"}))


@tool
def initiate_return(order_id: str, reason: str) -> str:
    """Initiate a return for an order."""
    return f"Return initiated for {order_id}. Reason: {reason}. Return label sent to customer email."


@tool
def apply_discount(order_id: str, discount_percent: int, reason: str) -> str:
    """Apply a discount to an order."""
    return f"Applied {discount_percent}% discount to {order_id}. Reason: {reason}."


agent = Agent(
    model=BedrockModel(model_id=MODEL_ID),
    tools=[lookup_order, initiate_return, apply_discount],
    system_prompt=SYSTEM_PROMPT,
)


@app.entrypoint
def invoke(payload, context):
    result = agent(payload.get("prompt", "Hello"))
    return {"response": result.message["content"][0]["text"]}


if __name__ == "__main__":
    app.run()
```

更新`app/csAgent/pyproject.toml`相依性：

```
dependencies = [
    "aws-opentelemetry-distro",
    "bedrock-agentcore >= 1.8.0",
    "boto3",
    "botocore[crt] >= 1.35.0",
    "strands-agents[otel] >= 1.13.0",
    "opentelemetry-distro",
    "opentelemetry-instrumentation",
]
```

部署控制版本 （這會建立版本 1)：

```
agentcore deploy
```

現在更新`main.py`為針對處理變體使用不同的模型並部署 （這會建立第 2 版）：

```
MODEL_ID = "global.anthropic.claude-opus-4-6-v1"
```

```
agentcore deploy
```

為每個版本建立具名端點並部署：

```
agentcore add runtime-endpoint \
  --runtime csAgent \
  --endpoint control \
  --version 1 \
  --description "Control variant — Claude Sonnet"

agentcore add runtime-endpoint \
  --runtime csAgent \
  --endpoint treatment \
  --version 2 \
  --description "Treatment variant — Claude Opus"

agentcore deploy
```

您現在擁有：
+ 執行時間端點 `control` — 使用 Claude Sonnet 提供第 1 版。
+ 執行時間端點 `treatment` — 使用 Claude Opus 提供第 2 版。

驗證執行時間是否正常運作：

```
agentcore invoke --runtime csAgent --prompt "What is the status of order ORD-1003?"
```

您現在擁有：
+ 執行時間端點 `control` — 使用 Claude Sonnet 提供第 1 版。
+ 執行時間端點 `treatment` — 使用 Claude Opus 提供第 2 版。

## 步驟 4：建立線上評估組態
<a name="target-based-online-eval"></a>

每個端點都有自己的日誌群組 （日誌群組名稱結尾為端點名稱），因此每個變體都需要一個線上評估組態：

```
agentcore add online-eval \
  --name controlEvalTb \
  --runtime csAgent \
  --endpoint control \
  --evaluator "Builtin.Helpfulness" \
  --sampling-rate 100.0 \
  --enable-on-create

agentcore add online-eval \
  --name treatmentEvalTb \
  --runtime csAgent \
  --endpoint treatment \
  --evaluator "Builtin.Helpfulness" \
  --sampling-rate 100.0 \
  --enable-on-create

agentcore deploy
```

每次部署後，請注意線上評估組態 ARN — 建立 A/B 測試時，您將需要兩者。

如需評估器選項和組態的詳細資訊，請參閱[建立線上評估](create-online-evaluations.md)。

## 步驟 5：建立閘道和目標
<a name="target-based-create-gateway"></a>

以目標為基礎的 A/B 測試會透過 AgentCore Gateway 路由流量，因此在開始測試之前，必須先部署閘道及其兩個目標。新增閘道並將每個執行時間端點註冊為`http-runtime`目標，然後部署：

```
agentcore add gateway --name csGateway

agentcore add gateway-target \
  --name customer-support-control \
  --gateway csGateway \
  --type http-runtime \
  --runtime csAgent \
  --runtime-endpoint control

agentcore add gateway-target \
  --name customer-support-treatment \
  --gateway csGateway \
  --type http-runtime \
  --runtime csAgent \
  --runtime-endpoint treatment

agentcore deploy
```

## 步驟 6：建立 A/B 測試
<a name="target-based-create-test"></a>

使用 啟動 A/B 測試`agentcore run ab-test`。每個變體都會參考您建立的其中一個閘道目標，並擁有自己的線上評估組態。命令會針對已部署的閘道，直接在服務上啟動測試。

**Example**  

```
agentcore run ab-test \
  --mode target-based \
  --name customerSupportTargetTest \
  --gateway csGateway \
  --runtime csAgent \
  --control-target customer-support-control \
  --treatment-target customer-support-treatment \
  --control-online-eval controlEvalTb \
  --treatment-online-eval treatmentEvalTb \
  --control-weight 80 \
  --treatment-weight 20
```
一旦命令傳回，測試即為 RUNNING。傳遞 `--disable-on-create` 以將其建立已停止。`--gateway` 旗標為必要項目，且必須參考您在步驟 5 中部署的閘道。每個閘道一次只能執行一個測試。命令會列印測試的任務 ID，也可從 欄位取得`--json``id`。您需要以下生命週期命令的此 ID。

```
import boto3
import uuid

REGION = "us-west-2"
ACCOUNT_ID = "123456789012"

# Runtime ARNs from Step 2 deployment output
CONTROL_RUNTIME_ARN = f"arn:aws:bedrock-agentcore:{REGION}:{ACCOUNT_ID}:runtime/ABTestTargetBased_CustomerSupportControl-abc123"
TREATMENT_RUNTIME_ARN = f"arn:aws:bedrock-agentcore:{REGION}:{ACCOUNT_ID}:runtime/ABTestTargetBased_CustomerSupportTreatment-def456"

# Online evaluation config ARNs from Step 3
CONTROL_EVAL_ARN = f"arn:aws:bedrock-agentcore:{REGION}:{ACCOUNT_ID}:online-evaluation-config/controlEvalTb-abc123"
TREATMENT_EVAL_ARN = f"arn:aws:bedrock-agentcore:{REGION}:{ACCOUNT_ID}:online-evaluation-config/treatmentEvalTb-def456"

# IAM roles
GATEWAY_ROLE_ARN = f"arn:aws:iam::{ACCOUNT_ID}:role/AgentCoreGatewayRole"
AB_TEST_ROLE_ARN = f"arn:aws:iam::{ACCOUNT_ID}:role/ABTestRole"

cp_client = boto3.client("bedrock-agentcore-control", region_name=REGION)
dp_client = boto3.client("bedrock-agentcore", region_name=REGION)

# 1. Create an AgentCore Gateway
gateway_response = cp_client.create_gateway(
    name="customerSupportTargetTest-gw",
    roleArn=GATEWAY_ROLE_ARN,
    authorizerType="AWS_IAM",
    clientToken=str(uuid.uuid4()),
)
gateway_id = gateway_response["gatewayId"]
gateway_arn = gateway_response["gatewayArn"]
print(f"Created AgentCore Gateway: {gateway_id}")

# 2. Add control runtime as an AgentCore Gateway target
cp_client.create_gateway_target(
    gatewayIdentifier=gateway_id,
    name="customer-support-control",
    targetConfiguration={
        "http": {
            "agentcoreRuntime": {
                "arn": CONTROL_RUNTIME_ARN,
                "qualifier": "DEFAULT"
            }
        }
    },
    clientToken=str(uuid.uuid4()),
)
print("Added target: customer-support-control")

# 3. Add treatment runtime as an AgentCore Gateway target
cp_client.create_gateway_target(
    gatewayIdentifier=gateway_id,
    name="customer-support-treatment",
    targetConfiguration={
        "http": {
            "agentcoreRuntime": {
                "arn": TREATMENT_RUNTIME_ARN,
                "qualifier": "DEFAULT"
            }
        }
    },
    clientToken=str(uuid.uuid4()),
)
print("Added target: customer-support-treatment")

# 4. Create the A/B test
response = dp_client.create_ab_test(
    name="customerSupportTargetTest",
    gatewayArn=gateway_arn,
    roleArn=AB_TEST_ROLE_ARN,
    evaluationConfig={
        "perVariantOnlineEvaluationConfig": [
            {"name": "C", "onlineEvaluationConfigArn": CONTROL_EVAL_ARN},
            {"name": "T1", "onlineEvaluationConfigArn": TREATMENT_EVAL_ARN}
        ]
    },
    gatewayFilter={
        "targetPaths": ["/customer-support-control/*"]
    },
    variants=[
        {
            "name": "C",
            "weight": 80,
            "variantConfiguration": {
                "target": {"name": "customer-support-control"}
            }
        },
        {
            "name": "T1",
            "weight": 20,
            "variantConfiguration": {
                "target": {"name": "customer-support-treatment"}
            }
        }
    ],
    enableOnCreate=True,
    clientToken=str(uuid.uuid4()),
)

ab_test_id = response["abTestId"]
print(f"Created A/B test: {ab_test_id}")
print(f"Status: {response['status']}")
print(f"Execution status: {response['executionStatus']}")
```

## 步驟 7：透過 AgentCore Gateway 傳送流量
<a name="target-based-send-traffic"></a>

A/B 測試執行後，請透過 AgentCore Gateway HTTP 端點傳送流量。AgentCore Gateway 會根據執行階段工作階段 ID，將每個請求指派給變體 （控制或處理）。

### 變體指派的運作方式
<a name="_how_variant_assignment_works"></a>

AgentCore Gateway 使用 `X-Amzn-Bedrock-AgentCore-Runtime-Session-Id`標頭來決定要將流量路由到哪個目標。此標頭是**選用**的，如果您未提供，執行時間會自動產生工作階段 ID。然後AgentCore Gateway 會使用工作階段 ID （無論您提供或產生的執行時間），根據設定的流量權重將請求指派給變體。

工作階段指派很**黏**：將工作階段 ID 指派給變體後，所有具有相同工作階段 ID 路由到相同目標的後續請求。這可確保工作階段內的一致體驗，同時仍然會根據流量分割在變體之間分配新的工作階段。

### 產生用於測試的流量
<a name="_generate_traffic_for_testing"></a>

將下列指令碼儲存為 `loadgen.sh`，將 `<gateway-id>`和 取代`<target-name>`為部署輸出中的值。您也可以從 複製完整的調用 URL`agentcore view ab-test <ab-test-id>`：

```
#!/bin/bash
export AWS_ACCESS_KEY_ID=$(aws configure get aws_access_key_id)
export AWS_SECRET_ACCESS_KEY=$(aws configure get aws_secret_access_key)
export AWS_SESSION_TOKEN=$(aws configure get aws_session_token)

GATEWAY_URL="https://<gateway-id>.gateway.bedrock-agentcore.us-west-2.amazonaws.com/<target-name>/invocations"

PROMPTS=(
  "What is the status of order ORD-1003?"
  "I want to return order ORD-1001, it doesn't fit."
  "My order ORD-1003 is late. Can I get a discount?"
  "Where is my order ORD-1002?"
  "I need help with a return for order ORD-1001. The color is wrong."
  "Can you check on order ORD-1003? I've been waiting forever."
  "I'd like to cancel order ORD-1002 if it hasn't shipped yet."
  "Order ORD-1003 is delayed again. This is unacceptable."
  "What's your return policy for order ORD-1001?"
  "My headphones order ORD-1003 still hasn't arrived. What can you do?"
)

for i in $(seq 1 30); do
  PROMPT="${PROMPTS[$(( (i - 1) % ${#PROMPTS[@]} ))]}"
  echo "=== Request $i: $PROMPT ==="
  curl -s --aws-sigv4 "aws:amz:us-west-2:bedrock-agentcore" \
    --user "$AWS_ACCESS_KEY_ID:$AWS_SECRET_ACCESS_KEY" \
    -H "x-amz-security-token: $AWS_SESSION_TOKEN" \
    -H "Content-Type: application/json" \
    -H "X-Amzn-Bedrock-AgentCore-Runtime-Session-Id: $(uuidgen)" \
    -d "{\"prompt\": \"$PROMPT\"}" \
    -X POST \
    "$GATEWAY_URL"
  echo ""
  sleep 2
done
```

執行 指令碼：

```
bash loadgen.sh
```

## 步驟 8：取得結果
<a name="target-based-get-results"></a>

輪詢 A/B 測試，以在樣本大小增加時監控結果。輪詢不會影響統計有效性。

**Example**  
取得目前結果 (`<ab-test-id>`以步驟 6 的任務 ID 取代）：  

```
agentcore view ab-test <ab-test-id>
```
取得 JSON 格式的結果：  

```
agentcore view ab-test <ab-test-id> --json
```
輪詢直到結果達到統計顯著性：  

```
import boto3
import time

client = boto3.client("bedrock-agentcore", region_name="us-west-2")

ab_test_id = "customerSupportTargetTest-Ab1Cd2Ef3G"

while True:
    response = client.get_ab_test(abTestId=ab_test_id)

    status = response["status"]
    exec_status = response["executionStatus"]
    print(f"Status: {status}, Execution: {exec_status}")

    results = response.get("results")
    if results:
        print(f"Analysis timestamp: {results.get('analysisTimestamp')}")
        for metric in results["evaluatorMetrics"]:
            evaluator = metric["evaluatorArn"]
            control = metric["controlStats"]
            print(f"\nEvaluator: {evaluator}")
            print(f"  Control: mean={control['mean']:.3f}, n={control['sampleSize']}")

            for variant in metric["variantResults"]:
                print(f"  {variant['variantName']}: mean={variant['mean']:.3f}, "
                      f"n={variant['sampleSize']}, "
                      f"pValue={variant.get('pValue', 'N/A')}, "
                      f"significant={variant['isSignificant']}")

                if variant["isSignificant"]:
                    print(f"  >>> Statistically significant! "
                          f"Change: {variant.get('percentChange', 0):.1f}%")

        # Check if any evaluator has reached significance
        all_significant = all(
            variant["isSignificant"]
            for metric in results["evaluatorMetrics"]
            for variant in metric["variantResults"]
        )
        if all_significant:
            print("\nAll evaluators have reached statistical significance.")
            break

    time.sleep(300)  # Poll every 5 minutes
```

**注意**  
結果出現所需的時間主要取決於線上評估組態中設定的工作階段逾時。一旦沒有新的請求在逾時時段內到達，工作階段就會視為完成。工作階段結束後，結果通常會在 15 分鐘內顯示。隨著更多工作階段完成，結果會累積 — 統計顯著性會隨著樣本大小而改善。
+  **p 值 < 0.05 且陽性 `percentChange`：**處理方式明顯優於控制。考慮部署處理方式。
+  **p 值 < 0.05 且負 `percentChange`：**處理方式明顯較差。保留控制項。
+  **p 值 >= 0.05：**沒有足夠的證據來得出差異。繼續收集樣本或增加對處理的流量。
+  **檢查所有評估者：**處理可能會改善一個指標，同時迴歸另一個指標。在決定之前，請檢閱所有評估者結果。

## 步驟 9：確認結果並停止 A/B 測試
<a name="target-based-confirm-stop"></a>

一旦 A/B 測試達到統計顯著性，請檢閱結果並停止實驗。

1.  **確認重要性。**確認目標評估器對`percentChange`處理變體具有正`isSignificant: true`值 （或者如果處理已迴歸，則確認控制項是獲勝者）。

1.  **停止 A/B 測試。**執行 `agentcore stop ab-test -i <ab-test-id>`。流量路由會立即結束，所有請求都會還原為預設目標。請參閱[檢視、暫停、繼續和停止](ab-testing-manage.md#manage-ab-test-start-stop)。

## 步驟 10：部署優勝者
<a name="target-based-deploy-winner"></a>

停止 A/B 測試後，將所有流量路由到獲勝的變體。

```
agentcore promote ab-test -i <ab-test-id>
agentcore deploy
```

 `promote` 會停止 A/B 測試 （如果仍在執行）、更新控制端點以指向處理版本 （例如，`control`從第 1 版更新到第 2 版），並移除處理端點。執行 `agentcore deploy`以套用變更。

或者，您也可以執行下列其中一項操作來手動部署優勝者：
+  **選項 A：**使用 [AgentCore Gateway 路由規則](gateway-rules.md)，將兩個目標的流量導向至獲勝的目標。
+  **選項 B：**從 AgentCore Gateway 移除遺失的目標，並將所有流量路由到獲勝者。
+  **選項 C：**更新遺失目標以指向獲勝的端點。

部署優勝者之後：
+  **刪除 A/B 測試**以清除資源。請參閱[刪除 A/B 測試](ab-testing-manage.md#manage-ab-test-remove)。
+  **監控新的基準。**線上評估會繼續對獲勝組態的工作階段進行評分。注意迴歸。
+  **開始下一個反覆運算。**獲勝組態的新追蹤為下一個建議週期提供了基礎。請參閱[其運作方式](optimization-how-it-works.md)。

## 了解結果
<a name="target-based-results-shape"></a>

當您呼叫 時`GetABTest`，當彙總管道處理足夠的工作階段時，回應會包含`results`物件。結果包含依變體細分的每個評估器指標。

### 結果結構
<a name="_results_structure"></a>

```
{
  "results": {
    "analysisTimestamp": "2026-04-30T18:45:00Z",
    "evaluatorMetrics": [
      {
        "evaluatorArn": "arn:aws:bedrock-agentcore:us-west-2:123456789012:evaluator/Builtin.Helpfulness",
        "controlStats": {
          "variantName": "C",
          "sampleSize": 24,
          "mean": 0.72
        },
        "variantResults": [
          {
            "variantName": "T1",
            "sampleSize": 6,
            "mean": 0.85,
            "absoluteChange": 0.13,
            "percentChange": 18.1,
            "pValue": 0.032,
            "confidenceInterval": {
              "lower": 0.02,
              "upper": 0.24
            },
            "isSignificant": true
          }
        ]
      }
    ]
  }
}
```

### 欄位參考
<a name="_field_reference"></a>


| 欄位 | 說明 | 
| --- | --- | 
|  `analysisTimestamp`  | 服務上次計算統計資料的時間。 | 
|  `evaluatorMetrics`  | 線上評估組態中的每個評估器一個項目。 | 
|  `controlStats.mean`  | 所有控制工作階段的平均評估者分數。 | 
|  `controlStats.sampleSize`  | 控制變體的得分工作階段數量。 | 
|  `variantResults[].mean`  | 所有治療工作階段的平均評估者分數。 | 
|  `variantResults[].sampleSize`  | 治療變體的得分工作階段數目。 | 
|  `variantResults[].absoluteChange`  | 處理平均值和控制平均值之間的差異。 | 
|  `variantResults[].percentChange`  | 相對於控制項的百分比改善 （正） 或迴歸 （負）。 | 
|  `variantResults[].pValue`  | 觀察差異的概率是偶然造成的。低於 0.05 表示統計顯著性。 | 
|  `variantResults[].confidenceInterval`  | 絕對變更的 95% 信賴區間 (`lower` `upper` 和 邊界）。 | 
|  `variantResults[].isSignificant`  |  `true` 當 p 值 < 0.05 且樣本大小足夠時。 | 

## 疑難排解
<a name="target-based-troubleshooting"></a>

### A/B 測試在傳送流量後未顯示任何結果
<a name="target-based-no-results"></a>

結果不會立即顯示。所需的時間取決於線上評估組態中設定的工作階段逾時 — 只有在沒有新請求在逾時時段內到達之後，才會將工作階段視為完成。工作階段結束後，預期會在大約 15 分鐘內得到結果。

如果在此視窗之後仍未顯示結果：
+  **驗證線上評估日誌群組。**線上評估組態必須指向執行時間代理程式的輸出日誌群組。如果線上評估組態參考不同的日誌群組 （或未接收來自執行時間的日誌群組），則工作階段將不會計分，且 A/B 測試永遠不會產生結果。
+  **檢查日誌群組名稱。**對於目標型路由，每個端點都有自己的日誌群組 （日誌群組名稱以端點名稱結尾）。確保每個線上評估組態參考正確的端點日誌群組。
+  **確認執行時間正在發出跨度。**檢查 CloudWatch Logs 是否有預期的日誌群組。您要在每個跨度上尋找的關鍵屬性：
  +  `aws.agentcore.gateway.routing_experiment_arn` 
  +  `aws.agentcore.gateway.routing_experiment_variant_name` （值： `C`或 `T1`)
  +  `session.id` 
+  **驗證 CLI 建立與手動組態。**如果您使用 `agentcore add online-eval --runtime <name>`，CLI 會自動設定正確的日誌群組。如果您透過 API 手動建立線上評估組態，請確定 AgentCore Online Eval Config `dataSourceConfig.cloudWatchLogs.logGroupNames`符合您執行時間的範圍日誌群組。