Ian Chou's Blog

CtxFST CH28 - 完整的導航比喻:Phase 1-7 各自對應什麼

CtxFST CH28:完整的導航比喻,Phase 1-7 各自是什麼

CH26 說 graph 是地圖,world state 是 GPS,planner 是導航。

但那只講了三層。整個架構有七層。如果要把比喻講完整,每一層到底對應導航系統的什麼?

這篇把它一次講清楚。然後回答一個關鍵的效率問題:如果地圖很大,系統會不會變慢?


完整對照

Phase 1-4(Graph)         = 地圖資料庫
Phase 5  (Prompt Adapter) = 車上的顯示螢幕
Phase 6  (World State)    = GPS 定位
Phase 7  (Planner)        = 導航演算法

一層一層看。


Phase 1-4:地圖資料庫

Phase 1 到 Phase 4 做的事情,就是建地圖。

四個 phase 做完,你拿到的是一包叫 ContextPack 的東西。裡面有:

ContextPack {
  matched_entities    // 查到的地點
  entity_chunks       // 地點的詳細資料
  expanded_entities   // 附近的相關地點
  graph_chunks        // 沿路展開找到的資料
  fused_chunks        // 融合排序後的結果
}

這就是地圖查詢的結果。一大包原始資料。


Phase 5:車上的顯示螢幕

地圖資料庫可能有整個城市的每一條路、每一棟建築、每一個路口。但你的車上螢幕就那麼大。

Phase 5(Prompt Adapter)做的事是:決定現在該顯示什麼,塞不下的裁掉,然後格式化成駕駛看得懂的畫面。

螢幕大小 = Token Budget

LLM 的 context window 有限。就算模型支援 200K token,你也不會想把整個地圖資料庫塞進 prompt。因為:

所以 Phase 5 有一個 token budget,就像螢幕有固定的像素數。超出 budget 的內容會被裁掉。

Priority = 什麼先顯示

不是所有資訊同等重要。Phase 5 用 priority 排序,確保最重要的先佔螢幕:

## Missing Preconditions     (priority 100)  ← 最重要:前方路斷了
## Active States             (priority 90)   ← 你目前在哪
## Relevant Entities         (priority 80)   ← 附近的重要地標
## Related Entities (Graph)  (priority 60)   ← 沿路展開的相關地點
## Supporting Chunks         (priority 50)   ← 補充資料

如果 token budget 不夠,從低 priority 開始砍。先犧牲「補充資料」,最後才犧牲「前方路斷了」。

沒有 Phase 5 會怎樣?

就像把整張城市地圖的原圖丟給駕駛。

資訊全在。但看不懂、來不及看、塞不進視野。

駕駛需要的不是完整的地圖,而是「現在這個路口該怎麼轉」。Phase 5 把地圖資料庫的查詢結果,壓縮成駕駛此刻需要的那一小塊視野。


Phase 6:GPS 定位

GPS 做一件事:告訴你,你在哪。

Phase 6(World State)也做一件事:告訴系統,這個 session 走到哪了。

WorldState {
  active_states: ["state:resume-uploaded", "state:resume-parsed"]
  completed_skills: ["upload-resume", "analyze-resume"]
  blocked_by: []
  goal: "state:report-generated"
}

四個欄位,完整描述「你在這裡」:

GPS 的特性

GPS 有幾個重要特性,Phase 6 完全對應:

即時更新。 你每走一步,GPS 座標就更新。Phase 6 也是——每次 skill 執行完成,applyPostconditions() 就更新 active_statescompleted_skills

不受地圖大小影響。 地球有多少條路,跟你的 GPS 定位速度無關。你的座標就是一個點。Phase 6 也是——不管 graph 有多少 entity,你的 session state 就是那幾十條記錄。

斷電不丟失。 GPS 掉線了重連,位置還在。Phase 6 的狀態存在 SQLite,session 關了再開,狀態還在。不像 LLM 的 context,session 一斷就全忘了。

不需要理解地圖。 GPS 不需要知道道路怎麼連接的、交通規則是什麼。它只負責「你在這裡」。Phase 6 也是——它不做 routing、不做 planning,只追蹤狀態。


Phase 7:導航演算法

導航演算法做的事是:根據你在哪(GPS)和你要去哪(目的地),在地圖上找出最佳路線,然後告訴你下一個路口怎麼轉。

Phase 7(Planner)也做同樣的事:

輸入:
  WorldState(GPS)    → 你在哪、做過什麼、被什麼卡住
  Goal(目的地)        → 你要達成什麼狀態
  Graph(地圖)         → entity 之間的 REQUIRES / LEADS_TO 關係

輸出:
  NextAction(下一個路口)→ 現在該執行哪個 skill

導航不是把所有路都列出來

Google Maps 不會告訴你「從台北到高雄有 47,832 條可能的路線」。它只告訴你最佳的 1-3 條。

Phase 7 也是。它不是列出 graph 裡所有的 skill chain,而是:

  1. Goal-aware pruning — 只看和目標相關的路徑
  2. Completed skills 降權 — 走過的路不重複推薦
  3. Blocked-aware routing — 前方封路就繞道
  4. Relation-aware weightingREQUIRESSIMILAR 重要

最後輸出一個明確的建議:「下一步做 X,因為 Y 已完成,Z 條件已滿足。」

導航會即時重算

你開過頭了,導航會重新規劃路線。不會堅持原來的路線叫你回頭。

Phase 7 也是。每次 skill 執行完、state 更新後,planner 會重新評估。如果中間出了意外(某個步驟失敗、使用者改了目標),它會根據新的 state 重新 plan。

state 更新 → planner 重算 → 新的 next action
state 更新 → planner 重算 → 新的 next action
state 更新 → planner 重算 → 新的 next action
...

這個 loop 會一直跑到 goal 被滿足,或者使用者中斷。


合在一起看

┌─────────────────────────────────────────────┐
│  Phase 7: Planner(導航演算法)              │
│  "下一個路口右轉"                            │
│  輸入:GPS + 目的地 + 地圖                    │
│  輸出:下一步指令                             │
├─────────────────────────────────────────────┤
│  Phase 6: World State(GPS)                 │
│  "你在這裡"                                   │
│  即時更新、斷電不丟、不受地圖大小影響          │
├─────────────────────────────────────────────┤
│  Phase 5: Prompt Adapter(顯示螢幕)          │
│  "現在該看這些資訊"                           │
│  Token budget = 螢幕大小,priority = 顯示順序  │
├─────────────────────────────────────────────┤
│  Phase 1-4: Graph(地圖資料庫)               │
│  "所有的路、所有的地點、所有的連接"            │
│  Parser → Index → Retrieval → Expansion       │
└─────────────────────────────────────────────┘

四層的關係:

缺任何一層,系統都不完整:


地圖很大的時候,誰會變慢?

這是一個很直覺的擔心:如果 graph 有 10,000 個 entity、50,000 條 relation,效率撐得住嗎?

答案:要看是哪一層。

Phase 6(GPS):不受影響

World state 是 session-scoped 的。不管地圖有多大,你這個 session 的狀態就是那幾十條記錄。

-- checkPreconditions 的查詢
SELECT state_key FROM world_states
WHERE session_id = 'abc'
  AND state_key IN ('state:resume-uploaded');

這是 primary key lookup。SQLite 在這種查詢上是微秒級的。就算 graph 有一百萬個 entity,這個查詢的速度完全不變。

Graph entities: 10,000    → checkPreconditions: 0.1ms
Graph entities: 100,000   → checkPreconditions: 0.1ms
Graph entities: 1,000,000 → checkPreconditions: 0.1ms

因為 checkPreconditions() 根本不碰 graph。它只碰 world_states 表,而那張表的大小只取決於你這個 session 做了多少事,和 graph 大小無關。

GPS 不會因為地球有更多道路就定位更慢。

Phase 7(導航):輕微影響,但有天然限制

Planner 做 skill chain search 時,理論上要在 graph 裡找路徑。如果全圖遍歷,確實會隨 graph 大小增長。

但實務上,planner 不會搜全圖。兩個天然限制:

Goal-scoped pruning。 Planner 只搜從「當前 active_states」到「goal」的可達路徑。大部分 entity 和你的目標無關,直接跳過。

全圖:10,000 entities
Goal-reachable subgraph:通常 < 50 entities

Relation type 過濾。 Planner 只沿 REQUIRESLEADS_TO 走。SIMILAR 和其他語意關係不參與路徑搜尋。

全部 relations:50,000
Operational relations(REQUIRES / LEADS_TO):通常 < 500

過濾完之後,planner 實際遍歷的子圖非常小。

這就像導航不會搜索地球上所有的路。它只在你附近、通往目的地方向的道路裡搜。

Phase 3-4(地圖查詢):受影響最大

Graph retrieval 是真正受地圖大小影響的層。

Query → vector search 100K chunks → top-50 → graph expand 1-hop

Vector search 的時間隨 chunk 數量增長(雖然用 ANN index 可以壓到 sublinear)。Graph expansion 的時間隨 relation 密度增長。

但這是 retrieval 的經典問題,有成熟的解法:

而且 Phase 3-4 的查詢發生在「理解問題」階段,不是「追蹤狀態」階段。查詢慢一點影響的是回應延遲,不是狀態正確性。

Phase 5(螢幕):不受影響

Phase 5 的輸入是 ContextPack,不是完整的 graph。不管 graph 有多大,ContextPack 的大小取決於 retrieval 回傳了多少結果(通常 top-50),不取決於 graph 總量。

Phase 5 只是對這 50 條結果做 priority 排序和 token budget 裁切。跟地圖大小無關。

效率總結

受地圖大小影響 原因 對使用者的影響
Phase 1-4(地圖查詢) 受影響 Vector search + graph expansion 隨資料量增長 回應延遲增加
Phase 5(螢幕) 不受 輸入是固定大小的 ContextPack
Phase 6(GPS) 不受 Session-scoped,只查自己的狀態
Phase 7(導航) 輕微 Goal-scoped pruning 大幅縮小搜索範圍 極端情況下路徑搜索稍慢

最慢的是地圖查詢(Phase 3-4),但那是 retrieval 的老問題,有成熟解法。World state 和 planner 基本不受影響。


一個完整的執行流程

把四層串起來,看一次完整的流程:

使用者問:"下一步該做什麼?"

Phase 3-4(查地圖)
  query → entity match → graph expansion
  → ContextPack(找到 5 個相關 entity、20 個 chunks)

Phase 6(查 GPS)
  db.getWorldState(sessionId)
  → active_states: [state:resume-uploaded, state:resume-parsed]
  → completed_skills: [upload-resume, analyze-resume]
  → goal: state:report-generated

Phase 5(顯示螢幕)
  ContextPack + WorldState → 結構化 prompt
  → "Resume 已解析。Generate Report 的 preconditions 已滿足。"
  → token budget 裁切,priority 排序

Phase 7(導航演算法)
  current state + goal + graph
  → Generate Report 的 preconditions 都在 active_states 裡
  → completed_skills 裡沒有 generate-report
  → 推薦:Generate Report

回應使用者:
  "下一步:Generate Report。
   原因:resume 已解析完成,這是通往 report-generated 的下一個步驟。"

四層各做各的事,合在一起就是一個完整的導航系統。


為什麼要把比喻講完整

不是因為比喻本身重要。而是因為比喻講完整之後,幾個常見的混淆就自動消失了:

「Phase 6 會不會很慢?」 — 不會。GPS 定位是 O(1),和地圖大小無關。

「Phase 5 為什麼不直接把 graph 塞進 prompt?」 — 因為螢幕就那麼大。你不會把整張城市地圖原圖丟給駕駛。

「Phase 7 需要搜全圖嗎?」 — 不需要。導航只搜你附近、通往目的地方向的路。

「沒有 Phase 6,Phase 7 能不能運作?」 — 不能。沒有 GPS 的導航不知道你在哪,就算有地圖也算不出路線。

「Phase 1-4 和 Phase 6-7 的關係是什麼?」 — 前者建地圖,後者用地圖。建地圖和用地圖是兩件不同的事,所以 graph layer 可以換(CH25),但 GPS 和導航要自己建。


收尾

一句話版本:

Phase 1-4 是地圖資料庫,Phase 5 是顯示螢幕,Phase 6 是 GPS,Phase 7 是導航演算法。地圖可以很大,但 GPS 定位永遠是一個點,導航只搜你附近的路。所以 world state 不會因為 graph 變大而變慢。

完整版本:

Phase 導航比喻 做的事 效率特性
1-4 地圖資料庫 建圖、索引、查詢、展開 隨資料量增長
5 顯示螢幕 Priority 排序、token budget 裁切 固定大小輸入
6 GPS 追蹤 session 狀態 O(1),不受地圖影響
7 導航演算法 Goal-aware routing、next action Goal-scoped,輕微影響

四層各有職責,各有效率特性,缺一不可。

這就是為什麼整個 Phase 1-7 的設計,不是七個功能的堆疊,而是一個導航系統的四個組件。每個組件解決一個不同的問題,合在一起才能讓 agent 從「有地圖」變成「會開車」。