12 KiB
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 用户画像 U(UserProfile)
用户画像结构以客户端问卷输出为准(V1.2),详见:
spec_kit/User Profile Scoring/spec.md(字段与缺失约定)modules/reco-engine/spec.md(本模块如何消费画像与缺失判定)
4.3 文案画像 C(ContentProfile)
模块需能从 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:UserProfilealready_recommended_ids:list[int|str]touched_or_viewed_ids:list[int|str](触达/浏览/点击等均归一为“已曝光”集合)k: int(feed 默认 30,push/widget 默认 1)now: 时间戳(用于时间衰减/冷却窗口)constraints(可选):如黑名单 author/template、最大风险等级、频控窗口天数等
- 输出:
items: 推荐结果数组(长度 ≤ k)meta: 本次推荐的统计与解释字段(用于打点/调参)
5.2 输出结果项(RecommendedItem)
每条推荐至少包含:
content_idtextfinal_scorefallback_level_finalexplanations(轻量可选):如命中 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.5U.context缺失 →S_context = 0.5U.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 Feed(Exploration-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 Push(Precision & 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 Widget(Consistency & 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_rawcandidate_pool_size_after_hard_filtercandidate_pool_size_after_dedupcandidate_pool_size_after_freqcapfallback_level_finalserved_kempty_reason(served_k=0 时必填)
建议额外输出(用于调参/排查):
sceneconf_Umissing_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 的 MMR;Push/Widget 冷却)modules/observability/spec.md:可观测事件结构与指标(candidate_pool_size_* 等)modules/integration-api-worker/spec.md:API 与 Celery 集成(请求入参、响应、任务封装)