Skip to content

双容器评测模型(Judge Model) ​

在 Neuro OJ 中,代码评测基于双容器协同沙箱展开。每次代码提交均在后台并行拉起两个高度受控的 Docker 容器:Evaluator 容器(运行出题人的评分程序)与 Solution 容器(运行选手的解答代码),二者通过评测宿主进程(noj-judge)进行双向 RPC 消息路由。


与传统 OJ 的本质差异 ​

传统在线评测(如 ACM/ICPC、OI 题目)通常将测试用例作为纯文本灌入用户程序的标准输入(stdin),再捕获标准输出(stdout)进行行级比对(Diff)。而在 AI 算法、深度学习与智能体评测场景下,这种形式存在严重瓶颈:

评测维度传统 OJ 文本流评测Neuro OJ 双容器 RPC 评测
交互契约标准输入输出(stdin $\to$ stdout 文本比对)原生 Python 函数调用(强类型入参与返回值)
测试数据管理一次性文本文件重定向,容易被选手一次性预读取由出题人 evaluate.py 动态加载,隐藏用例物理隔离于选手容器之外
调试输出print() 与答案混杂,易导致格式错误(PE)选手 print() 写入协议通道后由底层自动剥离,不污染评测输出
交互式评测需借助特殊交互器(Interactive Judge)管道天然原生交互:出题人可连续多次调用选手函数,支持多轮对话与智能体对抗

双容器角色分工与输出通道 ​

1. Evaluator 容器(裁判方) ​

Evaluator 容器由出题人提供的 evaluate.py 主导。它独占访问纯净题目包内的所有私密测试数据,拥有完全的评分控制权:

  • 核心判定权力:决定调用选手的哪个函数、传递何种边界参数、如何比对返回值的数学/逻辑正确性、计算最终得分以及构造反馈给做题人的详细 details.cases;
  • 输出通道划分:
    • stdout:同时承载 NDJSON 协议帧、评测输出文本以及作为评测终结标志的 ---RESULT--- 行;
    • stderr:仅作为评测执行日志使用,绝对不承载任何 RPC 帧。

2. Solution 容器(做题方) ​

Solution 容器负责隔离执行选手提交的解答:

  • 入口注入规范:评测引擎将选手代码以硬编码文件名 main.py 注入沙箱工作目录 /workspace;
  • Solution Host 协议守护:容器拉起 noj_solution_sdk.host 守护进程,以 --entry /workspace/main.py 动态加载用户模块(模块名固定为 user_solution),自动提取顶层导出的公共函数并进入事件监听循环;
  • 常驻内存特性:在单次评测生命周期内,Solution Host 是单例常驻的。同一选手的模块实例在多次 runner.call() 之间共享全局变量与内存状态(系统不提供重启重置功能)。

调用异常与提交终态映射 ​

在 RPC 交互过程中,选手的代码缺陷可能引发不同层次的错误。Evaluator SDK 会将底层协议错误映射为对应的 Python 异常:

异常情况底层协议 codeEvaluator 捕获异常典型排错原因
函数未定义NotFoundNotFoundError选手没有定义题面要求的顶层函数(如拼写错误)
执行崩溃抛错ExceptionSystemError选手代码内部抛出未捕获异常(包含清洗后的 Traceback)
非法返回类型RejectedRejectedError选手返回了不可序列化的对象(如函数引用、生成器)或帧体积超过 1 MiB

关键准则:调用失败 ≠ 提交失败

上述异常是选手代码在执行单次测试点时产生的错误,绝不等于本次提交失败!

  • 平台采用二元终态设计:提交终态只有 finished(正常评测完成)与 error(评测系统异常);
  • 优秀的出题人应当在 evaluate.py 中使用 try...except 稳健捕获这些异常,将其记录为当前测试点的失败(如 WrongAnswer / RuntimeError),并最终给出 finished + 0 分;
  • 只有当 evaluate.py 自身发生未捕获的严重崩溃、沙箱整体超时或容器无法启动时,终态才会归为 error。

两层超时机制与终态仲裁 ​

系统在架构上设计了严格的两层超时时间锁,防止选手恶意利用死循环或出题人评分逻辑冗长导致评测节点挂起:

超时层级监控主体默认控制权终态表现与做题人可操作性
评测全局总超时noj-judge 评测宿主题目 runtime_config.time_limit_mserror。系统直接熔断容器,选手无法仅凭优化单个函数规避
单函数调用超时SolutionRunner 内部计时题目 call_timeout_ms
或单次参数 timeout_ms
若出题人正常捕获,体现为 finished + 0 分;
若出题人未捕获导致评测脚本终止,落为 error
容器冷启动超时noj-judge 容器生命周期系统级宿主配置error。评测环境未按期就绪

单次函数调用全链路时序 ​

以下展示一次标准的 runner.call("solve", a, b) 端到端执行链路:

Neuro OJ 是一个独立社区项目,与 CCF、LMCC、IOAI 及 NOAI 无官方关系。