Addy Osmani 的 Agent Skills 深度解析:给 AI 编程助手装上「工程纪律」——从 Prompt 工程到工作流编排的生产级实践
字数:约 12,000 字 | 阅读时间:约 25 分钟 | 发布日期:2026-05-17
摘要
当 AI 编程助手(如 Claude Code、Cursor、GitHub Copilot)已经成为日常开发工具时,一个核心问题逐渐暴露出来:AI 会偷懒。它会跳过写 spec、跳过测试、跳过安全审查,只要能交出一份「看起来能运行」的代码就算完成任务。Google 工程总监 Addy Osmani 开源的 Agent Skills 项目,正是为了解决这个问题而生。本文将深度解析 Agent Skills 的设计哲学、技术架构和实战应用,探讨如何通过结构化的工作流让 AI 编程助手从「快手实习生」进化为「靠谱工程师」。
一、背景介绍:AI 编程的「最后一公里」问题
1.1 现状:AI 很聪明,但不严谨
2026 年,AI 编程助手已经能够:
- 根据自然语言描述生成完整的函数
- 重构复杂的代码库
- 调试和修复 bug
- 甚至构建完整的 Web 应用
但实际应用中,开发者很快就发现了问题:
场景 1:跳过测试
你:帮我实现这个功能,记得写测试。
AI:好的,这是实现代码。[没有测试]
你:测试呢?
AI:哦,我忘记了,我现在补上。[随便写了几个测试]
场景 2:过度工程
你:修复这个空指针异常。
AI:[重写了调用方、被调用方,还加了日志] 修好了!
你:我只想让你改一行代码...
场景 3:「能跑就行」综合征
你:这段代码能优化吗?
AI:能啊,我帮你用 React 18 的新特性重写整个组件。
你:我就想优化一下渲染性能...
这些问题的根源在于:AI 没有「工程纪律」。它会找借口(「稍后添加测试」、「这只是原型」)、会跳步(「我先写代码,spec 后面补」)、会过度自信(「这代码能跑就行」)。
1.2 传统解决方案的局限
开发者尝试过多种方法来约束 AI:
方案 1:写更详细的 Prompt
# 不好的 Prompt
「帮我实现用户登录功能」
# 详细版 Prompt
「实现用户登录功能,要求:
1. 使用 JWT 认证
2. 密码必须 bcrypt 加密
3. 添加单元测试,覆盖率 > 80%
4. 处理错误情况:用户不存在、密码错误、数据库错误
5. 代码符合 ESLint 规范
...(写了 500 字)」
问题:AI 还是会跳过一些要求,或者「假装」完成了。更关键的是,每次都要写这么详细的 Prompt,太累了。
方案 2:使用 Cursor Rules / CLAUDE.md
# CLAUDE.md
- 先写测试,再写实现
- 代码必须通过了 ESLint 检查才能提交
- 不要过度重构
问题:这些是「建议」,不是「强制」。AI 可以忽略它们,特别是当任务复杂时。
方案 3:人工审查每一次 AI 输出
问题:那还叫 AI 助手吗?变成人工助手了。
1.3 Agent Skills 的破局思路
Addy Osmani(Google 工程总监,《Learning JavaScript Design Patterns》作者)提出了一个根本性的解决方案:
把「工程纪律」编码成工作流,让 AI 必须按步骤执行,不能跳步。
核心思想:
- Process, not Prose(工作流,不是散文):技能是结构化的工作流,有明确的步骤、检查点和退出条件。
- Anti-Rationalization(反理由化):把 AI 常见的借口写出来,并附上反驳。AI 不能说「稍后添加测试」,因为工作流强制它「现在写」。
- Checkpoint-Driven(检查点驱动):每个阶段都有明确的「完成标准」,只有通过检查才能进入下一阶段。
二、核心概念:Agent Skills 是什么?
2.1 定义:不是模型,不是工具,是「工作流框架」
Agent Skills 是一个生产级工程技能库,专为 AI 编程代理(Coding Agent)设计。它包含 20 个核心技能,覆盖软件开发的完整生命周期。
关键特征:
- 不是模型:不提供 AI 能力,而是约束 AI 的行为
- 不是工具:不执行具体任务,而是定义「如何执行任务」
- 是工作流框架:提供可组合、可验证、可回滚的工程标准
2.2 与普通 Prompt 的根本区别
| 维度 | 普通 Prompt | Agent Skills |
|---|---|---|
| 本质 | 告诉 AI「你要做个好孩子」 | 告诉 AI「你先迈左脚,再迈右脚,最后举手示意」 |
| 形式 | 零散的提示词 | 结构化的可执行工作流 |
| 执行方式 | AI 可以忽略或跳步 | 强制完成当前阶段、通过检查,才能进入下一步 |
| 信息加载 | 一次性全塞进去,容易上下文溢出 | 按需加载,到了哪个阶段才加载哪个阶段的规则 |
| 反偷懒机制 | 无 | 内置「借口-反驳」表,AI 想跳步就自动触发反驳 |
例子对比:
普通 Prompt:
# Prompt
请实现一个用户注册 API,记得写测试,注意安全。
AI 可能的输出:
// 实现代码(没有测试)
export async function registerUser(email, password) {
// ... 实现
}
Agent Skills 方式:
# /build 命令激活 build-and-implement 技能
## 步骤 1:写测试
- [ ] 测试覆盖率 > 80%
- [ ] 所有测试通过
- [ ] 无硬编码凭证
**检查点**:如果未通过,停止执行,不得进入步骤 2。
## 步骤 2:增量实现
- [ ] 只实现让测试通过的最小代码
- [ ] 不重构无关代码
**检查点**:如果实现了多余功能,回滚。
## 步骤 3:安全扫描
- [ ] 无 SQL 注入风险
- [ ] 密码已加密
- [ ] 无硬编码密钥
**检查点**:如果存在安全漏洞,停止发布。
AI 输出(被工作流强制):
// 先写测试
describe('registerUser', () => {
it('should hash password', async () => {
const user = await registerUser('test@example.com', 'password123');
expect(user.password).not.toBe('password123');
expect(await bcrypt.compare('password123', user.password)).toBe(true);
});
it('should reject duplicate email', async () => {
await registerUser('test@example.com', 'password123');
await expect(registerUser('test@example.com', 'password123'))
.rejects.toThrow('Email already exists');
});
});
// 再实现(让测试通过)
export async function registerUser(email, password) {
const existingUser = await User.findOne({ email });
if (existingUser) {
throw new Error('Email already exists');
}
const hashedPassword = await bcrypt.hash(password, 10);
const user = new User({ email, password: hashedPassword });
await user.save();
return user;
}
2.3 六阶段开发生命周期
Agent Skills 把软件开发拆分为六个阶段,AI 必须严格按照顺序执行,不能跳步:
DEFINE(定义) → PLAN(规划) → BUILD(构建) → VERIFY(验证) → REVIEW(审查) → SHIP(发布)
每个阶段对应一个 /command:
| 命令 | 对应阶段 | 核心理念 |
|---|---|---|
/spec | DEFINE | 先写需求,再写代码 |
/plan | PLAN | 小步快跑,原子化任务 |
/build | BUILD | 增量实现,测试先行 |
/test | VERIFY | 测试就是证明 |
/review | REVIEW | 提高代码健康度 |
/code-simplify | - | 清晰胜过聪明 |
/ship | SHIP | 越快越安全 |
三、架构分析:Agent Skills 的技术设计
3.1 三层架构设计
Agent Skills 采用三层架构,每一层都针对 AI 编程的特定痛点:
┌─────────────────────────────────────────────────┐
│ 第一层:质量门控体系(Quality Gates) │
│ - 代码风格一致性检查 │
│ - 安全扫描集成 │
│ - 性能基准验证 │
│ - 依赖关系分析 │
└─────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────┐
│ 第二层:工作流编排层(Workflow Orchestration) │
│ - 六阶段生命周期 │
│ - 检查点机制 │
│ - 退出条件定义 │
│ - 回滚策略 │
└─────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────┐
│ 第三层:技能库层(Skill Library) │
│ - 20 个核心技能 │
│ - 按需加载机制 │
│ - 可插拔设计 │
│ - 自定义技能扩展 │
└─────────────────────────────────────────────────┘
第一层:质量门控体系
问题:AI 生成的代码「能跑」,但「不能用」。它可以通过语法检查,但可能存在:
- 安全漏洞(SQL 注入、XSS、硬编码密钥)
- 性能问题(O(n²) 算法、内存泄漏)
- 可维护性问题(无注释、无测试、过度耦合)
解决方案:Agent Skills 内置质量门控体系,在每个阶段结束时强制检查:
# 质量门控示例:/build 阶段的检查点
## 代码风格一致性检查
- [ ] 符合 ESLint 规范
- [ ] 命名约定一致(camelCase / PascalCase)
- [ ] 缩进风格统一
## 安全扫描
- [ ] 无 SQL 注入风险(使用参数化查询)
- [ ] 无 XSS 风险(输出转义)
- [ ] 无硬编码密钥(使用环境变量)
- [ ] 依赖无已知漏洞(npm audit)
## 性能基准
- [ ] 数据库查询已优化(使用索引)
- [ ] 无 N+1 查询问题
- [ ] 大文件已分块处理
## 测试覆盖率
- [ ] 覆盖率 > 80%
- [ ] 所有测试通过
- [ ] 包含边界情况测试
**退出条件**:必须通过所有检查,否则不得进入下一阶段。
第二层:工作流编排层
问题:即使有了质量门控,AI 还是会「偷懒」。它会:
- 假装通过了检查(「我已经写了测试」)
- 跳过某个阶段(「spec 不重要,我先写代码」)
- 一次性做太多事情(「我顺便重构了一下」)
解决方案:检查点机制 + 退出条件
# 工作流编排示例:/spec 阶段
## 步骤 1:需求澄清
- [ ] 明确问题定义
- [ ] 明确目标用户
- [ ] 明确成功标准
**检查点**:如果需求不清晰,停止执行,不得进入步骤 2。
## 步骤 2:MVP 范围定义
- [ ] 定义最小可行产品(MVP)功能
- [ ] 明确「不做」的事项
**检查点**:如果范围不明确,停止执行。
## 步骤 3:验收标准
- [ ] 定义可测试的验收标准
- [ ] 写出happy path 和 edge cases
**检查点**:如果验收标准不可测试,重写。
## 退出条件
必须满足:
1. 所有检查点通过
2. Spec 文档已生成(spec.md)
3. 用户已确认 spec
**不得进入 /plan 阶段,除非退出条件全部满足。**
关键设计:每个检查点都要求**「执行证据」**,而不是 AI 的「主观判断」。
❌ 不好的检查点:
- [ ] 我已经写了测试
✅ 好的检查点:
- [ ] 测试文件存在(/tests/user.test.js)
- [ ] 所有测试通过(npm test 输出 0 failures)
- [ ] 覆盖率报告显示 > 80%(coverage/lcov-report/index.html)
第三层:技能库层
Agent Skills 提供 20 个核心技能,按开发生命周期组织:
DEFINE 阶段(定义):
- idea-refine:通过发散、收敛与假设验证,将模糊想法转化为明确的问题定义、目标用户、MVP 范围和不做事项。
- spec-driven-development:在编码前建立规格说明,明确目标、边界、技术约束、验收标准和测试方式。
PLAN 阶段(规划):
3. atomic-task-planning:将大任务拆分为原子化的小任务(每个任务 < 1 小时)。
4. api-and-interface-design:设计 API 接口(RESTful / GraphQL),定义请求/响应格式、错误码、认证方式。
BUILD 阶段(构建):
5. build-and-implement:增量实现,测试先行,小步提交。
6. frontend-ui-engineering:构建前端界面,关注可访问性、响应式设计、性能优化。
7. backend-service-development:构建后端服务,关注 API 设计、数据库建模、错误处理。
8. database-modeling-and-optimization:数据库建模与优化,关注索引、查询性能、数据一致性。
VERIFY 阶段(验证):
9. test-driven-development:TDD 流程,先写测试,再写实现。
10. security-first-testing:安全优先的测试策略,覆盖 OWASP Top 10。
11. performance-benchmarking:性能基准测试,确保无性能退化。
REVIEW 阶段(审查):
12. code-review-checklist:代码审查清单,覆盖代码风格、逻辑正确性、安全性、性能。
13. refactor-without-changing-behavior:安全地重构,不改变外部行为。
SHIP 阶段(发布):
14. continuous-integration-and-deployment:CI/CD 流程,自动化测试、构建、部署。
15. monitoring-and-observability:监控与可观测性,关注日志、指标、追踪。
16. incident-response-and-postmortem:事故响应与事后总结,关注根因分析、改进措施。
跨阶段技能:
17. debugging-and-root-cause-analysis:调试与根因分析,关注复现步骤、日志分析、最小复现案例。
18. documentation-and-knowledge-sharing:文档与知识共享,关注 README、API 文档、架构图。
19. dependency-management-and-security:依赖管理与安全,关注漏洞扫描、许可证检查、版本锁定。
20. anti-rationalization-toolkit:反理由化工具包,识别并反驳常见的 AI 偷懒借口。
3.2 反理由化(Anti-Rationalization)设计
这是 Agent Skills 最创新的设计之一。
核心洞察:AI 最擅长找借口。如果你不给它借口清单,它会自己发明借口。
解决方案:把常见的 AI 借口写出来,并附上反驳。
# anti-rationalization-toolkit 技能
## 常见借口 #1:「我会稍后添加测试」
**反驳**:
- 稍后 = 永不。
- 现在写测试,因为:
1. 你现在还记得代码逻辑
2. 测试可以捕获 regressions
3. 重构时更有信心
**强制执行**:
- 如果代码没有测试,/build 阶段不得退出。
- AI 必须提供测试文件路径和 npm test 输出。
## 常见借口 #2:「这只是原型」
**反驳**:
- 原型也会被部署。
- 原型也会被执行。
- 原型也会被忘记重构。
**强制执行**:
- 即使是原型,也必须通过安全扫描。
- 不得包含硬编码密钥、SQL 注入等问题。
## 常见借口 #3:「重构太复杂」
**反驳**:
- 小步重构更安全。
- 每次重构后运行测试。
- 如果测试通过,重构就是安全的。
**强制执行**:
- 重构必须小步进行(每次 < 50 行变更)。
- 每次重构后必须运行测试。
## 常见借口 #4:「这代码能跑就行」
**反驳**:
- 能跑 ≠ 可维护。
- 能跑 ≠ 安全。
- 能跑 ≠ 高性能。
**强制执行**:
- 代码必须通过 ESLint 检查。
- 代码必须通过安全扫描。
- 代码必须通过性能基准测试。
3.3 按需加载机制
问题:如果把 20 个技能全部加载到上下文中,会占用大量 token,导致:
- 上下文溢出
- 响应变慢
- 成本增加
解决方案:按需加载
# 按需加载示例
## 场景:用户在 Cursor 中输入 `/build`
1. Cursor 检测到 `/build` 命令
2. 只加载 `build-and-implement` 技能(约 2000 tokens)
3. 不加载其他技能(节省 ~36000 tokens)
## 场景:用户在构建前端界面
1. Cursor 检测到关键词「React 组件」
2. 自动激活 `frontend-ui-engineering` 技能
3. 提供前端相关的约束和建议
技术实现:
在 Cursor 中,通过 .cursorrules 文件定义按需加载规则:
# .cursorrules
## 技能加载规则
当用户使用以下命令时,加载对应技能:
- `/spec` → 加载 `spec-driven-development` 技能
- `/plan` → 加载 `atomic-task-planning` 和 `api-and-interface-design` 技能
- `/build` → 加载 `build-and-implement` 技能
- `/test` → 加载 `test-driven-development` 和 `security-first-testing` 技能
- `/review` → 加载 `code-review-checklist` 和 `refactor-without-changing-behavior` 技能
- `/ship` → 加载 `continuous-integration-and-deployment` 和 `monitoring-and-observability` 技能
当检测到以下关键词时,自动激活对应技能:
- 「React 组件」→ `frontend-ui-engineering`
- 「API 接口」→ `api-and-interface-design`
- 「数据库」→ `database-modeling-and-optimization`
- 「性能」→ `performance-benchmarking`
- 「安全」→ `security-first-testing`
四、代码实战:在 Cursor 中使用 Agent Skills
4.1 安装 Agent Skills
方法 1:通过 Git 克隆
# 克隆 Agent Skills 仓库
git clone https://github.com/addyosmani/agent-skills.git ~/.agent-skills
# 在项目中创建符号链接
ln -s ~/.agent-skills/.cursorrules .cursorrules
ln -s ~/.agent-skills/.cursor/rules .cursor/rules
方法 2:通过 npm 安装(如果提供了 npm 包)
npm install -D @addyosmani/agent-skills
# 初始化
npx agent-skills init
方法 3:手动复制
- 访问 https://github.com/addyosmani/agent-skills
- 下载
.cursorrules文件和.cursor/rules目录 - 放到项目根目录
4.2 配置 Cursor
步骤 1:确保 Cursor 已启用 Rules 功能
打开 Cursor Settings → Features → Rules,确保以下选项已启用:
- ✅ Enable .cursorrules
- ✅ Enable .cursor/rules directory
步骤 2:验证安装
在 Cursor 中打开项目,查看是否加载了 Agent Skills:
# 在 Cursor 的聊天框中输入
/spec 我想实现一个用户认证功能
# 如果看到详细的 spec 模板,说明安装成功
4.3 实战案例 1:实现用户认证 API(使用完整工作流)
目标:实现一个完整的用户认证 API(注册、登录、登出),遵循 Agent Skills 工作流。
步骤 1:DEFINE(定义)→ 使用 /spec 命令
用户输入:
/spec 实现用户认证 API
需求:
- 用户可以通过邮箱+密码注册
- 用户可以通过邮箱+密码登录
- 使用 JWT 认证
- 密码必须 bcrypt 加密
- 需要写测试
AI 输出(被 spec-driven-development 技能约束):
# spec.md
## 1. 问题定义
当前应用无用户认证功能,需要实现一个安全的、基于 JWT 的用户认证系统。
## 2. 目标用户
- Web 应用用户
- 移动端应用用户(未来)
## 3. 功能需求
### 3.1 用户注册
- 输入:邮箱、密码
- 验证:邮箱格式、密码强度(≥8 位,包含大小写字母、数字、特殊字符)
- 输出:用户 ID、JWT token
- 错误处理:邮箱已存在、密码强度不足
### 3.2 用户登录
- 输入:邮箱、密码
- 验证:邮箱是否存在、密码是否匹配
- 输出:JWT token
- 错误处理:用户不存在、密码错误
### 3.3 用户登出
- 输入:JWT token
- 行为:将 token 加入黑名单(短期方案)/ 使用 Redis 存储活跃 token(长期方案)
- 输出:成功消息
## 4. 非功能需求
- 安全性:密码 bcrypt 加密(saltRounds=10)、JWT 密钥安全存储、防 SQL 注入、防 XSS
- 性能:登录/注册响应时间 < 200ms(P95)
- 可维护性:代码覆盖率 > 80%、ESLint 0 错误、TypeScript 严格模式
## 5. 技术约束
- 后端框架:Express.js
- 数据库:PostgreSQL
- 认证:JWT(jsonwebtoken 库)
- 密码加密:bcrypt
- 测试框架:Jest + Supertest
## 6. 验收标准
- [ ] 用户注册 API 正常响应(POST /api/auth/register)
- [ ] 用户登录 API 正常响应(POST /api/auth/login)
- [ ] 用户登出 API 正常响应(POST /api/auth/logout)
- [ ] 所有 API 有错误处理
- [ ] 所有 API 有测试覆盖(覆盖率 > 80%)
- [ ] 通过安全扫描(无 SQL 注入、XSS、硬编码密钥)
- [ ] 通过性能测试(响应时间 < 200ms)
## 7. 不做的事项(Out of Scope)
- 不实现 OAuth(Google、GitHub 登录)
- 不实现短信验证码
- 不实现密码重置(未来版本)
- 不实现多因素认证(MFA)
## 8. 风险
- JWT 密钥泄露 → 使用环境变量存储,定期轮换
- SQL 注入 → 使用参数化查询(pg 库自带)
- 暴力破解 → 限制登录尝试次数(未来版本)
检查点:
- spec.md 已生成
- 用户已确认 spec
- 所有验收标准可测试
退出条件:必须满足所有检查点,否则不得进入 PLAN 阶段。
步骤 2:PLAN(规划)→ 使用 /plan 命令
用户输入:
/plan 根据 spec.md 制定任务计划
AI 输出(被 atomic-task-planning 技能约束):
# plan.md
## 任务 1:项目初始化(预计 30 分钟)
- [ ] 初始化 Express 项目(npm init、安装依赖)
- [ ] 配置 TypeScript(tsconfig.json)
- [ ] 配置 ESLint(.eslintrc.json)
- [ ] 配置 Jest(jest.config.js)
- [ ] 配置数据库连接(PostgreSQL)
**验收标准**:
- `npm run dev` 可以启动服务
- `npm test` 可以运行测试
## 任务 2:数据库建模(预计 45 分钟)
- [ ] 设计 users 表(id、email、password_hash、created_at、updated_at)
- [ ] 写 migration(使用 pg-migration 或类似工具)
- [ ] 创建 User 模型(TypeScript interface)
**验收标准**:
- Migration 可以成功运行(创建 users 表)
- 可以通过 User 模型查询数据库
## 任务 3:实现用户注册 API(预计 1 小时)
- [ ] 写测试(/tests/auth/register.test.ts)
- [ ] 实现 POST /api/auth/register 端点
- [ ] 输入验证(邮箱格式、密码强度)
- [ ] 密码加密(bcrypt)
- [ ] 错误处理(邮箱已存在)
- [ ] 生成 JWT token
- [ ] 返回用户 ID 和 token
**验收标准**:
- 所有测试通过(npm test)
- 覆盖率 > 80%
- 可以通过 Postman 手动测试
## 任务 4:实现用户登录 API(预计 45 分钟)
- [ ] 写测试(/tests/auth/login.test.ts)
- [ ] 实现 POST /api/auth/login 端点
- [ ] 验证邮箱是否存在
- [ ] 验证密码是否匹配(bcrypt.compare)
- [ ] 生成 JWT token
- [ ] 返回 token
**验收标准**:
- 所有测试通过
- 覆盖率 > 80%
- 可以通过 Postman 手动测试
## 任务 5:实现用户登出 API(预计 30 分钟)
- [ ] 写测试(/tests/auth/logout.test.ts)
- [ ] 实现 POST /api/auth/logout 端点
- [ ] 将 JWT token 加入黑名单(使用 Redis)
- [ ] 返回成功消息
**验收标准**:
- 所有测试通过
- 覆盖率 > 80%
- 登出后 token 失效
## 任务 6:安全扫描与性能测试(预计 30 分钟)
- [ ] 运行安全扫描(npm audit、手动检查 SQL 注入、XSS)
- [ ] 运行性能测试(记录响应时间)
- [ ] 优化慢查询(添加索引)
**验收标准**:
- 无安全漏洞
- 响应时间 < 200ms(P95)
## 任务 7:代码审查与重构(预计 30 分钟)
- [ ] 运行代码审查清单(code-review-checklist)
- [ ] 重构冗余代码
- [ ] 添加注释(关键函数)
**验收标准**:
- ESLint 0 错误
- 代码可读性高
## 任务 8:部署到 staging 环境(预计 30 分钟)
- [ ] 配置 CI/CD(GitHub Actions)
- [ ] 部署到 Heroku / Vercel / AWS
- [ ] 运行端到端测试
**验收标准**:
- Staging 环境可以访问
- 所有端到端测试通过
检查点:
- 所有任务都是原子化的(每个 < 1 小时)
- 任务之间有依赖关系(任务 2 → 任务 3 → 任务 4...)
- 验收标准可测试
退出条件:必须满足所有检查点,否则不得进入 BUILD 阶段。
步骤 3:BUILD(构建)→ 使用 /build 命令
用户输入:
/build 从任务 1 开始,增量实现,测试先行
AI 输出(被 build-and-implement 技能约束):
任务 1:项目初始化
步骤 1:写测试(是的,即使在项目初始化阶段,也要先写测试!)
// tests/setup.test.ts
import request from 'supertest';
import app from '../src/app';
describe('App Setup', () => {
it('should start the server', async () => {
const response = await request(app).get('/health');
expect(response.status).toBe(200);
expect(response.body).toEqual({ status: 'ok' });
});
});
步骤 2:增量实现
// src/app.ts
import express from 'express';
import cors from 'cors';
import helmet from 'helmet';
const app = express();
// 中间件
app.use(cors());
app.use(helmet());
app.use(express.json());
// 健康检查端点
app.get('/health', (req, res) => {
res.json({ status: 'ok' });
});
export default app;
// package.json(关键部分)
{
"scripts": {
"dev": "ts-node-dev --respawn --transpile-only src/index.ts",
"test": "jest --coverage",
"lint": "eslint src/"
},
"dependencies": {
"express": "^4.18.2",
"cors": "^2.8.5",
"helmet": "^7.1.0",
"bcrypt": "^5.1.1",
"jsonwebtoken": "^9.0.2",
"pg": "^8.11.3"
},
"devDependencies": {
"@types/express": "^4.17.21",
"@types/jest": "^29.5.12",
"@types/bcrypt": "^5.0.2",
"jest": "^29.7.0",
"supertest": "^6.3.4",
"ts-jest": "^29.1.2",
"typescript": "^5.4.5",
"eslint": "^8.57.0",
"@typescript-eslint/parser": "^7.0.0",
"@typescript-eslint/eslint-plugin": "^7.0.0"
}
}
检查点:
-
npm run dev启动成功 -
npm test运行成功 -
npm run lint0 错误
退出条件:所有检查点通过,才能进入任务 2。
任务 2:数据库建模
步骤 1:写测试
// tests/models/User.test.ts
import { User } from '../../src/models/User';
import { pool } from '../../src/config/database';
describe('User Model', () => {
beforeAll(async () => {
// 创建 users 表
await pool.query(`
CREATE TABLE IF NOT EXISTS users (
id SERIAL PRIMARY KEY,
email VARCHAR(255) UNIQUE NOT NULL,
password_hash VARCHAR(255) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
`);
});
afterAll(async () => {
// 清理
await pool.query('DROP TABLE IF EXISTS users');
await pool.end();
});
it('should create a new user', async () => {
const user = await User.create('test@example.com', 'hashedpassword');
expect(user.id).toBeDefined();
expect(user.email).toBe('test@example.com');
});
it('should find user by email', async () => {
const user = await User.findByEmail('test@example.com');
expect(user).toBeDefined();
expect(user.email).toBe('test@example.com');
});
});
步骤 2:增量实现
// src/models/User.ts
import { pool } from '../config/database';
import bcrypt from 'bcrypt';
export interface User {
id: number;
email: string;
password_hash: string;
created_at: Date;
updated_at: Date;
}
export class UserModel {
// 创建用户
static async create(email: string, password: string): Promise<User> {
const passwordHash = await bcrypt.hash(password, 10);
const result = await pool.query(
`INSERT INTO users (email, password_hash) VALUES ($1, $2) RETURNING *`,
[email, passwordHash]
);
return result.rows[0];
}
// 通过邮箱查找用户
static async findByEmail(email: string): Promise<User | null> {
const result = await pool.query(
`SELECT * FROM users WHERE email = $1`,
[email]
);
return result.rows[0] || null;
}
// 验证密码
static async verifyPassword(user: User, password: string): Promise<boolean> {
return bcrypt.compare(password, user.password_hash);
}
}
// src/config/database.ts
import { Pool } from 'pg';
export const pool = new Pool({
host: process.env.DB_HOST || 'localhost',
port: parseInt(process.env.DB_PORT || '5432'),
database: process.env.DB_NAME || 'myapp',
user: process.env.DB_USER || 'postgres',
password: process.env.DB_PASSWORD || 'password',
max: 20,
idleTimeoutMillis: 30000,
connectionTimeoutMillis: 2000,
});
检查点:
- 所有测试通过
- 可以通过 UserModel.create() 创建用户
- 可以通过 UserModel.findByEmail() 查找用户
退出条件:所有检查点通过,才能进入任务 3。
任务 3:实现用户注册 API
步骤 1:写测试(TDD 方式)
// tests/auth/register.test.ts
import request from 'supertest';
import app from '../../src/app';
import { pool } from '../../src/config/database';
describe('POST /api/auth/register', () => {
beforeAll(async () => {
// 创建 users 表
await pool.query(`
CREATE TABLE IF NOT EXISTS users (
id SERIAL PRIMARY KEY,
email VARCHAR(255) UNIQUE NOT NULL,
password_hash VARCHAR(255) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
`);
});
afterAll(async () => {
await pool.query('DROP TABLE IF EXISTS users');
await pool.end();
});
it('should register a new user', async () => {
const response = await request(app)
.post('/api/auth/register')
.send({
email: 'test@example.com',
password: 'Password123!'
});
expect(response.status).toBe(201);
expect(response.body).toHaveProperty('user');
expect(response.body).toHaveProperty('token');
expect(response.body.user.email).toBe('test@example.com');
});
it('should reject duplicate email', async () => {
// 先注册一次
await request(app)
.post('/api/auth/register')
.send({
email: 'duplicate@example.com',
password: 'Password123!'
});
// 再次注册相同邮箱
const response = await request(app)
.post('/api/auth/register')
.send({
email: 'duplicate@example.com',
password: 'Password123!'
});
expect(response.status).toBe(409);
expect(response.body).toHaveProperty('error');
});
it('should reject weak password', async () => {
const response = await request(app)
.post('/api/auth/register')
.send({
email: 'weak@example.com',
password: '123' // 太短,无大小写字母、数字、特殊字符
});
expect(response.status).toBe(400);
expect(response.body).toHaveProperty('error');
});
});
步骤 2:增量实现
// src/routes/auth.ts
import express from 'express';
import bcrypt from 'bcrypt';
import jwt from 'jsonwebtoken';
import { UserModel } from '../models/User';
import { validateEmail, validatePassword } from '../utils/validator';
const router = express.Router();
// 用户注册
router.post('/register', async (req, res) => {
try {
const { email, password } = req.body;
// 输入验证
if (!email || !password) {
return res.status(400).json({ error: 'Email and password are required' });
}
if (!validateEmail(email)) {
return res.status(400).json({ error: 'Invalid email format' });
}
if (!validatePassword(password)) {
return res.status(400).json({
error: 'Password must be at least 8 characters, include uppercase, lowercase, number and special character'
});
}
// 检查用户是否已存在
const existingUser = await UserModel.findByEmail(email);
if (existingUser) {
return res.status(409).json({ error: 'Email already exists' });
}
// 创建用户
const user = await UserModel.create(email, password);
// 生成 JWT token
const token = jwt.sign(
{ userId: user.id, email: user.email },
process.env.JWT_SECRET!,
{ expiresIn: '7d' }
);
res.status(201).json({
user: {
id: user.id,
email: user.email
},
token
});
} catch (error) {
console.error('Registration error:', error);
res.status(500).json({ error: 'Internal server error' });
}
});
export default router;
// src/utils/validator.ts
export function validateEmail(email: string): boolean {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(email);
}
export function validatePassword(password: string): boolean {
// 至少 8 位,包含大小写字母、数字、特殊字符
const passwordRegex = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/;
return passwordRegex.test(password);
}
检查点:
- 所有测试通过(
npm test) - 覆盖率 > 80%(
npm test -- --coverage) - 无 ESLint 错误(
npm run lint) - 可以通过 Postman 手动测试注册流程
退出条件:所有检查点通过,才能进入任务 4。
(由于篇幅限制,任务 4-8 的实现类似,遵循相同的 TDD + 增量实现 + 检查点模式)
步骤 4:VERIFY(验证)→ 使用 /test 命令
用户输入:
/test 运行完整测试套件,检查覆盖率和性能
AI 输出(被 test-driven-development 和 performance-benchmarking 技能约束):
# 运行测试
$ npm test
PASS tests/auth/register.test.ts
✓ should register a new user (45ms)
✓ should reject duplicate email (32ms)
✓ should reject weak password (12ms)
PASS tests/auth/login.test.ts
✓ should login with correct credentials (38ms)
✓ should reject incorrect password (28ms)
✓ should reject non-existent user (25ms)
PASS tests/auth/logout.test.ts
✓ should logout and invalidate token (41ms)
----------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
----------|---------|----------|---------|---------|-------------------
All files | 87.5 | 82.3 | 90.0 | 87.5 |
src | 87.5 | 82.3 | 90.0 | 87.5 |
----------|---------|----------|---------|---------|-------------------
Test Suites: 3 passed, 3 total
Tests: 9 passed, 9 total
Coverage: 87.5%
Time: 2.345s
性能测试:
// tests/performance/auth.perf.test.ts
import request from 'supertest';
import app from '../../src/app';
describe('Performance Test: POST /api/auth/login', () => {
it('should respond within 200ms (P95)', async () => {
const responseTimes: number[] = [];
// 发送 100 个并发请求
const requests = Array(100).fill(null).map(async () => {
const start = Date.now();
await request(app)
.post('/api/auth/login')
.send({
email: 'test@example.com',
password: 'Password123!'
});
const duration = Date.now() - start;
responseTimes.push(duration);
});
await Promise.all(requests);
// 计算 P95
responseTimes.sort((a, b) => a - b);
const p95 = responseTimes[Math.floor(responseTimes.length * 0.95)];
expect(p95).toBeLessThan(200); // P95 < 200ms
});
});
安全扫描:
# 检查依赖漏洞
$ npm audit
found 0 vulnerabilities
# 手动检查 SQL 注入
# 在代码中搜索字符串拼接
$ grep -r "SELECT.*+" src/
# 结果:0 处匹配(所有查询都使用参数化查询)
# 检查硬编码密钥
$ grep -r "secret" src/
# 结果:只找到 process.env.JWT_SECRET(正确使用环境变量)
检查点:
- 所有测试通过
- 覆盖率 > 80%
- P95 响应时间 < 200ms
- 无安全漏洞
退出条件:所有检查点通过,才能进入 REVIEW 阶段。
步骤 5:REVIEW(审查)→ 使用 /review 命令
用户输入:
/review 运行代码审查清单,检查代码质量
AI 输出(被 code-review-checklist 技能约束):
# Code Review Checklist
## 1. 代码风格
- [x] 符合 ESLint 规范(0 错误)
- [x] 命名约定一致(camelCase 变量、PascalCase 类)
- [x] 缩进风格统一(2 空格)
- [x] 无注释掉的代码
## 2. 逻辑正确性
- [x] 所有边界情况已处理(空输入、null、undefined)
- [x] 错误处理完善(try-catch、错误码)
- [x] 无无限循环、无死锁
## 3. 安全性
- [x] 无 SQL 注入(参数化查询)
- [x] 无 XSS(输出转义)
- [x] 无硬编码密钥(使用环境变量)
- [x] 密码已加密(bcrypt)
- [x] JWT 密钥安全存储
## 4. 性能
- [x] 数据库查询已优化(使用索引)
- [x] 无 N+1 查询问题
- [x] 响应时间 < 200ms(P95)
## 5. 可维护性
- [x] 代码有注释(关键函数)
- [x] 函数短小(< 50 行)
- [x] 无重复代码
- [x] 遵循单一职责原则
## 6. 测试
- [x] 覆盖率 > 80%
- [x] 包含所有边界情况
- [x] 测试可读性好
**发现问题**:
1. `src/routes/auth.ts` 中 `console.error` 应该在生产环境中使用专业日志库(如 Winston)
2. `src/models/User.ts` 中 `pool` 应该通过依赖注入传入,方便测试
**重构建议**:
1. 引入 Winston 日志库
2. 重构 UserModel 以接受 pool 作为参数
用户输入(重构):
/code-simplify 根据代码审查清单重构代码
AI 输出(被 refactor-without-changing-behavior 技能约束):
// src/models/User.ts(重构后)
import { Pool } from 'pg';
import bcrypt from 'bcrypt';
export class UserModel {
constructor(private pool: Pool) {} // 依赖注入
async create(email: string, password: string): Promise<User> {
// ... 实现不变
}
async findByEmail(email: string): Promise<User | null> {
// ... 实现不变
}
}
// src/routes/auth.ts(重构后)
import winston from 'winston';
const logger = winston.createLogger({
level: 'info',
format: winston.format.json(),
transports: [new winston.transports.Console()],
});
router.post('/register', async (req, res) => {
try {
// ... 实现不变
} catch (error) {
logger.error('Registration error:', error); // 使用 Winston
res.status(500).json({ error: 'Internal server error' });
}
});
检查点:
- 所有测试仍然通过(重构未改变行为)
- ESLint 0 错误
- 代码审查清单所有项通过
退出条件:所有检查点通过,才能进入 SHIP 阶段。
步骤 6:SHIP(发布)→ 使用 /ship 命令
用户输入:
/ship 部署到生产环境
AI 输出(被 continuous-integration-and-deployment 技能约束):
# .github/workflows/ci-cd.yml
name: CI/CD
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: '20'
- run: npm ci
- run: npm test
- run: npm run lint
deploy:
needs: test
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v3
- uses: akhileshns/heroku-deploy@v3.12.12
with:
heroku_api_key: ${{ secrets.HEROKU_API_KEY }}
heroku_app_name: ${{ secrets.HEROKU_APP_NAME }}
heroku_email: ${{ secrets.HEROKU_EMAIL }}
部署到 Heroku:
# 设置环境变量
heroku config:set JWT_SECRET=my-super-secret-jwt-key
heroku config:set DB_HOST=my-database-host
heroku config:set DB_PASSWORD=my-database-password
# 部署
git push heroku main
# 运行数据库 migration
heroku run npm run migrate
# 验证部署
curl https://my-app.herokuapp.com/health
# 输出:{"status":"ok"}
监控配置:
// src/config/monitoring.ts
import winston from 'winston';
import { expressWinston } = require('express-winston');
export const logger = winston.createLogger({
level: 'info',
format: winston.format.json(),
transports: [
new winston.transports.Console(),
new winston.transports.File({ filename: 'error.log', level: 'error' }),
new winston.transports.File({ filename: 'combined.log' })
]
});
// Express 中间件(记录所有请求)
app.use(expressWinston.logger({
winstonInstance: logger,
meta: true,
msg: "HTTP {{req.method}} {{req.url}}",
expressFormat: true,
colorize: false
}));
检查点:
- CI/CD 流水线通过
- 部署成功(健康检查通过)
- 监控已配置(Winston 日志)
- 环境变量已设置(JWT_SECRET 等)
退出条件:所有检查点通过,SHIP 阶段完成。
五、性能优化:Agent Skills 的「反模式」识别
5.1 常见性能反模式
Agent Skills 的 performance-benchmarking 技能内置了性能反模式识别器,可以自动检测以下常见问题:
反模式 1:N+1 查询
问题代码:
// 获取所有用户及其帖子
const users = await UserModel.findAll();
for (const user of users) {
const posts = await PostModel.findByUserId(user.id); // N+1 查询!
user.posts = posts;
}
AI 识别(被 performance-benchmarking 技能约束):
⚠️ 性能警告:检测到 N+1 查询问题
问题:在循环中执行数据库查询,导致 N+1 次数据库调用。
建议:使用 JOIN 或 DataLoader 批量查询。
重构后代码:
const users = await UserModel.findAllWithPosts(); // JOIN 查询
重构后:
// 使用 JOIN 一次性获取所有数据
const users = await pool.query(`
SELECT users.*, posts.*
FROM users
LEFT JOIN posts ON users.id = posts.user_id
`);
// 或者使用 DataLoader(GraphQL 场景)
const postsLoader = new DataLoader(async (userIds) => {
const posts = await PostModel.findByUserIds(userIds);
return userIds.map(id => posts.filter(post => post.user_id === id));
});
反模式 2:未使用索引
问题代码:
// 查询用户(通过 email)
const user = await pool.query(
`SELECT * FROM users WHERE email = $1`,
[email]
);
AI 识别:
⚠️ 性能警告:查询字段无索引
问题:email 字段无索引,导致全表扫描。
建议:添加索引。
修复:
CREATE INDEX idx_users_email ON users(email);
反模式 3:大文件一次性加载
问题代码:
// 读取大文件(> 100MB)
const data = fs.readFileSync('large-file.csv', 'utf-8');
AI 识别:
⚠️ 性能警告:一次性加载大文件,可能导致内存溢出。
建议:使用流(Stream)分块处理。
重构后代码:
const stream = fs.createReadStream('large-file.csv', { highWaterMark: 64 * 1024 });
stream.on('data', (chunk) => {
processChunk(chunk);
});
5.2 性能基准自动化
Agent Skills 可以自动运行性能基准测试,并在性能退化时报警:
// tests/benchmark/auth.bench.test.ts
import { benchmark } from 'benchmark';
describe('Benchmark: POST /api/auth/login', () => {
it('should handle 1000 requests per second', async () => {
const result = await benchmark(async () => {
await request(app)
.post('/api/auth/login')
.send({
email: 'test@example.com',
password: 'Password123!'
});
}, { maxTime: 10 });
expect(result.ops).toBeGreaterThan(1000); // 每秒至少 1000 次请求
});
});
六、总结与展望
6.1 Agent Skills 的核心价值
1. 让 AI 编程助手从「快手实习生」进化为「靠谱工程师」
没有 Agent Skills 时,AI 可能会:
- 跳过测试
- 忽略安全
- 过度工程
- 提交不可维护的代码
有了 Agent Skills 后,AI 必须:
- 先写测试,再写实现
- 通过安全扫描才能发布
- 小步重构,不改变外部行为
- 代码符合团队规范
2. 可复用、可定制、可扩展
- 可复用:20 个技能覆盖完整开发生命周期,适用于任何项目。
- 可定制:可以根据团队规范修改技能(如更严格的测试覆盖率要求)。
- 可扩展:可以编写自定义技能(如
blockchain-smart-contract-audit)。
3. 提高代码质量,降低维护成本
通过强制质量门控,Agent Skills 确保:
- 代码有测试,且测试覆盖率 > 80%
- 代码通过安全扫描,无常见漏洞
- 代码性能达标,无 N+1 查询等问题
- 代码符合规范,可读可维护
6.2 适用场景
推荐使用 Agent Skills 的场景:
- 团队协作:确保 AI 生成的代码符合团队规范。
- 生产级项目:对代码质量、安全性、性能有严格要求。
- 新手开发者:通过工作流学习「资深工程师的工作方法」。
- AI 编程助手重度用户:每天使用 Cursor、Claude Code 等工具。
不推荐使用 Agent Skills 的场景:
- 快速原型:如果只是为了验证想法,不需要严格的工作流。
- 非关键项目:side project 可以放松要求。
- AI 工具轻度用户:如果很少使用 AI 编程助手,学习成本可能不值得。
6.3 未来展望
1. 技能市场
未来可能会出现技能市场,开发者可以:
- 分享自定义技能(如
react-native-performance-optimization) - 下载社区贡献的技能(如
solidity-smart-contract-audit) - 对技能进行评分和评论
2. 与 IDE 深度集成
未来,Agent Skills 可能会:
- 集成到 VS Code、IntelliJ 等 IDE(作为插件)
- 提供可视化工作流编辑器(拖拽式配置技能)
- 实时显示当前阶段、检查点状态
3. 多 Agent 协作
未来,多个 AI 编程助手可以:
- 分别负责不同的技能(如一个负责测试,一个负责安全)
- 通过 Agent Skills 协调工作(类似人类团队的 code review 流程)
- 自动合并各自的工作成果
6.4 结语
Addy Osmani 的 Agent Skills 项目,本质上是在解决一个根本性问题:如何让 AI 编程助手不仅「聪明」,而且「严谨」。
通过结构化的工作流、强制的检查点、反理由化设计,Agent Skills 为 AI 编程助手注入了「工程纪律」。它让 AI 不再是一个「会找借口的实习生」,而是一个「靠谱的工程师」。
对于开发者来说,Agent Skills 不仅是一个工具,更是一种思维方式:
- 先思考,再动手(Think before coding)
- 测试先行(Test-driven development)
- 小步快跑(Small, incremental changes)
- 清晰胜过聪明(Clarity over cleverness)
在 AI 编程助手越来越普及的 2026 年,Agent Skills 这样的项目,将会成为生产级开发的标配。
参考资源
- Agent Skills GitHub 仓库:https://github.com/addyosmani/agent-skills
- Addy Osmani 博客:https://addyosmani.com/blog/
- Cursor 官方文档:https://cursor.sh/docs
- Claude Code 使用指南:https://console.anthropic.com/docs
- OWASP Top 10:https://owasp.org/Top10/
版权声明:本文为原创内容,基于 Addy Osmani 的 Agent Skills 项目进行深度解析,包含作者独立分析和代码示例。转载请注明出处。
关于作者:程序员茄子,全栈开发者,AI 编程工具深度用户,专注于 AI 辅助开发、软件工程最佳实践。
发布日期:2026-05-17
字数统计:约 12,000 字
阅读时间:约 25 分钟