0、null、未采集:AI Agent 反馈系统最容易踩的语义坑

42 阅读3分钟

0、null、未采集:AI Agent 反馈系统最容易踩的语义坑

AI Agent 指标别把 null 当 0

做 Agent 反馈系统时,一个诱惑是把所有缺失指标补成 0:SQL 好聚合,图表不留空,前端也少写判断。但这个“方便”会同时破坏基线、增量和优化结论。

工程上应保留三个状态:真实零值、字段未知、没有采集证据。它们看起来都“没有数”,业务含义却完全不同。

工程场景与决策冲突

数据团队希望 Schema 稳定,产品团队希望仪表盘完整,采集端却面对平台字段缺失、登录状态和解析漂移。正确取舍不是强行补数,而是让数据模型显式承载不确定性。

方案拆解与关键权衡

指标三态数据模型

状态含义能否参与增量
0已采集,真实值为零可以
null已采集,但字段不可得或不可靠不可以
未采集没有本次观察证据不存在该快照

把 null 保留下来会增加查询判断,却换来一个重要保证:系统不会把证据缺失解释成表现差。

实现链路与最小示例

快照模型

@dataclass(frozen=True)
class Snapshot:
    article_id: str
    revision: int
    captured_at: datetime
    views: int | None
    likes: int | None
    collects: int | None
    comments: int | None

采集运行结果单独记录 completed/failed/skipped。只有 completed 才生成快照;失败不能生成一条全零数据冒充成功。

基线和增量

从基线到可信增量

第一次快照只建立 baseline。后续比较时,空值必须传播:

def compare(now, before):
    if now is None or before is None:
        return None
    if now < before:
        raise MetricRecalculated
    return now - before

不要用 int(value or 0),因为它同时吞掉合法的 0 和缺失的 None。

报告层

报告必须输出不同句子:

  • views_delta == 0:当前窗口没有新增阅读;
  • views_delta is None:当前窗口没有可比较的阅读增量;
  • 没有第二个快照:首次采集,只建立基线。

同样,所有互动字段都是 None 时,只能说“互动不可比较”,不能说“没有互动”。

证据、限制与自动化边界

真实实现还需要三道绑定:公开页标题与清单一致、同一内容 ID 不改绑其他文章、revision 不回退。否则即使数值类型正确,也可能把别人的数据接到当前序列。

平台不提供展现时,应保留 impressions=None,不能用阅读量代替。累计指标出现下降时,先记录异常并停止解释,避免把平台重算当成负增长。

RuyiBookCourse 对生产 Agent KPI 的要求是先定义多维指标并建立基线;项目测试则证明了更细的工程语义:首次快照不计算增量、收藏未知时增量保持空、全部互动未知时报告不得声称获得互动。

可复用检查清单

  • 数字字段允许 None,但禁止负数;
  • 采集运行状态与指标快照分开;
  • 失败或跳过不生成全零快照;
  • 首次快照只建基线;
  • 空值逐字段传播到增量;
  • 累计指标下降触发异常;
  • 文章 ID、标题、内容 ID、revision 绑定;
  • 不用阅读冒充展现;
  • 文案区分“真实为零”和“无法比较”。

收束

一个可信的反馈系统,不是每个单元格都有数字,而是每个数字都能说明它来自哪次真实观察。让 null 保持 null,是数据工程对决策最基本的诚实。

发布前门禁

  • 工程冲突和取舍明确
  • 示例与真实测试一致
  • 没有虚构平台指标
  • 本轮未认领实验