Alan Tsai 的學習筆記


學而不思則罔,思而不學則殆,不思不學則“網貸” 為現任微軟最有價值專家 (MVP)、微軟認證講師 (MCT) 、Blogger、Youtuber:記錄軟體開發的點點滴滴 著重於微軟技術、C#、ASP .NET、Azure、DevOps、Docker、AI、Chatbot、Data Science

[chatbot + AI = 下一代操作模式][05]深入IDialogContext - 處理上下文、對外的聯係和state

[chatbot + AI = 下一代操作模式][05]深入IDialogContext - 處理上下文以及對外的聯係.jpg
圖片來源:https://pixabay.com/en/books-spine-colors-pastel-1099067/ 

在上一篇([04]瞭解Bot Builder SDK的組成)完整看了EchoBot的程式碼組成,并且瞭解了Bot Builder SDK一些常見的物件。并且依照所學調整了部分程式碼。

這一篇將會聚焦在其中一個管理上下文以及對來連綫的物件IDialogContext

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

IDialogContext的用途

還記得主要邏輯是在RootDialog.cs裡面,而其中在裏面的程式碼用IDialogContext做了兩件事情:

  1. context.Wait() - 用來控制接下來的訊息要被什麽方法處理
  2. context.PostyAsync - 用來回傳訊息給使用者的方法

透過這兩個方法,可以看出IDialogContext有兩個作用:

  1. 控制流程 (底層Internals.IDialogStack) - Wait代表接下來的訊息處理,還有一些別的,例如 Call 用來呼叫別的Dialog
  2. 和外界聯係 (底層Internals.IBotToUser) - PostAsync把訊息回傳給使用者。

IDialogContext還有一個重要的功能,就是用來儲存和conversation或者user有關訊息的方式 (底層Internals.IBotData)。

儲存和conversation或者user有關訊息的方式

記錄訊息有三個等級:

ConversationData
和某個conversation有關的記錄。
PrivateConversationData
和某個user在某個conversation有關的記錄
UserData
和某個user有關的訊息 - 包含所有的channel以及conversation。

預設,這些訊息是儲存在Memory裡面,因此上了Production等級記得要做一些調整,預設有支援可以儲存在Azure的Cosmos DB或者Table Storage,當然如果有需要也可以儲存在別的地方,可以自己寫或者找套件。

這3個等級要依照需求去儲存,例如,假設是一個和使用者有關的訊息但是不和任一個conversation有關,例如他的名字,那麽可以儲存在UserData。反過來説,如果是某些訊息和某次conversation有關,例如 查旅館的時候記錄選擇了那個,那麽就可以使用PrivateConversationData

最後,要使用這些儲存很容易,把他們當成Dictionary使用就好。

Conversation是什麽

在上面提到儲存等級的時候,提到了兩個詞,Channel以及conversation。

channel之前提到過,其實就是使用者使用的溝通平臺。有可能你的bot部署到多個平臺,那麽用channel來區分可以做一些特定平臺相關處理

conversation則是某一次的交談。例如,一整串的對話就屬於一組conversation。等到10天後又過來就變成另外一組conversation。

這些資料其實有儲存在Activity裡面,我們透過emulator可以看到實際的值:

Bot Framework Emulator_2018-07-08_18-18-23.png
第1點可以看到是channel的值,然後conversation id來區分conversation

實際修改

上面提到了這些理論,接下來我們就來實際調整chatbot。

這邊的需求是,如果沒有使用者的姓名,需要先和使用者取得,取到了之後,不管使用者輸入什麽都會直接回傳并且加上使用者的姓名。

要做到上面的需求,整個流程變成:

  1. 先判斷是否已經有使用者的姓名
  2. 當詢問使用者名稱的時候記錄下一個回答是記錄名字
  3. 把回傳是使用者名稱,儲存起來

先判斷是否已經有使用者的姓名

這邊使用了UserData做儲存,因爲使用者姓名可能在多個conversation都可以使用(當然用什麽取決需求)

整個code調整如下:

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

	context.UserData.TryGetValue<string>("Name", out string name);

	if(string.IsNullOrEmpty(name))
	{
		// 還沒有姓名
	}
	else
	{
		// 已經有姓名直接輸出 姓名 + 輸入内容
		await context.PostAsync($"{name}: {activity.Text}");
	}

	context.Wait(MessageReceivedAsync);
}

當詢問使用者名稱的時候記錄下一個回答是記錄名字

我們接下來要處理詢問使用者名稱的部分。整個code如下:

....
if(string.IsNullOrEmpty(name))
{
	context.PrivateConversationData.
		TryGetValue<bool>("IsAskName", out bool isAskName);

	if(isAskName)
	{
		// 詢問過名字,準備記錄
	}
	else
	{
		context.PrivateConversationData.SetValue<bool>("IsAskName", true);

		await context.PostAsync("您的名字是?");
	}
}
....

PrivateConversationData來記錄是否詢問過名字,這邊選擇PrivateConversationData是因爲這個資訊和使用者有關,并且只和這次的conversation有關。

需要這個中繼的IsAskName是這樣才好區分已經在問名字還是正在詢問名字。

後面有更加好的處理方式,現在先暫時這樣。

把回傳是使用者名稱,儲存起來

那最後一塊就是做名稱儲存就是這樣:

...
if(isAskName)
{
	context.UserData.SetValue<string>("Name", activity.Text);

	await context.PostAsync($"{activity.Text} 您好,能夠幫助您什麽");
}
else
{
	context.PrivateConversationData.SetValue<bool>("IsAskName", true);

	await context.PostAsync("您的名字是?");
}
...

最後結果

接下來我們可以把chatbot run起來并且用emulator做一些測試。

Bot Framework Emulator_2018-07-08_18-42-42.png
測試結果
這邊會發現有個地方滿違和,也就是我需要先輸入一個你好才觸發整個chatbot。這個其實可以在使用者剛加入chatbot的時候詢問。這個就可以用ActivityTypes.ConversationUpdate來做這些處理。

結語

這一篇介紹了IDialogContext這個物件的作用,并且這篇著重於如何儲存個人資料。希望透過這篇對於IDialogContext的作用及用途會更清楚。

在下一篇([06]不只能輸出文字 - 看看各種内建卡片模式以及可自定的Adaptive Card)我們切換一下,如果只能輸出一般文字對現在使用者來説還是太乾了,來看看BotbBuilder在傳輸不同資料格式上面有什麽幫助。


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