Skip to content
Open
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
6 changes: 6 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@ path = "src/bin/orangu-server/main.rs"
name = "orangu-bench"
path = "src/bin/orangu-bench/main.rs"

# Developer retrieval benchmark for the hybrid lexical/semantic search PoC.
# See src/bin/orangu-search-bench/README.md.
[[bin]]
name = "orangu-search-bench"
path = "src/bin/orangu-search-bench/main.rs"

# Builds a model file: trains one from a manifest of permissively-licensed
# repositories, or rewrites an existing one at another weight format.
# Documented in doc/manual/en/77-gguf.md.
Expand Down
56 changes: 56 additions & 0 deletions src/bin/orangu-search-bench/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# orangu-search-bench

A small retrieval benchmark for the hybrid lexical/semantic search proof of
concept. It runs the committed query fixture (`queries.json`) against:

- **lexical-only** — BM25 over the lexical sidecar
- **semantic-only** — cosine similarity over the embedding index
- **hybrid** — Reciprocal Rank Fusion of both

and reports MRR, Recall@5, mean latency, indexing time, and the lexical
sidecar's on-disk size.

```sh
# Lexical-only (no embeddings server required)
cargo run --release --bin orangu-search-bench -- --workspace .

# Semantic-only and hybrid
cargo run --release --bin orangu-search-bench -- --workspace . \
--endpoint http://127.0.0.1:8080 --model embeddings
```

`--verbose` prints the top lexical, semantic and hybrid hits per query. The fixture marks
standalone constants that the current Rust extractor does not emit (for example
`MAX_CHUNK_CHARS`) as `coverage_gap`; those are reported separately rather than
counted as ranking failures.

Queries are evaluated against the workspace as it exists, so test code that
contains the same identifiers or strings also participates in the ranking.
The fixture avoids phrases that only exist in test code.

## Results

Run on the `hybrid-search-phases` branch, 2026-11-14: 22,493 indexed symbols,
18 scored queries (10 conceptual, 7 exact, 1 phrase). Embeddings:
`ggml-org/embeddinggemma-300M-GGUF:Q8_0` (768 dims, CPU).

| metric | semantic-only | lexical-only | hybrid |
|---|---|---|---|
| overall MRR | 0.294 | 0.541 | 0.567 |
| overall Recall@5 | 0.444 | 0.667 | 0.667 |
| conceptual MRR (n=10) | 0.278 | 0.173 | 0.220 |
| conceptual Recall@5 (n=10) | 0.500 | 0.400 | 0.400 |
| exact MRR / Recall@5 (n=7) | 0.357 / 0.429 | 1.000 / 1.000 | 1.000 / 1.000 |
| phrase rank | miss | 1 (verified exact) | 1 (verified exact) |
| mean query latency | 85 ms | 6 ms | 101 ms |

Semantic and hybrid latency include one query embedding request.

| index | cold build | warm build | on disk |
|---|---|---|---|
| lexical sidecar | 10.8 s | 2.8 s | 43,927 KiB (42.9 MiB) |
| semantic index | ~4.8 h | 26 s | 210 MB |

Standalone constants (`MAX_CHUNK_CHARS`, `EMBED_BATCH_TOKEN_BUDGET`, `RRF_K`)
are reported as extraction coverage gaps and excluded from the metrics.

Loading