46.6kデモを予約するログイン無料で始める

Gemini 1.5、BGE-M3、Milvus Lite、LangChainによるマルチモーダルRAGの構築

マルチモーダルRAGは、異なるモダリティからのデータをコンテキストとして受け入れることで、RAGを拡張する。Gemini 1.5、BGE-M3、Milvus、LangChainを使った構築方法を紹介する。

大規模言語モデル(LLMs)は、テキスト生成、言語翻訳、テキスト要約、質問応答など、様々な自然言語タスクを実行する際に非常に汎用性が高い。しかし、我々のアプリケーションでLLMを使用する際の重大な制限は、幻覚のリスクである。

幻覚とは、LLMによって生成された応答が説得力があり首尾一貫しているように見えるが、全く不正確であるという現象を指す。特に、LLMの応答が基づいている分野の専門家でない場合、この問題を検出することは困難です。

この記事では、Retrieval Augmented Generation (RAG)と呼ばれる、LLMの幻覚を軽減するための強力なソリューションについて説明する。具体的には、テキストや画像といった異なるモダリティのデータを組み合わせたマルチモーダルRAGを実装し、LLM幻覚を軽減する。それでは早速、RAGの一般的な理解から始めよう。

検索拡張世代(RAG)とは?

幻覚は、LLMに事前に訓練された知識を超えた質問をしたときによく起こります。例えば、医学や法学の高度に専門化されたトピックについて、専門用語の多いLLMに質問した場合、LLMからランダムで不正確な回答が返ってくる危険性があります。

この問題を軽減する1つの解決策は、ユースケースに特化したデータセットを使ってLLMを微調整することです。LLMのファインチューニングは効果的ですが、時間とメモリ消費の点で非常に高価です。

RAGは、LLMの幻覚を軽減するための、代替的でありながら強力なアプローチを提供する。このコンセプトは情報検索の手法に基づいており、まずユーザーのクエリを受け取り、次にLLMがクエリに正確に答えるのに役立つ、最も関連性の高いコンテキストをデータベースから見つける。ユーザーのクエリと一緒にコンテキストを提供することで、LLMの応答の精度を大幅に向上させることができる。

図1- RAGの上位概念](https://assets.zilliz.com/Figure_1_High_level_concept_of_RAG_1014643256.png)

**RAGは、検索、補強、生成の3つの要素から構成される。

検索コンポーネントにおいて、RAGは、LLMが文脈化された回答を生成するのに役立つ関連する文脈をフェッチする。コンテキストを取得するためには、まず、すべてのコンテキスト情報を、Sentence Transformers, OpenAI, VoyageAIなどの埋め込みモデルを用いて、ベクトル埋め込みに変換しなければならない。次に、これらの埋め込み情報をMilvusやZilliz Cloud(フルマネージドMilvus)のようなベクトルデータベースに格納します。

ユーザクエリが存在する場合、我々はコンテキストのエンコードに使用したのと同じモデルを使用して、それをベクトル埋め込みに変換する。Milvusのようなベクトルデータベースは、次にベクトル検索(ベクトル類似検索](https://zilliz.com/learn/vector-similarity-search)または意味的類似検索とも呼ばれる)を実行し、クエリ埋め込みとベクトルデータベース内のコンテキスト埋め込みとの間の類似度を計算する](https://zilliz.com/blog/similarity-metrics-for-vector-search)。最後に、ユーザのクエリとの類似度が最も高い上位k個の結果が、関連するコンテキストとして検索される。

オーグメンテーションコンポーネントでは、検索コンポーネントから取得された関連コンテキストは、元のユーザのクエリとマージされ、LLMに渡される首尾一貫したプロンプトを形成する。最後に、LLMは生成コンポーネントで、提供されたプロンプトに基づいてコンテキスト化された応答を生成する。

図2-RAGワークフロー](https://assets.zilliz.com/Figure_2_RAG_workflow_03d6a0c5b2.png)

マルチモーダルRAGとは?

ユーザーのクエリとともに関連するコンテキストを提供するRAGのコンセプトは、LLM応答の質を向上させる上で非常に強力であることが証明されている。しかし、実際のアプリケーションでは、すべての関連するコンテキストがテキストとして提供されるわけではありません。

最も関連性の高いコンテキストが、文書中の画像や表として提供される場合もある。問題は、ほとんどのLLMや埋め込みモデルは、その性質上、画像内のコンテンツを推論できないということだ。また、テーブルや表データの内容を推測することも苦手な傾向があります。すでにご存知かもしれませんが、LLMは、前のトークンが与えられたときに、次に来る可能性の高いトークンを予測するように事前に訓練されています。つまり、LLMは自然にテキストの内容を逐次的に理解しようとするが、表形式のデータではそうはいかない。

マルチモーダルRAGは、LLMに渡すコンテキストとして異なるモダリティからのデータを受け入れる新しいアプローチである。マルチモーダルRAGを実行するには、LLAVA、GPT4-V、Gemini 1.5、Claude 3.5 Sonnetなど、マルチモーダル機能を持つLLMを使う必要がある。CLIP](https://zilliz.com/learn/exploring-openai-clip-the-future-of-multimodal-ai-learning)のようなマルチモーダル埋め込みモデルも良い選択肢ですが、テキストベースの埋め込みモデルを使うこともできます。

マルチモーダルRAGを実装する方法はいくつかある:

  • CLIPのようなマルチモーダル埋め込みモデルを使って、テキストや画像を埋め込みに変換する。次に、クエリとテキスト/画像の埋め込み間の類似性検索を行うことで、関連するコンテキストを取得する。最後に、最も関連性の高いコンテキストのテキストや画像をマルチモーダルLLMに渡す。

  • マルチモーダルLLMを使って、画像や表のテキスト要約を作成する。次に、テキストベースの埋め込みモデルを使って、それらのテキスト要約を埋め込みに変換する。次に、クエリと要約埋め込み間のテキスト類似度検索を実行する。最後に、応答生成のために、最も関連性の高い要約の生画像をLLMに渡す。

以下のセクションでは、2番目のアプローチに基づいたマルチモーダルRAGを実装する。具体的には、Gemini 1.5モデルを使用して、画像と表のテキスト要約を生成し、最終的な応答を生成する。

Milvus Lite、Gemini 1.5、BGE M3、LangChainの紹介

4つの異なるテクノロジーを使ってマルチモーダルRAGシステムを構築する:ベクトルデータベースとしてMilvus、LLMとしてGemini 1.5、埋め込みモデルとしてBGE M3、そしてRAGシステム全体を構築するフレームワークとしてLangChainです。まず、それぞれの技術について説明しよう。

Milvus Lite

Milvusは、オープンソースのベクトルデータベースであり、RAGの検索段階において、10億スケールの埋め込みを効率的に格納し、高速なベクトル検索を行うことができる。Milvusのインストールには様々な方法がありますが、最も簡単な方法はMilvus Liteを使うことです。初心者の方で、とりあえずMilvusを使ってみたいという方には、Milvus Liteがおすすめです。pip installコマンドを実行するだけで、最大100万個のエンベッディングを保存することができます。以下がMilvus Liteのインストールコマンドです:


pip install pymilvus

しかし、アプリケーションをスケールさせ、最終的に本番環境にデプロイする際には、DockerコンテナやKubernetesでMilvusを実行するのがベストです。DockerコンテナやKubernetes経由でMilvusをインストールして実行するには、 Milvus公式インストールページを参照してください。

Gemini 1.5

Gemini 1.5は、Google DeepMindによって開発されたマルチモーダルLLMであり、以前のGemini 1.0モデルを改良したものである。このモデルは、数学、コーディング、多言語能力、画像、音声、動画の理解において、前モデルをはるかに凌駕している。このモデルは、モデルのアーキテクチャとコンテキストの長さという2つの明確な特徴によって、前バージョンとは一線を画している。

Gemini 1.5のアーキテクチャは、TransformerアーキテクチャとMixture of Experts(MoE)を組み合わせたものである。これにより、MoEモデルは、ユーザーの入力やプロンプトに応じて、ニューラルネットワーク内の最も関連性の高いエキスパート経路のみを選択的に活性化するように学習するため、モデルの効率が向上する。また、このバージョンでは、コンテキストの長さが以前のバージョンよりもはるかに長くなっている。Gemini 1.5は、最大100万トークンのコンテキストを扱うことができる。Gemini 1.0では32,000トークンしか扱えなかった。

図3- Gemini 1.5 Proのコンテキスト長](https://assets.zilliz.com/Figure_3_Gemini_1_5_Pro_s_context_length_1ee7297ccd.png)

図3: Gemini 1.5 Proのコンテキストの長さ。ソース

ジェミニ1.5には、現在までに2種類のモデルがある:FlashとProである。Flashは、Gemini 1.5の軽量版であり、より高速で効率的なレスポンスを生成する。一方、ProはGemini 1.5モデルの中で最もパフォーマンスの高いバージョンであり、これまでで最大のモデルであるGemini 1.0 Ultraと同レベルのパフォーマンスを発揮する。

この記事では、Gemini 1.5 Proを使用する。マルチモーダルRAGアプリケーションでこのモデルにアクセスするには、APIキーを設定する必要があります。APIキーを生成するためのステップバイステップの手順については、 このGoogleページ を参照してください。次のセクションでは、マルチモーダルRAGの実装でこのAPIキーを使用します。

BGE-M3

BGE-M3は、異なる粒度レベルで多言語のテキストを処理できる、汎用性の高い埋め込みモデルです。例えば、短い文章から8192トークンまでの長い文書まで、様々な言語のテキスト入力を持つことができます。この埋め込みモデルはまた、2つの異なる埋め込みを出力します:1つは密な埋め込み、もう1つは疎な埋め込み。

密な埋め込みは、疎な埋め込みよりもコンパクトな次元を持ち、元の入力の意味表現を保持します。一方、スパース埋め込みは、その名の通り、スパース、つまり、次元は高いが、要素のほとんどが0である埋め込みです。したがって、スパース埋め込みは、特殊なデータ構造とアルゴリズムを用いて、より効率的に格納・処理することができる。

以下のRAGの実装では、埋め込みモデルとしてBGE-M3を使用し、テキスト入力を埋め込みに変換します。BGE-M3をMilvusで実装するには以下のコードを使います:


pip install "pymilvus[モデル]"

from pymilvus.model.hybrid import BGEM3EmbeddingFunction

bge_m3_ef = BGEM3EmbeddingFunction(

    model_name='BAAI/bge-m3', # モデル名の指定

    device='cpu', # 使用するデバイスを指定(例:'cpu'または'cuda:0'

    use_fp16=False # fp16 を使用するかどうかを指定する。device` が `cpu` の場合は `False` に設定する。

)

docs = [

    「人工知能は1956年に学問分野として創設された、

    「アラン・チューリングは人工知能の実質的な研究を行った最初の人物である、

    「チューリングはロンドンのマイダベールで生まれ、イングランド南部で育った、

]

docs_embeddings = bge_m3_ef.encode_documents(docs)

# 埋め込みを印刷する

print("Embeddings:", docs_embeddings)

"""

出力:

埋め込み:{'dense': [array([-0.02505937, -0.00142193, 0.04015467, ..., -0.02094924、

        0.02623661, 0.00324098], dtype=float32), array([ 0.00118463, 0.00649292, -0.00735763, ..., -0.01446293、

        0.04243685, -0.01794822], dtype=float32), array([ 0.00415287, -0.0101492 , 0.0009811 , ..., -0.02559666、

        0.08084674, 0.00141647], dtype=float32)], 'sparse':<クラス 'numpy.float32'>' 型の<3x250002>スパース配列。

        圧縮されたスパース行形式で43個の要素が格納されています>}。

"""

ご覧のように、1つの入力から2つの異なるタイプのベクトル埋め込みが得られます:密な埋め込みと疎な埋め込みです。Milvusはハイブリッド検索をサポートしており、RAGシステムで検索されるコンテキストの精度と品質を向上させるために、ベクトル検索中にこれら2つの埋め込みを活用することができます。

LangChain

LangChainは、LLMを利用したアプリケーションを数分で構築するためのフレームワークです。これは、ベクトルデータベース、埋め込みモデル、LLMなどのコンポーネントを接続し、完全に機能するRAGシステムを構築するためのオーケストレーションツールです。これは、OpenAI、Anthropic、Googleのような人気のあるLLMプロバイダや、Zillizのようなベクトルデータベースプロバイダと簡単に統合できるおかげで実現可能です。

LangChainはまた、RAGシステムのエンドツーエンドのワークフローを処理します。データ解析、クリーニング、チャンキングなどのデータ前処理ステップから、LLMレスポンス生成、ユーザへの返信まで。このフレームワークの詳細な実装は次のセクションで説明する。

マルチモーダルRAGアプリケーションの構築

このセクションでは、マルチモーダルRAGアプリケーションを構築する。前述したように、ほとんどのLLMはその事前学習の性質上、画像や表のコンテキストを処理するのに苦労している。マルチモーダルRAGを実装することで、画像中のコンテキストを考慮することができ、LLMの全体的な応答品質を向上させることができる。

マルチモーダルRAGのユースケースは以下の通り:PDF形式で保存された研究論文に関連するあらゆる情報を求めることができるRAGシステムを構築する。私たちが扱う研究論文は、有名なTransformerアーキテクチャを紹介した「Attention is All You Need」である。

まず、PDFファイルをダウンロードし、すべての図と表を抽出します。次に、Gemini 1.5 Proを使って、抽出された各図と表の要約を生成し、コンテキスト検索を行う。次に、BGE-M3モデルを使用して、これらの要約を埋め込みに変換し、Milvusベクトルデータベースに格納する。

RAGの実装では、ユーザークエリを埋め込みに変換し、最も関連性の高い要約を見つけるためにベクトル検索を実行する。そして、ユーザークエリに従って最も関連性の高い要約の生の図または表がフェッチされ、応答生成のためにGemini 1.5に渡される。

この記事のコード実装は、 このノートブック からアクセスできます。

では、最初から始めましょう。好きな研究論文をダウンロードするのです。ArxivのAPIを使って、好きな論文をダウンロードすることができます:


インポート arxiv

インポート os

インポートgetpass

from langchain_core.prompts import PromptTemplate

from langchain_core.runnables import RunnableLambda, RunnablePassthrough

from langchain_core.output_parsers import StrOutputParser

from langchain_milvus.vectorstores import Milvus

from langchain.schema import ドキュメント

from langchain.embeddings.base import Embeddings

from pymilvus.model.hybrid import BGEM3EmbeddingFunction

from langchain_core.messages import HumanMessage

from langchain_google_genai import ChatGoogleGenerativeAI

from IPython.display import HTML, display

from PIL import Image

base64をインポート

from pdf2image import convert_from_path

lpとしてlayoutparserをインポートする

インポート cv2

npとしてnumpyをインポートする

file_path = "./Dataset/pdf/"

arxiv_client = arxiv.Client()

paper = next(arxiv.Client().results(arxiv.Search(id_list=["1706.03762"])))

# PDFをカスタムファイル名で指定したディレクトリにダウンロードします。

paper.download_pdf(dirpath=file_path, filename="attention.pdf")

Arxivで論文を閲覧する際、URLに記載されている対応するIDを入力することで、任意の研究論文をダウンロードすることができます。例えば、"Attention is All You Need "論文のIDは1706.03762です。

次に、PDFファイルから図表を抽出してみよう。私の観察によると、PubLayNetデータセットで学習したMask-RCNNモデルがこのタスクに最も正確です。LayoutParserはこのモデルを利用して抽出を実行するのに最適なライブラリです。


image_path = "./Dataset/img/"

def extract_image_and_table(pdf_path, output_dir):

    images = convert_from_path(pdf_path)

    model = lp.Detectron2LayoutModel('lp://PubLayNet/mask_rcnn_X_101_32x8d_FPN_3x/config'、

                                 extra_config=["MODEL.ROI_HEADS.SCORE_THRESH_TEST", 0.75]、

                                 label_map={0: "Text", 1: "Title", 2: "List", 3: "Table", 4: "Figure"})

    for i, image in enumerate(images):

        image_np = cv2.cvtColor(np.array(image), cv2.COLOR_RGB2BGR).

        layout_result = model.detect(image_np).

        blocks = lp.Layout([b for b in layout_result if b.type=='Table' or b.type=='Figure'])

        for j, block in enumerate(blocks._blocks):

            x1 = int(block.block.x_1)

            y1 = int(block.block.y_1)

            x2 = int(block.block.x_2)

            y2 = int(block.block.y_2)

            cropped_image = Image.fromarray(image_np).crop((x1, y1, x2, y2))

            cropped_image.save(f'{output_dir}/{i}_{j}.png', 'PNG')

extract_image_and_table(f"{file_path}attention.pdf", image_path)

さて、研究論文からすべての図と表を抽出したので、次はGemini 1.5 Proを使って各図と表の要約を生成しましょう。このLLMにアクセスするには、APIキーを使用する必要があります。

PDFファイルから抽出した図と表は、PNGファイルとして保存されています。しかし、ほとんどのAPIは、画像を含むすべての入力がテキスト形式であることを想定しているため、これらの生の画像ファイルをマルチモーダルLLMの入力として使用することはできません。そこで、これらの画像ファイルをbase64形式にエンコードする必要がある。このエンコード方式により、APIリクエストで画像をテキスト文字列として送信できるようになる。


def encode_image(image_path):

    with open(image_path, "rb") as image_file:

        return base64.b64encode(image_file.read()).decode("utf-8")

def image_summarize(img_base64, prompt):

    msg = llm_model.invoke(

        [

            HumanMessage(

                content=[

                    {"type":「text", "text": prompt}、

                    {

                        "type":「image_url"、

                        "image_url":{"url": f "data:image/jpeg;base64,{img_base64}"}、

                    }

                ]

            )

        ]

    )

    msg.content を返す

def generate_img_summaries(path):

    # Base64エンコードされた画像を保存

    img_base64_list = [].

    # 画像の要約を保存する

    image_summaries = [] # 画像サマリーを保存する

    # プロンプト

    prompt = """あなたは検索用に画像を要約する仕事を与えられたアシスタントです。

    これらの要約は埋め込まれ、生の画像を検索するために使用されます。

    検索用にうまく最適化された画像の簡潔な要約をしてください。""""

    # 画像に適用する

    for i, img_file in enumerate(sorted(os.listdir(path))):

        if img_file.endswith(".png"):

            img_path = os.path.join(path, img_file)

            base64_image = encode_image(img_path)

            img_base64_list.append(base64_image)

            image_summaries.append(image_summarize(base64_image, prompt))

    return img_base64_list, image_summaries

img_base64_list, image_summaries = generate_img_summaries(image_path)

documents = [Document(page_content=image_summaries[i], metadata={"source": img_base64_list[i]}) for i in range (len(image_summaries))].

それでは、Gemini 1.5 Proによって生成された図とそれに対応する要約の一例を見て、サニティチェックを行いましょう。以下のコードを実装することで可能です:


def plt_img_base64(img_base64):

    # base64文字列をソースとするHTMLのimgタグを作成します。

    image_html = f'<img src="data:image/jpeg;base64,{img_base64}"/>'

    # HTMLをレンダリングして画像を表示する

    表示(HTML(image_html))

 

 plt_img_base64(img_base64_list[0])

 print(image summaries[0])

図5-表とそれに対応する要約の例](https://assets.zilliz.com/Figure_5_Example_of_a_table_and_its_corresponding_summarization_0eed08f8f3.png)

Gemini 1.5 Proモデルによって生成された要約は、この図で提供される全体的なエッセンスと情報を捉えている。次に、これらの要約をエンコードされた画像とともにMilvusベクトルデータベースに格納する必要がある。

まず、要約を埋め込みに変換する必要がある。前述のように、BGE-M3モデルを使ってこの作業を行う。後ほど、BGE-M3から生成された高密度の埋め込みのみをコンテキスト検索に使用します。

LangChainでこの埋め込みモデルを使うには、2つの異なるメソッド embed_documents と embed_query を持つカスタムクラスを作る必要があります。LangChainは入力を埋め込みに変換してベクトルデータベースに格納する際に、デフォルトでこの2つの関数を呼び出します。


class BGEMilvusEmbeddings(Embeddings):

    def __init__(self):

        self.model = BGEM3EmbeddingFunction(

                    model_name='BAAI/bge-m3', # モデル名を指定する。

                    device='cpu', # 使用するデバイスを指定(例:'cpu'または'cuda:0'

                    use_fp16=False # fp16 を使用するかどうかを指定する。device` が `cpu` の場合は `False` に設定する。

                )

    def embed_documents(self, texts):

        embeddings = self.model.encode_documents(texts)

        return [i.tolist() for i in embeddings["dense"]].

    def embed_query(self, text):

        embedding = self.model.encode_queries([text])

        return embedding["dense"][0].tolist()

  

# 埋め込みモデルとLLMのインスタンス化

embedding_model = BGEMilvusEmbeddings()

os.environ['GOOGLE_API_KEY'] = getpass.getpass('ジェミニAPIキー:')

llm_model = ChatGoogleGenerativeAI(model="gemini-1.5-pro"、

                 temperature=0.7, top_p=0.85)

URI = "./milvus_demo.db"

# Milvusベクトルストアを作成し、その中にエンベッディングを格納する。

vectorstore = Milvus.from_documents(

    ドキュメント

    embedding_model、

    connection_args={"uri":URI}、

    コレクション名="multimodal_rag_demo"

)

# ベクターストアからリトリーバーを作成する

レトリーバー = vectorstore.as_retriever()

これで完了だ!これでRAGを実行する準備ができた。

クエリを送信してみましょう:「英語からドイツ語への翻訳タスクにおけるベースモデルのBLEUスコアは?次のコードで、RAGシステムによって検索された最も関連性の高い画像を調べることができます:


# レトリバーを使う

query = "英語からドイツ語への翻訳タスクにおけるベースモデルのBLEUスコアは?"

retrieved_docs = retriever.invoke(query, limit= 1)

plt_img_base64(retrieved_docs[0].metadata["source"])

図6- 指定されたクエリに対する検索されたコンテキストの例](https://assets.zilliz.com/Figure_6_Example_of_the_retrieved_context_for_a_given_query_ff5d9fc112.png)

ご覧のように、RAGシステムは、クエリに応じて最も関連性の高い画像を取得することができます。RAGパイプラインでは、この画像はGemini 1.5 Proのコンテキストとして使用され、コンテキスト化された回答が生成されます。

あとは、応答生成のためのエンドツーエンドのワークフローを構築するだけです。このプロセスには、ユーザークエリを受け取ること、最も関連性の高いコンテキストを取得すること、そのコンテキストとユーザークエリを1つの首尾一貫したプロンプトに組み込むこと、プロンプトをLLMに渡すこと、そして最後にLLMからユーザへの応答を生成することが含まれます。LangChainを使えば、これらのステップを数行のコードで実現できます:


def prepare_image_context(docs):

    b64_images = [].

    for docs:

        b64_images.append(doc.metadata["source"])

    return {"images": b64_images}.

def img_prompt(data_dict):

    メッセージ = [].

    if data_dict["context"]["images"]:

        for image in data_dict["context"]["images"]:

            image_message = {画像メッセージ

                "type":「image_url"、

                「image_url":{"url": f "data:image/jpeg;base64,{image}"}、

            

            messages.append(image_message)

    text_message = {(テキスト・メッセージ

        「タイプ":「text"、

        「テキスト":(

            "画像か表が与えられます。"

    "画像や表に含まれる情報を使って、ユーザーの質問に関連した文脈に沿った答えを提供します。n"

    f "ユーザから提供された質問:{data_dict['質問']}nn"

        ),

    }

    messages.append(text_message)

    HumanMessage(content=messages)] を返す。

def multi_modal_rag_chain(retriever):

    # RAGパイプライン

    chain = (

        {

            "context": retriever | RunnableLambda(prepare_image_context)、

            "question":RunnablePassthrough()、

        }

        | RunnableLambda(img_prompt)

        | llm_model

        | StrOutputParser()

    )

    リターン・チェーン

# RAGチェーンの作成

chain_multimodal_rag = multi_modal_rag_chain(retriever)

そして、1行のコードでクエリーに従ってRAGパイプラインを呼び出すことができる:


# RAG チェーンを実行する

chain_multimodal_rag.invoke(query)

"""

出力される:

英語からドイツ語への翻訳タスクにおけるベースTransformerモデルのBLEUスコアは27.3。

"""

このデモの全コードはこのノートを参照。

結論

おめでとうございます!あなたは、Milvus、Gemini 1.5、BGE-M3、LangChainを使ったマルチモーダルRAGシステムの構築に成功しました。マルチモーダルRAGは、特に関連するコンテキストがテキスト以外の形式で保存されている場合、特定のユーザークエリに対するLLMの応答精度を高めるために非常に有用なシステムです。

このRAGコンセプトを、他の研究論文を照会したり、異なるユースケースを探索したりするために適応させることができるようになりました。上記のデモでは、ベクトルデータベース内に約15個のコンテキストしか保存していない。しかし、Milvus Liteでは最大100万個のエンベッディングを格納することができます。Milvus LiteやMilvus on Docker/KubernetesやZilliz Cloud(フルマネージドMilvus)を使って、より多くのコンテキストをベクトルデータベースに格納することで、様々なユースケースを拡張したり、試したりすることをお勧めします。