← 返回 PaperDaily
大模型与智能体
SIGCSE 2025:四年800题,答案直接生成评分器
编程教育里最耗人精力的不是写题,而是给题配自动评分器。这篇SIGCSE 2025论文换个思路:让参考答案直接生成评分器,四年近800道题、百万级提交验证过的方案。做编程教育、在线测评、想提升出题效率的都值得一读。
龙哥读论文
发布于 2026-09-05 00:31:09
阅读 6
查看原文
原论文信息如下:
编程教育中的自动评分困境
很多教编程的老师都有过这种体验:出一道人机交互的练习题其实不难,真正折磨人的是给这道题写自动评分器。传统做法是人工枚举测试用例——得提前猜测学生可能会犯哪些错,然后针对性地设计输入输出对。这个过程既繁琐又容易出错,出题老师常常陷入「写了半天用例,结果学生一个邪门写法直接绕过所有检查」的尴尬局面。更麻烦的是,写完一堆测试用例,你压根不知道评分器到底准不准:用少了判不出错误代码,用多了又拖慢评分速度,而且糟糕的反馈还会让初学者一头雾水。
这个问题放到大规模编程入门课(CS1)里,就更加尖锐了。大家都很清楚,学生学编程最需要的就是大量刷题。可题目越多,评分器也越多——每道题都得配一个。如果评分器写起来又慢又不准,题库就永远只能是小打小闹,支持不了数千学生、上百万次提交的大规模教学场景。这已经不只是效率问题,而是直接卡住了整个教学模式的脖子。
但如果换个思路呢?自动评分和软件测试有个本质区别——出题老师手里有参考答案啊!软件测试里,测试用例和被测代码是互相校验的关系,谁也不比谁更权威;可编程题的参考答案就是唯一的「事实来源」:凡是跟参考答案行为一致的提交就算对,不一致就算错。那为什么不直接让参考答案来生成评分器,跳过人工枚举测试用例这一步?
这就是伊利诺伊大学团队在SIGCSE 2025上提出的「参考答案生成自动评分」方法,这个想法来自Geofrey Challen和Ben Nordick。他们把这套思路做成了名为Questioner的工具,在四年间支撑了近800道题的题库,被数千名学生使用,完成了数百万次提交的评测。图1展示的就是这些年题库的增长情况,可以清楚看到一门CS1课是如何靠一个老师积累出庞大题量的。
要理解这个工具的价值,先得明白传统编程题出题流程的痛点在哪里。在典型的在线评测系统(如LeetCode、HackerRank或课程自建平台)中,一道题从构思到上线,通常包含以下步骤:首先是题目描述与示例的撰写,这一步相对简单;其次是确定输入输出格式和边界条件,这需要出题人对问题域有深入理解;最后也是最耗时的,是编写一组能有效区分正确与错误提交的测试用例。这最后一步看似简单,实则暗藏玄机——测试用例太少,容易放过错误代码;测试用例太多,又可能过度约束,把正确的替代解法误判为错误。更糟糕的是,出题人往往只能凭经验猜测学生可能犯的错误,而实际学生的错误模式千奇百怪,远超出题人的想象。
Questioner的核心洞察在于:既然参考答案已经定义了「正确」的标准,那么评分器的任务就变成了「验证提交是否与参考答案行为一致」。这个视角的转换,把「写测试用例」这个主观性很强的任务,变成了「自动生成输入并比较输出」这个可以完全自动化的流程。出题人不再需要猜测学生怎么犯错,只需要提供一个正确的参考答案,剩下的交给系统去探索输入空间、验证行为一致性。这不仅大幅降低了出题门槛,还让评分器的质量变得可量化——通过变异测试,系统能明确知道自己的评分器能识别哪些类型的错误,不能识别哪些类型的错误。
解决方案生成的自动评分:核心思想
这套方法的核心逻辑非常直白:出题人只需要写题目描述和参考答案,剩下的事全部交给系统。系统拿到答案后,先分析它长什么样——比如例1里的`areIncreasingStrict`方法,接收三个整数,返回布尔值。知道了这些,系统就能用内置的随机数生成器造出大量合法的测试输入,比如`(1, 2, 3)`、`(-5, 0, 5)`之类。
但关键问题来了——测多少次才够?测少了怕漏掉错误提交,测多了又拖慢速度。最麻烦的是,评分器都没影呢,上哪儿找已知的错误代码来验证精度?论文团队做了个聪明到有点「自己证明自己」的骚操作:拿参考答案做变异,自动生成错误代码。
比如例2里展示的三个变异体:把条件里的`>`改成`>=`、把逻辑判断整个取反、或者直接删掉一个条件。这种「变异测试」的操作手法,本质上是在模拟学生可能犯的各种典型错误。Questioner从Jeed工具包集成了37种Java和Kotlin变异算子,覆盖了条件边界的偏移、运算符替换、删减逻辑等常见错误模式。
做完变异后,系统就拿着这些肯定有问题的代码来回测试:生成随机输入,比较参考答案和变异体的输出是否一致。等把所有变异体都识别出来,需要的迭代次数也就确定了——例1里用了21次测试就成功识别出了全部9个变异体。如果变异体没被识别出来,说明随机生成的输入还没测到点子上,系统会把这个「漏网之鱼」展示给作者,让作者决定是补充特殊输入还是禁用这个变异。
这样生成的评分器,精度是可以验证的——因为系统知道对照实验的结果。它和传统测试框架有本质区别:传统方式是你猜测学生的错误然后写用例去抓,而这里是用变异体去验证你的测试是否真的能区分对错,相当于给评分器本身做了「质检」。
这个「质检」过程的意义怎么强调都不为过。在传统出题流程中,出题人写完测试用例后,往往只能靠「感觉」判断评分器是否可靠——也许自己拿几个正确和错误的示例跑一遍,但无法系统性地验证。而Questioner通过变异测试,把「评分器质量」从一个模糊的主观判断,变成了一个可量化的客观指标:如果所有变异体都能被识别,那么评分器至少能抓住这37种典型错误模式;如果有变异体漏网,系统会明确提示出题人,让出题人有机会补充测试输入或调整变异策略。这种「可验证的评分器」在编程教育领域是一个重要的进步,它让出题人对自己出的题有了真正的信心。
另一个值得注意的设计是「变异体未识别」时的处理流程。当系统发现某个变异体无法被当前测试输入识别时,它不会简单地忽略或自动增加测试次数,而是把这个变异体展示给作者。作者可以选择:(1) 添加一个特殊的测试输入来覆盖这个变异体;(2) 禁用这个变异体,认为它代表的是不合理的错误模式;(3) 调整变异策略,让变异体更贴近学生的真实错误。这种「人机协作」的设计,既发挥了自动化的效率,又保留了出题人的专业判断,是Questioner能够在大规模实践中保持高质量的关键因素之一。
Questioner系统设计与实现
Questioner的实现架构颇有巧思。每道题的「控制类」——即包含题目描述、配置信息和参考答案的类——是所有逻辑的起点。系统会先解析控制类中的`@Correct`注解来获取题目的名称、作者、版本三重标识,然后将剩余部分作为参考答案提取出来。
拿到参考答案后,系统就自动拆解它的行为特征:需要什么样的参数类型、返回什么样的结果、有没有异常抛出、是否有标准输出。这些信息驱动内置的随机生成器构造测试输入。对于特殊参数类型,作者可以通过`@FixedParameters`给出候选值列表。比如例3的`isSecretNumber`问题,如果只靠随机生成整数,几乎永远撞不上88和888这两个关键数字,所以必须显式指定;再比如回文串检测这类需要满足特定性质的输入,随机生成的回文串比例太低,作者也可以写一个生成器函数来自动产出高质量测试数据。
除了行为正确性,Questioner还从参考答案里提取代码质量指标,包括圈复杂度、执行行数、内存分配量、提交长度、死代码和递归实现。这些维度都来自教学一线——比如老师想训练学生写递归却总有人用迭代蒙混过关,Questioner通过字节码插桩识别参考答案是否用了递归,如果答案是肯定的,那么提交也必须采用递归实现,从机制上杜绝了学生「逃课」的可能。再比如圈复杂度这个参数,它反映的是代码路径的多少,如果学生的提交复杂度远超参考答案,那大概率是写得绕了。这套思路的实用性很强:四个效率类指标(执行时间、内存、代码长度、死代码)都可以配置严格的界限,支持性能挑战型的编程题。
更值得一说的是它对「暴力破解」的防御。自动评分器通常会跑非常多次随机测试,有的学生就动起了歪脑筋——写一段代码,把所有测试输入和期望输出硬编码成一个巨大的if-else链。反正评分器能用的输入就那么多,枚举完就完事了。针对这种对抗性提交,Questioner设置了复杂度硬上限:这个上限比参考答案高不少,但比评分器使用的测试次数低得多。一旦你的if-else链长到一定程度,直接判不合格,从机制上杜绝了这种方式。
Questioner对类设计题也一视同仁。例4里的`EvenOddFlipFlop`要求内部状态在奇偶之间来回切换。系统会为类构造多个实例,随机调用方法序列,并且检测异常行为。对于这种有内部状态的类,如果某个错误要隔几次调用才暴露出来,反馈信息会包含完整的调用序列,而不是只给最后一次错误的调用——这能让调试负担降不少。借助Gradle插件,老师在IDE里检查HTML报告、发布题目到Docker化的后端服务,整个流程跑得很顺。
在输入生成方面,Questioner的策略也值得细说。对于基本类型(整数、浮点数、布尔值、字符),系统使用随机生成器,但会考虑边界值——比如整数的最小值、最大值、零、正负一等等。对于字符串,系统会随机生成不同长度的字符串,包括空字符串、单字符、长字符串,以及包含特殊字符的字符串。对于数组和集合,系统会生成不同大小和内容的组合。这些策略虽然简单,但覆盖面广,能够有效触发大多数常见错误。
对于更复杂的输入类型,如自定义类或需要满足特定约束的输入,Questioner提供了灵活的扩展机制。作者可以通过`@FixedParameters`注解指定一组候选值,系统会从这些候选值中随机选择。例如,对于`isSecretNumber`问题,作者可以指定`{88, 888, 8888}`作为特殊候选值,确保这些关键数字被测试到。对于需要生成满足特定性质的输入(如回文串、有序数组、无环图等),作者可以编写一个自定义生成器方法,系统会调用这个方法来产生测试输入。这种设计既保持了自动化的便利性,又给了出题人足够的控制权来处理特殊场景。
在评测执行层面,Questioner采用了沙箱机制来保证安全性。每个提交都在独立的Docker容器中运行,限制了CPU时间、内存使用和文件系统访问。这种隔离不仅防止了恶意代码对评测系统的攻击,也保证了评测结果的公平性——每个提交都在相同的受限环境中运行。评测完成后,系统会收集程序的退出码、标准输出、标准错误以及运行时统计信息(如执行时间、内存峰值),然后与参考答案的行为进行对比。
反馈信息的生成也是Questioner的一个亮点。当提交与参考答案行为不一致时,系统不会简单地说「答案错误」,而是提供详细的差异信息:哪个测试输入触发了错误、期望的输出是什么、实际的输出是什么、如果涉及异常则显示异常堆栈。对于有内部状态的类,反馈会包含完整的调用序列,帮助学生定位错误发生的上下文。这种丰富的反馈对于编程学习至关重要——研究表明,及时、具体的反馈是编程教育中影响学习效果的关键因素之一。
四年大规模课程实践验证
从2020年秋季开始,Questioner就在伊利诺伊大学的CS 124——一门大型CS1课程中投入实战。四年下来,题库积累到771道题,覆盖了Java和Kotlin的核心语言特性。表1可以看到这套系统对教学需求的覆盖能力:从声明方法、比较操作、if-else语句,到递归、二叉树、接口实现,基本上CS1教学涉及的所有核心概念题目,Questioner都能支撑出题。
这些题目的应用场景也很多样:51道作为每日计分作业,103道出现在日常课程作为带解析的练习,15场周测共39个题位随机抽题,176道用于计分测验,平均每道题被抽中4.5次,还有110道出现在模拟测验里。剩下的331道题(43%)全部开放给学生自由练习。一门CS1课能有如此丰富的题目层次,这背后是Questioner在支撑。
最出效果的是2024年春季学期的数据:Questioner收到了来自1054个用户的851,192次作业和练习提交,其中850,881次成功完成评测,成功率99.96%。评测耗时的中位数是43毫秒,99分位也不超过806毫秒——这意味着学生提交后几乎立刻就能看到反馈,这种实时互动的体验对编程学习非常重要。学生另外还提交了748,937次测验编程题答案。这套系统背后只有4台后端服务器支撑,HPC编译和沙箱执行功不可没。
这轮数据最有说服力的地方在于:771道题的题库主要是由一位老师写的。在2020年8月到2023年8月的三年里,高峰期平均每个工作日写一道题。这个产出速度,如果没有Questioner把「写评分器」这个最耗时的环节自动化,是根本不可能实现的。每个学期大部分时间都花在教学管理上,写新题只是每周几小时的例行工作而已。经过四年摸索,系统还沉淀出了对题库的统计能力——通过静态分析自动标记题目覆盖的语言特性,学生可以按知识点搜索和挑题练习,老师也能快速发现教学薄弱点。
从教学效果来看,这套系统也带来了积极的变化。由于出题效率大幅提升,课程可以频繁更新题目,减少学生之间互相抄袭的机会。同时,丰富的题库让学生可以根据自己的薄弱环节进行针对性练习——系统会记录每个学生的答题历史,推荐适合其水平的练习题。这种个性化的学习体验,在传统教学模式下是难以实现的。
值得注意的是,这套系统并不仅仅服务于「做题-判分」这个基本流程。在测验场景中,Questioner支持从题库中随机抽题,每个学生看到的题目可能不同,这大大降低了作弊的可能性。在周测中,15场测验共使用了39个题位,每个题位从题库中随机选择一道题,确保相邻座位的同学拿到不同的题目。这种随机化策略,在没有足够题库支撑时是无法实施的。
局限性与未来展望
任何工具都有边界,Questioner也不是万能的。目前它一次只能对比一个JVM类(Java虚拟机类)的行为,所有被测试的方法都必须由这一个类提供。虽然题目可以涉及多个类,但其他类由参考答案和提交共享,学生不需要自己写——这有时候需要重新组织既有题目才能适配。而且Questioner不会刻意生成「最小化」的评分器,也就是说可能包含冗余的输入和测试用例,不过评测时间已经很快了,实际影响不大。
关于对抗性提交,论文里的讨论倒是很实在。比如遇到一个`int addOne(int)`方法,它对绝大多数输入都正确+1,唯独在参数为960950时报错——这种恶意的隐藏错误确实难以完全防住。但论文也点出了一个看法:能在代码里藏这个后门的学生,往往已经掌握了题目的核心知识点,属于教学意义上的「已达标」。从教育视角看,这不算大问题。
展望一下,作者团队已经在开发Snapact——用同一套「参考答案生成自动评分」思路为Python题目服务的新实现,且能突破单类限制。结合最近生成式AI的火热,未来或许可以在题目描述与参考答案的基础上,自动生成更多元化的变异体和边界测试用例,给自动评分领域带来更多想象空间。这套方法的基本思路,是可以被复用到不同语言、不同教学场景的。
除了Snapact,作者还提到了几个可能的改进方向。一是引入更智能的变异体生成策略,让变异体更贴近学生的真实错误分布——例如,通过分析历史提交数据,找出学生最常犯的错误模式,然后针对性地生成变异体。二是支持更复杂的输入类型,比如树、图、JSON对象等,这需要更强大的输入生成器。三是与大型语言模型结合,自动生成题目描述和参考答案,进一步降低出题门槛。这些方向都值得关注。
从更广阔的视角来看,Questioner所代表的「参考答案驱动」思想,可能对编程教育的多个环节产生深远影响。除了自动评分,它还可以用于自动生成练习题、自动生成解题提示、自动评估学生的学习进度等。当参考答案成为系统的核心输入,许多原本需要人工参与的教学环节都有望实现自动化。当然,这需要更多的研究和实践来探索。
龙迷三问
这篇论文到底在解决什么问题? 编程课最费时的不是备课,而是给大量练习写自动评分器。
这篇工作最值得看的点是什么? 论文通过四年的大规模课程实践验证了Questioner系统的有效性,共编写771个编程问题,服务数千名学生,处理超过85万次提交,中位评分时间仅43毫秒。
这篇工作的边界或风险在哪里? 优点:1) 大幅提高自动评分器编写效率;2) 通过变异测试自动验证评分准确性;3) 提供代码质量评估功能;4) 支持多种题型和编程语言。缺点:1) 目前仅支持单类JVM类比较;2) 无法处理对抗性提交;3) 需要作者提供参考答案;4) 对特殊输入需要手动配置。
如果你还有哪些想要了解的,欢迎在评论区留言或者讨论~
龙哥点评
论文创新性分数: ★★★★☆
把「参考答案」作为自动评分器的生成来源,这个视角的转换是这篇文章最大的创新。变异测试在教育场景的落地也很有想法。虽然各个组件本身不是新技术,但组合方式和教育场景的应用是新的。
实验合理度: ★★★★☆
771道题、85万次提交、四年教学实践——这个实验规模在计算机教育领域很有说服力。虽然缺少传统意义上的A/B对比实验,但大规模真实场景的验证本身就很难得。
学术研究价值: ★★★★☆
给编程教育技术研究提供了一套新范式——从「写测试」到「提供参考答案」。这对教育工具开发、在线判题系统设计都有启发意义。未来结合LLM出题很有想象空间。
稳定性: ★★★★☆
四年教学实践、99.96%的评测成功率、43毫秒的中位响应时间,稳定性已经过大规模验证。不过目前仅支持Java/Kotlin,跨语言稳定性有待观察。
适应性以及泛化能力: ★★★☆☆
方法本身有普适性,但实现层面目前还是JVM生态绑定。Python支持由Snapact实现,还没看到大规模验证。对非编程类题目的适配基本没涉及。
硬件需求及成本: ★★★★★
4台后端服务器就能支撑一门大型课程的全部评测工作,评测时长毫秒级。这个成本对大多数学校来说都很友好,并不需要什么特殊硬件。
复现难度: ★★★☆☆
工具链依赖Gradle插件、Docker后端、MongoDB存储,架构描述得很清楚,但代码没有完全开源,复现需要投入不少工程精力。好在论文里给的方法流程足够详细。
产品化成熟度: ★★★★☆
已经支持课堂作业、练习、测验等多种场景,有完整的后台服务和管理接口。但要推广到其他学校使用,还需要完善文档、部署教程和社区支持,目前更像是「课程自用成熟、产品化待推进」的状态。
可能的问题: 论文对如何保证变异体质量与学生真实错误分布的匹配度讨论不足;对抗性提交的防御策略偏简单;单校单课程的经验数据虽然在规模上可观,但缺少跨机构、跨语言、跨教学模式的验证——这些都会影响结论的普适性。
主要参考文献
[1] Challen, G., & Nordick, B. (2025). Accelerating Accurate Assignment Authoring Using Solution-Generated Autograders. In Proceedings of the 56th ACM Technical Symposium on Computer Science Education V. 1 (SIGCSE TS 2025). https://doi.org/10.1145/3641554.3701862
[2] Jeed: Pedagogical Source Code Execution and Analysis Toolkit (Questioner 底层依赖的 Java/Kotlin 变异与执行工具)
*本文仅代表个人理解及观点,不构成任何论文审核或者项目落地推荐意见,具体以相关组织评审结果为准。欢迎就论文内容交流探讨,理性发言哦~ 想了解更多原文细节的小伙伴,可以点击 "阅读原文", 查看更多原论文细节哦!
今天这篇『答案自动生成评分器』的方法牛不牛?搞编程教育、在线判题的小伙伴一定深有体会——写测试用例写到吐,不如让答案自己干活!想跟龙哥和群里上千位算法/教育/AI同好继续唠?欢迎加入龙哥读论文粉丝群,
扫描下方二维码或者添加龙哥助手微信号加群 :kangjinlonghelper。
一定要备注:研究方向+地点+学校/公司+昵称(如 自动评分+北京+北大+小张) ,根据格式备注,可更快被通过且邀请进群。『龙哥读论文』微信群目前包含:图像处理、大模型及智能体、自动驾驶及机器人、AI医疗及AI金融5个群