你叫 agent 加一個功能。它很有自信地寫出一段乾淨的程式碼——用的卻是你半年前就拔掉的套件版本、早就放棄的目錄結構,還有一個這個 repo 從來沒用過的 auth 寫法。它能編譯,但每一個重要的地方都是錯的。
第一個反應,多半是怪模型:「它又在 hallucination 了。」也許吧。但更常見的真相是——以它手上握有的資訊,它做的其實完全合理。問題是,它手上握有的,只是你分心時隨手丟進對話框的一段模糊描述。
所以這篇想講一個有點刺耳的結論:多數時候,不是你的 agent 笨,是你餵的 context 不行。
為什麼是現在
過去很長一段時間,寫軟體最難的部分是「把程式碼寫出來」。這件事正在悄悄地不再成立。現在 agent 產出可用程式碼的速度,常常比我們 review 的速度還快。瓶頸往上游移動了——移到「把意圖講清楚」這件事上:你到底要什麼、有哪些限制、在「這個」codebase 裡怎樣才叫做好。
而我們在這件事上做得很差。我們把餵給 agent 的指令、規則、專案知識當成用過即丟的聊天內容:貼一段 prompt、拿到結果、然後那段 prompt 就永遠消失了。我們從來不會這樣對待自己的原始碼——原始碼我們會版控、會 review、會測試。Patrick Debois(當年不小心造出「DevOps」這個詞的人)講的正是這件事:context 就是新的 code,值得用同樣的工程紀律去對待。他把這套還在成形中的方法稱為 Context Development Lifecycle——像對待軟體一樣,去 generate、evaluate、distribute,並在 production 裡持續 observe。
我覺得這個框架是真的有用。但它也還很早期——比較像一個方向,而不是一條鋪好的路。所以接下來我跳過理論,直接講你明天就能動手做的部分。
一、把知識從腦袋(和對話)裡搬進檔案
槓桿最大的一步:別再把專案知識留在自己腦袋裡、留在聊天紀錄裡,把它寫進 agent 會自動讀取的版控檔案。
多數 agent 工具都支援某種專案指令檔——CLAUDE.md、agent.md、.cursorrules,名字不重要。把它當成一個真正的產物來經營:commit 它、在 PR 裡 review 它,讓它慢慢累積那些「新同事第一天上工會需要知道」的硬知識:
# agent.md
## Stack
- Node 20,TypeScript strict mode。不准用 `any`。
- Postgres 走 Drizzle。但我們「不」用 ORM 內建的 migration 工具——
migration 都放在 /migrations,用 npm run db:migrate 跑。
## Conventions
- API handler 一律回傳 Result<T>,不准跨邊界 throw。
- 測試用 Vitest,跟原始碼放一起,命名 *.test.ts。
## Don't
- 不要沒問過就加新套件。
- 不要碰 /legacy——那塊已凍結,正在被刪掉。
注意,這些都不是什麼厲害的 prompt 技巧。它們是「事實」——就是你會對一個真人新人講的那些話。好處在於:你只寫一次,往後每一個 session 一開場就是「已經知道」,而不是「重新猜一次」。
二、把規則分層,一層只做一件事
別把所有東西塞進同一個巨大的檔案。像拆 config 一樣,按「適用範圍」把 context 拆開。
- 全域規則(你做任何事都適用):你個人的偏好。「講清楚取捨,不要只會附和我。」「能用標準函式庫就不要加新套件。」這些跟著「你」走,跨專案都成立。
- 專案規則(只限這個 repo):技術棧、慣例、地雷。這些跟著「程式碼」走。
把兩者分開很重要,因為它們變動的速度與理由不同。你的個人風格相對穩定;專案的架構則會一直變。一旦混在一起,每次某個 repo 做了奇怪的事,你就得去動到你那份「universal 偏好」——然後那個怪癖就會悄悄滲進你「所有」其他專案。一個檔案,一件事。
三、餵事實,不要餵感覺
當你給 agent 的是「可以查證的東西」而不是「請你回想一下」,hallucination 會明顯下降。
「用最新版的 React Router」這種講法,等於請模型去把它訓練時看過的所有版本平均一下。換成「我們用 React Router 7,只走 data router,這是我們在用的三種 pattern:[貼上]」,你給的是 ground truth。來源越具體、越「當下」,它能自由發揮(瞎掰)的空間就越小。
具體來說:
- 版本講死。寫「React 19」,不要只寫「React」。
- 任何變動快的東西,直接貼上真正的 API 或文件片段,別賭它記得。
- 指向真實檔案:「照著
src/handlers/users.ts的 pattern 寫」勝過用文字描述那個 pattern。
一個可查證的來源,永遠贏過一段很有自信的記憶。
四、把 context 當成有限資源
這一點幾乎每個人都會踩雷。context window 不是無限的,而且——更關鍵的是——「越大不等於越好」。把整個 codebase 全塞進去,不會讓 agent 更聰明;過了某個點,反而更糟:真正相關的訊號被淹沒、模型抓不到重點,輸出品質就這樣悄悄地往下掉。
留意這些徵兆:回答開始偏離你的慣例、反覆問你早就講過的事、很有自信地改錯檔案。這通常不是模型變笨了——是 context 變雜了。
實際該做的事:
- 察覺到退化。 一個長 session 開始產出變差,那是訊號,不是運氣不好。
- compact 後重開。 把真正重要的東西——做過的決定、目前的狀態——濃縮進一個乾淨的新 session。多數工具都有 compact 的機制,刻意去用它,而不是讓一個 session 漫無止境地拖上好幾個小時。
- 不要預先塞。 context 是「這個任務需要時」才加,不是「以防萬一」先放著。一個聚焦的視窗,勝過一個塞滿的視窗。
把 attention 想成一份預算,只花在跟「這個任務」相關的東西上。
五、告訴 agent 你的環境長怎樣
你的程式碼不是只跑在一個地方。它跑在 local、跑在 CI/integration、也跑在 production——而這幾個環境的差異,往往就是會咬你一口的地方:不同的環境變數、不同的 feature flag、真資料庫對上 mock、某個環境有而另一個沒有的 secret。
這些 agent「全部都不知道」,除非你寫下來。所以,把它寫下來:
## Environments
- local:用 Docker Postgres,MOCK_PAYMENTS=true,跑種子測試資料。
- staging:用真的 Stripe 測試金鑰,schema 跟 prod 一致。
- prod:用真的金鑰。永遠不要在這裡跑破壞性腳本。
migration 一律要走人工核可才能上。
光是最後那一行,就可能救你一命——免得 agent 興高采烈地對著 production 跑了一個「清理」腳本,只因為從來沒人告訴它 production 是特別的。
六、修種子,不要修果子
這是讓上面所有努力產生複利的那個習慣。
agent 做錯時,你可以直接修「輸出」——改掉那段程式碼、繼續往下走。這修掉了「這一顆」果子。但壞掉的種子還埋在土裡,明天它會再長出一模一樣的錯。
槓桿更高的做法,是去修「指令」。agent 用錯了測試框架?別只是把測試重寫一遍——把「我們用 Vitest,不是 Jest」加進 agent.md。agent 一直去抓某個已棄用的 helper?把它加進「Don't」清單。每一次修正都變成永久的,同樣的錯就不會在往後每個 session 一再出現。
當下慢一點,一個月下來快非常多。你不再是在修輸出,而是在改善那個「產生輸出的東西」。
一點誠實的但書
這一切都還不是定下來的標準。你的 context 檔案還沒有一個 npm test 能跑、沒有公認的 linter 來檢查指令、也沒有 CI gate 會在你的 agent.md 跟現實脫節時亮紅燈。Context Development Lifecycle 是一個有用的視角,不是一套完成的工具鏈——工具都還在即時被發明出來,今天某些「最佳實務」,一年後回頭看大概會覺得很土。
但你不需要等工具鏈成熟,就能把大部分的價值先拿到手。版控的指令檔、分層的規則、可查證的事實、被尊重的 context window,加上「修種子而不是修果子」的紀律——這些今天就能做。它就是「一個一直在跟你作對的 agent」和「一個感覺真的懂你專案的 agent」之間的差別。
你的 agent,很可能比你的 context 願意讓它表現出來的,要好得多。
留一個問題給你
在你「自己」的 agent 指令檔裡,目前最有價值的一行是哪一行——那個讓某個一再發生的錯,從此戛然而止的事實?留言告訴我,好的我想偷來用。
這篇延伸自 Patrick Debois 的 Context Development Lifecycle——他的原文 Optimizing Context for AI Coding Agents 是這個想法更完整的版本。
Top comments (0)