Files
mindfulness/spec_kit/Personalized Reco/spec.md
2026-02-02 11:22:35 +08:00

288 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Personalized Reco后端个性化推荐算法模块解耦版Spec索引版
> 阶段高层规范spec
>
> 目标:把「个性化推荐算法」做成后端**独立模块**,输入用户画像与曝光历史,模块内部从数据库拉取文案候选并排序,输出推荐句子(支持 Feed / Push / Widget 三种场景的可调参策略)。
>
> 依据:
>
> - `设计说明文档/個性化推薦算法規則.md`
> - `设计说明文档/句子文案打分規則.md`risk_flags 命名与语义必须严格对齐)
> - `spec_kit/User Profile Scoring/spec.md`用户画像输入契约V1.2
---
## 1. 背景与动机(摘要)
需要在后端实现一套跨场景一致、可调参、可观测、可回退的推荐流程并将其做成独立模块以便复用API / Celery 均可调用),降低 Push 等高风险场景的翻车概率。
已确认约束:
- 数据库当前未设计且为空:本需求内新增 DB 设计与迁移子模块。
- risk_flags 等内容画像语义:严格按 `句子文案打分规则` 执行。
- 推荐请求:由客户端在请求时传入 `user_profile` 与历史集合。
- Widget 情绪区间0.4~0.8):采用软降权。
- Push/Widget需要频控与冷却且 API 与 Celery 两种调用方式都需要。
---
## 2. 目标Goals
- **模块解耦**:推荐逻辑以独立包/子模块方式存在,暴露稳定接口,不与 FastAPI 路由/ Celery 任务强绑定。
- **统一 Pipeline**Hard Filter → Soft Scoring → Rerank多样性/新鲜度/疲劳并支持候选不足的回退梯度L0~L3
- **输入明确**:输入用户画像(允许字段缺失)、已推荐的文案 ID、已触达/浏览的文案 ID用于去重/疲劳/频控)。
- **从 DB 取候选**:模块内部负责根据画像与场景召回候选,并从数据库读取文案画像/风险标记/元信息。
- **输出可直接下发**:输出推荐句子(含 content_id、文本、排序分与必要的解释字段用于打点/调参)。
- **可观测**:对候选规模、过滤原因、回退层级、 served_k 等关键指标提供统一事件结构,便于落点与报表。
---
## 3. 非目标Non-goals
- **不在本阶段定义**内容生产生成句子、人工审核工作流、embedding/向量检索V2 扩展)。
- **不绑定具体表结构**:仅规定“必须能读到的字段契约”,具体表名/索引/迁移由后续 plan 细化。
- **不负责推送/Widget 排程**:模块只负责“给定场景与约束,返回推荐结果”,调度由上层系统决定。
---
## 4. 术语与数据对象Definitions摘要
### 4.1 场景Scene
- **feed**探索优先Exploration-first返回序列 TopK例如 30 条)。
- **push**精确与安全优先Precision-first通常返回 Top1或 TopN 供上层挑选)。
- **widget**稳定与品牌一致性优先Consistency-first返回 Top1每日一句
### 4.2 用户画像 UUserProfile
用户画像结构以客户端问卷输出为准V1.2),详见:
- `spec_kit/User Profile Scoring/spec.md`(字段与缺失约定)
- `modules/reco-engine/spec.md`(本模块如何消费画像与缺失判定)
### 4.3 文案画像 CContentProfile
模块需能从 DB 读到(或通过视图/派生字段得到):
- **content_id**(稳定主键)
- **text**(句子文本)
- **stage**(或可推导的 stage 标签)
- **emotion_score**0~1 或标记为 general
- **need_suitability / context_suitability**(离散适配表/JSON
- **personalization_power**0 / 0.5 / 1
- **risk_flags**(集合/数组/位图均可,语义一致即可)
- (可选)**author_id / template_id / review_confidence**
---
## 5. 模块边界与接口API Contract
### 5.1 入口函数(推荐引擎接口)
推荐模块对外暴露一个主入口,建议形式(不限定语言结构,但需表达同等信息):
- **输入**
- `scene`: `"feed" | "push" | "widget"`
- `user_profile`: `UserProfile`
- `already_recommended_ids`: `list[int|str]`
- `touched_or_viewed_ids`: `list[int|str]`(触达/浏览/点击等均归一为“已曝光”集合)
- `k`: intfeed 默认 30push/widget 默认 1
- `now`: 时间戳(用于时间衰减/冷却窗口)
- `constraints`(可选):如黑名单 author/template、最大风险等级、频控窗口天数等
- **输出**
- `items`: 推荐结果数组(长度 ≤ k
- `meta`: 本次推荐的统计与解释字段(用于打点/调参)
### 5.2 输出结果项RecommendedItem
每条推荐至少包含:
- `content_id`
- `text`
- `final_score`
- `fallback_level_final`
- `explanations`(轻量可选):如命中 need/context/stage、是否 general、被降个性化的原因缺失/低置信度/回退)
### 5.3 数据访问抽象Repository / Gateway
为实现解耦,推荐模块**不得**直接依赖 FastAPI 的 `Depends`;应通过注入的 `ContentRepository` 读取数据:
- `fetch_candidates(scene, user_profile, limit, fallback_level) -> list[ContentProfile]`
- `fetch_contents_by_ids(ids) -> list[ContentProfile]`(用于补全曝光历史的画像信息时可用)
实现层可使用 `AsyncSession`SQLAlchemy完成具体查询但算法层不感知 ORM/SQL。
---
## 6. 推荐流程(统一 Pipeline
本节是工程必须遵守的“骨架”,参数可按场景配置。
### 6.1 Candidate Generation候选集生成
核心要求:
- 依据 `U` 召回一个候选池:匹配 need/context/stage 的内容 + 一部分通用内容general
- 若问卷允许跳过导致字段缺失need/context/emotion_score 缺失),候选池需自动提高通用安全内容占比,并视为至少进入 **L1**(限制 `personalization_power ≤ 0.5`)。
### 6.2 Hard Filter硬性过滤
基于 `risk_flags` 与产品规则做剔除(说明文档 3.x
- `block_health_medical`:全场景硬过滤。
- `U.stage.unknown=1`:过滤 `unsafe_for_stage_unknown`
- `U.stage.parenting=1`:过滤 `unsafe_for_stage_parenting`
- `U.emotion_score≤0.2`:过滤 `unsafe_for_emotion_low`
- 跨维度规则:`U.stage.unknown=1``need_suitability[parenting_pressure]=1``personalization_power=1` → 禁推。
Hard Filter 后需输出 `candidate_pool_size_after_hard_filter` 以及若清空的 `empty_reason`
### 6.3 Soft Scoring软性打分
采用说明文档的线性加权结构:
最终分:
\[
final\_score(U,C_i)=\mathbb{I}[pass]\times\Big(S_{core}+S_{personal}+S_{fresh}-P_{fatigue}-P_{repeat}-P_{risk}-P_{uncertainty}\Big)
\]
其中
\[
S_{core}=w_{need}S_{need}+w_{emotion}S_{emotion}+w_{stage}S_{stage}+w_{context}S_{context}
\]
缺失字段的保守计算(说明文档 V1.2,必须实现):
- `U.need` 缺失 → `S_need = 0.5`
- `U.context` 缺失 → `S_context = 0.5`
- `U.emotion_score` 缺失 → `S_emotion = 0.8`general 仍为 0.8
个性化加成(必须受回退梯度约束):
\[
S_{personal}=\alpha \cdot personalization\_power \cdot \max(S_{need}, S_{context})
\]
不确定性惩罚Push 建议默认开启,模块需支持开关):
\[
P_{uncertainty}=\beta \cdot (1-conf_U)\cdot(1-conf_{C_i})\cdot personalization\_power
\]
### 6.4 Rerank重排多样性/新鲜度/疲劳)
最小要求:
- **去重**:排除 `already_recommended_ids``touched_or_viewed_ids`(同一句不重复)。
- **多样性**:作者/模板/标签多样性Feed 场景建议使用 MMR说明文档 4.5)。
- **频控与冷却**Push/Widget 必做):同句/同作者/同模板在窗口 X 天内不重复(具体 X 由 plan 定参数)。
---
## 7. 回退策略Fallback Ladder
候选不足或过滤/频控导致 served_k 过少时,必须按梯度回退,并输出最终 `fallback_level_final`
- **L0正常**:按场景默认召回配比。
- **L1放宽匹配**need/context 命中阈值放宽;`personalization_power ≤ 0.5`
- **L2回退通用池**:增加 general 低风险句;`personalization_power = 0`
- **L3兜底安全池**:仅从通用安全句库/白名单池抽取。
规则:
- 回退时必须“降个性化与降风险”,尤其 Push。
- 每次回退都要重新统计候选规模,并记录触发原因(如 `hard_filter_all` / `freqcap_all` / `pool_empty`)。
---
## 8. 场景默认参数V1 建议)
### 8.1 FeedExploration-first
- 候选配比:匹配 60% + 通用 30% + 探索 10%
- 权重建议:`w_need=0.35, w_emotion=0.20, w_stage=0.15, w_context=0.30`
- 序列Top1 最高分,后续使用 MMRλ=0.7
### 8.2 PushPrecision & Safety-first
- 候选配比need 强命中 70% + emotion 命中 20% + 通用安全 10%
- 权重建议:`w_need=0.45, w_emotion=0.35, w_stage=0.15, w_context=0.05`
- 默认启用 `P_uncertainty`;当 `fallback_level_final>0` 或画像缺失/低置信度时自动降个性化
### 8.3 WidgetConsistency & Brand-first
- 候选池:以 general + `personalization_power≤0.5` 为主
- 权重建议:`w_need=0.25, w_emotion=0.25, w_stage=0.30, w_context=0.20`
- 情绪区间约束:建议 `emotion_score` 限制在 0.4~0.8(过低/过高降权或过滤,具体由 plan 定)
---
## 9. 数据库与存储契约DB Contract
模块需要 DB 提供以下能力(不限定表名实现方式):
- **按标签召回**:能按 need/context/stage/general、personalization_power、risk_flags 等条件筛选候选。
- **按 ID 批量拉取**:用于补全曝光历史或在 rerank 时补字段。
- **安全池支持**L3 兜底必须能拉到一批“通用安全句”(白名单/低风险集合)。
性能要求V1
- 单次推荐k<=30应避免 N+1 查询;候选批量拉取 + 内存打分。
- 查询需可加索引:`stage``general``personalization_power`、(以及用于召回的标签字段)。
---
## 10. 可观测性(事件与指标)
每次推荐Feed session / 每次 Push / 每日 Widget必须能产出以下字段由上层统一打点即可
- `candidate_pool_size_raw`
- `candidate_pool_size_after_hard_filter`
- `candidate_pool_size_after_dedup`
- `candidate_pool_size_after_freqcap`
- `fallback_level_final`
- `served_k`
- `empty_reason`served_k=0 时必填)
建议额外输出(用于调参/排查):
- `scene`
- `conf_U`
- `missing_fields`need/context/emotion 的缺失情况)
- `risk_filtered_count_by_flag`(可选聚合)
---
## 11. 安全与合规
- 严禁返回 Hard Filter 禁推内容。
- 对 Push 场景,默认倾向“低风险 + 低个性化强度”;当画像不确定或字段缺失时必须进一步降个性化。
- 不在仓库写入真实数据库账号密码;连接串统一从 `DATABASE_URL` 环境变量读取(已由 `server/app/core/config.py` 支持)。
---
## 12. 验收标准Acceptance Criteria
- 能以统一接口在三种场景运行:输入画像 + 曝光集合 → 输出 TopK 句子。
- 画像字段缺失时仍能产出排序,并触发降个性化策略(至少 L1不会报错。
- Hard Filter 规则生效:命中 `block_health_medical` 等风险标记的内容绝不出现在输出中。
- 候选不足时会逐级回退并输出 `fallback_level_final`,不会出现无输出且无原因字段的情况。
- 输出包含用于上层打点的 meta 统计字段,便于观测候选规模与回退率。
---
## 13. 子模块规范索引modules/
> 子模块规范用于实现拆分;本文件保留大需求高层约束与对外契约摘要。
- `modules/db-design/spec.md`:数据库设计与迁移(当前库为空,本需求内补齐)
- `modules/content-repository/spec.md`:数据访问层(按画像与场景拉取候选、按 ID 批量查)
- `modules/reco-engine/spec.md`:推荐引擎编排(候选→过滤→打分→重排→回退)
- `modules/scoring/spec.md`:打分与惩罚项(严格对齐规则文档;含缺失字段保守策略)
- `modules/rerank-freqcap/spec.md`:重排/去重/频控Feed 的 MMRPush/Widget 冷却)
- `modules/observability/spec.md`可观测事件结构与指标candidate_pool_size_* 等)
- `modules/integration-api-worker/spec.md`API 与 Celery 集成(请求入参、响应、任务封装)