こんにちは、イシイです。

弊社では社内会議室の施設予約管理をGoogleカレンダーで行っているのですが、今回はスマートスピーカーなどを通じて音声対話形式でこの施設予約ができるようなアプリケーションの開発にトライしてみたいと思います。音声対話形式のアプリケーションは、Googleアシスタントからの利用を想定して作ってみたいと思います。

Googleアシスタントを使ったアプリを作成するには

Googleアシスタントから会議室予約を行う場合、フローのイメージとしては、Googleアシスタントから会議室予約アプリを内部的に呼び出し、呼び出された会議室予約アプリとの対話で会議室予約まで行うような流れになります。つまり、会議室予約アプリ自体がGoogleアシスタントの拡張部分となり、この拡張部分のアプリ開発支援、テスト環境の提供、公開までの手続きなどを行ってくれるのがActions on Googleというものになります。

アプリ開発支援部分について、アプリの構築方法をActions on Googleのガイドで見てみると、大きく分けて「テンプレート」「Dialogflow」「Actions SDK」といったものが用意されているようです。

テンプレート」を使うと、ユーザーの問いに対して予め用意した回答を返すような、特定の限られた応答などを行うものをコードを書かずに作成できるようです。「Dialogflow」では、機械学習機能と自然言語を理解する機能が組み込まれたウェブIDEから、会話をしながらのインタフェースを作成しやすいものが提供されており、「Actions SDK」ではIDE部分が担っているビルドやデプロイまでを手動で行う形式のようです。

なんとなくの解釈としては、簡易な人工無脳ボットみたいなものを作りたい場合にはテンプレートを、対話形式の音声インタフェースを持ったアプリケーションを作りたい場合にはDialogflowを、さらにCUIベースでガツガツ作っていきたい場合にはActions SDKを使ってね、と大括りで捉え、今回はDialogflowを使ってみることにしました。

対話内容をイメージしてみる

まずは、施設を予約を行いたいユーザーと、それに応答するアプリケーションの会話イメージを起こしてみます。なお、今回は予約状況の確認や予約のキャンセルなどの処理は考えず、一方通行で予約登録を行う場合のみを考えます。

ユーザー:「OK,Google. 会議室予約。」
アシスタント:「こんにちは。会議室予約です。予約したい日付は何月何日ですか?」
ユーザー:「7月1日」
アシスタント:「開始時間は何時からですか?」
ユーザー:「14時」
アシスタント:「時間はどれくらいですか?」
ユーザー:「1時間」
アシスタント:「どこの会議室を使用しますか?」
ユーザー:「A1会議室」
アシスタント:「かしこまりました。7月1日の14時から1時間、A1会議室を予約しました。」

以上のような流れをイメージしてみます。
ここで出てくる要素は「日にち」「開始時間」「会議室利用時間」「会議室の場所」になります。これらの要素を対話の中で決定し、すべての要素が取得できたら予約処理を行う、という仕組みになりそうです。

Dialogflowを使ってみる

まずはDialogflowのサイトにアクセスし、右上の「GO TO CONSOLE」からコンソールページへと移動します。

エージェントの作成

左サイドメニューから「Create new agent」を選択し、DEFAULT LANGUAGEを「日本語」に、タイムゾーンを東京にして「CREATE」を実行します。今回は「4009」というエージェントを作成してみました。

thumb_1

Agentが作成されたら、左サイドメニューから「Intents」のページを開いてみると、「Default Welcome Intent」と「Default Fallback Intent」というインテントが入っています。

thumb_2

インテントとエンティティ

ここから進める前に、これから作業などに出てくるインテントとエンティティについて、先に触れておこうと思います。
インテントは、Dialogflowのドキュメントを見てみると、

An intent represents a mapping between what a user says and what action should be taken by your software.

とあるので、おおまかにはユーザーとの対話におけるアクションを定義するもの、と解釈できそうです。
エンティティは、Dialogflowのドキュメントを見てみると、

Entities are powerful tools used for extracting parameter values from natural language inputs. Any important data you want to get from a user's request, will have a corresponding entity.

The entities used in a particular agent will depend on the parameter values that are expected to be returned as a result of the agent functioning. In other words, a developer does not need to create entities for every possible concept mentioned in the agent – only for those needed for actionable data.

とあるので、自然言語入力の中で抽出したい要素を割り当てるもののようで、今回の対話イメージの中の「日にち」「開始時間」「会議室利用時間」「会議室の場所」にあたります。また、エンティティにはDialogflowで解析できるように元々ビルトインされているSystem Entitiesと、開発者側で独自に設定できるDeveloper Entitiesがあり、学習要素が必要なものについては、このDeveloper EntitiesとしてEntitiesの項目を追加していくような感じのようです。

インテントの作成(Default Welcome Intentの修正)

「Intents」のページにあった、エージェント作成時に既に生成されている「Default Welcome Intent」は、Actions on Googleによると、

・インテントの Events セクションには WELCOME イベントが指定されています。このイベントは、このインテントがアプリのデフォルト エントリ ポイントであることを示します。 各エージェントには、この WELCOME イベントを宣言するインテントが 1 つだけ必要です。 ユーザーがアプリを名前で呼び出したとき(「OK Google、Silly Name Maker と話す」など)、Google アシスタントはこのイベントを使ってアプリを起動します。

とあるので、「事前に準備された、アプリケーション起動時に最初にユーザーと対話するアクションを設定するもの」というように解釈できそうです。では、今回の対話イメージに沿って、起動時の最初の対話部分を作ってみます。

「Default Welcome Intent」のResponsesセクションのところを開くと、Text responsesフィールドに「こんにちは!」というテキストが既に入っているので、「こんにちは。会議室予約です。予約したい日付は何月何日ですか?」に書き換えて、「SAVE」を実行します。これで、ユーザーがアプリを開いたときの最初のアクションができました。

thumb_3

もうひとつ、先程の「Intents」のページで既に生成されていた「Default Fallback Intent」は、ユーザーとの対話の中でこちらが意図して設計したもの以外のアクションのとき(例えば、何を言ったのか聞き取れなかったとき。該当のエンティティがないときなど)に使われそうです。今回は特に手を加えずにそのままにしておきます。

インテントの作成(対話部分)

ユーザーとの対話が始まったら、聞き出したい要素は「日にち」「開始時間」「会議室利用時間」「会議室の場所」になりますが、インテントがおおまかに「ユーザーとの対話におけるアクションを定義するもの」とした場合、インテントの作成方法が2つ考えられます。
ひとつは「会議室予約に必要な要素を対話の中で聞き出す」といった要素全体をひとつのインテントで取得しきってしまう方法、もうひとつは各要素を取得するインテントをそれぞれ作成していく方法です。
今回は、前者のひとつのインテントで取得する方法を試してみたいと思います。「Training phrases」のセクションから「ADD TRAINING PHRASES」を、「Action and parameters」のセクションから「ADD PARAMETERS AND ACTION」をそれぞれ押下し、メニューを開きます。開いた状態がこちらです。ちなみにインテント名は「Hearing Intent」としました。

thumb_4

まず最初の対話の中の「Default Welcome Intent」で「予約したい日付は何月何日ですか?」と聞くようにしましたので、この「Training phrases」にユーザーからの返答の仕方などを登録していきます。
ここでは試しに、「7月1日」「7月の1日」「7月1日でお願いします」「7月」「1日」というように、月日まで言うパターン、月日の他に言葉を添えるパターン、月か日しか言わないパターンなどをいくつか登録してみました。

thumb_5

入力を済ますと、要素に該当しそうな部分が自動で解析されハイライト表示されます。なお、ハイライト表示された部分にズレがある場合には、カーソルをあわせて手動で調整することも可能です。

thumb_6

また、「Action and parameters」の方にも要素が追加されていて、マッチする要素が色分けされていることがわかります。さらに、「Action and parameters」のENTITYの項目には、@sys.dateと@sys.date-periodが設定されていますが、@sysで始まっているこの要素がSystem Entitiesです。このSystem Entitiesがどういうエンティティなのかは、こちらのSystem Entitiesのページで見ることができますが、今回の最初の対話部分で欲しいのは「月」と「日」になりますので、「月」の部分が「@sys.date-period」、「日」または「月日」の部分が「@sys.date」であって欲しいので、このENTITYの項目は自動で割り当てられたものがそのまま使えそうです。

この月日の要素は必ず取得したい要素なので、REQUIREDの項目にチェックを入れます。

thumb_7

すると、PROMPTSという項目と「Define prompts」というリンクテキストが現れました。このREQUIREDの仕組みは、REQUIRED設定した要素が取得できるまで、「Define prompts」で指定した内容でレスポンスを返し、別のインテントに飛ばないようにする仕組みのようです。

「Define prompts」をクリックし、「予約したい日付を月日で教えてください。」を入れておきます。

thumb_8

ここでは、「date」の方にはREQUIREDのチェックを入れ、「date-period」の方にはREQUIREDのチェックを入れませんでした。「date-period」で取得できる部分は月のみであり、結局のところここで取得したものは月日に該当する「date」の方だけなので、ユーザーが月だけしか伝えてこなかったときに「date-period」の方で補完されます。そもそも「date-period」の方自体も不要なのかもしれませんが、いったんは「Training phrases」で入力したユーザーからの返答の仕方に応じて出来た項目をベースに作ってみています。

これで「日にち」の部分が作れたので、残りの「開始時間」「会議室利用時間」「会議室の場所」を作っていきます。
前述の通り、今回は要素全体をひとつのインテントで取得しきってしまう方法を取りますが、Actions on Googleによるとこの方式は、「スロット充填」機能というものを使用したやり方のようで、スロット充填機能とは、

この機能を使うと、ユーザーから追加の入力パラメータを取得できるため、入力パラメータごとにインテントを作成する必要も、ユーザーが 1 つのフレーズで必要な入力情報をすべて話す必要もありません。また、スロット充填機能により、パラメータを [Required] として設定することができます。これによりエージェントは、ユーザーが必要なすべてのパラメータを指定するまで、入力を処理しません。

と説明されています。ではこのスロット充填機能を使って進めます。

新しく取得したい要素「開始時間」を、「Action and parameters」に追加してみます。こちらも取得したい要素なのでREQUIREDにチェックをし、「PARAMETER NAME」に「start-time」、「ENTITY」に「@sys.time」、「VALUE」に「$start-time」、「Define prompts」に「開始時間は何時からですか?」を設定します。

thumb_9

続けて、「会議室利用時間」を「Action and parameters」に追加します。REQUIREDにチェック、「PARAMETER NAME」に「use-time」、「ENTITY」に「@sys.duration」、「VALUE」に「$use-time」、「Define prompts」に「何時間のご利用ですか?」を設定します。

thumb_10

最後に、「会議室の場所」を作成したいのですが、会議室の場所というものは限られており、ユーザーが発した言葉のどこでも良い、というわけではありません。ここで、開発者側で独自に設定できるDeveloper Entitiesというものを使います。

エンティティの作成

左サイドメニューの「Entities」から、独自のエンティティの作成が行なうことができます。今回作成したいエンティティは「会議室の種類」で、種類は「A1会議室」と「A2会議室」の2つのみとします。「Entities」の「CREATE ENTITY」からエンティティ作成ページを開きます。ここではエンティティ名を「RoomType」としました。

thumb_11

「Define Synonyms」にチェックが入っていますが、これは指定した名称の類義語を許容するかどうかのチェックボックスで、こちらを有効にしておくことで、「A1会議室」という呼び名だけでなく、類義語登録した呼び名(例えば、A1、エーイチ、エーワンなど)も「A1会議室」として認識できるようになります。「Allow Automated Expansion」の方は、ここで登録していない類義語についても自動で追加していくようですが、今回は使用せず、定義した類義語のみで進めてみますので、チェックは付けずにそのままにしておきます。

作業後の画面は下記になります。「A1会議室」「A2会議室」というValueとそれに紐づく類義語を登録してみました。この独自のエンティティがDeveloper Entitiesにあたりますが、Developer Entitiesのタイプとしては「Dev Mapping」「Dev Enum」「Dev Composite」があり、今回のように類義語を登録していくタイプは「Dev Mapping」にあたります。

thumb_12

作成したエンティティをインテントに追加

左サイドメニューの「Intents」から、先程作成途中だったインテントを選択してページを開きます。
最後の要素「会議室の場所」を「Action and parameters」に追加してみます。REQUIREDにチェック、「PARAMETER NAME」に「room-type」、「ENTITY」には先程作成したエンティティが選択できるようになっているので作成した名称の「@RoomType」、「VALUE」に「$room-type」、「Define prompts」に「どの会議室をご利用ですか?」を設定します。

thumb_13

レスポンスの作成

取得したい要素の設定、取得したい要素が得られるまでの応答の文言(Define prompts)などの設定が終わりましたので、すべての要素が揃った後の応答部分を作成します。

「Responses」のセクションから「ADD RESPONSE」をそれぞれ押下し、メニューを開きます。「Text response」のフィールドに返答したい内容を記述していきますが、「Action and parameters」の項目で設定した「VALUE」を使うことで、取得した要素を変数として返答内容に盛り込むことができます。「かしこまりました。7月1日の14時から1時間、A1会議室を予約しました。」 というようなレスポンスを返したいので、「かしこまりました。$dateの$start-timeから$use-time、$room-typeを予約しました。」としてみました。

また、このレスポンスを持って対話を終わらせたいので、「Set this intent as end of conversation」をONにしておきます。

thumb_14

シミュレータを使って試してみる

Dialogflowを使って拡張部分が作成できたので、Actions on Googleで提供されているシミュレータを使って動作を確認してみたいと思います。

シミュレータを動かすための設定

シミュレータを使って検証するには、Googleアカウントのアクティビティ管理ページから「ウェブとアプリのアクティビティ」「端末情報」「音声アクティビティ」の3つのアクセス許可をオンにします。

thumb_15

シミュレータを動かす

Dialogflowの左サイドメニューから「Integrations」ページを開き、Googleアシスタントセクションの「INTEGRATION SETTINGS」をクリックすると、下図のような画面になります。

thumb_16

「Explicit invocation」に「Default Welcome Intent」が入力された状態になっていますが、これはGoogleアシスタントからアプリを呼び出したときに表示されるインテントが指定されています。「Default Welcome Intent」にて、レスポンスを「こんにちは。会議室予約です。予約したい日付は何月何日ですか?」と指定したので、アプリを呼び出したときにはこちらのレスポンスで返してくることになります。

「TEST」をクリックして、Actions on Googleのシミュレータ画面へ遷移し、音声またはキーボードから対話をしてみます。

thumb_17

これで、対話の中で会議室予約を行うために必要な要素を取得し、対話を終了する流れまでができました。

次回に向けて

今回はDialogflowを使って、対話から会議室予約に必要な要素を得るところまでを作り、その動作をシミュレータで確認するところまで行ってみましたが、Googleカレンダーの予約を行うまでにはまだいくつか対応が必要です。

まず、1番肝心な部分であるカレンダーとの連携機能についてはまだ触れていません。Dialogflowのレスポンス機能を使った応答まではできていますが、カレンダーとの連携機能の処理を行うにあたっては、今回触れなかったフルフィルメントの機能を使って実装を進める必要があります。

細かいところだと、シミュレータからの返答が「かしこまりました。2018-07-01の14:00:00から1時、A2会議室を予約しました。」となっていて、利用時間の部分は「1時間」と取得して返してきてほしかったのですが、ここでは「1時」となってしまっています。System Entitiesの@sys.durationに該当するところですが、想定と違う形になってしまっているので、ここをうまく処理してあげたり、Googleアシスタントからの呼び出しが「テスト用アプリにつないで」となっているので、ここを今回用のアプリ名称から呼び出すように変更する必要もあります。そして最後に実機での確認もしたいところです。

これらの部分については、次回エントリーにて進めてみたいと思います。