Direct Lineで会話エージェントをテストする

Direct Line API は、クライアント アプリケーションが Copilot Studio で構築された会話型エージェントとやり取りするための通信インターフェースとして機能します。 Direct Line API を使用すると、WebSocket ストリームまたは HTTP 要求を通じて、クライアント アプリケーションとエージェント間のメッセージの送受信が可能になります。 パフォーマンス テストにおいて、Direct Line は、負荷テストツールが実際のユーザーの行動を再現し、負荷を発生させ、応答時間を測定できるようにします。

WebSockets を使用して Direct Line と通信する

Copilot Studio で作成した会話型エージェントは、埋め込み iframe として、またはカスタム キャンバスを使用して、Web アプリケーションに展開できます。 どちらの展開オプションも、Direct Line との通信には WebSocket を使用します。 これらの方法のいずれかを使用して会話型エージェントをアプリに展開する場合、パフォーマンス テスト スクリプトでは、WebSocket 通信を使用して実際のユーザーの行動に近い負荷を生成し、高い信頼性をもってパフォーマンスを測定する必要があります。

Direct Line および WebSocket 通信を使用するクライアント アプリケーションは、以下のフローに従う必要があります:

  1. 会話を開始するには、クライアント アプリケーションはまず会話トークンを取得する必要があります。 エージェントに Direct Line のシークレットが構成されている場合は、Direct Line のリージョン別エンドポイントにリクエストを送信してトークンを取得してください。 シークレットを使わないエージェントのトークンはトークン エンドポイントから取得可能です。
  2. クライアント アプリケーションは、トークンを使用して会話を開始し、会話 ID と WebSocket ストリームの URL を受け取ります。
  3. ユーザーメッセージは、会話 ID を指定した HTTP POST 要求を送信することで送信されます。
  4. 会話型エージェントからのメッセージは、WebSocket ストリームを介して受信されます。

WebSockets を使用した Direct Line 通信のフローを示すスクリーンショット。

HTTP GET を使用して Direct Line と通信する

負荷テストツールで WebSocket 通信が使用できない場合、またはクライアント向けアプリケーションが WebSocket 通信を使用していない場合は、代わりに HTTP GET リクエストを送信することで活動を受信することができます。 次の図に示すように、会話開始の流れに変更はありません。

HTTP GET を使用した Direct Line 通信のフローを示すスクリーンショット。

応答時間の測定

負荷がユーザー エクスペリエンスにどのような影響を与えるかを評価するには、パフォーマンス テストス クリプトで以下の各ステップの応答時間を追跡し、報告するようにしてください:

Step ユーザー エクスペリエンスへの影響
トークンを生成する 新しい会話の開始に要する時間
会話を開始する 新しい会話の開始に要する時間
活動の送信 新規ユーザーへのメッセージ送信にかかる時間 (エージェントからの応答時間は含まない)
活動の受信/活動の取得 エージェントが返信するまでの時間

「トークンの生成」、「会話の開始」、「活動の送信」の応答時間を追跡することは、負荷テストツールにとっては容易に行えます。これらのステップでは標準的な HTTP 要求が使用されるためです。 しかし、エージェントがユーザーのメッセージに返信するまでの時間を測定することは、以下の理由により、より複雑になります。

  • Direct Line での活動の送受信は、非同期方式で行われます。 活動の送信リクエストを使用してユーザーメッセージを送信した場合、その応答はエージェントからのメッセージではありません。 その代わりに、ユーザー メッセージが正常に転記されたことを確認するだけです。

  • その設計によっては、対話型エージェントは、ユーザーからのメッセージに対して、任意の数のメッセージを応答する場合があります。 したがって、ほとんどの場合、エージェントの応答にかかる時間は、ユーザーからのメッセージとエージェントからの最後のメッセージの間に経過した時間として測定する必要があります。 次の例では、1 つのユーザーメッセージが 3 つのエージェント メッセージをトリガーし、その間に API 呼び出しが実行されます。 各メッセージの返信には約 2 秒かかりますが、ユーザーから見ると、エージェントがユーザーの要求に応答するまで 6 秒かかっていることになります。

    メッセージ間の応答時間を示すスクリーンショット。

エージェントの最後の応答を特定する

エージェントが応答を完了するまでの時間を測定するには、パフォーマンス テストスクリプトで以下の処理を行う必要があります:

  • ユーザー メッセージの直後に続く最後のエージェント メッセージを特定する
  • 2 つの間の時間差を計算します

Copilot Studio が使用する基盤となるプロトコルには、「最後の応答」という概念がありません。これは、エージェントもユーザーも、いつでもメッセージを送信できるためです。 したがって、パフォーマンス テストのスクリプトでは、エージェントが所定の時間内にメッセージを送信しなかった場合、次のユーザー メッセージが送信されるまで、それ以上のメッセージは送信されないものと想定する必要があります。 このロジックの実装方法は、スクリプトが Direct Line とどのように通信するかによって異なります。

WebSocket を使用する

WebSockets 経由で Direct Line と通信する際は、WebSocket から読み取れるフレームがなくなった場合、エージェントはそれ以上メッセージを送信しないとみなしてください。 次のフレームを読み込もうとした際にタイムアウトが発生することで、この現象が確認できる場合がありますが、具体的な挙動は実装によって異なります。 WebSockets を使用するリファレンス実装については、HTTP GET の使用を検討してください。

HTTP GET の使用

WebSockets の代わりに HTTP GET を使用するパフォーマンス テストのスクリプトでは、ユーザーおよびエージェントのメッセージをすべて取得するために、活動エンドポイントをポーリングする必要があります。 ポーリングを行う際は、エージェントが回答するための十分な時間を確保するようにしてください。 たとえば、エージェントがユーザーのクエリに応答するためにバックエンド API を呼び出す必要があり、その API の応答に最大 5 秒かかる場合、スクリプトは 5 秒が経過するまで活動のエンドポイントに対してポーリングを行わないようにする必要があります。

以下の簡略化されたペイロードは、活動エンドポイントから返される応答を表しています:

[
  {
    "type": "message",
    "id": "98SryQaHr2rGthOGpChPK2-us|0000012",
    "timestamp": "2025-01-07T09:12:22.0329242Z",
    "from": {
      "id": "a688eb7d-092a-42a8-8ef5-73123b9c2aaa",
      "name": ""
    },
    "conversation": {
      "id": "98SryQaHr2rGthOGpChPK2-us"
    },
    "text": "I also want to set up a new account",
  },
  {
    "type": "message",
    "id": "98SryQaHr2rGthOGpChPK2-us|0000017",
    "timestamp": "2025-01-07T09:12:24.5478686Z",
    "from": {
      "id": "4b56bfa5-5574-5bb3-7aa3-99b8798b9d90",
      "name": "Load Testing",
      "role": "bot"
    },
    "conversation": {
      "id": "98SryQaHr2rGthOGpChPK2-us"
    },
    "text": "Sure, please bear with me as I set up your new account",
    "replyToId": "98SryQaHr2rGthOGpChPK2-us|0000012",
  },
  {
    "type": "message",
    "id": "98SryQaHr2rGthOGpChPK2-us|0000018",
    "timestamp": "2025-01-07T09:12:33.1960413Z",
    "from": {
      "id": "4b56bfa5-5574-5bb3-7aa3-99b8798b9d90",
      "name": "Load Testing",
      "role": "bot"
    },
    "conversation": {
      "id": "98SryQaHr2rGthOGpChPK2-us"
    },
    "text": "Almost done! Thank you for your patience",
    "replyToId": "98SryQaHr2rGthOGpChPK2-us|0000012",
  },
  {
    "type": "message",
    "id": "98SryQaHr2rGthOGpChPK2-us|0000019",
    "timestamp": "2025-01-07T09:12:41.9166159Z",
    "from": {
      "id": "4b56bfa5-5574-5bb3-7aa3-99b8798b9d90",
      "name": "Load Testing",
      "role": "bot"
    },
    "conversation": {
      "id": "98SryQaHr2rGthOGpChPK2-us"
    },
    "text": "All done! Your new account is now active.",
    "inputHint": "acceptingInput",
    "replyToId": "98SryQaHr2rGthOGpChPK2-us|0000012"
  }
]

ペイロードを解析し、応答時間を算出する際は、以下のガイドラインを参考にしてください。

  • エージェントからのメッセージには role: bot というプロパティがありますが、ユーザーからのメッセージには role というプロパティがありません。
  • ユーザー メッセージへの応答として送信されるエージェントメッセージには、replyToId というプロパティがあり、その値はユーザーメッセージの id プロパティの値となります。
  • エージェントの応答時間は、ユーザーからのメッセージと、そのメッセージに対して返信したエージェントの最後のメッセージとの時間差として算出できます。