ユーザーへのプロアクティブな通知の送信
この記事の対象: SDK v4
通常、ボットは、ユーザーからのメッセージの受信に応答して、ユーザーに直接メッセージを送信します。 場合によっては、ボットがプロアクティブ メッセージ (ユーザーを起源としない要因に応答するメッセージ) を送信する必要がある場合があります。
プロアクティブ メッセージは、さまざまなシナリオで役立ちます。 たとえば、ユーザーが製品の価格を監視するようにボットに依頼していた場合、ボットは、製品の価格が 20% 下がった場合にユーザーにアラートを発行できます。 ユーザーの質問に対する回答をまとめるのに時間が必要な場合、ボットは、時間がかかることをユーザーに通知して、会話を続けられるようにします。 質問の回答がまとまったら、ボットは、その情報をユーザーと共有します。
この記事では、一般的なボットのプロアクティブ メッセージに関する情報について説明します。 Microsoft Teams におけるプロアクティブ メッセージの詳細については、以下をご覧ください。
- C#、JavaScript、Java または Python の Teams の会話ボット サンプル
- プロアクティブ メッセージを送信する方法に関する Microsoft Teams のドキュメント。
Note
Bot Framework JavaScript SDK、C#、Python SDK は引き続きサポートされますが、Java SDK については、最終的な長期サポートは 2023 年 11 月に終了する予定です。
Java SDK を使用して構築された既存のボットは引き続き機能します。
新しいボットを構築する場合は、Microsoft Copilot Studio の使用をご検討ください。また、適切なコパイロット ソリューションの選択に関する記事もお読みください。
詳細については、「The future of bot building」をご覧ください。
前提条件
- ボットの基本を理解する。
- プロアクティブ メッセージ サンプルのコピー (C#、JavaScript、Java、または Python)。 このサンプルは、この記事でプロアクティブ メッセージングを説明するために使用します。
プロアクティブ サンプルについて
一般に、アプリケーションとしてのボットにはいくつかのレイヤーがあります。
- HTTP 要求を受け入れることができ、特にメッセージング エンドポイントをサポートする Web アプリケーション。
- チャネルとの接続を処理するアダプター。
- ターンのハンドラー。通常、ボット アプリの会話の理由を処理する bot クラス内にカプセル化されます。
ユーザーからの受信メッセージに応答して、アプリはアダプターの プロセス アクティビティ メソッドを呼び出します。これにより、ターンおよびターン コンテキストが作成され、ミドルウェア パイプラインが呼び出され、ボットのターン ハンドラーが呼び出されます。
プロアクティブ メッセージを開始するには、ボット アプリケーションが他の入力を受信できる必要があります。 プロアクティブ メッセージを開始するためのアプリケーション ロジックは、SDK の範囲外です。 このサンプルでは、標準のメッセージ エンドポイントに加えて、通知エンドポイントを使用してプロアクティブ ターンをトリガーします。
この通知エンドポイントの GET 要求に応答して、アプリはアダプターの会話継続メソッドを呼び出します。これはアクティビティ処理メソッドと似た方法で動作します。 会話継続メソッド:
- プロアクティブ ターンに使用するための、ユーザーとコールバック メソッドの適切な会話参照を受け取ります。
- プロアクティブ ターンのイベント アクティビティとターン コンテキストを作成します。
- アダプターのミドルウェア パイプラインを呼び出します。
- 指定されたコールバック メソッドを呼び出します。
- ターン コンテキストでは、会話参照を使用して、メッセージをユーザーに送信します。
次の図に示すように、このサンプルには、ボット、メッセージ エンドポイント、プロアクティブ メッセージをボットに送信するために使用される追加エンドポイントが含まれています。
会話の参照を取得して格納する
Bot Framework Emulator がボットに接続する際に、ボットは 2 つの会話更新アクティビティを受け取ります。 次に示すように、ボットの会話更新アクティビティ ハンドラーで、会話の参照が取得され、ディクショナリに格納されます。
Bots\ProactiveBot.cs
private void AddConversationReference(Activity activity)
{
var conversationReference = activity.GetConversationReference();
_conversationReferences.AddOrUpdate(conversationReference.User.Id, conversationReference, (key, newValue) => conversationReference);
}
protected override Task OnConversationUpdateActivityAsync(ITurnContext<IConversationUpdateActivity> turnContext, CancellationToken cancellationToken)
{
AddConversationReference(turnContext.Activity as Activity);
return base.OnConversationUpdateActivityAsync(turnContext, cancellationToken);
}
会話の参照には、アクティビティが存在する会話を表す conversation プロパティが含まれます。 会話には、会話の参加ユーザーを一覧表示する user プロパティと、現在のアクティビティに対する返信が送信される可能性のある宛先を表す サービス URL プロパティが含まれます。 プロアクティブ メッセージをユーザーに送信するには、有効な会話の参照が必要です。 (Teams チャネルの場合、サービス URL はリージョン化されたサーバーにマップされます)。
Note
実際のシナリオでは、会話の参照はデータベースに保持されます。メモリ内のオブジェクトは使用されません。
プロアクティブなメッセージを送信する
2 番目のコント ローラーである "通知" コントローラーは、プロアクティブ メッセージをユーザーに送信する役割を果たします。 次の手順で、プロアクティブ メッセージを生成します。
- プロアクティブ メッセージを送信する先の会話の参照を取得します。
- アダプターの continue conversation メソッドを呼び出して、使用するターン ハンドラー デリゲートと会話の参照を指定します。 (continue conversation メソッドによって、参照されている会話に対してターン コンテキストが生成され、指定したターン ハンドラー デリゲートが呼び出されます。)
- デリゲートで、ターン コンテキストを使用して、プロアクティブ メッセージを送信します。 ここでは、委任は通知コントローラーで定義され、プロアクティブ メッセージをユーザーに送信します。
Note
各チャネルでは安定したサービス URL を使用する必要があります。URL は時間の経過によって変化する可能性があります。 サービス URL の詳細については、Bot Framework アクティビティ スキーマの 基本的なアクティビティ構造 と サービス URL のセクションを参照してください。
サービス URL が変更されると、以前の会話参照は無効になり、会話を続行するための呼び出しによってエラーまたは例外が生成されます。 この場合、ボットは、プロアクティブ メッセージを再度送信する前に、ユーザーの新しい会話参照を取得する必要があります。
Controllers\NotifyController .cs
ボットの通知ページが要求されるたびに、通知コントローラーは、ディクショナリから会話の参照を取得します。
次に、コントローラーは ContinueConversationAsync
メソッドと BotCallback
メソッドを使用して、プロアクティブ メッセージを送信します。
[Route("api/notify")]
[ApiController]
public class NotifyController : ControllerBase
{
private readonly IBotFrameworkHttpAdapter _adapter;
private readonly string _appId;
private readonly ConcurrentDictionary<string, ConversationReference> _conversationReferences;
public NotifyController(IBotFrameworkHttpAdapter adapter, IConfiguration configuration, ConcurrentDictionary<string, ConversationReference> conversationReferences)
{
_adapter = adapter;
_conversationReferences = conversationReferences;
_appId = configuration["MicrosoftAppId"] ?? string.Empty;
}
public async Task<IActionResult> Get()
{
foreach (var conversationReference in _conversationReferences.Values)
{
await ((BotAdapter)_adapter).ContinueConversationAsync(_appId, conversationReference, BotCallback, default(CancellationToken));
}
// Let the caller know proactive messages have been sent
return new ContentResult()
{
Content = "<html><body><h1>Proactive messages have been sent.</h1></body></html>",
ContentType = "text/html",
StatusCode = (int)HttpStatusCode.OK,
};
}
private async Task BotCallback(ITurnContext turnContext, CancellationToken cancellationToken)
{
await turnContext.SendActivityAsync("proactive hello");
}
}
プロアクティブ メッセージを送信するには、アダプターにボット用のアプリ ID が必要です。 ボットのアプリ ID は、運用環境で使用できます。 エミュレーターを使用してボットをローカルでテストする際に、空の文字列 ("") を使用できます。
ボットをテストする
- Bot Framework Emulator をインストールします (まだインストールしていない場合)。
- ご自身のマシンを使ってローカルでサンプルを実行します。
- エミュレーターを起動し、ボットに接続します。
- お使いのボットの API/通知ページに読み込みます。 これによりエミュレーターでプロアクティブ メッセージが生成されます。
追加情報
要件
プロアクティブ メッセージを送信する前に、ボットに会話参照が必要です。 ボットは、ユーザーから受信したアクティビティから会話参照を取得できますが、通常は、ボットがプロアクティブ メッセージを送信する前に、少なくとも 1 回はユーザーがボットと対話する必要があります。
多くのチャネルでは、ユーザーがボットに少なくとも 1 回メッセージを送信していない限り、ボットがユーザーにメッセージを送信することを禁止しています。 一部のチャネルでは例外が許可されます。 たとえば、Teams チャネルでは、ボットは、ボットが含まれる確立済みグループ会話内の個人に対して、プロアクティブ (またはマンツーマン) メッセージを送信できます。
設計上の考慮事項
お使いのボットでプロアクティブ メッセージを実装するときは、短い時間で複数のプロアクティブ メッセージを送信しないでください。 一部のチャネルでは、ボットがユーザーにメッセージを送信できる頻度に制限が設定され、これらの制限に違反した場合は、ボットが無効になります。
最も単純な種類のプロアクティブ メッセージの場合、ボットは、現在の状態や会話のトピックに関係なく、メッセージがトリガーされたときにメッセージを会話に挿入します。 このシナリオでは、プロアクティブ メッセージによって通常の会話フローが中断されます。
通知をより円滑に処理するには、会話フローに通知を統合するための他の方法を検討してください (会話の状態にフラグを設定する方法や、通知をキューに追加する方法など)。
プロアクティブ ターンについて
continue conversation メソッドでは 、会話 参照とターン コールバック ハンドラーを使用して次の操作を行います。
- ボット アプリケーションがプロアクティブ メッセージを送信できるターンを作成します。 アダプターは、このターンの
event
アクティビティを作成し、その名前を "ContinueConversation" に設定します。 - アダプターのミドルウェア パイプラインを経由してターンを送信します。
- ターン コールバック ハンドラーを呼び出してカスタム ロジックを実行します。
プロアクティブ メッセージ サンプルでは、ターン コールバック ハンドラーは通知コントローラーで定義され、ボットの通常のターン ハンドラーを介してプロアクティブ アクティビティを送信することなく、メッセージを会話に直接送信します。 また、このサンプル コードでは、プロアクティブ ターンでボットの状態にアクセスしたり、更新したりすることはありません。
多くのボットはステートフルであり、状態を使用して複数のターンで会話を管理します。 continue conversation メソッドでターン コンテキストが作成されると、ターンには適切なユーザーと会話の状態が関連付けられます。プロアクティブ ターンをボットのロジックに統合できます。 ボットのロジックがプロアクティブ メッセージを認識する必要がある場合は、何通りか方法があります。 次のことを実行できます。
- ボットのターン ハンドラーをターン コールバック ハンドラーとして指定します。 その後、ボットは "ContinueConversation" イベント アクティビティを受け取ります。
- ターン コールバック ハンドラーを使用して、最初にターン コンテキストに情報を追加してから、ボットのターン ハンドラーを呼び出します。
どちらの場合も、プロアクティブ イベントを処理するようにボット ロジックを設計する必要があります。