探索

形状一样才叠得起来 — 经验增殖的条件

作者: makemind · 2026年8月5日

2026 年 5 月我们刊出了《经验不是保存,而是增殖》。写得好的笔记三十年后仍能告诉你那天的判断,但也就到此为止;而以同一形状留下的经验不会停下,它会不断增加。

那篇文章的脊梁是三层。事实堆起来成为技术,技术堆起来成为预测。而让这三层成立的条件只有一个 —同一形状

形状一样就能叠着看。 而叠上去的那一刻,一个人一辈子看不到的东西就看见了。

我们现在仍然认为这话是对的。问题是那篇文章一次也没有让你看见「同一形状」到底长什么样。它只写了「同样的字段、同样的单位、同样的格式」。

这次我们写出那是几个字段,以及它扛得住什么。

同一形状是六个字段

这是温室控制样例里节点声明自己的格式。

printf("%s{\"id\":\"%s\",\"role\":\"%s\",\"kind\":\"%s\","
       "\"model\":\"%s\",\"unit\":\"%s\",\"value\":%.1f}",
       i ? "," : "", n->id, n->role, n->kind, n->model,
       n->unit, n->value);

六个字段。 id · role · kind · model · unit · value

原文举例的农事笔记有六栏:日期/作物/移栽日/第一朵花/当日气温/结果。数目碰巧一样挺有意思,但重要的不是数目,而是什么成了字段。

unit 作为一个字段存在,是这个格式的核心。温度计若以华氏测量,它就说华氏。它不会为了别人方便而预先把值换算好。 硬件只诚实地说自己知道的,而对齐的活儿在上面做。

而对齐的活儿正好只有一处。

/// 换算成规则所用单位后的值。
///
/// 这个样例里唯一知道单位的地方就是这里,而且它是看节点声明的 unit
/// 来判断,不是看型号。哪怕是谁也没听说过的传感器,只要它说出自己的
/// 单位,就会准确地落到这里。
double get canonicalValue {
  switch (unit) {
    case 'F':
      return (value - 32) * 5 / 9;
    default:
      return value;
  }
}

这就是「能叠着看」的实际实现。 让说着不同单位的不同产品站到同一个位置上的地方。原文只把这写成「放进同样的字段」,而真做出来才发现,决定字段,和把「往字段搬」这件事收拢到一处,是两件不同的活儿。

如果按型号分支,那一处就会变成一份清单。每买一款新产品,清单就长一行。看 unit 的话,第一次见的产品也会自动对上。同一形状不只是格式,也意味着解释这个格式的地方只有一个。

叠上去之后真的扛住了

原文说叠着看就能看见原本看不见的东西。在这个样例里,「叠」扛住了什么,日志里有。

我们用同一套规则跑了两个不同的安装。

A: discovered 3 nodes — t1:temperature:TH-100(C), h1:humidity:HM-20(pct), v1:vent:VT-9(pct)
A: vent above 26C: 26.5 -> v1=80.0

B: discovered 5 nodes — t1:temperature:FX-200(F), h1:humidity:HM-20(pct),
   c1:co2:CO-5(ppm), v1:vent:VT-9(pct), w1:valve:WV-3(pct)
B: vent above 26C: 26.3 -> v1=80.0
   server code: unchanged   rules: unchanged   rebuild: none

摄氏传感器和华氏传感器并排站在了同一个阈值前面。 而且中途多出了 CO2 传感器和阀门,却什么也没变。

这是原文所说的增殖最精瘦的形态。经验要增加,后来进来的必须能站在先前那些相同的位置上,而六个字段让这成为可能。

(温室的整条链条见换掉传感器,控制照旧。)

没有锚,例外就开不了口

原文里最好的观察是这一段。

有了一个锚,例外就开始开口说话了。 在散落的笔记里,22 天也好 15 天也好,都只会以「今年有点不一样啊」流过去。没有可比的基线,你连它是例外都不知道。

做出来之后,这个结构原样出现在了代码里。设备服务器递出机器状态时,不只给值,还一起给出这个值是拿什么来比的

final overdue = (m['runHours'] as int) > (m['serviceEveryHours'] as int);
final vibrationOver =
    (m['vibrationMm'] as num) > (m['vibrationLimitMm'] as num);
return _json({
  'id': id, ...m,
  'serviceOverdue': overdue,
  'vibrationOverLimit': vibrationOver,
});

只有 vibrationMm: 5.2 什么也说不了。旁边得有 vibrationLimitMm: 4.5,5.2 才成为例外。原文里「18 天这个锚」就是这个位置。

而在真实的答案里,这个差别显现出来。

A: CONV-03 needs attention — service is overdue (9310 h against a 8000 h interval)
   and vibration is above limit (5.2 mm against 4.5 mm).

数字旁边永远挂着参照。那就是原文所说「把平均立成基线,让每一年都在它之上被读」的实际样子。

(设备助手见把依据放在答案旁边。)

18 天与 250 年 — 写明它们是举例

原文里有好几个数字。移栽后平均第 18 天开第一朵花、偏差在两天以内,以及跨代之后 250 年份的经验。

那些数字是举例,不是观测。 我们没有看过哪家农户的真实记录,250 年是用算术画出来的图。可它们在文章里写得很具体,读起来就像观测。

这篇文章把那些数字明确标为举例。并且只把这个系列真正量过的东西写成数字。

性质
节点自我声明的字段数6实测(格式就是如此)
代码中知道单位的位置1 处实测
一小块规则7 行实测
从安装 A 到 B 变动的规则行数0实测
「移栽后第 18 天开第一朵花」举例
「跨代之后 250 年」举例

没能证明的那一层

原文的脊梁是三层。事实 → 技术 → 预测。

这个系列实际展示的只到第一层。 以同一形状摆放就能叠着看;这样摆放之后,新进来的会站在先前那些相同的位置上。就到这里。

第二层 — 事实堆积成为技术 — 没看见。 这些样例只跑了几天,还没到能称为「堆积」的时间。温室的规则是我写的,不是从数据里长出来的。

第三层 — 技术变成预测 — 连试都没试。 原文的那一部分至今仍停在当初的位置。

不把这一点含糊过去很重要。原文的问题在于把三层一口气讲完,让第一层的依据把第三层也一并带了过来。第一层为真和第三层为真,是两个不同尺寸的主张。

这个样例没有做的事

保留原文诚实一节里的四个条件。再写下做出来之后额外知道的。

守住「同一形状」的难处不只在人这一侧,也在格式这一侧。 原文把它看成人的问题 —「忙碌的春日里翻开笔记本对着栏目填写很难」。真做出来才发现,决定字段的人一旦选错了什么该成为字段,后面的一切都会错位。如果没有把 unit 设为字段,那么华氏传感器一进来,规则收到的就是差了二十来度的值。要设哪些字段,就是设计的全部。

这篇没有新样例。 引用的东西全部产自此前的几篇。

农户的叙述属于原文,不是采访。 因为是匿名叙述所以不涉及真实人物的问题,但为了不被读成观测,这篇只把它当作举例引用。

所谓「知识层」这个协议的实体,这个系列没有处理。 原文预告了那一层,而这里展示的六个字段是那一层非常靠下的一片。它上面长什么样,我们还不知道。

把东西留成可叠的

原文这样收尾 — 经验不是保存,而是增殖。

六个月后我们加一行。要增殖,先得能叠起来;要能叠起来,字段得一样;要字段一样,就得有人事先把那些字段选好。

是六个字段。少了其中一个(unit),剩下五个就毫无用处 — 因为华氏的值会原封不动地坐进摄氏的位子里。

增殖不会自己发生。但把「可以自己发生」的位置先做出来是做得到的,而那并不是了不起的技术,只是多设了一个字段。


makemind.dev 「探索」— 展示了「同一形状」实际上是六个字段。

Twitter