kuromoji.jsで形態素解析をする
slack botをもう少し賢くしたい。といかけに含まれる名詞に反応できるようにしたい。そこで、kuromoji.jsによる形態素解析をためしてみる。 github.com
$ node kuromoji_test.js 文をどうぞ 明日は関ヶ原で憎きあの男を倒すの [ '明日', '関ヶ原', '男' ] [ '倒す' ] [ '憎い' ]
さっそく使ってみた
var readline = require('readline'); var kuromoji = require("kuromoji"); kuromoji.builder({ dicPath: "node_modules/kuromoji/dict" }).build(function(err, tokenizer) { if (err) { throw err; } // 標準入力をうけつけるインターフェース var rl = readline.createInterface({ input: process.stdin, output: process.stdout }); // 入力をうけつけて解析する rl.question("文をどうぞ ", function(answer) { var result = tokenizer.tokenize(answer); console.log(result); rl.close(); }); });
実際に動かしてみた。
$ node kuromoji_test.js 文をどうぞ 今日はがんばったんだよ [ { word_id: 126270, word_type: 'KNOWN', word_position: 1, surface_form: '今日', pos: '名詞', pos_detail_1: '副詞可能', pos_detail_2: '*', pos_detail_3: '*', conjugated_type: '*', conjugated_form: '*', basic_form: '今日', reading: 'キョウ', pronunciation: 'キョー' }, { word_id: 93010, word_type: 'KNOWN', word_position: 3, surface_form: 'は', pos: '助詞', pos_detail_1: '係助詞', pos_detail_2: '*', pos_detail_3: '*', conjugated_type: '*', conjugated_form: '*', basic_form: 'は', reading: 'ハ', pronunciation: 'ワ' }, { word_id: 3153770, word_type: 'KNOWN', word_position: 4, surface_form: 'がんばっ', pos: '動詞', pos_detail_1: '自立', pos_detail_2: '*', pos_detail_3: '*', conjugated_type: '五段・ラ行', conjugated_form: '連用タ接続', basic_form: 'がんばる', reading: 'ガンバッ', pronunciation: 'ガンバッ' }, { word_id: 23430, word_type: 'KNOWN', word_position: 8, surface_form: 'た', pos: '助動詞', pos_detail_1: '*', pos_detail_2: '*', pos_detail_3: '*', conjugated_type: '特殊・タ', conjugated_form: '基本形', basic_form: 'た', reading: 'タ', pronunciation: 'タ' }, { word_id: 63530, word_type: 'KNOWN', word_position: 9, surface_form: 'ん', pos: '名詞', pos_detail_1: '非自立', pos_detail_2: '一般', pos_detail_3: '*', conjugated_type: '*', conjugated_form: '*', basic_form: 'ん', reading: 'ン', pronunciation: 'ン' }, { word_id: 23680, word_type: 'KNOWN', word_position: 10, surface_form: 'だ', pos: '助動詞', pos_detail_1: '*', pos_detail_2: '*', pos_detail_3: '*', conjugated_type: '特殊・ダ', conjugated_form: '基本形', basic_form: 'だ', reading: 'ダ', pronunciation: 'ダ' }, { word_id: 92300, word_type: 'KNOWN', word_position: 11, surface_form: 'よ', pos: '助詞', pos_detail_1: '終助詞', pos_detail_2: '*', pos_detail_3: '*', conjugated_type: '*', conjugated_form: '*', basic_form: 'よ', reading: 'ヨ', pronunciation: 'ヨ' } ]
品詞と標準型をとりだしてみる
var readline = require('readline'); var kuromoji = require("kuromoji"); kuromoji.builder({ dicPath: "node_modules/kuromoji/dict" }).build(function(err, tokenizer) { if (err) { throw err; } // 標準入力をうけつけるインターフェース var rl = readline.createInterface({ input: process.stdin, output: process.stdout }); // 入力をうけつけて解析する rl.question("文をどうぞ ", function(answer) { var result = tokenizer.tokenize(answer); // ひとつずつ確認する result.forEach(function(word) { console.log(word.pos + ' ' + word.basic_form); }); rl.close(); }); })
標準形にできると形容詞の使い勝手があがりそう。
$ node kuromoji_test.js 文をどうぞ 明日はマラソン大会で今からすごい憂鬱なの 名詞 明日 助詞 は 名詞 マラソン 名詞 大会 助詞 で 名詞 今 助詞 から 形容詞 すごい 名詞 憂鬱 助動詞 だ 助詞 の
名詞・動詞・形容詞ごとにとりだせるようにしておく
var readline = require('readline'); var kuromoji = require("kuromoji"); kuromoji.builder({ dicPath: "node_modules/kuromoji/dict" }).build(function(err, tokenizer) { if (err) { throw err; } // 標準入力をうけつけるインターフェース var rl = readline.createInterface({ input: process.stdin, output: process.stdout }); // 入力をうけつけて解析する rl.question("文をどうぞ ", function(answer) { var result = tokenizer.tokenize(answer); var noun = [], verb = [], adjective = []; // ひとつずつ確認する result.forEach(function(word) { switch (word.pos) { case '名詞': noun.push(word.basic_form); break; case '動詞': verb.push(word.basic_form); break; case '形容詞': adjective.push(word.basic_form); break; } }); // 一覧を確認する console.log(noun); console.log(verb); console.log(adjective); rl.close(); });
結果はこんな感じ。使いやすそうなので、実際に組み込んでみる。
$ node kuromoji_test.js 文をどうぞ 明日は関ヶ原で憎きあの男を倒すの [ '明日', '関ヶ原', '男' ] [ '倒す' ] [ '憎い' ]
Botkitでできること
Botkitでslcak botをつくることになった。かんたんにbotが作れると評判のslack bot、標準でどんなことができるのかREADMEをしらべてみる。
ユーザーからのメッセージに反応する
message_received
イベントの購読で、なにかしらの書き込みに反応できる。ほかにdirect_mention
やdirect_message
イベントでそれぞれの書き込みに反応できる。メッセージを返すのはbot.reply
関数を使ってできる。
controller.on('message_received', function(bot, message) { bot.reply(message, 'I heard... something!'); });
hears
関数で特定のワードに反応できる。ワードはリストで指定できる。
controller.hears(['keyword1', 'keyword2'], ['message_received'], function(bot, message) { bot.reply(message, 'You used a keyword!'); });
hears
関数ではパターンマッチングがつかえる。マッチした結果はmessage.match
フィールドに格納される。このフィールドにはJavaScriptのstring.match
と同じ形式で情報が格納されている。
controller.hears('open the (.*) doors', ['message_received'], function(bot, message) { var doorType = message.match[1]; if (doorType === 'pod bay') { return bot.reply(message, 'I\'m sorry, Dave. I\'m afraid I can\'t do that.'); } return bot.reply(message, 'Okay'); });
botからメッセージを送る
ユーザからのメッセージにひとつずつreplay
するだけでなく、bot.say
関数でbotからメッセージを発信することもできる。
controller.hears(['hello world'], 'message_received', function(bot, message) { bot.startConversation(message, function(err, convo) { convo.say('Hello!'); convo.say('Have a nice day!'); }); });
ユーザーにといかけて返答を待つこともできる。ask
関数を利用する。ユーザから返答がきたあとの処理をcallbackに記述しておけばいい。
controller.hears(['question me'], 'message_received', function(bot, message) { bot.startConversation(message, function(err, convo) { convo.ask('How are you?', function(response, convo) { convo.say('Cool, you said: ' + response.text); convo.next(); }); }) });
会話が複雑になってくるとcallbackの管理がつらくなってくる。そんなときのためにThered機能がある。
bot.createConversation(message, function(err, convo) { // create a path for when a user says YES convo.addMessage({ text: 'You said yes! How wonderful.', },'yes_thread'); // create a path for when a user says NO convo.addMessage({ text: 'You said no, that is too bad.', },'no_thread'); // create a path where neither option was matched // this message has an action field, which directs botkit to go back to the `default` thread after sending this message. convo.addMessage({ text: 'Sorry I did not understand.', action: 'default', },'bad_response'); // Create a yes/no question in the default thread... convo.ask('Do you like cheese?', [ { pattern: 'yes', callback: function(response, convo) { convo.changeTopic('yes_thread'); }, }, { pattern: 'no', callback: function(response, convo) { convo.changeTopic('no_thread'); }, }, { default: true, callback: function(response, convo) { convo.changeTopic('bad_response'); }, } ]); convo.activate(); });
いくつかの質問を続けるような場合は、ほかの関数を呼び出すような形で書くこともできる。
controller.hears(['pizzatime'], 'message_received', function(bot,message) { var askFlavor = function(err, convo) { convo.ask('What flavor of pizza do you want?', function(response, convo) { convo.say('Awesome.'); askSize(response, convo); convo.next(); }); }; var askSize = function(response, convo) { convo.ask('What size do you want?', function(response, convo) { convo.say('Ok.') askWhereDeliver(response, convo); convo.next(); }); }; var askWhereDeliver = function(response, convo) { convo.ask('So where do you want it delivered?', function(response, convo) { convo.say('Ok! Good bye.'); convo.next(); }); }; bot.startConversation(message, askFlavor); });
ユーザの「はい」「いいえ」を藩閥するのに便利な関数も提供されている。bot.utterances.yes
はyes, yeah, yup, ok and sure
にヒットする。 bot.utterances.no
はno, nah, nope
にヒットする。 日本語には対応していないみたいなので拡張をしながら使う。
情報の保存
デフォルトだとBotkitはJSON filesに書き込みをする形で情報を保存する。保存先を指定することができる。
var controller = Botkit.slackbot({ json_file_store: 'path_to_json_database' });
保存する際にキーを設定する。キーにはユーザーやチャンネル、チームを利用するといい。
controller.storage.users.save({id: message.user, foo:'bar'}, function(err) { ... }); controller.storage.users.get(id, function(err, user_data) {...}); controller.storage.users.delete(id, function(err) {...}); controller.storage.users.all(function(err, all_user_data) {...});
JSON files以外にもDBなどを利用することもできる。
var controller = Botkit.slackbot({ storage: my_storage_provider })