IBM Watson Retrieve and Rank に問い合わせるLINE Botの作成

  • 2
    Like
  • 0
    Comment

Watson Retrieve And Rank に問い合わせるLINE Botの作成

社内の勉強会でLINE Botを立てるお題が出たので、どうせならと構築済みの IBM Watson Retrieve and Rank サービスに問い合わせるようにしました。
もっとも実質は、ryuta46 さんの LINE Bot で雑談 Bot 作ってみた の問い合わせ先を Talk API から IBM Watson Retrieve and Rank に変えただけです。

準備1 - Watson Retrieve And Rank サービスの構築

まず、Watson Retrieve And Rank サービスを構築します。
今回は ダイヤルIBM FAQ のデータでサービスを構築しました。
構築方法は、良い記事があちこちにあるので、検索してみてください。
Bot アプリから R&R にアクセスするため下記の値を確認しておきます。

  • USERNAME
  • PASSWORD
  • COLLECTION名
  • CLUSTER ID
  • RANKER ID

準備2 - Bluemix上に Python のサーバーを構築

Bluemix 上である必然はないのですが、R&Rを使えるなら Bluemix にサーバーを立てられるだろうということで、こちらを使います。
url を確認しておきます。

準備3 - LINE Botの作成・設定

LINE Business Center から LINE Bot を作成・設定します。
Bot の設定は、良い記事があちこちにあるので、検索してみてください。
Webhook URL は、アプリに合わせ「サーバーURL + /callback」にします。
Channel Secret, Channel Access Token, QRコードを確認しておきます。

ryuta46 さんのLINE Bot の入手

ryuta46 さんは作成したコードを GitHub で公開されているので、これをありがたく頂戴します。

$ git clone https://github.com/ryuta46/linebottalk linebotrnr
$ cd linebotrnr/

構成用の情報の config.json から Talk API の情報を削除し R&R の情報を追加します。
もちろん、実際には値を各項目にセットします。

config.json
{
    "lineChannelToken":"",
    "lineChannelSecret": "",
    "RnR_USERNAME": "",
    "RnR_PASSWORD": "",
    "RnR_COLLECTION": "",
    "RnR_CLUSTER_ID":"",
    "RnR_RANKER_ID": ""
}

本体の server.py を変更していきます。

まず、R&R で利用するモジュールを import します。

server.py
import pysolr
from watson_developer_cloud import RetrieveAndRankV1

config.json の値を読み込んでいる部分があるので、talk_api_key を読まないようにし、R&R の情報を読むようにします。

さらに、初期化の一環として RetrieveAndRankV1 オブジェクトを作成します。

server.py
config_file = open('config.json' , 'r')
config = json.load(config_file)
# talk_api_key = config["talkApiKey"]
line_channel_token = config["lineChannelToken"]
line_channel_secret = config["lineChannelSecret"]:

rnr_collection = config["RnR_COLLECTION"]
rnr_cluster_id = config["RnR_CLUSTER_ID"]
rnr_ranker_id  = config["RnR_RANKER_ID"]

retrieve_and_rank = RetrieveAndRankV1(
     username = rnr_user, 
     password = rnr_pass )

Talk API へのアクセスが記述された handle_message メソッドを R&R へのアクセスに書き換えます。
LINE で大量のメッセージを返すと見づらいので、最大でも3つまでの「title」と「url」を返すようにしました。この「title」と「url」は、今回の R&R データ固有の項目なので、必要に応じて変更してください。
RANKER の 自信度「ranker.confidence」が 0 のものが見つかったら、一つ目だけを返し打ち切ることにしました。このあたりはお好みで。

server.py
def handle_message(event):

    #query_string = event.message.text.encode("utf-8")
    query_string = urllib.parse.quote(event.message.text.encode("utf-8"), safe='')
    results = solrclient._send_request("GET", path="/fcselect?q=%s&ranker_id=%s&wt=json&fl=ranker.confidence,title,url" % (query_string, rnr_ranker_id))

    reply=""

    if len(json.loads(results)["response"]["docs"]) == 0:
       reply="見つかりませんでした。"

    else:
    #    for doc in json.loads(results)["response"]["docs"]:
        for i, doc in enumerate(json.loads(results)["response"]["docs"]):
            reply += doc["title"] + "\n" + doc["url"] + "\n\n"
            # max 3 docs
            if i >= 2:
                break       
            # only one for confidence == 0 if >= 3 items 
            if doc["ranker.confidence"] <= 0 :
                break

    line_bot_api.reply_message(
          event.reply_token,
          TextSendMessage(text=reply.rstrip() ))

動かない純正API

実は IBM Watson Retrieve and Rank には検索したうえでランクを返す Python 用のSearch and rank API があることになっています。

こんなコードで利用できることになっています。

import json
import pysolr
from watson_developer_cloud import RetrieveAndRankV1

retrieve_and_rank = RetrieveAndRankV1(
  username='YOUR_SERVICE_USERNAME',
  password='YOUR_SERVICE_PASSWORD')

solrclient = retrieve_and_rank.get_pysolr_client("sce94ccf57_1ee8_410b_8e67_3b2b3311a994", "example_collection")

solr_results = solrclient.search("wing")

ranking_results = retrieve_and_rank.rank("B2E325-rank-67", solr_results)

for ranking_result in ranking_results:
  print(json.dumps(ranking_result, indent=2))

しかし試すと、retrieve_and_rank.rank で「AttributeError: 'Results' object has no attribute 'read'」のエラーが発生します。

user01@ubuntu1604:~/test$ python3 rankApiTest.py
Traceback (most recent call last):
  File "rankApiTest.py", line 32, in <module>
    ranking_results = retrieve_and_rank.rank(rnr_ranker_id, solr_results)
  File "/home/user01/.local/lib/python3.5/site-packages/watson_developer_cloud/retrieve_and_rank_v1.py", line 134, in rank
    accept_json=True)
  File "/home/user01/.local/lib/python3.5/site-packages/watson_developer_cloud/watson_developer_cloud_service.py", line 302, in request
    **kwargs)
  File "/home/user01/.local/lib/python3.5/site-packages/requests/api.py", line 58, in request
    return session.request(method=method, url=url, **kwargs)
  File "/home/user01/.local/lib/python3.5/site-packages/requests/sessions.py", line 488, in request
    prep = self.prepare_request(req)
  File "/home/user01/.local/lib/python3.5/site-packages/requests/sessions.py", line 431, in prepare_request
    hooks=merge_hooks(request.hooks, self.hooks),
  File "/home/user01/.local/lib/python3.5/site-packages/requests/models.py", line 308, in prepare
    self.prepare_body(data, files, json)
  File "/home/user01/.local/lib/python3.5/site-packages/requests/models.py", line 496, in prepare_body
    (body, content_type) = self._encode_files(files, data)
  File "/home/user01/.local/lib/python3.5/site-packages/requests/models.py", line 159, in _encode_files
    fdata = fp.read()
AttributeError: 'Results' object has no attribute 'read'

検索すると IBM dW に回避方法 がありました。
今回は、記載があった、この回避策を利用しています。

results = pysolr._send_request("GET", path="/fcselect?q=%s&ranker_id=%s&wt=json" % (query_string, ranker_id))
 for doc in json.loads(results)["response"]["docs"]:
     print doc

前提モジュールが追加になったので requirements.txt は下記のように変更しました。

requirements.txt
Flask==0.12.2
cf-deployment-tracker==1.0.4
cloudant==2.4.0
line-bot-sdk==1.1.0
pysolr==3.6.0
watson-developer-cloud==0.26.0

Python2系だと正常に動作しなかったので runtime..txt に下記をしています。

python-3.5.3

デプロイ

Blumix にコードをデプロイします。

cf login -a https://api.ng.bluemix.net -u ユーザー名
cf push アプリケーション名

テスト

バーコードからLINEで友達になってアクセスすると動いているのわかります。

image.png

今回のコード

今回のコードを GitHUb で公開しました。
ryuta46 さんのコード からの fork になっています。

ryuta46 さん、本当にありがとうございました。