144 lines
6.1 KiB
Markdown
144 lines
6.1 KiB
Markdown
# User Profile Scoring(任务清单 · V1.2)
|
||
|
||
> 依据:`spec_kit/User Profile Scoring/plan.md`(V1.2)
|
||
>
|
||
> 规则目标:在客户端实现“问卷答案(可跳过)→ 用户画像(含 profile_answered/confidence)+ 硬规则输出”,供推荐/Push/Widget 复用。
|
||
|
||
## 任务状态说明
|
||
|
||
- `[ ]`:未开始
|
||
- `[~]`:进行中
|
||
- `[x]`:已完成
|
||
- `[-]`:已取消/不做
|
||
|
||
---
|
||
|
||
## A. 代码实现(客户端 TypeScript 模块)
|
||
|
||
### A1. 建立目录与导出入口
|
||
|
||
- [x] 在 `client/src/features/` 下新增目录 `userProfileScoring/`
|
||
- **验收**:目录存在,且后续文件均放入该目录
|
||
- [x] 新建 `client/src/features/userProfileScoring/index.ts`
|
||
- **内容**:对外导出主函数与类型(避免上层直接深路径 import)
|
||
- **验收**:上层可仅从 `.../userProfileScoring` 引入
|
||
|
||
### A2. 定义类型与枚举(V1.2)
|
||
|
||
- [x] 新建 `client/src/features/userProfileScoring/types.ts`
|
||
- **定义输入类型**:`QuestionnaireAnswersV1_2`
|
||
- 字段允许 `undefined`/`null`:`mom_stage/emotion/context/need`
|
||
- **定义输出类型**:`UserProfileV1_2`
|
||
- 固定 `profile_version: "v1.2"`、`profile_source: "questionnaire"`
|
||
- `profile_answered`:`{ stage; emotion; context; need }`
|
||
- `emotion_score: number | null`
|
||
- `context/need: Record<string, 1>`(稀疏 one-hot;跳过为 `{}`)
|
||
- **定义扩展输出类型**:`UserProfileV1_2_Extended`
|
||
- `rule_hits: string[]`
|
||
- `hard_rules: { forbidden_risk_flags: string[]; forbidden_content_predicates: ... }`
|
||
- **验收**:类型字段与 `spec_kit/User Profile Scoring/spec.md`(V1.2)一致
|
||
|
||
### A3. 实现答案归一化(非法值按跳过)
|
||
|
||
- [x] 新建 `client/src/features/userProfileScoring/scoring.ts`
|
||
- [x] 实现 `normalizeAnswers(raw): QuestionnaireAnswersV1_2`
|
||
- **规则**:
|
||
- 值不在枚举内 → 归一化为 `undefined`
|
||
- `null` 保留为 `null`(表示显式跳过/无值)
|
||
- **验收**:任意输入不抛错;输出只包含合法枚举/`undefined`/`null`
|
||
|
||
### A4. 计算 profile_answered(V1.2)
|
||
|
||
- [x] 实现 `computeProfileAnswered(normalized)`:
|
||
- `stage = (mom_stage !== undefined && mom_stage !== null)`
|
||
- `emotion/context/need` 同理
|
||
- **验收**:与 `spec.md` 6.1.1 一致;可区分“缺失/跳过”与“明确作答”
|
||
|
||
### A5. 计算时间衰减 conf_time
|
||
|
||
- [x] 实现 `computeTimeConfidence(generatedAt, now): number`
|
||
- **规则**(与 `spec.md` 6.2 一致):
|
||
- 0–7 天:1.0
|
||
- 7–30 天:线性衰减到 0.7
|
||
- 30 天以上:0.5
|
||
- **验收**:给定固定时间输入,输出符合分段规则(允许浮点误差)
|
||
|
||
### A6. 计算 profile_confidence(V1.2 完整度因子 + clamp)
|
||
|
||
- [x] 实现 `computeProfileConfidence(conf_time, profile_answered): number`
|
||
- **规则**(与 `设计说明文档/客戶端問卷打分規則.md` V1.2 一致):
|
||
- `answered_count = Σ I[profile_answered.* = true]`
|
||
- `completion = answered_count / 4`
|
||
- `completion_factor = 0.5 + 0.5 * completion`
|
||
- `conf = clamp(conf_time * completion_factor, 0.2, 1.0)`
|
||
- **验收**:
|
||
- 全部跳过且 `conf_time=1` → `conf=0.5`
|
||
- 全部作答且 `conf_time=1` → `conf=1.0`
|
||
- 极端情况下不会低于 0.2 / 高于 1.0
|
||
|
||
### A7. 生成四维度画像字段(含跳过策略)
|
||
|
||
- [x] 实现 `buildUserProfileFromQuestionnaire(answers, options)`
|
||
- **元信息**:
|
||
- `profile_version="v1.2"`
|
||
- `profile_source="questionnaire"`
|
||
- `profile_generated_at`:允许注入;默认 `new Date().toISOString()`
|
||
- **stage**:
|
||
- 若 `mom_stage` 缺失/为 `null`:按安全策略输出 `stage.unknown=1`
|
||
- 若为枚举:对应 one-hot
|
||
- **emotion_score**:
|
||
- 若跳过:`null`
|
||
- 否则按映射表输出数值
|
||
- **context/need**:
|
||
- 若跳过:输出 `{}`
|
||
- 否则输出稀疏 one-hot(例如 `{ work: 1 }`)
|
||
- **验收**:与 `spec.md` 4.2/5.x/6.x 一致
|
||
|
||
### A8. 硬规则与可观测输出(rule_hits / hard_rules)
|
||
|
||
- [x] 在 `buildUserProfileFromQuestionnaire` 内生成 `rule_hits` 与 `hard_rules`
|
||
- **risk_flags 映射规则**(与 `spec.md` 7 一致):
|
||
- `stage.unknown=1` → `unsafe_for_stage_unknown`
|
||
- `stage.parenting=1` → `unsafe_for_stage_parenting`
|
||
- `emotion_score !== null && emotion_score<=0.2` → `unsafe_for_emotion_low`
|
||
- **跨维度规则谓词**(与 `spec.md` 7、客户端规则 V1.2 一致):
|
||
- 若 `stage.unknown=1`:追加谓词
|
||
- `id`: 建议 `unknown_block_parenting_pressure_personalized`
|
||
- `when_user`: `{ stage_unknown: true }`
|
||
- `forbid_content`: `{ need: "parenting_pressure", personalization_power: 1 }`
|
||
- **验收**:
|
||
- 输出结构稳定
|
||
- 不依赖内容侧字段(本模块只输出“可执行条件”)
|
||
|
||
---
|
||
|
||
## B. 单元测试(只测纯函数)
|
||
|
||
> 若当前工程暂不引入测试框架,可先把测试用例写成“可执行脚本/最小断言”;但建议最终落地 `vitest` 或 `jest`。
|
||
|
||
### B1. 建立测试框架(可选但建议)
|
||
|
||
- [x] 若 `client/` 尚无测试:引入 `vitest`(或 `jest`)并添加 `test` 脚本
|
||
- **验收**:能在本机运行测试并输出结果
|
||
|
||
### B2. 覆盖 spec.md 的关键用例
|
||
|
||
- [x] 用例:完整作答映射正确(对应 `spec.md` 用例 A/B)
|
||
- [x] 用例:时间衰减(对应 `spec.md` 用例 C/D/E)
|
||
- [x] 用例:全部跳过 `{}`(对应 `spec.md` 用例 A2)
|
||
- 断言:`profile_answered` 全 false;`stage.unknown=1`;`emotion_score=null`;`context/need={}`;`profile_confidence=0.5`(当 conf_time=1)
|
||
- [x] 用例:硬规则命中
|
||
- `emotion_score<=0.2` → 含 `unsafe_for_emotion_low`
|
||
- `stage.parenting=1` → 含 `unsafe_for_stage_parenting`
|
||
- `stage.unknown=1` → 含 `unsafe_for_stage_unknown` 且带跨维度谓词
|
||
|
||
---
|
||
|
||
## C. 接入与文档回写(后续任务)
|
||
|
||
- [ ] Onboarding 提交问卷处接入本模块(调用 `buildUserProfileFromQuestionnaire`)
|
||
- **验收**:提交后能生成并持久化/上传画像对象(具体存储策略由上层决定)
|
||
- [ ] 在推荐/Push/Widget 侧消费 `hard_rules`
|
||
- **验收**:Hard Filter 在打分前执行;且能使用跨维度谓词表达 unknown+育儿压力强个性化禁推
|
||
|