作答與題幹的內容格式
ProseMirror 內容文件、數學節點、長度限制,以及空白作答的處理
ProseMirror JSON 文件
本服務的部分內容欄位中,使用 ProseMirror JSON 格式(Tiptap 編輯器產生的序列化格式)以提供內容樣式與多模態內容的支援,例如一般文字、數學式、圖像、填空格,以及其他未來可能擴充支援的節點型別。
適用的內容欄位包含學生的作答(answer.doc)與題目的題幹(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": " 的最小值。" }
]
}
]
}ProseMirror JSON 的完整 schema 定義,請參考 Tiptap 官方文件。
允許的節點
文件除了驗證為合法 JSON 外,也將依照 ProseMirror 的節點(Node)定義進行檢查。
目前支援的節點清單有:
| 節點 | 用途 | 屬性 |
|---|---|---|
doc | 文件根節點 | — |
paragraph | 一段行內內容 | — |
text | 文字 | — |
math_inline | 行內公式 | latex(必填) |
math_block | 獨立成行的公式 | latex(必填) |
image | 以網址引用的圖片 | url(必填)、alt |
blank | 填充題題幹中的空格 | blank_id(必填) |
blank空格節點僅能於填空題fill_in_blank的題幹中使用。該節點用於宣告「答案應寫在此處」,而非學生答案。學生對於空格的作答格式,請參見「作答內容格式」段落。image圖片節點只接受網址引用,無法內嵌圖檔。math_*數學節點的 LaTeX 以 KaTeX 解析並驗證,請參考其官方文件所述支援的 LaTex 函式或巨集。
文件所屬的資源建立或更新時,將驗證涵蓋節點集合、各節點必要屬性,以及每個數學節點內的 LaTeX。以下情況則將回應 400 ERR_VALIDATION_FAILED 錯誤,並在錯誤訊息中指出錯誤的節點路徑:
- 存在不支援的節點類型
- 節點缺少必要屬性
math_數學節點 LaTex 不合法image節點的 URL 指向資源無法讀取:請參閱「圖片節點」段落。
圖片節點
含有圖片節點的文件在建立或更新時,會在本服務中對引用的圖片建立一份副本,並將 attrs.url 改寫指向該副本,以避免持續的依賴外部參照。圖片來源網址在文件建立當下,必須為可公開存取的狀態。
| 圖片節點使用位置 | 副本建立時間 | 副本無法建立時 |
|---|---|---|
| 題幹 | 寫入當下,同步 | 將回應 400 ERR_VALIDATION_FAILED 錯誤,錯誤訊息會指出是哪個節點遭遇錯誤;題目不會被建立 |
| 學生作答 | 批閱流程中,非同步 | 該筆作答以終態 failed 結束,不會產生批閱結果 |
學生作答無法建立圖片副本時,該筆作答將視為系統失敗。因此無法建立人工覆核。 在圖片恢復可讀之後,請於 Studio 重新排程該筆作答。
未來此一錯誤將有專屬的錯誤代碼。但在更新之前,response.grading_failed 會以 ERR_INTERNAL 回報。
LaTex 於文件內的解析
在 ProseMirror JSON 文件中,純文字節點(例如 text)不會解析以 $…$ 慣例包裹的 LaTex 數學式。所有需要被解析的 LaTex 均應使用任一個數學節點表示。
然而,非 ProseMirror JSON 的純字串欄位(如 scoring.reference_answer 以及評分標準的 description、examples)則應仍使用行內 $…$ 包裹 LaTex 公式。
長度限制
送出的文件長度與大小需滿足以下限制:
- 內容長度需小於 5,000。字元計算方式為所有
text節點的文字加上所有數學節點的原始碼。 - 文件大小為序列化後需小於 256 KB。
違反任一者時,將回應 400 ERR_CONTENT_TOO_LONG 錯誤。
空白作答
當學生完全沒有作答內容時,請使用以下文件內容,以送出一份空白的 ProseMirror 文件:
{
"type": "text",
"doc": { "type": "doc", "content": [{ "type": "paragraph" }] }
}請注意務必存在一個 content 節點,裡面存在一個沒有內容的 paragraph 物件。只有定義 content: [] 將導致驗證失敗。
若回答內容只有包含空白字元及換行符號,批閱系統將同樣視為空白。針對空白的回答,批閱結果將特別標記其 outcome: blank。
題目作答文件
學生對題目的作答文件,在 ProseMirror JSON 文件之外,另外需要依照以下任一種格式的包裹。請依照該題目的題型 type 選擇:
範例函式:從純文字內容建立 JSON 文件
我們建議,通常可以讓使用者終端直接使用 ProseMirror 或 Tiptap 編輯器進行輸入。
如果使用者終端使用 Markdown 格式輸入,則可使用 Tiptap 的 Markdown 擴充元件,將 Markdown 文件轉換為 ProseMirror JSON 文件。
若內容使用原生 HTML Input 或 Textarea,則其輸入值可能為一段純文字。下列範例提供一組示範 TypeScript 與 Python 函式,可將純文字轉換為 ProseMirror JSON 文件。
就算該函式呼叫時傳入空字串(如 textDoc('')),函式也可以產生一份合法的空白文件。
type Doc = { type: 'doc'; content: unknown[] };
/** 將純文字(可含換行)轉為內容文件。 */
export function textDoc(text: string): Doc {
return {
type: 'doc',
content: text.split('\n').map((line) => ({
type: 'paragraph',
content: line ? [{ type: 'text', text: line }] : [],
})),
};
}
/** 一段文字加一條獨立成行的公式。 */
export function textWithMath(lead: string, latex: string): Doc {
return {
type: 'doc',
content: [
{ type: 'paragraph', content: [{ type: 'text', text: lead }] },
{ type: 'math_block', attrs: { latex } },
],
};
}
/** 在送出前檢查長度。 */
export function contentLength(doc: Doc): number {
let n = 0;
const walk = (node: any) => {
if (node.type === 'text') n += node.text.length;
if (node.attrs?.latex) n += node.attrs.latex.length;
node.content?.forEach(walk);
};
walk(doc);
return n;
}