Native vector index integrity

The Rust qilbee_memory::HnswIndex is an approximate in-process vector index. This guide describes its numeric input, replacement and snapshot contracts. It is separate from the platform HTTP cosine and hybrid retrieval paths: this change does not alter HTTP scores, ranking versions, tenant authorization or embedding identity requirements.

Validate before indexing#

Use HnswIndex::try_new(config) to report invalid configuration immediately. The existing new(config) constructor remains source-compatible; insert, search and snapshot operations validate its configuration before using it. The native agent's with_semantic_search also rejects invalid index configuration.

Vectors must be nonempty, finite and match the configured dimension. If no dimension is configured, the first accepted insertion establishes it. Rejected input does not establish dimensions or replace an existing vector. Cosine also requires at least one nonzero component. Zero vectors are permitted for the other two metrics.

The native index does not impose a 1,536-dimension ceiling. Tests cover cosine at 3,072 and 32,768 dimensions, including the smallest positive f32 and f32::MAX. These are correctness cases, not latency or relevance benchmarks. Memory consumption and retrieval time still depend on dimensions and corpus size. Callers supply vectors; this index does not generate embeddings or verify that two vectors belong to the same model space.

Configuration requires m >= 2 with representable 2*m, positive construction and search candidate counts, a finite positive ml, a positive explicit dimension, and max_level < usize::BITS. The existing level-generation policy is unchanged by this feature.

Distance representation#

Products, sums and norms use f64 intermediates; returned distances remain f32. Smaller distances rank first.

Metric Returned distance Accepted magnitude
Cosine 1 - cosine, bounded to [0, 2] Any finite, nonzero f32 vector
Dot product Negative dot product Squared vector norm no greater than f32::MAX
Euclidean Euclidean distance Vector norm no greater than f32::MAX / 2

The dot-product and Euclidean norm bounds apply to stored vectors and queries. They conservatively ensure that distances between accepted inputs fit the public f32 result type. Inputs outside these bounds return an error instead of introducing infinite distances. This tightens acceptance of extreme legacy inputs; normalize or otherwise transform vectors consistently outside the index, then rebuild, if the application's model permits that transformation.

A request for k results uses at least max(ef_search, k) search candidates. It is no longer silently capped at ef_search. This does not guarantee k results or exhaustive nearest neighbors: the corpus may be smaller or its reachable graph may contain fewer nodes. Increasing k increases search work; callers must impose appropriate resource limits.

Replace and remove#

Inserting an existing identifier replaces its vector while preserving its assigned level and existing neighborhood before refreshing connections. It does not create self-connections. This prevents an entry-point replacement from first discarding the graph's outgoing links. It does not guarantee that updates preserve measured recall across arbitrary datasets.

Removal clears every incoming reference, including links whose reverse edge was previously pruned. Removing the last record clears the entry point and maximum occupied level. Removal scans the graph; this is an integrity contract, not a constant-time deletion claim. Remaining connectivity and retrieval quality must be evaluated for the application's update/deletion workload.

Load and recover snapshots#

The serialized field layout remains unchanged. A fixed-width legacy fixture is loaded, searched and written back byte-for-byte in compatibility tests. Snapshots must describe an existing entry point at the highest occupied level, consistent dimensions and node identities, valid per-node layer shapes, and neighbors that exist at the referenced layers. Input/configuration validation also applies to loaded vectors. Empty snapshots cannot retain a live entry point or occupied level. Trailing bytes are rejected.

Invalid snapshots return an error; the loader does not silently discard records or repair topology. Preserve the original bytes and rebuild a separate index from authoritative vectors and identifiers. Validate its contents and retrieval before replacing the old index. Previously accepted inconsistent snapshots or out-of-range vectors may now require this recovery procedure.

Structural validation does not authenticate snapshots, prove graph connectivity, prove exhaustive retrieval or impose an overall process memory budget. Accept snapshot files only from trusted storage and enforce size/resource limits at the application boundary. This in-process serializer is not itself an atomic disk backup or crash-recovery protocol.

Evidence and limitations#

Regression tests cover invalid-input rejection, finite high-dimensional cosine, requested counts beyond ef_search, entry-point replacement, broken snapshot references and dimensions, asymmetric deletion, empty-index recovery and legacy layout compatibility. These results demonstrate the tested integrity contracts. They do not establish better relevance, agent reasoning, production HTTP performance or state-of-the-art ANN performance.

The architectural reference is Malkov and Yashunin's HNSW paper. This implementation's construction, level policy and update workload need separate comparative qualification before making algorithm-equivalence or performance claims.