【Rails】Dialogflow(V1)(旧API.ai)からDialogflow(V2)への移行

## 概要

- Dialogflow V1 は今年の10月で廃止予定(`Dialogflow API V1 will be deprecated on October 23rd, 2019.`) とのことで、既存のシステムを改修してDialogflow V2 対応をすることになった。
- 使用していたgemはV1対応の `'api-ai-ruby'` だったので、Googleさん公式でV2対応の `'google-cloud-dialogflow'` に切り替えるとにした。OAuth2とかもgemでカバーしてくれているみたいなのでありがたい。

## 前提

- 既存の実装が、リクエストごとに文脈(`contexts`)を指定する方式だったので、本題の移行作業は最小限に抑えられている。
- Dialogflow側であれこれ設定している場合は、これよりもう少し手間がかかりそうでございまする。

## :memo: V1とV2の比較(cURL)

- 雰囲気を理解するためにメモ程度です。
- Dialogflow V1(旧API.ai)へのリクエスト
    - 参考： https://dialogflow.com/docs/reference/agent/query

``` shell
# Dialogflow V1の access token
API_AI_CLIENT_ACCESS_TOKEN="*****************************"
# 文脈
CONTEXTS=shop

curl \
  --request POST \
  --header 'Authorization: Bearer '${API_AI_CLIENT_ACCESS_TOKEN}'' \
  --header 'Content-Type: application/json; charset=UTF-8' \
  --include \
  --data '{
    "contexts": [
      "'${CONTEXTS}'"
    ],
    "lang": "jp",
    "query": "定期のお申し込み内容を変更する",
    "sessionId": "12345",
    "timezone": "Asia/Tokyo"
  }' \
  "https://api.dialogflow.com/v1/query?v=20150910"
```

- Dialogflow V2へのリクエスト
    - 参考： https://cloud.google.com/dialogflow-enterprise/docs/reference/rest/v2beta1/projects.agent.sessions/detectIntent

``` shell
GOOGLE_PROJECT_ID=**************
SESSION_ID=12345
ACCESS_TOKEN=**************
ACCESS_KEY=*************
# 文脈
CONTEXTS=shop

curl --request POST \
  'https://dialogflow.googleapis.com/v2beta1/projects/'${GOOGLE_PROJECT_ID}'/agent/sessions/'${SESSION_ID}':detectIntent?access_token='${ACCESS_TOKEN}'&alt=json' \
  --header 'Authorization: Bearer '${ACCESS_KEY}'' \
  --header 'Accept: application/json' \
  --header 'Content-Type: application/json; charset=UTF-8' \
  --data '{
    "queryParams": {
      "timeZone": "Asia/Tokyo",
      "contexts": [
        {
          "name": "projects/'${GOOGLE_PROJECT_ID}'/agent/sessions/'${SESSION_ID}'/contexts/'${CONTEXTS}'"
          "lifespanCount": 5
        }
      ]
    },
    "queryInput": {
      "text": {
        "text": "定期のお申し込み内容を変更する",
        "languageCode": "ja"
      }
    }
  }' \
  --compressed
```

## :memo: V1とV2の比較(Rails)

- 雰囲気を理解するためにメモ程度です。
- Dialogflow V1(旧API.ai)へのリクエスト(`'api-ai-ruby'` 使用)

``` ruby
# クライアント用意
api_ai_client = ApiAiRuby::Client.new(
  client_access_token: '**************',
  api_lang: 'ja',
  api_base_url: 'https://api.api.ai/v1/',
  api_version: '20150910',
  timeout_options: [:global, { write: 1, connect: 1, read: 1 }]
)
# セッションIDをセット
api_ai_client.api_session_id = 12345
# API実行
origin_response = api_ai_client.text_request(
  "定期のお申し込み内容を変更する",
  contexts: ['shop'],
  resetContexts: true
)
```

- Dialogflow V2へのリクエスト(`'google-cloud-dialogflow'` 使用)
    - `#detect_intent` の引数はオブジェクトもしくはハッシュ形式のようなのでこの例はハッシュ。

``` ruby
# クライアント用意
session_client = Google::Cloud::Dialogflow::Sessions.new
session = session_client.class.session_path(
  '****************', # プロジェクトID
  '12345'             # セッションID（文字列32文字まで）
)
# 入力するJsonをハッシュで宣言
query_input = {
  text: {
    text: '定期のお申し込み内容を変更する',
    language_code: 'ja'
  }
}
# コンテキストなどのオプションを指定
query_params = {
  time_zone: "Asia/Tokyo",
  contexts: [
    {
      name: "#{session}/contexts/shop",
      lifespan_count: 5
    }
  ]
}
# API実行
response = session_client.detect_intent(session, query_input, query_params: query_params)
# 結果を見れる
query_result = response.query_result
```

## やったこと

### Dialogflow側の準備

1. V1のエージェントをバックアップ
    - 「Export and Import」から「Export as zip」があったので実行。
    - Web上で全部できるの便利。
2. V2用に新しくエージェントを作成
    - メニューから「Create new agent」
    - 今回は別エージェントを用意するようにした。
    - 先のZIPファイルを「Import from Zip」で取り込む。
3. V2に切り替え
    - 新しいエージェントはV1と同じものができている（はず）なのでそのまま「API VERSION」を「V2 API」に切り替えた。
4. 資格ファイル（JSON）の獲得
    - Dialogflowでエージェントを新規に作ると、自動でGoogleCloudPlatform(GCP)のプロジェクトが作られていた。
    - 「Service Account」のリンクからGCPに飛べるので、ここから資格ファイルまで獲得する（後ほどgemで使う）
    - サービスアカウント編集から、「キーを作成」でJSONファイルが作成できる。保存する。

### Rails側の修正

- gem追加(`'google-cloud-dialogflow'`)
    - Gemfileを修正してbundle install
    - リリースの都合で古いGemも残している。

``` ruby:Gemfile
# api.ai
# DialogflowのV1は廃止となる。(V1が利用できるのは2019/10/23まで)
gem 'api-ai-ruby'
# DialogflowのV2対応のクライアントツール
# @see {https://github.com/googleapis/google-cloud-ruby/tree/master/google-cloud-dialogflow}
# @example {https://github.com/GoogleCloudPlatform/ruby-docs-samples/blob/master/dialogflow/detect_intent_texts.rb}
gem 'google-cloud-dialogflow'
```

- 資格ファイル(JSON)の配置、環境変数の設定
    - 先に獲得したJSONファイルをサーバー内に配置し、Railsの環境変数にパスを設定。
    - これを指定するだけでGemがいいかんじに認可の処理をしてくれている？？

``` shell
# Google Cloud の認証情報を記したJsonファイルのパス
# (各認証情報をそれぞれ宣言しても良い)
GOOGLE_APPLICATION_CREDENTIALS="/app/hogehoge-************.json"
```

- 呼び出し処理の実装
    - 先に書いていたように `Google::Cloud::Dialogflow::Sessions` クラスでクライアントを作成して `#detect_intent` を実行するだけ。
    - インフラ用のServiceクラスでも切り出して実装しておくと便利。

## つまずいたところ・うまくいったところ

- :scream: **認証がわからなかった。OAuth2つかうの？？**
    - JSONファイル配置するだけでよかった。Gemって便利。
    - GCPプロジェクト分けるときは、JSONファイルの内容を個別に宣言すればできる。
- :scream: **名前が違う。**
    - 該当プロジェクトに配属されたの初めてだったから詰んだ。ソース内をDialogflowで全文検索しても出てこない（笑）
    - api.ai の歴史も学んでおこう。
- :scream: **文脈(contexts)が無視される**
    - `lifespanCount` のデフォルトが0回だったため。contextsをせっかく指定しても寿命が切れていて使われていなかった。
    - `:lifespan_count` を指定しよう。
- :smiley: Googleさんの資料がめっちゃわかりやすい
    - Gemは読んだらだいたい分かる。↓↓このへんめっちゃ読んだ。
        - https://github.com/googleapis/google-cloud-ruby/blob/master/google-cloud-dialogflow/lib/google/cloud/dialogflow/v2/sessions_client.rb
        - https://github.com/googleapis/google-cloud-ruby/blob/master/google-cloud-dialogflow/lib/google/cloud/dialogflow/v2/doc/google/cloud/dialogflow/v2/session.rb
        - https://github.com/googleapis/google-cloud-ruby/blob/master/google-cloud-dialogflow/lib/google/cloud/dialogflow/v2/doc/google/cloud/dialogflow/v2/context.rb
    - APIをWeb上で試し打ちできる。デバッグにめっちゃ便利。
        - https://cloud.google.com/dialogflow-enterprise/docs/reference/rest/v2beta1/projects.agent.sessions/detectIntent