从 PRD 到上线:SpecKit 如何让 AI 真正参与前端交付

0 阅读13分钟

从 PRD 到上线:SpecKit 如何让 AI 真正参与前端交付

你有没有遇到过这种情况:

产品给了一份 PRD,你让 AI 帮忙拆任务,它能说出一堆“看起来正确”的建议;设计稿来了,你让 AI 写页面,它又开始猜组件结构;等到代码写完,再让 AI Review,它只能对着局部代码提一些泛泛而谈的建议。

问题不一定出在 AI 不够强。

很多时候,是我们给 AI 的输入太散了。

需求、设计、代码、Review、测试、上线,每个环节都有上下文。但这些上下文没有被结构化,AI 就只能凭经验补空白。补得准时很惊艳,补不准时就是返工。

这就是我想介绍 SpecKit 的原因。

SpecKit 不是某个单一工具,而是一套规范化的 AI 协作开发流程。它的核心目标是:把从需求到上线的每个环节,都变成 AI 能读懂、能执行、能验证的标准输入输出。

下面我会用一个“用户管理模块”的例子,拆一遍 SpecKit 在前端开发里的完整使用方式。

SpecKit 解决的不是写代码,而是减少猜测

传统开发流程里,AI 最容易翻车的地方通常有四类:

  • PRD 写得太口语化,AI 不知道哪些是硬需求。
  • 设计稿缺少结构说明,AI 只能按视觉截图猜组件。
  • 开发任务没有边界,AI 容易一次改太多。
  • Review 只看局部代码,很难发现流程级问题。

SpecKit 的做法很朴素:

  1. 需求拆解模板化。
  2. UI 还原标准化。
  3. 组件开发任务化。
  4. Code Review 清单化。
  5. 上线交付可验证。

也就是说,它不是让 AI “随便发挥”,而是把 AI 放进一条有约束的工程流水线里。

整体流程可以理解成这样:

flowchart TD
    A["PRD 产品需求文档"] --> B["需求拆解"]
    B --> C["技术任务清单 + 风险分析"]
    C --> D["UI 还原"]
    D --> E["组件结构 + 样式规范"]
    E --> F["组件开发"]
    F --> G["功能代码 + 测试用例"]
    G --> H["Code Review"]
    H --> I["修复建议 + 改进代码"]
    I --> J["测试与上线"]

inline-workflow-overview.png

上图可以理解为 SpecKit 的主线:先把目标说清楚,再让 AI 在需求、设计、开发、评审和部署之间传递结构化上下文。

关键点是:每一步都有明确的输入和输出。

这会显著降低 AI 的不确定性。

第一步:把 PRD 写成 AI 能拆的格式

很多团队的 PRD 其实是给人看的,不是给 AI 执行的。

人可以根据经验补上下文,AI 也会补,但它补出来的东西未必符合你的项目约束。

所以 SpecKit 的第一步,是把 PRD 写成结构化格式。

以用户管理模块为例,一个比较适合 AI 拆解的 PRD 可以这样写:

# 需求:用户管理模块

## 业务背景

管理员需要管理系统用户,包括查看、创建、编辑、删除、权限分配等功能。

## 功能需求

### 1. 用户列表页

- 展示用户名、邮箱、角色、状态、创建时间
- 支持按用户名、邮箱搜索
- 支持按角色、状态筛选
- 支持分页,每页 20 条
- 操作包括查看详情、编辑、删除、重置密码

### 2. 创建用户

- 必填字段:用户名、邮箱、密码、角色
- 选填字段:真实姓名、手机号、部门
- 用户名:3-20 字符,字母数字下划线
- 邮箱:标准邮箱格式
- 密码:8-20 字符,包含大小写字母和数字

### 3. 编辑用户

- 可修改除用户名外的所有字段
- 密码可选修改,为空则不修改

### 4. 删除用户

- 二次确认弹窗
- 删除后刷新列表
- 不能删除当前登录用户

## 非功能需求

- 列表加载时显示 skeleton
- 表单提交时按钮 loading
- 接口报错统一处理

## 接口清单

- GET /api/users
- GET /api/users/:id
- POST /api/users
- PUT /api/users/:id
- DELETE /api/users/:id
- POST /api/users/:id/reset-password

这个 PRD 的重点不是“写得漂亮”,而是每个字段都可以被 AI 映射到技术任务。

比如:

  • “分页每页 20 条”会进入列表状态管理。
  • “密码为空则不修改”会进入编辑表单 schema。
  • “不能删除当前登录用户”会进入操作列逻辑。
  • “按钮 loading”会进入 mutation 状态处理。

这比一句“做一个用户管理”稳定得多。

第二步:让 AI 输出技术任务清单

有了结构化 PRD,就可以让 AI 做需求拆解。

我通常会把 Prompt 写得很明确:

请根据以下 PRD,进行技术任务拆解。

【PRD 内容】
{粘贴 PRD}

【拆解要求】
1. 识别需要开发的页面和组件
2. 每个页面或组件列出:
   - 技术实现要点
   - 依赖的 API
   - 需要的状态管理
   - 表单校验规则
   - 异常处理场景
3. 识别技术难点和风险点
4. 给出开发任务优先级
5. 估算开发工作量

【技术栈】
- React 18 + TypeScript
- Ant Design 5.x
- React Query
- React Hook Form + Zod

理想输出不是一段“开发建议”,而是一份可执行的任务清单:

| 任务 | 优先级 | 工作量 | 依赖 |
| --- | --- | --- | --- |
| API 层开发 | P0 | 0.5 天 | 后端接口 ready |
| 用户列表页 | P0 | 1 天 | API 层 |
| 搜索筛选组件 | P1 | 0.5 天 | 列表页 |
| 创建用户表单 | P1 | 1 天 | API 层 |
| 编辑用户表单 | P1 | 0.5 天 | 创建表单 |
| 删除确认逻辑 | P2 | 0.5 天 | 列表页 |
| 重置密码功能 | P2 | 0.5 天 | API 层 |

inline-prd-to-tasks.png

这张图对应的就是需求拆解阶段:左边是结构化 PRD,右边是按优先级整理后的技术任务。

这一步非常关键。

它把“让 AI 写代码”之前的模糊需求,变成了“让 AI 按任务实现”的明确上下文。

从我的经验看,AI 协作质量提升最快的地方,不是在代码生成阶段,而是在任务拆解阶段。

因为任务拆得越清楚,后面越不需要赌模型理解力。

第三步:先定类型,再写接口

SpecKit 会鼓励先把类型和 API 层固定下来。

这一步很适合 AI 做,因为输入已经足够明确,输出也容易验证。

例如用户类型可以先这样定义:

export interface User {
  id: string;
  username: string;
  email: string;
  realName?: string;
  phone?: string;
  department?: string;
  role: 'admin' | 'user' | 'guest';
  status: 'active' | 'inactive';
  createdAt: string;
  updatedAt: string;
}

export interface UserListParams {
  page: number;
  pageSize: number;
  keyword?: string;
  role?: string;
  status?: string;
}

API 层则需要把 PRD 里的接口清单转成明确函数:

export const getUserList = (
  params: UserListParams,
): Promise<{ list: User[]; total: number }>;

export const getUserDetail = (id: string): Promise<User>;

export const createUser = (data: CreateUserParams): Promise<User>;

export const updateUser = (
  id: string,
  data: UpdateUserParams,
): Promise<User>;

export const deleteUser = (id: string): Promise<void>;

export const resetPassword = (id: string): Promise<void>;

为什么要先做这一层?

因为它是后续页面、表单、测试的共同契约。

如果类型没定,AI 后面生成的组件很容易每个文件都猜一套字段名。最后你会得到很多看似合理、但无法集成的代码。

第四步:列表页要把状态边界说清楚

用户列表页看起来是普通 CRUD,但这里藏着很多容易遗漏的细节:

  • 搜索条件是否进 URL?
  • 搜索后是否回到第一页?
  • 接口失败后是否保留旧数据?
  • 表格 loading 和空状态怎么处理?
  • 分页参数来自 URL 还是组件内部状态?

这些都应该在任务清单里提前说明。

例如:

const [searchParams, setSearchParams] = useSearchParams();

const { data, isLoading, refetch } = useQuery({
  queryKey: ['userList', searchParams.toString()],
  queryFn: () =>
    getUserList({
      page: Number(searchParams.get('page')) || 1,
      pageSize: 20,
      keyword: searchParams.get('keyword') || '',
      role: searchParams.get('role') || '',
      status: searchParams.get('status') || '',
    }),
});

这里有一个很实用的规则:

只要用户刷新页面后仍然希望保留的状态,就优先放进 URL。

对后台系统来说,搜索、筛选、分页通常都属于这类状态。

搜索表单也可以被拆成独立组件:

const handleSearch = (values: SearchFormValues) => {
  setSearchParams({
    ...values,
    page: '1',
  });
};

注意这里的 page: '1'

这是很典型的业务细节:筛选条件变化时,如果不重置页码,用户可能会停留在一个不存在数据的页。

这种细节越早写进 Spec,AI 越不容易漏。

第五步:表单复用要明确“相同”和“不同”

创建用户和编辑用户有大量相似逻辑。

但它们也有几个关键差异:

  • 创建时密码必填,编辑时密码可选。
  • 创建时用户名可输入,编辑时用户名禁用。
  • 编辑时需要初始值回填。
  • 编辑时空密码不提交或表示不修改。

这类场景最忌讳一句“复用表单组件”就结束。

更好的 Spec 是直接描述组件接口:

interface UserFormProps {
  mode: 'create' | 'edit';
  initialData?: User;
  onSubmit: (data: UserFormData) => Promise<void>;
}

然后再把校验规则写清楚:

const createUserSchema = z
  .object({
    username: z
      .string()
      .min(3, '用户名至少 3 个字符')
      .max(20, '用户名最多 20 个字符')
      .regex(/^[a-zA-Z0-9_]+$/, '用户名只能包含字母、数字、下划线'),
    email: z.string().email('请输入正确的邮箱格式'),
    password: z
      .string()
      .min(8, '密码至少 8 个字符')
      .max(20, '密码最多 20 个字符')
      .regex(/(?=.*[a-z])(?=.*[A-Z])(?=.*\d)/, '密码必须包含大小写字母和数字'),
    confirmPassword: z.string(),
    role: z.enum(['admin', 'user', 'guest']),
    realName: z.string().optional(),
    phone: z.string().regex(/^1[3-9]\d{9}$/, '请输入正确的手机号').optional().or(z.literal('')),
    department: z.string().optional(),
  })
  .refine((data) => data.password === data.confirmPassword, {
    message: '两次输入的密码不一致',
    path: ['confirmPassword'],
  });

这里还有一个简单但有用的判断标准:

如果两个表单 80% 一样,但校验规则有差异,先复用组件,再分离 schema。

不要为了“复用”把所有逻辑揉进一堆 if (mode === 'edit')

SpecKit 不是鼓励过度抽象,而是鼓励把差异显式写出来。

第六步:UI 还原不要只给截图

很多人让 AI 还原设计稿时,只丢一张截图。

这当然能生成页面,但稳定性一般。

更好的做法是让 AI 先拆组件结构:

根据这个设计稿,帮我拆解组件结构。

【要求】
1. 识别页面包含哪些组件
2. 说明组件嵌套关系
3. 判断组件是否可复用
4. 给出建议文件结构

用户列表页可以拆成:

UserListPage
├── PageHeader
├── SearchForm
├── UserTable
│   ├── StatusTag
│   ├── RoleTag
│   └── ActionButtons
└── Pagination

CreateUserModal
└── UserForm

EditUserModal
└── UserForm

然后再提取样式规范:

export const colors = {
  primary: '#1890ff',
  success: '#52c41a',
  warning: '#faad14',
  error: '#ff4d4f',
  textPrimary: '#262626',
  textSecondary: '#8c8c8c',
  border: '#d9d9d9',
  background: '#f0f2f5',
};

export const spacing = {
  xs: 8,
  sm: 12,
  md: 16,
  lg: 24,
  xl: 32,
};

inline-ui-components.png

把设计稿拆成 UI、组件树和 design tokens 后,AI 生成的代码会更接近工程里的真实边界。

这一步的价值是把“视觉还原”变成“设计 token + 组件结构 + 代码实现”。

AI 不再只是看图写 CSS,而是在明确边界内生成页面。

第七步:开发阶段用测试约束 AI

SpecKit 里我最推荐的一种写法,是先让 AI 写测试,再让 AI 实现代码。

例如表格组件可以先写测试:

describe('UserTable', () => {
  it('应该正确渲染用户列表', () => {
    const users = [mockUser1, mockUser2];

    render(<UserTable data={users} />);

    expect(screen.getByText(mockUser1.username)).toBeInTheDocument();
    expect(screen.getByText(mockUser2.username)).toBeInTheDocument();
  });

  it('点击编辑按钮应该触发回调', () => {
    const onEdit = jest.fn();

    render(<UserTable data={[mockUser1]} onEdit={onEdit} />);

    fireEvent.click(screen.getByText('编辑'));

    expect(onEdit).toHaveBeenCalledWith(mockUser1);
  });

  it('删除当前用户时应该禁用删除按钮', () => {
    render(<UserTable data={[mockUser1]} currentUserId="user-1" />);

    expect(screen.getByText('删除')).toBeDisabled();
  });
});

然后再让 AI 根据测试实现组件:

根据以下测试用例,生成 UserTable 组件代码。

【要求】
1. 所有测试通过
2. 使用 TypeScript
3. Props 类型完整
4. 不引入额外抽象

inline-tests-to-code.png

先有测试,再让 AI 补实现,等于给模型加了一条可验证的轨道。

这样做有两个好处:

  1. AI 的输出更聚焦。
  2. 你有一个客观验证标准。

没有测试时,我们只能说“看起来能用”。有测试时,我们可以说“这个行为已经被覆盖”。

第八步:Code Review 要给维度,不要只说“帮我看看”

“帮我 Review 一下代码”是一个很弱的 Prompt。

AI 会提建议,但建议很可能散。

SpecKit 更推荐把 Review 维度固定下来:

请 review 以下代码,从这几个维度评估:

1. 代码质量
   - 是否符合项目规范
   - 是否有代码坏味道
   - 是否有潜在 bug

2. 性能
   - 是否有不必要的 re-render
   - 是否有内存泄漏风险
   - 是否需要优化

3. 安全
   - 是否有 XSS 风险
   - 敏感信息是否泄露

4. 可维护性
   - 代码是否易读
   - 是否有重复代码
   - 注释是否充分

请给出具体修改建议,必要时用 diff 展示。

这类 Review 更容易得到可执行的结果。

比如:

- return <Table dataSource={data} />;
+ return <Table dataSource={data || []} />;

或者:

- }, []);
+ }, [userId]);

inline-review-quality-gate.png

Review 不是终点,它应该和测试、部署监控一起组成上线前的质量门。

真正有价值的 AI Review,不是“这段代码整体不错”,而是能指出具体位置、具体风险和具体改法。

SpecKit 的收益来自“可重复”

在源文档的案例里,用户标签功能从 PRD 到上线被拆成了 6 个阶段:

  1. 需求拆解:30 分钟。
  2. UI 还原:1 小时。
  3. 组件开发:1.5 天。
  4. Code Review:30 分钟。
  5. 测试:2 小时。
  6. 上线:10 分钟。

整体大约 2 天完成,而传统方式可能需要 4-5 天。

这个数字不一定适用于所有团队,但背后的逻辑很值得参考:

指标传统流程SpecKit 流程改善方向
需求理解靠反复沟通结构化拆解减少歧义
代码生成靠上下文猜测按任务实现减少返工
Review人工逐行看AI 先筛一遍提高效率
测试后补测试先写行为约束更容易验证
上线临时检查清单化交付降低遗漏

SpecKit 的核心收益不是“AI 一次生成完所有代码”。

它真正提高的是协作可重复性。

当流程稳定后,你可以把它用于多个类似业务模块,比如:

  • 用户管理
  • 角色权限
  • 标签系统
  • 审批流
  • 配置后台
  • 数据看板

这些需求都不是算法难题,但细节多、协作链路长,非常适合用 SpecKit 降低沟通成本。

什么时候不适合用 SpecKit?

也不是所有需求都适合。

SpecKit 更适合:

  • 标准 CRUD 需求。
  • 重复性较高的业务模块。
  • 多人协作项目。
  • 有明确设计稿和接口约束的前端页面。
  • 需要沉淀团队规范的项目。

不太适合:

  • 探索性原型。
  • 高度创新的交互实验。
  • 需求还没有成形的早期想法。
  • 只需要几十行代码的小改动。

如果需求本身还在探索,过早套 SpecKit 反而可能增加负担。

我的建议是:先从一个小而完整的需求试点。

比如一个后台列表页,一个设置表单,或者一个标签管理模块。

等团队跑顺之后,再把 Prompt 模板、代码规范、测试模板、Review 清单逐步沉淀下来。

最后:让 AI 从“会写代码”变成“会参与交付”

AI Coding 最容易被误解成“让 AI 多写点代码”。

但真实项目里,代码只是交付链路的一部分。

需求是否清楚、组件边界是否合理、状态是否可恢复、测试是否覆盖、Review 是否有效、上线是否有清单,这些才决定最终质量。

SpecKit 的价值就在这里:

它不是替代开发者,而是把开发者的判断前置,把 AI 的能力放进可验证的流程里。

如果你想在团队里更稳定地使用 AI 协作,可以从这 5 件事开始:

  1. 把 PRD 改成结构化模板。
  2. 让 AI 先输出技术任务清单。
  3. 每个任务都写清输入、输出和验收标准。
  4. 复杂组件先写测试,再生成实现。
  5. Review 固定维度,并要求给出可执行修改建议。

当这些动作成为习惯后,AI 就不再只是一个“代码补全工具”。

它会变成需求拆解、UI 还原、组件开发、Review 和上线检查里的协作者。

这才是 SpecKit 真正想解决的问题。

欢迎在评论区聊聊:

  • 你现在会让 AI 参与需求拆解吗?
  • 你团队里最适合 SpecKit 的模块是什么?
  • 你更想先标准化 PRD、UI 还原,还是 Code Review?