DialogflowをPython SDKで呼び出したレスポンスをJSON化する

Dialogflowは無償で使える枠も大きく、ちょっとチャットボットを試してみようという場合に使いやすいツールです。
私が講師を務めている研修でも、よくDialogflowを取り上げたりしています。
もちろん、開発のお仕事でも使っているのですが、ちょっとハマったことがあったので、それについて書いておこうと思います。

まずはDialogflowをPython SDKで使う

ハマった話の前に、DialogflowのPython SDKの基本的な使い方から。Dialogflowのエージェント作成と、GCPでのサービスアカウントの作成、JSONキーの作成までは完了した後から書きます。
そこまでについては、こちらにまとめてあります。

Python SDKのインストール

pipを使ってふつうにインストールします。Python3.6以上が必要です。

pip install google-cloud-dialogflow

JSONキーを使用したDialogflowへの接続

Dialogflowへの接続はちょっと面倒です。locationはglobalを指定していますが、他のロケーションを使っている場合は、ここで変更できます。
session_idは、会話を維持するものなので最初に採番(例えばUUIDで)して、以後は同じ値を持ち回ると良いでしょう。

from google.oauth2 import service_account
from google.cloud import dialogflow_v2 as dialogflow
import uuid

dialogflow_credential_path = 'JSONキーのパス'
with open(dialogflow_credential_path, 'r') as f:
    service_account_info = json.load(f)

location = 'global'
session_id = uuid.uuid4()

credential = service_account.Credentials.from_service_account_info(service_account_info)
session_client = dialogflow.SessionsClient(credentials=credential, client_options={'api_endpoint': '%s-dialogflow.googleapis.com:443' % (location,)})
project_id = '%s/locations/%s' % (service_account_info['project_id'], location,)
session_path = session_client.session_path(project_id, session_id)

Dialogflowと会話

detect_intent()を使ってDialogflowと会話します。引数として質問文の文字列を使う場合はTextInput、インテントのイベント名を指定する場合はEventInputなどで使い分けます。

question = 'こんにちは'

query_input = dialogflow.QueryInput(text=dialogflow.TextInput(text=question, language_code='ja'))
response = session_client.detect_intent(session=session_path, query_input=query_input)
query_result = response.query_result

QueryResultをどう扱うか

さて、ここからが本題です。response.query_resultにDialogflowからの回答が入っているのですが、これは単純なJSONとかじゃなくて、google.cloud.dialogflow_v2.types.session.QueryResultというクラスのオブジェクトです。
このQueryResultクラスは、proto.message.Messageを継承しています。

Dialogflowでは(というかGoogle Cloudの各種サービスでは?)、Googleが開発したProtobufというシリアライズフォーマットを使っていて、どうやらPython SDKにおいては、Proto Plus for Pythonというラッパーを使用しているようです。(いまひとつ、説明しきれない・・・)

例えば、こういう内容のQueryResultが返ってきます。(加工しています。)

query_text: ""
action: "ccc"
parameters {
}
all_required_params_present: true
fulfillment_messages {
  text {
    text: ""
  }
}
output_contexts {
  name: "projects/aaa/locations/global/agent/sessions/bbb/contexts/ddd"
  lifespan_count: 4
  parameters {
  }
}
output_contexts {
  name: "projects/aaa/locations/global/agent/sessions/bbb/contexts/ccc"
  lifespan_count: 1
  parameters {
  }
}
intent {
  name: "projects/aaa/locations/global/agent/intents/bbb"
  display_name: "ccc"
}
intent_detection_confidence: 1.0
language_code: "ja"

例えば、output_contextsはDialogflowでのコンテキストの中身で、次のリクエストで渡す必要のある値だったりするのですが、この値は、proto.marshal.collections.repeated.RepeatedCompositeクラスのオブジェクトだったりします。
これがJSONとかなら話は単純なのに・・・。

QueryResultをJSON化したい

QueryResultを、どうやってとっても扱いやすいJSONにするか。
回答は、下記のとおり。

from google.cloud.dialogflow_v2.types.session import QueryResult

QueryResult.to_json(query_result)

分かってしまえば簡単なんですけどね。この回答にたどり着くのに2〜3時間も費やしてしまった・・・。

dict化したい

Pythonのdictにすることもできます。
プログラムでいろいろいじるなら、こちらの方が良いでしょう。

from google.cloud.dialogflow_v2.types.session import QueryResult

QueryResult.to_dict(query_result)

ということで、この情報をズバリと書いているところはまだ見つからなかったので。
ご参考にしていただければ幸いです。

この記事を書いた人

井上 研一

株式会社ビビンコ代表取締役、ITエンジニア/経済産業省推進資格ITコーディネータ。AI・IoTに強いITコーディネータとして活動。2018年、株式会社ビビンコを北九州市に創業。IoTソリューションの開発・導入や、画像認識モデルを活用したアプリの開発などを行っている。近著に「使ってわかった AWSのAI」、「ワトソンで体感する人工知能」。日本全国でセミナー・研修講師としての登壇も多数。