사용자 입력을 수집하기 위한 사용자 고유의 프롬프트 만들기

적용 대상: SDK v4

봇과 사용자 간의 대화에는 사용자에게 정보를 요청(확인)하고, 사용자의 응답을 구문 분석한 다음, 해당 정보에 대한 작업을 수행하는 경우가 많습니다. 봇은 대화의 컨텍스트를 추적하여 해당 동작을 관리하고 이전의 질문에 대한 답변을 기억할 수 있어야 합니다. 봇의 상태는 들어오는 메시지에 적절하게 응답하기 위해 추적하는 정보입니다.

대화 상자 라이브러리는 사용자가 사용할 수 있는 더 많은 기능을 제공하는 기본 제공 프롬프트를 제공합니다. 이러한 프롬프트의 예는 순차적 대화 흐름 구현 문서에서 찾을 수 있습니다.

참고 항목

Bot Framework JavaScript, C#및 Python SDK는 계속 지원되지만 Java SDK는 2023년 11월에 종료되는 최종 장기 지원으로 사용 중지됩니다.

Java SDK를 사용하여 빌드된 기존 봇은 계속 작동합니다.

새 봇 빌드의 경우 Power Virtual Agents 사용을 고려하고 올바른 챗봇 솔루션을 선택하는 방법을 읽어 보세요.

자세한 내용은 봇 빌드의 미래를 참조 하세요.

필수 조건

샘플 코드 정보

샘플 봇은 사용자에게 일련의 질문을 하고, 몇 가지 답변의 유효성을 검사하고, 입력을 저장합니다. 다음 다이어그램은 봇, 사용자 프로필 및 대화 흐름 클래스 간의 관계를 보여 줍니다.

C# 샘플에 대한 클래스 다이어그램.

  • UserProfile 봇이 수집할 사용자 정보에 대한 클래스입니다.
  • ConversationFlow 사용자 정보를 수집하는 동안 대화 상태를 제어하는 클래스입니다.
  • 대화에 있는 위치를 추적하기 위한 내부 ConversationFlow.Question 열거형입니다.

사용자 상태는 사용자의 이름, 연령 및 선택한 날짜를 추적하고 대화 상태는 사용자에게 마지막으로 요청한 내용을 추적합니다. 이 봇을 배포할 계획이 없으므로 메모리 스토리지를 사용하도록 사용자 및 대화 상태를 구성합니다.

봇의 메시지 턴 처리기와 사용자 및 대화 상태 속성을 사용하여 대화 흐름 및 입력 컬렉션을 관리합니다. 봇에서 메시지 턴 처리기를 반복할 때마다 받은 상태 속성 정보를 기록합니다.

대화 및 사용자 개체 만들기

시작할 때 사용자 및 대화 상태 개체를 만들고 봇 생성자의 종속성 주입을 통해 사용합니다.

Startup.cs

// 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.
services.AddSingleton<UserState>();

// Create the Conversation state.
services.AddSingleton<ConversationState>();

Bots/CustomPromptBot.cs

private readonly BotState _userState;
private readonly BotState _conversationState;

public CustomPromptBot(ConversationState conversationState, UserState userState)
{
    _conversationState = conversationState;
    _userState = userState;
}

속성 접근자 만들기

사용자 프로필 및 대화 흐름 속성에 대한 속성 접근자를 만든 다음, GetAsync을 호출하여 상태에서 속성 값을 검색합니다.

Bots/CustomPromptBot.cs

protected override async Task OnMessageActivityAsync(ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken)
{
    var conversationStateAccessors = _conversationState.CreateProperty<ConversationFlow>(nameof(ConversationFlow));
    var flow = await conversationStateAccessors.GetAsync(turnContext, () => new ConversationFlow(), cancellationToken);

    var userStateAccessors = _userState.CreateProperty<UserProfile>(nameof(UserProfile));
    var profile = await userStateAccessors.GetAsync(turnContext, () => new UserProfile(), cancellationToken);

턴이 끝나기 전에 스토리지에 상태 변경 내용을 기록하도록 호출 SaveChangesAsync 합니다.

    await _conversationState.SaveChangesAsync(turnContext, false, cancellationToken);
    await _userState.SaveChangesAsync(turnContext, false, cancellationToken);
}

메시지 턴 처리기

메시지 작업을 처리할 때 메시지 처리기는 도우미 메서드를 사용하여 대화를 관리하고 사용자에게 메시지를 표시합니다. 도우미 메서드는 다음 섹션에 설명되어 있습니다.

Bots/CustomPromptBot.cs

protected override async Task OnMessageActivityAsync(ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken)
{
    var conversationStateAccessors = _conversationState.CreateProperty<ConversationFlow>(nameof(ConversationFlow));
    var flow = await conversationStateAccessors.GetAsync(turnContext, () => new ConversationFlow(), cancellationToken);

    var userStateAccessors = _userState.CreateProperty<UserProfile>(nameof(UserProfile));
    var profile = await userStateAccessors.GetAsync(turnContext, () => new UserProfile(), cancellationToken);

    await FillOutUserProfileAsync(flow, profile, turnContext, cancellationToken);

    // Save changes.
    await _conversationState.SaveChangesAsync(turnContext, false, cancellationToken);
    await _userState.SaveChangesAsync(turnContext, false, cancellationToken);
}

사용자 프로필 작성

봇은 이전 턴에서 봇이 요청한 질문에 따라 사용자에게 정보를 묻는 메시지를 표시합니다. 입력은 유효성 검사 방법을 사용하여 구문 분석됩니다.

각 유효성 검사 메서드는 유사한 디자인을 따릅니다.

  • 반환 값은 입력이 이 질문에 대한 유효한 답변인지 여부를 나타냅니다.
  • 유효성 검사가 통과하면 저장할 구문 분석되고 정규화된 값이 생성됩니다.
  • 유효성 검사에 실패하면 봇이 정보를 다시 요청할 수 있는 메시지를 생성합니다.

유효성 검사 메서드는 다음 섹션에 설명되어 있습니다.

Bots/CustomPromptBot.cs

{
    var 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?", null, null, cancellationToken);
            flow.LastQuestionAsked = ConversationFlow.Question.Name;
            break;
        case ConversationFlow.Question.Name:
            if (ValidateName(input, out var name, out message))
            {
                profile.Name = name;
                await turnContext.SendActivityAsync($"Hi {profile.Name}.", null, null, cancellationToken);
                await turnContext.SendActivityAsync("How old are you?", null, null, cancellationToken);
                flow.LastQuestionAsked = ConversationFlow.Question.Age;
                break;
            }
            else
            {
                await turnContext.SendActivityAsync(message ?? "I'm sorry, I didn't understand that.", null, null, cancellationToken);
                break;
            }

        case ConversationFlow.Question.Age:
            if (ValidateAge(input, out var age, out message))
            {
                profile.Age = age;
                await turnContext.SendActivityAsync($"I have your age as {profile.Age}.", null, null, cancellationToken);
                await turnContext.SendActivityAsync("When is your flight?", null, null, cancellationToken);
                flow.LastQuestionAsked = ConversationFlow.Question.Date;
                break;
            }
            else
            {
                await turnContext.SendActivityAsync(message ?? "I'm sorry, I didn't understand that.", null, null, cancellationToken);
                break;
            }

        case ConversationFlow.Question.Date:
            if (ValidateDate(input, out var 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.", null, null, cancellationToken);
                break;
            }
    }
}

입력 구문 분석 및 유효성 검사

봇은 다음 조건을 사용하여 입력의 유효성을 검사합니다.

  • 이름은 비어있지 않은 문자열이어야 합니다. 공백을 트리밍하여 정규화됩니다.
  • 연령은 18세에서 120세 사이여야 합니다. 정수 반환을 통해 정규화됩니다.
  • 날짜나중에 적어도 1시간 이상 날짜 또는 시간이어야 합니다. 구문 분석된 입력의 날짜 부분만 반환하여 정규화합니다.

참고 항목

연령 및 날짜 입력의 경우 샘플은 Microsoft/Recognizers-Text 라이브러리를 사용하여 초기 구문 분석을 수행합니다. 이는 입력을 구문 분석하는 한 가지 방법입니다. 이러한 라이브러리에 대한 자세한 내용은 프로젝트의 추가 정보를 참조하세요.

Bots/CustomPromptBot.cs

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.

        var results = NumberRecognizer.RecognizeNumber(input, Culture.English);

        foreach (var result in results)
        {
            // The result resolution is a dictionary, where the "value" entry contains the processed string.
            if (result.Resolution.TryGetValue("value", out var 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
    {
        var 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.
        var earliest = DateTime.Now.AddHours(1.0);

        foreach (var result in results)
        {
            // The 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 var 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;
}

로컬로 봇 테스트

Bot Framework Emulator다운로드하여 설치하여 봇을 로컬로 테스트합니다.

  1. 머신에서 로컬로 샘플을 실행합니다. 지침이 필요한 경우 C# 샘플, JS 샘플 또는 Python 샘플대한 파일을 참조 README 하세요.
  2. 에뮬레이터를 사용하여 테스트합니다.

추가 리소스

대화 상자 라이브러리대화 관리의 여러 측면을 자동화하는 클래스를 제공합니다.

다음 단계