iT邦幫忙

2026 iThome 鐵人賽

DAY 27
0

今天寫完第 3 期的程式碼導讀(出題 agent 與閉環),三期的筆記到齊了。

跟昨天一樣,重讀翻出了東西。但今天最值得記的那件事,性質跟昨天不同——
昨天是「發現當時沒想清楚」,今天是發現自己寫下了一句從來沒有驗證過的話。

那句話

寫到出題 agent 的降級機制時,我很自然地打了這麼一句:

這是三個 agent 裡唯一一個「模型掛掉仍能運作」的。

聽起來很順,因為出題的降級確實做得最完整:呼叫失敗、空內容、解析失敗、
模型挑了清單外的題號——四種情況全都退回「粗篩的第一名」,學生照樣換得到題。

打完之後我回去對了一下另外兩個 agent。錯了。

評測 agent 在有執行器的時候,模型掛掉一樣能動:

try:
    explanation = explainer(problem=problem, code=code, results=results)
except Exception:
    # 判定已經成立了。解讀只是錦上添花,它壞掉不該把事實一起拖下水。
    explanation = ""

判定是子行程跑出來的,模型只負責那句「為什麼」。它掛掉只是少一句話。

而這行程式碼是我自己在 Day 22 寫的,註解也是我寫的。 隔了五天,
我在另一個檔案裡寫下了跟它矛盾的一句話。

為什麼會這樣

因為「哪些 agent 撐得住模型掛掉」這個問題,我從來沒有被迫回答過。

寫程式的時候,我一次只面對一個 agent:寫出題的降級時想的是出題,
寫評測的容錯時想的是評測。每一段都是對的。
但「三個加起來是什麼樣子」這句話,沒有任何一段程式碼需要我講出來。

導讀筆記需要。它的格式逼我寫「為什麼這樣寫」,而寫到某個程度就會需要
橫著比較——而橫著比較的結論,是程式碼裡不存在的知識。

最後改成一張表:

agent 模型掛掉還能不能動 靠什麼
評測 能(有執行器時) 子行程跑出來的事實,explanation 空掉而已
出題 能 粗篩的第一名
導師 不能 引導本來就是模型的工作,沒有東西可以退回

然後才看出這張表其實在講一件更一般的事:

能算出來的部分要留在自己手上,模型只負責算不出來的那一段。
算得出來的部分越大,模型掛掉時剩下的功能越多。

這條原則在三個 agent 裡各出現過一次,我卻是今天才看見它。
評測把「過不過」交給執行器、出題把「排序」交給 shortlist()、
而導師什麼都交出去了——所以它最脆弱。

這不是我設計出來的,是寫筆記時才浮現的。

第二次撞到同一件事:文件與實作分岔

昨天發現設計文件寫「全過 → 直接出下一題」,實作是「全過 → 記下來 → 導師說一句」。

今天又一次。設計文件的第 3 期寫的是生成題目:

set_problem: { title, description, tests: [str], targets: [...] }

實作是從題庫挑。理由寫在 agents/setter.py 的模組 docstring 裡,
而且是個好理由——錯誤會傳染:

生成的測資錯了
   → 評測照著錯的測資判定(而且是「實際執行」,看起來非常確定)
   → summary 把錯的結果寫成一句話
   → 導師引用那句話去引導學生
   → 學生對著一份正確的程式碼,被要求找出不存在的錯

學生沒有任何辦法發現是題目錯了,因為系統的每一層都在說同一件事。

判斷沒問題。問題是我又一次沒回頭改文件。兩天兩次,這就不是意外了,是習慣缺口。

閉環有一半是為了被看見

targets(這題練到你的哪些弱點)與 pick_reason(為什麼挑這題)這兩個欄位,
系統內部沒有任何邏輯讀它們。

沒有分支靠它們決定、沒有計算用到它們。刪掉的話,挑題結果一模一樣。

它們存在的唯一理由是 app.py 那兩行:

st.caption(f"🎯 這題針對你的:{labels}")
st.caption(saved["pick_reason"])

寫筆記時我對這件事的說法是:

一個看不見的閉環,在使用者眼裡等於不存在。

這也是為什麼 reason 值得花一次 API 呼叫去問模型。粗篩算得出 targets,
但算不出一句人話。「因為你在迴圈上錯了 5 次,這題會練到迴圈邊界」——
這句話比一行 🎯 迴圈 更能讓人相信系統真的在看著自己。

三期走完,LangGraph 到底賺到什麼

設計文件第一天就誠實寫著:「以本專案的規模,框架在技術上賺不到成本,
採用它的理由是學習框架本身。」

跑完三期,這句話仍然成立,但可以講得更精確了。真正賺到兩件:

  1. checkpointer。 Streamlit 每次互動都清空狀態,這是真的技術效益。
    (而且 Day 25 換成 SQLite 之後,它連行程重啟都撐得住。)
  2. 結構可以獨立於 LLM 測試。 五個 lambda 就能驗完所有走訪路徑,
    一秒跑完、不用 API key。

沒賺到的是動態路由——route() 讀的是 UI 給的 intent,不是模型的決定。
這個系統的流程由按鈕決定,不由 agent 決定。

那才是 LangGraph 真正的主場,而這個專案沒有用到。

知道自己沒用到什麼,跟知道自己用到了什麼一樣重要。不然很容易把
「我用了 LangGraph」講成「我需要 LangGraph」。

今天學到的

  1. 橫著比較的結論,是程式碼裡不存在的知識。 每一段程式碼都只知道自己,
    沒有任何一行需要你回答「三個加起來是什麼樣子」。
  2. 順口寫下的那句話最危險。 它順口,正是因為你沒有查證就相信了。
  3. 同一個錯誤犯第二次就不是意外。(文件與實作分岔)
  4. 有些欄位存在的唯一理由是被看見。 它們不參與任何計算,刪了功能也不變,
    但刪了之後使用者就不知道系統在幹嘛。

程式碼沒動,測試維持 327 支全綠。三期的導讀筆記到齊:
docs/notes/phase-{1,2,3}-code-walkthrough.md。


上一篇
Day 26:把第 2 期重讀一遍,發現三件當時沒想清楚的事
系列文
基於 Multi-Agent 協作之蘇格拉底式程式學習與自動化評測系統 共 27 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言