上周,我把“合肥同盟架构师群”里关于AI写代码的421条聊天记录从头到尾翻了一遍。起因是群里连着几天在吵同一个问题:AI写代码单测全绿、格式工整、注释也齐全,为什么一进代码评审就被人拎出来说“这代码看着对,但我们三个月后改不动”?吵到最后,一位干了十五年架构的老哥说了句话让我印象很深:AI写代码不是能力不够,是它只拿到了局部信息,而架构决策需要的恰恰是那些全局的、甚至从来没被写下来的信息。
这句话基本把问题点透了。我翻了421条,发现大家吐槽的点高度集中,归纳下来其实就三类“从未被写下来的知识”。这篇文章就围绕这三类知识展开,讲清楚为什么AI容易陷进局部最优,以及架构师该怎么把这些隐性知识显性化,喂给AI,让产出从“局部正确”往“全局可演进”上靠。
1. 从421条群聊里看到的东西:不是模型笨,是知识断层
1.1 一个反复出现的群聊场景:单测全绿,评审被毙
群聊里高频出现的场景很有意思。A发了一段AI生成的代码,说功能跑通了、测试也过了、代码风格也很干净,结果被B一针见血地指出问题。比如有次大家讨论订单模块加缓存旁路的事,AI写出来的方案在小范围里确实合理——读缓存、写缓存、设置过期时间,一气呵成。但群友B直接反问:支付那边做幂等校验依赖字段顺序,你AI偷偷调整了DTO字段顺序,两个仓库分别跑CI是都绿的,合到一起呢?
这种场景我太熟了。AI在它能看到的那一小块上下文里做出了最优选择,但它看不到相邻服务对这个字段顺序的隐性依赖。因为它没有经历过那次线上事故,没有参与过那两个团队蹲在一个会议室里对接口定义的过程,它的世界里只有仓库里这一小片代码。
群里聊到后面,几乎每一条都在围绕同一个本质问题打转:AI对局部负责,人对全局负责,两者之间缺了某种知识传输通道。
1.2 什么是“局部最优”:下棋时只顾吃眼前那颗子
打个比方。AI写代码像一个下棋只看三步的棋手——它能看到局部的棋子交换不吃亏,但看不到整个棋局的厚薄、劫材和长远势能。单看每一步,都没问题;连起来看,棋形已经散了。
在代码场景里,“局部最优”的表现可以列得很具体:
- 函数内部实现很漂亮,但放在模块里,职责边界却糊了。
- 报错修好了,但同一个改动破坏了隔壁模块依赖的约定。
- 为了少写十行代码,直接跨层调用,绕过服务层访问仓库层。
- 让新增功能在这个时点最快跑通,但三个月后需求一变,只能推倒重来。
这些都是我在群聊里反复看到的真实吐槽。不是AI“笨”,它恰恰是太聪明了,在给定的局部约束里做出了最顺滑的选择。问题是它收到的约束本身就不完整。
1.3 为什么架构师群体对这个问题格外敏感
架构师和AI有一个天然错位:架构师的核心职责是让系统在时间维度上活得久,而AI写代码的第一目标是让当前任务最快完成。这两种目标函数天然就冲突。
群里有人说得挺直白:AI改代码时考虑的是“这个函数现在对不对”,架构师评审时考虑的是“这个模块半年后还扛不扛得住需求变化”。AI评分的信号来自测试、来自当前需求、来自它能看到的文件;架构师评分的信号来自事故记录、历史演进、团队协作习惯,还有一堆说不清道不明的经验直觉。
这也是为什么群里聊了421条都没吵出结果——因为大家都在用“对不对”衡量AI,而真正该衡量的是“有没有把该用的知识用上”。
2. 机制拆解:为什么大模型天然只有“局部视野”
2.1 上下文窗口是个硬边界,而架构决策往往在窗口之外
先说个基础机制。大模型处理代码时有个上下文窗口的限制,不管窗口是几十K还是几百K token,它都不可能把整个企业级代码库一次性装进去。工具的做法通常是检索相关文件片段喂给模型,或者用代码索引提前做一轮筛选。
问题就在这里:检索命中率高,不代表全局约束能传达到。比如你让AI改一个支付回调接口,它可能检索到了这个接口的实现文件和测试文件,但“回调必须幂等”这条约束写在三个仓库共享的wiki里,甚至只存在于某次事故复盘PPT里。AI根本不知道这些。
我在群里看到过更极端的例子。有人让AI优化一段老代码的循环逻辑,AI发现有个字段可以直接从配置中心读,省掉一次RPC。它改得确实没问题,但这个字段在另一个服务里有版本切换逻辑,跨服务引用会绕过版本控制。开发它的老员工早就离职了,代码注释里也没写,AI当然无从判断。
所以局部最优不是模型的“态度问题”,是它物理上拿不到全局信息。
2.2 训练信号和评测指标都偏爱“局部正确”
再往深一层看,训练信号也在强化这种局部性。大模型在预训练和微调阶段看到的样本,大多是“给出一个函数/文件/需求,生成对应实现”。评测基准里最多的也是单点问题:这个函数能不能跑通,这个测试能不能过。很少有评测会问“这个改动三个月后的变更成本是多少”。
RLHF阶段也一样,人类标注员偏好的是“像不像一个资深工程师写的代码”——更别提很多标注本身来自代码风格、注释规范这类显性特征。结果是模型学会了“写得像”,但没学会“扛得住”。群里一位做过大模型训练的架构师点了一句:AI的输出天然向着局部正确、即时可用去拟合,因为这是它学到的目标函数。
这也是为什么很多团队用AI写的代码看起来专业,拆开一看,耦合度惊人。
2.3 报错驱动的修复循环:越修越乱
更隐蔽的一种局部最优,出现在AI修Bug的时候。人类工程师修Bug之前会有个习惯:先想想这个报错是怎么引入的,改动面多大,会不会影响相邻模块。但很多AI工具的逻辑是“看到报错,找到相关代码,直接改”。
结果就是典型的拆东墙补西墙。比如有群友分享过:AI修复一个空指针异常,做法是在调用前加了一行判空,然后默认返回一个空对象。异常确实没了,但调用方的后续逻辑全部被静默跳过,数据对不上了。这种Bug比原来的空指针还难排查。
报错驱动本质上是一个极度局部化的反馈循环:模型看到的信号是“这个测试红了”,它只需要让这个测试变绿。至于这个测试为什么红、这个改动会让哪个下游变红,如果工具没有把信息送进上下文,它就看不到。
3. 第一类隐性知识:项目历史、事故记忆与“为什么不能这么做”
3.1 事故记忆是架构的隐形宪法
群聊里高频出现的一句话是:“当年我们不用这个框架,是因为线上OOM炸过一次。”这句话没有人写进代码注释,没有出现在架构文档里,但它真实地决定了这个团队的很多技术选型。
类似这种“为什么不能这么做”的知识,数量巨大且分散:为什么这个服务不用分布式事务,为什么这个表不允许直接删除,为什么网关层不能加业务逻辑。AI读代码只能看到现状,看不到每个现状背后的血泪。它可能看到代码里有一个“多余的”状态字段,很想“帮忙清理”掉,却不知道这个字段是某个合规需求的产物。
这就是第一类从未被写下来的知识:历史约束与事故记忆。
3.2 依赖和耦合的隐性规则:为什么不能新增反向依赖
群里有人贴过一次挺扎心的评审记录。AI为了实现一个功能,直接在Controller里查了数据库,理由是这个查询很轻量、只在这个接口用。从“让功能尽快上线”的角度看,这么写没问题,甚至少写了不少代码。但架构师看到的是:分层边界被撕开了,未来所有类似的轻量查询都会照着这个样板做,三个月后,Service层就形同虚设。
这类依赖规则通常也不会写下来。像“Controller不允许直接操作Repository”“跨服务调用必须走网关”“外部依赖必须封装在独立的适配层”这些约定,往往只在架构评审的口头和习惯里存在。AI不知道这些,因为它看到的是“有人这么写过”,而且跑得好好的。
群里有句评论我记下来了:AI学到的代码风格里,藏着一堆坏味道的样本。
3.3 落地:把历史约束固化成AI可读的约束文件
第一类知识的解法,不是靠提示词里写几句“请遵循架构规范”,而是把历史约束变成显式的、可检索的、AI能读到的文件。我在自己的项目里维护一份架构约束清单,路径放在代码仓库根目录,文件名就叫ARCHITECTURE_RULES.md,结构大概是这样的:
# 项目架构约束清单 ## 禁止事项 1. Controller 中禁止直接调用 Repository / DAO。 2. 禁止跨服务同步调用新接口,优先走消息总线。 3. 禁止在订单模块直接引用支付模块的内部实体类。 ## 必须事项 1. 所有缓存写入必须走 CacheService 统一入口,不允许直接操作 RedisTemplate。 2. 新增外部依赖前,必须在本文件追加一行说明并链接到 ADR。 3. 修改公共 DTO 字段时,必须在 PR 描述中列出所有下游调用方。 ## 历史事故索引 - 事故 #201:因直接删除 order_extra 表记录导致对账不平,约定该表只能逻辑删除。 - 事故 #87:因绕过网关调用老支付接口导致签名缺失,约定所有外部调用必须过网关。这个文件的价值有两个:一是给AI喂上下文时,只要把它作为背景材料一起发过去,就能把大部分基础约束传达到;二是逼着团队把每次评审、每次事故中的隐性结论沉淀下来。没有这一步,AI永远只能靠猜。
我在群聊里看到过一个更狠的做法:有人把约束文件直接作为测试的一部分,每次AI生成PR时自动检查是否违反清单规则,违反就打回。虽然没有全面解决问题,但已经能挡住至少一半的“局部最优”产物。
4. 第二类隐性知识:架构评审时的品味与验收偏好
4.1 架构师评审时看的从来不是“对不对”
第二类隐性知识更难传,因为它是关于“品味”的。
很多非架构岗位的人以为代码评审是在找逻辑错误、茬语法规范。实际上架构师评审时想的是另外几个问题:这个改动影响面有多大?如果产品需求明天翻转一下,这段代码要动多少?测试能不能支撑未来重构?这个模块边界是不是被侵蚀了?
这些东西很难量化,但它直接影响系统的寿命。群里有个老哥举过例子:AI给一个复杂业务场景生成了一段特别聪明的代码,用了嵌套泛型和函数式技巧,乍一看非常惊艳。评审时的原话是:“这段代码我三个月后要改,得想半天才能看懂它当时为什么要这么写,加班加点的时候谁有精力去解这种谜?”
架构师追求的不是“这段代码现在跑得聪明”,而是“这段代码未来好改”。这个标准在AI的训练语料里几乎不存在。
4.2 AI的“可读性”和架构师的“可演进性”是两回事
这里有一个关键的错位:AI生成代码时会模仿人类工程师的代码风格——变量命名规范、函数拆分得当、注释清晰,看起来可读性很高。但可读性高不等于可演进性好。真正的可演进性取决于模块边界是否清晰、依赖方向是否正确、接口抽象是否稳定。这些属性在单个文件里看不出来,要在整个系统的维度看。
我见过一个真实的群聊案例:AI重构了一个数据同步模块,把原先分散在三个文件里的逻辑统一到了一个工具类里。代码变得很短,可读性也提升了,但代价是三个原有业务模块共同依赖了同一个工具类,相当于把三个模块的耦合强制收拢到了一起。后来其中一个模块要改逻辑,另外两个全都得回归。这比原来的重复代码更难维护。
架构师看到的是这种变化,AI看到的是代码重复。这就是典型的品味差异。
4.3 把品味变成显式检查项:AI提交前的五个自问
品味虽然难传授,但可以拆成可执行的检查项。我整理了一份让AI在提交代码之前必须回答的问题清单,效果比单纯说“请写出高质量代码”好得多:
1. 这个改动会影响哪些外部接口或内部契约?请列出所有直接调用方。 2. 如果需求从一个字段扩展成一组字段,现有设计是否需要改动? 3. 这个改动是新增能力,还是修改既有依赖?修改依赖是否做到向后兼容? 4. 新增的逻辑是否只放在该放的层/模块里?跨层了吗? 5. 能否用现有测试表达这次改动的主要行为?如果不能,为什么?把这份清单放进提示词或Agent的工作流里,AI会在动手前先做一次影响面的思考。虽然它给出的答案不一定完整,但至少会把那些架构师在意的话题引入上下文,避免完全闷头写码。
群里有个观点我觉得说得特别好:你不能让AI替你做架构判断,但你可以逼它把架构判断需要的信息先摆出来,再由人来判断。信息摆出来的过程,就已经筛掉了一半的局部最优。
5. 第三类隐性知识:团队语境、口头契约与命名背后的江湖
5.1 同一个词,在不同团队里含义根本不同
第三类知识往往最隐蔽,也最容易被忽略。它就是团队语言环境里的语义差异。
“用户”这个词,在运营团队可能指注册账号的人,在支付团队可能指实际付款的人,在B端系统里可能指企业客户。AI生成代码时看到代码里有User这个实体类,它不知道这个User在特定业务场景下到底代表谁。一个群友分享过:AI生成了一批营销活动的代码,把“会员等级”和“客户等级”两个概念混用了,因为在他的代码库里这两个字段名字非常像,语义却完全不同。这种错误单测测不出来,只有熟悉业务的人一眼能看出来。
架构评审里大量时间花在澄清“你说的是哪个实体、哪个环节、哪个版本”上面。这些是团队语境,是默认知识。新员工需要几个月才能掌握,AI如果不去读取术语表或领域模型文档,永远只能靠字面意思猜。
5.2 接口契约不一定在YAML里:口头共识的隐形力量
很多团队里,接口契约并不存在于某个规范平台里,而是靠聊天群、评审会、甚至走廊里的几句话沉淀下来的。
比如“新增服务只走消息总线,不允许同步HTTP调用”这种约定,可能只出现在一次架构评审的会议纪要里,甚至只在某位架构师脑子里。代码仓库里看不到这条规则,AI自然不知道。它可能为了实现一个“实时”效果,直接发了个同步请求过去,功能是通了,但整个系统的异步边界和削峰能力全废了。
这类口头契约还有一个特点:它们往往是对立过、妥协过、权衡过之后的结果。没有参与过整个过程的人,很难理解为什么有时候明明同步调用更简单,却非要走消息总线。AI不知道背后的权衡,所以它会反复尝试已经被淘汰的方案。
5.3 让团队共识变得可调用:术语表 加 契约测试
解法还是那句:把隐性知识显性化。
我建议每个项目维护一个领域术语表,不用很复杂,就是一张词条对照表:
## 术语对照(按业务域分组) 支付域:用户 = 实际付款账号;客户 = 签约企业;会员 = 子账号。 订单域:订单号 = 业务主键;订单ID = 数据库自增主键,禁止对外暴露。 履约域:发货单 = 拆单后的最小履约单元;物流单 = 承运商跟踪号。喂给AI之前,先把这张表和上面的约束文件一起放进去,能明显减少语义混淆类的问题。
契约测试更重要。把那些口头约定编成自动化测试,比如“订单服务不得直接依赖支付内部实体”这种约束,写成单测或静态检查规则,通过CI强制保障。这样即使AI看不到口头约定,测试失败也会把信息反馈给AI,至少它知道这条路走不通。
我在群聊里说过一段话,后来被好几个群友转发过:如果你没把这个团队的共识写下来,那你不能怪AI不懂。它不是没有搜索能力,是根本没有索引目标。
6. 实操:三步把AI从局部最优往全局最优拽
6.1 先搭全局骨架,再让AI填肉
很多团队用AI写代码的方式是“直接提需求”,比如“帮我实现一个订单取消接口”。这是一个典型地把全局判断全部压给AI的动作,AI只能用拾取到的局部上下文凑一个答案。
更靠谱的方式是先把全局骨架喂给AI。我实际跑过的流程大概是这样的:先给一个仓库结构说明(哪个目录是什么模块),再给约束文件(前面写的ARCHITECTURE_RULES.md),再给这个改动对应的ADR或设计说明,最后才是具体需求。
提示词模板大概是这样的:
请你作为本项目的后端工程师,实现订单取消功能。 背景说明: - 项目采用DDD分层,Controller -> Application -> Domain -> Infrastructure。 - 涉及订单聚合根、支付流水表、履约任务表。 - 取消订单必须走统一状态机,不允许直接改状态字段。 约束文件(必须遵守): 见 ARCHITECTURE_RULES.md 术语表: 见 GLOSSARY.md 请先做影响分析: 1. 取消操作会影响哪些聚合、哪些事件、哪些下游调用? 2. 新增的领域服务应该放在哪个模块? 3. 如何保证幂等? 给出影响分析后再写实现。一个很直观的对比:同样的需求,没有这些背景文件的AI会直接生成一个“看起来完整”的实现,但层可能跨了、幂等可能漏了;喂了背景文件的AI至少会在输出的第一步先回答影响分析,把架构师关注的维度摆出来。
群里有个做嵌入式架构师的兄弟分享过同样的经验:他们团队用AI生成固件代码之前,必须先把芯片的内存布局、中断优先级表和硬件抽象层接口喂进去,不喂的话AI经常写出“功能正确但会爆内存”的代码。
6.2 把评审反馈回填成结构化规则
用AI写代码最大的问题不是一次生成不好,而是同类问题会反复出现。如果不做反馈闭环,每次都要靠架构师在评审会上给AI“擦屁股”。
我自己的做法是:每次评审发现问题后,把问题归类,回填到约束文件里。比如这次AI生成了跨层调用,就在约束文件里加一条“Application层不得直接使用Repository”;这次AI改了公共DTO字段顺序,就在文件里加一条“修改共享DTO必须先列全下游影响面”。
重点是频率。刚开始的时候大概每周要补充几条,跑两三个月后,新增约束的速度会明显降下来。因为很多高频规则已经写完,后续评审发现的都是一些边角问题。这个过程本质上就是在逐渐补齐前文说的三类隐性知识,让AI的知识库越来越接近一个老员工的认知。
群聊里有人把这种文件戏称为“AI的入职手册”,我觉得很贴切。新员工入职要读的东西,AI写代码之前也该读一遍。
6.3 让AI在写码前先做影响分析,而不是直接动手
这一步听上去反直觉,但实测效果很好。我一贯的做法是要求AI在任何代码生成之前,必须先提交一份“改动影响分析”,包含:
- 改动涉及的外部接口和内部契约。
- 可能的兼容性风险点。
- 需要同步更新的文档、测试、配置。
- 回滚成本评估。
之前我也觉得这会拖慢速度,实际跑下来发现,这个步骤花的时间会从后续返工里省回来。因为AI直接写代码时经常会在第三四步才暴露出影响判断问题,重写成本不比写分析低。而且架构师评审的时候,如果看到AI先交了一份合理的风险清单,会明显降低对“返工”的担心。
有个细节值得提一下:这套分析并不要求AI完全分析正确,因为AI不可能见过所有历史。但只要它开始列出影响面,架构师就能在最短时间内发现它漏了哪个模块,在动手前就把问题扼杀掉。这比等AI写完了再整体打回高效得多。
7. 我自己落地之后的几个反直觉结论
7.1 提示词里放约束文件,比让AI“扮演架构师”更有效
一开始我也试过在提示词里写“你现在是一位资深架构师,请从全局角度考虑问题”这种话,实测效果很虚。AI会写出一堆冠冕堂皇的设计说明,但代码里该跨层还是跨层。改成把真实约束文件塞进去后,行为立刻变得不一样。这说明AI真正依赖的是信息,不是角色扮演。
7.2 让AI先写设计说明,总时间反而更短
之前我担心强制要求影响分析会降低AI写代码的爽感,后来发现团队里真正容易出错的任务,恰恰是那种“需求一句话、影响一大片”的改动。先写设计说明虽然看起来多了一步,但至少把架构师从“评审时逐行找雷”变成了“对着分析补漏”,效率高了许多。
7.3 约束文件要当代码一样维护
最后一个体会是,约束文件不是写一次就完事的。它本质上是一份活的知识库,需要随着系统演进持续更新。我的习惯是把它和ADR放在一起管理,每次架构决策、事故复盘、评审纠偏都顺手更新两条。时间长了,这份文件会变成团队最有价值的“隐性知识显性化资产”,不只是给AI用,新员工入职也一样受益。
回到最开始的问题。AI写代码总在局部最优,不是因为它智商不够,而是因为它拿到的信息不足以支撑全局判断。那些从来没有被写下来的知识——历史事故、评审品味、团队口头契约——才是架构师与AI之间真正的分水岭。愿意花时间把这些知识整理出来、喂给AI的团队,才能让AI从一个“局部聪明”的工具,变成一个真正参与系统演进的协作者。