Alan Tsai 的學習筆記


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

[chatbot + AI = 下一代操作模式][08]如何微調FormFlow讓使用上更流暢

[chatbot + AI = 下一代操作模式][08]如何微調FormFlow讓使用上更流暢.jpg
圖片來源:https://pixabay.com/en/books-spine-colors-pastel-1099067/ 

在上一篇([07]使用FormFlow讓Chatbot搜集表單資訊更容易)我們瞭解了如何透過使用建立Model然後搭配FormFlow的方式讓我們的chatbot可以從使用者那邊搜集到表單類型的資訊。

不過我們也開始遇到一些問題,舉例來説,欄位名稱是英文,如果中途退出就gg了等等的細節問題。這些問題需要我們對Model或者FormFlow建立的時候做一些調整。

這篇將和大家介紹一下,如何做這些調整。

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

如何微調FormFlow内容?

還記得以前學MVC的時候,做CRUD資料的部分都是搭配Entity Framework儲存在資料庫。那個時候,表單呈現的欄位也是用Model定義出來,如果欄位名稱要調整,那時候我們需要做什麽?

沒錯,那時候我們會用DataAnnotation Attribute來給我們的Model欄位提供一些資訊,在前臺的Helper產生欄位的時候就會使用這些資訊對產生内容做出微調。而BotBuilder是一樣的概念

還有另外一種微調方式是在把Model轉換成爲FormFlow的時候,那邊也可以做一些調整。

在這篇,將會對以下幾個微調的地方做説明:

  • 增加開始時候的説明文字
  • 處理使用者退出不報錯
  • 調整呈現的文字
  • 加强資料驗證
  • 内建多語系説明

增加開始時候的説明文字

目前,當我們觸發FormFlow的時候,直接就進入了每一個欄位的問題,不過可能對使用者來説少了一個引導詞。因此,一般來説會加一個説明接下來會發生什麽事情。

加的方式是透過建立FormFlow的時候多執行一個Message的方法:

public static IForm<RoomReservation> BuildForm()
{
	return new FormBuilder<RoomReservation>()
		.Message
		("歡迎使用訂房功能。接下來將會問您一系列問題好讓我們幫您找到最好的房間。" +
			$"{Environment.NewLine}有任何問題隨時打入Help將有幫助文字出現。")
		.Build();
}

Bot Framework Emulator_2018-07-12_06-46-11.png
呈現效果,左邊是沒加過之前,右邊是加過之後

處理使用者退出不報錯

使用者在填寫表單過程中,如果有退出的情況(輸入quit),這個時候系統會報錯。原因是,當使用者退出的時候,一個exception會被丟出來。目前我們沒有接任何exception因此直接導致整個機器人就出錯了。

要解決也簡單,我們只要在FormFlow觸發時給的那個CallBack加上處理即可:

private async Task AfterReserveRoomAsync(IDialogContext context
            , IAwaitable<RoomReservation> result)
{
	RoomReservation reservation = null;

	try
	{
		reservation = await result;

		await context.PostAsync($"得到的結果:{Environment.NewLine} {JsonConvert.SerializeObject(reservation)}");
	}
	catch(FormCanceledException<RoomReservation> ex)
	{
		string reply;

		if (ex.InnerException == null)
		{
			reply = $"您在 {ex.Last} 的時候退出了 -- 如果有遇到任何問題請告訴我們";
		}
		else
		{
			reply = "機器人暫時罷工了,請稍後嘗試";
		}

		await context.PostAsync(reply);
	}
	finally
	{
		context.Wait(MessageReceivedAsync);
	}
}
Bot Framework Emulator_2018-07-12_06-54-40.png
左邊是原來的結果,右邊是經過處理的結果

調整呈現的文字

FormFlow在處理英文的時候我盡量幫忙切字,因此詢問欄位的時候不會怪怪的。例如,我們的StartDate在詢問的時候很好的切割變成:sart date

不過中文不是這樣運作,這個時候我們可以透過給Property Attribute設定來告訴FormFlow呈現内容是什麽。總共有3個Attribute和這個有關:

DescribeAttribute
這個是用來設定欄位的名稱
PromptAttribute
這個是用來設定整段問題的文字。
TemplateAttribute
這個和DescribeAttribute是一樣的,不過會影響到每個欄位,DescribeAttribute則是只影響到有被設定的那個欄位。

假設今天我們的StartDate要呈現為:入住日期,那麽我們可以在Model裡面加入PromptAttribute

[Serializable]
public class RoomReservation
{
	[Describe("入住日期")]
	public DateTime StartDate { get; set; }
...
Bot Framework Emulator_2018-07-12_07-11-13.png
左邊是原來的内容,右邊是有調整過呈現的内容

我們可以看到,修改之後,欄位文字變成我們在Describe定義的那樣,不過整體文字還是英文。這個時候就是PromptAttribute進來的時候:

[Serializable]
public class RoomReservation
{
	[Describe("入住日期")]
	[Prompt("請輸入您的 {&}")]
	public DateTime StartDate { get; set; }
...
Bot Framework Emulator_2018-07-12_07-47-12.png
左邊是原來的内容,右邊是調整過呈現的内容

PromptAttribute修改的是某個欄位的輸出,但是如果我全部的欄位都要調整呢?這個時候就是使用TemplateAttribute的時候。TemplateAttribute是放到class等級,因此影響所有欄位。

Prompt和Template輸入的内容其實是pattern language,裡面有一些特殊字用來替換,例如{&}代表這個地方放入欄位名稱。更多特殊字可以參考:Customize user experience with pattern language

加强資料驗證

欄位輸入的很大一個注意事項就是驗證,需要過濾掉使用者輸入有問題的欄位。

内建FormFlow已經有幫忙驗證基本形態,舉例來説,如果是數字形態,那麽輸入文字FormFlow會提示輸入錯誤。如果是enum,輸入不在enum的欄位也會提示,不過有些更商務邏輯的驗證就需要開發者處理。

舉例來説,數字類型的輸入只允許1到10之類。或者說,如果某個欄位輸入了什麽,另外一個欄位的值就要是多少。

在FormFlow有提供三種方式:

  1. 使用NumericAttribute - 定義數字型可以在什麽範圍内
  2. 使用 PatternAttribute - 用RegEx定義,輸入的内容可以是什麽
  3. 使用 FormFlow 來定義複雜邏輯

首先,先來看看數字型的驗證:

[Serializable]
public class RoomReservation
{
	...
	[Numeric(1, 5)]
	public int NumberOfNightToStay { get; set; }
...

Bot Framework Emulator_2018-07-12_08-40-30.png
左邊是原來的情況,右邊則是加入後的結果

PatternAttributeNumericAttribute是一樣的概念,也是透過設定在某個欄位上面就有效果。

最後一種可以在建立FormFlow的時候設定驗證邏輯。除了驗證邏輯之外,也可以用來改值。舉例來説,如果我要把所有的開始日期都加1天,我可以透過這個客製邏輯方式達到:

public static IForm<RoomReservation> BuildForm()
{
	return new FormBuilder<RoomReservation>()
		.Message
		("歡迎使用訂房功能。接下來將會問您一系列問題好讓我們幫您找到最好的房間。" +
			$"{Environment.NewLine}有任何問題隨時打入Help將有幫助文字出現。")
		.Field(nameof(StartDate),
				validate: async (state, value) =>
				{
					var result = new ValidateResult
						{ IsValid = true, Value = value };

					var datetime = (DateTime)value;

					result.Value = datetime.AddDays(1);

					return result;
				})
		  .Field(nameof(NumberOfNightToStay))
		  .Field(nameof(NumberOfOccupants))
		  .Field(nameof(BedSize))
		.Build();
}
Bot Framework Emulator_2018-07-12_20-03-04.png
最後呈現結果,每次日期+1

内建多語系説明

到目前爲止,我們的FormFlow主要都是英文,當然我們可以透過上面介紹的例如Template來達到全部改成中文,但是實際上FormFlow本身就有支援多語系。

内建只有簡體中文,如果要繁體中文就需要自己處理了。

我們可以透過Bot Emulator v3 版本,連綫的時候輸入中文的語系:zh-cn

botframework-emulator_2018-07-12_20-11-56.png
設定locale的畫面

接下來我們做測試,會發現FormFlow裡面本來是英文全部變成中文啦:

botframework-emulator_2018-07-12_20-12-40.png
FormFlow全部變成了中文

不過我們同時會發現,本來關鍵字也變成了中文。例如看狀態的status,可能以爲是狀態,但是實際上是进度

botframework-emulator_2018-07-12_20-13-14.png
看到目前輸入情況

參考資料

官方介紹今天提到的内容
Advanced features of FormFlow

結語

我們發現FormFlow非常的好用,能夠快速收集使用者的資料來完成表單填寫。

但是,魔鬼藏在細節裡面,而這些細節能夠透過這篇裡面做一些微調。

是時候在回到我們之前的問題,邏輯全部都擠在了RootDialog裡面,那以後怎麽維護?我們的SOLID都去了哪裏?

BotBuilder考慮到了這個事情,因此有IDialog,下一篇([09]使用IDialog來實現SoC)將來看看怎麽拆分我們邏輯讓一切變得更加乾净并且更SOLID。

comments powered by Disqus