0%

一篇关于使用AI来编写项目的心得与方法总结

引言

因为我想在自己的每次实践中都获得最高效的学习结果,所以我经常会尝试用不同的东西来尝试做同一件事情。本次就是轮到了利用 AI 来进行本项目几乎全编写

其实还有一点是我自己做毕业设计前需要提交一份毕业设计题目

如果你想跳过过程直接看结论的话可以直接移步到 第二次尝试开发 的章节中 审核计划书(plan) 的部分

关于项目的开头

一、想法

在思考毕设题目的时候,我就已经有一个想法了: 先做好一个比较完善的需求报告分析和系统设计规划
抱着这个的前提下,我现在指导老师提供的题库中选择了一个我觉得最可行的题目 在线考试平台的设计与实现 , 确定下来整个系统需要做什么之后,我就进入到了 需求分析系统的设计与规划

二、关于系统的设计与规划

  • 在这里我其实经历过一次技术选型的更改,原本指导老师给出的是 利用 PythonFast API 来做后端实现,但是这个框架我并不是很了解,因此我向指导老师进行了询问是否可以更换题目,指导老师给予了我肯定的回答。
  • 此时我面临一次选择的问题,一边是 Springboot 技术栈做后端,一边是 Node JS 做后端。思考了这个系统应改不会做的特别高难度的并发操作、且项目的功能需求也没有需要如此高规格的性能来支撑。在权衡了开发效率与性能之间,我选择了开发效率更高的 Node JS 来进行本次的开发。
  • 来到了 Node JS 的后端选型后,其实本项目已经没有任何需要思考的地方了。因为我本人也只学习过 ExpressKoa 这两款框架,根据我自己的学习经历我选择了开箱即用的 Express 。因为在 AI 开发时我比较敏感的事情就是让 AI 引入额外的依赖,我希望一个框架(依赖)可以解决 90% 的基本需求,剩下的特效需求我才会选择引入额外的依赖。
  • 而前端我也是毫不犹豫的选择了 Vue3 ,无他,原因只有是我觉得用的顺手,且我熟悉这里面的需要依赖、能做什么。

以上的技术选型都结束了,接下来就是我需要思考具体的项目的大致规划中项目结构、必须使用的依赖等等有哪一些。
但这一步我交给了 Gemini 来帮我编写,如:

项目的工程目录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/online-exam-system/
|
|-- /backend/ (Node.js Express 项目)
| |-- /config/ (配置文件,如数据库连接、JWT密钥)
| |-- /controllers/ (路由处理函数,处理请求和响应,e.g., userController.js)
| |-- /middlewares/ (Express 中间件,如JWT验证、权限RBAC, e.g., auth.js)
| |-- /models/ (Sequelize 数据表模型, e.g., User.js, Question.js)
| |-- /routes/ (API路由定义, e.g., auth.js, question.js)
| |-- /services/ (业务逻辑服务层, e.g., examService.js, gradingService.js)
| |-- /seeders/ (数据库初始数据脚本)
| |-- server.js (Express 应用主入口)
| |-- package.json (NPM 依赖)
| |-- .env (环境变量, 如数据库URL, JWT_SECRET)
|
|-- /frontend/ (Vue3 + Vite 项目)
| |-- /public/
| |-- /src/
| | |-- /api/ (封装 Axios 请求)
| | |-- /assets/
| | |-- /components/ (可复用组件)
| | |-- /router/ (Vue Router 配置)
| | |-- /store/ (Pinia stores)
| | |-- /utils/
| | |-- /views/ (页面级组件)
| | |-- App.vue
| | |-- main.js
| |-- index.html
| |-- package.json
| |-- vite.config.js

技术选型

Express 后端 (Node.js)

库 (Library) 作用 (Purpose)
express 核心Web框架,用于构建API。
mysql2 MySQL 数据库驱动(对应 Sequelize 的方言)。
sequelize ORM (对象关系映射),用于与数据库交互(定义模型、执行CRUD)。
jsonwebtoken 用于生成和验证JWT (JSON Web Tokens)。
bcryptjs 用于密码的哈希加密和验证(存储密码时使用)。
dotenv 加载 .env 文件中的环境变量。
body-parser 解析 HTTP 请求体(Middleware)。
cors 处理跨域资源共享(Middleware)。

Vue3 前端 (JavaScript/TypeScript) - 保持不变

库 (Library) 作用 (Purpose)
vue@^3 核心前端框架。
vue-router@^4 官方路由,用于管理前端页面跳转(SPA的核心)。
pinia 官方状态管理库,用于全局管理状态。
axios HTTP客户端,用于向Express后端发起Ajax请求。
element-plus UI组件库,提供丰富的即用型组件,加速开发。
vite 前端构建工具,提供极速的开发服务器和打包功能。

虽然从目前我已经开发完了整个项目来看,这里面还是缺了一些我现在新引入的一些依赖,但这些基础性的依赖确实支撑着我所有的基础功能的开发需求,且用的顺手

系统规划

用户角色的定义

这个是必须自己手写的最重要的一部分(之一)
这里我也不废话这么多,以下就是我最初版的角色设计,现在看起来特别简陋,但是能够覆盖了我后期的一些特殊需求的
这里的内容也是我与 Gemini 的对话中产出的,我让他思考在我编写的角色中,是否还有一些需要考虑的功能,他给我补全到了这样的程度我就停手了。因为我觉得再加功能我可能就做不过来了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
1. **学生 (Student)**
- **核心需求:** 参加考试、查看成绩。
- **功能:**
- 注册、登录、修改个人信息(如密码)。
- 浏览“待参加”的考试列表。
- 在规定时间内进入考试、答题、提交试卷。
- 查看已完成考试的成绩、排名(可选)和错题分析。
2. **教师 (Teacher)**
- **核心需求:** 管理教学资源、组织考试、分析学情。
- **功能:**
- 登录。
- **题库管理:** 对题目(按科目)进行增、删、改、查(CRUD)。
- **试卷管理:**
- 手动组卷:从题库中选择题目,设置分值,组成试卷。
- 随机组卷(选择开发):设定规则(如科目、题型、难度、数量),系统自动抽题组卷。
- **考试管理:** 发布考试(关联试卷,设定起止时间、考试时长、可见班级等)、查看学生考试情况(如谁未提交)。
- **成绩管理:** 自动阅卷(客观题)、查看成绩统计(平均分、最高/低分、分数分布)、查看错题分析。
3. **管理员 (Admin)**
- **核心需求:** 维护系统稳定和用户数据。
- **功能:**
- 登录。
- **用户管理:** 增、删、改、查自己创建的部门下的用户(包括重置教师或学生密码)。
- **角色分配:** 设置用户为“教师”或“学生”。
- 系统配置:如管理科目分类、全局公告。
数据库的设计

这个是必须自己手写的最重要的一部分(之二)
众所周知,每一个大型的项目的运行都离不开一个设计的足够好的数据库,在我这里也是同理。因为以前吃过开发的过程中老是修改数据库字段的麻烦,所以我吃了两次错了之后每一次做新的软件时我都会先把数据库设计的尽量扩展性足够高、尽量把一些核心的字段在前期就规划出来
然后我就把自己的上述的角色设计丢给了 Gemini 让他先给我出一版数据库,然后我再接着改成我所需要的样子。我自己改的差不多了再把内容丢回去让 Gemini 细化字段要求、外键、约束等等。
经过一番的编写后我得到了一份在前期来看是完全可用的设计的相对合理的数据库设计草稿

当然,我做到后面发现我的需求还是有一点高了,因此我对这个数据库进行过几次的改造,按照目前的阶段来说,这个数据库并不支持一些比较复杂的 N-M 的多对多的模型需求,所以在后面的开发我只有在对数据库重构的时候才会在与此同时的重构后端的内容,按照我自己的实践经历,还没有试过前后端没法实现我对需求的时候,项目需要重构都是数据库设计不足导致的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
#### A. 核心模块数据库表结构 (Sequelize Models)

1. **User (用户表 `Users`)**`id`, `username`, `hashed_password`, `full_name`, `role` (Enum: 'student', 'teacher', 'admin'), `createdAt`, `organization_id` (FK), `subUnit_id`(FK), `jion_date`
2. **Subject (科目表 `Subjects`)**`id`, `name`
3. **Question (试题表 `Questions`)**`id`, `question_text`, `question_type`, `options` (JSON), `answer` (JSON), `analysis`, `difficulty`, `subject_id` (FK), `creator_id` (FK), `is_active`
4. **Paper (试卷模板表 `Papers`)**`id`, `title`, `creator_id` (FK), `total_score`
5. **PaperQuestion (试卷-试题关联表 `PaperQuestions`)**`id`, `paper_id` (FK), `question_id` (FK), `score`, `order`
6. **Exam (考试表 `Exams`)**`id`, `title`, `paper_id` (FK), `start_time`, `end_time`, `duration_minutes`, `creator_id` (FK), `is_published`, `visible_to`
7. **ExamRecord (考试记录表 `ExamRecords`)**`id`, `student_id` (FK), `exam_id` (FK), `status` (Enum: 'pending', 'ongoing', 'submitted', 'graded'), `actual_start_time`, `submit_time`, `score`
8. **AnswerRecord (答题记录表 `AnswerRecords`)**`id`, `exam_record_id` (FK), `question_id` (FK), `student_answer` (JSON), `is_correct`, `score_obtained`
9. **Organization (归属单位表 `Organizations`)**: `id`, `name`, `code`, `type`, `join_date`
10. **SubUnit (归属子单位表`SubUnits`)**: `id`, `name`, `parent_id` (FK), `type`, `create_date`
11. **Notice (公告表 `Notices`)**: `id`, `title`, `content`, `creator_id` (FK), `organization_id` (FK), `sub_unit_id`, `created_at`, `subject_id`(FK)。

---

#### B. 核心模块数据库表结构 (Sequelize Models)

| 序号 | 表名 (`Model`) | 字段名 | 数据类型 (Sequelize/SQL) | 约束/关系 | 备注 |
| :----: | :----------------------------------- | :------------------ | :----------------------- | :------------------------- | :------------------------------------------------ |
| **1** | **Users (用户表)** | `id` | `INTEGER` | `PK, AUTO_INCREMENT` | 用户唯一标识 |
| | | `username` | `VARCHAR(100)` | `UNIQUE, NOT NULL` | 登录账号 |
| | | `hashed_password` | `VARCHAR(255)` | `NOT NULL` | 加密后的密码 |
| | | `full_name` | `VARCHAR(100)` | | 用户全名/昵称 |
| | | `role` | `ENUM` | `NOT NULL` | 角色:'student', 'teacher', 'admin' |
| | | `createdAt` | `DATETIME` | `NOT NULL` | 创建时间 |
| | | `organization_id` | `INTEGER` | `FK` to `Organizations` | 归属单位(学校/公司) |
| | | `subUnit_id` | `INTEGER` | `FK` to `SubUnits` | 归属子单位(班级/部门) |
| | | `join_date` | `DATE` | | 加入组织日期 |
| **2** | **Subjects (科目表)** | `id` | `INTEGER` | `PK, AUTO_INCREMENT` | 科目唯一标识 |
| | | `name` | `VARCHAR(100)` | `UNIQUE, NOT NULL` | 科目名称(如:高数、C++) |
| **3** | **Questions (试题表)** | `id` | `INTEGER` | `PK, AUTO_INCREMENT` | 试题唯一标识 |
| | | `question_text` | `TEXT` | `NOT NULL` | 试题内容 |
| | | `question_type` | `VARCHAR(50)` | `NOT NULL` | 题型(如:'single_choice', 'multiple_choice') |
| | | `options` | `JSON/TEXT` | | 选项列表 (JSON 格式存储) |
| | | `answer` | `JSON/TEXT` | `NOT NULL` | 标准答案 (JSON 格式存储) |
| | | `analysis` | `TEXT` | | 试题解析 |
| | | `difficulty` | `ENUM` | | 难度等级(如:'easy', 'medium', 'hard') |
| | | `subject_id` | `INTEGER` | `FK` to `Subjects` | 所属科目 |
| | | `creator_id` | `INTEGER` | `FK` to `Users` | 试题创建者(教师/管理员) |
| | | `is_active` | `BOOLEAN` | `NOT NULL, DEFAULT(TRUE)` | 逻辑删除标志,用于历史试卷数据完整性 |
| **4** | **Papers (试卷模板表)** | `id` | `INTEGER` | `PK, AUTO_INCREMENT` | 试卷模板唯一标识 |
| | | `title` | `VARCHAR(255)` | `NOT NULL` | 试卷模板标题 |
| | | `creator_id` | `INTEGER` | `FK` to `Users` | 试卷创建者(教师/管理员) |
| | | `total_score` | `FLOAT` | | 试卷总分 |
| **5** | **PaperQuestions (试卷-试题关联表)** | `id` | `INTEGER` | `PK, AUTO_INCREMENT` | 关联记录唯一标识 |
| | | `paper_id` | `INTEGER` | `FK` to `Papers` | 试卷模板 ID |
| | | `question_id` | `INTEGER` | `FK` to `Questions` | 试题 ID |
| | | `score` | `FLOAT` | `NOT NULL` | 该试题在本次试卷中的分值 |
| | | `order` | `INTEGER` | | 试题在试卷中的顺序 |
| **6** | **Exams (考试表)** | `id` | `INTEGER` | `PK, AUTO_INCREMENT` | 考试实例唯一标识 |
| | | `title` | `VARCHAR(255)` | `NOT NULL` | 考试名称 |
| | | `paper_id` | `INTEGER` | `FK` to `Papers` | 关联的试卷模板 |
| | | `start_time` | `DATETIME` | `NOT NULL` | 考试开始时间 |
| | | `end_time` | `DATETIME` | `NOT NULL` | 考试结束时间 |
| | | `duration_minutes` | `INTEGER` | `NOT NULL` | 考试时长(分钟) |
| | | `creator_id` | `INTEGER` | `FK` to `Users` | 考试发布者 |
| | | `is_published` | `BOOLEAN` | `NOT NULL, DEFAULT(FALSE)` | 是否已发布(草稿/正式) |
| | | `visible_to` | `JSON/TEXT` | | 可见单位/子单位列表 (JSON 格式存储) |
| **7** | **ExamRecords (考试记录表)** | `id` | `INTEGER` | `PK, AUTO_INCREMENT` | 考试记录唯一标识 |
| | | `student_id` | `INTEGER` | `FK` to `Users` | 参加考试的学生 ID |
| | | `exam_id` | `INTEGER` | `FK` to `Exams` | 关联的考试实例 ID |
| | | `status` | `ENUM` | `NOT NULL` | 状态:'pending', 'ongoing', 'submitted', 'graded' |
| | | `actual_start_time` | `DATETIME` | | 实际开始答题时间 |
| | | `submit_time` | `DATETIME` | | 提交试卷时间 |
| | | `score` | `FLOAT` | | 最终得分 |
| **8** | **AnswerRecords (答题记录表)** | `id` | `INTEGER` | `PK, AUTO_INCREMENT` | 答题记录唯一标识 |
| | | `exam_record_id` | `INTEGER` | `FK` to `ExamRecords` | 关联的考试记录 ID |
| | | `question_id` | `INTEGER` | `FK` to `Questions` | 关联的试题 ID |
| | | `student_answer` | `JSON/TEXT` | | 学生提交的答案 (JSON 格式存储) |
| | | `is_correct` | `BOOLEAN` | | 客观题是否正确 |
| | | `score_obtained` | `FLOAT` | | 本题得分 |
| **9** | **Organizations (归属单位表)** | `id` | `INTEGER` | `PK, AUTO_INCREMENT` | 单位唯一标识 |
| | | `name` | `VARCHAR(255)` | `NOT NULL` | 单位名称(如:学校名称) |
| | | `code` | `VARCHAR(50)` | `UNIQUE` | 单位编码 |
| | | `type` | `VARCHAR(50)` | | 单位类型 |
| | | `create_date` | `DATE` | | 创建日期 |
| **10** | **SubUnits (归属子单位表)** | `id` | `INTEGER` | `PK, AUTO_INCREMENT` | 子单位唯一标识 |
| | | `name` | `VARCHAR(255)` | `NOT NULL` | 子单位名称(如:班级/部门名称) |
| | | `parent_id` | `INTEGER` | `FK` to `Organizations` | 归属的父单位 ID |
| | | `type` | `VARCHAR(50)` | | 子单位类型 |
| | | `create_date` | `DATE` | | 创建日期 |
| **11** | **Notices (公告表)** | `id` | `INTEGER` | `PK, AUTO_INCREMENT` | 公告唯一标识 |
| | | `title` | `VARCHAR(255)` | `NOT NULL` | 公告标题 |
| | | `content` | `TEXT` | `NOT NULL` | 公告内容 |
| | | `creator_id` | `INTEGER` | `FK` to `Users` | 公告发布者 |
| | | `organization_id` | `INTEGER` | `FK` to `Organizations` | 公告可见的单位 ID |
| | | `sub_unit_id` | `INTEGER` | `FK` to `SubUnits` | 公告可见的子单位 ID |
| | | `created_at` | `DATETIME` | `NOT NULL` | 发布时间 |
| | | `subject_id` | `INTEGER` | `FK` to `Subjects` | 关联的科目(可选) |
API的约定

关于 API 其实没有什么好说的,但是接口的规范使用也很重要
这个我直接采用了目前最广为人接受的 RESTful API 来进行本项目的开发。并且我也写了一个指导规范,接口应该长什么样子。我就在这一份设计文档中,简单的写下了这几行的 API 接口规范
(API 基础路径为 /api/v1)

路径 方法 描述 权限
/questions/ POST 创建新试题 Teacher, Admin
/questions/ GET 获取试题列表(支持分页、搜索) Teacher, Admin
/questions/{id} GET 获取单个试题详情 Teacher, Admin
/questions/{id} PUT 更新试题 Teacher (限自己), Admin
/questions/{id} DELETE 删除试题(逻辑删除:设置 is_active=FALSE Teacher (限自己), Admin
功能模块的简单描述

这一块其实可以不用这么深入的去聊,因为现在连项目的实际情况都还不太清楚,所以我们可以先写上这个项目应该要有些什么功能,还有一些预想的、未来的、可能会开发的功能。
你只需要简单的写一写像下面这样的框架内容,然后丢给 AI ,让 AI 帮你细化规则内容,之后你再进行边界的约束。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
## (5) 考试管理模块:资源组织与发布

该模块的核心在于**试卷(Template)****考试(Instance)**的解耦设计。

### A. 试卷生成策略

- **手动组卷:** 精确控制。后端需处理 `Paper``Question` 的多对多关联映射(`PaperQuestion` 中间表),并同步计算总分。
- **随机组卷(动态抽取):** 灵活性。
- **设计要点:** 基于规则引擎(题型、学科、难度、数量)。
- **技术实现:** 利用数据库随机排序函数(如 `ORDER BY RAND()`)实现非重复抽样。

### B. 考试发布与分发

- **规则解耦:** 考试对象(`Exams`)引用试卷模板,并附加时间维度属性(开始/结束时间、限时)。
- **权限过滤:** 学生端接口需实现状态过滤逻辑(仅展示 `is_visible` 且处于活动时间段内的记录)。

---

## (6) 考试答题界面:高可靠性前端交互

该模块的设计重点在于**状态持久化****异常容错**

### A. 前端架构 (Vue3 + Pinia)

- **组件化拆分:** 将计时器、导航器、题目卡片解耦,提升复用性。
- **集中式状态:** `examStore` 作为唯一事实来源,管理考试实时数据流。

### B. 核心可靠性机制

- **双端计时同步:** 以服务器 `actual_start_time` 为基准计算绝对截止点,规避前端本地时间篡改。
- **数据防丢(自动保存):**
- **策略:** `watch` 侦听答案变化 + **防抖 (Debounce)** 处理。
- **逻辑:** 减少高频请求压力,实现静默背景提交。
- **灾难恢复(断网重连):** 挂载阶段主动轮询“进行中”状态,实现状态闭环恢复。

---

## (7) 成绩统计与查询:自动阅卷与数据聚合

该模块负责从“原始答项”到“结构化分数”的转化。

### A. 自动阅卷引擎 (Grading Service)

- **触发机制:** 提交动作立即触发。
- **比对算法:**
- **单选/判断:** 简单相等。
- **多选:** 需执行**排序后匹配**,确保逻辑严谨。
- **填空:** 字符串模糊或精确匹配。
- **数据更新:** 采用事务操作,同步更新 `AnswerRecord`(单题状态)与 `ExamRecord`(总分与完成状态)。

### B. 多维度数据展示

- **学生维度(反馈):** 侧重于“错题分析”与“解析查看”。
- **教师维度(统计):**
- **聚合计算:** 平均分、及格率等基础指标。
- **分布分析:** 提取分数区间分布。
- **质量回溯:** 识别高频错题,辅助后续教学调整。

章节结束

至此,本章结束了,我在这里完成了整个系统的规划与设计的全部内容。
我做了以下的事情

  • 工程的规范化(目录、技术选型)
  • 数据库的设计
  • API的约定
  • 大致功能模块的描述
    你看到这里是否觉得这是任务都很眼熟?对的,这就是在 2025年12月 突然在国内火起来的一个名为 Skill 的概念。2025年10月,我在做毕业设计选题的时候为了给自己打一个底,我就这样阴差阳错的做了一个项目的 "Skill"。在大致的规划上,我已经让这个系统拥有了一个相对可描述的雏形。

第一次尝试的开发

这一次其实是一个失败的经历,在 2025年11月 的时候,我开始尝试使用 Trae CN 中的 Builder ,来构建我的系统。但这一次很失败,原因我在后续会讲。
在这一次我比以前的任何一次开发都做得准备更加的充足

毕竟是毕设,不是课设,我必须要认真对待

搭建

数据库

在最开始我就拿着先前准备好的数据库设计文档,回去找了 Gemini 。让他给我写出这个数据库的 sql 脚本 其实主要是我懒,它给我写好脚本之后我就马上导入数据到对应的数据库,并且留了一份 sql 文件放在了项目的根目录中,供 AI 阅读当前数据库是什么样子。

项目的初始化

这个时候的我其实还不太会指挥 AI 来进行工作,所以我直接理所应当的跟 AI 说了一句

请你根据设计文档中的内容进行项目的初始化

但是 AI 行为有点超出我的预料,它的创建逻辑是先根据设计文档中写出的工程目录创建了文件夹,然后再通过脚手架来进行项目的初始化,这种和常识不太一样的操作我第一次看到还是挺震惊的。
但是现在的我来看其实也不奇怪, AI 确实在预期内的完成了该做的工作,并且没有做没写的东西,这一点是我的前期工作准备的疏忽。
并且为了项目的代码不会被 AI 该崩溃又无法回溯代码,我依旧保持了进行版本管理的最佳实践。每完成一个目标我就提交一次 git。

代码编写

在这里我给 AI 说了这么一段话:

请你根据设计文档的内容,编写完成整个项目,如果你有对数据库不动的地方,你可以查看项目根目录下的 sql 文件,该文件是当前数据库的情况。

我就在一边等了二十多分钟,运行好了之后,启动虽然不报错。但是到处都充满了报错提示,无论是前端还是后端的控制台。
然后我让 AI 进行修复项目,又过了两轮的对话,总算是可以注册账号、登录进去了。然而一进去依旧是充满了各种报错提示。

现在的我归纳总结这里犯的错误有着一些:

  1. 步子迈的太大了,让 AI 一次性做了这么多的工作,它的上下文必然爆炸,即使我并没有 token 用量的焦虑,我也不能这么使唤
  2. 指令并不够清晰,并没有明确的让 AI 去准确的做一件事情

戛然而止

对的,这次的尝试在这里就戛然而止了,因为我出门旅游了一趟,回来 忘了这件事情
虽然后面新年前我抽了一天回来接着补这个项目,但也没做什么进展特别大的工作。可以约等于我没有做过。
但我为了榨干这一次的价值,我让 Trae CN 的 Builder 把目前项目中已有的所有 API 接口都整理出来,形成一份类似 Swagger 作用的 API 文档,这份文件为后续的帮助也很大。

第二次尝试开发

引言

到了四个月后,也就是在编写这篇博客的一个星期前。我开起了本次的开发。这几个月内我也在持续的观察AI圈子。AI 圈子里面又涌现出来了很多的新概念,很多模型继续取得了各种性能指标上的突破。
其中我最关注的到的几个概念有:

  • 蜂群
  • Skill
    我发现这几个都是模拟了现实中,软件工程这一门工程学问的执行过程,并且我此时发觉我最初写的设计文档的作用和他们所提及的 Skill 本质上不就是同一个东西吗?
    在这之前我也一直在尝试如何用更高效、精准的提示次给 AI 进行指导方向,过了一段时间,我认为时机成熟了。
    于是我做出了一个决定,我决定使用一下顶级的模型来进行辅助我的开发。此前我一直用的都是免费的 Coding 助手,没有用过任何一个付费的 Coding 助手。我在结合了自己的经济情况,去找了一个相对稳定提供的 Codex 三方中转 API 提供商。

重构

有了上一次的经验,我最开始其实没想着用他来进行这么快速的开发,但是随着我使用了两三次,我发现之前我的方法一直都是走在了正确的道路了,瓶颈其实出现在了模型的能力上。
并且我把先前的代码全部都删除了,只留下 设计文档接口文档sql文件
接着,我还没有死心,我接着利用这些现有的文档,用Trae CN 的 Builder 进行再一次的重构开发。这次不同的是我引入了一个 Express 框架的 Skill
写了个项目的雏形,发现效果还不催。因为我的提示词变得更加准确了,也有一个 Skill 对 AI 的规范指导。我在这两天一直在接着尝试如何控制我的提示词让 AI 进行更好的输出。

这里完成了异界比较基础的路由、前后端连接的配置、后端与数据库的基础交互
接着,我购买了 CodeX 的当天,我给 Codex 5.3(超高推理) 说的第一句提示词并不是任何的 “请你帮我生成这个项目” 之类的话,而是:
请你阅读项目根目录下的设计文档接口文档sql文件,请你理解后告诉我你对项目有把握了。然后我们再进行开发。

Codex 很快的给出了理解的回答。于是我让他开始搭建初始化项目,他也会自己通过脚手架自动化的初始化项目,虽然选项还是我自己来选择,但至少聪明了一点。
然后我注意到了下面有一个 plan mode ,于是我尝试打开了这个开关,我仿佛进入到了一扇新世界的大门,编程体验、AI 提示词体验从未如此酣畅淋漓过
总的来说这个模式会根据你给出的提示词进行更多的思考,并在过程中为了实现你的提示词中的内容,有不懂的边界问题他基本都会多询问你几次,这点我觉得做的很不错。在最初我体验的一款 Coding 助手(Fitten Code)中也有一个计划模式,但是它的模型能力太弱了,所以我更多的是把它动作一个代码补全的一个强化工具。

请你根据设计文档、接口文档、sql 文件 中的内容进行该系统的个人信息页中修改密码的天下、登录框时的回车支持

过程他问了几个边界问题,我都回答了我需要的样子,然后他经过了几分钟的工作,我来验收一次发现居然还很不错,我一时间没法找到任何毛病。

提示词的进化

但我觉得它老是问我问题可能有点太过麻烦了,于是我对自己的提示词再一次的进化。因为我用的是 Trae CN 这个 IDE ,因此我在提交 git 的时候能自动生成本次的提交信息。于是我开始尝试学习 git 提交信息的风格进行输入提示词,我得到了一个完全不同的、更加高效的 AI 输出。

feat:
在所有用户下:

  1. 添加与【归属单位】【归属子单位】的联系
  2. 实现前端【归属子单位】的展示页面
  3. 新增后端中【归属单位】、【归属子单位】的实体类
    docs:
  4. 在你完成了所有的事情后去更新接口文档、系统设计文档

然后就这样的一次阴差阳错的尝试下,我体会到了更加高效的开发速度,这种提示词的方式帮助我迫使自己思考提出的内容属于什么、它应该怎么做、预期应该是如何的。换言之,这其实是我每次在提需求的时候都是一次的小型设计文档。
但是我依旧觉得这种效率也许还是太低了,我再次回到了 Gemini 上。这一次我先尝试自己打出上面的 第二版本 的提示词,丢给 Gemini ,让它帮我细化我的提示词边界、要求。
不出所料,利用 Gemini 输出的提示词质量更加的好,并且通常可以取得更好的系统控制与严谨性的设计。

审核计划书(plan)

随着我的项目的不断编写代码,每一次编写的量比起之前越来越多,不只是 Codex 的上下文每工作四五次就要进行一次上下文压缩,我在审核他每次提出的 plan 时也都感觉越来越费劲了,于是在这里我也引入了 Gemini
此时我的流程做法为:

  1. 自己先思考到需要的feat、fix之类的需求
  2. 丢给 Gemini 进行提示词的细化
  3. Gemini 输出的细化过后的提示词,再次丢给 Codex
  4. Codex 产出 plan
  5. 我把 plan 丢回给 Gemini ,让它阅读的同时我自己先读一遍 Gemini 给出的精简版内容,带着这些内容我再回去看详细的 plan
  6. 此时我可能还会有一个步骤。因为在上一步把 plan 丢给了 Gemini,因此它也会给出自己更多的基于这份 plan 的建议,我会自行决定是否要把 Gemini 给出的建议丢回给 Codex
  7. 此时 plan 的产出内容到我满意了,我按下实施按钮,静候它的完成。
  8. Codex 编写本次的内容到设计文档接口文档中。
  9. 这一步可以不用这么频繁的做。新启新的会话,让另一个 Codex 对当前项目进行一次审计评估,并编写一份报告
  10. 我来阅读文档(在这个过程中我可能也会把文档丢给 Gemini 进行阅读审核)
  11. 验收、测试bug、提出feat、bug等需求。
  12. 利用 Trae CN 的自动生成 git 信息提交新的 git (在这个步骤同时可以指导另外一个 AI 对于此项目的监控情况如何)
  13. 完成了这一工作流的闭环

对于这种工作流的看法

一个方案的出现必然带有好的与坏的一面,基本不存在完美的解决方案,我们永远都是选择自己能够接受的最便捷、不良后果最小的方案

优势

  1. 同时利用了几个不同的 AI,他们各司其职,高效的完成自己的工作(已经类似于前文所提及的 蜂群 概念,只不过是我本人来担当的主编排 Agent 这个位置)
  2. 我能够把控项目的细节与质量
  3. git 提交很方便(虽然这个可以交给 Codex 来完成,但是它没法在这个前面做到这么多的测试保证本次提交的内容是接近更完美的水平)

缺点

  1. 我的大部分时间都浪费在了:等待、阅读文档、测试bug等比较无聊、低效率,但不得不做的事情
  2. 工作过程中需要不断的在不同的浏览器窗口、系统角色、AI会话、开发软件窗口中不断的切换查看,比较耗神(心累)
  3. 没有引入测试自动化的 MCP 工具,导致的测试效率低下

结果

开发周期

如果不算上前面的第一次开发与第二次开发的前面重构的时间,那么这个项目的开发时间则是六天,如果我在中间没有被 Open AI 限制请求的话我可能开发周期可以在缩短一天,其中有一天因为 Open AI 的 429 强制停止了我的实验。

交付成果

  1. 交付了一个具有一下功能模块的系统:
  • N-M 的组织管理模块
    • 任意一个角色可被管理员赋予属于多个【归属单位】、【归属子单位】
    • 靠一张【归属子单位】表实现多层级的【归属子单位】表达
    • 带有约束检查的【归属子单位】支持删除该树下的所有子单位(级联删除),并将所有归属的用户进入一个【虚状态】,需要管理员进行处理
  • 带有四级角色权限并保证纵向、横向权限不泄露的 RBAC 设计
    • 超级管理员
      • 管理系统中的所有内容(除了不用考试)
    • 普通管理员
      • 管理自己被超级管理员所被赋予的【归属单位】下的所有内容(普通管理员无法 CURD 到自己未被授予的【归属单位】中的内容)
    • 老师
      • 管理自己被管理员所赋予的【归属子单位】下的除用户管理外的全部内容(无法 CURD 到自己未被授予的【归属子单位】中的内容)
    • 学生
      • 考试、接受公告、查看考试成绩、修改个人信息(无法 CURD 到自己违背收与的【归属子单位】中的内容)
  • 考试模块
    • 带有题目(需要设置难度、建议分数)
    • 试卷(支持自动与手动组卷、难度调节、本张试卷中实际分数)
    • 考试场次(可控制是否允许学生进行粘贴的行为、考试时长)
    • 学生端考试(支持自动同步当前答案到服务器端可供恢复记录与本地缓存、支持意外退出考试后重新进入考试)
    • 阅卷(支持快捷键的人性化操作阅卷)
    • 重考(若学生端发生了什么异常,可向老师汇报,老师向管理员汇报,可申请进行重考)
    • 自动评分(客观题可以全自动评分,填空题支持模糊对比内容进行评分,简答题仍需人工阅卷)
  • 登录注册模块
    • 支持手机号、邮箱、账号 + 密码的方式登录
    • 支持邮箱 + 邮箱验证码的方式登录
    • 支持通过【忘记密码】中的接受邮箱验证码方式重置新密码
    • 注册时检查账号无法与手机号、邮箱号重复,各个账号保证自己的各字段的唯一性
  • 公告模块
    • 支持关键词查询
    • 支持已读标记
    • 支持弹窗公告提醒、查看
    • 超级管理员管理所有的公告
    • 管理员角色支持批量选择已读、一键全部已读
    • 老师能对被管理员所赋予的【归属子单位】中进行单一班级、自己所有的班级,进行公告管理自己所拥有的
    • 学生端只能查看到被发送到的公告内容,且无法查看到比自己注册更早的公告
  • 日志模块
    • 记录每份考卷的打分日志
    • 答题记录(同时作为业务的一部分)
    • 考试记录(同时作为业务的一部分)
  • 验证码模块
    • 拥有 Gmail 与 QQ邮箱,两条线路进行主线与备线的验证码的发送方案
  1. 项目推进过程中的各个版本中变更所写出的接口文档、设计文档、审计文档、sql迁移脚本、sql全量数据脚本
  2. 一个代码统计脚本与产出的统计数量
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
=== 项目代码资产分类统计(模块 × 语言)===
扫描文件数: 181
有效代码总行: 42,931

【按模块汇总】
module | files | total | effective | comment | blank | bytes
---------|-------|--------|-----------|---------|-------|--------
backend | 65 | 14,568 | 12,555 | 341 | 1,672 | 455,163
frontend | 63 | 13,864 | 12,005 | 148 | 1,711 | 494,104
docs | 24 | 11,490 | 9,338 | 0 | 2,152 | 292,384
sql | 23 | 8,653 | 6,860 | 1,261 | 532 | 647,049
root | 5 | 2,655 | 1,887 | 635 | 133 | 78,470
other | 1 | 354 | 286 | 0 | 68 | 8,967

【按语言汇总】
language | files | total | effective | comment | blank | bytes
-----------|-------|--------|-----------|---------|-------|--------
js_like | 70 | 14,822 | 12,049 | 1,008 | 1,765 | 448,487
ts_like | 3 | 35 | 26 | 2 | 7 | 780
vue_html | 203 | 1,131 | 772 | 4 | 355 | 39,540
vue_css | 41 | 1,883 | 1,501 | 0 | 382 | 24,374
vue_js | 41 | 7,567 | 6,599 | 110 | 858 | 200,843
html | 1 | 14 | 13 | 0 | 1 | 355
css_like | 1 | 80 | 70 | 0 | 10 | 1,269
sql | 23 | 8,653 | 6,860 | 1,261 | 532 | 647,049
json | 8 | 4,155 | 4,144 | 0 | 11 | 142,875
markdown | 28 | 13,169 | 10,834 | 0 | 2,335 | 347,195
other_text | 6 | 75 | 63 | 0 | 12 | 3,466

【模块 × 语言交叉表(effective_lines)】
module | js_like | ts_like | vue_html | vue_css | vue_js | html | css_like | sql | json | markdown | other_text | row_total
---------|---------|---------|----------|---------|--------|------|----------|-------|-------|----------|------------|----------
backend | 11,171 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1,348 | 0 | 36 | 12,555
frontend | 744 | 26 | 772 | 1,501 | 6,599 | 13 | 70 | 0 | 2,251 | 3 | 26 | 12,005
docs | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 9,338 | 0 | 9,338
sql | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 6,860 | 0 | 0 | 0 | 6,860
root | 134 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 545 | 1,207 | 1 | 1,887
other | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 286 | 0 | 286

结语

本篇博客编写一方面是为了给以后的人看到这篇博客时看到再现时代的普通人是如何利用 AI 进行代码编写效率上的提升,一方面也是为了让我自己有一个梳理这个项目内容的机会。
如你所视,本项目中的代码里光是前端与后端的代码加起来 2.4W 行的代码,以我一个普通人是完全无法再这么短期的时间内消化的了这么多的代码。同时我还阅读很多的 Codex 的 plan 、各种报告,Gemini 的对话内容。短时间内阅读这么多的内容,对人脑的负担来说非常的大。
因此,我需要有一个输出的地方让我来进行一次消化理解这个内容。这篇博客的编写就是这个契机。