隨著蘋果Core ML的最新版本發佈,開發人員更容易構建人工智能應用程式,除了圖像識別和文本檢測是利用AI建置APP的好例子,另一種善於展現機器學習Power的應用程式類型則是chatbots。在本教程中,我們將使用Google的Dialogflow(以前稱為API.AI)構建在iOS上運行的chatbot應用程式!
聽起來很酷吧!接下來開始進入本教程的重點。
Intents(意圖)和Entities(關鍵字)快速概覽
在開始之前,我先解釋Dialogflow和chatbots的一般基本知識。 在構建chatbots時,你必須知道兩個術語:Intents(意圖)和Entities(關鍵字)。
An entity represents a term or object in the user’s input that provides clarification or specific context for a particular intent.
(entity表示用戶輸入中的術語或對象,為intent提供說明或使用情境。)An intent, on the other hand, represents something that the user wants to do. If intents represent verbs, then entities represent nouns.
(另一方面,intent代表用戶想要做的事情,如果intent代表動詞,則entities代表名詞。)
來看一個例子,在我們的專案中,可能會告訴我們的機器人以下聲明:
“Book me a room at the La Grande Hotel”(替我在La Grande Hotel訂一間房)
在這句話中,我們的intent(意圖)是”預定一個房間”,entity(關鍵字)是”La Grande Hotel”,現在,自然語言處理(NLP)算法可以計算兩種不同類型的對話內容。
- 基於意圖(Intent-based)的對話:這是當NLP算法使用intents和entities進行對話時,通過識別用戶聲明中的名詞和動詞,然後與它的dictionary交叉引用,讓bot可以執行有效的操作,這種類型的對話是Dialogflow使用的。
-
基於流程(Flow-based)的對話:基於流程的對話是智能通信的下一個級別。在這裡,我們會給予兩個人之間對話的許多不同樣本的RNN(循環神經網絡),創建的機器人將根據你訓練的ML模型進行響應。Wit.ai是在這個領域取得巨大進展的少數網站之一,不用擔心,我們不需要做到這個程度。
在本教程中,我們將創建一個可幫助你預訂hotel的機器人。
One of the most important rules when developing a chatbot is that it MUST have a personna. This means that it must behave like a real person. Therefore, let’s name out bot – Chip!
(開發chatbot最重要的規則之一,就是它必須有一個personna,意味著它必須像真人一樣行事。 因此,讓我們來替bot命名 – Chip!)
接下來,就進入本文重點!
熟悉Dialogflow
進入到Dialogflow,在右上角點擊”Go to Console(前往控制台)”。
系統會要求你使用Google帳戶登錄,並授權使用Dialogflow在Google雲端平台服務中查看和管理你的資訊,接受條款,你應該看到一個初始啟動頁面。
觀看介紹影片可以讓你更快速了解Dialogflow,但如果你不想花時間看也沒關係!點擊”Create Agent”按鈕,在Dialogflow中,一個agent(代理)意味著iOS應用將使用chatbot通過無線方式進行通訊以接收回應。
填寫代理名稱(比如Chip),然後點擊Create按鈕進行下一步,Dialogflow將為你創建agent。現在,讓我們來確認一下,你應該有2個預設intents:”Default Welcome Intent”和”Default Fallback Intent”。在左側欄位中,你應該可以看到Intents和Entities的tabs(選項)。
我們也會在下方看到其他tabs。現在,該開始創建機器人了!
添加Entities
首先,讓我們開始添加entities,如果你還記得,entities就像NLP算法可以理解的名詞,拿出一個可能經常用於我們機器人的entities名單,我已經把我整理的list放在下面,並且隨時可以添加它。
- Hotel
- Room
- Payment
選擇entities選項,然後點擊”Create Entity”按鈕。並將這個entity命名為”Hotel”,並點擊第一行,輸入”Hotel”當做參考值,當用戶使用你的機器人時,他們可能會使用Hotel以外的其他名稱。 因此,應該輸入關鍵字的一些同義詞,即使用戶使用”Hotel”以外的字,機器人仍然可以理解用戶在說什麼,看下面的圖片,我使用了一些同義詞。
現在,儲存你的entity,並按照剛才建置Hotel entity的步驟創建以下的entities。
現在我們已經創建了entities,接著來討論intents。
添加Intents
進入Intents頁面並點擊Default Welcome Intent,這個intent就是我們機器人在第一次啟動時會抓取的東西,你應該看到網頁呈現如下:
我們可以制定用戶應該說出哪個字去觸發intent。在本頁面的最底部,我們也可以制定回應的文本內容,由於這是一個Welcome intent,用戶可能會說”Hello!”或”How’s it going?” 因此,讓我們將這些短語(和任何類似的同義詞)添加到’User says’部分,以下是一些範例,你可以隨意地添加更多的訊息,設定你的用戶可能會對機器人說的字彙。
如果向下滾動,會找到* Response *部分。intent帶有一些內置的回應,我們添加一個follow up question(關聯性問題):”What can I do for you?”,我們最終Welcome intent應該是這樣的:
在我們開始創建下一個intent之前,如果你想在任何時候測試你的agent,請查看右側欄位,你可以輸入想要的任何內容,然後查看你的agent是否回應。輸入”Hello”,然後檢查agent是否回應了預期的回應。
到目前為止,agent只有一個intent,由於我們的機器人是為處理預訂hotel而設計的,因此我們必須創建另一個處理intent這些查詢,用戶可能會問:“能替我預訂一家旅館嗎?” 或類似的問句。
讓我們創建一個新的intent,並將其命名Begin Order。在User says欄位中,添加上面的表達式然後按下enter。一旦你輸入了這個表達式,將會看到agent已經在該語句中識別了一個@Hotel
關鍵字。與entities類似,用戶不會只用這個問句來表達預訂酒店的需求。所以這裡添加一些變化,以agent理可以了解用戶的意思,增加的變化越多,agent也就越聰明,以下是一個範例展示。
agent應該做的下一件事是搜索附近的hotel,並詢問用戶他/她需要幾間房。但是,如果我們要真正搜尋附近的酒店,則需要調用API並使用JavaScript將webhook與api.ai整合在一起,這超出了本教程的範圍,所以讓我們在Response欄位創建一些虛擬酒店,這是我創建的回應內容:
儲存intent並返回到主頁面,在右側欄位中測試你的agent,到目前為止,如果讀者有跟著前面的步驟,它應該按預期工作!正如你所看到的,不必提出確切的問題,Dialogflow將從你的陳述中學習並理解變化。
現在,我們來添加一些follow-up intent,將鼠標移動在你剛創建的intent上,你應該看到一個選項”Add follow-up intent”,選擇它並點擊Custom,將產生一個新的intent,並顯示”Begin Order – custom”,讓我們編輯這個intent!
請記住,我們agent對我們說的最後一件事情是”How many rooms do we want?”,用戶可能會回覆一個數字,他/她可能會說:”I would like 1 room”或簡單地回答”4″,你應該要預測用戶會說什麼,並填寫所有可能的答案,請參考以下我填寫的範例:
正如你所看到的,Dialogflow有一個內置的數字entity,不管你輸入一個數字還是一個單詞,它都能夠處理它並理解它的含義。
接下來,我們要讓機器人回應確認價格總額,並詢問用戶喜歡什麼付款方式。同樣的,由於這些是hotel虛擬資料,我們可以在機器人的反應中添加虛擬的價格。
保存這個intent,現在給讀者一個挑戰,創建最後一個intent,即詢問用戶使用何種付款方式付款,這應該是非常簡單的,因為我們已經做了兩次!
希望讀者都能順利完成,如果沒有也沒關係,請按照下面的方式!所以我們將回到intent的主頁面,然後點擊Create Intent。
將這個intent命名為Payment並添加用戶可能會說的內容,這是我們的Payment關鍵字(entity)使用的地方!
最後,讓機器人回應一些確認訊息。以下是一些範例:
- Done! You have rent the rooms!
- Success! We received your payment.
就是這樣!請記住保存Payment意圖,Chip現在可以使用了,在我們轉到本教程的iOS端之前,你可以在右側欄位中對其進行測試。
A Couple More Things
在開始真正的編程之前,讓我花點時間來解釋Dialogflow控制台左側欄位中的其他tabs,在Entities下,有一個名為Training的tab,如果點擊此選項,你將收到所有發送給agent的回覆訊息以及agent回覆的內容,如果你告訴你的agent一些回應文本,但它回應你不喜歡的輸出,這就非常有用,若你稍後意識到忘記了某個關鍵字的同義詞,並且用戶正在使用這個關鍵字,那麼也可能會有所幫助,可以去告訴你的代理在這種情況下應該做什麼。
在Training下方,你可以看到Integrations。在這裡,可以管理你的agent去串接不同的服務,例如Google Assistant,Twitter,Slack,Messenger,Cortana,Alexa等等。Integrations之後,還有Analytics,基本上用來顯示建議名稱,之後還有Fulfillment,如果你要調用一個API並實現一個webhook,這就是你會需要來的地方。
最後兩個選項功能非常簡單,但很有用。第一個是Prebuilt Agents,在這裡,你可以import一個預先存在的代理框架,有很多例子,如食物傳遞機器人,音樂機器人,甚至(抱歉,但你真的需要知道這個)hotel預訂機器人! 最後一個選項是Small Talk,如果你將代理設計為像Siri或Google Assistant這樣的每日夥伴(daily companion),這個選項非常有用,Small Talk允許你添加常見問題的答案,我們都喜歡問我們的機器人,如”你幾歲?”或”你住哪裡?”,以及更熱門的問題”你願意嫁給我嗎?”
現在你已經知道Dialogflow是什麼,並且對於如何操作有很好的觀念了,現在是時候移動到另一端,開始編寫Swift程式碼!
使用API.AI SDK連接到Dialogflow
現在我們移動到本教程Swift的部分,首先[下載初始專案](https://github.com/appcoda/ChatbotHotel/raw/master/ChatbotStarter.zip),我已經構建了基本的UI並綁定了API.AI SDK。如果你從頭構建應用程序,則可以使用CocoaPods安裝API.AI SDK(這是用於連接到Dialogflow的SDK),只需在Podfile中添加以下訊息:
pod 'ApiAI'
一旦你unzip初始專案,確保你打開了Chatbot Starter Project.xcworkspace
文件。進入Main.storyboard
,已經有一個UILabel
,UIButton
和UITextField
,它們的outlets也連接到ViewController.swift
。
先來看看AppDelegate.swift
,我們需要讓APP連接到Dialogflow的servers,在import UIKit
的正下方,輸入以下程式碼來導入framework:
import ApiAI
現在,需要使用client access token來初始化我們的配置,請參照下面範例更新didFinishLaunchingWithOptions
方法:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
let configuration = AIDefaultConfiguration()
configuration.clientAccessToken = "YOUR_CLIENT_ACCESS_TOKEN"
let apiai = ApiAI.shared()
apiai?.configuration = configuration
return true
}
將字符串”YOUR_CLIENT_ACCESS_TOKEN”替換你自己的機器人的client access token,如果你不知道在哪裡可以找到,請移動到Dialogflow中的chatbot settings。 在”General”選項下,你應該在”API keys欄位下找到client access token。
現在,當我們的應用程式啟動時,它將使用client access token連接到Chip bot。
讓裝置開始說話
前往ViewController.swift
,並且在import UIKit
之下,import ApiAI和AVFoundation框架:
import ApiAI import AVFoundation
我們需要導入AVFoundation
框架,因為我們需要bot與用戶交談,該框架附帶了能夠從文本轉為合成語音的“AVSpeechSynthesizer”類別,為了讓裝置與我們的用戶交談,在ViewController
類中插入以下幾行代碼:
let speechSynthesizer = AVSpeechSynthesizer()
func speechAndText(text: String) {
let speechUtterance = AVSpeechUtterance(string: text)
speechSynthesizer.speak(speechUtterance)
UIView.animate(withDuration: 1.0, delay: 0.0, options: .curveEaseInOut, animations: {
self.chipResponse.text = text
}, completion: nil)
}
讓我告訴你上面的程式碼做了哪些事。首先,我們定義一個常數speechSynthesizer
,並初始化一個 AVSpeechSynthesizer
的實例。AVSpeechSynthesizer是一個提供自文本轉換為語音的object,並允許存取控制正在進行的訪問,然後創建一個新的函式speechAndText(text: String)
,根據用戶輸入的內容執行更改。
在函式內部,我們創建一個AVSpeechUtterance的實例,最簡單的說,它是一個將被宣讀的文本塊。然後,我們要求裝置讀出這段文字,同時,我們想向用戶展示機器人的response,這就是為什麼我們將label的text設置為機器人的response。
我使用UIView.animate
方法為label轉換為微妙的動畫,當你正在開發自己的應用程式,但不具備創建高品質的動畫知識,則這種方法可以實現這一效果。
發送請求
我們只剩下最後一部分,當用戶點擊按鈕時,應發送request給我們的代理,讓我們看看我們該怎麼做!在sendMessage
操作方法中插入以下幾行程式碼:
let request = ApiAI.shared().textRequest()
if let text = self.messageField.text, text != "" {
request?.query = text
} else {
return
}
這段code是相當基本的,但是可能部分讀者不太理解這段代碼,還是讓我解釋一下。基本上,我們以用戶提供的query條件來準備API.AI文本請求,從messageField
中檢索文本並執行基本驗證,確保文本字段不是空白,一旦我們得到了這段text,就將它丟給request的query
屬性。
好的,文本request已經準備好了,下一步是發起請求並發送給機器人。當然,我們需要處理API.AI代理回應的任何內容,有兩種可能性:success或failure,如果代理程序返回成功訊息,那麼我們希望應用程式說出回應並將其顯示在螢幕上,如果出現失敗訊息,那麼應用程式只是打印錯誤到控制台,我們可以通過使用下面的程式碼來實現:
request?.setMappedCompletionBlockSuccess({ (request, response) in
let response = response as! AIResponse
if let textResponse = response.result.fulfillment.speech {
self.speechAndText(text: textResponse)
}
}, failure: { (request, error) in
print(error!)
})
request執行完成後,應用程式需要做什麼,你可以調用setMappedCompletionBlockSuccess
方法並在閉包中指定動作,一旦請求完成,完成處理程序將被調用,並將回應作為參數傳遞,在閉包中,我們調用前面創建的speechAndText(text: )
方法來說出並顯示回應內容,如果response顯示失敗,我們只需將其打印到logs即可。
剩下最後一件事,我們還沒有發起對API.AI的request,為此,我們調用enqueue
函式並放入指定request,這可以通過使用下面的程式碼來完成:
ApiAI.shared().enqueue(request)
messageField.text = ""
我們將請求發送到API.AI並清除textfield中的文字,你的整個sendMessage
方法應該如下所示
@IBAction func sendMessage(_ sender: Any) {
let request = ApiAI.shared().textRequest()
if let text = self.messageField.text, text != "" {
request?.query = text
} else {
return
}
request?.setMappedCompletionBlockSuccess({ (request, response) in
let response = response as! AIResponse
if let textResponse = response.result.fulfillment.speech {
self.speechAndText(text: textResponse)
}
}, failure: { (request, error) in
print(error!)
})
ApiAI.shared().enqueue(request)
messageField.text = ""
}
是時候了!現在運行應用程式(在iPhone X上),一切都應該按預期工作!
What’s Next
本教程包含很多的資訊,希望對讀者會是非常有益的,那麼,接下來你應該做什麼?我會建議你繼續擴展這個機器人,甚至創建你自己的機器人,期待你分享如何創建自己的機器人,歡迎發表在下面的評論!
以供參考,你可以在Github下載完成的專案。
關於Dialogflow的更多資訊,你你可以參考他們的documentation。
勇敢大膽的嘗試吧!你可以在Dialogflow上創建自己的聊天機器人,並把它放在Google Assistant上。如果你想嘗試一下,請查看這個影片,可以打開Google Assistant並透過”Talk to Max the Programmer”來測試我用Dialogflow製作的聊天機器人!鼓勵大家繼續研究Dialogflow,因為你可以在Google Assistant,Alexa,Twitter,Cortana,Facebook Messenger,Telegram等平台部署聊天機器人!
FB : https://www.facebook.com/yishen.chen.54
Twitter : https://twitter.com/YeEeEsS
原文:Building a Chatbot App for iOS with Dialogflow (API.AI) and Text-to-Speech