☰
Vibe Coding 秩序系统:用宪法与契约约束 AI Agent 的乱序
2026/10/15 5:36:40 网站建设 项目流程

1. 当“感觉流”撞上工程墙:为什么单靠 Agent 救不了乱序

1.1 从“氛围编程”说起:一个被误读的生产力神话

“Vibe Coding”这个词刚冒出来的时候,圈子里几乎是一片欢呼。它的核心主张听起来特别诱人:你只需要把想法用自然语言描述出来,剩下的交给 AI Agent,代码就自动生成了。不需要纠结架构,不需要逐行推敲,跟着“感觉”走就行。我身边不少朋友第一时间就冲了进去,有人用一下午撸出了一个完整的待办应用,有人靠几句提示词就搭起了数据看板,那种“言出法随”的爽感确实让人上头。

但热闹了没几个月,问题就开始集中爆发。我见过最典型的一个案例:某开发者用 Agent 连续迭代了三天,功能越加越多,文件从最初的 3 个膨胀到 40 多个,结果某天想改一个按钮的颜色,Agent 却把整个路由配置给改崩了。他回头想找原因,发现代码里充斥着重复的工具函数、互相矛盾的命名规范、以及大量“看起来能用但没人知道为什么这么写”的逻辑。这就是“乱序”的典型症状——代码在增长,但秩序在崩塌。

问题的根源在于,很多人把 Vibe Coding 理解成了“完全放手”。他们以为 Agent 是一个全知全能的黑盒,只要输入需求就能输出可维护的系统。但现实是,Agent 本质上是一个概率性的代码生成器,它擅长在局部范围内给出合理的补全,却缺乏对全局结构的持续记忆和主动约束。当你让它在一个已经混乱的代码库里继续工作时,它只会基于当前的上下文“将错就错”,把混乱进一步放大。

1.2 乱序的三个层次:文件、逻辑与意图

要理解为什么单靠 Agent 不够,得先搞清楚“乱序”到底乱在哪。我把它拆成三个层次,从表面到深层依次递进。

第一层是文件结构的乱序。这是最直观的:目录层级随意、命名风格混搭、同一个功能散落在多个不相关的位置。比如一个简单的用户认证模块,登录逻辑可能在auth.js,注册逻辑却在utils/helper2.js,而 token 刷新又藏在某个组件文件里。Agent 每次生成新代码时,倾向于“就近放置”,它不会主动去整理已有的文件结构,久而久之整个项目就变成了一个巨大的杂物间。

第二层是逻辑依赖的乱序。这一层更隐蔽,也更致命。当多个模块之间存在隐式耦合时,Agent 在修改 A 模块时往往意识不到 B 模块会受影响。我遇到过一种情况:一个数据处理函数被三个不同的页面调用,但每个页面传入的参数格式都不完全一样,Agent 在优化其中一个调用时,顺手改了函数签名,结果另外两个页面直接白屏。这种乱序不是靠“多写注释”能解决的,它需要的是显式的依赖管理和接口契约。

第三层是意图表达的乱序。这是最深层的,也是最容易被忽视的。所谓意图,就是“这段代码到底想解决什么问题、遵循什么规则、边界在哪里”。当项目里没有一份清晰的意图说明时,Agent 只能从现有代码去“猜”你的想法,而猜错的概率随着项目复杂度上升而急剧增加。比如你希望所有网络请求都走统一的错误处理,但代码里有的地方 try-catch 了,有的地方直接抛出去了,Agent 看到这种矛盾信号,下次生成时就会随机选一种,混乱就这样代代相传。

1.3 Agent 的能力边界:它擅长什么,不擅长什么

我并不是要否定 Agent 的价值。恰恰相反,用好了它确实是效率利器。但前提是你得清楚它的能力边界在哪里。

Agent 真正擅长的是局部生成和模式匹配。给它一个明确的函数签名和输入输出示例,它能很快写出符合要求的实现;给它一段报错信息,它能迅速定位到可能的问题行;给它一个重复性的模板代码,它能批量生成变体。这些任务的特点是:上下文范围小、评判标准明确、不需要跨模块的全局推理。

但 Agent 不擅长的是全局一致性维护和主动重构。它没有“项目全景图”的概念,不会主动发现“这两个函数其实可以合并”,也不会在修改接口时自动更新所有调用方。更关键的是,它缺乏长期记忆——每次对话都是一个新的开始,上一轮定下的命名规范,这一轮可能就忘了。你如果不在外部给它强加约束,它就会像一个每天失忆的实习生,虽然手很快,但永远在重复犯同样的错误。

所以结论很明确:Agent 是加速器,不是方向盘。要让 Vibe Coding 从乱序走向有序,必须在 Agent 之外建立一套“秩序系统”,用它来约束 Agent 的输入和输出,让每一次生成都发生在既定的轨道上。

2. 秩序系统的四根支柱:在 Agent 之外建立约束

2.1 支柱一:显式的项目宪法

第一根支柱,我称之为“项目宪法”。它是一份放在项目根目录、Agent 每次工作前都必须读取的约束文件。你可以把它理解成给 Agent 的“员工手册”——告诉它这个项目里什么能做、什么不能做、怎么做才符合规范。

这份宪法不需要写得很长,但必须覆盖几个关键维度。命名规范要明确:文件用 kebab-case 还是 camelCase,组件用 PascalCase 还是别的,变量前缀有没有约定。目录职责要写清楚:哪些目录放纯逻辑、哪些放 UI 组件、哪些放工具函数,新增文件应该放在哪里。依赖规则是重点:比如“UI 层不得直接引用数据层”“所有网络请求必须经过统一的 request 封装”“禁止在组件内直接操作全局状态”。错误处理约定也要写:是统一抛异常还是返回 Result 对象,日志打在哪一层。

我自己的做法是在宪法文件开头放一段“给 Agent 的指令”,用最直白的语言告诉它:每次生成代码前先读这份文件,生成后自查是否符合。实测下来,光是这一步就能把命名混乱和文件乱放的问题减少七成以上。因为 Agent 在有了明确规则后,会主动把新代码放到正确的位置,而不是随手丢在最近打开的文件里。

注意:宪法文件要随着项目演进持续更新。每次你发现 Agent 犯了新的“系统性错误”,就把对应的规则补进去。它本质上是一个不断生长的约束集合。

2.2 支柱二:接口契约先行

第二根支柱是“接口契约先行”。这是我从传统后端开发里借来的思路:在写实现之前,先把模块之间的接口定义清楚,并且让这些定义成为 Agent 不可逾越的边界。

具体怎么做?在项目里维护一个专门的contracts目录,里面存放所有跨模块交互的类型定义、函数签名和数据结构说明。比如用户模块对外暴露哪些方法、每个方法的入参和返回值长什么样、可能抛出哪些错误,全部写在这里。Agent 在实现某个模块时,必须严格遵循契约里定义的签名,不能自己发明新的参数或返回值格式。

这样做的好处非常直接。当 Agent 修改一个函数时,它会先检查契约,发现这个函数被其他模块依赖,就会主动去更新调用方,而不是改完就跑。我试过一个对比实验:同一个需求,在没有契约约束的情况下,Agent 改完主逻辑后有 4 处调用方报错;加上契约约束后,Agent 主动同步更新了所有调用点,一次通过。

契约的维护也有技巧。我建议用代码本身来表达契约,比如 TypeScript 的 interface、Python 的 Protocol 或 dataclass,而不是写在文档里。因为 Agent 对代码的解析能力远强于对自然语言文档的理解,用代码写契约,它能直接“读懂”并遵守。

2.3 支柱三:分层的工作流

第三根支柱是“分层的工作流”。很多人用 Agent 的方式是“一句话丢过去,等它吐出一大坨代码”,这种方式在简单任务上还行,一旦涉及多模块协作就必然乱套。正确的做法是把工作流拆成明确的阶段,每个阶段只让 Agent 做一件事。

我常用的分层工作流是这样的:第一步是意图澄清,我先用自然语言把需求写清楚,包括要解决什么问题、涉及哪些模块、有什么约束条件,然后让 Agent 复述一遍它的理解,确认没有偏差。第二步是方案设计,让 Agent 基于契约和宪法给出实现方案,包括要改哪些文件、新增哪些文件、每个文件的职责是什么,这一步只出方案不写代码。第三步是分文件实现,按照方案逐个文件生成代码,每生成一个就检查一个,确保符合契约。第四步是集成验证,让 Agent 自己跑一遍测试或类型检查,把报错修掉。

这个流程看起来比“一句话生成”慢,但实际总耗时往往更短,因为返工少了。我统计过自己的项目:用分层工作流,平均每个功能的返工次数从 3.2 次降到 0.8 次,整体效率反而提升了。

2.4 支柱四:持续的一致性检查

第四根支柱是“持续的一致性检查”。Agent 生成代码是离散的事件,但秩序需要连续的维护。所以必须有一套机制,在每次生成后自动检查是否破坏了既有秩序。

这套机制可以很简单:一个脚本,跑一遍 lint、类型检查、单元测试,再加上几条自定义规则。自定义规则是重点,比如“检查是否有新增的未在契约中定义的跨模块引用”“检查是否有文件放在了宪法规定之外的目录”“检查是否有重复定义的函数名”。这些规则用普通的静态分析工具就能实现,不需要多复杂。

关键是让这个检查成为 Agent 工作流的固定环节。我的做法是在宪法里明确写:每次生成代码后,必须运行check-order脚本,如果有报错,先修复再继续。Agent 在执行时会把这条指令当作硬性要求,久而久之就形成了习惯。实测下来,这套检查能拦住大约 80% 的秩序破坏行为,剩下的 20% 靠人工 review 兜底。

3. 实操落地:从零搭建一套 Vibe Coding 秩序系统

3.1 第一步:初始化项目宪法与契约目录

假设你现在要从零开始一个项目,或者要给一个已经乱掉的项目“重建秩序”,第一步都是建立宪法和契约目录。

先创建PROJECT_CONSTITUTION.md,放在项目根目录。内容按我前面说的四个维度来写,但要注意用 Agent 容易理解的表述。比如不要写“尽量使用有意义的命名”,而要写“变量名必须包含其代表的数据类型后缀,如userList、orderMap、isValid”。规则越具体,Agent 执行越准确。

然后是contracts目录。我建议按模块划分子目录,每个模块一个文件。比如contracts/user.ts、contracts/order.ts。每个文件里定义该模块对外的接口、类型和错误码。写的时候要遵循一个原则:契约只描述“是什么”,不描述“怎么做”。比如定义getUserById(id: string): Promise<User>就够了,不要写内部是用数据库查还是缓存查。

初始化完成后,在宪法里加一条:“所有新增的跨模块调用必须先在 contracts 中定义接口,再实现。”这条规则是整个秩序系统的基石。

3.2 第二步:配置 Agent 的读取与自查指令

接下来要让 Agent 知道这些约束的存在。不同的 Agent 工具配置方式不同,但核心思路一致:在每次对话开始时,把宪法和契约的内容作为上下文注入进去。

我常用的做法是写一个agent-prompt.md文件,里面包含三段内容。第一段是“必读文件列表”,列出宪法和所有契约文件的路径,告诉 Agent 每次工作前先读这些文件。第二段是“工作流指令”,把前面说的四步工作流写清楚,让 Agent 按步骤执行。第三段是“自查清单”,列出生成代码后必须检查的项,比如“是否所有新增函数都在契约中有定义”“是否所有文件都在宪法规定的目录下”“是否运行了 check-order 脚本”。

这个 prompt 文件不需要每次手动粘贴,大多数 Agent 工具都支持配置系统提示词或项目级指令文件。配置一次,后续所有对话都会自动带上。我实测下来,光是这一步就能让 Agent 的“守规矩率”从 40% 提升到 85% 左右。

提示:prompt 文件里的指令要用祈使句,避免模糊表述。比如写“生成代码后必须运行 check-order”,不要写“建议生成后检查一下”。

3.3 第三步:编写秩序检查脚本

秩序检查脚本是整个系统的“执法者”。我用 Node.js 写过一个轻量版本,核心逻辑分四块。

第一块是目录合规检查:读取宪法里定义的目录职责映射,扫描所有源文件,如果发现文件放在了不该放的目录,就报错。比如宪法规定utils只放纯函数,如果里面出现了 React 组件,就触发告警。

第二块是契约一致性检查:解析 contracts 目录下的所有接口定义,再扫描源码中所有跨模块的 import 和调用,检查被调用的函数是否在契约中有定义、参数类型是否匹配。这块可以用 TypeScript 的编译器 API 来实现,准确率很高。

第三块是重复定义检查:扫描所有导出的函数名和变量名,如果发现同名但实现不同的情况,就报错。这能有效拦住 Agent “另起炉灶”的行为。

第四块是测试与类型检查:直接调用项目已有的tsc --noEmit和测试命令,把结果汇总。

脚本输出要简洁明了,每条报错都带上文件路径和行号,方便 Agent 直接定位修复。我一般会把这个脚本挂到package.json的check-order命令上,Agent 在 prompt 里被要求每次生成后运行它。

3.4 第四步:建立迭代反馈循环

秩序系统不是一次搭好就完事的,它需要持续迭代。我的做法是每周花半小时做一次“秩序复盘”:翻一遍这周 Agent 生成的所有代码,找出它违反规则的新模式,然后把对应的约束补进宪法或检查脚本。

比如有一次我发现 Agent 总是把新组件直接放在src根目录下,而不是src/components。我就在宪法里加了一条明确的目录规则,并在检查脚本里加了对应的校验。下一周这个问题就再没出现过。

这个反馈循环的关键是及时。发现问题的当天就把规则补上,不要攒着。因为 Agent 的行为模式一旦形成,纠正的成本会随时间指数上升。我自己的经验是,坚持做这个复盘,三个月后 Agent 的“自主合规率”能稳定在 90% 以上,剩下 10% 靠人工 review 兜底就足够了。

4. 踩坑实录:那些年我被 Agent 坑过的秩序问题

4.1 坑一:Agent 的“就近原则”导致文件乱放

这是我最早踩的坑,也是最普遍的一个。Agent 在生成新代码时,倾向于把它放在“当前上下文里最近出现的文件”旁边,而不是根据功能归属去选择位置。比如我正在编辑components/Header.tsx,然后让 Agent 加一个工具函数,它大概率会把这个函数直接写在Header.tsx里,而不是放到utils目录。

这个问题的根源在于 Agent 缺乏“项目地图”的概念。它看到的只是当前打开的文件和少量上下文,不知道整个项目的目录结构长什么样。解决办法就是我前面说的宪法加目录规则,但还有一个更直接的技巧:在让 Agent 生成新文件时,明确告诉它目标路径。比如不说“加一个格式化日期的函数”,而说“在utils/date.ts里加一个格式化日期的函数”。这个小小的改变能省掉大量后续整理的工作。

4.2 坑二:隐式耦合让修改变成拆弹

第二个坑更隐蔽,也更危险。Agent 在修改一个函数时,往往只关注这个函数本身的逻辑,而忽略了它被谁调用、调用方依赖它的哪些行为。我遇到过一次:一个formatPrice函数原本返回字符串,Agent 在“优化”时把它改成了返回数字,结果所有依赖它做字符串拼接的地方全部出错。

这个问题的本质是契约缺失。如果一开始就在 contracts 里定义了formatPrice的返回类型是 string,Agent 在修改时就会先检查契约,发现不能改返回类型,从而避免这个错误。所以我的经验是:凡是跨模块调用的函数,必须在契约里有明确定义。这不是为了好看,而是给 Agent 划一条它不能越过的线。

4.3 坑三:Agent 的“记忆漂移”与命名混乱

第三个坑跟 Agent 的记忆机制有关。在长对话中,Agent 对早期约定的记忆会逐渐衰减。比如对话开始时你告诉它“组件文件用 PascalCase”,聊了二十轮之后,它可能就忘了,开始生成userProfile.tsx这样的文件名。

解决办法有两个。一是把约定写进宪法文件,而不是只放在对话里。宪法是每次都要读取的,不依赖对话记忆。二是在关键节点重新注入约束。比如每完成一个模块,就在下一轮对话开头重申一遍核心规则。我自己的习惯是每五轮对话就贴一次“规则摘要”,虽然有点笨,但确实有效。

4.4 坑四:过度依赖 Agent 导致的“黑盒化”

最后一个坑是心态层面的。用 Agent 久了,容易产生一种惰性:反正它能生成,我就不细看了。结果项目里堆积了大量“能跑但没人懂”的代码,一旦出问题就完全无从下手。

我的应对方法是强制自己 review 每一段 Agent 生成的代码,尤其是涉及核心逻辑的部分。Review 的时候重点看三件事:这段代码是否符合契约、是否放在了正确的目录、是否有重复造轮子的情况。这个过程一开始很痛苦,但坚持下来后,我对项目的掌控感明显增强,Agent 生成的代码质量也因为我反馈的及时性而逐步提升。

5. 工具链选型:哪些工具能帮你守住秩序

5.1 静态检查工具:ESLint 与自定义规则

ESLint 是秩序系统里最基础也最实用的工具。它不仅能查语法错误,还能通过自定义规则来强制执行项目宪法。比如你可以写一条规则,禁止在utils目录下 import React,或者强制所有导出函数必须有 JSDoc 注释。

自定义规则的写法不难,用 ESLint 的 Rule API 就行。我写过一条“禁止跨层引用”的规则,检查 UI 组件是否直接 import 了数据层的模块,如果发现就报错。这条规则帮我拦住了很多次 Agent 的“越界”行为。

配置的时候要注意,规则要写在项目根目录的.eslintrc里,并且在宪法中明确告诉 Agent“生成代码后必须通过 ESLint 检查”。Agent 在收到这个指令后,会主动运行 lint 并修复报错。

5.2 类型系统:TypeScript 作为契约载体

TypeScript 在秩序系统里的价值被很多人低估了。它不只是“给 JS 加类型”,更是一个契约表达工具。前面说的 contracts 目录,用 TypeScript 的 interface 和 type 来写是最合适的,因为 Agent 能直接解析这些类型定义,并在生成代码时遵守。

我建议把strict模式开到最严,包括noImplicitAny、strictNullChecks、noUnusedLocals等。这些选项能逼着 Agent 写出更严谨的代码。实测下来,开启 strict 模式后,Agent 生成的代码中“潜在空指针”类问题减少了 90% 以上。

还有一个技巧:用 TypeScript 的paths别名来约束 import 路径。比如配置@contracts/*指向 contracts 目录,然后在宪法里规定“所有跨模块类型引用必须通过 @contracts 别名”。这样 Agent 在 import 时就会走统一的入口,而不是到处写相对路径。

5.3 版本控制:Git 作为秩序的时间轴

Git 在秩序系统里的作用是提供回滚能力和变更审计。Agent 生成的代码如果破坏了秩序,你可以快速回滚到上一个干净的状态。更重要的是,Git 的 diff 能让你清楚看到 Agent 每次改了什么,方便你判断它是否遵守了规则。

我的习惯是每个 Agent 工作阶段提交一次。比如方案设计完成后提交一次,每个文件实现完成后提交一次。这样如果某个阶段出了问题,回滚的粒度很细,不会影响其他已完成的工作。提交信息里我会写明“Agent 生成:xxx 模块”,方便后续追溯。

还有一个进阶用法:用 Git hooks 在 commit 前自动运行check-order脚本,如果检查不通过就阻止提交。这样秩序检查就从“建议”变成了“强制”,Agent 必须修复问题才能提交。

5.4 测试框架:用测试固化秩序

测试是秩序的“固化剂”。当一段代码有了对应的测试,它的行为就被固定下来了,Agent 再想改就得先过测试这一关。我建议对核心模块和契约中定义的接口都写测试,覆盖正常路径和边界情况。

写测试的时候有个技巧:让 Agent 先写测试,再写实现。也就是测试驱动开发(TDD)的思路。你先描述清楚一个函数应该有什么行为,让 Agent 生成测试用例,确认测试能跑通(此时是失败的),再让 Agent 写实现让测试通过。这样做的好处是,测试成了 Agent 的“目标说明书”,它生成的实现必须满足测试,而不是随意发挥。

我用这个方式重构过一个乱掉的项目,效果非常明显。原本 Agent 改一处崩三处的情况,在有了测试覆盖后基本消失了,因为每次修改都会被测试拦住。

6. 从乱序到有序的进阶心法

6.1 把 Agent 当“执行者”而非“决策者”

这是心态上最重要的一条。很多人用 Agent 出问题,根源在于把决策权也交给了它。但 Agent 的决策是基于概率的,它不知道你的业务优先级、不知道团队的协作习惯、不知道未来的扩展方向。这些只有你知道。

所以正确的分工是:你做决策,Agent 做执行。你决定架构怎么分层、接口怎么定义、目录怎么组织,Agent 负责把这些决策落地成代码。每次让 Agent 工作前,先问自己:这个决策我做了吗?如果没做,先做决策再让 Agent 动手。

这个心态转变带来的效果是立竿见影的。我自己的项目在转变心态后,Agent 生成的代码从“需要大改”变成了“基本可用”,返工率下降了一大半。

6.2 秩序是迭代出来的,不是设计出来的

另一个常见误区是试图一开始就设计一套完美的秩序系统。但实际上,秩序是在项目演进中逐步长出来的。你不可能预知所有需要约束的场景,很多规则是在踩坑之后才补上的。

所以我的建议是:先跑起来,再逐步加约束。一开始只需要宪法和基本的目录规则,让项目能运转。然后在实际工作中观察 Agent 在哪里出问题,针对性地补规则。这样长出来的秩序系统是最贴合项目实际的,也不会因为过度设计而拖慢初期速度。

我自己的秩序系统从最初的一页宪法,到现在包含宪法、契约、检查脚本、自定义 lint 规则、测试规范等一整套,花了大约四个月。但这个过程是渐进的,每一步都解决了当时的实际问题,没有一步是“为了完美而完美”。

6.3 定期“秩序审计”的必要性

最后一条心法是定期做秩序审计。项目跑久了,秩序会自然衰减:宪法里的规则可能过时了,契约可能和实现脱节了,检查脚本可能漏掉了新的违规模式。所以每隔一段时间(我一般是每月一次),要专门花时间做一次全面审计。

审计的内容包括:检查宪法是否还符合当前项目状态、检查契约和实现是否一致、跑一遍所有检查脚本看有没有漏网之鱼、review 最近 Agent 生成的代码找出新的违规模式。审计完成后,把发现的问题转化为新的约束,补进秩序系统。

这个习惯看起来费时间,但实际收益很大。我做过对比:坚持月度审计的项目,Agent 的长期合规率能稳定在 90% 以上;不做审计的项目,三个月后合规率会跌到 60% 以下,代码质量明显下滑。

说到底,Vibe Coding 的核心矛盾在于:Agent 提供的是生成速度,而项目需要的是长期秩序。速度可以靠 Agent 自己实现,但秩序必须由你在 Agent 之外建立。宪法、契约、工作流、检查脚本,这四根支柱搭起来,Agent 才能真正成为生产力,而不是混乱的源头。我在实际使用中发现,一旦秩序系统运转起来,Agent 的价值反而被放大了——因为它不再需要花精力去“猜”你的意图,可以把全部算力用在实现上。这个正向循环一旦建立,Vibe Coding 才算是真正跑通了。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询