arrow-righthamburgerlogo-marksocial-facebooksocial-githubsocial-twitter
2018.09.15

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

のびすけ

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

こんにちは、n0bisukeです。

#linebootawardsのハッカソンで奈良先端科学技術大学院大学に来ています。

【9/15-16】奈良先端科学技術大学院大学ユビ研×LINE BOOT AWARDSハッカソン

前回はNode.jsでのチュートリアルを書きましたが、今回はPythonでのClovaの開発チュートリアルを書いていきます。

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

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

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

そもそも僕がPythonの使い方がイマイチ分かってないので抜けがある可能性高いのでコメントなどいただけたら幸いです。 -> @n0bisuke

作るもの

今回、「倦怠期のカップルや夫婦のコミュニケーションを円滑にするスキル」 という案が出ていたのでそれをネタにチュートリアルを進めてみたいと思います。

旦那「妻の気分を教えて

Clova「妻の気分はいい感じです。」

これを作ってみます。会話のキッカケを増やしたり作ったりするスキルですね。

完成はこんなイメージです。

View this post on Instagram

Pythonで書いてみた #倦怠期 #linebootawards #clova_cek

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

中身はハローワールド的な内容なので単純にClovaに喋ってもらう入門だと思ってください。

環境

でPythonのバージョン調べられます。

使い始めの申請

まずは、新規チャンネル作成からチャンネルを作成します。

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

チャネルの基本設定」の箇所まで進められばOKです。

ちなみに今回は以下の情報にしてみました。

「倦怠期(けんたいき)」と言う言葉が発話した際に「ケンタッキー」と誤認識される可能性があったので呼び出し名(サブ)にケンタッキーを登録しています。(笑うところ)

対話モデルの作成

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

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

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

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

なぜここだけ別ウィンドウなのかは永遠の謎。

インテントとスロット

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

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

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

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

スロット

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

今回の「妻の気分を教えて」という発話を認識させる為に

気分という名詞(スロット)を登録しましょう。

また、「妻の欲しい物を知りたい」というケースに対応するために欲しい物というスロットを登録します。

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

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

ここでスロットタイプのタイトルを決めますが、気分や欲しい物や妻がどういった状態かを知りたいので、ここではstatus(ステータス)としました。

書いてて今回の例が少し分かりにくいかなぁと思ってるのですが、Node.jsのチュートリアルでは「秋葉原のカレー屋を教えて」という例文を利用していて、秋葉原や、神田、神保町といった駅名や地名、場所にちなんだ単語をスロットに登録しました。この場合はarea(エリア)というスロットタイプにしました。

statusとスロットタイプに入力して作成を押すと、statusというスロットタイプが作成されます。

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

次にスロット登録です。

スロットの新たな代表語を入力の箇所に気分と入力し、同義語の箇所に読み方や別名を入力します。こうすることできぶんなどの名称でも認識してくれるようになります。欲しい物の場合は欲しいものほしいものなども登録してあげると認識精度が上がります。

同義語はカンマで区切って複数登録できます。

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

インテント

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

今回の「妻の気分を教えて」は妻の状態を知りたいという命令だと思うので

WifeStatusIntent(ワイフステータス)を作ってみます。

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

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

先ほどから何回も出ている「妻の気分を教えて」をここに入力してエンターを押しましょう。

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

気分をドラッグするとスロット登録が出来るフォームが出てくるのでそこでstatusと入力してスロット名を登録します。



さらにスロットタイプのプルダウンメニューが出てくるので先ほど作成したstatusスロットを紐付けします。

最後に保存を押しましょう。

対話モデルのビルド

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

ここ、少し時間かかります。

1~2分程度掛かります。昔は10~20分程掛かってたのですが、アップデートで凄く短縮されました。

ただ待つのも時間が勿体無いので次の準備にかかりましょう。

Pythonの環境準備

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

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

Python以外の言語のSDKも現時点ではSwift/Kotlin/Elixir/Java/Go/Node.jsのSDKが出ています。

Pythonのインストールがまだな方は公式サイトなどからDLしてインストールして下さい。

MacだとデフォルトでPythonがインストールされているのでインストールに関してはそこまで問題無いと思います。

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

準備

以下のコマンドで進めていきます。基本Mac向けなのでWinな人は適宜読み替えをお願いします。

pipはNode.jsでいうnpmのようなPython向けのモジュールの管理ツールです。

$ sudo easy_install pip

Python向けのマイクロフレームワークのFlaskをpip経由でインストールします。 Node.jsでいうExpressのようなものです。

$ pip install Flask

Clova CEKのPython SDKをインストールします。

$ pip install clova-cek-sdk

フォルダ作成と準備です。

$ mkdir clova_kentaiki
$ cd clova_kentaiki

server.pyを作成してプログラムを書いていきましょう。

$ touch server.py

コードを書いていく

server.pyに以下のコードを記述します。

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

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

以下のコードのapplication_id=“MY EXTENSION ID”の箇所を自分のExtension IDに書き換えましょう

# coding: utf-8

from flask import Flask, request, jsonify
import cek

app = Flask(__name__)

clova = cek.Clova(
    application_id="MY EXTENSION ID",
    default_language="ja",
    debug_mode=True)

# /clova に対してのPOSTリクエストを受け付けるサーバーを立てる
@app.route('/clova', methods=['POST'])
def my_service():
    body_dict = clova.route(body=request.data, header=request.headers)
    response = jsonify(body_dict)
    response.headers['Content-Type'] = 'application/json;charset-UTF-8'
    return response

# 起動時の処理
@clova.handle.launch
def launch_request_handler(clova_request):
    welcome_japanese = cek.Message(message="調子どうだい?", language="ja")
    response = clova.response([welcome_japanese])
    return response

# WifeStatusIntentの発火箇所
@clova.handle.intent("WifeStatusIntent")
def wife_status_handler(clova_request):
    print("ワイフインテント")
    message_japanese = cek.Message(message="奥さんの気分はいい感じです", language="ja")
    response = clova.response([message_japanese])
    return response

# 終了時
@clova.handle.end
def end_handler(clova_request):
    # Session ended, this handler can be used to clean up
    logger.info("Session ended.")

# 認識できなかった場合
@clova.handle.default
def default_handler(request):
    return clova.response("Sorry I don't understand! Could you please repeat?")

起動

記述出来たら以下のコマンドで起動しましょう。

$ FLASK_APP=server.py flask run
 * Serving Flask app "server.py"
 * Environment: production
   WARNING: Do not use the development server in a production environment.
   Use a production WSGI server instead.
 * Debug mode: off
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

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

5000番ポートでサーバーが起動します。

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

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

参考: https://ngrok.com/

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

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

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

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

$ npm i -g ngrok

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

npmを入れてない人はbrew経由でもインストール可能です。

$ brew install ngrok

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

$ ngrok http 5000

こんな雰囲気。右がngrok、左がPythonです。

ngrokを起動すると

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

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

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

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

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

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

テスターでテスト

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

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

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

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

Pythonを起動させているターミナル側で

ワイフインテントと表示されていればOKです。

疎通確認が出来ました!

コードでいうと@clova.handle.intent("WifeStatusIntent")の箇所までリクエストが通ってることになります。

# WifeStatusIntentの発火箇所
@clova.handle.intent("WifeStatusIntent")
def wife_status_handler(clova_request):
    print("ワイフインテント")
    message_japanese = cek.Message(message="奥さんの気分はいい感じです", language="ja")
    response = clova.response([message_japanese])
    return response

エラーが出る場合

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

Clovaにしゃべらせる

最後です!

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

server.py@clova.handle.intent("WifeStatusIntent")の箇所をまるっと差し替えてみましょう。

# WifeStatusIntentの発火箇所
@clova.handle.intent("WifeStatusIntent")
def wife_status_handler(clova_request):
    print("ワイフインテント")
    slot = clova_request.slot_value("status")
    message_japanese = cek.Message(message="もう一回言って下さい", language="ja")

    if u"気分" in slot:
        message_japanese = cek.Message(message="奥さんの気分はいい感じです", language="ja")
    elif u"欲しい物" in slot:
        message_japanese = cek.Message(message="奥さんは旅行に行きたがっています", language="ja")
    response = clova.response([message_japanese])
    return response

clova_request.slot_value("status")にスロット情報が入っているので、あとはif文で判定して喋らせる内容を変更していきます。

最後のresponse = clova.response([message_japanese])を呼ぶことでClovaが喋ってくれます。

ここまでをまとめたコードはこちらです。

実機テスト

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

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

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

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

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

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

のびすけ: 「倦怠期を起動して

Clova: 「調子どうだい?」 (← server.pyで記述している)

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

のびすけ:「妻の気分を教えて

Clova: 「奥さんの気分はいい感じです」 (← server.pyで記述している)

のびすけ:「妻の欲しい物を教えて

Clova: 「奥さんは旅行に行きたがっています」 (← server.pyで記述している)

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

完成動画はこの記事の上部を確認して下さい。↑

ちなみに、妻が状況を登録するときはこんな感じでやる模様です。

View this post on Instagram

倦怠期clova #linebootawards

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

おまけ: 発話履歴の確認

テスターの発話履歴を見るとClova側でどんな認識になっているかが分かるのですが”妻の”が”その”などに認識されていて認識の揺れが発生してるのが分かりますね。

Clovaが発話をうまく認識してくれないときはここを見て確認しましょう。

まとめ

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

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

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

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