flowchart LR SP["参考输入<br/>该做什么"]:::outside SP ==>|"人给定"| C C["Agent<br/>控制器"] --> P["代码库<br/>被控对象"] P --> S["三层判定<br/>传感器"] S -->|"误差"| C S -.->|"能判定的:<br/>有没有违反什么"| P SP -.->|"判不了的:<br/>这是不是我们要的"| SP classDef outside stroke-width:3px,stroke-dasharray: 6 3
20 参考输入在环外
这是全书最短的一章,因为它讲的是一件很快就能说清、 但很容易被忽略的事:这套系统有一类问题按定义就回答不了。 不是”还没做到”,是范畴上做不到。
20.1 全部是约束满足,不是参考跟踪
把三层判定放在一起看,会发现一件事:测试问的是这段代码的行为 符合断言吗,结构检查问的是这段代码的位置符合规则吗, 路径不变量问的是这次改动违反了这条路径的约定吗。 三层全部在问”有没有违反什么”,没有一层在问”这是不是我们要的”。
用控制的语言说,这是约束满足,不是参考跟踪。 用航空的话说更直观:这是一套非常好的包线保护—— 防失速、防超速、防过载,任何一个参数越界都会被立刻拉回来。 但包线保护不负责把飞机飞到目的地, 而一架被包线保护得完美的飞机,可以非常安全地飞到一个错误的机场。
图里那个虚线框是这一章的全部内容:回路里的每一个元件都可以被仪表化, 除了给回路提供目标的那一个。
20.2 闭环系统不能生成自己的设定点
这是一个范畴事实,不是这套系统的缺陷。
一个控制回路需要一个参考输入,也就是”输出应该是多少”。 这个参考输入必须来自回路之外,理由很简单: 一个能修改自己目标的控制器,永远会报告成功。 它会在遇到困难时悄悄把目标改成自己已经做到的那个, 而这个动作在回路内部完全看不出异常 —— 误差归零了,控制器”完成”了任务。
这套系统在长任务那套机制里做对了这件事。小节 8.7.1 里那六类记忆,第一条就是”不变意图:永不被迭代改;改它 = 人的决定”。 这条设计是完全正确的,而且它正是上面那条定理的直接应用。 但它只被用在了单个长任务这一层 —— 在整个系统这一层, 同样的问题存在,而且还没有被同样对待。
20.3 这个系统里最不冗余的一个元件
“每份可变状态收敛到单一 owner”这条原则被贯彻到了整个仓库, 贯彻到超过一半的路径不变量都在回答”这块状态归谁写” (小节 14.5)。这个系统里最重要的那份可变状态 —— “该做什么”—— 确实只有一个 owner:那个人自己。 没有第四格判定,没有观测器,没有第二个 writer。
具体的表现是:26 个产品,而全部材料里没有一处讨论 哪个有用户、留存多少、哪个该砍。这不是疏忽, 这恰恰是这套系统的边界所在。但它带来一个后果 —— 参考输入是这个系统里唯一没有被仪表化的元件, 而它恰恰是决定最终结果的那一个。
一个前馈完美、反馈严密、传感器带自检、观测器覆盖全面的控制系统, 如果设定点给错了,它会极其高效地收敛到一个错误的地方。 它跑得越好,收敛得越快。
20.4 判定的均匀会掩盖价值的不均匀
一套判定基建对它覆盖的所有东西一视同仁。这是它的优点 —— 同样的三层检查、同样的路径清单、同样的证据门, 不会因为谁更重要就更严一点,也就不会因为谁在场而漂移。
而这个优点有一个背面:它同样不会因为哪个东西不重要就少花一点。 在流水线眼里,一个没人用的产品和一个有真实用户的产品是同一种东西。 两者都要付全量检查的时间、依赖升级的适配成本、架构调整的迁移工作, 而这些成本逐次发生、随数量线性增长。 至于”这一个值不值得付这些成本”—— 没有任何机制会提出这个问题。
书里那个仓库有二十六个产品,几乎可以肯定其中一批的边际价值接近于零。 但这不是那个仓库特有的毛病:任何一个判定层做得足够好的系统 都会长出这个形状,因为判定越均匀、越可靠, 它就越不会替你区分什么值得做。
这个缺口的形状和 章节 19 那一章完全一样 —— 不是”这个判断很难做”,是”做这个判断需要的量根本没有被测过”。 留存、活跃、单位成本,这三个数一旦存在, “该砍哪个”就从一个需要勇气的决定变成一个读数据的动作。
所以参考输入的仪表化不是一件玄学。它的第一步和观测器的第一步 是同一句话:写下”这个东西在正常产生价值时,世界上什么会不同”。 写不出来的那些,就是答案本身。
20.5 参考输入的质量决定一切,而它无法被这套系统改善
这一节要说清楚一件容易被误解的事。这套系统提高了产出的效率和可靠性, 它没有、也不可能提高产出的正确性—— 如果”正确”指的是”做对了该做的事”。
两者的关系是乘法:最终价值等于参考输入的质量乘以执行的效率, 而这套系统只作用在第二项上。这意味着一件反直觉的事: 执行效率越高,参考输入的质量就越重要。
一个执行效率很低的团队,做错方向的代价被自己的低效限制住了 —— 半年只能做错一件事。换成一个能让几十个 Agent 并行、 一天合入五十二次的系统,做错方向的代价被同样地放大了。 效率不是中性的:它同时放大对和错。
20.6 人还要做什么
所以”不用人做代码审查”这句话必须被精确化 —— 审的东西变了,不是不审了。
人还要审四类东西:这个需求到底要解决什么问题(没有可比对的真值)、 两个方案的取舍(取舍依赖于外部目标)、边界是否定义完整 (“完整”相对于意图而言)、以及一个不可逆的动作是否真的应该发生。
最后一条是唯一一条既在环外、又能被机制辅助的。 路径清单里那四条最高风险的不变量守的正是这一类 (小节 14.6)。它们不能替人做决定, 但它们能保证这个决定被显式地做出,而不是被某次重试悄悄做掉。
这是机制在这条边界上能做到的极限,而它已经很有价值了 —— 因为大部分不可逆的错误,不是有人错误地决定要做它, 而是根本没有人意识到自己在决定。
20.7 容易被混淆的那条边界
有一类问题看起来在环外,实际在环内,值得单独拎出来: “这个实现方案好不好”在环内。 因为”好”在这里 有一个可以被判定的定义 —— 它满足那五条边界吗(小节 3.2): 行为能被观察、结构符合约定、尊重 owner 和不变量、 高风险动作显式、失败能定位。
下面这些则在环外:“这个功能该不该做”没有真值可比对; “这两个方案哪个更符合我们三年后的方向”依赖外部目标; “这个技术债现在该不该还”是资源分配问题。
分清这两类很重要,因为把环内的问题当成环外的, 会让人不去建本可以建的判定;而把环外的当成环内的, 会让人徒劳地找一个不存在的自动化答案。一个实用的判据是: 这个问题的答案,会不会因为公司的战略变了而变? 会变就在环外,不会变就在环内、可以判定。
20.8 参考输入失效的三种形态
既然它是系统里最不冗余的元件,值得列出它的失效方式 —— 虽然我给不出解法,但知道形态至少能识别它。
目标漂移:做着做着,目标悄悄变成了”把这个做完”, 而不是”解决那个问题”。小节 8.7.1 里的 “不变意图永不被迭代改”就是防这个的, 但它只在单个长任务这一层有效。
目标被指标替代:拦截率、合并次数、覆盖率 —— 这些是执行效率指标,而它们全都在变好的时候, 很容易被当成”我们在做对的事”。这套系统自己就是一个例子: 小节 21.1.1 讲过,所有指标都是过程指标, 没有一个是结果指标。
目标从未被明确表述:最常见的一种。 如果”我们要做什么”从来没有被写成一句可以被反驳的话, 那么它无法失效 —— 也无法被检验。
三种形态的共同点是它们都不会让任何检查变红。 这一章必须存在的理由就在这里:一本讲判定的书, 如果不明确说出”有一整类东西不在判定范围内”, 它就会被误读成一个完整的答案,而它不是。
20.9 一个人的系统里,这一章最要紧
我讲的所有东西,在一个人的系统里都成立, 而这一章在一个人的系统里成立得最狠。 因为参考输入的所有冗余机制 —— 讨论、争论、有人提出反对意见、 有人从另一个角度看同一个问题 —— 在一个人的系统里全都不存在。
这个缺失和别的缺失不一样。缺一道检查,某类问题会漏过去, 但迟早被发现;缺一个工具,Agent 只能猜,产出质量下降; 而缺参考输入的冗余,你会极其高效地把一件不该做的事做完, 而且过程中一切指标都很好看。
第三种没有自我纠正机制。 而这套系统的性质让它更严重: 因为执行效率极高,从”决定做”到”做完”的时间被压缩了 —— 留给”等一等,这件事对吗”的窗口也被压缩了。 一个执行效率低的系统,一个错误的决定要花三个月才能做完, 而这三个月里有很多次机会意识到它是错的; 一个一天能合五十二次的系统,那个窗口是几天。
20.9.1 唯一可行的缓解
我给不出解法,但可以指出一个方向: 把参考输入也变成一个有判定的东西。 具体形态是 小节 8.7.1 里那个”不变意图”的放大版 —— 把”我要做什么、为什么”写下来,写成一句可以被反驳的话; 定期(比如每月)回去读它,问”这句话现在还成立吗”; 记下每一次改变它的理由。
这三条看起来像是自我管理,但它们的实质是 给一个不可观测的状态建一个最简陋的观测器(章节 19)—— 把一个存在于脑子里的东西,变成一个存在于文件里的、可以被对照的东西。 不能保证它是对的,但至少能发现它变了 —— 而”发现它变了”对上一节那三种失效形态里的前两种是有效的。
20.10 参考输入的三个可以做的事
虽然它在环外,但有三件事是可以做的,而它们的共同点是: 把一个存在于脑子里的东西变成一个可以被对照的东西。
20.10.1 一、把它写成一句可以被反驳的话
“我们要做一个好用的产品”无法被反驳,所以它也无法失效。 换成”这批用户现在用 X 方式解决 Y 问题, 而我们认为他们会为一个更快的方式付费”可以被反驳 —— 用户可能不这么解决,可能不觉得慢是问题,可能不付费。 能被反驳,就能被检验。
值得注意的是这条要求和 小节 7.5.1 那条 “说不出失败形态的规则不该存在”是同一条纪律, 只不过对象从规则换成了目标。一个说不出自己怎么算错的目标, 和一条说不出自己守什么的规则一样 —— 它是品味,不是判据。
20.10.2 二、把改变它的理由记下来
不是记”目标变成了什么”,是记”为什么变”。 因为目标漂移的特征是每一次微小的改变都有理由, 而累积起来变成了另一件事。只有把理由记下来, 才能在半年后回头看这条链,判断它是”逐步逼近”还是”逐步走偏”。 这正是那六类记忆里”决策叙事”的作用(小节 8.7.1)—— 只追加、不可变、一轮一条。
20.10.3 三、给它一个固定的复核时刻
不是”想起来就看看”,是一个固定的节奏。 这个节奏该多长,用 小节 17.4 那条规则来定: 取决于外部世界给你反馈的延迟。 如果你的产品每周有数据, 每月复核一次;如果要等一个季度才知道效果,就每季度一次。
采样快于反馈延迟,采到的是噪声—— 而依据噪声调整目标,正好就是目标漂移。 这一条是这一章里唯一一处能从控制论直接借来的具体建议, 而它借的正是 小节 17.4 那条已经在别处被验证过的规则。
20.11 一个人怎么给自己造”第二个写者”
小节 20.9 说参考输入在一个人的系统里无冗余, 而 小节 20.9.1 给了一个方向。这一节把那个方向说得更具体, 因为”把目标写下来定期读”这句话,执行起来会滑成一种自我安慰。
滑掉的地方在于:读自己写的东西,读到的是自己当时的推理, 而不是那个推理的漏洞。一个月后重读那句目标, 你会重新想起当初为什么这么写,然后点头。 这不是冗余,这是回音。
有三个可以造出真正对抗性的做法,成本都不高。
第一是把目标写成一个预测,而不是一个意图。 “我要做一个更快的导出功能”是意图,无法失效; “这批用户会因为导出变快而把周使用频次从 1 提到 3”是预测, 一个月后可以对账。对账这个动作, 就是那个不存在的第二个写者。
第二是记下你放弃了什么。 每一次改变目标的时候, 不只记”改成了什么”和”为什么”,还记”这次决定意味着我不做什么”。 被放弃的那一列在半年后特别有信息量 —— 如果同一件事被连续放弃了五次,那它要么真的不重要, 要么你一直在回避它,而这两者你自己是分不清的, 但那份清单能分清。
第三是找一个不在这个系统里的人,每个季度讲一次。 不需要他懂技术,需要的只是他会问”为什么做这个而不是那个”—— 而这个问题,你自己问自己的时候会给出一个准备好的答案, 被别人问的时候不会。
三个做法的共同结构值得注意:它们都在制造一个 不共享你当时那套推理的对照物—— 一个未来的事实、 一份被放弃的清单、一个不知道你为什么这么做的人。 这正是”第二个写者”的实质:不是多一个人做决定, 是多一个不共享你前提的检查点。
20.12 三条实际的后果
先把这一章接回第一章。两堵墙讲的是判定跟不上产出; 这一章讲的是判定这一侧建好之后,瓶颈会移到一个判定管不到的地方。 合起来是一条完整的轨迹:产出慢、判定够的时候没有问题; 产出快了、判定跟不上,是第一、二堵墙;判定建好了,瓶颈移到参考输入。 大部分讲 Agent 工程的东西停在第二步,把”建好判定”当成终点 —— 知道下一个阶段在哪,决定了你什么时候该停下来(小节 16.11)。
由此有三条实际的后果。
不要用这套系统的指标去回答它回答不了的问题。 拦截率、失败率、转绿时长都是执行效率指标, 它们全都变好,和”我们在做正确的事”之间没有任何关系。
参考输入需要它自己的一套东西,而那套东西不在我这里。 用户访谈、留存数据、单位经济模型 —— 那是另一个领域的方法论。 我能贡献的只有一句:别指望判定边界替你做那件事。
如果你只有一个人,参考输入是你系统里风险最高的部件。 它无冗余、无判定、无观测器,失效还是静默的 —— 你会非常高效地把一件不该做的事做完。
第四部到这里收尾,四章是一个逐步收窄的过程: 章节 17 说回路可以被调,章节 18 说测量本身要可信, 章节 19 说有些状态根本测不到,而这一章说有一样东西按定义就在环外。 从”能做什么”走到”做不到什么”,这个顺序是刻意的 —— 一本讲方法的书如果不在最后划出方法的边界,它会被当成通用解。 它不是:它解决的是在有外部真值的领域里, 怎么让不确定的产出变得可以被判定。边界之外的东西,我一个字都答不了。