引子:很多人把 AI 协作做反了
过去半年我在实际协作里反复遇到一个现象:很多人把主要精力放在"怎么让 AI 写得更快、更准",但很少提前定义"AI 生成的代码应该落在哪些边界内"。
这两件事的优先级,我现在的判断是 ——应该掉个个儿。
让 AI 写得对,是短期效率问题。 让 AI 的产出能被治理,是长期工程问题。
前者决定你的下个 PR 能不能合,后者决定你的项目三年后还有没有人能维护。
我做这件事是从一个具体语境开始的:一套有多个子应用的 Vue 2.6 中后台老系统、有自定义组件库、有历史包袱、有不容修改的存量约定。在这个语境里用 AI 写代码,你很快会发现一件事 —— AI 写出来的不是错代码,是"在你项目里不该这么写"的代码。
这篇文章想讲的就是这件事:我从这个语境里沉淀出了一套怎样的工程做法,以及为什么我认为这套工程做法比"调教 AI"更重要。
AI 让"写代码"贬值了,让"判断代码该写成什么样"升值了。
一、AI 协作的真实失控,不在能力,在边界
如果你也在用 AI 写复杂项目的代码,下面这些场景你应该不陌生:
- 让 AI 写一个详情页,它把状态散落到了 5 个不同组件的
data里。 - 同一团队不同人产出的 AI 代码风格差异极大,看起来都没错,但放在一起像五个项目。
- AI 喜欢用
$bus.$emit('refresh')这种"看起来能跑"的方案,因为它在训练数据里见过太多类似写法。 - AI 写的
setInterval经常忘记clearInterval,或者写了但在错误的生命周期里清理。 - AI 倾向于加额外的
try-catch、加日志、加注释,但跟你项目实际的错误处理规范不符。 - AI 不知道你的项目有自定义 UI 库,直接用了原版 Element UI —— 看起来一样,但 prop 协议根本不兼容。
大多数人看到这些场景,第一反应是:"AI 还是不够聪明,等下一代模型就好了。"
我的判断是反过来的。
AI 写的不是错代码,是"在你项目里不该这么写"的代码。 问题不在 AI 的能力,在你给它的边界。
换句话说:这不是 AI 的能力问题,是项目本身没有给 AI 准备一份"它该往哪里填"的边界文档。
二、为什么"提示词约束"和"ESLint 约束"都不够
意识到边界问题后,大多数人会本能地走两条路:
- 写更详细的提示词 —— 每次让 AI 写代码前,把项目规范贴进去
- 加更严格的 lint 规则 —— 让 AI 写出来的代码在工具层被自动校正
这两条路我都走过。最后的结论是:都不够。
四层现有约束的天花板,各自是这样的:
| 约束方式 | 它能做什么 | 它的天花板 |
|---|---|---|
| 提示词约束 | 当次对话有效 | 跨会话失效、长上下文淡忘、每次重写心智成本高 |
| README / 风格文档 | 给人看的标准答案 | AI 不一定读,读了未必照做 |
| ESLint / Prettier | 语法和格式 | 管不到代码组织结构和架构选择 |
| Code Review | 兜底所有问题 | 事后审查 + 人力疲劳 + 漏检常态化 |
四层共同的问题是:它们都是"事后挑错"。
AI 已经把代码写出来了,你才在审查它。每一次都是一次重新发现问题、重新讲规则、重新返工的循环。
真正有效的约束,应该在 AI 动笔之前就存在。
约束 AI 的正确姿势,不是"事后审查",而是事前定型 —— 以"它能填什么"代替"它能写什么"。
三、我实际在用的「双入口 + 六目录」约束体系
这一节是文章核心,所以我会展开讲。但请注意:这一套结构不是首日交付清单,是增长结构。后面会专门讲清楚"什么时候写什么"。
3.1 整体结构图
project/
├── AGENTS.md ← 入口一:给通用 agent(Codex / Cursor)
├── CLAUDE.md ← 入口二:Claude Code 专属
└── docs/
├── 00-global/ ← 根规则层:项目宪法
├── 01-context/ ← 业务背景层:项目是干嘛的
├── 02-architecture/ ← 技术架构层:系统长什么形状
│ └── decisions/ ← ADR:架构决策记录
├── 03-components/ ← 组件规约层:组件怎么用
├── 04-patterns/ ← 写法范式层:典型页面怎么搭
└── 05-ai/ ← AI 协作专属层:只给 AI 看的
每个目录解决一类失控:
| 目录 | 它在防什么 |
|---|---|
00-global/ | AI 不知道项目的硬底线 |
01-context/ | AI 不懂业务,凭技术常识瞎写 |
02-architecture/ | AI 不知道技术骨架,写出"通用代码" |
03-components/ | AI 用错组件、混用组件 |
04-patterns/ | AI 单步对、整体错 |
05-ai/ | AI 重复推销已否决方案 |
3.2 重要提醒:不要一上来写满六目录
我先把丑话说在前面 —— 不是所有项目一上来都要写满六目录。
六目录是增长结构,不是首日交付清单。
正确的接入节奏是:
- 先写
AGENTS.md—— 提炼 5–10 条项目红线 - 写
docs/00-global/non-goals.md—— 告诉 AI 不做什么 - 写第一份 ADR —— 记录最重要的一个架构决策
- 边踩坑边写
docs/05-ai/anti-patterns.md - 其他目录按需补全
很多项目可能永远不会写满六目录,这是正常的。但一旦写到第 4 步,说明你的项目已经到了"AI 反复踩坑"的密度,继续往下补反而是工程化收益最高的事。
3.3 双入口的分工
| 文件 | 主要读者 | 角色 |
|---|---|---|
AGENTS.md | Codex / Cursor / 通用 agent | 入口摘要,200 行内 |
CLAUDE.md | Claude Code | Claude 专属补丁,引用 AGENTS.md |
两个工程约束:
AGENTS.md控制在 200 行以内,超过就该把内容下沉到docs/。入口一旦变长,AI 就开始挑着读,等于没读。CLAUDE.md第一段必须明写**"冲突时以 AGENTS.md 为准"**。不写这条,Claude Code 在工作流偏好和项目规则冲突时会瞎猜。
3.4 AGENTS.md 真实样例
下面是一份脱敏后的 AGENTS.md 精简版:
# AGENTS.md
## 项目基本约定
- Vue 2.6,不要用 Vue 3 语法
- 微前端架构(子应用模式)
- 使用项目自定义 UI 库,不要直接用原版 Element UI
## 命名规范
- 组件文件:PascalCase
- 工具函数:camelCase
- 常量:UPPER_SNAKE_CASE
## 必须使用的组件
- 弹窗 → 自定义 Dialog
- 表单 → 自定义 Form
- 不要混用 el-form 和自定义 Form
## 禁止的反模式
- 禁止在组件 data 里塞过多页面状态(改用项目约定的页面级状态方案)
- 禁止用 $bus.$emit 跨组件通信
- 禁止裸 setInterval(改用 $setInterval)
## AI 不确定时的默认行为
- 不要猜接口字段名,问我
- 不要发明项目里不存在的工具函数,问我
- 涉及架构建议前,先读 docs/05-ai/forbidden-suggestions.md
注意这份文档的几个特征:
- 每条规则都是可执行的,不是"应该尽量"这种软话
- 每条禁令后面都跟一个"改用什么",不让 AI 卡在"那我该怎么办"
- 结尾有"AI 不确定时的默认行为" —— 这条最关键,它把"我猜错了"的概率压下来
AGENTS.md 不只是给人看的文档,更是 prompt 的项目级版本 —— 把"每次都要重新告诉 AI 的事"沉淀成一个文件。
3.5 00-global / 01-context / 02-architecture:三层基础设施
这三层我用一张表交代清楚,不展开 —— 因为对大多数读者来说,真正的"杀招"在 03–05。
| 目录 | 定位 | 典型文件 |
|---|---|---|
00-global/ | 项目宪法:所有 AI / 任务都必须遵守的底线 | principles.md / tech-stack.md / non-goals.md |
01-context/ | 业务背景:让 AI 知道项目是干嘛的、用户是谁 | business-overview.md / domain-terms.md |
02-architecture/ | 技术骨架 + ADR:系统形状与历史决策 | system-overview.md / state-management.md / decisions/ |
三层各自的关键判断:
non-goals.md比principles.md更重要。规则文档最大的盲区是"项目不做什么",AI 会在这个盲区里反复推销"业界都在做的事"。tech-stack.md必须把版本钉死。AI 训练数据里有"业界最新版本",但你项目用的不是。版本以这个文件为准,AI 不要建议升级。decisions/子目录是 ADR 容器。架构决策的版本化历史挂在这里。每个 ADR 必须有AI 注意节,写"AI 可能会怎么绕回老路"。
3.6 03-components:组件规约层(重点)
这是最具操作性的一层 —— AI 写每一个组件都要查这里。
每个组件文档强制四块:Props / Slots / Events / 最小可工作示例。
写作守则就一条:高频组件和易误用组件必须有错例对照。低频组件可以降级为软规则,不强求全覆盖。
为什么单挑出来讲?因为大多数组件文档的失败模式是:只写正例,不写反例。AI 看到正例,以为只是"推荐这么写";看到反例,才知道"那么写会出事"。
举个例子。一个表单组件的错例对照:
## ❌ 反例:混用项目自定义 Form 和原生 form-item
错误代码:
<custom-form>
<el-form-item label="姓名">
<el-input v-model="name" />
</el-form-item>
</custom-form>
为什么错:
- 项目自定义 Form 和原生 form-item 的 prop 协议不兼容
- 校验逻辑会失效
- 视觉样式可能错位
正确写法:
<custom-form>
<custom-form-item label="姓名">
<custom-input v-model="name" />
</custom-form-item>
</custom-form>
这种错例对照,信息密度比正例高一个量级。正例告诉 AI "标准答案",反例告诉 AI "标准答案 + 错在哪 + 为什么错"。
3.7 04-patterns:写法范式层(重点)
这一层放典型页面的"标准做法剧本" —— 列表查询页、详情页、表单页、看板。
每个 pattern 必须包含的结构:
## Pattern: <名称>
### 适用场景
### 不适用场景
### 前置条件
### 标准步骤
### 涉及的组件
### 涉及的 ADR(反向引用)
### 完成判定(lint / 测试 / 手工验证)
### 此 pattern 不解决的问题
注意最后两节 —— "完成判定"和"此 pattern 不解决的问题"。这两节是大多数人不会写、但又是 AI 最需要的两节。
- 没有"完成判定",AI 写完不知道有没有写对
- 没有"此 pattern 不解决的问题",AI 会把列表页 pattern 套到详情页上
每个 pattern 文档都必须有反向引用 —— 引用对应 ADR 和组件文档。没有反向引用的 pattern 是孤立文档,AI 不会真正用到。
3.8 05-ai:AI 协作专属层(重点)
这是只为 AI 而写的一层。其他五层是项目通用知识 + AI 共享,这一层是 AI 专属增量。
三个核心文件:
anti-patterns.md—— AI 最容易犯的错(错例代码 + 为什么错 + 正确代码)forbidden-suggestions.md—— 已被否决的方案,反向警戒清单review-checklist.md—— AI 产出代码的 review 清单
anti-patterns 真实样例
## ❌ Anti-Pattern:用 $bus 跨组件通信
错误代码:
// FilterPanel.vue
this.$bus.$emit('refresh-table')
// TablePanel.vue
mounted() {
this.$bus.$on('refresh-table', this.fetchData)
},
beforeDestroy() {
this.$bus.$off('refresh-table') // 经常忘记
}
为什么错:
- 全局污染:$bus 是全局对象,事件名冲突难排查
- 隐式耦合:从代码搜不到"谁触发了 refresh-table"
- 销毁顺序:beforeDestroy 漏写一次就内存泄漏
正确写法:
// 用 store action 直接调用
this.pageStore.search()
forbidden-suggestions 真实样例
## 🚫 全量低代码方案
否决日期: 2026-03
相关 ADR: ADR-0002
否决理由: 页面之间共性不足以支撑全量 schema 化,强行做会牺牲灵活性
AI 注意:
- 不要建议"用 schema 描述页面"
- 不要建议"把现有页面改造成低代码生成"
- 半步低代码(配置 + 代码混合)可以,但全量低代码不做
这两份文档的差异是这套体系的精髓 —— 你必须分清楚它们各自管什么。
边界表(定海神针)
| 类型 | 管什么 | 例子 | 输出形式 |
|---|---|---|---|
| anti-pattern | 错误写法 | 裸 setInterval / $bus 通信 / 状态散落 | 错误代码 + 正确代码 |
| forbidden-suggestions | 已否决方案 | 全量低代码 / Page Schema Runtime / 重构底座 | 否决理由 + ADR 链接 |
anti-pattern 解决"代码怎么别写错",forbidden-suggestions 解决"方向怎么别再走回头路"。
这句话是这套体系里最重要的一句。
很多团队踩过的坑是 —— 把"已否决方案"写进 anti-patterns,然后发现 AI 还是会反复推销;或者把"反模式代码"写进 forbidden,结果文档变成一个超长的杂烩清单。两者必须分开,边界清晰,各管一段。
四、五条维护规则
讲清楚了"建什么",必须再讲"怎么维护"。否则这套体系建完就死了。
- anti-patterns 随 review 触发更新 —— 发现 AI 重复触犯同一类问题,就补一条最小记录:错误代码、为什么错、推荐写法。不强制日更,但发现就补。
- ADR 只增不删 —— 旧 ADR 标
Superseded by ADR-XXXX,保留决策历史轨迹。 - 每个 pattern 都要有反向引用 —— 引用 ADR 和组件文档,孤立文档不算数。
- 高频组件和易误用组件必须有错例对照 —— 低频组件可降级为软规则,不强求。
- 季度做一次「AI 失败档案」复盘 —— 抽 20 条 AI 误生成案例,反推哪个目录文档没写到位,补上去。
文档不是写出来的,是迭代出来的。AI 协作约束体系最大的成本不在搭建,在持续维护。
五、AI 阅读顺序(给读者抄作业)
知道这套结构怎么搭还不够,要让它真正生效,还得在入口文件里规定 AI 的阅读顺序。
Codex / 通用 agent:
1. AGENTS.md (每次必读)
2. docs/00-global/principles.md (写代码前必读)
3. docs/01-context/<匹配模块>.md (涉及业务术语)
4. docs/02-architecture/<相关>.md (涉及架构)
5. docs/03-components/<用到的>.md (写每个组件前)
6. docs/04-patterns/<匹配任务>.md (做典型任务)
7. docs/05-ai/forbidden-suggestions.md (提架构建议前)
Claude Code:
1. CLAUDE.md
2. AGENTS.md (CLAUDE.md 引用过来)
3. 同上 3-7
最关键的一条工程约束 —— 在 AGENTS.md 和 CLAUDE.md 里都明写:
涉及做架构建议或提出新方案时,必须先读
docs/05-ai/forbidden-suggestions.md和docs/02-architecture/decisions/。已被否决的方案不要再提。
这条规则专门拦"AI 善意地推销已死方案" —— 这是单人和团队最常见的失控点之一。
六、这套体系不解决什么
再强调一遍,这一节我不能省。
我列五件这套体系不能解决的事:
- 不能让 AI 写出"有创意的代码"
- 不能解决"AI 误解需求"的问题
- 对完全没有项目规范的新项目,价值有限
- 需要一次性投入(写一份合格的 AGENTS.md 不便宜)
- 不能原封不动跨项目复用 —— 结构可以复用,内容必须按项目重写
这不是银弹,是脚手架。它让你和 AI 协作的下限抬高,但不抬高上限。
如果你期待它把你的 AI 产出从 60 分提到 95 分,你会失望。它做的是把 30 分的乱码提到稳定的 70 分,消灭"灾难性输出"。在 AI 协作时代,这个比"追求 95 分"更重要。
七、这套体系真正给我带来的变化
讲收益,降调讲。
- 复杂页面接入 AI 的时间显著下降 —— 因为 AI 知道往哪填,不再"用代码探边界"
- AI 产出代码的 review 时间下降 —— 从"找 bug"降级为"确认结构"
- 新人接手时,看 AGENTS.md 比看代码更快理解项目 —— 这条最让我意外
- code review 不再被"小问题"占满 —— 因为前置约束挡住了大多数小问题
但最大的变化不是这些。
最大的变化是:我开始把"工程师的价值"从"产出"重新定位到"约束"。
这套约束的目标不是消灭 review,是让 review 从"找 bug"降级为"确认结构"。
AI 让"会写代码"贬值了,让"知道代码该写成什么样"升值了。 前者是在产线上拧螺丝,后者是在定义产线怎么运转。
八、这是一个正在验证的工程假设
我说的这些不是已被证明的最佳实践,只是一个一线工程师从复杂中后台项目里看到的方向。
AI 协作时代的工程基建会长什么样,现在没人知道。
我的判断是:未来的前端工程师价值,会从"写代码"转向"定义代码应该被生成到哪里"。
「双入口 + 六目录」是我的一个尝试,vue-page-store 这样的容器型 API 是另一个尝试。
可能都不对,但方向我认为是对的。
九、为什么我把这件事写出来
如果你看过我上一篇文章《复杂中后台页面状态治理:我为什么做 vue-page-store》,你会发现这两件事的内核是一样的。
vue-page-store 是我在"代码层面"做工程秩序提炼的样本。 「双入口 + 六目录」是我在"协作层面"做同一件事的样本。
它们都不是"我做了个东西"。
它们都是"我在一个具体语境里看到了结构性问题,然后判断哪些值得抽象、哪些值得沉淀"。
换一个项目、换一种协作方式,我不一定还会做这两件具体的东西。但我会用同样的方法 —— 在那个项目里找到真正值得被抽象、被治理、被沉淀的东西。
一个工程师真正的护城河,是他在混乱里能看见什么。
看见之后,工具只是结果,方法只是路径。
工具会过时,方法会迭代,判断力不会。
开源信息
- GitHub: weijianjunwjj/ai-code-rules
- 包含完整模板、ADR 样例、anti-pattern 样例、forbidden-suggestions 样例
- 配套阅读:《复杂中后台页面状态治理:我为什么做 vue-page-store》
欢迎所有针对设计的争议 —— 比 star 更值钱。