分類: AI 技術

  • 270 億參數上 iPhone 的實戰路線

    270 億參數上 iPhone 的實戰路線

    📌 本文重點

    • 27B 大模型壓縮後可在手機端側實用
    • 量化 +剪枝 +蒸餾是端側部署核心組合
    • 善用 NPU/GPU 與 RAG/Agent 架構提升體驗

    在端側塞進 Qwen 3.6-27B 這種體量的模型,直接解決了三個痛點

    1. 隱私 & 合規:聊天、RAG 查文件、簡單 Agent 全在本機,不丟到雲端。
    2. 互動延遲:減少網路 RTT,短指令(改句子、補程式)能接近即時回應。
    3. 成本 & 擴展:不用為每個活躍使用者準備一個雲端 GPU,端側模型成為主力推理,引流雲端模型解決長上下文或高難任務。

    PrismML 把 Qwen 3.6-27B 壓到能在 iPhone 17 Pro 上跑,給了我們一個清楚訊號:27B 級模型上手機是可行路線,但你要整套壓縮 + runtime + 系統調優一起做


    重點說明:端側大模型的幾個關鍵技術

    1. 量化:從 16bit 壓到 4bit / 2bit

    目的:壓縮權重體積 + 減輕記憶體頻寬壓力

    • 粗估:
    • FP16:27B × 2 bytes ≈ 54 GB(純權重就爆)
    • 4bit:27B × 0.5 bytes ≈ 13.5 GB
    • 2bit:27B × 0.25 bytes ≈ 6.75 GB

    💡 關鍵: 把 27B 權重從 54GB 壓到約 7–14GB,是端側部署能否落地的基礎門檻

    • 實務上常用 mixed-precision
    • attention / embedding 保留 8bit 或 16bit
    • MLP 可用 4bit 或 2bit(配合 group-wise scaling)
    • 在 iOS 端常見兩種路線:
    • GGUF + llama.cpp:用 Q4_K_M / Q3_K_M 這類 profile
    • Core ML / MLC:用 symmetric int4 / int8,用 LUT 把量化常數 bake 進權重

    對專案的好處:

    • 同一隻 app 可以提供 小模型 + 壓縮大模型 的雙模式:日常用 7B,開「專業模式」時啟動 27B(但速度變慢 + 發熱)。

    2. 剪枝與低秩分解:不只變小,還要能跑快

    量化只壓體積,不一定壓算力;要在功耗有限的手機跑得動,需要再用:

    1. 結構化剪枝(structured pruning)
    2. 砍掉整個 head、channel 或 FFN neuron。
    3. 例如把 32 heads 剪到 24 heads,或把 FFN hidden dim 從 8k 降到 6k。
    4. 這會直接減少 MACs,對 NPU / GPU 友善。
    5. 低秩分解(LoRA / SVD 分解 FFN 權重)
    6. 把大矩陣 (W \in \mathbb{R}^{d \times d}) 分成 (U \in \mathbb{R}^{d \times r}, V \in \mathbb{R}^{r \times d}),r ≪ d
    7. 搭配蒸餾可以維持合理能力。

    PrismML 類型的方案多半是 多階段:量化 + 剪枝 + 蒸餾 + runtime 特化 一起做,才有可能把 27B 折成 iPhone 等級的 latency。

    3. KV Cache / 推理圖優化:token/s 的決勝點

    端側 LLM 的痛點不是 model load,而是 持續推理的 token/s 與發熱

    關鍵是:

    • KV Cache 緊縮
    • 精度壓到 8bit/4bit(與權重量化分開考慮)。
    • 善用 sliding window / attention sink,減少長對話時的 KV 佔用。
    • 推理圖優化(graph optimization)
    • 透過 MLC / Core ML / 自研 runtime 把整段 transformer block fuse 成 1–2 個大 kernel。
    • 減少 CPU ↔ NPU ↔ GPU 之間的 context switch。

    效果:在 iPhone 17 Pro 這種等級裝置,27B 壓縮到 4bit + 優化 graph,合理預期 5–10 tok/s 左右(看溫控),可以應付聊天、簡單 RAG,但不是寫 3k 行程式碼那種長輸出場景。

    💡 關鍵: 端側大模型的體感速度主要由 token/s 決定,5–10 tok/s 大致只適合短回覆與簡單任務

    4. 善用手機 NPU / GPU:不是「丟上去就快」

    iOS 上大致有三條路:

    1. Core ML + ANE(NPU)
    2. 最省電,但要配合 Core ML 支援的 op / dtype,需要前置轉換。
    3. Metal / MLC
    4. 走 GPU / CPU + 少量 NPU,對自訂 kernel 比較自由。
    5. llama.cpp(Metal 後端)
    6. 直接吃 GGUF,開 Metal 加速;部分 op 仍在 CPU。

    實務上可以採用 混合策略:embedding / 前幾層在 NPU,後面在 GPU / CPU,平衡記憶體與溫度。


    實作範例:在 iOS 上做聊天 / RAG / Agent 的架構

    下面給一個「實際可落地」的 27B 壓縮部署思路,用 pseudo code 表示。

    1. 推理框架選型與模型準備

    方案 A:llama.cpp + GGUF(開發週期短)

    1. 先把 Qwen 3.6-27B 轉成 GGUF 並量化(在桌機完成):
    python convert_qwen_to_gguf.py \
      --model qwen-3.6-27b \
      --out qwen-3.6-27b-q4_k_m.gguf
    
    ./quantize \
      qwen-3.6-27b-q4_k_m.gguf \
      qwen-3.6-27b-q4_k_m.gguf \
      Q4_K_M
    
    1. iOS 使用 llama.cpp Swift / C API:
    let params = gpt_params_default()
    params.n_ctx = 4096
    params.n_gpu_layers = 20   // 前 20 層走 Metal
    
    let ctx = llama_init_from_file("qwen-3.6-27b-q4_k_m.gguf", params)
    
    func infer(prompt: String) -> String {
        llama_reset(ctx)
        llama_eval_prompt(ctx, prompt)
        var output = ""
        while !shouldStop(output) {
            let token = llama_eval_next(ctx)
            output += tokenizer.decode(token)
        }
        return output
    }
    

    注意:n_gpu_layers 設太高,會爆 VRAM / 發熱;設太低,會被 CPU 拖死。實測需要在 8–24 之間找平衡。

    方案 B:MLC LLM + Metal(更深度優化)

    • 透過 MLC 工具鏈把 Qwen 3.6-27B 壓成 int4 + fused kernels,生成 iOS 專用模型包:
    mlc_llm convert qwen-3.6-27b \
      --quantization q4f16_0 \
      --target iphone
    
    mlc_llm build ios qwen-3.6-27b-q4f16_0
    

    iOS 端呼叫:

    let engine = MLCChatEngine(model: "qwen-27b-q4f16_0")
    
    engine.generate(
      prompt: userPrompt,
      maxTokens: 512,
      temperature: 0.7,
      streamHandler: { token in
        appendToUI(token)
      }
    )
    

    實務建議:若不是要深度客製 runtime,MLC 會比自己改 llama.cpp 更好拿穩定 fps 和電源管理

    2. RAG:本機嵌入 + 文件切片

    架構:

    • 輕量 embedding 模型(如 384–768 dim)放端側
    • 文本切 chunk(512–1024 tokens),建立本地向量索引。
    • 查詢時在端側 embed + ANN 搜索 + top-k context 拼回 prompt,再丟給 27B 生成。

    簡易 Swift 流程(以 Core ML embedding 模型為例):

    // 1. 建立索引(啟動或背景時)
    for chunk in documentChunks {
        let emb = embeddingModel.encode(text: chunk.text) // [Float]
        annIndex.add(vector: emb, id: chunk.id)
    }
    
    // 2. 查詢時
    func ragAnswer(query: String) -> String {
        let qEmb = embeddingModel.encode(text: query)
        let topK = annIndex.search(query: qEmb, k: 4)
        let context = topK.map { $0.text }.joined(separator: "\
    ---\
    ")
    
        let prompt = """
    你是一個助理。以下是知識庫內容:
    \(context)
    
    問題:\(query)
    請根據知識庫回答,若沒有明確答案就說不知道。
    """
        return infer(prompt: prompt)
    }
    

    好處

    • 敏感文件不離開手機;
    • 即使 27B 很慢,RAG 因為 context 精準,需要生成的 token 也變少

    3. 簡單 Agent:有限循環 + 工具調用

    在端側做 Agent 不要太貪心,建議:

    • 限制 max steps(例如 4–6 步)。
    • 工具呼叫只做本機能力:檔案讀寫、日曆、藍牙裝置等。

    簡化 pseudo code:

    struct ToolCall { let name: String; let args: [String: Any] }
    
    func runAgent(task: String) {
        var state = ""
        for step in 0..<6 {
            let prompt = buildAgentPrompt(task: task, state: state)
            let output = infer(prompt: prompt)
    
            if let tool = parseToolCall(output) {
                let result = executeTool(tool)
                state += "\
    [tool-result]\
    " + result
            } else {
                renderFinal(output)
                break
            }
        }
    }
    

    這種架構在 27B 上 iPhone 實務可行,但要注意:token/s 慢 → 每一輪推理越短越好,prompt 盡量模板化、少廢話。


    建議與注意事項:坑在哪、怎麼踩得比較輕

    1. 記憶體與冷啟動

    • iPhone 17 Pro 類級別裝置,27B 4bit 純權重就十幾 GB,實務上需:
    • 模型分片 / 分層載入
      • 常駐 7B 小模型;
      • 27B 大模型在「高需求模式」才 mmap 載入部分層(如後 12 層)。
    • 或採 Mixture-of-Experts(MoE) 類方案,如 GLM 5.2 + Flash MoE,那類模型在 Mac M5 上可跑 2–2.8 tok/s,給端側設計方向參考:不是所有 layer 都要 full dense
    • 冷啟動:
    • 首次 load 27B 量化模型,可能 3–10 秒,一定要做 loading UI + 延遲初始化(app 啟動時先載小模型,使用者開「專業模式」才載大模型)。

    💡 關鍵: 3–10 秒的冷啟動延遲是端側大模型的 UX 關鍵,必須用分層載入與明確 UI 掩護

    2. 電量與發熱

    • 連續推理 1–2 分鐘,大模型會把 SoC 拉到 TDP 上限,掉頻後 token/s 會明顯下降。
    • 實作上建議:
    • 限制 max_tokens,避免長篇輸出。
    • 使用 溫度監控(ThermalState,高溫時降速:
    if ProcessInfo.processInfo.thermalState == .serious {
        params.n_threads = max(2, params.n_threads / 2)
    }
    

    3. 隱私與合規

    • Ce 合規角度,端側推理 + 本地 RAG 是加分項,但要注意:
    • 日誌、錯誤回報不得包含原文檔內容。
    • 若有雲端 fallback,要明確 UI 提示「此問題將送出至伺服器」。

    4. 測試方法與性能預估

    建議自建簡單基準腳本,統計:

    • 冷啟動時間(model load 完到第一 token)
    • token/s(前 50 tokens、整段平均)
    • 電池掉電率與機身溫度(5 分鐘連續推理)

    例:用 llama.cpp CLI 在開發機 / TestFlight 內部版測一次:

    ./main -m qwen-27b-q4_k_m.gguf \
      -p "Hello" -n 256 -s 42 --timings
    

    官方輸出會給:

    • prompt eval time / token
    • generation eval time / token

    再用這些數據估算:

    • 聊天:平均 50–100 tokens → 是否能在 3–5 秒內完成?
    • RAG:查詢 + 150 tokens → 整體延遲能否低於 10 秒?

    結論:端側 27B 的實際策略

    對多數 iOS 專案,建議的 可實作路線 是:

    1. 產品層:
    2. 7B–8B 模型 作為主力。
    3. 提供「離線高精度模式」載入壓縮 27B,給重度用戶 & 高價方案。
    4. 技術層:
    5. 量化採 4bit mixed-precision,配合剪枝 + 蒸餾。
    6. 選擇 MLC / llama.cpp Metal 後端,不要自幹 runtime,除非是公司核心技術。
    7. 系統層:
    8. 做好 分層部署 + 延遲載入 + 熱管理
    9. 嵌入模型 + RAG 索引用小模型處理,27B 只負責最終生成。

    這樣設計,你可以合理地把「看起來誇張的 27B」塞進 iPhone 裡,並且在聊天、RAG、輕量 Agent 這幾個場景拿到實際可用的體驗。真正的難點不只在模型壓縮,而是在整條端側推理鏈的工程化

    🚀 你現在可以做的事

    • 在開發機上用 llama.cpp 或 MLC 先跑一版 Qwen 3.6-27B 4bit,量測 token/s 與冷啟動時間
    • 選定一個 7B 模型做端側 RAG PoC,實作本地嵌入與向量索引流程
    • 規劃產品中的「專業模式」或「離線高精度模式」,設計 7B / 27B 雙模型切換與載入策略
  • 語義快取實戰:RAG 成本暴降指南

    語義快取實戰:RAG 成本暴降指南

    📌 本文重點

    • 語義快取可降 20–60% API 成本
    • 僅對「穩定且短」內容做 embedding
    • 加多鍵條件與門檻避免誤命中
    • 做成可監控、有版本的基礎設施

    多數 RAG / chat backend 在做完 模型選型、prompt 調參、retriever 調優 之後,推理帳單還是居高不下,關鍵原因往往是:只做 exact-match cache,幾乎等於沒快取。語義快取(semantic cache)能把「同一個問題的不同說法」視為同一查詢,實務上可以做到 20–60% API call 減少,延遲跟成本一起砍。

    💡 關鍵: 用語義快取把不同說法視為同一查詢,實務上可直接減少約 20–60% 的 API 呼叫與成本。

    下面直接從實作觀點拆解:要怎麼在典型 RAG 架構裡,把語義快取變成一個可上線的「基礎設施」,而不只是一個實驗性 side project。


    重點說明

    1. Exact-match cache vs Semantic cache:差在哪裡?

    典型 chat / RAG backend 的 exact-match cache 會用:

    key   = hash(user_id + model_id + prompt_string)
    value = LLM 回應(與中間狀態)
    

    問題:只要 user 換句話說、加入一句客套話、locale 不同,就完全 miss。

    語義快取改成

    1. embedding 模型 將「實際送進 LLM 的完整 prompt」轉成向量
    2. 在向量空間做 相似度搜尋,找到語義相近的歷史請求
    3. 若相似度高於門檻,就直接重用快取結果

    核心差異:

    • exact-match:字串完全一致才命中 → hit rate 極低
    • semantic cache:語義相似即可命中 → 命中率與長尾查詢成本大幅改善

    2. Embedding 維度、距離度量與門檻設計

    (1) Embedding 選型

    實務建議:

    • 優先用雲端提供的現成模型,例如:
    • OpenAItext-embedding-3-small(1536 維,CP 值高)
    • Anthropic:搭配外部 embedding 模型(現階段主力還是在 text model)
    • 本地bge-m3 / bge-large-zh 系列
    • 維度建議:768–1536 維,再低容易語義表達力不足,再高會拖慢向量查詢與存儲

    💡 關鍵: Embedding 維度落在 768–1536 通常能兼顧語義表達力與查詢成本,是實務上常用的安全區間。

    (2) 距離度量

    多數 embedding 預設是單位向量,直接用:

    • Cosine 相似度
    • Inner product(dot product) + normalization

    pgvector / RediSearch 中對應為:

    • pgvector:cosine_distance, inner_product
    • RediSearch:COSINE, IP

    (3) 相似度門檻

    實務上用相似度 score(越高越相似):

    • 一般 QA / FAQ 類:≥ 0.85 才視為命中
    • 容錯度高(例如推薦、summarize):可以放寬到 0.8
    • 安全關鍵(法務 / 醫療):建議 0.9+ 或只做「候選提示」不直接 auto-hit

    建議做成 可配置

    semantic_cache:
      min_similarity: 0.87
      max_age_seconds: 86400  # 24h
    

    💡 關鍵: 把相似度門檻做成可配置,可以依場景在 0.8–0.9+ 之間調整,兼顧命中率與錯答風險。

    3. 多鍵策略 & Prompt 模板變動

    (1) 多鍵策略:user_id / locale / model_id

    避免「錯人、錯模、錯語系」的語義誤命中,可以在向量檢索時加上多維限制:

    • user_id:B2B 場景常有客製知識庫,可用 tenant_id / org_id 分區
    • localezh-TW vs en-US 的語義可近但答案不同
    • model_id:不同模型輸出風格與能力差異大,cache 混用會有體感落差

    查詢條件大致會長這樣:

    WHERE tenant_id = $1 AND locale = $2 AND model_id = $3
    ORDER BY embedding <-> $query_embedding
    LIMIT 1
    

    (2) Prompt 模板變動導致 cache miss

    大部分團隊會把 系統提示 + tool 描述 + 歷史對話 + user 問句 串成完整 prompt 做快取 key;結果一改模板,整個 cache 幾乎報廢。

    實務作法:

    • 把要做 embedding 的內容拆成比較穩定的部分:
    • 不要含整個系統提示
    • 不要含 tool schema
    • 只對「語義關鍵」部分做 embedding:user 問句 + 精簡後的 RAG context
    • 快取 key 中再另外紀錄 prompt_template_version,查詢時限制:
    WHERE prompt_template_version = $ver
    

    這樣改模板時只需要 bump version,舊資料會自然「冷掉」,又不會影響新版本 cache 收斂。


    實作範例

    1. 架構視角

    以典型 RAG / chat backend 為例,語義快取插在:

    1. 收到 user query
    2. 組裝完整 prompt 前/後,計算 embedding
    3. 先查 semantic cache
    4. 命中:直接回應
    5. 未命中:
      • 走正常流程:檢索(RAG)、呼叫 OpenAI / Anthropic / Meta 模型
      • 回寫快取

    2. PostgreSQL + pgvector 範例

    建表

    CREATE EXTENSION IF NOT EXISTS vector;
    
    CREATE TABLE semantic_cache (
      id BIGSERIAL PRIMARY KEY,
      tenant_id TEXT NOT NULL,
      locale TEXT NOT NULL,
      model_id TEXT NOT NULL,
      prompt_template_version INT NOT NULL,
    
      prompt_text TEXT NOT NULL,       -- 關鍵語義部分(例如 user 問句 + RAG context 摘要)
      response_json JSONB NOT NULL,    -- 模型原始回傳(含tool_calls可選)
    
      embedding VECTOR(1536) NOT NULL,
      created_at TIMESTAMPTZ DEFAULT now(),
    
      UNIQUE (tenant_id, locale, model_id, prompt_template_version, id)
    );
    
    CREATE INDEX ON semantic_cache USING ivfflat (embedding vector_cosine_ops)
      WITH (lists = 100);
    

    查詢(Node / TS pseudo-code):

    const { embedding } = await openai.embeddings.create({
      model: "text-embedding-3-small",
      input: cacheKeyText, // user 問句 + RAG 摘要
    });
    
    const { rows } = await pg.query(
      `SELECT id, response_json,
              1 - (embedding <=> $4) AS similarity
       FROM semantic_cache
       WHERE tenant_id = $1
         AND locale = $2
         AND model_id = $3
         AND prompt_template_version = $5
       ORDER BY embedding <-> $4
       LIMIT 1`,
      [tenantId, locale, modelId, embedding, promptTemplateVersion]
    );
    
    if (rows[0] && rows[0].similarity >= MIN_SIMILARITY) {
      return rows[0].response_json; // 命中語義快取
    }
    

    回寫

    await pg.query(
      `INSERT INTO semantic_cache
       (tenant_id, locale, model_id, prompt_template_version,
        prompt_text, response_json, embedding)
       VALUES ($1, $2, $3, $4, $5, $6, $7)`,
      [tenantId, locale, modelId, promptTemplateVersion,
       cacheKeyText, responseJson, embedding]
    );
    

    3. Redis + RediSearch 範例

    定義向量索引

    FT.CREATE semantic_cache_idx ON HASH PREFIX 1 sc:
      SCHEMA tenant_id TAG locale TAG model_id TAG prompt_template_version NUMERIC
             embedding VECTOR HNSW 6 TYPE FLOAT32 DIM 1536 DISTANCE_METRIC COSINE
    

    查詢(Python pseudo-code):

    vec = get_embedding(cache_key_text)  # 1536 維 float32
    
    q = (
      f"(@tenant_id:{{{tenant_id}}} "
      f"@locale:{{{locale}}} "
      f"@model_id:{{{model_id}}} "
      f"@prompt_template_version:[{ver} {ver}])=>[KNN 1 @embedding $vec AS sim]"
    )
    
    res = redis.ft("semantic_cache_idx").search(q, query_params={"vec": vec})
    if res.docs:
      sim = 1 - float(res.docs[0].sim)
      if sim >= MIN_SIMILARITY:
        return json.loads(res.docs[0].response_json)
    

    建議與注意事項

    1. 長輸入的 embedding 成本「反向暴增」

    常見錯誤:把 整個 prompt(含長 RAG context) 丟去做 embedding。

    問題:

    • context 動輒上千 tokens,embedding 成本跟 LLM inference 一起爆
    • 一點點 context 差異就讓相似度下降 → 命中率不升反降

    實務建議

    • 只對「stable 且短」的部分做 embedding:
    • user 問句
    • RAG context 的「摘要」而不是原文(可用小模型先 summarize 成 2–3 句)
    • 大型企業專案:先用 request 日誌做統計,估算 embedding 成本佔比,再決定精度/長度 trade-off

    2. 語義誤命中 → 幻覺與錯答風險

    語義快取本質上是「猜這問題和之前那題是不是本質相同」,猜錯就會回錯答案,而且錯得非常「自信」。

    風險控制手段:

    1. 提高門檻 + 多條件
    2. similarity 0.9+ + 同 tenant / locale / model / template_version
    3. 加上 lightweight 檢查模型
    4. 對 candidate 問題 & 當前問題再丟給小模型,問:
    5. 「這兩個問題是否在同一個具體情境下?只回答 yes/no」
    6. 只回部分結果
    7. 把 cache 命中當作「示範答案」塞進 system prompt,而不是直接當最終輸出

    3. 線上 A/B、hit rate 與 cost saving 監控

    (1) A/B 框架

    • A 組:只用 exact-match cache
    • B 組:開啟 semantic cache
    • 衡量指標:
    • cache_hit_rate:命中次數 / 總請求
    • avg_latency:端到端延遲
    • cost_per_1k_requests:可用 token 使用量 × 單價估算

    (2) 實作例:log 設計

    {
      "request_id": "...",
      "tenant_id": "...",
      "model_id": "gpt-4.1-mini",
      "semantic_cache_hit": true,
      "semantic_similarity": 0.91,
      "exact_cache_hit": false,
      "prompt_tokens": 923,
      "completion_tokens": 134,
      "latency_ms": 480
    }
    

    可以直接在 ClickHouse / BigQuery 做每日 dashboard:

    • 按 tenant 和 model 切分 semantic_cache_hit_rate
    • 比較「命中 vs 未命中」的平均 latency / token usage

    4. 上線前的設計 Checklist

    • Embedding 模型
    • [ ] 維度 768–1536,成本可接受
    • [ ] 支援你主要語言(中/英通常 OK,但特定語種要確認)
    • 距離度量與索引
    • [ ] PostgreSQL 使用 pgvector + ivfflat,設好 lists
    • [ ] Redis 使用 HNSW,確認記憶體預算
    • 快取 key 策略
    • [ ] 僅對 user query + 短 RAG 摘要做 embedding
    • [ ] 有 tenant_id / locale / model_id / prompt_template_version 條件
    • 風險控制
    • [ ] similarity 門檻可配置,預設 ≥ 0.85
    • [ ] 高風險業務要額外加一層 lightweight 檢查
    • 監控與 rollback
    • [ ] 有 semantic_cache_hit_rate / latency / token usage 監控
    • [ ] 開關旗標(feature flag)可在出問題時快速關閉

    只要把語義快取做成這樣一個「有監控、有開關、有版本」的基礎組件,你的 RAG / chat backend 通常能在不改業務邏輯的前提下,拿到一個非常直接的 成本與體感雙重優化。從 infra 角度來說,這個投資的 ROI 幾乎是整條 LLM pipeline 裡最高的一塊。

    🚀 你現在可以做的事

    • 在現有 RAG backend 中,先對 user 問句接入 text-embedding-3-smallbge-m3 的語義快取實驗路徑
    • 用 ClickHouse 或 BigQuery 建一個簡單 dashboard,監控 semantic_cache_hit_rate、延遲與 token 成本變化
    • 增加 tenant_id / locale / model_id / prompt_template_version 條件,並設好 min_similarity 旗標,逐步在低風險場景 rollout
  • 用 Gemini Managed Agents 搭建可控多代理系統

    用 Gemini Managed Agents 搭建可控多代理系統

    📌 本文重點

    • Managed Agents 讓多代理 workflow 更可控可審計
    • 背景任務與長流程能安全持續運行
    • remote MCP + 沙盒工具提升協作與安全
    • 憑證輪替支援零信任長連線場景

    Gemini Managed Agents 的最新更新,直接解決了多代理系統在實務上的四個痛點:長流程容易中斷、背景任務難管理、多代理協作缺乏控制平面、工具執行缺乏安全邊界、長連線憑證管理容易出事。如果你目前只是在做「單模型聊天 + 幾個工具」,這批能力讓你可以往「有審計、有重試、有觀測性」的多代理 workflow 進化,而且不需要自己再搭一層任務編排框架。


    重點說明

    1. 背景任務與長流程編排:從同步聊天到任務隊列

    新的 Managed Agents 支援在代理內啟動 背景任務(background tasks),並維持任務狀態。

    關鍵好處:

    • 可以把耗時操作(例如 ETL、長時間 API 輪詢、批次報表)移到背景,不阻塞前端對話。
    • 每個背景任務都有 狀態與 ID,便於你實作自家任務隊列、重試策略與恢復機制。
    • 代理本身幫你維護「對話上下文 + 任務上下文」,你只需在外層規劃任務生命週期。

    💡 關鍵: 把長流程變成有狀態、可重試的背景任務,是從「聊天玩具」升級成「可靠工作流系統」的關鍵一步。

    典型設計:

    • 前端對話 → 由主 Agent 判斷是否需要啟動背景任務。
    • 使用 Agents API 建立 task,狀態儲存在 Managed Agents 內部或你自己的 DB
    • 外部有一個「任務監控 worker」定期查詢任務狀態、做重試或告警。

    2. remote MCP / 多代理協作:控制平面 vs 應用層 SDK

    Managed Agents 現在可以直接連到 remote MCP。實務上有兩種典型 architecture:

    • 控制平面導向(Control Plane first)
    • 多個工具 / 子代理掛在 MCP server(例如一個 operations MCP、一個 data MCP)。
    • Managed Agent 只要知道 MCP endpoint,就能呼叫裡面的工具。
    • 適合大型企業,把權限、審計、資源配額集中放在 MCP 層。

    • 應用層 SDK 導向(SDK first)

    • 你在應用程式碼中透過 SDK 把工具包成 Gemini Tool / Functions,再掛到 Managed Agents。
    • 權限管理偏向 app side,例如每個 tenant 對應一組工具設定。

    關鍵差異在 權限與隔離

    • 控制平面模式:透過 MCP 做 RBAC、租戶隔離、審計;Managed Agent 像「智慧前端」。
    • SDK 模式:更靈活,適合快速迭代,但要自己補一套完整審計與 resource control

    3. 安全沙盒內整合自定義工具與函式

    更新後的 Managed Agents 允許在 安全沙盒(sandbox) 同時使用:

    • 官方 sandbox 工具(如瀏覽器、code executor)。
    • 你自定義的 functions / tools

    好處:

    • 你可以在受控環境內執行「可能有副作用」的操作,例如 DB query、檔案處理,而不直接暴露到外部系統。
    • 工具執行與 LLM 推理同樣有 超時與資源配額 控制,避免單一任務吃光整個 pod

    設計重點:

    • 每個工具要有明確的 作用範圍(只讀 / 可寫),把「刪除、修改」操作拆成獨立工具並 預設關閉
    • 在工具層做 輸入驗證與錯誤處理,避免 LLM 亂塞參數導致意外副作用。

    4. 憑證刷新與長連線安全:token 旋轉 + 零信任

    Managed Agents 支援在 不丟失 state 的情況下刷新憑證

    • 代理可以維持長流程(幾小時到幾天)的狀態,同時你的服務端可以定期輪替 API tokenOIDC access token
    • 這讓零信任架構更好落地:
    • 不再有「因為流程長,只好給超長效 token」的妥協。
    • 可以要求所有外部呼叫都透過短效憑證 + 中央驗證服務。

    💡 關鍵: 「長流程 + 短效憑證」的組合,讓零信任不再與實務需求衝突。


    實作範例

    以下用一個從「單模型聊天」升級成「有背景任務 + 多代理協作 + 審計」的簡化範例示意(以 Node.js 伺服器 + Gemini API 為例,為示意用虛擬碼)。

    1. 建立核心 Managed Agent

    import { AgentsClient } from "@google-ai/gemini";
    
    const agents = new AgentsClient({
      projectId: process.env.GCP_PROJECT_ID,
      location: "global",
    });
    
    // 建立主 Agent:負責對話 + 任務編排
    async function createMainAgent() {
      const [agent] = await agents.createAgent({
        parent: "projects/xxx/locations/global",
        agent: {
          displayName: "orchestrator-agent",
          model: "gemini-2.0-pro",
          // 掛上 MCP 與工具
          tools: [
            { mcpServer: { endpoint: process.env.MCP_OPS_URL } },
            { mcpServer: { endpoint: process.env.MCP_DATA_URL } },
            { functionDeclarations: [
              {
                name: "schedule_background_job",
                description: "Create a background task for long-running workflow",
                parameters: {
                  type: "object",
                  properties: {
                    jobType: { type: "string" },
                    payload: { type: "object" },
                  },
                  required: ["jobType", "payload"],
                },
              },
            ]},
          ],
          // 安全設定:限制可寫操作
          safetySettings: {
            allowWriteOps: false,
          },
        },
      });
    
      return agent.name; // 用來後續呼叫
    }
    

    重點:

    • AgentsClient.createAgent 建立主 Agent,掛上多個 MCP 伺服器 與自定義 function
    • safetySettings 示意限制寫入操作,實務上可自訂更細。

    2. 前端對話:從「單次聊天」變成可啟動背景任務

    // 使用者傳入訊息,主 Agent 可能決定啟動背景任務
    async function handleUserMessage(agentName: string, sessionId: string, text: string) {
      const [response] = await agents.generateMessage({
        name: agentName,
        // sessionId 用你自己的,方便日後審計與追蹤
        session: { id: sessionId },
        prompt: { text },
      });
    
      // 若 LLM 觸發工具呼叫,可能是 schedule_background_job
      if (response.toolCall) {
        const call = response.toolCall;
        if (call.name === "schedule_background_job") {
          const taskId = await createBackgroundTask(call.args);
          // 把 taskId 回寫到對話,讓使用者可以查詢
          return { reply: `已建立背景任務,ID: ${taskId}` };
        }
      }
    
      return { reply: response.outputText };
    }
    

    這裡用 generateMessage(或官方實際命名類似方法)示意:

    • 你自己維護 sessionId,不要依賴傳輸層的 session 概念,以免遇到像 MCP 無狀態 變更就斷鏈。
    • 工具呼叫觸發後,交給應用層建立背景任務。

    3. 背景任務隊列與重試設計

    // 簡化版任務建立
    async function createBackgroundTask({ jobType, payload }) {
      const taskId = crypto.randomUUID();
    
      await db.tasks.insert({
        id: taskId,
        type: jobType,
        payload,
        status: "pending",
        retryCount: 0,
      });
    
      return taskId;
    }
    
    // 任務 worker:定期跑
    async function taskWorkerLoop() {
      const tasks = await db.tasks.find({
        status: { $in: ["pending", "retry"] },
      }).limit(50);
    
      for (const task of tasks) {
        try {
          await runTask(task); // 實際呼叫 MCP 或其他工具
          await db.tasks.update(task.id, { status: "done" });
        } catch (err) {
          const nextRetry = task.retryCount + 1;
          if (nextRetry > 3) {
            await db.tasks.update(task.id, { status: "failed" });
          } else {
            await db.tasks.update(task.id, {
              status: "retry",
              retryCount: nextRetry,
            });
          }
        }
      }
    }
    

    重點:

    • 背景任務管理放在你的應用層,但任務內容可以是對 Managed Agents / MCP 工具 的呼叫。
    • 任務狀態與重試策略明確放在 DB,避免「背景任務孤兒進程」沒人管。

    4. 憑證刷新與零信任示意

    // 透過中介層取得短效 token,供 AgentsClient 使用
    async function getAgentsClient() {
      const token = await authService.getRotatingToken(); // 有效期 15 分鐘
      return new AgentsClient({
        authToken: token,
        projectId: process.env.GCP_PROJECT_ID,
      });
    }
    
    // 每次呼叫都用最新 token
    async function safeGenerateMessage(agentName, sessionId, text) {
      const client = await getAgentsClient();
      const [response] = await client.generateMessage({
        name: agentName,
        session: { id: sessionId },
        prompt: { text },
      });
      return response;
    }
    

    這種做法搭配 Managed Agents 的「不丟 state 憑證刷新」能力,可以在維持長流程的同時,讓底層 token 持續輪替。


    建議與注意事項

    1. 防止 Agent 自動刪庫型事故

    • 所有「修改 / 刪除」類工具:
    • 預設不掛到主 Agent,改掛到專門的「ops Agent」,再用人工或嚴格策略觸發。
    • 在工具層做 白名單 / 黑名單 檢查,例如禁止 DROP TABLE、限制影響範圍。
    • 所有高風險操作應要求:
    • 二次確認(LLM 生成計畫 → 使用者或守門服務審核 → 才執行)。

    2. 審計與回溯:不要把責任丟給 MCP

    MCP 轉為 stateless 之後,如果你沒自己建立 trace id,就會遇到:

    • 同一條資金轉帳流程,log 看起來是四個不相干的事件,無法證明「誰觸發了什麼」。

    建議:

    • 在應用層產生 correlationId / traceId,寫入:
    • 所有 Agents API 呼叫
    • MCP 請求的 metadata
    • DB 任務表與 log 系統
    • 在出事時可以把「對話 → Agent 決策 → MCP 工具呼叫 → DB 操作」串回一條 timeline

    3. 背景任務治理:避免孤兒進程與資源爆炸

    • 每個任務必須有:
    • 明確 statuspending / running / retry / failed / done)。
    • 最大重試次數與 退避策略exponential backoff)。
    • 超時與最大執行時間限制。
    • 週期性 job 清理:
    • 清掉超過 SLApending 任務,標記為 timeout_failed
    • 對高失敗率任務發告警,不要無限重試打爆外部 API

    4. 多代理協作架構選型

    • 團隊偏「平台 / SRE」:建議 控制平面模式,用 MCP 集中治理,Managed Agents 做業務邏輯。
    • 團隊偏「產品 /快速迭代」:先用 SDK 模式,在 app 層掛工具,後續再逐步抽到 MCP。

    5. Observability:為多代理 workflow 補上眼睛

    • 最低限度:
    • 每次 Agent 呼叫記錄:agentNamesessionIdtraceId、使用工具列表、執行結果。
    • 建議導入:
    • 分散追蹤(如 OpenTelemetry),把 Agents / MCP / DB 統一進 tracing system。
    • 守門 dashboard:顯示背景任務隊列狀態、失敗率、平均耗時。

    結論:Gemini Managed Agents 的背景任務、remote MCP、安全沙盒工具與憑證刷新能力,讓你可以在現有專案裡自然從「單模型聊天」過渡到「可控、可審計的多代理 workflow」。核心心法是:把任務編排與審計留在應用層,讓 Managed Agents 專心做協作與自動化,並以零信任與資源治理觀點設計整體架構。

    🚀 你現在可以做的事

    • 在現有聊天應用中加入 sessionIdtraceId 與簡單任務表,開始嘗試背景任務編排
    • 盤點現有工具,決定哪些適合掛在 MCP、哪些用 SDK 模式直接掛到 Managed Agents
    • 規劃短效 token 取得流程,實作一個中介層 authService.getRotatingToken() 來配合 Managed Agents 使用
  • Hy3 MoE 架構與部署實戰

    Hy3 MoE 架構與部署實戰

    📌 本文重點

    • Hy3 以 MoE 架構達成「295B 效能 / 21B 成本」
    • 路由與專家分工讓幻覺率顯著下降至約 5.4%
    • 實務上可搭配小模型作為「精度後盾」降低整體成本
    • 部署時需特別注意 KV cache、量化與多卡配置風險

    Hy3 解決的是很直接的痛點:想要接近 300B 模型的效能,但推理預算只有 20B 級別的算力。透過 Mixture-of-Experts (MoE) 架構,Hy3 在推理時只激活約 21B active 參數,卻能逼近 2–5 倍大小 Dense 模型的表現,同時官方宣稱幻覺率約 5.4%。對成本敏感、需要長上下文與高可靠性的專案,這是相當實用的折衷方案。

    💡 關鍵: 透過只啟用約 21B 的活躍參數,Hy3 能以 20B 級成本,逼近 2–5 倍參數量 Dense 模型的效能,並把幻覺率壓到約 5.4%。


    重點說明

    1. Hy3 的 MoE 架構:295B Total / 21B Active 怎麼來

    Hy3 採用典型的 稀疏 MoE Transformer

    • 每層包含 多個 Experts(總參數加起來約 295B)
    • 每個 token 經過 Router(門控網路),選出 Top-k Experts(例如 k=2
    • 只對被選中的 Experts 做前向計算,也就是 active 參數 ≈ 21B

    這意味著:

    • 理論效能 接近「每層很多專家都參與訓練」的 295B 模型
    • 推理成本 接近 20B 左右 Dense 模型

    Hy3 的設計重點在於:

    • 路由網路足夠穩定,避免 token 在不同 experts 之間亂跑造成延遲抖動
    • 專家分工明確,在知識檢索、數學推理、長文本等不同領域有專門專家,提高精度並降低幻覺率

    💡 關鍵: 295B total / 21B active 的設計本質是「訓練用超大模型,推理只用少數專家」,在效能與成本間找到新的平衡。

    2. 路由與稀疏激活:為何能省算力又減少幻覺

    MoE 的核心是 Router:

    • Router 接收 hidden states,輸出每個 token 對各個 expert 的 score
    • 使用 Top-k routing,只選前 k 個分數最大的 expert
    • 透過 load balancing loss 等技術,讓各專家負載均衡

    實際好處:

    • 算力省下來:每個 token 不再過所有 FFN,而只過少數幾個 FFN experts
    • 幻覺降低:不同專家可專注在特定語域或任務上,例如事實問答 vs 創意寫作;Router 學會把事實查詢導向「穩定專家」,減少亂編內容

    對工程來說,這代表:

    • 你可以用 更少的 GPU / 更低成本,得到接近超大 Dense 模型的體感效能
    • RAG、Agent、長對話場景,MoE 尤其吃香:專家分工和路由能讓模型在多輪推理中維持上下文一致性

    💡 關鍵: MoE 不只是省算力,關鍵在「專家分工 +路由」讓模型更願意引用來源與承認不知道,實際上降低了幻覺率。

    3. Dense 模型 vs Hy3 在實務場景的差異

    以常見的 20B Dense 模型對比 Hy3(21B active):

    • RAG
    • Dense:檢索結果融合較「平均」,容易出現模糊答案
    • Hy3:某些專家專門處理檢索整合與引用,更願意說「不知道」或引用原文,幻覺率降低

    • Agent / 工具調用

    • Dense:對工具參數的格式、錯誤恢復通常要額外訓練
    • Hy3:專門專家負責結構化輸出,工具呼叫更穩定、出錯次數更少

    • 長對話 / 長上下文

    • Dense:上下文變長時,容易失焦或自相矛盾
    • Hy3:路由傾向把摘要、引用、狀態維持交給特定專家,長對話一致性更好

    實作範例

    以下示範在 Hugging Face 載入 Hy3,並在常見 GPU/CPU 環境下做推理。

    1. 基本載入與推理

    Hy3 模型集合:https://huggingface.co/collections/tencent/hy3(實際使用時請對應具體模型名稱)。

    from transformers import AutoModelForCausalLM, AutoTokenizer
    import torch
    
    MODEL_ID = "tencent/hy3-295b-21b-active"  # 示意名稱,請換成實際 ID
    
    # 建議:先用 bfloat16,在支援的 GPU 上效果最好
    dtype = torch.bfloat16 if torch.cuda.is_available() else torch.float32
    
    tokenizer = AutoTokenizer.from_pretrained(MODEL_ID)
    model = AutoModelForCausalLM.from_pretrained(
        MODEL_ID,
        torch_dtype=dtype,
        device_map="auto",  # 讓 HF 自動把 MoE 分配到多 GPU
    )
    
    prompt = "請用要點說明 Hy3 MoE 架構的優勢。"
    inputs = tokenizer(prompt, return_tensors="pt").to(model.device)
    
    with torch.inference_mode():
        outputs = model.generate(
            **inputs,
            max_new_tokens=256,
            do_sample=False,
            temperature=0.7,
            top_p=0.9,
        )
    
    print(tokenizer.decode(outputs[0], skip_special_tokens=True))
    

    關鍵 API / 參數

    • device_map="auto":MoE 結構下,讓 HF 自動做多 GPU 分配
    • torch_dtype:若打算量化,需要改成 torch.float16 或配合 bitsandbytes
    • max_new_tokens:MoE 下長輸出的 KV cache 成本高,這個值要控制

    2. GPU / CPU 配置建議

    以 Hy3 這種 295B total / 21B active 的等級,建議配置

    • 單機多卡:
    • A100 80G × 2 或 H100 80G × 1:可跑 bfloat16 推理,保留足夠 KV cache
    • 4090 24G × 2–3:需配合 4bit / 8bit 量化,避免 OOM

    • 混合 CPU/GPU:

    model = AutoModelForCausalLM.from_pretrained(
        MODEL_ID,
        torch_dtype=torch.float16,
        device_map={
            "router": 0,         # GPU 0:路由與部分 attention
            "expert_0": 0,
            "expert_1": 1,       # GPU 1:其他專家
            "lm_head": "cpu",   # CPU:輸出層,減少 GPU 記憶體壓力
        }
    )
    

    在真實模型中,模組名稱可能不同,但概念是:Router + 熱門專家放 GPU,冷門專家與 lm_head 可移至 CPU

    3. 與 Dense 模型在 RAG / Agent 的測試策略

    可以用同一套 RAG pipeline,切換模型比較:

    from my_rag_lib import rag_answer  # 假設你已有 RAG 模組
    
    models = {
        "dense_20b": "tencent/dense-20b",
        "hy3_moe": "tencent/hy3-295b-21b-active",
    }
    
    query = "根據文件說明,Hy3 的幻覺率是多少?請引用來源。"
    
    for name, mid in models.items():
        tokenizer = AutoTokenizer.from_pretrained(mid)
        model = AutoModelForCausalLM.from_pretrained(mid, device_map="auto")
        ans = rag_answer(query, model, tokenizer)
        print(f"[{name}]\n{ans}\n---")
    

    重點是觀察:

    • 是否傾向引用檢索片段
    • 是否願意說不知道
    • 對同一段長文件的理解一致性

    Hy3 理論上在這幾點會優於同算力的 Dense 模型。

    4. 多租戶場景的「前線小模型 + 背後 Hy3」策略

    典型架構:

    • 前線小模型(例如 7B Dense,低延遲、便宜)
    • 背後 Hy3:只在需要高精度 / 高價值查詢時調用

    簡化版路由邏輯:

    from fastapi import FastAPI
    
    app = FastAPI()
    
    small_model = AutoModelForCausalLM.from_pretrained("tencent/small-7b", device_map="auto")
    small_tok = AutoTokenizer.from_pretrained("tencent/small-7b")
    
    hy3_model = AutoModelForCausalLM.from_pretrained("tencent/hy3-295b-21b-active", device_map="auto")
    hy3_tok = AutoTokenizer.from_pretrained("tencent/hy3-295b-21b-active")
    
    
    def need_hy3(prompt: str, user_tier: str) -> bool:
        # 示例:
        # 1. 高價值客戶
        # 2. 涉及關鍵決策 / 法律 /醫療關鍵字
        # 3. 前線小模型給出低置信度(可用 logprob 或 self-consistency)
        if user_tier == "premium":
            return True
        if any(k in prompt for k in ["法律", "合約", "醫療", "風險"]):
            return True
        return False
    
    
    @app.post("/chat")
    async def chat(req: dict):
        prompt = req["prompt"]
        user_tier = req.get("tier", "free")
    
        if need_hy3(prompt, user_tier):
            model, tok = hy3_model, hy3_tok
        else:
            model, tok = small_model, small_tok
    
        inputs = tok(prompt, return_tensors="pt").to(model.device)
        with torch.inference_mode():
            outputs = model.generate(**inputs, max_new_tokens=512)
        resp = tok.decode(outputs[0], skip_special_tokens=True)
        return {"reply": resp}
    

    結論:Hy3 不一定要當唯一主力模型,更適合當「精度後盾」,搭配前線小模型可以大幅壓低整體推理成本。


    建議與注意事項

    1. MoE 路由不穩定與延遲抖動

    MoE 天生有一個問題:不同請求可能被 Router 分配到不同專家,造成 延遲不穩定

    建議:

    • 監控每次推理的 expert load metrics(如果官方提供)
    • 對延遲敏感的接口,可以限制 max_new_tokens,並在 Gateway 層做超時保護
    • 不要把 Hy3 直接暴露在毫秒級 SLA 的同步 API 上,加一層 queue 或 streaming 比較安全

    2. KV cache 與多專家記憶體放大

    MoE 下,KV cache 不只跟序列長度、層數關係,還跟實際活躍專家數有關

    • 長上下文 + 多輪對話時,KV cache 很容易頂滿 GPU

    最佳實踐:

    • 開啟 use_cache=True,但在自建服務中要做 分段裁剪(例如最多保留 N 輪對話)
    • 對長對話場景使用 摘要策略:定期用 Hy3 產生對話摘要,替換部分歷史訊息

    3. 量化與張量並行的細節

    MoE + 量化 + 多 GPU = 典型踩坑組合。

    注意:

    • 使用 bitsandbytes 4bit/8bit 量化 時,要確認 Router 及 lm_head 是否也被量化,避免路由精度崩壞
    from transformers import BitsAndBytesConfig
    
    bnb_config = BitsAndBytesConfig(
        load_in_4bit=True,
        bnb_4bit_quant_type="nf4",
        bnb_4bit_use_double_quant=True,
    )
    
    model = AutoModelForCausalLM.from_pretrained(
        MODEL_ID,
        quantization_config=bnb_config,
        device_map="auto",
    )
    
    • tensor parallel(如 DeepSpeed / vLLM)時,要確認 MoE 支援:
    • 有些框架只對 Dense 層做 TP,MoE 部分需要額外配置
    • 尤其是 Router 部分的跨卡通訊,可能成為瓶頸

    4. 遷移建議:從 Dense 轉 Hy3

    如果你現在線上跑的是 20B Dense 模型,想換 Hy3:

    • 先在離線評測跑一輪:包含 RAG、Agent、長對話測試集,確認幻覺率與延遲分布
    • 線上採用 灰度發布
    • 部分租戶或部分路由(例如高價值請求)切到 Hy3
    • 監控:錯誤回報率、延遲 95/99 百分位、GPU 利用率、成本/請求
    • 保留 Dense 模型作為 fallback:若 Hy3 OOM 或延遲過高,自動切回 Dense 小模型

    總結:Hy3 的 295B total / 21B active MoE 設計,本質上是在「效能 vs 成本」之間給工程團隊一個新的平衡點。只要處理好路由穩定性、KV cache、量化與多卡配置,你可以在不升級到超大 Dense 模型的前提下,大幅提升 RAG、Agent、長對話場景的可靠度與體感智慧度。

    🚀 你現在可以做的事

    • Hy3 模型集合 挑一個模型,在現有推理程式中測試 device_map="auto" 部署
    • 用你既有的 RAG / Agent 測試集,對比「20B Dense vs Hy3」在幻覺率與延遲上的差異
    • 在現有小模型服務前面,實作一個簡單的 need_hy3() 路由策略,做一周灰度流量測試成本與效果
  • Claude Sonnet 5 低成本 Agent 實戰指南

    Claude Sonnet 5 低成本 Agent 實戰指南

    📌 本文重點

    • Sonnet 5 適合作為預設 Agent 主力模型
    • 成本遠低於頂級模型且能力接近 Opus
    • 長上下文與工具調用適合多步驟工作流
    • 安全策略偏保守,較利企業導入

    Claude Sonnet 5 解決的痛點很直接:想做多步驟 Agent 工作流,但 Opus / GPT 旗艦太貴、開源模型又不夠穩。Sonnet 5 在長上下文、工具調用和安全策略上已能覆蓋大多數企業場景,同時單 token 成本顯著低於頂級模型,適合作為預設 Agent 基座,只在少數高難度任務再切換到更強模型。


    重點說明:為什麼 Sonnet 5 值得當主力 Agent 模型?

    1. 能力與成本曲線:接近 Opus,價格接近中階

    根據公開基準與 The Decoder 報導,Claude Sonnet 5 在 GDPval-AA v2 知識工作測試上已超過 Opus 4.8,但定價仍是「中階模型」等級。

    💡 關鍵: Sonnet 5 在知識工作表現已追近甚至超過頂級模型,但價格仍是中階,適合作為大多數任務的預設主力。

    實際含義:

    • 一般知識工作 / 文件處理 / 商務決策代理,Sonnet 5 足以勝任,不需要 Opus 級別。
    • 若你現在線上大量跑 GPT-4.5 / GPT-5.5 這類旗艦模型,把 70–80% 任務切到 Sonnet 5,只在高風險、高價值 task 才升級模型,通常能立刻降下 30–50% API 費用。

    2. 長上下文 + 工具調用:更適合多步驟流水線

    實務上 agent 不是一兩輪對話,而是:

    1. 讀長文件 / 多來源資料
    2. 規劃任務
    3. 多輪工具調用(DB / API / Code Interpreter
    4. 產出決策或報告

    Sonnet 5 的優勢:

    • 長上下文:支援巨大 context(依官方規格,多數情境已可覆蓋數十萬 token 級)。對 RAG + workflow 代表:
    • 可以減少 aggressive chunking,讓模型一次看到完整流程 / 合約 / 需求文檔。
    • 可以把多輪工具結果與中間推理保留在同一對話,減少「忘記之前做了什麼」。
    • 工具調用(Tool Use):支援結構化工具 schema,類似 OpenAI functions / tools,並在安全策略上更保守(例如對可疑指令會自動拒絕調用某些敏感工具)。這對企業代理的好處是:
    • 減少「亂調 API」的風險
    • 在合規敏感場景(金融、法務)較容易通過審查

    3. 安全策略:有意識地「不做太強」的資安能力

    Anthropic 明確說 Sonnet 5 在網路攻擊和資安任務上的能力刻意壓低,低於某些已被政府封鎖的模型。這點對企業是加分:

    • 碼農代理 / DevOps Agent場景,可以寫 code、修 bug,但不太會幫你設計攻擊腳本。
    • 對內部稽核來說,可當作「內建安全減速器」,搭配額外安全層更容易說服安全部門。

    實作範例:用 Sonnet 5 搭建多步驟 Agent 工作流

    以下用類 pseudo-code 展示一個文件審閱 + 系統操作的多步驟 Agent pipeline,並示範如何在 Sonnet 5 / Opus / 開源模型間切分任務。

    1. 基本呼叫:帶工具的 Sonnet 5 Agent

    import anthropic
    
    client = anthropic.Anthropic(api_key=ANTHROPIC_API_KEY)
    
    TOOLS = [
      {
        "name": "fetch_contract",
        "description": "依 contract_id 取得最新合約全文",
        "input_schema": {
          "type": "object",
          "properties": {"contract_id": {"type": "string"}},
          "required": ["contract_id"]
        }
      },
      {
        "name": "update_crm",
        "description": "更新 CRM 中的客戶標籤與備註",
        "input_schema": {
          "type": "object",
          "properties": {
            "customer_id": {"type": "string"},
            "tags": {"type": "array", "items": {"type": "string"}},
            "note": {"type": "string"}
          },
          "required": ["customer_id", "note"]
        }
      }
    ]
    
    SYSTEM_PROMPT = """
    你是一個企業合約審閱 Agent:
    - 先閱讀合約與上下文
    - 給出風險摘要與建議
    - 如有需要,呼叫工具 fetch_contract / update_crm 完成任務
    - 僅在確定資訊足夠時才更新 CRM
    """
    
    resp = client.messages.create(
      model="claude-3.7-sonnet-5",  # **核心:以 Sonnet 5 當主力 agent**
      max_tokens=2048,
      temperature=0.2,
      system=SYSTEM_PROMPT,
      tools=TOOLS,
      messages=[{
        "role": "user",
        "content": [
          {"type": "text", "text": "請審閱合約 C-2025-018,並視需要更新 CRM。"}
        ]
      }]
    )
    
    for content_block in resp.content:
      if content_block.type == "tool_use":
        tool = content_block
        if tool.name == "fetch_contract":
          contract = fetch_contract_from_db(tool.input["contract_id"])  # 你的實作
          # 把 tool result 回傳給 Sonnet 5,形成多輪 agent 流程
          resp = client.messages.create(
            model="claude-3.7-sonnet-5",
            max_tokens=2048,
            messages=[
              {"role": "assistant", "content": [tool]},
              {"role": "user", "content": [{
                "type": "tool_result",
                "tool_use_id": tool.id,
                "content": contract
              }]}
            ]
          )
    

    重點設定:

    • model:用 claude-3.7-sonnet-5 當預設 Agent,引導它做規劃 + 工具決策
    • tools:一定要寫清楚用途與輸入 schema,Sonnet 5 的工具調用在描述清晰時會穩定很多。
    • temperature=0.2:工作流類場景建議偏低,避免「創造力」帶來流程偏離。

    2. 多模型架構:什麼給 Sonnet 5,什麼留給 Opus / 開源?

    可採用一個簡單的 routing layer

    from enum import Enum
    
    class TaskClass(Enum):
      KNOWLEDGE_WORK = "knowledge_work"  # 合約審閱、報告撰寫
      HEAVY_REASONING = "heavy_reasoning"  # 複雜架構設計、難題推理
      LIGHT_UTILITY = "light_utility"  # 文本清洗、格式轉換
    
    
    def route_model(task: TaskClass) -> str:
      if task == TaskClass.KNOWLEDGE_WORK:
        return "claude-3.7-sonnet-5"  # **主力:成本與能力平衡點**
      if task == TaskClass.HEAVY_REASONING:
        return "claude-3.7-opus"      # 僅在高價值、關鍵決策時啟用
      if task == TaskClass.LIGHT_UTILITY:
        return "local-llama-3.2-8b"   # 或任一開源模型,跑在自家 GPU
    
    
    # 用法
    model_id = route_model(TaskClass.KNOWLEDGE_WORK)
    resp = client.messages.create(
      model=model_id,
      ...
    )
    

    實務建議:

    • 70–80% 任務:給 Sonnet 5(常駐 Agent、日常工作流)。
    • 10–20% 高難度:需要高可靠 reasoning / 風險極高決策 → 升級到 Opus / GPT-5.5 類。
    • 剩餘雜務:可以用開源模型批量處理(log 清洗、模板生成、簡單分類)。

    3. 記憶系統整合:避免「每次都要重新教」

    參考 Hermes 記憶系統的經驗,問題通常不在模型,而在記憶層設計

    核心做法:

    • Agent 視為「無狀態推理引擎」,持久狀態放在你自己的記憶服務DB + 向量庫)。

    簡化示意:

    # 1) 從 persistent storage 取出該使用者的長期記憶
    memories = memory_store.query(user_id="u_123", top_k=10)
    
    # 2) 把記憶壓縮成 system / context 提示
    memory_context = compress_memories(memories)  # 用另一個 Sonnet 5 批次壓縮也可以
    
    resp = client.messages.create(
      model="claude-3.7-sonnet-5",
      max_tokens=1536,
      system=f"""
    你是長期協助用戶的個人工作助理。
    以下是你對此用戶的長期記憶摘要,請在回應時優先參考:
    {memory_context}
    """,
      messages=[...
      ]
    )
    
    # 3) 回合結束後,把對話摘要寫回記憶系統
    summary = summarize_with_sonnet5(conversation_turn)
    memory_store.upsert(user_id="u_123", content=summary)
    

    重點:不要期待 Sonnet 5 自己「記得」所有歷史;記憶是架構問題,不是換模型就會好的問題


    建議與注意事項:真實專案導入 Sonnet 5 的坑

    1. Token 預算:Agent 能力被低估,成本也容易失控

    英國 AISI 的研究指出:把 token budget 放大 10 倍,軟體工程任務成功率可提升約 25%。這對 Sonnet 5 有兩個實務啟示:

    💡 關鍵: 在多步驟任務中適度提高 token budget,往往能顯著提升成功率,但必須搭配明確的預算控管機制。

    1. 不要用 benchmark 上的「單輪小 budget」結果直接低估 Sonnet 5 的 agent 能力。
    2. 真實系統要 顯式設計 token 過程控管,否則長上下文 + 多輪工具調用會把帳單拉爆。

    實作建議:

    • 在你的 orchestrator 層做全任務 token 上限,例如:
    MAX_TASK_TOKENS = 40_000
    
    state = {
      "tokens_used": 0,
      "steps": 0
    }
    
    while not done:
      resp = client.messages.create(...)
      state["tokens_used"] += resp.usage.input_tokens + resp.usage.output_tokens
      state["steps"] += 1
      if state["tokens_used"] > MAX_TASK_TOKENS:
        raise BudgetExceededError("Agent token budget exceeded")
    
    • 對於單次調用,根據場景設 max_tokens
    • 報告生成:1024–4096
    • 工具決策:256–768
    • 中間思考(chain-of-thought)可用隱式提示 + 上限控制,避免瘋狂自言自語。

    2. 錯誤恢復與重試:不要讓 Agent 一路跑到爆掉

    Sonnet 5 在工具調用上普遍穩定,但實務上仍會遇到:

    • 工具輸入 schema 不符合
    • 工具執行失敗(timeout / 400 / 500
    • Agent 因缺 context 做出錯誤決策

    最佳實踐:

    1. 工具層要有自己的驗證與重試,不要完全相信模型輸入。
    from pydantic import BaseModel, ValidationError
    
    class UpdateCrmPayload(BaseModel):
      customer_id: str
      tags: list[str] = []
      note: str
    
    
    def handle_tool_call(tool):
      try:
        payload = UpdateCrmPayload(**tool.input)
      except ValidationError as e:
        # 把錯誤回傳給 Sonnet 5,請它修正輸入
        return {"status": "invalid_input", "error": str(e)}
    
      try:
        res = call_crm_api(payload)
        return {"status": "success", "result": res}
      except Exception as e:
        return {"status": "tool_error", "error": str(e)}
    
    1. Agent 本身做step-level checkpoint:每完成一個關鍵子任務就落盤,失敗時從最近 checkpoint 重跑,而不是從頭開始。

    3. 任務適配:什麼放 Sonnet 5,什麼不要硬塞給它

    適合用 Sonnet 5 當主力的任務:

    • 多步驟 知識工作代理:合約 / 法遵審閱、財報分析、專案規劃、需求拆解。
    • 工具中樞 Agent:負責 orchestrate 多個內部服務與子 Agent
    • 長上下文流程:需要消化大量規格、流程文件後再操作系統。

    建議留給 Opus / 更強模型的任務:

    • 高風險決策:例如金融交易策略生成、法務最終意見草擬。
    • 需要極高推理深度的算法 / 架構設計題。

    建議留給更小 / 開源模型的任務:

    • 批量格式轉換、log 清洗與標註。
    • 嚴格成本敏感、但容錯率高的場景(例如內部搜尋候選排序)。

    4. 安全防護與供應鏈攻擊:不要只相信模型的「安全訓練」

    近期多個 AI Agent 供應鏈攻擊案例(prompt injection、工具回傳惡意內容、外部 API 回傳帶有攻擊指令的文字)提醒我們:

    • Sonnet 5 的安全性 是加分項,但不是防火牆

    💡 關鍵: 模型層安全訓練無法取代系統層權限控管與審核機制,特別是在 Agent 能直接操作內部系統時。

    • 尤其在 Agent 可以訪問內部系統時,要額外注意:
    • 工具白名單 + 嚴格權限:不同 Agent 只能看 / 改自己該動的系統。
    • 對所有來自外部世界的文字,在餵回模型前做最小化與清洗,避免讓外部 prompt 直接控制 Agent
    • 關鍵操作(轉帳、刪除資料、變更權限)一律加 人類確認 / 多重簽核,不要讓 Sonnet 5 直接下手。

    把 Claude Sonnet 5 當成「預設 Agent 基座」來設計系統,搭配:

    • 明確的多模型路由
    • 顯式的 token 預算與錯誤恢復
    • 外掛記憶系統與安全層

    你可以在不爆成本的前提下,把原本只能在 POC 裡玩的 Agent 工作流,真正放進生產環境跑起來。

    🚀 你現在可以做的事

    • 審視現有 GPT-4.5 / 5.5Opus 使用場景,標記出可降級給 claude-3.7-sonnet-5 的 70–80% 任務
    • 在現有 Agent 架構中加入 route_model() 邏輯,實作多模型路由與 token 預算控管
    • 建立一個簡單的記憶服務(DB + 向量庫),將 Sonnet 5 當作無狀態推理引擎接入現有業務流程
  • 用 agents-cli 打造可運維的企業級 AI 代理

    用 agents-cli 打造可運維的企業級 AI 代理

    📌 本文重點

    • agents-cli 把零散工具與 LLM 變成企業級代理工作流
    • 從本地 YAML 到 Google Cloud,自動處理部署與運維
    • 透過 Skill/Agent/Workflow 抽象設計長任務與多工具協作
    • 以 infra-as-code 思維接入 CI/CD,強調安全、成本與邊界控制

    agents-cli 解決的核心痛點很直接:把「一堆零散的工具 + LLM」變成「可觀測、可部署、可運維的企業級代理工作流」。從本地 YAML 設定開始,到在 Google Cloud 串 Cloud Run / Pub/Sub / Vertex AI,它幫你處理流程編排、狀態管理、重試機制與部署細節,讓 Agent 真正變成基礎設施,而不是一支難以維護的 side project。


    重點說明

    1. 架構:技能(Skill)與工作流(Agent Workflow)的抽象

    agents-cli 把整個系統拆成三層:

    • Skill:可被代理調用的「工具」,可以是 HTTP APICloud Run、自家微服務或腳本。
    • Agent:具備目標與決策能力的 LLM,根據上下文決定要呼叫哪些 Skill
    • Workflow:如何觸發、排程、分派與監控 Agent 任務(多步、多工具、長流程)。

    在專案結構上,你會看到類似:

    agents-project/
      skills/
        bigquery_query.yaml
        github_review.yaml
      agents/
        data-pipeline-agent.yaml
        support-agent.yaml
      workflows/
        nightly-etl.yaml
        release-checklist.yaml
      envs/
        dev.yaml
        prod.yaml
    

    Skill 定義了 輸入/輸出 schema、實體執行位置(Cloud Run URL / Pub/Sub topic)、權限需求Agent 則描述 使用的 LLM(如:Vertex AI Gemini)、工具列表、記憶/狀態儲存方式

    💡 關鍵: Skill / Agent / Workflow 三層抽象,讓複雜代理系統可以用清晰結構拆開管理與維護。


    2. 本地開發到雲端部署的標準流程

    在 CLI 層面,agents-cli 抽象出一條典型路徑:

    1. 本地定義與模擬:用 YAML/JSON 定義 SkillAgent,使用 agents run 在本地模擬工作流。
    2. 雲端資源生成:使用 agents deploy 把設定轉成 Cloud Run / Pub/Sub / Vertex AI 的組合。
    3. 可觀測性與評估:透過內建 trace/log,或串接類似 LangSmith 的觀測平台,監控 LLM 行為與成本。

    常見指令會長這樣:

    # 初始化專案
    agents init my-enterprise-agent
    
    # 本地跑某個工作流
    agents run workflows/nightly-etl.yaml --env envs/dev.yaml
    
    # 部署到 Google Cloud
    agents deploy --env envs/prod.yaml \
      --project my-gcp-project \
      --region asia-east1
    
    # 檢查部署狀態
    agents status --project my-gcp-project
    

    envYAML 會綁定 GCP 專案、Region、Service Account 等環境設定,讓同一套 Agent 設定可以跨 dev/stage/prod 運行。


    3. 多工具調用、狀態與長任務管理

    agents-cli 的工作流設計,基本上幫你處理三件事:

    • 多工具選擇與調度:LLM 透過工具描述(tool schema)和系統 prompt,決定當前步驟要用哪個 Skill
    • 狀態記錄:把每步執行的輸入、輸出與決策 trace 下來,存到 Cloud Logging / BigQuery / 自訂 DB
    • 長任務處理:用 Pub/Sub + Cloud Run 做非同步排程、重試、錯誤恢復。

    典型的 Skill 設定示例:

    # skills/bigquery_query.yaml
    name: bigquery_query
    runtime: cloud_run
    endpoint: https://bigquery-run-service-xxxx.run.app/query
    input_schema:
      type: object
      properties:
        sql:
          type: string
        dataset:
          type: string
    output_schema:
      type: object
      properties:
        rows:
          type: array
          items:
            type: object
    retry_policy:
      max_attempts: 3
      backoff_seconds: 30
    logging:
      enabled: true
      sink: bigquery
    

    Agent 設定中會引用這個 Skill

    # agents/data-pipeline-agent.yaml
    name: data-pipeline-agent
    model: vertex_ai_gemini_1_5_pro
    system_prompt: |
      你是資料工程代理,負責每日 ETL 任務。
      嚴格遵守工具輸入/輸出 schema,錯誤時優先重試或回報。
    skills:
      - bigquery_query
      - gcs_file_writer
    state_store:
      type: firestore
      collection: agent_states
    

    Workflow 則決定觸發方式:

    # workflows/nightly-etl.yaml
    name: nightly-etl
    agent: data-pipeline-agent
    trigger:
      type: schedule
      cron: "0 2 * * *"  # 每天 02:00
    initial_input:
      task: "跑昨日訂單 ETL,更新匯總表"
    error_handling:
      notify:
        type: pubsub
        topic: agent-errors
      retry:
        max_attempts: 2
        delay_seconds: 300
    

    這樣一來,長任務會由排程觸發 Pub/Sub,交給 Cloud Run 上的 Agent runtime 處理;錯誤時有自動重試與告警管道,不用自己寫排程器和重試邏輯。

    💡 關鍵: 透過 retry 與 error_handling 設定,長任務可以安全自動重試並告警,而不用額外寫排程與錯誤恢復程式。


    4. 把 Agent 當「基礎設施」接入 CI/CD

    agents-cli 的另一個實用點是:部署過程本身就長得像 infra-as-code,非常適合放進 GitHub Actions 或 GitLab CI。

    以 GitHub Actions 為例:

    # .github/workflows/deploy-agent.yaml
    name: Deploy Agents
    
    on:
      push:
        branches: [ main ]
        paths:
          - "agents/**"
          - "skills/**"
          - "workflows/**"
    
    jobs:
      deploy:
        runs-on: ubuntu-latest
        steps:
          - uses: actions/checkout@v4
          - name: Setup gcloud
            uses: google-github-actions/setup-gcloud@v2
            with:
              project_id: ${{ secrets.GCP_PROJECT_ID }}
              service_account_key: ${{ secrets.GCP_SA_KEY }}
          - name: Install agents-cli
            run: pip install agents-cli
          - name: Deploy agents
            run: |
              agents deploy \
                --env envs/prod.yaml \
                --project ${{ secrets.GCP_PROJECT_ID }} \
                --region asia-east1
    

    這樣做的好處:

    • Agent 配置版本化,每次改動都有 commit 與審查
    • 部署流程可審核、可回滾,適合安全與合規要求(尤其是醫療、金融領域)。
    • 搭配類似 Gemini Spark Workflow 的設計理念,可以明確設定 Agent 何時啟動、何種操作需要人工確認。

    實作範例

    1. 資料管線自動化(ETL / 報表)

    場景:每天需要從 BigQuery 拉數據、轉換後寫回 GCS 或更新 Looker 報表。

    Skill 組合:

    • bigquery_query:查詢資料
    • gcs_file_writer:寫檔到 Cloud Storage
    • report_notifier:透過 Pub/Sub 通知 BI 團隊或觸發下游流程

    Workflow 配置重點:

    • schedule 觸發,搭配 錯誤告警 Pub/Sub topic
    • 在 Agent prompt 中明確要求:遇到資料不完整先記錄再通知,不要靜默失敗

    2. 內部客服流程

    場景:員工在內部系統丟 ticket,Agent 自動分類、查 FAQ、串 Jira / ServiceNow 建單。

    Skill 組合:

    • faq_search(Vertex AI + 自家向量 DB)
    • ticket_creator(Cloud Run microservice)
    • slack_notifier

    設計重點:

    • 參考 Gemini Spark 的模式:重大操作(例如關閉工單)要經過使用者確認,在 Agent prompt 裡要求先生成「建議動作」,再透過 notifier skill 要求人工點擊確認。
    • state_store 記錄每個 ticket 的處理歷史,方便審計。

    3. 自動 Code Review/Release Checklist

    場景:PR 建立後,由 Agent 自動跑靜態檢查、變更摘要與 release checklist,最後用評論回寫到 GitHub。

    Skill 組合:

    • repo_diff_reader:抓取 PR diff
    • static_analyzer:呼叫現有 lint / SAST 工具
    • github_commenter:寫回 GitHub PR 留言

    Workflow:

    # workflows/release-checklist.yaml
    name: release-checklist
    agent: release-agent
    trigger:
      type: webhook
      source: github
      event: pull_request
    initial_input:
      task: "分析這個 PR 的風險、受影響模組與 release checklist"
    

    好處:

    • 把原本散落在 CI pipeline 的檢查整合,由 Agent 產出有脈絡的總結與建議
    • 透過多 Skill 組合(lint、測試結果、變更摘要),降低 reviewer 的認知負擔。

    建議與注意事項

    1. IAM / Service Account 權限邊界

    常見坑:

    • 把整個 Agent runtime 綁一個 過度授權的 Service Account,導致 Agent 有權限操作所有 GCP 資源。

    建議:

    • 每個 Skill 使用 專用 Service Account,並在 YAML 中註記:
    security:
      service_account: bigquery-reader-sa@project.iam.gserviceaccount.com
      iam_roles:
        - roles/bigquery.dataViewer
    
    • VPC Service Controls / 資料邊界 設計出「有邊界的代理」,敏感資料必須經人工確認技能(如 human_approval)才能存取。

    2. 成本與 LLM 調用暴衝

    長流程 + 多工具,很容易引起:

    • 太多決策回合(多次 model 呼叫)。
    • 錯誤重試導致費用倍增。

    最佳實踐:

    • 為 Agent 設定 max_steps / max_tokens
    execution_limits:
      max_steps: 20
      max_model_calls: 10
      max_tokens_per_call: 8000
    
    • 成本監控儀表板:把 trace 寫到 BigQuery,做月度成本分析。
    • 對純查詢型任務,考慮簡化:少用思維鏈、多用工具直接查資料

    💡 關鍵: 透過 execution_limits 和成本監控,把多步驟、多工具的代理流程費用控制在可預期範圍。

    3. 錯誤恢復與重試策略

    不要把「重試」交給 LLM 自己想。使用 agents-cli 的 retry_policyerror_handling 明確設定:

    • 對 idempotent 的 Skill(如查詢、讀檔)可以多次重試。
    • 對非 idempotent 操作(如寫入、付款)要先寫審計 log,再由人處理重試。

    結合 LangSmith 類型的觀測工具

    • 對每次錯誤 trace 下來,標記是「工具錯誤」還是「LLM 誤用工具」,方便調整 prompt 或工具設計。

    4. Agent 邊界與隱私風險

    實務上最容易被忽略的是:Agent 存取範圍默默變大

    控制方法:

    • 在 Agent 的 system_prompt 明確寫出:哪些資料不可存取/不得持久化。
    • Skill 層的邊界實作:敏感操作一律透過 human_approval skill,例如:
    # skills/human_approval.yaml
    name: human_approval
    runtime: pubsub
    topic: approval-requests
    required_for_actions:
      - "刪除資料"
      - "發送外部郵件"
    
    • 參考 HIPAA Voice Agent 的做法:敏感資料永不出邊界,必要時用本地模型或受控環境中的 Vertex AI。

    結論:agents-cli 把「AI 代理」拉回工程實務的語境——YAML 設定、CLI 部署、IAM 邊界、觀測與成本控制。如果你已經有一堆工具和 LLM 能力,但遲遲不能在企業落地成正式工作流,這套工具很適合直接試著把現有程式包成 Skill,從一個小型工作流開始,把 Agent 當作基礎設施來運維。

    🚀 你現在可以做的事

    • 在現有專案中列出 3–5 個常用服務,草擬對應的 Skill YAML 定義
    • 使用 agents init 建立試驗專案,先在本地用 agents run 模擬一條簡單工作流
    • 把這條工作流接入 GitHub Actions 或 GitLab CI,測試以 infra-as-code 管理 Agent 部署與更新
  • 本地 LLM+RAG 架構:從 NASA CMO-DA 學部署

    本地 LLM+RAG 架構:從 NASA CMO-DA 學部署

    📌 本文重點

    • 斷網環境可用本地 LLM+RAG 解決醫療輔助
    • llama.cpp 搭配容器化可在受限硬體穩定推理
    • 使用 OSS 向量庫打造可控、可回滾的 RAG pipeline
    • 模型與知識庫需分版本管理確保合規與安全

    在太空任務這種完全斷網、硬體受限且高合規的環境,NASA 的 CMO-DA 醫療助手用一套可攜、可封裝的本地 LLM+RAG 架構,解決了三個常見痛點:

    1. 不依賴雲端:模型推理與檔案檢索都在本地完成,沒有外部服務依賴。
    2. 可控資源與效能:透過 llama.cpp + 容器化(RamaLama 類工具),在 CPU/GPU 都受限的情況下仍能穩定跑推理。
    3. 安全與更新邊界清楚:醫療場景下做到知識庫可更新但可 rollback,模型版本可控且遵守合規要求。

    💡 關鍵: 在完全斷網且高合規場景中,能本地推理並可回滾的 LLM+RAG 架構是可行且可維護的方案

    下面用 NASA CMO-DA 的思路,拆成一個可直接套用的本地 LLM+RAG 模板。


    重點說明

    1. 為何選擇 llama.cpp + 容器化工具做本地推理

    在 CMO-DA 這類場景,llama.cpp 有幾個關鍵優勢:

    • 硬體覆蓋廣:支援 x86、ARM、多種 GPU(CUDA、Metal、OpenCL 等),適合未知/多樣化載具(太空艙、工廠、船艦)。
    • GGUF 模型格式:支援量化(q4_0, q5_K 等),可以在有限記憶體上跑中大型模型。
    • 單一 binary,易封裝:搭配像 RamaLama 這種工具,把模型 + 推理程式封裝成容器映像,做到「模型即容器」。

    實際好處:

    • 對你來說,部署路徑變成:git pullcmake → 放入 GGUF 模型 → 打包成 container,不需要大堆框架整合。
    • DeepSeek V4 已合併進 llama.cpp 主幹,你可以直接在本地跑 DeepSeek V4 GGUF,在推理品質與效能上有更好的選擇。

    容器化工具(以 RamaLama 類工具為例)則負責:

    • 自動 GPU 偵測與穿透(在 Kubernetes / OpenShift 中把 GPU 資源暴露給容器)。
    • 統一啟動參數與模型路徑,讓模型像應用程式一樣可複製、可移動。

    💡 關鍵: 透過 GGUF 量化與「模型即容器」封裝,能在受限硬體上穩定部署中大型本地模型

    2. 斷網環境下的 RAG pipeline 設計

    本地 RAG 的核心是:不要依賴雲端向量服務,一切用自架的 OSS 向量庫

    常見 OSS 選擇:

    • Qdrant:Rust 實作,支援 HNSW、瀏覽器/邊緣友好,有好用 REST / gRPC API。
    • Milvus / Weaviate:功能更完整,適合資料量非常大但資源較充裕的內網環境。

    醫療場景(也是典型高合規場景)的設計要點:

    1. Chunking 策略
    2. 醫療指引、SOP 文件為單位,再切成 512–1024 tokens chunk,重點是維持語境完整。
    3. 加上 overlap(例如 128 tokens),避免答案跨 chunk 斷裂。
    4. 針對表格或 checklist,考慮以「row / section」為粒度,而不是固定 tokens。

    5. Embedding 模型本地化

    6. 選一個能放進 GGUF 或支援 CPU/GPU 的 embedding 模型,例如 bge-large 的量化版本或 MiniLM 類模型。
    7. 避免用雲端 embedding API,保證完全離線。

    8. 延遲與資源限制

    9. 查詢流程:Embedding → Vector search → Top-k 文件 → LLM 回答,每一步都要評估延遲。
    10. 太空艙/工廠場景中通常只有 1–2 張 GPU,LLM 推理延遲是主瓶頸,可以:
      • 調低 max_tokens 回答長度。
      • 預先計算並緩存常見問答(FAQ)以降低實時查詢負擔。

    💡 關鍵: 在離線場景中,512–1024 tokens chunk 加 128 overlap 能平衡上下文完整性與檢索效率

    3. GPU 自動偵測與穿透:Kubernetes / OpenShift 配置

    在 CMO-DA 中,他們透過類似 RamaLama 的工具,在 OpenShift 上做到:

    • 自動偵測節點是否有 GPU(透過標籤或 device plugin)。
    • 在 pod 內把 GPU 暴露給 llama.cpp

    對你來說,可以簡化成 Kubernetes YAML 設計:

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: local-llm
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: local-llm
      template:
        metadata:
          labels:
            app: local-llm
        spec:
          containers:
            - name: llama-cpp
              image: your-registry/llama-cpp-gguf:latest
              args:
                - "--model=/models/med-llm.gguf"
                - "--ctx-size=4096"
                - "--n-gpu-layers=35"  # **關鍵參數**:LLM 分配到 GPU 的層數
              resources:
                limits:
                  nvidia.com/gpu: 1      # Kubernetes GPU 資源宣告
              volumeMounts:
                - name: models
                  mountPath: /models
          volumes:
            - name: models
              persistentVolumeClaim:
                claimName: llm-model-pvc
          nodeSelector:
            nvidia.com/gpu.present: "true"  # **GPU 自動偵測:只排程到有 GPU 的節點
    

    OpenShift 上同樣依賴 GPU Operator / device plugin,容器內不需要特殊程式碼,llama.cpp 會透過 CUDA / ROCm 自動偵測 GPU。


    實作範例:Minimal 本地 LLM+RAG PoC

    下面是一個可離線部署的小型 RAG 助理範例,組合:llama.cpp + DeepSeek V4 GGUF + Qdrant

    1. 準備模型與 llama.cpp

    # 取得最新 llama.cpp(含 DeepSeek V4 支援)
    git clone https://github.com/ggml-org/llama.cpp.git
    cd llama.cpp
    git pull
    cmake -B build
    cmake --build build -j
    
    # 假設你已下載 DeepSeek V4 GGUF 模型到 ./models/deepseek-v4-med.q4_0.gguf
    

    2. 啟動本地 Qdrant 向量庫

    docker run -d --name qdrant \
      -p 6333:6333 \
      -v qdrant_data:/qdrant/storage \
      qdrant/qdrant
    

    3. 建立 RAG Pipeline(Python 範例)

    import requests
    from sentence_transformers import SentenceTransformer
    
    # **Embedding 模型**:可替換為你量化後的本地模型
    embed_model = SentenceTransformer("sentence-transformers/all-MiniLM-L6-v2")
    
    QDRANT_URL = "http://localhost:6333"
    COLLECTION = "med_docs"
    
    def create_collection():
        body = {
            "vectors": {
                "size": 384,
                "distance": "Cosine"
            }
        }
        requests.put(f"{QDRANT_URL}/collections/{COLLECTION}", json=body).raise_for_status()
    
    def index_documents(docs):
        vectors = embed_model.encode([d["text"] for d in docs])
        points = [{
            "id": i,
            "vector": vectors[i].tolist(),
            "payload": docs[i]
        } for i in range(len(docs))]
        requests.put(f"{QDRANT_URL}/collections/{COLLECTION}/points", json={"points": points}).raise_for_status()
    
    def rag_search(query, top_k=3):
        vec = embed_model.encode([query])[0].tolist()
        body = {
            "vector": vec,
            "limit": top_k
        }
        res = requests.post(f"{QDRANT_URL}/collections/{COLLECTION}/points/search", json=body).json()
        return [r["payload"]["text"] for r in res]
    
    # 初始化
    create_collection()
    index_documents([
      {"text": "若出現輕微頭痛且無其他症狀,建議先休息並補充水分。"},
      {"text": "若有持續胸痛或呼吸困難,應立即啟動緊急醫療流程。"}
    ])
    
    print(rag_search("胸口痛怎麼辦?"))
    

    4. 將檢索結果交給 llama.cpp

    在本地,你可以用簡單的 HTTP wrapper 把 context 拼接給 llama.cpp--prompt

    ./build/bin/llama-cli \
      --model ./models/deepseek-v4-med.q4_0.gguf \
      --ctx-size 4096 \
      --n-gpu-layers 35 \
      --prompt "根據以下醫療指引回答問題:
    [指引1]
    若有持續胸痛或呼吸困難,應立即啟動緊急醫療流程。
    
    問題:胸口痛怎麼辦?"
    

    在正式專案中,你會把 Qdrant 的 top-k 結果串接到 prompt,組成完整 RAG 回答流程。


    建議與注意事項

    1. 醫療場景的安全邊界與 offline 更新策略

    醫療、工業安全等場景,關鍵是 模型與知識庫的版本分離

    • 模型(LLM)版本:透過容器映像管理,例如在 tag 中寫明版本:med-llm:v1.2.0
    • 知識庫(向量庫 + 原始文件)版本:在 Qdrant collection 命名中加入版本:med_docs_v2024_06

    更新策略建議:

    1. 離線同步
    2. 透過 USB / 專用線路將新模型(GGUF)與新向量庫 dump 帶到現場。
    3. 先在 staging 節點載入,跑自動化驗收測試(醫療 QA 集)。

    4. Rollback 機制

    5. 保留上一版模型映像與向量庫快照,決策錯誤時可以在幾分鐘內回退。
    6. 配置開關:只允許在非緊急任務時切換版本,避免任務中途變更行為。

    2. 常見坑與最佳實踐

    1. 坑:只量化模型但忘記量化記憶體 footprint
    2. 即使用 q4_0,context 太大(例如 32k)時仍可能爆 RAM。
    3. 建議:先用 --ctx-size=4096 做壓力測試,再逐步拉高。

    4. 坑:Chunk 太小導致回答失去上下文

    5. 斷網場景下沒有「多輪 call retriever 補充」的餘裕,chunk 要適度放大。
    6. 建議:醫療/工廠 SOP 至少維持 512–1024 tokens + 128 overlap

    7. 坑:GPU 穿透配置錯誤導致 fallback 到 CPU

    8. 沒有設 resources.limits.nvidia.com/gpu 或節點沒正確標記,pod 會跑在無 GPU 節點上。
    9. 建議:在 CI/CD 中加入簡單檢查:呼叫 nvidia-smillama.cpp GPU info API,確認有 GPU 再部署。

    10. 最佳實踐:在高合規場景用「白名單指令 + RAG」模式

    11. 不允許模型「自由想像」,回答必須引用 RAG 檢索到的片段。
    12. prompt 設計中加入:「只能根據提供的文件回答,若無相關資訊請回答『資料不足』」,降低幻覺風險。

    總結來說,NASA CMO-DA 的做法給了我們一個清晰模板:llama.cpp + 容器化 + OSS 向量庫 + 可控版本策略。只要把這套思路搬到你的工廠、船艦、礦場或企業內網,就能在斷網或高合規環境下建立一個可維護、可升級又安全的本地 LLM+RAG 助理。

    🚀 你現在可以做的事

    • 在 GitHub 搜尋並 git clone 官方 llama.cpp 專案,編譯並測試本地推理
    • 使用 Docker 拉起 Qdrant,依照文中 Python 範例建立一個最小 RAG PoC
    • 寫一份 Kubernetes Deployment YAML,把你的 GGUF 模型封裝成「模型即容器」,並在測試環境驗證 GPU 穿透与延遲表现
  • 用 Gemini 3.5 控螢幕:Agent 實戰架構解析

    用 Gemini 3.5 控螢幕:Agent 實戰架構解析

    📌 本文重點

    • Gemini 3.5 直接「看螢幕 + 點 UI」,大幅簡化自動化 Agent
    • 用 DSL 約束操作與多輪回饋,提升穩定性與錯誤恢復能力
    • 安全邊界與權限治理是避免變成「超級巨鼠標」的關鍵

    Gemini 3.5 的 Computer Use 功能直接解決了一個老問題:過去我們寫辦公自動化或 E2E 測試 Agent,必須在「語言模型」和「操作工具」之間做大量 glue code(PlaywrightSelenium、各種 RPA SDK)。現在模型本身就能看螢幕 + 理解 UI + 產生操作事件,實作「會自己點 UI」的 Agent 架構可以大幅簡化,同時在 OSWorld 這類 benchmarks 上已經能接近人類級別的操作能力。

    💡 關鍵: Gemini 3.5 直接讀螢幕與 DOM,讓原本需要大量 glue code 的自動化流程被模型本身吸收,工程複雜度大幅下降。


    重點說明:Gemini 控螢幕的技術斷面

    1. 螢幕感知:Screenshot + DOM 雙通道

    Gemini 3.5 FlashComputer Use 能力裡,核心是讓模型同時看到:

    • 螢幕截圖:提供視覺語意,例如按鈕風格、顏色、位置、Tooltip 等。
    • DOM 或可操作節點樹:提供結構語意,如 aria-labelroleid、階層關係。

    實作上通常會有一個中介層:

    • 你的 Agent Runtime 負責抓取 當前視窗 screenshot、序列化 DOM / widget tree 成結構化 JSON
    • 透過 Gemini API 以多模態輸入丟給模型,模型回傳抽象操作意圖(例如「點選『匯出報表』按鈕」),再轉成具體事件(例如 click(x, y)click(selector))。

    這比傳統 RPA 的「座標點擊」穩健得多,因為模型能根據文字與結構定位元素,不再怕視窗尺寸微調就全掛。

    2. 操作語義到事件映射:從自然語言到 GUI 事件模型

    Gemini 本身不直接產生 OS-level 事件,而是給出操作語義,由你的 Agent 層做事件映射。典型設計是:

    • 模型輸出一組高階指令
    • click element: 「匯出報表」scroll until: 「三月」type in field: 「搜尋」 -> "季報"

    • Agent 將其轉換成具體事件:

    • 瀏覽器端:document.querySelector(...) + element.click()
    • 桌面端:座標點擊(OS API / robot library)

    建議在系統層設計一個 操作 DSL,例如:

    {
      "actions": [
        { "type": "click", "target": { "text": "匯出報表", "role": "button" } },
        { "type": "fill", "target": { "placeholder": "搜尋" }, "value": "季報" },
        { "type": "wait", "condition": { "text": "匯出完成" }, "timeoutMs": 10000 }
      ]
    }
    

    讓 Gemini 的輸出約束在這個 DSL 內,再由你寫 resolverDSL 轉成實際 API 呼叫,如 PlaywrightChromium DevTools 或自製桌面自動化套件。

    3. 權限與安全沙箱:避免做成「超級巨鼠標」

    能「看螢幕 + 點 UI」的 Agent,在安全上本質上是半個遠端操控工具。要避免直接變成超級巨鼠標,需要在設計時就加上:

    • Scope 限制:只允許控制特定應用(例如公司內部 CRM),而不是整個 OS
    • 權限分級:讀取螢幕 ≠ 可以操作所有按鈕,對「刪除」「匯出」「設定」類操作加上額外人為確認。
    • 審計與可回溯:所有操作都要有 log,包括模型輸入、輸出 DSL、最後映射的實際事件。

    Gemini 在雲端側有基本的安全保護,但真正壓力在你自家的 Agent Runtime。核心結論:螢幕控制能力是一把雙面刃,沒安全邊界就等同開放一個高權限自動點擊器。

    💡 關鍵: 把控制範圍鎖在特定應用與低權限環境,是避免「一個 Bug 變成全系統災難」的第一道防線。


    實作範例:會自己點 UI 的辦公自動化 / E2E Agent

    以下用 pseudo-code 示範一個基於 Gemini Computer Use 的 Agent 架構,對比你現在用 PlaywrightRPA 的做法。

    1. Agent 主循環:狀態管理與事件模型

    假設你有一個 Browser Automation Runtime,可以:

    • 抓取 screenshot
    • dump DOMJSON
    • 執行 DSL 動作

    初始化與一次任務呼叫

    # 假設有 gemini_client 已封裝好
    from agent_runtime import get_screenshot, get_dom_tree, execute_actions
    
    SYSTEM_PROMPT = """
    你是一個辦公自動化 Agent。你只能透過提供的 action DSL 操作螢幕。
    目標:根據使用者需求,在瀏覽器完成操作。
    請只輸出 JSON,不要輸出其他文字。
    """
    
    state = {
        "task": "下載最新的月報表並存到桌面",
        "history": []
    }
    
    def run_step(state):
        screenshot = get_screenshot()
        dom = get_dom_tree()  # 結構化 JSON
    
        prompt = {
            "system": SYSTEM_PROMPT,
            "user": {
                "task": state["task"],
                "history": state["history"],
                "screen": screenshot,   # 圖像
                "dom": dom              # 結構化文字
            }
        }
    
        # 透過 Gemini 3.5 Flash 多模態 API
        resp = gemini_client.generate(
            model="gemini-3.5-flash-computer-use",
            input=prompt,
            # 關鍵參數:讓模型遵守 DSL
            tools=[{"name": "ui_action_dsl", "schema": ACTION_SCHEMA}],
            temperature=0.2
        )
    
        actions = resp["actions"]
        result = execute_actions(actions)
    
        state["history"].append({"actions": actions, "result": result})
        return state
    
    # 任務循環(直到模型判斷完成)
    while not state.get("done"):
        state = run_step(state)
    

    關鍵設計點:

    • 使用 多步迭代:每次看最新螢幕 & DOM,再決定下一步 action,類似人類操作流程。
    • 將狀態(history)回饋給模型,讓它能做錯誤恢復與路徑修正。

    2. DSL Schema:限制模型輸出空間

    JSON Schema 方式提供 ui_action_dsl 給 Gemini(伺服器側 Stub 示意):

    export const ACTION_SCHEMA = {
      type: "object",
      properties: {
        actions: {
          type: "array",
          items: {
            type: "object",
            properties: {
              type: { enum: ["click", "fill", "scroll", "wait", "assert"] },
              target: {
                type: "object",
                properties: {
                  text: { type: "string" },
                  role: { type: "string" },
                  selector: { type: "string" },
                  ariaLabel: { type: "string" }
                }
              },
              value: { type: "string" },
              condition: { type: "object" },
              timeoutMs: { type: "number" }
            },
            required: ["type", "target"]
          }
        }
      },
      required: ["actions"]
    };
    

    把這個 schema 當作 tool 定義丟給 Gemini,模型就會傾向輸出符合 schemaJSON,而不是隨意產生指令文字。這比讓模型自己「猜 Playwright API」更穩。

    3. 錯誤恢復策略:從 E2E 測試角度看

    E2E 測試模式下,你可以:

    • 每個 step 後檢查 result 是否有錯誤(DOM element 找不到、timeout 等)。
    • 把錯誤詳細資訊(包含「按鈕找不到」等)回餵給模型,再跑下一個 step,讓 Agent 自己嘗試調整操作策略。

    示意:

    result = execute_actions(actions)
    if result.error:
        state["history"].append({"actions": actions, "error": result.error})
    else:
        state["history"].append({"actions": actions, "ok": True})
    

    這種「模型 + 真實環境回饋」的迭代,比傳統 Playwright 寫死 selector 更有韌性:UI 調整、小改版時,Agent 仍可能靠語意找到正確控件。

    💡 關鍵: 把錯誤結果當成下一輪輸入的一部分,能讓 Agent 自我修正,比單純重試同一段 script 聰明得多。


    建議與注意事項:和 RPA / Playwright 對比,以及安全邊界

    與傳統 RPA / Playwright 的優缺點對比

    優勢:

    • 選擇器韌性更高:不是全靠 #idxpath,模型會綜合文字、位置、上下文理解「這顆按鈕是做什麼的」。
    • 對自然語言任務友善:「幫我下載三月月報表」這種任務,不需要你事先拆成多條 script,Agent 自己規劃路徑。
    • 多模態容錯:螢幕上即使有動態廣告、複雜排版,模型也能分辨主要內容與干擾。

    劣勢 / 需要評估的點:

    • 成本與延遲:每一個 step 都要 call 一次 LLM,比傳統 script 直接執行慢且貴,適合複雜流程,不適合同一動作高頻批量執行。
    • 可預期性Playwright script 是 deterministic;Gemini Agent 則有一定隨機性(雖然可降低 temperature),需要監控與回滾機制。

    真實專案的安全與治理建議

    1. 明確劃定控制範圍

    2. 用獨立瀏覽器容器(例如專用 Chrome profileWebView),讓 Agent 只能看到業務系統,而非整個桌面。

    3. 對不同任務配置不同權限 profile,例如「只讀報表」「允許建立草稿但不可送出」。

    4. 強制人機協同節點

    5. 對敏感操作(刪除資料、匯出大量客戶名單)設計「人工確認」步驟,讓 Agent 停在「準備送出」頁面,由人點最後一個按鈕。

    6. 使用 UI 上的明確標記(例如「需管理員確認」)讓模型也知道哪些步驟不應自動完成。

    7. 完整審計 log + 可回放

    8. 記錄:模型輸入(螢幕縮圖、DOM 摘要)、模型輸出 DSL、實際執行的事件與結果。

    9. 可選擇在安全事件發生時回放整個操作過程,做事後分析。

    10. 避免敏感資料洩露

    11. screenshot 做遮罩(mask),例如遮蓋客戶姓名、金額等,再送給模型。

    12. DOM 摘要可做字段裁剪,只提供必要欄位(如 label、角色、結構),不要直接把完整表格資料送到雲端。

    13. 從小範圍 PoC 起步

    14. 先在「測試環境 + 低權限帳號」上跑 Agent,驗證行為穩定,再逐步提升權限。

    15. 與現有 Playwright / RPA 共存:把 Gemini Agent 用在「探索性操作」與「易變 UI」,穩定的核心流程仍用傳統 script

    總結:Gemini 3.5 的螢幕感知與控制能力,讓我們可以用更少的硬編碼 selector 和流程腳本,實作能「看得懂 UI、自己點按鈕」的辦公自動化 / E2E Agent。真正的工程重點在於:設計好操作 DSL、狀態管理與錯誤恢復機制,並在安全邊界、審計與人機協同上做好防護,讓這個 Agent 是可控的助理,而不是失控的超級巨鼠標。

    🚀 你現在可以做的事

    • 在你現有的 Playwright / Selenium 專案中,先為 1 個流程試做一個 ui_action_dsl,讓 LLM 產生 JSON 再轉成實際指令
    • 建一個測試用瀏覽器容器(低權限帳號 + 測試環境),接上 Gemini 3.5 Flash 多模態 API 做小型 PoC
    • 為預計自動化的系統列出敏感操作,先設計好「人工確認」與操作 log / 回放機制
  • GPT-5.5-Cyber 資安工程實作指南

    GPT-5.5-Cyber 資安工程實作指南

    📌 本文重點

    • GPT-5.5-Cyber 讓安全檢查從「找漏洞」進化到「自動產生可審核 patch」
    • 透過 CI/CD 整合,資安檢查變成每個 PR 的標準流程
    • 自動修補仍需權限分離與人工審核,避免新風險

    GPT-5.5-Cyber 直擊的痛點很單純:你現在的 GPT 會幫你找漏洞,但不會負責把修補流程安全、穩定地接到 CI/CD 裡。Daybreak 計畫與 GPT-5.5-Cyber 的更新,把重心從「發現」轉成「自動化修補且可審核」,讓模型真正能扮演資安工程師,而不是只當顧問。


    重點說明

    1. 專用安全模型:介面、定價與能力差異

    OpenAI 把 GPT-5.5-Cyber當成專用安全模型來賣,有幾個差異:

    • 介面:仍走 OpenAI API,但會有獨立的 model 名稱,例如 gpt-5.5-cyber,並透過 Codex Security 插件接入 repo / CI 環境。
    • 定價:通常走「安全分析專用 tier」,
    • 較高的 input token 上限(方便吃整個 PR / 多檔案 diff),
    • 「按分析次數」或「按 token」計費,偏向企業安全預算,而不是一般應用開發費率。
    • 能力差異
    • 專門在 SAST/DAST、漏洞分類、修補建議上做過微調;
    • 比一般 GPT 更擅長 理解 CVE、OWASP Top 10、常見框架安全最佳實務
    • Daybreak 計畫的重點:從挖洞轉向自動 patch,包括生成可直接提交的修補 PR。

    對你的專案的實際好處:

    • 可以把 PR 安全檢查變成標準 pipeline,而不是「有空才找人看」。
    • 讓模型不只「指出有問題」,還能 產出具體 patch + 測試建議,減少資安團隊瓶頸。

    💡 關鍵: GPT-5.5-Cyber 的價值在於「自動產生可審核 patch」,讓模型從顧問變成可以真正動手修補的資安工程師。


    2. 如何接到現有 CI/CD、SAST/DAST 流程

    整合思路可拆三層:

    1. 觸發層:GitHub Actions / GitLab CI / Jenkins,在以下事件觸發:
    2. PR 建立 / 更新
    3. 新版部署前(pre-deploy check)
    4. incident response pipeline(安全告警被觸發)

    5. 分析層

    6. 先跑既有 SAST(如 SemgrepCodeQL)/ DAST(如 OWASP ZAP),產出報告。
    7. gpt-5.5-cyber消化:

      • 重點摘要
      • 風險分級
      • 針對特定檔案/函式生成 patch。
    8. 修補層

    9. Codex Security 插件或自寫 tooling:
      • 根據 diff 生成 patch
      • 產生額外測試案例
      • 建立新的修補 PR,而不是直接 push 到 main

    實作範例

    以下示範三種整合方式,皆以 GitHub Actions + OpenAI API 為例,你可以類推到其他 CI 平台。


    範例一:在 PR 上自動做靜態分析

    目標:

    • PR 建立時,自動跑 SAST,並用 GPT-5.5-Cyber產生「安全 reviewer 評語」貼回 PR。
    # .github/workflows/security-review.yml
    name: Security Review
    
    on:
      pull_request:
        types: [opened, synchronize]
    
    jobs:
      sast-and-ai-review:
        runs-on: ubuntu-latest
        steps:
          - name: Checkout
            uses: actions/checkout@v4
    
          - name: Run SAST (Semgrep)
            run: |
              pip install semgrep
              semgrep --config "p/owasp-top-ten" --json > semgrep-report.json
    
          - name: Generate AI Security Review
            env:
              OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
            run: |
              python .github/scripts/security_review.py
    
          - name: Post comment to PR
            uses: peter-evans/create-or-update-comment@v4
            with:
              issue-number: ${{ github.event.pull_request.number }}
              body: ${{ steps.generate-output.outputs.review_comment }}
    

    對應的 security_review.py(縮寫版):

    import os, json, requests
    
    with open('semgrep-report.json') as f:
        report = json.load(f)
    
    prompt = f"""
    你是資安工程師。以下是 Semgrep 報告,請:
    1. 挑出高風險項目(類似 SQLi、XSS、RCE)。
    2. 以 PR reviewer 的口吻,給出具體修正建議(含程式碼片段)。
    3. 若可,指出可加上的安全測試案例。
    
    Semgrep JSON:
    {report}
    """
    
    resp = requests.post(
        "https://api.openai.com/v1/chat/completions",
        headers={"Authorization": f"Bearer {os.environ['OPENAI_API_KEY']}",
                 "Content-Type": "application/json"},
        json={
            "model": "gpt-5.5-cyber",  # **關鍵:專用安全模型**
            "messages": [{"role": "user", "content": prompt}],
            "temperature": 0.1
        },
    )
    
    review_comment = resp.json()["choices"][0]["message"]["content"]
    print("::set-output name=review_comment::" + review_comment)
    

    實際好處:每個 PR 都會有一份「資安聚焦」評語,不依賴資安團隊逐 PR 人工 review。


    範例二:產生修補 PR 並跑測試

    目標:

    • 收到資安告警(例如某路由有 SQL injection)。
    • GPT-5.5-Cyber生成 patch + 測試,再由 CI 建立新 PR。

    假設有一個簡單 Python script:

    # .github/scripts/auto_patch.py
    import os, subprocess, json, requests
    
    FILE_PATH = "app/routes/user.py"
    
    code = open(FILE_PATH).read()
    
    prompt = f"""
    你是資安工程師。以下是程式碼,已被標記可能有 SQL injection 問題。
    請:
    1. 產生修補後的完整檔案內容。
    2. 為此路由新增至少一個測試案例(pytest),確認注入失效。
    3. 不要改動非必要邏輯,避免影響既有功能。
    
    程式碼:
    {code}
    """
    
    resp = requests.post(
        "https://api.openai.com/v1/chat/completions",
        headers={"Authorization": f"Bearer {os.environ['OPENAI_API_KEY']}",
                 "Content-Type": "application/json"},
        json={
            "model": "gpt-5.5-cyber",
            "messages": [{"role": "user", "content": prompt}],
            "response_format": {"type": "json_object"}
        },
    )
    
    data = resp.json()["choices"][0]["message"]["content"]
    patch = json.loads(data)
    
    # 假設模型回傳 {"patched_file": "...", "test_file": "tests/test_user_security.py"}
    open(FILE_PATH, "w").write(patch["patched_file"])
    open(patch["test_file"], "w").write(patch["test_code"])
    
    # 在新分支上提交
    branch = "security-fix-auto"
    subprocess.run(["git", "checkout", "-b", branch])
    subprocess.run(["git", "commit", "-am", "chore: auto security patch"])
    subprocess.run(["git", "push", "origin", branch])
    

    CI 的 YAML:

    name: Auto Security Patch
    on:
      workflow_dispatch:
        inputs:
          target_file:
            description: '被標記的檔案路徑'
            required: true
    
    jobs:
      patch-and-test:
        runs-on: ubuntu-latest
        steps:
          - uses: actions/checkout@v4
          - name: Run auto patch
            env:
              OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
            run: python .github/scripts/auto_patch.py
    
          - name: Run tests
            run: pytest
    

    設計重點

    • 權限分離:CI 只建立新分支與 commit,不直接 merge。
    • 人類 reviewer 仍要審核 PR,避免模型 patch 造成 Side effect。

    範例三:incident response 裡的 root cause 分析

    目標:當 WAF / IDS 有告警時:

    • 把 log + 程式碼 context 丟給 GPT-5.5-Cyber
    • 快速拿到 root cause + 修補建議,作為 oncall 的決策輔助。

    簡化版 pipeline:

    # incident.sh
    LOG_SNIPPET=$(tail -n 200 /var/log/app/security.log)
    CODE_SNIPPET=$(sed -n '80,140p' app/routes/login.js)
    
    curl https://api.openai.com/v1/chat/completions \  
      -H "Authorization: Bearer $OPENAI_API_KEY" \  
      -H "Content-Type: application/json" \  
      -d '{
        "model": "gpt-5.5-cyber",
        "messages": [
          {"role": "system", "content": "你是 incident response 資安工程師。"},
          {"role": "user", "content": "logs:\
    '"$LOG_SNIPPET"'\
    code:\
    '"$CODE_SNIPPET"'"}
        ],
        "temperature": 0.0
      }'
    

    你可以把輸出寫入 ticket 系統,或直接貼到 Slack 給 oncall 小組。

    好處:縮短「理解問題 +草擬修補方案」時間,讓人類專注在判斷與決策,而不是整理資訊。


    建議與注意事項

    1. 自動修補的風險控制

    幾個必要的 safeties:

    • 權限分離
    • CI 只產生 patch 或建立分支 / PR,絕不直接 deploy。這是最基本的安全線。
    • 安全 sandbox
    • 在隔離環境跑模型生成的修補 + 測試,避免未審核的變更觸及生產資源。
    • 避免「過度修補」
    • prompt 中要明確要求:「只修補漏洞相關部分,不改動非必要邏輯」
    • 針對 patch 做 diff review,檢查是否出現大面積重構。
    • 避免引入新漏洞
    • 生成 patch 後再次跑 SAST/DAST,當成 regression check。

    2. 最小可行落地方案與成本

    一個 MVP 組合大致是:

    • GitHub Actions + YAML:觸發 PR / 手動 workflow。
    • OpenAI API + gpt-5.5-cyber:分析與 patch 生成。
    • 既有測試框架pytest / Jest / go test 等,用來驗證 patch。

    粗略成本估算(示意):

    • 每次分析吃 50–200 KB 程式碼 + SAST 報告,假設約幾千到上萬 tokens。
    • 若採企業安全定價 tier,大致可以想成:「每個 PR 的資安 reviewer 只要幾角到幾塊美金」級別,通常比請人類資安顧問便宜很多。

    💡 關鍵: 把 GPT-5.5-Cyber 視為「低成本、可擴充的人力」,用幾角到幾塊美金就能替每個 PR 配一位資安 reviewer。

    建議先從:

    1. 對「敏感服務」的 PR 開始,先跑 AI 安全評語,不直接讓它 patch。
    2. 穩定後,再導入「自動產生 patch + 測試,但仍需人工 merge」的模式。

    3. 常見坑與防呆

    • 誤信模型安全建議
    • GPT-5.5-Cyber 仍會犯錯,不要把它當唯一真相來源。堅持:
      • 每個 patch 必須通過測試。
      • 高風險區域要有資安 engineer 審核。
    • 缺少審核人
    • 若團隊太小,至少設定「安全 owner」,負責最後 approve。
    • 別讓 auto-merge 跟 AI patch 直接掛在一起。
    • 日誌與敏感原始碼外洩風險
    • 對外呼叫 OpenAI API 時,注意:
      • 不要把完整 production secrets / customer data 丟進去。
      • 遮蔽敏感欄位(email、ID、token)再送出。
    • 熟讀供應商的 data retention / training policy,必要時啟用「不保留資料」選項。
    • 模型版本與政策更新
    • 專用安全模型會常更新,建議:
      • model 名稱抽成環境變數,方便切換。
      • 在 staging 先試新版本,再推到 production CI pipeline。

    核心結論

    • gpt-5.5-cyber + Codex Security 提供的是「能產出可審核 patch 的資安工程師」,不是只會報錯的 linters。
    • 真正的價值在於:把安全工作流程 codify 到 CI/CD 裡,讓資安不再是事後補救,而是每次 PR 都會自動被檢查和建議修補。

    如果你已經有基本的 LLM 開發能力,下一步可以直接從「在 PR 上掛一個 GPT-5.5-Cyber 安全 reviewer」開始,感受它對日常開發節奏的改變。

    💡 關鍵: 起手式可以非常小——先讓 GPT-5.5-Cyber 只當「安全 reviewer」,再逐步升級到自動產生 patch。


    🚀 你現在可以做的事

    • 在現有 GitHub repo 新增一個 PR workflow,接入 gpt-5.5-cyber 產生安全評語
    • 選一個高風險服務,實驗「AI 產生 patch + 測試,由你人工審核合併」
    • model 名稱、API key 等參數抽成環境變數,在 staging 先跑一週觀察效果
  • Fugu 式多模型協作實戰拆解

    Fugu 式多模型協作實戰拆解

    📌 本文重點

    • 單一 LLM 容易遇到成本與供應商風險問題
    • Fugu 用任務類型與路由實現多模型協作
    • 多模型需要良好編排、仲裁與可觀測性
    • 建議從抽象 Task 與 adapter 漸進導入

    單一 LLM 做所有事情的痛點很明確:成本不可控、供應商風險高、效能無法對應不同任務類型。Sakana 的 Fugu 路線給了一個很務實的答案:用一層編排(orchestration)把多個模型(雲端 + 本地 + 專用 code model)協作起來,把「選模型、聚合結果、錯誤控制」變成一套可維護的工程結構,而不是散落在業務程式碼裡的 if-else。


    重點說明

    1. Fugu 式類型系統:先定「任務類型」,再談選哪個模型

    Fugu 的關鍵不是多模型本身,而是任務類型(task types)+ 類型安全的輸入輸出

    • 每個任務明確定義:
    • input schema(如 QueryTask, CodeGenTask, LongFormTask
    • output schema(如 Answer, CodePatch, SearchPlan
    • 路由層只依賴任務類型與 metadata(長度、成本上限、延遲 SLA),不直接寫死「如果是寫程式就用 XXX」。

    範例(TypeScript 風格的 pseudo code):

    // 1. 任務類型定義
    interface BaseTaskMeta {
      maxLatencyMs: number;
      maxCostUSD: number;
      priority: 'low' | 'normal' | 'high';
    }
    
    interface QueryTask {
      type: 'query';
      input: { question: string; context?: string };
      meta: BaseTaskMeta & { allowWebSearch: boolean };
    }
    
    interface CodeGenTask {
      type: 'codegen';
      input: { spec: string; language: string };
      meta: BaseTaskMeta & { needTests: boolean };
    }
    
    interface LongFormTask {
      type: 'longform';
      input: { topic: string; minWords: number };
      meta: BaseTaskMeta & { allowStreaming: boolean };
    }
    
    type Task = QueryTask | CodeGenTask | LongFormTask;
    

    好處:

    • 模型路由只看 Task,不看業務細節,方便之後替換模型 / 供應商。
    • 不同模型可以有不同的 prompt / tool schema,但在編排層都被包成同一個 Task 抽象。

    💡 關鍵: 先用類型把任務抽象好,之後換模型或換供應商就變成「改路由」而不是「重寫業務程式碼」。


    2. 模型路由:任務類型 × 長度 × 成本

    一個實用的路由策略通常只靠幾個欄位就夠了:

    • 任務類型
    • codegen → 專用 code model(如 o3-miniDeepSeek-Coder、本地 Qwen code)
    • query → 一般對話模型(OpenAI / Anthropic / 本地)
    • longform → 長 context 模型(如 200k+ context),或拆段 + 聚合
    • 長度估計:預估輸入 token + 預計輸出 token,超過本地模型 context 就路由到雲端長上下文模型。
    • 成本與延遲
    • maxCostUSD 控制是否可以打貴模型
    • maxLatencyMs 決定是否啟用並行查詢 + 快速仲裁

    簡化版路由器:

    function routeModel(task: Task): 'openai:gpt-4.1-mini' | 'local:qwen' | 'openai:o3-mini' {
      const estTokens = estimateTokens(task.input);
    
      if (task.type === 'codegen') {
        // code 任務預設走專用 code model
        return task.meta.maxCostUSD < 0.05 ? 'local:qwen' : 'openai:o3-mini';
      }
    
      if (task.type === 'longform') {
        if (estTokens > 120_000) return 'openai:gpt-4.1-mini';
        return 'local:qwen';
      }
    
      // query 一般問答
      if (task.meta.maxLatencyMs < 3000) {
        // 低延遲預算 → 本地或較小雲端模型
        return 'local:qwen';
      }
    
      return 'openai:gpt-4.1-mini';
    }
    

    這類路由就是 Fugu 類型系統在工程上的落地:先把任務分型,路由邏輯就自然長出來

    💡 關鍵:maxLatencyMsmaxCostUSD 這類 metadata 控制路由,可以在同一套架構裡同時優化成本與延遲。


    3. 回覆聚合與仲裁:多模型輸出怎麼合成一個答案

    多模型協作的價值在於:

    • 一部分模型擅長查(search / recall),一部分擅長寫(rewrite / explain)
    • 或同一任務交給兩個模型,透過仲裁降低幻覺

    典型做法:

    1. 並行呼叫 2–3 個模型:如本地 Qwen + 雲端 GPT
    2. 用一個「仲裁模型」來閱讀所有候選答案,輸出最終回覆與信心分數

    仲裁 prompt 示意:

    const arbiterPrompt = `你是仲裁模型。你會看到多個模型的回答,請:
    1. 比較其一致性與是否自相矛盾。
    2. 檢查是否有推理錯誤或明顯幻覺。
    3. 選出最可信的一個,並在有疑慮時標記「不確定」。
    
    輸出 JSON:
    {
      "winner": "model_a" | "model_b",
      "confidence": 0-1,
      "final_answer": "...",
      "notes": "..."
    }`;
    

    好處:

    • 提高可靠性(特別是檢索或工具調用密集場景)
    • 可以把仲裁結果記錄下來,用於後續離線分析各模型表現

    成本上升是必然,常見做法是:

    • 只在 高價值任務 / 有風險的 domain(法律、醫療) 開啟仲裁
    • 其他場景靠單模型 + tool verification 解決

    4. 單一 LLM vs 多 LLM 編排:實際取捨

    單一 LLM

    • 優點:實作簡單、debug 容易、觀測鏈短
    • 缺點:
    • 價格彈性差:所有任務都用貴模型
    • 供應商風險:價格調整、限額、區域封鎖都直接影響產品
    • 難以對應極端需求(超長上下文、線下敏感數據)

    多 LLM 編排(Fugu 路線):

    • 優點:
    • 不同任務用最合適的模型 → 可靠性與成本可同時優化
    • 可以把敏感任務 route 到本地模型,降低隱私風險
    • 雲端服務掛了可以 fallback 到次佳方案
    • 缺點:
    • 觀測與 debug 變複雜(誰的錯?哪一層出問題?)
    • 延遲可能放大(串聯多步、多模型仲裁)
    • 各家 API、tool schema、系統提示格式不一致,需要一層 adapter

    對多數專案來說:

    • MVP 階段 → 單一 LLM + 清楚的 abstraction
    • 成本 / 隱私壓力出現後 → 漸進式導入多模型編排,而不是一次重寫

    💡 關鍵: 多模型不是為了「酷」,而是為了在成本、可靠性、隱私之間取得更穩定的折衷。


    實作範例:OpenAI + 本地 Qwen + 專用 code model

    以下給出一個可自建的最小多模型編排骨架(Node/TypeScript 風格,但概念可套任何語言)。

    1. API 介面設計

    對前端/上游只暴露一個 API:POST /v1/ai/execute,輸入統一的 Task 結構。

    // express / fastify handler
    app.post('/v1/ai/execute', async (req, res) => {
      const task: Task = req.body;
    
      const modelId = routeModel(task);
    
      const controller = new AbortController();
      const timeout = setTimeout(() => controller.abort(), task.meta.maxLatencyMs);
    
      try {
        const rawResponse = await callModel(modelId, task, { signal: controller.signal });
        const parsed = normalizeOutput(task, rawResponse);
    
        await logTask({ task, modelId, rawResponse: parsed });
    
        res.json(parsed);
      } catch (e) {
        const fallback = await tryFallback(task, modelId);
        res.json(fallback);
      } finally {
        clearTimeout(timeout);
      }
    });
    

    2. 模型 adapter:解決不同 API / token 格式

    常見坑是:

    • 系統訊令格式不同(OpenAI messages vs 本地單純 prompt
    • tool / function call schema 不同

    用 adapter 隔離差異:

    async function callModel(modelId: string, task: Task, opts: { signal: AbortSignal }) {
      switch (modelId) {
        case 'openai:gpt-4.1-mini':
          return callOpenAI(task, opts);
        case 'openai:o3-mini':
          return callOpenAICode(task, opts);
        case 'local:qwen':
          return callLocalQwen(task, opts);
        default:
          throw new Error(`Unknown model ${modelId}`);
      }
    }
    
    async function callOpenAI(task: Task, { signal }: { signal: AbortSignal }) {
      const messages = buildMessagesFromTask(task);
      const resp = await openai.chat.completions.create({
        model: 'gpt-4.1-mini',
        messages,
        temperature: 0.2,
        response_format: { type: 'json_object' },
        signal,
      });
      return resp.choices[0].message.content;
    }
    
    async function callLocalQwen(task: Task, { signal }: { signal: AbortSignal }) {
      const prompt = buildPromptFromTask(task); // 單一 string
      const resp = await fetch('http://localhost:8000/v1/completions', {
        method: 'POST',
        body: JSON.stringify({
          model: 'qwen-32b-instruct',
          prompt,
          max_tokens: 2048,
          temperature: 0.1,
        }),
        signal,
      }).then(r => r.json());
      return resp.choices[0].text;
    }
    

    只要嚴格把「怎麼跟模型講話」鎖在 adapter 裡,上層就可以只面對 Task


    3. 超時與 fallback 策略

    簡單可行的策略:

    1. 以延遲為主的 fallback

    2. 本地模型超時 → fallback 到雲端小模型

    3. 雲端模型錯誤/超時 → fallback 到本地(或退化版回答)
    async function tryFallback(task: Task, failedModelId: string) {
      const fallbackId = pickFallbackModel(task, failedModelId);
      if (!fallbackId) throw new Error('No fallback model');
    
      const raw = await callModel(fallbackId, task, { signal: AbortSignal.timeout(2000) });
      return normalizeOutput(task, raw, { degraded: true, usedFallback: true, fallbackId });
    }
    
    1. 回應標註退化狀態:在 normalizeOutput 中加入:
    {
      "answer": "...",
      "meta": {
        "modelId": "local:qwen",
        "usedFallback": true,
        "fallbackFrom": "openai:gpt-4.1-mini",
        "degraded": true
      }
    }
    

    讓前端可以決定是否顯示「此回答為備援模型生成」。


    4. 觀測與日誌結構:解決「昨晚 agent 到底做了什麼」

    多模型編排很容易變成黑箱。建議最少做到:

    • 每個 Task 一個 traceId
    • log 中至少包含:
    • traceId, task.type, task.meta
    • 選擇的 modelId、fallback 情況
    • 每步 latency、token 使用量
    • 仲裁結果(如果有)

    示意:

    interface TaskLog {
      traceId: string;
      taskType: Task['type'];
      modelId: string;
      fallbackFrom?: string;
      latencyMs: number;
      inputTokens: number;
      outputTokens: number;
      success: boolean;
      error?: string;
    }
    
    async function logTask(log: TaskLog) {
      // 可寫入 ClickHouse / BigQuery / Elastic
      console.log(JSON.stringify({ kind: 'taskLog', ...log }));
    }
    

    這類結構化 log 是後續做「loop engineering」(自動 self-correct / regression test)與成本優化的基石。


    建議與注意事項

    1. 延遲放大:多模型 ≠ 多倍延遲

    • 儘量並行呼叫可獨立的模型,再用仲裁合併。
    • 嚴格設定 per-model timeout,避免某個模型拖垮整個請求。
    • 對長任務(如自動寫測試、長時間 agent loop)要分段 log,避免只看到「跑了一小時,掛了」。

    2. 工具 / 系統 prompt 不一致

    • 不同家模型對 system / tools / function calling 的支援度不同。
    • 最好的做法:
    • 定義自己的 工具層 schema(如 JSON Tool 定義)
    • 在 adapter 把它映射成各模型需要的格式
    • 千萬避免在業務邏輯裡到處寫 if (model === 'gpt-4.1-mini') 這種分支。

    3. 責任歸屬與 debug 困難

    多模型編排容易出現:

    • prompt 沒設好 → 模型亂回答
    • 路由策略不合理 → 小模型被丟去做艱難任務
    • 仲裁錯誤 → 明明較好的答案被丟掉

    實務上建議:

    • 為每一層定義清楚的「契約」
    • 路由層:輸入 Task,輸出 modelId,不關心內容
    • adapter:保證把 Task 翻譯成該模型最佳格式
    • 仲裁層:對模型輸出負責,不對業務邏輯負責
    • 做回溯時先問:錯在路由、adapter、模型本身、還是仲裁?

    4. 不要一開始就 over-engineer

    • 若你現在是:一個雲端 LLM + 少數工具,建議只先做:
    • 抽出 Task 類型
    • 寫好 model adapter(即使目前只有一個模型)
    • 之後要引入本地 Qwen、專用 code model、Fugu 式仲裁機制,就只是替換實作,而不是重寫整個系統。

    核心結論: 多模型協作不是把更多模型硬塞進系統,而是用 清晰的任務類型 + 路由 + 仲裁 + 可觀測性,把「哪個模型做什麼」變成一個可以演進的工程決策。從這個角度看,你可以在自己的專案裡做一個「迷你 Fugu」,用極少的代碼換來更好的成本控制、可靠性與供應商彈性。


    🚀 你現在可以做的事

    • 在現有專案中抽出一層 Task 類型與 routeModel(),把模型選擇邏輯從業務程式碼移出來
    • 寫一個簡單的 model adapter(例如包一層 callModel()),即使目前只支援單一 gpt-4.1-mini
    • 為每次模型呼叫加上 traceId 與結構化 log,開始累積日後做成本與可靠性優化所需的資料