これは、なにをしたくて書いたもの?
Qdrantのチュートリアルから、「LlamaIndexを使ったマルチテナント(Multitenancy with LlamaIndex)」を試してみたいと思います。
Multitenancy with LlamaIndex - Qdrant
今回のチュートリアルの狙い
今回扱うチュートリアルはこちらの「LlamaIndexを使ったマルチテナント(Multitenancy with LlamaIndex)」です。
Multitenancy with LlamaIndex - Qdrant
マルチテナントが主題のように見えますが、ここで示したいのはQdrantとLlamaIndexとの統合ですね。
このチュートリアルの内容をざっくり言うと、LlamaIndexの機能でQdrantと統合し、ドキュメント登録とフィルタリング付きの検索を
行う、というのがよいんでしょうね。
Qdrantは様々なフレームワークと統合することができ、そのうちのひとつにLlamaIndexがあります。
LlamaIndexは、LLMと外部のデータを接続するためのインターフェースを提供するフレームワークだそうです。RAGのための
フレームワーク、と言ってもよさそうです。
LlamaIndex, Data Framework for LLM Applications
LlamaIndex自体には今回は踏み込みません。
Qdrantは他にも様々なフレームワークとの統合が可能なようです。

そして今回は、QdrantをLlamaIndexのベクトルデータベースとして組み合わせて使うチュートリアルとなっています。
Multitenancy with LlamaIndex - Qdrant
…なのですが、このドキュメント内で使われているLlamaIndexのバージョンが古いようなので、現在のLlamaIndex(0.10.12)に
合わせて読み替えていきます。
環境
今回の環境はこちら。Qdrantは172.17.0.2で動作しているものとします。
$ ./qdrant --version qdrant 1.7.4
QdrantのWeb UIは0.1.21を使っています。
Qdrant ClientおよびLlamaIndexを使うPython環境。
$ python3 --version Python 3.10.12 $ pip3 --version pip 22.0.2 from /usr/lib/python3/dist-packages/pip (python 3.10)
Qdrantのチュートリアル「LlamaIndexを使ったマルチテナント(Multitenancy with LlamaIndex)」を試す
それでは、Qdrantのチュートリアル「LlamaIndexを使ったマルチテナント(Multitenancy with LlamaIndex)」をドキュメントに沿って
進めていきます。
Multitenancy with LlamaIndex - Qdrant
必要なライブラリーのインストール。
$ pip3 install qdrant-client llama-index-vector-stores-qdrant llama-index-embeddings-huggingface
インストールしたライブラリーの一覧。
$ pip3 list Package Version ---------------------------------- ---------- aiohttp 3.9.3 aiosignal 1.3.1 annotated-types 0.6.0 anyio 4.3.0 async-timeout 4.0.3 attrs 23.2.0 certifi 2024.2.2 charset-normalizer 3.3.2 click 8.1.7 dataclasses-json 0.6.4 Deprecated 1.2.14 dirtyjson 1.0.8 distro 1.9.0 exceptiongroup 1.2.0 filelock 3.13.1 frozenlist 1.4.1 fsspec 2024.2.0 greenlet 3.0.3 grpcio 1.62.0 grpcio-tools 1.62.0 h11 0.14.0 h2 4.1.0 hpack 4.0.0 httpcore 1.0.4 httpx 0.27.0 huggingface-hub 0.20.3 hyperframe 6.0.1 idna 3.6 Jinja2 3.1.3 joblib 1.3.2 llama-index-core 0.10.12 llama-index-embeddings-huggingface 0.1.3 llama-index-vector-stores-qdrant 0.1.3 llamaindex-py-client 0.1.13 MarkupSafe 2.1.5 marshmallow 3.20.2 mpmath 1.3.0 multidict 6.0.5 mypy-extensions 1.0.0 nest-asyncio 1.6.0 networkx 3.2.1 nltk 3.8.1 numpy 1.26.4 nvidia-cublas-cu12 12.1.3.1 nvidia-cuda-cupti-cu12 12.1.105 nvidia-cuda-nvrtc-cu12 12.1.105 nvidia-cuda-runtime-cu12 12.1.105 nvidia-cudnn-cu12 8.9.2.26 nvidia-cufft-cu12 11.0.2.54 nvidia-curand-cu12 10.3.2.106 nvidia-cusolver-cu12 11.4.5.107 nvidia-cusparse-cu12 12.1.0.106 nvidia-nccl-cu12 2.19.3 nvidia-nvjitlink-cu12 12.3.101 nvidia-nvtx-cu12 12.1.105 openai 1.12.0 packaging 23.2 pandas 2.2.0 pillow 10.2.0 pip 22.0.2 portalocker 2.8.2 protobuf 4.25.3 pydantic 2.6.1 pydantic_core 2.16.2 python-dateutil 2.8.2 pytz 2024.1 PyYAML 6.0.1 qdrant-client 1.7.3 regex 2023.12.25 requests 2.31.0 safetensors 0.4.2 setuptools 59.6.0 six 1.16.0 sniffio 1.3.0 SQLAlchemy 2.0.27 sympy 1.12 tenacity 8.2.3 tiktoken 0.6.0 tokenizers 0.15.2 torch 2.2.1 tqdm 4.66.2 transformers 4.38.1 triton 2.2.0 typing_extensions 4.9.0 typing-inspect 0.9.0 tzdata 2024.1 urllib3 2.2.1 wrapt 1.16.0 yarl 1.9.4
いきなりドキュメントと違うモジュール(llama-index-vector-stores-qdrant、llama-index-embeddings-huggingface)を
インストールしていますが、これはLlmaIndex側のドキュメントを見るとモジュールが変わっていたからですね。
Configuring Settings - LlamaIndex 🦙 v0.10.12
Qdrant Vector Store - LlamaIndex 🦙 v0.10.12
Embeddings - LlamaIndex 🦙 v0.10.12
Defining and Customizing Documents - LlamaIndex 🦙 v0.10.12
また、llama-index-vector-stores-qdrantをインストールするとqdrant-clientも同時にインストールされますが、今回は明示的に
指定することにします。
この結果、作成するプログラムのimport等も元のドキュメントから変更が入ります。
最初は、Qdrantにコレクションを作成してポイントを登録するプログラムを作成します。
add_documents.py
from qdrant_client import QdrantClient from qdrant_client.models import HnswConfigDiff, PayloadSchemaType from llama_index.core import Settings, StorageContext, VectorStoreIndex from llama_index.core.node_parser import SentenceSplitter from llama_index.core.schema import Document from llama_index.embeddings.huggingface import HuggingFaceEmbedding from llama_index.vector_stores.qdrant import QdrantVectorStore documents = [ Document( text="LlamaIndex is a simple, flexible data framework for connecting custom data sources to large language models.", metadata={ "library": "llama-index", }, ), Document( text="LlamaIndex is a framework that can be integrated with Qdrant.", metadata={ "library": "llama-index", }, ), Document( text="Qdrant is a vector database & vector similarity search engine.", metadata={ "library": "qdrant", }, ), Document( text="Qdrant is implemented by Rust.", metadata={ "library": "qdrant", }, ), Document( text="Qdrant has a tutorial on integrating with LlamaIndex.", metadata={ "library": "qdrant", }, ), ] Settings.embed_model = HuggingFaceEmbedding( model_name="BAAI/bge-small-en-v1.5" ) Settings.node_parser = SentenceSplitter(chunk_size=512, chunk_overlap=32) client = QdrantClient("http://172.17.0.2:6333") vector_store = QdrantVectorStore( collection_name="my_collection", client=client ) storage_context = StorageContext.from_defaults(vector_store=vector_store) VectorStoreIndex.from_documents( documents=documents, storage_context=storage_context ) client.create_payload_index( collection_name="my_collection", field_name="metadata.library", field_type=PayloadSchemaType.KEYWORD, ) client.update_collection( collection_name="my_collection", hnsw_config=HnswConfigDiff(payload_m=16, m=0), )
QdrantClientは直接使うところはかなり限定されます。
こちらはQdrantにポイントとして登録するドキュメントです。チュートリアルから、少しサンプルを増やしておきました。
documents = [
Document(
text="LlamaIndex is a simple, flexible data framework for connecting custom data sources to large language models.",
metadata={
"library": "llama-index",
},
),
Document(
text="LlamaIndex is a framework that can be integrated with Qdrant.",
metadata={
"library": "llama-index",
},
),
Document(
text="Qdrant is a vector database & vector similarity search engine.",
metadata={
"library": "qdrant",
},
),
Document(
text="Qdrant is implemented by Rust.",
metadata={
"library": "qdrant",
},
),
Document(
text="Qdrant has a tutorial on integrating with LlamaIndex.",
metadata={
"library": "qdrant",
},
),
]
Defining and Customizing Documents - LlamaIndex 🦙 v0.10.12
使用する埋め込みモデルの設定。
Settings.embed_model = HuggingFaceEmbedding(
model_name="BAAI/bge-small-en-v1.5"
)
今回はBAAI/bge-small-en-v1.5をHugging Faceからダウンロードして使います。
Embeddings - LlamaIndex 🦙 v0.10.12
次に、ドキュメントをチャンクまたはノードに分割する設定をします。
Settings.node_parser = SentenceSplitter(chunk_size=512, chunk_overlap=32)
Configuring Settings - LlamaIndex 🦙 v0.10.12
QdrantClientを作成し、LlamaIndexで使うベクトルデータベース(QdrantVectorStore)として指定します。
client = QdrantClient("http://172.17.0.2:6333") vector_store = QdrantVectorStore( collection_name="my_collection", client=client )
QdrantVectorStoreをストレージにしてVectorStoreIndex経由でドキュメントを登録します。
storage_context = StorageContext.from_defaults(vector_store=vector_store)
VectorStoreIndex.from_documents(
documents=documents,
storage_context=storage_context
)
Qdrant Vector Store - LlamaIndex 🦙 v0.10.12
最後に、metadata.libraryフィールドに対してインデックスを付与して検索を効率化します。また、インデックス全体を検索することが
ないので(※)HNSWグラフをグローバルに構築しないようにします。
※クエリー時に常にmetadata.libraryをフィルター条件似指定します
client.create_payload_index(
collection_name="my_collection",
field_name="metadata.library",
field_type=PayloadSchemaType.KEYWORD,
)
client.update_collection(
collection_name="my_collection",
hnsw_config=HnswConfigDiff(payload_m=16, m=0),
)
プログラムができたので、実行します。
$ python3 add_documents.py
QdrantのWeb UIを使って、ドキュメント(Qdrantではポイント)が登録されたことを確認します。

OKですね。
今度は、検索をしてみます。
作成したプログラムはこちら。
search.py
import sys from llama_index.core.vector_stores.types import FilterOperator, MetadataFilter, MetadataFilters from qdrant_client import QdrantClient from llama_index.core import Settings, VectorStoreIndex from llama_index.core.node_parser import SentenceSplitter from llama_index.embeddings.huggingface import HuggingFaceEmbedding from llama_index.vector_stores.qdrant import QdrantVectorStore target_library = sys.argv[1] search_keyword = sys.argv[2] Settings.embed_model = HuggingFaceEmbedding( model_name="BAAI/bge-small-en-v1.5" ) Settings.node_parser = SentenceSplitter(chunk_size=512, chunk_overlap=32) client = QdrantClient("http://172.17.0.2:6333") vector_store = QdrantVectorStore( collection_name="my_collection", client=client ) index = VectorStoreIndex.from_vector_store( vector_store=vector_store ) print(f"target filter: {target_library}") print(f"search keyword: {search_keyword}") qdrant_retriver = index.as_retriever( filters=MetadataFilters( filters=[ MetadataFilter( key="library", operator=FilterOperator.EQ, value=target_library, ) ] ) ) nodes_with_scores = qdrant_retriver.retrieve(search_keyword) print() print("results:") for node in nodes_with_scores: print(f""" document id: {node.id_} text: {node.text} metadata: {node.metadata} score: {node.score}""")
検索時にドキュメントを登録した時に指定したメタデータのlibraryを指定するのですが、この値と検索キーワードはコマンドライン引数で
指定するようにしました。
target_library = sys.argv[1] search_keyword = sys.argv[2]
LlamaIndexの設定とQdrantVectorStoreのインスタンスの作成。
Settings.embed_model = HuggingFaceEmbedding(
model_name="BAAI/bge-small-en-v1.5"
)
Settings.node_parser = SentenceSplitter(chunk_size=512, chunk_overlap=32)
client = QdrantClient("http://172.17.0.2:6333")
vector_store = QdrantVectorStore(
collection_name="my_collection",
client=client
)
この後、VectorStoreIndexから検索で使うインデックスを取得するのですが、ここではドキュメントは登録しないので
VectorStoreIndex#from_vector_storeでベクトルデータベース(今回はQdrantVectorStore)のみを指定します。
index = VectorStoreIndex.from_vector_store(
vector_store=vector_store
)
ここで取得したVectorStoreIndexのインスタンスから、VectorStoreIndex#as_retrieverでRetriverのインスタンスを取得するのですが、
この時にフィルターを指定します。ドキュメント登録時にllama-indexとqdrantのどちらかを指定しましたが、検索結果に対する絞り込みを
行います。
print(f"target filter: {target_library}") print(f"search keyword: {search_keyword}") qdrant_retriver = index.as_retriever( filters=MetadataFilters( filters=[ MetadataFilter( key="library", operator=FilterOperator.EQ, value=target_library, ) ] ) ) nodes_with_scores = qdrant_retriver.retrieve(search_keyword)
あとは検索キーワードで検索ですね。
結果表示。
print() print("results:") for node in nodes_with_scores: print(f""" document id: {node.id_} text: {node.text} metadata: {node.metadata} score: {node.score}""")
では、実行してみます。
まずはlibraryにqdrantを指定。
$ python3 search.py qdrant 'large language models'
target filter: qdrant
search keyword: large language models
results:
document
id: bba4d033-7c06-4869-a5a0-b73410e68ddb
text: Qdrant is a vector database & vector similarity search engine.
metadata: {'library': 'qdrant'}
score: 0.6055155
document
id: e7e4ac84-0ef4-48f8-844b-cc2e8b6f97be
text: Qdrant has a tutorial on integrating with LlamaIndex.
metadata: {'library': 'qdrant'}
score: 0.58532107
次は、llama-indexを指定。
$ python3 search.py llama-index 'large language models'
target filter: llama-index
search keyword: large language models
results:
document
id: 2a1d4535-001d-4d93-937d-c34f99ea356c
text: LlamaIndex is a simple, flexible data framework for connecting custom data sources to large language models.
metadata: {'library': 'llama-index'}
score: 0.63576734
document
id: 7d1c5050-ea41-4f4e-bd29-e7cb7c1b63f1
text: LlamaIndex is a framework that can be integrated with Qdrant.
metadata: {'library': 'llama-index'}
score: 0.5662359
良さそうです。
libraryはllama-indexで、キーワードは「qdrant」。
$ python3 search.py llama-index 'qdrant'
target filter: llama-index
search keyword: qdrant
results:
document
id: 7d1c5050-ea41-4f4e-bd29-e7cb7c1b63f1
text: LlamaIndex is a framework that can be integrated with Qdrant.
metadata: {'library': 'llama-index'}
score: 0.68183947
document
id: 2a1d4535-001d-4d93-937d-c34f99ea356c
text: LlamaIndex is a simple, flexible data framework for connecting custom data sources to large language models.
metadata: {'library': 'llama-index'}
score: 0.5274638
こんなところでしょうか。
おわりに
Qdrantのチュートリアルから、「LlamaIndexを使ったマルチテナント(Multitenancy with LlamaIndex)」を試してみました。
初めてのLlamaIndexでもあったのですが、今回はさらっと流すつもりがQdrantのチュートリアルで使われていたLlamaIndexの内容が
古かったみたいで、結局そこそこLlamaIndexの情報も見ることになってしまいました。まあ、それもよいでしょう。
こうなると、LangChainと組み合わせたりしたくなりますね。