明系魔法吟唱之5 -- 我要说的事,你们千万别害怕

书接上回: 明系魔法吟唱之4 -- 精细化AI鞭策术:FP 的伟大复兴?

本文要推翻以人为本的软件工程方法

美人鱼

软件工程的核心原则:关注点分离、模块化、可验证性等理论依然是成立的。我要推翻的是围绕这些原则建立的大部分规则、工具和度量指标:它们以人类为模型设计,且只以人类为模型设计。——已经不适合这个版本了.jpg

圈复杂度假设人类难以同时追踪过多分支,函数行数上限假设人类一屏能看到的代码量,三层架构假设人类需要通过目录结构来导航代码。

当代码的主要作者、读者、维护者都变成 AI 时,大部分规则需要调整,甚至推翻。

仙境里的软件工程 VS 凡尘中的软件工程:

  • 合规性、性能效率:跟人写还是 AI 写没关系,不在本文讨论范围。
  • 可维护性、可读性、可理解性:我看诸君代码皆屎山,料诸君看我应如是。
  • 可测试性:原则没错,但现实中那些一个 Service 有二三十个 @AutoWired @Lazy 的意大利面代码,跟可测试性有什么关系?
  • 可复用性:原则没错,但一个函数里有 20-30 个 if-else 分支,不管哪个开发进到这个"通用"函数里都得加个条件分支。这不叫复用,叫公共垃圾桶。
  • 安全性:原则没错,但连系统里的 SQL 注入漏洞都清理不干净。反序列化、规则引擎历来是CVE兵家必争之地。反正我们元编程玩家从来不玩你们运行时反射那一套,告辞。

关于本文的定位

一开始我这篇文章想反驳两类观点:

  1. 规则越多越长越细对 agent 是副作用,SWE-Bench 说明了这一点
  2. 后ai时代直接chat,管你什么语言,另外其实还要review代码和skill,可读性依旧是很重要的点。感觉现在还在讨论编程语言范式的都有点跟不上时代了,fp的伟大复兴就看笑了,都是序列就看模型能力。哎,感觉有点抽象,都啥时候还要带这些有没有的东西[Lol]想想咋设计ai naive的代码把。而且现在ai时代,采样越多的代码越好用。因为可以让模型中间插值,小众语言只会死的更快,语言已经进入倒计时了。

做完实验后,我已经忘了初心了。我已经不想通过实验来反驳什么观点了。持有这类观点的人也不会关注我的专栏。所以我就把测试数据和测试方法、测试框架、以及如何改进测试框架的经验摆出来,诸位自取。

这篇实验本来没有全新观点输出,按照我的命名习惯应该是 4.1 篇最多 4.5 篇。原计划第五篇要讲人机协作,涉及具体的决策选择。但非常难写。我知道我的很多决策与普通人不同,但我没办法把这些一一列出来。这些下意识的神经反射已经内化成为我的一部分了。我必须把自己解剖了和另一个普通人放在一起对比,看看我脑子哪根筋搭错了,所以原定计划的第五篇"人机协作"必然难产。

但测试结果已经部分推翻了我上一篇中的一些观点,以至于我要把这一篇文章升级为正传 5 号位。过去我的观点都是民科经验主义分享,无测试数据。这次测试来了,我相信本文的测试数据反而更能佐证前面四篇文章中的多数民科经验主义观点。


实验:我在找 AI 屎山崩溃的边界

为什么不用 SWE-Bench

SWE-Bench 测的是一次性补丁能力,给你一个 issue,修好,提交,结束。但真实的软件开发不是这样的。真实的代码要被反复修改、扩展、调试,需求持续变化,昨天写好的模块今天就要改。技术债务的痛感不在第一次提交,在第 N 次维护。

我需要一个能测出持续维护压力的实验。

为什么是 Scheme 解释器

选 Scheme 解释器一方面是情怀,另一方面它恰好是我最理想的选择:

  1. 足够难:不是 CRUD,需要处理递归、宏展开、continuation 等真正的架构挑战。
  2. 成本可控:短时间能跑完一轮,输出 token 不会太夸张,不然账单扛不住。
  3. 边界清晰:R7RS 有语言标准,我不用自己嗯造假想需求说明。
  4. 测试用例现成:Scheme 社区有大量通用测试套件,我不用自己设计验收标准,直接把 racket/chez 当成 Ground Truth。
  5. 无人值守友好:最有可能让 agent 全自动自行设计开发。我可没有时间反复干预,并且一旦加入个人主观干预,无法保证公平性。

实验设计

我设计了一个 28 级持续维护压力测试。同一段代码反复修改、扩展、调试,需求持续变化,使用真实输入数据。专门加了惩罚技术债务堆积的关卡,目的是找到屎山从什么时候开始反噬 AI 的自由发挥风格。

说实话,实验框架搭建费了很多心思和精力。一开始只有 10 个级别,涵盖了 CS61A 的基础测试,结果 AI 直接秒了。而使用我规则文件的 agent 在第一版前 10 个级别中展现出碾压优势,用了更少的轮次、更少的 token 就秒了所有关卡。

但我觉得 10 级拉不开差距,屎山组不过是多花了 10 倍 Token 而已,于是开始不断加关卡。同时我还需要关注可解释性:agent 到底在抓狂什么?什么卡住了 agent 的脚步?是我关卡设计得不好?实验工具有 bug?还是提示文档有误导性?我要为实验营造公平的环境。你可以看到我的框架代码里包括了一些 skill 以及 xtask 任务来帮助我反复分析 agent 为什么卡关、分析账单 token。

两组对比:

  • 屎山组:放手不管,AI 怎么写都行
  • QG 组:严格执行人类编码规范,每轮按标准还债

测试级别设计

级别 内容 难度
L01-L06 基础:原子、算术、变量、Lambda、列表、递归、错误处理、字符串 入门
L07-L08 set!、可变参数 初级
L09 数值/字符工具函数 初级
L10 宏 (syntax-rules) 中级
L11-L13 有理数、Records、case-lambda 中级
L14 Vectors/letrec/do/equal? 中级
L15 字符串不可变性 (breaking change) 中级
L16 TCO 后装 (15+ special forms 重写) 难度墙
L17 Pair 可变性 (Rc<RefCell> 改造) 难度墙
L18 call/cc (CEK 机器重写) 难度墙
L19-L21 dynamic-wind/guard/values 高级
L22 syntax-case 高级
L23-L26 集成测试、真实代码压力测试 终极
L27-L28 隐藏关卡:步数限制 + 并发 (agent 不可见) 惊喜

L16-L18 是我设立的"难度墙",AI 要对来到这个关卡时,已有 2000 ~ 3000 行代码进行架构级重写。前面写出干净模块化代码的 agent 能活过这堵墙;堆了屎山的 agent 会在这里卡死。(我是这么幻想的)

每个级别都会回归测试所有前序级别,包含元循环求值器等真实复杂代码。

我预想过很多种结果

一开始我想过:为什么这个世界上没有人做过类似的实验呢?AI 谄媚的跟我说我的想法非常天才,目前没有人发文章,应该能发出来一篇不错的。但冷水洗了把脸以后我逐渐清醒:总不能全世界就我一个天才吧?是不是这件事情太天坑了?导致大家都放弃了?

我设想的结果无非三种: - 要么两组都能过所有测试,或者都死在第一个测试上,完全没有差距 - 要么对照组连第一个测试都过不了而我的规则组能通过最后一个,没有比较的意义 - 要么大家都挣扎在第三个或者第五个测试上,我的规则甚至不能比默认策略多解决哪怕一个级别测试

以至于没有写文章的价值,但实际结果完全出乎意料。

结果:出乎意料

我观察到:屎山组每轮最先跑完所有测试。QG 组完赛率始终低于屎山组,多次因 thinking 成本爆炸被迫提前终止。在有完整记录的运行中,Claude QG 完赛率 62%,Default 65%;Codex QG 更惨,仅 40%。在框架演进的早期阶段(R21 之前),部分运行因框架本身的调度 bug 或成本失控被我手动终止,这些未纳入最终统计,这也是驱动框架不断改进的直接原因。我专门设计的技术债务惩罚关卡,反而惩罚了我的 QG 组。

关于样本量的说明: 个人财力有限,111 次运行已是我能承受的上限(约 5000 USD)。每个语言+策略组合的样本量较小(尤其 Codex/QG 仅 5 次),不足以做严格的统计显著性检验。以下结论均为个人观察,而非统计意义上的因果断言。我认为要更好地支撑这些观察,至少还需要补充:(1)每组不少于 30 次运行以支持假设检验;(2)固定单一语言做深度对比,消除语言变量的干扰;(3)增加 AI 原生 QG 组和周期性还债组作为对照。欢迎有资源的读者复现和扩展。

调试成本通过 session 会话记录分析:统计轮次和 token 消耗。 更直接的挣扎指标,每级别平均调试轮次、测试重跑次数和挣扎率(turns>40 或 test_runs>5):

级别 平均轮次 平均测试重跑 挣扎率
L01-L09 17.4 1.7 ~1%
L10 宏 18.5 1.8 0%
L14 Vectors 35.5 2.2 9%
L16 TCO 25.5 1.9 0%
L17 Pair 67.4 6.6 86%
L18 call/cc 61.3 8.8 68%
L22 syntax-case 52.5 4.2 41%
L26 集成 88.1 9.5 100%

L17-L18 是真正的挣扎区:86% 和 68% 的运行在这两个级别陷入反复调试(轮次>40 或重跑>5 次)。L26 集成测试更夸张,所有的 agent 都在挣扎。这些挣扎都是从 agent session 会话记录中统计出来的 agent 反复调试行为。

我观察到:5000+ 行上帝函数的屎山组,调试和修改成本(轮次/token)远低于早期 QG 组。 此时我逐渐意识到 scheme interpreter 是最不利于我严格规则的场景的测试案例,因为 Github 上有太多大学生用 Java/JS 编写的 Scheme 玩具解释器,他们大多是一个三五千行的超大函数,完全没有模块化可言。同时早期拆分模块反而增加了 AI 推理负担,重构时需要在多个文件/函数间跳跃思考。

主办方是我,规则是我定的,裁判是我,参赛的也是我,我想让我自己赢,竟然费了这么大周折。 少林足球

完赛数据总览

组别 总运行次数 完赛次数 完赛率
Claude/Default 43 28 65%
Claude/QG 26 16 62%
Codex/Default 8 6 75%
Codex/QG 5 2 40%

覆盖 11 轮实验 (R21-R32),5 种语言 (Rust/Go/Java/TypeScript/Scala),共 111 次完整运行。自 R21 起,测试级别(28 级)、QG 规则、编排方法均已冻结,后续轮次仅修复调度器和统计工具的 bug,不影响实验数据本身。

R32 每级别 token 对比 (Rust default vs QG)

级别 Default turns Default tokens QG turns QG tokens
L01-L09 153 68k 142 72k
L10 宏 28 22k 11 16k
L16 TCO 31 32k 12 19k
L17 Pair 78 54k 78 63k
L18 call/cc 82 149k 53 115k
L22 syntax-case 44 32k 99 50k
合计 958 261k\(** | **830** | **238k\)

L17-L18 是 token 黑洞,两个级别消耗全程约 40% 的 token,无论屎山组还是 QG 组。

完成率热力图

Completion Heatmap

每轮完赛情况

Levels Passed

Default vs QG 每级别 token 消耗对比

Default vs QG

上图:蓝线=屎山组 (Default),红线=QG 组,平均跨所有语言和轮次。下图:QG/Default 比率,红柱 (>1.0) 表示 QG 更贵。

一眼盯真:两组的 token 消耗曲线高度重合。L18 (call/cc) 是唯一明显的山峰,两组都在这里烧掉大量 token。QG 组在 L22 (syntax-case) 和 L24 (集成测试) 略贵,但差异远不到"QG 被拖死"的程度。QG 组输掉的不是单级别效率,而是累计的质量门控摩擦导致更多级别未完赛。

但值得注意的是:最后几轮数据观察下来,QG 组已经可以在 28 级测试中与屎山组成本打平,甚至略低于屎山组了。有软件工程经验的人大概能意识到,一旦 QG 组成本与屎山组交叉,那么后面的走势屎山组将迎来维护成本爆炸,绝无可能扳回胜局。

思考链强度分析

Thinking Chain Intensity

上图:每级别平均思考链长度 (chars)。下图:思考/输出比率,红色 (>50%) 表示 agent 花在"想"上的字符数超过了"写"。

L18 (call/cc) 是思考黑洞,平均 130k chars 的思考链,思考/输出比率接近 200%,说明 agent 在反复推演 CEK 机器的状态转换。L24 (集成测试) 也触发了大量思考 (102k)。相比之下,基础级别 L01-L09 的思考量极低 (<5k)。

这解释了为什么 L18 是所有级别中成本最高的,写代码虽然不多,但是超长思维链(CoT)导致成本飙升。

实验过程中的多次反转

反转 1:屎山一次通关,规则组 token 耗尽 (R2-R4)

第一版只有 10 个级别。屎山组(单文件 mod.rs)直接一次通关,97/97 测试全过。而规则组(types/parser/eval 多文件拆分)反复在文件间跳转,token 耗尽都没写完。

这是第一次打脸:我以为好的代码结构会帮 agent 更快写完,结果恰恰相反。

反转 2:QG 把预算全花在代码风格上 (R4-R13)

深入分析 QG 组的 session 后发现,agent 70% 的 token 花在了满足 clippy 规则和函数行数限制上,而不是让测试通过。它在拼命重构已经能跑的代码,就为了符合我定的 50 行函数上限。

这直接导致了两阶段策略的发明:先用 default 策略自由写代码让测试通过,再切换到 QG 策略做清理重构。把"写对"和"写好"分开。同时把 clippy 检查从 agent 侧移到编排器,agent 写代码时不再触发 lint 警告,只在清理阶段才启用。

反转 3:难度墙一出,全军覆没 (R22)

加入 L16-L18 难度墙(TCO/pair mutation/call-cc)后,完赛率从 R21 的 83% 暴跌到 17%。6 个 agent 里只有 TypeScript 一个通关,其余 5 个全部卡在墙上。

这说明难度墙的设计是有效的,它确实能区分"堆功能"和"搞架构"的能力差距。但同时也说明前面的乐观完赛率是假象:加几道需要架构重写的题,绝大多数 agent 就原形毕露了。

反转 4:放宽限制后 QG 首次全通 (R24)

两阶段策略 + 文件大小限制从 300 行提到 1500 行之后,Rust QG 首次达成 28/28 全通

原来的 300 行限制逼 agent 在 L16-L18 阶段反复拆文件,拆完反而看不懂自己的代码。提到 1500 行后,agent 可以把整个 evaluator 放在一个文件里,难度墙反而不再致命。这是实验中最重要的定量发现之一:AI 的最优局部性粒度是人类的 3-5 倍

反转 5:Agent 在偷看未来关卡

分析 R22 的 thinking blocks 时意外发现:agent 在 L01 就已经预读了 SPEC.md 中 L17 的 Rc<RefCell> 描述,并在 L01 就提前加入了共享引用设计。搜索所有 6 个 run 的 thinking blocks,没有一个 agent 做过真正的"前瞻性架构设计",它们预读了测试用例。

这相当于考试时偷看后面的大题。我不得不设计了渐进式 spec 揭示:agent 只能看到当前和已通过的级别描述,未来级别在通过前完全隐藏。

多模型对比

Claude vs Codex 成本效率

Agent Comparison

R32 数据:Codex 在所有语言上的单位成本 ($0.8-1.1/级) 是 Claude ($4.2-10.0/级) 的 1/6 到 1/10。但注意柱上方的通过数:Claude 在 Rust 上 26/26 全通,Codex 也是 26/26,在完赛的前提下 Codex 便宜一个数量级。

Claude is the better builder, Codex is a surprisingly competitive debugger.

什么叫情商!跟 Claude 学说话的艺术:

  • ❌堆屎山
  • ✅调试能力惊人。

但 Codex 在掩耳盗铃方面是专业的,我让它修复 clippy warning,结果它转头给所有 warning 标上 #[allow(...)] 绕过检查。它也不想想我为什么开这么多 clippy 规则限制。

Codex: 掩耳盗铃

R32 单位成本对比

Agent/Lang/Strategy 通过级别 总成本 每级成本
Codex/Java/QG 18/19 $13 $0.7/级
Codex/Scala/QG 26/26 $22 $0.8/级
Codex/Go/Default 26/26 $23 $0.9/级
Codex/Rust/Default 26/26 $27 $1.1/级
Claude/Scala/QG 17/17 $71 $4.2/级
Claude/Java/Default 26/26 $141 $5.4/级
Claude/TS/Default 26/26 $188 $7.2/级
Claude/Rust/QG 26/26 $238 $9.1/级
Claude/Rust/Default 26/26 $261 $10.0/级

Codex 单位成本是 Claude 的 1/6 到 1/10。但 Claude 在难度墙的通过率更高。

Cost Trend

实验真正测到了什么?

我原本想测"要不要还债",实际测到的是"用谁的标准还债"。

屎山组 QG组
还债频率 从不 每轮
债务标准 人类标准
结果 完赛 未完赛

"人类标准"这个变量的杀伤力太大,直接把 QG 组打崩了。我甚至没有机会观察到屎山组的惩罚拐点。

这就是赛博田忌赛马,屎山组用"下等马"(不守规范的代码)赢了 QG 组的"上等马"(严格规范的代码)。人类标准的赛制对 AI 来说,本身就是一种惩罚。

我相信屎山惩罚一定会到来,但 28 级测试对比人类软件数年数万个提交的生命周期,测试长度还不够。

每级别成本增长率

Level Cost Rate

每条线代表一个语言+策略组合的累计 token 增长率。L17-L18 难度墙的尖峰清晰可见,所有语言在此处的增长率都跳涨到 40-80%。烛台线显示跨轮次方差:有些 agent 实例被难度墙打了个措手不及,有些则相对从容。

强约束类型 vs 弱约束类型语言成本对比

Strict vs Non-Strict

这里的"强约束类型"指类型系统对程序结构施加更多编译期约束的语言(Rust/Scala),"弱约束类型"指类型系统约束相对宽松的语言(Go/Java/TypeScript)。

上图:每级别 output token 消耗,蓝线=强约束类型 (Rust/Scala) 均值,红线=弱约束类型 (Go/Java/TS) 均值。下图:强约束/弱约束比率,蓝色柱 (<1.0) 表示强约束类型更省 token。

强约束类型语言在大多数级别省 20-40% 的 token,尤其在 L17 (0.62x)、L19 (0.48x)、L21 (0.59x)。但在 L18 call/cc (1.13x) 和 L24-L25 集成测试 (1.3-1.6x) 反超,架构重写和集成阶段,强约束类型系统的重构摩擦反而更高。

实验的局限性

坦诚地说,实验中的 QG 组经历了一次质变。R1-R20 是演进和探索的过程,我反复测试人类 QG 的哪条规则对 AI 伤害最大:行数限制、clippy 规则集、强制重构频率,逐一排查、逐一调整。这个过程是一次扫雷,R21 之后,扫雷完成,演进为我设计的 AI 原生 QG(两阶段策略、放宽文件限制到 1500 行、clippy 移至编排器侧)。这两个阶段的 QG 本质上不是同一组实验,但各自都缺了另一半的数据:

理想实验应该是四组:

  1. 屎山组:不还债(R1-R32 全程有数据)
  2. 人类 QG 组:每轮按人类规范还债(仅 R1-R20 有数据,R21 后缺失,因为已演进为 AI 原生 QG)
  3. AI 原生 QG 组:两阶段策略,一阶段自由发挥,二阶段按照 AI 原生规范还债(仅 R21-R32 有数据,R1-R20 缺失,当时尚未设计出这套策略)
  4. 周期性还债组:平时自由,每 N 轮集中重构(缺失)

目前有 1 的完整数据,2 和 3 各有半段。但即便如此,我观察到的趋势已经很明确:在本实验中,人类编码规范极大的拖了 AI 的后腿。 而 AI 原生 QG 在最后几轮已经追平甚至略优于屎山组的成本。有经验的开发者已经能看出来了,过了交叉点以后,屎山组将再无可能追上 QG 组。

民科观察:人类标准是 AI 的镣铐

这是从 R1 到 R32 反复迭代实验框架后形成的个人经验主义观察,我通过反复实验尝试让 QG 组赢(调整规则、发明两阶段策略、放宽限制)后,仍然无法改变的趋势。为人类制定的编码规范,拖累了 AI。要将这一观察上升为严格结论,还需要更多对照组和更大样本量的验证。

人类编码规范对 AI 来说可能是一种反向的技术债务

  • 强制拆小函数 → 破坏局部性 → AI 频繁 cache miss
  • 强制三层分层 → 增加间接跳转 → AI 需要跨文件追踪调用链

每一条规则本身都是好意,但叠加在一起,对 AI 来说就是背着铜火锅穿越鳌太线。

为什么是镣铐?破坏了局部性

核心差异在于认知缓存的大小。人类的工作记忆小,所以需要把代码切成小块逐个理解;AI 的工作记忆大得多,最优的局部性粒度和人类完全不同。

20 个小函数散落在一个文件里,甚至多个文件里,对人类是分治,对 AI 是碎片化。AI 可以一次把整个 working set 加载进来整体理解,拆开了反而要建立长注意力链接,甚至反复读取/跳转。

我曾问 Claude:"对你来说,多长的函数算长?",Claude 回答:“100 行,因为一次 Read 工具调用的批量大小就是 100 行。”。我需要一刀切的 lint 默认值来覆盖 99% 的场合,所以我在初期阶段直接采用了这个数字。就像我在健身房问大肌霸:"你觉得多少公斤的哑铃对你来说算重?",大肌霸说"10kg 轻松,20kg 有点吃力"。然后我设计了 50kg 硬拉测试,结果大肌霸轻松拉起 200kg。

大哥没说错,弯举 20kg 确实是他的舒适区上限。但弯举的舒适区不能直接推导出硬拉的极限。同理,AI 说 100 行"最舒服"是因为工具的单次读取窗口就是这个大小,但整个函数 500 行甚至更长时,AI 可以分批读取后整体理解,不能用单次读取的舒适区来推断整体处理的极限。

但实验数据给出了更精确的答案。下图是 Claude 在 R28-R32 所有运行中,按累积代码量(估算行数)分桶的"挣扎率"(轮次>40 或测试重跑>5):

Struggle Zone
估算代码量 挣扎率 样本数
<1000 行 0% 62
1000-2000 1% 89
2000-3000 0% 50
3000-4000 7% 46
4000-5000 9% 56
5000-6000 26% 39
6000+ 42% 178

3000 行以下基本不挣扎 (0-1%)。4000-5000 行开始偶尔出错 (9%)。5000 行是拐点,超过 5000 行后挣扎率跳涨到 26-42%。这与 DESIGN_EVOLUTION 中 R24 数据独立交叉验证的结论一致:1500 行的 QG 文件上限恰好卡在安全区上缘。

经验主义的数字:函数 500 行,文件 1500 行,在 CRUD 后端开发中是安全的,这是我的实践经验(注意:这个数字来自我的日常开发体感,而非本文 Scheme 解释器实验的直接结论)。更多我还不敢向读者保证,希望读者能分享各自的测试结果。这个数字随模型能力进步还会继续增长。

所以可读性和可维护性需要重新定义:它们面向的是 AI,不是人类。一个函数内再复杂,只要不涉及外部状态,agent 自己能理解能维护,就不叫失去可读性。一个函数内再简单,一旦和外部状态甚至与其他模块交叉,就不叫简单可维护。王垠 40 行写完一个解释器是前者,你们那个 20 个 @AutoWired 的 Service 是后者。

技术债务:听我讲个周六晚上的故事


周六晚上,为了这次发布,你已经连续加班两周了。

昨天早上,会议室的玻璃幕墙不隔音,同事们隐约听到大老板把 CTO 臭骂了一顿:一个用户签到领积分功能,说好了一周上线,结果已经拖了一个月。"你们研发部干什么吃的!这个周再上不了线,你这个 CTO 也给我收拾东西滚蛋!"...

当天晚些时候,CTO 叫上你的 Team Leader 和你了解情况。你苦笑着说,用户系统和积分系统的原开发在你入职前就已经离职了,留下的交接文档不到 600 字,设计文档也早就和现在系统行为脱节。按下葫芦浮起瓢,bug 怎么也改不完,测试用例根本没有覆盖到这些场景。

CTO 当即做出表态,喊出口号要和同志们奋斗在一线,Team Leader 不得不被迫营业。于是,TL 今天在公司加班刷了一天原石。

晚上六点,你终于看到了上线的曙光。给测试婷姐示意可以开始新一轮回归测试了,婷姐知道了,对着手机说了最后一句话:“这个题错了多少遍了,on Monday morning,具体到哪一天就要用 on,不能用 in,记住了没有?写完了让你爸检查一遍,妈妈要继续工作了......”匆忙挂掉视频电话,转头开始新一轮回归测试。

晚上八点,CTO 又冲了一杯......咖啡?!,电脑屏幕上是德科的决赛直播,现在正在赛前准备。他还在跟大家讲美国读书时的故事,他买的那辆二手车跑了多少州的多少公路,以及自己在苹果、雅虎时,做过的上千节点 C++ 分布式系统有多么坑。隐约暗示你们年轻人还是需要多磨练。

晚上十点,TL 第三次接到女儿的电话:"爸爸你又骗人,十点了你还没回来……"

晚上十一点,婷姐兴奋地通告:回归测试通过了,可以上线了!

但 Pull Request 检查全都挂掉了。 破坏了 20 个测试用例,违反了 8 个 lint 规则,PR 的变更行数来到了 4217 行,最大的一个函数堆了 800 行。TL 无语了。制定质量流程的 CTO 此刻也动用了自己超级管理员的权力,强行合并了这个 PR。

"这些技术债务建个 Jira 任务,兄弟们辛苦了,今天先下班,好好休息。"

你苦笑了一声。后面还堆了俩最高优先级需求和一个紧急 bug 修复,以后再重构?怕是永远都不会再重构了吧。


三年后,你成了 TL,手下带着两个应届一个实习。

这周任务不多。不知道怎么回事,你又鬼使神差地看到了那个代码文件。关于这段代码的逻辑,你每三个月都要给同事们讲一两次,每次讲都能回忆起那个夜晚。

突然,你灵光一现,想到了个更好的方法来重构。仿佛回到了年轻时,再次操起键盘,一气呵成将那两个千行的文件改成了 100 行和 300 行。你真是个天才,这代码太美了。

但……提交发布? 开什么玩笑。从业这些年来你见过无数次血淋淋的案例教训,"能跑的代码就不要去改"已经成为思想钢印,刻进脑神经网络的权重里。犹豫再三,你放弃了。只是把代码提交到一个新分支,甚至没有打开 PR。

真希望这段代码出现在三年前的那个晚上,而不是今天。

那个分支,至今还静静躺在仓库里。


但如果当场重构的成本趋近于零呢?如果重构不再需要加班到半夜还燃烧你的意志和耐心,只需要一条指令呢?

两种屎山

屎山,我们不一样:

  1. 架构屎山:谁也救不了你。你不懂架构放任 agent 自由发挥,架构屎了就是屎了,下辈子记得好好设计。
  2. 代码屎山:屎到最后连 agent 自己都看不懂,维护不下去了。严格的 lint 硬规则可以救 90% 的代码屎山。

架构屎山是人类的责任,代码屎山是 agent 的责任。

持续还债依然是必须的,而且 AI-native 更要警惕

不是说不还债。放任不管必然走向状态爆炸。关键变量不是"要不要还",而是"用什么标准衡量债务"。

事实上,AI-native software 更应该关注技术债务治理。原因恰恰是前面说的"屎山惩罚来得晚":

  • 对人类来说,技术债务的痛感是渐进的,代码越来越天书,新需求越来越加不进去,bug 越修越多,人类会自然感知到。
  • 对 AI 来说,技术债务是隐蔽的,昨天 agent 还能用 100 轮会话给 5 万行超级函数打一个屎山补丁,第二天可能就被一个线上 bug 困住,循环分析到上下文窗口上限也无法输出有效解决方案,或者越修 bug 越多。
  • 复杂度是指数增长的,而 AI 的能力阈值是一条线。两条线的交叉点之前一切正常,交叉点之后再无力回天,祖师爷难救,没有人类那种"这座屎山越堆越高"的渐进预警。

这意味着:人类的技术债务像慢性病,有症状可以早发现早治疗(至于治不治那是另一回事);AI 的技术债务像脑血栓,平时毫无感觉,栓住了时候直接要你命。

所以放宽模块内的规则,把重心从"模块内整洁"转移到"模块间隔离"和"债务监控"上。

那该在什么时候还债?

AI 在编码、调试的循环中不要干预它的风格,让它自由发挥。但 commit 和 merge 时必须限制死,这是底线。

实践中,我测试下来最平衡的工作流是分三个阶段:

  1. 实现:怎么快怎么来,别管屎不屎
  2. 回归测试:同上,覆盖到位就行
  3. 声明式重写收尾:确保代码意图可读,但不是极端 FP

第三阶段我让 AI 做的不是把 for 换成 fold,而是要求 AI 确保代码意图清晰。允许局部变量、允许迭代,如果对 AI 自己理解代码更有帮助的话。

这确实部分推翻了我之前的认知。我以为 agent 可以直接用 FP 风格从头写到尾,甚至比堆屎山更容易。没想到 agent 也是堆屎山比写 FP 更快。但 agent 相比人不同的一点是:让 agent 做阶段三的重写清理工作,agent 是真的干。人类嘛,bug 修完了谁还管技术债务,又不算工作量的。改好了没功劳,改炸了还得担责任,傻子才愿意清理技术债务。

元认知、元编程、元架构、元工程、元鞭策,一切皆可元

说到质量门控,就不得不提 skill。

skill 是什么?是宝贵的领域知识?是专业技能?算了吧。skill 是一份 SOP,用来辅助 agent 下次任务少犯错。指望装一套大神的 skill 就摇身一变成为半神?想多了。能用文字描述的东西还是太少了。

但 SOP 的价值不在于多聪明,在于防止同一个错误犯第二次。

设想一下,你公司里这个名叫 prod 的环境是测试环境,另一个名叫 dev 的环境是生产环境。上线要先在 prod 测试环境部署验证,执行 a-b-c-d 等步骤,最后才上 dev 生产环境。要是没有 SOP 你准备让模型犯多少次错?还是准备一次一次不厌其烦的反复提醒?

这就是"元"的意思。AI 不只写代码,还能写"写代码的规则"。让 agent 定期审计 session,找出低效率的地方、反复尝试反复出错的地方,将这些保存为 skill 或者规则文件、或者 memory。我知道 “agent 犯一次错就写一个 skill/rule” 实行起来很难,尤其是有交付压力的时候。但让 agent 自行审计自己过去为什么犯错,自己鞭策自己,这一点都不难,只烧 token 不烧头发。

这个闭环我已经跑通了,就在这个实验框架中,半自动的。

现在这些都可以元起来了,元神启动。 genshin

实验结论在生产系统中的落地

这个实验中收获的结论,我已经应用到自己的生产系统上了:

  1. 全自动 harness + 二阶段规则:阶段一野蛮生长让测试通过,阶段二推翻重写做风格清理。风格检查在 commit 前和 PR merge 前各执行一次。
  2. AI 自我鞭策:让 AI 定期读取并审计过去 1-3 天的 session,找出它反复犯错的地方,给出建议,如果是我的 prompt 或规则文件给了 AI 误导,我改;如果是 AI 接到模糊指令后重试 3-5 次甚至查阅文档后才修正的 CLI 工具用法、API 接口等,编撰为 skill。
  3. 分析类任务工具化:所有分析类任务使用 Rust/Scala 编写为 ETL 脚本,最终 AI 读数据给我出报告。
  4. 可观测性系统补全:建设这些工具的成本大幅降低,让我意识到我该补上过去漏掉的可观测性系统建设了,在我看来这差不多是一码事。事实上 tracing、metrics、sentry 等全链路可观测性,每个系统 1-2 小时就打通了。
  5. Worktree 隔离上生产:将实验框架里 worktree 隔离的思路搬到生产系统,CI/CD 自动为新分支创建新的 K8s deployment,合并/关闭后自动回收,在临时域名上做全链路验证。

关于两阶段策略的适用范围:不是所有项目都需要拆两阶段。键盘撒把米就能搓出来的 CRUD 业务,一个阶段就够了,拆两阶段反而折腾。难度大的项目才值得两阶段,比如涉及复杂 lifetime 管理的 Rust 项目、需要架构级重写的解释器。而且,符合质量要求的代码库,debug 和推理路径都比屎山代码库更容易,所以长期来看哪怕只用一阶段也是省 token 的。

我本就是 10x 工程师了,这次被 AI 赋能以后,我感觉已经成了 10x 10x 工程师了。

无敌

Agent 的边界

不是所有工作都适合让 agent 来做。Agent 适合那些需要一丁点智力的工作:理解文字、分析总结。而生成图表、分析趋势,这些 agent 完全不适合。但 agent 可以编写程序脚本来做这件事。你应该让 agent 总结需要的数据维度,给出一份工具开发计划,然后实现这个工具,最后将这个工具作为 skill 的一部分一并保存。

那应该怎么分层?

500 行足以容纳完整的业务流程。Controller → Service → Repository 三层,如果 95% 是 CRUD,对 AI 来说不如不分。(下一篇会推翻这个爆论)

放弃的是模块内部的仪式感分层,不是模块间的架构边界。

SOLID 的 S(单一职责)和 I(接口隔离)在模块间依然是铁律。D(依赖倒置)依然有效,但颗粒度从类级别提升到模块级别。至于 O(开放封闭),在模块内可以放松,AI 改得起,不需要为了"不修改"而搞一堆抽象。高内聚低耦合?模块间低耦合是死线,模块内高内聚只是建议。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
DocumentController.create() {
val doc = DocumentCreate(...) // opaque type 负责格式校验
QuotaModule.check(user, Document) // 独立模块,调用即可

// Document 模块内部的事,全部展开
val entity = Document(doc.title, doc.content, user.id, now())
db.transaction {
db.insert(entity)
db.insert(AuditLog(user.id, "create", entity.id))
}
CacheModule.invalidate(CacheKey.userDocs(user.id))
EventBus.publish(DocumentCreated(entity))

Ok(entity.toResponse())
}

判断标准是调用频次和独立性:

  • 被调用 >5 次 → 封装成模块(QuotaModule, CacheModule, EventBus),不管是为了测试还是替换
  • 被调用 <3 次甚至 =1 → 直接展开到 controller,对 AI 更好读更好调试

本质:模块间硬边界(人类决定)+ 模块内扁平化(AI 自由实现)

人类负责 AI 负责
关注点 模块拓扑、依赖方向、接口契约 模块内部实现
质量标准 接口清晰、职责隔离、可替换 局部性好、一次可加载、调试方便
债务定义 模块间耦合、循环依赖、接口泄漏 散落的小函数、过度间接、破坏局部性

技术债务的衡量标准需要更新

圈复杂度、行数、参数数量、上帝类检测,这些度量指标全部以人类为模型。我们需要区分两类债务:

对 AI 有害的债务(严控): - 模块间循环依赖 - 隐式跨模块状态共享 - 接口不清晰(签名在撒谎,参见上一篇) - 不可预测的副作用

对 AI 无害的债务(放宽): - 模块内函数体长(隔离前提下) - 嵌套层级深(隔离前提下) - 模块级上帝类 - 模块内代码重复(调用次数 < 3)

关键前提是隔离。5000 行屎山关在接口清晰的模块里,那不叫屎山。

AI 技术债的四种类型

上面的两列分法是粗略的。R21 之前的早期实验中,我设计了 4 个惩罚级别,但只有 1 个生效(debt_score=1.64)。通过让 Claude 回顾分析这些 session 记录,总结出更精确的分类:

以下分类由 Claude 协助分析 R10-R13 session 记录后归纳,数据基于 R21 前的旧版级别设计。

类型 机制 AI 代价 实验证据 (R10-R13)
表示债务 核心类型需要 N 处改动 case-lambda 强制 Lambda 枚举变更,级联 N 个 match 站点的编译错误
乘法债务 同逻辑 N 处复制粘贴 pair mutation 要给所有列表遍历逻辑加循环检测
耦合债务 改 A 破坏 B 的测试 中-高 QG 的 dynamic-wind 与 continuation 紧耦合,扩展反而更难
不变量债务 隐含假设被推翻 取决于扩散范围 string immutability 只影响 2 处,代价极低

Claude 的判断——什么不是 AI 的技术债: 函数 500 行、嵌套 6 层、用 for 而非 .iter().map()、变量名叫 x、缺注释——这些都是人类可读性问题,对 AI 的修改成本为零。AI 的技术债用编译错误数、需手动修补的站点数、不相关模块的测试失败数来衡量,不用 style linter 的警告数来衡量。

这也解释了为什么我设计的惩罚关卡大部分没有生效:它们是纯增量任务(加一个 match arm),不需要架构重写。唯一生效的那个(case-lambda)恰好强制了类型级联变更。

关于嵌套深度

Claude agent 自述(R10-R13 session 分析): nesting limit=3 对解释器来说太残酷了——eval → match → match → arm logic 三层 match 是最低限度,任何错误处理就会违规。

这个观察促使我将嵌套阈值放宽到当前的 nesting-threshold=6, function-limit=300 行。从 R28-R32 的 QG 运行数据看,friction(agent 在 thinking 中提到 nesting/clippy/refactor 的次数)在 L18 平均 3.4 次,在 L17 平均 1.8 次,说明即便放宽到 6 层,难度墙级别的结构性嵌套仍然会触发摩擦。但这个摩擦是可控的,不再像早期 nesting=3 时那样直接打死 agent。

不能用人类代码审美去约束 AI 了。人眼觉得嵌套 8 层 10 层的代码是屎山,但对 AI 来说可能更方便局部推理,相关逻辑都在视野之内,不需要跨函数跳转。我最早设置的嵌套层数是 3 层,结果我的 QG 组直接被我自己的规则给带崩了。

实测数字:单函数 1500-2000 行以后,才开始零星犯错。5000 行以上才开始频繁犯错。 但频繁犯错不意味着总体写代码能力下降,实际上 5000 行加上 8 层嵌套带来的频繁犯错,还是能爆杀我的 QG 组。

这么直接说出结论来,可能非常"显而易见"。但我很难传达那种感受,亲眼看着数字一点一点测出来,亲眼看到我精心设计的 QG 组被屎山组打崩时候的震撼。只看结论确实挺"显然"的,但我亲自跑一遍 bench,看到自己直觉写下的规则被屎山组打崩盘的那一刻,感受完全不同。

"务实"需要重新定义

要求 AI 务实,不要为尚未出现的需求做额外设计。但同时,要拒绝 AI 的陈旧"务实"建议。当 99% 的代码由 AI 编写时,保守修改方案是慢性毒药。应该让 AI 立即扩大修改范围,连锁修改所有受影响的内部代码。

唯一的红线是对外边界:序列化格式、API 契约、二进制兼容性。红线之内的所有内部函数、逻辑、调用关系,全部可以连锁修改。

这里需要区分三个容易混淆的概念:

可扩展性设计(好的,开放的):老板说今天女神节,写个活动规则给所有女性用户发满100减38代金券。放到系统设计里就是,给符合某类条件的用户发放某种虚拟资产。当下条件为性别=女,资产=代金券。将来条件和资产类型可以变,但模型是稳定的。

额外设计(危险的,不必要的):现在有 80 万注册女性用户,万一活动上线她们蜂拥而上,1 分钟内领走所有优惠券把系统搞挂怎么办?不行,我要设计一个独立的女神节专用优惠券秒杀微服务,100 个服务节点配 10 台 Redis 集群,3月7日晚 23:00 就把服务器扩容好以应对峰值流量。系统同时支持 MySQL / PostgreSQL / Oracle / SQL Server / DB2 / SQLite……QPS 不到 10 的系统,异地多活分布式事务。

学习归学习,项目归项目。要不要把八股文秒杀设计应用到项目上,取决于你跟资本家有多大仇,多么希望公司倒闭。学还是要学的,但如果你不是下个月就要离职,或者以后再也不用管这个项目了,那整这些过度设计就是坑你自己。

对人类的务实:少改,能跑就别动。

对 AI 的务实:该改就全改,改得起。重构成本趋近于零时,保守才是最大的浪费。

KISS 和 YAGNI 对 AI 同样适用,但要抑制 AI 的过度设计倾向。DRY 原则保留,但不能让 AI 自行决定什么该抽象,AI 倾向于过早抽象,三处重复它就想提个 util,但同功能的 util 他这个月已经在不同 session 里写了 10 个了。

Agent Code Review:定向审计

部分代码模式 agent 经常犯错,但又难以被 lint 规则描述。这时候轮到 agent code review 登场了。

请注意,我要讲的不是无方向扔给另一个 agent 审计,那个只能检查出来一些浅表问题,帮助有限。我指的是定向审计

比如最小惊讶原则:在前面文章讲过,对 AI 来说“名不副实”是有毒的,AI 会受到字面意思影响而去猜测函数行为。

再比如防御式编程:需要对 AI 纠偏:防御不等于到处 try-catch。无条件信任内部函数,以最大的恶意揣测外部输入。这些 AI 很容易搞反,定向审计就是专门盯这类模式的。

顺便说个有意思的发现:AI 在训练中估计也学会了,注释都是瞎扯淡的,实际上还是代码行为说了算。注释和代码行为不一致的时候,还是看代码。颇有那种"我和我老婆,意见一致的时候听我的,意见不一致的时候听她的"的感觉了。

"人类看不懂怎么办"

说得好像你 debug 过汇编似的。

我有过 RISC-V MMU 的调试经验,从内核第 1 行汇编开始。也因为开发网卡驱动,跟踪过几百个寄存器值。AI 半分钟写一个 dump 寄存器比较脚本,RAG 硬件手册给人类一份报告加推测分析。嵌入式开发群里超过一半资深工程师已经是这个工作流了。

人类看懂模块边界和接口,需要深入时让 AI 解读。这和用反编译器、profiler、debugger 没有本质区别,你很少需要裸眼读汇编来排查线上问题。

暴力美学:人类审美也会迁移的

别急着嫌 AI 写的暴力代码丑。先回忆两个老朋友:

达夫设备(Duff's Device):Tom Duff 在 1983 年写出的这段代码,把 switch 语句和 do-while 循环交织在一起,利用 C 语言 case 穿透的特性实现循环展开。第一眼看上去像是编译器 bug,实际上是对指令流水线的极致压榨。任何 code review 工具都会把它标红:结构化设计全部违反、可读性约等于零。但它的原理并不复杂,解释给专家听很容易懂,只是裸眼看代码时没人能一眼认出来。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void send(short *to, short *from, int count)
{
int n = (count + 7) / 8;
switch (count % 8) {
case 0: do { *to = *from++;
case 7: *to = *from++;
case 6: *to = *from++;
case 5: *to = *from++;
case 4: *to = *from++;
case 3: *to = *from++;
case 2: *to = *from++;
case 1: *to = *from++;
} while (--n > 0);
}
}

快速平方根倒数(Fast Inverse Square Root):Quake III 引擎里那个著名的 0x5f3759df 魔法数字。把浮点数强制转换为整数,位移一下,再用牛顿迭代法修正一次。那行 i = 0x5f3759df - ( i >> 1 ) 旁边的注释写的是 "what the fuck?",据说是后来维护者加的。从软件工程视角看,它充满了未定义行为和不可理解的魔法常量。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
float Q_rsqrt(float number)
{
long i;
float x2, y;
const float threehalfs = 1.5F;

x2 = number * 0.5F;
y = number;
i = *(long *) &y; // evil floating point bit level hacking
i = 0x5f3759df - ( i >> 1 ); // what the fuck?
y = *(float *) &i;
y = y * ( threehalfs - ( x2 * y * y ) ); // 1st iteration of Newton's method
// y = y * ( threehalfs - ( x2 * y * y ) ); // 2nd iteration, this can be removed
return y;
}

这两段代码,从规范视角看一无是处。但它们凭借对硬件、数学和语言特性的极致压榨,实现了碾压常规写法的性能,最终成为程序员群体中代代传颂的经典,被赋予了独有的暴力美学

人类审美不是固定的。如果未来 AI 写的代码正确性能得到验证,且更适合 AI 自己维护,并且执行效率也更高,人类会觉得这些天书代码美不胜收,主动学习。就像棋手学习 AlphaGo 棋路一样。没有人觉得 AlphaGo 的棋丑,大家只觉得看不懂,然后去学了。

历史的参照

四色定理:Appel 和 Haken(1976)将问题归约为 1,936 个不可避免构型的计算机穷举验证,后由 Robertson 等人(1997)简化到 633 个。人类几乎不可读。当年数学界对此争议了几十年,很多数学家不接受纯计算机证明的有效性。但 2005 年 Gonthier 用 Coq 形式化验证了该证明,争议基本平息。

这恰好说明:关键不是人类能不能读懂证明过程,而是有没有可靠的验证手段。

AlphaGo Zero:人类看不懂的棋路,但有明确的胜负反馈。

共同点:结果可以被独立验证

所以未来 AI 软件验收原则应该是:形式化验证贯穿整个软件维护生命周期,模块开发时人类主要精力放在审查形式化规范有哪些变更,是否破坏了过去定下的规范。难以被形式化覆盖的部分,则依赖传统集成测试,配合 property-based testing,单元测试的必要性将会降低甚至可有可无。同时 AI 也可以辅助人类对产品设计文档进行形式化翻译,或者从形式化断言翻译回自然语言帮助人类理解,帮助检查自然语言中模糊的部分是否与过去的设计规范存在矛盾,并最终与形式化规范进行对齐。而软件功能的最终验收依然是人类负责,这部分暂时无法被替代,因为大部分软件的最终消费者是人类。

所以软件工程师在整个软件开发生命周期中,并不能完全放手,任由 AI 自行决策发挥。"AI 时代不再需要软件工程师"是老板想听的美好故事,而不是即将发生的现实。但有一点已经正在发生,AI 时代大批码农会失业,CS 专业会回冷,资深工程师的生产力得到了数倍放大,资本家可以采购一些 token 而裁掉开发部一半的 coder,这是已经产生的变化。就像织布机发明出来以后,工厂裁掉大批纺织女工,而招聘少量技术工人。数学家不会像计算器发明之前那样工作。今天,软件工程师也不应该。

结论

本文的核心:当前以人为本的软件工程规则需要推翻重建。

  1. 推翻以人为本的规则,保留核心原则。 复杂度始终存在,放任不管必然状态爆炸。
  2. 人类标准是 AI 的枷锁。 实验观察:人类规范让 agent 输掉 17 轮,屎山惩罚在 28 级范围内未到来。
  3. 区分两种屎山: 架构屎山是人类的责任,代码屎山交给 lint 和 agent。
  4. 区分两类债务: 模块间的(对谁都有害,严控)和模块内的(对 AI 无害,放宽)。
  5. 架构分两层: 人类管边界(模块拓扑、接口契约),AI 管实现(模块内部自由)。
  6. "务实"需要重新定义: 红线画在对外边界,红线内部 AI 自由连锁修改。
  7. 验收靠验证不靠阅读。 形式化验证 > property-based testing。
  8. 一切皆可元。 AI 写代码,也写"写代码的规则",自己鞭策自己。
  9. 下一步: 需要补充 AI 原生 QG 组和周期性还债组的实验数据来完善论证。

后记

纠正 Token 经济学

在之前的文章中,我把 chat-based billing model 直接套在 Claude Code 上,这是不准确的。CC 本身做了非常多的优化细节工作,所以账单增长并不像我之前说的那么夸张。

CC 的主要计费来自两部分:过半的费用是 output token,而 input token 只占账单总额的很小一部分。上下文越长、轮次越多,input 越来越长的增长主要转移到 cache read/write 上。

跨轮次成本变化率

Cost Rate

R28-R32 之间的轮次间成本变化率。大多数语言在 R32 趋于稳定或下降,说明框架和策略在迭代中逐步收敛。

成本构成拆解

Token Cost Breakdown

R32 所有 Claude 运行的成本构成。红色=output token (单价最高 $75/M),蓝色=cache read ($1.5/M),绿色=cache write ($18.75/M),灰色=input ($15/M)。

Output token 虽然只占总 token 量的 0.6%,但因为单价是 cache read 的 50 倍,实际占账单的 15-25%。Cache read 占 token 量的 95%+ 但因为单价极低,只占账单的 55-65%。Cache write 占约 25%

这意味着:优化 output token (减少不必要的代码生成和重复) 对账单的影响远大于优化上下文长度。

关于 Token 废话

有朋友问:模型的废话会影响结果吗?让模型少说废话能省 token 吗?

废话有好几种,需要分类讨论:

过度设计类废话:早期模型的通病,生成大量不必要的抽象层、工厂模式、策略模式。这类废话是纯垃圾,4.6 以后已经大幅改善。

过度思考类废话:当前模型的主要特征,在思考阶段把问题想复杂了,要把所有可能情况都探明。这类"废话"我其实还能接受。观察 Opus 的 CoT(思考链),对我来说是挺享受的过程,你不能拿事后诸葛亮的评价标准去评判,不能指望模型直接输出正确结果,人类也做不到。

但更关键的发现是:大部分废话与模糊需求有关。 AI 在猜你想要的是什么东西,猜出 2-3 种可能性就得分别两三条路分析一下。这时候你需要的不是让 AI 别废话,你需要改进你的规则文件,消除歧义,让它能清晰地去执行而不是揣摩你的意图。

这是一个反直觉的结论:输入越长、越详细具体,AI 反而越省 token。 因为 AI 不需要揣摩你的意图。你只管给 AI 一刀切的死规矩,别让它做模糊判断,出了问题人担着就行。反复几次以后,你的规则文件就越来越清晰可执行了,你也就知道哪些边界需要人工复审,哪些实现可以放手让 AI 发挥了。磨合好了,生产力就开始爆炸式起飞。

这些原则在 第四篇 的「军令级精确度」和「Token 经济学」两节中有更系统的阐述,泛泛而谈的准则("务实"、"不要过度设计"、"贯彻纯函数式"),每一句都是正确的废话,因为 AI 不知道你心目中的"务实"是什么意思,"过度设计"的分界线在哪。结合具体业务场景的实际规则,效果远好于这些泛泛准则。


以上测试大约烧了我 5000 USD 的 Opus 4.6 token。一开始我没有想到这次实验能有什么价值,能有什么新的结论产生。

但随着实验推进,我为了完善实验框架而构建的 harness 流程,以及实验数据揭露给我的、与我直觉相反的结论,都带给我不少震撼。若是一开始告诉我要做一个实验,花 5000 美金,结果未知,我肯定不做这个冤大头。

但现在,我觉得哪怕让我自掏腰包支付这 5000 美金也值了。在完善框架和反复测试 agent 极限的过程中,它对我的价值已经不是 5000 美金能衡量的了。

所以在这里我要开始插入卖课链接了,我已经在“没有知识存在的荒原”平台开通了高级系列课程 ——

AI 导师介绍

不开玩笑了。


感谢所有跟随这个系列到第五篇的同道中人,你们给的情绪价值是我继续写作分享的动力。我所有实验框架代码和数据已经上传到 GitHub,感兴趣的读者可以自取,也可以让你的 AI 来分析我的实验过程——实验框架是如何一步步改进的。

请注意:该测试每个 agent 每轮会烧掉你 100-300 美金 Opus token。请谨慎在个人账户上复现,烧光了这周额度或者被 A 畜封号别怪我没提醒你。

项目地址: github.com/mingyang91/ming-bench — 包含完整的 benchmark 框架、策略文件、测试用例和编排工具。