气缸、建筑与 Vibe Coding

19 阅读35分钟

一个意外的发现

我最近尝试了一个实验。

不是"AI辅助写代码",而是用纯AI实现一个完整项目

  • 所有实现代码由AI生成
  • 我只负责架构设计和约束规则
  • 零人工代码干预

这不是什么"AI有多强"的成功故事。设计调整很麻烦,架构迭代费时间,Prompt来回调试,CI反复失败重跑。整个过程充满了挫折和试错。

但这个过程让我意识到一件事。

在某个深夜调试失败的第N次重跑时,我突然停下来思考:这个工作模式,我好像在哪里见过...

不是在别的AI项目里,而是在更久远的地方。编译器?不对,更早。云原生?也不完全是。甚至追溯到内燃机的发明——那个把混沌的爆炸转化为可靠动力的工程奇迹。

我发现了一个重复出现的模式。

一个贯穿计算机历史、甚至工程史的模式。两个简单但深刻的技术模式:

模式1:把复杂度推给系统内最强大的组件

不是让人类"变强"来适应复杂度,而是识别系统中最擅长处理这种复杂度的组件,然后把复杂度推给它。

模式2:Common Cases First / Good Enough

不需要解决所有问题,只需要解决大部分常见问题。先让系统"足够好"能够工作,然后快速迭代。

这两个模式不是新想法。它们在编译器的发明、云原生的兴起、甚至汽车工业的诞生中反复出现。

而AI编程,可能只是这个古老模式的最新一次重演。

这篇文章,是我对这两个模式的探讨。不是方法论指南,不是实现步骤,而是对工程演进规律的观察和思考。我可能错了,但我认为这值得讨论。


第一部分:两个技术模式

模式1:把复杂度推给最强组件

这是计算机演进的核心规律。

不是训练人类去适应越来越复杂的系统,而是识别系统中最强大的组件,把复杂度推给它处理

让我用三个不同时代、不同领域的案例来说明。

案例1:编译器的诞生

1960年代,程序员用汇编语言写程序。这意味着:

  • 手动管理寄存器分配
  • 计算每个跳转的精确偏移量
  • 追踪每个变量的内存地址
  • 处理指令级的优化
graph LR
    A[1960年代<br/>汇编编程] --> B[问题:<br/>人类处理复杂度]
    
    B --> B1[手动管理寄存器]
    B --> B2[计算跳转偏移]
    B --> B3[追踪内存地址]
    
    C[1970年代<br/>高级语言] --> D[解决:<br/>编译器处理复杂度]
    
    D --> D1[编译器分配寄存器]
    D --> D2[编译器计算跳转]
    D --> D3[编译器管理内存]
    
    B --> E[人类累死<br/>容易出错]
    D --> F[机器擅长<br/>更可靠]
    
    style E fill:#ffcccc
    style F fill:#ccffcc

当时有两种解决思路:

思路A:培训更多汇编程序员,让他们变得更熟练
思路B:发明编译器,让机器处理这些细节

历史选择了思路B。

为什么?

因为编译器是系统中"最强的组件"(在这个维度上):

  • 它不会疲劳
  • 它不会犯算术错误
  • 它可以做人类做不到的优化(如寄存器着色算法)

关键转变:不是让人类"变强",而是把复杂度推给更擅长的组件。


案例2:云原生的革命

2000年代,运维工程师的日常:

  • 凌晨3点被报警惊醒
  • 手动登录服务器
  • 分析日志,判断需要扩容几台
  • 手动配置负载均衡
  • 祈祷不要出错
graph TD
    subgraph "传统架构"
        A1[凌晨报警] --> A2[运维醒来]
        A2 --> A3[分析问题]
        A3 --> A4[手动扩容]
        A4 --> A5[配置负载均衡]
        A5 --> A6[监控是否正常]
    end
    
    subgraph "云原生架构"
        B1[流量激增] --> B2[控制平面检测]
        B2 --> B3[自动决策]
        B3 --> B4[自动扩容]
        B4 --> B5[自动配置]
        B5 --> B6[Self-healing]
    end
    
    style A6 fill:#ffcccc
    style B6 fill:#ccffcc

同样有两种思路:

思路A:培训运维"反应更快",建立更好的值班制度
思路B:让Kubernetes控制平面自动处理

历史再次选择了思路B。

为什么?

因为控制平面是系统中"最强的组件"(在这个维度上):

  • 7×24小时工作,毫秒级响应
  • 能同时监控成千上万个指标
  • 不会因为半夜起床而情绪波动

关键转变:不是训练"超人运维",而是把复杂度推给自动化系统。


案例3:Tokenizer的智慧

早期自然语言处理(NLP)面临一个问题:如何处理未见过的词?

比如训练集有 "running",但测试时遇到 "running's",该怎么办?

graph TD
    A[早期NLP] --> B[人类设计规则]
    B --> B1[穷举词典<br/>millions of words]
    B --> B2[手工语法规则<br/>复数、过去式...]
    B --> B3[处理特殊情况<br/>缩写、复合词...]
    
    C[现代NLP] --> D[Subword + 训练]
    D --> D1[自动拆分<br/>run + ##ning + ##'s]
    D --> D2[涌现语法理解<br/>模型自己学]
    D --> D3[泛化到未见词<br/>组合推理]
    
    B --> E[规则爆炸<br/>难以维护]
    D --> F[模型学习<br/>自动泛化]
    
    style E fill:#ffcccc
    style F fill:#ccffcc

两种方案对比:

方案A(人类处理复杂度)

  • 手工设计词形还原规则
  • 构建庞大的词典(几百万词条)
  • 为每种语言写特殊处理逻辑

方案B(机器处理复杂度)

  • Subword tokenization(BPE、WordPiece)
  • 把词拆成更小的子词单元
  • 让神经网络在训练中涌现出语法理解

现代NLP选择了方案B。

为什么?

因为神经网络+训练过程是"最强的组件":

  • 能从数据中学习模式
  • 能处理人类无法穷举的边界情况
  • 能自动泛化到新的语言现象

关键转变:把"语法理解"的复杂度,推给神经网络的学习能力。


统一的模式

这三个案例,跨越不同时代、不同领域,但背后是同一个模式:

graph TD
    A[识别复杂度] --> B{谁最擅长处理?}
    
    B -->|人类?| C[❌ 容易疲劳<br/>扩展性差<br/>成本高]
    
    B -->|机器?| D[✅ 不疲劳<br/>可扩展<br/>可优化]
    
    D --> E[设计接口]
    E --> F[推给机器处理]
    F --> G[人类专注更高层次]
    
    style C fill:#ffcccc
    style D fill:#ccffcc
    style G fill:#ccffcc

核心洞察

技术进步不是让人类"变得更强"去适应复杂度,而是识别系统中最强大的组件,设计好接口,把复杂度推给它

  • 编译器 → 处理底层指令细节
  • 控制平面 → 处理运维复杂度
  • 神经网络 → 处理语言理解

人类则专注于更高层次的工作

  • 算法设计(而不是手写汇编)
  • 架构设计(而不是半夜扩容)
  • 模型训练(而不是写规则)

这不是偶然,这是计算机演进的普遍规律。


模式2:Common Cases First / Good Enough

第二个模式同样重要,但常被误解。

工程不追求"完美",而追求"足够好"。先解决90%的常见场景,让系统可用,然后快速迭代。

这不是妥协,这是智慧。

80/20法则的工程表达

graph LR
    A[所有问题] --> B[80%<br/>Common Cases]
    A --> C[20%<br/>Edge Cases]
    
    B --> B1[投入: 20%努力]
    B --> B2[收益: 80%价值]
    
    C --> C1[投入: 80%努力]
    C --> C2[收益: 20%价值]
    
    B --> D[✅ 优先]
    C --> E[⚠️ 延后或接受]
    
    style D fill:#ccffcc
    style E fill:#ffffcc

关键问题:资源有限时,先投入哪里?

完美主义会说:必须解决所有问题,才能发布。
务实主义会说:先解决大部分问题,让系统可用。

历史一次又一次验证:务实主义创造了更大价值。


案例1:TCP的"不完美"

TCP协议不是理论上"最优"的传输协议。

它的"缺陷":

  • 有时候延迟会很高(队头阻塞)
  • 公平性不完美(一个连接可能占用更多带宽)
  • 拥塞控制算法是启发式的(不是最优解)
graph TD
    A[TCP不是完美的] --> B[妥协]
    
    B --> B1[延迟<br/>HOL blocking]
    B --> B2[公平性<br/>不完美]
    B --> B3[拥塞控制<br/>启发式]
    
    C[但足够好] --> D[优势]
    
    D --> D1[解决90%需求<br/>可靠传输]
    D --> D2[实现相对简单<br/>广泛部署]
    D --> D3[在Common Cases下<br/>表现优秀]
    
    B --> E[❌ 不完美]
    D --> F[✅ 支撑互联网]
    
    style E fill:#ffffcc
    style F fill:#ccffcc

关键问题:如果当年坚持"必须完美才能用",会怎样?

答案是:可能至今还在设计中,互联网也不会诞生。

启示:TCP在Common Cases(普通网络条件、大部分应用场景)下表现足够好,就能创造巨大价值。那些Edge Cases(极端网络条件、特殊需求)可以用其他协议(UDP、QUIC)补充。


案例2:JavaScript的"胜利"

JavaScript有无数被诟病的"设计缺陷":

  • 弱类型导致的奇怪行为([] + [] === ""
  • this绑定的混乱规则
  • 全局作用域污染
  • 类型强制转换的陷阱

任何一个计算机科学的课程,都会把它当作"反面教材"。

graph LR
    A[JS的设计缺陷] --> B[弱类型混乱]
    A --> C[this绑定问题]
    A --> D[全局污染]
    
    E[但足够好] --> F[浏览器原生支持]
    E --> G[学习曲线平缓]
    E --> H[生态繁荣]
    
    B & C & D --> I[❌ 不优雅]
    F & G & H --> J[✅ 最流行语言]
    
    style I fill:#ffffcc
    style J fill:#ccffcc

但现实是:JavaScript成为世界上最流行的编程语言之一。

为什么?

因为它在Common Cases(网页交互、表单验证、DOM操作)下"足够好":

  • 能完成任务
  • 学习门槛低
  • 生态系统强大

那些"完美设计"的语言(如Elm、PureScript),虽然理论上更优雅,但因为门槛高、生态弱,反而没有大规模普及。

启示:完美的设计 + 小众生态,往往输给"足够好"的设计 + 繁荣生态。


案例3:AI的"幻觉"问题

AI生成内容时会产生幻觉(生成不存在的事实)。

完美主义者会说:"在幻觉问题彻底解决之前,AI不能用于生产。"

务实主义者会说:"只要AI在Common Cases下准确率足够高,配合验证机制,就可以使用。"

graph TD
    A[AI生成的准确率] --> B{场景类型}
    
    B --> C[Common Cases<br/>90-95%准确]
    B --> D[Edge Cases<br/>60-70%准确]
    
    C --> C1[配合测试验证]
    C --> C2[足够可用]
    
    D --> D1[人工处理]
    D --> D2[或可接受误差]
    
    C1 & C2 --> E[✅ 创造价值]
    D1 & D2 --> F[⚠️ 成本可控]
    
    E --> G[实用的AI编程]
    
    style G fill:#ccffcc

现实验证

  • GitHub Copilot虽然会生成错误代码,但配合开发者审查,仍然大幅提升效率
  • ChatGPT虽然会犯错,但在大部分常见问题上表现足够好
  • AI绘画虽然有瑕疵,但已经在设计行业广泛应用

关键洞察:不需要100%完美,只需要在Common Cases下"足够好",配合适当的验证和人工把关,就能创造巨大价值。


工程务实主义的智慧

graph TD
    A[完美主义路径] --> B[追求100%覆盖]
    B --> C[开发周期长]
    C --> D[错过市场窗口]
    D --> E[❌ 可能失败]
    
    F[务实主义路径] --> G[先解决90%场景]
    G --> H[快速上线]
    H --> I[收集真实反馈]
    I --> J[迭代改进]
    J --> K[✅ 逐步完善]
    
    style E fill:#ffcccc
    style K fill:#ccffcc

这不是"降低标准",而是认识到:

  1. 完美是渐进的,不是一次性的
    先做到80分,上线验证,再迭代到90分、95分。

  2. 真实世界比想象复杂
    在实验室追求100%覆盖,不如在真实场景中解决90%的实际问题。

  3. 时间窗口很重要
    晚一年的完美方案,往往输给早半年的"足够好"方案。

Good Enough不是妥协,而是工程智慧。


创造的本质:组合与选择

在讨论这两个模式如何协同之前,让我们先理解创造本身是如何发生的

法国诗人Paul Valéry提供了一个深刻的洞察:

"任何发明都需要两个人:
一个负责发明(组合新事物),
另一个负责选择(判断哪些值得保留)。"

这揭示了创造的双层结构

graph LR
    A[创造过程] --> B[组合者<br/>Combinator]
    A --> C[选择者<br/>Selector]
    
    B --> B1[生成可能性空间]
    B --> B2[盲目变异]
    B --> B3[快速探索]
    
    C --> C1[筛选评估]
    C --> C2[选择性保留]
    C --> C3[价值判断]
    
    B --> D[扩展空间]
    C --> E[收缩空间]
    
    D & E --> F[创新涌现]
    
    style F fill:#ccffcc

组合者(Combinator)

  • 生成可能性空间
  • 盲目变异,不管好坏
  • 快速探索各种可能

选择者(Selector)

  • 在可能性中筛选、评估
  • 选择性保留有价值的
  • 淘汰不符合标准的

这不是诗意的比喻,而是创造的普遍机制

  • 认知科学(Herbert Simon):搜索理论——组合者扩展设计空间,选择者收缩空间以满足目标
  • 心理学(Donald Campbell):盲目变异与选择性保留——这是创造力的核心机制
  • 生物进化(Darwin):变异与自然选择——物种进化的驱动力

从生物进化到科学发现到艺术创作,这是创造的普遍机制。

在AI时代,这个二元论变得前所未有地清晰

graph LR
    A[传统创造] --> B[人类既组合<br/>又选择]
    B --> C[慢<br/>but整合]
    
    D[AI时代] --> E[AI组合<br/>人类选择]
    E --> F[快<br/>and专业化]
    
    style C fill:#ffffcc
    style F fill:#ccffcc
  • AI:最强大的组合者(超快的盲目变异能力)
  • 人类:关键的选择者(设计选择标准和机制)

我们的价值从"组合"转变为"选择"。

从"爆炸源"转变为"气缸的设计师"。

这个框架将贯穿后面的讨论,帮助我们理解AI编程的本质。


第二部分:历史的验证——内燃机

现在,让我们回到1860年代。

当时工程师面临一个看似矛盾的挑战:如何把一场混沌的爆炸,变成可靠的动力?

这个案例完美展示了两个模式如何协同工作,而且,它与我们今天讨论的AI编程有着惊人的相似性。

早期的困境

1860年,法国工程师Étienne Lenoir制造出第一台商用内燃机。

这是一个革命性的发明,但也是一个噩梦般的装置:

graph TD
    A[早期内燃机<br/>Lenoir 1860] --> B[爆炸的特性]
    
    B --> B1[强大<br/>高能量密度]
    B --> B2[混沌<br/>难以预测]
    B --> B3[危险<br/>随机回火]
    B --> B4[低效<br/>仅4%转化率]
    
    C[核心挑战] --> D[如何驯服这场爆炸?]
    
    style B1 fill:#ccffcc
    style B2 fill:#ffcccc
    style B3 fill:#ffcccc
    style B4 fill:#ffcccc

具体问题

  • 随机回火:点火时机不对,火焰反窜回化油器,整个装置可能炸飞
  • 过热失控:没有冷却系统,连续运行20分钟就烫得碰不得
  • 效率极低:只有4%的燃料能量转化为有用功,其余都是热量和噪音
  • 启动困难:经常点不着,或者一点就熄火

工程师们面临的根本问题是:

如何把这个强大但混乱的"爆炸",变成可靠的动力源?


模式1的应用:推给物理系统

当时有两种思路:

思路A:深入研究燃烧过程

  • 理解每个分子的运动
  • 建立精确的燃烧模型
  • 实时控制燃烧过程

思路B:设计物理约束系统

  • 不试图"理解"爆炸
  • 而是设计一个物理结构(气缸)
  • 让物理定律自动将混沌转化为有序

工程师选择了思路B。

为什么?

graph LR
    A[爆炸的复杂度] --> B{谁来处理?}
    
    B -->|人类理解?| C[❌ 分子级别<br/>极其复杂<br/>实时控制不可能]
    
    B -->|物理约束?| D[✅ 气缸壁<br/>物理定律<br/>自动转化]
    
    D --> E[压力 → 定向力]
    E --> F[无序 → 有序]
    F --> G[混沌 → 可靠动力]
    
    style C fill:#ffcccc
    style D fill:#ccffcc
    style G fill:#ccffcc

气缸的作用

不是"理解"或"控制"爆炸的每个细节,而是:

  1. 用坚固的金属壁限制爆炸范围
  2. 让物理定律(压力均匀作用)将爆炸能量转化为定向推力
  3. 通过机械结构(活塞、连杆)将往复运动转化为旋转运动

关键洞察1:接受黑箱,转向约束设计

早期工程师面临一个选择:是深入理解燃烧的每个分子运动,还是接受爆炸的混沌性,设计约束系统来引导它?

历史选择了后者。

为什么?

因为"理解爆炸"是个几乎不可能完成的任务(分子级别、量子效应、热力学复杂性),而且不必要

工程师的智慧

  • 不追求完全理解
  • 接受其"黑箱"特性
  • 专注于设计能承载和引导能量的外部结构

对AI的启示

我们不必等到AI完全可解释(这可能是遥不可及的目标)才开始工程化。我们可以接受其"黑箱"特性,转而专注于设计约束系统。


关键洞察2:约束是多层次的系统

内燃机的成功,不是依赖单一约束,而是一个协同系统:

graph TD
    A[约束系统] --> B[物理约束<br/>气缸壁]
    A --> C[流程约束<br/>四冲程]
    A --> D[时序约束<br/>精确点火]
    A --> E[稳定性约束<br/>冷却系统]
    
    B & C & D & E --> F[协同作用]
    F --> G[可靠动力]
    
    style G fill:#ccffcc
  1. 物理约束:气缸壁限制能量方向
  2. 流程约束:四冲程循环规范化过程(吸气→压缩→做功→排气)
  3. 时序约束:精确点火控制爆炸时机
  4. 稳定性约束:冷却系统防止过热失控

对AI的启示

单一约束(如仅靠Prompt)不足以驾驭AI。我们需要分层的约束架构:

  • 类型系统(物理约束)
  • 测试套件(流程约束)
  • API契约(时序约束)
  • 监控告警(稳定性约束)

关键洞察3:约束不是限制,而是"使能"(Enabling)

这是最违反直觉的洞察。

graph LR
    A[无约束的爆炸] --> B[无序破坏]
    B --> C[❌ 无法利用]
    
    D[约束下的爆炸] --> E[定向能量]
    E --> F[✅ 可靠动力]
    
    style C fill:#ffcccc
    style F fill:#ccffcc

没有气缸

  • 爆炸只是无序的破坏力
  • 能量四散,无法利用
  • 危险、不可控

有了气缸

  • 爆炸转化为定向推力
  • 能量被引导、被利用
  • 安全、可靠

气缸的约束,非但没有"限制"爆炸的能量,反而使其得以释放并转化为有用的功。

对AI的启示

好的工程约束(如TDD、类型系统)不会扼杀AI的创造力,而是将其引导到正确的方向,使其生成真正有价值、可交付的代码。

约束是使能,不是限制。

这是内燃机给我们的最深刻启示。


模式2的应用:Good Enough

有了气缸之后,工程师面临下一个问题:如何让它"更好"?

如果追求"完美",可能会这样设计:

  • 动态点火时机:根据每次燃烧情况实时调整
  • 理论最高效率:追求接近60%的理论上限(卡诺循环)
  • 零排放:完全燃烧,零污染
  • 完美冷却:保持恒定的最佳温度

但1876年,尼古拉斯·奥托(Nicolaus Otto)发明的四冲程循环,选择了"足够好"而不是"完美":

graph TD
    A[奥托循环的设计] --> B[不追求完美]
    
    B --> B1[效率20-25%<br/>vs 理论上限60%]
    B --> B2[固定时序<br/>vs 动态优化]
    B --> B3[可接受排放<br/>vs 零排放]
    
    C[但足够好] --> D[优势]
    
    D --> D1[简单可靠<br/>易于制造]
    D --> D2[成本可控<br/>可大规模生产]
    D --> D3[维护方便<br/>标准化]
    
    B --> E[⚠️ 有妥协]
    D --> F[✅ 支撑汽车工业]
    
    style E fill:#ffffcc
    style F fill:#ccffcc

关键权衡

graph LR
    A[Common Cases<br/>稳态运行] --> B[优化重点<br/>80%资源]
    
    C[Edge Cases<br/>极端情况] --> D[可接受<br/>20%资源]
    
    B --> B1[正常温度]
    B --> B2[稳定转速]
    B --> B3[常见负载]
    
    D --> D1[冷启动]
    D --> D2[极端温度]
    D --> D3[峰值负载]
    
    B --> E[做到很好]
    D --> F[做到够用]
    
    E & F --> G[商业成功]
    
    style G fill:#ccffcc

为什么这个"不完美"的设计获得了成功?

因为它在Common Cases(普通驾驶、常见工况)下"足够好":

  • 20-25%的效率足以驱动汽车
  • 固定时序虽然不是最优,但简单可靠
  • 可接受的排放水平符合当时的环境要求

那些追求"完美效率"的复杂设计,反而因为成本高、可靠性差、难以制造,没能大规模商用。


两个模式的协同

内燃机的成功,不是因为工程师"理解了爆炸",也不是因为设计"完美无缺"。

而是因为两个模式的巧妙结合:

graph TD
    A[内燃机的成功] --> B[模式1:<br/>推给物理系统]
    A --> C[模式2:<br/>Good Enough]
    
    B --> B1[不理解爆炸细节<br/>设计气缸约束]
    B --> B2[让物理定律<br/>处理复杂度]
    
    C --> C1[不追求完美效率<br/>20-25%够了]
    C --> C2[不动态优化<br/>固定循环够了]
    
    B1 & B2 & C1 & C2 --> D[协同效果]
    
    D --> D1[可靠]
    D --> D2[可制造]
    D --> D3[可商用]
    
    style D fill:#ccffcc

具体表现

  1. 可靠性:因为推给物理系统,不依赖复杂控制
  2. 可制造性:因为设计简单(固定循环),可大规模生产
  3. 可商用性:因为成本可控,能负担得起

这不是"降低标准",而是认清哪些复杂度该推给谁,做到什么程度就够了


对AI编程的启示

现在,让我们建立类比:

graph LR
    subgraph "内燃机"
        A1[爆炸<br/>强大但混沌] --> A2[气缸<br/>物理约束]
        A2 --> A3[可靠动力<br/>Good Enough]
    end
    
    subgraph "AI编程"
        B1[AI生成<br/>强大但概率] --> B2[约束系统<br/>验证机制]
        B2 --> B3[可靠代码<br/>Good Enough]
    end
    
    A1 -.类似.- B1
    A2 -.类似.- B2
    A3 -.类似.- B3
    
    style A3 fill:#ccffcc
    style B3 fill:#ccffcc

映射关系

内燃机AI编程共性
爆炸(强大但混沌)AI生成(强大但概率)有力量但不确定
气缸(物理约束)约束系统(验证机制)将混沌转化为有序
可靠动力可靠代码足够好即可用

两个模式的应用

模式1在AI编程

  • 不是试图"理解"AI的每个神经元
  • 而是设计约束系统(测试、类型、架构规则)
  • 把"代码实现"的复杂度推给AI

模式2在AI编程

  • 不追求AI 100%正确
  • 在Common Cases下90-95%正确 + 验证机制
  • 就足够创造价值

但我必须承认,这个类比不是完美的。


类比的局限

graph TD
    A[内燃机 vs AI] --> B[相似点]
    A --> C[本质差异]
    
    B --> B1[都是强大但混沌]
    B --> B2[都需要约束]
    B --> B3[都遵循Good Enough]
    
    C --> C1[确定性物理<br/>vs<br/>概率性生成]
    C --> C2[硬约束<br/>vs<br/>软约束]
    C --> C3[可完全建模<br/>vs<br/>黑箱机制]
    
    B --> D[✅ 启发思维<br/>展示模式]
    C --> E[⚠️ 不能照搬<br/>需要适应]
    
    style D fill:#ccffcc
    style E fill:#ffffcc

关键差异

  1. 物理 vs 概率
    内燃机的爆炸本质是确定性物理过程(给定条件,结果可预测)
    AI生成是概率性过程(相同输入,输出可能不同)

  2. 硬约束 vs 软约束
    气缸壁是物理硬约束(100%限制能量方向)
    逻辑约束是软约束(可能被"绕过"或失效)

  3. 可建模 vs 黑箱
    燃烧过程可以用热力学完整建模
    神经网络的决策过程是黑箱

类比的价值

不是说"AI编程就是内燃机",而是展示:

  • "把复杂度推给最强组件"是跨领域的普遍模式
  • "Good Enough"是工程实践的反复验证规律
  • 这两个模式在不同领域反复出现,不是偶然

接下来,让我们看这两个模式如何具体应用到AI编程中,以及如何应对那个关键的差异:概率性。


第三部分:AI编程——模式的延续

现在,让我们回到AI编程。

当我凌晨调试那个失败的CI时,我意识到:这不是一个全新的问题,而是一个古老模式的新实例。

范式的演进

让我们先看编程范式的历史演进:

graph LR
    A[命令式<br/>Imperative<br/>How] --> B[声明式<br/>Declarative<br/>What]
    B --> C[意图式<br/>Intentional<br/>Why/Intent]
    
    A --> A1[for i=0 to n<br/>手动控制流程]
    B --> B1[SELECT * FROM<br/>描述目标]
    C --> C1["写一个登录功能..."<br/>表达意图]
    
    A --> A2[人类处理细节]
    B --> B2[系统处理细节]
    C --> C2[AI处理细节]
    
    style C fill:#ccffcc
    style C1 fill:#ccffcc
    style C2 fill:#ccffcc

每次演进都是抽象层级的提升

命令式(1960s-1990s)

  • 告诉机器"怎么做"(How)
  • 人类管理所有细节:循环、条件、状态

声明式(1990s-2010s)

  • 告诉机器"要什么"(What)
  • 系统决定实现路径:SQL、React、Terraform

意图式(2020s-)

  • 告诉机器"意图是什么"(Why/Intent)
  • AI理解意图并生成实现

核心观察

每次抽象层提升,都是**"把复杂度推给更强组件"**的体现:

graph TD
    A[抽象层提升] --> B[命令式→声明式]
    A --> C[声明式→意图式]
    
    B --> B1[实现细节<br/>推给系统]
    C --> C1[代码编写<br/>推给AI]
    
    B --> B2[人类: 算法设计]
    C --> C2[人类: 架构+意图]
    
    B1 & B2 --> D[同样的模式]
    C1 & C2 --> D
    
    style D fill:#ccffcc

意图式编程不是革命,而是演进的延续。


AI的根本特性与工程张力

但在讨论如何应用两个模式之前,我们必须直面一个根本性的挑战。

这个挑战,正是AI与内燃机类比的核心差异所在。

AI生成的本质特性

graph TD
    A[AI生成代码的特性] --> B[概率性<br/>Probabilistic]
    A --> C[不可解释性<br/>Opaque]
    A --> D[质量波动<br/>Volatile]
    
    B --> B1[同样的Prompt<br/>可能不同输出]
    C --> C1[无法追踪<br/>为何这样实现]
    D --> D1[从优雅到Bug<br/>参差不齐]
    
    style B fill:#ffcccc
    style C fill:#ffcccc
    style D fill:#ffcccc

具体表现

概率性

  • 今天生成的登录函数用了bcrypt
  • 明天可能用argon2
  • 后天可能用PBKDF2
  • 相同意图,不同实现

不可解释性

  • 为什么选择这个算法?
  • 为什么用这种数据结构?
  • 为什么这样处理边界?
  • 我们不知道,AI也说不清。

质量波动

  • 有时生成完美的代码(一次通过所有测试)
  • 有时有隐蔽的bug(逻辑错误、安全漏洞)
  • 有时过度设计(不必要的抽象)
  • 无法预测这次会怎样。

工程交付的传统要求

graph TD
    A[工程交付要求] --> B[确定性<br/>Deterministic]
    A --> C[可追溯性<br/>Traceable]
    A --> D[质量保证<br/>Assured]
    
    B --> B1[相同输入<br/>相同输出]
    C --> C1[每行代码<br/>有明确作者和理由]
    D --> D1[可预测的<br/>高标准可靠性]
    
    style B fill:#ccffcc
    style C fill:#ccffcc
    style D fill:#ccffcc

具体要求

确定性

  • 编译相同代码 → 得到相同二进制
  • 运行相同测试 → 得到相同结果
  • 部署相同版本 → 行为可复现

可追溯性

  • 每个commit有作者
  • 每个change有review
  • 每个bug有归责
  • Git history完整

质量保证

  • 代码覆盖率达标
  • 静态分析通过
  • Security scan无漏洞
  • 性能测试达标

深刻的张力(Tension)

graph LR
    A[AI特性] --> C[根本冲突]
    B[工程要求] --> C
    
    A --> A1[概率的]
    A --> A2[黑箱的]
    A --> A3[波动的]
    
    B --> B1[确定的]
    B --> B2[可追溯的]
    B --> B3[保证的]
    
    A1 -.冲突.- B1
    A2 -.冲突.- B2
    A3 -.冲突.- B3
    
    C --> D[如何共存?]
    
    style C fill:#ffe6e6
    style D fill:#fff3e6

这构成了AI编程的核心挑战

我们既不想放弃AI带来的效率飞跃(10倍、100倍的生产力提升),也不能牺牲工程交付的可靠性与可维护性。

问题是:如何在"概率性的AI"和"确定性的工程"之间建立桥梁?

重新框架问题

传统思维(我认为是错误的):

  • 等待AI变得"确定性"
  • 等待AI变得"可解释"
  • 等待AI变得"零错误"

新的思维(可能是正确的):

  • 接受AI的概率性(就像接受爆炸的混沌)
  • 不追求理解AI(就像不追求理解每个分子)
  • 设计约束系统来"选择性保留"(气缸机制)
graph TD
    A[AI作为组合者] --> B[生成可能性]
    B --> C[概率的、波动的]
    
    D[约束系统作为选择者] --> E[筛选、验证]
    E --> F[确定的、可靠的]
    
    C --> G[自动化选择机制]
    G --> F
    
    style C fill:#ffcccc
    style F fill:#ccffcc
    style G fill:#fff3e6

核心洞察

不是让AI变得"确定",而是在AI的概率输出和工程的确定性要求之间,插入一个"自动化的选择层"

这就是约束系统的本质,也是Valéry框架在AI编程中的应用。


AI作为最强组合者

在Valéry的框架下,AI的角色变得清晰:

AI是一个超级组合者。

在代码生成这个维度上,AI是系统中"最强的组件"。

graph TD
    A[AI在代码生成的优势] --> B[记忆力]
    A --> C[速度]
    A --> D[不疲劳]
    A --> E[泛化能力]
    
    B --> B1[记住所有API签名<br/>vs<br/>人类查文档]
    
    C --> C1[秒级生成<br/>vs<br/>人类小时级]
    
    D --> D1[7×24工作<br/>vs<br/>人类需休息]
    
    E --> E1[跨语言迁移<br/>vs<br/>人类学习成本]
    
    style A fill:#ccffcc

对比表

维度人类AI
记忆API需要查文档内置知识
生成速度小时级秒级
工作时间8小时/天7×24
处理重复容易疲劳不会疲劳
边界情况可能遗漏系统性覆盖

所以,把"代码实现"的复杂度推给AI,符合模式1。


人类角色:从组合到选择

在Valéry的框架下,人类的角色也变得清晰:

我们不再是"组合者"(AI比我们快)
我们成为"选择者"(设计选择的标准和机制)

graph LR
    A[AI<br/>组合者] --> B[生成大量可能性]
    B --> C[盲目变异<br/>快速探索]
    
    D[人类<br/>选择者] --> E[设计选择标准]
    E --> F[选择性保留<br/>价值判断]
    
    C --> G[约束系统<br/>自动化选择]
    F --> G
    G --> H[可靠输出]
    
    style A fill:#e6f3ff
    style D fill:#fff3e6
    style H fill:#ccffcc

人类的四个核心价值

  1. 架构设计:定义系统的整体结构和边界
  2. 约束设计:设计"选择"的标准(测试、类型、契约)
  3. 粒度判断:决定如何拆分任务(这是组合者需要的输入格式)
  4. 质量把关:最终的选择者(人工审查Edge Cases)

关键转变

graph LR
    A[传统角色] --> B[Code Writer<br/>实现者]
    B --> C[手写每行代码]
    
    D[新角色] --> E[Constraint Designer<br/>选择者]
    E --> F[设计选择机制]
    
    style B fill:#ffcccc
    style E fill:#ccffcc

从"生成代码"到"设计选择代码的机制"。

这不是技能降级,这是抽象层次的提升


粒度判断:关键的Human in Loop

这里有个容易被忽视但极其重要的人类价值。

在我的实验中,我发现一个规律:

AI生成的质量,高度依赖于任务的粒度划分。

graph TD
    A[粒度判断] --> B[粒度太粗<br/>单个大任务]
    A --> C[粒度合适<br/>平衡点]
    A --> D[粒度太细<br/>过度拆分]
    
    B --> B1[AI理解困难<br/>容易出错]
    B --> B2[测试覆盖难]
    
    C --> C1[AI生成质量高<br/>很少出错]
    C --> C2[易于测试]
    C --> C3[维护友好]
    
    D --> D1[接口复杂<br/>协调成本高]
    D --> D2[过度设计]
    
    style C fill:#ccffcc
    style B fill:#ffcccc
    style D fill:#ffffcc

粒度判断是什么?

就是架构设计的一部分,类似于:

  • 这个系统该拆成几个微服务?
  • 这个类该有哪些方法?
  • 这个模块的职责边界在哪?

目前的现状

graph LR
    A[粒度判断] --> B[依赖人类经验]
    
    B --> C[❌ 没有自动工具]
    B --> D[❌ 没有通用规则]
    B --> E[✅ 需要实践积累]
    
    E --> F[第1个月: 经常判断错]
    F --> G[第3个月: 能预判]
    G --> H[第6个月: 形成直觉]
    
    style E fill:#ccffcc

未来可能的改进

  • 工具辅助:IDE提供"粒度建议"
  • 方法论突破:更好的提示工程模式
  • 模型能力提升:更强的长代码生成能力

但目前:粒度判断是不可或缺的human in loop环节。

这不是AI的局限,这是人类价值的所在。


Good Enough在AI编程

现在让我们看模式2如何应用。

核心问题:AI会产生错误,怎么办?

完美主义:必须等到AI 100%可靠才能用
务实主义:只要在Common Cases下足够好,配合验证,就可以用

graph TD
    A[AI编程的务实主义] --> B[不追求100%]
    
    B --> C[Common Cases<br/>90-95%准确]
    B --> D[Edge Cases<br/>人工处理或接受]
    
    C --> C1[配合约束系统]
    C --> C2[自动验证]
    C --> C3[足够可用]
    
    D --> D1[成本过高]
    D --> D2[收益有限]
    D --> D3[可接受]
    
    C1 & C2 & C3 --> E[✅ 创造价值]
    D1 & D2 & D3 --> F[⚠️ 成本可控]
    
    E & F --> G[实用的AI编程]
    
    style G fill:#ccffcc

关键洞察

不需要AI解决所有问题,只需要它在Common Cases(标准CRUD、常见模式、重复性任务)下"足够好"。

那些Edge Cases(复杂算法、极端性能要求、创新设计)可以:

  • 人工实现
  • 延后处理
  • 接受瑕疵(如果不影响核心功能)

函数式思维的启示

这里有一个重要的思维转变。

传统思维:关注"代码怎么写的"
新的思维:关注"接口和行为"

graph LR
    A[关注点转移] --> B[重要的<br/>✅ 优先]
    A --> C[次要的<br/>⚠️ 接受黑箱]
    
    B --> B1[输入输出类型]
    B --> B2[行为符合预期]
    B --> B3[测试覆盖]
    B --> B4[性能满足要求]
    
    C --> C1[用循环还是递归?]
    C --> C2[用哪种数据结构?]
    C --> C3[具体的写法?]
    
    B --> D[这是约束<br/>必须验证]
    C --> E[这是AI的自由度<br/>可以接受黑箱]
    
    style D fill:#ccffcc
    style E fill:#e6f3ff

类比第三方库

当你使用一个第三方库时,你关心什么?

graph TD
    A[使用第三方库] --> B[你关心]
    A --> C[你不关心]
    
    B --> B1[接口: 参数和返回值]
    B --> B2[行为: 功能正确性]
    B --> B3[性能: 时间和空间]
    B --> B4[稳定性: bug率]
    
    C --> C1[内部实现: 算法细节]
    C --> C2[代码风格: 变量命名]
    C --> C3[具体写法: 循环还是递归]
    
    B --> D[✅ 必须满足]
    C --> E[❌ 不需要知道]
    
    style D fill:#ccffcc
    style E fill:#eeeeee

AI生成的代码,本质上也是"第三方组件"

  • 你关心它的接口、行为、性能
  • 你不需要关心它内部怎么实现
  • 只要通过测试和验证,内部是黑箱也无妨

可声明性是关键

graph LR
    A[适合AI的特征] --> B[输入输出明确]
    A --> C[无复杂副作用]
    A --> D[可独立测试]
    A --> E[职责单一]
    
    F[不适合AI的特征] --> G[职责模糊]
    F --> H[状态交互复杂]
    F --> I[需要创新算法]
    F --> J[难以测试]
    
    B & C & D & E --> K[✅ 可声明<br/>适合AI]
    G & H & I & J --> L[❌ 难声明<br/>需要人类]
    
    style K fill:#ccffcc
    style L fill:#ffcccc

所以

不是"大任务不适合AI",而是"不可声明的任务不适合AI"。

小函数如果可以清楚声明接口和行为,反而最适合AI,因为:

  • 职责单一,AI容易理解
  • 易于测试,验证成本低
  • 黑箱实现不影响使用

大型系统如果架构清晰、模块边界明确,也可以用AI生成各个模块,人类负责整体设计。


第四部分:约束系统——自动化的选择者

现在我们理解了张力所在:概率性的AI vs 确定性的工程。

问题是:如何在两者之间建立桥梁?

答案是:设计一个自动化的选择系统——约束系统。

这不是我的原创想法。英国建筑理论家Christopher Alexander在60年前就提出了理论基础。

Christopher Alexander的失配检测理论

Alexander在1964年的著作《Notes on the Synthesis of Form》中提出了一个深刻的洞察:

好的设计,不是"满足所有需求",而是"消除所有失配(Misfit)"。

graph TD
    A[设计问题] --> B[传统思维<br/>满足需求]
    A --> C[Alexander思维<br/>消除失配]
    
    B --> B1[列出所有好的属性]
    B --> B2[试图全部实现]
    B --> B3[❌ 需求模糊<br/>难以验证]
    
    C --> C1[列出所有Misfits]
    C --> C2[逐个消除]
    C --> C3[✅ 失配明确<br/>可以验证]
    
    style B3 fill:#ffcccc
    style C3 fill:#ccffcc

什么是Misfit(失配)?

就是设计与上下文之间的"不适应":

  • 功能上:不满足需求
  • 性能上:太慢、太占内存
  • 安全上:有漏洞
  • 可维护性上:难以理解、难以修改

关键转变

传统:"这个设计应该满足X、Y、Z"(主观、难验证)
Alexander:"这个设计不应该有A、B、C这些失配"(客观、可验证)

为什么这个转变重要?

因为"失配"是可以被客观检测的。

  • 功能失配 → 测试失败
  • 性能失配 → 基准测试不达标
  • 安全失配 → Security Scanner报警
  • 可维护性失配 → 复杂度分析超标

应用到AI编程

graph LR
    A[AI生成代码] --> B[失配检测器<br/>Misfit Detector]
    
    B --> C{有Misfit?}
    
    C -->|是| D[❌ 拒绝]
    C -->|否| E[✅ 接受]
    
    D --> F[重新生成]
    F --> A
    
    style E fill:#ccffcc
    style D fill:#ffcccc

核心思想

将"选择"这一行为,从主观判断转变为客观、可重复的工程验证

不问"这个代码好不好"(主观),而问"这个代码有没有失配"(客观)。

Misfit列表就是约束系统。

这正是Valéry框架中的"选择者"的具体实现。


自动化失配检测器的实现

如何实现"自动化的选择者"?

通过多层次的失配检测器:

Layer 1: 测试驱动生成(TDD/BDD)

最强大的约束。

graph LR
    A[需求] --> B[写Misfit列表<br/>测试用例]
    B --> C[AI生成代码<br/>组合者]
    C --> D[运行测试<br/>检测Misfit]
    D --> E{有失败?}
    E -->|是| F[❌ 有Misfit<br/>拒绝]
    E -->|否| G[✅ 无Misfit<br/>接受]
    F --> C
    
    style G fill:#ccffcc
    style F fill:#ffcccc

工作原理

  1. 先写失配列表(测试):

    • 登录失败时不应该泄露"用户是否存在"(安全失配)
    • 密码验证不应该有时序攻击漏洞(安全失配)
    • 连续失败5次应该触发速率限制(功能失配)
    • 日志中不应该包含密码(安全失配)
  2. 让AI生成代码(组合者)

  3. 运行测试(失配检测):

    • 所有测试通过 → 无失配 → 接受
    • 有测试失败 → 有失配 → 拒绝,重新生成

这就是Alexander的"消除失配"在AI时代的实现。

Layer 2: 属性测试(Property-Based Testing)

比TDD更强大的约束。

TDD验证具体案例

属性测试验证不变性

  • 对于任何输入X,输出Y绝不能为null
  • 对于任何用户ID,查询结果的类型必须一致
  • 对于任何有效邮箱,验证函数必须返回true
graph TD
    A[属性测试] --> B[生成随机输入<br/>1000次]
    B --> C[AI生成的函数]
    C --> D{违反不变性?}
    D -->|是| E[❌ 发现Misfit<br/>拒绝]
    D -->|否| F[✅ 无Misfit<br/>接受]
    
    style E fill:#ffcccc
    style F fill:#ccffcc

失配类型

  • 违反类型不变性(类型失配)
  • 违反边界条件(逻辑失配)
  • 违反业务规则(语义失配)

Layer 3: 静态分析与Linting

自动检测代码风格、安全漏洞、反模式。

失配检测项

  • 代码风格失配(不符合团队规范)
  • 安全失配(SQL注入、XSS漏洞)
  • 性能失配(O(n²)算法可优化为O(n))
  • 可维护性失配(函数过长、复杂度过高)
graph LR
    A[AI生成代码] --> B[ESLint]
    A --> C[Security Scanner]
    A --> D[Complexity Analyzer]
    
    B & C & D --> E[失配报告]
    
    E --> F{有失配?}
    F -->|是| G[❌ 拒绝]
    F -->|否| H[✅ 接受]
    
    style G fill:#ffcccc
    style H fill:#ccffcc

Layer 4: 契约测试(Contract Testing)

确保AI生成的代码遵守API契约。

失配类型

  • 返回类型不匹配契约(类型失配)
  • 缺少必需字段(结构失配)
  • 违反语义约定(语义失配)
graph TD
    A[API契约] --> B[定义Misfit]
    B --> C[响应格式错误]
    B --> D[缺少必需字段]
    B --> E[类型不匹配]
    
    F[AI生成代码] --> G[契约验证]
    
    C & D & E --> G
    
    G --> H{有违反?}
    H -->|是| I[❌ 有Misfit]
    H -->|否| J[✅ 无Misfit]
    
    style I fill:#ffcccc
    style J fill:#ccffcc

多层协同

graph TD
    A[AI生成<br/>盲目变异] --> B[Layer 1: TDD<br/>功能失配]
    B --> C{通过?}
    C -->|否| Z[❌ 拒绝]
    C -->|是| D[Layer 2: 属性测试<br/>不变性失配]
    D --> E{通过?}
    E -->|否| Z
    E -->|是| F[Layer 3: 静态分析<br/>质量失配]
    F --> G{通过?}
    G -->|否| Z
    G -->|是| H[Layer 4: 契约测试<br/>集成失配]
    H --> I{通过?}
    I -->|否| Z
    I -->|是| J[✅ 通过所有选择器<br/>可交付]
    
    Z --> A
    
    style J fill:#ccffcc
    style Z fill:#ffcccc

核心机制

将AI混乱的"盲目变异"置于一个严酷的"选择性保留"过滤器之下,只有"适配"(Fit)的代码才能进入交付。

这就是Valéry框架 + Alexander理论在AI编程中的实现。


从概率到确定:张力的消解

回到之前的张力问题:

AI特性工程要求约束系统的作用
概率性输出确定性交付只有通过所有测试的输出才被接受
不可解释可追溯性通过Git记录生成历史和测试覆盖
质量波动质量保证多层失配检测器筛选低质量输出
graph LR
    A[AI概率输出<br/>1000种可能] --> B[约束系统<br/>失配检测]
    
    B --> C[通过所有层]
    B --> D[被某层拒绝]
    
    C --> E[✅ 确定性交付<br/>5-10种可靠方案]
    D --> F[❌ 重新生成]
    
    F --> A
    
    style A fill:#ffcccc
    style E fill:#ccffcc

关键洞察

不是改变AI(让它变确定),而是在AI的概率性和工程的确定性之间,插入一个自动化的选择层

AI仍然是概率的、黑箱的、波动的。
通过约束系统的筛选,最终交付的是确定的、可靠的、符合标准的。

类比内燃机

内燃机AI编程
爆炸是混沌的AI是概率的
气缸约束能量方向约束系统筛选输出
输出是可靠的动力输出是可靠的代码

约束不是限制AI,而是使能AI。

没有约束系统,AI的概率输出无法用于工程交付。
有了约束系统,AI的生成能力得以释放并转化为价值。

这就是两个模式的协同

  • 模式1:把"代码实现"推给AI(最强组合者)
  • 模式2:通过约束系统筛选Common Cases(Good Enough)

Valéry框架提供理论,Alexander提供方法,内燃机提供隐喻。

三者结合,形成AI编程的完整工程范式。


第五部分:边界与思考

任何工程方案都有边界。让我尽可能诚实地讨论这个框架的适用范围和局限。

适用场景的边界

AI编程不是万能的。它的边界不仅来自技术本身,还来自行业规范、商业现实和验证方法的差异。

边界1:技术特性的边界

graph TD
    A[软件类型] --> B[应用层开发<br/>✅ 非常适合]
    A --> C[系统底层<br/>⚠️ 谨慎使用]
    A --> D[安全关键<br/>❌ 需要形式化验证]
    
    B --> B1[CRUD操作]
    B --> B2[前端组件]
    B --> B3[API集成]
    B --> B4[标准模式]
    B --> B5[Common Cases多]
    
    C --> C1[数据库引擎]
    C --> C2[操作系统]
    C --> C3[编译器]
    C --> C4[Edge Cases关键]
    
    D --> D1[医疗设备]
    D --> D2[航空控制]
    D --> D3[金融核心]
    D --> D4[零容忍错误]
    
    style B fill:#ccffcc
    style C fill:#ffffcc
    style D fill:#ffcccc

这是技术维度的边界,相对好理解。但现实更复杂。


边界2:行业和监管的边界

并非所有行业都允许"Good Enough"。

graph TD
    A[行业类型] --> B[互联网/SaaS<br/>✅ 允许快速迭代]
    A --> C[金融/医疗<br/>⚠️ 严格监管]
    A --> D[航空/核能<br/>❌ 形式化验证]
    
    B --> B1[敏捷开发OK]
    B --> B2[炸了再修OK]
    B --> B3[快速试错]
    
    C --> C1[需要审计追踪]
    C --> C2[变更需审批]
    C --> C3[合规要求高]
    
    D --> D1[零容忍错误]
    D --> D2[数学证明必需]
    D --> D3[DO-178C等标准]
    
    style B fill:#ccffcc
    style C fill:#ffffcc
    style D fill:#ffcccc

具体例子

互联网公司(✅ 适合AI + Good Enough):

  • Facebook的座右铭:"Move fast and break things"
  • 云原生思维:先上线,监控指标,炸了再修
  • A/B测试:直接在真实用户上验证

金融机构(⚠️ 部分适合):

  • 不能"炸了再说",每笔交易都要准确
  • 但也不需要形式化验证
  • 商业现实:通过测试覆盖 + 审计 + 监控,达到可接受风险

航空航天(❌ 需要形式化验证):

  • 软件需要符合DO-178C标准
  • 关键代码需要数学证明(形式化验证)
  • 一个bug可能导致机毁人亡

关键洞察

不是"技术上能不能用AI",而是**"行业规范允不允许这种开发方式"**。

某些行业明确禁止敏捷方法,要求瀑布式开发 + 完整文档 + 形式化验证。在这些场景下,"Good Enough"不是工程选择,而是法律和伦理问题。


边界3:验证方法的边界(Dijkstra的幽灵)

Edsger Dijkstra有句名言:

"Program testing can be used to show the presence of bugs, but never to show their absence."
(测试可以证明bug的存在,但永远不能证明bug不存在。)

graph LR
    A[验证方法] --> B[测试驱动<br/>经验性验证]
    A --> C[形式化验证<br/>数学证明]
    
    B --> B1[证明bug存在]
    B --> B2[不能证明不存在]
    B --> B3[✅ 适合AI编程]
    
    C --> C1[数学证明正确性]
    C --> C2[可以证明不存在bug]
    C --> C3[❌ AI难以胜任]
    
    B --> D[Common Cases<br/>够用]
    C --> E[关键系统<br/>必需]
    
    style D fill:#ccffcc
    style E fill:#ffe6e6

两种世界观的对立

测试主义(云原生、敏捷):

  • 写足够多的测试
  • 覆盖Common Cases
  • 生产环境就是最好的测试
  • 哲学:完美不可能,快速迭代胜过延迟完美

形式化主义(Dijkstra、航空航天):

  • 用数学证明程序正确性
  • 不依赖经验验证
  • 在代码运行前就证明它是对的
  • 哲学:关键系统容不得一丝概率风险

现实的权衡

场景验证方法AI是否适用
Web应用测试 + 监控✅ 非常适合
金融系统测试 + 审计✅ 部分适合
医疗设备测试 + FDA验证⚠️ 谨慎
航空软件形式化验证❌ 当前不适合

关键问题

AI生成的代码,本质上是概率性输出。你可以通过测试增加信心,但永远无法获得数学级别的确定性。

对于需要形式化验证的系统,AI编程(至少在当前)是不适用的。


边界4:商业现实的权衡

理论很美好,现实很骨感。

graph TD
    A[商业现实] --> B[理想世界]
    A --> C[真实世界]
    
    B --> B1[完整的形式化验证]
    B --> B2[零bug交付]
    B --> B3[充分的开发时间]
    
    C --> C1[通过测试就行]
    C --> C2[炸了再修]
    C --> C3[时间窗口紧张]
    
    B --> D[❌ 成本太高<br/>市场不等人]
    C --> E[✅ 务实选择<br/>可接受风险]
    
    style D fill:#ffcccc
    style E fill:#ccffcc

真实的商业场景

场景A:创业公司的MVP

  • 预算有限,3个月窗口期
  • "完美设计"意味着错过市场
  • 现实选择:AI快速生成,测试覆盖关键路径,先上线验证需求
  • 权衡:可能有bug,但死于完美比死于bug更不值

场景B:云原生的Site Reliability

  • Netflix的哲学:"Chaos Monkey"随机搞破坏
  • 不追求零故障,追求快速恢复
  • 现实选择:监控 + 自动恢复 > 完美代码
  • 权衡:接受小规模故障,换取快速迭代

场景C:企业级系统

  • 需要稳定,但也需要响应业务变化
  • 现实选择:测试覆盖 + 灰度发布 + 回滚机制
  • 权衡:在稳定性和敏捷性之间找平衡

Dijkstra vs. 云原生

维度Dijkstra理想云原生现实
验证方法形式化证明测试 + 监控
正确性数学保证统计可信度
故障态度零容忍快速恢复
哲学完美主义务实主义

关键洞察

大部分商业软件,选择的是"务实主义":

  • 不是因为形式化验证不好
  • 而是因为成本和机会窗口不允许

这就是为什么"Good Enough"不是降低标准,而是商业现实的理性选择


四个边界的总结

graph TD
    A[AI编程的边界] --> B[技术边界<br/>能不能]
    A --> C[行业边界<br/>允不允许]
    A --> D[验证边界<br/>够不够]
    A --> E[商业边界<br/>值不值得]
    
    B --> B1[应用层✅<br/>系统底层⚠️<br/>安全关键❌]
    
    C --> C1[互联网✅<br/>金融⚠️<br/>航空❌]
    
    D --> D1[测试验证✅<br/>形式化验证❌]
    
    E --> E1[快速迭代场景✅<br/>关键系统⚠️]
    
    style B1 fill:#ccffcc
    style C1 fill:#ffffcc
    style D1 fill:#ccffcc
    style E1 fill:#ccffcc

不是简单的"能用"或"不能用",而是:

  1. 技术上:这个场景AI是否擅长?
  2. 行业上:规范是否允许这种开发方式?
  3. 验证上:测试驱动是否足够,还是需要形式化证明?
  4. 商业上:投入产出比是否合理?

大部分商业软件处于"可用区"

  • 技术上:应用层开发
  • 行业上:互联网、SaaS、企业软件
  • 验证上:测试覆盖 + 监控告警
  • 商业上:快速迭代 > 延迟完美

少数关键系统不适用

  • 航空航天、医疗设备、核能控制
  • 需要形式化验证
  • 成本高但必需(生命攸关)

类比的局限(再次审视)

我必须再次强调内燃机类比的局限性:

graph TD
    A[内燃机类比] --> B[价值]
    A --> C[局限]
    
    B --> B1[展示普遍模式<br/>两个模式]
    B --> B2[启发思维<br/>跨领域联系]
    B --> B3[历史验证<br/>模式重复]
    
    C --> C1[物理确定性<br/>vs<br/>概率随机性]
    C --> C2[硬约束<br/>vs<br/>软约束]
    C --> C3[可完全建模<br/>vs<br/>黑箱机制]
    
    B --> D[✅ 思维框架]
    C --> E[⚠️ 不能机械套用]
    
    style D fill:#ccffcc
    style E fill:#ffffcc

具体差异的影响

维度内燃机AI编程影响
可靠性气缸100%约束能量方向约束系统90-95%有效AI需要更多验证层
可预测性给定条件结果确定相同输入输出可能不同需要容忍不确定性
约束强度物理定律不可违背逻辑约束可能失效需要冗余验证

但这不影响核心洞察

两个模式(推给最强组件 + Good Enough)的普遍性是成立的,只是具体应用方式需要适应不同领域的特点。


深度与专业化分工

现在让我们讨论一个容易被误解的话题:深度的价值

我之前说"不是所有人都需要深入底层",这不是在贬低深度,而是在讨论专业化分工的必然性

健康的技术生态

一个健康的技术生态,需要不同层次的专业化分工:

graph TD
    A[健康的技术生态] --> B[底层专家<br/>1-5%]
    A --> C[工具构建者<br/>10-20%]
    A --> D[应用开发者<br/>75-90%]
    
    B --> B1[优化核心系统]
    B --> B2[探索新范式]
    B --> B3[推动技术创新]
    
    C --> C1[封装复杂度]
    C --> C2[构建开发工具]
    C --> C3[提供高层抽象]
    
    D --> D1[解决实际问题]
    D --> D2[快速交付价值]
    D --> D3[推动业务发展]
    
    B --> E[✅ 必需<br/>but少数]
    C --> F[✅ 必需<br/>and专业]
    D --> G[✅ 必需<br/>and大多数]
    
    style B fill:#ffe6e6
    style C fill:#ffffcc
    style D fill:#ccffcc

三者都不可或缺

  • 底层专家让技术不断进步
  • 工具构建者让技术变得易用
  • 应用开发者让技术创造价值

Python生态的完美例证

graph LR
    A[CPython核心团队<br/>< 100人] --> B[优化解释器<br/>实现新特性]
    
    C[库作者<br/>数千人] --> D[开发NumPy/Pandas<br/>封装复杂度]
    
    E[应用开发者<br/>数百万人] --> F[数据分析<br/>Web开发<br/>自动化]
    
    B --> G[提供基础设施]
    D --> H[提供工具]
    F --> I[创造价值]
    
    G --> D
    H --> F
    
    style A fill:#ffe6e6
    style C fill:#ffffcc
    style E fill:#ccffcc

关键问题

大部分Python用户(数百万人)不需要理解CPython的GIL(全局解释器锁)实现,这妨碍他们用Python创造价值吗?

答案是:完全不妨碍

他们使用高层抽象(pandas、flask、numpy),快速解决实际问题,创造了巨大价值。

同时,少数专家(< 100人)深入CPython源码,优化性能、修复bug、实现新特性,让整个生态受益。

这就是专业化分工的威力。

AI编程的未来分工

类似地,AI编程也会形成分工:

graph TD
    A[AI编程生态] --> B[约束系统设计者<br/>5-10%]
    A --> C[架构师<br/>15-20%]
    A --> D[应用开发者<br/>70-80%]
    
    B --> B1[研究AI可靠性]
    B --> B2[设计验证框架]
    B --> B3[开发约束工具]
    
    C --> C1[系统架构设计]
    C --> C2[定义模块边界]
    C --> C3[把控整体质量]
    
    D --> D1[使用AI生成代码]
    D --> D2[验证行为正确]
    D --> D3[快速交付业务]
    
    B --> E[提供方法论和工具]
    C --> F[提供架构模板]
    E --> C
    F --> D
    
    style B fill:#ffe6e6
    style C fill:#ffffcc
    style D fill:#ccffcc

每个层次都有价值

约束系统设计者(5-10%):

  • 深入研究:如何让AI更可靠
  • 开发工具:TDD框架、验证工具
  • 类似现在的:编译器作者、框架核心开发者

架构师(15-20%):

  • 设计系统架构和模块边界
  • 定义AI可生成的范围
  • 类似现在的:系统架构师、技术负责人

应用开发者(70-80%):

  • 在约束范围内使用AI
  • 验证生成代码的行为
  • 类似现在的:大部分业务开发工程师

专业化的灵活性

我想特别澄清一点:专业化不是非黑即白的。

graph TD
    A[专业化选择] --> B[水平专业化<br/>跨多个层次]
    A --> C[垂直专业化<br/>深入某一层]
    A --> D[实用主义<br/>够用就好]
    
    B --> B1[全栈工程师<br/>什么都懂一些]
    
    C --> C1[数据库专家<br/>只深入存储层]
    C --> C2[前端专家<br/>只深入UI层]
    C --> C3[性能优化专家<br/>只深入某个维度]
    
    D --> D1[大多数人<br/>学到够用为止]
    
    style B fill:#ffffcc
    style C fill:#ccffcc
    style D fill:#e6f3ff

关键洞察

不是说

  • ❌ "底层专家必须懂所有底层"
  • ❌ "要么全栈要么应用"
  • ❌ "必须选择一个固定深度"

而是说

  • ✅ "你可以选择在某个方向深入"
  • ✅ "即使在同一层,也可以只专注某个子领域"
  • ✅ "大多数人不需要那么垂直"

具体例子

即使都是"数据库专家",也有不同的垂直方向:

  • 有人专注存储引擎(B+树、LSM树)
  • 有人专注查询优化(CBO、统计信息)
  • 有人专注分布式(Paxos、Raft)

他们不需要三个都精通,选择一个深入就够了。

大多数人:实用主义

graph TD
    A[学习深度的选择] --> B[学到解决问题]
    
    B --> C[遇到性能问题]
    C --> D[学一些优化技巧]
    D --> E[问题解决了就停]
    
    B --> F[遇到复杂状态管理]
    F --> G[学一些架构模式]
    G --> H[能用了就停]
    
    E --> I[不需要成为专家]
    H --> I
    
    style I fill:#ccffcc

关键是

  • 不是"不能深入"
  • 而是"不需要为了深入而深入"
  • 学到够用,就可以了

这不是懒惰,这是理性的资源分配。

关键澄清

graph LR
    A[我不是在说] --> B[❌ 不需要深入底层]
    A --> C[❌ 底层不重要]
    A --> D[❌ 所有人都该用AI]
    
    E[我是在说] --> F[✅ 不是所有人<br/>都需要深入所有层次]
    E --> G[✅ 专业化分工<br/>让生态更健康]
    E --> H[✅ 每个层次<br/>都有其价值]
    
    style B fill:#ffcccc
    style C fill:#ffcccc
    style D fill:#ffcccc
    style F fill:#ccffcc
    style G fill:#ccffcc
    style H fill:#ccffcc

就像Python生态

  • ✅ 需要有人深入CPython(Guido van Rossum们)
  • ✅ 需要有人开发高性能库(NumPy核心团队)
  • ✅ 也需要大量人写脚本解决问题(绝大多数用户)

AI编程也会如此

  • ✅ 需要有人研究AI可靠性(学术界、研究团队)
  • ✅ 需要有人设计约束系统(框架作者、工具开发者)
  • ✅ 也需要大量人用AI快速交付(大部分开发者)

三者缺一不可,但不是同一群人。


尾声:一个完整的框架

让我们回顾整个思考的旅程。

核心链条

graph TD
    A[两个哲学] --> B[推给最强组件<br/>+ Good Enough]
    
    B --> C[理论基础<br/>Valéry二元论]
    C --> D[组合者 vs 选择者]
    
    D --> E[历史验证<br/>内燃机]
    E --> F[约束是使能]
    
    F --> G[AI编程<br/>概率vs确定]
    G --> H[约束系统<br/>Alexander失配检测]
    
    H --> I[边界与分工<br/>现实权衡]
    
    style B fill:#e6f3ff
    style D fill:#fff3e6
    style F fill:#f3e6ff
    style H fill:#ffe6f3
    style I fill:#ffffcc

这不是孤立的想法,而是一个完整的、经过验证的、有理论支撑的工程哲学。

  1. 两个模式(实践智慧):把复杂度推给最强组件 + Common Cases First
  2. Valéry框架(理论基础):组合与选择的二元论
  3. 内燃机案例(历史验证):约束如何使能混沌能量
  4. AI编程(当代应用):概率性AI vs 确定性工程的张力
  5. 约束系统(解决方案):Alexander的失配检测理论
  6. 边界与分工(现实权衡):Dijkstra vs 云原生,专业化的灵活性

这篇文章的定位

最后,我想再次明确这篇文章的定位:

这不是

  • ❌ 学术论文(我没做实证研究)
  • ❌ 完整方案(太多细节未解决)
  • ❌ 行业指南(我没有这个资格)
  • ❌ 方法论手册(不是实操指南)

这是

  • ✅ 一个实践者的观察
  • ✅ 对工程哲学的思考
  • ✅ 对历史模式的梳理
  • ✅ 一个开放的讨论

我可能错了。这个框架可能只适用于某些场景,可能是过渡期的脚手架,可能5年后会被证明是幼稚的。

但我认为这值得讨论。

开放的问题

如果你读到这里,我想问你:

关于两个模式

  • 你认同这两个模式的普遍性吗?
  • 你的工作中有类似的"把复杂度推给某个组件"的例子吗?
  • 你如何看待"Good Enough"在工程中的应用?

关于Valéry框架

  • 你觉得"组合者vs选择者"这个框架有启发性吗?
  • 在你的领域,谁是"组合者",谁是"选择者"?

关于AI编程

  • 在你的实践中,AI在哪些场景表现好?哪些不好?
  • 你如何判断任务的粒度?
  • 你觉得哪些人类技能在AI时代更重要?

关于未来

  • 你认为这个框架会长期有效,还是只是过渡期的产物?
  • 5年后,工程师的角色会变成什么样?
  • 还有其他视角或哲学可以解释这个演进吗?

我期待听到不同的声音。

最后的最后

引用Alan Kay的一句话作为结尾:

"The best way to predict the future is to invent it."
(预测未来的最好方法,是创造它。)

我不知道AI编程的未来会是什么样的。

但我知道,思考和探索本身,就是创造未来的一部分。

两个技术哲学,一个工程实践。

把复杂度推给最强的组件。
在常见场景下足够好就够了。

这是我从纯AI实现项目的实验中提炼的思考,结合Valéry的创造理论、Alexander的设计哲学、以及内燃机的历史启示。

可能对,可能错,但值得讨论。

欢迎讨论、批评、改进。

让我们一起探索,AI时代的工程师,应该是什么样子。


本文约12000字,阅读时间约25分钟

文中所有案例和图表均为说明性质,实际应用需根据具体场景调整

感谢阅读。如有错误或不同见解,欢迎指正。