Reranking#

Stage 3 of the recommendation pipeline. Rerankers apply business rules, diversity constraints, and fairness adjustments.

Quick Start#

from corerec.reranking import DiversityReranker, BusinessRulesReranker

diversity = DiversityReranker(lambda_=0.7)
business = BusinessRulesReranker()
business.add_boost(item_id=42, multiplier=2.0)
business.add_blocklist([999, 998])

API Reference#

Base classes for reranking stage.

Rerankers modify the ranked list to satisfy constraints beyond pure relevance: diversity, fairness, business rules, etc.

class corerec.reranking.base.BaseReranker(name: str | None = None)[source]#

Bases: ABC

Abstract base class for all rerankers.

A reranker takes a ranked list and modifies it to satisfy additional constraints. Unlike rankers which focus on relevance, rerankers balance relevance with other objectives.

Common reranking objectives: - Diversity: avoid showing too-similar items - Fairness: ensure exposure across item groups - Freshness: boost recently added items - Business: promote certain items, apply filters

abstractmethod rerank(ranked: List[RankedCandidate] | RankingResult, context: Dict[str, Any] | None = None, **kwargs) → RankingResult[source]#

Rerank the candidates.

Parameters:
  • ranked – ranked candidates from Stage 2

  • context – user context, constraints, etc.

  • **kwargs – reranker-specific parameters

Returns:

RankingResult with reranked candidates

Diversity Reranker#

Diversity Reranking

Prevents recommendation lists from being too homogeneous. Users get bored seeing the same type of items repeatedly.

class corerec.reranking.diversity.DiversityReranker(lambda_: float = 0.7, similarity_fn: Callable[[Any, Any], float] | None = None, category_key: str | None = None, name: str = 'diversity')[source]#

Bases: BaseReranker

Reranks to maximize diversity while preserving relevance.

Uses Maximal Marginal Relevance (MMR) or similar algorithms to iteratively select items that are both relevant AND different from already selected items.

Trade-off controlled by lambda_: - lambda_ = 1.0 – pure relevance (no diversity) - lambda_ = 0.0 – pure diversity (ignore relevance) - lambda_ = 0.5 – balanced

Example:

reranker = DiversityReranker(
    lambda_=0.7,
    similarity_fn=cosine_similarity,  # how to measure item similarity
)
diverse_recs = reranker.rerank(ranked_candidates)
__init__(lambda_: float = 0.7, similarity_fn: Callable[[Any, Any], float] | None = None, category_key: str | None = None, name: str = 'diversity')[source]#
Parameters:
  • lambda – trade-off between relevance (1) and diversity (0)

  • similarity_fn – function(item_a, item_b) -> similarity score

  • category_key – if set, diversify by this categorical attribute

  • name – identifier for this reranker

rerank(ranked: List[RankedCandidate] | RankingResult, context: Dict[str, Any] | None = None, top_k: int | None = None, **kwargs) → RankingResult[source]#

Rerank for diversity using MMR.

Parameters:
  • ranked – candidates sorted by relevance

  • context – optional context (unused by default)

  • top_k – number of items to return (default: all)

class corerec.reranking.diversity.CategorySpreadReranker(category_fn: Callable[[Any], Any], max_consecutive: int = 2, name: str = 'category_spread')[source]#

Bases: BaseReranker

Simple diversity by spreading categories.

Ensures no more than max_per_category consecutive items from the same category. Simpler than MMR, faster too.

Example:

reranker = CategorySpreadReranker(
    category_fn=lambda item: item_categories[item],
    max_consecutive=2
)
__init__(category_fn: Callable[[Any], Any], max_consecutive: int = 2, name: str = 'category_spread')[source]#
Parameters:
  • category_fn – function(item_id) -> category

  • max_consecutive – max items from same category in a row

  • name – identifier

rerank(ranked: List[RankedCandidate] | RankingResult, context: Dict[str, Any] | None = None, **kwargs) → RankingResult[source]#

Rerank to spread categories.

Fairness Reranker#

Fairness Reranking

Ensures fair exposure across item groups (e.g., sellers, brands). Prevents popularity bias from completely dominating recommendations.

class corerec.reranking.fairness.FairnessReranker(group_fn: Callable[[Any], Any], objective: str = 'proportional', fairness_weight: float = 0.3, group_targets: Dict[Any, float] | None = None, name: str = 'fairness')[source]#

Bases: BaseReranker

Reranks to ensure fair exposure across item groups.

Without intervention, popular items/sellers dominate recommendations. This reranker adjusts exposure to be more equitable while still respecting relevance.

Fairness objectives: - proportional – exposure proportional to group size - equal – equal exposure across groups - min_exposure – ensure minimum exposure per group

Example:

reranker = FairnessReranker(
    group_fn=lambda item: item_to_seller[item],
    objective='proportional',
    fairness_weight=0.3
)
fair_recs = reranker.rerank(ranked_candidates)
__init__(group_fn: Callable[[Any], Any], objective: str = 'proportional', fairness_weight: float = 0.3, group_targets: Dict[Any, float] | None = None, name: str = 'fairness')[source]#
Parameters:
  • group_fn – function(item_id) -> group_id

  • objective – ‘proportional’, ‘equal’, or ‘min_exposure’

  • fairness_weight – how much to weight fairness vs relevance

  • group_targets – explicit target exposure per group

  • name – identifier for this reranker

rerank(ranked: List[RankedCandidate] | RankingResult, context: Dict[str, Any] | None = None, top_k: int | None = None, **kwargs) → RankingResult[source]#

Rerank for fairness.

Uses a greedy algorithm that balances relevance with exposure fairness.

Business Rules Reranker#

Business Rules Reranking

Apply custom business logic: promotions, filtering, boosting, etc. This is where domain-specific rules live.

class corerec.reranking.business.BusinessRulesReranker(name: str = 'business_rules')[source]#

Bases: BaseReranker

Apply configurable business rules to rerank items.

Rules can: - Boost specific items (promotions) - Filter out items (blocklist) - Pin items to positions (sponsored) - Apply custom transformations

Example

reranker = BusinessRulesReranker() reranker.add_boost(item_id=123, multiplier=2.0) reranker.add_filter(lambda item: item not in blocked_items) reranker.add_pin(item_id=456, position=1) # sponsored slot

result = reranker.rerank(ranked_candidates)

add_boost(item_id: Any, multiplier: float) → BusinessRulesReranker[source]#

Boost an item’s score by a multiplier.

Parameters:
  • item_id – item to boost

  • multiplier – score multiplier (>1 = boost, <1 = demote)

add_filter(filter_fn: Callable[[Any], bool]) → BusinessRulesReranker[source]#

Add a filter function.

Items where filter_fn(item_id) returns False are removed.

Parameters:

filter_fn – function(item_id) -> keep (True/False)

add_pin(item_id: Any, position: int) → BusinessRulesReranker[source]#

Pin an item to a specific position.

Useful for sponsored/promoted slots.

Parameters:
  • item_id – item to pin

  • position – 1-indexed position to pin to

add_blocklist(item_ids: List[Any]) → BusinessRulesReranker[source]#

Block items from appearing in results.

Parameters:

item_ids – items to block

clear_rules() → BusinessRulesReranker[source]#

Clear all rules.

rerank(ranked: List[RankedCandidate] | RankingResult, context: Dict[str, Any] | None = None, top_k: int | None = None, **kwargs) → RankingResult[source]#

Apply business rules and rerank.

The incoming order is treated as the answer from the previous stage and kept as-is unless a rule here actually changes something. Before, this always re-sorted by .score, which is the original relevance score, so running it after DiversityReranker or FairnessReranker quietly put the list back in relevance order and threw their work away – even with no rules configured at all.

top_k truncates the output, same as the other rerankers. It used to be swallowed by **kwargs and ignored.