この記事で書いてあること
先日CEKの一般公開を期に、CEK + Lambda + API Gateway
を実践してみました。
この記事を一通りやると、簡単なClovaスキルが作成できるようになります。
Clova専用のSDKを作ったので、それを元にエンドポイント(今回はLambdaを使用)の実装について説明していく。
インテントやスロットはすでにCEKで定義している前提。
公式SDKを使ったLambdaでの開発方法も書きました。
Clovaの公式SDKがLambdaで使えるようになっていたので試してみた。
注意
今回は簡単化のために***「RSA公開鍵を用いてリクエストメッセージを検証する」***をしていません。
しなくても、動作はするのですがLINEさん公式では署名を検証することを推薦しています。
なので、この記事を拡張して一般公開用のスキルを作成する場合は検証機能を追加することをお勧めします。
詳しくはこちら
(次回の記事で自分の方でも検証のやり方の記事を書こうかなと思います。)
書きました。
[入門]Clovaスキル(CEK)は作りながら覚えて行く(signature検証編)
また、今回使用しているSDKはあくまで個人で開発したものであり、出来て間もないためバグなどがある可能性があります。ご了承ください。今後も修正を重ねて行く予定です。
なお、ask-sdkに似た様な書き方でかける様にしているのでおなじみの方にはやりやすいかと思います。
環境
- npm入ってる >> (npmなにそれ、って人向けにzipファイル用意しましたので必須ではないです。)
- node.js(8.10以上) >> (node.jsなにそれ、って人向けにzipファイル用意しましたので必須ではないです。)
- AWSのアカウント持ってる >> これは必須です。
まずは、簡単にスキルの説明
今回は、星座で占いをしてくれる「ほげほげ占い」を作っていく。
会話例
ユーザー: 「Clova、ほげほげ占いを開いて」
Clova: 「あなたの星座を教えてください」
ユーザー: 「私の星座は魚座です。」
Clova: 「魚座ですね。魚座の今日の運勢は○○です。」(スキル終了)
Slots
スロット名: constellation
値:
ふたご座
蟹座
魚座
乙女座
etc
インテント
インテント名: DivinationIntent
発話例:
私の星座は魚座です
私の星座は蟹座だ
私の星座はふたご座よ
開発の流れ
- ローカル(自分のパソコン)にてNode.jsでコードを書いちゃう。
- Lambdaにzipファイルとしてアップロードする。
- Lambdaのテスト画面で試してみる。
- 実機で試してみる。
ローカルでの開発
npmまたはNode.jsなんて知らないよ、という方にZIPファイル用意しました。ここからダウンロードしてください。
そして、「AWSのコンソール画面で」まですっ飛ばして大丈夫です。
初期設定
ディレクトリ作成
$ mkdir fortune-func
$ cd fortune-func
$ npm init #なんか色々聞かれるが全部エンター押していく
インストール
$ npm install --save love-clova
ファイル作成
$ vi index.js
好きなエディタをご利用ください。
コードのコピペ
index.js
に以下のコードをコピペする。
とりあえずの自作SDKなのでご了承を。
var clova = require("love-clova");
const LaunchRequestHandler = {
canHandle: function(handlerInput){
return handlerInput.requestEnvelope.isMatch('LaunchRequest');
},
handle: function(handlerInput){
var msg = "あなたの星座を教えてください";
return handlerInput.responseBuilder.speak(msg).reprompt(msg).getResponse();
}
}
const SessionEndedRequestHandler = {
canHandle: function(handlerInput){
return handlerInput.requestEnvelope.isMatch('SessionEndedRequest');
},
handle: function(handlerInput){
var msg = "";
return handlerInput.responseBuilder.speak(msg).reprompt(msg).getResponse();
}
}
const ClovaGuideIntentHandler = {
canHandle: function(handlerInput){
return handlerInput.requestEnvelope.isMatch('Clova.GuideIntent');
},
handle: function(handlerInput){
var msg = "このスキルは今日のあなたの運勢を占います。あなたの星座を教えてください。";
return handlerInput.responseBuilder.speak(msg).reprompt(msg).getResponse();
}
}
const DivinationIntentHandler = {
canHandle: function(handlerInput){
return handlerInput.requestEnvelope.isMatch('DivinationIntent');
},
handle: function(handlerInput){
// 運勢を配列で。
var fortunes = ["良い", "普通", "悪い"];
// fortunesの中からランダムで
var fortune = fortunes[Math.floor(Math.random() * fortunes.length)];
// slotsを取得
var constellation = handlerInput.requestEnvelope.request.intent.slots.constellation.value;
// clovaに話す内容を作成。
var msg = `${constellation}ですね。${constellation}の今日の運勢は${fortune}です。`;
return handlerInput.responseBuilder.speak(msg).getResponse();
}
}
const errorHandler = {
canHandle: function(handlerInput){
return true;
},
handle: function(handlerInput){
var msg = "エラー発生";
return handlerInput.responseBuilder.speak(msg).reprompt(msg).getResponse();
}
}
exports.handler = clova.extensionBuilders
.addRequestHandlers(LaunchRequestHandler,SessionEndedRequestHandler,ClovaGuideIntentHandler,DivinationIntentHandler)
.addErrorHandlers(errorHandler)
.lambda()
zipファイル化
lambdaにコードをあげる方法はいくつかあるが、簡単なZIPファイルにしてあげる方法にする。
fortune-funcディレクトリ配下でとりあえず、存在しているファイルを確認。
$ ls
index.js package-lock.json node_modules package.json
全部を圧縮する
$ zip -r upload.zip *
再度、存在しているファイルを確認
$ ls
index.js package-lock.json node_modules package.json upload.zip
upload.zip
というファイルが作成されているのが確認できる。
AWSのコンソール画面で
Lambdaで関数作成
AWSのLambdaの画面に移動し、右上の「関数の作成」ボタンを押す。

「一から作成」を選択肢、名前
、ランタイム
、ロール
を以下のように設定する
- 名前: FortuneFunc
- ランタイム: Node.js 8.10
- ロール: カスタムロールの作成(すでにある場合は既存のロールを選択する)

カスタムロールの作成を選ぶと新規画面が勝手に出てくるので、右下の許可
をクリック。

すると、先ほどの関数の作成画面に戻るはず。
右下の関数の作成
をクリック。
おそらく、以下のような画面になるはず。
ご指摘いただいたのですが、人によってはLambdaの出力(画面右の方)がCloudWatchだけだったりしますが今回は動作上問題ありません。

次に、画面少し下行くと関数コード
という項目があるので、その中のコードエントリタイプ
をzip ファイルをアップロード
を選択する
そんで、その下のアップロード
ボタンを押して先ほど作成下upload.zip
を選択して、右上の保存
ボタンで保存する。
とりあえず、テストしてみる。
まだ、ClovaとLambdaを連携させてないので実機でのテストはできないがLambda上でテストする方法がある。
Lambda画面右上の「テストイベントの設定」ボタンをクリックするとテストイベントの設定画面が出てくるので以下の様に入力してあげる。

イベント名
LaunchRequest
イベント名の下のJSONを入力するところ。
{
"version": "0.1.0",
"session": {
"new": true,
"sessionAttributes": {},
"sessionId": "sample",
"user": {
"userId": "V0qe",
"accessToken": "sample"
}
},
"context": {
"System": {
"application": {
"applicationId": "sample"
},
"user": {
"userId": "V0qe",
"accessToken": "sample"
},
"device": {
"deviceId": "sample",
"display": {
"size": "l100",
"orientation": "landscape",
"dpi": 96,
"contentLayer": {
"width": 640,
"height": 360
}
}
}
}
},
"request": {
"type": "LaunchRequest"
}
}

作成ボタンを押してから右上の「テスト」を押すとテストが実行される。

テストが成功すると以下の様な画面が表示される。
赤く囲った部分が実際にClovaが喋るテキストの部分である。

こんな感じだと、とりあえずちゃんと動いている。
API Gateway
このままではClova側と繋ぐことができないので、API Gatewayを使用する。
API Gatewayとは外部サービスとLambdaを連携させる入り口的な役割。
AWSのサービス一覧からAPI Gatewayのページに行き「APIの作成」ボタンを押す。
次に、以下の様にAPI名
と、説明(なくても良い)
を入力し「APIの作成」ボタンを押す。
API名: CEKGateway
説明: for CEK Gateway

左上の「アクション」ボタンより「リソースの作成」を押し、以下の様にリソース名を入力し「リソースの作成」ボタンを押す。
リソース名: Fortune

次に、また左上の「アクション」ボタンから「メソッドの作成」ボタンを押す。
すると、ドロップボタンができるので「POST」を選択しすぐ右のチェックボタンを押す。

セットアップ画面になるので、総合タイプを「Lambda関数」を選択し、Lambda関数項目には先ほど作成した「FortuneFunc」を入力する。

権限の追加の許可を聞かれるので「OK」をクリック

またまた、左上の「アクション」ボタンから「APIのデプロイ」をクリックする。

入力画面が出てくるので、以下の様に入力し「デプロイ」ボタンを押す。
デプロイされるステージ: 新しいステージ
ステージ名: prod
ステージの説明: production

画面左のディレクトリ構成みたいなところからprod/fortune/POST
を選択すると以下の様な画面になる。
URLの呼び出しってところのURLをメモっておく。
URLの最後の部分が/prod/foutuneとなっているのを確認。

CEK側でLambdaと連携させる
次にいよいよ、CEKとLambdaを連携させる。
先ほどの「スキル設定画面」より「サーバー設定」の欄に行く。
先ほどAPI GatewayでコピーしたURLを「ExtensionサーバーのURL」欄に入力する。
そして、保存。これで完成。

ブラウザでテスト
ブラウザでちゃんと動くかをテストしてみる。
画面左の、先ほどのビルドボタンのすぐ下あたりにある「テスト」ボタンをクリックする。
すると、ユーザーのサンプル発話をテストできるページが表示されるので私の星座は魚座です。を入力しエンターを押す。
すると、すぐ下にテスト結果が帰ってくる。
「サービスの応答」の項目にCLovaが喋る内容がテキストで帰ってきているのが確認できる。
実機でテスト
いよいよ実機でテストをしてみましょう。
実機でテストするには「Clova friends」もしくは、「Clova friends mini」が必要です。
さらには、開発したのと同じアカウントでセットアップしてある必要があります。
実機でのテストは簡単。
「Clova, ほげほげ占いを開いて」と行って見てください。
すると先ほど開発したスキルが起動します。
実機でのテストがうまくいかない。
スキルがうまく起動しない場合、考えられる原因としてClovaが誤認識した可能性があります。
ここでClovaが認識した内容を確認する方法が書かれているので参考にして見てください。
それでも、それでもうまくいかない場合
この記事か、SDKに問題がある場合も考えられるのでTwitterでここに@imasiroooご連絡いただけると助かります。
もちろん、Clova以外のスマートスピーカー開発での質問も受け付けてます。(答えられるかはわからないですが)
皆さんでスマスピを盛り上げていきましょう!!!
最後に
長々としてしまいましたが、読んでいただきありがとうございます。
今回使用したSDKは、日々修正していこうと思います。
もしご要望あればコードの中身だっったり、今回使ってない色々な機能を記事にしていこうかなと思います。
次は、公開には必須のsignature検証についてです。
[入門]Clovaスキル(CEK)は作りながら覚えて行く(signature検証編)