第十八章:我們學到了什麼
五個架構賭注
Claude Code 不是唯一的代理系統,也不是最早的。但它做出了五個架構賭注,使其在代理框架的版圖中脫穎而出,經過近兩千個檔案和十七個章節之後,這些賭注值得仔細檢視。
賭注一:生成器迴圈勝過回呼
大多數代理框架給你一個管線:定義工具、註冊處理器、讓框架進行編排。開發者撰寫回呼。框架決定何時呼叫它們。
Claude Code 做的恰恰相反。query() 函式是一個非同步生成器——開發者擁有迴圈的控制權。模型串流回應,生成器 yield 工具呼叫,呼叫者執行它們、附加結果,然後生成器循環。只有一個函式、一個資料流、一個所有互動都會通過的地方。生成器回傳型別的 10 個終止狀態和 7 個延續狀態編碼了每一種可能的結果。迴圈就是系統本身。
這個賭注是:即使一個生成器函式成長到 1,700 行,它仍然比分散的回呼圖更容易理解。研究過原始碼之後,這個賭注得到了回報。當你想了解一個會話為什麼結束時,你只需看一個函式。當你想新增一個終止狀態時,你只需在一個可辨識聯集(discriminated union)中新增一個變體。型別系統強制執行窮舉處理。回呼架構會將這些邏輯分散在數十個檔案中,而回呼之間的交互會是隱式的,而不是在控制流程中可見的。
賭注二:基於檔案的記憶勝過資料庫
第十一章已經詳細闡述了這個論點,但架構的重要性遠超記憶系統本身。選擇使用純 Markdown 檔案而非 SQLite、向量資料庫或雲端服務,是一個押注透明度勝過功能的賭注。資料庫可以支援更豐富的查詢、更快的查找和交易保證。檔案什麼都沒有。檔案提供的是信任。
一個使用者打開 ~/.claude/projects/myapp/memory/MEMORY.md 用 vim 檢視,看到代理記住了關於他們的確切內容——這和必須問代理「你記得什麼?」然後期望答案是完整的使用者,與系統之間有著根本不同的關係。基於檔案的設計讓代理的知識狀態可以被外部觀察到,而不僅僅是自我報告的。這比查詢效能更重要。LLM 驅動的回憶系統以檢索智慧補償了儲存的簡單性——一個 Sonnet 側查詢從清單中精選五條相關記憶,比嵌入相似度更精確,而且不需要任何基礎設施。
賭注三:自我描述的工具勝過中央編排器
代理框架通常提供一個工具註冊表:你在一個中央配置中描述你的工具,框架將它們呈現給模型。Claude Code 的工具自我描述。每個 Tool 物件攜帶自己的名稱、描述、輸入 schema、提示詞貢獻、並行安全旗標和執行邏輯。工具系統的職責不是向模型描述工具——而是讓工具描述自己。
這個賭注在可擴展性上獲得了回報。MCP 工具(第十五章)透過實作相同的介面成為一等公民。來自 MCP 伺服器的工具和內建工具對模型而言無法區分。系統不需要一個獨立的「MCP 工具轉接器」層——包裝產生一個標準的 Tool 物件,從那一刻起,現有的工具管線就能處理它:權限檢查、並行執行、結果預算控制、鉤子攔截。
賭注四:分叉代理實現快取共享
第九章涵蓋了分叉機制:一個子代理從父代理的完整對話開始,在其上下文視窗中共享父代理的提示快取。這不是一個方便性的最佳化——這是一個架構賭注,賭快取共享模型值得承擔分叉生命週期管理的複雜性。
替代方案——用對話摘要產生一個全新的代理——更簡單但更昂貴。每個全新的代理都必須從頭付出處理其上下文的全部成本。一個分叉代理可以免費獲得父代理的快取前綴(輸入 token 享有 90% 折扣),這使得為小型任務產生代理變得經濟實惠:記憶擷取、程式碼審查、驗證流程。背景記憶擷取代理(第十一章)在每次查詢迴圈轉換後執行,而它的成本之所以微乎其微,正是因為它共享了父代理的快取。沒有基於分叉的快取共享,那個代理的成本將高得令人卻步。
賭注五:鉤子勝過外掛
大多數可擴展性系統使用外掛——註冊功能並在宿主行程中執行的程式碼。Claude Code 使用鉤子——在生命週期節點執行的外部行程,透過退出碼和 stdin/stdout 上的 JSON 進行通訊。
這個賭注是:行程隔離值得付出額外開銷。外掛可以讓宿主崩潰。鉤子只會讓自己的行程崩潰。外掛可以將記憶體洩漏到宿主的堆積中。鉤子的記憶體會隨其行程一起消亡。外掛需要一個必須進行版本控管和維護的 API 表面。鉤子只需要 stdin、stdout 和一個退出碼——這個協定從 1971 年以來一直很穩定。
額外開銷是實際存在的:每次鉤子呼叫產生一個行程需要數毫秒,而行程內回呼則不需要。第十二章中內部回呼的 -70% 快速路徑表明系統知道這個成本很重要。但對於外部鉤子——使用者腳本、團隊 linter、企業策略伺服器——隔離保證使系統的擴展更安全。企業可以部署基於鉤子的策略執行,而不用擔心一個格式錯誤的鉤子腳本會讓開發者的會話崩潰。
什麼可以遷移,什麼不行
並非 Claude Code 中的每個模式都能普遍推廣。有些是規模、資源或特定約束條件的結果,其他代理開發者未必會面臨相同的情況。
可遷移到任何代理的模式
生成器迴圈模式。 任何需要串流回應、處理工具呼叫和管理多種終止狀態的代理,都能從將迴圈顯式化而非隱藏在回呼之後獲益。可辨識聯集回傳型別——精確編碼迴圈停止的原因——是一個能消除整類「代理為什麼停了?」除錯場景的模式。
帶有 LLM 回憶的基於檔案記憶。 具體的實作細節是 Claude Code 特有的,但原則——簡單儲存結合智慧檢索——適用於任何需要跨會話持久化知識的代理。四類型分類法(使用者、回饋、專案、參考)和可衍生性測試(「這能否從當前專案狀態重新衍生出來?」)是可重複使用的設計啟發。
遠端執行的非對稱讀寫通道。 當讀取是高頻串流而寫入是低頻 RPC 時,將它們分開是正確的,無論具體的傳輸協定為何。
搜尋的點陣圖預先過濾器。 任何在大型檔案索引中搜尋的代理都能從 26 位元字母點陣圖作為預先過濾器中獲益。每個條目四位元組,每個候選項一次整數比較——成本效益比非常出色。
提示快取穩定性作為架構考量。 如果你的代理使用帶有提示快取的 API,將穩定內容放在前面、易變內容放在後面來組織提示詞,這不是最佳化——而是一個決定你成本結構的架構決策。
Claude Code 規模特有的模式
分叉的終端機渲染器。 Claude Code 分叉了 Ink 並使用打包型別陣列、基於池的駐留和單元格層級差異比對重新實作了渲染管線,因為它需要在終端機中達到 60fps 的串流速度。大多數代理渲染到 Web 介面或簡單的日誌輸出。這種工程投資只有在終端機渲染是你的主要 UI 且你在高頻率串流時才有意義。
50 多個啟動分析檢查點。 當你有數十萬使用者且 0.5% 的取樣能產生具統計顯著性的資料時才有意義。對於較小的代理,更簡單的計時系統就足夠了。
八種 MCP 傳輸類型。 Claude Code 支援 stdio、SSE、HTTP、WebSocket、SDK、兩種 IDE 變體和一個 Claude.ai 代理,因為它必須與每種部署拓撲整合。大多數代理只需要 stdio 和 HTTP。
鉤子快照安全模型。 在啟動時凍結鉤子配置且永遠不隱式重新讀取,是對特定威脅的防禦:惡意儲存庫程式碼在使用者接受信任對話框後修改鉤子。這在你的代理在具有不受信任的 .claude/ 配置的任意儲存庫中執行時很重要。只在受信任環境中執行的代理可以使用更簡單的鉤子管理。
複雜性的代價
近兩千個檔案。這帶來了什麼,又付出了什麼代價?
檔案數量作為複雜性指標具有誤導性。其中很大一部分是測試基礎設施、型別定義、配置 schema 和分叉的 Ink 渲染器。實際的行為複雜性集中在少數高密度檔案中:query.ts(1,700 行,代理迴圈)、hooks.ts(4,900 行,生命週期攔截系統)、REPL.tsx(5,000 行,互動式編排器)以及記憶系統的提示詞組建函式。
複雜性來自三個來源,各有不同的特質:
協定多樣性。 支援五種終端機鍵盤協定、八種 MCP 傳輸類型、四種遠端執行拓撲和七種配置範圍本身就是複雜的。每個額外的協定是對程式庫的線性增加,而非指數級的——但總和很大。這種複雜性在 Brooks 的意義上是偶然的:它來自環境(終端機碎片化、MCP 傳輸演進、遠端部署拓撲),而不是來自正在解決的問題。
效能最佳化。 基於池的渲染、點陣圖搜尋預先過濾器、黏性快取閂鎖(Sticky Cache Latch)和推測性工具執行各自增加了複雜性,以換取可衡量的效能提升。這種複雜性因度量而合理化——每項最佳化之前都有識別出瓶頸的效能分析資料。風險在於最佳化會累積並以使熱路徑更難修改的方式相互作用。
行為調校。 記憶系統的提示指令、過時警告、驗證協定、「忽略記憶」反模式指令——這些不是程式碼複雜性。它們是提示詞複雜性,承載著不同的維護負擔。當模型的行為在版本之間發生變化時,透過評估精心調校的提示指令可能需要重新調校。評估基礎設施(在整個程式庫中以案例編號和評估分數引用)是防止退化的防線,但需要持續投入。
這個系統的維護負擔很重。一個閱讀程式庫的新工程師不僅必須理解程式碼路徑,還必須理解促成特定提示措辭的評估結果、促成特定安全檢查的生產事故,以及促成特定最佳化的效能概況。程式碼註解很詳盡——許多包含評估案例編號和前後測量——但近兩千個檔案中詳盡的註解本身也是一個閱讀負擔。
代理系統的未來走向
從 Claude Code 的模式中可以看到四個趨勢,它們指向了這個領域的發展方向。
MCP 作為通用協定
第十五章描述了 Claude Code 作為最完整的 MCP 客戶端之一。重要的不是 Claude Code 的實作——而是 MCP 的存在本身。一個標準化的工具發現和呼叫協定意味著為一個代理構建的工具可以與任何代理一起工作。生態系統效應是顯而易見的:一個 Postgres 的 MCP 伺服器一旦建好,就能服務每個使用 MCP 的代理。開發者在工具整合上的投資是可攜帶的。
對代理開發者的啟示:如果你正在定義自訂工具協定,你可能犯了一個錯誤。MCP 足夠好了,它在不斷改進,而標準協定的生態系統優勢會隨時間複利增長。構建一個 MCP 客戶端,為規範做出貢獻,讓協定透過社群回饋來演進。
多代理協調
Claude Code 的子代理系統(第八章)、任務協調(第十章)和分叉機制(第九章)是多代理模式的早期實作。它們解決了特定問題——快取共享、平行探索、結構化驗證——但它們也揭示了根本性的挑戰:協調開銷。
代理之間的每條訊息都消耗 token。每個分叉共享一個快取但增加了一個對話分支,父代理最終必須調和。任務系統的狀態機(排隊中、執行中、已完成、已失敗、已取消)是增加複雜性而不增加能力的協調機制。隨著代理變得更有能力,壓力將從「我們如何協調多個代理?」轉向「我們如何讓一個代理足夠有能力以至於不需要協調?」
目前的證據表明兩種方法將共存。簡單的任務將使用單一代理。複雜的任務將使用協調式多代理系統。工程挑戰是讓協調開銷足夠低,使得交叉點對真正的平行工作有利於多代理,而不僅僅是對複雜的任務有利。
持久化記憶
Claude Code 的記憶系統是持久化代理記憶的第一版。基於檔案的設計、四類型分類法、LLM 驅動的回憶、過時系統以及用於長時間執行會話的 KAIROS 模式,都是一個將顯著演進的問題的第一代解決方案。
未來的記憶系統很可能會增加結構化檢索(當前系統檢索整個檔案;未來系統可能檢索特定事實)、跨專案遷移學習(到處適用的使用者偏好、不適用的專案慣例)和協作記憶(第十一章的團隊記憶是第一步,但同步、衝突解決和存取控制都是最小化的)。
開放性問題是基於檔案的方法是否能擴展。在每個專案 200 條記憶時,它可以運作。在每個專案 2,000 條記憶時,Sonnet 側查詢的清單變得太大,整合變得太昂貴,索引超過其上限。基於檔案而非資料庫的架構賭注將在使用量增長時面臨最嚴峻的考驗。
自主運作
KAIROS 模式、背景記憶擷取代理、自動夢境整合、推測性工具執行——這些都是走向自主運作的步伐。代理在不被要求的情況下做有用的工作:它記住了你忘了告訴它去記的東西,它在你睡覺時整合自己的知識,它在當前回應完成之前就開始執行下一個工具。
軌跡是清晰的。未來的代理將更少被動、更多主動。它們會注意到使用者未描述的模式,建議使用者未要求的修正,並在沒有明確 /remember 指令的情況下維護自己的知識。Claude Code 的記憶系統,以其背景擷取安全網和精心設計的「該儲存什麼」啟發式提示詞,是這個未來的原型。
約束是信任。自主運作需要使用者信任代理在無人值守時會做正確的事。基於檔案的記憶、可觀察的鉤子系統、過時警告、權限對話框——所有這些存在的原因是信任必須被贏得,而不是被假定。通往更自主代理的道路要經過更透明的代理。
結語
十七個章節。六個核心抽象。一個生成器迴圈在中心,工具向外延伸,記憶向後穿越時間,鉤子守衛邊界,一個渲染引擎將一切轉譯為螢幕上的字元,而 MCP 將它連接到程式庫之外的世界。
Claude Code 中最深層的模式不是任何單一技術。而是一個反覆出現的決策:將複雜性推向邊界。渲染系統將複雜性推向池和差異比對——在管線內部,一切都是整數比較。輸入系統將複雜性推向分詞器和按鍵綁定解析器——在處理器內部,一切都是型別化的動作。記憶系統將複雜性推向寫入協定和回憶選擇器——在對話內部,一切都是上下文。代理迴圈將複雜性推向終止狀態和工具系統——在迴圈內部,只是:串流、收集、執行、附加、重複。
每個邊界吸收混沌並輸出秩序。原始位元組變成 ParsedKey。Markdown 檔案變成被回憶的記憶。MCP JSON-RPC 變成 Tool 物件。鉤子退出碼變成權限決策。在每個邊界的一側,世界是混亂的——五種鍵盤協定、脆弱的 OAuth 伺服器、過時的記憶、不受信任的儲存庫鉤子。在另一側,世界是有型別的、有邊界的、且被窮舉處理的。
如果你在構建代理系統,這就是可遷移的教訓。不是那些具體的技術——你可能不需要基於池的渲染、KAIROS 模式或八種 MCP 傳輸方式。但是原則是:定義你的邊界,在那裡吸收複雜性,並保持它們之間的一切乾淨。邊界是工程困難的地方。內部是工程令人愉快的地方。為愉快的內部而設計,並將你的複雜性預算投資在邊緣。
原始碼是開放的。螃蟹在牠的蟹螯裡握著地圖。去讀吧。