Bagikan melalui


Menerapkan alur percakapan berurutan

BERLAKU UNTUK: SDK v4

Mengumpulkan informasi dengan mengajukan pertanyaan adalah salah satu cara utama bot berinteraksi dengan pengguna. Pustaka dialog menyediakan fitur bawaan yang berguna seperti kelas prompt yang memudahkan untuk mengajukan pertanyaan dan memvalidasi respons untuk memastikannya cocok dengan jenis data tertentu atau memenuhi aturan validasi kustom.

Anda dapat mengelola alur percakapan linier dan lebih kompleks menggunakan pustaka dialog. Dalam interaksi linier, bot berjalan melalui urutan langkah tetap, dan percakapan selesai. Dialog berguna ketika bot perlu mengumpulkan informasi dari pengguna.

Artikel ini memperlihatkan cara menerapkan alur percakapan linier dengan membuat perintah dan memanggilnya dari dialog air terjun. Untuk contoh cara menulis perintah Anda sendiri tanpa menggunakan pustaka dialog, lihat artikel Membuat permintaan Anda sendiri untuk mengumpulkan input pengguna.

Catatan

Bot Framework JavaScript, C#, dan Python SDK akan terus didukung, namun, Java SDK dihentikan dengan dukungan jangka panjang akhir yang berakhir pada November 2023.

Bot yang ada yang dibangun dengan Java SDK akan terus berfungsi.

Untuk pembuatan bot baru, pertimbangkan untuk menggunakan Microsoft Copilot Studio dan baca tentang memilih solusi salinan yang tepat.

Untuk informasi selengkapnya, lihat Masa depan pembuatan bot.

Prasyarat

Tentang sampel ini

Sampel perintah multi-giliran menggunakan dialog air terjun, beberapa perintah, dan dialog komponen untuk membuat interaksi linier yang mengajukan serangkaian pertanyaan kepada pengguna. Kode menggunakan dialog untuk menelusuri langkah-langkah berikut:

Langkah-langkah Jenis permintaan
Tanyakan kepada pengguna tentang moda transportasi mereka Perintah pilihan
Tanyakan nama pengguna Perintah teks
Tanyakan kepada pengguna apakah mereka ingin memberikan usia mereka Konfirmasi permintaan
Jika mereka menjawab ya, mintalah usia mereka Perintah angka, dengan validasi untuk hanya menerima usia yang lebih besar dari 0 dan kurang dari 150
Jika mereka tidak menggunakan Microsoft Teams, minta gambar profil kepada mereka Prompt lampiran, dengan validasi untuk mengizinkan lampiran yang hilang
Tanyakan apakah informasi yang dikumpulkan "ok" Gunakan kembali perintah Konfirmasi

Akhirnya, jika mereka menjawab ya, tampilkan informasi yang dikumpulkan; jika tidak, beri tahu pengguna bahwa informasi mereka tidak akan disimpan.

Membuat dialog utama

Untuk menggunakan dialog, instal paket NuGet Microsoft.Bot.Builder.Dialogs .

Bot berinteraksi dengan pengguna melalui UserProfileDialog. Saat membuat kelas bot DialogBot , UserProfileDialog diatur sebagai dialog utamanya. Bot kemudian menggunakan metode pembantu Run untuk mengakses dialog.

Diagram kelas untuk sampel C#.

Dialog\UserProfileDialog.cs

Mulailah dengan membuat UserProfileDialog yang berasal dari ComponentDialog kelas , dan memiliki tujuh langkah.

UserProfileDialog Di konstruktor, buat langkah-langkah air terjun, perintah dan dialog air terjun, dan tambahkan ke kumpulan dialog. Perintah harus berada dalam kumpulan dialog yang sama di mana mereka digunakan.

public UserProfileDialog(UserState userState)
    : base(nameof(UserProfileDialog))
{
    _userProfileAccessor = userState.CreateProperty<UserProfile>("UserProfile");

    // This array defines how the Waterfall will execute.
    var waterfallSteps = new WaterfallStep[]
    {
        TransportStepAsync,
        NameStepAsync,
        NameConfirmStepAsync,
        AgeStepAsync,
        PictureStepAsync,
        SummaryStepAsync,
        ConfirmStepAsync,
    };

    // Add named dialogs to the DialogSet. These names are saved in the dialog state.
    AddDialog(new WaterfallDialog(nameof(WaterfallDialog), waterfallSteps));
    AddDialog(new TextPrompt(nameof(TextPrompt)));
    AddDialog(new NumberPrompt<int>(nameof(NumberPrompt<int>), AgePromptValidatorAsync));
    AddDialog(new ChoicePrompt(nameof(ChoicePrompt)));
    AddDialog(new ConfirmPrompt(nameof(ConfirmPrompt)));
    AddDialog(new AttachmentPrompt(nameof(AttachmentPrompt), PicturePromptValidatorAsync));

    // The initial child Dialog to run.
    InitialDialogId = nameof(WaterfallDialog);
}

Selanjutnya, tambahkan langkah-langkah yang digunakan dialog untuk meminta input. Untuk menggunakan perintah, panggil dari langkah dalam dialog Anda dan ambil hasil perintah dalam langkah berikut menggunakan stepContext.Result. Di balik layar, perintah adalah dialog dua langkah. Pertama, perintah meminta input. Kemudian mengembalikan nilai yang valid, atau dimulai kembali dari awal dengan proses ulang hingga menerima input yang valid.

Anda harus selalu mengembalikan non-null DialogTurnResult dari langkah air terjun. Jika tidak, dialog Anda mungkin tidak berfungsi seperti yang dirancang. Ditunjukkan di bawah ini adalah implementasi untuk NameStepAsync dalam dialog air terjun.

private static async Task<DialogTurnResult> NameStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
    stepContext.Values["transport"] = ((FoundChoice)stepContext.Result).Value;

    return await stepContext.PromptAsync(nameof(TextPrompt), new PromptOptions { Prompt = MessageFactory.Text("Please enter your name.") }, cancellationToken);
}

Dalam AgeStepAsync, tentukan permintaan coba lagi ketika input pengguna gagal divalidasi, baik karena dalam format yang tidak dapat diurai oleh perintah, atau input gagal dalam kriteria validasi. Dalam hal ini, jika tidak ada permintaan coba lagi yang disediakan, perintah akan menggunakan teks perintah awal untuk memprompexi ulang pengguna untuk input.

private async Task<DialogTurnResult> AgeStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
    if ((bool)stepContext.Result)
    {
        // User said "yes" so we will be prompting for the age.
        // WaterfallStep always finishes with the end of the Waterfall or with another dialog; here it is a Prompt Dialog.
        var promptOptions = new PromptOptions
        {
            Prompt = MessageFactory.Text("Please enter your age."),
            RetryPrompt = MessageFactory.Text("The value entered must be greater than 0 and less than 150."),
        };

        return await stepContext.PromptAsync(nameof(NumberPrompt<int>), promptOptions, cancellationToken);
    }
    else
    {
        // User said "no" so we will skip the next step. Give -1 as the age.
        return await stepContext.NextAsync(-1, cancellationToken);
    }
}

UserProfile.cs

Mode transportasi, nama, dan usia pengguna disimpan dalam instans UserProfile kelas.

public class UserProfile
{
    public string Transport { get; set; }

    public string Name { get; set; }

    public int Age { get; set; }

    public Attachment Picture { get; set; }
}

Dialog\UserProfileDialog.cs

Pada langkah terakhir, periksa yang stepContext.Result dikembalikan oleh dialog yang disebut di langkah air terjun sebelumnya. Jika nilai pengembalian benar, aksesor profil pengguna mendapatkan dan memperbarui profil pengguna. Untuk mendapatkan profil pengguna, panggil GetAsync lalu atur nilai userProfile.Transportproperti , , userProfile.NameuserProfile.Age dan userProfile.Picture . Terakhir, ringkas informasi untuk pengguna sebelum memanggil EndDialogAsync, yang mengakhiri dialog. Mengakhiri dialog akan memunculkannya dari tumpukan dialog dan mengembalikan hasil opsional ke induk dialog. Induk adalah dialog atau metode yang memulai dialog yang baru saja berakhir.

    else
    {
        msg += $" Your profile will not be kept.";
    }

    await stepContext.Context.SendActivityAsync(MessageFactory.Text(msg), cancellationToken);

    // WaterfallStep always finishes with the end of the Waterfall or with another dialog; here it is the end.
    return await stepContext.EndDialogAsync(cancellationToken: cancellationToken);
}

private async Task<DialogTurnResult> SummaryStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
    stepContext.Values["picture"] = ((IList<Attachment>)stepContext.Result)?.FirstOrDefault();

    // Get the current profile object from user state.
    var userProfile = await _userProfileAccessor.GetAsync(stepContext.Context, () => new UserProfile(), cancellationToken);

    userProfile.Transport = (string)stepContext.Values["transport"];
    userProfile.Name = (string)stepContext.Values["name"];
    userProfile.Age = (int)stepContext.Values["age"];
    userProfile.Picture = (Attachment)stepContext.Values["picture"];

    var msg = $"I have your mode of transport as {userProfile.Transport} and your name as {userProfile.Name}";

    if (userProfile.Age != -1)
    {
        msg += $" and your age as {userProfile.Age}";
    }

    msg += ".";

    await stepContext.Context.SendActivityAsync(MessageFactory.Text(msg), cancellationToken);

    if (userProfile.Picture != null)
    {
        try
        {
            await stepContext.Context.SendActivityAsync(MessageFactory.Attachment(userProfile.Picture, "This is your profile picture."), cancellationToken);
        }
        catch
        {
            await stepContext.Context.SendActivityAsync(MessageFactory.Text("A profile picture was saved but could not be displayed here."), cancellationToken);

Jalankan dialog

Bot\DialogBot.cs

Handler OnMessageActivityAsync menggunakan RunAsync metode untuk memulai atau melanjutkan dialog. OnTurnAsync menggunakan objek manajemen status bot untuk mempertahankan perubahan status apa pun pada penyimpanan. Metode ini ActivityHandler.OnTurnAsync memanggil berbagai metode penanganan aktivitas, seperti OnMessageActivityAsync. Dengan cara ini, status disimpan setelah handler pesan selesai tetapi sebelum giliran itu sendiri selesai.

public override async Task OnTurnAsync(ITurnContext turnContext, CancellationToken cancellationToken = default)
{
    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);
}

Mendaftarkan layanan untuk bot

Bot ini menggunakan layanan berikut:

  • Layanan dasar untuk bot: penyedia info masuk, adaptor, dan implementasi bot.
  • Layanan untuk mengelola status: penyimpanan, status pengguna, dan status percakapan.
  • Dialog yang akan digunakan bot.

Startup.cs

Daftarkan layanan untuk bot di Startup. Layanan ini tersedia untuk bagian lain dari kode melalui injeksi dependensi.

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

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

Catatan

Penyimpanan memori hanya digunakan untuk tujuan pengujian dan tidak ditujukan untuk penggunaan produksi. Pastikan untuk menggunakan jenis penyimpanan persisten untuk bot produksi.

Menguji bot

  1. Jika Anda belum melakukannya, instal Bot Framework Emulator.
  2. Jalankan sampel secara lokal di komputer Anda.
  3. Mulai Emulator, sambungkan ke bot Anda, dan kirim pesan seperti yang ditunjukkan di bawah ini.

Contoh transkrip percakapan dengan bot prompt multi-giliran.

Informasi Tambahan

Tentang dialog dan status bot

Dalam bot ini, dua aksesor properti status didefinisikan:

  • Satu dibuat dalam status percakapan untuk properti status dialog. Status dialog melacak tempat pengguna berada dalam dialog kumpulan dialog, dan diperbarui oleh konteks dialog, seperti ketika dialog mulai atau lanjutkan metode dialog dipanggil.
  • Satu dibuat dalam status pengguna untuk properti profil pengguna. Bot menggunakan ini untuk melacak informasi yang dimilikinya tentang pengguna, dan Anda harus secara eksplisit mengelola status ini dalam kode dialog.

Metode dapatkan dan atur aksesor properti status mendapatkan dan mengatur nilai properti dalam cache objek manajemen status. Cache diisi pertama kali nilai properti status diminta secara bergantian, tetapi harus dipertahankan secara eksplisit. Untuk mempertahankan perubahan pada kedua properti status ini, panggilan ke metode simpan perubahan , dari objek manajemen status yang sesuai, dilakukan.

Sampel ini memperbarui status profil pengguna dari dalam dialog. Praktik ini dapat berfungsi untuk beberapa bot, tetapi tidak akan berfungsi jika Anda ingin menggunakan kembali dialog di seluruh bot.

Ada berbagai opsi untuk menjaga langkah-langkah dialog dan status bot terpisah. Misalnya, setelah dialog mengumpulkan informasi lengkap, Anda dapat:

  • Gunakan metode dialog akhir untuk menyediakan data yang dikumpulkan sebagai nilai pengembalian kembali ke konteks induk. Ini bisa menjadi handler giliran bot atau dialog aktif sebelumnya pada tumpukan dialog dan itu adalah bagaimana kelas prompt dirancang.
  • Buat permintaan ke layanan yang sesuai. Ini mungkin berfungsi dengan baik jika bot Anda bertindak sebagai ujung depan ke layanan yang lebih besar.

Definisi metode validator perintah

UserProfileDialog.cs

Di bawah ini adalah contoh kode validator untuk AgePromptValidatorAsync definisi metode. promptContext.Recognized.Value berisi nilai yang diurai, yang merupakan bilangan bulat di sini untuk prompt angka. promptContext.Recognized.Succeeded menunjukkan apakah prompt dapat mengurai input pengguna atau tidak. Validator harus mengembalikan false untuk menunjukkan bahwa nilai tidak diterima dan dialog perintah harus memprompeksikan ulang pengguna; jika tidak, kembalikan true untuk menerima input dan kembali dari dialog prompt. Anda dapat mengubah nilai dalam validator per skenario Anda.

    }

    // WaterfallStep always finishes with the end of the Waterfall or with another dialog; here it is a Prompt Dialog.
    return await stepContext.PromptAsync(nameof(ConfirmPrompt), new PromptOptions { Prompt = MessageFactory.Text("Is this ok?") }, cancellationToken);
}

Langkah berikutnya