← 返回首頁
Jason Zuo
Jason Zuo
@xxxjzuo
61🔁 10
𝕏 (Twitter)🔥🔥🔥🔥

Claude Code 原始碼背後的設計哲學:51 萬行程式碼,絕大部分都與 AI 無關

今天 AI 圈最大的新聞,莫過於 Claude Code 的原始碼洩漏了。

51.2 萬行 TypeScript,1903 個檔案。很多人已經在拆裡面的具體實現了,甚至是手搓了開源版本。

我更想聊的是這份原始碼暴露出來的一套設計哲學,作為普通非技術使用者也可以複製的使用 AI Agent 的底層邏輯。

Claude Code 好用,不只是因為 Opus 模型本身的能力強。51 萬行程式碼裡,真正呼叫 LLM API 的部分可能不到 5%。剩下全是 harness:圍繞模型搭建的運行環境。安全檢查、權限控制、上下文管理、記憶系統、工具編排、Prompt 工程。

這 95% 就是 Claude Code 的 harness 設計哲學。也是它跟其他 AI 程式撰寫工具拉開差距的地方。模型是引擎,harness 是整台車。引擎再強,沒有煞車、方向盤和變速箱,你哪兒也去不了。


設計哲學的核心:不信任模型

讀 Claude Code 的原始碼,最強烈的感受不是"Anthropic 工程能力真強",而是"Anthropic 對自己的模型真不信任"。

這不是哲學層面的不信任。原始碼註釋裡有大量 A/B 測試資料和故障率統計。每個 harness 組件背後,都是一個被資料驗證過的"Claude 做不到這件事"。不是在吹模型多強,是在用程式碼承認模型的邊界,然後用架構補上去。

先讀後改鐵律。FileEditTool 會檢查你是不是已經用 FileReadTool 讀過這個檔案,沒讀過直接報錯,不讓改。原始碼裡的邏輯大致是這樣的:

// FileEditTool 核心檢查
if (!hasFileBeenRead(filePath)) {
  throw new ToolError(
    "You must read the file before editing it. Use FileReadTool first."
  );
}

為什麼?因為 LLM 會 hallucinate 沒讀過的檔案內容。如果不強制先讀,Claude 可能憑空寫一段程式碼覆蓋你的檔案。這條規則不是建議,是硬限制。程式碼級別的"你沒看過就不許動"。

這就是為什麼 Claude Code 改檔案的準確率比其他工具高。不是模型更聰明,是架構不給模型犯傻的機會。

三層上下文壓縮。LLM 管不好自己的 context window,對話長了就開始丟重要資訊。CC 設計了三層遞進式壓縮:

使用者對話開始
│
▼
[Level 1] 微壓縮 (Micro Compaction)
│  • 僅清理舊的 tool 呼叫結果
│  • 完整保留對話主線
│  • 成本最低 / 無語義損失
│
▼  (觸發條件:token ≈ 視窗上限)
[Level 2] 自動壓縮 (Auto Compaction)
│  • 觸發閾值:視窗大小 - 13000 buffer
│  • 壓縮歷史對話(保留關鍵資訊)
│  • 熔斷機制:
│      - 連續失敗超過閾值 → 停止壓縮
│
▼  (若仍超限)
[Level 3] 完全壓縮 (Full Compaction)
│  • AI 生成整段對話摘要
│  • 替換全部歷史訊息
│  • 強約束指令:
│      "只總結,不呼叫工具"
│
▼
繼續對話

每一層的觸發閾值、預留 buffer、熔斷上限,都不是拍腦袋定的,是在"保留足夠上下文繼續工作"和"騰出足夠空間接收新訊息"之間反覆調出來的平衡點。

sub-Agent 的身份注入。Claude 會無限遞歸 spawn 子 Agent。原始碼裡給子 Agent 注入了一段身份聲明:

// 子 Agent 的系統提示詞注入
"You are a sub-agent. You cannot create
additional sub-agents. Complete the task
yourself using only the tools available to you."

翻譯過來就是:"你是一個工人,不是經理。不要試著再雇人,自己幹活。" Coordinator 模式下有明確的並行規則:

Coordinator 並行策略:
只讀任務(研究/搜尋)→ 並行執行
寫入任務(改檔案)  → 按檔案分組串行
原文註釋:"Parallelism is your superpower"
前提:讀寫分離

BashTool 安全體系。一個 BashTool,18 個檔案做安全檢查。prompt.ts 裡有大約 350 行寫給 AI 看的行為守則。這就是為什麼 Claude Code 從不會擅自 git push --force。不是模型更有分寸,是 Prompt 裡已經把規矩講死了。

工具工廠的默認值是整個安全體系的基石:

// src/tools/tool-factory.ts

interface ToolDefinition {
  isConcurrencySafe: boolean; // 默認 false
  isReadOnly: boolean;       // 默認 false
}

兩個 false,一行程式碼。這叫 fail-closed。如果工具開發者忘了聲明安全屬性,系統默認它是"不安全的、會寫入的"。寧可過度保守,也不漏掉一個風險。這個設計決策決定了整個系統的安全底線。


Prompt 是編譯出來的,不是寫出來的

打開 src/constants/prompts.ts,會看到整個 Prompt 的組裝邏輯:

分界線上面是靜態內容,Claude API 可以緩存,不重複計費。分界線下面是動態內容,每次對話都不一樣。

function buildSystemPrompt() {
  return [
    // ─── 靜態部分(可緩存)───
    BASE_SYSTEM_PROMPT,         // 基礎人格和規則
    TOOL_PROMPTS,               // 所有工具的使用手冊
    SAFETY_RULES,               // 安全規則

    // ─── 緩存分界線 ───
    SYSTEM_PROMPT_DYNAMIC_BOUNDARY,

    // ─── 動態部分(每次不同)───
    gitBranch,                  // 當前 Git 分支
    claudeMdConfig,             // CLAUDE.md 專案配置
    userMemories,               // 使用者偏好記憶
    currentDateTime,            // 當前時間
  ];
}

Anthropic 把 Prompt 當編譯器的輸出來優化。靜態部分是編譯後的二進制,動態部分是運行時參數。省錢、快、靈活,三個好處同時拿到。

緩存穩定性的維護是一場持續的保衛戰。Claude API 的 Prompt cache 是基於字節級前綴匹配的。不只是工具順序,原始碼裡追蹤了十幾種可能打破緩存的因素:系統 Prompt、工具 schema、模型名稱、beta header 列表、effort 值,任何一個變化都會導致緩存失效。CC 團隊為此發明了專門的 latch 機制:某些參數一旦在會話中首次設定,即使後續狀態變化也不允許修改,因為改了就會打破數萬 token 的緩存。

緩存穩定性設計:

1. 工具排序寫死
   - 不按字母序
   - 不按使用頻率

2. 子 Agent fork 結果統一占位符
   - 使用相同占位符替換
   → 10 個子 Agent,只有第 1 個冷啟動
   → 後 9 個直接命中緩存

3. 靜態 Prompt 不包含任何動態變數

4. 動態參數用 latch 機制鎖定,防止中途變化打破緩存

每個工具目錄下有獨立的 prompt.ts,是專門寫給 LLM 看的使用手冊。BashTool 的 prompt.ts 大約 370 行。不是寫給人看的檔案,是寫給 AI 看的行為守則,每次啟動時注入系統提示詞。

42 個工具也不是全量注入的。透過 ToolSearchTool 按需發現,需要什麼載入什麼。每多一個工具就多一段 Prompt 描述,多花一份 token。設定 CLAUDE_CODE_SIMPLE=true,直接砍到只剩 3 個工具:Bash、讀檔案、改檔案。連 harness 自身都在做 context management。Prompt 的每一個 token 都有成本,harness 不能無節制地膨脹。

順便說一句,CC 團隊因為自家 SDK 的流式解析存在 O(n²) 性能問題,直接繞過了 Anthropic 官方 SDK,自己管理所有的流式狀態累積。旗艦產品繞過自家基礎設施,harness 工程的複雜度可見一斑。


記憶系統:精確度優先於召回率

用過 Claude Code 的人都有一個感受:它好像真的認識你。你告訴它"不要在測試中 mock 資料庫",下次對話它就不會再 mock。

背後的記憶檢索不是你以為的關鍵詞匹配或向量搜尋:

記憶檢索流程:
1. 使用者發送訊息
2. Claude Sonnet(小模型)掃描所有記憶檔案的標題+描述
3. 選出最相關的記憶
4. 把完整內容注入當前對話上下文
策略:precision > recall
寧可漏掉,不要污染

這個取捨很有意思:大多數系統追求"盡量多找到相關資訊",CC 反過來,追求"絕不注入無關資訊"。因為對 LLM 來說,context 裡的噪音比缺失更致命。

原始碼裡還有一個叫 KAIROS 的特性標誌。在這個模式下,長會話中的記憶存在按日期的追加式日誌中。然後有一個 /dream 技能會在低活躍期運行,把原始日誌蒸餾成結構化的主題檔案。AI 在"睡覺"的時候整理記憶。


三種安全哲學:不是好壞,是信任假設不同

為什麼 Anthropic 選了最難的那條路?因為只有這樣,AI 才能在你的真實環境裡幹活,而不是在一個乾淨房間裡寫一段程式碼讓你複製過來。

權限系統不是 allow/deny 二態,是四態決策:

權限決策:四態模型

allow → 直接執行,不問

deny → 直接拒絕,不執行

ask → 彈窗問使用者,等確認

passthrough → 低風險操作,默認放行

粒度比二態細得多。ask 讓使用者做即時判斷,passthrough 讓低風險操作直接透過。粒度決定體驗,太粗使用者煩,太細使用者累。四態是 Anthropic 試出來的平衡點。

三種不同的信任假設。Cursor 賭人類可以一直盯著。Copilot 賭隔離就是安全。Claude Code 賭精確控制行為邊界比限制接觸範圍更有效。51 萬行程式碼就是這個賭注的成本。


內部版和外部版不一樣

原始碼裡有個 isAnthropicEmployee 分支:

if (isAnthropicEmployee()) {
  // 更激進的輸出策略
  // "倒金字塔寫作法"
  // "不寫註釋除非 WHY 不明顯"

  // 實驗功能
  enableFeature("VerificationAgent");
  enableFeature("ExploreAndPlanAgent");
}

Anthropic 自己就是 Claude Code 最大的使用者。他們在用自己的產品開發自己的產品。內部版是實驗場,外部版是穩定版。

CC 的架構不是一次性設計出來的,是在 Anthropic 自己的高強度使用中不斷迭代的。每個功能從內部版畢業到外部版,都經過了實戰驗證。Opus 4.5 時代需要的某些限制,到 Opus 4.6 就不需要了。架構會隨著模型能力的提升持續調整,有些組件會被拆掉,有些新的會加上。這也是 harness 哲學的一部分:為拆除而設計。


對做 Agent 產品的人意味著什麼

最直接的就是社區已經大量用 Claude Code 分析了 Claude Code 原始碼,把核心邏輯抽出來做出了各種 Agent-SDK。claude-Agent-SDK 本來是 CC 套了一層殼做成 SDK,每次 query 要創建一個 CC 進程,開銷大。

利用這次洩露的內容,手搓的各種 Agent-SDK 把邏輯抽成函數呼叫,不依賴本地 CLI 進程。這件事本身就驗證了一點:CC 的價值不在調模型那一小部分程式碼,在於 harness 的工程實現。把 harness 邏輯抽出來,換個模型接進去,核心能力還在。

推薦 @idoubicc 做的 open-Agent-SDK https://x.com/idoubicc/status/2039006326882546141?s=20

從 CC 原始碼能提煉出幾條通用的 harness 設計原則:

Fail-closed 不是 fail-open。不確定一個操作是不是安全的?默認禁止。CC 的工具工廠就是這麼做的。一行默認值,決定了整個系統的安全底線。

每個 harness 組件要有一個顯式的假設。"因為 Claude 會 hallucinate 沒讀過的檔案,所以有先讀後改鐵律。"說不清這個組件在補什麼缺口,這個組件就不該存在。

為拆除而設計。模型升級後,有些限制就不需要了。今天你花一周搭的組件,半年後可能該拆了。如果拆不掉,說明它已經從"補模型缺口"變成了"產品本身的功能",那它就不再是 harness 組件了。

不過原始碼也暴露了這個原則的另一面。註釋裡工程師自己寫了:多遍 normalization 本質上是脆弱的,每一遍清理都可能製造出前一遍本該處理的條件。原則是對的,但現實中 patch 累積的速度往往比拆除的速度快。為拆除而設計是目標,不是現狀。

Harness 要管自己的開銷。工具按需載入、緩存邊界精確劃分、固定排序維護前綴匹配。Harness 本身也是 context 的消費者,不能無節制地膨脹。


51 萬行,絕大部分都是 harness。42 個工具對應系統呼叫,權限系統對應使用者權限管理,記憶系統對應持久化儲存,Agent 蜂群對應進程管理。Claude Code 不是一個套了 AI 的程式撰寫工具,是一個以 LLM 為內核的作業系統。

模型會迭代,具體的 harness 組件也會過時。但 fail-closed、先讀後改、角色分離、按需載入、為拆除而設計,這些設計哲學不會。

Claude Code 好用,是因為 Anthropic 用 51 萬行程式碼承認了一件事:再強的模型也需要一個好的運行環境。