Alexa アプリ(スキル)開発効率化メモ:ローカル開発/継続的インテグレーション/多言語対応
担当している AbemaTV が Amazon Alexa に対応しました。今回 Alexa スキル(Alexa に機能を追加するためのアプリをスキルと呼びます)の開発にあたって、課題感があったチームによる平行開発、継続的インテグレーションおよびデプロイ、多言語対応についてのメモを残したいと思います。
Web 技術を使って開発できるけど動作確認が大変
最近は Web で使われてきた技術がさまざまなデバイス用のプラットフォームでも利用できるようになり、Web エンジニアがこういった PC やスマートフォン以外のデバイス向けのアプリを開発することも多くなってきました。たとえば Amazon Echo などの Alexa 搭載端末や Google Chromecast 用アプリも Web 技術を使って開発できるので Web エンジニアが参入する障壁はかなり低いのですが、既存の Web アプリケーションと大きく違うのが、実機で動作確認するのが一苦労な点です。
Web アプリケーションであれば、実装している PC 上でビルドして同じ PC で Web サーバを立てるだけでほかの PC やスマートフォンからアクセスして動作確認できます。しかし、Amazon Echo や Google Chromecast のようなデバイスは、各々のアプリ開発者ポータルに登録されたアプリにしかアクセスできないようになっています。もちろん PC に直接デバイス接続するような機能もプラグもありません。両者とも物理的なインターフェースがシンプルすぎます。
実機で自作アプリの動作確認をするには毎回オンラインでアクセスできるどこかにデプロイする必要があります。アプリを実装する場所(PC)から動作テストを行える場所(オンラインのどこか)までがやたら遠いので、どうしても開発効率が悪くなりますし、複数人で平行して開発しようとすると人数分のテスト環境をどこかにホストする必要があり、そういった環境を別途管理するなど煩雑さも増します。
Alexa スキルをチームで開発するときの課題
そんな中、現在サービス開発を担当している AbemaTV で、いわゆるスマート・デバイス向けのアプリ開発を専門で担当するチームを立ち上げました。Alexa スキルをチームで開発するとなっても、平行開発しづらいこの環境ではマンパワーのメリットが活かせません。平行開発を可能にするために以下の要素を満たす必要があります。
- 開発に使っているローカル PC 上である程度の動作確認が可能なこと
- 継続的インテグレーションが可能なこと
この 2 点を実現する手段について紹介するのですが、Alexa スキルの開発が初めての方のために一般的な開発手順について認識がない方のために、まず Alexa スキルの概要と開発手順をざっくり説明します。
Alexa スキルの開発手順
Alexa スキルと呼ばれるものは、家電製品などを制御するためのスマートホームスキル、ニュースなどを読み上げるためのフラッシュブリーフィングスキルなど用途が特化しているものもありますが、今回は用途が汎用的なサービスを作ることができるカスタムスキルについてお話します。
カスタムスキルの開発手順は大まかに以下のようになります。
- Voice User Interface (VUI) 作成
- サービス・ロジック実装
- VUI からサービス・ロジックへの連携
- 実機やシミュレータによるテスト
- 申請
- 審査
- 公開
具体的な実装手順自体は Amazon Alexa が公式に公開しているチュートリアル Build An Alexa Fact Skill を一通りやると大体分かります。あと、昨日クラスメソッドさんが投稿している 【祝Alexa日本上陸】とりあえず日本語でスキルを作ってみる もスキルの作り方がとても分かりやすいのでオススメです。
Alexa スキルを開発するには、普段聞き慣れない Alexa 特有の概念をいくつか理解する必要があります。個人的には最初分かりづらかったので、補足がてら簡単に説明します。
Voice User Interface - VUI
Voice User Interface(以後 VUI)というのは、その名前の通り、声で操作するユーザー・インターフェースです。PC デスクトップ・アプリやスマートフォン・アプリでいうところのボタンとかテキスト入力ボックスなどにあたります。PC やスマートフォン上のアプリケーションはマウスやタッチパッドで操作するので、ボタンがクリックされたりテキストが入力されたときにアプリは特定の処理を実行します。しかし、Alexa の場合は操作手段が声です。どのように話しかけたときにどんな処理につなげるかを橋渡しする存在が VUI です。
VUI の少し深い話はこちらの記事など参考になりますが、とても抽象性が高い概念なので、ここでは以下の Alexa の VUI を構成する具体的な要素 3 つがどのようにユーザーの声とサービスをつなげているかを見ていきます。
- Invocation Name(呼び出し名)
- Intent(意図)
- Sample Utterance(発話サンプル)
Invocation Name
Invocation Name はいわゆるスキルの呼び出し名です。つまり、Alexa から特定のスキルを使いたいときに指定する名前です。スマホだとホームスクリーンでアプリを起動するときにアイコンをタップすると思いますが、Invocation Name に相当します。たとえば、AbemaTV スキルを呼び出したい場合であれば、AbemaTV が Invocation Name なので、「Alexa、AbemaTV を開いて」と話しかけると AbemaTV スキルが起動します。
Sample Utterance
Sample Utterance は、発話のサンプルです。ユーザーが実際に発話した文言をどんな意図(Intent)として受けるかを判断するための要素です。たとえばユーザーがランキングを知りたいときに、質問の仕方は何パターンもあります。ある人は「ランキングを教えてー」と言うかもしれませんし、別の人はランキングという言葉を使わず「いま人気の番組は何?」と尋くかもしれません。ただ、厳密な発話の仕方が異なってもユーザーが聞きたいことは結果同じだったりします。こういった異なる発話パターンをスキルがどんな意図として解釈するのかをマッピングするのが Sample Utterance の役割です。
上記のような Sample Utterance を書いた場合、「Alexa、AbemaTV のランキングを教えて」 と言っても、 「Alexa、AbemaTV のランキングが知りたい」 と話しかけても 「Alexa、AbemaTV でいま人気の番組は何?」 と訊いても全てランキングを知りたいという意図と解釈して処理するようになります。意図には名前が付けることができ、ここでは RankingIntent
という名前にしています。この名前を指定することは、次に説明する Intent に処理を接続するために重要です。
Intent
Intent は Sample Utterance によってマッピングされたユーザーの意図に対して実際のどんな処理を行う部分です。例の RankingIntent
の場合はユーザーがランキングを知りたいという意図に対する処理なので、実際に現在のランキング・データを取得して、それをユーザーに回答するための文章を作成します。
Amazon Echo などの Alexa 対応デバイスはユーザーの発話音声をクラウド上の Alexa サービスに送り音声からユーザーの意図を解釈した後は、具体的なサービス・ロジックの処理依頼を AWS Lambda のような別サービスにリクエストします。Alexa が Amazon のプロダクトなので、チュートリアルにあるように AWS Lambda 上に処理を実装すると連携も簡単ですし、Amazon が用意している SDK の恩恵にあずかることができます。このフローは Alexa 公式ブログの記事 Alexaスキル開発トレーニングシリーズ 第1回 初めてのスキル開発 の下記スキル実行仕組みの図が分かりやすいです。
Intent に対するサービス・ロジックの実装
Alexa サービスから Intent が指定されて AWS Lambda にリクエストが飛んできます。その Intent に対するサービス・ロジックを Lambda Function として実装します。Amazon が提供する Alexa Skills Kit SDK for Node.js では、Alexa をトリガーに呼び出された AWS Lambda のイベント情報を SDK を通じて Intent ごとのハンドラに振り分けてくれます。
この Lambda Function のエンドポイントを自作のスキルから接続するように設定することで Alexa にユーザーと対話させることができるようになります。
余談ですが、2017 年 11 月現在 AWS Lambda の Node.js サポート・バージョンが v6.10
なので、本記事での JavaScript コードは全て v6.10
用になっています。
Alexa スキルの開発手順を効率化したい
ここまでが Build An Alexa Fact Skill チュートリアルに載っている内容です。この内容をカスタマイズすれば、Alexa スキルを開発することはできます。しかし、前述したように、このスキルを動作確認するには毎回 AWS Lambda 上にコードをデプロイする必要があります。これでは開発効率も悪いです。しかも Alexa スキルは Lambda Function に紐づけて管理するので、チームで開発するとなると人数分のスキル設定と Lambda Function も別途用意しておく必要があります。しばらく開発してると、少々手間がかかりすぎるのでちょっと辛くなり、次のようなことができる環境が欲しいなと思い始めます。
- Amazon Skills Kit やAWS Lambda にデプロイすることなくローカルで気軽にテストしたい
- スキル設定と Lambda Function をまとめて継続的にインテグレーションしたい
結論を言うと、前者は alexa-app というサードパーティーの Alexa スキル用フレームワークを利用することで解決して、後者は Amazon が提供する ASK-CLI という Alexa スキル管理のためのコマンドラインツールを CI ツールで走らせることで解決しました。
ローカルで Alexa スキルを開発する
ローカルで Alexa スキルを動作確認したいと思っている人は多いだろうなと思い、ネット上で記事を漁っていると、やはり同じことを考えている人がちょこちょこいるようです。書かれたのが 2016 年 3 月と少し古いですが、ローカルで Alexa スキルを開発するための詳しい手順が書かれた Big Nerd Ranch さんによる Developing Alexa Skills Locally with Node.js: Implementing an Intent with Alexa-app and Alexa-app-server という記事を見つけました。
alexa-app
alexa-app は、Alexa スキルを開発するためのサードパーティ製のフレームワークです。基本機能としては Alexa からの JSON リクエストを簡単に扱うための API や Alexa へ返すレスポンスを簡単に生成するための API を提供してくれます。実装が少し楽になるにはなるのですが、ただそれだけのメリットだと、サードパーティ製ということもあり将来的なメンテナンスとか考慮すると使うのを躊躇するところです。しかし、これを使いたく一番大きな理由は、今回の課題である「ローカルでのスキル・テスト」と「スキル設定と Lambda Function の継続的インテグレーション」を実現するために必要な次の 2 つの機能を提供してくれるからです。
- 実装したスキルを Express にも連結できる
- Intent と Sample Utterance もフレームワークで管理できる
スキルを Express アプリとしてテスト可能
実機である Alexa 端末から Intent 処理のリクエストを AWS Lambda で受ける必要があるので、alexa-app で実装したスキルを AWS Lambda のハンドラとして連結することは当然可能ですが、このフレームワークは同じ実装コードを任意の Express サーバに接続することも可能です。これで Alexa スキルを Express アプリのようにテストできます。
たとえば簡単な Alexa スキルを alexa-app で書いてみます。
これは「Alexa、AbemaTV を開いて」と話しかけたときに Alexa に「人気の番組は何?、と訊いてください」と答えさせる処理を alexa-app で書いたものです。このテストを Jasmine で書くと:
const app = require('./app');
で読み込んでいるのが alexa-app のインスタンスです。これを Express に接続することで SuperTest などを使って通常の Express HTTP サーバをテストするのと同じ感覚でテストを書くことができます。
一点、注意なのですが、alexa-app インスタンスはそのままだと AWS Lambda に接続できないので、別途 index.js
などのエントリーポイントを作成して以下のようにハンドラに渡すようにしておきます。
alexa-app-server でデバッグ
alexa-app の Express 用のインターフェースを利用すればローカルでのデバッグ作業もかなり楽になります。
任意のリクエストに対するデバッグを行うときに、毎回リクエストを生成するコードを書くのは手間です。そこで alexa-app で書いたスキル用の Web サーバ alexa-app-server を使うと Web ブラウザから GUI で簡単にリクエストを生成できます。
alexa-app-server の設定は簡単です。まずプロジェクトに alexa-app-server を npm
か yarn
でインストールします。
プロジェクトのルート・ディレクトリ直下に apps
というディレクトリを作成して、そこに作成した alexa-app アプリのプロジェクトを移動します。(シンボリックリンクでも構いません。)
alexa-app-server は alexa-app インスタンスのモジュールを探すときに package.json
の main
プロパティの値をパスとして確認します。もしプロジェクトに package.json
がない場合や main
プロパティの値が alexa-app インスタンスのファイル・パスを指していない場合は変更します。
サーバの設定を記述します。alexa-app-server をインストールした方のプロジェクトのルート・ディレクトリに index.js
という名前でファイルを作成し次のように記述します。
保存したら起動してみます。
http://localhost:8080/alexa/sample-alexa-skill にアクセスすると自分が作ったスキル向けの JSON リクエストを生成できるインターフェースが表示されます。ここでスキルに実装済のインテントをプルダウンで設定したり任意の値を入力できるので、ローカルで効率的にスキルをデバッグすることが可能です。
多言語対応
スキルを多言語対応する場合、Alexa からのリクエストにロケール情報が入っているので、それを使って地域/言語別にレスポンスを変更できます。
alexa-app-server はロケール別のリクエスト切り替えがとても簡単なため多言語対応に関しても重宝します。通常、ロケールを頻繁に変更しながらのテストは大変です。Amazon Echo などの端末は Alexa の管理コンソール での登録時にしかロケールを変更できないように見えますし、Amazon 開発者コンソールのシミュレータも言語ごとに分けられているため、ロケールを頻繁に変更するテストには向いていません。
alexa-app-server のインターフェース上、まだ ja-JP
ロケールがオプションから選択できません。フォークして ja-JP
をオプションに追加したものを使っています。こちらプル・リクエスト中。
継続的インテグレーション
alexa-app を使うことで Express に連結してローカルで擬似的にテスト・デバッグできる範囲が広がり、複数人での平行チーム開発も可能になりました。しかし、チームで平行開発できるようになると今度はインテグレーションが問題になってきます。特に Alexa スキルの場合、Lambda Function とは別にスキル設定を Amazon 開発者コンソールで管理しているので、Lambda Function 用の最新コードにスキル設定が一致しないことが発生します。そういった不一致を発生させないために:
- スキル情報と Lambda Function の最新コードを常に同期する
- 同期タイミングは開発コードがメイン・レポジトリへ統合するタイミング
などが実現できれば嬉しいです。前者については、スキル情報とスキルに紐づいた Lambda をまとめて操作できるコマンドラインツールの Alexa Skill Kit Command-line Interface (ASK CLI) を使うことで同期を取ることができます。後者については、ソースコードのバージョン管理に Git/GitHub を使っているのであれば、GitHub との連携が簡単な CI ツールで ASK CLI を走らせれば実現できます。本記事では、CircleCI を使います。
ASK CLI を使うための認証
ASK CLI を使うために Amazon Developer アカウント と AWS ユーザーの認証が必要です。今回は AWS Lambda 用のコードも含めてスキル管理したいので、ASK CLI から AWS を使える状態にする必要があります。
AWS CLI のユーザー認証
ASK CLI の認証時に AWS の認証情報を紐づけたいので、先に AWS ユーザーを認証します。(AWS CLI を既に使ったことがある方で認証済のプロファイルがある場合は、ここは読み飛ばしていただくのが良いでしょう。)
まず AWS CLI をインストールします。Python パッケージで提供されているので、 pip
などでインストールします。
インストール完了後、 which aws
などでパスが表示されることを確認できたら、次に AWS のユーザー認証を行います。AWS のアカウントがまだない場合は アマゾン ウェブ サービス(AWS) で作成します。AWS のアカウントを持っている場合は、ログインして IAM Management Console サービスの Users で AWS CLI 用のユーザーを作成します。作成時に表示される AWS Access Key ID と AWS Secret Access Key をメモしておきます。
Alexa スキルに紐づける Lambda Function を作成したり、IAM の操作も許可する必要があるので、作成したユーザーに下記のポリシーを追加します。
作成した AWS CLI 用のユーザーで AWS CLI を認証します。認証は aws configure
コマンドで行います。
Default region name
ですが、Alexa Skills Kit のドキュメントに以下のように書いてあり、限られたリージョンでしか AWS Lambda の Alexa Skills Kit のサポートをしていないので注意が必要です。
Lambda functions for Alexa skills can be hosted in either the US East (N. Virginia) or EU (Ireland) region. These are the only regions the Alexa Skills Kit supports.
これで AWS CLI が認証できました。認証情報が ~/.aws/config
と ~/.aws/credentials
に保存されていれば OK です。
ASK CLI のアカウント認証
次に ASK CLI を Amazon Developer アカウントに認証します。 ask init
コマンドを使って AWS CLI でユーザー認証したプロファイルと紐づけながら Amazon Developer アカウントへの認証手順が進みます。
初めて実行する場合は下記のようなダイアログが表示されますが、既にデフォルトのプロファイルがある場合は、新規プロファイルを作成するのか、既存プロファイルを上書くのかを尋かれるダイアログが表示されます。
ここで紐づけたい AWS のプロファイルを選択します。例では、先程 aws configure
でユーザー認証したときプロファイル指定をしていないので、 default
という名前で保存されているので、 default
を選択します。
Web ブラウザが起動し、「Login with Amazon」のページで表示されるのでログインします。
次に権限の確認をされるので、問題なければ「Okay」ボタンをクリックします。
無事ログインが成功すると、Sign in was successful. Close this browser and return to the command line interface. というメッセージでブラウザを閉じろと言われるので閉じます。
ASK CLI の認証情報に関しては ~/.ask/cli_config
に保存されています。
ASK プロジェクトを作成
ユーザー認証が通ったので、ASK CLI でスキル全体を管理できるようにプロジェクトを新規作成します。
これで新規の Alexa スキル・プロジェクトの雛形が作成されます。tree コマンドを実行すると次のようなディレクトリ・ツリーが表示されるはずです。
この中で重要な各要素の役割はざっくりと次の通りです。
要素 | 役割 |
---|---|
lambda | Lambda 用のコードを格納するディレクトリ |
models | Intent Schema や Sample Utterance などを格納するディレクトリ |
skill.json | スキルの申請に必要な情報を記述するファイル |
雛形ができたので、スキルに必要な情報を設定していきます。
まず Alexa は Apple の App Store などと同様にスキルを公開するのに申請が必要なので、skill.json
にこのスキルの名前やこのスキルを使うためのフレーズ例など、申請に必要な情報を記述します。
次に lambda
ディレクトリには、今回 alexa-app で作った AWS Lambda 用モジュールを格納します。生成された lambda
ディレクトリ以下の雛形は必要ないので custom
ディレクトリごと削除してしまい、代わりに AWS Lambda 用モジュールのディレクトリを custom
という名前でここに移動します。
最後に models
ディレクトリにスキルの Intent のデータ構造を示した Intent Schema と Sample Utterance の情報を格納する必要があるのですが、これらは alexa-app フレームワーク上の実装コード内に記述されています。なので、フレームワークの API を使って JSON ファイルとして出力するスクリプトを書きます。
このスクリプトを実行した出力をロケール ID をファイル名にした JSON にパイプします。日本語であれば models/ja-JP.json
にパイプします。
ja-JP.json
の中身はこんな感じです。
多言語対応する場合は、必要な分、別のロケール ID の JSON ファイルにパイプします。ロケール ID をファイル名にした JSON ファイルを複数 models
ディレクトリに入れておくことにより、ASK CLI が言語別のスキル情報として登録してくれます。ここでは日本語と英語に対応するために ja-JS.json
とは別に en-US.json
を書き出します。
ここでは、環境変数 APP_LOCALE
に応じて Sample Utterance が切り替わるように alexa-app の Intent を実装しました。 en-US.json
の Sample Utterance 部分などが差し替わって出力されます。
言語に関する設定は models
ディレクトリのほかに skill.json
ファイルにも記述する必要があるので、必要に応じてロケール情報を追加しましょう。
これで alexa-app で実装した多言語対応スキルを ASK プロジェクトとして管理できるようになりました。
Alexa スキルをデプロイする
作成した ASK プロジェクトを Amazon Echo などの実機で試すためには、Alexa スキルとしてデプロイする必要があります。ASK CLI の deploy
コマンドを使うだけです。
Alexa スキルがデプロイされたことを確認するため、Amazon 開発者コンソール に行き、Alexa Skills Kit のスキル・リストに sample-alexa-skill
が登録されているか確認します。
GitHub 連携で CI
ASK CLI で Alexa スキルをデプロイできるところまで来たので、あとはこの手順を CI ツールに設定すれば継続的にテストしたりデプロイしたりすることができます。もちろん CI ツールは何でも構いませんが、ここでは GitHub に簡単に連携ができる CircleCI を使って、GitHub Flow ベースで単純で DevOps な感じの運用ができればいいなというイメージ。
デプロイ時に必要な処理の依存関係を Makefile にまとめる
CircleCI に処理を書いていく前に、スクリプトの実行手順に若干の依存関係ができてしまったので、明示的に手順を示す意味で Makefile にまとめます。
先程も説明した通り、多言語対応する場合は、環境変数別に gen-interaction-model.sh
を走らせて言語別の Model を書き出す処理もまとめておきます。
CircleCI にインテグレーション/デプロイ処理を追加する
デプロイ時に必要な処理もまとまったので、CircleCI プロジェクト用にインテグレーション処理とデプロイ処理を書いていきます。ASK プロジェクト・ディレクトリ直下に .circleci/config.yml
ファイルを作り、以下のような YAML でジョブを記述します。
テストの実行やらコード・カバレッジの取得やらしていますが、前述したようにデプロイに関しては
- スキル情報と Lambda Function の最新コードを常に同期する
- 同期タイミングは開発コードがメイン・レポジトリへ統合するタイミング
のように自動化したかったので、CircleCI で GitHub の master
ブランチへのマージのタイミングで下記 3 点の処理を実行するようにタスクを記述しています。
- AWS CLI のインストールおよび設定
- ASK CLI のインストールおよび設定
- スキル設定と AWS Lambda コードのデプロイ
ローカル同様、CircleCI 上でも AWS と ASK の認証が必要です。それぞれ CircleCI 上でインストールして認証情報を設定します。
まず ASK CLI で AWS Lambda をデプロイするために必要なので AWS CLI をインストールします。AWS CLI を認証するために環境変数 AWS_ACCESS_KEY_ID
と AWS_SECRET_ACCESS_KEY
を参照しています。なので、CircleCI 側の環境変数に両者を登録しておきます。
ASK CLI に関しては、先程の認証情報が ~/.ask/cli_config
に記述してあるので Base64 にエンコードしてクリップボードにコピーします。Mac 系の OS なら pbcopy
できるのでこんな感じです。
クリップボードの中身を CircleCI 側の環境変数 ASK_CLI_CONFIG
として設定します。これで GitHub にホストした master
ブランチに変更をマージする度に Amazon 開発者コンソールの Alexa スキル情報と紐づいた AWS Lambda Function コードが最新状態に更新されるようになりました。
Alexa スキルを実際に公開するためには、スキルを申請して審査を通過する必要があります。ASK CLI は申請もコマンドラインで送信できるので、それも自動化したい人は ask api submit
コマンドなど必要な手順を CI プロセスに追加してもいいかもしれません。
まとめ
Alexa スキルは Web 技術を使って簡単に開発を始めることができます。新しいデバイスなので勝手が掴めず、動作確認等々が大変な部分もあって最初はちょっととまどいましたが、探せば開発を助けるツールの恩恵を受けることができ、少しずつ開発しやすい環境を構築できるようになってるなと感じます。本記事がこれから Alexa スキルを開発をする人の効率化の参考になれば幸いです。
本記事の内容のサンプルコードは下記に上げてあります。