🦔

【Rails】Yahoo! 自然言語理解APIを使って雑談botを作る

2022/05/20に公開

Ruby on Railsの学習のため、Twitterのリプライを取得してリプ返を自動生成するWebアプリ「リプ返つらい」を作成しました。ユーザー登録不要で遊べるのでよかったら覗いてみてください。

https://replytsurai.onrender.com/

今回は、リプ返を生成するために用いた「Yahoo! 自然言語理解 API」の使い方をまとめます。

Yahoo! 自然言語理解 APIとは

https://developer.yahoo.co.jp/webapi/jlp/nlu/v1/

Yahoo!音声アシストでも使われている自然言語理解Web API。無料で利用可能(ただし、サイト上にYahooのクレジットを表記する必要あり)で、日本語文を解析し、情報の抽出を行うことができます。

公式ドキュメントが少々分かりづらいのですが、要は解析したいテキストをパラメータに含めてリクエストを送ると、API側がテキストの持つ意図などを抽出してレスポンスしてくれるという仕組みです。

出力仕様のひとつにSAY(自然言語による応答を返す)というものがあったので、この出力を使ってリプ返を自動生成するアプリを実装していきます。

Yahoo! アプリケーション登録

下準備として、Yahoo!デベロッパーネットワークの利用ガイドに沿って、Yahoo! JAPAN IDを取得してから、アプリケーションを登録し、アプリケーションID(Client ID)を発行します。

https://developer.yahoo.co.jp/start/


発行されたアプリケーションID(グレーで塗った部分)を使用します

素のRubyファイルでテスト

Railsに移植する前に、Yahoo 自然言語理解 APIの動作を素のRubyファイルでテストしてみます。

RubyからHTTPリクエストを送るために、net/httpライブラリを読み込んでいます。またレスポンスをjson形式に変換して扱いやすくするために、jsonライブラリも読み込んでいます。

yahoo_api_test.rb
require 'net/http'
require 'json'

yahoo_url = 'https://jlp.yahooapis.jp/NLUService/V1/analyze'
appid = 'アプリケーションID'

# 解析するテキストの取得
puts 'テキストを入力してください'
text = gets
escape_text =  URI.escape(text)

# HTTPリクエストを投げてAPIからレスポンスを得る
url = yahoo_url + '?appid=' + appid + '&intext=' + escape_text
uri = URI.parse(url)
response = Net::HTTP.get_response(uri)

# 結果をコンソール画面に出力
puts JSON.parse(response.body)
テキストを入力してください
おはようございます
{"result"=>{"intext"=>"おはようございます\n", "method"=>"SAY", 
"param_small_talk_intent"=>"GREETINGS_GOOD_MORNING", 
"param_text"=>"おはようございます。今日もよろしくお願いします。", "status"=>"200", 
"var_cv1"=>"0.855", "var_intext"=>"おはようございます\n", 
"var_intext_normalized"=>"おはようございます\n", 
"var_intext_timestamp"=>"1652968396", "var_test_btsc"=>"16"}}

テキストを入力してください
今日の天気は?
{"result"=>{"intext"=>"今日の天気は?\n", "method"=>"WEATHER", 
"param_datetime_from"=>"今日", "param_method_subcat"=>"GENERAL", 
"status"=>"200", "var_cv1"=>"0.855", "var_intext"=>"今日の天気は?\n", 
"var_intext_normalized"=>"今日の天気は\n", 
"var_intext_timestamp"=>"1652968429", "var_test_btsc"=>"49"}}

リクエストURLはhttps://jlp.yahooapis.jp/NLUService/V1/analyzeで、必須パラメータはアプリケーションIDを示すappidと、解析対象のテキストintextの2つです。

リクエストが成功すると、APIの解析結果をresultパラメータに含めてレスポンスが返ってきます。

APIの解析結果(応答)にはいくつか種類があり、resultの中のmethodパラメータを見るとどの種類の応答が返ってきているのかがわかります。

例えば、「おはようございます」という入力にはSAY(雑談)の応答が返ってきて、「今日の天気は?」という入力にはWEATHER(天気)の応答が返ってきました。

このmethodパラメータがSAYのときだけ、APIによる返事がレスポンスに含まれています。APIによる返事はparam_textパラメータから取得できます。

「おはようございます」という入力には、「おはようございます。今日もよろしくお願いします。」という返事が返ってきました。きちんと入力テキストの意図を理解して、対応する返事を返してくれています。

今回は自分がもらったリプライに対するリプ返を自動生成したいので、入力テキストはリプライのテキスト本文、出力はmethodパラメータがSAYのときはparam_textを、methodパラメータがSAY以外のときは返事なしとすることにします。

Railsで実装する

上記のテストコードをベースに、RailsでWebアプリ化します。作りたいアプリはCRUD機能がいらないためRailsで実装する必要はなかったのですが、一番慣れているフレームワークなのでRailsを使用しました。

Yahoo APIに関する部分だけを抜粋します。

config/routes.rb
Rails.application.routes.draw do
  get 'yahoo/show', to: 'yahoo#show'
end
app/controllers/yahoo_controller.rb
class YahooController < ApplicationController
  require 'net/http'
  require 'json'

  def show
    @text = params[:text]
    @reply = get_reply(@text)
  end

  private

  def get_reply(text)
    yahoo_url = 'https://jlp.yahooapis.jp/NLUService/V1/analyze'
    appid = ENV['YAHOO_APP_ID']

    escape_text =  URI.escape(text)
    url = yahoo_url + '?appid=' + appid + '&intext=' + escape_text
    uri = URI.parse(url)
    response = Net::HTTP.get_response(uri)

    json_response_body = JSON.parse(response.body)
    if response.code == '200' && json_response_body['result']['method'] == 'SAY'
      json_response_body['result']['param_text']
    else
      'AIもリプライを思いつきませんでした。自分で考えよう!'
    end
  end
end
app/views/yahoo/show.html.erb
<main class="container">
  <div class="row text-center justify-content-center">
    <div class="col">
      <div class="card text-center">
        <div class="card-header">
          もらったリプライ
        </div>
        <div class="card-body">
          <p class="card-text"><%= @text %></p>
        </div>
      </div>
      <p></p>
      <p>に対するAIのリプ返は…</p>
      <p></p>
      <div class="card text-center">
        <div class="card-header">
          AIが自動生成したリプ返
        </div>
        <div class="card-body">
          <h4 class="card-title"><%= @reply %></h4>
          <p class="card-text">#リプ返つらい</p>
        </div>
      </div>
      <p></p>
      <div>
        <% tweettext = "@#{@authorname}さんのツイート%0A「#{@text}」%0A%0AにAIがリプ返!%0A%0A「#{@reply}」%0A%0A" %>
        <%= link_to 'AIのリプ返をTwitterで投稿する', "https://twitter.com/intent/tweet?text=#{tweettext}&hashtags=リプ返つらい&url=https://replytsurai.onrender.com/", class: 'btn btn-primary' %>
      </div>
    </div>
  </div>
</main>

前段階でTwitter APIを用いてリプライを取得しておき(Twitter APIに関しては別の記事で解説します)、yahooコントローラのshowアクションに、リプライのテキストをparams[:text]に入れて渡します。

そしてget_replyメソッドでYahoo APIを叩いてリプ返を生成し、@replyに入れてビューファイルに渡します。

テストコードからの主な変更点は2つあり、1つ目はアプリケーションIDを外部から秘匿するために、環境変数ENV['YAHOO_APP_ID']から呼び出していることです。

開発時のローカル環境では、環境変数の設定は.bash_profileに記述しました。

#.bash_profileをvimで開く
vim ~/.bash_profile
# .bash_profileに下記の一行を追加
export YAHOO_APP_ID='アプリケーションID'
#.bash_profileを反映
source ~/.bash_profile

本番ではrender.comにデプロイしたので、render.comのダッシュボードに環境変数を設定しました。

変更点の2つ目は、get_replyメソッドの戻り値の部分です。レスポンスコードが200かつmethodパラメータがSAYのときはparam_textパラメータを戻り値とし、そうでないときは返事なしに該当するテキストを戻り値としています。

このように実装することで、誰でもリプ返を自動生成できるWebアプリが完成しました。

感想

Yahoo APIの内部でどのように返事を生成しているのか、そのアルゴリズムまでは公開されておらず気になるところですが、なかなか味のある返事を返してくれます。独自のカスタムルールも作れるので、簡単なチャットbotなどを作ってみたい方には面白いのではないでしょうか。

https://developer.yahoo.co.jp/webapi/jlp/nlu/v1/doc/tutorial/customize-response.html

Discussion