arrow-righthamburgerlogo-marksocial-facebooksocial-githubsocial-twitter
2018.08.03

Clova CEKでのスキル開発の始め方〜Node.jsで開発スタート編〜

のびすけ

スマートスピーカー開発入門
   このエントリーをはてなブックマークに追加  

こんにちは、n0bisukeです。

#linebootawards関連でハンズオンをやる機会が増えそうなので学習メモがてら、前回に引き続きClovaの開発チュートリアルを書いていきます。

Node.jsとClovaの連携をとりあえずやりたいって人向けです。

Clovaってなんぞやって人はこちらの記事を先に読んでみてください。

いわゆるClovaのAPIのことをCEKと呼ぶんですよ〜

作るもの

のびすけ「秋葉原のカレー屋さん教えて

Clova「秋葉原のオススメのカレー屋は ぺらぺらぺら」

これを作ってみます。完成はこんなイメージです。

ハンズオンに使えそうなカレースキル! #clova_cek #linebootawards

n0bisukeさん(@n0bisuke)がシェアした投稿 -

環境

使い始めの申請

こちらの記事を参照して、利用開始できる状態にしましょう。

対話モデルの作成

Clova Developer Centerにアクセスします。

作成中スキルの一覧が表示されます。

作成対象となるスキル名の対話モデル修正を選択します。

すると別ウィンドウが開き、対話モデルの編集画面になります。

インテントとスロット

インテントとスロットを設定していきます。

個人的にはインテントとスロットの概念を理解すればあとはBOT開発などとそこまで変わらないような印象があります。

説明はあってるのか不安なので、スマートスピーカー開発に慣れてる人が見て変だったら教えてください笑

公式ドキュメントのこの辺を見ると詳しく載っています。

スロット

スロットはスキル内で扱う名詞情報です。

今回の「秋葉原のカレー屋さん教えて」という発話を認識させる為に

秋葉原という名詞(スロット)を登録しましょう。

また、その名詞(スロット)はどういう属性かという上位概念をスロットタイプとして設定します。

では実際に

左側のメニューにカスタムスロットタイプという項目があるので、そこの+ボタンを押します。

ここでスロットタイプのタイトルを決めますが、秋葉原は駅名や地名なので場所にちなんだタイトルが良さそうです。ここではarea(エリア)としました。

作成を押すと、areaというスロットタイプが作成されます。

僕みたいに覚えにくい人はスロットタイプ=単語群だと思えば良いと思います。

次にスロット登録です。

スロットの新たな代表語を入力の箇所に秋葉原と入力し、同義語の箇所に読み方や別名を入力します。こうすることであきばなどの名称でも認識してくれるようになります。

秋葉原もカレー屋は多いですがカレーと言えば神保町や神田周辺もかなり多いのでareaに神保町と神田も追加しておきました。重要情報(謎)

最後に保存を押してスロットはOKです。個人的には難所50%クリアです。

インテント

インテントは会話や命令の種類です。

今回の「秋葉原のカレー屋さん教えて」はカレー情報の検索だと思うので

CurreySearchIntent(カレーサーチ)を作ってみます。

メニュー左のカスタムインテント箇所の+ボタンを選択し、インテント名を入力して進みます。

フォーム下部のスロットリストからこのインテントで利用するスロットの登録をします。 先ほど作成したareaを登録しましょう。

次にサンプル発話リスト箇所に例文を入れていきます。

先ほどから何回も出ている「秋葉原のカレー屋さん教えて」をここに入力します。

さらに秋葉原の部分が先ほど登録したスロット部分になるので、この文章のここがスロットだよということを登録してあげます。

秋葉原をドラッグするとスロット登録が出来るフォームが出てくるのでそこでareaを選択しましょう。

ここは操作が難しい印象なので↓のキャプチャGIF参照で!

完成したら保存しましょう。

対話モデルのビルド

ここまで来たら左上のビルドボタンを押してひとまず完了です。

ここ、けっこう時間かかります。

体感5~10分程度。

ただ待つのもしんどいので次の準備にかかりましょう。

Node.jsの環境準備

お待たせしました。コード書いていきましょう。

Node.jsのSDKがあるのでこちらを使っていきます。

Node.js以外にも現時点でSwift/Kotlin/ElixirのSDKが出ています。

Node.jsのインストールがまだな方はこちらの記事をみてインストールしてみましょう。

ちなみにNode.jsは公式サイトからではなくnodebrew経由でインストールする形にした方が後々便利です。僕のオススメは以下の記事にあるnodebrew経由でのインストールになります。

ターミナル操作が不安な方はこちらの記事を先に見ておきましょう。


準備

以下のコマンドで進めていきます。 フォルダ作成と準備です。

$ mkdir clova_curry
$ cd clova_curry
$ npm init -y

SDKをnpm経由でインストールします。expressとbody-parserも利用します。

$ npm i @line/clova-cek-sdk-nodejs express body-parser

現時点ではnode_modules,package-lock.json,package.jsonのファイルがある状態ですね。

$ ls
node_modules      package-lock.json package.json

コードを書いていく

app.jsというファイルを作成して以下のコードを記述します。

また、スキルの基本情報を登録した際のExtention IDを利用するので確認しておきましょう。

公式のサンプルよりもシンプルにしています。

const clova = require('@line/clova-cek-sdk-nodejs');
const express = require('express');

const clovaSkillHandler = clova.Client
    .configureSkill()

    //起動時に喋る
    .onLaunchRequest(responseHelper => {
        responseHelper.setSimpleSpeech({
            lang: 'ja',
            type: 'PlainText',
            value: 'カレー屋さんを探します。',
        });
    })

    //ユーザーからの発話が来たら反応する箇所
    .onIntentRequest(async responseHelper => {
        const intent = responseHelper.getIntentName();
        const sessionId = responseHelper.getSessionId();

        console.log('Intent:' + intent);
    })

    //終了時
    .onSessionEndedRequest(responseHelper => {
        const sessionId = responseHelper.getSessionId();
    })
    .handle();


const app = new express();
const port = process.env.PORT || 3000;

//リクエストの検証を行う場合。環境変数APPLICATION_ID(値はClova Developer Center上で入力したExtension ID)が必須
const clovaMiddleware = clova.Middleware({applicationId: 'YOUR_EXTENSION_ID'});
app.post('/clova', clovaMiddleware, clovaSkillHandler);

app.listen(port, () => console.log(`Server running on ${port}`));

起動

記述出来たら

$ node app.js
Server running on 3000

で起動です。エラーがなければとりあえずOKです。

ngrokでホスティングせずに疎通確認

ngrokというトンネリングツールを使ってローカル開発が出来るようにしましょう。

参考: https://ngrok.com/

LINE BOTの開発でも同様ですが、通常はHTTPS対応したサーバーにプログラムをホスティングしてClovaと通信させます。

HTTPS対応したサーバーを用意するのは骨が折れるのとホスティングして失敗するとエラー修正が大変なので、ローカル開発で最初は挙動確認するのが個人的にはおすすめです。

ngrokを使うことでローカル環境をホスティング環境のようにエミュレート出来るイメージです。

参考: 1時間でLINE BOTを作るハンズオン

$ npm i -g ngrok

これでインストール完了です。

以下のコマンドで利用しますが、ngrokがターミナル占有してしまうので、Node.jsを起動するターミナルとは別で立ち上げましょう。

$ ngrok http 3000

こんな雰囲気。右がngrok、左がNode.jsです。

ngrokを起動すると

Forwarding                    https://xxxxxxx.ngrok.io -> localhost:3000

といった項目が表示されます。

このhttps://xxxxxxx.ngrok.io/clovaを追加したアドレスをClovaのサーバー設定のExtensionサーバーのURLに記載して保存しましょう。

https://xxxxxxx.ngrok.io/clovaとなります。

xxxxxxの箇所はngrokを再起動すると変わってしまうのでその際は再度サーバー設定を更新してください。

これでClovaに話しかけると手元(ローカル環境)のNode.jsまでリクエストが来ます。

テスターでテスト

直接話しかけても良いのですが、テスターを使ってみましょう。

対話モデルのダッシュボードに戻ります。

たぶんビルドは完了してますよね。

テストを選択し、ユーザーのサンプル発話をテストの箇所に「秋葉原のカレー屋を教えて」と入力しテストボタン押しましょう。

Node.jsを起動させているターミナル側で

Intent:CurreySearchIntentと表示されていればOKです。

疎通確認が出来ました! コードでいうとonIntentRequest()の箇所までリクエストが通ってることになります。

.onIntentRequest(async responseHelper => {
    const intent = responseHelper.getIntentName();
    const sessionId = responseHelper.getSessionId();

    console.log('Intent:' + intent); //←ここが反応した
})

エラーが出る場合

Error: Invalid application id: ~~~

などのエラーが出た人はapp.js内のExtension IDの設定を忘れている可能性が高いので再度チェックしてみましょう。

スロット情報も取得してみる

app.jsonIntentRequest()の箇所をまるっと差し替えてみましょう。

//ユーザーからの発話が来たら反応する箇所
.onIntentRequest(async responseHelper => {
    const intent = responseHelper.getIntentName();
    const sessionId = responseHelper.getSessionId();

    console.log('Intent:' + intent);
    if(intent === 'CurreySearchIntent'){
        const slots = responseHelper.getSlots();
        console.log(slots); //←ここでスロット(今回の場合エリア名)が反応
    }
})

Node.jsを再起動して確認してみます。

テスターでは秋葉原のカレー情報教えて神保町のカレー情報教えて上野のカレー情報教えてを順番に実行してみます。

$ node app.js
Server running on 3000
Intent:CurreySearchIntent
{ area: '秋葉原' } //←秋葉原のカレー情報教えて
Intent:CurreySearchIntent
{ area: '神保町' } //←神保町のカレー情報教えて
Intent:CurreySearchIntent
{} //←上野のカレー情報教えて

先ほどのスロット名登録の際に、秋葉原神保町神田を登録してましたが、上野は登録してなかったので「上野のカレー情報教えて」だと空になります。

Clovaにカレー屋情報をしゃべらせる

最後です!

先ほど同様にapp.jsonIntentRequest()の箇所をまるっと差し替えてみましょう。

.onIntentRequest(async responseHelper => {
    const intent = responseHelper.getIntentName();
    const sessionId = responseHelper.getSessionId();

    console.log('Intent:' + intent);
    if(intent === 'CurreySearchIntent'){
        const slots = responseHelper.getSlots();
        console.log(slots);
        //デフォルトのスピーチ内容を記載 - 該当スロットがない場合をデフォルト設定
        let speech = {
            lang: 'ja',
            type: 'PlainText',
            value: `まだ登録されていないエリアです。`
        }
        if(slots.area === '秋葉原'){
            speech.value = `${slots.area}のオススメのカレー屋は フジヤマドラゴンカレー です。`;
        }else if(slots.area === '神保町'){
            speech.value = `${slots.area}のオススメのカレー屋は 共栄堂 です。`;
        }else if(slots.area === '神田'){
            //神田のカレー情報検索
            //何か自分で書いてみましょう。
        }
        responseHelper.setSimpleSpeech(speech);
        responseHelper.setSimpleSpeech(speech, true);
    }
})

slots.areaにエリア情報が入っているので、あとはif文で判定して喋らせる内容を変更していきます。

最後のresponseHelper.setSimpleSpeech()を呼ぶことでClovaが喋ってくれます。

実機テスト

Node.jsのサーバーを再起動して試してみましょう。

起動する際は、{ウェイクワード} -> {スキル名}を起動してとなります。

その後、設定したインテントを発話しましょう。

ということで、人によって違うのですが僕の場合はねぇ、Clovaがウェイクワードになっていて、今回はカレー情報というスキル名でした。

のびすけ: 「ねぇクローバ

Clova: 「ポンっ(LEDが緑色に光る)」

のびすけ: 「カレー情報を起動して

Clova: 「カレー屋さんを探します。」 (← Node.jsで記述している)

Clova: 「ポンっ(LEDが緑色に光る)」

のびすけ:「秋葉原のカレー屋さんを教えて

Clova: 「秋葉原のオススメのカレー屋はフジヤマドラゴンカレーです。」 (← Node.jsで記述している)

という感じの使い方になります。

動画もどうぞ

ハンズオンに使えそうなカレースキル! #clova_cek #linebootawards

n0bisukeさん(@n0bisuke)がシェアした投稿 -

まとめ

こんな感じでとりあえず試す手順を紹介しました。

ホスティングもnowやherokuを使うと簡単に出来るので立花さんのハンズオン資料を参照して理解を深めましょう!

ホスティングに関しても余裕があれば追記したいと思います。

   このエントリーをはてなブックマークに追加