LLM Embeddingを活用した問い合わせBotを社内向けに導入して効率化した話

こんにちは。
FindyでMLエンジニアをしているyusukeshimpo(@WebY76755963)です。
今回はLLM Embeddingを活用した自動応答Botを開発&導入し、社内の問い合わせ業務を効率化することができたので、その取り組みを紹介します。

Botを開発することになった背景

弊社ではSlackを使用し、自社サービスに関する社内質問に回答するチャンネルを運用しています。
主にビジネスサイドからの技術的な疑問にエンジニアが答える仕組みです。

質問はテンプレートを使って送信され、エンジニアが回答しますが、このワークフローには次のような問題が発生しています。

  • 同じ質問が異なる人から届いてしまう
  • 質問の度にエンジニアの工数が発生してしまう

半期で70件以上の質問が発生し、1件につき1~1.5時間かかることもあります。
多くの質問は社内ドキュメントで解決可能ですが、検索がしづらく利用されていません。

質問に関連するドキュメントを渡すことで、エンジニアに質問する前に自己解決を促すことができます。
これがBot導入の背景です。

Bot導入のためのアプローチ

Botの要件について

Botを導入する理由は、質問対応でエンジニアの労力を減らすことです。
しかし、Kibela(社内の情報共有ツール)のQ&A集やSlackの問い合わせ履歴などの社内ドキュメントは全て構造化されておらず、検索性に乏しいという課題があります。

また、Botが全てを解決するのは難しく、ハルシネーション(AIが実際には存在しない情報を生成する現象)の問題もあります。
これらを踏まえ、クイックに実装できる要件を考えました

そこでBotの機能としては、

  • 質問の内容に関連しそうな社内ドキュメントへのリンクを送る
  • 上記で解決できたかを質問者にフィードバックしてもらう
  • 解決できない場合はあらためてエンジニアへ質問を投げる

というものにしました。

Botの構成

上記の要件を踏まえて、Botの仕組みは次のようにしました。

① データの取得
② データの構造化
③ 類似度計算とBotの回答
④ 質問者からのフィードバック

以下に、各処理について簡単に説明します。

①データの取得

最初にデータの取得をKibelaとSlackからAPIを使って行います。
過去きた質問と同じ質問に回答できることや社内の基本的なドキュメントで解決することを考えて、KibelaとSlackチャンネルのメッセージ履歴をBotで使用するデータにしました。

②データの構造化

今回の開発で最も重要なことの一つに、テキストデータの構造化処理があります。
フォーマットが異なる、または整備されていないテキストを扱うため、地道な作業で対応しました。

以下に、非構造の社内ドキュメントを構造化するプロセスを図示しています。

このプロセスを経て、最終的にKibelaとSlackから次の情報を持った構造化データを作成します。

  • ドキュメントタイプ(Kibela or Slack)
  • テキスト
  • ドキュメントのURL
  • テキストのEmbedding(質問との類似度計算用)

このように構造化することで、質問内容に合わせた関連ドキュメントを検索することができるようになります。

テキストデータ処理後、コサイン類似度計算のためにOpenAIのtext-embeddingを使用しました。
作成した構造化データはBot内で保持します。

③類似度計算とBotの回答

質問者からのインプットである質問文をEmbeddingしたものと、構造化データ内のEmbeddingデータとのコサイン類似度を計算し、類似したドキュメントのリンクを渡せるようにします。
Botの返信内容として、質問と類似度の高いドキュメントのURLを採用します。

④質問者からのフィードバック

最後に、質問者が想定している回答を、Botが応答できているかどうか確認するプロセスを設けました。
ここでは、望んでいた応答が得られたかどうかをフィードバックしてもらい、フィードバック別にその後のフローを分けています。
フィードバック後のフローについてはこの後の工夫した点のところで、詳細を説明します。

Bot導入時の工夫

Botの導入の際に工夫した点が以下2点あるので、それについても紹介をさせてください。

  • ワークフローの変更
  • データの準備

ワークフローの変更(フィードバックプロセスの追加)

ハルシネーション問題に対応する為、Botはテキストで返答するのではなく類似度のリンクを返信するようにしています。
これにより、質問者がリンク先のドキュメントを参考に自己解決を促すようにしました。

しかし、Botが提案したドキュメントで解決しきれない場合があった為、既存のワークフローを「自己解決できなかった場合に、エンジニアへ質問として受け付けられる」よう工夫しました。

上記工夫を取り入れた変更後のワークフローを以下図のように変更しました。
このように、質問者がBotの応答で自己解決できるかをチェックし、解決できない場合に「いいえ」を選択するとエンジニアへ質問が飛ぶ仕組みとしています。

Slack APIによるフィードバックの作成方法

回答が役に立ったかどうかのフィードバックを作成する際に、Slack APIによる以下実装の要領で、質問者に手軽に回答してもらえるボタンを用意する事ができました。

@app.action("feedback_yes")
def handle_feedback_yes(ack, body, say):
    ack()
    thread_ts = body["message"]["ts"]
    say("あなたは「はい」を押しました\nフィードバックをありがとうございます!他にお問い合わせがある場合はフォームからお願いいたします。", thread_ts=thread_ts)

@app.action("feedback_no")
def handle_feedback_no(ack, body, say):
    ack()
    thread_ts = body["message"]["ts"]
    user_group_id = "hoge" # SlackグループID(メンション先)
    say(f"あなたは「いいえ」を押しました\n<!subteam^{user_group_id}>\n上記のお問い合わせが来ているのでご対応お願いいたします!", thread_ts=thread_ts)

このように、フィードバックステップを簡易に組み込む事ができたおかげで、Botでどのくらい自己解決を促す事ができたかどうかを後述のように可視化できました。
導入時点で組み込んでおいた事で、評価用プロセスの追加実装や別で実装する等の手間を省く事もできました。

データの準備

先述の通り、今回の仕組みによるBotを実装するにあたり、応答精度を高めるには様々な形式で保存されているテキストデータのクリーニングや加工は重要なポイントの1つでした。
これまで、Botを構築し社内ドキュメントを検索するような用途で参照する事を想定していなかった為に、今回の用途に合わせてデータを丁寧に加工する事で、応答精度を高める事ができました。

今回紹介した仕組みで自動応答Botのデータとして応用する事を検討する場合は、ドキュメントが構造化されやすく整理されているかが意識されていると実装コストが削減できるようになると感じました。
例えば、Kibelaのような自由記述のテキストであれば、テンプレートを用意する等して形式を統一すると前処理が楽になります。

社内ドキュメントの整備には手間がかかるため敬遠されがちですが、このような用途に利用できることがわかれば、社内ドキュメントの整備に時間を割きやすくなるように思います。  

Bot導入の結果

社内の問い合わせオペレーションを改善できた

実際にBotを導入してみて工数の削減ができたのかを集計してみました。
導入後2ヶ月で、お問い合わせ対応にかかる所要時間を1/3減少する事に貢献している事がわかりました。

オペレーションの改善になったポイント

このように効率化できたポイントは、問い合わせ対応のワークフローを改善できたことです。
今まではどんな質問でも問い合わせとして送信していたものを、Botによる自己解決を促すプロセスを組み込んだワークフローにできた事で、本来であれば自己解決する事ができた質問を炙り出す事ができるようになったからだと振り返っています。
Botを単純に導入するだけでなく、今回の評価プロセスのように「どんな課題に対してどのようにBotを組み込み効率的な仕組みを作るか」を意識することが大切です。

Botの精度は今後社内ドキュメントを充実させることで改善をしていきますが、質問への対応時間削減は実現できたので導入した甲斐はありました。
Botで社内向けの問い合わせ対応の効率化をしたいという方はぜひ参考にしていただければと思います!

最後に

以上が社内お問い合わせの自動化Botの開発についてでした。
参考にしていただければ幸いです。

また、弊社では機械学習エンジニア・データエンジニアなど一緒に働いてくれるメンバーを募集しております。
興味がある方は↓からご応募してしていただければと思います。

herp.careers