Go to list of users who liked
Share on X(Twitter)
Share on Facebook
More than 5 years have passed since last update.
Bot Builder v4 でボット開発 : 複数ダイアログの管理
前回はウォーターフォールダイアログを使って、複数のプロンプトを扱う方法を紹介しました。今回は複数の ComponentDialog ダイアログを既存のボットに追加して、管理する方法を見ていきます。
追加するダイアログとシナリオ
今回の記事では、前回追加した ProfileDialog に加え、以下のダイアログを追加します。
- MenuDialog : ボットの機能を伝えるダイアログ
- WeatherDialog : 天気を知らせてくれるダイアログ
- ScheduleDialog : 予定を知らせてくれるダイアログ
また、ProfileDialog はユーザーが初回にボットに話しかけたタイミングで実行するようにします。
ダイアログスタック
ダイアログにはスタックという考え方があります。
- まず初めのダイアログがスタックに追加される (BeginDialogAsync)
- 子ダイアログを呼ぶと、現在のダイアログの上に追加される (BeginDialogAsync)
- 子ダイアログが終了するとスタックから削除され元のダイアログに戻る (EndDialogAsync)
- 繰り返し同じダイアログを呼ぶ場合は、スタックが増えないよう入れ替える (ReplaceDialogAsync)
ダイアログスタックはダイアログコンテキストで管理されるため、以下コードで現在実行中のダイアログに遷移することが出来ます。
vardialogContext=awaitdialogs.CreateContextAsync(turnContext,cancellationToken);varresults=awaitdialogContext.ContinueDialogAsync(cancellationToken);ConversationUpdate アクティビティ タイプ
ユーザーとボットの間で発生するアクティビティには以下の種類があります。
message
ユーザーからメッセージが送られて来た。
conversationUpdate
ボットが会話に追加されたこと、他のメンバーが会話に追加された、または会話から削除されたこと、会話のメタデータが変更されたことを示す。
contactRelationUpdate
ユーザーの連絡先リストに対してボットが追加または削除されたことを示す。
typing
会話の相手側のユーザーまたはボットが応答を準備していることを示す。
deleteUserData
ボットに保存されている可能性のあるユーザー データを削除するようにユーザーが要求したことをボットに示す。
endOfConversation
会話の終了を示す。
conversationUpdate アクティビティタイプでユーザーに対する初期応答を行います。またユーザーに対してのみ処理を行うように、Actvity の Recepient と、MembersAdded で追加されたユーザー情報で確認します。
2. ProfileDialog.cs を Dialogs フォルダに移動。
3. MyBot.cs の OnTurnAsync を以下の様に変更。ダイアログコンテキストの作成および保存場所を変更し、ConversationUpdate でプロファイルダイアログを実行。
publicasyncTaskOnTurnAsync(ITurnContextturnContext,CancellationTokencancellationToken=default(CancellationToken)){// DialogSet からコンテキストを作成vardialogContext=awaitdialogs.CreateContextAsync(turnContext,cancellationToken);// ユーザーからメッセージが来た場合if(turnContext.Activity.Type==ActivityTypes.Message){// まず ContinueDialogAsync を実行して既存のダイアログがあれば継続実行。varresults=awaitdialogContext.ContinueDialogAsync(cancellationToken);// DialogTurnStatus が Complete の場合、ダイアログは完了したため結果を処理if(results.Status==DialogTurnStatus.Complete){varuserProfile=awaitaccessors.UserProfile.GetAsync(turnContext,()=>newUserProfile(),cancellationToken);awaitturnContext.SendActivityAsync(MessageFactory.Text($"ようこそ '{userProfile.Name}' さん!"));}}// ユーザーとボットが会話に参加したelseif(turnContext.Activity.Type==ActivityTypes.ConversationUpdate){// turnContext より Activity を取得varactivity=turnContext.Activity.AsConversationUpdateActivity();// ユーザーの参加に対してだけ、プロファイルダイアログを開始if(activity.MembersAdded.Any(member=>member.Id!=activity.Recipient.Id)){awaitturnContext.SendActivityAsync("ようこそ MyBot へ!");awaitdialogContext.BeginDialogAsync(nameof(ProfileDialog),null,cancellationToken);}}// 最後に現在の UserProfile と DialogState を保存awaitaccessors.UserState.SaveChangesAsync(turnContext,false,cancellationToken);awaitaccessors.ConversationState.SaveChangesAsync(turnContext,false,cancellationToken);}MenuDialog, WeatherDialog, ScheduleDialog の追加
残りのダイアログの追加を行います。
1. Dialogs フォルダ配下に MenuDialog.cs を追加し、以下のコードを追加。ポイントは以下の通り。
- 機能一覧を Choice で表示
- メニューの中身と子ダイアログの紐づけとして Dictionary を定義
- ChoiceFactory を使ってメニュー定義より Choice のリストを作成
- BeginDialogAsync で子ダイアログを実行
- ReplaceDialogAync で menu を再実行
- 子ダイアログを DialogSet に追加
usingSystem.Threading;usingSystem.Threading.Tasks;usingSystem.Collections.Generic;usingMicrosoft.Bot.Builder;usingMicrosoft.Bot.Builder.Dialogs;usingMicrosoft.Bot.Builder.Dialogs.Choices;usingSystem.Linq;publicclassMenuDialog:ComponentDialog{// メニューの作成。表示される文字と子ダイアログの名称をセットで登録privatestaticDictionary<string,string>menus=newDictionary<string,string>(){{"天気を確認",nameof(WeatherDialog)},{"予定を確認",nameof(ScheduleDialog)}};// ChoiceFactory で選択肢に設定する IList<Choice> を作成privatestaticIList<Choice>choices=ChoiceFactory.ToChoices(menus.Select(x=>x.Key).ToList());publicMenuDialog():base(nameof(MenuDialog)){// ウォーターフォールのステップを定義。処理順にメソッドを追加。varwaterfallSteps=newWaterfallStep[]{ShowMenuAsync,ProcessInputAsync,LoopMenu};// ウォーターフォールダイアログと各種プロンプトを追加AddDialog(newWaterfallDialog("menu",waterfallSteps));AddDialog(newChoicePrompt("choice"));AddDialog(newWeatherDialog());AddDialog(newScheduleDialog());}publicstaticasyncTask<DialogTurnResult>ShowMenuAsync(WaterfallStepContextstepContext,CancellationTokencancellationToken){// Choice プロンプトでメニューを表示returnawaitstepContext.PromptAsync("choice",newPromptOptions{Prompt=MessageFactory.Text("今日はなにをしますか?"),Choices=choices,},cancellationToken);}privateasyncTask<DialogTurnResult>ProcessInputAsync(WaterfallStepContextstepContext,CancellationTokencancellationToken){// 答えを確認して次のダイアログ名を取得varchoice=(FoundChoice)stepContext.Result;vardialogId=menus[choice.Value];// 子ダイアログの実行returnawaitstepContext.BeginDialogAsync(dialogId,null,cancellationToken);}privateasyncTask<DialogTurnResult>LoopMenu(WaterfallStepContextstepContext,CancellationTokencancellationToken){// スタックに乗せないように、Replace でメニューを再表示returnawaitstepContext.ReplaceDialogAsync("menu",null,cancellationToken);}}2. MyBot.cs で MenuDialog を使えるように変更。
- コンストラクタで DialogSet に MenuDialog を追加
- DialogTurnStatusComplete で MenuDialog を開始するよう設定
usingSystem;usingSystem.Linq;usingSystem.Threading;usingSystem.Threading.Tasks;usingMicrosoft.Bot.Builder;usingMicrosoft.Bot.Builder.Dialogs;usingMicrosoft.Bot.Schema;publicclassMyBot:IBot{privateMyStateAccessorsaccessors;privateDialogSetdialogs;// DI で MyStateAccessors は自動解決publicMyBot(MyStateAccessorsaccessors){this.accessors=accessors;this.dialogs=newDialogSet(accessors.ConversationDialogState);// コンポーネントダイアログを追加dialogs.Add(newProfileDialog(accessors));dialogs.Add(newMenuDialog());}publicasyncTaskOnTurnAsync(ITurnContextturnContext,CancellationTokencancellationToken=default(CancellationToken)){// DialogSet からコンテキストを作成vardialogContext=awaitdialogs.CreateContextAsync(turnContext,cancellationToken);// ユーザーからメッセージが来た場合if(turnContext.Activity.Type==ActivityTypes.Message){// まず ContinueDialogAsync を実行して既存のダイアログがあれば継続実行。varresults=awaitdialogContext.ContinueDialogAsync(cancellationToken);// DialogTurnStatus が Complete または Empty の場合、メニューへ。if(results.Status==DialogTurnStatus.Complete||results.Status==DialogTurnStatus.Empty){varuserProfile=awaitaccessors.UserProfile.GetAsync(turnContext,()=>newUserProfile(),cancellationToken);awaitturnContext.SendActivityAsync(MessageFactory.Text($"ようこそ '{userProfile.Name}' さん!"));// メニューの表示awaitdialogContext.BeginDialogAsync(nameof(MenuDialog),null,cancellationToken);}}// ユーザーとボットが会話に参加したelseif(turnContext.Activity.Type==ActivityTypes.ConversationUpdate){// turnContext より Activity を取得varactivity=turnContext.Activity.AsConversationUpdateActivity();// ユーザーの参加に対してだけ、プロファイルダイアログを開始if(activity.MembersAdded.Any(member=>member.Id!=activity.Recipient.Id)){awaitturnContext.SendActivityAsync("ようこそ MyBot へ!");awaitdialogContext.BeginDialogAsync(nameof(ProfileDialog),null,cancellationToken);}}// 最後に現在の UserProfile と DialogState を保存awaitaccessors.UserState.SaveChangesAsync(turnContext,false,cancellationToken);awaitaccessors.ConversationState.SaveChangesAsync(turnContext,false,cancellationToken);}}3. Dialogs フォルダに WeatherDialog.cs を追加し以下コードを張り付け。
- EndDialogAsync でダイアログを終了
usingSystem.Threading;usingSystem.Threading.Tasks;usingMicrosoft.Bot.Builder.Dialogs;publicclassWeatherDialog:ComponentDialog{publicWeatherDialog():base(nameof(WeatherDialog)){// ウォーターフォールのステップを定義。処理順にメソッドを追加。varwaterfallSteps=newWaterfallStep[]{ShowWeatherAsync,};// ウォーターフォールダイアログと各種プロンプトを追加AddDialog(newWaterfallDialog("weather",waterfallSteps));}publicstaticasyncTask<DialogTurnResult>ShowWeatherAsync(WaterfallStepContextstepContext,CancellationTokencancellationToken){awaitstepContext.Context.SendActivityAsync("今日の天気は晴れです");returnawaitstepContext.EndDialogAsync(true,cancellationToken);}}4. 同様に ScheduleDialog.cs を追加し以下コードを張り付け。
usingSystem.Threading;usingSystem.Threading.Tasks;usingMicrosoft.Bot.Builder.Dialogs;publicclassScheduleDialog:ComponentDialog{publicScheduleDialog():base(nameof(ScheduleDialog)){// ウォーターフォールのステップを定義。処理順にメソッドを追加。varwaterfallSteps=newWaterfallStep[]{ShowScheduleAsync,};// ウォーターフォールダイアログと各種プロンプトを追加AddDialog(newWaterfallDialog("schedule",waterfallSteps));}publicstaticasyncTask<DialogTurnResult>ShowScheduleAsync(WaterfallStepContextstepContext,CancellationTokencancellationToken){awaitstepContext.Context.SendActivityAsync("今日は予定はありません。");returnawaitstepContext.EndDialogAsync(true,cancellationToken);}}まとめ
今回は複数のダイアログを管理する方法と、ユーザーに対する初回応答の方法を紹介しました。次回はグローバルコマンドについて紹介します。
参照
基本的には同じことをしていますが、より詳細なコードを試したい場合は以下を参照。
Manage complex conversation flows with dialogs
Register as a new user and use Qiita more conveniently
- You get articles that match your needs
- You can efficiently read back useful information
- You can use dark theme



