다음을 통해 공유


사용자 및 대화 데이터 저장

적용 대상: SDK v4

봇은 기본적으로 상태 비지정입니다. 봇이 배포되면 동일한 프로세스 또는 동일한 컴퓨터에서 한 턴에서 다음 턴으로 실행되지 않을 수 있습니다. 그러나 봇은 대화의 동작을 관리하고 이전 질문에 대한 답변을 기억할 수 있도록 대화의 컨텍스트를 추적해야 할 수 있습니다. Bot Framework SDK의 상태 및 스토리지 기능을 사용하면 봇에 상태를 추가할 수 있습니다. 봇은 상태 관리 및 스토리지 개체를 사용하여 상태를 관리하고 유지합니다. 상태 관리자는 기본 스토리지 유형과 관계없이 속성 접근자를 사용하여 상태 속성에 액세스할 수 있는 추상화 계층을 제공합니다.

참고 항목

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

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

새 봇 빌드의 경우 Microsoft Copilot Studio를 사용하고 올바른 부조종사 솔루션 선택에 대해 알아봅니다.

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

필수 조건

  • 봇 기본 사항 및 봇이 상태를 관리하는 방법에 대한 지식이 필요합니다.
  • 이 문서의 코드는 상태 관리 봇 샘플을 기반으로 합니다. C#, JavaScript, Java 또는 Python에서 샘플의 복사본이 필요합니다.

이 샘플 정보

사용자 입력을 수신할 때 이 샘플은 저장된 대화 상태를 확인하여 이 사용자에게 해당 사용자의 이름을 제공하라는 프롬프트를 표시했는지 확인합니다. 프롬프트를 표시하지 않은 경우 사용자의 이름을 요청하며 해당 입력은 사용자 상태 내에 저장됩니다. 이 경우 사용자 상태 내에 저장된 이름은 수신된 시간 및 입력 채널 ID와 함께 사용자 및 해당 입력 데이터와 대화하는 데 사용됩니다. 시간 및 채널 ID 값은 사용자 대화 데이터에서 검색된 다음 대화 상태로 저장됩니다. 다음 다이어그램은 봇, 사용자 프로필 및 대화 데이터 클래스 간의 관계를 보여줍니다.

클래스 정의

상태 관리를 설정하는 첫 번째 단계는 사용자 및 대화 상태에서 관리하려는 모든 정보를 포함하는 클래스를 정의하는 것입니다. 이 문서에 사용된 예제에서는 다음 클래스를 정의합니다.

  • UserProfile.cs 봇이 수집할 사용자 정보에 대한 클래스를 정의 UserProfile 합니다.
  • ConversationData.cs 사용자 정보를 수집하는 동안 대화 상태를 제어하는 클래스를 정의 ConversationData 합니다.

다음 코드 예제에서는 클래스 및 ConversationData 클래스에 UserProfile 대한 정의를 보여 줍니다.

UserProfile.cs

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

ConversationData.cs

public class ConversationData
{
    // The time-stamp of the most recent incoming message.
    public string Timestamp { get; set; }

    // The ID of the user's channel.
    public string ChannelId { get; set; }

    // Track whether we have already asked the user's name
    public bool PromptedUserForName { get; set; } = false;
}

대화 및 사용자 상태 개체 만들기

다음으로, 만들고 개체를 만드는 UserState ConversationState 데 사용되는 등록 MemoryStorage 을 합니다. 사용자 및 대화 상태 개체가 생성 Startup 되고 종속성이 봇 생성자에 삽입됩니다. 등록된 봇에 대한 다른 서비스는 자격 증명 공급자, 어댑터 및 봇 구현입니다.

Startup.cs

// {
//     TypeNameHandling = TypeNameHandling.All,
// var storage = new BlobsStorage("<blob-storage-connection-string>", "bot-state");

// With a custom JSON SERIALIZER, use this instead.
// var storage = new BlobsStorage("<blob-storage-connection-string>", "bot-state", jsonSerializer);

/* END AZURE BLOB STORAGE */

Bots/StateManagementBot.cs

private BotState _conversationState;
private BotState _userState;

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

상태 속성 접근자 추가

이제 개체에 대한 핸들 BotStateCreateProperty 제공하는 메서드를 사용하여 속성 접근자를 만듭니다. 각 상태 속성 접근자를 사용하면 연결된 상태 속성의 값을 가져오거나 설정할 수 있습니다. 상태 속성을 사용하기 전에 각 접근자를 사용하여 스토리지에서 속성을 로드하고 상태 캐시에서 가져옵니다. 상태 속성과 연결된 적절한 범위의 키를 얻으려면 메서드를 호출합니다 GetAsync .

Bots/StateManagementBot.cs

var conversationStateAccessors = _conversationState.CreateProperty<ConversationData>(nameof(ConversationData));
var userStateAccessors = _userState.CreateProperty<UserProfile>(nameof(UserProfile));

봇에서 상태 액세스

이전 섹션에서는 봇에 상태 속성 접근자를 추가하는 초기화 시간 단계를 설명합니다. 이제 런타임에 이러한 접근자를 사용하여 상태 정보를 읽고 쓸 수 있습니다. 아래 샘플 코드는 다음 논리 흐름을 사용합니다.

  • 비어 있고 conversationData.PromptedUserForName trueuserProfile.Name 제공된 사용자 이름을 검색하고 사용자 상태 내에 저장합니다.
  • 비어 있고 conversationData.PromptedUserForName falseuserProfile.Name 사용자의 이름을 요청합니다.
  • 이전에 저장된 경우 userProfile.Name 사용자 입력에서 메시지 시간 및 채널 ID를 검색하고, 모든 데이터를 사용자에게 다시 에코하고, 검색된 데이터를 대화 상태 내에 저장합니다.

Bots/StateManagementBot.cs

protected override async Task OnMessageActivityAsync(ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken)
{
    // Get the state properties from the turn context.

    var conversationStateAccessors = _conversationState.CreateProperty<ConversationData>(nameof(ConversationData));
    var conversationData = await conversationStateAccessors.GetAsync(turnContext, () => new ConversationData());

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

    if (string.IsNullOrEmpty(userProfile.Name))
    {
        // First time around this is set to false, so we will prompt user for name.
        if (conversationData.PromptedUserForName)
        {
            // Set the name to what the user provided.
            userProfile.Name = turnContext.Activity.Text?.Trim();

            // Acknowledge that we got their name.
            await turnContext.SendActivityAsync($"Thanks {userProfile.Name}. To see conversation data, type anything.");

            // Reset the flag to allow the bot to go through the cycle again.
            conversationData.PromptedUserForName = false;
        }
        else
        {
            // Prompt the user for their name.
            await turnContext.SendActivityAsync($"What is your name?");

            // Set the flag to true, so we don't prompt in the next turn.
            conversationData.PromptedUserForName = true;
        }
    }
    else
    {
        // Add message details to the conversation data.
        // Convert saved Timestamp to local DateTimeOffset, then to string for display.
        var messageTimeOffset = (DateTimeOffset)turnContext.Activity.Timestamp;
        var localMessageTime = messageTimeOffset.ToLocalTime();
        conversationData.Timestamp = localMessageTime.ToString();
        conversationData.ChannelId = turnContext.Activity.ChannelId.ToString();

        // Display state data.
        await turnContext.SendActivityAsync($"{userProfile.Name} sent: {turnContext.Activity.Text}");
        await turnContext.SendActivityAsync($"Message received at: {conversationData.Timestamp}");
        await turnContext.SendActivityAsync($"Message received from: {conversationData.ChannelId}");
    }
}

턴 처리기를 종료하기 전에 상태 관리 개체의 SaveChangesAsync() 메서드를 사용하여 모든 상태 변경 내용을 스토리지에 다시 씁니다.

Bots/StateManagementBot.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);
}

봇 테스트

  1. 최신 Bot Framework Emulator를 다운로드하고 설치합니다.
  2. 머신에서 로컬로 샘플을 실행합니다. 지침이 필요한 경우 C#, JavaScript, Java 또는 Python에 대한 추가 정보(README)를 참조하세요.
  3. 에뮬레이터를 사용하여 샘플 봇을 테스트합니다.

추가 정보

이 문서에서는 봇에 상태를 추가하는 방법을 설명했습니다. 관련 항목에 대한 자세한 내용은 다음 표를 참조하세요.

항목 주의
개인 정보 보호 사용자의 개인 데이터를 저장하려면 GDPR(일반 데이터 보호 규정)을 준수해야 합니다.
상태 관리 상태 관리 호출은 모두 비동기적이며, 기본적으로 최종 작성자 인정(last-writer-wins)입니다. 실제로 봇에서 상태를 가능한 한 가깝게 설정하고 저장해야 합니다. 낙관적 잠금을 구현하는 방법에 대한 자세한 내용은 봇에 대한 사용자 지정 스토리지 구현을 참조하세요.
중요한 비즈니스 데이터 봇 상태를 사용하여 기본 설정, 사용자 이름 또는 마지막으로 주문한 것을 저장하지만 중요한 비즈니스 데이터를 저장하는 데는 사용하지 마세요. 중요한 데이터의 경우 고유한 스토리지 구성 요소를 만들거나 스토리지직접 씁니다.
인식기 텍스트 이 샘플에서는 Microsoft/Recognizers-Text 라이브러리를 사용하여 사용자 입력을 구문 분석하고 유효성을 검사합니다. 자세한 내용은 개요 페이지를 참조하세요.

다음 단계

사용자에게 일련의 질문을 하고, 답변의 유효성을 검사하고, 입력을 저장하는 방법을 알아봅니다.