使用對話方塊來取用技能

適用于: SDK v4

本文示範如何在技能取用者內使用 技能對話 。 技能對話方塊會將活動從父 Bot 張貼至技能 Bot,並將技能回應傳回給使用者。 此取用者存取的技能 Bot 可以同時處理訊息和事件活動。 如需實作技能的範例技能資訊清單和相關資訊,請參閱如何在 技能 中使用對話。

如需在對話外部使用技能 Bot 的相關資訊,請參閱如何實作技能取用

注意

Bot Framework JavaScript、C# 和 Python SDK 將會繼續受到支援,不過,JAVA SDK 即將淘汰,最終長期支援將于 2023 年 11 月結束。 只會執行此存放庫中的重要安全性和錯誤修正。

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

針對新的 Bot 建置,請考慮使用 Power Virtual Agents ,並閱讀 選擇正確的聊天機器人解決方案

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

必要條件

關於此範例

技能技能Dialog 範例包含兩個 Bot 的專案:

  • 對話 根 Bot ,它會使用技能對話 類別來取 用技能。
  • 對話 技能 Bot ,它會使用對話來處理來自技能取用者的活動。

本文著重于如何使用 根 Bot 中的技能對話 類別來管理技能、傳送訊息和事件活動,以及取消技能。

如需有關建立技能取用者之其他層面的資訊,請參閱如何 實作技能取用者

如需對話技能 Bot 的相關資訊,請參閱如何在 技能 內使用對話。

資源

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

應用程式設定

  1. 或者,將根 Bot 的身分識別資訊新增至組態檔。
  2. 將技能主機端點(服務或回呼 URL)新增至技能取用者應回復的技能。
  3. 為技能取用者將使用的每個技能新增專案。 每個專案都包含:
    • 技能取用者將用來識別每個技能的識別碼。
    • 或者,技能 Bot 的應用程式或用戶端識別碼。
    • 技能的傳訊端點。

注意

如果技能或技能取用者指定身分識別,則兩者都必須。

DialogRootBot\appsettings.json

選擇性地新增根 Bot 的身分識別資訊,並將回應技能 Bot 的應用程式或用戶端識別碼新增至 BotFrameworkSkills 陣列。

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

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

對話方塊邏輯

Bot 的主要對話包含 此 Bot 所取用之每個技能的技能對話 。 技能對話會透過您各種技能相關物件來管理技能,例如 技能用戶端 技能交談識別碼處理站 物件。 主要對話也會示範如何根據使用者輸入取消技能(透過技能對話)。

此 Bot 使用的技能支援幾個不同的功能。 它可以預訂航班或取得城市的天氣。 此外,如果它收到上述任一內容以外的訊息,且已設定 LUIS 辨識器,則會嘗試解譯使用者的意圖。

注意

Language Understanding (LUIS) 將于 2025 年 10 月 1 日淘汰。 從 2023 年 4 月 1 日起,您將無法建立新的 LUIS 資源。 新版的語言理解現在已提供作為 Azure AI 語言的一部分。

對話式語言理解(CLU)是 Azure AI 語言的一項功能,是 LUIS 的更新版本。 如需 Bot Framework SDK 中語言理解支援的詳細資訊,請參閱 自然語言理解

技能資訊清單( C# 、JavaScript JAVA Python )描述技能可執行檔動作、其輸入和輸出參數,以及技能的端點。 請注意,技能可以處理 「BookFlight」 或 「GetWeather」 事件。 它也可以處理訊息。

主要對話方塊包含的程式碼:

主要對話繼承自 元件對話 類別。 如需元件對話的詳細資訊,請參閱如何 管理對話複雜度

初始化主要對話方塊

主要對話包含對話(用於管理技能外部的對話流程)和技能對話(用於管理技能)。 瀑布包含下列步驟,在接下來的幾節中會更詳細地說明。

  1. 提示使用者選取要使用的技能。 (根 Bot 會取用一個技能。
  2. 提示使用者選取要用於該技能的動作。 (技能 Bot 定義三個動作。
  3. 根據所選動作,使用初始活動啟動所選技能。
  4. 技能完成後,如果有的話,顯示結果。 然後,重新開機瀑布。

DialogRootBot\Dialogs\MainDialog.cs

MainDialog 類別衍生自 ComponentDialog。 除了交談狀態之外,對話還需要根 Bot 的身分識別,以及技能交談識別碼處理站、技能 HTTP 用戶端和技能設定物件的參考。

對話建構函式會檢查其輸入參數、新增技能對話、新增提示和瀑布式對話來管理技能外部的對話流程,並建立屬性存取子來追蹤作用中的技能,如果有的話。

建構函式會呼叫 AddSkillDialogs 協助程式方法,為組態檔中包含的每個技能建立 SkillDialog ,如同從組態檔讀取至 SkillsConfiguration 物件一樣。

// Helper method that creates and adds SkillDialog instances for the configured skills.
private void AddSkillDialogs(ConversationState conversationState, SkillConversationIdFactoryBase conversationIdFactory, SkillsConfiguration skillsConfig, string botId)
{
    foreach (var skillInfo in _skillsConfig.Skills.Values)
    {
        // Create the dialog options.
        var skillDialogOptions = new SkillDialogOptions
        {
            BotId = botId,
            ConversationIdFactory = conversationIdFactory,
            SkillClient = _auth.CreateBotFrameworkClient(),
            SkillHostEndpoint = skillsConfig.SkillHostEndpoint,
            ConversationState = conversationState,
            Skill = skillInfo
        };

        // Add a SkillDialog for the selected skill.
        AddDialog(new SkillDialog(skillDialogOptions, skillInfo.Id));
    }
}

選取技能

在第一個步驟中,主要對話方塊會提示使用者要呼叫哪些技能,並使用 「SkillPrompt」 選擇提示來取得答案。 (此 Bot 只定義一個技能。

DialogRootBot\Dialogs\MainDialog.cs

// Render a prompt to select the skill to call.
private async Task<DialogTurnResult> SelectSkillStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
    // Create the PromptOptions from the skill configuration which contain the list of configured skills.
    var messageText = stepContext.Options?.ToString() ?? "What skill would you like to call?";
    var repromptMessageText = "That was not a valid choice, please select a valid skill.";
    var options = new PromptOptions
    {
        Prompt = MessageFactory.Text(messageText, messageText, InputHints.ExpectingInput),
        RetryPrompt = MessageFactory.Text(repromptMessageText, repromptMessageText, InputHints.ExpectingInput),
        Choices = _skillsConfig.Skills.Select(skill => new Choice(skill.Value.Id)).ToList()
    };

    // Prompt the user to select a skill.
    return await stepContext.PromptAsync("SkillPrompt", options, cancellationToken);
}

選取技能動作

在下一個步驟中,主要對話方塊:

  1. 儲存使用者選取之技能的相關資訊。
  2. 提示使用者要使用哪一個技能動作,並使用 「SkillActionPrompt」 選擇提示來取得答案。
    • 它會使用協助程式方法來取得要從中選擇的動作清單。
    • 如果使用者的輸入不符合其中一個選項,則與此提示相關聯的提示驗證程式預設會傳送訊息。

此 Bot 中包含的選項可協助測試為此技能定義的動作。 更通常,您會從技能資訊清單讀取選項,並根據該清單向使用者呈現選項。

DialogRootBot\Dialogs\MainDialog.cs

// Render a prompt to select the action for the skill.
private async Task<DialogTurnResult> SelectSkillActionStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
    // Get the skill info based on the selected skill.
    var selectedSkillId = ((FoundChoice)stepContext.Result).Value;
    var selectedSkill = _skillsConfig.Skills.FirstOrDefault(s => s.Value.Id == selectedSkillId).Value;

    // Remember the skill selected by the user.
    stepContext.Values[_selectedSkillKey] = selectedSkill;

    // Create the PromptOptions with the actions supported by the selected skill.
    var messageText = $"Select an action # to send to **{selectedSkill.Id}** or just type in a message and it will be forwarded to the skill";
    var options = new PromptOptions
    {
        Prompt = MessageFactory.Text(messageText, messageText, InputHints.ExpectingInput),
        Choices = GetSkillActions(selectedSkill)
    };

    // Prompt the user to select a skill action.
    return await stepContext.PromptAsync("SkillActionPrompt", options, cancellationToken);
}
// Helper method to create Choice elements for the actions supported by the skill.
private IList<Choice> GetSkillActions(BotFrameworkSkill skill)
{
    // Note: the bot would probably render this by reading the skill manifest.
    // We are just using hardcoded skill actions here for simplicity.

    var choices = new List<Choice>();
    switch (skill.Id)
    {
        case "DialogSkillBot":
            choices.Add(new Choice(SkillActionBookFlight));
            choices.Add(new Choice(SkillActionBookFlightWithInputParameters));
            choices.Add(new Choice(SkillActionGetWeather));
            break;
    }

    return choices;
}
// This validator defaults to Message if the user doesn't select an existing option.
private Task<bool> SkillActionPromptValidator(PromptValidatorContext<FoundChoice> promptContext, CancellationToken cancellationToken)
{
    if (!promptContext.Recognized.Succeeded)
    {
        // Assume the user wants to send a message if an item in the list is not selected.
        promptContext.Recognized.Value = new FoundChoice { Value = SkillActionMessage };
    }

    return Task.FromResult(true);
}

啟動技能

在下一個步驟中,主要對話方塊:

  1. 擷取使用者選取之技能與技能活動的相關資訊。
  2. 使用協助程式方法來建立活動,以一開始傳送至技能。
  3. 建立用來啟動技能對話方塊的對話方塊選項。 這包括要傳送的初始活動。
  4. 在呼叫技能之前儲存狀態。 (這是必要的,因為技能回應可能會來到技能取用者的不同實例。
  5. 開始技能對話方塊,傳入要呼叫的技能識別碼,以及要呼叫的選項。

DialogRootBot\Dialogs\MainDialog.cs

// Starts the SkillDialog based on the user's selections.
private async Task<DialogTurnResult> CallSkillActionStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
    var selectedSkill = (BotFrameworkSkill)stepContext.Values[_selectedSkillKey];

    Activity skillActivity;
    switch (selectedSkill.Id)
    {
        case "DialogSkillBot":
            skillActivity = CreateDialogSkillBotActivity(((FoundChoice)stepContext.Result).Value, stepContext.Context);
            break;

        // We can add other case statements here if we support more than one skill.
        default:
            throw new Exception($"Unknown target skill id: {selectedSkill.Id}.");
    }

    // Create the BeginSkillDialogOptions and assign the activity to send.
    var skillDialogArgs = new BeginSkillDialogOptions { Activity = skillActivity };

    // Save active skill in state.
    await _activeSkillProperty.SetAsync(stepContext.Context, selectedSkill, cancellationToken);

    // Start the skillDialog instance with the arguments. 
    return await stepContext.BeginDialogAsync(selectedSkill.Id, skillDialogArgs, cancellationToken);
}

摘要說明技能結果

在最後一個步驟中,主要對話方塊:

  1. 如果技能傳回值,請向使用者顯示結果。
  2. 從對話狀態清除使用中的技能。
  3. 從交談狀態移除作用中的技能屬性。
  4. 重新開機本身 (主要對話方塊)。

DialogRootBot\Dialogs\MainDialog.cs

// The SkillDialog has ended, render the results (if any) and restart MainDialog.
private async Task<DialogTurnResult> FinalStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
    var activeSkill = await _activeSkillProperty.GetAsync(stepContext.Context, () => null, cancellationToken);

    // Check if the skill returned any results and display them.
    if (stepContext.Result != null)
    {
        var message = $"Skill \"{activeSkill.Id}\" invocation complete.";
        message += $" Result: {JsonConvert.SerializeObject(stepContext.Result)}";
        await stepContext.Context.SendActivityAsync(MessageFactory.Text(message, message, inputHint: InputHints.IgnoringInput), cancellationToken: cancellationToken);
    }

    // Clear the skill selected by the user.
    stepContext.Values[_selectedSkillKey] = null;

    // Clear active skill in state.
    await _activeSkillProperty.DeleteAsync(stepContext.Context, cancellationToken);

    // Restart the main dialog with a different message the second time around.
    return await stepContext.ReplaceDialogAsync(InitialDialogId, $"Done with \"{activeSkill.Id}\". \n\n What skill would you like to call?", cancellationToken);
}

允許使用者取消技能

主要對話會覆寫 on continue dialog 方法的預設行為 ,以允許使用者取消目前的技能,如果有的話。 在 方法內:

  • 如果有作用中的技能,且使用者傳送「中止」訊息,請取消所有對話方塊,並將主要對話排入佇列,以從頭開始重新開機。
  • 然後,呼叫 on continue dialog 方法的基底實作 ,以繼續處理目前的回合。

DialogRootBot\Dialogs\MainDialog.cs

protected override async Task<DialogTurnResult> OnContinueDialogAsync(DialogContext innerDc, CancellationToken cancellationToken = default)
{
    // This is an example on how to cancel a SkillDialog that is currently in progress from the parent bot.
    var activeSkill = await _activeSkillProperty.GetAsync(innerDc.Context, () => null, cancellationToken);
    var activity = innerDc.Context.Activity;
    if (activeSkill != null && activity.Type == ActivityTypes.Message && activity.Text.Equals("abort", StringComparison.OrdinalIgnoreCase))
    {
        // Cancel all dialogs when the user says abort.
        // The SkillDialog automatically sends an EndOfConversation message to the skill to let the
        // skill know that it needs to end its current dialogs, too.
        await innerDc.CancelAllDialogsAsync(cancellationToken);
        return await innerDc.ReplaceDialogAsync(InitialDialogId, "Canceled! \n\n What skill would you like to call?", cancellationToken);
    }

    return await base.OnContinueDialogAsync(innerDc, cancellationToken);
}

活動處理常式邏輯

由於每個回合的技能邏輯都是由主要對話處理,所以活動處理常式看起來會像其他對話範例一樣。

DialogRootBot\Bots\RootBot.cs

public class RootBot<T> : ActivityHandler
    where T : Dialog
private readonly ConversationState _conversationState;
private readonly Dialog _mainDialog;

public RootBot(ConversationState conversationState, T mainDialog)
{
    _conversationState = conversationState;
    _mainDialog = mainDialog;
}
public override async Task OnTurnAsync(ITurnContext turnContext, CancellationToken cancellationToken = default)
{
    if (turnContext.Activity.Type != ActivityTypes.ConversationUpdate)
    {
        // Run the Dialog with the Activity.
        await _mainDialog.RunAsync(turnContext, _conversationState.CreateProperty<DialogState>("DialogState"), cancellationToken);
    }
    else
    {
        // Let the base class handle the activity.
        await base.OnTurnAsync(turnContext, cancellationToken);
    }

    // Save any state changes that might have occurred during the turn.
    await _conversationState.SaveChangesAsync(turnContext, false, cancellationToken);
}

服務註冊

使用技能對話所需的服務,與一般技能取用者所需的服務相同。 如需必要服務的討論,請參閱如何 實作技能取 用者。

測試根目錄 Bot

您可以在模擬器中測試技能取用者,就像是一般 Bot 一樣;不過,您必須同時執行技能與技能取用者 Bot。 如需如何設定技能的資訊,請參閱如何使用 技能 內的對話方塊。

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

  1. 在本機電腦上執行對話技能 Bot 和對話根 Bot。 如果您需要指示,請參閱範例的 C# JavaScript 、JAVA Python。 README
  2. 使用模擬器來測試 Bot。
    • 當您第一次加入交談時,Bot 會顯示歡迎訊息,並詢問您想要呼叫的技能。 此範例的技能 Bot 只有一個技能。
    • 選取 DialogSkillBot
  3. 然後,Bot 會要求您選擇技能的動作。 選擇 [BookFlight]。
    1. 回答提示。
    2. 技能會完成,而根 Bot 會顯示預約詳細資料,然後再提示您想要呼叫的技能。
  4. 再次選取 DialogSkillBot 和 「BookFlight」。
    1. 回答第一個提示,然後輸入「中止」以中斷技能。
    2. 根 Bot 會取消技能,並提示您輸入您想要呼叫的技能。

深入瞭解偵錯

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

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

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

其他資訊

瞭解如何實作技能取用者 ,以瞭解如何一般實作技能取用者。