手順3: AlexaディレクティブとLambdaのレスポンスを理解する
前のセクション(手順2: マルチモーダルデバイスでビデオスキルを有効化してテストする)では、AlexaクラウドからLambda関数にリクエストを送信し、Amazon Cloudwatchでリクエストとレスポンスのログを確認しました。Amazon Cloudwatchでログを表示し、Alexaが送信するディレクティブとLambda関数から返されるレスポンスを確認したので、ここではこのコードに踏み込んで、何が行われているかを詳しく説明します。
基本的なワークフロー
マルチモーダルデバイスに対してユーザーが「<メディアのタイトル>を再生して」などのフレーズを言うと、アーキテクチャの概要で説明しているワークフローが開始されます。Alexaでは、自然言語処理技術によりユーザーの発話を解析してユーザーのインテントを判断します。そして、ユーザーのインテントをパッケージ化してディレクティブに変換します。マルチモーダルデバイスとのやり取りでは、次のディレクティブが使用されます。
- GetPlayableItems
- GetPlayableItemsMetadata
- GetBrowseNodeItems
- GetDisplayableItems
- GetDisplayableItemsMetadata
- GetBrowseNodeItems
- GetNextPage
検索、再生、チャンネル変更など、シナリオに応じて異なるディレクティブが送信されます。このシナリオと送信されるディレクティブについては、Alexaから送信されるディレクティブおよびリファレンスドキュメントに記載されています。
ユーザーが「『マンチェスター・バイ・ザ・シー』の映画を再生して」と言うと、Alexaはそのメディアが含まれているカタログをすべて特定し、GetPlayableItemsディレクティブをそのカタログに適したLambda関数に送信します。GetPlayableItemsディレクティブの例を次に示します。
Alexaのリクエスト: GetPlayableItems
{
    "directive": {
        "header": {
            "correlationToken": "dFMb0z+PgpgdDmluhJ1LddFvSqZ/jCc8ptlAKulUj90jSqg==",
            "messageId": "9f4803ec-4c94-4fdf-89c2-d502d5e52bb4",
            "name": "GetPlayableItems",
            "namespace": "Alexa.VideoContentProvider",
            "payloadVersion": "3"
        },
        "endpoint": {
            "scope": {
                "type": "BearerToken",
                "token": "access-token-from-skill"
            },
            "endpointId": "videoDevice-001",
            "cookie": {
            }
        },
        "payload": {
            "entities": [
                {
                    "type": "Video",
                    "value": "マンチェスター・バイ・ザ・シー",
                    "externalIds": {
                        "imdb": "tt4574334"
                    }
                }
            ],
            "contentType": "RECORDING",
            "locale": "ja-JP",
            "minResultLimit": 8,
            "maxResultLimit": 25,
            "timeWindow": {
                "end": "2016-09-07T23:59:00+00:00",
                "start": "2016-09-01T00:00:00+00:00"
            }
        }
    }
}
ディレクティブ名はheaderブロックにあります。したがって、GetPlayableItemsディレクティブであることがわかります。
Lambdaはこのディレクティブをイベントとして受け取ります。Lambdaコードでは、(ディレクティブのpayloadに基づいて)必要な検索を実行し、リクエストに該当するメディアタイトルを特定する必要があります。この検索の過程で、ユーザーのリクエストと関連のあるメディアタイトルが複数見つかる場合があります。
次に、そのディレクティブタイプの要件を満たすレスポンスをLambdaがAlexaに返します。GetPlayableItemsディレクティブに対するGetPlayableItemsResponseの例を次に示します。
Lambdaのレスポンス: GetPlayableItemsResponse
{
    "event": {
        "header": {
            "correlationToken": "dFMb0z+PgpgdDmluhJ1LddFvSqZ/jCc8ptlAKulUj90jSqg==",
            "messageId": "5f0a0546-caad-416f-a617-80cf083a05cd",
            "name": "GetPlayableItemsResponse",
            "namespace": "Alexa.VideoContentProvider",
            "payloadVersion": "3"
        },
        "payload": {
            "nextToken": "fvkjbr20dvjbkwOpqStr",
            "mediaItems": [
                {
                    "mediaIdentifier": {
                        "id": "recordingId://provider1.dvr.rp.1234-2345-63434-asdf"
                    }
                },
                {
                    "mediaIdentifier": {
                        "id": "recordingId://provider1.dvr.rp.1234-2345-63434-asdf"
                    }
                }
            ]
        }
    }
}
このコードでは、ユーザーのリクエストに該当するmediaIdentifierの値が2種類あります。これらのmediaIdentifierプロパティの値は、カタログのコンテンツIDに対応しています。
Alexaでは、GetPlayableItemsResponseレスポンスを受信した後、ユーザーが再生したいのはどのメディアタイトルかをユーザーに確認します。また、同じメディアが複数のビデオプロバイダーから提供されている場合は、どのプロバイダーからメディアを再生したいかをユーザーに確認します。
ユーザーが再生したいメディアを解決できたら、AlexaからLambdaにGetPlayableItemsMetadataという別のディレクティブを送信します。このディレクティブは、選択されたメディアタイトルの詳細情報をLambdaに要求します。この情報は、Alexaでそのメディアタイトルに関する情報を表示する際に必要となります(recordingId://provider1.dvr.rp.1234-2345-63434-asdfといったmediaIdentifierの値はAlexaでは認識できません)。このmediaIdentifierに関する情報(タイトル、サムネイル、視聴時間、評価など、ユーザーの画面に表示する情報)を提供する必要があります。
Alexaから送信されるGetPlayableItemsMetadataの例を次に示します。
Alexaのリクエスト: GetPlayableItemsMetadata
{
    "directive": {
        "header": {
            "correlationToken": "dFMb0z+PgpgdDmluhJ1LddFvSqZ/jCc8ptlAKulUj90jSqg==",
            "messageId": "0f918d6e-ebae-48f1-a237-13c6f5b9f5da",
            "name": "GetPlayableItemsMetadata",
            "namespace": "Alexa.VideoContentProvider",
            "payloadVersion": "3"
        },
        "endpoint": {
            "scope": {
                "type": "BearerToken",
                "token": "access-token-from-skill"
            },
            "endpointId": "videoDevice-001",
            "cookie": {
            }
        },
        "payload": {
            "locale": "ja-JP",
            "mediaIdentifier": {
                    "id": "recordingId://provider1.dvr.rp.1234-2345-63434-asdf"
                }
        }
    }
}
ここでは、payloadでmediaIdentifier(ユーザーが再生したいメディアの識別子)が指定されています。
Lambdaでは、このmediaIdentifierに関する必要な情報を取得し、その詳細をGetPlayableItemsMetadataResponseレスポンスで返します。このレスポンスの例を次に示します。
Lambdaのレスポンス: GetPlayableItemsMetadataResponse
{
    "event": {
        "header": {
            "correlationToken": "dFMb0z+PgpgdDmluhJ1LddFvSqZ/jCc8ptlAKulUj90jSqg==",
            "messageId": "38ce5b22-eeff-40b8-a84f-979446f9b27e",
            "name": "GetPlayableItemsMetadataResponse",
            "namespace": "Alexa.VideoContentProvider",
            "payloadVersion": "3"
        },
        "payload": {
            "searchResults": [
                {
                    "name": "インターステラー",
                    "contentType": "ON_DEMAND",
                    "series": {
                        "seasonNumber": "1",
                        "episodeNumber": "1",
                        "seriesName": "ビッグバン・セオリー",
                        "episodeName": "パイロット"
                    },
                    "playbackContextToken": "{\"streamUrl\": \"http:\/\/samplemediasite.com\/sample\/video.mp4\", \"title\": \"<ビデオタイトル>\"}",
                    "parentalControl": {
                        "pinControl": "REQUIRED"
                    },
                    "absoluteViewingPositionMilliseconds": 1232340
                }
            ]
        }
    }
}
その後、AlexaからウェブプレーヤーにplaybackContextTokenが渡されます。ウェブプレーヤーでは、この識別子がメディア再生URLに変換され、メディアが読み込まれます。
やり取りのたとえ
具体的にイメージできるように、たとえ話でこのやり取りを説明しましょう。ビデオ店に客が入ってきて、カウンターの店員に「『アラバマ物語』(原題:To Kill a Mockingbird)を観たいのですが」と言ったとします。 店員はこのリクエストを裏方の担当者に伝えます。裏方の担当者は、メディアライブラリを調べて「Mockingbird」のメディアセクションを見つけます。すると、このメディアに該当するものが複数あることがわかりました。エディションやバリエーションがそれぞれ異なり、グレゴリー・ペック主演のもの以外にも、「ハンガー・ゲームFINAL:レジスタンス」(The Hunger Games: Mockingjay)という、原題に関連のあるものも見つかりました。
裏方の担当者がこの情報をカウンターの店員に伝えると、店員は客に「お探しのメディアタイトルはどちらでしょうか」とたずねます。 客は「最初のグレゴリー・ペック主演のものです」と答えます。 店員は裏方の担当者に向かって、客はグレゴリー・ペック主演のメディアタイトルを希望していると伝えます。裏方の担当者は、グレゴリー・ペックの作品の詳細情報をすべて確認し、その情報を店員に返します。店員は、メディアをプレーヤーに読み込んで客のために再生します。
つまり、ユーザーのリクエストはGetPlayableItemsディレクティブに変換され、Lambdaに送信されます。Lambdaからは、該当するタイトルのリストを含むGetPlayableItemsResponseが返されます。Alexaは、ユーザーが選択したタイトルに関するGetPlayableItemsMetadataディレクティブを返し、Lambdaは再生用の詳細情報をすべて含んだGetPlayableItemsMetadataResponseを返します。
Lambdaをどうコーディングし、必要なバックエンドサービスを実行して適切なデータをどう取得するかは、開発者に委ねられています。バックエンドサービスとやり取りして必要な情報を収集し、求められるレスポンスを生成する方法については、このドキュメントでは説明しません。使用するコードやバックエンドサービスがパートナーによって異なるうえ、バックエンドサービス自体もパートナーごとに大きな違いがあるためです。
サンプルLambda関数のセクションごとの説明
ここからは、サンプルLambda関数のロジックについて説明します。手順1: ビデオスキルとLambda関数を作成する(具体的には手順1.3: ビデオスキルのLambda関数を作成する)に記載されているサンプルLambda関数では、いくつかのディレクティブタイプに必要なレスポンスの例が示されています。このLambda関数をセクションごとに詳しく見ていきましょう。
hardCodedResponse関数を使用することにより、バックエンドサービスで実行すべき検索処理を省略しています。Lambdaで受信したイベントを処理するには、コード内でバックエンドサービスとやり取りして、情報の検索などを実行し、必要な情報を返す必要があります。詳細なコードを記述しても実際の実装とは異なるロジックが大量に含まれている可能性があるので、本質を見失わないように、ここではレスポンスをハードコードして、レスポンスの要件を確認するに留めます。また、Lambdaコードの作成にはさまざまな言語を使用できることも覚えておいてください。その他の言語の使用については、AWS Lambdaに関するドキュメントの以下のトピックを参照してください。
- Node.jsによるLambda関数のビルド
- PythonによるLambda関数のビルド
- RubyによるLambda関数のビルド
- JavaによるLambda関数のビルド
- GoによるLambda関数のビルド
- C#によるLambda関数のビルド
- PowerShellによるLambda関数のビルド
このチュートリアルではNode.jsを使用します。サンプルLambda関数のコード全体を以下に示します。コードの後で、セクションごとに内容を確認していきます。
// セクション1開始
var AWS = require('aws-sdk');
exports.handler = (event, context, callback) => {
    console.log("Interaction starts");
    hardCodedResponse(event, context);
};
// セクション1終了
// セクション2開始
function hardCodedResponse(event, context) {
    var name = event.directive.header.name;
    console.log("Alexaのリクエスト:", name, JSON.stringify(event));
// セクション2終了
// セクション3開始
var DiscoverResultResponse = {
    "event": {
        "header": {
            "namespace": "Alexa.Discovery",
            "name": "Discover.Response",
            "payloadVersion": "3",
            "messageId": "ff746d98-ab02-4c9e-9d0d-b44711658414"
        },
        "payload": {
            "endpoints": [{
                "endpointId": "ALEXA_VOICE_SERVICE_EXTERNAL_MEDIA_PLAYER_VIDEO_PROVIDER",
                "endpointTypeId": "TEST_VSK_MM",
                "manufacturerName": "TEST_VSK_MM",
                "friendlyName": "TEST_VSK_MM",
                "description": "TEST_VSK_MM",
                "displayCategories": ["APPLICATION"],
                "cookie": {},
                "capabilities": [{
                    "type": "AlexaInterface",
                    "interface": "Alexa.RemoteVideoPlayer",
                    "version": "1.0"
                }, {
                    "type": "AlexaInterface",
                    "interface": "Alexa.PlaybackController",
                    "version": "1.0"
                }, {
                    "type": "AlexaInterface",
                    "interface": "Alexa.SeekController",
                    "version": "1.0"
                }, {
                    "type": "AlexaInterface",
                    "interface": "Alexa.ChannelController",
                    "version": "1.0"
                },
    {
        "type": "AlexaInterface",
  "interface": "Alexa.MultiModalLandingPage",
  "version": "1.0"
    }]
            }]
        }
    }
};
var GetPlayableItemsResponse = {
    "event": {
        "header": {
            "correlationToken": "dFMb0z+PgpgdDmluhJ1LddFvSqZ/jCc8ptlAKulUj90jSqg==",
            "messageId": "5f0a0546-caad-416f-a617-80cf083a05cd",
            "name": "GetPlayableItemsResponse",
            "namespace": "Alexa.VideoContentProvider",
            "payloadVersion": "3"
        },
        "payload": {
            "nextToken": "fvkjbr20dvjbkwOpqStr",
            "mediaItems": [{
                "mediaIdentifier": {
                    "id": "tt1254207"
                }
            }]
        }
    }
};
var GetPlayableItemsMetadataResponse = {
    "event": {
        "header": {
            "correlationToken": "dFMb0z+PgpgdDmluhJ1LddFvSqZ/jCc8ptlAKulUj90jSqg==",
            "messageId": "38ce5b22-eeff-40b8-a84f-979446f9b27e",
            "name": "GetPlayableItemsMetadataResponse",
            "namespace": "Alexa.VideoContentProvider",
            "payloadVersion": "3"
        },
        "payload": {
            "searchResults": [{
                "name": "ビッグバックバニー",
                "contentType": "ON_DEMAND",
                "series": {
                    "seasonNumber": "1",
                    "episodeNumber": "1",
                    "seriesName": "ブレンダー財団ビデオ",
                    "episodeName": "パイロット"
                },
                "playbackContextToken": "{\"streamUrl\": \"http:\/\/commondatastorage.googleapis.com\/gtv-videos-bucket\/sample\/BigBuckBunny.mp4\", \"title\": \"ビッグバックバニー\"}",
                "parentalControl": {
                    "pinControl": "REQUIRED"
                },
                "absoluteViewingPositionMilliseconds": 1232340
            }]
        }
    }
};
var GetDisplayableItemsResponse = {
    "event": {
        "header": {
            "correlationToken": "dFMb0z+PgpgdDmluhJ1LddFvSqZ/jCc8ptlAKulUj90jSqg==",
            "messageId": "5f0a0546-caad-416f-a617-80cf083a05cd",
            "name": "GetDisplayableItemsResponse",
            "namespace": "Alexa.VideoContentProvider",
            "payloadVersion": "3"
        },
        "payload": {
            "nextToken": "fvkjbr20dvjbkwOpqStr",
            "mediaItems": [{
                "mediaIdentifier": {
                    "id": "tt1254207"
                }
            }, {
                "mediaIdentifier": {
                    "id": "tt0807840"
                }
            }]
        }
    }
};
var GetNextPageResponse = {
     "event": {
         "header": {
             "correlationToken": "dFMb0z+PgpgdDmluhJ1LddFvSqZ/jCc8ptlAKulUj90jSqg==",
             "messageId": "9f4803ec-4c94-4fdf-89c2-d502d5e52bb4",
             "name": "GetNextPageResponse",
             "namespace": "Alexa.VideoContentProvider",
             "payloadVersion": "3"
         },
         "endpoint": {
             "scope": {
                 "type": "BearerToken",
                 "token": "Alexa-access-token"
             },
             "endpointId": "appliance-001"
         },
         "payload": {
             "nextToken": "qefjrfiugef74",
             "mediaItems": [{
                     "mediaIdentifier": {
                         "id": "tt0807840"
                     }
                 },
                 {
                     "mediaIdentifier": {
                         "id": "tt1254207"
                     }
                 },
                                     {
                     "mediaIdentifier": {
                         "id": "tt7993892"
                     }
                 },
                                     {
                     "mediaIdentifier": {
                         "id": "tt2285752"
                     }
                 },
                                     {
                     "mediaIdentifier": {
                         "id": "tt4957236"
                     }
                 }
             ]
         }
     }
 }
var GetDisplayableItemsMetadataResponse = {
    "event": {
        "header": {
            "correlationToken": "dFMb0z+PgpgdDmluhJ1LddFvSqZ/jCc8ptlAKulUj90jSqg==",
            "messageId": "38ce5b22-eeff-40b8-a84f-979446f9b27e",
            "name": "GetDisplayableItemsMetadataResponse",
            "namespace": "Alexa.VideoContentProvider",
            "payloadVersion": "3"
        },
        "payload": {
            "resultsTitle": "SearchResults",
            "searchResults": [{
                "name": "ビッグバックバニー",
                "contentType": "ON_DEMAND",
                "itemType": "VIDEO",
                "releaseYear": "2014",
                "selectionAction": "PLAY",
                "thumbnailImage": {
                    "contentDescription": "ビッグバックバニーの画像",
                    "sources": [{
                        "url": "https:\/\/devportal-reference-docs.s3-us-west-1.amazonaws.com\/video-skills-kit\/bigbuckbunnythumb.png",
                        "size": "X_LARGE",
                        "widthPixels": 1920,
                        "heightPixels": 1280
                    }]
                },
                "runtime": {
                    "runTimeInMilliseconds": 5400000,
                    "displayString": "9分"
                },
                "closedCaption": {
                    "status": "AVAILABLE",
                    "displayString": "字幕"
                },
                "absoluteViewingPositionMilliseconds": 0,
                "parentalControl": {
                    "pinControl": "REQUIRED"
                },
                "viewingDisplayString": "PurchaseOptions",
                "reviews": [{
                    "totalReviewCount": 1897,
                    "type": "FIVE_STAR",
                    "ratingDisplayString": "4.06"
                }],
                "rating": {
                    "category": "G"
                },
                "mediaIdentifier": {
                    "id": "tt1254207"
                }
            }]
        }
    }
};
// セクション3終了
// セクション4開始
if (name === 'Discover') {
    console.log("Lambdaのレスポンス: DiscoverResultResponse", JSON.stringify(DiscoverResultResponse));
    context.succeed(DiscoverResultResponse);
} else if (name === 'GetPlayableItems') {
    console.log("Lambdaのレスポンス: GetPlayableItemsResponse", JSON.stringify(GetPlayableItemsResponse));
    context.succeed(GetPlayableItemsResponse);
} else if (name === 'GetPlayableItemsMetadata') {
    console.log("Lambdaのレスポンス: GetPlayableItemsMetadataResponse", JSON.stringify(GetPlayableItemsMetadataResponse));
    context.succeed(GetPlayableItemsMetadataResponse);
} else if (name === 'GetDisplayableItems') {
    console.log("Lambdaのレスポンス: GetDisplayableItemsResponse", JSON.stringify(GetDisplayableItemsResponse));
    context.succeed(GetDisplayableItemsResponse);
} else if (name === 'GetDisplayableItemsMetadata') {
    console.log("Lambdaのレスポンス: GetDisplayableItemsMetadataResponse", JSON.stringify(GetDisplayableItemsMetadataResponse));
    context.succeed(GetDisplayableItemsMetadataResponse);
}
else if (name === 'GetNextPage') {
    console.log("Lambdaのレスポンス: GetNextPageResponse", JSON.stringify(GetNextPageResponse));
    context.succeed(GetNextPageResponse);
}
};
// セクション4終了
セクション1の説明
// セクション1開始
var AWS = require('aws-sdk');
exports.handler = (event, context, callback) => {
    console.log("Interaction starts");
    hardCodedResponse(event, context);
    ...
  }
// セクション1終了
まず、Node.js内のAWS SDK for JavaScriptへの依存関係を宣言します。このSDKを使用すると、AWSのさまざまな機能をNode.jsコードで実行できます。SDKの詳細については、AWS SDKのドキュメントに記載されています。
handlerメソッドについては、Node.jsのAWS Lambda関数ハンドラーで説明されています。Lambda関数が呼び出されると、AWS Lambdaはhandler関数を呼び出してコードの実行を開始します。AWS Lambdaは、イベントデータをこのhandlerに最初のパラメーターとして渡します。ランタイムでは、handlerメソッドに3つの引数(event、context、callback)を渡します。
- event: 最初の引数は- eventオブジェクトです。このオブジェクトには、呼び出し元の情報が含まれています。この場合、呼び出し元はAlexaディレクティブです。Alexaでは、- Invokeを呼び出すときに、JSON形式の文字列としてこのディレクティブを渡します。
- context: 2つ目の引数は- contextオブジェクトです。このオブジェクトには、呼び出し、関数、実行環境に関する情報が含まれています。
- callback: 3つ目の引数の- callbackは、レスポンスを送信するためにnon-async関数で呼び出すことができる関数です。- callback関数は、エラーとレスポンスの2つの引数を取ります。レスポンスオブジェクトには、- JSON.stringifyとの互換性が必要です。
ハンドラーでは、受信したイベントデータを処理する必要があります。また、コード内でほかの関数/メソッドを呼び出すことができます。
eventとcontextを確認するには、次のようにしてコンソールにログを出力します。
console.log("Alexaのリクエスト:" + JSON.stringify(event, null, 2));
console.log("Context: " + JSON.stringify(context, null, 2));
eventは、hardCodedResponse関数の一部としてログに出力されます。contextは、サンプルLambda関数では一切ログに出力されません。
eventとcontextはJSONオブジェクトです。JSON.stringifyは、JSONオブジェクトを文字列に変換します。JSON.stringifyメソッドは複数のパラメーターを取ります。対象のオブジェクト、置き換え関数/置き換え配列(ここでは使用されていません)、空白文字の値です。
contextは、ここでは必ずしも重要ではありませんが、呼び出されるLambda関数の名前、ログストリーム、使用されているメモリなどの情報がわかります。このワークフローの場合、イベントのcontextはAmazon Cloudwatchでは次のようになっています。
Context:
{
    "callbackWaitsForEmptyEventLoop": true,
    "functionVersion": "$LATEST",
    "functionName": "hawaii_echo_lambda",
    "memoryLimitInMB": "128",
    "logGroupName": "/aws/lambda/hawaii_echo_lambda",
    "logStreamName": "2019/06/21/[$LATEST]2eaa24e01fff497187f6d0fcc2230e8d",
    "invokedFunctionArn": "arn:aws:lambda:us-east-1:458179560631:function:hawaii_echo_lambda",
    "awsRequestId": "1b0c6361-bbe5-440b-95cb-3024f3abfa53"
}
呼び出されるLambda関数はhawaii_echo_lambdaで、Amazon Cloudwatch内のログは/aws/lambda/hawaii_echo_lambdaというグループにまとめられていることがわかります。
この最初のhandlerメソッドの後、hardCodedResponse(event, context);という関数が実行されます。この関数にはパラメーターとしてeventとcontextがあります。この関数については、次のセクションで説明します。
セクション2の説明
// セクション2開始
function hardCodedResponse(event, context) {
    var name = event.directive.header.name;
    console.log("Alexaのリクエスト:", name, JSON.stringify(event));
// セクション2終了
...}
サンプルLambdaコードには、hardCodedResponseという関数が含まれており、パラメーターとしてeventとcontextを取ります。この関数は、受信したイベント(Alexaのリクエスト)から情報を取得し、そのイベントに含まれている情報を変数に設定する方法を示しています。
具体的には、サンプルのLambdaコードでは、nameという変数にevent.directive.header.nameの値を設定しています。
ディレクティブのどのプロパティでも、必要に応じて変数に設定し、検索に使用することができます。必要な情報(およびそれを処理する方法)は、実行しようとしているタスクによって異なります。たとえば、映画のタイトルで検索を実行する場合などが考えられます。そのため、バックエンドサービスで特定の詳細情報を検索用関数に入力する必要があります。
セクション3の説明
サンプルLambdaコードのこのセクションでは、ディレクティブへの応答時に使用する定義済みレスポンスの変数を定義しているだけです。具体的には次のとおりです。
// セクション3開始
var DiscoverResultResponse = {
  ...
};
var GetPlayableItemsResponse = {
...
};
var GetPlayableItemsMetadataResponse = {
    ...
};
var GetDisplayableItemsResponse = {
  ...
};
var GetNextPageResponse = {
  ...
};
var GetDisplayableItemsMetadataResponse = {
  ...
};
// セクション3終了
既に説明したように、このサンプルLambda関数ではレスポンスをハードコードしています。実際の実装では、バックエンドサービスを利用して、必要な情報をその都度取得する必要があります。具体的には、ペイロードを受信したら、その情報を取り出し、独自のロジックに取り込んでユーザーのリクエストに対応します。
ここで使用しているサンプルLambda関数では、メディア情報を扱うバックエンドサービスがないため、レスポンスを事前に定義しています。そのため、このサンプルLambda関数をテストで使用してマルチモーダルデバイスでのビデオスキルの動作を確認する場合、リクエストできるのは、ここで定義したコンテンツのみとなります。
将来的には、動的な検索ができるサンプルLambda関数をこのドキュメントで提供できる可能性もあります。ただし、検索のプロセスは、バックエンドサービスやプログラミング言語の違いによってパートナーごとに大きく異なるため、バックエンドサービスに問い合わせを行う部分のコードはあまり参考にならない場合があります。
セクション4の説明
// セクション4開始
    if (name === 'Discover') {
        console.log("response", JSON.stringify(discoverResult));
        context.succeed(discoverResult);
    } else if (name === 'GetPlayableItems') {
        console.log("response", JSON.stringify(getPlayableItems));
        context.succeed(getPlayableItems);
    } else if (name === 'GetPlayableItemsMetadata') {
        console.log("response", JSON.stringify(getPlayableItemsMetadata));
        context.succeed(getPlayableItemsMetadata);
    } else if (name === 'GetDisplayableItems') {
        console.log("response", JSON.stringify(getDisplayableItems));
        context.succeed(getDisplayableItems);
    } else if (name === 'GetDisplayableItemsMetadata') {
        console.log("response", JSON.stringify(getDisplayableItemsMetadata));
        context.succeed(getDisplayableItemsMetadata);
    }
    else if (name === 'GetNextPage') {
        console.log("Lambdaのレスポンス: GetNextPageResponse", JSON.stringify(GetNextPageResponse));
        context.succeed(GetNextPageResponse);
    }
};
// セクション4終了
この関数の最後のセクションでは、ディレクティブ名に基づいて適切なレスポンスを返します。ディレクティブがGetPlayableItemsだった場合は、GetPlayableItemsResponseをコールバックでAlexaに返します。context.succeedメソッドを使用して、情報をコールバックに設定します。
Amazon Cloudwatchのログで「Lambdaのレスポンス」で始まる行を検索すると、LambdaからAlexaに送信されたレスポンスを確認できます。

