我要出门几周,请了个朋友来帮我照顾植物。就是那个曾经把仙人掌养死的朋友。仙人掌。那种你把它忘在角落里一个月都死不了的植物。所以她说"行,我帮你浇水"的时候,我一点都没放心。

问题不在于她不上心。问题在于我架子上三盆植物想要三件完全不同的事,但从外面看,它们都只是"植物"。同一天全浇一遍,蕨类还是渴着,多肉已经淹了。我需要一个东西,每天告诉她哪盆该做什么,这样她不用猜,我也不用隔着时区发消息。

打开网易新闻 查看精彩图片

于是我给植物请了个保镖。Plant Parent 读取每盆植物的状态,每盆只给一个明确答案,按谁先出问题排序:

  • 蕨类:今天浇水(已逾期 3 天)
  • 多肉:移到更亮的地方(浇水还能撑 13 天)
  • 虎皮兰:状态良好,还能撑 16 天

整个思路就这一条。扫一眼就知道该碰哪盆、该放过哪盆。虎皮兰显示绿色和蕨类显示红色一样有用,因为在别人手里杀死植物的,恰恰是对着错的那盆瞎操心。蕨类今天就需要她。那个仙人掌杀手可以心安理得地两周不管虎皮兰。

它得自己跑起来

因为我不在场,它必须每天早上自己运行,不需要我。这部分比看起来更重要,也是真正花掉大部分工程量的地方。

面向朋友的界面就是一个网页。每盆植物是一张卡片:状态颜色、唯一要做的动作、还有多久会出问题。最渴的排最前面,所以今天要浇水的蕨类就待在你不可能错过的地方,两周都没事的虎皮兰安安静静待在一边。

标记一盆已完成是一键的事,卡片会回应:植物精神起来,水位计填满,状态落回"一切正常"。它被设计得让人有满足感,这样健忘的照顾者才真的会去点。

还有一个命令行视图,把同样的分诊结果打成表格,那是我搭建时自己用的。

预测核心:一张小表格

植物养护是一张假装成感觉的表格。每盆植物是一行:品种、盆的大小、光照、距上次浇水天数、温度、湿度。你想预测的东西(该做什么、还有几天出问题)不过是另外两列。这个形状正是 TabPFN 擅长的。

TabPFN 是面向表格数据的基础模型。你把带标签的行和想要求解的行交给它,它一次前向传播就给出预测。没有训练循环,没有超参数搜索,没有要管理的模型文件。对一架子植物来说这算严重超配,而这正是重点:整个预测核心只有几行代码,而且下个月我加一盆龟背竹它依然成立。

我把它拆成一个分类器(下一步动作是什么)和一个回归器(还有几天出问题),两者指向同一张特征表。三盆植物返回三个不同答案,是因为它们的行不同,不是因为我写了三个 if 语句。

冷启动问题,老实处理

演示里没人提的一个坑:我的植物没有历史。我这辈子没记录过一次浇水。没有数据的模型无话可说。

所以种子数据是合成的,我不打算假装不是。我写下大家都知道的养护规则——虎皮兰和多肉能扛过漫长干旱,蕨类几天内就会闹脾气、边缘发褐,光照强和小盆让土干得更快,温暖干燥的空气缩短窗口期——然后据此生成了几百行。TabPFN 把这些当作示例来读。规则是诚实的那部分;TabPFN 的工作是把它们泛化到某个具体位置、具体数值的植物上,这比查表做得多。

这个设计让合成数据成了特性而不是谎言。每次记录一次浇水,就是一行可以追加的真实数据。合成规则是它起步时的先验,真实的架子会慢慢接管。应用从不把规则输出包装成别的东西。当它够不到真实模型时,它会在页面上用大白话说明,而不是瞎猜。

用 Temporal 让晨检活下来

我不在的时候,没人看着这东西。没有我盯着的终端,没有"哦,脚本第三天挂了而我没发现"。检查必须每天早上自己触发,并且扛住一台连续跑几周的笔记本对进程做的一切。这就是 Temporal 上场的地方。

每日流水线是三步:读取每盆植物当前状态、跑预测、发出那一条通知。每一步都是一个 Temporal activity,这意味着编排能在出错时存活下来。