2020年3月14日 星期六

幫 Google Assistant 加上更多語言

上一篇我們成功做出一個會跟我們猜數字的 Assistant,不過因為方便我開發的時候都是用英文在開發,自己玩玩當然 OK,但對非英文使用者就不行了,因此我們來試著加上中文的回應。
目前 google assistant 支援 20 種語言(註),在 assistant 的開發頁面選擇 modify language setting 可以打開想要的語言,我這裡先開英文和繁體中文,兩個語言都要設定 assistant 的呼叫詞,中文的我們就叫它「猜數字程式」。

在連接的 dialogflow 的部分,點選 project 旁邊的小齒輪,language 分頁裡面也加上繁體中文的選項,在左側欄的 project 名稱下面就可以看到有兩個不同的語言。

加完語言之後,最麻煩的就是把整個設定複製一份了,所有的 intent 設定在新的語言都要再設定一遍,或者你在不同的語言想要採取不同的 flow 也可以,因為猜數字很簡單,我英文跟中文就用相同設定,training phrase 的部分,直接打上中文就可以了。


設定完 dialogflow 之後,下一步要設定我們的 webhook 讓它也能處理多國語言,我選用的套件是 python 的 python-i18n,可以用 yml 或 json 格式儲存想要翻譯的文字,這樣我們程式碼幾乎結構不用大修,只要在回應的地方呼叫 i18n 幫我們吐出翻譯過的文字就好。
修改後的目錄長成這樣:
main.py
locales
  | guess.en.json
  | guess.zh-tw.json
把文件檔都放在 locales 下面,檔案名稱為:{title}.{lang}.json,英文的 guess.en.json 內容如下:
{
  "en": {
    "test" : "test",
    "welcome" : "I have a number between %{low} and %{high}. Can you guess it?",
    "guess_out": "Are you sure? I said a number between %{low} and %{high}",
    "guess_unmatch" : "A number between %{low} and %{high}. Keep guess."
  }
}
對應的中文則是 guess.zh-tw.json:
{
  "zh-tw": {
    "test" : "測試字串",
    "welcome" : "我有一個介於 %{low} 到 %{high} 的數字,你能猜到嗎?",
    "guess_out": "你確定嗎?我說介於 %{low} 跟 %{high} 之間",
    "guess_unmatch" : "介於 %{low} 跟 %{high} 之間,再接再勵"
  }
}
開頭是 language code,這邊這個名字要跟檔案的 {lang} 是相同的,雖然說有點多此一舉的感覺,內容則是 key-value 的形式儲存文字內容。

主程式的部分也要對應的修改:
import i18n

i18n.load_path.append('locales')
i18n.set('file_format', 'json')
i18n.set('fallback', 'en')

language_code = data.get("queryResult").get("languageCode")
print(language_code)
i18n.set('locale', language_code)
text = i18n.t('guess.welcome', low = str(low), high = str(high))
因為我們把檔案都放在 locales 下面,在搜尋路徑上加上 locales;檔案格式為 json;預設的語言是英文。
在 assistant 的 request 裡面,語言設定會放在 queryResult -> languageCode 下,用 i18n.set 設定 locale;這時呼叫 i18n.t(title.key) 就會找出在 {title}.{lang}.json 檔案裡,{lang} 下面 key 對應的字串了,i18n.t 的參數,則可以代入預先設定好的 placeholder 代換到字串裡。

測試一下,在測試頁面可以選擇測試的語言,選擇繁體中文來試試:

現在我們的人工智障會講中文了。

附註:在設定頁面全部可選的語言(依英文首字排序)有:繁中、粵、丹、荷、法、德、印度、印尼、義、日、韓、挪、波蘭、葡、俄、西、瑞典、泰、土;其中西、法、英三大家有口音選項。
雖然在寫這篇的時候我有查到去年 12 月的新聞,說 google assistant 會支援 44 種語言,但截至我寫這個機器人的時候,還沒有這麼多的語言可以設定,可能是新聞跑得比開發工具快吧。另外 google assistant 的說明文件上,則是沒有這麼多種語言,可能開發工具又跑得比文件快一點。
結論:宣傳 > 開發工具 > 開發文件(欸

2020年3月1日 星期日

Rust std process 生成子行程

最近在玩 Rust 的時候,需要用 Rust 去呼叫一些 shell command 來幫我完成一些事,幸好 Rust std 裡面已經有 process 來幫我們完成這件事,使用起來很像 python 的 subprocess,不過實際在用遇到一些問題,所以寫個筆記記錄一下:

首先當然是從 std 引入這個模組的 Command,Stdio 很常用也順便 include 一下:
use std::process::{Command, Stdio};

一切的基礎就是一行 Command::new(command_name),在 command_name 的地方填入你想呼叫的指令。
Command 代表了一個準備好要跑的命令,就像是在 shell 裡面打下 command_name 直接按 enter 一樣,沒有參數、繼續現在行程的環境、位置和現在行程的位置相同。
如果要設定給命令的參數,就用 .arg 塞進去,如下面的例子:
let mut ls = Command::new(ls).arg("-al");
這個參數一次只能塞一個,有多個參數要連續呼叫 .arg 才行。

有個 Command 之後接下來有三種方式讓它跑起來:.spawn(), .output(), .status():

  • spawn fork 子行程執行,拿到一個子行程的 handler,回傳的型別是 Result<Child>。
  • output fork 子行程執行,等待(wait)它結束之後,收集它寫到 std output 的內容,回傳的型別是 Result<Output>。
  • status fork 子行程執行,等待它結束之後,收集它回傳的資訊,回傳的型別是 Result<ExitStatus>。

第一個可以注意到的是回傳的型別都是 Result,這是因為 command 可能會跑起來也可能會跑不起來,像是我打一個 Command::new("www") 但我的 shell 根本沒 www 這個指令,Result 提醒了這個可能性的存在,一般來說這邊最簡單的就是用 .expect 把 Result 解開。
第二個另人疑惑的,是後面的 Child, Output, ExitStatus 是什麼鬼,整理之下大概是這樣:

  • ExitStatus 是最簡單的,就是行程結束的狀態的封裝,Rust 提供兩個介面 success 跟 code 來判斷子行程有沒有正常結束以及對應的 exit code。
  • Output 是更上一層,裡面包了一層 status : ExitStatus,加上兩個 stdout, stderr 的 Vec<u8>,裡面存了子行程所有寫到 stdout 跟 stderr 的內容。
  • 最外層就是由 spawn 產生的 Child,比起 output 跟 status 一生成行程就自動幫你 wait,spawn 給了完全的操作能力,可以做更多事情。

三個啟動的函式影響最大的就是子行程的 stdin/stdout/stderr,在 spawn 跟 status 下,stdin/stdout/stderr 會繼承父行程的 stdin/stdout/stderr;在 output 時,stdin 會被設定成不可使用(接到 /dev/null), stdout 跟 stderr 則會設定成 piped 來讀取。
如果不想用預設的設定,可以在呼叫 status/output/spawn 前做設定,有三個選項可選 Stdio::inherit、Stdio::piped、Stdio::null,分別就是繼承父行程、接 piped 到父行程跟接上 /dev/null 。

現在就能來玩一些例子,例如在 rust 裡面呼叫 ls,用 status() 的話輸出會直接輸出到螢幕上面:
let p = Command::new("ls")
    .arg("-al")
    .status()
    .expect("ls command failed to start");
drwxr-xr-x 5 yodalee yodalee 4096 2月 29 09:45 .
drwxr-xr-x 16 yodalee yodalee 4096 2月 28 20:10 ..
-rw-r--r-- 1 yodalee yodalee 62279 2月 29 00:07 Cargo.lock
-rw-r--r-- 1 yodalee yodalee 312 2月 29 00:07 Cargo.toml

如果想要把 ls 的內容截下來的話,就要改用 output:
let p = Command::new("ls")
    .arg("-al")
    .output()
    .expect("ls command failed to start");
這時候可以從 p.stdout 裡面拿到 Vec<u8>,要轉成字串就要用 String 的 from_utf8/from_utf8_lossy/from_utf8_unchecked 函式轉。
let s = from_utf8_lossy(&p.stdout);
println!("{}", s);
drwxr-xr-x 5 yodalee yodalee 4096 2月 29 09:45 .
drwxr-xr-x 16 yodalee yodalee 4096 2月 28 20:10 ..
-rw-r--r-- 1 yodalee yodalee 62279 2月 29 00:07 Cargo.lock
-rw-r--r-- 1 yodalee yodalee 312 2月 29 00:07 Cargo.toml

如果要對子行程上下其手有完全的操控,就要使用 spawn 了,不過相對來說也要小心,因為 spawn 不會自動幫你 wait,不小心就會把子行程變殭屍行程。
產生出來的 Child 物件,本身就自帶一些函式,像

  • kill() 發 SIGKILL 把子行程砍了。
  • wait()、wait_output() 等待子行程結束,spawn + wait/wait_with_output 就相當於直接呼叫 status/output。

我們用 shell 的 rev 當作例子,它會輸入 stdin 反轉之後輸出,這裡不能用 output() 因為 output 的 stdin 不會打開;可以用 status ,這樣 stdin 會繼承本來的 shell 的 stdin 讓我們打字,但如果我們是要反轉程式裡面的一行字串呢?這時候我們就要用 s.chars().rev().collect::<String>() 然後這篇文就不用寫了 spawn 再操作 stdin 了。

具體來說大概像是這樣:
let mut p = Command::new("rev")
    .stdin(Stdio::piped())
    .spawn()
    .expect("rev command failed to start");
let stdin = p.stdin.as_mut().expect("Failed to open stdin");
stdin.write_all("Hello".as_bytes()).expect("Failed to write stdin");

本來用 spawn 的話子行程的 io 會繼承父行程的,相當於上面那行改成 .stdin(Stdio::inherit()),這裡我們改用 Stdio::piped() 把它接出來。
接著我們可以從 p (型別是 process::Child)裡去取得它的 stdin, stdout, stderr,這個拿到的都是 Option 型別,用 expect 把它給解開來,裡面就會拿到 Rust 的 io 物件,可以用呼叫對應write 系列函式對它寫入內容,這裡用 write_all 對 stdin 寫入 "Hello" 的 Vec<u8>。
在 stdout 螢幕上就會看到 "olleH" 的輸出了。

當然我們也可以在呼叫的時候把 stdout 也導向 piped 處理,讓我們讀出反轉的結果:

let mut p = Command::new("rev")
    .stdin(Stdio::piped())
    .stdout(Stdio::piped())
    .spawn()
    .expect("rev command failed to start");

let stdin = p.stdin.as_mut().expect("Failed to open stdin");
stdin.write_all("Hello".as_bytes()).expect("Failed to write stdin");
let output = p.wait_with_output().expect("Failed to read stdout");
let revs = String::from_utf8_lossy(&output.stdout);
assert_eq!(revs, "olleH");

以上大概就是 Rust std process 使用方法的整理了,我自己大概有三點感想:
  1. 用 Rust 寫其實沒有比 C 用 fork/exec 來寫來得簡單多少,畢竟我們就是要操作子行程,底層都是系統程式那套,Rust 頂多是封裝得比較完善一點,實際上用起來該設定的一個少不了。
  2. 要寫系統程式,系統程式的概念少不了,要寫 process 至少需要知道作業系統行程的概念(不然一不小心會變成 World War Z 殭屍產生器),操作輸入輸出需要大略知道 file descriptor 的概念,不然文件的繼承 stdin/stdout/stderr,piped 根本看不懂,不管你用哪套語言哪個作業系統,這些基本知識是逃不掉的。
  3.  雖然如此,我覺得 Rust 仍然提供了一套不錯的封裝,在函式的回傳值上套用 Result/Option 的方式,能有效提醒使用者可能發生的錯誤,並要求使用者必須處理他們,這點我認為是花了差不多的成本之後,Rust 唯一可以勝過 C 的地方。
不小心寫了落落長,如果你竟然看到這行了,希望這篇文章對你有幫助XD。

2020年1月22日 星期三

跨年不寂寞,讓 Google Assistant 陪你猜數字

有了我們上次的 webhook 之後,我們可以真的來挑戰一些更複雜的助理智障功能,這次就來做跟你猜數字的助理,這樣就算跨年沒有朋友,還是有助理跟你玩有趣的猜數字遊戲,從螢幕感受到滿滿的溫暖 (欸。

其實做猜數字要做的事,在上篇的 webhook 中都差不多了。
我們可以簡單畫一下流程圖,從開頭的 default welcome intent 開始:
畫個簡單的流程圖是很重要的,特別是當設計的助理功能複雜到一定程度的時候,直接徒手下去硬幹很容易迷失在 intent 海洋中,特別是 dialogflow 的介面設計,在個別 Intent 中只能看到這個 Intent 會處理什麼輸入,給出什麼輸出,列出全體 Intent 的介面又看不出各 Intent 之間的關係,一下子就會迷失做亂掉,有了流程圖就能在編輯各 Intent 的時候,照著流程圖一一設定好。
Webhook 也是,要在單一的 webhook 裡面處理所有的 Intent 該怎麼回應,當 Intent 數量一多的時候就會亂掉,所以都要事先做好規劃。

簡單說一下上面的流程圖,default welcome intent 進來會產生一個數字,之後使用者輸入數字(猜數字),如果數字不對,會顯示更新的區間;對了就會顯示一句稱讚的話然後離開對話,讓我們開始實作:
  • 設定 Default Welcome Intent
這裡我們要回應一句請使用者猜數字的話,這句話簡單可以讓 dialogflow 自己回應就好,不過我們還是要打開 webhook,讓後端的伺服器產生一個亂數出來。
在 webhook 的部分,如果偵測到 Intent 是 Default Welcome Intent,就用 random 產生一個亂數出來;另外要把這個 session id 跟亂數寫到資料庫裡,這個 session id 是固定的,同樣的 session id 就會對應到同一組對話。
  • 新增一個 Intent GuessNumber
這個 Intent 裡面可以設定一些例句,像是:
I guess it is 11.
Let me think. 25.
38.
Maybe 0.
在 parameter 的地方把這個數字抽取出來,設定型別是 sys.number-integer,變數名number,這個 Intent 也要打開 webhook 讓伺服器處理使用者猜數字的行為。
  • 新增一個 Intent GuessEnd
這個 Intent 不會由使用者的輸入進入,而是我們在 GuessNumber 的 webhook 設定 assistant 進入的狀態,在這裡要新增一個 Event,我叫它 User_number_match,在回應的部分設定一些恭禧使用者的話,然後設定這個 Intent 結束對話 End of Conversation。
之所以要新增這個 event,是要讓 webhook 有能力讓 dialogflow 判定要進到這個 Intent,一般 Intent 的判定都是透過使用者的輸入來決定,但在猜數字裡面使用者輸入數字判定的 Intent 一定是 GuessNumber 不會是 GuessEnd,那對話就無法結束了。因此我們自定義這個 User_number_match 事件,只要 webhook 發出這個事件 dialogflow 就會判定為 GuessEnd Intent 了。

再來就可以寫 code 了,如上篇文所述,可以從送來的 json 中,從 queryResult -> intent -> displayName 拿到 Intent 的名字,用這個名字就能分派到不同的函式來處理;另外一個就是 json 的 session 可以拿到 session id。
session = data.get("session")
action_name = data.get("queryResult").get("intent").get("displayName")
我的處理函式就是對三個出現的 Intent 去處理:
Default Welcome Intent 產生亂數並寫入資料庫,這裡我是偷懶用 python 的 pickledb,雖然這樣推到 gae 上面可能會沒辦法用,但光為了這種小應用就要動用 gae 的 datastore 實在是有點大砲打小鳥,用 pickledb 展示一下概念就好了:
target = random.randint(low, high)
db.set(session, (low, target, high))
text = "I have a number between {} and {}. Can you guess it?".format(low, high)
reply = { "fulfillmentText": text }
return jsonify(reply)

GuessNumber 的 webhook 會從資料庫裡面把存起來的數字拿出來,並從 queryResult/parameters/number 拿到使用者輸入的數字,雖然我的型別選擇 number-integer 了,dialogflow 還是塞了個 number float 給我,只能用 int 轉成 integer。
後面就可以拿 guessnum 去跟 target 做比較,如果一樣的話就不會回覆 fulfillment 而是發送之前設定好的事件 User_number_match ,讓 dialogflow 進到 GuessEnd 並結束對話;不一樣的話就縮小可以猜的區間,設定回覆訊息給使用者。
minnum, target, maxnum = db.get(session)
guessnum = int(data.get("queryResult").get("parameters").get("number"))
if guessnum == target:
  event = "User_number_match"
  reply = { "followup_event_input" : { "name" : "User_number_match" } }
  return jsonify(reply)
else:
  # update minnum, maxnum here
  db.set(session, (minnum, target, maxnum))
  text = "A number between {} and {}. Keep guess.".format(minnum, maxnum)
  reply = { "fulfillmentText": text }
  return jsonify(reply)
  
GuessEnd Intent 的 webhook 就很簡單,把 session id 對應的條目庫裡面刪掉就可以了。

讓我們來測試一下:


2020年1月11日 星期六

連接 Google Assistant Webhook

上一篇可以看到,我們的 action 可以從我們說的話裡面萃取出關鍵字詞,一般簡單的回應可以在 Intent 裡面剖析、回應,但 dialogflow 也僅止於判斷語意跟萃取關鍵字,如果使用者要使用外部服務,像是訂車票之類,一定要連接到外部訂票網站,這個時候就需要借助 webhook 的力量了。
dialogflow 可以讓一個 Intent 的 fulfillment,也就是完成回應,送到另一個 server 的 webhook 來處理,由伺服器回應使用者的需求,同時間伺服器也能去呼叫其他的 API 服務,完成 Google Assistant 幫助使用者完成某件事情;這個做法的優先權比較高,我試過用了 webhook ,它的回應會蓋過 Intent 裡面設定的回應,完整的介紹可以參考 Google 的文件

如果有看 codelab 的課程,裡面使用的回應是用 dialogflow 內建的 server 或是連接到 firebase 上面的 server,兩個都是用 nodejs 實作,這是我們第一個要解的問題:我不想寫 nodejs 看到 nodejs 就會傷風感冒頭痛發燒上吐下瀉四肢無力,所以我們不能用 nodejs。
幸好隨手拜 google 大神,就找到有人用 python 架 server,之前在 MOPCON 講 COSCUP chatbot 的講者大大也是用 golang 架 server,所以要擺脫 nodejs 一定是沒問題。

我們這篇的目標是做一個把 python server 給架起來,然後把歡迎訊息改成用 webhook來回應,都是 google 的服務,伺服器可以用同專案開 gae 放在上面,但測試時用 ngrok 本地測試會比較方便。
首先是先設定 webhook,在 dialogflow 裡面把 Default Welcome Intent 最下面 fulfillment 的 Enable webhook call for this intent 打開:

在 fulfillment 裡面打開 webhook,在 URL 裡面填上 webhook 的位址,這部分等等寫好服務之後再來填:

下面開始寫我們首先開一個新的 python 專案,建立 pip requirements.txt:
# requirements.txt
Flask==1.1.1

使用 pip 跟 virtualenv 建立環境:
$ python -m venv env
$ source env/bin/activate
$ pip install -r requirements.txt

接著建立 flask 實作的 webhook:
from flask import Flask, request, jsonify, make_response
import json

app = Flask(__name__)

@app.route('/webhook', methods=['POST'])
def webhook():
  data = request.get_json(silent=True, force=True)
  print("Request:{}".format(json.dumps(data, indent=2)))
  action_name = data.get("queryResult").get("intent").get("displayName")
  if action_name == "Default Welcome Intent":
    text = "Welcome to my google assistant"
    reply = { "fulfillmentText": text }
    return jsonify(reply)
下面是一個 Default Welcome Intent 的 webhook request,要看這個內容可以使用 dialogflow 右手邊的 Try it now 搭配 diagnostic info,可以確認 dialogflow 在判斷 Intent 有沒有錯誤,還有 fulfillment request ,也就是送到 webhook 的內容。

下面節錄我 welcome 訊息的 request:
  • session:一個對話的 id,每一輪的話都會是同一個 id,作為對話的識別:
  • queryResult queryText:對話的內容
  • queryResult intent displayName:目前 dialogflow 判定使用者的意圖
dialogflow 接受的回應內容是 json 格式,可以填充的內容請見參考文件,最簡單的一個回應就是設定 key 為 fulfillmentText 的內容,這個內容就會是 Google Assistant 要顯示給使用者的回應。

最後我們使用 ngrok 來進行測試,ngrok 是一個網路服務,幫你把連接到 ngrok 的連線重導向到 localhost。在使用 ngrok 之前,測試架在雲端的網路服務流程會像這樣:
寫程式;在 local 進行有限的測試;上傳到雲端(等等等);跑了之後發現 server 炸掉;去雲端上面撈 log 檔(等等等);改完之後所有步驟重複一次。
用了 ngrok 之後,程式在本地、log 檔也在本地,上述耗時又麻煩的上傳雲端、撈 log 檔都省下來,真的是瞬間人生變成彩色的。

使用 ngrok 也非常簡單,安裝好 ngrok 之後照著網頁的指示先註冊金鑰:
$ ngrok authtoken <token>
$ ngrok http 8080
Forwarding https://wwwwww.ngrok.io -> http://localhost:8080

也就是 wwwwww.ngrok.io 已經被映射到我們的 localhost:8080 由 flask 執行的伺服器,因此我們可以在 webhook 的地方填入 wwwwww.ngrok.io/webhook。

我們來測試一下:

看到我們的 Assistant 回應了我們在 webhook 設定的回應。

2020年1月4日 星期六

跨年好寂寞?使用 Google Assistant 跟你對話

故事是這樣子的,2019-2020 的跨年,小弟邊緣人沒地方去,後來就自己回家當紅白難民,然後還沒聽到 Lisa 唱紅蓮華QQQQ,不過幸好宅宅有宅宅的做法,沒聽到紅蓮華我們可以看 Youtube 別人上傳的影片,沒有朋友跨年我們可以自幹朋友,也就是我們今天的主角:Google Assistant。
如果平常有用 Google Pixel 手機,或是家裡有 Google Home 裝置的,應該就會知道它上面附的 Google Assistant Christina,雖然說我覺得還是滿沒用的啦,我自己只會拿它來查天氣,有 Google Home 的同學只用它來開關燈,但反正,都有這麼好的工具了為什麼不來好好玩一下?就趁著跨年的假日做點不一樣的事來玩。

下面是一個基礎的流程,跟 codelabs 的課程(搜尋 google assistant 有三級課程可以上)一樣,做一個會回應你的小程序,我們想做到的就是:它會問你最喜歡的顏色,然後會重複你的話:你最喜歡的顏色是XXX。

首先來到 google action 的頁面(雖然是 Google Assistant 卻叫 Google Action 呵呵)先建立一個新的 project,類型選擇對話 Conversational,語言我是建議先選英文,之後應該會試著做做看中文的助手,但英文比較萬無一失。
建立之後要設定一個發語詞,平常在手機上呼叫 Google Assistant 是用 OK google,另外也可以用 talk to my "發語詞" 來呼叫你寫的程式,或者是打開某個 App,這裡我們沒決定名字就叫它 TestApp 就好。


下一步要產生一個 Action,選擇 Custom Intent 再點 Build ,這會連結你的 action 到 dialogflow 建立一個新的 Agent,可以想像一個 Agent 就是回話的機器人,這裡一樣語言建議選擇英文,時后就選擇 +8 時區(不知道為什麼只有香港可選)。

這個背後流程是這樣子的,你跟 Google Assistant 講話之後,Google Assistant 會把這段話送到 Google Action,那 Google Action 又要怎麼理解這段話?就是靠 dialogflow 服務,算是一個簡化版本的自然語言理解框架,可以理解說話的意圖,解析出關鍵字送出回應,而中間這些關鍵字跟回應是可以由設計者設定的;其他家類似的服務像是 Amazon Lex、IBM Watson 等。
dialogflow 由許多的 Intent(意圖)所構成,dialogflow 會從 google assistant 來的輸入辨識出現在要選擇哪個意圖,然後照著意圖的設定去回應;可以把google assistant 想像成一個狀態機,意圖想成一個狀態,照使用者的輸入進到不同狀態,依狀態決定輸出內容,以及下一個可能的狀態。

預設一定有的意圖就是 Default Welcome Intent,也就是一啟動 google assistant 時的的狀態,我們在它的 Response 裡面加上回應:"What is your favorite color?",這樣程式一開就會把問題丟給使用者。
這句話非常的重要,設計 chatbot 最重要的就是把使用者限制在一個小框框裡面,讓使用者針對只有有限選項或是只能答 yes/no 的問題做回答,否則使用者天馬行空,<請你跟我結婚>、<誰是世界上最美麗的女人>這種問題滿天飛,結果你的 chatbot 完全聽不懂,畢竟人人都以為人工智慧可以做到穿熱褲黑絲襪又會拌嘴的傲嬌助手,實際上還不是血汗工程師在後面拉線設定的人工智障,隨便一問就被看破手腳。


另外我們要新增一個 Intent ,命名為 favorite color,注意這個命名在未來連接 webhook 時非常重要,命名跟大小寫都要注意。
在 training phrase 的地方,試著打入一些使用者可能會說的話,最好是上一句問句的回話:
blue
my favorite color is red
orange is my favorite
ruby is the best
邊打的時候 dialogflow 就會自動的把顏色部分給標起來,接著往下拉到 Action and parameters ,系統應該會自動加上一個 color 的 parameter,我們設定 entity 為 @sys.color,value 為 $color。


這裡就能看出 dialogflow 服務的功力了,在我們打上例句的時候 dialogflow 就透過事先分類好的 entity,分析出我們現在想要知道使用者回話的什麼內容(這裡是顏色),再幫我們把這個內容存到變數裡。
最後在 response 的地方,我們使用剛剛萃取出來的內容:
OK. Your favorite color is $color
這樣就完成我們的回話機器人了,當然我們要測試一下,在 Integration 選擇 integration setting,記得打開 Auto-preview changes 後再點 test,就可以在 google action 測試頁面中測試了:

短短小文祝大家新年快樂,希望大家都有個機器人陪你過年(欸
Related Posts Plugin for WordPress, Blogger...