Hello, Bot!
分类: Azure机器人 ◆ 标签: #Azure Bot Framework SDK #Azure Bot Service #机器人 ◆ 发布于: 2023-08-07 22:40:36
我们之前的文章简单的介绍了一下由微软提供的Azure Bot Framework SDK以及Azure Bot Service服务,比较不妙的是看起来这个服务真没啥人用,不能排除微软之后就不支持这个服务。但是Azure Bot Framework SDK是开源的,而且还有一个开源的社区,因此即便微软不支持Azure Bot Service了,Azure Bot Framework SDK还会以开源的形式继续存在,如果要做聊天机器人,还是可以继续使用这个框架,也可以考虑参与开源的开发。我为了自己的项目也尝试参与了两个开源的项目,一个是Bot基于SQL Server的IStore实现,另外一个是WeChat的Adapter, 这两个项目之前都是有的,但是太老,几年没有更新了,不能支持.Net6,因此我做的工作就是修改了一下他们,让他们支持.Net6, IStore我已经提交了PR,但是还没有被接受,不过可用了,WeChat的Adapter我还在调试,完成后也提交一下PR, 我PR估计比较严格,如果大家想用用看,也可以访问:
- https://github.com/hylinux/botbuilder-community-dotnet
- https://github.com/hylinux/BotFramework-WeChat
这个两个都是我的fork。
我们之前简单的利用Visual Studio的模板来快速的创建了一个echo Bot, 我们这一章不使用Visual Studio的模板,而是一步一步的使用.Net Cli工具来创建一个Echo Bot, 这样掰开揉碎了,入门更容易。
Hello Bot
创建这个Bot之前还是要做一些准备:
- 了解
.Net以及.Net Cli工具的基本使用。 - 了解
Asp.net Core的框架。 - 要安装测试工具
Bot Framework Emulator, 可以从这里下载回来:https://github.com/Microsoft/BotFramework-Emulator - 安装
Visual Studio Code的编辑器。
一个Bot的应用,实际就是一个Web API的应用,因此我们首先利用.Net Cli创建一个Web Api的项目:
mkdir EchoBot
cd EchoBot
dotnet new webapi
创建好了我们的基本项目之后,我们需要添加必要的引用包:
dotnet add package Microsoft.Bot.Builder.Integration.AspNet.Core
dotnet add package Microsoft.AspNetCore.Mvc.NewtonsoftJson
同时可以创建两个目录:
mkdir Bot
mkdir Adapter
Bot目录用于放置Bot定义的类, Adapter用于放置我们的Adapter类,用于和Channel通讯。
关于Bot, Adapter, Channel, 您可以参考我之前的文章:https://www.azuredeveloper.cn/article/what-is-azure-bot-service
使用Visual Studio Code 打开当前的目录:
code .
创建Adapter类
在目录Adapter里添加一个文件AdapterWithErrorHandler, 该类即是Adapter类,如下是该类的定义:
using Microsoft.Bot.Builder.Integration.AspNet.Core; using Microsoft.Bot.Builder.TraceExtensions; using Microsoft.Bot.Connector.Authentication; using Microsoft.Extensions.Logging; namespace EchoBot.Adapter; public class AdapterWithErrorHandler : CloudAdapter { public AdapterWithErrorHandler(BotFrameworkAuthentication auth, ILogger<IBotFrameworkHttpAdapter> logger) : base(auth, logger) { OnTurnError = async (turnContext, exception) => { //向日志中记录来自Bot中的异常 logger.LogError(exception, $"[OnTurnError] unhandled error : {exception.Message}"); // 发送消息通知用户有异常发生 await turnContext.SendActivityAsync("The bot encountered an error or bug."); await turnContext.SendActivityAsync("To continue to run this bot, please fix the bot source code."); //启用Activity trace, 如果使用模拟器调试可以看到详细的trace await turnContext.TraceActivityAsync("OnTurnError Trace", exception.Message, "https://www.botframework.com/schemas/error", "TurnError"); }; } }
Adapter类用于接收Channel的消息,并将消息封装成Activity, 同时生成TurnContext, 并且在Adaper里使用middleware机制,形成pipeline, 可以对进入的消息进行处理,同时由于Adapter生成回复的消息,也会使用middleware来依次处理即将回复给Channel的消息。
创建Bot
聊天机器人是一个单独的类,这个类用于接收由Adapter包装并处理好的Activity, 接收到Activity之后,Bot会根据Activity的类型不同,调用不同的处理程序,例如Activity是ConversationUpdateActivity类型的时候,会调用OnConversationUpdateAsync的方法处理该Acvitity, 如果是MessageActivity的话,那么会调用OnMessageActivity来处理该Activity, 如果不想区分是什么类型,Bot还有一个保底的方法,那就是OnTurnAsync 方法,实际上所有的Bot都会先调用这个方法,然后在这个方法里根据Activity的类型,再调用相应的方法,因此如果您在自己的类中覆盖OnTurnAsync方法,一定要记得在方法里调用base()。
我们这个Bot只需要实现很简单的功能,即用户输入了什么,echo back什么就好了, 同时在用户加入聊天的时候,输出欢迎词。
using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using Microsoft.Bot.Builder; using Microsoft.Bot.Schema; namespace EchoBot.Bot; public class EchoBot : ActivityHandler { protected override async Task OnMessageActivityAsync(ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken) { var replyText = $"Echo: {turnContext.Activity.Text}"; await turnContext.SendActivityAsync(MessageFactory.Text(replyText, replyText), cancellationToken); } protected override async Task OnMembersAddedAsync(IList<ChannelAccount> membersAdded, ITurnContext<IConversationUpdateActivity> turnContext, CancellationToken cancellationToken) { var welcomeText = "Hello and welcome!"; foreach (var member in membersAdded) { if (member.Id != turnContext.Activity.Recipient.Id) { await turnContext.SendActivityAsync(MessageFactory.Text(welcomeText, welcomeText), cancellationToken); } } } }
注意观察我们在Bot类里处理了两类类型的Activity,一个是MessageActivity, 另外一个是MemebersAddActivity, 所以定义了如上两个方法。
添加Service
打开Program.cs, 添加如下的服务:
using Microsoft.Bot.Builder.Integration.AspNet.Core; using Microsoft.Bot.Builder; using Microsoft.Bot.Connector.Authentication; using EchoBot.Adapter; var builder = WebApplication.CreateBuilder(args); builder.Services.AddControllers().AddNewtonsoftJson( options => { options.SerializerSettings.MaxDepth = HttpHelper.BotMessageSerializerSettings.MaxDepth; }); builder.Services.AddHttpClient(); builder.Services.AddSingleton<BotFrameworkAuthentication, ConfigurationBotFrameworkAuthentication>(); builder.Services.AddSingleton<IBotFrameworkHttpAdapter, AdapterWithErrorHandler>(); builder.Services.AddTransient<IBot, EchoBot.Bots.EchoBot>(); // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); var app = builder.Build(); if (app.Environment.IsDevelopment()) { app.UseSwagger(); app.UseSwaggerUI(); } app.UseHttpsRedirection(); app.UseAuthorization(); app.MapControllers(); app.Run();
尤其需要注意的是, 如果需要使用Bot,必须有这些配置:
uilder.Services.AddControllers().AddNewtonsoftJson( options => { options.SerializerSettings.MaxDepth = HttpHelper.BotMessageSerializerSettings.MaxDepth; }); builder.Services.AddHttpClient(); builder.Services.AddSingleton<BotFrameworkAuthentication, ConfigurationBotFrameworkAuthentication>(); builder.Services.AddSingleton<IBotFrameworkHttpAdapter, AdapterWithErrorHandler>(); builder.Services.AddTransient<IBot, EchoBot.Bots.EchoBot>();
配置Bot的调用
Bot就是一个WebAPI的应用,我们现在需要做的是给Bot配置一个调用的API: 在Controlls目录下添加一个文件BotController.cs:
using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; using Microsoft.Bot.Builder; using Microsoft.Bot.Builder.Integration.AspNet.Core; namespace EchoBot.Controllers; [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, HttpGet] public async Task PostAsync() { await _adapter.ProcessAsync(Request, Response, _bot); } }
我们透过方法PostAsync可以看到将Adapter和Bot类联合起来的方法:
await _adapter.ProcessAsync(Request, Response, _bot);
至此我们完成了一个Bot的开发了,可以尝试dotnet run看看结果了。
注意
启动Bot Framework Emulator之后,使用https的endpoint
需要注意上述红框的
https地址启动
Bot Framework Emulator:
设置
Bot的地址。如下是运行的结果:
注意1和2的运行结果以及可以用于调试的界面:


