분기 및 루프를 사용하여 고급 대화 흐름 만들기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.
  • 검토 선택 대화를 통해 사용자는 검토할 회사를 반복적으로 선택할 수 있습니다.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. 사용자의 나이를 기준으로 검토 선택 대화를 시작하거나 다음 단계로 진행합니다.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.

세 번째 단계(선택 시작)에서는 사용자의 나이에 따라 대화 흐름이 분기됩니다.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);
}

검토 선택 대화The review-selection dialog

검토 선택 대화는 2단계로 이루어집니다.The review-selection dialog has 2 steps:

  1. 사용자에게 검토할 회사를 선택하도록 요청하거나 done을 선택하여 완료합니다.Ask the user to choose a company to review or done to finish.
    • 대화가 초기 정보로 시작된 경우 폭포 단계 컨텍스트의 옵션 속성을 통해 정보를 사용할 수 있습니다.If the dialog was started with any initial information, the information is available through the options property of the waterfall step context. 검토 선택 대화가 다시 시작될 수 있으며 이는 사용자가 검토할 회사를 여러 개 선택할 수 있도록 하는 데 사용됩니다.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

대화 봇 클래스는 작업 처리기를 확장하며 대화 실행을 위한 논리를 포함합니다.The dialog bot class extends the activity handler, and it contains the logic for running the dialogs. 대화 및 환영 봇 클래스는 대화 봇을 확장하여 대화 참여 시 사용자를 환영합니다.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

대화를 구현하는 방법에 대한 소개는 순차적 대화 흐름 구현을 참조하세요. 여기에서는 단일 폭포 대화와 몇 가지 프롬프트를 사용하여 사용자에게 일련의 질문을 하는 간단한 상호 작용을 만듭니다.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.

Dialogs 라이브러리에는 프롬프트에 대한 기본 유효성 검사가 포함되어 있습니다.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