Implementar um skill

APLICA-SE A: SDK v4

Você pode usar skills para estender outro bot. Um skill é um bot que pode executar um conjunto de tarefas para outro bot.

  • A interface de um skill é descrita por um manifesto. Os desenvolvedores que não têm acesso ao código-fonte do skill podem usar as informações do manifesto para criar um consumidor de skill.
  • Um skill pode usar a validação de declarações para gerenciar quais bots ou usuários podem acessá-lo.

Este artigo demonstra como implementar um skill que ecoa a entrada do usuário.

Alguns tipos de consumidores de habilidades não são capazes de usar alguns tipos de bots de habilidade. A tabela a seguir descreve quais combinações têm suporte.

  Habilidade multilocatário Habilidade de locatário único Habilidade de identidade gerenciada atribuída pelo usuário
Consumidor multilocatário Com suporte Sem suporte Sem suporte
Consumidor de locatário único Sem suporte Com suporte se ambos os aplicativos pertencerem ao mesmo locatário Com suporte se ambos os aplicativos pertencerem ao mesmo locatário
Consumidor de identidade gerenciada atribuída pelo usuário Sem suporte Com suporte se ambos os aplicativos pertencerem ao mesmo locatário Com suporte se ambos os aplicativos pertencerem ao mesmo locatário

Pré-requisitos

Observação

A partir da versão 4.11, você não precisa de uma ID e senha do aplicativo para testar uma habilidade localmente no Bot Framework Emulator. Uma assinatura do Azure ainda é necessária para implantar sua habilidade no Azure.

Sobre este exemplo

O exemplo bot para bot de skills simples inclui projetos para dois bots:

  • O bot skill de eco, que implementa o skill.
  • O bot raiz simples, que implementa um bot raiz que consome o skill.

Este artigo se concentra no skill, que inclui a lógica de suporte em seu bot e adaptador.

Para obter informações sobre o bot raiz simples, consulte como Implementar um consumidor de skills.

Recursos

Para bots implantados, a autenticação bot-to-bot requer que cada bot participante tenha informações de identidade válidas. No entanto, você pode testar as habilidades multilocatários e os consumidores de habilidades localmente com o Emulator sem uma ID de aplicativo e senha.

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

Configuração de aplicativo

Opcionalmente, adicione as informações de identidade da habilidade ao arquivo de configuração. Se o consumidor de habilidades ou habilidades fornecer informações de identidade, ambos deverão.

A matriz de chamadores permitidos pode restringir quais consumidores de skills podem acessar o skill. Adicione um elemento "*" para aceitar chamadas de qualquer consumidor de habilidades.

Observação

Se você estiver testando sua habilidade localmente sem informações de identidade do bot, nem a habilidade nem o consumidor de habilidades executarão o código para executar a validação de declarações.

EchoSkillBot\appsettings.json

Opcionalmente, adicione as informações de identidade da habilidade ao arquivo appsettings.json.

{
  "MicrosoftAppType": "",
  "MicrosoftAppId": "",
  "MicrosoftAppPassword": "",
  "MicrosoftAppTenantId": "",

  // 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 do manipulador de atividades

Para aceitar parâmetros de entrada

O consumidor de skills pode enviar informações para o skill. Uma maneira de aceitar essas informações é aceitá-las por meio da propriedade valor em mensagens de entrada. Outra maneira é manipular atividades de evento e invocação.

A habilidade neste exemplo não aceita parâmetros de entrada.

Para continuar ou concluir uma conversa

Quando o skill envia uma atividade, o consumidor de skills deve encaminhar a atividade para o usuário.

No entanto, você precisa enviar uma atividade endOfConversation quando o skill for concluído; caso contrário, o consumidor de skills continuará a encaminhar as atividades do usuário para o skill. Opcionalmente, use a propriedade valor da atividade para incluir um valor retornado e use a propriedade código da atividade para indicar por que o skill está terminando.

EchoSkillBot\Bots\EchoBot.cs

protected override async Task OnMessageActivityAsync(ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken)
{
    if (turnContext.Activity.Text.Contains("end") || turnContext.Activity.Text.Contains("stop"))
    {
        // Send End of conversation at the end.
        var messageText = $"ending conversation from the skill...";
        await turnContext.SendActivityAsync(MessageFactory.Text(messageText, messageText, InputHints.IgnoringInput), cancellationToken);
        var endOfConversation = Activity.CreateEndOfConversationActivity();
        endOfConversation.Code = EndOfConversationCodes.CompletedSuccessfully;
        await turnContext.SendActivityAsync(endOfConversation, cancellationToken);
    }
    else
    {
        var messageText = $"Echo: {turnContext.Activity.Text}";
        await turnContext.SendActivityAsync(MessageFactory.Text(messageText, messageText, InputHints.IgnoringInput), cancellationToken);
        messageText = "Say \"end\" or \"stop\" and I'll end the conversation and back to the parent.";
        await turnContext.SendActivityAsync(MessageFactory.Text(messageText, messageText, InputHints.ExpectingInput), cancellationToken);
    }
}

Para cancelar o skill

Para skills de vários ciclos, você também aceitará atividades endOfConversation de um consumidor de skills, para permitir que o consumidor cancele a conversa atual.

A lógica dessa habilidade não muda de vez em quando. Se você implementar um skill que aloque recursos de conversa, adicione o código de limpeza de recurso ao manipulador de fim de conversa.

EchoSkillBot\Bots\EchoBot.cs

protected override Task OnEndOfConversationActivityAsync(ITurnContext<IEndOfConversationActivity> turnContext, CancellationToken cancellationToken)
{
    // This will be called if the root bot is ending the conversation.  Sending additional messages should be
    // avoided as the conversation may have been deleted.
    // Perform cleanup of resources if needed.
    return Task.CompletedTask;
}

Validador de declarações

Este exemplo usa uma lista de chamadores permitidos para validação de declarações. A lista é definida no arquivo de configuração do skill e é lida no objeto do validador quando ele é criado.

Você deve adicionar um validador de declarações à configuração de autenticação. As declarações são avaliadas após o cabeçalho de autenticação. O código de validação deve gerar um erro ou uma exceção para rejeitar a solicitação. Há muitos motivos pelos quais você pode rejeitar uma solicitação autenticada de outra forma. Por exemplo:

  • O skill faz parte de um serviço pago. O usuário não está no banco de dados não deve ter acesso.
  • O skill é proprietário. Somente determinados consumidores de skills podem chamar o skill.

Importante

Se você não fornecer um validador de declarações, o bot gerará um erro ou uma exceção ao receber uma atividade do consumidor de habilidades.

O SDK fornece uma AllowedCallersClaimsValidator classe que adiciona autorização no nível do aplicativo com base em uma lista simples de IDs dos aplicativos que têm permissão para chamar a habilidade. Se a lista contiver um asterisco (*), todos os chamadores serão permitidos. O validador de declarações está configurado em Startup.cs.

Adaptador de skill

Quando ocorre um erro, o adaptador do skill deve limpar o estado da conversa para o skill e também deve enviar uma atividade endOfConversation para o consumidor de skills. Use a propriedade código da atividade para sinalizar que o skill foi encerrado devido a um erro.

EchoSkillBot\SkillAdapterWithErrorHandler.cs

private async Task HandleTurnError(ITurnContext turnContext, Exception exception)
{
    // Log any leaked exception from the application.
    _logger.LogError(exception, $"[OnTurnError] unhandled error : {exception.Message}");

    await SendErrorMessageAsync(turnContext, exception);
    await SendEoCToParentAsync(turnContext, exception);
}

private async Task SendErrorMessageAsync(ITurnContext turnContext, Exception exception)
{
    try
    {
        // Send a message to the user.
        var errorMessageText = "The skill encountered an error or bug.";
        var errorMessage = MessageFactory.Text(errorMessageText, errorMessageText, InputHints.IgnoringInput);
        await turnContext.SendActivityAsync(errorMessage);

        errorMessageText = "To continue to run this bot, please fix the bot source code.";
        errorMessage = MessageFactory.Text(errorMessageText, errorMessageText, InputHints.ExpectingInput);
        await turnContext.SendActivityAsync(errorMessage);

        // Send a trace activity, which will be displayed in the Bot Framework Emulator.
        // Note: we return the entire exception in the value property to help the developer;
        // this should not be done in production.
        await turnContext.TraceActivityAsync("OnTurnError Trace", exception.ToString(), "https://www.botframework.com/schemas/error", "TurnError");
    }
    catch (Exception ex)
    {
        _logger.LogError(ex, $"Exception caught in SendErrorMessageAsync : {ex}");
    }
}

private async Task SendEoCToParentAsync(ITurnContext turnContext, Exception exception)
{
    try
    {
        // Send an EndOfConversation activity to the skill caller with the error to end the conversation,
        // and let the caller decide what to do.
        var endOfConversation = Activity.CreateEndOfConversationActivity();
        endOfConversation.Code = "SkillError";
        endOfConversation.Text = exception.Message;
        await turnContext.SendActivityAsync(endOfConversation);
    }
    catch (Exception ex)
    {
        _logger.LogError(ex, $"Exception caught in SendEoCToParentAsync : {ex}");
    }
}

Registro do serviço

O adaptador do Bot Framework usa um objeto de configuração de autenticação (definido quando o adaptador é criado) para validar o cabeçalho de autenticação em solicitações de entrada.

Este exemplo adiciona validação de declarações à configuração de autenticação e usa o adaptador de habilidade com o manipulador de erros descrito na seção anterior.

EchoSkillBot\Startup.cs

// Register AuthConfiguration to enable custom claim validation.
services.AddSingleton(sp =>
{
    var allowedCallers = new List<string>(sp.GetService<IConfiguration>().GetSection("AllowedCallers").Get<string[]>());

    var claimsValidator = new AllowedCallersClaimsValidator(allowedCallers);

    // If TenantId is specified in config, add the tenant as a valid JWT token issuer for Bot to Skill conversation.
    // The token issuer for MSI and single tenant scenarios will be the tenant where the bot is registered.
    var validTokenIssuers = new List<string>();
    var tenantId = sp.GetService<IConfiguration>().GetSection(MicrosoftAppCredentials.MicrosoftAppTenantIdKey)?.Value;

    if (!string.IsNullOrWhiteSpace(tenantId))
    {
        // For SingleTenant/MSI auth, the JWT tokens will be issued from the bot's home tenant.
        // Therefore, these issuers need to be added to the list of valid token issuers for authenticating activity requests.
        validTokenIssuers.Add(string.Format(CultureInfo.InvariantCulture, AuthenticationConstants.ValidTokenIssuerUrlTemplateV1, tenantId));
        validTokenIssuers.Add(string.Format(CultureInfo.InvariantCulture, AuthenticationConstants.ValidTokenIssuerUrlTemplateV2, tenantId));
        validTokenIssuers.Add(string.Format(CultureInfo.InvariantCulture, AuthenticationConstants.ValidGovernmentTokenIssuerUrlTemplateV1, tenantId));
        validTokenIssuers.Add(string.Format(CultureInfo.InvariantCulture, AuthenticationConstants.ValidGovernmentTokenIssuerUrlTemplateV2, tenantId));
    }

    return new AuthenticationConfiguration
    {
        ClaimsValidator = claimsValidator,
        ValidTokenIssuers = validTokenIssuers
    };
});

// Create the Bot Framework Authentication to be used with the Bot Adapter.
services.AddSingleton<BotFrameworkAuthentication, ConfigurationBotFrameworkAuthentication>();

// Create the Bot Framework Adapter with error handling enabled.
services.AddSingleton<IBotFrameworkHttpAdapter, SkillAdapterWithErrorHandler>();

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. A versão mais recente do esquema é v2.1.

EchoSkillBot\wwwroot\manifest\echoskillbot-manifest-1.0.json

{
  "$schema": "https://schemas.botframework.com/schemas/skills/skill-manifest-2.0.0.json",
  "$id": "EchoSkillBot",
  "name": "Echo Skill bot",
  "version": "1.0",
  "description": "This is a sample echo skill",
  "publisherName": "Microsoft",
  "privacyUrl": "https://echoskillbot.contoso.com/privacy.html",
  "copyright": "Copyright (c) Microsoft Corporation. All rights reserved.",
  "license": "",
  "iconUrl": "https://echoskillbot.contoso.com/icon.png",
  "tags": [
    "sample",
    "echo"
  ],
  "endpoints": [
    {
      "name": "default",
      "protocol": "BotFrameworkV3",
      "description": "Default endpoint for the skill",
      "endpointUrl": "http://echoskillbot.contoso.com/api/messages",
      "msAppId": "00000000-0000-0000-0000-000000000000"
    }
  ]
}

O esquema de manifesto do skill é um arquivo JSON que descreve o esquema do manifesto de skill. A versão atual do esquema é 2.1.0.

Testar o skill

Neste ponto, você pode testar o skill no Emulador como se fosse um bot normal. No entanto, para testá-lo como um skill, você precisaria implementar um consumidor de skills.

Baixe e instale o Bot Framework Emulator mais recente

  1. Execute o bot skill de eco localmente em seu computador. Se você precisar de instruções, consulte o arquivo para o README exemplo C#, JavaScript, Java ou Python .
  2. Use o Emulador para testar o bot, conforme mostrado abaixo. Quando você envia uma mensagem "end" ou "stop" para a habilidade, ela envia uma endOfConversation atividade além da mensagem de resposta. O skill envia a atividade endOfConversation para indicar que o skill foi concluído.

test the echo skill

Mais sobre depuração

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

  • O consumidor de habilidades e todas as habilidades que ele consome, direta ou indiretamente, devem estar em execução.
  • Se os bots estiverem em execução localmente e se algum dos bots tiver uma ID e senha do aplicativo, 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 o ngrok.
  • Se alguns dos bots estiverem em execução localmente e alguns forem implantados, veja como depurar uma habilidade ou um consumidor de habilidades.

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

Próximas etapas