スキルを実装する

この記事の対象: SDK v4

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

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

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

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

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

Note

Bot Framework JavaScript SDK、C#、Python SDK は引き続きサポートされますが、Java SDK については、最終的な長期サポートは 2023 年 11 月に終了する予定です。 このリポジトリ内の重要なセキュリティとバグの修正のみが行われます。

Java SDK を使用して構築された既存のボットは引き続き機能します。

新しいボットの構築については、Power Virtual Agents の使用を検討し、適切なチャットボット ソリューションの選択についてお読みください。

詳細については、「The future of bot building」をご覧ください。

前提条件

Note

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

このサンプルについて

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

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

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

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

リソース

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

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

アプリケーション構成

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

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

Note

ボット 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 には、スキルの呼び出しが許可されているアプリケーションの ID の単純なリストに基づいてアプリケーション レベルの承認を追加する、AllowedCallersClaimsValidator クラスが用意されています。 リストにアスタリスク (*) が含まれている場合、すべての呼び出し元が許可されます。 要求検証コントロールは 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

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

スキル マニフェスト

"スキル マニフェスト" は、スキルが実行できるアクティビティ、その入力パラメーターと出力パラメーター、およびスキルのエンドポイントが記述された 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. エコー スキル ボットをお使いのマシンでローカルに実行します。 手順が必要な場合は、README ファイルで C#JavaScriptJava、または Python のサンプルを参照してください。
  2. 次に示すように、エミュレーターを使用してボットをテストします。 スキルに "終了" または "停止" メッセージを送信すると、応答メッセージに加えて endOfConversation アクティビティが送信されます。 スキルが終了したことが示す endOfConversation アクティビティがスキルから送信されます。

Example transcript showing the end-of-conversation activity.

デバッグの詳細

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

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

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

次のステップ