共用方式為


實作技能

適用於: SDK v4

您可以使用技能來擴充另一個 Bot。 技能是 Bot,可以針對另一個 Bot 執行一組工作。

  • 指令清單描述技能的介面。 無法存取技能原始程式碼的開發人員可以使用指令清單中的資訊來設計其技能取用者。
  • 技能可以使用宣告驗證來管理哪些 Bot 或使用者可以存取它。

本文示範如何實作回應使用者輸入的技能。

某些類型的技能取用者無法使用某些類型的技能 Bot。 下表描述支援哪些組合。

  多租使用者技能 單一租使用者技能 使用者指派的受控識別技能
多租用戶取用者 支援 不支援 不支援
單一租用戶取用者 不支援 如果兩個應用程式都屬於相同的租使用者,則支援 如果兩個應用程式都屬於相同的租使用者,則支援
使用者指派的受控識別取用者 不支援 如果兩個應用程式都屬於相同的租使用者,則支援 如果兩個應用程式都屬於相同的租使用者,則支援

注意

Bot Framework JavaScript、C# 和 Python SDK 將會繼續受到支援,不過,Java SDK 即將淘汰,最終長期支援將於 2023 年 11 月結束。

使用 Java SDK 建置的現有 Bot 將繼續運作。

針對新的 Bot 建置,請考慮使用 Microsoft Copilot Studio ,並閱讀 選擇正確的 Copilot 解決方案

如需詳細資訊,請參閱 Bot 建置的未來。

必要條件

注意

從 4.11 版開始,您不需要應用程式識別碼和密碼,即可在 Bot Framework 模擬器本機測試技能。 仍然需要 Azure 訂用帳戶,才能將技能部署至 Azure。

關於此範例

技能 簡單的 Bot 對 Bot 範例包含兩個 Bot 的專案:

  • 顯技能 Bot,可實作技能。
  • 簡單的 根 Bot,其會實作取用技能的根 Bot。

本文著重於技能,其中包含其 Bot 和配接器中的支持邏輯。

如需簡單根 Bot 的相關信息,請參閱如何 實作技能取用者

資源

對於已部署的 Bot,Bot 對 Bot 驗證會要求每個參與的 Bot 都有有效的身分識別資訊。 不過,您可以使用模擬器在本機測試多租使用者技能和技能取用者,而不需要應用程式標識碼和密碼。

若要讓技能可供使用者面向的 Bot 使用,請向 Azure 註冊技能。 如需詳細資訊,請參閱如何使用 Azure AI Bot Service 註冊 Bot。

應用程式設定

或者,將技能的身分識別資訊新增至其組態檔。 如果技能或技能取用者提供身分識別資訊,則兩者都必須。

允許的 呼叫端 陣列可以限制哪些技能取用者可以存取技能。 若要接受來自任何技能取用者的呼叫,請新增 「*」 元素。

注意

如果您要在本機測試技能,而不需要 Bot 身分識別資訊,技能或技能取用者都不會執行程式碼來執行宣告驗證。

EchoSkillBot\appsettings.json

或者,將技能的身分識別資訊新增至appsettings.json檔案。

{
  "MicrosoftAppType": "",
  "MicrosoftAppId": "",
  "MicrosoftAppPassword": "",
  "MicrosoftAppTenantId": "",

  // This is a comma separate list with the App IDs that will have access to the skill.
  // This setting is used in AllowedCallersClaimsValidator.
  // Examples: 
  //    [ "*" ] allows all callers.
  //    [ "AppId1", "AppId2" ] only allows access to parent bots with "AppId1" and "AppId2".
  "AllowedCallers": [ "*" ]
}

活動處理程序邏輯

接受輸入參數

技能取用者可以將資訊傳送至技能。 接受這類資訊的其中一種方法是透過傳入訊息上的 value 屬性來接受它們。 另一種方式是處理事件和叫用活動。

此範例中的技能不接受輸入參數。

若要繼續或完成交談

當技能傳送活動時,技能取用者應該將活動轉送給使用者。

不過,您必須在技能完成時傳送 endOfConversation 活動;否則,技能取用者會繼續將用戶活動轉送至技能。 或者,使用活動的 value 屬性來包含傳回值,並使用活動的 程式代碼 屬性來指出技能結束的原因。

EchoSkillBot\Bots\EchoBot.cs

protected override async Task OnMessageActivityAsync(ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken)
{
    if (turnContext.Activity.Text.Contains("end") || turnContext.Activity.Text.Contains("stop"))
    {
        // Send End of conversation at the end.
        var messageText = $"ending conversation from the skill...";
        await turnContext.SendActivityAsync(MessageFactory.Text(messageText, messageText, InputHints.IgnoringInput), cancellationToken);
        var endOfConversation = Activity.CreateEndOfConversationActivity();
        endOfConversation.Code = EndOfConversationCodes.CompletedSuccessfully;
        await turnContext.SendActivityAsync(endOfConversation, cancellationToken);
    }
    else
    {
        var messageText = $"Echo: {turnContext.Activity.Text}";
        await turnContext.SendActivityAsync(MessageFactory.Text(messageText, messageText, InputHints.IgnoringInput), cancellationToken);
        messageText = "Say \"end\" or \"stop\" and I'll end the conversation and back to the parent.";
        await turnContext.SendActivityAsync(MessageFactory.Text(messageText, messageText, InputHints.ExpectingInput), cancellationToken);
    }
}

取消技能

針對多回合技能,您也會接受 endOfConversation 來自技能取用者的活動,以允許取用者取消目前的交談。

此技能的邏輯不會從回合變更為回合。 如果您實作配置交談資源的技能,請將資源清除程式代碼新增至交談結尾處理程式。

EchoSkillBot\Bots\EchoBot.cs

protected override Task OnEndOfConversationActivityAsync(ITurnContext<IEndOfConversationActivity> turnContext, CancellationToken cancellationToken)
{
    // This will be called if the root bot is ending the conversation.  Sending additional messages should be
    // avoided as the conversation may have been deleted.
    // Perform cleanup of resources if needed.
    return Task.CompletedTask;
}

宣告驗證程式

此範例會使用允許的呼叫端清單進行宣告驗證。 技能的組態檔會定義清單。 驗證程式對象接著會讀取清單。

您必須將宣告驗證程式新增至驗證組態。 宣告會在驗證標頭之後進行評估。 您的驗證碼應該擲回錯誤或例外狀況來拒絕要求。 您有許多原因可能會想要拒絕其他已驗證的要求。 例如:

  • 技能是付費服務的一部分。 使用者不在資料庫中不應該有存取權。
  • 技能是專屬的。 只有某些技能取用者可以呼叫技能。

重要

如果您沒有提供宣告驗證程式,Bot 會在從技能取用者收到活動時產生錯誤或例外狀況。

SDK 提供的 AllowedCallersClaimsValidator 類別會根據允許呼叫技能之應用程式的簡單標識符清單來新增應用層級授權。 如果清單包含星號 ,則會允許所有來電者。 宣告驗證程式是在 Startup.cs設定。

技能配接器

發生錯誤時,技能的配接器應該清除技能的交談狀態,而且也應該將活動傳送 endOfConversation 給技能取用者。 若要發出技能因錯誤而結束的訊號,請使用 活動的程式代碼 屬性。

EchoSkillBot\SkillAdapterWithErrorHandler.cs

private async Task HandleTurnError(ITurnContext turnContext, Exception exception)
{
    // Log any leaked exception from the application.
    _logger.LogError(exception, $"[OnTurnError] unhandled error : {exception.Message}");

    await SendErrorMessageAsync(turnContext, exception);
    await SendEoCToParentAsync(turnContext, exception);
}

private async Task SendErrorMessageAsync(ITurnContext turnContext, Exception exception)
{
    try
    {
        // Send a message to the user.
        var errorMessageText = "The skill 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.
        // Note: we return the entire exception in the value property to help the developer;
        // this should not be done in production.
        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 SendEoCToParentAsync(ITurnContext turnContext, Exception exception)
{
    try
    {
        // Send an EndOfConversation activity to the skill caller with the error to end the conversation,
        // and let the caller decide what to do.
        var endOfConversation = Activity.CreateEndOfConversationActivity();
        endOfConversation.Code = "SkillError";
        endOfConversation.Text = exception.Message;
        await turnContext.SendActivityAsync(endOfConversation);
    }
    catch (Exception ex)
    {
        _logger.LogError(ex, $"Exception caught in SendEoCToParentAsync : {ex}");
    }
}

服務註冊

Bot Framework 配 接器 會使用 驗證組態 物件(在建立配接器時設定)來驗證傳入要求的驗證標頭。

此範例會將宣告驗證新增至驗證組態,並使用 技能配接器搭配上一節所述的錯誤處理程式

EchoSkillBot\Startup.cs

    options.SerializerSettings.MaxDepth = HttpHelper.BotMessageSerializerSettings.MaxDepth;
});

// Register AuthConfiguration to enable custom claim validation.
services.AddSingleton(sp =>
{
    var allowedCallers = new List<string>(sp.GetService<IConfiguration>().GetSection("AllowedCallers").Get<string[]>());

    var claimsValidator = new AllowedCallersClaimsValidator(allowedCallers);

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

// Create the Bot Framework Authentication to be used with the Bot Adapter.
services.AddSingleton<BotFrameworkAuthentication, ConfigurationBotFrameworkAuthentication>();

技能指令清單

技能指令清單是 JSON 檔案,描述技能可執行的活動、其輸入和輸出參數,以及技能的端點。 指令清單包含從另一個 Bot 存取技能所需的資訊。 最新的架構版本是 v2.1

EchoSkillBot\wwwroot\manifest\echoskillbot-manifest-1.0.json

{
  "$schema": "https://schemas.botframework.com/schemas/skills/skill-manifest-2.0.0.json",
  "$id": "EchoSkillBot",
  "name": "Echo Skill bot",
  "version": "1.0",
  "description": "This is a sample echo skill",
  "publisherName": "Microsoft",
  "privacyUrl": "https://echoskillbot.contoso.com/privacy.html",
  "copyright": "Copyright (c) Microsoft Corporation. All rights reserved.",
  "license": "",
  "iconUrl": "https://echoskillbot.contoso.com/icon.png",
  "tags": [
    "sample",
    "echo"
  ],
  "endpoints": [
    {
      "name": "default",
      "protocol": "BotFrameworkV3",
      "description": "Default endpoint for the skill",
      "endpointUrl": "http://echoskillbot.contoso.com/api/messages",
      "msAppId": "00000000-0000-0000-0000-000000000000"
    }
  ]
}

技能指令清單架構是描述技能指令清單架構的 JSON 檔案。 目前的架構版本為 2.1.0

測試技能

此時,您可以測試模擬器中的技能,就像是一般 Bot 一樣。 不過,若要將它測試為技能,您必須 實作技能取用者

下載並安裝最新的 Bot Framework 模擬器

  1. 在本機計算機上執行回應技能 Bot。 如果您需要指示,請參閱 README C#JavaScriptJavaPython 範例的 檔案。
  2. 使用模擬器來測試 Bot。 當您將「結束」或「停止」訊息傳送給技能時,除了回復訊息之外,也會傳送 endOfConversation 活動。 技能會傳送 endOfConversation 活動,以指出技能已完成。

顯示交談結束活動的範例文字記錄。

深入瞭解偵錯

由於技能與技能取用者之間的流量已經過驗證,因此偵錯這類 Bot 時會有額外的步驟。

  • 技能取用者及其直接或間接取用的所有技能都必須執行。
  • 如果 Bot 在本機執行,且任何 Bot 都有應用程式識別碼和密碼,則所有 Bot 都必須具有有效的標識碼和密碼。
  • 如果 Bot 全部部署,請參閱如何使用 devtunnel 從任何通道偵錯 Bot。
  • 如果某些 Bot 在本機執行,且有些 Bot 已部署,請參閱如何偵錯技能或技能取用

否則,您可以偵錯技能取用者或技能,就像偵錯其他 Bot 一樣。 如需詳細資訊,請參閱使用 Bot Framework 模擬器進行 Bot 偵錯和偵錯。

下一步