

# ユーザーシミュレーション
<a name="user-simulation"></a>

ユーザーシミュレーションでは、LLM-backed アクターを使用して、エージェントとやり取りするエンドユーザーの役割を果たします。アクターのプロファイルと目標を定義し、目標が達成されるか、ターン制限に達するまで、アクターはエージェントとのマルチターン会話を駆動します。

**注記**  
ユーザーシミュレーションは、SDK 側で Amazon Bedrock モデルを呼び出して、アクターのレスポンスを生成します。これらの呼び出しには、標準の Amazon Bedrock モデル呼び出し料金が適用されます。詳細については、[AgentCore の料金」ページ](https://aws.amazon.com/bedrock/agentcore/pricing/)を参照してください。

これは、次の場合に便利です。
+  **現実的なバリエーションでテストする:** アクターは、実行ごとに異なるフレーズ、フォローアップの質問、会話パスを生成し、手動で作成されたシナリオが見逃すエッジケースを公開します。
+  **オープンエンド会話を評価する:** 自由形式の対話 (カスタマーサポート、チュートリアル、アドバイザリ) を処理するエージェントの場合、シミュレートされたシナリオは固定ターンシーケンスよりも実際のユーザー動作をより適切に反映します。
+  **シナリオカバレッジをスケールする:** 数十のマルチターンスクリプトを手動で記述する代わりに、さまざまなペルソナと目標を持つアクタープロファイルを定義し、アクターに会話を生成させます。
+  **多様性による回帰テスト:** 同じアクタープロファイルを複数回実行して、エージェントが同じインテントのさまざまな式を処理していることを確認します。

ユーザーシミュレーションは、[オンデマンド](dataset-evaluations-on-demand.md)データセットランナーと[バッチ](dataset-evaluations-batch.md)データセットランナーの両方で動作します。

## 仕組み
<a name="user-simulation-how-it-works"></a>

ランナーは、シミュレートされた各シナリオを会話ループで処理します。

1.  **開始:** ランナーは、シナリオの `input`フィールドを最初のターンとしてエージェントに送信します。

1.  **エージェント応答:** エージェントは入力を処理し、応答を返します。

1.  **アクターの評価:** LLM-backed アクターはエージェントの応答を受け取り、プロファイルと目標に基づいて次に何をするかを決定します。アクターは、以下を含む構造化されたレスポンスを生成します。
   +  **理由: アクターの内部の応答理由 (たとえば、**「エージェントはフライトオプションを提供しましたが、希望の時間を求めませんでした。 午前便を優先するように指定する必要があります」）。これは、アクターが特定の動作をした理由をデバッグするのに役立ちます。
   +  **メッセージ:** エージェントに送信する次のメッセージ。
   +  **停止シグナル:** アクターが目標を達成したと見なすかどうかを示すブール値。

1.  **続行または停止:** アクターが目標の完了 (`stop: true`) を通知するか、ターンカウントが に達すると`max_turns`、会話は終了します。それ以外の場合、アクターの次のメッセージは次のターンの入力になります。

1.  **評価:** 会話が完了すると、ランナーは事前定義されたシナリオと同様に、設定された評価者を使用してセッションを評価します。

## アクタープロファイル
<a name="user-simulation-actor-profile"></a>

シミュレートされた各シナリオには、アクター`ActorProfile`が誰で、何を達成するかを定義する が必要です。


| フィールド | 必要 | 説明 | 
| --- | --- | --- | 
|  `context`  | はい | アクターに関する背景情報。アクターが知っておくべき状況と関連する詳細について説明します。 | 
|  `goal`  | はい | アクターが会話で達成したいこと。アクターは、目標が達成されたと判断すると、完了を通知します。 | 
|  `traits`  | いいえ | アクターの特性 (専門知識レベル、コミュニケーションスタイル、忍耐など) を説明するキーと値のペア。デフォルトは空です。 | 

```
{
  "actor_profile": {
    "context": "A customer who purchased a laptop last week and it arrived with a cracked screen",
    "goal": "Get a replacement laptop shipped within 2 business days",
    "traits": {
      "expertise": "non-technical",
      "tone": "frustrated but polite",
      "patience": "low"
    }
  }
}
```

## シミュレーション設定
<a name="user-simulation-config"></a>

はアクターの動作`SimulationConfig`を制御し、ランナーの評価設定で設定されます。


| フィールド | デフォルト  | 説明 | 
| --- | --- | --- | 
|  `model_id`  | デフォルトモデル | アクター LLM に使用される Amazon Bedrock モデル ID。複雑なペルソナの指示に従うことができるモデルを選択します。省略すると、デフォルトのモデルが使用されます。 | 

```
from bedrock_agentcore.evaluation import SimulationConfig

simulation_config = SimulationConfig(
    model_id="<model-id>",
)
```

## データセットスキーマ
<a name="user-simulation-dataset-schema"></a>

シミュレートされたシナリオでは、 `input`の代わりに `actor_profile`と を使用します`turns`。

```
{
  "scenarios": [
    {
      "scenario_id": "geography-student",
      "scenario_description": "A curious student asks geography questions",
      "actor_profile": {
        "traits": {"expertise": "novice", "tone": "curious"},
        "context": "A student studying world geography who wants to learn about capitals",
        "goal": "Find out the capital cities of at least two different countries"
      },
      "input": "Hi! I'm studying geography. Can you help me learn about world capitals?",
      "max_turns": 5,
      "assertions": [
        "Agent provides accurate capital city information",
        "Agent is helpful and encouraging to the student"
      ]
    }
  ]
}
```


| フィールド | [Required] (必須) | デフォルト | [Description] (説明) | 
| --- | --- | --- | --- | 
|  `scenario_id`  | はい | — | シナリオの一意の識別子。 | 
|  `scenario_description`  | いいえ |  `""`  | シナリオを説明するオプションのメタデータ。結果のシナリオを整理および識別するのに役立ちます。 | 
|  `actor_profile`  | はい | — | アクターのアイデンティティと目的。「[アクタープロファイル](#user-simulation-actor-profile)」を参照してください。 | 
|  `input`  | はい | — | 会話を開始するためにエージェントに送信された最初のメッセージ。 | 
|  `max_turns`  | いいえ | 10 | 会話が停止するまでの最大ターン数。1 以上 | 
|  `assertions`  | いいえ | — | 予想される動作に関する自然言語アサーション。などのセッションレベルの評価者が使用します`Builtin.GoalSuccessRate`。 | 

**注記**  
会話フローが事前にわからない`expected_response`ため、シミュレートされたシナリオでは、ターンごとの `expected_trajectory`または はサポートされていません。シミュレートされたシナリオでグラウンドトゥルース`assertions`に使用します。

 `FileDatasetProvider` は JSON 構造からシナリオタイプを自動検出します。 `actor_profile`フィールド (および `turns`フィールドなし) のシナリオは としてロードされます`SimulatedScenario`。

## バッチデータセットランナーで を使用する
<a name="user-simulation-batch-example"></a>

次の例では、[バッチデータセットランナー](dataset-evaluations-batch.md)を使用してシミュレートされたシナリオ評価を実行します。`simulation_config` を に設定`BatchEvaluationRunConfig`し、データセットに`SimulatedScenario`インスタンスを含めます。

```
import boto3
import json
from bedrock_agentcore.evaluation import (
    BatchEvaluationRunner,
    BatchEvaluationRunConfig,
    BatchEvaluatorConfig,
    CloudWatchDataSourceConfig,
    SimulationConfig,
    AgentInvokerInput,
    AgentInvokerOutput,
    Dataset,
    SimulatedScenario,
    ActorProfile,
)

AGENT_ARN = "arn:aws:bedrock-agentcore:us-west-2:123456789012:runtime/MyAgent-abc123"  # Replace with your agent runtime ARN
REGION = "us-west-2"  # Replace with your region
RUNTIME_ID = AGENT_ARN.split("/")[-1]
AGENT_NAME = RUNTIME_ID.rsplit("-", 1)[0]
ENDPOINT_NAME = "DEFAULT"
LOG_GROUP = f"/aws/bedrock-agentcore/runtimes/{RUNTIME_ID}-{ENDPOINT_NAME}"
SERVICE_NAME = f"{AGENT_NAME}.{ENDPOINT_NAME}"
ACTOR_MODEL_ID = "global.anthropic.claude-haiku-4-5-20251001-v1:0"  # Replace with your preferred model

# Define the dataset with simulated scenarios
dataset = Dataset(
    scenarios=[
        SimulatedScenario(
            scenario_id="support-frustrated-customer",
            scenario_description="A frustrated customer with a defective product",
            actor_profile=ActorProfile(
                traits={"expertise": "non-technical", "tone": "frustrated but polite"},
                context="Purchased a laptop last week that arrived with a cracked screen",
                goal="Get a replacement laptop shipped within 2 business days",
            ),
            input="I received my laptop and the screen is cracked. I need help.",
            max_turns=8,
            assertions=[
                "Agent acknowledges the issue and apologizes",
                "Agent offers a replacement or refund",
                "Agent provides a timeline for resolution",
            ],
        ),
        SimulatedScenario(
            scenario_id="support-billing-question",
            scenario_description="A customer with a billing discrepancy",
            actor_profile=ActorProfile(
                traits={"expertise": "moderate", "tone": "calm"},
                context="Noticed a double charge on the last credit card statement",
                goal="Get the duplicate charge reversed and confirmation of the refund",
            ),
            input="I see two charges for the same order on my statement. Can you look into this?",
            max_turns=6,
            assertions=[
                "Agent investigates the billing issue",
                "Agent confirms whether a duplicate charge exists",
            ],
        ),
    ]
)

# Configure the evaluation
config = BatchEvaluationRunConfig(
    batch_evaluation_name="simulated-support-eval",
    evaluator_config=BatchEvaluatorConfig(
        evaluator_ids=[
            "Builtin.GoalSuccessRate",
            "Builtin.Helpfulness",
        ],
    ),
    data_source=CloudWatchDataSourceConfig(
        service_names=[SERVICE_NAME],
        log_group_names=[LOG_GROUP],
        ingestion_delay_seconds=180,
    ),
    simulation_config=SimulationConfig(
        model_id=ACTOR_MODEL_ID,
    ),
    polling_timeout_seconds=1800,
    polling_interval_seconds=30,
)

# Define the agent invoker
agentcore_client = boto3.client("bedrock-agentcore", region_name=REGION)

def agent_invoker(inp: AgentInvokerInput) -> AgentInvokerOutput:
    payload = inp.payload
    if isinstance(payload, str):
        raw_bytes = json.dumps({"prompt": payload}).encode()
    elif isinstance(payload, dict):
        raw_bytes = json.dumps(payload).encode()
    else:
        raw_bytes = json.dumps({"prompt": str(payload)}).encode()

    print(f"[{inp.session_id}] > sending payload: {raw_bytes.decode()}")
    response = agentcore_client.invoke_agent_runtime(
        agentRuntimeArn=AGENT_ARN,
        runtimeSessionId=inp.session_id,
        payload=raw_bytes,
    )
    response_body = response["response"].read()
    print(f"[{inp.session_id}] < received response: {response_body.decode()}")
    return AgentInvokerOutput(agent_output=json.loads(response_body))

# Run the evaluation
runner = BatchEvaluationRunner(region=REGION)
result = runner.run_dataset_evaluation(
    config=config,
    dataset=dataset,
    agent_invoker=agent_invoker,
)

# Display results
print(f"Status: {result.status}")
if result.evaluation_results:
    er = result.evaluation_results
    print(f"Sessions completed: {er.number_of_sessions_completed}")
    print(f"Sessions failed:    {er.number_of_sessions_failed}")
    for summary in er.evaluator_summaries or []:
        avg = summary.statistics.average_score if summary.statistics else None
        print(f"  {summary.evaluator_id}: avg={avg}")
```

## オンデマンドデータセットランナーでの の使用
<a name="user-simulation-on-demand-example"></a>

[オンデマンドデータセットランナー](dataset-evaluations-on-demand.md)は同じパターンに従います。`simulation_config` を に設定`EvaluationRunConfig`し、データセットに`SimulatedScenario`インスタンスを含めます。

**注記**  
オンデマンド評価は、消費に基づいて課金されます。詳細については、[AgentCore の料金」ページ](https://aws.amazon.com/bedrock/agentcore/pricing/)を参照してください。

```
from bedrock_agentcore.evaluation import (
    OnDemandEvaluationDatasetRunner,
    EvaluationRunConfig,
    EvaluatorConfig,
    CloudWatchAgentSpanCollector,
    SimulationConfig,
    FileDatasetProvider,
)

AGENT_ARN = "arn:aws:bedrock-agentcore:us-west-2:123456789012:runtime/MyAgent-abc123"  # Replace with your agent runtime ARN
REGION = "us-west-2"  # Replace with your region
RUNTIME_ID = AGENT_ARN.split("/")[-1]
ENDPOINT_NAME = "DEFAULT"
LOG_GROUP = f"/aws/bedrock-agentcore/runtimes/{RUNTIME_ID}-{ENDPOINT_NAME}"
ACTOR_MODEL_ID = "global.anthropic.claude-haiku-4-5-20251001-v1:0"  # Replace with your preferred model

# Load dataset (auto-detects simulated scenarios from actor_profile field)
dataset = FileDatasetProvider("simulated_dataset.json").get_dataset()

# Create span collector
span_collector = CloudWatchAgentSpanCollector(
    log_group_name=LOG_GROUP,
    region=REGION,
)

# Configure with simulation support
config = EvaluationRunConfig(
    evaluator_config=EvaluatorConfig(
        evaluator_ids=["Builtin.GoalSuccessRate", "Builtin.Helpfulness"],
    ),
    evaluation_delay_seconds=180,
    max_concurrent_scenarios=5,
    simulation_config=SimulationConfig(
        model_id=ACTOR_MODEL_ID,
    ),
)

# Run
runner = OnDemandEvaluationDatasetRunner(region=REGION)
result = runner.run(
    agent_invoker=agent_invoker,
    dataset=dataset,
    span_collector=span_collector,
    config=config,
)

for scenario in result.scenario_results:
    print(f"Scenario: {scenario.scenario_id} ({scenario.status})")
    for evaluator in scenario.evaluator_results:
        for r in evaluator.results:
            print(f"  {evaluator.evaluator_id}: {r.get('value')} ({r.get('label')})")
```

## 停止条件
<a name="user-simulation-stop-conditions"></a>

シミュレートされた会話は、次のいずれかの条件が満たされると終了します。

1.  **目標完了:** アクターはその目標が達成されたと判断し、 にシグナルを送信します`stop: true`。これは期待される結果です。

1.  **最大ターン到達:** 会話が`max_turns`制限に達しました。これは安全バックストップとして機能します。シナリオが頻繁にターン制限に達する場合は、アクターの目標を増やす`max_turns`か簡素化することを検討してください。

1.  **メッセージが生成されない:** アクターは次のメッセージを生成しませんが、明示的に停止のシグナルを送信しません。これは暗黙的な目標の完了として扱われます。

## 効果的なシミュレートシナリオのヒント
<a name="user-simulation-tips"></a>
+  **目標に具体的に示す: **「会話をする」などの曖昧な目標は、焦点のないやり取りにつながります。「注文 \#12345 の返金を受ける」などの特定の目標は、アクターに明確なエンドポイントを与えます。
+  **特性を使用して難易度を制御する:** を持つアクター`"expertise": "expert"`は、 を持つアクターよりも難しい質問をします`"expertise": "novice"`。特性を使用して、さまざまなユーザーセグメントにわたってエージェントをテストします。
+  **現実的なターン制限を設定する:** ほとんどのカスタマーサポートの会話は 5～10 ターンで解決されます。設定が高`max_turns`すぎるとコンピューティングが無駄になります。設定が低すぎると、目標に達する前に会話が中断される可能性があります。
+  グ**ラウンドトゥルースにアサーションを使用する:** 会話フローは動的であるため、ターンごとに`expected_response`は使用できません。特定のパスに関係なく、期待される結果を記述するアサーションを記述します。
+  **適切なアクターモデルを選択する:** アクターモデルは、ターン間で一貫したペルソナを維持するのに十分な能力を持っている必要があります。小規模なモデルは単純なペルソナで機能します。微妙な目標を持つ複雑なペルソナは、より有能なモデルから恩恵を受けます。