[初心者向け]LambdaでClovaスキル開発

# この記事の対象者

* Clovaのスキル作ったことない
* てか、スマートスピーカーのアプリ作ったことない
* まってまって、AWS Lambdaってなに？

そんな方を対象としています。

# はじめに

Clovaにおけるサードパーティーが開発できるアプリを「スキル」といいます。
スキルを開発するにはサーバーを自前で立てる必要がありました。
そのため、サーバーの運用知識なども必要でした
しかし、Lambdaを使うことで誰でも手軽に開発でき、リリースするまでできます。

この記事では一つのスキルをチュートリアル形式で作っていきます。
できるだけ画像を多く取り入れています。
記事自体は長くなっていますが誰でも簡単に作成することができるようになっています。

# 作成するスキル

何はともあれ、このチュートリアルで作成するスキルの概要を説明します。

<img width="476" alt="cek_handson.png" src="https://qiita-image-store.s3.amazonaws.com/0/195882/eed2343d-b83e-0014-048a-0ea79b71fd25.png">

Clovaに「夜（or昼or朝）ご飯何を食べたらいい？」などと聞くと、メニューを提案してくれるスキルになります。

さらに、最後にLINE Botと連携させ、以下のようなメッセージを送る機能もつけたいと思います。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/195882/495ad074-90a6-9e40-23c4-67743c730a28.png)


# スキル開発って何をするの？

スキル開発と言っても音声アプリの開発をしたことない方は何をするのかイメージがつきにくいかと思います。
WEBアプリと違って画面はありません。
ユーザーの発話した内容はどのように取得するの？Clovaに喋らす内容はどうやって定義するの？などなど。

音声認識や、自然言語処理などを私たちサードパーティーが実装する必要はありません。
それらは、Clovaが行ってくれます。
Clovaが音声認識や自然言語処理で認識してくれた内容をもとにClovaに喋らす内容を実装していくのが主になります。
細かい部分まで説明すると複雑になりますが、私たちスキルの開発者が作成する部分は以下の2つになります。

* 対話モデル: ユーザーが発話する意図に名前をつけ、エンドポイント(今回はLambda使用）での取得を簡単にします。
* エンドポイント（今回はLambda): ユーザーの発話の内容を元にClovaに何を喋らすかの定義をします。

# CEK

それではまず、Clovaのデベロッパーサイトからスキルを登録していきましょう。

## スキルの登録

以下のURLにアクセスし、右上のログインボタンからLINEアカウントでログインします。
https://clova-developers.line.me/

![image.png](https://qiita-image-store.s3.amazonaws.com/0/195882/0c44dfe2-a234-d17d-867f-667d224866d7.png)

---

ログイン後、画面中央の「スキル開発をする」をクリックし、スキル一覧画面へ移動します。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/195882/a0e0395a-2ff4-563e-1736-f6f01b2c4b5a.png)

---

「LINE Developer Centerでスキルチャンネルを新規作成」をクリックし、スキルを作成していきます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/195882/9a04a7a4-d1f2-5f00-c4c8-f9b2272dcb59.png)

---

まずは、プロバイダーを選択します。
プロバイダーを作成したことない方は、以下の画像と少し画面が違うかもしれませんが、[以下](https://developers.line.me/console/register/provider/)からプロバイダーを作成できます。
https://developers.line.me/console/register/provider/
ちなみに、プロバイダーとは個人名や組織名を表します。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/195882/7b560154-f3a1-1d5b-c662-c807b4b3b648.png)

---

次にチャンネル名を`サンプルメニュー`で入力します。
入力したら、チャンネルを作成します。
チャネルとはLINE Developer コンソールで作成されるアプリごとのアカウントです。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/195882/37327b4c-e781-c7ef-e71f-b5580192af8b.png)

---

チャンネルが作成されると、スキルの基本設定画面が開くと思います。
スキルの基本設定では以下を入力します。

|項目|値|
|---|---|
|タイプ|カスタム|
|ExtensionID|リバースドメイン形式の文字列。わからない方は「com.あなたのフルネーム.skill.first」としてください。後で、Lambdaの環境変数の設定で使用します。|
|スキル名|サンプルメニュー|
|呼び出し名（メイン）|サンプルメニュー|
|呼び出し名（サブ）|３ふるメニュー|
|AudioPlayerの使用|いいえ|
|提供者について|個人 ＞ 欄を埋める。LINE IDは現時点では不要。|

各項目について説明すると、

* ExtensionID: スキルのIDで、ユニークである必要があります。サーバー側でスキルの検証等に使います。
* スキル名: そのまま。スキルの名前です。
* 呼び出し名（メイン）: スキル起動時の「Clova、`サンプルメニュー`を開いて」という時の言葉になります。
* 呼び出し名（サブ）: ユーザーが「サンプルメニュー」と言ったつもりでもClovaが「３ふるメニュー」と認識してしまってもちゃんと起動するような設定です。

## 対話モデルの作成

次に対話モデルを作成していきますが、対話モデルとはなにか、という説明をしたいと思います。
スマートスピーカなどの音声デバイスでのアプリでは、ユーザーの入力を全て「会話」で行います。
この会話型のユーザーインターフェイスはVUI(Voice User Interface)と呼ばれています。
このVUIアプリがGUIアプリ（スマホアプリなど）と異なる点は入力パターンの数です。
VUIでの入力数がGUI比べ多いいです。

今回作成するスキルを例に説明します。
メニューをアプリに提案してもらうために「朝」「昼」「夜」を指定します。
まず、GUIの場合画面上に「朝」「昼」「夜」のボタンがあり、クリックするだけで結果が帰ってくるかと思います。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/195882/a3323d38-beba-9a4a-ce50-b1fa03365ec6.png)

しかし、VUIアプリではそうはいきません。
人と人の会話が１パターンでないように、入力方法が複数あります。
今回作成スキルでは、以下のような発話が考えられます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/195882/b2e2f63c-7be2-3f67-6764-5f9fa6212cc5.png)

このようにたくさん考えられるユーザーの入力をあらかじめ対話モデルとして定義しておきます。
そうすることによってサーバーでの取得を簡単にしています。

---

### 対話モデル設定画面を開く

画面上部の「基本情報の保護および規約同意」を押し、開いた画面の一番下にスクロールします。
次に「対話モデル」をクリックし対話モデル画面が開きます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/195882/99ee6558-8e7a-8ce8-888a-3a72f7e1caa0.png)

---

### スロットとは

スロットとは、プログラミングにおける変数的なものです。
まずは以下の3つの文を比べてみたください。

* 朝に何を食べたらいい？
* 昼に何を食べたらいい？
* 夜に何を食べたらいい？

これらは全て同じ意図を指していますが、「朝」「昼」「夜」の部分だけは可変になっています。
スロットはこの可変の部分だけをサーバー側で簡単に取得できるような仕組みがスロットです。

---

### スロットの作成

開いた対話モデルの画面左下の方の「カスタムスロットタイプ」の右のプラスボタンをクリックします。
***「カスタムインテント」の横のプラスボタンではありません。***

![image.png](https://qiita-image-store.s3.amazonaws.com/0/195882/d0811416-e9be-893d-1ae3-461b9783f615.png)

---

スロットタイプのタイトルの入力を求められるので以下を入力します。

```
timezone
```

![image.png](https://qiita-image-store.s3.amazonaws.com/0/195882/ee3679e6-4353-2ec7-679f-19a580b9b776.png)

---

スロットの値を入力していきます。
「スロットタイプの辞書」と書いてある下の入力欄に「朝」「昼」「夜」をそれぞれ入力します。

```
朝
昼
夜
```

![image.png](https://qiita-image-store.s3.amazonaws.com/0/195882/3c3a943f-6a5c-d3d3-d087-a6fbcfe642dd.png)

---

次に同義語を入力していきます。
同義語とは、今設定したスロットの値と同じ意味として捉えたい単語を入力して行きます。
例えば、「夜」を意味する単語にも「夜ご飯」や「晩飯」などがあると思います。
「夜ご飯」や「晩飯」とユーザーが発話しても、Clovaは全て「夜」と認識してサーバーに渡します。

```
夜ご飯, 夜ごはん, 夜御飯, 晩ご飯, 晩ごはん, 晩御飯, 晩飯
昼ご飯, 昼ごはん, 昼御飯, 昼食
朝ご飯, 朝ごはん, 朝御飯, 朝食
```

![image.png](https://qiita-image-store.s3.amazonaws.com/0/195882/654de0a0-7f85-8950-ebdf-76bf040960b0.png)

---

### インテントとは

インテントとは、ユーザーの発話の意図を取得するための仕組みです。
以下の文は発話自体は違えど、意図は同じです。

* 夜に何を食べたらいい？
* 夜ご飯を教えて

このように、同じ意図する文をあらかじめ定義することでサーバーでの取得を簡単にしています。
今回はこの意図に「MenuIntent」という名前をつけます。（インテント名）

---

### インテントの作成

画面左の「カスタムインテント」の横の「＋」ボタンより作成していきます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/195882/9d3d4634-954a-adec-e92d-81db251a4e24.png)

---

インテント名に「MenuIntent」と入力し作成します。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/195882/81210d16-424c-cc2e-956d-f3f603abafc5.png)

---

次に、ユーザーが発話する内容を入力していきます。（サンプル発話と言います）
「サンプル発話リスト」とある下に入力欄があるので「夜に何を食べたらいい？」と入力しEnterを押します。

```
夜に何を食べたらいい？
```

![image.png](https://qiita-image-store.s3.amazonaws.com/0/195882/4eaa0525-201e-17ef-533a-2cccd85abef3.png)

---

そして、先ほど作成した「timezone」スロットとの紐付けをして行きます。
スロットとして紐付けたい部分をドラッグします。
今回ですと「夜」の部分をドラッグします。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/195882/7e46b8a5-d80c-ab71-f484-28217795f2ba.png)

---

すると、スロットの選択画面が出てくると思います。
まだ、このインテントにはスロットを登録してないので新しいスロットに名前をつけてあげます。
「新しいスロットに名前をつける」の下の入力欄に「timezone」と入力します。

```
timezone
```

![image.png](https://qiita-image-store.s3.amazonaws.com/0/195882/47352d84-d98f-71dd-62f7-929b82099e13.png)

---

さっき作成した「timezone」と紐付けを完成させるにはするにはセレクトメニューから、スロットタイプを選択してあげてください。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/195882/27e2b3bd-ed35-ff4c-7ed7-f42a19e701ec.png)

---

同じ要領で、サンプル発話の複数入力。「夜ご飯を教えて」を追加します。

```
夜ご飯を教えて
```

![image.png](https://qiita-image-store.s3.amazonaws.com/0/195882/8144987c-0ec8-2990-2e4b-65a711ba3c84.png)

---

また、スロットの紐付けを行います。
ドラッグし、今回はすでに先程登録した「timezone」を選択します。
すると、スロットタイプのセッレクトメニューもすでに選択されているはず。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/195882/c4e969be-c249-9b75-0fe0-7ec039738c0e.png)

---

同様にサンプル発話をいくつか入力します。
入力し終わったら保存し、左上の「ビルド」ボタンからビルドを実行します。

```
朝ごはんを教えろ
昼ごはんでお願い
夜ご飯を教えて
朝
```

![image.png](https://qiita-image-store.s3.amazonaws.com/0/195882/fc6fd30f-6b6b-ee38-99f6-2a1873128ad8.png)


# Lambda

## Lambdaとは

この記事を読んでくださっている方はもしかしたらLambdaを知らない人は少ないかもしれないですが、一応説明しておきたいと思います。
Lambdaとは、AWSのサービスの一つで何かしらのイベントをトリガーにコードを実行させることができます。
今回はCEKからのPOSTをトリガーとして実行させたいと思います。

しかし、LambdaはPOSTをトリガーとして実行することができません。
そこで、CEKとLambdaの間にAPI Gatewayというものを挟んで実行させていきます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/195882/88fd3e26-248d-b7a3-7a6a-f32e6d1d6e1d.png)

## Lambda関数の作成

[こちら](https://aws.amazon.com/jp/console/)AWSにログインします。
ログイン後ダッシュボード画面が開くと思うので、サービス検索欄にlambdaと打つと検索結果に「Lambda」が現れます。
クリックしてLambdaのダッシュボードを開きます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/195882/b1b72927-4174-d11c-7afc-f0a080ad96dd.png)

---

「関数の作成」をクリックします。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/195882/74991da7-7352-99b6-f5d2-7ff643987641.png)

---

Lambdaの設定画面が表示されると思うので、「一から作成」を選択し、項目を入力していきます。
「ロール」を「カスタムロールの作成」を選択した方はロールの作成画面が出てくると思うので何も変更せずに許可を押します。
今までLambdaを使用したことある方はすでにあるともうので「既存のロールを選択」を選んでください

|項目|値|
|---|---|
|名前|sample-menu|
|ランタイム|Node.js 8.10|
|ロール|カスタムロールの作成(既存のロールがある方は「既存のロールを選択」を選んでください)|

![image.png](https://qiita-image-store.s3.amazonaws.com/0/195882/85bbed20-4bb1-c64a-09b9-39e4c0075f14.png)

---

関数の作成ができたら、以下のURLからダウンロードしたzipファイルをアップロードします。
https://github.com/imajoriri/sample-menu-skill/raw/master/upload.zip
次に環境変数を設定します。キーに「applicationId」を設定して、値に「作成したスキルのExtensionID」を入力します。
ExtensionIDを設定している理由としては、コードでここで設定した環境変数のExtensionIDとClovaスキルから送られてくるExtensionIDを比較しています。
自分が作成したスキルからのリクエストかを判断しています。

|キー|値|
|---|---|
|applicationId|スキルのExtensionID（スキルの設定画面から確認してください）|

![image.png](https://qiita-image-store.s3.amazonaws.com/0/195882/e6bf7618-39b0-9560-2cde-137ea6c7f72a.png)

ちなみに、ZIPファイルの中身はこちらから見ることができます。
https://github.com/imajoriri/sample-menu-skill/blob/master/index.js

こちらのコードでは公式のSDKを使用しています。
公式SDKの使い方は以下を参考にしてください。
[Clova公式SDK(Node.js)の使い方まとめ](https://qiita.com/miso_develop/items/6b256c1e8757c0ace4a9)

スロットと、インテントを取得しClovaに喋らす内容のテキストを作成し、CEKにJSONでレスポンスを返す処理を実行しています。

---

ExtensionIdは、Clovaスキルの基本設定画面から確認することができます。
呼び出し名等を設定した画面です。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/195882/168a153f-a28f-2dfc-5d9c-851f35ae2024.png)

## API Gatewayの作成

次に、LambdaとClovaスキルの紐づけの部分のAPIGatewayを作成して行きたいと思います。 
おそらくLambdaの画面にいると思うので、画面左上の「aws」ボタンを押して最初のダッシュボード画面に戻ります。 
また、検索欄に「api」と入力して「API gateway」をクリックします。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/195882/757f6110-4ce2-63a2-b844-95f1f77055a1.png)

---

「APIの作成」をクリックし以下を入力していきます。

|キー|値|
|---|---|
|API名|sample-menu-api|
|説明|サンプルメニューのAPIです。|
|エンドポイントタイプ|地域|

![image.png](https://qiita-image-store.s3.amazonaws.com/0/195882/a9559d5e-61bf-719e-9750-205d3be3f375.png)

---

画面左上の方の「アクション」ボタンをクリックし出てきたドロップダウンから「リソースの作成」をクリックします。
リソース名には「clova」を入力します。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/195882/6c5f00b7-e048-3cfb-8645-3194d46add3f.png)

---

次に「アクション」から「メソッドの作成」をクリックし、「POST」を選択してチェックボタンを押します。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/195882/dfe58fca-8001-190e-47cb-d867388569b8.png)

---

「結合タイプ」には「Lambda関数」をチェックし「Lambdaプロキシ結合の使用」にチェックを入れ、Lambda関数の部分には先ほど作成したLambdaの名前「`sample-menu`」を入力します。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/195882/afdd8511-4249-1775-0ea9-26a0f8dd351d.png)

---

次に、今作成したAPIをデプロイして実際に使えるようにします。
「アクション」から「APIのデプロイ」をクリックしてデプロイの設定画面が出てくると思うのでそれぞれ値を入力して行きます。

|キー|値|
|---|---|
|デプロイされるイメージ|新しいステージ|
|ステージ名|prod|
|ステージの説明|production|
|デプロイメントの説明|first release|

![image.png](https://qiita-image-store.s3.amazonaws.com/0/195882/2a8146d6-eab9-8a14-6ee0-dee5bd713840.png)

---

「デプロイ」ボタンを押すと、URLが発行されるのでメモします。
***デプロイボタンを押したすぐ後の画面にあるURLではなくて画面左からprod/clovaをクリックして出てきたURLをメモします。***

![image.png](https://qiita-image-store.s3.amazonaws.com/0/195882/45e975ef-c204-7061-0b15-d9eb7de50684.png)

---

次にClovaスキルの設定画面に戻り、サーバー設定の画面にて今メモしたURLを入力します。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/195882/16dfa2c1-160e-5a5e-febc-56bd49c8fe53.png)


# ブラウザでのテスト

対話モデルの画面、（インテントとかスロットとかを設定した）を開き画面左のテストボタンをクリックするとテスト画面が開きます。
発話の入力欄に「夜ご飯を教えて」などと入力するとテスト結果が観れると思います。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/195882/978a9431-adce-1cf5-ffc5-a9e329bc8acd.png)

# Botの作成

Clovaスキルの作成はここで終了ですが、次にBotのアカウントを作成してClovaスキルと連携させてみましょう。

まず、以下のURLからプロバイダー一覧を開き、スキルと同じプロバイダーを選択します。
https://developers.line.me/console/
そして、新規チャンネルを「MessagingAPI」で作成します。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/195882/5af93849-fe87-0b6e-2ca0-f518c57d1873.png)

---

チャンネル設定を入力していきます。

|項目|有力内容|
|---|---|
|アプリアイコン画像|お好きな画像をお使いください（特に何もしなくても大丈夫です。）|
|アプリ名|サンプルメニュー|
|アプリ説明|メニューを通知します。|
|プラン|Developer Trial|
|大業種|ご自身に合わせてお選びください|
|小業種|ご自身に合わせてお選びください|
|メールアドレス|ご自身に合わせてお選びください|

---

作成すると一度プロバイダーのトップ画面に戻ると思うのでもう一度作成したMessagingAPIのチャンネルを開きます。
画面下の方にいき、アクセストークンを再発行し、発行されたトークンをメモします。

メモしたアクセストークンは先ほど作成したLambdaの環境変数に追加します。

|キー|値|
|---|---|
|channelAccessToken|メモしたアクセストークン|

![image.png](https://qiita-image-store.s3.amazonaws.com/0/195882/bdea07c1-6d8e-c6a8-ad0f-86ff6cf260de.png)

---

最後にBot(MessagingAPI）とClovaを連携させます。
スキルの設定画面へ戻り、１番下の「Extensionと連携するLINEのアカウント」で作成したBotを選択します。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/195882/4bc22813-d72e-8e18-4ad7-e9789ecf4db1.png)

Botの連携については、以下で詳しく説明しています。
[ClovaからLINEアプリに通知する(Messaging APIなるものを使ってみる)](https://qiita.com/imajoriri/items/3a62eb95236425a44a1b)
[ClovaスキルからLINE BOTでメッセージを送信するまで（CEK SDK Node.js）]
(https://qiita.com/Omoti/items/0d4d26524303100b19d9)
# 実機で試す

スキルを作成したアカウントで登録しているClovaなら実機で試すことができます。
Botにもちゃんと通知がくるかを試してみたい場合はLINEで友達追加する必要があります。
友達追加はMessagingAPIの設定画面でQRコードから追加することができます。