# FinOps Agent Platform — Phase 1 任务拆解评审报告

**评审文档**: [finops-agent-phase1-tasks.md](./finops-agent-phase1-tasks.md)
**设计文档**: [finops-agent-platform-design.md](./finops-agent-platform-design.md)
**评审日期**: 2026-03-10
**评审角色**: Senior Architect Reviewer

---

## 目录

1. [设计完整性审查](#1-设计完整性审查)
2. [技术方案挑战](#2-技术方案挑战)
3. [任务拆解合理性](#3-任务拆解合理性)
4. [验收标准评估](#4-验收标准评估)
5. [风险补充](#5-风险补充)
6. [改进建议汇总](#6-改进建议汇总)

---

## 1. 设计完整性审查

### 1.1 Phase 1 需求覆盖度

对照设计文档 §11 Phase 1 路线图，逐项检查任务拆解的覆盖情况：

| 设计文档 Phase 1 需求 | 任务拆解覆盖 | 评估 |
|---|---|---|
| PostgreSQL + Prisma 集成 | Step 1 完整覆盖 | OK |
| docker-compose 配置 | Step 1.3 覆盖 | OK |
| NextAuth 回调自动创建 User | Step 2.1-2.2 覆盖 | OK |
| `/api/sessions` CRUD | Step 3.1-3.2 覆盖 | OK |
| 前端 localStorage 迁移 | Step 3.4-3.7 覆盖 | OK |
| chart-suggester 推荐图表 | Step 4.2 覆盖 | OK |
| result-chart 渲染 | Step 4.4 覆盖 | OK |
| Tab 切换 (Table/Chart) | Step 4.5 覆盖 | OK |
| few-shot 示例库 | Step 5.5 覆盖 | OK |
| 双模式交互 (Analyst/Data) | Step 5.1-5.3 覆盖 | OK |
| 持续对话 | Step 5.4 覆盖 | OK |
| xlsx 导出 | Step 6.1-6.2 覆盖 | OK |
| zod 校验重构 | Step 6.3-6.4 覆盖 | OK |

**结论**: 所有 Phase 1 需求均已在任务拆解中覆盖，无遗漏。

### 1.2 设计文档与任务拆解的差异

**差异 1: UserProfile 模型**

设计文档 §5.2 定义了 `UserProfile` 模型（用户画像），属于 Phase 1 的 Prisma Schema 范围。但任务拆解 Step 1.2 明确指出"Phase 1 需要的 3 个模型: User, ChatSession, Message"，未包含 UserProfile。

- **影响**: 如果 Phase 2/3 需要 UserProfile，需要新增 migration。这是合理的增量策略，但需要确认设计文档中 UserProfile 的 Phase 归属。
- **建议**: 设计文档 §7.6 用户画像系统没有明确标注 Phase，建议在设计文档中补充 Phase 归属（建议归入 Phase 3 飞书集成）。

**差异 2: 架构图中的 Chart Renderer 名称**

设计文档 §3.1 架构图中 Chart Renderer 仍标注为 "Recharts SSR"，但 §4.2 已改为 ECharts。这是文档内部不一致，不影响任务拆解，但应修正。

**差异 3: 分析模式流程差异**

设计文档 §6.1 描述分析模式为 "NL→SQL→执行→将结果传回 LLM→LLM 输出分析洞察"，即 chat API 内部会执行 SQL 并做二次分析。但任务拆解 Step 5.2 描述为 "NL→SQL→执行→将结果传回 LLM 做二次分析"，这里的"执行"是在 chat API 内部还是客户端触发后再传回？

当前代码中，SQL 执行是由客户端触发 `/api/bigquery` 完成的（用户点击 Run Query 按钮）。如果分析模式需要在 `/api/chat` 内部自动执行 SQL 并分析，这是对现有架构的**重大改动** — `/api/chat` 需要调用 BigQuery，打破了当前 chat 和 bigquery 两个 API 的职责分离。

- **影响范围**: Step 5.2 的工作量可能被低估
- **建议**: 明确分析模式的执行流程。如果是 server-side 自动执行，需要在 Step 5 中显式列出 `/api/chat` 调用 BigQuery 的任务

### 1.3 Prisma Schema 一致性

对比设计文档 §5.2 的 Prisma Schema 与任务拆解 Step 1.2 的描述：

- 设计文档定义了 9 个模型（User, UserProfile, ChatSession, Message, Subscription, AnomalyRule, AnomalyAlert, Report, Notification）
- 任务拆解正确地选择了仅创建 Phase 1 需要的 3 个模型
- **潜在问题**: Step 1.2 说"参见 platform-design.md §5.2"，但 §5.2 的 User 模型包含 `sessions`, `subscriptions`, `anomalyRules`, `notifications`, `profile` 等关联字段。Phase 1 创建 User 时，不能直接复制 §5.2 的定义，否则会因为引用了不存在的模型而报错。需要裁剪为仅包含 `sessions` 关联。
- **建议**: 在任务拆解中显式列出 Phase 1 版本的 Prisma Schema，而不仅是引用设计文档

---

## 2. 技术方案挑战

### 2.1 Prisma vs Drizzle ORM

- **当前方案**: Prisma（`prisma` + `@prisma/client`），设计文档理由为"Next.js 生态首选 ORM"
- **挑战**:
  - Prisma Client 需要 build-time 代码生成（`prisma generate`），增加 Docker 构建步骤
  - 生成的 client 体积较大（~2-5MB），影响 Docker 镜像大小
  - 冷启动时需要初始化连接池，在 serverless-like 环境（Next.js API Routes 可能被 cold-start）有延迟开销
  - Prisma 的查询引擎是 Rust binary（Query Engine），需要匹配部署平台架构（当前 Dockerfile 用 `node:22-bullseye-slim`，需要确认 binary 兼容性）
  - Prisma Migrate 在生产中需要额外的 `prisma migrate deploy` 步骤
- **替代方案**: Drizzle ORM
  - 纯 TypeScript，无 codegen，无 binary 依赖
  - 体积极小（~50KB），零冷启动开销
  - SQL-like API，更接近手写 SQL 的思维模型
  - Migration 通过 `drizzle-kit` 管理，同样支持声明式 schema
  - 缺点: 生态相对年轻，文档不如 Prisma 丰富
- **建议**: **维持 Prisma**。理由如下：
  1. 项目用户量极小（~50 人），Prisma 的性能开销可以忽略
  2. Prisma 的声明式 Schema + 自动 migration 对 Phase 2/3 的复杂模型（Subscription, AnomalyRule 等多表关联）更有优势
  3. Prisma Studio 是很好的调试工具
  4. 团队熟悉度 — Prisma 是 Next.js 社区事实标准
  5. 但需要在 Dockerfile 中正确处理 `prisma generate`，Step 1 应显式列出 Dockerfile 修改任务
- **影响范围**: 如果切换 Drizzle，Step 1 全部重写，Step 2/3/6 的数据库调用代码变化

### 2.2 ECharts vs 替代方案

- **当前方案**: Apache ECharts (`echarts` + `echarts-for-react`)，替代已安装但未使用的 Recharts
- **挑战**:
  - ECharts 全量 bundle 约 800KB+ (gzipped ~280KB)，即使做 dynamic import 和 tree-shaking，仍然偏大
  - `echarts-for-react` 社区维护频率不高（最近几年更新较少），React 18+ 兼容性需验证
  - ECharts 的 option-based API 与 React 声明式模型有阻抗不匹配
  - SSR 支持差 — ECharts 依赖 Canvas/SVG DOM，server-side 渲染困难（Phase 2 报告引擎需要 server-side 图表）
- **替代方案对比**:

  | 库 | Bundle Size | React 集成 | SSR | 图表类型 | 交互能力 |
  |---|---|---|---|---|---|
  | ECharts | ~800KB | 中等 (wrapper) | 差 | 极丰富 | 极强 |
  | Recharts | ~400KB | 优 (原生 React) | 中 | 中等 | 中等 |
  | Nivo | ~300KB (单图表) | 优 (原生 React) | 优 (SVG) | 丰富 | 中等 |
  | Plotly.js | ~1MB+ | 中 (wrapper) | 差 | 极丰富 | 极强 |
  | Chart.js | ~200KB | 良 (react-chartjs-2) | 差 | 中等 | 中等 |

- **建议**: **维持 ECharts，但需注意两点**:
  1. Phase 1 的使用场景（折线图、柱状图、饼图）其实 Recharts 完全胜任，且已安装。ECharts 的核心优势（热力图、大数据量渲染、丰富交互）在 Phase 1 并不急需
  2. 但考虑到 Phase 2/3 的报告引擎可能需要更复杂的图表，以及 ECharts 的 tooltip/dataZoom/brush 等交互能力确实更强，提前切换避免二次迁移是合理的
  3. **必须做 tree-shaking**: 不要 `import * as echarts from 'echarts'`，而是按需导入 `echarts/core` + 需要的图表类型和组件
  4. Phase 2 报告引擎的 server-side 图表渲染需要另外的方案（如 `echarts` 的 `node-canvas` 后端或改用 SVG 方案），这个风险在 Phase 1 文档中未提及
- **影响范围**: Step 4 全部，如果改回 Recharts 需重写 chart-suggester 输出格式（Recharts props vs ECharts option）

### 2.3 PostgreSQL vs SQLite

- **当前方案**: PostgreSQL 16 (Alpine)，Docker Compose 本地运行，生产用 db9.ai 托管
- **挑战**:
  - 项目用户量约 50 人，数据量极低（参考设计文档 §5.3：User ~50, ChatSession ~1K/月, Message ~5K/月）
  - PostgreSQL 需要额外的 Docker 容器（增加运维复杂度、资源消耗）
  - 开发者本地需要运行 `docker-compose up postgres` 才能开始开发
  - 生产部署需要托管 PostgreSQL 实例（db9.ai 或 Cloud SQL），增加成本
- **替代方案**: SQLite
  - 嵌入式数据库，无需额外进程
  - `better-sqlite3` 同步 API，极低延迟
  - Prisma 完整支持 SQLite（`provider = "sqlite"`）
  - Turso（LibSQL）提供托管 SQLite + 边缘复制，适合低流量应用
  - 单文件数据库，备份简单（复制文件即可）
  - 缺点: 不支持 PostgreSQL 特有的 `String[]` 数组类型（Prisma 在 SQLite 上不支持 `String[]`），需要改为 JSON 字段；不支持并发写入（但对 ~50 用户完全够用）
- **建议**: **维持 PostgreSQL**。理由：
  1. 设计文档中大量使用了 PostgreSQL 特有的 `String[]` 数组类型（如 `focusAccountIds`, `accountIds`, `channels`, `services` 等）。如果改用 SQLite，Phase 2/3 的所有 Subscription、AnomalyRule 模型都需要重构为 JSON 字段
  2. 生产环境 db9.ai 提供托管 PostgreSQL，运维成本可控
  3. Phase 3 的并发场景（多个 Cron job 同时写入告警 + 用户查询）PostgreSQL 更可靠
  4. 但应在文档中承认这是 **over-engineering for Phase 1**，如果团队反馈本地开发 Docker 依赖太重，可以考虑用 Turso/LibSQL 作为开发环境替代
- **影响范围**: 如果切换 SQLite，Step 1 部分重写（移除 docker-compose postgres 配置），所有 `String[]` 字段需改为 `Json`，Phase 2/3 的 Schema 全部受影响

### 2.4 localStorage → PostgreSQL 迁移策略

- **当前方案**: Step 3 描述的渐进迁移 — 保留 localStorage 作为未登录 fallback + "同步到云端"按钮一次性迁移
- **挑战**:
  - 迁移路径复杂: 需要同时维护 localStorage 和 API 两套数据源，容易产生 bug（数据不一致、重复会话）
  - 当前 `page.tsx` 有约 200 行 localStorage 相关逻辑（读取、保存、signature 去重、session 排序），将其改为 API 调用同时保留 fallback，改动面很大
  - "同步到云端"功能本身需要处理冲突（同一用户两个设备的 localStorage 都有数据怎么办？）
  - 未登录用户的 localStorage fallback — 但当前应用**强制要求登录**（NextAuth），什么场景下会有"未登录用户"？
- **替代方案**:
  - **方案 A: 直接切换，不做 fallback** — 已登录用户直接使用 API，旧 localStorage 数据提供一次性导入。考虑到用户量极小（~50），直接沟通通知即可
  - **方案 B: IndexedDB + Service Worker** — 作为中间层缓存 API 数据，支持离线使用。过度复杂，不推荐
- **建议**: **采用方案 A: 简化迁移，移除未登录 fallback**。理由：
  1. 当前应用已强制登录（`page.tsx` 依赖 `useSession`，未登录会重定向到 sign-in 页），不存在"未登录用户"场景
  2. 50 个用户可以直接通知"版本升级，请导入历史记录"
  3. 保留 localStorage 的一次性导入（Step 3.7 的 "同步到云端" 按钮）即可，但不需要持续的 fallback 逻辑
  4. 这样 Step 3.4 的改动量大幅减少，不需要同时维护两套逻辑
- **影响范围**: Step 3.4 简化（移除 fallback 分支），Step 3.7 保留但简化（仅导入，不需要"渐进迁移"逻辑）

### 2.5 xlsx 库选择

- **当前方案**: SheetJS (`xlsx` npm 包)
- **挑战**:
  - SheetJS 社区版（Community Edition）自 2023 年起改为限制性许可证（Apache 2.0 → 自定义许可证），商业使用需确认合规性
  - npm 上的 `xlsx` 包版本可能滞后于 SheetJS 官方仓库
  - SheetJS 的 API 设计偏底层，需要较多样板代码
  - 存在已知的安全漏洞历史（Prototype Pollution）
- **替代方案**:
  - **ExcelJS** (`exceljs`): MIT 许可证，纯 JavaScript，API 更友好，支持流式写入、样式、图片
  - **xlsx-populate**: MIT 许可证，但维护较少
- **建议**: **改用 ExcelJS**。理由：
  1. MIT 许可证，无合规风险
  2. API 更符合直觉（Workbook → Worksheet → Row → Cell）
  3. 内置支持数字格式、列宽自动调整、冻结首行等常用功能
  4. 活跃维护，npm 周下载量约 200 万
  5. 体积略大但由于是 server-side 操作（导出在浏览器侧生成 blob），不影响首屏加载
- **影响范围**: Step 6.1 — 将 `npm install xlsx` 改为 `npm install exceljs`，导出逻辑代码略有不同但接口职责不变

### 2.6 queryResult 存储于 messages 表

- **当前方案**: Message 模型中 `queryResult Json?` 字段直接存储完整查询结果
- **挑战**:
  - 单条查询结果可能非常大: BigQuery 最多返回 1000 行（`BIGQUERY_MAX_ROWS` 默认值），每行可能有 10+ 列，JSON 序列化后单条 message 的 queryResult 可达 **1-5MB**
  - Message 表每月约 5000 条，假设 20% 包含查询结果，每月数据增量约 1K 条 * 2MB = **2GB/月**
  - 加载会话详情时需要加载所有消息（含 queryResult），对 GET `/api/sessions/[id]` 的响应时间影响显著
  - PostgreSQL 的 JSONB 列存储大 JSON 性能下降，TOAST 存储会引入额外 I/O
- **替代方案**:
  - **方案 A: 独立存储表** — 创建 `QueryResultStore` 表，Message 中仅存 `queryResultId` 引用，按需加载
  - **方案 B: 截断存储** — 仅存储前 N 行（如 100 行）+ 元信息（totalRows, columns），完整数据不持久化
  - **方案 C: 不存储 queryResult** — 仅存储 SQL，用户需要时重新执行。queryResult 仅在前端 session state 中保持
- **建议**: **采用方案 B + C 混合: Message 表不存储 queryResult，仅存储 SQL 和 dryRun 结果。queryResult 在前端内存中保持（页面刷新后消失）。如果用户需要历史结果，可以重新执行 SQL**。理由：
  1. 当前代码中 queryResult 已经是"按需执行"的（用户点击 Run Query 后才有结果），不是消息的核心内容
  2. 大幅减少数据库存储开销
  3. 避免加载会话详情时传输大量数据
  4. 如果确实需要持久化结果，Phase 2 可以引入独立的 `QuerySnapshot` 表，存储前 100 行 + 元信息
- **影响范围**: Step 1.2（Message 模型移除 queryResult 字段），Step 3（API 和前端逻辑调整）

### 2.7 chart-suggester 规则引擎 vs LLM

- **当前方案**: 基于规则的 chart-suggester（时间列+数值列→折线图，分类列+数值列→柱状图，等）
- **挑战**:
  - 规则难以覆盖复杂场景: 如"同时有 2 个分类列 + 2 个数值列"应该推荐什么图表？
  - 列名语义识别困难: 规则需要判断哪个列是"时间列"、哪个是"分类列"。仅靠数据类型不够（比如 `billing_period_month` 是 STRING 还是 DATE 取决于 BigQuery 查询写法）
  - 新场景需要不断添加规则
- **替代方案**: LLM-based chart suggestion
  - 将列名 + 前几行样本数据 + 用户问题传给 LLM，让 LLM 推荐图表类型和配置
  - 优点: 理解语义更好（"按区域拆分" → LLM 知道这适合堆叠柱状图）
  - 缺点: 额外 LLM 调用增加延迟 (~2-3s) 和成本，且可能返回不一致的结果
- **建议**: **维持规则引擎，但加强列类型推断**。理由：
  1. Phase 1 覆盖的图表类型有限（折线/柱状/饼图/堆叠面积），规则足够
  2. 每次查询已经调用了 1-2 次 LLM（SQL 生成 + 分析模式的二次分析），再加一次 chart suggestion 的 LLM 调用，延迟和成本不合理
  3. 规则引擎的确定性输出更适合 UI 交互（用户不会因为刷新页面看到不同的图表推荐）
  4. 改进方向: 在 chart-suggester 中加入列名关键词匹配（如含 `date/month/year/time` 的列识别为时间列，含 `cost/amount/price` 的列识别为数值列）
- **影响范围**: 无（维持现有方案，建议增强实现细节）

### 2.8 zod 引入时机

- **当前方案**: Step 6.3 在 Phase 1 最后引入 zod，重构所有已有 API 的校验
- **挑战**:
  - 当前 `chat-request.ts` 已有手写校验逻辑（`parseChatPayload`, `normalizeMessages`），同样 `bigquery-request.ts` 也有 `parseBigQueryPayload`。这些手写校验逻辑运行良好
  - 在 Phase 1 最后才引入 zod，意味着 Step 2/3/5 新增的 API（`/api/sessions`, `/api/users/me` 等）会先手写校验，然后在 Step 6 用 zod 重写一遍——**双重工作量**
  - zod 改造涉及修改 `/api/chat`、`/api/bigquery`、新增的 `/api/sessions`、`/api/users/me` 等多个文件，是一个跨模块的横切关注点
- **替代方案**:
  - **方案 A: 将 zod 前置到 Step 1** — 在基础设施阶段就安装 zod，后续所有新 API 直接使用 zod
  - **方案 B: 不引入 zod** — 继续使用手写校验。对于 ~10 个 API 端点的小项目，手写校验足够且更灵活
- **建议**: **将 zod 安装前置到 Step 1，但不急于重构现有 API**。具体做法：
  1. Step 1 安装 zod（与 Prisma 一起）
  2. Step 2/3/5 的新 API 直接使用 zod schema 定义请求体
  3. Step 6 仅重构 `/api/chat` 和 `/api/bigquery` 两个现有 API（减少 Step 6 工作量）
  4. 在 `src/lib/validators.ts` 中统一管理所有 zod schema
- **影响范围**: Step 1（添加 `npm install zod`），Step 2/3/5（新 API 直接用 zod），Step 6（减少工作量，仅重构旧 API）

---

## 3. 任务拆解合理性

### 3.1 依赖关系分析

当前依赖图：

```
Step 1 (DB基础)
  ├→ Step 2 (用户系统)
  │    └→ Step 3 (会话持久化)
  ├→ Step 4 (图表可视化) — 可并行
  ├→ Step 5 (双模式+SQL质量) — 可并行
  └→ Step 6 (导出+校验) — 依赖 Step 4
```

**问题 1: Step 5 的依赖标注不准确**

Step 5 被标注为"无数据库依赖，可与 Step 2/3 并行"，但仔细看 Step 5.4（持续对话上下文管理），它要求"从数据库加载同一会话的前序消息作为上下文"——这**明确依赖 Step 3 的会话持久化 API**。如果 Step 3 未完成，前序消息只存在于前端 state 中，无法从服务端加载。

- **建议**: 将 Step 5 拆为两部分:
  - Step 5A（无依赖）: 双模式定义 + few-shot 示例库 + SQL 提取增强（可并行）
  - Step 5B（依赖 Step 3）: 持续对话上下文管理（从数据库加载历史消息）

**问题 2: Step 6 对 Step 4 的依赖过弱**

Step 6 说"依赖 Step 4 的 Tab 组件"，但 Step 6.2 的导出按钮添加到 `result-view.tsx` 中。实际上 Step 6.3-6.4 的 zod 重构也依赖 Step 2/3 新增的 API 端点（`/api/sessions`, `/api/users/me`）。

- **建议**: Step 6 应标注为"依赖 Step 2 + 3 + 4"

### 3.2 并行化建议

当前可并行的仅有 Step 4 和 Step 5（部分）。以下是更优的并行化方案：

```
Phase 1 (优化后):

Week 1-2:  Step 1 (DB基础) + Step 4 (图表可视化, 无DB依赖)
           └ Step 4 仅需 ECharts + chart-suggester + result-view, 纯前端

Week 2-3:  Step 2 (用户系统) + Step 5A (双模式 + few-shot, 纯前端/API逻辑)
           └ Step 2 依赖 Step 1
           └ Step 5A 无依赖

Week 3-4:  Step 3 (会话持久化) + Step 5B (持续对话上下文)
           └ Step 3 依赖 Step 2
           └ Step 5B 依赖 Step 3

Week 4-5:  Step 6 (导出 + zod 重构)
           └ 依赖 Step 2+3+4 全部完成
```

如果有两名开发者，可以进一步并行:

```
Developer A: Step 1 → Step 2 → Step 3 → Step 5B
Developer B: Step 4 → Step 5A → Step 6
```

### 3.3 任务粒度评估

**过大的任务**:

- **Step 3.4** "修改 page.tsx 使用服务端会话" — 当前 `page.tsx` 有 765 行，其中约 200 行是 localStorage 相关逻辑。这个任务包含: 替换数据源、添加 API 调用、处理加载状态、错误处理、保留 fallback。建议拆为:
  - 3.4a: 创建 `useSessionManager` custom hook 封装所有会话状态管理
  - 3.4b: 将 `page.tsx` 的 localStorage 逻辑替换为 hook 调用
  - 3.4c: 添加 loading/error UI 状态

- **Step 5.2** "实现分析模式" — 包含后端流程修改（`/api/chat` 内部执行 SQL + 二次 LLM）和前端展示修改（洞察文本 + 图表 + 可折叠数据）。建议拆为:
  - 5.2a: 后端分析模式流程（API 改造）
  - 5.2b: 前端分析模式 UI（展示层改造）

**过小的任务**:

- **Step 1.6** "添加 Prisma 到 Makefile" — 仅 3 行 Makefile 改动，可以合并到 Step 1.5
- **Step 2.3** "扩展 NextAuth 类型声明" — 仅一个 `.d.ts` 文件，可以合并到 Step 2.2

### 3.4 遗漏的任务

1. **Dockerfile 修改** — Step 1 涉及 Prisma 引入，但没有列出 Dockerfile 修改任务。当前 Dockerfile 没有 `prisma generate` 步骤，build 阶段会失败。需要:
   - 在 builder 阶段添加 `COPY data-agent/prisma ./prisma`
   - 在 build 前执行 `npx prisma generate`
   - 在 runner 阶段考虑 migration（`prisma migrate deploy` 还是在容器启动时运行？）

2. **数据库连接错误处理** — Step 1.4 创建 Prisma Client 单例，但没有提及连接失败时的错误处理。数据库不可用时 API 应返回 503 而非 500

3. **API 认证保护** — Step 3 新增的 `/api/sessions` 系列 API 需要认证保护，但任务清单中没有显式提及（虽然在"完成后检查"中有一条）。建议在 Step 3.1 中显式列出认证逻辑

4. **page.tsx 重构** — 当前 `page.tsx` 有 765 行，是一个巨大的单文件组件。Step 3/4/5 都在修改它，多人并行开发会有大量 Git 冲突。建议在 Step 3 之前增加一个预备步骤: 将 `page.tsx` 的逻辑拆分为 custom hooks（`useSessionManager`, `useChatMessages`, `useQueryExecution`）

5. **Next.js 14 的 App Router 中间件** — Step 2 的认证增强没有考虑使用 Next.js middleware 统一保护 `/api/*` 路由。当前每个 API 都手动检查 session，重复代码多

---

## 4. 验收标准评估

### 4.1 可测量性分析

| Step | 验收标准 | 可测量性 | 问题 |
|---|---|---|---|
| 1 | docker-compose up + prisma migrate + studio | 可执行验证 | OK |
| 2 | OAuth 登录 → users 表创建记录 | 可手动验证 | 缺少自动化测试 |
| 3 | 创建/切换/删除会话 | 可手动验证 | **缺少并发测试**（两个标签页同时操作） |
| 4 | 图表自动推荐 + 交互 | 可手动验证 | **缺少 bundle size 检查** |
| 5 | 分析模式 vs 数据模式 | 可手动验证 | **缺少延迟指标** |
| 6 | Excel 导出 + zod 校验 | 可手动验证 | OK |

### 4.2 缺失的验收标准

1. **性能基线**
   - Step 4: ECharts 懒加载后的首屏加载时间应 < 现有首屏时间（无回归）
   - Step 4: 图表渲染 1000 行数据的时间 < 2s
   - Step 5: 任务拆解中提及"分析模式延迟 < 15s，数据模式 < 8s"，这些是合理的，但应包含 P95 指标而非仅平均值

2. **错误场景**
   - Step 3: 数据库连接中断时的降级行为（返回 503？显示错误提示？回退 localStorage？）
   - Step 3: 会话数据迁移失败时的回滚策略
   - Step 5: LLM 二次分析超时或失败时，是否应该降级为数据模式？

3. **安全验收**
   - Step 3: 验证 A 用户无法访问 B 用户的会话（横向越权测试）
   - Step 2: 验证 `/api/users/me` 不会泄露其他用户信息
   - Step 6: zod 校验拒绝 XSS payload

4. **Docker 集成验收**
   - 整体: `docker-compose up -d --build` 后，完整功能可用
   - 任务拆解的"完成后检查"已包含此项，但应在每个 Step 验收中都包含 `make build` 通过

### 4.3 缺少自动化测试计划

任务拆解中没有任何自动化测试任务。当前项目已有 `npm run test` 和 `docs-loader.test.ts`，说明测试基础设施存在。建议至少为以下逻辑添加单元测试：

- `chart-suggester.ts` — 核心推荐逻辑，纯函数，非常适合单元测试
- `user-identity.ts` — 身份解析逻辑，涉及多个分支
- `session-api.ts` — API 封装层
- `validators.ts` — zod schema 校验

---

## 5. 风险补充

### 5.1 原文档已识别的风险（补充评估）

| 原文档风险 | 补充评估 |
|---|---|
| Prisma 增加构建复杂度 | **风险偏低**: 当前 Dockerfile 是 multi-stage build，添加 `prisma generate` 步骤影响有限。但需要测试 Docker ARM vs AMD64 的 Prisma 引擎兼容性（macOS M-series 开发者 + Linux 生产环境） |
| 会话迁移数据丢失 | **风险中等**: 建议增加"导出历史记录"功能作为保底（当前已有 `handleExportHistory` JSON 导出），在迁移前提醒用户先导出 |
| chart-suggester 推荐不准确 | **风险偏低**: 保守策略（不确定返回 null）是正确的。但需要考虑"推荐了图表但图表渲染失败"的场景（如数据格式异常） |
| few-shot 增加 context 长度 | **风险偏低**: 2-3 个示例 < 2000 tokens，当前 LLM context window（Gemini Flash 128K, OpenRouter 模型通常 32K+）完全容纳 |

### 5.2 新增风险

**风险 1: page.tsx 的 God Component 问题**

当前 `page.tsx` 是一个 765 行的巨型组件，包含: 会话管理、消息发送、dry-run、执行、历史管理、导入/导出、UI 渲染。Phase 1 的 Step 3/4/5 都需要修改它，存在:
- Git 冲突风险（如果并行开发）
- 回归 bug 风险（修改任何一个功能可能影响其他功能）
- 代码审查困难（PR 差异大）

**缓解**: 在 Phase 1 开始前，先将 `page.tsx` 重构为多个 custom hooks + 子组件（这应该作为 Step 0）

**风险 2: Next.js 14 版本锁定**

当前锁定 `next: 14.2.35`。Prisma 5.x 在 Next.js 14 App Router 中有已知的热重载问题（开发模式下 Prisma Client 实例泄漏）。虽然全局单例模式可以缓解，但需要测试验证。

**缓解**: Step 1.4 创建 Prisma Client 单例时，严格遵循 Prisma 官方推荐的 Next.js 单例模式，包含 `globalThis` 缓存

**风险 3: ECharts + Next.js dynamic import 的 SSR 兼容性**

ECharts 依赖浏览器 DOM API（Canvas, Window）。虽然 Step 4.4 提到用 `next/dynamic` 懒加载，但 `echarts-for-react` 内部可能在模块加载时就引用 `window`，导致 SSR 报错。

**缓解**: 需要在 dynamic import 时使用 `{ ssr: false }` 选项。当前代码已有先例（`ReactMarkdown`, `SqlViewer` 都用了 `dynamic` + `ssr: false`），同样模式应该可行，但需要实际测试

**风险 4: 分析模式的成本控制**

分析模式下每次查询需要 2 次 LLM 调用（SQL 生成 + 结果分析），加上 1 次 BigQuery 执行。如果用户频繁使用分析模式:
- LLM 成本翻倍
- BigQuery 查询在 `/api/chat` 内部执行，绕过了当前的 dry-run 保护机制

**缓解**:
- 设置每用户每日 LLM 调用次数限制
- 分析模式在执行 SQL 前，应先 dry-run 检查成本，避免意外执行昂贵查询
- 在设计文档中补充成本控制策略

**风险 5: 现有导出功能冲突**

当前 `result-table.tsx` 已经有"Save results"下拉菜单（CSV + JSON 导出）。Step 6 要在 `result-view.tsx` 添加"Export"下拉菜单（CSV + Excel）。如果不统一处理，用户会看到两个导出入口。

**缓解**: Step 6.2 应该明确移除 `result-table.tsx` 中的现有导出功能，将导出功能统一到 `result-view.tsx` 层级

---

## 6. 改进建议汇总

### 优先级分类

**P0: 必须修改（影响正确性或可行性）**

| # | 建议 | 影响 Step | 理由 |
|---|---|---|---|
| 1 | 添加 Dockerfile 修改任务（prisma generate + COPY prisma/） | Step 1 | 不修改 Dockerfile，`make build` 和 Docker 部署必定失败 |
| 2 | 修正 Step 5 依赖: Step 5.4（持续对话）依赖 Step 3 | Step 5 | 依赖标注错误，影响并行开发计划 |
| 3 | 明确分析模式流程: /api/chat 是否内部执行 BigQuery | Step 5.2 | 架构决策不明确，影响工作量估算 |
| 4 | Phase 1 Prisma Schema 需独立定义，不能直接引用设计文档§5.2 | Step 1.2 | 直接复制会因引用不存在的模型报错 |

**P1: 强烈建议修改（显著降低风险或提升效率）**

| # | 建议 | 影响 Step | 理由 |
|---|---|---|---|
| 5 | 将 zod 安装前置到 Step 1，新 API 直接使用 | Step 1/2/3/5/6 | 避免 Step 6 大规模重构 |
| 6 | 替换 `xlsx` 为 `exceljs` | Step 6.1 | 许可证合规风险 |
| 7 | 简化 localStorage 迁移: 移除未登录 fallback | Step 3.4 | 不存在未登录场景，减少代码复杂度 |
| 8 | 不在 Message 表存储 queryResult | Step 1.2/3 | 避免数据库膨胀，简化数据模型 |
| 9 | 增加 Step 0: page.tsx 重构为 hooks + 子组件 | 新增 | 降低并行开发冲突风险，提高代码可维护性 |
| 10 | 统一导出入口: 移除 result-table.tsx 中的现有导出 | Step 6.2 | 避免用户看到重复导出入口 |

**P2: 建议修改（改进质量）**

| # | 建议 | 影响 Step | 理由 |
|---|---|---|---|
| 11 | 为 chart-suggester 添加列名关键词推断 | Step 4.2 | 提升推荐准确性 |
| 12 | ECharts 按需导入（tree-shaking） | Step 4.1 | 减小 bundle size |
| 13 | 添加自动化测试计划（chart-suggester, user-identity, validators） | 所有 Step | 提高代码质量信心 |
| 14 | 添加 Next.js middleware 统一 API 认证 | Step 2 | 减少重复认证代码 |
| 15 | 修正设计文档 §3.1 架构图中 "Recharts SSR" → "ECharts" | 设计文档 | 文档一致性 |
| 16 | 明确 UserProfile 的 Phase 归属 | 设计文档 | 避免 Phase 1 Schema 混淆 |
| 17 | 添加性能验收基线（首屏时间、图表渲染时间、API 延迟 P95） | 验收标准 | 可量化的质量门槛 |
| 18 | 添加安全验收标准（横向越权、XSS） | 验收标准 | 安全基线 |

---

*Review Status: Complete*
*Reviewer: Senior Architect*
*Date: 2026-03-10*
