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 「探索」— 展示了「同一形状」实际上是六个字段。