---
id: qdrant
title: Qdrant
sidebar_label: Qdrant
---

## Quick Summary

Qdrant is a vector database and vector similarity search engine that is **optimized for fast retrieval**. It was written in rust, achieves 3ms response for 1M Open AI Embeddings, and comes with built-in memory compression.

:::info
You can easily get started with Qdrant in python by running the following command in your CLI:

```
pip install qdrant-client
```

:::

With DeepEval, you can evaluate your Qdrant retriever and **optimize for performance** in addition to speed, by configuring hyperparameters in your Qdrant retrieval pipeline such as `vector dimensionality`, `distance` (or similarity function), `embedding model`, `limit` (or top-K), among many others.

:::tip
To learn more about Qdrant, [visit their documentation](https://qdrant.tech/documentation/).
:::

This diagram demonstrates how the Qdrant retriever integrates with an external embedding model and an LLM generator to enhance your RAG pipeline.

<div
  style={{
    display: "flex",
    alignItems: "center",
    justifyContent: "center",
    flexDirection: "column",
  }}
>
  <ImageDisplayer src="https://miro.medium.com/v2/resize:fit:720/format:webp/1*d_t9FzfdZyelyzBx_CVUNA.png" />
  <div
    style={{
      fontSize: "13px",
    }}
  >
    Source: Ashish Abraham
  </div>
</div>

## Setup Qdrant

To get started with Qdrant, first create a Python `QdrantClient` to connect to your local or cloud-hosted Qdrant instance by providing the corresponding URL.

```python
import qdrant_client
import os

client = qdrant_client.QdrantClient(
    url="http://localhost:6333"  # Change this if using Qdrant Cloud
)
```

Next, create a Qdrant collection with the appropriate vector configurations. This collection will store your document embeddings as `vectors` and the corresponding text chunks as metadata. In the code snippet below, we set the `distance` function to cosine similarity and define a vector dimension of 384.

:::tip
You'll want to iterate and test different values for hyperparameters like `size` and `distance` if you don't achieve satisfying scores during evaluation.
:::

```python
...

# Define collection name
collection_name = "documents"

# Create collection if it doesn't exist
if collection_name not in [col.name for col in client.get_collections().collections]:
    client.create_collection(
        collection_name=collection_name,
        vectors_config=qdrant_client.http.models.VectorParams(
            size=384,  # Vector dimensionality
            distance="cosine"  # Similarity function
        ),
    )
```

To add documents to your Qdrant collection, first embed the chunks before upserting them using the `PointStruct` structure. In this example, we'll use `all-MiniLM-L6-v2` from `sentence_transformers` as our embedding model.

```python
# Load an embedding model
from sentence_transformers import SentenceTransformer
model = SentenceTransformer("all-MiniLM-L6-v2")

# Example document chunks
document_chunks = [
    "Qdrant is a vector database optimized for fast similarity search.",
    "It uses HNSW for efficient high-dimensional vector indexing.",
    "Qdrant supports disk-based storage for handling large datasets.",
    ...
]

# Store chunks with embeddings
for i, chunk in enumerate(document_chunks):
    embedding = model.encode(chunk).tolist()  # Convert text to vector
    client.upsert(
        collection_name=collection_name,
        points=[
            qdrant_client.http.models.PointStruct(
                id=i, vector=embedding, payload={"text": chunk}
            )
        ]
    )
```

We'll use this `Qdrant` collection in the following sections as our retrieval engine to retrieve contexts using cosine similarity for response generation. The retrieved contexts will be passed to our LLM generator, which will generate the final response in our RAG pipeline.

## Evaluating Qdrant Retrieval

To evaluate your Qdrant retriever, you'll first need to prepare an `LLMTestCase`, which includes an `input`, `actual_output`, `expected_output`, and `retrieval_context`. This requires defining an `input` and `expected_output` before generating a response and extracting the retrieval contexts.

In this example, we'll be using the following input:

```bash
"How does Qdrant work?"
```

and the corresponding expected output:

```bash
"Qdrant performs fast and scalable vector search using HNSW indexing and disk-based storage."
```

### Preparing your Test Case

To generate the response or `actual_output` from your RAG pipeline, you'll first need to retrieve relevant contexts from your `Qdrant` collection. To achieve this, we'll define a `search` function that embeds the `input` using the same embedding model (`all-MiniLM-L6-v2`) as above, then search for the top 3 most similar vectors and extract the corresponding texts.

```python
...

def search(query, top_k=3):
    query_embedding = model.encode(query).tolist()

    search_results = client.search(
        collection_name=collection_name,
        query_vector=query_embedding,
        limit=top_k  # Retrieve the top K most similar results
    )

    return [hit.payload["text"] for hit in search_results] if search_results else None

query = "How does Qdrant work?"
retrieval_context = search(query)
```

We'll then insert these contexts into our prompt template to provide additional context and help ground the response.

```python
...

prompt = """
Answer the user question based on the supporting context

User Question:
{input}

Supporting Context:
{retrieval_context}
"""

actual_output = generate(prompt) # hypothetical function, replace with your own LLM
print(actual_output)
```

We'll then pass the input and expected output that was initially defined into an `LLMTestCase`, along with the actual output and retrieval context that we generated and searched for.

```python
from deepeval.test_case import LLMTestCase

...

test_case = LLMTestCase(
    input=input,
    actual_output=actual_output,
    retrieval_context=retrieval_context,
    expected_output="Qdrant is a powerful vector database optimized for semantic search and retrieval.",
)
```

Before proceeding with evaluations, let's examine the `actual_output` that was generated:

```bash
Qdrant is a scalable vector database optimized for high-performance retrieval.
```

### Running Evaluations

To evaluate your `Qdrant` retriever engine, define the selection of metrics you wish to evaluate your retriever on, before passing the metrics and test case into the `evaluate` function.

:::tip
Unless you have custom evaluation criteria, it's best to evaluate your test case using `ContextualRecallMetric`, `ContextualPrecisionMetric`, and `ContextualRelevancyMetric`, as these metrics assess the effectiveness of your retriever. [You can learn more about RAG metrics here](/guides/guides-rag-evaluation)
:::

```python
from deepeval.metrics import (
    ContextualRecallMetric,
    ContextualPrecisionMetric,
    ContextualRelevancyMetric,
)

...

contextual_recall = ContextualRecallMetric(),
contextual_precision = ContextualPrecisionMetric()
contextual_relevancy = ContextualRelevancyMetric()

evaluate(
    [test_case],
    metrics=[contextual_recall, contextual_precision, contextual_relevancy]
)
```

## Improving Qdrant Retrieval

Let's say that after running multiple test cases, we observed that the **Contextual Precision** score is lower than expected. This suggests that while our retriever is fetching relevant contexts, some of them might not be the best match for the query, leading to noise in the response.

### Key Findings

| Query                                        | Contextual Precision Score | Contextual Recall Score |
| -------------------------------------------- | -------------------------- | ----------------------- |
| "How does Qdrant store vector data?"         | 0.39                       | 0.92                    |
| "Explain Qdrant's indexing method."          | 0.35                       | 0.89                    |
| "What makes Qdrant efficient for retrieval?" | 0.42                       | 0.83                    |

### Addressing Low Precision

Since **precision** evaluates how well the retrieved contexts match the query, a lower score often indicates that some retrieved results are not as semantically relevant as they should be. Possible solutions include:

- **Using a More Domain-Specific Embedding Model**  
  If your use case involves technical documentation, a general-purpose model like `all-MiniLM-L6-v2` might not be the best fit. Consider testing models such as:

  - `BAAI/bge-small-en` for better retrieval ranking.
  - `sentence-transformers/msmarco-distilbert-base-v4` for dense passage retrieval.
  - `nomic-ai/nomic-embed-text-v1` for long-form document retrieval.

- **Adjusting Vector Dimensions**  
  If switching models, ensure that the vector dimensions in Qdrant match the embedding output to avoid misalignment.

- **Filtering Less Relevant Results**  
  Applying metadata filters can help exclude unrelated chunks that might be skewing precision.

### Next Steps

Once you've tested alternative embedding models or other altnerate hyperparameters, you'll want to generate new test cases and re-evaluate retrieval quality to measure improvements. Keep an eye on **Contextual Precision**, as an increase indicates more focused and relevant context retrieval.

:::info
For deeper insights into retrieval performance and to compare embedding model variations, consider tracking your evaluations in [Confident AI](https://www.confident-ai.com/).
:::
