Share via


기술 소비자 구현

적용 대상: SDK v4

기술을 사용하여 다른 봇을 확장할 수 있습니다. 기술은 다른 봇에 대한 작업 집합을 수행하고 매니페스트를 사용하여 해당 인터페이스를 설명할 수 있는 봇입니다. 루트 봇은 하나 이상의 기술을 호출할 수 있는 사용자 연결 봇입니다. 루트 봇은 기술 소비자한 유형입니다.

  • 기술 소비자는 클레임 유효성 검사를 사용하여 액세스할 수 있는 기술을 관리해야 합니다.
  • 기술 소비자는 여러 기술을 사용할 수 있습니다.
  • 기술의 소스 코드에 액세스할 수 없는 개발자는 기술 매니페스트의 정보를 사용하여 기술 소비자를 디자인할 수 있습니다.

이 문서에서는 에코 기술을 사용하여 사용자의 입력을 에코하는 기술 소비자를 구현하는 방법을 보여 줍니다. 기술 매니페스트 샘플 및 에코 기술 구현에 대한 내용은 기술 구현 방법을 참조하세요.

기술 대화 상자를 사용하여 기술을 사용하는 방법에 대한 자세한 내용은 대화 상자를 사용하여 기술을 사용하는 방법을 참조하세요.

일부 유형의 기술 소비자는 일부 유형의 기술 봇을 사용할 수 없습니다. 다음 표에서는 지원되는 조합에 대해 설명합니다.

  다중 테넌트 기술 단일 테넌트 기술 사용자 할당 관리 ID 기술
다중 테넌트 소비자 지원 여부 지원되지 않음 지원되지 않음
단일 테넌트 소비자 지원되지 않음 두 앱이 동일한 테넌트에 속하는 경우 지원됨 두 앱이 동일한 테넌트에 속하는 경우 지원됨
사용자 할당 관리 ID 소비자 지원되지 않음 두 앱이 동일한 테넌트에 속하는 경우 지원됨 두 앱이 동일한 테넌트에 속하는 경우 지원됨

참고 항목

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

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

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

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

필수 조건

참고 항목

버전 4.11부터 Bot Framework Emulator에서 기술 소비자를 로컬로 테스트하기 위해 앱 ID와 암호가 필요하지 않습니다. Azure 구독은 여전히 소비자를 Azure에 배포하거나 배포된 기술을 사용하는 데 필요합니다.

이 샘플 정보

간단한 봇 간 샘플 기술에는 두 개의 봇 에 대한 프로젝트가 포함됩니다.

  • 기술을 구현하는 에코 스킬 봇입니다.
  • 단순 루트 봇 - 기술을 사용하는 루트 봇을 구현합니다.

이 문서에서는 봇 및 어댑터 개체의 지원 논리를 포함하고 활동을 기술과 교환하는 데 사용되는 개체를 포함하는 루트 봇에 중점을 둡니다. 여기에는 다음이 포함됩니다.

  • 기술로 활동을 보내는 데 사용되는 기술 클라이언트입니다.
  • 기술에서 활동을 수신하는 데 사용되는 기술 처리기입니다.
  • 기술 클라이언트와 처리기가 사용자 루트 대화 참조와 루트 기술 대화 참조 간에 변환하는 데 사용하는 기술 대화 ID 팩터리입니다.

에코 스킬 봇에 대한 자세한 내용은 기술을 구현하는 방법을 참조하세요.

리소스

배포된 봇의 경우 봇-봇 인증을 사용하려면 참여하는 각 봇에 유효한 ID 정보가 있어야 합니다. 그러나 앱 ID 및 암호 없이 에뮬레이터를 사용하여 로컬에서 다중 테넌트 기술 및 기술 소비자를 테스트할 수 있습니다.

애플리케이션 구성

  1. 필요에 따라 해당 구성 파일에 루트 봇의 ID 정보를 추가합니다. 기술 또는 기술 소비자가 ID 정보를 제공하는 경우 둘 다 필요합니다.
  2. 기술이 기술 소비자에게 회신해야 하는 기술 호스트 엔드포인트(서비스 또는 콜백 URL)를 추가합니다.
  3. 기술 소비자가 사용할 각 기술에 대한 항목을 추가합니다. 각 항목에는 다음이 포함됩니다.
    • 기술 소비자가 각 기술을 식별하는 데 사용할 ID입니다.
    • 필요에 따라 기술의 앱 또는 클라이언트 ID입니다.
    • 기술의 메시징 엔드포인트입니다.

참고 항목

기술 또는 기술 소비자가 ID 정보를 제공하는 경우 둘 다 필요합니다.

SimpleRootBot\appsettings.json

필요에 따라 루트 봇의 ID 정보를 추가하고 에코 기술 봇에 대한 앱 또는 클라이언트 ID를 추가합니다.

{
  "MicrosoftAppType": "",
  "MicrosoftAppId": "",
  "MicrosoftAppPassword": "",
  "MicrosoftAppTenantId": "",
  "SkillHostEndpoint": "http://localhost:3978/api/skills/",
  "BotFrameworkSkills": [
    {
      "Id": "EchoSkillBot",
      "AppId": "",
      "SkillEndpoint": "http://localhost:39783/api/messages"
    }
  ]
}

기술 구성

이 샘플은 구성 파일의 각 기술에 대한 정보를 기술 개체 컬렉션으로 읽습니다.

SimpleRootBot\SkillsConfiguration.cs

public class SkillsConfiguration
{
    public SkillsConfiguration(IConfiguration configuration)
    {
        var section = configuration?.GetSection("BotFrameworkSkills");
        var skills = section?.Get<BotFrameworkSkill[]>();
        if (skills != null)
        {
            foreach (var skill in skills)
            {
                Skills.Add(skill.Id, skill);
            }
        }

        var skillHostEndpoint = configuration?.GetValue<string>(nameof(SkillHostEndpoint));
        if (!string.IsNullOrWhiteSpace(skillHostEndpoint))
        {
            SkillHostEndpoint = new Uri(skillHostEndpoint);
        }
    }

    public Uri SkillHostEndpoint { get; }

    public Dictionary<string, BotFrameworkSkill> Skills { get; } = new Dictionary<string, BotFrameworkSkill>();
}

대화 ID 팩터리

이렇게 하면 기술에 사용할 대화 ID가 생성되고 기술 대화 ID에서 원래 사용자 대화 ID를 복구할 수 있습니다.

이 샘플의 대화 ID 팩터리는 다음과 같은 간단한 시나리오를 지원합니다.

  • 루트 봇은 하나의 특정한 기술을 사용하도록 설계되었습니다.
  • 루트 봇에는 한 번에 하나의 기술로 활성 대화가 하나만 있습니다.

SDK는 소스 코드를 복제본(replica) 요구하지 않고도 모든 기술에서 사용할 수 있는 클래스를 제공합니다SkillConversationIdFactory. 대화 ID 팩터리는 Startup.cs 구성됩니다.

더 복잡한 시나리오를 지원하려면 대화 ID 팩터리를 다음과 같이 설계합니다.

  • 기술 대화 ID 만들기 메서드는 적절한 기술 대화 ID를 가져오거나 생성합니다.
  • 대화 참조 가져오기 메서드는 올바른 사용자 대화를 가져옵니다.

기술 클라이언트 및 기술 처리기

기술 소비자는 기술 클라이언트를 사용하여 활동을 기술에 전달합니다. 클라이언트는 기술 구성 정보 및 대화 ID 팩터리를 사용하여 이 작업을 수행합니다.

기술 소비자는 기술 처리기를 사용하여 기술로부터 활동을 받습니다. 처리기는 대화 ID 팩터리, 인증 구성 및 자격 증명 공급자를 사용하여 이 작업을 수행하며 루트 봇의 어댑터 및 작업 처리기에 대한 종속성도 있습니다.

SimpleRootBot\Startup.cs

services.AddSingleton<IBotFrameworkHttpAdapter>(sp => sp.GetService<CloudAdapter>());
services.AddSingleton<BotAdapter>(sp => sp.GetService<CloudAdapter>());

기술의 HTTP 트래픽은 기술 소비자가 기술에 보급하는 서비스 URL 엔드포인트로 이동합니다. 언어별 엔드포인트 처리기를 사용하여 트래픽을 기술 처리기로 전달합니다.

기본 기술 처리기:

  • 앱 ID와 암호가 있는 경우 인증 구성 개체를 사용하여 봇-봇 인증 및 클레임 유효성 검사를 모두 수행합니다.
  • 대화 ID 팩터리를 사용하여 소비자-기술 대화에서 루트-사용자 대화로 다시 변환합니다.
  • 기술 소비자가 루트 사용자 턴 컨텍스트를 다시 설정하고 사용자에게 활동을 전달할 수 있도록 사전 대응 메시지를 생성합니다.

활동 처리기 논리

참고로, 기술 소비자 논리는 다음과 같습니다.

  • 활성 기술이 있는지 여부를 기억하고 활동을 적절하게 전달합니다.
  • 사용자가 기술로 전달해야 하는 요청을 수행하고 기술을 시작하는 경우를 확인합니다.
  • 모든 활성 기술에서 endOfConversation 활동을 찾아서 완료 시점을 확인합니다.
  • 해당되는 경우 사용자 또는 기술 소비자에서 아직 완료되지 않은 기술을 취소할 수 있는 논리를 추가합니다.
  • 모든 응답이 기술 소비자의 다른 인스턴스로 돌아올 수 있으므로 기술을 호출하기 전에 상태를 저장합니다.

SimpleRootBot\Bots\RootBot.cs

루트 봇에는 대화 상태, 기술 정보, 기술 클라이언트 및 일반 구성에 대한 종속성이 있습니다. ASP.NET은 종속성 주입을 통해 이러한 개체를 제공합니다. 또한 루트 봇은 활성 상태인 기술을 추적하는 대화 상태 속성 접근자를 정의합니다.

public static readonly string ActiveSkillPropertyName = $"{typeof(RootBot).FullName}.ActiveSkillProperty";
private readonly IStatePropertyAccessor<BotFrameworkSkill> _activeSkillProperty;
private readonly string _botId;
private readonly ConversationState _conversationState;
private readonly BotFrameworkAuthentication _auth;
private readonly SkillConversationIdFactoryBase _conversationIdFactory;
private readonly SkillsConfiguration _skillsConfig;
private readonly BotFrameworkSkill _targetSkill;

public RootBot(BotFrameworkAuthentication auth, ConversationState conversationState, SkillsConfiguration skillsConfig, SkillConversationIdFactoryBase conversationIdFactory, IConfiguration configuration)
{
    _auth = auth ?? throw new ArgumentNullException(nameof(auth));
    _conversationState = conversationState ?? throw new ArgumentNullException(nameof(conversationState));
    _skillsConfig = skillsConfig ?? throw new ArgumentNullException(nameof(skillsConfig));
    _conversationIdFactory = conversationIdFactory ?? throw new ArgumentNullException(nameof(conversationIdFactory));

    if (configuration == null)
    {
        throw new ArgumentNullException(nameof(configuration));
    }

    _botId = configuration.GetSection(MicrosoftAppCredentials.MicrosoftAppIdKey)?.Value;

    // We use a single skill in this example.
    var targetSkillId = "EchoSkillBot";
    _skillsConfig.Skills.TryGetValue(targetSkillId, out _targetSkill);

    // Create state property to track the active skill
    _activeSkillProperty = conversationState.CreateProperty<BotFrameworkSkill>(ActiveSkillPropertyName);
}

이 샘플에는 활동을 기술에 전달하는 도우미 메서드가 있습니다. 기술을 호출하기 전에 대화 상태를 저장하고 HTTP 요청이 성공했는지 여부를 검사.

private async Task SendToSkill(ITurnContext turnContext, BotFrameworkSkill targetSkill, CancellationToken cancellationToken)
{
    // NOTE: Always SaveChanges() before calling a skill so that any activity generated by the skill
    // will have access to current accurate state.
    await _conversationState.SaveChangesAsync(turnContext, force: true, cancellationToken: cancellationToken);

    // Create a conversationId to interact with the skill and send the activity
    var options = new SkillConversationIdFactoryOptions
    {
        FromBotOAuthScope = turnContext.TurnState.Get<string>(BotAdapter.OAuthScopeKey),
        FromBotId = _botId,
        Activity = turnContext.Activity,
        BotFrameworkSkill = targetSkill
    };
    var skillConversationId = await _conversationIdFactory.CreateSkillConversationIdAsync(options, cancellationToken);

    using var client = _auth.CreateBotFrameworkClient();

    // route the activity to the skill
    var response = await client.PostActivityAsync(_botId, targetSkill.AppId, targetSkill.SkillEndpoint, _skillsConfig.SkillHostEndpoint, skillConversationId, turnContext.Activity, cancellationToken);

    // Check response status
    if (!(response.Status >= 200 && response.Status <= 299))
    {
        throw new HttpRequestException($"Error invoking the skill id: \"{targetSkill.Id}\" at \"{targetSkill.SkillEndpoint}\" (status is {response.Status}). \r\n {response.Body}");
    }
}

참고로, 루트 봇에는 활동을 기술로 전달하고, 사용자의 요청에 따라 기술을 시작하고, 기술이 완료될 때 기술을 중지하는 논리가 포함되어 있습니다.

protected override async Task OnMessageActivityAsync(ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken)
{
    if (turnContext.Activity.Text.Contains("skill"))
    {
        await turnContext.SendActivityAsync(MessageFactory.Text("Got it, connecting you to the skill..."), cancellationToken);

        // Save active skill in state
        await _activeSkillProperty.SetAsync(turnContext, _targetSkill, cancellationToken);

        // Send the activity to the skill
        await SendToSkill(turnContext, _targetSkill, cancellationToken);
        return;
    }

    // just respond
    await turnContext.SendActivityAsync(MessageFactory.Text("Me no nothin'. Say \"skill\" and I'll patch you through"), cancellationToken);

    // Save conversation state
    await _conversationState.SaveChangesAsync(turnContext, force: true, cancellationToken: cancellationToken);
}

protected override async Task OnEndOfConversationActivityAsync(ITurnContext<IEndOfConversationActivity> turnContext, CancellationToken cancellationToken)
{
    // forget skill invocation
    await _activeSkillProperty.DeleteAsync(turnContext, cancellationToken);

    // Show status message, text and value returned by the skill
    var eocActivityMessage = $"Received {ActivityTypes.EndOfConversation}.\n\nCode: {turnContext.Activity.Code}";
    if (!string.IsNullOrWhiteSpace(turnContext.Activity.Text))
    {
        eocActivityMessage += $"\n\nText: {turnContext.Activity.Text}";
    }

    if ((turnContext.Activity as Activity)?.Value != null)
    {
        eocActivityMessage += $"\n\nValue: {JsonConvert.SerializeObject((turnContext.Activity as Activity)?.Value)}";
    }

    await turnContext.SendActivityAsync(MessageFactory.Text(eocActivityMessage), cancellationToken);

    // We are back at the root
    await turnContext.SendActivityAsync(MessageFactory.Text("Back in the root bot. Say \"skill\" and I'll patch you through"), cancellationToken);

    // Save conversation state
    await _conversationState.SaveChangesAsync(turnContext, cancellationToken: cancellationToken);
}

켜기 오류 처리기

오류가 발생하면 어댑터는 대화 상태를 지우고 사용자와의 대화를 다시 설정하며 오류 상태를 유지하지 않습니다.

기술 소비자에서 대화 상태를 지우기 전에 활성 기술에 대화 활동의 끝을 보내는 것이 좋습니다. 이렇게 하면 기술 소비자에서 대화를 해제하기 전에 기술에서 소비자-기술 대화와 관련된 모든 리소스를 해제할 수 있습니다.

SimpleRootBot\AdapterWithErrorHandler.cs

이 샘플에서 턴 오류 논리는 몇 가지 도우미 메서드 간에 분할됩니다.

private async Task HandleTurnError(ITurnContext turnContext, Exception exception)
{
    // Log any leaked exception from the application.
    // NOTE: In production environment, you should consider logging this to
    // Azure Application Insights. Visit https://aka.ms/bottelemetry to see how
    // to add telemetry capture to your bot.
    _logger.LogError(exception, $"[OnTurnError] unhandled error : {exception.Message}");

    await SendErrorMessageAsync(turnContext, exception);
    await EndSkillConversationAsync(turnContext);
    await ClearConversationStateAsync(turnContext);
}

private async Task SendErrorMessageAsync(ITurnContext turnContext, Exception exception)
{
    try
    {
        // Send a message to the user
        var errorMessageText = "The bot encountered an error or bug.";
        var errorMessage = MessageFactory.Text(errorMessageText, errorMessageText, InputHints.IgnoringInput);
        await turnContext.SendActivityAsync(errorMessage);

        errorMessageText = "To continue to run this bot, please fix the bot source code.";
        errorMessage = MessageFactory.Text(errorMessageText, errorMessageText, InputHints.ExpectingInput);
        await turnContext.SendActivityAsync(errorMessage);

        // Send a trace activity, which will be displayed in the Bot Framework Emulator
        await turnContext.TraceActivityAsync("OnTurnError Trace", exception.ToString(), "https://www.botframework.com/schemas/error", "TurnError");
    }
    catch (Exception ex)
    {
        _logger.LogError(ex, $"Exception caught in SendErrorMessageAsync : {ex}");
    }
}

private async Task EndSkillConversationAsync(ITurnContext turnContext)
{
    if (_skillsConfig == null)
    {
        return;
    }

    try
    {
        // Inform the active skill that the conversation is ended so that it has
        // a chance to clean up.
        // Note: ActiveSkillPropertyName is set by the RooBot while messages are being
        // forwarded to a Skill.
        var activeSkill = await _conversationState.CreateProperty<BotFrameworkSkill>(RootBot.ActiveSkillPropertyName).GetAsync(turnContext, () => null);
        if (activeSkill != null)
        {
            var botId = _configuration.GetSection(MicrosoftAppCredentials.MicrosoftAppIdKey)?.Value;

            var endOfConversation = Activity.CreateEndOfConversationActivity();
            endOfConversation.Code = "RootSkillError";
            endOfConversation.ApplyConversationReference(turnContext.Activity.GetConversationReference(), true);

            await _conversationState.SaveChangesAsync(turnContext, true);

            using var client = _auth.CreateBotFrameworkClient();

            await client.PostActivityAsync(botId, activeSkill.AppId, activeSkill.SkillEndpoint, _skillsConfig.SkillHostEndpoint, endOfConversation.Conversation.Id, (Activity)endOfConversation, CancellationToken.None);
        }
    }
    catch (Exception ex)
    {
        _logger.LogError(ex, $"Exception caught on attempting to send EndOfConversation : {ex}");
    }
}

private async Task ClearConversationStateAsync(ITurnContext turnContext)
{
    try
    {
        // Delete the conversationState for the current conversation to prevent the
        // bot from getting stuck in a error-loop caused by being in a bad state.
        // ConversationState should be thought of as similar to "cookie-state" in a Web pages.
        await _conversationState.DeleteAsync(turnContext);
    }
    catch (Exception ex)
    {
        _logger.LogError(ex, $"Exception caught on attempting to Delete ConversationState : {ex}");
    }
}

기술 엔드포인트

봇은 들어오는 기술 활동을 루트 봇의 기술 처리기에 전달하는 엔드포인트를 정의합니다.

SimpleRootBot\Controllers\SkillController.cs

[ApiController]
[Route("api/skills")]
public class SkillController : ChannelServiceController
{
    public SkillController(ChannelServiceHandlerBase handler)
        : base(handler)
    {
    }
}

서비스 등록

모든 클레임 유효성 검사와 모든 추가 개체를 포함하는 인증 구성 개체를 포함합니다. 이 샘플에서는 사용자와 기술 모두에서 활동의 유효성을 검사하기 위해 동일한 인증 구성 논리를 사용합니다.

SimpleRootBot\Startup.cs

// Register the skills configuration class
services.AddSingleton<SkillsConfiguration>();

// Register AuthConfiguration to enable custom claim validation.
services.AddSingleton(sp =>
{
    var allowedSkills = sp.GetService<SkillsConfiguration>().Skills.Values.Select(s => s.AppId).ToList();

    var claimsValidator = new AllowedSkillsClaimsValidator(allowedSkills);

    // If TenantId is specified in config, add the tenant as a valid JWT token issuer for Bot to Skill conversation.
    // The token issuer for MSI and single tenant scenarios will be the tenant where the bot is registered.
    var validTokenIssuers = new List<string>();
    var tenantId = sp.GetService<IConfiguration>().GetSection(MicrosoftAppCredentials.MicrosoftAppTenantIdKey)?.Value;

    if (!string.IsNullOrWhiteSpace(tenantId))
    {
        // For SingleTenant/MSI auth, the JWT tokens will be issued from the bot's home tenant.
        // Therefore, these issuers need to be added to the list of valid token issuers for authenticating activity requests.
        validTokenIssuers.Add(string.Format(CultureInfo.InvariantCulture, AuthenticationConstants.ValidTokenIssuerUrlTemplateV1, tenantId));
        validTokenIssuers.Add(string.Format(CultureInfo.InvariantCulture, AuthenticationConstants.ValidTokenIssuerUrlTemplateV2, tenantId));
        validTokenIssuers.Add(string.Format(CultureInfo.InvariantCulture, AuthenticationConstants.ValidGovernmentTokenIssuerUrlTemplateV1, tenantId));
        validTokenIssuers.Add(string.Format(CultureInfo.InvariantCulture, AuthenticationConstants.ValidGovernmentTokenIssuerUrlTemplateV2, tenantId));
    }

    return new AuthenticationConfiguration
    {
        ClaimsValidator = claimsValidator,
        ValidTokenIssuers = validTokenIssuers
    };
});

루트 봇 테스트

에뮬레이터에서 일반 봇인 것처럼 기술 소비자를 테스트할 수 있습니다. 그러나 기술과 기술 소비자 봇을 동시에 실행해야 합니다. 기술을 구성하는 방법에 대한 자세한 내용은 기술을 구현하는 방법을 참조하세요.

최신 Bot Framework Emulator를 다운로드하고 설치합니다.

  1. 에코 기술 봇과 간단한 루트 봇을 머신에서 로컬로 실행합니다. 지침이 필요한 경우 C#, JavaScript, Java 또는 Python 샘플에 대한 파일을 참조 README 하세요.
  2. 에뮬레이터를 사용하여 아래와 같이 봇을 테스트합니다. 기술로 메시지 end 또는 stop 메시지를 보내면 기술은 회신 메시지 외에도 루트 봇 endOfConversation 에 활동을 보냅니다. endOfConversation 활동의 code 속성은 기술이 성공적으로 완료되었음을 나타냅니다.

기술 소비자와의 상호 작용에 대한 예시 기록입니다.

디버깅에 대한 자세한 정보

기술과 기술 소비자 간의 트래픽이 인증되므로 이러한 봇을 디버깅할 때 추가 단계가 있습니다.

  • 기술 소비자와 직접 또는 간접적으로 사용하는 모든 기술을 실행해야 합니다.
  • 봇이 로컬로 실행되고 앱 ID와 암호가 있는 봇이 있는 경우 모든 봇에는 유효한 ID와 암호가 있어야 합니다.
  • 봇이 모두 배포된 경우 ngrok사용하여 모든 채널에서 봇을 디버그하는 방법을 참조하세요.
  • 일부 봇이 로컬로 실행되고 일부 봇이 배포된 경우 기술 또는 기술 소비자를 디버그하는 방법을 알아보세요.

그렇지 않으면 다른 봇을 디버그하는 것처럼 기술 소비자 또는 기술을 디버그할 수 있습니다. 자세한 내용은 Bot Framework Emulator를 사용하여 봇 디버깅 및 디버그를 참조하세요.

추가 정보

더 복잡한 루트 봇을 구현할 때 고려해야 할 몇 가지 사항은 다음과 같습니다.

사용자가 다단계 기술을 취소할 수 있도록 허용하려면

루트 봇은 사용자의 메시지를 활성 기술로 전달하기 전에 검사 합니다. 사용자가 현재 프로세스를 취소하려는 경우 루트 봇은 메시지를 전달하는 대신 기술에 활동을 보낼 endOfConversation 수 있습니다.

루트 봇과 기술 봇 간에 데이터를 교환하려면

기술 소비자는 기술에 대한 매개 변수를 보내기 위해 기술에 보내는 메시지에 대해 value 속성을 설정할 수 있습니다. 기술에서 반환 값을 받으려면 기술 소비자는 기술이 활동을 보낼 endOfConversation 때 값 속성을 검사 합니다.

여러 기술을 사용하려면

  • 기술이 활성화된 경우 루트 봇은 활성 상태인 기술을 확인하고 사용자의 메시지를 올바른 기술로 전달해야 합니다.
  • 활성 기술이 없는 경우 루트 봇은 봇 상태 및 사용자의 입력에 따라 시작할 기술을 결정해야 합니다.
  • 사용자가 여러 동시 기술 간에 전환할 수 있도록 허용하려는 경우 루트 봇은 사용자의 메시지를 전달하기 전에 사용자가 상호 작용하려는 활성 기술 중 어느 것을 결정해야 합니다.

예상 회신의 배달 모드를 사용하려면

예상 회신 배달 모드를 사용하려면:

  • 턴 컨텍스트에서 활동을 복제합니다.
  • 루트 봇에서 기술로 활동을 보내기 전에 새 활동의 배달 모드 속성을 "ExpectReplies"로 설정합니다.
  • 요청 응답에서 반환된 호출 응답 본문에서 예상 회신을 읽습니다.
  • 루트 봇 내에서 또는 원래 요청을 시작한 채널로 전송하여 각 활동을 처리합니다.

예상 회신은 활동에 회신하는 봇이 활동을 받은 봇의 인스턴스와 동일해야 하는 경우에 유용할 수 있습니다.