减少AI slop:用更少的代码让大模型产出高质量代码
2026/9/4 20:53:23 网站建设 项目流程

最近技术社区里讨论 AI slop 的声音明显变多。Matt Pocock 先开了这个话题,Dex Horthy 在回应时给了一个特别直接的观点:减少 AI slop 的办法,是写更少的代码。第一次看到这句话会觉得像口号,真放到工程流程里才发现,它几乎把问题重新定义了。

AI slop 不是指代码报错,而是指大模型生成出来的那种看起来很完整、实际上冗余、脆弱、难以维护的实现。它最麻烦的地方在于不报错,能跑,测试可能还全绿,但接手的人会非常痛苦。与其费力用更长的提示词去纠正它,不如先让代码库本身没有它发挥的空间。这篇文章不打算站队说“AI 代码都不行”,而是把这条观点拆成可以执行的工程动作:怎么识别 slop、为什么代码量下降后 slop 会减少、以及你在日常开发里怎么把这个原则落地。


1. 先看清楚:AI slop 在代码里是什么样子

1.1 不是所有“能跑的代码”都是好代码

很多开发者对 AI 生成代码的第一印象是“居然能跑”。这个判断标准太低了。AI slop 往往就是那种“能跑,但很别扭”的产物。

我给你一个很常见的例子。让人工智能读取一个本地配置文件,然后把端口打印出来。任务本身只有一个动作,但很多人拿到的结果是这种结构:

class ConfigLoader: def __init__(self, path: str) -> None: self.path = path self._data: dict[str, object] = {} def load(self) -> None: """加载配置文件到内存。""" with open(self.path, "r", encoding="utf-8") as file: self._data = json.load(file) def get_str(self, key: str) -> str: """根据 key 返回字符串配置项。""" value = self._data.get(key) if value is None: raise ConfigError(f"Missing config: {key}") return str(value) loader = ConfigLoader("config.json") loader.load() port = int(loader.get_str("port"))

如果你自己手写,大概率是这样:

with open("config.json", "r", encoding="utf-8") as f: config = json.load(f) port = int(config["port"])

两段代码都能完成同一个任务。区别在于第一段里有一套类、两个方法、一个自定义异常,这些东西在当下并没有为问题增加价值,只增加了查看成本。这就是 AI slop 的典型形态:不是做错了,而是做得过多。

1.2 识别 slop 的三个信号

不同团队对代码风格的标准不一样,但 AI slop 有几个跨团队都能对齐的信号。

第一个信号是抽象数量明显超过需求。一个几十行的功能,被拆成五六个类、接口、工厂、策略,看起来结构清晰,实际只有一个调用入口。第二个信号是出现了大量一次性辅助函数。这些函数理论上“以后可能复用”,实际上除了当前这一个调用点之外没有任何引用。第三个信号是注释在解释“代码做了什么”,而不是说明“为什么要这么做”。

第一类注释是最没价值的。代码做了什么是编译器告诉你的事,AI 只是把代码翻译成了人话。真正有价值的注释是记录约束和原因,比如“这里不能走默认超时,因为上游服务偶尔会慢”。

我识别 slop 时还会看一个细节:中间变量。AI 很喜欢把一个链式调用拆成七八个中间变量,每一行都起一个新名字,好像这样就能增加可读性。可当人review 的时候,这些中间变量只是在增加阅读负担。


2. 为什么“写更少的代码”能压住 AI slop

2.1 生成成本已经很低,评审成本还是你的

AI 一天能生成几千行代码,但你的评审速度没有变快,一小时能认真看完的 diff 仍然是有限的。这是“代码量变少”最直接的理由:生成是模型的,后果是你的。

当 AI 为一个只要 30 行就能解决的需求输出 200 行时,你没有多得到 170 行的能力,你多得到的是 170 行的评审义务。这些代码里还包括异常处理、参数校验、日志输出、防御性判断。每多一个分支,就多一个测试盲区。少写代码,本质上是在压缩未来必须由人接管的检查范围。

所以不要在评审阶段只问“它能不能跑”,要问“这段代码如果出问题,我能在十分钟内定位到吗”。AI slop 恰恰是定位的噩梦,因为它喜欢把问题分散到很多层里。

2.2 模型会顺着你的代码风格“复读”

很多人没意识到,AI 输出的风格很大程度上不是由提示词决定的,而是由上下文里的代码决定的。模型在补全时会模仿旁边文件的写法。如果你给它看的是一个已经堆满中间层、到处都是工厂和配置类的代码库,它就会认为你也想要这种风格。

反过来,如果你的代码库干净、直接、每个函数只做一件事,模型通常会延续这种简洁。Dex Horthy 说减少 AI slop 的办法是写更少的代码,我理解就是在讲这个闭环:代码库里的坏味道会喂养 AI,让 AI 生成更多同样的坏味道。想从源头治理,就得先把坏味道清出去。

2.3 代码干净,上下文里的噪声也更少

现在的 AI 编程工具几乎都要往上下文里塞代码,好让它理解项目结构。但上下文不是越长越好,里面塞满了没用的历史文件、死代码、重复代码时,模型反而会迷失重点。

代码量少的项目有一个天然优势:上下文干净,模型拿到的信号质量高。它不需要在两千行的旧模块里猜测哪些代码还活着,哪些已经废弃。这会直接影响生成质量。


3. 落地第一步:先给代码库本身做减法

3.1 动手写提示词之前,先删掉三样东西

想让 AI 少写代码,前提是你自己的代码已经足够“少”。我会定期在经常让 AI 修改的目录里做一次减法,重点找三样东西:

  • 死代码:搜不到引用,纯留着给心理安慰的函数和模块。
  • 重复代码:同一个逻辑在项目里出现了三遍以上,每个版本还略有不同。
  • 只服务单一调用点的抽象:那个“未来可能用到”的通用方法,通常永远不会有第二个调用者。

清理的实践顺序不复杂。先用静态分析或编辑器的 Find References 找到无引用的函数,再检查 import 列表,最后跑一遍测试,确认清理没有改变行为。很多 AI 辅助工具对重复代码特别敏感,你要是不清掉,它下次修改时会给每一处重复都加上新的注释和防御分支,等于把问题复制一遍。

3.2 能调库、能走标准库,就不要让 AI 造轮子

AI 格外喜欢生成自定义工具函数。任务是把字符串转成日期,它能给你写一个专门的 parser;任务是从 JSON 里读取配置,它能给你写一个配置管理器。

现实是,绝大多数常用操作已经有了标准库或成熟依赖。写更少代码不代表手写得更短,而是尽量不重复已经存在的功能。在让 AI 动手前,我会先确认两件事:这个项目已经有哪些依赖,标准库里对应功能叫什么。然后在提示词里直接告诉 AI:不要新增依赖,不要自定义工具函数,优先用现有库。

只要这条约束写清楚,输出长度会明显下降,更重要的是,代码的可维护性会上升。别人看到的是熟悉的 API,而不是某个只有作者知道的私有封装。

3.3 允许少量重复,不要为了“优雅”提前抽象

不少开发者把“写更少代码”误解成“更彻底的 DRY”,于是要求 AI 把所有相似代码都合并成一个通用函数。这其实会制造另一种 slop:为了追求不重复,生成一个参数巨多、分支复杂、调用点反而更难读的抽象。

判断标准可以定成“三次法则”。同一个结构出现两次,没必要立刻抽象;出现三次,才开始考虑提取公共部分。如果 AI 在任务里主动提出“把这段逻辑抽成公共方法”,而当前只有两个调用点时,我会问它一个简单问题:这个抽象在不同调用点之间的差异,是通过参数堆出来的,还是真的存在公共逻辑?

靠好几个布尔参数去控制不同行为,表面上是少写了一个函数,实际上是把复杂度转移到了调用方。这种代码不是减少,是压缩,压缩到最后反而更难维护。


4. 让 AI 写代码时,把“少”写进工作流

4.1 任务拆小,一次只让 AI 改一个点

AI slop 出现概率最高的场景是:你给它一个大而全的任务。“帮我写一个用户注册模块”这种需求,几乎没有模型能给出干净的小 diff,因为它必须自己决定边界、自己补全所有可能分支、自己组织目录结构。结果就是大段大段的生成代码堆在一起。

我会把任务按变更粒度拆开。先建数据表,再写接口,再写业务处理,每次只让 AI 专注一个点。单次生成的代码越少,出错的面就越小,评审时能覆盖的范围就越大。

一个很实用的提示格式是:先说现状,再说要改什么,最后说绝对不要碰什么。比如:

现有代码:xxx.py 里有一个 load_user() 函数,读取用户信息。 任务:给 load_user() 增加一个 timeout 参数,默认 5 秒。 约束: 1. 不要改动其他函数。 2. 不要新增依赖。 3. 不要给现有代码追加注释。 4. 只输出改动函数的最小 diff。

大部分模型会把这种任务当作“局部修改”而不是“重新创造”。输出质量和稳定性能有明显提升。

4.2 不只告诉 AI “要做什么”,还要告诉它“不要做什么”

很多提示词都在描述期望结果,却忘了划禁区。结果 AI 在完成任务时,“顺手”帮你重命名了函数,调整了变量名,把两个文件的 import 顺序重新排了一遍。这些额外的改动会让 diff 变得特别大,而且每一条都不是你要求的。

在提示词里明确禁区,是减少 slop 最便宜的方式。我常用的约束有这几条:不要新增文件,不要重命名已有符号,不要修改无关函数,不要重构已能工作的代码,不要添加额外注释。看起来限制很多,实际上是在保护旧逻辑不被 AI 的“审美偏好”污染。

4.3 拒绝让 AI 做“顺手的重构”

如果你常用 AI 编辑器就会发现,AI 特别容易在完成任务时把相邻函数一起改掉。它的理由是“让代码更统一”。这种重构的可怕之处在于它经常是对的,所以你会放松警惕。

我现在的做法是:只要看到 diff 里出现和本次需求无关的改动,整个 diff 先不接收。无论那个改动看起来多合理,都要单独确认。这不是不建议重构,而是重构必须基于清晰的意图,不能夹带在 AI 生成任务里混进来。否则你没法判断一次行为变化到底是哪行代码引起的。

4.4 用 diff 评审代替“全量接受”

AI 生成代码后,真正决定质量的时刻是你按下接受键之前。我见过不少工程师,窗口左边是 AI 生成的五百行新文件,右边是旧代码,他们只确认了接口能跑,就直接全量替换。下一次出问题时,根本不知道从哪看起。

更稳妥的流程是要求 AI 输出小 diff,然后只接受你真正看懂的改动。diff 越小,后面的验证成本越低。如果这次生成明显比预期长,不要硬着头皮评审,先把任务再拆细一点,或者补充之前的边界约束。把“diff 过大”当成一种需要回到上游去处理的信号,而不是可以容忍的现状。


5. 别把“少代码”搞成指标:边界、误区和验收方式

5.1 少不是目的,可维护才是

“写更少的代码”有价值,但一旦把它当成 KPI,就会产生新的问题。为了行数少把变量名压成一个字母,为了少写几行把多个逻辑层层嵌套,这种精简比 AI slop 更糟糕。

写更少代码的真正含义应该是:减少不必要的间接层、减少死代码、减少重复抽象、减少防御性冗余。它不等于禁止注释,也不等于拒绝在某些场景下写更长更明确的逻辑。有些地方恰恰应该写长一点,比如安全校验、权限判断、数据迁移脚本,这些场景追求的是显式,而不是优美。

5.2 什么场景下要谨慎使用“少代码”这个原则

如果你在维护一个大型遗留系统,或者要处理严格的审计场景,盲目追求代码精简会有风险。这种项目里,一部分看起来“多余”的代码其实是历史约束留下的,可能是兼容旧数据、可能是处理特定供应商行为。AI 不理解这些背景,它只会根据“这段代码看起来没用”来做判断。

所以我会把这个原则用在两处:新的功能开发,以及我们完全清楚上下文的老代码区域。对于不清楚来龙去脉的杂旧代码,先不要鼓励 AI 做减法,先让人工确认完再决定。

5.3 一套可以自检的验收标准

单看代码行数容易误判。我会用下面这张表来验收一次 AI 生成结果到底属不属于 slop:

检查项通过标准
diff 规模新增代码是否超过了本次需求的合理范围
抽象数量新类、新接口、新工具函数是否都有至少两个实际调用点
改动范围是否只修改了需求相关的函数和文件
注释价值删除注释后,只看代码能否理解行为
编译和测试修改后项目原有测试是否全部通过
回滚成本出问题后能否快速回滚到改动前
交接友好度不熟悉这个文件的同事能否直接看懂

我的经验是,AI 给出的结果只要有三项以上不满足,就值得回到任务描述层面调整,而不是在现有结果上继续打补丁。继续让它在同一段烂代码上补,往往只会生成更多烂代码。

5.4 当输出又开始发胖时的排查顺序

就算约束都写好了,AI 还是可能在某一次突然生成一大段冗余实现。此时不要急着调整模型,按下面顺序排查。

先看任务是否拆得足够小。如果提示词里包含两个以上的独立变更,模型就有机会制造不必要的中转结构。再看上下文里是否有历史烂代码。模型会模仿相邻文件风格,如果旁边就有一个三层的工厂类,它会认为你也需要。然后看约束是否足够具体。“请简洁一点”这种说法几乎没有约束力,要说清楚不新增文件、不加注释、不让现有符号改名。最后看输出中是否出现了和需求无关的改动,如果出现,直接把这次生成作废。

判断一次 AI 输出是不是 slop,先别看风格,先问一个问题:删掉那些“看起来高级”的抽象后,功能是否保持完整?如果答案是完全能保持,那这些抽象大概率是在制造债务。

我自己现在越来越倾向于一个观点:AI 时代,真正值钱的不是生成能力,而是删减能力。能在正确的位置删掉不必要的东西,比让模型多输出一百行“看起来专业”的代码重要得多。代码量少了,AI 能犯错的面积也就小了。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询