System Prompt 是每次请求发给模型的特权指令通道(Runtime Payload)。
AGENTS.md和rules不是与它并列的l提示词,而是本地源配置:Agent 工具读取它们,连同内置人格和工具定义,缝合成这一通道。两套「地图」不要混用:AGENTS.md是每轮都在的总索引(约 100 行鸟瞰 + 指针 +「这里不存在什么」);rules是路径命中才缝进 payload 的条件指引,注入的是规则正文,不自动挂载外部文档。Skills 是任务型操作手册,按需读取。选型看:这条句子要不要进最终 System Prompt、在什么条件下进、进了之后谁来维护。
结论先行
- 三者不是并列关系。 System Prompt = 运行时产物;AGENTS.md / rules / skills / 工具内置提示 = 源配置。你在
~/.agents/AGENTS.md或.cursor/rules/里写的每一句,本质是在模块化地重构本地 Agent 的 System Prompt。 - 谁能改 System Prompt,取决于入口。 API 调用通过
system/system_instruction/developer完全重写。Agent / IDE 不要改工具源码,用 AGENTS.md、rules、以及少数工具的SYSTEM.md做拼装入口。 - AGENTS.md = 宪法 / 总地图。 约 100 行鸟瞰:慢变量、入口命令、红线、「这里不存在什么」,用指针链到专业文档。没打开任何文件时也要找得到门。1000 行单文件会挤占干活窗口。
- Rules = 部门规章 / 条件地图。 glob 命中时进入 payload 的是 rule 正文,不是
docs/*.md自动挂载。正文可写成短不变量,或写成「先读某文档」的指针;后者不保证文档已在窗口里。没附匹配文件时,条件地图整份缺席。alwaysApply: true不是微观控制。Skills 才是「按任务打开手册」的原语。排除选项比枚举选项更省上下文。 - 「正在读写匹配文件才注入」是简化说法。 命中判定发生在组请求、拼装 System Prompt 时,依据的是本轮已在场的文件集合,不是每一次
read/write瞬时插一段。 - 可移植的是 Markdown 句子,不可移植的是条件调度器。
.cursor/rules/*.mdc不能被 Pi / Codex / Gemini CLI 直接识别。跨工具用嵌套AGENTS.md或「一份正文 + 薄适配器」。 - 安全红线必须进常驻 System Prompt。 放在 glob rule 里等于没命中就没有红线。
- Rule 是提示,不是执行。 「必须走统一错误类型」真正强制的是 linter / 类型检查;rule 只告诉 Agent 怎么写才能过检查。
- 先问「这句要不要出现在本轮发给模型的 System Prompt 里」,再决定放哪一层源文件。
一、System Prompt:运行时产物
System Prompt(系统提示词)是大模型对话里最底层的元指令(Meta-Instruction)。它在用户消息之前进入模型的特权指令通道,预先设定角色身份、行为边界、输出格式、安全红线以及工具调用约定。
各家 API 字段名不同:OpenAI 现多用 developer(旧称 system),Anthropic 用 system,Gemini 用 system_instruction。部分 Agent 会把项目上下文做成 system 消息里的标记区块,而不是独立 role。对使用者只需记:用户提问之前、模型已经看见的那一整段系统侧指令 = 本轮 System Prompt(Runtime Payload)。
它本身通常不落盘。落盘的是用来生成它的源文件。
谁可以改
| 角色 | 能改到哪一层 | 入口 |
|---|---|---|
| API 调用 | 100% 重写 | 请求里的 system / system_instruction / developer |
| Agent / IDE(Pi、Cursor、Codex、Claude Code、Aider) | 间接、高度可控 | 不改工具源码。用 AGENTS.md、rules、以及 Pi 的 SYSTEM.md / APPEND_SYSTEM.md 作为拼装入口 |
Agent 工具的内置 System Prompt 是底膜(工具如何调用、安全策略);你写的 Markdown 是往这块底膜上缝内容,不是替换整张膜——除非工具明确提供「替换系统提示」的文件(Pi 的 SYSTEM.md)。替换底膜会丢掉工具用法,一般不要用。
运行管线:源配置 → 调度层 → Runtime Payload
它们不是三种提示词,而是配置文件与最终产物:
flowchart TB
subgraph src["源配置 磁盘"]
direction TB
s1["工具内置:底膜 + 工具定义"]
s2["SYSTEM.md / APPEND_SYSTEM.md"]
s3["全局 AGENTS.md"]
s4["项目 AGENTS.md 含嵌套与 override"]
s5["本轮命中的 rules"]
s6["Skill 的 name + description"]
end
src --> sched["调度层:读取、过滤、模板化、拼接"]
sched --> payload["最终 System Prompt<br/>本轮 API 特权指令通道"]
payload --> llm["发送给 LLM"]
llm --> after["其后:对话 / @ 文件 / 工具结果"]
| 层 | 典型文件 | 进入 System Prompt 的方式 | 默认是否常驻 |
|---|---|---|---|
| 产品底膜 | 工具内置 system prompt + 工具 schema | 每轮整份 | 是 |
| 底膜替换/追加 | SYSTEM.md、APPEND_SYSTEM.md | 每轮整份 | 若存在则是 |
| 用户/全局人格 | ~/.pi/agent/AGENTS.md、~/.claude/CLAUDE.md、Cursor User Rules | 每轮整份 | 是 |
| 仓库总章程 | 根目录 AGENTS.md / CLAUDE.md / .github/copilot-instructions.md | 每轮整份 | 是 |
| 模块化规则 | .cursor/rules/*.mdc、.claude/rules/*.md、.github/instructions/*.md | 命中才把正文缝进去 | 不一定 |
| 技能索引 | 各 SKILL.md 的 name / description | 每轮只有短描述 | 描述常驻,正文否 |
| 本轮对话 | 用户消息、@ 文件、会话记忆、工具结果 | 不在 System Prompt,在后续 messages | 是 |
调度层做的事:
- 载入工具底膜和工具 schema
- 按目录向上收集 AGENTS.md / CLAUDE.md
- 用本轮文件集合跑 glob,决定哪些 rules 正文进入 payload
- 把 Skill 目录(通常只有描述)挂上
- 拼成一条(或少数几条)系统侧消息
你维护的是左侧源文件。模型看见的是右侧产物。排障「AI 不听话」时,要问的是:这句有没有进入本轮 payload,而不是磁盘上有没有这个文件。
Pi 对「底膜」和「上下文」的拆分
Pi 把源文件分成两类,但发送时都进系统侧:
| 文件 | 作用 |
|---|---|
| 内置 system prompt | 底膜:身份、工具用法 |
~/.pi/agent/SYSTEM.md 或 .pi/SYSTEM.md | 替换底膜 |
APPEND_SYSTEM.md(全局或项目) | 追加到底膜,不替换 |
AGENTS.md / CLAUDE.md / AGENTS.override.md | 上下文文件,拼在系统侧的项目指令区 |
| Skills | 描述进系统侧;正文按需 read,进入的是后续消息,不是 System Prompt 正文 |
--no-context-files / -nc 只关掉 AGENTS.md / CLAUDE.md,不停底膜。
二、三大概念对照
| 维度 | System Prompt | AGENTS.md | Rules(如 .cursor/rules) |
|---|---|---|---|
| 本质 | 单次 API 请求的特权指令通道 | 跨工具的全局/项目宏观配置 | 特定 IDE / 工具内部的微观条件规则 |
| 存在形态 | 内存中的文本(API payload) | 磁盘上的 Markdown | 磁盘上的 .md / .mdc |
| 生效机制 | 每次对话前整体注入 system / developer 作用域 | 初始化时被读取并挂进 payload | 按 glob 或操作类型动态决定是否缝进 payload |
| 控制主体 | 工具底层机制 / API 开发者 | 开发者 / 团队 / 用户 | 开发者 / 团队 / 用户 |
| 标准 | 各家 API 字段不同 | 跨工具(agents.md) | 厂商私有格式 |
| 粒度 | 本轮全量系统指令 | 一层目录一份地图(可嵌套);细则用指针 | 多文件、一主题一份 |
| Token | 本轮系统侧全量计费 | 每轮都付费;故应约 100 行 | 可只在命中时付费 |
| 主语 | 模型本轮必须服从的全部系统句 | 仓库 / 人;不变量用「不存在」写 | 文件类型 |
| 人读性 | 运行时才能看见完整拼接结果 | 高,像地图 | 中,目录一多就要索引 |
再加一层 Skills,避免和 rules 混用:
| Skills | |
|---|---|
| 本质 | 任务型操作手册 |
| 进 payload 的部分 | 通常只有 description |
| 正文 | 匹配后 read,进入对话而非 System Prompt |
| 主语 | 任务类型 |
记忆:
- System Prompt = 本轮发给模型的成品
- AGENTS.md = 宪法 / 总地图(每轮在,约 100 行,指针 + 不存在)
- rules = 部门规章 / 条件地图(命中才在;注入的是正文,不是外部文档自动挂载)
- skills / docs = 操作手册(源,正文延迟加载)
三、AGENTS.md:给地图,不给手册
agents.md 是约定,不是某一家产品功能:仓库里给编码 Agent 看的地图。工具读取后,把它缝进 System Prompt 的项目指令区。
上下文窗口是稀缺资源。约 100 行的 AGENTS.md 充当鸟瞰,用指针链到更深层的专业文档;1000 行单文件说明书会挤占真正干活的空间,长指令还会中间遗忘。地图只展示很少变化的内容:入口命令、安全红线、架构不变量、指向手册的路径。会随功能迭代的细则放在 docs/、子树 AGENTS.md、rules 或 Skill 里,Agent 需要时再读。根地图只负责总索引;按路径才出现的强调句见 五、两套地图:总索引 vs 条件指引。
写什么(地图层)
- 怎么构建、测试、lint、提交(唯一入口命令)
- 鸟瞰:主要目录各干什么,细则指针指向哪里
- 很少变化的风格与错误处理约定
- 安全红线(禁止生产迁移、禁止提交密钥)
- 这里不存在什么(架构不变量,见下)
用「不存在」写不变量
告诉 Agent 这个项目里不用什么,往往比枚举「用什么」更有效。排除选项比展开选项更省上下文,也更难过期:技术选型清单会变长,禁区相对稳定。
## 这里不存在
- 不用 ORM,SQL 写在 `src/db/queries/`
- 不用 GraphQL,对外走现有 HTTP 接口
- 不引入新的状态管理库,UI 状态跟邻接文件
- 不在应用代码里读环境变量以外的密钥源
「用 Postgres + 手写 SQL + 本目录 query 文件」若还要列出所有允许的库、所有表访问模式,那是手册。地图只封死 ORM / GraphQL 这条岔路。
不写什么(手册层,用指针)
- 某个子包的实现细节 →
docs/data.md或src/db/AGENTS.md - 某文件类型的 import 顺序 → rule / linter
- 会随重构过期的目录长文 → 架构文档,地图里留一行链接
- 大段示例代码 → 专业文档或测试;过期示例会变成错误示范
- 历史、愿景、架构叙事 →
docs/,需要时再读
加载特征
- 会话开始读入,拼进系统/项目上下文(即 System Prompt 的一部分)
- 可从 cwd 向上走父目录,嵌套仓库叠多层
- 一份文件服务多种 Agent(Codex、Pi、Claude Code 兼容层、部分 Copilot/Cursor 等)
Pi 的具体行为
- 全局:
~/.pi/agent/AGENTS.md - 从 cwd 向上找
AGENTS.md或CLAUDE.md,全部拼接 - 同目录有
AGENTS.override.md时,只替换该目录的AGENTS.md/CLAUDE.md,其他层仍保留 --no-context-files/-nc可关掉- 改完
/reload,不必重启
Pi 没有 glob rules。条件细则的对应物是 Skill,或子树嵌套 AGENTS.md,或自行写扩展扫描 .claude/rules/。
推荐骨架
# 项目名
## 地图
- 架构:`docs/architecture.md`
- 数据:`docs/data.md`(改 `src/db` 时先读)
- 前端:`docs/frontend.md`(仅前端目录;校验库等细则见该文档,不要写进根地图)
## 命令
- 开发:`pnpm dev`
- 检查:`pnpm check`(改完必须跑)
- 测试:`pnpm test -- <相关文件>`
## 这里不存在
- 不用 ORM,SQL 写在 `src/db/queries/`
- 不用 GraphQL,对外走现有 HTTP 接口
- 不引入新的状态管理库
## 硬约束
- 不要提交 .env / 密钥
- 不要在本地跑生产迁移
- 公共 API 变更必须同步更新调用方
## 代码
- 跟随邻接文件的既有模式,不引入新抽象除非消除真实重复
- 生成代码后跑 `pnpm check`
篇幅目标:约 100 行。80–150 行是舒适区,超过约 200 行就该把细则改成指针。它每轮都进 System Prompt:1000 行单文件会挤占干活空间,长指令还会中间遗忘。
四、Rules:往 System Prompt 上做条件缝合
rules 没有单一国际标准,是各家 harness 的「可拆分、可条件加载」指令。调度器决定本轮要不要把这段正文写进 System Prompt。
共同设计目标:
- 别把所有细则塞进一份永远在场的大文件
- 按 glob / 路径 / 描述自动挂上
- 一条规则一个主题,方便评审和复用
三态调度(微观控制的状态机)
flowchart LR
A["Always<br/>alwaysApply: true"] --> A2["正文每轮进 payload<br/>不是微观控制"]
B["Auto Attached<br/>globs"] --> B2["命中才进正文"]
C["Agent Requested / Manual<br/>仅 description"] --> C2["模型选用或 @rule"]
| 类型 | Frontmatter | 索引是否常驻进 System Prompt | 正文何时进入 System Prompt |
|---|---|---|---|
| Always | alwaysApply: true | 正文常驻 | 每轮。不是微观控制 |
| Auto Attached | globs: ... | 通常只有文件名/描述 | 本轮上下文文件命中 glob |
| Agent Requested / Manual | 仅 description | 只有描述 | 模型选用,或用户 @rule |
真正省窗口的是后两种。Always 只改善可维护性,对 System Prompt 体积的效果与写在 AGENTS.md 里相同。
命中集合从哪来
组请求、拼装 System Prompt 时收集文件集合再跑 glob:
flowchart LR
tabs["打开的 tab"] --> set["本轮文件集合"]
at["用户 @ 的文件"] --> set
rw["本轮读过或改过的文件"] --> set
recent["最近 tab 视产品而定"] --> set
set --> glob{"跑 glob"}
glob -->|命中| inn["rule 正文缝进 System Prompt"]
glob -->|未命中| out["本轮 payload 无此 rule"]
反直觉后果:
- 只问「查询层该怎么设计」、没附任何
src/db文件 → Auto Attached 可能不进本轮 System Prompt;Agent Requested 才可能被模型拉进来。 - 同时改
src/db/user.ts和src/ui/Button.tsx→ db rule 与 UI rule 可能同时缝进同一条 System Prompt,跨层任务上微观控制失效。 - Agent 先
read了匹配文件、下一轮才组新请求 → 有的产品把已读文件算进命中集,有的要等显式 attach。不要假设「读到就立刻改写 System Prompt」。 - glob 写
src/db/**/*.ts,实际是.sql或.ts.bak→ 不命中,本轮 payload 里没有这段。
写 rule 时把触发源想成:人类或 Agent 已经把哪些路径放进了这轮上下文,而不是「磁盘上存在匹配文件」。
各家形态
Cursor — .cursor/rules/*.mdc
---
description: 修改 src/db 下查询与迁移时使用
globs: src/db/**/*.ts
alwaysApply: false
---
SQL 只放本目录 queries/。禁止改已合入的旧迁移文件。
四种挂载:Always / Auto Attached(globs)/ Agent Requested(description)/ Manual(@rule)。
Claude Code — CLAUDE.md(≈ AGENTS.md)+ .claude/rules/*.md
---
paths:
- src/db/**/*.ts
---
用户级:~/.claude/CLAUDE.md。
GitHub Copilot
.github/copilot-instructions.md≈ AGENTS.md.github/instructions/*.instructions.md+applyTo≈ rules
Windsurf — .windsurf/rules/*.md,字段名随版本变,精神同 glob。
Cline / Roo — .clinerules 或 .clinerules/,条件能力弱于 Cursor .mdc。
Pi — 原生不读上述目录。官方示例扩展 claude-rules.ts:启动扫描 .claude/rules/,把清单追加进 System Prompt,正文仍要模型 read。这是 Agent Requested,不是 Auto Attached。
锁定不只是路径不同,还有:glob 语法、多条命中是拼接还是覆盖、description 是否进入可点选目录、用户规则/项目规则/团队规则的合并顺序。即使人工搬运正文,触发器也会丢——触发器属于调度层,不属于可移植的 System Prompt 句子。
五、两套地图:总索引 vs 条件指引
AGENTS.md 的指针和 rules 的「先读某文档」看起来都像地图,调度器不同,缺席时的后果也不同。混用会导致:总索引里没有门、干活时手册没加载、或者同一段细则进 payload 两次。
区别
| AGENTS.md 总地图 | Rules 条件地图 | |
|---|---|---|
| 出现时机 | 每轮都在 System Prompt | 本轮文件集合命中 glob(或 Agent 选用)才在 |
| 没打开匹配文件时 | 指针仍在,问「API 怎么设计」也找得到门 | 整份缺席,包括指针 |
| 调度器注入的内容 | 地图正文本身 | rule 正文本身,不是外部 docs/ 自动挂载 |
| 专业文档何时进窗口 | Agent 按指针 read(不保证) | 同左;若把手册贴进 rule,则命中时整份在 |
| 主语 | 仓库:门在哪、禁区是什么 | 文件类型:碰这类路径时强调什么 |
| 跨工具 | 是 | 否(厂商 Frontmatter) |
| 失败形态 | 地图过长,挤占干活窗口 | 假触发 / 漏触发;或把 1000 行手册贴进 rule |
根地图回答:任何任务如何找到专业文档。
条件地图回答:已经在改这类文件时,还要强调哪几条、要不要再去读哪份文档。
Rules 的两种写法(不要当成同一种)
正文即短手册。 glob 命中 → 这些句子一定在 payload 里。适合 5–15 条路径级不变量(统一日志、错误类型、禁止越层访问)。这是条件加载约束,不是加载 docs/data.md。
正文当指针。 命中后 payload 里只有「先读 docs/data.md」。文档进不进窗口,取决于模型是否 read。多数编码 Agent 会跟,调度器不强制。排障时不要把「rule 在磁盘上」当成「专业文档已在上下文里」。
把 1000 行 docs/data.md 贴进 glob rule:拥挤从「每轮」挪到「每次碰 src/db」。条件地图同样给地图、不给全书。
没有通用 Frontmatter 如 attach: docs/data.md。要保证长文在窗口里,只能:短约束写进 rule 正文、或让 Agent/Skill 去 read。
和 Skill 的边界
| 意图 | 放哪 |
|---|---|
| 任何任务都要找得到入口 | 根 AGENTS.md 一行指针 |
| 碰某类文件时强调短不变量 / 再读哪份文档 | glob rule(短正文) |
| 长流程、多文件必读清单(先 A 再 B 再跑 C) | Skill |
Skill 的主语是任务类型;rule 的主语是文件类型。修 Bug、发版、导翻译用 Skill,不要用 glob 假装任务触发。
叠法(总索引留门,条件层强调,手册不常驻)
flowchart TB
root["根 AGENTS.md<br/>每轮在:总索引<br/>数据细则见 docs/data.md"]
rule[".cursor/rules/db.mdc<br/>仅 src/db/**/*.ts<br/>短不变量 + 先读 docs/data.md"]
doc["docs/data.md<br/>专业文档,按需 read"]
root -.->|"指针始终可见"| doc
rule -.->|"命中后提醒再读"| doc
根上不写某目录的实现细则(那是手册)。rule 上不承担「没打开对应文件时也能找到门」(那是总索引)。docs/data.md 不进常驻 System Prompt。
跨 Cursor / CLI 时:条件层用 src/db/AGENTS.md 代替 .mdc,总索引仍然在仓库根。不要在根地图复制 rule 正文。
六、Token 与服从度
System Prompt 按本轮全量计费。常驻源文件有硬成本:200 行 AGENTS.md × 每轮请求,长会话里重复吃几万 token。模型对长指令会中间遗忘,越长越容易只记住头尾。
经验阈值:
| 载体 | 建议 |
|---|---|
| 全局 / 仓库 AGENTS.md | 约 100 行地图;80–150 舒适,超 200 改成指针。1000 行单文件会挤占干活窗口 |
| 单条 rule | 一屏能看完,约 30–80 行 |
| Skill description | 短触发条件;正文按需加载,不进 System Prompt |
账本示例(尚未计底膜和工具 schema):
flowchart LR
subgraph all["全缝进 payload"]
a["AGENTS 400 + API 200 + UI 200 + DB 300 + 测试 150 = 1250 / 轮"]
end
subgraph globbed["只改 src/db 且 db rule 为 glob"]
b["AGENTS 400 + API 200 = 600 / 轮"]
end
三种情况把账打穿:
- Always 规则过多 — 拆文件零收益,System Prompt 体积不变
- glob 过宽 —
**/*.{ts,tsx}等于 always - Agent Requested 描述目录过长 — 50 条 × 80 token 描述 = 4000 token 索引,可能比直接注入几条正文更贵
最优形态:少量 always(宪法)+ 少量窄 glob(部门法)+ 少量高价值 Agent Requested(判例) 。规则文件数量不是专业度。System Prompt 应瘦,源文件可以多——前提是调度器真的会过滤。
七、优先级与冲突
拼进同一条 System Prompt 之后,模型看到的是平铺文本。没有宇宙统一优先级,多数工具接近:
flowchart TB
u["1. 用户当轮明确指令<br/>在后续 messages"] --> sec["2. 工具内置安全策略<br/>底膜,一般不要替换"]
sec --> near["3. 更近目录的 AGENTS.md"]
near --> user["4. 用户级全局 AGENTS / User Rules"]
user --> root["5. 仓库根 AGENTS.md"]
root --> rules["6. 模块 rules / Skill 描述"]
用户当轮指令通常压过系统侧的风格偏好,但压不过工具硬编码的安全策略。更近的目录上下文(如 packages/app/AGENTS.md)压过仓库根。
仓库根建议写死:
冲突时:用户本轮指令 > 本目录 AGENTS.md > 仓库根 AGENTS.md > 全局 AGENTS.md
path-specific rules 只补充,不推翻更高层的安全红线。
安全类句子(密钥、生产破坏、许可证)必须进入常驻 System Prompt,不要只放在可能不被缝合的 rule 里。
同一约束不要在 AGENTS.md 和 rule 里复制。两处都会进 payload 时,改一处漏一处,模型还会收到矛盾句。
用 SYSTEM.md 替换底膜时,确认工具用法和安全句还在。缺工具说明的 System Prompt 会让 Agent 不会用工具。
八、微观控制:能做 / 不能做
能做(且值得做成 rule,条件缝进 System Prompt)
- 该路径上的 API 形状、校验库、错误码
- 测试文件的放置与命名
- 某目录禁止的依赖或模式(
src/db不许直接碰外部 HTTP) - 生成代码的局部格式(迁移文件只追加、不改已合入版本)
不能做
| 意图 | 应放 |
|---|---|
| 任何任务都要找得到某手册 | 根 AGENTS.md 一行指针(总索引) |
| 碰某类文件时的短不变量 | glob rule 正文(条件地图,5–15 条) |
| 碰某类文件时再读哪份文档 | glob rule 里一行指针;文档本身不贴进 rule |
| 安全红线 | AGENTS.md / always(保证进每一轮 System Prompt) |
| 替换工具底膜 | 仅当明确需要时用 SYSTEM.md;默认用 AGENTS.md 追加 |
| 跨目录重构 | 更粗的 rule,或 Skill |
| 长流程、多文件必读清单 | Skill(正文不要常驻 System Prompt) |
| 与打开文件无关的问答 | 根地图短入口;不要只写在 glob rule 里 |
| 「必须走统一错误类型」的强制执行 | linter / 类型检查;rule 只给写法 |
| 把专业文档全文条件灌入 | 无此原语;短约束进 rule,长文按需 read |
rule 的主语应是文件类型,不是任务类型。
- 「写查询时 SQL 只放
src/db/queries」→ rule(条件进入 System Prompt) - 「修 Bug 先下日志」→ Skill(不要撑大 System Prompt)
- 「永远用中文回复」→ AGENTS.md(每轮 System Prompt 都需要)
九、怎么写才叫微观
glob 要窄到误挂成本低
# 差:几乎任何前端改动都挂上,等于 always 进 System Prompt
globs: "**/*.{ts,tsx}"
# 差:包含了测试,测试文件会吃到运行时约束
globs: "src/db/**"
# 好:只覆盖实现文件
globs:
- "src/db/**/*.ts"
- "!src/db/**/*.test.ts"
不是所有产品支持否定 glob。不支持就拆成 api.mdc 和 api-test.mdc。
正文只写可执行约束
好:
- SQL 只放 `src/db/queries/`,不要在 handler 里拼字符串
- 错误走仓库统一的 Result/错误码,不要 throw 字符串
- 日志走项目 logger,不要 `console.log`
差:愿景、考古、40 行易过期示例。Rule 是编译器警告的自然语言版:短、可违反时可被发现、不讲故事。它一旦命中,就是 System Prompt 的一部分,废话会占窗口。
description 给调度器用
# 差
description: API 相关规范
# 好
description: 编写或修改 src/db 下查询与迁移时使用。SQL 只放 queries/,禁止改已合入迁移。
Agent Requested 全靠这一句决定要不要把正文拉进 System Prompt。空泛 = 乱触发或永不触发。
一条 rule 一个变化轴
api-validation.mdc 与 api-response.mdc 只有总是同时适用才合并。合并后无法单独关闭,微观控制变粗,System Prompt 也无法按轴裁剪。
十、按工具落地
| 你想表达 | Cursor | Claude Code | Copilot | Pi | Codex |
|---|---|---|---|---|---|
| 改工具底膜 | 少用 | 少用 | 少用 | SYSTEM.md 替换 / APPEND_SYSTEM.md 追加 | 产品设置 |
| 跨工具项目章程 | AGENTS.md | AGENTS.md 或 CLAUDE.md | 可另写 copilot-instructions.md,或让工具读 AGENTS | AGENTS.md | AGENTS.md |
| 全局个人习惯 | User Rules | ~/.claude/CLAUDE.md | VS Code user instructions | ~/.pi/agent/AGENTS.md | 用户级 AGENTS |
| 某目录细则 | .cursor/rules/*.mdc + globs | .claude/rules/ | .github/instructions/ + applyTo | Skill,或嵌套 AGENTS.md | 嵌套 AGENTS / skills |
| 专项流程 | Skills | Skills | 自定义 agent | Skills | Skills |
Monorepo: 根 AGENTS.md 写公共命令和红线;apps/web/AGENTS.md、packages/db/AGENTS.md 写局部。这是不依赖厂商 rules 的可移植拆分,各工具仍把它们缝进 System Prompt。
覆盖同目录旧文件: Pi 用 AGENTS.override.md,避免再写一份补充说明造成双重指令同时进入 payload。
对抗厂商锁定
不要维护三份语义不同的规则。维护一份正文,多份薄适配器。正文是未来 System Prompt 的句子;适配器只提供调度器能读的 Frontmatter。
flowchart TB
subgraph portable["可移植层 进 git"]
d1["docs/agent/rules/api.md<br/>纯约束,无 frontmatter"]
d2["根 AGENTS.md<br/>地图:慢变量 + 指针 + 这里不存在"]
d3["src/db/AGENTS.md<br/>零锁定的子树约束"]
end
subgraph adapters["调度层薄包装"]
c1[".cursor/rules/api.mdc"]
c2[".claude/rules/api.md"]
c3[".github/instructions/api.instructions.md"]
end
d1 --> c1
d1 --> c2
d1 --> c3
嵌套 src/db/AGENTS.md 不是 glob 那么精准(进了子树就可能带上),但 Codex、Pi、Claude、部分 Cursor 都会沿目录树上卷,零锁定。微观控制要求极高再上 .mdc;要跨工具先用嵌套 AGENTS.md。
Pi 等价物:
| 意图 | Pi |
|---|---|
| 替换/追加底膜 | SYSTEM.md / APPEND_SYSTEM.md |
| 宪法(进 System Prompt 项目区) | AGENTS.md |
| 某子树默认约束 | src/db/AGENTS.md |
| 任务型流程 | Skill(description 进 System Prompt,正文按需读) |
| 仿 Cursor glob | 自己写扩展,按当前文件集合注入 System Prompt |
十一、决策流程
flowchart TD
start{"这句要进本轮 System Prompt 吗?"}
start -->|"是,且每个任务都要"| always{"改的是什么"}
always -->|"工具底膜 / 工具用法"| leave["不要动<br/>必要时 APPEND_SYSTEM.md"]
always -->|"项目约定 / 总索引 / 这里不存在"| agents["仓库 AGENTS.md"]
always -->|"个人习惯"| global["全局 AGENTS.md / User Rules"]
start -->|"否,或只要有时出现"| sometimes{"触发条件"}
sometimes -->|"匹配某些路径"| path{"内容多长"}
path -->|"5-15 条不变量或先读"| glob["glob rule 条件地图"]
path -->|"整份专业文档"| pointer["不要贴进 rule<br/>指针 + 按需 read"]
sometimes -->|"某类任务长流程"| skill["Skill"]
sometimes -->|"某子树且跨 CLI"| nested["该子树 AGENTS.md"]
检查清单:
- 删掉这句,Agent 会在任意任务上找不到门或犯错吗?会 → 根 AGENTS.md 总索引
- 没附
src/db文件时还需要这句吗?需要 → 不能只写在 glob rule 里 - 这句会随功能迭代经常改、或超过几行才能说清?→
docs/+ 总索引一行指针;rule 里最多再留一行「先读」 - 能用「不用 X」代替「用 A/B/C/D」吗?→ 写排除,不写枚举
- 只在碰某类文件才错、且能写成短句?→ rule 正文(条件地图)
- 只在「发版 / 翻译导入 / 查 Jira」这类任务才需要?→ Skill
- 这句是在教模型「你是谁、工具怎么用」?→ 底膜;不要写进 AGENTS.md 重复一份,更不要用
SYSTEM.md覆盖后丢掉工具说明 - 根地图和 rule 是否复制了同一段细则?复制则删成「根指针 + rule 短约束」
- 写完后:有没有和更高层对打、或过期路径?有就删
十二、失败模式与反模式
管线层
- 以为磁盘有文件就等于模型看见了。 没命中 glob、被
-nc关掉,本轮 System Prompt 里没有这句。 - 用
SYSTEM.md整段替换底膜。 工具调用说明消失,Agent 变成不会用工具的聊天模型。 - 把 Skill 正文粘进 AGENTS.md。 长流程常驻 System Prompt,窗口被操作手册撑满。
- 把 AGENTS.md 写成 1000 行单文件手册。 地图变成全书,干活空间被挤占;细则应指针化。
Rules 特有
- 假触发。 glob
**/*route*把src/ui/Router.tsx算进去,错误句子进了 System Prompt。 - 漏触发。 文件在
src/data/,规则写src/db/。看起来像「AI 不听话」,其实 payload 里没有规则。排障第一步:看产品「本轮已附加 rules」UI,或导出本轮 system 消息。 - 跨层任务失效。 「给这个 API 补前端表单」会同时缝两侧,或只缝打开的那一侧。
- 规则互殴。
src/db/v2/**同时命中 v1 与 v2,两条矛盾句出现在同一条 System Prompt。各家很少做最长前缀获胜。自己保证 glob 互斥。 - 用 rule 当 linter。 模型会忘。强制约束放机器检查。
- 在 Auto Attached 里写安全禁令。 没打开匹配文件时,本轮 System Prompt 没有禁令。
- 把条件地图当成总索引。 只在 glob rule 里写
docs/data.md指针:没附匹配文件时门也不见。 - 把总索引当成条件加载。 根 AGENTS.md 写满某子域实现细则:改 CSS 也每轮付费。
- 以为 rule 指针 = 文档已加载。 注入的是「先读」四个字,不是
docs/data.md正文。 - 把专业文档全文贴进 glob rule。 碰该目录时窗口被手册挤占,与 1000 行 AGENTS.md 同类。
通用
- 把 AGENTS.md 写成第二份 README 或全书(全部常驻进 payload)
- 用枚举技术栈代替「这里不存在」:允许列表会膨胀,禁区更稳、更短
- 全部
alwaysApply: true(System Prompt 体积与不拆相同) - rules 与 AGENTS 复制同一段(地图里应只留指针)
- 用 400 行 always-on rule 模拟 Skill
- 为「完整」塞示例 diff
- 为了「专业」堆规则文件(描述索引本身会撑大 System Prompt)
十三、最小落地模板
三层不要互相抄正文。
仓库根 AGENTS.md(总地图,约 100 行,每轮进 System Prompt):入口命令、红线、「这里不存在」、指向 docs/ 的指针。不要抄全局人格,不要把某子域实现细则贴进来。
## 地图
- 数据:`docs/data.md`(改 `src/db` 时先读)
.cursor/rules/db.mdc(条件地图,命中才进 System Prompt):短不变量 + 再读哪份文档。不要贴 docs/data.md 全文。
---
description: 修改 src/db 下查询与迁移时使用
globs: src/db/**/*.ts
alwaysApply: false
---
先读 `docs/data.md`。
- SQL 只放 queries/
- 禁止改已合入的旧迁移文件
- 错误走仓库统一 Result,不要 throw 字符串
docs/data.md:专业文档,按需 read。
跨 CLI 时,条件层用 src/db/AGENTS.md 代替 .mdc,总索引仍在仓库根。
前端 TypeScript HTTP 层若使用 Zod 一类校验库,只写在该目录的 rule / docs/frontend.md 里,作为路径级特例,不要写进根地图或当成所有仓库的统一模板。
需要追加底膜而不替换时(Pi):~/.pi/agent/APPEND_SYSTEM.md 或 .pi/APPEND_SYSTEM.md。
十四、一句话选用
System Prompt 是成品;AGENTS.md / rules / skills 是源。
两套地图:AGENTS.md 是每轮都在的总索引(约 100 行慢变量 + 指针 +「这里不存在」);rules 是路径命中才出现的条件指引(注入 rule 正文,不自动挂载 docs/)。宪法进总索引,路径级短约束进 rule,长手册进 docs/ / Skill 按需读。
Rules 的微观控制 = 用 glob/description 当调度器,决定哪些局部句子写进本轮 System Prompt。精度取决于「本轮文件集合 ∩ glob」。Always 没有微观;过宽 glob 没有微观;没附匹配文件时条件地图整份缺席。任务型流程用 Skill,不要用 glob 假装。排除比枚举更省窗口。调度器可以锁在厂商目录里,即将进入成品的句子必须另有一份无 frontmatter 的来源。