# Next Best Product (NBP) Recommendations

Show every customer the thing they are most likely to want next.

## Overview and Use Cases

The NBP workflow from Treasure AI's machine learning platform (AI Signals) generates a ranked list of products, services, or content most likely to resonate with each customer. It learns from who bought what and when, then predicts what each person is most likely to want next.

NBP offers three complementary algorithms under one workflow. Each covers a different part of your base, which is why most teams run more than one:

**Collaborative filtering (`als`)** learns latent preferences through matrix factorization across your entire user base. It discovers patterns like "users who bought X also tend to buy Y" without needing explicit item metadata. It is the highest-quality personalization option and works best for known users with rich transaction history.

**Item similarity (`similar_to_latest`)** recommends items most similar to each user's most recent purchase. It works for any user with at least one transaction and stays fast and resilient when new items appear often.

**Trending items (`popular`)** returns the most-purchased or most-viewed items across the whole user base. It handles cold-start users who have no history at all and serves as a reliable universal fallback.

[Combining Algorithms for Coverage](#combining-algorithms-for-coverage) explains how to combine them.

**Common use cases**

- Personalize product carousels on site and in app
- Power product recommendations in email and push campaigns
- Build cross-sell and replenishment journeys from what a customer already owns
- Recommend content, articles, video, or podcasts, between purchases
- Cover the cold-start gap by layering algorithms so every customer gets something


**Who benefits most:** Lifecycle marketers, personalization teams, and CRM managers who need per-customer recommendations across millions of profiles without a dedicated ML platform or data science team.

### How It Fits with the Other AI Signals

| **Question you're asking**  | **Use**  |
|  --- | --- |
| Who matters, based on past purchasing? | [RFM AI Signals](/products/customer-data-platform/machine-learning/ai-signals/rfm-ai-signals): descriptive segments, no modeling required |
| How likely is this customer to do X? | [Propensity Scoring AI Signals](/products/customer-data-platform/machine-learning/ai-signals/propensity-ai-signals): a probability per event you define |
| How much will this customer be worth? | [CLTV AI Signals](/products/customer-data-platform/machine-learning/ai-signals/cltv-ai-signals): a value forecast and percentile rank |
| **Which product should I recommend next?** | **NBP AI Signals (you are here)**: a ranked list of products, services, or content |
| Which action should I take for this customer? | [NBA AI Signals](/products/customer-data-platform/machine-learning/ai-signals/nba-ai-signals): a learned per-user policy across candidate actions |


***NBP or NBA?** This is the only pair readers regularly confuse. NBP ranks items from a catalog by affinity: what is this person most likely to want. NBA learns a policy over a small set of marketing decisions you control, based on what has actually worked: which channel, which send time, which offer. NBP works from purchase and interaction history. NBA needs logged action and outcome pairs. The two compose well: let NBP choose the product, then let NBA choose how and when to pitch it.*

*These signals feed each other rather than compete. RFM and CLTV scores make useful inputs when deciding who to send recommendations to at all.*

## Quick Start

NBP runs as two functions on the Treasure AI ML Batch API:

- **`nbp_train`** fits the chosen algorithm on your transaction history, writes ranking metrics, and registers the model under `model_name`.
- **`nbp_predict`** loads that registered model and writes a ranked list of items per customer.


Your input is one row per interaction with three columns: a user identifier, an item identifier, and a timestamp. All three column names are configurable, so point the parameters at whatever your table already uses. NBP works from raw transaction-level rows and does not accept pre-aggregated summaries. [Data Preparation](#data-preparation) covers the full schema and data requirements.

**You only need to set a handful of parameters.** `input_table`, `output_table`, and `model_name` are required. Add `user_column`, `item_column`, and `tstamp_column` if your columns are not already named `userid`, `itemid`, and `tstamp`. Everything else has a working default, including `algorithm`, which defaults to `als`.

Security
Store your Treasure AI API key as a Workflow secret named `td.apikey`, never as a literal value in the workflow definition. Referencing it as a secret keeps the key out of version control, so teams can securely manage sensitive information. See [Setting Workflow Secrets from Treasure Console](/products/customer-data-platform/data-workbench/workflows/setting-workflow-secrets-from-td-console).

### Train

```yaml
# nbp_train.dig
# Step 1: Train, fit the chosen algorithm and register it under `model_name`

+train:
  http>: https://ml-batch-api.treasuredata.com/v1/runs/
  method: POST
  headers:
    - authorization: "TD1 ${secret:td.apikey}"
    - X-TD-ML-SESSION-ID: ${session_id}
    - X-TD-ML-ATTEMPT-ID: ${attempt_id}
  store_content: true
  content:
    input_table: your_database.transactions
    output_table: your_database.nbp_train_metrics
    solution_name: nbp_train
    solution_arguments:
      model_name: "nbp_retail_als_v1"
      algorithm: "als"
      user_column: "user_id"
      item_column: "item_id"
      tstamp_column: "timestamp"

+print_response:
  echo>: "Training job submitted. Response: ${http.last_content}"

# Poll until training completes. The API returns HTTP 408 while the job
# is running; Treasure Workflow retries until it receives HTTP 200.
+train_poll_status:
  http>: https://ml-batch-api.treasuredata.com/v1/runs/${JSON.parse(http.last_content)['id']}/status
  method: GET
  headers:
    - authorization: "TD1 ${secret:td.apikey}"
```

**Expected output:** a metrics table with one row per training run, carrying ranking scores such as `map_10` and `ndcg_10`. The trained model is saved to managed storage under `model_name`. See [Reading Training Metrics](#reading-training-metrics) for how to read the metrics.

### Predict

```yaml
# nbp_predict.dig
# Step 2: Predict, load the registered model and write ranked recommendations

+pred:
  http>: https://ml-batch-api.treasuredata.com/v1/runs/
  method: POST
  headers:
    - authorization: "TD1 ${secret:td.apikey}"
    - X-TD-ML-SESSION-ID: ${session_id}
    - X-TD-ML-ATTEMPT-ID: ${attempt_id}
  store_content: true
  content:
    input_table: your_database.transactions
    output_table: your_database.nbp_recommendations
    solution_name: nbp_predict
    solution_arguments:
      model_name: "nbp_retail_als_v1"     # Must match the training run
      algorithm: "als"
      user_column: "user_id"
      item_column: "item_id"
      tstamp_column: "timestamp"
      topk: 10

+print_response:
  echo>: "Prediction job submitted. Response: ${http.last_content}"

# Poll until prediction completes. The API returns HTTP 408 while the job
# is running; Treasure Workflow retries until it receives HTTP 200.
+pred_poll_status:
  http>: https://ml-batch-api.treasuredata.com/v1/runs/${JSON.parse(http.last_content)['id']}/status
  method: GET
  headers:
    - authorization: "TD1 ${secret:td.apikey}"
```

**Expected output:** one row per customer with a ranked array of item identifiers.

```text
user_id          rec_items
A1146YGPNU9EUL   [B007FHX9OK, B008DJIIG8]
44039871         [79255, 82886, 93487, 48930, 30495]
```

To create and schedule these workflows, see [Getting Started with Treasure Workflow](/products/customer-data-platform/data-workbench/workflows/getting-started-with-treasure-workflow).

## Model Configuration

Each NBP AI Signals function takes two kinds of input: the transaction table you point it at, and the parameters you pass it. Both are documented under Data Preparation and Workflow Parameters below.

[Model Types](#model-types) lists each algorithm's complete `nbp_train` parameter set, so you can configure an algorithm from one table.

### `nbp_train`

`nbp_train` fits one algorithm on your transaction history and saves it for reuse.

#### Data Preparation

A **transaction-level table**, one row per interaction. All three column names are configurable.

| **Field**  | **Type**  | **Required**  | **Description**  |
|  --- | --- | --- | --- |
| `userid` | VARCHAR | Yes | Customer identifier. Column name is customizable via `user_column`. |
| `itemid` | VARCHAR | Yes | Item identifier. Can be a product, service, article, video, or any other asset. Column name is customizable via `item_column`. |
| `tstamp` | LONG | Yes | When the interaction occurred. Column name is customizable via `tstamp_column`. |


Example rows:

```
userid           itemid       tstamp
A1146YGPNU9EUL   B007FHX9OK   1704067200
A1146YGPNU9EUL   B008DJIIG8   1706745600
44039871         79255        1707350400
```

**Data requirements.**

- **Transaction-level rows only.** NBP aggregates internally, so pre-aggregated summaries are not usable input.
- **One row per interaction**, not per customer. A customer with twelve purchases contributes twelve rows.
- **`als` only recommends for users present in training.** Anyone absent at training time gets nothing back from that model. See [Combining Algorithms for Coverage](#combining-algorithms-for-coverage).
- **`similar_to_latest` cannot recommend cold items.** An item that appears zero times in training cannot be recommended by it.


#### Workflow Parameters

See [Model Types](#model-types) for each algorithm's complete parameter set, including the algorithm-specific options.

| **Parameter**  | **Type**  | **Default**  | **Required**  | **Description**  |
|  --- | --- | --- | --- | --- |
| `input_table` | string | — | Yes | Source transaction table in `dbname.table_name` format. |
| `output_table` | string | — | Yes | Destination table for training metrics. |
| `model_name` | string | — | Yes | Unique name used to save and retrieve the trained model. Unique per Treasure AI account. Reusing a name overwrites the previous model regardless of which algorithm produced it, so include the algorithm in the name. |
| `algorithm` | string | als | No | `popular`, `als`, or `similar_to_latest`. See [Model Types](#model-types). |
| `user_column` | string | userid | No | Name of the customer identifier column. |
| `item_column` | string | itemid | No | Name of the item identifier column. |
| `tstamp_column` | string | tstamp | No | Name of the timestamp column. |
| `rating_column` | string | — | No | Name of an explicit rating or weight column, if you have one. |
| `filter_num` | int | 0 | No | Minimum number of users an item must have interacted with to be included in training. `0` means no filtering. Raising it trims long-tail items and shrinks the model. |
| `metrics_table` | string | — | No | Separate destination for training metrics, in `dbname.table_name` format, if you want metrics written apart from `output_table`. |


#### Output

A metrics history table, one row per training run, identified by `session_id`. Metric column names carry the `topk` value used, so a run with `topk: 10` writes `map_10`, `ndcg_10`, and so on.

| **Field**  | **Type**  | **Description**  |
|  --- | --- | --- |
| `session_id` | INTEGER | Workflow session that produced this run. Use it to trace a model back to its training job. |
| `model_params` | VARCHAR | The parameter set used, serialized. Useful when comparing runs. |
| `map_{topk}` | FLOAT | Mean Average Precision at K. |
| `ndcg_{topk}` | FLOAT | Normalized Discounted Cumulative Gain at K. |
| `precision_{topk}` | FLOAT | Precision at K. |
| `recall_{topk}` | FLOAT | Recall at K. |
| `mrr_{topk}` | FLOAT | Mean Reciprocal Rank at K. |


This table is append-only, so it accumulates a history you can chart over time. See [Reading Training Metrics](#reading-training-metrics) for how to read the numbers.

### `nbp_predict`

`nbp_predict` loads a registered model and writes ranked recommendations.

#### Data Preparation

The same transaction-level table format described under [`nbp_train`](#nbp_train). Prediction input must match the training format.

#### Workflow Parameters

| **Parameter**  | **Type**  | **Default**  | **Required**  | **Description**  |
|  --- | --- | --- | --- | --- |
| `input_table` | string | — | Yes | Table of users to generate recommendations for, in `dbname.table_name` format. |
| `output_table` | string | — | Yes | Destination table for the recommendations. |
| `model_name` | string | — | Yes | Must match the name used at training time. |
| `algorithm` | string | als | No | Algorithm used by the registered model: `popular`, `als`, or `similar_to_latest`. Must match the algorithm used at training time. |
| `output_mode` | string | overwrite | No | `overwrite` replaces the table, suitable for full-base runs. Use `append` for incremental subsets. |
| `user_column` | string | userid | No | Name of the customer identifier column. |
| `item_column` | string | itemid | No | Name of the item identifier column. |
| `tstamp_column` | string | tstamp | No | Name of the timestamp column. |
| `rating_column` | string | — | No | Name of an explicit rating or weight column, if you have one. |
| `topk` | int | 10 | No | Number of recommended items per customer. Maximum 100. |
| `lastk` | int | 1 | No | Number of the customer's most recent transactions used to build recommendations. Applies to `similar_to_latest` only. See [Item Similarity (`similar_to_latest`)](#item-similarity--similar_to_latest-). |


#### Output

One row per customer.

| **Field**  | **Type**  | **Description**  |
|  --- | --- | --- |
| `user_id` | VARCHAR | Customer identifier, carried forward from input. |
| `rec_items` | ARRAY | Recommended item identifiers, ordered by predicted rank with the strongest first. Length is controlled by `topk`. |


```
user_id          rec_items
A1146YGPNU9EUL   [B007FHX9OK, B008DJIIG8]
44039871         [79255, 82886, 93487, 48930, 30495]
```

**Using the recommendations.** Join the output to your customer table so `rec_items` becomes an attribute available to Master Segment rules, then activate it through standard CDP activation workflows into Journeys, Triggers, Personalization, Segment Builder, and Agent Console. Because the array is ordered, taking the first element gives a single hero recommendation, while the full array feeds a carousel.

## Model Types

NBP supports three algorithms from the [RecTools](https://github.com/MobileTeleSystems/RecTools) library. They differ in what they need from a customer and what they can cover, which is why most teams run more than one.

| **Algorithm**  | **Covers**  | **Strength**  | **Main tradeoff**  |
|  --- | --- | --- | --- |
| `popular` (Trending items) | Everyone, including brand-new customers | Fast, resilient, never fails to return something | Not personalized |
| `als` (Collaborative filtering) | Users present in training data | Highest personalization quality | Slow on large datasets; no cold-start coverage |
| `similar_to_latest` (Item similarity) | Anyone with at least one transaction | Handles new customers and new catalog items well | Cannot recommend items unseen in training |


Each section below lists that algorithm's complete `nbp_train` parameter set, so you can configure it from one table.

### Trending Items (`popular`)

Recommends the most-purchased or most-viewed items across the whole base. There is no personalization: every eligible customer can receive the same list.

Its value is coverage. `popular` works for customers with no history at all, which makes it the standard fallback layer and a sensible baseline to measure the other two against.

#### Model-Specific Parameters

Parameters in **bold** are specific to this algorithm. The rest are shared across all three.

| **Parameter**  | **Type**  | **Default**  | **Required**  | **Description**  |
|  --- | --- | --- | --- | --- |
| `input_table` | string | — | Yes | Source transaction table in `dbname.table_name` format. |
| `output_table` | string | — | Yes | Destination table for training metrics. |
| `model_name` | string | — | Yes | Unique name used to save and retrieve the trained model. |
| `algorithm` | string | als | No | `"popular"`. Must be set explicitly, since the default is `als`. |
| `user_column` | string | userid | No | Name of the customer identifier column. |
| `item_column` | string | itemid | No | Name of the item identifier column. |
| `tstamp_column` | string | tstamp | No | Name of the timestamp column. |
| `rating_column` | string | — | No | Name of an explicit rating or weight column, if you have one. |
| `filter_num` | int | 0 | No | Minimum number of users an item must have to be included in training. |
| **`popularity`** | string | n_users | No | How popularity is measured, for example by distinct users rather than raw interaction count. |
| **`period`** | string | — | No | Rolling window to measure popularity over, for example the last 30 days. Without it, popularity is measured across all history, which favors long-established items. |
| **`begin_from`** | string | — | No | Fixed start date for the popularity window, as an alternative to `period`. |
| `filter_viewed` | boolean | false | No | Exclude items a customer has already interacted with. |


This algorithm does not use `factors`, `regularization`, `iterations`, `alpha`, `tunable`, `k`, `distance`, `similar_items_table`, or `topk` at training time.

#### Workflow Example

```yaml
solution_arguments:
  model_name: "nbp_retail_popular_v1"
  algorithm: "popular"
  user_column: "user_id"
  item_column: "item_id"
  tstamp_column: "timestamp"
  period: "30D"
  filter_viewed: true
```

### Collaborative Filtering (`als`)

Alternating Least Squares, a matrix factorization approach. It represents the user-item interaction matrix as the product of two smaller matrices, learning latent preferences from the behavior of the whole base. It discovers co-purchase patterns without needing any product metadata.

This is the highest-quality personalization of the three, and the most demanding. It is slow on large datasets, and it can only recommend for users who were present in the training data, so it needs a partner algorithm for cold-start coverage.

#### Model-Specific Parameters

Parameters in **bold** are specific to this algorithm. `similar_items_table` and `topk` are shared with `similar_to_latest`; the rest are shared across all three.

| **Parameter**  | **Type**  | **Default**  | **Required**  | **Description**  |
|  --- | --- | --- | --- | --- |
| `input_table` | string | — | Yes | Source transaction table in `dbname.table_name` format. |
| `output_table` | string | — | Yes | Destination table for training metrics. |
| `model_name` | string | — | Yes | Unique name used to save and retrieve the trained model. |
| `algorithm` | string | als | No | `"als"`, or leave unset since this is the default. |
| `user_column` | string | userid | No | Name of the customer identifier column. |
| `item_column` | string | itemid | No | Name of the item identifier column. |
| `tstamp_column` | string | tstamp | No | Name of the timestamp column. |
| `rating_column` | string | — | No | Name of an explicit rating or weight column, if you have one. |
| `filter_num` | int | 0 | No | Minimum number of users an item must have to be included in training. |
| **`factors`** | int | 10 | No | Size of the latent representation. Higher captures more nuance and costs more memory and time. |
| **`regularization`** | float | 0.1 | No | Regularization strength, guarding against overfitting to heavy buyers. |
| **`iterations`** | int | 10 | No | Number of optimization passes. |
| **`alpha`** | float | 50 | No | Confidence weight applied to observed interactions. |
| **`tunable`** | boolean | false | No | Learn the hyperparameters from your data instead of using the defaults. Improves quality but makes runtime and memory unpredictable, so avoid it on a schedule with tight limits. |
| `similar_items_table` | string | — | No | Write the top similar items per item to this table, in `dbname.table_name` format. See [Similar Items Table](#similar-items-table). |
| `topk` | int | 10 | No | Number of similar items written per item when `similar_items_table` is set. |
| `filter_viewed` | boolean | false | No | Exclude items a customer has already interacted with. |


This algorithm does not use `popularity`, `period`, `begin_from`, `k`, or `distance`.

#### Workflow Example

```yaml
solution_arguments:
  model_name: "nbp_retail_als_v1"
  algorithm: "als"
  user_column: "user_id"
  item_column: "item_id"
  tstamp_column: "timestamp"
  factors: 32
  iterations: 15
  similar_items_table: your_database.nbp_similar_items
```

### Item Similarity (`similar_to_latest`)

Recommends items most similar to what a customer bought most recently. Similarity comes from co-occurrence across the base rather than from product metadata.

It sits between the other two on both quality and coverage. Any customer with at least one transaction gets a personalized list, including people who were not in the training data, which `als` cannot do. It also handles a catalog that changes often. Its limit is the opposite one: an item that never appears in training cannot be recommended.

#### Model-Specific Parameters

Parameters in **bold** are specific to this algorithm. `similar_items_table` and `topk` are shared with `als`; the rest are shared across all three.

| **Parameter**  | **Type**  | **Default**  | **Required**  | **Description**  |
|  --- | --- | --- | --- | --- |
| `input_table` | string | — | Yes | Source transaction table in `dbname.table_name` format. |
| `output_table` | string | — | Yes | Destination table for training metrics. |
| `model_name` | string | — | Yes | Unique name used to save and retrieve the trained model. |
| `algorithm` | string | als | No | `"similar_to_latest"`. Must be set explicitly, since the default is `als`. |
| `user_column` | string | userid | No | Name of the customer identifier column. |
| `item_column` | string | itemid | No | Name of the item identifier column. |
| `tstamp_column` | string | tstamp | No | Name of the timestamp column. |
| `rating_column` | string | — | No | Name of an explicit rating or weight column, if you have one. |
| `filter_num` | int | 0 | No | Minimum number of users an item must have to be included in training. |
| **`k`** | int | 20 | No | Number of nearest neighbors considered per item. |
| **`distance`** | string | bm25 | No | Similarity measure: `bm25`, `tf-idf`, or `cosine`. |
| `similar_items_table` | string | — | No | Write the top similar items per item to this table, in `dbname.table_name` format. See [Similar Items Table](#similar-items-table). |
| `topk` | int | 10 | No | Number of similar items written per item when `similar_items_table` is set. |
| `filter_viewed` | boolean | false | No | Exclude items a customer has already interacted with. |


This algorithm does not use `popularity`, `period`, `begin_from`, `factors`, `regularization`, `iterations`, `alpha`, or `tunable`.

**`lastk` applies here at prediction time.** By default only the single most recent transaction drives the recommendation. Raising `lastk` uses that many recent items, ranks the `lastk * topk` candidates, and returns the best `topk`. This usually improves quality for customers who buy in baskets, at the cost of time and memory.

#### Workflow Example

```yaml
solution_arguments:
  model_name: "nbp_retail_sim_v1"
  algorithm: "similar_to_latest"
  user_column: "user_id"
  item_column: "item_id"
  tstamp_column: "timestamp"
  k: 20
  distance: "bm25"
```

## Relevant Topics

### Combining Algorithms for Coverage

No single algorithm covers everyone, which is why a common pattern is to train all three and merge their outputs.

| **Customer**  | **Best available algorithm**  |
|  --- | --- |
| In the training data, with history | `als`, the highest-quality personalization |
| Has at least one transaction but was not in training | `similar_to_latest` |
| Brand new, no transactions | `popular` |


Train each algorithm as its own model under its own `model_name`, run prediction for each, then merge the outputs in priority order: `als`, then `similar_to_latest`, then `popular`. Each customer takes the highest-priority recommendation available to them, and `popular` guarantees nobody comes back empty.

Merging happens outside NBP, in your own workflow or SQL. NBP does not combine models in a single task.

### Similar Items Table

Both `als` and `similar_to_latest` can write an item-to-item similarity table during training by setting `similar_items_table`.

| **Field**  | **Type**  | **Description**  |
|  --- | --- | --- |
| `target_item_id` | VARCHAR | The item being compared from. |
| `item_id` | VARCHAR | A similar item. |
| `score` | FLOAT | Similarity strength. Higher is more similar. |


This table is useful in its own right. It powers "customers also bought" modules without a per-user prediction run, and it gives you a way to recommend for customers who were filtered out of training by `filter_num` but who have at least one known purchase.

### Reading Training Metrics

Every training run appends a row to the metrics table with five ranking metrics at your chosen `topk`.

| **Metric**  | **What it rewards**  |
|  --- | --- |
| `precision_{topk}` | The share of recommended items that were relevant. Answers whether the list is clean. |
| `recall_{topk}` | The share of relevant items that made the list. Answers whether the list is complete. |
| `map_{topk}` | Precision averaged across positions, so correct items near the top count for more. |
| `ndcg_{topk}` | Similar to MAP but with a smoother position discount. The usual headline number for ranking quality. |
| `mrr_{topk}` | How high the first relevant item lands. Matters most when you show a single recommendation rather than a carousel. |


Higher is better for all five. Which one to lead with depends on how you display recommendations: `mrr` for a single hero slot, `ndcg` or `map` for a ranked carousel, `recall` when the list is a candidate pool for later filtering.

Because the table is append-only, the most useful reading is a trend rather than a single number. Compare the same algorithm across runs, and compare algorithms against `popular` as a baseline. A personalized model that cannot beat trending items is not earning its runtime.

### Scale and Parallelization

Training is designed to run infrequently, monthly for most teams. Prediction is cheaper and can run daily or weekly against the saved model.

For large profile bases, prediction parallelizes by splitting the input table across concurrent tasks. Two constraints shape how you split it:

- **Split by user, never randomly.** Splitting mid-user produces duplicate recommendations from multiple instances.
- **Parallelism has overhead.** Below roughly 10 million profiles, a single task usually finishes sooner than a parallelized run.


Training scales differently by algorithm. `als` must see the full dataset, since it cannot recommend for users absent from training. `similar_to_latest` and `popular` can be trained on a representative sample and then used to predict across the whole base, which is the usual approach at very large scale. For training data that includes every item, `similar_to_latest` needs the full catalog present or it will treat missing items as cold.

### Model Storage and Expiry

Trained models are saved to managed storage under `model_name`, unique per Treasure AI account, and retrieved automatically by `nbp_predict`.

Two things to plan around:

- **Models expire after six months.** After that a prediction run needs a freshly trained model, so retrain before the deadline rather than discovering it during a campaign.
- **Reusing a `model_name` overwrites the previous model**, regardless of which algorithm produced it. Since the recommended pattern is to run all three algorithms, put the algorithm in the name, for example `nbp_retail_als_v1`, to avoid one training run silently replacing another.


## FAQs

### Getting Started

**What data do I need?** A transaction-level table with one row per interaction, carrying a user identifier, an item identifier, and a timestamp. All three column names are configurable. There is no minimum history requirement, though `als` improves considerably with more.

**Which algorithm should I use?** Most teams use all three: `als` for established customers, `similar_to_latest` for anyone with at least one transaction, and `popular` as the fallback. Merge the outputs in that priority order. See [Combining Algorithms for Coverage](#combining-algorithms-for-coverage).

**Can I recommend content instead of products?** Yes. `itemid` can be any asset: an article, a video, a podcast episode, a service. NBP only needs a user, an item, and a time per interaction, and never looks at what the item actually is.

### Data and Coverage

**How do I handle new customers with no transaction history?** Use `popular` as a universal fallback. It requires nothing from the customer and always returns a list, which is why the standard pattern layers it underneath the other two.

**Why do some customers get no recommendations from `als`?** ALS can only recommend for users who were present in its training data. Anyone who arrived since the last training run is invisible to it. Cover them with `similar_to_latest` or `popular` rather than retraining more often.

**Can NBP recommend a brand-new product?** Not from `similar_to_latest`, which cannot recommend an item that appears zero times in training. `popular` will pick it up once it starts selling. For a launch, seed it through merchandising rules rather than expecting the model to surface it.

**Can I use pre-aggregated data?** No. NBP needs raw transaction-level rows and does its own aggregation. This is the opposite of RFM, which requires pre-aggregation.

### Tuning and Operating

**What do `topk` and `lastk` do?** `topk` sets how many items each customer receives, defaulting to 10 with a maximum of 100. `lastk` sets how many of the customer's recent transactions inform the recommendation, and applies only to `similar_to_latest`. Raising `lastk` usually helps for customers who buy in baskets.

**How often should I re-train?** Weekly suits most teams, since preferences shift slowly relative to the cost of training. Run prediction far more often, daily or hourly, against the saved model. Models expire after six months, so a retrain is required at least that often.

**Should I turn on `tunable` for `als`?** Only when you can absorb unpredictable runtime and memory. It learns hyperparameters from your data and usually improves quality, but it makes the job's resource profile hard to plan around on a fixed schedule.

**Does `filter_num` help?** It trims items that too few users have touched, which shrinks the model and speeds up training. The tradeoff is that filtered items become unrecommendable, so raise it only if long-tail coverage does not matter to you.

### Scale and Limits

**My dataset has 200M profiles. Can NBP handle it?** Yes, with care. Train `similar_to_latest` and `popular` on a representative sample and predict across the full base. `als` must train on the full set to cover those users. Split prediction across parallel tasks by user.

**When is parallelization worth it?** Above roughly 10 million profiles. See [Scale and Parallelization](#scale-and-parallelization).

**Can I train incrementally as new transactions arrive?** No. Training is designed for infrequent full runs. Partial and online training are not supported, so schedule a periodic full retrain instead.

**How do I activate recommendations in a campaign?** Join `rec_items` to your customer table so it becomes a Master Segment attribute, then activate through standard CDP activation workflows into Journeys, Triggers, Personalization, Segment Builder, and Agent Console.

## Glossary

| **Term**  | **Definition**  |
|  --- | --- |
| Cold start | The problem of recommending for users or items with little or no history. |
| Cold item | An item with too few training interactions to be recommended on its own. |
| Collaborative filtering | Recommending items based on patterns across many users' behavior rather than item attributes. |
| ALS | Alternating Least Squares, a matrix factorization technique that learns latent user and item preferences. |
| Matrix factorization | Representing the user-item interaction matrix as the product of two smaller matrices. |
| Popularity model | Recommends the most-purchased or most-viewed items across the whole base. |
| `similar_to_latest` | Recommends items most similar to a customer's most recent transaction. |
| `topk` | Number of items returned per customer. Default 10, maximum 100. |
| `lastk` | Number of recent transactions informing recommendations under `similar_to_latest`. |
| `filter_num` | Minimum number of users an item must have interacted with to enter training. |
| MAP, nDCG, Precision, Recall, MRR at K | Standard ranking metrics written to the training metrics table. Higher is better for all of them. See [Reading Training Metrics](#reading-training-metrics). |
| RecTools | The open-source recommendation library powering NBP. |