このブログは英語の記事 3 Tips to Troubleshoot Your Custom Alexa Skill's Back End を一部翻訳、情報を追加しております。
Alexaスキルの開発では「スキルがリクエストに正しく応答できませんでした。」のエラーに遭遇することがあります。このエラーを解決するために、Alexaのリクエストについて理解する必要があります。AlexaはスキルのエンドポイントとHTTP over SSL/TLSで通信しています。ユーザーがスキルを起動するとき、スキルにはJSON形式のリクエストが送信されます。このリクエストには、スキルが処理を行って応答を生成するために必要なパラメータが含まれています。スキルが生成するJSON形式の応答はAlexaに対する応答の形式に従う必要があります。応答の形式が正しくない場合、Alexaは上記のエラーメッセージを返します。
このブログでは、スキルの応答でエラーが発生した際のおすすめのトラブルシューティング方法をご紹介します。使用するコードサンプルはAlexa Skills Kit (ASK) Software Development Kit (SDK)、Node.jsとPythonの例となります。
ログを出力し、いつどんなエラーが起きているのか状況を把握します。スキルのエンドポイントで何が起きているのかを知るためには、リクエスト毎に以下3つをログに出力します。
ASK SDKでは上記のログを出力するための便利な機能があります。
インターセプターはリクエストハンドラーが実行される直前、もしくは直後に呼び出されます。データベースからデータを取得するなど様々な用途がありますが、ここではリクエストと応答のログ出力に利用します。
/**
* Alexaからのリクエストをログに出力するインターセプター
*/
const LogRequestInterceptor = {
process(handlerInput) {
console.log("==== REQUEST ======");
console.log(JSON.stringify(handlerInput.requestEnvelope, null, 2));
}
}
/**
* Alexaへの応答をログに出力するインターセプター
*/
const LogResponseInterceptor = {
process(handlerInput, response) {
console.log("==== RESPONSE ======");
console.log(JSON.stringify(response, null, 2));
}
}
/**
* インターセプターの登録
*/
exports.handler = skillBuilder
.addRequestHandlers(...)
.addRequestInterceptors(LogRequestInterceptor)
.addResponseInterceptors(LogResponseInterceptor)
.lambda();
import logging
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
#Alexaからのリクエストをログに出力するインターセプター
class RequestLogger(AbstractRequestInterceptor):
def process(self, handler_input):
# type: (HandlerInput) -> None
logger.debug("Alexa Request: {}".format(
handler_input.request_envelope.request))
#Alexaへの応答をログに出力するインターセプター
class ResponseLogger(AbstractResponseInterceptor):
def process(self, handler_input, response):
# type: (HandlerInput, Response) -> None
logger.debug("Alexa Response: {}".format(response))
#インターセプターの登録
sb = SkillBuilder()
sb.add_global_request_interceptor(RequestLogger())
sb.add_global_response_interceptor(ResponseLogger())
lambda_handler = sb.lambda_handler()
エラーハンドラーはリクエストハンドラーと似ていますが、リクエスト処理の最中で問題が発生した場合のみSDKによって呼び出されます。イメージとして、すべてのリクエストハンドラーをtry...catch文で例外処理していると考えていいでしょう。グローバルエラーハンドラーですべてのエラーに対応するか、いくつかのエラーハンドラーを作成してエラー内容によって処理を変えることもできます。
/**
* リクエストハンドラーでのエラーを出力し、Alexaへ応答を返す
*/
const GlobalErrorHandler = {
canHandle(handlerInput, error) {
return true;
},
handle(handlerInput, error) {
// エラーをログに出力
console.log("==== ERROR ======");
console.log(error);
// Alexaへの応答
const speechText = "すみません、よく聞き取れませんでした。もう一度言ってください。";
return handlerInput.responseBuilder
.speak(speechText)
.reprompt(speechText)
.getResponse();
},
};
/**
* エラーハンドラーの登録
*/
exports.handler = Alexa.SkillBuilders.custom()
.addRequestHandlers(...)
.addErrorHandlers(
GlobalErrorHandler,
)
.addRequestInterceptors(LogRequestInterceptor)
.addResponseInterceptors(LogResponseInterceptor)
.lambda();
# リクエストハンドラーでのエラーを出力し、Alexaへ応答を返す
class CatchAllExceptionHandler(AbstractExceptionHandler):
def can_handle(self, handler_input, exception):
# type: (HandlerInput, Exception) -> bool
return True
def handle(self, handler_input, exception):
# type: (HandlerInput, Exception) -> Response
logger.error(exception, exc_info=True)
speak_output = "すみません、よく聞き取れませんでした。もう一度言ってください。"
return (
handler_input.response_builder
.speak(speak_output)
.ask(speak_output)
.response
)
# エラーハンドラーの登録
sb = SkillBuilder()
sb.add_exception_handler(CatchAllExceptionHandler())
lambda_handler = sb.lambda_handler()
スキルのエンドポイントがAlexa-hostedスキルかAWS Lambdaの場合、ログはAmazon CloudWatchに出力されます。Alexa-hostedスキルの場合はAlexa Developer Console > 「コードエディタ」> 「CloudWatch Logs」から確認することができます。AWS Lambdaの場合はAWS マネジメントコンソールから確認することができます。
Alexaは、ユーザーの操作によってスキルに異なるタイプのリクエストを送信します。スキルはすべてのタイプのリクエスト受け付けて、それぞれのリクエストのタイプに従って応答する必要があります。スキルに送信されるリクエストのタイプ一覧は標準のリクエストタイプのリファレンスを参照してください。通常一つのインテントにつき、一つのリクエストハンドラーを作成します。
エラーが起きないよう、すべてのタイプのリクエストを処理するリクエストハンドラー追加しようと考えているかもしれませんが、注意すべきことがあります。リクエストのタイプのよって正しい応答が異なります。すべてのリクエストに対し、応答で一律にoutputSpeechとカード、再プロンプトを含めることはできません。例えばユーザーが「終了」と言ったとき、もしくはスキルに応答しなかったとき、スキルにはSessionEndedRequestが送信されます。スキルは、SessionEndedRequestに応答を返すことはできません。正しい応答は空の応答となります。outputSpeechなどの応答を返した場合はエラーが発生します。
/**
* SessionEndedRequestを処理するハンドラー
* 何かの理由で進行中のスキルセッションが終了したときに送信されます
*/
const SessionEndedRequestHandler = {
canHandle(handlerInput) {
return handlerInput.requestEnvelope.request.type === 'SessionEndedRequest';
},
handle(handlerInput) {
// セッションが終了した原因をログに出力
const reason = handlerInput.requestEnvelope.request.reason;
console.log("==== SESSION ENDED WITH REASON ======");
console.log(reason);
// Alexaへの応答 (空の応答)
return handlerInput.responseBuilder.getResponse();
},
};
# SessionEndedRequestを処理するハンドラー
# 何かの理由で進行中のスキルセッションが終了したときに送信されます
class SessionEndedRequestHandler(AbstractRequestHandler):
def can_handle(self, handler_input):
# type: (HandlerInput) -> bool
return is_request_type("SessionEndedRequest")(handler_input)
def handle(self, handler_input):
# type: (HandlerInput) -> Response
# セッションが終了した原因をログに出力
logger.info("In SessionEndedRequestHandler")
logger.info("Session ended reason: {}".format(
handler_input.request_envelope.request.reason))
# Alexaへの応答 (空の応答)
return handler_input.response_builder.response
AudioPlayerインターフェースの応答でも同じです。このインターフェースはオーディオをストリーミングしたり再生状況を監視したりするディレクティブとリクエストを提供します。このインターフェースのほとんどのリクエストに対して、スキルはディレクティブを返します。outputSpeechやカード、再プロンプトなどの応答を返した場合はエラーが発生します。
空の応答が必要なリクエスト:
outputSpeechやカード、再プロンプトなどの応答を返せないリクエスト:
Alexa Developer Consoleのテストタブからスキルのテストを有効にすると、同じ開発者アカウントに登録されているEchoデバイスでスキルをテストすることができます。実際のユーザーと同じ環境でスキルを体験することができます。ここでAlexaアプリを活用することをおすすめします。
Alexaアプリから発話の履歴を確認することができます。実際の声をAlexaがどのように聞き取ったのかを確認し、音声インターフェースの改善に役立てることができます。
モバイルアプリでの確認方法:
ブラウザでの確認方法:
カードは音声インターフェースをサポートする視覚的な要素です。Alexaアプリにカードを送るための詳細はスキルの応答にカードを追加するをご覧ください。
モバイルアプリでの確認方法:
スキルでエラーが発生した時、Alexaはカードを送ることがあります。例えば、「スキルがリクエストに正しく応答できませんでした。」のエラーが発生した時、カードのエラーメッセージがデバッグのヒントとなることがあります。以下の例では、「Invalid SSML Output Speech」とあり、SSMLの形式が正しくないことを意味します。
このブログの1番でご紹介したログを出力している場合はこのリクエストのログを確認してデバッグすることができます。他のエラーカードや考えられる原因はカスタムスキルのテストとデバッグをご覧ください。