こんにちは!株式会社 ABEJA で ABEJA Platform 開発を行っている坂井(GitHub : @Yagami360)です。
LangChain を使用すれば、RAG [Retrieval Augment Generation] を使用した LLM アプリケーションを簡単に作成できるので便利ですよね。
今回 LangChain での RAG を使用して、LLM が学習に使用していない特定ドメインでの用語を応答する Slack ボットをさくっと作ってみたので共有します。
本コード一式は、以下の GitHub レポジトリに保管しています。
使い方
事前準備として{用語集スプレッドシートの作成・Slack アプリの初期設定・各種 GCP リソースへのデプロイ}が必要になります。一度初期設定すれば、以降は Slack アプリ上から /glossary-chat-bot ${質問文}
コマンドで使用することが可能です。
詳細は、GitHub レポジトリの README.md を参照してください。
コード解説
LangChain での RAG の実装は非常に簡単であるし世の中沢山の記事が出回っているので、ここでは本アプリケーションのアーキテクチャの全体像と RAG の仕組みの概要だけ説明します。
アーキテクチャ
まずは、軽くアーキテクチャの全体像から説明します。
RAG を使用した LLM アプリケーションを Flask + Slack Bolt での API として docker コンテナ化し、Cloud Run 上で動作させています。
- Cloud Run にした理由は、docker が使える点とサーバーレスでコストを抑えるためです。一方で、コールドスタートで起動した場合にレスポンス時間が遅くなるデメリットはあります
API のエンドポイント
/slack/events
は、ユーザーが Slack アプリ上から/glossary-chat-bot ${質問文}
でメッセージ送信時に呼び出されるエンドポイントで、RAG を使用して用語集スプレッドシートに定義している特定ドメインでの用語の意味を Slack で応答します。API のエンドポイント
/update_db
は、Cloud Sheduler により1時間に1回呼び出されるエンドポイントで、以下の用語集スプレッドシートから特徴量データベースへの更新処理を行っています。用語集スプレッドシートを csv に変換
csv データのテキストデータを各行で分割し、それぞれ埋め込みモデルで埋め込みベクトルに変換
上記分割テキストの埋め込みベクトルを特徴量データベース(Chroma or Faiss)に保存
RAG で使用する用語集のデータは、Google スプレッドシートで管理しています。BigQuey などのデータ基盤に保存するのが一般的かもしれませんが、スクリプトシートで管理することで誰でも用語の追加修正を簡単に行えるようにしています。また回答文に続く Slack メッセージとして、用語集スプレッドシートへの URL を返しユーザーが直接スプレッドシートにアクセスできるようにすることで、簡易的なヒューマンインザループでの品質改善を行えるようにしています
RAG の仕組み
本アプリケーションでは、RAG [Retrieval Augment Generation] と呼ばれる手法を使用していますが 、その RAG の仕組みだけ軽く説明しておきます。
RAG は、LLM が学習していない外部データセット(今回の例では、用語集スプレッドシート)に対して、外部データセットと入力文の埋め込みベクトルでの類似度検索&取得(リトライブ)により、LLM をファインチューニングすることなしにうまく応答できるようにする手法です。より詳細には、以下のような処理を行います。
- 外部データセット(今回のケースでは用語集スプレッドシート)をテキスト分割し、それぞれ埋め込みモデル(
text-embedding-ada-002
など)で埋め込みベクトルに変換する - ユーザーが入力したテキストも同じく埋め込みモデル(
text-embedding-ada-002
など)で埋め込みベクトルに変換する - ユーザーが入力した文章の埋め込みベクトルと、外部データセットの分割した各テキストの埋め込みベクトルの類似度をリトライバーで検索&取得(リトライブ)する
類似度の高い外部データセットの分割テキストデータ(リトライブ結果)を参考情報
{context}
とし、ユーザーからの入力文を{question}
として、例えば以下のようなプロンプトテンプレートからプロンプトを作成するあなたは質問応答のアシスタントです。[参考]部分の情報を使って[質問]に日本語で回答してください。答えがわからない場合は、わかりませんと答えましょう。 [質問] {question} [参考] {context}
上記プロンプトを入力文として、LLM(
gpt-3.5-turbo
など) 推論を行う。参考情報context
としてトライブ結果(入力文に対して類似度の高い外部データセットのテキストデータ)が渡されているので、LLM としては、類似度の高いテキストデータからユーザーの入力文に対して応答するという簡単化されたタスクになり、LLM が学習に使用していない外部データに対してもうまく応答文を出力できるようになる。
単語やテキストの埋め込みや埋め込みベクトルの概念については、以下の記事を参照してください。
LLM をファインチューニングする方法でも外部データセットに対してうまく応答できるようにすることができますが、一般的に多くの外部データセット数が必要になることや、巨大なディープラーニングモデルである LLM を再学習する際の学習時間とサーバー費用(特にGPUコスト)が莫大になる問題があるので、この RAG による手法はそういう意味でも非常にコスパがよく現実的な方法になります。
RAG は、LangChain の Retrievers を使用すれば簡単に実装することができます。*1
具体的な実装方法は、インターネットに沢山の記事が見つかるのでそちらを見るか、本記事の GitHub レポジトリのコードをみてもらえればと思います。
ヒューマンインザループによる継続的品質改善
LLM アプリケーションに限らずですが、一般的に機械学習アプリケーションにおいては、人手によるアノテーションを含んだヒューマンインザループによる継続的品質改善の仕組みが非常に有益になります。特に生成 AI においては、従来の機械学習モデルとは違って多種多様な入力パターンが存在するために、それら入力パターンに対して十分なレベルでのモデルの汎化性能を最初の開発フェイズで実現するのは容易ではないと思います。そのため運用フェイズにおけるヒューマンインザループを含んだ継続的品質改善の仕組みはより重要だと思ってます。
本アプリケーションでは、誰でも編集が容易なスプレッドシートで用語集を管理し Slack 応答メッセージにスプレッドシートのリンクを送ることで、以下の図のようなヒューマンインザループを含んだ継続的品質改善の仕組みを超簡易的に実現しています。
まとめ
RAG を使用すれば、LLM をファインチューニングすることなしに、LLM が学習に使用していない外部データセット(例えば、社内用語など)に対してもうまく応答できるようになります。
LangChain を使用すれば実装も非常に簡単なので、ちょっとした LLM アプリケーションもさくっと作ってみれるかと思います!
We Are Hiring!
株式会社ABEJAでは共に働く仲間を募集しています!
機械学習モデル開発や機械学習プロダクトに関わるフロントエンド開発バックエンド開発に興味あるエンジニアの方々!こちらの採用ページから是非ご応募くださいませ!
*1:LangChain 以外にも RAG を簡単に実装できるライブラリは続々誕生しています。