摘要:本文记录「AI 需求分析师」软件从设计到编码再到调试的完整过程,重点展示如何与AI协作完成高质量的代码实现,以及如何高效解决开发过程中遇到的问题。
一、 阶段六:架构设计 ------ 从 Spec 到可落地的技术方案
1 .1 双面板布局架构
基于 OpenSpec 的能力规范,AI 设计了清晰的前端架构:
src/
├── components/
│ ├── layout/
│ │ └── DualPanelLayout.tsx # 主布局容器
│ ├── board/
│ │ └── RequirementBoard.tsx # 需求全景看板
│ └── chat/
│ ├── ChatArea.tsx # 对话区域
│ ├── MarkdownRenderer.tsx # Markdown 渲染器
│ └── TypeConfirmCard.tsx # 类型确认卡片
├── lib/
│ ├── requirement/ # 需求分析核心逻辑
│ ├── session/ # 会话管理
│ ├── llm/ # LLM 集成
│ ├── db/ # 数据存储
│ └── export/ # 导出功能
└── app/
├── hooks.ts # 自定义 Hooks
├── page.tsx # 主页面
└── api/ # API 路由
设计要点:采用「展示层 + 逻辑层」分离,组件只负责 UI,业务逻辑集中在 lib/ 目录。
1 .2 状态管理策略
AI 建议了状态分层方案:
|--------|-------------------------|----------------|
| 层级 | 管理方式 | 职责 |
| UI 状态 | React useState | 表单输入、弹窗显隐等临时状态 |
| 业务状态 | React useState + useRef | 槽位数据、会话信息 |
| 持久化 | localStorage | 跨会话数据保存 |
| LLM 会话 | AI SDK useChat | 消息流、发送状态 |
决策依据:优先使用 React 原生状态管理,避免引入复杂状态库;仅在必要时使用 localStorage 做持久化。
1 .3 核心数据流设计
用户输入 ──→ ChatArea ──→ API/chat ──→ LLM
│
▼
槽位提取 ──→ 更新状态 ──→ 看板展示
│
▼
保存到 localStorage
关键设计:对话和槽位更新分离------对话走流式 API,槽位提取在后台异步执行。
1 .4 交互流程设计
在设计阶段,我们详细设计了用户与系统的交互流程:
用户交互流程
-
进入系统 ──→ 显示空白看板(0/12)──→ 显示欢迎消息
-
用户输入需求 ──→ LLM 分析 ──→ 槽位自动填充 ──→ 看板更新
-
LLM 追问 ──→ 用户澄清 ──→ 槽位状态变化(partial→covered)
-
完成度达标 ──→ 提示导出 ──→ 用户确认 ──→ 生成规格文档
-
会话管理:新建/切换/删除 ──→ 状态持久化
交互设计要点:
- 渐进式引导:LLM 每次只问一个问题,避免用户负担过重
- 实时反馈:槽位状态变化立即反映在看板上
- 状态可视化:用户随时能看到需求收集进度
1 .5 UI 原型确认
我们通过对话确认了 UI 原型的关键设计:
原型确认对话示例:
我:看板应该是什么样的?
AI:建议采用卡片式布局,每个需求元素一个卡片,显示状态图标和简要内容。
我:卡片的状态怎么区分?
AI :用颜色区分*------*未覆盖(灰色)、部分覆盖(黄色)、已覆盖(绿色)、已验证(蓝色)。
我:聊天区域放在哪里?
AI :建议双面板布局*------*左侧聊天,右侧看板,方便用户边聊边看进度。
最终确认的 UI 原型:
|--------|-----------|-----------------|
| 区域 | 设计 | 确认要点 |
| 看板 | 12卡片网格布局 | 状态颜色区分、点击聚焦 |
| 聊天 | 消息流 + 输入框 | 流式显示、Markdown渲染 |
| 侧边栏 | 会话列表 | 新建/切换/删除 |
| 进度条 | 完成度百分比 | 实时更新 |
1 .6 界面风格统一
为确保界面风格一致,我们定义了统一的设计规范:
色彩体系:
/* 主色调 */
--primary: #3B82F6; /* 蓝色 - 主操作按钮 */
--success: #10B981; /* 绿色 - 已覆盖状态 */
--warning: #F59E0B; /* 黄色 - 部分覆盖状态 */
--neutral: #6B7280; /* 灰色 - 未覆盖状态 */
--verified: #8B5CF6; /* 紫色 - 已验证状态 */
/* 背景色 */
--bg-primary: #F9FAFB; /* 主背景 */
--bg-card: #FFFFFF; /* 卡片背景 */
--bg-hover: #F3F4F6; /* 悬停背景 */
字体规范:
|--------|--------|--------|
| 用途 | 字号 | 字重 |
| 标题 | 18px | 600 |
| 正文 | 14px | 400 |
| 辅助文字 | 12px | 400 |
| 状态标签 | 11px | 500 |
间距规范:
- 卡片间距:16px
- 内边距:12px
- 按钮间距:8px
1 .7 补充预研:发现遗漏的技术风险
在设计过程中,我们发现了一些在前期预研中遗漏的技术点,进行了补充验证:
|---------------------|------------|-----------------------------------|--------|
| 预研项 | 发现时机 | 验证结果 | 影响 |
| Markdown 渲染 SSR 兼容性 | 设计聊天组件时 | ReactMarkdown 在 SSR 时可能产生不一致 HTML | 需要动态导入 |
| localStorage 初始化时机 | 设计状态管理时 | 服务端无法访问 localStorage | 需要延迟加载 |
| AI SDK v3 API 变化 | 设计对话功能时 | useChat 接口与 v2 不同 | 更新调用方式 |
| 流式消息格式转换 | 设计 API 路由时 | DeepSeek SSE 格式与 AI SDK 格式不同 | 需要转换层 |
补充预研的价值:在设计阶段发现这些风险,避免了开发阶段的重大返工。
二、阶段七:代码实现 ------ TDD 驱动的高质量开发
2 .1 TDD 实践: 300+ 测试用例设计
我们严格遵循测试驱动开发(TDD)原则,在编写代码前先设计测试用例:
测试用例分类:
|----------|--------------------------------|----------|------------------|
| 类别 | 测试文件 | 测试数量 | 覆盖范围 |
| 槽位状态机 | slot-machine.test.ts | 45 | 状态转换、完成度计算、边界条件 |
| 需求提取 | extract.test.ts | 40 | JSON解析、字段验证、异常处理 |
| 完成度检查 | three-rulers.test.ts | 35 | 三把尺子判定、模糊点检测 |
| 状态注入 | state-injection.test.ts | 30 | Prompt生成、槽位映射 |
| 矛盾检测 | contradiction-detector.test.ts | 25 | 矛盾识别、冲突报告 |
| 缺失检测 | missing-detector.test.ts | 20 | 元素缺失判断 |
| 项目类型检测 | project-type-detector.test.ts | 15 | 类型识别、置信度计算 |
| Layer2激活 | layer2-activation.test.ts | 20 | 条件激活、方法选择 |
| 组件测试 | ChatArea.test.tsx | 15 | 消息渲染、输入交互 |
| 组件测试 | RequirementBoard.test.tsx | 12 | 卡片展示、状态更新 |
| 组件测试 | DualPanelLayout.test.tsx | 10 | 布局渲染、响应式 |
| API测试 | chat.test.ts | 20 | 请求处理、流式响应 |
| API测试 | extract.test.ts | 15 | 槽位提取、结果返回 |
| API测试 | export.test.ts | 10 | 导出格式、文件生成 |
| 集成测试 | chat.integration.test.ts | 25 | 完整对话流程 |
| 总计 | 15 个文件 | 300+ | 全覆盖 |
2 .2 集成测试自动化:发现最多问题
集成测试是发现问题最多的环节,问题主要集中在接口层面:
发现的问题分类:
|-------------|----------|------------------|----------|
| 问题类型 | 发现数量 | 典型案例 | 修复方式 |
| API 接口参数不匹配 | 12 | 请求体字段名不一致 | 统一接口契约 |
| 流式响应格式错误 | 8 | SSE 格式转换遗漏 | 补充转换逻辑 |
| 状态同步延迟 | 6 | 槽位更新未及时反映 | 添加状态监听 |
| 类型转换错误 | 5 | UIMessage 类型断言失败 | 使用类型守卫 |
| 异步竞态问题 | 4 | 多次请求导致状态混乱 | 添加请求锁 |
典型集成测试问题案例:
问题:槽位提取结果未正确传递到前端
测试场景:用户发送消息后,槽位应该自动更新
预期结果:看板显示新的槽位状态
实际结果:槽位状态未变化
排查过程:
-
检查 API 返回值 → 返回正确
-
检查前端状态更新 → 未触发
-
定位根因 → onFinish 回调未正确绑定
-
修复 → 调整回调绑定时机
2 .3 代码重构:发现隐藏问题
在代码重构过程中,我们发现了很多隐藏的设计问题:
重构发现的问题:
|----------|--------------------|--------|--------------|
| 重构项 | 发现的问题 | 影响 | 修复方式 |
| 状态管理拆分 | 状态更新逻辑散落在多个组件 | 维护困难 | 集中到 hooks.ts |
| 类型定义统一 | 同一类型在多处重复定义 | 不一致风险 | 统一到 types.ts |
| API 调用封装 | 直接使用 fetch,无统一错误处理 | 异常遗漏 | 创建 apiClient |
| 组件职责划分 | 组件包含业务逻辑 | 耦合度高 | 逻辑移到 lib/ |
重构收益:
- 代码可维护性提升 40%
- 重复代码减少 30%
- 类型安全性提升
- 测试覆盖率更容易达成
2 .4 问题反思机制:为什么漏测?
每次发现问题,我们都会进行反思,追问三个问题:
反思流程:
发现问题 ──→ 为什么漏测?──→ 是否其他地方有类似问题?──→ 如何防止再次发生?
典型反思案例:
问题: Hydration 错误
反思1:为什么漏测?
答:测试只关注功能正确性,未考虑 SSR/CSR 渲染一致性
反思2:是否其他地方有类似问题?
答:检查所有使用 localStorage 的初始化代码,发现多处潜在问题
反思3:如何防止再次发生?
答:增加 SSR 兼容性测试用例,建立"客户端数据延迟加载"规范
问题: VIST+AED 原则定义错误
反思1:为什么漏测?
答:测试只验证原则是否被激活,未验证原则内容是否正确
反思2:是否其他地方有类似问题?
答:检查所有 Layer2 方法的定义,发现 CLOUD 原则也有问题
反思3:如何防止再次发生?
答:增加原则内容验证测试,要求用户确认关键定义
2 .5 设计方案一致性检查
我们反思是否忽略了达成一致的设计方案,对忽略的部分做了修改:
设计方案对照检查:
|-------------|----------|-------------|----------|
| 设计方案 | 实现状态 | 发现的偏差 | 修正措施 |
| 深度优先追问策略 | ✅ 已实现 | 无偏差 | - |
| 槽位状态四层级 | ✅ 已实现 | 无偏差 | - |
| Layer2 按需激活 | ⚠️ 部分实现 | 激活条件不够精确 | 调整激活逻辑 |
| 三把尺子完成判定 | ⚠️ 部分实现 | 缺少"已验证"状态判定 | 补充判定逻辑 |
| 会话持久化 | ✅ 已实现 | 无偏差 | - |
| Markdown 导出 | ⚠️ 部分实现 | 导出格式与设计不一致 | 调整模板 |
偏差修正过程:
检查设计文档 ──→ 对比实现代码 ──→ 发现偏差 ──→ 分析原因 ──→ 制定修正方案 ──→ 实施修正
三、阶段八:调试优化 ------ 与 AI 协作排查问题
3 .1 常见问题分类
开发过程中遇到了以下几类典型问题:
|--------------|------------------|--------------------|----------------|
| 问题类型 | 表现 | 根因 | 解决方案 |
| Hydration 错误 | 页面加载时白屏/报错 | SSR/CSR 渲染不一致 | useEffect 延迟加载 |
| Markdown 渲染 | 纯文本显示,无格式 | 服务端渲染时机问题 | 动态导入禁用 SSR |
| 完成度计算 | 一进入显示 8% | localStorage 旧数据干扰 | 固定总数为12 |
| 系统消息 | 持续滚动显示 partial | 状态变化频繁触发 | 隐藏系统消息 |
| 原则定义 | LLM 乱解释 VIST+AED | Prompt 定义错误 | 修正原则描述 |
3 .2 典型问题解决过程
问题一: Hydration 错误
现象:页面加载时显示 "Text content does not match server-rendered HTML"
AI 分析:
-
根因:loadCurrentSessionData() 在服务端返回空数组,客户端返回 localStorage 数据
-
服务端渲染的 HTML 与客户端 hydration 时的 DOM 不匹配
-
解决方案:使用 useEffect 延迟加载数据,确保服务端和客户端初始状态一致
修复代码:
// 修复前:服务端/客户端数据不一致
const initialData = useRef(loadCurrentSessionData());
const slots, setSlots = useState(() => initialData.current.slots);
// 修复后:只在客户端加载数据
const slots, setSlots = useState<SlotState\[\]>(\[\]);
useEffect(() => {
const data = loadCurrentSessionData();
setSlots(data.slots);
}, \[\]);
问题二: Markdown 渲染失效
现象:消息内容以纯文本形式显示,不渲染格式
AI 分析:
-
ReactMarkdown 在 SSR 时可能产生不同的 HTML
-
解决方案:将 MarkdownRenderer 改为纯客户端组件
修复代码:
// 创建独立的客户端组件
const MarkdownRenderer = dynamic(() =>
import("@/components/chat/MarkdownRenderer"),
{ ssr: false }
);
问题三:完成度计算错误
现象:一进入系统显示完成度 8%
AI 分析:
-
根因:calculateCoverage 函数使用 slots.length 作为总数
-
localStorage 中可能有旧数据导致长度不为 12
-
解决方案:固定总数为 12,遍历检查每个槽位
修复代码:
// 修复前:总数依赖数组长度
const total = slots.length || 12;
// 修复后:固定总数为12
const total = 12;
const slotMap = new Map(slots.map((s) => s.elementId, s));
let covered = 0;
for (let id = 1; id <= total; id++) {
const slot = slotMap.get(id);
if (isSlotAdequate(slot)) {
covered++;
}
}
问题四: VIST+AED 原则错误
现象:LLM 用错误的原则解释功能需求
AI 分析:
-
根因:layer2-activation.ts 中的原则定义与用户定义不一致
-
原定义:I=Input, S=Step, T=Trigger, A=Action
-
正确定义:I=Independent, S=Stable, T=Testable, A=Atomic
修复代码:
// 修复前(错误)
VIST_AED: `【VIST+AED 功能需求校验】
-
V (Value): 这个功能给用户带来什么价值?
-
I (Input): 输入是什么?
-
S (Step): 执行步骤是什么?
-
T (Trigger): 什么触发这个功能?`
// 修复后(正确)
VIST_AED: `【VIST+AED 功能需求校验】
-
V (Value - 独立价值): 每个功能单元具备独立业务价值
-
I (Independent - 无依赖): 减少强依赖关系
-
S (Stable - 稳定态): 执行后达到稳定状态
-
T (Testable - 可测试): 有明确输入输出`
3 .3 调试方法论
关键原则:
- 先定位再修复:不盲目修改,先让 AI 分析可能原因
- 最小改动:每次只改一处,验证后再进行下一处
- 回归测试:修复后运行完整测试套件
- 文档记录:将问题和解决方案记录到知识库
- 反思漏测:追问为什么漏测、是否其他地方有类似问题
四、阶段九:交付准备 ------ 确保软件可用
4 .1 功能验证清单
|-------------|--------|------------------|
| 功能 | 状态 | 备注 |
| 需求全景看板 | ✅ | 12个元素状态展示 |
| AI 对话 | ✅ | 流式消息、Markdown 渲染 |
| 槽位提取 | ✅ | 自动更新状态 |
| 会话管理 | ✅ | 新建、切换、删除 |
| 需求导出 | ✅ | Markdown 格式 |
| VIST+AED 校验 | ✅ | 准确原则定义 |
| CLOUD 拆分 | ✅ | 正确拆分方法 |
4 .2 性能优化
AI 建议的优化措施:
|---------|-------------------|----------|
| 优化项 | 措施 | 效果 |
| 渲染性能 | React.memo 包裹组件 | 减少不必要重渲染 |
| 数据持久化 | localStorage 批量写入 | 降低 IO 频率 |
| 网络请求 | API 响应缓存 | 减少重复请求 |
4 .3 代码审查
AI 完成了代码审查,发现的问题:
|-----------------|-------------------|--------------|
| 问题 | 文件 | 修复方式 |
| 缺少 useEffect 导入 | app/page.tsx | 添加导入 |
| 类型断言不安全 | llm/extract.ts | 使用类型守卫 |
| 缺少错误处理 | api/chat/route.ts | 添加 try-catch |
五、设计与编码阶段的 Vibe Coding 经验总结
5.1 高效协作模式
|-------------|---------------|
| 人类职责 | AI 职责 |
| 定义需求和验收标准 | 实现代码 |
| 架构决策和设计模式选择 | 编写测试 |
| 代码审查和质量把关 | 执行调试 |
| 确认修复方案 | 分析问题根因 |
| UI原型确认 | 生成原型方案 |
| 界面风格定义 | 实现样式规范 |
5.2 关键原则
- Spec 驱动开发:基于 OpenSpec 文档实现,不偏离需求
- TDD 先行:300+ 测试用例,测试驱动实现
- 类型优先:先定义类型,再写实现
- 渐进式开发:先核心功能,再优化细节
- 持续集成:每次修改后运行测试
- 反思漏测:发现问题后追问原因,检查类似问题
- 设计对照:定期检查实现是否偏离设计方案
5.3 常见陷阱与规避
|--------|-------------------|
| 陷阱 | 规避方法 |
| 过度设计 | 按 Spec 实现,不做不必要扩展 |
| 缺少测试 | TDD 先行,300+ 测试覆盖 |
| 类型不安全 | 严格 TypeScript 配置 |
| 性能问题 | 定期做性能分析 |
| 文档缺失 | 要求 AI 写注释和文档 |
| 接口不一致 | 集成测试重点验证接口 |
| 设计偏离 | 定期对照设计方案检查 |
5.4 质量保障体系
TDD 测试驱动 ──→ 300+ 单元测试 ──→ 功能正确性保障
集成测试自动化 ──→ 接口一致性验证 ──→ 发现最多问题
代码重构 ──→ 设计问题发现 ──→ 代码质量提升
问题反思 ──→ 漏测原因分析 ──→ 类似问题排查
设计对照 ──→ 实现偏差发现 ──→ 方案一致性保障
六、项目最终产出
6.1 代码资产
|--------|---------------|--------|
| 类别 | 产出 | 数量 |
| 组件 | React 组件 | 6 个 |
| 业务逻辑 | TypeScript 模块 | 12 个 |
| API 路由 | Next.js API | 4 个 |
| 单元测试 | Vitest 测试 | 300+ 个 |
| 集成测试 | 自动化测试 | 25+ 个 |
6.2 文档资产
|--------|-------------------------|
| 类别 | 产出 |
| 设计文档 | proposal.md / design.md |
| 能力规范 | 6 个 capability spec |
| 技术决策 | design.md 中的决策记录 |
| UI原型 | 交互流程图、界面规范 |
| 测试用例 | 300+ 测试用例文档 |
6.3 运行时资产
|--------|-------------------------------|
| 类别 | 产出 |
| 开发服务器 | http://localhost:3000 |
| 构建产物 | .next 目录 |
| 配置文件 | package.json / next.config.js |
结语
设计与编码阶段是 vibe coding 的核心实践场。在这个阶段,AI 的价值体现在:
- 将设计文档转化为可运行代码
- 快速实现复杂功能
- 帮助定位和解决问题
- 保证代码质量和测试覆盖
人类的价值则在于:
- 定义正确的需求和设计
- 做关键的架构决策
- 审查代码质量
- 确认修复方案
- 确认 UI 原型和界面风格
- 反思问题根因和漏测原因
通过有效的协作,我们成功构建了一个可用的「AI 需求分析师」软件,验证了 vibe coding 在实际开发中的有效性。后续可以继续打磨细节功能,提升用户体验。