Implementación de un consumidor de aptitudesImplement a skill consumer

se aplica a: SDK V4APPLIES TO: SDK v4

Puede usar las aptitudes para ampliar otro bot.You can use skills to extend another bot. Una aptitud es un bot que puede realizar un conjunto de tareas para otro bot y que usa un manifiesto para describir su interfaz.A skill is a bot that can perform a set of tasks for another bot and uses a manifest to describe its interface. Un bot raíz es un bot accesible para el usuario que puede invocar a una o más aptitudes.A root bot is a user-facing bot that can invoke one or more skills. Un bot raíz es un tipo de consumidor de aptitudes.A root bot is a type of skill consumer.

  • Un consumidor de aptitudes debe usar la validación de notificaciones para administrar qué aptitudes pueden acceder a ella.A skill consumer must use claims validation to manage which skills can access it.
  • Un consumidor de aptitudes puede utilizar varias aptitudes.A skill consumer can use multiple skills.
  • Los desarrolladores que no tienen acceso al código fuente de la aptitud pueden usar la información del manifiesto de la aptitud para diseñar su consumidor de aptitudes.Developers who don't have access to the skill's source code can use the information in the skill's manifest to design their skill consumer.

En este artículo se muestra cómo implementar un consumidor de aptitudes que usa la aptitud de eco para repetir los datos de entrada del usuario.This article demonstrates how to implement a skill consumer that uses the echo skill to echo the user's input. Para ver un ejemplo de un manifiesto de aptitud e información sobre cómo implementar la aptitud de eco, consulte cómo implementar una aptitud.For a sample skill manifest and information about implementing the echo skill, see how to implement a skill.

Para más información sobre el uso de un diálogo de aptitudes para consumir una aptitud, consulte cómo usar un diálogo para consumir una aptitud.For information about using a skill dialog to consume a skill, see how to use a dialog to consume a skill.

PrerrequisitosPrerequisites

Nota

A partir de la versión 4.11, no necesita un identificador de aplicación y una contraseña para probar un consumidor de aptitudes localmente en el emulador.Starting with version 4.11, you do not need an app ID and password to test a skill consumer locally in the Emulator. Todavía se requiere una suscripción de Azure para implementar el consumidor en Azure o para consumir una aptitud implementada.An Azure subscription is still required to deploy your consumer to Azure or to consume a deployed skill.

Acerca de este ejemploAbout this sample

El ejemplo skills simple bot-to-bot incluye proyectos para dos bots:The skills simple bot-to-bot sample includes projects for two bots:

  • El bot de aptitud de eco, que implementa la aptitud.The echo skill bot, which implements the skill.
  • El bot raíz simple, que implementa un bot raíz que utiliza la aptitud.The simple root bot, which implements a root bot that consumes the skill.

Este artículo se centra en el bot raíz, que incluye la lógica subyacente en sus objetos de bot y adaptador, e incluye los objetos que se usan para intercambiar actividades con una aptitud.This article focuses on the root bot, which includes support logic in its bot and adapter objects and includes objects used to exchange activities with a skill. Entre ellas se incluyen las siguientes:These include:

  • Un cliente de aptitudes, que se usa para enviar actividades a una aptitud.A skill client, used to send activities to a skill.
  • Un controlador de aptitudes, que se usa para recibir actividades de una aptitud.A skill handler, used to receive activities from a skill.
  • Un generador de identificadores de conversación de aptitudes, que el cliente y el controlador de aptitudes utilizan para traducir de la referencia de la conversación entre el bot raíz y el usuario a la referencia de la conversación entre el bot raíz y la aptitud, y viceversa.A skill conversation ID factory, used by the skill client and handler to translate between the user-root conversation reference and the root-skill conversation reference.

Para más información sobre el bot de aptitud de eco, consulte cómo implementar una aptitud.For information about the echo skill bot, see how to Implement a skill.

RecursosResources

Para los bots implementados, la autenticación de bot a bot requiere que cada bot participante tenga un identificador de aplicación y una contraseña válidos.For deployed bots, bot-to-bot authentication requires that each participating bot has a valid app ID and password. Sin embargo, puede probar las aptitudes y los consumidores de aptitudes localmente con el emulador sin un identificador de aplicación y una contraseña.However, you can test skills and skill consumers locally with the Emulator without an app ID and password.

Configuración de aplicaciónApplication configuration

  1. Opcionalmente, agregue el identificador de aplicación y la contraseña del bot raíz al archivo de configuración.Optionally, add the root bot's app ID and password to the config file.
  2. Agregue el punto de conexión del host de aptitud (el servicio o la dirección URL de devolución de llamada) al que las aptitudes deben responder al consumidor de aptitudes.Add the skill host endpoint (the service or callback URL) to which the skills should reply to the skill consumer.
  3. Agregue una entrada para cada aptitud que usará el consumidor de aptitudes.Add an entry for each skill the skill consumer will use. Cada entrada incluye:Each entry includes:
    • Un identificador que el consumidor de aptitudes usará para identificar cada aptitud.An ID the skill consumer will use to identify each skill.
    • Opcionalmente, el identificador de aplicación de la aptitud.Optionally, the skill's app ID.
    • El punto de conexión de mensajería de la aptitud.The skill's messaging endpoint.

Nota

Si el consumidor de aptitudes o aptitudes usa un identificador de aplicación y una contraseña, ambos deben hacerlo.If either the skill or skill consumer uses an app ID and password, both must.

SimpleRootBot\appsettings.jsonSimpleRootBot\appsettings.json

Opcionalmente, agregue el identificador de aplicación y la contraseña del bot raíz y agregue el identificador de aplicación para el bot de aptitud de eco a la BotFrameworkSkills matriz.Optionally, add the root bot's app ID and password and add the app ID for the echo skill bot to the BotFrameworkSkills array.

{
  "MicrosoftAppId": "",
  "MicrosoftAppPassword": "",
  "SkillHostEndpoint": "http://localhost:3978/api/skills/",
  "BotFrameworkSkills": [
    {
      "Id": "EchoSkillBot",
      "AppId": "TODO: Add here the App ID for the skill",
      "SkillEndpoint": "http://localhost:39783/api/messages"
    }
  ]
}

Configuración de aptitudesSkills configuration

En este ejemplo, se lee la información de cada aptitud del archivo de configuración en una colección de objetos skill.This sample reads information for each skill in the configuration file into a collection of skill objects.

SimpleRootBot\SkillsConfiguration.csSimpleRootBot\SkillsConfiguration.cs

public class SkillsConfiguration
{
    public SkillsConfiguration(IConfiguration configuration)
    {
        var section = configuration?.GetSection("BotFrameworkSkills");
        var skills = section?.Get<BotFrameworkSkill[]>();
        if (skills != null)
        {
            foreach (var skill in skills)
            {
                Skills.Add(skill.Id, skill);
            }
        }

        var skillHostEndpoint = configuration?.GetValue<string>(nameof(SkillHostEndpoint));
        if (!string.IsNullOrWhiteSpace(skillHostEndpoint))
        {
            SkillHostEndpoint = new Uri(skillHostEndpoint);
        }
    }

    public Uri SkillHostEndpoint { get; }

    public Dictionary<string, BotFrameworkSkill> Skills { get; } = new Dictionary<string, BotFrameworkSkill>();
}

Generador de identificadores de conversaciónConversation ID factory

Crea el identificador de conversación que se va a usar con la aptitud y puede recuperar el identificador de la conversación original con el usuario a partir del identificador de la conversación con la aptitud.This creates the conversation ID for use with the skill and can recover the original user conversation ID from the skill conversation ID.

En este ejemplo, el generador de identificadores de conversación admite un escenario simple en el que:The conversation ID factory for this sample supports a simple scenario where:

  • El bot raíz está diseñado para consumir una aptitud específica.The root bot is designed to consume one specific skill.
  • El bot raíz solo tiene una conversación activa con una aptitud a la vez.The root bot has only one active conversation with a skill at a time.

SimpleRootBot\SkillConversationIdFactory.csSimpleRootBot\SkillConversationIdFactory.cs

public class SkillConversationIdFactory : SkillConversationIdFactoryBase
{
    private readonly ConcurrentDictionary<string, string> _conversationRefs = new ConcurrentDictionary<string, string>();

    public override Task<string> CreateSkillConversationIdAsync(SkillConversationIdFactoryOptions options, CancellationToken cancellationToken)
    {
        var skillConversationReference = new SkillConversationReference
        {
            ConversationReference = options.Activity.GetConversationReference(),
            OAuthScope = options.FromBotOAuthScope
        };
        var key = $"{options.FromBotId}-{options.BotFrameworkSkill.AppId}-{skillConversationReference.ConversationReference.Conversation.Id}-{skillConversationReference.ConversationReference.ChannelId}-skillconvo";
        _conversationRefs.GetOrAdd(key, JsonConvert.SerializeObject(skillConversationReference));
        return Task.FromResult(key);
    }

    public override Task<SkillConversationReference> GetSkillConversationReferenceAsync(string skillConversationId, CancellationToken cancellationToken)
    {
        var conversationReference = JsonConvert.DeserializeObject<SkillConversationReference>(_conversationRefs[skillConversationId]);
        return Task.FromResult(conversationReference);
    }

    public override Task DeleteConversationReferenceAsync(string skillConversationId, CancellationToken cancellationToken)
    {
        _conversationRefs.TryRemove(skillConversationId, out _);
        return Task.CompletedTask;
    }
}

Para admitir escenarios más complejos, diseñe un generador de identificadores de conversación que haga lo siguiente:To support more complex scenarios, design your conversation ID factory so that:

  • El método crear id. de conversación de aptitudes obtiene o genera el identificador de conversación de aptitudes adecuado.The create skill conversation ID method gets or generates the appropriate skill conversation ID.
  • El método obtener referencia de conversación obtiene la conversación de usuario correcta.The get conversation reference method gets the correct user conversation.

Controlador de aptitudes y cliente de aptitudesSkill client and skill handler

El consumidor de aptitudes usa un cliente de aptitudes para reenviar actividades a la aptitud.The skill consumer uses a skill client to forward activities to the skill. El cliente utiliza la información de configuración de las aptitudes y el generador de identificaciones de conversación para hacerlo.The client uses the skills configuration information and conversation ID factory to do so.

El consumidor de aptitudes usa un controlador de aptitudes para recibir actividades de una aptitud.The skill consumer uses a skill handler to receive activities from a skill. El controlador usa el generador de identificaciones de conversación, la configuración de autenticación y un proveedor de credenciales para hacerlo, y también tiene dependencias en el adaptador del bot raíz y en el controlador de actividades.The handler uses the conversation ID factory, the authentication configuration, and a credential provider to do so, and also has dependencies on the root bot's adapter and activity handler

SimpleRootBot\Startup.csSimpleRootBot\Startup.cs

services.AddHttpClient<SkillHttpClient>();
services.AddSingleton<ChannelServiceHandler, SkillHandler>();

El tráfico HTTP de la aptitud entrará en la URL del punto de conexión de servicio en el que el consumidor de aptitudes publica información para la aptitud.HTTP traffic from the skill will come into the service URL endpoint that the skill consumer advertizes to the skill. Use un controlador de puntos de conexión específico del lenguaje para reenviar el tráfico al controlador de aptitudes.Use a language-specific endpoint handler to forward traffic to the skill handler.

El controlador de aptitudes predeterminado:The default skill handler:

  • Si hay un identificador de aplicación y una contraseña, usa un objeto de configuración de autenticación para realizar la autenticación de bot a bot y la validación de notificaciones.If an app ID and password are present, uses an authentication configuration object to perform both bot-to-bot authentication and claims validation.
  • Usa el generador de identificadores de conversación para traducir de la conversación entre el consumidor y la aptitud de nuevo a la conversación entre el bot raíz y el usuario.Uses the conversation ID factory to translate from the consumer-skill conversation back to the root-user conversation.
  • Genera un mensaje activo para que el consumidor de aptitudes pueda volver a establecer el contexto del turno y las actividades de reenvío de la conversación entre el bot raíz y el usuario.Generates a proactive message so that the skill consumer can reestablish a root-user turn context and forward activities to the user.

Lógica del controlador de actividadesActivity handler logic

Tenga en cuenta que la lógica del consumidor de aptitudes debe:Of note, the skill consumer logic should:

  • Recordar si hay aptitudes activas y reenviarles las actividades según corresponda.Remember whether there are any active skills and forward activities to them as appropriate.
  • Identificar si un usuario realiza una solicitud que debe reenviarse a una aptitud, e iniciar la aptitud.Notice when a user makes a request that should be forwarded to a skill, and start the skill.
  • Buscar una actividad endOfConversation de cualquier aptitud activa, para identificar cuándo se completa.Look for an endOfConversation activity from any active skill, to notice when it completes.
  • Si es necesario, agregue lógica para que el usuario o el consumidor de aptitudes puedan cancelar una aptitud que aún no ha finalizado.If appropriate, add logic to let the user or skill consumer cancel a skill that has not completed yet.
  • Guarde el estado antes de realizar la llamada a una aptitud, ya que la respuesta podría volver a una instancia diferente del consumidor de aptitudesSave state before making the call to a skill, as any response may come back to a different instance of the skill consumer. (equilibrio de carga, etc.).(load balancing, etc.)

SimpleRootBot\Bots\RootBot.csSimpleRootBot\Bots\RootBot.cs

El bot raíz tiene dependencias del estado de la conversación, la información de las aptitudes, el cliente de aptitudes y la configuración general.The root bot has dependencies on conversation state, the skills information, the skill client, and the general configuration. ASP.NET proporciona estos objetos mediante la inserción de dependencias.ASP.NET provides these objects through dependency injection. El bot raíz también define un descriptor de acceso a la propiedad de estado de la conversación para realizar el seguimiento de qué aptitud está activa.The root bot also defines a conversation state property accessor to track which skill is active.

public static readonly string ActiveSkillPropertyName = $"{typeof(RootBot).FullName}.ActiveSkillProperty";
private readonly IStatePropertyAccessor<BotFrameworkSkill> _activeSkillProperty;
private readonly string _botId;
private readonly ConversationState _conversationState;
private readonly SkillHttpClient _skillClient;
private readonly SkillsConfiguration _skillsConfig;
private readonly BotFrameworkSkill _targetSkill;

public RootBot(ConversationState conversationState, SkillsConfiguration skillsConfig, SkillHttpClient skillClient, IConfiguration configuration)
{
    _conversationState = conversationState ?? throw new ArgumentNullException(nameof(conversationState));
    _skillsConfig = skillsConfig ?? throw new ArgumentNullException(nameof(skillsConfig));
    _skillClient = skillClient ?? throw new ArgumentNullException(nameof(skillsConfig));
    if (configuration == null)
    {
        throw new ArgumentNullException(nameof(configuration));
    }

    _botId = configuration.GetSection(MicrosoftAppCredentials.MicrosoftAppIdKey)?.Value;
    if (string.IsNullOrWhiteSpace(_botId))
    {
        throw new ArgumentException($"{MicrosoftAppCredentials.MicrosoftAppIdKey} is not set in configuration");
    }

    // We use a single skill in this example.
    var targetSkillId = "EchoSkillBot";
    if (!_skillsConfig.Skills.TryGetValue(targetSkillId, out _targetSkill))
    {
        throw new ArgumentException($"Skill with ID \"{targetSkillId}\" not found in configuration");
    }

    // Create state property to track the active skill
    _activeSkillProperty = conversationState.CreateProperty<BotFrameworkSkill>(ActiveSkillPropertyName);
}

En este ejemplo hay un método auxiliar para reenviar actividades a una aptitud.This sample has a helper method for forwarding activities to a skill. Guarda el estado de la conversación antes de invocar la aptitud y comprueba si la solicitud HTTP se realizó correctamente.It saves conversation state before invoking the skill, and it checks whether the HTTP request was successful.

private async Task SendToSkill(ITurnContext turnContext, BotFrameworkSkill targetSkill, CancellationToken cancellationToken)
{
    // NOTE: Always SaveChanges() before calling a skill so that any activity generated by the skill
    // will have access to current accurate state.
    await _conversationState.SaveChangesAsync(turnContext, force: true, cancellationToken: cancellationToken);

    // route the activity to the skill
    var response = await _skillClient.PostActivityAsync(_botId, targetSkill, _skillsConfig.SkillHostEndpoint, turnContext.Activity, cancellationToken);

    // Check response status
    if (!(response.Status >= 200 && response.Status <= 299))
    {
        throw new HttpRequestException($"Error invoking the skill id: \"{targetSkill.Id}\" at \"{targetSkill.SkillEndpoint}\" (status is {response.Status}). \r\n {response.Body}");
    }
}

Tenga en cuenta que el bot raíz incluye lógica para el reenvío de actividades a la aptitud, el inicio de la aptitud en la solicitud del usuario y la detención de la aptitud cuando esta se completa.Of note, the root bot includes logic for forwarding activities to the skill, starting the skill at the user's request, and stopping the skill when the skill completes.

protected override async Task OnMessageActivityAsync(ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken)
{
    if (turnContext.Activity.Text.Contains("skill"))
    {
        await turnContext.SendActivityAsync(MessageFactory.Text("Got it, connecting you to the skill..."), cancellationToken);

        // Save active skill in state
        await _activeSkillProperty.SetAsync(turnContext, _targetSkill, cancellationToken);

        // Send the activity to the skill
        await SendToSkill(turnContext, _targetSkill, cancellationToken);
        return;
    }

    // just respond
    await turnContext.SendActivityAsync(MessageFactory.Text("Me no nothin'. Say \"skill\" and I'll patch you through"), cancellationToken);

    // Save conversation state
    await _conversationState.SaveChangesAsync(turnContext, force: true, cancellationToken: cancellationToken);
}
protected override async Task OnEndOfConversationActivityAsync(ITurnContext<IEndOfConversationActivity> turnContext, CancellationToken cancellationToken)
{
    // forget skill invocation
    await _activeSkillProperty.DeleteAsync(turnContext, cancellationToken);

    // Show status message, text and value returned by the skill
    var eocActivityMessage = $"Received {ActivityTypes.EndOfConversation}.\n\nCode: {turnContext.Activity.Code}";
    if (!string.IsNullOrWhiteSpace(turnContext.Activity.Text))
    {
        eocActivityMessage += $"\n\nText: {turnContext.Activity.Text}";
    }

    if ((turnContext.Activity as Activity)?.Value != null)
    {
        eocActivityMessage += $"\n\nValue: {JsonConvert.SerializeObject((turnContext.Activity as Activity)?.Value)}";
    }

    await turnContext.SendActivityAsync(MessageFactory.Text(eocActivityMessage), cancellationToken);

    // We are back at the root
    await turnContext.SendActivityAsync(MessageFactory.Text("Back in the root bot. Say \"skill\" and I'll patch you through"), cancellationToken);

    // Save conversation state
    await _conversationState.SaveChangesAsync(turnContext, cancellationToken: cancellationToken);
}

Controlador de errores On turnOn turn error handler

Cuando se produce un error, el adaptador borra el estado de la conversación para restablecer la conversación con el usuario y evitar que un estado de error persista.When an error occurs, the adapter clears conversation state to reset the conversation with the user and avoid persisting an error state.

Se recomienda enviar una actividad de final de la conversación las aptitudes activas antes de borrar el estado de la conversación en el consumidor de aptitudes.It is a good practice to send an end of conversation activity to any active skill before clearing conversation state in the skill consumer. De este modo, la aptitud libera todos los recursos asociados con la conversación entre el consumidor y la aptitud antes de que el consumidor de aptitudes libere la conversación.This lets the skill release any resources associated with the consumer-skill conversation before the skill consumer releases the conversation.

SimpleRootBot\AdapterWithErrorHandler.csSimpleRootBot\AdapterWithErrorHandler.cs

En este ejemplo, la lógica de errores de turno se divide entre algunos métodos auxiliares.In this sample the turn error logic is split up among a few helper methods.

private async Task HandleTurnError(ITurnContext turnContext, Exception exception)
{
    // Log any leaked exception from the application.
    // NOTE: In production environment, you should consider logging this to
    // Azure Application Insights. Visit https://aka.ms/bottelemetry to see how
    // to add telemetry capture to your bot.
    _logger.LogError(exception, $"[OnTurnError] unhandled error : {exception.Message}");

    await SendErrorMessageAsync(turnContext, exception);
    await EndSkillConversationAsync(turnContext);
    await ClearConversationStateAsync(turnContext);
}

private async Task SendErrorMessageAsync(ITurnContext turnContext, Exception exception)
{
    try
    {
        // Send a message to the user
        var errorMessageText = "The bot 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
        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 EndSkillConversationAsync(ITurnContext turnContext)
{
    if (_skillClient == null || _skillsConfig == null)
    {
        return;
    }

    try
    {
        // Inform the active skill that the conversation is ended so that it has
        // a chance to clean up.
        // Note: ActiveSkillPropertyName is set by the RooBot while messages are being
        // forwarded to a Skill.
        var activeSkill = await _conversationState.CreateProperty<BotFrameworkSkill>(RootBot.ActiveSkillPropertyName).GetAsync(turnContext, () => null);
        if (activeSkill != null)
        {
            var botId = _configuration.GetSection(MicrosoftAppCredentials.MicrosoftAppIdKey)?.Value;

            var endOfConversation = Activity.CreateEndOfConversationActivity();
            endOfConversation.Code = "RootSkillError";
            endOfConversation.ApplyConversationReference(turnContext.Activity.GetConversationReference(), true);

            await _conversationState.SaveChangesAsync(turnContext, true);
            await _skillClient.PostActivityAsync(botId, activeSkill, _skillsConfig.SkillHostEndpoint, (Activity)endOfConversation, CancellationToken.None);
        }
    }
    catch (Exception ex)
    {
        _logger.LogError(ex, $"Exception caught on attempting to send EndOfConversation : {ex}");
    }
}

private async Task ClearConversationStateAsync(ITurnContext turnContext)
{
    try
    {
        // Delete the conversationState for the current conversation to prevent the
        // bot from getting stuck in a error-loop caused by being in a bad state.
        // ConversationState should be thought of as similar to "cookie-state" in a Web pages.
        await _conversationState.DeleteAsync(turnContext);
    }
    catch (Exception ex)
    {
        _logger.LogError(ex, $"Exception caught on attempting to Delete ConversationState : {ex}");
    }
}

Punto de conexión de aptitudesSkills endpoint

El bot define un punto de conexión que reenvía las actividades de aptitudes entrantes al controlador de aptitudes del bot raíz.The bot defines an endpoint that forwards incoming skill activities to the root bot's skill handler.

SimpleRootBot\Controllers\SkillController.csSimpleRootBot\Controllers\SkillController.cs

[ApiController]
[Route("api/skills")]
public class SkillController : ChannelServiceController
{
    public SkillController(ChannelServiceHandler handler)
        : base(handler)
    {
    }
}

Registro de serviciosService registration

Incluya un objeto de configuración de autenticación con la validación de notificaciones, además de todos los objetos adicionales.Include an authentication configuration object with any claims validation, plus all the additional objects. En este ejemplo se usa la misma lógica de configuración de autenticación para validar las actividades de los usuarios y las aptitudes.This sample uses the same authentication configuration logic for validating activities from both users and skills.

SimpleRootBot\Startup.csSimpleRootBot\Startup.cs

// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers().AddNewtonsoftJson();

    // Configure credentials
    services.AddSingleton<ICredentialProvider, ConfigurationCredentialProvider>();

    // Register the skills configuration class
    services.AddSingleton<SkillsConfiguration>();

    // Register AuthConfiguration to enable custom claim validation.
    services.AddSingleton(sp => new AuthenticationConfiguration { ClaimsValidator = new AllowedSkillsClaimsValidator(sp.GetService<SkillsConfiguration>()) });

    // Register the Bot Framework Adapter with error handling enabled.
    // Note: some classes use the base BotAdapter so we add an extra registration that pulls the same instance.
    services.AddSingleton<BotFrameworkHttpAdapter, AdapterWithErrorHandler>();
    services.AddSingleton<BotAdapter>(sp => sp.GetService<BotFrameworkHttpAdapter>());

    // Register the skills client and skills request handler.
    services.AddSingleton<SkillConversationIdFactoryBase, SkillConversationIdFactory>();
    services.AddHttpClient<SkillHttpClient>();
    services.AddSingleton<ChannelServiceHandler, SkillHandler>();

    // Register the storage we'll be using for User and Conversation state. (Memory is great for testing purposes.)
    services.AddSingleton<IStorage, MemoryStorage>();

    // Register Conversation state (used by the Dialog system itself).
    services.AddSingleton<ConversationState>();

    // Register the bot as a transient. In this case the ASP Controller is expecting an IBot.
    services.AddTransient<IBot, RootBot>();
}

Prueba del bot raízTest the root bot

Puede probar el consumidor de aptitudes en el emulador como si fuera un bot normal; sin embargo, tiene que ejecutar los bots de aptitud y del consumidor de aptitudes al mismo tiempo.You can test the skill consumer in the Emulator as if it were a normal bot; however, you need to run both the skill and skill consumer bots at the same time. Vea cómo implementar una aptitud para más información sobre cómo configurar la aptitud.See how to implement a skill for information on how to configure the skill.

Descargue e instale la versión más reciente de Bot Framework Emulator.Download and install the latest Bot Framework Emulator

  1. Ejecute el bot de aptitud de eco y el bot raíz simple en su máquina local.Run the echo skill bot and simple root bot locally on your machine. Si necesita instrucciones, consulte el archivo LÉAME del ejemplo de C#, JavaScript, Javao Python.If you need instructions, refer to the README file for the C#, JavaScript, Java, or Python sample.
  2. Use el emulador para probar el bot tal y como se muestra a continuación.Use the Emulator to test the bot as shown below. Tenga en cuenta que cuando se envía un mensaje end o stop a la aptitud, esta envía al bot raíz una actividad endOfConversation, además del mensaje de respuesta.Note that when you send an end or stop message to the skill, the skill sends to the root bot an endOfConversation activity, in addition to the reply message. La propiedad code de la actividad endOfConversation indica que la aptitud se completó correctamente.The endOfConversation activity's code property indicates that the skill completed successfully.

prueba del consumidor de aptitudes

Información adicionalAdditional information

Estos son algunos aspectos que se deben tener en cuenta al implementar un bot raíz más complejo.Here are some things to consider when implementing a more complex root bot.

Para que el usuario pueda cancelar una aptitud de varios pasosTo allow the user to cancel a multi-step skill

El bot raíz debe comprobar el mensaje del usuario antes de reenviarlo a la aptitud activa.The root bot should check the user's message before forwarding it to the active skill. Si el usuario desea cancelar el proceso actual, el bot raíz puede enviar una actividad endOfConversation a la aptitud, en lugar de reenviar el mensaje.If the user wants to cancel the current process, the root bot can send an endOfConversation activity to the skill, instead of forwarding the message.

Para intercambiar datos entre el bot raíz y el bot de aptitudTo exchange data between the root and skill bots

Para enviar parámetros a la aptitud, el consumidor de aptitudes puede establecer la propiedad value en los mensajes que envía a la aptitud.To send parameters to the skill, the skill consumer can set the value property on messages it sends to the skill. Para recibir los valores devueltos de las aptitudes, el consumidor de aptitudes debe comprobar la propiedad value cuando la aptitud envíe una actividad endOfConversation.To receive return values from the skill, the skill consumer should check the value property when the skill sends an endOfConversation activity.

Para usar varias aptitudesTo use multiple skills

  • Si hay una aptitud activa, el bot raíz debe determinar cuál y reenviar el mensaje del usuario a la aptitud correcta.If a skill is active, the root bot needs to determine which skill is active and forward the user's message to the correct skill.
  • Si no hay ninguna aptitud activa, el bot raíz debe determinar qué aptitud debe iniciarse, según el estado del bot y los datos de entrada del usuario.If no skill is active, the root bot needs to determine which skill to start, if any, based on bot state and the user's input.
  • Si desea que el usuario pueda cambiar entre varias aptitudes simultáneas, el bot raíz debe determinar con qué aptitudes activas tiene previsto interactuar el usuario antes de reenviar el mensaje del usuario.If you want to allow the user to switch between multiple concurrent skills, the root bot needs to determine which of the active skills the user is intending to interact with before forwarding the user's message.

Para usar un modo de entrega de respuestas esperadasTo use a delivery mode of expect replies

Para usar el modo de entrega de espera de respuestas:To use the expect replies delivery mode:

  • Clone la actividad desde el contexto de turno.Clone the activity from the turn context.
  • Establezca la propiedad de modo de entrega de la nueva actividad en "ExpectReplies" antes de enviar la actividad desde el bot raíz a la aptitud.Set the delivery mode property of the new activity to "ExpectReplies" before sending the activity from root bot to skill.
  • Lea las respuestas esperadas del cuerpo de la respuesta de invocación devueltas desde la respuesta de la solicitud.Read expected replies from the invoke response body returned from the request response.
  • Procese cada actividad, ya sea dentro del bot raíz o enviándolo al canal que inició la solicitud original.Process each activity, either within the root bot or by sending it on to the channel which initiated the original request.

Las respuestas esperadas pueden ser útiles en situaciones en las que el bot que responde a una actividad debe ser la misma instancia del bot que recibió la actividad.Expect replies can be useful in situations in which the bot that replies to an activity needs to be the same instance of the bot that received the activity.