ARTICLE DETAIL

资讯详情

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

Coding Agent 太能写?四层约束体系让代码生成可控

Coding Agent 太能写?四层约束体系让代码生成可控 1. 为什么“太能写”反而成了 Coding Agent 的头号风险1.1 从“不会写”到“写太多”的认知反转刚开始用 Coding Agent 的那阵子我跟大多数人一样最担心的是它“不会写”——怕它理解不了需求怕它生成的代码跑不起来怕它连基本的语法都搞错。但用了几个月之后我发现真正让我头疼的问题完全反过来了它太能写了。你给它一个“帮我加个用户登录接口”的需求它可能一口气给你生成 800 行代码包含完整的用户模型、密码加密、JWT 签发、刷新令牌、权限中间件、异常处理、日志埋点甚至还顺手帮你重构了三个不相关的模块。代码看起来都很合理注释也很漂亮但问题是我只想要一个登录接口它却动了我的整个项目结构。这不是个别现象。Coding Agent 的底层逻辑是“尽可能生成完整的、看起来正确的代码”它的训练目标就是让输出更丰富、更全面、更“专业”。但工程实践的核心恰恰相反——好的工程不是写得多而是写得准、写得少、写得可控。1.2 四个真实踩坑场景我整理了自己和团队在使用 Coding Agent包括 Claude Code、Cursor 等工具过程中遇到的四类典型问题每一个都和“太能写”直接相关。场景一范围蔓延。你让它修一个 bug它顺手重构了周边代码。你让它加一个字段它把整个数据模型重新设计了一遍。结果是 diff 巨大review 成本飙升而且引入新 bug 的概率远高于修复原 bug 的收益。场景二风格漂移。项目里明明有一套统一的错误处理模式它偏偏要发明一套新的。你用的是 snake_case它给你生成 camelCase。你用的是自定义的日志工具它直接引入了一个新的第三方库。代码能跑但整个项目的风格一致性被打破了。场景三依赖膨胀。需要解析一个 YAML 文件项目里已经有 js-yaml 了它非要装一个 yaml 包。需要一个日期格式化它引入了 dayjs而项目里用的是 date-fns。每多一个依赖就多一份维护成本和安全风险。场景四过度抽象。一个简单的 CRUD 操作它给你搞出 Repository 层、Service 层、Factory 模式、Strategy 模式五个文件加起来 600 行。你只是想查个数据库而已。这些问题的共同根源是Coding Agent 缺少工程约束。它不知道你的项目边界在哪里不知道哪些东西不能碰不知道你的团队约定是什么。它只知道“生成尽可能好的代码”但“好”的定义在工程语境下是高度上下文相关的。1.3 四层约束的整体设计思路我给 Coding Agent 加的这四层约束核心思路是在 Agent 的生成能力和工程的收敛需求之间建立一道缓冲。不是限制它的能力而是给它划定一个安全的工作空间。四层约束从外到内分别是第一层项目级约束——告诉 Agent 这个项目是什么、用什么技术栈、有哪些全局规则第二层任务级约束——告诉 Agent 这次任务的范围是什么、能改什么、不能改什么第三层输出级约束——告诉 Agent 代码应该长什么样、遵循什么风格、用什么模式第四层验证级约束——在 Agent 输出之后自动检查是否违反了前面的约束这四层不是孤立的而是一个递进的关系。项目级约束是基础任务级约束是动态的输出级约束是具体的验证级约束是兜底的。下面我逐层拆解。2. 第一层项目级约束——让 Agent 先读懂“家规”2.1 为什么项目级约束是地基很多人用 Coding Agent 的方式是打开工具直接输入需求等结果。这就像让一个新员工第一天上班就直接干活不给他看任何文档、不介绍项目背景、不说明团队规范。他可能技术很强但产出大概率不符合预期。项目级约束的目的就是给 Agent 提供“入职培训”。它需要知道这个项目用什么语言、什么框架、什么版本代码目录怎么组织有哪些全局性的编码规范哪些文件是核心文件不能随便动测试怎么跑构建怎么构建。这些信息如果不在每次对话开始时提供给 Agent它就会按照自己的“默认习惯”来生成代码。而它的默认习惯是基于海量开源代码训练出来的“平均风格”不一定适合你的项目。2.2 用配置文件固化项目规则我的做法是在项目根目录放一个专门给 Agent 看的配置文件。不同的工具对这个文件的命名和格式支持不同但核心内容是一致的。以 Claude Code 为例它支持在项目根目录放CLAUDE.md文件Cursor 支持.cursorrules文件。我通常两个都放内容基本一致。这个文件里我一般包含以下几类信息# 项目概述 - 这是一个基于 Node.js 20 TypeScript 5.3 的后端服务 - 使用 Fastify 作为 Web 框架Prisma 作为 ORMPostgreSQL 作为数据库 - 包管理器使用 pnpm不要使用 npm 或 yarn # 目录结构 - src/routes/ 存放路由定义 - src/services/ 存放业务逻辑 - src/repositories/ 存放数据访问层 - src/utils/ 存放工具函数 - tests/ 存放测试文件 # 编码规范 - 所有函数必须显式声明返回类型 - 错误处理统一使用 src/utils/errors.ts 中定义的 AppError 类 - 日志统一使用 src/utils/logger.ts 中的 logger 实例 - 不要引入新的第三方依赖除非明确说明理由 # 禁止事项 - 不要修改 prisma/schema.prisma 文件 - 不要修改 src/config/ 目录下的任何文件 - 不要修改 package.json 中的依赖版本 - 不要删除或重命名已有的导出函数这个文件的关键在于具体。不要写“遵循良好的编码规范”这种废话要写“所有函数必须显式声明返回类型”这种可执行的规则。Agent 不需要理解为什么它只需要知道做什么。2.3 项目级约束的三个实操心得心得一约束要可验证。“代码要优雅”这种约束等于没写因为 Agent 无法判断自己是否违反了。“不要引入新的第三方依赖”就是可验证的Agent 可以检查自己是否在 import 语句中引入了不在 package.json 中的包。心得二约束要分层。我把约束分成“硬约束”和“软约束”。硬约束是绝对不能违反的比如“不要修改数据库 schema”。软约束是尽量遵守的比如“优先使用函数式风格”。硬约束写在配置文件的最前面用加粗或特殊标记标出。心得三定期更新。项目在演进约束也要跟着变。我每个月会 review 一次配置文件把过时的规则删掉把新出现的约定加进去。这个习惯看起来简单但能避免 Agent 按照半年前的规则生成代码。3. 第二层任务级约束——把“能改什么”说清楚3.1 任务边界模糊是最大的坑项目级约束解决的是“长期规则”的问题但每次任务的具体边界是不一样的。修一个 bug 和加一个新功能允许改动的范围完全不同。如果不把任务边界说清楚Agent 就会按照自己的判断来扩大范围。我踩过最惨的一次坑是让 Agent 修一个日期格式化的 bug结果它把整个日期处理模块重写了还改了三个调用方的代码。虽然最终 bug 是修了但引入了两个新的边界情况问题花了更多时间才修好。从那以后我养成了一个习惯每次给 Agent 下任务时明确列出“可以改的文件”和“不可以改的文件”。3.2 任务模板的结构化写法我现在给 Agent 下任务基本遵循一个固定的模板## 任务描述 [一句话说明要做什么] ## 允许修改的文件 - src/services/userService.ts - src/routes/userRoutes.ts ## 禁止修改的文件 - 其他所有文件 ## 验收标准 - [ ] 新增的接口返回 200 状态码 - [ ] 已有的测试全部通过 - [ ] 不引入新的依赖 ## 补充说明 - 参考 src/services/orderService.ts 中的错误处理方式 - 如果需要新增类型定义放在 src/types/user.ts 中这个模板看起来有点繁琐但实际用下来它节省的时间远超写模板的时间。因为 Agent 有了明确的边界生成的 diff 小了很多review 起来快了很多返工率也大幅下降。3.3 用“最小改动原则”约束 Agent除了明确文件范围我还会在任务描述中加一句“请遵循最小改动原则只修改实现目标所必需的代码”。这句话看起来是废话但对 Agent 的行为有实际影响。实测下来加了这句话之后Agent 的 diff 平均缩小了 40% 左右。它不再“顺手”重构不相关的代码不再“顺便”优化命名不再“额外”添加注释和文档。它变得像一个有经验的工程师知道什么时候该动手什么时候该收手。当然最小改动原则也有例外。如果任务本身就是重构那当然要允许大范围改动。关键是让 Agent 知道这次任务的类型是什么。我会在任务描述中明确标注“这是一次重构任务允许修改相关模块的代码结构”或者“这是一次 bug 修复任务请严格限制改动范围”。4. 第三层输出级约束——让代码“长得像项目里的代码”4.1 风格一致性为什么重要代码风格一致性不是审美问题是工程问题。当项目里 90% 的代码用某种模式剩下 10% 用另一种模式时维护成本会显著上升。读代码的人需要不断切换思维模式新人需要花更多时间理解为什么有两种写法工具链linter、formatter也需要额外的配置来处理例外。Coding Agent 天然倾向于“发明”新的写法因为它的训练数据来自成千上万个不同的项目它没有一个“当前项目”的概念。所以我们需要在输出层面给它更具体的约束。4.2 用示例驱动代替规则驱动我试过写详细的风格规则比如“使用 2 空格缩进”、“函数名使用 camelCase”、“常量使用 UPPER_SNAKE_CASE”。这些规则有用但效果有限。因为 Agent 对规则的理解是抽象的它可能在一个地方遵守了在另一个地方又忘了。更有效的方式是给示例。我会在项目级配置文件中放几个“参考文件”告诉 Agent“如果你要写一个新的 service请参考 src/services/orderService.ts 的风格如果你要写一个新的 route请参考 src/routes/orderRoutes.ts 的风格。”示例驱动的好处是Agent 可以直接模仿具体的代码结构、命名习惯、错误处理方式、日志格式而不需要从抽象规则中推导。实测下来这种方式生成的代码风格一致性明显更高。4.3 输出级约束的具体清单除了示例驱动我还会在任务描述中附加一个“输出检查清单”让 Agent 在生成代码后自己对照检查检查项要求违反后果函数返回类型必须显式声明重新生成错误处理必须使用 AppError重新生成日志必须使用 logger 实例重新生成依赖不得引入新依赖重新生成命名遵循项目现有命名习惯提示修正注释只在复杂逻辑处添加提示修正测试新增功能必须有测试重新生成这个清单我会放在任务描述的最后Agent 生成代码后会自己检查一遍。虽然它不一定能 100% 遵守但有了这个清单违反率会大幅下降。5. 第四层验证级约束——自动兜底不靠自觉5.1 为什么需要自动验证前三层约束都是“事前”约束依赖 Agent 的自觉性。但 Agent 不是人它没有“责任感”它只是在概率上倾向于遵守约束。所以必须有“事后”的自动验证机制在 Agent 输出之后检查是否真的遵守了约束。自动验证的核心思路是把约束转化成可执行的检查脚本。比如“不要引入新依赖”可以转化成“检查 git diff 中是否有 package.json 的改动”。“必须使用 AppError”可以转化成“检查新增代码中是否有 throw new Error 的调用”。5.2 用 Git Hook 做自动检查我的做法是在项目中配置一个 pre-commit hook当 Agent 生成代码并尝试提交时自动运行一系列检查。如果检查不通过提交会被阻止Agent 会收到错误信息然后根据错误信息修正代码。这个 hook 的核心逻辑大概是这样的#!/bin/bash # pre-commit hook # 检查是否有 package.json 改动 if git diff --cached --name-only | grep -q package.json; then echo 错误检测到 package.json 改动请确认是否真的需要新增依赖 exit 1 fi # 检查是否有禁止修改的文件被改动 FORBIDDEN_FILESprisma/schema.prisma src/config/ for file in $FORBIDDEN_FILES; do if git diff --cached --name-only | grep -q $file; then echo 错误禁止修改的文件 $file 被改动 exit 1 fi done # 检查新增代码中是否使用了 throw new Error if git diff --cached | grep -q ^.*throw new Error; then echo 错误请使用 AppError 代替 throw new Error exit 1 fi # 运行测试 pnpm test if [ $? -ne 0 ]; then echo 错误测试未通过 exit 1 fi echo 所有检查通过这个 hook 看起来简单但效果非常好。Agent 在收到错误信息后通常能很快修正问题。而且因为检查是自动的不需要我手动 review 每一行代码节省了大量时间。5.3 验证级约束的边界自动验证不是万能的。它只能检查“可机械化验证”的约束比如文件改动、依赖引入、特定字符串的出现。对于“代码是否优雅”、“逻辑是否正确”这类主观判断自动验证无能为力。所以我的策略是能用自动验证的用自动验证不能用自动验证的用人工 review。自动验证覆盖 70% 的常见问题人工 review 聚焦在剩下的 30% 上。这样整体效率最高。另外自动验证的规则也需要定期维护。项目在变约束在变检查脚本也要跟着变。我一般每两周 review 一次检查脚本把不再适用的规则删掉把新出现的约束加进去。6. 四层约束的协同工作流6.1 一次完整的任务执行流程把四层约束串起来一次完整的任务执行流程大概是这样的准备阶段Agent 读取项目级配置文件了解项目背景和全局规则任务下发我按照任务模板描述需求明确文件范围和验收标准生成阶段Agent 根据项目级约束和任务级约束生成代码同时参考输出级约束中的示例和检查清单自检阶段Agent 对照输出检查清单自查修正明显问题验证阶段pre-commit hook 自动运行检查不通过则阻止提交并返回错误信息修正阶段Agent 根据错误信息修正代码重新提交人工 review我 review 最终 diff确认逻辑正确性和整体质量这个流程看起来步骤很多但实际用下来大部分任务在 3-5 分钟内就能完成。相比之前“生成-发现问题-返工-再发现问题-再返工”的循环效率提升非常明显。6.2 约束的优先级和冲突处理四层约束之间偶尔会有冲突。比如项目级约束说“不要引入新依赖”但任务级约束说“需要解析 YAML”而项目里没有 YAML 解析库。这时候怎么办我的处理原则是任务级约束优先于项目级约束但需要显式说明理由。如果确实需要引入新依赖我会在任务描述中明确写“本次任务允许引入 yaml 包因为项目中没有现成的 YAML 解析方案”。这样 Agent 就知道这是一个被批准的例外。输出级约束和验证级约束之间的冲突比较少因为验证级约束本来就是输出级约束的自动化版本。如果出现冲突说明检查脚本写错了需要修正脚本。6.3 约束的迭代和优化四层约束不是一次写完就固定的它需要持续迭代。我的做法是每次遇到 Agent 违反约束的情况就问自己一个问题“这个约束是否足够明确是否可以被自动验证如果不能能不能转化成可验证的形式”比如最开始我写的约束是“代码要遵循项目风格”但 Agent 经常违反。后来我把它拆解成具体的检查项函数返回类型、错误处理方式、日志格式、命名习惯。拆解之后违反率大幅下降。另一个迭代方向是减少约束。有些约束写了之后发现 Agent 从来不违反或者违反了也没什么影响那就删掉。约束太多会增加 Agent 的认知负担反而降低整体效果。我现在的配置文件大概 200 行左右比最开始精简了不少。7. 常见问题与排查技巧实录7.1 Agent 无视约束怎么办这是最常见的问题。你明明在配置文件里写了“不要引入新依赖”Agent 还是引入了。原因通常有三个原因一约束不够具体。“不要引入新依赖”可能被 Agent 理解为“尽量不要”而不是“绝对不要”。改成“禁止在 package.json 中添加新的 dependencies 或 devDependencies”就明确多了。原因二约束位置不对。如果约束写在配置文件的最后面Agent 可能没注意到。把最重要的约束放在最前面用加粗或标题标出。原因三缺少自动验证。如果只有文字约束没有自动检查Agent 违反了你也不一定发现。加上 pre-commit hook 之后违反约束会直接导致提交失败Agent 不得不修正。7.2 约束太严导致 Agent 无法完成任务这是另一个极端。约束太严Agent 束手束脚连正常任务都完不成。比如你禁止修改任何文件那 Agent 当然什么都做不了。我的经验是约束应该限制“不必要的行为”而不是限制“必要的行为”。修 bug 必须改代码那就允许改代码但限制改动的范围。加功能必须新增文件那就允许新增文件但限制新增文件的位置和命名。如果发现 Agent 因为约束太严而无法完成任务先检查约束是否过于宽泛。比如“不要修改 src/ 目录下的文件”就太宽泛了应该改成“不要修改 src/config/ 和 src/middleware/ 目录下的文件”。7.3 不同 Agent 工具的约束兼容性我用过 Claude Code、Cursor 等不同的 Coding Agent 工具它们对约束的支持方式不太一样。Claude Code 支持CLAUDE.md文件Cursor 支持.cursorrules文件有些工具还支持.editorconfig或自定义配置文件。我的做法是把约束内容写在一个地方然后通过软链接或脚本同步到各个工具支持的配置文件中。这样只需要维护一份约束不用在多个文件之间来回同步。另外不同工具对约束的理解能力也不一样。有些工具能很好地理解自然语言约束有些工具更依赖结构化配置。对于理解能力较弱的工具我会把约束写得更具体、更结构化减少歧义。7.4 常见问题速查表问题可能原因解决方法Agent 引入新依赖约束不具体或缺少自动检查明确禁止并加 pre-commit 检查Agent 修改禁止文件约束位置靠后或未标红放在配置文件最前面并加粗Agent 生成代码风格不一致缺少示例或检查清单提供参考文件并加输出检查清单Agent 改动范围过大任务边界不明确使用任务模板明确文件范围约束太严导致任务失败约束过于宽泛缩小约束范围只限制必要行为不同工具约束不生效配置文件格式不兼容统一内容多格式同步自动检查误报检查脚本规则过时定期 review 并更新检查脚本Agent 自检不通过但不修正错误信息不明确在错误信息中给出具体修正建议8. 我个人的实操体会这套四层约束体系不是一天建成的是踩了无数坑之后慢慢摸索出来的。最开始我只用项目级约束发现 Agent 经常越界后来加了任务级约束发现代码风格还是不一致再加输出级约束发现 Agent 还是会偷偷违反最后加上验证级约束才算真正把问题控制住。如果让我给刚接触 Coding Agent 的人一个建议我会说先从任务级约束开始。因为任务级约束最容易见效写一个任务模板明确文件范围就能立刻减少 50% 以上的返工。等项目级和输出级约束的需求浮现出来之后再逐步补充。另外约束不是越多越好。我见过有人写了 500 行的配置文件结果 Agent 根本记不住反而经常混淆。约束的核心是“少而精”每一条都要有明确的理由和可验证的标准。如果一条约束你无法判断 Agent 是否违反了那它大概率是无效的。最后分享一个小技巧定期让 Agent 自己 review 约束配置文件。我会每隔一段时间把配置文件发给 Agent问它“这些约束中有哪些是模糊的、矛盾的、或者无法验证的”Agent 通常能给出不错的建议因为它比任何人都清楚哪些约束它理解不了。这个习惯帮我删掉了不少冗余约束也让剩下的约束更加有效。
返回列表