CtxFST CH11 - 從 v1.0 到 v2.0:把 Semantic Graph 升級成可操作的 World Model
CtxFST CH11:從 v1.0 到 v2.0,把 Semantic Graph 升級成可操作的 World Model
到第十章為止,我們已經把 CtxFST 做成一個很強的 GraphRAG 基礎層了。
它已經能做到:
- 用
chunks把長文切成可檢索的語意單位 - 用
entities把概念節點標準化 - 用
chunks[].entities把內容和概念連起來 - 用 embedding 自動算出
SIMILAR邊 - 用 entity graph 做 graph expansion,補上純 vector search 找不到的未知領域
如果你回頭看 CH10,那一章的核心突破其實很清楚:
系統已經不只是找相似段落,而是能沿著 entity graph,把使用者原本不知道的相關概念主動拉出來。
也就是說,到 CH10 為止,我們已經解決了這個問題:
哪些概念彼此相近?哪些 chunk 可以沿著圖被重新打撈出來?
但接下來自然會冒出另一個更大的問題:
如果 graph 已經不只是拿來檢索,那 agent 能不能直接把這張圖當成「世界模型」來行動?
這就是 CH10 和 CH11 之間真正的分水嶺。
CH10的重點是:用 entity embedding graph 改善檢索與發現CH11的重點是:讓 semantic graph 變成 agent 可操作的 state space
前一章回答的是:
這些東西彼此像不像?
這一章要往前推成:
如果我現在在這個圖上的某個位置,下一步最適合做什麼?
這一套很強,但它還主要是在回答一種問題:
哪些內容彼此有關?
如果你想再往前走一步,讓 agent 不只會「找資料」,還會「在語意世界裡採取下一步行動」,那只靠 v1.x 的 semantic graph 就不夠了。
這就是第十一章要做的事:
把
CtxFST從 GraphRAG 的資料格式,升級成 agent 可以操作的 semantic world model。
更白話地說:
v1.0-v1.3比較像是「知識地圖」v2.0要開始變成「可行動的世界狀態機」
先講結論:v2.0 不是推翻,而是加一層
這章最重要的觀念只有一句:
CtxFST v2.0不是把舊的 schema 打掉重練,而是在原本的chunk + entity + graph骨架上,再加一層 world model 語意。
所以它不是:
- 把
chunks拿掉 - 把
entities改成別的結構 - 把原本的 hybrid RAG 全部作廢
而是:
- 保留
v1.x已經很穩的資料骨架 - 新增操作型節點
- 新增因果 / 前置條件 / 狀態轉移邊
- 新增
SKILL.md的 graph-aware 契約 - 新增 runtime state layer
這樣一來,同一份資料可以同時支援兩種用途:
- 舊世界:GraphRAG / Hybrid Search / Entity Graph Expansion
- 新世界:Agent Planning / Skill Selection / World State Transition
這就是 v2.0 最重要的設計原則:嚴格超集(strict superset)。
為什麼 v1.0-v1.3 還不夠?
前面的版本已經很適合做:
- chunk retrieval
- entity extraction
- similarity graph
- skill graph
- GraphRAG navigation
但如果你想讓 agent 根據當前狀態選 skill、執行 skill、再更新世界狀態,v1.x 會遇到四個明顯缺口。
Gap 1:Entity type 幾乎都是描述性的,缺少操作型節點
目前常見的 entity type 比較像這些:
skilltoollibraryframeworkplatformdatabasearchitectureprotocolconceptdomainproduct
這些 type 很適合描述世界裡「有什麼東西」,但不夠表達:
- 世界現在處於什麼狀態
- 哪些東西是 action
- 當前目標是什麼
- 誰是 agent / user
- 哪些東西是 evidence
如果要做 world model,至少還需要這些節點型別:
| type | 用途 | 例子 |
|---|---|---|
state |
世界狀態節點 | state:resume-parsed, state:user-goal-defined |
action |
可執行動作節點 | action:analyze-resume, action:career-mapping |
goal |
任務目標節點 | goal:learn-kubernetes-path |
agent |
行動者節點 | agent:ian-chou |
evidence |
證據或觀察結果 | evidence:docker-3yr-experience |
也就是說,v1.x 比較像在描述「地圖上的物件」,而 v2.0 必須開始描述「地圖上的狀態、動作與目標」。
Gap 2:邊幾乎只有 SIMILAR,但 world model 需要的是可轉移關係
在前面幾章,我們的 graph 很大一部分建立在 similarity 上:
FastAPI -> HonoDocker -> KubernetesOpenAI -> Anthropic Claude
這種邊非常適合做:
- 相似推薦
- graph expansion
- unknown unknowns discovery
但它不夠回答下面這些問題:
- 執行某個 skill 之前需要先滿足什麼?
- 做完某個 action 之後,下一步通常會走去哪?
- 某個 evidence 是否足以解鎖下一步?
- 哪條路徑是被 blocker 卡住的?
所以 v2.0 至少需要擴充到這類 relation vocabulary:
| relation | 語意 | 例子 |
|---|---|---|
SIMILAR |
語意相近 | FastAPI -> Hono |
REQUIRES |
前置條件 | Kubernetes -> Docker |
LEADS_TO |
後繼行動 / 因果 | analyze-resume -> career-mapping |
EVIDENCE |
已觀察到的證據 | agent:ian -> Docker |
IMPLIES |
邏輯蘊含 | Docker -> containerization |
COMPLETED |
執行完成 | agent:ian -> analyze-resume |
BLOCKED_BY |
被阻擋 | deploy -> missing-cert |
這裡的關鍵差別是:
SIMILAR主要是語意導航。
REQUIRES / LEADS_TO / COMPLETED才是世界狀態轉移。
Gap 3:SKILL.md 還是技能說明書,不是 graph-aware action contract
現在的 SKILL.md 比較像:
- 這個 skill 什麼時候該用
- 這個 skill 會讀哪些上下文
- 這個 skill 大概怎麼做
這已經很有用了,但對 world model 來說還少了一層:
- 它的前置條件是什麼?
- 它執行完會讓世界狀態多出什麼?
- 它在 semantic graph 上綁到哪些 nodes?
- 它跟哪些 skill 常常前後銜接?
也就是說,SKILL.md 需要從「文字技能說明」再升級成「可執行 action 的 declarative contract」。
最小版本可以長這樣:
---
name: analyze-resume
description: "Parse raw resume and extract skill evidence"
preconditions:
- "state:has-raw-resume"
- "NOT state:has-parsed-resume"
postconditions:
- "state:has-parsed-resume"
- "state:has-skill-evidence"
related_nodes:
- "entity:resume-parsing"
- "entity:skill-extraction"
related_skills:
- "career-mapping"
- "skill-gap-analysis"
cost: low
idempotent: true
---
這時候 SKILL.md 就不只是文件,而是 agent loop 裡的 action schema。
Gap 4:目前 pipeline 是靜態 export,不是 runtime state machine
這是最大的差別。
前面的 CtxFST pipeline 主要是:
.md
-> validate
-> export
-> profiles
-> entity graph
這是一個很乾淨的靜態流程,但 world model 還需要另一層:
- 當前 session state
- active states 的管理
- skill 完成紀錄
- graph mutation
- precondition checking
- goal-relevant subgraph extraction
換句話說,v1.x 主要在建「資料」,
而 v2.0 還要開始處理「執行中的世界」。
這一層不能只靠 frontmatter,本質上一定要有 runtime layer。
v2.0 的核心定義:Semantic Graph + World State + Skill Loop
如果用一句最短的話來定義 CtxFST v2.0,我會這樣寫:
CtxFST v2.0= Semantic Graph 作為外部世界結構,SKILL.md作為可執行動作集合,runtime state layer 作為世界演化記錄。
也就是這個結構:
[Semantic Graph] <-> [World State] <-> [Skill Selector] <-> [SKILL.md Execution]
如果把它畫成比較完整的架構圖,會像這樣:
flowchart LR
A[CtxFST Documents
chunks + entities + context] --> B[Semantic Graph
Entity nodes + relation edges]
B --> C[World State
goal + active states + completed skills]
C --> D[Skill Selector
precondition matching + cost ranking]
D --> E[SKILL.md Execution
run one action]
E --> F[Execution Result
evidence + status + summary]
F --> C
F --> B
G[Entity Embeddings
SIMILAR edges] --> B
H[Manual or Runtime Edges
REQUIRES / LEADS_TO / COMPLETED] --> B
I[SKILL.md YAML
preconditions + postconditions + related_nodes] --> D
I --> E
這張圖剛好也可以拿來看 CH10 和 CH11 的差別:
CH10主要做到左半邊,也就是CtxFST Documents -> Entity Embeddings -> Semantic GraphCH11要補上右半邊,也就是World State -> Skill Selector -> SKILL Execution -> Graph Update
所以兩章不是斷開的,而是很自然地前後相接:
CH10把圖建出來。
CH11開始思考 agent 怎麼在圖上移動。
每一層負責的事情不同:
1. Semantic Graph
負責存世界的相對穩定結構:
- skills
- tools
- frameworks
- goals
- states
- evidence
- relation vocabulary
這一層比較像地圖與規則骨架。
2. World State
負責記錄「這次 session 現在走到哪裡」:
- 現在的 goal
- 已激活的 state
- 已完成的 skills
- 目前最相關的子圖
這一層比較像遊戲存檔。
3. Skill Selector
負責根據當前 world state 去篩選:
- 哪些 skill 的 preconditions 已滿足
- 哪些 skill 成本較低
- 哪些 skill 會帶來更多 postconditions
這一層不一定要一開始就用 LLM,rule-based 其實就能先跑起來。
4. SKILL Execution
真正執行 action,執行後再更新世界。
例如:
- 選
analyze-resume - 執行 skill
- 產生新 evidence
- 將
state:has-parsed-resume寫回 world state - 解鎖下一批可執行 skill
這樣就形成了一個簡化版的 world model loop。
v2.0 的 schema 該怎麼升級?
最重要的是:結構不重排,只做 additive upgrade。
1. schema.json:擴充 entity types,不破壞既有形狀
entities[] 的 shape 不用改掉,還是維持:
idnametypealiases
但 type 的 enum 需要擴充:
[
"skill", "tool", "library", "framework", "platform",
"database", "architecture", "protocol", "concept",
"domain", "product",
"state", "action", "goal", "agent", "evidence"
]
除此之外,entity 可以新增 optional 欄位:
preconditions: string[]postconditions: string[]related_skills: string[]
這幾個欄位都應該是 optional,不能讓舊文件瞬間失效。
2. chunks[]:補一個 state-aware 錨點欄位
例如新增:
state_refs: string[]
用途是讓某些 chunk 能明確掛到世界狀態,例如:
- 這段內容是在描述
state:resume-parsed - 這段內容是在描述
state:goal-defined
這對之後做 state explanation 或 world-state-grounded retrieval 很有幫助。
3. entity-graph.json:從單一 similarity graph 升級成多關係 operational graph
現在的 graph 可以繼續保留,但 edge schema 應該擴充成:
{
"source": "entity:docker",
"target": "entity:kubernetes",
"relation": "REQUIRES",
"score": 0.91,
"properties": {
"timestamp": "2026-03-13T10:00:00+08:00",
"confidence": 0.92,
"result_summary": "Docker experience unlocks Kubernetes learning path",
"status": "active"
}
}
注意這裡的設計重點:
- 自動算出來的邊仍然可以是
SIMILAR - 手動或 runtime 產生的邊可以是
REQUIRES,LEADS_TO,COMPLETED properties是 optional,讓靜態邊和動態邊共存
build_entity_graph.py 應該怎麼改?
我不建議一開始就讓它自動「學因果」。
這一點非常重要。
因為 similarity 跟 causality 不是同一件事。
Docker和Kubernetes常常一起出現,不代表Docker一定是Kubernetes的唯一前置條件FastAPI和Hono很像,不代表學了FastAPI就自動導致會Hono
所以更穩的做法是:
build_entity_graph.py自動產生SIMILAR邊- 額外提供
--extra-edges參數 - 讓手動定義或上層 runtime 產生的
REQUIRES / LEADS_TO / EVIDENCE邊 merge 進同一份 graph
也就是說:
相似關係可以自動學。
因果與操作關係,先由人或上層 policy 明確定義。
這是我覺得最實際的 v2.0 路線。
為什麼我不建議一開始就自動推斷因果邊?
因為你會很快掉進「把相關當因果」的坑。
例如:
Python和SQL常一起出現,但不表示Python REQUIRES SQLReact和Next.js很接近,但也不表示React LEADS_TO Next.js- 某些 skill 常在同一份履歷裡出現,可能只是市場共現,不是學習路徑
如果這些邊一開始就由模型自己亂長,agent 在上面規劃時會非常難 debug。
所以比較好的順序是:
Phase 1
先做成「手動 + runtime 更新」版本:
SIMILAR由 embedding 自動算REQUIRES / LEADS_TO / COMPLETED / EVIDENCE由明確規則或上層 runtime 產生
Phase 2
等你累積了一批穩定邊與 execution logs,再讓系統做:
- 建議新的因果邊
- 給出 confidence
- 等人工審核後寫回 graph
這樣的半自動策略,比一開始就讓模型亂補世界規則安全得多。
SKILL.md 在 v2.0 的地位會變得非常關鍵
到了 v2.0,SKILL.md 不再只是 agent 的說明文件。
它開始變成這三件事的交集:
- Action spec
- Policy hint
- World transition contract
也就是說,SKILL.md 一方面還保留人類可讀的操作說明,另一方面又能被機器當成:
- precondition checker 的輸入
- skill selector 的候選資料
- runtime updater 的 postcondition 來源
這裡我會建議把 SKILL.md 裡的 world model 欄位定成 optional,但明確文件化。
例如:
| 欄位 | 型別 | 用途 |
|---|---|---|
preconditions |
string[] |
哪些 state entity 必須先存在 |
postconditions |
string[] |
執行後要建立或更新哪些 state |
related_nodes |
string[] |
skill 在 semantic graph 的錨點 |
related_skills |
string[] |
前後相接或互補的 skill |
cost |
low | medium | high |
給 selector 排序用 |
idempotent |
boolean |
是否安全重跑 |
這樣未來上層 orchestrator 或 agent runtime 就能很自然地做:
read current world state
-> filter skills by preconditions
-> rank by cost / expected gain
-> execute one skill
-> write back postconditions
v2.0 一定要補一個 runtime state layer
這層我認為是這次升級真正的核心。
因為沒有 runtime state,你永遠只是在看一張靜態圖,而不是一個會演化的世界。
最小可用的 world-state.json 可以長這樣:
{
"session_id": "uuid",
"goal": "goal:learn-kubernetes-path",
"active_states": [
"state:resume-parsed",
"state:has-skill-evidence"
],
"completed_skills": [
{
"skill": "analyze-resume",
"timestamp": "2026-03-13T10:00:00+08:00",
"result": "success",
"summary": "Extracted Docker and backend API evidence from resume"
}
],
"current_subgraph": {
"nodes": ["entity:docker", "entity:kubernetes"],
"edges": [
{
"source": "entity:docker",
"target": "entity:kubernetes",
"relation": "REQUIRES"
}
]
}
}
這份檔案的價值在於,它把「知識圖」和「當前任務進度」真正接起來了。
我會怎麼拆實作:library + CLI 雙模式
如果真的要做 v2.0,我很建議不要只做 shell script。
最穩的切法是:
- 底層做成 Python library
- 外層再包一層 CLI
例如:
world_state.py
提供可重用 API:
load_state()save_state()init_state()add_state()remove_state()complete_skill()check_preconditions()update_subgraph()
然後最下面再留:
if __name__ == "__main__":
main()
這樣你同時得到兩個好處:
- CLI 方便你現在直接操作與測試
- library 方便未來 agent loop 直接 import
skill_selector.py
我也不建議一開始就把 LLM 綁死在這一層。
比較好的設計是:
skill_selector.py專心做 deterministic work- 解析
SKILL.mdYAML - 做 precondition matching
- 依
cost、postconditions數量、goal proximity 做 rule-based 排序 - 輸出 candidate list JSON
然後真正要不要用 LLM 做更高階規劃,留給上層 agent loop。
這樣底層保持:
- 可測
- 可重現
- 不綁 provider
- 不把所有決策都變成黑盒
這會不會破壞原本的 GraphRAG?
這是最重要的相容性問題。
答案是:不會,只要你把 v2.0 做成嚴格超集。
也就是要守住四個原則。
原則 1:不要改掉既有欄位的語意
下面這些核心欄位都不要動:
chunks[].idchunks[].contextchunks[].contentchunks[].tagschunks[].entitiesentities[].identities[].nameentities[].type
只要這些維持原樣,現有的 hybrid RAG pipeline 就能繼續跑。
原則 2:新欄位全部 optional
像是:
preconditionspostconditionsrelated_skillsstate_refsrelated_nodescostidempotent
都應該先做成 optional。
即使 spec 文案說「推薦」,schema 也不應該立刻把它們變成 required。
原則 3:保留 SIMILAR 邊
舊的 GraphRAG 很可能只在乎:
- graph 裡有 nodes
- edges 裡有
relation == "SIMILAR"
那就讓它繼續這樣用就好。
新的 relation 只是多出來,不應該讓舊流程壞掉。
原則 4:保留 v1-style export
舊文件在完全沒有 world model 欄位的情況下,也應該能輸出跟今天幾乎一樣的:
chunks.jsonentity-profiles.jsonentity-graph.json
只要做得到這件事,v2.0 就是一次健康升級,而不是破壞性重構。
所以 v2.0 實際上要改哪些檔?
如果用工程角度來看,我會把它切成下面幾塊。
| 檔案 | 改動量 | 要做什麼 |
|---|---|---|
schema.json |
小 | entity type enum 擴充,新增 world model optional 欄位 |
ctxfst-spec.md |
中 | 規格升級到 v2.0,補 layer matrix、relation vocabulary、migration notes |
entity-graph-schema.json |
新 | 正式定義多 relation 的 graph schema |
world-state-schema.json |
新 | 定義 runtime session state 的格式 |
SKILL.md |
小 | 新增 graph-aware YAML fields 說明 |
export_to_lancedb.py |
小 | 透傳新欄位 |
build_entity_profiles.py |
小 | 讓 action/state/goal 類型進 representation text |
build_entity_graph.py |
中 | 保留自動 SIMILAR,支援 --extra-edges merge |
validate_chunks.py |
小 | 驗證新的 optional 欄位格式 |
world_state.py |
新 | runtime state manager |
skill_selector.py |
新 | skill candidate filtering 與排序 |
你會發現,真正大的新增其實只有兩塊:
world_state.pyskill_selector.py
因為這兩個檔案代表的是從「靜態資料格式」進入「執行中系統」。
一個最小可跑的 v2.0 POC 長什麼樣?
如果你只想先做最小可用版,我會建議先跑這個 loop:
Step 1:建立 graph
- document -> chunks
- chunks -> entities
- entities -> similarity graph
- 額外載入手動的
REQUIRES / LEADS_TO邊
Step 2:初始化 world state
goal: learn-kubernetes-path
active_states: [state:has-raw-resume]
completed_skills: []
Step 3:掃描 skills
讀所有 SKILL.md 的 YAML header,找出:
- preconditions 滿足的 skills
- 成本最低的 skills
- 最可能推進 goal 的 skills
Step 4:執行一個 skill
例如執行 analyze-resume。
Step 5:更新世界
把執行結果寫回:
state:resume-parsedstate:has-skill-evidenceagent:ian -> COMPLETED -> action:analyze-resumeagent:ian -> EVIDENCE -> entity:docker
Step 6:進入下一輪
這時候下一輪可用的 skill 可能就變了,例如:
career-mappingskill-gap-analysiskubernetes-path-recommendation
這樣就形成一個真正可運作的 agent planning loop。
這個升級真正改變了什麼?
如果只用一句話講:
前面的 CtxFST 主要在回答「知識怎麼組織」。
v2.0開始回答「agent 接下來該做什麼」。
這個差別非常大。
在 v1.x
你有的是:
- retrievable chunks
- normalized entities
- similarity graph
- graph-enhanced retrieval
在 v2.0
你開始多出:
- explicit state
- executable actions
- goal-conditioned planning
- runtime transitions
- session-aware graph mutation
所以它不是單純多幾個欄位,而是系統定位改變了:
從 GraphRAG document format,升級成 agent-operable semantic operating layer。
我的建議:v2.0 要先 symbolic,再逐步引入 learned world model
最後講一個最實務的建議。
很多人一看到 world model,就會很想立刻做:
- message passing 預測
- graph neural dynamics
- LLM 自動推斷因果
- 自動生成 transition graph
這些不是不能做,但不適合當第一步。
對 CtxFST 來說,比較合理的順序是:
- 先把 symbolic world model 打穩
- 先讓
state / action / goal / evidence有清楚表示 - 先讓
SKILL.md有 pre/postconditions - 先讓 runtime state manager 跑起來
- 先讓 deterministic selector 能選 skill
- 最後再用 learned model 去建議邊、預測結果、做更強規劃
這樣你的系統會有一個很大的好處:
即使沒有最花俏的模型,它也已經能工作。
而且每一層都容易 debug。
結語:v2.0 的真正意義,不是更複雜,而是更可行動
如果回頭看整個系列:
- CH1 到 CH5 比較像在建立 schema 與 chunk 基礎
- CH6 到 CH10 比較像在建立 entity graph 與 graph-enhanced retrieval
- CH11 則是第一次把問題往 agent 與 world model 推進
所以這章最重要的結論不是「多了幾個 enum」或「多了幾種 edge relation」。
真正的重點是:
CtxFST v2.0讓 semantic graph 不再只是用來檢索的知識地圖,而是變成 agent 可以在其中採取行動、更新狀態、逐步推進目標的世界模型骨架。
也因此,v2.0 的最佳設計不是推翻 v1.x,而是保留原本對 GraphRAG 很有價值的 chunk/entity/similarity 能力,再往上加一層:
- state
- action
- goal
- evidence
- preconditions
- postconditions
- runtime state
當這一層補上去之後,CtxFST 就不只是「會切 chunk 的格式」,也不只是「會建 entity graph 的格式」。
它會開始變成一個更有野心的東西:
一個讓語意圖、agent skills、world state 三者真正接起來的操作介面。
這,才是我認為 CtxFST v2.0 最值得做的地方。
📌
CtxFST v2.0的核心不是讓圖更大,而是讓圖可以被行動。
- ← Previous
CtxFST CH10 - 全端實戰:用 Entity Embedding Graph 打敗純 Vector Search,發現你筆記中的未知領域 - Next →
CtxFST CH12 - v2.0 落地:為什麼我把它重構成 World Model First