ブランチとループを使用して高度な会話フローを作成するCreate advanced conversation flow using branches and loops

適用対象: SDK v4APPLIES TO: SDK v4

ダイアログ ライブラリを使用して、複雑な会話フローを作成できます。You can create complex conversation flows using the dialogs library. この記事では、分岐およびループする複雑な会話を管理する方法と、ダイアログのさまざまな部分の間で引数を渡す方法について説明します。This article covers how to manage complex conversations that branch and loop and how to pass arguments between different parts of the dialog.

前提条件Prerequisites

このサンプルについてAbout this sample

このボットのサンプルでは、ユーザーがサインアップし、一覧から最大 2 つの会社をレビューできます。This sample represents a bot that can sign users up to review up to two companies from a list. ボットは 3 つのコンポーネント ダイアログを使用して、会話フローを管理します。The bot uses 3 component dialogs to manage the conversation flow. 各コンポーネント ダイアログには、ウォーターフォール ダイアログと、ユーザー入力を収集するために必要なプロンプトが含まれています。Each component dialog includes a waterfall dialog and any prompts needed to gather user input. これらのダイアログについては、以下のセクションで詳しく説明します。These dialogs are described in more detail in the following sections. 会話状態を使用してダイアログを管理し、ユーザー状態を使用してユーザーおよびユーザーがレビューする会社に関する情報を保存します。It uses conversation state to manage its dialogs and uses user state to save information about the user and which companies they want to review.

ボットはアクティビティ ハンドラーから派生します。The bot derives from the activity handler. 多くのサンプル ボットと同様に、ユーザーに歓迎の意を示し、ダイアログを使用してユーザーからのメッセージを処理し、ターンが終わる前にユーザーと会話状態を保存します。Like many of the sample bots, it welcomes the user, uses dialogs to handle messages from the user, and saves user and conversation state before the turn ends.

ダイアログを使用するには、Microsoft.Bot.Builder.Dialogs NuGet パッケージをインストールします。To use dialogs, install the Microsoft.Bot.Builder.Dialogs NuGet package.

C# の複雑なボット フロー図

ユーザー プロファイルを定義するDefine the user profile

ユーザー プロファイルには、ダイアログによって収集された情報、ユーザーの名前、年齢、およびレビュー対象として選択された会社が含まれます。The user profile will contain information gathered by the dialogs, the user's name, age, and companies selected to review.

UserProfile.csUserProfile.cs

/// <summary>Contains information about a user.</summary>
public class UserProfile
{
    public string Name { get; set; }
    public int Age { get; set; }

    // The list of companies the user wants to review.
    public List<string> CompaniesToReview { get; set; } = new List<string>();
}

ダイアログを作成するCreate the dialogs

このボットには、次の 3 つのダイアログが含まれています。This bot contains 3 dialogs:

  • メイン ダイアログでは、プロセス全体が開始され、収集された情報が要約されます。The main dialog starts the overall process and then summarizes the collected information.
  • 最上位レベルのダイアログでは、ユーザー情報が収集され、ユーザーの年齢に基づく分岐ロジックが含まれます。The top-level dialog collects the user information and includes branching logic, based on the user's age.
  • review-selection ダイアログでは、ユーザーはレビューする会社を繰り返し選択できます。The review-selection dialog allows the user to iteratively select companies to review. ループ ロジックを使用してこれを行います。It uses looping logic to do so.

メイン ダイアログThe main dialog

メイン ダイアログには、次の 2 つのステップがあります。The main dialog has 2 steps:

  1. 最上位レベルのダイアログを開始します。Start the top-level dialog.
  2. 最上位レベルのダイアログによって収集されたユーザー プロファイルを取得して要約し、その情報をユーザー状態に保存して、メイン ダイアログの終了を通知します。Retrieve and summarize the user profile that the top-level dialog collected, save that information to user state, and then signal the end of the main dialog.

Dialogs\MainDialog.csDialogs\MainDialog.cs

private async Task<DialogTurnResult> InitialStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
    return await stepContext.BeginDialogAsync(nameof(TopLevelDialog), null, cancellationToken);
}

private async Task<DialogTurnResult> FinalStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
    var userInfo = (UserProfile)stepContext.Result;

    string status = "You are signed up to review "
        + (userInfo.CompaniesToReview.Count is 0 ? "no companies" : string.Join(" and ", userInfo.CompaniesToReview))
        + ".";

    await stepContext.Context.SendActivityAsync(status);

    var accessor = _userState.CreateProperty<UserProfile>(nameof(UserProfile));
    await accessor.SetAsync(stepContext.Context, userInfo, cancellationToken);

    return await stepContext.EndDialogAsync(null, cancellationToken);
}

最上位レベルのダイアログThe top-level dialog

最上位レベルのダイアログには、次の 4 つのステップがあります。The top-level dialog has 4 steps:

  1. ユーザー名の入力を求めます。Ask for the user's name.
  2. ユーザーの年齢の入力を求めます。Ask for the user's age.
  3. ユーザーの年齢に基づいて、review-selection ダイアログを開始するか、次のステップに進みます。Either start the review-selection dialog or progress to the next step, based on the user's age.
  4. 最後に、ユーザーが参加してくれたことに対してお礼を述べ、収集した情報を返します。Finally, thank the user for participating and return the collected information.

最初のステップでは、ダイアログ状態の一部として空のユーザー プロファイルが作成されます。The first step creates an empty user profile as part of the dialog state. ダイアログは空のプロファイルで開始され、その進行とともにプロファイルに情報が追加されます。The dialog starts with an empty profile and adds information to the profile as it progresses. 終了すると、最後のステップで、収集された情報が返されます。When it ends, the last step returns the collected information.

3 番目 (選択の開始) のステップでは、ユーザーの年齢に基づいて会話フローが分岐します。In the third (start selection) step, the conversation flow branches, based on the user's age.

Dialogs\TopLevelDialog.csDialogs\TopLevelDialog.cs

private static async Task<DialogTurnResult> NameStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
    // Create an object in which to collect the user's information within the dialog.
    stepContext.Values[UserInfo] = new UserProfile();

    var promptOptions = new PromptOptions { Prompt = MessageFactory.Text("Please enter your name.") };

    // Ask the user to enter their name.
    return await stepContext.PromptAsync(nameof(TextPrompt), promptOptions, cancellationToken);
}

private async Task<DialogTurnResult> AgeStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
    // Set the user's name to what they entered in response to the name prompt.
    var userProfile = (UserProfile)stepContext.Values[UserInfo];
    userProfile.Name = (string)stepContext.Result;

    var promptOptions = new PromptOptions { Prompt = MessageFactory.Text("Please enter your age.") };

    // Ask the user to enter their age.
    return await stepContext.PromptAsync(nameof(NumberPrompt<int>), promptOptions, cancellationToken);
}

private async Task<DialogTurnResult> StartSelectionStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
    // Set the user's age to what they entered in response to the age prompt.
    var userProfile = (UserProfile)stepContext.Values[UserInfo];
    userProfile.Age = (int)stepContext.Result;

    if (userProfile.Age < 25)
    {
        // If they are too young, skip the review selection dialog, and pass an empty list to the next step.
        await stepContext.Context.SendActivityAsync(
            MessageFactory.Text("You must be 25 or older to participate."),
            cancellationToken);
        return await stepContext.NextAsync(new List<string>(), cancellationToken);
    }
    else
    {
        // Otherwise, start the review selection dialog.
        return await stepContext.BeginDialogAsync(nameof(ReviewSelectionDialog), null, cancellationToken);
    }
}

private async Task<DialogTurnResult> AcknowledgementStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
    // Set the user's company selection to what they entered in the review-selection dialog.
    var userProfile = (UserProfile)stepContext.Values[UserInfo];
    userProfile.CompaniesToReview = stepContext.Result as List<string> ?? new List<string>();

    // Thank them for participating.
    await stepContext.Context.SendActivityAsync(
        MessageFactory.Text($"Thanks for participating, {((UserProfile)stepContext.Values[UserInfo]).Name}."),
        cancellationToken);

    // Exit the dialog, returning the collected user information.
    return await stepContext.EndDialogAsync(stepContext.Values[UserInfo], cancellationToken);
}

review-selection ダイアログThe review-selection dialog

review-selection ダイアログには、次の 2 つのステップがあります。The review-selection dialog has 2 steps:

  1. ユーザーに対して、レビューする会社を選択するか、done を選択して完了するよう求めます。Ask the user to choose a company to review or done to finish.
    • ダイアログが初期情報を使用して開始された場合、その情報はウォーターフォール ステップ コンテキストの options プロパティを通じて使用可能になります。If the dialog was started with any initial information, the information is available through the options property of the waterfall step context. review-selection ダイアログは、自動的に再起動することができ、これを使用して、レビューする会社をユーザーが複数選択できるようにします。The review-selection dialog can restart itself, and it uses this to allow the user to choose more than one company to review.
    • レビューする会社をユーザーが既に選択している場合、その会社は使用可能な選択肢から削除されます。If the user has already selected a company to review, that company is removed from the available choices.
    • ユーザーがループを早期に終了できるように done の選択肢が追加されます。A done choice is added to allow the user to exit the loop early.
  2. 必要に応じて、このダイアログを繰り返すか終了します。Repeat this dialog or exit, as appropriate.
    • レビューする会社をユーザーが選択した場合は、それを一覧に追加します。If the user chose a company to review, add it to their list.
    • ユーザーが 2 つの会社を選択した場合、または終了を選択した場合は、ダイアログを終了し、収集された一覧を返します。If the user has chosen 2 companies or they chose to exit, end the dialog and return the collected list.
    • それ以外の場合は、ダイアログを再起動し、それを一覧の内容で初期化します。Otherwise, restart the dialog, initializing it with the contents of their list.

Dialogs\ReviewSelectionDialog.csDialogs\ReviewSelectionDialog.cs

private async Task<DialogTurnResult> SelectionStepAsync(
    WaterfallStepContext stepContext,
    CancellationToken cancellationToken)
{
    // Continue using the same selection list, if any, from the previous iteration of this dialog.
    var list = stepContext.Options as List<string> ?? new List<string>();
    stepContext.Values[CompaniesSelected] = list;

    // Create a prompt message.
    string message;
    if (list.Count is 0)
    {
        message = $"Please choose a company to review, or `{DoneOption}` to finish.";
    }
    else
    {
        message = $"You have selected **{list[0]}**. You can review an additional company, " +
            $"or choose `{DoneOption}` to finish.";
    }

    // Create the list of options to choose from.
    var options = _companyOptions.ToList();
    options.Add(DoneOption);
    if (list.Count > 0)
    {
        options.Remove(list[0]);
    }

    var promptOptions = new PromptOptions
    {
        Prompt = MessageFactory.Text(message),
        RetryPrompt = MessageFactory.Text("Please choose an option from the list."),
        Choices = ChoiceFactory.ToChoices(options),
    };

    // Prompt the user for a choice.
    return await stepContext.PromptAsync(nameof(ChoicePrompt), promptOptions, cancellationToken);
}

private async Task<DialogTurnResult> LoopStepAsync(
    WaterfallStepContext stepContext,
    CancellationToken cancellationToken)
{
    // Retrieve their selection list, the choice they made, and whether they chose to finish.
    var list = stepContext.Values[CompaniesSelected] as List<string>;
    var choice = (FoundChoice)stepContext.Result;
    var done = choice.Value == DoneOption;

    if (!done)
    {
        // If they chose a company, add it to the list.
        list.Add(choice.Value);
    }

    if (done || list.Count >= 2)
    {
        // If they're done, exit and return their list.
        return await stepContext.EndDialogAsync(list, cancellationToken);
    }
    else
    {
        // Otherwise, repeat this dialog, passing in the list from this iteration.
        return await stepContext.ReplaceDialogAsync(nameof(ReviewSelectionDialog), list, cancellationToken);
    }
}

ダイアログを実行するRun the dialogs

dialog bot クラスは、アクティビティ ハンドラーを拡張します。また、ダイアログを実行するためのロジックを含んでいます。The dialog bot class extends the activity handler, and it contains the logic for running the dialogs. dialog and welcome bot クラスは、ダイアログ ボットを拡張し、ユーザーが会話に参加したときに歓迎の意も示します。The dialog and welcome bot class extends the dialog bot to also welcome a user when they join the conversation.

ボットのターン ハンドラーによって、3 つのダイアログで定義された会話フローが繰り返されます。The bot's turn handler repeats the conversation flow defined by the 3 dialogs. ユーザーからメッセージを受信すると、次の操作を行います。When it receives a message from the user:

  1. メイン ダイアログを実行します。It runs the main dialog.
    • ダイアログ スタックが空の場合は、メイン ダイアログが開始されます。If the dialog stack is empty, this will start the main dialog.
    • それ以外の場合は、ダイアログはまだ処理中であり、アクティブなダイアログが継続されます。Otherwise, the dialogs are still in mid-process, and this will continue the active dialog.
  2. 状態を保存します。これにより、ユーザー、会話、およびダイアログの状態に対するすべての更新が保持されます。It saves state, so that any updates to the user, conversation, and dialog state are persisted.

Bots\DialogBot.csBots\DialogBot.cs

public override async Task OnTurnAsync(ITurnContext turnContext, CancellationToken cancellationToken = default(CancellationToken))
{
    await base.OnTurnAsync(turnContext, cancellationToken);

    // Save any state changes that might have occurred during the turn.
    await ConversationState.SaveChangesAsync(turnContext, false, cancellationToken);
    await UserState.SaveChangesAsync(turnContext, false, cancellationToken);
}

protected override async Task OnMessageActivityAsync(ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken)
{
    Logger.LogInformation("Running dialog with Message Activity.");

    // Run the Dialog with the new message Activity.
    await Dialog.RunAsync(turnContext, ConversationState.CreateProperty<DialogState>(nameof(DialogState)), cancellationToken);
}

ボット用のサービスを登録するRegister services for the bot

必要に応じてサービスを作成および登録します。Create and register services as needed:

  • ボット用の基本サービス: アダプターおよびボット実装。Basic services for the bot: an adapter and the bot implementation.
  • 状態を管理するためのサービス: ストレージ、ユーザー状態、および会話の状態。Services for managing state: storage, user state, and conversation state.
  • ボットが使用するルート ダイアログ。The root dialog the bot will use.

Startup.csStartup.cs

public void ConfigureServices(IServiceCollection services)
{
    services.AddHttpClient().AddControllers().AddNewtonsoftJson();

    // Create the Bot Framework Adapter with error handling enabled.
    services.AddSingleton<IBotFrameworkHttpAdapter, AdapterWithErrorHandler>();

    // Create the storage we'll be using for User and Conversation state. (Memory is great for testing purposes.)
    services.AddSingleton<IStorage, MemoryStorage>();

    // Create the User state. (Used in this bot's Dialog implementation.)
    services.AddSingleton<UserState>();

    // Create the Conversation state. (Used by the Dialog system itself.)
    services.AddSingleton<ConversationState>();

    services.AddSingleton<MainDialog>();
    // Create the bot as a transient. In this case the ASP Controller is expecting an IBot.
    services.AddTransient<IBot, DialogAndWelcomeBot<MainDialog>>();
}

注意

メモリ ストレージはテストにのみ使用され、実稼働を目的としたものではありません。Memory storage is used for testing purposes only and is not intended for production use. 運用環境のボットでは、必ず永続タイプのストレージを使用してください。Be sure to use a persistent type of storage for a production bot.

ボットをテストするTo test the bot

  1. Bot Framework Emulator をインストールします (まだインストールしていない場合)。If you have not done so already, install the Bot Framework Emulator.
  2. ご自身のマシンを使ってローカルでサンプルを実行します。Run the sample locally on your machine.
  3. 以下に示すように、エミュレーターを起動し、お使いのボットに接続して、メッセージを送信します。Start the Emulator, connect to your bot, and send messages as shown below.

複雑なダイアログのサンプルをテストする

その他のリソースAdditional resources

ダイアログの実装方法の概要については、連続して行われる会話フローの実装に関するページをご覧ください。この記事では、1 つのウォーターフォール ダイアログと、いくつかのプロンプトを使用して、ユーザーに一連の質問を行う単純なやり取りを作成します。For an introduction on how to implement a dialog, see implement sequential conversation flow, which uses a single waterfall dialog and a few prompts to create a simple interaction that asks the user a series of questions.

ダイアログ ライブラリには、プロンプト用の基本的な検証が含まれます。The Dialogs library includes basic validation for prompts. カスタム検証を追加することもできます。You can also add custom validation. 詳細については、ダイアログ プロンプトを使用したユーザー入力の収集に関するページをご覧ください。For more information, see gather user input using a dialog prompt.

ご自身のダイアログ コードを簡素化し、複数のボットで再利用するために、ダイアログ セットの一部を別のクラスとして定義できます。To simplify your dialog code and reuse it multiple bots, you can define portions of a dialog set as a separate class. 詳細については、ダイアログの再利用に関するページをご覧ください。For more information, see reuse dialogs.

次のステップNext steps