Back to Practice

VectorStoreIndex over QdrantVectorStore

Worked
rag

In this worked example, you will replicate the core logic of build_index_from_nodes from the repository. This function builds a VectorStoreIndex that stores nodes and their pre‑computed embeddings in a Qdrant collection, making Qdrant the single source of truth (the payload holds the chunk text and metadata, so no separate docstore is needed).

Why this wiring?

  • Qdrant as docstore: By setting index_doc_id=True and text_key="text", each Qdrant point stores the full node text and metadata. Retrieval hydrates nodes straight from the payload.
  • No re‑embedding: Nodes already have .embedding set by embed_nodes_cached; the index uses them exactly as given.
  • Deterministic node IDs: _stable_node_ids gives each node a UUID5 based on namespace and content, so re‑runs upsert the same points instead of duplicating.
  • Collection naming: _collection_name builds a unique name from the namespace and a fingerprint of sorted node IDs, preventing cross‑lane contamination.

Your task

Study the code below and understand how each part fits together. The build_index_from_nodes function is already fully written; here we distill its essential steps into a standalone implementation that you will examine. Pay attention to:

  1. Creating the Qdrant client via _get_qdrant_client().
  2. Making node IDs deterministic with _stable_node_ids.
  3. Deriving the collection name with _collection_name.
  4. Configuring QdrantVectorStore with the correct parameters.
  5. Setting up a StorageContext that points to this vector store.
  6. Returning a VectorStoreIndex built from the pre‑embedded nodes.

The starter_code below gives you the skeleton. The solution_code is the full reference. Read the reasoning in the comments.

Starter code

python
import os
from llama_index.core import StorageContext, VectorStoreIndex
from llama_index.vector_stores.qdrant import QdrantVectorStore

# Assume these helper functions are available from the repository:
# _get_qdrant_client, _stable_node_ids, _collection_name, vector_store_backend, qdrant_location

def build_index_from_nodes(nodes, embed_model, *, namespace: str = "", verbose: bool = True):
    # Your implementation goes here
    pass

Solution code

python
import os
from llama_index.core import StorageContext, VectorStoreIndex
from llama_index.vector_stores.qdrant import QdrantVectorStore

# Assume these are imported from the repository:
# from .memory_common import _get_qdrant_client, _stable_node_ids, _collection_name, vector_store_backend, qdrant_location

def build_index_from_nodes(nodes, embed_model, *, namespace: str = "", verbose: bool = True):
    """Build a VectorStoreIndex over Qdrant, using pre‑embedded nodes."""
    # 1. Determine backend: default is qdrant, opt out with VECTOR_STORE=memory
    from .memory_common import vector_store_backend, qdrant_location
    if vector_store_backend() != "qdrant":
        # Fallback to plain in‑memory index (no Qdrant)
        return VectorStoreIndex(nodes, embed_model=embed_model)

    # 2. Get the process‑wide Qdrant client (or None if unavailable)
    from .memory_common import _get_qdrant_client
    client = _get_qdrant_client()
    if client is None:
        # Qdrant unavailable – fallback to memory
        return VectorStoreIndex(nodes, embed_model=embed_model)

    # 3. Assign deterministic node IDs (re‑run ⇒ upsert, no duplicates)
    from .memory_common import _stable_node_ids
    _stable_node_ids(nodes, namespace=namespace)

    # 4. Compute a stable collection name from namespace + corpus fingerprint
    from .memory_common import _collection_name
    collection = _collection_name(nodes, namespace=namespace)

    if verbose:
        print(f"[vector-store] qdrant collection {collection} "
              f"({len(nodes)} node(s)) at {qdrant_location()}")

    # 5. Create the QdrantVectorStore with the contract that makes Qdrant the docstore
    vector_store = QdrantVectorStore(
        client=client,
        collection_name=collection,
        index_doc_id=True,   # store node.id_ as doc_id for direct access
        text_key="text",      # payload key holding the chunk text
    )

    # 6. Build a StorageContext that uses this Qdrant store (no in‑memory docstore)
    storage_context = StorageContext.from_defaults(vector_store=vector_store)

    # 7. Create the index; LlamaIndex will not re‑embed because nodes already have embeddings
    return VectorStoreIndex(
        nodes,
        storage_context=storage_context,
        embed_model=embed_model,
    )

Key takeaways

  • The QdrantVectorStore parameters index_doc_id=True and text_key="text" are essential for Qdrant to act as the single source of truth.
  • Deterministic node IDs via _stable_node_ids prevent duplicate points on re‑runs.
  • The collection name derived by _collection_name ensures each (namespace, corpus) pair gets its own collection.
  • If Qdrant is unavailable, the code gracefully falls back to an in‑memory index.

Now review the rubric below to confirm you understand the critical details.

Your code
Sources
  • roadmap-kg/kg/memory_common.py:931-972
  • roadmap-kg/kg/memory_common.py:975-990
  • roadmap-kg/kg/memory_common.py:836-872