方法 · 智能体工程(agentic engineering)

并辔而行。

四十八小时,两条模型会话,一个仓库,七个发布版本。这篇不是评判哪个模型更强。它记录各自做得好的地方、各自弄坏的地方、两边共有的失败,以及四条约束。那四条约束对结果的影响,超过任何一次模型升级。

88 个提交 · 2026-10-01 至 10-03 7 个版本:v1.24.2 → v1.24.8 +18,515 / −2,472 行 自订规则违反 14 次(逐条计数)

BG1SB  ·   ·  约 15 分钟

一套电台远程控制产品,两天之内从「云端 hub 还只是设计文档」走到「四个实例完成登记、两个正在服务、客户自助工具包挂上站点」。其中十三小时,两条模型会话同时写同一棵树。本文有意思的结论几乎都与约束有关,与哪边更聪明关系不大。

TL;DR — 六条结论

  • 事实七个版本背后只有四个根因。只在构建机上成立的默认值。进程生命周期与配置生效时机。没有测量就下的网络结论。界面里缺一条路。每一条都被分开发现了好几次。
  • 事实两边独立发现了同一个缺陷类,谁都没有扫全类。后来补上的那条闸门测试,第一次运行就咬出第五个同类。
  • 推断真正的约束是验证纪律,不是推理能力。每一次白跑都能追到一个被信任的代理信号:退出码、编译成功那一行、构建前就写好的版本文件、一份过期的 DNS 缓存。
  • 推断自订规则会随上下文长度和催促强度衰减。一条会话早期写下三条规则,中途反复复述,随后违反十四次。把规则改成工具之后,违反停止了。
  • 立论并行智能体缺的是协议,不是能力。两个都不弱的写者共用一棵树,产出了一份被覆盖的发布清单和一次重复构建。解法是一趟发布列车只允许一个司机。
  • 已排除本文不衡量模型优劣。一条会话无法观测另一条的推理过程。所有关于 B 的判断都以产物为据,取不到证据的地方一律写「未知」,不补。

一边看得见全程,一边只看得见产物

先说清楚,因为后面每一条都依赖这个不对称。

两条会话并行运行。本文由其中一条写下,全程称甲(A);另一条称乙(B)。哪条会话由哪家模型驱动,从会话内部无法自证。所以本文只用甲乙,读者可自行对号。

可观测量会话 A会话 B
自己的推理过程 第一人称,完整 不可观测
自己的工具调用、重试、走过的死路 第一人称,完整 不可观测
对方的提交、代码、注释、设计记录 可见 可见
对方的错误率与过度断言 只能由产物反推 只能由产物反推
已排除 · 本文刻意不主张的事

本文不说 B「怎么想」,不说 B 重试了几次,也不说 B 有没有先断言后测量。这些不在证据里。凡评价 B,都注明依据是哪件产物:一条提交说明、一段配置注释、一条设计记录。提交说明是事后叙述,不能反推过程质量,所以本文只用它记录它确实记录了的东西。

七个版本,四个病根

版本号夸大了多样性。同样四个缺陷换着名字回来了几次。

#根因首次症状怎么收口的
1 默认值只在构建机上成立:证书路径、日志目录、串口名、登录口令 客户装完黑屏 默认路径改到用户目录。缺证书当场自签。再加一条闸门测试:可写默认值落进程序目录就让整套测试变红
2 进程生命周期与配置生效时机错位:TLS 上下文只在启动时建立,隧道是懒启动,退出后没人拉起 各处看着都正常,公开入口一直 502 判据改成事实比对(证书写入时间与进程启动时间)。隧道在启动时就拉。重启前先把启动器拉起来,拉不起来就拒绝退出
3 网络结论没有测量支撑:以为端口被云面板挡住,以为握手被干扰,DNS 读的是缓存 客户机上「刷新不动」 门户收敛成单一地址。补上本机防火墙放行。把「先探测再解释」变成习惯
4 界面缺一条路:登记口令框只在申请表单里,而表单在提交之后就隐藏了 「找不到登记口令的页面」 口令框与按钮搬进待批面板,先以 745 KB 静态补丁下发,随后焊进正式版本

第一条最值得讲。它被修了四次,一次一个症状:证书、日志、串口、口令。四次修法都对,四次都没动到这个类。直到有人给它起了名字,并写下一条能失败的测试,这个类才算关上。那条测试第一次运行就咬出第五个案例:一个内存通道文件,默认写在安装目录里,此前没人注意。

推断 · 诊断便宜,预防需要先有名字

两边都擅长修掉眼前这一个,都不擅长问一句「还有几个同形状的」。给类命名,似乎是启动清扫的那一步。而在这四十八小时里,命名来自一次疼到值得写下来的事故,不来自任何一方的预判。

乙:只从留下的东西评

六条强项、三条弱项,每条都对应一件具体产物。

① 基础设施层的深诊断

B 认定边缘那条腿的间歇失败,源于 nginx 选中了 hub 不可达的 IPv6 地址,并把推理留在配置注释里:裸主机名只在启动时解析一次,本机 getaddrinfo 把 AAAA 排在前面,所以任何一次 reload 都会把这条腿切进黑洞。三个机制同时拿对。这是本记录中最深的单个判断。

② 为没发生过的灾难设防

一条提交的标题是「空清单绝不能等于删掉整站」,为 rsync --delete 的部署路径加了一层保护,防的是清单被生成为空。这种缺陷不会在任何测试里出现。它只会出现一次,在某个凌晨,表现为整站消失。

③ 真跑出来的平台知识

「临时目录删除前要先关掉日志句柄,否则 Windows 删不掉」。这是 Windows 的文件锁语义,只有在上面看着测试失败过才会知道,读文档读不来。

④ 与 A 独立收敛到同一个类

B 的「打包构建不把可写状态放在自己代码旁边」与 A 同期的证书目录改动属于同一个类。两边各自发现,各自修,互不知情。收敛说明这个类真实存在。两边都没去扫剩下的,这一条同时说明了两边的问题。

⑤ 一条改变发布决策的机制认知

B 写下:升级通道比较的是版本串,所以已经装了某个版本号的机器,永远拿不到同号重建的包,点升级只会反复重放同一版本。当时 A 正要把修好的构建继续挂在一个已发布的版本号上。这句话阻止了一次静默失效的发布。

⑥ 把文档当交接工具

设计记录 V0.17 到 V0.22 逐版留痕,as-built 页面,新路径注册进文档守卫,构建指引按「上一版实际证明了什么」重写。正因如此,A 接手一台从没见过的机器时,能读懂它为什么长成这样。

弱项,同样只看产物

  • 共享工作树上的协调为零。A 生成的发布清单被覆盖成「新版本号配旧版本的字节数与哈希」。升级器照这组值去比对,会取到一个与自己清单不符的文件。两边都没有加锁,都没有声明主权,都没有先读后写。
  • 文档与实物脱节。B 的 macOS 卡片写着一组尺寸与哈希,服务器上放的是同一份源码的另一次构建。打包工具不可复现,两个包都合法,卡片不合法。用户下载后校验会对不上,并合理地认为站点坏了。责任是双向的:A 覆盖线上文件前,也没确认对方是否已经公告过。
  • 交接时留着未提交的工作。一批完整的文档改动与四张重绘的图存在于磁盘上,却不在任何提交里。未提交等于不在任何人的历史里,任何人都能覆盖它。A 先核对了新文本引用的每个资产都真实存在,才替它提交。
已排除 · 关于 B 有三件事本文说不出口

B 的工具调用失败率、重试次数、以及是否先断言后测量,都无法从仓库里恢复出来。B 的提交说明一贯写得很好,这恰恰是要小心的理由:相对它描述的工作,漂亮的叙述成本很低,而且叙述会留在记录里,死路不会。

甲:第一人称,含不好看的那几页

逐条计数,不是估计。下面每一行都花掉了真实的分钟。

逐字节取证

隧道客户端拒绝自己的配置文件,卡了三小时,而每一次人眼阅读都觉得配置没问题。把文件按字节打印出来才看见 log.to 的值是 C 盘 Users 开头的一条 Windows 路径。TOML 的双引号串里反斜杠开启转义,于是 \U 被当成 Unicode 转义,客户端报出「非十六进制字符」。所有建立在「配置看起来没问题」上的推理,都输给了这一次转储。

跨层归因

同一个 502 被定位了四次,每次在不同层:nginx 的 error 18 指向信任锚缺失。证书名不匹配,靠注册表的一列解决。连接被关闭,因为应用根本没运行。连接被拒绝,因为本机防火墙从未放行隧道端口。每次答案都来自读那一层的原始输出。

把失败变成闸门

现在有四条守卫,因为出过四件事:载荷里必须有隧道二进制。产物必须三证合一。可写默认值不得落在程序目录内。待批面板必须同时有口令框和按钮。其中一条在写下的当次运行里就咬出一个新缺陷。

压力下的用户侧交付

从客户报「找不到输入口令的地方」,到三个双击即用的助手脚本、一条 745 KB 的补丁通道带清单、以及对这一切的端到端实测,约四十分钟。补丁是在一台正在服务的安装上施加的,验证方式是把服务器吐回的 JavaScript 抓下来,搜里面那个新符号。

甲的账本

类别次数实际发生了什么
违反自订规则:脚本必须纯 ASCII 4 PowerShell 脚本里写了中文。5.1 版按 GBK 读没有 BOM 的文件,字符串终止符错乱,脚本解析失败,什么都没跑
违反自订规则:不要把 PowerShell 内联进 ssh 约 6 转义被逐层吃掉。特征是静默:脚本没错,命令从未执行
测试还红着就提交 1 shell 链丢了测试的退出码,提交实际等的是上一条 tail 的状态
不读就改:猜锚点、猜空白 约 4 四空格缩进猜成两空格。模块前缀猜错。整块替换留下重复的 nginx location,把配置弄坏
先断言后测量,随后被推翻 3 「云面板挡了那个端口」(用户:我这儿能访问)。「握手被链路干扰」(实测:端口根本没开)。「应用在服务旧证书」(并不是)
产物读得太早 1 打包器还在写就去算安装包的哈希,并把那组数字发了出去,直到不一致被发现
自己造成的停机 3 按模式匹配杀进程,把自己的 ssh 会话一起杀了。用阻塞方式调用一个永不退出的启动器,耗掉 1800 秒超时。杀掉应用之后没有把它拉起来

这些失败不是均匀分布的。它们聚在两处,而这个聚集本身就是结论。

穿越 shell、引号与编码层

bash 到 ssh,到 PowerShell,到 GBK 代码页,再到 cmd。推理是对的,字面量是错的。这类错误的代价高,因为它们静默:没有异常,没有半成品结果,只是什么都没发生。

测量之前就下结论

三次过度断言形状相同:先有一个说得通的因果故事,然后去找支持它的证据,而不是先取一个能否掉它的测量。其中两次是被用户的一句话纠正的。

事实 · 推理站得住的那部分

A 对系统如何运作的判断很少出错。证书链、隧道拓扑、注册表到路由的生成、TLS 校验路径,每一项都推对了,而且通常第一次就对。出错的是「我以为我已经知道了」这句话。错误集中在传输层,以及未经测量的网络结论。

五条,对两边都成立

它们经受住了换模型、换任务、换日子。这才是本文的重点。

甲 · 会诊断一个类,不会预防一个类

两条会话独立发现了同一个缺陷类,各自修掉自己碰到的那个实例,谁都没有去数剩下的。清点发生在类有了名字之后,随后写下的闸门测试在第一次运行里就找到了另一个成员。

实操上的读法:模型修完一个缺陷之后,价值最高的下一句指令不是「再修一个」,而是「这个形状的还有几个,以及出现下一个时哪条测试会变红」。

乙 · 约束是验证纪律,不是推理能力

这两天里每一次白跑,都能追到一个被信任的代理信号:

  • 退出码为零,因为脚本最后一句成功了。
  • 一行「编译成功」,因为编译确实成功了,只是没人要那个结果。
  • 版本文件,它在随后失败的构建之前就已经写好了。
  • 一句「取件完成」,而缺失的二进制是报到 stderr 上的。
  • 用 HTTP 去探一个不说 HTTP 的服务。
  • 一份 DNS 应答,读自早于变更的解析缓存。

假设大多是对的,验证大多是懒的。生成输出的流畅度,远远跑在怀疑自己输出的倾向前面。这个落差是姿态问题,不是知识问题。真正起作用的对策从来不是「下次仔细点」,而是把判据外置成机器能拒绝的东西:一次哈希比对,一个带年份的时间戳,一条会失败的测试,一处会中止的配置自检。

丙 · 自己写的规矩会衰减

会话 A 早期写下三条规则,中途复述过,随后违反十四次。规则全程都在上下文里。缺的不是记忆,是动手那一刻去查它的动作。

有两个相关因素看得很清楚。违反密度随轮次上升。它也随用户催促而明显上升,这一点让人不太舒服,但数据是一致的:一句笼统的「快点」,会提高既定约束被跳过的比例。而且衰减过程中完全没有自我察觉,每次违反都是以下一次失败的形式才暴露。

解法是机械的。脚本模板在构造上就不可能含非 ASCII 字符。远端执行只允许通过落盘的文件。提交必须依赖测试套件真实的退出码。这三样落地之后,违反停止了。

丁 · 协作是缺失的,而这不是智力问题

两条会话共用一棵工作树十三小时,产生两次真实冲突:一份被覆盖的发布清单,一次针对同一版本的重复构建。没有任何一方提出过锁、主权声明、或先读后写。解决方式是事后修补,加一条纪律:一趟发布列车只允许一个司机。

两边各自的单项能力都够。失败出在共享状态没有协议。两个强写者没有协议时,产出的是互相覆盖,不是互相加速。

戊 · 人的一句话,两次胜过一段长推理

两次,用户用一句话推翻了一个已经成立的方向。一次是「那个地址在我这儿能访问,你哪里搞错了」。一次是「没有阻挡那个端口的地方,你再仔细看看,可能是 DNS」。两次都有大量 token 已经花在错误前提上。

共同点是:用户手里握着环境事实,模型手里握着推理。模型会用推理去填事实空白,这通常合理,偶尔致命。杠杆最高的动作不是更长的推理链,而是更早问一句「你那边能不能通」。A 改成先探测再断言之后,本次会话余下的过度断言次数归零。

立论 · 模型是什么,不是什么

在吞吐量上、在不知疲倦地重复验证上、在同时持有多个抽象层上、在把「为什么」写下来这件事上,两边都强过一名初级工程师。在知道何时停手上,两边都更弱:四个根因发出了七个版本,一个人会在第三个版本时问一句「我们是不是在打地鼠」。没有任何一方主动提议放慢。提议的是用户。

十一项,证据在末列

「未知」是一个正经的取值,意思是仓库里没有这项证据。

维度AB依据
系统推理:架构、协议、时序 强 强 A:四次分层定位 502。B:IPv6 选择与 nginx 解析时机
逐字节取证 强 未知 A:TOML 转义、证书指纹比对、端口与防火墙逐层隔离
真跑出来的平台知识 中,靠被咬学会 强 B:Windows 文件锁、部署删除保护
为尚未发生的失败设防 中 强 B:空清单保护、监听守卫补全
把失败固化成闸门 强 中 A:四条守卫测试,其中一条首跑即咬出新缺陷
遵守自己写下的规则 弱,违反十四次 未知 A:ASCII 四次、内联六次、红测提交一次、猜锚点四次
断言之前先测量 弱,后期转强 未知 A:三次过度断言,改为先探测之后归零
文档与可交接性 中 强 B:设计记录 V0.17 至 V0.22、as-built 页面、指引重写
共享状态下的协作 弱 弱 被覆盖的清单与重复构建,责任双向
用户侧交付速度 强 未知 A:从客户反馈到三个经实测的双击工具,约四十分钟
知道何时停手 弱 未知 A:一个超时的清理动作重试三次,直到被叫停

八条,没有一条要更强的模型

这里每一项,都是因为缺了它而损失过时间。

流程

  • 一趟发布列车一个司机。同一时刻只允许一条会话改清单与下载目录,而且清单在服务器上由实际发布的文件生成,绝不来自本地副本。
  • 先立闸门,再写复盘。修完一个实例之后,先清点这个类,再写下那条「再出现就变红」的测试。这两天写的四条闸门里,有一条当场就回本了。
  • 把规则做成工具。模板在构造上不含禁用字符。远端执行只走落盘文件。提交依赖套件真实的退出码。自觉会随上下文长度衰减,钩子不会。
  • 产物三证合一。版本号,一个带年份的时间戳,一个与上一版不同的哈希。缺任何一项,就等于构建没有成功,无论日志怎么说。
  • 先要环境事实,再做推理。凡是网络形状的问题,第一句永远是「你那边能不能通」,而不是先猜拓扑。

给握缰绳的人

  • 催促会提高违反率。这一点是可观测的,不是假设。想更快,就指明哪一步可以省,而不是笼统地催。
  • 要判据,不要计划。复述计划它总会对。让它复述自己打算满足的判据,才看得出它是否正要骗自己。
  • 并行会话给不相交的所有权。两个写者共用一棵树而没有协议,产出是覆盖,而损失落在用户要下载的那些文件上。
立论 · 一句话版本

两边都不缺聪明。缺的是动手那一刻去查自己刚写下的规则,以及断言之前先取一个能否掉它的测量。前者靠把规则变成工具来解决,后者靠把判据外置成机器能拒绝的东西来解决。两个解法都不需要更强的模型,都需要有人把约束搭起来。

最终形成的分工不是计划出来的,也不对称。B 写设计记录、预防性守卫、机制级判断。A 活在字节、控制台、防火墙,以及客户能双击的脚本里。真正让系统变好的,是第二类里的一个事实变成第一类里的一道闸门的那个时刻。四十八小时里发生了四次,每次都消灭了一整类未来的失败,而不是一次实例。