六個月前,我寫了關於香港 AI 設定的文章,內容涵蓋電話號碼、DNS 路由、付款變通方案。當時的瓶頸是地理位置。你甚至能連上工具嗎?
現在那個問題已經解決了。一台 5 美元的 VPS 加上 Shadowsocks 就能輕鬆存取。工具到處都是。但解決存取問題並沒有讓事情變簡單,反而浮現了下一個問題。當你能存取所有模型、執行所有 agent 時,困難的問題就不再是「我能連上嗎?」,而是「什麼該連到什麼,由誰決定?」一個瓶頸消失了,取而代之的是十幾個協調問題。
這就是翻轉。失敗模式轉移到環境內部。存取問題解決了;協調與信任才是真正的挑戰。正如我在〈Agent 需要架構,不是 Prompt〉中所主張的,真正的失敗不是來自你在 prompt 裡寫了什麼,而是來自你的架構允許了什麼。我接下來要描述的技術棧,就是當我認真看待這個主張並將其應用到自己的環境時,所長出來的東西。
這個翻轉是漸進的。每一項新能力都移除了一個外部限制,同時揭露了一個內部限制。付款變通方案解鎖了模型存取。模型存取揭露了一個 agent 無法同時處理多個上下文。拆分 agent 揭露了一個記憶系統無法服務三種不同的回憶。拆分記憶揭露了工具鏈本身需要分類。每個解決方案都創造了下一個問題,而技術棧也隨之成長。
四個瓶頸依序驅動了這個成長:上下文碰撞——當一個系統提示試圖容納所有東西,領域互相滲透。記憶碎片化——當單一嵌入儲存無法回答三種不同的問題。工具蔓延——當不斷增長的執行環境、模型和訂閱方案列表變成一個等待發生的分類錯誤。信任模糊——當本地和代管元件並存,卻沒有明確的邊界。
以下就是浮現出來的技術棧。它在紙面上看起來很複雜:三個 agent 設定檔、三個記憶系統、一個模型路由器、一個協調器、一個 ADE,以及橫跨執行環境、模型和訂閱方案的工具鏈。沒有一個元件是因為我在收集而存在的。每一層都是因為先嘗試了更簡單的替代方案,但失敗了。
臨界點出現在一個系統提示試圖容納所有東西的時候——運維指令、寫作風格指南、社群媒體角色、營養追蹤邏輯,全部互相拉扯。
兩種污染模式讓單一 agent 方法變得不可行。第一,社交上下文洩漏到私人的運維決策中。先前互動的對話語氣會滲入基礎設施指令。Agent 並沒有當機,而是產出看起來正確但稍微錯誤的輸出——這是最難察覺的失敗,因為沒有任何東西會警示你。你事後才會發現,當某個決策浮現時,它已經被錯誤的上下文軟化了。
第二,營養的專用邏輯與治理規則衝突。卡路里追蹤和部署政策放在同一個 prompt 裡,兩個領域都沒有得到完整的關注。系統提示變成了競爭利益之間的協商,而 agent 正在輸掉這場協商,而且只有在事後才會顯現出來。
解決方案是結構性的:為不同的關注點設立不同的 agent。一個系統提示不足以構成邊界。跨領域共用一個 prompt 會讓 agent 的上下文變得可滲透——無論你多麼仔細地撰寫指令,領域之間還是會互相滲透。一個 prompt 可以說「不要混合這些上下文」,但結構性的拆分讓混合變得不可能。
所以我將一個 agent 拆分為三個設定檔,每個都是 Hermes(Nous Research 的開源 agent 框架,作為技術棧的協調器)的一個配置。它們不是獨立的程式,而是不同的角色,擁有不同的工具存取權限,在同一個執行環境上執行。
Norty 是私人的、完整權限的設定檔。負責治理、程式開發、運維、基礎設施。使用 OmniRoute 路由,預設模型為 GLM 5.2,透過 Ollama Cloud Pro 提供。Norty 擁有所有權限,因為 Norty 的範圍是我建構和操作的所有東西。沒有社交上下文,沒有營養邏輯,只有工作。
Nora 是對外的設定檔:社群、朋友圈,工具有限。SearXNG 用於搜尋,Firecrawl 用於網頁爬取。Nora 不是權限較少的 Norty——她是一個獨立的角色,有明確的職責。工具限制就是重點。Nora 不需要基礎設施存取權限,給她反而會造成範圍違規。
Bento 是單一用途的設定檔:營養追蹤。Telegram 介面,SQLite 資料庫。沒有模型路由複雜度,沒有治理規則,沒有社群媒體。Bento 只知道卡路里和巨量營養素,其他一概不知。當 Bento 做出決策時,完全不可能受到運維上下文或寫作風格指南的影響——那些上下文在 Bento 的世界裡根本不存在。Bento 無法洩漏它不包含的東西。
每個設定檔都獲得一組不同的工具集。一個設定檔能存取的工具定義了它能做什麼,而且沒有另外疊加一層權限系統。工具列表就是邊界。這是技術棧第一次撞上兩個結構性障礙:權限——誰能做什麼——以及範圍——防止上下文互相污染。設定檔拆分之所以存在,是因為一個 agent 試圖無所不在,結果到處都不可靠。
拆分設定檔解決了上下文污染,但浮現了下一個瓶頸:記憶。
單一記憶系統無法服務三種不同的回憶。一個為對話相似度而建的平面嵌入儲存,當被問到「三個月前為什麼做出那個架構決策?」時,會回傳語義上最相似的對話,而那可能是關於完全不同專案的討論,只是用了相似的詞彙。程式碼庫的結構和決策的時間線對它來說是不可見的。失敗不在於它什麼都沒回傳,而是它回傳了看似合理但錯誤的內容。
Agent 需要三種回憶粒度:跨 session 的對話連續性、對程式碼庫的結構理解,以及關於過去決策的時間推理。這些是不同的問題。一個記憶系統無法涵蓋全部。因此技術棧有三個記憶系統,各自對應不同的回憶範圍。
Honcho 處理跨 session 的對話記憶。透過 Docker 自架,基於對等架構,具備辯證推理能力,能回答「我們上週聊了什麼?」它直接與 Hermes 整合,讓每個設定檔在 session 之間有連續性,同時不會在設定檔之間洩漏上下文。對話記憶無法告訴你哪個模組裡有哪個函式,因為它本來就不是設計來做這個的。
Graphify 處理程式碼結構索引。它建立基於圖的程式碼庫知識表示,顯示哪些函式呼叫哪些函式、哪些模組依賴哪些模組、邊界在哪裡。它取代了已棄用的 Codebase-Memory MCP。當程式碼 agent 需要在修改前了解架構時,Graphify 提供那張地圖。但程式碼索引無法重建上週的推理;它知道結構,不知道歷史。
Graphiti 處理時間知識。由 Zep 建構,是一個基於 Neo4j 的雙時間知識圖譜,同時追蹤有效時間(某件事為真的時間)和系統時間(我們得知它的時間)。它填補了其他兩個系統無法觸及的缺口:某個時間點的知識狀態,包括在當時已知的背景下為什麼做出那個決策。但時間知識圖譜無法容納當前的對話上下文。它是為跨時間推理而建,不是為 session 內的連續性。
這不是三個競爭的解決方案。它們是三個系統,對應三種不同的回憶粒度,類似於一個大腦中的短期、長期和程序記憶。每一個之所以存在,是因為單一記憶系統被要求涵蓋全部三種時,回傳的答案看似合理但錯誤。
一旦設定檔拆分、記憶專門化之後,下一個瓶頸浮現了:工具蔓延。元件的列表不斷增長,包括執行環境、模型、訂閱方案、路由器、協調器、ADE,而把它們列出來卻不說明它們之間的關係,造成了分類混淆。模型不是執行環境,訂閱方案不是路由器,協調器也不是這些東西。問題不再是「技術棧裡有哪些工具?」,而是「什麼連到什麼,以及什麼不連?」
以下是所有 agent 層級的元件,按類別和功能分組。
| 元件 | 類別 | 功能 |
|---|---|---|
| Claude Code CLI | 執行環境/CLI | 主要開發、深度推理、多檔案修改 |
| Kimi Code CLI | 執行環境/CLI | 次要、快速修補、外部審查 |
| OpenCode CLI | 執行環境/CLI | 供應商無關的開源程式碼 agent(MIT 授權,GitHub 約 165K 星) |
| Codex CLI | 執行環境/CLI | 僅驗證與程式碼審查,稽核路徑 |
| GLM 5.2 | 模型 | Norty 的預設模型,透過 Ollama Cloud Pro 提供 |
| Kimi K3 | 模型 | 零樣本程式碼模型,透過 Kimi Code CLI 在 Allegro 方案存取 |
| MiniMax M3 | 模型 | 次要輔助,100 萬 token 上下文,透過直接 API 和 OpenCode Go |
| OpenCode Go | 訂閱方案 | 每月 10 美元,14 個模型,與所有供應商簽訂 ZDR 協議 |
| Ollama Cloud Pro | 訂閱方案 | 代管 Ollama 層,提供 GLM 5.2 和開放權重模型 |
| Claude Max 20x | 訂閱方案 | 每月 200 美元,解鎖 Claude Code |
| Kimi Allegro | 訂閱方案 | 每月 99 美元,解鎖 Kimi Code |
| OmniRoute | 模型路由器 | 自架於 localhost:20128,依任務和成本路由 |
| Hermes | 協調器 | 管理設定檔、記憶、排程、多平台閘道 |
| Orca | ADE | 在隔離的 git worktrees 中平行執行 agent,自備 agent CLI |
| Cortex | 編輯管線 | 第二個獨立的 OmniRoute 消費者,與 Hermes 不同 |
類別防止了分類錯誤。OpenCode CLI 是一個免費、MIT 授權的執行環境。OpenCode Go 是一個付費訂閱方案,同一個品牌,但不同類別。OmniRoute 是路由器,不是執行環境。Orca 是 ADE,不是 IDE。模型不是執行環境,訂閱方案不是模型。把它們全部扁平化成一個列表,會失去讓拓撲可讀的資訊。
但類別之所以有趣,是因為它們之間的邊。
三個設定檔各自作為 Hermes 的配置執行。當一個設定檔需要模型時,Hermes 呼叫 OmniRoute,後者根據任務和成本選擇供應商和模型。Cortex(驅動這個網站內容工作流程的編輯與雷達管線)是第二個獨立的 OmniRoute 消費者。它不經過 Hermes。Hermes 和 Cortex 是兄弟關係,不是鏈狀。
Orca 將程式碼 CLI 作為平行 agent 在隔離的 git worktrees 中產生。Claude Code 是主要的:深度工作、多檔案專案。Kimi Code 是次要的:快速修補。OpenCode CLI 是供應商無關的備用方案。每個程式碼 CLI 直接呼叫自己的供應商:Claude Code → Anthropic(Claude Max 20x),Kimi Code → Kimi Allegro。這些不經過 OmniRoute 路由。它們是各自訂閱方案下的供應商產品。
Codex 僅用於審查,有三個介面:Claude Code 內部的 Codex MCP 用於 session 內審查、獨立 CLI 用於直接審查、以及由 PR 事件觸發的 GitHub 應用程式。第三個介面值得注意:它是由儲存庫事件觸發的,而不是由技術棧中的任何元件呼叫。一個沒有被任何元件呼叫的審查者,連接到儲存庫事件而不是呼叫圖。
程式碼層級:Claude(主要)> Kimi(次要)> Codex(輔助,僅審查)> MiniMax M3(輔助,廣上下文助手,非日常使用)。
然後是那些非連線,它們和連線一樣重要,因為它們是拓撲設計要防止的錯誤。
OmniRoute 不路由程式碼 CLI。 它們是各自訂閱方案下的供應商產品。OmniRoute 服務的是我建構的東西——透過 Hermes 的設定檔和 Cortex——而不是我購買的東西。
沒有東西呼叫 Orca。 它是一個手動操作的環境,不是自動化鏈中的服務。Orca 向外呼叫,產生 CLI;沒有東西向內呼叫。
不存在 OmniRoute → Orca → Codex 管線。 這三個東西位於不同的層面:一個路由器、一個手動操作的環境、一個審查者。它們不形成鏈。
這個拓撲不是因為我在收集元件而存在的。它是因為工具蔓延迫使我們給出結構性答案。當你同時擁有執行環境、模型、訂閱方案、路由器、協調器和 ADE 時,唯一能把它們理清的方法,就是知道每個東西是什麼、它連到什麼、以及它不連到什麼。IDE 是給你的。ADE 是給你和你的 agent 的。 這就是 Orca 的區別。IDE 承載你的 session。ADE 平行承載多個 agent session,每個都在自己的隔離工作目錄中,各自有自己的執行環境和模型指派。
第四個瓶頸是信任模糊。當本地和代管元件並存,卻沒有明確的邊界時,預設假設會變成「全部私有」或「全部暴露」,而兩者都是錯的。
技術棧橫跨兩個世界。有些元件在本地的 GMKtec NucBox M7 Ultra(一台 AMD Ryzen 7 PRO 6850U 迷你 PC,64GB DDR5)上執行。其他則是代管服務,有自己的政策。邊界不是一個可以切換的功能。它是每個元件所在位置以及它能觸及範圍的結構性屬性。
| 元件 | 執行位置 | 信任模型 |
|---|---|---|
| Ollama BGE-M3 | 本地 | 完全私有,僅嵌入,無網路路由 |
| OmniRoute | 本地(路由器) | 橋接兩邊,依任務路由到本地或代管 |
| OpenCode Go | 代管(第三方) | 依服務而定,已與所有供應商簽署 ZDR 協議 |
| Claude Code | 代管(Anthropic) | 依服務而定,受 Anthropic 資料政策規範 |
| Kimi Code | 代管(Moonshot) | 依服務而定,受 Kimi 資料政策規範 |
Ollama BGE-M3 真正在本地執行,處理語義搜尋和程式碼索引的嵌入,沒有網路路由。那就是私有。OpenCode Go 是代管服務。模型推理透過他們的 API 進行,雖然他們已與所有供應商簽署零資料保留協議,但信任模型與本地程序不同。OmniRoute 橋接兩邊:它可以路由到本地的 Ollama 進行嵌入,或路由到代管後端進行推理,取決於任務。技術棧中沒有一概而論的「零資料保留」聲明。每個服務都有自己的政策。重要的是知道哪個是哪個。
信任邊界也在設定檔層級強制執行。每個設定檔的工具集定義了它能存取什麼,而那個工具集就是它的信任邊界。Norty 有完整權限,擁有所有鑰匙,符合其範圍。Nora 只有 SearXNG 和 Firecrawl,對外,沒有基礎設施存取權限。Bento 只有 Telegram 和 SQLite,信任邊界最小,無法存取模型或基礎設施。
對外的存取控制層是一個單一整合的元件:Cloudflare Tunnel 加上 Google SSO,以 Google Workspace 作為身份提供者進行閘控。cloudflared 建立一個對外連線到 Cloudflare 的邊緣,沒有入站埠,沒有在公開介面上監聽。Cloudflare Access 放在前面,只有通過 Google Workspace 認證的身份才能通過。免費方案,最多 50 個使用者。
信任不是一次設定就好的設定。它是在每一層劃定的邊界:元件在哪裡執行、設定檔能存取什麼、以及網路允許什麼進入。技術棧不做一概而論的隱私聲明。它做出明確的邊界,而每個邊界都有理由。
這個技術棧看起來很複雜。三個設定檔、三個記憶系統、執行環境和路由器與訂閱方案的拓撲、多層次的信任邊界。自然的問題是:為什麼需要這麼複雜?
答案不是個人偏好。而是每個多 agent 設定最終都會撞上的三道結構性障礙。
第一道牆:權限。 誰能做什麼?當一個 agent 有所有權限時,問題很簡單。答案是「所有東西」。但一旦你拆分為設定檔,權限就變成設計問題。Norty 可以部署基礎設施。Nora 不行。Bento 甚至無法存取模型。工具列表就是權限邊界,而且必須透過結構強制執行,而不是透過一個說「不要使用這些工具」的 prompt,而是透過讓使用它們變得不可能的設定。
第二道牆:範圍。 每個 agent 持有什麼上下文?單一 prompt 方法失敗,因為範圍有漏洞。社交上下文洩漏到運維,營養與治理衝突。拆分為設定檔解決了 agent 層級的範圍問題,但浮現了記憶層級的範圍問題:一個記憶系統無法服務三種回憶粒度。範圍不只是讓 agent 彼此分開,而是讓每個元件只持有它需要的上下文,沒有其他。
第三道牆:驗證。 你怎麼知道輸出是正確的?當一個 agent 產生所有東西時,驗證是臨時的。你閱讀輸出並判斷它是否正確。當多個 agent 產生的輸出互相餵入時,驗證需要結構。Codex 作為僅審查路徑存在,與主要程式碼 CLI 分開。拓撲中的非連線——OmniRoute 不路由程式碼 CLI、沒有東西呼叫 Orca、不存在 OmniRoute → Orca → Codex 管線——就是驗證邊界。它們確保寫程式碼的元件不是審查它的元件。
這三道牆直接對應到四個瓶頸。上下文碰撞是範圍問題,領域互相滲透,因為邊界是 prompt 而不是結構。記憶碎片化是不同粒度的範圍問題,一個儲存被要求服務三種回憶。工具蔓延是權限問題,當元件沒有分類、它們的邊不明確時,你不知道誰能存取什麼。信任模糊是權限和驗證問題,沒有本地和代管之間的明確邊界,沒有設定檔範圍的工具存取,你只能猜測什麼是私有的。
這些牆不是 bug。它們是無論你是否承認都存在的限制。技術棧就是當你不再繞過它們,而是開始與它們一起建構時所產生的結果。
這篇文章描述了技術棧。下一篇文章將描述當你正面撞上每道牆時會發生什麼:失敗模式、設計決策,以及當權限、範圍和驗證成為首要關注事項而非事後想法時所浮現的模式。