快速上手
建立題目、組成考試、提交作答、取回分數
本頁面將說明並示範如何完成一次完整的非選擇題批閱流程:建立題目、組成考試、提交一筆作答、取回分數。
若您尚未取得 API 金鑰,請先參閱如何在 Studio 取得 API 金鑰。
本頁面示範的 API 金鑰需要以下權限範圍: exams:read、questions:manage、exams:manage、
submissions:create、submissions:read、evaluations:read。
在 Studio 建立金鑰,或調整您要使用的金鑰權限時,請確保此這六項均有勾選。
在開始前,建議您預先設定相關環境變數:
export NORMA_API_KEY="sk_live_..."
export NORMA_API="https://api.norma.terathinker.com/v1"步驟 0:確認 API 金鑰可用
您可以先傳送一個唯讀請求,以確認連線與憑證是否正常:
curl -s -i "$NORMA_API/exams?limit=1" \
-H "Authorization: Bearer $NORMA_API_KEY"端點回應 200 狀態代表 API 金鑰有效。若非成功的請求,請對照以下錯誤狀態確認並排除:
| 狀態 | 意義 |
|---|---|
401 ERR_UNAUTHORIZED | API 金鑰值有誤,或標頭格式錯誤 |
401 ERR_API_KEY_DISABLED | API 金鑰已被停用,請建立新的金鑰 |
403 ERR_INVALID_SCOPE | API 金鑰有效但缺少需要的權限 |
API 金鑰的權限清單,可以在 Studio 的金鑰頁面上查詢,或調整權限範圍。
步驟 1:建立題目(Question)
題目為學生建立作答的基本單位。每一道題目均需要題幹與評分標準。該題目的評分標準可直接嵌入題目,或引用自可跨題目共用的評分標準物件(Rubric)。此處將以直接內嵌於題目作為範例。
題幹內文為一段 ProseMirror JSON,可以同時容納文字、數學式,及其他文字樣式。請詳見作答與題幹的內容格式。
curl -s -X POST "$NORMA_API/questions" \
-H "Authorization: Bearer $NORMA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"custom_id": "photosynthesis-01",
"type": "short_answer",
"content": {
"stem": {
"type": "doc",
"content": [
{ "type": "paragraph", "content": [
{ "type": "text", "text": "請說明光合作用的光反應與暗反應。" }
]}
]
}
},
"scoring": {
"max_score": 10,
"reference_answer": "光反應在類囊體膜產生 ATP 與 NADPH;暗反應在基質固定 CO₂ 生成醣類。",
"rubric": {
"dimensions": [
{ "name": "內容正確性", "levels": [
{ "score": 6, "description": "正確說明光反應與暗反應的過程與產物" },
{ "score": 3, "description": "僅說明部分過程" },
{ "score": 0, "description": "內容有重大錯誤或未作答" }
]},
{ "name": "完整性", "levels": [
{ "score": 4, "description": "涵蓋所有主要步驟" },
{ "score": 2, "description": "涵蓋部分步驟" },
{ "score": 0, "description": "缺少關鍵步驟或未作答" }
]}
]
}
}
}'成功建立題目的 HTTP 回應為 201:
{
"data": {
"id": "q_7g2NkP",
"version": 1,
"status": "active",
"custom_id": "photosynthesis-01",
"type": "short_answer",
"content": { "stem": { "…": "…與送出時相同…" } },
"scoring": {
"max_score": 10,
"reference_answer": "光反應在類囊體膜產生 ATP 與 NADPH;暗反應在基質固定 CO₂ 生成醣類。",
"rubric": { "…": "…與送出時相同…" }
},
"created_at": "2026-03-01T08:15:00Z"
}
}其中的 id 與 version 代表本題目的識別碼與版本號。本服務中,另外會將 ID 與 Version 串連起來,形成如 "q_7g2NkP:1" 的表示法,稱為版本化參照(versioned reference)。
步驟 2:組成考試(Exam)
一份考試需給定一個或多個題目的版本化參照進行組題。題目的版本亦可使用 :latest 指定,並會在建立的當下,自動解析並替換為最新的明確版本。
curl -s -X POST "$NORMA_API/exams" \
-H "Authorization: Bearer $NORMA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: exam-bio-midterm-2026" \
-d '{
"name": "期中考 - 生物",
"custom_id": "midterm-bio-2026",
"questions": ["q_7g2NkP:latest"]
}'成功建立後,考試便可以立即接受提交,同時其 questions 列表(包含題目及其版本)也將固定,後續不得修改,以避免學生的提交無法符合題目內容。
{
"data": {
"id": "exam_kQXzTR",
"custom_id": "midterm-bio-2026",
"name": "期中考 - 生物",
"status": "active",
"questions": [{ "question_ref": "q_7g2NkP:1", "…": "…" }],
"created_at": "2026-03-02T09:00:00Z"
}
}步驟 3:建立學生提交(Submission)
建立考卷後,即可開始建立學生的考卷提交(Submission)。且系統會在成功建立提交後,立即自動開始排程批閱其中的題目作答。
所有考卷提交都必須包含一組自訂的冪等鍵 Idempotency-Key(格式不限),以確保同樣的請求重送時得以正確識別去重。擁有相同鍵與相同內容的請求,將會收到原本的回應重播,不會產生第二筆提交。
responses 必須完整涵蓋該考試的所有題目。您可以參考考試的 gradeable_count 參數,以確認應送出的題目總數;若考試包含題組,則應作答總數將以其最末端子題計算。
就算學生有題目未作答,仍應送出一份符合 ProseMirror JSON 規範的 doc,並內涵一段空的 paragraph 作答。
curl -s -X POST "$NORMA_API/submissions" \
-H "Authorization: Bearer $NORMA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: sub-student0042-exam_kQXzTR-20260302" \
-d '{
"exam_id": "exam_kQXzTR",
"student_ref": "student_0042",
"responses": [
{
"question_ref": "q_7g2NkP:1",
"answer": {
"type": "text",
"doc": {
"type": "doc",
"content": [
{ "type": "paragraph", "content": [
{ "type": "text", "text": "光反應在類囊體膜上進行,把光能轉成 ATP 與 NADPH;暗反應在基質中利用這些能量固定二氧化碳。" }
]}
]
}
}
}
]
}'成功回應的 HTTP 狀態碼為 201,提交狀態將為 pending,且系統將自動立即排程開始批閱。
{
"data": {
"id": "sub_9Xw2Lp",
"exam_id": "exam_kQXzTR",
"student_ref": "student_0042",
"source": "direct",
"status": "pending",
"response_count": 1,
"responses": [
{
"id": "resp_5Kc3Yv",
"question_ref": "q_7g2NkP:1",
"status": "pending",
"eta": 3,
"…": "…"
}
],
"eta": 3,
"created_at": "2026-03-02T09:30:00Z"
}
}eta 欄位為該份考卷提交(或該筆題目作答)的預估批閱剩餘分鐘數。該數值由當下系統負載、題目類型等資訊即時估算,且欄位只在批閱未完成時出現。該時間僅供估算參考,並非批閱完成的時間保證,也不盡然將會隨時間絕對遞減。
步驟 4:取回結果
使用考卷提交的查詢端點,以查詢批閱狀態與結果:
curl -s "$NORMA_API/submissions/sub_9Xw2Lp" \
-H "Authorization: Bearer $NORMA_API_KEY"正式生產環境中,請避免對此端點輪詢以更新狀態。建議使用 Webhook 訂閱 submission.completed 事件,以獲得批閱完成的即時通知。
完成批閱後,每筆作答都會內嵌其批閱結果:
{
"data": {
"id": "sub_9Xw2Lp",
"status": "completed",
"responses": [
{
"id": "resp_5Kc3Yv",
"question_ref": "q_7g2NkP:1",
"status": "completed",
"evaluation": {
"id": "eval_8Sm4Tb",
"version": 1,
"method": "rubric_grading",
"outcome": "graded",
"score": { "earned": 7, "max": 10 },
"dimensions": [
{
"key": "內容正確性",
"score": { "earned": 3, "max": 6 },
"feedback": "光反應說明正確,暗反應產物有誤。"
},
{
"key": "完整性",
"score": { "earned": 4, "max": 4 },
"feedback": "涵蓋所有主要步驟。"
}
],
"feedback": "整體結構完整,建議補強暗反應的化學細節。",
"review_suggested": false,
"grader_revision": "grev_5a1d8c",
"graded_by": "ai",
"graded_at": "2026-03-02T09:33:15Z"
}
}
],
"summary": {
"total_responses": 1,
"completed": 1,
"failed": 0,
"total_score": 7,
"max_score": 10
},
"completed_at": "2026-03-02T09:33:15Z"
}
}批閱結果的完整物件與欄位說明請參考取得批閱結果頁面。