---
title: "AI 工具鏈的翻轉：從存取到架構"
description: "AI 工具的存取問題已經解決。現在真正的挑戰是：協調三個 agent、三種記憶系統，以及信任邊界。這是一個能擴展的技術棧。"
pubDate: 2026-07-21
tags: ["ai-agents", "memory-granularity", "toolchain-design", "governance"]
locale: zh-tw
canonical: https://mikeng.io/zh-tw/writing/inversion-of-ai-tooling-from-access-to-architecture/
---

六個月前，我寫了關於[香港 AI 設定](https://mikeng.io/writing/hk-ai-tools-setup-2026/)的文章，內容涵蓋電話號碼、DNS 路由、付款變通方案。當時的瓶頸是地理位置。你甚至能*連上*工具嗎？

現在那個問題已經解決了。一台 5 美元的 VPS 加上 Shadowsocks 就能輕鬆存取。工具到處都是。但解決存取問題並沒有讓事情變簡單，反而浮現了下一個問題。當你能存取所有模型、執行所有 agent 時，困難的問題就不再是「我能連上嗎？」，而是「什麼該連到什麼，由誰決定？」一個瓶頸消失了，取而代之的是十幾個協調問題。

這就是翻轉。失敗模式轉移到環境內部。存取問題解決了；協調與信任才是真正的挑戰。正如我在[〈Agent 需要架構，不是 Prompt〉](https://mikeng.io/writing/agents-need-architecture-not-prompts/)中所主張的，真正的失敗不是來自你在 prompt 裡寫了什麼，而是來自你的架構允許了什麼。我接下來要描述的技術棧，就是當我認真看待這個主張並將其應用到自己的環境時，所長出來的東西。

這個翻轉是漸進的。每一項新能力都移除了一個外部限制，同時揭露了一個內部限制。付款變通方案解鎖了模型存取。模型存取揭露了一個 agent 無法同時處理多個上下文。拆分 agent 揭露了一個記憶系統無法服務三種不同的回憶。拆分記憶揭露了工具鏈本身需要分類。每個解決方案都創造了下一個問題，而技術棧也隨之成長。

四個瓶頸依序驅動了這個成長：**上下文碰撞**——當一個系統提示試圖容納所有東西，領域互相滲透。**記憶碎片化**——當單一嵌入儲存無法回答三種不同的問題。**工具蔓延**——當不斷增長的執行環境、模型和訂閱方案列表變成一個等待發生的分類錯誤。**信任模糊**——當本地和代管元件並存，卻沒有明確的邊界。

以下就是浮現出來的技術棧。它在紙面上看起來很複雜：三個 agent 設定檔、三個記憶系統、一個模型路由器、一個協調器、一個 ADE，以及橫跨執行環境、模型和訂閱方案的工具鏈。沒有一個元件是因為我在收集而存在的。每一層都是因為先嘗試了更簡單的替代方案，但失敗了。

## 一個 Agent，三個設定檔

臨界點出現在一個系統提示試圖容納所有東西的時候——運維指令、寫作風格指南、社群媒體角色、營養追蹤邏輯，全部互相拉扯。

兩種污染模式讓單一 agent 方法變得不可行。第一，社交上下文洩漏到私人的運維決策中。先前互動的對話語氣會滲入基礎設施指令。Agent 並沒有當機，而是產出看起來正確但稍微錯誤的輸出——這是最難察覺的失敗，因為沒有任何東西會警示你。你事後才會發現，當某個決策浮現時，它已經被錯誤的上下文軟化了。

第二，營養的專用邏輯與治理規則衝突。卡路里追蹤和部署政策放在同一個 prompt 裡，兩個領域都沒有得到完整的關注。系統提示變成了競爭利益之間的協商，而 agent 正在輸掉這場協商，而且只有在事後才會顯現出來。

```mermaid
flowchart TD
    S["單一系統提示\n運維 + 寫作 + 社交 + 營養"]
    S -->|"上下文碰撞"| BREAK["污染：\n社交洩漏到運維\n營養與治理衝突"]
    BREAK --> SPLIT["拆分為三個設定檔"]
    SPLIT --> NORTY["Norty\n私人、完整權限\n治理 / 程式開發 / 運維 / 基礎設施\nOmniRoute → GLM 5.2"]
    SPLIT --> NORA["Nora\n對外\n社群、朋友圈\nSearXNG + Firecrawl"]
    SPLIT --> BENTO["Bento\n單一用途\n營養追蹤\nTelegram + SQLite"]
```

解決方案是結構性的：為不同的關注點設立不同的 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。模型不是執行環境，訂閱方案不是模型。把它們全部扁平化成一個列表，會失去讓拓撲可讀的資訊。

但類別之所以有趣，是因為它們之間的邊。

```mermaid
flowchart TD
    NORTY["Norty"] --> HERMES
    NORA["Nora"] --> HERMES
    BENTO["Bento"] --> HERMES

    HERMES["Hermes"] -->|"經由"| OMNI["OmniRoute"]
    CORTEX["Cortex"] -->|"經由"| OMNI

    OMNI -->|"到"| MODELS["GLM 5.2, DeepSeek V4, Kimi K3, ..."]

    ORCA["Orca ADE"] -->|"產生"| CC["Claude Code\n主要"]
    ORCA -->|"產生"| KC["Kimi Code\n次要"]
    ORCA -->|"產生"| OC["OpenCode CLI"]

    CC -->|"直接計費"| ANTHROPIC["Anthropic"]
    KC -->|"直接計費"| KIMI["Kimi Allegro"]
    OC -->|"直接計費"| PROVIDERS["各家供應商"]

    CODEX["Codex\n僅審查"] -.->|"MCP 內部"| CC
    CODEX -.->|"獨立 CLI"| STANDALONE["直接審查"]
    CODEX -.->|"GitHub 應用程式"| GH["PR 觸發"]
```

三個設定檔各自作為 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。它們是無論你是否承認都存在的限制。技術棧就是當你不再繞過它們，而是開始與它們一起建構時所產生的結果。

這篇文章描述了技術棧。下一篇文章將描述當你正面撞上每道牆時會發生什麼：失敗模式、設計決策，以及當權限、範圍和驗證成為首要關注事項而非事後想法時所浮現的模式。
