Partilhar via


Usar diálogos dentro de uma habilidade

APLICA-SE A: SDK v4

Este artigo demonstra como criar uma habilidade que suporta várias ações. Ele suporta essas ações usando diálogos. A caixa de diálogo principal recebe a entrada inicial do consumidor de habilidades e, em seguida, inicia a ação apropriada. Para obter informações sobre como implementar o consumidor de habilidades para o código de exemplo associado, consulte como consumir uma habilidade usando caixas de diálogo.

Este artigo pressupõe que você já esteja familiarizado com a criação de habilidades. Para saber como criar um bot de habilidades em geral, veja como implementar uma habilidade.

Nota

Os SDKs JavaScript, C# e Python do Bot Framework continuarão a ser suportados, no entanto, o Java SDK está sendo desativado com suporte final de longo prazo terminando em novembro de 2023.

Os bots existentes construídos com o Java SDK continuarão a funcionar.

Para a criação de novos bots, considere usar o Microsoft Copilot Studio e leia sobre como escolher a solução de copilot certa.

Para obter mais informações, consulte O futuro da criação de bots.

Pré-requisitos

Nota

O Language Understanding (LUIS) será aposentado em 1 de outubro de 2025. A partir de 1 de abril de 2023, não será possível criar novos recursos LUIS. Uma versão mais recente do entendimento de idiomas agora está disponível como parte do Azure AI Language.

O entendimento de linguagem conversacional (CLU), um recurso do Azure AI Language, é a versão atualizada do LUIS. Para obter mais informações sobre o suporte à compreensão de linguagem no Bot Framework SDK, consulte Compreensão de linguagem natural.

Sobre este exemplo

O exemplo skillDialog de habilidades inclui projetos para dois bots:

  • O bot raiz de diálogo, que usa uma classe de diálogo de habilidade para consumir uma habilidade.
  • O bot de habilidade de diálogo, que usa uma caixa de diálogo para lidar com atividades provenientes de consumidores de habilidades. Essa habilidade é uma adaptação do exemplo de bot principal. (Para obter mais informações sobre o bot principal, veja como adicionar compreensão de linguagem natural ao seu bot.)

Este artigo se concentra em como usar uma caixa de diálogo dentro de um bot de habilidade para gerenciar várias ações.

Para obter informações sobre o bot consumidor de habilidades, consulte como consumir uma habilidade usando caixas de diálogo.

Recursos

Para bots implantados, a autenticação de bot para bot requer que cada bot participante tenha uma identidade válida. No entanto, você pode testar habilidades e consumidores de habilidades localmente com o Bot Framework Emulator sem informações de identidade.

Para disponibilizar a habilidade para bots voltados para o usuário, registre a habilidade no Azure. Para obter mais informações, consulte como registrar um bot com o Serviço de Bot do Azure AI.

Opcionalmente, o bot de habilidade pode usar um modelo LUIS de reserva de voo. Para usar esse modelo, use o arquivo CognitiveModels/FlightBooking.json para criar, treinar e publicar o modelo LUIS.

Configuração da aplicação

  1. Opcionalmente, adicione as informações de identidade da habilidade ao arquivo de configuração da habilidade. (Se o consumidor de habilidade ou habilidade especificar uma identidade, ambos devem.)

  2. Se você estiver usando o modelo LUIS, adicione o ID do aplicativo LUIS, a chave da API e o nome do host da 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": [ "*" ]
}

Lógica de roteamento de atividades

A habilidade suporta alguns recursos diferentes. Pode reservar um voo ou obter o clima para uma cidade. Além disso, se receber uma mensagem fora de qualquer um destes contextos, pode utilizar o LUIS para tentar interpretar a mensagem. O manifesto da habilidade descreve essas ações, seus parâmetros de entrada e saída e os pontos finais da habilidade. De notar, a habilidade pode lidar com um evento "BookFlight" ou "GetWeather". Ele também pode lidar com atividades de mensagem.

A habilidade define uma caixa de diálogo de roteamento de atividade que usa para selecionar qual ação iniciar, com base na atividade inicial recebida do consumidor de habilidades. Se fornecido, o modelo LUIS pode reconhecer as intenções book-flight e get-weather em uma mensagem inicial.

A ação livro-voo é um processo de várias etapas, implementado como um diálogo separado. Quando a ação começa, as atividades de entrada são tratadas por essa caixa de diálogo. A ação get-weather tem lógica de espaço reservado que seria substituída em um bot totalmente implementado.

A caixa de diálogo de roteamento de atividades inclui código para:

As caixas de diálogo usadas na habilidade herdam da classe de diálogo do componente. Para obter mais informações sobre caixas de diálogo de componentes, consulte como gerenciar a complexidade da caixa de diálogo.

Inicializar a caixa de diálogo

A caixa de diálogo de roteamento de atividades inclui uma caixa de diálogo filho para reservar um voo. A caixa de diálogo principal em cascata tem uma etapa que iniciará uma ação com base na atividade inicial recebida.

Também aceita um reconhecedor LUIS. Se esse reconhecedor for inicializado, a caixa de diálogo o usará para interpretar a intenção de uma atividade de mensagem inicial.

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

Processar uma atividade inicial

Na primeira (e única) etapa da caixa de diálogo principal em cascata, a habilidade verifica o tipo de atividade de entrada.

  • As atividades de evento são encaminhadas para um manipulador de atividades on event que inicia a ação apropriada com base no nome do evento.
  • As atividades de mensagem são encaminhadas para um manipulador de atividades na mensagem que executa processamento adicional antes de decidir o que fazer.

Se a habilidade não reconhecer o tipo de atividade de entrada ou o nome do evento, ela enviará uma mensagem de erro e terminará.

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

Manipular atividades de mensagens

Se o reconhecedor LUIS estiver configurado, a habilidade chamará LUIS e, em seguida, iniciará uma ação com base na intenção. Se o reconhecedor LUIS não estiver configurado ou a intenção não for suportada, a habilidade enviará uma mensagem de erro e terminará.

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

Iniciar uma ação em várias etapas

A ação de reservar voo inicia uma caixa de diálogo de várias etapas para obter os detalhes da reserva do usuário.

A ação get-weather não é implementada. Atualmente, ele envia uma mensagem de espaço reservado e, em seguida, termina.

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

Devolver um resultado

A habilidade inicia uma caixa de diálogo de reserva para a ação de livro-voo. Como a caixa de diálogo de roteamento de atividades tem apenas uma etapa, quando a caixa de diálogo de reserva termina, a caixa de diálogo de roteamento de atividades também termina e o resultado da caixa de diálogo de reserva se torna o resultado da caixa de diálogo de roteamento de atividades.

A ação get-weather simplesmente termina sem definir um valor de retorno.

Cancelar uma ação de várias etapas

A caixa de diálogo de reserva e a caixa de diálogo de resolução de data filho derivam da caixa de diálogo base cancel-and-help, que verifica as mensagens do usuário.

  • Em "ajuda" ou "?", ele exibe uma mensagem de ajuda e, em seguida, continua o fluxo de conversa no turno seguinte.
  • Em "cancelar" ou "sair", ele cancela todas as caixas de diálogo, o que encerra a habilidade.

Para obter mais informações, consulte como lidar com interrupções do usuário.

Registo do serviço

Os serviços necessários para essa habilidade são os mesmos necessários para um bot de habilidade em geral. Veja como implementar uma habilidade para uma discussão sobre os serviços necessários.

Manifesto de habilidades

Um manifesto de habilidade é um arquivo JSON que descreve as atividades que a habilidade pode executar, seus parâmetros de entrada e saída e os pontos de extremidade da habilidade. O manifesto contém as informações que você precisa para acessar a habilidade de outro bot.

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"
        }
      }
    }
  }
}

O esquema de manifesto de habilidade é um arquivo JSON que descreve o esquema do manifesto de habilidade. A versão mais recente do esquema é v2.1.

Teste o bot de habilidades

Você pode testar a habilidade no emulador com o consumidor de habilidade. Para fazer isso, você precisa executar os bots de consumidor de habilidade e habilidade ao mesmo tempo. Veja como usar uma caixa de diálogo para consumir uma habilidade para obter informações sobre como configurar a habilidade.

Baixe e instale o mais recente Bot Framework Emulator.

  1. Execute o bot de habilidade de diálogo e o bot raiz de diálogo localmente em sua máquina. Se precisar de instruções, consulte o arquivo de README exemplo para C#, JavaScript, Java ou Python.
  2. Use o emulador para testar o bot.
    • Quando você entra pela primeira vez na conversa, o bot exibe uma mensagem de boas-vindas e pergunta qual habilidade você gostaria de chamar. O bot de habilidade para este exemplo tem apenas uma habilidade.
    • Selecione DialogSkillBot.
  3. Em seguida, o bot pede que você escolha uma ação para a habilidade. Escolha "BookFlight".
    1. A habilidade começa sua ação livro-vôo; Responda às solicitações.
    2. Quando a habilidade for concluída, o bot raiz exibirá os detalhes da reserva antes de solicitar novamente a habilidade que você gostaria de chamar.
  4. Selecione DialogSkillBot novamente e "BookFlight".
    1. Responda ao primeiro prompt e digite "cancelar" para cancelar a ação.
    2. O bot de habilidades termina sem concluir a ação, e o consumidor solicita a habilidade que você gostaria de chamar.

Mais sobre depuração

Como o tráfego entre habilidades e consumidores de habilidades é autenticado, há etapas extras ao depurar esses bots.

  • O consumidor de competências e todas as competências que consome, direta ou indiretamente, devem estar a correr.
  • Se os bots estiverem sendo executados localmente e se algum deles tiver um ID de aplicativo e senha, todos os bots deverão ter IDs e senhas válidas.
  • Se todos os bots estiverem implantados, veja como Depurar um bot de qualquer canal usando devtunnel.
  • Se alguns dos bots estiverem sendo executados localmente e alguns forem implantados, veja como Depurar uma habilidade ou consumidor de habilidades.

Caso contrário, você pode depurar um consumidor de habilidade ou habilidade da mesma forma que depurar outros bots. Para obter mais informações, consulte Depurando um bot e Depurando com o emulador do Bot Framework.

Informações adicionais