7.1 KiB
7.1 KiB
Observability(可观测性与打点载荷)|Plan
对应规范:
spec_kit/Personalized Reco/modules/observability/spec.md规则来源(必须对齐):
设计说明文档/個性化推薦算法規則.md(candidate_pool_size_*、fallback_level_final、empty_reason 等必打点字段)spec_kit/Personalized Reco/overview.md(模块边界:本模块被reco-engine与integration-api-worker共同使用)
1. 目标与交付物
1.1 目标
- 定义推荐模块统一的
RecoMeta(meta/event 载荷),随推荐结果一起返回,由调用方负责上报/落库/打点。 - 确保每次推荐调用都能产出可用于监控与排障的关键字段:
- 覆盖率、回退率、候选规模分布、过滤原因分布
served_k=0时必须给出准确的empty_reason(定位清空阶段)
- 与现有子模块接口对齐:
- 候选生成(content-repository)
- Hard Filter(引擎阶段)
- Soft Scoring(scoring)
- Rerank/Freqcap(rerank-freqcap)
1.2 交付物
modules/observability/plan.md:本技术计划(本文件)。- 代码实现(后续 tasks 阶段落地)建议位置:
server/app/features/personalized_reco/observability/- 包含:
types.py:RecoMeta(Pydantic BaseModel)builder.py:RecoMetaBuilder(在 pipeline 中逐步填充)utils.py:empty_reason判定工具函数
- 单元测试(后续 tasks 阶段落地):
served_k=0时 empty_reason 必填且阶段一致- 计数口径一致性(after_* 的单调性与非负)
2. 模块职责边界(V1 约定)
2.1 本模块负责
- 提供统一的数据结构
RecoMeta(返回给调用方)与构建方式(builder)。 - 在推荐 pipeline 中收集各阶段统计,避免“散落日志/散落字段”:
- 候选生成规模(raw)
- Hard Filter 后规模
- Dedup 后规模
- Freqcap 后规模
- 回退层级与触发原因(由引擎提供)
- served_k 与 empty_reason
conf_U与missing_fields- (可选)risk flag 命中统计
2.2 不在本模块实现
- 不负责真正的上报实现(埋点 SDK / 日志落库 / 指标上报)。
- 不负责决定回退策略与过滤规则,只负责“把发生了什么”记录成统一载荷。
3. RecoMeta 字段定义(V1)
以
modules/observability/spec.md为准,本 plan 补充“口径/生成时机/默认值”。
3.1 必须字段(每次推荐都要产出)
scene: "feed" | "push" | "widget"candidate_pool_size_raw: intcandidate_pool_size_after_hard_filter: intcandidate_pool_size_after_dedup: intcandidate_pool_size_after_freqcap: intfallback_level_final: intserved_k: intempty_reason: str | Noneserved_k=0时必填served_k>0时可为None(或输出"unknown",但建议为 None 更干净)
conf_U: floatmissing_fields: { need: boolean; context: boolean; emotion: boolean }
3.2 可选字段(建议支持,便于排障/调参)
risk_filtered_count_by_flag: { flag: count }- 其他调参辅助字段(V1 可先不返回给客户端,仅在内部日志/事件中使用):
config_snapshot(权重、alpha/beta、mmr_lambda、cooldown 等)fallback_trigger_reason(例如 pool_empty/hard_filter_all/freqcap_all)
4. 口径与生成时机(V1 必须写死)
4.1 数量统计口径(强约束)
- 计数必须满足:
- 全部为非负整数
- 单调不增:
raw >= after_hard_filter >= after_dedup >= after_freqcap >= served_k
- 每个阶段计数的来源:
candidate_pool_size_raw:候选生成阶段拿到的候选数(从content-repository返回的候选列表长度)candidate_pool_size_after_hard_filter:Hard Filter 过滤后的候选数candidate_pool_size_after_dedup:去重(基于历史集合)后的候选数candidate_pool_size_after_freqcap:频控/冷却后的候选数(包含作者/模板维度若执行)served_k:最终输出 items 的长度(≤ k)
4.2 empty_reason(served_k=0 时必填)
枚举建议(对齐 spec):
hard_filter_allfreqcap_allpool_emptyunknown
判定逻辑(V1 推荐写死,保证阶段一致):
- 若
candidate_pool_size_raw == 0→pool_empty - 否则若
candidate_pool_size_after_hard_filter == 0→hard_filter_all - 否则若
candidate_pool_size_after_freqcap == 0→freqcap_all - 否则 →
unknown
说明:dedup 导致清空通常也会表现为
after_freqcap==0(若 dedup 发生在 freqcap 前),V1 先统一归入freqcap_all,并建议在可选字段中输出更细分的empty_stage(例如dedup),后续迭代细化。
4.3 conf_U 与 missing_fields 口径
conf_U:直接取user_profile.profile_confidencemissing_fields:need:user_profile.need为空对象{}或不存在context:user_profile.context为空对象{}或不存在emotion:user_profile.emotion_score为null/不存在
5. 工程落地方式(V1)
5.1 Builder 模式(避免散落)
推荐在 reco-engine 内使用 RecoMetaBuilder:
- 初始化:
builder = RecoMetaBuilder(scene, user_profile, k, now) - 各阶段更新:
builder.set_candidate_pool_size_raw(n)builder.set_after_hard_filter(n, risk_filtered_count_by_flag=...)builder.set_after_dedup(n)builder.set_after_freqcap(n, freqcap_filtered_counts=...)builder.set_fallback_level_final(level, reason=...)builder.set_served_k(len(items))
- 最终:
meta = builder.build()(内部负责 empty_reason 判定与默认值填充)
5.2 API/Celery 的返回策略(边界清晰)
integration-api-worker对外返回:itemsmeta(RecoMeta)
- 是否对客户端透出所有 meta 字段:
- V1 建议:对客户端返回最小必要字段;但服务端事件中保留完整 meta(含可选字段)
- 具体裁剪由 API 层决定,Observability 模块只负责提供完整结构
6. 测试计划(V1)
6.1 单元测试(pure)
empty_reason判定:- raw=0 → pool_empty
- raw>0 且 after_hard_filter=0 → hard_filter_all
- after_freqcap=0 → freqcap_all
- 单调性断言(若输入不满足单调性,builder 应做防御式 clamp 或记录告警,V1 可选择“以最后写入为准”并在测试中覆盖)
6.2 最小集成验证(与 reco-engine 串联)
- 构造一次推荐调用:
- items 长度与
served_k一致 candidate_pool_size_*与实际阶段产物一致served_k=0时empty_reason与清空阶段一致
- items 长度与
7. 风险与后续演进
7.1 已知风险
- V1 可能缺少细分 empty_stage(例如 dedup_all 与 freqcap_all 的区分),导致排障粒度不足。
7.2 演进方向
- 增加
empty_stage: "candidate" | "hard_filter" | "dedup" | "freqcap" | "unknown",保持与empty_reason并存。 - 增加
freqcap_filtered_counts、risk_filtered_count_by_flag的统一结构与上报策略,便于报表按维度聚合。