Watson Conversationでユーザーの名前だけ抜き出す

Bluemix Platformでチャットボットを作る時に必ず使うであろうWatson Conversationサービスで苦労したのでメモ。

Watson Dialogサービスのときは簡単に抽出できていたはずの名前のエンティティーを抜き出す方法が、特に日本語ではWebで確認しても見当たらないので、格闘することに。今回はその結果をまとめました。

他にWatson Conversationサービスだけで完結した方法で、適切なやり方があれば是非教えてください。

Watson Conversationサービスに関しては、例えば以下の記事が参考になります:
 - [Watson ConversationのTutorialを日本語でやってみた （前編）](http://qiita.com/sukusuku_watson_editorial_room/items/2ab2b4738e31b276b91e)
 - [Watson ConversationのTutorialを日本語でやってみた （後編）](http://qiita.com/sukusuku_watson_editorial_room/items/e3111d2ccce996db7feb)

# 課題: ユーザーの名前だけを取り出したい
例えば、会話の中でユーザー名を聞き、後々「XXXさんにオススメな商品は〜です」というようにユーザー名を再利用するような会話の流れがあったとします。

最初にWatson Conversationから「お名前は?」と聞くことになりますが、「私は鈴木です」と答える人もいれば、「鈴木です」と答える人もいます。他にも「俺は鈴木だ」という人もいるでしょう。このブレる要素のある応答からユーザー名、この場合は「鈴木」だけを抜き出してユーザー情報としてしばらく格納しておきたいというのがモチベーションです。

# 特定用途向けの定義済みエンティティーはあるけど・・・
Watson Conversationサービスにはシステム・エンティティー(System Entities)と呼ばれる特殊なエンティティー(目的語に相当)があって、こちらを使うことでメッセージ内にある時刻やお金に対するエンティティーを拾ってくることが可能です。

例えば、「ダイエットを始めて何ヶ月ですか?」という問いに対して「5ヶ月です」という回答が返ってきたとします。この回答のうち「5」という数値を抜き出して後続の処理につなげたい、という要望を満たしたい、そういう場合はシステム・エンティティーの中から数値を抜き出す「@system-number」あるいは時刻を抜き出す「@system-date」を使えば良いです。

![スクリーンショット 2017-05-31 18.36.21.png](https://qiita-image-store.s3.amazonaws.com/0/101771/faa014ee-3a9b-6b0c-4cea-338d68804950.png)

上記のように「5ヶ月です」というメッセージに対し@system-numberではピンポイントに「5」、@system-dateでは本日の日付(今日は2017-05-31)からみて５ヶ月後の日付が得られます。あとはこれをcontext内で任意の変数に保存しておけば、後々のメッセージ内で再利用可能になります。

このシステム・エンティティーは設定自体は簡単で、エンティティーの設定項目で使用したいエンティティーを有効化するだけ。ON/OFFは簡単に切り替えられます。以下のスクリーンショットでは全部有効にしていますが、不要なものはOFFっておいた方が良いかなと思います。
![スクリーンショット 2017-05-31 18.36.40.png](https://qiita-image-store.s3.amazonaws.com/0/101771/95ba709f-cd44-84b7-59df-2239ebd880a5.png)


システム・エンティティーに関して、詳しくは以下をみてください。
[Watson Developer Cloud - System entities reference](https://www.ibm.com/watson/developercloud/doc/conversation/system-entities.html)

しかしながら、ユーザー名をピックアップするようなシステム・エンティティーは見る限りありません。さあどうしたものか、作るしかないか。

# いざ実装 
というわけで強引に作りました。

「私は鈴木です」、「僕の名前は鈴木だ」、「鈴木だよ」などのブレから「鈴木」だけをピックアップして取り出す例です。

名前を取得するDialogノードを追加し、以下の設定で実装してみました:

## トリガー (Trigger)
正規表現で強引に書きました。とかくむずいです。「々」のような漢字は個別に設定しないと「野々山」さんがヒットしません。気づいてよかった。。。

2017/06/02:
正規表現が適切でなかったので修正しました。

```bash
input.text.matches( '[私|僕|俺]?[の|は]?(名前は)?([一-龠々〆〤ぁ-ゔァ-ヴーa-zA-Zａ-ｚＡ-Ｚ]+)(です|だ)?(よ)?' )
```

## レスポンス (Response)
ユーザー名を「username」というcontextの変数に押し込めます。extractメソッドを使って文字列を部分抽出することはできるので、それを駆使して名前に相当するものを取得できるようにしました。

```bash
{
  "context": {
    "username": "<? input.text.extract('[私|僕|俺]?[の|は]?(名前は)?(.*?)(です|だ)?(よ)?$',2) ?>"
  },
  "output": {
    "text": {
      "values": [
        "よう, <? context.username ?>さん!!"
      ],
      "selection_policy": "sequential"
    }
  }
}
```

以上で実装終わり。結果は以下のスクリーンショットの通りです。

![スクリーンショット 2017-05-31 18.20.59.png](https://qiita-image-store.s3.amazonaws.com/0/101771/1a2d221f-5299-cfb0-6a49-964bfbabef88.png)

期待通りの動作になってそうです!!

# 終わりに
もう今回は強引に正規表現で名前部分をとる方法で実装しました。もちろんConversationサービスにメッセージを投げる前にフロントのアプリで名前部分を抽出しちゃうとか他にも方法はあるかと思います。

また、エンティティー抽出ならWatson APIには[Watson Natural Language Understanding](https://www.ibm.com/watson/developercloud/doc/natural-language-understanding/)があって、これを使えば良いじゃんという同僚からの指摘もありましたが、2017年5月31日時点ではエンティティー抽出に関しては日本語サポートはまだだったはずです(リレーションだけの認識)。日本語対応してから実装案2としてまた記事にまとめたいなと思っています。