題目 Question
題型、內容文件、評分設定,以及版本與封存的運作方式
題目(Question)物件用以儲存可跨考試重複使用的題庫資源。題目為版本化的資源,亦即每次更新產生新版本,版本一旦建立即不可變更。且考試等引用者,在參照時需指定明確版本。
一個題目將包含題幹,以及含評分標準在內的評分設定。
一個最小的完整題目如下。content.stem 是題幹,scoring 是評分設定。
{
"custom_id": "q-bio-photosynthesis-sa",
"type": "short_answer",
"content": {
"stem": {
"type": "doc",
"content": [
{
"type": "paragraph",
"content": [{ "type": "text", "text": "光反應提供給暗反應的兩項產物為何?" }]
}
]
}
},
"scoring": {
"max_score": 4,
"reference_answer": "ATP 與 NADPH",
"rubric_ref": "rub_fhz:1"
}
}您可以在 custom_id 欄位中使用您自有的題目識別碼,方便日後對照;id 與 version 由伺服器建立,寫入時請勿提供。
題型 Type
type | 說明 | 批閱方式 |
|---|---|---|
fill_in_blank | 填充,一格或多格 | 逐格判定(method: blank_grading) |
short_answer | 簡答,可含數學 | AI 依評分標準批閱(method: rubric_grading) |
composition | 作文、申論等長文作答 | AI 依評分標準批閱(method: rubric_grading) |
nested | 題組,共用題幹底下掛子題 | 本身不具有可批閱效果,底下必須存在可批閱的子題 |
type 於建立時固定,之後不能更改,產生新版本時亦同。
多選題(multi_choice_question)與其 OMR 判讀評分目前尚未支援。
未來支援進度請見未來規劃。
評分標準 Rubric
每個會被 AI 批閱的題目,都必須給定其評分標準,使用以下兩種方式擇一給定:
scoring.rubric_ref:引用可跨題目共用的評分標準物件scoring.rubric:內嵌專屬的評分標準
兩者皆給或皆不給,都將於送出建立請求時驗證失敗,並回應 400 ERR_VALIDATION_FAILED 錯誤。
根據題目不同的題型,rubric 或 rubric_ref 於題目中建立的位置亦有差別:
| 題型 | 所屬的物件 |
|---|---|
short_answer、composition | 逕行於題目上建立(scoring.rubric / scoring.rubric_ref) |
fill_in_blank | 於個別空格上建立(scoring.blanks[].rubric / .rubric_ref) |
nested | 於需評分的末端子題建立 |
缺少評分標準的題目,將在建立時被拒絕。
若題目使用共用的評分標準物件 rubric_ref,則題目可以在引用時,選擇性的另行指定該題目的客製標準答案 reference_answer。此參數提供正確答案的參考,而引用的評分標準則界定答案對照該正確答案之下的評分尺度與原則。
更加完整的評分機制及標準設定,請見評分標準頁面。
內容與題幹
題幹(content.stem)的格式採用 ProseMirror JSON 文件。
寫入題目時,系統將依照 ProseMirror JSON 規範,對題幹進行解析與驗證,包含節點集合、必要屬性,以及每個數學節點內的 LaTeX 都會檢查。
完整說明請見作答與題幹的內容格式頁面。
圖片
圖片採用 image 節點建立,並給定網址 attrs.url,不接受內嵌圖檔資料。
{
"type": "doc",
"content": [
{
"type": "paragraph",
"content": [{ "type": "text", "text": "請依下圖說明光反應的電子傳遞路徑。" }]
},
{
"type": "image",
"attrs": {
"url": "https://cdn.example.com/bio/chloroplast-electron-transport.png",
"alt": "葉綠體類囊體膜上的電子傳遞鏈示意圖"
}
}
]
}url 必填、alt 選填。image 是區塊層級的節點,與 paragraph 並列於 doc 之下,不能放在段落的 content 之中。
圖片寫入時的副本建立
建立或更新題目時,每一個 image 節點的網址都會被實際抓取,並於本服務的伺服器建立其圖片副本,且所有原題幹圖片的 attrs.url 均會被自動取代指向本地副本。
因此,所有圖片的來源網址必須在寫入題目當下,可不需額外不需憑證的進行公開存取。若題目寫入當下無法存取任一圖片,則該建立題目的請求將會被拒絕,並回應 400 ERR_VALIDATION_FAILED 錯誤。錯誤訊息將指出無法成功建立副本的 attrs.url。
學生作答中同樣支援圖片,也會被抓取並複製,但改在批閱流程中非同步進行,若副本建立失敗,其影響也會不同。 詳見作答與題幹的內容格式。
數學式
所有內容的數學式均以 LaTeX 標記,並以 KaTeX 解析。請參閱 KaTeX 官方文件,以確認受支援的 TeX function 集合。
數學式的標記方式
數學式的表示及標記方式,依所在位置而略有不同的 wrapper。
- ProseMirror JSON(如題幹
content) 之內:數學式採用 ProseMirror 節點(math_inline或math_block),並將 LaTeX 字串置於attrs.latex。文件內不會對其他文字節點解析$…$的表示法。 - 其他純文字欄位:數學式採用
$…$包裹的行內 LaTeX。包含scoring.reference_answer以及評分標準的description、examples等純文字字串。
例如以下題目物件:
{
"custom_id": "q-math-quadratic-min",
"type": "short_answer",
"content": {
"stem": {
"type": "doc",
"content": [
{
"type": "paragraph",
"content": [
{ "type": "text", "text": "求函數 " },
{
"type": "math_inline",
"attrs": { "latex": "f(x) = 2x^2 - 8x + 3" }
},
{ "type": "text", "text": " 的最小值及其對應的 " },
{ "type": "math_inline", "attrs": { "latex": "x" } },
{ "type": "text", "text": " 值。" }
]
}
]
}
},
"scoring": {
"max_score": 6,
"reference_answer": "最小值為 $-5$,於 $x = 2$ 時取得。",
"rubric": {
"dimensions": [
{
"name": "整體",
"levels": [
{ "score": 6, "description": "最小值與 $x$ 值皆正確。" },
{ "score": 3, "description": "僅一項正確。" },
{ "score": 0, "description": "皆錯誤或未作答。" }
]
}
]
}
}
}填充題
一道填充題支援一個或多個空格,且每格獨立批閱與計分。
- 在題幹中,以
blank節點定義空格的位置,並給定attrs.blank_id以建立參照。請勿直接以底線字元「____」排版空格。 - 配分與判定方式
scoring.blanks[],一個空格一筆。
以下為單一空格的最小範例。題幹中的 blank 節點與 scoring.blanks[] 為一對一關係,兩者以同一個 blank_id 相互對應:
{
"custom_id": "q-bio-stroma-fill",
"type": "fill_in_blank",
"content": {
"stem": {
"type": "doc",
"content": [
{
"type": "paragraph",
"content": [
{ "type": "text", "text": "暗反應發生在葉綠體的 " },
{ "type": "blank", "attrs": { "blank_id": "mockBlank1leeu4igarik" } },
{ "type": "text", "text": " 中。" }
]
}
]
}
},
"scoring": {
"max_score": 2,
"blanks": [
{
"blank_id": "mockBlank1leeu4igarik",
"max_score": 2,
"method": "exact_match",
"accepted": ["基質", "stroma"]
}
]
}
}多格與混用兩種批閱方式的完整範例,見填充題的批閱方式。
blank_id 的產生規則
請在建立題目時自行依照下列規則產生隨機字串:
| 項目 | 規則 |
|---|---|
| 字元集 | 0-9、a-z、A-Z,共 62 個字元 |
| 長度 | 21 個字元 |
| 大小寫 | 有區別,aB… 與 Ab… 是不同的識別碼 |
不符合此格式的 blank_id,將會在寫入題目時回應 400 ERR_VALIDATION_FAILED 錯誤。
我們建議直接使用 NanoID 進行建立:
import { customAlphabet } from 'nanoid';
const newBlankId = customAlphabet(
'0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ',
21,
);
newBlankId(); // "mockBlank1leeu4igarik"請勿使用具有語意或順序性的字串作為 blank_id。
blank_id 存在於題幹之中,因此在讀取題幹時,此些 blank_id 亦可被讀取。
故請勿將標準答案、主題標籤、難度標記或任何可讀懂的資訊作為衍生或組合識別碼的來源。
也請避免使用 b1、b2 這類具有順序性的序號識別碼。日後在句子中間插入空格時,序號的語意可能因整體位移而喪失判讀的易讀性。
識別碼應隨機產生,內部對各空格的稱呼另以對照表維護。
填充題的批閱方式
填充題空格可以使用的批閱方式,有以下兩種:
method | 判定方式 | 所需設定 |
|---|---|---|
exact_match | 經正規化後,與參考答案 accepted[] 逐一直接比對字串 | 標準答案清單 accepted[],不需評分標準 |
rubric_grading | 由 AI 依該格的標準批閱 | rubric 或 rubric_ref,可另加 reference_answer |
同一題中若有多個空格,可以混用兩種方式。但批閱是逐格進行的,因此例如「請列出任三項」這類不計順序的題目,請建立為一個空格,並使用 rubric_grading 設定標準並進行評分。
以下列題目為例,前兩格是專有名詞,採完全比對而不經 AI 批閱;第三格容許不同說法,因此交由 AI 判定。
{
"custom_id": "q-bio-photosynthesis-fill",
"type": "fill_in_blank",
"content": {
"stem": {
"type": "doc",
"content": [
{
"type": "paragraph",
"content": [
{ "type": "text", "text": "光反應發生在葉綠體的 " },
{ "type": "blank", "attrs": { "blank_id": "mockBlank1leeu4igarik" } },
{ "type": "text", "text": ",暗反應則發生在 " },
{ "type": "blank", "attrs": { "blank_id": "mockBlank2r7fnq2xoahd" } },
{ "type": "text", "text": "。光反應提供給暗反應的兩項產物是 " },
{ "type": "blank", "attrs": { "blank_id": "mockBlank3ve1kzsp6mtb" } },
{ "type": "text", "text": "。" }
]
}
]
}
},
"scoring": {
"max_score": 6,
"blanks": [
{
"blank_id": "mockBlank1leeu4igarik",
"max_score": 2,
"method": "exact_match",
"accepted": ["類囊體膜", "囊狀膜", "thylakoid membrane"]
},
{
"blank_id": "mockBlank2r7fnq2xoahd",
"max_score": 2,
"method": "exact_match",
"accepted": ["基質", "stroma"]
},
{
"blank_id": "mockBlank3ve1kzsp6mtb",
"max_score": 2,
"method": "rubric_grading",
"reference_answer": "ATP 與 NADPH",
"rubric": {
"dimensions": [
{
"name": "整體",
"levels": [
{ "score": 2, "description": "兩項產物皆答出。" },
{ "score": 1, "description": "僅答出其中一項。" },
{ "score": 0, "description": "皆未答出或錯誤。" }
]
}
]
}
}
]
}
}建立或編輯填充題時時請檢查:
- 題幹至少要有一個空格節點
- 每個空格節點與
scoring.blanks[]具有一對一關係 blank_id不重複- 各格
max_score加總等於題目的max_score - 一題最多 20 格
- 題目層不得再放
rubric、rubric_ref或reference_answer
若有違反以上限制,則建立或編輯題目的請求將會被拒絕。
空格字串比對的正規化
批閱方式 exact_match 會先將學生作答與可接受的標準答案集 accepted[] 都套用正規化,再進行字串比對。正規化的項目包含:
| 正規化項目 | 作用 | 正規化前 | 正規化後 |
|---|---|---|---|
| 全形轉半形(NFKC) | Unicode 相容分解後再合成,全形英數與符號轉為半形 | ATP | ATP |
| 去除頭尾空白 | 移除字串前後的空白字元(含換行與 tab) | ␣␣ATP␣ | ATP |
| 移除中文字之間的空白 | 中文字之間的空白一律移除 | 葉␣綠␣體 | 葉綠體 |
| 去除頭尾標點 | 移除字串前後的標點符號,中英標點皆適用 | 「基質」。 | 基質 |
| 大小寫不分 | 一律折疊為同一種大小寫再比對 | Stroma | stroma |
| 繁簡不分 | 繁體與簡體折疊為同一種字形再比對 | 葉绿体 | 葉綠體 |
正規化只發生在比對的當下,且參考答案與學生作答都會套用;正規化後的字串不會被儲存,也不會出現在任何回應中。
上述規則中,因應特殊題目需求,可選擇性的關閉正規化機制。設定方式、作用及建議適用的情況如下:
| 欄位 | 關閉的項目 | 適用情況 |
|---|---|---|
case_sensitive: true | 大小寫不分 | 遺傳學的 Aa 與 aa、pH 這類大小寫本身即為答案的題目 |
variant_sensitive: true | 繁簡不分 | 字形本身即為答案的題目,否則「幹淨」會被判為「乾淨」的正解 |
數學題目的完全比對
若為數學科填充題,其字串比對將以 LaTeX 原始字串進行,因此 \frac{1}{2}、0.5、\dfrac12
會被視為三個不同的答案。現階段若需容許數學答案等價的不同表達式,請改用 rubric_grading。
未來將支援專用的 math_match ,採用電腦代數系統計算數值等價,詳見未來規劃。
作答格式見作答與題幹的內容格式,逐格結果見取得批閱結果。
題組
一個題組的母題目的 type 需指定為 nested,並在其下附帶其他子題。該母題目即可作為所有子題的共用題幹,例如一篇閱讀短文、一組實驗敘述,或一個共用的多小題情境。
- 共用題幹置於題組的
content.stem,子題置於content.children[],依印在試卷上的順序排列。 - 子題也可以是
nested,最多支援三層的嵌套關係。 nested類型的題目沒有任何可批閱的作答欄位。只有非nested類型的末端子題會有批閱的作用。- 任何一層
nested題組都不帶評分標準、也不產生作答,其scoring只有max_score,並逐層向上加總。 - 末端子題可以是多格填充題;空格是格子,不是另一層題目。
以下是一個兩層題組:共用的實驗敘述題幹,底下掛一個填充子題與一個簡答子題。母題的 scoring 只有 max_score,其值為兩個子題的加總。
{
"custom_id": "q-bio-experiment-set",
"type": "nested",
"content": {
"stem": {
"type": "doc",
"content": [
{
"type": "paragraph",
"content": [
{ "type": "text", "text": "某生進行光合作用速率實驗,以不同光照強度測量單位時間的氧氣產生量。" }
]
}
]
},
"children": [
{
"sub_id": "mockSubQ1gk3vphrn2ecx",
"type": "fill_in_blank",
"content": {
"stem": {
"type": "doc",
"content": [
{
"type": "paragraph",
"content": [
{ "type": "text", "text": "本實驗的操作變因為 " },
{ "type": "blank", "attrs": { "blank_id": "mockBlank1leeu4igarik" } },
{ "type": "text", "text": "。" }
]
}
]
}
},
"scoring": {
"max_score": 4,
"blanks": [
{
"blank_id": "mockBlank1leeu4igarik",
"max_score": 4,
"method": "exact_match",
"accepted": ["光照強度", "光強度"]
}
]
}
},
{
"sub_id": "mockSubQ2wd8j5tramx6f",
"type": "short_answer",
"content": {
"stem": {
"type": "doc",
"content": [
{
"type": "paragraph",
"content": [{ "type": "text", "text": "請說明光照強度提高後,氧氣產生量趨於平緩的原因。" }]
}
]
}
},
"scoring": {
"max_score": 6,
"reference_answer": "光照不再是限制因子,速率改由二氧化碳濃度或酵素活性決定。",
"rubric_ref": "rub_fhz:1"
}
}
]
},
"scoring": { "max_score": 10 }
}送出學生對子題目的作答時,只需提供 sub_id,其完整識別碼(q_set01_sub_…)由伺服器組出,見下。
題組的版本化與參照引用
整個題組採用單一版本單位,亦即改動任何一層子題內容,都會產生整個題組的新版本。子題不存在自己的獨立版本。
考試引用的題目,應為最上層的母題(第一層的 nested 類型題目)而非其子題。但建立提交時,作答對象則是末端子題,詳見考試。
子題 ID 的產生規則
子題的完整識別碼,為每一層各附加一段 _sub_{sub_id}:
q_set01 題組本身,考試釘選的即為它
q_set01_sub_mockSubQ1gk3vphrn2ecx 中間分組
q_set01_sub_mockSubQ1gk3vphrn2ecx_sub_mockSubQ2wd8j5tramx6f 末端子題
q_set01_sub_mockSubQ1gk3vphrn2ecx_sub_mockSubQ2wd8j5tramx6f:1 它的 ref,版本號為整個題組的版本各子題本身的 sub_id ,其產生規則與填充題的 blank_id 完全相同。
版本
使用 POST /questions 建立題目時,初次建立的版本序號將為版本 1。使用 PATCH /questions/{id} 更新題目時,將建立新版本並回傳。題目的舊版本完全不受影響,且引用該些舊版本題目的考試,將繼續以舊版本內容批閱。
更新 scoring 欄位必須傳送完整的物件
更新 scoring 時,請傳送完整的 scoring 物件。
若只傳送例如 {"scoring": {"max_score": 12}} 的部分欄位,將會產生一個沒有評分標準的版本,因而驗證失敗。
版本註記 :latest 只在建立考卷時可以引用,且會在使用當下解析為明確版本。其餘的版本參照均不得使用 :latest。
封存
使用 POST /questions/{id}/archive 使題目封存,自可用題庫中撤下。
封存後的題目,將:
- 該題目的所有版本均會被標記為封存
- 不再出現於預設的可查詢題目列表
GET /questions中 - 不能再被引用進新的考試(但已經引用題目的考試,及其已經產生的批閱結果則仍可照常解析,結果與先前完全相同。在題目封存前已經引用的考試,也能繼續接收新的作答,除非該考試本身也被設定為封存。)
- 進入唯讀狀態,不能對其編輯以新版本(
PATCH將回應409 ERR_CONFLICT錯誤)
封存可以被取消,使用 POST /questions/{id}/unarchive 以取消封存。
相關
- API 參考 — 題目
- 評分標準 — 標準的設計方式
- 作答與題幹的內容格式 — 內容文件的完整規則
- 考試 — 將題目組成考試