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:
ABCAbstract 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:
BaseRerankerReranks 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 – balancedExample:
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:
BaseRerankerSimple 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:
BaseRerankerReranks 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 groupExample:
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:
BaseRerankerApply 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
**kwargsand ignored.