Поделиться через


Используйте диалоги в навыке

ОБЛАСТЬ ПРИМЕНЕНИЯ: ПАКЕТ SDK версии 4

В этой статье показано, как создать навык, который поддерживает несколько действий. Для этого в нем используются диалоги. Основной диалог получает исходные входные данные от потребителя навыка, а затем запускает соответствующее действие. Сведения о реализации потребителя навыков и соответствующий пример кода см. в статье об использовании навыков с помощью диалогов.

В этой статье предполагается, что вы уже знакомы с созданием навыков. Сведения о создании бота навыка см. в статье о реализации навыка.

Примечание.

Пакеты SDK для JavaScript, C# и Python для Bot Framework по-прежнему будут поддерживаться, однако поддержка пакета SDK Java будет прекращена, и окончательная долгосрочная поддержка завершится в ноябре 2023 года.

Существующие боты, созданные с помощью пакета SDK для Java, будут продолжать функционировать.

Для создания нового бота рекомендуется использовать Microsoft Copilot Studio и прочитать о выборе правильного решения copilot.

Дополнительные сведения см. в статье "Будущее создания бота".

Предварительные условия

Примечание.

Языковое понимание (LUIS) будет прекращено 1 октября 2025 года. Начиная с 1 апреля 2023 года вы не сможете создавать новые ресурсы LUIS. Новая версия распознавания речи теперь доступна как часть языка ИИ Azure.

Распознавание и понимание разговорной речи (CLU), функция Azure AI Language, является обновленной версией LUIS. Дополнительные сведения о поддержке распознавания речи в пакете SDK Bot Framework см. в разделе "Распознавание естественного языка".

Об этом примере

В пример skills skillDialog включены проекты двух ботов:

  • Корневой бот диалога, который использует диалог навыка для применения навыка.
  • Бот диалогового навыка, который использует диалог для обработки действий от пользователей навыков. Этот навык является адаптацией примера core bot. (Чтобы получить больше сведений о базовом боте, см. статью о добавлении функции распознавания естественного языка в бота.)

В этой статье описывается, как использовать диалоговое окно в боте навыка для управления несколькими действиями.

Сведения о боте, выполняющем роль потребителя навыка, см. в статье об использовании навыка с помощью диалогов.

Ресурсы

Для развернутых ботов аутентификация между ботами требует, чтобы каждый участвующий бот обладал допустимой идентичностью. Однако вы можете протестировать навыки и их потребителей локально с помощью эмулятора Bot Framework без идентификационной информации.

Чтобы сделать навык доступным для пользователей, взаимодействующих с ботами, зарегистрируйте навык в Azure. Дополнительные сведения см. в статье о регистрации бота в службе Azure AI Bot Service.

Кроме того, бот навыка может использовать модель LUIS для бронирования авиабилетов. Чтобы применить эту модель, с помощью файла CognitiveModels/FlightBooking.json создайте, обучите и опубликуйте модель LUIS.

Конфигурация приложений

  1. При необходимости добавьте сведения об идентификации навыка в файл конфигурации навыка. (Если либо навык, либо пользователь навыка указывает удостоверение, оба должны это сделать.)

  2. Если вы используете модель LUIS, добавьте идентификатор приложения LUIS, ключ API и имя узла API.

DialogSkillBot\appsettings.json

{
  "MicrosoftAppType": "",
  "MicrosoftAppId": "",
  "MicrosoftAppPassword": "",
  "MicrosoftAppTenantId": "",
  "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": [ "*" ]
}

Логика маршрутизации активности

Этот навык поддерживает несколько разных функций. Он может забронировать авиабилет и получить прогноз погоды для города. Кроме того, если он получает сообщение за пределами одного из этих контекстов, он может использовать LUIS, чтобы попытаться интерпретировать сообщение. Манифест навыка описывает эти действия, входные и выходные параметры, а также конечные точки навыка. Обратите внимание: навык может обрабатывать события BookFlight и GetWeather. Он также может обрабатывать действия, связанные с сообщениями.

Навык определяет используемый диалог маршрутизации активности, который служит для выбора действия, запускаемого в зависимости от входящего действия от пользователя навыка. Если предоставлена модель LUIS, она может распознавать в исходном сообщении намерения бронирования авиабилетов и получения прогноза погоды.

Действие по бронированию рейса — это многоэтапный процесс, который осуществляется в отдельном диалоговом окне. Как только действие начнется, входящие действия обрабатываются этим диалогом. Действие get-weather содержит логику заполнителя, которую для полноценной реализации бота нужно будет заменить.

Диалог маршрутизации действий содержит код для выполнения следующих действий:

Используемые в этом навыке диалоги унаследованы от класса компонентного диалога. Дополнительные сведения о компонентных диалогах см. в статье об управлении сложностью диалогов.

инициализация диалога;

Диалог маршрутизации активности включает в себя дочерний диалог для бронирования авиабилетов. В главном каскадном диалоге есть один шаг, который запускает действие на основе полученного исходного действия.

Он также принимает распознаватель LUIS. Если этот распознаватель инициализирован и готов к работе, диалог будет использовать его для интерпретации намерения исходного сообщения.

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

выполнить начальное действие

На первом (и только первом) шаге в главном каскадном диалоге навык проверяет тип входящей активности.

  • Действия событий передаются обработчику действий события, который запускает соответствующее действие на основе имени события.
  • Действия с сообщениями передаются обработчику выполнения действий с сообщениями, который выполняет дополнительную обработку, прежде чем решать, что делать дальше.

Если навык не может распознать тип входящего действия или имя события, он выдает сообщение об ошибке и завершает работу.

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

управление действиями сообщений

Если настроен распознаватель LUIS, то навык вызывает LUIS и запускает действие на основе намерения. Если распознаватель LUIS не настроен или намерение не поддерживается, навык отправляет сообщение об ошибке и заканчивается.

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

запуск многошагового действия;

Действие book-flight инициирует многошаговый диалог для получения сведений о бронировании от пользователя.

Действие get-weather не реализовано. Сейчас оно отправляет сообщение-заглушку, а затем завершает работу.

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

возврат результата.

Навык запускает диалог бронирования для действия "заказать перелет". Так как диалог activity-routing содержит только один шаг, при завершении диалога бронирования завершается и диалог activity-routing, а результат выполнения диалога бронирования становится результатом диалога activity-routing.

Действие get-weather просто завершается без установки возвращаемого значения.

Отмена многошагового действия

Диалог бронирования и его дочерний диалог date-resolver являются производными от базового диалога cancel-and-help, который проверяет получение сообщений от пользователя.

  • При вводе help или ? он отображает справочную информацию, а затем продолжает поток беседы на следующем шаге.
  • При вводе cancel или quit он завершает все диалоги, завершая работу навыка.

Дополнительные сведения см. в разделе о обработке прерываний пользователя.

Регистрация службы

Для этого навыка нужны те же службы, что и для любого стандартного бота навыка. Узнайте, как реализовать навык, чтобы обсудить требуемые службы.

Манифест навыка

Манифест навыка — это файл в формате JSON с описанием действий, которые может выполнять навык, его входных и выходных параметров, а также конечных точек навыка. Манифест содержит сведения, необходимые вам для доступа к навыку другого бота.

DialogSkillBot\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 с описанием схемы манифеста навыков. Последняя версия схемы — версия 2.1.

Тест бота способностей

Вы можете проверить навык в эмуляторе с помощью потребителя навыков. Для этого одновременно запустите оба бота (навыка и потребителя навыка). Посмотрите, как используется диалог для применения навыка, чтобы получить информацию о его настройке.

Скачайте и установите последнюю версию Bot Framework Emulator.

  1. Запустите бота диалогового навыка и диалоговый корневой бот локально на вашем компьютере. Если вам нужны инструкции, ознакомьтесь с файлом примера README для C#, JavaScript, Java, или Python.
  2. Примените эмулятор для тестирования бота.
    • Когда вы впервые присоединяетесь к беседе, бот отображает приветственное сообщение и спрашивает, какой навык вы хотите активировать. В этом примере у бота с навыками есть всего один навык.
    • Выберите DialogSkillBot.
  3. Затем бот просит выбрать действие для навыка. Выберите BookFlight.
    1. Навык начинает действие бронирования полета; ответьте на подсказки.
    2. Когда навык завершит работу, корневой бот отобразит сведения о резервировании и снова предложит вам вызвать тот навык.
  4. Снова выберите DialogSkillBot и BookFlight.
    1. Ответьте на первый вопрос, а затем введите cancel, чтобы отменить действие.
    2. Бот навыка завершит работу, не завершив действие, а потребитель снова предложит вам вызвать навык.

Дополнительные сведения об отладке

Так как трафик между навыками и потребителями навыков проходит проверку подлинности, при отладке таких ботов выполняются дополнительные действия.

  • Потребитель навыка и все навыки, которые он потребляет, прямо или косвенно, должны работать.
  • Если боты работают локально и если у любого из ботов есть идентификатор приложения и пароль, все боты должны иметь действительные идентификаторы и пароли.
  • Если боты развернуты, узнайте, как отладить бота из любого канала с помощью devtunnel.
  • Если некоторые боты выполняются локально, а некоторые развернуты, см. инструкции по отладке навыка или его потребителя.

В противном случае можно отладить потребителя навыка или сам навык так же, как отладить других ботов. Дополнительные сведения см. в статье отладка бота и отладка с помощью эмулятора Bot Framework.

Дополнительная информация: