實作技能取用者Implement a skill consumer

適用于: SDK v4APPLIES TO: SDK v4

您可以使用技能擴充另一個 Bot。You can use skills to extend another bot. 「技能」是 Bot,可以針對另一個 Bot 執行一組工作,並使用資訊清單來描述其介面。A skill is a bot that can perform a set of tasks for another bot and uses a manifest to describe its interface. 「根目錄 Bot」 是使用者對應的 Bot,可叫用一或多項技能。A root bot is a user-facing bot that can invoke one or more skills. 根目錄 Bot 是一種「技能取用者」類型。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.

PrerequisitesPrerequisites

注意

從4.11 版開始,您不需要應用程式識別碼和密碼,即可在模擬器中本機測試技能取用者。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

技能簡單 Bot 對 Bot 範例包含兩個 Bot 的專案:The skills simple bot-to-bot sample includes projects for two bots:

  • 「回應技能 Bot」會實作技能。The echo skill bot, which implements the skill.
  • 「簡單根目錄 Bot」 會實作取用技能的根目錄 Bot。The simple root bot, which implements a root bot that consumes the skill.

本文著重於根目錄 Bot,內容包括 Bot 和配接器物件中的支援邏輯,以及用來與技能交換活動的物件。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.
  • 技能交談識別碼中心,可供技能用戶端和處理常式在使用者根交談參考與根技能交談參考之間轉譯。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.

如需有關回應技能 Bot 的詳細資訊,請參閱如何實作技能For information about the echo skill bot, see how to Implement a skill.

資源Resources

針對已部署的 bot,bot 對 bot 驗證會要求每個參與的 bot 都具備有效的應用程式識別碼和密碼。For deployed bots, bot-to-bot authentication requires that each participating bot has a valid app ID and password. 不過,您可以使用模擬器在本機測試技能和技能取用者,而不需要應用程式識別碼和密碼。However, you can test skills and skill consumers locally with the Emulator without an app ID and password.

應用程式設定Application configuration

  1. (選擇性)將根 bot 的應用程式識別碼和密碼新增至設定檔。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:
    • 技能取用者用來識別每項技能的識別碼。An ID the skill consumer will use to identify each skill.
    • (選擇性)技能的應用程式識別碼。Optionally, the skill's app ID.
    • 技能的傳訊端點。The skill's messaging endpoint.

注意

如果技能或技能取用者使用應用程式識別碼和密碼,則兩者都必須是。If either the skill or skill consumer uses an app ID and password, both must.

SimpleRootBot\appsettings.jsonSimpleRootBot\appsettings.json

(選擇性)新增根 bot 的應用程式識別碼和密碼,並將回應技能 bot 的應用程式識別碼新增至 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": "TODO: Add here the App ID for the skill",
      "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>();
}

交談識別碼中心Conversation ID factory

這會建立與技能搭配使用的交談識別碼,而且可以從技能交談識別碼復原原始使用者交談識別碼。This creates the conversation ID for use with the skill and can recover the original user conversation ID from the skill conversation ID.

此範例的交談識別碼中心支援簡單案例,其中:The conversation ID factory for this sample supports a simple scenario where:

  • 根目錄 Bot 的設計訴求是取用一種特定的技能。The root bot is designed to consume one specific skill.
  • 根目錄 Bot 一次只能與一項技能進行作用中的交談。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;
    }
}

若要支援更複雜的案例,請設計您的交談識別碼中心,以便:To support more complex scenarios, design your conversation ID factory so that:

  • create skill conversation ID 方法能取得或產生適當的技能交談識別碼。The create skill conversation ID method gets or generates the appropriate skill conversation ID.
  • get conversation reference 方法能取得正確的使用者交談。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. 用戶端會使用技能組態資訊和交談識別碼中心來執行此動作。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. 處理常式會使用交談識別碼中心、驗證組態和認證提供者來執行這項操作,而且也具有根目錄 Bot 的配接器和活動處理常式的相依性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

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 advertizes to the skill. 使用特定語言的端點處理常式,將流量轉送至技能處理常式。Use a language-specific endpoint handler to forward traffic to the skill handler.

預設的技能處理常式:The default skill handler:

  • 如果有應用程式識別碼和密碼,則會使用驗證設定物件來執行 bot 對 bot 驗證和宣告驗證。If an app ID and password are present, uses an authentication configuration object to perform both bot-to-bot authentication and claims validation.
  • 會使用交談識別碼中心,從取用者技能交談轉譯回根使用者交談。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. (例如負載平衡等)(load balancing, etc.)

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

根目錄 Bot 具有交談狀態、技能資訊、技能用戶端和一般設定的相依性。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;
    if (string.IsNullOrWhiteSpace(_botId))
    {
        throw new ArgumentException($"{MicrosoftAppCredentials.MicrosoftAppIdKey} is not set in configuration");
    }

    // We use a single skill in this example.
    var targetSkillId = "EchoSkillBot";
    if (!_skillsConfig.Skills.TryGetValue(targetSkillId, out _targetSkill))
    {
        throw new ArgumentException($"Skill with ID \"{targetSkillId}\" not found in configuration");
    }

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

此範例有一個 helper 方法,可將活動轉送至技能。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}");
    }
}

請注意,根 Bot 包含將活動轉送至技能、在使用者的要求上開始技能,以及在技能完成時停止技能的邏輯。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 handler

發生錯誤時,配接器會清除交談狀態,以重設與使用者的交談,並避免錯誤狀態持續存在。When an error occurs, the adapter clears conversation state to reset the conversation with the user and avoid persisting an error state.

在清除技能取用者中的交談狀態之前,最好先將「結束交談」活動傳送給所有作用中的技能。It is 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

在此範例中,回合錯誤邏輯會在幾個 helper 方法中分割。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

Bot 會定義一個端點,將傳入技能活動轉送到根目錄 Bot 的技能處理常式。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 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>();
}

測試根目錄 BotTest the root bot

您可以在模擬器中測試技能取用者,將其當做是一般 的 Bot;不過,您必須同時執行技能和技能取用者 Bot。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 EmulatorDownload and install the latest Bot Framework Emulator

  1. 在電腦本機執行回應技能 Bot 和簡單的根目錄 Bot。Run the echo skill bot and simple root bot locally on your machine. 如需相關指示,請參閱 C#JavaScriptPython 範例的讀我檔案。If you need instructions, refer to the README file for the C#, JavaScript, or Python sample.
  2. 使用模擬器來測試 Bot,如下所示。Use the Emulator to test the bot as shown below. 請注意,當您將 endstop 訊息傳送至技能時,除了回復訊息以外,技能也會將 endOfConversation 活動傳送至根目錄 Bot。Note that 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 活動的「程式碼」屬性指出技能已順利完成。The endOfConversation activity's code property indicates that the skill completed successfully.

測試技能取用者

其他資訊Additional information

以下是在執行更複雜的根目錄 Bot 時要考慮的一些事項。Here are some things to consider when implementing a more complex root bot.

允許使用者取消多步驟的技能To allow the user to cancel a multi-step skill

根目錄 Bot 應該先檢查使用者的訊息,再將其轉送至有效技能。The root bot should check the user's message before forwarding it to the active skill. 如果使用者想取消目前的流程,根目錄 Bot 可將 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.

在根與技能 Bot 之間交換資料To exchange data between the root and skill bots

若要將參數傳送給技能,技能取用者可以針對傳送給技能的訊息,設定「值」屬性。To send parameters to the skill, the skill consumer can set the value property on messages it sends to the skill. 若要從技能接收傳回值,技能取用者應該在技能傳送 endOfConversation 活動時,檢查「值」屬性。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

  • 如果技能作用中,根目錄 Bot 必須判斷哪些技能為作用中,並將使用者的訊息轉送至正確的技能。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.
  • 如果沒有作用中的技能,根目錄 Bot 就必須根據 Bot 狀態和使用者的輸入,判斷要啟動的技能 (如果有的話)。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.
  • 如果您想允許使用者在多個並行技能之間切換,根目錄 Bot 必須判斷使用者在轉送使用者的訊息之前,要與其互動的使用中技能。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",然後才將活動從根 bot 傳送至技能。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.
  • 處理每個活動,不論是在根 bot 內,或是將它傳送至起始原始要求的通道。Process each activity, either within the root bot or by sending it on to the channel which initiated the original request.

當回復活動的 bot 必須是接收活動之 bot 的相同實例時,預期的回復可能會很有用。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.