スキルを実装する

適用対象: SDK v4

スキルを使用して別のボットを拡張することができます。 "スキル" は、別のボットに対して一連のタスクを実行できるボットです。

  • スキルのインターフェイスはマニフェストで記述されます。 スキルのソース コードにアクセスできない開発者は、マニフェストの情報を使用してスキル コンシューマーを設計できます。
  • スキルでは、要求検証を使用して、スキルにアクセスできるボットやユーザーを管理できます。

この記事では、ユーザーの入力をエコーするスキルを実装する方法について説明します。

一部の種類のスキル コンシューマーは、一部の種類のスキル ボットを使用できません。 次の表では、サポートされる組み合わせについて説明します。

  マルチテナント スキル シングルテナント スキル ユーザー割り当てマネージド ID スキル
マルチテナント コンシューマー サポートされています サポートされていません サポートされていません
シングルテナント コンシューマー サポートされていません 両方のアプリが同じテナントに属している場合にサポートされます 両方のアプリが同じテナントに属している場合にサポートされます
ユーザー割り当てマネージド ID コンシューマー サポートされていません 両方のアプリが同じテナントに属している場合にサポートされます 両方のアプリが同じテナントに属している場合にサポートされます

前提条件

注意

バージョン 4.11 以降では、Bot Framework Emulatorでスキルをローカルでテストするためにアプリ ID とパスワードは必要ありません。 スキルを Azure にデプロイするには、引き続き Azure サブスクリプションが必要です。

このサンプルについて

skills simple bot-to-bot サンプルには、次の 2 つのボットのプロジェクトが含まれています。

  • スキルを実装する "エコー スキル ボット"。
  • スキルを使用するルート ボットを実装する "単純なルート ボット"。

この記事では、ボットとアダプターにサポート ロジックが組み込まれているスキルに焦点を当てます。

単純なルート ボットについては、「スキル コンシューマーを実装する」を参照してください。

リソース

デプロイされたボットの場合、ボット間認証では、参加している各ボットに有効な ID 情報が必要です。 ただし、アプリ ID とパスワードを使用せずに、Emulatorを使用してマルチテナント スキルとスキル コンシューマーをローカルでテストできます。

ユーザー向けボットでスキルを使用できるようにするには、そのスキルを Azure に登録します。 詳細については、「ボットを Azure Bot Service に登録する」を参照してください。

アプリケーションの構成

必要に応じて、スキルの ID 情報を構成ファイルに追加します。 スキルまたはスキル コンシューマーのいずれかが ID 情報を提供する場合は、両方が必要です。

"許可される呼び出し元" の配列 (AllowedCallers) では、スキルにアクセスできるスキル コンシューマーを制限できます。 "*" 要素を追加して、スキル コンシューマーからの呼び出しを受け入れます。

注意

ボット ID 情報なしでスキルをローカルでテストする場合、スキルもスキル コンシューマーも、要求の検証を実行するためにコードを実行しません。

EchoSkillBot\appsettings.json

必要に応じて、スキルの ID 情報を 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": [ "*" ]
}

アクティビティ ハンドラーのロジック

入力パラメーターを受け入れる

スキル コンシューマーはスキルに情報を送信できます。 この情報を受け入れる方法の 1 つは、受信したメッセージの value プロパティを介して受け入れることです。 もう 1 つの方法は、イベントを処理し、アクティビティを呼び出すことです。

この例のスキルは、入力パラメーターを受け入れていません。

会話を続行または完了する

スキルからアクティビティが送信されると、スキル コンシューマーからユーザーにそのアクティビティが転送されます。

ただし、スキルが終了したときは endOfConversation アクティビティを送信する必要があります。そうしないと、スキル コンシューマーからスキルにユーザー アクティビティが転送され続けます。 必要に応じて、アクティビティの value プロパティに戻り値を入れ、アクティビティの code プロパティを使用してスキルが終了する理由を示します。

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);
    }
}

スキルをキャンセルする

複数ターン スキルの場合は、スキル コンシューマーから endOfConversation アクティビティを受け入れて、コンシューマーが現在の会話をキャンセルできるようにすることもできます。

このスキルのロジックは順番に変更されません。 会話リソースを割り当てるスキルを実装する場合は、会話終了ハンドラーにリソース クリーンアップ コードを追加します。

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;
}

要求検証コントロール

このサンプルでは、許可される呼び出し元のリストが要求検証に使用されます。 このリストは、スキルの構成ファイルで定義され、作成時に検証コントロール オブジェクトに読み込まれます。

要求検証コントロールを認証構成に追加する必要があります。 要求は認証ヘッダーの後に評価されます。 要求を拒否する場合は、検証コードからエラーまたは例外をスローする必要があります。 通常であれば認証される要求を、さまざまな理由で拒否することができます。 次に例を示します。

  • スキルが有料サービスの一部である。 データベースにユーザーがいない場合は、アクセス権を持つべきではありません。
  • スキルが専用のものである。 特定のスキル コンシューマーのみがスキルを呼び出すことができます。

重要

要求検証コントロールを指定しない場合、ボットはスキル コンシューマーからアクティビティを受け取るとエラーまたは例外を生成します。

SDK には、スキルの AllowedCallersClaimsValidator 呼び出しが許可されているアプリケーションの ID の単純な一覧に基づいて、アプリケーション レベルの承認を追加するクラスが用意されています。 リストにアスタリスク (*)が含まれている場合、すべての呼び出し元が許可されます。 要求検証コントロールは Startup.cs で構成されます。

スキル アダプター

エラーが発生した場合は、スキルのアダプターは、スキルの会話状態をクリアするとともに、スキル コンシューマーに endOfConversation アクティビティを送信する必要があります。 アクティビティの code プロパティを使用して、エラーのためにスキルが終了したことを通知します。

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}");
    }
}

サービス登録

"Bot Framework アダプター" は "認証構成" オブジェクト (アダプターの作成時に設定) を使用して、受信した要求の認証ヘッダーを検証します。

次のサンプルでは、要求検証を認証構成に追加し、前のセクションで説明した "エラー ハンドラー付きスキル アダプター" を使用しています。

EchoSkillBot\Startup.cs

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

// Create the Bot Framework Adapter with error handling enabled.
services.AddSingleton<IBotFrameworkHttpAdapter, SkillAdapterWithErrorHandler>();

スキル マニフェスト

"スキル マニフェスト" は、スキルが実行できるアクティビティ、その入力パラメーターと出力パラメーター、およびスキルのエンドポイントが記述された JSON ファイルです。 マニフェストには、別のボットからスキルにアクセスするために必要な情報が含まれています。 最新のスキーマ バージョンは 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"
    }
  ]
}

"スキル マニフェスト スキーマ" は、スキル マニフェストのスキーマが記述された JSON ファイルです。 現在のスキーマ バージョンは 2.1.0 です。

スキルのテスト

この時点で、スキルを通常のボットと同様にエミュレーターでテストできます。 ただし、スキルとしてテストするには、スキル コンシューマーを実装する必要があります。

最新の Bot Framework Emulator をダウンロードしてインストールします

  1. エコー スキル ボットをお使いのマシンでローカルに実行します。 手順が必要な場合は、C#JavaScriptJava、または Python サンプルのファイルを参照してくださいREADME
  2. 次に示すように、エミュレーターを使用してボットをテストします。 スキルに "終了" または "停止" メッセージを送信すると、応答メッセージに加えてアクティビティが送信 endOfConversation されます。 スキルが終了したことが示す endOfConversation アクティビティがスキルから送信されます。

test the echo skill

デバッグの詳細

スキルとスキル コンシューマーの間のトラフィックは認証されるため、このようなボットをデバッグする際には追加の手順があります。

  • スキル コンシューマーと、消費するすべてのスキルが、直接的または間接的に実行されている必要があります。
  • ボットがローカルで実行されていて、いずれかのボットにアプリ ID とパスワードがある場合、すべてのボットに有効な ID とパスワードが必要です。
  • ボットがすべてデプロイされている場合は、 ngrok を使用して任意のチャネルからボットをデバッグする方法を参照してください。
  • 一部のボットがローカルで実行されていて、一部がデプロイされている場合は、 スキルまたはスキル コンシューマーをデバッグする方法を参照してください。

それ以外の場合は、他のボットをデバッグするのと同じように、スキル コンシューマーまたはスキルをデバッグできます。 詳細については、「ボットのデバッグ」と「Bot Framework Emulatorを使用したデバッグ」を参照してください

次のステップ