Google Assistant経由でWatson Assistantと会話する(Conversational Actions版)

#やったこと
Google Assistant経由で、Watson Assistantと対話するサンプルはすでにIBM Developerサイトに掲載されており、gitでコードも公開されています
ただし、Google Assistantの"Actions On Google"は、もともとは「Dialogflow」というものが利用されていたものの、2020/6に「Conversational Actions」がリリースされ、こちらが標準になったようです。公開されているサンプルはDialogflow前提のようなので、この記事ではConversational ActionsでWatson Assistant対話させてみました。

![ezgif.com-gif-maker.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/76526/8ee4fcf9-ea16-222f-d1d3-4ba0fe79e7ef.gif)


ただし、対応はまだ中途半端で、以下の制約があります。
・Google Assistantの固定のシーン(Watsonで言うダイアログ)で会話させるようにしているが、検証で確認している限り、同じシーンに3回戻すと強制的に会話が終了されてしまう
・Watson Assitantの会話終了を拾って、Google Assistant側の対話を終了するハンドリングが未実装


参考.Watson Assistant を利用して Google アクションを作成する
https://developer.ibm.com/jp/technologies/artificial-intelligence/patterns/create-an-agent-for-rental-car-reservations/

参考.watson-google-assistant
https://github.com/IBM/watson-google-assistant


#概要
Conversational ActionsからIBM Cloud Functions経由でWatson Assistantにリクエスト、レスポンスを受け取ります。IBM Cloud Functionsで、Conversational ActionsとWatson Assistantとでやり取りさせるために、jsonパラメーター上のデータの入れ替えを行っています。

![Untitled Diagram.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/76526/b9f93fa4-645f-9ab0-9800-87c5677b16c3.png)

#解説
##Conversational Actionsの設定
Main InvocationとNo_matchインテントから、webhooksを呼び出す設定にしておく。そして「askwatson」というSceneを作成しています。こうすることでWebhooks経由でWatson Assistantにリクエストが飛び、google Assistant上では「askwatson」というscene上で処理が進みます。

詳細は手順参照。

##Conversational Actionsのリクエストのパース
Conversational Actionsからリクエストを受け取り、パースしている部分のコードを以下に記載しています。
sessionIdとsceneの名前を取得し、グローバル変数に格納しています。
Watson Assistantのcontextは、Google Assistantのセッションストレージというパラメーターに格納されている想定で取得しています。

参考.Session storage
https://developers.google.com/assistant/conversational/storage-session

```Javascript
if (!args.__ow_body) {
        return reject(errorResponse('body is required'));
      }
      console.log("__ow_body:")
      console.log(args.__ow_body);
      const rawBody = Buffer.from(args.__ow_body, 'base64').toString('ascii');
      console.log("rawBody:");
      console.log(rawBody);
      const body = JSON.parse(rawBody);
      console.log("body:");
      console.log(body);

      // google assistant attributes hold our context
      const googleAssistantSessionStorage = body.session.params;
      console.log('googleAssistantSessionStorage attributes:');
      console.log(googleAssistantSessionStorage);
      if (typeof googleAssistantSessionStorage.hasOwnProperty(googleAssistantSessionStorage, 'watsonContext')) {
        console.log("set watsonContext from googleAssistantSessionStorage.")
        context = googleAssistantSessionStorage.watsonContext;
      } else {
        context = {};
      }

      //global googleSesionId
      googleSesionId = body.session.id
      googleScene = body.scene.name

      const query = body.intent.query;
      console.log('input:' + body.intent.query);
      console.log('context:'+context)
```

##Watson AssistantのレスポンスをConversational Actionsに返す
sessionIdとsceneをセットし、Watson Assistantのcontextは、Google Assistantのセッションストレージに格納して返しています。

参考.conversational actionsのwebhookのパラメータ
https://developers.google.com/assistant/conversational/webhooks

```Javascript
function sendResponse(response, resolve,reject) {
  try{
    console.log('Begin sendResponse');
    // console.log(response);

    // Combine the output messages into one message.
    const output = response.result.output.text.join(' ');
    console.log('Output text: ' + output);

    resolve({
      body:formatResponseForGoogleConv(output)
      });
  }catch(err){
    reject({body:formatResponseForGoogleConv(err)});
  }
}

function formatResponseForGoogleConv(outputText){
  console.log("outputText");
  console.log(outputText);
  console.log("googleSesionId");
  console.log(googleSesionId);
  console.log("context");
  console.log(context);
  console.log("googleScene");
  console.log(googleScene);
  return {
    "session": {
      "id": googleSesionId,
      "params": {
        "watsonContext":context
      }
    },
    "prompt": {
      "override": false,
      "firstSimple": {
        "speech": outputText,
        "text": ""
      }
    },
    "scene": {
      "name": googleScene,
      "slots": {},
      "next": {
        "name": "askwatson"
      }
    }
  }
}
```

#手順
リポジトリはこちら。利用手順はReadmeに記載しています。
https://github.com/GodaiAoki/watson-google-assistant/blob/master/README.md