1. 毕业设计常见痛点:时间紧、技术栈不熟、工程规范缺失
做毕业设计时,90% 的同学都会踩到这三颗雷:
- 时间紧:导师一句“系统要上线”,留给自己只有 4-6 周,还要兼顾实习、考研、面试。
- 技术栈不熟:课堂作业只写过单体脚本,突然要搞微服务、容器化、高并发,搜索引擎都来不及。
- 工程规范缺失:没有 Code Review、没有单测、没有日志规范,代码能跑就行,结果答辩现场一演示就崩。
刷题平台看似“小项目”,但涉及题库管理、自动判题、用户体系、权限控制、并发安全,如果全部手写,光一个“沙箱隔离”就能卡两周。于是我把 AI 辅助开发当成“外挂”,用 LLM 当“副驾”,自己当“主驾”,在 3 周内交付了一套可灰度发布的系统,顺利拿到优秀。
2. AI 工具链选型与落地姿势
我主要用了两款工具:GitHub Copilot(IDE 内嵌)与通义灵码(Web 端对话)。实测下来,它们在不同阶段各有胜负。
2.1 需求 → 原型阶段
把“我要一个刷题平台”这句话丢给通义灵码,让它生成用户故事,再让它用 PlantUML 画用例图。LLM 会给出:
- 学生:刷题、提交、查看判题结果
- 教师:录入题目、管理班级
- 管理员:开关题、查看报表
我只需人工删掉“教师”角色(降低范围 Coping),5 分钟就能拿到原型图,比手画快 10 倍。局限:LLM 容易过度设计,比如硬塞一个“积分商城”,需要人工裁剪。
2.2 API 设计阶段
在 VSCode 里新建openapi.yaml,敲paths:后让 Copilot 自动补全。提示词越具体越好:
# 提示词:为刷题平台生成 /api/problem/{id} 的 GET、POST、PUT、DELETE,返回字段包含 id, title, tags, difficulty, sampleIn, sampleOutCopilot 能一次生成 60 行符合 OpenAPI 3.0 的规范,再让通义灵码自动生成 Mock Server(prism),前端同学就能并行开发。局限:枚举值容易写错,比如把 difficulty 写成 “Middle”,需人工校正。
2.3 判题逻辑生成
这是最容易翻车的环节。我先把需求拆成原子动作:
- 拉取题目输入输出样例
- 把用户代码写入
main.py - 用 Docker 启动沙箱,挂载
/tmp为只读 - 执行
python main.py < in.txt > out.txt - 比对 out.txt 与 expected.txt,返回结果
把上面 5 句话粘进 Copilot,它能一次性生成 80 行 Python 函数,包含subprocess.run、docker-py调用、异常捕获。我只需补两行资源限制:
--memory=128m --cpus=0.5就能防止死循环把宿主机打挂。局限:LLM 默认不写“超时控制”,必须手动加--timeout=5s,否则判题进程会堆积。
3. 核心模块实现细节
3.1 题目存储模型
采用“题目基表 + 版本控制”方案,避免频繁改表结构。
- 基表:problem(id, title, difficulty, tags, creator_id, created_at)
- 版本表:problem_version(id, problem_id, description_md, input_desc, output_desc, sample_in, sample_out, time_limit, memory_limit, created_at)
好处:同一题目可迭代多个版本,回滚只需改外键;坏处:查询时要 join,性能 100ms 内可接受。
AI 生成 SQL 时,Copilot 会忘记给problem_id加索引,导致全表扫描,务必手动explain检查。
3.2 基于 Docker 的判题沙箱
沙箱核心就三句话:
- 镜像最小化:
python:3.11-slim仅 55 MB,冷启动 1.2s。 - 资源隔离:使用
docker run --rm自动回收,配合--pids-limit 20防止 fork 炸弹。 - 结果回传:容器内把 exit_code、stdout、stderr 写进
/tmp/result.json,宿主机挂载同一 volume 读取。
关键代码片段(Python):
import docker, json, uuid cli = docker.from_env() container = cli.containers.run( image='python:3.11-slim', command=f'python /tmp/main.py < /tmp/in.txt > /tmp/out.txt 2>&2; echo $? > /tmp/exitcode', volumes={f'/tmp/sandbox/{uuid.uuid4()}': {'bind': '/tmp', 'mode': 'rw'}}, mem_limit='128m', nano_cpus=5e8, # 0.5 核 detach=True, remove=True, network_mode='none' # 禁止外联 ) container.wait(timeout=10) with open(f'/tmp/sandbox/{uuid.uuid4()}/result.json') as f: res = json.load(f)Clean Code 原则:函数名judge只做一件事,超时、异常、日志全部抛给调用方,保持单一职责。
3.3 JWT 用户认证
毕业设计最怕“演示时掉登录”。我选 JWT 而非 Session,原因:
- 无状态,方便容器水平扩展
- 省掉 Redis,减少依赖
生成与校验流程:
- 登录接口验证账号密码 → 生成 access_token(15 min)+ refresh_token(7 d)
- 网关层(OpenResty)统一校验签名,失败直接 401,不穿透到业务
- 刷新令牌接口需校验指纹(fp=Hash(UA+IP))防止重放
AI 会忘记给 refresh_token 加旋转策略,需人工补“一次性刷新即失效”逻辑,避免无限续命。
4. 性能与安全考量
- 防代码注入:沙箱内禁止
__import__('os'),用RestrictedPython或sys.modules.clear()先清理 - 资源隔离:上文已用
mem_limit、nano_cpus,再叠加ulimit -f 1024限制输出文件大小 - 请求幂等:提交接口带
client_token,后端用 Redis SETNX 做幂等,5 min 自动过期,防止用户疯狂点击 - 日志脱敏:把用户代码写进日志前,先截断 500 字符,防止超大源码把 ELK 打爆
5. 生产环境踩坑实录
- 冷启动延迟:Docker 每次
--rm会重新拉镜像,若机房网络差,一次 pull 要 30s。解决:提前docker pull到本地,并开启 Docker Daemon 的live-restore,防止宿主机重启后镜像被清。 - 判题超时控制:Python
subprocess默认等无限,需显式timeout=10,否则堆积 200 个容器就把 CPU 打满。 - 数据库连接泄漏:FastAPI 原生
SQLAlchemy要在@app.on_event("shutdown")里dispose(),否则 MySQL 会报Too many connections。 - 文件句柄耗尽:沙箱目录未清理,
/tmp下 5 万个uuid文件夹把 inode 占光。用tmpwatch每小时清一次,或把 volume 类型换成tmpfs。
6. 可维护性边界:AI 代码不是银弹
- LLM 擅长“模板”,不擅长“业务”:它能把 JWT 校验写漂亮,却搞不懂你们学院“教师可以查看学生提交次数”的奇葩需求。
- 测试用例必须自己写:Copilot 生成的
test_judge只覆盖 AC 场景,WA、TLE、RE 全没测,结果答辩现场演示 RE 直接 500。 - Code Review 不能省:AI 喜欢“魔法数”,比如把 128M 写死,后期改参数要翻十处,最好提变量
DEFAULT_MEM_LIMIT。
一句话:把 AI 当“初级外包”用,它出活快,但接口契约、边界测试、性能调优,还得自己拍板。
7. 留给你的扩展作业
- 排行榜:用 Redis Sorted Set,score 为提交通过时间戳,实现“刷题速度榜”
- 错题本:把用户 WA 的题目 id 写进 Mongo 数组,每晚离线跑 MR 任务,推荐相似 Tag 的新题
- 移动端适配:把模板引擎换成 Nuxt3,开启 SSR,分数秒开
动手前先写单元测试,再让 AI 帮你填逻辑,你会发现“人机结对”才是毕业设计的最优解。祝你 3 周上线,4 周优雅答辩,早日把“刷题平台”写进简历,收割心仪 Offer。