Skip to content

RPC 与可传递数据

本页说明 Evaluator 与 Solution 之间的调用协议语义。出题人通常只需要使用 SolutionRunner,不需要手写协议帧;但理解协议有助于设计题目、解释错误和避免传递不支持的数据。

协议角色

text
Evaluator SDK
  |
  | __NOJ_RPC__{...} 写到 evaluator stderr
  v
Judge Worker
  |
  | JSON 请求写入 Solution Host stdin
  v
Solution Host
  |
  | JSON 响应写到 Solution Host stdout
  v
Judge Worker
  |
  | JSON 响应写回 evaluator stdin
  v
Evaluator SDK

Evaluator stderr 中只有带 __NOJ_RPC__ 前缀的行会被当作 RPC 帧。其他 stderr 内容会作为普通评测输出保留。

Solution Host 的 stdout 是协议通道。用户代码的 stdout 会被重定向到 stderr,避免用户 print() 破坏协议。

调用请求

Evaluator SDK 调用:

python
runner.call("solve", 1, 2)

会生成概念上类似的请求:

json
{
  "id": "uuid",
  "method": "call",
  "name": "solve",
  "args": [1, 2],
  "kwargs": {}
}

字段含义:

字段含义
id单次调用 ID,用于匹配响应
method当前支持 callrestart
name要调用的用户函数名
args编码后的定位参数列表
kwargs编码后的关键字参数字典
timeout_ms可选。正整数 = 本次调用的超时(毫秒),仅由 Judge Worker 计时;缺省 / 非法时回退题目级 runtime_config.solution.call_timeout_ms。帧会原样透传到 Solution Host,但 host 不消费该字段

cap_reg 帧(capability 默认超时上报)

Evaluator 在 register_capability(name, handler, timeout_ms=...) 时向 stdout 写一次性 cap_reg 帧,上报 Judge 该 capability 的调用默认超时:

json
{"type": "cap_reg", "name": "request_llm_completion", "timeout_ms": 10000}
  • timeout_ms 缺省表示删除映射(该 capability 回退题目级 call_timeout_ms)。
  • cap_reg 是 Evaluator → Judge 的私有协议帧,Judge 不转发给 Solution Host
  • 重复注册同名 capability:最近一次生效(含超时映射)。

成功响应

用户函数成功返回时,Solution Host 返回:

json
{
  "id": "uuid",
  "ok": true,
  "result": 3
}

Evaluator SDK 会解码 result 并作为 runner.call() 的返回值。

错误响应

调用失败时,Solution Host 或 Judge Worker 返回:

json
{
  "id": "uuid",
  "ok": false,
  "error": {
    "type": "FunctionNotFound",
    "message": "solve"
  }
}

Evaluator SDK 会抛出 SolutionCallError,错误对象可通过 exc.error 读取。

错误对象常见字段:

字段含义
type错误类型
message错误消息
traceback用户异常 traceback,可能被截断
stderrSolution stderr 尾部片段,最多约 2000 字符

常见错误来源:

类型来源含义
FunctionNotFoundSolution Host用户模块中不存在目标函数
NotCallableSolution Host同名对象存在,但不可调用
InvalidFunctionNameSolution Host函数名为空或不是字符串
用户异常类名Solution Host用户函数执行时抛出异常
InvalidJsonSolution HostHost 收到的请求不是合法 JSON
UnknownMethodSolution Host请求方法未知
CallTimeoutJudge Worker单次调用超过调用级 timeout_ms(缺省回退题目级 call_timeout_ms);capability 调用按注册时配置的默认超时。该错误由 Judge 直接注入;若 evaluator 未捕获(evaluate.py 异常退出、无 ---RESULT---),最终状态为 TimeLimitExceeded
HostWriteFailedJudge Worker无法向 Solution Host 写入请求
InvalidHostResponseJudge WorkerHost 响应不是合法 JSON
RestartFailedJudge Worker重启 Solution Host 失败
InvalidRpcFrameJudge Workerevaluator 发出的 RPC 帧不是合法 JSON

restart 请求

runner.restart() 会请求重启 Solution Host:

json
{
  "id": "uuid",
  "method": "restart"
}

重启成功后,用户模块会重新导入,全局状态被清空。普通题目通常不需要重启;只有当你明确希望隔离多轮调用状态时才使用。

可传递的数据类型

Neuro OJ RPC 使用 JSON 加一层 Neuro OJ codec。当前支持:

Python 类型传递语义
None原样传递为 JSON null
bool原样传递
int原样传递
float仅支持有限浮点数
str原样传递
bytes编码为 base64 包装对象
list递归编码元素
tuple编码为列表,返回后不保留 tuple 类型
dict递归编码值,但 key 必须是字符串

bytes 的编码形式:

json
{
  "__noj_type__": "bytes",
  "base64": "SGVsbG8="
}

不支持的数据

以下内容不能直接通过 runner.call() 传递或返回:

  • NaNInfinity-Infinity 等非有限浮点数。
  • key 不是字符串的字典。
  • 函数、类、模块、文件句柄、生成器、迭代器。
  • 自定义对象实例。
  • 异常对象本身。

行为

  • Evaluator 传参runner.call() 在发出 RPC 帧之前递归校验参数(validate_type)。遇到不允许的类型立即抛出 RejectedError,错误消息带路径与类型名(如 arg[0]: 不支持的类型 MyClass(仅 None/bool/int/float/str/bytes/list/dict));RPC 帧不发出,Solution 侧完全不知情。帧序列化超过 1 MiB 软上限同理。
  • Solution 返回值:校验失败时 Judge Worker 返回 code="Rejected" 的错误帧,Evaluator 侧收到 RejectedError(与传参失败是同一个异常类型)。
  • 出题人可用 try/except RejectedError 把这类调用按失败用例处理;不捕获则 evaluate.py 异常退出,该次评测落为 SystemError

如果题目需要复杂结构,建议转换成由 dict[str, ...]list、数字、字符串和字节串组成的数据结构。

传递数据的设计建议

  • 只把用户求解所需的输入传给 Solution,不要传隐藏用例的期望答案。
  • 大型静态数据应放在纯净评测包中由 Evaluator 读取,再传递必要片段给 Solution。
  • 返回值应尽量稳定、可 JSON 化,便于 evaluator 比较和写入 details
  • 对浮点题目,应在 evaluator 中定义误差容忍,而不是要求用户返回字符串。
  • 不要把 RPC 当作文件传输通道;大量数据会增加序列化和日志成本。

输出与截断

Judge Worker 会限制收集到的容器输出大小。当前单个输出缓冲最多约 4 MiB,超过后会追加截断提示。

当调用失败时,Judge Worker 会把 Solution stderr 的尾部片段附加到错误对象中,帮助 evaluator 记录调试信息。出题人应避免把完整 stderr 原样暴露给所有用户,尤其是隐藏用例场景。

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