# Observability（可观测性与打点载荷）｜Tasks

> 对应计划：`spec_kit/Personalized Reco/modules/observability/plan.md`
>
> 本清单执行原则：
>
> - Observability 只负责**统一 meta 结构与构建**，不负责埋点 SDK/落库/上报实现。
> - `RecoMeta` 必须可被 `reco-engine` 与 `integration-api-worker` 共同使用（同一结构、同一口径）。

---

## 0. 任务标记规则

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

---

## 1. 文档对齐（先把口径写死，避免实现漂移）

- [x] 1.1 校对 `modules/observability/spec.md` 与 `modules/observability/plan.md` 一致性
  - **检查点**：
    - `RecoMeta` 必须字段集合一致（scene、candidate_pool_size_*、fallback_level_final、served_k、empty_reason、conf_U、missing_fields）
    - `empty_reason` 枚举与判定逻辑一致
  - **验收**：两份文档无冲突；V1 的默认值/缺失策略写清楚。

---

## 2. 目录与骨架（与推荐子模块同级）

- [x] 2.1 新建目录 `server/app/features/personalized_reco/observability/`
  - **包含**：
    - `__init__.py`
    - `types.py`（`RecoMeta`、`MissingFields` 等 Pydantic 模型）
    - `utils.py`（`compute_empty_reason` 等纯函数）
    - `builder.py`（`RecoMetaBuilder`：逐阶段填充并 build）
  - **验收**：可通过 `app.features.personalized_reco.observability.*` 正常 import。

---

## 3. 类型定义（稳定契约）

- [x] 3.1 定义 `MissingFields`（布尔结构）
  - **字段**：`need/context/emotion`
  - **验收**：字段名与 `spec.md` 一致；序列化输出稳定。

- [x] 3.2 定义 `RecoMeta`（统一 meta 载荷）
  - **必须字段**：
    - `scene`
    - `candidate_pool_size_raw`
    - `candidate_pool_size_after_hard_filter`
    - `candidate_pool_size_after_dedup`
    - `candidate_pool_size_after_freqcap`
    - `fallback_level_final`
    - `served_k`
    - `empty_reason`（served_k=0 必填；served_k>0 可为 None）
    - `conf_U`
    - `missing_fields`（`MissingFields`）
  - **可选字段**：
    - `risk_filtered_count_by_flag`
    - `freqcap_filtered_counts`
    - `config_snapshot`（V1 可先不实现，仅预留字段）
  - **验收**：字段集合固定；可被 API/Celery 直接返回。

---

## 4. 纯函数与判定逻辑（V1 写死）

- [x] 4.1 实现 `compute_missing_fields(user_profile) -> MissingFields`
  - **规则**：
    - need：`user_profile.need` 为空对象 `{}` 或不存在
    - context：`user_profile.context` 为空对象 `{}` 或不存在
    - emotion：`user_profile.emotion_score` 为 `null`/不存在
  - **验收**：单测覆盖三种缺失情况与全不缺失情况。

- [x] 4.2 实现 `compute_empty_reason(...) -> str | None`
  - **规则**（对齐 plan）：
    - served_k>0 → None
    - raw==0 → `pool_empty`
    - raw>0 且 after_hard_filter==0 → `hard_filter_all`
    - after_freqcap==0 → `freqcap_all`
    - 其他 → `unknown`
  - **验收**：单测覆盖所有分支。

---

## 5. Builder（在 pipeline 中逐阶段填充）

- [x] 5.1 实现 `RecoMetaBuilder`（最小可用）
  - **能力**：
    - 初始化：scene/user_profile/k/now
    - set：raw/after_hard_filter/after_dedup/after_freqcap/fallback_level_final/served_k
    - 可选 set：risk_filtered_count_by_flag/freqcap_filtered_counts
    - build：补齐 conf_U、missing_fields、empty_reason
  - **验收**：
    - 任意顺序调用 set 不抛异常（V1 可约定必须先 set raw，再 set after_*；但 builder 需给出默认值）
    - build 输出满足非负与单调性（若出现违背，做防御式 clamp 或记录 debug 并以最保守值输出）

---

## 6. 单元测试（pytest）

- [x] 6.1 新建测试文件 `server/tests/test_observability.py`
  - **用例覆盖**：
    - empty_reason 判定所有分支
    - missing_fields 判定
    - builder build 输出字段集合稳定
    - 单调性约束：输入异常时 builder 的防御策略生效（不输出负数）
  - **验收**：`pytest -q tests/test_observability.py` 通过。

---

## 7. 最终自检清单（合入前）

- [x] 7.1 文档一致性检查
  - **验收**：`spec.md` / `plan.md` / `RecoMeta` 类型字段一致。

- [x] 7.2 全量测试通过
  - **命令**（在 `server/`）：
    - `pytest -q`
  - **验收**：所有用例通过。

- [x] 7.3 全部完成后更新大规范 `overview.md`
  - **变更点**：
    - 将 `modules/observability/` 标记为“已实施”
    - 增加一条变更记录（日期 + 交付物：plan/tasks/代码/测试）
  - **验收**：`spec_kit/Personalized Reco/overview.md` 中模块状态与交付记录准确。

