標籤: 多代理系統

  • 企業級 Agent Harness 七大能力實戰拆解

    企業級 Agent Harness 七大能力實戰拆解

    📌 本文重點

    • 單一 Agent PoC 聰明,上線即暴露治理問題
    • 需要 Agent Harness 統一管控工具、成本與政策
    • 先建可觀測、可回放的 Harness,再談多 Agent 擴展
    • 將治理與業務邏輯解耦,避免生產環境變實驗場

    企業在玩 PoC 時,單一 Agent 看起來很聰明;一上線就暴露本質問題:工具亂叫、成本失控、記憶混亂、錯誤難重現、政策無法落地。這不是模型不夠強,而是缺少一層專門管控 Agent 行為的 Agent Harness(Agent 的 runtime & control plane)。

    💡 關鍵: 問題不在模型本身,而在缺少專門負責治理、成本與行為管控的中介層。

    這一層做的事很務實:集中管控 工具註冊與版本 / 權限、step-level tracing + replay、記憶與知識更新、錯誤恢復與重試、成本與 Token Budget、政策注入(policy engine)、標準化的 human-in-the-loop 介面。有了它,你才真的能把多 Agent 系統放進生產環境,而不是「高級 demo」。


    重點說明:Agent Harness 的 7 大能力

    1. 工具 / 函式註冊與版本管理

    把所有可被 Agent 呼叫的 API/工具收斂到一個 工具註冊中心,統一管理:

    • 白名單 + Scope:不同 Agent 只能看到被授權的工具,避免「直接讓模型自由呼叫所有內部 API」。
    • 版本管理:工具有 version 與 deprecation 概念,支援灰度切換與回滾。
    • 結構化 schema:對應到 LLM 的 tool schema / function calling,並強制要求輸入輸出型別。

    2. 觀察、Tracing 與 Replay

    沒有 step-level tracing,任何「為什麼產生這個請款請求?」都變成佛系排查。Harness 要提供:

    • 每一步 prompt / tool call / response 的結構化 log。
    • replay 能力:拿同一套 input/context/tool version 在測試環境重放。
    • 與 APM/日誌系統整合(如 OpenTelemetry, Datadog)。

    💡 關鍵: 有了 step-level tracing 與 replay,才能在出錯時精準重現並修復 Agent 行為,而不是盲目排查。

    3. 記憶與知識源更新

    多數系統踩的坑是「把 RAG/記憶全塞進 context」,結果是:

    • token 爆炸 → 成本高、延遲高
    • 資訊新鮮度難管控

    Harness 要把記憶拆成:

    • 短期工作記憶(task-local state):存在 run/session 中。
    • 長期記憶(user / account profile, conversation history):向量庫或 KV 存儲,按策略查再放入 context。
    • 權威知識源(DB / data warehouse / API):由工具抽象,不直接塞原始資料進 prompt。

    4. 錯誤恢復與重試策略

    Agent 在實際環境裡會遇到:工具 5xx、timeout、schema mismatch、LLM 回傳壞 JSON 等。Harness 要集中:

    • 可配置重試策略(per tool 的 max_attempts、backoff)。
    • 降級策略:改用備援工具、或中止並要求人工介入。
    • 把錯誤與重試記錄在 tracing 中,方便事後分析。

    5. 成本與 Token Budget 控制

    程式化 Agent(CI、排程、webhook 觸發)最容易「安靜地燒錢」。Harness 應實作:

    • per-run token budget:單一任務總 token / cost 上限。
    • per-tool cost 上限:避免某個向量搜尋或報表 API 被無限 loop 呼叫。
    • 依租戶 / team 維度聚合成本,餵到帳務系統或告警系統。

    💡 關鍵: 透過 per-run 與 per-tool 的 token 與成本上限,可以在不影響功能的前提下,防止自動化流程悄悄造成巨額開銷。

    6. 策略 / 合規政策注入(Policy Engine)

    只在 prompt 放幾條「不要外流 PII」是不夠的。企業需要一個 policy engine:

    • 針對工具呼叫、輸入輸出內容,做 策略判斷與拒絕。
    • 支援條件式規則,如「財務資料工具只能在工作時間、由 Finance-Agent 使用」。
    • 配合零信任閘道(如 SolonGate 類產品)做額外的身分驗證與審計。

    7. Human-in-the-loop 標準化接口

    有些動作永遠不該全自動(退款、合約變更…)。Harness 要提供:

    • 標準化的 approval 任務結構:包含 who, what, diff, risks。
    • 挂在 ticket 系統 / 內部前端(如模仿 LangGraph 的 human node)。
    • Agent 在收到人類決策後能繼續流程,而不是整個 run 重來。

    實作範例:最小可用 Agent Harness(Python)

    以下是簡化版的「Agent 執行器」,示範:

    • 工具註冊 + 白名單
    • logging + tracing id
    • 超時 / 重試
    • per-run token budget
    • per-tool 次數上限
    import time
    import uuid
    from dataclasses import dataclass, field
    from typing import Any, Callable, Dict, List, Optional
    
    # === 1. 工具註冊中心 ===
    
    @dataclass
    class ToolConfig:
        name: str
        func: Callable[[Dict[str, Any]], Any]
        version: str = "v1"
        max_calls_per_run: int = 5
        timeout_sec: float = 10.0
        cost_per_call: float = 0.001  # 自訂邏輯用
        enabled: bool = True
    
    
    class ToolRegistry:
        def __init__(self):
            self._tools: Dict[str, ToolConfig] = {}
    
        def register(self, tool: ToolConfig):
            key = f"{tool.name}:{tool.version}"
            self._tools[key] = tool
    
        def get_allowed_tools(self, whitelist: List[str]) -> Dict[str, ToolConfig]:
            # whitelist 用的是 name,不帶 version
            out = {}
            for key, tool in self._tools.items():
                if tool.name in whitelist and tool.enabled:
                    out[key] = tool
            return out
    
    
    # === 2. Harness 執行設定 ===
    
    @dataclass
    class RunBudget:
        max_tokens: int
        max_cost: float
        used_tokens: int = 0
        used_cost: float = 0.0
    
        def charge_tokens(self, tokens: int):
            self.used_tokens += tokens
            if self.used_tokens > self.max_tokens:
                raise RuntimeError("Run token budget exceeded")
    
        def charge_cost(self, cost: float):
            self.used_cost += cost
            if self.used_cost > self.max_cost:
                raise RuntimeError("Run cost budget exceeded")
    
    
    @dataclass
    class AgentRunContext:
        run_id: str
        user_id: str
        allowed_tools: Dict[str, ToolConfig]
        budget: RunBudget
        tool_call_counts: Dict[str, int] = field(default_factory=dict)
    
    
    # === 3. LLM 客戶端包一層(示意) ===
    
    class LLMClient:
        def __init__(self, model: str):
            self.model = model
    
        def chat(self, messages: List[Dict[str, str]], tools_schema: List[Dict]) -> Dict[str, Any]:
            """
            回傳格式假設:
            {
              "content": "...",
              "tool_calls": [
                {"tool": "search:v1", "arguments": {"q": "foo"}}
              ],
              "usage": {"input_tokens": 200, "output_tokens": 150}
            }
            """
            # 這裡應呼叫實際 LLM API;為示意略過
            return {
                "content": "stub",
                "tool_calls": [],
                "usage": {"input_tokens": 50, "output_tokens": 30},
            }
    
    
    # === 4. Harness 核心執行器 ===
    
    class AgentHarness:
        def __init__(self, llm: LLMClient, tool_registry: ToolRegistry, logger):
            self.llm = llm
            self.tool_registry = tool_registry
            self.logger = logger
    
        def run(self, *, user_id: str, messages: List[Dict[str, str]],
                tool_whitelist: List[str], max_steps: int = 8,
                max_tokens: int = 8000, max_cost: float = 0.5) -> Dict[str, Any]:
    
            run_id = str(uuid.uuid4())
            allowed_tools = self.tool_registry.get_allowed_tools(tool_whitelist)
            ctx = AgentRunContext(
                run_id=run_id,
                user_id=user_id,
                allowed_tools=allowed_tools,
                budget=RunBudget(max_tokens=max_tokens, max_cost=max_cost),
            )
    
            self.logger.info(f"run_start", extra={"run_id": run_id, "user_id": user_id})
    
            for step in range(max_steps):
                step_id = step + 1
                self.logger.info("llm_step_start", extra={"run_id": run_id, "step": step_id})
    
                tools_schema = [
                    {"name": t.name, "version": t.version} for t in allowed_tools.values()
                ]
                resp = self.llm.chat(messages, tools_schema)
    
                usage = resp.get("usage", {})
                ctx.budget.charge_tokens(usage.get("input_tokens", 0) + usage.get("output_tokens", 0))
    
                tool_calls = resp.get("tool_calls", [])
                if not tool_calls:
                    # 任務完成
                    self.logger.info("run_complete", extra={"run_id": run_id, "step": step_id})
                    return {"run_id": run_id, "final": resp["content"]}
    
                # 執行工具呼叫
                for call in tool_calls:
                    tool_key = self._resolve_tool_key(call["tool"], allowed_tools)
                    tool_cfg = allowed_tools[tool_key]
    
                    count = ctx.tool_call_counts.get(tool_key, 0) + 1
                    if count > tool_cfg.max_calls_per_run:
                        raise RuntimeError(f"tool {tool_key} call limit exceeded")
                    ctx.tool_call_counts[tool_key] = count
    
                    result = self._call_tool_with_retry(tool_cfg, call["arguments"], ctx)
                    messages.append({"role": "tool", "name": tool_cfg.name, "content": str(result)})
    
            raise RuntimeError("max_steps_exceeded")
    
        def _resolve_tool_key(self, tool_name: str, allowed_tools: Dict[str, ToolConfig]) -> str:
            # 簡化:同名只允許一個版本
            matches = [k for k, t in allowed_tools.items() if t.name == tool_name]
            if not matches:
                raise RuntimeError(f"tool {tool_name} not allowed")
            return matches[0]
    
        def _call_tool_with_retry(self, tool_cfg: ToolConfig, args: Dict[str, Any], ctx: AgentRunContext):
            attempts = 0
            while True:
                attempts += 1
                start = time.time()
                try:
                    # 超時控制示意:可用 asyncio.wait_for 或 thread + join
                    result = tool_cfg.func(args)
                    elapsed = time.time() - start
                    self.logger.info(
                        "tool_call",
                        extra={
                            "run_id": ctx.run_id,
                            "tool": tool_cfg.name,
                            "version": tool_cfg.version,
                            "attempts": attempts,
                            "elapsed_sec": elapsed,
                        },
                    )
                    ctx.budget.charge_cost(tool_cfg.cost_per_call)
                    return result
                except Exception as e:
                    if attempts >= 3:
                        self.logger.error("tool_failed", extra={"tool": tool_cfg.name, "err": str(e)})
                        raise
                    time.sleep(0.5 * attempts)
    

    你可以在這層再接上:

    • policy engine:在 _call_tool_with_retry 前後做輸入輸出檢查。
    • human-in-the-loop:當特定工具被呼叫,改成發出審核任務,而不是直接執行。
    • tracing 平台:把 run_id、step、tool 當成 span/trace id,上報到 OpenTelemetry。

    建議與注意事項

    1. 工具權限與多 Agent 協作

    • 不同 Agent(或不同租戶)應有獨立的 tool whitelist 和輸入遮罩。
    • 多 Agent 團隊協作時,要明確:誰能看哪些記憶 / 哪些知識源,避免「助理 Agent 無意間看到 CEO 的對話紀錄」。

    2. 記憶與 RAG 的分層設計

    • 不要「一次把所有 RAG 結果塞進 context」,應做:
    • 第一階段檢索:向量庫 / keyword 檢索
    • 第二階段過濾 / re-rank:只把 top-k、與當前任務強相關的放入 prompt
    • 對長期記憶,引入 TTL / 熱度分級,過舊資料只保存在冷存儲,需要時再查。

    3. Tracing / Replay 一開始就要做

    • 踩坑:先上線,出事再補 tracing → 你根本不知道 Agent 做了什麼。
    • 最少要記:run_id、step、messages(摘要即可)、tool_calls、錯誤堆疊、tool version。
    • 為 replay 保留「當時的工具版本與政策版本」,不然重放行為會不一致。

    4. 成本 / Budget 不只是一個大數字

    • 按 run / user / team / workflow 類型 切分配額,結合告警(例如超過預估平均三倍就發訊息)。
    • 對「自動觸發型」Agent(CI、webhook)尤其要設 per-run token budget + max_steps,避免 prompt/工具錯誤導致無限 loop。

    5. 安全與政策落地要有「硬限制」

    • 除了 prompt 提醒,還要有:
    • policy engine:直接阻擋不符規則的工具呼叫 / 輸出內容。
    • 零信任閘道(如 SolonGate 類)在真正的企業 API 前再加一層,做身分、範圍、頻率控制。

    總結: 不要把「Agent 邏輯」和「治理、成本、安全控制」寫死在同一份業務程式碼裡。先抽出一層可組態、可觀測、可回放的 Agent Harness,你才能放心地擴到多 Agent、跨團隊、跨租戶,而不把整個公司變成昂貴的實驗場。

    🚀 你現在可以做的事

    • 審視現有 Agent PoC,列出目前缺少的 tracing、budget、policy 能力,畫出一層簡易 Harness 設計草圖
    • 在現有程式碼中加入 run_id、step-level log 與最小可用的 replay 機制,先讓行為可觀測
    • 實作一個簡單的 ToolRegistry 與 per-run token budget,從一個 Agent 開始逐步遷移到 Harness 架構
  • 用 Claude Corps 組一支虛擬專案團隊

    用 Claude Corps 組一支虛擬專案團隊

    📌 本文重點

    • Claude Corps 把單一 Claude 變成多角色協作團隊
    • 透過 rubric 審稿 Agent,大幅提升輸出品質
    • 先從「主 Agent + 審稿 Agent」的小流程開始實驗

    一句話定位:Claude Corps 就是「多代理版 Claude 工作流引擎」——讓多個 AI 角色分工協作、互相審查,幫你跑完整個專案流程,而不只是回一個答案。

    👉 官方介紹文:https://www.anthropic.com/news/claude-corps
    👉 rubric grading 實驗文:https://pub.towardsai.net/i-added-one-agent-to-my-claude-setup-and-output-quality-jumped-10-overnight-6e5947a62141


    核心功能:把「一個 Claude」變成「一個團隊」

    1. 多角色分工:策略 / 執行 / 審查

    Claude Corps 的核心概念是:你先設計好幾個固定角色,然後讓他們一起跑流程,而不是一個大 prompt 想包山包海。

    常見三種角色:

    • 策略 Agent(Strategist):負責想方向、列計畫、拆步驟。
    • 執行 Agent(Executor):根據計畫產出具體內容(文案、規格、程式碼)。
    • 審查 Agent(Reviewer):按照事先定好的 rubric(評分標準)檢查、打分、要求重寫。

    你可以從自己現有流程開始,照這三個角色拆:

    行動建議:拿你最近一個要反覆修改的任務(例如長文案撰寫、PRD 撰寫),先在筆記裡寫下:
    – 「策略」要產出什麼?
    – 「執行」要交付什麼?
    – 「審查」要檢查什麼?

    後面我們會把這三塊直接翻成多代理設定。


    2. 長流程協作:從需求到結果的「接力棒」

    Claude Corps 的運作方式,可以理解為:

    1. 每個 Agent 有自己的「角色設定 + 任務說明」。
    2. 任務在 Agent 之間有順序地傳遞(像專案流程),每一步可以讀取前面 Agent 的輸出。
    3. 某些 Agent 還可以「退回修改」——例如 Reviewer 要求 Executor 重寫。

    這讓長流程任務可以被拆成清楚的階段:

    • 不再是「一個超長 prompt」,而是一組可重複、可維護的流程。
    • 你可以只換掉「執行 Agent」,就測試不同寫作風格或程式風格。

    行動建議:選一個你每週都會做的流程(例如「寫週報」或「做競品整理」),用 3–5 個步驟寫成清單,想像每一步由一位 Agent 負責,準備在工具裡實作成 workflow。


    3. 內建 rubric grading 迴圈:讓 AI 自己打分再重寫

    在 Towards AI 那篇案例中,作者只做了一件事:

    在原本的 Claude 產生流程後面,多加一個「評分 Agent」,用 rubric grading 機制打分,如果分數太低就要求重寫。

    💡 關鍵: 只多加一個評分 Agent,就能在既有流程上建立「自評+重寫」迴圈,顯著提升輸出品質。

    關鍵是「分離」:

    • 產出內容的是 主 Agent。
    • 負責打分與給修改建議的是 評分 Agent。

    這樣可以:

    • 降低主 Agent 自己「自賣自誇」的偏差。
    • 讓評分標準可以獨立調整、版本管理。

    行動建議:想一個你最在意品質的輸出(例如「產品頁文案」或「技術文件」),列出 5 條評分標準(如結構、清晰度、錯字、符合品牌語氣等),後面我們會把它翻成 rubric grading Agent。


    實際案例:三種你可以馬上想像的流程

    案例 1:行銷活動全流程

    用 Claude Corps,你可以把一個行銷活動拆成:

    1. 策略 Agent:
    2. 根據產品、目標客群、預算,產出「活動目標 + 訊息主軸 + 渠道組合」。
    3. 內容 Agent:
    4. 依策略生成:EDM 內容、社群貼文、廣告標題、LP 架構。
    5. 審查 Agent:
    6. 根據品牌語氣、禁用詞清單、法規注意事項打分。
    7. 如果某項低於 7/10,要求內容 Agent 重寫該段。

    💡 關鍵: 先用簡單門檻(如 7/10)判斷是否重寫,可以在不大改流程下快速導入品質控管。

    下一步你可以做:先把你現有的一個活動企劃書丟給 Claude,請它幫你拆成「策略 / 內容 / 審查」三階段,當作之後設定多代理流程的草稿。


    案例 2:自動化研究彙整

    研究型工作(PM、顧問、投資研究)非常適合用多代理拆分:

    1. 搜尋 Agent:
    2. 根據關鍵字,抓資料來源(論文標題、新聞、公司年報等摘要)。
    3. 整理 Agent:
    4. 把零散資料整理成結構化表格(時間、來源、關鍵發現)。
    5. 分析 Agent:
    6. 做趨勢整理、異常點分析、風險與機會列表。
    7. 審查 Agent:
    8. 檢查引用是否明確、有無過度延伸推論。

    下一步你可以做:拿近期一份你自己做的研究報告,請 Claude 列出「如果讓三個 AI 分工,你會分給誰做什麼」,你就有一個可以搬進 Corps 的流程骨架。


    案例 3:產品規格 → 需求分解 → 工單產生

    對產品 / 工程團隊來說,最實用的一個模式是:

    1. PRD Agent:
    2. 輸入你草寫的產品想法,產出結構化 PRD(目標、用例、成功指標)。
    3. 需求分解 Agent:
    4. 把 PRD 拆成功能需求、技術任務、依賴關係。
    5. 工單 Agent:
    6. 依照 Jira / Linear 格式,產生工單標題、描述、驗收標準。
    7. 審查 Agent:
    8. 檢查:需求是否有模糊詞、是否有驗收方式、是否與目標對齊。

    下一步你可以做:從你現有的一張 Jira 票開始,請 Claude 幫你「反推」:如果這張票是 AI 產的,上游的 PRD 會長怎樣?然後再用多代理方式讓 AI 自行從 PRD 走到工單。


    如何在現有 Claude 專案中,加一個「審稿/評分 Agent」

    這裡給你一個 最小可行實驗(MVP):不需要整套 Claude Corps,只要你現在有在程式裡呼叫 Claude API,就可以加上「rubric grading 迴圈」。

    假設你目前有一個簡單的 Python 流程:

    from anthropic import Anthropic
    
    client = Anthropic(api_key="YOUR_API_KEY")
    
    user_task = "請寫一篇 500 字的產品介紹文案,產品是…"
    
    # 原本:直接讓 Claude 產出
    resp = client.messages.create(
        model="claude-3-5-sonnet-20240620",
        max_tokens=1200,
        messages=[{"role": "user", "content": user_task}],
    )
    original_output = resp.content[0].text
    print(original_output)
    

    第一步:定義你的 rubric(評分標準)

    先用文字列出你在意的點,例如:

    RUBRIC = """
    你是嚴格的審稿員,負責評估一段文案品質。
    
    請依照以下四個面向,每項 1–10 分評分,並給出具體修改建議:
    1. 結構清晰度(是否有明確開頭、重點段落、結尾)
    2. 語言流暢度(是否口語自然、無明顯錯字)
    3. 說服力(是否清楚說明產品價值,舉例是否具體)
    4. 品牌一致性(是否符合「專業、友善、不浮誇」的語氣)
    
    輸出格式:
    - scores: JSON 格式,包含四項分數與總分 total
    - suggestions: 對每一項給出具體修改建議
    """
    

    第二步:新增一個評分 Agent 呼叫

    def grade_output(text: str):
        resp = client.messages.create(
            model="claude-3-5-sonnet-20240620",
            max_tokens=800,
            messages=[
                {"role": "system", "content": RUBRIC},
                {
                    "role": "user",
                    "content": f"請評分以下文案:\n\n{text}",
                },
            ],
        )
        return resp.content[0].text
    

    你可以先直接 print(grade_output(original_output)) 看看評分長怎樣,再決定要怎麼解析。


    第三步:加上「低分就重寫」的迴圈

    以下是一個簡化版迴圈(概念接近 Towards AI 文中那個 80 行示範):

    import json
    
    MIN_SCORE = 32  # 四項各 8 分的總分門檻
    MAX_TRIES = 3
    
    current_output = original_output
    
    for i in range(MAX_TRIES):
        grading_raw = grade_output(current_output)
        print("=== Grading round", i+1, "===")
        print(grading_raw)
    
        # 嘗試從回覆中抓 JSON scores
        try:
            start = grading_raw.index("{")
            end = grading_raw.rindex("}") + 1
            scores = json.loads(grading_raw[start:end])
        except Exception:
            break
    
        if scores.get("total", 0) >= MIN_SCORE:
            break
    
        # 分數不夠,帶著建議重寫
        rewrite_prompt = f"""
    你剛剛寫了一段文案,得到以下評分與建議:
    
    {grading_raw}
    
    請在保留核心資訊的前提下,依照建議,完整重寫文案。
    """
        resp = client.messages.create(
            model="claude-3-5-sonnet-20240620",
            max_tokens=1200,
            messages=[
                {"role": "system", "content": "你是專業文案撰寫人員。"},
                {"role": "user", "content": rewrite_prompt},
            ],
        )
        current_output = resp.content[0].text
    
    print("=== 最終文案 ===")
    print(current_output)
    

    做到這裡,你就已經擁有:

    • 一個主 Agent(寫文案)
    • 一個評分 Agent(打分 + 給建議)
    • 一個簡單的「多輪改善」迴圈

    💡 關鍵: 設定 MIN_SCORE 與 MAX_TRIES,能在成本可控的情況下,讓文案經過多輪自動優化。

    下一步你可以做:
    – 把 RUBRIC 換成「技術文件」或「教學文章」版本。
    – 把同樣的架構套到「程式碼審查」:改成檢查可讀性、錯誤風險、測試覆蓋建議。


    適合誰用:先把自己當「專案經理」,再讓 AI 來當團隊

    以下幾種工作型態,特別適合導入 Claude Corps 或至少導入「審稿 Agent」:

    • 行銷 / 內容團隊:需要大量產出文案,但又要維持品牌一致、避免錯漏。
    • 產品 / UX 團隊:PRD、用戶故事、需求拆解到工單,流程清楚但繁瑣。
    • 顧問 / 研究 / 投資分析:資料搜集、整理、分析、報告撰寫有明確步驟。
    • 單人創業者 / 自媒體:你一個人要扮演整個團隊,用多代理來「外包」給 AI。

    建議起手式:
    – 不要一開始就設計 10 個 Agent。
    – 先從「主 Agent + 審稿 Agent」開始,把最痛的一個環節自動化。


    怎麼開始:從一個 rubric Agent 起步

    目前 Claude Corps 的完整能力會陸續擴展,不過你可以立刻從現有 Claude API 或 Claude 網頁版開始實驗:

    1. 如果你會寫一點程式:
    2. 申請 Anthropic API Key。
    3. 建一個最小專案(就像上面的 Python 範例),先讓「主 Agent + 評分 Agent」跑起來。
    4. 把 rubric 存成版本可控的檔案,當作你團隊的「寫作 SOP」。

    5. 如果你只用 Claude 網頁版(或其他聊天介面):

    6. 在同一個對話裡,先請 Claude 當「執行者」產出草稿。
    7. 接著開新一則訊息,明確指定:

      你現在是嚴格的審稿員,請依照以下四個面向評分並給修改建議……

    8. 再下一則訊息,把整段評分貼回去,請它依建議完整重寫。

    9. 如果你準備導入多代理架構(如 Claude Corps 或其他 Agent 框架):

    10. 把目前流程畫成 3–5 個步驟(策略 / 執行 / 審查)。
    11. 每一步寫清楚:輸入是什麼?輸出是什麼?由誰接手?
    12. 在框架裡把每一步實作成單獨的 Agent,再用 workflow 串起來。

    只要你成功把「審稿 Agent」加進現有流程,通常輸出品質就會有肉眼可見的提升,接下來要不要擴充成完整多代理團隊,你會更有感覺。


    小結

    • 把 Claude 當成一個人,很容易塞爆它的 prompt;把它拆成多個 Agent,流程會變得好維護、好重複。
    • 最值得先做的第一步,就是加上一個「rubric 審稿 Agent」,讓 AI 自己打分自己改。
    • 從一個最小流程開始(例如單一文案產出),等你看見品質真的上來,再考慮把整個專案流程搬進 Claude Corps 或其他多代理框架。

    🚀 你現在可以做的事

    • 選一個你常做的任務,照「策略 / 執行 / 審查」拆成 3 段,寫成流程草稿
    • 依文中的 Python 範例,在現有 Claude API 流程中加入一個 RUBRIC 評分 Agent
    • 在 Claude 網頁版實際跑一次「先產出、再評分、再依建議重寫」的完整迴圈
  • 用 ASSERT 替 AI Agent 寫單元測試

    用 ASSERT 替 AI Agent 寫單元測試

    📌 本文重點

    • ASSERT 用文字規格測「整個 Agent 流程」
    • 可 mock 工具與政策,做可預期的回歸測試
    • 非決定性輸出用「性質斷言」而非比字串

    多數團隊在做 LLM/Agent 開發時,測試痛點很具體:

    • 同一個 prompt 今天過、明天壞,沒有紅綠燈,只能祈禱
    • 多 Agent + 工具調用後,bug 出在「流程」不是「回答內容」,傳統單元測試很難 cover
    • 合規、安全團隊寫了一堆 policy,難以自動驗證 Agent 是否真的遵守

    微軟開源的 ASSERT 直接對準這個痛:用純文字規格定義 Agent 的預期行為,讓你像寫單元測試一樣寫回歸測試,從「這個答案正不正確」提升到「整個 Agent workflow 行為可預期」。


    重點說明

    1. 從「回答對不對」到「行為對不對」

    傳統 LLM 評估多半是:

    • 給一段 input
    • 看 output 文本是否符合 ground truth / rubric

    ASSERT 的思維是:

    測試的是 Task + Workflow:給定初始指令、工具與外部環境,整個 Agent 互動過程是否符合文字規格中的 行為斷言。

    它特別適合:

    • 多 Agent 協作(例如 Planner → Worker → Reviewer)
    • 有工具調用(DB 查詢、API call、程式執行)
    • 有政策/合規約束(不得外洩個資、不得跨區讀資料)

    💡 關鍵: ASSERT 把測試焦點從單次回答,提升到整個任務與 workflow 的行為是否符合規格。


    2. ASSERT 規格長什麼樣:文字規格 + 執行模型 + 斷言機制

    ASSERT 的核心是測試規格檔(YAML / JSON / 純文字皆可包裝),通常包含三部分:

    1. scenario:描述這次要跑的任務
    2. execution:怎麼把這個 scenario 丟給 Agent workflow
    3. assertions:要驗證哪些行為/輸出

    簡化的規格示意:

    name: "refund_flow_basic"
    scenario:
      description: |
        使用者要求退貨,訂單已在可退貨期限內,客服 Bot 應該自動建立退貨申請,並口頭說明流程。
      input:
        user_message: "我想退掉上週買的藍色 T-shirt,訂單號 12345"
    execution:
      entry_point: customer_support_agent.handle_message
      tools:
        - name: get_order
          mock_response:
            id: 12345
            status: "delivered"
            days_since_delivery: 3
            refundable: true
        - name: create_refund
          record_calls: true
    assertions:
      - type: tool_called
        tool: create_refund
        times: 1
        with_args:
          order_id: 12345
      - type: text_includes
        source: final_response
        any:
          - "已為您建立退貨申請"
          - "退貨流程"
      - type: policy
        name: "no_personal_data_leak"
    

    重點:

    • 用自然語言描述 scenario,方便 PM / 合規一起維護
    • 工具可 mock / record,這是把 Agent 當程式測的關鍵
    • assertions 可以混合:工具行為、對話內容、policy 檢查

    💡 關鍵: 把工具層 mock 起來、再對工具呼叫與回應做斷言,是從「prompt 測試」進化到「Agent 測試」的核心步驟。


    3. 怎麼嵌進多 Agent、治理與外部工具

    搭配近期微軟的可攜式政策檔(portable policy files),ASSERT 可以變成:

    • CI 裡的 治理紅綠燈:每次變更 prompt / policy / 模型,都跑一輪 ASSERT spec
    • 多 Agent 系統中的 守門員:有點像 Reddit 討論的 Guardian agents,只是這次是「測試守門員」,不是線上 runtime 監管

    架構上的典型串法:

    • Agent Workflow:Orchestrator(如 Semantic Kernel / 自寫 orchestrator)
    • 工具層:
    • 真實工具:DB、REST API、向量庫
    • 測試時由 ASSERT 注入 mock tool adapter 或 固定回應
    • 政策層:NIST / 企業規範 → portable policy 文件 → 在 assertions 中當作 policy assertion 來跑

    這樣做的實際好處:

    • 你可以在不碰線上真環境的情況下,回歸測試整條 Agent 流程
    • 合規團隊寫的 policy,可以直接被 ASSERT 當作測試規範執行,而不只是 PDF 文件

    實作範例

    下面用三個場景示範:客服 Bot、資料 ETL、CI 裡修 Bug Agent。

    1. 客服 Bot:測「流程」而不是只看一句回答

    假設你有一個多 Agent 客服系統:

    • UserAgent:跟使用者聊天
    • OrderAgent:查詢訂單
    • PolicyAgent:檢查回應是否合規

    測試規格可以這樣寫:

    name: "support_refund_policy_safe"
    scenario:
      description: |
        使用者要求退貨,系統應建立退貨、不得暴露完整信用卡號。
      input:
        user_message: "我要退貨,訂單 98765,付費卡號是 4111111111111111"
    execution:
      entry_point: support_orchestrator.run
      tools:
        - name: query_order
          mock_response:
            id: 98765
            refundable: true
        - name: payment_gateway
          mock_response:
            last4: "1111"
    assertions:
      - type: tool_called
        tool: query_order
      - type: tool_not_called
        tool: payment_gateway
        reason: "不應直接打外部金流 API"
      - type: text_not_matches
        source: final_response
        pattern: "[0-9]{16}"
      - type: text_includes
        source: final_response
        any:
          - "已協助您申請退貨"
          - "將退款至原支付方式"
    

    這裡沒有要求「逐字比對」,而是用:

    • text_not_matches 避免輸出完整卡號
    • text_includes any 容忍 LLM 的表達多樣性

    2. 資料 ETL Agent:檢查中間狀態與外部副作用

    想像一個 Agent:

    • 從 S3 抓 CSV
    • 清洗欄位
    • 寫入 Data Warehouse

    用 ASSERT,你可以 mock S3 / DWH,專注檢查 轉換邏輯 是否符合預期。

    name: "etl_normalize_user_table"
    scenario:
      description: "將 user_raw.csv 正規化成 user_clean,email 小寫、移除測試帳號"
      input:
        job_id: "nightly_2024_01_01"
    execution:
      entry_point: etl_agent.run_job
      tools:
        - name: s3_get_object
          mock_response_file: "fixtures/user_raw.csv"
        - name: dwh_insert_rows
          record_calls: true
    assertions:
      - type: tool_called
        tool: dwh_insert_rows
        where:
          table: "user_clean"
      - type: dataset_equals
        source: tool_call[dwh_insert_rows].args.rows
        fixture: "fixtures/expected_user_clean.json"
        ignore_order: true
    

    dataset_equals 是典型對非文字輸出做 assertion 的方式:你比對結構化資料,而不是 LLM 的自然語言回覆。


    3. CI 裡的自動修 Bug Agent:把 Anthropic 安全掃描類場景做成 regression

    參考 Anthropic 的 Project Glasswing/Claude Security:AI 找漏洞、再幫忙修。你也可能有一個 FixBot:

    • 接收測試失敗訊息
    • 讀 code
    • 生成 patch
    • 開 PR 或直接 commit

    ASSERT 可以幫你確保 FixBot 至少要做到:

    • 不會刪整個檔案
    • 會更新/新增對應的單元測試
    name: "fixbot_does_not_delete_file"
    scenario:
      description: "FixBot 收到 NullPointerException 應該局部修改,而不是刪檔案"
      input:
        failing_test_output: "NullPointerException at UserService.java:42"
    execution:
      entry_point: fixbot_agent.run
      tools:
        - name: git_diff
          mock_response_file: "fixtures/fixbot_patch.diff"
    assertions:
      - type: diff_policy
        source: tool_call[git_diff].response
        rules:
          - "禁止整檔刪除 (*.java)"
          - "至少有一個新增或修改的測試檔 (*Test.java)"
    

    在 CI 裡,你可以:

    • 每次改 FixBot prompt、模型版本、或 policy,就跑 ASSERT 測試
    • 把 ASSERT 結果送進既有的 觀測系統(如 Application Insights、Datadog),當作一條獨立的 quality signal

    建議與注意事項

    1. 非決定性輸出:不要比字串,要比「性質」

    LLM / Agent 的非決定性,是大家寫測試最怕遇到的坑。建議:

    • 儘量使用 text_includes / text_not_includes / regex / any-of 這種「鬆綁」的 assertion
    • 把重點放在:
    • 是否有該說的關鍵資訊
    • 是否避免不該說的內容(個資、敏感字)

    • 對較長回答,可以用自動 rubric 評分:

    - type: llm_judge
      rubric: |
        檢查回答是否:
        1. 有解釋退貨步驟
        2. 沒有要求多餘敏感資訊
      threshold: 0.7
    

    這裡的 llm_judge 其實是「用另一個 LLM 做 assertion」,要注意模型成本與安全配置。


    2. 固定工具回應:mock / replay 是關鍵

    如果你直接讓測試呼叫真實工具,會踩到:

    • 線上資料變動 → 測試結果漂移
    • 外部 API 限流 / timeout → CI 不穩

    最佳做法:

    • 在 ASSERT 的 execution.tools 段落中,預設開 mock_response / mock_response_file,除非你真的需要打真環境
    • 重要的整合測試可以用 record & replay 模式:第一次記錄真實 tool 回應,以後回歸測試直接重放

    3. 整合 CI/CD 與觀測:讓 Agent 上線也有紅綠燈

    推薦的落地流程:

    1. 建立 baseline spec:
    2. 把現有的「用例」整理成 ASSERT 規格(客服 10 條、ETL 5 條、FixBot 5 條)
    3. 這些就是你的 regression suite

    4. 接到 CI pipeline:

    5. 在 GitHub Actions / Azure DevOps / GitLab CI 裡加一個步驟:
    - name: Run agent tests
      run: |
        assert-cli run specs/**/*.yaml \
          --report-json reports/assert-report.json \
          --fail-on-error
    
    1. 接到觀測 /治理系統:
    2. 把 ASSERT 的結果送到 log / metrics:
      • 每次部署的測試通過率
      • 哪些 spec 常壞(容易暴露 prompt / policy 問題)
    3. 若你有像 ServiceNow / Bedrock 那種 Control Tower / Guardian Agent 架構,可以把 ASSERT 的失敗 spec 直接丟給「治理 Agent」分析與產生修正建議

    4. 不要期待 ASSERT 解決「所有安全問題」

    Nvidia + Microsoft 的研究已經說得很白:AI Agents 不會自己在意安全與可靠性。ASSERT 能做的是:

    • 把你定義好的安全與行為規範自動化檢查
    • 把治療從「事後看 log」提前到「部署前的紅綠燈」

    真正上線時,你仍然需要:

    • 率限制、風險評分、多層防護(runtime policy enforcement)
    • 真實世界的行為監控與 A/B 驗證

    ASSERT 的定位比較像:讓 Agent 開發過程長出一套跟傳統軟體一樣嚴謹的測試文化,從「祈禱不要出事」變成「明確知道自己 cover 哪些情境、沒 cover 哪些」。


    結論:如果你的專案已經走到多 Agent + 工具調用階段,建議盡快挑幾條關鍵 user journey,用 ASSERT + 文字規格 寫出第一批回歸測試。只要第一批 spec 建起來,後面不論換模型、改 prompt、加新工具,都有一條明確的品質與治理基準線可以守住。

    🚀 你現在可以做的事

    • 整理現有 3–10 條關鍵 user journey,轉寫成 ASSERT scenario + execution + assertions 規格檔
    • 在現有 CI(如 GitHub Actions)新增 assert-cli run specs/**/*.yaml 步驟,讓 Agent 變更都有紅綠燈
    • 將工具層接上 mock_response / record & replay,先從一條多 Agent + 工具調用的關鍵流程開始做回歸測試
  • 一行指令組好自己的 AI 代理團隊

    一行指令組好自己的 AI 代理團隊

    📌 本文重點

    • 把每個 Agent 當成可版本控制的 Git repo
    • 用 AGENTS.md 與 skills/ 拆開角色與能力
    • .agentlas/ 管理記憶與設定,輕鬆切換模型
    • 用一行 CLI 指令生成並維護多代理 AI 團隊

    只用一行指令,你就能建立一個「像程式專案一樣可版本控制」的多代理 AI 團隊,解決長期記憶混亂、每次對話都要重講一遍的痛點。

    參考架構原文(作者在 Reddit 分享):https://www.reddit.com/r/artificial/comments/1twmhya/an_opensource_agent_architecture_that_solves_the/


    核心功能:把 Agent 當成一個 repo,而不是一段 prompt

    這套開源架構的關鍵想法:每個 Agent 是一個 Git 倉庫,而不是存在聊天框裡的一段系統 prompt。

    💡 關鍵: 把 Agent 轉成可版控的 Git repo,可以用熟悉的軟體開發流程(PR、review、版本管理)來調整 AI 行為,而不是每次重寫 prompt。

    1. AGENTS.md:把「人設 + 任務範圍」寫成文件,而不是憑記憶

    AGENTS.md 是這個架構的核心說明書,裡面通常會包含:

    • 每個代理的角色定位
    • 能做什麼(scope) / 不能做什麼(限制)
    • 彼此如何協作(誰丟任務給誰、輸出長什麼樣)

    你可以做的事:

    1. 在任意資料夾新增 AGENTS.md,用這樣的格式寫第一個代理:

    “`markdown
    # Agents

    ## doc_assistant
    – 角色:技術文件整理員
    – 任務:整理長篇文件、產生摘要與目錄
    – 輸出格式:Markdown,必須包含「摘要」「重點條列」「待釐清問題」三段

    ## code_reviewer
    – 角色:程式碼審查員
    – 任務:針對 Pull Request 提出具體修改建議
    – 限制:不直接改動程式,只提出建議與風險說明
    “`

    1. 把這個 repo 推上 Git(GitHub / GitLab),之後團隊改需求只改這份文件。

    2. skills/:把「能力」拆成工具,而不是糊在一大段 prompt 裡

    傳統代理常見問題是:所有指令、規則、流程都揉在一段很長的系統 prompt 中,改一行就怕爆。

    在這個架構裡,每個能力是一個 skill 檔案,放在 skills/ 資料夾,例如:

    • skills/summarize_docs.md:怎麼讀、怎麼切段、輸出格式
    • skills/review_pr.md:審查步驟、要檢查的細項
    • skills/fetch_urls.md:如何抓網頁、處理錯誤

    你可以做的事:

    1. 新增資料夾 skills/,寫一個最小可用的 skill:

    “`markdown
    # summarize_docs

    步驟:
    1. 讀取輸入文件
    2. 把文件拆成 3–7 個主題
    3. 每個主題用 3 行內說清楚

    輸出格式(Markdown):
    – 一句總結
    – 主題列表(子彈點)
    “`

    1. 在 AGENTS.md 裡指定某個 agent 可以使用 summarize_docs 這個 skill。

    3. .agentlas/:把「記憶和設定」存在檔案,而不是模型腦袋

    .agentlas/ 是這套系統自己的設定與記憶資料夾,用來存:

    • 各代理的偏好設定(語氣、輸出格式)
    • 長期記憶索引(不是直接塞進模型,而是存成檔、用時再讀)
    • 已完成任務的 metadata(方便日後追蹤與再訓練)

    這樣的好處是:

    • 不會把所有對話硬塞進「長期記憶」變成噪音
    • 每次執行前可以用規則選擇要載入哪一段內容

    你可以做的事:

    • 在 .agentlas/config.yaml 加上最基本設定(示意):

    yaml
    default_model: claude
    memory:
    enabled: true
    strategy: recent-and-related
    max_items: 50


    適合誰用:3 個具體場景

    1. 文件整理 + 程式碼審查:建立雙代理 pipeline

    目標:

    • 代理 A 整理設計文件
    • 代理 B 以文件為準則,審查 PR 是否符合設計

    你可以怎麼做:

    1. 在 AGENTS.md 定義兩個代理:

    “`markdown
    ## doc_assistant
    – skills: [summarize_docs]

    ## code_reviewer
    – skills: [review_pr]
    – 會先讀 doc_assistant 的輸出再開始審查
    “`

    1. 把專案文件放在 docs/、程式碼在 src/,PR diff 存到 pr/123.patch。

    2. 在終端機執行(以架構作者的描述為例):

    bash
    agentlas run doc_assistant "整理 docs/ 裡的文件,產出開發規範摘要"
    agentlas run code_reviewer "根據最新開發規範,審查 pr/123.patch"

    1. 審查邏輯日後有變,就更新 skills/review_pr.md,而不是重新教一次模型。

    2. 資料抓取 + 報表生成:自動化情報小幫手

    目標:

    • 代理 C 負責抓網站、清洗文字
    • 代理 D 讀整理後資料,輸出報表(例如每週競品動態)

    步驟:

    1. 在 skills/fetch_urls.md 寫清楚:怎麼從列表讀網址、輸出 JSON 或 Markdown 表格。

    2. 定義兩個 agent:

    “`markdown
    ## data_collector
    – skills: [fetch_urls]

    ## report_writer
    – skills: [summarize_docs]
    – 輸出格式:週報(含「本週重點」「風險」「下週觀察」)
    “`

    1. 每週只需要:

    bash
    agentlas run data_collector "從 urls.txt 抓內容,輸出到 data/this_week.md"
    agentlas run report_writer "讀 data/this_week.md,生成 weekly_report.md"

    1. 週報模板要改,就改 skills/summarize_docs.md 或另開 skills/weekly_report.md。

    3. 多人團隊:把「AI 同事」當成共同維護的 repo

    你可以把這個代理倉庫當成一個共享 AI SOP:

    • PM 改 AGENTS.md 描述角色與流程
    • 開發者在 skills/ 補上新的工具使用說明(例如專案腳本、內部 API)
    • 新同事只要 clone 下來,就能直接用同一套 AI 工作流

    實際做法:

    1. 建一個 GitHub repo:company-ai-agents。

    2. 定一條規則:

    3. 改 Agent 行為 = 改 AGENTS.md / skills/,必須走 PR 流程

    4. 不再使用的 Agent 要標記 deprecated,避免沒人知道它還在跑

    5. 在 README 裡寫清楚「如何在本機執行 agentlas、如何切換模型」。


    怎麼開始:一行指令 + 接上你習慣的模型

    這個架構的一大好處是:不綁特定模型,可以跑在 Claude Code、Codex、Gemini CLI 等環境。

    💡 關鍵: 架構與模型解耦,讓你能在不改 AGENTS.md 和 skills/ 的情況下,自由切換到成本更低或能力更強的模型。

    1. 安裝指令(假設已提供 CLI:agentlas)

    作者在 Reddit 說明,整套系統可以透過一行安裝:

    pip install agentlas  # 或作者實際提供的套件名稱
    

    接著在任意資料夾初始化:

    agentlas init
    

    這通常會自動產生:

    • AGENTS.md
    • skills/
    • .agentlas/

    你可以做的事:

    • 直接在一個新資料夾跑 agentlas init,把它當成「AI 同事模板專案」。

    2. 接上常見模型:Claude / Codex / Gemini CLI

    根據作者說明,這個架構支援多個 runtime,不鎖在某一家:

    • 想用 Claude:在 .agentlas/config.yaml 設定 runtime: claude_code
    • 想用 OpenAI / Codex:設為 runtime: codex
    • 想用 Gemini CLI:設為 runtime: gemini_cli

    範例設定:

    runtime: claude_code
    model: claude-3-5-sonnet
    api_key_env: ANTHROPIC_API_KEY
    

    你可以做的事:

    1. 先用你現在線上付費的模型(例如 Claude)跑通第一個 agent。

    2. 之後想切換模型,只改 config,不改 AGENTS.md 和 skills/ 的內容。

    3. 一句描述,讓系統自動幫你生出代理團隊

    根據 Reddit 原文描述,你可以直接用一句話生成整個代理團隊,例如:

    agentlas new "幫我建立一個:整理產品需求文件 + 產出技術任務清單的雙代理工作流"
    

    CLI 會:

    • 生成初版 AGENTS.md(定義 2 個代理)
    • 建好對應的 skills 檔案
    • 幫你填入基本步驟和輸出格式,之後你再微調

    你可以做的事:

    • 先讓工具幫你生一個「80 分」版本,再用 Git 慢慢調整到「95 分」。

    延伸閱讀:為什麼要從「串 prompt」升級到「Agent 專案」?

    如果你還在用 LangChain 式的「串 prompt + 自己管工具 schema」,可以參考這兩篇:

    這套開源架構跟 MCP 的共通點是:都把 Agent 當成一個長期維護的系統,而不是當場臨時寫的 prompt。

    差別在於,這裡用「實際檔案 + repo」把行為和記憶拆開,讓你可以用熟悉的 Git 流程維護整套 AI 工作流。

    現在你可以做的最後一件事:

    • 開一個新 repo,跑一次 agentlas init,寫一個最小的 AGENTS.md
    • 選一個你每天真的會用到的小流程(例如「整理會議紀錄」)
    • 把它做成第一個可版本控制的 AI 代理

    之後,每次你覺得「這件事好像可以交給 AI」,就把需求寫進 AGENTS.md 和 skills/,你自己的多代理 AI 團隊就會越長越完整。

    🚀 你現在可以做的事

    • 在本機建立新資料夾,執行 agentlas init,產出第一版 AGENTS.md 與 skills/
    • 選一個日常流程(如整理會議紀錄),寫進 AGENTS.md 並建立對應的 skills/xxx.md
    • 建一個 company-ai-agents repo,推上 GitHub,讓團隊透過 PR 共同維護你的 AI 代理 SOP
  • 用 Claude+n8n 打造會自己跑的 AI 工作流

    用 Claude+n8n 打造會自己跑的 AI 工作流

    📌 本文重點

    • 用 Claude /goal、/loop 自動化長流程任務
    • 用 n8n 串 API、Email、DB 打造完整工作流
    • 一定要保留「人工審核閘」避免 AI 誤發內容

    用 Claude 搭配 n8n,你可以把「每週拉數據、寫報告、發信或更新網站」這種例行公事交給 AI 自動跑完,只在最後一關人工點頭即可。


    核心功能:這套組合幫你做什麼?

    1. Claude 長任務指令:/goal、/loop 讓 AI 自己把事做完

    先理解 Claude 的幾個關鍵指令(在 Claude Code 或支援指令的環境使用):

    • /goal:一次講清楚最終目標,讓 AI 自己拆步驟、規劃流程、按順序執行。
    • /loop:針對一批項目重複跑同一種處理,例如對 50 個關鍵字依序寫摘要、產出標題。
    • /batch:一次處理多筆輸入,適合批次內容生成或批次分析。

    可參考這篇詳細說明:Claude Code: The Autonomous Commands…

    你可以馬上做的事:

    • 在 Claude 裡建一個新專案,貼上你常做的流程(例如「每週 SEO 報表」),試著用這個提示:

    /goal
    目標:我想把「每週 SEO 報表」變成一個自動流程。
    請你:
    1. 問我目前是怎麼做(包含用到哪些工具、檔案格式)
    2. 拆成清楚步驟,分出「AI 可以做」和「一定要人工做」
    3. 用適合 /loop 或 /batch 的地方標註出來


    2. n8n:把 API、Email、資料庫都接在一起

    n8n 是一個開源自動化工具(像是開源版 Zapier),負責把「資料源 → Claude → 發送結果」串起來。

    常用節點大概就是:

    • Trigger:時間觸發(Cron),例如每週一早上 9 點跑一次。
    • HTTP Request / API 節點:去叫 Google Search Console、SEO 工具、你的內部系統。
    • Claude / OpenAI 類節點:把資料丟給 Claude 分析或生成內容。
    • Email / Slack / Notion 節點:把結果送到你或客戶手上。

    你可以馬上做的事:

    • 註冊 n8n(雲端版)或用 Docker 在本機跑:https://n8n.io
    • 開一個最簡單 workflow:
    • Cron → HTTP Request → Email
    • HTTP Request 隨便先叫一個公開 API(例如 Github trending),收回結果後,用 Email 節點寄給自己,確認「API → 自己」這條管線沒問題。

    3. 人工審核閘:一定要保留的「最後一關」

    這一步是整篇文章最重要的重點。

    在一篇實戰分享裡,作者用 Claude + n8n + SE Ranking API 自動產出客戶 SEO 週報,為了省 token 做了一個「優化」:當某些欄位沒資料時,讓 Claude 自己補。結果 Claude 開始用其他客戶的品牌數據去補空缺,還一臉正常,差點寄給一整串客戶,最後是靠人工審核節點擋住了災難(原文連結)。

    💡 關鍵: 再「聰明」的自動化流程也可能補錯資料,最後一關一定要由人審核才能避免嚴重誤發。

    結論:千萬不要把最後的人審自動化掉。

    你可以馬上做的事:

    • 在 n8n 裡加一個「人審」節點(可以是):
    • 把報告先丟到 Slack 私訊給你,附兩個按鈕:Approve / Reject。
    • 或者寄到你 Email,只有你手動轉寄給客戶才算「送出」。
    • 規則很簡單:
    • AI 可以草擬與整理,
    • 你來決定要不要「正式送出」。

    實戰案例:自動 SEO 週報(含人審)

    參考 Reddit 上「Claude is my entire SEO team」的做法(原文),我們做一個最小可行版本:

    流程圖(文字版)

    1. Cron 觸發:每週一 09:00。
    2. 抓數據:n8n 呼叫 Google Search Console / SE Ranking / GA4 API。
    3. 整理成 JSON/CSV:在 n8n 做基本清洗、欄位統一。
    4. 丟給 Claude:
    5. 提示大意:

      “`
      你是一位 SEO 分析師。請根據以下資料:
      – 找出本週與上週的主要變化(曝光、點擊、CTR、排名)
      – 標記前三個值得關注的關鍵字或頁面
      – 用「給客戶看的語氣」寫一份 300-500 字報告
      – 最後用 bullet points 給出下週三個具體行動建議

      資料如下(JSON):
      {{data}}
      “`

    6. 產出報告草稿:Claude 回傳 Markdown 或 HTML。

    7. 人工審核閘:
    8. n8n 把草稿丟到 Slack 或 Email。
    9. 你檢查內容、改幾句,手動按「OK」。
    10. 正式發送:
    11. n8n 把最終版本寄給客戶,存一份到 Notion/GDrive。

    你可以馬上做的事:

    • 先不接 API,把第 2 步改成「從 Google Search Console 下載 CSV,手動上傳到 n8n」:
    • n8n workflow:Manual Trigger → Upload(Webhook / Form)→ Claude → Email to yourself。
    • 等流程穩定,再把手動上傳改成 API 自動抓。

    多代理與 MCP:什麼時候要用,什麼時候別急著上

    當你開始想:

    • 一個 Agent 負責抓資料
    • 一個 Agent 負責分析
    • 一個 Agent 負責 QA / 測試

    就會踩到「多代理系統」與 MCP(Model Context Protocol)的世界。

    有人已經用 Claude Agent SDK + MCP 做出:

    • 看板(Kanban)每張卡片就是一個開發任務
    • Cron 定時啟動 Claude,為每張卡開一個隔離環境,
    • 拉 repo → 寫 code → Git 提交 → Vercel 建預覽 → 第二個 Claude 做 QA 測試 → 不過就自動重試(案例影片)。

    但實務上,多代理+多個 MCP server 很容易變成:

    • 工具太多、描述太長,模型不斷選錯工具。
    • 每次調用都把全部工具 schema 塞進 context,token 成本暴漲(有研究與實務案例顯示,長 agent run 可能是一般聊天的 1000 倍 token)。

    💡 關鍵: 多代理與 MCP 雖強大,但會大幅拉高複雜度與 token 成本,先把單一流程跑穩再擴充效益最高。

    建議:先把單一流程 + 人審做好,再考慮多代理。

    你可以馬上做的事:

    • 若你不是工程背景,目前只要記得兩件事:
    • MCP = 讓 AI 直接操作一堆內部工具的「插座規格」。
    • 「工具越多越好」是錯誤想像,實務上要刻意減少工具數量,讓 AI 比較不會選錯。

    適合誰用:三種典型場景

    角色 / 團隊類型 痛點 可以先做的第一條工作流
    個人創業者 / 獨立站長 每週 SEO 數據、內容企劃很花時間 自動 SEO 週報 + 下週內容建議(保留人審)
    接案顧問 / 行銷代理商 多個客戶週報、月報內容高度重複 客戶週報模板 + 客製評論區,由 Claude 先填、你負責最後一段「專業觀點」
    內容團隊主編 多篇文章排程、更新 meta、內鏈整理 用 /loop 對多篇文章產出標題、描述,n8n 更新到 CMS 草稿,人工再審核發佈

    工具比較:Claude、n8n 以及類似選項

    名稱 核心功能 免費方案 適合誰
    Claude (Claude Code) 長任務指令(/goal、/loop)、多代理、強文字與程式處理 有免費網頁版與有限額度 需要寫報告、寫程式、設計長流程的人
    n8n 視覺化工作流、自動化各種 API/Email/DB 有自架免費版,雲端有免費層 想用「拖拉方式」把不同工具串起來的人
    Zapier/Make SaaS 自動化平台,介面更友善,內建大量整合 有免費層但步數較少 不想部署系統,只想快點測試概念的人

    💡 關鍵: Claude 負責「想與寫」,n8n 和 Zapier/Make 負責「串與送」,搭配起來才能形成真正的自動化流水線。


    怎麼開始:從「一個安全的流程」練起

    按照這個順序,你可以在半天內完成第一條「會自己跑,但有你把關」的 AI 工作流:

    1. 開通帳號
    2. 註冊 Claude 帳號:https://claude.ai
    3. 註冊 n8n(雲端或自架):https://n8n.io

    4. 在 Claude 裡寫清楚你的「流程說明書」

    5. 建一個專屬專案,新增檔案 WORKFLOW.md,內容包含:

      • 你每週報告的步驟
      • 用到資料來源
      • 哪些步驟你不想讓 AI 自動做(例如「最後寄給客戶」)
    6. 做第一個最小工作流(本機測試版)

    7. n8n:Manual Trigger → 手動貼 CSV → Claude → Email 給自己。
    8. 實際跑一次,看 Claude 生成的報告是否接近你平常寫的內容。

    9. 加上人工審核閘

    10. 把收件人改成你的 Slack / 私人 Email。
    11. 只有你手動確認後,才另外轉寄給客戶或上線。

    12. 再考慮進階:API、自動抓數據、多客戶分流

    13. 等你對這條流量和錯誤模式有感覺後,再把手動步驟替換成 API。

    只要守住「AI 做草稿,人做決定」這一條線,你就可以放心把「會自己跑」的工作流丟給 Claude 和 n8n,真正把時間留給需要判斷與創意的事情。

    🚀 你現在可以做的事

    • 在 Claude 建一個專案,照文中範例貼上你的「每週報表流程」並用 /goal 讓它幫你拆步驟
    • 在 n8n 建立「Cron → HTTP Request → Email」或「Manual Trigger → Claude → Email」的最小工作流
    • 為現有任一例行報告加上一個「人工審核閘」,先從「AI 草擬、人來定稿」開始運行
  • AI Middleware 控制層實戰指南

    AI Middleware 控制層實戰指南

    📌 本文重點

    • 直接在業務程式碼呼叫 LLM API 是反模式
    • 需要獨立 AI Middleware 控制層集中治理
    • 控制層是從 demo 到 production 的關鍵分水嶺
    • 多代理與記憶治理必須納入審計與安全設計

    先講結論:直接在產品程式碼裡 fetch('https://api.llm.com') 是一種反模式。當功能從「問答」長成「多工具、多代理、多租戶」時,你會需要一層專門的 AI Middleware 控制層,把:

    • 模型路由與選型
    • Tool / Agent 協調
    • log / trace / metrics
    • 成本與速率限制
    • cache / 重試策略
    • 合規與安全策略注入

    從業務程式碼中抽出來,集中治理。這一層就是 LLM 應用從 demo 到 production 的分水嶺。

    💡 關鍵: 把所有 LLM / Tool / Agent 呼叫收斂到單一控制層,是從玩具 demo 變成可維運產品的必要條件


    重點說明:為什麼要獨立 AI Middleware 控制層?

    1. 從「直接 call 模型」到「控制層」

    傳統 Web 三層:UI 層 → Service 層 → DB 層。

    LLM 應用如果是:UI → 直接呼叫模型 API,你會發現:

    • 想加 多模型路由(如 GPT + Claude + 本地模型)時,只能四處找出 openai.chat.completions.create 逐一改。
    • 想統一 重試 / 超時 / 灰階 / rollback,根本沒有共同入口可以掛。
    • 想做 成本統計、用戶配額,只剩 trace log 回頭瞎猜。

    控制層的做法是:

    Product Code → AI Middleware(控制層)→ 各家模型 / 工具 / Agent

    所有模型呼叫先經過這一層,才能實作像 API Gateway + Service Mesh 那種集中治理能力。

    2. 控制層的核心職責拆解

    實務上,控制層至少要負責這幾件事(建議直接當 checklist):

    1. 路由與模型選擇
    2. 依 任務類型、租戶、成本上限、延遲預算 選擇具體模型。
    3. 範例策略:summary 走便宜模型;寫 SQL 走更準確模型;UGC 敏感類先加安全模型前置審查。

    4. Tool / Agent 協調

    5. 統一管理 工具 registry、Tool 調用權限、Agent orchestration。
    6. 在多代理系統中,把「誰能叫什麼工具」和「記憶寫入策略」集中配置,而不是散落在各 Agent 類別裡。

    7. 觀測與追蹤(logs / traces / metrics)

    8. 每個 LLM 請求、工具呼叫、記憶讀寫 都要有 trace id。
    9. 對接 OpenTelemetry 或 APM:把 token 數量、延遲、錯誤碼、cache 命中等變成可查詢的 metrics。

    10. 成本與速率限制

    11. 按 user / org / feature 做 token 配額與 qps 限制。
    12. 將成本計算邏輯(model 單價、權重)集中在控制層。

    13. cache 與重試策略

    14. 根據 prompt 指紋 + 入參 做 deterministic 回答的 cache。
    15. 定義 哪些錯誤可重試、最大重試次數、退避策略,避免在業務層各自實作。

    16. 合規與安全策略注入

    17. 對接 DLP、敏感詞檢測、角色權限,把 prompt / tool call / output 走同一條審查管線。
    18. 企業內部可在這裡插入 審批流(human-in-the-loop)。

    💡 關鍵: 成本、風險與合規控制若不集中在控制層,很難在日後擴展時補強而不「大重構」

    3. 審計追蹤與記憶治理:多代理系統必備

    多代理系統與企業場景最常見的兩個雷:

    • 沒有審計 trail:agent 到處點擊、下單、寫 ticket,最後出了事誰也說不清「是第幾步決定下單」?
      → 參考 Reddit 討論:AI agents 比起更多自主性,更需要完整 audit trail。控制層就是自然的落點。

    • 記憶污染嚴重:所有 agent 都能隨意寫向量庫,久了之後充滿過期決策、敏感資料。
      → 引入 Memory Curator 概念:worker agent 只能發出 記憶事件,由專門的記憶治理層決定要不要寫入、寫到哪個 scope(個人 / 團隊 / 專案 / 會話)。

    這兩件事如果不在控制層規範好,就會像 Reddit 上那位開發者形容的:「前三週一切正常,之後 retrieval 變成噪音地獄」。


    實作範例:用 Genkit 與 Vercel AI Gateway 搭 Node / Python 控制層

    下面用兩個路線示範:

    • Node:Google Genkit + Vercel AI Gateway
    • Python:簡易自製 Middleware(搭配 OpenTelemetry)

    範例一:Node + Genkit Middleware(含 model 路由與 observability)

    安裝與基礎設定(簡化示意):

    npm install @genkit-ai/core @genkit-ai/openai @genkit-ai/firebase
    npm install @opentelemetry/api @opentelemetry/sdk-node
    

    建立 aiClient.ts 當控制層入口:

    // aiClient.ts
    import { genkit, z } from "@genkit-ai/core";
    import { openai } from "@genkit-ai/openai";
    
    const ai = genkit({
      plugins: [openai()],
    });
    
    // 定義模型路由策略
    const pickModel = (task: string) => {
      if (task === "summary") return "gpt-4.1-mini";
      if (task === "sql") return "gpt-4.1";
      return "gpt-4o";
    };
    
    // 中央統一的生成函數
    export async function generateText(req: {
      task: "summary" | "sql" | "general";
      input: string;
      userId: string;
    }) {
      const model = pickModel(req.task);
    
      // observability: 附上 trace metadata
      const traceMeta = {
        userId: req.userId,
        task: req.task,
        model,
      };
    
      return ai.generate(
        {
          model,
          prompt: req.input,
        },
        {
          // middleware hooks: 可插 retry / cache / logging
          trace: traceMeta,
        }
      );
    }
    

    在 Genkit middleware hooks 中加入 cache / retry / 安全策略(概念程式):

    // middleware.ts
    import { registerMiddleware } from "@genkit-ai/core";
    
    registerMiddleware(async (ctx, next) => {
      const key = hashPrompt(ctx.request);
    
      // 1. Cache 命中
      const cached = await cacheGet(key);
      if (cached) {
        ctx.log.info("cache-hit", { key });
        return cached;
      }
    
      // 2. 成本與速率限制
      await enforceQuota(ctx.trace.userId, ctx.request.model);
    
      // 3. 安全策略:例如 DLP 檢查
      await checkPromptPolicy(ctx.request.prompt);
    
      // 4. 重試包一層
      return retry(async () => {
        const res = await next();
        await cacheSet(key, res);
        return res;
      }, { retries: 2, backoffMs: 200 });
    });
    

    前端 / 服務層就只呼叫 generateText,完全不碰 openai.chat 類 API。未來換模型、加灰階、接別家供應商,只要改控制層即可。

    範例二:Node + Vercel AI Gateway 作為集中入口

    Vercel AI Gateway 提供 統一 endpoint + 多模型路由 + 速率限制 + 規則引擎,很適合當外部控制層。

    基本設定(vercel.json 或 UI 設定):

    • 建立一個 Gateway route,例如:https://ai.yourdomain.com/v1/chat。
    • 配置 providers:OpenAI / Anthropic / 自建模型。
    • 寫 routing rule:
    • if (task == 'summary') -> openai:gpt-4.1-mini
    • if (tenant == 'premium') -> anthropic:claude-3.7

    前端程式碼(Next.js 伺服器端)只需要呼叫單一 gateway:

    // app/api/ai/route.ts
    import { NextRequest, NextResponse } from "next/server";
    
    export async function POST(req: NextRequest) {
      const body = await req.json();
    
      const res = await fetch(process.env.AI_GATEWAY_URL!, {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
          "X-Tenant": body.tenantId,
          "X-Task": body.task,
        },
        body: JSON.stringify({
          messages: body.messages,
          stream: body.stream ?? false,
        }),
      });
    
      // 這裡可以加 audit log / trace id
      return new NextResponse(res.body, { status: res.status });
    }
    

    優點:

    • 速率限制、成本統計、提供者 failover 直接用 Vercel 的 console 配。
    • 灰階與回滾:改 routing rule 即可,比如 10% 流量導到新模型。

    💡 關鍵: 把流量管理與模型路由交給 gateway,可用設定檔與後台調整,而不必每次動到應用程式碼

    範例三:Python 控制層 + 記憶治理(Memory Curator)

    假設你在做多代理系統,希望 worker agent 不能直接寫入向量庫。

    # ai_control_layer.py
    from dataclasses import dataclass
    from typing import Literal, Dict, Any
    
    Scope = Literal["agent", "team", "project", "session"]
    
    @dataclass
    class MemoryEvent:
      scope: Scope
      content: str
      evidence: Dict[str, Any]
      actor: str  # 哪個 agent
    
    class MemoryCurator:
      def __init__(self, store):
        self.store = store
    
      def decide(self, event: MemoryEvent):
        # 簡單篩選:過長、含敏感資訊則拒絕
        if len(event.content) > 2000:
          return "discard"
        if "password" in event.content.lower():
          return "discard"
    
        # 不同 scope 寫不同 index
        index_name = {
          "agent": f"agent-{event.actor}",
          "team": "team-shared",
          "project": "project-global",
          "session": None,  # 只放在 runtime context
        }[event.scope]
    
        if index_name:
          self.store.write(index_name, event.content, event.evidence)
          return f"written:{index_name}"
        else:
          # session-only: 不寫 durable store
          return "session-only"
    
    # worker agent 不直接調 store
    curator = MemoryCurator(store=vectordb)
    
    async def worker_store_memory(proposed_content: str, actor: str):
      event = MemoryEvent(
        scope="project",
        content=proposed_content,
        evidence={"source": "task_result"},
        actor=actor,
      )
      result = curator.decide(event)
      return result
    

    在這個架構下:

    • 所有記憶寫入 都經過 MemoryCurator,可審計、可控。
    • 之後要加 審計 log / OpenTelemetry span 也只改這一處。

    建議與注意事項:常見踩坑與實戰指引

    1. 不要把控制層寫成巨大 God Service

    常見錯誤:

    把所有邏輯(模型路由、prompt 模板、tool orchestration、記憶管理、審批流)塞進同一個 ai_service.ts。

    結果:

    • 難以測試、難以灰階、任何小改都要全 redeploy。
    • 無法對特定職責做獨立 scaling(例如工具呼叫爆量時)。

    建議:依職責拆模組,例如:

    • ModelRouter
    • ToolRegistry
    • RetryPolicy
    • QuotaManager
    • MemoryCurator

    控制層本身只是一個 薄 orchestration 層,組裝這些模組,不要變成 monolith。

    2. 一開始就設計可觀測性 schema

    很多團隊是到事故發生後才加 log,結果 schema 雜亂無章,完全無法統計。

    最低限度,請在控制層統一定義以下欄位:

    • trace_id / span_id:串起同一個 user request 下的所有模型與工具呼叫。
    • user_id / tenant_id / feature_name:方便做成本報表與配額控制。
    • model_name / provider / tokens_in / tokens_out / latency_ms:跑出模型性能與成本比較。
    • cache_hit / retry_count / policy_blocked (bool):分析策略的實際效果。

    搭配 OpenTelemetry:

    • 在 control layer 的入口建立 root span。
    • 每個 model_call、tool_call、memory_write 建子 span。
    • 把上述欄位當 attributes 寫入。

    3. 灰階與回滾機制不要事後補

    Anthropic 的觀察顯示:非程式碼型 agent 在 production 常常因為資料與流程難控而失敗。這不是模型問題,而是缺乏 安全試錯機制。

    在控制層預先設計:

    • 灰階發布:例如新增模型時,只讓 5% 的流量使用;表現不好立即切回。
    • 可以在 Vercel AI Gateway 或內部 router 實作簡單的百分比路由。
    • 策略回滾:策略(例如更嚴格的 DLP、不同記憶寫入規則)要抽成 可配置,而不是寫死在程式裡。
    • 配合 feature flag 平台(LaunchDarkly 等)效果更佳。

    4. 把 audit trail 當成產品需求,不是附加功能

    對多步 agent 流程,請明確要求:

    • 每一步 「想做什麼 → 實際做了什麼 → 結果如何」 都寫 audit log。
    • 在 UI(或 admin console)內提供「代理執行歷史」頁面,讓使用者能依 trace id 看到整條鏈。

    這件事最好放在控制層完成:

    • 所有 tool_call 都經過控制層。
    • 控制層負責序列化成統一格式:

    json
    {
    "trace_id": "...",
    "step": 3,
    "actor": "billing_agent",
    "tool": "update_invoice",
    "input": {"invoice_id": 123},
    "output": {"status": "ok"},
    "started_at": "...",
    "duration_ms": 532
    }

    這直接提升企業客戶的信任度,也方便日後做合規稽核。


    總結: 如果你的專案準備從「demo 給老闆看」變成「真的要上線給客戶用」,請先停下來,把所有 LLM / Tool / Agent 呼叫集中收斂到一個 AI Middleware 控制層。路由、觀測、策略、記憶治理都放在這裡,才能控制成本、降低事故、加快迭代速度。

    🚀 你現在可以做的事

    • 整理專案中所有 openai.chat / fetch('llm') 呼叫點,設計一個統一的控制層介面(如 generateText())
    • 在控制層導入基本的 trace_id、user_id、model_name log schema,並串接 OpenTelemetry 或現有 APM
    • 為現有的多代理或 RAG 系統設計一個 MemoryCurator 類別,強制所有記憶寫入都經過同一治理入口
  • 把 Claude 變成你的主力工作代理人

    把 Claude 變成你的主力工作代理人

    📌 本文重點

    • 把 Claude 當「可配置代理人」,而非一次性問答工具
    • 善用 Claude.md、Skills、Subagents、Plugins 建立工作流程
    • 針對身份配置專屬 workflow,讓重複工作自動化

    一句話定位:不是再多寫一個 prompt,而是把 Claude 配好「記憶+流程+工具」,變成每天幫你跑任務的主力工作代理人。


    核心功能:先理解這 4 個積木

    目標:看完這一節,你要能說出「Claude 現在在哪裡記東西、怎麼做事、怎麼叫外部工具」。


    1. Claude.md:給代理人一個「長期腦袋」

    • 角色:你的私人知識庫與工作說明書(system prompt 的升級版)。
    • 放什麼:
    • 你的背景(職位、產業、常用技術棧)
    • 任務偏好(寫作語氣、程式碼風格、專案管理習慣)
    • 長期專案的關鍵資訊、慣用模板
    • 使用效果:每次新對話,Claude 自動帶著這份設定,不用再重複解釋自己。

    💡 關鍵: 善用 Claude.md,可以一次設定、長期沿用,省去每次從零解釋背景與偏好的時間成本。

    可以馬上做的事:

    1. 到 claude.ai 登入。
    2. 在設定或 Workspace 設定裡找到 Claude.md 或類似「AI 配置檔」。
    3. 寫第一版內容(建議結構):

    “`markdown
    # 關於我
    – 角色:B2B SaaS 產品經理
    – 常用語言:繁體中文 + 英文技術文件

    # 工作偏好
    – 寫作:偏實用教學,少形容詞,多步驟
    – 文件格式:盡量用 Markdown,標題層級清楚

    # 長期專案
    – 專案 A:AI 產品內部知識庫
    – 專案 B:每週技術選型評估報告
    “`

    1. 日後只要養成習慣:有長期沿用的規則/資訊,就補進 Claude.md,而不是每次對話再講一次。

    2. Skills:可重用的「任務模板」

    • 角色:把你常做的工作,變成一顆一鍵呼叫的小程式。
    • 適合類型:
    • 每週重複的報告(例:每週產品更新摘要)
    • 固定格式的輸出(例:Code review 指南、文章大綱格式)
    • 需要多步驟的任務(例:先讀文件 → 列出問題 → 提出修改建議)

    一個簡單 Skill 範例:專案讀書筆記器

    邏輯:給 Claude 一篇文章連結或貼上內容,Skill 幫你產出固定結構的筆記。

    Skill 內容可以長這樣(概念示意):

    # 技能名稱:research_note
    
    ## 功能
    將一篇文章整理成結構化研究筆記。
    
    ## 輸入
    - `content`: 文章全文或重點摘錄
    
    ## 任務步驟
    1. 先用 3 句話摘要內容。
    2. 條列:關鍵概念、重要數據、引用來源。
    3. 給出:
       - 對我目前專案的啟發
       - 後續可以延伸研究的 3 個問題
    
    ## 輸出格式
    使用 Markdown:
    - 概覽
    - 關鍵概念
    - 數據與引用
    - 對專案的啟發
    - 後續問題
    

    可以馬上做的事:

    1. 在 Claude 介面中找到 Skills(通常在左邊或設定的 Skills / Tools 區)。
    2. 新增一個 Skill,把上面範例貼進去並調整成你的語氣與領域。
    3. 之後看到文章,直接對 Claude 說:

    用 research_note 幫我整理這篇,內容在下面:


    3. Subagents:把大任務拆給專職小幫手

    • 角色:一個大代理人(你平常在聊的 Claude),底下可以派出「專職分身」。
    • 適合情境:
    • 寫作:資料研究 agent、結構調整 agent、潤稿 agent 各司其職
    • 工程:debug agent、測試覆蓋率 agent、文件撰寫 agent
    • 專案管理:需求整理 agent、風險盤點 agent、會議紀錄 agent

    參考 多代理系統實作文章 的做法,你可以這樣配置:

    • ResearchAgent:只負責找資料、整理重點,不下結論。
    • WriterAgent:只根據整理好的重點寫初稿。
    • EditorAgent:只負責風格、語氣、構優化。

    可以馬上做的事:

    1. 在 Skills 區先各自為 research、write、edit 建立對應 Skill。
    2. 建一個「總控 Skill」,流程像這樣:

    markdown
    1. 呼叫 ResearchAgent Skill,輸入主題。
    2. 將輸出傳給 WriterAgent Skill,生成草稿。
    3. 將草稿丟給 EditorAgent Skill,優化文稿。

    1. 你對 Claude 下命令就只剩一句:

    用你的寫作工作流幫我處理這個主題:XXX

    這呼應了「Plan 模式比一次搞定更重要」的思路(可參考 這篇 Plan mode 深入解析)。


    4. Plugins / MCP:把 Notion、GitHub、日曆接進來

    • Plugins(或 MCP 伺服器):讓 Claude 能「去別的服務做事」,而不是只在聊天室輸出文字。
    • 常見接法:
    • Notion:讀寫頁面、幫你整理研究筆記
    • GitHub:查 issue、看 PR、寫 review 建議
    • 日曆:生成待辦、排會議時間

    可以馬上做的事:

    1. 在 Claude 設定中找到「Plugins」或「MCP」管理頁面。
    2. 授權你常用的工具(例如 Notion / GitHub)。
    3. 測試一個實用指令:
    4. Notion:

      幫我把這篇研究筆記整理成 Notion 新頁面,放在資料庫「AI Research」底下。

    5. GitHub:

      幫我看這個 PR,先列出潛在風險,再寫一段給同事看的 review。連結在這裡:XXX


    適合誰用:3 種典型配置範例

    目標:對照自己的身份,直接抄一套設定起來。


    1. 個人工作者:寫作 / 研究 / 專案管理

    建議配置:

    • Claude.md:
    • 明確定義你的寫作風格、常用結構(例如所有教學文都用「背景 → 步驟 → 範例 → 常見錯誤」)。
    • Skills:
    • research_note:文章/論文筆記
    • outline_builder:輸入主題,自動給 2–3 個不同角度的大綱
    • meeting_minute:貼原始會議筆記,輸出「決策/待辦/風險」三欄
    • MCP / Plugins:
    • Notion 或 Obsidian 相關工具,讓筆記自動進入你的知識庫。

    日常 workflow 範例:

    1. 丟 3–5 篇相關資料給 Claude,用 research_note 各自整理。
    2. 用 outline_builder 產出文章大綱,選一版改一改。
    3. 完稿後請 Claude 依指定格式,寫成 Notion 新頁面並歸檔。

    2. 工程師:Code Review + 多 repo 協作

    建議配置:

    • Claude.md:
    • 寫清楚專案技術棧、程式碼風格規範、測試原則。
    • Skills:
    • code_review:給 PR 連結或 patch,輸出:問題清單、風險點、建議 commit 清單。
    • test_case_generator:根據函式/API,幫你列出缺的測試案例。
    • Subagents:
    • ArchitectureAgent:只看架構與邏輯。
    • StyleAgent:只看命名、風格、一致性。
    • MCP / Plugins:
    • GitHub / GitLab 工具,直接讀 PR diff、issue 歷史。

    日常 workflow 範例:

    1. 對 Claude 說:

    幫我用你的 code_review 工作流檢查這個 PR:XXX

    1. Claude 讀 GitHub diff,先由 ArchitectureAgent 看設計,再由 StyleAgent 補充風格問題,最後合併成一份可直接貼回 PR 的 review。

    3. 小團隊:共用一組技能與工作流

    建議配置:

    • 共用 Workspace 的 Claude.md:
    • 放團隊寫作規範、技術決策原則、產品定位。
    • 共用 Skills:
    • weekly_update:所有人都用同一個模板輸出週報。
    • spec_template:PRD / 技術規格文件的標準結構。
    • MCP / Plugins:
    • 共用的 Notion、Jira、GitHub 專案權限。

    操作方式:

    1. 由一人負責整理團隊的 Claude.md 和 Skills。
    2. 其他成員只需要:
    3. 在週報時輸入:

      用 weekly_update 幫我整理這週的進度,重點在 XXX。

    4. 寫新功能時:

      用 spec_template 幫我產生這個功能的 PRD 初稿,功能說明如下:XXX。


    一小時內用起來:實戰 workflow 示範

    目標:照這段做完,你就有「從資料收集 → 產出大綱 → 自動整理到知識庫」的一套日常代理人流程。


    Step 1:開啟並寫好第一版 Claude.md(10 分鐘)

    1. 打開 claude.ai → 設定 → Claude.md。
    2. 貼入這個模板再依需求修改:

    “`markdown
    # 關於我
    – 角色:__
    – 主要領域:
    ____

    # 工作偏好
    – 回覆語言:繁體中文
    – 文件格式:預設使用 Markdown
    – 風格:先給結論,再拆解步驟

    # 長期專案
    – 專案 1:__(簡述目的與目前進度)
    – 專案 2:
    ____
    “`


    Step 2:建立第一個 Skill:研究筆記(10 分鐘)

    1. 到 Skills 管理頁,新增 Skill research_note。
    2. 使用前面提供的 research_note 範本,改成你的領域語氣。
    3. 找一篇你最近在看的文章,把內容貼給 Claude,指定使用 research_note。

    Step 3:讓 Subagent 接手子任務:大綱生成(15 分鐘)

    1. 新增 Skill outline_builder,內容類似:

    “`markdown
    # 技能名稱:outline_builder

    ## 功能
    針對一個主題,產生 2–3 個不同角度的大綱,每個大綱最多 5 個主標。

    ## 步驟
    1. 簡要理解主題與目標讀者。
    2. 提出 2–3 種切入角度。
    3. 針對每種角度,列出主標與一句說明。
    “`

    1. 建立一個「寫作工作流 Skill」,流程:
    2. 先呼叫 research_note 消化資料。
    3. 再把研究結果摘要丟給 outline_builder。
    4. 你只要給主題 + 資料清單,就能一次拿到整理後的研究+多版本大綱。

    💡 關鍵: 把 research_note + outline_builder 串成 workflow,一次輸入主題就能從資料整理到多版本大綱,大幅降低起稿門檻。


    Step 4:接上 Notion,自動寫入知識庫(15–25 分鐘)

    1. 在 Plugins / MCP 介面中,啟用 Notion 並完成授權。
    2. 建一個 Notion 資料庫,叫做「AI 研究庫」。
    3. 對 Claude 下指令:

    從現在開始,所有用 research_note 產出的內容,幫我整理成 Notion 新頁面,放在「AI 研究庫」,標題用日期+主題。

    1. 測試一次完整流程:
    2. 丢一篇文章 → 用 research_note 整理。
    3. 要求 Claude 寫入 Notion。
    4. 打開 Notion 確認格式與欄位是否符合期待,必要時微調規則。

    延伸工具:如果想把多個 AI 輸出集中管理

    如果你平常 Claude / ChatGPT / Gemini 都會用,可以考慮配合像 Coffer 這類工具,把所有 AI 回覆存進一個可搜尋的 vault(原作者分享在 Reddit)。

    名稱 核心功能 免費方案 適合誰
    Claude 主力工作代理人:Claude.md + Skills + MCP 有 想把 AI 當長期工作夥伴的人
    Coffer 儲存多家 AI 回應到本地可搜尋知識庫 有 重度問 AI、怕內容散佈的人

    💡 關鍵: 把多家 AI 的回應集中到同一個可搜尋知識庫,可以避免資訊分散在不同聊天裡,長期累積成真正可用的資產。


    只要先把 Claude 當成「可以配置的代理人」,而不只是「一次性的問答工具」,從 Claude.md、Skills 到 Subagents、Plugins 一步步搭起來,你就會開始感受到:每天重複的工作,有越來越多可以丟給它接手。

    🚀 你現在可以做的事

    • 登入 claude.ai,建立並填好你的第一版 Claude.md
    • 新增一個 research_note Skill,實際拿一篇文章讓 Claude 幫你整理研究筆記
    • 啟用 Notion 或 GitHub Plugin,測試一次「從 Skill 輸出到外部工具」的完整工作流
  • 96 個 Gemini Agent 幫你寫系統?

    96 個 Gemini Agent 幫你寫系統?

    📌 本文重點

    • 多 Agent 可複製「多人分工」開發流程
    • Runtime 設計比單純換更大模型更關鍵
    • 用開源工具就能打造迷你版 Antigravity

    用一群 Agent 代替「一個工程師慢慢寫」,解決的是:複雜專案要靠多人分工,AI 也可以用 Runtime + 多代理系統做到同樣的協作和自動化。

    核心觀念先講白:模型戰爭差不多打完了,現在比的是誰的 Agent Runtime 能把「模型能力」變成可落地的、多步驟的自動化工作流。

    Google 在 I/O 上展示的 Antigravity 2.0 + Gemini 3.5 Flash 是一個很極端的例子:

    • 96 個子代理分工
    • 12 小時寫完一套從零開始的作業系統
    • Token 成本不到 1,000 美金
    • OS 還能跑《Doom》

    💡 關鍵: 多代理 + 強 Runtime 已經能在「12 小時、不到 1,000 美金」內完成從零開發 OS,顯示關鍵瓶頸不再是模型本身,而是協作與流程設計。

    這不是叫你明天也去做一個 OS,而是提供一個「如何設計多 Agent 開發流程」的範本。下面我們拆成三件你可以直接抄的事:

    1. 多代理分工設計:任務 → 子任務 → Agent 編隊
    2. 強健 Runtime:重試、檢查點、錯誤恢復
    3. 平價版本實作:在你自己的專案做一個「迷你 Antigravity」

    核心功能:Antigravity 2.0 給開發者的三個啟示

    1. 多代理分工:任務 → 子任務 → Agent 編隊

    Antigravity 的做法,其實很像你帶一個遠端工程團隊:

    • 架構師 Agent:決定 OS 的模組切分(檔案系統、排程、驅動、UI…)
    • 模組作者 Agent:各自負責某一個模組的程式碼生成
    • 測試員 Agent:寫測試、跑測試、收斂錯誤
    • 整合者 Agent:把各模組組合、處理相依性、打包成可啟動的系統

    對你來說,可直接套用成一個通用流程:

    1. 寫一個頂層任務描述
      例:建立一個 RESTful CRUD 服務,管理任務(待辦事項),含 API、DB schema、簡單前端。

    2. 讓「架構師 Agent」自動拆解:

    3. API 設計與 OpenAPI spec

    4. 後端框架與資料庫層
    5. 前端 UI
    6. 測試與 CI script

    7. 為每個子任務設計 Agent 角色:

    8. api-architect-agent:只產出 API spec

    9. backend-agent:根據 spec 產生程式碼
    10. frontend-agent:負責 UI
    11. tester-agent:生成並執行測試
    12. integrator-agent:檢查專案結構、跑 build / lint

    13. 在 Runtime 中定義工作流:

    14. 任務圖(DAG):架構師 → 模組作者 → 測試員 → 整合者

    15. 每個節點定義輸入/輸出檔案、工具(Git、DB、HTTP client)

    可行動步驟:

    • 選一個你熟的框架(例如 FastAPI / Next.js)
    • 用自然語言寫清楚「最終可交付物」
    • 為這個專案定義 3–5 個 Agent 角色,明確限制各自輸入輸出

    2. Runtime 比模型重要:90% 成功率在多步任務會變災難

    多步任務有一個殘酷數學:

    • 假設每一步成功率 90%
    • 要跑 20 步,整體成功率 ≈ 0.9^20 ≈ 12%

    💡 關鍵: 即使單步有 90% 成功率,20 步工作流成功率只剩約 12%,所以不加 Runtime 管控,多步任務幾乎註定失敗。

    這就是為什麼像 Forge 這種開源 guardrails 會被重視:作者實測,一個 8B 模型在多步代理任務上,從 53% 提到 99% 成功率,完全不改模型,只改 Runtime。

    你在自己的「迷你 Antigravity」裡,要做三件事:

    1. 重試與 nudging

    2. 為每個步驟設 max_retries(例如 3 次)

    3. 失敗時自動加上「修正提示」,例如:上一步測試失敗,錯誤訊息如下,請修正而不是重寫整檔。

    4. 檢查點(checkpoint)

    5. 每完成一個重要子任務,就把中間產出存到 Git / DB

    6. 失敗時從最近的檢查點重跑,而不是重頭來

    7. 錯誤恢復流程

    8. 專門的 debug-agent:只看錯誤訊息 & log,產出修復建議

    9. Runtime 層做:自動建立 bug report、開 issue、指派給對應 Agent

    如果你用 Forge,它已內建:

    • Tool-agnostic 重試策略
    • 步驟執行強制與錯誤恢復
    • VRAM-aware context 管理(對本地模型很重要)
    • 評估套件與 Dashboard,可量化成功率

    可行動步驟:

    • 先把現有「單 Agent 自動流程」改成有重試與 checkpoint
    • 對每個任務記錄:總步數、失敗點、重試次數,在 Dashboard 裡看瓶頸

    3. 平價版本:你也能做一個「迷你 Antigravity」

    你不需要 Gemini 3.5 Flash + Google 內部 Runtime 才能玩多 Agent。下面這些工具可以在自家專案做一個縮小版:

    名稱 核心功能 免費方案 適合誰
    Forge 多步代理 guardrails、重試、Dashboard 開源 想提升本地 / 自架 LLM 可靠性的工程師
    llama.cpp + Qwen 本地 Agent 在個人電腦跑本地模型 + 簡易工具調用 開源 想省雲端費用、在內網跑 Agent 的團隊
    MCP 生態(如 OpenAI MCP、各種 server) 統一的工具協議,讓 Agent 調用資料庫、API 等 多數開源 / 免費 想把既有系統暴露為 Agent 工具的後端工程師

    一個實用組合示例:

    • 模型:Qwen 2.5 7B / 14B(透過 llama.cpp 或 Ollama 跑)
    • Runtime:Forge 當 guardrails
    • 工具層:一組 MCP server(例如 PostgreSQL、HTTP、Filesystem)

    你可以先做一個「自動搭建 CRUD 服務」的迷你 Antigravity:

    1. 使用者輸入需求(自然語言)
    2. architect-agent 產出設計 + 任務拆解
    3. backend-agent + frontend-agent 寫程式碼
    4. tester-agent 自動開發 & 執行測試
    5. integrator-agent 跑 build 並回報狀態

    適合誰用:三種典型場景

    1. 後端 / 全端工程師:自動化 CRUD 小專案

    你可以把「打造新微服務」變成一個表單:

    • 輸入資料模型 + 幾個業務規則
    • 多 Agent 流程負責 scaffold、API、測試、docker-compose

    行動:從一個只需要 3–5 小時就能手刻完的小服務開始,先讓多 Agent 幫你做到 70–80%,你只負責 code review。


    2. 資料團隊:資料管線與 ETL 任務

    • planner-agent:解析需求、拆成抽取/轉換/載入步驟
    • sql-agent:產生查詢與 view
    • check-agent:比對 row count、品質指標

    行動:挑一個每天都在重複手動跑的 ETL 任務,做成標準流程,讓 Agent 幫你自動生成 SQL + 驗證報表。


    3. 產品 / PM:快速驗證 Side Project

    • 搭配 Gemini 3.5(雲端)或本地 LLM
    • 定義一個「最小可行功能」(例如 landing page + 簡單 API)
    • 用多 Agent 完成第一版,再丟給工程師接手

    行動:每次新點子,給自己一個規則:「先讓多 Agent 寫一版 Demo,我只在最後 2 小時調整。」


    怎麼開始:一個最小可行範例

    這裡給一條「3–5 小時內可完成」的路線,你可以直接照做:

    步驟 1:選模型 + Agent 框架

    • 模型:
    • 想省錢/本地:Qwen 2.5 7B(透過 llama.cpp 或 Ollama)
    • 想雲端無痛:Gemini 3.5 Flash(透過 Google AI Studio)
    • Runtime / 框架:
    • 想要 guardrails:裝 Forge
    • 想用現成 MCP:選一個支援 MCP 的 Agent 框架(如 OpenAI 官方 Agent SDK)

    步驟 2:挑一個小系統

    條件:

    • 單服務、沒有第三方整合
    • 你自己寫大約 3–5 小時能完成

    例:任務管理 CRUD API + 簡單 React 前端

    步驟 3:設計任務拆分與 Agent 角色

    1. 任務描述寫成一個 markdown 檔(會給 architect-agent 看)
    2. 在 Runtime 中註冊 4 個 Agent:
    3. architect
    4. backend
    5. frontend
    6. tester/integrator
    7. 為每個 Agent 明確:
    8. 可用工具(Git、Filesystem、HTTP…)
    9. 輸入(上一個 Agent 的輸出 / 檔案)
    10. 必須產出什麼檔案

    步驟 4:加上監控 Dashboard

    • 如果用 Forge:直接啟用它的 Dashboard,看每次工作流的步驟成功率
    • 若自己實作:
    • 為每個步驟記錄:開始時間、結束時間、是否重試
    • 每次失敗時存 log + 輸入輸出到一個資料夾

    步驟 5:只做一件事的迭代

    • 第一版只要求「能跑起來」,不追求漂亮結構
    • 每次失敗,你只調整:
    • 任務拆分是否太粗/太細
    • Agent 提示是否太模糊
    • 重試與 checkpoint 是否設太少

    等到這個小系統穩定後,你才讓 Runtime 去碰更大的專案。


    小結:Runtime 是你的「AI 開發主管」

    Antigravity 2.0 用 96 個 Gemini Agent 寫出一套能跑《Doom》 的 OS,看起來很遠,但背後用到的概念其實都可落在你今天的 side project 上:

    • 把任務拆成 Agent 可接手的小單位
    • 用 Runtime 管控流程,而不是寄望模型每次都猜對
    • 利用開源工具(Forge、llama.cpp、MCP)做出自己的「迷你 Antigravity」

    💡 關鍵: 關鍵不是再換一個更大的模型,而是把現有模型放進可靠的 Runtime,讓它真的「交付」可用產物。

    關鍵不是再換一個更大的模型,而是先把你手上的模型,放進一個可靠的 Runtime 裡,讓它真的幫你「交付」東西。

    🚀 你現在可以做的事

    • 在 GitHub 上看看 Forge 專案,了解多步代理的 guardrails 怎麼設計
    • 挑一個 3–5 小時能手刻完的 CRUD 小服務,照文中的 4 個 Agent 角色拆任務實作一次
    • 把既有的單 Agent 自動化腳本,加上 max_retries 和簡單 checkpoint 機制,量化成功率變化
  • 12-Factor Agents 實戰:讓 Agent 真正上得了線

    12-Factor Agents 實戰:讓 Agent 真正上得了線

    📌 本文重點

    • LLM/Agent 要先抽象成可替換依賴
    • Prompt/Tool/Memory 行為必須版本化與可回滾
    • 觀測性與成本控管是上線前必備基礎
    • 單體腳本可漸進重構為 12-Factor Agents

    多數 Agent demo 都卡在「好酷,但不敢上線」。12-Factor Agents 的目的,就是把 LLM/Agent 拉回正常軟體工程軌道:
    – 不被單一模型綁死,支援熱切換與灰度升級
    – prompt / tool / memory 都能 versioning + 測試 + rollback
    – 有 token-level log、decision trace,出問題找得到責任點

    下面用 12-Factor 觀念,拆成對工程實作有幫助的 4 個面向,最後用一個簡單 multi-agent pipeline 示範如何重構。


    重點說明

    1. 把 LLM/Agent 抽象成可替換的依賴

    核心做法:不要在業務程式碼裡直接綁某個模型 API,而是統一經過一層 LLMClient / AgentRuntime。

    關鍵能力:
    – 用 model alias(如 report-writer@v2)取代具體 gpt-4.1-mini / claude-3.7
    – 支援 routing 策略:A/B test、流量分配、fallback
    – 對外只暴露 統一介面:complete() / chat() / stream()

    // llm-registry.ts
    export type ModelAlias = 'planner@v1' | 'crawler@v1' | 'analyst@v2';
    
    interface LLMConfig {
      provider: 'openai' | 'anthropic' | 'local';
      model: string;
      maxTokens: number;
      temperature: number;
    }
    
    const REGISTRY: Record<ModelAlias, LLMConfig> = {
      'planner@v1': { provider: 'openai', model: 'gpt-4.1-mini', maxTokens: 1024, temperature: 0.2 },
      'analyst@v2': { provider: 'anthropic', model: 'claude-3.7', maxTokens: 2048, temperature: 0.1 },
      'crawler@v1': { provider: 'local', model: 'llama-3-8b', maxTokens: 512, temperature: 0.3 },
    };
    
    export function getLLMConfig(alias: ModelAlias): LLMConfig {
      return REGISTRY[alias];
    }
    

    業務端只拿 alias:

    // llm-client.ts
    export async function complete(alias: ModelAlias, messages: ChatMessage[]): Promise<string> {
      const cfg = getLLMConfig(alias);
      const client = getProviderClient(cfg.provider); // 封裝 OpenAI / Anthropic SDK
    
      const res = await client.chat({
        model: cfg.model,
        messages,
        max_tokens: cfg.maxTokens,
        temperature: cfg.temperature,
      });
    
      return res.output;
    }
    

    好處:
    – 模型升級只改 registry config,不用全 repo 改 model: 'xxx'
    – 可以針對某 alias 做 灰度發布:10% 流量走新模型

    💡 關鍵: 透過 model alias 把模型細節藏在 registry,可以在不動業務程式碼的前提下做灰度升級與快速回滾。

    2. Prompt / Tool / Memory:行為配置要能 versioning + rollout

    對 Agent 而言,行為大多來自「配置」,而不是 code:
    – system prompt
    – tool schema / API 介面
    – memory 策略(context window、摘要邏輯)

    建議把這些都變成 宣告式 config,並且:
    – 每個 Agent 一個 behavior version:planner@v1.3
    – 行為改動先跑 離線回放測試 + 小流量試 run

    # configs/agents/planner.v1.3.yaml
    name: planner
    version: v1.3
    model_alias: planner@v1
    system_prompt: |
      你是一個專門規劃網站資料收集與分析的技術 PM。
      - 只產出結構化 JSON
      - 不要寫多餘文字
    
    output_schema:
      type: object
      properties:
        crawl_targets:
          type: array
          items:
            type: object
            properties:
              url: { type: string }
              depth: { type: integer, maximum: 2 }
              notes: { type: string }
    
    memory:
      type: redis
      ttl_seconds: 3600
      key_prefix: planner_session_
    

    載入時明確綁定行為版本:

    // agent-loader.ts
    interface AgentSpec {
      name: string;
      version: string;      // e.g. v1.3
      modelAlias: ModelAlias;
      systemPrompt: string;
      outputSchema: JSONSchema;
    }
    
    export function loadAgentSpec(name: string, version: string): AgentSpec {
      const path = `configs/agents/${name}.${version}.yaml`;
      const raw = fs.readFileSync(path, 'utf8');
      const cfg = yaml.parse(raw);
      return {
        name: cfg.name,
        version: cfg.version,
        modelAlias: cfg.model_alias,
        systemPrompt: cfg.system_prompt,
        outputSchema: cfg.output_schema,
      };
    }
    

    好處:
    – prompt 調整可以 像發版一樣受控,支援 rollback
    – tool schema 變更(新增欄位、型別改動)有明確 diff,避免隱性 breaking change

    💡 關鍵: 把 prompt、tool、memory 行為寫進版本化 config,可以像管理程式碼一樣管控變更與回滾。

    3. Observability:token-level log + decision trace + retry 策略

    傳統 APM 看不到「LLM 想了什麼」。受《The Rise of Cognitive Observability》啟發,建議:

    1. token-level log / cost log:每次 call 記錄 prompt_tokens、completion_tokens、cost_usd。
    2. decision trace:multi-agent 流程中,記下每一步的:
    3. input
    4. output
    5. 使用的 model / behavior version
    6. tool 呼叫與對應結果
    7. 分類錯誤:
    8. infra error(timeout、rate limit)→ 可以 retry
    9. cognitive error(推理錯誤、亂寫 schema)→ 需要 prompt/tool 設計調整
    // observability.ts
    export async function tracedLLMCall(params: {
      agent: string;
      behaviorVersion: string;
      modelAlias: ModelAlias;
      messages: ChatMessage[];
      spanId: string;
    }) {
      const start = Date.now();
      try {
        const res = await rawProviderCall(params.modelAlias, params.messages);
    
        logToWarehouse({
          span_id: params.spanId,
          agent: params.agent,
          behavior_version: params.behaviorVersion,
          model_alias: params.modelAlias,
          latency_ms: Date.now() - start,
          prompt_tokens: res.usage.prompt_tokens,
          completion_tokens: res.usage.completion_tokens,
          cost_usd: estimateCost(res.usage, params.modelAlias),
          raw_output: res.output,
        });
    
        return res.output;
      } catch (e) {
        logError({ span_id: params.spanId, agent: params.agent, error: e });
        throw e;
      }
    }
    

    重試策略:

    export async function withRetry<T>(fn: () => Promise<T>, opts = { maxAttempts: 3, backoffMs: 500 }) {
      let lastErr;
      for (let i = 0; i < opts.maxAttempts; i++) {
        try { return await fn(); } catch (e: any) {
          lastErr = e;
          if (!isInfraError(e)) break; // 認知錯誤不要盲目重試
          await sleep(opts.backoffMs * (i + 1));
        }
      }
      throw lastErr;
    }
    

    4. Multi-Agent 任務重構:規劃 → 爬蟲 → 分析 → 報告

    目標:把一個看似「單體腳本」的 agent 流程,拆成可觀察、可恢復的 pipeline。

    服務切分:
    – planner-service:輸入題目 → 輸出 crawl plan
    – crawler-service:依照 plan 用傳統爬蟲抓 HTML / 文本
    – analyst-service:對資料做分析與結構化結論
    – reporter-service:產出自然語言報告

    狀態管理:
    – 任務狀態存在 PostgreSQL 或 MongoDB:tasks、artifacts
    – 中間資料(暫存內容、短期記憶)放 Redis(key = task:{id}:stage)

    隊列與超時:
    – 使用 Redis Stream / Kafka / RabbitMQ 做 stage 間消息隊列
    – 每個 stage worker 有自己的 timeout + retry + DLQ(死信隊列)

    // pseudo: task Orchestrator
    async function runTask(taskId: string) {
      const spanId = newSpan();
    
      // 1) 規劃
      const plan = await withRetry(() => plannerAgent.run({ taskId, spanId }), { maxAttempts: 2 });
      await saveArtifact(taskId, 'plan', plan);
    
      // 2) 爬蟲(可能 fan-out 多個 URL)
      await enqueueCrawlJobs(taskId, plan.crawl_targets); // 放到 queue
    
      // 3) 等 crawler 全數完成,再觸發 analyst
      await waitForAllCrawls(taskId, { timeoutMs: 300_000 });
      const pages = await loadArtifacts(taskId, 'crawl_result');
    
      const analysis = await withRetry(() => analystAgent.run({ taskId, spanId, pages }), { maxAttempts: 2 });
      await saveArtifact(taskId, 'analysis', analysis);
    
      // 4) 報告
      const report = await reporterAgent.run({ taskId, spanId, analysis });
      await saveArtifact(taskId, 'report', report);
    
      await markTaskDone(taskId);
    }
    

    回退策略:
    – 某 stage 連續失敗 → 使用 上一個穩定 behavior version 重跑
    – 報告無法產出 → 回傳「部分完成」狀態 + 中間分析結果給前端呈現


    實作範例

    以下示範如何把「planner」 agent 做到可替換模型、可版本管理、可觀察的最小實作。

    1. Planner Agent 行為定義

    # configs/agents/planner.v1.0.yaml
    name: planner
    version: v1.0
    model_alias: planner@v1
    system_prompt: |
      你負責規劃完成使用者任務所需的爬蟲與分析步驟。
      僅輸出 JSON,符合 output_schema 定義。
    output_schema:
      type: object
      required: [crawl_targets]
      properties:
        crawl_targets:
          type: array
          items:
            type: object
            required: [url]
            properties:
              url: { type: string }
              depth: { type: integer, default: 1 }
              notes: { type: string }
    

    2. 執行 Planner Agent

    // planner-agent.ts
    import { loadAgentSpec } from './agent-loader';
    import { tracedLLMCall } from './observability';
    import Ajv from 'ajv';
    
    const ajv = new Ajv();
    
    export async function runPlanner(taskId: string, goal: string, spanId: string) {
      const spec = loadAgentSpec('planner', 'v1.0');
      const validate = ajv.compile(spec.outputSchema);
    
      const messages = [
        { role: 'system', content: spec.systemPrompt },
        { role: 'user', content: `任務說明:${goal}` },
      ];
    
      const raw = await tracedLLMCall({
        agent: spec.name,
        behaviorVersion: spec.version,
        modelAlias: spec.modelAlias,
        messages,
        spanId,
      });
    
      let json;
      try { json = JSON.parse(raw); } catch {
        throw new Error('planner_output_not_json');
      }
    
      if (!validate(json)) {
        throw new Error('planner_output_schema_mismatch');
      }
    
      await saveArtifact(taskId, 'plan', json); // 存 DB
      return json;
    }
    

    好處:
    – output 一旦 JSON 格式錯誤或 schema 不符合,會被明確標記為 cognitive error,方便後續調 prompt / schema
    – 透過 behaviorVersion 追蹤哪一版規劃器造成問題

    3. 成本暴衝防護

    // cost-guard.ts
    const MAX_COST_PER_TASK_USD = 0.5;
    
    export async function guardCost<T>(taskId: string, fn: () => Promise<T>): Promise<T> {
      const costSoFar = await getTaskCostUsd(taskId);
      if (costSoFar > MAX_COST_PER_TASK_USD) {
        throw new Error('task_cost_limit_exceeded');
      }
      const before = costSoFar;
      const res = await fn();
      const after = await getTaskCostUsd(taskId);
    
      if (after - before > 0.2) { // 單次呼叫超過 0.2 USD
        // 觸發告警
        emitAlert({ taskId, deltaCost: after - before });
      }
    
      return res;
    }
    

    把 tracedLLMCall 包在 guardCost 裡,就能防止 prompt 異常導致 token 疯狂膨脹。

    💡 關鍵: 設定 MAX_COST_PER_TASK_USD 與單次呼叫成本門檻,可以在成本暴衝前主動阻斷與告警。


    建議與注意事項

    1. 模型抽象層一定要一開始就設計好:
    2. 把 provider SDK 完全封裝起來(OpenAI、Anthropic、local),對業務端只暴露 統一型別。
    3. 不要在 service 裡直接用 openai.chat.completions.create 這種具體 API。

    4. Prompt / Tool 變更要像 schema migration 一樣看待:

    5. 每次變更必須 版本號 + changelog,否則 debug 會非常痛苦。
    6. tool 的欄位移除或語意改變,要視為 breaking change,需要同步更新所有使用該 tool 的 Agent。

    7. Observability 優先級要比「多搞幾個 Agent」高:

    8. 沒 trace,multi-agent 只會變成 多倍混亂。
    9. 最低限度:每一步的輸入、輸出、模型 alias、behavior version、token 用量都要記。

    10. 模型升級前先做 replay test:

    11. 從線上 log 抽一批真實任務,對舊模型與新模型跑一遍,對比:

      • 通過率(JSON parse、schema validate)
      • 任務完成率(可部分人工標註)
      • 成本差異
    12. 不要迷信 retry,可以只是讓錯誤變貴:

    13. infra error(timeout、429)才值得 retry
    14. cognitive error(結果不符合 schema / business rule)應該記錄下來,調整 prompt 或 tool,而不是盲目重試

    15. 從單體腳本往 12-Factor Agents 過渡的實務建議:

    16. 先做 3 件事:
      • 抽出 LLMClient 抽象層
      • 把 prompt / schema 拉到 config + Git 管理
      • 導入最小版 token cost log + decision trace
    17. 等這三件穩定後,再考慮拆成獨立 microservices 或 multi-agent pipeline。

    照著這套把 demo 重構一次,你會發現:
    – 模型換得比較放心
    – 成本能被預期
    – 最重要的是:Agent 行為變得「可觀察、可控」,才有資格進入生產環境。

    🚀 你現在可以做的事

    • 把現有專案中的 openai / anthropic 呼叫封裝成統一的 LLMClient,並導入 model alias registry
    • 將目前的主要 Agent prompt、tool schema 抽出成獨立 config 檔,放進 Git 做版本管理
    • 為一個關鍵任務流程加入 token 用量與 cost_usd 的記錄,並開始對新模型做 replay test
  • A2A 多代理協議實戰與踩坑筆記

    A2A 多代理協議實戰與踩坑筆記

    📌 本文重點

    • A2A 讓 agents 變成可重用服務,而非每案重寫
    • 協議核心是註冊發現、任務路由與狀態冪等設計
    • 穩定的 A2A 契約可讓框架與部署自由演進

    一旦你有第二個客戶、第二條業務線,原本那套「單體 agent + 一大坨編排器」就會開始失控:每個客戶複製一份 agent 系統、工具無法共享、編排器越寫越巨大。A2A(Agent to Agent)協議的目標就是:用一個標準化通訊協議,把 agent 變成可重用服務,而不是每個專案重造一輪輪子。


    重點說明:A2A 的核心設計

    1. 代理註冊與發現:從「硬編碼 URL」到「可發現的服務」

    單體時代常見寫法:

    // 單體編排器內部硬呼叫
    const result = await routingAgent.handle(request);
    

    一旦你要把同一個 routingAgent 給 N 個客戶用,就需要:

    1. 註冊機制:每個 agent 在啟動時向一個 Registry / Discovery Service 報到:
    2. id:shipment-validator
    3. capabilities:支援的任務類型(schema / tags)
    4. endpoint:如 https://agents.mycorp.com/shipment-validator
    5. 發現機制:編排器不再硬編 URL,而是呼叫 Registry API:
    GET /agents?task=shipment.validate
    

    得到 agent 清單後再去調用具體 endpoint。

    好處:同一組 agent 服務可以給多個客戶編排器共用;換掉實作(LangChain ➝ 自研框架)也只要更新註冊資料。

    💡 關鍵: 透過註冊 / 發現機制,同一個 agent 可被多客戶共用,大幅減少重複實作與維護成本。


    2. 任務路由:編排器既是 server 也是 client

    在多代理場景中,編排器不只是 HTTP server,也必須是 HTTP client:

    • 收到來自外部系統、前端的請求:server 身份
    • 需要委派子任務給其他 agents / 其他編排器:client 身份

    一個最小 A2A 任務請求格式可以長這樣(HTTP + JSON):

    POST /invoke HTTP/1.1
    Content-Type: application/json
    X-Trace-Id: 9f2e0a1c-...
    
    {
      "task": "shipment.validate",
      "input": { "shipmentId": "S123" },
      "context": {
        "tenantId": "customer-a",
        "locale": "zh-TW"
      },
      "caller": {
        "agentId": "orchestrator-logistics",
        "replyUrl": "https://orch-a.mycorp.com/a2a/callback"
      }
    }
    

    返回結果:

    {
      "task": "shipment.validate",
      "status": "success",
      "output": {
        "isValid": true,
        "warnings": []
      },
      "error": null,
      "meta": {
        "traceId": "9f2e0a1c-...",
        "agentId": "shipment-validator",
        "durationMs": 324
      }
    }
    

    關鍵點:

    • task:描述抽象任務,而不是具體路由 URL,方便 routing / 升級
    • caller.replyUrl:允許非同步回調(長耗時任務、跨 VPC 任務)
    • X-Trace-Id + meta.traceId:貫穿多跳 agent 的追蹤

    3. 狀態、錯誤、冪等:分散式 agent 的生存三寶

    多 agent 跨服務邊界後,你必須清楚回答三件事:

    1. 狀態放哪裡?

    2. 不要把 workflow state 塞在 LLM context 裡。

    3. 建議:

      • 長期業務狀態:外部 DB / 狀態機服務(如 Statewright 類型)
      • 短期呼叫狀態:每個 A2A 請求帶 taskRunId,在 DB 以 event-sourcing 或簡單 JSON blob 存歷程。
    4. 錯誤怎麼傳遞?

    {
      "status": "error",
      "error": {
        "type": "VALIDATION_ERROR",
        "message": "Unknown shipmentId S123",
        "retryable": false,
        "details": { "field": "shipmentId" }
      }
    }
    
    • retryable 很重要:編排器根據這個決定是否自動重試或改走 fallback。

    • 冪等怎麼做?

    • 每個任務呼叫帶 requestId:

    {
      "task": "shipment.create",
      "requestId": "create-S123-20240501T100000Z",
      ...
    }
    
    • agent 收到相同 requestId 時,重複回傳同一結果而不是再次寫 DB。
    • 真實踩坑:物流「建立訂單 + 發通知」若沒冪等,在重試時會重複發貨或發兩封簡訊。

    💡 關鍵: 為每個任務設計 requestId + retryable,是避免重複扣款、重複發貨等災難級錯誤的關鍵保險絲。


    實作範例:從單體 Agent app 演進到 A2A 系統

    下面是一個極簡版 A2A 實作,基於 Node.js + Express + HTTP + JSON,示範:

    • agent 如何註冊
    • orchestrator 如何發現 + 呼叫
    • 如何保留 traceId、處理重試

    1. Agent 啟動與註冊

    // agent/shipment-validator.ts
    import express from 'express';
    import fetch from 'node-fetch';
    
    const app = express();
    app.use(express.json());
    
    const AGENT_ID = 'shipment-validator';
    const REGISTRY_URL = process.env.REGISTRY_URL!;
    
    async function register() {
      await fetch(`${REGISTRY_URL}/agents/register`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          id: AGENT_ID,
          endpoint: process.env.PUBLIC_URL,
          capabilities: ['shipment.validate'],
          version: '1.0.0'
        })
      });
    }
    
    app.post('/invoke', async (req, res) => {
      const traceId = req.header('X-Trace-Id') || crypto.randomUUID();
      const { task, input, requestId } = req.body;
    
      if (task !== 'shipment.validate') {
        return res.status(400).json({ status: 'error', error: { type: 'UNKNOWN_TASK' }});
      }
    
      // TODO: 查 DB 或外部 API
      const exists = input.shipmentId?.startsWith('S');
    
      res.json({
        task,
        status: 'success',
        output: { isValid: !!exists },
        meta: { traceId, agentId: AGENT_ID }
      });
    });
    
    app.listen(3001, async () => {
      await register();
      console.log('shipment-validator listening on 3001');
    });
    

    2. Registry:最小可用版本

    // registry/index.ts
    import express from 'express';
    const app = express();
    app.use(express.json());
    
    const agents = new Map<string, any>();
    
    app.post('/agents/register', (req, res) => {
      const { id, endpoint, capabilities, version } = req.body;
      agents.set(id, { id, endpoint, capabilities, version });
      res.json({ ok: true });
    });
    
    app.get('/agents', (req, res) => {
      const task = req.query.task as string;
      const matches = [...agents.values()].filter(a =>
        a.capabilities.includes(task)
      );
      res.json(matches);
    });
    
    app.listen(3000, () => console.log('registry on 3000'));
    

    3. Orchestrator:既當 server 又當 client

    // orchestrator/logistics.ts
    import express from 'express';
    import fetch from 'node-fetch';
    
    const app = express();
    app.use(express.json());
    
    const REGISTRY_URL = process.env.REGISTRY_URL!;
    
    async function findAgentForTask(task: string) {
      const res = await fetch(`${REGISTRY_URL}/agents?task=${encodeURIComponent(task)}`);
      const list = await res.json();
      if (!list.length) throw new Error(`No agent for task ${task}`);
      return list[0]; // naive: take first
    }
    
    app.post('/shipment/check', async (req, res) => {
      const traceId = req.header('X-Trace-Id') || crypto.randomUUID();
      const { shipmentId } = req.body;
    
      const agent = await findAgentForTask('shipment.validate');
    
      const requestBody = {
        task: 'shipment.validate',
        requestId: `validate-${shipmentId}`,
        input: { shipmentId },
        context: { tenantId: 'customer-a' },
        caller: {
          agentId: 'orchestrator-logistics',
          replyUrl: null
        }
      };
    
      const resp = await fetch(`${agent.endpoint}/invoke`, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          'X-Trace-Id': traceId
        },
        body: JSON.stringify(requestBody)
      });
    
      const data = await resp.json();
    
      // 簡化處理,實際上應依 data.status 分支
      res.setHeader('X-Trace-Id', data.meta?.traceId || traceId);
      res.json(data);
    });
    
    app.listen(4000, () => console.log('orchestrator on 4000'));
    

    這樣,你就從原本的「單體 app 內部函式呼叫」,演進到:

    • 多個客戶可以共用 shipment-validator agent
    • 想新增 pricing-agent,只要註冊到 Registry,不改 orchestrator 主程式

    進一步要上 gRPC、使用隊列系統(Kafka / SQS)時,只是把 /invoke 的 transport 換掉,協議 payload 大致可以不變。

    💡 關鍵: 先穩定 JSON 協議,再替換 HTTP、gRPC、Queue 等傳輸層,可以減少大規模重構帶來的風險。


    建議與注意事項:真實踩坑整理

    1. 編排器雙角色帶來的競態條件

    當 orchestrator 既是 server 又是 client 時,常見問題:

    • 同步與非同步混用:一部分任務是同步 HTTP 呼叫,一部分透過 queue 非同步回調,結果狀態管理爆炸。

    建議:

    • 明確區分 請求-回應 vs fire-and-forget 任務 的 API。
    • 在邏輯架構層(可參考 AI Agent Logical Architecture 那篇思路)先畫出 state machine,再實作;工具如 Statewright 類型可以讓流程更可視化。

    2. 訊息格式與 schema 演進

    多客戶、多 agent 之後,改一個欄位就是全網恐慌:

    • 務必使用明確的 version 欄位:
    {
      "protocolVersion": "a2a-1.1",
      "task": "shipment.validate",
      ...
    }
    
    • 新增欄位 ➝ 預設 optional,舊 agent 收到可忽略。
    • 刪除 / 改名欄位 ➝ 用 Deprecation window(先標註 deprecated: true 一段時間)。

    3. 重試與冪等:不要指望下游「應該沒事」

    在多代理系統裡,任何一跳都可能:

    • timeout
    • 500
    • 只執行了一半邏輯

    建議:

    • 所有會造成 side effect 的任務必須有 requestId
    • 在 DB 以 (requestId, task) 做唯一索引
    • 重試時以 requestId 查找舊結果,若存在則直接回傳

    4. 日誌與追蹤:traceId 一定要往外帶

    常見錯誤是只在 API gateway 或第一個 service 打 log。多 agent 下:

    • 每個 A2A 呼叫都要帶 X-Trace-Id header。
    • 每個 agent 回傳結果時,把自己的 agentId、task、durationMs 打到 log。
    • 方便之後在 log system(如 ELK / OpenTelemetry)中串連整條 workflow。

    5. 和 MCP、serverless 編排器整合時的性能與隔離

    • MCP / LangChain / LlamaIndex 等框架:
    • 不要把所有工具都塞進同一個 process;可以把重型工具封裝為獨立 agent,透過 A2A 呼叫,避免單一框架 process 變成巨石。
    • Serverless 編排器(如 Step Functions、Temporal):
    • 用 A2A 把「呼叫 agent」當成一個 task type。
    • 注意冷啟動 + LLM 啟動成本:盡量把 agent 部署為長跑服務,serverless 只負責 orchestration。
    • 隊列系統整合(Kafka / SQS / RabbitMQ):
    • A2A 協議層仍用同一份 JSON schema,只是 transport 從 HTTP 換成 message。
    • 確保 message 中也有 traceId、requestId、task、protocolVersion,否則 debug 會極其痛苦。

    總結:

    • A2A 的本質不是某個框架,而是一套清楚的多代理通訊契約。
    • 一旦協議穩定,你可以自由更換 LLM、agent framework、部署方式,但還能:
    • 在多客戶間重用同一組 agents
    • 控制編排器複雜度
    • 在真實生產環境中可觀測、可回溯、可演進。

    如果你已經有一個「會動但很醜」的單體 agent app,建議先抽出最常用的 1–2 個子任務,照上面的 minimal A2A 協議拆出去,從那裡開始演進,而不是一次重寫全系統。

    🚀 你現在可以做的事

    • 把現有單體 agent app 中 1–2 個常用任務,先按文中 JSON 協議拆成獨立 /invoke 服務
    • 為這些任務統一增加 requestId、X-Trace-Id,並在日誌中打出 agentId、task、durationMs
    • 實作一個最小版 Registry(照文中 registry/index.ts),讓 orchestrator 透過發現機制而不是硬編 URL 呼叫 agents