1. 规则文件写了却没反应,问题到底出在哪
刚上手 CodeBuddy 那阵子,我踩过一个特别典型的坑:花了大半个下午精心写了一份CODEBUDDY.md,把项目的编码规范、目录约定、命名风格、提交信息格式全塞了进去,结果在对话里让它帮我改一个函数,它给出的代码跟我的规范八竿子打不着。我当时第一反应是"这工具是不是不支持自定义规则",差点就放弃了。后来翻了不少资料、做了几轮对照实验才发现,问题根本不在工具本身,而在于我没搞懂它的规则加载机制——文件放错位置、frontmatter 写错、glob 匹配不上,任何一个环节出问题,规则都会静默失效,连个报错都不给你。
这篇就把我踩过的坑和后来摸清楚的加载逻辑完整梳理一遍。核心围绕三样东西:CODEBUDDY.md这个规则载体、rules这套校验规则体系、以及frontmatter和glob这两个决定规则"生不生效"的关键字段。如果你正在用 CodeBuddy,或者从别的 AI 编程工具(比如 Cursor、Trae 这类同样支持 rules 的工具)迁移过来,发现规则时灵时不灵,那这篇基本能帮你把问题定位到具体某一层。我会从整体设计思路讲到具体实操,再到常见故障排查,尽量让刚接触的人也能照着复现。
先说结论性的判断:规则不生效,九成以上是"加载路径"和"匹配条件"的问题,而不是规则内容本身写得不好。很多人一上来就怀疑自己规则写得不够详细,其实方向反了。工具读不到你的规则,你写一万字也是白搭。所以下面我会把"它到底从哪里读、按什么顺序读、什么条件下才应用"这三件事讲透。
2. 规则加载机制的整体设计与思路拆解
2.1 为什么要有 CODEBUDDY.md 和 rules 两套东西
很多人第一次接触会困惑:既然有了CODEBUDDY.md,为什么还要单独搞一套rules目录?这俩不是重复了吗?其实它们解决的是两个不同层次的问题,理解这一点是后面所有操作的基础。
CODEBUDDY.md更像是项目级的全局说明书。它通常放在项目根目录,作用范围是整个仓库,内容偏向"这个项目是什么、用什么技术栈、有哪些通用约定"。你可以把它理解成给 AI 看的一份 README,只不过这份 README 是专门用来约束 AI 行为的。它的特点是全局生效、内容宽泛、加载优先级相对靠后。
而rules目录下的规则文件,是细粒度的、可条件触发的。它允许你针对特定文件类型、特定目录、特定场景写专门的规则。比如"所有.tsx文件必须用函数式组件"、"api/目录下的请求必须走统一封装"、"测试文件里禁止使用only"。这些规则如果全塞进CODEBUDDY.md,文件会变得又臭又长,而且没法做到"只在编辑某类文件时才提醒 AI"。
所以设计上的分工很清晰:全局通用的放CODEBUDDY.md,场景化、条件化的放rules。我后来养成的习惯是,CODEBUDDY.md只保留不超过一屏的核心约定,剩下的全部拆到rules里按主题管理。这样维护起来清爽,出问题也容易定位——某类文件规则失效,我直接去看对应的那个 rule 文件就行,不用在几千字的 md 里大海捞针。
2.2 加载顺序与优先级:谁覆盖谁
这是最容易踩坑的地方。规则不是"全都读进来然后合并"这么简单,它是有优先级和覆盖关系的。根据我的实测和多方资料对照,大致的加载逻辑是这样的:
| 层级 | 来源 | 作用范围 | 优先级 |
|---|---|---|---|
| 1 | 用户级全局规则 | 所有项目 | 最低 |
| 2 | 项目根CODEBUDDY.md | 当前项目 | 中 |
| 3 | rules目录下的规则文件 | 按 glob 匹配 | 高 |
| 4 | 对话中临时指定的规则 | 当前会话 | 最高 |
这个顺序背后的逻辑其实很符合直觉:越具体、越靠近当前操作的规则,优先级越高。用户级全局规则是你个人的通用偏好,项目级规则是这个项目的特殊要求,而rules里的规则是针对具体文件的精确约束,临时对话指令则是你当下的即时意图。当它们冲突时,更精确的那一层说了算。
我踩的第一个大坑就在这里。当时我在用户级全局配置里写了一条"优先使用箭头函数",又在项目CODEBUDDY.md里写了"本项目统一使用function声明",结果 AI 一会儿用箭头一会儿用 function,我还以为是它"记性不好"。实际上是我自己制造了规则冲突,而我没搞清楚覆盖关系,导致行为看起来飘忽不定。规则冲突不会报错,只会表现为"时灵时不灵",这是最坑的地方。
提示:写规则前先想清楚这条规则应该放在哪一层。个人偏好放用户级,项目约定放项目级,文件类型相关的放 rules。不要在多处重复定义同一条规则,否则覆盖行为会让你怀疑人生。
2.3 frontmatter 和 glob:决定规则"生不生效"的开关
如果说加载路径决定规则"能不能被读到",那frontmatter和glob就决定规则"读到了之后会不会被应用"。这两个是 rules 文件里最关键的元数据,也是最容易写错的地方。
frontmatter是文件顶部用---包裹的那段元信息,通常包含规则的描述、触发条件、适用范围等。glob则是用来匹配文件路径的模式串,决定这条规则对哪些文件生效。举个我实际在用的例子:
--- description: React 组件编码规范 globs: - "src/components/**/*.tsx" - "src/components/**/*.jsx" alwaysApply: false --- - 组件必须使用函数式写法,禁止 class 组件 - Props 必须显式定义类型 - 每个组件文件只导出一个组件这里globs就是核心。它用的是标准的 glob 匹配语法,**表示任意层级目录,*表示任意文件名。如果我把globs写成src/component/*.tsx(少了个 s,或者少了个**),那src/components/button/index.tsx这种嵌套路径就匹配不上,规则自然不生效。我遇到过的"规则写了不生效",至少一半是 glob 写错导致的。
alwaysApply这个字段也值得单独说。它控制规则是"始终应用"还是"仅在匹配到 glob 时应用"。如果你希望某条规则无条件生效,就设成true,这时候globs可以留空。但如果你既设了alwaysApply: true又写了globs,不同工具的处理方式可能不一样,有的会忽略 globs,有的会取并集。为了行为可预测,我的建议是二选一,不要同时用。
3. 核心细节解析与实操要点
3.1 目录结构:文件到底该放哪
规则不生效,第一个要排查的就是"文件放对地方了吗"。CodeBuddy 读取规则是有固定路径约定的,放错位置它根本不会去看。常见的正确位置有这么几个:
- 项目根目录的
CODEBUDDY.md:全局项目规则 - 项目根目录下的
.codebuddy/rules/目录:存放各个 rule 文件 - 用户主目录下的全局配置目录:存放跨项目的个人规则
我一开始把规则文件随手放在了docs/目录下,心想"反正都是 md 文件,它应该能扫到吧"。结果当然是不生效。工具不会递归扫描整个项目去找规则文件,它只认约定好的那几个路径。这一点跟很多人的直觉相反,但恰恰是最需要记住的。
.codebuddy/rules/这个目录名也要注意大小写和拼写。我见过有人写成.codeBuddy、.code-buddy、codebuddy/rules(少了前面的点),全都不行。目录名是硬编码匹配的,差一个字符都读不到。建议直接从文档复制目录名,别手打。
3.2 frontmatter 字段逐个拆解
frontmatter 里每个字段都有明确用途,写错了轻则规则不生效,重则整个文件被跳过。我把常用的几个字段整理成表,方便对照:
| 字段 | 作用 | 常见错误 |
|---|---|---|
description | 规则描述,帮助识别 | 写成中文导致某些解析器乱码 |
globs | 匹配生效的文件路径 | 路径写错、漏写**、用了反斜杠 |
alwaysApply | 是否无条件应用 | 与 globs 同时设置导致行为不确定 |
priority | 规则优先级 | 数值写反,以为越大越优先 |
关于globs的写法,有几个细节必须强调。第一,路径分隔符统一用正斜杠/,即使在 Windows 上也是。我有个同事在 Windows 上写规则,习惯性用了反斜杠src\components\*.tsx,结果一条都没匹配上,排查了半天。第二,**和*的区别要分清:*只匹配当前层级的文件名,不跨目录;**才匹配任意层级。第三,多个 glob 用列表形式写,不要用逗号拼在一行,不同解析器对逗号分隔的支持不一致。
# 推荐写法:列表形式,清晰且兼容性好 globs: - "src/**/*.ts" - "src/**/*.tsx" # 不推荐:逗号分隔,兼容性存疑 globs: "src/**/*.ts, src/**/*.tsx"3.3 规则内容的写法:怎么让 AI 真的照做
文件放对了、frontmatter 写对了,接下来才是规则内容本身。这里也有讲究。我早期写的规则特别"客气",比如"建议尽量使用 TypeScript 的严格模式",结果 AI 经常忽略。后来我改成祈使句、明确指令,效果明显好转。
核心原则是:规则要写成明确的、可判定的指令,而不是模糊的建议。"尽量"、"建议"、"最好"这类词会让 AI 觉得这是可选项。改成"必须"、"禁止"、"统一使用"这种强约束词,执行率会高很多。另外,规则条目要具体到可操作,比如不要写"注意代码质量",而要写"函数参数超过 3 个时必须使用对象参数"。
还有一点,规则之间不要自相矛盾。我见过一个项目的规则里同时写着"禁止使用 any"和"复杂类型可以先用 any 占位",AI 遇到这种情况就会随机选一条执行,表现出来就是"规则时灵时不灵"。写规则前通读一遍,确保没有互相打架的条目。
4. 实操过程与核心环节实现
4.1 从零搭一套能生效的规则体系
下面这套流程是我现在每个新项目都会走一遍的,照着做基本不会踩加载的坑。
第一步,在项目根目录创建CODEBUDDY.md,只写最核心的全局约定,控制在 30 行以内:
# 项目约定 - 技术栈:React 18 + TypeScript + Vite - 包管理器:pnpm,禁止使用 npm 或 yarn - 提交信息遵循 Conventional Commits - 所有新增代码必须通过 ESLint 检查第二步,创建.codebuddy/rules/目录,按主题拆分规则文件。比如react-components.md、api-layer.md、testing.md。每个文件顶部写清楚 frontmatter:
--- description: API 层请求规范 globs: - "src/api/**/*.ts" alwaysApply: false --- - 所有请求必须通过 `src/api/client.ts` 导出的实例发起 - 禁止在组件内直接使用 fetch 或 axios - 请求函数必须显式声明返回类型 - 错误必须统一走 `handleApiError` 处理第三步,验证规则是否真的被加载。这一步很多人跳过,结果出了问题不知道从哪查。我的做法是故意写一条容易触发的规则,然后让 AI 做一件违反它的事,看它会不会纠正。比如规则里写"禁止使用 var",然后让 AI 写一段用 var 的代码,如果它主动改成 let/const,说明规则生效了;如果它照写 var,说明规则没被读到。
4.2 glob 匹配的实测与调试
glob 写对与否,光看是看不出来的,得实测。我常用的调试方法是用一条"必然触发"的规则来验证匹配范围。比如我想确认src/components/**/*.tsx到底匹配哪些文件,就先写一条规则"在此文件顶部添加注释// RULE-ACTIVE",然后让 AI 编辑不同层级的组件文件,看哪些文件被加了注释。
实测下来,几个容易出错的 glob 场景:
| 你想要的 | 正确写法 | 错误写法 | 后果 |
|---|---|---|---|
| 所有 ts 文件 | **/*.ts | *.ts | 只匹配根目录 |
| components 下所有层级 | src/components/**/*.tsx | src/components/*.tsx | 漏掉子目录 |
| 排除测试文件 | 需配合 ignore 字段 | 在 globs 里写! | 多数工具不支持 |
关于排除,这里要单独提醒:很多工具的 globs 字段不支持!取反语法。我一开始想当然地写了!**/*.test.ts,结果整条规则直接失效。正确做法是用单独的 ignore 字段,或者干脆把规则写成"只匹配非测试文件的正向模式"。这个坑我踩得挺深,因为!在命令行 glob 里是支持的,很容易迁移过来。
4.3 规则生效的完整验证流程
把上面几步串起来,一个完整的验证流程是这样的:
- 确认文件路径正确(根目录
CODEBUDDY.md+.codebuddy/rules/) - 确认 frontmatter 语法正确(
---成对、YAML 缩进正确) - 确认 globs 能匹配到目标文件(用"必然触发"规则实测)
- 确认规则内容无冲突、无模糊表述
- 在对话中触发一次,观察 AI 是否遵守
这五步里,第 3 步是最容易被跳过、也最容易出问题的。我建议把它固化成习惯:每加一条带 globs 的规则,都先验证匹配范围,再写具体内容。顺序反了的话,你会花大量时间怀疑规则内容,其实是 glob 根本没匹配上。
5. 常见问题与排查技巧实录
5.1 规则完全不生效的排查清单
遇到规则一点反应都没有,按这个顺序查,基本能定位:
| 排查项 | 检查方法 | 典型问题 |
|---|---|---|
| 文件位置 | 确认在约定目录 | 放错到 docs/ 等目录 |
| 文件名 | 确认拼写和大小写 | .codeBuddy大小写错 |
| frontmatter | 确认---成对 | 只写了开头没写结尾 |
| YAML 语法 | 用 YAML 校验器过一遍 | 缩进用了 Tab |
| globs | 用必然触发规则实测 | 路径写错、漏** |
| 规则冲突 | 通读所有规则 | 多条规则互相矛盾 |
我印象最深的一次,是 frontmatter 的---只写了开头。文件长这样:
--- description: 某规则 globs: - "src/**/*.ts" - 规则内容...结尾的---漏了,导致整个 frontmatter 解析失败,规则文件被静默跳过。这种错误不会报错,只会表现为"规则不生效",特别隐蔽。后来我养成了习惯,写完 frontmatter 先数一下---是不是两个。
5.2 规则"时灵时不灵"的真相
比"完全不生效"更让人抓狂的是"时灵时不灵"。这种情况几乎都是规则冲突或 glob 部分匹配导致的。
规则冲突的典型场景:用户级规则和项目级规则打架。比如用户级写了"使用单引号",项目级写了"使用双引号",AI 在不同文件里表现不一致,看起来就像随机行为。解决办法是统一规则来源,把冲突的规则合并到最高优先级那一层,删掉低优先级的重复定义。
glob 部分匹配的场景:规则只对部分文件生效。比如globs写的是src/**/*.ts,但你的项目里有些文件是.tsx,那这些文件就不受规则约束。表现出来就是"改 .ts 文件时规则生效,改 .tsx 时失效"。排查方法是列出所有目标文件类型,逐一确认 glob 覆盖到了。
5.3 几个我踩过的独家坑
第一个坑:规则文件里写了 Markdown 标题,被误解析成 frontmatter 的一部分。我在规则内容里用了# 标题,结果某些解析器把它当成了 YAML 的注释或结构,导致规则内容错乱。后来我改成用列表和加粗,不再在规则文件里用#标题。
第二个坑:中文标点导致 YAML 解析异常。frontmatter 里的description如果用了中文全角冒号或引号,某些解析器会报错。我的做法是description尽量用英文,或者用引号把中文内容包起来。
第三个坑:规则写太多,AI"记不住"。我一度在一个 rule 文件里塞了 50 多条规则,结果 AI 只遵守了前几条。后来我拆成多个小文件,每个文件不超过 10 条,执行率明显提升。规则不是越多越好,聚焦才有效。
第四个坑:改了规则文件但没重新加载。有些工具会缓存规则,改完文件需要重启会话或触发重新加载才生效。我改完规则直接测试,发现没变化,以为写错了,其实是缓存。改完规则先重新加载,再验证。
5.4 跨工具迁移时的注意事项
从 Cursor、Trae 这类同样支持 rules 的工具迁移过来时,有几个地方不能直接照搬。虽然大家的概念相似(都有 rules、都有 frontmatter、都用 glob),但字段名和目录约定往往不一样。比如有的工具用.cursor/rules/,有的用.trae/rules/,CodeBuddy 用的是.codebuddy/rules/。直接复制目录结构肯定不行。
我的做法是迁移时只搬规则内容,frontmatter 重新写。把原工具的规则正文提取出来,在新工具里按新工具的 frontmatter 规范重新组织。这样虽然麻烦一点,但能避免"看起来迁移了实际没生效"的问题。另外,不同工具对 glob 语法的支持程度也有差异,迁移后一定要重新验证匹配范围。
6. 把规则体系维护成长期资产
规则体系搭起来只是开始,真正体现价值的是长期维护。我现在的做法是把规则当成代码一样管理:每次发现 AI 犯了某类错误,就补一条规则;每次规则失效,就排查是加载问题还是内容问题。时间长了,这套规则就成了项目的"隐性知识库",新人接手时看规则文件就能快速理解项目约定。
有一点体会特别深:规则的价值不在于写得多全,而在于每一条都真的生效。我见过太多项目,规则文件写得洋洋洒洒,实际生效的没几条,反而给人"这工具不好用"的错觉。与其追求大而全,不如先把三五条最关键的规则调通、验证生效,再逐步扩展。这个思路跟我一开始踩坑时的心态正好相反——那时候我总想一次把所有规范都写进去,结果一条都没生效,白白浪费了一个下午。
最后分享一个我常用的小技巧:给每条规则加一个"验证用例"。比如规则是"禁止使用 any",我就在心里记一个触发场景"让 AI 写一个带 any 的函数"。每次改完规则,用这个用例快速验证一遍。这样规则库越大,验证成本也不会失控。规则这东西,写是次要的,能稳定生效才是关键。