SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

Elasticsearchと対話Botによる対話型の検索システム

SlackとElasticsearchを連携し、使いやすい検索システムを作成する

Elasticsearchと対話Botによる対話型の検索システム 第5回

  • X ポスト
  • このエントリーをはてなブックマークに追加

検索処理部分との連携モジュールについて

 前回の記事と異なる部分は以下の通りです。

  • 応答の判断部分
  • Elasticsearchによる検索処理部分
  • Slackへの投稿部分

応答の判断部分

 応答は質問かそうでないか判断して返すようにしています。判断基準は「?」(全角)もしくは「?」(半角)を含んでいるかどうか、です。質問の場合はElasticsearchによる検索処理を行います。

リスト1 Code/app.py
if len(self.data) >= 1 and "text" in self.data[0]:
    input_text = self.data[0]["text"]
    if "search_bot:" in input_text:
        if "?" in input_text or "?" in input_text:
            word = self.__search_word(input_text)
            self.__slack_call(word)
        else:
            word = self.message
            self.__slack_call(word)

検索処理部分

  1. 改行や判定のためのトリガーワードなど、検索に不要な単語を削除しています
  2. 検索のモジュールには1.の処理を行った後の、不要な単語を削除した文章を渡しています
  3. 検索モジュールから返却された結果があれば必要な部分のみを抽出し、返却されなければ「No match」という単語を返しています

 環境によってはソースコード中に「¥」記号が表示される場合がありますが、実際にはバックスラッシュです。

リスト2 Code/app.py
replace_input = re.sub("search_bot:|\?|\?", "", input_text.strip())
self.elastic_search.search_data(replace_input)
if len(self.elastic_search.search_result) > 0:
    hyp_batch = self.elastic_search.search_result[0]
    word = hyp_batch["title"] + "\n" + hyp_batch["abstract"] + "\n" + hyp_batch["url"]
else:
    word = "No match"
return word

Slackへの投稿部分

 前回の記事と同様の内容なので詳細の説明は除きますが、wordに検索された単語が入っています。

リスト3 Code/app.py
print(self.slack_channel.api_call("chat.postMessage", username=self.user_name, channel=self.chan, text=word, \
                                  icon_url=self.icon_url))

検索モジュールについて

  1. 初期設定
  2. 検索処理
  3. 検索クエリ設定

 初期設定では以下のように「--link」で連携したコンテナの名前を指定します。この場合はElasticsearchのポート番号である9200番を指定しています。

リスト4 Code/get_answer.py
self.es = Elasticsearch(['elasticsearch_dialogue'], port=9200,)
self.doc = {}
self.elastic_index = "_all"
self.search_result = []

 検索結果を初期化して検索クエリを設定し、そのクエリを使用して検索後、ヒットした結果を配列に追加しています。

リスト5 Code/get_answer.py
self.search_result = []
self.setting_search_query(search_key_word)
self.res = self.es.search(index=self.elastic_index, body=self.query)
for hit in self.res['hits']['hits']:
    self.search_result.append(hit["_source"])

 その下で検索用のクエリを作成しています。検索スコア設定の際に「boost」で値の重み付けを大きくしています。検索スコアの設定については初回の記事をご覧ください。今回のクエリの場合、「title」の方が検索における重要性を増すように作っています。

リスト6 Code/get_answer.py
self.query = {
    "query": {
      "bool": {
        "should": [
          {
            "match": {
              "title": {
                "query": "\"" + search_key_word + "\"",
                "boost": 10
              }
            }
          },
          {
            "match": {
              "abstract": "\"" + search_key_word + "\""
            }
          }
        ]
      }
    }
}

 では、動作確認を行います。前回の記事で設定した、Slack上でBotが反応するチャネルに対して「捕鯨?」と質問文を入力してみましょう。先ほどのスクリプトでSlackのコンテナに入っているため、以下のコマンドを打つと質問応答に対応するBotが立ち上がります。

python app.py
図2 動作確認
図2 動作確認

 「捕鯨」と調べたかったのですが結果として「反捕鯨」が出ています。なぜこのような結果になったのかというと、「捕鯨」と登録されたデータの「abstract」に「捕鯨」に関係している要素が入っていないためです。そこで、「title」だけで検索してみます。「get_answer.py」の中にコメントアウトしている部分があるので、そのコメントアウトを外し、先ほどの検索クエリはコメントアウトしてください。

リスト7 Code/get_answer.py
self.query = {
    "query": {
      "bool": {
        "should": [
          {
            "match": {
              "title": {
                "query": "\"" + search_key_word + "\""
              }
            }
          }]
      }
    }
}

 再度、以下のコマンドを実行してください。

python app.py

 この場合、「title」のみにフォーカスして「捕鯨」を検索しているため、欲しい結果を取得できます。本文も考慮して検索するほうが検索の質は向上しそうですが、データの品質によってはそうでない場合もあるため、今回のように確認を行いながら検索することが重要になってきます。

図3 動作比較
図3 動作比較

 このように検索クエリを変えるだけでも多様な検索を行うことができます。さらに、この変更は検索用のコンテナに対する影響がないので、ここでも疎結合な環境による効果を体感できます。

 なお、現状の実装では末尾ではなく文字列内に半角の「?」と全角の「?」が含まれた場合は反応するようになっているため、注意が必要です。

最後に

 これでUI部分と検索部分を切り離した構成の、Elasticsearchと対話Botによる対話型の検索システムが作成できました。今回の構成の場合、単純な応答からルールを作成した応答への変更などは既存の検索部分を修正せずに行うことができます。また、UI部分もSlackからFacebook、LINEなどの多様なインターフェースに変更可能で、今回のようにDockerを使用した構成の魅力になっています。本記事の内容を応用していただけると幸いです。

参考

この記事は参考になりましたか?

  • X ポスト
  • このエントリーをはてなブックマークに追加
Elasticsearchと対話Botによる対話型の検索システム連載記事一覧

もっと読む

この記事の著者

WINGSプロジェクト 大串 正矢(オオグシ マサヤ)

WINGSプロジェクトについて>有限会社 WINGSプロジェクトが運営する、テクニカル執筆コミュニティ(代表 山田祥寛)。主にWeb開発分野の書籍/記事執筆、翻訳、講演等を幅広く手がける。2018年11月時点での登録メンバは55名で、現在も執筆メンバを募集中。興味のある方は、どしどし応募頂きたい。著書記事多数。 RSS X: @WingsPro_info(公式)、@WingsPro_info/wings(メンバーリスト) Facebook

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

山田 祥寛(ヤマダ ヨシヒロ)

静岡県榛原町生まれ。一橋大学経済学部卒業後、NECにてシステム企画業務に携わるが、2003年4月に念願かなってフリーライターに転身。Microsoft MVP for Visual Studio and Development Technologies。執筆コミュニティ「WINGSプロジェクト」代表。主な著書に「独習シリーズ(Java・C#・Python・PHP・Ruby・JSP&サーブレットなど)」「速習シリーズ(ASP.NET Core・Vue.js・React・TypeScript・ECMAScript、Laravelなど)」「改訂3版JavaScript本格入門」「これからはじめるReact実践入門」「はじめてのAndroidアプリ開発 Kotlin編 」他、著書多数

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/10005 2017/03/13 14:00

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング