⛓️

Ruby版LangchainのLangchainrbをさわってみる(RAG編)

2024/06/30に公開

はじめに

https://github.com/patterns-ai-core/langchainrb
本記事では、Ruby 版 Langchain の Langchainrb を紹介します。
以前、基本的なチャット操作を試してみて以下の記事にまとめました。
興味があればそちらもぜひ読んでみてください。
https://zenn.dev/keisuke90/articles/a9f8bfcbae952c
今回はタイトルにある通り Langchainrb の RAG 機能に関してまとめたいと思います。

そもそも RAG とは?

RAG とは Retrieval Augmented Generation(検索拡張生成)の頭文字を取った言葉です。
LLM は学習に用いられたデータの中からしか回答することができず、最新の情報や社内データに関する情報は持ち合わせていません。
また LLM には正しくない情報を出力してしまうハルシネーションという現象もあります。
そういった課題を解決するために RAG という技術が有効で、最新のデータを回答に含めたり、出力に使われた背景の情報を確認することができるようになります。
RAG より詳しい説明は割愛しますが、ざっくりと以下のような手順で RAG は実現します。

  1. 質問文を投げる
  2. 質問分から DB のクエリを生成
  3. 質問分に関連するデータを取得
  4. 元の質問文と、③ で取得したデータを合わせて LLM にリクエストする
  5. LLM から独自データを含んだレスポンスを得る

データベースにはベクトルデータベースを利用することが一般的で、Langchainrb は各種ベクトル DB をラップした API を提供しており、簡単に扱うことができます。
記事執筆時点では Langchainrb は上記 8 種類の DB をサポートしています。

準備

ここでは Pinecone と OpenAI の API を使って RAG を試してみたいと思います。
https://www.pinecone.io/
※Pinecone のセットアップに関しては割愛します。
Pinecone のライブラリをインストールします。

gem "pinecone", "~> 0.1.6"

LLM とベクトル DB のインスタンスを初期化します。

llm = Langchain::LLM::OpenAI.new(
        api_key: ENV['OPENAI_API_KEY'],
        default_options: {
          chat_completion_model_name: 'gpt-3.5-turbo',
        }
      )

client = Langchain::Vectorsearch::Pinecone.new(
  environment: ENV['PINECONE_ENVIRONMENT'],
  api_key: ENV['PINECONE_API_KEY'],
  index_name: ENV['PINECONE_INDEX_NAME'],
  llm: llm
)

index 未作成の場合は、index を作成します。
このとき index 名は初期化時に引数に渡した index_name になります。

client.create_default_schema

ベクトル DB の操作

ベクトル DB のクライアントは大きく分けて以下の三つの操作をすることができます。

  1. データの追加
  2. データの検索
  3. 検索したデータを元に回答を出力する(RAG)

データの追加

データの追加はテキストを与えるか、ファイルから読み込むことも可能です。
データは配列で渡すことができるので、まとめてデータ追加を行うことができます。

# テキストからindexを追加する
client.add_texts(
	texts: [
		"テキストを入力",
		"テキストを入力"
	]
)

# ファイルからindexを追加する
my_pdf = Langchain.root.join("path/to/my.pdf")
my_text = Langchain.root.join("path/to/my.txt")
my_docx = Langchain.root.join("path/to/my.docx")
client.add_data(paths: [my_pdf, mytext, my_docx])

データの検索

単純にデータを検索するには similarity_search というメソッドを使います

client.similarity_search(query: {検索ワード}, k: {出力の個数})

試しに任天堂 Switch のよくある質問を追加してみて、検索を行ってみます。

https://support-jp.nintendo.com/app/home

# サンプルコード
query = 'switchの電源が点かない'
response = client.similarity_search(query: query, k: 5)

res.each_with_index do |item, idx|
  puts "[No.#{idx+1}]=========================== "
  puts item.dig("metadata", "content")
end
# 出力
[No.1]===========================
【Switch】本体の電源がONになりません。どうすればよいですか?

本体がフリーズしている可能性があります。
電源ボタンを12秒以上押し続けて、本体の電源を完全にOFFにしてください。そのあと、あらためて電源をONにして、改善しているかご確認ください。
※上記操作により改善した場合は、画面が真っ暗な状態でフリーズしていた可能性があります。
バッテリー残量がなくなっている可能性があります。
本体を30分以上充電してください(フル充電は約3時間)。そのあと、あらためて電源をONにして、改善しているかご確認ください。
※充電方法について、詳しくはこちらをご覧ください。:
[No.2]===========================
【Switch】再起動の方法を知りたい。

次の手順で、Nintendo Switchを再起動することができます。
再起動の手順
本体の上にある「電源ボタン」を 3秒 押し続けてください。電源メニューが表示されるので「電源オプション」を選択してください。
「再起動」を選択してください。
[No.3]===========================
【Switch】エラーコード「2153-1540」が表示されました。どうすればよいですか?

本体のシステムバージョンを最新にすることで改善する可能性があります。本体更新をお試しください。
[No.4]===========================
【Switch】ユーザーと連携しているニンテンドーアカウントを確認したい。

Nintendo Switchのユーザーと、ニンテンドーアカウントは連携することができます(ユーザーとは?)。
Nintendo Switchの各ユーザーが、どのニンテンドーアカウントと連携しているかは「マイページ」で確認することができます。
[No.5]===========================
【ニンテンドーアカウント】登録情報を変更・確認したい。

ニンテンドーアカウントの登録情報は、パソコンまたはスマートフォンを使って変更・確認することができます。変更・確認できる情報と、操作手順は次のとおりです。

データを 5 個しか追加していないので下位の結果は関係ないものですが、一番最初に関連のある Q&A を出力することができました。

検索したデータを元に回答を生成する(RAG)

次は DB から取得したデータをプロンプトに含めて RAG として利用します。
次も上の例と同様に任天堂 Switch の質問をデータとして追加した状態で試します。
まずは RAG を使わずに素の LLM に質問してみます。

# サンプルコード
message = {role: 'user', content: 'switchの電源が点かない'}
puts llm.chat(messages: [message]).completion

# 出力
まず、以下の手順を試してみてください。

1. コンセントや延長コードが正しく接続されているか確認してください。
2. スイッチの電源ボタンを確認し、正しく押されているか確認してください。
3. スイッチの電源コードが正しく接続されているか確認してください。
4. スイッチの電源コードが破損していないか確認してください。
5. 他の電化製品を同じコンセントに接続して、電源が入るか確認してください。

これらの手順を試しても電源が点かない場合は、故障している可能性がありますので、修理や交換を検討してください。

それらしい回答は返ってきていますが、当然ながら任天堂 switch としては認識していないようです。
RAG として回答を得るためには ask メソッドを使います。

# サンプルコード
query = 'switchの電源が点かない'
res = client.ask(question: query)
puts res.raw_response.dig("choices", 0, "message", "content")

#出力
本体がフリーズしている可能性があります。電源ボタンを12秒以上押し続けて、本体の電源を完全にOFFにしてください。
その後、再度電源をONにして改善しているか確認してください。
バッテリー残量がなくなっている可能性も考えられるので、本体を30分以上充電してから再度電源をONにして確認してください。

ちゃんと Q&A にある内容を元に回答してくれました 🎉

さいごに

最後まで読んでいただきありがとうございます。
RAG を実際に利用する場合は、より質問の意図にあったデータを取得するために、どのようにデータをチャンク化するのかなど考慮すべき部分は多いようです。
ですが特に難しいことを考えずに、RAG の基本的な動作を試すことができるので、とりあえず RAG を動かしてみる第一歩としてはとても便利です。
次回は Langchainrb の Assistant 機能をさわってまとめてみたいと思います。

Discussion