learn-claude-code 教學手冊
版本:v1.2(2026-07)
適用對象:資深工程師、架構師、AI Agent 開發團隊
定位:企業級 AI Coding Agent Harness Engineering 實戰白皮書
參考專案:shareAI-lab/learn-claude-code(69.3k+ Stars,MIT License)
線上學習平台:learn.shareai.run
目錄
- 1. 專案概述
- 2. 系統整體架構設計
- 3. Agent 核心機制解析(20 Sessions)
- 3.1 Agent Loop(s01)
- 3.2 Tool Use 與 Dispatch Map(s02)
- 3.3 Permission 三層防護(s03)
- 3.4 Hooks 擴展點機制(s04)
- 3.5 Planning / Reflection — TodoWrite(s05)
- 3.6 Subagent 機制(s06)
- 3.7 Skills 按需載入(s07)
- 3.8 Context Compact — 壓縮策略(s08)
- 3.9 Memory System 記憶系統(s09)
- 3.10 System Prompt 動態組裝(s10)
- 3.11 Error Recovery 錯誤恢復(s11)
- 3.12 Task 持久化與相依管理(s12)
- 3.13 Background Tasks(s13)
- 3.14 Cron Scheduler 排程機制(s14)
- 3.15 Agent Teams 與 Mailbox(s15-s16)
- 3.16 Autonomous Agent 自治(s17)
- 3.17 Worktree + Task Isolation(s18)
- 3.18 MCP Plugin 外部工具整合(s19)
- 3.19 完整 Agent 整合(s20)
- 4. Harness Engineering(核心競爭力)
- 5. 實作教學(Step-by-step)
- 6. 建立企業級 Coding Agent
- 7. Multi-Agent 設計(進階)
- 8. 實際應用場景
- 9. 與企業架構整合
- 10. 安全與 SSDLC
- 11. 使用指南(給團隊)
- 12. 最佳實踐(Best Practices)
- 13. 願景:用 Agent 覆蓋每一個領域
- 14. 延伸資源與姊妹專案
- 15. 附錄
1. 專案概述
1.1 learn-claude-code 是什麼
learn-claude-code 是由 shareAI-lab 開源的 AI Coding Agent 教學專案(GitHub 69.3k+ Stars,MIT License,創立於 2025-06),核心理念為「Harness Engineering for Real Agents」。專案副標題精確概括了其定位:
Bash is all you need — A nano claude code-like 「agent harness」, built from 0 to 1
該專案透過 20 個遞進式實作單元(Sessions),從零開始教你構建一個功能完整的 AI Coding Agent 系統。每個 Session 新增一個 Harness 機制,且都附帶一句設計格言:
| 階段 | Sessions | 核心學習 | 新增機制 |
|---|---|---|---|
| Stage 1:核心能力 | s01-s06 | Agent Loop / Tool Dispatch / Permission / Hooks / TodoWrite / Subagent | 基礎 6 大機制 |
| Stage 2:複雜任務 | s07-s11 | Skills / Context Compact / Memory / System Prompt / Error Recovery | 知識管理與容錯 |
| Stage 3:長期運作 | s12-s14 | Task DAG / Background Tasks / Cron Scheduler | 持久化與排程 |
| Stage 4:多 Agent | s15-s18 | Agent Teams / Protocols / Autonomous / Worktree | 團隊協作 |
| Stage 5:整合延伸 | s19-s20 | MCP Plugin / 完整整合 | 外部工具 + 系統整合 |
各 Session 格言速覽:
| Session | 格言 | 核心機制 |
|---|---|---|
| s01 | “One loop & Bash is all you need” | Agent Loop |
| s02 | “Adding a tool means adding one handler” | Tool Dispatch Map |
| s03 | “Set boundaries first, then grant freedom” | Permission 三閘門 |
| s04 | “Hook around the loop, never rewrite the loop” | Hooks 擴展點 |
| s05 | “An agent without a plan drifts” | TodoWrite |
| s06 | “Break big tasks down; each subtask gets clean context” | Subagent |
| s07 | “Load knowledge on demand, not upfront” | Skills 按需載入 |
| s08 | “Context always fills up — have a way to make room” | 四層 Context Compact |
| s09 | “Remember what matters, forget what doesn’t” | Memory System |
| s10 | “Prompts are assembled at runtime, not hardcoded” | 動態 System Prompt |
| s11 | “Errors aren’t the end, they’re the start of a retry” | Error Recovery |
| s12 | “Break big goals into small tasks, order them, persist” | Task DAG |
| s13 | “Slow ops go background, agent keeps thinking” | Background Tasks |
| s14 | “Fire on schedule, no human kick needed” | Cron Scheduler |
| s15 | “Too big for one agent — delegate to teammates” | Agent Teams |
| s16 | “Teammates need shared communication rules” | Team Protocols |
| s17 | “Teammates check the board, claim work themselves” | Autonomous Agents |
| s18 | “Each works in its own directory, no interference” | Worktree Isolation |
| s19 | “Not enough capability? Plug in more via MCP” | MCP Plugin |
| s20 | “Many mechanisms, one loop” | 完整整合 |
專案配套提供三語文件(English / 中文 / 日本語)、互動學習平台(learn.shareai.run),以及基於 pytest 的 CI 煙霧測試。
專案不教你「使用」現成 AI 工具,而是教你「如何從零建構」一個類似 Claude Code 的代理系統,理解每一個機制背後的設計原理。
1.2 Agency 的本質 — 來自模型訓練而非程式碼
在討論任何程式碼之前,必須先釐清一個根本性的認知:
Agency — 感知、推理、行動的能力 — 來自模型訓練,而非外部程式碼的編排。
但一個能運作的 Agent 產品,需要模型和 Harness 缺一不可。模型是駕駛者,Harness 是載具。本專案教你造載具。
Agent Product = Model(智能) + Harness(環境)
│ │
│ ├── Tools(工具)
│ ├── Knowledge(知識)
│ ├── Observation(觀測)
│ ├── Action Interfaces(行動介面)
│ └── Permissions(權限)
│
└── 由訓練而得的感知、推理、行動能力每一個 Agent 的核心都是一個神經網路 — Transformer、RNN 或某種經過訓練的函式 — 經過數十億次梯度更新,在行動序列資料上學會了感知環境、推理目標、採取行動。Agency 從來不是外圍程式碼賦予的,而是模型在訓練中習得的。
人類本身就是最佳範例:一個由數百萬年演化訓練出來的生物神經網路,透過感官感知世界,透過大腦推理,透過身體行動。當 DeepMind、OpenAI 或 Anthropic 說「Agent」時,他們的核心含義永遠相同:一個通過訓練學會行動的模型,加上讓它能在特定環境中運作的基礎設施。
1.3 Agency 的歷史脈絡
歷史上的每一個 Agent 里程碑,都證實了同一個事實 — Agency 是訓練出來的,不是編出來的:
| 年份 | 里程碑 | 意義 |
|---|---|---|
| 2013 | DeepMind DQN 玩 Atari | 單一神經網路,僅接收原始像素和分數,學會 7 款 Atari 遊戲,超越所有先前演算法。2015 年擴展至 49 款遊戲並達到職業測試員水準,發表於 Nature。沒有遊戲專屬規則,沒有決策樹。一個模型,從經驗中學習。 |
| 2019 | OpenAI Five 征服 Dota 2 | 五個神經網路自我對戰 45,000 年的 Dota 2,2-0 擊敗 TI8 世界冠軍 OG。公開競技場 42,729 場比賽勝率 99.4%。模型完全透過自我對弈學會團隊協作和戰術。 |
| 2019 | DeepMind AlphaStar 制霸星際爭霸 II | 閉門賽 10-1 擊敗職業選手,在歐洲伺服器達到宗師段位 — 90,000 名玩家中的前 0.15%。 |
| 2019 | 騰訊絕悟統治王者榮耀 | 5v5 擊敗 KPL 職業選手。1v1 模式職業選手 15 場僅贏 1 場。訓練強度:一天等於人類 440 年。完全從零通過自我對弈學習整個遊戲。 |
| 2024-2025 | LLM Agent 重塑軟體工程 | Claude、GPT、Gemini — 在人類全部程式碼和推理上訓練的大型語言模型 — 被部署為 Coding Agent。它們閱讀程式碼庫、撰寫實作、除錯失敗、團隊協作。架構與先前每一個 Agent 完全相同。 |
每一個里程碑都指向同一個事實:模型提供智能,環境提供行動空間,兩者合在一起才是完整的 Agent。
1.4 Agent 不是什麼 — 破除「提示詞水管工」迷思
「Agent」這個詞已被一整個「提示詞水管工」產業劫持。拖拽式工作流建構器、無程式碼「AI Agent」平台、Prompt Chain 編排函式庫 — 它們共享同一個幻覺:把 LLM API 呼叫用 if-else 分支、節點圖、硬編碼路由邏輯串在一起就算是「建構 Agent」了。
事實並非如此。 它們造出來的東西是魯布.戈德堡機械 — 一個過度工程化的、脆弱的程序式規則流水線,LLM 被嵌入其中充當美化過的文字補全節點。那不是 Agent,那是一個有著宏大妄想的 Shell Script。
❌ 提示詞水管工「Agent」 ✅ 真正的 Agent
══════════════════════ ══════════════════
┌──────┐ ┌──────────────┐
│ Node │→ if/else │ Model │
│ Graph│→ prompt chain │ (trained) │
│ │→ hardcoded routing │ │
│ │→ rule trees │ 決策 + 推理 │
└──────┘ └──────┬───────┘
│
脆弱、不可擴展、 ┌──────┴───────┐
無法泛化 │ Harness │
│ Tools + │
│ Knowledge + │
│ Permissions │
└──────────────┘
靈活、可組合、
跨領域泛化核心認知:你不可能透過工程手段「編碼」出 Agency。Agency 是學出來的,不是編出來的。那些系統是 GOFAI(Good Old-Fashioned AI,經典符號 AI)的現代還魂 — 幾十年前就被學界拋棄的符號規則系統,現在噴了一層 LLM 的漆又登場了。
1.5 心智轉換:從「開發 Agent」到開發 Harness
當一個人說「我在開發 Agent」時,他只可能是兩個意思之一:
1. 訓練模型。 透過強化學習、微調、RLHF 或其他基於梯度的方法調整權重。收集任務過程資料(Task-Process Data) — 真實領域中感知、推理、行動的實際序列 — 用它們來塑造模型的行為。這是 DeepMind、OpenAI、騰訊 AI Lab、Anthropic 在做的事。
2. 建構 Harness。 撰寫程式碼,為模型提供一個可操作的環境。這是我們大多數人在做的事,也是 learn-claude-code 的核心。
模型做決策 ←→ Harness 執行
模型做推理 ←→ Harness 提供上下文
模型是駕駛者 ←→ Harness 是載具
程式開發 Agent 的 Harness = IDE + 終端機 + 檔案系統
農業 Agent 的 Harness = 感測器 + 灌溉控制 + 氣象資料
飯店 Agent 的 Harness = 訂房系統 + 客戶通訊 + 設施管理 API
Agent(智能、決策者)永遠是模型。Harness 因領域而變。Agent 跨領域泛化。learn-claude-code 教你造載具。程式開發用的載具。但設計模式可以泛化到任何領域:莊園管理、農田運營、工廠製造、物流調度、醫療保健、教育培訓、科學研究。只要有一個任務需要被感知、推理和執行 — Agent 就需要一個 Harness。
1.6 為何選擇 Claude Code 作為教學標本
為什麼 learn-claude-code 專門拆解 Claude Code?
因為 Claude Code 是目前所見最優雅、最完整的 Agent Harness 實作。不是因為某個巧妙的技巧,而是因為它沒做的事:它沒有試圖成為 Agent 本身。它沒有強加僵化的工作流。它沒有用精心設計的決策樹去替模型做判斷。它給模型提供了工具、知識、上下文管理和權限邊界 — 然後讓開了。
Claude Code 本質:
一個 Agent Loop
+ 工具(bash, read, write, edit, glob, grep, browser...)
+ 按需 Skill 載入
+ Context 壓縮
+ Subagent 派生
+ 帶相依圖的任務系統
+ 非同步郵箱的團隊協調
+ Worktree 隔離的平行執行
+ 權限治理每一個元件都是 Harness 機制 — 為 Agent 建構的棲居世界的一部分。Agent 本身呢?是 Claude。一個模型。由 Anthropic 在人類推理和程式碼的全部廣度上訓練而成。Harness 沒有讓 Claude 變聰明。Claude 本來就聰明。Harness 給了 Claude 雙手、雙眼和一個工作空間。
learn-claude-code 的每一個課程(s01-s12)都在逆向工程 Claude Code 架構中的一個 Harness 機制。學完之後,你理解的不只是 Claude Code 怎麼運作,而是適用於任何領域、任何 Agent 的 Harness 工程通用原則。
啟示:最好的 Agent 產品,出自那些明白自己的工作是 Harness 而非 Intelligence 的工程師之手。
1.7 與 Claude Code / Cursor 的差異
| 比較項目 | Claude Code | Cursor | learn-claude-code |
|---|---|---|---|
| 性質 | Anthropic 官方商用產品 | 商用 IDE | 開源教學專案 |
| 目的 | 直接使用 | 直接使用 | 學習原理並自建 Agent |
| 模型綁定 | Claude 系列 | 多模型 | 可替換任意 LLM |
| 可客製化 | 有限(CLAUDE.md / Hooks) | Plugin 系統 | 完全可控 |
| 企業適用性 | 受限於 API 政策 | 受限於 SaaS | 可私有部署、完全自主 |
| 原始碼 | 閉源 | 閉源 | MIT 開源 |
| 安全控管 | 依賴平台 | 依賴平台 | 自行設計權限邊界 |
| 成本 | 按 Token 計費 | 訂閱制 | 自選模型,成本可控 |
| 學習價值 | 黑箱 | 黑箱 | 每個機制透明可學 |
| Task-Process Data | 不可得 | 不可得 | 完整保留,可用於微調 |
1.8 為何企業應該自己打造 Agent
企業自建 Agent 的六大理由
資料主權與合規
- 金融業程式碼不可外洩至第三方 SaaS
- GDPR / 個資法 / 金管會要求資料落地
- 自建 Agent 可部署於內網、Air-gapped 環境
深度客製化
- 整合企業特有的框架、規範、流程
- 注入企業知識庫(內部 API 文件、架構決策紀錄)
- 自訂 Tool 對接企業基礎設施(DB2 / MQ / 內部 CI)
成本最佳化
- 可選用開源模型(DeepSeek / Qwen / Llama)
- 混合路由:簡單任務用小模型、複雜任務用大模型
- 避免被單一供應商綁定
安全邊界可控
- 自行設計 Tool 權限、沙箱隔離
- 實作 Audit Log、合規審計
- Prompt Injection 防護策略可客製
競爭力護城河
- 累積企業特有的 Task-Process Data(訓練素材)
- Agent 效能隨使用持續提升
- 形成不可複製的智能開發能力
Task-Process Data 資產化
- Agent 在 Harness 中執行的每一條行動序列都是訓練信號
- 真實部署中的感知—推理—行動軌跡是微調下一代 Agent 模型的原材料
- 你的 Harness 不僅服務於 Agent — 它還能幫助進化 Agent
實務建議:建議企業從 learn-claude-code 學習核心機制,再基於此架構開發符合企業需求的 Agent 產品。初期可搭配 Claude / GPT API,中期逐步導入開源模型降低成本。
2. 系統整體架構設計
2.1 Agent Runtime 架構
graph TB
subgraph "Agent Runtime"
AL[Agent Loop<br/>while + stop_reason]
TL[Tool Layer<br/>Dispatch Map]
SM[System Message<br/>角色 + 規則]
MM[Message Manager<br/>對話歷史]
end
subgraph "LLM Gateway"
LLM[LLM API<br/>Claude / GPT / DeepSeek]
end
subgraph "Tool Ecosystem"
BT[Bash Tool]
RF[Read File]
WF[Write File]
EF[Edit File]
GR[Grep / Glob]
TD[TodoWrite]
SA[Subagent]
TK[Task Manager]
end
subgraph "Persistence Layer"
FS[File System]
TQ[Task Queue<br/>JSONL]
MB[Mailbox<br/>JSONL]
WT[Worktree]
end
User -->|prompt| AL
AL -- "messages[]" --> LLM
LLM -->|response| AL
AL -->|tool_use| TL
TL --> BT & RF & WF & EF & GR & TD & SA & TK
TL -->|tool_result| AL
TK --> TQ
SA --> MB
BT --> FS
RF --> FS
WF --> FS
TK --> WT核心運作流程:
User Request
│
▼
┌─────────────────────────────────────────┐
│ Agent Loop │
│ ┌──────────────────────────────────┐ │
│ │ 1. 組裝 messages[] │ │
│ │ 2. 呼叫 LLM API │ │
│ │ 3. 檢查 stop_reason │ │
│ │ ├─ "tool_use" → 執行工具 │ │
│ │ │ ├─ 取得 tool_result │ │
│ │ │ └─ 加入 messages[] → 回到 1│ │
│ │ └─ "end_turn" → 回傳結果 │ │
│ └──────────────────────────────────┘ │
└─────────────────────────────────────────┘
│
▼
Response to User2.2 LLM Gateway(可替換 OpenAI / Claude)
learn-claude-code 採用 Anthropic Messages API 格式,但架構上支援 LLM 替換:
# 抽象 LLM Gateway 設計
import os
from anthropic import Anthropic
# 基本配置(支援替換 base_url)
client = Anthropic(
api_key=os.getenv("ANTHROPIC_API_KEY"),
base_url=os.getenv("ANTHROPIC_BASE_URL", "https://api.anthropic.com"),
)
MODEL = os.getenv("MODEL", "claude-sonnet-4-6")企業級 LLM Gateway 設計建議:
┌───────────────────────────────────┐
│ LLM Gateway │
│ ┌─────────────────────────────┐ │
│ │ Model Router │ │
│ │ ├─ 簡單任務 → DeepSeek │ │
│ │ ├─ 複雜任務 → Claude │ │
│ │ ├─ 程式碼 → GPT-4o │ │
│ │ └─ 本地機密 → 私有模型 │ │
│ ├─────────────────────────────┤ │
│ │ Rate Limiter │ │
│ │ Token Counter │ │
│ │ Cost Tracker │ │
│ │ Retry / Fallback │ │
│ └─────────────────────────────┘ │
└───────────────────────────────────┘| 模型 | 適用場景 | 成本級別 | 建議用途 |
|---|---|---|---|
| Claude Sonnet | 一般開發任務 | 中 | 主力模型 |
| Claude Opus | 複雜架構設計 | 高 | 關鍵決策 |
| DeepSeek V3 | 程式碼生成 | 低 | 大量批次 |
| Qwen 2.5 | 中文文件 | 低 | 文件產生 |
| 本地模型 | 機密程式碼 | 硬體成本 | 內網使用 |
2.3 Tool Layer 設計
Tool Layer 是 Harness 的核心,採用 Dispatch Map 模式:
# Tool 定義(JSON Schema 格式)
TOOLS = [
{
"name": "bash",
"description": "執行 shell 命令。用於編譯、測試、Git 操作等。",
"input_schema": {
"type": "object",
"properties": {
"command": {
"type": "string",
"description": "要執行的 shell 命令"
}
},
"required": ["command"]
}
},
{
"name": "read_file",
"description": "讀取檔案內容",
"input_schema": {
"type": "object",
"properties": {
"path": {"type": "string", "description": "檔案路徑"},
"start_line": {"type": "integer", "description": "起始行號"},
"end_line": {"type": "integer", "description": "結束行號"}
},
"required": ["path"]
}
},
{
"name": "write_file",
"description": "寫入檔案",
"input_schema": {
"type": "object",
"properties": {
"path": {"type": "string", "description": "檔案路徑"},
"content": {"type": "string", "description": "檔案內容"}
},
"required": ["path", "content"]
}
}
]
# Dispatch Map(名稱 → 處理函式)
TOOL_HANDLERS = {
"bash": handle_bash,
"read_file": handle_read_file,
"write_file": handle_write_file,
"edit_file": handle_edit_file,
"glob": handle_glob,
"grep": handle_grep,
"todo_write": handle_todo_write,
"subagent": handle_subagent,
}新增 Tool 的標準流程:
1. 定義 Tool Schema(JSON Schema)
2. 實作 Handler 函式
3. 註冊到 TOOL_HANDLERS dict
4. Agent Loop 無需修改 — 自動 dispatch格言:
"Adding a tool means adding one handler"— 迴圈不變,新工具只需註冊進 dispatch map。
2.4 Task Queue 與持久化
Task 系統使用 檔案式 CRUD + 相依圖(Dependency Graph) 設計:
# Task 資料結構
{
"id": "task-001",
"title": "實作使用者登入 API",
"status": "in_progress", # todo / in_progress / done / blocked
"assignee": "backend-agent",
"dependencies": ["task-000"], # 相依任務 ID
"worktree": "/worktrees/task-001",
"created_at": "2026-05-02T10:00:00Z",
"updated_at": "2026-05-02T10:30:00Z"
}Task 相依圖範例:
task-001 (DB Schema)
│
├── task-002 (Entity 實作) ──┐
│ ├── task-004 (API 整合測試)
└── task-003 (API 實作) ────┘
│
task-005 (部署)2.5 Workspace / Worktree 隔離
每個 Task 分配獨立的 Git Worktree,避免平行執行互相干擾:
專案根目錄/
├── .git/ # 共用 Git 倉庫
├── main-workspace/ # 主工作區
├── worktrees/
│ ├── task-001/ # Task 1 獨立工作區
│ │ ├── src/
│ │ └── ...
│ ├── task-002/ # Task 2 獨立工作區
│ │ ├── src/
│ │ └── ...
│ └── task-003/ # Task 3 獨立工作區# 建立隔離的 Worktree
git worktree add worktrees/task-001 -b feature/task-001
# 在隔離環境中執行任務
cd worktrees/task-001
# Agent 在此目錄中讀寫檔案、執行測試
# 完成後合併回主分支
git checkout main
git merge feature/task-001
git worktree remove worktrees/task-0012.6 Memory 系統(短期 / 長期)
┌─────────────────────────────────────────┐
│ Memory 架構 │
│ │
│ ┌─────────────┐ ┌─────────────────┐ │
│ │ 短期記憶 │ │ 長期記憶 │ │
│ │ (Context) │ │ (Persistent) │ │
│ │ │ │ │ │
│ │ • messages[]│ │ • Skills 文件 │ │
│ │ • Tool 結果 │ │ • Task 歷史 │ │
│ │ • 當前 Todo │ │ • 架構決策紀錄 │ │
│ │ │ │ • 錯誤修復經驗 │ │
│ └──────┬──────┘ └────────┬────────┘ │
│ │ │ │
│ ▼ ▼ │
│ Context Compact On-Demand Load │
│ (三層壓縮) (tool_result 注入) │
└─────────────────────────────────────────┘三層壓縮策略(s06):
| 層級 | 觸發時機 | 壓縮方式 | 保留內容 |
|---|---|---|---|
| L1 | Token 用量 > 50% | 摘要舊對話 | 近期完整 + 遠期摘要 |
| L2 | Token 用量 > 70% | 移除工具細節 | 僅保留結論 |
| L3 | Token 用量 > 90% | 僅保留當前任務 | 任務描述 + 關鍵結果 |
2.7 Multi-Agent Coordination
graph TB
subgraph "Lead Agent"
LA[Lead Agent<br/>任務分派 + 協調]
end
subgraph "Agent Team"
BA[Backend Agent]
FA[Frontend Agent]
QA[QA Agent]
DA[DevOps Agent]
end
subgraph "Communication"
MB1[Mailbox<br/>backend.jsonl]
MB2[Mailbox<br/>frontend.jsonl]
MB3[Mailbox<br/>qa.jsonl]
MB4[Mailbox<br/>devops.jsonl]
end
LA -->|任務分派| MB1 & MB2 & MB3 & MB4
MB1 -->|讀取任務| BA
MB2 -->|讀取任務| FA
MB3 -->|讀取任務| QA
MB4 -->|讀取任務| DA
BA & FA & QA & DA -->|回報結果| LAJSONL Mailbox 協定:
{"from": "lead", "to": "backend", "type": "request", "task_id": "task-002", "content": "實作 UserService.login() 方法", "timestamp": "2026-05-02T10:00:00Z"}
{"from": "backend", "to": "lead", "type": "response", "task_id": "task-002", "content": "已完成,通過 5 個單元測試", "timestamp": "2026-05-02T10:15:00Z"}
{"from": "lead", "to": "qa", "type": "request", "task_id": "task-003", "content": "對 UserService.login() 進行整合測試", "timestamp": "2026-05-02T10:16:00Z"}2.8 與企業系統整合方式
graph LR
subgraph "Agent System"
AS[Agent Runtime]
TL[Tool Layer]
end
subgraph "Enterprise Systems"
DB[(DB2 / Oracle<br/>PostgreSQL)]
MQ[Kafka /<br/>RabbitMQ]
CI[Jenkins /<br/>GitHub Actions]
GIT[GitLab /<br/>GitHub]
JIRA[Jira /<br/>Azure DevOps]
REG[Container<br/>Registry]
end
TL -->|JDBC / SQL Tool| DB
TL -->|Producer/Consumer Tool| MQ
TL -->|Pipeline Trigger Tool| CI
TL -->|Git Operations Tool| GIT
TL -->|Issue CRUD Tool| JIRA
TL -->|Docker Tool| REG整合方式:
| 企業系統 | 整合 Tool | 用途 |
|---|---|---|
| DB2 / Oracle | db_query Tool | Schema 分析、資料查詢、Migration |
| Kafka | mq_publish / mq_subscribe Tool | 事件驅動整合 |
| Jenkins | ci_trigger Tool | 觸發建置 / 部署 |
| Jira | issue_crud Tool | 自動建立 Issue / 更新狀態 |
| SonarQube | security_scan Tool | 程式碼品質 / 安全掃描 |
實務案例:某金融機構將 Agent 整合至內部 GitLab + Jenkins 流水線,Agent 可自動建立 MR、觸發 CI、等待建置結果後修復失敗的測試。
2.9 範圍說明與設計限制(Scope)
learn-claude-code 是一個聚焦於 Harness 核心機制 的教學專案,並非一個生產就緒的框架。理解它刻意省略的部分,對於正確使用本教材至關重要:
| 刻意省略的機制 | 說明 | 原因 |
|---|---|---|
| 完整的事件/Hook 匯流排 | 未實作 event bus / hook 系統 | 教學專注於 Agent Loop 本身,事件系統屬於應用層 |
| 規則式權限治理引擎 | 未實作細粒度的 RBAC / ABAC | 教學示範權限概念,生產級治理需企業自行設計 |
| Session 生命週期管理 | 未實作完整的連線管理 / 重連 / Session 持久化 | 屬於部署基礎設施層面 |
| 完整的 MCP Runtime | 未深入 Model Context Protocol 的完整規格 | MCP 是獨立規範,本專案聚焦於 Harness 設計模式 |
| 生產級錯誤恢復 | 未實作跨 Session 的錯誤恢復 | 教學環境以單次執行為主 |
重要提醒:這些省略是 設計決策,不是遺漏。learn-claude-code 的價值在於讓你完全理解每一個 Harness 機制的設計原理,而非提供一個可以直接 copy-paste 到生產環境的框架。企業應基於這些設計原理,結合自身需求建構生產級系統。
3. Agent 核心機制解析(20 Sessions)
3.1 Agent Loop(s01)
格言:
"One loop & Bash is all you need"— 一個迴圈 + 一個工具 = 一個 Agent
Agent Loop 是整個系統的心臟,核心程式碼僅約 20 行:
import os
from anthropic import Anthropic
client = Anthropic()
MODEL = os.getenv("MODEL", "claude-sonnet-4-6")
SYSTEM = "你是一位資深軟體工程師。使用 bash 工具執行命令。"
TOOLS = [{
"name": "bash",
"description": "執行 shell 命令",
"input_schema": {
"type": "object",
"properties": {
"command": {"type": "string", "description": "Shell 命令"}
},
"required": ["command"]
}
}]
def handle_bash(command: str) -> str:
"""安全執行 shell 命令"""
import subprocess
try:
result = subprocess.run(
command, shell=True, capture_output=True,
text=True, timeout=30, cwd=os.getcwd()
)
output = result.stdout + result.stderr
return output[:10000] # 限制輸出長度
except subprocess.TimeoutExpired:
return "ERROR: 命令執行超時(30 秒)"
except Exception as e:
return f"ERROR: {e}"
TOOL_HANDLERS = {"bash": handle_bash}
def agent_loop(user_message: str):
"""核心 Agent 迴圈"""
messages = [{"role": "user", "content": user_message}]
while True:
response = client.messages.create(
model=MODEL,
system=SYSTEM,
max_tokens=4096,
messages=messages,
tools=TOOLS,
)
# 將 assistant 回覆加入歷史
messages.append({
"role": "assistant",
"content": response.content
})
# 如果模型決定停止(不再呼叫工具),結束迴圈
if response.stop_reason != "tool_use":
# 印出最終文字回覆
for block in response.content:
if hasattr(block, "text"):
print(block.text)
return
# 執行模型請求的所有工具
results = []
for block in response.content:
if block.type == "tool_use":
print(f"[Tool] {block.name}: {block.input}")
output = TOOL_HANDLERS[block.name](**block.input)
results.append({
"type": "tool_result",
"tool_use_id": block.id,
"content": output,
})
# 將工具結果作為 user 訊息送回
messages.append({"role": "user", "content": results})
# 使用
if __name__ == "__main__":
agent_loop("列出當前目錄下的所有 Python 檔案,並統計行數")運作流程圖:
User: "列出所有 Python 檔案"
│
▼
Agent Loop 開始
│
▼
LLM 決定呼叫 bash tool
│
▼
執行: find . -name "*.py" | wc -l
│
▼
tool_result: "42"
│
▼
LLM 收到結果,決定再呼叫 bash
│
▼
執行: find . -name "*.py" -exec wc -l {} +
│
▼
tool_result: "共 3,247 行"
│
▼
LLM 決定停止 (stop_reason = "end_turn")
│
▼
回覆: "找到 42 個 Python 檔案,共 3,247 行程式碼"關鍵理解:模型決定何時呼叫工具、呼叫哪個工具、何時停止。程式碼只負責執行模型的決策。
3.2 Tool Use 與 Dispatch Map(s02)
格言:
"Adding a tool means adding one handler"— 新增工具不需修改迴圈
# 新增 read_file 和 write_file 工具
def handle_read_file(path: str, start_line: int = None, end_line: int = None) -> str:
"""讀取檔案,支援行號範圍"""
try:
with open(path, "r", encoding="utf-8") as f:
lines = f.readlines()
if start_line and end_line:
lines = lines[start_line - 1:end_line]
return "".join(lines)[:10000]
except FileNotFoundError:
return f"ERROR: 檔案不存在: {path}"
except Exception as e:
return f"ERROR: {e}"
def handle_write_file(path: str, content: str) -> str:
"""寫入檔案(含目錄自動建立)"""
os.makedirs(os.path.dirname(path) or ".", exist_ok=True)
with open(path, "w", encoding="utf-8") as f:
f.write(content)
return f"OK: 已寫入 {path}({len(content)} 字元)"
def handle_edit_file(path: str, old_string: str, new_string: str) -> str:
"""精確替換檔案中的字串"""
with open(path, "r", encoding="utf-8") as f:
content = f.read()
if old_string not in content:
return f"ERROR: 找不到要替換的字串"
if content.count(old_string) > 1:
return f"ERROR: 找到多個匹配,請提供更多上下文"
new_content = content.replace(old_string, new_string, 1)
with open(path, "w", encoding="utf-8") as f:
f.write(new_content)
return f"OK: 已替換"
# 擴展 Dispatch Map — 迴圈完全不變
TOOL_HANDLERS = {
"bash": handle_bash,
"read_file": handle_read_file,
"write_file": handle_write_file,
"edit_file": handle_edit_file,
}Tool 設計三原則:
- 原子性(Atomic):每個 Tool 做一件事,做好做完
- 可組合(Composable):Tool 之間可自由組合,由模型決定順序
- 描述清晰(Well-described):description 和 schema 要讓模型明確理解用途
3.3 Permission 三層防護(s03)
格言:
"Set boundaries first, then grant freedom"— 先設邊界,再授予自由
Permission 系統是 Agent 安全邊界的核心,採用三閘門管線設計:
class PermissionGateway:
"""三層防護:拒絕清單 → 規則匹配 → 人工審批"""
# 第一層:硬性拒絕清單(永遠不允許)
DENY_LIST = [
r"rm\s+-rf\s+/", # 遞迴刪除根目錄
r"DROP\s+DATABASE", # 刪除資料庫
r"curl.*\|\s*bash", # 遠端程式碼執行
r"chmod\s+777\s+/", # 授予全域寫入
r">\s*/etc/passwd", # 覆蓋系統檔案
]
# 第二層:規則引擎(根據上下文決定)
RULE_ENGINE = [
# 格式:(pattern, condition, verdict)
(r"git push", "branch == 'main'", "REQUIRE_APPROVAL"),
(r"kubectl apply", "namespace == 'prod'", "REQUIRE_APPROVAL"),
(r"\.env", "operation == 'write'","DENY"),
(r"secrets/", "any", "DENY"),
]
def check(self, tool_name: str, tool_input: dict, context: dict) -> tuple:
"""三閘門權限檢查,回傳 (allowed: bool, reason: str)"""
# Gate 1: 硬性拒絕清單
cmd = tool_input.get("command", "") or tool_input.get("path", "")
for pattern in self.DENY_LIST:
if re.search(pattern, cmd, re.IGNORECASE):
return False, f"[DENY_LIST] 禁止操作: {pattern}"
# Gate 2: 規則引擎
for pattern, condition, verdict in self.RULE_ENGINE:
if re.search(pattern, cmd, re.IGNORECASE):
if self._eval_condition(condition, context):
if verdict == "DENY":
return False, f"[RULE] 規則禁止: {condition}"
elif verdict == "REQUIRE_APPROVAL":
return self._request_human_approval(cmd)
# Gate 3: 預設允許(白名單外需人工確認)
return True, "OK"
def _request_human_approval(self, command: str) -> tuple:
"""非同步人工審批"""
print(f"\n⚠️ 需要審批:{command}")
decision = input("允許此操作?[y/n]: ").strip().lower()
return (True, "已核准") if decision == "y" else (False, "已拒絕")三層防護架構:
使用者 Prompt
│
▼
┌─────────────────────────────────────────┐
│ Gate 1: Deny List(硬性拒絕) │
│ • 硬編碼的永久禁止清單 │
│ • 不可繞過,不可設定 │
│ 匹配 → BLOCKED(立即) │
└─────────────┬───────────────────────────┘
│ 通過
▼
┌─────────────────────────────────────────┐
│ Gate 2: Rule Engine(情境規則) │
│ • 根據上下文(分支、環境、操作類型) │
│ • 可設定、可擴展 │
│ 匹配 → DENY 或 REQUIRE_APPROVAL │
└─────────────┬───────────────────────────┘
│ 通過
▼
┌─────────────────────────────────────────┐
│ Gate 3: Human Approval(人工審批) │
│ • 高風險操作需人工確認 │
│ • 非同步等待決策 │
│ 批准 → ALLOWED / 拒絕 → BLOCKED │
└─────────────────────────────────────────┘企業實踐建議:
| 場景 | 建議策略 |
|---|---|
| 開發環境 | Gate 1 + Gate 2(規則寬鬆) |
| 測試環境 | Gate 1 + Gate 2(中等嚴格) |
| 生產環境 | Gate 1 + Gate 2 + Gate 3(全部啟用) |
| CI/CD 自動化 | Gate 1 + Gate 2(無 Gate 3,改用審批 PR) |
格言的深意:安全不是禁止 Agent 做任何事,而是確保它只做被授權的事。過度限制會讓 Agent 無法完成工作;缺乏限制會造成災難。三層防護找到這個平衡點。
3.4 Hooks 擴展點機制(s04)
格言:
"Hook around the loop, never rewrite the loop"— 在迴圈外掛鉤,永遠不要重寫迴圈
Hooks 是 Agent Loop 的擴展點系統,讓你在不修改核心迴圈的情況下注入額外行為:
from typing import Callable, Optional
from dataclasses import dataclass
@dataclass
class HookContext:
"""Hook 執行上下文"""
tool_name: str
tool_input: dict
tool_result: Optional[str] = None
agent_name: str = "main"
session_id: str = ""
class HookSystem:
"""Agent Loop 的 Hooks 管理器"""
def __init__(self):
self._hooks: dict[str, list[Callable]] = {
"UserPromptSubmit": [], # 使用者提交 Prompt 後
"PreToolUse": [], # Tool 執行前
"PostToolUse": [], # Tool 執行後
"Stop": [], # Agent 正常停止時
"SubagentStop": [], # Subagent 停止時
}
def register(self, event: str, handler: Callable):
"""註冊 Hook Handler"""
if event not in self._hooks:
raise ValueError(f"未知 Hook 事件: {event}")
self._hooks[event].append(handler)
def fire(self, event: str, ctx: HookContext) -> Optional[str]:
"""觸發 Hook,回傳 None 表示繼續,回傳字串表示覆寫結果"""
for handler in self._hooks.get(event, []):
result = handler(ctx)
if result is not None:
return result # 第一個非 None 結果覆寫後續
return None
# === 企業級 Hook 範例 ===
# 1. PreToolUse:自動安全掃描
def security_scan_hook(ctx: HookContext) -> Optional[str]:
"""在 bash 工具執行前掃描危險命令"""
if ctx.tool_name == "bash":
cmd = ctx.tool_input.get("command", "")
if "DROP TABLE" in cmd.upper():
return "HOOK_BLOCKED: 偵測到 DROP TABLE,已阻止執行"
return None # 允許繼續
# 2. PostToolUse:成本追蹤
def cost_tracking_hook(ctx: HookContext):
"""在每次 Tool 執行後記錄成本"""
import json, time
entry = {
"timestamp": time.time(),
"tool": ctx.tool_name,
"agent": ctx.agent_name,
"result_len": len(ctx.tool_result or ""),
}
with open("tool_usage.jsonl", "a") as f:
f.write(json.dumps(entry) + "\n")
# 3. Stop:任務完成通知
def notification_hook(ctx: HookContext):
"""Agent 完成任務時發送 Slack 通知"""
import subprocess
subprocess.run([
"curl", "-X", "POST", "-H", "Content-type: application/json",
"--data", f'{{"text": "Agent [{ctx.agent_name}] 已完成任務"}}',
os.getenv("SLACK_WEBHOOK_URL", "")
], capture_output=True)
# === 整合到 Agent Loop ===
hooks = HookSystem()
hooks.register("PreToolUse", security_scan_hook)
hooks.register("PostToolUse", cost_tracking_hook)
hooks.register("Stop", notification_hook)
def agent_loop_with_hooks(user_message: str):
messages = [{"role": "user", "content": user_message}]
while True:
response = client.messages.create(
model=MODEL, system=SYSTEM,
max_tokens=4096, messages=messages, tools=TOOLS,
)
messages.append({"role": "assistant", "content": response.content})
if response.stop_reason != "tool_use":
# 觸發 Stop Hook
hooks.fire("Stop", HookContext(tool_name="", tool_input={}))
break
results = []
for block in response.content:
if block.type == "tool_use":
ctx = HookContext(tool_name=block.name, tool_input=block.input)
# 觸發 PreToolUse Hook
blocked = hooks.fire("PreToolUse", ctx)
if blocked:
output = blocked # Hook 阻止執行
else:
output = TOOL_HANDLERS[block.name](**block.input)
# 觸發 PostToolUse Hook
ctx.tool_result = output
hooks.fire("PostToolUse", ctx)
results.append({
"type": "tool_result",
"tool_use_id": block.id,
"content": output,
})
messages.append({"role": "user", "content": results})五種核心 Hook 事件的用途:
| Hook 事件 | 觸發時機 | 典型用途 |
|---|---|---|
UserPromptSubmit | 使用者提交 Prompt 後 | Prompt Injection 掃描、意圖分類 |
PreToolUse | Tool 執行前 | 安全檢查、命令審查、權限驗證 |
PostToolUse | Tool 執行後 | 成本追蹤、輸出清洗、Audit Log |
Stop | Agent 正常停止 | 完成通知、結果報告、資源清理 |
SubagentStop | Subagent 停止 | 子任務完成彙報、結果聚合 |
核心設計原則:Hooks 不應該替代核心迴圈邏輯,而是在迴圈的關鍵節點提供觀察和干預能力。過度使用 Hooks 會讓系統難以追蹤;適度使用則提供強大的可擴展性而不破壞主流程的清晰度。
3.5 Planning / Reflection — TodoWrite(s05)
格言:
"An agent without a plan drifts"— 沒有計畫的 Agent 會迷失方向
TodoWrite 機制讓 Agent 在執行前先列出計畫步驟,顯著提升任務完成率:
class TodoManager:
"""管理 Agent 的任務計畫"""
def __init__(self):
self.todos = [] # [{"id": int, "task": str, "status": str}]
def write(self, todos: list) -> str:
"""建立 / 更新任務清單"""
self.todos = todos
return self._format()
def update(self, todo_id: int, status: str) -> str:
"""更新單一任務狀態"""
for todo in self.todos:
if todo["id"] == todo_id:
todo["status"] = status
return self._format()
return f"ERROR: 找不到 ID {todo_id}"
def _format(self) -> str:
"""格式化輸出,方便 LLM 閱讀"""
lines = ["=== 任務清單 ==="]
for t in self.todos:
icon = {"todo": "⬜", "in_progress": "🔄", "done": "✅"}
lines.append(f"{icon.get(t['status'], '?')} [{t['id']}] {t['task']}")
return "\n".join(lines)
# 關鍵機制:Nag Reminder
# 在每次 tool_result 後附加未完成任務提醒
def inject_nag_reminder(results: list, todo_manager: TodoManager) -> list:
"""注入任務提醒,防止 Agent 偏離計畫"""
pending = [t for t in todo_manager.todos if t["status"] != "done"]
if pending:
reminder = "\n⚠️ 待完成任務:\n"
for t in pending:
reminder += f" - [{t['id']}] {t['task']}\n"
reminder += "請繼續執行下一個待完成任務。"
results.append({
"type": "tool_result",
"tool_use_id": "nag",
"content": reminder,
})
return results效果對比:
| 指標 | 無 TodoWrite | 有 TodoWrite |
|---|---|---|
| 任務完成率 | ~45% | ~85% |
| 步驟遺漏率 | ~30% | ~5% |
| Context 使用效率 | 低(重複探索) | 高(有序執行) |
3.6 Subagent 機制(s06)
格言:
"Break big tasks down; each subtask gets a clean context"— 子任務使用獨立的 messages[]
def handle_subagent(task: str, allowed_tools: list = None) -> str:
"""啟動子 Agent,使用獨立的對話歷史"""
# 關鍵:子 Agent 有全新的 messages[]
sub_messages = [{"role": "user", "content": task}]
# 可選:限制子 Agent 可用的工具集
sub_tools = TOOLS if not allowed_tools else [
t for t in TOOLS if t["name"] in allowed_tools
]
# 子 Agent 獨立迴圈
while True:
response = client.messages.create(
model=MODEL,
system="你是專門處理子任務的助手。完成後簡潔回報結果。",
max_tokens=4096,
messages=sub_messages,
tools=sub_tools,
)
sub_messages.append({
"role": "assistant",
"content": response.content
})
if response.stop_reason != "tool_use":
# 提取子 Agent 的最終回覆
for block in response.content:
if hasattr(block, "text"):
return block.text
return "子任務完成(無文字回覆)"
results = []
for block in response.content:
if block.type == "tool_use":
output = TOOL_HANDLERS[block.name](**block.input)
results.append({
"type": "tool_result",
"tool_use_id": block.id,
"content": output,
})
sub_messages.append({"role": "user", "content": results})Subagent 隔離的好處:
主 Agent Context 子 Agent Context
┌─────────────────┐ ┌──────────────────┐
│ 對話歷史 (大) │ │ 全新 messages[] │
│ 多個任務記錄 │ spawn │ 僅包含子任務 │
│ 各種 Tool 結果 │ ──────────► │ 乾淨的 Context │
│ │ │ 專注完成一件事 │
│ ◄──── 結果摘要 ─┤ │ │
└─────────────────┘ └──────────────────┘
優點:
• 子任務的噪音不會汙染主 Context
• 失敗的子任務可獨立重試
• 不同子任務可用不同模型 / 權限3.7 Skills 按需載入(s07)
格言:
"Load knowledge when you need it, not upfront"— 知識按需載入,不要一次塞進 system prompt
# Skills 目錄結構
# skills/
# ├── spring-boot/
# │ └── SKILL.md # Spring Boot 開發指南
# ├── react/
# │ └── SKILL.md # React 開發指南
# ├── database-migration/
# │ └── SKILL.md # DB Migration 指南
# └── security-review/
# └── SKILL.md # 安全審查指南
def handle_load_skill(skill_name: str) -> str:
"""按需載入知識文件"""
skill_path = f"skills/{skill_name}/SKILL.md"
if not os.path.exists(skill_path):
available = os.listdir("skills")
return f"ERROR: 找不到 skill '{skill_name}'。可用: {available}"
with open(skill_path, "r", encoding="utf-8") as f:
content = f.read()
return f"=== Skill: {skill_name} ===\n{content}"企業 Skill 知識庫範例:
<!-- skills/spring-boot-upgrade/SKILL.md -->
# Spring Boot 升級指南
## 從 Spring Boot 2.x 升級到 3.x
### 前置檢查
1. 確認 Java 版本 >= 17
2. 檢查所有依賴的 Jakarta EE 相容性
3. 掃描 `javax.*` → `jakarta.*` 的 import 變更
### 升級步驟
1. 更新 parent pom 版本
2. 批次替換 javax → jakarta
3. 更新 Spring Security 配置
4. 執行完整測試套件
### 常見問題
- Hibernate 6.x 的 HQL 語法變更
- Spring Security 的 SecurityFilterChain 新寫法
- Actuator endpoint 路徑變更3.8 Context Compact — 壓縮策略(s08)
格言:
"Context will fill up; you need a way to make room"— Context 必定填滿,你需要壓縮策略
def compact_context(messages: list, model: str = MODEL) -> list:
"""三層壓縮策略"""
total_tokens = estimate_tokens(messages)
max_tokens = 200000 # Claude 的 context window
if total_tokens < max_tokens * 0.5:
return messages # 還不需要壓縮
# L1:摘要舊對話(保留最近 10 輪)
if total_tokens < max_tokens * 0.7:
old_messages = messages[:-20] # 舊的部分
recent_messages = messages[-20:] # 近期保留
summary = client.messages.create(
model=model,
system="請摘要以下對話的關鍵資訊,保留所有重要的決策和結論。",
max_tokens=2000,
messages=[{
"role": "user",
"content": f"請摘要:\n{format_messages(old_messages)}"
}]
)
return [{
"role": "user",
"content": f"[對話摘要]\n{summary.content[0].text}"
}] + recent_messages
# L2:移除工具執行細節,僅保留結論
# L3:僅保留當前任務描述 + 最近結果
# ...(遞進壓縮)3.9 Memory System 記憶系統(s09)
格言:
"Remember what matters, forget what doesn't"— 記住重要的,遺忘不重要的
Memory System 讓 Agent 在長任務中持續保持對關鍵資訊的記憶,由三個子系統組成:
class MemorySystem:
"""三層記憶架構:選取 → 擷取 → 整合"""
def __init__(self, memory_file: str = ".agent_memory.json"):
self.memory_file = memory_file
self.memories = self._load()
# --- 第一層:選取(Selection)---
# 決定「哪些資訊值得記住」
MEMORY_TRIGGERS = [
"使用者偏好",
"架構決策",
"已知錯誤和修復方式",
"環境配置",
"重要約束條件",
]
def should_remember(self, content: str) -> bool:
"""判斷此內容是否值得記住"""
return any(trigger in content for trigger in self.MEMORY_TRIGGERS)
# --- 第二層:擷取(Extraction)---
# 決定「以何種形式儲存」
def extract_and_store(self, content: str, category: str):
"""擷取關鍵資訊並結構化存儲"""
entry = {
"category": category, # 類別
"content": content[:500], # 內容摘要
"created_at": datetime.now().isoformat(),
"access_count": 0, # 存取次數(用於遺忘策略)
}
self.memories.setdefault(category, []).append(entry)
self._save()
# --- 第三層:整合(Consolidation)---
# 定期合併相似記憶,防止記憶庫膨脹
def consolidate(self):
"""整合相似記憶,保持記憶庫精簡"""
for category, entries in self.memories.items():
if len(entries) > 10:
# 使用 LLM 摘要並合併
summary_prompt = f"請整合以下 {category} 類別的記憶,保留關鍵資訊:\n"
for e in entries:
summary_prompt += f"- {e['content']}\n"
consolidated = client.messages.create(
model=MODEL,
system="你是記憶整合助手,請將多條記憶合併為簡潔的摘要。",
max_tokens=500,
messages=[{"role": "user", "content": summary_prompt}]
).content[0].text
self.memories[category] = [{
"category": category,
"content": consolidated,
"created_at": datetime.now().isoformat(),
"access_count": 0,
}]
self._save()
def recall(self, query: str) -> str:
"""從記憶庫中取回相關記憶"""
all_memories = []
for entries in self.memories.values():
all_memories.extend(entries)
if not all_memories:
return ""
# 簡單關鍵字匹配(生產環境可用向量搜尋)
relevant = [m for m in all_memories if any(w in m["content"] for w in query.split())]
if not relevant:
return ""
result = "=== 相關記憶 ===\n"
for m in relevant[:5]:
result += f"[{m['category']}] {m['content']}\n"
return result記憶生命週期:
新資訊輸入
│
▼
Selection(值得記住嗎?)
│ 是
▼
Extraction(以何種結構儲存?)
│
▼
Storage(存入 .agent_memory.json)
│
▼ ← 定期執行
Consolidation(記憶超過 10 條時整合)
│
▼
Recall(按需從記憶庫取回注入 Context)3.10 System Prompt 動態組裝(s10)
格言:
"Prompts are assembled at runtime, not hardcoded"— System Prompt 在執行時動態組裝,而非硬編碼
傳統做法是把 System Prompt 寫死,但高效能的 Agent 會在執行時根據狀態動態組裝:
class SystemPromptBuilder:
"""動態 System Prompt 組裝器"""
def __init__(self):
self.sections = {
"identity": "", # 角色定義(固定)
"tools": "", # 可用工具說明(半固定)
"context": "", # 當前任務上下文(動態)
"skills": "", # 按需載入的技能(動態)
"memory": "", # 長期記憶摘要(動態)
"constraints": "", # 當前約束條件(動態)
}
def set_identity(self, role: str, expertise: list):
"""設定角色身份(通常固定)"""
self.sections["identity"] = f"""你是 {role}。
專業領域:{', '.join(expertise)}。
你的目標是高效、安全地完成使用者交付的任務。"""
def set_active_skills(self, skill_names: list):
"""注入當前激活的技能知識"""
skill_content = []
for name in skill_names:
path = f"skills/{name}/SKILL.md"
if os.path.exists(path):
with open(path) as f:
skill_content.append(f"## Skill: {name}\n{f.read()[:2000]}")
self.sections["skills"] = "\n\n".join(skill_content)
def inject_memory(self, memory: MemorySystem, current_task: str):
"""注入相關記憶"""
recalled = memory.recall(current_task)
self.sections["memory"] = recalled
def set_task_context(self, task_desc: str, progress: str):
"""設定當前任務上下文"""
self.sections["context"] = f"""## 當前任務
{task_desc}
## 進度
{progress}"""
def build(self) -> str:
"""組裝最終 System Prompt"""
parts = []
# 固定部分(永遠包含)
if self.sections["identity"]:
parts.append(self.sections["identity"])
if self.sections["tools"]:
parts.append(self.sections["tools"])
# 動態部分(非空時包含)
for section in ["context", "skills", "memory", "constraints"]:
if self.sections[section]:
parts.append(self.sections[section])
return "\n\n---\n\n".join(parts)
# 使用範例
builder = SystemPromptBuilder()
builder.set_identity("資深 Java 工程師", ["Spring Boot", "Microservices", "TDD"])
builder.set_active_skills(["spring-boot-3", "security-checklist"])
builder.inject_memory(memory_system, current_task="實作使用者登入 API")
builder.set_task_context("實作 JWT 認證模組", "已完成 Entity 層,正在實作 Service 層")
DYNAMIC_SYSTEM = builder.build()靜態 vs 動態 System Prompt 對比:
| 比較項目 | 靜態 System Prompt | 動態組裝 |
|---|---|---|
| Token 消耗 | 固定(包含所有知識) | 按需(只含當前需要的) |
| 靈活性 | 低 | 高 |
| 維護成本 | 低 | 中 |
| 適用場景 | 簡單 Agent | 複雜、長任務 Agent |
| 知識覆蓋 | 有限(受 Token 限制) | 無限(按需載入) |
設計原則:System Prompt 應該像衣服,不是像皮膚。根據任務穿上適合的裝備,而不是把所有知識都塞進去。
3.12 Task 持久化與相依管理(s12)
格言:
"Break big goals into small tasks, order them, persist to disk"— 大目標拆成小任務,排序,持久化
import json
from datetime import datetime
class TaskManager:
"""檔案式任務管理器,支援相依圖"""
TASK_FILE = ".tasks.jsonl"
def create(self, title: str, deps: list = None) -> dict:
task = {
"id": f"task-{self._next_id():03d}",
"title": title,
"status": "todo",
"dependencies": deps or [],
"created_at": datetime.now().isoformat(),
}
self._append(task)
return task
def update_status(self, task_id: str, status: str) -> dict:
tasks = self._load_all()
for t in tasks:
if t["id"] == task_id:
# 檢查相依任務是否都已完成
if status == "in_progress":
for dep_id in t.get("dependencies", []):
dep = next((x for x in tasks if x["id"] == dep_id), None)
if dep and dep["status"] != "done":
return {"error": f"相依任務 {dep_id} 尚未完成"}
t["status"] = status
t["updated_at"] = datetime.now().isoformat()
self._save_all(tasks)
return t
return {"error": f"找不到任務 {task_id}"}
def get_ready_tasks(self) -> list:
"""取得所有相依已滿足、可執行的任務"""
tasks = self._load_all()
done_ids = {t["id"] for t in tasks if t["status"] == "done"}
return [
t for t in tasks
if t["status"] == "todo"
and all(d in done_ids for d in t.get("dependencies", []))
]3.13 Background Tasks(s13)
格言:
"Run slow operations in the background; the agent keeps thinking"— 慢操作丟到背景,Agent 繼續思考
import threading
import queue
class BackgroundRunner:
"""背景任務執行器"""
def __init__(self):
self.notify_queue = queue.Queue()
def run_in_background(self, task_id: str, command: str):
"""在 daemon thread 中執行命令"""
def _run():
import subprocess
result = subprocess.run(
command, shell=True,
capture_output=True, text=True
)
self.notify_queue.put({
"task_id": task_id,
"command": command,
"stdout": result.stdout,
"stderr": result.stderr,
"returncode": result.returncode,
})
thread = threading.Thread(target=_run, daemon=True)
thread.start()
return f"背景任務 {task_id} 已啟動: {command}"
def check_notifications(self) -> list:
"""檢查已完成的背景任務"""
notifications = []
while not self.notify_queue.empty():
notifications.append(self.notify_queue.get_nowait())
return notifications典型應用場景:
Agent 主迴圈
│
├── 啟動背景任務:mvn test(耗時 2 分鐘)
│ └── daemon thread 執行中...
│
├── 同時繼續:撰寫下一個類別
│
├── 檢查通知:測試完成!3 個失敗
│
└── 修復失敗的測試3.14 Cron Scheduler 排程機制(s14)
格言:
"Fire on schedule, no human kick needed"— 按排程觸發,無需人工介入
Cron Scheduler 讓 Agent 能夠自動定時執行任務,實現真正的「常駐式」運作:
import json
import time
import threading
from datetime import datetime, timedelta
class CronJob:
"""持久化排程任務定義"""
def __init__(self, job_id: str, cron_expr: str, task: str, enabled: bool = True):
self.id = job_id
self.cron_expr = cron_expr # "0 2 * * *" = 每天凌晨 2 點
self.task = task # 要執行的任務描述
self.enabled = enabled
self.last_run = None
self.next_run = None
class CronScheduler:
"""耐久性排程器:基於檔案的任務佇列 + 輪詢執行"""
CRON_FILE = ".cron_jobs.json"
POLL_INTERVAL = 30 # 每 30 秒檢查一次
def __init__(self):
self.jobs = self._load_jobs()
self._running = False
def add_job(self, cron_expr: str, task: str) -> str:
"""新增排程任務"""
job_id = f"cron-{int(time.time())}"
job = {
"id": job_id,
"cron_expr": cron_expr,
"task": task,
"enabled": True,
"created_at": datetime.now().isoformat(),
"last_run": None,
"next_run": self._calc_next_run(cron_expr),
}
self.jobs[job_id] = job
self._save_jobs()
return job_id
def remove_job(self, job_id: str):
"""移除排程任務"""
if job_id in self.jobs:
del self.jobs[job_id]
self._save_jobs()
def start(self, agent_executor):
"""啟動排程輪詢 daemon"""
self._running = True
def poll_loop():
while self._running:
now = datetime.now().isoformat()
for job_id, job in list(self.jobs.items()):
if not job.get("enabled"):
continue
if job.get("next_run") and job["next_run"] <= now:
print(f"[Cron] 觸發排程任務: {job['task']}")
# 在背景執行 Agent 任務
threading.Thread(
target=agent_executor,
args=(job["task"],),
daemon=True
).start()
# 更新執行記錄
job["last_run"] = now
job["next_run"] = self._calc_next_run(job["cron_expr"])
self._save_jobs()
time.sleep(self.POLL_INTERVAL)
thread = threading.Thread(target=poll_loop, daemon=True)
thread.start()
return thread
def _calc_next_run(self, cron_expr: str) -> str:
"""解析 cron 表達式,計算下次執行時間(簡化版)"""
# 生產環境建議使用 croniter 套件
# 這裡用簡單的 daily 示意
next_run = datetime.now() + timedelta(hours=24)
return next_run.isoformat()
def _load_jobs(self) -> dict:
if os.path.exists(self.CRON_FILE):
with open(self.CRON_FILE) as f:
return json.load(f)
return {}
def _save_jobs(self):
with open(self.CRON_FILE, "w") as f:
json.dump(self.jobs, f, indent=2, ensure_ascii=False)
# 企業應用範例
scheduler = CronScheduler()
# 每天凌晨 2 點掃描安全漏洞
scheduler.add_job("0 2 * * *", "對整個程式碼庫執行 SAST 掃描,產出安全報告")
# 每週一早上 9 點產生技術債報告
scheduler.add_job("0 9 * * 1", "分析所有 TODO/FIXME 評論,產出技術債報告")
# 每小時檢查 CI 狀態
scheduler.add_job("0 * * * *", "檢查最近 1 小時內失敗的 CI 建置,並嘗試自動修復")
# 啟動排程器
scheduler.start(agent_executor=lambda task: agent_loop(task))常見企業排程任務設計:
| 排程 | Cron 表達式 | 任務 |
|---|---|---|
| 每日安全掃描 | 0 2 * * * | SAST + 依賴弱掃 |
| 週報產生 | 0 9 * * 1 | 程式碼品質 + 技術債彙整 |
| 每小時 CI 監控 | 0 * * * * | 失敗建置自動修復 |
| 每月合規檢查 | 0 0 1 * * | 授權合規 + 證書到期檢查 |
| 每日文件同步 | 0 18 * * * | API 文件自動更新 |
Cron vs Background Task:Background Task(s13)是「單次非同步執行」,Cron Scheduler(s14)是「週期性定時觸發」。前者用於長時間的單一任務;後者用於建立常態性的 Agent 運作節奏。兩者可以組合:Cron 觸發 → Background Task 執行。
3.15 Agent Teams 與 Mailbox(s15-s16)
格言:
"When the task is too big for one, delegate to teammates"— 任務太大就委派給隊友
class AgentTeam:
"""Agent 團隊管理器"""
def __init__(self, team_dir: str = ".team"):
self.team_dir = team_dir
os.makedirs(team_dir, exist_ok=True)
def register_teammate(self, name: str, role: str, tools: list):
"""註冊隊友"""
config = {
"name": name,
"role": role,
"allowed_tools": tools,
"status": "idle",
}
with open(f"{self.team_dir}/{name}.json", "w") as f:
json.dump(config, f, indent=2)
def send_message(self, to: str, message: dict):
"""透過 JSONL Mailbox 發送訊息"""
mailbox = f"{self.team_dir}/{to}.mailbox.jsonl"
message["timestamp"] = datetime.now().isoformat()
with open(mailbox, "a") as f:
f.write(json.dumps(message, ensure_ascii=False) + "\n")
def read_messages(self, agent_name: str) -> list:
"""讀取信箱中的訊息"""
mailbox = f"{self.team_dir}/{agent_name}.mailbox.jsonl"
if not os.path.exists(mailbox):
return []
with open(mailbox, "r") as f:
return [json.loads(line) for line in f if line.strip()]Team Protocol(s10)— 請求-回應模式:
Lead Agent Backend Agent
│ │
├─── REQUEST ──────────────► │
│ {type: "request", │
│ task: "實作 login API"} │
│ │
│ ├── 讀取程式碼
│ ├── 實作功能
│ ├── 執行測試
│ │
│ ◄──────── RESPONSE ────────┤
│ {type: "response", │
│ status: "done", │
│ summary: "完成,5 測試通過"}│
│ │3.16 Autonomous Agent 自治(s17)
格言:
"Teammates scan the board and claim tasks themselves"— 隊友自行掃描任務板並認領
def autonomous_cycle(agent_name: str, team: AgentTeam, task_mgr: TaskManager):
"""自治 Agent 的 idle cycle"""
while True:
# 1. 檢查信箱是否有直接指派的任務
messages = team.read_messages(agent_name)
if messages:
for msg in messages:
if msg["type"] == "request":
execute_task(msg)
team.send_message(msg["from"], {
"type": "response",
"task_id": msg["task_id"],
"status": "done"
})
continue
# 2. 主動掃描任務板,認領可執行的任務
ready_tasks = task_mgr.get_ready_tasks()
my_tasks = [t for t in ready_tasks if matches_my_skills(t, agent_name)]
if my_tasks:
task = my_tasks[0]
task_mgr.update_status(task["id"], "in_progress")
execute_task(task)
task_mgr.update_status(task["id"], "done")
continue
# 3. 沒有任務,idle 等待
time.sleep(5)3.17 Worktree + Task Isolation(s18)
格言:
"Each works in its own directory, no interference"— 各自在獨立目錄工作,互不干擾
def create_isolated_workspace(task_id: str) -> str:
"""為任務建立隔離的 Git Worktree"""
import subprocess
branch_name = f"agent/{task_id}"
worktree_path = f"worktrees/{task_id}"
# 建立 worktree
subprocess.run(
f"git worktree add {worktree_path} -b {branch_name}",
shell=True, check=True
)
return worktree_path
def cleanup_workspace(task_id: str):
"""清理完成的 Worktree"""
import subprocess
worktree_path = f"worktrees/{task_id}"
subprocess.run(f"git worktree remove {worktree_path}", shell=True)
subprocess.run(f"git branch -d agent/{task_id}", shell=True)完整的 Task + Worktree 綁定流程:
1. 建立任務 → task_mgr.create("實作登入 API")
2. 建立隔離空間 → create_isolated_workspace("task-001")
3. Agent 在 worktrees/task-001/ 中工作
4. 執行測試(在隔離環境中)
5. 完成後:
a. git merge 回主分支
b. 清理 worktree
c. 更新任務狀態為 done3.11 Error Recovery 錯誤恢復(s11)
格言:
"Errors aren't the end, they're the start of a retry"— 錯誤不是終點,而是重試的起點
Agent 的錯誤修復能力是企業級應用的關鍵:
def handle_bash_with_recovery(command: str, max_retries: int = 3) -> str:
"""帶錯誤恢復的 bash 執行"""
import subprocess
result = subprocess.run(
command, shell=True,
capture_output=True, text=True, timeout=60
)
output = result.stdout + result.stderr
if result.returncode != 0:
# 提供結構化的錯誤資訊,讓模型能分析並修復
return (
f"COMMAND_FAILED (exit code: {result.returncode})\n"
f"STDOUT:\n{result.stdout[:3000]}\n"
f"STDERR:\n{result.stderr[:3000]}\n"
f"HINT: 請分析錯誤原因並嘗試修復。"
)
return output[:10000]Error Recovery 流程:
Agent 執行 mvn test
│
▼
測試失敗:3 errors
│
▼
Agent 分析錯誤訊息
│
▼
read_file 讀取失敗的測試
│
▼
read_file 讀取對應的原始碼
│
▼
edit_file 修復程式碼
│
▼
bash: mvn test(重新測試)
│
▼
全部通過 ✅實務注意:
- 限制重試次數(建議 3 次),避免無限迴圈
- 錯誤訊息截斷至合理長度(3000-5000 字元),避免浪費 Context
- 結構化錯誤輸出(分離 STDOUT / STDERR),提升模型分析效率
3.18 MCP Plugin 外部工具整合(s19)
格言:
"Not enough capability? Plug in more via MCP"— 能力不足?透過 MCP 插入更多
MCP(Model Context Protocol)是 Anthropic 制定的開放標準,讓 Agent 能以統一介面連接任意外部系統,無需為每個系統手工撰寫 Tool handler:
import subprocess
import json
class MCPClient:
"""MCP 客戶端:透過標準協議連接外部 MCP Server"""
def __init__(self, server_command: str):
"""啟動 MCP Server 子程序"""
self.process = subprocess.Popen(
server_command.split(),
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
self._msg_id = 0
self._initialize()
def _send(self, method: str, params: dict = None) -> dict:
"""透過 JSON-RPC 2.0 與 MCP Server 通訊"""
self._msg_id += 1
request = {
"jsonrpc": "2.0",
"id": self._msg_id,
"method": method,
"params": params or {},
}
payload = json.dumps(request) + "\n"
self.process.stdin.write(payload.encode())
self.process.stdin.flush()
response = self.process.stdout.readline()
return json.loads(response)
def _initialize(self):
"""初始化 MCP 連接"""
self._send("initialize", {
"protocolVersion": "2024-11-05",
"clientInfo": {"name": "learn-claude-code", "version": "1.0"},
})
def list_tools(self) -> list:
"""列出 MCP Server 提供的所有工具"""
result = self._send("tools/list")
return result.get("result", {}).get("tools", [])
def call_tool(self, tool_name: str, arguments: dict) -> str:
"""呼叫 MCP 工具"""
result = self._send("tools/call", {
"name": tool_name,
"arguments": arguments,
})
content = result.get("result", {}).get("content", [])
return "\n".join(c.get("text", "") for c in content)
class MCPToolPool:
"""統一工具池:內建 Tool + MCP 動態 Tool 的無縫整合"""
NAMING_PREFIX = "mcp__{server}__{tool}"
def __init__(self, builtin_tools: list, builtin_handlers: dict):
self.tools = list(builtin_tools) # 內建工具定義
self.handlers = dict(builtin_handlers) # 內建工具處理器
self.mcp_clients = {}
def connect_mcp_server(self, server_name: str, command: str):
"""連接 MCP Server 並整合其工具"""
client = MCPClient(command)
self.mcp_clients[server_name] = client
# 取得 MCP Server 的工具清單
mcp_tools = client.list_tools()
for tool in mcp_tools:
# 使用命名前綴避免衝突:mcp__github__create_issue
safe_name = self.NAMING_PREFIX.format(
server=server_name,
tool=self._normalize_name(tool["name"])
)
# 加入工具定義(與內建工具格式一致)
self.tools.append({
"name": safe_name,
"description": tool.get("description", ""),
"input_schema": tool.get("inputSchema", {}),
})
# 動態建立 Handler
def make_handler(client, original_name):
return lambda **kwargs: client.call_tool(original_name, kwargs)
self.handlers[safe_name] = make_handler(client, tool["name"])
def _normalize_name(self, name: str) -> str:
"""標準化工具名稱,移除特殊字元"""
import re
return re.sub(r'[^a-zA-Z0-9_]', '_', name).lower()
# 企業 MCP Server 整合範例
tool_pool = MCPToolPool(TOOLS, TOOL_HANDLERS)
# 連接 GitHub MCP Server(管理 Issue / PR)
tool_pool.connect_mcp_server("github", "npx @modelcontextprotocol/server-github")
# 連接資料庫 MCP Server
tool_pool.connect_mcp_server("postgres", "npx @modelcontextprotocol/server-postgres")
# 連接 Jira MCP Server(企業自建)
tool_pool.connect_mcp_server("jira", "python mcp_servers/jira_server.py")
# Agent Loop 無需任何改動,自動支援所有 MCP 工具
print(f"可用工具數量:{len(tool_pool.tools)} 個")
# 輸出:可用工具數量:42 個(5 內建 + 37 MCP 動態)MCP 生態系統現況(2026):
| MCP Server | 功能 | 適用場景 |
|---|---|---|
| GitHub MCP | Issue、PR、Code Review 管理 | DevOps 自動化 |
| PostgreSQL MCP | 資料庫查詢與 Schema 管理 | 資料分析任務 |
| Filesystem MCP | 增強型檔案操作 | 複雜檔案管理 |
| Slack MCP | 頻道訊息、通知 | 團隊協作整合 |
| Jira MCP | Ticket 管理、Sprint 追蹤 | 專案管理整合 |
| Puppeteer MCP | 瀏覽器自動化 | E2E 測試、爬蟲 |
| 企業自建 MCP | 對接內部系統 | ERP、OA、資安平台 |
自建 MCP vs 手工 Tool 的選擇時機:
選 MCP Server:
✅ 需要與多個 Agent / 多個團隊共享工具
✅ 工具需要獨立部署、版本管理
✅ 整合的系統已有 MCP Server 生態
✅ 需要支援多種傳輸協議(stdio / HTTP / WebSocket)
選手工 Tool Handler:
✅ 單一 Agent 使用的簡單工具
✅ 快速原型開發
✅ 需要與 Agent 緊密耦合的業務邏輯
✅ 敏感資訊不適合透過網路傳輸3.19 完整 Agent 整合(s20)
格言:
"Many mechanisms, one loop"— 眾多機制,一個迴圈
s20 是 learn-claude-code 的最終整合課程,將前 19 個機制整合進單一可運作的 Harness。整合後的 Agent 具備 27+ 內建工具 + 動態 MCP 工具:
s20 完整 Agent 工具清單(27+ 內建):
核心工具(s01-s02):
bash, read_file, write_file, edit_file, glob, grep
計畫與隔離(s05-s06):
todo_write, todo_update, subagent_spawn
知識管理(s07, s10):
load_skill, list_skills, update_system_prompt
記憶(s09):
memory_store, memory_recall, memory_consolidate
任務系統(s12):
task_create, task_update, task_list, task_get_ready
背景執行(s13):
background_run, background_check
排程(s14):
cron_add, cron_remove, cron_list
團隊協作(s15-s16):
team_send, team_read, team_register
工作區(s18):
worktree_create, worktree_remove
MCP 整合(s19):
mcp_connect, mcp_list_tools(動態)
+ MCP 動態工具(無限擴充)整合架構圖:
使用者 Prompt
│
▼ UserPromptSubmit Hook(Prompt 掃描)
┌──────────────────────────────────────────────────┐
│ Agent Loop (s01) │
│ │
│ ┌─────────────────────────────────────────────┐ │
│ │ System Prompt(動態組裝 s10) │ │
│ │ = Identity + Skills + Memory + Context │ │
│ └─────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────┐ │
│ │ Tool Pool(s02 + s19) │ │
│ │ = 27 內建工具 + N 個 MCP 動態工具 │ │
│ └─────────────────────────────────────────────┘ │
│ │ │
│ ▼ PreToolUse Hook │
│ Permission Gateway (s03) │
│ Deny List → Rules → Approval │
│ │ │
│ ▼ │
│ Tool Execution │
│ │ │
│ ▼ PostToolUse Hook │
│ Audit Log + Cost Track │
│ │
│ Memory System (s09) ← 自動記憶關鍵資訊 │
│ Context Compact (s08) ← Token > 70% 時觸發 │
│ Task Manager (s12) ← 追蹤多步驟任務 │
│ Cron Scheduler (s14) ← 定時觸發 │
│ Agent Teams (s15-16) ← 複雜任務委派 │
│ Worktree (s18) ← 每任務獨立目錄 │
└──────────────────────────────────────────────────┘
│
▼ Stop Hook(完成通知、資源清理)
Response to Users20 的核心啟示:20 個機制,每一個都圍繞著同一個核心迴圈。迴圈從未改變。這正是 Harness Engineering 的精髓 — 透過分層的機制為模型建構越來越豐富的運作環境,而不是試圖讓迴圈本身變得複雜。
4. Harness Engineering(核心競爭力)
4.1 什麼是 Harness
Harness(外殼/支架)是 Agent 運作所需的一切環境設施,但不包含智能本身:
Agent Product = Model(智能) + Harness(環境)
Harness = Tools + Knowledge + Observation + Action Interfaces + Permissions
Tools: 檔案 I/O、Shell、網路、資料庫、瀏覽器
Knowledge: 產品文件、領域參考、API 規格、風格指南
Observation: git diff、錯誤日誌、瀏覽器狀態、感測器數據
Action: CLI 命令、API 呼叫、UI 互動
Permissions: 沙箱、審批流程、信任邊界核心理念:
┌──────────────────────────────────────────┐
│ │
│ 模型 = 駕駛員(決策者) │
│ Harness = 車輛(執行環境) │
│ │
│ 模型決定去哪裡、怎麼開 │
│ Harness 提供方向盤、油門、煞車 │
│ │
│ 你的工作:打造一輛好車 │
│ 模型的工作:開好這輛車 │
│ │
└──────────────────────────────────────────┘不同領域的 Harness 差異:
| 領域 | Tools | Knowledge | Observation |
|---|---|---|---|
| 程式開發 | 讀寫檔案、Shell、Git | API 文件、架構指南 | 編譯錯誤、測試結果 |
| 金融交易 | 下單 API、風控查詢 | 法規、市場數據 | 持倉狀態、風險指標 |
| DevOps | Docker、K8s、Terraform | Runbook、SLA | 監控指標、Alert |
| 文件處理 | OCR、PDF 解析、DB | 格式規範、模板 | 驗證結果 |
4.2 Harness 工程師的五大職責
理解了「Agent = Model + Harness」之後,Harness 工程師的工作邊界也隨之清晰。你不是在教模型思考,你是在為模型建構一個可操作的世界。 以下是 Harness 工程師的五大核心職責:
| # | 職責 | 說明 | learn-claude-code 對應 |
|---|---|---|---|
| 1 | 實作工具(Implement Tools) | 定義 Agent 可以呼叫的原子操作。每個 Tool 是一個函式:接收結構化輸入、執行副作用、回傳文字結果。Tool 的設計品質直接決定 Agent 的能力上限。 | s02: Tool Dispatch Map |
| 2 | 策展知識(Curate Knowledge) | 蒐集、整理、組織 Agent 需要的領域知識。包括 API 文件、架構指南、程式碼風格規範、最佳實踐文件。知識不是一次性的,而是持續維護的策展過程。 | s05: Skills 按需載入 |
| 3 | 管理上下文(Manage Context) | 設計 Context Window 的使用策略。什麼時候壓縮?保留什麼?丟棄什麼?如何確保 Agent 在長任務中不會「忘記」關鍵資訊? | s06: Context Compact |
| 4 | 控管權限(Control Permissions) | 定義 Agent 的行為邊界:哪些路徑可讀寫、哪些命令需要審批、哪些操作完全禁止。安全不是附加功能,是 Harness 設計的一級公民。 | s11: Permission + Approval |
| 5 | 收集任務過程資料(Collect Task-Process Data) | 記錄 Agent 在 Harness 中執行每一項任務的完整軌跡:感知了什麼、推理了什麼、採取了什麼行動、結果如何。這些資料是微調下一代 Agent 模型的原材料,也是評估 Harness 品質的量化依據。 | s07-s08: Task 持久化 |
Harness 工程師的工作流:
┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐
│ 1.Tools │ → │2.Knowledge│ → │3.Context│ → │4.Perms │ → │ 5.Data │
│ │ │ │ │ │ │ │ │ │
│ 建構 │ │ 策展 │ │ 管理 │ │ 治理 │ │ 收集 │
│ 行動能力│ │ 領域知識│ │ 記憶窗口│ │ 安全邊界│ │ 訓練素材│
└─────────┘ └─────────┘ └─────────┘ └─────────┘ └─────────┘
↑ │
└──────── 持續反饋迴圈 ─────────┘關鍵認知:這五項職責中,沒有一項是「讓模型變聰明」。模型的智能由 Anthropic / OpenAI / DeepSeek 等基礎模型供應商負責。你的工作是為這份智能提供最好的運作環境。
4.3 如何設計 Tool Interface
設計原則
# ✅ 好的 Tool 設計:原子、可組合、描述清晰
{
"name": "db_query",
"description": "對指定資料庫執行唯讀 SQL 查詢。僅支援 SELECT,不可執行 DDL/DML。結果以 JSON 陣列回傳,最多 100 筆。",
"input_schema": {
"type": "object",
"properties": {
"database": {
"type": "string",
"enum": ["app_db", "audit_db", "analytics_db"],
"description": "目標資料庫名稱"
},
"sql": {
"type": "string",
"description": "SELECT SQL 語句"
}
},
"required": ["database", "sql"]
}
}
# ❌ 不好的 Tool 設計:太泛、無限制、描述模糊
{
"name": "database",
"description": "操作資料庫",
"input_schema": {
"type": "object",
"properties": {
"query": {"type": "string"}
}
}
}Tool 設計檢查清單
- 名稱明確:動詞 + 名詞(
read_file、run_test、query_db) - 描述完整:說明用途、限制、回傳格式
- Schema 嚴格:使用 enum 限制選項、required 標示必填
- 輸出可控:限制回傳長度、結構化輸出
- 錯誤清晰:回傳有意義的錯誤訊息
- 權限邊界:明確標示可做 / 不可做的操作
4.4 如何限制 Agent 行為(安全性)
# 多層安全機制設計
class ToolPermissionManager:
"""Tool 權限管理器"""
# 1. 白名單機制:限制可用工具
ALLOWED_TOOLS = {
"junior_agent": ["read_file", "grep", "glob"],
"senior_agent": ["read_file", "write_file", "edit_file", "bash", "grep", "glob"],
"admin_agent": ["read_file", "write_file", "edit_file", "bash", "grep", "glob", "db_query"],
}
# 2. 路徑限制:限制可存取的檔案範圍
ALLOWED_PATHS = [
"src/",
"tests/",
"docs/",
]
BLOCKED_PATHS = [
".env",
"secrets/",
"credentials/",
"*.pem",
"*.key",
]
# 3. 命令黑名單:禁止危險命令
BLOCKED_COMMANDS = [
"rm -rf /",
"DROP DATABASE",
"DROP TABLE",
"DELETE FROM", # 不允許不帶 WHERE 的 DELETE
"curl.*|bash", # 禁止遠端程式碼執行
"chmod 777",
]
# 4. 審批流程:需要人工確認的操作
REQUIRE_APPROVAL = [
"git push",
"docker push",
"kubectl apply",
"terraform apply",
"npm publish",
]
def check_permission(self, agent_role: str, tool_name: str,
tool_input: dict) -> tuple:
"""檢查權限,回傳 (allowed: bool, reason: str)"""
# 檢查工具白名單
if tool_name not in self.ALLOWED_TOOLS.get(agent_role, []):
return False, f"角色 {agent_role} 無權使用工具 {tool_name}"
# 檢查路徑限制
if "path" in tool_input:
path = tool_input["path"]
if any(path.startswith(bp) or path.endswith(bp.lstrip("*"))
for bp in self.BLOCKED_PATHS):
return False, f"禁止存取路徑: {path}"
# 檢查命令黑名單
if tool_name == "bash":
import re
cmd = tool_input.get("command", "")
for pattern in self.BLOCKED_COMMANDS:
if re.search(pattern, cmd, re.IGNORECASE):
return False, f"禁止執行命令: {cmd}"
# 檢查是否需要審批
if tool_name == "bash":
cmd = tool_input.get("command", "")
for pattern in self.REQUIRE_APPROVAL:
if pattern in cmd:
return self._request_approval(cmd)
return True, "OK"
def _request_approval(self, command: str) -> tuple:
"""請求人工審批"""
print(f"\n⚠️ 需要審批的操作: {command}")
approval = input("是否允許?(y/n): ").strip().lower()
if approval == "y":
return True, "已核准"
return False, "使用者拒絕"4.5 如何提升成功率(Prompt / Retry / Guardrails)
System Prompt 設計
SYSTEM_PROMPT = """你是一位資深軟體工程師,負責在企業級 Java 專案中進行開發。
## 工作規則
1. 在修改程式碼前,先使用 read_file 了解現有程式碼
2. 使用 todo_write 規劃任務步驟後再開始執行
3. 每次修改後執行測試(mvn test 或 npm test)
4. 遵循專案的 coding style(見 .editorconfig)
## 安全規則
- 不得修改 .env、credentials 等機密檔案
- 不得執行 rm -rf、DROP TABLE 等危險命令
- SQL 查詢必須使用參數化查詢,禁止字串拼接
## 程式碼品質
- Java 使用 Google Java Style
- 所有 public 方法需有 JavaDoc
- 單元測試覆蓋率需達 80%
## 錯誤處理
- 遇到編譯錯誤:讀取錯誤訊息 → 分析原因 → 修復 → 重新編譯
- 遇到測試失敗:讀取測試程式碼 → 讀取實作程式碼 → 修復 → 重跑測試
- 最多重試 3 次,若仍失敗則報告問題
"""Retry 機制
def execute_with_retry(tool_name: str, tool_input: dict,
max_retries: int = 3) -> str:
"""帶 retry 的工具執行"""
for attempt in range(max_retries):
result = TOOL_HANDLERS[tool_name](**tool_input)
if "ERROR" not in result and "FAILED" not in result:
return result
if attempt < max_retries - 1:
# 附加重試資訊讓模型知道這是第幾次嘗試
result += f"\n\n[重試 {attempt + 1}/{max_retries}] 請分析錯誤原因並調整策略。"
return result + "\n\n[已達最大重試次數] 請回報此問題給使用者。"Guardrails(護欄機制)
# 輸出長度限制
def truncate_output(output: str, max_chars: int = 10000) -> str:
if len(output) <= max_chars:
return output
half = max_chars // 2
return (
output[:half] +
f"\n\n... [截斷 {len(output) - max_chars} 字元] ...\n\n" +
output[-half:]
)
# Token 預算控制
def check_token_budget(messages: list, max_budget: int = 150000) -> bool:
current = estimate_tokens(messages)
if current > max_budget:
# 觸發 context compact
return False
return True
# 迴圈次數限制
MAX_LOOP_ITERATIONS = 50
def agent_loop_with_guardrails(user_message: str):
messages = [{"role": "user", "content": user_message}]
iteration = 0
while iteration < MAX_LOOP_ITERATIONS:
iteration += 1
if not check_token_budget(messages):
messages = compact_context(messages)
response = client.messages.create(
model=MODEL, system=SYSTEM_PROMPT,
max_tokens=4096, messages=messages, tools=TOOLS,
)
# ... 正常迴圈邏輯 ...
print(f"⚠️ 已達最大迴圈次數 ({MAX_LOOP_ITERATIONS}),強制停止")實務案例:某企業在導入 Agent 初期,因未設定迴圈次數限制,Agent 陷入「修改 → 測試失敗 → 修改」的無限迴圈,消耗了大量 Token。設定 50 次上限 + 3 次 retry 後,成本降低約 40%。
4.6 CLAUDE.md 系統設計
CLAUDE.md 是 Claude Code 實現「跨 Session 持久化指令」的核心機制,讓 Harness 工程師可以在專案層面永久定義 Agent 的行為規範,無需每次重新 Prompt。
載入層次與優先順序
載入優先順序(高 → 低):
1. 全域設定(~/.claude/CLAUDE.md)
└── 適用所有專案的個人偏好、通用規則
2. 專案根目錄(./CLAUDE.md)
└── 整個專案的 Agent 行為規範
3. 子目錄(./src/CLAUDE.md、./tests/CLAUDE.md)
└── 特定模組的局部覆蓋規則
規則合併原則:
子目錄 > 專案根目錄 > 全域設定
衝突時,較具體的規則優先企業級 CLAUDE.md 模板
<!-- 專案根目錄 CLAUDE.md -->
# 專案 Agent 行為規範
## 專案背景
這是一個金融業核心交易系統,使用 Java 21 + Spring Boot 3.2。
所有程式碼變更必須符合 PCI-DSS 合規要求。
## 技術規範
- 架構:Clean Architecture(Controller → Service → Repository)
- 框架:Spring Boot 3.2、Spring Security 6、JPA/Hibernate 6
- 測試:JUnit 5、Mockito、AssertJ(覆蓋率 >= 80%)
- 程式碼風格:Google Java Style Guide
## 強制性行為規則
1. **閱讀優先**:修改任何檔案前必須先用 read_file 了解現有實作
2. **先計畫後執行**:複雜任務必須先用 todo_write 列出步驟
3. **修改後必測**:每次程式碼修改後執行 `mvn test -q`
4. **禁止刪除測試**:不可刪除現有測試,只能新增
## 安全紅線(絕對禁止)
- 不可修改 .env、application-prod.yml 等機密配置
- SQL 查詢必須使用參數化,禁止字串拼接
- 不可在程式碼中硬編碼 API Key、密碼、Token
- 不可直接操作生產資料庫
## 命名規範
- 類別:PascalCase
- 方法:camelCase,動詞開頭(findById, createOrder)
- 常數:UPPER_SNAKE_CASE
- DB 欄位:snake_case
## 錯誤處理 SOP
遇到編譯錯誤:
1. 讀取完整錯誤訊息
2. read_file 讀取出錯檔案(含上下文 ±20 行)
3. 分析根因
4. edit_file 修復
5. 重新編譯驗證
最多重試 3 次,若仍失敗則回報問題
## 常用指令
- 編譯:`mvn clean compile -q`
- 測試:`mvn test -q`
- 安全掃描:`mvn dependency-check:check -q`CLAUDE.md vs Skills vs System Prompt 的職責分工
| 機制 | 作用範圍 | 更新頻率 | 適合存放 |
|---|---|---|---|
| CLAUDE.md | 跨 Session,持久化 | 低(專案層面) | 不變的規則、禁止事項、技術規範 |
| Skills | 按需載入,暫時 | 中(知識更新) | 技術文件、最佳實踐、領域知識 |
| System Prompt | 單次 Session | 高(動態組裝) | 當前任務上下文、記憶摘要 |
CLAUDE.md 最佳實踐:
✅ 適合放入 CLAUDE.md:
- 專案架構描述(不常變)
- 絕對禁止的操作(安全紅線)
- 必須遵守的流程(閱讀 → 計畫 → 執行 → 測試)
- 命名規範和程式碼風格
- 常用指令速查
❌ 不適合放入 CLAUDE.md:
- 詳細的 API 文件(應放 Skills)
- 當前任務說明(應放 Prompt)
- 頻繁更動的配置
- 過長的內容(建議 < 500 行)與 Skills 的關鍵差異:CLAUDE.md 是「強制規則」,Agent 必須遵守;Skills 是「參考知識」,Agent 在需要時主動查詢。前者定義邊界,後者擴充能力。
5. 實作教學(Step-by-step)
5.1 環境安裝
系統需求
| 項目 | 最低要求 | 建議 |
|---|---|---|
| Python | 3.10+ | 3.12+ |
| Node.js | 18+(Web 平台用) | 20+ |
| Git | 2.30+ | 最新版 |
| OS | Windows 10 / Ubuntu 20.04 / macOS 12 | 最新版 |
| 記憶體 | 4 GB | 8 GB+ |
安裝步驟
# 1. Clone 專案
git clone https://github.com/shareAI-lab/learn-claude-code
cd learn-claude-code
# 2. 建立虛擬環境(建議)
python -m venv .venv
# Windows
.venv\Scripts\activate
# Linux / macOS
source .venv/bin/activate
# 3. 安裝依賴
pip install -r requirements.txt
# 4. 設定環境變數
cp .env.example .env設定 API Key
編輯 .env 檔案:
# Anthropic API Key(必要)
ANTHROPIC_API_KEY=sk-ant-xxxxxxxxxxxxx
# 模型選擇(可選,預設 claude-sonnet-4-6)
MODEL=claude-sonnet-4-6
# 如果使用替代 API 端點
# ANTHROPIC_BASE_URL=https://your-proxy.company.com/v1驗證安裝
# 執行最簡單的 Agent(s01)
python agents/s01_agent_loop.py
# 輸入測試指令
> 請列出當前目錄下的所有 Python 檔案5.2 專案初始化
專案目錄結構
learn-claude-code/
├── agents/ # 12 個 Session 的 Python 實作
│ ├── s01_agent_loop.py # Session 1: 基礎 Agent Loop
│ ├── s02_tool_use.py # Session 2: Tool Dispatch
│ ├── s03_todo_write.py # Session 3: TodoWrite 計畫
│ ├── s04_subagent.py # Session 4: Subagent 隔離
│ ├── s05_skills.py # Session 5: Skills 按需載入
│ ├── s06_context_compact.py # Session 6: Context 壓縮
│ ├── s07_tasks.py # Session 7: Task 持久化
│ ├── s08_background.py # Session 8: 背景任務
│ ├── s09_agent_teams.py # Session 9: Agent 團隊
│ ├── s10_team_protocols.py # Session 10: 團隊協定
│ ├── s11_autonomous.py # Session 11: 自治 Agent
│ ├── s12_worktree_task_isolation.py # Session 12: Worktree 隔離
│ └── s_full.py # 完整版:所有機制整合
├── docs/
│ ├── en/ # 英文文件
│ ├── zh/ # 中文文件
│ └── ja/ # 日文文件
├── skills/ # Skill 知識文件
├── web/ # Next.js 互動學習平台
├── tests/ # 測試
├── .env.example # 環境變數範本
└── requirements.txt # Python 依賴建議的學習路徑
Week 1:基礎(獨自完成)
├── Day 1-2:s01 Agent Loop — 理解核心迴圈
├── Day 3-4:s02 Tool Use — 新增工具到 dispatch map
└── Day 5 :s03 TodoWrite — 加入計畫能力
Week 2:進階(配對學習)
├── Day 1-2:s04 Subagent — 子任務隔離
├── Day 3 :s05 Skills — 按需知識載入
└── Day 4-5:s06 Context Compact — 壓縮策略
Week 3:持久化與團隊(團隊討論)
├── Day 1-2:s07 Tasks + s08 Background
├── Day 3-4:s09-s10 Agent Teams
└── Day 5 :s11-s12 Autonomous + Worktree
Week 4:整合與企業客製(實戰)
└── 基於 s_full.py 開發企業級 Agent5.3 第一個 Agent — Agent Loop
目標:用最少的程式碼建立一個能執行 Shell 命令的 Agent。
#!/usr/bin/env python3
"""
Session 01: The Agent Loop
格言:"One loop & Bash is all you need"
這是最基礎的 Agent — 一個 while 迴圈 + 一個 bash 工具。
模型決定何時呼叫工具、何時停止。程式碼只負責執行。
"""
import os
import subprocess
from anthropic import Anthropic
client = Anthropic()
MODEL = os.getenv("MODEL", "claude-sonnet-4-6")
# System Prompt:定義 Agent 的角色和行為規則
SYSTEM = """你是一位資深軟體工程師。
你可以使用 bash 工具執行 shell 命令來完成任務。
在執行命令前,先解釋你要做什麼以及為什麼。
"""
# Tool 定義:使用 JSON Schema 描述工具的名稱、用途、參數
TOOLS = [{
"name": "bash",
"description": "在當前目錄執行 shell 命令。適用於查看檔案、執行程式、Git 操作等。",
"input_schema": {
"type": "object",
"properties": {
"command": {
"type": "string",
"description": "要執行的 shell 命令"
}
},
"required": ["command"]
}
}]
def handle_bash(command: str) -> str:
"""執行 shell 命令並回傳輸出"""
try:
result = subprocess.run(
command, shell=True,
capture_output=True, text=True,
timeout=30, cwd=os.getcwd()
)
output = result.stdout + result.stderr
return output[:10000] if output else "(無輸出)"
except subprocess.TimeoutExpired:
return "ERROR: 命令執行超時(30 秒限制)"
TOOL_HANDLERS = {"bash": handle_bash}
def agent_loop():
"""互動式 Agent 主迴圈"""
print("=== Agent Loop (s01) ===")
print("輸入任務指令,Agent 會使用 bash 工具完成。輸入 'exit' 離開。\n")
messages = []
while True:
user_input = input("You> ").strip()
if user_input.lower() in ("exit", "quit"):
break
messages.append({"role": "user", "content": user_input})
# Agent 內部迴圈:持續執行直到模型決定停止
while True:
response = client.messages.create(
model=MODEL, system=SYSTEM,
max_tokens=4096, messages=messages, tools=TOOLS,
)
messages.append({
"role": "assistant",
"content": response.content
})
# 模型決定停止
if response.stop_reason != "tool_use":
for block in response.content:
if hasattr(block, "text"):
print(f"\nAgent> {block.text}\n")
break
# 執行模型請求的工具
results = []
for block in response.content:
if block.type == "tool_use":
print(f" [執行] {block.name}: {block.input}")
output = TOOL_HANDLERS[block.name](**block.input)
print(f" [結果] {output[:200]}...")
results.append({
"type": "tool_result",
"tool_use_id": block.id,
"content": output,
})
messages.append({"role": "user", "content": results})
if __name__ == "__main__":
agent_loop()執行範例:
=== Agent Loop (s01) ===
輸入任務指令,Agent 會使用 bash 工具完成。輸入 'exit' 離開。
You> 查看這個專案用了哪些 Python 套件
[執行] bash: {'command': 'cat requirements.txt'}
[結果] anthropic>=0.40.0\npython-dotenv>=1.0.0...
Agent> 這個專案使用了以下 Python 套件:
- anthropic:Anthropic API 客戶端
- python-dotenv:環境變數管理5.4 Tool 註冊(讀檔 / 寫檔 / Shell)
目標:擴展 Agent 的能力,新增讀寫檔案工具。
"""
Session 02: Tool Use
格言:"Adding a tool means adding one handler"
新增工具 = 新增一個 handler + 一個 schema
Agent Loop 完全不需要修改
"""
# 新增工具定義
NEW_TOOLS = [
{
"name": "read_file",
"description": "讀取檔案內容。可指定行號範圍。",
"input_schema": {
"type": "object",
"properties": {
"path": {"type": "string", "description": "檔案的相對路徑"},
"start_line": {"type": "integer", "description": "起始行號(1-based,可選)"},
"end_line": {"type": "integer", "description": "結束行號(1-based,可選)"}
},
"required": ["path"]
}
},
{
"name": "write_file",
"description": "建立或覆寫檔案。目錄不存在時自動建立。",
"input_schema": {
"type": "object",
"properties": {
"path": {"type": "string", "description": "檔案路徑"},
"content": {"type": "string", "description": "檔案完整內容"}
},
"required": ["path", "content"]
}
},
{
"name": "edit_file",
"description": "精確替換檔案中的一段文字。old_string 必須在檔案中出現恰好一次。",
"input_schema": {
"type": "object",
"properties": {
"path": {"type": "string", "description": "檔案路徑"},
"old_string": {"type": "string", "description": "要被替換的原始文字(含上下文)"},
"new_string": {"type": "string", "description": "替換後的新文字"}
},
"required": ["path", "old_string", "new_string"]
}
},
{
"name": "glob",
"description": "用 glob 模式搜尋檔案路徑",
"input_schema": {
"type": "object",
"properties": {
"pattern": {"type": "string", "description": "glob 模式,例如 '**/*.java'"}
},
"required": ["pattern"]
}
},
{
"name": "grep",
"description": "在檔案中搜尋文字或正規表達式",
"input_schema": {
"type": "object",
"properties": {
"pattern": {"type": "string", "description": "搜尋模式(支援 regex)"},
"path": {"type": "string", "description": "搜尋路徑(可為目錄)"},
"include": {"type": "string", "description": "只搜尋匹配的檔案,如 '*.java'"}
},
"required": ["pattern"]
}
}
]
# 新增 Handler 函式
import glob as glob_module
def handle_read_file(path: str, start_line: int = None, end_line: int = None) -> str:
try:
with open(path, "r", encoding="utf-8") as f:
lines = f.readlines()
if start_line and end_line:
selected = lines[max(0, start_line - 1):end_line]
header = f"[{path} L{start_line}-{end_line}]\n"
return header + "".join(selected)
return "".join(lines)[:15000]
except FileNotFoundError:
return f"ERROR: 檔案不存在: {path}"
def handle_write_file(path: str, content: str) -> str:
os.makedirs(os.path.dirname(path) or ".", exist_ok=True)
with open(path, "w", encoding="utf-8") as f:
f.write(content)
return f"OK: 已寫入 {path}({len(content)} 字元)"
def handle_edit_file(path: str, old_string: str, new_string: str) -> str:
try:
with open(path, "r", encoding="utf-8") as f:
content = f.read()
except FileNotFoundError:
return f"ERROR: 檔案不存在: {path}"
count = content.count(old_string)
if count == 0:
return "ERROR: 找不到要替換的字串"
if count > 1:
return f"ERROR: 找到 {count} 個匹配,請提供更精確的上下文"
new_content = content.replace(old_string, new_string, 1)
with open(path, "w", encoding="utf-8") as f:
f.write(new_content)
return "OK: 替換完成"
def handle_glob(pattern: str) -> str:
matches = sorted(glob_module.glob(pattern, recursive=True))
if not matches:
return f"找不到匹配 '{pattern}' 的檔案"
return "\n".join(matches[:100])
def handle_grep(pattern: str, path: str = ".", include: str = None) -> str:
import subprocess
cmd = f'grep -rn "{pattern}" {path}'
if include:
cmd += f' --include="{include}"'
result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
output = result.stdout
return output[:10000] if output else f"找不到匹配 '{pattern}' 的內容"
# 擴展 Dispatch Map
TOOL_HANDLERS.update({
"read_file": handle_read_file,
"write_file": handle_write_file,
"edit_file": handle_edit_file,
"glob": handle_glob,
"grep": handle_grep,
})
# Agent Loop 完全不變 — 新工具自動可用5.5 加入計畫能力 — TodoWrite
"""
Session 03: TodoWrite
格言:"An agent without a plan drifts"
加入 todo_write 工具 + nag reminder 機制
讓 Agent 先規劃再執行,大幅提升完成率
"""
class TodoManager:
def __init__(self):
self.todos = []
def write(self, todos: list) -> str:
self.todos = [
{"id": i + 1, "task": t["task"], "status": t.get("status", "todo")}
for i, t in enumerate(todos)
]
return self._format()
def update(self, todo_id: int, status: str) -> str:
for t in self.todos:
if t["id"] == todo_id:
t["status"] = status
return self._format()
return f"ERROR: 找不到任務 #{todo_id}"
def _format(self) -> str:
icons = {"todo": "⬜", "in_progress": "🔄", "done": "✅", "skipped": "⏭️"}
lines = ["📋 任務清單:"]
for t in self.todos:
lines.append(f" {icons.get(t['status'], '?')} #{t['id']} {t['task']}")
pending = sum(1 for t in self.todos if t["status"] in ("todo", "in_progress"))
lines.append(f"\n 進度:{len(self.todos) - pending}/{len(self.todos)} 完成")
return "\n".join(lines)
def get_nag_reminder(self) -> str:
"""產生提醒訊息"""
pending = [t for t in self.todos if t["status"] in ("todo", "in_progress")]
if not pending:
return ""
lines = ["\n⚠️ 尚未完成的任務:"]
for t in pending:
lines.append(f" - #{t['id']} {t['task']} [{t['status']}]")
lines.append("請繼續執行下一個任務。")
return "\n".join(lines)
todo_mgr = TodoManager()
# 註冊 todo_write tool
TODO_TOOL = {
"name": "todo_write",
"description": "建立或更新任務計畫清單。在開始複雜任務前,先用此工具規劃步驟。",
"input_schema": {
"type": "object",
"properties": {
"todos": {
"type": "array",
"items": {
"type": "object",
"properties": {
"task": {"type": "string"},
"status": {"type": "string", "enum": ["todo", "in_progress", "done", "skipped"]}
},
"required": ["task", "status"]
}
}
},
"required": ["todos"]
}
}
def handle_todo_write(todos: list) -> str:
return todo_mgr.write(todos)整合到 Agent Loop(加入 Nag Reminder):
# 在 tool_result 注入 nag reminder
def agent_loop_with_planning(user_message: str):
messages = [{"role": "user", "content": user_message}]
while True:
response = client.messages.create(
model=MODEL, system=SYSTEM,
max_tokens=4096, messages=messages, tools=TOOLS,
)
messages.append({"role": "assistant", "content": response.content})
if response.stop_reason != "tool_use":
for block in response.content:
if hasattr(block, "text"):
print(block.text)
return
results = []
for block in response.content:
if block.type == "tool_use":
output = TOOL_HANDLERS[block.name](**block.input)
results.append({
"type": "tool_result",
"tool_use_id": block.id,
"content": output,
})
# 🔑 關鍵:注入 Nag Reminder
nag = todo_mgr.get_nag_reminder()
if nag:
results[-1]["content"] += nag
messages.append({"role": "user", "content": results})5.6 加入測試能力
# 專門的測試工具
TEST_TOOL = {
"name": "run_tests",
"description": "執行專案的測試套件。支援指定測試檔案或測試類別。",
"input_schema": {
"type": "object",
"properties": {
"test_path": {
"type": "string",
"description": "測試檔案或目錄路徑(可選,預設執行全部測試)"
},
"test_class": {
"type": "string",
"description": "Java 測試類別名稱(可選)"
},
"framework": {
"type": "string",
"enum": ["maven", "gradle", "pytest", "jest"],
"description": "測試框架"
}
},
"required": ["framework"]
}
}
def handle_run_tests(framework: str, test_path: str = None,
test_class: str = None) -> str:
"""執行測試並回傳結構化結果"""
cmd_map = {
"maven": "mvn test",
"gradle": "./gradlew test",
"pytest": "python -m pytest",
"jest": "npx jest",
}
cmd = cmd_map.get(framework, "mvn test")
if test_class and framework == "maven":
cmd += f" -Dtest={test_class}"
elif test_path and framework == "pytest":
cmd += f" {test_path}"
result = subprocess.run(
cmd, shell=True, capture_output=True,
text=True, timeout=300 # 測試可能耗時較長
)
output = result.stdout + result.stderr
# 結構化輸出
status = "PASSED" if result.returncode == 0 else "FAILED"
return (
f"TEST_RESULT: {status}\n"
f"EXIT_CODE: {result.returncode}\n"
f"OUTPUT:\n{output[-5000:]}" # 取最後 5000 字元(通常包含結果摘要)
)5.7 加入錯誤修復能力
# 在 System Prompt 中加入錯誤修復指引
ERROR_RECOVERY_PROMPT = """
## 錯誤修復流程
當你遇到錯誤時,請遵循以下步驟:
### 編譯錯誤
1. 仔細閱讀錯誤訊息,找出出錯的檔案和行號
2. 使用 read_file 讀取出錯的程式碼(含上下文)
3. 分析錯誤原因
4. 使用 edit_file 修復
5. 重新編譯驗證
### 測試失敗
1. 閱讀測試失敗的詳細資訊(expected vs actual)
2. read_file 讀取測試程式碼
3. read_file 讀取被測試的實作程式碼
4. 判斷是測試錯誤還是實作錯誤
5. 修復並重跑測試
### 執行時期錯誤
1. 閱讀 stack trace
2. 定位到拋出例外的程式碼
3. 檢查輸入資料和狀態
4. 修復問題
### 注意事項
- 最多重試 3 次同一個修復
- 如果 3 次都無法解決,回報問題讓使用者決定
- 不要盲目修改,先理解再動手
"""
# 範例:自動修復流程
SYSTEM_WITH_RECOVERY = SYSTEM + ERROR_RECOVERY_PROMPT完整的自動修復範例流程:
User: "在 src/main/java/com/example 下建立 Calculator.java,包含加減乘除方法,
並建立對應的 JUnit 測試。所有測試必須通過。"
Agent 計畫(TodoWrite):
⬜ #1 建立 Calculator.java
⬜ #2 建立 CalculatorTest.java
⬜ #3 執行測試
⬜ #4 修復任何失敗的測試
Agent 執行:
🔄 #1 write_file → Calculator.java ✅
🔄 #2 write_file → CalculatorTest.java ✅
🔄 #3 run_tests → 2 FAILED
│
├── 分析:除法方法未處理除以零
├── read_file Calculator.java
├── edit_file → 加入除以零檢查
├── run_tests → 1 FAILED
│
├── 分析:測試期望的例外類型不對
├── read_file CalculatorTest.java
├── edit_file → 修正例外類型
├── run_tests → ALL PASSED ✅
│
✅ #4 所有測試通過實務注意:
- 在 System Prompt 中明確定義錯誤修復 SOP
- 限制重試次數,避免無限迴圈消耗 Token
- 鼓勵 Agent 先分析再修復,不要盲目嘗試
5.8 加入 Hooks 機制
在 Agent 具備基本的規劃與錯誤修復能力後,下一步是加入 Hooks 機制,讓系統具備可擴展的監控與安全防護能力,而無需修改核心迴圈。
實作步驟:
#!/usr/bin/env python3
"""
實作 Hooks 機制(對應 s04)
讓 Agent Loop 具備 PreToolUse / PostToolUse / Stop 三種擴展點
"""
import os
import json
import time
import subprocess
from anthropic import Anthropic
client = Anthropic()
MODEL = os.getenv("MODEL", "claude-sonnet-4-6")
# =========================================================
# Step 1: 定義 Hook Registry
# =========================================================
_HOOKS = {
"PreToolUse": [],
"PostToolUse": [],
"Stop": [],
}
def on(event: str):
"""裝飾器:將函式註冊為 Hook Handler"""
def decorator(fn):
_HOOKS[event].append(fn)
return fn
return decorator
def fire(event: str, **kwargs) -> dict:
"""觸發 Hook,收集所有 Handler 的回傳值"""
results = {}
for handler in _HOOKS.get(event, []):
result = handler(**kwargs)
if result:
results.update(result)
return results
# =========================================================
# Step 2: 實作各 Hook
# =========================================================
@on("PreToolUse")
def security_guard(tool_name: str, tool_input: dict, **_) -> dict | None:
"""安全防護:阻止危險命令"""
if tool_name == "bash":
cmd = tool_input.get("command", "")
DANGEROUS = ["rm -rf /", "DROP DATABASE", "chmod 777 /", "curl | bash"]
for d in DANGEROUS:
if d.lower() in cmd.lower():
print(f"🔴 [Security] 阻止危險命令: {cmd}")
return {"blocked": True, "reason": f"危險命令: {d}"}
return None
@on("PreToolUse")
def audit_logger(tool_name: str, tool_input: dict, **_):
"""審計日誌:記錄所有工具呼叫"""
entry = {
"timestamp": time.time(),
"event": "PRE_TOOL",
"tool": tool_name,
"input_summary": str(tool_input)[:200],
}
with open("audit.jsonl", "a") as f:
f.write(json.dumps(entry, ensure_ascii=False) + "\n")
@on("PostToolUse")
def token_tracker(tool_name: str, result: str, **_):
"""成本追蹤:累計 Token 使用量(估算)"""
estimated_tokens = len(result) // 4
entry = {
"timestamp": time.time(),
"event": "POST_TOOL",
"tool": tool_name,
"estimated_tokens": estimated_tokens,
}
with open("cost_tracking.jsonl", "a") as f:
f.write(json.dumps(entry) + "\n")
@on("Stop")
def completion_notifier(**_):
"""任務完成通知(可整合 Slack / Teams / Email)"""
print("✅ [Hook] Agent 任務完成")
# 實際整合:發送 Slack Webhook、觸發下游系統等
# =========================================================
# Step 3: 整合到 Agent Loop(僅需在工具呼叫前後插入 fire())
# =========================================================
def handle_bash(command: str) -> str:
result = subprocess.run(
command, shell=True, capture_output=True,
text=True, timeout=30
)
return (result.stdout + result.stderr)[:10000]
TOOL_HANDLERS = {"bash": handle_bash}
TOOLS = [{
"name": "bash",
"description": "執行 shell 命令",
"input_schema": {
"type": "object",
"properties": {"command": {"type": "string"}},
"required": ["command"]
}
}]
def agent_loop_with_hooks(user_message: str):
messages = [{"role": "user", "content": user_message}]
while True:
response = client.messages.create(
model=MODEL, max_tokens=4096,
messages=messages, tools=TOOLS,
)
messages.append({"role": "assistant", "content": response.content})
if response.stop_reason != "tool_use":
fire("Stop") # ← Stop Hook
for block in response.content:
if hasattr(block, "text"):
print(block.text)
return
results = []
for block in response.content:
if block.type != "tool_use":
continue
# ↓ PreToolUse Hook
hook_result = fire("PreToolUse",
tool_name=block.name,
tool_input=block.input)
if hook_result.get("blocked"):
output = f"BLOCKED: {hook_result.get('reason', '安全防護阻止')}"
else:
output = TOOL_HANDLERS[block.name](**block.input)
# ↓ PostToolUse Hook
fire("PostToolUse", tool_name=block.name, result=output)
results.append({
"type": "tool_result",
"tool_use_id": block.id,
"content": output,
})
messages.append({"role": "user", "content": results})
if __name__ == "__main__":
agent_loop_with_hooks("列出當前目錄下的所有 Python 檔案並統計行數")Hooks 機制的架構效益:
未使用 Hooks: 使用 Hooks 後:
┌──────────────┐ ┌──────────────┐
│ Agent Loop │ │ Agent Loop │
│ │ │ ↕ PreToolUse │
│ if cmd dangerous: │ ├─ security_guard
│ return error │ └─ audit_logger
│ if cmd ran: │ ↕ PostToolUse│
│ log_to_file(...) │ └─ token_tracker
│ if done: │ ↕ Stop │
│ send_notification(...) │ └─ completion_notifier
└──────────────┘ └──────────────┘
迴圈程式碼越來越複雜 迴圈程式碼保持乾淨
新增功能需修改核心迴圈 新增功能只需掛鉤設計原則:所有橫切面關注點(安全、審計、監控、通知)都透過 Hooks 實現,而非嵌入核心迴圈。這讓每一段程式碼各司其職,可以獨立測試、獨立部署。
6. 建立企業級 Coding Agent
6.1 Code Generation
企業級 Code Generation Agent 需要理解專案上下文,生成符合規範的程式碼:
# Code Generation 專用 System Prompt
CODE_GEN_SYSTEM = """你是企業級 Java 後端開發專家。
## 程式碼規範
- 使用 Clean Architecture(Controller → Service → Repository)
- 所有 public 方法需有 JavaDoc
- 使用 Lombok 簡化 boilerplate
- Entity 使用 JPA 註解
- DTO 與 Entity 分離
- 使用 MapStruct 做物件轉換
- 例外處理使用自訂 BusinessException
## 命名規範
- 類別:PascalCase(UserService、OrderController)
- 方法:camelCase(findById、createOrder)
- 常數:UPPER_SNAKE_CASE(MAX_RETRY_COUNT)
- 資料表:snake_case(user_account、order_detail)
## 安全規範
- SQL 必須使用參數化查詢
- 密碼必須 BCrypt 加密
- API 需有 JWT 驗證
- 輸入必須驗證(@Valid + @NotNull 等)
## 工作流程
1. 分析需求
2. 規劃程式碼結構(TodoWrite)
3. 生成程式碼(由底層往上:Entity → Repository → Service → Controller)
4. 生成測試
5. 執行測試確認通過
"""範例:生成完整的 CRUD API
Prompt: "建立使用者管理模組,包含:
- User Entity(id, username, email, password, status, createdAt)
- UserRepository(JPA)
- UserService(CRUD + 密碼加密)
- UserController(RESTful API)
- UserDTO / CreateUserRequest / UpdateUserRequest
- 完整的 JUnit 測試"
Agent 自動規劃並依序產生:
1. User.java(Entity + JPA 註解)
2. UserRepository.java(extends JpaRepository)
3. UserDTO.java + CreateUserRequest.java + UpdateUserRequest.java
4. UserMapper.java(MapStruct)
5. UserService.java(含 BCrypt 密碼加密)
6. UserController.java(REST + Swagger 註解)
7. UserServiceTest.java(Mockito)
8. UserControllerTest.java(MockMvc)
9. 執行 mvn test → 全部通過6.2 Code Review
# Code Review Agent 的 System Prompt
CODE_REVIEW_SYSTEM = """你是資深程式碼審查員。審查時請關注:
## 審查維度(依優先順序)
1. **安全性**(Critical)
- SQL Injection
- XSS
- 硬編碼密碼 / Token
- 不安全的反序列化
- 路徑遍歷
2. **正確性**(High)
- 邏輯錯誤
- 邊界條件
- Null 處理
- 併發問題
- 資源洩漏
3. **效能**(Medium)
- N+1 查詢
- 不必要的迴圈
- 大量物件建立
- 缺少索引
4. **可維護性**(Low)
- 命名清晰度
- 方法長度(< 30 行)
- 類別職責(SRP)
- 重複程式碼
## 輸出格式
對每個問題產出:
- 嚴重等級:Critical / High / Medium / Low
- 檔案和行號
- 問題描述
- 建議修復方式
- 修復後的程式碼片段
"""使用方式:
def code_review_agent(file_paths: list) -> str:
"""對指定檔案執行 Code Review"""
# 讀取所有待審查的檔案
file_contents = []
for path in file_paths:
with open(path, "r") as f:
file_contents.append(f"--- {path} ---\n{f.read()}")
prompt = f"""請審查以下程式碼:
{chr(10).join(file_contents)}
請依照安全性 > 正確性 > 效能 > 可維護性的優先順序,列出所有發現的問題。"""
# 使用 subagent 執行,避免汙染主 context
return handle_subagent(prompt, allowed_tools=["read_file", "grep"])6.3 Test Generation
TEST_GEN_SYSTEM = """你是測試工程師,負責為 Java 程式碼撰寫完整的單元測試。
## 測試框架
- JUnit 5
- Mockito(Mock 依賴)
- AssertJ(流暢斷言)
## 測試策略
1. **正常路徑**:驗證預期行為
2. **邊界條件**:空值、零、最大值、空集合
3. **錯誤路徑**:例外情境、無效輸入
4. **安全測試**:SQL Injection 嘗試、XSS payload
## 命名規範
- 測試方法:should_預期結果_when_條件
例如:should_throwException_when_userNotFound
## 覆蓋率目標
- 行覆蓋率 >= 80%
- 分支覆蓋率 >= 70%
- 關鍵業務邏輯 >= 90%
"""6.4 Refactoring
REFACTOR_SYSTEM = """你是重構專家,負責改善現有程式碼品質。
## 重構原則
1. **保持行為不變**:重構不改變外部行為
2. **小步前進**:每次只做一個重構
3. **測試保護**:重構前確認測試通過,重構後再跑一次
4. **編譯確認**:每步驟後編譯確認
## 常見重構手法
- Extract Method:將長方法拆分
- Extract Class:將大類別拆分
- Move Method:將方法移到更合適的類別
- Replace Conditional with Polymorphism
- Introduce Parameter Object
- Replace Magic Number with Constant
## 工作流程
1. 閱讀現有程式碼
2. 執行測試確認全部通過
3. 規劃重構步驟(TodoWrite)
4. 依序執行每個重構
5. 每步驟後執行測試
6. 提交可編譯、可測試的結果
"""6.5 文件產生
DOC_GEN_SYSTEM = """你是技術文件撰寫專家。
## 文件類型
1. **API 文件**:Swagger / OpenAPI 格式
2. **架構文件**:系統架構圖 + 元件說明
3. **開發指南**:環境建置 + 開發流程
4. **變更日誌**:版本變更紀錄
## 文件規範
- 使用 Markdown 格式
- 包含 Mermaid 圖表
- 提供程式碼範例
- 標示版本和最後更新時間
## 自動生成流程
1. 掃描專案結構(glob + grep)
2. 讀取關鍵程式碼
3. 分析架構和模式
4. 產出文件
"""實務案例:某團隊使用 Doc Generation Agent 自動分析 200+ 個 Java 類別,產出完整的架構文件和 API 文件,原本需要 2 人月的工作量縮短至 2 天。
7. Multi-Agent 設計(進階)
7.1 Agent Team 架構設計
graph TB
subgraph "Agent Team"
LA[Lead Agent<br/>架構決策 + 任務分派]
subgraph "Development Team"
AA[Architect Agent<br/>架構設計]
BA[Backend Agent<br/>後端開發]
FA[Frontend Agent<br/>前端開發]
end
subgraph "Quality Team"
QA[QA Agent<br/>測試撰寫]
SA[Security Agent<br/>安全掃描]
end
subgraph "Operations Team"
DA[DevOps Agent<br/>CI/CD + 部署]
DOC[Doc Agent<br/>文件產生]
end
end
LA --> AA
LA --> BA
LA --> FA
LA --> QA
LA --> SA
LA --> DA
LA --> DOC
AA -->|架構規格| BA & FA
BA -->|API 規格| FA
BA & FA -->|程式碼| QA & SA
QA -->|測試報告| BA & FA
SA -->|安全報告| BA & FA
DA -->|部署結果| LA各 Agent 角色定義:
| Agent | 角色 | 可用工具 | 模型建議 |
|---|---|---|---|
| Lead | 架構決策、任務拆解、協調 | todo_write, task_mgr, mailbox | Claude Opus |
| Architect | 系統設計、技術選型 | read_file, write_file, grep | Claude Sonnet |
| Backend | Java 後端開發 | 全部開發工具 | Claude Sonnet |
| Frontend | Vue/React 前端開發 | 全部開發工具 | Claude Sonnet |
| QA | 撰寫/執行測試 | read_file, write_file, run_tests | Claude Sonnet |
| Security | 安全掃描、弱掃 | read_file, grep, bash(受限) | Claude Sonnet |
| DevOps | CI/CD 配置、部署 | bash, write_file, docker | Claude Sonnet |
| Doc | 技術文件產生 | read_file, write_file, glob | DeepSeek(成本低) |
7.2 任務拆解策略
def decompose_task(high_level_task: str) -> list:
"""
Lead Agent 的任務拆解策略
原則:
1. 每個子任務在 50% context 內可完成
2. 明確定義輸入/輸出
3. 建立清晰的相依關係
"""
# 範例:拆解「建立使用者管理模組」
tasks = [
{
"id": "task-001",
"title": "設計使用者管理 API 規格",
"assignee": "architect",
"dependencies": [],
"deliverables": ["api-spec.yaml"],
},
{
"id": "task-002",
"title": "建立 User Entity 和 Repository",
"assignee": "backend",
"dependencies": ["task-001"],
"deliverables": ["User.java", "UserRepository.java"],
},
{
"id": "task-003",
"title": "實作 UserService(CRUD + 業務邏輯)",
"assignee": "backend",
"dependencies": ["task-002"],
"deliverables": ["UserService.java"],
},
{
"id": "task-004",
"title": "實作 UserController(REST API)",
"assignee": "backend",
"dependencies": ["task-003"],
"deliverables": ["UserController.java"],
},
{
"id": "task-005",
"title": "建立使用者管理前端頁面",
"assignee": "frontend",
"dependencies": ["task-001"], # 只需要 API 規格即可開始
"deliverables": ["UserList.vue", "UserForm.vue"],
},
{
"id": "task-006",
"title": "撰寫單元測試和整合測試",
"assignee": "qa",
"dependencies": ["task-003", "task-004"],
"deliverables": ["UserServiceTest.java", "UserControllerTest.java"],
},
{
"id": "task-007",
"title": "安全掃描",
"assignee": "security",
"dependencies": ["task-004"],
"deliverables": ["security-report.md"],
},
]
return tasks7.3 Agent 溝通方式 — JSONL Mailbox
class MailboxProtocol:
"""JSONL Mailbox 通訊協定"""
def __init__(self, team_dir: str = ".team"):
self.team_dir = team_dir
os.makedirs(team_dir, exist_ok=True)
def send_request(self, from_agent: str, to_agent: str,
task_id: str, content: str):
"""發送任務請求"""
msg = {
"id": f"msg-{int(time.time()*1000)}",
"from": from_agent,
"to": to_agent,
"type": "request",
"task_id": task_id,
"content": content,
"status": "pending",
"timestamp": datetime.now().isoformat(),
}
self._append(to_agent, msg)
return msg["id"]
def send_response(self, from_agent: str, to_agent: str,
task_id: str, status: str, result: str):
"""回覆任務結果"""
msg = {
"from": from_agent,
"to": to_agent,
"type": "response",
"task_id": task_id,
"status": status, # "done" / "failed" / "blocked"
"result": result,
"timestamp": datetime.now().isoformat(),
}
self._append(to_agent, msg)
def read_inbox(self, agent_name: str) -> list:
"""讀取信箱中的未處理訊息"""
mailbox_path = f"{self.team_dir}/{agent_name}.mailbox.jsonl"
if not os.path.exists(mailbox_path):
return []
with open(mailbox_path, "r") as f:
messages = [json.loads(line) for line in f if line.strip()]
# 回傳未處理的訊息
return [m for m in messages if m.get("status") == "pending"]
def _append(self, agent_name: str, msg: dict):
path = f"{self.team_dir}/{agent_name}.mailbox.jsonl"
with open(path, "a") as f:
f.write(json.dumps(msg, ensure_ascii=False) + "\n")溝通流程範例:
Lead → Backend Mailbox:
{"type": "request", "task_id": "task-003",
"content": "請根據 api-spec.yaml 實作 UserService,包含 CRUD 和密碼加密"}
Backend 處理:
1. 讀取 api-spec.yaml
2. 實作 UserService.java
3. 執行單元測試
4. 回報結果
Backend → Lead Mailbox:
{"type": "response", "task_id": "task-003", "status": "done",
"result": "已完成 UserService.java,6 個方法,12 個測試全部通過"}
Lead 收到回報:
→ 更新 task-003 狀態為 done
→ 檢查 task-006(QA)的相依是否滿足
→ 分派 task-006 給 QA Agent7.4 狀態管理與生命週期
class AgentLifecycle:
"""Agent 生命週期管理"""
STATES = ["idle", "working", "waiting", "shutdown"]
def __init__(self, agent_name: str):
self.name = agent_name
self.state = "idle"
self.current_task = None
self.events = [] # 事件日誌
def transition(self, new_state: str, reason: str = ""):
"""狀態轉換"""
old_state = self.state
self.state = new_state
event = {
"agent": self.name,
"from": old_state,
"to": new_state,
"reason": reason,
"timestamp": datetime.now().isoformat(),
}
self.events.append(event)
print(f"[{self.name}] {old_state} → {new_state}: {reason}")
def claim_task(self, task_id: str):
"""認領任務"""
self.current_task = task_id
self.transition("working", f"開始處理 {task_id}")
def complete_task(self):
"""完成任務"""
task_id = self.current_task
self.current_task = None
self.transition("idle", f"完成 {task_id}")
def request_shutdown(self):
"""請求關機"""
self.transition("shutdown", "收到關機指令")Team Protocol 有限狀態機(FSM):
┌───────────┐
建立 │ idle │◄────────────────┐
─────► │ (閒置) │ │
└─────┬─────┘ │
│ 認領任務 │ 完成任務
▼ │
┌───────────┐ │
│ working │──────────────────┘
│ (工作中) │
└─────┬─────┘
│ 等待相依
▼
┌───────────┐
│ waiting │──► 相依完成 ──► working
│ (等待) │
└─────┬─────┘
│ 關機指令
▼
┌───────────┐
│ shutdown │
│ (關機) │
└───────────┘實務建議:
- 初期使用 2-3 個 Agent(Lead + Backend + QA)即可
- 隨團隊熟練度提升再增加 Agent 數量
- 每個 Agent 使用獨立的 Worktree 避免衝突
- 建議在 CI/CD 中加入 Agent 操作的 Audit Log
8. 實際應用場景
8.1 Web Application 開發
Vue + Spring Boot 全端開發流程
sequenceDiagram
participant U as 使用者
participant L as Lead Agent
participant A as Architect Agent
participant B as Backend Agent
participant F as Frontend Agent
participant Q as QA Agent
U->>L: 建立訂單管理系統
L->>A: 設計系統架構
A-->>L: 架構文件 + API 規格
par 平行開發
L->>B: 實作後端 API
L->>F: 實作前端頁面
end
B-->>L: API 完成(含測試)
F-->>L: 前端完成
L->>Q: 整合測試
Q-->>L: 測試報告
L-->>U: 開發完成具體操作範例:
Step 1: 使用者下達需求
─────────────────────
Prompt: "建立訂單管理系統,需要:
- 訂單 CRUD API(Spring Boot)
- 訂單列表/詳情/建立頁面(Vue 3)
- 訂單狀態流轉(草稿→已提交→已確認→已完成/已取消)
- 分頁 + 搜尋功能"
Step 2: Lead Agent 拆解任務
────────────────────────────
📋 任務清單:
⬜ #1 [Architect] 設計 API 規格(OpenAPI)
⬜ #2 [Backend] 建立 Order Entity + Repository
⬜ #3 [Backend] 實作 OrderService(含狀態機)
⬜ #4 [Backend] 實作 OrderController
⬜ #5 [Frontend] 建立 OrderList.vue
⬜ #6 [Frontend] 建立 OrderDetail.vue
⬜ #7 [Frontend] 建立 OrderForm.vue
⬜ #8 [QA] 撰寫 API 整合測試
⬜ #9 [QA] 撰寫 E2E 測試
Step 3: 各 Agent 依相依順序執行
──────────────────────────────
Architect Agent 產出 → api-spec.yaml
Backend Agent 依序產出:
→ Order.java(Entity + 狀態枚舉)
→ OrderRepository.java
→ OrderService.java(含狀態轉換邏輯)
→ OrderController.java
Frontend Agent(與 Backend 平行開始):
→ orderApi.ts(根據 API 規格生成)
→ OrderList.vue(含分頁、搜尋)
→ OrderDetail.vue(含狀態流轉按鈕)
→ OrderForm.vue(含表單驗證)8.2 舊系統逆向工程
分析 Legacy Code 的 Agent 流程
REVERSE_ENG_SYSTEM = """你是逆向工程專家,負責分析舊系統程式碼。
## 分析流程
1. **結構探索**:掃描目錄結構,了解模組劃分
2. **進入點分析**:找到 main 方法、Controller、Servlet
3. **呼叫鏈追蹤**:從進入點追蹤到資料庫
4. **業務邏輯擷取**:識別核心業務規則
5. **資料模型分析**:分析 Entity / DAO / SQL
6. **文件產出**:產生架構文件和業務規則文件
## 產出物
- 系統架構圖(Mermaid)
- 模組清單與職責
- API 列表
- 資料庫 Schema 文件
- 業務規則文件
- 技術債清單
"""操作範例:
Prompt: "分析 /legacy-app 目錄下的舊系統程式碼,產出:
1. 系統架構文件
2. API 清單
3. 資料庫 Schema
4. 核心業務邏輯文件
5. 重構建議"
Agent 執行:
1. glob("**/*.java") → 找到 387 個 Java 檔案
2. grep("@Controller|@RestController|@RequestMapping") → 找到 23 個 Controller
3. grep("@Entity|@Table") → 找到 45 個 Entity
4. 逐一讀取關鍵檔案
5. 產出分析報告
產出範例(架構圖):graph TB
subgraph "Presentation Layer"
WEB[JSP Pages<br/>45 個頁面]
CTRL[Spring MVC Controllers<br/>23 個]
end
subgraph "Business Layer"
SVC[Service Classes<br/>38 個]
BIZ[Business Rules<br/>散布在 Service 中]
end
subgraph "Data Layer"
DAO[MyBatis Mappers<br/>45 個]
ENT[Entity Classes<br/>45 個]
end
subgraph "Infrastructure"
DB[(Oracle 11g<br/>78 個資料表)]
FTP[FTP Server<br/>檔案交換]
MQ[IBM MQ<br/>訊息佇列]
end
WEB --> CTRL
CTRL --> SVC
SVC --> BIZ
SVC --> DAO
DAO --> DB
SVC --> FTP
SVC --> MQ技術債清單(自動產出):
┌────┬────────────────────────────┬──────┬──────┐
│ # │ 問題 │ 嚴重 │ 影響 │
├────┼────────────────────────────┼──────┼──────┤
│ 1 │ SQL 字串拼接(32 處) │ High │ 安全 │
│ 2 │ 硬編碼密碼(5 處) │ High │ 安全 │
│ 3 │ 無單元測試(覆蓋率 0%) │ High │ 品質 │
│ 4 │ 巨型 Service(>500 行 x12)│ Med │ 維護 │
│ 5 │ 已棄用 API 使用(Java 8) │ Med │ 升級 │
│ 6 │ 無日誌記錄(Exception catch 後空)│ Med │ 維運 │
└────┴────────────────────────────┴──────┴──────┘8.3 Framework 升級
Spring Boot 2.x → 3.x 升級 Agent
UPGRADE_SYSTEM = """你是 Framework 升級專家。
## Spring Boot 2.x → 3.x 升級步驟
### Phase 1:評估
1. 掃描 pom.xml,列出所有依賴
2. 檢查 Java 版本(需 >= 17)
3. 分析 javax → jakarta 的影響範圍
4. 評估第三方套件相容性
### Phase 2:升級
1. 更新 parent pom 版本
2. 批次替換 javax → jakarta import
3. 更新 Spring Security 配置
4. 更新 Hibernate/JPA 配置
5. 處理已移除的 API
### Phase 3:驗證
1. 編譯專案
2. 執行全部測試
3. 逐一修復失敗
4. 執行整合測試
5. 產出升級報告
"""操作範例:
Prompt: "將此專案從 Spring Boot 2.7 升級到 3.2,Java 11 升級到 21"
Agent 自動執行:
📋 任務清單:
✅ #1 掃描現有依賴(找到 47 個依賴)
✅ #2 分析 javax import(找到 156 處)
✅ #3 更新 pom.xml
- spring-boot-starter-parent: 2.7.18 → 3.2.5
- java.version: 11 → 21
✅ #4 批次替換 javax → jakarta
- javax.persistence → jakarta.persistence(89 處)
- javax.validation → jakarta.validation(34 處)
- javax.servlet → jakarta.servlet(33 處)
✅ #5 更新 Spring Security 配置
- WebSecurityConfigurerAdapter → SecurityFilterChain
✅ #6 編譯 → 12 錯誤
✅ #7 修復編譯錯誤
✅ #8 執行測試 → 5 失敗
✅ #9 修復測試
✅ #10 全部通過 → 產出升級報告8.4 Batch 系統改寫
FTP / 排程 / 檔案處理 Agent
BATCH_REWRITE_SYSTEM = """你是批次系統改寫專家。
## 舊系統特徵
- Shell Script / COBOL / 舊版 Java
- FTP 檔案交換
- crontab 排程
- 固定格式文字檔案(Fixed-length / CSV / EBCDIC)
## 改寫策略
1. 分析現有批次流程(排程、輸入輸出、相依)
2. 設計新架構(Spring Batch / Apache Camel)
3. 實作 Reader → Processor → Writer
4. 建立測試資料
5. 比對新舊系統輸出
## 改寫原則
- 保持業務邏輯不變
- 使用 Spring Batch 的 chunk-oriented processing
- FTP → SFTP(安全升級)
- crontab → Spring Scheduler / Kubernetes CronJob
- 固定格式 → 結構化格式(JSON / Parquet)
- 加入監控和告警
"""範例:改寫 FTP 對帳批次
舊系統流程:
1. crontab 每日 02:00 觸發
2. Shell script 從 FTP 下載對帳檔
3. COBOL 程式解析固定格式檔案
4. 逐筆比對資料庫
5. 產出差異報表
6. FTP 上傳報表
新系統設計(Agent 產出):
┌─────────────────────────────────────────┐
│ Spring Batch Job │
│ │
│ ┌──────────┐ ┌──────────┐ ┌────────┐│
│ │ SFTP │ │ Processor│ │ Writer ││
│ │ Reader │→│ (比對) │→│ (報表) ││
│ └──────────┘ └──────────┘ └────────┘│
│ │
│ Step 1: Download from SFTP │
│ Step 2: Parse → ReconciliationItem │
│ Step 3: Compare with DB │
│ Step 4: Generate Report │
│ Step 5: Upload Report via SFTP │
│ Step 6: Send Notification │
└─────────────────────────────────────────┘
排程:Kubernetes CronJob
監控:Prometheus + Grafana
告警:PagerDuty / Teams Webhook實務注意:
- 批次改寫必須進行新舊系統輸出比對(Reconciliation)
- 使用 Agent 自動產生測試資料和比對腳本
- 灰度切換:先平行跑新舊系統,確認一致後再切換
- FTP → SFTP 需要與外部夥伴協調時程
9. 與企業架構整合
9.1 資料庫整合(DB2 / Oracle / PostgreSQL)
資料庫 Tool 設計
import os
DB_QUERY_TOOL = {
"name": "db_query",
"description": "對指定資料庫執行唯讀 SQL 查詢(僅 SELECT)。回傳 JSON 格式結果,最多 100 筆。",
"input_schema": {
"type": "object",
"properties": {
"database": {
"type": "string",
"enum": ["app_db", "audit_db", "analytics_db"],
"description": "目標資料庫"
},
"sql": {
"type": "string",
"description": "SELECT SQL 語句(禁止 DDL/DML)"
}
},
"required": ["database", "sql"]
}
}
DB_SCHEMA_TOOL = {
"name": "db_schema",
"description": "查詢資料庫 Schema 資訊(表結構、欄位、索引)",
"input_schema": {
"type": "object",
"properties": {
"database": {"type": "string", "enum": ["app_db", "audit_db"]},
"table_name": {"type": "string", "description": "資料表名稱(可選,不填則列出所有表)"}
},
"required": ["database"]
}
}
def handle_db_query(database: str, sql: str) -> str:
"""安全的資料庫查詢"""
import re
# 安全檢查:僅允許 SELECT
sql_upper = sql.strip().upper()
if not sql_upper.startswith("SELECT"):
return "ERROR: 僅允許 SELECT 查詢"
# 禁止危險操作
dangerous = ["DROP", "DELETE", "UPDATE", "INSERT", "ALTER", "TRUNCATE", "EXEC"]
for keyword in dangerous:
if re.search(rf'\b{keyword}\b', sql_upper):
return f"ERROR: 禁止使用 {keyword} 操作"
# 限制結果筆數
if "LIMIT" not in sql_upper and "FETCH FIRST" not in sql_upper:
sql = sql.rstrip(";") + " LIMIT 100"
# 執行查詢(示意)
# conn = get_connection(database)
# cursor = conn.cursor()
# cursor.execute(sql)
# results = cursor.fetchall()
# return json.dumps(results, default=str)
return "(資料庫查詢結果)"
def handle_db_schema(database: str, table_name: str = None) -> str:
"""查詢 Schema 資訊"""
if table_name:
# 查詢特定表結構
sql = f"""
SELECT column_name, data_type, is_nullable, column_default
FROM information_schema.columns
WHERE table_name = '{table_name}'
ORDER BY ordinal_position
"""
else:
# 列出所有表
sql = """
SELECT table_name, table_type
FROM information_schema.tables
WHERE table_schema = 'public'
ORDER BY table_name
"""
return handle_db_query(database, sql)多資料庫整合架構
┌───────────────────────────────────────┐
│ Agent Tool Layer │
│ │
│ db_query ─── Connection Pool │
│ ├── DB2 (JDBC) │
│ ├── Oracle (JDBC) │
│ └── PostgreSQL (JDBC) │
│ │
│ db_schema ── Metadata Cache │
│ └── 5 分鐘 TTL │
│ │
│ db_migration ── Flyway / Liquibase │
│ └── Version Control │
└───────────────────────────────────────┘9.2 訊息佇列(Kafka / RabbitMQ)
MQ_TOOL = {
"name": "mq_publish",
"description": "發送訊息到指定的 Kafka Topic 或 RabbitMQ Queue",
"input_schema": {
"type": "object",
"properties": {
"broker": {"type": "string", "enum": ["kafka", "rabbitmq"]},
"topic": {"type": "string", "description": "Topic 或 Queue 名稱"},
"message": {"type": "string", "description": "訊息內容(JSON 格式)"},
"key": {"type": "string", "description": "訊息 Key(Kafka 用,可選)"}
},
"required": ["broker", "topic", "message"]
}
}
MQ_CONSUME_TOOL = {
"name": "mq_consume",
"description": "從指定的 Topic/Queue 讀取最近的訊息(用於除錯)",
"input_schema": {
"type": "object",
"properties": {
"broker": {"type": "string", "enum": ["kafka", "rabbitmq"]},
"topic": {"type": "string"},
"count": {"type": "integer", "description": "讀取筆數(最多 10)", "maximum": 10}
},
"required": ["broker", "topic"]
}
}企業整合場景:
Agent 開發微服務時的 MQ 整合流程:
1. Agent 分析業務需求 → 決定使用事件驅動架構
2. 設計 Event Schema(OrderCreatedEvent、PaymentCompletedEvent)
3. 生成 Producer 程式碼(Spring Kafka Template)
4. 生成 Consumer 程式碼(@KafkaListener)
5. 使用 mq_publish tool 測試訊息發送
6. 使用 mq_consume tool 驗證訊息接收9.3 微服務架構整合
MICROSERVICE_SKILL = """
# 微服務開發 Skill
## 服務間通訊
- 同步:REST API(OpenFeign / WebClient)
- 異步:Kafka Event(Event-Driven)
- 服務發現:Consul / Eureka
## 共用元件
- API Gateway(Spring Cloud Gateway)
- Config Server(Spring Cloud Config)
- Circuit Breaker(Resilience4j)
- Distributed Tracing(Micrometer + Zipkin)
## 資料管理
- 每個服務獨立資料庫(Database per Service)
- 分散式交易(Saga Pattern)
- Event Sourcing + CQRS(複雜場景)
## 部署
- Docker Container
- Kubernetes Deployment
- Helm Chart
"""Agent 在微服務開發中的工作流:
graph LR
subgraph "Agent Team"
LA[Lead Agent]
BA1[Order Service Agent]
BA2[Payment Service Agent]
BA3[Notification Agent]
end
subgraph "Microservices"
OS[Order Service<br/>:8081]
PS[Payment Service<br/>:8082]
NS[Notification Service<br/>:8083]
end
subgraph "Infrastructure"
GW[API Gateway<br/>:8080]
KF[Kafka]
DB1[(Order DB)]
DB2[(Payment DB)]
end
LA --> BA1 & BA2 & BA3
BA1 --> OS
BA2 --> PS
BA3 --> NS
OS --> DB1
PS --> DB2
OS -->|OrderCreated| KF
KF -->|Event| PS & NS
GW --> OS & PS & NS9.4 CI/CD 整合(GitHub Actions / Jenkins)
CI_TOOL = {
"name": "ci_trigger",
"description": "觸發 CI/CD Pipeline 並等待結果",
"input_schema": {
"type": "object",
"properties": {
"platform": {"type": "string", "enum": ["github_actions", "jenkins"]},
"pipeline": {"type": "string", "description": "Pipeline 名稱"},
"parameters": {"type": "object", "description": "Pipeline 參數"}
},
"required": ["platform", "pipeline"]
}
}
def handle_ci_trigger(platform: str, pipeline: str,
parameters: dict = None) -> str:
"""觸發 CI/CD Pipeline"""
if platform == "github_actions":
# 使用 GitHub CLI
cmd = f"gh workflow run {pipeline}"
if parameters:
for k, v in parameters.items():
cmd += f" -f {k}={v}"
return handle_bash(cmd)
elif platform == "jenkins":
# 使用 Jenkins CLI
import urllib.request
jenkins_url = os.getenv("JENKINS_URL")
token = os.getenv("JENKINS_TOKEN")
# ... Jenkins API 呼叫
return "Pipeline 已觸發"GitHub Actions 整合範例:
# .github/workflows/agent-ci.yml
name: Agent CI Pipeline
on:
workflow_dispatch:
inputs:
task_id:
description: 'Agent Task ID'
required: true
branch:
description: 'Branch to build'
required: true
jobs:
build-and-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.event.inputs.branch }}
- name: Setup Java 21
uses: actions/setup-java@v4
with:
java-version: '21'
distribution: 'temurin'
- name: Build and Test
run: mvn clean verify
- name: Security Scan
run: mvn dependency-check:check
- name: Notify Agent
if: always()
run: |
echo "Task: ${{ github.event.inputs.task_id }}"
echo "Status: ${{ job.status }}"9.5 權限控管(RBAC)
class RBACManager:
"""角色基礎存取控制"""
ROLES = {
"viewer": {
"description": "僅能讀取",
"tools": ["read_file", "glob", "grep", "db_query"],
"paths": ["**/*"],
"actions": ["read"],
},
"developer": {
"description": "一般開發人員",
"tools": [
"read_file", "write_file", "edit_file",
"glob", "grep", "bash", "run_tests",
"todo_write", "db_query", "db_schema",
],
"paths": ["src/**", "tests/**", "docs/**"],
"actions": ["read", "write", "execute"],
"blocked_paths": [".env", "secrets/**", "*.key", "*.pem"],
},
"tech_lead": {
"description": "技術主管",
"tools": ["*"], # 所有工具
"paths": ["**/*"],
"actions": ["read", "write", "execute", "deploy"],
"require_approval": ["ci_trigger", "db_migration"],
},
"admin": {
"description": "系統管理員",
"tools": ["*"],
"paths": ["**/*"],
"actions": ["*"],
},
}
def __init__(self, role: str):
if role not in self.ROLES:
raise ValueError(f"未知角色: {role}")
self.role = role
self.config = self.ROLES[role]
def can_use_tool(self, tool_name: str) -> bool:
allowed = self.config["tools"]
return "*" in allowed or tool_name in allowed
def can_access_path(self, path: str) -> bool:
import fnmatch
blocked = self.config.get("blocked_paths", [])
for pattern in blocked:
if fnmatch.fnmatch(path, pattern):
return False
return True
def needs_approval(self, tool_name: str) -> bool:
return tool_name in self.config.get("require_approval", [])實務建議:
- 初期使用
developer角色,限制 Agent 的破壞力- 生產環境操作必須使用
tech_lead角色 + 審批流程- 所有操作記錄到 Audit Log
- 定期審查 Agent 的操作紀錄
9.6 MCP Server 整合
Model Context Protocol(MCP)是 Anthropic 於 2024 年推出的開放標準,在 2025-2026 年已成為企業 Agent 生態整合的主流方式。相比手工撰寫 Tool handler,MCP 提供標準化的服務發現、工具協商和安全邊界。
企業 MCP Server 部署架構
企業內網
┌──────────────────────────────────────────────────┐
│ │
│ Agent Runtime │
│ ┌─────────────────────────────────────────────┐ │
│ │ Tool Pool(MCPToolPool) │ │
│ │ ├── 內建工具(bash, read_file, etc.) │ │
│ │ └── MCP 動態工具(按需發現) │ │
│ └──────────────────┬──────────────────────────┘ │
│ │ MCP Protocol (JSON-RPC 2.0) │
│ ┌───────────────┼────────────────┐ │
│ ▼ ▼ ▼ │
│ MCP Server 1 MCP Server 2 MCP Server 3 │
│ [GitHub] [PostgreSQL] [Jira 企業版] │
│ - 建立 Issue - 執行查詢 - 建立 Ticket │
│ - Code Review - Schema 瀏覽 - 更新 Sprint │
│ - PR 管理 - Migration - 讀取任務板 │
└──────────────────────────────────────────────────┘企業自建 MCP Server 範例
# mcp_servers/internal_db_server.py
# 企業自建 MCP Server:封裝內部資料庫存取
from mcp.server import Server
from mcp.server.models import InitializationOptions
import mcp.server.stdio
import mcp.types as types
import asyncio
import json
# 建立 MCP Server 實例
server = Server("internal-db-server")
@server.list_tools()
async def handle_list_tools() -> list[types.Tool]:
"""宣告此 MCP Server 提供的工具清單"""
return [
types.Tool(
name="query_app_db",
description="查詢應用程式資料庫(唯讀 SELECT)",
inputSchema={
"type": "object",
"properties": {
"sql": {
"type": "string",
"description": "SELECT SQL 語句(禁止 DDL/DML)"
},
"limit": {
"type": "integer",
"description": "最大回傳筆數(預設 50,最大 200)",
"default": 50
}
},
"required": ["sql"]
}
),
types.Tool(
name="get_table_schema",
description="查詢指定資料表的欄位結構",
inputSchema={
"type": "object",
"properties": {
"table_name": {"type": "string"}
},
"required": ["table_name"]
}
),
]
@server.call_tool()
async def handle_call_tool(
name: str, arguments: dict
) -> list[types.TextContent]:
"""執行工具呼叫並回傳結果"""
if name == "query_app_db":
sql = arguments["sql"]
limit = min(arguments.get("limit", 50), 200)
# 安全檢查
sql_upper = sql.strip().upper()
if not sql_upper.startswith("SELECT"):
return [types.TextContent(
type="text",
text="ERROR: 僅允許 SELECT 查詢"
)]
# 加入 LIMIT 限制
if "LIMIT" not in sql_upper:
sql = sql.rstrip(";") + f" LIMIT {limit}"
# 執行查詢(使用連線池)
# results = await db_pool.fetch(sql)
# return [types.TextContent(type="text", text=json.dumps(results))]
return [types.TextContent(type="text", text="(資料庫查詢結果)")]
elif name == "get_table_schema":
table = arguments["table_name"]
schema_sql = f"""
SELECT column_name, data_type, is_nullable, column_default
FROM information_schema.columns
WHERE table_name = '{table}'
ORDER BY ordinal_position
"""
# results = await db_pool.fetch(schema_sql)
return [types.TextContent(type="text", text=f"Schema for {table}")]
raise ValueError(f"未知工具: {name}")
async def main():
"""以 stdio 模式啟動 MCP Server"""
async with mcp.server.stdio.stdio_server() as (read_stream, write_stream):
await server.run(
read_stream, write_stream,
InitializationOptions(
server_name="internal-db-server",
server_version="1.0.0",
)
)
if __name__ == "__main__":
asyncio.run(main())企業 MCP 部署設定(settings.json)
{
"mcpServers": {
"internal-db": {
"command": "python",
"args": ["mcp_servers/internal_db_server.py"],
"env": {
"DB_HOST": "internal-postgres.company.com",
"DB_PORT": "5432",
"DB_NAME": "app_db",
"DB_USER": "agent_readonly"
}
},
"github": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"],
"env": {
"GITHUB_PERSONAL_ACCESS_TOKEN": "${GITHUB_TOKEN}"
}
},
"jira": {
"command": "python",
"args": ["mcp_servers/jira_server.py"],
"env": {
"JIRA_URL": "https://company.atlassian.net",
"JIRA_TOKEN": "${JIRA_API_TOKEN}"
}
}
}
}MCP 安全考量
| 風險點 | 防護措施 |
|---|---|
| 工具命名衝突 | 使用 mcp__{server}__{tool} 命名前綴 |
| 機密資訊洩漏 | MCP Server 內建存取控制,不直接暴露連線字串 |
| 未授權工具呼叫 | 在 PreToolUse Hook 中驗證 MCP 工具白名單 |
| Server 不可用 | 實作 fallback 機制,降級使用內建工具 |
| 供應鏈攻擊 | 僅使用官方或自建 MCP Server,審計第三方套件 |
導入建議:企業初期建議優先自建 MCP Server 封裝內部系統(資料庫、Jira、內部 API),再逐步引入官方 MCP Server(GitHub、Slack 等)。所有 MCP Server 應部署於企業內網,使用服務帳號而非個人 Token。
10. 安全與 SSDLC
10.1 Prompt Injection 防護
class PromptInjectionGuard:
"""Prompt Injection 防護機制"""
# 常見 Prompt Injection 模式
INJECTION_PATTERNS = [
r"ignore\s+(previous|above|all)\s+instructions",
r"you\s+are\s+now\s+a",
r"new\s+instructions?:",
r"system\s*:\s*",
r"forget\s+(everything|all|previous)",
r"override\s+(system|instructions)",
r"jailbreak",
r"DAN\s+mode",
r"pretend\s+you\s+are",
]
@classmethod
def check_input(cls, text: str) -> tuple:
"""檢查輸入是否包含注入嘗試"""
import re
for pattern in cls.INJECTION_PATTERNS:
if re.search(pattern, text, re.IGNORECASE):
return False, f"偵測到可疑的 Prompt Injection 模式: {pattern}"
return True, "OK"
@classmethod
def sanitize_tool_output(cls, output: str) -> str:
"""清理 Tool 輸出中的潛在注入內容"""
# 將可能被解讀為指令的內容用引號包裹
if "SYSTEM:" in output.upper() or "INSTRUCTION:" in output.upper():
return f"[Tool Output - Sanitized]\n```\n{output}\n```"
return output
@classmethod
def check_file_content(cls, content: str, file_path: str) -> tuple:
"""檢查讀取的檔案內容是否包含注入嘗試"""
safe, reason = cls.check_input(content)
if not safe:
return False, f"檔案 {file_path} 包含可疑內容: {reason}"
return True, "OK"防護整合到 Agent Loop:
def secure_agent_loop(user_message: str):
"""帶安全防護的 Agent Loop"""
# 1. 檢查使用者輸入
safe, reason = PromptInjectionGuard.check_input(user_message)
if not safe:
print(f"⚠️ 安全警告: {reason}")
return
messages = [{"role": "user", "content": user_message}]
while True:
response = client.messages.create(
model=MODEL, system=SYSTEM,
max_tokens=4096, messages=messages, tools=TOOLS,
)
messages.append({"role": "assistant", "content": response.content})
if response.stop_reason != "tool_use":
break
results = []
for block in response.content:
if block.type == "tool_use":
# 2. 權限檢查
allowed, reason = rbac.can_use_tool(block.name)
if not allowed:
results.append({
"type": "tool_result",
"tool_use_id": block.id,
"content": f"PERMISSION_DENIED: {reason}",
"is_error": True,
})
continue
# 3. 執行工具
output = TOOL_HANDLERS[block.name](**block.input)
# 4. 清理輸出
output = PromptInjectionGuard.sanitize_tool_output(output)
results.append({
"type": "tool_result",
"tool_use_id": block.id,
"content": output,
})
messages.append({"role": "user", "content": results})10.2 Tool 權限控管
class ToolSecurityPolicy:
"""Tool 安全策略"""
def __init__(self):
self.audit_log = []
def pre_execute_check(self, tool_name: str, tool_input: dict,
agent_context: dict) -> tuple:
"""執行前安全檢查"""
checks = [
self._check_path_traversal,
self._check_command_injection,
self._check_sensitive_data_access,
self._check_rate_limit,
]
for check in checks:
passed, reason = check(tool_name, tool_input, agent_context)
if not passed:
self._log_security_event("BLOCKED", tool_name, tool_input, reason)
return False, reason
self._log_security_event("ALLOWED", tool_name, tool_input, "")
return True, "OK"
def _check_path_traversal(self, tool_name, tool_input, ctx):
"""檢查路徑遍歷攻擊"""
if "path" in tool_input:
path = tool_input["path"]
# 禁止 .. 路徑遍歷
if ".." in path:
return False, f"禁止路徑遍歷: {path}"
# 禁止絕對路徑(限制在專案目錄內)
if os.path.isabs(path):
return False, f"禁止絕對路徑: {path}"
return True, "OK"
def _check_command_injection(self, tool_name, tool_input, ctx):
"""檢查命令注入"""
if tool_name == "bash":
cmd = tool_input.get("command", "")
# 檢查危險的命令串接
dangerous_chars = [";", "&&", "||", "|", "`", "$(",]
for char in dangerous_chars:
if char in cmd:
# 允許 pipe 但需要更嚴格的檢查
if char == "|":
continue # pipe 在某些場景是合法的
return False, f"禁止命令中包含 '{char}'"
return True, "OK"
def _check_sensitive_data_access(self, tool_name, tool_input, ctx):
"""檢查敏感資料存取"""
sensitive_patterns = [
".env", "password", "secret", "token", "credential",
".key", ".pem", ".p12", ".jks",
]
if "path" in tool_input:
path = tool_input["path"].lower()
for pattern in sensitive_patterns:
if pattern in path:
return False, f"禁止存取敏感檔案: {tool_input['path']}"
return True, "OK"
def _check_rate_limit(self, tool_name, tool_input, ctx):
"""速率限制"""
# 最近 1 分鐘內的操作次數
import time
now = time.time()
recent = [e for e in self.audit_log if now - e.get("timestamp", 0) < 60]
if len(recent) > 100:
return False, "操作過於頻繁,請稍後再試(1 分鐘限 100 次)"
return True, "OK"
def _log_security_event(self, action, tool_name, tool_input, reason):
"""記錄安全事件"""
import time
self.audit_log.append({
"timestamp": time.time(),
"action": action,
"tool": tool_name,
"input_summary": str(tool_input)[:200],
"reason": reason,
})10.3 Code 安全掃描
SECURITY_SCAN_TOOL = {
"name": "security_scan",
"description": "對程式碼執行安全掃描(SAST)",
"input_schema": {
"type": "object",
"properties": {
"scan_type": {
"type": "string",
"enum": ["sast", "dependency", "secret"],
"description": "掃描類型"
},
"target": {
"type": "string",
"description": "掃描目標路徑"
}
},
"required": ["scan_type"]
}
}
def handle_security_scan(scan_type: str, target: str = ".") -> str:
"""執行安全掃描"""
if scan_type == "sast":
# 使用 SonarQube / Semgrep
return handle_bash(f"semgrep --config=auto {target} --json")
elif scan_type == "dependency":
# 使用 OWASP Dependency Check
return handle_bash("mvn dependency-check:check")
elif scan_type == "secret":
# 使用 git-secrets / trufflehog
return handle_bash(f"trufflehog filesystem {target} --json")
return "ERROR: 不支援的掃描類型"SSDLC 整合流程:
graph LR
subgraph "Development Phase"
CODE[Agent 產生程式碼]
REVIEW[Code Review Agent]
SAST[SAST 掃描]
end
subgraph "Testing Phase"
UT[單元測試]
IT[整合測試]
ST[安全測試]
end
subgraph "Deployment Phase"
DEP[Dependency Check]
IMG[Container Scan]
DEPLOY[部署]
end
CODE --> REVIEW
REVIEW --> SAST
SAST --> UT
UT --> IT
IT --> ST
ST --> DEP
DEP --> IMG
IMG --> DEPLOY10.4 Audit Log
import json
import time
from datetime import datetime
class AuditLogger:
"""操作審計日誌"""
def __init__(self, log_file: str = "audit.jsonl"):
self.log_file = log_file
def log(self, event_type: str, agent_name: str,
tool_name: str, tool_input: dict,
result_summary: str, user_id: str = None):
"""記錄操作事件"""
entry = {
"timestamp": datetime.now().isoformat(),
"event_type": event_type, # TOOL_CALL, PERMISSION_DENIED, ERROR, etc.
"agent_name": agent_name,
"user_id": user_id or "system",
"tool_name": tool_name,
"tool_input": self._sanitize_input(tool_input),
"result_summary": result_summary[:500],
"session_id": os.getenv("SESSION_ID", "unknown"),
}
with open(self.log_file, "a") as f:
f.write(json.dumps(entry, ensure_ascii=False) + "\n")
def _sanitize_input(self, tool_input: dict) -> dict:
"""移除敏感資訊"""
sanitized = {}
sensitive_keys = ["password", "token", "secret", "api_key"]
for k, v in tool_input.items():
if any(s in k.lower() for s in sensitive_keys):
sanitized[k] = "***REDACTED***"
else:
sanitized[k] = str(v)[:200]
return sanitized
def query(self, agent_name: str = None,
start_time: str = None, end_time: str = None) -> list:
"""查詢審計日誌"""
with open(self.log_file, "r") as f:
entries = [json.loads(line) for line in f if line.strip()]
if agent_name:
entries = [e for e in entries if e["agent_name"] == agent_name]
if start_time:
entries = [e for e in entries if e["timestamp"] >= start_time]
if end_time:
entries = [e for e in entries if e["timestamp"] <= end_time]
return entries10.5 合規(金融場景)
# 金融業合規 Agent 配置
FINANCIAL_COMPLIANCE_CONFIG = {
# 資料分級
"data_classification": {
"public": ["API 文件", "README"],
"internal": ["架構文件", "開發規範"],
"confidential": ["客戶資料", "交易記錄"],
"restricted": ["加密金鑰", "認證憑證"],
},
# Agent 存取限制
"access_rules": {
# Agent 不可直接接觸客戶個資
"no_direct_pii_access": True,
# 所有 DB 查詢必須審計
"audit_all_db_queries": True,
# 程式碼不可包含硬編碼憑證
"no_hardcoded_credentials": True,
# 所有外部 API 呼叫必須加密(TLS 1.2+)
"enforce_tls": True,
},
# 合規標準
"compliance_standards": [
"OWASP Top 10",
"PCI-DSS(信用卡資料)",
"ISO 27001",
"金管會資安管理辦法",
],
# 自動合規檢查
"auto_checks": [
"dependency_vulnerability_scan",
"static_code_analysis",
"secret_detection",
"license_compliance",
],
}金融場景的 Agent 安全架構:
┌──────────────────────────────────────────────┐
│ 安全邊界(Trust Boundary) │
│ │
│ ┌──────────────────┐ ┌────────────────┐ │
│ │ Agent Runtime │ │ Audit System │ │
│ │ ┌────────────┐ │ │ │ │
│ │ │ Permission │ │ │ • 操作日誌 │ │
│ │ │ Manager │──┼───│ • 異常偵測 │ │
│ │ └────────────┘ │ │ • 合規報告 │ │
│ │ ┌────────────┐ │ │ • 告警通知 │ │
│ │ │ Data │ │ └────────────────┘ │
│ │ │ Sanitizer │ │ │
│ │ └────────────┘ │ ┌────────────────┐ │
│ │ ┌────────────┐ │ │ Secret Vault │ │
│ │ │ Rate │ │ │ (HashiCorp │ │
│ │ │ Limiter │ │ │ Vault) │ │
│ │ └────────────┘ │ └────────────────┘ │
│ └──────────────────┘ │
│ │
│ ⛔ Agent 不可存取: │
│ • 生產資料庫(使用脫敏資料) │
│ • 客戶個資(PII) │
│ • 加密金鑰(透過 Vault API 間接使用) │
│ • 網路設備配置 │
└──────────────────────────────────────────────┘實務案例:某銀行在導入 Agent 時,要求所有 Agent 操作經過以下流程:
- Agent 產生程式碼 → 自動 SAST 掃描
- Agent 修改資料庫相關程式碼 → 自動 SQL Injection 檢查
- Agent 完成開發 → 自動依賴弱掃
- 所有操作日誌保留 7 年(合規要求)
11. 使用指南(給團隊)
11.1 日常開發流程
graph TD
A[收到需求] --> B[撰寫 Prompt]
B --> C{需求複雜度}
C -->|簡單| D[單 Agent 直接執行]
C -->|中等| E[Agent + TodoWrite 規劃]
C -->|複雜| F[Multi-Agent Team 協作]
D --> G[Review Agent 產出]
E --> G
F --> G
G --> H{品質是否達標}
H -->|是| I[提交 Code Review]
H -->|否| J[調整 Prompt 重試]
J --> B
I --> K[合併到主分支]日常開發 SOP:
1. 需求分析(5 分鐘)
- 閱讀需求文件
- 確認範圍和驗收標準
- 決定使用哪些 Agent
2. 撰寫 Prompt(10 分鐘)
- 描述需求(What)
- 指定技術規範(How)
- 定義驗收條件(Done)
3. Agent 執行(自動,15-60 分鐘)
- Agent 規劃任務
- 依序執行開發
- 自動測試和修復
4. 人工 Review(20 分鐘)
- 檢查架構決策
- 驗證業務邏輯
- 確認安全合規
- 驗證測試覆蓋率
5. 提交(5 分鐘)
- Git commit with meaningful message
- 建立 Pull Request
- 觸發 CI/CD11.2 如何下 Prompt
Prompt 結構模板
## 任務描述
[用 1-3 句話說明要做什麼]
## 技術規範
- 語言/框架:[Java 21 / Spring Boot 3.2]
- 架構模式:[Clean Architecture / 三層式]
- 資料庫:[PostgreSQL / JPA]
## 具體需求
1. [需求 1]
2. [需求 2]
3. [需求 3]
## 驗收條件
- [ ] 所有測試通過
- [ ] 覆蓋率 >= 80%
- [ ] 無 SAST 高風險項目
- [ ] API 有 Swagger 文件
## 注意事項
- [例外處理方式]
- [命名規範]
- [其他限制]範例 Prompt(由簡到繁)
簡單任務:
建立 StringUtils.java 工具類別,包含:
- isEmpty(String s): 檢查空字串(含 null 和空白)
- truncate(String s, int maxLen): 截斷字串
- toCamelCase(String s): snake_case 轉 camelCase
每個方法都要有 JavaDoc 和完整的 JUnit 測試。中等任務:
為現有的 Order 模組新增「訂單匯出 Excel」功能:
技術規範:
- 使用 Apache POI 4.x
- Spring Boot 3.2 / Java 21
需求:
1. 新增 OrderExportService,支援條件查詢後匯出
2. 匯出欄位:訂單編號、客戶名稱、金額、狀態、建立日期
3. 支援日期範圍篩選
4. 新增 REST API:GET /api/orders/export?startDate=&endDate=
5. 回傳 Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
驗收條件:
- 匯出 10,000 筆資料在 5 秒內完成
- 有完整的單元測試
- 有錯誤處理(無資料時回傳 204)複雜任務:
設計並實作「即時通知系統」微服務:
架構要求:
- 獨立 Spring Boot 微服務
- 使用 Kafka 接收事件
- 支援 Email / SMS / Push 三種通知管道
- 使用 Strategy Pattern 管理管道
- 通知模板使用 Thymeleaf
功能需求:
1. Kafka Consumer 監聽 notification-topic
2. NotificationEvent 包含:type, channel, recipient, templateId, variables
3. 每種管道有獨立的 Sender 實作
4. 通知記錄持久化到 PostgreSQL
5. 提供 REST API 查詢通知歷史
非功能需求:
- 重試機制:失敗後最多重試 3 次(指數退避)
- 限流:每秒最多 100 封 Email
- 監控:Prometheus metrics(發送量、失敗率、延遲)
請依序產出:
1. 專案結構和 pom.xml
2. Entity + Repository
3. Kafka Consumer
4. NotificationService + Strategy
5. 各管道 Sender
6. REST Controller
7. 完整測試11.3 如何 Debug Agent
常見 Debug 方式:
1. 查看 Agent 的任務規劃
- 檢查 TodoWrite 的輸出是否合理
- 任務是否遺漏重要步驟
2. 檢查 Tool 執行結果
- 每次 Tool call 的輸入和輸出
- 是否有 ERROR 被忽略
3. Context 分析
- 當前 messages[] 的 Token 用量
- 是否因 Context 過長導致「遺忘」
4. 重現問題
- 記錄導致問題的 Prompt
- 在乾淨的 Context 中重試
- 使用 subagent 隔離問題
5. 常用 Debug 指令
> "請解釋你剛才為什麼選擇這個做法"
> "請列出你目前的任務清單和進度"
> "請重新規劃任務,考慮 XXX 限制"11.4 常見錯誤與排除
| 問題 | 原因 | 解決方式 |
|---|---|---|
| Agent 不斷重複相同操作 | 缺少 TodoWrite 計畫 | 要求先規劃再執行 |
| Agent 忘記之前的指示 | Context 過長被壓縮 | 使用 Skill 注入、重要指示放 System Prompt |
| 產出的程式碼不符規範 | System Prompt 未定義規範 | 加入 Coding Style Skill |
| 測試一直失敗不修復 | 重試次數未限制 | 設定 max_retries = 3 |
| Agent 修改了不該改的檔案 | 路徑權限未設定 | 設定 ALLOWED_PATHS / BLOCKED_PATHS |
| Token 消耗過快 | 讀取過多大檔案 | 使用行號範圍讀取、限制輸出長度 |
| Agent 回答「我無法存取」 | API Key 過期或額度用完 | 檢查 .env 中的 API Key |
| 編譯成功但行為錯誤 | Agent 對業務邏輯理解有誤 | 提供更詳細的業務規則 Skill |
12. 最佳實踐(Best Practices)
12.1 Prompt 設計
✅ DO(好的做法)
─────────────────
1. 明確指定技術堆疊和版本
2. 提供具體的驗收條件
3. 複雜任務先請 Agent 規劃(Plan Mode)
4. 用範例說明預期輸出
5. 指出「不要」做什麼(避免過度設計)
❌ DON'T(壞的做法)
─────────────────
1. 模糊描述:「幫我做個登入功能」
2. 過於龐大:一次塞入 10 個功能
3. 無驗收標準:「做好就好」
4. 矛盾指令:同時要求簡潔和詳盡
5. 跳過 Review:直接合併 Agent 產出12.2 Tool 設計原則
| 原則 | 說明 | 範例 |
|---|---|---|
| 原子性 | 每個 Tool 做一件事 | read_file 只讀、write_file 只寫 |
| 可組合 | Tool 之間可自由組合 | 模型決定先 read 再 edit |
| 描述清晰 | description 要讓模型懂 | 說明用途、限制、回傳格式 |
| 安全邊界 | 限制可操作範圍 | 路徑白名單、命令黑名單 |
| 輸出可控 | 限制回傳長度 | 最多 10000 字元 |
| 錯誤友善 | 回傳有意義的錯誤 | ERROR: 找不到檔案 xxx |
12.3 Agent 拆分策略
判斷何時需要多個 Agent:
單一 Agent 適用場景:
• 修改 1-3 個檔案
• 單一模組的 CRUD
• Bug 修復
• 小型重構
Multi-Agent 適用場景:
• 跨模組開發(前後端 + DB)
• 大型功能(> 10 個檔案)
• 需要不同專業知識
• 需要平行處理
Agent 拆分原則:
1. 按職責拆分(SRP for Agents)
2. 每個 Agent 有明確的角色和工具集
3. 通訊透過 Mailbox,不共享 Context
4. 相依關係清晰(DAG)
5. 可獨立失敗和重試12.4 成本控制
Token 消耗優化策略:
1. 選擇合適的模型
┌────────────────────────────────────────┐
│ 任務類型 建議模型 相對成本 │
│ 架構設計 Opus $$$ │
│ 程式碼開發 Sonnet $$ │
│ 文件產生 Haiku/DeepSeek $ │
│ 格式轉換 Haiku $ │
└────────────────────────────────────────┘
2. 減少不必要的 Context
- 使用行號範圍讀取檔案(非全檔)
- 及時 compact(50% 時觸發)
- Subagent 隔離避免 Context 膨脹
3. 避免無效重試
- 設定 max_retries = 3
- 設定 max_loop_iterations = 50
- 失敗時回報而非無限重試
4. 批次處理
- 相似任務批次處理(降低 overhead)
- 使用 background task 平行執行
5. 預估成本
- 每個 Prompt 約消耗 1K-5K input tokens
- 每個 Tool call 約消耗 500-2K tokens
- 完整開發任務約 50K-200K tokens
- 按 Sonnet 費率:約 $0.15-$0.60 / 任務12.5 Context Engineering 最佳實踐
Context Engineering 是 2025-2026 年 Agent 工程的核心差異化能力。與傳統 Prompt Engineering(單一輸入最佳化)不同,Context Engineering 涵蓋跨 Session 狀態管理、多層次指令優先序、動態 Context 壓縮三個維度,決定了 Agent 在企業長期運行的品質與成本。
CLAUDE.md 撰寫最佳實踐
CLAUDE.md 是 Claude Code 跨 Session 持久化指令的載體,其內容在每次新 Session 啟動時自動注入 System Prompt。錯誤的 CLAUDE.md 設計是企業 Agent 品質不穩定的最常見來源。
# CLAUDE.md 企業模板(最佳實踐版)
## 角色定位(Role)
你是 [公司名稱] 的企業級 AI 開發助理,專注於 [領域]。
本地 repo 路徑:/home/agent/workspace
主要程式語言:TypeScript + Python
## 硬性規則(Hard Rules)— 永遠遵守,不得例外
- 禁止直接推送到 main/master 分支
- 禁止刪除 production 環境資源
- 所有外部 API 呼叫必須使用 .env 中的配置,不得硬編碼憑證
- 資料庫操作前必須確認備份存在
## 工作流程(Workflow)
1. 接收任務時,先閱讀 docs/architecture.md 了解系統架構
2. 修改前先執行 git status 確認工作目錄乾淨
3. 每完成一個子任務,更新 TODO.md 中的進度
4. PR 前必須通過 npm run lint && npm run test
## 目錄結構慣例(Project Structure)
src/
api/ # REST API 路由層
services/ # 業務邏輯層(禁止在此層直接呼叫 DB)
repositories/# 資料存取層
models/ # TypeORM Entity 定義
## 常用指令備忘(Quick Commands)
- 啟動開發環境:docker-compose up -d
- 執行測試:npm run test:watch
- 查看 log:docker logs -f app --tail=100反模式警告:CLAUDE.md 不應包含「請盡力做好每件事」、「保持創意」等模糊指令。每一條指令應可驗證(有明確的成功/失敗判斷標準)。
System Prompt 快取策略(Prompt Caching)
Anthropic API 支援 Prompt Caching,當 System Prompt 超過 1024 tokens 時,快取可將後續請求的 input token 成本降低 90%,並減少首個 token 延遲。
# prompt_cache_config.py
# 正確設定 Prompt Caching(企業 Harness 應在此層實作)
import anthropic
client = anthropic.Anthropic()
def build_cached_request(user_message: str, tools: list) -> dict:
"""建構帶 Prompt Cache 標記的請求"""
# System Prompt 主體(超過 1024 tokens 才觸發快取)
system_content = [
{
"type": "text",
"text": load_base_system_prompt(), # 基礎角色定義(穩定、不變)
"cache_control": {"type": "ephemeral"} # 標記為可快取
},
{
"type": "text",
"text": load_project_context(), # 專案背景(每日更新一次)
"cache_control": {"type": "ephemeral"} # 另一個快取層
},
{
"type": "text",
"text": f"今日日期:{get_today()}|任務背景:{get_session_context()}"
# 不加 cache_control:每次請求都變化,不值得快取
}
]
return {
"model": "claude-sonnet-4-6",
"max_tokens": 8192,
"system": system_content,
"tools": tools,
"messages": [{"role": "user", "content": user_message}]
}
def load_base_system_prompt() -> str:
"""載入穩定的基礎 System Prompt(快取命中率高)"""
# 這部分應包含:角色定義、工具使用規則、輸出格式要求
# 避免包含任何動態內容(時間、用戶名稱、任務ID)
with open("prompts/base_system.md") as f:
return f.read()
def load_project_context() -> str:
"""載入專案背景(每日重新快取)"""
# 這部分包含:架構摘要、程式碼慣例、API 端點清單
# 可以在 CI/CD 時自動更新
with open("prompts/project_context.md") as f:
return f.read()Token 預算管理
# token_budget_manager.py
# 企業 Agent 的 Token 預算控制層
class TokenBudgetManager:
"""
Token 預算三層架構:
- Session 預算:單次 Agent 任務的上限
- Daily 預算:每日總消耗上限(接入帳務系統)
- 模型選擇策略:依任務複雜度動態選擇模型
"""
MODEL_PRICING = {
"claude-opus-4-8": {"input": 15.0, "output": 75.0}, # per 1M tokens
"claude-sonnet-4-6": {"input": 3.0, "output": 15.0},
"claude-haiku-4-5-20251001": {"input": 0.8, "output": 4.0},
}
def __init__(self, session_budget_usd: float = 1.0):
self.session_budget = session_budget_usd
self.spent = 0.0
self.call_count = 0
def select_model(self, task_complexity: str) -> str:
"""依複雜度選擇最符合成本效益的模型"""
mapping = {
"high": "claude-opus-4-8", # 架構設計、複雜推理
"medium": "claude-sonnet-4-6", # 一般開發任務
"low": "claude-haiku-4-5-20251001" # 格式轉換、文件生成
}
return mapping.get(task_complexity, "claude-sonnet-4-6")
def record_usage(self, model: str, input_tokens: int, output_tokens: int):
"""記錄 Token 使用量並計算費用"""
pricing = self.MODEL_PRICING[model]
cost = (input_tokens / 1_000_000 * pricing["input"] +
output_tokens / 1_000_000 * pricing["output"])
self.spent += cost
self.call_count += 1
if self.spent > self.session_budget * 0.8:
print(f"[警告] 已消耗 {self.spent:.4f} USD,接近預算上限")
def check_budget(self) -> bool:
"""檢查是否仍有剩餘預算"""
return self.spent < self.session_budget
def summary(self) -> dict:
return {
"total_calls": self.call_count,
"total_spent_usd": round(self.spent, 4),
"budget_remaining_usd": round(self.session_budget - self.spent, 4),
"budget_utilization_pct": round(self.spent / self.session_budget * 100, 1)
}Context 壓縮策略對照
| 策略 | 觸發時機 | 效果 | 風險 |
|---|---|---|---|
| Prompt Caching | System Prompt > 1024 tokens | 節省 90% input 成本 | 快取失效時成本回升 |
| Context Compact | Context 達 50% 時 | 維持 Session 可用 | 可能遺失早期細節 |
| Subagent 隔離 | 獨立子任務 | 避免 Context 膨脹 | 子任務間需要明確傳遞狀態 |
| 行號範圍讀取 | 讀取大型檔案 | 減少無效 Context | 需要事先知道目標行號 |
| CLAUDE.md 摘要 | 重複性背景資訊 | 減少每次描述成本 | 需定期更新維護 |
核心原則:Context 的品質 > Context 的量。50 個精確相關的 Token 比 5000 個「可能有用」的 Token 更能提升 Agent 輸出品質。定期審視 System Prompt 中哪些內容 Agent 真正「用到了」,刪除未被引用的背景資訊。
13. 願景:用 Agent 覆蓋每一個領域
13.1 跨領域 Agent 設計模式
learn-claude-code 教的是程式開發領域的 Harness,但其設計模式具有跨領域的普適性。Agent 的核心能力(感知、推理、行動)由模型提供,Harness 因領域而變,但設計模式保持一致。
以下是將 Agent Harness 架構泛化到不同領域的設計模式:
通用 Agent Harness 架構(跨領域):
┌───────────────────────────────────────────────────┐
│ Agent Loop │
│ │
│ 感知(Observe) → 推理(Reason) → 行動(Act) → 回饋 │
└───────────────────┬───────────────────────────────┘
│
┌───────────────────┴───────────────────────────────┐
│ Harness │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Tools │ │Knowledge │ │ Context │ │
│ │ (領域) │ │ (領域) │ │ Manager │ │
│ └──────────┘ └──────────┘ └──────────┘ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │Permission│ │ Task │ │ Memory │ │
│ │ Boundary │ │ System │ │ System │ │
│ └──────────┘ └──────────┘ └──────────┘ │
└───────────────────────────────────────────────────┘| 領域 | Tools | Knowledge | Observation | 典型任務 |
|---|---|---|---|---|
| 程式開發 | 讀寫檔案、Shell、Git、LSP | API 文件、架構指南、程式碼風格 | 編譯錯誤、測試結果、Lint 警告 | 寫功能、修 Bug、重構、Review |
| 農業管理 | 灌溉控制、感測器讀取、氣象 API | 作物知識庫、土壤資料、農藥規範 | 土壤濕度、溫度、氣象預報 | 灌溉排程、病蟲害預警、收成預測 |
| 金融交易 | 下單 API、風控查詢、合規檢查 | 法規文件、市場數據、歷史模型 | 持倉狀態、風險指標、市場行情 | 風險評估、自動對沖、報表產出 |
| 製造業 | 設備 OPC-UA、MES API、SCADA | 製程規格、品質標準、設備手冊 | 設備狀態、良率數據、告警 | 預防保養、品質預測、排程優化 |
| 醫療保健 | HIS/EMR API、檢驗查詢、藥品庫 | 臨床指引、藥典、診斷規則 | 病人數據、檢驗報告、生命徵象 | 診斷輔助、藥物交互檢查、報告 |
| 物流調度 | 路線規劃 API、車隊管理、倉儲 | 路網數據、承載規範、時效要求 | GPS 位置、交通狀態、庫存 | 路線優化、調度排程、異常處理 |
| 教育培訓 | 學習平台 API、評量系統、課程庫 | 課程大綱、教學策略、學習理論 | 學習進度、成績分析、行為數據 | 個人化學習路徑、自動出題 |
| 科學研究 | 儀器控制、資料庫查詢、計算資源 | 論文庫、實驗流程、統計方法 | 實驗數據、模擬結果、文獻 | 實驗設計、數據分析、論文輔助 |
核心洞察:上表中每一列的 Harness 結構完全相同 — 只是 Tool、Knowledge 和 Observation 的具體內容不同。這正是 learn-claude-code 教學的價值所在:一旦你理解了 Harness 工程的設計模式,你就可以為任何領域建構 Agent。
13.2 從被動會話到主動常駐助手
傳統的 AI Agent 是「被動式」的 — 你給它一個指令,它執行完畢,對話結束。但真正改變世界的 Agent 是常駐式的 — 它們永遠在線、主動監控、自動回應事件。
被動 Agent(Chat → Response):
使用者 → "幫我寫一個函式" → Agent 執行 → 完成 → 結束
常駐 Agent(Always-On):
┌─────────────────────────────────────────────────┐
│ │
│ ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ │
│ │Cron │ │Event │ │IM │ │Webhook│ │
│ │定時 │ │事件 │ │即時 │ │外部 │ │
│ │巡檢 │ │觸發 │ │通訊 │ │觸發 │ │
│ └──┬───┘ └──┬───┘ └──┬───┘ └──┬───┘ │
│ └──────────┴──────────┴──────────┘ │
│ │ │
│ ┌─────┴─────┐ │
│ │ Agent Core│ │
│ │ (Model + │ │
│ │ Harness) │ │
│ └─────┬─────┘ │
│ │ │
│ ┌──────────────┼──────────────┐ │
│ │ │ │ │
│ ┌──┴──┐ ┌────┴───┐ ┌────┴──┐ │
│ │Memory│ │Actions │ │Report │ │
│ │記憶 │ │行動 │ │報告 │ │
│ └─────┘ └────────┘ └───────┘ │
│ │
└─────────────────────────────────────────────────┘常駐 Agent 的關鍵機制:
| 機制 | 說明 | 例子 |
|---|---|---|
| Heartbeat | 定期自我健康檢查,確保 Agent 持續運作 | 每 5 分鐘檢查 Agent 狀態 |
| Cron | 排程式任務觸發,定時巡檢和維護 | 每天凌晨 2 點掃描安全漏洞 |
| IM Integration | 透過即時通訊接收指令和回報 | Slack / Discord / Teams 整合 |
| Memory Persistence | 跨 Session 的記憶保存,讓 Agent 「記得」 | 記住上次部署的版本和問題 |
| Soul(個性/角色) | 定義 Agent 的行為風格和溝通方式 | 嚴謹的安全工程師 vs 友善的教學助手 |
前瞻:learn-claude-code 的姊妹專案 claw0 正是這一願景的實現 — 一個常駐式 AI Agent 教學專案。詳見 14.3 claw0。
14. 延伸資源與姊妹專案
learn-claude-code 並非獨立存在的專案。它是 shareAI-lab 生態系統中的基礎教學元件,與多個姊妹專案共同構成完整的 Agent 開發生態。
14.1 Kode Agent CLI
Kode CLI 是基於 learn-claude-code 設計原理打造的生產級 Coding Agent 命令列工具。
# 安裝
npm i -g @shareai-lab/kode
# 使用
kode "重構 UserService,將驗證邏輯抽出為獨立類別"| 特性 | 說明 |
|---|---|
| 多模型支援 | Claude / GPT / DeepSeek / Qwen / Gemini / 本地 Ollama |
| Skill 系統 | 按需載入領域知識(對應 s05) |
| LSP 整合 | 語言伺服器協定整合,更精準的程式碼理解 |
| Context 管理 | 自動壓縮和摘要(對應 s06) |
| Task 系統 | 帶相依圖的任務持久化(對應 s07) |
| 權限控管 | 安全邊界和審批流程(對應 s11) |
定位:learn-claude-code 教你原理,Kode CLI 讓你直接上手使用。兩者互補。
14.2 Kode Agent SDK
Kode Agent SDK 提供程式化介面,讓你在自己的應用程式中嵌入 Agent 能力。
import { Agent, Tool, Skill } from '@shareai-lab/kode-agent-sdk';
// 建立自訂 Agent
const agent = new Agent({
model: 'claude-sonnet-4-6',
tools: [readFile, writeFile, bash, dbQuery],
skills: ['./skills/company-coding-style'],
permissions: {
allowedPaths: ['src/', 'tests/'],
blockedCommands: ['rm -rf', 'DROP TABLE'],
requireApproval: ['git push', 'deploy'],
},
});
// 執行任務
const result = await agent.run('分析並修復 OrderService 中的效能問題');| 適用場景 | 說明 |
|---|---|
| CI/CD 整合 | 在 Pipeline 中嵌入 Agent 自動修復失敗的建置 |
| Internal Tool | 建構企業內部的 AI 開發助手 |
| SaaS 產品 | 在自己的產品中嵌入 Coding Agent 功能 |
| 自動化工作流 | 與現有自動化系統整合 |
14.3 claw0 — 常駐 Agent 教學專案
claw0(claw zero)是 learn-claude-code 的姊妹教學專案,專注於教授常駐式(Always-On)Agent 的建構方法。
如果 learn-claude-code 教你如何建構「你呼叫、它回應」的被動式 Agent,claw0 則教你如何建構「永遠在線、主動行動」的常駐 Agent。
learn-claude-code 的 Agent: claw0 的 Agent:
User → Prompt → Agent → Done Agent ──────────────────┐
│ Heartbeat(健康檢查) │
│ Cron(排程巡檢) │
│ IM(即時通訊整合) │
│ Memory(跨 Session) │
│ Soul(人格與風格) │
└────────────────────────┘
永遠在線,主動行動claw0 的五大核心機制:
| 機制 | 功能 | 對應 learn-claude-code |
|---|---|---|
| Heartbeat | 定期自我健康檢查、資源監控 | — (新機制) |
| Cron | 排程式任務:定時安全掃描、效能報告、程式碼品質巡檢 | s08 Background Tasks 的延伸 |
| IM Integration | Slack / Discord / Teams 整合,讓團隊成員透過聊天指揮 Agent | — (新機制) |
| Persistent Memory | 跨 Session 的長期記憶,Agent 記得歷史上下文 | s06 Context Compact 的延伸 |
| Soul | 定義 Agent 的角色、個性、溝通風格 | System Prompt 的進階版 |
學習路徑建議:先完成 learn-claude-code 的 s01-s12,掌握 Harness 基礎機制,再進入 claw0 學習常駐 Agent 的進階模式。
14.4 OpenClaw — 社群生態
OpenClaw 是 shareAI-lab 的開放生態計畫,鼓勵社群基於 learn-claude-code 和 claw0 的設計模式,為不同領域建構專屬的 Agent Harness。
目標是讓每一個有價值的領域都有一個「好用的載具」等著 Agent 駕駛。
15. 附錄
15.1 範例 Prompt 集
A. 新功能開發
## 任務:實作使用者認證模組
### 技術規格
- Spring Boot 3.2 + Spring Security 6
- JWT Token(Access Token + Refresh Token)
- BCrypt 密碼加密
- PostgreSQL
### 功能需求
1. POST /api/auth/register — 使用者註冊
2. POST /api/auth/login — 登入取得 Token
3. POST /api/auth/refresh — 刷新 Token
4. POST /api/auth/logout — 登出(Token 黑名單)
### 資料模型
- User: id, username, email, passwordHash, roles, createdAt, updatedAt
- RefreshToken: id, userId, token, expiresAt, createdAt
### 安全要求
- Access Token 有效期 15 分鐘
- Refresh Token 有效期 7 天
- 密碼需符合複雜度(>= 8 字元,含大小寫和數字)
- 登入失敗 5 次鎖定 30 分鐘
### 驗收條件
- [ ] 所有 API 可正常運作
- [ ] 單元測試覆蓋率 >= 80%
- [ ] 包含整合測試
- [ ] 無 OWASP Top 10 弱點B. Bug 修復
## 問題描述
OrderService.calculateTotal() 在處理折扣時計算錯誤。
## 重現步驟
1. 建立訂單包含 3 個商品
2. 套用 20% 折扣
3. 預期總金額:800,實際:1000(折扣未生效)
## 相關檔案
- src/main/java/com/example/service/OrderService.java
- src/test/java/com/example/service/OrderServiceTest.java
## 請求
1. 找到 Bug 原因
2. 修復問題
3. 新增測試覆蓋此情境
4. 確認所有既有測試仍通過C. 程式碼重構
## 重構目標
將 UserService.java(目前 520 行)重構為符合 SRP 的多個類別。
## 重構範圍
- src/main/java/com/example/service/UserService.java
## 限制
- 不改變 public API 介面
- 所有既有測試必須通過
- 每次重構後編譯確認
## 預期結果
- UserService(核心 CRUD,< 100 行)
- UserValidationService(驗證邏輯)
- UserNotificationService(通知邏輯)
- UserStatisticsService(統計邏輯)D. 逆向工程
## 分析目標
分析 /legacy-app/src 目錄下的舊系統程式碼,產出以下文件:
1. **系統架構文件**
- 模組劃分和職責
- 系統架構圖(Mermaid)
- 模組間依賴關係
2. **API 清單**
- 所有 REST API 端點
- 請求/回應格式
- 認證方式
3. **資料庫文件**
- ER 圖(Mermaid)
- 表結構說明
- 關鍵索引
4. **業務規則文件**
- 核心業務邏輯說明
- 狀態流轉規則
- 驗證規則
5. **技術債清單**
- 安全問題
- 效能問題
- 可維護性問題
- 建議修復優先順序15.2 CLI 指令速查表
# ===== 基礎操作 =====
# 啟動 Agent(各 Session)
python agents/s01_agent_loop.py # 基礎迴圈
python agents/s02_tool_use.py # 多工具
python agents/s03_todo_write.py # 計畫能力
python agents/s04_subagent.py # 子 Agent
python agents/s05_skills.py # Skills 載入
python agents/s06_context_compact.py # Context 壓縮
python agents/s07_tasks.py # 任務管理
python agents/s08_background.py # 背景任務
python agents/s09_agent_teams.py # Agent 團隊
python agents/s10_team_protocols.py # 團隊協定
python agents/s11_autonomous.py # 自治 Agent
python agents/s12_worktree_task_isolation.py # Worktree 隔離
python agents/s_full.py # 完整版
# ===== Web 平台 =====
cd web && npm install && npm run dev # http://localhost:3000
# ===== 環境管理 =====
cp .env.example .env # 建立環境變數檔
pip install -r requirements.txt # 安裝依賴
python -m pytest tests/ # 執行測試
# ===== Git Worktree(s12 使用)=====
git worktree add worktrees/task-001 -b feature/task-001 # 建立隔離空間
git worktree list # 列出所有 worktree
git worktree remove worktrees/task-001 # 清理 worktree15.3 常見問題 FAQ
Q1:需要什麼等級的 API Key? A:需要 Anthropic API Key。建議使用 Claude Sonnet 系列模型,平衡效能和成本。若預算充裕,複雜架構決策可使用 Opus。
Q2:可以使用 OpenAI / DeepSeek 等模型嗎?
A:可以。修改 .env 中的 ANTHROPIC_BASE_URL 指向相容的 API 端點(如 OpenRouter),並調整 MODEL 參數。注意不同模型的 tool_use 格式可能不同,需要適配。
Q3:Token 消耗大概多少? A:視任務複雜度而定:
- 簡單任務:5K-20K tokens(約 $0.01-$0.06)
- 中等任務:20K-100K tokens(約 $0.06-$0.30)
- 複雜任務:100K-500K tokens(約 $0.30-$1.50)
Q4:Agent 陷入無限迴圈怎麼辦?
A:設定 MAX_LOOP_ITERATIONS = 50 和 max_retries = 3。在 System Prompt 中明確指示「最多重試 3 次,若仍失敗則回報問題」。
Q5:如何確保 Agent 不會破壞程式碼? A:
- 使用 Git Worktree 隔離(每個任務獨立分支)
- 設定路徑白名單和黑名單
- 設定命令黑名單
- 要求每次修改後執行測試
- 人工 Review 後才合併
Q6:多人同時使用 Agent 會衝突嗎? A:使用 Worktree 隔離(s12),每個 Agent 在獨立的 Git 分支工作,不會互相干擾。完成後透過 Git merge 合併。
Q7:如何讓 Agent 理解企業特有的規範?
A:使用 Skills 機制(s05)。將企業規範寫成 SKILL.md 檔案,Agent 在需要時按需載入。例如 skills/company-coding-style/SKILL.md。
Q8:Agent 可以直接操作生產環境嗎? A:強烈不建議。Agent 應該在開發/測試環境工作,產出透過 CI/CD Pipeline 經過人工審批後才部署到生產環境。
Q9:如何衡量 Agent 的效能? A:追蹤以下指標:
- 任務完成率(首次 vs 重試後)
- 平均 Token 消耗 / 任務
- 人工 Review 的修改率
- 測試通過率
Q10:learn-claude-code 和 Kode CLI 是什麼關係?
A:learn-claude-code 是教學專案,教你理解原理。Kode CLI 是基於同樣原理打造的生產級 Coding Agent CLI 工具,支援 Skill / LSP / 多模型,可直接安裝使用:npm i -g @shareai-lab/kode。
15.4 檢查清單(Checklist)
環境建置 Checklist
- Python 3.10+ 已安裝
- Node.js 18+(如需 Web 平台)
- Git 2.30+ 已安裝
- 專案已 Clone:
git clone https://github.com/shareAI-lab/learn-claude-code - 虛擬環境已建立:
python -m venv .venv - 依賴已安裝:
pip install -r requirements.txt -
.env已設定ANTHROPIC_API_KEY - 可成功執行
python agents/s01_agent_loop.py
Agent 開發 Checklist
- System Prompt 已定義角色和規則
- Tool 定義包含清晰的 description 和 schema
- Tool Handler 有錯誤處理
- 設定 MAX_LOOP_ITERATIONS 限制
- 設定 max_retries 限制
- 輸出長度有截斷限制
- 路徑存取有白名單/黑名單
- 危險命令有黑名單
- Audit Log 已啟用
企業導入 Checklist
- 安全策略已定義(RBAC、Tool 權限)
- Prompt Injection 防護已啟用
- Audit Log 已配置
- CI/CD 整合已設定
- 團隊已完成 Session 1-6 的學習
- Coding Style Skill 已建立
- Error Recovery SOP 已定義
- 成本監控已設定
- 緊急停止機制已就緒
- Code Review 流程已建立
生產環境 Checklist
- Agent 不可直接存取生產資料庫
- 所有操作透過 CI/CD Pipeline
- 部署需人工審批
- 監控和告警已設定
- Rollback 計畫已準備
- 合規檢查已通過
- 團隊已完成安全訓練
- 事故應變流程已建立
文件資訊
- 作者:AI Agent 開發團隊
- 版本:v1.2(2026-07)
- 最後更新:2026-07-01
- 授權:MIT License
- 參考專案:shareAI-lab/learn-claude-code(69.3k+ Stars)
- 線上學習:learn.shareai.run
- 姊妹工具:Kode CLI | Kode SDK | claw0
Agency comes from the model. The harness makes agency real. Build great harnesses. The model will do the rest.