新功能:个性化推荐算法

This commit is contained in:
吕新雨
2026-02-02 16:47:37 +08:00
parent 936094211b
commit 6dc4e2b943
119 changed files with 7427 additions and 357 deletions

View File

@@ -0,0 +1,172 @@
# Rerank & Freqcap重排 / 去重 / 频控Tasks
> 对应计划:`spec_kit/Personalized Reco/modules/rerank-freqcap/plan.md`
>
> 本清单执行原则:
>
> - 本模块只做 **Dedup / Freqcap / Feed MMR 重排**,不做 Soft Scoring 与 Hard Filter。
> - V1 冷却窗口以“输入集合代表窗口内历史”为准(后续接入服务端历史再按天计算)。
---
## 0. 任务标记规则
- 用勾选框标记执行状态:
- `[ ]` 未开始
- `[x]` 已完成
- 每个任务都要求可独立验收(有明确产出/可运行的检查方式)。
---
## 1. 文档对齐(先把口径写死,避免实现漂移)
- [x] 1.1 校对 `modules/rerank-freqcap/spec.md``modules/rerank-freqcap/plan.md` 一致性
- **检查点**
- 输入:`scene/scored_candidates/history/k/config` 字段与命名一致
- 输出:`ranked_items``meta` 字段集合一致
- 去重键:`sentence_key=content_id``author_key``template_key` 口径一致
- FeedMMR λ=0.7 与 Sim 规则一致
- **验收**两份文档不存在冲突描述且“V1 冷却窗口语义”写清楚(集合代表窗口内历史)。
- [x] 1.2 在 `plan.md` 中补充/固定“标签构造策略”与“候选截断策略”(若后续要改再更新)
- **变更点**
- 明确 `tags` 的 V1 定义:`stage + need_argmax + context_argmax`
- 明确 `top_n_for_mmr` 默认值与作用(性能兜底)
- **验收**:实现时不会出现“标签到底取哪些 key”的二义性。
---
## 2. 目录与骨架(与推荐子模块同级)
- [x] 2.1 新建目录 `server/app/features/personalized_reco/rerank_freqcap/`
- **包含**
- `__init__.py`
- `types.py``ScoredCandidate``RerankConfig``RerankMeta``RerankResult`
- `defaults.py`(按 scene 的默认参数λ、cooldown_*、top_n_for_mmr
- `utils.py`ID 归一化、Jaccard、tag 构造等)
- `rerank.py`(主入口 `rerank_and_freqcap`
- **验收**:可通过 `app.features.personalized_reco.rerank_freqcap.*` 正常 import。
---
## 3. 类型与接口(稳定契约,便于 reco-engine 调用)
- [x] 3.1 定义 `ScoredCandidate`(最小字段集合)
- **必须字段**
- `content_id: int`
- `final_score: float`
- **建议字段**(用于多样性/频控):
- `author_id: str | None`
- `template_id: str | None`
- `content_profile`(至少能取到 stage/need_suitability/context_suitability缺失时需降级
- **验收**:能承载 MMR 相似度计算所需数据;缺失字段不会导致异常。
- [x] 3.2 定义 `RerankConfig`(可调参)
- **字段**
- Feed`mmr_lambda`(默认 0.7)、`top_n_for_mmr`(默认 200
- Push/Widget`cooldown_sentence_days/cooldown_author_days/cooldown_template_days`(用于配置与可观测)
- **验收**:能从 scene 推导默认 config或由调用方传入覆盖
- [x] 3.3 定义 `RerankMeta``RerankResult`
- **meta 必须字段**
- `candidate_pool_size_after_dedup`
- `candidate_pool_size_after_freqcap`
- `missing_history_fields`
- **建议字段**
- `freqcap_filtered_counts`sentence/author/template
- **验收**:字段集合固定;任何输入都能产出 meta包括候选为空
---
## 4. 核心算法实现V1 最小集合)
- [x] 4.1 实现历史 ID 归一化(避免 str/int 混用漏过滤)
- **规则**
- `already_recommended_ids` / `touched_or_viewed_ids` 尽量转为 `int` 集合
- 转换失败的值忽略并记录 debug
- **验收**:单测覆盖 `["1", 2, "bad"]` 等混合输入,过滤结果正确且稳定。
- [x] 4.2 实现 Dedup必做
- **规则**:过滤 `content_id ∈ seen_ids` 的候选
- **产出**`meta.candidate_pool_size_after_dedup`
- **验收**:输出不包含历史出现过的 `content_id`
- [x] 4.3 实现 FreqcapPush/Widget 必做Feed 可选)
- **V1 策略**
- 句子维度(必做):同句硬过滤(使用 dedup 的 seen_ids 即可)
- 作者/模板维度(增强项):
- 若提供 `recent_author_ids`:命中则硬过滤;否则在 `missing_history_fields` 记录 `author`
- 若提供 `recent_template_ids`:命中则硬过滤;否则在 `missing_history_fields` 记录 `template`
- **产出**
- `candidate_pool_size_after_freqcap`
- `freqcap_filtered_counts`(建议)
- **验收**:在有/无 `recent_*_ids` 输入时行为一致且可解释。
- [x] 4.4 实现 Feedtag 构造与相似度 `Sim`
- **tag 规则V1 写死)**
- `stage:<stage>`
- `need:<argmax_key>`(从 `need_suitability` 取最大值 key为空则跳过
- `context:<argmax_key>`(从 `context_suitability` 取最大值 key为空则跳过
- **Jaccard**`|A∩B|/|AB|`,空集合时返回 0
- **Sim 累加规则**
- 同 content_id → 1
- template_id 相同且非空 → +0.6
- author_id 相同且非空 → +0.3
- +0.1 * Jaccard(tags)
- clamp 到 `[0,1]`
- **验收**:单测覆盖缺失字段(无 author/template/tags时仍能算出稳定 Sim。
- [x] 4.5 实现 FeedMMR 选序列
- **规则**
- Top1`final_score` 最大
- 后续:按 `MMR(c)=λ*Rel(c)-(1-λ)*maxSim` 选择
- `Rel=final_score`
- **性能兜底**:先截断候选到 `top_n_for_mmr` 再做 MMR
- **验收**
- Top1 恒等于最高分
- 候选足够时,序列不出现大量同作者/同模板紧邻重复(可用阈值断言)
- [x] 4.6 实现 Push/Widget最终 TopK
- **规则**dedup+freqcap 后按 `final_score` 降序取前 k 条
- **验收**:输出长度 ≤ k且分数单调不增允许相等
- [x] 4.7 实现主入口 `rerank_and_freqcap(...) -> RerankResult`
- **规则**
- 三场景共用 dedup
- FeedMMRPush/WidgetTopK
- 必须输出 meta即使 ranked_items 为空)
- **验收**:任何输入(含空候选)不抛异常,并输出稳定结构。
---
## 5. 单元测试pytest纯函数为主
- [x] 5.1 新建测试文件 `server/tests/test_rerank_freqcap.py`
- **用例覆盖**
- dedup历史集合过滤正确含 str/int 混用)
- freqcap有/无 recent_author/template 的分支与 meta 缺失标记
- feed mmrTop1=最高分;后续避免同作者/模板紧邻(构造数据断言)
- push/widgetTopK 输出正确
- **验收**`pytest -q tests/test_rerank_freqcap.py` 通过。
---
## 6. 最终自检清单(合入前)
- [x] 6.1 文档一致性检查
- **验收**`spec.md` / `plan.md` / 实现接口签名三者一致(尤其 meta 字段与默认参数)。
- [x] 6.2 回归检查(不影响已实施模块)
- **验收**:不修改 `content-repository``scoring` 的既有逻辑;仅新增 `rerank-freqcap` 模块与测试。
- [x] 6.3 全量测试通过
- **命令**(在 `server/`
- `pytest -q`
- **验收**:所有用例通过。
- [x] 6.4 全部完成后更新大规范 `overview.md`
- **变更点**
-`modules/rerank-freqcap/` 标记为“已实施”
- 增加一条变更记录(日期 + 交付物plan/tasks/代码/测试)
- **验收**`spec_kit/Personalized Reco/overview.md` 中模块状态与交付记录准确。