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 podem usar alguns tipos de bots de habilidades. A tabela a seguir descreve quais combinações são compatíveis.

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

Observação

Os SDKs do Bot Framework para JavaScript, C# e Python continuarão a ter suporte, no entanto, o SDK para Java está sendo desativado. Seu suporte final de longo prazo será encerrado em novembro de 2023. Somente correções críticas de segurança e de bugs serão realizadas neste repositório.

Os bots existentes criados com o SDK para Java continuarão a funcionar.

Para a criação de novos bots, considere usar o Power Virtual Agents e leia sobre como escolher a solução de chatbot mais adequada.

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

Pré-requisitos

Observação

A partir da versão 4.11, você não precisa de um ID do aplicativo e senha 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 de bot para bot exige que cada bot participante tenha informações de identidade válidas. No entanto, você pode testar as habilidades de vários locatários e os consumidores de habilidades localmente com o Emulator sem um ID do aplicativo e senha.

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.

Configuração de aplicativo

Opcionalmente, adicione as informações de identidade da habilidade ao seu arquivo de configuração. Se a habilidade ou o consumidor de habilidade fornecer informações de identidade, ambos deverão fazê-lo.

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 habilidade.

Observação

Se você estiver testando sua habilidade localmente sem informações de identidade do bot, nem a habilidade nem o consumidor da habilidade executarão o código para realizar 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 turno para turno. 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. Os usuários que não estão no banco de dados não devem 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, seu bot gerará um erro ou uma exceção ao receber uma atividade do consumidor de habilidade.

O SDK fornece uma classe AllowedCallersClaimsValidator que adiciona autorização em nível de 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 é 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

    options.SerializerSettings.MaxDepth = HttpHelper.BotMessageSerializerSettings.MaxDepth;
});

// 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>();

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 é a 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 precisar de instruções, confira o arquivo README do exemplo em C#, JavaScript, Java ou Python.
  2. Use o Emulador para testar o bot, conforme mostrado abaixo. Quando você envia uma mensagem de "fim" ou "parada" para a habilidade, ela envia uma atividade endOfConversation além da mensagem de resposta. O skill envia a atividade endOfConversation para indicar que o skill foi concluído.

Example transcript showing the end-of-conversation activity.

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 os bots estiverem todos implantados, confira como Depurar um bot em qualquer canal usando o ngrok.
  • 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.

Próximas etapas