Skip to content

Routing overhaul: measured eval harness, hijack-proof corpus, top-3 pooling - #35

Merged
bradflaugher merged 1 commit into
mainfrom
feat/routing-eval-and-corpus-overhaul
Jun 10, 2026
Merged

bradflaugher merged 1 commit into
mainfrom
feat/routing-eval-and-corpus-overhaul

Conversation

@bradflaugher

Copy link
Copy Markdown
Owner

Summary

Routing quality is now measured, not guessed — and dramatically better. A new offline eval harness scores both routers against a held-out benchmark of 353 labeled real-world queries:

Router Before After
Semantic (in-browser model) 76.8% 98.9%
Keyword (?lite=1 / low-memory) 45.9% 97.5%

No destinations added or removed; same engines, same bangs.

What changed

  • scripts/eval_routing.py (new): replicates the browser routing pipeline exactly (same quantized ONNX MiniLM, same scoring math) and reports per-engine accuracy plus the exact corpus phrase that "won" each misroute — corpus debugging is now mechanical. scripts/routing_benchmark.json (new) holds the labeled queries; the validator enforces it stays held-out of the training corpus.
  • Corpus rewrite (scripts/search_phrases.json): removed every single-word "attractor" phrase (buy, music, guide, gif, twitter… each one hijacked unrelated queries under nearest-neighbor pooling), sharpened intent boundaries — Brave = synthesized answers / creative / live sentiment, DDG = simple facts + navigation, Wirecutter = new-product buying advice, eBay = used/vintage/parts — and expanded keyword rules so lite mode covers every engine (eBay was at 9%, HN 6%, YouTube 13%; all now 93–100%).
  • Scoring upgrade (index.html): scoreAll() now uses the mean of each route's top-3 cosine similarities instead of pure max-pooling, so one stray phrase can't hijack a route (+1.7pts measured, structurally more robust).
  • Bug fixes: crash path when search-config.json fails to load but the model loads (semantic result could name an engine the UI can't render); !gh placeholder advertised a bang that doesn't exist; MIN_QUERY_LEN_FOR_MODEL comment/code mismatch.
  • Validator hardening (scripts/validate_config.py): rejects single-word phrases outside the ddg route, duplicate phrases across routes, and benchmark leakage into the corpus.
  • Test fix: ?q=how+to+make+pizza asserted a non-DDG routing, but a simple recipe lookup should go to DDG under the routing philosophy; swapped to a video-shaped how-to with a wide non-DDG margin.
  • README documents measured accuracy, the eval workflow, and the phrase-writing rules.

Test plan

  • python3 scripts/eval_routing.py — 98.9% / 97.5% on the held-out benchmark, 4 remaining misses all sub-0.05-margin ambiguous cases
  • npm run lint (ESLint + Ruff + config validator)
  • Playwright: 173/173 pass on Chromium + Firefox locally (WebKit can't launch on the local Fedora host — missing Ubuntu-built deps, pre-existing; CI runs it on ubuntu-latest)

🤖 Generated with Claude Code

…ooling

Routing accuracy measured on a new held-out benchmark of 353 labeled
queries (scripts/routing_benchmark.json):

  semantic router:  76.8% -> 98.9%
  keyword (?lite=1): 45.9% -> 97.5%

What changed:

- scripts/eval_routing.py: offline eval that replicates the browser
  pipeline exactly (same quantized ONNX model, same scoring); reports
  per-engine accuracy and names the corpus phrase that won each
  misroute. Run after any corpus edit.
- scripts/search_phrases.json: full corpus rewrite. Removed all
  single-word "attractor" phrases ('buy', 'music', 'guide' each caused
  multiple hijacks), sharpened intent boundaries (brave = synthesized
  answers / creative / live sentiment, ddg = simple facts + navigation,
  wirecutter = new-product buying advice, ebay = used/vintage/parts),
  expanded keyword rules so lite mode covers every engine (ebay was 9%,
  hn 6%, youtube 13%).
- index.html scoreAll(): max-pooling -> mean of top-3 neighbors, so a
  single stray phrase can't hijack a route (+1.7pts measured). Also:
  drop undispatchable routes when config load fails but the model
  loads (crash fix), remove '!gh' placeholder (no such bang), bump
  EMBEDDINGS_VERSION to 15.
- scripts/validate_config.py: enforce corpus rules — no single-word
  phrases outside the ddg route, no phrase in two routes, benchmark
  queries must stay held-out of the corpus.
- tests: the '?q=how+to+make+pizza routes non-DDG' assertion conflicted
  with recipes correctly being simple-DDG lookups; switched to a
  video-shaped how-to with a wide non-DDG margin.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@bradflaugher
bradflaugher merged commit 9b3fdf9 into main Jun 10, 2026
7 checks passed
@bradflaugher
bradflaugher deleted the feat/routing-eval-and-corpus-overhaul branch June 10, 2026 12:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant