How it works
Deterministic engines, an agent that explains them.
Where the data comes from, how ranking and scoring work, why the advisor is an agent, and what it is and isn't allowed to do.
01
Why it exists
Know what you have
A shelf of twelve bottles is hard to see as a whole. Sillage maps it: families, signature accords, which seasons and occasions you can actually cover.
Buy the right next one
Recommendations start from what you own and what you've said you like, and every pick explains itself, so you can tell a real gap from a fifth version of the same scent.
Use what you own better
Most collections are under-worn. Today picks from your own shelf; Scent Lab helps you try pairings and learn from how they actually wore.
The bet is that collection context makes advice much more relevant, and that it's only trustworthy if every claim traces back to data. That's a hypothesis: the evals test that the system behaves as designed, not yet that people prefer it.
02
Where the data comes from
About 38,000 fragrances with notes, accords and community votes come from Fragrantica's public pages via the open Decant dataset. Sillage keeps facts and aggregates only (note pyramids, accord strengths, ratings, longevity, projection, season and day/night votes, short review themes) and links each page back to its source.
When search finds nothing, Open Beauty Facts is queried live. Those listings have no notes, so they're marked unverified and can be collected but not recommended.
Prices aren't in the data. Budget filtering uses estimated price bands from each brand's typical pricing (395 brands), always labelled as estimates. Real prices only come from the price check.
Occasion fit isn't in the data either. It's inferred from day/night votes, projection and accords, labelled “estimated” wherever it appears, and lowers confidence.
03
How recommendations are ranked
Tested code, not a language model: the same shelf and filters give the same answer. Every result carries reasons and caveats, and every exclusion is counted.
- 1
Taste profile
Each bottle contributes its accords, weighted by your rating: love 1.0, like 0.7, neutral 0.2, unrated 0.15, dislike counts against.
- 2
Hard filters
Owned bottles, notes you avoid (oud also catches agarwood), budget, season, time of day and marketing preference. Different drops near-copies (≥ 75% overlap); Fill gaps drops ≥ 85%.
- 3
Score
Taste fit, the mode's goal, budget, season/time and performance, weighted per mode (below).
- 4
Diversify
At most two picks per brand; other versions of a bottle you own are flagged, and budget alternatives are labelled with what they resemble.
| Row | Taste | Goal | Budget | Season/time | Performance |
|---|---|---|---|---|---|
| Similar | 35% | 25% | 20% | 10% | 10% |
| Different | 35% | 25% | 20% | 10% | 10% |
| Fill gaps | 25% | 35% | 20% | 10% | 10% |
04
Collection Score, Today and Scent Lab
Three features on one shared fragrance profile. Every number is calculated by versioned code; when data is missing, that factor is left out and the rest reweighted. It never counts as zero.
Collection Score
How far your shelf reaches, 0–100. A coverage description, never a judgement of taste, and owning more doesn't raise it by itself. Preview any bottle to see the change before you buy.
| Season coverage | 25% |
|---|---|
| Occasion coverage | 25% |
| Versatility | 20% |
| Scent diversity | 15% |
| Low redundancy | 15% |
Today
Ranks only bottles you own for an occasion, time of day, weather and intensity. Weather is manual unless you choose to share location, which is rounded and never stored.
| Occasion | 30% |
|---|---|
| Weather | 25% |
| Intensity | 15% |
| Preference | 15% |
| Time of day | 10% |
| Rotation (under-worn) | 5% |
Scent Lab
A labelled heuristic for two of your bottles, from a small editable ruleset of complementary and tension accords. Not chemistry. Your wear ratings move similar pairings by at most ±10 points.
| Accord complementarity | 35% |
|---|---|
| Low clash risk | 25% |
| Intensity balance | 20% |
| Family balance | 10% |
| Your preference | 10% |
05 · The advisor agent
Why an agent, and why only one.
A question like “a woody winter scent under $150 that isn't like anything I own” isn't one lookup. It means reading the shelf, translating “isn't like anything I own” into Different mode, setting the season and budget, checking the top pick more closely, and explaining the trade-offs. A fixed pipeline handles one shape of question; an agent can plan the steps for each one.
What the agent doesn't do matters as much. It doesn't rank fragrances, score collections or pair bottles itself: deterministic, tested engines do, and the agent calls them as tools. That keeps the numbers reproducible and the language flexible.
One agent with ten typed tools is enough for this job. A multi-agent setup would add cost, latency and failure modes without a clear benefit, so it was left out on purpose.
The loop is model-agnostic. It runs on Google Gemini's free tier today and on Anthropic's Claude with one environment variable; the tools, the six-round cap and the grounding check are identical either way, and every answer's trace names the model that wrote it.
- You
“A woody winter scent under $150 that isn't like anything I own?”
The loop
01
Read
Starts from your shelf and preferences, every time. A recommendation that doesn't know what you own is just a search.
02
Decide
Chooses which engine fits the question: Similar, Different or Fill gaps; the score; today's pick; a price check.
03
Act
Calls the tool with typed, validated inputs. Parallel calls where it can, at most six rounds in total.
04
Inspect
Reads the results, and looks closer at the best candidate when the answer depends on it.
05
Check
Before you see anything, every cited fragrance and price is matched against what the tools returned.
06
Answer
Explains in plain English, with each pick linked and the engine's caveats kept.
Its tools
| get_user_profile | Shelf size, family mix, season and day/night coverage, preferences | read |
| get_owned_fragrances | Each bottle and how you rated it (ownership alone isn't treated as liking) | read |
| search_catalog | Resolve a fragrance you mention by name | read |
| get_fragrance_details | Notes, accords, community votes, review themes (labelled untrusted) | read |
| recommend_candidates | Runs Discover's deterministic ranking engine | read |
| find_offers | Variant-matched price check for one exact bottle | read |
| get_collection_score | Your Collection Score, its five dimensions, strengths and gaps | read |
| pick_for_today | Ranks only bottles you own for an occasion, time and temperature | read |
| propose_add_to_wishlist | Shows you a confirm button; saves nothing by itself | propose |
| propose_preference_update | A note to avoid, a liked note or a budget, for you to confirm | propose |
Guardrails and failure modes
- Output contract. Fragrances are cited by id. Any id that didn't come back from a tool in that turn, and any dollar amount that didn't appear in a tool result, is removed and counted in the trace. In the eval, 0 of 7 planted fabrications got through.
- No silent writes. Saving or changing preferences goes through “propose” tools that render a confirm button. The model never touches the database.
- Prompt injection. Review themes and product names come from third parties and are labelled untrusted in tool results; with no write tools, there's nothing for injected text to trigger.
- Bounded cost. Six tool rounds at most, low reasoning effort, prompt caching on the fixed instructions and tools, 20 questions per person per hour; every answer shows its tokens, latency and estimated cost.
- Graceful degradation. With no model, or on an error or decline, a rule-based mode answers recommendations, score and “what to wear” questions with the same engines, and says so.
- Same rules elsewhere. Today's explanation can be phrased by the model, but only from the ranker's factors: if it names the wrong bottle or introduces a new number, the template is used instead.
06
How prices are matched
- You choose the exact bottle: concentration and size. A fragrance can be found before its size is known, but not priced until it is.
- An exact match needs the same brand, name, concentration and size, and a new, full, single bottle. Testers, samples, refills, gift sets, pre-owned and flankers are set aside.
- Unknown extra words (“Qahwa”, “Lucky”) go to “Needs verification”. The first version used a blocklist and the eval caught it calling flankers exact.
- Live results come from Google Shopping via SerpApi, cached 6 h, 12 checks per person per hour. Otherwise you see labelled sample offers from fictional stores, with no links. Never “cheapest anywhere”.
07
Privacy and security
- Everything you own, rate, wear, pair or ask is locked to your account in the database itself (row-level security), with tests that one user can't read another's data.
- API keys stay on the server; the privileged database key is used only for shared caches, rate limits and demo sandboxes.
- Location for live weather is optional, rounded to about 10 km, used once and never stored.
- Product analytics record only an allowlist of event names and safe properties: never notes, location or chat text.