기술 소비자 구현Implement a skill consumer

적용 대상: SDK v4APPLIES TO: SDK v4

기술을 사용하여 다른 봇을 확장할 수 있습니다.You can use skills to extend another bot. 기술 은 다른 봇에 대한 작업 세트를 수행하고, 매니페스트를 사용하여 해당 인터페이스를 설명하는 봇입니다.A skill is a bot that can perform a set of tasks for another bot and uses a manifest to describe its interface. 루트 봇 은 하나 이상의 기술을 호출할 수 있는 사용자 지향 봇입니다.A root bot is a user-facing bot that can invoke one or more skills. 루트 봇은 기술 소비자 의 한 유형입니다.A root bot is a type of skill consumer.

  • 기술 소비자는 클레임 유효성 검사를 사용 하 여 액세스할 수 있는 기술을 관리 해야 합니다.A skill consumer must use claims validation to manage which skills can access it.
  • 기술 소비자는 여러 가지 기술을 사용할 수 있습니다.A skill consumer can use multiple skills.
  • 기술의 소스 코드에 액세스할 수 없는 개발자는 기술 매니페스트의 정보를 사용하여 기술 소비자를 설계할 수 있습니다.Developers who don't have access to the skill's source code can use the information in the skill's manifest to design their skill consumer.

이 문서에서는 에코 기술을 사용하여 사용자의 입력을 에코하는 기술 소비자를 구현하는 방법을 보여 줍니다.This article demonstrates how to implement a skill consumer that uses the echo skill to echo the user's input. 기술 매니페스트 샘플 및 에코 기술 구현에 대한 내용은 기술 구현 방법을 참조하세요.For a sample skill manifest and information about implementing the echo skill, see how to implement a skill.

기술 대화 상자를 사용하여 기술을 사용하는 방법에 대한 자세한 내용은 대화 상자를 사용하여 기술을 사용하는 방법을 참조하세요.For information about using a skill dialog to consume a skill, see how to use a dialog to consume a skill.

사전 요구 사항Prerequisites

참고

버전 4.11부터, Emulator에서 로컬로 기술 소비자를 테스트 하기 위해 앱 ID와 암호가 필요 하지 않습니다.Starting with version 4.11, you do not need an app ID and password to test a skill consumer locally in the Emulator. Azure에 소비자를 배포 하거나 배포 된 기술을 사용 하려면 Azure 구독이 필요 합니다.An Azure subscription is still required to deploy your consumer to Azure or to consume a deployed skill.

이 샘플 정보About this sample

skills simple bot-to-bot 샘플에는 두 개의 봇에 대한 프로젝트가 포함되어 있습니다.The skills simple bot-to-bot sample includes projects for two bots:

  • 에코 기술 봇 - 기술을 구현합니다.The echo skill bot, which implements the skill.
  • 단순 루트 봇 - 기술을 사용하는 루트 봇을 구현합니다.The simple root bot, which implements a root bot that consumes the skill.

이 문서에서는 봇 및 어댑터 개체의 지원 논리와 기술을 사용하여 작업을 교환하는 데 사용되는 개체를 포함하는 루트 봇에 대해 중점을 둡니다.This article focuses on the root bot, which includes support logic in its bot and adapter objects and includes objects used to exchange activities with a skill. 이러한 개체는 다음과 같습니다.These include:

  • 기술 클라이언트 - 활동을 기술에 보내는 데 사용됩니다.A skill client, used to send activities to a skill.
  • 기술 처리기 - 활동을 기술로부터 받는 데 사용됩니다.A skill handler, used to receive activities from a skill.
  • 기술 대화 ID 팩터리 - 기술 클라이언트와 처리기에서 이를 사용하여 사용자-루트 대화 참조와 루트-기술 대화 참조 사이를 변환합니다.A skill conversation ID factory, used by the skill client and handler to translate between the user-root conversation reference and the root-skill conversation reference.

에코 기술 봇에 대한 자세한 내용은 기술 구현 방법을 참조하세요.For information about the echo skill bot, see how to Implement a skill.

리소스Resources

배포 된 bot의 경우 bot 인증을 사용 하려면 각 참여 봇에 유효한 앱 ID와 암호가 있어야 합니다.For deployed bots, bot-to-bot authentication requires that each participating bot has a valid app ID and password. 그러나 앱 ID 및 암호 없이 Emulator를 사용 하 여 기술과 기술 소비자를 로컬로 테스트할 수 있습니다.However, you can test skills and skill consumers locally with the Emulator without an app ID and password.

애플리케이션 구성Application configuration

  1. 필요에 따라 구성 파일에 루트 bot의 앱 ID와 암호를 추가 합니다.Optionally, add the root bot's app ID and password to the config file.
  2. 기술이 기술 소비자에 게 회신할 기술 호스트 끝점 (서비스 또는 콜백 URL)을 추가 합니다.Add the skill host endpoint (the service or callback URL) to which the skills should reply to the skill consumer.
  3. 기술 소비자에서 사용할 각 기술에 대한 항목을 추가합니다.Add an entry for each skill the skill consumer will use. 포함되는 각 항목은 다음과 같습니다.Each entry includes:
    • 기술 소비자에서 각 기술을 식별하는 데 사용하는 IDAn ID the skill consumer will use to identify each skill.
    • 필요에 따라 기술 앱 ID입니다.Optionally, the skill's app ID.
    • 기술의 메시징 엔드포인트The skill's messaging endpoint.

참고

기술 또는 기술 소비자가 앱 ID 및 암호를 사용 하는 경우 둘 다 여야 합니다.If either the skill or skill consumer uses an app ID and password, both must.

SimpleRootBot\appsettings.jsonSimpleRootBot\appsettings.json

필요에 따라 루트 봇의 앱 ID와 암호를 추가 하 고, 에코 기술 봇의 앱 ID를 배열에 추가 합니다 BotFrameworkSkills .Optionally, add the root bot's app ID and password and add the app ID for the echo skill bot to the BotFrameworkSkills array.

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

기술 구성Skills configuration

이 샘플은 구성 파일의 각 기술 정보를 기술 개체의 컬렉션으로 읽어 들입니다.This sample reads information for each skill in the configuration file into a collection of skill objects.

SimpleRootBot\SkillsConfiguration.csSimpleRootBot\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 팩터리Conversation ID factory

이렇게 하면 기술에 사용할 대화 ID가 만들어지고, 기술 대화 ID에서 원본 사용자 대화 ID를 복구할 수 있습니다.This creates the conversation ID for use with the skill and can recover the original user conversation ID from the skill conversation ID.

이 샘플의 대화 ID 팩터리는 다음과 같은 간단한 시나리오를 지원합니다.The conversation ID factory for this sample supports a simple scenario where:

  • 루트 봇은 하나의 특정한 기술을 사용하도록 설계되었습니다.The root bot is designed to consume one specific skill.
  • 루트 봇에는 한 번에 하나의 기술을 사용하는 하나의 활성 대화만 있습니다.The root bot has only one active conversation with a skill at a time.

SimpleRootBot\SkillConversationIdFactory.csSimpleRootBot\SkillConversationIdFactory.cs

public class SkillConversationIdFactory : SkillConversationIdFactoryBase
{
    private readonly ConcurrentDictionary<string, string> _conversationRefs = new ConcurrentDictionary<string, string>();

    public override Task<string> CreateSkillConversationIdAsync(SkillConversationIdFactoryOptions options, CancellationToken cancellationToken)
    {
        var skillConversationReference = new SkillConversationReference
        {
            ConversationReference = options.Activity.GetConversationReference(),
            OAuthScope = options.FromBotOAuthScope
        };
        var key = $"{options.FromBotId}-{options.BotFrameworkSkill.AppId}-{skillConversationReference.ConversationReference.Conversation.Id}-{skillConversationReference.ConversationReference.ChannelId}-skillconvo";
        _conversationRefs.GetOrAdd(key, JsonConvert.SerializeObject(skillConversationReference));
        return Task.FromResult(key);
    }

    public override Task<SkillConversationReference> GetSkillConversationReferenceAsync(string skillConversationId, CancellationToken cancellationToken)
    {
        var conversationReference = JsonConvert.DeserializeObject<SkillConversationReference>(_conversationRefs[skillConversationId]);
        return Task.FromResult(conversationReference);
    }

    public override Task DeleteConversationReferenceAsync(string skillConversationId, CancellationToken cancellationToken)
    {
        _conversationRefs.TryRemove(skillConversationId, out _);
        return Task.CompletedTask;
    }
}

더 복잡한 시나리오를 지원하려면 대화 ID 팩터리를 다음과 같이 설계합니다.To support more complex scenarios, design your conversation ID factory so that:

  • 기술 대화 ID 만들기 메서드는 적절한 기술 대화 ID를 가져오거나 생성합니다.The create skill conversation ID method gets or generates the appropriate skill conversation ID.
  • 대화 참조 가져오기 메서드는 올바른 사용자 대화를 가져옵니다.The get conversation reference method gets the correct user conversation.

기술 클라이언트 및 기술 처리기Skill client and skill handler

기술 소비자는 기술 클라이언트를 사용하여 활동을 기술에 전달합니다.The skill consumer uses a skill client to forward activities to the skill. 이 작업은 클라이언트에서 기술 구성 정보와 대화 ID 팩터리를 사용하여 수행합니다.The client uses the skills configuration information and conversation ID factory to do so.

기술 소비자는 기술 처리기를 사용하여 기술로부터 활동을 받습니다.The skill consumer uses a skill handler to receive activities from a skill. 이 작업은 처리기에서 대화 ID 팩터리, 인증 구성 및 자격 증명 공급자를 사용하여 수행합니다. 처리기에는 루트 봇의 어댑터 및 활동 처리기에 대한 종속성이 있습니다.The handler uses the conversation ID factory, the authentication configuration, and a credential provider to do so, and also has dependencies on the root bot's adapter and activity handler

SimpleRootBot\Startup.csSimpleRootBot\Startup.cs

// Register the skills client and skills request handler.
services.AddSingleton<SkillConversationIdFactoryBase, SkillConversationIdFactory>();
services.AddHttpClient<SkillHttpClient>();
services.AddSingleton<ChannelServiceHandler, SkillHandler>();

기술에서 제공 하는 HTTP 트래픽은 기술 소비자가 기술에 알리는 서비스 URL 끝점에 제공 됩니다.HTTP traffic from the skill will come into the service URL endpoint that the skill consumer advertises to the skill. 언어 관련 엔드포인트 처리기를 사용하여 트래픽을 기술 처리기로 전달합니다.Use a language-specific endpoint handler to forward traffic to the skill handler.

기본 기술 처리기는 다음을 수행합니다.The default skill handler:

  • 앱 ID와 암호가 있는 경우는 인증 구성 개체를 사용 하 여 봇 대 봇 인증과 클레임 유효성 검사를 모두 수행 합니다.If an app ID and password are present, uses an authentication configuration object to perform both bot-to-bot authentication and claims validation.
  • 대화 ID 팩터리를 사용하여 소비자-기술 대화에서 루트-사용자 대화로 다시 변환합니다.Uses the conversation ID factory to translate from the consumer-skill conversation back to the root-user conversation.
  • 기술 소비자에서 루트-사용자 턴 컨텍스트를 다시 설정하고 사용자에게 활동을 전달할 수 있도록 자동 관리 메시지를 생성합니다.Generates a proactive message so that the skill consumer can reestablish a root-user turn context and forward activities to the user.

활동 처리기 논리Activity handler logic

참고로, 기술 소비자 논리는 다음과 같습니다.Of note, the skill consumer logic should:

  • 활성 기술이 있는지 확인하고 이를 활동에 적절하게 전달합니다.Remember whether there are any active skills and forward activities to them as appropriate.
  • 사용자가 기술에 전달해야 하는 요청을 수행하고 기술을 시작하는 시점을 확인합니다.Notice when a user makes a request that should be forwarded to a skill, and start the skill.
  • 모든 활성 기술에서 endOfConversation 활동을 찾아서 완료 시점을 확인합니다.Look for an endOfConversation activity from any active skill, to notice when it completes.
  • 해당되는 경우 사용자 또는 기술 소비자에서 아직 완료되지 않은 기술을 취소할 수 있는 논리를 추가합니다.If appropriate, add logic to let the user or skill consumer cancel a skill that has not completed yet.
  • 기술 소비자의 다른Save state before making the call to a skill, as any response may come back to a different instance of the skill consumer.

SimpleRootBot\Bots\RootBot.csSimpleRootBot\Bots\RootBot.cs

루트 봇에는 대화 상태, 기술 정보, 기술 클라이언트 및 일반 구성에 대한 종속성이 있습니다.The root bot has dependencies on conversation state, the skills information, the skill client, and the general configuration. ASP.NET은 종속성 주입을 통해 이러한 개체를 제공합니다.ASP.NET provides these objects through dependency injection. 또한 루트 봇은 활성 상태인 기술을 추적하는 대화 상태 속성 접근자를 정의합니다.The root bot also defines a conversation state property accessor to track which skill is active.

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

public RootBot(ConversationState conversationState, SkillsConfiguration skillsConfig, SkillHttpClient skillClient, IConfiguration configuration)
{
    _conversationState = conversationState ?? throw new ArgumentNullException(nameof(conversationState));
    _skillsConfig = skillsConfig ?? throw new ArgumentNullException(nameof(skillsConfig));
    _skillClient = skillClient ?? throw new ArgumentNullException(nameof(skillsConfig));
    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);
}

이 샘플에는 활동을 기술에 전달하는 도우미 메서드가 있습니다.This sample has a helper method for forwarding activities to a skill. 기술을 호출하기 전에 대화 상태를 저장하고, HTTP 요청이 성공했는지 여부를 확인합니다.It saves conversation state before invoking the skill, and it checks whether the HTTP request was successful.

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);

    // route the activity to the skill
    var response = await _skillClient.PostActivityAsync(_botId, targetSkill, _skillsConfig.SkillHostEndpoint, 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}");
    }
}

참고로, 루트 봇에는 활동을 기술에 전달하고, 사용자의 요청에 따라 기술을 시작하고, 기술이 완료되면 기술을 중지하는 논리가 포함되어 있습니다.Of note, the root bot includes logic for forwarding activities to the skill, starting the skill at the user's request, and stopping the skill when the skill completes.

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);
}

턴 오류 발생 시(on turn error) 처리기On turn error handler

오류가 발생하면 어댑터에서 대화 상태를 지워 사용자와의 대화를 다시 설정하고 오류 상태를 유지하지 않도록 합니다.When an error occurs, the adapter clears conversation state to reset the conversation with the user and avoid persisting an error state.

기술 소비자의 대화 상태를 지우기 전에 대화 작업 끝 을 활성 기술로 보내는 것이 좋습니다.It's a good practice to send an end of conversation activity to any active skill before clearing conversation state in the skill consumer. 이렇게 하면 기술 소비자에서 대화를 해제하기 전에 기술에서 소비자-기술 대화와 관련된 모든 리소스를 해제할 수 있습니다.This lets the skill release any resources associated with the consumer-skill conversation before the skill consumer releases the conversation.

SimpleRootBot\AdapterWithErrorHandler.csSimpleRootBot\AdapterWithErrorHandler.cs

이 샘플에서는 턴 오류 논리가 몇 가지 도우미 메서드 간에 분할 됩니다.In this sample, the turn error logic is split up among a few helper methods.

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 (_skillClient == null || _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);
            await _skillClient.PostActivityAsync(botId, activeSkill, _skillsConfig.SkillHostEndpoint, (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}");
    }
}

기술 엔드포인트Skills endpoint

봇은 들어오는 기술 활동을 루트 봇의 기술 처리기로 전달하는 엔드포인트를 정의합니다.The bot defines an endpoint that forwards incoming skill activities to the root bot's skill handler.

SimpleRootBot\Controllers\SkillController.csSimpleRootBot\Controllers\SkillController.cs

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

서비스 등록Service registration

클레임 유효성 검사가 포함된 인증 구성 개체 및 모든 추가 개체를 포함합니다.Include an authentication configuration object with any claims validation, plus all the additional objects. 이 샘플에서는 사용자와 기술의 활동에 대 한 유효성을 검사 하는 동일한 인증 구성 논리를 사용 합니다.This sample uses the same authentication configuration logic for validating activities from both users and skills.

SimpleRootBot\Startup.csSimpleRootBot\Startup.cs

// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers().AddNewtonsoftJson();

    // Configure credentials
    services.AddSingleton<ICredentialProvider, ConfigurationCredentialProvider>();

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

    // Register AuthConfiguration to enable custom claim validation.
    services.AddSingleton(sp => new AuthenticationConfiguration { ClaimsValidator = new Microsoft.BotBuilderSamples.SimpleRootBot.Authentication.AllowedSkillsClaimsValidator(sp.GetService<SkillsConfiguration>()) });

    // Register the Bot Framework Adapter with error handling enabled.
    // Note: some classes use the base BotAdapter so we add an extra registration that pulls the same instance.
    services.AddSingleton<BotFrameworkHttpAdapter, AdapterWithErrorHandler>();
    services.AddSingleton<BotAdapter>(sp => sp.GetService<BotFrameworkHttpAdapter>());

    // Register the skills client and skills request handler.
    services.AddSingleton<SkillConversationIdFactoryBase, SkillConversationIdFactory>();
    services.AddHttpClient<SkillHttpClient>();
    services.AddSingleton<ChannelServiceHandler, SkillHandler>();

    // Register the storage we'll be using for User and Conversation state. (Memory is great for testing purposes.)
    services.AddSingleton<IStorage, MemoryStorage>();

    // Register Conversation state (used by the Dialog system itself).
    services.AddSingleton<ConversationState>();

    // Register the bot as a transient. In this case the ASP Controller is expecting an IBot.
    services.AddTransient<IBot, RootBot>();
}

루트 봇 테스트Test the root bot

Emulator에서 기술 소비자를 표준 봇처럼 테스트할 수 있습니다. 그러나 기술과 기술 소비자를 동시에 실행해야 합니다.You can test the skill consumer in the Emulator as if it were a normal bot; however, you need to run both the skill and skill consumer bots at the same time. 기술을 구성하는 방법에 대한 자세한 내용은 기술 구현 방법을 참조하세요.See how to implement a skill for information on how to configure the skill.

최신 Bot Framework Emulator를 다운로드하고 설치합니다.Download and install the latest Bot Framework Emulator

  1. 머신에서 에코 기술 봇과 단순 루트 봇을 로컬로 실행합니다.Run the echo skill bot and simple root bot locally on your machine. 지침이 필요한 경우 README c #, JavaScript, Java또는 Python 샘플에 대 한 파일을 참조 하세요.If you need instructions, refer to the README file for the C#, JavaScript, Java, or Python sample.
  2. Emulator를 사용하여 아래와 같이 봇을 테스트합니다.Use the Emulator to test the bot as shown below. end또는 stop 메시지를 기술에 보내면이 기술은 endOfConversation 회신 메시지 외에도 활동을 루트 봇으로 보냅니다.When you send an end or stop message to the skill, the skill sends to the root bot an endOfConversation activity, in addition to the reply message. endOfConversation 활동의 code 속성은 기술이 성공적으로 완료되었음을 나타냅니다.The endOfConversation activity's code property indicates that the skill completed successfully.

기술 소비자 테스트

추가 정보Additional information

더 복잡한 루트 봇을 구현할 때 고려해야 할 몇 가지 사항은 다음과 같습니다.Here are some things to consider when implementing a more complex root bot.

사용자가 다단계 기술을 취소할 수 있게 하려면To allow the user to cancel a multi-step skill

사용자의 메시지를 활성 기술에 전달하기 전에 루트 봇에서 이 메시지를 확인해야 합니다.The root bot should check the user's message before forwarding it to the active skill. 사용자가 현재 프로세스를 취소하려는 경우 루트 봇은 메시지를 전달하는 대신 endOfConversation 활동을 기술에 보낼 수 있습니다.If the user wants to cancel the current process, the root bot can send an endOfConversation activity to the skill, instead of forwarding the message.

루트 봇과 기술 봇 간에 데이터를 교환하려면To exchange data between the root and skill bots

기술 소비자는 기술에 대한 매개 변수를 보내기 위해 기술에 보내는 메시지에 대해 value 속성을 설정할 수 있습니다.To send parameters to the skill, the skill consumer can set the value property on messages it sends to the skill. 기술 소비자는 기술로부터 반환 값을 받기 위해 기술에서 endOfConversation 활동을 보낼 때 value 속성을 확인해야 합니다.To receive return values from the skill, the skill consumer should check the value property when the skill sends an endOfConversation activity.

여러 기술을 사용하려면To use multiple skills

  • 기술이 활성화된 경우 루트 봇에서 활성 상태의 기술인지 확인하여 사용자의 메시지를 올바른 기술로 전달해야 합니다.If a skill is active, the root bot needs to determine which skill is active and forward the user's message to the correct skill.
  • 기술이 활성화되지 않은 경우 루트 봇에서 봇 상태 및 사용자의 입력에 따라 시작할 기술(있는 경우)을 결정해야 합니다.If no skill is active, the root bot needs to determine which skill to start, if any, based on bot state and the user's input.
  • 사용자가 여러 동시 기술 간에 전환할 수 있도록 하려면 루트 봇에서 사용자의 메시지를 전달하기 전에 해당 사용자가 상호 작용할 활성 기술을 결정해야 합니다.If you want to allow the user to switch between multiple concurrent skills, the root bot needs to determine which of the active skills the user is intending to interact with before forwarding the user's message.

예상 회신의 배달 모드를 사용하려면To use a delivery mode of expect replies

예상 응답 배달 모드를 사용하려면 다음을 수행합니다.To use the expect replies delivery mode:

  • 턴 컨텍스트에서 활동을 복제합니다.Clone the activity from the turn context.
  • 루트 봇에서 기술로 활동을 보내기 전에 새 활동의 배달 모드 속성을 "ExpectReplies"로 설정합니다.Set the delivery mode property of the new activity to "ExpectReplies" before sending the activity from root bot to skill.
  • 요청 응답에서 반환된 호출 응답 본문에서 예상 회신을 읽습니다.Read expected replies from the invoke response body returned from the request response.
  • 루트 봇 내에서 또는 원래 요청을 시작한 채널로 전송하여 각 활동을 처리합니다.Process each activity, either within the root bot or by sending it on to the channel that initiated the original request.

예상 회신은 활동에 응답하는 봇이 활동을 수신한 봇의 동일한 인스턴스여야 하는 경우에 유용할 수 있습니다.Expect replies can be useful in situations in which the bot that replies to an activity needs to be the same instance of the bot that received the activity.