使用技能內的對話Use dialogs within a skill

適用于: SDK v4APPLIES TO: SDK v4

本文示範如何建立可支援多個動作的技能。This article demonstrates how to create a skill that supports multiple actions. 其會使用對話來支援這些動作。It supports these actions using dialogs. 主要對話會接收來自技能取用者的初始輸入,然後啟動適當的動作。The main dialog receives the initial input from the skill consumer, and then starts the appropriate action. 如需有關如何為相關聯的範例程式碼實作技能取用者的詳細資訊,請參閱如 使用對話來取用技能For information about implementing the skill consumer for the associated sample code, see how to consume a skill using dialogs.

本文假設您已經很熟悉建立技能。This article assumes you are already familiar with creating skills. 若要了解通常如何建立技能 Bot,請參閱如何實作技能For how to create a skill bot in general, see how to implement a skill.

PrerequisitesPrerequisites

注意

從4.11 版開始,您不需要應用程式識別碼和密碼,即可在模擬器的本機測試技能。Starting with version 4.11, you do not need an app ID and password to test a skill locally in the Emulator. 將您的技能部署至 Azure 時,仍需要 Azure 訂用帳戶。An Azure subscription is still required to deploy your skill to Azure.

關於此範例About this sample

技能 skillDialog 範例包含兩個 Bot 的專案:The skills skillDialog sample includes projects for two bots:

  • 對話根 Bot,其使用 技能對話 類別來取用技能。The dialog root bot, which uses a skill dialog class to consume a skill.
  • 對話技能 Bot,其使用對話來處理來自技能取用者的活動。The dialog skill bot, which uses a dialog to handle activities coming from skill consumers. 此技能是 核心 Bot 範例的調適。This skill is an adaptation of the core bot sample. (如需核心 Bot 的詳細資訊,請參閱如何將自然語言理解新增至 Bot。)(For more about the core bot, see how to add natural language understanding to your bot.)

本文著重於如何在技能 Bot 內使用對話來管理多個動作。This article focuses on how to use a dialogs within a skill bot to manage multiple actions.

如需有關技能取用者 Bot 的詳細資訊,請參閱如何使用對話來取用技能For information about the skill consumer bot, see how to consume a skill using dialogs.

資源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.

若要將技能提供給使用者面向的 bot,請向 Azure 註冊技能。To make the skill available to user-facing bots, register the skill with Azure. 您可以使用 Bot 通道註冊。You can use a Bot Channels Registration. 如需詳細資訊,請參閱如何使用 Azure Bot Service 註冊 BotFor more information, see how to register a bot with Azure Bot Service.

(選擇性) 技能 bot 可使用航班預訂 LUIS 模型。Optionally, the skill bot can use a flight-booking LUIS model. 若要使用此模型,請使用 CognitiveModels/FlightBooking.json 檔案來建立、訓練和發佈 LUIS 模型。To use this model, use the CognitiveModels/FlightBooking.json file to create, train, and publish the LUIS model.

應用程式設定Application configuration

  1. (選擇性)將技能的應用程式識別碼和密碼新增至技能的設定檔。Optionally, add the skill's app ID and password to the skill's configuration file. (如果技能或技能取用者使用應用程式識別碼和密碼,兩者都必須是。 ) (If either the skill or skill consumer uses an app ID and password, both must.)

  2. 如果您使用 LUIS 模型,請新增 LUIS 應用程式識別碼、API 金鑰和 API 主機名稱。If you are using the LUIS model, Add the LUIS app ID, API key, and API host name.

DialogSkillBot\appsettings.jsonDialogSkillBot\appsettings.json

{
  "MicrosoftAppId": "",
  "MicrosoftAppPassword": "",
  "ConnectionName": "",

  "LuisAppId": "",
  "LuisAPIKey": "",
  "LuisAPIHostName": "",

  // 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": [ "*" ]
}

活動路線規劃邏輯Activity-routing logic

此技能支援幾項不同的功能。The skill supports a couple different features. 其可預訂航班或取得某個城市的氣象。It can book a flight or get the weather for a city. 此外,如果其收到上述任一內容以外的訊息,則可使用 LUIS 來嘗試解譯訊息。In addition, if it receives a message outside either of these contexts, it can use LUIS to try to interpret the message. 技能的資訊清單會描述這些動作、其輸入和輸出參數,以及技能的端點。The skill's manifest describes these actions, their input and output parameters, and the skill's endpoints. 請注意,技能可以處理 "BookFlight" 或 "GetWeather" 事件。Of note, the skill can handle a "BookFlight" or "GetWeather" event. 也可以處理訊息活動。It can also handle message activities.

技能會根據來自技能取用者的初始傳入活動,定義活動路線規劃對話,用以選取要起始的動作。The skill defines an activity-routing dialog it uses to select which action to initiate, based on the initial incoming activity from the skill consumer. 若已提供,LUIS 模型即可辨識初始訊息中的預訂航班和取得氣象意圖。If provided, the LUIS model can recognize book-flight and get-weather intents in an initial message.

預訂航班動作是一個多步驟的程序,會以個別的對話實作。The book-flight action is a multi-step process, implemented as a separate dialog. 動作開始後,該對話就會處理傳入活動。Once the action begins, incoming activities are handled by that dialog. 在完全實作的 Bot 中,取得氣象動作的預留位置邏輯會被取代。The get-weather action has placeholder logic that would be replaced in a fully implemented bot.

活動路線規劃對話包含用以執行下列作業的程式碼:The activity-routing dialog includes code to:

技能中使用的對話繼承自「元件對話」類別。The dialogs used in the skill inherit from the component dialog class. 如需元件對話的詳細資訊,請參閱如何管理對話複雜性For more about component dialogs, see how to manage dialog complexity.

初始化對話Initialize the dialog

活動路線規劃對話包含用於預訂航班的子對話。The activity-routing dialog includes a child dialog for booking a flight. 主要瀑布式對話有一個步驟會根據所收到的初始活動來啟動動作。The main waterfall dialog has one step that will start an action based on the initial activity received.

其也會接受 LUIS 辨識器。It also accepts a LUIS recognizer. 如果已初始化此辨識器,對話會將其用來解譯初始訊息活動的意圖。If this recognizer is initialized, the dialog will use it to interpret the intent of an initial message activity.

DialogSkillBot\Dialogs\ActivityRouterDialog.csDialogSkillBot\Dialogs\ActivityRouterDialog.cs

private readonly DialogSkillBotRecognizer _luisRecognizer;

public ActivityRouterDialog(DialogSkillBotRecognizer luisRecognizer)
    : base(nameof(ActivityRouterDialog))
{
    _luisRecognizer = luisRecognizer;

    AddDialog(new BookingDialog());
    AddDialog(new WaterfallDialog(nameof(WaterfallDialog), new WaterfallStep[] { ProcessActivityAsync }));

    // The initial child Dialog to run.
    InitialDialogId = nameof(WaterfallDialog);
}

處理初始活動Process an initial activity

在主要瀑布式對話的第一個 (且唯一的) 步驟中,技能會檢查傳入的活動類型。In the first (and only) step of the main waterfall dialog, the skill checks the incoming activity type.

  • 事件活動會轉送到「事件活動」處理常式,以根據事件的名稱啟動適當的動作。Event activities are forwarded to an on event activity handler that starts the appropriate action based on the name of the event.
  • 訊息活動會轉送到「訊息活動」處理常式,以在決定該做什麼之前執行額外處理。Message activities are forwarded to an on message activity handler that performs additional processing before deciding what to do.

如果技能無法辨識傳入活動的類型或事件的名稱,則會傳送錯誤訊息並結束。If the skill doesn't recognize the type of the incoming activity or the name of the event, it sends an error message and ends.

DialogSkillBot\Dialogs\ActivityRouterDialog.csDialogSkillBot\Dialogs\ActivityRouterDialog.cs

private async Task<DialogTurnResult> ProcessActivityAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
    // A skill can send trace activities, if needed.
    await stepContext.Context.TraceActivityAsync($"{GetType().Name}.ProcessActivityAsync()", label: $"Got ActivityType: {stepContext.Context.Activity.Type}", cancellationToken: cancellationToken);

    switch (stepContext.Context.Activity.Type)
    {
        case ActivityTypes.Event:
            return await OnEventActivityAsync(stepContext, cancellationToken);

        case ActivityTypes.Message:
            return await OnMessageActivityAsync(stepContext, cancellationToken);

        default:
            // We didn't get an activity type we can handle.
            await stepContext.Context.SendActivityAsync(MessageFactory.Text($"Unrecognized ActivityType: \"{stepContext.Context.Activity.Type}\".", inputHint: InputHints.IgnoringInput), cancellationToken);
            return new DialogTurnResult(DialogTurnStatus.Complete);
    }
}
// This method performs different tasks based on the event name.
private async Task<DialogTurnResult> OnEventActivityAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
    var activity = stepContext.Context.Activity;
    await stepContext.Context.TraceActivityAsync($"{GetType().Name}.OnEventActivityAsync()", label: $"Name: {activity.Name}. Value: {GetObjectAsJsonString(activity.Value)}", cancellationToken: cancellationToken);

    // Resolve what to execute based on the event name.
    switch (activity.Name)
    {
        case "BookFlight":
            return await BeginBookFlight(stepContext, cancellationToken);

        case "GetWeather":
            return await BeginGetWeather(stepContext, cancellationToken);

        default:
            // We didn't get an event name we can handle.
            await stepContext.Context.SendActivityAsync(MessageFactory.Text($"Unrecognized EventName: \"{activity.Name}\".", inputHint: InputHints.IgnoringInput), cancellationToken);
            return new DialogTurnResult(DialogTurnStatus.Complete);
    }
}

處理訊息活動Handle message activities

如果已設定 LUIS 辨識器,技能會呼叫 LUIS,然後根據意圖啟動動作。If the LUIS recognizer is configured, the skill calls LUIS and then starts an action based on the intent. 如果未設定 LUIS 辨識器或不支援此意圖,則技能會傳送錯誤訊息並結束。If the LUIS recognizer is not configured or the intent is not supported, the skill sends an error message and ends.

DialogSkillBot\Dialogs\ActivityRouterDialog.csDialogSkillBot\Dialogs\ActivityRouterDialog.cs

// This method just gets a message activity and runs it through LUIS. 
private async Task<DialogTurnResult> OnMessageActivityAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
    var activity = stepContext.Context.Activity;
    await stepContext.Context.TraceActivityAsync($"{GetType().Name}.OnMessageActivityAsync()", label: $"Text: \"{activity.Text}\". Value: {GetObjectAsJsonString(activity.Value)}", cancellationToken: cancellationToken);

    if (!_luisRecognizer.IsConfigured)
    {
        await stepContext.Context.SendActivityAsync(MessageFactory.Text("NOTE: LUIS is not configured. To enable all capabilities, add 'LuisAppId', 'LuisAPIKey' and 'LuisAPIHostName' to the appsettings.json file.", inputHint: InputHints.IgnoringInput), cancellationToken);
    }
    else
    {
        // Call LUIS with the utterance.
        var luisResult = await _luisRecognizer.RecognizeAsync<FlightBooking>(stepContext.Context, cancellationToken);

        // Create a message showing the LUIS results.
        var sb = new StringBuilder();
        sb.AppendLine($"LUIS results for \"{activity.Text}\":");
        var (intent, intentScore) = luisResult.Intents.FirstOrDefault(x => x.Value.Equals(luisResult.Intents.Values.Max()));
        sb.AppendLine($"Intent: \"{intent}\" Score: {intentScore.Score}");

        await stepContext.Context.SendActivityAsync(MessageFactory.Text(sb.ToString(), inputHint: InputHints.IgnoringInput), cancellationToken);

        // Start a dialog if we recognize the intent.
        switch (luisResult.TopIntent().intent)
        {
            case FlightBooking.Intent.BookFlight:
                return await BeginBookFlight(stepContext, cancellationToken);

            case FlightBooking.Intent.GetWeather:
                return await BeginGetWeather(stepContext, cancellationToken);

            default:
                // Catch all for unhandled intents.
                var didntUnderstandMessageText = $"Sorry, I didn't get that. Please try asking in a different way (intent was {luisResult.TopIntent().intent})";
                var didntUnderstandMessage = MessageFactory.Text(didntUnderstandMessageText, didntUnderstandMessageText, InputHints.IgnoringInput);
                await stepContext.Context.SendActivityAsync(didntUnderstandMessage, cancellationToken);
                break;
        }
    }

    return new DialogTurnResult(DialogTurnStatus.Complete);
}

開始多步驟動作Begin a multi-step action

預訂航班動作會啟動多步驟對話,以取得使用者的預訂詳細資料。The book-flight action starts a multi-step dialog to get the booking details from the user.

並未實作取得氣象動作。The get-weather action is not implemented. 目前,其會傳送預留位置訊息,然後結束。Currently, it sends a placeholder message and then ends.

DialogSkillBot\Dialogs\ActivityRouterDialog.csDialogSkillBot\Dialogs\ActivityRouterDialog.cs

private async Task<DialogTurnResult> BeginBookFlight(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
    var activity = stepContext.Context.Activity;
    var bookingDetails = new BookingDetails();
    if (activity.Value != null)
    {
        bookingDetails = JsonConvert.DeserializeObject<BookingDetails>(JsonConvert.SerializeObject(activity.Value));
    }

    // Start the booking dialog.
    var bookingDialog = FindDialog(nameof(BookingDialog));
    return await stepContext.BeginDialogAsync(bookingDialog.Id, bookingDetails, cancellationToken);
}
private static async Task<DialogTurnResult> BeginGetWeather(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
    var activity = stepContext.Context.Activity;
    var location = new Location();
    if (activity.Value != null)
    {
        location = JsonConvert.DeserializeObject<Location>(JsonConvert.SerializeObject(activity.Value));
    }

    // We haven't implemented the GetWeatherDialog so we just display a TODO message.
    var getWeatherMessageText = $"TODO: get weather for here (lat: {location.Latitude}, long: {location.Longitude}";
    var getWeatherMessage = MessageFactory.Text(getWeatherMessageText, getWeatherMessageText, InputHints.IgnoringInput);
    await stepContext.Context.SendActivityAsync(getWeatherMessage, cancellationToken);
    return new DialogTurnResult(DialogTurnStatus.Complete);
}

傳回結果Return a result

技能會針對預訂航班動作開始預訂對話。The skill starts a booking dialog for the book-flight action. 由於活動路線規劃對話只有一個步驟,因此當預訂對話結束時,活動路線規劃對話也會結束,而預訂對話的對話結果會變成活動路線規劃對話的對話結果。Since the activity-routing dialog has just one step, when the booking dialog ends, the activity-routing dialog also ends, and the dialog result from the booking dialog becomes the dialog result for the activity-routing dialog.

若未設定傳回值,取得氣象動作就會直接結束。The get-weather action simply ends without setting a return value.

取消多步驟動作Canceling a multi-step action

預訂對話及其子日期解析程式對話都衍生自基底「取消並說明」對話,而該對話會檢查來自使用者的訊息。The booking dialog and its child date-resolver dialog both derive from the base cancel-and-help dialog, which checks messages from the user.

  • 對於 "help" 或 "?",其會顯示說明訊息,然後在接下來的回合繼續交談流程。On "help" or "?", it displays a help message, and then continues the conversation flow on the following turn.
  • 對於 "cancel" 或 "quit",其會取消所有對話,因而結束技能。On "cancel" or "quit", it cancels all dialogs, which ends the skill.

如需詳細資訊,請參閱如何處理使用者中斷For more information, see how to handle user interruptions.

服務註冊Service registration

此技能所需的服務通常與技能 Bot 所需的服務相同。The services needed for this skill are the same as those needed for a skill bot in general. 如需必要服務的討論,請參閱如何實行技能See how to implement a skill for a discussion of the required services.

技能資訊清單Skill manifest

「技能資訊清單」 是 JSON 檔案,描述技能可執行的活動、其輸入和輸出參數,以及技能的端點。A skill manifest is a JSON file that describes the activities the skill can perform, its input and output parameters, and the skill's endpoints. 資訊清單包含從另一個 Bot 存取技能所需的資訊。The manifest contains the information you need to access the skill from another bot.

DialogSkillBot\wwwroot\manifest\dialogchildbot-manifest-1.0.jsonDialogSkillBot\wwwroot\manifest\dialogchildbot-manifest-1.0.json

{
  "$schema": "https://schemas.botframework.com/schemas/skills/skill-manifest-2.0.0.json",
  "$id": "DialogSkillBot",
  "name": "Skill bot with dialogs",
  "version": "1.0",
  "description": "This is a sample skill definition for multiple activity types.",
  "publisherName": "Microsoft",
  "privacyUrl": "https://dialogskillbot.contoso.com/privacy.html",
  "copyright": "Copyright (c) Microsoft Corporation. All rights reserved.",
  "license": "",
  "iconUrl": "https://dialogskillbot.contoso.com/icon.png",
  "tags": [
    "sample",
    "travel",
    "weather",
    "luis"
  ],
  "endpoints": [
    {
      "name": "default",
      "protocol": "BotFrameworkV3",
      "description": "Default endpoint for the skill.",
      "endpointUrl": "https://dialogskillbot.contoso.com/api/messages",
      "msAppId": "00000000-0000-0000-0000-000000000000"
    }
  ],
  "activities": {
    "bookFlight": {
      "description": "Books a flight (multi turn).",
      "type": "event",
      "name": "BookFlight",
      "value": {
        "$ref": "#/definitions/bookingInfo"
      },
      "resultValue": {
        "$ref": "#/definitions/bookingInfo"
      }
    },
    "getWeather": {
      "description": "Retrieves and returns the weather for the user's location.",
      "type": "event",
      "name": "GetWeather",
      "value": {
        "$ref": "#/definitions/location"
      },
      "resultValue": {
        "$ref": "#/definitions/weatherReport"
      }
    },
    "passthroughMessage": {
      "type": "message",
      "description": "Receives the user's utterance and attempts to resolve it using the skill's LUIS models.",
      "value": {
        "type": "object"
      }
    }
  },
  "definitions": {
    "bookingInfo": {
      "type": "object",
      "required": [
        "origin"
      ],
      "properties": {
        "origin": {
          "type": "string",
          "description": "This is the origin city for the flight."
        },
        "destination": {
          "type": "string",
          "description": "This is the destination city for the flight."
        },
        "travelDate": {
          "type": "string",
          "description": "The date for the flight in YYYY-MM-DD format."
        }
      }
    },
    "weatherReport": {
      "type": "array",
      "description": "Array of forecasts for the next week.",
      "items": [
        {
          "type": "string"
        }
      ]
    },
    "location": {
      "type": "object",
      "description": "Location metadata.",
      "properties": {
        "latitude": {
          "type": "number",
          "title": "Latitude"
        },
        "longitude": {
          "type": "number",
          "title": "Longitude"
        },
        "postalCode": {
          "type": "string",
          "title": "Postal code"
        }
      }
    }
  }
}

「技能資訊清單結構描述」是 JSON 檔案,可描述技能資訊清單的結構描述。The skill manifest schema is a JSON file that describes the schema of the skill manifest. 最新的架構版本為 2.1版。The latest schema version is v2.1.

測試技能 BotTest the skill bot

您可以透過技能取用者在模擬器中測試技能。You can test the skill in the Emulator with the skill consumer. 若要這麼做,您必須同時執行技能和技能取用者 Bot。To do so, you need to run both the skill and skill consumer bots at the same time. 如需如何設定技能的相關資訊,請參閱如何使用對話來取用技能See how to use a dialog to consume 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 dialog skill bot and dialog 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.
    • 當您第一次加入交談時,Bot 會顯示歡迎訊息,並詢問您想要呼叫的技能。When you first join the conversation, the bot displays a welcome message and asks you what skill you would like to call. 這個範例的技能 Bot 只有一個技能。The skill bot for this sample has just one skill.
    • 選取 DialogSkillBotSelect DialogSkillBot.
  3. Bot 接下來會要求您選擇技能的動作。The bot next asks you to choose an action for the skill. 選擇 "BookFlight"。Choose "BookFlight".
    1. 技能會開始其預訂航班動作;答覆提示。The skill begins its book-flight action; answer the prompts.
    2. 當技能完成時,根 Bot 會先顯示預訂詳細資料,然後再次提示您提供想要呼叫的技能。When the skill completes, the root bot displays the booking details before prompting again for the skill you'd like to call.
  4. 再次選取 DialogSkillBot 和 "BookFlight"。Select DialogSkillBot again and "BookFlight".
    1. 答覆第一個提示,然後輸入 "cancel" 以取消動作。Answer the first prompt, then enter "cancel" to cancel the action.
    2. 技能 Bot 會在未完成動作的情況下結束,而取用者會提示您提供想要呼叫的技能。The skill bot ends without completing the action, and the consumer prompts for the skill you'd like to call.

其他資訊Additional information