Day 15 做完 Interactive Learning Lab 之後,我第一次覺得 Visual Learning Lab 不只是「解釋教材」。
像正弦波 v(t)=V_m cos(ωt+θ),AI 不只會告訴我 V_m 是振幅、ω 是角頻率、θ 是相位,而是會直接替這份教材生成 slider、即時曲線和衍生數值。改變參數之後,圖形在本機重新計算,不需要一直呼叫 OpenAI API。
後來我又拿阻尼振動、拋體運動、Logistic Growth 去測,確認它不是只會做正弦波。
但 Day 15 做完之後,我馬上又開始嫌棄自己的功能。
因為有些東西雖然可以「拉參數看圖」,但它本質上其實是一個會隨時間變化的系統。
拋體不是一條拋物線而已。
它應該真的從地面飛出去、上升、到最高點,再掉回來。
阻尼振動也不是一張 x(t) 曲線而已。
它應該真的震,而且每一次震盪的幅度都比前一次小。
所以 Day 16 我決定把昨天的 Interactive Lab 再往前推一步。
這次的目標叫做:
Dynamic Simulation Studio。
昨天的 Interactive Lab 大概是:
教材中的數學模型
↓
AI 產生安全的實驗規格
↓
Slider
↓
重新計算
↓
即時圖形
Day 16 則在這條線上再多一層:
Interactive Lab
↓
建立動態模擬
↓
AI 產生 declarative simulation spec
↓
本機產生所有時間 frame
↓
Play / Pause / Replay
↓
時間軸可以拖動
這裡我還是維持 Day 15 的原則:
AI 不可以產生一段 Python 然後直接執行。
它只能描述這個模擬應該有哪些物件、這些物件的位置怎麼由公式計算、時間範圍是多少,以及哪些數值值得顯示。
真正的數學運算還是走昨天做好的 restricted expression evaluator。
也就是說,AI 負責「描述模擬」,程式負責「安全地算出模擬」。
這次我最不想做出的東西,就是一個「看起來會動,但其實一直在燒 API」的動畫。
所以建立 Dynamic Simulation 時,可以有一次額外的 API request,讓模型根據目前教材和 Interactive Lab 產生 simulation specification。
但是一旦 spec 建好之後,Play、Pause、Replay、拖時間軸、改播放速度,以及每一幀的位置計算,全部都在本機完成。
目前大概是這樣:
| 操作 | OpenAI API |
|---|---|
| 第一次分析教材 | 是 |
| 建立 Interactive Lab | 跟主分析一起 |
| 建立 Dynamic Simulation | 是,使用者主動觸發 |
| Play / Pause | 否 |
| Replay | 否 |
| 拖時間軸 | 否 |
| 改播放速度 | 否 |
| 改參數後重新計算動畫 | 否 |
這一點對我來說滿重要。
因為如果播放一段 120 frame 的動畫,就送 120 次 request,那這功能基本上不能用。
現在 AI 只負責「把規則建立起來」,後面時間怎麼跑,是程式自己的事情。
第一個還是先拿最熟悉的 Sinusoids 測。
這次建立的動態模擬會沿著時間軸計算目前的 v(t),畫面上可以看到目前位置、已經走過的軌跡,以及完整路徑。

上面還會同步顯示像目前電壓、目前相位角、週期 T=2π/ω 這些 frame-specific metrics。
比較有感的是時間軸。
以前我如果想知道「現在走到哪個 phase」,其實只能自己看公式。
現在直接拖 timeline,就可以跳到某個時間點,看當下波形的位置。
播放之後,也不是 Streamlit 每一幀重新 rerun 整個 app,而是把 frame data 先準備好,再交給 Plotly 在瀏覽器端播放。
這樣至少不會發生:
播一下 → 整頁閃一下 → 再播一下 → 整頁再閃一下
這種很可怕的動畫。
接著我拿昨天已經測成功的阻尼振動:
x(t)=Ae^{-bt}cos(ωt+φ)
這一題其實是我 Day 16 最想看的。
因為 Day 15 已經可以畫出它的曲線,但「振幅逐漸衰減」這件事,本來就很適合用時間看。

這次畫面上會看到現在的位置沿著 x(t) 前進,而上下兩條 envelope 也會一起顯示出 Ae^{-bt} 的衰減範圍。
所以一眼就可以看出:
一開始振幅很大,之後逐漸縮小,最後靠近 0。
而且這個畫面還是跟 Day 15 的參數 state 接在一起。
例如把阻尼係數 b 拉大之後,不需要重新叫 AI 建一個模擬。
程式直接用同一份 simulation spec 和新的 b 值,把 frame 重新算一次。
我實際測試時,b 拉大之後振動真的會更快衰減;改 ω 則會改變震盪速度。
這就開始有一點「模擬器」的感覺了。
不是 AI 告訴我:
阻尼係數越大,振幅衰減越快。
而是我真的可以把它拉大,看它更快停下來。
接著是我覺得這次最適合 Demo 的拋體運動。
Day 15 已經可以根據:
y(x)=x tanθ - gx²/(2v₀² cos²θ)
自動做出 v₀、θ、g 三個 slider,再畫出一條 trajectory。
但那時候它還是一張圖。
Day 16 之後,模型可以把同一個物理關係轉成時間形式:
x(t)=v₀ cosθ · t
y(t)=v₀ sinθ · t - 1/2 gt²
然後真的讓一個點沿著軌跡移動。

畫面上可以同時看到完整的理論軌跡、目前已經走過的位置,以及移動中的 projectile。
上方還有目前水平位置、目前高度、理論飛行時間、理論最大高度。
這次我甚至把播放速度切到 2× 測試。
Play、Pause、Replay、時間軸都正常。
最重要的是,我再回去改 v₀ 或 θ,整條軌跡和動畫也會跟著重新計算,而且沒有再次出現 AI spinner。
這就證明參數修改真的留在本地。
一開始想到 Day 16 時,其實很容易掉進一個坑:
我要做動畫,因為動畫很炫。
但真的做完幾個例子之後,我覺得比較合理的標準應該是:
這個概念如果動起來,會不會更容易理解?
阻尼振動會。
拋體運動會。
相位隨時間前進也會。
但不是每一個公式都需要硬做成動畫。
例如我後來又拿 Laplace Transform 的常數函數 F(s)=c/s 去跑。
系統可以做 Interactive Lab,讓我改變 c,觀察 F(s) 的形狀如何改變。

但這種內容如果硬要做成「一個點在 c/s 曲線上跑來跑去」,學習價值其實沒有比較高。
所以我反而希望未來系統更敢回答:
這個內容適合互動圖,但不需要動畫。
而不是看到 formula 就一定要放一個 Play 按鈕。
這也是我現在越來越在意的事情:
Visual Learning Lab 不應該追求「每份教材都生成最多功能」。
而是要選擇:
哪一種呈現方式最適合這份內容。
Day 16 有一個地方我滿慶幸昨天有先做好,就是 safe expression evaluator。
如果 Day 15 是直接用 eval() 執行 AI 回傳的數學式,今天要開始生成動態 scene,風險只會變得更大。
現在 simulation 裡面的物件只能使用有限的 primitive,例如 moving point、vector、line、trajectory 等,再搭配經過白名單驗證的 mathematical expression。
模型不能突然回傳:
open(...)
或 __import__(...)
然後期待程式去執行。
它能做的事情被限制在:
用已經宣告的參數和時間變數,計算這個物件現在應該在哪裡。
我覺得這個架構慢慢變得滿有意思。
現在 AI 其實不是在「寫程式」。
它更像是在寫一份很受限制的 learning scene description。
而 Visual Learning Lab 是負責解讀這份描述的 runtime。
Day 16 另一個比較工程面的重點,是我不希望 Day 15 Interactive Lab 和 Day 16 Simulation 各自有一組完全獨立的參數。
假設 Interactive Lab 裡現在是:
A=1
b=0.25
ω=3.4
φ=-1.44
那打開阻尼振動動畫時,就應該用同一組數字。
不應該進到動畫後,又突然變回另一套 default。
所以兩邊現在盡量共用同一份 parameter state。
這個改動看起來不像新功能,但實際使用差很多。
因為「Interactive Lab」和「Dynamic Simulation」開始不像兩個工具,而比較像同一份模型的兩種觀察方式:
一個適合看整體曲線。
一個適合看時間如何推進。
做到 Day 15 時,t 對系統來說其實只是 x 軸。
Day 16 做完之後,t 第一次真的變成「時間」。
現在可以按 Play。
可以 Pause。
可以把時間拉回去。
可以看某一刻的位置。
可以把播放速度改成 2×。
也可以觀察同一組參數在不同時間到底發生什麼。
所以如果要用一句話講 Day 15 和 Day 16 的差別,我會寫:
Day 15 讓我改變模型。
Day 16 讓我看著模型活起來。
現在 Visual Learning Lab 對數學教材的處理大概已經變成:
看到公式
↓
理解參數與關係
↓
產生 Interactive Lab
↓
安全地操作參數
↓
如果時間演化有學習價值
↓
產生 Dynamic Simulation
↓
本機播放、暫停、拖曳、重新計算
我滿喜歡這條線的一個原因是:
AI 最重要的工作其實停在前面。
它負責理解教材、判斷什麼值得呈現、建立安全的結構。
真正開始操作之後,使用者不用一直跟模型來回對話。
拉 slider 不需要問 AI。
播放動畫不需要問 AI。
時間往前走也不需要問 AI。
這些互動都只是數學模型自己在運作。
做到 Day 16,我覺得 Visual Learning Lab 已經越來越不像「把 GPT 包在 Streamlit 裡」。
它現在開始有自己的 runtime、自己的資料結構、自己的學習流程,也開始能把 AI 的理解轉換成真正可以操作的東西。
昨天我寫:
AI 開始替教材做工具。
今天可以再往前一句:
它開始替教材做模擬器。
GitHub Repo:
https://github.com/f24131128-lgtm/visual-learning-lab