Compartir a través de


Implementar una habilidad

SE APLICA A: SDK v4

Puede utilizar tus habilidades para ampliar otro bot. Una aptitud es un bot que puede realizar un conjunto de tareas para otro bot.

  • Un manifiesto describe la interfaz de una aptitud. Los desarrolladores que no tienen acceso al código fuente de la habilidad pueden usar la información del manifiesto para diseñar su usuario de la habilidad.
  • Una habilidad puede usar la validación de afirmaciones 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 usuarios de habilidades no pueden usar algunos tipos de bots de habilidades. Las combinaciones admitidas se describen en la tabla siguiente.

  Aptitud multiinquilino Habilidad de un inquilino único Habilidad de identidad administrada asignada por el usuario
Consumidor multiusuario Compatible No soportado No soportado
Consumidor de un solo inquilino No soportado 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 soportado Se admite si ambas aplicaciones pertenecen al mismo inquilino Se admite si ambas aplicaciones pertenecen al mismo inquilino

Nota:

Para crear agentes con su elección de servicios de inteligencia artificial, orquestación y conocimientos, considere la posibilidad de usar el SDK de agentes de Microsoft 365. El SDK de agentes admite C#, JavaScript o Python. Puede obtener más información sobre el SDK de agentes en aka.ms/agents. Si busca una plataforma de agente basada en SaaS, considere Microsoft Copilot Studio. Si tiene un bot existente creado con Bot Framework SDK, puede actualizar el bot al SDK de agentes. Puede revisar los cambios principales y las actualizaciones en la guía de migración del Bot Framework SDK al SDK de Agentes. Las incidencias de soporte técnico del SDK de Bot Framework ya no se atenderán a partir del 31 de diciembre de 2025.

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 el emulador 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. Para aceptar llamadas de cualquier consumidor de habilidades, agregue un elemento "*".

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

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 habilidad 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 continuará reenviando las actividades del 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 habilidad 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 reclamaciones

Este ejemplo usa una lista de autores de llamada permitidos para la validación de notificaciones. El archivo de configuración de la aptitud define la lista. A continuación, el objeto de validador lee la lista.

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 es posible que quiera rechazar una solicitud autenticada de otro modo. Por ejemplo:

  • La habilidad es parte de un servicio pagado. Los usuarios que no están en la base de datos no tienen acceso.
  • La habilidad es privativa. Solo ciertos usuarios de la habilidad pueden activar la habilidad.

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. Para indicar que la habilidad finalizó debido a un error, use la propiedad code de la actividad.

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 necesaria para acceder a la habilidad desde otro bot. La versión más reciente es v2.1.

\wwwroot\manifest\echoskillbot-manifest-1.0.jsEchoSkillBot en

{
  "$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. Cuando se envía un mensaje "end" o "stop" a la aptitud, envía una actividad endOfConversation además del mensaje de respuesta. La habilidad envía la actividad endOfConversation para indicar que ha finalizado.

Transcripción de ejemplo que muestra la actividad al final de la 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 devtunnel.
  • 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