AI 开发规范:让 AI 在真实项目里稳定工作的工程体系
AI 开发规范:让 AI 在真实项目里稳定工作的工程体系
本文是【AI 专题精讲】系列第 14 篇。 上一篇:Harness Engineering:从”AI 辅助”到”驾驭 AI”的工程效能革命
这篇文章你会得到什么
上一篇讲了 Harness Engineering 的框架——Rule、Skill、Hook、Subagent 四层体系。但框架是抽象的,真实项目里它长什么样?
这篇文章基于两个真实项目:
- conor-site:个人技术网站,Next.js + AI Persona 引擎,v1 已上线,v2 开发中
- ai-memory:开源 CLI 工具,从 AI 编辑器对话里提取知识,npm 上架,持续迭代
它们各有一套经过实战打磨的 AI 开发规范。这篇文章把这两套规范拆解开,提炼出一套可以直接复用的工程体系。
AI 在真实项目里怎么失控的
在你把 AI 引入真实项目之前,先看看它会怎么搞坏你的项目:
场景一:AI 改了不该改的代码
你说”优化一下首页加载性能”,AI 顺手把 v1 已经稳定上线的登录模块也重构了。你没说要改,但 AI 觉得”顺便优化一下”。
场景二:AI 遗忘了上一个 session 的决策
Week 1 你和 AI 决定用 event sourcing 处理账单,Week 3 新开一个 session,AI 直接写了一个 UPDATE 账单表的接口。两周前的架构决策,它一无所知。
场景三:AI 生成了”能跑但不对”的代码
AI 生成的 JSON 字段名是 user_id,你的 Schema 里用的是 userId。测试用的是 mock 数据,全绿。上线后调用方报错。
场景四:AI 在无声无息地做架构决策
你问 AI “这个功能怎么实现”,AI 直接开始写代码,在里面用了一个你没用过的库,绑定了一个你没有评估过的技术方案。这个决策没有被记录,三个月后你想换方案,发现已经深度耦合。
这四个场景的根本原因是同一个:AI 没有足够的约束和记忆。
它不知道哪些代码不能动、哪些决策已经做过、输出要满足什么规范。
这篇文章要解决的就是这件事。
第一步:规划——定好边界,再开工
AI 需要一份”入职文件”
普通程序员入职,HR 会给他一份《开发规范》文档。AI 也需要同样的东西,但不是给人看的那种——而是一份 AI 入职文件,专门为 AI 助手设计,让它在每次开始工作前都能快速对齐:
- 这个项目在做什么
- 现在进行到哪个阶段
- 什么是绝对不能碰的
- 代码应该怎么写
这份文件就是 AGENTS.md(或 Cursor 的 .cursor/rules/)。
以 conor-site 为例,它的 AGENTS.md 开头就是强制阅读顺序:
强制阅读顺序(开工前 MUST READ):
1. persona-charter.md ← 灵魂 · v2 为什么这么做(WHY)
2. ROADMAP.md ← 位置 · 现在在哪个 Phase(WHAT)
3. MEMORY.md 的 v2 章节 ← 进度 · 上次做到哪
4. docs/v2/README.md ← v2 文档索引
5. docs/v2/anti-patterns.md ← 设计新功能前必读
读不懂就停下问。不要边读边改代码。
AI 看到这个,不会莽撞地开始写代码。它知道先读懂上下文。
隔离区:明确告诉 AI 不能碰什么
这是规范里最重要的一条,但很多人忘了写。
conor-site 的做法:
🔒 v1 代码不动原则
v1 已上线并稳定运行。除非 bug 修复 / 安全更新 / 本人明确要求,
否则 v1 代码一行不改。
v2 全部走新路径:
- 新包:packages/ai/src/persona/、packages/memory/
- 新路由:/live、/admin/persona
- 新表:visitor_memory、persona_corrections
ai-memory 的做法类似,用的是”Critical Rules”:
## Critical Rules(不可违反)
1. 永远不破坏 CLI 接口——现有 flag 和命令必须向后兼容
2. Memory 文件格式稳定——.ai-memory/ 里的 Markdown 格式是公开 API
3. Bundle schema 有版本——version: 1 是契约,破坏性改变需要 version: 2
4. 所有新 feature 都要有测试
5. LLM Prompt 是关键——改 prompts.ts 会影响所有用户的提取质量
6. 永远不改 npm 包名
隔离区的意义:让 AI 知道”这里有高压线”,在它犯规之前就设好护栏,而不是犯规之后再修复。
认知地图:告诉 AI 去哪里找什么
ai-memory 的 AGENTS.md 末尾有一个”Where to Look”索引表:
| 问题 | 去哪里找 |
|---|---|
| 做了什么/接下来做什么? | ROADMAP.md |
| 每次发版改了什么? | CHANGELOG.md |
| 为什么用这个包名? | docs/decisions/2026-04-24-naming.md |
| 代码是怎么分层的? | docs/ARCHITECTURE.md |
| 怎么发版? | RELEASE-CHECKLIST.md |
这张表把 AI 的”下一步要去哪里”全部结构化。新开一个 session,AI 不用猜,直接按表查。
第二步:文档基建——给 AI 持久的记忆
AI 最大的工程问题不是能力不足,而是没有跨 session 的记忆。每次对话重开都是白板。在一个持续迭代的真实项目里,这是致命的。
四文档体系(conor-site 模式)
conor-site 用四份文档解决记忆问题:
| 文档 | 作用 | 更新时机 |
|---|---|---|
persona-charter.md | 产品灵魂,Why + 不变原则 | 极少改动,改了所有 AI 必须重读 |
ROADMAP.md | 当前 Phase + 决策日志 | Phase 切换时更新 |
MEMORY.md | 上次做到哪,当前状态 | 每次开发后必须更新 |
docs/v2/anti-patterns.md | 踩过的坑,不能重蹈 | 每次踩坑后更新 |
关键是 MEMORY.md。它是 AI 的”上次进度条”——每次开新 session,AI 读一遍 MEMORY.md,就能知道:
- 上一个 session 完成了什么
- 当前有哪些 blocking 问题
- 下一步要做什么
而且有一条铁律:
未更新文档 = Phase 未关门。
不是”开发完就完了”,而是开发完 + 更新文档才算完。这条规则强制让文档保持新鲜,不会变成三个月后无人维护的烂尾档案。
ADR(架构决策记录)——ai-memory 模式
ai-memory 用的是 ADR 体系。每个关键的架构或技术决策,都写一个日期命名的 Markdown 文件:
docs/decisions/
├── 2026-04-24-naming.md ← 为什么叫 ai-memory-cli
├── 2026-04-25-category-positioning.md ← 产品定位决策
└── 2026-04-26-post-v2.4-strategy.md ← v2.5 开发策略
每个 ADR 的结构:
# ADR 2026-04-24 — 包名选型
**Status:** Accepted
## Context
我们需要确定 npm 包名...
## Decision
使用 `ai-memory-cli`,而不是 `ai-memory`。
## 为什么不是其他方案
- `ai-memory` 太泛,不表达 CLI 工具特性
- `memory-cli` 没有 AI 属性
- `cursor-memory` 绑定单一编辑器
## Consequences
包名确定后不能再改,这是一个不可逆决策。
新开 session,AI 读 docs/decisions/ newest-first,立刻知道历史决策轨迹,不会在同样的问题上转第二遍。
两种模式的对比
| 维度 | MEMORY.md 模式(conor-site) | ADR 模式(ai-memory) |
|---|---|---|
| 适合场景 | 快速迭代,需要记住”当前状态” | 每个重大决策需要独立存档 |
| 维护成本 | 低(一个文件) | 中(多个文件,但每个简洁) |
| 历史追溯 | 差(覆盖式更新) | 好(每个决策独立保留) |
| AI 阅读效率 | 高(一次读完) | 中(多文件,需索引) |
实践建议:两者结合用——MEMORY.md 记当前状态,docs/decisions/ 存关键决策。
第三步:五层约束体系
有了入职文件和文档体系,AI 知道了”做什么”和”历史是什么”。下一步是约束”怎么做”——从轻到重,五个层次。
L1:AGENTS.md / Cursor Rules——文档级约束
最轻量的约束,靠 AI 读文档自觉遵守。
适合放:
- 项目上下文(技术栈、目标用户、北极星指标)
- 隔离区(不能碰的代码、不能用的方案)
- 认知顺序(读什么文档、在哪里找什么)
局限:AI 有时候”选择性遗忘”,尤其是 session 很长之后。
L2:.cursor/rules/*.mdc——自动注入约束
Rules 文件会被自动注入到每次对话,不需要 AI 主动去读。适合放代码规范。
# .cursor/rules/ts-strict.mdc
---
description: TypeScript 编码规范
globs: "**/*.ts,**/*.tsx"
---
- 禁止 any,用 unknown + 类型守卫
- async 函数必须有 try/catch
- 模块导入用 .js 扩展名(ESM)
- 禁止 console.warn,用 printWarning() 替代
# .cursor/rules/no-touch-v1.mdc
---
description: v1 代码保护规则
alwaysApply: true
---
v1 代码绝对不改,除非明确说"修 v1 的 bug"。
v2 开发全部走 packages/ai/src/persona/ 新路径。
ai-memory 的约束清单里有一条典型的:
Minimal runtime dependencies——只允许
@modelcontextprotocol/sdk和zod作为运行时依赖。新增任何依赖前必须开 discussion,优先用 Node.js 内置模块。
把这类约束写进 Rules,AI 每次生成代码时都会受到约束,不会手滑多装一个 lodash。
L3:Canonical Pattern——模式级约束
这是 ai-memory 用得很深的一层:把”正确的代码写法”固化为有名字的模式,写进 AGENTS.md。
Pure-function Pattern(纯函数模式):
业务逻辑 = 纯函数(可测试,无副作用)
IO 操作 = 薄包装层(在外面单独处理)
具体体现:
// ✅ 正确:纯函数做合并逻辑,IO 在外层
export function mergeMcpConfig(existing: string, additions: McpEntry[]): MergeResult {
// 纯逻辑,不碰文件系统
// 返回 'created' | 'updated' | 'already-up-to-date' | 'conflict'
}
// 外层 IO 薄包装
export async function writeMcpConfig(path: string, additions: McpEntry[]) {
const existing = await fs.readFile(path, 'utf8').catch(() => '{}');
const result = mergeMcpConfig(existing, additions); // 调纯函数
if (result.type !== 'already-up-to-date') {
await fs.writeFile(path, result.content);
}
return result;
}
为什么这是 Pattern 而不只是代码习惯?因为它被明确命名并写进了 AGENTS.md,AI 在写任何涉及”读文件 → 处理 → 写文件”的代码时,都知道要走这个模式,测试因此变得简单——只需要 mock 纯函数的输入,不需要 mock 文件系统。
Common Tasks 手册:
ai-memory 把每类重复性开发任务都写成了标准步骤:
### 添加新 CLI 命令
1. 创建 src/commands/my-command.ts
- 导出 runMyCommand(opts: CliOptions): Promise<number>
2. 在 types.ts 的 CliOptions.command union 里添加
3. 在 cli.ts 的 parseArgs() 里注册
4. 在 src/index.ts 的 switch 里路由
5. 在 cli.ts HELP 常量里添加帮助文本
6. 在 __tests__/cli.test.ts 里写测试
没有这个手册,每次新开命令 AI 都可能漏步骤。有了手册,AI 按清单走,步骤不缺。
L4:Feature Gate——品控级约束
conor-site 的”5 道门”是最有意思的一层。它不约束代码怎么写,而是约束什么条件下才能开始写。
每个新功能上线前,必须在 PR / commit message 里回答:
## Feature Gate Self-Check
- G1 信号耦合: 受哪个 signal 驱动 + 订阅代码路径
- G2 5分钟记忆: 一句带 because 的复述
- G3 反撞衫: 全网最接近的 3 个先例 + 为什么不是它们
- G4 删除测试: 失去的"唯一"东西
- G5 贯穿测试: loading/error/empty/404 的语气表现
任何一道门答不出来 → STOP,先问本人。
G1 确认功能有数据驱动;G3 确认做了竞品分析;G4 是核心——强迫你想清楚”这个功能独特在哪里”;G5 确认异常态也想清楚了。
AI 在生成代码之前,先被迫回答这五道门。很多”感觉该加”的功能,在这里就死掉了——答不出 G3,说明没想清楚。
L5:Spike-First——最重量级的约束
ai-memory 在 v2.5 开发中确立了一条规则:
涉及外部边界的 feature,必须先写 spike doc,再动一行代码。
“外部边界”包括:LLM API 调用方式、Marketplace 接入规格、Agent 指令格式、第三方协议解析。
为什么这么做?因为在 v2.5 的开发里,Spike-First 两次抓到了真实 Bug:
- 第一次:写
--redact功能前的 spike doc 发现,anthropic-key正则(sk-ant-api03-...)会被openai-key正则(sk-...[A-Za-z0-9])提前匹配吞掉——两个规则的执行顺序是有 bug 的,但不写 spike doc 根本不会想到这个问题。 - 第二次:写 Skills 输出前的 spike doc 发现,社区流传的”description ≤ 200 字符”是错误的,官方文档是”1536 字符”——如果先写代码,发布出去的 Skills 全都会被截断。
Spike doc 的结构很简单:
# Spike: [功能名] — [日期]
## 目标
要解决什么问题
## 调研结论
外部 spec / API / 协议的真实情况
## 决策
选什么方案,为什么不是其他方案
## 实施计划
分几步做,每步的依赖关系
## Re-spike 触发条件
什么情况下这份文档需要重新调研
写完 spike doc,再开始写代码。这条规则的代价是”多花 30-60 分钟”,收益是”不在生产环境里修复设计 bug”。
第四步:Benchmark——给 AI 输出质量打分
这是很多项目缺失的一环。AI 改了 Prompt、换了模型、调了参数——效果变好了还是变坏了?光靠”感觉”是不可靠的。
ai-memory 自建了 CCEB(Cursor Conversation Extraction Benchmark)——专门测量 AI 从对话中提取知识的质量。
Benchmark 的设计思路
bench/cceb/
├── fixtures/ ← 手工标注的"标准答案"对话集(30 个)
├── scorer.ts ← 纯函数打分器(P / R / F1)
├── runner.ts ← 跑 AI 提取 → 对比标准答案
└── README.md ← 评测方法 + 如何添加 fixture
使用方式:
npm run bench:cceb:dry # 不调 LLM,只做 smoke test,~1 秒
npm run bench:cceb # 真实跑,需要 OPENAI_API_KEY,~3 分钟
Benchmark 带来的工程价值
v2.4 → v2.5 的一次 Prompt 改进:
改动:提取 Prompt 里加了"one decision per discussion thread"约束
+ 提高 TODO 的判定门槛(要有明确的 commitment language)
效果:F1 从 56% → 76%,Precision 从 44% → 67%,Recall 从 78% → 89%
这个数字意味着:AGENTS.md 里的内容质量提升了——原来 44% 精准度意味着 AGENTS.md 里一半是噪音,AI 读了也没用。
没有 Benchmark,这次 Prompt 改动的效果只能是”感觉好像好一点”。有了 Benchmark,F1 56% → 76% 是可以写进 README、可以放进发布公告的具体数字。
你的项目该怎么做 Benchmark
不是所有项目都需要做 CCEB 这种级别的评测。但如果你有 AI 驱动的核心功能,可以参考这个最小模型:
# eval.py —— 最小可行 AI 输出评测
import json
test_cases = [
{
"input": "帮我总结这段会议记录...",
"expected_traits": ["action items", "决策", "负责人"],
"forbidden_traits": ["个人信息", "闲聊内容"],
},
# 更多测试用例...
]
def evaluate(ai_output: str, case: dict) -> dict:
hits = [t for t in case["expected_traits"] if t in ai_output]
violations = [t for t in case["forbidden_traits"] if t in ai_output]
return {
"hit_rate": len(hits) / len(case["expected_traits"]),
"violation_count": len(violations),
"passed": len(violations) == 0 and len(hits) / len(case["expected_traits"]) >= 0.8,
}
关键是先写标准答案,再改 Prompt。不要倒过来——先改了 Prompt 再说”感觉好多了”,那是没有约束的乱改。
第五步:Same-Day Audit Pass——完工后换个视角看一遍
这是 ai-memory 在 v2.5 里形成的一个习惯:每个 feature 开发完之后,当天做一次”换视角审计”。
具体操作:
- 不看”我写了什么”
- 看”如果我第一次看到这段代码,我会测什么”
- 特别关注:边界 case、文档是否同步、相关的其他模块是否遗漏
v2.5 的每个 feature 这样审计下来的结果:
| Feature | 审计发现的问题数 | 典型问题 |
|---|---|---|
| Skills 输出 | 2 个 | --json 计数字段漏掉了 architecture 类型 |
--redact | 4 个 | ”零命中”和”未启用”在 JSON 里无法区分 |
| Codex 适配器 | 4 个 | Bundle 导入白名单漏了 codex 这个 source type |
每次 30-60 分钟,发现 2-4 个真实问题。代价极低,收益明显。
这个习惯的本质是强制换视角。开发者在写代码时是”作者视角”,只看自己做了什么。审计时切换到”读者视角”,从”用这个功能的人会遇到什么”出发来找问题。
把这个习惯写进 AGENTS.md,AI 在完成一个 feature 后会主动做审计,而不需要你提醒。
完整体系总览
把上面五步组合起来,一个真实项目的 AI 开发规范长这样:
项目初始化时
├── AGENTS.md ← AI 入职文件(上下文 + 隔离区 + 认知地图)
├── ROADMAP.md ← 当前 Phase 和方向
├── MEMORY.md ← 当前进度(每次开发后更新)
├── docs/decisions/ ← ADR(关键决策存档)
└── docs/v2/anti-patterns.md ← 踩坑记录
每次开发前
├── AI 读 AGENTS.md ← 对齐上下文
├── AI 读 MEMORY.md ← 恢复上次进度
└── (复杂 feature) 先写 spike doc ← Spike-First 纪律
开发中
├── .cursor/rules/*.mdc ← 自动注入代码规范
├── Canonical Pattern ← 按照 AGENTS.md 里的模式写代码
└── Feature Gate 5 道门 ← 功能上线前强制回答
开发完
├── Same-Day Audit Pass ← 换视角审计
├── 更新 MEMORY.md ← 未更新文档 = Phase 未关门
└── (关键决策) 写 ADR ← 决策存档
持续运营
├── Benchmark(如 CCEB) ← 量化 AI 输出质量
├── anti-patterns.md 更新 ← 踩坑即记录
└── AGENTS.md 迭代 ← 规范随项目演进
最小可行版本:三个文件起步
如果你现在就想开始,不需要一步到位。三个文件就能搭起骨架:
文件一:AGENTS.md
# [项目名] AI 开发规则
## 项目概述
[一句话说项目是什么、用什么技术栈]
## 强制阅读顺序
1. ROADMAP.md ← 当前在哪
2. MEMORY.md ← 上次到哪
## 隔离区(不能碰)
- [稳定上线的模块]:不改,除非明确说修 bug
- [生产数据库]:不直接操作
## 代码规范
- [你最重要的 3-5 条编码规范]
## 不可违反的红线
- [破坏性改变需要明确授权]
- [所有新功能必须有测试]
文件二:MEMORY.md
# 开发进度
## 当前状态
[现在在做什么,卡在哪里]
## 已完成
- [日期] [功能1]
- [日期] [功能2]
## 下一步
- [ ] [任务1]
- [ ] [任务2]
## 注意事项
[最近发现的坑,AI 应该避开的地雷]
文件三:docs/decisions/README.md(ADR 索引)
# 架构决策记录
| 日期 | 文件 | 决策 |
|------|------|------|
| 2026-01-01 | 2026-01-01-tech-stack.md | 选 Next.js 而不是 Remix |
三个文件,半小时能写完。AI 有了这三个文件,在你的项目里就不是完全的白板了。
总结
AI 开发规范不是限制 AI,而是给 AI 提供它需要但默认没有的东西:
| AI 缺少的 | 规范提供的 |
|---|---|
| 跨 session 记忆 | MEMORY.md + ADR |
| 项目上下文 | AGENTS.md |
| 代码规范 | .cursor/rules/*.mdc |
| 架构边界感知 | 隔离区 + Critical Rules |
| 功能质量把关 | Feature Gate |
| 输出质量验证 | Benchmark |
第 13 篇讲的 Harness Engineering 提供了框架(Rule/Skill/Hook/Subagent),这篇是它的实战落地——两个真实项目里,这套框架还额外需要:
- ADR:给 AI 提供历史决策记忆(Harness 里的记忆层)
- Spike-First:外部边界必须先设计再编码(Harness 里的元约束)
- Benchmark:量化 AI 输出质量,感觉不算数(Harness 里的质量度量)
这三件事合在一起,才能在真实项目里让 AI 稳定工作。