Skip to content

Add RAG layer: semantic search + grounded explanations - #13

Merged
bryanshao07 merged 13 commits into
mainfrom
feature/rag-layer
Jul 31, 2026
Merged

bryanshao07 merged 13 commits into
mainfrom
feature/rag-layer

Conversation

@bryanshao07

Copy link
Copy Markdown
Owner

Summary

Adds a unified retrieval layer to the CrossOver backend, powering two features from one precomputed artifact set:

  • Semantic player search — GET /search?mode=semantic embeds a natural-language query and ranks players by cosine similarity over precomputed style-card embeddings. Falls back to the existing substring search when unavailable.
  • Grounded explanations — /explain now injects each player's retrieved style card into the Gemini prompt, so bullets reference concrete stylistic facts instead of paraphrasing raw attribute numbers. (Replaces the previously-unused stats_a/stats_b params.)

Everything degrades gracefully: with no artifacts or no GEMINI_API_KEY, the app behaves exactly as before.

How it works

  • Offline build script backend/scripts/build_rag.py generates a ~3-sentence style card per player from their raw stat line (gemini-2.5-flash) and embeds it (text-embedding-004), writing exports/style_cards.json + exports/style_embeddings.json (idempotent/resumable, atomic writes).
  • data_store loads these once at startup into an L2-normalized NumPy matrix (same in-memory approach as sim_matrix.csv — no vector DB; ~585 players). Resilient to both missing and corrupt artifacts.
  • services/rag.py embeds queries (lru_cached) and ranks by cosine.

Design & plan

  • Spec: docs/superpowers/specs/2026-07-29-rag-layer-design.md
  • Plan: docs/superpowers/plans/2026-07-29-rag-layer.md

Testing

New hermetic tests for the data layer, rag service, semantic search, explanation grounding, and the build script's prompt builder. Full backend suite green except 3 pre-existing auth tests that require a local PostgreSQL (unrelated to this branch).

Follow-up (not in this PR)

  • Generate + commit the artifacts (python -m scripts.build_rag). Currently pending — the available API key is rate/quota limited. Until the artifacts exist, semantic search falls back to keyword and explanations stay ungrounded (by design).
  • Frontend "smart search" toggle on the results page (separate increment).

🤖 Generated with Claude Code

@vercel

vercel Bot commented Jul 29, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
crossover Ready Ready Preview Jul 31, 2026 6:39am

bryanshao07 and others added 3 commits July 30, 2026 16:33
Rate-limit errors back off exponentially (honoring the API's retry_delay
hint when present) instead of dropping the player; other transient errors
back off briefly. Re-raises after MAX_RETRIES so a persistently-failing
call still surfaces. Adds hermetic tests for the retry logic.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The account's key 404s on gemini-2.5-flash ("no longer available to new
users") and text-embedding-004 (not served). Switch to gemini-flash-latest
for generation and gemini-embedding-001 for embeddings. Centralize both ids
in config as the single source of truth so the offline document build and the
runtime query embedding can never drift apart (a mismatch breaks cosine).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Generated via scripts/build_rag with gemini-flash-latest (cards) and
gemini-embedding-001 (3072-dim embeddings). 585/585 players covered, no
empties. Consistent with the other precomputed files in exports/.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@bryanshao07

Copy link
Copy Markdown
Owner Author

Artifacts generated + committed ✅

The RAG artifacts are now built and included in the branch:

  • 585/585 style cards (exports/style_cards.json, 318K) — 0 empty
  • 585/585 embeddings (exports/style_embeddings.json, 24M) — all 3072-dim, none missing

Model migration (required): the target account 404s on the originally-specced gemini-2.5-flash ("no longer available to new users") and text-embedding-004 (not served on this API version). Switched to gemini-flash-latest (cards) and gemini-embedding-001 (embeddings), with both IDs centralized in config.py as the single source of truth so the offline document build and the runtime query embedding can never drift apart. Also added 429/504 backoff+retry to the build script.

Live sanity check (real embeddings, mode=semantic):

  • "lockdown perimeter defender with elite passing" → Davion Mitchell, Kawhi Leonard, Derrick White, OG Anunoby
  • "high-volume three point sharpshooter" → DiVincenzo, A.J. Green, Grayson Allen, Stephen Curry
  • "dominant physical rim protector" → Clingan, Zubac, Anthony Davis, Mark Williams

Note on repo size: the embeddings file is 24 MB (3072-dim vectors). Committed per decision, consistent with other precomputed files in exports/. If repo weight is a concern later, gemini-embedding-001 supports output_dimensionality reduction (e.g. 768/1536) which would shrink it ~2–4× at a small recall cost — a one-line change + rebuild.

The comparison explanation now feeds each player's real numbers into the
prompt — absolute counting stats (PPG/APG/RPG, goals/assists) plus their top
percentile ranks — and instructs the model to wrap every stat/number/metric in
**double asterisks**. The frontend already renders **bold** via formatBullet;
fixed its parenthetical-highlight rule so it no longer swallows an already-
bolded "(AST **top 7%**)" and leaks literal asterisks.

Also hardened _parse_bullets so stripping the leading bullet marker can't eat
the opening ** of a bullet that starts with a bolded metric.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@bryanshao07
bryanshao07 merged commit 5748c1b into main Jul 31, 2026
4 checks passed
@bryanshao07
bryanshao07 deleted the feature/rag-layer branch July 31, 2026 06:45

This branch was successfully deployed

1 active deployment
Preview — 0485c222 Deployed Jul 31, 2026 by vercel[bot]
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