カスタムスキルのトラブルシューティング

Chihiro Tatara Aug 03, 2021
Share:
Tips & Tools Test Tutorial
Blog_Header_Post_Img

このブログは英語の記事 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の例となります。

line
line

1. ログを出力する

ログを出力し、いつどんなエラーが起きているのか状況を把握します。スキルのエンドポイントで何が起きているのかを知るためには、リクエスト毎に以下3つをログに出力します。

  • Alexaから送信されたJSONのリクエスト
  • スキルが送り返したJSONの応答
  • 応答を生成する過程で起きたエラー

ASK SDKでは上記のログを出力するための便利な機能があります。

リクエストと応答のインターセプター

インターセプターはリクエストハンドラーが実行される直前、もしくは直後に呼び出されます。データベースからデータを取得するなど様々な用途がありますが、ここではリクエストと応答のログ出力に利用します。

Node.jsの例:
Copied to clipboard
/**
 * 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();
Pythonの例:
Copied to clipboard
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文で例外処理していると考えていいでしょう。グローバルエラーハンドラーですべてのエラーに対応するか、いくつかのエラーハンドラーを作成してエラー内容によって処理を変えることもできます。

グローバルエラーハンドラー - Node.jsの例:
Copied to clipboard
/**
 * リクエストハンドラーでのエラーを出力し、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();
グローバルエラーハンドラー - Pythonの例:
Copied to clipboard
# リクエストハンドラーでのエラーを出力し、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 マネジメントコンソールから確認することができます。

line

2. すべてのタイプのリクエストを処理できることを確認

すべてのタイプのリクエストを処理する

Alexaは、ユーザーの操作によってスキルに異なるタイプのリクエストを送信します。スキルはすべてのタイプのリクエスト受け付けて、それぞれのリクエストのタイプに従って応答する必要があります。スキルに送信されるリクエストのタイプ一覧は標準のリクエストタイプのリファレンスを参照してください。通常一つのインテントにつき、一つのリクエストハンドラーを作成します。

レスポンスのJSONフォーマットが正しいことを確認する

エラーが起きないよう、すべてのタイプのリクエストを処理するリクエストハンドラー追加しようと考えているかもしれませんが、注意すべきことがあります。リクエストのタイプのよって正しい応答が異なります。すべてのリクエストに対し、応答で一律にoutputSpeechとカード、再プロンプトを含めることはできません。例えばユーザーが「終了」と言ったとき、もしくはスキルに応答しなかったとき、スキルにはSessionEndedRequestが送信されます。スキルは、SessionEndedRequestに応答を返すことはできません。正しい応答は空の応答となります。outputSpeechなどの応答を返した場合はエラーが発生します。

Node.jsの例:
Copied to clipboard
/**
 * 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();
  },
};
Pythonの例:
Copied to clipboard
# 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やカード、再プロンプトなどの応答を返せないリクエスト:

line

3. Alexaアプリを活用

Alexa Developer Consoleのテストタブからスキルのテストを有効にすると、同じ開発者アカウントに登録されているEchoデバイスでスキルをテストすることができます。実際のユーザーと同じ環境でスキルを体験することができます。ここでAlexaアプリを活用することをおすすめします。

発話の履歴を確認する

Alexaアプリから発話の履歴を確認することができます。実際の声をAlexaがどのように聞き取ったのかを確認し、音声インターフェースの改善に役立てることができます。

モバイルアプリでの確認方法:

  1. Alexaアプリを開く
  2. 「その他」> 「設定」> 「Alexaプライバシー」> 「音声履歴を確認」


ブラウザでの確認方法:

  1. https://www.amazon.co.jp/alexaprivacysettings を開く
  2. 「音声履歴を確認」

カードを確認する

カードは音声インターフェースをサポートする視覚的な要素です。Alexaアプリにカードを送るための詳細はスキルの応答にカードを追加するをご覧ください。

モバイルアプリでの確認方法:

  1. Alexaアプリを開く
  2. 「その他」> 「アクティビティ 」


スキルでエラーが発生した時、Alexaはカードを送ることがあります。例えば、「スキルがリクエストに正しく応答できませんでした。」のエラーが発生した時、カードのエラーメッセージがデバッグのヒントとなることがあります。以下の例では、「Invalid SSML Output Speech」とあり、SSMLの形式が正しくないことを意味します。

card_error

このブログの1番でご紹介したログを出力している場合はこのリクエストのログを確認してデバッグすることができます。他のエラーカードや考えられる原因はカスタムスキルのテストとデバッグをご覧ください。

line

関連記事

Subscribe