AI 专题 APR 28, 2026

AI 开发规范:让 AI 在真实项目里稳定工作的工程体系

#AI 开发规范#AGENTS.md#Cursor Rules#工程体系#ADR

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/sdkzod 作为运行时依赖。新增任何依赖前必须开 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 类型
--redact4 个”零命中”和”未启用”在 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 稳定工作。

评论