Ian Chou's Blog

CtxFST CH16 - 先別急著變聰明:為 Agent Loop 補上 End-to-End Test Suite

CtxFST CH16:先別急著變聰明,先為 Agent Loop 補上 End-to-End Test Suite

到了 CH15CtxFST 的 world model runtime 已經長得很像一個真正的 agent system 了。

它已經有:

這時候很自然會想繼續往前衝,例如:

但如果這時候直接衝下去,會有一個很大的風險:

你開始讓系統變聰明了,卻沒有一個穩定的方法知道它是不是還正常。

所以這一章的主題很簡單,也很工程:

在繼續升級 planner 之前,先把 agent loop 的 end-to-end 測試基線補起來。


為什麼這一章很重要?

因為前面幾章雖然已經把 runtime 組件都做出來了,但在沒有完整測試之前,你真正擁有的還比較像是:

這還不算真正穩。

尤其到了 CH15 之後,selector 已經不再只是簡單排序器,而是會開始參考:

這時候如果沒有自動化測試,你後面每改一次 routing,都很難確定自己到底是:

所以這章最核心的觀點就是:

在 world model runtime 裡,test suite 不是附屬品,而是後續演化的基線。


這不是單元測試而已,而是 runtime contract 測試

這次新增的 tests/test_agent_loop.py 值得強調的一點是,它不是只在測某個函式有沒有回傳對的值。

它更像是在測:

整個 agent loop 的行為契約有沒有成立。

也就是說,它測的是這些更大的問題:

這些都不是單一模組能回答的問題,而是整個 runtime 協作之後才會出現的行為。

所以我會把這份測試定義成:

CtxFST world model runtime 的第一套 end-to-end contract tests。


為什麼現在補測試,比 relation-aware routing 更急?

因為 relation-aware routing 當然很值得做,但在那之前你需要一個可靠的 baseline,否則你連「變更是否讓系統更好」都很難判斷。

目前的狀況其實很清楚:

已經有的

還缺的

這裡的優先順序其實很工程常識:

  1. 先把今天的行為固定下來
  2. 再去做更複雜的 routing 升級

不然你會進入一種很危險的狀態:

所以這章不是保守,而是為後續加速鋪路。


這次測試到底覆蓋了什麼?

這份測試總共 20 個 cases,涵蓋 7 類核心行為。

1. Happy Path:三步 skill chain 正常到達 goal

這是最基本但也最重要的情境。

測試驗證:

這組測試其實就是整個 world model loop 的黃金路徑。

如果這裡壞掉,後面一切都不用談了。

2. Goal Already Satisfied:一開始就已達成目標

這個 case 很容易被忽略,但很重要。

如果 goal 一開始就存在於 active_states,那 loop 應該:

這其實是在測 runtime 有沒有最基本的停止條件意識。

3. No Candidates:沒有任何可執行技能

這裡測兩種情況:

兩種情況都應該正確回傳:

這很重要,因為真實系統裡常常不是失敗,而是 simply 沒路可走。

4. Stop On Failure / Continue On Failure

這一組在測 loop 對執行失敗的行為策略。

stop_on_failure=True

預設情況下:

stop_on_failure=False

在允許繼續的模式下:

這組測試很有價值,因為它證明 loop 已經不只是「一路成功才算成立」,而是已經開始具備 workflow 容錯語意。

5. Max Iterations:避免無限迴圈

world model runtime 最怕的其中一件事,就是 agent 一直重做同一件事。

這組測試驗證:

這是在保護 runtime,不讓它變成卡住的 state machine。

6. Graph Writeback:COMPLETED edge 是否正確寫回

這組測試非常關鍵,因為它直接對應 CH14 的核心主張。

測試驗證:

這代表測試不只在驗 state,也在驗 graph execution trace。

7. Goal Proximity Routing 與 Idempotent Re-execution

這兩組測試是在保護最新加入的 planner 行為。

Goal proximity

驗證:

這是在保護 CH15 的成果。

Idempotent / non-idempotent

驗證:

這是在保護整個 selector 的行為契約。


20/20 全數通過,代表了什麼?

這次測試結果很乾淨:

20/20 passed

這個數字本身當然很好看,但我覺得更重要的是它代表了三件事。

1. 目前的 runtime backbone 已經相當穩

因為這次測的不是單點,而是跨三個檔案:

如果這三層之間的 contract 有任何明顯錯位,通常不太可能一次 20 個都過。

2. goal-aware routing 現在有 baseline 保護了

這點很重要。

因為在 CH15 之後,selector 的排序規則已經變得更聰明,也更複雜。

現在有了專門的測試 case 去保護:

這代表你接下來要做 relation-aware routing 時,不再是裸改。

3. CtxFST 的 agent loop 已經不再只是 demo

這是我最在意的一點。

當一個系統只有 demo,沒有測試時,它還比較像 concept proof。

但當它開始有:

它就開始比較像一個真的 runtime 了。


這份測試最有價值的地方,不是 coverage,而是 confidence

我覺得要特別說明的是,這份測試最有價值的地方,不只是數量,也不是單純追求 coverage 百分比。

真正的價值是:

它讓你之後敢改 selector、敢改 executor、敢改 graph writeback。

沒有這份測試時,每一次重構都很像在拆炸彈。

有了這份測試後,你至少知道:

這種 confidence,對後續架構演進比任何單次 feature 都重要。


這也讓下一步變得更清楚

有了這套測試之後,接下來最自然的兩步其實就很明確了。

1. Relation-Aware Routing

目前 _goal_hop_distances() 還是把所有 edge 當等權重處理。

下一步可以開始區分:

例如:

這樣 selector 會更像在走因果路徑,而不是語意近鄰。

2. Multi-Step Planning

目前 selector 仍然是 greedy single-step。

未來可以升級成:

這會讓系統從 router 進一步長成真正的 lookahead planner。

但重點是,現在你終於可以在有測試保護的情況下做這些事。


這章真正完成了什麼?

如果用一句話總結:

CH16 不是讓 CtxFST 變得更聰明,而是讓它開始變得更可靠。

這句話非常重要。

因為很多系統死得不是因為功能太少,而是因為每往前加一層功能,就把前一層默默弄壞。

所以這章的價值不在於又多了一個 feature,而在於:

CtxFST world model runtime 第一次有了正式的 regression safety net。

這會直接改變整個專案接下來的演化速度與穩定性。


結語:會跑很重要,但可驗證才是真的開始成熟

如果回頭看這幾章的節奏:

所以這章最重要的結論,不是「多了 20 個 tests」。

真正的結論是:

從這一章開始,CtxFST 的 agent loop 不再只是可運作的原型,而是開始有條件成為可持續演化的 runtime。

因為一個世界模型真正成熟的前提,不只是它能表示世界、能改變世界、能朝目標前進。

還要包括:

你能穩定地驗證,它今天仍然按照你以為的方式運作。

這,就是 CH16 的意義。


參考實作


📌 CH13 讓 loop 跑起來,CH14 讓 loop 寫回世界,CH15 讓 loop 朝 goal 前進,而 CH16 則讓這一切開始變得可驗證、可維護、可演進。