AI 辅助开发工作流教程
📅 最后更新:2026年3月27日
本文档教你如何使用 AI 辅助开发工具,从需求到上线全流程覆盖。适合有基础编程能力但刚接触 AI 辅助开发的开发者。
核心目标:让 AI 成为你的得力助手,从”会用”到”用好”。
📖 目录
第一部分:快速入门
如果你是第一次使用 AI 辅助开发,请按以下顺序阅读:
第 1 步 → 阅读本章「核心概念」(约 5 分钟)
↓
第 2 步 → 尝试完成「第一个任务」(约 15 分钟)
↓
第 3 步 → 根据需要阅读工具章节
↓
第 4 步 → 选择适合的工作流程1. 你的第一个 AI 协作任务(5 分钟)
让我们从一个最简单的任务开始,体验 AI 辅助开发的完整流程。
1.1 启动 OpenCode
# 在项目目录下启动
cd your-project
opencode
# 或直接启动(会自动检测当前目录)
opencode预期输出:
OpenCode v1.x.x
Working directory: /path/to/your-project
Model: claude-3-5-sonnet
Ready for input...1.2 完成第一个任务
任务:让 AI 帮你解释项目的技术栈。
# 直接输入自然语言
"请解释一下这个项目的技术栈"AI 会做什么:
- 读取
package.json分析依赖 - 检查配置文件(
tsconfig.json、next.config.js等) - 扫描源代码结构
- 生成技术栈报告
预期结果:
这个项目使用以下技术栈:
框架:Next.js 15 (App Router)
语言:TypeScript 5.x
样式:Tailwind CSS v4
构建工具:Turbopack
...
主要依赖:
- react: 19.x
- next: 15.x
- typescript: 5.x
...1.3 完成第二个任务(代码修改)
任务:修改一个按钮的文案。
"请把登录页面的按钮文案从'确定'改成'立即登录'"AI 会做什么:
- 搜索包含”确定”按钮的文件
- 定位登录页面
- 修改文案
- 展示修改内容,等待你确认
预期结果:
找到文件:app/login/page.tsx
修改内容:
- <button>确定</button>
+ <button>立即登录</button>
是否确认修改?(y/n)1.4 验证修改
# 类型检查
pnpm typecheck
# 构建验证
pnpm build
# 手动验证
# 在浏览器中打开登录页面,确认文案已更新永远不要跳过验证!
AI 生成的代码可能有错误,必须验证后才能确认完成。
1.5 新手常见错误
| 错误 | 原因 | 解决方法 |
|---|---|---|
| 不知道说什么 | 担心说错 | 想说什么就说什么,AI 会追问澄清 |
| 描述太模糊 | AI 不知道要做什么 | 用示例补充,如”类似微信的登录页” |
| 直接接受 AI 的修改 | 没看就确认 | 养成先审查再确认的习惯 |
| 不验证就认为完成 | 代码可能有问题 | 每次都要运行 pnpm build 验证 |
2. 核心概念速览
2.1 什么是 TDD?
TDD(Test-Driven Development) = 测试驱动开发
简单说就是:先写测试,再写代码。
传统开发:写代码 → 写测试 → 发现 bug → 修 bug
(像做完题再对答案,发现错了再改)
TDD 开发:写测试 → 测试失败 → 写代码 → 测试通过 → 完成
(像先知道答案,做题时就知道对不对)为什么 TDD 更好?
- 测试失败 = 你知道要实现什么(答案告诉你还差什么)
- 测试通过 = 你知道功能完成了(答案告诉你做对了)
- 代码永远有测试保护(下次改代码不会改坏)
一个简单例子:
// 步骤 1:写失败的测试
test('登录成功后应该跳转到首页', async () => {
await login('user@example.com', 'password');
expect(window.location.pathname).toBe('/dashboard');
});
// 步骤 2:运行测试,确认失败
// Error: Expected '/dashboard' but got '/login'
// 步骤 3:写最小代码通过测试
async function login(email: string, password: string) {
// 最小实现
window.location.pathname = '/dashboard';
}
// 步骤 4:运行测试,确认通过
// ✓ 登录成功后应该跳转到首页
// 步骤 5:重构优化(测试必须保持通过)2.2 什么是 MCP?
MCP(Model Context Protocol) = AI 工具协议
简单说就是:让 AI 能够操作浏览器、调试器等工具的标准接口。
没有 MCP 时:
你:帮我测试登录功能
AI:好的,你手动打开浏览器,输入用户名密码,点击登录按钮...
有 MCP 后:
你:帮我测试登录功能
AI:好的,我已经打开浏览器,输入了测试账号,点击了登录,验证通过。你什么时候需要 MCP?
| 你的需求 | 需要哪个 MCP 工具 | 必要性 |
|---|---|---|
| 让 AI 帮我点按钮、填表单 | Playwright MCP | 开发 Web 应用时很有用 |
| 让 AI 帮我分析页面性能 | Chrome DevTools MCP | 优化性能时需要 |
| 让 AI 测试已登录的功能 | Real Browser MCP | 测试后台功能时需要 |
| 只是想让 AI 写代码 | 不需要 MCP | 直接让 AI 写就行 |
2.3 什么是人/AI 协同?
核心原则:人做决策,AI 做执行
为什么需要协同?AI 不是什么都能做吗?
不是的。AI 有三大局限:
- 不懂业务价值:AI 不知道”登录失败 3 次锁定账户”是否符合你的业务需求
- 不懂风险判断:AI 不知道这段代码如果出错会造成多大损失
- 代码可能有问题:45% 的 AI 代码有安全漏洞
所以分工是这样的:
| 你负责(决策) | AI 负责(执行) |
|---|---|
| 决定”做什么” | 执行”怎么做” |
| 定义验收标准 | 生成测试用例 |
| 审查代码安全性 | 写代码、修 bug |
| 最终验收确认 | 生成文档、报告 |
一个例子:
你:我要一个登录功能,支持手机号登录,失败 3 次锁定
AI:好的,我来写代码
AI:代码写好了,请审查
你:审查通过,运行测试
AI:测试通过了
你:我在浏览器里试试...可以!确认完成永远不要让 AI 决策关键事项!
AI 不理解业务价值、法律风险、公司政策。安全相关的代码必须人工审查。
2.4 什么是 OpenSpec?
OpenSpec = 规格驱动开发工具
简单说就是:先写清楚要做什么,再让 AI 实现。
传统方式:
你:做一个登录功能
AI:[直接写代码]
你:不对,我要的是手机号登录
AI:[改代码]
你:还要失败 3 次锁定
AI:[再改代码]
...(反复返工)
OpenSpec 方式:
你:做一个登录功能
AI:[生成规格文档]
- 支持手机号登录
- 失败 3 次锁定
- 锁定后 15 分钟解锁
你:确认规格
AI:[按规格实现]
你:验收通过OpenSpec 四阶段:
3. 适用场景判断
**不是所有项目都需要完整流程。**使用前先判断:
3.1 快速判断表
问:你的项目是否需要完整流程?
├─ 纯静态文档站点(无业务逻辑)
│ └─ ❌ 不适用 → 使用简化版流程
│
├─ 原型验证阶段
│ └─ ❌ 不适用 → 快速迭代优先,事后再补充测试
│
├─ 核心业务逻辑开发
│ └─ ✅ 适用 → 重点使用 TDD 章节
│
├─ 高风险模块(支付、认证等)
│ └─ ✅ 适用 → 必须使用完整流程
│
└─ 复杂应用、团队协作项目
└─ ✅ 适用 → 完整流程3.2 任务复杂度判断
| 任务类型 | 特征 | 推荐工作流 | 时间投入 |
|---|---|---|---|
| 简单 | 单文件、明确目标、不涉及架构 | One-Shot 模式 | ~1 分钟 |
| 中等 | 多文件、需理解上下文、有验收标准 | Plan Mode | ~2-3 分钟 |
| 复杂 | 核心功能、业务逻辑、需高质量保证 | TDD + AI | ~5-10 分钟 |
| 超复杂 | 大型功能、团队协作、长期维护 | Spec-Driven | ~10-30 分钟 |
3.3 何时不适用本文档
| 场景 | 原因 | 替代方案 |
|---|---|---|
| 纯静态文档站点 | 无业务逻辑,无测试需求 | 简化版流程 |
| 原型验证阶段 | 快速迭代优先 | Vibe Coding + 事后重构 |
| 一次性脚本 | 无长期维护需求 | One-Shot 模式 |
| 配置文件修改 | 简单直接 | One-Shot 模式 |
第二部分:工具生态全景
本章介绍 AI 辅助开发的完整工具生态,帮助你理解每个工具的作用和使用场景。
4. OpenCode 入门
4.1 什么是 OpenCode?
OpenCode 是一个 AI 辅助开发的命令行工具,提供:
- 自然语言交互
- 代码生成与修改
- 多代理编排
- 技能系统
- MCP 工具集成
4.2 安装与配置
安装
# 使用 npm 安装
npm install -g @anthropic/opencode
# 或使用 pnpm
pnpm add -g @anthropic/opencode
# 验证安装
opencode --version配置文件
OpenCode 使用 opencode.json 配置文件:
// .ai/.config/opencode/opencode.json
{
"model": "claude-3-5-sonnet",
"maxTokens": 8192,
"temperature": 0.7,
"mcp": {
// MCP 工具配置
},
"skills": [
// 技能配置
]
}API 订阅推荐
根据官方推荐,以下订阅可良好运行:
| 订阅 | 价格 | 推荐指数 |
|---|---|---|
| ChatGPT Subscription | $20/月 | ⭐⭐⭐⭐⭐ |
| GLM Coding Plan | $10/月 | ⭐⭐⭐⭐ |
4.3 核心命令速查
| 命令 | 用途 | 示例 |
|---|---|---|
opencode | 启动交互模式 | opencode |
opencode "prompt" | 单次执行 | opencode "解释这个函数" |
opencode --help | 查看帮助 | opencode --help |
/clear | 清理会话 | /clear |
/exit | 退出 | /exit |
4.4 代理系统概览
OpenCode 内置多个专业代理,自动路由到最优模型:
核心代理
| 代理 | 用途 | 何时使用 |
|---|---|---|
| sisyphus | 主指挥官 | 默认入口,编排所有代理 |
| explore | 代码搜索 | 查找代码模式、理解结构 |
| librarian | 文档查阅 | 外部库文档、API 参考 |
| oracle | 架构咨询 | 复杂问题、架构决策 |
| prometheus | 规划师 | 面试式战略规划 |
其他代理
| 代理 | 用途 |
|---|---|
atlas | 通用代理 |
metis | 需求分析 |
momus | 计划评审 |
hephaestus | 深度工作者 |
4.5 Category 委派系统
当 Sisyphus 委派子任务时,选择 category,系统自动路由到最优模型:
| Category | 适用场景 | 示例 |
|---|---|---|
visual-engineering | UI、样式、动画、布局 | 重新设计侧边栏 |
ultrabrain | 复杂逻辑、架构决策、算法 | 实现缓存淘汰算法 |
deep | 自主研究 + 端到端实现 | 完整功能开发 |
artistry | 非常规问题、创意方法 | 创新解决方案 |
quick | 单文件修改、简单配置 | 修复 typo |
writing | 文档、技术写作 | 编写 API 文档 |
4.6 代理调用语法
// 方式 1: 使用 category(推荐用于任务委派)
task(
category="quick", // 必需:任务类别
load_skills=[], // 必需:技能数组
description="Fix type error", // 必需:短描述
prompt="Fix the TypeScript error in src/utils.ts line 42...", // 必需:完整提示词
run_in_background=false // 必需:false=任务委派,true=并行探索
)
// 方式 2: 使用 subagent_type(直接调用专门代理)
task(
subagent_type="explore", // 指定代理类型
load_skills=[],
description="Find auth patterns",
prompt="搜索认证实现模式...",
run_in_background=true // 并行探索时用 true
)
// 方式 3: 继续之前的 session
task(
session_id="ses_abc123", // 上次返回的 session_id
load_skills=[],
description="Continue auth task",
prompt="继续上一个任务...",
run_in_background=false
)参数说明:
| 参数 | 必需 | 说明 |
|---|---|---|
category 或 subagent_type | 二选一 | category 自动路由模型,subagent_type 直接指定代理 |
load_skills | 是 | 技能名称数组,空数组 [] 表示不加载 |
description | 是 | 短描述(3-5词) |
prompt | 是 | 完整详细提示词 |
run_in_background | 是 | false=任务委派,true=并行探索 |
session_id | 否 | 继续之前的 session 时提供 |
5. OpenSpec 工作流
5.1 什么是 OpenSpec?
OpenSpec 是一个规格驱动开发工具,帮助你:
- 明确需求边界
- 生成可追溯的规格文档
- 分解任务
- 归档资产
5.2 四阶段流程
5.3 Propose(创建规格)
命令:/opsx-propose 或 /openspec-propose
适用场景:明确需求,需要生成规格文档。
操作步骤:
# 方式 1: 指定变更名称
/opsx-propose add-user-login
# 方式 2: 直接描述需求,AI 自动生成名称
"我要添加一个用户登录模块,支持手机号和邮箱登录"输出物:
openspec/changes/add-user-login/
├── proposal.md # 提案(做什么、为什么)
├── design.md # 设计(怎么做)
└── tasks.md # 任务清单proposal.md 示例:
# 用户登录模块
## 概述
添加用户登录功能,支持手机号和邮箱登录。
## 验收标准
- [ ] 手机号登录成功
- [ ] 邮箱登录成功
- [ ] 失败 3 次锁定账户 15 分钟
- [ ] 锁定后提示用户
## 禁止事项
- 不修改现有注册逻辑
- 不添加第三方登录
## 非目标
- 不包含密码重置功能
- 不包含多因素认证人确认要点:
- 验收标准是否完整
- 禁止事项是否明确
- 任务拆分是否合理
5.4 Explore(需求探索)
命令:/opsx-explore 或 /openspec-explore
适用场景:模糊想法,需要深入探索需求边界。
操作步骤:
# 进入探索模式
/opsx-explore
# 对话式探索
"我需要一个用户登录模块"
"支持手机号和邮箱登录"
"失败 3 次锁定账户"
# AI 会追问:
# - 手机号格式是哪国?
# - 锁定后如何解锁?
# - 是否需要验证码?探索模式特点:
- 不写代码,只思考
- 追问澄清需求
- 发现潜在风险
- 生成需求文档
何时退出探索模式:
- 需求边界清晰
- 验收标准明确
- 准备开始实现
5.5 Apply(执行实现)
命令:/opsx-apply 或 /openspec-apply-change
适用场景:规格确认后,执行实现。
操作步骤:
# 标准执行
/opsx-apply add-user-login
# 全功率执行(复杂任务)
ultrawork
# 或
/ulw-loop执行模式对比:
| 模式 | 适用场景 | 特点 |
|---|---|---|
| 标准执行 | 单一任务、简单需求 | 按任务顺序执行 |
| 全功率执行 | 复杂需求、多任务并行 | 并行启动多个子代理 |
全功率执行流程:
5.6 Archive(归档保存)
命令:/opsx-archive 或 /openspec-archive-change
适用场景:实现完成,归档所有资产。
操作步骤:
/opsx-archive add-user-login
# 输出:
# 归档到 openspec/changes/archive/2026-03-27-add-user-login/归档内容:
openspec/changes/archive/2026-03-27-add-user-login/
├── proposal.md
├── design.md
├── tasks.md
├── code/ # 代码快照
└── tests/ # 测试快照5.7 命令别名速查
| 完整命令 | 简写 |
|---|---|
/openspec-propose | /opsx-propose |
/openspec-explore | /opsx-explore |
/openspec-apply-change | /opsx-apply |
/openspec-archive-change | /opsx-archive |
6. Superpowers 核心技能
6.1 什么是 Superpowers?
Superpowers 是一套经过验证的 AI 辅助开发技能,帮助你:
- 系统化地完成任务
- 避免常见陷阱
- 提高代码质量
6.2 核心技能概览
6.3 brainstorming(创意探索)
触发条件:创建功能、构建组件、添加功能、修改行为之前。
核心原则:先探索意图和需求,再实现。
使用方式:
# 方式 1: 自动触发(推荐)
# AI 会根据任务自动判断是否需要 brainstorming
# 方式 2: 手动调用
skill(name="brainstorming")工作流程:
实际示例:
用户: 我要做一个用户登录模块
AI (brainstorming 模式):
让我先理解你的需求...
1. 登录方式:
- 手机号登录?
- 邮箱登录?
- 第三方登录(微信、GitHub)?
2. 安全要求:
- 失败几次锁定?
- 锁定多久?
- 如何解锁?
3. 用户体验:
- 记住登录状态?
- 自动填充上次账号?
请告诉我你的具体需求...何时跳过 brainstorming:
- 简单 bug 修复
- 明确的小改动
- 紧急修复
6.4 test-driven-development(TDD)
触发条件:实现功能或修复 bug 之前。
核心原则:先写测试,再写代码。
使用方式:
skill(name="test-driven-development")RED-GREEN-REFACTOR 循环:
阶段 1: RED(写失败测试)
// tests/auth/login.spec.ts
import { test, expect } from '@playwright/test';
test('用户应该能够成功登录', async ({ page }) => {
// Given: 用户在登录页
await page.goto('/login');
// When: 填写正确的凭据并提交
await page.getByLabel('邮箱').fill('test@example.com');
await page.getByLabel('密码').fill('password123');
await page.getByRole('button', { name: '登录' }).click();
// Then: 应该跳转到仪表板
await expect(page).toHaveURL('/dashboard');
});验证测试失败:
npx playwright test tests/auth/login.spec.ts
# 期望输出:Error: page.waitForURL: Timeout
# (说明测试正确地失败了,因为功能还没实现)常见错误:测试失败是因为语法错误,而不是功能缺失。
一定要确认失败原因是”功能不存在”,而不是”代码写错了”。
阶段 2: GREEN(最小实现)
// app/login/page.tsx
'use client';
import { useRouter } from 'next/navigation';
import { FormEvent } from 'react';
export default function LoginPage() {
const router = useRouter();
const handleSubmit = async (e: FormEvent) => {
e.preventDefault();
// 最小实现:直接跳转(后续再实现真正的认证逻辑)
router.push('/dashboard');
};
return (
<form onSubmit={handleSubmit}>
<input aria-label="邮箱" type="email" name="email" />
<input aria-label="密码" type="password" name="password" />
<button type="submit">登录</button>
</form>
);
}验证测试通过:
npx playwright test tests/auth/login.spec.ts
# 期望:1 passed阶段 3: REFACTOR(重构优化)
// 重构内容:
// 1. 提取表单组件
// 2. 添加表单验证
// 3. 实现真正的认证逻辑
// 4. 添加错误处理
// 每次重构后都要运行测试,确保不破坏功能
npx playwright test tests/auth/login.spec.ts
# 期望:1 passed何时使用 TDD:
✅ 推荐使用:
- 核心业务逻辑
- 支付、认证等高风险模块
- 复杂算法实现
- 需要长期维护的代码
❌ 可跳过:
- UI/UX 密集型代码
- 快速原型验证
- 一次性脚本
- 配置文件修改6.5 systematic-debugging(系统化调试)
触发条件:遇到 bug、测试失败、意外行为时。
核心原则:先定位问题,再修复。
使用方式:
skill(name="systematic-debugging")调试流程:
实际示例:
用户: 登录失败 3 次没有锁定账户
AI (systematic-debugging 模式):
让我系统化地调试这个问题...
步骤 1: 复现问题
- 输入错误密码 3 次
- 观察账户状态
- 结果:账户未被锁定
步骤 2: 检查相关代码
- 查找登录逻辑:src/auth/login.ts
- 查找锁定逻辑:src/auth/lockout.ts
步骤 3: 定位问题
- 发现:lockout.ts 中的计数器没有正确递增
- 原因:使用了错误的变量名
步骤 4: 验证假设
- 修复变量名
- 运行测试
步骤 5: 确认修复
- 测试通过
- 手动验证:账户正确锁定6.6 verification-before-completion(完成验证)
触发条件:声称工作完成、修复完成、测试通过之前。
核心原则:无证据不完成。
使用方式:
skill(name="verification-before-completion")强制规则:
⚠️ 无验证证据 = 未完成
必须验证:
1. 类型检查:pnpm typecheck
2. 构建验证:pnpm build
3. 代码检查:pnpm lint
4. 测试通过:pnpm test
5. 功能手动验证(最重要)验证清单:
- [ ] 代码通过 lint 检查
- [ ] TypeScript 编译无错误
- [ ] 构建成功
- [ ] 测试全部通过
- [ ] 功能手动验证过
- [ ] 边界情况考虑过
- [ ] 安全风险审查过实际示例:
AI: 登录功能已完成
你: 请运行验证
AI:
运行 pnpm typecheck... ✓ 通过
运行 pnpm build... ✓ 通过
运行 pnpm lint... ✓ 通过
运行 pnpm test... ✓ 8 passed
验证结果:
- 类型检查:通过
- 构建:成功
- Lint:无错误
- 测试:8 passed
请手动验证:
1. 在浏览器中测试登录功能
2. 测试失败 3 次锁定
3. 测试锁定后解锁
你: 手动验证通过,确认完成6.7 其他重要技能
writing-plans(编写计划)
触发条件:有规格或需求,需要多步骤任务,在写代码之前。
使用方式:
skill(name="writing-plans")输出:详细的实现计划,包含步骤、验证点、风险。
executing-plans(执行计划)
触发条件:有书面实现计划,需要在独立会话中执行。
使用方式:
skill(name="executing-plans")特点:按计划执行,有检查点,可恢复。
requesting-code-review(请求审查)
触发条件:完成任务、实现主要功能、合并前。
使用方式:
skill(name="requesting-code-review")receiving-code-review(接收审查)
触发条件:收到代码审查反馈,实施建议之前。
使用方式:
skill(name="receiving-code-review")7. MCP 工具集成
**MCP 工具需要手动安装和配置。**以下工具默认未启用。
重要:请全局安装 MCP 工具(npm install -g),避免使用 npx 方式启动。npx 每次运行都可能会临时下载包,受 npm 自动清理机制或缓存有效期机制影响,导致启动延迟和网络依赖。
7.1 MCP 工具概览
| 工具 | 核心能力 | 适用场景 | 是否默认启用 |
|---|---|---|---|
| Playwright MCP | 浏览器自动化、代码生成 | 新功能开发、E2E 测试 | ❌ 需手动启用 |
| Chrome DevTools MCP | 性能分析、网络调试 | 性能优化、问题诊断 | ❌ 需手动启用 |
| Real Browser MCP | 真实浏览器、已登录会话 | 已登录场景、真实环境验证 | ❌ 需手动启用 |
7.2 安装步骤
# 1. 全局安装 MCP 工具
npm install -g @playwright/mcp@latest
npm install -g chrome-devtools-mcp
npm install -g real-browser-mcp
# 2. 修改配置文件
# 编辑 .ai/.config/opencode/opencode.json
# 在 "mcp" 对象中添加配置(见下文)7.3 Playwright MCP 配置
// opencode.json
{
"mcp": {
"playwright-mcp": {
"command": ["playwright-mcp", "--cdp-endpoint", "http://localhost:9222"],
"enabled": false,
"type": "local"
}
}
}使用示例:
# 启动开发服务器
pnpm dev
# 启用 Playwright MCP(修改配置:enabled: true)
# 自然语言指令
"打开 http://localhost:3000/login,填写测试账号并提交"
"验证登录成功后跳转到 /dashboard"Playwright AI Agents:
Playwright v1.56+ 提供三个内置 AI Agent:
| Agent | 用途 | 命令 |
|---|---|---|
| @planner | 生成测试计划 | @planner 为登录流程生成测试计划 |
| @generator | 生成测试代码 | @generator 将测试计划转换为测试 |
| @healer | 修复失败测试 | @healer 修复失败的测试 |
7.4 Chrome DevTools MCP 配置
// opencode.json
{
"mcp": {
"chrome-devtools-mcp": {
"command": ["chrome-devtools-mcp", "-u", "http://localhost:9222"],
"enabled": false,
"type": "local"
}
}
}启动方式:
# 启动 Chrome 并开启调试端口
google-chrome --remote-debugging-port=9222
# 或使用快捷脚本
pnpm chrome:debug使用示例:
# 性能分析
"在 localhost:3000/products 运行性能追踪"
"报告 LCP、CLS、INP 指标并提供改进建议"
# 网络调试
"检查 localhost:3000/api/users 的网络请求"
"分析请求耗时和响应数据"
# 控制台调试
"捕获 localhost:3000 的控制台错误"
"分析错误堆栈并提供修复建议"7.5 Real Browser MCP 配置
// opencode.json
{
"mcp": {
"real-browser-mcp": {
"command": ["real-browser-mcp"],
"enabled": false,
"type": "local",
"environment": {
"PORT": "7225"
}
}
}
}使用场景:
- 已登录的 Web 应用测试
- 需要真实浏览器环境的操作
- 避免反爬检测
启动方式:
# 默认:headless 模式,端口 9851
scripts/real_browser.sh
# 可见窗口模式
scripts/real_browser.sh --headed
# 自定义端口
scripts/real_browser.sh 9888
# 保留登录状态(更快启动)
scripts/real_browser.sh --keep-profile登录状态说明:
| 站点类型 | 示例 | 登录状态 |
|---|---|---|
| 普通站点 | X, Reddit, GitHub, 知乎 | ✅ 保留 |
| 高安全站点 | Google, Claude, ChatGPT | ⚠️ 可能登出 |
7.6 工具选择决策树
问:需要什么类型的浏览器自动化?
│
├─ 开发新功能 / E2E 测试
│ └─ 使用 Playwright MCP
│
├─ 性能分析 / 问题诊断
│ └─ 使用 Chrome DevTools MCP
│
├─ 已登录场景 / 真实环境
│ └─ 使用 Real Browser MCP
│
└─ 复杂场景(组合使用)
└─ Playwright MCP (开发) + Chrome DevTools MCP (调试)7.7 组合使用方案
方案 A: 开发到验证全流程
# 阶段 1: 开发(Playwright MCP)
@planner 为用户注册流程生成测试计划
@generator 将测试计划转换为测试代码
# 阶段 2: 实现
# AI 生成代码,人审查并调整
# 阶段 3: 验证(Playwright MCP)
npx playwright test
# 阶段 4: 性能检查(Chrome DevTools MCP)
"在注册页面运行性能追踪"
"确保 LCP < 2.5s"
# 阶段 5: 真实环境验证(Real Browser MCP)
"使用已登录账号验证注册流程"方案 B: Bug 修复流程
# 阶段 1: 复现(Chrome DevTools MCP)
"捕获 localhost:3000/dashboard 的控制台错误"
"分析错误堆栈"
# 阶段 2: 定位
# 人根据错误信息定位代码位置
# 阶段 3: TDD 修复
# 写失败的测试复现 bug
# 修复代码
# 确认测试通过
# 阶段 4: 验证(Playwright MCP)
npx playwright test第三部分:项目特定技能
本章介绍项目中的特定技能,帮助你更高效地完成各类任务。
8. 浏览器与调试技能
8.1 real-browser(登录状态测试)
描述:启动带有登录状态的真实 Chrome,暴露 CDP 端口。
触发条件:
- 测试需要登录的功能
- 需要真实浏览器环境
- 避免反爬检测
使用方式:
# 加载技能
skill(name="real-browser")
# 同时需要加载 agent-browser 技能
skill(name="agent-browser")工作流程:
启动命令:
# 默认:headless 模式,端口 9851
scripts/real_browser.sh
# 可见窗口模式
scripts/real_browser.sh --headed
# 自定义端口
scripts/real_browser.sh 9888
# 保留登录状态
scripts/real_browser.sh --keep-profile实际示例:
# 1. 启动浏览器
scripts/real_browser.sh
# 2. 使用 agent-browser 操作
agent-browser --cdp 9851 open https://x.com
agent-browser --cdp 9851 wait --load networkidle
agent-browser --cdp 9851 snapshot -i
# 3. 点击元素
agent-browser --cdp 9851 click @e3
# 4. 填写表单
agent-browser --cdp 9851 fill @e5 "hello@world.com"环境变量:
| 变量 | 用途 | 示例 |
|---|---|---|
REAL_BROWSER_BIN | 覆盖 Chrome 路径 | REAL_BROWSER_BIN=/usr/bin/brave-browser |
REAL_BROWSER_EXTRA_ARGS | 额外参数 | REAL_BROWSER_EXTRA_ARGS="--no-sandbox" |
REAL_BROWSER_LOG_LEVEL | 日志级别 | REAL_BROWSER_LOG_LEVEL=debug |
注意事项:
--cdp <port>必须在每个 agent-browser 命令中open后必须wait --load networkidle- 交互前必须
snapshot -i获取最新引用 - 不要混用
chrome-devtools和agent-browser
8.2 chrome-devtools(性能分析)
描述:使用 Chrome DevTools 进行调试、性能分析、浏览器自动化。
触发条件:
- 调试 Web 页面
- 分析性能
- 检查网络请求
使用方式:
skill(name="chrome-devtools")核心概念:
- 浏览器生命周期:首次调用工具时自动启动
- 页面选择:使用
list_pages和select_page切换 - 元素交互:使用
take_snapshot获取元素uid
工作流程:
实际示例:
# 1. 导航到页面
"打开 https://example.com"
# 2. 等待内容加载
"等待页面加载完成"
# 3. 获取页面快照
"获取页面结构"
# 4. 性能分析
"运行性能追踪"
"报告 LCP、CLS、INP 指标"
# 5. 网络调试
"检查网络请求"
"分析请求耗时"工具选择:
| 场景 | 推荐工具 |
|---|---|
| 自动化/交互 | take_snapshot(文本化,更快) |
| 视觉检查 | take_screenshot(需要看视觉状态) |
| 获取额外数据 | evaluate_script(执行 JS) |
故障排除:
如果 chrome-devtools-mcp 不足,引导用户使用 Chrome DevTools UI:
8.3 web-design-reviewer(设计审查)
描述:视觉检查网站设计,识别并修复设计问题。
触发条件:
- “review website design”
- “check the UI”
- “fix the layout”
- “find design problems”
使用方式:
skill(name="web-design-reviewer")适用范围:
- 静态站点(HTML/CSS/JS)
- SPA 框架(React/Vue/Angular/Svelte)
- 全栈框架(Next.js/Nuxt/SvelteKit)
- CMS 平台(WordPress/Drupal)
工作流程:
检查项目:
布局问题
| 问题 | 描述 | 严重性 |
|---|---|---|
| 元素溢出 | 内容溢出父元素或视口 | 高 |
| 元素重叠 | 非预期的元素重叠 | 高 |
| 对齐问题 | Grid 或 Flex 对齐问题 | 中 |
| 间距不一致 | Padding/Margin 不一致 | 中 |
响应式问题
| 问题 | 描述 | 严重性 |
|---|---|---|
| 非移动友好 | 小屏幕布局崩溃 | 高 |
| 断点问题 | 屏幕尺寸变化时过渡不自然 | 中 |
| 触摸目标 | 移动端按钮太小 | 中 |
可访问性问题
| 问题 | 描述 | 严重性 |
|---|---|---|
| 对比度不足 | 文本与背景对比度低 | 高 |
| 无焦点状态 | 键盘导航时无法确定状态 | 高 |
| 缺少 alt 文本 | 图片无替代文本 | 中 |
视口测试:
| 名称 | 宽度 | 代表设备 |
|---|---|---|
| Mobile | 375px | iPhone SE/12 mini |
| Tablet | 768px | iPad |
| Desktop | 1280px | 标准 PC |
| Wide | 1920px | 大显示器 |
实际示例:
# 1. 提供 URL
用户: 请审查 http://localhost:3000 的设计
AI:
开始设计审查...
## 信息收集
- 框架:Next.js 15
- 样式:Tailwind CSS
- 检查范围:首页
## 视觉检查
正在检查桌面视图 (1280px)...
正在检查移动视图 (375px)...
## 发现问题
### [P1] 按钮溢出
- 页面:首页
- 元素:.cta-button
- 问题:按钮在小屏幕上溢出容器
- 修复文件:app/page.tsx
### [P2] 对比度不足
- 页面:首页
- 元素:.description
- 问题:文本对比度 3.2:1,低于 WCAG AA 标准 4.5:1
- 修复文件:app/globals.css
## 修复中...
✓ 按钮溢出已修复
✓ 对比度已修复
## 重新验证
✓ 桌面视图正常
✓ 移动视图正常
## 完成报告
- 检测问题:2
- 修复问题:2
- 未修复:0最佳实践:
- ✅ 修复前保存截图
- ✅ 一次修复一个问题并验证
- ✅ 遵循项目现有代码风格
- ❌ 不要一次修复多个问题
- ❌ 不要忽略设计系统或品牌指南
9. 代码质量技能
9.1 code-simplifier(代码简化)
描述:简化和优化代码,提高清晰度、一致性和可维护性,同时保留所有功能。
触发条件:
- 代码写完后
- 代码审查时
- 重构时
使用方式:
skill(name="code-simplifier")核心原则:
- 保留功能:永远不改变代码做什么,只改变怎么做
- 应用项目标准:遵循 CLAUDE.md 中的编码标准
- 增强清晰度:减少不必要的复杂度和嵌套
- 保持平衡:避免过度简化
简化内容:
| 简化类型 | 说明 |
|---|---|
| 减少嵌套 | 消除不必要的 if-else 嵌套 |
| 消除冗余 | 移除重复代码和抽象 |
| 改进命名 | 使用清晰的变量和函数名 |
| 合并逻辑 | 整合相关逻辑 |
| 移除无用注释 | 删除描述明显代码的注释 |
实际示例:
简化前:
// 检查用户是否可以访问
function checkAccess(user: User, resource: Resource): boolean {
let canAccess = false;
if (user !== null) {
if (user.isActive === true) {
if (resource.isPublic === true) {
canAccess = true;
} else {
if (user.role === 'admin') {
canAccess = true;
} else {
if (user.permissions.includes(resource.requiredPermission)) {
canAccess = true;
} else {
canAccess = false;
}
}
}
} else {
canAccess = false;
}
} else {
canAccess = false;
}
return canAccess;
}简化后:
function checkAccess(user: User | null, resource: Resource): boolean {
if (!user?.isActive) return false;
if (resource.isPublic) return true;
if (user.role === 'admin') return true;
return user.permissions.includes(resource.requiredPermission);
}注意事项:
- ❌ 避免嵌套三元运算符,使用 switch 或 if/else
- ❌ 不要为了”更少行数”牺牲可读性
- ✅ 选择清晰而非简洁
- ✅ 显式代码通常比过度紧凑的代码更好
9.2 review-and-refactor(审查重构)
描述:根据项目编码规范审查和重构代码。
触发条件:
- 代码审查
- 重构需求
- 代码质量检查
使用方式:
skill(name="review-and-refactor")工作流程:
- 读取
.github/instructions/*.md和.github/copilot-instructions.md中的编码规范 - 审查所有代码
- 根据规范进行重构
- 确保测试仍然通过
审查要点:
| 检查项 | 说明 |
|---|---|
| 代码风格 | 是否符合项目规范 |
| 命名规范 | 变量、函数、组件命名是否清晰 |
| 代码结构 | 是否有重复、冗余代码 |
| 最佳实践 | 是否遵循项目最佳实践 |
| 测试覆盖 | 测试是否仍然通过 |
实际示例:
用户: 请审查并重构 src/auth/ 目录下的代码
AI:
正在审查代码...
## 审查结果
### 发现问题
1. **命名不规范**
- 文件:src/auth/login.ts
- 问题:函数名 `doLogin` 不符合 camelCase 规范
- 建议:改为 `login`
2. **重复代码**
- 文件:src/auth/login.ts, src/auth/register.ts
- 问题:验证逻辑重复
- 建议:提取到共享函数
3. **缺少类型注解**
- 文件:src/auth/utils.ts
- 问题:函数缺少返回类型
- 建议:添加显式类型注解
## 重构中...
✓ 重命名 doLogin → login
✓ 提取共享验证函数到 src/auth/validation.ts
✓ 添加类型注解
## 验证
运行 pnpm test... ✓ 通过
运行 pnpm typecheck... ✓ 通过
重构完成!10. 前端开发技能
10.1 frontend-design(前端设计)
描述:创建高质量、生产级的前端界面。
触发条件:
- 构建 Web 组件
- 创建页面
- 设计 UI
- 样式美化
使用方式:
skill(name="frontend-design")设计原则:
- 高设计质量
- 避免”AI 美学”(通用、无特色的设计)
- 创意、精致的代码和 UI 设计
实际示例:
用户: 创建一个登录页面
AI (frontend-design 模式):
我将创建一个现代、精致的登录页面...
[生成代码]
- 独特的视觉风格
- 精心设计的交互
- 响应式布局
- 可访问性支持10.2 shadcn-ui(组件库)
描述:提供 shadcn/ui 组件库模式,包括安装、配置和实现。
触发条件:
- 设置 shadcn/ui
- 安装组件
- 构建表单(React Hook Form + Zod)
- 自定义主题
使用方式:
skill(name="shadcn-ui")核心功能:
| 功能 | 说明 |
|---|---|
| 安装配置 | 初始化 shadcn/ui |
| 组件安装 | 添加 Button、Dialog、Dropdown 等 |
| 表单构建 | React Hook Form + Zod 验证 |
| 主题定制 | Tailwind CSS 自定义 |
实际示例:
# 初始化
npx shadcn@latest init
# 安装组件
npx shadcn@latest add button
npx shadcn@latest add dialog
npx shadcn@latest add form
# 使用组件
import { Button } from "@/components/ui/button"
import { Dialog, DialogContent, DialogTrigger } from "@/components/ui/dialog"10.3 tailwind-design-system(设计系统)
描述:使用 Tailwind CSS v4 构建可扩展的设计系统。
触发条件:
- 创建组件库
- 实现设计系统
- 标准化 UI 模式
使用方式:
skill(name="tailwind-design-system")核心概念:
- 设计令牌(Design Tokens)
- 组件库
- 响应式模式
实际示例:
/* globals.css */
@theme {
--color-primary: oklch(0.6 0.2 250);
--color-secondary: oklch(0.7 0.15 180);
--font-sans: "Inter", system-ui, sans-serif;
--spacing-section: 4rem;
--radius-card: 1rem;
}10.4 ahooks(React Hooks)
描述:全面的 ahooks React Hooks 库专家,覆盖 76+ 个 Hooks。
触发条件:
- 使用 ahooks 库
- 需要 React Hooks 工具
- 学习最佳实践
使用方式:
skill(name="ahooks")核心 Hooks 分类:
| 分类 | 示例 Hooks |
|---|---|
| State | useRequest, useToggle, useBoolean |
| Effect | useDebounce, useThrottle, useUpdateEffect |
| DOM | useSize, useScroll, useMouse |
| Advanced | useCreation, usePersistFn |
实际示例:
import { useRequest, useDebounce } from 'ahooks';
function SearchComponent() {
const [keyword, setKeyword] = useState('');
const debouncedKeyword = useDebounce(keyword, { wait: 500 });
const { data, loading } = useRequest(
() => fetchSearchResults(debouncedKeyword),
{ refreshDeps: [debouncedKeyword] }
);
return (
<div>
<input onChange={(e) => setKeyword(e.target.value)} />
{loading ? 'Loading...' : <Results data={data} />}
</div>
);
}第四部分:工作流程实战
本章提供完整的工作流程示例,让你可以直接复制运行。
11. 简单任务:One-Shot 模式
11.1 适用场景
特征:
- 单文件或少数文件
- 明确的输入输出
- 不涉及架构决策
- 能在 1-2 句话内描述清楚
示例:
- 修改按钮文案
- 添加一个工具函数
- 修复一个简单 bug
- 更新配置文件
11.2 操作步骤
步骤 1:描述需求
# 直接描述,一句话搞定
"把 handleLogin 函数中的 try-catch 改为返回 Result 类型"
"在 utils.ts 中添加一个 formatDate 函数,支持相对时间"
"修复 src/api/users.ts 中的 TypeScript 错误"步骤 2:AI 执行
AI 会:
- 定位相关文件
- 理解现有代码
- 生成修改
- 展示修改内容
步骤 3:审查结果
# 检查 AI 的修改
# - 逻辑是否正确
# - 是否符合项目风格
# - 是否有潜在问题步骤 4:验证
# 类型检查
pnpm typecheck
# 手动验证
# 在浏览器中测试功能11.3 完整示例
任务:添加一个格式化日期的函数
# 步骤 1:描述需求
用户: 在 src/utils/date.ts 中添加一个 formatDate 函数,支持:
- 相对时间(如"3 分钟前")
- 绝对时间(如"2026-03-27 10:30")
- 自动选择(小于 24 小时用相对时间)
# 步骤 2:AI 执行
AI:
好的,我来添加这个函数...
创建文件:src/utils/date.ts
```typescript
/**
* 格式化日期
* @param date - 日期对象或时间戳
* @param mode - 格式化模式:'relative' | 'absolute' | 'auto'
*/
export function formatDate(
date: Date | number,
mode: 'relative' | 'absolute' | 'auto' = 'auto'
): string {
const timestamp = typeof date === 'number' ? date : date.getTime();
const now = Date.now();
const diff = now - timestamp;
if (mode === 'relative' || (mode === 'auto' && diff < 24 * 60 * 60 * 1000)) {
return formatRelative(diff);
}
return formatAbsolute(new Date(timestamp));
}
function formatRelative(diff: number): string {
const seconds = Math.floor(diff / 1000);
const minutes = Math.floor(seconds / 60);
const hours = Math.floor(minutes / 60);
if (seconds < 60) return '刚刚';
if (minutes < 60) return `${minutes} 分钟前`;
if (hours < 24) return `${hours} 小时前`;
return `${Math.floor(hours / 24)} 天前`;
}
function formatAbsolute(date: Date): string {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
const hour = String(date.getHours()).padStart(2, '0');
const minute = String(date.getMinutes()).padStart(2, '0');
return `${year}-${month}-${day} ${hour}:${minute}`;
}是否确认创建?
步骤 3:审查结果
用户: 确认
步骤 4:验证
AI: 运行 pnpm typecheck… ✓ 通过
请手动验证:
- formatDate(new Date(), ‘relative’) 返回 “刚刚”
- formatDate(Date.now() - 60000, ‘auto’) 返回 “1 分钟前”
- formatDate(new Date(‘2026-01-01’), ‘absolute’) 返回 “2026-01-01 00:00”
用户: 验证通过,完成
---
## 12. 中等任务:Plan Mode + 验证
### 12.1 适用场景
**特征**:
- 涉及多个文件
- 需要理解现有代码模式
- 有明确的验收标准
- 需要先规划再执行
**示例**:
- 添加用户权限系统
- 重构某个模块
- 集成第三方服务
- 添加新功能
### 12.2 操作步骤
```mermaid
flowchart TB
A[描述需求] --> B[AI 分析并制定计划]
B --> C[审查计划]
C --> D{计划合理?}
D -->|否| E[调整需求]
E --> B
D -->|是| F[分步实现]
F --> G[每步验证]
G --> H{还有步骤?}
H -->|是| F
H -->|否| I[最终验证]步骤 1:描述需求
"我需要添加用户权限系统。先分析现有代码,然后制定实施计划。"步骤 2:AI 制定计划
AI 会:
- 分析现有代码结构
- 识别需要修改的文件
- 制定实施步骤
- 识别潜在风险
步骤 3:审查计划
检查:
- 步骤是否合理
- 是否遗漏了什么
- 风险是否可控
步骤 4:分步实现
每完成一步,验证后再继续下一步。
12.3 计划模板
## 目标
[一句话描述要达成什么]
## 涉及文件
- file1.ts - [修改原因]
- file2.ts - [修改原因]
## 实施步骤
1. [第一步] → 验证点:[如何验证]
2. [第二步] → 验证点:[如何验证]
3. [第三步] → 验证点:[如何验证]
## 风险点
- [潜在问题及应对]12.4 完整示例
任务:添加用户权限系统
# 步骤 1:描述需求
用户: 我需要添加用户权限系统。要求:
1. 支持角色:admin、editor、viewer
2. 支持权限:read、write、delete
3. 角色与权限的映射关系
4. 在 API 中间件中检查权限
# 步骤 2:AI 分析并制定计划
AI:
让我分析现有代码结构...
## 分析结果
- 当前认证:src/auth/middleware.ts
- 用户模型:src/models/user.ts
- API 路由:src/api/*
## 实施计划
### 目标
添加基于角色的权限控制系统
### 涉及文件
- src/models/user.ts - 添加角色字段
- src/auth/permissions.ts - 新建权限定义
- src/auth/middleware.ts - 添加权限检查
- src/api/users/route.ts - 应用权限检查
### 实施步骤
1. 定义权限和角色 → 验证:类型检查通过
2. 更新用户模型 → 验证:数据库迁移成功
3. 创建权限中间件 → 验证:单元测试通过
4. 应用到 API 路由 → 验证:集成测试通过
### 风险点
- 现有用户默认角色:需要设置默认值
- 权限检查遗漏:需要全面审查 API
是否确认此计划?
# 步骤 3:审查并确认
用户: 确认,开始执行
# 步骤 4:分步实现
AI:
开始执行步骤 1:定义权限和角色...
[创建 src/auth/permissions.ts]
```typescript
// 权限定义
export type Permission = 'read' | 'write' | 'delete';
// 角色定义
export type Role = 'admin' | 'editor' | 'viewer';
// 角色权限映射
export const ROLE_PERMISSIONS: Record<Role, Permission[]> = {
admin: ['read', 'write', 'delete'],
editor: ['read', 'write'],
viewer: ['read'],
};
// 检查权限
export function hasPermission(role: Role, permission: Permission): boolean {
return ROLE_PERMISSIONS[role].includes(permission);
}验证:pnpm typecheck ✓ 通过
继续步骤 2?
用户: 继续
AI: 执行步骤 2:更新用户模型…
[修改 src/models/user.ts]
import { Role } from '../auth/permissions';
interface User {
id: string;
email: string;
name: string;
role: Role; // 新增
createdAt: Date;
updatedAt: Date;
}验证:pnpm typecheck ✓ 通过
继续步骤 3?
用户: 继续
[… 继续执行 …]
步骤 5:最终验证
AI: 所有步骤完成!
运行 pnpm typecheck… ✓ 通过 运行 pnpm build… ✓ 通过 运行 pnpm test… ✓ 12 passed
请手动验证:
- admin 用户可以删除
- editor 用户不能删除
- viewer 用户只能读取
用户: 验证通过,完成
---
## 13. 核心功能:TDD + AI
### 13.1 适用场景
**特征**:
- 核心业务逻辑
- 支付、认证等高风险模块
- 复杂算法实现
- 需要长期维护的代码
### 13.2 RED-GREEN-REFACTOR 循环
```mermaid
flowchart TB
subgraph RED["RED 阶段"]
R1[写失败测试] --> R2[运行测试确认失败]
R2 --> R3{失败原因正确?}
R3 -->|否| R4[修正测试]
R4 --> R2
R3 -->|是| G1
end
subgraph GREEN["GREEN 阶段"]
G1[写最小实现] --> G2[运行测试确认通过]
G2 --> G3{测试通过?}
G3 -->|否| G4[修改代码]
G4 --> G2
G3 -->|是| F1
end
subgraph REFACTOR["REFACTOR 阶段"]
F1[重构代码] --> F2[运行测试确认通过]
F2 --> F3{测试通过?}
F3 -->|否| F4[回滚或修复]
F4 --> F2
F3 -->|是| F5{还有功能?}
F5 -->|是| R1
F5 -->|否| DONE[完成]
end13.3 完整示例:登录功能
需求:实现用户登录功能,支持:
- 邮箱密码登录
- 失败 3 次锁定账户 15 分钟
- 登录成功返回 token
阶段 1:RED(写失败测试)
// tests/auth/login.spec.ts
import { describe, test, expect, beforeEach } from 'vitest';
import { login, LoginError } from '@/auth/login';
describe('login', () => {
const validEmail = 'user@example.com';
const validPassword = 'password123';
beforeEach(() => {
// 重置测试状态
resetAuthState();
});
test('成功登录返回 token', async () => {
const result = await login(validEmail, validPassword);
expect(result.success).toBe(true);
expect(result.token).toBeDefined();
expect(result.token).toMatch(/^eyJ/); // JWT 格式
});
test('密码错误返回失败', async () => {
const result = await login(validEmail, 'wrong-password');
expect(result.success).toBe(false);
expect(result.error).toBe(LoginError.INVALID_CREDENTIALS);
});
test('失败 3 次锁定账户', async () => {
// 失败 3 次
await login(validEmail, 'wrong');
await login(validEmail, 'wrong');
await login(validEmail, 'wrong');
// 第 4 次即使密码正确也应该被锁定
const result = await login(validEmail, validPassword);
expect(result.success).toBe(false);
expect(result.error).toBe(LoginError.ACCOUNT_LOCKED);
});
test('锁定 15 分钟后自动解锁', async () => {
// 失败 3 次锁定
await login(validEmail, 'wrong');
await login(validEmail, 'wrong');
await login(validEmail, 'wrong');
// 模拟 15 分钟后
vi.useFakeTimers();
vi.advanceTimersByTime(15 * 60 * 1000);
const result = await login(validEmail, validPassword);
expect(result.success).toBe(true);
vi.useRealTimers();
});
});运行测试,确认失败:
pnpm test tests/auth/login.spec.ts
# 期望输出:
# ❌ login 成功登录返回 token
# Error: Cannot find module '@/auth/login'
# ❌ login 密码错误返回失败
# ❌ login 失败 3 次锁定账户
# ❌ login 锁定 15 分钟后自动解锁阶段 2:GREEN(最小实现)
// src/auth/login.ts
import jwt from 'jsonwebtoken';
export enum LoginError {
INVALID_CREDENTIALS = 'INVALID_CREDENTIALS',
ACCOUNT_LOCKED = 'ACCOUNT_LOCKED',
}
interface LoginResult {
success: boolean;
token?: string;
error?: LoginError;
}
// 简单的内存存储(实际项目用数据库)
const failedAttempts = new Map<string, number>();
const lockoutUntil = new Map<string, number>();
export async function login(
email: string,
password: string
): Promise<LoginResult> {
// 检查是否被锁定
const lockoutTime = lockoutUntil.get(email);
if (lockoutTime && Date.now() < lockoutTime) {
return { success: false, error: LoginError.ACCOUNT_LOCKED };
}
// 验证凭据(简化版,实际项目要查数据库)
const validCredentials = email === 'user@example.com' && password === 'password123';
if (!validCredentials) {
// 记录失败次数
const attempts = (failedAttempts.get(email) || 0) + 1;
failedAttempts.set(email, attempts);
// 3 次失败后锁定
if (attempts >= 3) {
lockoutUntil.set(email, Date.now() + 15 * 60 * 1000);
}
return { success: false, error: LoginError.INVALID_CREDENTIALS };
}
// 登录成功,重置失败计数
failedAttempts.delete(email);
lockoutUntil.delete(email);
// 生成 token
const token = jwt.sign({ email }, process.env.JWT_SECRET!, { expiresIn: '1h' });
return { success: true, token };
}
// 测试辅助函数
export function resetAuthState() {
failedAttempts.clear();
lockoutUntil.clear();
}运行测试,确认通过:
pnpm test tests/auth/login.spec.ts
# 期望输出:
# ✓ login 成功登录返回 token
# ✓ login 密码错误返回失败
# ✓ login 失败 3 次锁定账户
# ✓ login 锁定 15 分钟后自动解锁
#
# Test Files 1 passed
# Tests 4 passed阶段 3:REFACTOR(重构优化)
// src/auth/login.ts(重构后)
import jwt from 'jsonwebtoken';
export enum LoginError {
INVALID_CREDENTIALS = 'INVALID_CREDENTIALS',
ACCOUNT_LOCKED = 'ACCOUNT_LOCKED',
USER_NOT_FOUND = 'USER_NOT_FOUND',
}
interface User {
id: string;
email: string;
passwordHash: string;
}
interface LoginResult {
success: boolean;
token?: string;
error?: LoginError;
}
interface AuthConfig {
maxAttempts: number;
lockoutDurationMs: number;
tokenExpiry: string;
}
const DEFAULT_CONFIG: AuthConfig = {
maxAttempts: 3,
lockoutDurationMs: 15 * 60 * 1000, // 15 分钟
tokenExpiry: '1h',
};
export class AuthService {
private failedAttempts = new Map<string, number>();
private lockoutUntil = new Map<string, number>();
constructor(
private getUser: (email: string) => Promise<User | null>,
private config: AuthConfig = DEFAULT_CONFIG
) {}
async login(email: string, password: string): Promise<LoginResult> {
// 检查锁定状态
if (this.isLocked(email)) {
return { success: false, error: LoginError.ACCOUNT_LOCKED };
}
// 获取用户
const user = await this.getUser(email);
if (!user) {
return { success: false, error: LoginError.USER_NOT_FOUND };
}
// 验证密码
const validPassword = await this.verifyPassword(password, user.passwordHash);
if (!validPassword) {
this.recordFailure(email);
return { success: false, error: LoginError.INVALID_CREDENTIALS };
}
// 登录成功
this.clearFailures(email);
const token = this.generateToken(user);
return { success: true, token };
}
private isLocked(email: string): boolean {
const lockoutTime = this.lockoutUntil.get(email);
return lockoutTime !== undefined && Date.now() < lockoutTime;
}
private recordFailure(email: string): void {
const attempts = (this.failedAttempts.get(email) || 0) + 1;
this.failedAttempts.set(email, attempts);
if (attempts >= this.config.maxAttempts) {
this.lockoutUntil.set(
email,
Date.now() + this.config.lockoutDurationMs
);
}
}
private clearFailures(email: string): void {
this.failedAttempts.delete(email);
this.lockoutUntil.delete(email);
}
private generateToken(user: User): string {
return jwt.sign(
{ sub: user.id, email: user.email },
process.env.JWT_SECRET!,
{ expiresIn: this.config.tokenExpiry }
);
}
private async verifyPassword(password: string, hash: string): Promise<boolean> {
// 实际项目使用 bcrypt 或 argon2
const bcrypt = await import('bcrypt');
return bcrypt.compare(password, hash);
}
// 测试辅助方法
resetState(): void {
this.failedAttempts.clear();
this.lockoutUntil.clear();
}
}运行测试,确认重构后仍通过:
pnpm test tests/auth/login.spec.ts
# 期望输出:
# ✓ login 成功登录返回 token
# ✓ login 密码错误返回失败
# ✓ login 失败 3 次锁定账户
# ✓ login 锁定 15 分钟后自动解锁
#
# Test Files 1 passed
# Tests 4 passed14. 大型项目:Spec-Driven Development
14.1 适用场景
特征:
- 跨多个系统/服务
- 需要架构决策
- 长期维护需求
- 团队协作
14.2 四阶段流程
14.3 完整示例:支付系统
需求:实现完整的支付系统,包含订单、支付、回调。
阶段 1:Specify(规格)
/opsx-propose add-payment-systemproposal.md:
# 支付系统
## 概述
实现完整的支付系统,支持订单创建、支付处理、回调通知。
## 用户故事
作为用户,我想要在线支付订单,以便完成购买。
## 验收标准
- [ ] 用户可以创建订单
- [ ] 用户可以选择支付方式(微信、支付宝)
- [ ] 支付成功后订单状态更新
- [ ] 支付失败后订单状态更新
- [ ] 支付超时后订单自动取消
- [ ] 支付回调验签正确
## 约束条件
- 必须使用 HTTPS
- 必须验证支付回调签名
- 敏感信息必须加密存储
## 非目标
- 不包含退款功能(后续迭代)
- 不包含分期付款(后续迭代)design.md:
# 支付系统设计
## 架构
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ 前端 │────▶│ API │────▶│ 支付网关 │
│ (Next.js) │ │ (Node.js) │ │ (微信/支付宝)│
└─────────────┘ └─────────────┘ └─────────────┘
│
▼
┌─────────────┐
│ 数据库 │
│ (PostgreSQL)│
└─────────────┘
## 数据模型
### Order(订单)
- id: UUID
- userId: UUID
- amount: Decimal
- status: Enum (pending, paid, failed, cancelled)
- paymentMethod: Enum (wechat, alipay)
- createdAt: DateTime
- paidAt: DateTime?
### Payment(支付记录)
- id: UUID
- orderId: UUID
- transactionId: String
- status: Enum
- rawCallback: JSON
- createdAt: DateTime
## API 设计
### POST /api/orders
创建订单
### POST /api/orders/:id/pay
发起支付
### POST /api/payments/callback/wechat
微信支付回调
### POST /api/payments/callback/alipay
支付宝支付回调
## 安全考虑
1. 回调验签
2. 幂等性处理
3. 金额校验
4. 超时处理tasks.md:
# 支付系统任务
## 阶段 1:基础设施
- [ ] 创建数据库迁移
- [ ] 定义数据模型
- [ ] 创建基础 API 路由
## 阶段 2:订单模块
- [ ] 实现订单创建 API
- [ ] 实现订单查询 API
- [ ] 编写订单测试
## 阶段 3:支付模块
- [ ] 集成微信支付 SDK
- [ ] 集成支付宝 SDK
- [ ] 实现支付发起 API
- [ ] 实现支付回调 API
- [ ] 编写支付测试
## 阶段 4:前端
- [ ] 创建订单页面
- [ ] 创建支付页面
- [ ] 创建支付结果页面
## 阶段 5:集成测试
- [ ] 端到端测试
- [ ] 性能测试
- [ ] 安全测试阶段 2:Plan(规划)
# 使用 Prometheus 规划师
/start-work
# 或直接对话规划
"基于支付系统规格,生成详细的技术实现计划"阶段 3:Implement(实现)
# 全功率执行
ultrawork
# 或标准执行
/opsx-apply add-payment-system阶段 4:Archive(归档)
/opsx-archive add-payment-system15. 完整案例:用户登录模块开发
本节提供一个完整的、可执行的案例,展示从需求到上线的全流程。
15.1 需求定义
用户故事:
作为用户,我想要通过手机号登录,以便快速访问我的账户。
验收标准:
- 手机号格式验证(中国大陆 11 位,1 开头)
- 验证码发送(6 位数字,5 分钟有效)
- 验证码验证
- 登录成功返回 token
- 登录失败提示明确
禁止事项:
- 不修改现有邮箱登录逻辑
- 不添加第三方登录
15.2 规格生成
/opsx-propose add-phone-login输出:
openspec/changes/add-phone-login/
├── proposal.md
├── design.md
└── tasks.md15.3 测试设计
skill(name="test-driven-development")测试文件:
// tests/auth/phone-login.spec.ts
import { describe, test, expect, beforeEach, vi } from 'vitest';
describe('手机号登录', () => {
const validPhone = '13800138000';
const validCode = '123456';
beforeEach(() => {
vi.clearAllMocks();
});
describe('发送验证码', () => {
test('有效手机号发送成功', async () => {
const result = await sendVerificationCode(validPhone);
expect(result.success).toBe(true);
expect(result.message).toBe('验证码已发送');
});
test('无效手机号返回错误', async () => {
const result = await sendVerificationCode('12345');
expect(result.success).toBe(false);
expect(result.error).toBe('手机号格式错误');
});
test('60 秒内重复发送返回错误', async () => {
await sendVerificationCode(validPhone);
const result = await sendVerificationCode(validPhone);
expect(result.success).toBe(false);
expect(result.error).toBe('验证码发送过于频繁');
});
});
describe('验证码登录', () => {
test('正确验证码登录成功', async () => {
await sendVerificationCode(validPhone);
const result = await loginWithPhone(validPhone, validCode);
expect(result.success).toBe(true);
expect(result.token).toBeDefined();
});
test('错误验证码登录失败', async () => {
await sendVerificationCode(validPhone);
const result = await loginWithPhone(validPhone, '000000');
expect(result.success).toBe(false);
expect(result.error).toBe('验证码错误');
});
test('过期验证码登录失败', async () => {
await sendVerificationCode(validPhone);
// 模拟 6 分钟后
vi.useFakeTimers();
vi.advanceTimersByTime(6 * 60 * 1000);
const result = await loginWithPhone(validPhone, validCode);
expect(result.success).toBe(false);
expect(result.error).toBe('验证码已过期');
vi.useRealTimers();
});
});
});15.4 代码实现
// src/auth/phone-login.ts
import jwt from 'jsonwebtoken';
interface SendCodeResult {
success: boolean;
message?: string;
error?: string;
}
interface LoginResult {
success: boolean;
token?: string;
error?: string;
}
// 验证码存储(实际项目用 Redis)
const verificationCodes = new Map<string, { code: string; expiresAt: number }>();
const lastSentAt = new Map<string, number>();
// 手机号验证
function isValidPhone(phone: string): boolean {
return /^1[3-9]\d{9}$/.test(phone);
}
// 发送验证码
export async function sendVerificationCode(phone: string): Promise<SendCodeResult> {
// 验证手机号
if (!isValidPhone(phone)) {
return { success: false, error: '手机号格式错误' };
}
// 检查发送频率
const lastSent = lastSentAt.get(phone);
if (lastSent && Date.now() - lastSent < 60 * 1000) {
return { success: false, error: '验证码发送过于频繁' };
}
// 生成验证码
const code = Math.random().toString().slice(2, 8);
const expiresAt = Date.now() + 5 * 60 * 1000; // 5 分钟有效
// 存储
verificationCodes.set(phone, { code, expiresAt });
lastSentAt.set(phone, Date.now());
// 发送(实际项目调用短信服务)
console.log(`[SMS] 发送验证码 ${code} 到 ${phone}`);
return { success: true, message: '验证码已发送' };
}
// 验证码登录
export async function loginWithPhone(phone: string, code: string): Promise<LoginResult> {
// 验证手机号
if (!isValidPhone(phone)) {
return { success: false, error: '手机号格式错误' };
}
// 获取存储的验证码
const stored = verificationCodes.get(phone);
if (!stored) {
return { success: false, error: '请先获取验证码' };
}
// 检查是否过期
if (Date.now() > stored.expiresAt) {
verificationCodes.delete(phone);
return { success: false, error: '验证码已过期' };
}
// 验证码校验
if (stored.code !== code) {
return { success: false, error: '验证码错误' };
}
// 登录成功,清除验证码
verificationCodes.delete(phone);
// 生成 token
const token = jwt.sign(
{ phone },
process.env.JWT_SECRET!,
{ expiresIn: '7d' }
);
return { success: true, token };
}15.5 API 路由
// app/api/auth/send-code/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { sendVerificationCode } from '@/auth/phone-login';
export async function POST(request: NextRequest) {
const { phone } = await request.json();
const result = await sendVerificationCode(phone);
return NextResponse.json(result);
}// app/api/auth/phone-login/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { loginWithPhone } from '@/auth/phone-login';
export async function POST(request: NextRequest) {
const { phone, code } = await request.json();
const result = await loginWithPhone(phone, code);
return NextResponse.json(result);
}15.6 前端页面
// app/login/page.tsx
'use client';
import { useState } from 'react';
import { useRouter } from 'next/navigation';
export default function PhoneLoginPage() {
const router = useRouter();
const [phone, setPhone] = useState('');
const [code, setCode] = useState('');
const [countdown, setCountdown] = useState(0);
const [loading, setLoading] = useState(false);
const [error, setError] = useState('');
// 发送验证码
const handleSendCode = async () => {
if (countdown > 0) return;
setLoading(true);
setError('');
try {
const res = await fetch('/api/auth/send-code', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ phone }),
});
const data = await res.json();
if (data.success) {
// 开始倒计时
setCountdown(60);
const timer = setInterval(() => {
setCountdown((prev) => {
if (prev <= 1) {
clearInterval(timer);
return 0;
}
return prev - 1;
});
}, 1000);
} else {
setError(data.error);
}
} finally {
setLoading(false);
}
};
// 登录
const handleLogin = async () => {
setLoading(true);
setError('');
try {
const res = await fetch('/api/auth/phone-login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ phone, code }),
});
const data = await res.json();
if (data.success) {
// 存储 token
localStorage.setItem('token', data.token);
router.push('/dashboard');
} else {
setError(data.error);
}
} finally {
setLoading(false);
}
};
return (
<div className="min-h-screen flex items-center justify-center bg-gray-50">
<div className="max-w-md w-full space-y-8 p-8 bg-white rounded-lg shadow">
<h2 className="text-2xl font-bold text-center">手机号登录</h2>
{error && (
<div className="p-3 text-red-600 bg-red-50 rounded">
{error}
</div>
)}
<div className="space-y-4">
<div>
<label htmlFor="phone" className="block text-sm font-medium">
手机号
</label>
<input
id="phone"
type="tel"
value={phone}
onChange={(e) => setPhone(e.target.value)}
placeholder="请输入手机号"
className="mt-1 block w-full px-3 py-2 border rounded-md"
/>
</div>
<div>
<label htmlFor="code" className="block text-sm font-medium">
验证码
</label>
<div className="flex gap-2">
<input
id="code"
type="text"
value={code}
onChange={(e) => setCode(e.target.value)}
placeholder="请输入验证码"
className="flex-1 px-3 py-2 border rounded-md"
/>
<button
onClick={handleSendCode}
disabled={countdown > 0 || loading}
className="px-4 py-2 bg-blue-600 text-white rounded-md disabled:opacity-50"
>
{countdown > 0 ? `${countdown}s` : '发送验证码'}
</button>
</div>
</div>
<button
onClick={handleLogin}
disabled={loading}
className="w-full py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 disabled:opacity-50"
>
{loading ? '登录中...' : '登录'}
</button>
</div>
</div>
</div>
);
}15.7 验证确认
# 自动化验证
pnpm typecheck # ✓ 类型检查通过
pnpm build # ✓ 构建成功
pnpm lint # ✓ 代码检查通过
pnpm test # ✓ 8 passed
# 手动验证
# 1. 打开 http://localhost:3000/login
# 2. 输入手机号 13800138000
# 3. 点击发送验证码
# 4. 查看控制台输出的验证码
# 5. 输入验证码
# 6. 点击登录
# 7. 确认跳转到 /dashboard15.8 归档保存
/opsx-archive add-phone-login
# 输出:
# 归档到 openspec/changes/archive/2026-03-27-add-phone-login/第五部分:常见陷阱与解决方案
本章总结 AI 辅助开发中的常见陷阱,帮助你避免踩坑。
16. 测试陷阱
16.1 测试实现细节
问题:测试 CSS 类或内部状态,UI 改变时测试失败。
错误示例:
// ❌ 测试 CSS 类
await expect(page.locator('.error-message')).toHaveClass('visible');
// ❌ 测试内部状态
expect(component.state.isLoading).toBe(true);正确做法:
// ✅ 测试用户看到的内容
await expect(page.getByText('登录失败')).toBeVisible();
// ✅ 测试用户可观察的行为
await expect(page.getByRole('alert')).toHaveText('加载中...');16.2 硬编码等待
问题:使用固定等待时间,测试变慢且不稳定。
错误示例:
// ❌ 硬编码等待
await page.waitForTimeout(3000);
await page.click('button');
// ❌ 等待固定时间
await new Promise(resolve => setTimeout(resolve, 2000));正确做法:
// ✅ 使用 Playwright 自动等待
await page.click('button');
// ✅ 等待特定条件
await expect(page.getByText('加载完成')).toBeVisible();
// ✅ 等待网络请求完成
await page.waitForLoadState('networkidle');16.3 脆弱选择器
问题:使用 CSS 类或 XPath,页面结构变化时测试失败。
选择器优先级:
1. BEST - Test ID: page.getByTestId('submit-button')
2. GOOD - Role: page.getByRole('button', { name: '提交' })
3. OK - Label: page.getByLabel('邮箱')
4. AVOID - CSS: page.locator('.btn-primary.submit')
5. NEVER - XPath: page.locator('//div[@class="btn"]')错误示例:
// ❌ 脆弱的 CSS 选择器
page.locator('.btn-primary.submit')
page.locator('div.container > form > button:nth-child(3)')
// ❌ XPath
page.locator('//button[contains(@class, "submit")]')正确做法:
// ✅ Test ID(最稳定)
page.getByTestId('submit-button')
// ✅ Role(语义化)
page.getByRole('button', { name: '提交' })
// ✅ Label(表单元素)
page.getByLabel('邮箱')
// ✅ Text(文本内容)
page.getByText('登录')16.4 测试间共享状态
问题:测试之间共享状态,导致测试不稳定。
错误示例:
// ❌ 全局状态
let user: User;
beforeAll(async () => {
user = await createUser(); // 所有测试共享
});
test('test1', () => {
// 修改 user
});
test('test2', () => {
// user 可能已被 test1 修改
});正确做法:
// ✅ 每个测试独立状态
beforeEach(async () => {
user = await createUser(); // 每个测试前重新创建
});
// ✅ 使用 fixtures
test('test1', async ({ page, user }) => {
// user 是每个测试独立的
});16.5 过度 E2E 测试
问题:所有测试都写 E2E,反馈循环慢。
测试金字塔:
/\
/ \ E2E 测试(慢,少)
/----\
/ \ 集成测试(中)
/--------\
/ \ 单元测试(快,多)
/------------\建议:
| 测试类型 | 比例 | 速度 | 适用场景 |
|---|---|---|---|
| 单元测试 | 70% | 快 | 纯函数、工具类 |
| 集成测试 | 20% | 中 | API、组件交互 |
| E2E 测试 | 10% | 慢 | 关键用户流程 |
17. 代码陷阱
17.1 过度信任 AI 输出
问题:AI 生成的代码 45% 包含 OWASP 安全漏洞。
数据来源:Veracode 2025 研究报告
常见漏洞:
| 漏洞类型 | 占比 | 示例 |
|---|---|---|
| XSS | 86% | 未转义用户输入 |
| SQL 注入 | 72% | 字符串拼接 SQL |
| 敏感数据泄露 | 45% | 日志输出密码 |
解决方案:
// ❌ AI 可能生成的危险代码
app.get('/user/:id', (req, res) => {
const query = `SELECT * FROM users WHERE id = ${req.params.id}`;
db.query(query);
});
// ✅ 安全的代码
app.get('/user/:id', (req, res) => {
const query = 'SELECT * FROM users WHERE id = ?';
db.query(query, [req.params.id]);
});审查清单:
- [ ] 是否有 SQL 注入风险?
- [ ] 是否有 XSS 漏洞?
- [ ] 敏感数据是否加密?
- [ ] 权限检查是否完整?
- [ ] 输入验证是否充分?17.2 认知债务累积
问题:速度提升导致对代码理解下降。
症状:
- 不知道 AI 生成的代码做了什么
- 无法解释代码的工作原理
- 修改时不知道从哪里下手
解决方案:
-
定期停下来理解代码
# 每完成一个功能,花 5 分钟理解 "请解释这段代码的工作原理" -
不让 AI 完全替代判断
# 关键决策自己来做 "这里有两个方案,我选择方案 A,因为..." -
保持核心技能练习
# 偶尔手动写代码 # 不依赖 AI 完成所有工作
17.3 跳过验证
问题:不验证就认为完成,功能实际不工作。
强制规则:
⚠️ 无验证证据 = 未完成
必须验证:
1. 类型检查:pnpm typecheck
2. 构建验证:pnpm build
3. 代码检查:pnpm lint
4. 测试通过:pnpm test
5. 功能手动验证(最重要)验证清单:
- [ ] 代码通过 lint 检查
- [ ] TypeScript 编译无错误
- [ ] 构建成功
- [ ] 测试全部通过
- [ ] 功能手动验证过
- [ ] 边界情况考虑过
- [ ] 安全风险审查过18. 流程陷阱
18.1 过度工程
问题:简单任务使用复杂流程,浪费时间。
判断标准:
任务复杂度判断:
简单任务特征:
✓ 单文件或少数文件
✓ 明确的输入输出
✓ 不涉及架构决策
→ 使用 One-Shot 模式
中等任务特征:
✓ 涉及多个模块
✓ 需要理解现有代码模式
✓ 有明确的验收标准
→ 使用 Plan Mode
复杂任务特征:
✓ 跨多个系统/服务
✓ 需要架构决策
✓ 长期维护需求
→ 使用 Spec-Driven Development18.2 中断丢失
问题:任务中断后无法恢复,重复工作。
解决方案:
# 查看活跃变更
openspec list --json
# 查看活跃 session
session_list
# 继续执行
/opsx-apply <change-name>
# 或使用 session_id 继续
task(
session_id="ses_abc123",
load_skills=[],
description="Continue task",
prompt="继续之前的任务...",
run_in_background=false
)18.3 规格模糊
问题:规格阶段没有充分澄清,后期返工。
解决方案:
在 /opsx-explore 阶段:
-
追问边界情况
"如果用户输入了无效数据会怎样?" "如果网络请求失败会怎样?" "如果并发访问会怎样?" -
明确验收标准
## 验收标准 - [ ] 正常场景:用户输入正确数据,功能正常 - [ ] 异常场景:用户输入错误数据,提示明确 - [ ] 边界场景:用户输入边界值,处理正确 -
定义禁止事项
## 禁止事项 - 不修改现有登录逻辑 - 不添加第三方登录 - 不存储明文密码
19. 工具陷阱
19.1 MCP 配置错误
问题:MCP 工具配置不正确,无法使用。
常见错误:
// ❌ 错误:缺少 type 字段
{
"mcp": {
"playwright-mcp": {
"command": ["playwright-mcp"],
"enabled": true
}
}
}
// ❌ 错误:command 不是数组
{
"mcp": {
"playwright-mcp": {
"command": "playwright-mcp",
"enabled": true,
"type": "local"
}
}
}正确配置:
// ✅ 正确
{
"mcp": {
"playwright-mcp": {
"command": ["playwright-mcp", "--cdp-endpoint", "http://localhost:9222"],
"enabled": true,
"type": "local"
}
}
}19.2 代理调用错误
问题:代理调用语法错误,无法执行。
常见错误:
// ❌ 错误:缺少必需参数
task(category="quick")
// ❌ 错误:参数名错误
task(
type="quick", // 应该是 category
skills=[], // 应该是 load_skills
desc="Fix bug" // 应该是 description
)
// ❌ 错误:run_in_background 不是布尔值
task(
category="quick",
load_skills=[],
description="Fix bug",
prompt="...",
run_in_background="yes" // 应该是 true/false
)正确调用:
// ✅ 正确
task(
category="quick",
load_skills=[],
description="Fix type error",
prompt="Fix the TypeScript error in src/utils.ts line 42...",
run_in_background=false
)19.3 Session 恢复问题
问题:session_id 无效或过期,无法恢复。
解决方案:
# 查看所有 session
session_list
# 搜索 session 内容
session_search(query="认证实现")
# 读取 session 历史
session_read(session_id="ses_abc123")注意事项:
- session_id 区分大小写
- session 可能过期(取决于配置)
- 建议及时保存重要信息
第六部分:FAQ
本章回答 AI 辅助开发中的常见问题。
20. 入门问题
Q1:我是 AI 开发新手,应该从哪里开始?
A:按以下步骤开始:
第 1 步 → 安装 OpenCode
opencode --version
第 2 步 → 完成第一个简单任务
"请解释这个项目的技术栈"
第 3 步 → 尝试修改代码
"把按钮文案改成中文"
第 4 步 → 学习验证流程
pnpm typecheck && pnpm build
第 5 步 → 根据需要学习进阶内容Q2:AI 生成的代码可以直接用吗?
A:**不可以!**必须审查:
| 检查项 | 说明 |
|---|---|
| 逻辑正确性 | 是否真正解决了问题? |
| 边界情况 | 是否处理了异常输入? |
| 安全性 | 是否有注入、泄露风险? |
| 性能 | 是否有不必要的复杂度? |
| 可维护性 | 代码是否容易理解? |
研究表明,AI 生成的代码中 45% 包含 OWASP 安全漏洞。
Q3:什么时候需要写测试?
A:判断标准:
改动影响 < 10 行代码 → 可以不写测试
改动影响 10-50 行代码 → 建议写测试
改动影响 > 50 行代码 → 必须写测试
涉及核心业务逻辑 → 必须写测试
涉及支付/认证 → 必须 TDD + 人工审查Q4:AI 不理解我的需求怎么办?
A:优化你的描述方式:
-
使用更具体的语言
❌ "登录后进入主页" ✅ "点击登录按钮后跳转到 /dashboard" -
提供示例
❌ "做一个登录页面" ✅ "类似微信的登录页面,有手机号输入框和验证码按钮" -
明确边界
❌ "密码不能太短" ✅ "密码长度 6-20 位,必须包含数字和字母" -
分步骤描述
❌ "做一个完整的用户系统" ✅ "第一步:用户注册;第二步:用户登录;第三步:密码重置"
Q5:如何避免 AI 生成不安全的代码?
A:遵循以下原则:
- 安全相关代码必须人工审查
- 使用安全检查清单:
- [ ] 是否有 SQL 注入风险? - [ ] 是否有 XSS 漏洞? - [ ] 敏感数据是否加密? - [ ] 权限检查是否完整? - 使用静态分析工具:
pnpm lint可以发现部分问题
21. 工具使用问题
Q6:如何选择正确的 MCP 工具?
A:根据场景选择:
开发新功能 / E2E 测试 → Playwright MCP
性能分析 / 问题诊断 → Chrome DevTools MCP
已登录场景 / 真实环境 → Real Browser MCPQ7:MCP 工具启动失败怎么办?
A:检查以下项:
-
是否正确安装
npm list -g @playwright/mcp@latest -
配置是否正确
{ "mcp": { "playwright-mcp": { "command": ["playwright-mcp"], "enabled": true, "type": "local" } } } -
端口是否被占用
lsof -i :9222
Q8:如何查看当前有哪些活跃的变更?
A:使用以下命令:
# 查看活跃变更
openspec list --json
# 或
/opsx-apply # 不指定名称时会提示选择Q9:如何继续之前中断的任务?
A:两种方式:
# 方式 1:继续执行变更
/opsx-apply <change-name>
# 方式 2:使用 session_id
task(
session_id="ses_abc123",
load_skills=[],
description="Continue task",
prompt="继续之前的任务...",
run_in_background=false
)Q10:如何获取 session_id?
A:每次调用 task() 后,返回结果中会包含 session_id:
// task() 返回结果示例
{
"session_id": "ses_abc123",
"status": "completed",
...
}22. 流程问题
Q11:什么时候使用 TDD?
A:
✅ 推荐使用:
- 核心业务逻辑
- 支付、认证等高风险模块
- 复杂算法实现
- 需要长期维护的代码
❌ 可跳过:
- UI/UX 密集型代码
- 快速原型验证
- 一次性脚本
- 配置文件修改Q12:什么时候使用多代理?
A:仅在以下情况:
- 任务高度开放,路径不可预测
- 需要不同专业领域的处理
- 有明确的成功标准和反馈机制
优先尝试的简化方案:
- 单代理 + 多工具
- Prompt Chaining
- Plan-and-Execute
Q13:规格文档写错了怎么办?
A:直接编辑 openspec/changes/<name>/ 下的文件,然后继续执行。
Q14:执行过程中发现设计有问题?
A:
- 暂停执行(直接告诉 AI)
- 进入
/opsx-explore重新讨论 - 更新规格文档
- 继续
/opsx-apply
Q15:测试一直失败怎么办?
A:
- 使用
skill(name="systematic-debugging")进行系统化调试 - 检查测试是否正确描述了预期行为
- 必要时咨询
oracle子代理
22. CI/CD 集成方案
22.1 Playwright 测试 CI 配置
GitHub Actions 配置示例:
# .github/workflows/playwright.yml
name: Playwright Tests
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
test:
timeout-minutes: 60
runs-on: ubuntu-latest
strategy:
matrix:
shard: [1/4, 2/4, 3/4, 4/4]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Install Playwright Browsers
run: npx playwright install --with-deps
- name: Run Playwright tests
run: npx playwright test --shard=${{ matrix.shard }}
- name: Upload test results
if: always()
uses: actions/upload-artifact@v4
with:
name: playwright-report-${{ matrix.shard }}
path: playwright-report/
retention-days: 30
merge-reports:
needs: test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
- name: Download artifacts
uses: actions/download-artifact@v4
with:
path: all-blob-reports
- name: Merge reports
run: npx playwright merge-reports --reporter html ./all-blob-reports
- name: Upload merged report
uses: actions/upload-artifact@v4
with:
name: playwright-report
path: playwright-report/
retention-days: 3022.2 Playwright 配置文件
// playwright.config.ts
import { defineConfig } from '@playwright/test';
export default defineConfig({
testDir: './tests/e2e',
fullyParallel: true,
// CI 环境感知
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
workers: process.env.CI ? '50%' : undefined,
reporter: process.env.CI ? 'blob' : 'html',
use: {
trace: 'on-first-retry',
screenshot: 'only-on-failure',
video: 'retain-on-failure',
baseURL: process.env.BASE_URL || 'http://localhost:3000',
},
projects: [
{
name: 'chromium',
use: { browserName: 'chromium' },
},
],
// 本地开发时自动启动服务器
webServer: process.env.CI ? undefined : {
command: 'pnpm dev',
url: 'http://localhost:3000',
reuseExistingServer: true,
},
});22.3 测试目录结构
tests/
├── e2e/ # E2E 测试(按功能组织)
│ ├── auth/
│ │ ├── login.spec.ts
│ │ └── signup.spec.ts
│ ├── checkout/
│ │ └── checkout.spec.ts
│ └── dashboard/
│ └── overview.spec.ts
├── pages/ # Page Object Model
│ ├── BasePage.ts
│ ├── LoginPage.ts
│ └── DashboardPage.ts
├── fixtures/ # 自定义 fixtures
│ └── auth.fixture.ts
└── utils/ # 共享工具
└── data-factory.ts22.4 本地验证脚本
// package.json
{
"scripts": {
"test:e2e": "playwright test",
"test:e2e:ui": "playwright test --ui",
"test:e2e:debug": "playwright test --debug",
"test:e2e:report": "playwright show-report"
}
}22.5 质量门禁配置
推荐的 CI 质量门禁:
# 添加到 CI 工作流
- name: Type check
run: pnpm typecheck
- name: Lint check
run: pnpm lint
- name: Build check
run: pnpm build
- name: Unit tests
run: pnpm test
- name: E2E tests
run: pnpm test:e2e23. 故障排除
Q16:OpenCode 启动失败
检查项:
# 1. 检查安装
opencode --version
# 2. 检查 Node.js 版本
node --version # 需要 v18+
# 3. 检查配置文件
cat .ai/.config/opencode/opencode.json
# 4. 检查 API Key
echo $ANTHROPIC_API_KEYQ17:构建失败
检查项:
# 1. 清理缓存
rm -rf .next node_modules
pnpm install
# 2. 检查 TypeScript 错误
pnpm typecheck
# 3. 检查依赖冲突
pnpm why <package-name>Q18:测试超时
解决方案:
// 增加超时时间
test('my test', async () => {
// ...
}, { timeout: 30000 }); // 30 秒
// 或全局配置
// playwright.config.ts
export default defineConfig({
timeout: 30000,
});Q19:浏览器自动化失败
检查项:
# 1. 检查浏览器安装
npx playwright install
# 2. 检查 CDP 端口
lsof -i :9222
# 3. 检查 MCP 配置
cat .ai/.config/opencode/opencode.json | grep -A 10 "mcp"Q20:AI 输出质量下降
原因:长会话导致上下文污染。
解决方案:
# 1. 清理会话
/clear
# 2. 开始新会话
/exit
opencode
# 3. 使用 session_id 继续相关工作
task(session_id="ses_abc123", ...)附录
A. 核心命令速查卡
OpenCode 命令
| 命令 | 用途 |
|---|---|
opencode | 启动交互模式 |
opencode "prompt" | 单次执行 |
/clear | 清理会话 |
/exit | 退出 |
OpenSpec 命令
| 命令 | 简写 | 用途 |
|---|---|---|
/openspec-propose | /opsx-propose | 创建规格 |
/openspec-explore | /opsx-explore | 需求探索 |
/openspec-apply-change | /opsx-apply | 执行实现 |
/openspec-archive-change | /opsx-archive | 归档保存 |
Superpowers 技能
| 技能 | 触发方式 | 作用 |
|---|---|---|
| brainstorming | skill(name="brainstorming") | 创建功能前的意图探索 |
| test-driven-development | skill(name="test-driven-development") | 编码前的测试先行 |
| systematic-debugging | skill(name="systematic-debugging") | 系统化调试 |
| verification-before-completion | skill(name="verification-before-completion") | 完成前的验证证据 |
Playwright AI Agents
| Agent | 命令 | 用途 |
|---|---|---|
| @planner | @planner 为登录流程生成测试计划 | 生成测试计划 |
| @generator | @generator 将测试计划转换为测试 | 生成测试代码 |
| @healer | @healer 修复失败的测试 | 修复失败测试 |
代理调用
// 使用 category
task(
category="quick",
load_skills=[],
description="Fix type error",
prompt="Fix the TypeScript error...",
run_in_background=false
)
// 使用 subagent_type
task(
subagent_type="explore",
load_skills=[],
description="Find auth patterns",
prompt="搜索认证实现模式...",
run_in_background=true
)
// 继续之前的 session
task(
session_id="ses_abc123",
load_skills=[],
description="Continue task",
prompt="继续之前的任务...",
run_in_background=false
)B. 验证清单
基础验证
- [ ] 类型检查: pnpm typecheck
- [ ] 构建成功: pnpm build
- [ ] 代码检查: pnpm lint
- [ ] 测试通过: pnpm test完整验证
- [ ] 代码通过 lint 检查
- [ ] TypeScript 编译无错误
- [ ] 构建成功
- [ ] 测试全部通过
- [ ] 功能手动验证过
- [ ] 边界情况考虑过
- [ ] 安全风险审查过AI 代码审查
- [ ] 逻辑正确性:是否真正解决了问题?
- [ ] 边界情况:是否处理了异常输入?
- [ ] 安全性:是否有注入、泄露风险?
- [ ] 性能:是否有不必要的复杂度?
- [ ] 可维护性:代码是否容易理解?安全检查
- [ ] 是否有 SQL 注入风险?
- [ ] 是否有 XSS 漏洞?
- [ ] 敏感数据是否加密?
- [ ] 权限检查是否完整?
- [ ] 输入验证是否充分?C. 行业数据参考
AI 编程工具现状(2025-2026)
| 指标 | 数据 | 来源 |
|---|---|---|
| AI 工具使用率 | 84% | Stack Overflow 2025 |
| 高度信任 AI 输出 | 仅 3% | Stack Overflow 2025 |
| 不信任 AI 准确性 | 46% | Stack Overflow 2025 |
| AI 代码漏洞率 | 2.74x 于人工 | Veracode 2025 |
| 含 OWASP 漏洞 | 45% | Veracode 2025 |
| XSS 漏洞失败率 | 86% | Veracode 2025 |
| Java 安全失败率 | 72% | Veracode 2025 |
| 连续任务成功率下降 | 80% → 38% | USC 研究 |
| 实际速度变化 | 可能慢 19% | METR RCT |
关键结论
- AI 是乘数,不是自动提升器:高级工程师获益更多
- 信任与实际存在差距:感觉更快 ≠ 实际更快
- 安全性需要额外关注:AI 代码漏洞率显著更高
- 简单模式通常足够:Anthropic 官方验证
TDD 效果数据
| 研究 | 发现 |
|---|---|
| Damm (2016) | 88% 研究显示外部质量提升 |
| IEEE 元分析 | 对质量有小正面影响 |
| 工业环境 | 质量提升 40-90%,开发时间增加 15-35% |
D. 参考资料
官方文档
技能文档
最佳实践
研究报告
E. 术语表
| 术语 | 全称 | 解释 |
|---|---|---|
| TDD | Test-Driven Development | 测试驱动开发,先写测试再写代码 |
| MCP | Model Context Protocol | AI 工具协议,让 AI 操作浏览器等工具 |
| CDP | Chrome DevTools Protocol | Chrome 调试协议 |
| E2E | End-to-End | 端到端测试 |
| SDD | Spec-Driven Development | 规格驱动开发 |
| OWASP | Open Web Application Security Project | 开放式 Web 应用安全项目 |
| JWT | JSON Web Token | JSON 网络令牌 |
| RSC | React Server Components | React 服务端组件 |
F. 更新日志
v2.0.0 (2026-03-27)
新增:
- 完整的工具生态介绍(OpenCode、OpenSpec、Superpowers、MCP)
- 19 个项目技能详细指南
- 完整可执行的工作流程示例
- 扩展的 FAQ(20 个问题)
- 行业数据参考
改进:
- 重新组织文档结构(六大部分)
- 增加更多代码示例
- 添加 Mermaid 流程图
- 补充故障排除指南
基于:
- work-flow-1~5.md
- 项目技能文档
- OpenCode/Superpowers/OpenSpec 官方文档
记住这三句话:
- 人做决策,AI 做执行 — 你是决策者,AI 是执行者
- 无证据不完成 — 没有验证就没有完成
- AI 代码必须审查 — 45% 的 AI 代码有安全漏洞
祝你与 AI 协作愉快!