1. 先搞清楚一件事:你说的“朴实易读”在工程里叫什么
其实每次有人到我这儿来问“怎么让AI写得朴素一点”,我第一个反应都是:你先把你自己要什么,用工程的词汇翻译一遍。因为如果你自己都说不清什么叫“朴实”,那AI、agent、哪怕是人,都没法替你执行。
你描述的这种需求,在软件工程里是有明确说法的。它不是玄学,也不是审美偏好,而是一整套早就被前人总结过、踩过坑、写成原则的东西。
最常见的几个说法:
- 可读性优先(Readability First):代码是写给下一个维护者看的,不只是写给编译器看的。在代码评审里,读代码的时间远多于写代码的时间,所以“一眼能看懂”比“写得聪明”更有价值。
- KISS原则(Keep It Simple, Stupid):用最简单的方式完成任务。不是不能用复杂方案,而是当简单方案够用时,不引入额外复杂度。
- YAGNI(You Aren't Gonna Need It):不为“万一以后用得上”的需求提前做设计。很多人让AI越写越复杂,就是因为在对话里不停追加“顺便支持一下”“预留一个扩展位”,结果生生把一个两天的需求拱成了两周的架构。
- 最少惊讶原则(Principle of Least Astonishment):一个函数的命名、行为、返回值都应当符合调用者的直觉。看到
getUser()就应当返回一个用户对象,而不是可能返回null、可能抛异常、还可能顺便改库。 - 低耦合高内聚(Low Coupling, High Cohesion):模块之间依赖少,模块内部关系紧密。本质上也是为了能单独看懂一块,而不需要把整个系统都装在脑子里。
- 防御式编程的反向警告:很多程序员以为“防御式编程”就是每个函数开头都加一堆空值判断。实际上,真正的防御式编程是区分“内外部边界”的,在外部边界做防御,在内部逻辑保持清爽。让AI在所有地方都做防御,代码就会变成一堆if。
有意思的是,当你跟AI agent说“写得简单一点”,它不一定能理解。但当你跟它说“遵循KISS原则、避免防御式编程滥用、优先使用显式辅助函数拆解逻辑、去除YAGNI视角下不需要的抽象”,它是可以理解的——因为这些概念在它的训练语料里出现次数足够多,它知道对应的代码形态长什么样。
所以,传达需求的第一步不是研究怎么给agent下指令,而是先把你的风格要求翻译成它认识的原则词汇。
2. 为什么AI越写越复杂?根子往往不在模型,在你自己
我在带团队做AI辅助开发的时候,观察到一个特别常见的现象:一个人工智能agent写的代码,一开始还挺干净,但聊到第20轮的时候,代码已经膨胀成了一个带工厂模式、策略模式、还有事件总线的微框架。
这时候大家的第一反应是怪agent“过度设计”。但你把整个对话记录拉出来看,你会发现里面至少出现过五六次这样的输入:
- “可不可以顺便支持一下XML格式?”
- “以后如果换数据库怎么办?加个抽象层吧。”
- “这块逻辑后面可能被多个地方复用,先封装成服务。”
每一条你看起来是“提问”或“讨论”的话,agent全部都解读成了需求。因为大语言模型的对话机制本身就是:你说什么,它就把它当作上下文约束。
这就是YAGNI原则在AI协作里最容易失效的场景。
真实工程里,如果你跟一个同事说“以后可能换数据库”,你的同事会问“现在要换吗?不换我就不做”。但agent不会问,它会非常热心地帮你在今天就把未来十年的架构演进全部安排上。因为它没有成本概念,它不知道多出来的这几百行抽象,未来三个月里每一次改需求都要跟着一起改。
那怎么治?
我自己在实践里折腾出一套很管用的方法:给agent设立“复杂度预算”。
什么意思?就是你在需求里明确告诉它,允许使用哪些结构、禁止使用哪些结构、最多允许几层抽象。这比空泛地说“保持简单”要扎实得多。
举个例子,我在项目说明文件里会写一段这样的话:
本项目的代码风格要求:
- 优先使用普通函数和显式数据传递,禁止引入IOC容器、事件总线、动态代理等机制,除非任务说明中明确要求。
- 单个函数禁止超过40行,超过时必须拆分为多个语义明确的辅助函数。
- 禁止在数据访问层之上再包一层Repository,直接用领域服务调用数据访问对象。除非有多数据源需求并已显式声明。
- 不要添加项目未要求的配置项、配置文件、扩展点、接口抽象。
- 每个文件顶部用三行以内注释说明“这个文件干什么、被谁用、边界在哪”。函数体内尽量少写行内注释,能用命名说清楚的事就不要用注释解释。
每次agent看到这一段,产出的代码风格都会明显收敛。它仍然会有想发挥的冲动,但至少有了边界。边界不是靠模型自觉守住的,是你反复在需求里强调、在代码评审里纠偏出来的。
另一个特别隐蔽的复杂度来源,是“让AI解释代码”。
很多程序员写完代码之后,喜欢让agent“解释一下这段逻辑”,agent就会用一堆架构词汇把代码讲得好像一个分布式系统。你听完觉得,哇,原来我写的这个模块这么牛。然后你就舍不得简化它了。这其实是语言的自我实现预言。代码本身可能只有十行,agent的解释却在暗示它的设计很精妙。
所以我一般要求团队成员:要解释,就解释“这个函数在什么条件下被调用、读写了哪些数据、返回什么”,不要解释“这是基于某某模式设计的”。如果agent对代码的解释需要大谈设计模式,那基本说明这代码设计得太复杂了。
3. 把风格要求传达到位的四种渠道,不只是“提示词”
很多人以为把需求传达给AI agent就是写好对话开头的那一段提示词。其实,真正规范的软件工程环境里,风格约束的传递至少应该有四个渠道。
3.1 第一层:项目级规范文件,先让agent“读得到”
现在主流AI编程工具普遍支持在项目里放一个类似AGENTS.md、CLAUDE.md、.cursorrules之类的项目记忆文件。不同的工具叫法不一,但逻辑一样:新会话开始的时候,AI会自动读取这个文件里的内容,把它当作环境上下文的一部分。
这里有个关键认知:这个文件不是写给agent看的“魔法咒语”,它本质上是项目干系人对项目的共同假设。
你团队里每来一个新人,第一周会问什么问题?项目结构是什么、依赖怎么加、代码风格什么标准、哪里是核心逻辑不能乱动。这些信息以前靠口头传,后来靠wiki,现在应该落到这个文件里。
我在自己的项目里会分四个区块来写这份文件:
- 项目目标和边界:这个项目解决什么问题、明确不做什么。
- 技术栈和结构约定:允许用哪些框架、目录怎么组织。
- 代码风格标准:长度限制、命名规范、抽象层级限制。
- 工作流程约束:改代码之前先列影响范围、单次修改不超过哪些模块、测试要覆盖什么。
这四部分的信息密度远大于你每次对话前临时想出来的提示词。而且它对所有新会话生效,不需要你重复讲。
3.2 第二层:任务级提示词,每轮对话都要有“人话约束”
光有项目规范文件还不够,因为agent经常会“看了但不执行”。大模型对长上下文的注意力天然会分布在对话靠后的位置。如果项目文件是三千字的长文,而你的最新需求是“把那个按钮的颜色改一下”,它很可能就只顾着按钮,忘了前面关于代码风格的长篇大论。
所以我习惯在每个任务提示词的末尾,都附加一个简短的“本任务约束”尾巴。哪怕只是重复一句“保持改动范围最小,不重构无关代码”。
经验上,这句话写在“任务描述之后”比写在“任务描述之前”效果更好。原因也挺直观的,新指令替代旧指令的倾向,在最新位置上的指令权重更高。
3.3 第三层:示例驱动,给它看“这就是我要的样子”
讲再多抽象原则,不如给它看一小段你认可的代码长什么样。agent对模式识别的能力远强于对规则推理的能力。你给它一段“符合本项目风格”的函数示例,它就会以这个示例为模板去生成其他函数。
这个技巧我用了很多次,效果出奇地好。
有一次我在项目里推“纯函数优先”的风格,团队里有个agent动不动就想在模块里搞单例对象、搞全局状态。我在那个仓库的约束文件里放了一个大约18行的纯函数示例,这个示例没有什么高深技巧,就是输入一个状态对象、返回一个新对象,完全没有副作用。从那以后,agent生成的代码里,纯函数的比例大幅上升。
为什么?因为示例在视觉上比一堆文字规则更容易被模型“抄作业”。规则是抽象的,模型需要在规则和代码形态之间做一次映射;示例是具体的,模型可以直接对齐,你给什么画风,它就能临摹出什么画风。
3.4 第四层:评审闭环,让风格问题“被看见”
很多个人开发者在用AI编程的时候,缺少一个环节:代码评审。
在团队协作里,代码评审是风格约束的最后一道关卡。一个人写得再自由,只要有另一个人类在评审时打回来说“这里过度设计了啊”,风格就不会彻底跑偏。但个人用AI,没有这个评审者,于是agent生成的代码就会在几十轮迭代里逐步失控,而且没人拦得住。
我有段时间是一个人维护一个开源项目,用AI辅助写了不少代码。后来我自己给自己定了个规矩:每个merge请求合入前,必须用一句话回答自己——“如果这段代码突然坏了,我能不能在五分钟内定位到问题所在的文件和函数?”如果答案是“不能”,那不管它跑起来多正常,我都会让agent重写,拆成更小更直白的函数。
这一段经历让我意识到:AI编程工具真正改变的不是“写代码”这个动作,而是把“代码评审”这个原本属于团队的环节,推给了每个独立的开发者。你能不能守住风格,取决于你有没有一个可执行的审查信号,而不是取决于你反复看了多少遍。
4. 手把手教你怎么写agent的开发约束文档
这部分上点硬货。我把近一年在多个项目里用过的项目规则文件核心片段摘出来,逐段解释为什么要这么写。你直接参考着改就能用。
4.1 技术栈与约束声明,先划边界
一个典型的问题:你项目用的是Spring Boot,agent有时候会帮你引入一个你没用过的工具库。为啥?因为它觉得“这个功能用XX库很方便”。但每个新依赖都是有维护成本和安全风险的。
约束文件里我一般这么写:
本项目运行环境是Java 17 + Spring Boot 3.x,数据库是PostgreSQL。除非本任务明确说明,禁止引入任何新的第三方框架、工具库或中间件。所有需求都应优先使用现有依赖实现。
这段的用意特别直白:不让它加东西。agent在候选方案里看到某个功能用现有依赖也能实现,只是代码稍微多几行时,它通常会反过来觉得“多几行就多几行吧,总比加依赖强”。
4.2 代码风格量化,让“朴实”变成可以校验的指标
刚才提到“可读性”这个词,但可读性本身没法自动检查。你需要把它拆成可以执行的指标。
我目前用下来比较好用的一套约束清单:
## 代码风格红线 1. 最大嵌套深度不超过四层,超出必须提前返回或拆分函数。 2. 单函数不超过50行(空行不计算在内),超出必须拆分。 3. 相同结构的逻辑必须抽取公共函数,禁止出现两段结构相同但内容散落的代码。 4. 禁止全局可变状态,如有状态共享需求,通过方法参数显式传递。 5. 命名不得使用常见缩写和拼音缩写。临时变量可以短,但公开方法名、参数名、数据库字段必须完整单词。 6. 能不创建类就不创建类,普通函数解决不了的时候再考虑封装。 7. 异常处理分两种情况:外部输入或IO操作需要显式处理;内部纯计算逻辑不吞异常,能早抛就早抛。这条清单本身不是从什么教科书里抄的,是相当多维护场景试出来的。你别小看这套数字,它给agent一个非常确定的判断标准。它不需要去理解什么叫“简洁”,只需要对照着“是否超过50行”“是否嵌套超过四层”来检查自己的输出。
4.3 需求变更的版本约束,对抗需求蔓延
agent的对话历史越长,越容易把早期的临时想法当成最终需求。这是上下文衰减带来的一个很难避免的现象。
所以我在比较大的项目库里,会再追加这么一段:
如果本任务是从既有需求上扩展而来的,请先列出原有实现的关键逻辑,再说明本次新增部分的改动点。如果本次改动会波及超过三个文件的既有逻辑,先停下来,向用户列一个“影响面清单”,等待用户确认后再动手写代码。
这段规约的实际效果是:把“是否要大改”这个决策权重新拿回人类手里。agent不是不能做大规模重构,但做大规模重构的决定,应该由人来下。agent默认应该干的是最小化改动。
4.4 提交与解释模板,把风格约束延伸到提交说明
代码提交说明是一个极容易被忽视的风格约束落点。agent提交的commit message往往非常“AI腔”,什么“feat: 优化若干功能”“refactor: 完善项目结构”,你根本看不出它改了啥。
我在约束文件里会写:
commit message 必须包含以下三要素:本次改动解决的问题、核心改动点、涉及的主要文件。禁止写“优化”“完善”“增强”这类没有信息量的动词。
这个约束还有一个额外的作用:它是代码评审前的自检。agent如果写不出一个清晰的commit说明,大概率说明这次改动是模糊的。强制它写出“改了什么、为什么改”,某种程度上会让它在动手之前先思考清楚。
5. 实操中的提示词模板,直接就能抄
下面这几个提示词模板,都是我在多个编程辅助场景中反复调整过的。你根据自己的实际任务微调就能用。注意,不是让你把它们拼接成一段超长提示词一次性甩给agent。经验上,分段、按阶段给,效果更好。
5.1 任务启动时,用“场景式”语境开场
很多人的提示词第一句就是“帮我写一个XX功能”,这个开场略掉了太多约束。我习惯换一种方式:
我正在维护的项目是一个长期运行的后端服务,代码会被后续多位同事迭代维护。请你帮我实现用户注册接口。项目已有的技术栈是XX,项目规范文件是AGENTS.md,开始写代码前先通读该文件,并先按我下面的约束输出方案,经我确认后再写正文。
注意这里的关键词是“长期运行”“后续多位同事迭代”。为什么这么讲?因为agent缺少场景感。你告诉它“这个代码会被维护十年”,它就会更偏向可维护性;你告诉它“这是一个一周后可能要删掉的MVP验证脚本”,它就自然愿意写得更快更糙。给它场景,就是给它选择风格方向的依据。
5.2 方案评审时,用“反问式”逼它做减法
当agent输出一个明显过度复杂的方案时,硬要它“改简单点”往往不如用反问来引导。我会用这一组问题:
请先回答我三个问题再写代码:
- 这个方案相比最简单的实现方案,额外增加了哪些类、接口或配置?
- 每增加一项,分别是为了解决当前哪个具体需求?如果现在不解决也不会影响当前功能,请删掉它。
- 方案里的抽象模型,在未来三个可预见的迭代里,是否真的会被复用?如果不存在明确复用点,请不要引入。
这一步的本质是倒逼agent进行“需求回溯”。agent会发现自己很多的架构设计其实并没有对应的需求来源,只是在按概率采样的“常规最佳实践”。你多问几次,它就会变得克制得多。
5.3 需求迭代时,用“冻结范围”提醒
在继续对话前,如果你感觉上下文已经很长了,先做一次“范围冻结”:
下面我们会进入新一轮迭代。请先忘记之前所有未被当前需求覆盖的讨论,只保留已经实现的代码作为上下文。现有代码里,不要因为本次需求的引入而主动改动与需求无关的部分。如果后续有改动需要涉及无关部分,请先单独列出,经确认后再做。
这个提示多次让我避免了“改一行需求,全项目被重构”的悲剧。你可以当成一个安全锁来使用。
5.4 复现风格示例时,用“对照法”描述
给示例的时候,光给代码不够,最好再给一段描述性的对照:
请注意,示例代码的好处在于,它没有过度使用设计模式、没有任何装饰性的封装、所有函数都是显式传参。请用同样的风格实现下面的功能。如果新功能没有示例中的对应结构,请用示例中最接近的结构来仿写,不要自创新模式。
为什么强调“仿写”?因为我发现agent在见到示例后,仍然可能因为新功能而“放飞自我”,自作主张换一套它更熟悉的模式。明确要求它“用已有结构仿写”,能把它固定在当前项目的风格轨道上。
6. 常见“风格失控”的现场与排查,全是真实踩过的坑
光写好规则文件还不够,执行过程中一定会遇到各类失控情况。我挑几个高频的场景分享下排查思路,你以后遇到了至少知道往哪个方向检查。
6.1 无论怎么强调,它还是写出一堆“毕业设计”代码
场景重现:你明确说了“保持简单、不要抽象”,结果agent还是给你生成一个AbstractFactory配合Builder再加Strategy的多层结构。
这类情况的根因通常不在agent,而是你要求它实现的功能本身没有明确边界。人话说就是“需求不清晰”。功能一旦模糊,模型为了兜住所有可能性,就会选择最通用的架构形态。
排查动作:先别急着怪agent,花五分钟把需求里所有“尽量”“可能需要”“多种场景”这类模糊词给删掉,把需求改成“单一入口、单一流程、返回单一结果”的明确形式,再让它重写一遍。大多数情况下,代码复杂度会当场掉一半。
6.2 一改旧代码就“顺手优化”,改出一堆风格不一致
你只是让它加一个字段,它把整个类都重写了。这是agent缺少“改动边界感”的典型表现。
排查动作:去看它重写前的代码是不是有风格问题。比如原来有大量的重复代码、废弃注释、长函数,agent在接触代码库之后产生了“我应该顺便清理干净”的判断。
严格来说这个动机是好的,但对大型项目来说,这种行为等同于一个新人入职第一天就重构核心模块。要在规则里明确说:禁止在任务实现过程中重构与任务无关的代码,如有重构建议,可以单独整理成“建议清单”,不能直接执行。
6.3 agent喜欢给自己留后门技巧,写一堆看似聪明的元编程
你让它处理某些重复性的样板代码时,它可能会使用动态代理、eval执行、反射调用、动态拼接等等“魔法技巧”。它觉得这样代码量少、通用性强,但对维护者来说,这类代码的调试成本极高。
排查动作:这种场景我在约束文件里不会泛泛说“不许用魔法”,而是会明确点名禁止项:
禁用语法:反射、eval、exec、动态生成源代码并编译、通过字符串拼接方式动态调用方法、隐式类型转换的黑魔法。
注意,不是所有项目都绝对禁止反射。但如果你追求的是“朴实易读”,那反射这个技术本身就属于“高级用法”,它带来的灵活性和它造成的理解成本通常是等量的。新人接手的时候,一句“这代码为什么能跑起来”能耗掉半天时间。禁止反射可能损失一些编码灵活性,但极大的降了维护时的认知负担。
6.4 上下文太长之后,规则逐渐“失忆”
这是最让所有用agent开发的人头疼的问题。项目等级越高,对话越长,早期的代码风格约束越容易失效。模型不是真的“失忆”,而是当上下文中积压了太多关于业务逻辑的讨论之后,那些“风格问题”占的权重被压低了,模型更倾向满足最新、最具体的指令。
排查动作:如果一段工作会话已经超过大概二三十次的往来,而且你明显感觉到agent的风格开始飘了,不要犹豫,新建一个会话,重新加载规则文件,然后把当前的需求压缩成一段简报贴进去,继续工作。别想着在旧会话里硬掰回来,掰不回来了。
我自己的使用习惯是:每次新开会话前,都会把旧会话中关于需求和约束的关键结论手动整理成一个几行的“会话简报”,在下一个会话开头粘贴。这样既保持了上下文的连续性,又主动丢弃了几十轮无关讨论。
这个技巧看上去很原始,效果却是惊人的好。
7. 把“风格”当工程资产来经营,而不是当聊天参数
走到这一步,你会发现一个很关键的认知转变:编程风格约束在一开始可能只是一种“提示词写作技巧”,但长期来看,它应当是工程资产的一部分,是需要被反复维护和更新的配置文件。
这意味着什么呢?
首先,你的规则文件需要定期迭代。我大概每隔两三周会回看一次自己的agent约束文件,看看哪些规则真的起作用了,哪些规则已经形同虚设,有没有新踩出来的坑需要补进去。这个过程,和你在项目里重构一个核心模块没有任何区别。
其次,规则文件需要版本管理。千万不要把它当成一个随手写的备忘录。我们团队的做法是规则文件和代码在同一个仓库里管理,改动也要走PR评审流程。如果有人觉得某条规则不合适,可以提出来讨论。经过讨论沉淀下来的约束,比某个人拍脑袋写出的一条提示词要坚实得多。
不要小看这些“软性约定”的价值。很多项目最后做不下去,绕不开一个历史原因——代码在多次需求和人员的变迁中失去了统一的可读标准。维护者每打开一个文件都要花很长时间理解作者的思路,变更成本指数级上升,直到没人敢动那套代码,项目就僵死了。
而你说“编程风格朴实、不要复杂化、强化可读性”,本质上是在对抗这个死亡螺旋。
我踩过几次坑之后的体会是:别指望AI agent天生理解你的品味。品味是一种高度个人的东西,模型的默认输出是“大多数人的平均品味”,而平均水平往往意味着中庸中带一点炫技的冲动。你得通过规则、示例、评审闭环,把你的品味一点点“教”给它。
你教得越具体、越系统、越像对待一个真正的工程需求那样对待风格约束,agent给你的回馈就越好。你只是随口说一句“写简单点”,它也只能给你一个随口做出来的简单。
反过来,当你在项目文件里写下“这个项目不要引入无谓的框架,不要预测不需要的未来”,然后用一轮又一轮的代码评审去校准它的时候,你会慢慢发现,AI辅助编程这件事,本质上就是把你自己变成一个更严格、更清醒的工程管理者。你没法当甩手掌柜,但你可以用更低的成本,把手底下的AI变成一个风格稳定、不飘不浪的可靠成员。