Inspiration

Most shopping search engines require you to already know exactly what you want. Type the wrong words, get the wrong products. We wanted to flip that — build an agent that asks rather than assumes, progressively narrowing down to what a shopper truly needs through natural conversation.

The TechJam 2026 Track 4 challenge gave us the perfect framing: a hidden target product, a simulated customer, ten turns to find it. The scoring formula penalised showing a weak candidate too early and rewarded reaching the target quickly. That tension between when to recommend and when to ask became the heart of our design.


What It Does

The agent conducts a short clarifying conversation with a shopper and surfaces the right product as early as possible — usually within two turns. It classifies each message as buying, browsing, intent_override, or boundary, extracts disclosed preferences verbatim, and uses them to score and rank products from a 50,000-item catalogue.

A key behaviour is the exposure gate: the agent shows only one candidate in the first two turns, expanding to the full top-ten once intent is clear. This prevents a weak early guess from permanently capping the session's score. An evict-on-conflict memory handles mind-changes cleanly — "actually, leather instead of cotton" replaces only the contradicted preference, leaving everything else intact.


How We Built It

We started from the supplied BM25 baseline (score: 0.107) and iterated through controlled ablations on the 200-session public set, measuring every change before keeping it.

The pipeline flows through five stages: Intent Router → Constraint Memory → Category-Bucket Retrieval → Clarification Engine → Exposure Gate. The retrieval stage was the biggest unlock: resolving the coarse product category from the opening message and restricting the pool to that bucket — rather than searching the full catalogue — accounted for most of the score gain.

Category-first retrieval

Rather than searching the full 50k-product catalogue, we first resolve the coarse product category from the opening message and restrict subsequent retrieval to that bucket. This makes attributes such as colour, material, fit, or size significantly more discriminative and was the single largest contributor to performance: removing category filtering dropped the score from 0.969 to 0.467.

Verbatim constraint memory

We retain the user's actual preference wording across turns, allowing constraints to accumulate into an increasingly complete representation of intent rather than restarting retrieval from each individual message.

Contradiction-aware updates

When the user changes a preference, we replace only the conflicting constraint while retaining everything else — for example, "black lightweight jacket" followed by "actually blue" becomes "blue lightweight jacket" rather than carrying both colours forward.

Constraint Index Ranking

Within the category bucket, products are reranked according to how well they satisfy the accumulated constraints, with exact catalogue matches weighted most strongly, followed by substring and token-level matches.

User preference-aware retrieval and reranking

Session-level preference tags and rating behaviour provide an additional personalization signal, influencing BM25 field weighting, constraint-coverage bonuses, and popularity-based tie-breaking.

Progressive Reveal Strategy

Instead of immediately exposing ten candidates, we return only the strongest recommendation during the first two turns and widen to the full top 10 from turn three onwards, giving the system more opportunity to gather useful evidence before committing rankings.

This conversational strategy was particularly important because of how the evaluation metric behaves. A session's reciprocal rank is frozen when the target first enters the top 10, meaning a premature low-ranked hit cannot be corrected later. Progressive reveal therefore preserves the upside of an early correct top-1 result while reducing the risk of committing a weak ranking before enough preferences have been disclosed.


Challenges We Ran Into

Exposure timing. Returning the full top-ten from turn one locked in the reciprocal rank before any clarification, capping scores on vague openers. The exposure gate was unintuitive to design but essential to the final score.

Contradiction-aware memory. A mind-change should replace only the contradicted constraint. We built token-overlap conflict detection: "cotton" and "cotton blend" are a refinement (kept), "cotton" and "leather" are a contradiction (old one evicted). Getting that boundary right took several iterations.

Category resolution as a single point of failure. If the opening message does not name a resolvable category, the agent falls through to whole-catalogue matching and quality drops sharply. Vocabulary-derived aliases helped, but a true semantic fallback was out of scope.

Missing price data. Price is absent for ~79% of the catalogue, so stated budgets rarely filtered results in practice. The parser is correct; the signal just isn't there in the data.


Accomplishments That We're Proud Of

Reaching a technical score of 0.969 — HitRate@10 of 1.000, MRR of 0.968, and a mean of just 2.07 turns to conversion — against a 0.107 BM25 baseline. The target is the first result in 189 of 200 sessions and within the top three in 197.

We're also proud that every one of those numbers was produced with zero LLM calls on the scored path — no API keys, no GPU, no network. The system runs on a single CPU core in under 1 GB of RAM, making it reproducible anywhere.


What's Next for Conversational Shopping Search Agent

  1. Semantic category fallback. A lightweight classifier that fires when the opening message doesn't resolve to a catalogue bucket, rather than falling through to whole-catalogue matching.
  2. Wider paraphrase robustness. The constraint extractor is tuned to the public set's fixed templates. A held-out set with different phrasing would degrade retrieval before ranking is reached — widening the parser is the highest-priority next step.
  3. Price imputation. Range-bucketing or soft-signal weighting for the ~79% of products missing a price field, so stated budgets actually filter results.
  4. Gated semantic retrieval. A dense-vector layer that fires only when the lexical path is weak, rather than always contributing — the condition under which our earlier vector experiments all lost points.

Built With

+ 3 more
Share this project:

Updates