“AI 帮我建了个烂网站”的故事,绝大多数其实是”我写了份烂规格”的故事。问题很少出在模型身上。截至 2026 年 6 月,Claude Opus 4.7 在 SWE-bench Verified 上拿到 87.6%,GPT-5.5 在 Terminal-Bench 2.0 上拿到 82.7%——今天的 Agent 完全能把一整个站点装进脑子里。它做不到的,是替你猜那些你从没写下来的决定。这篇就给你一份持久的规格文件(你的工具本来就会读的 CLAUDE.md / AGENTS.md),外加一套单任务 prompt 结构,让 Agent 在几十次编辑里都不脱轨。
一句话结论
把技术栈、约定、以及那些”绝不能做”的硬规则写进仓库根目录的规格文件——Claude Code 读 CLAUDE.md,Codex、Cursor、Copilot、Gemini CLI 等二十多个工具读 AGENTS.md。控制在 150 行以内。然后用三段式 prompt 驱动每个任务:context(指向规格)、task(一个交付物)、constraints(明确的负向约束)。小 diff、多文件改动先计划后执行、现实一变就立刻更新规格。光是这一个习惯,就能消掉大部分 Agent 漂移。
“漂移”到底是什么
漂移就是 Agent 在一个任务到下一个任务之间,悄悄改了你的设计系统、路由约定或内容 schema。这不是模型的 bug——而是每个新任务都缺了”前面发生过什么”的锚。解法是让规格做到显式(写下来,而不是默认)、持久(跨 session 保留)、窄(一个 prompt 只干一件事)。
出现下面这些情况,就是在漂移:
- Agent 生成的代码和前一个任务自相矛盾。
- 每次新 session 都要把技术栈重新讲一遍。
- 目录结构慢慢偏离你最初的设计。
- 它发明了你没要求的 UI 组件或路由。
- 同一个 prompt 在不同 session 产出明显不同的结果。
你的工具读哪个规格文件?
下面两种格式基本覆盖了 2026 年中所有的 Agent 编码工具。它们都是纯 Markdown——不用构建步骤,也没有 schema 要满足。
| 文件 | 原生读取的工具 | 需要 frontmatter? | 备注(截至 2026 年 6 月) |
|---|---|---|---|
CLAUDE.md | Claude Code(Opus 4.7 / Sonnet 4.6) | 否 | 层级:~/.claude/CLAUDE.md(你个人)→ 仓库根 → 子目录。支持 @path 导入和 # 快速记录。 |
AGENTS.md | Codex、Cursor、GitHub Copilot、Gemini CLI、Aider、Zed、Windsurf、Jules 等 20+ 工具 | 否 | 开放标准,约 6 万+ 公开仓库在用。离被编辑文件最近的那份优先;聊天里的显式指令压倒一切。 |
.cursor/rules/*.mdc | Cursor(可选,更细粒度) | 是(description、globs、alwaysApply) | 想要按路径生效的规则时用(比如只在 src/api/ 里加载的 API 规则)。 |
实操原则:只选一个主规格文件,保证单一权威来源。换 Agent 时让另一种格式指向它——Claude Code 可以在 CLAUDE.md 里 @AGENTS.md,于是你只维护一份文件,两个工具都能读。
开始前准备
- 定好工具会读的规格文件名(
CLAUDE.md、AGENTS.md,或用 import 同时支持两者)。 - 技术栈和约定是已经定下来的,不是边做边发现的——先写下来。
- 给规格初稿留 30-60 分钟。一周内就回本。
实操步骤
- 仓库根建一个单一权威规格文件。 写紧凑:超过约 200 行的文件会实测降低指令服从度,因为每一行每一轮都会被加载。模板:
# CLAUDE.md(或 AGENTS.md —— 内容相同,都是纯 Markdown)
## 技术栈
- Framework: Astro 5(Content Collections + MDX)
- Styling: Tailwind v4(不要 CSS modules,不要 styled-components)
- Hosting: Firebase Hosting(静态;`firebase.json` 是权威)
- Languages: en、zh —— src/content/articles/ 下并行目录
## 约定
- Slug: `^[a-z0-9-]+$`,kebab-case,无日期,无分类前缀
- 目录: src/content/articles/[lang]/[hub]/[slug].mdx
- 内容 schema: 写在 src/content/config.ts;不显式要求不要改
- 图片引用: 用 `image()` helper,不要 `/public/...`
## 文章形态(mdx 文件)
- schema 要求的 frontmatter: title, description, urlSlug, category, tags, publishedAt, lang, translationKey
- 正文顺序: Lead, Background, How to tell, Step by step, FAQ, Related
## 构建与测试
- build 前先跑 `npm run audit:content`
- 用 `NODE_OPTIONS=--max-old-space-size=8192 npm run build` 构建
## 绝不要做(硬约束)
- 未询问就加 npm 包
- 编辑用户指定路径之外的文件
- 重命名任何 frontmatter 字段
- 在 src/components/mdx/ 已有组件时还发明新组件
照抄,填进你的真实值。“绝不要做”那块才是真正止住漂移的部分,别跳过。
- 用三段式 prompt 驱动每个任务: context(指向规格)、task(一个交付物)、constraints(明确的负向约束)。模板:
[CONTEXT]
仓库约定在 CLAUDE.md。文章 schema 在 src/content/config.ts。用 Astro Content Collections。
[TASK]
在 src/content/articles/zh/indie-dev/[slug].mdx 加一篇文章,遵循 CLAUDE.md 里的文章形态。
标题: "[标题]"。主关键词: "[关键词]"。600-900 字。
至少含一段 bash 代码块和一段配置代码块。
[CONSTRAINTS]
- 不要改 src/content/config.ts。
- 不要加 npm 依赖。
- 不要动其它文章。
- slug 必须与文件名一致并匹配 /^[a-z0-9-]+$/。
- 散文里不要出现裸 brace(MDX brace bug)。需要字面 brace 就放进反引号。
只回文件内容,我来 commit。
-
每个任务都切到能在两分钟内看完一个 diff 的大小。 200 行 diff 是软上限。再大,你就会开始批准其实没读过的代码——这正是下一个任务继承错误的途径。
-
写明负向约束。 constraints 这一段是你最大的杠杆所在。备一份可复用清单:
约束库
─────────────────────────────
不要加 npm/pip 依赖
不要改 [path] 之外的文件
不要改 [named-config-file]
不要给 [page-type] 加客户端 JS
不要重命名别处用到的 export/symbol
不要静默升级包版本
不要写注释复述显然的代码
- 输出接近但不对时,发 diff,别重发 prompt。 比起”按这些要求重做”(会把整个文件重新 roll 一遍),Agent 对精确编辑指令的服从可靠得多。格式:
应用这个 diff 并产出更新后的文件:
@@ src/content/articles/zh/indie-dev/foo.mdx @@
- 有问题的句子
+ 改对的句子
-
每次接受改动后,检查规格是否隐式变了。 今天 2 行的编辑,省下一个月后 2 小时的漂移 debug。在 Claude Code 里你可以 session 中途以
#开头写一行——它会问你存哪,然后写进对应的CLAUDE.md。 -
多文件重构先要计划,批准后再执行。 模板:
开始改任何文件前,先列出:
1. 你打算改的文件
2. 每个改动的一句话描述
3. 风险 / 顺序依赖
我说 "go" 之后才出 diff。
- 把成功的 prompt 存成模板。 建一个
prompts/目录,能用的就归档。你的 prompt 库复利会比代码库还快——一条存好的 prompt 就是一个可复现的结果。
为什么显式胜过”记得”
对话记忆不是规格。在 Claude Code 里,根目录的 CLAUDE.md 会在 /compact 之后被重新注入,但子目录文件不会,要等 Agent 下次读到那个目录里的文件才重新加载;而你只在聊天里说过的东西,一旦上下文窗口塞满就可能被忘掉。Cursor 和 Codex 同理:AGENTS.md 每一轮都会被拼进 prompt,散落的聊天指令不会。所以规则很简单——一个决定要是得跨 session 活下去,它就该进文件,而不是进对话。
随时在 Claude Code 里跑 /memory,能看到当前加载了哪些指令文件、按什么顺序。某条规则总被忽略时,先确认它是不是真的在一个被读取的文件里。
怎么验证生效了
- 用同一份规格 + 任务 prompt 开一个全新 session,跑出近乎一样的 diff。方差小,就是漂移被消掉的信号。
- 任务一旦会违反规格规则,Agent 会拒绝或反问,而不是硬着头皮往下做。
- 规格文件的 diff 与你接受的特性改动一一对应。
容易踩的坑
- 把 prompt 写成对话而不是规格。“能不能让 nav 好看点”得到的是”感觉不错”的不可预测输出。
- 规格不更新。
CLAUDE.md还写着 Tailwind,但你已经换了 CSS modules——下次它又把 Tailwind 拉回来。 - 一次问太多。“改路由、加 OG 标签、再更新 footer”,最后只对一个、其余全错。
- 觉得”它显然不会这么干”就不写约束。 它就是会这么干。
- 相信 session 内记忆。 长 session 会丢早期上下文,重要的部分要重述。
- 没读就批准大 diff。 下一个任务会从你批准的内容上继续分叉。
- 把规格写胖。 超过约 150-200 行,服从度就掉。拆成
CONTENT.md/DESIGN.md再 import,别养一个巨型文件。
FAQ
- 规格文件多长合适?: 目标 150 行以内,把约 200 行当硬上限。每一行每一轮都会被加载,过了这个点就是在交”上下文税”、还掉服从度。拆成分文件,由顶层规格引用。
- CLAUDE.md 还是 AGENTS.md?: 主要待在 Claude Code 就用
CLAUDE.md;用 Codex、Cursor、Copilot、Gemini CLI 就用AGENTS.md(开放标准,截至 2026 年 6 月被 20+ 工具读取、约 6 万+ 公开仓库在用)。想同时支持,就保一份文件、从另一份里@导入它。 .cursor/rules会取代 AGENTS.md 吗?: 不会,是互补。AGENTS.md放全项目通用规则,.cursor/rules/*.mdc(带globs和alwaysApply)放只对特定路径生效的规则,比如只在src/api/里加载的 API 约定。- Codex 和 Cursor 的做法一样吗?: 一样。原则与模型无关。区别只在文件名,以及 Codex/Cursor 会从仓库根往下拼接
AGENTS.md(离得最近的那份优先)。 - 我不太懂技术,写不出规格怎么办?: 让 Agent 通过访谈你的技术栈和约定,先写出 v1,你再编辑。bootstrap 没问题——规格只要存在就行。
- 规格里要带例子吗?: 要。一个具体例子(一个真实 slug、一段真实 frontmatter)顶五条抽象规则。
外部参考:Claude Code 记忆文档 与 AGENTS.md 开放标准。