標籤: LLM 可觀測性

  • 企業級 Agentic AI 落地技術戰略

    企業級 Agentic AI 落地技術戰略

    📌 本文重點

    • 先定 Agent 能力邊界與標準介面,再談擴張
    • 用 RBAC+資料分區避免 Shadow Agents 失控
    • 透過 trace、成本與回滾機制把 Agent 變成可營運系統
    • Prompt 只是策略層,規則與流程要結構化治理

    企業現在面臨的痛點不是「怎麼做出一兩個酷炫 Demo Agent」,而是如何把 Agentic AI 變成可治理、可觀測、可疊代的跨部門平台:可以安全連接內部系統、量化成效、避免 Shadow Agents 失控,同時不讓整個架構被一堆 prompt 綁死。

    以下從標準化介面、權限與安全、可觀測性與恢復機制三個技術面切入,並結合客服、財務、IT 運維場景,討論具體落地策略。


    重點說明

    1. 從「玩票 Agent」到「平台」:先定能力邊界,再定介面

    MIT Tech Review 指出,80% 的大型企業都有 Agent 試點,但多停留在孤立 PoC。要走到平台化,核心是:

    💡 關鍵: 多數企業已經在做 Agent 試點,但真正的差異在於能否從零散 PoC 升級為有標準介面與治理的「平台」。

    • 能力邊界 = 業務責任 + 技術能力:先定這個 Agent 負責哪段流程,而不是先想「讓它能做越多越好」。例如客服 Agent:僅負責「查詢訂單+生成回覆草稿」,不負責「修改付款狀態」。
    • 介面標準化:所有 Agent 都只透過標準化 Tool/Skill API接觸系統與資料,不直接操作底層服務。這是之後做權限控管、審計、成本管理的基礎。
    • 把 Prompt 從架構裡拆出來:Prompt 層只是策略配置,不是系統邏輯。不把「業務流程規則」寫死在 prompt,而是盡量下放到結構化配置與工具約束。

    2. 權限與安全:用 RBAC + 資料分區治理 Agent

    從 TechCrunch 曝露的「Agent swarm 不受控上網」案例可以看到:沒有明確安全模型的多代理系統,很容易長出 Shadow Agents——開發團隊自己私下接小工具、接新 API,完全不在統一治理之下。

    企業級 Agent 平台至少需要:

    • RBAC(Role-Based Access Control):先管「人」與「業務角色」,再管 Agent。Agent 的權限是由呼叫方的角色繼承,而不是 Agent 自己決定。「客服 Agent 被客服人員操作」,與「同一 Agent 被實驗帳號操作」,應視為兩個不同權限情境。
    • 資料分區(Data Partitioning):客服、財務、IT 運維 Agent 對同一套後端系統(例如 ERP)只看到各自分區。例如:財務 Agent 可查對帳資料,但不能查客服備註;IT Agent 可查系統狀態,但不能查客戶身份資訊。
    • 審計與技能治理:每一個 Tool/Skill 都必須有註冊流程、owner、風險等級、審計日誌。新工具不得直接進生產 Agent,而要經過安全審核與風險評估,避免 Shadow Skills 靜悄悄破壞邊界。

    3. 可觀測性與失敗恢復:從「跑起來」到「可營運」

    Towards AI 的經驗很直接:上線 LLM 功能很容易,讓它穩定運行才是難題。多代理系統更需要:

    💡 關鍵: 只有能追蹤每次任務的工具調用、token 成本與結果,你才有辦法把 Agent 變成可衡量、有 KPI 的正式能力。

    • Trace 全鏈路:每一個任務需要可以回溯「哪個 Agent → 用了哪些 Tool → 花了多少 tokens → 產生了什麼輸出」。這不只是 debug,而是之後算 KPI 的依據。
    • 成本與 KPI 對齊:紀錄每個 Agent 任務的token 成本、API 調用次數、成功率、平均處理時間,與業務 KPI(節省人力、縮短處理時間、減少錯誤率)直接對齊。避免盲目堆 Agent 卻無法證明價值。
    • 失敗恢復與回滾:任務不是「跑完就算了」,要有明確的任務狀態機 + 回滾策略:
    • 工單創建失敗要可重試
    • 財務 Agent 執行「修改憑證」要有 undo 行為
    • IT Agent 執行變更時需預先建立 rollback plan

    實作範例

    以下用一個簡化的「多部門 Agent 平台」示意,重點在介面與治理,而非特定框架。

    1. 標準化 Tool/Skill 介面

    先定一個統一 Tool 介面,所有 Agent 只能透過這個 interface 操作系統:

    // tool-definition.ts
    export type ToolContext = {
      userId: string;
      role: "customer_service" | "finance" | "it_ops";
      tenantId: string;
      traceId: string;
    };
    
    export type ToolInput = Record<string, unknown>;
    export type ToolOutput = Record<string, unknown>;
    
    export interface ToolDefinition {
      name: string;                     // 如 "create_ticket", "post_journal_entry"
      description: string;              // 給 LLM 用的清楚說明
      inputSchema: object;              // JSON Schema,用於驗證
      outputSchema: object;             // JSON Schema,用於驗證
      allowedRoles: string[];           // RBAC:哪些業務角色可用
      handler: (ctx: ToolContext, input: ToolInput) => Promise<ToolOutput>;
    }
    
    // 範例:客服工單建立工具
    export const CreateTicketTool: ToolDefinition = {
      name: "create_ticket",
      description: "為客戶建立客服工單,僅限客服角色使用",
      inputSchema: {
        type: "object",
        properties: {
          customerId: { type: "string" },
          subject: { type: "string" },
          priority: { type: "string", enum: ["low", "normal", "high"] },
        },
        required: ["customerId", "subject"],
      },
      outputSchema: {
        type: "object",
        properties: {
          ticketId: { type: "string" },
          status: { type: "string" },
        },
        required: ["ticketId", "status"],
      },
      allowedRoles: ["customer_service"],
      async handler(ctx, input) {
        // 在這裡做資料分區與權限驗證
        enforceTenant(ctx.tenantId);
        enforceRole(ctx.role, this.allowedRoles);
    
        const ticket = await TicketService.create({
          tenantId: ctx.tenantId,
          customerId: input.customerId as string,
          subject: input.subject as string,
          priority: (input.priority as string) ?? "normal",
          createdBy: ctx.userId,
          traceId: ctx.traceId,
        });
    
        auditLog({
          traceId: ctx.traceId,
          toolName: this.name,
          userId: ctx.userId,
          role: ctx.role,
          input,
          output: { ticketId: ticket.id, status: ticket.status },
        });
    
        return { ticketId: ticket.id, status: ticket.status };
      },
    };
    

    實際好處:

    • 所有 Agent 操作系統的入口一致,方便日後做審計與成本分析
    • 把業務邏輯(例如 tenantId、role 驗證)放在 Tool handler,而不是 prompt 裡,降低維護成本
    • 可以針對 Tool 做版本治理與安全審核,避免 Shadow Agents 私接非授權 API

    2. Agent 註冊與權限綁定

    建立 Agent 時明確綁定可用 Tool 與業務角色,避免「萬能 Agent」:

    // agent-registry.ts
    
    export type AgentProfile = {
      id: string;
      name: string;
      department: "customer_service" | "finance" | "it_ops";
      allowedTools: string[];     // 限定能用哪些 Tool.name
      maxTokensPerRun: number;    // 成本限制
      observability: {
        enableTrace: boolean;
        enableCostTracking: boolean;
      };
    };
    
    // 範例:客服 Agent
    export const CustomerServiceAgent: AgentProfile = {
      id: "agent_cs_01",
      name: "Customer Support Agent",
      department: "customer_service",
      allowedTools: ["create_ticket", "get_order", "suggest_reply"],
      maxTokensPerRun: 32_000,
      observability: {
        enableTrace: true,
        enableCostTracking: true,
      },
    };
    
    // 範例:財務 Agent(不能動客服工單)
    export const FinanceAgent: AgentProfile = {
      id: "agent_fin_01",
      name: "Finance Reconciliation Agent",
      department: "finance",
      allowedTools: ["get_invoice", "post_journal_entry", "run_reconciliation"],
      maxTokensPerRun: 48_000,
      observability: {
        enableTrace: true,
        enableCostTracking: true,
      },
    };
    

    實際好處:

    • 每個 Agent 有清楚「能力邊界」,便於對應部門 KPI 與風險評估
    • 可以在平台層實作 工具白名單,防止 Agent 任意調用未審核的 Skill
    • 有助於避免將所有需求堆成一個巨型 Prompt + 巨型 Agent,後期難以維護

    3. Trace、成本與回滾機制

    建立一個最小可用的任務執行管線,集中處理 trace、成本與任務狀態:

    // task-runner.ts
    
    type AgentTaskInput = {
      agentId: string;
      userId: string;
      role: ToolContext["role"];
      tenantId: string;
      goal: string;            // 高階任務描述
    };
    
    async function runAgentTask(input: AgentTaskInput) {
      const traceId = generateTraceId();
      const agentProfile = loadAgentProfile(input.agentId);
    
      const ctx: ToolContext = {
        userId: input.userId,
        role: input.role,
        tenantId: input.tenantId,
        traceId,
      };
    
      const tokenBudget = agentProfile.maxTokensPerRun;
      let tokenUsed = 0;
      const steps: Array<{ tool: string; input: ToolInput; output: ToolOutput }> = [];
    
      try {
        // 這裡可以接 OpenAI / 其他 LLM API
        const result = await runLLMAgent({
          goal: input.goal,
          tools: agentProfile.allowedTools.map(loadToolDefinition),
          context: ctx,
          onToolCall: async (toolDef, toolInput) => {
            const output = await toolDef.handler(ctx, toolInput);
            steps.push({ tool: toolDef.name, input: toolInput, output });
            return output;
          },
          onTokenUsage: (delta) => {
            tokenUsed += delta;
            if (tokenUsed > tokenBudget) throw new Error("Token budget exceeded");
          },
        });
    
        await saveTaskTrace({ traceId, agentId: input.agentId, steps, tokenUsed, status: "success" });
        return result;
      } catch (err) {
        await saveTaskTrace({ traceId, agentId: input.agentId, steps, tokenUsed, status: "failed", error: String(err) });
    
        // 任務回滾示意:對有副作用的工具,呼叫對應 undo
        await rollbackSideEffects(steps, ctx);
    
        throw err;
      }
    }
    
    async function rollbackSideEffects(steps, ctx: ToolContext) {
      for (const step of steps.reverse()) {
        const undoTool = loadUndoTool(step.tool); // 如 "undo_post_journal_entry"
        if (!undoTool) continue;
        await undoTool.handler(ctx, { originalOutput: step.output });
      }
    }
    

    實際好處:

    • 每次 Agent 執行都有完整 trace,可支援:
    • 事後審計(誰在什麼情境下做了什麼)
    • 成本分析(tokenUsed 與業務價值對比)
    • 失敗調查(步驟 / 工具調用序列)
    • 對所有有副作用的 Tool 都可以要求提供對應 undo Tool,讓變更可回滾,而不是把一切交給「LLM 自己想辦法」

    建議與注意事項

    1. 別堆 Agent,先定 KPI 與路線圖

    常見錯誤是:看到 Basis、Clay 這些 AI-native 公司用 Agent 做 onboarding、帳戶管理,就開始瘋狂做各種 Agent,卻沒有清楚 KPI。建議:

    💡 關鍵: 每個 Agent 至少要對應一組可以衡量的業務指標,否則很難在組織內持續爭取資源。

    • 每個 Agent 都要對應一個可量化業務目標(例如:客服平均處理時間 -20%,財務月結時間 -30%)
    • 技術路線圖分階段:
    • Phase 1:只讀 + 建議(不改資料)
    • Phase 2:可創建低風險實體(工單、草稿憑證)
    • Phase 3:可進行有限變更+完備回滾(例如 IT 運維中小型變更)

    2. 防止 Shadow Agents:技能治理要跟上

    隨著內部開發者與業務團隊越來越熟悉 Agent,很容易出現「自己接一個新工具測試一下」的 Shadow Agent:

    • 所有 Tool/Skill 必須註冊到統一 Registry,沒註冊就不能被任何生產 Agent 調用
    • 對 Tool 定義Owner、風險等級、安全審核流程,高風險工具(例如財務憑證變更、資金轉帳)需額外審核與雙重確認
    • 使用審計與 Trace,定期檢查是否有 Agent 使用未授權 Tool,必要時強制下線

    3. 不要把 Prompt 當微服務架構

    另一個常見踩坑:把所有業務邏輯寫在 Prompt 裡,結果:

    • 要改一條規則就得改一大段 Prompt,難以版本管理
    • 無法對規則做靜態分析、測試與審核

    建議:

    • 把流程與規則放在結構化配置(JSON/YAML)與 Tool 邏輯內,Prompt 只描述「角色、目標、約束的高階語意」
    • 使用系統訊息 + 明確 Tool 描述作為主要引導手段,而不是自然語言長篇故事
    • 對 Prompt 做版本管理,就像 code 一樣(Git、review、A/B)

    4. 記憶設計:資料所有權與分區

    參考 HuggingFace 在 Coding Agents 記憶上的作法:

    • Agent 的長期記憶(客戶偏好、財務歷史、系統變更紀錄)應存在企業自有資料層,而不是完全依賴雲端 provider 的黑箱記憶
    • 每個租戶與部門資料分區清楚,避免客服 Agent 記住不該看到的財務資訊
    • 提供記憶管理介面給管理員:可以查詢、刪除、凍結特定 Agent 的記憶,以符合合規與隱私要求

    總結:企業級 Agentic AI 的核心不是「多聰明的模型」,而是一套可治理的介面、權限與可觀測性框架。先把 Tool/Skill、RBAC、trace 與回滾設計好,再去思考要放多少「自動化」給 Agent。這樣才能避免走向 Shadow Agents 失控的路,而是真正把 Agent 當作企業營運能力的一部分。

    🚀 你現在可以做的事

    • 盤點現有內部 Agent/LLM 專案,為每一個補上清楚的業務 KPI 與能力邊界說明
    • 建立一份簡單的 Tool/Skill Registry(含 owner、風險等級、allowedRoles),把現有工具全部註冊進來
    • 選一個低風險場景(如客服查詢+回覆草稿),實作含 trace、成本統計與 undo 機制的最小可用 Agent 任務管線