CtxFST CH20 - Interactive Plan Critique:讓 Planner 進入真正的人機協作
CtxFST CH20:Interactive Plan Critique,讓 Planner 進入真正的人機協作
如果說:
CH18讓 planner 開始會往前看 skill chainCH19讓 planner 開始能把「為什麼這樣選」說清楚
那下一步就很自然了:
既然 planner 已經能解釋,那人類能不能直接根據這份解釋介入規劃?
這個問題一旦成立,系統的性質就會再變一次。
因為 explanation 不再只是 log,也不再只是 debug 輸出,而會開始變成:
人類與 planner 互動的介面。
這就是 CH20 要做的事情:
讓
CtxFST的 planner 不只會找 plan、解釋 plan,還能接受人類對 plan 的即時回饋。
也就是 interactive plan critique。
先講一句最短的定義
這次升級可以濃縮成一句話:
CH19的 explanation 是單向可讀;CH20的 critique 則是雙向可改。
這個差別非常大。
在 CH19 裡,人類能做的是:
- 看 plan
- 看每一步理由
- 看 alternatives
但系統仍然是:
先算好,然後你只能旁觀。
到了 CH20,這件事變成:
先算好,解釋給你聽,然後你可以說「不要這條」、「先跳過這個 skill」、「我想強制走另一條」。
這時候 planner 就真正進入 human-in-the-loop 階段了。
這一步為什麼重要?
因為很多 agent 系統做到 explanation 就停了,覺得已經很透明。
但其實對真實 workflow 來說,光透明還不夠。
人類更常需要的是:
- 我懂你為什麼選這條路,但我不想這樣走
- 這個 skill 現在先不要做
- 我知道你有別的候選,請重算
- 我想強制先執行某個 skill
也就是說,真正的人機協作不是:
AI 解釋給人聽
而是:
AI 解釋給人聽,然後人可以修正 AI 的規劃。
這才是 critique 的核心價值。
這次做的不是 execution confirmation,而是 planning-level critique
這一點很重要,必須先講清楚。
這次新增的不是「執行前按一下 yes/no」那種簡單確認框。
它做的是更高一層的事情:
在 execution 之前,先讓人類對 plan 本身提出批評、限制、偏好與重規劃要求。
所以它不是:
- 你要不要跑這個 skill?
而是:
- 你要不要接受這整條 plan?
- 你要不要排除 plan 裡某個 skill?
- 你要不要強制下一步改走某個 skill?
- 你要不要清空這些限制重新規劃?
這就是為什麼我會把它叫做 planning-level critique,而不是 execution confirmation。
critique_plan() 到底補了什麼?
核心很簡單:
critique_plan(explanation, state, skills, max_depth)讓 planner 的 explanation 變成可互動的 replanning loop。
它發生的位置也很準確:
- 不是在 executor 裡
- 不是在 skill 執行之後
- 而是在
run_loop()中,選完 plan、真正執行前
也就是說,流程變成:
find_plan_with_explanation()
-> 顯示 best plan + step traces + alternatives
-> critique_plan()
-> 接受人類指令
-> 若有需要則重新規劃
-> 最後才執行第一步
這個插入點非常合理,因為它剛好卡在:
- planner 已經把意圖算出來
- 但世界還沒真的被改掉
這是最適合人類介入的時機。
這次提供的 5 個指令,非常實用
這套 critique 介面目前有 5 個指令,我覺得切得很好。
| 指令 | 意義 | 適用情境 |
|---|---|---|
a |
Accept | 我接受這條 plan,照目前第一步執行 |
s <skill> |
Skip | 先不要某個 skill,請排除後重規劃 |
f <skill> |
Force | 我想強制某個 skill 當第一步 |
r |
Reset | 清掉目前 constraints,從頭重規劃 |
q |
Quit | 中止整個 loop |
這五個指令的好處是,它們剛好對應人類在規劃階段最常見的五種回饋:
- 同意
- 排除
- 指定
- 撤銷偏好
- 中止
也就是說,這不是任意湊出來的 CLI 指令,而是很貼近實際協作的設計。
一個很典型的使用場景
這次 feature 最好懂的地方,就是它的互動流程非常直覺。
例如 planner 先提出:
Best plan (3 steps): analyze-resume → match-skills → generate-plan
然後人類看完說:
s match-skills
系統就會回:
Replanning without match-skills...
接著如果有 alternative:
Best plan (3 steps): analyze-resume → alt-match → generate-plan
最後人類再輸入:
a
系統才真的開始執行。
這個流程很重要,因為它代表 planner 不再只是給你一條路,而是:
先提出路徑,再允許你對這條路徑做偏好修正。
這已經很像真正的協作式規劃了。
為什麼 skip 很重要?
我覺得這五個指令裡,skip 特別有代表性。
因為它其實在表達一種很真實的人類需求:
我不是不知道你這條路合理,但我現在就是不想走這個 skill。
可能的原因很多:
- 這個 skill 太貴
- 這個 skill 需要外部資源,現在拿不到
- 這個 skill 使用者不喜歡
- 這個 skill 在商業規則上暫時禁用
而 skip 的價值就在於:
人類不用自己重新規劃整條路,只要指出「這個不行」,planner 就應該幫你重算。
這正是人機協作最理想的分工方式。
force 更進一步:不是只排除,而是注入偏好
如果說 skip 是負向約束,那 force 就是正向約束。
它表示:
我知道你有自己的排序,但我想指定下一步先走這個 skill。
當然,這個強制不是無條件亂來。
它仍然會先檢查:
- 這個 skill 是否存在
- preconditions 是否滿足
如果不滿足,就不能硬塞進 plan。
這個設計很重要,因為它保留了兩件事的平衡:
- 人類偏好
- world model 的合法性
也就是說,系統不是「人說什麼都照做」,而是:
人在合法行動空間內,可以對規劃加偏好。
這是比較健康的 human-in-the-loop 設計。
reset 的價值:讓 critique 不會把 planner 越帶越歪
當使用者做了一連串:
- skip
- force
- 再 skip
之後,規劃空間會逐漸被各種 constraint 壓縮。
這時如果沒有 reset,整個互動很容易變得僵硬,甚至讓使用者忘了自己到底加了哪些限制。
所以 reset 的存在很重要。
它代表:
人類不只可以改 plan,還可以撤回自己對 plan 的干預。
這讓 critique 介面不只是「越修越多限制」,而是有真正的可逆性。
這種設計對長一點的互動非常重要。
q / EOF / Ctrl-C 也被正式納入語意,這很好
這次我很喜歡的一點是,quit 不是被當成例外錯誤處理,而是被當成:
合法的 loop termination reason
也就是:
terminated_reason = "user_aborted"
這很合理,因為在真實人機協作裡,「人決定先停下來」本來就是一種正常結果,不應該被當成 crash。
這樣的設計會讓之後的 logs、測試、甚至 UI 都更一致。
這次和 CH19 的 explanation 是怎麼接起來的?
CH19 的 explanation 是這次 critique 能成立的前提。
因為如果 planner 只能吐出:
Best plan: s1 -> s2 -> s3
那人類很難真正做出有根據的 critique。
但現在有了:
- best plan summary
- step traces
- alternatives
- relation-specific reasoning
人類終於可以有根據地說:
- 不要這個 step
- 先強制另一個 skill
- 這條 alternative 其實更符合我的偏好
所以這次並不是在 CH19 上多加一個 CLI,而是:
把 explanation 從被動資訊,升級成主動互動界面。
這就是兩章之間最重要的承接。
agent_loop.py --critique 的意義:讓互動式規劃變成正式 runtime 模式
如果這次只是 library 裡多一個 critique_plan(),價值還有限。
真正讓它變成系統能力的,是:
run_loop(..., critique=True)--critique
而且這裡還有一個很好的 UX 決策:
如果使用者指定
--critique但沒指定--lookahead,系統會自動開啟lookahead=5。
這很合理,因為 critique 本來就是針對 plan,而不是針對單一步驟的。
沒有 lookahead,critique 的價值會大幅下降。
這種「合理預設」很值得保留。
這次的 14 個新測試,讓 critique 不是 demo,而是 contract
這次最重要的工程點之一,是 interactive critique 也不是裸上。
你用 unittest.mock.patch('builtins.input') 把整套 CLI 互動測起來,這非常關鍵。
新增後總測試數來到:
66 tests, all green
而這 14 個新測試,保護的不是小細節,而是整個互動 contract:
- accept
- quit
- EOF
- skip + replan
- skip unknown
- reset
- force valid
- force unmet preconditions
- force unknown
- invalid command
- critique vs non-critique outcome consistency
- run_loop 在 critique 模式下的 goal reached / user aborted / alt route 行為
這意味著:
人機互動這層現在也正式成為 runtime contract 的一部分。
這很重要,因為很多系統的 interactive mode 都只是 demo 層,沒有測試保護,一改就壞。
現在這層有測試了,後面比較敢持續演化。
這一步代表 CtxFST 的 planner 又變成什麼了?
如果用前幾章來對照:
CH15:selector 有方向感CH17:selector 尊重 relation 語意CH18:planner 開始搜尋 skill chainCH19:planner 開始解釋 skill chainCH20:人類開始能直接修改 planner 的下一步選擇
這一步很關鍵,因為它把 planner 從:
- explainable planner
推進成:
- collaborative planner
這已經不是單純的 agent runtime 了,而是更接近:
一個人類可以和它共同編輯決策方向的 planning substrate。
這還不是最終型,但已經很接近真正的協作介面
現在這版 critique 還是 CLI 形式,但已經很有味道了。
未來很自然可以延伸成:
1. UI 化的人機規劃面板
把:
- best plan
- step traces
- alternatives
- skip / force / reset
變成真正可點擊的介面。
2. 偏好持久化
目前 critique 是單次互動。
未來可以把使用者偏好存成:
- 避免某些 skills
- 偏好低成本
- 偏好某類 relation path
讓 planner 之後自動學會這些偏好。
3. LLM-assisted critique
現在完全不需要 LLM,這很好。
但未來可以再往上加一層:
- 人類用自然語言描述偏好
- 系統把它轉成 skip / force / weight adjustments
這樣互動門檻會更低。
結語:從這一章開始,Planner 不只可解釋,還可被協作地修正
如果 CH19 的核心是:
planner 開始能把理由講清楚
那 CH20 的核心就是:
planner 現在開始允許人類根據這些理由,直接改變規劃。
這是非常重要的升級。
因為從這裡開始,CtxFST 的 planner 不再只是:
- 會找路
- 會解釋
- 會輸出 alternatives
它還開始具備另一個真正面向實務的能力:
在不破壞 world model 合法性的前提下,接受人類對規劃的即時偏好修正。
這就讓系統從「可觀察的 planner」正式跨進「可協作的 planner」。
而這,正是 human-in-the-loop 真正開始成立的地方。
參考實作
📌
CH19讓 planner 會解釋,CH20則讓人類可以利用這份解釋直接修正 plan,真正把 explanation 變成協作介面。