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. 專案概述

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-s06Agent Loop / Tool Dispatch / Permission / Hooks / TodoWrite / Subagent基礎 6 大機制
Stage 2:複雜任務s07-s11Skills / Context Compact / Memory / System Prompt / Error Recovery知識管理與容錯
Stage 3:長期運作s12-s14Task DAG / Background Tasks / Cron Scheduler持久化與排程
Stage 4:多 Agents15-s18Agent Teams / Protocols / Autonomous / Worktree團隊協作
Stage 5:整合延伸s19-s20MCP 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 是訓練出來的,不是編出來的:

年份里程碑意義
2013DeepMind DQN 玩 Atari單一神經網路,僅接收原始像素和分數,學會 7 款 Atari 遊戲,超越所有先前演算法。2015 年擴展至 49 款遊戲並達到職業測試員水準,發表於 Nature。沒有遊戲專屬規則,沒有決策樹。一個模型,從經驗中學習。
2019OpenAI Five 征服 Dota 2五個神經網路自我對戰 45,000 年的 Dota 2,2-0 擊敗 TI8 世界冠軍 OG。公開競技場 42,729 場比賽勝率 99.4%。模型完全透過自我對弈學會團隊協作和戰術。
2019DeepMind AlphaStar 制霸星際爭霸 II閉門賽 10-1 擊敗職業選手,在歐洲伺服器達到宗師段位 — 90,000 名玩家中的前 0.15%。
2019騰訊絕悟統治王者榮耀5v5 擊敗 KPL 職業選手。1v1 模式職業選手 15 場僅贏 1 場。訓練強度:一天等於人類 440 年。完全從零通過自我對弈學習整個遊戲。
2024-2025LLM 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 CodeCursorlearn-claude-code
性質Anthropic 官方商用產品商用 IDE開源教學專案
目的直接使用直接使用學習原理並自建 Agent
模型綁定Claude 系列多模型可替換任意 LLM
可客製化有限(CLAUDE.md / Hooks)Plugin 系統完全可控
企業適用性受限於 API 政策受限於 SaaS可私有部署、完全自主
原始碼閉源閉源MIT 開源
安全控管依賴平台依賴平台自行設計權限邊界
成本按 Token 計費訂閱制自選模型,成本可控
學習價值黑箱黑箱每個機制透明可學
Task-Process Data不可得不可得完整保留,可用於微調

1.8 為何企業應該自己打造 Agent

企業自建 Agent 的六大理由

  1. 資料主權與合規

    • 金融業程式碼不可外洩至第三方 SaaS
    • GDPR / 個資法 / 金管會要求資料落地
    • 自建 Agent 可部署於內網、Air-gapped 環境
  2. 深度客製化

    • 整合企業特有的框架、規範、流程
    • 注入企業知識庫(內部 API 文件、架構決策紀錄)
    • 自訂 Tool 對接企業基礎設施(DB2 / MQ / 內部 CI)
  3. 成本最佳化

    • 可選用開源模型(DeepSeek / Qwen / Llama)
    • 混合路由:簡單任務用小模型、複雜任務用大模型
    • 避免被單一供應商綁定
  4. 安全邊界可控

    • 自行設計 Tool 權限、沙箱隔離
    • 實作 Audit Log、合規審計
    • Prompt Injection 防護策略可客製
  5. 競爭力護城河

    • 累積企業特有的 Task-Process Data(訓練素材)
    • Agent 效能隨使用持續提升
    • 形成不可複製的智能開發能力
  6. 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 User

2.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-001

2.6 Memory 系統(短期 / 長期)

┌─────────────────────────────────────────┐
│              Memory 架構                 │
│                                         │
│  ┌─────────────┐   ┌─────────────────┐  │
│  │  短期記憶    │   │  長期記憶        │  │
│  │  (Context)  │   │  (Persistent)   │  │
│  │             │   │                 │  │
│  │ • messages[]│   │ • Skills 文件    │  │
│  │ • Tool 結果  │   │ • Task 歷史      │  │
│  │ • 當前 Todo  │   │ • 架構決策紀錄   │  │
│  │             │   │ • 錯誤修復經驗   │  │
│  └──────┬──────┘   └────────┬────────┘  │
│         │                    │           │
│         ▼                    ▼           │
│  Context Compact      On-Demand Load    │
│  (三層壓縮)           (tool_result 注入) │
└─────────────────────────────────────────┘

三層壓縮策略(s06)

層級觸發時機壓縮方式保留內容
L1Token 用量 > 50%摘要舊對話近期完整 + 遠期摘要
L2Token 用量 > 70%移除工具細節僅保留結論
L3Token 用量 > 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 -->|回報結果| LA

JSONL 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 / Oracledb_query ToolSchema 分析、資料查詢、Migration
Kafkamq_publish / mq_subscribe Tool事件驅動整合
Jenkinsci_trigger Tool觸發建置 / 部署
Jiraissue_crud Tool自動建立 Issue / 更新狀態
SonarQubesecurity_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 設計三原則

  1. 原子性(Atomic):每個 Tool 做一件事,做好做完
  2. 可組合(Composable):Tool 之間可自由組合,由模型決定順序
  3. 描述清晰(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 掃描、意圖分類
PreToolUseTool 執行前安全檢查、命令審查、權限驗證
PostToolUseTool 執行後成本追蹤、輸出清洗、Audit Log
StopAgent 正常停止完成通知、結果報告、資源清理
SubagentStopSubagent 停止子任務完成彙報、結果聚合

核心設計原則: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. 更新任務狀態為 done

3.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 MCPIssue、PR、Code Review 管理DevOps 自動化
PostgreSQL MCP資料庫查詢與 Schema 管理資料分析任務
Filesystem MCP增強型檔案操作複雜檔案管理
Slack MCP頻道訊息、通知團隊協作整合
Jira MCPTicket 管理、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 User

s20 的核心啟示: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 差異

領域ToolsKnowledgeObservation
程式開發讀寫檔案、Shell、GitAPI 文件、架構指南編譯錯誤、測試結果
金融交易下單 API、風控查詢法規、市場數據持倉狀態、風險指標
DevOpsDocker、K8s、TerraformRunbook、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_filerun_testquery_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 環境安裝

系統需求

項目最低要求建議
Python3.10+3.12+
Node.js18+(Web 平台用)20+
Git2.30+最新版
OSWindows 10 / Ubuntu 20.04 / macOS 12最新版
記憶體4 GB8 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 開發企業級 Agent

5.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, mailboxClaude Opus
Architect系統設計、技術選型read_file, write_file, grepClaude Sonnet
BackendJava 後端開發全部開發工具Claude Sonnet
FrontendVue/React 前端開發全部開發工具Claude Sonnet
QA撰寫/執行測試read_file, write_file, run_testsClaude Sonnet
Security安全掃描、弱掃read_file, grep, bash(受限)Claude Sonnet
DevOpsCI/CD 配置、部署bash, write_file, dockerClaude Sonnet
Doc技術文件產生read_file, write_file, globDeepSeek(成本低)

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 tasks

7.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 Agent

7.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 & NS

9.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 --> DEPLOY

10.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 entries

10.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 操作經過以下流程:

  1. Agent 產生程式碼 → 自動 SAST 掃描
  2. Agent 修改資料庫相關程式碼 → 自動 SQL Injection 檢查
  3. Agent 完成開發 → 自動依賴弱掃
  4. 所有操作日誌保留 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/CD

11.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 CachingSystem Prompt > 1024 tokens節省 90% input 成本快取失效時成本回升
Context CompactContext 達 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   │          │
  │  └──────────┘ └──────────┘ └──────────┘          │
  └───────────────────────────────────────────────────┘
領域ToolsKnowledgeObservation典型任務
程式開發讀寫檔案、Shell、Git、LSPAPI 文件、架構指南、程式碼風格編譯錯誤、測試結果、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 教學專案

claw0claw 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 IntegrationSlack / 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  # 清理 worktree

15.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 = 50max_retries = 3。在 System Prompt 中明確指示「最多重試 3 次,若仍失敗則回報問題」。

Q5:如何確保 Agent 不會破壞程式碼? A:

  1. 使用 Git Worktree 隔離(每個任務獨立分支)
  2. 設定路徑白名單和黑名單
  3. 設定命令黑名單
  4. 要求每次修改後執行測試
  5. 人工 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 計畫已準備
  • 合規檢查已通過
  • 團隊已完成安全訓練
  • 事故應變流程已建立

文件資訊


Agency comes from the model. The harness makes agency real. Build great harnesses. The model will do the rest.