103 lines
5.1 KiB
Markdown
103 lines
5.1 KiB
Markdown
# 子模块:Content Repository(候选查询与数据访问层)|Spec
|
||
|
||
## 1. 目标描述
|
||
|
||
提供推荐算法可注入的、与 ORM/SQL 解耦的数据访问接口:
|
||
|
||
- 按场景与用户画像拉取候选内容画像(Cᵢ)。
|
||
- 按 `content_id` 批量获取内容画像(去重/重排/补字段)。
|
||
- 对 risk_flags、suitability JSON 等“存储形态”做统一解析与兼容(对上提供稳定结构)。
|
||
|
||
> 规则口径:risk_flags 命名与语义严格按 `句子文案打分规则`;若历史数据存在旧 flag,需在读取层做一次映射(避免语义漂移)。
|
||
>
|
||
> 存储形态对齐 `modules/db-design/plan.md`:读取层需要将 `contents` + `content_profiles` + `content_risk_flags` 组装为上层稳定的 `ContentProfile` 结构。
|
||
|
||
---
|
||
|
||
## 2. 输入 / 输出定义
|
||
|
||
### 2.1 输入
|
||
|
||
- `scene`: `feed | push | widget`
|
||
- `user_profile`: 客户端问卷画像(V1.2;字段允许缺失)
|
||
- `fallback_level`: `0|1|2|3`
|
||
- `limit`: 候选条数上限(由引擎配置)
|
||
- (可选)排除集合:`exclude_content_ids`(用于 DB 层先排一部分,减少传输;类型为 `List[int]`,与 MySQL 自增 `content_id` 对齐)
|
||
- 数据库会话/连接(实现层使用 `AsyncSession` 注入)
|
||
|
||
### 2.2 输出
|
||
|
||
- `List[ContentProfile]`(稳定字段契约):
|
||
- `content_id`:`int`(MySQL 自增主键)
|
||
- `text`:文案文本
|
||
- `stage`:`general | expecting | parenting | unknown`
|
||
- `emotion_score`:`float | None`(约定:`None` 表示 general)
|
||
- `context_suitability`:`Dict[str, float]`(见 4.1 的 key 集合;值为 `0/0.5/1`)
|
||
- `need_suitability`:`Dict[str, float]`(见 4.1 的 key 集合;值为 `0/0.5/1`)
|
||
- `personalization_power`:`float`(对上稳定口径为 `0/0.5/1`;若 DB 存 `0/5/10`,读取层需映射)
|
||
- `risk_flags`:`List[str]`(已做旧→新映射、去重;命名只允许 `unsafe_for_* / block_* / soft_*`)
|
||
- 可选:`author_id`、`template_id`、`review_confidence`
|
||
|
||
### 2.3 数据存储形态(对齐 DB 设计)
|
||
|
||
> 对齐:`spec_kit/Personalized Reco/modules/db-design/plan.md`
|
||
|
||
- `contents`:提供 `content_id`、`text`、(可选)`author_id`、`template_id`
|
||
- `content_profiles`:提供 `stage`、`emotion_score`、`context_suitability_json`、`need_suitability_json`、`personalization_power`(推荐存 `0/5/10`)、(可选)`review_confidence`
|
||
- `content_risk_flags`:通过关联表提供风险标记集合(同一 content 下按 `uniq_content_flag(content_id, flag)` 去重)
|
||
|
||
---
|
||
|
||
## 3. 接口(建议)
|
||
|
||
推荐模块对该子模块只依赖抽象接口(Python Protocol/ABC 均可):
|
||
|
||
- `fetch_candidates(scene, user_profile, fallback_level, limit, exclude_content_ids=None) -> List[ContentProfile]`
|
||
- `fetch_contents_by_ids(content_ids: List[int]) -> List[ContentProfile]`
|
||
|
||
---
|
||
|
||
## 4. 关键规则与实现约束
|
||
|
||
### 4.1 字段缺失与默认值
|
||
|
||
- 若 `review_confidence` 缺失:输出时默认按 `0.7`(对齐 `句子文案打分规则` V1.2 约定)。
|
||
- `context_suitability/need_suitability` 若缺失:**读取层必须补齐为“全 0.5 的通用可推”结构**(稳定输出,避免上层分支判断)。
|
||
- `context_suitability` 必须包含 5 个 key:`family/work/relationship/friends/health`
|
||
- `need_suitability` 必须包含 5 个 key:`emotional_support/parenting_pressure/self_worth/anxiety_relief/rest_balance`
|
||
- 补齐时上述 key 的默认值均为 `0.5`
|
||
- `personalization_power`:若 DB 采用 `0/5/10` 存储,读取层必须映射为 `0.0/0.5/1.0` 对上输出。
|
||
|
||
### 4.2 risk_flags 兼容映射(若存在历史旧数据)
|
||
|
||
对齐 `句子文案打分规则` 的旧→新映射:
|
||
|
||
- `block_stage_unknown` → `unsafe_for_stage_unknown`
|
||
- `block_stage_parenting` → `unsafe_for_stage_parenting`
|
||
- `block_emotion_low` → `unsafe_for_emotion_low`
|
||
- `block_health_sensitive` → `block_health_medical`(读取层默认采用更保守的硬拦截映射;除非未来引入可判定的细分字段再放宽为 `soft_health_sensitive`)
|
||
|
||
输出约束:
|
||
|
||
- 输出的 flags 必须已去重,且不得包含任何旧命名。
|
||
|
||
### 4.3 性能约束
|
||
|
||
- 不得产生 N+1 查询:候选与字段必须一次或少量批量查询获取。
|
||
- `fetch_candidates` 必须支持 limit,并在 DB 层尽量过滤(减少应用层扫描)。
|
||
- `fetch_contents_by_ids` / `fetch_candidates` 推荐查询形态:
|
||
- `contents` JOIN `content_profiles`,再 LEFT JOIN `content_risk_flags`(或先批量取 profiles,再批量取 flags 并在应用层聚合),避免按 `content_id` 循环查 flags。
|
||
|
||
---
|
||
|
||
## 5. 验收标准(可验证)
|
||
|
||
- `fetch_contents_by_ids`:
|
||
- 输入任意 `content_id` 列表,返回包含完整字段的 `ContentProfile` 列表(无重复、可缺省字段按约定兜底)。
|
||
- `fetch_candidates`:
|
||
- 在不同 `scene` 与 `fallback_level` 下能返回候选(即便画像缺失也不报错)。
|
||
- risk_flags 映射正确:输出的 flag 名称集合只包含新命名(`unsafe_for_* / block_* / soft_*`)。
|
||
- 性能:
|
||
- 单次调用不出现按 content_id 循环查库的行为(可通过日志/测试断言查询次数)。
|
||
|