Files
2026-02-02 11:22:35 +08:00

6.1 KiB
Raw Permalink Blame History

User Profile Scoring任务清单 · V1.2

依据:spec_kit/User Profile Scoring/plan.mdV1.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/nullmom_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.mdV1.2)一致

A3. 实现答案归一化(非法值按跳过)

  • 新建 client/src/features/userProfileScoring/scoring.ts
  • 实现 normalizeAnswers(raw): QuestionnaireAnswersV1_2
    • 规则
      • 值不在枚举内 → 归一化为 undefined
      • null 保留为 null(表示显式跳过/无值)
    • 验收:任意输入不抛错;输出只包含合法枚举/undefined/null

A4. 计算 profile_answeredV1.2

  • 实现 computeProfileAnswered(normalized)
    • stage = (mom_stage !== undefined && mom_stage !== null)
    • emotion/context/need 同理
    • 验收:与 spec.md 6.1.1 一致;可区分“缺失/跳过”与“明确作答”

A5. 计算时间衰减 conf_time

  • 实现 computeTimeConfidence(generatedAt, now): number
    • 规则(与 spec.md 6.2 一致):
      • 07 天1.0
      • 730 天:线性衰减到 0.7
      • 30 天以上0.5
    • 验收:给定固定时间输入,输出符合分段规则(允许浮点误差)

A6. 计算 profile_confidenceV1.2 完整度因子 + clamp

  • 实现 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=1conf=0.5
      • 全部作答且 conf_time=1conf=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.md 4.2/5.x/6.x 一致

A8. 硬规则与可观测输出rule_hits / hard_rules

  • buildUserProfileFromQuestionnaire 内生成 rule_hitshard_rules
    • risk_flags 映射规则(与 spec.md 7 一致):
      • stage.unknown=1unsafe_for_stage_unknown
      • stage.parenting=1unsafe_for_stage_parenting
      • emotion_score !== null && emotion_score<=0.2unsafe_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. 单元测试(只测纯函数)

若当前工程暂不引入测试框架,可先把测试用例写成“可执行脚本/最小断言”;但建议最终落地 vitestjest

B1. 建立测试框架(可选但建议)

  • client/ 尚无测试:引入 vitest(或 jest)并添加 test 脚本
    • 验收:能在本机运行测试并输出结果

B2. 覆盖 spec.md 的关键用例

  • 用例:完整作答映射正确(对应 spec.md 用例 A/B
  • 用例:时间衰减(对应 spec.md 用例 C/D/E
  • 用例:全部跳过 {}(对应 spec.md 用例 A2
    • 断言:profile_answered 全 falsestage.unknown=1emotion_score=nullcontext/need={}profile_confidence=0.5(当 conf_time=1
  • 用例:硬规则命中
    • 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+育儿压力强个性化禁推