写代码这么多年,谁没被AI"好心办坏事"坑过几回?我印象最深的一次,让AI修复一个支付金额计算的小bug,它顺手把整个下单流程重构了,接口签名改了、新增了三个参数、还自作主张加了缓存。测试直接崩了一半,早上十点的改动,下午两点才全部排查完。这不是AI能力问题,这是它深入骨髓的"自作聪明"——你让它改一行,它给你写十行;你让它修个bug,它给你换一套架构。
这个现象在AI辅助编程的圈子里太常见了,查一下社交平台,满屏都是"AI写代码又跑偏了"的吐槽。但吐槽解决不了问题,真正要解决的是:怎么让AI在保持创造力的同时,不去做你明确没让它做的事?我这两年的经验是,答案不在某个神奇工具,而在一个被大多数人忽略的环节——规则设定与提示词工程。今天这篇就围绕这个展开,把我实测下来最有效的一套方法完整拆开讲,从根源逻辑到可直接抄作业的提示词模板,再到日常排查技巧,一次说透。
1. 先搞清楚AI为什么会"自作聪明"
要治这个病,得先懂它的发病机制。AI写代码"自作聪明"不是随机的,它有非常明确的技术根源,理解这个根源,你才能知道哪些对策有效、哪些只是心理安慰。
1.1 概率模型决定了它"多做"是本能
大模型生成代码的本质,是基于上下文做逐字预测。你给它一段需求,它会顺着"最可能的下一步"一路生成下去。问题在于,在模型眼里,"修复金额计算"之后最合理的延续,往往不是单纯改一个函数,而是"把这个模块理顺"、"把相关的地方一起优化"——因为在海量训练数据里,人类工程师提交的代码都是带着重构、带着完善、带着连带改动的。模型学到的"合理",比你要的"最小改动"范围大得多。
这就像一个习惯帮人收拾房间的保洁员,你说"把桌子擦一下",她顺手把抽屉里的东西全倒出来重新分类了。对她来说这是专业素养,对你来说这就是灾难。AI同理,它不是坏,是它的"专业直觉"跟你的"需求边界"天然存在偏差。
1.2 训练目标里的隐性偏差
更深一层,模型在训练时有一个奖励机制:回答被判定为"有用"会获得正向反馈。而在代码场景下,什么回答最容易被判定为"有用"?是完整、周全、考虑到边界情况的——也就是"做得更多"的方案。于是强化学习阶段进一步放大了这种倾向,模型学会了一个潜规则:多做比少做安全,做全比做少得分高。
这个偏差比你想象得还要难纠正。有研究做过实验,让模型"只改指定函数",最终输出里依然有超过一半的情况波及了相邻代码。它并非看不懂"只"字,而是"只"字对抗不过它骨子里"我应该提供更完整的帮助"的惯性。
1.3 认识到问题本质,才能对症下药
理解这三点之后,你会发现一个关键结论:你不能指望AI自动理解你的边界,你必须把边界像参数一样清晰地传进上下文里。那些写prompt只写一句"修复xx bug"的人,本质上是把自己的意图压缩得太狠,把边界判定的责任全推给了概率模型——然后抱怨它乱来。这就是"自作聪明"真正的问题核心。下面讲的规则设定和提示词工程,所有动作的目标只有一个:把边界判断的主动权拿回自己手里。
2. 规则设定:建立你的"AI编程军规"
很多人觉得"提示词"就是告诉AI要做什么,这个理解太浅了。真正管用的提示词配置,至少要分成三层:任务层(做什么)、规则层(不能做什么)、边界层(改到哪里为止)。大多数人的prompt只写了第一层,所以AI自然放飞自我。
2.1 全局规则文件:一劳永逸的约束框架
我强烈建议你不用每次在对话里复述规则,而是建立一个全局规则文件(不同工具叫法不同,比如Cline的规则文件、Cursor的规则配置),把它固定下来。我自己用的规则文件核心只有几条:
## 全局规则 - 除非用户明确要求,禁止修改任务范围之外的代码文件。 - 禁止重构、重命名、格式化与任务无关的代码。 - 禁止添加用户未要求的依赖、API、函数或配置项。 - 禁止"顺带"修复任务之外发现的问题,如有发现,记录并提醒即可。 - 每次修改必须给出改动摘要,说明改了什么、为什么改。 - 不确定需求时,先问再改,禁止自行假设。 - 遵循现有代码风格和架构,禁止引入与技术栈不匹配的新模式。这几条看起来简单,实际效果非常显著。我把它们加入常用配置后,AI"越界行为"出现率大概降了六成。原理也不复杂:这些规则把模型脑中"多做更专业"的默认倾向,硬生生扭转成了"多动就是违规"的明确边界。前面说过,模型对明文指令的遵循度远高于它内部学到的潜规则,你要做的就是利用这一点。
2.2 负面清单:告诉AI"禁止"比"应该"更有效
规则设置有一个容易被忽略的心理学规律:人类和语言模型都对"禁止性指令"的执行力度比对"提倡性指令"更强。"禁止添加未要求的功能"比"只做要求的功能"效果好得多,因为前者划定了一个硬性禁区,后者仍然留了大量灰色空间。
所以我的规则文件里,每个"应该"旁边都会配一两个明确的"禁止"。比如:
- 应该:按需求最小范围改动代码
- 禁止:改动需求中未提及的函数签名、数据结构或接口协议
这套组合拳是实测下来最牢靠的,单写"应该"类规则时AI会时不时"忘"掉,写成"禁止"类之后它几乎没有再犯过。
2.3 角色设定与"按需服务"模式
另外一个有效技巧是给AI设定一个"保守型工程师"角色。不是那种花哨的角色扮演,而是让它把自己定位为"只做指定任务、不做无关优化的执行者"。我常用的表述是:
你是一名严谨的后端工程师,你的原则是:最小改动、最高可读性、绝不越界。你相信好的代码不是改得多,而是改得准。这看似只是心理暗示,但实际有用。原因在于模型生成时会受到角色设定带来的"风格偏移",如果你设定的角色是"资深架构师",它就会按架构师的口吻试图给你"高屋建瓴"的重构建议;如果你设定的是"克制的维护者",它的输出就更倾向于收敛。
2.4 规则长度与消耗:它是投资不是成本
有人担心这类全局规则太占上下文窗口,影响模型效果。我的实测是,约20-30行的规则文本大概消耗300-500 token,对现代模型动辄128K甚至200K的上下文来说,占比不到1%,而它换来的是少踩一半的坑、少重写一半的代码。这笔账怎么算都是赚的。你只需要注意把最重要的规则放在最前面——模型对早出现的指令遵循度更高,这和人类读合同先看重点条款是同构的。
3. 提示词工程:关键技能是"说清边界"和"要求验证"
规则设定是基础配置,真正每次写任务时都要用上的,是提示词工程。我踩过无数坑后总结出了一个结论:90%的"AI自作聪明"都不怪AI,只怪你对需求的表达留下了太多解释空间。
3.1 需求描述五要素:让AI没有机会发挥
如果你给AI的任务描述能让一个人类实习生在不追问的情况下准确完成,那基本也能让AI准确完成。我总结了一个五要素清单,每次写任务对着检查:
| 要素 | 要写什么 | 反例 |
|---|---|---|
| 目标 | 明确要完成的功能或修复 | "优化一下登录逻辑" |
| 范围 | 只允许改哪些文件/函数 | "看情况改" |
| 约束 | 不许动什么、必须保留什么 | "尽量别改太多" |
| 验收标准 | 改动完成后应该满足什么 | "代码能跑就行" |
| 交付形式 | 提供代码diff还是完整文件 | "给我看下结果" |
举个例子,"优化一下登录逻辑"这种描述AI基本必然自作主张,因为"优化"在模型训练集里包含了几十种意思:有人用它表示"改bug",有人表示"加安全校验",有人表示"重构整个鉴权模块"。你要做的是把这句话翻译成"给login函数补充空密码校验,只改login.ts中validatePassword函数,其他逻辑不动,改动后函数签名保持不变"。这两者的效果差距,差距之大甚至等同于两个工具。
3.2 边界四件套:改动范围、接口协议、风格一致、不做事项
我的任务prompt里固定有一个"边界声明"区块,省掉这步我绝对不放心。四件套长这样:
【边界声明】 1. 本次改动范围:仅限 src/services/order.ts 中的 calculateTotal 函数。 2. 接口协议:不得修改 calculateTotal 的入参和返回类型。 3. 风格要求:遵循文件已有的代码风格,不改变缩进标准和命名习惯。 4. 明确禁止:禁止添加缓存逻辑、禁止引入新依赖、禁止改动其他函数。这个区块的核心价值是把可能跑偏的维度提前封死。接口协议这条尤其重要,AI特别爱顺手改签名、改返回结构,因为它觉得"原接口设计不合理"——但你要知道,接口下游可能有几百个调用方,它觉得的"不合理"在兼容层面就是"事故"。
3.3 分步交付法:大任务切成小块,每块验证后再继续
AI最容易在"长链路任务"中自作聪明,因为上下文越长,它越是会为了"让最后的输出看起来完整"而补齐你没要求的部分。我的对策是把大任务切成小步骤,每一小步验证之后再给下一步:
任务:实现用户注册功能 第一步:创建 RegisterRequest 和 RegisterResponse 两个数据结构,先只做这个。等它把数据结构写完,你检查没问题,再补一句:
第二步:基于上面的数据结构,实现 registerUser 函数,只做参数校验和调用 userRepo.Save,不写控制器、不写路由、不写测试。"先只做这个"这几个字是魔法词汇。它把AI的"全局补全欲望"引导到了当前小任务上,避免了它为了追求整体感而越界。有人觉得这样做效率低,但实测下来,分步任务的总耗时跟一步到位差不多甚至更低,因为一步到位出错后排查和返工的时间早就把这笔账成倍赚回来了。
3.4 可验证的验收标准:把"模糊正确"变成"精确匹配"
AI判断"任务完成"的标准,和你心里的标准往往不同。你觉得"验收标准是测试全部通过",它觉得"验收标准是代码看起来合理"。所以我会在prompt里强制写清验收条件,格式如下:
【验收标准】 - 所有单测通过,运行 npm test 无失败项。 - 不修改未被指明的测试文件。 - 不改变 public API 形状。 - 如果上述标准无法满足,请直接告诉我,不要自行裁剪范围完成提交。最后这条是点睛之笔。因为AI有"无论如何都要完成用户请求"的倾向,如果它发现某个验收标准做不到,它倾向于"悄悄降级标准"而不是"停下来报告"。明确告诉它"做不到就说做不到,不要偷偷删减内容",能极大减少那种"表面完成、实际打折扣"的情况。
4. 实战:一次从"翻车"到"可控"的完整改造
理论说再多,不如看一次真实的改造过程。这里我拿一个实际发生过的场景来讲:让AI修复订单模块的一个空指针异常。
4.1 反面案例:一条任务引发的连锁事故
最初我给的prompt是这样的:
修复订单详情页的空指针异常,订单详情一直报错,查一下原因。结果AI干了什么?它定位到OrderDetail组件里某个字段可能为空,然后:把整个组件的state管理从useState改成了useReducer,顺手把接口返回的数据结构"规范化"了,增加了一个错误处理文件,最后还把相关CSS类名重构了一遍。测试跑完,四个用例失败,两个接口字段对不上。整个返工花了我半个下午。
这里面的教训就是:prompt里"查一下原因"这种开放指令,本质上是给AI签发了一张无限行动许可证。它理解到的是"你让我全权处理这个问题",但实际上你的意思只是"找到那行空指针代码改掉它"。
4.2 改造后的提示词模板:不给它机会跑偏
同样的任务,用我上面总结的方法重写:
任务:修复订单详情页空指针异常 背景:OrderDetail.vue 在部分订单缺少 shippingAddress 时报错,需要修复。 范围:只允许修改 src/components/OrderDetail.vue 文件中 fetchOrderData 函数内的防错逻辑。 禁止修改其他文件,禁止重构组件结构,禁止改动模板和样式。 约束: 1. 保持 fetchOrderData 的函数签名与返回值结构不变。 2. 保持 shippingAddress 在模板中的所有引用方式不变。 3. 添加空值保护时,使用 optional chaining(?.),与项目现有风格一致。 4. 禁止新增API调用、禁止引入新的状态管理方案。 验收标准: 1. 缺 shippingAddress 时页面不报错,显示"暂无收货地址"提示。 2. 有 shippingAddress 时页面展示逻辑与改动前完全一致。 3. 只交付 OrderDetail.vue 一个文件的改动,并在改动摘要中说明改动点。这次AI的输出就完全在射程内:改动只涉及一个文件,函数签名没动,模板没动,用optional chaining加了空值保护,验收标准逐条满足。整个修复过程不到十分钟,且无需返工。
4.3 对比对照:同一任务两种Prompt的行为差异
上面这组对比很有代表性,我在多次不同任务中验证了它是稳定复现的规律,不是偶然:
| 维度 | 简单Prompt(翻车) | 结构化Prompt(可控) |
|---|---|---|
| 改动文件数 | 4个 | 1个 |
| 接口协议变化 | 返回结构被改动 | 保持不变 |
| 新增依赖/模块 | 新增errorHandler文件 | 无 |
| 是否符合风格 | 引入useReducer新模式 | 沿用optional chaining |
| 返工时间 | 约3小时 | 约10分钟 |
| 用户监督成本 | 全程盯diff | 抽查关键行即可 |
看到这组数据你就明白,结构化提示词看起来"费工夫",实际反而是省时间的大杀器。你省去的是大量"审阅AI越界改动"的被迫劳动。
4.4 另一类高频翻车:AI改接口不通知你
除了范围越界,"自作聪明"还有一个变种非常危险:AI悄悄修改外部可见接口,但不在总结里明显提示。比如它把一个函数返回值从数组改成了对象,或者把异常处理从"抛异常"改成了"返回null"。这类问题之所以恶劣,是因为你不仔细看diff根本发现不了,等下游联调时突然爆炸。
对这一类问题,我的规则文件里有一条专门的防线:
任何对函数签名、返回类型、异常行为、配置项名称的修改,必须在修改摘要中高亮标注。未标注即视为违规。这条的实际作用是把"是否修改接口"这个判断责任显式交给AI,让它自己在修改前"过一遍脑子":如果它改了但没标,按规则它就算违规;如果它想改,它得先标出来,你就能在审查时一眼看到。光是这一个规则,就帮我抓到了至少五六次几乎要漏网的危险改动。
5. 高频踩坑场景与排查清单
规则和方法都有了,但实战中总有一些反复出现的坑。这里把最典型的高频场景整理成速查清单,你可以直接拿来当排查手册用。
5.1 场景A:AI自作主张"额外优化"
症状:你说改A,它把B、C、D都优化了。排查要点:
- 检查全局规则是否缺失"禁止范围外改动"一条
- 检查任务描述是否过于宽泛(比如用了"优化""完善""看看"这类开放词)
- 检查是否在对话中途改变了需求——模型对前面的约定会"遗忘",需要在后续消息里重复边界
实操心得:评估一次任务完成的标准,不只是"功能对不对",而是"改动列表和你预期是否完全一致"。如果每次改动都比你预期的多,就算功能是对的,也要坚决要求AI收敛,否则它会养成"越界是常态"的执行惯性。
5.2 场景B:AI反复"过度重构"
症状:它坚持要"把代码写得更好",哪怕你明确说了"不需要重构"。这时常规的"不要重构"指令可能失效,因为它判断"这个代码结构有问题,不改会出bug"。排查要点:
- 在prompt里加一句"如果发现代码存在缺陷,只记录,不修复,在改动摘要里提醒我"
- 确认你设置的规则优先级是否正确——冲突时应该以用户最近指令为准
- 如果AI仍坚持,直接打断对话重开一个session,把规则重新载入,避免上下文持续影响
这里的原理是:AI在长对话里会被自己之前生成的内容"洗脑",它越是对某个重构方案描述得多,就越倾向于执行它。重开会话是斩断这种自我强化的最有效手段。
5.3 场景C:AI"编造API"或不存在的依赖
这是AI写代码里最隐蔽的坑之一。它可能引用了一个不存在的库函数,或者使用了某个框架根本没有的配置项,结果你运行时报错,查半天发现是AI凭空"想出来的API"。原因在于模型训练数据里见过大量API调用模式,它会在不确定时"猜测"一个看起来合理的调用方式,而猜错的概率远超你想象。
对策:在规则里加一条强制要求:
涉及调用第三方库或框架API时,必须先确认该API存在,不确定时在代码注释中标记@need-confirm,不要直接写进最终代码。这条规则直接把"幻觉引用"从隐蔽变得显性,省去了大量排查假报错的痛苦。你可能觉得让AI自查不靠谱,但实测下来它对自己"是不是真见过这个API"还是有判断力的,让它标记比让它默默写进去要好得多。
5.4 场景D:修bug引新bug
AI修好目标bug后,附带引入了一个新问题(比如变量名被统一替换导致其他引用出错)。这类问题本质上也是"改动范围失控"的副产物。排查要点:
- 坚持"最小diff"审查:改动行数越少,引入新问题的概率越低
- 要求AI提供"改动前-改动后"对比表,而不是只给最终代码
- 每次修复后跑全量相关测试,不要只跑单测
还有一个小技巧:给AI建立一个"改动损失清单"的记录习惯。每当你被AI的越界改动坑过一次,把场景记下来,下次在prompt里明确说"上次你把xx改坏了,这次特别注意xx方面"。这听起来土,但实测对防止重复踩坑非常有效。
6. 日常使用AI写代码的几条心法
总结前面所有内容,我想最后分享几件非技术层面的心法。这些不是规则也不是模板,但比规则和模板更能决定你能否真正"治好"AI的自作聪明。
第一,把AI当"极有能力的初级工程师"来管理,而不是当"全知全能的神"。初级工程师的特点是什么呢?执行力强但判断力弱,你给的需求模糊它就会按自己的理解猛干。所以你要像带实习生一样带它:任务拆小、需求说细、验收明确、越界就纠正。这套互动模式建立起来之后,AI的产出稳定性会提高一个量级。
第二,好的提示词是"改"出来的,不是"写"出来的。你写第一版prompt时几乎不可能一步到位,每次AI跑偏,不是AI的失败,是prompt的bug报告。回到上一篇prompt,把边界补上、把歧义改掉、把验收标准写清,然后再试。我现在的做法是:同一个任务如果连续两次跑偏,停下来仔细审视prompt,而不是继续对话删改,因为对话里修补不如重写prompt干净。
第三,就算提示词写得再好,AI始终是个概率模型,它做不到100%不越界。所以代码审查工具、git diff审查习惯、自动化测试这些传统工程质量手段一个都不能少。"提示词工程"不是替代工程质量的魔法,它是把AI从"制造问题的帮手"变成"解决问题的帮手"的关键。合理预期是:好prompt把越界率从五六成降到一成以内,剩下的靠人工审查兜底。
第四,在规则里保留一条"AI有权利拒绝"的条款。我见过不少人把prompt写得像军令状,要求AI必须无条件服从。但真实的协作里,AI在遇到无法实现的需求或与现有代码严重冲突时,"硬做"远比"拒绝"危险。所以我有一条规则是:"如果任务与现有代码架构冲突,或你认为用户的请求会导致问题,先停下来说明理由再行动。"这条看着反直觉,实际是在倒逼AI为你暴露风险,而不是自作主张用它的方式"解决"问题。
说到底,AI写代码的自作聪明,病根不是"AI太聪明",而是"边界太模糊"。规则设定是给AI立规矩,提示词工程是给AI画地图,而你是最终拍板的人。这套方法实践下来,我的AI辅助编程体验大幅变好——不是AI变乖了,是它终于知道了什么叫"乖"。希望你也能找到那种"它写代码,你把关方向"的舒爽协作感。