腦子系統實證篇:本地 Gateway 完整實作版(v2.3,674 行真能接 CC)

重點摘要(TL;DR)

  • 前 8 篇是藍圖。本篇是實作真實版:在 Mini PC(無 GPU、32GB RAM、Ryzen 7)用 364 行 FastAPI 跑通搬離方法論,真能接 Claude Code。
  • 核心邏輯:Gateway 看 prompt 內容,命中 A 級字典 → 地端最強模型(14b);其他 → cloud Claude(若有 API key)或 fallback 地端。
  • 關鍵設計原則(別搞錯):A 級資料用地端最強模型,不是最弱。敏感資料因為更重要,需要更可靠的回答。小模型只能當分類器或 fail-safe。
  • 真接 CC 的關鍵:用 Anthropic 原生 /v1/messages endpoint,不是 OpenAI 的 /v1/chat/completions,並做完整翻譯層(request / response / tool use / SSE)。
  • Harness 三 agent 永遠走 cloud(地端跑不動三 agent 並行 + long context),只是輸入經 Gateway 強脫敏 — 這是搬離後最關鍵的工作流保護。
  • 本文是腦子系統九部曲實證篇。前八篇:Why / How / Scale / Tools / ERP / Self-Service / ISO / Execution

一、為什麼寫這篇 — 從藍圖到實作真實版

前 8 篇腦子系統累積了大量「應該怎樣」的論述:Why / How / Scale / Tools / ERP / Self-Service / ISO / Execution。對真正要動手的人,這些都還是紙上的東西

本篇是分水嶺 — 用一台 Mini PC(沒 GPU,32GB RAM,Ryzen 7 4700U,2020 年款)跑通可以真的接 Claude Code 的搬離 Gateway,證明:

  • 不需要 GPU,純 CPU 也能 host gateway logic
  • 不需要 LiteLLM / Portkey 等大框架,純 Python 364 行搞定
  • 不需要 ANTHROPIC_API_KEY 也能跑(有 fallback 模式)
  • CC + Agent Team + Harness 工作流不變,只改 BASE_URL

二、5 條設計原則(別搞錯)

原則 1:A 級資料地端,不可協商

A 級的定義是「送出去會出事」 — 客戶機密、財報、製程 know-how。這個層級不能因為 cloud 模型強就送出。地端是底線。

原則 2:A 級用地端最強模型,不是最弱

這條最容易搞錯。直覺是「敏感資料 = 風險高 = 用小模型」,但 logic 應該倒過來:敏感資料因為更重要,需要更可靠的回答

情境 地端模型選擇 理由
A 級主處理 地端最強(14b / 32b / 80B-A3B) 資料越敏感,回答越要可靠
分級判斷器 小模型(0.5b / 1.7b)or regex 分類本身不需要強能力
Fail-safe 容錯 小模型保守路由 寧可路由保守不要錯放

原則 3:路由邏輯走字典 + regex,不靠 LLM

分級判斷不該交給 LLM(慢、不確定、可被 prompt injection 騙)。改用字典 + regex,毫秒級完成,可審計。

原則 4:Anthropic 原生 endpoint(/v1/messages),不是 OpenAI 的 /v1/chat/completions

CC 用 Anthropic Messages API,你 Gateway 必須 expose /v1/messages,不是 OpenAI 的 endpoint。並且做完整 Anthropic ↔ OpenAI 翻譯(因為地端 Ollama 用 OpenAI compatible 格式)。

原則 5:沒 API key 也能跑(fallback 地端)

Gateway 設計成:有 ANTHROPIC_API_KEY 就 C 級走真 cloud Claude;沒有就 fallback 走地端。讓你能純地端先驗證 logic,再加 cloud

2.1 雙維度決策表(敏感度 × 可用性)— 別搞混

fallback 不只看「cloud 有沒有 key」,還要看「資料能不能上 cloud」。雙維度決策才完整:

分級 主路由 Fallback 關鍵保護
A 級 地端最強(14b/32b/80B) 沒 fallback — 地端跑不動 = 等 / 改題目 即使有 cloud key 也不走 cloud
B 級 地端優先 地端不可用 → 脫敏後 cloud 能脫敏才 fallback,不能脫敏寧願報錯
C 級 cloud 優先 沒 key → 地端 純技術問題,無敏感度

常見誤解:有 cloud key 就什麼都走 cloud。錯。A 級即使有 key 也不該走 cloud — 因為「資料外洩風險 > 模型能力差異」。Gateway 的職責就是替你擋住這個誘惑:你 prompt 命中 A 級字典,Gateway 不問你「要不要送 cloud」,直接路由到地端。

本版實作狀態:A 級 + C 級已實作完整;B 級的「地端優先 + cloud fallback + 脫敏」是 TODO,本版 B 級 keyword 命中時邏輯等同 A 級(全地端)。完整 B 級實作見最末「待補的東西」章節。

三、364 行 Gateway 完整實作

結構:

gateway.py(364 行)
├─ Classifier              (~30 行)— 抽 messages 文字 + 字典命中
├─ Anthropic→OpenAI Req    (~80 行)— system / messages / tool_use / tool_result 翻譯
├─ OpenAI→Anthropic Resp   (~40 行)— content blocks / stop_reason / usage
├─ SSE Streaming           (~40 行)— 6 種 Anthropic 事件 from OpenAI delta
├─ Backend Forwarders      (~80 行)— Ollama / Anthropic 雙路 forward + fallback
└─ Main Endpoint           (~30 行)— /v1/messages,分類後派到對應 forward

3.1 核心邏輯(主要 dispatcher)

@app.post("/v1/messages")
async def messages(request: Request):
    auth = request.headers.get("authorization", "")
    if MASTER_KEY not in auth and not ANTHROPIC_API_KEY:
        raise HTTPException(401, "bad master key")

    body = await request.json()
    original_model = body.get("model", "claude-opus-4-7")
    decision, keyword = classify(body.get("messages", []), body.get("system"))

    if decision == "A":
        log.warning(f"[A-LEVEL] 命中 '{keyword}' → 地端 {MODEL_A_LEVEL}")
        return await forward_to_ollama(body, MODEL_A_LEVEL, original_model)
    else:
        log.info(f"[C-LEVEL] → cloud {original_model}" if ANTHROPIC_API_KEY else f"[C-LEVEL] no key → local fallback")
        return await forward_to_anthropic(body, request, original_model)

3.2 Anthropic ↔ OpenAI 翻譯的 4 個關鍵點

# 1. Anthropic system 是 top-level → OpenAI 是 system message
sys = body.get("system")
if isinstance(sys, str):
    openai_messages.append({"role": "system", "content": sys})

# 2. Anthropic tool_use 是 content block → OpenAI 是 message 上的 tool_calls
if btype == "tool_use":
    tool_calls.append({
        "id": block["id"],
        "type": "function",
        "function": {"name": block["name"],
                     "arguments": json.dumps(block["input"])}
    })

# 3. Anthropic tool_result 在 user message 內 → OpenAI 是 role:tool 獨立 message
if btype == "tool_result":
    openai_messages.append({
        "role": "tool",
        "tool_call_id": block["tool_use_id"],
        "content": str(result_content)
    })

# 4. SSE 翻譯:OpenAI delta 累積 → Anthropic 6 種事件
#    message_start → content_block_start → content_block_delta(每個 token)
#    → content_block_stop → message_delta(stop_reason)→ message_stop

3.3 Forwarder(雙路 + fallback)

async def forward_to_ollama(body, target_model, original_model):
    """A 級 → 翻譯成 OpenAI format,forward to Ollama 地端強模型。"""
    openai_body = anthropic_to_openai_request(body, target_model)
    is_stream = openai_body.get("stream", False)
    if is_stream:
        return StreamingResponse(stream_anthropic_from_openai(...))
    async with httpx.AsyncClient(timeout=600) as client:
        r = await client.post(f"{OLLAMA_URL}/v1/chat/completions", json=openai_body)
    return JSONResponse(openai_to_anthropic_response(r.json(), original_model))


async def forward_to_anthropic(body, request, original_model):
    """C 級 → 直接 proxy 到 api.anthropic.com,沒 key 就 fallback 地端。"""
    if not ANTHROPIC_API_KEY:
        return await forward_to_ollama(body, ANTHROPIC_FALLBACK_MODEL, original_model)
    headers = {"x-api-key": ANTHROPIC_API_KEY, "anthropic-version": "2023-06-01"}
    if body.get("stream"):
        # SSE 直接透傳(Anthropic format,不用翻譯)
        return StreamingResponse(...)
    async with httpx.AsyncClient(timeout=600) as client:
        r = await client.post("https://api.anthropic.com/v1/messages", json=body, headers=headers)
    return JSONResponse(r.json())

v2.3 完整 Gist(674 行 gateway + 24 個 pytest + benchmark + demo + README,5 個檔案):
👉 https://gist.github.com/tm731531/c82c51ae2a73bfe640dec5b61e5a542a

Gist 含 README + 5 步驟啟動 + 測試 curl 範例 + 已知限制。clone 下來改字典即可用。

3.1 v2 → v2.1 changelog(review 後修)

v2 上 Gist 後又收到 review,點出 3 個有實際影響的 bug,其中 1 個是安全問題。**全修了**:

  • 🔴 Bug 1(安全):Auth 邏輯反了 — 原本「沒設 cloud key 才檢查 master_key」意思是「接了 cloud 反而不檢查」,任何人能燒你 quota。修法:無條件檢查 master_key,並兼容 x-api-key + Authorization: Bearer 兩種 header。實測 no-key/wrong-key 都回 401
  • 🔴 Bug 2(功能):Streaming 模式 tool use 完全不工作 — 原本 stream_anthropic_from_openai 只翻譯 text delta,沒處理 delta.tool_calls。CC 的 Read/Edit/Bash 都是 tool use → A 級 + streaming 時 CC 卡住。修法:加 tool_calls delta 累積邏輯,追蹤 tool_call_index → our_block_index mapping,送 content_block_start (tool_use) + input_json_delta 事件序列。約 +60 行
  • 🟡 Bug 3:streaming 模式 stop_reason 寫死成 end_turn,即使 OpenAI 端因 max_tokens 截斷或 tool_calls 收尾也誤標。修法:streaming 過程累積最後 finish_reason,結束時用真實值映射(stop→end_turn / length→max_tokens / tool_calls→tool_use)
  • + 結構改進:content blocks 改 lazy open(只在真有內容時送 content_block_start),text 跟 tool 可正確交錯;dead import 清掉;docstring 改寫(原版誤稱用 sse-starlette)

從 v1(80 行,描述跟 code 矛盾) → v2(364 行,文字宣稱) → v2 Gist(394 行,實際存在但 3 bug) → v2.1(502 行,bug 修完)。三天四個版本,每一輪 review 都點出真實問題。這個迭代過程本身就是 brain 系統「review-driven development」的最佳示範

四、CC + Agent Team + Harness 三件事的協作

4.1 CC 接 Gateway(0 行 code 改動)

# Terminal 設環境變數
export ANTHROPIC_BASE_URL=http://localhost:4000
export ANTHROPIC_AUTH_TOKEN=sk-walsin-test

# 跑 CC 跟原本一樣
claude

CC 完全不知道後面接的是 Gateway。所有 prompt 自動經分類 → 路由。

4.2 Agent Team 走 Gateway(子進程繼承 BASE_URL)

你在 CC 裡 spawn 7 個 opus agent 並行 — 每個 sub-agent 共用同一個 BASE_URL(從父 process 繼承)。Gateway 對每個 agent 的 prompt 獨立分類:

你 (CC main)
├─ Agent 1 (opus): "review 這份 SAP API 設計"  → C 級 → cloud Claude
├─ Agent 2 (opus): "找 [client_alpha] 客訴 case" → A 級 → 地端 14b
├─ Agent 3 (opus): "寫 Kafka consumer"          → C 級 → cloud Claude
├─ Agent 4 (opus): "看 [project_xxx] 的合約"    → A 級 → 地端 14b
├─ Agent 5-7 (opus): 其他 C 級任務              → cloud Claude

大多數 Agent Team 任務不命中 A 級字典,99% 體感跟原本一樣。少數命中的會走地端,慢一點但隔離。

4.3 Harness 三 agent — 永遠走 cloud(關鍵保護)

Anthropic 2026/3 發布的三 agent harness(Planner / Generator / Evaluator)是給 cloud 設計的。地端 80B-A3B 跑三 agent 並行 = GPU 排隊,根本跑不動。

正解:Harness 永遠走 cloud,但輸入經 Gateway 強脫敏

用戶: "幫我 refactor [project_xxx] 的支付模組"
    ↓
Gateway 偵測 [project_xxx](A 級字典)
    ↓
若強脫敏成功 → "幫我 refactor [PROJECT] 的支付模組" → cloud Claude(三 agent)
若無法脫敏 → 整個任務改地端 14b sequential 跑(慢但安全)
    ↓
Planner: 拆 task → Generator: 寫 code → Evaluator: 檢查
    ↓
結果經 Gateway 回到用戶

Harness 的價值在 long context + 複雜 reasoning,地端在這兩點本就弱。硬搬就是自虐。脫敏走 cloud 才是對的策略。

4.4 三件事的協作全景

你 (CC main session, ANTHROPIC_BASE_URL=gateway)
    │
    ├─ 普通 prompt → Gateway → 路由 → 對應 backend
    │
    ├─ Spawn Agent Team(7 個 opus 並行)
    │   ├─ 每個 sub-agent 繼承 BASE_URL
    │   ├─ Gateway 對每個 prompt 獨立分類
    │   └─ A 級走地端 14b,C 級走 cloud Claude
    │
    └─ Spawn Harness(Planner / Generator / Evaluator)
        ├─ 三 agent 共用 BASE_URL
        ├─ Gateway 強制路由全 cloud(脫敏後)
        └─ 因為地端跑不動三 agent 並行

五、Brain 系統整合(sensitivity_level frontmatter)

你的 brain markdown 系統(~/.claude/projects/.../memory/)是搬離的核心資產。整合方式:

5.1 brain frontmatter 加分級欄位

# 一般 brain(C 級,可上 cloud)
---
name: kafka_consumer_pattern
type: technical
sensitivity_level: C
---
Kafka consumer 群組 rebalance 機制...

# 敏感 brain(A 級,只地端 + 強模型)
---
name: client_alpha_oncall_pattern
type: business_incident
sensitivity_level: A
applies_to: [bu_xxx]
---
[client_alpha] 客訴流程,聯絡窗口...

5.2 build.sh 編譯時依分級過濾

#!/bin/bash
# 編譯雙版本 CLAUDE.md

# Cloud-bound CLAUDE.md(沒 A 級)
find brain/ -name "*.md" \
  | xargs grep -L "sensitivity_level: A" \
  | xargs cat > .claude/CLAUDE.md.cloud

# Local-bound CLAUDE.md(全部,A 級也進)
cat brain/**/*.md > .claude/CLAUDE.md.local

# Gateway 看員工任務目標選對應 CLAUDE.md

5.3 brain 的 A 級關鍵字自動同步到 Gateway 字典

# 從所有 A 級 brain 抽出 client name / project code 等
grep -h "sensitivity_level: A" -A 20 brain/**/*.md \
  | grep -oP '\[client_\w+\]|\[project_\w+\]' \
  | sort -u > /tmp/A_keywords.txt

# Gateway 啟動時 load
A_KEYWORDS = open("/tmp/A_keywords.txt").read().splitlines() + DEFAULT_A_KEYWORDS

5.4 公開版 brain repo 自動過濾

如果你的 brain 有公開版(教學分享 / 開源),build script 自動排除 sensitivity_level: A 條目,只發 B / C。不用手動審 brain 是否能公開

這是brain 系統跟 Gateway 的接合點:你寫 brain 時標分級,Gateway 自動知道哪些字串該擋,公開版自動過濾。一個 frontmatter 欄位,三個地方用

六、放大邏輯 — 個人 → 80 人 → 萬人

面向 個人(本文實證) 80 人公司 萬人集團
Gateway 實作 364 行 FastAPI LiteLLM Docker K8s HPA + Portkey
A 級字典 3-10 個關鍵字 100 個 1000+ 自動同步 brain
A 級 backend Ollama Qwen3:14b(CPU) Ollama Qwen3:32b(1x 4090) 中央 GPU H100 跑 80B-A3B + 區域副本
C 級 backend cloud Claude(個人 API key)or fallback 地端 Anthropic Enterprise Anthropic Enterprise + Azure / Bedrock 多家
脫敏 字典 + regex Microsoft Presidio + LLM 兜底
認證 master key 員工 SSO SSO + Token Impersonation
Audit log stdout SQLite / OpenSearch 三軌制 + WORM + HSM mapping
治理 0 Working Group 三道防線
時程 30-60 分鐘 2-3 個月 12 個月
預算 0 ~30 萬 NTD 4000-6000 萬 NTD

核心邏輯一模一樣(看 prompt → 字典分類 → 路由)。差的只是:

  • 規模(字典條數、並發、儲存)
  • 治理(Working Group、三道防線、ISO 認證)
  • 合規(SOX / J-SOX / 個資法 / GDPR)
  • 能力 backend(14b vs 80B-A3B)

七、能力降級補償策略

實際擔心:地端模型比 Claude Opus 4.7 弱,搬完會不會生產力崩?

實話:會降,看你會不會用補償工具。具體 benchmark 沒跑(個人 mini PC 沒 GPU 跑不了 32B+ 對比),但業界經驗的補償清單:

地端弱的地方 補償工具 效果
Long context 弱 RAG (Chroma / Qdrant) + chunking context 不全進 LLM,只進 top-K
Reasoning 弱 Chain-of-thought structured prompt 強制分步,單步難度降
Tool use 不穩 function calling 限縮 5-10 個 tools 減少選擇,提升正確率
並行 Agent 跑不動 改 sequential workflow 一個跑完再下一個
跨檔 refactor 弱 限定 working set(≤ 5 檔) 降低 context
Memory 弱 brain markdown 強制 inject 永遠帶 context

而且這只用在 5% A 級任務,其他 95% 還是 cloud。整體生產力下降可控,具體百分比待 SWE-bench Lite 子集 + 真實工作流 case 量化

八、5 步驟讓你今晚就跑起來

  1. 裝 Ollama + 拉模型:
    ollama pull qwen3:14b      # A 級主處理(地端最強)
    ollama pull qwen3:1.7b     # 可選,當分類器 fail-safe
  2. 裝 Python 套件:
    pip install --user fastapi uvicorn httpx
  3. 存 364 行 gateway.py(本文第三章 + 完整版見 GitHub Gist)
  4. 跑起來:
    # 沒 API key 也能跑(fallback 地端)
    python3 gateway.py &
    curl -s http://localhost:4000/health   # 確認 OK
    
    # 有 API key 完整版
    ANTHROPIC_API_KEY=sk-ant-... python3 gateway.py &
  5. CC 切過去:
    export ANTHROPIC_BASE_URL=http://localhost:4000
    export ANTHROPIC_AUTH_TOKEN=sk-walsin-test
    claude   # 跟原本一樣寫 code

30-60 分鐘搞定。設定完後 99% 工作跟原本一樣,只有 prompt 命中 A 級字典時自動切地端。

九、跑不起來時會看到什麼(失敗模式排查)

Gist 證明能跑,失敗模式證明跑過。下面是實作過程實際踩過的 7 個錯誤:

錯誤訊息 / 症狀 根本原因 排查指令
connection reset by peer + log 完全空 Container 還在 init(LiteLLM 啟動慢 30s-1min),或 Python stdout buffering docker exec <container> ps auxf 看 PID 1 是否還在跑;加 PYTHONUNBUFFERED=1
404 Not Found from CC Gateway 用 OpenAI /v1/chat/completions,CC 打 Anthropic /v1/messages 看 Gateway log 有沒有「POST /v1/messages」;改用本文 Anthropic 原生 endpoint
httpx.ReadTimeout 在 forward_to_ollama Ollama 模型在 CPU 第一次 load 太慢(超過 timeout) ollama run <model> "warm" 先暖機;timeout 從 300 改 600
OCI runtime exec failed: "curl" not found LiteLLM image 沒裝 curl,內部 health check 工具有限 用 host 端 curl 測 http://localhost:4000/health 不要 docker exec
{"detail": "bad master key"} CC 設了 ANTHROPIC_AUTH_TOKEN 但 Gateway 沒 match echo $ANTHROPIC_AUTH_TOKEN 跟 Gateway 的 MASTER_KEY 對
CC 卡住沒回應(streaming 不出來) SSE 翻譯漏了 message_stop 事件,client 等不到結束 Gateway log 看最後送出的 event;確認 6 種事件全送(message_startcontent_block_start/delta/stopmessage_deltamessage_stop)
A 級 prompt 沒命中字典(看到走 C 級) 字典 keyword 是 case-sensitive 漏了 re.IGNORECASE,或字典裡沒這條 curl -s gateway.../health 看 keywords_count;echo $PROMPT | grep -i <keyword>

十、Gist 上線前檢查清單(13 條)

從文章第一版到本版踩過的所有雷,清單化:

  1. Authorization header 兩種格式都要兼容:CC 可能送 x-api-key: xxxAuthorization: Bearer xxx,Gateway 都要認
  2. anthropic-version header 別漏:Anthropic API 要求 anthropic-version: 2023-06-01(或更新),proxy 過去要保留
  3. system 欄位三種型別都要處理:Anthropic 的 system 可以是 string、list of {type:text,text:…},或 unset
  4. tool_use ID 不能掉:翻譯後對應的 tool_calls 要保留同一個 ID,不然 client 對不上 tool_result
  5. tool_result 在 user message 內,翻譯後要拆成獨立 role:tool message
  6. SSE 6 個事件全送:message_start → content_block_start → content_block_delta(每個 token)→ content_block_stop → message_delta → message_stop,漏一個 client 卡死
  7. SSE event 名稱要寫 event:,data: 兩行:不是只送 data,Anthropic SSE 格式有 event 名
  8. Ollama 連線斷掉時 fallback 邏輯不能 race:用 try/except 包 forward_to_ollama,失敗才 fallback,不要兩個 task 同時跑
  9. timeout 要設 600 秒以上:CPU 跑 14b 慢,300 秒會 timeout
  10. master_key 預設值不要外洩:Gist 上的 sk-walsin-test 是 placeholder,部署前換掉
  11. A 級字典不能放 secret:keyword 本身會出現在 log,別放真實 client name(用 placeholder 例如 [client_alpha])
  12. health endpoint 不檢查 master_key:不然 monitoring 工具會 401
  13. 關 Gateway 用 SIGTERM 不要 SIGKILL:kill 不加 -9 讓 uvicorn 優雅關閉,避免 streaming response 中斷

十一、TODO 全部 close(v2.2 update)

原本標的 4 個 TODO 全做完了,本版升 v2.2(620 行)。逐項說:

原 TODO v2.2 處理 行數
B 級「地端優先 + cloud fallback + 脫敏」 ✅ 完整實作:ollama_alive() 健康檢查 → 失敗 sanitize_anthropic_body() → fallback cloud;sanitize 沒命中拒絕(503) +90 行
Benchmark benchmark_runner.py 獨立檔(258 行):跑 SWE-bench Lite 子集 + 自家 prompts × 多 model,輸出 markdown 報表。不打分,只跑數據(讓人類自己判斷,避免 premise drift) 258 行新檔
Asciinema 60 秒 demo demo_record.sh:health → C 級 → A 級 → auth fail 4 個 step,可直接跑或 asciinema rec -c 包起來錄影 110 行新檔
Token usage 真實計算 ✅ 用 tiktoken 估算累積 text + tool args,取代原本的 chunk count(嚴重低估) +20 行

11.1 v2.1 → v2.2 主要新邏輯

elif decision == "B":
    # v2.2 完整 B 級實作
    if await ollama_alive():
        return await forward_to_ollama(body, MODEL_B_LEVEL, original_model)

    # 地端死了,看能不能 fallback cloud
    if not (ANTHROPIC_API_KEY and B_LEVEL_CLOUD_FALLBACK):
        raise HTTPException(503, "B-level: local unavailable, cloud fallback disabled")

    sanitized_body, hit = sanitize_anthropic_body(body)
    if not hit:
        # 地端死 + 脫敏沒命中 = B 字典跟脫敏字典不一致,寧願報錯
        raise HTTPException(500, "B-level: local down + sanitization mismatch")

    return await forward_to_anthropic(sanitized_body, request, original_model)

11.2 Sanitization 字典(v2.2 新增)

SANITIZE_MAP = {
    r"\[internal_process\]": "[PROCESS]",
    r"\[vendor_quote\]": "[QUOTE]",
    r"\[employee_name\]": "[PERSON]",
    # 通用 PII patterns
    r"\b[\w.+-]+@[\w-]+\.[\w.-]+\b": "[EMAIL]",
    r"\b(?:\d{1,3}\.){3}\d{1,3}\b": "[IP]",
    r"\b\d{4}-\d{4}-\d{4}-\d{4}\b": "[CARD]",
}

實作策略:regex-based 簡單脫敏(快、可審計);生產環境建議升 Microsoft Presidio(NER + checksum + 多語言)。

11.3 Benchmark Runner 跑法

# 跑全部 prompts × 你已 pull 的 ollama 模型
python3 benchmark_runner.py

# 加 cloud Claude 對比(有 ANTHROPIC_API_KEY 才能)
ANTHROPIC_API_KEY=sk-ant-... python3 benchmark_runner.py \
  --models qwen3:14b,qwen3:4b \
  --anthropic-models claude-opus-4-7

# 只跑 SWE-bench Lite 子集
python3 benchmark_runner.py --suite swe --output report.md

跑出來是 markdown 報表,每 model × 每 prompt 的 latency / tokens / 截斷回應。故意不打分 — 因為「能力 = X%」這種宣稱本身就是 review 點過的 premise drift 風險。**跑數據給人看,人類自己判斷**,比 AI 講百分比有 integrity。

11.4 Demo 錄影

# 純跑(看 terminal output)
bash demo_record.sh

# 用 asciinema 錄影
asciinema rec -c "bash demo_record.sh" walsin-demo.cast
asciinema upload walsin-demo.cast   # (可選)上傳分享

4 個 step:health check → C 級 prompt → A 級 prompt(命中字典)→ 沒帶 key 401。每一步都看到 x-gateway-decision + x-gateway-model headers。

11.5 v2.2 → v2.3 self-review 後再清 7 個漏洞

「考試不能邊改邊考」 — 我自己當最嚴格 reviewer 把 v2.2 從頭審一次,找到 7 個應修的(不是別人指出),全清:

優先 問題 v2.3 修法
🔴 P0 Auth substring match 漏洞 — MASTER_KEY not in auth 太寬,sk-test-extra 也通過 secrets.compare_digest 精確比對 + Bearer 解析
🔴 P0 SSE 透傳格式錯 — aiter_lines + "\n" 會剝掉 \n\n event 結尾 aiter_bytes 直通,SSE 格式 byte-for-byte 完整
🔴 P0 Sanitize 漏 tool_use input + tool_result content — 只處理 text block 改遞迴 _sanitize_value 處理任意巢狀 dict / list / str
🟡 P1 MASTER_KEY 預設 hardcoded,生產環境壞習慣 沒設環境變數時 log warning,提示部署前必設
🟡 P1 demo_record.sh 缺 pre-flight,gateway 沒啟動 script crash 開頭加 curl /health,失敗給友善提示 + 啟動指令
🟡 P1 /health 沒回報 ollama 狀態,monitoring 不夠 ollama: alive/down + b_level_model + b_cloud_fallback 配置
🟡 P1 B 級走 cloud(脫敏後)client 不知道 回應加 X-Gateway-Sanitized: 1 header,透明度

11.6 24 個 pytest 全綠(v2.3 新)

$ pip install --user pytest pytest-asyncio
$ MASTER_KEY=sk-test-secret python3 -m pytest test_gateway.py -v

test_gateway.py::TestClassify::test_C_level_default              PASSED
test_gateway.py::TestClassify::test_A_level_keyword_match        PASSED
test_gateway.py::TestClassify::test_A_level_in_system            PASSED
test_gateway.py::TestClassify::test_A_level_in_list_content      PASSED
test_gateway.py::TestClassify::test_B_level_match                PASSED
test_gateway.py::TestClassify::test_A_takes_precedence_over_B    PASSED
test_gateway.py::TestMasterKey::test_correct_bearer              PASSED
test_gateway.py::TestMasterKey::test_correct_bare                PASSED
test_gateway.py::TestMasterKey::test_empty                       PASSED
test_gateway.py::TestMasterKey::test_wrong                       PASSED
test_gateway.py::TestMasterKey::test_substring_extra_suffix_blocked  PASSED  ← v2.3 修
test_gateway.py::TestMasterKey::test_substring_prefix_blocked    PASSED  ← v2.3 修
test_gateway.py::TestMasterKey::test_lower_case_bearer           PASSED
test_gateway.py::TestSanitization::test_string_email             PASSED
test_gateway.py::TestSanitization::test_string_ip                PASSED
test_gateway.py::TestSanitization::test_string_no_hit            PASSED
test_gateway.py::TestSanitization::test_recursive_dict           PASSED
test_gateway.py::TestSanitization::test_recursive_list           PASSED
test_gateway.py::TestSanitization::test_anthropic_body_text_block            PASSED
test_gateway.py::TestSanitization::test_anthropic_body_tool_use_input_v23    PASSED  ← v2.3 修
test_gateway.py::TestSanitization::test_anthropic_body_tool_result_v23       PASSED  ← v2.3 修
test_gateway.py::TestRequestTranslation::test_system_string_to_message       PASSED
test_gateway.py::TestRequestTranslation::test_tool_use_to_tool_calls         PASSED
test_gateway.py::TestRequestTranslation::test_tool_result_becomes_separate_message PASSED

============================== 24 passed in 0.61s ==============================

4 個 v2.3 安全修正關鍵 test 全綠 — 證明 substring 攻擊擋下、tool_use input 真的會被 sanitize。

11.7 真的還剩什麼不會做(誠實)

  • SWE-bench 完整跑數據:需要 GPU 跑 32B+,我這台 mini PC 不行。Runner 寫好了,你有 GPU 自己跑
  • 真錄 asciinema 公開連結:script 寫好(含 v2.3 pre-flight check),你自己 run + upload
  • Microsoft Presidio 升級:regex 已夠 demo,生產時換成 NER + checksum
  • httpx async mock 整合測試:現在的 24 個 unit test 涵蓋純函式,async stream 整合測試還沒寫

策略:能在我環境做的全做,不能做的寫好工具讓你自己做。每一輪迭代都比上一輪誠實。

十二、5 個學到的事(實作後)

  1. Gateway 路由邏輯不複雜(364 行 Python 含完整翻譯層 + SSE),別被 LiteLLM / Portkey / Kong 這些大框架嚇到
  2. CC 工作流不用改(只改 BASE_URL),搬離成本低於想像。但要真接 CC 必須做 Anthropic 原生 endpoint + 完整翻譯層
  3. A 級資料用地端最強,不是最弱。敏感資料因為更重要,需要更可靠回答 — 這條最容易搞反
  4. Mini PC 雖弱但能跑(CPU 跑 14b 約 1-3 tok/s,慢但能用),證明搬離方法論不需要先投資 GPU
  5. Harness 不該硬搬地端(三 agent 並行 + 長 context 是 cloud 的價值,脫敏走 cloud 才是對的)

結語:從藍圖到可執行的搬離

前 8 篇腦子系統告訴你「應該怎樣」。本篇告訴你「實際怎樣」。

364 行 Python + Mini PC + Ollama + Claude Code = 搬離方法論的可執行實作。

這不是教你「怎麼蓋萬人企業 AI 治理」 — 那是另外 8 篇的事。

這是教你「怎麼今晚就在自己電腦上跑通搬離 logic」 — 證明你的方法論不只是紙上的。

有了這個實作,你才有立場跟集團 IT 提 PoC,跟 CFO 提預算,跟法遵提合規。

下一步:你的 mini PC 有沒有變慢?Agent Team 還能 spawn 嗎?Brain 還在嗎?都沒事 — 因為 Gateway 是個獨立 process,不影響任何沒設 BASE_URL 的工作流。你想停掉就 kill 一個 process,連配置都不用改。

這就是搬離方法論的真實樣子:低風險、可逆、漸進、實作在前、規模在後

延伸閱讀:腦子系統九部曲

留言

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *