多 Agent Token 優化系列 pt.6:Agent 專責化實戰指南 — 打造精準高效的專家團隊

在《多 Agent 系統的 Token 用量調優指南》中,我們介紹了 Agent 專責化 作為降低 System Prompt Token 消耗的核心策略。本文將深入實作層面,探討如何將臃腫的「全能 Agent」拆分為精準的「專家團隊」,讓每個 Agent 只專注於單一職責,從而大幅減少每次 API 呼叫的固定成本。


為什麼需要 Agent 專責化?

通用 Agent 的問題

通用 Agent(反模式)的 System Prompt:

┌─────────────────────────────────────────────────────────────────────┐
│                                                                     │
│  "你是一個全能 AI 助手,可以幫助使用者完成以下任務:               │
│                                                                     │
│  【資料分析】                                                       │
│  - SQL 查詢撰寫和優化                                               │
│  - 統計分析和趨勢預測                                               │
│  - 資料視覺化建議                                                   │
│  - ETL 流程設計                                                     │
│  [詳細說明... 2000 tokens]                                          │
│                                                                     │
│  【程式開發】                                                       │
│  - Python、JavaScript、Go、Rust、Java 開發                          │
│  - 前端和後端開發                                                   │
│  - API 設計和實作                                                   │
│  - 資料庫設計                                                       │
│  [詳細說明... 3000 tokens]                                          │
│                                                                     │
│  【系統架構】                                                       │
│  - 微服務設計                                                       │
│  - 雲端架構                                                         │
│  - 效能優化                                                         │
│  [詳細說明... 2500 tokens]                                          │
│                                                                     │
│  【文件撰寫】                                                       │
│  - 技術文件                                                         │
│  - API 文件                                                         │
│  - 使用手冊                                                         │
│  [詳細說明... 1500 tokens]                                          │
│                                                                     │
│  【程式碼審查】                                                     │
│  - 安全性審查                                                       │
│  - 效能審查                                                         │
│  - 最佳實踐檢查                                                     │
│  [詳細說明... 2000 tokens]                                          │
│                                                                     │
│  【工具定義】                                                       │
│  - 30+ 個工具的 Schema                                              │
│  [工具定義... 5000 tokens]                                          │
│                                                                     │
│  總計:~16,000 tokens 的 System Prompt                              │
│  問題:每次呼叫都發送全部內容,即使只做簡單翻譯                    │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

專責化的效益

專責化 Agent 團隊:

┌─────────────────┐  ┌─────────────────┐  ┌─────────────────┐
│ 資料分析 Agent  │  │ 程式開發 Agent  │  │ 架構設計 Agent  │
│                 │  │                 │  │                 │
│ System: 2,500t  │  │ System: 3,000t  │  │ System: 2,800t  │
│ Tools: 5 個     │  │ Tools: 6 個     │  │ Tools: 4 個     │
│ Model: Haiku    │  │ Model: Sonnet   │  │ Model: Opus     │
└─────────────────┘  └─────────────────┘  └─────────────────┘

┌─────────────────┐  ┌─────────────────┐  ┌─────────────────┐
│ 文件撰寫 Agent  │  │ 程式審查 Agent  │  │ 協調者 Agent    │
│                 │  │                 │  │                 │
│ System: 1,800t  │  │ System: 2,200t  │  │ System: 1,500t  │
│ Tools: 3 個     │  │ Tools: 4 個     │  │ Tools: 2 個     │
│ Model: Haiku    │  │ Model: Haiku    │  │ Model: Sonnet   │
└─────────────────┘  └─────────────────┘  └─────────────────┘

效益計算:
- 通用 Agent 每次呼叫:16,000 tokens
- 專責 Agent 平均每次:2,300 tokens
- 節省:85% 的 System Prompt tokens
- 額外好處:可針對任務選擇最適合的模型

核心概念:職責邊界設計

職責劃分原則

Agent 職責劃分的 SOLID 原則:

S - Single Responsibility(單一職責)
    每個 Agent 只負責一種類型的任務
    ❌ "分析資料並生成報告和撰寫程式碼"
    ✅ "分析資料" → Agent A
    ✅ "生成報告" → Agent B
    ✅ "撰寫程式碼" → Agent C

O - Open/Closed(開放封閉)
    Agent 可擴展新能力,但核心職責不變
    ✅ 資料分析 Agent 可支援新的資料來源
    ❌ 資料分析 Agent 不應該開始寫程式碼

L - Liskov Substitution(里氏替換)
    同類型 Agent 應可互換
    ✅ Python 開發 Agent 和 TypeScript 開發 Agent 有相同介面

I - Interface Segregation(介面隔離)
    Agent 只暴露必要的介面
    ✅ 程式碼審查 Agent 只需要 read_file,不需要 write_file

D - Dependency Inversion(依賴反轉)
    Agent 依賴抽象的 Context,不依賴具體實作
    ✅ 透過 Orchestrator 傳遞標準化 Context

常見的 Agent 職責模式

 1from enum import Enum
 2from dataclasses import dataclass, field
 3from typing import Optional
 4
 5class AgentRole(Enum):
 6    """Agent 角色類型"""
 7    # 分析類
 8    REQUIREMENTS_ANALYST = "requirements_analyst"
 9    DATA_ANALYST = "data_analyst"
10    SECURITY_ANALYST = "security_analyst"
11
12    # 設計類
13    ARCHITECT = "architect"
14    UI_DESIGNER = "ui_designer"
15    API_DESIGNER = "api_designer"
16
17    # 開發類
18    BACKEND_DEVELOPER = "backend_developer"
19    FRONTEND_DEVELOPER = "frontend_developer"
20    DATABASE_DEVELOPER = "database_developer"
21
22    # 品質類
23    CODE_REVIEWER = "code_reviewer"
24    SECURITY_REVIEWER = "security_reviewer"
25    PERFORMANCE_REVIEWER = "performance_reviewer"
26
27    # 文件類
28    DOC_WRITER = "doc_writer"
29    API_DOC_WRITER = "api_doc_writer"
30
31    # 測試類
32    TEST_DESIGNER = "test_designer"
33    TEST_IMPLEMENTER = "test_implementer"
34
35    # 協調類
36    ORCHESTRATOR = "orchestrator"
37    TASK_ROUTER = "task_router"
38
39@dataclass
40class AgentCapability:
41    """Agent 能力定義"""
42    role: AgentRole
43    description: str
44    core_skills: list[str]
45    tools_needed: list[str]
46    typical_output: str
47    recommended_model: str = "claude-sonnet-4-20250514"
48    max_system_tokens: int = 3000
49
50# 預定義的 Agent 能力庫
51AGENT_CAPABILITIES = {
52    AgentRole.REQUIREMENTS_ANALYST: AgentCapability(
53        role=AgentRole.REQUIREMENTS_ANALYST,
54        description="分析和整理使用者需求",
55        core_skills=["需求提取", "使用者故事撰寫", "驗收標準定義"],
56        tools_needed=["read_document"],
57        typical_output="結構化需求文件",
58        recommended_model="claude-sonnet-4-20250514",
59        max_system_tokens=2500
60    ),
61
62    AgentRole.BACKEND_DEVELOPER: AgentCapability(
63        role=AgentRole.BACKEND_DEVELOPER,
64        description="實作後端程式碼和 API",
65        core_skills=["Python/Go/Node.js", "API 設計", "資料庫操作"],
66        tools_needed=["read_file", "write_file", "run_command"],
67        typical_output="可執行的後端程式碼",
68        recommended_model="claude-sonnet-4-20250514",
69        max_system_tokens=3500
70    ),
71
72    AgentRole.CODE_REVIEWER: AgentCapability(
73        role=AgentRole.CODE_REVIEWER,
74        description="審查程式碼品質和最佳實踐",
75        core_skills=["程式碼分析", "問題識別", "改進建議"],
76        tools_needed=["read_file"],
77        typical_output="審查報告和建議",
78        recommended_model="claude-3-5-haiku-20241022",  # 輕量任務用 Haiku
79        max_system_tokens=2000
80    ),
81
82    AgentRole.ORCHESTRATOR: AgentCapability(
83        role=AgentRole.ORCHESTRATOR,
84        description="協調多個 Agent 完成複雜任務",
85        core_skills=["任務分解", "Agent 調度", "結果整合"],
86        tools_needed=["delegate_task"],
87        typical_output="整合後的最終結果",
88        recommended_model="claude-sonnet-4-20250514",
89        max_system_tokens=2000
90    ),
91}

策略一:System Prompt 精簡化

設計最小化的 System Prompt

  1import anthropic
  2from dataclasses import dataclass
  3from typing import Optional
  4
  5client = anthropic.Anthropic()
  6
  7@dataclass
  8class MinimalSystemPrompt:
  9    """最小化 System Prompt 結構"""
 10    role_definition: str       # 角色定義(必要)
 11    core_responsibility: str   # 核心職責(必要)
 12    output_format: str         # 輸出格式(必要)
 13    constraints: list[str]     # 限制條件(可選)
 14    examples: Optional[str] = None  # 範例(僅複雜任務需要)
 15
 16    def build(self) -> str:
 17        """建構最小化的 System Prompt"""
 18        parts = [
 19            f"# 角色\n{self.role_definition}",
 20            f"\n# 職責\n{self.core_responsibility}",
 21            f"\n# 輸出格式\n{self.output_format}",
 22        ]
 23
 24        if self.constraints:
 25            constraints_text = "\n".join(f"- {c}" for c in self.constraints)
 26            parts.append(f"\n# 限制\n{constraints_text}")
 27
 28        if self.examples:
 29            parts.append(f"\n# 範例\n{self.examples}")
 30
 31        return "\n".join(parts)
 32
 33    def token_estimate(self) -> int:
 34        """估算 token 數"""
 35        return len(self.build()) // 3
 36
 37
 38class SpecializedAgentFactory:
 39    """專責 Agent 工廠"""
 40
 41    # 預定義的精簡 System Prompts
 42    SPECIALIZED_PROMPTS = {
 43        AgentRole.REQUIREMENTS_ANALYST: MinimalSystemPrompt(
 44            role_definition="你是需求分析師,專精於理解和結構化使用者需求。",
 45            core_responsibility="""從使用者描述中提取:
 461. 功能需求(必須有的功能)
 472. 非功能需求(效能、安全、可用性)
 483. 限制條件
 494. 驗收標準""",
 50            output_format="""以 JSON 格式輸出:
 51{
 52    "functional_requirements": [...],
 53    "non_functional_requirements": [...],
 54    "constraints": [...],
 55    "acceptance_criteria": [...]
 56}""",
 57            constraints=[
 58                "只分析需求,不提供實作方案",
 59                "保持客觀,不加入假設",
 60                "不確定的需求標記為 [待確認]"
 61            ]
 62        ),
 63
 64        AgentRole.BACKEND_DEVELOPER: MinimalSystemPrompt(
 65            role_definition="你是後端開發者,專精於 Python/FastAPI 開發。",
 66            core_responsibility="根據需求和設計實作乾淨、可維護的後端程式碼。",
 67            output_format="""輸出完整可執行的程式碼:
 681. 必要的 import
 692. 完整的實作
 703. 基本的錯誤處理
 714. 簡短的使用說明""",
 72            constraints=[
 73                "遵循 PEP 8 風格",
 74                "函數單一職責",
 75                "加入類型提示"
 76            ]
 77        ),
 78
 79        AgentRole.CODE_REVIEWER: MinimalSystemPrompt(
 80            role_definition="你是程式碼審查員,專注於發現問題和提供改進建議。",
 81            core_responsibility="""審查程式碼的:
 821. 正確性
 832. 可讀性
 843. 效能
 854. 安全性""",
 86            output_format="""以列表形式輸出:
 87- 🔴 嚴重問題:[問題描述和建議]
 88- 🟡 建議改進:[改進點和原因]
 89- 🟢 做得好的地方:[肯定的部分]""",
 90            constraints=[
 91                "只審查,不修改程式碼",
 92                "每個問題都要有具體建議",
 93                "按嚴重程度排序"
 94            ]
 95        ),
 96
 97        AgentRole.DOC_WRITER: MinimalSystemPrompt(
 98            role_definition="你是技術文件撰寫者,專精於清晰易懂的文件。",
 99            core_responsibility="根據程式碼和設計撰寫技術文件。",
100            output_format="""Markdown 格式,包含:
1011. 概述
1022. 安裝/使用方法
1033. API 說明
1044. 範例""",
105            constraints=[
106                "使用簡潔清晰的語言",
107                "包含程式碼範例",
108                "面向目標讀者撰寫"
109            ]
110        ),
111
112        AgentRole.ORCHESTRATOR: MinimalSystemPrompt(
113            role_definition="你是任務協調者,負責分解任務並協調多個專家完成。",
114            core_responsibility="""分析任務後:
1151. 分解為子任務
1162. 指派給合適的專家
1173. 整合各專家的輸出""",
118            output_format="""JSON 格式的執行計畫:
119{
120    "subtasks": [
121        {"id": 1, "description": "...", "assigned_to": "agent_type"},
122        ...
123    ],
124    "execution_order": [1, 2, 3],
125    "dependencies": {"2": [1], "3": [1, 2]}
126}""",
127            constraints=[
128                "最小化子任務數量",
129                "明確定義依賴關係",
130                "每個子任務單一職責"
131            ]
132        ),
133    }
134
135    @classmethod
136    def create_agent(
137        cls,
138        role: AgentRole,
139        custom_constraints: Optional[list[str]] = None
140    ) -> "SpecializedAgent":
141        """創建專責 Agent"""
142        base_prompt = cls.SPECIALIZED_PROMPTS.get(role)
143        capability = AGENT_CAPABILITIES.get(role)
144
145        if not base_prompt or not capability:
146            raise ValueError(f"未定義的角色: {role}")
147
148        # 合併自訂限制
149        if custom_constraints:
150            prompt = MinimalSystemPrompt(
151                role_definition=base_prompt.role_definition,
152                core_responsibility=base_prompt.core_responsibility,
153                output_format=base_prompt.output_format,
154                constraints=base_prompt.constraints + custom_constraints,
155                examples=base_prompt.examples
156            )
157        else:
158            prompt = base_prompt
159
160        return SpecializedAgent(
161            role=role,
162            system_prompt=prompt.build(),
163            model=capability.recommended_model,
164            tools=capability.tools_needed
165        )
166
167
168class SpecializedAgent:
169    """專責 Agent"""
170
171    def __init__(
172        self,
173        role: AgentRole,
174        system_prompt: str,
175        model: str,
176        tools: list[str]
177    ):
178        self.role = role
179        self.system_prompt = system_prompt
180        self.model = model
181        self.tools = tools
182        self._stats = {
183            "calls": 0,
184            "total_input_tokens": 0,
185            "total_output_tokens": 0
186        }
187
188    def execute(self, task: str, context: str = "") -> str:
189        """執行任務"""
190        full_task = f"{context}\n\n任務:{task}" if context else task
191
192        response = client.messages.create(
193            model=self.model,
194            max_tokens=4096,
195            system=self.system_prompt,
196            messages=[{"role": "user", "content": full_task}]
197        )
198
199        # 更新統計
200        self._stats["calls"] += 1
201        self._stats["total_input_tokens"] += response.usage.input_tokens
202        self._stats["total_output_tokens"] += response.usage.output_tokens
203
204        return response.content[0].text
205
206    def get_stats(self) -> dict:
207        return {
208            **self._stats,
209            "system_prompt_tokens": len(self.system_prompt) // 3,
210            "model": self.model,
211            "tools_count": len(self.tools)
212        }
213
214
215# 使用範例
216if __name__ == "__main__":
217    # 創建專責 Agents
218    analyst = SpecializedAgentFactory.create_agent(AgentRole.REQUIREMENTS_ANALYST)
219    developer = SpecializedAgentFactory.create_agent(AgentRole.BACKEND_DEVELOPER)
220    reviewer = SpecializedAgentFactory.create_agent(AgentRole.CODE_REVIEWER)
221
222    print("專責 Agent System Prompt 大小:")
223    print(f"  需求分析師: ~{analyst.get_stats()['system_prompt_tokens']} tokens")
224    print(f"  後端開發者: ~{developer.get_stats()['system_prompt_tokens']} tokens")
225    print(f"  程式碼審查: ~{reviewer.get_stats()['system_prompt_tokens']} tokens")
226
227    # 對比通用 Agent
228    general_prompt_tokens = 16000
229    specialized_avg = (
230        analyst.get_stats()['system_prompt_tokens'] +
231        developer.get_stats()['system_prompt_tokens'] +
232        reviewer.get_stats()['system_prompt_tokens']
233    ) / 3
234
235    print(f"\n對比通用 Agent ({general_prompt_tokens} tokens):")
236    print(f"  專責 Agent 平均: ~{specialized_avg:.0f} tokens")
237    print(f"  節省: {(1 - specialized_avg/general_prompt_tokens) * 100:.1f}%")

策略二:工具最小化配置

每個 Agent 只配置必要的工具

  1from typing import Any
  2
  3# 完整的工具定義庫
  4TOOL_DEFINITIONS = {
  5    "read_file": {
  6        "name": "read_file",
  7        "description": "讀取檔案內容",
  8        "input_schema": {
  9            "type": "object",
 10            "properties": {
 11                "path": {"type": "string", "description": "檔案路徑"}
 12            },
 13            "required": ["path"]
 14        }
 15    },
 16    "write_file": {
 17        "name": "write_file",
 18        "description": "寫入檔案",
 19        "input_schema": {
 20            "type": "object",
 21            "properties": {
 22                "path": {"type": "string"},
 23                "content": {"type": "string"}
 24            },
 25            "required": ["path", "content"]
 26        }
 27    },
 28    "run_command": {
 29        "name": "run_command",
 30        "description": "執行系統命令",
 31        "input_schema": {
 32            "type": "object",
 33            "properties": {
 34                "command": {"type": "string"}
 35            },
 36            "required": ["command"]
 37        }
 38    },
 39    "query_database": {
 40        "name": "query_database",
 41        "description": "執行資料庫查詢",
 42        "input_schema": {
 43            "type": "object",
 44            "properties": {
 45                "query": {"type": "string"},
 46                "database": {"type": "string"}
 47            },
 48            "required": ["query"]
 49        }
 50    },
 51    "search_web": {
 52        "name": "search_web",
 53        "description": "搜尋網頁資訊",
 54        "input_schema": {
 55            "type": "object",
 56            "properties": {
 57                "query": {"type": "string"}
 58            },
 59            "required": ["query"]
 60        }
 61    },
 62    "delegate_task": {
 63        "name": "delegate_task",
 64        "description": "將任務委派給其他 Agent",
 65        "input_schema": {
 66            "type": "object",
 67            "properties": {
 68                "agent_type": {"type": "string"},
 69                "task": {"type": "string"},
 70                "context": {"type": "string"}
 71            },
 72            "required": ["agent_type", "task"]
 73        }
 74    },
 75    "analyze_code": {
 76        "name": "analyze_code",
 77        "description": "分析程式碼結構和依賴",
 78        "input_schema": {
 79            "type": "object",
 80            "properties": {
 81                "code": {"type": "string"},
 82                "language": {"type": "string"}
 83            },
 84            "required": ["code"]
 85        }
 86    },
 87    "run_tests": {
 88        "name": "run_tests",
 89        "description": "執行測試套件",
 90        "input_schema": {
 91            "type": "object",
 92            "properties": {
 93                "test_path": {"type": "string"},
 94                "coverage": {"type": "boolean"}
 95            },
 96            "required": ["test_path"]
 97        }
 98    }
 99}
100
101# Agent 角色到工具的映射(最小化配置)
102ROLE_TOOL_MAPPING = {
103    AgentRole.REQUIREMENTS_ANALYST: [
104        "read_file",  # 讀取需求文件
105    ],
106    AgentRole.DATA_ANALYST: [
107        "read_file",
108        "query_database",
109    ],
110    AgentRole.ARCHITECT: [
111        "read_file",
112        "search_web",  # 搜尋最佳實踐
113    ],
114    AgentRole.BACKEND_DEVELOPER: [
115        "read_file",
116        "write_file",
117        "run_command",
118    ],
119    AgentRole.FRONTEND_DEVELOPER: [
120        "read_file",
121        "write_file",
122        "run_command",
123    ],
124    AgentRole.CODE_REVIEWER: [
125        "read_file",  # 只需要讀取,不需要寫入
126        "analyze_code",
127    ],
128    AgentRole.SECURITY_REVIEWER: [
129        "read_file",
130        "analyze_code",
131    ],
132    AgentRole.DOC_WRITER: [
133        "read_file",
134        "write_file",
135    ],
136    AgentRole.TEST_DESIGNER: [
137        "read_file",
138    ],
139    AgentRole.TEST_IMPLEMENTER: [
140        "read_file",
141        "write_file",
142        "run_tests",
143    ],
144    AgentRole.ORCHESTRATOR: [
145        "delegate_task",
146    ],
147}
148
149
150class MinimalToolConfigurator:
151    """最小化工具配置器"""
152
153    def __init__(self):
154        self.tool_definitions = TOOL_DEFINITIONS
155        self.role_mapping = ROLE_TOOL_MAPPING
156
157    def get_tools_for_role(self, role: AgentRole) -> list[dict]:
158        """取得角色所需的最小工具集"""
159        tool_names = self.role_mapping.get(role, [])
160        return [
161            self.tool_definitions[name]
162            for name in tool_names
163            if name in self.tool_definitions
164        ]
165
166    def estimate_tools_tokens(self, role: AgentRole) -> int:
167        """估算工具定義的 token 數"""
168        import json
169        tools = self.get_tools_for_role(role)
170        tools_json = json.dumps(tools, ensure_ascii=False)
171        return len(tools_json) // 3
172
173    def compare_tool_overhead(self) -> dict:
174        """比較不同角色的工具開銷"""
175        # 計算全部工具的開銷
176        import json
177        all_tools_tokens = len(json.dumps(list(self.tool_definitions.values()), ensure_ascii=False)) // 3
178
179        results = {
180            "all_tools_tokens": all_tools_tokens,
181            "role_tools": {}
182        }
183
184        for role in AgentRole:
185            if role in self.role_mapping:
186                tokens = self.estimate_tools_tokens(role)
187                results["role_tools"][role.value] = {
188                    "tokens": tokens,
189                    "tools_count": len(self.role_mapping[role]),
190                    "savings_pct": f"{(1 - tokens/all_tools_tokens) * 100:.1f}%"
191                }
192
193        return results
194
195
196class ToolMinimizedAgent:
197    """工具最小化的 Agent"""
198
199    def __init__(
200        self,
201        role: AgentRole,
202        system_prompt: str,
203        model: str = "claude-sonnet-4-20250514"
204    ):
205        self.role = role
206        self.system_prompt = system_prompt
207        self.model = model
208
209        # 只配置必要的工具
210        configurator = MinimalToolConfigurator()
211        self.tools = configurator.get_tools_for_role(role)
212
213        self._tool_calls: list[dict] = []
214
215    def _handle_tool_call(self, tool_name: str, tool_input: dict) -> str:
216        """處理工具呼叫(示範實作)"""
217        # 實際應用中,這裡會呼叫真正的工具
218        self._tool_calls.append({"name": tool_name, "input": tool_input})
219
220        if tool_name == "read_file":
221            return f"[模擬] 讀取檔案 {tool_input.get('path')} 的內容..."
222        elif tool_name == "write_file":
223            return f"[模擬] 已寫入 {tool_input.get('path')}"
224        elif tool_name == "run_command":
225            return f"[模擬] 執行命令: {tool_input.get('command')}"
226        else:
227            return f"[模擬] 工具 {tool_name} 執行完成"
228
229    def execute(self, task: str, context: str = "") -> str:
230        """執行任務(支援工具呼叫)"""
231        messages = []
232
233        if context:
234            messages.append({"role": "user", "content": f"{context}\n\n任務:{task}"})
235        else:
236            messages.append({"role": "user", "content": task})
237
238        # 迭代處理工具呼叫
239        while True:
240            response = client.messages.create(
241                model=self.model,
242                max_tokens=4096,
243                system=self.system_prompt,
244                tools=self.tools if self.tools else None,
245                messages=messages
246            )
247
248            # 檢查是否有工具呼叫
249            tool_use_blocks = [
250                block for block in response.content
251                if block.type == "tool_use"
252            ]
253
254            if not tool_use_blocks:
255                # 沒有工具呼叫,返回文字回應
256                text_blocks = [
257                    block.text for block in response.content
258                    if hasattr(block, "text")
259                ]
260                return "\n".join(text_blocks)
261
262            # 處理工具呼叫
263            messages.append({"role": "assistant", "content": response.content})
264
265            tool_results = []
266            for tool_block in tool_use_blocks:
267                result = self._handle_tool_call(tool_block.name, tool_block.input)
268                tool_results.append({
269                    "type": "tool_result",
270                    "tool_use_id": tool_block.id,
271                    "content": result
272                })
273
274            messages.append({"role": "user", "content": tool_results})
275
276    def get_tool_stats(self) -> dict:
277        """取得工具使用統計"""
278        return {
279            "configured_tools": len(self.tools),
280            "tool_calls_made": len(self._tool_calls),
281            "tools_used": list(set(tc["name"] for tc in self._tool_calls))
282        }
283
284
285# 使用範例
286if __name__ == "__main__":
287    configurator = MinimalToolConfigurator()
288    comparison = configurator.compare_tool_overhead()
289
290    print("工具配置對比:")
291    print(f"全部工具: {comparison['all_tools_tokens']} tokens\n")
292
293    for role, stats in comparison["role_tools"].items():
294        print(f"{role}:")
295        print(f"  工具數: {stats['tools_count']}")
296        print(f"  Tokens: {stats['tokens']}")
297        print(f"  節省: {stats['savings_pct']}")

策略三:模型差異化選擇

根據任務複雜度選擇模型

  1from enum import Enum
  2from dataclasses import dataclass
  3
  4class ModelTier(Enum):
  5    """模型層級"""
  6    FAST = "fast"       # Haiku - 快速便宜
  7    BALANCED = "balanced"  # Sonnet - 平衡
  8    POWERFUL = "powerful"  # Opus - 最強
  9
 10@dataclass
 11class ModelConfig:
 12    """模型配置"""
 13    tier: ModelTier
 14    model_id: str
 15    input_cost_per_1m: float
 16    output_cost_per_1m: float
 17    best_for: list[str]
 18
 19MODEL_CONFIGS = {
 20    ModelTier.FAST: ModelConfig(
 21        tier=ModelTier.FAST,
 22        model_id="claude-3-5-haiku-20241022",
 23        input_cost_per_1m=0.80,
 24        output_cost_per_1m=4.0,
 25        best_for=["分類", "摘要", "格式化", "簡單審查", "路由決策"]
 26    ),
 27    ModelTier.BALANCED: ModelConfig(
 28        tier=ModelTier.BALANCED,
 29        model_id="claude-sonnet-4-20250514",
 30        input_cost_per_1m=3.0,
 31        output_cost_per_1m=15.0,
 32        best_for=["程式碼生成", "分析", "一般任務", "協調"]
 33    ),
 34    ModelTier.POWERFUL: ModelConfig(
 35        tier=ModelTier.POWERFUL,
 36        model_id="claude-opus-4-20250514",
 37        input_cost_per_1m=15.0,
 38        output_cost_per_1m=75.0,
 39        best_for=["複雜架構", "創意設計", "深度分析", "關鍵決策"]
 40    ),
 41}
 42
 43# Agent 角色到模型層級的映射
 44ROLE_MODEL_MAPPING = {
 45    # 快速模型 - 簡單任務
 46    AgentRole.CODE_REVIEWER: ModelTier.FAST,
 47    AgentRole.DOC_WRITER: ModelTier.FAST,
 48    AgentRole.TEST_DESIGNER: ModelTier.FAST,
 49    AgentRole.TASK_ROUTER: ModelTier.FAST,
 50
 51    # 平衡模型 - 一般任務
 52    AgentRole.REQUIREMENTS_ANALYST: ModelTier.BALANCED,
 53    AgentRole.BACKEND_DEVELOPER: ModelTier.BALANCED,
 54    AgentRole.FRONTEND_DEVELOPER: ModelTier.BALANCED,
 55    AgentRole.DATA_ANALYST: ModelTier.BALANCED,
 56    AgentRole.ORCHESTRATOR: ModelTier.BALANCED,
 57    AgentRole.TEST_IMPLEMENTER: ModelTier.BALANCED,
 58
 59    # 強力模型 - 複雜任務
 60    AgentRole.ARCHITECT: ModelTier.POWERFUL,
 61    AgentRole.SECURITY_ANALYST: ModelTier.POWERFUL,
 62    AgentRole.SECURITY_REVIEWER: ModelTier.POWERFUL,
 63}
 64
 65
 66class ModelOptimizedAgentSystem:
 67    """模型優化的 Agent 系統"""
 68
 69    def __init__(self):
 70        self.agents: dict[AgentRole, SpecializedAgent] = {}
 71        self._cost_tracker = {
 72            "total_input_tokens": 0,
 73            "total_output_tokens": 0,
 74            "total_cost": 0.0,
 75            "by_tier": {tier: {"tokens": 0, "cost": 0.0} for tier in ModelTier}
 76        }
 77
 78    def get_model_for_role(self, role: AgentRole) -> str:
 79        """取得角色對應的模型"""
 80        tier = ROLE_MODEL_MAPPING.get(role, ModelTier.BALANCED)
 81        return MODEL_CONFIGS[tier].model_id
 82
 83    def get_model_tier(self, role: AgentRole) -> ModelTier:
 84        """取得角色對應的模型層級"""
 85        return ROLE_MODEL_MAPPING.get(role, ModelTier.BALANCED)
 86
 87    def register_agent(self, role: AgentRole, system_prompt: str):
 88        """註冊 Agent"""
 89        model = self.get_model_for_role(role)
 90
 91        # 取得最小化工具
 92        configurator = MinimalToolConfigurator()
 93        tools = configurator.get_tools_for_role(role)
 94
 95        self.agents[role] = SpecializedAgent(
 96            role=role,
 97            system_prompt=system_prompt,
 98            model=model,
 99            tools=[t["name"] for t in tools]
100        )
101
102    def execute(self, role: AgentRole, task: str, context: str = "") -> str:
103        """執行任務並追蹤成本"""
104        if role not in self.agents:
105            raise ValueError(f"Agent {role} 未註冊")
106
107        agent = self.agents[role]
108        tier = self.get_model_tier(role)
109        config = MODEL_CONFIGS[tier]
110
111        # 執行
112        result = agent.execute(task, context)
113
114        # 追蹤成本
115        stats = agent.get_stats()
116        input_tokens = stats["total_input_tokens"]
117        output_tokens = stats["total_output_tokens"]
118
119        cost = (
120            input_tokens * config.input_cost_per_1m / 1_000_000 +
121            output_tokens * config.output_cost_per_1m / 1_000_000
122        )
123
124        self._cost_tracker["total_input_tokens"] += input_tokens
125        self._cost_tracker["total_output_tokens"] += output_tokens
126        self._cost_tracker["total_cost"] += cost
127        self._cost_tracker["by_tier"][tier]["tokens"] += input_tokens + output_tokens
128        self._cost_tracker["by_tier"][tier]["cost"] += cost
129
130        return result
131
132    def get_cost_report(self) -> dict:
133        """取得成本報告"""
134        return {
135            "total_tokens": self._cost_tracker["total_input_tokens"] + self._cost_tracker["total_output_tokens"],
136            "total_cost": f"${self._cost_tracker['total_cost']:.4f}",
137            "by_tier": {
138                tier.value: {
139                    "tokens": stats["tokens"],
140                    "cost": f"${stats['cost']:.4f}"
141                }
142                for tier, stats in self._cost_tracker["by_tier"].items()
143            }
144        }
145
146    def estimate_savings_vs_single_model(self) -> dict:
147        """估算相比單一模型的節省"""
148        total_tokens = (
149            self._cost_tracker["total_input_tokens"] +
150            self._cost_tracker["total_output_tokens"]
151        )
152
153        if total_tokens == 0:
154            return {"error": "尚無執行記錄"}
155
156        # 假設全部使用各層級模型的成本
157        all_opus_cost = total_tokens * (15.0 + 75.0) / 2 / 1_000_000
158        all_sonnet_cost = total_tokens * (3.0 + 15.0) / 2 / 1_000_000
159        all_haiku_cost = total_tokens * (0.8 + 4.0) / 2 / 1_000_000
160
161        actual_cost = self._cost_tracker["total_cost"]
162
163        return {
164            "actual_cost": f"${actual_cost:.4f}",
165            "if_all_opus": f"${all_opus_cost:.4f}",
166            "if_all_sonnet": f"${all_sonnet_cost:.4f}",
167            "if_all_haiku": f"${all_haiku_cost:.4f}",
168            "savings_vs_opus": f"{(1 - actual_cost/all_opus_cost) * 100:.1f}%" if all_opus_cost > 0 else "N/A",
169            "savings_vs_sonnet": f"{(1 - actual_cost/all_sonnet_cost) * 100:.1f}%" if all_sonnet_cost > 0 else "N/A",
170        }

策略四:完整的專責化系統

整合所有策略的生產級系統

  1import anthropic
  2from dataclasses import dataclass, field
  3from typing import Optional, Any, Callable
  4from enum import Enum
  5import json
  6import time
  7
  8client = anthropic.Anthropic()
  9
 10@dataclass
 11class AgentExecutionResult:
 12    """Agent 執行結果"""
 13    role: AgentRole
 14    output: str
 15    input_tokens: int
 16    output_tokens: int
 17    model_used: str
 18    execution_time_ms: float
 19    cost: float
 20    success: bool
 21    error: Optional[str] = None
 22
 23@dataclass
 24class SpecializedAgentConfig:
 25    """專責 Agent 配置"""
 26    role: AgentRole
 27    system_prompt: str
 28    model_tier: ModelTier
 29    tools: list[str]
 30    max_tokens: int = 4096
 31
 32    # 可選的自訂處理器
 33    output_parser: Optional[Callable[[str], Any]] = None
 34    pre_processor: Optional[Callable[[str], str]] = None
 35
 36
 37class SpecializedAgentOrchestrator:
 38    """
 39    專責化 Agent 協調系統
 40
 41    整合:
 42    1. 最小化 System Prompt
 43    2. 最小化工具配置
 44    3. 模型差異化選擇
 45    4. 成本追蹤
 46    """
 47
 48    def __init__(self):
 49        self.agents: dict[AgentRole, SpecializedAgentConfig] = {}
 50        self.tool_configurator = MinimalToolConfigurator()
 51
 52        self._execution_history: list[AgentExecutionResult] = []
 53        self._stats = {
 54            "total_executions": 0,
 55            "total_tokens": 0,
 56            "total_cost": 0.0,
 57            "by_role": {},
 58            "by_model": {}
 59        }
 60
 61    def register_agent(self, config: SpecializedAgentConfig):
 62        """註冊專責 Agent"""
 63        self.agents[config.role] = config
 64        self._stats["by_role"][config.role.value] = {
 65            "executions": 0,
 66            "tokens": 0,
 67            "cost": 0.0
 68        }
 69
 70    def register_from_factory(self, role: AgentRole, custom_constraints: list[str] = None):
 71        """從工廠註冊 Agent"""
 72        # 取得預定義的 System Prompt
 73        base_prompt = SpecializedAgentFactory.SPECIALIZED_PROMPTS.get(role)
 74        if not base_prompt:
 75            raise ValueError(f"未定義角色 {role} 的 System Prompt")
 76
 77        # 建構 System Prompt
 78        if custom_constraints:
 79            prompt = MinimalSystemPrompt(
 80                role_definition=base_prompt.role_definition,
 81                core_responsibility=base_prompt.core_responsibility,
 82                output_format=base_prompt.output_format,
 83                constraints=base_prompt.constraints + custom_constraints,
 84                examples=base_prompt.examples
 85            )
 86        else:
 87            prompt = base_prompt
 88
 89        # 取得模型和工具配置
 90        model_tier = ROLE_MODEL_MAPPING.get(role, ModelTier.BALANCED)
 91        tools = [t["name"] for t in self.tool_configurator.get_tools_for_role(role)]
 92
 93        config = SpecializedAgentConfig(
 94            role=role,
 95            system_prompt=prompt.build(),
 96            model_tier=model_tier,
 97            tools=tools
 98        )
 99
100        self.register_agent(config)
101
102    def _get_model_id(self, tier: ModelTier) -> str:
103        """取得模型 ID"""
104        return MODEL_CONFIGS[tier].model_id
105
106    def _calculate_cost(self, tier: ModelTier, input_tokens: int, output_tokens: int) -> float:
107        """計算成本"""
108        config = MODEL_CONFIGS[tier]
109        return (
110            input_tokens * config.input_cost_per_1m / 1_000_000 +
111            output_tokens * config.output_cost_per_1m / 1_000_000
112        )
113
114    def execute_agent(
115        self,
116        role: AgentRole,
117        task: str,
118        context: str = ""
119    ) -> AgentExecutionResult:
120        """執行單個專責 Agent"""
121        if role not in self.agents:
122            return AgentExecutionResult(
123                role=role,
124                output="",
125                input_tokens=0,
126                output_tokens=0,
127                model_used="",
128                execution_time_ms=0,
129                cost=0,
130                success=False,
131                error=f"Agent {role.value} 未註冊"
132            )
133
134        config = self.agents[role]
135        model_id = self._get_model_id(config.model_tier)
136
137        # 預處理
138        if config.pre_processor:
139            task = config.pre_processor(task)
140
141        # 組合輸入
142        full_task = f"{context}\n\n任務:{task}" if context else task
143
144        # 取得工具定義
145        tools = self.tool_configurator.get_tools_for_role(role) if config.tools else None
146
147        start_time = time.time()
148
149        try:
150            kwargs = {
151                "model": model_id,
152                "max_tokens": config.max_tokens,
153                "system": config.system_prompt,
154                "messages": [{"role": "user", "content": full_task}]
155            }
156
157            if tools:
158                kwargs["tools"] = tools
159
160            response = client.messages.create(**kwargs)
161
162            # 提取文字輸出
163            text_output = "\n".join(
164                block.text for block in response.content
165                if hasattr(block, "text")
166            )
167
168            # 後處理
169            if config.output_parser:
170                text_output = config.output_parser(text_output)
171
172            execution_time = (time.time() - start_time) * 1000
173            cost = self._calculate_cost(
174                config.model_tier,
175                response.usage.input_tokens,
176                response.usage.output_tokens
177            )
178
179            result = AgentExecutionResult(
180                role=role,
181                output=text_output,
182                input_tokens=response.usage.input_tokens,
183                output_tokens=response.usage.output_tokens,
184                model_used=model_id,
185                execution_time_ms=execution_time,
186                cost=cost,
187                success=True
188            )
189
190        except Exception as e:
191            result = AgentExecutionResult(
192                role=role,
193                output="",
194                input_tokens=0,
195                output_tokens=0,
196                model_used=model_id,
197                execution_time_ms=(time.time() - start_time) * 1000,
198                cost=0,
199                success=False,
200                error=str(e)
201            )
202
203        # 更新統計
204        self._update_stats(result, config.model_tier)
205        self._execution_history.append(result)
206
207        return result
208
209    def _update_stats(self, result: AgentExecutionResult, tier: ModelTier):
210        """更新統計"""
211        self._stats["total_executions"] += 1
212        tokens = result.input_tokens + result.output_tokens
213        self._stats["total_tokens"] += tokens
214        self._stats["total_cost"] += result.cost
215
216        # 按角色
217        role_key = result.role.value
218        if role_key in self._stats["by_role"]:
219            self._stats["by_role"][role_key]["executions"] += 1
220            self._stats["by_role"][role_key]["tokens"] += tokens
221            self._stats["by_role"][role_key]["cost"] += result.cost
222
223        # 按模型
224        tier_key = tier.value
225        if tier_key not in self._stats["by_model"]:
226            self._stats["by_model"][tier_key] = {"executions": 0, "tokens": 0, "cost": 0.0}
227        self._stats["by_model"][tier_key]["executions"] += 1
228        self._stats["by_model"][tier_key]["tokens"] += tokens
229        self._stats["by_model"][tier_key]["cost"] += result.cost
230
231    def execute_pipeline(
232        self,
233        task: str,
234        pipeline: list[AgentRole],
235        context_builder: Optional[Callable[[list[AgentExecutionResult]], str]] = None
236    ) -> list[AgentExecutionResult]:
237        """
238        執行 Agent 管線
239
240        Args:
241            task: 原始任務
242            pipeline: Agent 執行順序
243            context_builder: 自訂 Context 建構器
244
245        Returns:
246            所有執行結果
247        """
248        results = []
249
250        for i, role in enumerate(pipeline):
251            print(f"\n{'='*50}")
252            print(f"Step {i+1}/{len(pipeline)}: {role.value}")
253            print(f"{'='*50}")
254
255            # 建構 Context
256            if context_builder and results:
257                context = context_builder(results)
258            elif results:
259                # 預設:使用上一個 Agent 的輸出摘要
260                last_result = results[-1]
261                context = f"[上一步 {last_result.role.value} 的輸出]\n{last_result.output[:2000]}..."
262            else:
263                context = ""
264
265            # 執行
266            result = self.execute_agent(role, task, context)
267            results.append(result)
268
269            # 輸出狀態
270            if result.success:
271                print(f"✅ 成功")
272                print(f"   模型: {result.model_used}")
273                print(f"   Tokens: {result.input_tokens + result.output_tokens}")
274                print(f"   成本: ${result.cost:.4f}")
275                print(f"   時間: {result.execution_time_ms:.0f}ms")
276            else:
277                print(f"❌ 失敗: {result.error}")
278
279        return results
280
281    def get_stats(self) -> dict:
282        """取得統計"""
283        return {
284            **self._stats,
285            "avg_tokens_per_execution": (
286                self._stats["total_tokens"] / self._stats["total_executions"]
287                if self._stats["total_executions"] > 0 else 0
288            ),
289            "avg_cost_per_execution": (
290                self._stats["total_cost"] / self._stats["total_executions"]
291                if self._stats["total_executions"] > 0 else 0
292            )
293        }
294
295    def get_optimization_report(self) -> str:
296        """取得優化報告"""
297        lines = [
298            "="*60,
299            "專責化 Agent 系統優化報告",
300            "="*60,
301            "",
302            f"總執行次數: {self._stats['total_executions']}",
303            f"總 Token 使用: {self._stats['total_tokens']:,}",
304            f"總成本: ${self._stats['total_cost']:.4f}",
305            "",
306            "按模型層級分布:",
307        ]
308
309        for tier, stats in self._stats["by_model"].items():
310            pct = stats["tokens"] / self._stats["total_tokens"] * 100 if self._stats["total_tokens"] > 0 else 0
311            lines.append(f"  {tier}: {stats['tokens']:,} tokens ({pct:.1f}%), ${stats['cost']:.4f}")
312
313        lines.extend([
314            "",
315            "按角色分布:",
316        ])
317
318        for role, stats in self._stats["by_role"].items():
319            if stats["executions"] > 0:
320                lines.append(f"  {role}: {stats['executions']} 次, {stats['tokens']:,} tokens, ${stats['cost']:.4f}")
321
322        # 估算節省
323        if self._stats["total_tokens"] > 0:
324            # 假設全部使用 Opus
325            all_opus_cost = self._stats["total_tokens"] * (15.0 + 75.0) / 2 / 1_000_000
326            savings = (all_opus_cost - self._stats["total_cost"]) / all_opus_cost * 100
327
328            lines.extend([
329                "",
330                "成本優化效果:",
331                f"  若全部使用 Opus: ${all_opus_cost:.4f}",
332                f"  實際成本: ${self._stats['total_cost']:.4f}",
333                f"  節省: {savings:.1f}%"
334            ])
335
336        lines.append("="*60)
337        return "\n".join(lines)
338
339
340# 使用範例
341def create_development_team() -> SpecializedAgentOrchestrator:
342    """創建開發團隊"""
343    orchestrator = SpecializedAgentOrchestrator()
344
345    # 註冊各專責 Agent
346    roles = [
347        AgentRole.REQUIREMENTS_ANALYST,
348        AgentRole.ARCHITECT,
349        AgentRole.BACKEND_DEVELOPER,
350        AgentRole.CODE_REVIEWER,
351        AgentRole.DOC_WRITER,
352    ]
353
354    for role in roles:
355        try:
356            orchestrator.register_from_factory(role)
357            print(f"✅ 註冊 {role.value}")
358        except ValueError as e:
359            print(f"⚠️ 跳過 {role.value}: {e}")
360
361    return orchestrator
362
363
364if __name__ == "__main__":
365    # 創建專責團隊
366    team = create_development_team()
367
368    # 定義開發任務
369    task = """
370    開發一個簡單的 URL 縮短服務:
371    - 接受長 URL,返回短碼
372    - 支援重定向
373    - 記錄訪問統計
374    """
375
376    # 執行開發管線
377    pipeline = [
378        AgentRole.REQUIREMENTS_ANALYST,
379        AgentRole.BACKEND_DEVELOPER,
380        AgentRole.CODE_REVIEWER,
381        AgentRole.DOC_WRITER,
382    ]
383
384    results = team.execute_pipeline(task, pipeline)
385
386    # 輸出報告
387    print("\n")
388    print(team.get_optimization_report())

效能比較與分析

專責化 vs 通用 Agent 對比:

┌─────────────────────────────────────────────────────────────────────┐
│                    Token 使用量對比                                  │
├────────────────────┬───────────────┬───────────────┬────────────────┤
│ 項目               │ 通用 Agent    │ 專責 Agent    │ 節省           │
├────────────────────┼───────────────┼───────────────┼────────────────┤
│ System Prompt      │ 16,000 tok    │ 2,500 tok     │ 84%            │
│ 工具定義           │ 5,000 tok     │ 800 tok       │ 84%            │
│ 每次呼叫固定成本   │ 21,000 tok    │ 3,300 tok     │ 84%            │
├────────────────────┼───────────────┼───────────────┼────────────────┤
│ 5 次呼叫總固定成本 │ 105,000 tok   │ 16,500 tok    │ 84%            │
└────────────────────┴───────────────┴───────────────┴────────────────┘

成本計算(假設 Sonnet $3/1M input):

5 個任務的固定成本:
- 通用 Agent: 105,000 × $3 / 1M = $0.315
- 專責 Agent: 16,500 × $3 / 1M = $0.0495
- 節省: $0.2655 (84%)

模型差異化額外節省:
- 若 3/5 任務使用 Haiku: 再節省 ~60%
- 總節省: 90%+

最佳實踐清單

Agent 專責化 Checklist:

職責設計
□ 每個 Agent 是否只負責單一類型任務?
□ Agent 之間的職責邊界是否清晰?
□ 是否避免了職責重疊?

System Prompt 優化
□ System Prompt 是否精簡到必要最小?
□ 是否移除了不相關的說明和範例?
□ 是否使用了結構化的格式?

工具配置
□ 每個 Agent 是否只配置必要的工具?
□ 是否移除了不會使用的工具?
□ 工具定義是否精簡?

模型選擇
□ 簡單任務是否使用輕量模型(Haiku)?
□ 複雜任務是否使用強力模型(Opus)?
□ 是否根據實際效能調整模型選擇?

監控
□ 是否追蹤各 Agent 的 Token 使用?
□ 是否追蹤各模型層級的成本分布?
□ 是否有優化效果的量化指標?

總結

Agent 專責化是降低多 Agent 系統固定成本的核心策略。本文介紹的方案涵蓋:

優化面向策略預期節省
System Prompt精簡到必要最小70-85%
工具配置只配置必要工具60-80%
模型選擇任務匹配模型40-70%
綜合效果全面專責化80-95%

關鍵原則:

  1. 單一職責:每個 Agent 專注於一種任務類型
  2. 最小配置:只包含完成任務必要的 Prompt 和工具
  3. 模型匹配:簡單任務用便宜模型,複雜任務用強力模型
  4. 持續優化:根據實際使用數據調整配置

透過 Agent 專責化,你可以在保持系統功能的同時,將固定成本降低 80% 以上,打造真正高效的多 Agent 系統。

Yen

Yen

Yen