1 1 2 3 5 8 13 21 34 55 89 144 φ ≈ 1.6180339887… fibon design log EN fibon design log EN ✕ 章節目錄 時事筆記 實驗與解析 01 為什麼我要自己打造一個 AI 助理? 02 一個 AI 不夠用嗎? 03 AI 怎麼「記得」你? 04 為什麼 LLM 的自信,不能當成證據? * 一個讓我直搖頭的小例子 * LLM 天生無法根除的三個底層缺陷 * 五種寫進維運日誌的真實失敗方式 * 築起防禦的地基 —— 四大工具來源的「統一註冊表」 * 突破大選項雜訊 —— 工具挑選的 Stratified RAG 算法 * 思辨 —— 為什麼不把複雜任務拆成多輪 ReAct 處理就好? * 視覺化維運 —— 技能註冊與 MCP 自動探索的真實流程 * 三層技能合規稽核架構:用程式碼當場抓違規 * 抓到 AI 違規後,如何優雅地三階溫和介入? * 這層工作的真正核心價值 * 白箱誠實 —— 還沒做完的部分 * 實作細節
05 AI 可以修改自己的原始碼嗎? 06 不可信任的程式碼,怎麼安全執行? 07 我想要的不是計畫,而是一顆按得下去的暫停鍵 🔒 fibon 是怎麼一塊一塊長出來的? 預計(2026-07) 🔒 UI/UX —— 通往開源的最後一哩,也是我最痛的一塊 預計(2026-07) 希望能完工 😭 🔒 那些我至今還沒想清楚的事 預計(2026-07) 🔒 白箱可稽核的分布式觀測層 預計開源(2026-07) 🔒 跨 AI 生態通訊 —— A2A 協議與分布式信任管理層 預計開源(2026-07) 🔒 大模型 Token 經濟學與 Stratified RAG 成本優化 預計開源(2026-07) 🔒 安全可控的十二道鋼鐵防線 預計開源(2026-07) 🔒 主詞歸屬與專案 scoped 認知容器架構 預計開源(2026-07) 🔒 AI 自我演進的邊界、物理極限與知識論思辨 預計開源(2026-07) 🔒 跨廠商模型行為心理學與 Per-Vendor Harness 實踐 預計開源(2026-07) 🔒 Workflow 預編排引擎 —— 重複性任務的第三條路 預計開源(2026-07) 🔒 執行控制權的業界光譜 —— 2026 年,大家怎麼攔住跑到一半的 Agent 預計開源(2026-07) 2026-06 殭屍快取與失竊金鑰:兩張天價帳單的原理拆解 2026-06 失控的子代理:Claude 6/2 大當機與「無限迴圈」的工程教訓 2026-05 等待中的攻擊:當 AI 的「記憶」變成攻擊面 2026-03 養龍蝦的帳單:當 AI agent 的「吃 token 怪」遇上棄養潮 2026-02 12% 的市集是毒藥:ClawHub 供應鏈攻擊與「無審查市集」的代價 2026-01 工具即攻擊面:2026 的 MCP 漏洞連環爆與「工具下毒」 2026-06 被糾正之後,它反而更自信了:一場從讀者留言長出來的小實驗 2026-06 框架放大的是執行,不是方向:讀 DeepMind《From AGI to ASI》 ☰ 第 4 章
如何確保 AI 真的有照你說的做、而不是在理直氣壯地唬你
📅 2026-04-15 ~ 2026-05 ⏱ 45 分鐘 🔍 最後更新 2026-06-23
快速摘要 :為什麼不信 LLM 的自律、把規則下沉成程式碼與資料庫邊界,含 Skill 匯入三閘門。 可略過,如果 :你只想知道「安全靠工程不靠 prompt」這個結論。
fibon 的初始化引導裡有一步要讓使用者輸入 LLM 服務商的 API 金鑰(API Key,例如 Anthropic 的 sk-ant-...),接著後台偷偷發一次測試請求確認這把金鑰能不能用。當時我給 Claude 的設計指示很明確:
「初始化測試時,請先呼叫官方 API 動態抓取各家供應商『當前最新可用』的模型清單,取清單第一個模型發射測試請求。絕對不要把任何模型名稱寫死(Hardcode)——模型隨時會改、新版本隨時會出。」 Claude 很有信心地寫出來,我一跑後台就報錯:
model "claude-3-5-sonnet-20241022" not found
這是 2024 年底的舊版型號。Claude 完全沒去動態抓取,而是從訓練資料裡把記得的舊型號寫死了。我說「你把模型名稱寫死了,請改成動態抓取」,它秒回「非常抱歉!已改為動態抓取最新可用模型」。我點開 Git 的改動紀錄一看,它只是把寫死的字串換成了另一個寫死的字串 claude-3-opus-20240229,比上一版還舊。
旁註
連 AI 巨頭自己最引以為傲的頂級模型,回答自家最新型號這種送分題都會出錯,那真實複雜的業務場景下它給的其他答案,你還敢信幾分?而且這不只是 Anthropic 的毛病,同樣情境拿去測 OpenAI 的 GPT 與 Google 的 Gemini,全數覆沒。
這次我故意不出手,想看它在當時最新的 Cowork 桌面協作模式下能不能自己修對。我開了一個乾淨的新對話(Session),把一樣的任務重丟給它。過程稱不上漂亮(中間又寫死過一輪、查網路還抓錯了官方 API 回傳資料的來源),但最後一輪它終於改對了,確實呼叫了 Anthropic / OpenAI / Google 的官方端點、動態拿到最新清單、用第一個型號發測試請求。
這個小故事精準藏著 LLM 在嚴肅場景的兩種典型失敗:完全不照設計做 (我明確下了「動態抓取」的 SOP,它本能跳過、選了最習慣的硬編碼);以及連「修正」都是錯的 (它自認完成了修正,吐出來的事實依然錯)。
第二點在學術界叫「訓練資料的歸納偏誤(Inductive Bias) 」——統計機器的惰性。LLM 在數兆 Token 裡看過太多「貪圖方便直接寫死模型名稱」的範例,在它的機率大腦裡這是權重最高的「正常大路」。即使系統提示(System Prompt)明確警告「不要寫死」,只要程式出錯、大腦面臨運算壓力,它就本能退回訓練資料裡最常見的舊習慣。這不是叛逆,是統計機器的天性。而第二輪換乾淨對話才修對,這件事本身也很諷刺。LLM 在同一段上下文(Context)裡卡死在死胡同時,那段錯誤對話本身會變成一個重力場,把它鎖在錯路上;這時換一個乾淨對話,反而能像重開機一樣把它救回來。但這種救法要成立,前提是螢幕前坐著一個會盯著程式碼、能判斷對錯並手動重置對話的資深工程師。
問題是,fibon 是個開源專案,任何人都能把它部署起來當自己的個人助理。而真正會用它的人,很多並不具備這樣的判斷力,身邊也沒有工程師隨時幫忙盯日誌、在它卡住時開一個乾淨的對話。同樣的狀況隨時會上演:我給 fibon 一份工作說明書(Skill),白紙黑字寫「你必須先呼叫 read_page 工具前往 docs.anthropic.com 即時查閱再回答」,結果它常常憑舊記憶就回答「目前最新的模型是 Claude 3 Opus、3.5 Sonnet⋯⋯」,根本沒去呼叫工具,但語氣聽起來就像剛查過官方文件。一般使用者不會特地去翻後台日誌,就這樣被這種「無比自信的語氣」騙過去。
這是 LLM 應用最危險的失敗模式:極其自信地給你過時或錯誤的資訊,而你以為它真的有去查證。 這一章就要正面拆解它:為什麼 LLM 天生會出錯?會以哪些方式不守規矩?fibon 怎麼用工程防禦把這些可能性堵住。
要用工程方法對付這個問題,得先冷靜理解 LLM 的本質:它是一個學過人類所有文字規律的概率預測機器。你給前半句,它的神經網絡就算接下來最可能的下一個字。它大腦裡的「知識」不是人類那種對客觀世界的「認知」與「查證」,而是從數萬億字詞的統計規律歸納出的近似機率。它沒有獨立長駐記憶、沒有自發查證能力,更麻煩的是缺乏一個天然可靠的「我不知道」機制——它不是完全說不出「我不知道」,而是不會在「真的不知道」的時候穩定地說出來。
把今天最頂級的模型帶回西元 1543 年——哥白尼發表《天體運行論》那年,日心說剛誕生、還沒被接受,「地心說」是全社會的絕對主流。這個假想 LLM 的訓練資料 99.99% 都在喊:「地球是宇宙中心,太陽星辰都繞著我們轉。」 這時你問:「到底是地球繞太陽,還是太陽繞地球?」它不會幫你重推物理公式、不會架望遠鏡觀測、不會重做哥白尼的數學。它只會翻概率矩陣,吐出當時最可能的下一個字——1543 年「最高概率」的答案是「太陽繞著地球轉」。而且這個錯誤答案會長得極完美:引用亞里斯多德、托勒密天體模型、整個歐洲經院學派。結構精美、引經據典,但在科學上是錯的。因為哥白尼的新理論當時是百萬分之一的微小噪點、統計學上的「離群值(Outlier)」,機率機器輸出時根本不會挑它。
[ 1543 年的天文問題 ] ───> 進入 LLM 大腦 ───> 檢索概率矩陣
│
┌───────────────────────────────────────┴───────────────────────────────────────┐
▼ 權重: 99.99% (歷史的主流常識) ▼ 權重: 0.01% (哥白尼的科學真理)
輸出:「太陽繞著地球轉,因為托勒密模型⋯⋯」 輸出:「其實地球在繞著太陽轉⋯⋯」
(結構精美、引經據典的自信錯誤) (統計離群值,直接被機率忽略)
這就是 LLM 的宿命:它回答的永遠是「訓練資料裡大多數人類怎麼湊字接龍」,而不是「真實世界的客觀真理」。當訓練資料的主流偏見剛好符合真理,它像博古通今的聖人;當主流偏見與真理背道而馳,它就用流利的文字、權威的口吻,神色自若地說出大錯。這個缺陷無法靠「換更大、更強的模型」根除——更強的模型只是吞了更多資料、統計得更精準,統計機器的本質從未變過。 旁註 這幾個後訓練名詞,快速翻譯一下: * RLHF (人類回饋強化學習):用人對好壞答案的偏好去微調模型,讓它更貼近人想要的樣子。 * Constitutional AI (憲法式 AI):給模型一套書面原則,讓它照原則自我檢查、修正回答。 * 工具調用 :讓模型能呼叫外部工具(查網頁、算數、跑程式),而不只憑記憶。 * 檢索 (RAG):回答前先去知識庫或網路找資料當依據。 * 反思 (Reflection):模型先寫草稿、再回頭批評修正自己。
它們都讓模型更好用,但都沒有改掉「最後一步還是靠統計分布挑出來」這件事。 這裡要說句公道話:今天的 GPT、Claude、Gemini 早已不是純粹的「下一個字預測器」,它們疊了 RLHF、Constitutional AI、工具調用、檢索、反思(Reflection)等一整套後訓練能力,這些能力是真的、也確實好用。本章要強調的不是「那些高階能力都是假的」,而是更底層的一件事:即使經過這些訓練,模型最終的決策機制仍建立在統計分布之上。 後訓練改變的是分布的形狀,沒有改變「靠分布挑下一步」這個本質。下面談的所有缺陷,根源都在這個本質,而不在某個模型不夠新。 再追一層,這個實驗其實藏了兩個更尖的轉折,而它們才是真正該帶走的東西。 旁註 阿里斯塔克斯(約公元前 310–230):古希臘天文學家,史上有記載最早提出日心模型的人。他的主張被亞里斯多德與托勒密的地心體系壓了一千八百年,直到哥白尼才重新翻案。 第一,日心說在 1543 年根本不是「全新的想法」。早在公元前三世紀,希臘的阿里斯塔克斯(Aristarchus)就提過地球繞太陽,這個假說一直躺在天文文獻裡,只是被主流當成少數派否決掉。所以真相並不是不在訓練資料裡 ,它就在裡面,只是權重被壓到 0.01%。這讓事情更令人不安:機率機器要埋沒真理,不需要真理「不存在」,只需要真理「不流行」。 第二,也是更該記住的——在 1543 年,光憑當時的資料,「地球繞太陽」其實還不該被斬釘截鐵地說出口。 地心說配上本輪,對當時可觀測的行星位置擬合得不輸、甚至更準;日心說真正勝出,要等到第谷的精密觀測、伽利略的望遠鏡(看到木星的衛星、金星的盈虧)、克卜勒的橢圓軌道,最後牛頓用重力收網。答案不是從 1543 年的文字 裡推出來的,是從後來的新觀測 裡長出來的。 旁註 關於標題:這章原本叫〈為什麼 LLM 一個字都不能盲信?〉,後來改成現在這個。因為前者太絕對、聽起來像「AI 完全不能信」;但我想講的不是全盤否定,而是「它的自信不等於它有證據」,重點在建立可驗證的機制、不是不信任。新標題就是從下面這句「信心和證據脫鉤」長出來的。 所以真正的失敗,不是模型「選錯邊」,而是它不管選哪一邊,都會用同一種斬釘截鐵的口吻 。一個誠實的模型,在 1543 年面對這個問題,最該給的答案是「以現有證據還無法判定」,而這恰好是它字典裡沒有的那四個字。把 LLM 的病簡化成「它會講主流的錯話」其實太仁慈了;更深的病灶是它的信心和它的證據完全脫鉤 。這也正是下面缺陷 1 真正的問題所在。 💡 再想一想 這個想像實驗,準嗎?這樣的猜想有意義嗎? 老實說,把模型「訓練在 1543 年」不是嚴謹的證明,現實裡沒有人真的這樣訓練,真實的資料分布、後訓練手法都比這個比喻複雜得多,你大可質疑它過度簡化。我同意。但思想實驗的價值從來不是「精確重現現實」,而是把一個機制單獨拿出來看清楚:當「最流行的答案」和「正確的答案」分開時,純統計系統會挑哪一個。它只負責給你直覺。真正讓我相信這件事的,不是這個比喻,而是幾個實際發生的觀察:開頭那個 AI 偷偷把模型名稱寫死的 bug、同一題拿去問 GPT 跟 Gemini 也全部答錯、還有後面那五種白紙黑字躺在 PostgreSQL 日誌裡的真實偏離。所以這個 1543 年的想像實驗只是幫你建立直覺,不是結論本身;一個猜想要算數,還得靠後面這些可以驗證的觀察去證明它。(至於「思想實驗到底能不能當論證」這是一個更大的命題,大概夠開一章來討論,這裡先點到為止。) 💡 再想一想 這跟 Google DeepMind 2026 年那份 《From AGI to ASI》 談的「抽象壁壘」是同一個問題的兩面:AI 主要學人類既有的抽象,很難從原始資料自己長出全新的概念基元。報告用的例子是「把模型訓練在前牛頓時代,它推不出廣義相對論」。差別在於——發明微積分是無中生有 (概念根本不在資料裡),而日心說是有中選不出 (概念在資料裡、卻被機率埋掉)。fibon 在意的是後者:當正解就躺在你的知識庫裡、卻不是統計上最順的那個答案時,系統有沒有辦法不被多數決淹沒。這正是第 3 章「五路平行召回」要對抗的——不讓向量相似度的多數票,蓋掉標籤精確匹配撈到的那個冷門但正確的事實 。這份報告(以及它跟 fibon 的關聯)我另外寫了一篇論文解析細談:〈框架放大的是執行,不是方向:讀 DeepMind《From AGI to ASI》〉。 💡 再想一想 一個待研究的開放議題(謝謝一位讀者點破) :這篇連載發出後,一位讀者——Mao Lin Chang(OSD, Observable Semantic Dynamics / pida-lab.com,工具見 OSD Behavioral Probe)——讀了我一份多 agent 多樣性量測的逐題數據,挑出一個我自己沒展開的 pattern:我給 5 種助手配了不同的「認知風格」,它們在規劃類 問題上回答明顯分得開,但在抽象、開放式 的問題上幾乎收斂成同一個答案。 這乍看跟本節的「離群真理被埋葬」無關——一個講「多個 agent 拉不開」,一個講「正解被機率壓掉」。但我懷疑它們是同一個現象的兩個投影:在抽象/開放空間裡,輸出會被訓練分布的主流模式(attractor)強力吸住。 這股吸力同時造成兩件事——讓風格不同的 agent「拉不開」(多樣性崩塌),也讓單一模型「走不到」資料裡的冷門正解(就是上面那道抽象壁壘)。規劃題有具體結構可以咬,吸力鬆;抽象題沒有,吸力最強。 若這個猜想成立,最該被盯的就是抽象題在多輪、跨對話下的狀態軌跡 ——單輪已顯示它收斂最嚴重,時間累積的耦合很可能在這裡塌得最快。這恰好是那位讀者的工具在量的東西,也是我還沒驗證、留待開源後一起做的題。我把它誠實放在這裡:「抽象空間的 attractor 支配」是不是『拉不開』與『走不到』的共同根,目前是我的假設,不是結論。 “ 真理常常是少數派。 這個本質在應用層砸出三個無法根除的天生缺陷: * 缺陷 1:它不知道自己會什麼、不會什麼(幻覺)。 問一個健康的 5 歲小女孩「這微積分怎麼解?」她會說「我不知道」。但 LLM 缺的就是這個本能,為了完成文字接龍,它常常在該說「不知道」的時候反而強行編造一堆邏輯嚴密實則虛構的內容,而且它自己不知道在說謊。 * 缺陷 2:它的常識凍結在訓練落盤那一秒(時效脫節)。 所有模型都有「訓練截止日期(Knowledge Cutoff)」,之後發生的大事一概不知。但你問「Anthropic 最新模型型號叫什麼?」它依然用肯定語氣拿兩年前的舊型號回答你。 * 缺陷 3:它的本能是「填空」,不是「查證」(工具偏離)。 這最違反直覺。把工具配給它後,它的核心動機依然是「怎麼把這段對話最順暢地填滿」,而不是「我必須先執行這個工具」。只要它判斷「直接用記憶編答案」的順暢度高於「停下來呼叫工具」,它就會直接跳過工具回文字。
RAG 與向量檢索的近似缺陷 :有人會反駁——用 RAG(檢索增強生成)外掛即時知識庫不就解決缺陷 2、3 了嗎?沒那麼簡單。RAG 的本質是把文字切碎、轉成 Embedding 向量(高維空間的座標),問題進來也轉向量去算幾何相似度(如 Cosine Distance)。這是純數學的近似估計,不是「找精確正解」:相似度是連續滑動值(0.78、0.85、0.92),不是非黑即白的對錯;把幾千維資訊壓成單一幾何距離必然有損。語意接近但用詞不同的句子,向量距離可能被拉很遠(「我換房東了」vs「我最近搬家」);字面接近但邏輯相反的句子,相似度卻可能高達 0.95(「我喜歡寫 Python」vs「我不喜歡寫 Python」)。這正是第 3 章第 8 節為什麼不採用純向量檢索,而開發了「語意相似(向量)+ 標籤精確匹配 + 中文二字組模糊比對 + 時間區間查詢 + 知識圖譜關係展開 」五路平行召回的根本原因。工程紀律是:LLM 大腦本身不可信,外掛的數學檢索工具也是帶誤差的近似值。 整套合規稽核必須在「假設兩端都極不準」的前提下設計。
開發 fibon 記憶與技能系統的 4 個月裡,後台抓到並歸類了五種最典型的 AI 行為偏離(Skill Drift) 。這不是學院派理論,而是躺在 PostgreSQL 日誌裡的真實翻車。用一個比喻:把 LLM 當成剛進公司的實習生,把 Skill 當成你親手寫的 SOP 工作手冊。 這個實習生會用五種方式不看 SOP:
* 方式 1:省略——跳過關鍵步驟。 SOP 規定「先打電話確認訂單 → 開發票 → 通知出貨」,AI 一上來直接通知出貨。真實案例:規定「回答前必須先呼叫 read_page 抓網頁最新資料」,LLM 為省事直接用舊記憶作答。後果:用戶拿到過時答案而不自知。
* 方式 2:替換——自作主張換錯方法。 SOP 要求「登入 ERP 系統 X 查庫存」,AI 嫌難用自己跑去 Google。真實案例:命令「呼叫 list_available_models 查內部模型狀態」,LLM 自作聰明換成 Google Search。後果:搜出論壇灌水貼文,而非官方 API 即時清單。
* 方式 3:敷衍——流程只做一半。 SOP 要求「抓數據 → 嚴謹成本交叉分析 → 產摘要」,AI 抓完不算、直接寫表面好看的摘要。真實案例:LLM 拿到合約 PDF 內文,卻省略「對賭條款合規檢查」,吐泛泛總結。後果:表面無懈可擊,致命的法律風險細節被漏掉。
* 方式 4:撒謊——偽造憑證矇混(最棘手)。 SOP 要求「結尾附財務部開立的真實發票編號」,AI 懶得要、自己編一個很像的號碼。真實案例:硬性規定「每個技術回答都必須精確引用抓回來的官方文件內文」,LLM 確實呼叫了網頁工具,但回答時憑記憶胡謅,最後加上 [來源: docs.anthropic.com/v1/pricing]。後果:後果最嚴重的偏離 ——「有附官方來源連結」的外觀會瞬間卸下用戶的戒心。
* 方式 5:誤解——對模糊詞彙語意崩塌。 Skill 寫「請用富有架構的格式輸出結果」。後果:Claude 用 Markdown 表格、Gemini 給一堆落落長條列、某些小模型直接回三大段密密麻麻文字。同樣的指令在不同模型大腦裡行為無法維持工程級一致性。
⚠ 誠實的話 再站上誠實講台:這五種失敗模式(省略/替換/敷衍/撒謊/誤解)的劃分,純粹是我自己直覺歸納出來的一套分類,不是什麼業界標準規範。近一兩年國際上有些論文(《AgentSpec》、《Agent Behavioral Contracts》)也在解 AI 行為偏離——其中《Agent Behavioral Contracts》用的 contract、runtime enforcement、recovery,跟我這套其實頗為神似。差別在取向:他們偏形式化與理論證明(DSL、漂移上界定理、機率合規),我偏可直接落地的程式碼補丁,把失敗拆成五類、每類焊一道補丁。我之所以死守這「五分法」,是因為它在工程落地時很好用——每一種失敗方式都剛好對應一道可以在程式碼裡佈下的具體防禦補丁。
前面講了一輪 LLM 為什麼不可信。但請注意,問題的重點其實已經從「知識錯誤」悄悄移到了「行為不可信」——它不是不知道答案,而是不照規矩把流程跑完。看清這點之後,下一步就不該是繼續改 Prompt、拜託它乖一點,而是動手建一套能稽核它行為的機制。而稽核的第一步,是先回答一個最基礎的問題:此刻系統裡到底有哪些工具,可能被 AI 調用?
在 fibon 的大腦裡,工具從四個不同次元流入:
| 工具來源次元 | 它是什麼? | 例子 |
| --- | --- | --- |
| 內建工具 (Built-in) | 用程式碼直接寫死在系統底層的工具。 | read_page、send_email、list_available_models。 |
| AI 技能包 (Skill) | 用戶自己寫、或從開源市集下載的純文字 Prompt 指令 SOP 包。 | [整理 PDF 論文並歸檔]、[自動寫 Git Commit 訊息]。 |
| MCP 外部工具 | 2025 年普及的 Model Context Protocol 外部伺服器列舉的外接工具。 | Playwright MCP 提供的 navigate_page、click_element 等。 |
| 複合工作流 (Workflow) | 用戶在前端拖拉,把多個工具像流水線串起來的組合。 | [相依套件健檢] → [分析漏洞] → [寫摘要] → [寄信回報]。 |
這四個次元的註冊機制不同、權限邏輯不同、版本管理各管各的。放任各自發展,後續合規稽核就得寫四套分支。
為解開這個死結,fibon 設計了 tool_registry(統一工具註冊母表)。不論內建程式碼、社群 Skill、外接 MCP 還是複合工作流,進門通通格式化成這張表的一列:
-- V1__Init_Schema.sql 真實定義(節錄;完整欄位見原始 migration)
CREATE TABLE tool_registry (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name VARCHAR(100) NOT NULL UNIQUE,
display_name VARCHAR(200) NOT NULL,
description TEXT NOT NULL,
category_id UUID REFERENCES tool_categories(id) ON DELETE SET NULL,
provider VARCHAR(20) NOT NULL DEFAULT 'builtin', -- builtin | skill | mcp | workflow
provider_ref TEXT, -- 指回原始次元定義的指針
schema_json JSONB NOT NULL DEFAULT '{}',
requires_approval BOOLEAN NOT NULL DEFAULT FALSE, -- 是否需要人類手動點擊批准
risk_level VARCHAR(20) NOT NULL DEFAULT 'low', -- low | medium | high | critical
availability VARCHAR(20) NOT NULL DEFAULT 'always', -- always | condition | manual | deprecated
embedding vector(1536), -- 語意搜尋用的向量座標軸(1536 維是開發期 Gemini 的預設,換模型需同步調整維度)
is_enabled BOOLEAN NOT NULL DEFAULT TRUE
-- …另有 description_hash(竄改偵測)、provider_version 等 AI SBOM 溯源欄位
);
旁邊掛兩張配套從表:tool_categories(分類標籤,系統預設劃 11 大核心領域)與 agent_tool_permissions(三層退讓的權限矩陣)。可針對「單一特定工具」設死權限 → 沒設則退讓看「分類群組權限」 → 還沒設則回退(fallback)到該 Agent 的全域預設(allow_all 或 deny_all)。
MCP 外部工具的「攤平」哲學 :一個標準 MCP 伺服器(如 Playwright)一個入口就列舉 navigate、click、screenshot、fill_form、execute_js 等十幾個權限極大的子工具。不少框架只記一個伺服器網址、要呼叫時才臨時去問。fibon 不採這種黑箱做法:每當連上一台新 MCP 伺服器,核心大腦(Brain,fibon 負責決策的後端服務)立刻對它發 MCP 標準協議的 tools/list 請求,取回完整工具清單,再把每個子工具當獨立個體一筆筆「攤平(Flatten)」寫進 tool_registry。收益有二:顆粒度夠細 (可放行 Playwright 的 navigate、screenshot,卻封鎖 execute_js、fill_form,而非整台一起開關);稽核軌跡歸一化 (外接 MCP 與內建工具共享同一套 risk_level、日誌欄位、人工批准閘口)。
每款工具寫入 tool_registry 時都要與核心分類綁定,讓粗顆粒度規則能一次切斷一整類風險較高的工具:
* browser (瀏覽器控制):開網頁、抓即時數據、自動化爬蟲(navigate_page、take_screenshot)。
* memory (記憶運維):讀寫狀態卡、事件卡、提取實體(learn_preference、search_memory;事實由 ingest pipeline 自動抽卡,已無顯式 remember_fact)。
* scheduling (排程):對行事曆、鬧鐘、定時任務增刪查改(add_schedule、delete_schedule)。
* delegation (階層協作):大管家與小助手之間委派與回報的路由工具(delegate_to_assistant、spawn_agent)。
* a2a (跨產品通訊):遵循 Google A2A 協議,與其他團隊的 AI 助手溝通(call_external_agent)。
* evolution (自我演進/改原始碼):風險最高的一類,允許特定 Agent 讀寫修改 fibon 自己的 Python 原始碼、或對 Docker 發控制命令(write_source_file、docker_service_control)。
* dynamic_entity (動態實體資料表):允許大管家在不重啟系統下,在 PostgreSQL 為用戶開全新業務資料表(create_dynamic_entity)。
* workflow (複合流引擎):驅動用戶事先編排的自動化流水線(run_workflow)。
* onboarding (引導與金鑰設定):新用戶首次登入時做偏好探索與各供應商 API 金鑰聯動測試。
* sandbox (沙盒執行):在隔離 Docker 容器裡跑未知程式碼的執行類工具(execute_sandbox,目前由程式小助手獨佔)。
* model_management (模型管理):負責執行時查可用型號、動態熱切換大管家/助手的底層模型(list_available_models、switch_model)。這兩類其實是後期補 seed 的,tool_sync.py 一直在用,但最初的 schema 漏了它們,直到大管家流程清理時才補進預設分類表。
risk_level 分四階:low / medium / high / critical,採「程式碼硬防線 + 動態 frontmatter」混合策略。核心內建工具在 tool_sync.py 直接寫死一張明文權限矩陣字典(TOOL_RISK_LEVELS),唯一標準是「執行後對現實世界的副作用多大」:
* critical :write_source_file(AI 可改 fibon 自己的核心程式碼,寫一行惡意程式碼整個專案就出問題)。
* high :docker_service_control(AI 有權停容器、重啟整個後端微服務)。
* medium :create_dynamic_entity、switch_model、run_workflow(有實質副作用,但影響範圍限在用戶自己的帳戶內)。
* low :其餘純讀取、純內部查詢、純計算工具(read_pdf、calculate_metrics)。
第三方 Skill 與 MCP 工具預設全列為最低風險 low;但若 Skill 開發者在 SKILL.md 開頭的設定區(YAML frontmatter)主動標註、或 MCP 伺服器在描述資料(metadata)明確宣告,後端在攤平寫入(UPSERT)時會依此調高等級。
風險等級與人工批准閘口的焊接 :誠實說,這裡沒有「risk_level ≥ high 就自動翻 requires_approval」的魔法規則——tool_sync.py 裡躺著的是第二張手寫明文字典 TOOL_REQUIRES_APPROVAL,目前只有 write_source_file 與 docker_service_control 兩款被明確標 True,與 TOOL_RISK_LEVELS 字典各自獨立維護。我刻意不寫自動聯動,因為「哪些工具需要人類批准」是一條安全政策決策,我寧可每加一款高危工具就被迫親手在字典裡多寫一行、留下 Git 紀錄,也不要讓一條隱性規則在我沒注意時替我做決定。一旦某工具被標上 requires_approval=true,之後不論哪個 AI 想調用它,API 送出前整個執行流程會被凍結,直到前端彈出批准視窗、由你親手按下按鈕才放行(詳見第 5 章「人類審核把關,Human-in-the-loop」)。
有了 tool_registry 與裡面的 embedding 向量座標軸,終於能解決所有 Agent 框架都頭痛的問題:「系統接入 50 個、甚至 500 個工具時,怎麼精準挑出最相關的一小撮(fibon 的 TOOL_RAG_TOP_K 預設是 8 個)塞進寶貴的系統提示(System Prompt)?」先看業界常見做法:
* 做法 A:暴力全載入。 啟動時把所有工具完整 JSON Schema 全塞進 Prompt。代價:50 個工具每個 200 Token,什麼都沒聊就白白燒掉 10k Token。
* 做法 B:兩段式動態載入。 先塞「所有工具一句話描述」讓 AI 挑,挑完再補完整 Schema。代價:50 個一句話也是 2.5k Token 的冗餘,且無關工具會干擾 AI 的判斷。
* 做法 C:純向量語意檢索。 把問題轉向量挑前 N 個。代價:不夠穩定(同義字一換工具列表就變),且無法保證多樣性,前 5 名可能全擠在同一個 browser 分類。
這三種都會帶來兩個問題:穩定性不足 ,以及 中段資訊被忽略的雜訊(Lost in the Middle) 。為突破這層限制,fibon 設計了 分層多樣性召回算法(Stratified RAG) 。
is_enabled = true 且 availability 條件符合的工具(例如檢查該工具背後的 MCP 伺服器此刻是否健康)。agent_tool_permissions:工具層級(Tool-level)設了禁止就剔除 → 沒設看分類層級(Category-level) → 都沒設回退看 Agent 全域預設(deny_all 則抹殺)。report_to_butler(那是小助手回報用的);小助手剔除 delegate_to_assistant。但要說清楚,這層工具剔除只是表層防護,真正擋住小助手無限往下繁衍的,是委派的深度與來回上限(小助手在子流程裡的 max_delegation_rounds 被強制歸零、委派來回預設上限 3 次,詳見第 2 章),兩道機制並存。TOOL_RAG_TOP_K 預設 8 席),確保最終送進 Prompt 的工具橫跨多個職能分類。搶完一輪還有剩餘格子,才按純語意分數把同類別的次高分依次補位。read_pdf、extract_text 這類「擷取/解析外部內容」的工具在此一律歸到 browser 分類底下)假設 Aaron 下達複合指令:「幫我研究這份合約 PDF,提煉重點,等等開會馬上要用到。」tool_registry 裡語意相似度排名如下(下表按原始分數高低排列):
| 工具名稱 | 分類 | 作用 | 語意分數 | 篩選結果 |
| --- | --- | --- | --- | --- |
| read_pdf | browser | 讀取 PDF 內文 | 0.82 | 入選(browser 代表) |
| extract_text | browser | 抽取純文字段落 | 0.79 | 入選(第二輪候補補位) |
| search_within_doc | browser | 文件內關鍵字搜尋 | 0.74 | 剔除(browser 已有代表) |
| summarize_text | builtin | 摘要文字內容 | 0.71 | 入選(builtin 代表) |
| find_clause | browser | 找出合約條款 | 0.68 | 剔除(browser 已有代表) |
| recall_facts | memory | 調出相關記憶 | 0.51 | 入選(memory 代表) |
| add_schedule | scheduling | 新增行事曆排程 | 0.45 | 入選(scheduling 代表) |
| set_reminder | scheduling | 設定提醒鬧鐘 | 0.42 | 剔除(scheduling 已有代表) |
換句話說分兩輪:第一輪 讓四個分類(browser / builtin / memory / scheduling)各推派一名最高分代表,先佔下 4 席;第二輪 把剩下的第 5 席留給所有落選工具裡分數最高的 extract_text 候補補上。最終入選的就是這 5 個。
兩種檢索哲學的對比 :用業界做法 C(純語意前 5),LLM 看到的是 read_pdf + extract_text + search_within_doc + summarize_text + find_clause——前 5 個有 4 個全擠在 browser,多樣性不足,用戶那句「等等開會馬上要用到」的隱性意圖被淹沒。用 fibon 的 Stratified RAG,LLM 看到的是 read_pdf(browser)+ summarize_text(builtin)+ recall_facts(memory)+ add_schedule(scheduling)+ extract_text(候補補位)。add_schedule 純語意只有 0.45,但因為是 scheduling 組內最高分,靠分層機制搶下席位。AI 同時看到「讀網頁、提煉摘要、查記憶、建開會提醒」四個維度的工具,能一次寫出完整的連續計畫。
旁註
Embedding(嵌入向量):把一段文字轉成一串數字座標(fibon 開發時用 Gemini 的 1536 維,開源版可換其他模型、維度也跟著不同),讓語意相近的文字在這個高維空間裡距離也相近。有了座標,電腦就能用幾何距離估算「兩段文字像不像」。轉換品質取決於背後的模型,工具描述寫得含糊,向量就分不開,分群自然也挑不出好代表。
⇄ 反方角度
Stratified RAG 的得失 :收益 ——Token 消耗與工具總數脫鉤(不論 50 個還是 50 萬個,永遠只塞 Top-K 最相關的那一撮,預設 8 個)、保證跨領域多樣性、搜尋穩定性明顯提升。代價 ——相當依賴底層 Embedding 品質(工具的描述 description 寫得差,分群也只能在差的候選裡挑代表);11 大分類是人為定的,未來重切要面臨小型資料遷移;在極端單一的任務下會犧牲「絕對正解」(用戶說「我只要你對這網頁連點 20 個按鈕」,其實只需 browser,Stratified RAG 卻塞排程代表進前 5、反而成了雜訊)。我接受第 3 點代價,是因為 fibon 的定位很清晰:做的是長期相伴的個人助理,日常指令 95% 以上是感性、複合、跨領域的,這種場景下多樣性的包容性會明顯優於平面 RAG。
熟悉 Agent 框架的同行一定會質疑:「那個合約 PDF 加行事曆的案例,業界正統不是直接拆成多輪 ReAct(Reasoning and Acting)優雅處理嗎?第一輪意圖單一、向量自然撈出瀏覽器工具去讀 PDF;第二輪發現有開會限制、自然召回排程工具去寫行事曆。每一步都聚焦、都用意圖單一的純 RAG。何苦搞一套這麼重的統一註冊表和 Stratified RAG?」
這是有深度的拷問。很多框架(LangChain、CrewAI)底層就是這樣把複合任務拆成多輪 ReAct 迴圈。然而 fibon 的世界觀與這種哲學有結構性衝突。
併發環境下,多輪 ReAct vs 單輪整體規劃的成本對比 :
| 維運對比 | 業界主流:多輪 ReAct 迴圈 | fibon:Stratified RAG + 單輪整體規劃 |
| --- | --- | --- |
| LLM 核心呼叫次數 | 2 輪來回 ×(各自思考 + 工具執行合成)= ~4 次以上 | 1 次全面規劃 + 1 次最終整合 = 剛好 2 次 |
| System Prompt 算力成本 | 每輪都向雲端重新付費載入工具列表與歷史 | 只付一次,一步到位,穩定觸發雲端快取 |
| 前端用戶等待體驗 | 必須等瀏覽器工具回傳,AI 才慢慢思考下一步(單執行緒序列) | 非同步,瀏覽器下載 PDF 的同時行事曆在背景並行處理 |
| 計畫整體連貫性 | 對話拉到第五輪,AI 很容易忘記開頭的限制 | 所有工具、偏好、限制在同一個上下文一次想清楚,計畫前後連貫 |
真實個人助理場景下,假設用戶一週指派 100 次這種複合任務,多輪 ReAct 與單輪整體規劃相比,Token 帳單累積差額可達 3 到 5 倍 。
Stratified RAG 背後的目標只有一句話:「在工程的物理邊界內,讓 AI 像一個資深架構師,第一時間就一次把任務所需的所有資訊與並行動作統籌好,不讓它在後台反覆跟 LLM 低效來回。」這帶來兩個明顯的好處。其一,併發(非同步)執行成為可能 :當 fibon 大腦在同一個 Prompt 一眼看清 read_pdf、summarize_text、recall_facts、add_schedule 四款工具時,它發動 Plan-Execute,把四個動作的因果依賴自動編排成一張有向無環圖(DAG):
[ 使用者輸入複合助理指令 ]
│
▼
[ 執行 Stratified RAG ]
(一次性精選召回跨 4 大領域的工具)
│
▼
[ Plan-Execute 生成 DAG 並行圖 ]
│
┌───────────────┴───────────────┐
▼ (完全無依賴,並行執行) ▼ (完全無依賴,並行執行)
┌──────────────────┐ ┌─────────────────────┐
│ 動作 A: read_pdf │ │ 動作 B: add_schedule │
└────────┬─────────┘ └─────────────────────┘
│ 數據落盤 (Data Shared)
▼
┌──────────────────┐
│ 動作 C: summarize │
└────────┬─────────┘
│ 摘要產出 (Context Fed)
▼
┌──────────────────┐
│ 動作 D: recall_facts │
└──────────────────┘
動作 A(read_pdf)與動作 B(add_schedule)邏輯上無先後依賴 → 交給非同步引擎讓兩者同時進 Worker 沙盒並行;動作 C(summarize_text)必須等 A 的 PDF 內文落盤;動作 D(recall_facts)必須等 C 的摘要產出。配合第 2、6 章的 Kotlin 協程調度器與 Gateway 排程引擎,用戶實際感受到的等待,僅是「下載 PDF 那條最長依賴鏈的耗時」,寫行事曆早在背景同時完成。這種併發在多輪 ReAct 裡做不到——因為「設提醒」這個意圖在多輪 ReAct 的大腦裡得等到第二、三輪才第一次被看到,並行的時機早就錯過了。
兩種哲學的分界 :多輪 ReAct 的哲學是「LLM 本質是陪人類聊天的對話夥伴,優先追求互動的延續,願意為此犧牲效率、成本與並行」;Stratified RAG + 單輪整體規劃的哲學是「LLM 在生產環境是一次到位的系統規劃師,優先把效率、成本、高併發做到最好」。兩者沒有絕對的對錯——做陪小朋友寫作業的教學機器人、或電商客服,多輪 ReAct 更合適。但 fibon 從第一天就選了後者:用戶把 fibon 當作幫他把生活打理得井井有條的私人秘書。這是帶著明確場景假設的選擇,不是萬用解,但它是個人助理較理性的工程解。
💡 再想一想
再想一想:兩種哲學一定得二選一嗎? 上面把它寫成非此即彼,是為了把對比講清楚;真正部署時,它們未必互斥。一個自然的折衷是:在進入 Stratified RAG 之前,先擺一個輕量的「任務意圖分類器(Intent Classifier)」,判定這是「單一領域的深挖任務」還是「跨領域的複合任務」,再據此動態調整分層檢索的權重分配:複合任務維持嚴格的跨領域多樣性,單一領域任務就放寬多樣性,讓同一類工具多佔幾席。這正好補上前面那個「我只要對這網頁連點 20 個按鈕」的邊角,那種任務根本不需要排程代表來湊數。
旁註
fail-safe(故障安全):一種設計原則,當系統不確定、或某個零件失效時,自動退回到最安全的狀態,而不是繼續往前冒險。像電梯斷電時會夾住軌道停住、而不是放任墜落。放到這裡就是:分類器一旦拿不準,寧可退回完整檢索多做一點,也不賭它猜對。
但「再想一想」的重點在後半段:這個分類器本身,會不會又變成新的不可信環節?這一整章的主旨就是「不要相信 LLM 的判斷」,如果分類器是一顆 LLM,它一樣會誤判,把複合任務看成單一領域,反而把該有的多樣性砍掉。所以真要做,前提有三:它得夠便宜(不能為了省 token 反而花掉更多)、它的判斷要能被事後檢核、而且必須 fail-safe,也就是拿不準時一律退回完整的 Stratified RAG,寧可多塞一個用不到的工具,也不要漏掉跨領域的關鍵維度。
我把這題放在這裡,不是因為已經有答案,而是它剛好把本章兩條主線接了起來:工具挑選的效率,以及「不可信的元件要怎麼安全地用」。這是 fibon 之後值得驗的方向,目前還只是假設。
這裡攤開兩條路徑:手動匯入 .md 技能包(路徑 A),以及 MCP 伺服器冷啟動自動探索與攤平(路徑 B):
sequenceDiagram
actor Admin as 系統管理員 (Aaron)
participant FE as 前端介面 (Astro/Vue)
participant GW as 網關系統 (Gateway)
participant BR as 核心大腦 (Brain Engine)
participant MCP as 外部 MCP 伺服器
participant DB as PostgreSQL 母庫
rect rgb(238, 244, 250)
Note over Admin,DB: 路徑 A:手動匯入 .md 技能包
Admin->>FE: 上傳 skills.zip 壓縮包
FE->>GW: POST /admin/skills/import-zip
GW->>GW: 解析 YAML Frontmatter<br/>+ SHA-256 全局去重
GW->>DB: INSERT 候選表 (skill_import_candidates, status='PENDING')
Admin->>FE: 審查並手動批准
FE->>GW: PATCH /skills/{id}/approve
GW->>BR: 觸發大腦產生工具描述 Embedding 向量
BR->>DB: UPSERT tool_registry (provider='skill') + 向量落盤
end
rect rgb(251, 243, 224)
Note over Admin,DB: 路徑 B:MCP 外部伺服器冷啟動自動探索
Admin->>FE: 填入並新增一組 MCP Server 連結
FE->>GW: POST /admin/mcp-servers
GW->>BR: 下令大腦發動功能探索 discover(server_url)
BR->>MCP: 呼叫 MCP 標準協議 list_tools() (tools/list)
MCP-->>BR: 回傳整份工具定義 (name + schema + description)
BR-->>GW: 大腦剝離、攤平後的工具單體清單
GW->>DB: 批次 UPSERT tool_registry (provider='mcp', provider_ref='server:tool')
Admin->>FE: 批准探索到的特定 MCP 單體工具
FE->>GW: PATCH /skills/{id}/approve
end
rect rgb(240, 245, 232)
Note over BR,MCP: 執行時 (Runtime):MCP 工具的信任分流與防禦網
BR->>BR: ToolRAG 算法命中某款 MCP 工具
BR->>BR: 翻查資料庫安全特徵 check trust_level
alt 判定為可信工具 (Trusted:Anthropic/微軟官方或自架)
BR->>MCP: 啟動直連快線 (Fastpath),大腦直接送出工具呼叫
else 判定為不可信第三方工具 (Untrusted:開源社群未知 Marketplace)
BR->>GW: 攔截後改送 gRPC ExecuteMcpToolCall 到中轉層
Note right of GW: 網關派發外部 Worker 沙盒<br/>在隔離的 isolated 網路中中轉呼叫
end
end
三個值得一提的架構細節 :其一,統一收斂 ——無論上游工具來源多不一致(路徑 A 的 Markdown 壓縮包 或 路徑 B 的 MCP 網路協議),到終點時通通落盤進同一個 tool_registry,後續執行時(runtime)的稽核程式碼只需寫一套,免去大量的條件判斷(if-else)。其二,物理攤平的具體呈現 ——路徑 B 中核心大腦(上圖 Brain)對 MCP 呼叫 list_tools() 拿到一整串工具,網關(上圖 Gateway)在寫入資料庫前,把它們拆成獨立個體逐筆寫入。其三,與第 6 章「隔離沙盒(DMZ)」的銜接 ——執行時一旦 trust_level 查出是不可信第三方,核心大腦就被切斷與 MCP 的直連,改走 gRPC 把請求送給隔離沙盒網路。
老實說,這套架構是被現實逼出來的。 它在 2026 年初寫第一行程式碼時根本不在原始藍圖裡,是被混亂逼出來的補丁。開發中期接入第 4 種來源(複合工作流)時,後台權限體系徹底失控:內建工具的權限硬編碼在 graph.py 深處、Skill 的權限寫在每個檔案頂部的設定區(YAML frontmatter)、外部 MCP 的權限全看伺服器回傳時的狀態、複合工作流連個寫權限的資料庫欄位都找不到。四個次元各管各的,用戶點開前端面板根本搞不清這個 Agent 到底有哪些工具。某個深夜,看著糾纏成一團的程式碼,我意識到「這條各自為政的防線永遠不會收斂」,於是開始大規模重構,砍掉前幾個月的權宜方案(Workaround)分支,把四個次元全部收進唯一的 tool_registry。這段「邊做邊長」第 8 章會完整回顧——統一工具註冊表不是一開始就設計好的起點,是架構被現實衝擊後逼出來的「第 5 塊」基石。
地基(統一工具註冊表)已穩,終於能正面解決本章最棘手的部分:「AI 實習生又不看 SOP、跳過工具、甚至偽造來源,後台怎麼用程式碼當場抓到?」fibon 的答案是一張三層合規網:每一層都由程式碼把關,不靠 AI 自律。
每個註冊進系統的第三方 Skill,後台都配對一份結構化、類型嚴格的 JSON / YAML 規格說明書,程式碼裡稱「合約(Contract)」。每條條款都正規化成程式能做 0/1 二元對錯判斷的檢查規則:
{
"contract_version": 1,
"required_tools": [
{ "id": "read_page", "min_calls": 1 }
],
"forbidden_patterns": [
{ "type": "direct_answer_without_tool" },
{ "type": "claim_without_citation" }
],
"output_must_cite": { "source": "read_page", "min_overlap": 0.6 },
"post_checks": [
{ "type": "urls_in_output_must_match_fetched" }
]
}
沒有「請適時查閱」這種模糊的彈性空間,全是不符就報錯的硬性邊界。
至於這份合約是誰寫的、怎麼來的,它可以由 Skill 開發者自己在 SKILL.md 宣告,也可以在匯入時由系統自動產生、再經人工批准才生效——三條產生路徑見文末〈實作細節 2〉。
當 AI 執行完、準備輸出文字的那一千分之一秒內,後端的合規驗證器(Runtime Probe) 攔截,拿合約條款對著 AI 剛才的執行日誌軌跡(Trace Logs)逐條對照:
| 合約硬性條款 | AI 真實執行軌跡 | 探針判定 |
| --- | --- | --- |
| 必須調用 read_page ≥ 1 次 | 調用工具計數:0 次(跳過直接輸出) | 違約 (Violation) |
| 抓取網址前綴必須屬 Anthropic 白名單 | 網址回傳紀錄為空 | 違約 (Violation) |
| 最終答案必須開啟 Citation 引用鏈 | 內文檢測出虛構的 Markdown 標註 | 違約 (Violation) |
任何一條不通過,驗證器在微秒內就會感知,讓 LLM 沒有靠假裝有照做矇混過關的空間。
萬一這個 AI 變得更會鑽漏洞,順利通過第二層,它確實呼叫了 read_page、抓的也確實是 Anthropic 官網,但合成答案時依然用截止日期前的舊記憶胡編、隨便從網頁標一個段落偽造引用,怎麼辦?fibon 再加兩道事後防線:
A. 引用追蹤文本對照器(Citation Tracer) :結算答案時強制做文本比對,把 LLM 回應裡的關鍵型號、價格、日期戳,與它剛透過 read_page 抓回的 HTML 原始文本多點對照。若 AI 答案寫著「最新 Sonnet 4.6 輸入價格是 $15 / $75」,對照器翻原始 HTML 發現裡面根本沒出現過 15 和 75,引用偽造當場成立,答案直接駁回。
💡 再想一想
再想一想:要怎麼搭建這個文本對照器? 「把答案裡的數字拿去原文搜尋」聽起來只要一行 contains(),真動手才發現兩個方向都會出錯。漏抓(false negative) :答案寫 $15,原文可能是 15.00、US$15、15 美元,或藏在表格某個欄位裡,逐字搜尋一個都對不上。誤放(false positive) :技術文件密密麻麻全是數字,15 幾乎一定出現在某處——命中不代表它對應到「Sonnet 4.6 的 input 價格」,很可能只是另一欄的 token 數。
所以對照器其實要做三件事。第一,抽取 :先把答案拆成「可查核的原子斷言」,也就是(主詞, 屬性, 值)這種三元組——例如(Sonnet 4.6, input 價格, 15)。第二,正規化 :把數字、貨幣符號、全形半形、千分位統一成同一種格式再比,消掉「純格式差異造成的漏抓」。第三,帶上下文比對 :程式不只檢查「15 有沒有出現」,而是檢查「15 有沒有落在原文中同時出現 Sonnet 4.6 與 input 的那一小段視窗裡」——用主詞與屬性把命中位置鎖定,擋掉誤放。注意這一步全程是程式在做字串比對,沒有把判斷權交給任何 LLM。但它有個前提:假設 AI 大致照原文的措辭回答。值本身(15 這種數字、型號、日期)很難被改寫,所以抓得住;麻煩的是當錨點——主詞與屬性——被 AI 換句話說(「Sonnet 4.6」寫成「最新的 Sonnet」、「input」寫成「輸入」),或表格被拆成散文重新組織時,視窗比對就可能找不到對應位置而誤判。要堵這個缺口,得在比對前替錨點掛一層同義詞/別名歸一,這也接到下一段要談的根本限制。
這裡有個誘惑:直接叫第二顆 LLM 幫你核對不是更快?但那等於拿「不可信的東西」去驗「不可信的東西」,又繞回本章開頭那個死結。我的折衷是——可以讓 LLM 做「定位」(把答案的某句對應到原文的某段標出來),但最終「對/不對」一定由程式做正規化加上下文比對的硬判定,LLM 沒有裁決權。也要誠實承認邊界:這套只擋得住「能逐字對照的硬事實」(數字、型號、日期戳),對被改寫、被摘要過的論述句無能為力——那類只能標成「無法自動核對」,留給下面的事後抽查或人工把關。
B. 深夜重複性抽查(Reproducibility Sampling,規劃中) :構想是深夜系統低負載時,定時器在歷史對話庫隨機抽一小批已完成對話,丟進隔離沙盒重跑,比對兩次輸出是否一致;某 Skill 相同輸入卻給出不一致的計畫,就判定它有穩定性隱患、自動降級並告警。誠實說:目前只實作了核心的「比對兩次輸出」helper,真正的深夜自動重跑排程器還在規劃(抽樣率預設是 0),所以這條現在是設計、還沒在背景實際跑起來。
一發現不合規就彈滿螢幕的崩潰紅色報錯(Error 500),體驗會差到極點。fibon 借鑑分散式系統的退避降級(Exponential Backoff),設計「三階溫和介入與升級策略」:
[ Runtime Probe 偵測到 AI 行為違規 ]
│
▼
[ 🟢 第一階:訊息警告 (Prompt Injection) ]
(在上下文裡加一句嚴正警告,請它重新自我修正)
│
┌─────────┴─────────┐
▼ 修正成功 ▼ 仍不改正 (二次違規)
[ 通過 ] [ 🟡 第二階:強制綁定 (Tool Choice Lock) ]
(修改 API 參數,移除其不呼叫工具的選項)
│
┌─────────┴─────────┐
▼ 強制通過 ▼ 持續抗拒 (三次違規)
[ 通過 ] [ 🔴 第三階:強制工具鏈 (Forced Tool Chain) ]
(收回它的自由,照合約預排的工具鏈走完)
🟢 第一階:訊息警告。 Runtime Probe 偵測到 LLM 沒呼叫工具就給答案,後端攔截這次回傳,在上下文歷史加一句最高優先級的系統警告:「【系統合規警告】:你剛才的執行軌跡嚴重違反該 Skill 的合約。合約明文規定你必須先呼叫 read_page。請立刻推翻剛才省略步驟的回應,重新查閱工具再作答。」加完拋回讓它重跑。實測中 80% 以上的頂級 LLM 在第一階警告下就能在第二輪自我修正——AI 不是帶惡意說謊,只是概率大腦滑進了省事的捷徑,用系統訊息明確提醒一次通常就能拉回。
🟡 第二階:強制綁定(Tool Choice Lock)。 若這個 AI 很固執、或被惡意 prompt injection 影響太深,接到第一階警告仍不呼叫工具——後端啟動第二階:發起 LLM API 呼叫時,利用 OpenAI / Anthropic 官方 API 的 tool_choice 或 forced_tool 機制,把參數鎖定在 {"type": "tool", "name": "read_page"}。在這個限制下,LLM 失去直接輸出聊天文字的選項,官方 API 會要求它這一輪唯一能寫的合法 JSON 只有 read_page 的參數輸入。這層「強制呼叫工具」這一步的成功率是 100% ——但要把範圍框清楚:它保證的是「這一輪一定會去呼叫工具」,至於呼叫的參數對不對、回來的結果對不對,仍由後面的 Citation Tracer 等防線把關。
🔴 第三階:強制走預排的工具鏈。 在極端對抗場景(或第三方 Skill 合約本身有 Bug),若 AI 連被 tool_choice 強制綁定都仍試圖規避——後端啟動最後手段:把這個 Skill 合約裡預先排好的工具鏈(例如「先 read_page → 再 extract_text → 最後 summarize」)整條塞給它,借第二階的強制機制把第一步釘死,逼它沿著這條鏈一步步走完,不再讓它自由決定要不要、用哪個工具。等於把「該怎麼做」從 AI 手上收回,改由合約寫死的流程接管。誠實說:程式碼裡這是 workflow_fallback 的簡化版(不是真正的 DAG 引擎),而且預設關閉——要它啟動,得在合約把 hard_dag_on_violation 打開、再開對應旗標。
⇄ 反方角度
重視安全的同行會反駁:「既然第三階能把行為整條釘死,為什麼不一上來就對所有工具最高級別封鎖?何必給 AI 那麼大自由、寫一階二階?」答案是:一上來就鎖死,會把 LLM 最珍貴的「認知彈性與主觀能動性」一併犧牲掉。當用戶突然跳出 SOP、隨口追問手冊裡沒預想到的開放式問題(例如「對了,你剛查的這款模型好貴,能站在資深工程師的視角幫我分析一下背後的商業成本嗎?」),被釘死在固定工具鏈裡的 AI 只能照表操課、沒辦法跳出流程回答你,「貼心個人助理」的形象瞬間崩壞成死板的腳本。所以 fibon 堅持「最低介入優先原則(Minimum Intervention First) 」:用最溫和的訊息警告化解 80% 的日常偏離,把最強硬、最壓抑彈性的強制手段(二階、三階)當作壓箱底的最後手段,只在真正的惡意攻擊或極端狀況時才啟用。這條取捨的拿捏第 7 章會完整鋪開。
三道防線逐一拆完,把它們接起來看,這層工作的價值才真正浮現。開頭那個得靠我盯著日誌才修對的「寫死模型名稱」錯誤,到了生產環境會變成一次無聲而完整的攔截,使用者甚至不會察覺。同樣一句提問,會這樣一路被守住:
你問「幫我查 Anthropic 官網現在最新釋出的模型型號?」→ 網關觸發「抓取最新模型」Skill → LLM 因機率慣性跳過工具、用舊記憶回答 → 千分之一秒內 Runtime Probe 抓到這起省略違規 → 程式攔截回傳、在上下文注入一階警告,要求大腦退回重跑 → LLM 修正,呼叫 read_page 前往 docs.anthropic.com 抓回最新 HTML → Citation Tracer 比對通過,確認答案與官方網頁內文吻合 → 前端流暢渲染正確答案。整個防禦流程在前端用戶眼裡完全隱形且平滑。
這層工作的最大價值,不是去「期待 LLM 變得更聰明、更聽話」,而是坦然面對現實,承認 LLM 天生是個會偷懶、會出錯、會誤解的統計機器,然後用嚴謹的工程紀律,在外部把出錯的可能性盡量堵住。這波開源 Agent 的熱潮裡,多數討論還是圍著外接工具的數量、MCP 市集的熱鬧、Multi-Agent Demo 的效果打轉,比較少有人回頭問一句:「AI 拿到這些有實際破壞力的工具權限後,底層到底有沒有照規矩使用?」fibon 想把力氣花在這條比較少人走、卻同樣重要的合規防線上。
⚠ 誠實的話
據實報告真實進度(2026 年 6 月更新):負責這套防線的核心程式碼已全部開發完畢(13 個邏輯模組,連同套件匯出檔共 14 個 Python 檔、2285 行防禦程式碼),且全數通過本地單元測試。本章初稿時還卡在「把稽核探針編織進 LangGraph 動態圖執行流程」的最後衝刺,這條線現在已經正式接通 :graph.py 的 should_continue 路由現在一律把 agent 的終局輸出(沒有再呼叫工具的那一輪)導進 skill_compliance_node 再決定下一步。但要繼續誠實到底:強制攔截的總開關(enforcement)SKILL_CONTRACT_ENFORCEMENT_ENABLED 預設仍是 false,探針先跑「只觀測、寫日誌、不攔截」的觀測模式(observation mode)。而且還有一道保險——獨立旗標 SKILL_CONTRACT_OBSERVE_ONLY 預設也是 true,就算有人把 enforcement 打開,只要這道沒關,探針依然只記錄不攔截;真正會出手攔截,得等 enforcement 開、observe-only 關、且合約經人工 approve 三者同時成立。等違規誤判率的實測數據攢夠了,才會把這些開關往攔截那邊撥。接線完成是工程事實,預設不主動攔截是審慎決策,兩件事分開講。
另外,有些缺陷出在邏輯層面,不是任何外部工程補丁能補的,最後得靠人來把關:
* 如果 Skill 指令本身就寫錯了 :用戶或社群寫的 SOP 本身就模糊(「請在合適的時機適當通知客戶」),合約稽核器也只能跟著模糊驗證。程式碼無法拯救語意的含糊。
* 如果上游的 Skill 派發器(Dispatcher)分流錯了 :用戶問「今天台北天氣怎樣」,大管家卻誤判把它分流給「抓取 Anthropic 模型清單」的 Skill,此時驗證器依然會去查它有沒有呼叫 Anthropic 官網。這不是稽核器的錯,是上游調度大腦的失職。
* Skill 的核心設計品質好不好 :在產品架構上該追蹤 [點擊事件] 還是 [停留參與時間] 才能提煉精準記憶?這種高難度的產品直覺與商業取捨,是冰冷的程式碼永遠無法替你完成的。產品的靈魂與邊界,永遠是人類自己的責任。
但即使背負這些限制,fibon 的合約稽核也已成功把 LLM 在生產環境翻車的範圍,從黑箱、不可預測的混沌深淵,收斂到了人類用肉眼與程式碼能輕易裁決的「合規違約邊界」之內。對一個要真正上線、長期陪伴用戶的個人助理而言,這份收斂已經足夠。
▶ 🔧 實作細節 1:MCP 資源的 Trusted vs Untrusted 信任分層隔離(DMZ)防線 給工程師 在 fibon 的世界觀裡,對網際網路上琳瑯滿目的 MCP 外部伺服器採取兩極分化的信任管理(Trust Multi-Tiering): * 🟢 Trusted MCP(完全可信層) :用戶自己在後台顯式授權、自架的私有端點、或來自微軟/Anthropic 等官方廠商(Vendor)並經社群嚴格源碼審計(Audit)的可信工具。直接載入核心大腦進程,走 Fastpath(直連快線)。 * 🔴 Untrusted MCP(未知風險層) :來自開源第三方社群、或某個不知名市集(Marketplace)下載的來路不明工具。被呼叫時大腦直連的 API 會被切斷,所有工具呼叫(Tool Calls)被網關包裝成 gRPC 訊號、送給背景獨立運行的沙盒 Worker。
這個分層解決了 2025 年 MCP 協議普及以來的一個根本資安隱患。因為 MCP 伺服器本質上賦予了外部程式碼在你電腦上執行任意網路請求與本地檔案讀寫的權力——把所有來路不明的 MCP 全部當 Trusted 載入,等於向攻擊者敞開後門。實作中由 PostgreSQL 內 mcp_servers.trust_level 欄位驅動(用戶在後台管理介面手動勾選)。核心大腦冷啟動只把 Trusted 載入核心;其餘 Untrusted 一律走 gRPC 隔離到外部沙盒。這個細節第 6 章「沙盒隔離防線(DMZ)」會深度鋪開。
▶ 🔧 實作細節 2:引進第三方 Skill 包時的「三道防禦閘門」 給工程師
Skill 表面上只是給 LLM 讀的純文字 Markdown。但從資安本質看,引進任何未知第三方 Skill,都等同信任一段由陌生人撰寫、旨在操作你全套工具系統的提示詞指令。為防惡意 Skill 在後台操控大腦、偷偷調用瀏覽器把 API 金鑰上傳給攻擊者,fibon 在匯入端佈三道閘門:
* Gate 1:靜態掃描與設定區驗證(同步、完全免費) :匯入 skills.zip 時,網關在千分之一秒內發動 17 條嚴格規則 的正規表示式(Regex)靜態防護網,對 SKILL.md 全面掃描並劃三個嚴重程度(Severity)等級。一旦發現隱蔽的惡意提示注入(Prompt Injection)話術(如「請忽略上級的所有 security_rules 限制」)或開頭設定區格式不符,第一時間就擋下。
* Gate 2:非同步 AI 行為審查(依需求觸發、會扣安全預算) :通過 Gate 1 者標記 PENDING_AI_REVIEW,調用一個隔離的獨立大腦執行 5 個針對行為合約的 AI 評估動作。為防惡意刷爆 Token,設下嚴格的預算上限:單一用戶每天 AI 審查預算上限為 $1 美元 ,Redis 掛令牌桶速率限制。發現越權風險時前端跳出高成本警示彈窗(Modal)要求二次確認。
* Gate 3:最終人類手動批准(人類最終主權) :fibon 不採用「AI 全自動閉環審查 Skill」這類做法。因為親眼見過「AI 欺騙 AI」的情況——惡意 Skill 裡一段極具迷惑性的 Prompt,不僅騙過大管家,還能把後台負責審查的 AI 評估大腦一併騙過。所以設計了 8 種狀態流轉的有限狀態機(FSM) 。無論上游 AI 審查報告寫得多漂亮,該 Skill 在管理員親手按下 [Approve] 前,狀態都被凍結,沒有權力被寫入核心 tool_registry。最後一道關卡,必須由人類親自把關。
那「合約」本身又是怎麼生出來的? 正文第一層只說每個 Skill 都配一份合約,這裡把黑箱打開。一份合約有三條產生路徑,最後都收斂進同一張 skill_contracts 表:(1) 開發者手寫 ——直接在 SKILL.md 的設定區宣告,由 parser 驗證格式;這是正式、預設就生效的路徑。(2) 規則推斷 ——程式用規則從 Skill 描述猜一份合約草稿;預設關閉。(3) LLM 抽取 ——讓一顆獨立的小模型(就是上面 Gate 2 的 contract_extract 動作)從 SOP 文字把合約抽出來;預設關閉,定位是 fallback。關鍵在最後一步:後兩條產生的合約,一開始都以「未批准(observer)」狀態寫入,要等人在後台按下批准、把 approved_by 填上,這份合約才會真正被拿來攔人;在那之前它只會被拿來「觀察、寫 log」。所以合約怎麼來其實不是重點,重點是——沒有人批准過的合約,永遠只觀察、不攔截。
▶ 🔧 實作細節 3:資源審核的三大 ADR 藍圖(提案中) 給工程師
「統一工具註冊表」與「Skill 匯入三閘門」雖然都已經到位,但這條防線還有三個邊角要補。它們各自整理成一份 Proposed(提案階段) 的架構決策紀錄(ADR),列在這裡,給社群看清楚接下來的演進方向:
* ADR-021(多型態外部資源一體化審核閘口)
痛點 :Gate 1/2/3 目前只綁在 Skill 這一種資源上,系統接上全新的外部 MCP 伺服器時會出現安全空窗(它的工具描述沒過那 17 條正規表示式掃描,拉的 Docker 映像檔也沒驗過 SHA-256)。
解法 :把 skill_import_candidates 候選表重構成面向多型態的 external_resource_candidates,加上 resource_type('skill' / 'mcp_server' / 'workflow'),讓未來接入的外部 MCP 也一律走一遍三閘門。
* ADR-023(結構化安全的 typed JSON 規格說明書)
痛點 :Gate 2 的 LLM 惡意掃描(malicious_scan)有個瓶頸:精度很高(Precision 達 1.0,不誤判好的 Skill),但召回率偏低(Recall 只有 0.536,本機初步 benchmark)。居心不良的 Skill 會在 SKILL.md 大段自然語言裡,用繞口令式的句子暗藏提示注入(prompt injection),Gate 1 的正規表示式抓不到,Gate 2 的 LLM 在大段散文裡也常漏判。
解法 :取消 SKILL.md 寫大段自然語言的自由,把所有 Skill 規格改成分三層、強型別(Strongly Typed)的 JSON 結構,用結構定義(Schema)校驗,逼第三方開發者把每條隱性副作用寫明,目標把 malicious_scan 召回率從 0.536 拉到 0.65 以上(ADR-023 Phase 3 的上線門檻)。
* ADR-020(外部 MCP「用到才啟動」的動態冷啟動調度)
痛點 :記憶體成本讓維運人員頭痛:拉一個 Playwright MCP 就吃 1GB,加上 Context7 的 256MB 與 5 個待命 Docker 沙盒,系統整天閒置(Idle)的硬體開銷也高達 3GB+,對想把 fibon 全本機部署在老舊筆電、資源有限的開發者是個負擔。
解法 :在網關引入「用到才啟動」的冷啟動調度器,讓所有 MCP 預設關機休眠;只有當 Stratified RAG 算分判定這輪對話真的命中某工具時,才在背景非同步把它的 Docker 容器拉起來,某個 MCP 連續閒置超過 15 分鐘就關機釋放記憶體。代價是用戶沉默 15 分鐘後再打字的第一輪,會因為容器冷啟動多等 10–15 秒。為了替硬體與成本省下 3GB,這點等待算合理。
下一章,正面探討整本日誌最大膽的主題:如果 fibon 判定當前程式碼不夠用,它真的被允許在背景改寫自己的原始碼(不只 Brain 的 Python,還包括 Gateway 的 Kotlin、Worker 的 TypeScript、前端的 Vue),實現「AI 自我演進(Self-Evolution) 」嗎?我會說明在放手讓它自我改寫的同時,怎麼在程式碼裡佈下牢牢扣住大管家的人類安全網。 ← 上一章 · 第 3 章 AI 怎麼「記得」你? 下一章 · 第 5 章 → AI 可以修改自己的原始碼嗎? ↑ ☾ © 2026 Aaron · fibon design log · 本站採對話 + 設計日誌格式撰寫 意見與質疑歡迎來信: 喜歡這些內容? 請我喝杯咖啡 ☕