主流 Agent 入门教程 / CodeBuddy
10. 案例7-让它守规矩
原文备份 · 第 12 / 16 篇 · 查看极客时间原知识库 ↗
本篇目录 · 13 节
来源:CodeBuddy
场景
前面六个案例,你每次都在提示词里重复同样的要求:
- "提交信息用 Conventional Commits"
- "改完跑测试"
- "不要改我没让你改的文件"
- "金额计算要有测试,边界要测"
说第三遍的时候你就该意识到:这些不该在提示词里说,应该写成规则让它自己记住。
这一章你会带走:一套提交进仓库的项目规范、一个自定义的审查子代理、一个接进来的外部工具。
先分清四样东西
很多人把它们搞混。一句话一个:
| 东西 | 是什么 | 什么时候加载 |
|---|---|---|
记忆(CODEBUDDY.md) |
项目背景知识 | 每次都加载 |
规则(.codebuddy/rules/*.md) |
具体的行为约束 | 按条件触发(或常驻) |
| 子代理 | 一个有独立上下文的专职角色 | 你点名或它自动派活时 |
| MCP | 外部工具/数据源的接入 | 需要时调用 |
记忆回答"这是什么",规则回答"该怎么做",子代理回答"谁来干",MCP 回答"能用什么工具"。
第一步:把规矩写成规则文件
先让它帮你整理:
回顾我们前面几章做过的所有要求和项目约定,
把它们整理成一组规则文件,放在 .codebuddy/rules/ 下。
要求:
1. 一个主题一个文件,不要塞进一个大文件
2. 每条规则必须是可检查的(能判断做没做到),
不能是"要写好代码"这种没法验证的话
3. 用 frontmatter 标注触发条件:
- 有些规则要一直生效(比如提交信息格式)
- 有些只在改特定文件时生效(比如改到 pricing.py 才提醒金额测试)
4. 每条规则给出正反例子
先列一份清单给我看,确认后你再写文件。
它整理的清单大概是这样:
| 文件 | 触发条件 | 内容 |
|---|---|---|
commit.md |
常驻 | 提交信息用 Conventional Commits,标题 ≤50 字 |
testing.md |
tests/**、**/*pricing* |
金额与边界必须有测试,禁止假断言 |
python-style.md |
**/*.py |
类型注解、禁止裸 except、路径用 pathlib |
git-workflow.md |
常驻 | 重构与修 bug 分分支;危险命令先确认 |
security.md |
常驻 | 禁止把密钥写进代码;禁止读 .env |
单个规则文件长这样(frontmatter 的字段名以你当前版本为准,不确定就在会话里问它一句):
---
description: 金额计算相关的改动必须有边界测试
paths: ["**/pricing*.py", "**/models.py", "**/invoice*.py"]
alwaysApply: false
---
# 金额计算改动规范
修改任何涉及金额计算的代码时,必须同时补测试。
## 必须覆盖
- 档位/门槛的边界值(恰好等于、刚好超过、刚好不足)
- 负数、零、空输入
- 浮点精度(断言用 `pytest.approx`,不要直接 `==`)
## 禁止
- `assert result is not None` 这类不验证数值的断言
- 只测正常流程不测异常
## 正例
assert tiered_price(10000, "basic") == pytest.approx(200.0)
## 反例
assert tiered_price(10000, "basic") is not None
注意"正反例子"这部分。 抽象的规则它容易理解偏,给个反例它就不会犯。这是写规则最有效的一招。
再看一个常驻规则的写法:
---
description: 提交信息格式
alwaysApply: true
---
# 提交信息规范
格式:`<type>(<scope>): <subject>`
type 只能是:feat / fix / refactor / test / docs / chore / perf / build
subject 用中文或英文都可以,但同一仓库要统一,不超过 50 字,结尾不加句号。
正文(可选)说明"为什么改",不是"改了什么"——改了什么看 diff 就知道。
## 反例
改了点东西
fix bug
Update files
第二步:写一个自定义审查子代理
规则是"它自己记住",子代理是"派一个专职的人去干"。
我们来写一个 code-reviewer:
帮我定义一个子代理 code-reviewer,放在 .codebuddy/agents/code-reviewer.md。
它的职责:改完代码之后做一次独立审查。
要求:
1. 只读,不允许修改任何文件(要在定义里写死它的工具权限)
2. 审查清单:
- 逻辑正确性(边界、异常、并发)
- 安全性(注入、越权、敏感信息)
- 是否符合 .codebuddy/rules/ 里的所有规则
- 是否有遗漏的测试
- 与项目既有约定的一致性
3. 输出格式:按 [阻塞] [建议] [可选] 三类,每条标注文件名和行号
4. 每条问题必须给出具体的修改建议,不能只说"这里有问题"
5. 如果没发现问题,就明确说"没发现问题",不要硬凑
定义文件大概是这样:
---
name: code-reviewer
description: 代码改动完成后做独立审查。改完代码、提交前、或需要第二意见时使用。
tools:
- Read
- Grep
- Glob
---
你是这个项目的代码审查者。你只做审查,不修改任何文件。
## 审查清单
1. 逻辑正确性:边界条件、异常处理、并发问题
2. 安全性:注入、越权、敏感信息是否可能被打印或提交
3. 规范符合度:逐条对照 .codebuddy/rules/ 下的规则
4. 测试覆盖:这次改动有没有对应的测试
5. 一致性:命名、错误处理风格是否和既有代码一致
## 输出格式
[阻塞] 文件路径:行号 —— 问题描述 —— 建议怎么改
[建议] ...
[可选] ...
没问题的分类写"无"。不要为了显得有输出而凑问题。
tools 那一栏是关键——只给它只读工具,它就没法改代码。这比在提示词里说"不要改"可靠得多。
用起来:
用 code-reviewer 审查一下我刚才的改动
或者让它自动跑:
以后每次我让你改完代码并且跑通测试之后,
自动用 code-reviewer 子代理再审查一遍,把结果给我。
用 /agents 可以查看和管理所有子代理。
子代理的真正价值是上下文隔离。 它去翻一百个文件找线索,翻完只把结论交给你——你的主上下文不会被那些搜索过程污染。审查、考古、调研这类"要翻很多东西但结论很短"的活,都该派子代理。
第三步:接一个外部工具(MCP)
MCP 是让 CodeBuddy 能调用外部服务的标准协议——数据库、Jira、内部 API、文档系统,都能接进来。
我想让你可以直接查我们项目的数据库(只读)。
帮我:
1. 说明 MCP 配置应该放在哪个文件、什么格式
2. 给出一个只读数据库 MCP server 的配置示例
3. 告诉我怎么验证它真的接上了
4. 提醒我这类配置里哪些东西绝对不能提交进仓库
会话里敲 /mcp 可以看到当前接入了哪些服务和它们的状态。
第 4 点那个提醒很重要:MCP 配置里通常是连接串、token、账号密码。这类东西要么放 settings.local.json(自动被忽略),要么用环境变量引用,绝对不要写进共享的 settings.json。
第四步:配置分层,哪些共享哪些不共享
.codebuddy/
├── settings.json 团队共享:权限规则、允许/禁止的工具 → 提交
├── settings.local.json 个人覆盖:我自己喜欢的输出风格、本地路径 → 不提交
├── rules/ 项目规则 → 提交
├── agents/ 自定义子代理 → 提交
└── CODEBUDDY.md 项目记忆 → 提交
~/.codebuddy/
├── CODEBUDDY.md 我的个人偏好(所有项目生效) → 不提交
└── rules/ 我的个人规则 → 不提交
判断标准就一条:这条规矩换个人来还成立吗? 成立就提交,不成立就放 local。
比如"提交信息用 Conventional Commits"是团队约定 → 提交;"我习惯让它回答简短一点"是个人偏好 → 放 local。
检查一下有没有漏网的:
检查 .gitignore 是否正确忽略了 .codebuddy/settings.local.json
和任何含密钥的文件。然后用 git status 确认一遍:
现在待提交的文件里有没有包含密钥、token、本机绝对路径?
踩一次坑:写一堆正确的废话
对比一下两种规则写法。
没用的写法:
---
description: 代码规范
alwaysApply: true
---
# 代码规范
- 要写好代码
- 注意代码质量
- 遵循最佳实践
- 保持代码整洁
这四条它都会"遵守"——因为它本来就觉得自己一直在遵守。这类规则等于没写,还白白占上下文。
有用的写法:
---
description: Python 代码规范
paths: ["**/*.py"]
---
1. 所有公开函数必须有类型注解和 docstring
2. 禁止裸 `except:` 和 `except Exception: pass`
3. 文件路径一律用 `pathlib.Path`,不要字符串拼接
4. 单个函数不超过 50 行,超了就拆
5. 金额计算必须用 Decimal 或明确说明为什么用 float
每条都能一眼判断做没做到。判断不了的规则,就是无效规则。
还有一条经验:规则不要一次写太多。写 30 条规则,它每条都打折扣;写 8 条精准的,它每条都执行到位。先写最痛的那几条,跑一段时间再补。
Git:共享配置提交,个人配置忽略
git add .codebuddy/settings.json .codebuddy/rules .codebuddy/agents CODEBUDDY.md
git commit -m "chore: 建立项目级 AI 协作规范
- rules/:提交信息、测试、Python 风格、安全、git 工作流五条规则
- agents/code-reviewer:只读审查子代理
- settings.json:禁止读取 .env 与执行破坏性命令
- 个人偏好放 settings.local.json,已加入 gitignore"
平台差异:配置目录在哪
| 环境 | 用户级配置目录 |
|---|---|
| macOS / Linux | ~/.codebuddy/ |
| Windows PowerShell | %USERPROFILE%\.codebuddy\(通常是 C:\Users\你\.codebuddy) |
| WSL2 | ~/.codebuddy/(WSL 内部,和 Windows 侧不共享) |
WSL 和 Windows 各有一份用户级配置,不互通。项目级配置(.codebuddy/)因为跟着仓库走,两边都能读到——所以团队共享的东西一定要放在项目级,别放用户级。
另外,Windows 上做软链(ln -s)和 Unix 行为不一样。想把个人规则软链到项目里的话,Windows 上要用 mklink 或者直接复制,别指望 ln -s 能正常工作。
本章你多了什么
.codebuddy/rules/下五条可检查的规则- 一个只读的
code-reviewer子代理 - 一份权限与工具配置,敏感文件已被挡住
- 一套"什么该共享、什么该本地"的判断标准
本章新知识点
| 知识点 | 说明 |
|---|---|
| 规则比提示词可靠 | 写一次,之后每次都生效 |
| 规则要可检查 | 判断不了的规则等于没写 |
| 正反例子 | 给反例它就不会犯 |
paths 条件触发 |
不是所有规则都要常驻,省上下文 |
子代理的 tools 限制 |
只给只读工具,没有授予写文件和 Bash 工具 |
| 上下文隔离 | 子代理翻文件的"过程"不占主上下文 |
| 配置分层 | 团队共享 vs 个人偏好 |
延伸练习
- 再加一个
test-writer子代理,专门负责按规则写测试,并且让它只能用Write写tests/下的文件 - 写一条规则:改动数据库 schema 时必须同时检查 migrations 目录,看看会发生什么
- 把第 9 章那个 PR 审查工作流改成调用
code-reviewer子代理,对比一下输出质量
常见坑
规则写了很多,但感觉没生效。 检查三件事:frontmatter 语法对不对、文件路径是不是 .codebuddy/rules/、触发条件是不是没匹配上。可以让它自己确认:"列出你当前加载的所有规则"。
规则之间互相冲突。 比如一条说"函数要短",另一条说"不要把逻辑拆太散"。规则多了容易打架,定期让它检查一遍:"读一遍所有规则,找出互相冲突或重复的地方"。
子代理还是改了代码。 检查它的 tools 列表里有没有 Write / Edit / Bash(不带限制的 Bash 等于什么都能干)。
密钥被提交上去了。 每次提交前跑一遍那句检查:"待提交的文件里有没有密钥?"发现已经提交了,立刻改密钥——git 历史里的密钥删不掉,只能作废。
到这里,你已经能让它按你的规矩干活了。但规矩之外还有一层:在它动手之前和之后各插一道卡子,以及把整套做事方法打包成可复用的能力——那是 Plan 模式、子代理、Skills 和 Hooks 的事。
下一章就把这四个讲透,每个都配能直接抄走的配置。
规则验收:分别打开匹配 paths 的 pricing.py 与不匹配的文本文件,检查规则加载情况。子代理只给 Read、Grep、Glob;需要 diff 时由外部先生成后提供。规则和提示词不是系统访问控制,最终仍检查实际工具授权与文件差异。