# 正念 APP|三種分發場景推薦算法方案(Feed / Push / Widget · V1) > **目標**: > 針對三種分發方式(Feed、App Push、Widget 每日一句)制定一套一致但可調參的推薦流程。 > > **共用基礎**: > > * 用戶畫像:U(mom_stage one-hot;emotion/context/need 允许缺失/跳过;含 `profile_confidence/profile_answered`) > * 文案畫像:Cᵢ(stage、emotion_score、context/need suitability、personalization_power、risk_flags;建議含 content_id/author_id/template_id/review_confidence) > * 推薦骨架:Hard Filter → Soft Scoring → Rerank(多樣性/新鮮度/疲勞) --- ## 一、先說整體流程與思路(適用三種場景) ### 1) 核心理念:同一個用戶同一天需要「不同強度」的個性化 * **Push**:最容易翻車 → **高精準 + 低風險**(Precision-first) * **Widget**:每天唯一一句 → **穩定、代表性、可長期持續**(Consistency-first) * **Feed**:可上下滑探索 → **可試探、可擴展、多樣性**(Exploration-first) ### 2) 共用三段式 Pipeline 1. **Candidate Generation(候選集生成)** * 依 U 取一個「候選池」:匹配 need/context/stage 的內容 + 一部分通用內容 * V1.2:若問卷允許跳過導致 `U.need/U.context/U.emotion_score` 缺失,候選池需自動提高通用安全內容占比,并视为至少进入 L1(限制 `personalization_power≤0.5`) 2. **Hard Filter(硬性過濾)** * 依 risk_flags 與產品規則剔除高風險內容 3. **Soft Scoring(軟性打分)** * 用加權模型計算核心匹配分 4. **Rerank(重排)** * 去重、作者/模板多樣性、頻控、時間衰減 5. **Serve(分發)** * 依場景不同策略選 TopK 或組成序列 ### 3) 工程必備:候選不足回退策略 + 可觀測指標(V1.1) #### 3.1 候選不足回退(Fallback Ladder) > 目的:避免「過濾/頻控後候選為空」導致體驗斷崖;同時在回退時**自動降個性化與降風險**(尤其 Push)。 共用定義: * `fallback_level`:從 0 開始遞增(0=不回退) * 回退原則:**先放寬匹配,再放寬探索,再回退到通用安全池**;同時限制 `personalization_power` 上限 建議梯度(可按場景調整): * **L0(正常)**:按原候選配比取候選 * **L1(放寬匹配)**:`need/context` 由 `1 → ≥0.5`;`personalization_power` 上限降為 `≤0.5` * **L2(回退通用池)**:加入更多 `general`/低風險句;`personalization_power` 上限降為 `0` * **L3(兜底)**:僅從「通用安全句庫」抽取(需單獨維護白名單/安全池) 每次請求需輸出最終使用的 `fallback_level`(用於分析覆蓋率與風險)。 #### 3.2 可觀測指標(必打點) 每次生成推薦(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,記錄原因(如 hard_filter_all / freqcap_all / pool_empty / unknown) 核心監控衍生指標(報表層計算): * **覆蓋率**:`served_k > 0` 的比例(按場景分開) * **回退率**:`fallback_level_final > 0` 的比例 * **頻控後候選數分布**:`candidate_pool_size_after_freqcap` 的 P50/P90/P99 --- ## 二、總分結構(Score Decomposition) 對任意文案 Cᵢ: [ \textbf{final_score}(U,Cᵢ) = \mathbb{I}[\text{pass filters}] \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} ] * `S_need = Cᵢ.need_suitability[U.need]`(若 U.need 缺失,见 V1.2) * `S_context = Cᵢ.context_suitability[U.context]`(若 U.context 缺失,见 V1.2) * `S_stage`:general=1;命中=1;unknown對非unknown=0.7;其餘=0 * `S_emotion`:若文案 general→0.8;否則 `1-|U.emotion_score-Cᵢ.emotion_score|`(若 U.emotion_score 缺失,见 V1.2) ### V1.2:当问卷允许跳过题目时的打分约定(工程必读) > 目标:用户画像字段不全时仍能产出排序;并在不确定时自动降个性化/降风险。 判定缺失: * `U.need` 为空对象 `{}` 或不存在 → 视为缺失 * `U.context` 为空对象 `{}` 或不存在 → 视为缺失 * `U.emotion_score` 为 `null`/不存在 → 视为缺失 缺失时的保守计算: * `S_need`: * 若 `U.need` 缺失 → `S_need = 0.5` * 否则 → `S_need = Cᵢ.need_suitability[U.need]` * `S_context`: * 若 `U.context` 缺失 → `S_context = 0.5` * 否则 → `S_context = Cᵢ.context_suitability[U.context]` * `S_emotion`: * 若 `U.emotion_score` 缺失 → `S_emotion = 0.8` * 否则按原定义计算(文案 general 仍为 0.8) 额外约束(与回退梯度一致,6A): * 若 `conf_U` 偏低或字段缺失明显:必须启用 `P_uncertainty` * 若进入回退梯度(`fallback_level_final>0`):按 L1/L2/L3 限制 `personalization_power` 上限(见 3.1) 個性化加成: [ S_{personal} = \alpha \cdot Cᵢ.personalization_power \cdot \max(S_{need}, S_{context}) ] 不確定性懲罰(Push 建議啟用): [ P_{uncertainty} = \beta \cdot (1-\text{conf}_U) \cdot (1-\text{conf}_{Cᵢ}) \cdot Cᵢ.personalization\_power ] > 說明: > > * `conf_U`:用戶畫像置信度(建議由問卷生成後隨時間衰減;見「用戶畫像」文件 V1.1) > * `conf_{Cᵢ}`:內容標註置信度(`Cᵢ.review_confidence`;缺省可給 0.7) > * `P_{uncertainty}` 可併入 `P_risk`(工程上更簡單),但需保留獨立打點便於調參 --- ## 三、Hard Filter(共用禁推規則) ### 3.1 risk_flags(內容自帶) > V1.1 起,risk_flags 命名/語義以「句子文案打分規則」為準(`unsafe_for_*` / `block_*` / `soft_*`)。 * `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` * `Cᵢ.risk_flags` 含 `block_health_medical` → **全场景禁推(Hard Filter)**(仅挡「医疗/诊断/严重暗示」) > `soft_health_sensitive` 不做 Hard Filter,改由 Soft Penalty(見 Push 的 P_risk) ### 3.2 產品規則(跨維度) * `U.stage.unknown=1` 且 `Cᵢ.need_suitability[parenting_pressure]=1` 且 `Cᵢ.personalization_power=1` → 禁推 --- # 四、Feed 模式(App 內上下滑)— Exploration-first ## 4.1 目標 * 讓用戶快速感到「被理解」 * 同時探索更多題材,避免內容疲乏 * 允許一定程度的試探(比 Push 寬鬆) ## 4.2 流程(Feed Session) 1. **建候選池**(大) * 60%:匹配 need/context 的內容(含 personalization_power=0.5/1) * 30%:通用內容(general) * 10%:探索內容(與 context/need 邊緣相關) 2. Hard Filter(仍嚴格) 3. Soft Scoring(偏多樣性) 4. Rerank:用 MMR 做序列 ## 4.3 Feed 打分權重(建議) * `w_need=0.35, w_emotion=0.20, w_stage=0.15, w_context=0.30` > Feed 更重視「語境共鳴」與內容多樣性,所以 context 權重上調。 ## 4.4 Feed 序列重排(MMR) * Top1:最高分(確保第一眼命中) * 後續:在高分候選中,最大化與已選內容的差異(作者/模板/need/context) ### 4.5 Feed 的 MMR 定義(工程側定義,V1.1) 選擇第 t 個內容: [ \text{MMR}(c)=\lambda \cdot \text{Rel}(c) - (1-\lambda)\cdot \max_{s \in S_{t-1}} \text{Sim}(c,s) ] 建議預設:`λ = 0.7`(更偏相關性,仍保留多樣性) * `Rel(c)`:使用 `final_score(U,c)`(或 Soft Scoring 後的分數) * `Sim(c,s)`:相似度定義(無 embedding 也可落地) 相似度(離散特徵版,推薦 V1 上線): * 若 `content_id` 相同 → `Sim=1` * 若 `template_id` 相同且非空 → `Sim += 0.6` * 若 `author_id` 相同且非空 → `Sim += 0.3` * `need/context/stage` 標籤重合(Jaccard)→ `Sim += 0.1 * Jaccard(tags_c, tags_s)` * 最終 `Sim` clamp 到 `[0,1]` 字段依賴與回退: * 缺 `template_id/author_id` 時,只用 tags 相似度(Sim 仍可算) * 若後續引入文本 embedding,可將 embedding cosine 加權合併(V2) --- # 五、App Push(可設定 0–5 次)— Precision & Safety-first ## 5.1 目標 * Push 是「打擾」:錯一次就掉留存 * 追求「準、溫柔、少翻車」 ## 5.2 流程(每次 Push) 1. **候選池**(小而精) * 70%:need 命中(S_need=1)的內容 * 20%:emotion 命中(距離小)的內容 * 10%:通用安全句(general + 低風險) 2. Hard Filter(最嚴格) 3. Soft Scoring(偏 need + emotion) 4. 冷卻與頻控(必做) * 同一句/同作者/同模板至少 X 天不重複 5. 發送 > Push 追加規則(V1.1):若 `fallback_level_final > 0` 或 `conf_U` 偏低,需自動「降個性化」: > > * `personalization_power` 上限降為 `≤0.5`(L1)或 `0`(L2/L3) > * 若候選不足,優先回退到「通用安全句庫」,而不是擴大高個性化內容 > > V1.2(支持问卷跳过)补充: > > * 若 `profile_answered.need=false` 或 `U.need` 缺失:不得按“need 强命中”召回,候选池应提高通用安全句占比,并至少按 L1 降个性化 > * 若 `profile_answered.emotion=false` 或 `U.emotion_score` 缺失:Push 应偏向 `Cᵢ.emotion_score` 为 `general` 或 0.4–0.8 的稳定内容,并启用 `P_uncertainty` ## 5.3 Push 打分權重(建議) * `w_need=0.45, w_emotion=0.35, w_stage=0.15, w_context=0.05` > Push 更在乎「此刻需要」與「語氣安全」。context 權重降低。 ## 5.4 Push 風險懲罰(P_risk) * 若 `Cᵢ.personalization_power=1` 且 `S_need<1` 且 `S_context<1` → 額外扣分 * 若 content 涉及健康敏感(`soft_health_sensitive`)→ 扣分(Soft Penalty,不做禁推) * 不確定性懲罰:`P_uncertainty`(或併入 `P_risk`),用於在 `conf_U/conf_C` 偏低時自動降權高個性化內容 ## 5.5 0–5 次推送的排程策略 * 使用者設定次數 N: * 第 1 條:need 命中 + 安全 * 第 2 條:emotion 修復 * 第 3 條:self-worth / rest_balance(視 need 而定) * 第 4–5 條:通用句補位,避免疲勞 --- # 六、Widget(每日一句)— Consistency & Brand-first ## 6.1 目標 * Widget 是「代表你這個 App 的一句話」 * 需要:穩定、可持續、低風險、避免太私密 ## 6.2 流程(每日生成) 1. 候選池:以 **general + personalization_power≤0.5** 為主 2. Hard Filter(嚴格) 3. Soft Scoring:偏 stage 安全 + emotion 中性/平靜 4. 跨日多樣性:7 日內不重複作者/模板/同一句 > 若候選不足:同樣走回退梯度(優先增加 general 安全句;不提高個性化力度)。 ## 6.3 Widget 打分權重(建議) * `w_need=0.25, w_emotion=0.25, w_stage=0.30, w_context=0.20` > Widget 需要「身份語境安全」與「普適」,stage 權重更高。 ## 6.4 Widget 的情緒區間約束 * 建議限制 `Cᵢ.emotion_score` 在 **0.4–0.8**(tired→calm) * 避免太低(沉重) * 避免太高(過嗨) --- ## 七、場景差異總結(快速對照) | 場景 | 目標 | 候選池 | 風險容忍 | 權重重點 | 序列策略 | | ------ | ----- | --- | ---- | ------------------- | ------ | | Feed | 探索+共鳴 | 大 | 中 | context↑、diversity↑ | MMR 序列 | | Push | 準+安全 | 小 | 低 | need↑、emotion↑ | 頻控+冷卻 | | Widget | 穩+代表性 | 中小 | 低 | stage↑、emotion中性 | 跨日多樣性 | --- ## 八、(後段解釋預告)提及的算法/概念 * Candidate Generation(混合召回:命中 + 通用 + 探索) * Hard Filter(規則引擎) * Linear Weighted Scoring(加權線性模型) * MMR(Maximal Marginal Relevance,多樣性重排) * Time Decay(時間衰減) * Frequency Capping(頻控) * Cooldown Keys(去重鍵:句子/作者/模板) > 工程側去重鍵定義(V1.1): > > * `sentence_key = content_id`(若無 content_id,需生成 stable hash,但建議補 content_id) > * `author_key = author_id`(可空) > * `template_key = template_id`(可空) > > 冷卻/頻控同時支持三層 key,避免只靠單一 key 造成過度重複或過度稀釋。 --- ## 九、V1 可直接上線的預設參數 * Feed:Top1 命中 + MMR 序列 30 條 * Push:每次 Top1;冷卻 7 天不重複作者,14 天不重複同句 * Widget:emotion_score 限制 0.4–0.8;7 日不重複作者/模板 V1.1 補充(工程預設): * Feed:MMR `λ=0.7` * Push:啟用 `P_uncertainty`(或併入 `P_risk`);`fallback_level>=1` 時 `personalization_power` 上限降為 `≤0.5` * Health:`block_health_medical` 全场景 Hard Filter;`soft_health_sensitive` 走 Soft Penalty > 一句話: > **同一套 U×Cᵢ 打分,按場景調候選池、權重與重排約束。**