feat(agent): HDF5Memory::search with source filters, re-ranking, confidence
`HDF5Memory::search(query_embedding, query_text, &SearchOptions)` is the store's full search path. `SearchOptions::new(k)` is plain hybrid search with the tuned default fusion; each further stage is opt-in: - `with_sources([..])`: only records from these source channels. The filter applies before ranking, so a filtered search still returns up to k results, normalised over what it can return. The HNSW pool is over-fetched in proportion to what the filter removes, and the allowed records are scanned exactly whenever that costs fewer distance evaluations than the index would (~pool x M) — and as the fallback if the pool comes back short. Keyword matches are filtered too. - `with_rerank(ReRankConfig)` re-ranks a max(3k, 10) candidate pool by relevance, recency, source authority and activation; `with_confidence(ConfidenceConfig)` drops low-confidence results; `at_time(now)` pins the recency clock. These were reachable only through the OpenClaw backend, which is now `search` with both on. Its Hebbian boost now goes to the k results it returns rather than the whole 3k candidate pool. `hybrid_search` and `hybrid_search_with` are wrappers and unchanged (tested bit for bit). Measured on tank (search_harness --options-study --full, 3 runs): at 100K every filter — 50%, 10%, 1% of the store, and records far from the query — returns the exact filtered top 10, and none is slower than an unfiltered search (1%: 2.3 ms vs 4.6 ms). Re-rank + confidence costs about 3%. A first version decided between index and exact scan by pool size vs store size; it measured 0.976 recall at 12.3 ms on the far-from-query filter, which is why the rule compares costs instead. Tests: tests/search_options.rs (filter correctness and full pages via both paths, far-from-query fallback, edge cases, equality with hybrid_search_with, re-rank recency, confidence, boost scope). Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
@@ -149,6 +149,49 @@ vectors, and recall is measured against brute-force ground truth rather than
|
||||
against the f32 index, whose own approximation errors a re-scored search is
|
||||
entitled to get right.
|
||||
|
||||
### Search options: source filters, re-ranking, confidence
|
||||
|
||||
Measured 2026-09-24 on tank (AMD Ryzen 7 7800X3D). `HDF5Memory::search`
|
||||
with `SearchOptions`, clustered 384-dim data, k = 10, Hebbian boosting off.
|
||||
Filters keep 50%, 10% or 1% of the store at random, or two whole clusters
|
||||
chosen *away* from each query's own — the case an ANN index handles worst,
|
||||
because nothing it finds near the query is allowed. Recall is vector-only
|
||||
against an exact scan of the allowed records; latency is full hybrid search
|
||||
(vector + BM25). 200 queries, medians of three runs (recall was identical in
|
||||
every run).
|
||||
|
||||
```bash
|
||||
cargo run --release -p clawhdf5-bench --bin search_harness -- --options-study --full
|
||||
```
|
||||
|
||||
| N | options | filtered recall@10 | p50 ms | p99 ms |
|
||||
|---:|---|---:|---:|---:|
|
||||
| 10 000 | no filter | 1.0000 | 0.488 | 0.518 |
|
||||
| 10 000 | random 50% | 1.0000 | 0.520 | 0.563 |
|
||||
| 10 000 | random 10% | 1.0000 | 0.342 | 0.367 |
|
||||
| 10 000 | random 1% | 1.0000 | 0.220 | 0.239 |
|
||||
| 10 000 | 2 clusters away from the query | 1.0000 | 0.252 | 0.278 |
|
||||
| 10 000 | re-rank | — | 0.562 | 0.674 |
|
||||
| 10 000 | re-rank + confidence | — | 0.554 | 0.575 |
|
||||
| 100 000 | no filter | 0.9995 | 4.600 | 5.245 |
|
||||
| 100 000 | random 50% | 1.0000 | 4.770 | 5.614 |
|
||||
| 100 000 | random 10% | 1.0000 | 3.244 | 4.008 |
|
||||
| 100 000 | random 1% | 1.0000 | 2.288 | 2.943 |
|
||||
| 100 000 | 2 clusters away from the query | 1.0000 | 2.298 | 3.048 |
|
||||
| 100 000 | re-rank | — | 4.747 | 5.411 |
|
||||
| 100 000 | re-rank + confidence | — | 4.748 | 5.526 |
|
||||
|
||||
A filtered search finds the exact filtered top 10 and is never slower than an
|
||||
unfiltered one. The filter applies before ranking — over-fetching the index in
|
||||
proportion to what the filter removes, and scanning the allowed records
|
||||
exactly whenever that costs fewer distance evaluations than the index would
|
||||
(roughly `pool × M`). The first version compared the over-fetch to the store
|
||||
size instead, and that measured badly at 100K: the 1% filter took 5.9 ms at
|
||||
recall 0.9965 and the away-from-query filter 12.3 ms at 0.976, both through an
|
||||
index asked for ~16 000 candidates, where scanning the few hundred or thousand
|
||||
allowed records is exact and cheap. Re-ranking a 3k candidate pool and
|
||||
confidence rejection add about 3%.
|
||||
|
||||
### float16 embedding storage (`MemoryConfig::float16`)
|
||||
|
||||
Measured 2026-09-23 on tank (AMD Ryzen 7 7800X3D). The same clustered
|
||||
|
||||
Reference in New Issue
Block a user