【Watson Assistant使ってみた】チャットボットに社内wikiを学習させて質問できるようにする（前編）

# はじめに
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1146658/913ce2ea-c620-f118-a139-b419c4c9354e.png)

こんにちは、 @yuta-segawa です。
普段はニフティ株式会社でエンジニアやデータ活用の仕事をしています。

Googleで検索できない「社内特有の質問」にも答えてくれる、社内通なバーチャル社員がいると便利ですよね。
というわけで、会社の研究開発の一環として「社内特有の質問に答えてくれるチャットボット作り」に挑戦したので記事にしてみました。

技術的には、今回はIBM Cloudの「[Watson Assistant](https://www.ibm.com/jp-ja/cloud/watson-assistant)」と「[Watson Discovery](https://www.ibm.com/jp-ja/cloud/watson-discovery)」を使ってみました。
ところが参考にできる情報が少なめで、Watsonシリーズに一体どんな特徴があって何ができるのかよくわからないまま、頑張って公式を読みつつ構築して学んでいった経緯があります（笑）
そこでこの記事では、これらのWatsonサービスに入門するための簡単な使い方について共有できればと思っています。

長くなりそうだったので、今回の記事は前編として以下のことについて触れていきます。

- [そもそもどんなチャットボットを作ろうとしてるのか](#ナレッジを学習して回答してくれるチャットボットが欲しい
)
- [IBM Cloud「Watson Assistant」とは](#watson-assistantとは
)
- [実際にWatson Assistant使ってみた](#実際にwatson-assistantを触ってみる
)

後編では「Watson Discovery」の使い方について触れる予定です。

## ナレッジを学習して回答してくれるチャットボットが欲しい
社内wikiのおかげで大半の社内情報は見つかるのですが、こんなケースもあります。

1. 知りたい情報が載ったページが（存在するものの）ヒットせず。結局担当者に聞くことになる
2. それっぽいページがヒットしても、求めている情報を読み解くのに時間がかかる

原因はケースバイケースですが、「1」は調べる側の検索スキル or 担当者側の執筆スキルの問題であることが多いです。
全社員がwiki研修する手もなくはないですが、技術者としては「見つけやすくする仕組み」を用意したくなります。

一方「2」はキーワード検索の性質上、ヒットしたあと自分の求める情報をページから読み解く作業はほぼ必須です。
（Google検索でも、ページ内でさらにCtrl+Fして検索される方は多いのではないでしょうか）

そこで、**「自分の代わりに情報を読み解いてくれて、質問すれば答えてくれる物知りbotがいると楽なのでは？」**と考えました。

わからない事はとりあえずチャットボットに聞く世界、実現できれば生産性が上がりそうです。

  - 「〇〇したいんだけど、どうすればいい？」 → ボットが自然に返答「ならこのサイトから○○してください」
  - 理想的にはシステム仕様の質問などにも詳しくなってもらって担当者の負担を軽減
  - 仮にボットが知らなくても「そのことなら〇〇さんが詳しいです」とルーティング

その上、自分でナレッジを学習し続けてくれるボットならメンテナンス効率が上がりそうです。

  - 勝手に社内wikiから新着記事をクロールして学習
  - ユーザーのフィードバックで再学習して精度向上
  - 気付けば社内一の情報通に

思い描く要件はこんな感じでした。

## まずはプロトタイプを作ってみる

いきなり全て実現させるのは厳しいので、プロトタイプを作ることにしました。
コンセプトは以下のとおりです。

- 事前知識として社内wikiデータをbotに学習させておく
- botに質問が来たら回答させる
- 良くある質問はFAQデータとして登録できるようにする

実は、当社ニフティには既に一人のバーチャル社員「nifbot」がいます！
なので今回のプロトタイプはこのnifbotをインターフェースとしました。

nifbotはニフティ社員がSlack上で利用できるチャットボットで、以下のようなことができます。

- キーワードをメンションすると「社員の名前」「商品情報」など様々なマスターデータから欲しい情報を拾ってきてくれる
- 例：ある社員のIDを知りたい時に `@nifbot 瀬川`（筆者です）とメンションすると、`瀬川`と名のついた社員情報が返ってくる
- しかも顔写真付きなので、このテレワーク時代に割と重宝する

そんな活躍中のnifbot君に、さらに社内wikiのデータを学習してもらって、一番の社内通になってもらいます・・・。
最終的な目標は「単なるキーワード検索」ではなく、「一人の社員のように会話形式で情報を教えてくれる」状態です。

## Watson Assistant & Discoveryの構成と利用イメージ
いよいよWatsonシリーズを使った構成設計の段階です。
プロトタイプでは**「Watson Assistant」**と**「Watson Discovery」**を採用して、画像のようなフローで利用できるようにしました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1146658/de1e6df3-30f7-a9b5-6a49-ac567f693bf2.png)

⓪ ユーザーがnifbotにメンション
① 社内マスターデータを検索し（従来のnifbot機能）、ヒットしなければ質問文と判断して次のステップへ
② **「Watson Assistant」**に登録されたFAQの会話パターンから、質問文に合致する回答を探す
③ Assistantに該当するFAQがなければ、**「Watson Discovery」**で社内wikiから回答、または関連ページを探す
④ 質問者に返答し、フィードバックを求める
⑤ フィードバックを**「Watson Discovery」**に送信して学習を最適化していく

ざっくり言うと、

- Watson Assistant：ある程度定型のQAパターンを学習
- Watson Discovery：社内wikiを網羅的に学習

という使い分けになっています。

今回の記事では、前者の「Watson Assisntant」の使い方について触れていきます。
なお「Watson Discovery」の使い方については、次回の後編で触れたいと思います。

# Watson Assistantを使ったQAパターンの会話
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1146658/913ce2ea-c620-f118-a139-b419c4c9354e.png)

ここまでは、プロトタイプとして作成したチャットボットについて紹介しました。
ここからは、実際にプロトタイプに使用した「Watson Assistant」の概要と構築手順について説明していきます。

## Watson Assistantとは？

Watson Assistantは、チャットボットにとっての「対話ロジック」部分を構築できるサービスです。
ああ言われたらこういう、という会話の条件分岐フローを構築できるものですね。

実際の設定イメージ図を用意してみました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1146658/7cb3b729-58b6-8f54-2f01-d11310ac7233.png)

会話の入力に対しフローを定義して、どの返答・処理をするかを視覚的に定義できます。
複雑なプログラミングを要さない点も魅力ですね。

Assistantのポイントは**問いかけられた「意図（インテント）」と登場する「概念（エンティティ）」を解釈して会話してくれる**という点ですね。

つまりAssistantが自動的に質問文の意図と文中の概念を抽出してくれるので、具体的には、

- どんな意図の質問かを判定し、それに合わせた回答を返せるようになる
- どんな物事（概念）についての質問かを判定し、それに合わせた回答を返せるようになる
- 結果的に、質問文（key）に完全一致しなくても柔軟に答え（value）が返せるようになる

というメリットがあります。

この辺りは後ほど詳しく利用手順の中で説明していきます。

## なぜAssistantを採用した？
主な理由は以下の通りです。

- Watson Discoveryとの統合が強力
- ほとんどの機能が無償で使える（検証用途に嬉しい）

実は先に「Watson Discovery」から採用していたため、Assistantも一緒に採用したという経緯があります（笑）
※ではなぜ「Watson Discovery」を選んだかというと、詳細は後編で紹介しますが、主に「日本語対応」によるものでした。

また、**無償で使える**という点も便利ですね！
今回紹介する一連の機能を触るのに、一切お金がかかりませんでした。

## Assistantに登場する用語と機能
Watson Assistantに触れる上で理解が必要な用語を整理してみます。

|名称|説明|用途・機能|
|--|--|--|
|アシスタント|チャットボット本体に相当する最上位概念|各種スキルを登録して機能させる|
|スキル|アシスタントにアタッチする機能|対話スキル（dialog）とWatson Discoveryへのルーティング（Search）スキルの2種類がある<br>ベータ版では加えて「Actions」スキルも作れるようになっている|
|インテグレーション|チャットボットと外部サービスの統合|WEBサイトへチャットを埋め込んだりSlack Appと連携可能|
|インテント|会話の意図。動詞とも言える。<br>例えば「`#休暇を申請したい`」を定義しておく|意図に応じて会話を条件分岐できるようになる|
|エンティティ|会話に登場する概念。インテントが動詞ならエンティティは名詞。<br>例えば「`@休暇休日`」と定義する|質問文中の`@休暇休日`っぽい要素を抽出し変数に格納してくれるようになる。返信テキストを動的にしたり、会話の分岐条件に使える|

# 実際にWatson Assistantを触ってみる
ここからは実際の構築手順を追いながら「Watson Assistant」の設定方法を紹介します。
IBM Cloudのアカウントを準備して、Watson Assistantを起動した状態からスタートです。

手順は次の通りです。

1. 初期設定（アシスタント作成）
2. 会話機能の設定（ダイアログスキル作成）
3. 受け付ける質問の定義（インテント作成）
4. 会話フローの定義（ダイアログ作成）
4. 会話のテスト
4. 概念によって会話を分岐（エンティティの作成）
4. Slackから会話できるようにする（外部統合）

## 1. 初期設定（アシスタント作成）
まずは「アシスタント」を作成。ボット本体に相当する最上位概念です。
作成後の画面がこちら。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1146658/25ac6cc3-4183-f512-ca6f-d8ea3c82c7aa.png)

この画面では「スキル」（アシスタントの機能）と「インテグレーション」（外部統合）の2つを設定できます。

現状スキルは以下の3つがあるようです。

1. アクション（ベータ版）：顧客のアクション（請求確認など）支援のための会話フロー設定に特化したスキル
1. ダイアログ：基本の会話スキル
1. サーチ：Watson Discoveryと統合。有料プランのみ

インテグレーションについては後ほど紹介します。

## 2. 会話機能の設定（ダイアログスキル作成）
今回は基本の「ダイアログスキル」を作ってみます。
この時言語を聞かれるので、日本語化することをお忘れなく。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1146658/b37f9555-02cf-317d-3977-fb6d186888f3.png)

## 3. 受け付ける質問の定義（インテント作成）

スキルの作成画面に遷移すると、インテントの作成を求められます。
インテントは、受け付ける質問内容（＝ユーザーの会話の意図）に相当します。

まずはCreate Intentからインテントを定義してみます。

ここの例では、「年次休暇の付与日数が知りたい」意図の質問に対するチャットボットの挙動を作っていきましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1146658/c2325704-f11f-8f37-d525-fec3b7c9866c.png)

設定するべき項目は Intent name と User examples です。
User examples（ユーザーがどう話しかけくるかの具体例）の登録には工夫が必要らしく、以下が推奨されています。

- ユーザーが話しかけてきそうなパターンをなるべく多く登録する（少なくとも5例）
- 句読点の有無、類似語のパターンもなるべく多い方が望ましい

日本語だとこれらのパターンを網羅するのはなかなか大変です（笑）
※どんなUser examplesを登録すればいいか例を示してくれる「Recommended examples」機能がありますが、有料のようなので今回は扱いません。

## 4. 会話フローの定義（ダイアログ作成）

実際に質問と回答の受け答えができるように会話フローを組んでみます。

Dialogをクリックし、画像のようなツリー画面を表示します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1146658/85ced1ed-94ec-8d50-a80a-e3bade15f931.png)

ユーザーからの文字入力などをフックすると、左上の丸いアイコン（開始地点）から下流へ、条件に合致するノードを探しにいきます。

初期状態では2つのノードだけが登録されています。

- 「ようこそ」：Assistant起動時のみ利用されるノード
- 「その他」：他のノードのいずれの条件にも一致しない場合に利用されるノード

早速「Add node」より、先程作成したインテントを検知した時に起動するノードを作成してみます。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1146658/5cfe1568-20aa-c694-7c2d-5a8cced28369.png" width="400"/>

ノードには以下のように設定をしてみます。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1146658/8cd7381c-187d-2dbe-9714-21c0dbf66e72.png" width="400"/>

- If assistant recognizes
    - ノードの起動条件。インテント「#年次休暇の付与日数が知りたい」を認識した時に起動するよう設定
- Assistant resopnds
    - 質問に対応するTextを返答する
    - Text以外にも、画像、オプション選択、有人への切り替えなど色々できるようです

## 5. 会話のテスト

実際に会話テストをしてみます。
コンソール画面右上の「Try it out」ボタンからいつでもテストができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1146658/e1685bd0-dc8f-a3cc-f412-3fb02b6b0a6d.png)

質問を入力してみます。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1146658/d0098462-2ba0-0f70-42ce-9c3c4bfa8950.png" width="400"/>

質問の意図をインテントとして認識し、ノードの設定通り返答を返せていますね。

では、登録したインテントに完全一致しない表現へ変えるとどうでしょう。


<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1146658/4d38dcdb-2922-3d5b-4302-06c3373f1abe.png" width="400"/>

他にインテントを登録していないのでヒットしやすいというのもありますが、同様の質問として解釈してくれています。

## 6. 概念によって会話を分岐（エンティティの作成と会話フローの調整）

前述したインテントの作成では、想定される質問表現を列挙することで柔軟な質問認識ができました。
この時点でも意図したような回答はできていますが、運用するうちに以下のような課題が出てくるかもしれません。

- 年次休暇という単語を想定外の言い回しで質問する人が多くインテントを調整したいが、その都度質問パターンを追加するのが大変
- 「年次休暇」以外の休暇でも大体同じ質問パターンになるのでインテントを使いまわしたい

このような場合には「年次休暇」という概念部分を **エンティティ** として定義して、変数の如く使い回すのがおすすめです。
抽出されたエンティティの値によって、その後の会話を分岐することができます。

### エンティティの作成


それでは、左ペインの「Entities」から、エンティティを作成してみます。

ちなみに System entities という項目には、「日付」や「時刻」「金額」といった出現頻度の高い概念が既に定義されています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1146658/1724119f-1928-9df5-de41-75c1c60000e1.png)

My Entities から、先ほどのインテントに登場した「年次休暇」に関するエンティティを定義していきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1146658/90ce1b99-305a-c2ec-6597-f449b9966577.png)

ここでは以下のような状況を想定して、「休暇休日」というエンティティに「年次休暇」という値を定義しています。

- 同じ休暇休日でも「結婚休暇」のような別の種類が存在する
- どの種類かによって質問の回答が変わる

どのようなエンティティを定義するべきか？という設計の裁量は、チャットbotに対応させる話題の範囲などに応じて変わる部分になりそうです。

続いて、例として結婚休暇の値も定義しておきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1146658/cd36a7b0-20f5-ca4d-ccc3-eab0784ddbda.png)

これで、同じ休暇休日を聞く質問であっても「年次休暇」か「結婚休暇」かによって返答を出し分けられるので、ダイアログの定義を最大限再利用できます。


### エンティティでインテントを書き換える

登録したインテントをさらに汎用的にするために、エンティティを使ってインテントを置き換えてみます。

具体的には、インテント「#年次休暇の付与日数が知りたい」に登録したUser examplesを以下のように書き換えます。

- 書き換え前「1年間での年休の日数は？」
- 書き換え後**「1年間での @休日休暇 の日数は？」**

こうすることで、直接エンティティ部分を指定するよりも柔軟に文章を解釈させることが可能になります。

このように、エンティティを含めたインテントを登録して認識させることを [エンティティ参照](https://cloud.ibm.com/docs/assistant?topic=assistant-intents&locale=ja#intents-entity-references) と呼ぶのだそうです。

### エンティティによって回答文を変えてみる

それではエンティティを作成した目論見通り、休暇休日の種類によって回答を変えられるようにしてみます。

わかりやすさのため、まずインテントのタイトルを「#休暇休日の付与日数が知りたい」に変えておきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1146658/e7e060b8-8115-1bbf-fd89-ec295caec054.png)

次に、ダイアログより会話ツリーを編集してみます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1146658/7cb3b729-58b6-8f54-2f01-d11310ac7233.png)

まず親ノード周りを以下のように変更しています。

- 先ほど登録したノードを親として、子ノードを2つぶら下げる
- 親ノードはインテントの認識を条件に起動させ、アシスタントは処理をスキップする（子に渡す）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1146658/842b8e3c-c705-bb73-f6f6-e953bc9acc75.png)

子ノードにはそれぞれエンティティ条件と回答を設定するようにしています。

- 子ノードは認識したエンティティの値の一致を条件に起動させ、対応した回答をさせる

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1146658/bf38664f-e86e-a808-330d-742e81156bbb.png" width="400"/>

すると狙い通り、エンティティによって回答が分岐しました。

## 7. Slackから会話できるようにする

最後に、Slack Appを通じて作ったAssistantと会話できるようにしてみます。
AssistantにSlackとの統合機能が用意されているので、設定は簡単です。

なお以下の通りSlack Appの情報入手・設定変更が必要なので、事前に準備しておきます。

- verification token の入手
- Bot user OAuth access token の入手
- Bot Token Scopes にて以下権限の設定
    - `app_mentions:read`
    - `chat:write`
    - `im:history`
    - `im:read`
    - `im:write`

まず作成したAssistantの設定画面へ遷移し、画面右より「Add integration」を選択します。
するといくつかの統合方法が選択できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1146658/4d721d89-b6ec-5c8f-1592-108d63599886.png)

WEBチャットや電話のようなスタンドアロン型の統合もできるようですね。

今回はサードパーティ型のSlackを選択します。
設定画面へ推移するので、画面に従って用意した情報を入力していきます。

指示に従っていると、URL生成ボタンをクリックすることになり、AssistantのエンドポイントURLが生成されます。
これはSlackで受け取った会話内容をAssistantに送る Event Subscription 用の設定URLです。

生成されたURLをSlack App側のEvent Subscriptions > Request URL欄にに貼り付けます。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1146658/5ac971d7-596e-b873-777c-3a6d799e150b.png" width="600"/>

設定を完了すると、Slack Appへのメッセージで先程のテスト同様の会話ができるようになります。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1146658/a9a930ca-4b11-789f-1734-42e62c4f0ebd.png" width="600"/>

## Watson Assistantだけでは物足りない

ここまでの過程からご想像の通り、現状のチャットボットはAssistant事前に登録した質問にしか回答できません。
これでは誰か質問登録担当を立てて大きな工数をかけて運用することになってしまいます。

質問の回答に必要な情報は社内wikiに転がっているはずなので、これを網羅的に学習していい感じに返答できるようにしてくれるサービスはないものか・・・。

そこで登場するのが、「Watson Discovery」です。

# おわりに
今回の記事ではチャットボットに質問できるようにするためのWatson Assistantの使い方について触れました。
次回はWatson Discoveryの使い方を軸に説明しつつ、結局このチャットボットがどうなったのかについて記事にしたいと思います。

## 筆者紹介

ニフティ株式会社で「N1！データサイエンティスト」をしている、瀬川（@yuta-segawa）と申します。
こんな感じで研究開発的な案件に着手したり、もう少しビジネス寄りの課題を取り上げてビッグデータで解決する仕事をしています。

当社についてご興味あれば、以下のリンクをご覧下さい。

- [ニフティの採用サイト](https://recruit.nifty.co.jp/)
- [ニフティの「N1!制度」とは](https://recruit.nifty.co.jp/workplace/initiative/n1/)
- [N1!データサイエンティストについて](https://www.wantedly.com/companies/nifty/post_articles/324643)