再次使用AI完成前后端应用——RD Curve AI

https://do1e.cn/notes/9

年初的时候有尝试使用大语言模型完成一个前后端项目，详见“记一次使用 AI 完成一整个前后端项目”。
那时的观点是很难用，即使已经有了现成的其他语言的代码作为参考，依然难以复现，很大程度上还是需要人工参与到代码的编写中（尤其是对一些编译问题和运行时 bug）。

但今年 agent 的快速进步也推动了 Vibe Coding 的快速发展，以及我自己对 Next.js 框架的逐渐熟悉，似乎这件事也变得更加简单了。

作为一个压缩领域的研究者，我一直苦恼于每次画新的 RD 曲线图都要从过往的实验数据中找到旧方法的实验结果，并编写 matplotlib 代码绘制。这几天终于忍不住想要搞一个管理相关实验数据并进行可视化的应用 ——RD Curve AI，也欢迎大家试用并给我一些改进建议。

这次 Vibe Coding 实际上仅用了一天时间就完成了，个人认为还是很顺利的，大致流程如下：

首先详尽地编写一个提示词，描述要使用的技术栈、组件等内容。其实也不用组织得特别好，只要条目足够多，足够覆盖脑海中的需求即可。这里也给出我的示例：

Copy
我是视频图像压缩的研究者，每次有新数据都要写一个python代码绘制RD曲线实在是太麻烦了，请帮我写一个网页完成下述功能。

使用better-auth进行邮箱注册和登录。
登录后用户可以新建一个数据集，编写名称和描述。
在数据集中，用户可以新建一个方法，比如“JPEG”，并填写描述。
用户可以编辑（包括删除）该方法或该方法的所有RD数据点。
用户也可以上传任意格式的数据文件（如txt、csv等）或者直接粘贴文本的实验结果，由AI进行解析，转换为类似{"name": "JPEG", "description": "some description", "data":[{bpp: float, psnr: float, ms_ssim: float}, ...]}格式的标准json数据并展示，由用户判断是否插入到数据库中，如果有相同的名称，则按照bpp排序合并数据点，不更新其它内容。
如果name或description没有解析出来，则让用户输入，其中name不能为空。
每一个点必须具有bpp，代表横轴。PSNR、MS-SSIM等均为可选，代表纵轴。并且允许用户自定义其他纵轴，如LPIPS等，两个默认的纵轴允许用户删除。
每一个纵轴数据均有一个映射函数，需要用户自定义，表示在图片上的数值。如MS-SSIM的范围是0-1，但是在图片上显示时需要-10*log10(1 - ms_ssim)，用户可以填写这个映射函数，但请注意防止用户填写危险的函数。
AI使用OpenAI格式的API进行数据解析，可以通过环境变量指定endpoint、api key、模型等参数。
用户可以在数据集界面可以直接查看各个方法的RD曲线图，并支持取消显示某些方法的曲线，或者方法中的某些点。这些展示信息可以保存在浏览器本地，方便下次访问时恢复。
所有的API返回和页面内容请使用英文。
产品名称为“RD Curve AI”。
前端请使用 shadcn/ui 组件库和lucide图标库。
数据库请使用Drizzle ORM连接PostgreSQL。
使用pnpm作为包管理工具，shadcn/ui组件库使用npx命令安装和初始化。
类型使用zod进行校验和转换，并且统一保存在lib/types.ts文件中。
环境变量使用lib/config.ts文件进行统一管理和导出。
在必要时，你可以调用 shadcn mcp 用于检索组件库，调用 lucide-icons mcp 用于检索图标库，以及 context7 mcp 检索使用到的技术栈的最新资料和文档。
尽可能复用已有的lib文件夹中的代码。
所有的API信息和前端文本均使用英文。

除了这个提示词外，我还喂了一些之前写好的密码加密、better-auth 的配置进去，因为根据之前的经验，这部分需要 AI 迭代好多轮才能输出一个符合我预期的代码。

写一个项目的整体提示词，这个可以交给 AI，但仍需要对其内容进行一些微调。这里也给出我微调后的版本：

Copy
# RD Curve AI - Copilot Instructions

You are assisting with "RD Curve AI", a Next.js 16 application for video/image compression researchers to manage datasets and plot Rate-Distortion (RD) curves.

## Tech Stack & Key Libraries
- **Framework:** Next.js 16 (App Router), React 19
- **Database:** PostgreSQL, Drizzle ORM (`lib/schema.ts`, `lib/db.ts`)
- **Auth:** Better-Auth (`lib/auth.ts`, `lib/auth-client.ts`)
- **UI:** Tailwind CSS, shadcn/ui, Recharts (`components/rd-chart.tsx`)
- **Validation:** Zod (`lib/types.ts`)
- **AI:** OpenAI (for data parsing)

## Architecture Overview

### Data Model (`lib/schema.ts`)
The core hierarchy is **Dataset -> Method -> DataPoint**.
- **Dataset:** A collection of methods (e.g., "Kodak Dataset").
- **Method:** A specific algorithm (e.g., "JPEG", "HEVC").
- **DataPoint:** A single result with `bpp` (x-axis) and dynamic `metrics` (y-axis) stored in a `jsonb` column.
- **Metric:** Custom definitions for y-axis values (e.g., "PSNR", "MS-SSIM") with a `mapping_function` string for display transformation.

### Authentication Pattern
- **Server-side:** Use `auth.api.getSession({ headers: await headers() })` in API routes/Server Components.
- **Client-side:** Use `authClient` hooks (e.g., `useSession`).
- **Protection:** Always check `if (!session)` in API routes before sensitive operations.

### API Route Pattern (`app/api/**`)
Follow this pattern for Route Handlers:
1. **Auth Check:** Verify session immediately.
2. **Validation:** Parse request body with Zod schemas from `lib/types.ts`.
3. **DB Operation:** Use Drizzle to query/mutate.
4. **Response:** Return `NextResponse.json`.

## Critical Conventions

### Database & Drizzle
- **Schema:** Define all tables in `lib/schema.ts`.
- **IDs:** Use `uuid` with `.defaultRandom()`.
- **Relations:** Use `drizzle-orm` relations for cleaner queries.
- **JSONB:** Use the `metrics` jsonb column in `data_point` for flexible metric storage.

### Frontend Components
- **UI:** Use `components/ui` (shadcn) for primitives, install new ones with `npx shadcn@latest add <component>`.
- **Charts:** Use `Recharts` in `components/rd-chart.tsx`. Handle the `mapping_function` logic (likely via `lib/safe-eval.ts`) when rendering axes.
- **Icons:** Use `lucide-react`.
- **MCP:** Use `context7` mcp for documentation if needed. Use `shadcn` mcp for UI components. Use `lucide-icons` mcp for searching icons.

### Configuration
- **Env Vars:** Access via `lib/config.ts` (e.g., `CONFIG.OPENAI_API_KEY`), NOT `process.env` directly.
- **Types:** Keep shared Zod schemas and TS interfaces in `lib/types.ts`.

### Language
- All API responses and frontend text must be in English.

## Developer Workflows
- **Migrations:** Run `pnpm db:generate` and `pnpm db:migrate` when changing schema.
- **AI Parsing:** The parsing logic (converting text to JSON) should handle unstructured input and map it to the `data_point` structure.

花了一段时间后 AI 就能把整个框架搭好了，此时就需要根据编辑器给出的错误提示，逐条交给 AI 去解决。

当项目成功跑起来，就是逐步调整包括功能、前端页面了，这一部分也是最耗时的一段。比如我就中途想到加入一个 BD-Rate 计算的功能。在这里进行反复迭代最后也许就能得到你想要的项目了。

最后想要说明的是，Vibe Coding 依然无法帮助小白完成一个较为复杂的系统任务，人在其中虽然不一定要完全读懂 AI 写的代码，但一定要对系统框架有一个较为全面的掌握。这里引用一条暴论：”AI（几乎）不能帮你完成你并不会的任务 “。我对 Next.js 框架的熟悉就是这一年自学并在 AI 的帮助下完成一些小项目或者改进一些开源项目中逐渐成长起来的，当有了这些经验积累后，AI 才能变成一个提升效率的优秀工具。