黒澤ルビィ、可愛いですよね。
LINE上で喋るルビィちゃんを作りました。Rubyで。
以下の仕組みで動いています。
-
LINE Messaging API
を用いて、「黒澤ルビィ」としてLINEメッセージの受信、送信を行う。 -
Ruby(Ruby on rails)
でLINE Messaging API
とやり取りするAPIサーバを作る。 - メッセージを形態素解析し、その結果をもとにマルコフ連鎖で新しく文章を生成する
LINEアカウントを作る
前準備としてルビィちゃんとして動くアカウント(厳密に言うと、Messaging APIのチャネル)が必要です。それをつくります。
LINE Developersから登録します。
Create a new channel
から新しく作ります。なお、作るときにどういうTypeか聞かれると思います。今回作りたいものはMessaging APIで作ることができるので、該当するものを選択してください。
無事作成できると、アクセストークンを取得できます。
また、アカウントのアイコンや自己紹介欄などを設定できます。1
APIサーバを作る
画面を持つ予定はないので、APIモードでRailsプロジェクトを作ります。
line-bot-api
gem2を入れます。先程作成したアカウントのアクセストークンを使ってLINEとやりとりできるようになります。
Controllerを作る
先程作成したアカウントに対してWebhook URLを設定すると、そのアカウントに対して送信したメッセージをトリガーにしてLINEがリクエストを送ってくれます。
具体的なリクエストの中身についてはMessaging APIリファレンスを参照してください。以下の例はテキストメッセージに反応して、「メッセージ」と返すだけの仕組みです。
def client
@client ||= Line::Bot::Client.new { |config|
config.channel_secret = ENV["LINE_CHANNEL_SECRET"]
config.channel_token = ENV["LINE_CHANNEL_TOKEN"]
}
end
def callback
body = request.body.read
signature = request.env['HTTP_X_LINE_SIGNATURE']
head :bad_request unless client.validate_signature(body, signature)
events = client.parse_events_from(body)
events.each do |event|
next unless event.is_a?(Line::Bot::Event::Message)
next unless event.type == Line::Bot::Event::MessageType::Text
client.reply_message(event['replyToken'], message)
end
head :ok
end
def message
# cf. https://developers.line.biz/ja/reference/messaging-api/#text-message
{
"type": 'text',
"text": 'メッセージ'
}
end
このmessage
に相当する部分を、マルコフ連鎖で生成した文章にさせます。
マルコフ連鎖で文章を作る
マルコフ連鎖による文章作成の大枠については解説しているものがたくさんあるので、詳しくは「マルコフ連鎖 文章作成」とかで検索してください。
ざっくり説明すると、あらかじめ形態素解析(言語で意味をもつ最小単位に分解すること)によって言葉のつながりを学習させ、ある単語に対してそれに続く言葉を過去の学習から確率的に選択していくことでそれっぽい文章ができるやつです。
形態素解析する
形態素解析は、Yahoo!が提供している日本語形態素解析を使っています。MeCab(Natto)を使って、オタク用語に特化した辞書を入れるのもアリかなと思います。
LINEメッセージが送られたらそのメッセージをそのまま形態素解析します。結果をパースし、言葉と品詞がセットとなったハッシュの配列にします。3
単語として保存するWord
モデルと、つながりを保存するMarkovDic
モデルを作り、それぞれ保存します。
Word
モデル
カラム名 | 型 | 説明 |
---|---|---|
pos | String | 品詞(例:名詞) |
surface | String | 単語(例:庭) |
MarkovDic
モデル
カラム名 | 型 | 説明 |
---|---|---|
prefix_1 | String | 単語のつながりをA-B-Cと表現したときのA (例:私) |
prefix_2 | String | 単語のつながりをA-B-Cと表現したときのB (例:は) |
suffix | String | 単語のつながりをA-B-Cと表現したときのC (例:犬) |
# 形態素解析を行い、WordレコードとMarkovDicレコードを作成する。名詞の配列を返す。
def record(text, user)
text = remove_url(text) # URLを取り除いたりしている
morphological_words = exec(text)
poses = save_words(morphological_words, user)
save_markov_dics(morphological_words)
poses
end
# 単語を保存する。
def save_words(morphological_words)
words = []
morphological_words.each do |word|
w = Word.new(word)
words << w.attributes.compact!.merge({ created_at: now, updated_at: now })
end
Word.insert_all(words) if words.present?
end
def save_markov_dics(morphological_words)
markov_dics = []
morphological_words.each_cons(3) do |p1, p2, suf|
next if p1['surface'] == "\n"
next if p2['surface'] == "\n"
md = MarkovDic.new(prefix_1: p1['surface'], prefix_2: p2['surface'], suffix: suf['surface'])
md.suffix = 'END_OF_SENTENCE' if md.suffix == "\n" # 文章の終わりを意味するフラグ
markov_dics << md.attributes.compact!.merge({ created_at: now, updated_at: now })
end
eos = morphological_words.last(2)
if eos.size == 2
p1 = eos[0]
p2 = eos[1]
md = MarkovDic.new(prefix_1: p1['surface'], prefix_2: p2['surface'], suffix: 'END_OF_SENTENCE')
markov_dics << md.attributes.compact!.merge({ created_at: now, updated_at: now })
end
MarkovDic.insert_all(markov_dics) if markov_dics.present?
end
文章を作る
LINEに送られたメッセージを保存することができたので、今度は文章を作ります。
始まりの言葉は受け取ったLINEのメッセージからランダムに名詞で始まるMarkovDic
を選んでいます。
ざっくり以下のような感じです。
def create_sentence(text, markov_dic)
return text if markov_dic.suffix == 'END_OF_SENTENCE'
next_markov_dic = MarkovDic.where(prefix_1: markov_dic.prefix_2, prefix_2: markov_dic.suffix).sample(1).first
return text unless next_markov_dic
text << markov_dic.suffix
create_sentence(text, next_markov_dic)
end
def prefixes
prefix_1 + prefix_2
end
Webhook URLを設定する
作成したAPIサーバをデプロイします。(今回はHerokuにデプロイしました。)
最初に作成したアカウントの設定画面でWebhook URLを設定し、メッセージがきたタイミングでLINEがリクエストを送ってくれるようにします。
LINEグループに追加します。いい感じにレスポンスしてくれます。
その他
- グループラインにルビィちゃんを混ぜたところ、「かわいい」「キメラが誕生した」「新しいおもちゃ」と非常に好評でした。
- クソデカ羅生門を覚えさせた人がいてめちゃくちゃになりました。
- グループラインにBotアカウントは一つまでしか入れられないようです。黒澤姉妹で話すことはできませんでした。
- 常に返信がくるとグループラインがやかましくなるので、ランダムで投稿したりしなかったりさせています。(ちなみに直接ルビィちゃんに話しかけると100%返信が来ます。)
- push_messageを使うとルビィちゃんから話しかけることもできます。
- 本当はアカウントを紹介したいのですが、記録した言葉の中に含まれる個人情報を考慮すると中々難しいものがあります。
- 今の所ルビィちゃん要素が0なので、ルビィちゃんっぽくさせたいです。
まとめ
LINE Messaging API
とRuby
を用いて黒澤ルビィを作りました。
LINE Botは意外と簡単に作ることができるので、ぜひ作ってみてください。
追記:リポジトリはこちらで公開しております。
-
開発用のアカウントも合わせて作ったりしています。ちなみに画像は友達が描いたやつです。 ↩
-
以下を参考にしました。Ruby on Rails で Yahoo のテキスト解析 APIを使う-スケ郎のお話 ↩