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. O diálogo principal recebe a entrada inicial do utilizador da competência e inicia a ação apropriada de seguida. 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
- Conhecimento das noções básicas de bots, como os bots de habilidades funcionam e como implementar uma habilidade.
- Uma assinatura do Azure (para implantar sua habilidade). Se não tiver uma, crie uma conta gratuita antes de começar.
- Opcionalmente, uma conta LUIS. (Para obter mais informações, consulte como adicionar compreensão de linguagem natural ao seu bot.)
- Uma cópia do exemplo skillDialog de habilidades em C#, JavaScript, Java ou Python.
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 de skills skillDialog inclui projetos para dois bots.
- O bot de raiz de diálogo, que usa uma classe de diálogo de habilidade para executar uma habilidade.
- O bot de competências de diálogo, que usa um diálogo para lidar com atividades provenientes de consumidores de competências. Esta funcionalidade é uma adaptação do exemplo de Core Bot. (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, veja como registar 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
Opcionalmente, adicione as informações de identidade da habilidade ao arquivo de configuração da habilidade. (Se uma identidade for especificada, tanto pela habilidade como pelo utilizador de habilidade, ambos devem.)
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. É importante notar que a habilidade pode lidar com um evento "BookFlight" ou "GetWeather". Ele também pode lidar com atividades de mensagem.
A habilidade define um diálogo para direcionamento de atividades que usa para selecionar qual ação iniciar, com base na atividade inicial recebida do utilizador da habilidade. Se fornecido, o modelo LUIS pode reconhecer as intenções book-flight e get-weather em uma mensagem inicial.
A ação de reservar 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 pela caixa de diálogo correspondente. A ação get-weather tem lógica de placeholders que será substituída num bot totalmente implementado.
A caixa de diálogo de roteamento de atividades inclui código para:
- Inicializar a caixa de diálogo
- Processar uma atividade inicial
- Manipular atividades de mensagens
- Iniciar uma ação em várias etapas
- Devolver um resultado
Os diálogos usados 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
O diálogo de sequenciamento de atividades inclui um diálogo secundário 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 do 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);
}
}
Gerir 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 genérica 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 funcionalidade inicia um diálogo de reservas para a ação de reserva de voo. Como o diálogo de roteamento de atividades tem apenas uma etapa, quando o diálogo de reserva termina, o diálogo de roteamento de atividades também termina e o resultado do diálogo de reserva torna-se o resultado do 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 sua caixa de diálogo filha de resolução de data derivam da caixa de diálogo base de cancelamento e ajuda, que analisa as mensagens dos utilizadores.
- Quando "ajuda" ou "?" são acionados, é exibida uma mensagem de ajuda e, em seguida, o fluxo da conversa continua no turno seguinte.
- Em "cancelar" ou "sair", ele cancela todas as caixas de diálogo, o que encerra a funcionalidade.
Para mais informações, veja como lidar com interrupções de utilizador.
Registo de 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 utilizador da habilidade. Para fazer isso, você/tu precisa executar os bots de habilidade e consumidor de habilidade simultaneamente. Veja como usar uma caixa de diálogo para aceder a uma competência para obter informações sobre como configurar a competência.
Baixe e instale o mais recente Bot Framework Emulator.
- 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. - Use o emulador para testar o bot.
- Quando entras pela primeira vez na conversa, o bot apresenta uma mensagem de boas-vindas e pergunta qual função gostarias de utilizar. O bot de habilidade para este exemplo tem apenas uma habilidade.
- Selecione DialogSkillBot.
- Em seguida, o bot pede que você escolha uma ação para a habilidade. Escolha "BookFlight".
- A habilidade inicia a sua ação de voo do livro; Responde às perguntas.
- Quando a funcionalidade for concluída, o bot raiz exibirá os detalhes da reserva antes de solicitar novamente a funcionalidade que gostaria de ativar.
- Selecione DialogSkillBot novamente e "BookFlight".
- Responda ao primeiro prompt e digite "cancelar" para cancelar a ação.
- O bot de habilidades encerra-se sem concluir a ação, e o utilizador solicita a habilidade que gostaria de ativar.
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.
Alternativamente, pode depurar um consumidor de skill ou uma skill da mesma forma que depura outros bots. Para obter mais informações, consulte Depurando um bot e Depurando com o emulador do Bot Framework.
Informações adicionais
- Usar um diálogo para consumir uma habilidade descreve como consumir uma habilidade usando um diálogo de habilidade.