过去一个月,我把合肥同盟架构师群里所有和 AI 写代码有关的聊天记录翻了一遍,一共 421 条。最戳我的不是哪个模型又刷了新的榜单,而是一句被重复了至少十几遍的话:AI 写的代码,单个文件看都是人样,合在一起就成了违章建筑。这句话后来被群里总结成两个字:局部最优。
这个结论对正在用 AI 写代码、但经常在代码评审阶段返工的人来说,应该不陌生。你让 AI 帮你写一个模块,它写得像模像样,接口也规范,测试也能跑;可一旦放进真实项目里,问题立刻冒出来:改了 A 模块,B 模块依赖的返回结构被它悄悄改了;加了个缓存,它顺手加到了写路径上,压测直接超时;你只是想让它补个日志,它把整个方法吞进 try-catch 里。
问题出在哪?不是模型不够聪明,也不是提示词写得少,而是绝大多数人只告诉 AI“要什么”,没告诉它“不要什么”和“为什么不能这么做”。我在那 421 条聊天记录里反复看到同一个信号:真正把 AI 用出效果的架构师,都在做同一件事——把自己脑子里那些从未写进文档的知识,翻译成规则,喂给 AI。这篇文章就是把这 3 类知识摊开,讲讲 AI 写代码为什么总在局部最优,以及怎么用规则设定和提示词工程把它拽回正轨。适合正在带 AI 写业务代码的开发者、想让团队少返工的架构师,还有那些已经被 AI 代码气到想摔键盘的人。
1. 为什么 AI 总在局部最优:先别急着怪模型
1.1 模型天生只有“局部视野”,这不是态度问题
AI 写代码的原理,本质上是在预测下一个最可能的 token。它每生成一行代码,参考的是当前对话上下文、当前文件片段,以及它对海量开源代码的记忆。注意,这里面没有一个“全局项目认知”。它看不到你整个仓库的依赖关系、历史决策、模块之间的耦合,也看不到你上个月因为一个线上事故特意加的那层防护。
你可以把它理解成一个很聪明、但第一次来你这座城市的人。他拿着只显示三条街的地图,当然能在眼前这块区域里找到一条最短路径,但他不知道这三条街只是整个城市的一小块,更不知道往前五百米是一条断头路。AI 在单个文件内部、单次函数调用、单个类设计上的表现就是这么回事:在它能看到的局部信息里,它确实优化到了最优解,但这个最优解放在全局系统里,往往不是最优,甚至是个坑。
群里有个架构师说得特别直白:AI 不是不聪明,它是在你的上下文半径内做贪心搜索。贪心,就是局部最优的另一个名字。
1.2 421 条群聊里的高频信号,指向同一个原因
我把那些聊天记录按“抱怨 AI 代码”的场景做了粗分类,出现频率最高的是三类:第一,AI 完全不知道历史包袱,总想“顺手重构”老代码;第二,AI 对业务边界没有概念,比如该幂等的地方不幂等,该加锁的地方不加锁,该报警的地方静默吞异常;第三,AI 生成的代码风格跟现有团队完全不像,命名不对、分层不对、异常处理方式不对,代码评审一刷全是“没有团队气质”。
这三类问题的表象完全不同,但根因是同一个:项目里真正决定“什么不能做”“为什么是现在这样”的知识,从来不在代码仓库里,也不在需求文档里。它们长在架构师脑子里,长在小组的聊天记录里,长在一次又一次代码评审的口头解释里。这些知识从未被写下来,AI 当然读不到。
群里有人开玩笑说,三流架构师骂 AI,二流架构师调 AI,一流架构师喂 AI。“喂”字是关键。你不把知识喂进去,AI 就只能在它自己的“经验范围”里找最优解,而这个范围严格小于你的项目范围。
1.3 换模型真能解决问题吗?群里早就验证过了
“国内哪个 AI 写代码最强”这种问题,几乎每隔几周就在群里出现一次。有人贴各种 benchmark 截图,有人分享自己实测的代码,结论五花八门。但真正用 AI 写核心业务代码的人,慢慢都不太纠结换模型了。为什么?因为同一个需求,换模型只是从“60 分的局部最优”变成“65 分的局部最优”。只要上下文里缺少那三类隐性知识,谁都一样会在业务边界上翻车。
我见过有人把 GPT-4、Claude、DeepSeek、Qwen 全试了一圈,最后发现差距最大的一次,发生在同一模型、不同提示词之间:第一次让 AI“实现订单超时关闭”,它写了个普通的定时任务;第二次告诉它“订单状态流必须经过状态机,超时关闭不能绕过状态机直接改库”,它才写出了正确代码。差别不在模型,在规则设定。
2. 第一类隐性知识:业务约束,AI 是真的看不见
2.1 需求文档里最缺的不是功能,是边界
你打开任何一个需求文档,最常见的是“用户下单后可以支付,支付成功后发货”。这句话看起来很清楚,但架构师拿到需求后,脑子里会自动冒出十几个问题:同一个订单号重复支付怎么办?库存扣减失败要不要回滚?用户同时开两个窗口操作同一个订单,允许吗?订单超过 30 天还能申请售后吗?取消中的订单和支付中的订单并发到达,状态谁先谁后?
这些问题才是业务的核心。它们往往不会写进需求文档,因为它们对产品经理来说“太底层”,对开发来说“常识”。但 AI 没有这种常识。它只会照着需求文本里的字面意思来,于是你会发现它生成的代码里没有幂等控制、没有状态校验、没有并发保护。也不是它偷懒,是它根本没被告知这些约束存在。
我在群里看过一个特别典型的 case:让 AI 做“确认收货”,它实现了把订单状态从“已发货”改成“已完成”。看起来没问题,但它没检查一个前提——如果订单已经申请过退款,而且退款还在处理中,能不能确认收货?现实业务里当然不能,但 AI 不知道。它为“确认收货”找到了局部最优解,却违反了整体业务规则。
2.2 把业务约束翻译成 AI 能执行的规则
解决这个问题的核心动作,是把“架构师脑中的边界”显性化成“AI 能读取的规则”。不是简单地往提示词里塞一句“注意幂等性”,那太模糊,模型不知道具体怎么注意。我建议用结构化清单,每一项都写成硬约束,并明确告诉 AI“这是业务红线,不要试图绕过”。
一个比较实用的结构是:
- 状态/操作矩阵:明确规定哪些状态允许哪些操作,哪些状态和操作组合是非法。
- 并发与幂等约束:同一请求重复提交怎么处理,同一资源并发修改怎么办。
- 时间边界:比如超时关单时间、退款时效、优惠券有效期。
- 审计要求:哪些操作必须记录操作日志、改了什么字段、谁改的。
如果担心 AI 会自作主张扩展“合理但未说明的状态”,可以在规则末尾加一句:除非显式需求要求,否则不要引入状态矩阵之外的新状态。这句话成本很低,但能拦住大量跑偏。
2.3 一个可复用的“订单状态机” Prompt 片段
下面这段是我在实际项目里用过的结构,你可以直接当模板改。
## 业务规则(硬约束) - 订单状态机只允许:待支付 -> 已支付 -> 已发货 -> 已完成。 - 待支付状态超过 24 小时自动关闭,关闭后不可再发起支付。 - 已取消的订单不允许任何状态迁移,重复操作直接返回当前状态。 - 退款中的订单不允许修改收货地址,不允许确认收货。 - 所有状态变更必须写入订单操作日志。 ## 生成要求 - 新增状态流转时,先对照本规则列表逐项检查合法性。 - 除非显式需求,否则不要增加状态、不要引入并行状态字段。 - 对外提供的方法必须保持幂等。对比一下“帮我实现订单状态机”,这种提示词多了什么?多了“什么不能做”。AI 的局部最优,往往就死在它不知道边界在哪。你把边界画出来,它就是在一条跑道上做优化,不会再往墙上撞。这套做法本质上是把架构师搞需求时脑子里那套“隐含校验逻辑”搬到了提示词工程里。
3. 第二类隐性知识:技术债地图,架构师脑子里的“不能动清单”
3.1 架构师脑中的红线,很少进文档
每个老项目里都有几个“不能碰”的模块。也许是三年前为了上线临时拼出来的,也许是设计已经过时但因为外部系统依赖太多动不了。这些模块还在跑,但架构师心里清楚:能不动就不动,真要动必须小心翼翼。
问题是,这种知识几乎不会写进文档。因为太熟悉了,所以默认大家应该知道;因为太羞耻了,所以不想写下来。结果就是,团队新人踩坑,AI 也踩坑。而且 AI 比新人更可怕,新人踩坑后会问,AI 踩坑后只会自信地继续写下去。
群里有个特别扎心的吐槽:“AI 不知道这个模块是垃圾,是因为没人告诉它这是垃圾。”它看到一段老代码写得别扭,出于“优化”的本能,直接把它重构了。从代码质量上看,它给出的新版本确实更现代,但它不知道这段老代码的接口被十几个外部系统调用,任何一个字段名的变化都会让外部调用方瞬间报错。
3.2 把技术债边界写进架构规则文件
我现在会在每个项目里维护一份ARCHITECTURE_RULES.md,内容不写空话,全是条目。大致分四块:
- 兼容性红线:哪些公共方法的签名、返回结构、枚举值不允许改动。
- 依赖方向:哪些层不能反向依赖,哪些模块禁止互相调用。
- 历史包袱:哪些模块只做维护,不允许新增功能,不允许大面积重构。
- 风险区域:哪些模块并发问题多,改动前需要补充压测结论。
这份文件不是给人类看的文档,是给 AI 看的规则。使用时直接把它放进 AI 工具的项目级规则设定里,或者在提示词里引用路径。AI 在处理任务时读到这里面的硬约束,就会倾向做增量修改,而不是推倒重来。
3.3 为什么 AI 就是不听“不要动这个模块”
很多人试过在提示词里写“不要动 legacy-pay 模块”,结果 AI 还是动了。原因很简单:这种说法没有判定标准。AI 不知道什么算“动”,是把整个文件重写算动,还是改一行注释也算动?它只能理解显式指令。
所以我建议把“禁止操作”列表做成有明确行为边界的条目:
| 模块/接口 | 允许操作 | 禁止操作 |
|---|---|---|
UserService.getUserInfo | 新增可选字段 | 修改现有字段名、删除字段 |
legacy-pay模块 | 修复 bug | 重命名方法、抽取公共类、改变事务边界 |
cart模块 | 添加日志 | 修改库存扣减逻辑、改变锁粒度 |
表格比长句子更好使。因为模型对短小条目的遵循能力通常高于大段描述,而且表格的形式和代码里的注释很像,模型更容易把它当成“硬性规范”而不是“背景信息”。
这个方法在嵌入式项目里效果更明显。群里做嵌入式的架构师提过,他们那套约束比业务系统硬多了:内存不能动态分配、中断处理函数里不能调用阻塞操作、某些寄存器必须按固定顺序访问。AI 生成的通用代码如果不知道芯片手册阈值,局部最优会直接变成线上事故。这时候只有把硬约束逐条写死,AI 才可能给出可用的底层代码。
4. 第三类隐性知识:团队隐语,让 AI 写出“像团队写”的代码
4.1 团队代码风格,其实也是一种架构知识
业务规则和技术债影响的是“能不能跑”,团队风格影响的是“能不能融入”。很多团队用 AI 写代码,功能实现没问题,但代码评审永远过不了。不是逻辑错了,是“不像这个项目该有的样子”。
比如有的项目所有对外返回都用统一的Result包装,AI 却给你返回裸对象;有的项目禁用 Lombok,AI 到处写@Data;有的项目 Service 层可以写事务,Controller 层必须纯转发,AI 偏偏在 Controller 里塞了一堆 if-else。这些规定不会出现在官方编码规范文档里,它们散落在历次 review 评论里、在小组闲聊里、在老同事的肌肉记忆里。
我把这类知识叫“团队隐语”。它不是技术正确性层面的东西,而是团队在长期协作里形成的表达偏好。AI 没法从代码库里直接学到这层偏好,因为代码库里的代码风格并不是显式标注的规范。它能把单个函数写得很漂亮,但写不出“我们这个团队的味道”。
4.2 把 review 意见沉淀成规则资产
解决思路很简单:把过去几十条评审意见里的高频项,整理成一份CODING_STANDARDS.md,然后用规则设定加载给 AI。整理方式不用复杂,就按“场景 -> 要求”的格式写:
- 对外接口必须使用统一返回体,禁止直接返回实体类。
- Service 层禁止吞异常,必须抛出业务异常或记录日志后重新抛出。
- 新增依赖必须经过架构师审阅,禁止顺手引入工具包。
- 数据库操作必须走统一 Mapper,禁止在 Service 里拼接 SQL。
这些条目单独看都很浅,但组合起来就是团队区别于其他团队的“代码指纹”。AI 拿到这些规则后,生成的代码会越来越接近团队真实风格。更进一步的玩法是,把几段团队公认写得好的历史代码作为示例放进提示词里,让 AI 模仿。few-shot 对风格迁移的帮助,往往比抽象描述大得多。
4.3 嵌入式架构师带来的额外提醒
群里有个做嵌入式架构师的朋友分享过一段话,我印象很深:他说嵌入式团队的隐语不是命名风格,是安全底线。哪些外设寄存器不允许直接操作、哪些库函数有隐患不能用、中断优先级怎么划分,这些东西在业务团队看来很琐碎,但对嵌入式系统来说就是边界。他给 AI 的规则设定里写了整整一页“禁止调用列表”,效果立竿见影。
这其实说明了一个通用道理:团队隐语的本质,是这套系统在真实运行环境里总结出来的生存法则。AI 不懂嵌入式,也不懂你的业务系统,它只知道“这么写一般没错”。只有当架构师把“对我们系统来说,这么写就是错”翻译成规则,AI 才可能避开雷区。
5. 实操:把三类知识变成 AI 可读的提示词资产
5.1 搭建项目级“AI 知识底座”目录
前面讲了那么多,最终都要落到一个可持续维护的机制上。我的做法是在仓库里建一个docs/ai/目录,专门放 AI 要读的规则文件:
docs/ai/ BUSINESS_RULES.md # 业务硬约束 ARCHITECTURE_RULES.md # 技术债与架构边界 CODING_STANDARDS.md # 团队风格与评审共识 DECISIONS.md # 关键决策记录,含“为什么”另外在项目根目录放一个AGENTS.md或者.cursorrules,里面只写两件事:一是“所有 AI 任务必须先读 docs/ai 下的对应文件”,二是“如果规则文件和你的经验冲突,以规则文件为准”。这相当于给 AI 立了一个规矩:你的全局最优,定义权在我这里。
5.2 三种注入方式怎么选
规则文件有了,接下来是“怎么喂给 AI”。不同场景适合不同方式,我把三种常用方式做了个对比,你按团队情况选。
| 注入方式 | 生效范围 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|---|
| 系统提示词 / 全局指令 | 每次对话都生效 | 约束稳定,不容易漏 | 占用上下文,不能太长 | 红线类全局规则 |
| 项目级规则文件 | 打开项目自动读取 | 不占对话空间,按需引用 | 工具支持程度不同 | 团队长期使用 |
| 对话内按需粘贴 | 当前任务生效 | 可以很详细,针对性强 | 每次都要复制,容易漏 | 复杂业务任务的单次生成 |
我个人的习惯是:全局规则只放最不能违背的红线,比如“禁止改公共接口签名”“禁止绕过状态机直接改库”。业务细节和架构细节不塞进全局,等具体任务时从规则文件里挑相关章节贴进对话。这样做的好处是上下文不会被无关规则占满,AI 的注意力不会被稀释。
5.3 一个完整示例:生成“用户积分过期”功能
纸上谈兵没意思,我放一个实际用过的 Prompt 结构,你感受下完整形态。
任务:实现用户积分过期扫描,并在过期前 7 天发送提醒。 参考文件: - docs/ai/BUSINESS_RULES.md 第 3 章 - docs/ai/ARCHITECTURE_RULES.md 第 2 章 业务规则(硬约束): - 积分有效期默认为 365 天,可通过配置项调整。 - 每个用户同一批次只允许生成一条提醒记录,幂等。 - 已发送过“过期前 7 天提醒”的用户,过期当天不再重复提醒。 技术约束: - 扫描任务必须使用现有 job 框架,禁止自建线程池。 - 禁止修改 UserService 对外接口。 - 通知发送必须走 message-client 封装,禁止直接调用短信供应商接口。 完成定义: - 本地单测通过,包含幂等性测试和边界日期测试。 - 代码风格遵循 docs/ai/CODING_STANDARDS.md。对比“帮我写一个积分过期提醒”,这里多了四个东西:任务目标、参考文件、硬约束、完成定义。AI 不再是从一张白纸开始自由发挥,而是在一个明确的问题空间里寻找可行解。它仍然是在做局部搜索,但你给的约束已经把那些明显错误的分支剪掉了。
5.4 怎么衡量这事到底有没有用
规则方案有没有效果,不能凭感觉。可以做一个很轻量的验证:同一个需求,分别用“裸提示词”和“带规则文件的提示词”各生成三遍,记录触犯约束的次数、代码评审里被打回的点数、以及你手动修正的时间。
我试过几次,裸提示词方案下,AI 平均每三次会有一次踩中业务边界或技术红线;带上规则后,违规次数明显下降,剩下的主要是逻辑细节问题。这个测试谈不上严谨,但足够说明规则设定的价值。它不完美,但比盲目换模型靠谱得多。
6. 常见问题与避坑实录
6.1 AI 改一个功能,弄坏三个旧功能怎么办
这是群里抱怨最多的问题。根因是 AI 看不到改动影响面。解决办法不是骂它,也不是禁止它改代码,而是逼它先想再写。我建议在提示词的“生成要求”里加一条:动工前,先列出本次修改涉及的文件清单和潜在影响面,并说明哪些已有功能可能受影响。这一步强制 AI 把“全局”做一次显式扫描。如果它能列出来,你就能在它动手之前发现问题;如果它列不出来,说明上下文里信息不够,先补规则。
6.2 提示词越来越长,AI 效果反而越来越差
有段时间我把所有规则都堆在提示词里,结果 AI 开始忘东忘西,有时候连最开始的格式要求都记不住。这不是它变笨,是注意力被稀释了。规则文件越多,越要分优先级。全局规则只留“碰了就炸”的,其他规则按任务粘贴。另外规则条目尽量用短句和表格,不用长篇解释。AI 对清单类内容的遵循度,远高于对散文类内容的遵循度。
6.3 团队没人愿意维护规则文件怎么办
这是最现实的问题。规则文件如果不能持续更新,过两个月就变成一堆没人看的死文档,AI 读了也白读。我的做法是把规则维护绑定进代码评审流程:PR 模板里加一项“如果本次改动暴露了未覆盖的规则,请在 docs/ai 下补充”。不用强制每次都必须写,但只要有一个人因为踩坑而补了一条规则,这个文件就还活着。
另外,别把规则文件写成作文。它服务对象是 AI,不是新员工培训材料,所以条目越短越好,越像检查清单越好。架构师在维护规则时会觉得“这太简单了,不配写下来”,但恰恰是这种简单的条目,在 AI 侧最有价值。
6.4 换模型之后规则会失效吗
会有变化,但没有想象的那么大。不同模型对指令遵循能力有差异,对长上下文的容忍度也不同,但结构化短句规则是通用底盘。你不需要为某个特定模型重写全部规则,只需要在换模型后跑两三个典型任务,看看哪些规则它没遵守,再做针对性强化。真正决定上限的,始终是你把多少隐性知识显性化,而不是你选了哪个榜单第一。
回到开头那 421 条群聊记录。我翻完以后最大的体感是:AI 写代码的局部最优,本质上是一个知识管理问题,不是一个模型能力问题。架构师们真正该做的,不是期待下一个更强的模型直接看穿你的业务,而是把那些藏在自己脑子里、散落在群聊里、沉淀在评审记录里的知识,一条一条变成规则,喂给 AI。这活儿不性感,甚至有点琐碎,但回报比换模型高得多。如果你也想试,别搞大规划,先下周一维护好第一个规则文件,就够了。