尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

AIGC在线编程评测平台:从题目生成到自动判题的全链路实践

发布时间:2026/9/29 18:12:15

资讯中心
01
ARTICLE

AIGC在线编程评测平台:从题目生成到自动判题的全链路实践

AIGC在线编程评测平台:从题目生成到自动判题的全链路实践
简介一套基于AIGC的在线编程题目评测系统完整源码面向编程教育平台开发者、算法练习系统设计者及具备Java基础的学员。系统整合人工智能生成内容技术覆盖编程题目动态生成、代码自动评测、实时反馈、学习路径推荐等核心模块并包含用户认证授权、数据缓存与异步消息处理机制属于可运行的完整在线评测解决方案。压缩包共61个文件以46个Java源码文件为主辅以XML配置、YAML与Properties配置文件及SQL初始化脚本另有README说明文档整体仅81KB轻量易部署。目前已有52人学习下载。借助该资源读者可掌握AIGC题目生成与评测系统的整体架构和实现细节学习题目生成策略、代码评判流程、缓存设计及消息异步处理等关键代码适合作为课程设计、毕业设计或企业实训的参考蓝本。1. AIGC在线编程评测平台为什么说它是编程练习系统的下一个形态如果你带过编程入门班或者维护过学校的OJOnline Judge系统大概率经历过这样的场景题库不够用、学生提交代码后要等几秒才有结果、不同水平的学员挤在同一套题目里要么太简单要么太难。这个基于AIGC的在线编程题目评测系统把生成式AI塞进了传统OJ的每个环节——题目由模型根据知识点自动生成代码提交后由评测引擎自动判定结果通过异步消息实时推回前端再基于答题记录给学生推学习路径。它解决的不是「有一个能判题的网站」而是「从题目供给到反馈闭环的整条链路能不能自动化」。适合正在做在线教育平台、实验教学系统或者想给现有OJ加AI能力的开发者。下面按模块拆。2. 题目生成与预处理让AIGC输出的不只是「看起来像题」2.1 生成链路设计这套系统的题目生成不是简单调一次大模型接口就完事。它把一条生成请求拆成四段知识点解析、题目草案生成、测试用例生成、题目清洗入库。知识点解析这一步决定了后续所有环节的边界——模型拿到「数组、双指针、LeetCode 中等难度」这类原始输入后先被要求输出结构化的题目定义包括函数签名、输入约束、时间复杂度和空间复杂度要求。# 题目生成管线伪代码 knowledge_point extract_kp(user_input) # 数组双指针中等 prompt build_generation_prompt(knowledge_point) raw call_llm(prompt, temperature0.7, max_tokens2048) parsed parse_llm_response(raw) # 强制JSON结构 testcases generate_testcases(parsed) cleaned quality_check(parsed, testcases) # 规则模型双重过滤 save_to_db(cleaned)这里最关键的是parse_llm_response这一步。模型输出经常出现JSON截断、代码块标记残留、中英混杂的问题所以解析函数里要做三层兜底先用正则提取json代码块失败则按大括号匹配截取再失败就直接丢弃该条结果重新生成。temperature参数设到0.7是为了在多样性避免同一知识点反复生成雷同题和稳定性避免题面逻辑飘之间取平衡。生成测试用例时建议单独调用一次模型而不是复用题面生成的结果因为模型在生成题面时往往只会给出示例输入输出不会考虑边界值、大数溢出、空数组这些判题必须覆盖的情况。2.2 题目入库前的质量防线题目生成完直接能用吗不能至少有三个坑是常态题目语义不完整比如漏了「输出格式」说明、参考解法本身有bug、测试用例和题目描述不一致。这套系统的质量检查模块用规则加模型双通道做过滤——规则层查题面长度、是否包含函数签名、测试用例是否能被参考解法跑通模型层则是把题面重新丢给模型让它扮演学生回答「这题在考什么、边界条件有哪些」两次输出的知识点标签做比对。RULES [ (题面长度, lambda s: len(s) 80), (函数签名存在, lambda s: re.search(rdef \w\(|class \w, s)), (测试用例可执行, lambda c: len(c[cases]) 5), ]规则条件里「测试用例至少5条」是硬性要求。低于5条的情况下模型生成的参考解法可以通过一些很偏的巧合路径通过全部用例但用户提交的稍微不同的合法解法会挂。另一个容易被忽略的点是题面语言如果你把用户手动添加的英文题也丢进生成链路模型会倾向输出英文题面和系统的中文题目混在一起后面按语言筛选时数据会脏。我一般会在入库前加一道语言检测非目标语言直接标记待翻译。2.3 和直接用ChatGPT生成题目的差别有人会问为什么不做成对话式生成因为对话式生成缺少「可回滚」的确定性。用户和模型来回聊三遍改需求得到的题目可能每一版都不一样评测系统的题库要求的是稳定的题目ID、稳定的测试用例集、稳定的难度标签。这套AIGC链路本质上是用「结构化约束加多轮清洗」把模型输出的随机性压到可接受范围内。从实际数据看经过完整管线过滤后的题目合格率能到八成以上剩下的两成大多是逻辑复杂度过高或题面有歧义需要人工介入这个比例在可接受范围。3. 代码自动评测沙箱隔离、测试用例比对与实时反馈链路3.1 评测沙箱的设计逻辑代码评测最核心的问题是安全。用户提交的代码是不可信的它可能试图读服务器文件、写磁盘、申请无限内存或者fork子进程。这套系统的评测沙箱走的是一条主流OJ的路子容器隔离加资源限制。每个提交跑在独立的容器里通过cgroup限定CPU时间、内存上限、PID数量同时把网络接口直接禁掉。# 容器运行配置 docker run --rm \ --network none \ --memory 256m \ --cpus 1 \ --pids-limit 32 \ --read-only \ --tmpfs /tmp:rw,size64m \ -v /var/judge/submissions:/sub \ judge-image:python3.11 python3 /sub/main.py /sub/input.txt--network none断网是为了防止恶意代码做数据外带--read-only让根文件系统只读写操作只能走tmpfs挂载的 /tmp 目录且上限64MB。CPU限制设的是1核--pids-limit 32防止fork炸弹。这些参数任何一个漏掉都可能出事——尤其是--pids-limit不设的话一段几行代码就能把宿主机的进程表打满。判题镜像里预装的语言运行时不要带包管理器越干净越好标准库之外的第三方依赖需要在题目配置里显式声明并预编译进镜像。3.2 评测流程与反馈闭环评测流程分三步走编译、运行、比对。编译阶段根据题目配置的语言模板执行对应的编译命令比如Go就是go buildJava就是javac编译失败直接返回编译错误信息。运行阶段执行编译产物喂入测试用例输入捕获标准输出。比对阶段把实际输出和预期输出做精确匹配——这里有个容易踩的坑是行尾空格和末尾换行评测系统默认要做严格模式比对但建议在比对前做一次统一的换行符规范化处理。def normalize_output(s: str) - str: return \n.join(line.rstrip() for line in s.rstrip().splitlines())答案呈现给用户的方式比大多数OJ更细每个测试用例分别给出「通过/失败/超时/运行错误」状态失败的用例展示你的输出和期望输出的差异而不仅是「wrong answer」。这套反馈机制看起来简单实际对用户做题体验的提升非常明显。你的代码卡在哪个用例上、差在什么地方、是边界没处理还是格式错了一目了然。从行为上看用户在看到具体用例失败信息后的平均重试次数明显低于只返回「WA」的原始版本。评测结果不直接写回数据库再等前端轮询而是通过消息队列异步发布前端通过WebSocket订阅结果事件。整个链路的延迟分解是容器创建约200ms代码编译视语言而定运行阶段以毫秒计整体从提交到看到结果控制在1-2秒内。这个实时性数据是传统OJ通常3-5秒轮询周期的显著优化。3.3 多语言评测的边界系统支持的主流语言集中在Python、Java、Go、C但语言差异会在评测细节上冒出来。Python以其无编译期的特性获得最快的反馈速度是新手友好度最高的入口Java因为JVM冷启动缘故首次运行会比Python多花几百毫秒但它的强类型特性天然拦截了一类低级错误Go的编译时间和运行时间都很稳定很适合做性能敏感题目的评测基准。每种语言的内存计算方式也不一样。Python只能统计到进程常驻内存而Java堆内存按Xmx上限的70%计这些差异需要做成配置项而不是写死在评测引擎里。一个可行的做法是为每种语言维护一个default.json配置里面包含编译命令、运行参数、超时阈值、内存计算系数这样新增语言时只需要新加一个配置目录不用动评测核心代码。4. 缓存、异步与学习路径让系统扛住高并发的三个关键模块4.1 Redis缓存层的热点设计评测系统的高频请求集中在题目列表、题目详情、提交状态查询三类。这三类的查询特点是读多写少热点集中且短时间流量可以非常大。系统用Redis做了两层缓存第一层缓存题目实体的JSON序列化结果过期时间15分钟第二层缓存用户对某道题的最优提交结果用于做题状态展示。# 缓存层设计 def get_problem(problem_id: int) - dict: cache_key fproblem:{problem_id} cached redis.get(cache_key) if cached: return json.loads(cached) data db.query_one(problem_id) redis.setex(cache_key, 900, json.dumps(data)) return data缓存过期时间设15分钟而不是永久是因为题目可能被管理员手动修正措辞调整、测试用例补充。手动修改时不直接更新Redis而是通过删除缓存键的方式让下次请求重建。这个细节比直接覆盖缓存更安全——如果更新过程中写入的JSON结构有问题覆盖缓存会把坏数据提供给后续所有请求而删除键最多导致一次缓存穿透。你说缓存被穿透怎么办正常的做法是加互斥锁只有一个线程回源查库其他线程等锁释放后读缓存。这套系统的单机版上Redis和Web服务在同一台机器时加不加锁区别不大但一旦拆成多副本部署不加锁就会发生同时回源数据库压力成倍增加。如果按这套系统的默认配置走单机短期内不处理也是能跑的但建议在代码里保留锁的逻辑。4.2 异步消息评测从请求到回写全流程评测请求是天然异步的Web容器很快把提交请求落库随后把评测任务扔进RabbitMQ判题机从队列里拿任务去执行完成后把结果发到通知队列Web服务消费通知队列更新数据库并通过WebSocket推送给在线用户。这套「生产者-消费者-结果回调」的三段式结构把评测执行时间从请求链路里剥离开了。web - submit API - MySQL(状态排队) - RabbitMQ - judge-worker - RabbitMQ(result) - notifier - WebSocket推送给用户队列的配置有讲究评测任务队列用direct交换机绑定judge_worker_queue消费者设置预取数量prefetch1而不是默认值。原因很简单评测任务执行时间不均匀有的题目C编译加运行只要几百毫秒有的Java题要好几秒如果预取数量大于1先拿到的Java任务还在跑后面的C快速任务也发给同一worker反而导致快速任务排队等慢任务。prefetch1保证worker每次只处理一个任务处理完再接下一个这是在任务耗时方差大的场景下更合理的设置。失败重试机制也是消息系统必须考虑的。评测任务可能会因为判题机宕机、容器镜像损坏、代码触发了安全限制等原因失败正常的策略是超时或执行异常的消息投递到延迟队列5分钟后重新消费最多重试3次重试超过3次的进死信队列同时通知后端管理员介入处理。这套重试机制在初始版本里没有实现当时评测任务失败后直接丢弃有个恶性bug导致一批Python提交在超时后无结果返回、前端一直转圈后来才把延迟队列补上。4.3 学习路径推荐基于行为数据的轻量推荐学习路径推荐模块的实现思路不算复杂数据基础是每名用户的答题记录。系统把题目的知识点标签比如「动态规划」「双指针」「二叉树」和答题状态通过、编译错误、运行错误、超时组装成用户向量然后做两个维度的匹配用户薄弱知识点和未做题目之间的难度匹配、以及未做题和当前用户水平的难度匹配。def recommend_next(user_id: int, limit: int 5): profile load_user_profile(user_id) # 知识点-掌握度 weak_points sorted(profile.items(), keylambda x: x[1])[:3] candidates db.query(未做过 知识点匹配 难度±1级) return rank_by_estimated_success_rate(candidates)这里的rank_by_estimated_success_rate是推荐效果的关键把候选题目往用户的历史正确率曲线上一套预估正确率在30%-70%之间的题目优先推荐。低于30%说明太难做了打击信心高于70%说明太简单没有学习增益。基于这个阈值过滤后推荐出来的题目通常用户的实际通过率会落在20%-80%区间比较合理。推荐不追求算法炫技矩阵分解、图神经网络这些在数据量不够时都容易过拟合几十万条答题记录拼不过一个「关联规则加难度匹配」的基准线。做学习路径的第一版把数据基建做好、记录做全远比上一个看似智能的模型重要。这里的数据记录包括做题用时、查看提示次数、失败用例的差异类型这些在推荐时都是很好的信号。5. 落地避坑手册AIGC评测系统最常见的八个深坑5.1 生成的测试用例会「自我印证」现象用参考解法验证题目时完全通过但用户提交的正确解法被判「答案错误」。原因模型生成的测试用例是基于「它自己的参考解法」写的不是基于「题目描述」写的。当参考解法有隐藏bug或语义偏差时测试用例也跟着歪了。这是AIGC题目生成最隐蔽的问题——测试用例和参考解法互相印证形成了一个校验闭环但这个闭环和真实题目语义是脱离的。解决加一道「独立验证」流程用第二套模型或者同一个模型的不同temperature设定不看参考解法只根据题面描述手写一遍解法再用第一套测试用例去跑。两份解法输出不一致的地方就是测试用例的疑点标记出来人工确认或直接丢弃。5.2 沙箱容器句柄泄漏导致判题机假死现象判题机运行几天后响应越来越慢最后所有评测任务都卡在「排队中」。原因容器删除后与容器关联的文件句柄尤其是日志管道和临时文件映射没有被应用正确回收。每次评测泄漏几个fd几万次评测后把进程可用的文件描述符耗尽判题机再也无法创建新容器。解决判题机进程加一个fd数量监控超过阈值强制重启。同时不要用shell脚本包装docker命令而是直接在应用代码里调用Docker SDK退出时用with上下文管理器确保资源释放。监控命令可以用ls /proc/{pid}/fd | wc -l看数量曲线超过500就该排查泄漏点。5.3 Java内存上限踩了Xmx的坑现象设置了-Xmx256m但进程实际占用的内存远超256MB多个Java容器同时跑把宿主机撑爆。原因-Xmx限制的是Java堆不是整个进程。JVM的Metaspace、JIT编译产物、线程栈、GC相关的本地内存都不受Xmx控制加起来可能翻倍。解决认识两个层面cgroup层限制256MBJVM层设-XX:MaxRAMPercentage50让JVM自行感知容器配额并且只分配一半内存给堆给本地内存留出空间。坑在Java语言评测里尤其突出不做这个调整C和Go的评测都很省内存Java一高并发必爆。5.4 WebSocket长连接被前置代理切断现象用户提交代码后结果偶尔不推送给前端刷新页面才能看到评测结果。原因前置Nginx的proxy_read_timeout默认60秒就断掉空闲的WebSocket连接但前端没有自动重连逻辑。评测在60秒以上时比如Java大测试集连接早被切断结果推送发了个寂寞。解决Nginx加proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection upgrade; proxy_read_timeout 3600s;四件套前端加心跳每30秒ping一次和断线重连按1s/2s/4s指数退避逻辑。这两边都要改只改一边下次还会踩。5.5 同一道题的知识点标注自相矛盾现象题目生成时标记为「中等」难度用户平均通过率只有12%明显应该是「困难」。原因难度标签是模型生成第一版题目时就定的但当时没有参考「用户行为数据」。模型对难度的估计偏向「按文本复杂度判断」而实际难度还受题目坑点数量、边界条件复杂度、常见错误率影响。解决给每道题加「动态难度校准」字段。答题数据积累到50次以上后根据实际通过率覆盖初始难度标签。同时模型生成时给到更明确的难度锚点描述比如「中等难度应该是接近LeetCode 198号题的水平」比仅仅说「中等」管用。5.6 缓存穿透热点题刷新时把数据库打垮现象管理员修改了一道热门题后缓存键被删除一瞬间大量请求全部穿透到数据库数据库压力飙升。原因删除缓存键的瞬间恰好有高并发请求到来所有请求都在缓存里没查到同时回源查库。这道题越是热门问题越严重。解决加互斥锁实现「单飞」只有第一个请求回源其余等待结果或者给缓存额外加一层「null值缓存」防穿透。后者实现代价低查询数据库结果为空时也写一个带短过期时间比如60秒的空值到缓存后续请求不再打到数据库。5.7 异步消息顺序错乱导致状态回退现象用户连续提交两次代码第二次是想覆盖第一次最后看到的结果是第一次的。原因两条消息被不同的worker消费第二次的评测结果比第一次先回到数据库。或者多次提交是意图抢先后一次提交在一次前返回。用户看到的最终状态是「较旧」的那次结果造成「代码明明改了结果没变化」的错觉。解决在提交记录表上加attempt_seq序号Web服务写数据库时递增评测结果回写时只更新attempt_seq与最新attempt_seq匹配的记录并用UPDATE WHERE attempt_seq ?保证条件更新。这比用时间戳更可靠——时间戳在毫秒级并发下会撞车。5.8 LLM接口不稳定导致题目生成偶发失败现象批量生成100道题运行到第30道时接口超时整个任务失败前面已生成的几十道题也随之丢失。原因题目生成是批量任务按「全部成功或全部回滚」的方式处理事务。一次偶发的接口超时导致整个批次数据回滚前面成功的也白做了。解决批量改成「逐条成功逐条提交」的策略每条题目生成后立即独立落库成草稿状态所有题目完成后统一切换到正式状态。超时的题目单独重试不影响已完成记录的保存。这个改动把一次批量任务的失败率影响从「整批丢失」降到「个别题目缺失」。6. 评测结果的可信度验证从功能上线到数据可信的三层校验法系统开发到能跑通不是终点评测结果能不能被用户相信才是运行系统的立身之本。我的做法是三层校验第一层是新题目上线前的规则校验。每道新题必须在初始化后的系统上跑三遍参考解法三次结果必须完全一致且运行时间波动不超过20%。如果参考解法本身有隐藏依赖比如随机数、系统时间三次运行的结果会有差异直接暴露题目的非确定性特征。这道题要么改成确定性解法要么在题目描述里明确说明输出格式的不确定性。校验脚本很简单for i in 1 2 3; do docker run --rm judge-image python3 /sub/main.py input.txt output_$i.txt done diff output_1.txt output_2.txt diff output_2.txt output_3.txt echo deterministic第二层是AB正确率对比。挑20道题目覆盖简单、中等、困难三个难度随机分配给两组各50个用户做题。如果AIGC生成的题目组通过率与经典题目组在同一知识点的历史通过率差距在10个百分点以内认为题目难度标注和反馈有效性可信。这个验证通常在系统上线前做一轮上线后每周抽10道生成题再跑一次防止模型或提示词迭代后生成质量漂移。记得AB两组的用户数量的起点差异要控制用同样的男女比例、同样的年级分布或者同样的编程经验自评分区间不然两组通过率差异会混淆「题目难度」和「用户水平」两个变量。第三层是异常提交追踪。从评测日志里拉出所有「用户提交了完全合法代码却被判错」的case人工审阅这些case到底是用例错了还是参考解法错了。我记录的这个比例在连续两周里稳在2%以下说明整个评测链路的质量可信。超过5%就说明生成环节出问题了需要回滚最近一次Prompt改动。这套方法不一定适用于所有场景但值得你在自己的环境里试一下——它能拦截「功能上线了但没有实际用过」的假象。希望这些拆解能帮到你。从那以后我每次给系统加新功能都强制走一遍AB对比和异常追踪的流程不只是做功能的自检更让我自己在代码里少点玄学式的自信。本文还有配套的精品资源点击获取
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

◈

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

◐

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

▲

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。