Skip to content

讓 Alexa 懂你 – 用 Alexa Skill Kit 製作客製化技能

做任何事情做久了,總難免會有些職業病跑出來。

我記得十年前我每次進去到 KTV 的第一件事情就是亂按他們的點歌系統,想看看怎樣可以讓它當掉...以便好好嘲笑一番,也安慰自己,世界上不是只有我寫的程式會當機...

幾個月前跟朋友的LINE群組有人拉進來一個機器人,會貼美女圖給大家看。只不過這個全部都中年男子的聊天室,需要一些更重口味的照片和影片,於是自己寫了一個LINE BOT,三點全露、葷腥不忌...

這次上網買了 Amazon Echo,然後在還沒收到機器之前,職業病發作,手癢了起來,於是開始看他的SDK的範例 --- 然後,在我的 Amazon Echo 還沒啟用之前,我的自製 Skill 已經提交送審了...

切入正題。

我上網看了一小段 Alexa Skill Kit 的說明以後,了解到要做一個 Alexa Skill,只要準備下面兩樣東西:

  1. 去 https://developer.amazon.com/alexa 註冊一個開發者帳號。然後建立一個自訂的 Skill。
  2. 一個能傳回 Alexa 指定格式的 API endpoint。

就這樣就夠了。當然,項目 2. 的endpoint,必然是背後的運算後的結果。而既然這個是Amazon 的服務,當然使用 AWS Lambda 當作 endpoint 是最無痛的啦。所以,很快的,我的目標就是做一個 "簡單的 AWS Lambda function",設定給在開發者帳號裡面設定的 Alexa Skill 就可以了,看起來很簡單啊? 有什麼理由不動手試試看呢?

接著,想著要拿什麼來開刀當 Lambda function。於是想到的是要 "讓 Alexa 回答 GeoNet 的 API 的第一筆資料" --- 基本上,GeoNet 有一個 API,呼叫以後會傳回 JSON格式 的紐西蘭的最近100則有感地震。所以目標是取出第一則 (API 傳回的是倒序),然後把字串格式成口語文字,送回給Alexa請他唸出來。

用這種 live 的 public API 當練習的好處是,你的 Lambda 將會非常簡單,也不需要準備儲存,拿來練功別人也不會抱怨。其實整個來說,我所做的事情也就是把拉拉雜雜的 live response,縮減為 "簡短到適合讓 Alexa 念出來",就這樣而已。這整個製作 Alexa Skill 的過程加上摸索的鬼打牆,總共花費的時間大概四個小時左右而已

建立Alexa Skill

首先前往網頁 https://developer.amazon.com/alexa,註冊好開發者帳號以後 (如果已經有 AWS 帳號那就直接開通 Alexa Skill Kit),就可以開始 "Start a Skill" 了。

先幫 Skill 取個好名字 --- 這個將來會顯示在 Alexa Skill (就像 AppStore 或 PlayStore) 讓人下載的時候看的。

然後選擇 Skill 的模板,這邊選自定 Custom。

進入到主要的控制畫面了。接著先幫自己的 Skill 設定一個"口令" (這裡被稱為invocation)。比如說,我未來想要這樣啟動我的 Skill:

"Alexa, ask GeoNet ..... ",那麼這個口令就是 GeoNet。

但是由於 Alexa 會需要做語句分析,所以:

  1. 只管讀音,所以全部小寫,
  2. 如果不是普通單字,那麼你要把它拆成音節輸入會比較好。

在我的例子裡面,原本的 GeoNet 變成 geonet,但是怕 Alexa  "聽不好" (注意,這裡是 Alexa 要從你的語音試圖認出你的口令,比對存在他機器裡面的各種口令,看到底是要叫哪一個 Skill 起來),所以乾脆把他拆開,變成 "geo net"。

再來輸入你的命令 (這裡用的是 intent - "意圖")。同樣一個口令,但是你可以要你的 Skill 做不同的事情,此時不同的事情就是不同的 intent。

我們先建立一個 intent:

幫 intent 取個名字。這個名字未來你的客戶並不會看到,所以方便管理就行。

再來就是輸入命令的語句(這裡用 utterances)了。這個地方就把所有你想要啟動這個intent 的語句通通輸入進去:

從上面的輸入,我未來的整句話是像這樣:

  • Alexa, ask GeoNet the latest earthquake
  • Alexa, ask GeoNet the latest quake
  • Alexa, ask GeoNet recent earthquake
  • Alexa, ask GeoNet recent quake

(當然,對 Alexa 來說,我念的是 "geo net")。

好了以後,來到 endpoint 的設定的地方。

Alexa 的 endpoint 可以是自訂的 HTTPS web service,也可以是 AWS Lambda function。我們在這裡先停下來,把這個 Skill 先存起來放一下。現在要先去做我們的endpoint,然後再回到這邊。

建立Lambda function (使用Go 1.x)

最前面已經提過為什麼我要使用 Lambda function 而不使用自訂的 HTTPS 服務,所以這邊就不再重複了。

注意: Alexa Kit 目前只支援使用美東一區 (N.Virginia) 的 Lambda function,如果你把你的 Lambda function 建立在別的 region,後面設定 Alexa 的 endpoint 時會出現問題。因此,請確定你的 region 是選對了。

我用的是 Go,基本上現有的 Alexa 相關的藍圖,都是以 Node.js 為基礎的 (所以意思是說如果你用的是 Node.js,那麼做起來更快),所以就只能 "Author from scratch"。

如上圖,Role 就讓他新建一個就是了。權限的設定,我的 service 簡單到不行,所以用Simple Microservice permissions 就好。

接著來到 Lambda function 的主要設定畫面。

除了要把 Handler 改為 main 以外,幾乎沒有什麼東西需要設定的。我的是 GO 程式,因此就是把程式編譯成 linux/amd64 的 binary 然後 zip 壓縮 (無目錄),傳上來就好。

Alexa 的 endpoint,有特定的 response,請參閱這裡

整個data flow是:

使用者的語音,經過分析以後,組合成一個 request JSON,送到你的 endpoint,然後你的 endpoint 做完處理以後,組成一個 response JSON,送回給 Alexa。

就這麼單純。

其實整個 request 的結構頗為複雜,裡面會包含哪個 intent、使用者的 utterance 等等,方便你的 service 進行處理。不過在我們這個練習裡面,根本不用理會使用者的輸入(你看這個練習多好),只要這個 Skill 被觸發,我們就是傻傻的去扒 GeoNet API 然後傳回第一筆,不管使用者多說了什麼。

整個 service 的 source code 在這裡。其實事情很單純,就是把上面那段描述data flow的文字轉換成程式寫出來罷了。唯一一個我特別處理的地方是這個,GeoNet API 傳回的整個地震資訊的範例是像這樣,而其中的單一一筆地震內容是:

這樣的 JSON 對於寫程式來說簡單明瞭,但是要用念的大概是不太可能,所以我們要把這樣的內容重新整理成為一句話,讓 Alexa 唸出來。

因此這段

t, err := time.Parse(time.RFC3339, p.Time)
if err != nil {
return Response{}, err
}

ts := t.Format("Monday 2 January 2006, 3 4 PM")

return NewResponse(fmt.Sprintf("The latest earthquake was a magnitude %0.1f earthquake near %s at %s", p.Magnitude, p.Locality, ts)), nil

我們把 JSON 的日期

2018-06-01T02:03:03.119Z

轉換成為

Friday 1 June 2018, 2 3 AM

讓 Alexa 照著念,同時把浮點數只留下一個位數,再把整個結構順成ㄧ句話

"The latest earthquake was  a magnitude 2.6 earthquake near 10 km south of Waipukurau at Firday 1 June 2018, 2 3 AM"

然後把這句話放入 response 結構的 OutputSpeech 內的 Text 就好了。

把 GO 程式碼編譯為 linux,amd64 的 binary 以後,壓縮傳回到剛剛的 Lambda function建立介面中。接著我們來測試一下。按下畫面上方的 Test,會出現建立 test case 的畫面。

但是由於我們的這個 Lambda function 根本不在乎輸入的是什麼,所以直接按下 Create就好了。建立好 Test 以後,在畫面上方的下拉選單選擇新建的 test case,按下 Test,出現執行結果:

上面的結果中,傳回的就是符合 Alexa response 格式的 JSON,然後裡面的 text 跟我們預期的相同,一切都對了。

接著,你就可以把你的 Lambda function 儲存並且從 Action 選 Publish new version,進行發佈了。

然後,要把這個 Lambda function 跟 Alexa Skill 掛鉤。

在版號:1的 Lambda function 畫面中(注意: 不能用 LATEST VERSION),有這個東西要選一下:

左邊的 Add triggers 要選 Alexa Skill Kit,然後在底下 Configure triggers 的地方

需要你的 Skill ID。(你也可以把這個限制 Disable,這樣就是任何東西都可以來觸發你的Lambda function。為了安全起見,還是設定對來吧)

我們回到 Alexa Skill Kit 的 endpoint 畫面

你選的如果是 AWS Lambda ARN的話,底下就會出現你的 SKill ID,把他複製過來,貼回到上面的 Lambda 畫面裡的 Configure triggers 裡面就好了。

接著,你在 Lambda 畫面最上方會有你的 Lambda function 的 ARN,把他複製以後,貼回到 Alexa Skill 的 endpoint 裡面(注意: 你要複製的 ARN 必須是含有版號,例如最後面有個:1)

另外,你至少要貼到 Default 和 North America 這兩個 endpoint 上面。

現在可以存起來了。如果你的 Lambda 設定有錯的話,你按下上面的 Save Endpoint 會告訴你不對,請再回去Lambda function那邊檢查看哪裡有錯。

到了這邊,你的Alexa Skill 已經完成了。我們再回到 Invocation 頁籤中,最上面有個 Build Model 按下去,讓系統開始建立Skill。

等系統把你的 Skill 建立好以後,你可以在網頁頂部的 tab 中,按下 Test,切換到測試畫面中。你可以在要輸入對話的地方輸入你要此用的字 --- 你可以輸入完整的對話,像是前面寫的稍早的完整範例,或者是按下畫面上的那個麥克風符號進行測試:

(用滑鼠點在麥克風上按著不要放,然後講完話以後再放開)

如果沒問題的話,你會看到他的回答

測試無誤的話,要先退回到前面的Build,然後再左上角的語言那邊選一下 Languauge Settings

英文到哪邊都是英文,所以如果你的是英文的話,通通選上去

儲存以後,再回到主畫面,這裡接著有個囉唆事情要做。選擇剛剛新加入的語言:

然後你會發現那些 Invocation 和 Intents 都空了,所以要每個語言逐一重新把語句設定進來...

辛苦完畢了,看樣子沒問題的話,再來就去選上面的 Launch 做提交。這邊填的東西基本上就像是 AppStore 或是 GooglePlay 裡面提交 app 所要填的東西了,以及做個漂亮的icon,這邊就不多說了。

大約兩天後,接到審核完畢的通知,然後去網站上面搜索:

耶~~~完成

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *

This site uses Akismet to reduce spam. Learn how your comment data is processed.