从"让 AI 写代码"到"约束 AI 产出":复杂 Vue 老系统中的 AI 协作治理实践

38 阅读11分钟

引子:很多人把 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 约束"都不够

意识到边界问题后,大多数人会本能地走两条路:

  1. 写更详细的提示词 —— 每次让 AI 写代码前,把项目规范贴进去
  2. 加更严格的 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 重要提醒:不要一上来写满六目录

我先把丑话说在前面 —— 不是所有项目一上来都要写满六目录

六目录是增长结构,不是首日交付清单。

正确的接入节奏是:

  1. 先写 AGENTS.md —— 提炼 5–10 条项目红线
  2. docs/00-global/non-goals.md —— 告诉 AI 不做什么
  3. 写第一份 ADR —— 记录最重要的一个架构决策
  4. 边踩坑边写 docs/05-ai/anti-patterns.md
  5. 其他目录按需补全

很多项目可能永远不会写满六目录,这是正常的。但一旦写到第 4 步,说明你的项目已经到了"AI 反复踩坑"的密度,继续往下补反而是工程化收益最高的事。

3.3 双入口的分工

文件主要读者角色
AGENTS.mdCodex / Cursor / 通用 agent入口摘要,200 行内
CLAUDE.mdClaude CodeClaude 专属补丁,引用 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

注意这份文档的几个特征:

  1. 每条规则都是可执行的,不是"应该尽量"这种软话
  2. 每条禁令后面都跟一个"改用什么",不让 AI 卡在"那我该怎么办"
  3. 结尾有"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.mdprinciples.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,结果文档变成一个超长的杂烩清单。两者必须分开,边界清晰,各管一段


四、五条维护规则

讲清楚了"建什么",必须再讲"怎么维护"。否则这套体系建完就死了。

  1. anti-patterns 随 review 触发更新 —— 发现 AI 重复触犯同一类问题,就补一条最小记录:错误代码、为什么错、推荐写法。不强制日更,但发现就补。
  2. ADR 只增不删 —— 旧 ADR 标 Superseded by ADR-XXXX,保留决策历史轨迹。
  3. 每个 pattern 都要有反向引用 —— 引用 ADR 和组件文档,孤立文档不算数。
  4. 高频组件和易误用组件必须有错例对照 —— 低频组件可降级为软规则,不强求。
  5. 季度做一次「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.mdCLAUDE.md 里都明写:

涉及做架构建议或提出新方案时,必须先读 docs/05-ai/forbidden-suggestions.mddocs/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 是我在"代码层面"做工程秩序提炼的样本。 「双入口 + 六目录」是我在"协作层面"做同一件事的样本。

它们都不是"我做了个东西"。

它们都是"我在一个具体语境里看到了结构性问题,然后判断哪些值得抽象、哪些值得沉淀"。

换一个项目、换一种协作方式,我不一定还会做这两件具体的东西。但我会用同样的方法 —— 在那个项目里找到真正值得被抽象、被治理、被沉淀的东西

一个工程师真正的护城河,是他在混乱里能看见什么

看见之后,工具只是结果,方法只是路径。


工具会过时,方法会迭代,判断力不会。


开源信息

欢迎所有针对设计的争议 —— 比 star 更值钱