Alan Tsai 的學習筆記


學而不思則罔,思而不學則殆,不思不學則“網貸” 為現任微軟最有價值專家 (MVP)、微軟認證講師 (MCT) 、Blogger、Youtuber:記錄軟體開發的點點滴滴 著重於微軟技術、C#、ASP .NET、Azure、DevOps、Docker、AI、Chatbot、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產生欄位的時候就會使用這些資訊對產生内容做出微調。而Bot Builder SDK是一樣的概念

還有另外一種微調方式是在把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都去了哪裏?

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


如果文章對您有幫助,就請我喝杯飲料吧
街口支付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