ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

AI写代码总在局部最优?用架构约束把它拉回全局最优

AI写代码总在局部最优?用架构约束把它拉回全局最优 这几年最让我上头的一个现象不是AI写不出代码而是它写出来的代码“哪儿都对但就是在不该对的地方对”。你让AI加个缓存它能把缓存写得滴水不漏却完全不去想这个函数压根不该有缓存你让它优化一条查询它能给你造出五个索引唯独没发现慢的根本是那条线上的一次全表扫描。这种感觉就像你请了个装修师傅师傅手艺极好但他没见过你家户型图把卫生间装修得金碧辉煌最后你发现他想打掉的那面墙是承重墙。干得越久我越确定一件事AI写代码的问题根本不在写代码本身而在它总是陷在局部最优里。什么是局部最优拆开说就是它在给定的一小块上下文里找到了看着最合理的解但这块小上下文只是整个系统的冰山一角。上个月我把一个以合肥开发者为主的架构师同盟交流群里的421条历史消息过了一遍挑出了三类从来不会写进文档、但真正决定架构走向的知识。这篇文章就把这三类知识展开讲顺便聊聊我是怎么用它们反过来约束AI、逼它跳出局部最优的。1. AI写代码总在局部最优问题出在三个根子上1.1 上下文窗口就是“视野半径”AI模型的上下文窗口决定了它一眼能看多远。现在主流模型动辄几十万tokens听起来很大但放在真实项目里一个中大型服务常常几百个文件、几万行代码远超过它一次能容纳的范围。更关键的是它读到你提供的context之前并不知道你还有哪些既有约定和边界。用生活化的类比来说上下文窗口就是“视野半径”。你在请求里只贴了一个模块的三百行代码它最多只能看到这三百行。在这个半径内它可以做得非常精细但半径之外的事它既看不见也想不起来。这个特征直接引发了几类典型事故。最常见的是“改签名不改调用方”AI重构成一个函数的时候只看到函数本身的定义没看到下游还有七八个调用点签名一改一片报错。其次是“重造轮子”项目里明明有一个loadConfig方法AI因为没读全工程又写了一个loadConfiguration两个方法语义重复后续维护时大家还得小心翼翼分辨该用哪个。实操建议不要让AI处理它视野半径之外的事。大改动拆成小任务每次喂给它足够的上下文包括调用链、边界约定、异常处理要求。我给团队成员定的规矩是单次AI任务只覆盖一个模块或一条链路要动跨模块结构时先让AI输出影响面清单再动代码。1.2 模型的训练目标不是“最优”是“最像”很多人在第二次遇到AI给出平庸方案时会疑惑它明明知道很多种解法为什么总选最普通的那一种这要说到语言模型的基本原理。大模型在训练时的目标是最大化预测下一个token的概率也就是说它天生倾向于生成“在训练语料里出现频率高、看起来最顺”的代码而不是“在你这套系统里最优”的代码。这带来两个直接后果。第一流行但未必适合的技术栈会频繁出现在AI建议中——Redis、Kafka、微服务、消息队列这些高频词汇只要功能沾边它都倾向于往上堆。第二约束条件缺失时AI会默认选“最常见”而不是“最合适”的方案数据量只有几千条的表它照样建议你上缓存并发只有几十的场景它照样建议你加MQ。这个问题的本质是概率逻辑和工程逻辑的错位。工程逻辑里方案是否合理取决于约束条件团队熟不熟这个技术栈、运维成本能不能接受、现有架构能不能平滑升级。概率逻辑里方案是否合理取决于见过多少次。所以如果不在上下文里明确给出约束AI给你的永远是“人类的平均答案”而不是“你系统的正确答案”。实操建议把项目的关键约束写进一个上下文文件让AI在生成代码前先读一遍。我在后面的第4.2节会给一个可以直接用的模板。1.3 提问方式提前框死了答案的天花板第三个根子出在提问的人身上。大多数开发者问AI的方式是“帮我给getUserInfo加一个缓存”或者“把这段代码改成异步”。这种问法的本质是你已经把方案定死了AI只是在帮你执行方案。它不会跳出来问你“这个函数真的需要缓存吗”“改成异步之后原来的错误处理逻辑要怎么办”。更合适的问法是先描述问题让AI给方案。比如“getUserInfo这个接口每天调用量很大偶尔出现性能瓶颈请你先分析可能的原因、列出一到三个方案、说明每个方案的影响范围选一个最稳妥的给我。”同一个需求两种问法得到的结果完全不同。前者得到一段缓存代码后者得到的是方案评估和推荐实现。我把这叫做“提问即暗示边界”。你提出的是一个方案还是一个需要决策的问题直接决定了AI给你的是局部优化的东西还是全局视角的东西。这也是后面所有实操方法的核心前提想跳出局部最优先改变你和AI的对话方式。2. 架构师群聊里最值钱的话从来都不是技术名词2.1 421条消息里高频出现的三个词我花了一晚上翻完那个架构师同盟交流群的聊天记录一共421条有效消息。先说结论群里讨论得最多的不是某个框架的具体用法也不是某段代码怎么写而是三类抽象得多的东西——边界、取舍、节奏。边界指的是“这个模块该归谁管”“这个能力应该下沉到基建还是留在业务层”取舍指的是“这里先牺牲一点性能换研发速度还是先追求质量”“这个底层组件要不要继续维护”节奏指的是“这块改动是直接一步到位还是分三个版本推进”“先接监控还是先上缓存”。这些内容有一个共同点它们几乎不会出现在任何一篇官方文档里。框架文档告诉你这个组件怎么用不会告诉你什么时候不该用技术博客告诉你最佳实践不会告诉你在你们团队的具体情况下哪里该绕过最佳实践。而这些恰恰决定了架构能否长期维持健康。2.2 为什么这些知识从没被写下来我认识的架构师里十有八九都承认自己“很少写架构文档”。原因很简单写文档的收益是长期且不确定的成本是当下且确定的。项目排期紧的时候没人愿意花半天写一份下周就过时的架构说明。更深一层隐性知识大多以否定形式存在。群里最常见的讨论是“这个方案不行因为……”而不是“这个方案很好”。只有“这个方案为什么不行”这种话通常只停留在对话里很少有人会把它写成文档。代码库里只记录了“最终选用了什么”没记录“为什么没有选用什么”。对后来的人或者对AI来说缺失的恰恰是后者。举一个我真实遇到的例子。某个支付服务早期讨论过要不要引入分布式事务框架讨论到最后放弃了原因是团队人少、场景单一、现有方案够用。但这件事没有任何文档记录。半年后新人接手看代码里没有事务框架以为是大意遗漏又提了一个“引入分布式事务”的方案整个评审会又重演了一遍三个小时前的老辩论。类似的事情不断发生。隐性知识存在的价值恰恰在于它能让后人不再犯同样的错但它又天然地不会被记录。2.3 架构师交付的不是代码是约束条件聊天的另一个收获是群里的架构师们很少说自己“写了多少代码”说得更多的是“我定了哪些规则”。架构师的真正产出是一组约束条件哪些技术栈可用哪些不能碰哪些模块可以动哪些是红线哪种改动可以一步到位哪种必须分阶段。这些约束条件恰恰就是AI缺少的信息。一个大模型看到“提升性能”的需求默认会给出加缓存、上MQ、拆服务这些“标准答案”因为它完全不知道这个项目的约束条件里写着“当前单机支持5000QPS暂不考虑拆分”。如果架构师把约束条件整理出来、交给模型AI的表现会完全不同。所以与其说AI写代码要跳出局部最优不如说它缺的是一张“全局地图”。而这张地图正是架构师手里那些从未被写下来的知识。3. 三类从未被写下来、但应当被写下来的知识3.1 第一类决策上下文——为什么选了A而不是B先定义一下什么是决策上下文。代码里清晰地写着“我们用了异步消息队列”但没有写“为什么不用RPC”代码里写着“这个服务保留了单机部署”但没有写“为什么不做微服务拆分”。前者是代码本身后者是决策上下文。代码是一张结果快照决策上下文是一段思考过程。为什么要特别强调这类知识因为它直接影响你未来能不能正确地改代码。举个例子假设系统里有一个老模块单机跑了好几年代码写得也不算漂亮。新人接手时第一反应肯定是“重构、拆服务”。但如果有人告诉他当初不拆是因为团队只有三个人、业务波动大、单体比微服务更容易快速迭代他可能就会重新评估重构的必要性。缺了决策上下文任何后来者包括AI都会在“为什么这里这么丑”的驱动下做出危险的“优化”。我现在的做法是每做一次关键决策就写三行到五行的ADRArchitecture Decision Record架构决策记录不需要复杂模板固定三个字段就行——背景、方案、原因。比如背景支付回调偶发超时业务方要求提高资源利用率方案暂不引入消息中间件继续使用同步回调本地重试表原因当前并发量低引入中间件运维成本高重试表方案改动最小且可观测。这几行字写起来不到五分钟但价值极高。喂给AI之后它能帮你做两件事一是生成新代码时自动避开那条被否定的技术路线二是在你提出类似需求时主动提醒你“之前已经否决过这个方案”。3.2 第二类不做清单——全局最优常常是减法第二类知识比第一类更反直觉。大多数开发者习惯把知识理解成“怎么做”但架构里很大一部分高阶知识是“不做什么”。我把这类内容叫“不做清单”它记录的是一条条被明确排除的选项。举例某项目的不做清单可能是“不引入ORM框架SQL一律手写”“不引入Redis缓存用本地内存DB兜底”“不做微服务拆分”“不引入前端状态管理库”。每条听起来都很反主流但是放在具体的项目背景下往往是对的。手写SQL是因为业务查询复杂度极高ORM的生成结果不可控不引入Redis是因为实例规模和小并发下本地缓存收益更高、运维成本更低。AI天然是“做加法”的因为它学习的语料里充满了“引入XX框架解决了XX问题”这类教程。让它自由发挥时它大概率会给你引入一个新的依赖。所以就需要把“不做清单”写下来、投喂给AI让它在提方案时先检查有没有踩到红线。我常用的提示词一句话就能说清楚“请先列出这个方案会引入的所有新依赖和架构改动再检查一遍与项目不做清单的冲突项确认没有冲突之后才能写代码。”注意不做清单也不是永恒的。它同样是有时效的约束。所以我会在清单上标注保留日期和负责人半年复盘一次该删的删该加的加。它维护的不是“永远不”而是“当前阶段不”。这个边界想清楚才不会被僵化成教条。3.3 第三类演进节奏——先做什么后做什么不能乱第三类知识最容易被忽略但也最影响全局。架构演进是有节奏的不是一次性大爆炸。翻群聊时大家反复强调一个观点再合理的架构方案如果落地的顺序不对也会变成事故现场。举个例子。假设你的系统遇到性能瓶颈方案是“加一层缓存换数据库”。如果按“先换数据库再加缓存”的顺序做风险极高换库过程中数据迁移就有坑更别说新库调优还需要时间。更稳的顺序往往是“先加监控、确认瓶颈再上缓存、小流量验证最后才评估是否换库”。每一步的验证结果都可能推翻后面的计划。这个顺序就是演进节奏。AI在这个问题上的表现是典型的局部最优它太喜欢“一步到位”了。你问它“这个模块怎么优化”它倾向于直接给你一份完整重构后的代码而不是告诉你“第一步先加日志第二步再调整慢查询第三步观察效果后再决定”。因为它训练到的“重构教程”都是展示最终结果很少展示过程。应对方法也很简单在提示词里强制要求它先给节奏再给代码。我会加这样一句话“请勿直接修改代码。先输出影响面清单再输出分三步执行的迁移计划每步包含验证标准和回滚方案。确认后再开始写第一步的代码。”这条提示词虽然简短但在实际项目里帮我避免了无数次翻车。4. 四招实操把AI从局部最优里拽出来4.1 把“任务式提示词”换成“评审式提示词”先列几个常见的“任务式提示词”“帮我给这个接口加个缓存”“把这段代码改成异步使用CompletableFuture”“给整个服务加上链路追踪”这些提示词给AI的限制太大了相当于你告诉它“方案已经定了你负责执行”。与其这样不如换成“评审式提示词”——你先描述问题让AI先给方案、给影响范围、给取舍然后再写代码。格式上可以在提示词里直接加一句“先不要写代码”。我用过一个模板效果还不错分享给各位我遇到一个问题[具体问题描述]。 请先分析可能的根因列出1到3个候选方案说明每个方案的改动范围、风险、验证成本与回滚成本最后给出你的推荐并解释为什么。 在你给出推荐方案之前不要编写任何代码。 确认方案后再一次性输出完整实现并在实现里注明关键决策点。这套模板的核心很简单把“做”的请求变成“先想再做”的请求。让AI进入评审者角色就是在逼它把视野从“怎么改这一段”扩展到“改这一段会影响什么”。这是成本最低、见效最快的第一招。4.2 给AI一份属于你项目的“架构地图”第二招是建一份项目上下文文件。我推荐在代码库里新建一个文件比如docs/ARCHITECTURE.md把那些从未被写下来的三类知识都收纳进去决策上下文、不做清单、演进节奏。模板大概长这样# ARCHITECTURE.md ## 项目边界 - 本服务只负责xxx域不处理yyy域 - 对外提供HTTP接口内部调用统一走RPC - 不允许跨模块直接引用数据库Repository ## 关键决策 - 支付模块不回滚采用本地消息表禁止引入分布式事务2024-10, 张三 - 用户模块暂不拆分单机部署可支撑5000QPS拆分的运维成本收益不明显2024-12, 李四 ## 不做清单 - 不引入Redis本地缓存Caffeine已足够 - 不引入ORMSQL全部手写 - 不搞微服务当前规模不需要 ## 演进节奏 - 新模块必须先加监控、再上功能、最后优化性能 - 任何数据库变更必须分两批先加兼容字段再切换写入这个文件不需要很长关键是要真实记录那些“不做什么”和“为什么”。使用它的时候在提示词里带上这句话请阅读项目中的 docs/ARCHITECTURE.md 文件并严格遵循其中的边界、决策、不做清单与演进节奏。若发现你的方案与这些约束冲突先停止并说明冲突点。实测下来有这份文件和无这份文件的差别是质变。没有它AI是“基于通用常识在写代码”有它AI是“基于你们项目的全局约束在写代码”。前者是局部最优后者才谈得上全局视角。4.3 先让AI写影响面清单再让它写代码第三招适合跨模块或风险较大的改动。不要一上来就让AI直接改代码先让它输出影响面清单你确认之后才放行。流程大概是给出变更需求要求AI输出“影响面分析报告”报告里必须包含所有受影响的方法、调用方、存储结构、测试用例、风险点人工审核报告确认无遗漏后再提交第二个请求“基于这份影响面清单开始实现”实现完成后让AI对照清单逐项自检。我举一个实际例子。有一次我需要改造一个用户服务里非常核心的登录链路涉及token生成、鉴权、会话管理三个模块。直接让AI写代码它大概率改完一个模块就忘了另一个。于是我先发出请求请分析“改造登录链路”这个需求的影响面不要写代码。 输出格式受影响模块、被调用的接口、可能破坏的现有功能、需要更新的测试、回滚方案。AI很快列出9个受影响的接口、3个存量测试用例和2个风险点。我看完清单之后发现其中一个风险点确实是我之前忽略掉的。然后我再让它基于清单动手。整个过程走下来AI没有漏掉关键模块因为影响面清单天然把它的视野拉到了整条链路。4.4 让AI当评审者自己挑自己代码的毛病第四招听起来有点“自虐”让AI写完代码之后再让它以评审者身份去挑自己代码的毛病。不要指望AI能自己跳出局部最优但“让它扮演另一种角色”往往能打破它的惯性。具体做法是在代码生成后追加一条请求现在请你扮演资深代码评审者审查你刚刚生成的代码。 重点检查 1. 公共接口是否被不必要地修改 2. 是否引入了与项目架构约束冲突的新依赖或新模式 3. 异常路径与边界条件是否都被覆盖 4. 是否存在与其他模块功能的重复实现。 请直接指出问题并给出修改建议。不要因为代码是你写的就手下留情。我试过很多次AI当评审者时找出的问题往往比它自己当作者时考虑得全面。原因不复杂生成代码时它在顺着“最像人类”的概率走评审代码时它切换到“找茬模式”激活的是另一套能力。对开发者来说最实用的收获是这句提醒“你这个改动与ARCHITECTURE.md中的不做清单冲突了需要先说明例外原因。”这正好补上了局部最优最常缺的那块“全局约束”。当然AI的评审不能替代真人的代码评审这个定位要说清楚。5. 避坑实录半年踩坑总结与常见问题速查5.1 AI自信地乱改公共接口编译直接挂这是最典型的坑几乎每个用过AI写代码的人都遇到过。AI重构一个函数时只专注于函数本身对下游调用方视而不见直接把公共方法签名改了然后整个工程编译报错。让人恼火的是它改完还很自信不等编译结果都不意识不到问题。规避方法有两个。第一在提示词里加红线“不允许修改任何被外部调用的公共方法签名如需修改请先列出全部调用方并征得确认。”第二在工程里开启更严的编译检查把对外的API变更纳入CI阻断。经过两次教训后我把这两条都加进了默认提示词里乱改公共接口的现象基本消失。5.2 AI生成的重构方案过猛微服务全家桶警告另一个高频事故发生在问“如何提升系统性能”的时候。AI的教科书式回答通常有三板斧加缓存、加消息队列、拆微服务。但实际上很多系统的瓶颈根本不在架构层而在一条慢SQL或者一个死循环上。AI不会去问“你确认瓶颈在这里吗”它只会按通用最佳实践堆方案。针对这个情况我的做法是在ARCHITECTURE.md的不做清单里明确写了“禁止引入微服务架构当前团队规模和业务量不需要”同时把这条加进每次对话的上下文。实测效果立竿见影AI回答“如何提升性能”时会从索引优化、连接池参数、缓存热点这些更具体的方向去展开而不是动辄拆服务。5.3 团队用AI“刷代码”代码库出现无主代码这个坑属于管理层面的。当AI写代码成本极低之后团队里会出现一种“刷代码”现象一个人一天能提交好几千行新代码但这些代码没有经过充分的业务推演和评审质量参差不齐。更麻烦的是代码库开始出现“无主代码”——没有人能解释这段代码为什么存在、为什么这么写。我的经验是设置两条规则。第一任何AI生成的代码必须由提交者独立讲清楚设计思路讲不清楚就不合入。第二关键模块不搞“AI自由发挥”而是先人工定好接口和数据模型AI只负责填充实现。把AI定位成执行者而不让它决定业务逻辑团队代码的所有权才能保持清晰。这套规则运行了半年代码库的混乱度显著下降。5.4 AI总选最流行方案不选最合适方案这是模型概率逻辑的延续。AI推荐技术栈时天然偏好训练语料中出现最多、教程最多、讨论最热烈的方案。你问缓存方案它八成推荐Redis你问消息队列它八成推荐Kafka。但如果你们的项目只有两个节点、日请求量还不到百万Redis和Kafka带来的运维成本可能远超收益。规避手段就是把推荐范围也约束起来。在ARCHITECTURE.md里列一个“技术栈白名单”凡是生成代码时用到的新依赖必须在白名单里否则要先说明替换理由。这个白名单配合“不做清单”能让AI的建议从“社区主流”切回“项目最优”。5.5 常见问题速查表为方便日常查询我把上面这些坑整理成一张速查表症状根因对症方案AI改公共接口导致编译挂上下文窗口内的视野盲区提示词声明公共签名不可改CI阻断API变更重构方案过猛、全家桶化偏好通用最佳实践在上下文里写死演进节奏与不做清单代码库出现无主代码AI生成过快、所有权稀释提交者必须能讲清设计思路关键模块人工定接口推荐技术栈不匹配LLM概率偏好流行内容维护技术栈白名单与不做清单AI直接改代码不先给方案提问方式预先框定了答案使用“评审式提示词”先方案后实现改动影响面失控缺少影响面分析环节先让AI输出影响面清单审核后再实现最后聊一点个人体会。这套方法实践了差不多三四个月我最大的感受是AI写代码本身不是问题问题在于我们默认它“应该什么都知道”。它确实知道很多但它不知道你上个月为什么砍掉那个方案、不知道你们团队不希望引入新的基础设施、不知道你现在最需要的是稳而不是快。这些东西你不写下来并喂给它它就永远只能按“平均人的水平”给你答案。所以我现在养成两个习惯一是任何关键决策哪怕只有三行也写进项目文档二是和AI对话的第一条请求永远是“先分析、先给方案别急着写代码”。这两件事看起来都是多走了半步实际却让后面所有的“加速”都有了方向和地基。如果你也在被“AI写的代码看着没问题但不敢上线”困扰可以先从这两条习惯试起再配合文中的ARCHITECTURE.md模板去沉淀你们团队的知识。等这些隐性知识真正变成项目的“全局地图”你会发现AI从局部最优到全局最优的距离其实没有想象中那么远。
返回列表