LoginSignup
11
4

ClojureScriptでAlexaスキルを書いてみた

Last updated at Posted at 2017-12-10

みなさま、おはようございます。
人生初のコミュニティのアドベントカレンダー(= 会社のじゃないやつ)でドギマギしている toku345 です。

この記事は Clojure Advent Calendar 2017 の 11 日目の記事です。

さて今回は、近頃盛り上がりを見せている Amazon Echo と好きなように会話するために ClojureScript (以降 cljs) で Alexa Skill を書いて、公開するまでをご紹介します。

どこからか「素の JS で書いたほうが楽だよー」みたいな声も聞こえて来そうですが、完全に私の趣味なので悪しからず。。。

では、始まり始まり〜

何を作ったの?

せっかく cljs で書くのだから、cljs に関係するものがいいなということで...
問いかけると最新の cljs のバージョンを教えてくれるスキルを作ってみました。

Alexa って何? Echo と何が違うの?

Amazon Alexa

クラスメソッドさんのこちらの記事の説明がすごくわかりやすく、引用させていただくと

Amazon Alexa とは、Amazon が提供する、クラウドベースの音声認識サービスです。Alexa に対応したデバイスが認識した音声はクラウドサービスに送信されます。クラウドサービスは音声をテキスト変換し、そのテキストを処理し、処理結果をデバイスに返して音声として再生されます。

Alexaイメージ図

Amazon Echo ≠ Alexa で、Echo は Amazon が販売している Alexa 対応ガジェットという位置づけみたいですね。

Alexa Skill

こちらも先程のクラスメソッドさんの記事から引用させていただくと、

Alexa Skill とは、Alexa 対応デバイスから音声として送られてきて、Alexa が音声から変換したテキストに対する処理を実行するプログラムです。
Amazon 自体、またはサードパーティが開発し公開しています。Skill は Lambda Function で実装されます。

AlexaSkillイメージ図

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 実機で喋ってもらうまでの大まかな流れは以下のとおりです。

前準備

  1. Amazon Developer アカウント作成
  2. AWS アカウント作成

Alexa Skill の公開、並びに実機でのテストには Amazon Developer アカウントが必要となります。
※ AWS アカウントとは違うので注意!

Amazon Developer アカウント作成は こちらの記事のステップ1を、AWS アカウント作成は こちらの記事で詳しく説明されていますので、ここでは割愛します。

スキル開発〜登録

  1. Skill の作成
  2. Skill のバックエンドとなる Lambda 関数を準備
  3. Skill に Lambda 関数を連携させる
  4. サービスシュミレーターで動作確認
  5. その他設定を入力
  6. テスター設定

1. Skill の作成

Amazon Developer コンソールにサインインし、「Alexa」メニュー(①)をクリック。
画面遷移後、Alexa Skill Kit の「始める」ボタン(②)をクリック。
アマゾン_アプリ_開発者ポータル.png

右上の「新しいスキルを追加する」ボタン(③)をクリック。
アマゾン_アプリ_開発者ポータル.png

スキル情報

以下のように設定する。

Pasted_Image_2017_12_10_16_37.png

項目 設定値 備考
スキルの種類 カスタム対話モデル -
言語 Japanese -
スキル名 cljs バージョン確認 任意の値で OK
呼び出し名 バージョン確認 Echo に呼びかける際にキーとなるので注意!

※ グローバルフィールドはそのままで OK

設定後、「保存」ボタンをクリック。
保存が終わると「次へ」ボタンが現れるのでクリック。

対話モデル

こちらは以下のように設定する。

Pasted_Image_2017_12_10_17_03.png

インテントスキーマ
{
  "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 のダッシュボードを開き「関数の作成」ボタンをクリック。
Lambda_Management_Console_and_1__rlwrap___Users_toku345_works_cljs_node-apps_hello-aws-cli__java_.png

関数の作成

「設計図」を選択し(①)、検索欄に「Alexa」を入力し Enter を押す(②)。
検索結果から「alexa-skill-kit-sdk-factskill」を選択(③)。
Lambda_Management_Console.png

関数の設定

Lambda_Management_Console.png

項目 設定値 備考
名前 cljsVersionNotify 任意の値で OK
ロール image.png ← を選択するとロール作成用の別ページが開くので「許可」をクリック

まだこの段階では Lambda 関数のコードは変更できないので、右下の「関数の作成」ボタンをクリック。

トリガーの追加

Alexa からの呼び出しイベントに対応するために、トリガーを設定します。

左のトリガー 一覧から「Alexa Skills Kit」を選択。
image.png

アクセス許可を設定する必要があるので、トリガー設定にある「追加」ボタンをクリック。

image.png

さあ、これで Lambda 関数に関する設定ができました。
次はサンプルコードを自作コードに置き換えましょう!

関数を書く

ここからが楽しい実装の時間ですね

今回、ターゲットはブラウザではなく Node.js なので、figwheel-node テンプレートを使います。

$ lein new figwheel-node cljs-version-notify

project.clj の :cljsbuild -> :builds の "dev""prod" に :npm-deps と :install-deps オプションを追加。

project.clj
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 に以下のように記述。

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 にしてアップロードしても良いのですが、今回は手動で設定しましょう。

注意!
関数エディタが表示されていない場合は、関数名をクリックしてください。
image.png

トランスパイルした JS コードを貼り付け(①)、エディタの変更を保存(②)
Lambda_Management_Console.png

コード保存後、ページ右上の「保存」ボタンをクリック。
保存ボタンの上の ARN は次で使うので控えておいてください。
Lambda_Management_Console.png

3. Skill に Lambda 関数を連携させる

再び Amazon Developer コンソールを開きます。

設定

image.png

項目 設定値 備考
サービスエンドポイントのタイプ AWS Lambda の ARN -
デフォルト (環境によって異なる) Lambda 関数設定時に確認した ARN 値

他の設定は「いいえ」、または未選択で OK。
設定後、「保存」→「次へ」ボタンをクリック。

4. サービスシュミレーターで動作確認

「発話を入力してください」欄に Skill を使う際に話しけたい文章を入力し、「cljs バージョン確認を呼び出す」ボタンをクリック。
右側の サービスレスポンス最新のcljsのバージョンは... と言った文言が含まれていれば動作 OK!
Pasted_Image_2017_12_10_21_06.png

実機がある場合は「次へ」をクリック。

5. その他設定を入力

公開情報プライバシーとコンプライアンス をいい感じに入力します。

入力例

公開情報 プライバシーとコンプライアンス
Pasted_Image_2017_12_10_21_19.png Pasted_Image_2017_12_10_21_20.png

6. テスター設定

5 の設定が終わると 「☆ スキルのベータテスト」ボタンが現れるので、お使いの Echo に紐付けたメールアドレス宛にテストリンクを送付できるようになります。
アマゾン_アプリ_開発者ポータル.png

Skills_Beta_Testing_for_Alexa.png

これで実機でテストできるようになりました!

まとめ

長々とお付き合いありがとうございました。
ご覧の通り ClojureScript でも簡単に Alexa Skill 書くことができました。

これを機にもっと ClojureScript で書かれた Skill が増えるといいなと思っています。

今回作った Skill は Alexa Skill ストアに並んでいるのでぜひ使ってみてください!

Amazon_Alexa.png

コード全体をご覧になりたい方ははこちらをどうぞ
https://github.com/toku345/cljs-version-notify/tree/0.1.0


今回できなかったことリスト

ClojureScript のバージョンを動的に取得する

→ こちらは現在、実装中です...

AWS SAM を使ってデプロイする

→ こちらも実装中です...

Clojure でも実装してみる

Clojure / cljs で Lambda を本格的に使いたいときは k2nakamura さんのこちらスライド CLJ/CLJS 向け SERVERLESS ソリューションがめっちゃ役に立ちます!

  1. もちろん Clojure でも RAM を増やせば立ち上がりも早くなるみたいですが、クラウド破産が怖かったので...

11
4
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
4