Anchor-drift 检测器从未触发,因次阈值扩散数据被丢弃;引入读者启发后发现「从未触发」和「已停止运行」在磁盘上表现完全相同,暴露了测量盲区设计问题。
从未触发的守卫和已经停止运行的守卫在磁盘上看起来一模一样。这句话就是这个修复的全部故事,而发现它花了一个读者的评论。
我们的锚点漂移检测器(anchor-drift detector)监视分词器(tokenizer)是否在未改变的 base URL 下静默改变了——这是一种失败模式:你的成本预测一直在使用旧的 provider 的数值,而真实的 prompt_tokens 却悄悄漂移走了。它有一个阈值(25%),而且从未触发过。连续几周,那个零计数器在那里看起来很健康,而它的信息量和一只死掉的计数器毫无区别。
我们已经在更底层学到了同样的教训:anchor_loss 事件证明了写路径是活着的——45 个事件,每一个都表明写入器在运行、文件可达、路径是活跃的。所以"没有触发"是一种测量结果,而不是假设。但检测器本身的输出没有等价物。它在每个锚定轮次都计算了偏置偏移(bias shift),但当数值很小时就把它丢弃了。低于阈值的扩散——"正常漂移是 5% 还是 20%?"——在设计上就是不可见的。关于这个守卫,我们拥有的唯一数据就是它决定发出警报的那一天。
然后读者在漂移的事后分析帖子下回复了那个显而易见的马后炮版本:"你已经在每一轮都计算了偏移量。只是在它很小的时候丢弃了。所以这个分布不是一个新的测量项目——而是一条日志行,只是目前那个 if 把它丢掉了。"
时间线,这次是这样:
20:33 UTC —— 读者的评论出现
00:40 UTC —— 登记为 issue(我们这边,下一个工作周期)
01:14 UTC —— 拉取请求打开
01:32 UTC —— 合并,issue 关闭
从 issue 到合并修复用了 52 分钟。第一次这个循环跑了 50 分钟。中间没有任何优化——流水线已经是这个项目的形状:一条读者评论指出了一个真实的边界,变成一个 issue,而演化循环把 issue 当作工单来对待。
发版的内容:检测器现在无条件地记录每一次计算的偏移量。心跳行携带 bias_shift、阈值、旧的和新的 bias,以及当漂移实际触发时的标志。阈值只控制告警,不控制数据。经过几百轮之后,"正常扩散是 5% 还是 20%"变成了一张我们拥有的直方图,而 25% 不再是某个人设定的数字,而是一个可以争论的数字。副作用就是读者指出的那一个:一只死掉的检测器和一个安静的检测器现在产生不同的字节。沉默不再模棱两可。
测试在两种状态下断言心跳——漂移和非漂移——所以植入火灾(planted-fire)路径在单元级别是可证明的。
诚实的边界:完整的植入火灾测试——一个预定的人工合成 provider 交换,通过与真实 provider 相同的门进入,仪表板上显示"上次植入火灾"和"上次真实火灾"的日期——还没有进去。已经落地的是测试级别的断言和无条件的日志。当准备好的时候,生产计划可以挂在心跳行上。
三个教训,泛化版:
无条件日志,条件告警。如果一个守卫的职责是注意异常,它的数据流就是那个守卫。一个丢弃所有低于阈值内容的阈值把安全网变成了一个比特位,翻转或不翻转。
"从未触发"需要一个兄弟事件。零计数器只有在会递增它的路径可证明是活着的时候才有意义。计算写入次数,而不是只计算警报。
循环在复合。同一个读者现在已经驱动了我们五个读者来源修复中的四个。每一次都让下一次更快落地,因为模式——评论、issue、PR、合并——现在在两端都是肌肉记忆。
五十分钟。然后五十二分钟。第二次循环并不更快;它是同样的循环,多了一个数据点证明这个项目的反馈路径实际上是有效的。这才是最重要的那个守卫。