新功能:个性化推荐算法
This commit is contained in:
271
spec_kit/Personalized Reco/modules/scoring/plan.md
Normal file
271
spec_kit/Personalized Reco/modules/scoring/plan.md
Normal file
@@ -0,0 +1,271 @@
|
||||
# Scoring(软打分与惩罚项)|Plan
|
||||
|
||||
> 对应规范:`spec_kit/Personalized Reco/modules/scoring/spec.md`
|
||||
>
|
||||
> 规则来源(必须严格对齐):
|
||||
>
|
||||
> - `设计说明文档/個性化推薦算法規則.md`(Soft Scoring 公式、V1.2 缺失字段口径、场景默认权重)
|
||||
> - `设计说明文档/句子文案打分規則.md`(risk_flags 语义与默认值口径)
|
||||
>
|
||||
> 模块边界参考:`spec_kit/Personalized Reco/overview.md`(由 `reco-engine` 串起候选→过滤→打分→重排)
|
||||
|
||||
---
|
||||
|
||||
## 1. 目标与交付物
|
||||
|
||||
### 1.1 目标
|
||||
|
||||
- 实现三种场景统一的软打分函数 `final_score(U, Cᵢ)`,并输出可观测的 `breakdown` 以便调参与回归测试。
|
||||
- 严格实现 V1.2 缺失字段的保守策略(`S_need/S_context/S_emotion` 的默认值)。
|
||||
- 实现 Push 默认启用的不确定性惩罚 `P_uncertainty`。
|
||||
- 实现 Widget 情绪区间(0.4~0.8)的**软降权**(不硬过滤,但能明显压低分数)。
|
||||
|
||||
### 1.2 交付物
|
||||
|
||||
- `modules/scoring/plan.md`:本技术计划(本文件)。
|
||||
- 代码实现(tasks 阶段落地)建议位置:
|
||||
- `server/app/features/personalized_reco/scoring/`
|
||||
- 包含:
|
||||
- 纯函数 `score_content(...) -> ScoreResult`
|
||||
- `ScoreConfig`(场景默认参数 + 可覆盖)
|
||||
- `ScoreBreakdown`(稳定字段集合,用于可观测)
|
||||
- 单元测试(tasks 阶段落地):
|
||||
- 缺失字段一致性
|
||||
- Push 不确定性惩罚生效
|
||||
- Widget 情绪软降权生效(区间外明显更低)
|
||||
|
||||
---
|
||||
|
||||
## 2. 模块职责边界(V1 约定)
|
||||
|
||||
### 2.1 本模块负责
|
||||
|
||||
- 计算并返回:
|
||||
- `S_need/S_context/S_stage/S_emotion`
|
||||
- `S_core`
|
||||
- `S_personal`
|
||||
- `P_uncertainty`(按开关控制,Push 默认启用)
|
||||
- `P_widget_emotion_out_of_range`(Widget 专用软降权项,归入 `P_risk` 或单独字段均可;V1 建议单独字段,便于打点)
|
||||
- 生成 `breakdown`,用于可观测与调参。
|
||||
|
||||
### 2.2 不在本模块实现(但接口预留/可注入)
|
||||
|
||||
为避免与 `rerank-freqcap` / `reco-engine` 的职责重叠,V1 约定以下项**由外部模块产出**并作为输入注入(若不提供,默认按 0 处理):
|
||||
|
||||
- `S_fresh`:新鲜度/时间衰减相关(可由引擎或重排阶段计算)
|
||||
- `P_fatigue`:疲劳惩罚(基于历史触达/浏览/频控)
|
||||
- `P_repeat`:重复惩罚(同句/同作者/同模板等)
|
||||
- `P_risk`:软风险惩罚(例如 `soft_health_sensitive` 等)
|
||||
|
||||
> 说明:硬过滤(Hard Filter)由引擎编排阶段执行,本模块只消费 `pass`(是否通过硬过滤)并在总分中乘上 \(\mathbb{I}[pass]\)。
|
||||
|
||||
---
|
||||
|
||||
## 3. 输入/输出与数据结构(V1)
|
||||
|
||||
### 3.1 输入(对齐 spec)
|
||||
|
||||
- `scene`: `feed | push | widget`
|
||||
- `user_profile`(U,允许字段缺失/跳过)
|
||||
- `content_profile`(Cᵢ)
|
||||
- `now`:预留,用于 freshness/时间衰减(V1 可不实现具体公式)
|
||||
- `config`:
|
||||
- `w_need/w_emotion/w_stage/w_context`
|
||||
- `alpha`:个性化加成系数
|
||||
- `beta`:不确定性惩罚系数
|
||||
- `enable_uncertainty_penalty`:是否启用 `P_uncertainty`(Push 默认 true)
|
||||
- `widget_emotion_soft_range`:Widget 情绪软区间(默认 `[0.4, 0.8]`)
|
||||
- `widget_emotion_penalty_gamma`:Widget 情绪软降权强度(V1 取“适中”默认 0.25)
|
||||
- `pass`: `boolean`(来自 Hard Filter 结果;默认 true)
|
||||
- `external_terms`(可选,来自其他模块注入):
|
||||
- `S_fresh`、`P_fatigue`、`P_repeat`、`P_risk`
|
||||
- 若未提供则按 0 处理
|
||||
|
||||
### 3.2 输出
|
||||
|
||||
- `final_score: float`
|
||||
- `breakdown`(建议始终返回,便于可观测与断言):
|
||||
- `S_need/S_context/S_stage/S_emotion`
|
||||
- `S_core/S_personal/S_fresh`
|
||||
- `P_fatigue/P_repeat/P_risk/P_uncertainty`
|
||||
- `P_widget_emotion_out_of_range`(可选但建议保留)
|
||||
- `missing_fields: string[]`(`need/context/emotion`)
|
||||
- `pass: boolean`
|
||||
- `scene: feed|push|widget`
|
||||
|
||||
---
|
||||
|
||||
## 4. 核心公式与实现细则(必须对齐)
|
||||
|
||||
### 4.1 总分结构(线性加权 + 惩罚)
|
||||
|
||||
\[
|
||||
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 约定:`S_fresh/P_fatigue/P_repeat/P_risk` 允许外部注入;若缺失则当作 0,以确保函数可用且输出结构稳定。
|
||||
|
||||
### 4.2 分解项:S_need / S_context(V1.2 缺失字段兜底)
|
||||
|
||||
- `S_need`:
|
||||
- 若 `U.need` 缺失(为空对象 `{}` 或不存在)→ `S_need = 0.5`
|
||||
- 否则 → `S_need = Cᵢ.need_suitability[U.need_key]`
|
||||
- `S_context`:
|
||||
- 若 `U.context` 缺失(为空对象 `{}` 或不存在)→ `S_context = 0.5`
|
||||
- 否则 → `S_context = Cᵢ.context_suitability[U.context_key]`
|
||||
|
||||
> 工程约定:`U.need/U.context` 在客户端为稀疏 one-hot(最多一个 key=1)。实现时需提供一个“取唯一 key”的工具函数:若出现多个 key=1,按第一个(字典序或插入序)取值并记录告警(V1 可先 debug 日志)。
|
||||
|
||||
### 4.3 分解项:S_emotion(general=0.8;否则 1-|u-c|)
|
||||
|
||||
- 若 `U.emotion_score` 缺失 → `S_emotion = 0.8`
|
||||
- 否则:
|
||||
- 若 `Cᵢ.emotion_score` 为 general(`None`)→ `S_emotion = 0.8`
|
||||
- 否则 → \(S_{emotion}=1-|U.emotion\_score - C_i.emotion\_score|\)
|
||||
|
||||
实现约束:
|
||||
|
||||
- 将 `S_emotion` clamp 到 `[0,1]`,避免异常值导致负分或溢出。
|
||||
|
||||
### 4.4 分解项:S_stage(对齐算法规则)
|
||||
|
||||
规则(按 `设计说明文档/個性化推薦算法規則.md`):
|
||||
|
||||
- 若 `Cᵢ.stage == "general"` → `S_stage = 1`
|
||||
- 若 `Cᵢ.stage` 命中用户阶段(例如用户 `expecting=1` 且内容 `stage="expecting"`)→ `S_stage = 1`
|
||||
- 若用户阶段为 `unknown` 且内容阶段为非 unknown → `S_stage = 0.7`
|
||||
- 其余 → `S_stage = 0`
|
||||
|
||||
### 4.5 个性化加成 S_personal(含降个性化约束)
|
||||
|
||||
\[
|
||||
S_{personal}=\alpha \cdot C_i.personalization\_power \cdot \max(S_{need}, S_{context})
|
||||
\]
|
||||
|
||||
降个性化约束(对齐 spec 与回退梯度):
|
||||
|
||||
- 若 `fallback_level>=1` 或字段缺失明显/低置信度:
|
||||
- L1:限制 `personalization_power ≤ 0.5`
|
||||
- L2/L3:限制 `personalization_power = 0`
|
||||
|
||||
> 工程实现:本模块只做“限制后的 effective_personalization_power”,由调用方传入 `fallback_level`(或直接传入已限制后的 `content_profile.personalization_power`)。V1 建议:在 `content-repository`/引擎侧先做候选池约束,本模块再做一次保护性 clamp(防御式编程)。
|
||||
|
||||
### 4.6 不确定性惩罚 P_uncertainty(Push 默认启用)
|
||||
|
||||
\[
|
||||
P_{uncertainty}=\beta \cdot (1-conf_U)\cdot(1-conf_{C_i})\cdot C_i.personalization\_power
|
||||
\]
|
||||
|
||||
其中:
|
||||
|
||||
- `conf_U = user_profile.profile_confidence`
|
||||
- `conf_{C_i} = content_profile.review_confidence`(缺省 0.7)
|
||||
|
||||
开关策略(V1):
|
||||
|
||||
- `scene=push`:默认启用
|
||||
- `scene=feed/widget`:默认关闭(可通过 config 打开)
|
||||
|
||||
### 4.7 Widget 情绪区间软降权(适中默认实现)
|
||||
|
||||
目标:当 `scene=widget` 且 `Cᵢ.emotion_score` 可计算(非 general)时,若超出 `[0.4, 0.8]` 不硬过滤,但应产生明显降权。
|
||||
|
||||
V1 选择一个“适中、可调参、可解释”的惩罚函数:
|
||||
|
||||
- 设区间为 `[lo, hi]`(默认 `0.4, 0.8`)
|
||||
- 距离:
|
||||
- 若 `e < lo`,`d = lo - e`
|
||||
- 若 `e > hi`,`d = e - hi`
|
||||
- 否则 `d = 0`
|
||||
- 惩罚:
|
||||
- \(P_{widget} = \gamma \cdot \frac{d}{(hi-lo)}\)
|
||||
- 默认 `γ = 0.25`(适中强度)
|
||||
- clamp 到 `[0, γ]`
|
||||
|
||||
落地方式:
|
||||
|
||||
- 将 `P_widget_emotion_out_of_range` 单独输出到 breakdown;
|
||||
- 在总分中计入:
|
||||
- `P_risk_effective = external.P_risk + P_widget_emotion_out_of_range`
|
||||
|
||||
> 解释:当 `emotion_score` 达到区间边界外最大距离约为 0.4(例如 0 或 1)时,惩罚接近 `γ`,足以在 Widget 场景把“过低/过高情绪”的句子压到更靠后,但不会一刀切。
|
||||
|
||||
---
|
||||
|
||||
## 5. 场景默认参数(V1 建议)
|
||||
|
||||
对齐 `spec.md` 与算法规则:
|
||||
|
||||
- Feed:`w_need=0.35, w_emotion=0.20, w_stage=0.15, w_context=0.30`
|
||||
- Push:`w_need=0.45, w_emotion=0.35, w_stage=0.15, w_context=0.05`,并默认 `enable_uncertainty_penalty=true`
|
||||
- Widget:`w_need=0.25, w_emotion=0.25, w_stage=0.30, w_context=0.20`,并启用 `widget_emotion_soft_range=[0.4,0.8]`
|
||||
|
||||
推荐默认:
|
||||
|
||||
- `alpha=0.15`(可调,V1 用于让个性化加成“次要但可见”)
|
||||
- `beta=0.30`(可调,V1 用于在低置信度时明显压低高个性化内容)
|
||||
- `widget_emotion_penalty_gamma=0.25`(适中软降权强度)
|
||||
|
||||
> 注:`alpha/beta/gamma` 为工程默认建议值,后续应通过回归测试与线上指标调参;本模块必须允许 config 覆盖。
|
||||
|
||||
---
|
||||
|
||||
## 6. V1:S_fresh / P_fatigue / P_repeat 的处理方案(能落地且可演进)
|
||||
|
||||
### 6.1 V1 解决方式
|
||||
|
||||
- 在 `ScoreBreakdown` 中**保留** `S_fresh/P_fatigue/P_repeat` 字段;
|
||||
- 本模块计算时:
|
||||
- 若上游未提供对应值,则默认按 0;
|
||||
- 若提供,则原样计入总分(本模块不解释其来源/计算方式)。
|
||||
|
||||
### 6.2 接口建议(为后续模块对接预留)
|
||||
|
||||
- `external_terms` 中携带:
|
||||
- `S_fresh`:例如时间衰减、跨日新鲜度(未来可由 `rerank-freqcap` 或 `reco-engine` 计算)
|
||||
- `P_fatigue/P_repeat`:由 `rerank-freqcap` 基于历史集合与冷却窗口计算
|
||||
|
||||
> 好处:V1 先保证“可排序 + 可解释 + 可插拔”;后续接入重排/频控时无需改动打分主干,只需注入外部项。
|
||||
|
||||
---
|
||||
|
||||
## 7. 测试计划(V1)
|
||||
|
||||
### 7.1 单元测试(纯函数)
|
||||
|
||||
- 缺失字段一致性:
|
||||
- `U.need` 缺失 → `S_need=0.5`
|
||||
- `U.context` 缺失 → `S_context=0.5`
|
||||
- `U.emotion_score` 缺失 → `S_emotion=0.8`
|
||||
- 不确定性惩罚(Push 默认启用):
|
||||
- `conf_U`/`conf_C` 低且 `personalization_power` 高 → `final_score` 明显降低
|
||||
- 关闭开关后 `P_uncertainty=0`
|
||||
- Widget 软降权:
|
||||
- `emotion_score=0.6`(区间内)→ `P_widget=0`
|
||||
- `emotion_score=0.0/1.0`(区间外)→ `P_widget` 接近 `gamma`,`final_score` 明显更低
|
||||
- `pass=false`:
|
||||
- `final_score` 必须为 0(或按实现约定为 0),且 breakdown 中保留分解项(便于排查)
|
||||
|
||||
### 7.2 断言建议
|
||||
|
||||
- 断言 `breakdown` 字段集合稳定(不会因缺省而缺字段)。
|
||||
- 断言所有分项均为有限数(非 NaN/Infinity),并在合理范围内(可对 `S_*` clamp 到 `[0,1]`)。
|
||||
|
||||
---
|
||||
|
||||
## 8. 风险与后续演进
|
||||
|
||||
### 8.1 已知风险
|
||||
|
||||
- `U.need/U.context` 若出现多个 key=1,会导致取值歧义;V1 需明确选择策略并记录告警,避免 silent bug。
|
||||
- Widget 软降权强度(`gamma`)对结果影响较大,需要配合回归测试与线上指标调参。
|
||||
|
||||
### 8.2 后续演进方向(V1.1+)
|
||||
|
||||
- 将 `P_risk` 细化为可配置的多项惩罚(例如 health sensitive、过度个性化翻车风险等),并在 breakdown 中拆分输出。
|
||||
- 引入 `S_fresh` 的时间衰减公式,并与 `rerank-freqcap` 的跨日多样性联动。
|
||||
|
||||
167
spec_kit/Personalized Reco/modules/scoring/tasks.md
Normal file
167
spec_kit/Personalized Reco/modules/scoring/tasks.md
Normal file
@@ -0,0 +1,167 @@
|
||||
# Scoring(软打分与惩罚项)|Tasks
|
||||
|
||||
> 对应计划:`spec_kit/Personalized Reco/modules/scoring/plan.md`
|
||||
>
|
||||
> 本清单执行原则:
|
||||
>
|
||||
> - `scoring` 只做**软打分**与本模块定义的惩罚项(`P_uncertainty`、Widget 情绪软降权)。
|
||||
> - Hard Filter / 频控重排 / 新鲜度等由其他模块产出,本模块通过 `pass` 与 `external_terms` 接收注入(缺省按 0)。
|
||||
|
||||
---
|
||||
|
||||
## 0. 任务标记规则
|
||||
|
||||
- 用勾选框标记执行状态:
|
||||
- `[ ]` 未开始
|
||||
- `[x]` 已完成
|
||||
- 每个任务都要求可独立验收(有明确产出/可运行的检查方式)。
|
||||
|
||||
---
|
||||
|
||||
## 1. 文档对齐(先把口径写死,避免实现漂移)
|
||||
|
||||
- [x] 1.1 校对 `modules/scoring/spec.md` 与 `modules/scoring/plan.md` 一致性
|
||||
- **检查点**:
|
||||
- 输入:`scene/user_profile/content_profile/now/config` 是否一致
|
||||
- 输出:`final_score` 与 `breakdown` 字段集合是否一致
|
||||
- 关键公式:`S_core/S_personal/P_uncertainty` 是否与 `设计说明文档/個性化推薦算法規則.md` 一致
|
||||
- **验收**:两份文档无冲突描述,且关键参数命名统一(例如 `enable_uncertainty_penalty`、`widget_emotion_soft_range`)。
|
||||
|
||||
- [x] 1.2 在 `modules/scoring/plan.md` 中“明确写死”V1 的职责边界(若后续有调整再更新)
|
||||
- **变更点**(如需微调措辞):
|
||||
- `S_fresh/P_fatigue/P_repeat/P_risk` 为外部注入项,缺省按 0
|
||||
- `pass` 来自 Hard Filter,本模块只消费并乘上 \(\mathbb{I}[pass]\)
|
||||
- **验收**:阅读 plan.md 时不会产生“谁负责计算哪一项”的歧义。
|
||||
|
||||
---
|
||||
|
||||
## 2. 目录与骨架(与推荐子模块同级)
|
||||
|
||||
- [x] 2.1 新建目录 `server/app/features/personalized_reco/scoring/`
|
||||
- **包含**:
|
||||
- `__init__.py`
|
||||
- `types.py`(`ScoreConfig/ScoreBreakdown/ScoreResult/ExternalTerms`)
|
||||
- `defaults.py`(场景默认参数)
|
||||
- `utils.py`(clamp、one-hot 取 key 等纯工具)
|
||||
- `score.py`(核心纯函数 `score_content`)
|
||||
- **验收**:可通过 `app.features.personalized_reco.scoring.*` 正常 import。
|
||||
|
||||
---
|
||||
|
||||
## 3. 类型与接口(稳定契约,便于引擎编排调用)
|
||||
|
||||
- [x] 3.1 定义 `ScoreConfig`(含场景默认值 + 可覆盖)
|
||||
- **字段**:
|
||||
- 权重:`w_need/w_emotion/w_stage/w_context`
|
||||
- 系数:`alpha/beta`
|
||||
- 开关:`enable_uncertainty_penalty`
|
||||
- Widget:`widget_emotion_soft_range`、`widget_emotion_penalty_gamma`
|
||||
- **验收**:类型完整;能从 scene 推导默认 config(或由调用方传入)。
|
||||
|
||||
- [x] 3.2 定义 `ExternalTerms`(注入项,V1 可选)
|
||||
- **字段**:`S_fresh/P_fatigue/P_repeat/P_risk`
|
||||
- **默认值策略**:缺省按 0
|
||||
- **验收**:score 函数即使没有 external_terms 也能工作且输出字段稳定。
|
||||
|
||||
- [x] 3.3 定义 `ScoreBreakdown`(用于可观测/调参)
|
||||
- **必须包含**:
|
||||
- `S_need/S_context/S_stage/S_emotion`
|
||||
- `S_core/S_personal/S_fresh`
|
||||
- `P_fatigue/P_repeat/P_risk/P_uncertainty`
|
||||
- `missing_fields`
|
||||
- `pass/scene`
|
||||
- `P_widget_emotion_out_of_range`(建议保留)
|
||||
- **验收**:字段集合固定;不会因缺省而缺字段;所有值为有限数(非 NaN/Infinity)。
|
||||
|
||||
---
|
||||
|
||||
## 4. 纯函数实现(严格对齐公式 + 防御式兜底)
|
||||
|
||||
- [x] 4.1 实现 one-hot 取唯一 key 的工具函数(need/context)
|
||||
- **规则**:
|
||||
- `{}` 或不存在 → 视为缺失
|
||||
- 只有一个 key=1 → 返回该 key
|
||||
- 多个 key=1 → 选择“第一个”(写死策略:字典序优先或插入序优先)并记录 debug 日志
|
||||
- **验收**:单测覆盖空对象/单 key/多 key 的行为,且行为确定。
|
||||
|
||||
- [x] 4.2 实现 `S_need`/`S_context`(V1.2 缺失兜底)
|
||||
- **规则**:
|
||||
- need 缺失 → `S_need=0.5`;否则取 `Cᵢ.need_suitability[key]`
|
||||
- context 缺失 → `S_context=0.5`;否则取 `Cᵢ.context_suitability[key]`
|
||||
- 若内容侧缺 key/值非法 → 兜底为 0.5(防御式)
|
||||
- **验收**:单测覆盖用户缺失与内容缺失两侧情况,且不抛异常。
|
||||
|
||||
- [x] 4.3 实现 `S_emotion`(general=0.8;否则 `1-|u-c|`)
|
||||
- **规则**:
|
||||
- `U.emotion_score` 缺失 → 0.8
|
||||
- `Cᵢ.emotion_score` 为 general(None)→ 0.8
|
||||
- 否则 `1-abs(u-c)` 并 clamp 到 `[0,1]`
|
||||
- **验收**:单测覆盖缺失/general/正常值/越界值。
|
||||
|
||||
- [x] 4.4 实现 `S_stage`(对齐算法规则口径)
|
||||
- **规则**:
|
||||
- `content.stage=general` → 1
|
||||
- 命中用户阶段 → 1
|
||||
- 用户 unknown 且内容非 unknown → 0.7
|
||||
- 其余 → 0
|
||||
- **验收**:单测覆盖 general/命中/unknown→非unknown/其余组合。
|
||||
|
||||
- [x] 4.5 实现 `S_core`(线性加权)
|
||||
- **规则**:`S_core=w_need*S_need + w_emotion*S_emotion + w_stage*S_stage + w_context*S_context`
|
||||
- **验收**:单测断言与手算一致;权重可配置。
|
||||
|
||||
- [x] 4.6 实现 `S_personal`(对齐公式)
|
||||
- **规则**:`S_personal=alpha * personalization_power * max(S_need, S_context)`
|
||||
- **防御**:`personalization_power` clamp 到 `[0,1]`
|
||||
- **验收**:单测覆盖 power=0/0.5/1,且随 `alpha` 单调变化。
|
||||
|
||||
- [x] 4.7 实现 `P_uncertainty`(Push 默认启用)
|
||||
- **规则**:`beta*(1-conf_U)*(1-conf_C)*personalization_power`
|
||||
- **默认值**:`conf_C` 缺失→0.7;`conf_U` 缺失→按 1.0(或 0.7,需在代码注释写死;V1 建议按 1.0 避免过惩罚)
|
||||
- **开关**:`enable_uncertainty_penalty` 控制;Push scene 默认 true
|
||||
- **验收**:单测覆盖低置信度与关闭开关两类情况。
|
||||
|
||||
- [x] 4.8 实现 Widget 情绪区间软降权(适中默认)
|
||||
- **规则**:
|
||||
- `scene=widget` 且 `content.emotion_score` 非 general:
|
||||
- 若超出 `[0.4,0.8]`:`P_widget = gamma * d/(hi-lo)` 并 clamp `[0,gamma]`
|
||||
- 区间内:`P_widget=0`
|
||||
- `P_widget` 计入总分(建议并入 `P_risk`),并在 breakdown 中单独暴露
|
||||
- **验收**:单测断言:
|
||||
- `emotion_score=0.6` → `P_widget=0`
|
||||
- `emotion_score=0.0/1.0` → `P_widget` 接近 `gamma`
|
||||
- 不硬过滤(仍返回分数,只是更低)
|
||||
|
||||
- [x] 4.9 实现 `score_content(...) -> ScoreResult`(总分与 breakdown)
|
||||
- **规则**:
|
||||
- `final_score = I[pass] * (S_core + S_personal + S_fresh - P_fatigue - P_repeat - P_risk - P_uncertainty)`
|
||||
- `S_fresh/P_fatigue/P_repeat/P_risk` 从 `external_terms` 读取,缺省 0
|
||||
- `pass=false` 时 `final_score=0`(breakdown 仍输出便于排查)
|
||||
- **验收**:单测覆盖 `pass=false` 与 external_terms 缺省两种情况。
|
||||
|
||||
---
|
||||
|
||||
## 5. 单元测试(pytest,纯函数为主)
|
||||
|
||||
- [x] 5.1 新建测试文件 `server/tests/test_scoring.py`
|
||||
- **包含用例**:
|
||||
- 缺失字段一致性(need/context/emotion)
|
||||
- Push 不确定性惩罚生效(低 conf 时分数更低)
|
||||
- Widget 软降权生效(区间外明显更低)
|
||||
- pass=false 行为(final_score=0)
|
||||
- **验收**:`pytest -q` 能跑通该文件。
|
||||
|
||||
- [x] 5.2 增加“输出稳定性”断言(breakdown 字段集合固定)
|
||||
- **验收**:任意输入(含缺失字段)都返回同一套 breakdown key。
|
||||
|
||||
---
|
||||
|
||||
## 6. 最终自检清单(合入前)
|
||||
|
||||
- [x] 6.1 文档一致性检查
|
||||
- **检查点**:`spec.md` / `plan.md` / 实现接口签名三者一致(尤其是 config 字段与默认值策略)。
|
||||
- **验收**:阅读任一文档都能找到对应实现位置与参数含义。
|
||||
|
||||
- [x] 6.2 回归检查(不影响其他模块)
|
||||
- **验收**:不修改现有 `content-repository` 与 `user_profile_scoring` 逻辑,仅新增 scoring 模块与测试。
|
||||
|
||||
Reference in New Issue
Block a user