AI Agent 工程目录架构:把 Rules、Skills、MCP 各归其位

文章目录

用 AI 编码工具的人大多经历过这样的循环:模型不听话,往配置文件里加一条规则;还是不听话,再加一条。半年下来 AGENTS.md 攒了两千行,效果却没见涨多少。问题多半不在规则的数量,而在这些内容的组织方式。这篇文章拆解一套正在被越来越多团队采用的工程目录结构,它把散落各处的配置收敛成八个层次,每层只管一件事。

一个大提示词撑不住了

先把现状说清楚。大多数人现在的用法可以叫"一个大提示词":项目约定、编码规范、部署步骤、常用命令,全部塞进 CLAUDE.md 或者 AGENTS.md,再配一段每次手动粘贴的系统提示词。工具越强,塞的东西越多。

这个用法在项目只有几百行代码时够用,规模一上去就会撞上三堵墙。

第一堵墙是上下文成本。那些规则是常驻内容,每一次对话都要整体加载。你为一次改 typo 的任务付了全套部署规范的钱。上下文窗口是预算不是仓库,这句话值得写在每个团队的工程规范首页。

第二堵墙是行为漂移。模型对超长文档中间部分的注意力天然偏弱,规则越多,单条规则的执行率越低。两千行里第 1400 行的一条约定,被遵守基本靠运气。你加规则的动机是"让它记住",实际效果常常是稀释了其他规则的权重。

第三堵墙是职责纠缠。"怎么连数据库""部署分几步""绝对不许碰哪些目录"是三种性质完全不同的内容:第一种是外部能力接线,第二种是方法论,第三种是红线。混在一个文件里,改部署流程要小心别碰坏权限声明,想删一条过期规范得通读全文。维护成本随着文件长度非线性上涨。

三个症状指向同一个诊断:缺的不是规则,是架构。下面这套结构就是给配置做的模块化。顺带回应一个常见质疑:个人项目用不着这么讲究,这话对了一半。层次可以精简,职责分离的原则省不掉;团队超过三人、仓库活过半年之后,这套结构的回报开始加速,因为配置从备忘录变成了基础设施。

全景:八层各管一件事

先看目录长什么样。以 Codex 的布局为例,Claude Code 和 Cursor 有各自的对应目录,思路完全一致:

text 复制代码
your-project/
├── AGENTS.md                    # 项目名片:技术栈、命令、长期约定
├── .codex/
│   ├── settings/
│   │   ├── settings.json        # 团队共享配置:权限、模型、审批策略
│   │   └── settings.local.json  # 个人覆盖,gitignore,不进版本库
│   ├── rules/
│   │   ├── typescript.md        # 语言规范
│   │   ├── tests.md             # 测试规约
│   │   └── http-api.md          # 接口设计约束
│   ├── commands/
│   │   ├── pr-review.md         # /pr-review 命令入口
│   │   └── resolve-bug.md       # /resolve-bug 命令入口
│   ├── skills/
│   │   └── deploy/
│   │       ├── SKILL.md         # 部署流程主文档
│   │       └── checklist.md     # 发布清单,按需加载
│   ├── agents/
│   │   ├── pr-reviewer.md       # 代码审查角色
│   │   └── security-review.md   # 安全审查角色
│   └── hooks/
│       └── post-edit-lint.sh    # 编辑后自动 lint
└── .mcp.json                    # 外部工具接线声明

一张表总结分工:

回答的问题 加载时机
AGENTS.md 这个项目是什么 常驻
settings 工具怎么运行、权限边界在哪 常驻(机制层)
rules 什么必须做、什么禁止做 常驻
commands 流程从哪里进入 用户触发
skills 具体怎么做 任务匹配后按需
agents 谁用独立视角看一遍 编排时启动
hooks 哪些校验必须机械执行 事件触发,不占上下文
.mcp.json 手能伸到哪些外部系统 建立连接时读取

各家工具的概念对应关系大致如下,具体路径随版本演进,以官方文档为准:

概念 Codex Claude Code Cursor
主说明文件 AGENTS.md CLAUDE.md AGENTS.md
规则目录 rules/ CLAUDE.md 分节或引入文件 .cursor/rules
技能目录 .agents/skills/ .claude/skills/ .cursor/skills/
命令入口 prompts .claude/commands/ commands
外部工具 config.toml / .mcp.json .mcp.json MCP 配置项

眼尖的人会发现,标题里的三件套只有 Rules、Skills、MCP,怎么长成了八层?三件套是骨架,落地时很快暴露出四个缺口:流程缺固定入口,于是有了 commands;审查缺独立视角,于是有了 agents;校验缺强制力,于是有了 hooks;项目缺身份、个人缺差异化,于是有了 AGENTS.md 和 settings。八层不是设计出来的完备性,是用出来的完备性。

接下来逐层展开。

AGENTS.md:项目名片

AGENTS.md 在 2025 年成为跨工具的事实标准,Codex、Cursor、Gemini CLI 等几十款工具都认这个文件名。Claude Code 用的是 CLAUDE.md,思路相同,不少团队拿 AGENTS.md 当唯一源,再软链一份 CLAUDE.md 过去,避免两处维护。操作就是一行命令,ln -s AGENTS.md CLAUDE.md,从此只改一处。

它的内容应该是任何 agent 打开项目的头一分钟就需要知道的事:

markdown 复制代码
# 项目简介
Java 21 + Spring Boot 3 后端,pnpm 管理,前后端同仓。

# 常用命令
- 构建:pnpm build
- 测试:pnpm test
- 本地起服务:pnpm dev

# 长期约束
- 不要修改 packages/legacy/ 下的代码
- 提交信息用中文,格式为 <类型>: <描述>

同样重要的是不写什么。领域细则交给 rules,操作手册交给 skills,会频繁变动的信息干脆别写。名片过期比没有名片更糟,模型拿着三个月前的启动命令反复报错,比它自己探索还费时间。

先看反面教材。有的团队把名片写成了仓库:三百行里一半是某个功能的接口说明,四分之一是上个月临时活动的部署注意事项,剩下散落着"代码要整洁"这类无法执行的愿望。模型读到这些内容的处境,接近新人入职第一天拿到一本公司大事记,抓不住重点,还不敢跳读。

对照着看差距在哪。合格的名片只回答三个问题,这是什么项目、怎么跑起来、哪些地方绝对不能动,每一条都可执行、可验证。判断某段内容配不配进名片的办法很直接:想象新同事第一天上班,这句话对他的下一步行动有指导意义吗?没有就挪去别的层。

有一个反直觉的原则:AGENTS.md 越短越好。它是常驻上下文,每个字符都在每次会话里消耗注意力。写一条之前先问自己,违反了会造成实际损失吗?不会的话删掉。经验上百行以内足够覆盖绝大多数项目。

settings/:分层配置

这一层管工具行为,和模型知识无关。三个文件对应三种作用域:用户级的 ~/.claude/settings.json 对机器上所有项目生效,放个人默认偏好;项目里的 settings.json 进版本库,放团队统一约定;settings.local.json 默认被 gitignore,放个人覆盖。

优先级从高到低依次是企业托管策略、命令行参数、本地设置、项目设置、用户设置。越局部的文件,匹配到的键优先级越高。

最常见的用途是权限管理:

json 复制代码
{
  "permissions": {
    "allow": ["Bash(pnpm test:*)"],
    "deny": ["Read(./secrets/**)"]
  }
}
java 复制代码
"permission": {
    "bash": {
      "rm *": "ask",
      "rmdir *": "ask",
      "rm -rf / *": "ask",
      "chmod *": "ask",
      "chown *": "ask",
      "mv *": "ask",
      "git push --force *": "ask",
      "git push -f *": "ask",
      "git push --force-to-lease *": "ask",
      "git reset --hard *": "ask",
      "git clean *": "ask",
      "git rebase *": "ask",
      "git filter-branch *": "ask",
      "git filter-repo *": "ask",
      "git branch -D *": "ask",
      "git checkout -- *": "ask",
      "git restore *": "ask",
      "git revert *": "ask",
      "gh repo delete *": "ask",
      "gh pr merge *": "ask",
      "gh pr close *": "ask",
      "gh issue close *": "ask",
      "gh release delete *": "ask",
      "gh api *": "ask",
      "npm publish *": "ask",
      "bun publish *": "ask",
      "kill *": "ask",
      "pkill *": "ask",
      "killall *": "ask",
      "pip uninstall *": "ask"
    }
  }

团队在项目 settings.json 里统一放行测试命令,个人在 local 文件里放行自己顺手的工具,互不干扰。settings.local.json 存在的意义就是让"个性化"不必污染"标准化",这个思路和 .env.local 一脉相承,写过后端的人会觉得非常眼熟。

除了 permissions,这一层常见的键还有几类。env 注入环境变量,给工具进程设置代理地址或内部 registry;model 锁定默认模型型号,让团队行为保持一致;statusLine、outputStyle 这类界面偏好也归它管。一份带注释的项目级配置大致如下:

json 复制代码
{
  "model": "sonnet",
  "env": { "HTTP_PROXY": "http://internal-proxy:7890" },
  "permissions": {
    "allow": ["Bash(pnpm *)", "Read(./docs/**)"],
    "deny": ["Read(./secrets/**)", "Bash(rm -rf *)"]
  }
}

判断一条配置该不该放这里的标准很简单:它是控制工具运行的,不是教模型做事的。教模型做事的内容不在这层停留,往下去 rules。

三层的版本管理策略跟着作用域走:用户级配置留在个人机器,不进任何仓库;项目共享文件随代码提交,评审通过才生效;本地覆盖写进全局 gitignore,防止手滑带出去。作用域、优先级、版本策略三者对齐,配置就不会乱。

rules/:做什么,不做什么

rules 是硬约束,性质接近 lint 规则和人肉 code review 标准的结合体。示例结构里按领域拆成 typescript.mdtests.mdhttp-api.md 三个文件,这个拆法有讲究。

按领域拆而不堆一个大文件,理由有三个。可维护,改接口规范的 diff 只出现在一个文件里;可挂载,monorepo 里前端包挂前端规则,服务包挂服务规则;可审计,新同事熟悉这套约束时按领域读,不用啃一千行的合集。

看一段典型的 tests.md

markdown 复制代码
# 测试规约

- 新增功能必须带测试,先写失败测试再实现
- 不允许 mock 被测方法本身
- 断言必须验证行为,不允许只断言不抛异常
- 集成测试连真实数据库,单元测试不许访问网络

再看 http-api.md 里会写的条款:所有变更接口同步更新 OpenAPI 文档,错误码从统一枚举取值,分页参数固定叫 page 与 size。单看每条都琐碎,合在一起就是团队接口风格的完整约定,agent 生成的每个端点都会自动对齐。

monorepo 场景还有个进阶玩法。规则不必全挂在仓库根,前端包挂组件命名与样式约束,服务包挂事务与幂等要求,agent 进入哪个包就加载哪套规则。约束跟着代码走,比集中堆放精准得多。

注意 rules 和 skills 的边界:永远生效的约束放 rules,特定任务才用到的方法放 skills。"测试必须验证行为"是任何写代码的任务都要守的,所以是 rule;"怎么做性能压测"只在压测任务里有用,所以是 skill。

rules 也是常驻内容,膨胀速度通常比 AGENTS.md 更快。建议季度审一次,标准很朴素:这条规则半年内拦住过错误吗?没有就删。规则库和代码一样会长草。

commands/:薄入口

commands 目录下每个 markdown 文件是一个斜杠命令。用户敲 /resolve-bug,对应的提示词模板接管会话。

这一层的核心纪律是薄。command 只做编排:声明进入什么流程、第一步读哪个 skill、由哪个 agent 收尾。知识本体一行都不放。一个健康的 command 文件不超过几十行:

markdown 复制代码
---
description: 定位并修复 bug
---

按以下顺序处理:
1. 读问题描述,先在本机复现
2. 按 debugging skill 的假设法定位根因
3. 最小化修复,禁止顺手重构
4. 补回归测试,然后跑全量测试
5. 完成后调用 security-review 做安全检查

为什么需要这一层?没有它,触发多步流程靠口述。今天说"帮我查一下这个 bug",明天说"这个问题你看看",说法不同,模型的执行路径就不同。command 把入口固化下来,同一个词永远进入同一条流水线。对团队来说还有一层价值:老工程师的排查套路沉淀成 command,新人直接继承,培训成本大幅下降。命令还能接收参数,模板里写 $ARGUMENTS 占位符,用户敲 /resolve-bug 登录接口 502 时,后半截自动填进占位符的位置。命令之间也允许组合,/ship 可以在自己的步骤里指明先走一遍 /pr-review 流程,短命令拼成长流水线。

skills/:渐进式披露的方法论

skills 是整套架构里设计最精巧的一层。每个技能一个目录,核心是 SKILL.md,外围可以带参考文档和脚本:

text 复制代码
skills/deploy/
├── SKILL.md          # 主文档:元数据 + 流程
├── checklist.md      # 发布清单,执行到检查环节才读
└── scripts/
    └── pre-deploy.sh # 确定性检查脚本

SKILL.md 的头部是 YAML 元数据,正文是流程:

markdown 复制代码
---
name: deploy
description: 发布上线流程。当用户要求部署、发版、上线时使用。
---

# 步骤

1. 运行 scripts/pre-deploy.sh 确认工作区干净
2. 按环境检查配置,测试环境跳过灰度
3. 执行发布命令,观察日志五分钟
4. 读取 checklist.md 逐项验收

精巧之处在加载机制,官方称为渐进式披露,分三层。启动时 agent 只读所有技能的元数据,name 加 description 大约一百个 token,装五十个技能也就五千 token 常驻。用户请求命中某个技能时,才加载正文全文,一般建议正文控制在五千 token 以内。正文引用的资源文件,比如 checklist.md 和脚本,用到了才读。

对比一下:把五十篇部署文档全部塞进常驻上下文,轻松吃掉几十万 token;按需加载后,常驻成本降了两个数量级。这就是 skills 和 rules 采用不同策略的原因:rules 是法律,必须时刻在场;skills 是操作手册,干活时翻开就行。

拿这个 deploy 技能走一遍完整生命周期。早上会话启动,agent 只知道存在一个叫 deploy 的技能和它的一句话描述,成本约一百 token;上午用户聊别的需求,deploy 全程隐身。下午用户说发版到测试环境,description 匹配命中,正文两千 token 进入上下文,模型按步骤执行。跑到验收环节,checklist.md 才被读进来;pre-deploy.sh 直接执行,内容从头到尾不进上下文。同一个技能,四种出场深度,每种只付当下需要的钱。

description 字段值得单独强调。它是技能被选中的唯一依据,agent 靠它判断当前任务该不该启用这个技能。写"处理各种部署相关事务"这类含糊表述,技能可能永远不会被命中。正确写法要说清做什么、什么时候用,必要时把典型触发语句直接列进去。

把触发链路再拆开看一层。启动时那约一百 token 的元数据进入系统提示词后,模型理解用户请求时会做一次匹配,判断这个任务像不像某个 description 描述的场景,命中才展开正文。这意味着两件事。其一,description 是整个技能体系的路由表,路由错了,技能写得再好也等于不存在。其二,技能数量本身有成本,装两百个互相重叠的技能会让匹配变得犹豫,宁精勿多。

references 目录的组织有一条实践共识:扁平优于嵌套。REFERENCE.mdFORMS.mdapi-docs.md 平铺一层,SKILL.md 正文里点名引用,agent 按名字取用,层级深了引用路径本身就是负担。技能之间的协作遵循同样思路:deploy 技能的验收步骤可以引用 review 技能的产物,但不要在元数据层面互相依赖,每个技能保持单独可用。

agents/:独立视角的子代理

agents 目录定义子代理,每个文件是一个角色:独立的系统提示词、独立的上下文窗口,还可以限定工具白名单。

为什么要拆出 pr-reviewer 和 security-review 两个角色,而不是写一个万能审查员?三个原因。

视角不同。代码审查关心正确性、可读性和架构一致性,安全审查盯注入、越权、密钥泄漏,两者的检查清单几乎没有交集。塞进一个角色,清单互相稀释,哪边都查不深。

上下文隔离。主对话已经装满实现细节,让同一个上下文审查自己的产出,等于让作者当自己的编辑。子代理带着干净的上下文进场,只看 diff 和相关文件,反而更容易发现问题。

可以并行。两类审查同时开跑,各自出报告,主会话合并裁决。顺带提醒一句,agent 之间不要互相调用,链式的代理每过一手就丢一层细节,成本还不透明。编排权留给 command 或者用户本人。

角色文件本身长什么样?通常包含四部分,名称、描述、工具白名单、系统提示词:

markdown 复制代码
---
name: security-review
description: 安全审查。改动涉及鉴权、输入处理、密钥管理时使用。
tools: Read, Grep
---

你是安全审查工程师。只报告可利用的真实风险,
按严重程度排序,每条附复现路径与修复建议。
不评论代码风格。

tools 白名单是经常被忽略的字段。审查员只发只读工具,它想改代码也没有手,权限边界从物理层面成立。系统提示词里的措辞决定审查的锋利程度,"只报告可利用的真实风险"这句就是在防误报。安全审查最常见的失败模式不是漏报,是狼来了,十条里八条吓唬人,第九条真漏洞也没人信了。

hooks/:确定性兜底

hooks 目录放事件触发的脚本。post-edit-lint.sh 的含义写在名字里:每次编辑动作之后自动执行 lint。

这一层体现一条贯穿整套架构的原则:能用脚本保证的事,不要指望模型自觉。

模型的输出是概率性的,规则写得再狠也有失手率。而 lint 通过与否、schema 校验过不过、敏感文件有没有被碰,这些都是确定性的判定。确定性的活交给确定性的工具,失败了阻断后续动作,把报错喂回给模型修正。hook 不经过模型,不占上下文,执行成本几乎为零,可靠性却接近百分之百。

主流工具的 hook 都挂在生命周期事件上,常见的有工具调用前、工具调用后、提交前、会话结束等。把校验从提示词搬进 hook 的过程,本质是把"拜托模型遵守"升级成"违反就过不去"。翻翻你的规则库,凡是出现"必须""严禁"字样的条目,先想想能不能变成一条 hook。能变的都变过去,剩下真正需要判断力的部分才留给模型。

hook 在配置层的注册大致长这样:

json 复制代码
{
  "hooks": {
    "PostToolUse": [{
      "matcher": "Edit|Write",
      "hooks": [{ "type": "command", "command": "./scripts/lint-changed.sh" }]
    }]
  }
}

含义是每当编辑或写入工具执行完,就对改动文件跑一遍 lint,非零退出码会把 stderr 回灌给模型。整个过程里模型只是收到一条"你刚才的修改不符合规范",它感知不到背后有个脚本在把关,但行为已经被修正了。

脚本本身往往出奇地朴素:

bash 复制代码
#!/bin/bash
# 只检查本次改动的 ts 文件
changed=$(git diff --name-only --diff-filter=ACM | grep '\.ts$')
[ -z "$changed" ] && exit 0
npx eslint $changed

十几行,没有依赖,坏了当场报错。确定性工具的另一重含义是它自己的行为也完全可预测。

.mcp.json:外部接线

MCP(Model Context Protocol)是 Anthropic 在 2024 年底推出的协议,解决连接标准化的问题:server 声明自己提供哪些工具,客户端发现并调用。在此之前,接 GitHub 要写一套集成,接数据库再写一套;现在都是装现成的 MCP server,配置一段 JSON。

.mcp.json 放项目根目录,声明本项目需要哪些外部能力:

json 复制代码
{
  "mcpServers": {
    "browser": { "command": "npx", "args": ["@browser/mcp"] },
    "postgres": { "command": "npx", "args": ["@pg/mcp", "--readonly"] }
  }
}

浏览器用来验证前端改动的真实渲染效果,数据库用来查真实数据而不是猜表结构。注意上面示例里的 --readonly,能力的授予要跟着最小权限走。这部分属于安全边界,靠配置保证,不靠提示词恳求。

协议层面还有两点值得一提。工具发现是动态的,客户端接入后通过 tools/list 拿到该 server 暴露的全部工具和参数说明,新增一个 server 不用改任何提示词。实现与传输解耦,server 可以是本地进程也可以是远程服务,官方 SDK 覆盖主流语言,给内部平台封装一套 MCP 接口通常百来行代码。传输形态有两种:本地进程走标准输入输出,适合连数据库、跑内部脚本;远程服务走 HTTP,适合团队统一维护的内部平台。选型原则照旧,能本地的先本地,少一层网络就少一层故障面。远程 server 接入内部平台时补一句鉴权提醒:token 走环境变量注入,不要明文落盘,工具说明里也不该出现任何密钥。

三条设计主线

八层拆完,回头看它们共同遵循的三条主线,这是理解这套架构的关键。

第一条,连接性与能力分离。MCP 解决"够得着",skills 解决"怎么用",一个是手,一个是操作手册。同一个数据库 MCP server 可以被数据分析技能使用,也可以被故障排查技能使用;反过来,数据分析技能换了数据库,只需要换 MCP 配置,方法论原样保留。基础设施与业务逻辑解耦之后,两边才能各自演进。举个具体场景,公司把数据库从 PostgreSQL 迁到另一款存储,数据分析技能一行不用动,改的是 .mcp.json 里的连接配置;反过来,分析技能重写成新的三步法,连接原封不动。迁移成本被压到配置层,这正是分层要买的东西。

第二条,上下文预算。把所有配置按加载时机重新归拢:常驻的是 AGENTS.md 和 rules;按需的是 skills 三层;完全不占上下文的是 hooks,它们直接执行。算一笔账就能看出差距,三千行规则全塞常驻大约十几万 token,而渐进式披露下五十个技能常驻只要五千。省下来的预算就是模型留给关键信息的注意力。上下文越干净,关键规则被执行的概率越高,这不是玄学,是注意力机制的物理事实。什么内容配得上常驻资格,可以用三个问题过滤:每次会话都用得到吗、违反了有真实损失吗、一句话说得清吗。三个全是才进常驻,有一个否就降级到按需层,或者干脆砍掉。

第三条,概率与确定性分离。模型擅长判断,不擅长纪律。凡是能写成 if else 的校验都下沉到 hook 或脚本;需要理解语义、权衡取舍的判断留给模型。skill 里的 scripts 目录就是为此准备的,"检查覆盖率是否达标"应该是一段脚本输出 true 或 false,而不是让模型目测代码猜个数。模型守纪律的失败案例并不罕见:提示词里写了不许提交密钥,赶工时它照样把测试用的凭证贴进代码。这不是模型故意作恶,而是概率系统在长会话、多干扰下的正常波动。纪律这件事,交给不会波动的环节才踏实。

选型决策表

实际动手时最常见的困惑是"这个东西该放哪"。一张表收尾:

你手里有一段...... 放哪里
长期有效的全局约定 AGENTS.md
某个领域的硬性规范 rules/
可复用的操作流程 skills/
流程的快捷入口 commands/
外部系统的连接方式 .mcp.json
必须机械执行的校验 hooks/
需要独立视角的任务 agents/
个人偏好与权限放行 settings.local.json

两组容易混淆的判定补充在下面。rule 还是 skill,看加载时机,永远在场的是 rule,任务到了才用的是 skill。command 还是 skill,看厚薄,command 是入口薄壳只管编排,skill 是知识本体负责干活。一个完整的流程往往是 command 进门、skill 干活、agent 复核、hook 把关。拿不准时可以画一条加载时机轴:这段内容若出现在每次对话的第一屏,它归常驻层;只在特定动作后才登场,归按需层或事件层。位置画出来,归属自然清楚。

一次完整的穿行

把八层串起来走一遍。假设测试报告了登录接口的一个 bug,开发者敲下 /resolve-bug 附上复现步骤。

command 模板接管会话,指示按 debugging 技能的假设法推进。模型列出三个候选根因,通过 MCP 连接的数据库逐一验证,排除两个,锁定会话过期逻辑里的时区换算错误。修改代码的瞬间,hooks 触发 lint,一处命名违规当场被拦,模型立即修正。修复完成后补回归测试,全量测试通过。接着 /pr-review 发起审查,pr-reviewer 检查逻辑完整性和边界条件,security-review 单独扫一遍鉴权与注入风险,两份报告回到主会话合并成一份审查结论。全程 AGENTS.md 保证它没有碰 legacy 目录,tests 规约保证每处修复都有对应断言。

值得留意的是,这条流水线没有任何环节依赖"模型记住第 400 行的规则"。约束要么开场就在场,要么在被需要的瞬间精准加载,要么根本不经过模型,由脚本直接把关。稳定的行为来自结构,不是来自模型的自觉。

同一个结构对新人的价值更直接。入职第一天的工程师 clone 下仓库,第一次打开 agent 就带着全部团队经验:AGENTS.md 告诉他怎么跑起来,rules 替他挡住风格雷区,deploy 技能把发布流程手把手带一遍。过去要靠三个月口口相传的隐性知识,现在压缩在一棵目录树里。

从零落地:一个四周节奏

如果从零开始搭,不建议照着成品一口气建八个目录,更现实的节奏是每周解决一类真实痛点。

第一周写好两张纸。AGENTS.md 控制在百行以内,构建、测试、启动命令加红线写清楚;顺手建 settings.local.json,把每天手动确认的命令加进 allow 列表。仅这一步就能消掉大半重复解释。

第二周立规矩。挑团队争论最多的领域写 rule,通常是测试和接口风格,同时配一两条 hook 把格式类校验自动化。五条被严格遵守的规则,胜过五十条被无视的。

第三周沉淀第一个完整流程。找到重复次数最多的多步操作,多半是发版或修 bug,做成 command 加 skill 的组合。团队从这个流程上第一次直观看到收益,后续推广的阻力会小很多。

第四周引入 agents 和更多 MCP 连接。给审查加上独立的安全视角,接上浏览器让前端改动可以被真实渲染验证。到这一步,八层结构自然成形,每层都有明确的存在理由。

配置也是代码

还有一个观念转变值得单独说:这套目录里的每个文件都是代码资产,应当享受代码的同等待遇。进版本库、走评审、写变更说明。rules 改了哪条、为什么改,commit message 里讲清楚;skill 升级前跑一遍真实任务验证;谁都可以提规则,但没人可以悄悄改规则。

反过来,配置也要有人认领。每个 rule 文件、每个 skill 目录指定维护者,过期内容有人负责下架。没有归属的配置库,最终结局都是无人敢动的遗迹。

落地的坑

落地路上有几个高频的坑,全部来自真实团队的教训。

坑一,目录漂移。团队里 Claude 一份配置、Cursor 一份配置,三个月后两边规则打架,谁也不敢删。解法是唯一源加软链:维护一个 .agents 目录,各家配置目录软链过去,改动只发生在一处。软链还有个隐患是断链,换机器 clone 或打包分发时容易掉。加一条 CI 检查成本极低,脚本里对各个软链路径跑一次 test -e,断了当场红灯。

坑二,规则膨胀。看到什么都想写成规则,半年攒出两千行,又回到老路。常驻内容要像花钱一样花,季度审计一次,删掉从未拦住过错误的条款。审计可以更机械一些:把每条规则近半年拦下的案例数记出来,零案例的进候选删除名单;拿不准的挪去观察区文件,一个月后仍无人引用就删。规则库瘦身和依赖清理是同一门手艺。

坑三,技能变垃圾场。所有资料都往 SKILL.md 正文里塞,五千 token 的上限形同虚设,渐进式披露退化成一次性全量加载。长清单挪去 references,可脚本化的挪去 scripts,正文只留主干流程。

坑四,把提示词当安全边界。SKILL.md 是写给模型看的文字,不是权限系统。"禁止删除生产数据"写一百遍,不如 MCP 连接层不给写权限、settings deny 加一条来得可靠。文本引导行为,配置限制能力,两层都要有,但不要互相替代。

坑五,追求一步到位。没必要一次搭满八层,也不存在标准顺序。从最痛的一环开始:天天手动粘贴 review 标准,就先建 command 和 skill;反复向模型解释项目结构,就把 AGENTS.md 写扎实;同一类低级错误反复出现,就加一条 hook。这套架构是长出来的,不是一次浇筑的。

五个常见疑问

CLAUDE.mdAGENTS.md 到底用哪个?跟主工具走。全队主力是 Codex 就以 AGENTS.md 为准,主力是 Claude Code 就维护 CLAUDE.md,另一份用软链生成。两个文件内容各自演化的团队,几乎都栽在手工双维护上。

这套结构会不会拖慢 agent?恰好相反。常驻内容变小,单次请求的 token 开销随之下降;按需加载的部分只在命中时付出成本。真正变慢的用法只有一种,把所有东西都塞成常驻。

个人习惯和团队规范冲突了听谁的?分层早就给了答案。团队规范住共享配置和公共目录,个人偏好住本地覆盖与用户级配置,覆盖可以,出仓不行。冲突不需要说服对方,换个作用域就行。

老项目怎么迁移?不要一次性翻译存量文档。把现有文件里仍然有效的条款分类倒进新层次,过期直接删,拿不准的观察一个月,没人引用就删。迁移期允许新旧并存,但在旧文件顶部标注已冻结,不再新增。

子代理会不会很贵?会,多一个视角就多一份 token 账单,这是买独立上下文的合理对价。控成本有两个抓手:给 agent 划定阅读范围,只喂 diff 和相关文件,禁止自由探索全库;并行执行缩短墙钟时间,总开销不变但人等得少了。

怎么判断它在起作用

架构有没有生效不用靠体感,几个信号可以直接观测。

看常驻上下文体积。AGENTS.md 加 rules 的总量应稳定在几千 token 以内,持续膨胀说明过滤机制失效,该做季度审计了。

看同一错误的出现频次。格式类问题被 hook 拦截后,不应再出现在代码评审意见里;反复出现说明 hook 没挂对事件,或者脚本静默失败。

看流程的触发一致性。同一个 command 进来的两次会话,执行路径应当大体相同;明显发散说明模板指令不够具体,或者技能描述写偏了。

三个指标都不需要专门建设,翻会话记录就能统计。架构好坏不看感觉,看趋势。

结语

回到开头的三个症状。上下文浪费,解法是分层加载的预算设计;行为漂移,解法是职责分离加上确定性兜底;配置纠缠,解法是八个目录各司其职。你会发现这套东西没有任何黑科技,全是软件工程的老手艺,模块化、单一职责、配置与环境分离,只是这一次被搬到了 agent 配置上。

可能还有人存着一个隐忧:模型迭代这么快,这套目录会不会很快过时?恰恰相反,工具会换,目录里的内容不会。规则还是那些规范,技能还是那些流程,接线还是那些系统。形式在演化,分工的逻辑只会越来越清晰。

模型的能力还会继续涨,但组织信息的能力始终是工程师自己的活。工具越强,越考验你能不能把自己的知识整理成它消化的形状。目录树就是那个形状。