Files
2026-02-02 16:47:37 +08:00

9.4 KiB
Raw Permalink Blame History

Content Repository候选查询与数据访问层Tasks

对应计划:spec_kit/Personalized Reco/modules/content-repository/plan.md

本清单已对齐确认点:

  • text 按客户端 locale 输出(请求携带)
  • 不允许语言回退(缺少目标语言文本的内容直接过滤,不返回)
  • 需要做 MySQLdevDB 集成测试
  • 代码与其他推荐子模块放同一目录:server/app/features/personalized_reco/
  • fetch_contents_by_ids 返回顺序必须与输入 content_ids 一致

执行说明:

  • 本次已在 dev MySQL 环境跑通 pytest,且测试不做破坏性操作:
    • 不清表(不执行 DELETE/TRUNCATE
    • 每个用例使用事务并在结束时 rollback
    • 默认不执行 Alembic 迁移(如需自动迁移需显式设置 ALLOW_SCHEMA_MIGRATION=1

0. 任务标记规则

  • 用勾选框标记执行状态:
    • [ ] 未开始
    • [x] 已完成
  • 每个任务都要求可独立验收(有明确产出/可运行的检查方式)。

1. 文档对齐(先把口径写死,避免实现漂移)

  • 1.1 更新 modules/content-repository/spec.md,加入 locale 相关契约

    • 变更点
      • 在输入中新增 locale(例如 en/en-US/tc/zh-TW/zh-HK),声明由客户端请求携带并透传至 repository
      • 在输出字段 text 补充语言选择规则:
        • locale=en*:必须从 text_en 产出;若缺失则该内容不可返回(过滤)
        • locale=zh-TW|zh-HK:必须从 text_tc 产出;若缺失则该内容不可返回(过滤)
        • 说明:当前仅支持 EN/TC不做繁转简若未来新增 zh-CN 再单独设计转换策略
      • 明确:fetch_contents_by_ids 返回顺序与入参一致
    • 验收spec.md 中输入/输出与接口签名不再缺少 localetext 的来源与“不允许语言回退”规则清晰。
  • 1.2 更新 modules/content-repository/plan.md,补齐 locale 选文与“繁转简”技术方案

    • 变更点
      • 在“读取层规范化Normalization”新增 text 规范化章节:语言选择 + 简中转换
      • 说明:当前仅支持 EN/TC不做繁转简若未来新增 zh-CN 再单独设计转换策略
      • 明确策略:不允许语言回退;缺少目标语言文本的内容视为不可用,必须在候选/按 ID 获取时过滤掉
    • 验收plan.md 有明确依赖与落地策略(含依赖包/转换时机/兜底策略),不留二义性。

2. 目录与骨架(与推荐子模块同级)

  • 2.1 新建目录 server/app/features/personalized_reco/content_repository/

    • 包含
      • __init__.py
      • types.pyDTOContentProfile、locale 类型等)
      • interface.pyContentRepository Protocol/ABC
      • normalization.py解析与兜底suitability、risk_flags、power、text
      • sqlalchemy_repo.pySQLAlchemy 实现)
    • 验收:可被 app.features.personalized_reco.content_repository.* 正常 import。
  • 2.2 依赖补齐(如采用 OpenCC

    • 说明:当前仅支持 EN/TC此任务可跳过若未来新增 zh-CN 并需要繁转简,再引入 OpenCC。

3. 数据结构与接口(面向引擎注入)

  • 3.1 定义 ContentProfile DTO稳定字段契约

    • 字段:对齐 modules/content-repository/spec.md,并补齐 review_confidence 输出兜底为 0.7
    • 注意text 为最终对外输出文本(已按 locale 选择/转换)
    • 验收DTO 字段齐全;类型清晰;不暴露 ORM 模型。
  • 3.2 定义 ContentRepository 接口(含 locale

    • 建议签名(示例,最终以 spec 为准):
      • fetch_candidates(scene, user_profile, fallback_level, limit, locale, exclude_content_ids=None) -> List[ContentProfile]
      • fetch_contents_by_ids(content_ids, locale) -> List[ContentProfile]
    • 验收:推荐引擎可以仅依赖该接口,不依赖 SQLAlchemy/FastAPI Depends。

4. 规范化工具函数(可单测)

  • 4.1 suitability 解析与兜底

    • 规则
      • 缺失/NULL/解析失败 → 全 0.5(固定 key 集合)
      • 部分 key 缺失 → 对缺失 key 补 0.5
      • 非法值 → 兜底 0.5
    • 验收:单元测试覆盖缺失/部分缺失/非法值。
  • 4.2 risk_flags 映射、去重与排序

    • 规则:旧→新映射对齐 spec去重稳定排序例如字典序
    • 验收:单元测试断言输出不含旧命名且顺序稳定。
  • 4.3 personalization_power 映射

    • 规则0/5/10 -> 0.0/0.5/1.0;非法值 -> 0.0
    • 验收:单元测试覆盖正常/异常值。
  • 4.4 text 选择与简中转换

    • 输入text_entext_tclocale
    • 规则:按 1.1/1.2 写死的策略执行(当前仅支持 EN/TC不做繁转简
    • 验收:单元测试覆盖:
      • en 取英文
      • zh-TW 取繁中
      • 缺失目标语言文本时的行为:返回“不可用”(例如返回空字符串 + 上层过滤,或直接返回 None 由调用方过滤;实现中必须一致)

5. SQLAlchemy 实现(无 N+1、顺序可控

说明:当前 DB 模型为:

  • contentstext_en / text_tc / author_id / template_id
  • content_profilesJSON、power、stage、is_safe_pool、review_confidence
  • content_risk_flags关联表1:N
  • 5.1 实现 fetch_contents_by_ids(content_ids, locale)

    • 实现要点
      • 输入去重,但输出必须按原始输入顺序重排(并忽略不存在的 id 或明确行为:不存在则跳过)
      • 两段式查询避免行膨胀:
        1. contents JOIN content_profiles 批量取主体与画像
        2. content_risk_flags 批量取 flags再按 content_id 聚合
      • 组装 DTO 时执行 normalization含 text locale 规则)
    • 验收
      • 返回顺序与输入一致
      • 缺失目标语言文本的 content_id 不返回(跳过,不做语言回退)
      • 不产生按 id 循环查 flags 的查询(查询次数为常数级)
  • 5.2 实现 fetch_candidates(scene, user_profile, fallback_level, limit, locale, exclude_content_ids)

    • 实现要点
      • 计算 effective_fallback_level
        • 若画像 need/context/emotion 任一缺失,则 effective_fallback_level = max(fallback_level, 1)
      • DB 粗过滤对齐 plan
        • L1personalization_power <= 5
        • L2personalization_power = 0stage = general
        • L3is_safe_pool = truestage = generalpersonalization_power = 0(如需更严格可在此明确)
      • 排序V1content_profiles.updated_at DESCcontents.updated_at DESC(择一写死并记录)
      • 两段式候选:
        1. 先查候选 idLIMIT limit * multiplier
        2. 调用 fetch_contents_by_ids 补全字段
      • locale 文本存在性过滤:
        • locale=en*contents.text_en IS NOT NULL
        • locale=zh-*contents.text_tc IS NOT NULL
      • 输出顺序:
        • 返回顺序按候选 id 列表顺序(用于后续引擎打分/重排);最终截断至 limit
    • 验收
      • 在不同 effective_fallback_level 下能返回候选
      • 不返回缺少目标语言文本的内容(不做语言回退)
      • 查询次数为常数级(不随 limit 线性增长)

6. DB 集成测试dev MySQL

  • 6.1 建立测试目录与 pytest 配置

    • 目标:在 server/ 内新增 tests/(或 app/**/__tests__/,但建议统一为 server/tests/
    • 内容
      • server/tests/conftest.py:提供 AsyncEngine/AsyncSession、清库策略、query count 统计工具
      • 测试运行约定:通过 DATABASE_URL 指向 dev 测试库(建议单独库名,例如 mindfulness_dev_test
    • 验收pytest 可在 server/ 下运行并发现测试。
  • 6.2 测试库 schema 初始化(用 Alembic

    • 策略(二选一写死):
      • A测试启动时 alembic upgrade head(确保 schema 最新)
      • B在 CI/本地提前准备库,仅在测试中清表
    • 验收:测试运行前 schema 可用,且不会污染开发主库数据(推荐使用独立 test 库)。
  • 6.3 集成测试用例:fetch_contents_by_ids

    • 准备数据:插入最小内容 2~3 条(覆盖 text_en/text_tc 缺失组合、profiles、flags含旧 flag
    • 断言
      • 返回顺序与输入一致
      • en 不返回 text_en 缺失的内容(不回退 text_tc
      • risk_flags 映射后不含旧命名
      • review_confidence NULL → 0.7
    • 验收:测试稳定通过。
  • 6.4 集成测试用例:fetch_candidates

    • 准备数据:覆盖 personalization_power 0/5/10、is_safe_pool true/false、不同 stage
    • 断言
      • L1/L2/L3 粗过滤生效
      • exclude_content_ids 生效
      • 查询次数为常数级(用 before_cursor_execute 计数)
    • 验收:测试稳定通过。

7. 最终自检清单(合入前)

  • 7.1 文档一致性检查

    • spec.md / plan.md / 实现接口签名三者一致(尤其是 localetext 输出规则)
  • 7.2 性能检查(最小)

    • fetch_contents_by_ids / fetch_candidates 查询次数断言通过(无 N+1
  • 7.3 回归检查

    • 不影响现有 user_profile_scoring 模块与迁移脚本(仅新增模块与测试)