ユーザー入力を収集するために独自のプロンプトを作成するCreate your own prompts to gather user input

注意

このトピックは、最新リリースの 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.

多くの場合、ボットとユーザー間の会話では、ユーザーに情報の入力を求め、ユーザーの応答を解析し、その情報に基づいてアクションを実行する必要があります。A conversation between a bot and a user often involves asking (prompting) the user for information, parsing the user's response, and then acting on that information.

お使いのボットは会話のコンテキストを追跡する必要があります。これにより、自身の動作を管理し、以前の質問に対する回答を記憶することができます。Your bot should track the context of a conversation, so that it can manage its behavior and remember answers to previous questions. ボットの "状態" は、受信メッセージに適切に応答するためにボットが追跡する情報です。A bot's state is information it tracks to respond appropriately to incoming messages.

前提条件Prerequisites

サンプル コードについてAbout the sample code

この記事では、ユーザーに対して一連の質問を行い、その回答の一部を検証して、入力を保存します。In this article, we ask the user a series of questions, validate some of their answers, and save their input. 会話のフローと入力のコレクションの管理には、ボットのターン ハンドラーと、ユーザーおよび会話の状態プロパティを使用します。We use the bot's turn handler and user and conversation state properties to manage the flow of the conversation and the collection of input.

  1. 状態を定義して構成しますDefine and configure state
  2. 状態プロパティを使用して会話を制御しますUse state properties to direct the conversation
    1. ボットのターン ハンドラーを更新します。Update the bot's turn handler.
    2. ユーザー データのコレクションを管理するヘルパー メソッドを実装します。Implement a helper method to mange the collection of user data.
    3. ユーザー入力の検証メソッドを実装します。Implement validation methods for the user input.

状態を定義して構成するDefine and configure state

次の情報を追跡するようにボットを構成する必要があります。We need to configure our bot the track the following information:

  • ユーザーの名前、年齢、および選択した日付。これらはユーザー状態で定義する情報です。The user's name, age, and chosen date, which we'll define in user state.
  • ユーザーに質問した内容。これは会話状態で定義する情報です。What we've just asked the user, which we'll define in conversation state.

このボットはデプロイする予定がないため、"メモリ ストレージ" を使用するように、ユーザー状態と会話情報の両方を構成します。Since we don't plan to deploy this bot, we'll configure both user and conversation state to use memory storage. 構成コードの重要な側面についていくつか次に説明します。Below, we describe some key aspects of the configuration code.

次の種類を定義します。We define the following types.

  • ボットによって収集されるユーザー情報を表す UserProfile クラス。A UserProfile class for the user information that the bot will collect.
  • 会話の進行状況に関する情報を追跡するための ConversationFlow クラス。A ConversationFlow class to tack information about where we are in the conversation.
  • 会話の進行状況を追跡するための内部 ConversationFlow.Question 列挙型。An inner ConversationFlow.Question enumeration for tracking where we are in the conversation.
  • 状態管理情報をバンドルする CustomPromptBotAccessors クラス。A CustomPromptBotAccessors class in which to bundle the state management information.

ボット アクセサー クラスには、状態管理オブジェクトと状態プロパティ アクセサー オブジェクトが含まれています。このクラスは、ASP.NET Core の依存関係の挿入を介してボットに渡されます。The bot accessors class contains our state management and and state property accessor objects, and it gets passed to the bot via dependency injection in ASP.NET Core. ボットでは、各ターンの作成時に受信される状態プロパティ アクセサー情報を記録します。In our bot, we record the state property accessor information that you receive when the bot's created each turn.

状態プロパティを使用して会話を制御するUse state properties to direct the conversation

状態プロパティを構成したら、それをボットで使用できます。Once we have our state properties configured, we can use them in our bot.

ボットのターン ハンドラーThe bot's turn handler

状態プロパティ アクセサーを使用して、ターン コンテキストから状態プロパティを取得します。We use the state property accessors to get our state properties from the turn context. ユーザー プロファイルを入力する必要がある場合は、ヘルパー メソッドを呼び出して、状態の変更を保存します。If we need to fill out the user profile, we call our helper method and then save any state changes.

public async Task OnTurnAsync(ITurnContext turnContext, CancellationToken cancellationToken = default(CancellationToken))
{
   if (turnContext.Activity.Type == ActivityTypes.Message)
    {
        // Get the state properties from the turn context.
        ConversationFlow flow = await _accessors.ConversationFlowAccessor.GetAsync(turnContext, () => new ConversationFlow());
        UserProfile profile = await _accessors.UserProfileAccessor.GetAsync(turnContext, () => new UserProfile());

        await FillOutUserProfileAsync(flow, profile, turnContext);

        // Update state and save changes.
        await _accessors.ConversationFlowAccessor.SetAsync(turnContext, flow);
        await _accessors.ConversationState.SaveChangesAsync(turnContext);

        await _accessors.UserProfileAccessor.SetAsync(turnContext, profile);
        await _accessors.UserState.SaveChangesAsync(turnContext);
    }
}

ユーザー プロファイルを入力するFilling out the user profile

まず情報を収集します。We'll start by collecting information. それぞれに同様のインターフェイスが用意されています。Each one will provide a similar interface.

  • 戻り値は、入力が、この質問に対して有効な回答であるかどうか示しています。The return value indicates whether the input is a valid answer for this question.
  • 検証に合格すると、解析および正規化された値が生成され、保存されます。If validation passes, it produces a parsed and normalized value to save.
  • 検証に失敗した場合はメッセージが生成され、ボットはこれを使用して情報を再度求めることができます。If validation fails, it produces a message with which the bot can ask for the information again.

次のセクションでは、ユーザー入力を解析して検証するヘルパー メソッドを定義します。In the next section, we'll define the helper methods to parse and validate user input.

private static async Task FillOutUserProfileAsync(ConversationFlow flow, UserProfile profile, ITurnContext turnContext)
{
    string input = turnContext.Activity.Text?.Trim();
    string message;
    switch (flow.LastQuestionAsked)
    {
        case ConversationFlow.Question.None:
            await turnContext.SendActivityAsync("Let's get started. What is your name?");
            flow.LastQuestionAsked = ConversationFlow.Question.Name;
            break;
        case ConversationFlow.Question.Name:
            if (ValidateName(input, out string name, out message))
            {
                profile.Name = name;
                await turnContext.SendActivityAsync($"Hi {profile.Name}.");
                await turnContext.SendActivityAsync("How old are you?");
                flow.LastQuestionAsked = ConversationFlow.Question.Age;
                break;
            }
            else
            {
                await turnContext.SendActivityAsync(message ?? "I'm sorry, I didn't understand that.");
                break;
            }
        case ConversationFlow.Question.Age:
            if (ValidateAge(input, out int age, out message))
            {
                profile.Age = age;
                await turnContext.SendActivityAsync($"I have your age as {profile.Age}.");
                await turnContext.SendActivityAsync("When is your flight?");
                flow.LastQuestionAsked = ConversationFlow.Question.Date;
                break;
            }
            else
            {
                await turnContext.SendActivityAsync(message ?? "I'm sorry, I didn't understand that.");
                break;
            }
        case ConversationFlow.Question.Date:
            if (ValidateDate(input, out string date, out message))
            {
                profile.Date = date;
                await turnContext.SendActivityAsync($"Your cab ride to the airport is scheduled for {profile.Date}.");
                await turnContext.SendActivityAsync($"Thanks for completing the booking {profile.Name}.");
                await turnContext.SendActivityAsync($"Type anything to run the bot again.");
                flow.LastQuestionAsked = ConversationFlow.Question.None;
                profile = new UserProfile();
                break;
            }
            else
            {
                await turnContext.SendActivityAsync(message ?? "I'm sorry, I didn't understand that.");
                break;
            }
    }
}


入力を解析して検証するParse and validate input

次の条件を使用して、入力を検証します。We'll use the following criteria to validate input.

  • name は、空でない文字列にする必要があります。The name must be a non-empty string. 空白文字を削除することで正規化します。We'll normalize by trimming white-space.
  • age は、18 から 120 の値にする必要があります。The age must be between 18 and 120. 整数を返すことで正規化します。We'll normalize by returning an integer.
  • date は、1 時間以上未来の日付または時刻にする必要があります。The date must be any date or time at least an hour in the future. 解析された入力の日付部分のみを返すことで正規化します。We'll normalize by returning just the date portion of the parsed input.

注意

age と date の入力については、Microsoft/Recognizers-Text ライブラリを使用して、初期解析を実行します。For the age and date input, we use the Microsoft/Recognizers-Text libraries to perform the initial parsing. サンプル コードは提供されますが、テキスト認識エンジン ライブラリの動作方法については説明されていません。また、これは入力を解析する方法の一例にすぎません。While we provide sample code, we do not explain how the text recognizers libraries work, and this is just one way to parse the input. これらのライブラリの詳細については、リポジトリの README を参照してください。For more information about these libraries, see the repository's README.

次の検証メソッドをお使いのボットに追加します。Add the following validation methods to your bot.

private static bool ValidateName(string input, out string name, out string message)
{
    name = null;
    message = null;

    if (string.IsNullOrWhiteSpace(input))
    {
        message = "Please enter a name that contains at least one character.";
    }
    else
    {
        name = input.Trim();
    }

    return message is null;
}

private static bool ValidateAge(string input, out int age, out string message)
{
    age = 0;
    message = null;

    // Try to recognize the input as a number. This works for responses such as "twelve" as well as "12".
    try
    {
        // Attempt to convert the Recognizer result to an integer. This works for "a dozen", "twelve", "12", and so on.
        // The recognizer returns a list of potential recognition results, if any.
        List<ModelResult> results = NumberRecognizer.RecognizeNumber(input, Culture.English);
        foreach (var result in results)
        {
            // result.Resolution is a dictionary, where the "value" entry contains the processed string.
            if (result.Resolution.TryGetValue("value", out object value))
            {
                age = Convert.ToInt32(value);
                if (age >= 18 && age <= 120)
                {
                    return true;
                }
            }
        }

        message = "Please enter an age between 18 and 120.";
    }
    catch
    {
        message = "I'm sorry, I could not interpret that as an age. Please enter an age between 18 and 120.";
    }

    return message is null;
}

private static bool ValidateDate(string input, out string date, out string message)
{
    date = null;
    message = null;

    // Try to recognize the input as a date-time. This works for responses such as "11/14/2018", "9pm", "tomorrow", "Sunday at 5pm", and so on.
    // The recognizer returns a list of potential recognition results, if any.
    try
    {
        List<ModelResult> results = DateTimeRecognizer.RecognizeDateTime(input, Culture.English);

        // Check whether any of the recognized date-times are appropriate,
        // and if so, return the first appropriate date-time. We're checking for a value at least an hour in the future.
        DateTime earliest = DateTime.Now.AddHours(1.0);
        foreach (ModelResult result in results)
        {
            // result.Resolution is a dictionary, where the "values" entry contains the processed input.
            var resolutions = result.Resolution["values"] as List<Dictionary<string, string>>;
            foreach (var resolution in resolutions)
            {
                // The processed input contains a "value" entry if it is a date-time value, or "start" and
                // "end" entries if it is a date-time range.
                if (resolution.TryGetValue("value", out string dateString)
                    || resolution.TryGetValue("start", out dateString))
                {
                    if (DateTime.TryParse(dateString, out var candidate)
                        && earliest < candidate)
                    {
                        date = candidate.ToShortDateString();
                        return true;
                    }
                }
            }
        }

        message = "I'm sorry, please enter a date at least an hour out.";
    }
    catch
    {
        message = "I'm sorry, I could not interpret that as an appropriate date. Please enter a date at least an hour out.";
    }

    return false;
}

ボットをローカルでテストするTest the bot locally

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

primitive-prompts

その他のリソースAdditional resources

ダイアログ ライブラリには、会話の管理に関するさまざまな側面を自動化するクラスが用意されています。The Dialogs library provides classes that automate many aspects of managing conversations.

次のステップNext step