Using the Qdrant connector (Preview)
Warning
The Semantic Kernel Vector Store functionality is in preview, and improvements that require breaking changes may still occur in limited circumstances before release.
Overview
The Qdrant Vector Store connector can be used to access and manage data in Qdrant. The connector has the following characteristics.
Feature Area | Support |
---|---|
Collection maps to | Qdrant collection with payload indices for filterable data fields |
Supported key property types |
|
Supported data property types |
|
Supported vector property types | ReadOnlyMemory<float> |
Supported index types | Hnsw |
Supported distance functions |
|
Supports multiple vectors in a record | Yes (configurable) |
IsFilterable supported? | Yes |
IsFullTextSearchable supported? | Yes |
StoragePropertyName supported? | Yes |
Getting started
Add the Qdrant Vector Store connector NuGet package to your project.
dotnet add package Microsoft.SemanticKernel.Connectors.Qdrant --prerelease
You can add the vector store to the dependency injection container available on the KernelBuilder
or to the IServiceCollection
dependency injection container using extension methods provided by Semantic Kernel.
using Microsoft.SemanticKernel;
// Using Kernel Builder.
var kernelBuilder = Kernel
.CreateBuilder()
.AddQdrantVectorStore("localhost");
using Microsoft.SemanticKernel;
// Using IServiceCollection with ASP.NET Core.
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddQdrantVectorStore("localhost");
Extension methods that take no parameters are also provided. These require an instance of the Qdrant.Client.QdrantClient
class to be separately registered with the dependency injection container.
using Microsoft.Extensions.DependencyInjection;
using Microsoft.SemanticKernel;
using Qdrant.Client;
// Using Kernel Builder.
var kernelBuilder = Kernel.CreateBuilder();
kernelBuilder.Services.AddSingleton<QdrantClient>(sp => new QdrantClient("localhost"));
kernelBuilder.AddQdrantVectorStore();
using Microsoft.Extensions.DependencyInjection;
using Microsoft.SemanticKernel;
using Qdrant.Client;
// Using IServiceCollection with ASP.NET Core.
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddSingleton<QdrantClient>(sp => new QdrantClient("localhost"));
builder.Services.AddQdrantVectorStore();
You can construct a Qdrant Vector Store instance directly.
using Microsoft.SemanticKernel.Connectors.Qdrant;
using Qdrant.Client;
var vectorStore = new QdrantVectorStore(new QdrantClient("localhost"));
It is possible to construct a direct reference to a named collection.
using Microsoft.SemanticKernel.Connectors.Qdrant;
using Qdrant.Client;
var collection = new QdrantVectorStoreRecordCollection<Hotel>(
new QdrantClient("localhost"),
"skhotels");
Data mapping
The Qdrant connector provides a default mapper when mapping data from the data model to storage. Qdrant requires properties to be mapped into id, payload and vector(s) groupings. The default mapper uses the model annotations or record definition to determine the type of each property and to do this mapping.
- The data model property annotated as a key will be mapped to the Qdrant point id.
- The data model properties annotated as data will be mapped to the Qdrant point payload object.
- The data model properties annotated as vectors will be mapped to the Qdrant point vector object.
Property name override
For data properties and vector properties (if using named vectors mode), you can provide override field names to use in storage that is different to the property names on the data model. This is not supported for keys, since a key has a fixed name in Qdrant. It is also not supported for vectors in single unnamed vector mode, since the vector is stored under a fixed name.
The property name override is done by setting the StoragePropertyName
option via the data model attributes or record definition.
Here is an example of a data model with StoragePropertyName
set on its attributes and how that will be represented in Qdrant.
using Microsoft.Extensions.VectorData;
public class Hotel
{
[VectorStoreRecordKey]
public ulong HotelId { get; set; }
[VectorStoreRecordData(IsFilterable = true, StoragePropertyName = "hotel_name")]
public string HotelName { get; set; }
[VectorStoreRecordData(IsFullTextSearchable = true, StoragePropertyName = "hotel_description")]
public string Description { get; set; }
[VectorStoreRecordVector(4, DistanceFunction.CosineDistance, IndexKind.Hnsw, StoragePropertyName = "hotel_description_embedding")]
public ReadOnlyMemory<float>? DescriptionEmbedding { get; set; }
}
{
"id": 1,
"payload": { "hotel_name": "Hotel Happy", "hotel_description": "A place where everyone can be happy." },
"vector": {
"hotel_description_embedding": [0.9, 0.1, 0.1, 0.1],
}
}
Getting started
Install semantic kernel with the qdrant extras, which includes the qdrant client.
pip install semantic-kernel[qdrant]
You can then create a vector store instance using the QdrantStore
class, this will create a AsyncQdrantClient using the environment variables QDRANT_URL
, QDRANT_API_KEY
, QDRANT_HOST
, QDRANT_PORT
, QDRANT_GRPC_PORT
, QDRANT_PATH
, QDRANT_LOCATION
and QDRANT_PREFER_GRPS
to connect to the Qdrant instance, those values can also be supplied directly. If nothing is supplied it falls back to location=:memory:
.
from semantic_kernel.connectors.memory.qdrant import QdrantStore
vector_store = QdrantStore()
You can also create the vector store with your own instance of the qdrant client.
from qdrant_client.async_qdrant_client import AsyncQdrantClient
from semantic_kernel.connectors.memory.qdrant import QdrantStore
client = AsyncQdrantClient(host='localhost', port=6333)
vector_store = QdrantStore(client=client)
You can also create a collection directly.
from semantic_kernel.connectors.memory.qdrant import QdrantCollection
collection = QdrantCollection(collection_name="skhotels", data_model_type=hotel)
Serialization
The Qdrant connector uses a model called PointStruct
for reading and writing to the store. This can be imported from from qdrant_client.models import PointStruct
. The serialization methods expects a output of a list of PointStruct objects, and the deserialization method recieves a list of PointStruct objects.
There are some special considerations for this that have to do with named or unnamed vectors, see below.
For more details on this concept see the serialization documentation.
Qdrant vector modes
Qdrant supports two modes for vector storage and the Qdrant Connector with default mapper supports both modes. The default mode is single unnamed vector.
Single unnamed vector
With this option a collection may only contain a single vector and it will be unnamed in the storage model in Qdrant. Here is an example of how an object is represented in Qdrant when using single unnamed vector mode:
new Hotel
{
HotelId = 1,
HotelName = "Hotel Happy",
Description = "A place where everyone can be happy.",
DescriptionEmbedding = new float[4] { 0.9f, 0.1f, 0.1f, 0.1f }
};
{
"id": 1,
"payload": { "HotelName": "Hotel Happy", "Description": "A place where everyone can be happy." },
"vector": [0.9, 0.1, 0.1, 0.1]
}
Hotel(
hotel_id = 1,
hotel_name = "Hotel Happy",
description = "A place where everyone can be happy.",
description_embedding = [0.9f, 0.1f, 0.1f, 0.1f],
)
from qdrant_client.models import PointStruct
PointStruct(
id=1,
payload={ "hotel_name": "Hotel Happy", "description": "A place where everyone can be happy." },
vector=[0.9, 0.1, 0.1, 0.1],
)
Named vectors
If using the named vectors mode, it means that each point in a collection may contain more than one vector, and each will be named. Here is an example of how an object is represented in Qdrant when using named vectors mode:
new Hotel
{
HotelId = 1,
HotelName = "Hotel Happy",
Description = "A place where everyone can be happy.",
HotelNameEmbedding = new float[4] { 0.9f, 0.5f, 0.5f, 0.5f }
DescriptionEmbedding = new float[4] { 0.9f, 0.1f, 0.1f, 0.1f }
};
{
"id": 1,
"payload": { "HotelName": "Hotel Happy", "Description": "A place where everyone can be happy." },
"vector": {
"HotelNameEmbedding": [0.9, 0.5, 0.5, 0.5],
"DescriptionEmbedding": [0.9, 0.1, 0.1, 0.1],
}
}
Hotel(
hotel_id = 1,
hotel_name = "Hotel Happy",
description = "A place where everyone can be happy.",
hotel_name_embedding = [0.9f, 0.5f, 0.5f, 0.5f],
description_embedding = [0.9f, 0.1f, 0.1f, 0.1f],
)
from qdrant_client.models import PointStruct
PointStruct(
id=1,
payload={ "hotel_name": "Hotel Happy", "description": "A place where everyone can be happy." },
vector={
"hotel_name_embedding": [0.9, 0.5, 0.5, 0.5],
"description_embedding": [0.9, 0.1, 0.1, 0.1],
},
)
To enable named vectors mode, pass this as an option when constructing a Vector Store or collection. The same options can also be passed to any of the provided dependency injection container extension methods.
using Microsoft.SemanticKernel.Connectors.Qdrant;
using Qdrant.Client;
var vectorStore = new QdrantVectorStore(
new QdrantClient("localhost"),
new() { HasNamedVectors = true });
var collection = new QdrantVectorStoreRecordCollection<Hotel>(
new QdrantClient("localhost"),
"skhotels",
new() { HasNamedVectors = true });
In python the default value for named_vectors
is True, but you can also disable this as shown below.
from semantic_kernel.connectors.memory.qdrant import QdrantCollection
collection = QdrantCollection(
collection_name="skhotels",
data_model_type=Hotel,
named_vectors=False,
)