- Recall — the fraction of truly relevant results that the system returns. If there are 10 relevant documents and the system finds 8, recall is 80%.
- Precision — the fraction of returned results that are actually relevant. If the system returns 10 results and 7 are relevant, precision is 70%.
- Measure — Establish a ground truth baseline with exact search.
- Tune HNSW — Adjust
m,ef_construct, andhnsw_effor the recall–speed trade-off. - Choose distance — Pick the right metric for your embeddings.
- Tune search-time parameters — Use
hnsw_efandexactmode. - Use multistage prefetch — Widen the candidate pool then rerank.
- Apply payload filters — Accelerate filtered search.
- Set score thresholds — Cut noise at the right level.
- Rebuild and compact — Keep index quality fresh after updates.
Environment setup
Run the following command to install the Python packages required for all code samples in this tutorial.Step 1: Create a test collection and ingest data
This step sets up the shared imports, constants, and embedding helpers used throughout the tutorial. Running this block loads theall-MiniLM-L6-v2 model, defines two encoding functions, and establishes the server address and collection name that all subsequent steps reference.
Expected output
This block embeds all 30 corpus documents, constructsPointStruct objects pairing each vector with its text and category payload, upserts them into the Retrieval-Quality collection using cosine distance and default HNSW settings (m=16, ef_construct=128), flushes the writes to disk, and queries the server for the total stored vector count.
Step 2: Establish a baseline with exact search
Before tuning anything, measure ground truth. Anexact (brute-force) search scans every vector in the collection and returns the mathematically correct nearest neighbours with 100% recall. Every tuning step in this tutorial should be measured against this baseline.
The following block defines three functions: exact_search, which runs a brute-force scan; approx_search, which uses the HNSW index with an optional hnsw_ef override; and compute_recall, which calculates what fraction of the exact top-K results the approximate search also returned. Running the block then queries both functions for the same input and prints a side-by-side comparison. Every tuning step in this tutorial should be measured against this baseline.
Expected output
This block queries the same sentence using bothexact_search (brute-force scan) and approx_search (HNSW traversal), then computes recall@10. When both result sets share identical IDs, recall reaches 100%, confirming the HNSW index is producing no approximation error on this query.
Step 3: Tune HNSW index parameters
The HNSW index has two sets of parameters: build-time parameters that affect the quality of the graph structure stored on disk, and search-time parameters that affect how many nodes the query traverses at runtime.Build-time parameters: m and ef_construct
m and ef_construct are set when creating the collection. Once set, changing them requires recreating the index. The following block creates four collections at different quality levels and measures recall across all of them.
| Parameter | Low | Default | High | Max |
|---|---|---|---|---|
m | 4 | 16 | 32 | 64 |
ef_construct | 32 | 128 | 256 | 512 |
| Build speed | Fastest | Fast | Slower | Slowest |
| Memory usage | Lowest | Moderate | Higher | Highest |
| Recall potential | Lower | Good | Better | Best |
m— The number of bi-directional links created for each node. Higher values produce a denser graph with more traversal paths, which improves recall at the cost of memory and build time.ef_construct— The search width used during index construction. Higher values produce a better-connected graph. Set this to at least2 * m.
Measure recall across configurations
Expected output
This block embeds the query, fetches exact ground-truth results from the baseline collection, then runs the same approximate search against each of the four HNSW collections and computesrecall@10 for each. The low configuration uses a sparse graph that misses some traversal paths; default and above close that gap entirely.
All configurations return 100% recall on this small 30-doc corpus. Differences become visible at larger scale (1M+ vectors).
Search-time parameter: hnsw_ef
hnsw_ef controls how many candidate nodes the search explores at query time. It can be set per request without rebuilding the index, which makes it the primary knob for trading latency against recall at runtime.
Expected output
This block sweeps six values ofhnsw_ef and for each measures recall against the exact baseline and wall-clock latency.
AllThe following table mapshnsw_efvalues return 100% recall on this small corpus. Latency differences are minimal; at larger scale higherefvalues trade measurable latency for better recall.
hnsw_ef ranges to their typical recall and latency characteristics.
hnsw_ef | Recall | Latency | Use case |
|---|---|---|---|
| 16–32 | Lower | Fastest | Real-time autocomplete, high QPS |
| 64–128 | Good | Fast | General search, most applications |
| 256–512 | Excellent | Slower | High-accuracy retrieval, RAG |
| Exact mode | Perfect | Slowest | Evaluation, ground truth |
Step 4: Choose the right distance metric
The distance metric defines what the index considers “similar”. Choosing the wrong metric for your embedding model produces systematically lower recall regardless of any other tuning.| Embedding model | Recommended metric | Why |
|---|---|---|
all-MiniLM-L6-v2 | Cosine | Produces normalized vectors. |
text-embedding-3-small | Cosine | Normalized by default. |
| CLIP (ViT-B-32) | Cosine | Normalized image/text embeddings. |
| Custom non-normalized | Dot or Euclid | Magnitude carries meaning. |
| Sparse/hybrid | Dot | Standard for TF-IDF/BM25. |
Expected output
all-MiniLM-L6-v2 because it produces unit-normalized vectors (cosine = dot product for unit vectors). Euclid returns distance values — lower is more similar, which inverts the ranking intuition.
Step 5: Use multistage prefetch to widen the candidate pool
A single HNSW traversal only explores one path through the graph. If the most relevant documents sit in a different region of the vector space — for example, in a specific category — that path may never reach them. Multi-stage prefetch runs several candidate-gathering passes in parallel, then reranks the combined pool.| Approach | Candidate pool | Trade-off |
|---|---|---|
| Single search | Top-K from one pass. | May miss results outside the HNSW traversal path. |
| Prefetch (unfiltered) | Broader initial pool, then rerank. | Catches near-misses from the same vector region. |
| Prefetch (multifilter) | Candidates from different payload categories. | Ensures diversity across category boundaries. |
| Prefetch (multivector) | Candidates from different embedding spaces. | Enables cross-perspective matching. |
limit=5 reranks from the union of all prefetched candidates. Even if one prefetch path misses a relevant result, another path may find it.
Expected output
id=6 (ml) from rank 3 to rank 2, reflecting that the ML-filtered prefetch stream also returned it as a top candidate — consensus across streams boosts its position.
Step 6: Set the right score threshold
A score threshold rejects any result whose similarity score falls below a minimum value. Setting it too low returns noisy, irrelevant results. Setting it too high discards valid matches. The right threshold depends on the score distribution of your specific embedding model and corpus.Expected output
This block runsthreshold_analysis with the query “machine learning and neural network training” and the known-relevant set {5, 6, 7, 8, 9, 10, 11}. It applies seven score thresholds and prints precision and recall at each level.
- At threshold 0.2 — 87.5% precision, 100% recall — good balanced starting point.
- At threshold 0.3 — 100% precision, 71% recall — trades some recall for clean results.
- At threshold 0.4+ — fewer than 3 results remain; too aggressive for this corpus.
Step 7: Rebuild and compact for sustained quality
After many updates and deletions, index quality degrades over time. Deleted vectors leave tombstones that waste memory and slow search. Segments accumulate and fragment, reducing scan locality. The following block runs a full rebuild, optimization, and compaction sequence.Expected output
| Operation | When to run | Impact |
|---|---|---|
rebuild_index | After bulk updates (more than 20% of data changed). | Rebuilds HNSW graph for better recall. |
optimize | Periodically (daily or weekly). | Merges small segments for better locality. |
compact_collection | After many deletions. | Purges tombstones and reclaims memory. |
flush | After any write operation. | Persists data to disk. |
Step 8: Update HNSW config without rebuilding data
collections.update lets you change HNSW parameters on an existing collection without re-ingesting any data. This is useful when you start a project with conservative settings for fast iteration and want to raise quality before going to production.
m and ef_construct values keeps build times fast during development. Increasing them before deployment raises the recall ceiling without requiring any data migration.
Retrieval quality checklist
The following tables summarize every lever available in Actian VectorAI DB for optimizing retrieval quality, grouped by when the parameter takes effect.Collection-level settings (set once, rebuild to change)
These parameters are fixed at collection creation time. Changing them requires recreating the index.| Lever | Parameter | Effect on quality |
|---|---|---|
| Distance metric | Distance.Cosine / Dot / Euclid | Defines similarity semantics. |
| HNSW connectivity | HnswConfigDiff(m=16) | Higher m = denser graph = better recall. |
| HNSW build quality | HnswConfigDiff(ef_construct=128) | Higher = better-connected graph. |
Query-level settings (adjustable per search)
These parameters can be tuned on every search request without changing or rebuilding the index.| Lever | Parameter | Effect on quality |
|---|---|---|
| Search width | SearchParams(hnsw_ef=128) | Higher = more accurate, slower. |
| Exact mode | SearchParams(exact=True) | Perfect recall, no approximation. |
| Score threshold | score_threshold=0.5 | Removes low-confidence results. |
| Multi-stage prefetch | PrefetchQuery(query=..., filter=..., limit=20) | Widens candidate pool from multiple angles. |
Operational maintenance (run periodically)
Run these operations on a schedule to keep retrieval quality from degrading as data changes over time.| Lever | Method | Effect on quality |
|---|---|---|
| Index rebuild | vde.rebuild_index() | Refreshes HNSW graph after bulk changes. |
| Optimization | vde.optimize() | Merges segments for better locality. |
| Compaction | vde.compact_collection() | Purges deleted vectors and reclaims memory. |
Next steps
- Similarity search — Learn the core retrieval workflow.
- Predicate filters — Combine vector search with structured payload constraints.
- Reranking search results — Improve relevance with cross-encoder and reciprocal rank fusion reranking.
- Open-source embedding models — Integrate open-source models like Sentence Transformers and BGE.