Spaces:
Running
Running
siddhm11 Claude Opus 5.5 commited on
Commit Β·
ce8a24c
1
Parent(s): 9856058
docs: log the 2026-09-30 multi-interest and starter-feed decisions
Browse filesDoc 06 changelog entry for the four approved changes, with the replay
measurements behind them; CLAUDE.md sections 3.3, 3.5 and the quick
reference, and the CURRENT-STATE contract rows, updated to match.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- CLAUDE.md +4 -3
- docs/CURRENT-STATE.md +4 -3
- docs/research/06-Deep-Research-Verdict.md +30 -0
CLAUDE.md
CHANGED
|
@@ -106,7 +106,8 @@ If you find `alpha_long = 0.10` anywhere in code or config, it is a bug from doc
|
|
| 106 |
- Algorithm: **Ward hierarchical agglomerative** via `scipy.cluster.hierarchy.ward`.
|
| 107 |
- Code lives in: `app/recommend/clustering.py`.
|
| 108 |
- **L2-normalize embeddings BEFORE Ward, then use Euclidean distance.** Cosine Ward via sklearn is mathematically not Ward (Murtagh and Legendre 2014). L2-norm + Euclidean is monotonically equivalent to cosine and gives the intended behavior. This normalization is already in the code.
|
| 109 |
-
- **No fixed K.** Cut the dendrogram by adaptive gap-based threshold (see `_adaptive_threshold()`). Cap at `K_max = 7` (currently; doc 06 says `K_max = 20` for heavy users β raise this when users exist).
|
|
|
|
| 110 |
- **Medoid, not centroid.** Medoid = arg min over cluster members of sum of squared distances. Cache medoid paper IDs. This is implemented in `_find_medoid()`.
|
| 111 |
- **Hungarian-match cluster IDs across reclusterings** β implemented via `stabilize_cluster_ids`.
|
| 112 |
- Recompute on each feed request currently (not nightly batch β no batch job infrastructure yet).
|
|
@@ -144,7 +145,7 @@ If you find `alpha_long = 0.10` anywhere in code or config, it is a bug from doc
|
|
| 144 |
took a correct 61/39 pool and selected 39/1, dumping 60 minority papers into the
|
| 145 |
exploration pool where no later quota stage could recover them. See doc 06,
|
| 146 |
2026-08-21.
|
| 147 |
-
- Exploration
|
| 148 |
- Quota (3.1) handles cross-cluster diversity. MMR handles within-quota redundancy.
|
| 149 |
This is a statement about WHERE each runs, not just what it is for.
|
| 150 |
- Do NOT use DPPs in v1.
|
|
@@ -479,7 +480,7 @@ If a topic is too large for a 06 changelog entry, create `docs/research/07-[topi
|
|
| 479 |
| Reranker? | Personalized heuristic by default; optional LightGBM/auto. |
|
| 480 |
| Latency budget? | <30ms end-to-end (compute only; metadata I/O excluded). |
|
| 481 |
| Cold start? | Hybrid: categories + seed papers + popularity fallback (Phase 5 complete). Author import removed from current roadmap. |
|
| 482 |
-
| When does behavioral take over? | ~10 saved papers. Currently
|
| 483 |
| When to add CF? | 500+ users (Phase 9). |
|
| 484 |
| Current status? | Local implementation and validation are distinct from deployed status. See `docs/CURRENT-STATE.md`. |
|
| 485 |
| ArXiv ID type? | String. Always. `dtype=str` in pandas. |
|
|
|
|
| 106 |
- Algorithm: **Ward hierarchical agglomerative** via `scipy.cluster.hierarchy.ward`.
|
| 107 |
- Code lives in: `app/recommend/clustering.py`.
|
| 108 |
- **L2-normalize embeddings BEFORE Ward, then use Euclidean distance.** Cosine Ward via sklearn is mathematically not Ward (Murtagh and Legendre 2014). L2-norm + Euclidean is monotonically equivalent to cosine and gives the intended behavior. This normalization is already in the code.
|
| 109 |
+
- **No fixed K.** Cut the dendrogram by adaptive gap-based threshold (see `_adaptive_threshold()`). Cap at `K_max = 7` (currently; doc 06 says `K_max = 20` for heavy users β raise this when users exist) and at ceil(N/2) (`AVG_CLUSTER_SIZE_FLOOR = 2`). Take the finest cut within the cap that leaves no singleton; never fold a split pair into another cluster (doc 06, 2026-09-30).
|
| 110 |
+
- **From 2 saves, not 5.** Below `MIN_PAPERS_FOR_CLUSTERING = 5` each save is its own interest (`per_save_clusters`) on the same quota/MMR path. Do not average unrelated early saves into one vector; Tier 2 EWMA is only the fallback.
|
| 111 |
- **Medoid, not centroid.** Medoid = arg min over cluster members of sum of squared distances. Cache medoid paper IDs. This is implemented in `_find_medoid()`.
|
| 112 |
- **Hungarian-match cluster IDs across reclusterings** β implemented via `stabilize_cluster_ids`.
|
| 113 |
- Recompute on each feed request currently (not nightly batch β no batch job infrastructure yet).
|
|
|
|
| 145 |
took a correct 61/39 pool and selected 39/1, dumping 60 minority papers into the
|
| 146 |
exploration pool where no later quota stage could recover them. See doc 06,
|
| 147 |
2026-08-21.
|
| 148 |
+
- Exploration: 2 serendipitous papers per page, drawn in `_build_page()`. The pool is starter papers from ticked interests that no cluster medoid covers (`_uncovered_interest_papers`), otherwise the far half of the reader's own candidates. Draws stay uniform so propensities hold.
|
| 149 |
- Quota (3.1) handles cross-cluster diversity. MMR handles within-quota redundancy.
|
| 150 |
This is a statement about WHERE each runs, not just what it is for.
|
| 151 |
- Do NOT use DPPs in v1.
|
|
|
|
| 480 |
| Reranker? | Personalized heuristic by default; optional LightGBM/auto. |
|
| 481 |
| Latency budget? | <30ms end-to-end (compute only; metadata I/O excluded). |
|
| 482 |
| Cold start? | Hybrid: categories + seed papers + popularity fallback (Phase 5 complete). Author import removed from current roadmap. |
|
| 483 |
+
| When does behavioral take over? | ~10 saved papers. Currently: 1 save = similar-to, 2β4 = one interest per save, 5+ = Ward clustering; EWMA single-vector is a fallback. |
|
| 484 |
| When to add CF? | 500+ users (Phase 9). |
|
| 485 |
| Current status? | Local implementation and validation are distinct from deployed status. See `docs/CURRENT-STATE.md`. |
|
| 486 |
| ArXiv ID type? | String. Always. `dtype=str` in pandas. |
|
docs/CURRENT-STATE.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
| 1 |
# Current technical contract
|
| 2 |
|
| 3 |
-
Updated: 2026-09-
|
| 4 |
2026-09-28; the live checks are in DISCOVERY-PLAN.md (deployment record).
|
| 5 |
Re-verify with `/healthz/*` rather than trusting this date.
|
| 6 |
|
|
@@ -19,10 +19,11 @@ and deployed. Never copy old corpus counts or benchmark results into current cla
|
|
| 19 |
| Search | Original + optional rewritten query; dense shard fanout and independent FTS5; Zilliz without FTS; RRF; optional top-10 MiniLM; title/citation boost | `app/hybrid_search_svc.py`, `app/config.py` |
|
| 20 |
| Missing encodings | Available FTS query forms still run; actual contributing arms determine `retrieval_mode` | `app/hybrid_search_svc.py` |
|
| 21 |
| Search total miss | arXiv keyword fallback; no invented hits for unfamiliar concepts | `app/routers/search.py` |
|
| 22 |
-
| Detailed interests | Saved papers -> long/short/negative EWMA + Ward medoids
|
| 23 |
| Multi-interest balance | Importance-weighted quota, within-cluster MMR, quota-bound served order | `fusion.py`, `app/routers/recommendations.py` |
|
| 24 |
| Scoring | Personalized heuristic default; optional citation-trained LightGBM | `reranker.py`, `config.py` |
|
| 25 |
-
| Cold start |
|
|
|
|
| 26 |
| Refresh | Personalized candidate retrieval excludes last seven days of served papers; one exhaustion retry allows labelled repeats | recommendation router, `app/db.py` |
|
| 27 |
| Paging | Cached ordered pool per query ID, bound to user; per-user lock includes serving/impression write in one process | recommendation router |
|
| 28 |
| Preferences | Editable `/interests`; zero to eight known categories; no deletion of existing saves | `app/routers/onboarding.py` |
|
|
|
|
| 1 |
# Current technical contract
|
| 2 |
|
| 3 |
+
Updated: 2026-09-30 (feed changes not yet deployed). Deployed to the Hugging Face Space at `67cba21` on
|
| 4 |
2026-09-28; the live checks are in DISCOVERY-PLAN.md (deployment record).
|
| 5 |
Re-verify with `/healthz/*` rather than trusting this date.
|
| 6 |
|
|
|
|
| 19 |
| Search | Original + optional rewritten query; dense shard fanout and independent FTS5; Zilliz without FTS; RRF; optional top-10 MiniLM; title/citation boost | `app/hybrid_search_svc.py`, `app/config.py` |
|
| 20 |
| Missing encodings | Available FTS query forms still run; actual contributing arms determine `retrieval_mode` | `app/hybrid_search_svc.py` |
|
| 21 |
| Search total miss | arXiv keyword fallback; no invented hits for unfamiliar concepts | `app/routers/search.py` |
|
| 22 |
+
| Detailed interests | Saved papers -> long/short/negative EWMA. 2β4 saves: one interest per save; 5+: Ward medoids, K β€ min(7, ceil(N/2)), finest singleton-free cut (2026-09-30) | `app/recommend/profiles.py`, `clustering.py` |
|
| 23 |
| Multi-interest balance | Importance-weighted quota, within-cluster MMR, quota-bound served order | `fusion.py`, `app/routers/recommendations.py` |
|
| 24 |
| Scoring | Personalized heuristic default; optional citation-trained LightGBM | `reranker.py`, `config.py` |
|
| 25 |
+
| Cold start | Balanced per ticked interest; citations per month over 24 months, every third slot from the corpus's last 3 months (needs refreshed citations to rank those); epsilon-greedy fresh block and deterministic recycled block | `app/discovery_svc.py`, `local_meta.py`, recommendation router |
|
| 26 |
+
| Exploration | 2 per page; from ticked interests no medoid covers, else the far half of own candidates | recommendation router |
|
| 27 |
| Refresh | Personalized candidate retrieval excludes last seven days of served papers; one exhaustion retry allows labelled repeats | recommendation router, `app/db.py` |
|
| 28 |
| Paging | Cached ordered pool per query ID, bound to user; per-user lock includes serving/impression write in one process | recommendation router |
|
| 29 |
| Preferences | Editable `/interests`; zero to eight known categories; no deletion of existing saves | `app/routers/onboarding.py` |
|
docs/research/06-Deep-Research-Verdict.md
CHANGED
|
@@ -450,3 +450,33 @@ needed before HF candidates can enter the live feed. Bound retries, invalidate
|
|
| 450 |
changed-text embeddings, and reject stale/future candidates. Report source failures.
|
| 451 |
**Action items:** Implemented locally with pipeline regressions. Deploy a durable
|
| 452 |
worker and establish real-model/relevance evidence before an opt-in serving pilot.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 450 |
changed-text embeddings, and reject stale/future candidates. Report source failures.
|
| 451 |
**Action items:** Implemented locally with pipeline regressions. Deploy a durable
|
| 452 |
worker and establish real-model/relevance evidence before an opt-in serving pilot.
|
| 453 |
+
|
| 454 |
+
### 2026-09-30 β Multi-interest from two saves, finer interest cap, grounded exploration
|
| 455 |
+
**Decision:** The user approved four changes after a trace of their own interests
|
| 456 |
+
(NLP, CV, ML, AI, Robotics, Software Engineering) with production vectors:
|
| 457 |
+
1. The multi-interest path (quota + per-cluster MMR) starts at 2 saves. Below 5,
|
| 458 |
+
each save is its own interest (`per_save_clusters`); Ward still starts at 5.
|
| 459 |
+
2. `AVG_CLUSTER_SIZE_FLOOR` 4 β 2, so K β€ ceil(N/2) (still β€ `K_max = 7`), and
|
| 460 |
+
`compute_clusters` takes the finest dendrogram cut at or below K that leaves
|
| 461 |
+
no singleton before the existing singleton merge.
|
| 462 |
+
3. Exploration draws from ticked interests that no cluster medoid lists in its
|
| 463 |
+
categories. Otherwise it keeps the far-half-of-own-candidates pool.
|
| 464 |
+
4. Tier 0 balances per ticked interest instead of per arXiv code. It ranks by
|
| 465 |
+
citations per month since publication and gives every third slot per
|
| 466 |
+
interest to a paper from the corpus's last three months.
|
| 467 |
+
**Supersedes:** Tier 2 EWMA single-vector search as the 3β4-save tier (it remains
|
| 468 |
+
the fallback); the soft cap of ceil(N/4); exploration drawn only from the reader's
|
| 469 |
+
own retrieval neighbourhoods; Tier 0 per-code round-robin by raw citations.
|
| 470 |
+
**Rationale:** Measured: averaging Llama 3 + Ο0 + OpenHands returned generic
|
| 471 |
+
"LLM agents" surveys matching none of them. Seven saves across five interests
|
| 472 |
+
allowed only 2 clusters, and 12 across six allowed only 3 (5 at the new floor),
|
| 473 |
+
while single-interest readers are unchanged. Lowering the floor alone collapsed
|
| 474 |
+
a noisy 3 + 2 reader into one cluster (the singleton merge folded the split
|
| 475 |
+
pair into the majority), which the singleton-free cut fixes. Tier 0 held 0 of
|
| 476 |
+
200 papers from 2026 and gave Computer Vision 22 of 200. Replay method and
|
| 477 |
+
figures are in the 2026-09-30 feed trace. There is no judged relevance set yet,
|
| 478 |
+
so these rest on the replay and on the invariants, not on an offline metric.
|
| 479 |
+
**Action items:** Implemented with regression tests. Refresh citation counts
|
| 480 |
+
(zero for every paper since 2025-06) and ingest papers after 2026-07-30, or
|
| 481 |
+
Tier 0's fresh lane has no quality signal. Build a small judged set before
|
| 482 |
+
tuning further; multi-medoid retrieval for loose clusters is deferred until then.
|