Translator Text APIでBotのQnA Makerの精度をあげる

この記事は [Microsoft Azure Advent Calendar 2017](https://qiita.com/advent-calendar/2017/azure) の5日目の記事です。

# この記事のメインのテーマ
Azure Bot ServiceでQnA MakerとTranslator Text APIを繋ぎこむ方法。
実装はNode.jsの例です。

# QnA Maker
Microsoft AzureのCognitive Servicesの一つであるQnA Makerは簡単にQAのナレッジを管理できるサービスです。インポートもURLから解析して楽チンだったりします。
Azure Bot Serviceから繋ぎこんで利用します。

[QnA Makerの公式サイトはこちら](https://azure.microsoft.com/ja-jp/services/cognitive-services/qna-maker/)
QnA Makerの目玉機能の一つとしては、質問の意図解釈を行ってくれて揺らぎを吸収してくれる事です。

# 問題
QnA Makerのコンソールでテストすると揺らぎを吸収して回答が返ってくるのですが、なぜかBot Serviceのテストでは返ってこないケースがあります。

ナレッジが以下の通りに登録されているとします。

<img width="500" alt="QnA_Maker.png" src="https://qiita-image-store.s3.amazonaws.com/0/14929/7679d452-c088-fc5d-9b3b-aeb6aaa1dd62.png">

そして、QnA Makerのテスト画面では以下の通り、揺らぎを吸収して表示されます。

<img width="500" alt="QnA_Maker.png" src="https://qiita-image-store.s3.amazonaws.com/0/14929/8a28e606-8dcd-609e-0ea9-ac7e28d66c22.png">

がしかし！なぜかBot Serviceのテストでは、

<img width="300" alt="hira-bot-test01_-_Microsoft_Azure.png" src="https://qiita-image-store.s3.amazonaws.com/0/14929/fbea8fc2-804a-13ef-68a7-da322a3014d8.png">

と、かなりお馬鹿さんになってしまいます。
英語の場合はそんな事はなく賢く回答してくれます。

言語の設定が必要か？と思ったがそのような場所は見つからず。。
もし、どなたか知っていたら教えてください。

そこで見つけたソリューションは、「Translator Text APIを使う」という方法です。

# Translator Text APIとは
Microsoft AzureのCognitive Servicesの一つで翻訳機能を提供するAPIです。
[Translator Text APIの公式はこちら](https://azure.microsoft.com/ja-jp/services/cognitive-services/translator-text-api/)

# Translator Text APIとQnA Maker
考え方は至ってシンプルで、QnA MakerのKBのQ側を英語に翻訳しておいて、QnA Makerに問い合わせる前にTranslator Text APIを使って日本語->英語の翻訳を行う。という事です。
英語が日本語より意図解釈の精度が高い事をうまく活かします。

流れは、
1. Botが日本語のメッセージを受ける
2. Translator Text APIを使ってメッセージを英訳
3. 英訳したメッセージでQnA Makerに問い合わせる
4. QnA Makerが返却した回答をBotが返す

といった感じです。

# 前提
Azure Bot ServiceとQnA Makerの設定が済んでいる状態が前提で進めます。
言語はNode.jsを選択しています。

Azure Bot Serviceの作り方は以下の記事を参考にしてください。
[Azure BOT Service とCognitive Service QnA MakerでFAQのBOTを作る](https://qiita.com/taiki_yoshida/items/d48d6286ce5b8f3aa2a8)
[ChatBot入門-Azure Bot ServiceとQnA MakerでWebサイトで使えるFAQBotを作る](https://qiita.com/torisan_kanasan/items/f5d816f74a69ca1660ba)

# Translator Text APIの利用を開始する
Azure PortalでTranslator Text APIを選択します。

<img width="600" alt="Everything_-_Microsoft_Azure_and_QnA_Maker.png" src="https://qiita-image-store.s3.amazonaws.com/0/14929/e04b03c8-e85a-884e-dcbf-ca60ebb74623.png">

「作成」を押します。

<img width="379" alt="Create_-_Microsoft_Azure_and_「Translator_Text_APIでBotのQnA_Makerの精度をあげる為に苦労した話」を編集_-_Qiita.png" src="https://qiita-image-store.s3.amazonaws.com/0/14929/0be326e8-9dae-95e6-7ab4-6d1b984a4a68.png">

諸々選んで「作成」します。
「デプロイに成功しました。」と表示されるまで待ちます。
<img width="500" alt="hira-chat-test_-_Microsoft_Azure.png" src="https://qiita-image-store.s3.amazonaws.com/0/14929/93392d00-599d-47bc-67c3-ecec4db7538c.png">

Cognitive Servicesのリソースが表示されている事を確認し、開きます。

<img width="699" alt="Quick_Start_-_Microsoft_Azure.png" src="https://qiita-image-store.s3.amazonaws.com/0/14929/37117598-b728-053f-13df-67ff77455b85.png">

必要なキー情報を取得します。

<img width="389" alt="Manage_keys_-_Microsoft_Azure.png" src="https://qiita-image-store.s3.amazonaws.com/0/14929/82fd2516-229c-fb4e-4323-6909fe639f5c.png">

後ほど、実装時に使うのでTranslator Text APIのKEYを控えておきます。
※ KEYは一つだけで問題ありません。

# 実装
では、どのようにTranslator Text APIとQnA Makerを繋ぎ込んでいるか見ていきましょう。
Translator Text API呼び出し部分の実装に当たっては、以下の記事を参考にさせていただきました。
[Bot FrameworkとTranslator Text APIを使って簡単な翻訳Botを作ってみる ② 機能追加編](https://azure-recipe.kc-cloud.jp/2017/09/cognitive-service-translator-text-02/)

今回は、オンラインエディタを使って解説します。

app.jsを開きます。
まず、ファイル冒頭でrequestをrequireします。

```js:app.js
var request = require('request');
```
次にTranslator Text APIとQnA Makerを繋ぎこむ箇所です。
最後の行のほうを書き換えます。

```js:app.js
// 以下をコメントアウト
// bot.dialog('/', basicQnAMakerDialog);
//以下を追加
bot.dialog('/', (session) => {
    var to_lang = 'en';
    // Translator Text APIとQnA Makerの連携
    translate(session.message.text, to_lang, (trans_text) => {
        // メッセージを英訳したものに置き換える
        session.message.text = trans_text;
        // QnA Makerに問い合わせ
        basicQnAMakerDialog.replyReceived(session);
    });
});

/**
 * Translater Text APIに問い合わせる
 */
var translate = (text, to_lang, callback) => {

    var api_key = "xxxxxxxxxx"; // 先ほど控えたtranslator text apiのkey
    get_token(api_key, (token) => {
        // リクエスト用のオプション
        var options = {
            url: "https://api.microsofttranslator.com/V2/Http.svc/Translate",
            qs: {
                'to': to_lang,
                'text': text
            },
            method: 'GET',
            headers: {
                'Authorization': 'Bearer ' + token
            },
            json: true
        };

        request(options, (error, response, body) => {
            // タグの除去
            body = body.replace(/<(.+?)>|<\/string>/g, '');
            callback(body);
        });
    });
};

/**
 * Cognitive Serviceへのリクエストに必要なトークンを取得する
 */
var get_token = (key, callback) => {
    // リクエスト用のオプション
    var options = {
        url: "https://api.cognitive.microsoft.com/sts/v1.0/issueToken",
        method: 'POST',
        headers: {
            'Content-Type': 'application/jwt',
            'Ocp-Apim-Subscription-Key': key
        },
        json: true,
    };
    
    request(options, (error, response, body) => {
        callback(body);
    });
};
```

上記の、
`var api_key = "xxxxxxxxxx"`の箇所を先ほど取得したTranslator Text APIのkeyに置き換えます。

なお今回は便宜上、省きましたが、
本来であればgitでソースを管理し、ローカルのエディタで開発できる環境を作るべきです。手元でデバッグをして開発できたほうがいいでしょう。
あと、ベタ書きしてるKEYの情報も環境変数で渡すようにすべきです。

# QnA Makerのナレッジを更新
Q側を英訳しておきましょう。
<img width="500" alt="QnA_Maker.png" src="https://qiita-image-store.s3.amazonaws.com/0/14929/e7493920-ac6f-e2b0-c78a-86573d8ebc46.png">


# テスト
<img width="300" alt="hira-bot-test01_-_Microsoft_Azure_and_app_js_—_isao-demo-chat.png" src="https://qiita-image-store.s3.amazonaws.com/0/14929/1ca91b0a-7dfb-b940-3a55-b2172e6376ff.png">

ちゃんと揺らぎを吸収してますね！

# 注意事項
QnA Makerはオプション機能として回答の候補を表示する事ができるんですが、当然Q側は英語に翻訳されて登録されているので英語のまま表示されます。
これも力技ですべて翻訳してから表示する事も可能ですが、Translator Text APIを複数回（3〜4回）呼ぶ事になるのでレスポンスタイムが極端に遅くなりますし、機械翻訳の結果をそのまま返す事になるのでトンチンカンな文章になる可能性があります。
回答の候補表示を利用されている場合は無効にしておくことをお勧めします。

# 懸念事項
* Qは英語、Aは日本語なのでQnA Makerのナレッジの管理が煩雑になります。Qのメンテナンスを行う際に、英語に堪能な方ならそのまま編集して問題ない事ですが、そうでない場合は英語->日本語に[Microsoft Translator](https://www.bing.com/translator/)などの機械翻訳サービスを使って翻訳し、本文全体を修正する必要があります。
現状だと、日本語の意図解釈の精度とメンテナンスコストはトレードオフになります。
* 毎回、受け取ったメッセージをTranslator Text APIに投げるので、Botのレスポンスが遅くなります。ここもまあまあ無視できないレベルではあります。当然、同期的にTranslator Text API + QnA Makerの両方のレスポンスを待たなければいけません。ユーザが、そのBotに対してクイックレスポンスを期待する場合は、実運用に耐えられない可能性があります。

# おわりに
Azure Bot Serviceと連携できるCognitive Servicesは他にも沢山あり、本件のように繋ぎ込んで連携させると様々な事が実現できます。
また、Bot ServiceとQnA Makerの結果の違いについては今後改善される可能性が高いので暫くウォッチしたほうが良さそうです。
もし、改善されているようであれば、上記の注意事項、懸念事項は考えなくてもよくなるので、Translator Text APIの接続は外すべきです。

以上、素晴らしいBotライフを。