iT邦幫忙

2026 iThome 鐵人賽

DAY 24
0

前一天收尾時我記下一個「已知但今天不碰」的問題。今天回來處理它,
因為它其實不是小瑕疵,是這個系統目前最嚴重的一次誤報。

四個點

  1. 學生按「送出程式碼」→ 評測真的跑過 → all_passed: True
  2. 學生手癢又改了兩行,改壞了
  3. 他在右欄打字:「我剛剛又改了一下,這樣可以嗎?」
  4. 導師回:「這段程式碼已經順利通過所有測試了,做得很好!」

那是今天真的跑出來的回覆,不是我編的。

原因在 state 的生命週期:grade 一旦寫進去就留到換題為止,
而 code 每輪都以 UI 為真、重送最新的(這是第 1 期就定好的分工,本身沒錯)。
於是打字提問那一輪的 prompt 長這樣:

【學生目前的程式碼】     ← 改壞的新版
【評測結果】以下是**實際執行**學生的程式…
- 已確認全部通過。
這一題已經通過了。直接告訴學生**已答對**…

兩個區塊各自都是真的,並排在一起就成了一句謊話。

這是同一條原則的第三次出現

這個專案從第 2 期開始反覆撞到同一件事:

  • 第 2 期:評測是模型推測,UI 不可以寫「測試用例通過率」
  • Day 21:執行器跑出來的是事實,模型讀出來的是解讀,兩者不能塞同一個欄位
  • 今天:判定是對某一份程式碼下的,那份程式碼換了,判定就不再適用

前兩次講的是「這個結論從哪來」,今天這次講的是「這個結論關於什麼」。
一份沒有繫在對象上的判定,時間一久就會漂到另一個對象身上——
而且漂的過程完全沒有徵兆。

把判定繫回它判的那份程式碼

grade 裡多記一個欄位:

"graded_code": code,     # 這份判定是對哪份程式碼下的

然後導師的 prompt 與左欄各自比對一次。不一致就明講:

注意:這份評測是對上一版程式碼跑的,學生之後又改過了,
它不代表眼前這份程式碼的對錯。

而且 all_passed 那道「直接告訴學生已答對」的指令,
從此只在判定對得上眼前這份程式碼時才下。

兩個判準都是往同一個方向倒:

  • 舊 checkpoint 沒有 graded_code → 當成過期。 跟 source 欄位缺失時
    一律當推測是同一條:「這是你現在這份的判定」是個宣稱,宣稱要有人明講,
    不能靠欄位遺漏預設成真。
  • 逐字比對,改一個空白也算改過。 寧可多說一次「你改過了」,
    不要有一次謊稱判定還有效。

左欄的那個小麻煩

左欄(評測結果)在版面上排在中欄(編輯器)之前,
所以渲染左欄的時候,user_code 這個變數還不存在。

解法是繞過變數,直接讀 widget 的狀態:

current_code = st.session_state.get(f"code:{problem['title']}", INITIAL_CODE)

Streamlit 的 widget 值存在 session_state 裡,key 是我們自己給的
(這個 key 綁著題目標題,本來是為了換題時重置編輯器)。
rerun 一開始它就已經在那裡了——它是上一次 rerun 結束時學生眼前的值,
正是我們要比對的東西。

排版順序造成的限制,靠「狀態不跟著版面走」繞開。這也算 Streamlit rerun
模型的一個實用推論:想拿某個 widget 在版面後面的值,讀它的 key,不要讀變數。

量到差別了

昨天學到的教訓(prompt 改動要對真模型 A/B)今天就用上。同一個情境跑兩次:

導師的回覆
改動前 「這段程式碼已經順利通過所有測試了,做得很好!你可以直接點擊『換一題』」
改動後 「我目前看不出這段程式碼有什麼明顯的問題,建議你直接點擊『送出程式碼』讓系統評測看看」

誤報消失了。

值得誠實記一筆的是:改動後導師也沒看出那份程式碼把 int(a) + int(b)
改成了 a + b(字串相加)。但那是提示階梯的設計使然——level 0 在沒有判定時
只能說「我看不出問題」,不能自己宣稱程式碼是錯的(第 1 期的 NO_FAULT_RULE)。

修掉一個誤報,換來的是一句誠實但沒那麼有用的話。 這是划算的:
說錯話的代價是學生帶著壞掉的程式碼走人,說得保守的代價只是多按一次送出。

今天學到的

  1. 一份判定要繫在它判的對象上。 只記「結論是什麼」不夠,
    還要記「這是關於誰的結論」——否則對象換了,結論會自己漂過去。
  2. 兩個各自為真的區塊,並排起來可以是假的。 prompt 是拼出來的,
    每一塊都對不代表整份對。
  3. 欄位缺失一律往保守的那邊倒。 這條今天是第三次用到了:
    沒有 source 就當推測,沒有 graded_code 就當過期。
  4. 版面順序擋住的東西,通常可以從狀態那一側繞過去。

測試 317 → 324 支,全綠。


上一篇
Day 23:一條新規則
下一篇
Day 25:把狀態放到行程活不過的地方
系列文
基於 Multi-Agent 協作之蘇格拉底式程式學習與自動化評測系統 共 25 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言