Compartilhar via


Usar diálogos em uma habilidade

APLICA-SE A: SDK v4

Este artigo demonstra como criar uma habilidade que dá suporte a várias ações. Ela dá suporte a essas ações por meio de diálogos. O diálogo principal recebe a entrada inicial do consumidor de habilidades e inicia a ação apropriada. Para obter informações sobre como implementar o consumidor de habilidades para o código de exemplo associado, confira como Consumir uma habilidade usando diálogos.

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, confira como Implementar uma habilidade.

Observação

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

Os bots existentes criados com o SDK para Java 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 copiloto certa.

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

Pré-requisitos

Observação

O reconhecimento de linguagem (LUIS) será desativado em 1º de outubro de 2025. A partir de 1º de abril de 2023, você não poderá criar recursos do LUIS. Uma versão mais recente do reconhecimento de linguagem já está disponível como parte da Linguagem de IA do Azure.

A compreensão da linguagem coloquial (CLU), um recurso da Linguagem de IA do Azure, é a versão atualizada do LUIS. Para obter mais informações sobre o suporte ao reconhecimento de linguagem no SDK do Bot Framework, confira Reconhecimento de linguagem natural.

Sobre este exemplo

O exemplo skills skillDialog inclui projetos para dois bots:

  • O bot raiz do diálogo, que usa uma classe de diálogo de habilidades para consumir uma habilidade.
  • O bot de skill de caixa de diálogo, que usa uma caixa de diálogo para lidar com atividades provenientes de consumidores de skill. Essa habilidade é uma adaptação da amostra de bot básico. (Para saber mais sobre o bot básico, confira como Adicionar o reconhecimento de idioma natural ao seu bot.)

Este artigo destaca como usar um diálogo em um bot de habilidades para gerenciar várias ações.

Para obter informações sobre o bot do consumidor de habilidades, confira como Consumir uma habilidade usando diálogos.

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, confira como registrar um bot no Serviço de Bot de IA do Azure.

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

Configuração de aplicativo

  1. Opcionalmente, adicione as informações de identidade da habilidade ao arquivo de configuração da habilidade. (Se a habilidade ou o consumidor de habilidades especificar uma identidade, ambos deverão fazê-lo.)

  2. Se você estiver usando o modelo LUIS, adicione a ID do aplicativo, a chave de API e o nome do host da API do LUIS.

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 dá suporte a alguns recursos diferentes. Ele pode reservar um voo ou obter o clima de uma cidade. Além disso, se ele receber uma mensagem fora desses contextos, poderá usar o LUIS para tentar interpretar a mensagem. O manifesto da habilidade descreve essas ações, os parâmetros de entrada e saída e os pontos de extremidade da habilidade. Observe que o skill pode lidar com um evento "BookFlight" ou "GetWeather". Ela também pode processar atividades de mensagens.

A habilidade define um diálogo de roteamento de atividades usado para selecionar qual ação deve ser iniciada, com base na atividade de entrada inicial do consumidor de habilidades. Se ela for fornecida, o modelo LUIS poderá reconhecer as intenções book-flight e get-weather em uma mensagem inicial.

A ação book-flight é um processo de várias etapas, implementado como um diálogo separado. Depois que a ação é iniciada, as atividades de entrada são processadas por esse diálogo. A ação get-weather tem uma lógica de espaço reservado que será substituída em um bot totalmente implementado.

O diálogo de roteamento de atividades inclui um código para:

Os diálogos usados na habilidade são herdados da classe de diálogo de componentes. Para obter mais informações sobre diálogos de componente, confira como Gerenciar a complexidade do diálogo.

Inicializar o diálogo

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

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

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

Processar uma atividade inicial

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

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

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

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

Processar atividades de mensagens

Se o reconhecedor LUIS estiver configurado, a habilidade chamará o LUIS e iniciará uma ação com base na intenção. Se o reconhecedor LUIS não estiver configurado ou se não houver suporte para a intenção, a habilidade enviará uma mensagem de erro e será encerrada.

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

Iniciar uma ação de várias etapas

A ação book-flight inicia um diálogo de várias etapas para obter os detalhes de reserva do usuário.

A ação get-weather não está implementada. Atualmente, ela envia uma mensagem de espaço reservado e, em seguida, é encerrada.

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

Retornar um resultado

A habilidade inicia um diálogo de reservas para a ação book-flight. Como o diálogo de roteamento de atividades tem apenas uma etapa, quando o diálogo de reserva é encerrado, o diálogo de roteamento de atividades também é encerrado e o resultado do diálogo de reserva se torna o resultado do diálogo de roteamento de atividades.

A ação get-weather simplesmente é encerrada sem definir um valor retornado.

Como cancelar uma ação de várias etapas

O diálogo de reserva e o diálogo de resolvedor de data filho derivam do diálogo base cancel-and-help, que verifica as mensagens do usuário.

  • Em "ajuda" ou "?", ele exibe uma mensagem de ajuda e continua o fluxo de conversa no próximo turno.
  • Em "cancelar" ou "sair", ele cancela todos os diálogos, o que encerra a habilidade.

Para obter mais informações, confira como Tratar as interrupções do usuário.

Registro do serviço

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

Manifesto de skills

Um manifesto de skill é um arquivo JSON que descreve as atividades que o skill pode executar, seus parâmetros de entrada e saída e os pontos de extremidade do skill. O manifesto contém as informações necessárias para acessar o skill por meio 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 do skill é um arquivo JSON que descreve o esquema do manifesto de skill. A versão mais recente do esquema é a v2.1.

Testar o bot de habilidades

Teste a habilidade no Emulator com o consumidor de habilidades. Para fazer isso, você precisará executar os bots de habilidades e de consumidor de habilidades ao mesmo tempo. Confira como Usar um diálogo para consumir uma habilidade para obter informações sobre como configurar a habilidade.

Baixe e instale o Bot Framework Emulator mais recente.

  1. Execute o bot de skill de caixa de diálogo e o bot raiz de caixa de diálogo localmente em seu computador. Se precisar de instruções, veja o arquivo README do exemplo para C#, JavaScript, Java ou Python.
  2. Use o Emulador para testar o bot.
    • Quando você ingressa na conversa pela primeira vez, o bot exibe uma mensagem de boas-vindas e pergunta qual skill você gostaria de chamar. O bot de skill para este exemplo tem apenas um skill.
    • Selecione DialogSkillBot.
  3. Em seguida, o bot solicita que você escolha uma ação para a habilidade. Escolha "BookFlight".
    1. A habilidade começa a ação book-flight; 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ê deseja chamar.
  4. Selecione DialogSkillBot novamente e "BookFlight".
    1. Responda à primeira solicitação e, em seguida, digite "Cancelar" para cancelar a ação.
    2. O bot de habilidades será encerrado sem concluir a ação e o consumidor solicitará a habilidade que você deseja chamar.

Mais sobre depuração

Como o tráfego entre as habilidades e os consumidores de habilidades é autenticado, há etapas adicionais na depuração desses bots.

  • O consumidor de habilidades e todas as habilidades que ele consome, direta ou indiretamente, devem estar em execução.
  • Se os bots estiverem sendo executados localmente e se algum dos bots tiver um ID do aplicativo e uma senha, então todos os bots deverão ter IDs e senhas válidos.
  • 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 estiverem implantados, confira como Depurar uma habilidade ou um consumidor de habilidades.

Caso contrário, você poderá depurar um consumidor de habilidades ou uma habilidade da mesma forma como você depura outros bots. Para obter mais informações, confira Depurar um bot e Depurar com o Bot Framework Emulator.

Informações adicionais