LangChainとPinecone Vector Databaseを使用したカスタムQ&Aアプリケーションの構築

LangChainとPinecone Vector Databaseを使用したカスタムQ&Aアプリケーションの構築' 'Building a custom Q&A application using LangChain and Pinecone Vector Database

イントロダクション

大規模な言語モデルの登場は、現代における最もエキサイティングな技術の進展の一つです。これにより、人工知能の分野でさまざまな産業において実際の問題に対する解決策を提供する無限の可能性が開かれました。これらのモデルの魅力的な応用の一つは、個人や組織のデータソースから取得した情報をもとに、カスタムの質疑応答やチャットボットを開発することです。しかし、一般的なデータで訓練された大規模言語モデルは、常にエンドユーザーにとって特定の回答または有用な回答を提供するわけではありません。この問題を解決するために、LangChainなどのフレームワークを使用して、データに基づいた特定の回答を提供するカスタムチャットボットを開発することができます。この記事では、Streamlit Cloudでの展開を伴うカスタムQ&Aアプリケーションの構築方法について学びます。

学習目標

この記事に深く入る前に、主な学習目標を以下に概説しましょう:

  • カスタムの質疑応答のワークフロー全体を学び、各コンポーネントの役割を理解する
  • Q&Aアプリケーションの利点を知り、カスタムの言語モデルの微調整との比較を行う
  • Pineconeベクトルデータベースの基礎を学び、ベクトルの保存と取得を行う
  • OpenAIの言語モデル、LangChain、およびPineconeベクトルデータベースを使用してセマンティックサーチパイプラインを構築し、Streamlitアプリケーションを開発する

この記事はData Science Blogathonの一部として公開されました。

Q&Aアプリケーションの概要

出典:ScienceSoft

質疑応答または「データに基づくチャット」は、LLMsとLangChainの広範なユースケースです。LangChainは、ユースケースに対して見つけることができるすべてのデータソースをロードするための一連のコンポーネントを提供しています。LangChainは多くのデータソースとトランスフォーマーをサポートし、ベクトルデータベースに保存するために文字列のシリーズに変換します。データがデータベースに保存されたら、リトリーバーと呼ばれるコンポーネントを使用してデータベースにクエリを送信することができます。さらに、LLMsを使用することで、ドキュメントを大量に参照することなく、チャットボットのような正確な回答を得ることができます。

LangChainは以下のデータソースをサポートしています。画像で確認できるように、様々なデータソースに接続するための120以上の統合が可能です。

出典:LangChain Docs

Q&Aアプリケーションのワークフロー

LangChainがサポートするデータソースについて学びました。これにより、LangChainで利用可能なコンポーネントを使用して、質疑応答パイプラインを開発することができます。以下に、ドキュメントのロード、保存、リトリーバル、LLMによる出力生成に使用されるコンポーネントを示します。

  1. ドキュメントローダー:ユーザードキュメントをベクトル化および保存するためにロードするためのコンポーネント
  2. テキストスプリッター:これらは、ドキュメントを固定のチャンク長に変換して効率的に保存するドキュメントトランスフォーマーです
  3. ベクトル保存:入力テキストのベクトル埋め込みを保存するためのベクトルデータベースの統合
  4. ドキュメントリトリーバル:データベースからユーザークエリに基づいてテキストを取得するためのコンポーネント。類似性検索技術を使用して取得します
  5. モデル出力:クエリと取得したテキストの入力プロンプトから生成されたユーザークエリへの最終モデル出力

これは質疑応答パイプラインのハイレベルなワークフローであり、多くの現実世界の問題を解決することができます。LangChainコンポーネントについては詳しく説明していませんが、詳細を学びたい場合は、以前にAnalytics Vidhyaで公開された私の前の記事をチェックしてください(リンク:こちらをクリック)

Q&Aアプリ - ワークフローダイアグラム(著者提供の画像)

モデルの微調整に比べたカスタムQ&Aアプリケーションの利点

  1. 特定の文脈に基づく回答
  2. 新しい入力ドキュメントに適応可能
  3. モデルの微調整が不要で、モデルトレーニングのコストを節約
  4. 一般的な回答ではなく、より正確かつ具体的な回答

Pineconeベクトルデータベースとは何ですか?

Pineconeは、LLMパワードアプリケーションの構築に使用される人気のあるベクトルデータベースです。高性能なAIアプリケーションに対応するために、汎用性がありスケーラブルなものです。ユーザーはインフラストラクチャの手間を気にせず、完全に管理されたクラウドネイティブのベクトルデータベースを利用できます。

LLMベースのアプリケーションでは、大量の非構造化データが関与し、最大の精度で情報を取得するために洗練された長期記憶が必要とされます。生成型AIアプリケーションでは、ベクトル埋め込みの意味的検索に頼り、ユーザーの入力に基づいて適切なコンテキストを返します。

Pineconeは、そのようなアプリケーションに適しており、低レイテンシで多くのベクトルを格納しクエリを行うために最適化されており、使いやすいアプリケーションを構築することができます。質問応答アプリケーション用のPineconeベクトルデータベースを作成する方法を学びましょう。

# pinecone-clientのインストール
pip install pinecone-client

# pineconeをインポートし、APIキーと環境名で初期化する
import pinecone
pinecone.init(api_key="YOUR_API_KEY", environment="YOUR_ENVIRONMENT")

# 最初のインデックスを作成してベクトルの格納を開始する
pinecone.create_index("first_index", dimension=8, metric="cosine")

# サンプルデータ(5つの8次元ベクトル)をupsertする
index.upsert([
    ("A", [0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1]),
    ("B", [0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2]),
    ("C", [0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3]),
    ("D", [0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4]),
    ("E", [0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5])
])

# list_indexes()メソッドを使用してdbで利用可能なインデックスの数を取得する
pinecone.list_indexes()

[Output]>>> ['first_index']

上記のデモでは、プロジェクト環境でベクトルデータベースを初期化するためにpineconeクライアントをインストールしています。ベクトルデータベースが初期化されたら、必要な次元とメトリックを持つインデックスを作成し、ベクトル埋め込みをベクトルデータベースに挿入することができます。次のセクションでは、アプリケーションのためにPineconeとLangChainを使用して意味的検索パイプラインを構築します。

OpenAIとPineconeを使用した意味的検索パイプラインの構築

質問応答アプリケーションのワークフローには5つのステップがあることを学びました。このセクションでは、ドキュメントの読み込み、テキストの分割、ベクトルの保存、ドキュメントの検索の最初の4つのステップを実行します。

これらのステップをローカル環境またはGoogle Colabのようなクラウドベースのノートブック環境で実行するには、いくつかのライブラリをインストールし、それぞれのOpenAIとPineconeのAPIキーを取得するためにOpenAIとPineconeにアカウントを作成する必要があります。環境のセットアップから始めましょう:

必要なライブラリのインストール

# langchainとopenaiとその他の依存関係のインストール
!pip install --upgrade langchain openai -q
!pip install pillow==6.2.2
!pip install unstructured -q
!pip install unstructured[local-inference] -q
!pip install detectron2@git+https://github.com/facebookresearch/[email protected] /
                                            #egg=detectron2 -q
!apt-get install poppler-utils
!pip install pinecone-client -q
!pip install tiktoken -q

# openaiの環境設定
import os
os.environ["OPENAI_API_KEY"] = "YOUR-API-KEY"

# ライブラリのインポート
import os
import openai
import pinecone
from langchain.document_loaders import DirectoryLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.embeddings.openai import OpenAIEmbeddings
from langchain.vectorstores import Pinecone
from langchain.llms import OpenAI
from langchain.chains.question_answering import load_qa_chain

インストールセットアップの後、上記のコードスニペットに記載されているすべてのライブラリをインポートします。その後、以下の次の手順に従います:

ドキュメントの読み込み

このステップでは、AIプロジェクトパイプラインの出発点としてディレクトリからドキュメントを読み込みます。ディレクトリには2つのドキュメントがあり、これらをプロジェクト環境に読み込みます。

# content/dataディレクトリからドキュメントを読み込む
directory = '/content/data'

# langchain関数を使用してドキュメントを読み込むload_docs関数
def load_docs(directory):
  loader = DirectoryLoader(directory)
  documents = loader.load()
  return documents

documents = load_docs(directory)
len(documents)
[出力]>>> 5

テキストデータの分割

テキスト埋め込みとLLMは、各ドキュメントが固定の長さを持つ場合により良いパフォーマンスを発揮します。したがって、テキストを均等なチャンクの長さに分割することは、LLMのユースケースでは必要です。テキストドキュメントと同じサイズに変換するために「RecursiveCharacterTextSplitter」を使用してドキュメントを変換します。

# recursive text splitterを使用してドキュメントを分割する
def split_docs(documents, chunk_size=200, chunk_overlap=20):
  text_splitter = RecursiveCharacterTextSplitter(chunk_size=chunk_size, chunk_overlap=chunk_overlap)
  docs = text_splitter.split_documents(documents)
  return docs

# ドキュメントを分割する
docs = split_docs(documents)
print(len(docs))
[出力]>>>12

データをベクトルストレージに保存する

ドキュメントが分割されたら、OpenAI埋め込みを使用してその埋め込みをベクトルデータベースに保存します。

# ランダムな単語に対する埋め込みの例
embeddings = OpenAIEmbeddings()

# pinecondbの初期化
pinecone.init(
    api_key="YOUR-API-KEY",
    environment="YOUR-ENV"
)

# インデックス名の定義
index_name = "langchain-project"

# データと埋め込みをpineconeインデックスに保存する
index = Pinecone.from_documents(docs, embeddings, index_name=index_name)

ベクトルデータベースからデータを取得する

このステージでは、ベクトルデータベースからセマンティック検索を使用してドキュメントを取得します。ベクトルは「langchain-project」というインデックスに格納されており、以下のようにクエリを実行すると、データベースから最も類似したドキュメントが取得できます。

# データベースへのクエリの例
query = "ペット動物の異なる種類は何ですか?"

# 類似性検索を行い、結果をresult変数に保存する
result = index.similarity_search(
    query,  # 検索クエリ
    k=3  # 3つの最も関連のあるドキュメントを返す
)
-
--------------------------------[出力]--------------------------------------
result
[Document(page_content='ハムスターやモルモット、ウサギなどの小型哺乳類は、お手入れが簡単なためよく選ばれます。鳥は美しさと歌声があり、亀やトカゲなどの爬虫類も興味深いペットになります。',
metadata={'source': '/content/data/Different Types of Pet Animals.txt'}),
 Document(page_content='ペット動物は、異なるライフスタイルと家庭環境に合わせたさまざまな形状とサイズで存在します。犬と猫が最も一般的で、その仲間意識と独特な個性で知られています。小型', metadata={'source': '/content/data/Different Types of Pet Animals.txt'}),
 Document(page_content='興味深いペットです。魚も落ち着いた存在感があり、素晴らしいペットになることがあります。',
metadata={'source': '/content/data/Different Types of Pet Animals.txt'})]

上記のコードスニペットに示すように、ベクトルストアからセマンティック検索に基づいてドキュメントを取得できます。セマンティック検索アプリケーションについて詳しく学びたい場合は、このトピックに関する私の以前の記事を読むことを強くお勧めします(リンク:こちらをクリック)

Streamlitを使用したカスタム質問応答アプリケーション

質問応答アプリケーションの最終ステージでは、すべてのワークフローコンポーネントを統合して、ユーザーがWebベースの記事、PDF、CSVなどのさまざまなデータソースを入力してチャットできるカスタムQ&Aアプリケーションを構築します。これにより、日常の活動で生産性が向上します。GitHubリポジトリを作成し、次のファイルを追加する必要があります。

リポジトリの構造

これらのプロジェクトファイルを追加する

  1. main.py — streamlitのフロントエンドコードを含むPythonファイル
  2. qanda.py — ユーザーのクエリに対して回答を返すためのプロンプトデザインとモデルの出力関数
  3. utils.py — 入力ドキュメントの読み込みと分割を行うためのユーティリティ関数
  4. vector_search.py — テキスト埋め込みとベクトル保存関数
  5. requirements.txt — streamlit public cloudでアプリケーションを実行するためのプロジェクト依存関係

このプロジェクトのデモンストレーションでは、2つのタイプのデータソースをサポートしています:

  1. Web URLベースのテキストデータ
  2. オンラインPDFファイル

これら2つのタイプには、さまざまなテキストデータが含まれており、多くのユースケースで頻繁に使用されます。アプリのユーザーインターフェースを理解するために、以下にmain.pyのPythonコードを表示できます。

# 必要なライブラリをインポート
import streamlit as st
import openai
import qanda
from vector_search import *
from utils import *
from io  import StringIO

# OpenAI APIキーを入力
api_key = st.sidebar.text_input("OpenAI APIキーを入力してください:", type='password')
# OpenAIキーを設定
openai.api_key = str(api_key)

# アプリのヘッダー
_ , col2,_ = st.columns([1,7,1])
with col2:
    col2 = st.header("Simplchat: データとのチャット")
    url = False
    query = False
    pdf = False
    data = False
    # ユーザーのニーズに基づいてオプションを選択
    options = st.selectbox("データソースのタイプを選択",
                            options=['Web URL','PDF','既存のデータソース'])
    # データソースのオプションに基づいてクエリを入力
    if options == 'Web URL':
        url = st.text_input("データソースのURLを入力してください")
        query = st.text_input("クエリを入力してください")
        button = st.button("送信")
    elif options == 'PDF':
        pdf = st.text_input("PDFリンクを入力してください") 
        query = st.text_input("クエリを入力してください")
        button = st.button("送信")
    elif options == '既存のデータソース':
        data= True
        query = st.text_input("クエリを入力してください")
        button = st.button("送信") 

# クエリとデータソースに基づいて出力を取得するコードを記述   
if button and url:
    with st.spinner("データベースを更新中..."):
        corpusData = scrape_text(url)
        encodeaddData(corpusData,url=url,pdf=False)
        st.success("データベースが更新されました")
    with st.spinner("回答を検索中..."):
        title, res = find_k_best_match(query,2)
        context = "\n\n".join(res)
        st.expander("コンテキスト").write(context)
        prompt = qanda.prompt(context,query)
        answer = qanda.get_answer(prompt)
        st.success("回答:" + answer)

# クエリとデータソースに基づいて出力を取得するコードを記述
if button and pdf:
    with st.spinner("データベースを更新中..."):
        corpusData = pdf_text(pdf=pdf)
        encodeaddData(corpusData,pdf=pdf,url=False)
        st.success("データベースが更新されました")
    with st.spinner("回答を検索中..."):
        title, res = find_k_best_match(query,2)
        context = "\n\n".join(res)
        st.expander("コンテキスト").write(context)
        prompt = qanda.prompt(context,query)
        answer = qanda.get_answer(prompt)
        st.success("回答:" + answer)
        
if button and data:
    with st.spinner("回答を検索中..."):
        title, res = find_k_best_match(query,2)
        context = "\n\n".join(res)
        st.expander("コンテキスト").write(context)
        prompt = qanda.prompt(context,query)
        answer = qanda.get_answer(prompt)
        st.success("回答:" + answer)
        
        
# データベースからベクトルを削除
st.expander("データベースからインデックスを削除")
button1 = st.button("現在のベクトルを削除")
if button1 == True:
    index.delete(deleteAll='true')

他のコードファイルをチェックするには、プロジェクトのGitHubリポジトリを訪問してください。 (リンク: こちらをクリック)

Streamlit CloudにおけるQ&Aアプリのデプロイ

アプリのUI

Streamlitは、アプリケーションを無償でホストするためのコミュニティクラウドを提供しています。さらに、Streamlitは自動化されたCI/CDパイプラインの機能により、使いやすさが向上しています。アプリを構築するためのStreamlitについて詳しく学ぶには、私の以前の記事をAnalytics Vidyaでご覧ください(リンク:こちらをクリック)

カスタムQ&Aアプリケーションの業界利用事例

この分野で新たな革新的なユースケースが出現するにつれて、多くの業界でカスタムの質問応答アプリケーションが採用されています。以下にそのようなユースケースを見てみましょう。

顧客サポート支援

LLMの台頭により、顧客サポートの革命が始まりました。Eコマース、通信、または金融業界であれ、企業のドキュメントを基に開発された顧客サービスボットは、顧客がより迅速かつ適切な意思決定を行うのに役立ち、収益の増加につながります。

医療業界

情報は患者が特定の疾患の適切な治療を受けるために重要です。医療企業は、実際の人物を必要とせずに、自然言語で医療情報、薬物情報、症状の説明、治療ガイドラインを提供するインタラクティブなチャットボットを開発することができます。

弁護士は、法的情報と文書の大量の取り扱いを行い、裁判事案を解決します。このような大量のデータを使用して開発されたカスタムのLLMアプリケーションは、弁護士がより効率的になり、より速く事件を解決するのに役立ちます。

テクノロジー業界

Q&Aアプリケーションの最も大きなゲームチェンジングなユースケースは、プログラムのサポートです。テクノロジーカンパニーは、内部のコードベース上にそうしたアプリを構築することで、プログラマーが問題解決やコードの構文理解、エラーのデバッグ、特定の機能の実装をサポートできます。

政府および公共サービス

政府の政策や制度には多くの情報が含まれており、多くの人々を圧倒する可能性があります。政府のサービス向けにカスタムアプリケーションを開発することで、市民は政府プログラムや規制に関する情報を得ることができます。また、政府のフォームや申請書の正確な記入にも役立ちます。

結論

まとめると、LangChainとPineconeベクターデータベースを使用して、カスタムの質問応答アプリケーションを構築する可能性について探求しました。このブログでは、質問応答アプリケーションの概要からPineconeベクターデータベースの機能理解まで、基本的な概念を学びました。OpenAIの意味検索パイプラインのパワーをPineconeの効率的なインデックスと検索システムと組み合わせることで、Streamlitを使用して堅牢で正確な質問応答ソリューションを作成する可能性を活用しました。記事のキーポイントを以下にまとめます:

キーポイント

  • 大規模言語モデル(LLM)はAIを革新し、多様なアプリケーションを可能にしました。個人や組織のデータを使用してチャットボットをカスタマイズすることは、強力なアプローチです。
  • 一般的なLLMは言語の広範な理解を提供しますが、チューニングされたパーソナライズされたLLMと比較して、カスタムの質問応答アプリケーションは柔軟性とコスト効率の面で明確な利点があります。
  • Pineconeベクターデータベース、OpenAIのLLM、およびLangChainを組み合わせることで、意味検索パイプラインを開発し、Streamlitのようなクラウドベースのプラットフォームに展開する方法を学びました。

よくある質問

この記事に表示されているメディアはAnalytics Vidhyaの所有ではなく、著者の裁量により使用されています。

We will continue to update VoAGI; if you have any questions or suggestions, please contact us!

Share:

Was this article helpful?

93 out of 132 found this helpful

Discover more