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