Google Home MiniとWatson Assistantを連携する

【注】2018/03/15付でWatson ConversationはWatson Assistantに名称変更されました。

#概要
- Googole HomeやAmazon EchoなどのAIスピーカー・デバイスが人気だ。ネットを日常的に活用している人や、ITの深い知識がなくとも簡単にAIに触れることが出来るなどで活用が広がっている。反面、AIスピーカーを利用したことのある人は体験しているかもしれないが、日常生活でのよく利用する天気や音楽、スケジュールの照会、家電などの操作はAIスピーカーはそれなりに出来るものの、ちょっとした応用問題的な問いには答えられない場面によく遭遇する。

- 一方、企業やソーシャルにおけるチャットボットは、既にかなり実用化が進んでおり、多様な消費者との応答に対して、音声ではなくチャットの傾向が強いが、精度を増す傾向にある。多くのチャットボットシステムが開発され、その種類や量またはノウハウ、そしてアセットも蓄積され始めており、チャットボットであればより人間に近いやり取りが可能と言える未来が見えてきているとも考えられる。
- 人気で高性能の音声認識のAIスピーカーをフロント（消費者側）に、複雑な質問や専門知識、ロングテールでニッチな対話を企業の人工知能にと動的に連携できれば、より価値が高く、更に人間に近いボットが出来る可能性があると考えるのは自然な思考回路であろう。この記事では、Google　Home Miniと、チャットボットサービスの代表格であるIBM Watson Assistantを連携する技術的な手法について解説する。

#前提となる環境
- Google Home Mini（なくてもブラウザで代替も可）
- GoogleユーザーID
- IBM Cloud(旧Bluemix）のアカウント（無料トライアルで可）
- IBM Cloud(旧Bluemix)のカタログからNode-RED Starterのインスタンスを作成（ライトプラン（無料）で可）
- IBM Cloud(旧Bluemix)のカタログからWatson Assistantのインスタンスを作成（ライトプラン（無料）で可）

#処理の流れ
- グーグルは2017年11月9日、スマートスピーカー「Google Home」やAndroid端末などで利用できるGoogleアシスタントのサードパーティ連携機能「Actions on Google」を発表した。
https://developers.google.com/actions/
- このActions on Googleが外部サービスと連携するために、Google Homeのボットとの会話に利用するIntentsに処理が入った際に予め指定されたWebhookを実行し、決められたWebサービスを実行する機能が備わっている。
- 一方IBM Watson Assistantも会話のIntentsやEntity、Dialogを定義しチャットボットの会話を成立する機能を保有しており、これはREST APIで呼び出すことが可能である。このActions on GoogleとWatson AssistantをAPIで連携するが、出し側と受け側が想定しているAPI入出力のJSONのフォーマットが（当然）異なるため、そのままでは受け渡すことができない。
- そこで、Actions on GoolgeとWatsonの間のJSONフォーマットを調整し、受け渡す機能が必要になる。様々な技術的な方法があるが、今回はIBM Cloud（旧Bluemix）のNode-REDを利用しビジュアルにその処理を実行することにした。ビジュアルに機能を開発することができ、コードを書く量が少なくなり開発生産性が高いことが選択の理由である。大まかな登場するコンポーネントと処理の流れは以下のとおりとなる。
<img width="1192" alt="スクリーンショット 2017-12-12 1.08.44.png" src="https://qiita-image-store.s3.amazonaws.com/0/172568/60b3a562-f248-f705-d4ad-375fa9f1201c.png">

- 今回は、この処理の流れに沿って、手順通りに進めれば、決して難しくなくGoogle Home MiniとWatson Assistantを連携を実現できる手法を以下でガイドする。


#手順の詳細
##1.Watson Assistantでチャットボットを準備する
　ここではWatson Assistantでチャットボットを作成する手順は本題ではないので詳細は割愛する。以下の記事などを参考にされたい。

- IBM Watson でコーディングのできないド素人がチャットボットを作ってみた
 https://www.ibm.com/think/jp-ja/watson/chatbot-conversation-1/
- Watsonに博多弁をしゃべらせてみた - IBM THINK Watson - Japan
 https://www.ibm.com/think/jp-ja/watson/hakataben/

　上記の記事などにより、Watson Assistantの開発機能により、簡易なテスト用チャットボットを作成するか、既存の開発済のチャットボットのWorkspaceを利用する、Workspaceを新たなWatson Assistantサービスインスタンスにインポートされた状態など、何らかの形でWatson AssistantのチャットボットのWorkspaceが作成されている状態を想定する。Watson AssistantのWorkspace　IDが後続の処理で必要となるため、このWorkspace IDを確認する手順を以下に記述する。

Watson AssistantのWorspace　IDはAssistant ToolsのWorkspaceの右上の設定リンクから、View Detailsを選択することで確認することができる。このWorkspace IDは後段の手順で必要となるためメモ帳などに控えておく。
<img width="413" alt="スクリーンショット 2017-12-11 22.32.12.png" src="https://qiita-image-store.s3.amazonaws.com/0/172568/b0ee94b3-496d-e6b2-1850-fd35f1ef5d97.png">
<img width="414" alt="スクリーンショット 2017-12-11 22.31.38.png" src="https://qiita-image-store.s3.amazonaws.com/0/172568/7e8def48-560a-83f4-e81d-700fdfffcb21.png">





##2.Node-REDでActions on GoogleとWatson Assistantのメッセージの送受信機能を記述する

　　前述のとおり、Google Home Miniの機能を司るActions on GoogleとIBM Watson Assistantの間のメッセージの送受信を実現するコンポーネントをNode-REDで作成する。
Node-REDによるアプリケーション開発の手順については、当投稿の本題ではないため詳細は割愛するが、必要に応じて以下の記事などを参考にされたい。

- IBM Bluemix ハンズオン・セミナー Node-RED
https://www.ibm.com/developerworks/community/wikis/form/anonymous/api/wiki/463f5f7a-0339-49a2-9431-37d1f0f5aa0d/page/8bcc670a-c1d4-479e-acf0-5045f70b8bf3/attachment/daed2552-dc24-4af6-8e23-08d50953e7fe/media/BluemixHandsOn_NodeRed_20160315.pdf

また、当投稿のNode-REDのフローについては一から作成せずとも当該フローをNode-REDでインポートすることで実現できる手順は以下に付記しておく。

Node-REDで作成するフローは以下となり、開始と終端にはREST APIのRequest/Responseのためのhttp inとhttp outのノードを配置し、中間にはWatson　Converサービスの前後にメッセージの受信と加工の処理を配置、そして問題判別のためのデバッグのノードを配置している。
<img width="709" alt="スクリーンショット 2017-12-12 0.22.29.png" src="https://qiita-image-store.s3.amazonaws.com/0/172568/bd5ab5ba-02e4-11d6-a220-f54faa921edf.png">

このフローをImportするには、以下のJSONをコピーし、Node-REDフローエディター上で読み込みを行うことにより、フローを復元する事が可能である。復元した後に、フロー上のAssistantサービスのWorkspace IDを、前段でメモしておいたご自分のAssistantサービスのWorkspace IDに書き換える必要がある。

```JavaScript
[
  {
    "id": "429583a4.f231cc",
    "type": "watson-conversation-XX",
    "z": "f1b75d09.6e0f5",
    "name": "",
    "workspaceid": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXX",
    "multiuser": true,
    "context": true,
    "empty-payload": true,
    "default-endpoint": false,
    "service-endpoint": "",
    "x": 590,
    "y": 300,
    "wires": [
      [
        "33f48a6.ab13d76"
      ]
    ]
  },
  {
    "id": "4235440e.856b5c",
    "type": "http in",
    "z": "f1b75d09.6e0f5",
    "name": "rest api",
    "url": "/botchat",
    "method": "post",
    "upload": false,
    "swaggerDoc": "",
    "x": 173.24999237060547,
    "y": 300.99999618530273,
    "wires": [
      [
        "2afbdaf3.fe0b86"
      ]
    ]
  },
  {
    "id": "75572a92.0b5554",
    "type": "http response",
    "z": "f1b75d09.6e0f5",
    "name": "",
    "statusCode": "",
    "headers": {},
    "x": 1045.7499923706055,
    "y": 191.24999713897705,
    "wires": []
  },
  {
    "id": "33f48a6.ab13d76",
    "type": "function",
    "z": "f1b75d09.6e0f5",
    "name": "post processing",
    "func": "msg.payload.speech = msg.payload.output.text[0];\nmsg.payload.displayText = msg.payload.output.text[0];\n\nreturn msg;\n\n",
    "outputs": 1,
    "noerr": 0,
    "x": 797.75,
    "y": 300.5,
    "wires": [
      [
        "75572a92.0b5554",
        "e02e6768.992a58"
      ]
    ]
  },
  {
    "id": "e02e6768.992a58",
    "type": "debug",
    "z": "f1b75d09.6e0f5",
    "name": "",
    "active": true,
    "console": "false",
    "complete": "payload",
    "x": 1065.7499923706055,
    "y": 411.24999713897705,
    "wires": []
  },
  {
    "id": "2afbdaf3.fe0b86",
    "type": "function",
    "z": "f1b75d09.6e0f5",
    "name": "Webhookで送られてくるデータ",
    "func": "msg.payload=msg.req.body\n\nmsg.params = { \"context\": msg.req.body.context};\nif(typeof msg.req.body.context == 'undefined') msg.params.context = { \"conversation_id\" : undefined};\nelse msg.params.context.missmatchCounter = Number(msg.req.body.context.missmatchCounter);\n\nmsg.user = \"AAA\";  // Temp User Name\n\nreturn msg;\n",
    "outputs": 1,
    "noerr": 0,
    "x": 345.74999237060547,
    "y": 371.24999713897705,
    "wires": [
      [
        "20ab83ae.dd2f2c",
        "bb977b95.b22fb8"
      ]
    ]
  },
  {
    "id": "20ab83ae.dd2f2c",
    "type": "function",
    "z": "f1b75d09.6e0f5",
    "name": "変数any値の取り出し",
    "func": "msg.payload=msg.payload.result.resolvedQuery;\nreturn msg;",
    "outputs": 1,
    "noerr": 0,
    "x": 575.7499923706055,
    "y": 431.24999713897705,
    "wires": [
      [
        "54773cc1.082d24",
        "429583a4.f231cc"
      ]
    ]
  },
  {
    "id": "54773cc1.082d24",
    "type": "debug",
    "z": "f1b75d09.6e0f5",
    "name": "",
    "active": true,
    "console": "false",
    "complete": "false",
    "x": 768.2499923706055,
    "y": 478.24999713897705,
    "wires": []
  },
  {
    "id": "bb977b95.b22fb8",
    "type": "debug",
    "z": "f1b75d09.6e0f5",
    "name": "",
    "active": true,
    "console": "false",
    "complete": "false",
    "x": 534.2499923706055,
    "y": 499.24999713897705,
    "wires": []
  }
]
```

##3.Actions on GoogleでのProjectの作成と開発

●Actions on GoogleのProject作成

いよいよGoogle Home MiniとWatson Assistantの連携の本体の機能である、Actions on GoogleのConsoleからProjectを作成する。
https://console.actions.google.com
<img width="709" alt="スクリーンショット 2017-12-11 16.19.14.png" src="https://qiita-image-store.s3.amazonaws.com/0/172568/d4f49ae9-0aa4-06ff-e682-412a7084f97b.png">

Add project画面でProject NameとCountry/Regionを指定してCREATEを押下。
ProjectのOverviewの画面から、DialogflowのBUILDを押下。
<img width="709" alt="スクリーンショット 2017-12-11 16.21.19.png" src="https://qiita-image-store.s3.amazonaws.com/0/172568/3f5560cc-0903-de57-e163-ca9cddeff1c3.png">


Use Dialogflow to add actions to your Assistant appの画面から、CREATE ACTIONS　ON DIALOGFLOWボタンを押下
<img width="713" alt="スクリーンショット 2017-12-11 16.24.54.png" src="https://qiita-image-store.s3.amazonaws.com/0/172568/591aad39-b93f-8b94-52c9-f7574de3cded.png">


Dialogflowの該当プロジェクトの設定画面から、DEFAULT LANGUAGEでJapanese-jaをプルダウンで選択し、上部のCREATEボタンを押下。
<img width="709" alt="スクリーンショット 2017-12-11 16.25.45.png" src="https://qiita-image-store.s3.amazonaws.com/0/172568/33359db5-cbea-3204-83c3-22b0edf90ada.png">


Intentsの作成画面に遷移し、Default　Fallback　IntentとDefault Welcome Intentが生成されている。ここではIntentsを作成せず、Intentsを作成する際に指定するNode-REDのWebhookの作成を先に行う。


●Fulfillment - Webhookの作成

DialogのメニューからFulfillmentをクリックし、Fullfillment作成の画面でWebhookのスイッチをDisabledからEnabledに変更する。
WebhookのURLを、２の手順で作成したNode-REDのURLを指定し、スクロールダウンして、SAVEボタンを押下。
<img width="709" alt="スクリーンショット 2017-12-11 19.54.44.png" src="https://qiita-image-store.s3.amazonaws.com/0/172568/907a15db-a746-6dbc-8dc3-7e6c57147f87.png">

●Watson連携のIntentsの作成

Watsonと連携するためのIntentsは３つ作成する。
①startWatsonSeeeion：Watsonとの連携セッションの開始
②eventWatsonSession:WatsonAPIでの処理
③endWatsonSession:Watsonとの連携セッションの終了

Intents 「startWatsonSeeeion」

|項目	|設定内容	|補足
|---|---|---|
|Intents名|startWatsonSeeeion
|Contexts(output)	|conversationWithWatson	|Watsonとの連携処理中であるという処理状態（Context）を開始している。変数の左に丸で表示されている数字はLifespanと呼ばれ状態を表す。この定義では５を設定する。
|User says	|ワトソンに聞いてみて|日本語でいうので「Watson」ではなく「ワトソン」とタイプしている	
|Text response	|ワトソンとの会話を開始します。ワトソンとの会話を終了するときは、ワトソンとの会話終了といってください。

上記のIntentsの設定を行い、上部のSAVEボタンで保存。

補足情報としては、Contextは、Watsonとの連携処理中であるという処理状態（Context）をconversationWithWatsonというContext変数で表す。開始の場合のLifespanの番号は５を指定する。
<img width="567" alt="スクリーンショット 2017-12-11 17.11.24.png" src="https://qiita-image-store.s3.amazonaws.com/0/172568/88cd14c5-e3a2-35bb-509e-ad7dc1e79308.png">


Intents 「eventWatsonSeeeion」

|項目	|設定内容	|補足
|---|---|---|
|Intents名|eventWatsonSeeeion
|Contexts(input)	|conversationWithWatson	|Watsonとの連携処理中であるという処理状態（Context）を引き継いでいる。Lifespanは５を設定。
|Contexts(output)	|conversationWithWatson	|Watsonとの連携処理中であるという処理状態（Context）を後続に渡す
|User says	|*(半角アスタリスクのみ入力）|アスタリスクをマークしてダブルクリックし、Entityに「@sys.any」を割り当てる）|	
|Text response	|外部接続のエラーです|webhookが失敗したときにこのメッセージが返答されます。
|Use webhook|チェック
|Use webhook for slot-filling|チェック

上記のIntentsの設定を行い、上部のSAVEボタンで保存。

補足情報としては、Contextには、最初の一文字（この場合はC）を入力すると、既に登録されているContext変数のリストがプルダウンに現れ、変数名は全て入力する必要はない。

User saysに＊（半角アスタリスク）を入力し、マークしてダブルクリックするとシステムエンティティのリストが現れるので、＠ｓｙｓ.anｙを選択すると、ENTITYに＠ｓｙｓ．anyが自動で指定される。、Watsonとの連携処理中であるという処理状態（Context）をconversationWithWatsonというContext変数で表す。この場合の番号は５（デフォルト）を指定する。
<img width="567" alt="スクリーンショット 2017-12-11 17.38.51.png" src="https://qiita-image-store.s3.amazonaws.com/0/172568/18952d44-9f1a-31e9-31ba-3842f39a3625.png">

Intents 「endWatsonSeeeion」

|項目	|設定内容	|補足
|---|---|---|
|Intents名|endWatsonSeeeion
|Contexts(input)	|指定なし
|Contexts(output)	|conversationWithWatson	|このContext変数を指定し、Lifespanは０を指定する
|User says	|ワトソンとの会話終了|	
|Text response	|ワトソンとの会話を終了します|
|End conversation|チェック

上記のIntentsの設定を行い、上部のSAVEボタンで保存。

補足情報としては、Context（output)には、conversationWithWatsonのContext変数を設定し、lifespanに０を設定する。lifespanの数字はカーソルを当てると編集が可能。０を指定すると自動で変数に取り消し線が入る。
<img width="567" alt="スクリーンショット 2017-12-11 17.58.57.png" src="https://qiita-image-store.s3.amazonaws.com/0/172568/0868e549-0cea-a9cd-f3c5-b74956f2eca4.png">


● Google Assistanceとの連携を設定する

DailogflowのメニューのIntegrationを選択し、Integrationsの画面から、Google Assistantを選択が選択され、スイッチがオンになっていることを確認する。
<img width="709" alt="スクリーンショット 2017-12-11 18.05.57.png" src="https://qiita-image-store.s3.amazonaws.com/0/172568/da6b63a3-f298-3a1b-01cf-daa0c1de5a37.png">

Settingのリンクをクリックすると、Google Assistantの設定画面が表示される。Testボタンを押下し　Test now activeが表示されたら、Actions Simulatorを選択する。
<img width="567" alt="スクリーンショット 2017-12-11 18.09.43.png" src="https://qiita-image-store.s3.amazonaws.com/0/172568/7a9448e7-2032-7e13-da4c-3f157c9acb35.png">

Action on Googleの当該プロジェクトのSimulatorコンソール画面が表示される。

●　チャットでプロジェクトのテストを行う

テスト用アプリのダイアログで、「テスト用アプリを起動する」を入力すると、テスト用アプリが起動され、「わかりました。 テストバージョンのテスト用アプリです。」「こんにちは！」などの返答がアプリから返ってくる。
次に定義したIntentsをテストする。「ワトソンに聞いてみて」を入力すると、「ワトソンにつなぎます。ワトソンとの会話を終了したいときは、ワトソンとの会話終了、と言って下さい。」との返答が返ってくる。
<img width="1680" alt="スクリーンショット 2017-12-11 18.11.26.png" src="https://qiita-image-store.s3.amazonaws.com/0/172568/412551bf-349b-7432-dc17-00e0a7c1e57f.png">

ここから先は、Watson Assistant　のWorkspaceで定義したAssistant側の定義に沿った会話となる。質問を投げて、意図した回答が帰ってくれば連携は成功。

うまくいかない場合は、Action on GoogleのSimulatorコンソールのJSON出力やNode-REDのdebugの出力を見ながら問題判別を行う。

##4.Google Home Miniで話させてみる

実は、前述のテストまで終わっていれば、自分のGoogleのIDの環境には、なにもせずともテスト用アプリはデプロイされている。Google Home Miniのデバイス設定で、自分のGoogle IDがセットされていれば、既に使える状況になっている。

「OK、グーグル、テスト用アプリを起動して」と話しかけると、「わかりました。 テストバージョンのテスト用アプリです。」「こんにちは！」などの返答がGoogle Homeから返ってくる。この状態で、上記と同じ内容で開発していれば「ワトソンに聞いてみて」と話しかけると、テスト用アプリのIntents「startWatsonSession」に入り、それ以降の発話は全て「eventWatsonSession」に入り、Webhookに定義したWatson AssistantのBotの会話になり、その返答はGoogle Homeが喋ることになる。

このテスト用アプリを公開すると、Googleに公開の申請が提出され、承認されると公開機能になるらしいが、筆者はまだ公開のプロセスは試してみていない。実際にやった方がいれば、是非フィードバックいただければありがたい。

#【参考文献】
- Create an App for the Google Assistant that Uses Your Server:
https://youtu.be/ykSFAs6S_Kg
- Actions on GoogleでGoogleHome向けTwitter検索アプリを作ってみる:
https://kotodama.today/?p=228
- Actions on Googleでapi.aiを使ってGoogle Homeに何か言わせてみる:
https://qiita.com/syarihu/items/53ea1a65f481f8121109
- DialogflowのwebhookにNode-REDを使ってみる：https://qiita.com/HiroyukiYasui/items/6943cc7a636c9d78a9ff