[日本語Alexa] SessionEndedRequestの正しい料理法

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

1 はじめに

スキルの開発では、Alexaから送られてくる各種のリクエストを処理しますが、このリクエストのタイプには、以下の3つがあります。

  • LaunchRequest
  • IntentRequest
  • SessionEndedRequest

LaunchRequestは、「アレクサ、(スキルの呼び出し名)を開いて」のように、特定のインテントが指定されない状態で呼び出された時に送られて来ます。また、IntentRequestは、インテントスキーマで定義したインテントのいずれかにヒットした場合に送られて来ます。スキルの開発者は、この2つのリクエストに対して、適切なレスポンスを返す必要があります。

そして、3つ目のSessionEndedRequestですが、こちらは、スキル開発者が明示的にセッションを終了させていないのに、セッションが終了してしまった場合に送られてきます。SessionEndedRequestが来た時は、セッションが終了しているわけですから、スキルから何らかのレスポンスを返す(発話する)ことは不可能です。

つまり下記のような実装は、誤りとなります。

'SessionEndedRequest': function () {
    this.emit(':tell', '終了します');
},

今回は、このSessionEndedRequestについて、整理してみました。

2 SessionEndedRequestの例

SessionEndedRequestは、スキル開発者が明示的にセッションを終了させた場合以外に送られて来るのですが、そのパターンは、以下の3つです。

  • ユーザーによる終了
  • 適切な応答がない場合
  • エラーが発生した場合

(1) 明示的なセッション終了

最初に、SessionEndedRequestが来ないパターンですが、スキル開発者が明示的にセッションを終了させた場合について確認しておきます。

これは、レスポンスのshouldEndSessiontrueにセットされた時のことで、Alexa SDKでは、下記のようなコードとなります。

this.emit(':tell','終了します');
this.response.speak(speechOutput);
this.emit(':responseReady');

(2) ユーザーによる終了

ユーザーによる終了とは、ユーザが「終了」とか「おしまい」と言って、Alexaがセッションを終了した場合です。

A: 何か言って下さい
U: おしまい  (「終了」「終了して」など)
"request": {
    "type": "SessionEndedRequest",
    "reason": "USER_INITIATED"
}

ここで注意が必要なのは、ユーザー「ストップ」とか言って、AMAZON.StopIntentにヒットした場合です。この場合は、セッションは継続中ですので、SessionEndedRequestが来ないで、AMAZON.StopINtentが来ることになります。この場合は、:tellを送ることで、スキルから明示的にセッションを終了させなければなりません。

A: 何か言って下さい
U: ストップ
AMAZON.StopIntent
'AMAZON.StopIntent': function () { 
    this.emit(':tell','ストップします');
}

AMAZON.Cancelも同じパターンです。

A: 何か言って下さい
U: キャンセルして
AMAZON.CancelIntent
'AMAZON.CancelIntent': function () { 
    this.emit(':tell','キャンセルします');
}

もし、明示的にセッションを終了させない場合 次のようにエラーのSessionEndedRequestを受けることになります。

A: 何か言って下さい
U: ストップ
AMAZON.StopIntent
A: スキルからの応答に問題があります。
'AMAZON.StopIntent': function () { 
    // レスポンスを返していない
},
"request": {
    "type": "SessionEndedRequest",
    "reason": "ERROR",
    "error": {
        "type": "INVALID_RESPONSE",
        "message": "SpeechletResponse was null"
    }
}

(3) 適切な応答がない場合

ユーザーから適切な応答がない場合、セッションは終了となり、SessionEndedRequestが送られてきます。

'LaunchRequest': function () { 
    this.emit(':ask','何か言って下さい', 'ちゃんと言って下さい');
},
A: 何か言って下さい
U: ・・・・(無言)
A: ちゃんと言って下さい
U: ・・・・(無言)
"request": {
    "type": "SessionEndedRequest",
    "reason": "EXCEEDED_MAX_REPROMPTS"
}

(4) エラーが発生した場合

'LaunchRequest': function () { 
    test <=無効なコード
},

上のように、コードに問題がありLambdaがエラーとなってしまう場合、当然セッションは終了し、エラーが入ったSessionEndedRequestが送られてきます。

U: ***をスタートして
A: スキルからの応答に問題があります
"request": {
    "type": "LaunchRequest",
}

Lambdaエラー発生

"request": {
    "type": "SessionEndedRequest",
    "reason": "ERROR",
    "error": {
        "type": "INVALID_RESPONSE",
        "message": "An exception occurred while dispatching the request to the skill."
    }
}

3 SessionEndedRequestのパラメーター

SessionEndedRequestのパラメーターとして含まれるreason及び、errorは、以下のとおりです。

(1) reason

reasonは、セッションが終了した理由を示しており、下記の3種類があります。

  • USER_INITIATED: ユーザーがセッションを終了
  • ERROR: エラーのためにセッションが終了
  • EXCEEDED_MAX_REPROMPTS: ユーザーの応答がないか、定義されたインテントに一致しなかかった

(2) error

errorは、エラーに関する詳細情報です。このオブジェクトには、次の2つのプロパティが含まれています。

  • type: 発生したエラーのタイプ
    • INVALID_RESPONSE
    • DEVICE_COMMUNICATION_ERROR
    • INTERNAL_ERROR
  • message: 詳細情報

4 SessionEndedRequestの料理法

それでは、セッションが終了してから送られてくるSessionEndedRequestで、何をすれば良いのでしょうか?

(1) 定義は必須

実は、中身は空っぽでも良いのですが、絶対に定義が必要です。

例えば、下記のように、SessionEndedRequestが定義されていなかった場合、SessionEndedRequestが送られてきた時にAlexa SDKがエラーとなってしまいます。

var handlers = {
    'LaunchRequest': function () { 
    this.emit(':ask','何か言って下さい', 'ちゃんと言って下さい');
    },
}
A: 何か言って下さい
U: ・・・・(無言)
A: ちゃんと言って下さい
U: ・・・・(無言)
A: スキルからの応答に問題あります
"request": {
    "type": "SessionEndedRequest",
    "reason": "EXCEEDED_MAX_REPROMPTS"
}

Lambdaでエラーが発生

errorMessage": "In state: . No handler function was defined for event SessionEndedRequest and no 'Unhandled' function was defined.",

空の定義があるだけで、この問題は発生しません。(Unhandledの定義がある場合も、この問題は発生しません)

var handlers = {
    'LaunchRequest': function () { 
    this.emit(':ask','何か言って下さい', 'ちゃんと言って下さい');
    },
     'SessionEndedRequest': function () { 
     // 特に何も実装しない
    }
}

(2) DynamoDBへの保存

this.attributes['key'] = value;

Alexa SDKでは、上記のように、attributesに書き込んだ内容は、セッション継続中ずっと保持されます。そして、セッション終了後(例えば次回起動時まで)その情報を保持したい場合は、簡単にDynamoDBが利用できるようになっています。

alexa.dynamoDBTableName = 'YourTableName'; 

しかし、DynamoDBへの情報の保存は、開発者がセッションを終了したタイミングで行われるため、ユーザーからの終了では、保存されません。

これを避けるた、DynamoDBを使用する場合は、下記の実装が必須となります。

'SessionEndedRequest': function () {
    this.emit(':saveState', true); // セッション情報をDynamoDBに保持する
},

(3) エラーログ

これは、必須ではありませんが、SessionEndedRequestに含まれるエラー情報をログに残すことは有効かもしれません。

'SessionEndedRequest': function () { 
    if (this.event.request.error) {
        const error = this.event.request.error;
        console.log("? type:" + error.type + " message:" + error.message);
    }
}

5 最後に

今回は、SessionEndedRequestについて確認してみました。

Alexaから送られてくるリクエストのうち、LaunchRequestや、IntentRequestは、その動作に大きく影響するため、充分に試行錯誤されている思いますが、もしかするとSessionEndedRequestは、ちょっと、軽視されている可能性は無いでしょうか?

SessionEndedRequestについても、しっかりと仕様を把握して実装すると、不要な嵌まりどころを減らす事ができるかも知れません。