Building recommendations that admit what they do not know
The hard part is not ranking. It is knowing when to return nothing.
Use the same function twice
Our recommendation layer derives a preference profile from what someone has already collected, then scores the catalogue against it — using the identical similarity function the identification engine uses, imported from the same module.
That is deliberate, and it is the first decision worth defending. It is tempting to write a second, recommendation-specific notion of similarity tuned to its own evaluation. Two independent definitions of similar will eventually disagree on some pair of items, and when they do, the product starts giving answers that contradict each other. Users do not report that as a bug. They report it as the recommendations feeling arbitrary, which is much harder to act on and much easier to lose them over.
Three rules, each with a test behind it
- It will not suggest anything below a minimum collection size. A profile derived from one item is not a profile, it is that item. The feature stays quiet and says how many more it needs.
- It never recommends something already owned. Returning someone their own library is the fastest way to make a feature look broken, and it is trivially avoidable.
- It returns an empty list rather than padding. Below a similarity floor, the row stays empty. A weak suggestion is not free — it spends the user's trust to fill space, and it spends it every time they look.
All three are refusals. That is the pattern: the interesting engineering in a recommender is mostly in the cases where it declines to produce output, because those are the cases where producing output is easy and wrong.
Explanations need contrast, not accuracy
Every suggestion explains itself. The first version named the attributes the suggestion shared with the profile. It was correct, it passed its tests, and it was useless.
Consider a collection dominated by one attribute. Every candidate that scores well shares that attribute — that is what scoring well means. So every explanation named the same thing, and a column of six suggestions carried six copies of one sentence. The text was accurate on every row and informative on none.
The fix was to name what is familiar and what is new: shares this, adds that. And the more useful fix was to the test, which had asserted the explanation was correct. We changed it to assert that two different suggestions produce two different reasons — the property that actually mattered, and the one the original test could never have caught.
Thresholds are product decisions in disguise. The minimum collection size, the similarity floor, the evidence bar for naming a dominant category — these get written as constants and reviewed as implementation detail. They are not. Each one is a decision about how confident the product should be before it speaks, which is the most visible thing about it.
The empty state is a feature
A recommender that always fills the row is easy to build and easy to distrust. Users work out surprisingly quickly whether a system is answering or performing, and once they have decided it is performing, every subsequent correct answer is discounted too.
Silence, with a reason attached, is the cheapest credibility a system can buy.
The next generation of software will be intelligent.
ScentID Solutions is building toward that future — one platform, one integration, and one intelligent solution at a time.
Work With Us