Alan Tsai 的學習筆記


學而不思則罔,思而不學則殆,不思不學則“網貸” 記錄軟體開發的點點滴滴 著重於微軟技術、網頁開發、DevOps、C#, Asp .net Mvc、Azure、AI、Chatbot、Docker、Data Science

[chatbot + AI = 下一代操作模式][04]瞭解BotBuilder的組成

[chatbot + AI = 下一代操作模式][04]瞭解BotBuilder的架構.jpg
圖片來源:https://pixabay.com/en/books-spine-colors-pastel-1099067/ 

在上一篇([03]建立第一個chatbot - EchoBot)透過使用Project Template建立出一個EchoBot出來,并且透過了bot emulator瞭解了如何和chatbot做測試。

這篇將會深入一些,看看BotBuilder的組成以及一些比較重要的class。

這篇的程式碼github頁面是alantsai-samples/mhat-hotelbot:blog/chapter-04

BotBuilder的運作方式

首先看一下上一篇建立出來的的VS 專案内容。裡面東西不復雜,可以看到只有幾個資料夾以及class:

devenv_2018-07-08_11-28-39.png
最基本的BotBuilder專案結構

我們可以看一下:

  1. MessagesController.cs
  2. RootDialog.cs

MessageController.cs

之前有提到過,BotBuilder是以Asp .Net Web Api為基地,因此一定會有一個Controller作爲進入點。而這個class就是我們整個服務的進入點。

往裏面看可以看到只有一個Post方法,并且接的參數形態是Activity。然後依照不同的ActivityType,有不同的處理:

public async Task Post([FromBody]Activity activity)
{
	if (activity.GetActivityType() == ActivityTypes.Message)
	{
		await Conversation.SendAsync(activity, () => new Dialogs.RootDialog());
	}
	else
	{
		HandleSystemMessage(activity);
	}
	var response = Request.CreateResponse(HttpStatusCode.OK);
	return response;
}

當ActivityType是訊息類型的時候,我們會呼叫RootDialog,要不然會到另外一個方法裡面處理不同類型的ActivityType。

Activity這個class是什麽東西呢?

Activity

還記得之前的一張圖嗎:

a238a09a-ee30-4106-8ec1-175e92acb902.png
整個Bot Framework的結構

我們提到過,爲了支援不同平臺,BotBuilder會用一個統一格式作爲最後的資料結構,而Activity或者更抽象的說IActivity就是這麽一個統一的形態。

因此BotBuilder每一次收到的都是一個Activity,而Activity又有分形態:

ActivityTypes.Message
最常見的一種形態,只要是使用者傳入的訊息都是這種。
ActivityTypes.ConversationUpdate
當有使用者加入或者退出chatbot的時候
ActivityTypes.DeleteUserData
當使用者退出chatbot并且要求刪除資料。這個可以用來清理chatbot對於某個使用者記錄的訊息。
ActivityTypes.Typing
當使用者正在打字的時候,還沒送出的類型。這種使用情景比較少。
ActivityTypes.Ping
一般來説這種都是平臺用來確定chatbot是不是還活著。

Activity取決於不同的Type也會包含不同的資訊。例如,如果是ActivityTypes.Message,那麽取決於是什麽類型的訊息(文字、圖片或語音)那麽從Activity取得資料的方式也不同。

最基本的文字類型,可以透過Text這個Property取得值。其他的訊息類型後面有遇到在介紹。

RootDialog.cs

Dialog是BotBuilder裡面的一個概念,可以簡單理解成爲:專門處理一件事情的最小單位,所以例如我的chatbot有查訂單和查旅館兩個功能,那麽就有2個dialog分別處理對應事情。

Dialog在後面拆分邏輯的時候會有更加詳細的介紹,以目前來説可以理解為RootDialog是我們的文字訊息處理的主要邏輯。

一個正確的Dialog有兩個條件:

  1. 實作IDialog<T>
  2. 把class訂位Serlizable

整個程式碼如下:

public Task StartAsync(IDialogContext context)
{
	context.Wait(MessageReceivedAsync);

	return Task.CompletedTask;
}

private async Task MessageReceivedAsync(IDialogContext context, IAwaitable<object>result)
{
	var activity = await result as Activity;

	// Calculate something for us to return
	int length = (activity.Text ?? string.Empty).Length;

	// Return our reply to the user
	await context.PostAsync($"You sent {activity.Text} which was {length} characters");

	context.Wait(MessageReceivedAsync);
}

StartAsync是IDialog本身要求實作的部分,他透過context.Wait表示等待下一個訊息要直接由 MessageReceivedAsync做處理。

而在MessageReceivedAsync裡面則是從Activity裡面取得了文字内容,并且透過context回傳了文字本身内容以及長度。

最後呼叫了一個context.wait,并且指向同一個方法。換句話説,變成了無限回圈,下一個訊息還是由MessageRecievedAsysnc來處理。

IDialogContext是一個輔助上下文的物件 - 裡面可以取得和這個使用者相關的一些訊息,和控制接下來的chatbot流程。下一篇將會介紹這個部分,因此暫時忽略。

調整chatbot

有了以上的資訊之後,對於如何修改程式就有了基本概念。我們要把理論變成實作。

假設今天有了一個需求:如果使用者輸入了Hello,chatbot回傳World,要不然就保留目前的回傳内容。

那麽怎麽修改呢?

從上面的理解知道所有邏輯都在RootDialog裡面,因此如果在裡面加入一些判斷邏輯就可以啦:

private async Task MessageReceivedAsync(IDialogContext context, IAwaitable<object> result)
{
	var activity = await result as Activity;

	if (activity.Text == "Hello")
	{
		await context.PostAsync("World");
	}
	else
	{
		// Calculate something for us to return
		int length = (activity.Text ?? string.Empty).Length;

		// Return our reply to the user
		await context.PostAsync($"You sent {activity.Text} which was {length} characters");
	}

	context.Wait(MessageReceivedAsync);
}
Bot Framework Emulator_2018-07-08_12-27-38.png
測試結果

這篇幾個重要Keyword總結

IActivity
整個BotBuilder接受以及傳送的一個共用的資料形態。
IDialog
一個最小單位的功能邏輯 - 透過Dialog可以做到Separtation of Concern。
IDialogContext
處理Chatbot上下文以及流程的物件。

結語

這篇對於整個BotBuilder的運作以及幾個重要物件做了介紹,并且透過一個小修改調整了chatbot的邏輯。

可是接下來有個很重要的問題是,我如果要記錄和使用者有關的訊息要怎麽存?

下一篇([05]深入IDialogContext - 處理上下文、對外的聯係和state),將會看一下專門處理上下文的物件IDialogContext


如果文章對您有幫助,就請我喝杯飲料吧
街口支付QR Code
街口支付QR Code
街口支付QR Code
支付寶QR Code
街口支付QR Code
微信支付QR Code
comments powered by Disqus