スキルセッションとセッションアトリビュートの管理
ユーザーとの対話のやり取りを行う間、スキルではスキルセッションを開いたままにしておくことができます。セッションが開いている間、ユーザーは呼び出し名を使わなくてもスキルに話しかけることができます。
スキルセッションのライフサイクル
-
スキルセッションは、ユーザーがスキルを呼び出してAlexaがスキルにリクエストを送信すると開始します。リクエストにはsessionオブジェクトが含まれます。このオブジェクトには、新規のセッションを表す
new
というブール値が入ります。 -
スキルはリクエストを受け取り、Alexaがユーザーに答える応答を返します。
-
次に行われる処理は、スキルが返す応答の
shouldEndSession
パラメーターに入る値によって異なります。true
– セッションを終了します。それ以降は、スキルではなくAlexaがユーザーの発話を処理します。ユーザーが再度スキルを呼び出すと、Alexaは新規のセッションを作成します。手順1に戻ります。-
false
またはnull
– セッションは開いたままです。Alexaはマイクをオンにしてユーザーからスキルへの応答を待ちます。ユーザーの応答が定義済みの対話モデルにマッピングされると、Alexaは新しいインテントをスキルに送信します(ステップ2に戻ります)。ただし、数秒待ってもユーザーからの応答がない場合、Alexaはマイクをオフにします。スキルが再プロンプトを指定した場合、Alexaは再度ユーザーに話しかけ、さらに数秒間マイクをオンにします。それでもユーザーが応答しない場合、セッションは終了します。
画面付きのデバイスでスキルを使う場合、マイクがオフになった後も、短時間セッションが開いたままになる場合があります。これについては、画面付きデバイスがスキルセッションに与える影響を参照してください。
また、唯一の例外はスキル内課金の購入フローを開始するディレクティブです。この場合、
shouldEndSession
の値にかかわらず、ディレクティブが自動でセッションを終了します。購入フローが完了した後にスキルを再開するには、永続ストレージを使う必要があります。詳細については、スキルコードにISPのサポートを追加するを参照してください。 undefined
(未定義) – セッションの動作は、Echoデバイスのタイプによって異なります。デバイスに画面があり、スキルの応答に視覚コンテンツが含まれている場合、セッションは最大30秒間開いたままになります。ただし、マイクをオンにしてユーザーに入力を求めることはありません。詳細については、画面付きデバイスがスキルセッションに与える影響を参照してください。ユーザーがリクエストの前にウェイクワードを付けて話しかけると(「アレクサ」など)Alexaはリクエストをスキルに送信します。それ以外の場合、Alexaはユーザーの音声を無視します。Alexa Gadgetsイベントハンドラーが有効な場合、スキルがCustomInterfaceController.StopEventHandler
を呼び出すまで、あるいはイベントハンドラーの有効期限が切れない限り、セッションは開いたままになります。
画面付きデバイスがスキルセッションに与える影響
ユーザーが画面付きデバイスを使ってスキルを呼び出した場合、マイクがオフになった後も最大30秒間は画面が開いたままになります。このため、ユーザーは引き続き画面に表示されるスキル関連のコンテンツを見ることができます。また、ユーザーは、Alexaに呼びかけるウェイクワードに続けてスキルの対話モデルにマッピングされる発話を行うことで、スキルとの対話を続けることができます。
このようなセッションの延長は、次のすべての条件を満たす場合に行われます。
- ユーザーが画面付きデバイス(Echo Showなど)を使ってスキルを呼び出した場合。
- スキルは、画面付きのデバイスをサポートします。画面付きデバイスに対応するには、開発者コンソールの ビルド > インターフェースページで次のいずれかのオプションを有効にします。
- Alexa Presentation Language(APL)の概要
- Displayインターフェース(非推奨)
- スキルの応答に画面に表示するコンテンツが含まれる場合。視覚コンテンツは、次のいずれかで提供できます。
- Alexa Presentation Languageドキュメントは
Alexa.Presentation.APL.RenderDocument
ディレクティブを返すと表示されます。 - Alexaアプリカードは、応答に
card
オブジェクトが含まれる場合に表示されます。カードは本来Alexaアプリ用ですが、画面に表示するコンテンツがほかにない場合はカードコンテンツが表示されます。 - Displayテンプレートは
Display.RenderTemplate
ディレクティブを返すと表示されます。
- Alexa Presentation Languageドキュメントは
- 応答の
shouldEndSession
値がfalse
、undefined
(未設定)のいずれかの場合。undefined
の場合、延長セッションが約30秒間開いたままになります。false
の場合、ユーザーに再度話しかけても応答がなかったときに延長セッションが始まります。この場合、セッションは20~30秒間開いたままとなります。
たとえば、画面付きデバイスをサポートし、画面にコンテンツを表示するスキルでは、画面付きデバイスで次のような対話が行われる可能性があります。
ユーザー: 宇宙博士を開いて。
スキルはLaunchRequest
を受け取ると、テキストを読み上げて応答し、shouldEndSession
をfalse
にセットします。
Alexa: 宇宙博士へようこそ。私は、ある惑星からほかの惑星まで旅するのにかかる時間から、宇宙についてのジョークまで、宇宙に関するいろいろなことを知っています。何を知りたいですか?
画面には宇宙博士に関連するコンテンツ(RenderDocument
ディレクティブで送られたAPLドキュメントなど)が表示されます。
ユーザー: …(ユーザーは何も答えず、数秒が経過します。)
Alexa: 宇宙について私に何か聞いてください。 (Alexaは応答に指定されたreprompt
を読み上げます。)
ユーザー: えーっと…
さらに時間が経過します。
マイクはオフになりますが、セッションは開いたままです。宇宙博士に関するコンテンツは引き続き表示されます。
ユーザー: アレクサ、火星について教えて。 (スキルセッションがまだ有効なため、ユーザーは呼び出し名を使わずにスキルと対話します。)
AlexaはスキルにPlanetFacts
インテントを送信します。リクエストはこれが新しいセッションではなく、継続セッションであることを表しています。
Alexa: 火星では…
セッションデータの保存
セッションデータを保存する必要がある場合は、セッションアトリビュートを使います。保存するデータでキーと値のペアのマップを作成します。このマップを応答のsessionAttributes
プロパティに含めます。Alexaが同じセッションの一部として次のリクエストを送信すると、リクエストのsession.attributes
プロパティには同じマップが含まれます。定義したキーを使って、マップのデータを取得します。
たとえば以下のようにします。
…これ以前の対話が行われています。
ユーザー: お気に入りの色は青色です
Alexaは、favoriteColor
スロットに「青色」をセットしてスキルにFavoriteColorIntent
を送信します。スキルはテキストを読み上げて応答し、キーがfavoriteColor
のセッションアトリビュートの値を「青色」にセットします。</em>
Alexa: わかりました。お気に入りの色は青色ですね。
ユーザー: もう一度、何色か教えてくれる?
AlexaはスキルにWhatsMyColorIntent
インテントを送信します。リクエストには、favoriteColor
セッションアトリビュートが含まれます。スキルはこのアトリビュートを取得して応答を作成します。
Alexa: あなたのお気に入りの色は青色です。
Alexa Skills Kit SDKが提供するAttributesManager
を使えば、応答にセッションアトリビュートを追加して、受信リクエストからアトリビュートを取得できます。
以下は、インテントハンドラーがデータをセッションアトリビュートに保存する方法の例です。この例では、FavoriteColorIntent
にfavoriteColor
という必須スロットが1つあります。インテントには自動デリゲートが設定されているため、ユーザーが最初に値を提供しなかった場合はAlexaがユーザーにスロットの値をたずねます。
このサンプルコードはAlexa Skills Kit SDK for Node.js (v2)を使用しています。
const FavoriteColorIntentHandler = {
canHandle(handlerInput) {
getRequestType(handlerInput.requestEnvelope) === 'IntentRequest'
&& getIntentName(handlerInput.requestEnvelope) === 'FavoriteColorIntent'
&& getDialogState(handlerInput.requestEnvelope) === 'COMPLETED';
},
handle(handlerInput) {
const sessionAttributes = handlerInput.attributesManager.getSessionAttributes();
const favoriteColor = getSlotValue(handlerInput.requestEnvelope, 'favoriteColor')
sessionAttributes.favoriteColor = favoriteColor;
handlerInput.attributesManager.setSessionAttributes(sessionAttributes);
const speechText = `セッションアトリビュートに値${favoriteColor}を保存しました。
アトリビュートを取得しますのであなたのお気に入りの色を私に聞いてください。`;
const repromptText = `私のお気に入りの色は何、と聞いてみてください。`;
return handlerInput.responseBuilder
.speak(speechText)
.reprompt(repromptText)
.getResponse();
}
};
このサンプルコードはAlexa Skills Kit SDK for Pythonを使用しています。
from ask_sdk_core.dispatch_components import AbstractRequestHandler
from ask_sdk_core.utils import is_intent_name, get_dialog_state, get_slot_value
from ask_sdk_core.handler_input import HandlerInput
from ask_sdk_model import Response, DialogState
class FavoriteColorIntentHandler(AbstractRequestHandler):
"""FavoriteColorIntentのハンドラー"""
def can_handle(self, handler_input):
# type: (HandlerInput) -> bool
return is_intent_name("FavoriteColorIntent")(
handler_input) and get_dialog_state(
handler_input=handler_input) == DialogState.COMPLETED
def handle(self, handler_input):
# type: (HandlerInput) -> Response
# 受信リクエストから既存アトリビュートを取得します
session_attr = handler_input.attributes_manager.session_attributes
# リクエストからスロット値を取得し、セッションに追加します
# アトリビュートディクショナリーに追加します。ダイアログモデルとダイアログの
# デリゲートのため、このコードはfavoriteColorスロットに
# 値が含まれているときにだけ実行されます。nullチェックは必要ありません。
fav_color = get_slot_value("favoriteColor")
session_attr["favoriteColor"] = fav_color
# SDKは次のインテントで値を使用できるように、
# アトリビュートをセッションに自動で保存します
speech_text = ("{}の値をセッションアトリビュートに保存しました。"
"アトリビュートを取得しますので、あなたのお気に入りの色を"
"私に聞いてください。").format(fav_color)
reprompt_text = "私のお気に入りの色は何、と聞いてみてください。"
return handler_input.response_builder.speak(speech_text).ask(
reprompt_text).response
このサンプルコードはAlexa Skills Kit SDK for Javaを使用しています。
package handlers;
import static com.amazon.ask.request.Predicates.intentName;
import java.util.Map;
import java.util.Optional;
import com.amazon.ask.attributes.AttributesManager;
import com.amazon.ask.dispatcher.request.handler.HandlerInput;
import com.amazon.ask.dispatcher.request.handler.impl.IntentRequestHandler;
import com.amazon.ask.model.DialogState;
import com.amazon.ask.model.IntentRequest;
import com.amazon.ask.model.Response;
import com.amazon.ask.request.RequestHelper;
public class FavoriteColorIntentHandler implements IntentRequestHandler {
@Override
public boolean canHandle(HandlerInput handlerInput, IntentRequest intentRequest) {
// このインテントには必須スロットと自動デリゲートが設定されているため、
// ハンドラーは完了したダイアログを処理するだけです。
return handlerInput.matches(intentName("FavoriteColorIntent"))
&& intentRequest.getDialogState() == DialogState.COMPLETED;
}
@Override
public Optional<Response> handle(HandlerInput handlerInput, IntentRequest intentRequest) {
RequestHelper requestHelper = RequestHelper.forHandlerInput(handlerInput);
// 受信リクエストから既存のアトリビュートを取得します
AttributesManager attributesManager = handlerInput.getAttributesManager();
Map<String,Object> attributes = attributesManager.getSessionAttributes();
// リクエストからスロット値を取得し、アトリビュートのマップに追加します。
// ダイアログモデルとダイアログのデリゲートのため、このコードは
// favoriteColorスロットに値が含まれる場合にのみ実行されます。nullチェックは
// 必要ありません。
Optional<String> favoriteColor = requestHelper.getSlotValue("favoriteColor");
attributes.put("favoriteColor", favoriteColor.get());
// これによりアトリビュートがセッションに保存されるので、値は
// 次のインテントで使用できます。
attributesManager.setSessionAttributes(attributes);
// 応答に再プロンプトを含めて、自動で
// shouldEndSessionをfalseにセットします。
return handlerInput.getResponseBuilder()
.withSpeech("値" + favoriteColor.get() +
"をセッションアトリビュートに保存しました。アトリビュートを取得しますので、" +
"あなたのお気に入りの色を私に聞いてください。" )
.withReprompt("私のお気に入りの色は何、と聞いてみてください。")
.build();
}
}
以下は、FavoriteColorIntentHandler
が送信するJSON応答です。sessionAttributes
オブジェクトにはfavoriteColor
アトリビュートが含まれています。
{
"version": "1.0",
"sessionAttributes": {
"favoriteColor": "青"
},
"response": {
"outputSpeech": {
"type": "SSML",
"ssml": "<speak>青の値をセッションアトリビュートに保存しました。アトリビュートを取得しますのであなたのお気に入りの色を私に聞いてください。</speak>"
},
"reprompt": {
"outputSpeech": {
"type": "SSML",
"ssml": "<speak>私のお気に入りの色は何、と聞いてみてください。</speak>"
}
},
"shouldEndSession": false
}
}
以下は、インテントハンドラーがセッションアトリビュートに保存されたデータにアクセスする方法の例です。この例のWhatsMyColorIntent
にはスロットがありません。セッションアトリビュートに以前保存したデータを取得して、応答に使います。データがまだない場合(ユーザーがFavoriteColorIntent
を呼び出す前にこのインテントを呼び出したため)、ハンドラーはDialog.ElicitSlot
ディレクティブを使ってFavoriteColorIntent
を呼び出し、ユーザーに足りないデータについてたずねます。
このサンプルコードはAlexa Skills Kit SDK for Node.js (v2)を使用しています。
const WhatsMyColorIntentHandler = {
canHandle(handlerInput) {
getRequestType(handlerInput.requestEnvelope) === 'IntentRequest'
&& getIntentName(handlerInput.requestEnvelope) === 'WhatsMyColorIntent';
},
handle(handlerInput) {
const sessionAttributes = handlerInput.attributesManager.getSessionAttributes();
if (sessionAttributes.favoriteColor) {
return handlerInput.responseBuilder
.speak(`あなたのお気に入りの色は${sessionAttributes.favoriteColor}です`)
.getResponse();
} else {
return handlerInput.responseBuilder
.speak('最初に、あなたのお気に入りの色を私に教えてください。')
.reprompt('あなたのお気に入りの色は何ですか?')
.addElicitSlotDirective('favoriteColor')
.getResponse();
}
}
}
このサンプルコードはAlexa Skills Kit SDK for Pythonを使用しています。
from ask_sdk_core.dispatch_components import AbstractRequestHandler
from ask_sdk_core.utils import is_intent_name, get_slot_value
from ask_sdk_core.handler_input import HandlerInput
from ask_sdk_model import Response, Intent
from ask_sdk_model.dialog import ElicitSlotDirective
class WhatsMyColorIntentHandler(AbstractRequestHandler):
"""WhatsMyColorIntentのハンドラー"""
def can_handle(self, handler_input):
# type: (HandlerInput) -> bool
return is_intent_name("WhatsMyColorIntent")(handler_input)
def handle(self, handler_input):
# type: (HandlerInput) -> Response
session_attr = handler_input.attributes_manager.session_attributes
# ユーザーはお気に入りの色を設定する前にこのインテントを呼び出す可能性があります。
# セッションアトリビュートをまずチェックしてください。
if "favoriteColor" in session_attr:
fav_color = session_attr["favoriteColor"]
return handler_input.response_builder.speak(
"あなたのお気に入りの色は{}です。さようなら".format(
fav_color)).set_should_end_session(True).response
else:
# ユーザーはお気に入りの色を設定する前にこのインテントを呼び出したようです。
# FavoriteColorIntentをトリガーしてユーザーに
# favoriteColorスロットの値をたずねます。ElicitSlotディレクティブを使う場合、スキルには*ダイアログモデル*
# が必要です。
return handler_input.response_builder.speak(
"最初に、あなたのお気に入りの色を私に教えてください。").ask(
"あなたのお気に入りの色は何ですか?").add_directive(
directive=ElicitSlotDirective(
updated_intent=Intent(
name="FavoriteColorIntent"),
slot_to_elicit="favoriteColor")).response
このサンプルコードはAlexa Skills Kit SDK for Javaを使用しています。
package handlers;
import static com.amazon.ask.request.Predicates.intentName;
import java.util.Map;
import java.util.Optional;
import com.amazon.ask.attributes.AttributesManager;
import com.amazon.ask.dispatcher.request.handler.HandlerInput;
import com.amazon.ask.dispatcher.request.handler.impl.IntentRequestHandler;
import com.amazon.ask.model.Intent;
import com.amazon.ask.model.IntentRequest;
import com.amazon.ask.model.Response;
public class WhatsMyColorIntentHandler implements IntentRequestHandler {
@Override
public boolean canHandle(HandlerInput handlerInput, IntentRequest intentRequest) {
return handlerInput.matches(intentName("WhatsMyColorIntent"));
}
@Override
public Optional<Response> handle(HandlerInput handlerInput, IntentRequest intentRequest) {
AttributesManager attributesManager = handlerInput.getAttributesManager();
Map <String,Object> attributes = attributesManager.getSessionAttributes();
// ユーザーはお気に入りの色を設定する前にこのインテントを呼び出す可能性があります。
// セッションアトリビュートをまずチェックしてください。
if (attributes.containsKey("favoriteColor")){
String favoriteColor = attributes.get("favoriteColor").toString();
return handlerInput.getResponseBuilder()
.withSpeech("あなたのお気に入りの色は" + favoriteColor + "です。さようなら")
.withShouldEndSession(true)
.build();
} else {
// ユーザーはお気に入りの色を設定する前にこのインテントを呼び出したようです。
// FavoriteColorIntentをトリガーしてユーザーに
// favoriteColorスロットの値をたずねます。ElicitSlotディレクティブを使う場合、スキルには*ダイアログモデル*が
// が必要です。
// インテントを作成します。
Intent intent = Intent.builder()
.withName("FavoriteColorIntent")
.build();
return handlerInput.getResponseBuilder()
.withSpeech("最初に、あなたのお気に入りの色を私に教えてください。")
.withReprompt("あなたのお気に入りの色は何ですか?")
.addElicitSlotDirective("favoriteColor", intent)
.build();
}
}
}
以下のJSONは、ユーザーが色の名前を既に提供している場合に受け取ったIntentRequest
を表しています。値はsession.attributes
に保存されています。
{
"version": "1.0",
"session": {
"new": false,
"sessionId": "amzn1.echo-api.session.1",
"application": {
"applicationId": "amzn1.ask.skill.1"
},
"attributes": {
"favoriteColor": "青"
},
"user": {
"userId": "amzn1.ask.account.1"
}
},
"context": {},
"request": {
"type": "IntentRequest",
"requestId": "amzn1.echo-api.request.1",
"timestamp": "2019-02-13T04:22:36Z",
"locale": "ja-JP",
"intent": {
"name": "WhatsMyColorIntent",
"confirmationStatus": "NONE"
}
}
}
次のようなシナリオでは、セッションアトリビュートが有効です。
- さまざまなスキル状態を処理するためにデータをトラッキングします。たとえば、ユーザーが既にゲームを開始しているか、新しいゲームを始められる状態かを示す
state
アトリビュートなどです。アトリビュートを使用して、コードでハンドラーが特定のリクエストを処理できるかどうかを判断します。これは、スキルがユーザーに複数の「はい」か「いいえ」で答えられる質問をする場合に特に有効です。「はい」や「いいえ」がスキルの状態によって違う意味を持つ場合があるからです。 - ゲームのスコアやカウンターを追跡します。
- 追加の値をユーザーにたずねる際に、ユーザーが提供したスロット値を保存します。ダイアログモデルとダイアログのデリゲートを使って、セッションアトリビュートを使わずに行う方法もあります。Alexaにダイアログをデリゲートするを参照してください。
セッションアトリビュートの構造
キーと値のペアのマップとして構成できるデータであれば、より複雑なデータをセッションアトリビュートに渡すこともできます。たとえば、クイズゲームのスキルには、ゲームの現在のステータス、ユーザーの現在のスコア、今出題した質問の正解などをトラッキングするアトリビュートが必要になります。この例のquizitem
アトリビュートは、現在の質問の正解を表します。
{
"sessionAttributes": {
"quizscore": 0,
"quizproperty": "STATEHOOD_YEAR",
"response": "さて、 これからアメリカ合衆国に関する問題を10問出します。",
"state": "_QUIZ",
"counter": 1,
"quizitem": {
"name": "ネバダ",
"abbreviation": "エヌブイ",
"capital": "カーソンシティ",
"statehoodYear": "1864年",
"statehoodOrder": "36番目"
}
}
}
ゲーム状態に関するより複雑なセッションアトリビュートのサンプル全体については、クイズゲームのサンプルスキルを参照してください。
セッション間データの保存
セッションアトリビュートは、セッションが開いている間存在します。セッションが終了すると、そのセッションに関連するすべてのアトリビュートは失われます。複数のセッションにまたがってデータを記憶しておく必要がある場合、データをDynamoDBやS3などの永続ストレージに保存する必要があります。
ユーザーを識別するには、各リクエストで提供されるuserId
を使用します。指定したユーザーの userId
は、ユーザーがAlexaアプリでスキルを有効にしたときに生成されます。ユーザーがスキルを無効にしない限り、そのユーザーからスキルへの後続のすべてのリクエストには同じ userId
が含まれます。
スキルセッション間でのアトリビュートの保持は、ASK SDKs for Node.js、Java、およびPythonに組み込まれています。詳細については、アトリビュートの管理に関するASK SDKドキュメントを参照してください。
ユーザーアトリビュートの保持に関するサンプルスキルについては、ハイ&ローゲームを参照してください。
関連トピック
最終更新日: 2021 年 12 月 21 日