AI编码助手越写越乱?用Agent Skills约束代码复杂度

发布时间:2026/10/11 10:22:59

AI编码助手越写越乱?用Agent Skills约束代码复杂度 1. 当代码生成不再是瓶颈复杂度成了新的战场最近半年我一直在折腾一件事把 AI 编码助手真正用进日常开发流里。从最初的新鲜感到后来的“能跑就行”再到现在的隐隐不安——我发现一个越来越明显的问题AI 写代码越快代码库烂得也越快。这不是危言耸听。我做过一个粗略统计在一个中等规模的后端项目里让 AI 连续生成大约 40 个功能模块之后代码行数从 1.2 万涨到了 3.7 万但真正被复用的公共函数只有 6 个。剩下的全是“就地展开”的重复逻辑。每个模块单独看都没问题合在一起就是一团乱麻。改一个字段名要翻十几个文件加一个校验规则得在五六个地方同步修改。这就是我想聊的核心话题AI 写完代码以后谁来控制复杂度我的答案是——把《软件设计的哲学》里那套关于“深模块”“信息隐藏”“接口与实现分离”的思想做成一套可执行的 Agent Skills让 AI 在生成代码的同时也接受设计原则的约束。这套东西我断断续续做了两个多月踩了不少坑也总结出一些真正能落地的做法。下面我会从整体设计思路、核心细节、实操过程、常见问题四个维度把整套方案拆开讲清楚。如果你也在用 AI 辅助编码并且开始感受到“代码越写越乱”的痛这篇内容应该能帮到你。2. 整体设计思路为什么要把设计原则做成 Skills2.1 问题的根源AI 的“局部最优”陷阱AI 编码助手有一个天然倾向它只对当前上下文负责。你让它写一个用户注册接口它就会认认真真把参数校验、密码加密、数据库写入、返回格式全部塞进一个函数里。单看这个函数逻辑完整、能跑通、甚至还有注释。但问题是它不会主动去想这个校验逻辑是不是和登录接口重复了密码加密是不是应该抽成独立服务返回格式是不是应该统一这就是“局部最优”陷阱。每一次生成都是局部最优解但全局来看重复、耦合、职责不清的问题会迅速累积。传统开发中这些问题靠代码评审和重构来消化但 AI 生成的速度太快评审根本跟不上重构更是无从谈起。我试过几种应对方式。第一种是“事后重构”等 AI 生成完一批代码再人工梳理。实测下来效率极低因为 AI 生成的代码往往缺乏清晰的边界重构成本比从头写还高。第二种是“提示词约束”在 prompt 里加一句“请遵循单一职责原则”。效果也很有限因为 AI 对这类抽象原则的理解非常表面它会在函数上加一行注释写着“单一职责”然后继续把三件事塞在一起。2.2 核心思路把设计原则翻译成可执行的检查项真正让我找到方向的是重读《软件设计的哲学》时的一个念头这本书里讲的“深模块”“信息隐藏”“避免浅模块”本质上都是可操作的判断标准而不是空洞的口号。比如“深模块”的定义是一个模块的接口应该比它的实现简单得多。这句话翻译成可执行的检查项就是如果一个函数的参数超过 4 个或者函数体超过 50 行或者需要调用者了解内部实现细节才能正确使用那它大概率是一个“浅模块”需要重新设计。再比如“信息隐藏”原则模块应该隐藏那些最可能发生变化的设计决策。翻译成检查项就是如果两个模块共享了同一个数据结构的具体字段并且这个字段可能变化那就应该把这个字段的访问封装起来。基于这个思路我开始把书里的核心原则逐条拆解成 Agent Skills。每个 Skill 包含三部分触发条件什么时候检查、检查逻辑具体看什么、修正建议发现问题后怎么改。这样 AI 在生成代码时不只是“写出来”还会“停下来想一想”。2.3 方案选型为什么选择 Agent Skills 而不是其他形式在确定这个方案之前我对比过几种实现路径。第一种是自定义 lint 规则。用 ESLint 或类似工具写规则在提交时检查。优点是成熟稳定缺点是只能检查语法层面的问题比如“函数不能超过 50 行”但无法判断“这个抽象是否合理”。设计原则的很多判断需要语义理解lint 做不到。第二种是独立的代码评审 Agent。让一个专门的 Agent 在代码生成后做评审输出改进建议。我试过效果一般。因为评审 Agent 和生成 Agent 是分离的生成 Agent 不知道评审标准评审 Agent 又缺乏生成时的上下文两边经常“打架”。第三种就是Agent Skills。把设计原则直接嵌入生成过程让 AI 在写代码的同时就接受约束。这相当于把“设计评审”前置到了“设计生成”阶段。实测下来这种方式对代码质量的提升最明显因为它改变了 AI 的生成行为而不是事后补救。提示Agent Skills 的核心价值在于“行为约束”而不是“事后检查”。它要求你在 AI 生成代码的每一个关键节点都插入一个设计原则的检查点。2.4 整体架构三层约束体系最终我搭建的是一套三层约束体系。第一层是接口层约束。在 AI 生成任何函数或类之前先要求它明确接口输入是什么、输出是什么、调用者需要知道什么、不需要知道什么。这一层对应的是“深模块”原则。第二层是实现层约束。在 AI 生成函数体时检查是否存在重复逻辑、是否暴露了内部细节、是否把多个职责混在一起。这一层对应的是“信息隐藏”和“单一职责”原则。第三层是演进层约束。在 AI 修改已有代码时检查修改是否破坏了原有抽象、是否引入了新的耦合。这一层对应的是“避免浅模块”和“依赖倒置”原则。这三层约束不是独立的而是嵌套在 AI 的生成流程里。每生成一个代码单元都会依次经过这三层检查。如果某一层不通过AI 会先修正再继续生成。3. 核心细节解析把设计原则拆成可执行的检查项3.1 深模块检查接口复杂度与实现复杂度的比值“深模块”是整套体系里最核心的概念。书里的定义很简洁接口的复杂度应该远小于实现的复杂度。但什么叫“远小于”我把它量化成了一个比值。具体做法是给接口复杂度和实现复杂度分别打分。接口复杂度看三个维度参数数量、参数类型的复杂度、调用者需要了解的约束条件数量。实现复杂度也看三个维度代码行数、分支数量、内部状态数量。然后计算比值。如果接口复杂度除以实现复杂度大于 0.5就认为这个模块“太浅”需要重新设计。举个例子一个函数有 5 个参数函数体只有 8 行那接口复杂度大概是 5实现复杂度大概是 8比值 0.625超过阈值。这说明调用者需要了解很多信息但函数本身做的事情很少典型的“浅模块”。修正方向通常是两个要么合并参数把 5 个参数合并成 2 个配置对象要么增加实现把相关逻辑收进来让函数真正“深”起来。注意这个比值不是绝对的。有些工具函数天然就是“浅”的比如max(a, b)。所以我在 Skill 里加了一个例外规则如果函数是纯计算且无副作用阈值可以放宽到 0.8。3.2 信息隐藏检查识别“泄露的设计决策”信息隐藏的核心是模块应该隐藏那些最可能发生变化的设计决策。但 AI 往往会把所有东西都暴露出来因为它觉得“这样更灵活”。我设计的检查逻辑是先识别代码中所有的“设计决策点”然后看这些决策点是否被封装在模块内部。设计决策点包括数据结构的具体字段、外部服务的调用方式、配置项的读取方式、错误处理的具体策略。如果发现某个决策点被多个模块直接访问就判定为“泄露”。比如三个模块都直接读取user.email字段那这个字段的访问方式就是一个泄露的设计决策。因为一旦 email 的存储方式变化比如从字符串变成对象三个模块都要改。修正建议通常是引入一个访问函数或封装类把字段访问收进去。这样变化只影响一个地方。3.3 单一职责检查用“变化原因”而不是“代码行数”来判断很多开发者对单一职责的理解是“一个函数只做一件事”。但“一件事”的定义太模糊了。AI 更是经常把“一件事”理解成“一个流程”然后把整个流程塞进一个函数。我采用的判断标准来自书里的一个观点一个模块应该只有一个变化的原因。具体检查时我会让 AI 列出这个模块可能变化的所有原因。如果原因超过一个就判定为职责过多。比如一个processOrder函数可能的变化原因有订单格式变化、支付方式变化、库存扣减逻辑变化、通知方式变化。四个原因说明这个函数承担了四个职责。修正方向是按变化原因拆分订单解析、支付处理、库存管理、通知发送各自独立。这个检查的难点在于AI 需要理解业务上下文才能列出变化原因。我的做法是在 Skill 里内置一个“变化原因模板”涵盖常见的几类数据格式、外部依赖、业务规则、用户界面、性能要求。AI 只需要对照模板逐项检查。3.4 依赖方向检查确保依赖指向稳定的一端依赖倒置原则说高层模块不应该依赖低层模块两者都应该依赖抽象。但在 AI 生成的代码里经常出现“业务逻辑直接依赖具体数据库驱动”的情况。我的检查逻辑是先识别代码中的依赖关系然后判断依赖方向是否合理。合理的依赖方向应该是不稳定的模块依赖稳定的模块。如果两个模块都不稳定就应该引入抽象层。具体操作时我会让 AI 给每个模块标注“稳定性等级”工具函数最稳定业务逻辑次之外部接口最不稳定。然后检查依赖方向。如果发现业务逻辑直接依赖外部接口就提示引入适配层。3.5 复杂度增长检查防止“熵增”这一层是针对 AI 修改代码的场景。AI 在修改已有代码时往往会“就地扩展”而不是“重新抽象”。比如给一个函数加一个参数、加一个 if 分支、加一个特殊情况处理。每次修改看起来都很小但累积起来就是复杂度爆炸。我的检查逻辑是在 AI 修改代码前先记录当前模块的复杂度指标参数数量、分支数量、代码行数。修改后再次计算。如果任一指标增长超过 20%就要求 AI 解释增长原因并提供替代方案。替代方案通常是引入新函数、使用策略模式、提取配置对象。这样修改不会直接增加原模块的复杂度而是通过组合来扩展功能。4. 实操过程从零搭建一套可运行的 Agent Skills4.1 环境准备与基础配置我用的是一套本地运行的 Agent 框架支持自定义 Skill 注入。基础配置包括三部分Skill 定义文件、检查逻辑实现、生成流程钩子。Skill 定义文件用 YAML 格式每个 Skill 包含名称、触发条件、检查逻辑、修正建议。比如深模块检查的 Skill 定义大概是这样的name: deep-module-check trigger: before_function_generation check: | interface_complexity count_params count_constraints implementation_complexity count_lines count_branches ratio interface_complexity / implementation_complexity if ratio 0.5 and not is_pure_function: return shallow_module_detected suggestion: | 考虑合并参数或增加实现逻辑。 如果参数超过4个尝试合并为配置对象。 如果函数体少于10行考虑将相关逻辑收进来。检查逻辑实现用 Python 写因为需要做一定的语法分析和指标计算。生成流程钩子则是在 AI 生成代码的每个关键节点调用对应的 Skill。提示Skill 的触发时机很关键。接口层检查应该在生成函数签名之前实现层检查应该在生成函数体之后演进层检查应该在修改已有代码之前。4.2 深模块检查的完整实现深模块检查的完整流程分四步。第一步是参数分析。解析函数签名统计参数数量。如果参数超过 4 个标记为“参数过多”。然后分析参数类型如果多个参数属于同一概念比如name、email、phone都属于用户信息建议合并为对象。第二步是约束分析。检查函数文档或注释中是否包含“调用者必须……”“注意……”等约束性描述。每条约束加 1 分。如果约束分超过 2说明调用者需要了解太多信息。第三步是实现分析。统计函数体的代码行数和分支数量。分支包括 if、else、switch、循环、异常捕获。如果代码行数少于 10 且分支少于 2标记为“实现过浅”。第四步是比值计算与判定。接口复杂度 参数数量 约束分。实现复杂度 代码行数 分支数量 × 2。比值超过 0.5 且不是纯函数就触发修正建议。实测下来这套检查能抓住大约 70% 的“浅模块”问题。剩下的 30% 主要是那些“接口简单但实现也简单”的函数这类函数本身没问题只是需要判断是否应该合并到其他模块。4.3 信息隐藏检查的实操细节信息隐藏检查的难点在于“识别设计决策点”。我的做法是维护一个“决策点模式库”包含常见的几类数据结构字段访问obj.field形式的直接访问外部服务调用http.get、db.query等配置读取config.get、env等错误处理try/catch、throw等检查时先扫描代码中所有匹配模式库的语句然后统计每个决策点的访问次数。如果某个决策点被 3 个以上不同模块访问就判定为“泄露”。修正建议分两种情况。如果是数据结构字段建议引入 getter/setter 或封装类。如果是外部服务调用建议引入适配层或服务类。如果是配置读取建议集中到配置模块。我踩过的一个坑是过度封装。有一次 AI 把所有字段访问都包了一层 getter结果代码量翻倍可读性反而下降。后来我加了一个规则只有当字段被 3 个以上模块访问且字段类型可能变化时才建议封装。否则直接访问更清晰。4.4 单一职责检查的变化原因模板变化原因模板是我自己总结的包含五类变化原因类型典型场景检查问题数据格式变化字段增减、类型调整这个模块是否直接依赖了具体字段外部依赖变化接口调整、服务替换这个模块是否直接调用了外部服务业务规则变化策略调整、条件增减这个模块是否包含了业务判断逻辑用户界面变化展示方式、交互调整这个模块是否包含了展示逻辑性能要求变化缓存、批量、异步这个模块是否包含了性能优化逻辑检查时AI 逐项对照模板如果某个模块匹配了两个以上类型就判定为职责过多。修正建议是按类型拆分模块。这个模板的好处是它把抽象的“职责”概念具体化了。AI 不需要理解“职责”的哲学含义只需要对照模板做匹配。4.5 依赖方向检查的稳定性标注依赖方向检查需要先给模块标注稳定性等级。我的标注规则是工具函数不依赖任何外部资源稳定性最高等级 1业务逻辑依赖工具函数和抽象接口稳定性中等等级 2外部接口依赖具体服务、数据库、网络稳定性最低等级 3标注完成后检查所有依赖关系。如果发现等级 2 的模块直接依赖等级 3 的模块就判定为“依赖方向不合理”。修正建议是引入抽象接口让等级 2 依赖抽象等级 3 实现抽象。这个检查在重构时特别有用。有一次 AI 生成了一段业务逻辑直接调用了具体的数据库驱动。检查触发后AI 自动引入了 Repository 接口业务逻辑依赖接口数据库驱动实现接口。后来换数据库时只改了一个实现类。4.6 复杂度增长检查的阈值设定复杂度增长检查的阈值设定很关键。设得太松起不到约束作用设得太紧AI 会频繁触发修正影响生成效率。我试过几组阈值。最初设的是 10%结果 AI 几乎每次修改都会触发因为加一个参数就可能超过 10%。后来放宽到 30%又太松复杂度还是慢慢涨上去了。最终定在 20%并且加了一个“累计增长”规则如果连续三次修改都导致复杂度增长即使每次都没超过 20%也触发修正。具体指标包括参数数量、分支数量、代码行数、依赖数量。任一指标增长超过 20%或者四个指标中有三个增长超过 10%就触发检查。修正建议通常是提取新函数、引入策略模式、使用配置对象、拆分模块。AI 会根据具体情况选择。5. 常见问题与排查技巧实录5.1 检查太频繁导致生成效率下降这是最开始遇到的问题。每个 Skill 都在生成过程中触发AI 要反复停下来检查、修正、再检查。一个简单的函数生成可能要花五六轮。我的解决方法是分级触发。把 Skill 分成“强制检查”和“建议检查”两类。强制检查只在关键节点触发比如生成公共 API、修改核心模块、新增依赖关系。建议检查则在后台异步运行不阻塞生成流程只在最后汇总报告。这样调整后生成效率恢复了大约 80%同时关键的设计问题仍然能被抓住。5.2 AI 对抽象原则的理解偏差AI 对“深模块”“信息隐藏”这类抽象概念的理解经常跑偏。有一次我让它“隐藏实现细节”它直接把所有函数都改成了私有结果调用者完全无法使用。后来我调整了策略不给 AI 讲原则只给它讲检查项。不说“请遵循信息隐藏原则”而是说“请检查是否有字段被三个以上模块直接访问如果有请封装”。这样 AI 的理解准确率大幅提升。5.3 修正建议过于激进导致过度设计AI 在收到修正建议后往往会“过度执行”。比如建议“合并参数”它会把所有参数合并成一个巨大的配置对象包括那些本来很清晰的独立参数。我的应对方法是加约束条件。在修正建议里明确写出“不要做什么”。比如“合并参数时保留 2-3 个核心参数只把相关参数合并。不要把所有参数合并成一个对象。”5.4 常见问题速查表问题现象可能原因排查方法解决技巧生成效率明显下降检查触发太频繁统计各 Skill 触发次数分级触发非关键检查异步化AI 修正后代码更乱修正建议太抽象检查建议是否包含具体操作把原则翻译成检查项不给抽象指令过度封装导致代码膨胀修正建议缺少约束检查是否有“不要做什么”在建议里加约束条件检查漏报严重模式库覆盖不足人工评审对比检查结果持续补充决策点模式库依赖方向检查误报稳定性标注不准抽查标注结果定期校准稳定性等级5.5 独家避坑技巧第一个技巧是先跑通再优化。不要一开始就追求完美的检查逻辑。我最初花了大量时间设计复杂的指标计算结果发现很多指标在实际场景中根本用不上。后来改成“先跑通最简单的检查再根据实际问题逐步补充”效率高了很多。第二个技巧是保留人工评审环节。Agent Skills 能抓住大部分明显问题但有些设计决策需要业务上下文才能判断。我的做法是Skill 检查通过后仍然保留一个轻量级的人工评审只看关键模块的接口设计。第三个技巧是定期回顾检查记录。我会每周看一次 Skill 的触发记录统计哪些检查最常触发、哪些修正建议最常被采纳。这能帮我发现代码库中的系统性问题也能帮我优化 Skill 本身。第四个技巧是不要追求零误报。有些检查会误报但只要误报率在可接受范围内我设的是 15%就不需要过度优化。因为优化误报的成本往往高于误报本身的成本。6. 实际效果与个人体会这套 Agent Skills 我用了大约两个月覆盖了三个中等规模的项目。最直观的变化是AI 生成的代码不再需要大规模重构了。以前每生成一批代码我都要花半天时间梳理结构现在生成完基本就能用只需要做少量调整。另一个变化是代码的“可修改性”提升了。以前改一个字段名要翻十几个文件现在通常只需要改两三个地方。因为信息隐藏检查把字段访问收拢了依赖方向检查把变化隔离了。当然这套东西不是银弹。它解决的是“设计原则的执行”问题而不是“设计原则的理解”问题。AI 仍然需要人来告诉它什么是好的设计只是现在这个“告诉”变成了可执行的检查项而不是抽象的口号。我个人的体会是AI 编码的真正瓶颈不在生成速度而在生成质量。而生成质量的核心不是代码能不能跑而是代码能不能改。把《软件设计的哲学》做成 Agent Skills本质上是在给 AI 装一个“可修改性”的约束。这个约束不会让 AI 写得更快但会让 AI 写得更好。最后分享一个小技巧如果你也想尝试这套方案建议从“深模块检查”开始。它是最容易实现、效果最明显的检查项。先让 AI 学会“接口要简单、实现要深入”再逐步加入其他检查。不要一次全上否则 AI 会被太多约束搞晕生成效率反而下降。
延伸阅读

更多相关文章

2026/10/11 10:22:59

rea:规则驱动的命令行文本抽取与字段映射工具实战

我入行头几年,最怕听到的四个字就是“导出文件”。不管是业务系统的明细、网关日志还是上游的数据对账表,落到手里永远是各种格式的纯文本:有的是制表符分隔,有的用竖线,有的干脆是几万行带时间戳的半结构化记录。而我…

2026/10/11 10:22:59

从“impeccable”到工程实践:代码格式化、静态检查与CI流水线

“impeccable”这个词,按读音是 /ɪmˈpɛkəbəl/,意思是“无可挑剔、毫无瑕疵”。我见过不少人把它当成代码注释里的形容词,写“keep the code impeccable”。说实话,第一次看到某公司前端代码仓库的提交规范里,用这…

2026/10/11 10:17:59

Homelab NVMe故障修复:固件降级与内核参数调优实战

1. 项目概述:这不是一次简单的硬盘更换,而是一场对存储底层逻辑的重新校准“Homelab NVMe 修复记录”——看到这个标题,很多刚搭起自己小机房的朋友第一反应可能是:“哦,又一块SSD坏了,换掉就行。”但如果你…

2026/10/11 11:23:02

过度约束设计的隐性成本:识别、量化与规避方法

1. 从一次返工说起:过度约束到底贵在哪前阵子帮一个做智能硬件的朋友看他们新一版的结构件图纸,聊到一半他叹了口气,说这个项目本来三个月能收尾,结果拖到第五个月还在改。我问他卡在哪,他说不是技术难题,是…

2026/10/11 11:23:02

工程代码中模糊缩写‘rea‘的溯源与治理方法

项目标题“rea”目前在公开网络环境中未形成明确、稳定、可验证的语义指向。经多平台实时检索(含主流搜索引擎、社交媒体热榜、技术社区、词源数据库及新词监测工具),该字符串未出现在近期权威热词榜单、行业术语库或大众传播语境中&#xff…

2026/10/11 11:23:02

Java远程控制源码拆解:Robot抓屏、TCP传输与事件注入

简介:这是一份面向Java中高级学习者的远程控制源码资源包,围绕RMI与JMX两条技术路线组织,帮助读者理解跨JVM的方法调用、远程对象注册与分布式管理机制。包内共有46个文件,包括4个Java源文件、38个已编译的class文件,以…

2026/10/11 11:23:02

Total Uninstall Pro 快照差分机制与批量静默卸载实战指南

简介:这是一款面向Windows用户的专业级软件卸载工具,专门解决系统自带卸载程序、360强力卸载等常规手段无法彻底清除的顽固软件残留问题,尤其适合需要深度清理系统程序、释放磁盘空间或排查卸载故障的进阶用户。压缩包共18个文件,…

2026/10/11 11:18:02

OpenCV手势识别控制小米智能家居:毕设源码实战与避坑指南

简介:这份资源是一套基于OpenCV实现手势控制小米智能家居的完整项目源码,面向计算机视觉入门者、智能家居爱好者及需要毕业设计选题的学生,帮助解决手势识别与设备远程控制联动的实践问题。压缩包共14个文件,约1.36MB,…

2026/10/11 0:02:13

Python调用Gemini Structured Outputs实现工单路由门禁

客服工单最怕的不是模型“答错一句话”,而是它给出一段看起来合理的说明,程序却从中猜错优先级。通俗做法是:要求模型只交 JSON(JavaScript Object Notation,轻量数据格式),再让代码验证它。Gem…

2026/10/11 0:02:13

Spring Boot超市进销存系统毕设实战:从需求拆解到答辩通关

最近带的一个学生项目组里,有A同学跑来问我:选什么毕设题目最稳妥,既能让评审老师觉得工作量够,又不会在答辩时被问到语无伦次。我第一反应就是推荐基于Spring Boot的超市仓库管理系统——也就是超市进销存系统。这个题目乍一看平…

2026/10/11 0:02:13

Flutter StatefulWidget 生命周期核心解析

很多刚开始接触 Flutter 的朋友,在看完一堆“Hello World”和基础组件之后,大概率都会撞上同一堵墙:StatefulWidget 里那堆 initState、build、dispose 方法,到底什么时候被调用?为什么顺序是那样?在里面到…

2026/10/11 0:02:13

Python调用Gemini Structured Outputs实现工单路由门禁

客服工单最怕的不是模型“答错一句话”,而是它给出一段看起来合理的说明,程序却从中猜错优先级。通俗做法是:要求模型只交 JSON(JavaScript Object Notation,轻量数据格式),再让代码验证它。Gem…

2026/10/11 0:02:13

Spring Boot超市进销存系统毕设实战:从需求拆解到答辩通关

最近带的一个学生项目组里,有A同学跑来问我:选什么毕设题目最稳妥,既能让评审老师觉得工作量够,又不会在答辩时被问到语无伦次。我第一反应就是推荐基于Spring Boot的超市仓库管理系统——也就是超市进销存系统。这个题目乍一看平…

2026/10/11 0:02:13

Flutter StatefulWidget 生命周期核心解析

很多刚开始接触 Flutter 的朋友,在看完一堆“Hello World”和基础组件之后,大概率都会撞上同一堵墙:StatefulWidget 里那堆 initState、build、dispose 方法,到底什么时候被调用?为什么顺序是那样?在里面到…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑