
程式碼編寫代理的組成要素
本文探討程式碼編寫代理的整體設計,說明這些代理如何透過工具、記憶與儲存庫上下文,在代理框架中協同運作,使大型語言模型在實際開發中發揮更強大的效能。
程式碼代理的組成要素
程式碼代理如何利用工具、記憶與儲存庫上下文,讓 LLM 在實務中運作得更好
在本文中,我想探討程式碼代理(Coding Agents)與代理控制架構(Agent Harnesses)的整體設計:它們是什麼、如何運作,以及不同組件在實務中如何協作。我的著作《從零開始構建大語言模型》(Build a Large Language Model (From Scratch))與《從零開始構建大推理模型》(Build a Large Reasoning Model (From Scratch))的讀者經常詢問關於代理的問題,因此我認為撰寫一份可供參考的指南會很有幫助。
更廣泛地說,代理已成為一個重要課題,因為近期實用 LLM 系統的進步不僅在於更好的模型,更在於我們如何使用它們。在許多現實世界的應用中,周邊系統(如工具呼叫、上下文管理與記憶)所起的作用與模型本身一樣重要。這也解釋了為什麼像 Claude Code 或 Codex 這樣的系統,會讓人覺得比在普通對話界面中使用相同的模型要強大得多。
在本文中,我列出了程式碼代理的六個主要構建區塊。
Claude Code、Codex CLI 與其他程式碼代理
你可能對 Claude Code 或 Codex CLI 很熟悉,但為了先奠定基礎,它們本質上是「代理化」的程式碼工具,將 LLM 包裝在一個應用層(即所謂的代理控制架構)中,使其在處理程式碼任務時更方便且效能更好。

程式碼代理是專為軟體工作而設計的,其關鍵部分不僅在於模型的選擇,還在於周邊系統,包括儲存庫上下文(Repo Context)、工具設計、提示詞快取(Prompt-cache)穩定性、記憶以及長對話的連續性。
這種區分很重要,因為當我們談論 LLM 的程式編寫能力時,人們往往會將模型、推理行為和代理產品混為一談。但在深入探討程式碼代理的細節之前,讓我簡要提供更多關於廣義概念、LLM、推理模型與代理之間差異的背景。
關於 LLM、推理模型與代理之間的關係
LLM 是核心的「下一個標記(Next-token)」預測模型。推理模型(Reasoning Model)仍然是 LLM,但通常是經過訓練和/或提示,在推論時間(Inference-time)投入更多計算資源於中間推理、驗證或對候選答案進行搜尋的模型。
代理(Agent)是位於其上的一個層級,可以理解為圍繞模型的控制迴圈(Control Loop)。通常在給定目標後,代理層(或控制架構)會決定下一步要檢查什麼、呼叫哪些工具、如何更新其狀態以及何時停止等。
粗略地說,我們可以這樣思考它們的關係:LLM 是引擎,推理模型是強化版引擎(更強大但使用成本更高),而代理控制架構則幫助我們駕馭模型。這個類比並不完美,因為我們也可以將傳統 LLM 和推理 LLM 作為獨立模型使用(在聊天界面或 Python 會話中),但我希望它能傳達核心觀點。

換句話說,代理是在環境中重複呼叫模型的系統。
簡而言之,我們可以總結如下:
- LLM:原始模型
- 推理模型:經過優化以輸出中間推理軌跡並加強自我驗證的 LLM
- 代理:結合模型、工具、記憶與環境回饋的運作迴圈
- 代理控制架構(Agent Harness):圍繞代理的軟體支架,負責管理上下文、工具呼叫、提示詞、狀態與控制流
- 程式碼控制架構(Coding Harness):代理控制架構的一種特殊情況;即針對軟體工程的特定任務架構,負責管理程式碼上下文、工具、執行與迭代回饋
如上所述,在代理與程式碼工具的語境中,我們還有兩個流行術語:代理控制架構與(代理化)程式碼控制架構。程式碼控制架構是圍繞模型的軟體支架,幫助其有效地編寫和修改程式碼。而代理控制架構則更廣泛,不限於程式碼(例如 OpenClaw)。Codex 和 Claude Code 都可以被視為程式碼控制架構。
總之,更好的 LLM 為推理模型(涉及額外訓練)提供了更好的基礎,而控制架構則能從推理模型中發揮更多潛力。
當然,LLM 和推理模型本身(不含控制架構)也具備解決程式碼任務的能力,但程式碼工作僅有一部分是關於下一個標記的生成。很大一部分在於儲存庫導航、搜尋、函式查找、套用差異(Diff)、執行測試、檢查錯誤,以及將所有相關資訊保持在上下文中。(程式設計師都知道這是繁重的腦力勞動,這也是為什麼我們不喜歡在寫程式時被打斷的原因 :))。

這裡的重點是,一個好的程式碼控制架構可以讓推理模型和非推理模型感覺比在普通對話框中強大得多,因為它有助於上下文管理等工作。
程式碼控制架構(The Coding Harness)
如前一節所述,當我們提到「控制架構(Harness)」時,通常是指模型周圍的軟體層,它負責組合提示詞、開放工具、追蹤檔案狀態、套用編輯、執行指令、管理權限、快取穩定的前綴、儲存記憶等等。
現今在使用 LLM 時,與直接提示模型或使用網頁聊天界面(更接近「與上傳的檔案聊天」)相比,這一層決定了大部分的使用者體驗。
在我看來,由於現今各家 LLM 的原始版本能力非常接近(例如 GPT-5.4、Opus 4.6 和 GLM-5 等的原始版本),控制架構往往成為區分一個 LLM 是否比另一個更好用的關鍵因素。
這純屬推測,但我懷疑如果我們將最新的、最強大的開源權重 LLM(如 GLM-5)放入類似的控制架構中,它的表現可能與 Codex 中的 GPT-5.4 或 Claude Code 中的 Claude Opus 4.6 旗鼓相當。即便如此,針對特定控制架構的後訓練(Post-training)通常是有益的。例如,OpenAI 歷史上曾維持獨立的 GPT-5.3 和 GPT-5.3-Codex 變體。
在下一節中,我想更深入探討細節,並使用我的 Mini Coding Agent(https://github.com/rasbt/mini-coding-agent)來討論程式碼控制架構的核心組件。

順帶一提,在本文中,為了簡化,我會交替使用「程式碼代理」和「程式碼控制架構」這兩個詞。(嚴格來說,代理是模型驅動的決策迴圈,而控制架構是提供上下文、工具和執行支援的周邊軟體支架。)

總之,以下是程式碼代理的六個主要組件。你可以查看我那個極簡但功能完整、從零開始構建的 Mini Coding Agent(純 Python 實現)的原始碼,以獲取更具體的程式碼範例。該程式碼透過註釋標註了下文討論的六個組件:
1. 即時儲存庫上下文 (Live Repo Context)
這可能是最顯而易見的組件,但也是最重要的組件之一。
當使用者說「修復測試」或「實作 xyz」時,模型應該知道它是否在 Git 儲存庫中、在哪個分支上、哪些專案文件可能包含指令等等。
這是因為這些細節經常變化,並會影響正確的操作。例如,「修復測試」並非一個自給自足的指令。如果代理看到 AGENTS.md 或專案 README,它可能會學到該執行哪個測試指令等。如果它知道儲存庫的根目錄和佈局,它就可以在正確的地方查找,而不是靠猜測。
此外,Git 分支、狀態和提交紀錄有助於提供更多關於當前正在進行哪些更改以及重點應放在何處的上下文。

重點在於,程式碼代理在執行任何工作之前會先收集資訊(作為工作區摘要的「穩定事實」),這樣它就不會在每次提示時都從零開始、毫無背景資訊。
2. 提示詞形狀與快取重用 (Prompt Shape And Cache Reuse)
一旦代理擁有了儲存庫視圖,下一個問題就是如何將這些資訊提供給模型。前圖顯示了這一點的簡化視圖(「組合提示詞:前綴 + 請求」),但在實務中,在每次使用者查詢時都重新組合並處理工作區摘要是相對浪費的。
也就是說,程式碼對話是重複性的,代理規則通常保持不變。工具描述通常也保持不變。甚至工作區摘要通常也(大部分)保持不變。主要的變化通常是最新的使用者請求、近期的對話紀錄,以及可能的短期記憶。
「聰明」的執行環境不會在每一輪對話中都將所有內容重新構建為一個巨大的、無差別的提示詞,如下圖所示。

與第 1 節的主要區別在於,第 1 節是關於收集儲存庫事實。而在這裡,我們感興趣的是如何高效地封裝和快取這些事實,以便重複呼叫模型。
「穩定提示詞前綴(Stable prompt prefix)」意味著其中包含的資訊不會頻繁變動。它通常包含一般指令、工具描述和工作區摘要。如果沒有重大變動,我們不想在每次互動中浪費計算資源從頭開始構建它。
其他組件則更新得更頻繁(通常是每一輪)。這包括短期記憶、近期對話紀錄和最新的使用者請求。
簡而言之,「穩定提示詞前綴」的快取方面僅僅是指聰明的執行環境會嘗試重用該部分。
3. 工具存取與使用 (Tool Access and Use)
工具存取與使用是讓它感覺不再像聊天,而更像代理的地方。
普通模型可以用文字建議指令,但程式碼控制架構中的 LLM 應該做一些更精確且有用的事情,並且能夠實際執行指令並獲取結果(而不是讓我們手動執行指令並將結果貼回聊天框)。
但與其讓模型隨意即興創作語法,控制架構通常會提供一個預定義的、具名的允許工具列表,並具有明確的輸入和邊界。(當然,像 Python 的 subprocess.call 也可以是其中的一部分,以便代理也能執行廣泛的 Shell 指令。)
工具使用的流程如下圖所示。

為了說明這一點,以下是使用我的 Mini Coding Agent 時,使用者通常看到的樣子。(這不像 Claude Code 或 Codex 那麼漂亮,因為它非常極簡,且使用純 Python,沒有任何外部依賴。)

在這裡,模型必須選擇一個控制架構能識別的動作,例如列出檔案、讀取檔案、搜尋、執行 Shell 指令、寫入檔案等。它還必須以控制架構可以檢查的形式提供參數。
因此,當模型要求執行某項操作時,執行環境可以停止並執行程式化檢查,例如:
- 「這是一個已知的工具嗎?」
- 「參數是否有效?」
- 「這是否需要使用者批准?」
- 「請求的路徑是否在工作區內?」
只有在這些檢查通過後,才會實際執行任何操作。
雖然執行程式碼代理當然帶有一定的風險,但控制架構的檢查也提高了可靠性,因為模型不會執行完全隨意的指令。
此外,除了拒絕格式錯誤的動作和批准門控外,還可以透過檢查檔案路徑將檔案存取限制在儲存庫內。
從某種意義上說,控制架構給予模型的自由度較小,但同時也提升了可用性。
4. 最小化上下文膨脹 (Minimizing Context Bloat)
上下文膨脹並非程式碼代理特有的問題,而是 LLM 普遍面臨的問題。當然,現在的 LLM 支援越來越長的上下文(我最近寫過關於讓這在計算上更可行的注意力機制變體),但長上下文仍然昂貴,且可能引入額外的噪音(如果存在大量無關資訊)。
現代 LLM 中注意力機制變體的視覺指南
由於重複的檔案讀取、冗長的工具輸出、日誌等,程式碼代理在多輪對話中比普通 LLM 更容易受到上下文膨脹的影響。
如果執行環境以完整精度保留所有內容,它很快就會耗盡可用的上下文標記。因此,一個好的程式碼控制架構在處理上下文膨脹方面通常非常老練,而不僅僅是像普通聊天 UI 那樣刪減或總結資訊。
從概念上講,程式碼代理中的上下文壓縮運作方式如下圖所示。具體來說,我們進一步放大前一節圖 8 中的裁剪(步驟 6)部分。

一個極簡的控制架構至少使用兩種壓縮策略來管理該問題。
第一種是裁剪(Clipping),它會縮短長文件片段、大型工具輸出、記憶筆記和對話紀錄條目。換句話說,它防止任何一段文本僅僅因為冗長就佔據了大部分的提示詞預算。
第二種策略是對話紀錄縮減或總結,它將完整的對話歷史(下一節將詳細介紹)轉化為較小的、可放入提示詞的摘要。
這裡的一個關鍵技巧是保留較豐富的近期事件,因為它們更有可能對當前步驟產生影響。而我們會更激進地壓縮較舊的事件,因為它們可能相關性較低。
此外,我們還會對舊的檔案讀取進行去重,這樣模型就不會因為在對話早期多次讀取同一個檔案而反覆看到相同的內容。
總體而言,我認為這是優秀程式碼代理設計中被低估且枯燥的部分。許多顯而易見的「模型品質」實際上是「上下文品質」。
5. 結構化對話記憶 (Structured Session Memory)
在實務中,這裡涵蓋的所有 6 個核心概念都是高度交織的,不同的章節和圖表以不同的側重點或縮放級別來涵蓋它們。在前一節中,我們介紹了歷史紀錄在提示時的使用以及我們如何構建壓縮的對話紀錄。那裡的問題是:在下一輪中,有多少過去的內容應該回傳給模型?因此重點在於壓縮、裁剪、去重和近期性。
而本節「結構化對話記憶」是關於歷史紀錄在儲存時的結構。這裡的問題是:代理會保留什麼作為永久記錄?因此重點在於執行環境將更完整的對話紀錄保留為持久狀態,同時還有一個更輕量、更小且會被修改和壓縮(而非僅僅是追加)的記憶層。
總結來說,程式碼代理將狀態分為(至少)兩層:
- 工作記憶(Working memory):代理顯式保留的小型、精煉狀態
- 完整對話紀錄(Full transcript):涵蓋所有使用者請求、工具輸出和 LLM 回應

上圖展示了兩個主要的對話檔案:完整對話紀錄和工作記憶,它們通常以 JSON 檔案的形式儲存在磁碟上。如前所述,完整對話紀錄儲存了整個歷史,如果我們關閉代理,它是可以恢復的。工作記憶則更像是包含當前最重要資訊的精煉版本,這與壓縮對話紀錄有些關聯。
但壓縮對話紀錄和工作記憶的職責略有不同。壓縮對話紀錄用於提示詞重構,其工作是為模型提供近期歷史的壓縮視圖,以便它可以在不看每一輪完整紀錄的情況下繼續對話。工作記憶則更傾向於任務連續性,其工作是保留一份跨輪次的重要內容摘要,例如當前任務、重要檔案和近期筆記。
按照上圖中的步驟 4,最新的使用者請求連同 LLM 回應和工具輸出,隨後將在下一輪中作為「新事件」記錄在完整對話紀錄和工作記憶中(圖中未顯示以減少雜亂)。
6. 具備(受限)子代理的委派 (Delegation With (Bounded) Subagents)
一旦代理擁有了工具和狀態,下一個有用的能力就是委派。
原因在於它允許我們透過子代理(Subagents)將某些工作並行化為子任務,並加速主任務。例如,主代理可能正在處理一項任務,但仍需要一個側面答案,例如:哪個檔案定義了某個符號、設定檔說了什麼,或者為什麼測試失敗了。將其拆分為一個受限的子任務是有用的,而不是強迫一個迴圈同時處理每一條工作線。
(在我的 Mini Coding Agent 中,實作較為簡單,子代理仍同步執行,但底層理念是一致的。)
子代理只有在繼承了足夠的上下文來執行實際工作時才有用。但如果我們不加以限制,現在就會有多個代理在重複工作、觸碰相同的檔案,或產生更多的子代理等等。
因此,棘手的設計問題不僅在於如何產生子代理,還在於如何約束它 :)。

這裡的技巧在於子代理繼承了足夠的上下文以發揮作用,但也受到了約束(例如,唯讀且限制遞迴深度)。
Claude Code 支援子代理已有很長時間,而 Codex 最近也加入了此功能。Codex 通常不會強迫子代理進入唯讀模式。相反,它們通常繼承主代理的大部分沙盒和批准設定。因此,邊界更多在於任務範圍、上下文和深度。
組件總結
上述章節試圖涵蓋程式碼代理的主要組件。如前所述,它們在實作中或多或少是深度交織的。然而,我希望逐一介紹它們有助於建立關於程式碼控制架構如何運作,以及為什麼它們能讓 LLM 比簡單的多輪對話更有用的整體心智模型。

如果你有興趣看到這些功能以簡潔、極簡的 Python 程式碼實作,你可能會喜歡我的 Mini Coding Agent。
這與 OpenClaw 相比如何?
OpenClaw 可能是一個有趣的對比對象,但它並非完全相同類型的系統。
OpenClaw 更像是一個本地的、通用的代理平台,它也可以編寫程式碼,而不是一個專門的(終端機)程式碼助手。
它與程式碼控制架構仍有幾個重疊之處:
- 它在工作區中使用提示詞和指令檔案,如
AGENTS.md、SOUL.md和TOOLS.md - 它保留 JSONL 對話檔案,並包含對話紀錄壓縮和對話管理
- 它可以產生輔助對話和子代理
- 等等
然而,如上所述,重點有所不同。程式碼代理針對在儲存庫中工作並要求程式碼助手高效檢查檔案、編輯程式碼和執行本地工具的人員進行了優化。OpenClaw 則更側重於在聊天、頻道和工作區中執行許多長效的本地代理,而程式碼編寫只是其多個重要工作負載之一。
我很高興地分享,我已經完成了《從零開始構建推理模型》(Build A Reasoning Model (From Scratch))的寫作,目前所有章節都已進入早期存取階段。出版社目前正在處理排版,預計今年夏天面世。
這可能是我迄今為止最有野心的一本書。我花了約 1.5 年的時間撰寫,並投入了大量的實驗。這也可能是我在時間、精力和打磨上最用心的一本書,希望你們會喜歡。

主要主題包括:
- 評估推理模型
- 推論時間擴展(Inference-time scaling)
- 自我精煉(Self-refinement)
- 強化學習
- 蒸餾(Distillation)
關於 LLM 的「推理」有很多討論,我認為理解它在 LLM 語境下真正含義的最佳方式,就是從零開始實作一個!