Partilhar via


Crie um fluxo de conversação avançado usando ramificações e loops

APLICA-SE A: SDK v4

Você pode criar fluxos de conversa complexos usando a biblioteca de diálogos. Este artigo aborda como gerenciar conversas complexas que ramificam e fazem loop e como passar argumentos entre diferentes partes da caixa de diálogo.

Nota

Os SDKs JavaScript, C# e Python do Bot Framework continuarão a ser suportados, no entanto, o Java SDK está sendo desativado com suporte final de longo prazo terminando em novembro de 2023.

Os bots existentes construídos com o Java SDK continuarão a funcionar.

Para a criação de novos bots, considere usar o Power Virtual Agents e leia sobre como escolher a solução de chatbot certa.

Para obter mais informações, consulte O futuro da criação de bots.

Pré-requisitos

Sobre este exemplo

Este exemplo representa um bot que pode inscrever usuários para revisar até duas empresas de uma lista. O bot usa três caixas de diálogo de componentes para gerenciar o fluxo de conversa. Cada caixa de diálogo de componente inclui uma caixa de diálogo em cascata e todos os prompts necessários para coletar a entrada do usuário. Essas caixas de diálogo são descritas com mais detalhes nas seções a seguir. Ele usa o estado da conversa para gerenciar suas caixas de diálogo e usa o estado do usuário para salvar informações sobre o usuário e quais empresas eles desejam revisar.

O bot deriva do manipulador de atividades. Como muitos dos bots de exemplo, ele dá as boas-vindas ao usuário, usa caixas de diálogo para lidar com mensagens do usuário e salva o estado do usuário e da conversa antes que a vez termine.

Para usar caixas de diálogo, instale o pacote NuGet Microsoft.Bot.Builder.Dialogs .

Diagrama de classes para exemplo de C#.

Definir o perfil de usuário

O perfil de usuário conterá informações coletadas pelas caixas de diálogo, o nome do usuário, idade e empresas selecionadas para revisão.

UserProfile.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>();

Criar as caixas de diálogo

Este bot contém três caixas de diálogo:

  • A caixa de diálogo principal inicia o processo geral e, em seguida, resume as informações coletadas.
  • A caixa de diálogo de nível superior coleta as informações do usuário e inclui lógica de ramificação, com base na idade do usuário.
  • A caixa de diálogo revisão-seleção permite que o usuário selecione iterativamente as empresas a serem revisadas. Ele usa a lógica de looping para fazer isso.

A caixa de diálogo principal

A caixa de diálogo principal tem duas etapas:

  1. Inicie a caixa de diálogo de nível superior.
  2. Recupere e resuma o perfil de usuário coletado pela caixa de diálogo de nível superior, salve essas informações no estado do usuário e sinalize o fim da caixa de diálogo principal.

Caixas de diálogo\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);
}

A caixa de diálogo de nível superior

A caixa de diálogo de nível superior tem quatro etapas:

  1. Peça o nome do usuário.
  2. Pergunte a idade do usuário.
  3. Inicie a caixa de diálogo de seleção de revisão ou avance para a próxima etapa, com base na idade do usuário.
  4. Por fim, agradeça ao usuário pela participação e devolva as informações coletadas.

A primeira etapa cria um perfil de usuário vazio como parte do estado da caixa de diálogo. A caixa de diálogo começa com um perfil vazio e adiciona informações ao perfil à medida que ele progride. Quando termina, a última etapa retorna as informações coletadas.

Na terceira etapa (seleção inicial), o fluxo de conversa se ramifica, com base na idade do usuário.

Caixas de diálogo\TopLevelDialog.cs

            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);
        }
    }
}

A caixa de diálogo revisão-seleção

A caixa de diálogo revisão-seleção tem duas etapas:

  1. Peça ao usuário para escolher uma empresa para revisar ou done concluir.
    • Se a caixa de diálogo foi iniciada com qualquer informação inicial, as informações estão disponíveis através da propriedade options do contexto da etapa em cascata. A caixa de diálogo de seleção de revisão pode ser reiniciada e usa isso para permitir que o usuário escolha mais de uma empresa para revisar.
    • Se o usuário já tiver selecionado uma empresa para revisão, essa empresa será removida das opções disponíveis.
    • Uma done opção é adicionada para permitir que o usuário saia do loop mais cedo.
  2. Repita esta caixa de diálogo ou saia, conforme apropriado.
    • Se o usuário escolheu uma empresa para analisar, adicione-a à lista.
    • Se o usuário escolheu duas empresas ou optou por sair, encerre a caixa de diálogo e retorne a lista coletada.
    • Caso contrário, reinicie a caixa de diálogo, inicializando-a com o conteúdo de sua lista.

Caixas de diálogo\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(InitialDialogId, list, cancellationToken);
    }
}

Executar as caixas de diálogo

A classe de bot de diálogo estende o manipulador de atividades e contém a lógica para executar as caixas de diálogo. A classe de bot de diálogo e boas-vindas estende o bot de diálogo para também dar as boas-vindas a um usuário quando ele ingressa na conversa.

O manipulador de turnos do bot repete o fluxo de conversa definido pelas três caixas de diálogo. Quando recebe uma mensagem do utilizador:

  1. Ele executa a caixa de diálogo principal.
    • Se a pilha de diálogo estiver vazia, isso iniciará a caixa de diálogo principal.
    • Caso contrário, as caixas de diálogo ainda estão no meio do processo, e isso continuará a caixa de diálogo ativa.
  2. Ele salva o estado, para que todas as atualizações do usuário, da conversa e do estado da caixa de diálogo sejam persistentes.

Bots\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);
}

Registrar serviços para o bot

Crie e registre serviços conforme necessário:

  • Serviços básicos para o bot: um adaptador e a implementação do bot.
  • Serviços para gerenciar o estado: armazenamento, estado do usuário e estado da conversa.
  • A caixa de diálogo raiz que o bot usará.

Startup.cs

// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
    services.AddHttpClient().AddControllers().AddNewtonsoftJson(options =>
    {
        options.SerializerSettings.MaxDepth = HttpHelper.BotMessageSerializerSettings.MaxDepth;
    });

    // Create the Bot Framework Authentication to be used with the Bot Adapter.
    services.AddSingleton<BotFrameworkAuthentication, ConfigurationBotFrameworkAuthentication>();

    // Create the Bot 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>();

Nota

O armazenamento de memória é usado apenas para fins de teste e não se destina ao uso em produção. Certifique-se de usar um tipo persistente de armazenamento para um bot de produção.

Testar o bot

  1. Se você ainda não fez isso, instale o Bot Framework Emulator.

  2. Execute a amostra localmente na sua máquina.

  3. Inicie o emulador, conecte-se ao seu bot e envie mensagens como mostrado abaixo.

    Exemplo de transcrição de uma conversa com o bot de diálogo complexo.

Recursos adicionais

Para obter uma introdução sobre como implementar uma caixa de diálogo, consulte implementar fluxo de conversa sequencial, que usa uma única caixa de diálogo em cascata e alguns prompts para fazer uma série de perguntas ao usuário.

A biblioteca Diálogos inclui validação básica para prompts. Você também pode adicionar validação personalizada. Para obter mais informações, consulte coletar a entrada do usuário usando um prompt de diálogo.

Para simplificar seu código de diálogo e reutilizá-lo vários bots, você pode definir partes de um conjunto de diálogo como uma classe separada. Para obter mais informações, consulte caixas de diálogo de reutilização.

Próximos passos