Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion skills/qdrant-edge/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ There is no built-in `.sync()`. Sync is a pattern you assemble from shard helper

Use when: you need exact-term or BM25 matching, alone or alongside vectors.

- BM25 is built into Edge (`Bm25`, `Bm25Config`, `embed_document`, `embed_query`) with the IDF `Modifier` on `EdgeSparseVectorParams`, and is wire-compatible with server BM25: a shard seeded from a server snapshot answers local BM25 queries without re-indexing. Do not ship a second BM25 library [Edge BM25](https://skills.qdrant.tech/md/documentation/edge/edge-bm25/)
- BM25 is built into Edge (`Bm25`, `Bm25Config`, `embed_document`, `embed_query`) with the IDF `Modifier` on `EdgeSparseVectorParams`, and is wire-compatible with the server's sparse vector BM25 model (Edge BM25 docs cover only the sparse vector variant): a shard seeded from a server snapshot answers local BM25 queries without re-indexing. Do not ship a second BM25 library [Edge BM25](https://skills.qdrant.tech/md/documentation/edge/edge-bm25/)
- Dense embeddings are NOT in Edge: generate them on device with the separate `fastembed` package [FastEmbed embeddings](https://skills.qdrant.tech/md/documentation/edge/edge-fastembed-embeddings/)
- For hybrid search, run the dense and sparse legs as `prefetches` and fuse them with a `Fusion` query in a single `query` call, instead of combining rankings in application code [Reading data](https://skills.qdrant.tech/md/documentation/edge/edge-api/reading-data/)

Expand Down
6 changes: 5 additions & 1 deletion skills/qdrant-model-migration/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ You MUST re-embed if: changing model provider (OpenAI to Cohere), changing archi

You do NOT need to re-embed existing dense vectors if:

- Adding BM25 for hybrid search on v1.20+: no vectors to generate. Create a `text` payload index with `scoring` enabled on the payload field; Qdrant builds it from the stored payload [BM25 scoring](https://skills.qdrant.tech/md/documentation/manage-data/indexing/?s=bm25-scoring)
- Adding sparse vectors for hybrid search: generate only the sparse vectors. On v1.18+, add the sparse field to the existing collection and backfill it with `UpdateVectors`. On v1.17 or earlier, copy the dense vectors into the new collection instead of recomputing them [Update vectors](https://skills.qdrant.tech/md/documentation/manage-data/points/?s=update-vectors)
- Using Matryoshka models: use the `dimensions` parameter to output lower-dimensional embeddings (some recall loss, good for 100M+ datasets)
- Changing quantization (binary to scalar): Qdrant re-quantizes automatically [Quantization](https://skills.qdrant.tech/md/documentation/manage-data/quantization/)
Expand Down Expand Up @@ -73,6 +74,9 @@ If you anticipate future model migrations, define both vector fields upfront at

Use when: adding sparse/BM25 vectors to an existing dense-only collection. Most common migration pattern.

- If the cluster is v1.20 or later and the text is in the payload, prefer BM25 on a full-text index over sparse vectors: create a `text` index with `scoring` enabled (plus the language's stemmer and stopwords) on the text field and add a `text` query prefetch next to the dense one. It needs no schema change, no vector backfill, and no `avg_len`, and works on a collection of any age. Re-indexing a large collection can take a while [BM25 on a full-text index](https://skills.qdrant.tech/md/documentation/search/text-search/full-text-search/?s=bm25)
- Use the sparse vector options below on v1.19 or earlier, or when the text isn't stored in the payload.

- If the cluster is v1.18 or later, add the sparse vector field directly, even if the dense vector is unnamed [Update vector schema](https://skills.qdrant.tech/md/documentation/manage-data/collections/?s=update-vector-schema)
- Generate sparse vectors for existing points and backfill with `UpdateVectors`; existing dense vectors stay as they are [Update vectors](https://skills.qdrant.tech/md/documentation/manage-data/points/?s=update-vectors)

Expand All @@ -82,7 +86,7 @@ Use when: adding sparse/BM25 vectors to an existing dense-only collection. Most
- Scroll the old collection with `with_vectors=True` to copy the dense vectors, and generate only the sparse vectors
- Migrate payloads, swap alias

Sparse vectors at chunk level have different TF-IDF characteristics than document level. Test retrieval quality after migration, especially for non-English text without stop-word removal.
Sparse vectors and full-text BM25 statistics at chunk level have different TF-IDF characteristics than document level. Test retrieval quality after migration, especially for non-English text without stop-word removal.


## Re-embedding Is Too Slow
Expand Down
4 changes: 2 additions & 2 deletions skills/qdrant-monitoring/setup/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ Use when: configuring Kubernetes health checks.

Use when: setting up alerts for production or Hybrid Cloud deployments.

- Qdrant Cloud provides pre-configured email alerts for 11 cluster issues. Use scraped metrics when you need custom alerts or routing to other targets. See [Cloud cluster monitoring](https://skills.qdrant.tech/md/documentation/cloud/cluster-monitoring/)
- Qdrant Cloud provides pre-configured email alerts for 11 cluster issues. Use scraped metrics when you need custom alerts or routing to other targets. See [Cloud cluster monitoring](https://skills.qdrant.tech/md/documentation/cloud/cluster-monitoring/)
- Use AlertmanagerConfig to route alerts to Slack, PagerDuty, or other targets based on labels
- At minimum, alert on: optimizer errors, node not ready, replication factor below target, disk usage >80%

Expand All @@ -50,7 +50,7 @@ Use when: enterprise compliance requires centralized logs or audit trails.

- Enable JSON log format for structured analysis: set `logger.format` to `json` in config [Configuration](https://skills.qdrant.tech/md/documentation/ops-configuration/configuration/)
- Use FluentD/OpenSearch for log aggregation
- Audit logs (v1.17+) write to local filesystem (`/qdrant/storage/audit/`), not stdout. Mount a Persistent Volume and deploy a sidecar container to tail these files to stdout so DaemonSets can pick them up. [Audit logging](https://skills.qdrant.tech/md/documentation/security/?s=audit-logging)
- Audit logs (v1.17+) write to local filesystem (`/qdrant/storage/audit/`) by default. On v1.20 or newer, set `audit.output` to `stdout`, `stderr`, `file_and_stdout`, or `file_and_stderr` (env var `QDRANT__AUDIT__OUTPUT`) so a DaemonSet can collect them with no sidecar or Persistent Volume. Each line carries `"kind": "audit"` to tell it from regular logs, and the `/audit/logs` API returns nothing on a node that writes only to a stream. On v1.19 or older, mount a Persistent Volume and deploy a sidecar container to tail the files to stdout. [Audit logging](https://skills.qdrant.tech/md/documentation/security/?s=audit-log-output)


## What NOT to Do
Expand Down
1 change: 1 addition & 0 deletions skills/qdrant-multitenancy/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ One collection holds every tenant. A payload field marks ownership, and a filter

- Create a keyword payload index on the tenant field with `is_tenant=true` (the flag requires v1.11+). `is_tenant` tells Qdrant the field identifies tenants, so each tenant's vectors are stored together and served by sequential reads. Check [](https://api.qdrant.tech/api-reference/indexes/create-field-index).
- At query time, isolate each tenant with a `must` filter on the tenant field. Without it, a query searches every tenant's data. Check [Payload-based multitenancy](https://skills.qdrant.tech/md/documentation/manage-data/multitenancy/?s=partition-by-payload).
- Text relevance (BM25 or sparse vectors with the IDF modifier) is scored with statistics computed over the whole shard, so tenants with different vocabularies distort each other's scores. On Qdrant 1.19 or newer, pass the `idf` search parameter with the tenant filter and index the tenant field; on 1.20 or newer this covers BM25 `text` queries on a full-text index too [Per-tenant scoring statistics](https://skills.qdrant.tech/md/documentation/manage-data/multitenancy/?s=per-tenant-scoring-statistics).
- With this strategy, the indexing speed might become a bottleneck at scale because every tenant indexes into the same collection. To avoid this, you can disable the global HNSW creation (for the entire collection) and only build per-tenant indexes: set `m=0` and `payload_m` to a non-zero value. Although this accelerates the indexing process, keep in mind that requests without a tenant filter will become slower as they must scan all groups. So only make this trade if you hit the bottleneck and cross-tenant search is rare. [Calibrate performance](https://skills.qdrant.tech/md/documentation/manage-data/multitenancy/?s=calibrate-performance).


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ description: "Diagnoses and reduces Qdrant memory usage. Use when someone report

Qdrant operates with two types of memory:

- Resident memory (aka RSSAnon) - memory used for internal data structures like the ID tracker, plus components that stay fully in RAM. On Qdrant 1.19 or newer this is controlled per-component with `memory: pinned` (e.g. quantized vectors, payload indexes); see [memory tier legacy settings](https://skills.qdrant.tech/md/documentation/ops-configuration/memory-tiers?s=legacy-settings) for deployments on version 1.18 or older.
- Resident memory (aka RSSAnon) - memory used for internal data structures like the ID tracker, plus components that stay fully in RAM. As of Qdrant 1.20 the ID tracker has its own `memory` tier (default `pinned`; `cached` lets the OS evict it, `cold` needs no up-front RAM), where it was always resident before. On Qdrant 1.19 or newer this is controlled per-component with `memory: pinned` (e.g. quantized vectors, payload indexes); see [memory tier legacy settings](https://skills.qdrant.tech/md/documentation/ops-configuration/memory-tiers?s=legacy-settings) for deployments on version 1.18 or older.

- OS page cache - memory used for caching disk reads, which can be released when needed. Original vectors are normally stored in page cache, so the service won't crash if RAM is full, but performance may degrade. On Qdrant 1.19 or newer this corresponds to `memory: cached` (pre-warmed into page cache at startup) or `memory: cold` (lazy disk reads, not pre-warmed); on 1.18 or older it's controlled via the `on_disk` boolean on vectors, HNSW config, sparse vector index, and payload index. See [Memory Tiers docs](https://skills.qdrant.tech/md/documentation/ops-configuration/memory-tiers/) (available on 1.19+).

Expand Down Expand Up @@ -41,17 +41,17 @@ There are some scenarios, however, when it can be a good option:

- Deployments with low latency disks - local NVMe or similar.
- Multi-tenant deployments, where only a subset of tenants is frequently accessed, so that only a fraction of data & index is loaded in RAM at a time.
- For deployments with [inline storage](https://skills.qdrant.tech/md/documentation/ops-optimization/optimize/?s=inline-storage-in-hnsw-index) enabled.
- For deployments with [inline storage](https://skills.qdrant.tech/md/documentation/ops-optimization/optimize/?s=inline-storage-in-hnsw-index) enabled. On Qdrant 1.20 or newer, inline storage keeps the original vectors only in the HNSW index file, so `hnsw_config.memory` decides their tier and `vectors.memory` is ignored for those segments. Unless quantization has its own explicit `memory`, quantized vectors are `cold` when the HNSW index is `cold` and `pinned` in any other tier, so set it explicitly (for example `pinned`) if the index is `cold` but you want the quantized vectors in RAM [Inline storage](https://skills.qdrant.tech/md/documentation/ops-configuration/memory-tiers/?s=inline-storage).


## How to minimize memory footprint

The main challenge is to put on disk those parts of data, which are rarely accessed.
Here are the main techniques to achieve that:

- Use quantization to store only compressed vectors in RAM [Quantization docs](https://skills.qdrant.tech/md/documentation/manage-data/quantization/)
- Use quantization to store only compressed vectors in RAM. On Qdrant 1.20 or newer, 8-bit TurboQuant (`bits8`, 4x compression) is the recommended default over scalar quantization at the same compression (use scalar for the Manhattan metric), and it doesn't rescore by default, unlike `bits2`, `bits1_5`, and `bits1` [Quantization docs](https://skills.qdrant.tech/md/documentation/manage-data/quantization/)

- Use float16 or uint8 datatypes to reduce memory usage of vectors by 2x or 4x respectively, with some tradeoff in precision. On Qdrant 1.19 or newer, the `turbo4` datatype (TurboQuant-based, 4 bits/dimension, dense vectors only) reduces memory by ~8x, and can be paired with 1-bit quantization for cheaper rescoring than pairing 1-bit quantization with full-precision vectors. Read more about vector datatypes in [documentation](https://skills.qdrant.tech/md/documentation/manage-data/vectors/?s=datatypes)
- Use float16 or uint8 datatypes to reduce memory usage of vectors by 2x or 4x respectively, with some tradeoff in precision. `uint8` only makes sense for vectors already quantized to 0-255. On Qdrant 1.19 or newer, the TurboQuant datatypes replace the original vectors with a compressed form (dense vectors and multivectors, not sparse): `turbo4` (4 bits/dimension, ~8x, from 1.19), `turbo8` (~4x, closer to `float32` recall, from 1.20), and `turbo16` (~2x, from 1.20, closer to `float32` than `float16`). Any of them can be paired with 1-bit quantization for cheaper rescoring than pairing 1-bit quantization with full-precision vectors. A TurboQuant datatype is not TurboQuant quantization, which keeps the originals and adds a copy. Read more about vector datatypes in [documentation](https://skills.qdrant.tech/md/documentation/manage-data/vectors/?s=datatypes)

- Leverage Matryoshka Representation Learning (MRL) to store only small vectors in RAM while keeping large vectors on disk. Examples of how to use MRL with Qdrant Cloud inference: [MRL docs](https://skills.qdrant.tech/md/documentation/inference/matryoshka-models/?s=reduce-vector-dimensionality-with-matryoshka-models)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@ Use when: filtered search is significantly slower than unfiltered. Most common S
- Create payload index on the filtered field [Payload index](https://skills.qdrant.tech/md/documentation/manage-data/indexing/?s=payload-index)
- Use `is_tenant=true` for primary filtering condition: [Tenant index](https://skills.qdrant.tech/md/documentation/manage-data/indexing/?s=tenant-index)
- Try ACORN algorithm for complex filters: [ACORN](https://skills.qdrant.tech/md/documentation/search/search/?s=acorn-search-algorithm)
- Avoid using `nested` filtering conditions as a primary filter. It might force qdrant to read raw payload values instead of using index.
- `nested` filter conditions check each remaining candidate's payload element by element, which is slow when many candidates remain. On Qdrant 1.20 or newer, create per-element indexes (the `nested` parameter of the payload index) on every field the nested filter uses, so Qdrant answers it from the indexes without reading payload [Per-element index](https://skills.qdrant.tech/md/documentation/manage-data/indexing/?s=per-element-index-for-arrays-of-objects).
- Substring match (`match` with `substring`, 1.20+) is expensive: it checks every distinct value of the field, and Qdrant rejects it whenever strict mode is enabled (the Qdrant Cloud default). Prefer prefix match (keyword index with `prefix: true`) or full-text match where the use case allows. A keyword index, especially with `prefix`, speeds it up; a full-text index doesn't help [Substring match](https://skills.qdrant.tech/md/documentation/search/filtering/?s=substring-match)
- If payload index was added after HNSW build, trigger re-index to create filterable subgraph links


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ description: "Guides Qdrant vertical scaling decisions. Use when someone asks 'h

# What to Do When Qdrant Needs to Scale Vertically

Vertical scaling means increasing CPU, RAM, or disk on existing nodes rather than adding more nodes. This is the recommended first step before considering horizontal scaling. Vertical scaling is simpler and avoids distributed system complexity.
Vertical scaling means increasing CPU, RAM, or disk on existing nodes rather than adding more nodes. This is the recommended first step before considering horizontal scaling. Vertical scaling is simpler and avoids distributed system complexity.

- Vertical scaling for Qdrant Cloud is done through the [Qdrant Cloud Console](https://cloud.qdrant.io/)
- For self-hosted deployments, resize the underlying VM or container resources
Expand Down Expand Up @@ -35,7 +35,8 @@ Use when: current node resources (RAM, CPU, disk) are insufficient, but the work
- Estimate RAM per component (vectors, HNSW index, ID tracker, payload indexes) for the structures you keep in RAM, using the formulas in [Capacity planning](https://skills.qdrant.tech/md/documentation/capacity-planning/?s=calculating-ram-and-disk-size). For a full sizing walkthrough, see [qdrant-sizing](../../../qdrant-sizing/SKILL.md)
- Quantization adds a compressed copy alongside the original vectors. RAM for vectors drops only when the originals move to disk (`memory: cold`) and the quantized copy stays in RAM. With scalar quantization, the copy is 1/4 the size (INT8 reduces each float32 to 1 byte) [Quantization](https://skills.qdrant.tech/md/documentation/manage-data/quantization/)
- With binary quantization, the copy is 1/32 the size [Binary quantization](https://skills.qdrant.tech/md/documentation/manage-data/quantization/?s=binary-quantization)
- On Qdrant 1.19 or newer, the `turbo4` datatype (dense vectors only) divides by ~8 on its own, without needing separate quantization [Vector datatypes](https://skills.qdrant.tech/md/documentation/manage-data/vectors/?s=datatypes)
- On Qdrant 1.19 or newer, the TurboQuant datatypes shrink vectors on their own, without separate quantization: `turbo4` by ~8x (1.19+), `turbo8` by ~4x and `turbo16` by ~2x (both 1.20+, higher recall). They apply to dense vectors and multivectors [Vector datatypes](https://skills.qdrant.tech/md/documentation/manage-data/vectors/?s=datatypes)
- On Qdrant 1.20 or newer, the ID tracker can move to `cached` or `cold` memory tiers (previously always resident), which frees RAM at very high point counts [Memory tiers](https://skills.qdrant.tech/md/documentation/ops-configuration/memory-tiers/)
- Add overhead for the HNSW index (about 150 bytes per point at the default `m: 16`) and payload indexes; the WAL counts against disk, not RAM [Capacity planning](https://skills.qdrant.tech/md/documentation/capacity-planning/)
- Reserve 20% headroom for optimizer operations and OS cache
- Monitor actual usage via Grafana/Prometheus before and after resizing [Monitoring](../../../qdrant-monitoring/SKILL.md)
Expand Down
4 changes: 2 additions & 2 deletions skills/qdrant-search-quality/diagnosis/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ Use when: results are irrelevant or missing expected matches and you need to iso
- Check if filters are too restrictive (then you might need to use ACORN)
- If duplicate results from chunked documents, use Grouping API to deduplicate [Grouping](https://skills.qdrant.tech/md/documentation/search/search/?s=grouping-api)

Payload filtering and sparse vector search are different things. Metadata (dates, categories, tags) goes in payload for filtering. Text content goes in sparse vectors for search.
Payload filtering and ranked text search are different things. Metadata (dates, categories, tags) goes in payload for filtering. To rank text content, on Qdrant 1.20 or newer use BM25 on a full-text index with `scoring` enabled (queried with a `text` query); on older versions, or for learned sparse models, use sparse vectors. A full-text index without `scoring` only filters.

## Approximate Search Worse Than Exact

Expand Down Expand Up @@ -72,4 +72,4 @@ Use when: exact search also returns bad results and model choice is confirmed by
- Skip payload indexes on filtered fields then blame quality (HNSW can't traverse filtered-out nodes, and filterable HNSW is built only if payload indexes were set up prior)
- Deploy without baseline recall or other search relevance metrics (no way to measure regressions)
- Compare two configs on a query set too small to resolve the difference between them (the result is noise, not evidence)
- Confuse payload filtering with sparse vector search (different things, different config)
- Confuse payload filtering with ranked text search (different things, different config)
Loading
Loading