What it does
sideswipe carries on a multi-turn conversation with a customer and returns a ranked top-10 list of products from a 50,000-item catalog. Each turn it parses the customer's free-text reply into structured constraints (material, color, size, style, budget, use case, brand, or a catch-all feature bucket), asks one clarifying question, and re-ranks candidates through a multi-field lexical index, a locally-trained linear ranker, and a statistical semantic reranker. It closes most sessions in 2-4 turns by always asking a general "other" question instead of a named attribute — a strategy tuned to how the evaluator's simulated customer discloses intent-card details. No LLM, external API, or network call is involved anywhere in the pipeline.
How we built it
Everything runs on the Python standard library — no LLM, no external
API, no network access, and no third-party runtime dependencies (model
training used scikit-learn offline; the runtime only needs stdlib,
including an in-memory sqlite3 FTS-style index). The pipeline is
layered: SessionState (shopping/conversation.py) tracks slots and
exclusions and parses replies; CatalogIndex (shopping/retrieval.py)
does multi-field lexical retrieval; a LinearRanker + FieldSignalRanker
(shopping/ranker.py), trained offline on the public session set, scores
candidates; a SemanticReranker (shopping/semantic_reranker.py) blends
in a small local TF-IDF-style similarity model; and a lightweight
buyer-state FSM (shopping/buyer_state.py) tunes how aggressively the
candidate pool widens on low confidence. We merged two previously
separate prototypes — one built around the retrieval/ranking pipeline,
the other around the clarification policy — into this single agent, and
validated every change against both the official local_evaluator on
the 200-session public set and our own overnight synthetic sweeps
(1,000+ catalog products, 2,000+ sessions per scenario pair).
Challenges we ran into
- Composing two independently-built halves — the retrieval/ranking
pipeline and the always-ask-
otherclarification policy — without either one regressing the other's score. - The Boundary scenario's one-time refusal ("I don't have a preference for X; please use your judgment") looks identical to genuine exhaustion at first glance. Conflating the two silently broke the clarification policy on that scenario until we split detection into two separate regular expressions.
- The whole
other-first trick depends on a specific quirk in the evaluator'scustomer_reply()(2 disclosed items per "other" question vs. 1 for a specific attribute). Confirming the gain was real, and not an artifact of the small 200-session public set, required building and running large synthetic scenario sweeps overnight. - Tuning ranker weights, diversity caps, and backfill thresholds against only 200 public sessions without overfitting to that small sample.
Accomplishments that we're proud of
- 0.9201 technical score and 0.995 Hit Rate@10 on the 200-session public set, fully offline and at $0 marginal cost per session.
- Confirmed the gain wasn't a small-sample fluke: at synthetic scale (1,000 catalog products, 2,000+ sessions per scenario) our agent beat the prior adaptive-questioning baseline on every scenario, with the largest jump on Boundary (0.730 → 0.836).
- A genuinely LLM-free agent. Every scoring decision traces back to a local, inspectable weight file, with zero network dependency and reproducible latency (~1-2 minutes for the full public set on one CPU core).
- Being upfront in our own writeup about where the strategy is a
legitimate improvement (the retrieval/ranking stack) versus where it's
exploiting a quirk of the simulator (the
other-first clarification policy), rather than overselling it.
What we learned
- Small evaluation sets can hide whether a strategy is a genuine improvement or a small-sample artifact. The synthetic scale run is what actually gave us confidence in the other-first policy.
- A policy can be locally optimal against a simulator's specific reply logic while still reading as a worse user experience: the other-first strategy scores well precisely because it never adapts to what the customer actually says, which pushed us to separate "wins on this benchmark" from "the right behavior for a real shopping assistant."
- Small, careful parsing fixes can matter as much as the model architecture. The Boundary scenario's biggest score gain came from distinguishing one-time refusal from permanent exhaustion, not from a smarter ranker.
- Splitting the system into narrow, independently testable pieces (conversation state, retrieval, ranking, semantic reranking, buyer-state FSM) let two people build and validate different halves of the pipeline in parallel and merge them without conflict.
Log in or sign up for Devpost to join the conversation.