Teams AI ライブラリの機能

Teams AI ライブラリは JavaScript をサポートしており、Microsoft Teamsと対話できるボットを構築するプロセスを簡略化し、既存のボットの移行を容易にするように設計されています。 AI ライブラリでは、メッセージング機能、メッセージ拡張機能 (ME) 機能、アダプティブ カード機能を新しい形式に移行できます。 これらの機能を使用して既存の Teams アプリをアップグレードすることもできます。

以前は、BotBuilder SDK を直接使用して、Microsoft Teams用のボットを作成していました。 Teams AI ライブラリは、Microsoft Teamsと対話できるボットの構築を容易にするように設計されています。 Teams AI ライブラリの主な機能の 1 つは、お客様が利用できる AI サポートですが、最初の目的は、AI なしで現在のボットをアップグレードすることです。 アップグレード後、ボットは AI ライブラリで使用できる AI または大きな言語モデル (LLM) に接続できます。

Teams AI ライブラリでは、次の機能がサポートされています。

AI ライブラリを使用して、ボットとアダプティブ カード ハンドラーをソース ファイルにスキャフォールディングする必要があります。

次のセクションでは、 AI ライブラリ のサンプルを使用して、各機能と移行へのパスについて説明しました。

メッセージの送受信

Bot Framework を使用してメッセージを送受信できます。 アプリは、ユーザーがメッセージを送信することをリッスンし、このメッセージを受信すると、会話状態を削除し、メッセージをユーザーに送信します。 また、アプリは会話で受信したメッセージの数を追跡し、これまでに受信したメッセージの数でユーザーのメッセージをエコーバックします。

 // Listen for user to say "/reset" and then delete conversation state
    app.OnMessage("/reset", ActivityHandlers.ResetMessageHandler);

    // Listen for ANY message to be received. MUST BE AFTER ANY OTHER MESSAGE HANDLERS
    app.OnActivity(ActivityTypes.Message, ActivityHandlers.MessageHandler);

    return app;

メッセージ拡張機能

Bot Framework SDK の TeamsActivityHandlerでは、ハンドラー メソッドを拡張してメッセージ拡張クエリ ハンドラーを設定する必要があります。 アプリは、検索アクションとアイテムタップをリッスンし、検索結果をパッケージ情報を表示する HeroCards の一覧として書式設定します。 結果は、メッセージング拡張機能に検索結果を表示するために使用されます。

// Listen for search actions
    app.MessageExtensions.OnQuery("searchCmd", activityHandlers.QueryHandler);
    // Listen for item tap
    app.MessageExtensions.OnSelectItem(activityHandlers.SelectItemHandler);

    return app;

 // Format search results in ActivityHandlers.cs

            List<MessagingExtensionAttachment> attachments = packages.Select(package => new MessagingExtensionAttachment
            {
                ContentType = HeroCard.ContentType,
                Content = new HeroCard
                {
                    Title = package.Id,
                    Text = package.Description
                },
                Preview = new HeroCard
                {
                    Title = package.Id,
                    Text = package.Description,
                    Tap = new CardAction
                    {
                        Type = "invoke",
                        Value = package
                    }
                }.ToAttachment()
            }).ToList();

            // Return results as a list

            return new MessagingExtensionResult
            {
                Type = "result",
                AttachmentLayout = "list",
                Attachments = attachments
            };

アダプティブ カードの機能

アダプティブ カード アクション ハンドラーは、 app.adaptiveCards プロパティを使用して登録できます。 アプリは、 static または dynamic キーワードを含むメッセージをリッスンし、 StaticMessageHandler または DynamicMessageHandler メソッドを使用してアダプティブ カードを返します。 また、アプリは動的検索カードからのクエリをリッスンし、アダプティブ カードのボタンを送信します。

// Listen for messages that trigger returning an adaptive card
    app.OnMessage(new Regex(@"static", RegexOptions.IgnoreCase), activityHandlers.StaticMessageHandler);
    app.OnMessage(new Regex(@"dynamic", RegexOptions.IgnoreCase), activityHandlers.DynamicMessageHandler);

    // Listen for query from dynamic search card
    app.AdaptiveCards.OnSearch("nugetpackages", activityHandlers.SearchHandler);
    // Listen for submit buttons
    app.AdaptiveCards.OnActionSubmit("StaticSubmit", activityHandlers.StaticSubmitHandler);
    app.AdaptiveCards.OnActionSubmit("DynamicSubmit", activityHandlers.DynamicSubmitHandler);

    // Listen for ANY message to be received. MUST BE AFTER ANY OTHER HANDLERS
    app.OnActivity(ActivityTypes.Message, activityHandlers.MessageHandler);

    return app;

コア機能

アクションを処理するためのボット ロジック

ボットは、ライトをオンにするためのアクション LightsOn でユーザーの入力に応答します。

次の例は、Teams AI ライブラリを使用して、アクション LightsOn または LightsOff を処理するためのボット ロジックを管理し、OpenAI で使用されるプロンプトに接続する方法を示しています。

/ Create AI Model
if (!string.IsNullOrEmpty(config.OpenAI?.ApiKey))
{
    builder.Services.AddSingleton<OpenAIModel>(sp => new(
        new OpenAIModelOptions(config.OpenAI.ApiKey, "gpt-3.5-turbo")
        {
            LogRequests = true
        },
        sp.GetService<ILoggerFactory>()
    ));
}
else if (!string.IsNullOrEmpty(config.Azure?.OpenAIApiKey) && !string.IsNullOrEmpty(config.Azure.OpenAIEndpoint))
{
    builder.Services.AddSingleton<OpenAIModel>(sp => new(
        new AzureOpenAIModelOptions(
            config.Azure.OpenAIApiKey,
            "gpt-35-turbo",
            config.Azure.OpenAIEndpoint
        )
        {
            LogRequests = true
        },
        sp.GetService<ILoggerFactory>()
    ));
}
else
{
    throw new Exception("please configure settings for either OpenAI or Azure");
}

// Create the bot as transient. In this case the ASP Controller is expecting an IBot.
builder.Services.AddTransient<IBot>(sp =>
{
    // Create loggers
    ILoggerFactory loggerFactory = sp.GetService<ILoggerFactory>()!;

    // Create Prompt Manager
    PromptManager prompts = new(new()
    {
        PromptFolder = "./Prompts"
    });

    // Adds function to be referenced in the prompt template
    prompts.AddFunction("getLightStatus", async (context, memory, functions, tokenizer, args) =>
    {
        bool lightsOn = (bool)(memory.GetValue("conversation.lightsOn") ?? false);
        return await Task.FromResult(lightsOn ? "on" : "off");
    });

    // Create ActionPlanner
    ActionPlanner<AppState> planner = new(
        options: new(
            model: sp.GetService<OpenAIModel>()!,
            prompts: prompts,
            defaultPrompt: async (context, state, planner) =>
            {
                PromptTemplate template = prompts.GetPrompt("sequence");
                return await Task.FromResult(template);
            }
        )
        { LogRepairs = true },
        loggerFactory: loggerFactory
    );

    return new TeamsLightBot(new()
    {
        Storage = sp.GetService<IStorage>(),
        AI = new(planner),
        LoggerFactory = loggerFactory,
        TurnStateFactory = () =>
        {
            return new AppState();
        }
    });
});

// LightBotActions defined in LightBotActions.cs
    
[Action("LightsOn")]
        public async Task<string> LightsOn([ActionTurnContext] ITurnContext turnContext, [ActionTurnState] AppState turnState)
        {
            turnState.Conversation.LightsOn = true;
            await turnContext.SendActivityAsync(MessageFactory.Text("[lights on]"));
            return "the lights are now on";
        }

        [Action("LightsOff")]
        public async Task<string> LightsOff([ActionTurnContext] ITurnContext turnContext, [ActionTurnState] AppState turnState)
        {
            turnState.Conversation.LightsOn = false;
            await turnContext.SendActivityAsync(MessageFactory.Text("[lights off]"));
            return "the lights are now off";
        }

        [Action("Pause")]
        public async Task<string> LightsOff([ActionTurnContext] ITurnContext turnContext, [ActionParameters] Dictionary<string, object> args)
        {
            // Try to parse entities returned by the model.
            // Expecting "time" to be a number of milliseconds to pause.
            if (args.TryGetValue("time", out object? time))
            {
                if (time != null && time is string timeString)
                {
                    if (int.TryParse(timeString, out int timeInt))
                    {
                        await turnContext.SendActivityAsync(MessageFactory.Text($"[pausing for {timeInt / 1000} seconds]"));
                        await Task.Delay(timeInt);
                    }
                }
            }

            return "done pausing";
        }

メッセージ拡張クエリ

Teams AI ライブラリでは、Teams Bot Framework SDK の以前のイテレーションと比較して、さまざまなメッセージ拡張クエリ コマンドのハンドラーを作成するためのより直感的なアプローチが提供されます。 新しい SDK は、既存の Teams Bot Framework SDK と共に機能します。

次に、 searchCmd コマンドのメッセージ拡張クエリを処理するようにコードを構造化する方法の例を示します。

// Listen for search actions
    app.MessageExtensions.OnQuery("searchCmd", activityHandlers.QueryHandler);
    // Listen for item tap
    app.MessageExtensions.OnSelectItem(activityHandlers.SelectItemHandler);

    return app;

 // Format search results
            List<MessagingExtensionAttachment> attachments = packages.Select(package => new MessagingExtensionAttachment
            {
                ContentType = HeroCard.ContentType,
                Content = new HeroCard
                {
                    Title = package.Id,
                    Text = package.Description
                },
                Preview = new HeroCard
                {
                    Title = package.Id,
                    Text = package.Description,
                    Tap = new CardAction
                    {
                        Type = "invoke",
                        Value = package
                    }
                }.ToAttachment()
            }).ToList();

            return new MessagingExtensionResult
            {
                Type = "result",
                AttachmentLayout = "list",
                Attachments = attachments
            };

アクションの意図

アクションと予測のためのシンプルなインターフェイスを使用すると、ボットがアクションを実行するための高い信頼度を持つときに反応できます。 アンビエント プレゼンスを使用すると、ボットは意図を学習し、ビジネス ロジックに基づいてプロンプトを使用し、応答を生成できます。

AI ライブラリのおかげで、プロンプトはボットでサポートされているアクションの概要を説明し、それらのアクションを使用する方法のいくつかのショットの例を提供するだけで済みます。 会話の履歴は、ユーザーとボットの間の自然な対話に役立ちます。たとえば、 食料雑貨リストにシリアルを追加し、 続いてコーヒーを追加します。これは、コーヒーが食料品リストに追加されることを示す必要があります。

AI アシスタントとの会話を次に示します。 AI アシスタントはリストを管理でき、次のコマンドを認識します。

  • する <action> <optional entities>
  • 言う <response>

以下のアクションがサポートされています。

  • addItem list="<list name>" item="<text>"
  • removeItem list="<list name>" item="<text>"
  • summarizeLists

すべてのエンティティは、アクションに必要なパラメーターです。

  • 現在のリスト名:

    {{conversation.listNames}} 
    
    
    Examples:  
    
    Human: remind me to buy milk
    AI: DO addItem list="groceries" item="milk" THEN SAY Ok I added milk to your groceries list
    Human: we already have milk
    AI: DO removeItem list="groceries" item="milk" THEN SAY Ok I removed milk from your groceries list
    Human: buy ingredients to make margaritas
    AI: DO addItem list="groceries" item="tequila" THEN DO addItem list="groceries" item="orange liqueur" THEN DO addItem list="groceries" item="lime juice" THEN SAY Ok I added tequila, orange liqueur, and lime juice to your groceries list
    Human: do we have have milk
    AI: DO findItem list="groceries" item="milk"
    Human: what's in my grocery list
    AI: DO summarizeLists  
    Human: what's the contents of all my lists?
    AI: DO summarizeLists
    Human: show me all lists but change the title to Beach Party
    AI: DO summarizeLists
    Human: show me all lists as a card and sort the lists alphabetically
    AI: DO summarizeLists
    
    
  • 会話の履歴:

    {{conversation.(history}} 
    
  • 現在のクエリ:

    Human: {{activity.text}} 
    
  • 現在のリスト名:

    {{conversation.listNames}}
    
  • AI: ボット ロジックは、 addItemremoveItemなどのアクションのハンドラーを含むように合理化されています。 アクションとプロンプトの間のこの個別の分離は、アクションとプロンプトの実行方法に関する AI を導く強力なツールとして機能します。

        [Action("AddItem")]
        public string AddItem([ActionTurnState] ListState turnState, [ActionParameters] Dictionary<string, object> parameters)
        {
            ArgumentNullException.ThrowIfNull(turnState);
            ArgumentNullException.ThrowIfNull(parameters);

            string listName = GetParameterString(parameters, "list");
            string item = GetParameterString(parameters, "item");

            IList<string> items = GetItems(turnState, listName);
            items.Add(item);
            SetItems(turnState, listName, items);

            return "item added. think about your next action";
        }

        [Action("RemoveItem")]
        public async Task<string> RemoveItem([ActionTurnContext] ITurnContext turnContext, [ActionTurnState] ListState turnState, [ActionParameters] Dictionary<string, object> parameters)
        {
            ArgumentNullException.ThrowIfNull(turnContext);
            ArgumentNullException.ThrowIfNull(turnState);
            ArgumentNullException.ThrowIfNull(parameters);

            string listName = GetParameterString(parameters, "list");
            string item = GetParameterString(parameters, "item");

            IList<string> items = GetItems(turnState, listName);

            if (!items.Contains(item))
            {
                await turnContext.SendActivityAsync(ResponseBuilder.ItemNotFound(listName, item)).ConfigureAwait(false);
                return "item not found. think about your next action";
            }

            items.Remove(item);
            SetItems(turnState, listName, items);
            return "item removed. think about your next action";
        }

次の手順