Implementieren eines Skill-ConsumersImplement a skill consumer

gilt für: SDK v4APPLIES TO: SDK v4

Sie können Qualifikationen verwenden, um einen anderen Bot zu erweitern.You can use skills to extend another bot. Eine Qualifikation ist ein Bot, der eine Reihe von Aufgaben für einen anderen Bot durchführen kann und ein Manifest nutzt, um seine Oberfläche zu beschreiben.A skill is a bot that can perform a set of tasks for another bot and uses a manifest to describe its interface. Ein Stamm-Bot ist ein Bot für Benutzer, der eine oder mehrere Qualifikationen aufrufen kann.A root bot is a user-facing bot that can invoke one or more skills. Ein Stamm-Bot ist eine Art von Skill-Consumer.A root bot is a type of skill consumer.

  • Ein Skill-Consumer muss die Anspruchsvalidierung verwenden, um zu verwalten, welche Qualifikationen darauf zugreifen können.A skill consumer must use claims validation to manage which skills can access it.
  • Ein Skill-Consumer kann mehrere Qualifikationen verwenden.A skill consumer can use multiple skills.
  • Entwickler, die keinen Zugriff auf den Quellcode der Qualifikation haben, können die Informationen im Manifest der Qualifikation nutzen, um ihren „Skill-Consumer“ zu entwerfen.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.

In diesem Artikel wird veranschaulicht, wie Sie einen Skill-Consumer implementieren, von dem die Echo-Qualifikation (Echo Skill) genutzt wird, um die Eingabe des Benutzers zu wiederholen.This article demonstrates how to implement a skill consumer that uses the echo skill to echo the user's input. Ein Beispiel für ein Qualifikationsmanifest und Informationen zur Implementierung der Echo-Qualifikation finden Sie unter Implementieren einer Qualifikation.For a sample skill manifest and information about implementing the echo skill, see how to implement a skill.

Informationen zum Verwenden eines Dialogs zum Nutzen eines Skills finden Sie unter Verwenden eines Dialogs zum Nutzen eines Skills.For information about using a skill dialog to consume a skill, see how to use a dialog to consume a skill.

VoraussetzungenPrerequisites

Hinweis

Ab Version 4.11 benötigen Sie keine App-ID und kein Kennwort, um einen Skill-Consumer lokal im Emulator zu testen.Starting with version 4.11, you do not need an app ID and password to test a skill consumer locally in the Emulator. Ein Azure-Abonnement ist weiterhin erforderlich, um Ihren Consumer in Azure bereitzustellen oder eine bereitgestellte Qualifikation zu nutzen.An Azure subscription is still required to deploy your consumer to Azure or to consume a deployed skill.

Informationen zu diesem BeispielAbout this sample

Das Beispiel skills simple bot-to-bot enthält Projekte für zwei Bots:The skills simple bot-to-bot sample includes projects for two bots:

  • Echo Skill Bot (Echo-Bot für Qualifikation) zum Implementieren der Qualifikation.The echo skill bot, which implements the skill.
  • Simple Root Bot (einfacher Stamm-Bot) zum Implementieren eines Stamm-Bots, der die Qualifikation nutzt.The simple root bot, which implements a root bot that consumes the skill.

Der Schwerpunkt dieses Artikels liegt auf dem Stamm-Bot, und es wird die Unterstützungslogik in den Bot- und Adapterobjekten beschrieben. Darüber hinaus werden Objekte beschrieben, die zum Austauschen von Aktivitäten mit einer Qualifikation verwendet werden.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. Dazu gehören:These include:

  • Ein Skill-Client, der zum Senden von Aktivitäten an eine Qualifikation verwendet wird.A skill client, used to send activities to a skill.
  • Ein Skill-Handler, der zum Empfangen von Aktivitäten von einer Qualifikation verwendet wird.A skill handler, used to receive activities from a skill.
  • Eine Konversations-ID-Factory für Qualifikationen, die vom Skill-Client und -Handler genutzt wird, um die Übersetzung zwischen dem Benutzer/Stamm-Konversationsverweis und dem Stamm/Qualifikation-Konversationsverweis durchzuführen.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.

Weitere Informationen zum „Echo Skill Bot“ finden Sie unter Implementieren einer Qualifikation.For information about the echo skill bot, see how to Implement a skill.

RessourcenResources

Für bereitgestellte Bots erfordert die Bot-zu-Bot-Authentifizierung, dass jeder teilnehmende Bot über eine gültige App-ID und ein Kennwort verfügt.For deployed bots, bot-to-bot authentication requires that each participating bot has a valid app ID and password. Sie können Skills und Skill-Consumer jedoch lokal mit dem Emulator ohne App-ID und Kennwort testen.However, you can test skills and skill consumers locally with the Emulator without an app ID and password.

AnwendungskonfigurationApplication configuration

  1. Fügen Sie der Konfigurationsdatei optional die App-ID und das Kennwort des Stammbots hinzu.Optionally, add the root bot's app ID and password to the config file.
  2. Fügen Sie den Skillhostendpunkt (den Dienst oder die Rückruf-URL) hinzu, auf den die Skills dem Skill-Consumer antworten sollen.Add the skill host endpoint (the service or callback URL) to which the skills should reply to the skill consumer.
  3. Fügen Sie einen Eintrag für jede Qualifikation hinzu, die vom Skill-Consumer verwendet wird.Add an entry for each skill the skill consumer will use. Jeder Eintrag umfasst Folgendes:Each entry includes:
    • Eine ID, die vom Skill-Consumer verwendet wird, um die einzelnen Qualifikationen zu identifizieren.An ID the skill consumer will use to identify each skill.
    • Optional die App-ID des Skills.Optionally, the skill's app ID.
    • Den Messagingendpunkt der Qualifikation.The skill's messaging endpoint.

Hinweis

Wenn der Skill- oder Skill-Consumer eine App-ID und ein Kennwort verwendet, müssen beides.If either the skill or skill consumer uses an app ID and password, both must.

SimpleRootBot\appsettings.jsonSimpleRootBot\appsettings.json

Fügen Sie optional die App-ID und das Kennwort des Stammbots hinzu, und fügen Sie dem Array die App-ID für den Echo-Skillbot BotFrameworkSkills hinzu.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"
    }
  ]
}

Konfiguration von QualifikationenSkills configuration

In diesem Beispiel werden Informationen zu den einzelnen Qualifikationen in die Konfigurationsdatei einer Sammlung mit Objekten vom Typ Qualifikation eingelesen.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>();
}

Konversations-ID-FactoryConversation ID factory

Hiermit wird die Konversations-ID für die Qualifikation erstellt, und die ursprüngliche ID der Benutzerkonversation kann aus der Konversations-ID der Qualifikation wiederhergestellt werden.This creates the conversation ID for use with the skill and can recover the original user conversation ID from the skill conversation ID.

Die Konversations-ID-Factory für dieses Beispiel unterstützt ein einfaches Szenario, für das Folgendes gilt:The conversation ID factory for this sample supports a simple scenario where:

  • Der Stamm-Bot ist dafür ausgelegt, eine bestimmte Qualifikation zu nutzen.The root bot is designed to consume one specific skill.
  • Der Stamm-Bot verfügt jeweils nur über eine aktive Konversation mit einer Qualifikation.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;
    }
}

Entwerfen Sie Ihre Konversations-ID-Factory wie folgt, um komplexere Szenarien zu unterstützen:To support more complex scenarios, design your conversation ID factory so that:

  • Mit der create skill conversation ID-Methode wird die entsprechende Konversations-ID für eine Qualifikation abgerufen bzw. generiert.The create skill conversation ID method gets or generates the appropriate skill conversation ID.
  • Mit der get conversation reference-Methode wird die richtige Benutzerkonversation abgerufen.The get conversation reference method gets the correct user conversation.

Skill-Client und Skill-HandlerSkill client and skill handler

Der Skill-Consumer verwendet einen Skill-Client, um Aktivitäten an die Qualifikation weiterzuleiten.The skill consumer uses a skill client to forward activities to the skill. Der Client nutzt hierfür die Konfigurationsinformationen für die Qualifikation und die Konversations-ID-Factory.The client uses the skills configuration information and conversation ID factory to do so.

Der Skill-Consumer nutzt einen Skill-Handler, um Aktivitäten von einer Qualifikation zu empfangen.The skill consumer uses a skill handler to receive activities from a skill. Der Handler nutzt hierfür die Konversations-ID-Factory, die Authentifizierungskonfiguration und einen Anmeldeinformationsanbieter und verfügt zudem über Abhängigkeiten vom Adapter und Aktivitätshandler des Stamm-Bots.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>();

Der HTTP-Datenverkehr der Qualifikation fließt an den URL-Dienstendpunkt, der vom Skill-Consumer für die Qualifikation angekündigt wird.HTTP traffic from the skill will come into the service URL endpoint that the skill consumer advertizes to the skill. Verwenden Sie einen sprachspezifischen Endpunkthandler, um Datenverkehr an den Skill-Handler weiterzuleiten.Use a language-specific endpoint handler to forward traffic to the skill handler.

Für den Skill-Standardhandler gilt Folgendes:The default skill handler:

  • Wenn eine App-ID und ein Kennwort vorhanden sind, verwendet ein Authentifizierungskonfigurationsobjekt, um sowohl die Bot-zu-Bot-Authentifizierung als auch die Anspruchsüberprüfung durchzuführen.If an app ID and password are present, uses an authentication configuration object to perform both bot-to-bot authentication and claims validation.
  • Verwendet die Konversations-ID-Factory, um die Rückübersetzung von der Consumer/Qualifikation-Konversation in die Stamm/Benutzer-Konversation durchzuführen.Uses the conversation ID factory to translate from the consumer-skill conversation back to the root-user conversation.
  • Generiert eine proaktive Nachricht, damit der Skill-Consumer wieder einen Stamm/Benutzer-Durchlaufkontext herstellen und Aktivitäten an den Benutzer weiterleiten kann.Generates a proactive message so that the skill consumer can reestablish a root-user turn context and forward activities to the user.

AktivitätshandlerlogikActivity handler logic

In Bezug auf die Logik des Skill-Consumers sollte Folgendes beachtet werden:Of note, the skill consumer logic should:

  • Ermitteln Sie, ob aktive Qualifikationen vorhanden sind, und leiten Sie dafür je nach Bedarf Aktivitäten weiter.Remember whether there are any active skills and forward activities to them as appropriate.
  • Achten Sie darauf, wann ein Benutzer eine Anforderung sendet, die an eine Qualifikation weitergeleitet werden soll, und starten Sie dann die Qualifikation.Notice when a user makes a request that should be forwarded to a skill, and start the skill.
  • Suchen Sie nach einer endOfConversation-Aktivität einer beliebigen aktiven Qualifikation, um verfolgen zu können, wann sie abgeschlossen wird.Look for an endOfConversation activity from any active skill, to notice when it completes.
  • Fügen Sie Logik hinzu (falls zutreffend), mit der der Benutzer oder Skill-Consumer eine Qualifikation abbrechen kann, die noch nicht abgeschlossen wurde.If appropriate, add logic to let the user or skill consumer cancel a skill that has not completed yet.
  • Speichern Sie den Zustand, bevor Sie eine Qualifikation aufrufen, da es sein kann, dass eine Antwort an eine andere Instanz des Skill-Consumers zurückgesendet wird.Save state before making the call to a skill, as any response may come back to a different instance of the skill consumer. (Dies kann beispielsweise der Lastenausgleich oder eine andere Komponente sein.)(load balancing, etc.)

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

Der Stamm-Bot verfügt über Abhängigkeiten in Bezug auf den Konversationszustand, die Qualifikationsinformationen, den Skill-Client und die allgemeine Konfiguration.The root bot has dependencies on conversation state, the skills information, the skill client, and the general configuration. ASP.NET stellt diese Objekte per Abhängigkeitsinjektion bereit.ASP.NET provides these objects through dependency injection. Der Stamm-Bot definiert auch einen Eigenschaftenaccessor für den Konversationszustand, um nachzuverfolgen, welche Qualifikation aktiv ist.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);
}

Dieses Beispiel enthält eine Hilfsmethode für die Weiterleitung von Aktivitäten an eine Qualifikation.This sample has a helper method for forwarding activities to a skill. Der Konversationszustand wird gespeichert, bevor die Qualifikation aufgerufen wird, und es wird überprüft, ob die HTTP-Anforderung erfolgreich war.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}");
    }
}

Beachten Sie, dass der Stammbot Logik für das Weiterleiten von Aktivitäten an den Skill beinhaltet, mit der der Skill auf Anforderung des Benutzers gestartet und bei Abschluss des Skills beendet wird.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);
}

OnTurn-FehlerhandlerOn turn error handler

Wenn ein Fehler auftritt, löscht der Adapter den Konversationszustand, um die Konversation mit dem Benutzer zurückzusetzen und einen dauerhaften Fehlerzustand zu vermeiden.When an error occurs, the adapter clears conversation state to reset the conversation with the user and avoid persisting an error state.

Es ist eine bewährte Vorgehensweise, eine Aktivität vom Typ end of conversation an aktive Qualifikationen zu senden, bevor der Konversationszustand im Skill-Consumer gelöscht wird.It is a good practice to send an end of conversation activity to any active skill before clearing conversation state in the skill consumer. Auf diese Weise kann die Qualifikation alle Ressourcen freigeben, die der Consumer/Qualifikation-Konversation zugeordnet sind, bevor der Skill-Consumer die Konversation freigibt.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

In diesem Beispiel ist die Durchlauffehlerlogik auf mehrere Hilfsmethoden aufgeteilt.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}");
    }
}

Endpunkt für QualifikationSkills endpoint

Der Bot definiert einen Endpunkt, mit dem eingehende Qualifikationsaktivitäten an den Skill-Handler des Stamm-Bots weitergeleitet werden.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)
    {
    }
}

DienstregistrierungService registration

Fügen Sie ein Objekt für die Authentifizierungskonfiguration mit den zugehörigen Anspruchsüberprüfungen sowie alle zusätzlichen Objekte hinzu.Include an authentication configuration object with any claims validation, plus all the additional objects. In diesem Beispiel wird die gleiche Authentifizierungskonfigurationslogik verwendet, um Aktivitäten von Benutzern und Qualifikationen zu überprüfen.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>();
}

Testen des Stamm-BotsTest the root bot

Sie können den Skill-Consumer im Emulator wie einen regulären Bot testen. Sie müssen aber sowohl den Bot für die Qualifikation als auch den Bot für den Skill-Consumer ausführen.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. Informationen zum Konfigurieren der Qualifikation finden Sie unter Implementieren einer Qualifikation.See how to implement a skill for information on how to configure the skill.

Laden Sie die aktuelle Version von Bot Framework Emulator herunter, und installieren Sie sie.Download and install the latest Bot Framework Emulator

  1. Führen Sie den „Echo Skill Bot“ und den „Simple Root Bot“ lokal auf Ihrem Computer aus.Run the echo skill bot and simple root bot locally on your machine. Anweisungen finden Sie in der README-Datei für das C#-, JavaScript-, Java-oder Python-Beispiel.If you need instructions, refer to the README file for the C#, JavaScript, Java, or Python sample.
  2. Verwenden Sie den Emulator, um den Bot wie unten dargestellt zu testen.Use the Emulator to test the bot as shown below. Beachten Sie Folgendes: Wenn Sie eine end- oder stop-Nachricht an die Qualifikation senden, sendet die Qualifikation zusätzlich zur Antwortnachricht eine endOfConversation-Aktivität an den Stamm-Bot.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. Die code-Eigenschaft der endOfConversation-Aktivität gibt an, dass die Qualifikation erfolgreich abgeschlossen wurde.The endOfConversation activity's code property indicates that the skill completed successfully.

Testen des Skill-Consumers

Zusätzliche InformationenAdditional information

Hier werden einige Aspekte beschrieben, die Sie beim Implementieren eines komplexeren Stamm-Bots berücksichtigen sollten.Here are some things to consider when implementing a more complex root bot.

So lassen Sie für Benutzer das Abbrechen einer Qualifikation mit mehreren Schritten zuTo allow the user to cancel a multi-step skill

Der Stamm-Bot sollte die Nachricht des Benutzers überprüfen, bevor er sie an die aktive Qualifikation weiterleitet.The root bot should check the user's message before forwarding it to the active skill. Wenn der Benutzer den aktuellen Prozess abbrechen möchte, kann der Stamm-Bot eine endOfConversation-Aktivität an die Qualifikation senden, anstatt die Nachricht weiterzuleiten.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.

So tauschen Sie die Daten zwischen Stamm-Bots und Skill-Bots ausTo exchange data between the root and skill bots

Zum Senden von Parametern an die Qualifikation kann der Skill-Consumer die value-Eigenschaft für die Nachrichten festlegen, die er an die Qualifikation sendet.To send parameters to the skill, the skill consumer can set the value property on messages it sends to the skill. Der Skill-Consumer sollte die value-Eigenschaft überprüfen, wenn die Qualifikation eine endOfConversation-Aktivität sendet, um Rückgabewerte von der Qualifikation zu erhalten.To receive return values from the skill, the skill consumer should check the value property when the skill sends an endOfConversation activity.

So verwenden Sie mehrere QualifikationenTo use multiple skills

  • Wenn eine Qualifikation aktiv ist, muss der Stamm-Bot ermitteln, welche Qualifikation dies ist, und die Nachricht des Benutzers an die richtige Qualifikation weiterleiten.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.
  • Falls keine Qualifikation aktiv ist, muss der Stamm-Bot anhand des Botzustands und der Eingabe des Benutzers ermitteln, welche Qualifikation gestartet werden soll (falls zutreffend).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.
  • Wenn Sie für Benutzer den Wechsel zwischen mehreren gleichzeitigen Qualifikationen zulassen möchten, muss der Stamm-Bot ermitteln, mit welcher aktiven Qualifikation der Benutzer interagieren möchte, bevor die Nachricht des Benutzers weitergeleitet wird.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.

So verwenden Sie den Übermittlungsmodus "Antworten erwarten"To use a delivery mode of expect replies

So verwenden Sie den Übermittlungsmodus "Antworten erwarten":To use the expect replies delivery mode:

  • Klonen Sie die Aktivität aus dem Turn-Kontext.Clone the activity from the turn context.
  • Legen Sie die Übermittlungsmoduseigenschaft der neuen Aktivität auf "ExpectReplies" fest, bevor Sie die Aktivität vom Stammbot an die Qualifikation senden.Set the delivery mode property of the new activity to "ExpectReplies" before sending the activity from root bot to skill.
  • Liest erwartete Antworten aus dem Aufrufantwort-Text, der von der Anforderungsantwort zurückgegeben wird.Read expected replies from the invoke response body returned from the request response.
  • Verarbeiten Sie jede Aktivität, entweder innerhalb des Stammbots oder durch Senden an den Kanal, der die ursprüngliche Anforderung initiiert hat.Process each activity, either within the root bot or by sending it on to the channel which initiated the original request.

Antworten erwarten kann in Situationen nützlich sein, in denen der Bot, der auf eine Aktivität antwortet, dieselbe Instanz des Bots sein muss, der die Aktivität empfangen hat.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.