一个意外的发现
我最近尝试了一个实验。
不是"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
这不是"降低标准",而是认识到:
-
完美是渐进的,不是一次性的
先做到80分,上线验证,再迭代到90分、95分。 -
真实世界比想象复杂
在实验室追求100%覆盖,不如在真实场景中解决90%的实际问题。 -
时间窗口很重要
晚一年的完美方案,往往输给早半年的"足够好"方案。
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:接受黑箱,转向约束设计
早期工程师面临一个选择:是深入理解燃烧的每个分子运动,还是接受爆炸的混沌性,设计约束系统来引导它?
历史选择了后者。
为什么?
因为"理解爆炸"是个几乎不可能完成的任务(分子级别、量子效应、热力学复杂性),而且不必要。
工程师的智慧:
- 不追求完全理解
- 接受其"黑箱"特性
- 专注于设计能承载和引导能量的外部结构
对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
- 物理约束:气缸壁限制能量方向
- 流程约束:四冲程循环规范化过程(吸气→压缩→做功→排气)
- 时序约束:精确点火控制爆炸时机
- 稳定性约束:冷却系统防止过热失控
对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
具体表现:
- 可靠性:因为推给物理系统,不依赖复杂控制
- 可制造性:因为设计简单(固定循环),可大规模生产
- 可商用性:因为成本可控,能负担得起
这不是"降低标准",而是认清哪些复杂度该推给谁,做到什么程度就够了。
对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
关键差异:
-
物理 vs 概率
内燃机的爆炸本质是确定性物理过程(给定条件,结果可预测)
AI生成是概率性过程(相同输入,输出可能不同) -
硬约束 vs 软约束
气缸壁是物理硬约束(100%限制能量方向)
逻辑约束是软约束(可能被"绕过"或失效) -
可建模 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
人类的四个核心价值:
- 架构设计:定义系统的整体结构和边界
- 约束设计:设计"选择"的标准(测试、类型、契约)
- 粒度判断:决定如何拆分任务(这是组合者需要的输入格式)
- 质量把关:最终的选择者(人工审查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
工作原理:
-
先写失配列表(测试):
- 登录失败时不应该泄露"用户是否存在"(安全失配)
- 密码验证不应该有时序攻击漏洞(安全失配)
- 连续失败5次应该触发速率限制(功能失配)
- 日志中不应该包含密码(安全失配)
-
让AI生成代码(组合者)
-
运行测试(失配检测):
- 所有测试通过 → 无失配 → 接受
- 有测试失败 → 有失配 → 拒绝,重新生成
这就是Alexander的"消除失配"在AI时代的实现。
Layer 2: 属性测试(Property-Based Testing)
比TDD更强大的约束。
TDD验证具体案例:
- 输入"test@example.com" → 返回true
- 输入"invalid" → 返回false
属性测试验证不变性:
- 对于任何输入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
不是简单的"能用"或"不能用",而是:
- 技术上:这个场景AI是否擅长?
- 行业上:规范是否允许这种开发方式?
- 验证上:测试驱动是否足够,还是需要形式化证明?
- 商业上:投入产出比是否合理?
大部分商业软件处于"可用区":
- 技术上:应用层开发
- 行业上:互联网、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
这不是孤立的想法,而是一个完整的、经过验证的、有理论支撑的工程哲学。
- 两个模式(实践智慧):把复杂度推给最强组件 + Common Cases First
- Valéry框架(理论基础):组合与选择的二元论
- 内燃机案例(历史验证):约束如何使能混沌能量
- AI编程(当代应用):概率性AI vs 确定性工程的张力
- 约束系统(解决方案):Alexander的失配检测理论
- 边界与分工(现实权衡):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分钟
文中所有案例和图表均为说明性质,实际应用需根据具体场景调整
感谢阅读。如有错误或不同见解,欢迎指正。