Day 18 做完 Spatial Learning World 之後,Visual Learning Lab 已經可以把一份教材變成波形、向量、模擬,甚至一個可以操作的物理場景。不過功能越做越多,我開始遇到另一個問題:畫面上這些 AI 生成出來的東西,到底是從原教材哪裡來的?
以前我已經有 Source Check,也能保留 PDF 頁碼。例如系統可以告訴我「這個概念來自第 3 頁」,但如果第 3 頁本身有兩張圖、三個公式、一堆標籤和箭頭,只有頁碼其實還是不夠。
所以 Day 19 我把這件事再往下做一層。
今天新增的是 Source Atlas:讓系統不只知道「來自哪一頁」,還開始知道「來自原始 PDF 的哪一個區域」,而且這些來源區域可以和 Day 17、Day 18 已經存在的 Semantic Scene 互相連結。
今天最後真的測到的結果是:
我在生成的 Spatial Learning World 選到「重力向量 g」,左邊的原始 PDF 真的會回到對應頁面,並把教材裡的 Gravity g 箭頭框起來。
這也是我第一次覺得 Visual Learning Lab 的 Source Check 開始不只是附上一個頁碼,而是真的能回答「你這個東西到底是從教材哪裡來的」。
GitHub Repo:
https://github.com/f24131128-lgtm/visual-learning-lab
Day 6 做 Visual Evidence 時,系統其實已經能看 PDF 裡面的公式、波形、表格和圖。不過當時保存的資訊比較像:
「第 3 頁有一張正弦波相位差比較圖。」
今天想做的事情更細。
現在建立 Source Atlas 後,系統會嘗試在原始 PDF 上建立一些有學習意義的來源區域,例如公式、圖表、向量、標籤、解題步驟或一整個 diagram。
而且我不想重新畫一張假的教材給使用者看,Source Atlas 的底還是使用原始 PDF 頁面,只是在上面加上可以被選取與追溯的區域。

我先拿之前一直在測的 Sinusoids 教材試。
原本這張圖裡同時有兩條波形、相位差 φ、v₁ = Vₘ sinωt、v₂ = Vₘ sin(ωt+φ) 和振幅標記。建立 Source Atlas 後,系統可以把這些內容拆成不同的來源物件,我切換來源物件時,原始頁面也會跟著框出對應區域。
這裡我一開始其實有點疑惑:如果只是「PDF 上多幾個框框」,那這個功能到底有什麼用?
後來真正把它和 Learning World 接起來後,答案才比較清楚。
Day 19 想建立的其實不是一個 PDF annotation tool。
它真正的資料流比較像:
Original PDF Region
↕
Semantic Concept / Quantity
↕
Learning Scene / Spatial Learning World
原始教材上的一支箭頭,不只是座標 (x1, y1, x2, y2)。
它應該還要知道:
「這是 velocity。」
而 Spatial Learning World 裡的一支生成向量也知道:
「我代表 velocity。」
只要兩邊共用同一層 semantic identity,就有機會把原教材和生成場景接起來。
這也是今天最關鍵的部分。
Sinusoid 可以證明 Source Atlas 有辦法做區域定位,但我還想測一個更直接的案例,所以另外準備了一份拋體運動教材。
裡面刻意放了:
這樣我可以很明確地知道,如果系統真的懂來源與場景之間的關係,它應該要知道哪支箭頭是哪支箭頭,而不是只知道「這頁在講拋體」。

右邊是 Day 18 做出的 Spatial Learning World,左邊則是 Day 19 的 Source Atlas。
兩邊現在不再是完全獨立的功能。
例如 Spatial World 裡有 trajectory、velocity vector、gravity vector 和目前位置,原教材裡也有相對應的視覺元素。這次的測試就是要確認它們能不能真的指向彼此。
我後來在 Spatial Learning World 裡選擇 重力向量 g。
結果左邊 Source Atlas 自動回到原始 PDF 的拋體示意圖,而且直接把 Gravity g 那支向下箭頭框出來。

這張是我今天最想留下來的測試結果。
因為它證明的已經不是:
「AI 知道這份 PDF 在講拋體。」
甚至也不只是:
「AI 知道重力在第 1 頁。」
而是開始做到:
「右邊這個生成出來的 gravity vector,對應的是左邊原始教材裡的這支箭頭。」
這就是我想做的 generated object ↔ original source region。
而且這套機制不是為拋體寫死的。Day 19 的 runtime 是接 Day 17、Day 18 原本的 semantic scene,所以 Sinusoid、Probability、Spatial Dynamics 都是在同一套 Source Atlas 架構底下處理。
除了乾淨的教材,我還拿之前的手寫機率筆記測了一次。
其中一頁是在算抽球問題,而且同一題寫了 ordered counting 和 combination 兩種做法。
Source Atlas 可以先抓出整個解題範圍,再把其中不同的解題區域分開。

這個例子也提醒我一件很重要的事情:來源定位不能假裝自己永遠非常精準。
乾淨的電子講義裡,一條公式可能很好框;但手寫筆記、複雜 diagram、歪掉的標記就不一定。
所以 Day 19 沒有把 bounding box 當成絕對真相,而是保留 grounding confidence。
如果系統對某個區域只有很低的信心,我寧願讓它只保留「這個概念來自這一頁」,也不要硬生出一個看起來非常準、實際上卻框錯地方的小方框。
目前畫面下方也直接寫著:
區域為估計定位,請核對原頁
我覺得這句其實很重要。
今天另外做了一層 Formula Trace。
假設原教材裡有:
v₂ = Vₘ sin(ωt + φ)
Source Atlas 不只知道「這裡有一條公式」,還可以保存這條公式和 Semantic Model 裡 quantity 的關係,例如:
v₂
Vₘ
ω
t
φ
這代表未來我從原教材選 φ,不一定只能得到文字解釋。
它可以繼續往下接到:
也就是原教材本身有機會變成 Visual Learning Lab 的操作入口。
教材通常不會把一個概念只講一次。
可能第 2 頁先定義 Phase Difference,第 3 頁出現公式,第 4 頁才用 Phasor Diagram 解釋。
所以今天也開始加入 cross-page source trace。
這比我以前直接列:
來源:p2、p3、p4
更有意義。
理想上應該能變成:
p2 → 定義
p3 → 方程式
p4 → 圖像表示
使用者可以從同一個 semantic concept 往回看教材裡不同形式的證據,而不是自己在七八頁 PDF 裡來回找。
Source Atlas 本身目前也有一些比較偏學習介面的功能。
例如可以:
「隱藏標籤」這個功能我覺得之後很有發展空間。
像 Sinusoid 圖裡可以先把 φ 隱藏,問學生:
「這兩條波之間的水平差代表什麼?」
或者拋體圖先把 Velocity / Gravity 標籤拿掉,讓使用者自己判斷箭頭代表什麼,再揭示答案。
這樣以後 Quiz 甚至不一定都要由 AI 重新生成文字題,原本教材的圖本身就可以直接變成練習。
Day 19 一樣是先由 ChatGPT 把產品目標、資料結構、安全限制和 acceptance cases 拆出來,再交給 Codex 做大量 repo-level implementation。
這次比較麻煩的地方是,Source Atlas 不能自己活在一個新頁面裡就算完成。
它必須同時接:
Day 6 的 Visual Evidence、Day 17 的 Learning Scene、Day 18 的 Spatial Learning World、session state、cache、semantic focus 和原本的 PDF page references。
整合完成時,Codex 的結果是:
Day 19 focused tests:30 passed
完整 regression suite:173 passed
沒有新增 dependency。
而且每次明確建立 Source Atlas 時,最多只處理少量相關頁面,目前限制在最多 3 頁,避免每次都把整份 PDF 丟給模型。建立完成後,切來源、縮放、切換圖層、選 semantic object 等操作都在本地完成,不需要每按一下就重新呼叫 OpenAI。
今天 live-test 的第一輪其實直接 crash。
錯誤是:
AttributeError: 'NoneType' object has no attribute 'get'
原因沒有很高深。
Source Atlas runtime 原本假設使用者已經建立 Learning Scene,所以程式會直接去讀 scene.get(...)。
但真實使用者完全有可能:
上傳 PDF
→ 分析
→ 先建立 Source Atlas
→ 還沒建立 Learning Scene
這時 scene = None 本身是合法狀態,但程式沒有處理。
所以今天又出現一次我這幾天很常遇到的情況:
tests 全過,不代表實際使用流程沒有問題。
修掉之後,Source Atlas 在沒有 Scene 的情況下也應該可以自己顯示,只是暫時沒有 bidirectional scene linking,而不是整個功能一起死亡。
之後我才重新拿拋體 PDF 做完整 live-test,最後成功看到 Spatial World 的 gravity object 回頭定位到原始教材裡真正的 Gravity g 箭頭。
對我來說,這個真人驗收比「173 passed」更重要。
雖然 bidirectional source linking 已經真的跑通,但現在的操作方式還沒有完全到我理想中的樣子。
目前比較像:
選擇一個 semantic object
→ Source Atlas 幫我定位原教材區域
而我最後想要的是更直接:
我直接點 PDF 裡的某一支箭頭
→ Spatial World 立刻知道我點的是 velocity
→ waveform / equation / vector view 一起切到相同 focus
→ 旁邊直接出現「從這裡開始學」
例如點教材上的 φ,就直接打開相位差的互動解釋。
點 Gravity g,就直接讓我操作重力加速度,看 trajectory 怎麼改變。
點一條公式,就直接進 Formula Trace 或對應的 Interactive Lab。
現在 Day 19 已經把最底下的 grounding layer 接起來了,但這個「原教材直接變成操作介面」的 UX 還可以再往前做很多。
這部分我不想因為今天有框、有雙向定位,就假裝已經全部完成。
以前我想的 Source Check 比較像引用來源。
AI 說一句話,旁邊告訴你:
Source: p.3
現在我開始覺得這還不夠。
真正有用的 Source Check 應該要讓使用者可以一直追問:
「你這個東西是哪裡來的?」
一個生成出來的 velocity vector,點下去可以回到教材裡真正的箭頭。
一個 phase parameter,可以回到原公式裡的 φ。
一個 Probability Scene 裡的 event,可以回到手寫筆記裡的那段解法。
反過來,教材裡的東西也應該能把我帶進系統裡繼續學。
所以做到 Day 19,Visual Learning Lab 的資料流開始不像以前單向的:
PDF
↓
AI
↓
Visualization
而比較像:
Original Material
↕
Semantic Model
↕
Interactive Learning World
原始教材不再只是輸入檔案。
它開始成為整個 Learning World 的來源、證據,以及之後可以被操作的入口。
這就是 Day 19 真正完成的東西。