Context Engineering 深度解析:Prompt 寫得好,為什麼 Agent 還是做錯?
這是「Agentic Engineering 實戰手冊」系列第四篇。上一篇:2026 年 AI Coding 工具怎麼選?
Prompt 很清楚,Agent 為什麼還是選錯?
假設你請 agent 替 Astro 部落格加一個「相關文章」區塊。需求寫得很完整:依 tags 排序、顯示三篇、排除自己、手機版要能閱讀。
結果它做出一個 React component,從不存在的 /api/posts 呼叫資料,再用專案沒裝的 CSS-in-JS library 排版。
任務描述沒有錯。Agent 也不是完全沒理解功能;它只是不知道三件事:
- 這是 static Astro site,不是有 API server 的 React app
- 文章來自 Astro Content Collections
- 專案已經有
PostCard.astro和 Tailwind 樣式
這三件事不一定要全部塞在 prompt 裡,但 agent 在決策前必須有辦法取得。這就是 Context Engineering 要處理的問題:在每一步,把會影響下一個決策的正確資訊,放進模型可用的工作狀態。
Context Engineering 不等於「Prompt 寫更長」
Anthropic 的工程文章把 context engineering 定義成:策劃與維護推論時會進入模型的資訊,不只包含 prompt,也包含 system instructions、tools、外部資料與 message history。
它提出的實用原則是:找出最小、但訊號足夠的 token 集合,提高產生期望結果的機率。
這裡有三個容易忽略的重點:
- Context 是每一步的狀態,不是開場文件包。 Agent 搜尋檔案、跑測試、讀 log 之後,context 會持續改變。
- 更多不一定更好。 無關資料會佔用有限注意力,也可能把 agent 帶往錯誤方向。
- Context 不能取代驗證。 資訊給得再完整,模型仍然可能推論錯誤;最後要靠可執行回饋收斂。
Prompt Engineering 仍然重要,它負責清楚表達當前任務。Context Engineering 則多問一步:模型還看見哪些規則、事實、工具與歷史?哪些資訊已經過期?下一步真正需要的是什麼?
Context 六層模型
我把 coding agent 的 context 分成六層。這是我的實作框架,不是任何廠商的官方分級;用途是幫助你分辨來源、保存期限與維護責任。
Layer 1:System 與 Runtime Policy
這層包含工具廠商提供的 system instructions、可用能力,以及執行環境實際套用的 sandbox、approval 與組織政策。
你通常不能直接改 system instructions,但可以選擇執行模式與權限。不同工具即使收到同一個 prompt,也可能因工具集合、預設規則和安全邊界不同而採取不同路徑。
設計重點:先理解預設行為,不要用 project rules 假裝自己能覆蓋更高層政策。對高風險任務,技術權限限制比一句「不要刪資料」可靠。
Layer 2:Durable Project Instructions
這是 AGENTS.md、CLAUDE.md、.cursor/rules/ 或 repo 內的開發文件。適合放跨多個 task 都成立的「房子規則」:
## Commands
npm run check
npm run build
## Architecture
- Astro + MDX + Tailwind CSS + TypeScript
- Blog posts live in src/content/blog/
## Conventions
- Keep lang="zh-TW"
- Reuse existing components and CSS tokens
- Do not modify unrelated generated content
設計重點:短、準確、可執行。第一版只放 build/test、核心架構與高頻限制;當 agent 重複犯同一類錯,再補規則。一次性需求不要永久寫進 rules。
Layer 3:Retrieved Code 與文件
Agent 透過搜尋、讀檔、索引或 language server 取得 repo 內容。不同工具的 retrieval 策略不同,因此不要因為它說「理解 codebase」,就假設每次都看到了正確範圍。
設計重點:在實作前要求它先列出相關檔案、現有模式、資料流與未知處。清楚的檔名、資料夾結構與一致的 abstraction,同時幫助人和 agent 找到事實。
Layer 4:Working History
這是目前 session 的訊息、計畫、程式碼片段與工具結果。它會隨任務累積,也最容易混入已被推翻的方案、重複 log 和過期假設。
設計重點:重要決策與未完成項目要寫入 plan 或 note,不要只靠對話記憶。長任務可以使用 compaction 或開新 session,但摘要後要重新確認限制,因為壓縮一定可能遺失細節。
Layer 5:Tools 與 External Data
MCP、API、issue tracker、設計文件、資料庫 view、瀏覽器與其他工具,讓 agent 能按需取得 repo 之外的資訊或執行動作。
設計重點:只開當前任務需要的最小工具集合,並區分「讀取資料」與「改變外部狀態」的權限。工具回傳的是新 evidence,不是自動可信的真相;來源、時間與存取範圍都要能查核。
Layer 6:Runtime Feedback
這是 test failure、type error、build output、browser screenshot、git diff、log 與 metric。它們反映程式在真實環境的反應,是 agent 從猜測走向收斂的關鍵。
提出假設 → 修改 → 執行檢查 → 讀取結果 → 修正或回退
設計重點:把驗證指令做成 agent 可直接執行、結果可判讀。若測試需要一串只存在某位工程師腦中的手動步驟,agent 和新同事都很難利用它。
六層總覽
下面的表格列出六層各自的內容和風險,但沒講一件事:同一次 agent 執行裡,這六層「動」的頻率完全不一樣。畫出來會更清楚:
flowchart TD
subgraph FIXED["幾乎不動:跨任務都成立"]
L1["Layer 1<br/>System 與 Runtime Policy"]
L2["Layer 2<br/>Durable Project Instructions"]
end
subgraph LOOP["每輪變動:隨任務持續累積"]
L3["Layer 3<br/>Retrieved Code 與文件"]
L4["Layer 4<br/>Working History"]
L5["Layer 5<br/>Tools 與 External Data"]
L6["Layer 6<br/>Runtime Feedback"]
end
L1 --> L2
L2 --> L3
L3 --> L4
L4 --> L5
L5 --> L6
L6 -->|"讀取結果<br/>修正或回退"| L4
Layer 1 和 Layer 2 幾乎不會在一次任務裡變動——一個是你通常改不了的政策,一個是刻意寫成跨任務都成立的房子規則。真正在轉的是 Layer 3 到 Layer 6:retrieved code 隨搜尋而變、working history 隨任務累積、tools 按需呼叫、runtime feedback 每輪都重新產生。那條從 Layer 6 繞回 Layer 4 的線,才是整張圖的重點:讀到的測試結果、build output 不會自動消失,而是被寫回 working history,變成下一輪「提出假設」的依據。這也是為什麼 Layer 4 的設計重點特別提醒你把決策寫進 plan 或 note——這層才是真正在累積、也真正該花力氣維護的地方,不是每次都重寫一遍 Layer 1、Layer 2 那種幾乎不動的規則。
| 層 | 典型內容 | 主要風險 | 維護責任 |
|---|---|---|---|
| System / Policy | 系統規則、tools、sandbox | 誤解權限與優先順序 | 廠商、平台、管理者 |
| Project Instructions | rules、commands、conventions | 過長、衝突、過期 | Repo 維護者 |
| Retrieved Code / Docs | 原始碼、測試、架構文件 | 找錯範圍、讀到舊資料 | Agent + 工程師 |
| Working History | 對話、plan、工具輸出 | 累積雜訊、遺失決策 | Agent + 使用者 |
| Tools / External Data | MCP、API、ticket、browser | 權限過大、不可信內容 | 使用者、平台 |
| Runtime Feedback | test、log、diff、metric | 驗證不完整、錯誤解讀 | 工程團隊 |
一份好 Project Rules,應該寫什麼?
最常見的兩個極端是「什麼都沒寫」和「把整本團隊手冊塞進去」。比較耐用的做法是三層:
必須常駐:每個 Task 都用得到
- build、test、lint、type check 指令
- 主要技術棧與目錄位置
- 重要的禁止事項與安全限制
- 會造成大量返工的核心慣例
用路徑引用:需要時才讀
- 架構決策紀錄
- API contract 與 data model
- deploy、migration、incident runbook
- 完整 style guide 與 review checklist
主規則檔只需要說「何時讀哪一份」,不必複製全文。
做成 Skill 或自動化:重複流程
如果同一套步驟會反覆使用,例如 pre-commit review、部署檢查或文章發佈,把它做成可載入的 skill、script 或 CI job。規則負責導航,自動化負責穩定執行。
判斷某一條是否值得加入時,我會問:
- 它會跨多個 task 長期成立嗎?
- Agent 是否已重複在這裡犯錯?
- 能否寫成具體動作,而不是「寫出高品質 code」?
- 有沒有更可靠的 test、lint 或 permission 可以取代文字提醒?
Context Window 很大,為什麼還要節制?
Context window 的容量增加,不代表模型在每個位置都同樣穩定地使用資訊。
《Lost in the Middle》研究在多文件問答與 key-value retrieval 任務中發現,相關資訊位於長 context 中間時,受測模型的表現常比資訊放在開頭或結尾差。這不是說所有新模型都會以相同比例失準,而是提醒我們:能放進去,不等於能可靠取用。
此外,大量無關 context 還有三個實務成本:
- 增加 latency 與 token cost
- 讓衝突或過期資訊更難被發現
- 誘導 agent 花時間解釋與任務無關的資料
因此不要追求「完整載入」,要追求「當下決策所需」。
三種管理長任務的方法
Anthropic 的 context engineering 指南整理了幾種長任務策略。放進 coding workflow,可以這樣理解。
Progressive Disclosure:先給索引,再按需讀取
開場提供任務、project rules 與關鍵路徑;讓 agent 用搜尋與工具逐步展開,而不是直接貼上五十個檔案。
路徑、query、ticket ID 與文件連結都是輕量索引。它們讓 agent 在需要時取得原文,也比較不容易把舊副本長期留在 context。
Compaction 與 Structured Notes:保留決策,不保留噪音
長 session 接近上限時,把已確認決策、完成項目、失敗嘗試、風險與下一步寫成結構化摘要。大段重複 log 和早已推翻的草案可以丟掉。
摘要不是無損壓縮。重新開始後,要先檢查 acceptance criteria、限制與未驗證項目是否仍在。
Sub-Agent:隔離探索 Context
當任務真的有多個可獨立查核的探索面,可以讓不同 sub-agent 研究 code path、測試模式或外部文件,只把來源、結論與未知處交回主線。
它的代價是更多 token、協調與摘要風險。小任務不需要為了「看起來很 agentic」硬拆;邊界不清的子任務,也容易得到彼此衝突的答案。
Context 也是安全邊界
Agent 讀到的 README、issue、網頁、MCP 回應或 log,都可能包含錯誤內容,甚至刻意引導它忽略原任務。外部文字進入 context 後,不應自動升格成可信指令。
實務上要做四件事:
- 把不可信內容當資料,不當高優先級指令
- 限制 tools、網路與 secrets 到任務最小需要
- 對寫入外部系統、執行部署與刪除資料保留核准
- 要求關鍵主張附來源,關鍵動作留下 audit trail
Context Engineering 不只是在追求更好的答案,也是在控制 agent 能被什麼資訊影響,以及影響之後能做多大的事。
Before / After:同一個任務,Context 怎麼改?
Before:只有功能描述
在文章底部加相關文章區塊,依 tags 推薦三篇。
這句話說清楚了產品功能,沒有說清楚技術環境。Agent 若找不到 project rules 或沒有先探索 repo,就可能選擇訓練資料裡常見、但不適合這個專案的實作。
After:任務 + 可發現的專案事實 + 驗證
Project rules 已經包含技術棧、目錄與檢查指令。Task spec 再補上當次限制:
在 BlogLayout.astro 底部加入相關文章。
Acceptance criteria:
- 使用 Astro Content Collections 取得文章
- 依 tags 交集數量排序,最多三篇
- 排除當前文章與 draft
- 重用既有 PostCard.astro
- 不新增 client-side framework
- 完成後執行 npm run check 與 npm run build
Agent 還是要自己搜尋 BlogLayout.astro、collection schema 與 PostCard.astro;不同之處是它知道去哪裡找、哪些限制不能自行發明,最後也有可執行的完成證據。
差別不在 prompt 變長,而在每一種資訊放到對的生命週期:長期規則留在 repo,任務限制留在 spec,程式事實即時讀取,正確性透過 runtime 驗證。
五個常見失敗模式
1. 把所有文件都設成 Always-On
常駐內容越多,越容易衝突與過期。主規則保留導航和高頻限制,細節按需讀取。
2. 規則只寫偏好,沒有可執行結果
「保持 clean code」沒有共同判準;「不要新增日期 formatter,先搜尋 src/lib」才會改變行為。
3. 同一條規則散落多處
Global、repo、子目錄與 task prompt 各寫一份,遲早互相矛盾。保留單一事實來源,其他地方只引用。
4. 相信 Tool Output 就是真相
API 可能回舊資料、測試可能只跑了一部分、文件可能已過期。記錄來源、時間、執行範圍與 exit code。
5. 任務結束後不把教訓帶回系統
若 agent 每次都在同一處失敗,問題可能不在 prompt,而在缺少 regression test、文件、命名或權限邊界。把一次修正轉成下一次可重用的防線。
實作 Checklist
開始任務前:
- 成功、失敗與非目標是否清楚?
- Project rules 是否仍正確、沒有衝突?
- Agent 是否知道去哪裡找相關 code 與文件?
- 權限與 tools 是否縮到最小需要?
執行過程中:
- 新證據是否讓原本假設失效?
- 對話是否累積大量無關輸出或舊方案?
- 關鍵決策是否寫進 plan/note,而不是只留在聊天?
完成之前:
- 測試與檢查真的覆蓋 acceptance criteria 嗎?
- 最終 diff、未驗證項目與風險是否清楚?
- 這次重複失敗是否該變成規則、測試或自動化?
Takeaway
- Context Engineering 管的是模型每一步可用的完整狀態,不只是開場 prompt。
- 最小且高訊號,比把所有資料塞滿 context window 更可靠。
- 好的 context 讓 agent 找到事實;runtime feedback 才讓它驗證假設。
- 外部 context 也可能不可信,必須和權限、核准與 audit 一起設計。