【Alexa】ASK SDKにスキルエンジニアの強い味方「ASK SDK Utilities」が登場。 #Alexa #AlexaDevs
せーのでございます。
Alexaスキルを開発する時に、必要なものなので何回も何回も書くのだけれど、いい加減これもう書くのが面倒くさい、という記述ってありますよね。
例えばこれ。
handle(handlerInput) { var slotXXX = handlerInput.requestEnvelope.request.intent.slots.XXX.value;
はい、スロットの値を取ってくる式ですね。もう何百回これを書いたことか。SDKかV2になって目的の値に到達するまで長くなり、書くのがどんどん億劫になっていきます。Visual Studio CodeなどスニペットのでないIDEで開発する場合は微妙なタイプミスが起こったり。
今回はそんな「あるあるの関数」が簡単に取れるようになる「ASK SDK Utilities」がリリースされたのでご紹介します。
使い方
ASK SDK Utilitiesは「ask-sdk-core」ライブラリの中に入っています。ちょっと宣言部分を覗いてみると
....... var RequestEnvelopeUtils_1 = require("./util/RequestEnvelopeUtils"); exports.getAccountLinkingAccessToken = RequestEnvelopeUtils_1.getAccountLinkingAccessToken; exports.getApiAccessToken = RequestEnvelopeUtils_1.getApiAccessToken; exports.getDeviceId = RequestEnvelopeUtils_1.getDeviceId; exports.getDialogState = RequestEnvelopeUtils_1.getDialogState; exports.getIntentName = RequestEnvelopeUtils_1.getIntentName; exports.getLocale = RequestEnvelopeUtils_1.getLocale; exports.getRequestType = RequestEnvelopeUtils_1.getRequestType; exports.getSlot = RequestEnvelopeUtils_1.getSlot; exports.getSlotValue = RequestEnvelopeUtils_1.getSlotValue; exports.getSupportedInterfaces = RequestEnvelopeUtils_1.getSupportedInterfaces; exports.isNewSession = RequestEnvelopeUtils_1.isNewSession; .......
このように関数名が直接exportsされています。ですので使い方としては
const accesstoken = Alexa.getAccountLinkingAccessToken(handlerInput.requestEnvelope);
と、「Alexa」オブジェクトの後ろに直接書くといいでしょう。
種類
ASK SDK Utilitiesには様々な値を簡単に取ってくるユーティリティ関数が用意されています。 ざっと見ていきましょう。
RequestEnvelopeUtils
まずはスキルの要、RequestEnvelopeに関するユーティリティです。
関数名 | 内容 |
---|---|
getLocale(RequestEnvelope): string; | Echoのロケール |
getRequestType(RequestEnvelope): string; | リクエストタイプ |
getIntentName(RequestEnvelope): string; | インテント名 |
getAccountLinkingAccessToken(RequestEnvelope): string; | Account Linkingのアクセストークン |
getApiAccessToken(RequestEnvelope): string; | API使用時のアクセストークン |
getDeviceId(RequestEnvelope): string; | EchoのデバイスID |
getDialogState(RequestEnvelope): string; | ダイアログモデル使用時の現在の状態 |
getSlot(RequestEnvelope, slotName): Slot; | スロット名 |
getSlotValue(RequestEnvelope, slotName): string; | スロットの値 |
getSupportedInterfaces(RequestEnvelope): SupportedInterfaces; | Echoが画面付きかどうか |
isNewSession(RequestEnvelope): boolean; | 初回アクセスかどうか |
Handleの中に書きそうなものだけでなく、CanHandleの条件に書くものも結構入っていていいですね。
SsmlUtils
次はSSMLを書く際によく使うユーティリティです。
関数名 | 内容 |
---|---|
escapeXmlCharacters(string): string; | SSML内のキャラクターをエスケープする |
SSMLはマークアップランゲージなので、中にタグ的な要素(“<“とか”>”とか)やクオーテーション系が入るとエラーになりますね。escapeXmlCharactersはそれをエスケープしてくれる、という便利関数です。
ViewportUtils
最後にViewportに関するユーティリティです。 Viewportというのは画面付きデバイスのデザインをAPLで書く際に使うパッケージグループです。
関数名 | 内容 |
---|---|
getViewportOrientation(width, height): ViewportOrientation; | 画面が縦長か横長か |
getViewportSizeGroup(size): ViewportSizeGroup; | dp単位の画面幅/高さによるサイズ |
getViewportDpiGroup(dpi): ViewportDpiGroup; | dpi単位での画面密度のグループ |
getViewportProfile(requestEnvelope): ViewportProfile; | Echoの画面形状 |
ここら辺はAPLや画面デザインに慣れていない人にはちょっとわかりにくいのでもう少し詳しく掘り下げてみましょう。
ViewportOrientation
画面付きEchoはEcho Spotなどの幅と高さが同じデバイスとEcho Showのように画面が横長のデバイスがあります。タブレットにもAlexaは搭載され始めていますので、将来的には縦長の表示も考えられます。 APLをデザインする際には横長か同じかによって変わりますので、この関数に幅と高さを入れて、デバイスがどういう形なのかを把握します。
ViewportSizeGroup
ViewportSizeを理解するためにまず「dp」という単位を理解しましょう。 dpとは一般に「画面非依存ピクセル」「密度非依存ピクセル」と呼ばれます。
画面には様々な解像度があり、解像度が高ければ1pxの大きさは小さくなります。つまり「この四角の大きさは10px x 10px」と指定しても、実際に表示される大きさは画面によって違うわけです。
dpというのはある解像度の画面における1pxを基準にして、それより高い解像度の場合は大きく、低い解像度の場合は小さくなることで、見た目の大きさを合わせるための単位です。 例えば四角の大きさを「10dp × 10dp」とした場合、ある画面でそれが10px × 10pxで表示されたとしたら、その倍の解像度のある画面では20px × 20pxで表示されます。
dpは「携帯電話を見るときの距離で画面を持ち、画面のピクセル密度が1インチ(3cm)あたり約160ピクセルであると仮定した場合の画面のビジュアルサイズ」となります。
Alexaではこのdp単位で幅と高さを入力した場合の画面サイズをグループとして管理しています。
- 600dp未満: viewportSizeXSmall
- 600dp以上960dp未満: viewportSizeSmall
- 960dp以上1280dp未満: viewportSizeMedium
- 1280dp以上1920dp未満: viewportSizeLarge
- 1920dp以上: viewportSizeXLarge
この関数ではこのdp単位の数字によってこれらのサイズグループが返ってきます。
ViewportDpiGroup
dpi(dot per inch)とはその名の通り1インチあたりのドットの数、つまり画面の密度を指します。
ここら辺から一気にややこしくなるので、特に興味のない人は「へー、画面の密度ね。よく印刷物とかディスプレイとかで使うやつね。OKOK」で読み飛ばしちゃってください。要はdpiが高いほど綺麗な画像で、ViewportDpiGroupはその綺麗さを
”XLOW”|”LOW”|”MEDIUM”|”HIGH”|”XHIGH”|”XXHIGH”
でグループ分けしている、ということです。
さて、本来dpiというのは同じ画素数でも画面のサイズによって変わります。iPhoneのRetinaディスプレイは画素数的にはフルHD(1080p)も無いのですが、かなり綺麗ですよね。一方50インチくらいのTVになるとフルHDではぼやっと見えてしまいますので4K(2160p)くらいあると綺麗です。画面の幅や高さに対して入っているピクセルの数が多いと密度が高い、つまりdpiが高く、綺麗に見えるのです。
ですがViewportにおけるdpiというのは画面の大きさに関わらず一定として計算されます。具体的には
dpi = 160 * (ピクセルサイズ/dpサイズ)
となります。 これは画面のサイズによって適切と言われる視聴距離が違うため、結果的に人間から見た画面の大きさは同じになる、という考えからきています。
例えばフルHD(1080p)の場合、適切な視聴距離は「画面の高さの3倍」と言われています。Amazon的には画面インチの2倍、となっています。画面インチは対角線の距離を指す(40インチTVなら対角線の長さが40インチ)ので、16:9なら対角線の長さは高さの約2倍になります。少し遠いですね。アメリカ基準なのでしょうか。日本の視聴距離は視力1.0を基準にすると綺麗に見える距離、としていますので、アメリカの視聴者の視力基準が違うのかもしれません。
話が少しそれました。Amazonの基準で言うとフルHDのdpiは画面に関わらず320となります。公式に当てはめるとdpサイズは2となります。つまり、10px × 10pxの四角は320dpiの場合2倍の20px × 20pxで表示されることになります。
ViewportProfile
最後はViewportProfileです。これは画面の大きさや形によってカテゴリ分けするものです。 Alexaの場合、このカテゴリは
- hubRoundSmall(画面が丸くて小さい。Echo Spotなど)
- hubLandscapeMedium(画面が横長で少し小さめ。初代Echo Showなど)
- hubLandscapeLarge(画面が横長で少し大きい。二代目Echo Showなど)
- tvLandscapeXLarge(画面が横長でかなり大きい。FireTVなど)
と分かれています。デバイスに話しかけた時にAlexaに対して、話しかけられたデバイスの情報がrequestEnvelopに入ってきます。その中にこのProfile情報も入ってきます。それをこのユーティリティで簡単に取り出せるわけです。
やってみた
ローカル
では簡単なものからやってみましょう。 通常通り作業用のディレクトリを作ったらnpmでask-sdkをインストールします。
npm install --save ask-sdk
お手持ちのエディタでインストールされたask-sdkのCHANGELOGやpackage.jsonを確認してみましょう。
**# (2019-03-07)** [2.5.0](https://github.com/alexa/alexa-skills-kit-sdk-for-nodejs/compare/v2.4.0...v2.5.0) **### Bug Fixes** * update peer dependency of ask-sdk-model ([#526](~https://github.com/alexa/alexa-skills-kit-sdk-for-nodejs/issues/526~)) ([9167297](~https://github.com/alexa/alexa-skills-kit-sdk-for-nodejs/commit/9167297~)) **### Features** * add RequestEnvelopeUtils functions ([#525](~https://github.com/alexa/alexa-skills-kit-sdk-for-nodejs/issues/525~)) ([14be7a8](~https://github.com/alexa/alexa-skills-kit-sdk-for-nodejs/commit/14be7a8~)) * auto escape invalid SSML characters ([#522](~https://github.com/alexa/alexa-skills-kit-sdk-for-nodejs/issues/522~)) ([7a4f215](~https://github.com/alexa/alexa-skills-kit-sdk-for-nodejs/commit/7a4f215~)), closes [#472](~https://github.com/alexa/alexa-skills-kit-sdk-for-nodejs/issues/472~)
2019年4月23日時点での最新のバージョンは「2.5.1」となっています。Utilitiesの機能は2.5.0にて追加されたんですね。ちなみにコンポーネント版であるask-sdk-coreも2.5.1、ask-ask-modelは1.9.4が最新となります。
後は普通通りスキルを作っていくだけです。血液型を聞くスキルを作ってみます。
Intentに血液型を返す発話を書き込みます。
血液型を表すSlotを作ります。
Intentにて設定していた変数と作ったSlotをつなげます。
これでビルドすると出来上がりです。
つづけてバックエンドです。こんな感じでスロットを取り出す部分にユーティリティを使ってみました。ちなみにここではSkilbuilderはStandardを使っていますが、Customでask-sdk-coreだけでも動きます。
const Alexa = require('ask-sdk'); const LaunchRequestHandler = { canHandle(handlerInput) { return Alexa.getRequestType(handlerInput.requestEnvelope) === 'LaunchRequest'; }, handle(handlerInput) { const speechText = 'こんにちは。あなたの血液型を教えてください。'; return handlerInput.responseBuilder .speak(speechText) .reprompt(speechText) .getResponse(); } }; const HelloWorldIntentHandler = { canHandle(handlerInput) { return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest' && Alexa.getIntentName(handlerInput.requestEnvelope) === 'HelloWorldIntent'; }, handle(handlerInput) { const bloodtype = Alexa.getSlotValue(handlerInput.requestEnvelope, "bloodtype"); const bloodtypeParent = Alexa.getSlot(handlerInput.requestEnvelope, "bloodtype") let speechText = bloodtype + 'なんですね。'; console.log("getSlotValue: " + bloodtype); console.log("getSlot: " + JSON.stringify(bloodtypeParent, " ", 2)); const val = bloodtypeParent.resolutions.resolutionsPerAuthority[0].values[0].value.name; if (val === "A型") { speechText +="きっと几帳面な方なんでしょうね。"; } if (val === "B型") { speechText += "きっと才能あふれる方なんでしょうね。"; } if (val === "O型") { speechText += "きっとおおらかな方なんでしょうね。"; } if (val === "AB型") { speechText += "きっと芸術性の高い方なんでしょうね。"; } return handlerInput.responseBuilder .speak(speechText) //.reprompt('add a reprompt if you want to keep the session open for the user to respond') .getResponse(); } }; ........
HelloWorldIntentでbloodtypeというスロットを拾って答えを返しています。 ではテストしてみましょう。
正常に動きますね。成功です。
alexa-hosted skill
hosted skillでもUtilitiesは使えます。やってみましょう。 開発者コンソールで「Alexaをホストにする」を選択してスキルを作ります。
「コードエディタ」を開きます。 デフォルトではバージョンがまだ低いので、最新のバージョンに上げます。hosted skillではpackage.jsonを書き換えてデプロイしてあげれば新しいライブラリを引いてきてくれます。
これでバージョンが上がりました。後は同じようにコーディングすればOKです。簡単ですね。
まとめ
以上、ASK SDK Utilitiesについてまとめてみました。 今回はサンプルとしてスロットの取得だけやってみましたが、canHandleでのリクエストタイプやインテント名などの取得は全てこのUtilitiesで置き換えられます。 とても使い勝手のいいライブラリなので公式からこういうのが出てくれるとありがたいですね。どんどん使っていきましょう。
参考リンク
- https://ask-sdk-for-nodejs.readthedocs.io/en/latest/ASK-SDK-Utilities.html#
- https://github.com/alexa/alexa-skills-kit-sdk-for-nodejs/tree/2.0.x/ask-sdk-core/lib/util
- http://ask-sdk-node-typedoc.s3-website-us-east-1.amazonaws.com/
- https://developer.amazon.com/ja/docs/alexa-presentation-language/apl-viewport-property.html#dpi