Implementación de una aptitud

SE APLICA A: SDK v4

Puede usar las aptitudes para ampliar otro bot. Una aptitud es un bot que puede realizar un conjunto de tareas para otro bot.

  • La interfaz de una aptitud se describe mediante un manifiesto. Los desarrolladores que no tienen acceso al código fuente de la aptitud pueden usar la información del manifiesto para diseñar su consumidor de aptitudes.
  • Una aptitud puede usar la validación de notificaciones para administrar qué bots o usuarios pueden tener acceso a ella.

En este artículo se muestra cómo implementar una aptitud que repite la entrada del usuario.

Algunos tipos de consumidores de aptitudes no pueden usar algunos tipos de bots de aptitudes. Las combinaciones admitidas se describen en la tabla siguiente.

  Aptitud multiinquilino Habilidad de un inquilino único Aptitud de identidad administrada asignada por el usuario
Consumidor multiinquilino Compatible No compatible No compatible
Consumidor de un solo inquilino No compatible Se admite si ambas aplicaciones pertenecen al mismo inquilino Se admite si ambas aplicaciones pertenecen al mismo inquilino
Consumidor de la identidad administrada asignada por el usuario No compatible Se admite si ambas aplicaciones pertenecen al mismo inquilino Se admite si ambas aplicaciones pertenecen al mismo inquilino

Nota:

Los SDK de JavaScript, C# y Python de Bot Framework seguirán siendo compatibles, pero el SDK de Java se va a retirar con la compatibilidad final a largo plazo que finaliza en noviembre de 2023.

Los bots existentes creados con el SDK de Java seguirán funcionando.

Para la creación de nuevos bots, considera el uso de Power Virtual Agents y lee sobre cómo elegir la solución de bot de chat adecuada.

Para obtener más información, consulta El futuro de la creación de bots.

Requisitos previos

Nota:

A partir de la versión 4.11, no necesita un identificador de aplicación ni una contraseña para probar una aptitud localmente en Bot Framework Emulator. Todavía se requiere una suscripción de Azure para implementar la aptitud en Azure.

Acerca de este ejemplo

El ejemplo skills simple bot-to-bot incluye proyectos para dos bots:

  • El bot de aptitud de eco, que implementa la aptitud.
  • El bot raíz simple, que implementa un bot raíz que utiliza la aptitud.

Este artículo se centra en la aptitud, que incluye la lógica subyacente del bot y el adaptador.

Para más información sobre el bot raíz simple, consulte Implementación de un consumidor de aptitudes.

Recursos

Para los bots implementados, la autenticación de bot a bot requiere que cada bot participante tenga información de identidad válida. Sin embargo, puede probar las aptitudes multiinquilino y los consumidores de aptitudes localmente con Emulator sin un identificador de aplicación y una contraseña.

Para que la aptitud esté disponible para los bots orientados al usuario, registre la aptitud en Azure. Para más información, vea cómo registrar un bot con Azure Bot Service.

Configuración de aplicaciones

Opcionalmente, agregue la información de identidad de la aptitud a su archivo de configuración. Si el consumidor de aptitudes o aptitudes proporciona información de identidad, ambos deben proporcionarla.

La matriz de autores de llamadas permitidos pueden restringir qué consumidores pueden acceder a la aptitud. Agregue un elemento "*" para aceptar llamadas de cualquier consumidor de aptitudes.

Nota:

Si va a probar la aptitud localmente sin información de identidad del bot, ni la aptitud ni el consumidor de aptitudes ejecutan el código para realizar la validación de notificaciones.

EchoSkillBot\appsettings.json

Opcionalmente, agregue la información de identidad de la aptitud al archivo 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 del controlador de actividades

Para aceptar parámetros de entrada

El consumidor de aptitudes puede enviar información a la aptitud. Una manera de aceptar dicha información es hacerlo mediante la propiedad value de los mensajes entrantes. Otra manera es controlar las actividades de evento e invocación.

La aptitud de este ejemplo no acepta parámetros de entrada.

Para continuar o completar una conversación

Cuando la aptitud envía una actividad, el consumidor de aptitudes debe reenviar la actividad al usuario.

Sin embargo, debe enviar una actividad endOfConversation cuando finalice la aptitud; de lo contrario, el consumidor de aptitudes seguirá reenviando las actividades de usuario a la aptitud. También puede usar la propiedad value de la actividad para incluir un valor devuelto, y usar la propiedad code de la actividad para indicar por qué está finalizando la aptitud.

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 la aptitud

En el caso de aptitudes multiproceso, también se aceptan actividades endOfConversation de un consumidor de aptitudes para que el consumidor pueda cancelar la conversación actual.

La lógica de esta aptitud no cambia de un turno a otro. Si implementa una aptitud que asigna recursos de conversación, agregue el código de limpieza de recursos al controlador de final de la conversación.

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 notificaciones

Este ejemplo usa una lista de autores de llamada permitidos para la validación de notificaciones. La lista se define en el archivo de configuración de la aptitud y se lee en el objeto de validador cuando se crea.

Debe agregar un validador de notificaciones a la configuración de autenticación. Las notificaciones se evalúan después del encabezado de autenticación. El código de validación debe producir un error o una excepción para rechazar la solicitud. Hay muchas razones por las que puede querer rechazar una solicitud autenticada de otro modo. Por ejemplo:

  • La aptitud es parte de un servicio de pago. Los usuarios que no están en la base de datos no tienen acceso.
  • La aptitud es propietaria. Solo algunos consumidores de aptitudes pueden llamar a la aptitud.

Importante

Si no proporciona un validador de notificaciones, el bot generará un error o una excepción al recibir una actividad del consumidor de aptitudes.

El SDK proporciona una clase AllowedCallersClaimsValidator que agrega autorización de nivel de aplicación basada en una lista sencilla de identificadores de las aplicaciones que pueden llamar a la aptitud. Si la lista contiene un asterisco (*), se permiten todos los llamadores. El validador de notificaciones está configurado en Startup.cs.

Adaptador de la aptitud

Cuando se produce un error, el adaptador de la aptitud debe borrar el estado de la conversación para la aptitud y también debe enviar una actividad endOfConversation al consumidor de aptitudes. Use la propiedad code de la actividad para indicar que la aptitud finalizó debido a un error.

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 de servicios

El adaptador de Bot Framework usa un objeto de configuración de la autenticación (establecido cuando se crea el adaptador) para validar el encabezado de autenticación de las solicitudes entrantes.

En este ejemplo, se agrega la validación de notificaciones a la configuración de la autenticación y se usa el adaptador de aptitud con controlador de errores descrito en la sección 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>();

Manifiesto de aptitud

Un manifiesto de aptitud es un archivo JSON que describe las actividades que puede realizar la aptitud, sus parámetros de entrada y salida, y sus puntos de conexión. El manifiesto contiene la información que necesita para tener acceso a la aptitud desde otro bot. La versión más reciente es 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"
    }
  ]
}

El esquema del manifiesto de aptitud es un archivo JSON que describe el esquema del manifiesto de aptitud. La versión de esquema actual es 2.1.0.

Prueba de la aptitud

Llegados a este punto, puede probar la aptitud en el emulador como si fuera un bot normal. Sin embargo, para probarla como aptitud, tendría que implementar un consumidor de aptitudes.

Descargue e instale la versión más reciente de Bot Framework Emulator.

  1. Ejecute el bot de aptitud de eco localmente en la máquina. Si necesita instrucciones, consulte el archivo README de C#, JavaScript, Java o Python.
  2. Use el emulador para probar el bot tal y como se muestra a continuación. Cuando se envía un mensaje "end" o "stop" a la aptitud, envía una actividad endOfConversation además del mensaje de respuesta. La aptitud envía la actividad endOfConversation para indicar que ha finalizado.

Transcripción de ejemplo que muestra la actividad de fin de conversación.

Más sobre la depuración

Dado que se autentica el tráfico entre aptitudes y consumidores de aptitudes, hay pasos adicionales al depurar estos bots.

  • El consumidor de aptitudes y todas las aptitudes que consume, directa o indirectamente, deben ejecutarse.
  • Si los bots se ejecutan localmente y si alguno de los bots tiene un identificador de aplicación y una contraseña, todos los bots deben tener identificadores y contraseñas válidos.
  • Si todos los bots están implementados, consulte cómo depurar un bot desde cualquier canal mediante ngrok.
  • Si algunos de los bots se ejecutan localmente y algunos se implementan, consulte cómo depurar una aptitud o consumidor de aptitudes.

De lo contrario, puede depurar un consumidor de aptitudes o una aptitud como depurar otros bots. Para obtener más información, consulte Depuración de un bot y Depuración con Bot Framework Emulator.

Pasos siguientes