PythonでLangChainとMomento Vector Indexを使ってQ&AエンジンのRAGパイプラインを構築する方法

ベクトル・インデックスとLangChainを使えば、質問応答システムを作るのは簡単です。

注:このチュートリアルでは、PythonとTypeScript(Node.js)の両方のコード例を紹介します。これはPythonのチュートリアルです。TypeScript版はこちら!

このブログポストでは、Momento Vector IndexとLangChainを応用して、ドキュメントを使って質問に対する人間が読める回答を生成できるRAGパイプラインを作成する方法を探ります。まず、LangChainを使ってドキュメントを読み込んで管理する方法を説明します。次に、ドキュメントをチャンクに分割し、Momento Vector Indexでインデックスを作成します。最後に、より自然な回答を生成するために、GPT 3.5とインデックスを接続します。このガイドでは、ベクターインデックスの構築と活用に必要な技術的ステップを提供し、ベクター検索とデータ検索の機能を強調します。

‍一般的なベクトルインデックスについては、Momentoの開発ドキュメントLangChainチャットボットに関するビデオをご覧ください。

前提条件

サンプルに従うには、PythonPoetryをインストールする必要があります。PythonとPoetryをインストールしたら、Poetryのサイトの指示に従って新しいプロジェクトを作成します。それが設定できたら、Momento Vector IndexとLangChainを始めるために必要な依存関係を以下に示します:

poetry add momento langchain langchain-openai tiktoken wikipedia

使用するインポートは以下の通り:

from langchain_community.document_loaders import WikipediaLoader
from langchain.text_splitter import TokenTextSplitter
from langchain_community.vectorstores import MomentoVectorIndex
from langchain_openai import OpenAIEmbeddings
from langchain.chains import RetrievalQA
from langchain_openai import ChatOpenAI

APIキー

Momento Vector Indexを使用するには、スーパーユーザーAPIキーが必要です。生成方法は開発者向けドキュメントを参照してください。

OpenAI API Keyも必要で、これはOpenAIから入手できます。

これらのAPIキーをMOMENTO_API_KEYOPENAI_API_KEY環境変数に書き込めば、LangChainとMomentoが自動的にそれらを見つけてくれます。で実行時に読み込むこともできます:

os.environ["MOMENTO_API_KEY"] = getpass.getpass("Momento API Key:")
os.environ["OPENAI_API_KEY"] = getpass.getpass("OpenAI API Key:")

データのロード

この例では、Wikipediaのニンジンのページをインデックス化します。Pythonでは、LangChainが便利なWikipediaローダーを提供し、必要なドキュメントをフェッチしてくれます:

docs = WikipediaLoader(query="Carrot", load_max_docs=1, doc_content_chars_max=30000).load()
print(len(docs[0].page_content))
print(docs[0].page_content[:500])

21534.

ニンジン(Daucus carota subsp. sativus)は根菜で、一般的にはオレンジ色をしているが、紫、黒、赤、白、黄色などの品種もあり、いずれもヨーロッパと南西アジア原産の野生のニンジンDaucus carotaを家畜化したものである。原産地はおそらくペルシャで、もともとは葉と種子のために栽培されていた。最もよく食べられているのは根の部分だが、茎や葉も食べられている。

ウィキペディアを検索し、デフォルトでは100件までの結果を返します。load_max_docs=1で制限できまsy。また、doc_content_chars_maxで上書きしない限り、返されるドキュメントは4,000文字に切り詰められます。

ウィキペディア以外のソースが必要な場合、LangChainは他の多くのドキュメントローダーを提供しています。

Split

これで一つの大きなドキュメントができました。これをベクターとして埋め込み、ベクター・インデックスに追加すると、どのようなクエリに対してもドキュメント全体が返されることになる。有用な答えを得るためには、ページの最も関連性の高い部分だけが返されるように、チャンクに分割する必要があります。チャンクサイズの選択とオーバーラップはこのドキュメントの範囲外ですが、より深く知りたい方はこちらの論文を参考にしてください。

text_splitter = TokenTextSplitter(chunk_size=200, chunk_overlap=0)
split_docs = text_splitter.split_documents(docs)
print(len(split_docs))

25

TokenTextSplitterは、ドキュメントを指定された長さのチャンクに分割しようとします。OpenAIのモデルが使っているのと同じアルゴリズムであるバイトペアエンコーディングを使って、ドキュメントをトークン化します。この方法で作成されたトークンは長さが固定されていないため、チャンクにはわずかに異なる数の文字が含まれる可能性があります。目安としては、トークンは平均して4文字程度です。

Index

ドキュメント・チャンクができたので、それをインデックスに追加しよう。

vector_db = MomentoVectorIndex.from_documents(
    split_docs, OpenAIEmbeddings(), index_name="carrot-py"
)

OpenAIを使ってチャンクをベクターに埋め込み、Momentoにアップロードします。インデックスがまだ存在しない場合は、インデックスを作成します。OpenAIからレート制限のエラーが発生する場合は、.NET FrameworkのOpenAIEmbeddingschunk_sizeオプションを試してみてください。

Query

これで検索可能なベクトル・インデックスができました!このようにクエリーできます:

result = vector_db.similarity_search("Are all carrots orange?")
print(result[0].page_content)

10世紀、あるいはそれ以前かもしれない。現代まで残っている東洋ニンジンの標本は、紫色か黄色が一般的で、根が枝分かれしていることが多い。西洋」ニンジンは17世紀にオランダで生まれた。オレンジ色をしていることから、オレンジ家とオランダ独立闘争の象徴としてオランダで広まったという俗説があるが、その根拠はほとんどない。オレンジ色は、これらの品種に含まれる豊富なカロテンに起因する。西洋ニンジンの品種は、一般的に根の形によって分類される。一般的には以下の4種類である: シャンテネー・キャロット。根は他の品種より短いが、葉は旺盛で胴回りが大きく、肩幅が広く、先端が鈍く丸みを帯びている。貯蔵性に優れ、芯は淡い色をしており、主に加工用に使われる。

見ての通り、最も関連性の高いチャンクを見つけて返します。質問した内容が正しい情報とベクトル空間で十分に類似していれば、有用な答えが得られます。その情報をより人間が読みやすい形式に変えたい場合は、LangChainを使ってチャンクとクエリをOpenAIのLLMに送り、より自然な答えを生成することができます:

llm = ChatOpenAI(model_name="gpt-3.5-turbo", temperature=0)
qa_chain = RetrievalQA.from_chain_type(llm, retriever=vector_db.as_retriever())

これにより、Momento Vector IndexをGPT 3.5に接続し、次のようなクエリを実行できるようになります:

llm_result = qa_chain({"query": "Are all carrots orange?"})
print(llm_result["result"])

いや、すべてのニンジンがオレンジ色というわけではない。ニンジンには紫、黒、赤、白、黄色の品種もある。これらは家宝品種であり、野生のニンジンを家畜化したものである。

これで、結果には必要な情報だけが含まれるようになりました。もちろん、GPT 3.5はすでにニンジンについて知っていると思いますが、この方法でどんな情報も人間が読める結果に変換することができます。このフローが完成すると、LangChainがどのように複数のデータソースをつなげて、有用な答えに変換しているかがわかります。

この時点で、データの保存とクエリに使用できるインデックスができ、ベクトル検索をサポートするあらゆるチェーンに接続できます。ドキュメントに基づいて質問に答えたり、チャットボットのメッセージ履歴を保存したり、ベクターを使って想像できることは何でもできます。

LangChainを使わずにMomento Vector Indexを直接使う方法については、このブログ記事をご覧ください。または、Momentoのドキュメントを読んで、すぐに飛び込んでください。