您现在访问的是微软AZURE全球版技术文档网站,若需要访问由世纪互联运营的MICROSOFT AZURE中国区技术文档网站,请访问 https://docs.azure.cn.

机器人的工作原理How bots work

适用于: 是SDK v4 否SDK v3 APPLIES TO: yesSDK v4 no SDK v3

机器人是用户使用文本、图形(例如卡片或图像)或语音通过聊天的方式与之进行交互的应用。A bot is an app that users interact with in a conversational way, using text, graphics (such as cards or images), or speech. 用户与机器人之间的每次交互会生成一个活动。 Every interaction between the user and the bot generates an activity. Bot Framework Service 是 Azure 机器人服务的组件,可在连接机器人的用户应用(例如 Facebook、Skype、Slack 等,称为“通道”)与机器人之间发送信息。 The Bot Framework Service, which is a component of the Azure Bot Service, sends information between the user's bot-connected app (such as Facebook, Skype, Slack, etc. which we call the channel) and the bot. 每个通道可以在发送的活动中包含其他信息。Each channel may include additional information in the activities they send. 在创建机器人之前,必须了解机器人如何使用活动对象来与其用户通信。Before creating bots, it is important to understand how a bot uses activity objects to communicate with its users. 首先,让我们了解在运行简单的聊天机器人时交换的活动。Let's first take a look at activities that are exchanged when we run a simple echo bot.

活动示意图

此处演示了两种活动类型:聊天更新和消息。 Two activity types illustrated here are: conversation update and message.

当某一方参与聊天时,Bot Framework Service 可以发送聊天更新。The Bot Framework Service may send a conversation update when a party joins the conversation. 例如,使用 Bot Framework Emulator 开始聊天时,会看到两个聊天更新活动(一个活动指出用户正在参与聊天,另一个活动指出机器人正在参与聊天)。For example, on starting a conversation with the Bot Framework Emulator, you will see two conversation update activities (one for the user joining the conversation and one for the bot joining). 若要区分这些聊天更新活动,请检查“已添加成员”属性是否包含除机器人以外的成员。 To distinguish these conversation update activities, check whether the members added property includes a member other than the bot.

消息活动承载参与方之间的聊天信息。The message activity carries conversation information between the parties. 在聊天机器人示例中,消息活动承载简单的文本,通道呈现这些文本。In an echo bot example, the message activities are carrying simple text and the channel will render this text. 消息活动也可以承载要讲述的文本、建议的操作或要显示的卡片。Alternatively, the message activity might carry text to be spoken, suggested actions or cards to be displayed.

在此示例中,机器人创建并发送了一个消息活动,以响应它收到的入站消息活动。In this example, the bot created and sent a message activity in response to the inbound message activity it had received. 但是,机器人可通过其他方式响应收到的消息活动;机器人经常通过在消息活动中发送一些欢迎文本来响应聊天更新活动。However, a bot can respond in other ways to a received message activity; it’s not uncommon for a bot to respond to a conversation update activity by sending some welcome text in a message activity. 欢迎用户中可以找到更多信息。More information can be found in welcoming the user.

HTTP 详细信息HTTP Details

活动通过 HTTP POST 请求从 Bot Framework Service 抵达机器人。Activities arrive at the bot from the Bot Framework Service via an HTTP POST request. 机器人使用 200 HTTP 状态代码响应入站 POST 请求。The bot responds to the inbound POST request with a 200 HTTP status code. 从机器人发送到通道的活动通过单独的 HTTP POST 发送到 Bot Framework Service。Activities sent from the bot to the channel are sent on a separate HTTP POST to the Bot Framework Service. 而此请求也会通过 200 HTTP 状态代码得到确认。This, in turn, is acknowledged with a 200 HTTP status code.

协议不会指定这些 POST 请求及其确认的发送顺序。The protocol doesn’t specify the order in which these POST requests and their acknowledgments are made. 但是,为了适应常见的 HTTP 服务框架,这些请求通常会嵌套,这意味着,出站 HTTP 请求将在入站 HTTP 请求的范围内从机器人发出。However, to fit with common HTTP service frameworks, typically these requests are nested, meaning that the outbound HTTP request is made from the bot within the scope of the inbound HTTP request. 上图演示了此模式。This pattern is illustrated in the diagram above. 由于两个不同的 HTTP 连接相继出现,安全模型必须能够应对这种情况。Since there are two distinct HTTP connections back to back, the security model must provide for both.

定义轮次Defining a turn

在聊天时,人们通常一次说一句话,并且会轮流说话。In a conversation, people often speak one-at-a-time, taking turns speaking. 使用机器人时,通常是由机器人对用户输入进行响应。With a bot, it generally reacts to user input. 在 Bot Framework SDK 中,一轮通话既包含用户传给机器人的活动,又包含机器人发回用户的作为即时响应的活动。 Within the Bot Framework SDK, a turn consists of the user's incoming activity to the bot and any activity the bot sends back to the user as an immediate response. 可以将一个轮次视为给定活动抵达时的相关处理。You can think of a turn as the processing associated with the arrival of a given activity.

轮次上下文对象提供有关活动的信息,例如发送方和接收方、通道,以及处理该活动所需的其他数据。 The turn context object provides information about the activity such as the sender and receiver, the channel, and other data needed to process the activity. 使用该对象还能在机器人的不同层中处理轮次期间添加信息。It also allows for the addition of information during the turn across various layers of the bot.

轮次上下文是 SDK 中最重要的抽象之一。The turn context is one of the most important abstractions in the SDK. 轮次上下文不仅将入站活动传递到所有中间件组件将和应用程序逻辑,而且还提供所需的机制让中间件组件和应用程序逻辑发送出站活动。Not only does it carry the inbound activity to all the middleware components and the application logic but it also provides the mechanism whereby the middleware components and the application logic can send outbound activities.

活动处理堆栈The activity processing stack

让我们深入查看上图,并重点关注消息活动的抵达。Let's drill into the previous diagram with a focus on the arrival of a message activity.

活动处理堆栈

在上述示例中,机器人使用包含相同文本消息的另一个消息活动回复了原始消息活动。In the example above, the bot replied to the message activity with another message activity containing the same text message. 处理工作从 HTTP POST 请求(包含以 JSON 有效负载形式传递的活动信息)抵达 Web 服务器时开始。Processing starts with the HTTP POST request, with the activity information carried as a JSON payload, arriving at the web server. 在 C# 中,这通常是一个 ASP.NET 项目;在 JavaScript Node.js 项目中,这可能是 Express 或 Restify 等常用框架之一。In C# this will typically be an ASP.NET project, in a JavaScript Node.js project this is likely to be one of the popular frameworks such as Express or Restify.

适配器(SDK 的集成组件)是 SDK 运行时的核心。 The adapter, an integrated component of the SDK, is the core of the SDK runtime. 活动以 JSON 形式承载在 HTTP POST 正文中。The activity is carried as JSON in the HTTP POST body. 将反序列化此 JSON 以创建 Activity 对象,然后通过调用 process activity 方法将此对象传递给适配器。This JSON is deserialized to create the Activity object that is then handed to the adapter with a call to process activity method. 收到活动时,适配器会创建轮次上下文并调用中间件。 On receiving the activity, the adapter creates a turn context and calls the middleware.

如前所述,轮次上下文提供一个机制来让机器人发送出站活动(主要是为了响应入站活动)。As mentioned above, the turn context provides the mechanism for the bot to send outbound activities, most often in response to an inbound activity. 为此,轮次上下文提供 send、update 和 delete activity 响应方法。To achieve this, the turn context provides send, update, and delete activity response methods. 每个响应方法都在异步进程中运行。Each response method runs in an asynchronous process.

重要

线程处理主机器人轮次完成后处理上下文对象释放。The thread handling the primary bot turn deals with disposing of the context object when it is done. 确保 await 任何活动调用,以便主线程等待生成的活动,再完成处理并释放轮次上下文。Be sure to await any activity calls so the primary thread will wait on the generated activity before finishing it's processing and disposing of the turn context. 否则,如果某个响应(包括其处理程序)在已占用大量时间的情况下尝试对上下文对象执行操作,则会出现“上下文已释放”错误。 Otherwise, if a response (including its handlers) takes any significant amount of time and tries to act on the context object, it may get a context was disposed error.

活动处理程序Activity handlers

当机器人收到某个活动时,它会将该活动传递给其活动处理程序。 When the bot receives an activity, it passes it on to its activity handlers. 幕后还有一个称为“轮次处理程序”的基本处理程序。 Under the covers, there is one base handler called the turn handler. 所有活动都通过轮次处理程序路由。All activities get routed through there. 然后,对于收到的任何类型的活动,该轮次处理程序会调用各个活动处理程序。That turn handler then calls the individual activity handler for whatever type of activity it received.

例如,如果机器人收到了某个消息活动,轮次处理程序将会看到该传入的活动,并将其发送到 OnMessageActivityAsync 活动处理程序。For example, if the bot receives a message activity, the turn handler would see that incoming activity and send it to the OnMessageActivityAsync activity handler.

生成机器人时,用于处理和响应消息的机器人逻辑将进入此 OnMessageActivityAsync 处理程序。When building your bot, your bot logic for handling and responding to messages will go in this OnMessageActivityAsync handler. 同样,用于处理正在添加到聊天中的成员的逻辑将进入 OnMembersAddedAsync 处理程序,每次将成员添加到聊天时,都会调用该处理程序。Likewise, your logic for handling members being added to the conversation will go in your OnMembersAddedAsync handler, which is called whenever a member is added to the conversation.

若要实现这些处理程序的逻辑,需要根据下面的机器人逻辑部分所述,在机器人中重写这些方法。To implement your logic for these handlers, you will override these methods in your bot as seen in the Bot logic section below. 其中的每个处理程序没有基实现,因此,只需在重写中添加所需的逻辑。For each of these handlers, there is no base implementation, so just add the logic that you want in your override.

在某些情况下,需要在轮次结束时重写基轮次处理程序,例如保存状态There are certain situations where you will want to override the base turn handler, such as saving state at the end of a turn. 执行此操作时,请务必先调用 await base.OnTurnAsync(turnContext, cancellationToken);,以确保 OnTurnAsync 的基实现在其他代码之前运行。When doing so, be sure to first call await base.OnTurnAsync(turnContext, cancellationToken); to make sure the base implementation of OnTurnAsync is run before your additional code. 除此之外,该基实现还负责调用剩余的活动处理程序,例如 OnMessageActivityAsyncThat base implementation is, among other things, responsible for calling the rest of the activity handlers such as OnMessageActivityAsync.

中间件Middleware

中间件非常类似于其他任何消息传送中间件,由一组线性组件构成,其中每个组件按顺序执行,并有机会对活动运行。Middleware is much like any other messaging middleware, comprising a linear set of components that are each executed in order, giving each a chance to operate on the activity. 中间件管道的最后一个阶段是回调已由应用程序注册到适配器 process activity 方法的机器人类中的轮次处理程序。The final stage of the middleware pipeline is a callback to the turn handler on the bot class the application has registered with the adapter's process activity method. 该轮次处理程序通常是 C# 中的 OnTurnAsync 和 JavaScript 中的 onTurnThe turn handler is generally OnTurnAsync in C# and onTurn in JavaScript.

轮次处理程序采用轮次上下文作为参数。通常,在轮次处理程序函数内部运行的应用程序逻辑将处理入站活动的内容,在响应中生成一个或多个活动,并使用轮次上下文中的 send activity 函数发出这些活动。The turn handler takes a turn context as its argument, typically the application logic running inside the turn handler function will process the inbound activity’s content and generate one or more activities in response, sending these out using the send activity function on the turn context. 调用轮次上下文中的 send activity 会导致针对出站活动调用中间件组件。Calling send activity on the turn context will cause the middleware components to be invoked on the outbound activities. 中间件组件在机器人的轮次处理程序函数之前和之后执行。Middleware components execute before and after the bot’s turn handler function. 执行在本质上是嵌套的,因此,有时称作“俄罗斯套娃”或类似叫法。The execution is inherently nested and, as such, sometimes referred to being like a Russian Doll. 有关中间件的更深入信息,请参阅中间件主题For more in depth information about middleware, see the middleware topic.

机器人结构Bot structure

以下部分介绍 EchoBot 的关键片段,可以使用针对 CSharpJavaScript 提供的模板轻松创建这些片段。 In the following sections, we examine key pieces of an EchoBot that you can easily create using the templates provided for CSharp or JavaScript.

机器人是一个 Web 应用程序,我们提供了适用于每种语言的模板。A bot is a web application, and we provide templates for each language.

VSIX 模板生成 ASP.NET MVC Core Web 应用。The VSIX template generates a ASP.NET MVC Core web app. ASP.NET 基础知识中,可以看到 Program.csStartup.cs 等文件包含类似的代码。If you look at the ASP.NET fundamentals, you'll see similar code in files such as Program.cs and Startup.cs. 这些文件并非特定于机器人,所有 Web 应用都需要它们。These files are required for all web apps and are not bot specific.

appsettings.json 文件appsettings.json file

appsettings.json 文件指定机器人的配置信息,例如应用 ID 、密码等。The appsettings.json file specifies the configuration information for your bot, such as the app ID, and password among other things. 如果使用特定的技术或者在生产环境中使用此机器人,则需要将特定的密钥或 URL 添加到此配置。If using certain technologies or using this bot in production, you will need to add your specific keys or URL to this configuration. 但是,对于此聊天机器人,目前不需要执行任何操作;暂时可将应用 ID 和密码保持未定义状态。For this Echo bot, however, you don't need to do anything here right now; the app ID and password may be left undefined at this time.

机器人逻辑Bot logic

机器人逻辑处理来自一个或多个通道的传入活动,并在响应中生成传出活动。The bot logic processes incoming activities from one or more channels and generates outgoing activities in response.

主要机器人逻辑在机器人代码(此处名为 Bots/EchoBot.cs)中定义。The main bot logic is defined in the bot code, here called Bots/EchoBot.cs. EchoBot 派生自 ActivityHandler,后者又派生自 IBot 接口。EchoBot derives from ActivityHandler, which in turn derives from the IBot interface. ActivityHandler 为不同类型的活动定义各种处理程序,例如,此处定义了两个处理程序:OnMessageActivityAsyncOnMembersAddedAsyncActivityHandler defines various handlers for different types of activities, such as the two defined here: OnMessageActivityAsync, and OnMembersAddedAsync. 这些方法受保护,但可将其覆盖,因为它们派生自 ActivityHandlerThese methods are protected, but can be overwritten since we're deriving from ActivityHandler.

ActivityHandler 中定义的处理程序为:The handlers defined in ActivityHandler are:

事件Event HandlerHandler 说明Description
已收到任一活动类型Any activity type received OnTurnAsync 根据收到的活动类型调用其他处理程序之一。Calls one of the other handlers, based on the type of activity received.
已收到消息活动Message activity received OnMessageActivityAsync 重写此方法可以处理 message 活动。Override this to handle a message activity.
已收到聊天更新活动Conversation update activity received OnConversationUpdateActivityAsync 收到 conversationUpdate 活动时,如果除机器人以外的成员加入或退出聊天,则调用某个处理程序。On a conversationUpdate activity, calls a handler if members other than the bot joined or left the conversation.
非机器人成员加入了聊天Non-bot members joined the conversation OnMembersAddedAsync 重写此方法可以处理加入聊天的成员。Override this to handle members joining a conversation.
非机器人成员退出了聊天Non-bot members left the conversation OnMembersRemovedAsync 重写此方法可以处理退出聊天的成员。Override this to handle members leaving a conversation.
已收到事件活动Event activity received OnEventActivityAsync 收到 event 活动时,调用特定于事件类型的处理程序。On an event activity, calls a handler specific to the event type.
已收到令牌响应事件活动Token-response event activity received OnTokenResponseEventAsync 重写此方法可以处理令牌响应事件。Override this to handle token response events.
已收到非令牌响应事件活动Non-token-response event activity received OnEventAsync 重写此方法可以处理其他类型的事件。Override this to handle other types of events.
已收到消息回应活动Message reaction activity received OnMessageReactionActivityAsync 收到 messageReaction 活动时,如果已在消息中添加或删除了一个或多个回应,则调用处理程序。On a messageReaction activity, calls a handler if one or more reactions were added or removed from a message.
消息回应已添加到消息Message reactions added to a message OnReactionsAddedAsync 重写此方法可处理添加到消息的回应。Override this to handle reactions added to a message.
从消息中删除了消息回应Message reactions removed from a message OnReactionsRemovedAsync 重写此方法可处理从消息中删除的回应。Override this to handle reactions removed from a message.
已收到其他活动类型Other activity type received OnUnrecognizedActivityTypeAsync 重写此方法可以处理未经处理的任何活动类型。Override this to handle any activity type otherwise unhandled.

这些不同的处理程序具有一个 turnContext,用于提供有关对应于入站 HTTP 请求的传入活动的信息。These different handlers have a turnContext that provides information about the incoming activity, which corresponds to the inbound HTTP request. 活动可以是各种类型,因此,每个处理程序在其轮次上下文参数中提供一个强类型化的活动,在大多数情况下,始终会处理 OnMessageActivityAsyncActivities can be of various types, so each handler provides a strongly-typed activity in its turn context parameter; in most cases, OnMessageActivityAsync will always be handled, and is generally the most common.

与在此框架的 4.x 旧版中一样,还有一个选项可以实现公共方法 OnTurnAsyncAs in previous 4.x versions of this framework, there is also the option to implement the public method OnTurnAsync. 目前,此方法的基实现会处理错误检查,然后根据传入活动的类型调用每个特定的处理程序(例如本示例中定义的两个处理程序)。Currently, the base implementation of this method handles error checking and then calls each of the specific handlers (like the two we define in this sample) depending on the type of incoming activity. 在大多数情况下,可以不用理会该方法并使用单个处理程序,但如果你的情况要求使用 OnTurnAsync 的自定义实现,则仍可以考虑该方法。In most cases, you can leave that method alone and use the individual handlers, but if your situation requires a custom implementation of OnTurnAsync, it is still an option.

重要

如果重写 OnTurnAsync 方法,则需要调用 base.OnTurnAsync 以获取用于调用其他所有 On<activity>Async 处理程序的基实现,或自行调用这些处理程序。If you do override the OnTurnAsync method, you'll need to call base.OnTurnAsync to get the base implementation to call all the other On<activity>Async handlers or call those handlers yourself. 否则不会调用这些处理程序,并且不会运行该代码。Otherwise, those handlers won't be called and that code won't be run.

在本示例中,我们将欢迎新用户,或者回显用户使用 SendActivityAsync 调用发送的消息。In this sample, we welcome a new user or echo back the message the user sent using the SendActivityAsync call. 出站活动对应于出站 HTTP POST 请求。The outbound activity corresponds to the outbound HTTP POST request.

public class MyBot : ActivityHandler
{
    protected override async Task OnMessageActivityAsync(ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken)
    {
        await turnContext.SendActivityAsync(MessageFactory.Text($"Echo: {turnContext.Activity.Text}"), cancellationToken);
    }

    protected override async Task OnMembersAddedAsync(IList<ChannelAccount> membersAdded, ITurnContext<IConversationUpdateActivity> turnContext, CancellationToken cancellationToken)
    {
        foreach (var member in membersAdded)
        {
            await turnContext.SendActivityAsync(MessageFactory.Text($"welcome {member.Name}"), cancellationToken);
        }
    }
}

从应用访问机器人Access the bot from your app

设置服务Set up services

Startup.cs 文件中的 ConfigureServices 方法加载连接的服务、从 appsettings.json 或 Azure Key Vault 加载这些服务的密钥(如果有)、连接状态,等等。The ConfigureServices method in the Startup.cs file loads the connected services, as well as their keys from appsettings.json or Azure Key Vault (if there are any), connects state, and so on. 此处,我们将在服务中添加 MVC 并设置兼容版本,然后设置可以通过依赖项注入在机器人控制器中使用的适配器和机器人。Here, we're adding MVC and setting the compatibility version on our services, then setting up the adapter and bot to be available through dependency injection to the bot controller.

// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);

    // Create the credential provider to be used with the Bot Framework Adapter.
    services.AddSingleton<ICredentialProvider, ConfigurationCredentialProvider>();

    // Create the Bot Framework Adapter.
    services.AddSingleton<IBotFrameworkHttpAdapter, BotFrameworkHttpAdapter>();

    // Create the bot as a transient. In this case the ASP Controller is expecting an IBot.
    services.AddTransient<IBot, EchoBot>();
}

Configure 方法通过指定应用需使用 MVC 和其他几个文件来完成应用的配置。The Configure method finishes the configuration of your app by specifying that the app use MVC and a few other files. 使用 Bot Framework 的所有机器人需要该配置调用,但是,在生成机器人时,该调用已在示例或 VSIX 模板中定义。All bots using the Bot Framework will need that configuration call, however that will already be defined in samples or the VSIX template when you build your bot. 应用启动时,运行时会调用 ConfigureServicesConfigureConfigureServices and Configure are called by the runtime when the app starts.

机器人控制器Bot Controller

采用标准 MVC 结构的控制器可让你确定消息和 HTTP POST 请求的路由方式。The controller, following the standard MVC structure, lets you determine the routing of messages and HTTP POST requests. 对于我们的机器人,我们会将传入的请求传递到前面活动处理堆栈部分所述的适配器 process async activity 方法。For our bot, we pass the incoming request on to the adapter's process async activity method as explained in the activity processing stack section above. 在该调用中,指定可能需要的机器人和其他任何授权信息。In that call, we specify the bot and any other authorization information that may be required.

控制器实现 ControllerBase,保存 Startup.cs 中设置的适配器和机器人(此处通过依赖项注入来使用该适配器和机器人),并在收到传入的 HTTP POST 时,将所需的信息传递给机器人。The controller implements ControllerBase, holds the adapter and bot that we set in Startup.cs (that are available here through dependency injection), and passes the necessary information on to the bot when it receives an incoming HTTP POST.

在此处可以看到路由和控制器属性继续处理的类。Here, you'll see the class proceeded by route and controller attributes. 这些属性可以帮助框架适当路由消息,以及了解要使用哪个控制器。These assist the framework to route the messages appropriately and know which controller to use. 如果更改路由属性中的值,会更改仿真器或其他通道用来访问机器人的终结点。If you change the value in the route attribute, that changes the endpoint the emulator or other channels use access your bot.

// This ASP Controller is created to handle a request. Dependency Injection will provide the Adapter and IBot
// implementation at runtime. Multiple different IBot implementations running at different endpoints can be
// achieved by specifying a more specific type for the bot constructor argument.
[Route("api/messages")]
[ApiController]
public class BotController : ControllerBase
{
    private readonly IBotFrameworkHttpAdapter Adapter;
    private readonly IBot Bot;

    public BotController(IBotFrameworkHttpAdapter adapter, IBot bot)
    {
        Adapter = adapter;
        Bot = bot;
    }

    [HttpPost]
    public async Task PostAsync()
    {
        // Delegate the processing of the HTTP POST to the adapter.
        // The adapter will invoke the bot.
        await Adapter.ProcessAsync(Request, Response, Bot);
    }
}

管理机器人资源Manage bot resources

需要适当管理机器人资源,例如连接的服务的应用 ID、密码、密钥或机密。The bot resources, such as app ID, passwords, keys or secrets for connected services, will need to be managed appropriately. 有关如何执行此操作的详细信息,请参阅管理机器人资源For more on how to do so, see Manage bot resources.

其他资源Additional resources