# CES-Alpha 题目 JSON 文件格式 — 版本 1.0

本文档规定了用于保存 CES-Alpha 自动评分题目（一道或多道）的 JSON 文件格式。无论是手工编写、用脚本生成，还是让 AI 智能体生成这类文件，都请遵循本文档。

- 本文档网页版：<https://document.ces-alpha.jp/manual/auto_grade_question_format.html>
- 本文档 Markdown 版：<https://document.ces-alpha.jp/manual/auto_grade_question_format.zh-Hans.md>
- 示例文件：见第 9 节

## 1. 文件的使用场景

在教师的题目编辑页面中：

- **下载**：页面底部的“题目 JSON 文件”区域可将当前题目下载为本格式的文件（“下载 JSON 文件”）。
- **加载一个文件**：“加载题目” → “从本地 JSON 文件”可将一道题目填入表单。在按下保存按钮之前，不会保存任何内容。
- **加载多道题目**：如果选择了多个文件、一个 `.zip` 文件，或一个包含题目列表的文件，其中所有题目都会作为新题目添加到页面上所选的分组中。

系统有两个站点，一个文件只对应其中一个站点：

| 站点 | `site` | 题目 |
|---|---|---|
| 编程站点（`code.ces-alpha.org` 及其子站点） | `"code"` | 编程题，以及简短文本作答题 |
| 数学站点（`math.ces-alpha.org`） | `"math"` | 通过符号计算评分的数学题 |

## 2. 文件基本规则

- 编码：**UTF-8**。格式：JSON（[RFC 8259](https://www.rfc-editor.org/rfc/rfc8259)）。扩展名：`.json`。
- 一道题目就是一个 JSON **对象**。多道题目可采用以下任一方式：
    - 在一个文件中使用由题目对象组成的 JSON **数组**；
    - 同时选择多个 `.json` 文件；
    - 使用包含 `.json` 文件的 `.zip` 文件（其中可以有文件夹；以 `.` 开头的文件和 `__MACOSX` 文件夹会被跳过）。
- 所有文本值都是 JSON 字符串。换行写作 `\n`，反斜杠写作 `\\`，因此 LaTeX 的 `\frac{1}{2}` 在文件中写作 `"\\frac{1}{2}"`。
- 本文档未列出的键会被忽略。
- 空字符串 `""` 与缺少该键的含义相同。

## 3. 文件头（必需）

格式 1.0 的每个题目对象都以下面两个键开头：

| 键 | 类型 | 值 |
|---|---|---|
| `ces_question_format` | 字符串 | `"1.0"`：格式版本，形式为 `"主版本.次版本"`。 |
| `site` | 字符串 | `"code"` 或 `"math"`：题目所属的站点。 |

读取文件时的规则：

- **主版本相同、次版本任意**（`"1.0"`、`"1.2"`、…）：文件会被读取；较新次版本中新增的键会被忽略。
- **主版本不同**（`"2.0"`、…）：拒绝读取该文件。
- **`site` 与当前页面的站点不一致**：拒绝读取该文件。数学题不能加载到编程站点，反之亦然。
- **没有 `ces_question_format` 键**：该文件被视为 1.0 之前的文件（见第 8 节），按以前的方式读取。

## 4. 两个站点共用的字段

| 键 | 类型 | 必需 | 默认值 | 含义 |
|---|---|---|---|---|
| `question_type` | 字符串 | 是 | — | 题目类型。取值见第 5 节（数学）和第 6 节（编程）。 |
| `question_title` | 字符串 | 是\* | `""` | 在题目列表中显示的标题。 |
| `question_content` | 字符串 | 是\* | `""` | 显示给学生的题目正文（第 7 节）。 |
| `score` | 整数 | 否 | `1` | 题目的分值（表单中的“成绩设定”）。 |
| `evaluation_method` | 整数 | 否 | 数学 `0`，编程 `1` | 答案的评分方式。取值见第 5 节和第 6 节。 |
| `question_hint` | 字符串 | 否 | `""` | 学生可以打开查看的提示（表单中的“提示”）。 |
| `q_solution_explanation` | 字符串 | 否 | `""` | 解答说明，与正确答案一起显示给学生（表单中的“问题解答说明”）。 |
| `note` | 字符串 | 否 | `""` | 给教师看的备注，学生永远看不到（表单中的“注解”）。 |

\* `question_title` 和 `question_content` 至少要设置一个。建议两个都设置。

## 5. 数学站点（`"site": "math"`）

### 5.1 题目类型

| `question_type` | 学生…… | 使用的字段 |
|---|---|---|
| `"text"` | 输入一个或多个表达式（表单中的“文本对话框中回答”） | `question_answer`、`question_variable`、`answer_const` |
| `"select"` | 从列表中选择一项（表单中的“选择题”） | `answer_candidate`、`question_answer` |
| `"matrix"` | 输入向量或矩阵（表单中的“以矩阵或者向量形式回答”） | `question_answer`、`question_variable` |

### 5.2 字段

| 键 | 类型 | 必需 | 含义 |
|---|---|---|---|
| `question_variable` | 字符串 | 否 | 参数（表单中的“参数”）：随机取值，使每个学生的题目各不相同（5.3）。 |
| `question_answer` | 字符串 | 是 | 正确答案。其形式取决于 `question_type`（5.4）。 |
| `answer_candidate` | 字符串数组 | 仅 `select` | 各个选项，按显示顺序排列。 |
| `answer_const` | 布尔值 | 否（仅 `text`） | `true`：与 `question_answer` 只相差一个常数的答案也判为正确（用于不定积分；表单中的“答案判定时忽略不定积分结果中的常数。”）。默认 `false`。 |

### 5.3 参数（`question_variable`）

每行一个参数（各行之间用 `\n` 分隔），格式为 `名称=取值`：

```text
a=1,2,5
b_1=-3,...,-1,1,...,5
alpha=1,...,5
beta=a+b_1
```

- 逗号分隔的数值列表：为每个学生随机抽取其中一个值。`m,...,n` 表示从 `m` 到 `n` 的所有整数。
- 表达式（含有字母，例如 `beta=a+b_1`）：由其他参数计算得到。
- 名称：一个字母（`a`、`b`、`x`、…）或希腊字母名称（`alpha`、`beta`、…；但不能用 `lambda`、`pi` 或 `e`），可以带一位数字的下标（`b_1`、`beta_9`）。
- `E`（或 `e`）、`pi` 和 `I`（虚数单位）是数学常数。
- 在 `question_content` 中，`$...$` 内的参数会被替换为其取值。在那里希腊字母要带反斜杠书写（`$\\alpha$`）；在 `question_variable` 中则不带反斜杠（`alpha`）。

### 5.4 答案（`question_answer`）

**`text`**：每个答案框一行，格式为 `名称=表达式`：

```text
A=2*a*x+b
B=2*a+b
```

- 名称（`A`、`B`）是答案框的标签。在题目正文中用 `\ans{A}` 标出答案框的位置（第 7 节）。
- 表达式的写法与计算器相同，采用 SymPy 语法：乘法用 `*`，乘方用 `^` 或 `**`，以及 `sqrt(x)`、`sin(x)`、`exp(x)`、`log(x)`、`pi`、`E`、`I`、`oo`（无穷大）。可以使用参数。
- 一个答案框有多个正确答案时，用 `;` 分隔（`x=2;-2`）。
- 只对某一行指定评分方式：在该行末尾加上 `# mode:1`、`# mode:2` 或 `# mode:3`（`A=x+1 # mode:3`）。编号与 5.5 中的相同。

**`matrix`**：每行写矩阵的一行，元素之间用逗号分隔。列向量每行写一个元素。

```text
a+2, 2
1, 1
```

**`select`**：正确选项的文本，必须与 `answer_candidate` 中的写法完全一致。

### 5.5 评分方式（`evaluation_method`）

| 值 | 表单中的名称 | 判为正确的条件 |
|---|---|---|
| `0` | [mode:1] 等价性判定(Equivalence check) | 数学含义相同：`(x-1)^2` = `x^2-2x+1`。 |
| `1` | [mode:2] 形式判定（Formal check） | 形式相同（不计顺序）：`(x-3)(x-1)` = `(x-1)(x-3)`，但 `3x` ≠ `x+2x`。 |
| `2` | [mode:3] 严格判定(Strict check) | 字符串完全相同：`2x` ≠ `2*x`。 |

`select` 不使用此字段，它总是比较所选的文本。

## 6. 编程站点（`"site": "code"`）

### 6.1 题目类型

| `question_type` | 学生…… | 评分方式 |
|---|---|---|
| `"code"` | 编写程序（表单中的“编写程序”） | 用相同输入运行 `sample_code` 和学生的代码，并比较结果 |
| `"text"` | 输入简短答案（表单中的“文本对话框中回答”） | 与 `question_answer` 进行比较 |

### 6.2 `code` 题目的字段

| 键 | 类型 | 必需 | 含义 |
|---|---|---|---|
| `code_language` | 字符串 | 是 | `"Python"`（Python 3）、`"R"` 或 `"MATLAB"`（用 Octave 运行）。默认 `"Python"`。 |
| `answer_template` | 字符串 | 否 | 学生编辑器中的初始代码。 |
| `sample_code` | 字符串 | 是 | 参考答案。它在评分服务器上运行，学生看不到。 |
| `object_for_grade` | 字符串 | 是 | 比较的对象（6.3）。 |
| `input_argument` | 字符串 | 否 | 函数调用的输入，每行一个测试用例（6.3）。 |
| `evaluation_method` | 整数 | 否 | `1`：运行代码并比较结果（默认；表单中的“检查程序运行结果”）。 |

### 6.3 比较的对象（`object_for_grade`、`input_argument`）

有两种方式。

**函数。** `object_for_grade` 是一个函数调用，例如 `add(a, b)`。`input_argument` 的每一行是一个测试用例：其中逗号分隔的各个值按顺序代入各参数的位置。对每个测试用例，分别调用 `sample_code` 和学生代码中的该函数，返回值必须一致。

```json
"object_for_grade": "add(a, b)",
"input_argument": "1, 2\n-3, 5\n0.5, 0.25"
```

- 列表和嵌套调用也可以作为值：`[1,2], 5` 和 `c(1,2,3), 1000` 都各是两个值。
- 也可以使用占位符 `var1`、`var2`、…（只有一个值时用 `var`），占位符也可以写在表达式中：`solve(var1, np.array([var2, var3]))`。
- 如果没有 `input_argument`，函数会不带参数调用一次（`f()`）。

**变量。** `object_for_grade` 是逗号分隔的变量名列表，例如 `m, mx`。两个程序都会运行，运行结束时这些变量的值必须一致。此时不使用 `input_argument`。

### 6.4 编程站点上的 `text` 题目

`question_answer` 每个答案框一行，格式为 `名称=值`，与 5.4 相同。多个正确答案用 `;` 分隔（`PrimeNumber=3;13;23`）。如果答案本身含有分号，写作 `\;`（在 JSON 文件中写作 `"\\;"`）。

## 7. 题目正文、提示和解答说明

- `question_content`、`question_hint` 和 `q_solution_explanation` 采用 **Markdown** 格式。空一行表示开始新段落。代码可以用反引号书写。
- 在数学站点上可以使用 LaTeX 公式：行内公式用 `$...$`，独立公式块用 `$$...$$`。
- `\ans{名称}` 会在正文中答案 `名称` 的位置绘制一个答案框。

## 8. 1.0 之前的文件

没有 `ces_question_format` 的文件按以前的方式读取。仅为了兼容，在这类文件中还接受以下其他名称。新文件中请不要使用它们。

| 1.0 的键 | 同样接受的其他名称 |
|---|---|
| `question_type` | `exercise_type`、`q_type` |
| `question_title` | `title` |
| `question_content` | `q_content` |
| `question_variable` | `q_variable` |
| `question_answer` | `answer` |
| `answer_candidate` | `answer_choices` |
| `question_hint` | `hint` |
| `q_solution_explanation` | `solution_explanation` |
| `answer_template` | `code_template` |
| `object_for_grade` | `code_output` |

在 1.0 之前的文件中，如果缺少题目类型，或题目类型不是该站点的类型，系统会推断题目类型：在编程站点上，含有 `answer_template` 或 `object_for_grade` 的题目视为 `code`，否则视为 `text`。

## 9. 示例

| 文件 | 站点 | 类型 |
|---|---|---|
| [code_python_function.json](https://document.ces-alpha.jp/manual/question_format_examples/code_python_function.json) | code | `code`，带测试用例的函数 |
| [code_python_variables.json](https://document.ces-alpha.jp/manual/question_format_examples/code_python_variables.json) | code | `code`，变量 |
| [code_text.json](https://document.ces-alpha.jp/manual/question_format_examples/code_text.json) | code | `text` |
| [math_text.json](https://document.ces-alpha.jp/manual/question_format_examples/math_text.json) | math | 带参数的 `text`，两个答案 |
| [math_text_integral.json](https://document.ces-alpha.jp/manual/question_format_examples/math_text_integral.json) | math | 带 `answer_const` 的 `text` |
| [math_select.json](https://document.ces-alpha.jp/manual/question_format_examples/math_select.json) | math | `select` |
| [math_matrix.json](https://document.ces-alpha.jp/manual/question_format_examples/math_matrix.json) | math | `matrix` |
| [math_several_questions.json](https://document.ces-alpha.jp/manual/question_format_examples/math_several_questions.json) | math | 包含两道题目的数组 |

一道编程题：

```json
{
  "ces_question_format": "1.0",
  "site": "code",
  "question_type": "code",
  "question_title": "Sum of two numbers",
  "question_content": "Write a function `add(a, b)` that returns the sum of `a` and `b`.",
  "score": 1,
  "evaluation_method": 1,
  "code_language": "Python",
  "answer_template": "def add(a, b):\n    # Write your code here.\n    return None\n",
  "sample_code": "def add(a, b):\n    return a + b\n",
  "object_for_grade": "add(a, b)",
  "input_argument": "1, 2\n-3, 5\n0.5, 0.25",
  "question_hint": "Use the + operator.",
  "q_solution_explanation": "Return `a + b`.",
  "note": "Week 1"
}
```

一道数学题：

```json
{
  "ces_question_format": "1.0",
  "site": "math",
  "question_type": "text",
  "question_title": "Derivative of a polynomial",
  "question_content": "Let $f(x) = a x^2 + b x$. Find $f'(x)$ and $f'(1)$.\n\n$f'(x) = $ \\ans{A}, $f'(1) = $ \\ans{B}",
  "question_variable": "a=1,...,5\nb=-3,-2,-1,1,2,3",
  "question_answer": "A=2*a*x+b\nB=2*a+b",
  "evaluation_method": 0,
  "score": 2,
  "question_hint": "$(x^n)' = n x^{n-1}$",
  "q_solution_explanation": "$f'(x) = 2ax + b$, so $f'(1) = 2a + b$."
}
```

## 10. 给 AI 智能体的说明

请把本节或整篇文档交给负责编写题目文件的 AI 智能体。

```text
你按照“CES-Alpha 题目 JSON 文件格式 1.0”为 CES-Alpha 编写题目文件。
- 只输出合法的 JSON，使用 UTF-8，不加注释，前后不附带任何文字。
- 每道题目是一个对象，以 "ces_question_format": "1.0" 和 "site": "code" 或 "math" 开头。
- 多道题目时，输出由这类对象组成的 JSON 数组。
- 只使用本格式规定的键。绝不使用第 8 节中的旧名称。
- 务必设置 question_type、question_title、question_content 和 question_answer
  （对于编程站点的 "code" 题目，用 code_language、sample_code 和 object_for_grade 代替 question_answer）。
- 数学站点：question_type 为 "text"、"select" 或 "matrix"；表达式使用 SymPy 语法（2*x、x^2、sqrt(x)、pi、E）。
  "text" 题目的答案每个答案框一行 "name=expression"；在正文中用 \ans{name} 标出每个答案框。
  "select" 题目的答案必须与 answer_candidate 中的某一项完全一致。
- 编程站点：question_type 为 "code" 或 "text"；code_language 为 "Python"、"R" 或 "MATLAB"。
  sample_code 必须能无错误地运行，并定义 object_for_grade 中所指的对象。
  对于函数，在 input_argument 中给出多个测试用例，每行一个。
- JSON 字符串内的转义：换行写作 \n，反斜杠写作 \\（LaTeX 的 \frac 写作 \\frac），双引号写作 \"。
- 写入答案之前，先确认答案是正确的。
```

## 11. 变更记录

| 版本 | 日期 | 变更内容 |
|---|---|---|
| 1.0 | 2026-10 | 第一版：新增文件头 `ces_question_format` 和 `site`；新增 `answer_const`；第 8 节中的旧名称仅用于兼容。 |
