考古題練習工具
AI幫幫忙系列

AI幫幫忙-4:題庫工具功能設計與思路:多題型、簡答、模擬考試、計時與還原

功能設計篇・需求對照・進度對照・多題型判分・簡答出題・模擬考試保存重考・計時器・未交卷還原・IM 與 Quiz API 尚未實作

上一篇《如何結合AI 及 IM BOT》是規劃文章。這一篇有兩個任務:一是把計畫逐項對照現況(進度對照),二是把這一輪「長出來」的功能,怎麼設計、為什麼這樣設計、中間改過什麼一次寫清楚。 主角是五件事:多題型的判分模型、簡答題的出題方式、模擬考試的保存與重考、計時器、未交卷的作答還原。

V2 保留不動,V3 測試版在這裡:https://blog.coins2ex.com/info-test/v3/info-test.html。誠實聲明:本文依我上傳的最新版 info-test.html 逐項對照寫成。文中的「完成」只代表我讀過程式、做過語法檢查與部分模擬測試,我還沒有在真實瀏覽器與手機上逐項實測;IM 機器人與 Quiz API 那一半依然只是規劃。


前言:需求把順序改了

第 3 篇的里程碑是 M0、M1、M2……,先契約、再多題型、再 Quiz API。實際發生的事完全不是這個順序。我手上多了四門空大課程的自我評量與期中考簡答題,逼得工具先回答幾個很實際的問題:

  1. 題目不只有單選,有是非、複選,還有「請至少寫出三點」的簡答題,怎麼作答、怎麼評分?
  2. 題庫從資安擴大到 AI 應用與空大課程,單一下拉選單塞不下,怎麼分類?
  3. 想練「像考試」的卷子:選擇加簡答、有時間壓力、做到一半關掉頁面還能接著做、同一份試卷可以重考。

所以這一篇先看版本差異與進度對照,再講設計原則,接著逐項拆解功能,最後講資料與流程、驗證到哪裡。

V2 與 V3 測試版:先看差異

兩個版本我同時保留,網址不同。V2 是穩定版;V3 是同一個工具往多題型與課程題庫延伸的測試版。

項目 V2(穩定版,保留) V3(測試版)
題型 單選 單選、是非、複選、簡答(自評)
簡答題 無 最少 N 點加提示,或提示加填空,每次隨機
模擬考試 無 同科目客觀題加簡答,依占分配分;試卷與結果完整保存,可重載重考
計時 無 作答後開始計時,靜止、切換或關閉頁面即停止
未交卷還原 無 自動保存草稿,重開頁面詢問是否繼續
題庫選擇 年度 / 科目單層選單 先選題庫大分類,再選年度 / 科目
選項與分析文字 過長時截斷 完整顯示、自動折行
綜合題庫題數 10 / 25 / 50 10 / 20 / 25 / 50 / ALL
作答紀錄 作答、錯題、備註 另記各題得分、用時,模擬考試另存試卷與總分
IM 機器人、Quiz API 無 尚未實作

保留 V2 的理由很單純:V3 新增的欄位(得分、用時、試卷、簡答文字答案)伺服器端是否保存,我還沒驗證。穩定版不動,測試版才敢放手改。

需求對照:最新版 info-test.html 做到哪裡

這一段是我拿「要求清單」逐項核對上傳的最新版程式(約 59 KB)的結果。標記沿用下方進度對照的五種。

需求 現況 實際做法與差異
1. 計時器:出題並開始答題後計時;靜止超過 10 分鐘或關閉頁面即停止 ✔ 完成 出題後先不計時,第一次在題目區輸入或選取才開始。超過 10 分鐘沒有任何操作就停止,並扣掉靜止那段時間;切換分頁、關閉頁面也暫停,回來後有動作即恢復。交卷後停止,檢視舊作答時不計時。用時寫入每次作答紀錄
2. 答題狀態紀錄:cookie 保留未交卷答案;重開頁面詢問是否繼續,並提示科目與上次時間 △ 修改 提示內容符合:顯示考試種類、題庫標題(含科目)、最後儲存時間、已作答題數、已用時,可選「繼續」或「放棄並刪除」。但答案存在 localStorage,不是 cookie:cookie 容量約 4 KB,放不下整份答案;cookie 只寫一份摘要(draftmeta),而且目前程式沒有讀它
3. 「模擬考試卷(期中 / 期末)」改名為「模擬考試」 ✔ 完成 區塊標題、按鈕「產生模擬考試」、試卷標題都已改;紀錄檔 id 仍是 exam-能力-科目
4. 考試設定不須選擇期中 / 期末 ✔ 完成 已移除考試類型選單與預設表;產生試卷時預設客觀題為 20 題與題庫題數取小者、簡答 4 題與題庫題數取小者,客觀占 60%(沒有簡答題庫時 100%),都可手動改
5. 同科目沒有選擇 / 是非以外的題型,就不顯示簡答 / 填空設定 ◐ 部分完成 模擬考試卡片會依「該科目有沒有簡答題庫」隱藏簡答題數與占分欄位,並把簡答題數歸零、客觀占 100%。填空題尚未實作,所以沒有填空設定可顯示。但「② 出題設定」裡的「簡答題作答方式」選單不分科目,永遠顯示
6. 試卷題目與測試結果完整保存,可選擇重載題目重複測試 ✔ 完成 見功能三:試卷快照、歷次結果、重載重考、檢視作答、匯出匯入、刪除都有

另外有三個我讀程式時發現、需求沒提到、但值得知道的行為:

進度對照:計畫與現況

我用五種標記,避免把「做了一半」寫成「完成」:

標記 意思
✔ 完成 程式或資料已做完,並在沙盒驗證過(語法檢查、模擬測試);仍未實機
◐ 部分完成 一部分做了,其餘沒做,或關鍵環節無法驗證
○ 未完成 沒有動
+ 新增 原計畫沒有,因需求產生
△ 修改 原計畫有,但做法或決定被改掉

里程碑

里程碑 計畫內容 現況 說明
M0 契約定稿 題庫 v3 欄位、API 規格、狀態機、資料表 ◐ 部分完成 題庫欄位與題型已落地(寫進題庫並整理成規則 V4 的第 3 節);API 規格、狀態機、資料表仍只是草案
M1 是非與複選 grade()、render 分支、records.php 放寬 ◐ 部分完成 前端完成,且擴及簡答題;records.php 沒有上傳,是否保存新欄位無法確認;正式的單元與回歸測試沒有做
M2 Quiz API 基礎 資料庫、角色、綁定、稽核 ○ 未完成 沒有動
M3 匯入與審核 驗證器、管理後台、上架 ○ 未完成 程式沒有;但我用人工流程先頂著(見下方「+ 爭議檢查」)
M4 Telegram 做題 外掛、工作階段、按鈕 ○ 未完成 沒有動
M5 LINE 做題 Reply only ○ 未完成 沒有動
M6 對話式匯入 /匯入 指令與閉環 ○ 未完成 沒有動
M7 網站聊天窗做題 中轉層帶入 uid ○ 未完成 沒有動
M8 填空與簡答自評 判分與介面 ◐ 部分完成 簡答自評前端完成(含自動比對與出題方式);填空(fill)沒有做,仍被略過並提示
M9 QQ(可選) 官方機器人適配 ○ 未完成 沒有動

計畫外的新增與修改

項目 標記 內容
題庫資料層 v1.1 欄位 + 新增 schemaVersion: 2、visibility、owner、source、aiMeta、answerSource、simulatedFields,每題 type、grading,先寫進題庫,工具暫不讀
題庫大分類 + 新增 單一題庫先選大分類,再選年度與科目;group 欄位可覆寫
選項與分析完整顯示 + 新增 不再用「…」截斷,改為自動折行
綜合題庫題數 △ 修改 由 10、25、50 改成 10、20、25、50、ALL
簡答題出題 + 新增 最少 N 點加提示,或提示加填空,每次隨機
模擬考試 + 新增 同科目客觀題與簡答題,依占分比例配分
空大課程題庫 + 新增 新分類 ou,4 份選擇與是非題庫(共 340 題)、3 份簡答題庫(共 22 題)
標準答案升級流程 + 新增 題庫匯出成純文字,我標 *,AI 回填並回報差異
爭議檢查 + 新增 每題都檢查誤植、答案唯一性與時效,有疑問寫進 note
visibility 預設值 △ 修改 計畫寫預設 private;現況未標示視為 public,新匯入題庫預設才是 private
V2 保留、V3 另立測試網址 + 新增 V2 穩定版不動;V3 測試版放在獨立路徑
計時器 + 新增 作答後開始計時,靜止、切換或關閉頁面即停止,用時存入紀錄
未交卷草稿與還原 + 新增 作答中自動保存,重開頁面詢問是否繼續;答案存 localStorage,cookie 只放摘要
模擬考試試卷保存與重考 + 新增 試卷快照、歷次結果、重載重考、檢視作答、匯出與匯入
模擬考試改名、不分期中期末 △ 修改 原「模擬考試卷(期中 / 期末)」改為「模擬考試」,取消考試類型選單
系列文章與索引頁 + 新增 第 3 篇補上 V3 測試網址與上下篇連結;索引頁新增第 4 篇與 V2 / V3 差異表
簡答 AI 評分 ○ 未完成 依原計畫排在最後,沒有動

設計原則:三條規則撐住所有改動

1. 最小修改,不整份重寫。 每次改動都在上傳的 info-test.html 上做,保留元素 id、class 與 JSON 欄位名,改完列出差異。大分類那一輪只動約 35 行;簡答與試卷那一輪,原有程式碼只改了 11 處,其餘都是新增。

2. 得分統一用 0 到 1。 單選與是非是 0 或 1,複選可以按比例給分,簡答是命中比例,自評則是 1、0.5、0。「錯題」的定義也統一成「得分小於 1」。於是錯題統計、備註、歷史錯題練習、綜合題庫、模擬考試計分,完全不用為新題型各寫一套。

3. 資料層先行,執行層後做。 欄位(type、grading、visibility、group……)先寫進題庫,工具只讀它需要的。我把狀態拆成兩層:資料層是欄位已定義、已寫進題庫;執行層是工具或 API 是否讀取並改變行為。visibility、owner、aiMeta 現在資料層都有,但工具只用 visibility 決定「更新 index.json」要不要收錄。這代表私有題庫放在可下載的資料夾裡,一樣會被下載到,真正的保護要等 Quiz API。沒有真實資料的欄位用模擬值,並列入 simulatedFields。

功能一:多題型的判分模型

判分入口只有一個 grade(q, userAns),依題型分流,回傳一致的結果(得分、是否完全正確、訊息):

題型 作答 得分 備註
single radio 0 或 1 沒寫 type 的舊題一律當單選
tf radio(是 Y / 否 N) 0 或 1 視為兩個選項的單選
multi checkbox all:全對才 1;partial:依對錯比例 partial 未滿分仍算錯題
short 文字輸入 關鍵字比對得 0 到 1,可自評覆寫 見功能二
fill — — 尚未實作,載入時略過並提示

三個細節:

功能二:簡答題為什麼要「提示加隨機」

簡答題最常見的場景是:題目寫「請至少寫出三點」,教材卻有六項。只給一個空白輸入框,壓力大,而且每次背同一組答案。設計目標是讓同一題重複練習時,看到的線索不一樣。

兩種作答方式

模式 適用 呈現
A:最少 N 點加提示 題目有「至少 N 點」 隨機給 0 到 2 個提示(提示算入 N 點),再填其餘點數
B:提示加填空 題目沒有指定點數 給 1 到 2 個提示,另指定 2 到 3 項要點讓你填空

預設依題意自動選;設定裡可以強制一律 A 或一律 B。每次出題(包含「重新作答」)提示與填空位置都重新抽;但同一份模擬考試重考時,預設沿用原本的提示,勾選「重考時重新隨機」才重抽。

把出題結果集中在一個函式

出題邏輯只放在 shortSetup 一處,畫面和評分共用同一份結果:

// 概念示意(非完整程式)
function shortSetup(q){
  const items = shortItems(q);              // 要點:items → 由 reference 拆 → 關鍵字
  const need  = q.answer.need ?? Math.ceil(q.answer.pass * items.length);
  const mode  = pickMode(need, items.length);   // A 或 B
  if (mode === 'A') {
    const h = rand(0, Math.min(2, need - 1, items.length - 1));
    return { mode, hints: pick(items, h), reqA: need - h };
  }
  const hints  = pick(items, items.length >= 3 ? rand(1, 2) : 1);
  const blanks = pick(rest(items, hints), rand(2, 3));
  return { mode, hints, blanks };
}

如果畫面和評分各自抽一次,就會出現「畫面給了提示,評分卻以為沒給」。而且這份結果現在還要一起存進草稿與試卷快照,才能在還原或重考時畫面一致,所以集中成一份資料格外重要。邊界條件也在這裡處理,例如 A 模式提示上限是 N 減 1,避免使用者「一個都不用寫」。

評分:關鍵字先比,自評最後定

要點資料格式沿用 reference 與 keywords,每個要點一組同義詞:

{"type":"short","question":"…請至少寫出三點?",
 "answer":{"reference":"(1) … (2) … (3) …",
           "keywords":[{"any":["遷移","關廠","歇業"],"w":1}],
           "pass":0.5,"items":["…"],"need":3},
 "grading":{"mode":"self"}}

A 看「命中點數 ÷ 需要點數」,B 看「填對格數 ÷ 總格數」。顯示答案後,使用者可以按「對、部分、錯」覆寫得分。我刻意不一開始就交給 AI 評分:關鍵字比對一定會誤判,必須有人工自評當最後一道防線;AI 評分要經過伺服器、金鑰、額度,屬於第四期。

功能三:模擬考試——從「出一張卷」到「可保存、可重考」

配對與配分

簡答題庫的科目代碼是「對應科目加 short」(如 hrs 對 hrsshort),模擬考試靠這個約定把客觀題庫和簡答題庫配成同一科目。優點是不必改任何既有題庫;缺點是改錯一個字就配不上,我標成「待確認」。

項目 規則
客觀題每題 滿分 × 客觀占分 ÷ 客觀題數
簡答題每題 滿分 × 其餘占分 ÷ 簡答題數
預設 客觀 20 題、簡答 4 題(都不超過題庫題數)、客觀占 60%、滿分 100;沒有簡答題庫時客觀占 100%
版面 一、是非題 二、選擇題 三、簡答題,區標題標示每題分數與小計
計分 得分 = Σ(配分 × 該題得分),另記滿分

模擬測試時抓到過一個小錯:3 題簡答、每題 13.33 分,如果先四捨五入再相加,總分變成 99.99。修正是內部保留完整精度,只在顯示時四捨五入。

為什麼不分期中、期末

原本我把「期中 / 期末」做成選單,只是兩組不同的預設題數。實際用起來,題數本來就會依科目題庫大小調整,選單沒有帶來新資訊,反而多一個決定。現在拿掉,預設值依題庫大小自動取小。

試卷是一份「快照」

這是這一輪最重要的設計決定:模擬考試產生之後,存的不是「抽題規則」,而是整份試卷的內容。快照包含:題目全文(含選項、解析、配分、分區)、選項順序、簡答的提示與填空位置、產生時間、設定。理由很實際:

每次測試的結果另存為一筆作答紀錄,包含各題答案與得分、總分、用時,以及用到的試卷代號。於是一份試卷底下可以累計多次測試。介面上對每份試卷提供:

操作 作用
重載重考 以同一份試卷重新作答;可勾選「重考時重新隨機選項順序與簡答提示」
檢視作答 以唯讀方式還原某一次的作答與結果
匯出 / 匯入 試卷加歷次結果存成一個 JSON 檔,可備份或換機器
刪除 刪除試卷題目內容,但歷次成績仍保留在作答紀錄

紀錄檔是 exam-能力-科目.records.json,試卷放在 papers,成績放在 attempts。合併時以試卷代號去重,刪除用「墓碑」標記,避免多個裝置合併時把已刪除的試卷又帶回來。

這個設計的代價:試卷帶整份題目文字,紀錄檔會隨試卷數量變大,目前沒有保存份數上限;而 Web 模式的 api/records.php 我沒有看過,不確定它會不會保留 papers、elapsed 等新欄位。在確認之前,把試卷「匯出」備份是比較穩的做法。

功能四:計時器——怎麼算才算「用時」

需求很直覺,但「用時」其實有幾個容易算錯的地方:

實作很小:一個狀態物件(累計毫秒、是否執行、最後活動時間)、一個每秒執行的 timerTick,以及活動事件。我用模擬時鐘驗證過核心邏輯:未作答不開始、第一次作答開始、靜止超過 10 分鐘會停止並扣除靜止時段、再操作會恢復、關閉頁面會暫停。真實瀏覽器的背景分頁節流與手機休眠,我沒有實測。

功能五:未交卷還原——為什麼答案不放 cookie

需求寫的是「用 cookie 保留未交卷的答案」。我讀完實作後發現這裡改了做法:答案存 localStorage,cookie 只放摘要。理由是容量:cookie 單筆約 4 KB,而且每次請求都會送到伺服器;一份 40 題的作答加上簡答文字、選項順序、簡答提示、模擬考試的整份試卷,很容易超過。

項目 做法
存什麼 題庫或試卷代號、題號清單、各題答案、選項順序與簡答提示、已用時、最後儲存時間;模擬考試連整份試卷快照一起存
何時存 每次輸入後約 0.5 秒、計時進行中每秒、切換分頁與關閉頁面時;至少作答一題才存
何時清 交卷、按「重新作答」、選擇「放棄並刪除」,以及產生或重載模擬考試時
重開頁面 頂端出現提示:題庫標題(含科目)、最後儲存時間、已作答題數、已用時,按鈕「繼續上次的考試」或「放棄並刪除」
還原後 題目順序、選項順序、簡答提示和用時都回到中斷時的狀態,繼續作答即恢復計時

這個做法有三個已知缺點,我寫在這裡而不是藏起來:

  1. 只有一個草稿欄位,同時做兩份會互相覆蓋。
  2. 產生或重載模擬考試會直接清掉未完成的草稿,沒有確認。 這應該改成先詢問。
  3. 不能跨裝置。 要跨裝置就必須把草稿存到伺服器,牽涉到 api/records.php,目前沒有。

功能六:題庫分類與完整顯示

命名的坑:空大課程原本用 ability: ec,後來統一改成 ou。內建的大分類對照沒有 ou,所以這些題庫必須帶 group: 空大課程 才會歸對;題庫 id 一改,舊的作答紀錄就對不上。命名要一次想清楚,事後改名的成本還在使用者累積的紀錄。

題庫整理:標準答案與 AI 判斷分開

來源格式 處理
選項前 * 標準答案
CHx 單獨一行 章節,寫入 category
題號前 Y 或 N 是非題
沒有選項的題目 是非題(依詳解判定)
詳解: 後面的字母 該題答案
沒有任何答案標示 AI 判斷,並查證條文

標準答案可以覆蓋 AI 判斷。 舊題庫的答案全是 AI 判斷,我把題庫匯出成純文字、在標準答案前加 *,再貼回去重新生成。結果有 6 題 AI 原本判斷錯了(112 年資安技術概論第 23、24 題、113 年資安管理概論第 45 題、113 年資安技術概論第 35、38 題、114 年資安管理概論第 27 題)。重點不是 AI 會錯,而是有一個機制讓我的答案能直接蓋過去,而且一眼看得出每題答案是誰給的。

懷疑標準答案有誤時,只標註,不擅自改。 例如跨境電子商務 CH5 第 5 題標「否」、第 6 題標「是」,兩題描述的卻是同一件事;就業服務管理 CH1 第 13、14 題的答案與課程資料對不上。AI 在備註寫「標準答案疑有問題」與理由,由我對照教材決定。有意思的是,有些疑慮後來被簡答題原文解開:簡答題寫明阿里巴巴的四個經營理念是「找商機、建立公司、交商友、看商情」,所以選擇題的「以上皆非」其實成立。同一門課的不同題型,可以互相驗證。

查證會推翻記憶。 就業服務管理 CH4(25 題)沒有標準答案,我要求先搜尋條文再判斷,結果幾個數字與記憶不同:身心障礙者就業基金撥交就業安定基金的比率是 30%、勞保滯納金上限是應納費額的 20%。沒查證的部分,備註一律寫「依記憶,未經本次搜尋查證」。

目前累積的空大課程題庫:選擇與是非題 340 題(電子商務導論 92、跨境電子商務 77、就業服務管理 71、人工智慧導論 100),簡答題庫 22 題(就業服務管理 9、電子商務導論 6、跨境電子商務 7)。

驗證:我做了什麼,做不到什麼

這一輪對最新版檔案做的:

之前對簡答與試卷做過、但這次沒有重跑的:

做不到的,也是我最擔心的:

還沒做、也不想誇大的地方

這一輪和 AI 協作的心得

1. 叫 AI 拿「上傳的檔案」去對照,而不是憑記憶說進度。 這次我把需求清單丟給它,請它對照最新版程式。結果它抓出一個我自己沒意識到的偏差:答案其實存在 localStorage,不是 cookie;還有一個風險:產生新試卷會默默清掉草稿。進度文件最怕憑印象。

2. 先把「判分模型」想清楚,再談介面。 得分統一成 0 到 1 之後,新題型和模擬考試計分幾乎是「接上去」而不是「改下去」。

3. 需求寫「怎麼做」之前,先問「為什麼」。 「用 cookie 保存」背後的目的是「重開頁面能接著做」;換成 localStorage 一樣達成,而且不會撞到容量。只要目的達成、差異寫清楚,實作可以調整。

4. 把「標準答案」和「AI 判斷」分開,並讓前者可以覆蓋後者;AI 可以指出疑點,但不要讓它改你的答案。

5. 查證會推翻記憶,所以要記錄「查過」與「沒查」。 條文的數字,AI 憑記憶寫的和搜尋到的不一樣至少有三處,備註寫明查證狀態,下次回頭看才知道哪些可以信。

6. 模擬測試抓邏輯,抓不到畫面。 它抓得到配分與計時的錯,但抓不到排版與按鈕。所以我要求 AI 明講「沒有實測的部分」,那些才是我要自己點開瀏覽器確認的。

7. 需求會把計畫打亂,但文件要跟著更新。 這次我沒有照 M0 到 M9 走,而是被期中考推著做簡答、模擬考試、計時與還原。計畫不必假裝照走,但要定期回頭對照、標清楚誰完成誰沒完成。這就是「進度對照」和「需求對照」兩節的由來。

下一步

順序 事項 為什麼
1 在真實瀏覽器與手機實測簡答、模擬考試、計時與還原 目前最大的未知
2 產生或重載模擬考試前,先詢問是否覆蓋未完成的草稿 避免默默丟掉作答
3 取得並放寬 api/records.php 讓 Web 模式保存試卷、用時等新欄位
4 用「更新 index.json」重建索引,移除空字串 新題庫才會被載入
5 「簡答題作答方式」選單也依科目隱藏 完成需求 5
6 補填空題(fill) 完成 M8
7 評估簡答題庫的明確配對欄位,取代命名約定 降低改錯字就配不上的風險
8 回到 M2:Quiz API 與角色 真正的私有題庫保護與對話功能的前提

IM 做題與對話式匯入,我依然認為值得做,但在上面幾件事有結果之前,不會動。

結語

這個系列的做法一直沒變:把重複的事拆成資料和工具,讓 AI 做機械性的整理,我負責決定方向和驗收。這一輪多了一個體會:判分模型、資料格式與流程的品質,比功能數量更決定這個工具好不好用。 能把答案的來源標清楚、能發現教材的矛盾、能誠實說出沒驗證的部分,比多一個按鈕重要。

如果你也在做自己的題庫工具,建議先決定「每一題的得分怎麼表示」和「答案是誰給的」,再談題型與介面。