ダイアログ プロンプトを使用してユーザー入力を収集するGather user input using a dialog prompt

注意

このトピックは、最新リリースの SDK (v4) を対象としています。This topic is for the latest release of the SDK (v4). 前のバージョンの SDK (v3) のコンテンツは、こちらにあります。You can find content for the older version of the SDK (v3) here.

質問を投稿して情報を収集することは、ボットがユーザーとやり取りする主な手段の 1 つです。Gathering information by posing questions is one of the main ways a bot interacts with users. "ダイアログ" ライブラリを使うことで、質問が行いやすくなるだけでなく、応答が検証され、確実に特定のデータ型と一致するように、またはカスタム検証ルールを満たすようになります。The dialogs library makes it easy to ask questions, as well as validate the response to make sure it matches a specific data type or meets custom validation rules. このトピックでは、ウォーターフォール ダイアログからプロンプトを作成して呼び出す方法について詳しく説明します。This topic details how to create and call prompts from a waterfall dialog.

前提条件Prerequisites

プロンプトの使用Using prompts

ダイアログでは、ダイアログとプロンプトの両方が同じダイアログ セットに含まれている場合にのみ、プロンプトを使用できます。A dialog can use a prompt only if both the dialog and prompt are in the same dialog set. ダイアログ内の複数のステップおよび同じダイアログ セットの複数のダイアログで同じプロンプトを使用できます。You can use the same prompt in multiple steps within a dialog and in multiple dialogs in the same dialog set. ただし、初期化時にカスタム検証をプロンプトに関連付けます。However, you associate custom validation with a prompt at initialization time. 同じ種類のプロンプトに異なる検証を使用するには、そのプロンプトの種類のインスタンスを複数用意し、それぞれに独自の検証コードを使用する必要があります。To use different validation for the same type of prompt, you need multiple instances of the prompt type, each with its own validation code.

ダイアログの状態の状態プロパティ アクセサーを定義するDefine a state property accessor for the dialog state

この記事で使用されるダイアログ プロンプトのサンプルでは、ユーザーに対して予約情報を入力するよう求めます。The Dialog Prompt sample used in this article prompts the user for reservation information. パーティの人数と日付を管理するには、DialogPromptBot.cs ファイルで予約情報の内部クラスを定義します。To manage party size and date, we define an inner class for reservation information in the DialogPromptBot.cs file.

public class Reservation
{
    public int Size { get; set; }

    public string Date { get; set; }
}

次に、予約データの状態プロパティ アクセサーを追加します。Next, we add a state property accessor for the reservation data.

public class DialogPromptBotAccessors
{
    public DialogPromptBotAccessors(ConversationState conversationState)
    {
        ConversationState = conversationState ?? throw new ArgumentNullException(nameof(conversationState));
    }

    public static string DialogStateAccessorKey { get; } = "DialogPromptBotAccessors.DialogState";
    public static string ReservationAccessorKey { get; } = "DialogPromptBotAccessors.Reservation";

    public IStatePropertyAccessor<DialogState> DialogStateAccessor { get; set; }
    public IStatePropertyAccessor<DialogPromptBot.Reservation> ReservationAccessor { get; set; }

    public ConversationState ConversationState { get; }
}

Startup.cs で、アクセサーを設定するように ConfigureServices メソッドを更新します。In Startup.cs, we update ConfigureServices method to set the accessors.

public void ConfigureServices(IServiceCollection services)
{
    // ...

    // Create and register state accesssors.
    // Acessors created here are passed into the IBot-derived class on every turn.
    services.AddSingleton<BotAccessors>(sp =>
    {
        // ...

        // Create the custom state accessor.
        // State accessors enable other components to read and write individual properties of state.
        var accessors = new BotAccessors(conversationState)
        {
            DialogStateAccessor = conversationState.CreateProperty<DialogState>(DialogPromptBotAccessors.DialogStateAccessorKey),
            ReservationAccessor = conversationState.CreateProperty<DialogPromptBot.Reservation>(DialogPromptBotAccessors.ReservationAccessorKey),
        };

        return accessors;
    });
}

ダイアログ セットとプロンプトを作成するCreate a dialog set and prompts

一般的には、ボットを初期化するときに、プロンプトとダイアログを作成し、ダイアログ セットに追加します。In general, you create and add prompts and dialogs to your dialog set when you initialize your bot. ボットがユーザーからの入力を受け取ると、ダイアログ セットはプロンプトの ID を解決できます。The dialog set can then resolve the prompt's ID when the bot receives input from the user.

DialogPromptBot クラスで、ダイアログ、プロンプト、およびダイアログ セットの識別子を定義します。In the DialogPromptBot class define identifiers for dialogs, prompts, and dialog set.

private const string ReservationDialog = "reservationDialog";
private const string PartySizePrompt = "partyPrompt";
private const string LocationPrompt = "locationPrompt";
private const string ReservationDatePrompt = "reservationDatePrompt";

private readonly DialogSet _dialogSet;

ボットのコンストラクターで、ダイアログ セットを作成し、プロンプトと予約ダイアログを追加します。In the bot's constructor, create the dialog set, add the prompts, and add the reservation dialog. カスタム検証は、プロンプトを作成するときに追加します。検証機能は後で実装します。We include the custom validation when we create the prompts, and we will implement the validation functions later.

// The following code creates prompts and adds them to an existing dialog set. The DialogSet contains all the dialogs that can 
// be used at runtime. The prompts also references a validation method is not shown here.

public DialogPromptBot(DialogPromptBotAccessors accessors, ILoggerFactory loggerFactory)
{
   // ...

    // Create the dialog set and add the prompts, including custom validation.
    _dialogSet = new DialogSet(_accessors.DialogStateAccessor);
    _dialogSet.Add(new NumberPrompt<int>(PartySizePrompt, PartySizeValidatorAsync));
    _dialogSet.Add(new ChoicePrompt(LocationPrompt));
    _dialogSet.Add(new DateTimePrompt(ReservationDatePrompt, DateValidatorAsync));

    // Define the steps of the waterfall dialog and add it to the set.
    WaterfallStep[] steps = new WaterfallStep[]
    {
        PromptForPartySizeAsync,
        PromptForLocationAsync,
        PromptForReservationDateAsync,
        AcknowledgeReservationAsync,
    };
    _dialogSet.Add(new WaterfallDialog(ReservationDialog, steps));


}

ダイアログ ステップを実装するImplement dialog steps

メインのボット ファイルで、ウォーターフォール ダイアログの各ステップを実装します。In the main bot file, we implement each of our steps of the waterfall dialog. プロンプトが追加されたら、ウォーターフォール ダイアログの 1 つのステップでプロンプトを呼び出し、次のダイアログ ステップでプロンプトの結果を取得します。After a prompt is added, we call it in one step of a waterfall dialog, and get the prompt result in the following dialog step. ウォーターフォール ステップ内からプロンプトを呼び出すには、"ウォーターフォール ステップ コンテキスト" オブジェクトの prompt メソッドを呼び出します。To call a prompt from within a waterfall step, call the waterfall step context object's prompt method. 最初のパラメーターは、使用するプロンプトの ID です。2 番目のパラメーターには、プロンプトのオプション (ユーザーに入力を求める際に使用するテキストなど) が含まれます。The first parameter is the ID of the prompt to use, and the second parameter contains the options for the prompt, such as the text used to ask the user for input.

DialogPromptBot.cs ファイルで、ウォーターフォール ダイアログの PromptForPartySizeAsyncPromptForLocationAsyncPromptForReservationDateAsyncAcknowledgeReservationAsync の各ステップを実装します。In the DialogPromptBot.cs file, we implement the PromptForPartySizeAsync, PromptForLocationAsync, PromptForReservationDateAsync, and AcknowledgeReservationAsync steps of the waterfall dialog.

ここでは、PromptForPartySizeAsyncPromptForLocationAsync のみを示しています。これらは、ウォーターフォール ダイアログの 2 つの連続するステップのデリゲートです。Here we are only showing PromptForPartySizeAsync and PromptForLocationAsync that are two consecutive step delegates of a waterfall dialog.

private async Task<DialogTurnResult> PromptForPartySizeAsync(WaterfallStepContext stepContext)
{
    // Prompt for the party size. The result of the prompt is returned to the next step of the waterfall.
    // If the input is not valid, the prompt is restarted, causing it to reprompt for input
    // and this set of steps is repeated next turn. Otherwise, the prompt ends and returns a _dialog turn result_ object 
    // to the parent dialog. Control passes to the next step of your waterfall dialog, with the result of the prompt 
    // available in the waterfall step context's _result_ property.
    return await stepContext.PromptAsync(
        PartySizePrompt,
        new PromptOptions
        {
            Prompt = MessageFactory.Text("How many people is the reservation for?"),
            RetryPrompt = MessageFactory.Text("How large is your party?"),
        },
        cancellationToken);
}

private async Task<DialogTurnResult> PromptForLocationAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
    // Record the party size information in the current dialog state.
    int size = (int)stepContext.Result;
    stepContext.Values["size"] = size;

    return await stepContext.PromptAsync(
        "locationPrompt",
        new PromptOptions
        {
            Prompt = MessageFactory.Text("Please choose a location."),
            RetryPrompt = MessageFactory.Text("Sorry, please choose a location from the list."),
            Choices = ChoiceFactory.ToChoices(new List<string> { "Redmond", "Bellevue", "Seattle" }),
        },
        cancellationToken);
}

前述の例は、3 つのプロパティをすべて指定して、選択プロンプトを使用する方法を示しています。The example above shows how to use a choice prompt, providing all three properties. PromptForLocationAsync メソッドは、ウォーターフォール ダイアログのステップとして使用されます。ダイアログ セットには、ウォーターフォール ダイアログと、locationPrompt という ID の選択プロンプトが含まれます。The PromptForLocationAsync method is used as a step in a waterfall dialog, and our dialog set contains both the waterfall dialog and a choice prompt with an ID of locationPrompt.

prompt メソッドの 2 番目のパラメーターは、prompt options オブジェクトを受け取ります。このオブジェクトには、次のプロパティがあります。The second parameter of the prompt method takes a prompt options object, which has the following properties.

プロパティProperty 説明Description
promptprompt 入力を求めるためにユーザーに送信する最初のアクティビティ。The initial activity to send the user, to ask for their input.
retry promptretry prompt 最初の入力が有効ではなかった場合にユーザーに送信するアクティビティ。The activity to send the user if their first input did not validate.
choiceschoices ユーザーが選択できる選択肢のリスト。選択プロンプトで使用されます。A list of choices for the user to choose from, for use with a choice prompt.

一般に、prompt プロパティと retry prompt プロパティはアクティビティですが、さまざまなプログラミング言語でこれを処理する方法にいくつかのバリエーションがあります。In general, the prompt and retry prompt properties are activities, though there is some variation on how this is handled in different programming languages.

ユーザーに送信する最初のプロンプト アクティビティは常に指定する必要があります。You should always specify the initial prompt activity to send the user.

再試行プロンプトを指定すると、ユーザーの入力が、プロンプトで解析できない形式であるか (数値プロンプトに対して「tomorrow」と入力された場合など)、検証基準を満たしていないために、検証に失敗する場合に役立ちます。Specifying a retry prompt is useful for when the user's input fails to validate, either because it is in a format that the prompt can not parse, such as "tomorrow" for a number prompt, or the input fails a validation criteria. 再試行プロンプトが指定されていない場合、プロンプトでは、最初のプロンプト アクティビティを使用してユーザーに再度入力を求めます。In this case, if no retry prompt was provided, the prompt will use the initial prompt activity to re-prompt the user for input.

選択プロンプトの場合、使用可能な選択肢のリストを常に提供する必要があります。For a choice prompt, you should always provide the list of available choices.

カスタム検証Custom validation

ウォーターフォールの次のステップに値を返す前にプロンプトの応答を検証できます。You can validate a prompt response before returning the value to the next step of the waterfall. 検証関数は、prompt validator context パラメーターを持ち、入力が検証に合格したかどうかを示すブール値を返します。A validator function has a prompt validator context parameter and returns a Boolean, indicating whether the input passes validation.

prompt validator context には、次のプロパティが含まれます。The prompt validator context includes the following properties:

プロパティProperty 説明Description
コンテキストContext ボットの現在のターン コンテキスト。The current turn context for the bot.
RecognizedRecognized プロンプト認識エンジンの結果。認識エンジンによって処理された、ユーザー入力に関する情報が含まれます。A prompt recognizer result that contains information about the user input, as processed by the recognizer.

プロンプト認識エンジンの結果には、次のプロパティがあります。The prompt recognizer result has the following properties:

プロパティProperty 説明Description
成功Succeeded 認識エンジンが入力を解析できたかどうかを示します。Indicates whether the recognizer was able to parse the input.
Value 認識エンジンからの戻り値。The return value from the recognizer. 必要に応じて、検証コードでこの値を変更できます。If necessary, the validation code can modify this value.

検証コードを実装するImplement validation code

パーティの人数の検証コントロールParty-size validator

予約を 6 人から 20 人のパーティに制限します。We limit reservations to parties of 6 to 20 people.

private async Task<bool> PartySizeValidatorAsync(
    PromptValidatorContext<int> promptContext,
    CancellationToken cancellationToken)
{
    // Check whether the input could be recognized as an integer.
    if (!promptContext.Recognized.Succeeded)
    {
        await promptContext.Context.SendActivityAsync(
            "I'm sorry, I do not understand. Please enter the number of people in your party.",
            cancellationToken: cancellationToken);
        return false;
    }

    // Check whether the party size is appropriate.
    int size = promptContext.Recognized.Value;
    if (size < 6 || size > 20)
    {
        await promptContext.Context.SendActivityAsync(
            "Sorry, we can only take reservations for parties of 6 to 20.",
            cancellationToken: cancellationToken);
        return false;
    }

    return true;
}

日時の検証Date time validation

予約日の検証コントロールで、予約時刻を現在の時刻から 1 時間以上後に制限します。In the reservation date validator, we limit reservations to an hour or more from the current time. 条件に一致する最初の解決を維持し、残りをクリアします。We are keeping the first resolution that matches our criteria, and clearing the rest. 以下の検証コードはすべてを網羅しているわけではなく、日付と時刻を解析する入力に適しています。The validation code below is not exhaustive, and it works best for input that parses to a date and time. ここで示しているのは、日時プロンプトを検証する場合のオプションです。実装は、ユーザーから収集しようとしている情報によって異なります。It does demonstrate some of the options for validating a date-time prompt, and your implementation will depend on what information you are trying to collect from the user.

// Validates whether the reservation date is appropriate.
// Reservations must be made at least an hour in advance.
private async Task<bool> DateValidatorAsync(
    PromptValidatorContext<IList<DateTimeResolution>> promptContext,
    CancellationToken cancellationToken = default(CancellationToken))
{
    // Check whether the input could be recognized as an integer.
    if (!promptContext.Recognized.Succeeded)
    {
        await promptContext.Context.SendActivityAsync(
            "I'm sorry, I do not understand. Please enter the date or time for your reservation.",
            cancellationToken: cancellationToken);
        return false;
    }

    // Check whether any of the recognized date-times are appropriate,
    // and if so, return the first appropriate date-time.
    DateTime earliest = DateTime.Now.AddHours(1.0);
    DateTimeResolution value = promptContext.Recognized.Value.FirstOrDefault(v =>
        DateTime.TryParse(v.Value ?? v.Start, out DateTime time) && DateTime.Compare(earliest,time) <= 0);
    if (value != null)
    {
        promptContext.Recognized.Value.Clear();
        promptContext.Recognized.Value.Add(value);
        return true;
    }

    await promptContext.Context.SendActivityAsync(
            "I'm sorry, we can't take reservations earlier than an hour from now.",
            cancellationToken: cancellationToken);
    return false;
}

日時プロンプトは、ユーザー入力に一致する使用可能な "日時解決" のリストまたは配列を返します。The date-time prompt returns a list or array of the possible date-time resolutions that match the user input. たとえば、9:00 は午前 9 時または午後 9 時を意味する可能性があり、Sunday もあいまいです。For example, 9:00 could mean 9 AM or 9 PM, and Sunday is also ambiguous. さらに、日時解決では、日付、時刻、日時、または範囲を表すことができます。In addition, a date-time resolution can represent a date, a time, a date-time, or a range. 日時プロンプトでは、Microsoft/Recognizers-Text を使用してユーザー入力を解析します。The date-time prompt uses the Microsoft/Recognizers-Text to parse the user input.

ターン ハンドラーを更新するUpdate the turn handler

ダイアログを開始し、完了時にダイアログからの戻り値を受け入れるように、ボットのターン ハンドラーを更新します。Update the bot's turn handler to start the dialog and accept a return value from the dialog when it completes. ここでは、ユーザーがボットと対話中であり、ボットにアクティブなウォーターフォール ダイアログが存在し、ダイアログの次のステップでプロンプトを使用することを想定しています。Here we assume the user is interacting with a bot, the bot has an active waterfall dialog, and the next step in the dialog uses a prompt.

  1. ユーザーがボットにメッセージを送信すると、次のことが行われます。When the user sends a message to the bot, it does the following:
    1. ボットのターン ハンドラーによって、ダイアログ コンテキストが作成され、continue メソッドが呼び出されます。The bot's turn handler creates a dialog context and call its continue method.
    2. アクティブなダイアログ (この例ではウォーターフォール ダイアログ) の次のステップに制御が移ります。Control passes to the next step in the active dialog, which in this case is your waterfall dialog.
    3. このステップで、ユーザーに入力を求める、ウォーターフォール ステップ コンテキストの prompt メソッドを呼び出します。The step calls its waterfall step context's prompt method to ask the user for input.
    4. ウォーターフォール ステップ コンテキストで、プロンプトがスタックにプッシュされて開始されます。The waterfall step context pushes the prompt onto the stack and starts it.
    5. プロンプトによって、入力を求めるアクティビティがユーザーに送信されます。The prompt sends an activity to the user to ask for their input.
  2. ユーザーがボットに次のメッセージを送信すると、次のことが行われます。When the user sends their next message to the bot, it does the following:
    1. ボットのターン ハンドラーによって、ダイアログ コンテキストが作成され、continue メソッドが呼び出されます。The bot's turn handler creates a dialog context and call its continue method.
    2. アクティブなダイアログの次のステップに制御が移ります。これがプロンプトの 2 番目のターンになります。Control passes to the next step in the active dialog, which is the second turn of the prompt.
    3. プロンプトによってユーザーの入力が検証されます。The prompt validates the user's input.

プロンプトの結果の処理Handling prompt results

プロンプトの結果の処理は、ユーザーにその情報を要求した理由によって異なります。What you do with the prompt result depends on why you requested the information from the user. 次のオプションがあります。Options include:

  • ユーザーが確認または選択プロンプトに応答する場合など、情報を使用してダイアログのフローを制御します。Use the information to control the flow of your dialog, such as when the user responds to a confirm or choice prompt.
  • 情報をダイアログの状態にキャッシュし (ウォーターフォール ステップ コンテキストの values プロパティの値の設定など)、ダイアログの終了時に収集した情報を返します。Cache the information in the dialog's state, such as setting a value in the waterfall step context's values property, and then return the collected information when the dialog ends.
  • 情報をボットの状態に保存します。Save the information to bot state. この場合、ボットの状態プロパティ アクセサーにアクセスできるようにダイアログを設計する必要があります。This would require you to design your dialog to have access to the bot's state property accessors.
public async Task OnTurnAsync(ITurnContext turnContext, CancellationToken cancellationToken = default(CancellationToken))
{
    switch (turnContext.Activity.Type)
    {
        // On a message from the user:
        case ActivityTypes.Message:

            // Get the current reservation info from state.
            Reservation reservation = await _accessors.ReservationAccessor.GetAsync(
                turnContext, () => null, cancellationToken);

            // Generate a dialog context for our dialog set.
            DialogContext dc = await _dialogSet.CreateContextAsync(turnContext, cancellationToken);

            if (dc.ActiveDialog is null)
            {
                // If there is no active dialog, check whether we have a reservation yet.
                if (reservation is null)
                {
                    // If not, start the dialog.
                    await dc.BeginDialogAsync(ReservationDialog, null, cancellationToken);
                }
                else
                {
                    // Otherwise, send a status message.
                    await turnContext.SendActivityAsync(
                        $"We'll see you {reservation.Date}.",
                        cancellationToken: cancellationToken);
                }
            }
            else
            {
                // Continue the dialog.
                DialogTurnResult dialogTurnResult = await dc.ContinueDialogAsync(cancellationToken);

                // If the dialog completed this turn, record the reservation info.
                if (dialogTurnResult.Status is DialogTurnStatus.Complete)
                {
                    reservation = (Reservation)dialogTurnResult.Result;
                    await _accessors.ReservationAccessor.SetAsync(
                        turnContext,
                        reservation,
                        cancellationToken);

                    // Send a confirmation message to the user.
                    await turnContext.SendActivityAsync(
                        $"Your party of {reservation.Size} is confirmed for {reservation.Date}.",
                        cancellationToken: cancellationToken);
                }
            }

            // Save the updated dialog state into the conversation state.
            await _accessors.ConversationState.SaveChangesAsync(turnContext, false, cancellationToken);
            break;

        // Handle other incoming activity types as appropriate to your bot.
        default:
            await turnContext.SendActivityAsync($"{turnContext.Activity.Type} event detected");
            break;
    }
}

同様の手法を使用し、あらゆる種類のプロンプトの回答を検証できます。You can use the similar techniques to validate prompt responses for any of the prompt types.

ボットをテストするTest your bot

  1. ご自身のマシンを使ってローカルでサンプルを実行します。Run the sample locally on your machine. 手順については、README ファイルで C# または JS を参照してください。If you need instructions, refer to the README file for C# or JS.
  2. エミュレーターを起動し、次に示すように、メッセージを送信してボットをテストします。Start the emulator, send messages as shown below to test the bot.

ダイアログ プロンプト サンプルをテストする

その他のリソースAdditional resources

お使いのターン ハンドラーからプロンプトを直接呼び出すには、C# または JSprompt-validations サンプルを参照してください。To call a prompt directly from your turn handler, see the prompt-validations sample in C# or JS.

ダイアログ ライブラリには、ユーザーの代わりに別のアプリケーションにアクセスするときに使用する "OAuth トークン" を取得するための "OAuth プロンプト" も含まれています。The dialog library also includes an OAuth prompt for obtaining an OAuth token with which to access another application on behalf of the user. 認証の詳細については、お使いのボットに認証を追加する方法に関する記事をご覧ください。For more about authentication, see how to add authentication to your bot.

次の手順Next steps