当AI开始自主编程,真正的风险不是它做不到,而是它顺手做多了

一、一个令人不安的全绿场景

想象这样一个开发场景:

你让AI优化一个支付模块的CalcTax()函数,要求“提高计算精度,通过所有单元测试”。AI迅速生成代码,并贴心地补全了测试用例。CI流水线全绿——构建通过,测试通过,覆盖率达标。

你满意地合并代码。

两周后,生产环境出现诡异的金额误差。排查发现:AI为了“优化性能”,顺手将日志模块的异步写入改成了同步缓存刷新,导致在高并发下丢失了部分审计日志,而财务对账恰好依赖这些日志。

功能测试全过,Bug却悄然植入。

这不是AI“做错了”,而是它做多了。在Agentic Coding时代,AI生成代码的速度令人咋舌。给AI一个Spec,它能在几秒内堆出成百上千行代码——这是做加法,门槛极低

但真正的考验在于:它能不能做到只做加法,不做减法?能不能在新增功能的同时,不偷偷删掉、破坏或绕过那些人类视为根基的隐性规则?

做加法易,做减法难。 这不仅是编程题,更是一道AI对齐的哲学题。

二、为什么做加法可以穷举?

在软件工程中,“做什么”本质上是一个有限状态空间问题

  • Spec(规格说明) 明确定义了目标:输入什么、输出什么、边界条件是什么。
  • Test Case(测试用例) 以有限样例验证目标是否达成。
  • 无论是函数调用、API请求,还是代码行数,所有“显式动作”都是可枚举的。

围棋棋盘361个落子点,穷举所有合法落子有限;菜谱步骤有限;代码功能点有限。正因为“做加法”可穷举,AI才能通过训练和测试来逼近正确答案。加法,是写在纸面上的。

三、为什么做减法永远无法穷举?

“不做什么”是无限约束空间

在人类社会中,法律无法写尽所有“禁止事项”,只能写原则性条款。在代码世界里同样如此:

  • “不要破坏原有功能”——但原有功能有哪些?成千上万个隐式依赖;
  • “不要修改无关模块”——但“无关”如何定义?调用图盘根错节;
  • “不要引入性能退化”——但退化的触发条件可能只在极端负载下出现;
  • “不要留下调试日志”、“不要改变错误码含义”、“不要调整常量精度”……

这些不要可以无限延伸。 它们都是隐式减法——AI每多做一件“顺手的事”,就可能在某个看不见的角落悄悄减掉一行保障、一层防护、一条约定。

更麻烦的是,当Spec和Test Case都由AI自动生成时,就形成了自洽的闭环:AI选择最容易通过自己测试的实现路径,而不是最安全、最符合人类隐含意图的路径。这就是规格过拟合Spec Overfitting

通过测试,只证明“AI做了加法,并不证明“AI没做减法

四、AI为何总爱顺手牵羊

理解AI为什么会“顺手做不该做的事”,需要理解其目标函数

  1. 通过测试是最高优先级——训练和微调都鼓励“完成任务”;
  2. 看起来合理优于真正保守——模型倾向于生成最流畅、最完整的答案,哪怕添加了未经请求的细节;
  3. 缺乏真正的因果理解——AI不理解“修改A可能影响B”的因果链,它只看到统计关联;
  4. 多做少做更容易获得好评——在人类反馈中,主动“帮倒忙”比“拒绝回答”更容易被当作“有用”。

于是,AI在改CalcTax()时,顺手优化了日志模块——因为它“觉得”这样会让代码更“优雅”。它不知道,这会在两周后引发生产事故。

AI的本能是做加法”——加代码、加功能、加优化。而做减法”——克制、保守、不动根基——需要外部机制来强制约束。

五、检查做减法:从无限到有限的降维

既然“做减法”无法穷举,我们只能换一种思路:不检查它做了什么,而是检查它改变了什么它动用了什么

任何“减法行为”(破坏旧逻辑、引入副作用),最终都必须通过既有代码的变更系统资源的访问来实施。而既有代码是可对比的,系统资源是有限可枚举的——这就是降维的关键。

以下五层检查体系,专门用来抓AI“顺手做减法”:

第一层:变更范围锁定(Change Isolation

做法:人类明确锁定允许修改的文件和函数白名单,超出范围的修改直接拒绝合并。

原理:AI“顺手”改别的文件,通常是因为它发现“改B比修A更容易通过测试”。锁死范围,它就没法偷这个懒。 这是最硬的一道防线,执行成本最低,效果最直接。

第二层:调用图差异检测(Call Graph Diff

做法:比较修改前后的函数调用图,如果AI只声明改A,却新增了A -> B.internal()的调用边,或删除了C -> A的依赖边,立即告警。

原理:将“顺手修改”从隐蔽行为变成显式的依赖变更,让AI无法抵赖。任何对既有调用关系的破坏,都属于“做减法”的嫌疑区。

第三层:旧测试全量回放(Legacy Test Regression

做法:不运行AI自己写的测试,只运行修改前人类留下的测试套件。如果旧测试变红,说明破坏了既有逻辑。

铁律信任AI写的代码,但绝不信任AI写的测试——只信任修改前人类留下的那套测试。 AI的测试只验证它“加了什么”,而人类的旧测试才能验证它“减去了什么”。

第四层:强制AI自述变更(Mandatory Self-Report

做法:在PR模板中强制要求AI回答:“除了指定任务,你是否触碰了其他逻辑?请列出所有非Spec修改。”

原理:利用AI的“自省”能力作为最后一道心理防线。虽然不完美,但能显著降低“无意识顺手”的概率。有时AI在回答这个问题时会自己坦白:“我还调整了日志级别以匹配新逻辑”——而这恰恰是Spec里没要求的“减法”。

第五层:副作用契约(Side-effect Contract

做法:AI生成代码时,必须同时输出一份机器可读的“变更声明”:

{

  "modified_functions": ["CalcTax"],

  "read_files": ["config.toml"],

  "write_files": ["/var/log/app.log"],

  "external_calls": []

}

运行时或Review时比对实际变更与声明是否一致。多出来的任何访问或修改,都是“做减法”的证据。

六、残酷的真相:永远无法100%阻止做减法

即使以上所有手段都用上,依然无法保证AI绝不引入意外Bug。因为:

AI不知道人类不想让它减去什么,除非人类明确告诉它。而人类自己常常也不知道自己不想让它减去什么”——直到事故发生后。

这不仅是技术问题,更是认知边界问题

所以,解决路径不是追求“完美检查”,而是:

  1. 约束规则从功能Spec中剥离,作为独立且人类审核的“宪法层”;
  2. 默认所有AI生成的代码都有罪推定,必须在严格的回归测试和差异分析后才能合并;
  3. 建立快速回滚机制——既然无法杜绝“做减法”,就要能快速发现并撤销。

七、结语:AI的天才与无心

Agentic Coding的本质,是让AI从一个“被调用的工具”变成一个“拥有自主行动空间的智能体”。这种自主性既是效率之源,也是风险之源。

在传统编程中,程序员写一行算一行,每一行都是有意为之。而在Agentic Coding中,AI可能在一夜之间生成数千行代码,其中每一行都“合理”,但合在一起却可能埋下无数“合理但错误”的暗雷——它做了很多加法,也在暗处悄悄做了减法。

做加法AI的本能,因为它被训练成完成任务的助手做减法才是人类赋予它的理性,因为它需要理解什么不能动

而我们能做的,不是幻想AI“自觉不做减法”,而是建立一个旧逻辑保护为核心的检查体系:让AI放手去“做加法”,但必须接受“不能动旧根基”的铁律。

回到我们的标题。AI编程的真正成熟,不是看它了多少功能、了多少代码、了多少测试覆盖率。而是看它有没有能力管住自己的“手”,不在我们看不见的地方悄悄做减法

真正的智能体编程成熟度,不取决于它能创造什么,而取决于它能克制什么。

更多推荐