
产品思维(四):自愈系统 — 教机器自己修自己
能自己发现、诊断和修复故障的系统的哲学与工程——从 watchdog 反模式到自主改进引擎。
自己修好的那个 Bug#
2026 年 5 月下旬的一个早上,我的钉钉弹了一条系统消息:
“self_heal Rule 37 triggered: restarted research-pipeline after 3 consecutive OOM kills. Root cause: scanner thread retained full PDF buffers across iterations. Applied patch: explicit
delafter extraction. Validation: 45 minutes post-patch, RSS stable at 1.2 GB (was 2.4 GB pre-patch).”

这个补丁不是我写的。我甚至不知道有这个问题。系统自己注意到了一个模式——六小时内连续三次 OOM 被杀——把 kill 时间点和 scanner 的活动日志做了关联,诊断出内存泄漏,提出了一个针对性的修复方案,以 git commit 的形式应用了补丁,跑了 45 分钟的验证窗口,确认 RSS 数据稳定后才通知我。
这就是自愈。不是吞错误,不是盲目重试,而是一种结构化的、经过验证的故障响应,让系统比故障前更强壮。
我不是一夜之间就有这个能力的。花了好几个月:构建、搞坏、用惨痛代价学到反模式,再一点一点把那些经验提炼为系统可以自主消费的东西。这篇文章就是这段旅程——从一个会杀死健康进程的幼稚 watchdog,到一个能提议、守卫、应用、验证、学习的完整 kaizen 自动驾驶仪。
为什么自愈比可靠性工程更重要#
传统可靠性工程问的是:“我们怎么预防故障?“自愈问的是另一个问题:“既然故障不可避免,我们怎样让系统对故障的响应成为一种内在能力,而不是外部干预?”
这个区别是实打实的。我在一台 4 核 CPU、7.5 GB 内存的服务器上跑一套研究智能体系统,管着 12 个并发 agent(scanner、reader、ideator、experimenter、designer、writer、reviewer、statistician 等等),协调跨机器的 fleet worker,维护一个几万节点的知识图谱,自主发表研究论文,24/7 不停。我是一个人,我要睡觉。
如果每个故障都需要我来处理,系统一半时间都是死的。数学很直接:40+ 个已知故障模式,每种每天哪怕只有 5% 的概率,几乎每天都会碰上点什么。每次 30 分钟诊断加修复,我全部清醒时间都填进去了。
自愈改变了这个等式。每种故障模式一旦被人修复过,就被编码为一条下次自动触发的规则。几个月下来,系统积累的是制度性知识——不是没人看的文档,而是每分钟都在执行的逻辑。我的角色从消防员变成了规则作者,再变成审计员。
师傅原则#
我现在构建的每个系统都运行在我所说的"师傅原则"之下。这个比喻是刻意选的:老师傅不只是修屋顶——他教徒弟怎么认出屋顶需要修、怎么诊断具体的故障、怎么正确地修好。知识是会复利的。
原则有三层:
第一层:即时修复。 东西坏了就修好它。这是基本功,每个工程师都做。
第二层:经验提取。 修完之后,把模式提炼为结构化数据:问题签名(什么可观测信号说明有故障)、诊断路径(检查了什么、检查的顺序)、修复模板(修复方案,参数化以便复用)。不是模糊的"经验教训"文档,是机器可读的结构。
第三层:自动化扫描规则。 把经验编码为系统可以执行的东西。主动扫描,在同类故障显现前就能检测到;自动修复规则,在模式重现时应用同样的修复;守卫,从根本上防止条件产生。
大多数工程师止步于第一层,好的工程师到第二层。我构建的系统被设计为自动到达第三层——kaizen 自动驾驶仪读取过去干预的结果,通过 LLM 提炼教训,基于观察到的模式提出新的扫描规则。
元原则:你的代码应该是你的徒弟,而不只是你的工具。教它的是推理过程,不只是动作本身。
教训格式长什么样#
提炼这一步是大多数工程师过早退出循环的地方。他们写个 commit message,关掉 ticket,继续干下一件事。但 kaizen 提议者真正需要的是另一种东西——它需要推理一条过去的教训是否适用于眼前的新情况,这就需要结构。
研究智能体的教训库用这套 schema:
| |
retired_when 字段是最不显眼但最重要的。没有它,规则会在悄悄失效后继续存在。一条为 1 万节点图谱校准的规则,在图谱长到 10 万节点、查询延迟自然增加之后就失效了。Rule 22 正好踩了这个坑——kaizen 发现了校准偏移,更新了阈值,保留了规则的结构,只改了参数。
提议者在生成候选干预之前会读教训库。如果一种干预类型有"遇到 X 时优先 Y,因为 Z"的教训,提议者要么遵循它(并在提议中引用),要么明确论证为什么不适用(并记录原因)。这就是过去经验约束未来提议的机制,不需要把行为硬编码进去。
架构:四层,四个时间尺度#
我的研究智能体自愈在四个层次上运作,每层捕捉上一层捕捉不到的故障。
Level 0:进程监控(秒级)#
supervisor 每分钟通过 cron 跑一次,逻辑很直接:核心进程还活着吗?不在了就重启。监控七个进程:
research-coordinator (systemd, on-failure 重启)
research-pipeline (systemd, on-failure 重启)
research-dashboard (systemd, always 重启)
research-supervisor (systemd, always 重启)
research-kg-merger (systemd, on-failure 重启)
research-dingtalk (systemd, on-failure 重启)
research-fleet (supervisor 子进程)
处理崩溃、OOM 被杀、进程卡死。粗糙,但必要——没有稳定的基底,上层愈合根本跑不起来。
supervisor 还处理"陈旧代码"问题:当 agents/、framework/ 或 lib/ 中的源文件被修改后,coordinator 还在跑旧代码。supervisor 比对源文件 mtime 和进程启动时间,发现差异后带 5 分钟防抖发送 SIGTERM,60 秒内优雅关闭失败则 SIGKILL 跟上。
Level 1:信号监控(分钟级)#
每 5 分钟采一次健康快照,追踪时间序列信号:
papers_accepted_24h (输出吞吐量)
ideas_approved_rate_7d (管线产出率)
api_error_rate_1h (上游健康)
coordinator_rss_mb (内存趋势)
pipeline_rss_mb (内存趋势)
worker_sync_lag_s (fleet 一致性)
self_heal_recoveries_24h (自愈成功率)
self_heal_failures_24h (升级次数)
每个信号都带元数据:单位、方向(上升是好还是坏?)、告警阈值。kaizen 系统同时把这些信号作为观察输入(系统现在什么状态?)和验证输出(我的干预真的让指标动了吗?)。
Level 2:自愈规则(分钟到小时)#
研究智能体在 supervisor 和 coordinator 中有 40+ 条自愈规则。每条规则遵循同一结构:
- 触发:对日志、指标或状态的模式匹配
- 诊断:确认问题是真实的,不是瞬态毛刺
- 动作:具体的、有边界的修复
- 验证:确认修复生效
- 升级:修复失败时通知人类
规则编号(Rule 1 到 Rule 41+),记录带一致的前缀:self_heal Rule N: <action>。这让它们可以被 grep、计数、审计。
在这些规则出现之前,我陷在反复的救火循环里——非原子写导致的损坏、崩溃后变僵死的 PID 锁、agent 之间状态不一致没有任何检测。2026 年 5 月的系统性修复从基础层解决了这些:原子文件写(临时文件+重命名)、fcntl.flock 锁(进程崩溃自动释放)、state_machine.py 验证 protocol/idea 状态转移并记录历史、invariant_checker.py 每 10 个 coordinator 周期跑一次,检查实验一致性、僵死锁、资源异常。
invariant checker 是最关键的一块。没有它,不一致会悄悄积累,直到几小时或几天后以令人困惑的症状爆发。有了它,违反立即被检测和记录——自愈规则可以在问题复合之前就对违反日志采取行动。
一条规则的实际形态:
Rule 12: 僵死锁清扫
触发: 锁文件存在超过预期进程生命周期 2 倍
诊断: 检查锁文件里的 PID 是否还活着
动作: PID 已死则删除锁文件
验证: 被阻塞的进程应在 60 秒内恢复运行
升级: 锁移除后进程仍然卡住,则重启它
这条规则建立后,Rule 12 在三个月内自动触发了 47 次,每次省掉一次 2 小时的调试循环。第三次触发时就回本了,之后全是纯收益。
Level 3:Kaizen 自动驾驶仪(小时到天)#
这是最有意思的部分。kaizen 系统是独立的守护进程,从外部观察研究智能体,提出改进方案,带安全护栏地应用,验证结果,并为未来提议提炼教训。它跑在自己的 systemd unit 上,不能修改自身(这条路径被硬性阻断)。
循环是:观察 → 提议 → 守卫 → 应用 → 验证 → 学习。
观察:每 5 分钟采样所有信号,构建时间序列历史。
提议:每小时,一个 LLM(“提议者”)审查最近的观察、当前教训和历史干预,生成候选干预——带有明确假设和数值预测的具体动作。
守卫(预飞检查):任何干预被应用之前,必须过六关:
- 不在被阻止的路径上(kaizen 不能碰自己、git 内部文件或密钥)
- 不是被阻止的动作类型
- 在每日预算之内(研究智能体最多 5 次/天;AI4Marketing 生产侧最多 3 次/天)
- 在并发限制之内(最多 2 个同时进行)
- 不是最近动作的重复(2 小时去重窗口)
- 已定义具体的回滚方案
风险 >= 中等:必须有金丝雀基础设施。没有就进 backlog 等待,不自动 apply——这迫使我在系统能在某个区域自我修改之前,先把金丝雀能力建起来。约束本身就是路线图。
应用:adapter 把动作作为 git commit 执行,commit SHA 记录备用。
验证:可配置窗口(最少 60 分钟)后,检查干预的预测是否成立——目标信号是否按预测方向移动了预测幅度?
学习:每 24 小时,蒸馏器审查所有已终结干预(已验证、无效果、已回滚),提取结构化教训,反馈给下一个提议周期。
元审查:每周,更高层次的分析检视校准漂移(预测变得更准还是更不准?)、反复出现的盲区和最高价值的下一步。
/health 端点:可观测性是第一公民#
上面所有自愈都建立在一个前提上:你得能看清系统的状态。在写任何自动修复逻辑之前,我先建了 /health 端点——一个 URL,返回整个研究智能体的结构化 JSON 快照:管线状态、agent 活动、fleet worker 同步延迟、各进程内存占用、知识图谱大小和近期增长、实验吞吐量、过去 24 小时自愈规则触发历史。
GET http://113.249.102.134:8081/health
{
"pipeline": "running",
"coordinator_rss_mb": 812,
"pipeline_rss_mb": 614,
"graph_nodes": 47532,
"graph_edges": 129968,
"papers_accepted_24h": 3,
"self_heal_recoveries_24h": 2,
"self_heal_failures_24h": 0,
"worker_sync_lag_s": 4.2,
"invariant_violations_open": 0
}
我在做任何干预(手动或自动)之前都先看这个。kaizen 提议者每 5 分钟调用它来构建信号历史。invariant_violations_open 非零意味着 invariant checker 发现了尚未被任何规则解决的不一致——这是新事情正在发生的信号。
/health 端点也是我从 Mac 上做快速心跳检测的方式,不用 SSH:curl -s http://...:8081/health | jq .pipeline。返回 “running” 就说明 Level 0 一切正常。返回 “crashed” 或请求超时,Level 0 要么已经处理了,要么正在处理。这种检测的速度和精确度,是 5 分钟恢复和 30 分钟恢复之间的差距。
战争故事:六个教会我一切的故障#
1. 杀掉健康进程的 Watchdog#
系统早期,我给钉钉监听进程写了个幼稚的 watchdog:检查日志文件的修改时间够不够新,如果 5 分钟没被写入就认为进程死了,杀掉重启。
钉钉是消息驱动的。5 分钟没有消息到达(夜间很正常),进程就不往日志写东西。它完全健康——只是在等待。watchdog 每天晚上都忠实地杀掉它,引起重启循环,产生虚假告警,触发下游连锁反应。
教训是根本性的:存活信号必须是进程的内在属性,不能从它的输出推断。 没有消息要处理的消息驱动进程不是死了,是在等待。修复:给钉钉监听器加显式心跳,切换到 systemd 的 WatchdogSec= 协议(进程每 N 秒向 systemd socket 发 ping;ping 停了 systemd 才认为进程死了)。
更深层的模式:用输出产物作为存活探针,混淆了"正在工作"和"还活着"这两件正交的事。进程可以活着但空闲,也可以在忙循环中实际上已死(没有处理任何有价值的东西)。好的存活检查测控制平面,生产力检查测数据平面,不要混。
这个教训成了 kaizen 扫描规则:检测任何使用文件 mtime 作为存活信号的监控,标记为需审查。
2. 0% 有效的奖励守卫#
研究智能体有一个奖励守卫——一个质量门控,在实验结果进入知识图谱之前验证它们,检查效应量、样本量、复现尝试。
有两天,守卫命中率是 0%。每个实验都没被检查就过关了,系统在快乐地积累垃圾数据。
根因是 async 竞态。守卫放在了"生产端”——experimenter agent 报告完成后立即运行,检查磁盘上的结果文件。但 experimenter 异步写结果:agent 信号完成,coordinator 推进到下一步,而文件写入通过异步 flush 在毫秒后才落盘。
结果:守卫检查的是一个还没落盘的文件。什么都没有。逻辑是"没有产物就没什么需要守卫的”——对实验不产生输出的情况,这是合理的默认。所以每次都通过。
修复:把守卫移到"消费端"——在下一个消费者(statistician 或 knowledge engine)读取产物的时刻验证。那时文件肯定存在(否则消费者就报错了)。在消费处守卫,而非生产处守卫。
2 天窗口期内被接纳的 14 篇论文追溯重新验证(backfill),其中几篇被标记为需要复现。教训进了 kaizen 库:“在异步管线中,优先选消费端验证;生产端守卫可能比异步 flush 先跑。”
3. 丢了 57% 数据的实验#
experimenter agent 通过 Claude Code CLI 跑 Python 实验,每个实验 4–8 个条件,CLI 有超时,触发时进程被杀。
几个月来,57% 的实验没有可用数据。原因:超时 kill 丢弃一切。条件 1–3 可能已经完成并产生了有效数据,但没保留。下一次运行从头开始,重做条件 1–3,在条件 4 又超时,无限循环。计算全浪费,没有进展。
修复两部分:
检查点感知:把每个条件的原始计数注入下次 prompt。agent 能看到:“条件 1:200 样本完成,条件 2:200 样本,条件 3:45 样本(部分),条件 4:未开始。“然后从条件 3 恢复,而不是重头来。
部分数据保留:即使实验被杀,已写入的数据不丢弃。statistician 学会处理部分数据集——标记为统计功效较低但仍有信息价值。数据跨运行累积,而不是每次超时就蒸发。
结果:完成率从 43% 升到 89%。剩下 11% 是真正需要更多计算的实验——而它们现在也在跨运行积累数据,而不是每次从零开始。
原则:永远不要设计一个崩溃意味着全部数据丢失的系统。 每个长时间运行的操作都应该可以做检查点。数据库设计里这是常识(预写日志),LLM agent 工作流里几乎没人做——大家把每次调用当作无状态的,它不是。
4. 饿死 28 天的管线#
研究管线通过 FSM 驱动的 coordinator 调度工作。想法经历状态:proposed → debating → approved → assigned → running → complete。有一个分支——finance 方向——连续 28 天产出了零个 approved 想法。
表面症状是"finance 想法没被 approve”。排查要沿整条调度链追溯:从 ideator 生成,经过 debate 门,经过 classifier,到 coordinator 分配。根因在 coordinator 的 enumerate_candidates():它按命名惯例过滤想法,而经过 debate 阶段的 finance 想法被打上了 *_debate 后缀——过滤器跳过这个后缀,本来是为了避免已经辩论过的想法再次被辩论。
但过滤条件写错了地方。它在分配阶段也跳过了 *_debate 想法,而不只是辩论阶段。于是成功通过辩论的 finance 想法永远不会被分配给 experimenter。它们停在 “approved” 状态,41 个批准的 finance 想法里有 40 个是这个状态——饿了 28 天。
系统性教训:FSM 诊断需要从源头到末端追溯完整的状态链,不能只检查单个跳转。kaizen 教训:“当某个类别的调度管线吞吐率为 0 时,追溯 enumerate_candidates 的过滤逻辑——为某个目的排除特定状态的命名惯例,可能意外地在其他地方也排除了它们。”
5. 从没记录到日志的 Logger#
研究智能体的每个 daemon 都用 SqliteHandler 设置自己的 logger,把结构化日志记录到本地数据库供后续分析。有两天,钉钉监听器的错误日志一片沉默,明明有错误在发生。handler 配置正确,daemon 在运行,但数据库里没有任何记录。
原因:root logger 已经挂了一个 handler——某个更早的 import 加上去的 SqliteHandler。当 daemon 调用 logging.basicConfig(...) 配置自己的 handler 时,basicConfig 在 root logger 已有 handler 的情况下是个空操作。daemon 的 handler 从未被注册。所有日志都跑到了已有的 root handler(一个写 stdout 的 StreamHandler),而 stdout 没有被分析管线捕获。
修复:显式挂载 handler,而不是依赖 basicConfig。每个需要结构化日志的 daemon 现在这样做:
| |
propagate = False 是工程师容易漏掉的第二件事。没有它,daemon 自己的 logger 处理了一条记录之后,它还会向上传播到 root logger,被再处理一次——双重记录,双倍存储成本,分析混乱。
教训:“在可能被已调用过 basicConfig 的进程 import 的 daemon 里,永远不要用 basicConfig。用显式的 logger 级别 handler 挂载,加 propagate = False。“现在是 kaizen 扫描规则:当 root logger 已有 handler 时,检测任何用 basicConfig 的 daemon 初始化代码。
6. 永远不会断开的熔断器#
系统有一个 API 调用的熔断器(DashScope、Claude、外部服务)。某个服务的写操作以约 30% 的比率失败了好几周,熔断器从未触发。读操作成功,熔断器显示一切正常。
bug 在 record_success() 里:记录一次成功调用时,它清除了整个失败窗口——不是递减计数器,是直接归零。在读成功/写失败交替的模式下,每次成功的读都在写失败能累积到阈值之前把计数器清零。熔断器永远无法触发。
修复区分了状态:
- CLOSED(正常):失败按时间窗口追踪,成功不清除失败,旧失败只靠时间过期。
- HALF_OPEN(测试恢复):单次探针成功 → 回到 CLOSED 并重置。
- OPEN(已触发):所有调用被短路到降级路径。
关键洞察:在 CLOSED 状态下,成功和失败是独立证据。一次成功的读不能撤销一次失败的写——它们可能打的是不同端点、不同代码路径、不同后端分片。只有在 HALF_OPEN——你在显式测试"问题解决了吗?"——一次成功才意味着"清除失败记录”。
“M 秒内 N 次失败,任何成功都重置"这个流行模式,对混合操作服务来说是根本性缺陷。kaizen 教训:“CLOSED 状态的熔断器必须靠时间老化失败记录,不能靠成功清零;success-based 清零只在 HALF_OPEN 时正确。”
自愈不是吞错误#
这一点我要说清楚,因为对自愈系统最常见的误解是:它们只是精心包装的 try/except: pass。
自愈是吞错误的反面。吞错误藏问题,自愈暴露问题、诊断它、修复它、验证修复、记录教训。实现自愈后系统更可观测,不是更少。
每个自愈动作记录完整的决策链:
- 什么触发了它(异常或模式匹配)
- 它诊断了什么(根因假设)
- 它做了什么(具体动作)
- 它预期什么(带数值幅度的预测)
- 实际发生了什么(验证结果)
- 它学到了什么(提炼的教训)
grep "self_heal Rule" /data/research-agent/logs/*.log | wc -l 给我每次自主干预的计数。我可以审计每一条,可以不同意系统的选择,可以加紧守卫条件。kaizen 的护栏(每天最多 5 次、强制回滚方案、区域冻结)存在的目的就是防止系统陷入不可观察的自我修改。
设计原则:自愈必须比手动修复更透明,而不是更少。 我手动修某个东西时可能忘了记录。系统修某个东西时,在结构上不可能不记录——记录就是机制本身。
fix → distill → encode → verify:四步元模式#
上面所有故障里,同一个模式反复出现。我把它叫做 fix → distill → encode → verify。
Fix:遇到故障,修复它。即时代价已经付出,修复本身不是教训——它是原材料。
Distill:提炼教训——不是"改了什么”,而是"什么时候要检测这个、怎么诊断、为什么这个修复有效、什么时候可能不适用”。这一步需要 15–20 分钟,大多数工程师跳过了。跳过意味着下一个工程师(或六个月后的你)从零开始。
Encode:把教训写成系统能执行的东西。扫描规则、守卫条件、验证阈值。格式很重要:当 X 时 / 优先 Y / 因为 Z / 可通过 W 证伪。“因为 Z"让教训在原始场景之外也能复用。“可通过 W 证伪"让教训可以被测试。没有这两个,教训只是注释,不是规则。
Verify:带着新规则跑系统,确认它能捕捉到它被设计针对的那类故障。可以通过注入合成实例来测试(手动制造一个僵死锁文件,确认规则触发)。没有这步,你有的是信念,不是工程。
Rule 12 的修复花了 2 小时诊断和实现。distill、encode、verify 的循环花了 25 分钟。三个月里规则自动触发了 47 次,每次省掉一次 2 小时的调试循环。第三次触发就回本了,之后全是复利。
这就是为什么师傅原则坚持三层都要做。第一层(fix)产生原始经验。第二层(distill)从中提炼可复用的知识。第三层(encode)把知识部署为自动化能力。缺任何一层,复利就断了。
从被动到主动到自主#
回顾系统的演进,三个阶段很清晰。
第一阶段:被动(第 1–2 个月)。 一切手动。supervisor 对崩溃的进程跑 systemctl restart,我收钉钉告警,登录、诊断、修复。平均恢复时间 30–60 分钟。5 种已知故障模式,都靠"重启那个东西"处理。
第二阶段:主动(第 3–4 个月)。 开始编码模式。supervisor 不只重启崩溃的进程,还能检测预崩溃条件:内存向 OOM 攀升、磁盘快满、API 速率限制接近,采取预防动作。自愈规则从 5 条长到 20+,每条都带文档——不是给我看的,是给 kaizen 提议者读的。已知故障模式的恢复时间降到 1–5 分钟,大多数在我注意到之前就解决了。
第三阶段:自主(第 5 个月以后)。 kaizen 自动驾驶仪把系统从"固定响应库"带到"提出新颖响应”。它不限于我写的那 40+ 条规则——能观察系统、对从未见过的问题提出假设、提出修复。
最有意思的涌现行为:kaizen 开始提议改进自愈规则本身。Rule 22 触发得太激进——重启一个实际上健康但速度较慢的进程。Kaizen 注意到模式(高重启次数 + 输出指标无改善),提议放宽阈值,应用了它,验证重启次数下降且输出无退化,提炼了教训:“Rule 22 的阈值是为较小的知识图谱校准的;图谱增长后查询时间自然增加,原阈值过于激进。”
这是元学习。系统不只在修 bug——它在改进自己的 bug 修复机制。
自主修改的三条铁律#
任何修改自身的系统都需要硬性约束。没有它们,系统会把自己"修"进不可恢复的状态。我的三条规则,来自失败:
规则一:每个干预都有回滚方案。 没有回滚计划,不 apply——在守卫层强制执行。研究智能体用 git revert 作为回滚:确定性且可审计。kaizen 早期提议过一个修改 SQLite schema 的"修复”,回滚方案是"从备份恢复”,但根本没做备份。现在 adapter 要求任何修改 schema 的动作都必须在回滚方案里包含备份步骤。
规则二:风险 >= 中等需要金丝雀基础设施。 可能影响系统稳定性的变更(进程参数、API 路由、管线逻辑),必须有金丝雀——一部分流量或影子模式,让变更在不完全提交的情况下得到验证。没有金丝雀基础设施的区域,干预进 backlog 等待,不 apply。这迫使我先把金丝雀能力建好,系统才能在那里自我修改。约束就是路线图。
规则三:三次连续回滚冻结区域 24 小时。 一个区域连续三次应用后回滚,24 小时内不再提议或 apply 任何干预。这防止系统震荡——在一个问题是结构性的、需要人类洞察的区域反复尝试同类修复。冻结是系统在说:“我试了三次都失败了,我需要帮助。“这是正确的行为。
给任何想建自愈系统的人#
先做监控,再做智能。 在构建 LLM 驱动的提议者之前,确保进程崩溃时会重启。这解决了 60% 的生产问题。
先让故障可观测,再让它可修复。 你不能治愈看不见的东西。在投入自动修复逻辑之前,先投入结构化健康快照、错误聚合和信号时间序列。
在消费处守卫,而非生产处。 在异步系统里,在产物被读取的地方验证它,不是在写入的地方。写入可能还没完成,产物可能在写入后被损坏了。消费者知道它需要什么。
区分存活和生产力。 进程可以活着但空闲,也可以在输出东西但实际上已死(无进展的重试循环)。存活探针测控制平面,生产力探针测数据平面,不要混。
编码"为什么”,不只是"什么”。 教训里的"因为 Z"让系统知道教训什么时候不再适用。没有它,规则在失效之后还会继续被引用。
设计升级路径,不只是修复路径。 当自愈失败——三次连续回滚、守卫条件无法满足、预测永远不验证——系统需要优雅地升级。24 小时冻结加钉钉通知是升级路径。把干预放进 backlog 并注明"缺少金丝雀基础设施"也是升级路径。静默失败不是。
给教训做版本控制。 引用具体阈值的教训(RSS > 2 GB,超时 > 300s,错误率 > 5%)会随系统增长而失效。记录每条教训创建时的上下文、校准场景、以及什么会让它被废弃。否则教训库会变成一个充满过期规则的坟场,提议者读着错误地应用。
目标不是零人工干预。 目标是人工干预只用于新颖问题,不用于反复出现的问题。每个需要我介入的老问题都是自愈系统的失败,每个被正确升级给我的新问题都是成功。
下一步:跨系统学习#
研究智能体目前从自己的干预中提炼教训。下一步是跨系统学习——一个系统学到的教训,通过 kaizen DaaS chain 协议传播到其他系统,提议者检查同样的模式是否出现在本地代码里。
架构:每台主机在共享的 GitHub 分支里维护一个签名教训的 outbox(Ed25519 签名的 JSON),每 15 分钟 push 自己的新教训并 pull 同伴的。提议者读取 inbox,检查是否有引进的教训适用于本地代码。
2026 年,这套机制已经走通两轮:
第一轮:DaaS 系统产出了一条关于 JSON 静默丢失的教训(写操作看起来成功但文件被截断)。教训传播到了研究智能体。提议者审计了 lib/pro_gate.py:81,发现了完全相同的模式,自动 seed 了修复。
第二轮:同一条教训也传播到了 AI4Marketing。提议者审计了 kaizen 在那台机器上自己的干预状态文件,发现了同样的 bug——在 kaizen 自己的代码里。元脆弱性:自愈系统带着它正在教其他系统去避免的漏洞。修复被提议、过了守卫、应用了。
一次人工修复 → 一条教训 → 三个系统被修补。这是复利在最递归的层次上的体现。
教机器是什么意思#
我以一个在我睡觉时修好内存泄漏的系统开始了这篇文章。驱动这一切的哲学框架:传统软件工程把代码当工具,你写它、它做你告诉它做的事、出问题时你来修。单向。
自愈系统改变了这个关系的一部分。代码观察自身、提出变更并执行——在一个由人设计的约束、验证和教训积累框架内。系统不是"想做什么就做什么"意义上的自主,而是"处理日常问题让人专注于困难问题"意义上的自主。
正确的比喻不是机器人取代人类,而是徒弟向师傅学习。我修问题,并在修的过程中教系统(徒弟)如何识别和修复那类问题。随着时间推移,徒弟处理越来越多的日常工作,我的角色从"做"变成"教"再变成"审计"。
这就是师傅原则在最深层次的意思。每次我修一个 bug,不只是在修——我是在教。我教学的质量决定了徒弟能变得多能干。一条模糊的教训(“async 有点问题”)产生脆弱的规则。一条精确的教训(“在异步管线中,在消费端守卫,因为生产端的写可能还没 flush”)产生能泛化的规则。
理想状态:我消失一周,回来发现系统比我离开时更健康——不是因为什么都没出错,而是因为每件出错的事都被处理了、验证了、记录了、学到了。我们还没到那里。但每一条自愈规则、每一条 kaizen 教训、每一次守卫验证都是一步。系统比六个月前学得更快,因为它现在在从自己的学习中学习。
你的代码应该是你的徒弟,而不只是你的工具。教它推理,不只是动作。
本文是 产品思维 系列的第 4 篇,共 5 篇。上一篇:第 3 篇 — 用户体验与设计系统 · 下一篇:第 5 篇 — 抽象思维
产品思维 5 篇
- 01 产品思维(一):架构设计 — 从单体到自治 Agent
- 02 产品思维(二):安全工程 — 不偏执的纵深防御
- 03 产品思维(三):用户体验与设计系统 — Token、暗色与双语
- 04 产品思维(四):自愈系统 — 教机器自己修自己 当前
- 05 产品思维(五):抽象思维 — 从数学到系统