Skip to content

Modeling

The modeling module is where the recommender model itself lives. It is deliberately the smallest substantial module in the tutorial. ADR-0007 records the decision to keep the model intentionally modest: content similarity over article text, with a popularity-by-category cold-start fallback. No training, no fine-tuning, no two-stage architecture.

The point is not that this model is impressive. The point is that the platform around it does enough work that the model does not have to be. That is the platform-as-leverage argument made concrete.

Modeling is split across two homes:

  • tutorial/serving/src/serving/embeddings.py loads an off-the-shelf multilingual sentence-transformer (works on Danish, Norwegian, and English article text) and exposes the encoding interface the rest of the platform calls.
  • tutorial/serving/dbt/models/staging/article_embeddings.py is the dbt Python model that runs the embedding once per article and materialises the vector column into the Parquet substrate. This means the embeddings are part of the analytical contract — analysts can query them just like any other column.
  • tutorial/serving/src/serving/recommendations.py does candidate generation: average the embedding of the user’s recently read articles, find the top-N nearest articles by cosine similarity, exclude already-read articles, and return the candidate set.

The cold-start path is in the same file — when a user has no read history, the code falls back to popularity-by-category over a recent time window. No fancy bandits, no warmup model. The honest acknowledgement that cold-start is a real problem is treated as more important than the particular technique chosen.

test_embeddings.py covers the encoder behaviour, and test_recommendations.py covers candidate generation end-to-end with seeded user histories and embedding fixtures. Both run in milliseconds without a database, which is the test discipline ADR-0007 promises.

Why the model deliberately stays this small

Section titled “Why the model deliberately stays this small”

ADR-0004 predicts the failure mode: if the platform argument only works because the model is impressive, the platform is not actually doing much work. Keeping the model modest puts the architecture under pressure in the right place.

It also matches the positioning advice in the JP/Politikens Hus evaluation: the candidate’s strongest signals are platform / product / leadership, not deep recommender research. Overclaiming the model would weaken the artefact for its actual audience.

The candidate sets this module produces flow into editorial, where the ranker applies the five editorial constraints. The same candidate set is what the editor interface displays before-and-after when an editor moves a slider. And the evaluation harness sweeps constraint configurations against these candidates to render the Pareto chart.