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的运行结果以及可以用于调试的界面: