下午两点,展示柜的温度悄悄爬过了线。Dede Mensah 要到五点才会再去看一眼。中间这三个小时,墙上那块夹着记录纸的写字板什么都不会说——它不会报警,不会记下超温从哪一刻开始,也不会告诉她峰值到了多少。
![]()
Dede 卖奶酪。店门口一台展示冷柜,后厨一台步入式冷库,每台旁边都拧着一块写字板。她一天写两次温度。她所遵循的英国食品标准局计划(Safer Food Better Business,零售版的 HACCP)要求她这么做:监控冷藏点、记录读数、一旦超出范围就采取行动。
问题出在两次记录之间。如果展示柜在下午两点开始偏暖,而她只在上午九点和下午五点各看一次,纸面上的结论永远是"一切正常"。这个空档不是编出来的,HACCP 的义务也不是编出来的——这正是值得动手解决的部分。
把传感器数据流变成两样东西
Baridi 做的事很具体:把冰箱传感器的读数流,变成 Dede 那块写字板给不了的两样东西——一条超温时间线,和一个不会说谎的告警。
名字来自斯瓦希里语。"Baridi"意思是冷,也是形容天气凉、饮料冰的日常用词,正好指向这个工具盯着的那件事:冷,还是不是冷的?
回放一天的读数,你会得到那条时间线。凡是温度持续超过5C达到至少10 分钟的时段,都会作为一次超温事件出现,带上开始时间、结束时间、持续时长和峰值。顶部一行汇总:"2 次超过 5C,共 43 分钟。"
告警这一侧,每一次独立的超温事件只写一条记录。哪怕跑到一半的进程被杀掉,它也会继续正确地写下去。
谁为这个付钱
一个人的食品生意,已经在用时间和风险付钱了。写字板是无偿劳动,却仍然留下一个检查员一眼能看见的窟窿。市面上的解法是按月计费的 HACCP 记录订阅服务,把你的读数存在别人的服务器上。
Baridi 是本地版本:读数留在店里,没有按条计费,超温的算法可以被检查。对一个利润很薄的奶酪柜台来说,本地部署加上每条读数零成本,决定了这件事是做还是不做。
演示站点跑在 baridi.tech,超温时间线对着真实的 TimescaleDB,走 HTTPS 提供。仪表盘在 baridi.tech/dashboard。
Dede 看到的那一屏是这样的:当天状态是 FAIL,两次超温各自画在 5C 这条线上,峰值温度和超时分钟数被单独标出。可以切换传感器和日期,页脚报告实时数据集大小和实测查询耗时。
她也可能看到想要的那一天:每个传感器都保持在 5C 或以下,状态是 PASS / 全部正常,而不是一片糊弄人的绿色。
同样的数据,原始 API 输出长这样:
- 第一次超温是门没关:32 分钟爬升,上午中段峰值9.404C
- 第二次是化霜循环:11 分钟,峰值7.11C
- 第三个事件是凌晨 05:00 一分钟的尖峰,冲到9.87C,它被故意放进数据里,却没有出现在时间线上——因为规则是持续十分钟,一分钟不算超温。
杀掉进程再重启的那次验证
对一个记录工具来说,这一条最要紧:一个崩溃之后不能信任的记录器,比纸还糟。告警活动被故意加了 25 秒延迟,好让它在超温已被检测到、告警还没写出的那个窗口里被杀掉,然后重启一个全新的 worker。
结果是告警迟到了,但没有丢,也没有重复触发。最终日志里,两条记录各自带着自己的键:fridge-01:2026-10-12:2026-10-12T10:04:00+00:00和fridge-01:2026-10-12:2026-10-12T17:04:00+00:00。这个验证也放在站上,以录制测试的形式呈现,而不是现场操作。
朴素扫行 vs 连续聚合
超温的计算有个笨办法:把每一行原始数据拉出来,在 Python 里拼接。也有 TimescaleDB 的办法:读一个连续聚合,在 SQL 里分组。
在干净的一天上,两者结果完全一致,笨办法还略快一点——1440 行实在不算什么。朴素扫行五次取最好成绩4.08 ms,连续聚合4.52 ms,两条路径对超温的判断一致。
但合规日志不是一天的事,是好几台冰箱好几个月的事。把数据加载到1,038,240条读数之后,同一条单日查询的执行计划讲的是另一个故事:朴素方案共享命中 5782 个缓冲页,把 5760 行原始数据交给 Python;重方案共享命中 59 个缓冲页,返回 4 行已经算完的结果。
页面读取少了大约98%,超温检测发生在引擎里,而不是 Python 循环里。墙钟时间是14.96 ms对19.63 ms,在那个数据量下是 1.31 倍的单日加速。往百万行历史里加一天新数据,只刷新那一天的桶,耗时1.27s,不需要全量重建。
生成时间线的那段 SQL 用time_bucket建了一个按分钟聚合的连续物化视图,超温事件则来自在这个视图上跑的 gaps-and-islands 查询:给每一分钟打上超阈值或未超阈值的标记,状态每变一次就开一个新的岛,只保留长度达到持续窗口的岛。
关键路径上没有 AI 模型
这是故意的。超温的判断是算术:温度超过阈值,持续超过一个窗口。模型给不出代码无法验证的东西,所以事实归代码管。作者留了一个可选的"瞬时波动 vs 真实超温"模型的接口,然后没有接上——因为持续窗口已经把那一分钟的尖峰挡掉了,而那个尖峰本来正是要交给模型去抓的。硬把模型塞进去,只是装饰。
线上站点从头到尾跑在开放组件上:FastAPI 应用放在 Caddy 后面自动处理 HTTPS,TimescaleDB 跑在容器里,超温查询写在 SQL 里。TimescaleDB 绑定在回环地址上,公开 API 做了限流。
读数应该留在店里
Dede 的读数是她自己的合规责任。那是检查员会索要的记录,也是顾客投诉时保护她的记录。这份记录不该待在别人的订阅服务后面、别人的服务器上,还按条计费。
Baridi 从头到尾用开放组件搭起来:容器里的 TimescaleDB、Temporal 的开放开发服务器、一套 Python 技术栈。读数从不离开这台机器,没有按条计的云费用,超温算法是一条她和检查员都能读懂的 SQL,而不是厂商的黑箱。
边界也写得很直白。Baridi 是记录和告警的辅助工具,不是经过认证的合规记录,界面上就这么写着。1.31 倍的数据库优势是真的,但在单日数据量下并不惊人;更有说服力的是缓冲页读取和真实数据量下的增量刷新。数据是合成的,由带种子的脚本生成,文章里也说明了这一点。那个瞬时波动模型是被推迟了,不是被藏起来了:持续窗口已经解决了问题,模型就留在外面。
一个闭源 API 也能做这套算术。但它没法把读数免费留在店里——而那才是整件事的重点。
特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。
Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.