この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
1 はじめに
本記事は、Alexa SDK for Node.js Ver2入門と題して、入門用という位置付けで、Alexa SDKの使い方を、順に紹介しているものです。(対象は、Node.js用のみです。Java用には触れておりません)
その4では、スキル属性(セッション情報等)について少し詳しく見ていきたいと思います。
Alexa SDKでは、セッション中や、セッションを跨いだデータの保存が可能です。 これは、Skill Attributesという概念で、その生存期間に応じて次の3種類に分類されています。
- Request attributes (1回のリクエスト中のみ有効)
- Session attributes (セッション継続間に有効)
- Persistent attributes (セッションを跨いで永続化 DynamoDB等が必要)
以下、それぞれについて見ていきます。
2 Request attributes(1回のリクエストでのみ有効)
Request attributesは、一回のリクエストの中でのみ有効です。Alexaからのリクエストが到着した時点では空であり、レスポンスを返した時に破棄されます。 利用場面の一例としては、追加の情報やヘルパークラスを使用したい場合に、インターセプターでこれを注入し、ハンドラ内で既存の機能のように利用することです。
下記の例は、インターセプタで、ローカライズ用のヘルパークラスを作成し、それをハンドラ内で利用しているものです。ちょうど、Ver1にあったローカライズ用のリソースをVer2でそのまま使うようになっています(必須ハンドラー、エラー制御などは省略されています)
「alexa/skill-sample-nodejs-howto」を参考にさせて頂きました。
const Alexa = require('ask-sdk');
const i18n = require('i18next');
const sprintf = require('i18next-sprintf-postprocessor');
let skill;
exports.handler = async function (event, context) {
if (!skill) {
skill = Alexa.SkillBuilders.custom()
.addRequestInterceptors(LocalizationInterceptor) // インターセプターの追加
.addRequestHandlers(
LaunchRequestHandler,
StopIntentHandler)
.create();
}
return skill.invoke(event);
}
// リソース
const languageStrings = {
'ja-JP': {
translation: {
GOODBYE_MESSAGE: 'さようなら',
WELCOME_MESSAGE: 'ようこそ',
},
},
'en-US': {
translation: {
GOODBYE_MESSAGE: 'Good bye',
WELCOME_MESSAGE: 'Welcome',
},
}
};
// インターセプター
const LocalizationInterceptor = {
process(handlerInput) {
// ヘルパー関数の定義
const localizationClient = i18n.use(sprintf).init({
lng: handlerInput.requestEnvelope.request.locale,
overloadTranslationOptionHandler: sprintf.overloadTranslationOptionHandler,
resources: languageStrings,
returnObjects: true
});
// リクエスト情報の取得
const attributes = handlerInput.attributesManager.getRequestAttributes();
// リクエスト情報への注入
attributes.t = function (...args) {
return localizationClient.t(...args);
};
},
};
// 起動時の処理
const LaunchRequestHandler = {
canHandle(handlerInput) {
return handlerInput.requestEnvelope.request.type === 'LaunchRequest';
},
handle(handlerInput) {
// リクエスト情報の取得
const requestAttributes = handlerInput.attributesManager.getRequestAttributes();
const speechText = requestAttributes.t('WELCOME_MESSAGE');
return handlerInput.responseBuilder
.speak(speechText)
.reprompt(speechText)
.getResponse();
}
};
// その他の処理
const StopIntentHandler = {
canHandle(handlerInput) {
return true;
},
handle(handlerInput) {
// リクエスト情報の取得
const requestAttributes = handlerInput.attributesManager.getRequestAttributes();
const speechText = requestAttributes.t('GOODBYE_MESSAGE');
return handlerInput.responseBuilder
.speak(speechText)
.getResponse();
}
};
リソースが変化していることを確認できます。
3 Session attributes(セッション継続間に有効)
Session attributesは、セッション継続中に有効です。 保存された情報は、レスポンスでAlexaサービスに渡され、次のリクエストで、再びリクエストに含まれるようになります。外部ストレージなどは一切必要ありません。
例として、セッション中に数値を記憶する、下記のようなスキルを作成してみました。
U:アレクサ、テストスキルをスタートして
A:現在記憶している数字は4です。2乗しますか?
U:はい
A:現在記憶している数字は16です。2乗しますか?
U:はい
A:現在記憶している数字は256です。2乗しますか?
U:いいえ
A:終わります。
下記が、その実装です。(必須ハンドラー、エラー制御などは省略されています)
const Alexa = require('ask-sdk');
let skill;
exports.handler = async function (event, context) {
if (!skill) {
skill = Alexa.SkillBuilders.custom()
.addRequestHandlers(
LaunchRequestHandler,
YesIntentHandler,
StopHandler)
.create();
}
return skill.invoke(event);
}
function response(handlerInput, counter) {
const speechText = `現在記憶している数字は${counter}です。2乗しますか?`;
return handlerInput.responseBuilder
.speak(speechText)
.reprompt(speechText)
.getResponse();
}
function readCounter(handlerInput, pow) {
// セッション情報の取得
let attributes = handlerInput.attributesManager.getSessionAttributes();
// 初期化
if(!attributes.counter){
attributes.counter = 2;
}
// 2乗する
if (pow) {
attributes.counter = Math.pow(attributes.counter, 2);
}
// セッション情報の保存
handlerInput.attributesManager.setSessionAttributes(attributes);
return attributes.counter;
}
// スキル起動時の処理
const LaunchRequestHandler = {
canHandle(handlerInput) {
return handlerInput.requestEnvelope.request.type === 'LaunchRequest';
},
handle(handlerInput) {
// 2乗しないでカウンターを取得する
const counter = readCounter(handlerInput, false);
return response(handlerInput, counter);
}
};
// 「はい」と答えた場合の処理
const YesIntentHandler = {
canHandle(handlerInput) {
return handlerInput.requestEnvelope.request.type === 'IntentRequest'
&& handlerInput.requestEnvelope.request.intent.name === 'AMAZON.YesIntent';
},
handle(handlerInput) {
// 2乗してカウンターを取得する
const counter = readCounter(handlerInput, true);
return response(handlerInput, counter);
}
};
// その他の処理
const StopHandler = {
canHandle(handlerInput) {
return true;
},
handle(handlerInput) {
return handlerInput.responseBuilder
.speak('終わります')
.getResponse();
}
};
セッション期間中だけ、情報が保存されていることを確認できます。
4 Persistent attributes(セッションを跨いで永続化する DynamoDB等が必要)
Persistent attributesは、セッションのライフサイクルを超えてデータを永続化します。
Persistent attributesを使用するためには、インスタンスの生成時にPersistenceAdapterの設定が必要であり、具体的には、withTableName()の呼び出しになります。また、withTableName()を使用するために、スキルの生成もAlexa.SkillBuilders.standard()を使用することになります。
const Alexa = require('ask-sdk');
let skill;
exports.handler = async function (event, context) {
if (!skill) {
skill = Alexa.SkillBuilders.standard() // <= standard()
.addRequestHandlers(
LaunchRequestHandler,
YesIntentHandler,
StopHandler)
.withTableName("sampleTableName") // これを追加(テーブル名)
.withAutoCreateTable(true) //テーブル作成もスキルから行う場合は、これも追加
.create();
}
return skill.invoke(event);
}
function response(handlerInput, counter) {
const speechText = `現在記憶している数字は${counter}です。2乗しますか?`;
return handlerInput.responseBuilder
.speak(speechText)
.reprompt(speechText)
.getResponse();
}
async function readCounter(handlerInput, pow) {
// 永続化情報の取得
let attributes = await handlerInput.attributesManager.getPersistentAttributes()
// 初期化
if(!attributes.counter){
attributes.counter = 2;
}
// 2乗する
if (pow) {
attributes.counter = Math.pow(attributes.counter, 2);
}
// 永続化情報の保存
handlerInput.attributesManager.setPersistentAttributes(attributes);
await handlerInput.attributesManager.savePersistentAttributes();
return attributes.counter;
}
// スキル起動時の処理
const LaunchRequestHandler = {
canHandle(handlerInput) {
return handlerInput.requestEnvelope.request.type === 'LaunchRequest';
},
async handle(handlerInput) {
// 2乗しないでカウンターを取得する
const counter = await readCounter(handlerInput, false);
return response(handlerInput, counter);
}
};
// 「はい」と答えた場合の処理
const YesIntentHandler = {
canHandle(handlerInput) {
return handlerInput.requestEnvelope.request.type === 'IntentRequest'
&& handlerInput.requestEnvelope.request.intent.name === 'AMAZON.YesIntent';
},
async handle(handlerInput) {
// 2乗してでカウンターを取得する
const counter = await readCounter(handlerInput, true);
return response(handlerInput, counter);
}
};
// その他の処理
const StopHandler = {
canHandle(handlerInput) {
return true;
},
handle(handlerInput) {
return handlerInput.responseBuilder
.speak('終わります')
.getResponse();
}
};
永続化データは、Id(UserId)をキーにしてDynamoDBに保存されています。
実行している様子です。スキルが終了してもデータが保持されている様子が分かります。
5 最後に
今回は、スキル属性(セッション情報等)について確認してみました。情報の生存期間が明確となっていますので、それに応じた記述が必要になります。
Ver1では、ステートという概念が有り、インテント名とステート名でルーティングを行っていましたが、Ver2には、その機能はありません。同様の仕組みを使用する場合は、セッション情報に保存して、独自にステートの仕組みを実装する必要があります。
6 参考リンク
Alexa Skills Kit SDK for Node.js
Now Available: Version 2 of the ASK Software Development Kit for Node.js
GitHub repository for the SDK v2 for Node.js
[日本語Alexa] Alexa SDK for Node.js Ver2入門(その1)はじめの一歩
[日本語Alexa] Alexa SDK for Node.js Ver2入門(その2)ハンドラの登録
[日本語Alexa] Alexa SDK for Node.js Ver2入門(その3)レスポンスの作成
[日本語Alexa] Alexa-SDK Ver2(その4)スキル属性
[日本語Alexa] Alexa-SDK Ver2(その5)ダイアログモード
[日本語Alexa] Alexa-SDK Ver2(その6) 所在地情報
[日本語Alexa] Alexa-SDK Ver2(その7) ディスプレイ表示