みなさま、おはようございます。
人生初のコミュニティのアドベントカレンダー(= 会社のじゃないやつ)でドギマギしている toku345 です。
この記事は Clojure Advent Calendar 2017 の 11日目の記事です。
さて今回は、近頃盛り上がりを見せている Amazon Echo と好きなように会話するために ClojureScript (以降cljs) で Alexa Skill を書いて、公開するまでをご紹介します。
どこからか「素のJSで書いたほうが楽だよー」みたいな声も聞こえて来そうですが、完全に私の趣味なので悪しからず。。。
では、始まり始まり〜
何を作ったの?
せっかくcljsで書くのだから、cljsに関係するものがいいなということで...
問いかけると最新のcljsのバージョンを教えてくれるスキルを作ってみました。
ClojureScriptで誰得?なスキルを公開してみた。
— Fumitaka Tokumitsu (@toku345) 2017年12月9日
こんなこともちゃんと答えてくれるようになるなんてAlexa可愛いな! pic.twitter.com/dG11gdvPB9
Alexa って何? Echoと何が違うの?
Amazon Alexa
クラスメソッドさんのこちらの記事の説明がすごくわかりやすく、引用させていただくと
Amazon Alexaとは、Amazonが提供する、クラウドベースの音声認識サービスです。Alexaに対応したデバイスが認識した音声はクラウドサービスに送信されます。クラウドサービスは音声をテキスト変換し、そのテキストを処理し、処理結果をデバイスに返して音声として再生されます。
Amazon Echo ≠ Alexa で、Echoは Amazon が販売している Alexa 対応ガジェットという位置づけみたいですね。
Alexa Skill
こちらも先程のクラスメソッドさんの記事から引用させていただくと、
Alexa Skillとは、Alexa対応デバイスから音声として送られてきて、Alexaが音声から変換したテキストに対する処理を実行するプログラムです。
Amazon自体、またはサードパーティが開発し公開しています。SkillはLambda Functionで実装されます。
Alexa Skill のエンドポイントとしては Lambda 以外にも、任意のHTTPSエンドポイントを設定できるみたいですが、今回はお手軽な Lambda を使って書くことにします。
なぜ Clojure じゃなくて ClojureScript を選んだの?
Lambda上でのClojure/cljsのパフォーマンスを比較したこちらサイトでの検証結果をみると
Lambda | RAM | First call | Once warm |
---|---|---|---|
Clojure | 256MB | 8900ms | 1500ms |
ClojureScript | 128MB | 2300ms | 1600ms |
(※ 上記サイトからの引用)
Clojure(バックエンドはJVM)だと初動がものすごく遅いため、そんなに頻繁に使われないような Skill には向いてなさそうだと判断し ClojureScript で書くことを決めました。1
Alexa Skill を書き始める前にハマったこと
Alexa Skill Kit というSDKが公式に Node.js/Java向けに公開されているのでこれを使いたかったのですが、npmパッケージを cljs から呼び出すためにちょっとハマっちゃいました...
詳しくはこちらの私の記事を参照ください。
ClojureScript内からnpmモジュールを呼び出す(npm-deps)
自作Alexa SkillをEcho実機で喋ってもらうまで
自作Alexa SkillをEcho実機で喋ってもらうまでの大まかな流れは以下のとおりです。
前準備
Alexa Skillの公開、並びに実機でのテストにはAmazon Developerアカウントが必要となります。
※ AWSアカウントとは違うので注意!
Amazon Developerアカウント作成は こちらの記事のステップ1を、AWSアカウント作成は こちらの記事で詳しく説明されていますので、ここでは割愛します。
スキル開発〜登録
- Skillの作成
- SkillのバックエンドとなるLambda関数を準備
- Skill に Lambda関数を連携させる
- サービスシュミレーターで動作確認
- その他設定を入力
- テスター設定
1. Skillの作成
Amazon Developerコンソールにサインインし、「Alexa」メニュー(①)をクリック。
画面遷移後、Alexa Skill Kitの「始める」ボタン(②)をクリック。
スキル情報
以下のように設定する。
項目 | 設定値 | 備考 |
---|---|---|
スキルの種類 | カスタム対話モデル | - |
言語 | Japanese | - |
スキル名 | cljsバージョン確認 | 任意の値でOK |
呼び出し名 | バージョン確認 | Echoに呼びかける際にキーとなるので注意! |
※ グローバルフィールドはそのままでOK
設定後、「保存」ボタンをクリック。
保存が終わると「次へ」ボタンが現れるのでクリック。
対話モデル
こちらは以下のように設定する。
{
"intents": [
{
"intent": "CljsVersionIntent"
},
{
"intent": "AMAZON.HelpIntent"
},
{
"intent": "AMAZON.StopIntent"
},
{
"intent": "AMAZON.CancelIntent"
}
]
}
※ カスタムスロットタイプ関係は空欄のままでOK
CljsVersionIntent シーエルジェーエス の 最新のバージョン を 教えて
CljsVersionIntent クロージャースクリプト の 最新のバージョン を 教えて
CljsVersionIntent cljs の 最新のバージョン を 教えて
CljsVersionIntent ClojureScript の 最新のバージョン を 教えて
CljsVersionIntent 最新バージョン を 教えて
CljsVersionIntent 最新バージョン を 調べて
CljsVersionIntent 最新バージョン は どう
CljsVersionIntent 最新バージョン は
CljsVersionIntent バージョンチェック して
インテントスキーマ、カスタムスロットタイプについてはAlexaスキル開発トレーニングがわかりやすいので、こちらの記事を参照ください...
設定後、「保存」→「次へ」ボタンをクリック。
次のページで Lambda関数のエンドポイントが必要になるので、一旦ここまででストップ。
2. SkillのバックエンドとなるLambda関数を準備
AWSコンソールにサインイン後、Lambdaのダッシュボードを開き「関数の作成」ボタンをクリック。
関数の作成
「設計図」を選択し(①)、検索欄に「Alexa」を入力しEnterを押す(②)。
検索結果から「alexa-skill-kit-sdk-factskill」を選択(③)。
関数の設定
項目 | 設定値 | 備考 |
---|---|---|
名前 | cljsVersionNotify | 任意の値でOK |
ロール | ![]() |
←を選択するとロール作成用の別ページが開くので「許可」をクリック |
まだこの段階では Lambda 関数のコードは変更できないので、右下の「関数の作成」ボタンをクリック。
トリガーの追加
Alexa からの呼び出しイベントに対応するために、トリガーを設定します。
左のトリガー 一覧から「Alexa Skills Kit」を選択。
アクセス許可を設定する必要があるので、トリガー設定にある「追加」ボタンをクリック。
さあ、これで Lambda関数に関する設定ができました。
次はサンプルコードを自作コードに置き換えましょう!
関数を書く
ここからが楽しい実装の時間ですね
今回、ターゲットはブラウザではなく Node.js なので、figwheel-node
テンプレートを使います。
$ lein new figwheel-node cljs-version-notify
project.clj の :cljsbuild -> :builds の "dev"
と "prod"
に :npm-deps と :install-deps オプションを追加。
diff --git a/project.clj b/project.clj
index 6ad593a..fda3880 100644
--- a/project.clj
+++ b/project.clj
@@ -25,6 +25,8 @@
:output-to "target/js/compiled/cljs_version_notify.js"
:output-dir "target/js/compiled/dev"
:target :nodejs
+ :npm-deps {:alexa-sdk "^1.0.23"}
+ :install-deps true
:optimizations :none
:source-map-timestamp true}}
{:id "prod"
@@ -33,6 +35,7 @@
:output-to "server.js"
:output-dir "target/js/compiled/prod"
:target :nodejs
+ :npm-deps {:alexa-sdk "^1.0.23"}
:optimizations :simple}}]}
:profiles {:dev {:dependencies [[figwheel-sidecar "0.5.13"]
src/cljs_version_notify/core.cljs に以下のように記述。
(ns cljs-version-notify.core
(:require [cljs.nodejs :as nodejs]))
(nodejs/enable-util-print!)
(defonce Alexa (nodejs/require "alexa-sdk"))
(def ^:const cljs-latest-version "1.9.946") ; TODO: 最新バージョンを動的に取得したい...
(def ^:const stop-message "クロージャースクリプトの最新バージョン番号が気になったら、また声をかけてくださいね。")
(def skill-handlers #js {"LaunchRequest" (fn [] (this-as this (.emit this "AMAZON.HelpIntent")))
"SessionEndedRequest" (fn [] (this-as this (.emit this "AMAZON.StopIntent")))
"AMAZON.HelpIntent" (fn [] (this-as this
(.emit this
":ask"
(str "クロージャースクリプトの最新バージョン番号をお知らせする非公式のスキルです。"
"クロージャースクリプトの最新バージョン番号をお知らせしましょうか?"))))
"CljsVersionIntent" (fn [] (this-as this
(let [message (str "最新のcljsのバージョンは" cljs-latest-version "です。")]
(.emit this ":tell" message)
(.log js/console (str "message: " message)))))
"AMAZON.CancelIntent" (fn [] (this-as this (.emit this "AMAZON.StopIntent")))
"AMAZON.StopIntent" (fn [] (this-as this (.emit this ":tell" stop-message)))})
(defn- handler [event context callback]
(let [alexa (.handler Alexa event context)]
(.registerHandlers alexa skill-handlers)
(.execute alexa)))
(set! (.-exports js/module) #js {:handler handler})
cljs → js
$ lein cljsbuild once prod
Compiling ClojureScript...
Compiling ["server.js"] from ["src"]...
Successfully compiled ["server.js"] in 25.799 seconds.
プロジェクトのルートディレクトリに server.js
という名で出力されます。
出力したJSコードをLambda関数として設定
zipにしてアップロードしても良いのですが、今回は手動で設定しましょう。
注意!
関数エディタが表示されていない場合は、関数名をクリックしてください。
トランスパイルしたJSコードを貼り付け(①)、エディタの変更を保存(②)
コード保存後、ページ右上の「保存」ボタンをクリック。
保存ボタンの上の ARN は次で使うので控えておいてください。
3. Skill に Lambda関数を連携させる
再び Amazon Developerコンソールを開きます。
設定
項目 | 設定値 | 備考 |
---|---|---|
サービスエンドポイントのタイプ | AWS Lambda の ARN | - |
デフォルト | (環境によって異なる) | Lambda関数設定時に確認したARN値 |
他の設定は「いいえ」、または未選択でOK。
設定後、「保存」→「次へ」ボタンをクリック。
4. サービスシュミレーターで動作確認
「発話を入力してください」欄に Skillを使う際に話しけたい文章を入力し、「cljsバージョン確認を呼び出す」ボタンをクリック。
右側の サービスレスポンス に 最新のcljsのバージョンは...
と言った文言が含まれていれば動作OK!
実機がある場合は「次へ」をクリック。
5. その他設定を入力
公開情報 と プライバシーとコンプライアンス をいい感じに入力します。
入力例
公開情報 | プライバシーとコンプライアンス |
---|---|
![]() |
![]() |
6. テスター設定
5 の設定が終わると 「☆スキルのベータテスト」ボタンが現れるので、お使いのEchoに紐付けたメールアドレス宛にテストリンクを送付できるようになります。
↓
これで実機でテストできるようになりました!
まとめ
長々とお付き合いありがとうございました。
ご覧の通りClojureScript でも簡単に Alexa Skill 書くことができました。
これを機にもっと ClojureScript で書かれた Skill が増えるといいなと思っています。
今回作った Skill は Alexa Skillストアに並んでいるのでぜひ使ってみてください!
コード全体をご覧になりたい方ははこちらをどうぞ
https://github.com/toku345/cljs-version-notify/tree/0.1.0
今回できなかったことリスト
ClojureScript のバージョンを動的に取得する
→ こちらは現在、実装中です...
AWS SAM を使ってデプロイする
→ こちらも実装中です...
Clojureでも実装してみる
Clojure / cljs で Lambda を本格的に使いたいときは k2nakamura さんのこちらスライド CLJ/CLJS向けSERVERLESSソリューションがめっちゃ役に立ちます!
-
もちろんClojureでもRAMを増やせば立ち上がりも早くなるみたいですが、クラウド破産が怖かったので... ↩