Day 22 做完之後,Visual Learning Lab 已經可以把不同知識送進不同的 Learning World。
拋體運動可以進 Spatial World,Phase 可以進動態數學世界,Probability 可以進集合與機率世界,Ohm’s Law 可以生成 Analogy World,Queue 也開始能走進 generic Process Runtime。
做到這裡其實很容易產生一種錯覺:
好像已經很通用了。
但問題是,前面很多案例都是我們開發過程中一直拿來測的東西。
我們知道 Queue 長什麼樣、知道 Projectile 怎麼跑,也知道 Ohm’s Law 適合用水流來做類比。如果最後一直拿這些熟悉的題目證明系統很強,很容易變成:
我做了一個系統,再挑一堆它本來就會的題目測它。
所以 Day 23 我決定先不急著塞新的大功能。
而是反過來做一件比較殘忍的事:
準備一批之前沒有拿來開發的教材,先把預期答案凍結,再讓目前的 Universal Learning World Compiler 去撞牆。
GitHub Repo:
https://github.com/f24131128-lgtm/visual-learning-lab
這次一開始我就特別要求 Codex:
不准先跑系統,再回頭改標準答案。
不然這個 benchmark 就沒什麼意義了。
所以 Day 23 先準備了一批 frozen materials,一共 12 份,涵蓋:
其中 3 份根據官方文件的事實重新撰寫,另外 9 份則是原創測試材料。
而且在執行之前就先標記好:
也就是先建立 gold,再讓系統跑。
這個步驟我覺得甚至比「測試數量」本身還重要。
第一輪完全使用 Day 22 當時的 architecture。
結果沒有想像中漂亮。
| 指標 | Phase A |
|---|---|
| Planner 選擇可接受 | 8 |
| Planner 明顯選錯 | 1 |
| 沒有成功進入 Planner | 3 |
| 成功編譯成 Learning World | 5 |
| 本地互動正常 | 4 |
| Formal ID / page 檢查通過 | 5 |
| 失敗後仍保留正常學習內容 | 8 / 8 |
| 編譯後額外 API 呼叫 | 0 |
乍看之下 8 個 Planner 選對好像還可以,但更大的問題其實在後面:
選對 representation,不代表真的編譯得出來。
真正「Planner 選擇合理,而且最後成功變成 runtime artifact」的只有:
4 / 8
這次也讓我第一次很明確地把錯誤拆開。
以前可能只會看到:
Learning World 建立失敗。
但現在可以繼續問:
AI 是不是一開始就看錯教材?
還是:
AI 選對了,但 Compiler 接不住?
還是:
Compiler 接住了,但 Runtime 根本不能照語意執行?
這三種問題其實完全不同。
Day 23 後,我不想再用一個布林值把所有問題混在一起。
所以整條流程開始被拆成:
Representation Selection
↓
Compilation
↓
Runtime Interaction
↓
Grounding
↓
Fallback
例如 Planner 判斷某個教材適合 interactive process。
這個判斷可能是對的。
但如果它產生的 spec 裡有一個 optional 欄位沒有通過 Validator,那其實是 Compiler 的問題,不應該算 Planner 看錯。
反過來,如果一份純定義教材被硬塞進物理模擬,那才是真的 representation selection 失敗。
這個拆法後面幫了非常大的忙。
Phase A 跑完後,我沒有叫 Codex:
把這 12 題全部修到會。
因為這樣最後很容易變成十二個 hard-coded patch。
這次的要求是找「重複出現的系統性問題」。
最後主要找到三個。
第一個是部分 Comparison 物件沒有完整 semantic ID。
第二個則是:
只要有數值參數,就被誤認成一定需要時間動畫。
但「可以調一個值」和「這是一個動態系統」根本是兩件不同的事。
第三個則是某些 representation 根本不使用 process 欄位,但只要那個 process 欄位不符合規則,整份結果還是可能一起被擋掉。
這又回到前幾天 Analogy World 曾經遇到的問題:
Optional 的東西壞掉,不應該殺死正確的核心語意。
所以 Phase B 加入了一個比較小的 capability contract。
不是建立超大 plugin system,而是讓 Planner 在選 representation 的時候知道目前 Runtime 真正支援什麼能力。
例如:
continuous numeric
ordered state
finite transition
spatial dynamics
analogy
static structure
Planner 不應該承諾一個 Runtime 根本做不到的互動形式。
這一輪也重新參考 JSXGraph、XState、Cytoscape 等既有工具,但沒有因為看到新輪子就全部裝進來。
Day 23 Phase B 沒有新增 dependency。
最重要的是:
Phase B 沒有換題目。
還是完全相同的 frozen materials。
結果變成:
| 指標 | Phase A | Phase B |
|---|---|---|
| Planner 選擇可接受 | 8 | 12 |
| 成功編譯產物 | 5 | 11 |
| 本地操作通過 | 4 | 11 |
| Formal ID / page 檢查通過 | 5 | 12 |
| Safe fallback | 8 / 8 | 1 / 1 |
| 編譯後額外 API 呼叫 | 0 | 0 |
如果只看「合理 proposal 最後有沒有真的編譯成功」:
4 / 8 → 11 / 12
這個提升我覺得比再增加兩個漂亮 Demo 有意義很多。
因為這次不是:
又多做了一個 renderer。
而是:
原本已存在的 runtime,開始比較能接住模型產生的語意。
但測到這裡,新的問題又冒出來了。
Phase B 之後有幾個 case 讓我發現,目前系統雖然比較會「選」了,但還不能保證:
它真的按照概念本身的規則執行。
第一個是相似 label。
兩個不同概念可能都叫做 Ready。
如果 Runtime 只是拿畫面上的 label 當 identity:
Ready
Ready
那它們很容易被錯當成同一個 entity。
第二個是 Recursion。
畫一張:
S(4)
↓
S(3)
↓
S(2)
↓
S(1)
非常容易。
但這不代表系統真的知道:
第三個則是 Numeric Parameter。
如果教材只說:
amplitude = 4
decay = 0.25
這兩個其實就是固定值。
如果系統硬生一個:
min = max
的 slider,畫面可能還是 render 得出來,但語意其實已經怪掉了。
所以 Day 23 本來差點在這裡收工,最後我決定:
再往下一層挖。
Phase C 的目標只有一句:
Learning World 不只要「看起來像這個概念」,還要真的 obey 這個概念的規則。
這次開始更明確區分:
Semantic ID
Runtime ID
Display Label
Source Locator
Display Label 只是顯示給人看的。
Identity 不可以靠文字名稱猜。
例如:
Assembly Ready = {p,q}
Inspection Ready = {q,r}
兩個都有 Ready,但它們是兩個不同 semantic entity。
最後真人測試時,點選這兩個項目,Learning Inspector 可以正確切換成不同概念。
所以:
Label 相似,不再代表 Identity 相同。
這是 Phase C 我最喜歡的一部分。
之前 generic Process Runtime 已經可以處理:
但 recursion 比較麻煩,因為它真的有 call frame。
所以這次新增的是一個受限的 call / return execution contract。
注意,它不是在執行 AI 生出來的 Python。
也不是把內容丟進 eval() 或 exec()。
它是一個 bounded declarative runtime。
概念大概像:
Caller
↓ Call child
Caller = waiting
Child = active
↓
Child completes
↓ Return
Result → exact caller
↓
Caller resumes
真正負責規則的是可信任的 Runtime。
它自己管理:
AI 只描述:
這裡有什麼 call 關係,以及每個步驟的教學意義。
Phase C 最後有一個 nested_sum 驗收案例。
我實際一路按:
Call child
Call child
Call child
Call child
建立 nested frame。
到了底層之後:
Complete current call
接著逐層:
Return to caller
Complete current call
最後最外層真的得到:
Result = 10
而且完成後,不合法的:
都會被停用。
Reset 仍然可以回到初始狀態。
這跟前幾天的「畫一個像 recursion 的東西」差很多。
現在比較接近:
模型宣告遞迴語意,Runtime 真正執行 call → wait → complete → return。
另一個我很在意的小地方,是 fixed parameter。
例如來源只有:
amplitude = 4
decay = 0.25
以前很容易因為想讓畫面更「互動」,就替它生出一個滑桿。
但那等於系統自己偷偷創造探索範圍。
所以現在開始更明確區分:
fixed
adjustable
derived
如果來源只給固定值,它就是固定值。
除非明確標記為:
generated pedagogical exploration range
否則不能假裝教材本來提供了一整段可調範圍。
最後 fixed_decay 真人測試中:
amplitude = 4
decay = 0.25
保持固定。
只改 Time 到:
t = 6 seconds
得到:
magnitude ≈ 0.8925
rate ≈ -0.2231
符合預期結果。
而播放、時間軸和數值更新全部都在本地 Runtime 完成。
修完之後,也沒有直接拿 regression fixture 自己宣布成功。
Phase C 又建立一批新的 holdout,專門測:
最後:
7 / 7 representation 選擇正確
其中:
6 / 7 成功編譯
這 6 個成功編譯的案例也都通過 bounded semantic check。
而且:
postcompile API delta = 0
也就是世界一旦編譯完成,之後的互動還是在本地執行。
Phase C 這一輪用了:
我也特別限制 Codex 不准一直用 API 抽卡修 deterministic bug。
Schema、Validator、Reducer、Runtime 的錯誤,應該用 deterministic test 解掉。
Day 23 Phase C 完成後:
48 個 focused tests 通過
20 個 Phase C tests 通過
最後完整 regression:
而且沒有加入:
eval
exec
這天最重要的改變,其實不是測試數量。
而是問題變了。
一開始我們問的是:
這份教材能不能產生 Learning World?
現在開始問:
這個 Learning World 有沒有真的 obey 這個知識本身的 semantic rules?
這兩個標準差很多。
今天最後整條路線比較像:
SOURCE
↕
FORMAL SEMANTIC ENTITY
↕
REPRESENTATION PLAN
↕
VALIDATED EXECUTION CONTRACT
↕
LEARNING WORLD
↕
LOCAL LEARNER ACTION
以前 Visual Learning Lab 比較像:
AI 看懂教材,再生成一張很像的東西。
現在我希望它慢慢變成:
AI 看懂教材並宣告語意,再交給可信任的 Runtime 執行。
因為如果每個 Learning World 都只是漂亮的圖,那這條路很快就會撞牆。
但如果底下開始有:
那新的教材不一定需要新的 App。
它可能只需要:
新的 semantic declaration。
Day 23 沒有做出最炫的新畫面。
但它讓我比較敢相信,前幾天一直講的 Universal Learning World Compiler,開始不只是名字而已。