Dialogflow と Firebase Cloud Functions で Actions On Google 作り

日本語で話しかけることができるGoogle Homeに期待を込めて。タイトルのようにActions On Googleを作成してみましたのでやり方手順を共有してみようと思います。

# 作るものと手順

今回作ったのは、**”会社でグループによって振られる所属コードをGoogle Homeに教えてもらう”**と言うもの。

自グループの所属コードは、社内ポータルにあるドキュメントに記載されているのですが、いちいち探しに行くのが面倒なので、グループ名をGoogle Homeに問いかけて答えてくれるようにします。手間が省けますね！（メモしておけ？ メモを探すのがめんどくｓ）

すごく個人的な内容であるため、Googleの審査に出すのもどうかと思うので、今回はテストモードで実行できるようになるところまで実装していこうと思います。

※この投稿での所属コードは念のため、デタラメなものにマスクしてあります。

手順は下記のようになりました。

- Actions On Googleプロジェクト作成
- Dialogflow作成①
- Firebase Cloud Functions作成
- Dialogflow作成②
- Actions On Googleプロジェクト設定
- 実機（シミュレーション）テスト

では手順に沿って解説していきます。

# Actions On Googleプロジェクト作成

Actions On Googleプロジェクトは、[Actions Console](https://console.actions.google.com/?pli=1)へとアクセスし、作成を行います。"TeachGroupCode"という名前でプロジェクトを作成しました。
<img width="1196" alt="Screen Shot 2017-10-26 at 10.33.28.png" src="https://qiita-image-store.s3.amazonaws.com/0/92506/ce8f68e9-37a1-a4ef-2f91-6ed07f15c454.png">


# Dialogflow作成①
プロジェクトを作成したら、Overviewタブをクリックします。Assistant app draft 画面が表示されるので、その順番通りに設定していきます。最初はActionsの作成です。

"ADD ACTIONS"ボタンから遷移した画面で Dialogflow カードの"BUILD"をクリックします。開いたダイアログの"CREATE ACTIONS ON DIALOGFLOW"ボタンをクリックし、Dialogflow のページに遷移します。（当リンクからDialogflowページにアクセスしないとプロジェクトとDialogflowが紐付かなそうな気がします。）

## Agent作成

遷移先画面の Create Agent で DEFAULT LANGUAGE と DEFAULT TIME ZONE を日本のものに変更し、"SAVE"して　Agent　を作成します。

## Entities作成

Agent が作成されると自動で Agent設定画面に遷移します。

Dialogflowでは、会話に関する設定を行います。
まずは Entities から設定していきます。
<img width="924" alt="Screen Shot 2017-10-26 at 11.43.25.png" src="https://qiita-image-store.s3.amazonaws.com/0/92506/7c8c2b3b-6503-545a-59b8-1467c0d3b39e.png">


Entities は、Google Assistant が単語を聞き取るためのものです。

私が所属しているグループは「R&Dグループ」と言いますが、人によって呼び方が変わることがあります。
例えば、「R&D」、「R&Dグループ」、「RDグループ」、「Research & Developmentグループ」。
上記したように、人によって色々な呼び方をされることが考えられます。

ですので、違う呼び方をされても、「R&Dグループ」のことを指しているということを認識させるため、「呼び方は違うけど、同じグループのことだよ」というのをあらかじめ　Google Assistant　に教えておく必要があります。
その設定を行うのが　Entities　になります。

Entitiesは、下記のように設定しました。

| Enter Reference Value | Enter Synonym     |
|:----------------------|:------------------|
| 13111010              | R&D, R&Dグループ, RDグループ, Research&Developmentグループ |
| 13111020              | 技術企画グループ, 技企, 技企画, 技企画グループ |
| 13112000              | テクニカルグループ, テクニカル, テク,　テクニカルサポートグループ |

<img width="910" alt="Screen Shot 2017-10-26 at 12.50.17.png" src="https://qiita-image-store.s3.amazonaws.com/0/92506/df271407-d330-a772-e231-d498bd97e805.png">

後の処理で、一意に判別するために、"Enter Reference Value"の値は一意な所属コードにしておきます。"Enter Synonym"には、その所属コードのグループ名の呼ばれ方を思いつく限り入力しておきます。

入力できたら"SAVE"をクリックし、作成完了です。

## Intents作成

次は Intents の作成を行います。
Intents には会話のパターンを登録します。

Intentsタブを開くと既に登録されている Intent が存在します。"Default Welcome Intent"は、このActionを起動した時に実行する会話パターン、"Default Fallback Intent"は、うまく内容を聞き取れなかった場合にする会話パターンになっています。
<img width="942" alt="Screen Shot 2017-10-26 at 13.58.45.png" src="https://qiita-image-store.s3.amazonaws.com/0/92506/f392fa85-9741-949f-7394-1f76160d24ba.png">


ここでは新たに、ある特定の会話を投げかけると特定のアクションを返すという　Intent　を作成します。

"CREATE INTENT"ボタンをクリックします。
開いた画面の"User says"に"{Entitiesで登録した単語}の所属コード"と入力して、Enterを押下します。
すると、{Entitiesで登録した単語}のバックグラウンド色が変わり、下図のように下にある表に行が追加されます。

<img width="913" alt="Screen Shot 2017-10-26 at 14.15.45.png" src="https://qiita-image-store.s3.amazonaws.com/0/92506/f56700fe-4c79-0a20-dd83-2cf3d14f194a.png">

こうすることで、"Entitiesに登録された単語を含む会話パターン"が作成されます。
下図のように考えられる会話パターンを出来る限り登録しておきましょう。

<img width="650" alt="Screen Shot 2017-10-26 at 14.24.25.png" src="https://qiita-image-store.s3.amazonaws.com/0/92506/8b11adcd-e668-d620-2433-c28bbfea5cdf.png">

この Entities の機能で、User says の黄色背景部に色々な単語が当てはまるようになりますが、バックグラウンドである Firebase に渡される値は、Entities で登録したそれぞれの"Enter Reference Value"になります。

Action項目には、バックグラウンドに渡す任意のAction名を登録します。バックグラウンド側で登録したAction名を指定すると任意のアクションを実行させることができます。

以降の項目ですが、"Response"は空で、"Google Assistant"の"End conversation"には、チェックを入れます。
"End conversation"は、Intent を実行後、Actionを終了するかを設定する項目になります。
<img width="650" alt="Screen Shot 2017-10-26 at 14.33.07.png" src="https://qiita-image-store.s3.amazonaws.com/0/92506/0cdeec6b-def6-00dc-48e4-55571105cd1d.png">



# Firebase Cloud Functions作成

ここまで　Dialogflow　の設定をしたら、次は　Firebase　での設定に入っていきます。

Actions Console　に戻り、"Backend services"タブを選択します。その後、"Cloud Functions"の"GET STARTED"をクリックします。
<img width="968" alt="Screen Shot 2017-10-26 at 14.35.36.png" src="https://qiita-image-store.s3.amazonaws.com/0/92506/8de04b0d-2392-623f-7909-2a37e44f6fc4.png">


Firebase　の実行環境ができていない場合は、`npm install -g firebase-tools`コマンドを実行しておきましょう。
また、Cloud Functions　は、Node　v6.11.1 を実行するので、Nodeのバージョンには注意してください。該当環境以上でなければ、上記コマンドを打つとエラーが出るかと思います。

バックグラウンド処理はテンプレートを利用して作成していきます。
https://github.com/actions-on-google/dialogflow-webhook-template-nodejs
上記Githubのソースコードをクローンします。

```
sudo git clone https://github.com/actions-on-google/dialogflow-webhook-template-nodejs.git
```

クローン後、`firebase login`コマンドを実行してログインを行い、Firebaseプロジェクトのディレクトリに移動後、`firebase init functions`コマンドを実行します。
実行中、既にあるファイルを上書きするかを2回ほどと、npmで依存関係をインストールするかの選択が出てきます。前の2つは"N"で答え、最後の「npm環境をインストールするか？」については"Y"で答えてください。
<img width="593" alt="Screen Shot 2017-10-26 at 14.53.33.png" src="https://qiita-image-store.s3.amazonaws.com/0/92506/14f8b199-2a71-89cf-265e-18aac70f6dd0.png">

※permission系のエラーが出た場合は管理者権限で実行し直してください。

"actions-on-google sdk"などのモジュールがプロジェクトに組み込まれます。

バックグラウンド側の処理を、テンプレートを変更して実装していきます。
下記が今回実装したソースコードになります。

```js:index.js
'use strict';

process.env.DEBUG = 'actions-on-google:*';
const { DialogflowApp } = require('actions-on-google');
const functions = require('firebase-functions');

exports.yourAction = functions.https.onRequest((request, response) => {
  const app = new DialogflowApp({request, response});
  console.log('Request headers: ' + JSON.stringify(request.headers));
  console.log('Request body: ' + JSON.stringify(request.body));

  // Fulfill action business logic
  function responseHandler (app) {
    var speechStr = "";
    const uidStr = JSON.stringify(request.body.result.parameters.GroupCode);
    var uid = uidStr.replace(/\"/g, "");
    if (uid === "13111010") {
      speechStr = "R&Dグループの所属コードは、1、3、1、1、1、0、1、0 です。";
    } else if (uid === "13111020") {
      speechStr = "技術企画グループの所属コードは、1、3、1、1、1、0、2、0 です。";
    } else if (uid === "13112000") {
      speechStr = "テクニカルグループの所属コードは、1、3、1、1、2、0、0、0 です。";
    } else {
      speechStr = "該当するグループはありません。";
    }
    app.tell(speechStr);
  }

  const actionMap = new Map();
  actionMap.set('action.teachcode', responseHandler);

  app.handleRequest(actionMap);
});
```

if文でがーっと書いちゃいましたが、Firebase　の　Database　などを使って華麗に値を返すのが普通で、スマートだと思います。

responseHandler の中に、実行したい処理を実装します。
actionMap.set の引数に Dialogflow で決めたaction名（今回action.teachcode）を指定します。

所属コードの部分は、数字を連ねて書くと「いっせんさんびゃくじゅういちまん〜」と読まれてしまうので、一桁ずつ読んでもらうため、句読点を打って区切っています。

処理が書けたら、下記コマンドを実行します。
`firebase deploy --only functions`
エラーがなければ、Firebase に Function がデプロイされ、Function のエンドポイントが付与されます。
![Screen Shot 2017-10-26 at 15.56.09.png](https://qiita-image-store.s3.amazonaws.com/0/92506/6a1ffc0e-6c57-66b6-5d45-63fd5912d90d.png)

ダッシュボードでFunctionのエンドポイントを確認したら、再びDialogflowに戻ります。

# Dialogflow作成②

Dialogflow にて、Fulfillmentタブを選択します。Webhock のトグルをオンにして、先程作成した Function のエンドポイントをURL項目に貼り付けます。
<img width="918" alt="Screen Shot 2017-10-26 at 16.00.19.png" src="https://qiita-image-store.s3.amazonaws.com/0/92506/995591df-c4df-f5cc-fd47-f94819b69fb0.png">

Fulfillment を設定したら、作成した Intent に Fulfillment項目が表示されるので、"Use webhock"にチェックを入れます。
<img width="652" alt="Screen Shot 2017-10-26 at 16.00.52.png" src="https://qiita-image-store.s3.amazonaws.com/0/92506/5c305c92-b65b-6bbc-a161-a07db74f64cf.png">

"Use webhock"はバックグランド処理を利用するかの設定になっています。

次に　Integrationタブを選択し、Google Assistant の SETTINGSトグルがオンになっていることを確認後、カードを押下します。
ダイアログが開くので、"UPDATE DRAFT"ボタンを押下します。

これで Actions On Google の処理部分が完成です。
"Try it now"項目に会話を入力して意図した答えが返ってくるか確認しておきましょう。


# Actions On Googleプロジェクト設定

仕上げです。
Actions Console に戻り、App information を設定します。
<img width="1212" alt="Screen Shot 2017-10-26 at 16.04.46.png" src="https://qiita-image-store.s3.amazonaws.com/0/92506/1037d05d-5044-8b8e-b96b-942837d7e7f6.png">

設定するのは、App informationと言う通りアプリの情報です。
アプリの名前や概要、アイコンを設定していきます。

Actions On Google を利用する際には、まず「{アプリ名}につないで」というように話しかけます。
その際のアプリ名が、最初の項目 Assistant app name の Pronunciation となりますので、呼びかけやすい名前に設定しておきましょう。
Image などは Google　Home で利用する時には出てきませんが、Actions On Google は、Allo やチャットアプリなどと連携できるのでその際に必要となります。必須なので埋めましょう。アプリ提供の際のアイコンにもなるのだと思います。今回は公開しないので意味ないですが。。。

Policy もどうやって設定すればいいのかわかりにくいですが、テンプレートのGoogleドキュメントをコピーして内容をアプリのポリシーに書き換え、そのドキュメントのURLを入力すればいいようです。"LEARN MORE"に書いてありました。
※Account Linking は必須ではないので今回設定は行いません。


# 実機（シミュレーション）テスト

設定を埋め、保存を行い、Overview に戻ります。
![Screen Shot 2017-10-26 at 16.14.30.png](https://qiita-image-store.s3.amazonaws.com/0/92506/10d53a85-51c6-781c-dddc-2e670915b61f.png)

その画面で"TEST DRAFT"を押下すると、Actions On Google を作成したアカウントに紐づく、Google Home で Actions On Google が実行できるようになっています。
（ちなみに隣のボタン"SUBMIT DRAFT FOR REVIEW"を押下するとGoogleへレビュー依頼がいきます。）

Google Home に話しかけてみると、うまくいきました。

会話は下記のようになりました。

- 自「OK, Google」
- 自「GroupCodeにつないで」
- GH「はい、テストバージョンのGroupCodeです。」
- GH「こんにちは。」
- 自「R&Dグループの所属コード」
- GH「R&Dグループの所属コードは、1、3、1、1、1、0、1、0です」

Action を起動すると急に片言になります。
英語をしゃべらせようとすると和製英語になったり、どうやってアクセントなどをコントロールするのかなど考える余地はありますが、ともあれ実機で　Action　を実行することができました。

いい感じに話せるようにするには今後の研究次第って感じですかね。

複数Actionを作ろうとすると、すぐにデフォルトのプロジェクト作成上限の3つに引っかかりそうですが、Intentやバックグラウンド処理に色々ぶち込みまくれば、1つのプロジェクトで結構いいように使えるのではないかと思いました。

どうしても聞き取ってくれない単語はありますが。。。（私の名字とか

# 参考
- [はじめての Actions On Google](https://www.slideshare.net/nsface/actions-ongoogle)