Creare un flusso di conversazione avanzato con rami e cicli

SI APPLICA A: SDK v4

È possibile creare flussi di conversazione semplici e complessi usando la libreria di dialoghi. Questo articolo illustra come gestire conversazioni complesse con rami e cicli e come passare argomenti tra parti diverse del dialogo.

Nota

Gli SDK JavaScript, C# e Python di Bot Framework continueranno a essere supportati, ma Java SDK verrà ritirato con il supporto finale a lungo termine che termina a novembre 2023.

I bot esistenti creati con Java SDK continueranno a funzionare.

Per la creazione di nuovi bot, è consigliabile usare Microsoft Copilot Studio e leggere le informazioni sulla scelta della soluzione copilota appropriata.

Per altre informazioni, vedere Il futuro della compilazione di bot.

Prerequisiti

Informazioni sull'esempio

Questo esempio rappresenta un bot che può consentire agli utenti di accedere per recensire fino a due aziende da un elenco. Il bot usa tre dialoghi componenti per gestire il flusso di conversazione. Ogni dialogo componente include un dialogo a cascata ed eventuali richieste necessarie per raccogliere l'input dell'utente. Questi dialoghi vengono descritti in maggior dettaglio nelle sezioni seguenti. Il bot usa lo stato della conversazione per gestire i dialoghi e lo stato dell'utente per salvare informazioni sull'utente e sulle aziende che vuole recensire.

Il bot deriva dal gestore attività. Come molti bot di esempio, saluta l'utente, usa i dialoghi per gestire i messaggi che invia e salva il suo stato e quello della conversazione prima che termini in turno.

Per usare i dialoghi, installare il pacchetto NuGet Microsoft.Bot.Builder.Dialogs.

Diagramma classi per l'esempio C#.

Definire il profilo utente

Il profilo utente conterrà le informazioni raccolte dai dialoghi, il nome e l'età dell'utente, nonché le aziende selezionate per la recensione.

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

Creare i dialoghi

Questo bot contiene tre dialoghi:

  • Il dialogo principale avvia il processo generale e quindi riepiloga le informazioni raccolte.
  • Il dialogo di primo livello raccoglie le informazioni dell'utente e include la logica di diramazione, in base all'età dell'utente.
  • Il dialogo di selezione della recensione consente all'utente di scegliere le aziende in modo iterativo. A tale scopo, usa la logica di ciclo.

Il dialogo principale

Il dialogo principale prevede due passaggi:

  1. Avviare il dialogo di primo livello.
  2. Recuperare e riepilogare il profilo utente raccolto dal dialogo di primo livello, salvare le informazioni nello stato dell'utente e quindi segnalare la fine del dialogo principale.

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

Dialogo di primo livello

Il dialogo di primo livello prevede quattro passaggi:

  1. Chiedere il nome dell'utente.
  2. Chiedere l'età dell'utente.
  3. Avviare il dialogo di selezione della recensione o procedere con il passaggio successivo, in base all'età dell'utente.
  4. Ringraziare infine l'utente per aver partecipato e restituire le informazioni raccolte.

Il primo passaggio consiste nella creazione di un profilo utente vuoto come parte dello stato del dialogo. Il dialogo inizia con un profilo vuoto e vi aggiunge informazioni man mano che prosegue. Al termine, l'ultimo passaggio restituisce le informazioni raccolte.

Nel terzo passaggio (avvio della selezione), il flusso della conversazione si dirama, in base all'età dell'utente.

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

Dialogo di selezione della recensione

Il dialogo di selezione della recensione prevede due passaggi:

  1. Chiedere all'utente di scegliere un'azienda da recensire o scegliere done per terminare.
    • Se il dialogo è stato avviato con informazioni iniziali, tali informazioni sono disponibili tramite la proprietà options del contesto del passaggio a cascata. Il dialogo di selezione della recensione può riavviarsi automaticamente e in questo modo consente all'utente di scegliere più di un'azienda da recensire.
    • Se l'utente ha già selezionato un'azienda da recensire, quest'ultima viene rimossa dalle opzioni disponibili.
    • Viene aggiunta un'opzione done per consentire all'utente di uscire prima dal ciclo.
  2. Ripetere questo dialogo o uscire, a seconda dei casi.
    • Se l'utente sceglie un'azienda da recensire, aggiungerla al suo elenco.
    • Se l'utente ha scelto due società o ha scelto di uscire, terminare la finestra di dialogo e restituire l'elenco raccolto.
    • In caso contrario, riavviare il dialogo, inizializzandolo con il contenuto dell'elenco.

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

Eseguire i dialoghi

La classe dialog bot estende il gestore attività e contiene la logica per l'esecuzione dei dialoghi. La classe dialog and welcome bot classe estende la classe dialog bot per aggiungere anche un messaggio di benvenuto all'utente quando si aggiunge alla conversazione.

Il gestore dei turni del bot ripete il flusso di conversazione definito dai tre dialoghi. Quando riceve un messaggio dall'utente:

  1. Esegue il dialogo principale.
    • Se lo stack del dialogo è vuoto, viene avviato il dialogo principale.
    • In caso contrario, i dialoghi sono ancora in corso e verrà continuato il dialogo attivo.
  2. Lo stato viene salvato, per cui eventuali aggiornamenti apportati allo stato dell'utente, della conversazione e del dialogo vengono resi persistenti.

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

Registrare i servizi per il bot

Creare e registrare i servizi in base alle esigenze:

  • Servizi di base per un bot: un adattatore e l'implementazione del bot.
  • Servizi per la gestione dello stato: archiviazione, stato utente e stato conversazione.
  • Il dialogo radice che verrà usato dal bot.

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

L'archiviazione di memoria viene usata solo a scopo di test e non è destinata all'uso in produzione. Assicurarsi di usare un tipo di archiviazione permanente per un bot di produzione.

Test del bot

  1. Se non è già stato fatto, installare Bot Framework Emulator.

  2. Eseguire l'esempio in locale nel computer.

  3. Avviare l'emulatore, connettersi al bot e inviare messaggi come mostrato di seguito.

    Trascrizione di esempio da una conversazione con il bot di dialogo complesso.

Risorse aggiuntive

Per un'introduzione su come implementare un dialogo, vedere Implementare un flusso di conversazione sequenziale, che usa un singolo dialogo a cascata e alcune richieste per porre all'utente una serie di domande.

La libreria Dialogs include la convalida di base per i prompt. È anche possibile aggiungere una convalida personalizzata. Per altre informazioni, vedere Raccogliere l'input degli utenti con una richiesta di dialogo.

Per semplificare il codice del dialogo e riusarlo per più bot, è possibile definire parti di un set di dialoghi come classe separata. Per altre informazioni, vedere Riusare i dialoghi.

Passaggi successivi