了解 Teams AI 库

Teams AI 库支持 JavaScript,并简化了构建可与 Microsoft Teams 交互的机器人。 它还有助于迁移现有机器人以使用 AI 支持的功能。 库支持将消息传送、消息扩展和自适应卡片功能迁移到新格式。 还可以使用这些功能升级现有 Teams 应用。

之前,你使用 BotBuilder SDK 创建适用于 Teams 的机器人。 Teams AI 库旨在简化此过程,并包含内置的 AI 支持。 最初,可以在没有 AI 的情况下升级机器人,但在升级后,它可以连接到 AI 库中提供的 AI 或大型语言模型 (LLM) 。

使用 Teams AI 库,可以专注于:

活动处理程序

Teams AI 库支持以下活动处理程序:

需要使用 AI 库在源文件中搭建机器人和自适应卡片处理程序的基架。 在后续部分中,我们使用 AI 库中 的示例来解释每个功能和迁移路径。

发送或接收消息

此功能演示如何使用 Bot Framework 发送和接收消息。 该示例侦听用户消息,删除收到时的对话状态,跟踪对话中的消息数,并使用计数回显用户的消息。

.NET 代码示例

 // 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中设置消息扩展。 此示例演示应用如何侦听搜索作和项点击。 它将搜索结果的格式设置为显示包信息的“主卡”,并在消息传递扩展中显示它们。

.NET 代码示例

// 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 属性注册自适应卡片作处理程序。 此示例侦听具有 staticdynamic 关键字的消息,并使用 或 DynamicMessageHandler()返回自适应卡片StaticMessageHandler()。 该应用还侦听来自动态搜索卡的查询和提交按钮作。

.NET 代码示例

// 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 库如何通过映射作(例如 LightsOnLightsOff )来提示函数来帮助管理机器人逻辑,尤其是在使用 OpenAI 进行响应时。

.NET 代码示例

/ 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 一起使用。

以下示例演示如何构造代码以处理命令的消息扩展查询 searchCmd

.NET 代码示例

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

作意向

本部分介绍 Teams 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 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:使用 和 removeItemaddItem作的处理程序简化了机器人逻辑。 作和提示之间的这种区别指导 AI 执行正确的作。

.NET 代码示例

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

后续步骤