概要
MicrosoftのLUISは自然言語を解析してくれるサービスですが、現状 英語/フランス語/イタリア語/スペイン語/中国語 にしか対応していないという問題があります。
【参考記事】
『Build2016 : LUIS による自然言語入力解析』
https://blogs.msdn.microsoft.com/bluesky/2016/04/05/build2016-luis-natural-language-understanding/
しかし、Bot Connectorを通すことでその自動翻訳機能を利用することができ、結果的に「自然な日本語で受け答えができるボット」を開発することができます。
今回は「日付を入力したらその曜日を答える」ボットを作ってみます。
ボットを作成し、Bot Connectorに登録する
下記記事にMicrosoft Bot Frameworkを使って作成したボットをBot Connectorに登録するまでの流れが紹介されています。
『SlackとWeb上のチャットをつなぐ / MS謹製のbot作成フレームワーク「Bot Builder」で遊んでみた!』
http://qiita.com/o0h/items/c10491a48cf014195338
記事中に公式ドキュメントから引用された図が貼られていますが、"Bot Connector"の欄に"Automatic translation to 30+ languages"という記述があります。今回はこれのお世話になろうというわけです。
コードは「Bot Builderを利用して」に貼られている方をベースとします。以下、転載となります。
var restify = require('restify');
var builder = require('botbuilder');
var bot = new builder.BotConnectorBot({ appId: process.env.appId, appSecret: process.env.appSecret});
var dialog = new builder.CommandDialog();
dialog.matches(['Hi', 'Hello', 'こんにちは'], function (session) {
session.send('こんにちは');
});
bot.add('/', dialog);
var server = restify.createServer();
server.post('/v1/messages', bot.listen());
server.listen(process.env.port || 8080, function () {
console.log('%s listening to %s', server.name, server.url);
});
これをAzure(App Service)あたりに上げます。
よろしければ、下記記事もご覧ください。
『Macで開発したボットをAzureで運用する』
http://qiita.com/fullkawa/items/5a661d55311d45279aaf
Bot Connectorに登録する際、エンドポイントとして用意したパス(上記コードで言うと server.post('/v1/messages', bot.listen());
の部分)を登録するのがポイントです。
appId, appSecret は「アプリケーション設定 > アプリ設定」から渡します。
OK! 無事、Bot Connectorを通して受け答えができるようになりました。
LUISにIntent, Entityを登録する
Microsoft LUISに関する説明、簡単な使い方は下記記事が参考になります。
『Build2016 : LUIS による自然言語入力解析』
https://blogs.msdn.microsoft.com/bluesky/2016/04/05/build2016-luis-natural-language-understanding/
このあたりから少し詳しく説明していきます。
まず、新規アプリケーションを作成します。
ご覧の通り、選択できる言語のリストに日本語はありません。
ここは"English"を選択しておきます。
今回は「日付を入力したらその曜日を答える」ボットを作るのが目的だったわけですが、ここで具体的な問答を考えてみましょう。
問:今日は何曜日ですか?
答:その日は水曜日です。
上記問の「今日」の部分はいろいろな日付表現を受け付けたいところです。これが今回の"Entity"となります。
LUISには組み込みのエンティティ(Pre-built Entities)に日付(datetime)があるので、それを利用しましょう。
左メニュー「Pre-built Entities」横の追加ボタンを押し、"datetime"を選択し、[OK]を押します。
"datetime"が利用可能になりました。
次にIntentを登録します。
例文を英語で入力する必要がありますが、Bot Connectorで自動翻訳された英文がLUISに渡されるのであれば、Bing翻訳の英訳で十分でしょう。これで英語に自信がない人でも大丈夫です。
左メニュー「Intents」横の追加ボタンを押し、インテント名(任意)と先程翻訳した例文を登録し、[Save]を押します。
Entity候補として自動的に"today"の部分が選択されました。これで問題ありませんので、[Submit]を押します。
"what_day"が登録されました。
"None"は最初から登録されているIntentで、どれにもマッチしない場合を表します。
左下"Train"ボタンを押して、例文を学習させます。
左メニュー「Publish」から[Publish web service]を押すとURLが表示されます。
ここから、idとsubscription-keyを取得します。
Intentに対する返答を実装する
ここまでで以下の流れができるようになっています。
[chat] 今日は何曜日ですか?
↓
[Bot Connector] Today what is the day of the week?
↓
[LUIS] Intent="what_day", Entity(datetime)="today"
これに対する返答をNode.jsで実装してやります。
サンプルコードは下記の通りです。
CommandDialogの代わりにLuisDialogを利用しています。id
, subscription-key
は「アプリケーション設定 > アプリ設定」から渡すようにします。
単に返答を返すだけでなく、どのようなデータが受け渡されているかをログに出力してみました。
var restify = require('restify');
var builder = require('botbuilder');
var bot = new builder.BotConnectorBot({ appId: process.env.appId, appSecret: process.env.appSecret});
var url = 'https://api.projectoxford.ai/luis/v1/application?id=' + process.env.luisId
+ '&subscription-key=' + process.env.luisSubscriptionKey
var dialog = new builder.LuisDialog(url);
bot.add('/', dialog);
// Intent="what_day"の場合の処理
dialog.on('what_day', function(session, args) {
console.log('message:');
console.log(session.message);
var date = builder.EntityRecognizer.findEntity(args.entities, 'builtin.datetime.date');
console.log('date:');
console.log(date);
if (date != undefined && date.resolution != undefined) {
var d = new Date(date.resolution.date);
var day = '日月火水木金土'[d.getDay()];
session.send('その日は「' + day + '曜日」です。');
} else {
session.send('日付を取得できませんでした。');
}
});
// Intent="None"の場合の処理
dialog.onDefault(function(session, args) {
console.log('args:');
console.log(args);
console.log('message:');
console.log(session.message);
session.send("質問を理解できませんでした。もう一度、少し表現を変えて質問してみてください。")
});
var server = restify.createServer();
server.post('/v1/messages', bot.listen());
server.listen(process.env.port || 8080, function () {
console.log('%s listening to %s', server.name, server.url);
});
動作確認
入力した文章を理解してもらえませんでした。
args:
{ intents:
[ { intent: 'None', score: 0.57440114 },
{ intent: 'what_day', score: 0.0471038148 } ],
entities: [] }
message:
{ type: 'Message',
id: '9UcWTbjioJI',
conversationId: 'DFS1VbGtB9ae5Ez4m49dp5TFGUj6kP5ol81I6nu7AVEEJ3Il',
created: '2016-06-15T03:15:44.8295028Z',
language: 'en',
text: '今日は何曜日ですか?',
ログを確認すると、LUISはこの文を'what_day'ではなく'None'だと解釈したようです。
Messageの内容を見ると、language: 'en'
となっています。入力をそのまま英文として解釈したようですね。
ボットに対してこれから日本語を使うことを伝え、再度同じ質問をすると今度は期待した答えをくれました。
message:
{ type: 'Message',
id: 'GuWITxWtRCb',
conversationId: '7Uj2OT5w85OUFhL9UNfzG2p8fF5wR78wFwaAVlDgCLs7om',
created: '2016-06-15T03:17:29.3002618Z',
sourceText: '今日は何曜日ですか?',
sourceLanguage: 'ja',
language: 'en',
text: 'Today what is the day of the week?',
(中略)
date:
{ entity: 'today',
type: 'builtin.datetime.date',
startIndex: 0,
endIndex: 4,
resolution: { date: '2016-06-15' } }
sourceText → text と自動翻訳されているのが分かります。
また、翻訳された"today"がEntityとして解釈され、それがLUISによって"2016-06-15"と解決されていることも分かります。
Bot Connectorの設定項目に"Default Conversation Language"というのがあり、これに"ja"と入れることで言語切替なしでいけることを期待したのですが、そうなりませんでした。他の設定手段があるのでしょうか?
それはさておき、他のパターンも試してみましょう。
和暦も理解してくれるのはありがたいですね!
message:
{ type: 'Message',
id: 'GQPdJkOyxrc',
conversationId: '7Uj2OT5w85OUFhL9UNfzG2p8fF5wR78wFwaAVlDgCLs7om',
created: '2016-06-15T03:31:41.7877018Z',
sourceText: '平成28年6月16日は何曜日だっけ?',
sourceLanguage: 'ja',
language: 'en',
text: '6/16/2016, what is the day of the week?',
(中略)
date:
{ entity: '6/16/2016',
type: 'builtin.datetime.date',
startIndex: 0,
endIndex: 8,
resolution: { date: '2016-06-16' } }
では、デタラメな質問を投げると…?
Intentの判定には成功しましたが、Entityは取得できませんでした。
妥当な結果だと思います。
message:
{ type: 'Message',
id: '7ZrZJP1tEcH',
conversationId: '7Uj2OT5w85OUFhL9UNfzG2p8fF5wR78wFwaAVlDgCLs7om',
created: '2016-06-15T04:31:46.4026778Z',
sourceText: '私は何曜日でしょうか?',
sourceLanguage: 'ja',
language: 'en',
text: 'I what what day of the week?',
(中略)
date:
null
このクオリティであれば、十分実用に耐えうるのではないでしょうか。