# 子模块:Rerank & Freqcap(重排 / 去重 / 频控)|Spec ## 1. 目标描述 对 Soft Scoring 后的候选集做最终的“可下发排序”,解决: - **去重**:避免同一句在短期内反复出现。 - **多样性**:Feed 场景需要序列多样性(作者/模板/标签)。 - **频控与冷却**:Push/Widget 必做,按句子/作者/模板维度做窗口期不重复。 > 当前确认:历史集合由客户端在请求时传入(已推荐 ID、已触达/浏览 ID)。 --- ## 2. 输入 / 输出定义 ### 2.1 输入 - `scene`: `feed | push | widget` - `scored_candidates`: `List[{ content_id, score, content_profile... }]` - `already_recommended_ids`: `List[str|int]` - `touched_or_viewed_ids`: `List[str|int]` - (可选)`recent_author_ids` / `recent_template_ids`(若客户端暂不传,可由后续服务端侧补齐) - `k`: 目标条数 - `config`: - Feed:`mmr_lambda`(默认 0.7) - 冷却窗口:`cooldown_sentence_days / cooldown_author_days / cooldown_template_days` ### 2.2 输出 - `ranked_items`: 排好序的候选(长度 ≤ k) - `meta`: - `candidate_pool_size_after_dedup` - `candidate_pool_size_after_freqcap` - `freqcap_filtered_counts`(可选,按维度统计) --- ## 3. 规则与算法(V1 最小集合) ### 3.1 去重(必做) - 过滤 `content_id` ∈ `already_recommended_ids ∪ touched_or_viewed_ids` 的候选。 - 去重键定义(对齐算法规则的工程约定): - `sentence_key = content_id` - `author_key = author_id`(可空) - `template_key = template_id`(可空) ### 3.2 Feed:MMR 序列重排(建议) - Top1:最高分,确保第一眼命中。 - 后续:使用 MMR 选择序列,最大化与已选内容的差异(作者/模板/need/context/stage)。 MMR 定义: \[ MMR(c)=\lambda\cdot Rel(c) - (1-\lambda)\cdot \max_{s\in S} Sim(c,s) \] 其中: - `Rel(c)`:可直接使用 `final_score` - `Sim(c,s)`:离散特征版(V1 推荐): - content_id 相同:1 - template_id 相同:+0.6 - author_id 相同:+0.3 - 标签重合(Jaccard):+0.1 * Jaccard(tags_c, tags_s) - clamp 到 [0,1] ### 3.3 Push/Widget:频控与冷却(必做) 最小要求: - 同一句在窗口 X 天内不重复(句子冷却)。 - 同作者/同模板在窗口期内尽量不重复(可作为硬频控或强降权,plan 阶段定)。 > 当前 V1 输入侧只确认有 content_id 历史;若 author/template 历史暂不具备,可先对 content_id 强硬去重,并将 author/template 频控作为“可选增强”(meta 仍需统计缺失原因)。 --- ## 4. 验收标准(可验证) - **去重正确**:输出不包含 `already_recommended_ids` 与 `touched_or_viewed_ids`。 - **Feed 序列多样**:在候选足够时,序列中不会出现大量同作者/同模板连续重复(可用统计阈值验收)。 - **Push/Widget 频控生效**:在冷却窗口内同一句不会再次被推荐(基于传入历史集合验证)。 - **可观测**:输出 meta 中包含 `after_dedup/after_freqcap` 的候选规模,便于排查 served_k 不足原因。